mirror of
https://thingvellir.net/git/overte
synced 2025-03-27 23:52:03 +01:00
Revert "Merge branch 'master' into fixGoToButtonInterstitial"
This reverts commitb4b0c519f3
, reversing changes made to77d5ebf34f
.
This commit is contained in:
parent
b4b0c519f3
commit
c5468fd8e9
24 changed files with 458 additions and 486 deletions
|
@ -11,7 +11,6 @@
|
||||||
|
|
||||||
#include "AvatarMixerClientData.h"
|
#include "AvatarMixerClientData.h"
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <udt/PacketHeaders.h>
|
#include <udt/PacketHeaders.h>
|
||||||
|
|
||||||
#include <DependencyManager.h>
|
#include <DependencyManager.h>
|
||||||
|
@ -219,10 +218,6 @@ uint16_t AvatarMixerClientData::getLastBroadcastSequenceNumber(const QUuid& node
|
||||||
}
|
}
|
||||||
|
|
||||||
void AvatarMixerClientData::ignoreOther(SharedNodePointer self, SharedNodePointer other) {
|
void AvatarMixerClientData::ignoreOther(SharedNodePointer self, SharedNodePointer other) {
|
||||||
ignoreOther(self.data(), other.data());
|
|
||||||
}
|
|
||||||
|
|
||||||
void AvatarMixerClientData::ignoreOther(const Node* self, const Node* other) {
|
|
||||||
if (!isRadiusIgnoring(other->getUUID())) {
|
if (!isRadiusIgnoring(other->getUUID())) {
|
||||||
addToRadiusIgnoringSet(other->getUUID());
|
addToRadiusIgnoringSet(other->getUUID());
|
||||||
auto killPacket = NLPacket::create(PacketType::KillAvatar, NUM_BYTES_RFC4122_UUID + sizeof(KillAvatarReason), true);
|
auto killPacket = NLPacket::create(PacketType::KillAvatar, NUM_BYTES_RFC4122_UUID + sizeof(KillAvatarReason), true);
|
||||||
|
@ -240,20 +235,9 @@ void AvatarMixerClientData::ignoreOther(const Node* self, const Node* other) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AvatarMixerClientData::isRadiusIgnoring(const QUuid& other) const {
|
void AvatarMixerClientData::removeFromRadiusIgnoringSet(SharedNodePointer self, const QUuid& other) {
|
||||||
return std::find(_radiusIgnoredOthers.cbegin(), _radiusIgnoredOthers.cend(), other) != _radiusIgnoredOthers.cend();
|
if (isRadiusIgnoring(other)) {
|
||||||
}
|
_radiusIgnoredOthers.erase(other);
|
||||||
|
|
||||||
void AvatarMixerClientData::addToRadiusIgnoringSet(const QUuid& other) {
|
|
||||||
if (!isRadiusIgnoring(other)) {
|
|
||||||
_radiusIgnoredOthers.push_back(other);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void AvatarMixerClientData::removeFromRadiusIgnoringSet(const QUuid& other) {
|
|
||||||
auto ignoredOtherIter = std::find(_radiusIgnoredOthers.cbegin(), _radiusIgnoredOthers.cend(), other);
|
|
||||||
if (ignoredOtherIter != _radiusIgnoredOthers.cend()) {
|
|
||||||
_radiusIgnoredOthers.erase(ignoredOtherIter);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cfloat>
|
#include <cfloat>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <vector>
|
#include <unordered_set>
|
||||||
#include <queue>
|
#include <queue>
|
||||||
|
|
||||||
#include <QtCore/QJsonObject>
|
#include <QtCore/QJsonObject>
|
||||||
|
@ -45,7 +45,6 @@ public:
|
||||||
|
|
||||||
int parseData(ReceivedMessage& message) override;
|
int parseData(ReceivedMessage& message) override;
|
||||||
AvatarData& getAvatar() { return *_avatar; }
|
AvatarData& getAvatar() { return *_avatar; }
|
||||||
const AvatarData& getAvatar() const { return *_avatar; }
|
|
||||||
const AvatarData* getConstAvatarData() const { return _avatar.get(); }
|
const AvatarData* getConstAvatarData() const { return _avatar.get(); }
|
||||||
AvatarSharedPointer getAvatarSharedPointer() const { return _avatar; }
|
AvatarSharedPointer getAvatarSharedPointer() const { return _avatar; }
|
||||||
|
|
||||||
|
@ -91,11 +90,11 @@ public:
|
||||||
void loadJSONStats(QJsonObject& jsonObject) const;
|
void loadJSONStats(QJsonObject& jsonObject) const;
|
||||||
|
|
||||||
glm::vec3 getPosition() const { return _avatar ? _avatar->getWorldPosition() : glm::vec3(0); }
|
glm::vec3 getPosition() const { return _avatar ? _avatar->getWorldPosition() : glm::vec3(0); }
|
||||||
bool isRadiusIgnoring(const QUuid& other) const;
|
glm::vec3 getGlobalBoundingBoxCorner() const { return _avatar ? _avatar->getGlobalBoundingBoxCorner() : glm::vec3(0); }
|
||||||
void addToRadiusIgnoringSet(const QUuid& other);
|
bool isRadiusIgnoring(const QUuid& other) const { return _radiusIgnoredOthers.find(other) != _radiusIgnoredOthers.end(); }
|
||||||
void removeFromRadiusIgnoringSet(const QUuid& other);
|
void addToRadiusIgnoringSet(const QUuid& other) { _radiusIgnoredOthers.insert(other); }
|
||||||
|
void removeFromRadiusIgnoringSet(SharedNodePointer self, const QUuid& other);
|
||||||
void ignoreOther(SharedNodePointer self, SharedNodePointer other);
|
void ignoreOther(SharedNodePointer self, SharedNodePointer other);
|
||||||
void ignoreOther(const Node* self, const Node* other);
|
|
||||||
|
|
||||||
void readViewFrustumPacket(const QByteArray& message);
|
void readViewFrustumPacket(const QByteArray& message);
|
||||||
|
|
||||||
|
@ -167,7 +166,7 @@ private:
|
||||||
int _numOutOfOrderSends = 0;
|
int _numOutOfOrderSends = 0;
|
||||||
|
|
||||||
SimpleMovingAverage _avgOtherAvatarDataRate;
|
SimpleMovingAverage _avgOtherAvatarDataRate;
|
||||||
std::vector<QUuid> _radiusIgnoredOthers;
|
std::unordered_set<QUuid> _radiusIgnoredOthers;
|
||||||
ConicalViewFrustums _currentViewFrustums;
|
ConicalViewFrustums _currentViewFrustums;
|
||||||
|
|
||||||
int _recentOtherAvatarsInView { 0 };
|
int _recentOtherAvatarsInView { 0 };
|
||||||
|
|
|
@ -13,7 +13,6 @@
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <random>
|
#include <random>
|
||||||
#include <chrono>
|
|
||||||
|
|
||||||
#include <glm/glm.hpp>
|
#include <glm/glm.hpp>
|
||||||
#include <glm/gtx/norm.hpp>
|
#include <glm/gtx/norm.hpp>
|
||||||
|
@ -34,8 +33,6 @@
|
||||||
#include "AvatarMixer.h"
|
#include "AvatarMixer.h"
|
||||||
#include "AvatarMixerClientData.h"
|
#include "AvatarMixerClientData.h"
|
||||||
|
|
||||||
namespace chrono = std::chrono;
|
|
||||||
|
|
||||||
void AvatarMixerSlave::configure(ConstIter begin, ConstIter end) {
|
void AvatarMixerSlave::configure(ConstIter begin, ConstIter end) {
|
||||||
_begin = begin;
|
_begin = begin;
|
||||||
_end = end;
|
_end = end;
|
||||||
|
@ -212,18 +209,7 @@ void AvatarMixerSlave::broadcastAvatarData(const SharedNodePointer& node) {
|
||||||
_stats.jobElapsedTime += (end - start);
|
_stats.jobElapsedTime += (end - start);
|
||||||
}
|
}
|
||||||
|
|
||||||
AABox computeBubbleBox(const AvatarData& avatar, float bubbleExpansionFactor) {
|
|
||||||
AABox box = avatar.getGlobalBoundingBox();
|
|
||||||
glm::vec3 scale = box.getScale();
|
|
||||||
scale *= bubbleExpansionFactor;
|
|
||||||
const glm::vec3 MIN_BUBBLE_SCALE(0.3f, 1.3f, 0.3);
|
|
||||||
scale = glm::max(scale, MIN_BUBBLE_SCALE);
|
|
||||||
box.setScaleStayCentered(glm::max(scale, MIN_BUBBLE_SCALE));
|
|
||||||
return box;
|
|
||||||
}
|
|
||||||
|
|
||||||
void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node) {
|
void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node) {
|
||||||
const Node* destinationNode = node.data();
|
|
||||||
|
|
||||||
auto nodeList = DependencyManager::get<NodeList>();
|
auto nodeList = DependencyManager::get<NodeList>();
|
||||||
|
|
||||||
|
@ -234,7 +220,7 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node)
|
||||||
|
|
||||||
_stats.nodesBroadcastedTo++;
|
_stats.nodesBroadcastedTo++;
|
||||||
|
|
||||||
AvatarMixerClientData* nodeData = reinterpret_cast<AvatarMixerClientData*>(destinationNode->getLinkedData());
|
AvatarMixerClientData* nodeData = reinterpret_cast<AvatarMixerClientData*>(node->getLinkedData());
|
||||||
|
|
||||||
nodeData->resetInViewStats();
|
nodeData->resetInViewStats();
|
||||||
|
|
||||||
|
@ -256,8 +242,12 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node)
|
||||||
int traitBytesSent = 0;
|
int traitBytesSent = 0;
|
||||||
|
|
||||||
// max number of avatarBytes per frame
|
// max number of avatarBytes per frame
|
||||||
int maxAvatarBytesPerFrame = int(_maxKbpsPerNode * BYTES_PER_KILOBIT / AVATAR_MIXER_BROADCAST_FRAMES_PER_SECOND);
|
auto maxAvatarBytesPerFrame = (_maxKbpsPerNode * BYTES_PER_KILOBIT) / AVATAR_MIXER_BROADCAST_FRAMES_PER_SECOND;
|
||||||
|
|
||||||
|
// FIXME - find a way to not send the sessionID for every avatar
|
||||||
|
int minimumBytesPerAvatar = AvatarDataPacket::AVATAR_HAS_FLAGS_SIZE + NUM_BYTES_RFC4122_UUID;
|
||||||
|
|
||||||
|
int overBudgetAvatars = 0;
|
||||||
|
|
||||||
// keep track of the number of other avatars held back in this frame
|
// keep track of the number of other avatars held back in this frame
|
||||||
int numAvatarsHeldBack = 0;
|
int numAvatarsHeldBack = 0;
|
||||||
|
@ -270,38 +260,66 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node)
|
||||||
bool PALIsOpen = nodeData->getRequestsDomainListData();
|
bool PALIsOpen = nodeData->getRequestsDomainListData();
|
||||||
|
|
||||||
// When this is true, the AvatarMixer will send Avatar data to a client about avatars that have ignored them
|
// When this is true, the AvatarMixer will send Avatar data to a client about avatars that have ignored them
|
||||||
bool getsAnyIgnored = PALIsOpen && destinationNode->getCanKick();
|
bool getsAnyIgnored = PALIsOpen && node->getCanKick();
|
||||||
|
|
||||||
// Bandwidth allowance for data that must be sent.
|
if (PALIsOpen) {
|
||||||
int minimumBytesPerAvatar = PALIsOpen ? AvatarDataPacket::AVATAR_HAS_FLAGS_SIZE + NUM_BYTES_RFC4122_UUID +
|
// Increase minimumBytesPerAvatar if the PAL is open
|
||||||
sizeof(AvatarDataPacket::AvatarGlobalPosition) + sizeof(AvatarDataPacket::AudioLoudness) : 0;
|
minimumBytesPerAvatar += sizeof(AvatarDataPacket::AvatarGlobalPosition) +
|
||||||
|
sizeof(AvatarDataPacket::AudioLoudness);
|
||||||
|
}
|
||||||
|
|
||||||
// setup a PacketList for the avatarPackets
|
// setup a PacketList for the avatarPackets
|
||||||
auto avatarPacketList = NLPacketList::create(PacketType::BulkAvatarData);
|
auto avatarPacketList = NLPacketList::create(PacketType::BulkAvatarData);
|
||||||
static auto maxAvatarDataBytes = avatarPacketList->getMaxSegmentSize() - NUM_BYTES_RFC4122_UUID;
|
|
||||||
|
|
||||||
// compute node bounding box
|
// Define the minimum bubble size
|
||||||
const float MY_AVATAR_BUBBLE_EXPANSION_FACTOR = 4.0f; // magic number determined emperically
|
static const glm::vec3 minBubbleSize = avatar.getSensorToWorldScale() * glm::vec3(0.3f, 1.3f, 0.3f);
|
||||||
AABox nodeBox = computeBubbleBox(avatar, MY_AVATAR_BUBBLE_EXPANSION_FACTOR);
|
// Define the scale of the box for the current node
|
||||||
|
glm::vec3 nodeBoxScale = (nodeData->getPosition() - nodeData->getGlobalBoundingBoxCorner()) * 2.0f * avatar.getSensorToWorldScale();
|
||||||
|
// Set up the bounding box for the current node
|
||||||
|
AABox nodeBox(nodeData->getGlobalBoundingBoxCorner(), nodeBoxScale);
|
||||||
|
// Clamp the size of the bounding box to a minimum scale
|
||||||
|
if (glm::any(glm::lessThan(nodeBoxScale, minBubbleSize))) {
|
||||||
|
nodeBox.setScaleStayCentered(minBubbleSize);
|
||||||
|
}
|
||||||
|
// Quadruple the scale of both bounding boxes
|
||||||
|
nodeBox.embiggen(4.0f);
|
||||||
|
|
||||||
|
|
||||||
|
// setup list of AvatarData as well as maps to map betweeen the AvatarData and the original nodes
|
||||||
|
std::vector<AvatarSharedPointer> avatarsToSort;
|
||||||
|
std::unordered_map<AvatarSharedPointer, SharedNodePointer> avatarDataToNodes;
|
||||||
|
std::unordered_map<QUuid, uint64_t> avatarEncodeTimes;
|
||||||
|
std::for_each(_begin, _end, [&](const SharedNodePointer& otherNode) {
|
||||||
|
// make sure this is an agent that we have avatar data for before considering it for inclusion
|
||||||
|
if (otherNode->getType() == NodeType::Agent
|
||||||
|
&& otherNode->getLinkedData()) {
|
||||||
|
const AvatarMixerClientData* otherNodeData = reinterpret_cast<const AvatarMixerClientData*>(otherNode->getLinkedData());
|
||||||
|
|
||||||
|
AvatarSharedPointer otherAvatar = otherNodeData->getAvatarSharedPointer();
|
||||||
|
avatarsToSort.push_back(otherAvatar);
|
||||||
|
avatarDataToNodes[otherAvatar] = otherNode;
|
||||||
|
QUuid id = otherAvatar->getSessionUUID();
|
||||||
|
avatarEncodeTimes[id] = nodeData->getLastOtherAvatarEncodeTime(id);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
class SortableAvatar: public PrioritySortUtil::Sortable {
|
class SortableAvatar: public PrioritySortUtil::Sortable {
|
||||||
public:
|
public:
|
||||||
SortableAvatar() = delete;
|
SortableAvatar() = delete;
|
||||||
SortableAvatar(const AvatarData* avatar, const Node* avatarNode, uint64_t lastEncodeTime)
|
SortableAvatar(const AvatarSharedPointer& avatar, uint64_t lastEncodeTime)
|
||||||
: _avatar(avatar), _node(avatarNode), _lastEncodeTime(lastEncodeTime) {}
|
: _avatar(avatar), _lastEncodeTime(lastEncodeTime) {}
|
||||||
glm::vec3 getPosition() const override { return _avatar->getClientGlobalPosition(); }
|
glm::vec3 getPosition() const override { return _avatar->getWorldPosition(); }
|
||||||
float getRadius() const override {
|
float getRadius() const override {
|
||||||
glm::vec3 nodeBoxScale = _avatar->getGlobalBoundingBox().getScale();
|
glm::vec3 nodeBoxHalfScale = (_avatar->getWorldPosition() - _avatar->getGlobalBoundingBoxCorner() * _avatar->getSensorToWorldScale());
|
||||||
return 0.5f * glm::max(nodeBoxScale.x, glm::max(nodeBoxScale.y, nodeBoxScale.z));
|
return glm::max(nodeBoxHalfScale.x, glm::max(nodeBoxHalfScale.y, nodeBoxHalfScale.z));
|
||||||
}
|
}
|
||||||
uint64_t getTimestamp() const override {
|
uint64_t getTimestamp() const override {
|
||||||
return _lastEncodeTime;
|
return _lastEncodeTime;
|
||||||
}
|
}
|
||||||
const Node* getNode() const { return _node; }
|
AvatarSharedPointer getAvatar() const { return _avatar; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const AvatarData* _avatar;
|
AvatarSharedPointer _avatar;
|
||||||
const Node* _node;
|
|
||||||
uint64_t _lastEncodeTime;
|
uint64_t _lastEncodeTime;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -311,18 +329,16 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node)
|
||||||
AvatarData::_avatarSortCoefficientSize,
|
AvatarData::_avatarSortCoefficientSize,
|
||||||
AvatarData::_avatarSortCoefficientCenter,
|
AvatarData::_avatarSortCoefficientCenter,
|
||||||
AvatarData::_avatarSortCoefficientAge);
|
AvatarData::_avatarSortCoefficientAge);
|
||||||
sortedAvatars.reserve(_end - _begin);
|
sortedAvatars.reserve(avatarsToSort.size());
|
||||||
|
|
||||||
for (auto listedNode = _begin; listedNode != _end; ++listedNode) {
|
// ignore or sort
|
||||||
Node* otherNodeRaw = (*listedNode).data();
|
const AvatarSharedPointer& thisAvatar = nodeData->getAvatarSharedPointer();
|
||||||
if (otherNodeRaw->getType() != NodeType::Agent
|
for (const auto& avatar : avatarsToSort) {
|
||||||
|| !otherNodeRaw->getLinkedData()
|
if (avatar == thisAvatar) {
|
||||||
|| otherNodeRaw == destinationNode) {
|
// don't echo updates to self
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto avatarNode = otherNodeRaw;
|
|
||||||
|
|
||||||
bool shouldIgnore = false;
|
bool shouldIgnore = false;
|
||||||
// We ignore other nodes for a couple of reasons:
|
// We ignore other nodes for a couple of reasons:
|
||||||
// 1) ignore bubbles and ignore specific node
|
// 1) ignore bubbles and ignore specific node
|
||||||
|
@ -330,39 +346,53 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node)
|
||||||
// happen if for example the avatar is connected on a desktop and sending
|
// happen if for example the avatar is connected on a desktop and sending
|
||||||
// updates at ~30hz. So every 3 frames we skip a frame.
|
// updates at ~30hz. So every 3 frames we skip a frame.
|
||||||
|
|
||||||
|
auto avatarNode = avatarDataToNodes[avatar];
|
||||||
assert(avatarNode); // we can't have gotten here without the avatarData being a valid key in the map
|
assert(avatarNode); // we can't have gotten here without the avatarData being a valid key in the map
|
||||||
|
|
||||||
const AvatarMixerClientData* avatarClientNodeData = reinterpret_cast<const AvatarMixerClientData*>(avatarNode->getLinkedData());
|
const AvatarMixerClientData* avatarNodeData = reinterpret_cast<const AvatarMixerClientData*>(avatarNode->getLinkedData());
|
||||||
assert(avatarClientNodeData); // we can't have gotten here without avatarNode having valid data
|
assert(avatarNodeData); // we can't have gotten here without avatarNode having valid data
|
||||||
quint64 startIgnoreCalculation = usecTimestampNow();
|
quint64 startIgnoreCalculation = usecTimestampNow();
|
||||||
|
|
||||||
// make sure we have data for this avatar, that it isn't the same node,
|
// make sure we have data for this avatar, that it isn't the same node,
|
||||||
// and isn't an avatar that the viewing node has ignored
|
// and isn't an avatar that the viewing node has ignored
|
||||||
// or that has ignored the viewing node
|
// or that has ignored the viewing node
|
||||||
if ((destinationNode->isIgnoringNodeWithID(avatarNode->getUUID()) && !PALIsOpen)
|
if (!avatarNode->getLinkedData()
|
||||||
|| (avatarNode->isIgnoringNodeWithID(destinationNode->getUUID()) && !getsAnyIgnored)) {
|
|| avatarNode->getUUID() == node->getUUID()
|
||||||
|
|| (node->isIgnoringNodeWithID(avatarNode->getUUID()) && !PALIsOpen)
|
||||||
|
|| (avatarNode->isIgnoringNodeWithID(node->getUUID()) && !getsAnyIgnored)) {
|
||||||
shouldIgnore = true;
|
shouldIgnore = true;
|
||||||
} else {
|
} else {
|
||||||
// Check to see if the space bubble is enabled
|
// Check to see if the space bubble is enabled
|
||||||
// Don't bother with these checks if the other avatar has their bubble enabled and we're gettingAnyIgnored
|
// Don't bother with these checks if the other avatar has their bubble enabled and we're gettingAnyIgnored
|
||||||
if (destinationNode->isIgnoreRadiusEnabled() || (avatarNode->isIgnoreRadiusEnabled() && !getsAnyIgnored)) {
|
if (node->isIgnoreRadiusEnabled() || (avatarNode->isIgnoreRadiusEnabled() && !getsAnyIgnored)) {
|
||||||
|
float sensorToWorldScale = avatarNodeData->getAvatarSharedPointer()->getSensorToWorldScale();
|
||||||
|
// Define the scale of the box for the current other node
|
||||||
|
glm::vec3 otherNodeBoxScale = (avatarNodeData->getPosition() - avatarNodeData->getGlobalBoundingBoxCorner()) * 2.0f * sensorToWorldScale;
|
||||||
|
// Set up the bounding box for the current other node
|
||||||
|
AABox otherNodeBox(avatarNodeData->getGlobalBoundingBoxCorner(), otherNodeBoxScale);
|
||||||
|
// Clamp the size of the bounding box to a minimum scale
|
||||||
|
if (glm::any(glm::lessThan(otherNodeBoxScale, minBubbleSize))) {
|
||||||
|
otherNodeBox.setScaleStayCentered(minBubbleSize);
|
||||||
|
}
|
||||||
|
// Change the scale of both bounding boxes
|
||||||
|
// (This is an arbitrary number determined empirically)
|
||||||
|
otherNodeBox.embiggen(2.4f);
|
||||||
|
|
||||||
// Perform the collision check between the two bounding boxes
|
// Perform the collision check between the two bounding boxes
|
||||||
const float OTHER_AVATAR_BUBBLE_EXPANSION_FACTOR = 2.4f; // magic number determined empirically
|
|
||||||
AABox otherNodeBox = computeBubbleBox(avatarClientNodeData->getAvatar(), OTHER_AVATAR_BUBBLE_EXPANSION_FACTOR);
|
|
||||||
if (nodeBox.touches(otherNodeBox)) {
|
if (nodeBox.touches(otherNodeBox)) {
|
||||||
nodeData->ignoreOther(destinationNode, avatarNode);
|
nodeData->ignoreOther(node, avatarNode);
|
||||||
shouldIgnore = !getsAnyIgnored;
|
shouldIgnore = !getsAnyIgnored;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Not close enough to ignore
|
// Not close enough to ignore
|
||||||
if (!shouldIgnore) {
|
if (!shouldIgnore) {
|
||||||
nodeData->removeFromRadiusIgnoringSet(avatarNode->getUUID());
|
nodeData->removeFromRadiusIgnoringSet(node, avatarNode->getUUID());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!shouldIgnore) {
|
if (!shouldIgnore) {
|
||||||
AvatarDataSequenceNumber lastSeqToReceiver = nodeData->getLastBroadcastSequenceNumber(avatarNode->getUUID());
|
AvatarDataSequenceNumber lastSeqToReceiver = nodeData->getLastBroadcastSequenceNumber(avatarNode->getUUID());
|
||||||
AvatarDataSequenceNumber lastSeqFromSender = avatarClientNodeData->getLastReceivedSequenceNumber();
|
AvatarDataSequenceNumber lastSeqFromSender = avatarNodeData->getLastReceivedSequenceNumber();
|
||||||
|
|
||||||
// FIXME - This code does appear to be working. But it seems brittle.
|
// FIXME - This code does appear to be working. But it seems brittle.
|
||||||
// It supports determining if the frame of data for this "other"
|
// It supports determining if the frame of data for this "other"
|
||||||
|
@ -387,10 +417,12 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node)
|
||||||
|
|
||||||
if (!shouldIgnore) {
|
if (!shouldIgnore) {
|
||||||
// sort this one for later
|
// sort this one for later
|
||||||
const AvatarData* avatarNodeData = avatarClientNodeData->getConstAvatarData();
|
uint64_t lastEncodeTime = 0;
|
||||||
auto lastEncodeTime = nodeData->getLastOtherAvatarEncodeTime(avatarNodeData->getSessionUUID());
|
std::unordered_map<QUuid, uint64_t>::const_iterator itr = avatarEncodeTimes.find(avatar->getSessionUUID());
|
||||||
|
if (itr != avatarEncodeTimes.end()) {
|
||||||
sortedAvatars.push(SortableAvatar(avatarNodeData, avatarNode, lastEncodeTime));
|
lastEncodeTime = itr->second;
|
||||||
|
}
|
||||||
|
sortedAvatars.push(SortableAvatar(avatar, lastEncodeTime));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -398,31 +430,19 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node)
|
||||||
|
|
||||||
int remainingAvatars = (int)sortedAvatars.size();
|
int remainingAvatars = (int)sortedAvatars.size();
|
||||||
auto traitsPacketList = NLPacketList::create(PacketType::BulkAvatarTraits, QByteArray(), true, true);
|
auto traitsPacketList = NLPacketList::create(PacketType::BulkAvatarTraits, QByteArray(), true, true);
|
||||||
|
|
||||||
const auto& sortedAvatarVector = sortedAvatars.getSortedVector();
|
const auto& sortedAvatarVector = sortedAvatars.getSortedVector();
|
||||||
for (const auto& sortedAvatar : sortedAvatarVector) {
|
for (const auto& sortedAvatar : sortedAvatarVector) {
|
||||||
const Node* otherNode = sortedAvatar.getNode();
|
const auto& avatarData = sortedAvatar.getAvatar();
|
||||||
auto lastEncodeForOther = sortedAvatar.getTimestamp();
|
remainingAvatars--;
|
||||||
|
|
||||||
|
auto otherNode = avatarDataToNodes[avatarData];
|
||||||
assert(otherNode); // we can't have gotten here without the avatarData being a valid key in the map
|
assert(otherNode); // we can't have gotten here without the avatarData being a valid key in the map
|
||||||
|
|
||||||
AvatarData::AvatarDataDetail detail = AvatarData::NoData;
|
// NOTE: Here's where we determine if we are over budget and drop to bare minimum data
|
||||||
|
|
||||||
// NOTE: Here's where we determine if we are over budget and drop remaining avatars,
|
|
||||||
// or send minimal avatar data in uncommon case of PALIsOpen.
|
|
||||||
int minimRemainingAvatarBytes = minimumBytesPerAvatar * remainingAvatars;
|
int minimRemainingAvatarBytes = minimumBytesPerAvatar * remainingAvatars;
|
||||||
bool overBudget = (identityBytesSent + numAvatarDataBytes + minimRemainingAvatarBytes) > maxAvatarBytesPerFrame;
|
bool overBudget = (identityBytesSent + numAvatarDataBytes + minimRemainingAvatarBytes) > maxAvatarBytesPerFrame;
|
||||||
if (overBudget) {
|
|
||||||
if (PALIsOpen) {
|
|
||||||
_stats.overBudgetAvatars++;
|
|
||||||
detail = AvatarData::PALMinimum;
|
|
||||||
} else {
|
|
||||||
_stats.overBudgetAvatars += remainingAvatars;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
auto startAvatarDataPacking = chrono::high_resolution_clock::now();
|
quint64 startAvatarDataPacking = usecTimestampNow();
|
||||||
|
|
||||||
++numOtherAvatars;
|
++numOtherAvatars;
|
||||||
|
|
||||||
|
@ -439,18 +459,32 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node)
|
||||||
nodeData->setLastBroadcastTime(otherNode->getUUID(), usecTimestampNow());
|
nodeData->setLastBroadcastTime(otherNode->getUUID(), usecTimestampNow());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Typically all out-of-view avatars but such avatars' priorities will rise with time:
|
// determine if avatar is in view which determines how much data to send
|
||||||
bool isLowerPriority = sortedAvatar.getPriority() <= OUT_OF_VIEW_THRESHOLD;
|
glm::vec3 otherPosition = otherAvatar->getClientGlobalPosition();
|
||||||
|
glm::vec3 otherNodeBoxScale = (otherPosition - otherNodeData->getGlobalBoundingBoxCorner()) * 2.0f * otherAvatar->getSensorToWorldScale();
|
||||||
|
AABox otherNodeBox(otherNodeData->getGlobalBoundingBoxCorner(), otherNodeBoxScale);
|
||||||
|
bool isInView = nodeData->otherAvatarInView(otherNodeBox);
|
||||||
|
|
||||||
if (isLowerPriority) {
|
// start a new segment in the PacketList for this avatar
|
||||||
|
avatarPacketList->startSegment();
|
||||||
|
|
||||||
|
AvatarData::AvatarDataDetail detail;
|
||||||
|
|
||||||
|
if (overBudget) {
|
||||||
|
overBudgetAvatars++;
|
||||||
|
_stats.overBudgetAvatars++;
|
||||||
|
detail = PALIsOpen ? AvatarData::PALMinimum : AvatarData::NoData;
|
||||||
|
} else if (!isInView) {
|
||||||
detail = PALIsOpen ? AvatarData::PALMinimum : AvatarData::MinimumData;
|
detail = PALIsOpen ? AvatarData::PALMinimum : AvatarData::MinimumData;
|
||||||
nodeData->incrementAvatarOutOfView();
|
nodeData->incrementAvatarOutOfView();
|
||||||
} else if (!overBudget) {
|
} else {
|
||||||
detail = distribution(generator) < AVATAR_SEND_FULL_UPDATE_RATIO ? AvatarData::SendAllData : AvatarData::CullSmallData;
|
detail = distribution(generator) < AVATAR_SEND_FULL_UPDATE_RATIO
|
||||||
|
? AvatarData::SendAllData : AvatarData::CullSmallData;
|
||||||
nodeData->incrementAvatarInView();
|
nodeData->incrementAvatarInView();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool includeThisAvatar = true;
|
bool includeThisAvatar = true;
|
||||||
|
auto lastEncodeForOther = nodeData->getLastOtherAvatarEncodeTime(otherNode->getUUID());
|
||||||
QVector<JointData>& lastSentJointsForOther = nodeData->getLastOtherAvatarSentJoints(otherNode->getUUID());
|
QVector<JointData>& lastSentJointsForOther = nodeData->getLastOtherAvatarSentJoints(otherNode->getUUID());
|
||||||
|
|
||||||
lastSentJointsForOther.resize(otherAvatar->getJointCount());
|
lastSentJointsForOther.resize(otherAvatar->getJointCount());
|
||||||
|
@ -460,14 +494,14 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node)
|
||||||
AvatarDataPacket::HasFlags hasFlagsOut; // the result of the toByteArray
|
AvatarDataPacket::HasFlags hasFlagsOut; // the result of the toByteArray
|
||||||
bool dropFaceTracking = false;
|
bool dropFaceTracking = false;
|
||||||
|
|
||||||
auto startSerialize = chrono::high_resolution_clock::now();
|
quint64 start = usecTimestampNow();
|
||||||
QByteArray bytes = otherAvatar->toByteArray(detail, lastEncodeForOther, lastSentJointsForOther,
|
QByteArray bytes = otherAvatar->toByteArray(detail, lastEncodeForOther, lastSentJointsForOther,
|
||||||
hasFlagsOut, dropFaceTracking, distanceAdjust, viewerPosition,
|
hasFlagsOut, dropFaceTracking, distanceAdjust, viewerPosition,
|
||||||
&lastSentJointsForOther);
|
&lastSentJointsForOther);
|
||||||
auto endSerialize = chrono::high_resolution_clock::now();
|
quint64 end = usecTimestampNow();
|
||||||
_stats.toByteArrayElapsedTime +=
|
_stats.toByteArrayElapsedTime += (end - start);
|
||||||
(quint64) chrono::duration_cast<chrono::microseconds>(endSerialize - startSerialize).count();
|
|
||||||
|
|
||||||
|
static auto maxAvatarDataBytes = avatarPacketList->getMaxSegmentSize() - NUM_BYTES_RFC4122_UUID;
|
||||||
if (bytes.size() > maxAvatarDataBytes) {
|
if (bytes.size() > maxAvatarDataBytes) {
|
||||||
qCWarning(avatars) << "otherAvatar.toByteArray() for" << otherNode->getUUID()
|
qCWarning(avatars) << "otherAvatar.toByteArray() for" << otherNode->getUUID()
|
||||||
<< "resulted in very large buffer of" << bytes.size() << "bytes - dropping facial data";
|
<< "resulted in very large buffer of" << bytes.size() << "bytes - dropping facial data";
|
||||||
|
@ -493,11 +527,8 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (includeThisAvatar) {
|
if (includeThisAvatar) {
|
||||||
// start a new segment in the PacketList for this avatar
|
|
||||||
avatarPacketList->startSegment();
|
|
||||||
numAvatarDataBytes += avatarPacketList->write(otherNode->getUUID().toRfc4122());
|
numAvatarDataBytes += avatarPacketList->write(otherNode->getUUID().toRfc4122());
|
||||||
numAvatarDataBytes += avatarPacketList->write(bytes);
|
numAvatarDataBytes += avatarPacketList->write(bytes);
|
||||||
avatarPacketList->endSegment();
|
|
||||||
|
|
||||||
if (detail != AvatarData::NoData) {
|
if (detail != AvatarData::NoData) {
|
||||||
_stats.numOthersIncluded++;
|
_stats.numOthersIncluded++;
|
||||||
|
@ -515,13 +546,15 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node)
|
||||||
// It would be nice if we could tweak its future sort priority to put it at the back of the list.
|
// It would be nice if we could tweak its future sort priority to put it at the back of the list.
|
||||||
}
|
}
|
||||||
|
|
||||||
auto endAvatarDataPacking = chrono::high_resolution_clock::now();
|
avatarPacketList->endSegment();
|
||||||
_stats.avatarDataPackingElapsedTime +=
|
|
||||||
(quint64) chrono::duration_cast<chrono::microseconds>(endAvatarDataPacking - startAvatarDataPacking).count();
|
quint64 endAvatarDataPacking = usecTimestampNow();
|
||||||
|
_stats.avatarDataPackingElapsedTime += (endAvatarDataPacking - startAvatarDataPacking);
|
||||||
|
|
||||||
// use helper to add any changed traits to our packet list
|
// use helper to add any changed traits to our packet list
|
||||||
traitBytesSent += addChangedTraitsToBulkPacket(nodeData, otherNodeData, *traitsPacketList);
|
traitBytesSent += addChangedTraitsToBulkPacket(nodeData, otherNodeData, *traitsPacketList);
|
||||||
remainingAvatars--;
|
|
||||||
|
traitsPacketList->getDataSize();
|
||||||
}
|
}
|
||||||
|
|
||||||
quint64 startPacketSending = usecTimestampNow();
|
quint64 startPacketSending = usecTimestampNow();
|
||||||
|
@ -533,7 +566,7 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node)
|
||||||
_stats.numBytesSent += numAvatarDataBytes;
|
_stats.numBytesSent += numAvatarDataBytes;
|
||||||
|
|
||||||
// send the avatar data PacketList
|
// send the avatar data PacketList
|
||||||
nodeList->sendPacketList(std::move(avatarPacketList), *destinationNode);
|
nodeList->sendPacketList(std::move(avatarPacketList), *node);
|
||||||
|
|
||||||
// record the bytes sent for other avatar data in the AvatarMixerClientData
|
// record the bytes sent for other avatar data in the AvatarMixerClientData
|
||||||
nodeData->recordSentAvatarData(numAvatarDataBytes);
|
nodeData->recordSentAvatarData(numAvatarDataBytes);
|
||||||
|
@ -543,7 +576,7 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node)
|
||||||
|
|
||||||
if (traitsPacketList->getNumPackets() >= 1) {
|
if (traitsPacketList->getNumPackets() >= 1) {
|
||||||
// send the traits packet list
|
// send the traits packet list
|
||||||
nodeList->sendPacketList(std::move(traitsPacketList), *destinationNode);
|
nodeList->sendPacketList(std::move(traitsPacketList), *node);
|
||||||
}
|
}
|
||||||
|
|
||||||
// record the number of avatars held back this frame
|
// record the number of avatars held back this frame
|
||||||
|
|
Binary file not shown.
Binary file not shown.
Before Width: | Height: | Size: 4.4 KiB |
Binary file not shown.
|
@ -346,15 +346,7 @@ Item {
|
||||||
target: loginDialog
|
target: loginDialog
|
||||||
onHandleLoginCompleted: {
|
onHandleLoginCompleted: {
|
||||||
console.log("Login Succeeded, linking steam account")
|
console.log("Login Succeeded, linking steam account")
|
||||||
var poppedUp = Settings.getValue("loginDialogPoppedUp", false);
|
|
||||||
if (poppedUp) {
|
|
||||||
console.log("[ENCOURAGELOGINDIALOG]: logging in")
|
|
||||||
var data = {
|
|
||||||
"action": "user logged in"
|
|
||||||
};
|
|
||||||
UserActivityLogger.logAction("encourageLoginDialog", data);
|
|
||||||
Settings.setValue("loginDialogPoppedUp", false);
|
|
||||||
}
|
|
||||||
if (loginDialog.isSteamRunning()) {
|
if (loginDialog.isSteamRunning()) {
|
||||||
loginDialog.linkSteam()
|
loginDialog.linkSteam()
|
||||||
} else {
|
} else {
|
||||||
|
@ -362,20 +354,23 @@ Item {
|
||||||
bodyLoader.item.width = root.pane.width
|
bodyLoader.item.width = root.pane.width
|
||||||
bodyLoader.item.height = root.pane.height
|
bodyLoader.item.height = root.pane.height
|
||||||
}
|
}
|
||||||
|
if (Settings.getValue("loginDialogPoppedUp", false)) {
|
||||||
|
var data = {
|
||||||
|
"action": "user logged in"
|
||||||
|
};
|
||||||
|
UserActivityLogger.logAction("encourageLoginDialog", data);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
onHandleLoginFailed: {
|
onHandleLoginFailed: {
|
||||||
console.log("Login Failed")
|
console.log("Login Failed")
|
||||||
var poppedUp = Settings.getValue("loginDialogPoppedUp", false);
|
mainTextContainer.visible = true
|
||||||
if (poppedUp) {
|
toggleLoading(false)
|
||||||
console.log("[ENCOURAGELOGINDIALOG]: failed logging in")
|
if (Settings.getValue("loginDialogPoppedUp", false)) {
|
||||||
var data = {
|
var data = {
|
||||||
"action": "user failed logging in"
|
"action": "user failed logging in"
|
||||||
};
|
};
|
||||||
UserActivityLogger.logAction("encourageLoginDialog", data);
|
UserActivityLogger.logAction("encourageLoginDialog", data);
|
||||||
Settings.setValue("loginDialogPoppedUp", false);
|
|
||||||
}
|
}
|
||||||
mainTextContainer.visible = true
|
|
||||||
toggleLoading(false)
|
|
||||||
}
|
}
|
||||||
onHandleLinkCompleted: {
|
onHandleLinkCompleted: {
|
||||||
console.log("Link Succeeded")
|
console.log("Link Succeeded")
|
||||||
|
|
|
@ -3498,9 +3498,7 @@ bool Application::isServerlessMode() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Application::setIsInterstitialMode(bool interstitialMode) {
|
void Application::setIsInterstitialMode(bool interstitialMode) {
|
||||||
Settings settings;
|
if (_interstitialMode != interstitialMode) {
|
||||||
bool enableInterstitial = settings.value("enableIntersitialMode", false).toBool();
|
|
||||||
if (_interstitialMode != interstitialMode && enableInterstitial) {
|
|
||||||
_interstitialMode = interstitialMode;
|
_interstitialMode = interstitialMode;
|
||||||
|
|
||||||
DependencyManager::get<AudioClient>()->setAudioPaused(_interstitialMode);
|
DependencyManager::get<AudioClient>()->setAudioPaused(_interstitialMode);
|
||||||
|
@ -6348,7 +6346,6 @@ void Application::updateWindowTitle() const {
|
||||||
|
|
||||||
auto nodeList = DependencyManager::get<NodeList>();
|
auto nodeList = DependencyManager::get<NodeList>();
|
||||||
auto accountManager = DependencyManager::get<AccountManager>();
|
auto accountManager = DependencyManager::get<AccountManager>();
|
||||||
auto isInErrorState = nodeList->getDomainHandler().isInErrorState();
|
|
||||||
|
|
||||||
QString buildVersion = " - "
|
QString buildVersion = " - "
|
||||||
+ (BuildInfo::BUILD_TYPE == BuildInfo::BuildType::Stable ? QString("Version") : QString("Build"))
|
+ (BuildInfo::BUILD_TYPE == BuildInfo::BuildType::Stable ? QString("Version") : QString("Build"))
|
||||||
|
@ -6356,19 +6353,14 @@ void Application::updateWindowTitle() const {
|
||||||
|
|
||||||
QString loginStatus = accountManager->isLoggedIn() ? "" : " (NOT LOGGED IN)";
|
QString loginStatus = accountManager->isLoggedIn() ? "" : " (NOT LOGGED IN)";
|
||||||
|
|
||||||
QString connectionStatus = isInErrorState ? " (ERROR CONNECTING)" :
|
QString connectionStatus = nodeList->getDomainHandler().isConnected() ? "" : " (NOT CONNECTED)";
|
||||||
nodeList->getDomainHandler().isConnected() ? "" : " (NOT CONNECTED)";
|
|
||||||
QString username = accountManager->getAccountInfo().getUsername();
|
QString username = accountManager->getAccountInfo().getUsername();
|
||||||
|
|
||||||
setCrashAnnotation("username", username.toStdString());
|
setCrashAnnotation("username", username.toStdString());
|
||||||
|
|
||||||
QString currentPlaceName;
|
QString currentPlaceName;
|
||||||
if (isServerlessMode()) {
|
if (isServerlessMode()) {
|
||||||
if (isInErrorState) {
|
|
||||||
currentPlaceName = "serverless: " + nodeList->getDomainHandler().getErrorDomainURL().toString();
|
|
||||||
} else {
|
|
||||||
currentPlaceName = "serverless: " + DependencyManager::get<AddressManager>()->getDomainURL().toString();
|
currentPlaceName = "serverless: " + DependencyManager::get<AddressManager>()->getDomainURL().toString();
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
currentPlaceName = DependencyManager::get<AddressManager>()->getDomainURL().host();
|
currentPlaceName = DependencyManager::get<AddressManager>()->getDomainURL().host();
|
||||||
if (currentPlaceName.isEmpty()) {
|
if (currentPlaceName.isEmpty()) {
|
||||||
|
|
|
@ -176,29 +176,29 @@ float AvatarManager::getAvatarSimulationRate(const QUuid& sessionID, const QStri
|
||||||
}
|
}
|
||||||
|
|
||||||
void AvatarManager::updateOtherAvatars(float deltaTime) {
|
void AvatarManager::updateOtherAvatars(float deltaTime) {
|
||||||
{
|
|
||||||
// lock the hash for read to check the size
|
// lock the hash for read to check the size
|
||||||
QReadLocker lock(&_hashLock);
|
QReadLocker lock(&_hashLock);
|
||||||
if (_avatarHash.size() < 2 && _avatarsToFade.isEmpty()) {
|
if (_avatarHash.size() < 2 && _avatarsToFade.isEmpty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
lock.unlock();
|
||||||
|
|
||||||
PerformanceTimer perfTimer("otherAvatars");
|
PerformanceTimer perfTimer("otherAvatars");
|
||||||
|
|
||||||
class SortableAvatar: public PrioritySortUtil::Sortable {
|
class SortableAvatar: public PrioritySortUtil::Sortable {
|
||||||
public:
|
public:
|
||||||
SortableAvatar() = delete;
|
SortableAvatar() = delete;
|
||||||
SortableAvatar(const std::shared_ptr<Avatar>& avatar) : _avatar(avatar) {}
|
SortableAvatar(const AvatarSharedPointer& avatar) : _avatar(avatar) {}
|
||||||
glm::vec3 getPosition() const override { return _avatar->getWorldPosition(); }
|
glm::vec3 getPosition() const override { return _avatar->getWorldPosition(); }
|
||||||
float getRadius() const override { return _avatar->getBoundingRadius(); }
|
float getRadius() const override { return std::static_pointer_cast<Avatar>(_avatar)->getBoundingRadius(); }
|
||||||
uint64_t getTimestamp() const override { return _avatar->getLastRenderUpdateTime(); }
|
uint64_t getTimestamp() const override { return std::static_pointer_cast<Avatar>(_avatar)->getLastRenderUpdateTime(); }
|
||||||
std::shared_ptr<Avatar> getAvatar() const { return _avatar; }
|
AvatarSharedPointer getAvatar() const { return _avatar; }
|
||||||
private:
|
private:
|
||||||
std::shared_ptr<Avatar> _avatar;
|
AvatarSharedPointer _avatar;
|
||||||
};
|
};
|
||||||
|
|
||||||
auto avatarMap = getHashCopy();
|
auto avatarMap = getHashCopy();
|
||||||
|
AvatarHash::iterator itr = avatarMap.begin();
|
||||||
|
|
||||||
const auto& views = qApp->getConicalViews();
|
const auto& views = qApp->getConicalViews();
|
||||||
PrioritySortUtil::PriorityQueue<SortableAvatar> sortedAvatars(views,
|
PrioritySortUtil::PriorityQueue<SortableAvatar> sortedAvatars(views,
|
||||||
|
@ -207,24 +207,22 @@ void AvatarManager::updateOtherAvatars(float deltaTime) {
|
||||||
AvatarData::_avatarSortCoefficientAge);
|
AvatarData::_avatarSortCoefficientAge);
|
||||||
sortedAvatars.reserve(avatarMap.size() - 1); // don't include MyAvatar
|
sortedAvatars.reserve(avatarMap.size() - 1); // don't include MyAvatar
|
||||||
|
|
||||||
// Build vector and compute priorities
|
// sort
|
||||||
auto nodeList = DependencyManager::get<NodeList>();
|
|
||||||
AvatarHash::iterator itr = avatarMap.begin();
|
|
||||||
while (itr != avatarMap.end()) {
|
while (itr != avatarMap.end()) {
|
||||||
const auto& avatar = std::static_pointer_cast<Avatar>(*itr);
|
const auto& avatar = std::static_pointer_cast<Avatar>(*itr);
|
||||||
// DO NOT update _myAvatar! Its update has already been done earlier in the main loop.
|
// DO NOT update _myAvatar! Its update has already been done earlier in the main loop.
|
||||||
// DO NOT update or fade out uninitialized Avatars
|
// DO NOT update or fade out uninitialized Avatars
|
||||||
if (avatar != _myAvatar && avatar->isInitialized() && !nodeList->isPersonalMutingNode(avatar->getID())) {
|
if (avatar != _myAvatar && avatar->isInitialized()) {
|
||||||
sortedAvatars.push(SortableAvatar(avatar));
|
sortedAvatars.push(SortableAvatar(avatar));
|
||||||
}
|
}
|
||||||
++itr;
|
++itr;
|
||||||
}
|
}
|
||||||
// Sort
|
|
||||||
const auto& sortedAvatarVector = sortedAvatars.getSortedVector();
|
const auto& sortedAvatarVector = sortedAvatars.getSortedVector();
|
||||||
|
|
||||||
// process in sorted order
|
// process in sorted order
|
||||||
uint64_t startTime = usecTimestampNow();
|
uint64_t startTime = usecTimestampNow();
|
||||||
uint64_t updateExpiry = startTime + MAX_UPDATE_AVATARS_TIME_BUDGET;
|
const uint64_t UPDATE_BUDGET = 2000; // usec
|
||||||
|
uint64_t updateExpiry = startTime + UPDATE_BUDGET;
|
||||||
int numAvatarsUpdated = 0;
|
int numAvatarsUpdated = 0;
|
||||||
int numAVatarsNotUpdated = 0;
|
int numAVatarsNotUpdated = 0;
|
||||||
|
|
||||||
|
@ -243,12 +241,18 @@ void AvatarManager::updateOtherAvatars(float deltaTime) {
|
||||||
avatar->updateOrbPosition();
|
avatar->updateOrbPosition();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ignoring = DependencyManager::get<NodeList>()->isPersonalMutingNode(avatar->getID());
|
||||||
|
if (ignoring) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// for ALL avatars...
|
// for ALL avatars...
|
||||||
if (_shouldRender) {
|
if (_shouldRender) {
|
||||||
avatar->ensureInScene(avatar, qApp->getMain3DScene());
|
avatar->ensureInScene(avatar, qApp->getMain3DScene());
|
||||||
}
|
}
|
||||||
avatar->animateScaleChanges(deltaTime);
|
avatar->animateScaleChanges(deltaTime);
|
||||||
|
|
||||||
|
const float OUT_OF_VIEW_THRESHOLD = 0.5f * AvatarData::OUT_OF_VIEW_PENALTY;
|
||||||
uint64_t now = usecTimestampNow();
|
uint64_t now = usecTimestampNow();
|
||||||
if (now < updateExpiry) {
|
if (now < updateExpiry) {
|
||||||
// we're within budget
|
// we're within budget
|
||||||
|
@ -269,7 +273,7 @@ void AvatarManager::updateOtherAvatars(float deltaTime) {
|
||||||
// no time to simulate, but we take the time to count how many were tragically missed
|
// no time to simulate, but we take the time to count how many were tragically missed
|
||||||
while (it != sortedAvatarVector.end()) {
|
while (it != sortedAvatarVector.end()) {
|
||||||
const SortableAvatar& newSortData = *it;
|
const SortableAvatar& newSortData = *it;
|
||||||
const auto& newAvatar = newSortData.getAvatar();
|
const auto newAvatar = std::static_pointer_cast<Avatar>(newSortData.getAvatar());
|
||||||
bool inView = newSortData.getPriority() > OUT_OF_VIEW_THRESHOLD;
|
bool inView = newSortData.getPriority() > OUT_OF_VIEW_THRESHOLD;
|
||||||
// Once we reach an avatar that's not in view, all avatars after it will also be out of view
|
// Once we reach an avatar that's not in view, all avatars after it will also be out of view
|
||||||
if (!inView) {
|
if (!inView) {
|
||||||
|
|
|
@ -66,8 +66,6 @@ void SafeLanding::addTrackedEntity(const EntityItemID& entityID) {
|
||||||
Locker lock(_lock);
|
Locker lock(_lock);
|
||||||
EntityItemPointer entity = _entityTree->findEntityByID(entityID);
|
EntityItemPointer entity = _entityTree->findEntityByID(entityID);
|
||||||
|
|
||||||
if (entity) {
|
|
||||||
|
|
||||||
_trackedEntities.emplace(entityID, entity);
|
_trackedEntities.emplace(entityID, entity);
|
||||||
int trackedEntityCount = (int)_trackedEntities.size();
|
int trackedEntityCount = (int)_trackedEntities.size();
|
||||||
|
|
||||||
|
@ -76,9 +74,6 @@ void SafeLanding::addTrackedEntity(const EntityItemID& entityID) {
|
||||||
}
|
}
|
||||||
qCDebug(interfaceapp) << "Safe Landing: Tracking entity " << entity->getItemName();
|
qCDebug(interfaceapp) << "Safe Landing: Tracking entity " << entity->getItemName();
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
qCDebug(interfaceapp) << "Safe Landing: Null Entity: " << entityID;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SafeLanding::deleteTrackedEntity(const EntityItemID& entityID) {
|
void SafeLanding::deleteTrackedEntity(const EntityItemID& entityID) {
|
||||||
|
@ -151,7 +146,7 @@ bool isEntityPhysicsReady(const EntityItemPointer& entity) {
|
||||||
bool hasAABox;
|
bool hasAABox;
|
||||||
entity->getAABox(hasAABox);
|
entity->getAABox(hasAABox);
|
||||||
if (hasAABox && downloadedCollisionTypes.count(modelEntity->getShapeType()) != 0) {
|
if (hasAABox && downloadedCollisionTypes.count(modelEntity->getShapeType()) != 0) {
|
||||||
return (!entity->shouldBePhysical() || entity->isReadyToComputeShape());
|
return entity->isReadyToComputeShape();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -161,23 +156,12 @@ bool isEntityPhysicsReady(const EntityItemPointer& entity) {
|
||||||
|
|
||||||
bool SafeLanding::isEntityLoadingComplete() {
|
bool SafeLanding::isEntityLoadingComplete() {
|
||||||
Locker lock(_lock);
|
Locker lock(_lock);
|
||||||
|
|
||||||
|
|
||||||
auto entityTree = qApp->getEntities();
|
auto entityTree = qApp->getEntities();
|
||||||
auto entityMapIter = _trackedEntities.begin();
|
auto entityMapIter = _trackedEntities.begin();
|
||||||
|
|
||||||
while (entityMapIter != _trackedEntities.end()) {
|
while (entityMapIter != _trackedEntities.end()) {
|
||||||
auto entity = entityMapIter->second;
|
auto entity = entityMapIter->second;
|
||||||
|
bool isVisuallyReady = (entity->isVisuallyReady() || !entityTree->renderableForEntityId(entityMapIter->first));
|
||||||
bool isVisuallyReady = true;
|
|
||||||
|
|
||||||
Settings settings;
|
|
||||||
bool enableInterstitial = settings.value("enableIntersitialMode", false).toBool();
|
|
||||||
|
|
||||||
if (enableInterstitial) {
|
|
||||||
isVisuallyReady = (entity->isVisuallyReady() || !entityTree->renderableForEntityId(entityMapIter->first));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isEntityPhysicsReady(entity) && isVisuallyReady) {
|
if (isEntityPhysicsReady(entity) && isVisuallyReady) {
|
||||||
entityMapIter = _trackedEntities.erase(entityMapIter);
|
entityMapIter = _trackedEntities.erase(entityMapIter);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -363,13 +363,13 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent
|
||||||
memcpy(destinationBuffer, &packetStateFlags, sizeof(packetStateFlags));
|
memcpy(destinationBuffer, &packetStateFlags, sizeof(packetStateFlags));
|
||||||
destinationBuffer += sizeof(packetStateFlags);
|
destinationBuffer += sizeof(packetStateFlags);
|
||||||
|
|
||||||
#define AVATAR_MEMCPY(src) \
|
|
||||||
memcpy(destinationBuffer, &(src), sizeof(src)); \
|
|
||||||
destinationBuffer += sizeof(src);
|
|
||||||
|
|
||||||
if (hasAvatarGlobalPosition) {
|
if (hasAvatarGlobalPosition) {
|
||||||
auto startSection = destinationBuffer;
|
auto startSection = destinationBuffer;
|
||||||
AVATAR_MEMCPY(_globalPosition);
|
auto data = reinterpret_cast<AvatarDataPacket::AvatarGlobalPosition*>(destinationBuffer);
|
||||||
|
data->globalPosition[0] = _globalPosition.x;
|
||||||
|
data->globalPosition[1] = _globalPosition.y;
|
||||||
|
data->globalPosition[2] = _globalPosition.z;
|
||||||
|
destinationBuffer += sizeof(AvatarDataPacket::AvatarGlobalPosition);
|
||||||
|
|
||||||
int numBytes = destinationBuffer - startSection;
|
int numBytes = destinationBuffer - startSection;
|
||||||
|
|
||||||
|
@ -380,8 +380,17 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent
|
||||||
|
|
||||||
if (hasAvatarBoundingBox) {
|
if (hasAvatarBoundingBox) {
|
||||||
auto startSection = destinationBuffer;
|
auto startSection = destinationBuffer;
|
||||||
AVATAR_MEMCPY(_globalBoundingBoxDimensions);
|
auto data = reinterpret_cast<AvatarDataPacket::AvatarBoundingBox*>(destinationBuffer);
|
||||||
AVATAR_MEMCPY(_globalBoundingBoxOffset);
|
|
||||||
|
data->avatarDimensions[0] = _globalBoundingBoxDimensions.x;
|
||||||
|
data->avatarDimensions[1] = _globalBoundingBoxDimensions.y;
|
||||||
|
data->avatarDimensions[2] = _globalBoundingBoxDimensions.z;
|
||||||
|
|
||||||
|
data->boundOriginOffset[0] = _globalBoundingBoxOffset.x;
|
||||||
|
data->boundOriginOffset[1] = _globalBoundingBoxOffset.y;
|
||||||
|
data->boundOriginOffset[2] = _globalBoundingBoxOffset.z;
|
||||||
|
|
||||||
|
destinationBuffer += sizeof(AvatarDataPacket::AvatarBoundingBox);
|
||||||
|
|
||||||
int numBytes = destinationBuffer - startSection;
|
int numBytes = destinationBuffer - startSection;
|
||||||
if (outboundDataRateOut) {
|
if (outboundDataRateOut) {
|
||||||
|
@ -415,7 +424,13 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent
|
||||||
|
|
||||||
if (hasLookAtPosition) {
|
if (hasLookAtPosition) {
|
||||||
auto startSection = destinationBuffer;
|
auto startSection = destinationBuffer;
|
||||||
AVATAR_MEMCPY(_headData->getLookAtPosition());
|
auto data = reinterpret_cast<AvatarDataPacket::LookAtPosition*>(destinationBuffer);
|
||||||
|
auto lookAt = _headData->getLookAtPosition();
|
||||||
|
data->lookAtPosition[0] = lookAt.x;
|
||||||
|
data->lookAtPosition[1] = lookAt.y;
|
||||||
|
data->lookAtPosition[2] = lookAt.z;
|
||||||
|
destinationBuffer += sizeof(AvatarDataPacket::LookAtPosition);
|
||||||
|
|
||||||
int numBytes = destinationBuffer - startSection;
|
int numBytes = destinationBuffer - startSection;
|
||||||
if (outboundDataRateOut) {
|
if (outboundDataRateOut) {
|
||||||
outboundDataRateOut->lookAtPositionRate.increment(numBytes);
|
outboundDataRateOut->lookAtPositionRate.increment(numBytes);
|
||||||
|
@ -516,8 +531,12 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent
|
||||||
|
|
||||||
if (hasAvatarLocalPosition) {
|
if (hasAvatarLocalPosition) {
|
||||||
auto startSection = destinationBuffer;
|
auto startSection = destinationBuffer;
|
||||||
const auto localPosition = getLocalPosition();
|
auto data = reinterpret_cast<AvatarDataPacket::AvatarLocalPosition*>(destinationBuffer);
|
||||||
AVATAR_MEMCPY(localPosition);
|
auto localPosition = getLocalPosition();
|
||||||
|
data->localPosition[0] = localPosition.x;
|
||||||
|
data->localPosition[1] = localPosition.y;
|
||||||
|
data->localPosition[2] = localPosition.z;
|
||||||
|
destinationBuffer += sizeof(AvatarDataPacket::AvatarLocalPosition);
|
||||||
|
|
||||||
int numBytes = destinationBuffer - startSection;
|
int numBytes = destinationBuffer - startSection;
|
||||||
if (outboundDataRateOut) {
|
if (outboundDataRateOut) {
|
||||||
|
@ -548,24 +567,19 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QVector<JointData> jointData;
|
|
||||||
if (hasJointData || hasJointDefaultPoseFlags) {
|
|
||||||
QReadLocker readLock(&_jointDataLock);
|
|
||||||
jointData = _jointData;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If it is connected, pack up the data
|
// If it is connected, pack up the data
|
||||||
if (hasJointData) {
|
if (hasJointData) {
|
||||||
auto startSection = destinationBuffer;
|
auto startSection = destinationBuffer;
|
||||||
|
QReadLocker readLock(&_jointDataLock);
|
||||||
|
|
||||||
// joint rotation data
|
// joint rotation data
|
||||||
int numJoints = jointData.size();
|
int numJoints = _jointData.size();
|
||||||
*destinationBuffer++ = (uint8_t)numJoints;
|
*destinationBuffer++ = (uint8_t)numJoints;
|
||||||
|
|
||||||
unsigned char* validityPosition = destinationBuffer;
|
unsigned char* validityPosition = destinationBuffer;
|
||||||
unsigned char validity = 0;
|
unsigned char validity = 0;
|
||||||
int validityBit = 0;
|
int validityBit = 0;
|
||||||
int numValidityBytes = calcBitVectorSize(numJoints);
|
int numValidityBytes = (int)std::ceil(numJoints / (float)BITS_IN_BYTE);
|
||||||
|
|
||||||
#ifdef WANT_DEBUG
|
#ifdef WANT_DEBUG
|
||||||
int rotationSentCount = 0;
|
int rotationSentCount = 0;
|
||||||
|
@ -575,21 +589,30 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent
|
||||||
destinationBuffer += numValidityBytes; // Move pointer past the validity bytes
|
destinationBuffer += numValidityBytes; // Move pointer past the validity bytes
|
||||||
|
|
||||||
// sentJointDataOut and lastSentJointData might be the same vector
|
// sentJointDataOut and lastSentJointData might be the same vector
|
||||||
|
// build sentJointDataOut locally and then swap it at the end.
|
||||||
|
QVector<JointData> localSentJointDataOut;
|
||||||
if (sentJointDataOut) {
|
if (sentJointDataOut) {
|
||||||
sentJointDataOut->resize(numJoints); // Make sure the destination is resized before using it
|
localSentJointDataOut.resize(numJoints); // Make sure the destination is resized before using it
|
||||||
}
|
}
|
||||||
|
|
||||||
float minRotationDOT = (distanceAdjust && cullSmallChanges) ? getDistanceBasedMinRotationDOT(viewerPosition) : AVATAR_MIN_ROTATION_DOT;
|
float minRotationDOT = !distanceAdjust ? AVATAR_MIN_ROTATION_DOT : getDistanceBasedMinRotationDOT(viewerPosition);
|
||||||
|
|
||||||
for (int i = 0; i < jointData.size(); i++) {
|
for (int i = 0; i < _jointData.size(); i++) {
|
||||||
const JointData& data = jointData[i];
|
const JointData& data = _jointData[i];
|
||||||
const JointData& last = lastSentJointData[i];
|
const JointData& last = lastSentJointData[i];
|
||||||
|
|
||||||
if (!data.rotationIsDefaultPose) {
|
if (!data.rotationIsDefaultPose) {
|
||||||
// The dot product for larger rotations is a lower number.
|
bool mustSend = sendAll || last.rotationIsDefaultPose;
|
||||||
|
if (mustSend || last.rotation != data.rotation) {
|
||||||
|
|
||||||
|
bool largeEnoughRotation = true;
|
||||||
|
if (cullSmallChanges) {
|
||||||
|
// The dot product for smaller rotations is a smaller number.
|
||||||
// So if the dot() is less than the value, then the rotation is a larger angle of rotation
|
// So if the dot() is less than the value, then the rotation is a larger angle of rotation
|
||||||
if (sendAll || last.rotationIsDefaultPose || (!cullSmallChanges && last.rotation != data.rotation)
|
largeEnoughRotation = fabsf(glm::dot(last.rotation, data.rotation)) < minRotationDOT;
|
||||||
|| (cullSmallChanges && glm::dot(last.rotation, data.rotation) < minRotationDOT) ) {
|
}
|
||||||
|
|
||||||
|
if (mustSend || !cullSmallChanges || largeEnoughRotation) {
|
||||||
validity |= (1 << validityBit);
|
validity |= (1 << validityBit);
|
||||||
#ifdef WANT_DEBUG
|
#ifdef WANT_DEBUG
|
||||||
rotationSentCount++;
|
rotationSentCount++;
|
||||||
|
@ -597,15 +620,12 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent
|
||||||
destinationBuffer += packOrientationQuatToSixBytes(destinationBuffer, data.rotation);
|
destinationBuffer += packOrientationQuatToSixBytes(destinationBuffer, data.rotation);
|
||||||
|
|
||||||
if (sentJointDataOut) {
|
if (sentJointDataOut) {
|
||||||
(*sentJointDataOut)[i].rotation = data.rotation;
|
localSentJointDataOut[i].rotation = data.rotation;
|
||||||
|
localSentJointDataOut[i].rotationIsDefaultPose = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sentJointDataOut) {
|
|
||||||
(*sentJointDataOut)[i].rotationIsDefaultPose = data.rotationIsDefaultPose;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (++validityBit == BITS_IN_BYTE) {
|
if (++validityBit == BITS_IN_BYTE) {
|
||||||
*validityPosition++ = validity;
|
*validityPosition++ = validity;
|
||||||
validityBit = validity = 0;
|
validityBit = validity = 0;
|
||||||
|
@ -627,17 +647,17 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent
|
||||||
|
|
||||||
destinationBuffer += numValidityBytes; // Move pointer past the validity bytes
|
destinationBuffer += numValidityBytes; // Move pointer past the validity bytes
|
||||||
|
|
||||||
float minTranslation = (distanceAdjust && cullSmallChanges) ? getDistanceBasedMinTranslationDistance(viewerPosition) : AVATAR_MIN_TRANSLATION;
|
float minTranslation = !distanceAdjust ? AVATAR_MIN_TRANSLATION : getDistanceBasedMinTranslationDistance(viewerPosition);
|
||||||
|
|
||||||
float maxTranslationDimension = 0.0;
|
float maxTranslationDimension = 0.0;
|
||||||
for (int i = 0; i < jointData.size(); i++) {
|
for (int i = 0; i < _jointData.size(); i++) {
|
||||||
const JointData& data = jointData[i];
|
const JointData& data = _jointData[i];
|
||||||
const JointData& last = lastSentJointData[i];
|
const JointData& last = lastSentJointData[i];
|
||||||
|
|
||||||
if (!data.translationIsDefaultPose) {
|
if (!data.translationIsDefaultPose) {
|
||||||
if (sendAll || last.translationIsDefaultPose || (!cullSmallChanges && last.translation != data.translation)
|
bool mustSend = sendAll || last.translationIsDefaultPose;
|
||||||
|| (cullSmallChanges && glm::distance(data.translation, lastSentJointData[i].translation) > minTranslation)) {
|
if (mustSend || last.translation != data.translation) {
|
||||||
|
if (mustSend || !cullSmallChanges || glm::distance(data.translation, lastSentJointData[i].translation) > minTranslation) {
|
||||||
validity |= (1 << validityBit);
|
validity |= (1 << validityBit);
|
||||||
#ifdef WANT_DEBUG
|
#ifdef WANT_DEBUG
|
||||||
translationSentCount++;
|
translationSentCount++;
|
||||||
|
@ -650,15 +670,12 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent
|
||||||
packFloatVec3ToSignedTwoByteFixed(destinationBuffer, data.translation, TRANSLATION_COMPRESSION_RADIX);
|
packFloatVec3ToSignedTwoByteFixed(destinationBuffer, data.translation, TRANSLATION_COMPRESSION_RADIX);
|
||||||
|
|
||||||
if (sentJointDataOut) {
|
if (sentJointDataOut) {
|
||||||
(*sentJointDataOut)[i].translation = data.translation;
|
localSentJointDataOut[i].translation = data.translation;
|
||||||
|
localSentJointDataOut[i].translationIsDefaultPose = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sentJointDataOut) {
|
|
||||||
(*sentJointDataOut)[i].translationIsDefaultPose = data.translationIsDefaultPose;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (++validityBit == BITS_IN_BYTE) {
|
if (++validityBit == BITS_IN_BYTE) {
|
||||||
*validityPosition++ = validity;
|
*validityPosition++ = validity;
|
||||||
validityBit = validity = 0;
|
validityBit = validity = 0;
|
||||||
|
@ -674,7 +691,6 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent
|
||||||
destinationBuffer += packOrientationQuatToSixBytes(destinationBuffer, controllerLeftHandTransform.getRotation());
|
destinationBuffer += packOrientationQuatToSixBytes(destinationBuffer, controllerLeftHandTransform.getRotation());
|
||||||
destinationBuffer += packFloatVec3ToSignedTwoByteFixed(destinationBuffer, controllerLeftHandTransform.getTranslation(),
|
destinationBuffer += packFloatVec3ToSignedTwoByteFixed(destinationBuffer, controllerLeftHandTransform.getTranslation(),
|
||||||
TRANSLATION_COMPRESSION_RADIX);
|
TRANSLATION_COMPRESSION_RADIX);
|
||||||
|
|
||||||
Transform controllerRightHandTransform = Transform(getControllerRightHandMatrix());
|
Transform controllerRightHandTransform = Transform(getControllerRightHandMatrix());
|
||||||
destinationBuffer += packOrientationQuatToSixBytes(destinationBuffer, controllerRightHandTransform.getRotation());
|
destinationBuffer += packOrientationQuatToSixBytes(destinationBuffer, controllerRightHandTransform.getRotation());
|
||||||
destinationBuffer += packFloatVec3ToSignedTwoByteFixed(destinationBuffer, controllerRightHandTransform.getTranslation(),
|
destinationBuffer += packFloatVec3ToSignedTwoByteFixed(destinationBuffer, controllerRightHandTransform.getTranslation(),
|
||||||
|
@ -691,27 +707,34 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent
|
||||||
glm::vec3 mouseFarGrabPosition = extractTranslation(mouseFarGrabMatrix);
|
glm::vec3 mouseFarGrabPosition = extractTranslation(mouseFarGrabMatrix);
|
||||||
glm::quat mouseFarGrabRotation = extractRotation(mouseFarGrabMatrix);
|
glm::quat mouseFarGrabRotation = extractRotation(mouseFarGrabMatrix);
|
||||||
|
|
||||||
AVATAR_MEMCPY(leftFarGrabPosition);
|
data->leftFarGrabPosition[0] = leftFarGrabPosition.x;
|
||||||
// Can't do block copy as struct order is x, y, z, w.
|
data->leftFarGrabPosition[1] = leftFarGrabPosition.y;
|
||||||
|
data->leftFarGrabPosition[2] = leftFarGrabPosition.z;
|
||||||
|
|
||||||
data->leftFarGrabRotation[0] = leftFarGrabRotation.w;
|
data->leftFarGrabRotation[0] = leftFarGrabRotation.w;
|
||||||
data->leftFarGrabRotation[1] = leftFarGrabRotation.x;
|
data->leftFarGrabRotation[1] = leftFarGrabRotation.x;
|
||||||
data->leftFarGrabRotation[2] = leftFarGrabRotation.y;
|
data->leftFarGrabRotation[2] = leftFarGrabRotation.y;
|
||||||
data->leftFarGrabRotation[3] = leftFarGrabRotation.z;
|
data->leftFarGrabRotation[3] = leftFarGrabRotation.z;
|
||||||
destinationBuffer += sizeof(data->leftFarGrabPosition);
|
|
||||||
|
|
||||||
AVATAR_MEMCPY(rightFarGrabPosition);
|
data->rightFarGrabPosition[0] = rightFarGrabPosition.x;
|
||||||
|
data->rightFarGrabPosition[1] = rightFarGrabPosition.y;
|
||||||
|
data->rightFarGrabPosition[2] = rightFarGrabPosition.z;
|
||||||
|
|
||||||
data->rightFarGrabRotation[0] = rightFarGrabRotation.w;
|
data->rightFarGrabRotation[0] = rightFarGrabRotation.w;
|
||||||
data->rightFarGrabRotation[1] = rightFarGrabRotation.x;
|
data->rightFarGrabRotation[1] = rightFarGrabRotation.x;
|
||||||
data->rightFarGrabRotation[2] = rightFarGrabRotation.y;
|
data->rightFarGrabRotation[2] = rightFarGrabRotation.y;
|
||||||
data->rightFarGrabRotation[3] = rightFarGrabRotation.z;
|
data->rightFarGrabRotation[3] = rightFarGrabRotation.z;
|
||||||
destinationBuffer += sizeof(data->rightFarGrabRotation);
|
|
||||||
|
|
||||||
AVATAR_MEMCPY(mouseFarGrabPosition);
|
data->mouseFarGrabPosition[0] = mouseFarGrabPosition.x;
|
||||||
|
data->mouseFarGrabPosition[1] = mouseFarGrabPosition.y;
|
||||||
|
data->mouseFarGrabPosition[2] = mouseFarGrabPosition.z;
|
||||||
|
|
||||||
data->mouseFarGrabRotation[0] = mouseFarGrabRotation.w;
|
data->mouseFarGrabRotation[0] = mouseFarGrabRotation.w;
|
||||||
data->mouseFarGrabRotation[1] = mouseFarGrabRotation.x;
|
data->mouseFarGrabRotation[1] = mouseFarGrabRotation.x;
|
||||||
data->mouseFarGrabRotation[2] = mouseFarGrabRotation.y;
|
data->mouseFarGrabRotation[2] = mouseFarGrabRotation.y;
|
||||||
data->mouseFarGrabRotation[3] = mouseFarGrabRotation.z;
|
data->mouseFarGrabRotation[3] = mouseFarGrabRotation.z;
|
||||||
destinationBuffer += sizeof(data->mouseFarGrabRotation);
|
|
||||||
|
destinationBuffer += sizeof(AvatarDataPacket::FarGrabJoints);
|
||||||
|
|
||||||
int numBytes = destinationBuffer - startSection;
|
int numBytes = destinationBuffer - startSection;
|
||||||
|
|
||||||
|
@ -738,23 +761,41 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent
|
||||||
outboundDataRateOut->jointDataRate.increment(numBytes);
|
outboundDataRateOut->jointDataRate.increment(numBytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (sentJointDataOut) {
|
||||||
|
|
||||||
|
// Mark default poses in lastSentJointData, so when they become non-default we send them.
|
||||||
|
for (int i = 0; i < _jointData.size(); i++) {
|
||||||
|
const JointData& data = _jointData[i];
|
||||||
|
JointData& local = localSentJointDataOut[i];
|
||||||
|
if (data.rotationIsDefaultPose) {
|
||||||
|
local.rotationIsDefaultPose = true;
|
||||||
|
}
|
||||||
|
if (data.translationIsDefaultPose) {
|
||||||
|
local.translationIsDefaultPose = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Push new sent joint data to sentJointDataOut
|
||||||
|
sentJointDataOut->swap(localSentJointDataOut);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hasJointDefaultPoseFlags) {
|
if (hasJointDefaultPoseFlags) {
|
||||||
auto startSection = destinationBuffer;
|
auto startSection = destinationBuffer;
|
||||||
|
QReadLocker readLock(&_jointDataLock);
|
||||||
|
|
||||||
// write numJoints
|
// write numJoints
|
||||||
int numJoints = jointData.size();
|
int numJoints = _jointData.size();
|
||||||
*destinationBuffer++ = (uint8_t)numJoints;
|
*destinationBuffer++ = (uint8_t)numJoints;
|
||||||
|
|
||||||
// write rotationIsDefaultPose bits
|
// write rotationIsDefaultPose bits
|
||||||
destinationBuffer += writeBitVector(destinationBuffer, numJoints, [&](int i) {
|
destinationBuffer += writeBitVector(destinationBuffer, numJoints, [&](int i) {
|
||||||
return jointData[i].rotationIsDefaultPose;
|
return _jointData[i].rotationIsDefaultPose;
|
||||||
});
|
});
|
||||||
|
|
||||||
// write translationIsDefaultPose bits
|
// write translationIsDefaultPose bits
|
||||||
destinationBuffer += writeBitVector(destinationBuffer, numJoints, [&](int i) {
|
destinationBuffer += writeBitVector(destinationBuffer, numJoints, [&](int i) {
|
||||||
return jointData[i].translationIsDefaultPose;
|
return _jointData[i].translationIsDefaultPose;
|
||||||
});
|
});
|
||||||
|
|
||||||
if (outboundDataRateOut) {
|
if (outboundDataRateOut) {
|
||||||
|
@ -839,6 +880,7 @@ const unsigned char* unpackFauxJoint(const unsigned char* sourceBuffer, ThreadSa
|
||||||
|
|
||||||
// read data in packet starting at byte offset and return number of bytes parsed
|
// read data in packet starting at byte offset and return number of bytes parsed
|
||||||
int AvatarData::parseDataFromBuffer(const QByteArray& buffer) {
|
int AvatarData::parseDataFromBuffer(const QByteArray& buffer) {
|
||||||
|
|
||||||
// lazily allocate memory for HeadData in case we're not an Avatar instance
|
// lazily allocate memory for HeadData in case we're not an Avatar instance
|
||||||
lazyInitHeadData();
|
lazyInitHeadData();
|
||||||
|
|
||||||
|
@ -890,7 +932,7 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) {
|
||||||
auto newValue = glm::vec3(data->globalPosition[0], data->globalPosition[1], data->globalPosition[2]) + offset;
|
auto newValue = glm::vec3(data->globalPosition[0], data->globalPosition[1], data->globalPosition[2]) + offset;
|
||||||
if (_globalPosition != newValue) {
|
if (_globalPosition != newValue) {
|
||||||
_globalPosition = newValue;
|
_globalPosition = newValue;
|
||||||
_globalPositionChanged = now;
|
_globalPositionChanged = usecTimestampNow();
|
||||||
}
|
}
|
||||||
sourceBuffer += sizeof(AvatarDataPacket::AvatarGlobalPosition);
|
sourceBuffer += sizeof(AvatarDataPacket::AvatarGlobalPosition);
|
||||||
int numBytesRead = sourceBuffer - startSection;
|
int numBytesRead = sourceBuffer - startSection;
|
||||||
|
@ -914,11 +956,11 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) {
|
||||||
|
|
||||||
if (_globalBoundingBoxDimensions != newDimensions) {
|
if (_globalBoundingBoxDimensions != newDimensions) {
|
||||||
_globalBoundingBoxDimensions = newDimensions;
|
_globalBoundingBoxDimensions = newDimensions;
|
||||||
_avatarBoundingBoxChanged = now;
|
_avatarBoundingBoxChanged = usecTimestampNow();
|
||||||
}
|
}
|
||||||
if (_globalBoundingBoxOffset != newOffset) {
|
if (_globalBoundingBoxOffset != newOffset) {
|
||||||
_globalBoundingBoxOffset = newOffset;
|
_globalBoundingBoxOffset = newOffset;
|
||||||
_avatarBoundingBoxChanged = now;
|
_avatarBoundingBoxChanged = usecTimestampNow();
|
||||||
}
|
}
|
||||||
|
|
||||||
sourceBuffer += sizeof(AvatarDataPacket::AvatarBoundingBox);
|
sourceBuffer += sizeof(AvatarDataPacket::AvatarBoundingBox);
|
||||||
|
@ -1019,7 +1061,7 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) {
|
||||||
glm::mat4 sensorToWorldMatrix = createMatFromScaleQuatAndPos(glm::vec3(sensorToWorldScale), sensorToWorldQuat, sensorToWorldTrans);
|
glm::mat4 sensorToWorldMatrix = createMatFromScaleQuatAndPos(glm::vec3(sensorToWorldScale), sensorToWorldQuat, sensorToWorldTrans);
|
||||||
if (_sensorToWorldMatrixCache.get() != sensorToWorldMatrix) {
|
if (_sensorToWorldMatrixCache.get() != sensorToWorldMatrix) {
|
||||||
_sensorToWorldMatrixCache.set(sensorToWorldMatrix);
|
_sensorToWorldMatrixCache.set(sensorToWorldMatrix);
|
||||||
_sensorToWorldMatrixChanged = now;
|
_sensorToWorldMatrixChanged = usecTimestampNow();
|
||||||
}
|
}
|
||||||
sourceBuffer += sizeof(AvatarDataPacket::SensorToWorldMatrix);
|
sourceBuffer += sizeof(AvatarDataPacket::SensorToWorldMatrix);
|
||||||
int numBytesRead = sourceBuffer - startSection;
|
int numBytesRead = sourceBuffer - startSection;
|
||||||
|
@ -1076,7 +1118,7 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) {
|
||||||
sourceBuffer += sizeof(AvatarDataPacket::AdditionalFlags);
|
sourceBuffer += sizeof(AvatarDataPacket::AdditionalFlags);
|
||||||
|
|
||||||
if (somethingChanged) {
|
if (somethingChanged) {
|
||||||
_additionalFlagsChanged = now;
|
_additionalFlagsChanged = usecTimestampNow();
|
||||||
}
|
}
|
||||||
int numBytesRead = sourceBuffer - startSection;
|
int numBytesRead = sourceBuffer - startSection;
|
||||||
_additionalFlagsRate.increment(numBytesRead);
|
_additionalFlagsRate.increment(numBytesRead);
|
||||||
|
@ -1096,7 +1138,7 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) {
|
||||||
if ((getParentID() != newParentID) || (getParentJointIndex() != parentInfo->parentJointIndex)) {
|
if ((getParentID() != newParentID) || (getParentJointIndex() != parentInfo->parentJointIndex)) {
|
||||||
SpatiallyNestable::setParentID(newParentID);
|
SpatiallyNestable::setParentID(newParentID);
|
||||||
SpatiallyNestable::setParentJointIndex(parentInfo->parentJointIndex);
|
SpatiallyNestable::setParentJointIndex(parentInfo->parentJointIndex);
|
||||||
_parentChanged = now;
|
_parentChanged = usecTimestampNow();
|
||||||
}
|
}
|
||||||
|
|
||||||
int numBytesRead = sourceBuffer - startSection;
|
int numBytesRead = sourceBuffer - startSection;
|
||||||
|
@ -1145,6 +1187,8 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) {
|
||||||
int numBytesRead = sourceBuffer - startSection;
|
int numBytesRead = sourceBuffer - startSection;
|
||||||
_faceTrackerRate.increment(numBytesRead);
|
_faceTrackerRate.increment(numBytesRead);
|
||||||
_faceTrackerUpdateRate.increment();
|
_faceTrackerUpdateRate.increment();
|
||||||
|
} else {
|
||||||
|
_headData->_blendshapeCoefficients.fill(0, _headData->_blendshapeCoefficients.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hasJointData) {
|
if (hasJointData) {
|
||||||
|
@ -2829,8 +2873,10 @@ void RayToAvatarIntersectionResultFromScriptValue(const QScriptValue& object, Ra
|
||||||
value.extraInfo = object.property("extraInfo").toVariant().toMap();
|
value.extraInfo = object.property("extraInfo").toVariant().toMap();
|
||||||
}
|
}
|
||||||
|
|
||||||
float AvatarData::_avatarSortCoefficientSize { 8.0f };
|
const float AvatarData::OUT_OF_VIEW_PENALTY = -10.0f;
|
||||||
float AvatarData::_avatarSortCoefficientCenter { 4.0f };
|
|
||||||
|
float AvatarData::_avatarSortCoefficientSize { 1.0f };
|
||||||
|
float AvatarData::_avatarSortCoefficientCenter { 0.25 };
|
||||||
float AvatarData::_avatarSortCoefficientAge { 1.0f };
|
float AvatarData::_avatarSortCoefficientAge { 1.0f };
|
||||||
|
|
||||||
QScriptValue AvatarEntityMapToScriptValue(QScriptEngine* engine, const AvatarEntityMap& value) {
|
QScriptValue AvatarEntityMapToScriptValue(QScriptEngine* engine, const AvatarEntityMap& value) {
|
||||||
|
|
|
@ -1097,7 +1097,7 @@ public:
|
||||||
void fromJson(const QJsonObject& json, bool useFrameSkeleton = true);
|
void fromJson(const QJsonObject& json, bool useFrameSkeleton = true);
|
||||||
|
|
||||||
glm::vec3 getClientGlobalPosition() const { return _globalPosition; }
|
glm::vec3 getClientGlobalPosition() const { return _globalPosition; }
|
||||||
AABox getGlobalBoundingBox() const { return AABox(_globalPosition + _globalBoundingBoxOffset - _globalBoundingBoxDimensions, _globalBoundingBoxDimensions); }
|
glm::vec3 getGlobalBoundingBoxCorner() const { return _globalPosition + _globalBoundingBoxOffset - _globalBoundingBoxDimensions; }
|
||||||
|
|
||||||
/**jsdoc
|
/**jsdoc
|
||||||
* @function MyAvatar.getAvatarEntityData
|
* @function MyAvatar.getAvatarEntityData
|
||||||
|
@ -1169,6 +1169,8 @@ public:
|
||||||
// A method intended to be overriden by MyAvatar for polling orientation for network transmission.
|
// A method intended to be overriden by MyAvatar for polling orientation for network transmission.
|
||||||
virtual glm::quat getOrientationOutbound() const;
|
virtual glm::quat getOrientationOutbound() const;
|
||||||
|
|
||||||
|
static const float OUT_OF_VIEW_PENALTY;
|
||||||
|
|
||||||
// TODO: remove this HACK once we settle on optimal sort coefficients
|
// TODO: remove this HACK once we settle on optimal sort coefficients
|
||||||
// These coefficients exposed for fine tuning the sort priority for transfering new _jointData to the render pipeline.
|
// These coefficients exposed for fine tuning the sort priority for transfering new _jointData to the render pipeline.
|
||||||
static float _avatarSortCoefficientSize;
|
static float _avatarSortCoefficientSize;
|
||||||
|
|
|
@ -538,6 +538,7 @@ void AccountManager::requestAccessToken(const QString& login, const QString& pas
|
||||||
|
|
||||||
QNetworkReply* requestReply = networkAccessManager.post(request, postData);
|
QNetworkReply* requestReply = networkAccessManager.post(request, postData);
|
||||||
connect(requestReply, &QNetworkReply::finished, this, &AccountManager::requestAccessTokenFinished);
|
connect(requestReply, &QNetworkReply::finished, this, &AccountManager::requestAccessTokenFinished);
|
||||||
|
connect(requestReply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(requestAccessTokenError(QNetworkReply::NetworkError)));
|
||||||
}
|
}
|
||||||
|
|
||||||
void AccountManager::requestAccessTokenWithSteam(QByteArray authSessionTicket) {
|
void AccountManager::requestAccessTokenWithSteam(QByteArray authSessionTicket) {
|
||||||
|
@ -632,6 +633,12 @@ void AccountManager::requestAccessTokenFinished() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AccountManager::requestAccessTokenError(QNetworkReply::NetworkError error) {
|
||||||
|
// TODO: error handling
|
||||||
|
qCDebug(networking) << "AccountManager: failed to fetch access token - " << error;
|
||||||
|
emit loginFailed();
|
||||||
|
}
|
||||||
|
|
||||||
void AccountManager::refreshAccessTokenFinished() {
|
void AccountManager::refreshAccessTokenFinished() {
|
||||||
QNetworkReply* requestReply = reinterpret_cast<QNetworkReply*>(sender());
|
QNetworkReply* requestReply = reinterpret_cast<QNetworkReply*>(sender());
|
||||||
|
|
||||||
|
|
|
@ -106,6 +106,7 @@ public slots:
|
||||||
void requestAccessTokenFinished();
|
void requestAccessTokenFinished();
|
||||||
void refreshAccessTokenFinished();
|
void refreshAccessTokenFinished();
|
||||||
void requestProfileFinished();
|
void requestProfileFinished();
|
||||||
|
void requestAccessTokenError(QNetworkReply::NetworkError error);
|
||||||
void refreshAccessTokenError(QNetworkReply::NetworkError error);
|
void refreshAccessTokenError(QNetworkReply::NetworkError error);
|
||||||
void requestProfileError(QNetworkReply::NetworkError error);
|
void requestProfileError(QNetworkReply::NetworkError error);
|
||||||
void logout();
|
void logout();
|
||||||
|
|
|
@ -816,10 +816,8 @@ bool AddressManager::setDomainInfo(const QUrl& domainURL, LookupTrigger trigger)
|
||||||
const QString hostname = domainURL.host();
|
const QString hostname = domainURL.host();
|
||||||
quint16 port = domainURL.port();
|
quint16 port = domainURL.port();
|
||||||
bool emitHostChanged { false };
|
bool emitHostChanged { false };
|
||||||
// Check if domain handler is in error state. always emit host changed if true.
|
|
||||||
bool isInErrorState = DependencyManager::get<NodeList>()->getDomainHandler().isInErrorState();
|
|
||||||
|
|
||||||
if (domainURL != _domainURL || isInErrorState) {
|
if (domainURL != _domainURL) {
|
||||||
addCurrentAddressToHistory(trigger);
|
addCurrentAddressToHistory(trigger);
|
||||||
emitHostChanged = true;
|
emitHostChanged = true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -55,9 +55,6 @@ DomainHandler::DomainHandler(QObject* parent) :
|
||||||
|
|
||||||
// stop the refresh timer if we connect to a domain
|
// stop the refresh timer if we connect to a domain
|
||||||
connect(this, &DomainHandler::connectedToDomain, &_apiRefreshTimer, &QTimer::stop);
|
connect(this, &DomainHandler::connectedToDomain, &_apiRefreshTimer, &QTimer::stop);
|
||||||
|
|
||||||
// stop the refresh timer if redirected to the error domain
|
|
||||||
connect(this, &DomainHandler::redirectToErrorDomainURL, &_apiRefreshTimer, &QTimer::stop);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void DomainHandler::disconnect() {
|
void DomainHandler::disconnect() {
|
||||||
|
@ -110,7 +107,6 @@ void DomainHandler::softReset() {
|
||||||
QMetaObject::invokeMethod(&_settingsTimer, "stop");
|
QMetaObject::invokeMethod(&_settingsTimer, "stop");
|
||||||
|
|
||||||
// restart the API refresh timer in case we fail to connect and need to refresh information
|
// restart the API refresh timer in case we fail to connect and need to refresh information
|
||||||
if (!_isInErrorState) {
|
|
||||||
QMetaObject::invokeMethod(&_apiRefreshTimer, "start");
|
QMetaObject::invokeMethod(&_apiRefreshTimer, "start");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -118,7 +114,6 @@ void DomainHandler::hardReset() {
|
||||||
emit resetting();
|
emit resetting();
|
||||||
|
|
||||||
softReset();
|
softReset();
|
||||||
_isInErrorState = false;
|
|
||||||
|
|
||||||
qCDebug(networking) << "Hard reset in NodeList DomainHandler.";
|
qCDebug(networking) << "Hard reset in NodeList DomainHandler.";
|
||||||
_pendingDomainID = QUuid();
|
_pendingDomainID = QUuid();
|
||||||
|
|
|
@ -172,11 +172,6 @@ public slots:
|
||||||
void processICEResponsePacket(QSharedPointer<ReceivedMessage> icePacket);
|
void processICEResponsePacket(QSharedPointer<ReceivedMessage> icePacket);
|
||||||
void processDomainServerConnectionDeniedPacket(QSharedPointer<ReceivedMessage> message);
|
void processDomainServerConnectionDeniedPacket(QSharedPointer<ReceivedMessage> message);
|
||||||
|
|
||||||
// sets domain handler in error state.
|
|
||||||
void setRedirectErrorState(QUrl errorUrl, int reasonCode);
|
|
||||||
|
|
||||||
bool isInErrorState() { return _isInErrorState; }
|
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void completedHostnameLookup(const QHostInfo& hostInfo);
|
void completedHostnameLookup(const QHostInfo& hostInfo);
|
||||||
void completedIceServerHostnameLookup();
|
void completedIceServerHostnameLookup();
|
||||||
|
|
|
@ -37,7 +37,6 @@ Socket::Socket(QObject* parent, bool shouldChangeSocketOptions) :
|
||||||
_shouldChangeSocketOptions(shouldChangeSocketOptions)
|
_shouldChangeSocketOptions(shouldChangeSocketOptions)
|
||||||
{
|
{
|
||||||
connect(&_udpSocket, &QUdpSocket::readyRead, this, &Socket::readPendingDatagrams);
|
connect(&_udpSocket, &QUdpSocket::readyRead, this, &Socket::readPendingDatagrams);
|
||||||
connect(this, &Socket::pendingDatagrams, this, &Socket::processPendingDatagrams, Qt::QueuedConnection);
|
|
||||||
|
|
||||||
// make sure we hear about errors and state changes from the underlying socket
|
// make sure we hear about errors and state changes from the underlying socket
|
||||||
connect(&_udpSocket, SIGNAL(error(QAbstractSocket::SocketError)),
|
connect(&_udpSocket, SIGNAL(error(QAbstractSocket::SocketError)),
|
||||||
|
@ -316,85 +315,55 @@ void Socket::checkForReadyReadBackup() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Socket::readPendingDatagrams() {
|
void Socket::readPendingDatagrams() {
|
||||||
int packetsRead = 0;
|
|
||||||
|
|
||||||
int packetSizeWithHeader = -1;
|
int packetSizeWithHeader = -1;
|
||||||
// Max datagrams to read before processing:
|
|
||||||
static const int MAX_DATAGRAMS_CONSECUTIVELY = 10000;
|
|
||||||
while (_udpSocket.hasPendingDatagrams()
|
|
||||||
&& (packetSizeWithHeader = _udpSocket.pendingDatagramSize()) != -1
|
|
||||||
&& packetsRead <= MAX_DATAGRAMS_CONSECUTIVELY) {
|
|
||||||
// grab a time point we can mark as the receive time of this packet
|
|
||||||
auto receiveTime = p_high_resolution_clock::now();
|
|
||||||
|
|
||||||
|
while (_udpSocket.hasPendingDatagrams() && (packetSizeWithHeader = _udpSocket.pendingDatagramSize()) != -1) {
|
||||||
// setup a buffer to read the packet into
|
|
||||||
auto buffer = std::unique_ptr<char[]>(new char[packetSizeWithHeader]);
|
|
||||||
|
|
||||||
QHostAddress senderAddress;
|
|
||||||
quint16 senderPort;
|
|
||||||
|
|
||||||
// pull the datagram
|
|
||||||
auto sizeRead = _udpSocket.readDatagram(buffer.get(), packetSizeWithHeader,
|
|
||||||
&senderAddress, &senderPort);
|
|
||||||
|
|
||||||
// we either didn't pull anything for this packet or there was an error reading (this seems to trigger
|
|
||||||
// on windows even if there's not a packet available)
|
|
||||||
if (sizeRead < 0) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
_incomingDatagrams.push_back({ senderAddress, senderPort, packetSizeWithHeader,
|
|
||||||
std::move(buffer), receiveTime });
|
|
||||||
++packetsRead;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if (packetsRead > _maxDatagramsRead) {
|
|
||||||
_maxDatagramsRead = packetsRead;
|
|
||||||
qCDebug(networking) << "readPendingDatagrams: Datagrams read:" << packetsRead;
|
|
||||||
}
|
|
||||||
emit pendingDatagrams(packetsRead);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Socket::processPendingDatagrams(int) {
|
|
||||||
// setup a HifiSockAddr to read into
|
|
||||||
HifiSockAddr senderSockAddr;
|
|
||||||
|
|
||||||
while (!_incomingDatagrams.empty()) {
|
|
||||||
auto& datagram = _incomingDatagrams.front();
|
|
||||||
senderSockAddr.setAddress(datagram._senderAddress);
|
|
||||||
senderSockAddr.setPort(datagram._senderPort);
|
|
||||||
int datagramSize = datagram._datagramLength;
|
|
||||||
auto receiveTime = datagram._receiveTime;
|
|
||||||
|
|
||||||
// we're reading a packet so re-start the readyRead backup timer
|
// we're reading a packet so re-start the readyRead backup timer
|
||||||
_readyReadBackupTimer->start();
|
_readyReadBackupTimer->start();
|
||||||
|
|
||||||
|
// grab a time point we can mark as the receive time of this packet
|
||||||
|
auto receiveTime = p_high_resolution_clock::now();
|
||||||
|
|
||||||
|
// setup a HifiSockAddr to read into
|
||||||
|
HifiSockAddr senderSockAddr;
|
||||||
|
|
||||||
|
// setup a buffer to read the packet into
|
||||||
|
auto buffer = std::unique_ptr<char[]>(new char[packetSizeWithHeader]);
|
||||||
|
|
||||||
|
// pull the datagram
|
||||||
|
auto sizeRead = _udpSocket.readDatagram(buffer.get(), packetSizeWithHeader,
|
||||||
|
senderSockAddr.getAddressPointer(), senderSockAddr.getPortPointer());
|
||||||
|
|
||||||
// save information for this packet, in case it is the one that sticks readyRead
|
// save information for this packet, in case it is the one that sticks readyRead
|
||||||
_lastPacketSizeRead = datagramSize;
|
_lastPacketSizeRead = sizeRead;
|
||||||
_lastPacketSockAddr = senderSockAddr;
|
_lastPacketSockAddr = senderSockAddr;
|
||||||
|
|
||||||
// Process unfiltered packets first.
|
if (sizeRead <= 0) {
|
||||||
|
// we either didn't pull anything for this packet or there was an error reading (this seems to trigger
|
||||||
|
// on windows even if there's not a packet available)
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
auto it = _unfilteredHandlers.find(senderSockAddr);
|
auto it = _unfilteredHandlers.find(senderSockAddr);
|
||||||
|
|
||||||
if (it != _unfilteredHandlers.end()) {
|
if (it != _unfilteredHandlers.end()) {
|
||||||
// we have a registered unfiltered handler for this HifiSockAddr (eg. STUN packet) - call that and return
|
// we have a registered unfiltered handler for this HifiSockAddr - call that and return
|
||||||
if (it->second) {
|
if (it->second) {
|
||||||
auto basePacket = BasePacket::fromReceivedPacket(std::move(datagram._datagram),
|
auto basePacket = BasePacket::fromReceivedPacket(std::move(buffer), packetSizeWithHeader, senderSockAddr);
|
||||||
datagramSize, senderSockAddr);
|
|
||||||
basePacket->setReceiveTime(receiveTime);
|
basePacket->setReceiveTime(receiveTime);
|
||||||
it->second(std::move(basePacket));
|
it->second(std::move(basePacket));
|
||||||
}
|
}
|
||||||
_incomingDatagrams.pop_front();
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if this was a control packet or a data packet
|
// check if this was a control packet or a data packet
|
||||||
bool isControlPacket = *reinterpret_cast<uint32_t*>(datagram._datagram.get()) & CONTROL_BIT_MASK;
|
bool isControlPacket = *reinterpret_cast<uint32_t*>(buffer.get()) & CONTROL_BIT_MASK;
|
||||||
|
|
||||||
if (isControlPacket) {
|
if (isControlPacket) {
|
||||||
// setup a control packet from the data we just read
|
// setup a control packet from the data we just read
|
||||||
auto controlPacket = ControlPacket::fromReceivedPacket(std::move(datagram._datagram), datagramSize, senderSockAddr);
|
auto controlPacket = ControlPacket::fromReceivedPacket(std::move(buffer), packetSizeWithHeader, senderSockAddr);
|
||||||
controlPacket->setReceiveTime(receiveTime);
|
controlPacket->setReceiveTime(receiveTime);
|
||||||
|
|
||||||
// move this control packet to the matching connection, if there is one
|
// move this control packet to the matching connection, if there is one
|
||||||
|
@ -406,13 +375,13 @@ void Socket::processPendingDatagrams(int) {
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// setup a Packet from the data we just read
|
// setup a Packet from the data we just read
|
||||||
auto packet = Packet::fromReceivedPacket(std::move(datagram._datagram), datagramSize, senderSockAddr);
|
auto packet = Packet::fromReceivedPacket(std::move(buffer), packetSizeWithHeader, senderSockAddr);
|
||||||
packet->setReceiveTime(receiveTime);
|
packet->setReceiveTime(receiveTime);
|
||||||
|
|
||||||
// save the sequence number in case this is the packet that sticks readyRead
|
// save the sequence number in case this is the packet that sticks readyRead
|
||||||
_lastReceivedSequenceNumber = packet->getSequenceNumber();
|
_lastReceivedSequenceNumber = packet->getSequenceNumber();
|
||||||
|
|
||||||
// call our hash verification operator to see if this packet is verified
|
// call our verification operator to see if this packet is verified
|
||||||
if (!_packetFilterOperator || _packetFilterOperator(*packet)) {
|
if (!_packetFilterOperator || _packetFilterOperator(*packet)) {
|
||||||
if (packet->isReliable()) {
|
if (packet->isReliable()) {
|
||||||
// if this was a reliable packet then signal the matching connection with the sequence number
|
// if this was a reliable packet then signal the matching connection with the sequence number
|
||||||
|
@ -426,7 +395,6 @@ void Socket::processPendingDatagrams(int) {
|
||||||
qCDebug(networking) << "Can't process packet: version" << (unsigned int)NLPacket::versionInHeader(*packet)
|
qCDebug(networking) << "Can't process packet: version" << (unsigned int)NLPacket::versionInHeader(*packet)
|
||||||
<< ", type" << NLPacket::typeInHeader(*packet);
|
<< ", type" << NLPacket::typeInHeader(*packet);
|
||||||
#endif
|
#endif
|
||||||
_incomingDatagrams.pop_front();
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -442,8 +410,6 @@ void Socket::processPendingDatagrams(int) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_incomingDatagrams.pop_front();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,6 @@
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <list>
|
|
||||||
|
|
||||||
#include <QtCore/QObject>
|
#include <QtCore/QObject>
|
||||||
#include <QtCore/QTimer>
|
#include <QtCore/QTimer>
|
||||||
|
@ -95,7 +94,6 @@ public:
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void clientHandshakeRequestComplete(const HifiSockAddr& sockAddr);
|
void clientHandshakeRequestComplete(const HifiSockAddr& sockAddr);
|
||||||
void pendingDatagrams(int datagramCount);
|
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void cleanupConnection(HifiSockAddr sockAddr);
|
void cleanupConnection(HifiSockAddr sockAddr);
|
||||||
|
@ -103,7 +101,6 @@ public slots:
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void readPendingDatagrams();
|
void readPendingDatagrams();
|
||||||
void processPendingDatagrams(int datagramCount);
|
|
||||||
void checkForReadyReadBackup();
|
void checkForReadyReadBackup();
|
||||||
|
|
||||||
void handleSocketError(QAbstractSocket::SocketError socketError);
|
void handleSocketError(QAbstractSocket::SocketError socketError);
|
||||||
|
@ -148,17 +145,6 @@ private:
|
||||||
SequenceNumber _lastReceivedSequenceNumber;
|
SequenceNumber _lastReceivedSequenceNumber;
|
||||||
HifiSockAddr _lastPacketSockAddr;
|
HifiSockAddr _lastPacketSockAddr;
|
||||||
|
|
||||||
struct Datagram {
|
|
||||||
QHostAddress _senderAddress;
|
|
||||||
int _senderPort;
|
|
||||||
int _datagramLength;
|
|
||||||
std::unique_ptr<char[]> _datagram;
|
|
||||||
p_high_resolution_clock::time_point _receiveTime;
|
|
||||||
};
|
|
||||||
|
|
||||||
std::list<Datagram> _incomingDatagrams;
|
|
||||||
int _maxDatagramsRead { 0 };
|
|
||||||
|
|
||||||
friend UDTTest;
|
friend UDTTest;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -286,7 +286,6 @@ void PhysicsEngine::reinsertObject(ObjectMotionState* object) {
|
||||||
void PhysicsEngine::processTransaction(PhysicsEngine::Transaction& transaction) {
|
void PhysicsEngine::processTransaction(PhysicsEngine::Transaction& transaction) {
|
||||||
// removes
|
// removes
|
||||||
for (auto object : transaction.objectsToRemove) {
|
for (auto object : transaction.objectsToRemove) {
|
||||||
bumpAndPruneContacts(object);
|
|
||||||
btRigidBody* body = object->getRigidBody();
|
btRigidBody* body = object->getRigidBody();
|
||||||
if (body) {
|
if (body) {
|
||||||
removeDynamicsForBody(body);
|
removeDynamicsForBody(body);
|
||||||
|
|
|
@ -16,16 +16,55 @@
|
||||||
#include "NumericalConstants.h"
|
#include "NumericalConstants.h"
|
||||||
#include "shared/ConicalViewFrustum.h"
|
#include "shared/ConicalViewFrustum.h"
|
||||||
|
|
||||||
// PrioritySortUtil is a helper for sorting 3D things relative to a ViewFrustum.
|
/* PrioritySortUtil is a helper for sorting 3D things relative to a ViewFrustum. To use:
|
||||||
|
|
||||||
const float OUT_OF_VIEW_PENALTY = -10.0f;
|
(1) Derive a class from pure-virtual PrioritySortUtil::Sortable that wraps a copy of
|
||||||
const float OUT_OF_VIEW_THRESHOLD = 0.5f * OUT_OF_VIEW_PENALTY;
|
the Thing you want to prioritize and sort:
|
||||||
|
|
||||||
|
class SortableWrapper: public PrioritySortUtil::Sortable {
|
||||||
|
public:
|
||||||
|
SortableWrapper(const Thing& thing) : _thing(thing) { }
|
||||||
|
glm::vec3 getPosition() const override { return _thing->getPosition(); }
|
||||||
|
float getRadius() const override { return 0.5f * _thing->getBoundingRadius(); }
|
||||||
|
uint64_t getTimestamp() const override { return _thing->getLastTime(); }
|
||||||
|
Thing getThing() const { return _thing; }
|
||||||
|
private:
|
||||||
|
Thing _thing;
|
||||||
|
};
|
||||||
|
|
||||||
|
(2) Make a PrioritySortUtil::PriorityQueue<Thing> and add them to the queue:
|
||||||
|
|
||||||
|
PrioritySortUtil::PriorityQueue<SortableWrapper> sortedThings(viewFrustum);
|
||||||
|
std::priority_queue< PrioritySortUtil::Sortable<Thing> > sortedThings;
|
||||||
|
for (thing in things) {
|
||||||
|
sortedThings.push(SortableWrapper(thing));
|
||||||
|
}
|
||||||
|
|
||||||
|
(3) Loop over your priority queue and do timeboxed work:
|
||||||
|
|
||||||
|
NOTE: Be careful using references to members of instances of T from std::priority_queue<T>.
|
||||||
|
Under the hood std::priority_queue<T> may re-use instances of T.
|
||||||
|
For example, after a pop() or a push() the top T may have the same memory address
|
||||||
|
as the top T before the pop() or push() (but point to a swapped instance of T).
|
||||||
|
This causes a reference to member variable of T to point to a different value
|
||||||
|
when operations taken on std::priority_queue<T> shuffle around the instances of T.
|
||||||
|
|
||||||
|
uint64_t cutoffTime = usecTimestampNow() + TIME_BUDGET;
|
||||||
|
while (!sortedThings.empty()) {
|
||||||
|
const Thing& thing = sortedThings.top();
|
||||||
|
// ...do work on thing...
|
||||||
|
sortedThings.pop();
|
||||||
|
if (usecTimestampNow() > cutoffTime) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
namespace PrioritySortUtil {
|
namespace PrioritySortUtil {
|
||||||
|
|
||||||
constexpr float DEFAULT_ANGULAR_COEF { 1.0f };
|
constexpr float DEFAULT_ANGULAR_COEF { 1.0f };
|
||||||
constexpr float DEFAULT_CENTER_COEF { 0.5f };
|
constexpr float DEFAULT_CENTER_COEF { 0.5f };
|
||||||
constexpr float DEFAULT_AGE_COEF { 0.25f };
|
constexpr float DEFAULT_AGE_COEF { 0.25f / (float)(USECS_PER_SECOND) };
|
||||||
|
|
||||||
class Sortable {
|
class Sortable {
|
||||||
public:
|
public:
|
||||||
|
@ -46,8 +85,7 @@ namespace PrioritySortUtil {
|
||||||
PriorityQueue(const ConicalViewFrustums& views) : _views(views) { }
|
PriorityQueue(const ConicalViewFrustums& views) : _views(views) { }
|
||||||
PriorityQueue(const ConicalViewFrustums& 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)
|
||||||
, _usecCurrentTime(usecTimestampNow()) {
|
{ }
|
||||||
}
|
|
||||||
|
|
||||||
void setViews(const ConicalViewFrustums& views) { _views = views; }
|
void setViews(const ConicalViewFrustums& views) { _views = views; }
|
||||||
|
|
||||||
|
@ -55,7 +93,6 @@ namespace PrioritySortUtil {
|
||||||
_angularWeight = angularWeight;
|
_angularWeight = angularWeight;
|
||||||
_centerWeight = centerWeight;
|
_centerWeight = centerWeight;
|
||||||
_ageWeight = ageWeight;
|
_ageWeight = ageWeight;
|
||||||
_usecCurrentTime = usecTimestampNow();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t size() const { return _vector.size(); }
|
size_t size() const { return _vector.size(); }
|
||||||
|
@ -94,18 +131,23 @@ namespace PrioritySortUtil {
|
||||||
glm::vec3 offset = position - view.getPosition();
|
glm::vec3 offset = position - view.getPosition();
|
||||||
float distance = glm::length(offset) + 0.001f; // add 1mm to avoid divide by zero
|
float distance = glm::length(offset) + 0.001f; // add 1mm to avoid divide by zero
|
||||||
const float MIN_RADIUS = 0.1f; // WORKAROUND for zero size objects (we still want them to sort by distance)
|
const float MIN_RADIUS = 0.1f; // WORKAROUND for zero size objects (we still want them to sort by distance)
|
||||||
float radius = glm::max(thing.getRadius(), MIN_RADIUS);
|
float radius = glm::min(thing.getRadius(), MIN_RADIUS);
|
||||||
// Other item's angle from view centre:
|
float cosineAngle = (glm::dot(offset, view.getDirection()) / distance);
|
||||||
float cosineAngle = glm::dot(offset, view.getDirection()) / distance;
|
float age = (float)(usecTimestampNow() - thing.getTimestamp());
|
||||||
float age = float((_usecCurrentTime - thing.getTimestamp()) / USECS_PER_SECOND);
|
|
||||||
|
|
||||||
// the "age" term accumulates at the sum of all weights
|
// we modulatate "age" drift rate by the cosineAngle term to make periphrial objects sort forward
|
||||||
float angularSize = radius / distance;
|
// at a reduced rate but we don't want the "age" term to go zero or negative so we clamp it
|
||||||
float priority = (_angularWeight * angularSize + _centerWeight * cosineAngle) * (age + 1.0f) + _ageWeight * age;
|
const float MIN_COSINE_ANGLE_FACTOR = 0.1f;
|
||||||
|
float cosineAngleFactor = glm::max(cosineAngle, MIN_COSINE_ANGLE_FACTOR);
|
||||||
|
|
||||||
|
float priority = _angularWeight * glm::max(radius, MIN_RADIUS) / distance
|
||||||
|
+ _centerWeight * cosineAngle
|
||||||
|
+ _ageWeight * cosineAngleFactor * age;
|
||||||
|
|
||||||
// decrement priority of things outside keyhole
|
// decrement priority of things outside keyhole
|
||||||
if (distance - radius > view.getRadius()) {
|
if (distance - radius > view.getRadius()) {
|
||||||
if (!view.intersects(offset, distance, radius)) {
|
if (!view.intersects(offset, distance, radius)) {
|
||||||
|
constexpr float OUT_OF_VIEW_PENALTY = -10.0f;
|
||||||
priority += OUT_OF_VIEW_PENALTY;
|
priority += OUT_OF_VIEW_PENALTY;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -117,13 +159,12 @@ namespace PrioritySortUtil {
|
||||||
float _angularWeight { DEFAULT_ANGULAR_COEF };
|
float _angularWeight { DEFAULT_ANGULAR_COEF };
|
||||||
float _centerWeight { DEFAULT_CENTER_COEF };
|
float _centerWeight { DEFAULT_CENTER_COEF };
|
||||||
float _ageWeight { DEFAULT_AGE_COEF };
|
float _ageWeight { DEFAULT_AGE_COEF };
|
||||||
quint64 _usecCurrentTime { 0 };
|
|
||||||
};
|
};
|
||||||
} // namespace PrioritySortUtil
|
} // namespace PrioritySortUtil
|
||||||
|
|
||||||
// for now we're keeping hard-coded sorted time budgets in one spot
|
// for now we're keeping hard-coded sorted time budgets in one spot
|
||||||
const uint64_t MAX_UPDATE_RENDERABLES_TIME_BUDGET = 2000; // usec
|
const uint64_t MAX_UPDATE_RENDERABLES_TIME_BUDGET = 2000; // usec
|
||||||
const uint64_t MIN_SORTED_UPDATE_RENDERABLES_TIME_BUDGET = 1000; // usec
|
const uint64_t MIN_SORTED_UPDATE_RENDERABLES_TIME_BUDGET = 1000; // usec
|
||||||
const uint64_t MAX_UPDATE_AVATARS_TIME_BUDGET = 2000; // usec
|
|
||||||
|
|
||||||
#endif // hifi_PrioritySortUtil_h
|
#endif // hifi_PrioritySortUtil_h
|
||||||
|
|
||||||
|
|
|
@ -35,13 +35,10 @@ var DEFAULT_SCRIPTS_COMBINED = [
|
||||||
];
|
];
|
||||||
var DEFAULT_SCRIPTS_SEPARATE = [
|
var DEFAULT_SCRIPTS_SEPARATE = [
|
||||||
"system/controllers/controllerScripts.js",
|
"system/controllers/controllerScripts.js",
|
||||||
|
"system/interstitialPage.js"
|
||||||
//"system/chat.js"
|
//"system/chat.js"
|
||||||
];
|
];
|
||||||
|
|
||||||
if (Settings.getValue("enableInterstitialMode", false)) {
|
|
||||||
DEFAULT_SCRIPTS_SEPARATE.push("system/interstitialPage.js");
|
|
||||||
}
|
|
||||||
|
|
||||||
// add a menu item for debugging
|
// add a menu item for debugging
|
||||||
var MENU_CATEGORY = "Developer";
|
var MENU_CATEGORY = "Developer";
|
||||||
var MENU_ITEM = "Debug defaultScripts.js";
|
var MENU_ITEM = "Debug defaultScripts.js";
|
||||||
|
|
|
@ -860,7 +860,7 @@ var toolBar = (function () {
|
||||||
propertiesTool.setVisible(false);
|
propertiesTool.setVisible(false);
|
||||||
selectionManager.clearSelections();
|
selectionManager.clearSelections();
|
||||||
cameraManager.disable();
|
cameraManager.disable();
|
||||||
selectionDisplay.disableTriggerMapping();
|
selectionDisplay.triggerMapping.disable();
|
||||||
tablet.landscape = false;
|
tablet.landscape = false;
|
||||||
Controller.disableMapping(CONTROLLER_MAPPING_NAME);
|
Controller.disableMapping(CONTROLLER_MAPPING_NAME);
|
||||||
} else {
|
} else {
|
||||||
|
@ -876,7 +876,7 @@ var toolBar = (function () {
|
||||||
gridTool.setVisible(true);
|
gridTool.setVisible(true);
|
||||||
grid.setEnabled(true);
|
grid.setEnabled(true);
|
||||||
propertiesTool.setVisible(true);
|
propertiesTool.setVisible(true);
|
||||||
selectionDisplay.enableTriggerMapping();
|
selectionDisplay.triggerMapping.enable();
|
||||||
print("starting tablet in landscape mode");
|
print("starting tablet in landscape mode");
|
||||||
tablet.landscape = true;
|
tablet.landscape = true;
|
||||||
Controller.enableMapping(CONTROLLER_MAPPING_NAME);
|
Controller.enableMapping(CONTROLLER_MAPPING_NAME);
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
/* global SelectionManager, SelectionDisplay, grid, rayPlaneIntersection, rayPlaneIntersection2, pushCommandForSelections,
|
/* global SelectionManager, SelectionDisplay, grid, rayPlaneIntersection, rayPlaneIntersection2, pushCommandForSelections,
|
||||||
getMainTabletIDs, getControllerWorldLocation, TRIGGER_ON_VALUE */
|
getMainTabletIDs, getControllerWorldLocation */
|
||||||
|
|
||||||
var SPACE_LOCAL = "local";
|
var SPACE_LOCAL = "local";
|
||||||
var SPACE_WORLD = "world";
|
var SPACE_WORLD = "world";
|
||||||
|
@ -22,7 +22,6 @@ var HIGHLIGHT_LIST_NAME = "editHandleHighlightList";
|
||||||
|
|
||||||
Script.include([
|
Script.include([
|
||||||
"./controllers.js",
|
"./controllers.js",
|
||||||
"./controllerDispatcherUtils.js",
|
|
||||||
"./utils.js"
|
"./utils.js"
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
@ -450,8 +449,6 @@ SelectionDisplay = (function() {
|
||||||
|
|
||||||
var RAIL_AXIS_LENGTH = 10000;
|
var RAIL_AXIS_LENGTH = 10000;
|
||||||
|
|
||||||
var NO_HAND = -1;
|
|
||||||
|
|
||||||
var TRANSLATE_DIRECTION = {
|
var TRANSLATE_DIRECTION = {
|
||||||
X: 0,
|
X: 0,
|
||||||
Y: 1,
|
Y: 1,
|
||||||
|
@ -482,6 +479,8 @@ SelectionDisplay = (function() {
|
||||||
ROLL: 2
|
ROLL: 2
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var NO_TRIGGER_HAND = -1;
|
||||||
|
|
||||||
var spaceMode = SPACE_LOCAL;
|
var spaceMode = SPACE_LOCAL;
|
||||||
var overlayNames = [];
|
var overlayNames = [];
|
||||||
var lastControllerPoses = [
|
var lastControllerPoses = [
|
||||||
|
@ -803,21 +802,11 @@ SelectionDisplay = (function() {
|
||||||
|
|
||||||
// We get mouseMoveEvents from the handControllers, via handControllerPointer.
|
// We get mouseMoveEvents from the handControllers, via handControllerPointer.
|
||||||
// But we dont' get mousePressEvents.
|
// But we dont' get mousePressEvents.
|
||||||
that.triggerClickMapping = Controller.newMapping(Script.resolvePath('') + '-click');
|
that.triggerMapping = Controller.newMapping(Script.resolvePath('') + '-click');
|
||||||
that.triggerPressMapping = Controller.newMapping(Script.resolvePath('') + '-press');
|
Script.scriptEnding.connect(that.triggerMapping.disable);
|
||||||
that.triggeredHand = NO_HAND;
|
that.triggeredHand = NO_TRIGGER_HAND;
|
||||||
that.pressedHand = NO_HAND;
|
|
||||||
that.triggered = function() {
|
that.triggered = function() {
|
||||||
return that.triggeredHand !== NO_HAND;
|
return that.triggeredHand !== NO_TRIGGER_HAND;
|
||||||
}
|
|
||||||
function pointingAtDesktopWindowOrTablet(hand) {
|
|
||||||
var pointingAtDesktopWindow = (hand === Controller.Standard.RightHand &&
|
|
||||||
SelectionManager.pointingAtDesktopWindowRight) ||
|
|
||||||
(hand === Controller.Standard.LeftHand &&
|
|
||||||
SelectionManager.pointingAtDesktopWindowLeft);
|
|
||||||
var pointingAtTablet = (hand === Controller.Standard.RightHand && SelectionManager.pointingAtTabletRight) ||
|
|
||||||
(hand === Controller.Standard.LeftHand && SelectionManager.pointingAtTabletLeft);
|
|
||||||
return pointingAtDesktopWindow || pointingAtTablet;
|
|
||||||
}
|
}
|
||||||
function makeClickHandler(hand) {
|
function makeClickHandler(hand) {
|
||||||
return function (clicked) {
|
return function (clicked) {
|
||||||
|
@ -825,39 +814,26 @@ SelectionDisplay = (function() {
|
||||||
if (that.triggered() && hand !== that.triggeredHand) {
|
if (that.triggered() && hand !== that.triggeredHand) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!that.triggered() && clicked && !pointingAtDesktopWindowOrTablet(hand)) {
|
if (!that.triggered() && clicked) {
|
||||||
|
var pointingAtDesktopWindow = (hand === Controller.Standard.RightHand &&
|
||||||
|
SelectionManager.pointingAtDesktopWindowRight) ||
|
||||||
|
(hand === Controller.Standard.LeftHand &&
|
||||||
|
SelectionManager.pointingAtDesktopWindowLeft);
|
||||||
|
var pointingAtTablet = (hand === Controller.Standard.RightHand && SelectionManager.pointingAtTabletRight) ||
|
||||||
|
(hand === Controller.Standard.LeftHand && SelectionManager.pointingAtTabletLeft);
|
||||||
|
if (pointingAtDesktopWindow || pointingAtTablet) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
that.triggeredHand = hand;
|
that.triggeredHand = hand;
|
||||||
that.mousePressEvent({});
|
that.mousePressEvent({});
|
||||||
} else if (that.triggered() && !clicked) {
|
} else if (that.triggered() && !clicked) {
|
||||||
that.triggeredHand = NO_HAND;
|
that.triggeredHand = NO_TRIGGER_HAND;
|
||||||
that.mouseReleaseEvent({});
|
that.mouseReleaseEvent({});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
function makePressHandler(hand) {
|
that.triggerMapping.from(Controller.Standard.RTClick).peek().to(makeClickHandler(Controller.Standard.RightHand));
|
||||||
return function (value) {
|
that.triggerMapping.from(Controller.Standard.LTClick).peek().to(makeClickHandler(Controller.Standard.LeftHand));
|
||||||
if (value >= TRIGGER_ON_VALUE && !that.triggered() && !pointingAtDesktopWindowOrTablet(hand)) {
|
|
||||||
that.pressedHand = hand;
|
|
||||||
that.updateHighlight({});
|
|
||||||
} else {
|
|
||||||
that.pressedHand = NO_HAND;
|
|
||||||
that.resetPreviousHandleColor();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
that.triggerClickMapping.from(Controller.Standard.RTClick).peek().to(makeClickHandler(Controller.Standard.RightHand));
|
|
||||||
that.triggerClickMapping.from(Controller.Standard.LTClick).peek().to(makeClickHandler(Controller.Standard.LeftHand));
|
|
||||||
that.triggerPressMapping.from(Controller.Standard.RT).peek().to(makePressHandler(Controller.Standard.RightHand));
|
|
||||||
that.triggerPressMapping.from(Controller.Standard.LT).peek().to(makePressHandler(Controller.Standard.LeftHand));
|
|
||||||
that.enableTriggerMapping = function() {
|
|
||||||
that.triggerClickMapping.enable();
|
|
||||||
that.triggerPressMapping.enable();
|
|
||||||
};
|
|
||||||
that.disableTriggerMapping = function() {
|
|
||||||
that.triggerClickMapping.disable();
|
|
||||||
that.triggerPressMapping.disable();
|
|
||||||
}
|
|
||||||
Script.scriptEnding.connect(that.disableTriggerMapping);
|
|
||||||
|
|
||||||
// FUNCTION DEF(s): Intersection Check Helpers
|
// FUNCTION DEF(s): Intersection Check Helpers
|
||||||
function testRayIntersect(queryRay, overlayIncludes, overlayExcludes) {
|
function testRayIntersect(queryRay, overlayIncludes, overlayExcludes) {
|
||||||
|
@ -985,7 +961,32 @@ SelectionDisplay = (function() {
|
||||||
return Uuid.NULL;
|
return Uuid.NULL;
|
||||||
};
|
};
|
||||||
|
|
||||||
that.updateHighlight = function(event) {
|
// FUNCTION: MOUSE MOVE EVENT
|
||||||
|
var lastMouseEvent = null;
|
||||||
|
that.mouseMoveEvent = function(event) {
|
||||||
|
var wantDebug = false;
|
||||||
|
if (wantDebug) {
|
||||||
|
print("=============== eST::MouseMoveEvent BEG =======================");
|
||||||
|
}
|
||||||
|
lastMouseEvent = event;
|
||||||
|
if (activeTool) {
|
||||||
|
if (wantDebug) {
|
||||||
|
print(" Trigger ActiveTool(" + activeTool.mode + ")'s onMove");
|
||||||
|
}
|
||||||
|
activeTool.onMove(event);
|
||||||
|
|
||||||
|
if (wantDebug) {
|
||||||
|
print(" Trigger SelectionManager::update");
|
||||||
|
}
|
||||||
|
SelectionManager._update();
|
||||||
|
|
||||||
|
if (wantDebug) {
|
||||||
|
print("=============== eST::MouseMoveEvent END =======================");
|
||||||
|
}
|
||||||
|
// EARLY EXIT--(Move handled via active tool)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
// if no tool is active, then just look for handles to highlight...
|
// if no tool is active, then just look for handles to highlight...
|
||||||
var pickRay = generalComputePickRay(event.x, event.y);
|
var pickRay = generalComputePickRay(event.x, event.y);
|
||||||
var result = Overlays.findRayIntersection(pickRay);
|
var result = Overlays.findRayIntersection(pickRay);
|
||||||
|
@ -1038,35 +1039,6 @@ SelectionDisplay = (function() {
|
||||||
} else {
|
} else {
|
||||||
that.resetPreviousHandleColor();
|
that.resetPreviousHandleColor();
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
// FUNCTION: MOUSE MOVE EVENT
|
|
||||||
var lastMouseEvent = null;
|
|
||||||
that.mouseMoveEvent = function(event) {
|
|
||||||
var wantDebug = false;
|
|
||||||
if (wantDebug) {
|
|
||||||
print("=============== eST::MouseMoveEvent BEG =======================");
|
|
||||||
}
|
|
||||||
lastMouseEvent = event;
|
|
||||||
if (activeTool) {
|
|
||||||
if (wantDebug) {
|
|
||||||
print(" Trigger ActiveTool(" + activeTool.mode + ")'s onMove");
|
|
||||||
}
|
|
||||||
activeTool.onMove(event);
|
|
||||||
|
|
||||||
if (wantDebug) {
|
|
||||||
print(" Trigger SelectionManager::update");
|
|
||||||
}
|
|
||||||
SelectionManager._update();
|
|
||||||
|
|
||||||
if (wantDebug) {
|
|
||||||
print("=============== eST::MouseMoveEvent END =======================");
|
|
||||||
}
|
|
||||||
// EARLY EXIT--(Move handled via active tool)
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
that.updateHighlight(event);
|
|
||||||
|
|
||||||
if (wantDebug) {
|
if (wantDebug) {
|
||||||
print("=============== eST::MouseMoveEvent END =======================");
|
print("=============== eST::MouseMoveEvent END =======================");
|
||||||
|
@ -1163,10 +1135,9 @@ SelectionDisplay = (function() {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
function controllerComputePickRay(hand) {
|
function controllerComputePickRay() {
|
||||||
var hand = that.triggered() ? that.triggeredHand : that.pressedHand;
|
var controllerPose = getControllerWorldLocation(that.triggeredHand, true);
|
||||||
var controllerPose = getControllerWorldLocation(hand, true);
|
if (controllerPose.valid && that.triggered()) {
|
||||||
if (controllerPose.valid) {
|
|
||||||
var controllerPosition = controllerPose.translation;
|
var controllerPosition = controllerPose.translation;
|
||||||
// This gets point direction right, but if you want general quaternion it would be more complicated:
|
// This gets point direction right, but if you want general quaternion it would be more complicated:
|
||||||
var controllerDirection = Quat.getUp(controllerPose.rotation);
|
var controllerDirection = Quat.getUp(controllerPose.rotation);
|
||||||
|
@ -1178,12 +1149,6 @@ SelectionDisplay = (function() {
|
||||||
return controllerComputePickRay() || Camera.computePickRay(x, y);
|
return controllerComputePickRay() || Camera.computePickRay(x, y);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getControllerAvatarFramePositionFromPickRay(pickRay) {
|
|
||||||
var controllerPosition = Vec3.subtract(pickRay.origin, MyAvatar.position);
|
|
||||||
controllerPosition = Vec3.multiplyQbyV(Quat.inverse(MyAvatar.orientation), controllerPosition);
|
|
||||||
return controllerPosition;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getDistanceToCamera(position) {
|
function getDistanceToCamera(position) {
|
||||||
var cameraPosition = Camera.getPosition();
|
var cameraPosition = Camera.getPosition();
|
||||||
var toCameraDistance = Vec3.length(Vec3.subtract(cameraPosition, position));
|
var toCameraDistance = Vec3.length(Vec3.subtract(cameraPosition, position));
|
||||||
|
@ -2118,7 +2083,6 @@ SelectionDisplay = (function() {
|
||||||
var rotation = null;
|
var rotation = null;
|
||||||
var previousPickRay = null;
|
var previousPickRay = null;
|
||||||
var beginMouseEvent = null;
|
var beginMouseEvent = null;
|
||||||
var beginControllerPosition = null;
|
|
||||||
|
|
||||||
var onBegin = function(event, pickRay, pickResult) {
|
var onBegin = function(event, pickRay, pickResult) {
|
||||||
var proportional = directionEnum === STRETCH_DIRECTION.ALL;
|
var proportional = directionEnum === STRETCH_DIRECTION.ALL;
|
||||||
|
@ -2254,9 +2218,6 @@ SelectionDisplay = (function() {
|
||||||
|
|
||||||
previousPickRay = pickRay;
|
previousPickRay = pickRay;
|
||||||
beginMouseEvent = event;
|
beginMouseEvent = event;
|
||||||
if (that.triggered()) {
|
|
||||||
beginControllerPosition = getControllerAvatarFramePositionFromPickRay(pickRay);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
var onEnd = function(event, reason) {
|
var onEnd = function(event, reason) {
|
||||||
|
@ -2296,14 +2257,12 @@ SelectionDisplay = (function() {
|
||||||
pickRay = previousPickRay;
|
pickRay = previousPickRay;
|
||||||
}
|
}
|
||||||
|
|
||||||
var controllerPose = getControllerWorldLocation(that.triggeredHand, true);
|
|
||||||
var controllerTrigger = HMD.isHMDAvailable() && HMD.isHandControllerAvailable() &&
|
|
||||||
controllerPose.valid && that.triggered();
|
|
||||||
|
|
||||||
// Are we using handControllers or Mouse - only relevant for 3D tools
|
// Are we using handControllers or Mouse - only relevant for 3D tools
|
||||||
|
var controllerPose = getControllerWorldLocation(that.triggeredHand, true);
|
||||||
var vector = null;
|
var vector = null;
|
||||||
var newPick = null;
|
var newPick = null;
|
||||||
if (controllerTrigger && directionFor3DStretch) {
|
if (HMD.isHMDAvailable() && HMD.isHandControllerAvailable() &&
|
||||||
|
controllerPose.valid && that.triggered() && directionFor3DStretch) {
|
||||||
localDeltaPivot = deltaPivot3D;
|
localDeltaPivot = deltaPivot3D;
|
||||||
newPick = pickRay.origin;
|
newPick = pickRay.origin;
|
||||||
vector = Vec3.subtract(newPick, lastPick3D);
|
vector = Vec3.subtract(newPick, lastPick3D);
|
||||||
|
@ -2327,23 +2286,12 @@ SelectionDisplay = (function() {
|
||||||
var newDimensions;
|
var newDimensions;
|
||||||
if (proportional) {
|
if (proportional) {
|
||||||
var viewportDimensions = Controller.getViewportDimensions();
|
var viewportDimensions = Controller.getViewportDimensions();
|
||||||
var toCameraDistance = getDistanceToCamera(position);
|
|
||||||
var dimensionsMultiple = toCameraDistance * STRETCH_DIRECTION_ALL_CAMERA_DISTANCE_MULTIPLE;
|
|
||||||
|
|
||||||
var dimensionChange;
|
|
||||||
if (controllerTrigger) {
|
|
||||||
var controllerPosition = getControllerAvatarFramePositionFromPickRay(pickRay);
|
|
||||||
var vecControllerDifference = Vec3.subtract(controllerPosition, beginControllerPosition);
|
|
||||||
var controllerDifference = vecControllerDifference.x + vecControllerDifference.y +
|
|
||||||
vecControllerDifference.z;
|
|
||||||
dimensionChange = controllerDifference * dimensionsMultiple;
|
|
||||||
} else {
|
|
||||||
var mouseXDifference = (event.x - beginMouseEvent.x) / viewportDimensions.x;
|
var mouseXDifference = (event.x - beginMouseEvent.x) / viewportDimensions.x;
|
||||||
var mouseYDifference = (beginMouseEvent.y - event.y) / viewportDimensions.y;
|
var mouseYDifference = (beginMouseEvent.y - event.y) / viewportDimensions.y;
|
||||||
var mouseDifference = mouseXDifference + mouseYDifference;
|
var mouseDifference = mouseXDifference + mouseYDifference;
|
||||||
dimensionChange = mouseDifference * dimensionsMultiple;
|
var toCameraDistance = getDistanceToCamera(position);
|
||||||
}
|
var dimensionsMultiple = toCameraDistance * STRETCH_DIRECTION_ALL_CAMERA_DISTANCE_MULTIPLE;
|
||||||
|
var dimensionChange = mouseDifference * dimensionsMultiple;
|
||||||
var averageInitialDimension = (initialDimensions.x + initialDimensions.y + initialDimensions.z) / 3;
|
var averageInitialDimension = (initialDimensions.x + initialDimensions.y + initialDimensions.z) / 3;
|
||||||
percentChange = dimensionChange / averageInitialDimension;
|
percentChange = dimensionChange / averageInitialDimension;
|
||||||
percentChange += 1.0;
|
percentChange += 1.0;
|
||||||
|
|
Loading…
Reference in a new issue