mirror of
https://github.com/overte-org/overte.git
synced 2025-08-09 14:47:41 +02:00
Merge commit '041a561dbcaa7280fd2c14ba2051b2add756ca6f'
This commit is contained in:
commit
3ef02085ff
21 changed files with 423 additions and 147 deletions
|
@ -23,6 +23,7 @@
|
||||||
#include <QtCore/QRegularExpression>
|
#include <QtCore/QRegularExpression>
|
||||||
#include <QtCore/QTimer>
|
#include <QtCore/QTimer>
|
||||||
#include <QtCore/QThread>
|
#include <QtCore/QThread>
|
||||||
|
#include <QtCore/QJsonDocument>
|
||||||
|
|
||||||
#include <AABox.h>
|
#include <AABox.h>
|
||||||
#include <AvatarLogging.h>
|
#include <AvatarLogging.h>
|
||||||
|
@ -32,6 +33,8 @@
|
||||||
#include <SharedUtil.h>
|
#include <SharedUtil.h>
|
||||||
#include <UUID.h>
|
#include <UUID.h>
|
||||||
#include <TryLocker.h>
|
#include <TryLocker.h>
|
||||||
|
#include "../AssignmentDynamicFactory.h"
|
||||||
|
#include "../entities/AssignmentParentFinder.h"
|
||||||
|
|
||||||
const QString AVATAR_MIXER_LOGGING_NAME = "avatar-mixer";
|
const QString AVATAR_MIXER_LOGGING_NAME = "avatar-mixer";
|
||||||
|
|
||||||
|
@ -55,6 +58,9 @@ AvatarMixer::AvatarMixer(ReceivedMessage& message) :
|
||||||
ThreadedAssignment(message),
|
ThreadedAssignment(message),
|
||||||
_slavePool(&_slaveSharedData)
|
_slavePool(&_slaveSharedData)
|
||||||
{
|
{
|
||||||
|
DependencyManager::registerInheritance<EntityDynamicFactoryInterface, AssignmentDynamicFactory>();
|
||||||
|
DependencyManager::set<AssignmentDynamicFactory>();
|
||||||
|
|
||||||
// make sure we hear about node kills so we can tell the other nodes
|
// make sure we hear about node kills so we can tell the other nodes
|
||||||
connect(DependencyManager::get<NodeList>().data(), &NodeList::nodeKilled, this, &AvatarMixer::handleAvatarKilled);
|
connect(DependencyManager::get<NodeList>().data(), &NodeList::nodeKilled, this, &AvatarMixer::handleAvatarKilled);
|
||||||
|
|
||||||
|
@ -69,6 +75,8 @@ AvatarMixer::AvatarMixer(ReceivedMessage& message) :
|
||||||
packetReceiver.registerListener(PacketType::RequestsDomainListData, this, "handleRequestsDomainListDataPacket");
|
packetReceiver.registerListener(PacketType::RequestsDomainListData, this, "handleRequestsDomainListDataPacket");
|
||||||
packetReceiver.registerListener(PacketType::SetAvatarTraits, this, "queueIncomingPacket");
|
packetReceiver.registerListener(PacketType::SetAvatarTraits, this, "queueIncomingPacket");
|
||||||
packetReceiver.registerListener(PacketType::BulkAvatarTraitsAck, this, "queueIncomingPacket");
|
packetReceiver.registerListener(PacketType::BulkAvatarTraitsAck, this, "queueIncomingPacket");
|
||||||
|
packetReceiver.registerListenerForTypes({ PacketType::OctreeStats, PacketType::EntityData, PacketType::EntityErase },
|
||||||
|
this, "handleOctreePacket");
|
||||||
|
|
||||||
packetReceiver.registerListenerForTypes({
|
packetReceiver.registerListenerForTypes({
|
||||||
PacketType::ReplicatedAvatarIdentity,
|
PacketType::ReplicatedAvatarIdentity,
|
||||||
|
@ -240,6 +248,10 @@ void AvatarMixer::start() {
|
||||||
|
|
||||||
int lockWait, nodeTransform, functor;
|
int lockWait, nodeTransform, functor;
|
||||||
|
|
||||||
|
{
|
||||||
|
_entityViewer.queryOctree();
|
||||||
|
}
|
||||||
|
|
||||||
// Allow nodes to process any pending/queued packets across our worker threads
|
// Allow nodes to process any pending/queued packets across our worker threads
|
||||||
{
|
{
|
||||||
auto start = usecTimestampNow();
|
auto start = usecTimestampNow();
|
||||||
|
@ -775,6 +787,7 @@ void AvatarMixer::sendStatsPacket() {
|
||||||
slavesAggregatObject["sent_4_averageDataBytes"] = TIGHT_LOOP_STAT(aggregateStats.numDataBytesSent);
|
slavesAggregatObject["sent_4_averageDataBytes"] = TIGHT_LOOP_STAT(aggregateStats.numDataBytesSent);
|
||||||
slavesAggregatObject["sent_5_averageTraitsBytes"] = TIGHT_LOOP_STAT(aggregateStats.numTraitsBytesSent);
|
slavesAggregatObject["sent_5_averageTraitsBytes"] = TIGHT_LOOP_STAT(aggregateStats.numTraitsBytesSent);
|
||||||
slavesAggregatObject["sent_6_averageIdentityBytes"] = TIGHT_LOOP_STAT(aggregateStats.numIdentityBytesSent);
|
slavesAggregatObject["sent_6_averageIdentityBytes"] = TIGHT_LOOP_STAT(aggregateStats.numIdentityBytesSent);
|
||||||
|
slavesAggregatObject["sent_7_averageHeroAvatars"] = TIGHT_LOOP_STAT(aggregateStats.numHeroesIncluded);
|
||||||
|
|
||||||
slavesAggregatObject["timing_1_processIncomingPackets"] = TIGHT_LOOP_STAT_UINT64(aggregateStats.processIncomingPacketsElapsedTime);
|
slavesAggregatObject["timing_1_processIncomingPackets"] = TIGHT_LOOP_STAT_UINT64(aggregateStats.processIncomingPacketsElapsedTime);
|
||||||
slavesAggregatObject["timing_2_ignoreCalculation"] = TIGHT_LOOP_STAT_UINT64(aggregateStats.ignoreCalculationElapsedTime);
|
slavesAggregatObject["timing_2_ignoreCalculation"] = TIGHT_LOOP_STAT_UINT64(aggregateStats.ignoreCalculationElapsedTime);
|
||||||
|
@ -882,13 +895,15 @@ AvatarMixerClientData* AvatarMixer::getOrCreateClientData(SharedNodePointer node
|
||||||
void AvatarMixer::domainSettingsRequestComplete() {
|
void AvatarMixer::domainSettingsRequestComplete() {
|
||||||
auto nodeList = DependencyManager::get<NodeList>();
|
auto nodeList = DependencyManager::get<NodeList>();
|
||||||
nodeList->addSetOfNodeTypesToNodeInterestSet({
|
nodeList->addSetOfNodeTypesToNodeInterestSet({
|
||||||
NodeType::Agent, NodeType::EntityScriptServer,
|
NodeType::Agent, NodeType::EntityScriptServer, NodeType::EntityServer,
|
||||||
NodeType::UpstreamAvatarMixer, NodeType::DownstreamAvatarMixer
|
NodeType::UpstreamAvatarMixer, NodeType::DownstreamAvatarMixer
|
||||||
});
|
});
|
||||||
|
|
||||||
// parse the settings to pull out the values we need
|
// parse the settings to pull out the values we need
|
||||||
parseDomainServerSettings(nodeList->getDomainHandler().getSettingsObject());
|
parseDomainServerSettings(nodeList->getDomainHandler().getSettingsObject());
|
||||||
|
|
||||||
|
setupEntityQuery();
|
||||||
|
|
||||||
// start our tight loop...
|
// start our tight loop...
|
||||||
start();
|
start();
|
||||||
}
|
}
|
||||||
|
@ -976,3 +991,45 @@ void AvatarMixer::parseDomainServerSettings(const QJsonObject& domainSettings) {
|
||||||
qCDebug(avatars) << "Avatars other than" << _slaveSharedData.skeletonURLWhitelist << "will be replaced by" << (_slaveSharedData.skeletonReplacementURL.isEmpty() ? "default" : _slaveSharedData.skeletonReplacementURL.toString());
|
qCDebug(avatars) << "Avatars other than" << _slaveSharedData.skeletonURLWhitelist << "will be replaced by" << (_slaveSharedData.skeletonReplacementURL.isEmpty() ? "default" : _slaveSharedData.skeletonReplacementURL.toString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AvatarMixer::setupEntityQuery() {
|
||||||
|
_entityViewer.init();
|
||||||
|
DependencyManager::registerInheritance<SpatialParentFinder, AssignmentParentFinder>();
|
||||||
|
DependencyManager::set<AssignmentParentFinder>(_entityViewer.getTree());
|
||||||
|
_slaveSharedData.entityTree = _entityViewer.getTree();
|
||||||
|
|
||||||
|
// ES query: {"avatarPriority": true, "type": "Zone"}
|
||||||
|
QJsonObject priorityZoneQuery;
|
||||||
|
priorityZoneQuery["avatarPriority"] = true;
|
||||||
|
priorityZoneQuery["type"] = "Zone";
|
||||||
|
|
||||||
|
_entityViewer.getOctreeQuery().setJSONParameters(priorityZoneQuery);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AvatarMixer::handleOctreePacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode) {
|
||||||
|
PacketType packetType = message->getType();
|
||||||
|
|
||||||
|
switch (packetType) {
|
||||||
|
case PacketType::OctreeStats:
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PacketType::EntityData:
|
||||||
|
_entityViewer.processDatagram(*message, senderNode);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PacketType::EntityErase:
|
||||||
|
_entityViewer.processEraseMessage(*message, senderNode);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
qCDebug(avatars) << "Unexpected packet type:" << packetType;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AvatarMixer::aboutToFinish() {
|
||||||
|
DependencyManager::destroy<AssignmentDynamicFactory>();
|
||||||
|
DependencyManager::destroy<AssignmentParentFinder>();
|
||||||
|
|
||||||
|
ThreadedAssignment::aboutToFinish();
|
||||||
|
}
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
#include <PortableHighResolutionClock.h>
|
#include <PortableHighResolutionClock.h>
|
||||||
|
|
||||||
#include <ThreadedAssignment.h>
|
#include <ThreadedAssignment.h>
|
||||||
|
#include "../entities/EntityTreeHeadlessViewer.h"
|
||||||
#include "AvatarMixerClientData.h"
|
#include "AvatarMixerClientData.h"
|
||||||
|
|
||||||
#include "AvatarMixerSlavePool.h"
|
#include "AvatarMixerSlavePool.h"
|
||||||
|
@ -29,6 +30,7 @@ class AvatarMixer : public ThreadedAssignment {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
AvatarMixer(ReceivedMessage& message);
|
AvatarMixer(ReceivedMessage& message);
|
||||||
|
virtual void aboutToFinish() override;
|
||||||
|
|
||||||
static bool shouldReplicateTo(const Node& from, const Node& to) {
|
static bool shouldReplicateTo(const Node& from, const Node& to) {
|
||||||
return to.getType() == NodeType::DownstreamAvatarMixer &&
|
return to.getType() == NodeType::DownstreamAvatarMixer &&
|
||||||
|
@ -57,6 +59,7 @@ private slots:
|
||||||
void handleReplicatedBulkAvatarPacket(QSharedPointer<ReceivedMessage> message);
|
void handleReplicatedBulkAvatarPacket(QSharedPointer<ReceivedMessage> message);
|
||||||
void domainSettingsRequestComplete();
|
void domainSettingsRequestComplete();
|
||||||
void handlePacketVersionMismatch(PacketType type, const HifiSockAddr& senderSockAddr, const QUuid& senderUUID);
|
void handlePacketVersionMismatch(PacketType type, const HifiSockAddr& senderSockAddr, const QUuid& senderUUID);
|
||||||
|
void handleOctreePacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
|
||||||
void start();
|
void start();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -71,8 +74,13 @@ private:
|
||||||
|
|
||||||
void optionallyReplicatePacket(ReceivedMessage& message, const Node& node);
|
void optionallyReplicatePacket(ReceivedMessage& message, const Node& node);
|
||||||
|
|
||||||
|
void setupEntityQuery();
|
||||||
|
|
||||||
p_high_resolution_clock::time_point _lastFrameTimestamp;
|
p_high_resolution_clock::time_point _lastFrameTimestamp;
|
||||||
|
|
||||||
|
// Attach to entity tree for avatar-priority zone info.
|
||||||
|
EntityTreeHeadlessViewer _entityViewer;
|
||||||
|
|
||||||
// FIXME - new throttling - use these values somehow
|
// FIXME - new throttling - use these values somehow
|
||||||
float _trailingMixRatio { 0.0f };
|
float _trailingMixRatio { 0.0f };
|
||||||
float _throttlingRatio { 0.0f };
|
float _throttlingRatio { 0.0f };
|
||||||
|
|
|
@ -16,6 +16,10 @@
|
||||||
|
|
||||||
#include <DependencyManager.h>
|
#include <DependencyManager.h>
|
||||||
#include <NodeList.h>
|
#include <NodeList.h>
|
||||||
|
#include <EntityTree.h>
|
||||||
|
#include <ZoneEntityItem.h>
|
||||||
|
|
||||||
|
#include "AvatarLogging.h"
|
||||||
|
|
||||||
#include "AvatarMixerSlave.h"
|
#include "AvatarMixerSlave.h"
|
||||||
|
|
||||||
|
@ -62,7 +66,7 @@ int AvatarMixerClientData::processPackets(const SlaveSharedData& slaveSharedData
|
||||||
|
|
||||||
switch (packet->getType()) {
|
switch (packet->getType()) {
|
||||||
case PacketType::AvatarData:
|
case PacketType::AvatarData:
|
||||||
parseData(*packet);
|
parseData(*packet, slaveSharedData);
|
||||||
break;
|
break;
|
||||||
case PacketType::SetAvatarTraits:
|
case PacketType::SetAvatarTraits:
|
||||||
processSetTraitsMessage(*packet, slaveSharedData, *node);
|
processSetTraitsMessage(*packet, slaveSharedData, *node);
|
||||||
|
@ -80,7 +84,38 @@ int AvatarMixerClientData::processPackets(const SlaveSharedData& slaveSharedData
|
||||||
return packetsProcessed;
|
return packetsProcessed;
|
||||||
}
|
}
|
||||||
|
|
||||||
int AvatarMixerClientData::parseData(ReceivedMessage& message) {
|
namespace {
|
||||||
|
using std::static_pointer_cast;
|
||||||
|
|
||||||
|
// Operator to find if a point is within an avatar-priority (hero) Zone Entity.
|
||||||
|
struct FindPriorityZone {
|
||||||
|
glm::vec3 position;
|
||||||
|
bool isInPriorityZone { false };
|
||||||
|
static bool operation(const OctreeElementPointer& element, void* extraData) {
|
||||||
|
auto findPriorityZone = static_cast<FindPriorityZone*>(extraData);
|
||||||
|
if (element->getAACube().contains(findPriorityZone->position)) {
|
||||||
|
const EntityTreeElementPointer entityTreeElement = static_pointer_cast<EntityTreeElement>(element);
|
||||||
|
entityTreeElement->forEachEntity([&findPriorityZone](EntityItemPointer item) {
|
||||||
|
if (item->getType() == EntityTypes::Zone
|
||||||
|
&& item->contains(findPriorityZone->position)
|
||||||
|
&& static_pointer_cast<ZoneEntityItem>(item)->getAvatarPriority()) {
|
||||||
|
findPriorityZone->isInPriorityZone = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (findPriorityZone->isInPriorityZone) {
|
||||||
|
return false; // For now, stop at first hit.
|
||||||
|
} else {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} else { // Position isn't within this subspace, so end recursion.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // Close anonymous namespace.
|
||||||
|
|
||||||
|
int AvatarMixerClientData::parseData(ReceivedMessage& message, const SlaveSharedData& slaveSharedData) {
|
||||||
// pull the sequence number from the data first
|
// pull the sequence number from the data first
|
||||||
uint16_t sequenceNumber;
|
uint16_t sequenceNumber;
|
||||||
|
|
||||||
|
@ -90,9 +125,33 @@ int AvatarMixerClientData::parseData(ReceivedMessage& message) {
|
||||||
incrementNumOutOfOrderSends();
|
incrementNumOutOfOrderSends();
|
||||||
}
|
}
|
||||||
_lastReceivedSequenceNumber = sequenceNumber;
|
_lastReceivedSequenceNumber = sequenceNumber;
|
||||||
|
glm::vec3 oldPosition = getPosition();
|
||||||
|
|
||||||
// compute the offset to the data payload
|
// compute the offset to the data payload
|
||||||
return _avatar->parseDataFromBuffer(message.readWithoutCopy(message.getBytesLeftToRead()));
|
if (!_avatar->parseDataFromBuffer(message.readWithoutCopy(message.getBytesLeftToRead()))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto newPosition = getPosition();
|
||||||
|
if (newPosition != oldPosition) {
|
||||||
|
//#define AVATAR_HERO_TEST_HACK
|
||||||
|
#ifdef AVATAR_HERO_TEST_HACK
|
||||||
|
{
|
||||||
|
const static QString heroKey { "HERO" };
|
||||||
|
_avatar->setPriorityAvatar(_avatar->getDisplayName().contains(heroKey));
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
EntityTree& entityTree = *slaveSharedData.entityTree;
|
||||||
|
FindPriorityZone findPriorityZone { newPosition, false } ;
|
||||||
|
entityTree.recurseTreeWithOperation(&FindPriorityZone::operation, &findPriorityZone);
|
||||||
|
_avatar->setPriorityAvatar(findPriorityZone.isInPriorityZone);
|
||||||
|
//if (findPriorityZone.isInPriorityZone) {
|
||||||
|
// qCWarning(avatars) << "Avatar" << _avatar->getSessionDisplayName() << "in hero zone";
|
||||||
|
//}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AvatarMixerClientData::processSetTraitsMessage(ReceivedMessage& message,
|
void AvatarMixerClientData::processSetTraitsMessage(ReceivedMessage& message,
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
#include <QtCore/QJsonObject>
|
#include <QtCore/QJsonObject>
|
||||||
#include <QtCore/QUrl>
|
#include <QtCore/QUrl>
|
||||||
|
|
||||||
#include <AvatarData.h>
|
#include "MixerAvatar.h"
|
||||||
#include <AssociatedTraitValues.h>
|
#include <AssociatedTraitValues.h>
|
||||||
#include <NodeData.h>
|
#include <NodeData.h>
|
||||||
#include <NumericalConstants.h>
|
#include <NumericalConstants.h>
|
||||||
|
@ -45,11 +45,12 @@ public:
|
||||||
using HRCTime = p_high_resolution_clock::time_point;
|
using HRCTime = p_high_resolution_clock::time_point;
|
||||||
using PerNodeTraitVersions = std::unordered_map<Node::LocalID, AvatarTraits::TraitVersions>;
|
using PerNodeTraitVersions = std::unordered_map<Node::LocalID, AvatarTraits::TraitVersions>;
|
||||||
|
|
||||||
int parseData(ReceivedMessage& message) override;
|
using NodeData::parseData; // Avoid clang warning about hiding.
|
||||||
AvatarData& getAvatar() { return *_avatar; }
|
int parseData(ReceivedMessage& message, const SlaveSharedData& SlaveSharedData);
|
||||||
const AvatarData& getAvatar() const { return *_avatar; }
|
MixerAvatar& getAvatar() { return *_avatar; }
|
||||||
const AvatarData* getConstAvatarData() const { return _avatar.get(); }
|
const MixerAvatar& getAvatar() const { return *_avatar; }
|
||||||
AvatarSharedPointer getAvatarSharedPointer() const { return _avatar; }
|
const MixerAvatar* getConstAvatarData() const { return _avatar.get(); }
|
||||||
|
MixerAvatarSharedPointer getAvatarSharedPointer() const { return _avatar; }
|
||||||
|
|
||||||
uint16_t getLastBroadcastSequenceNumber(NLPacket::LocalID nodeID) const;
|
uint16_t getLastBroadcastSequenceNumber(NLPacket::LocalID nodeID) const;
|
||||||
void setLastBroadcastSequenceNumber(NLPacket::LocalID nodeID, uint16_t sequenceNumber)
|
void setLastBroadcastSequenceNumber(NLPacket::LocalID nodeID, uint16_t sequenceNumber)
|
||||||
|
@ -163,7 +164,7 @@ private:
|
||||||
};
|
};
|
||||||
PacketQueue _packetQueue;
|
PacketQueue _packetQueue;
|
||||||
|
|
||||||
AvatarSharedPointer _avatar { new AvatarData() };
|
MixerAvatarSharedPointer _avatar { new MixerAvatar() };
|
||||||
|
|
||||||
uint16_t _lastReceivedSequenceNumber { 0 };
|
uint16_t _lastReceivedSequenceNumber { 0 };
|
||||||
std::unordered_map<NLPacket::LocalID, uint16_t> _lastBroadcastSequenceNumbers;
|
std::unordered_map<NLPacket::LocalID, uint16_t> _lastBroadcastSequenceNumbers;
|
||||||
|
|
|
@ -281,7 +281,34 @@ AABox computeBubbleBox(const AvatarData& avatar, float bubbleExpansionFactor) {
|
||||||
return box;
|
return box;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
class SortableAvatar : public PrioritySortUtil::Sortable {
|
||||||
|
public:
|
||||||
|
SortableAvatar() = delete;
|
||||||
|
SortableAvatar(const MixerAvatar* avatar, const Node* avatarNode, uint64_t lastEncodeTime)
|
||||||
|
: _avatar(avatar), _node(avatarNode), _lastEncodeTime(lastEncodeTime) {
|
||||||
|
}
|
||||||
|
glm::vec3 getPosition() const override { return _avatar->getClientGlobalPosition(); }
|
||||||
|
float getRadius() const override {
|
||||||
|
glm::vec3 nodeBoxScale = _avatar->getGlobalBoundingBox().getScale();
|
||||||
|
return 0.5f * glm::max(nodeBoxScale.x, glm::max(nodeBoxScale.y, nodeBoxScale.z));
|
||||||
|
}
|
||||||
|
uint64_t getTimestamp() const override {
|
||||||
|
return _lastEncodeTime;
|
||||||
|
}
|
||||||
|
const Node* getNode() const { return _node; }
|
||||||
|
const MixerAvatar* getAvatar() const { return _avatar; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
const MixerAvatar* _avatar;
|
||||||
|
const Node* _node;
|
||||||
|
uint64_t _lastEncodeTime;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // Close anonymous namespace.
|
||||||
|
|
||||||
void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node) {
|
void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node) {
|
||||||
|
const float AVATAR_HERO_FRACTION { 0.4f };
|
||||||
const Node* destinationNode = node.data();
|
const Node* destinationNode = node.data();
|
||||||
|
|
||||||
auto nodeList = DependencyManager::get<NodeList>();
|
auto nodeList = DependencyManager::get<NodeList>();
|
||||||
|
@ -314,8 +341,9 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node)
|
||||||
int identityBytesSent = 0;
|
int identityBytesSent = 0;
|
||||||
int traitBytesSent = 0;
|
int traitBytesSent = 0;
|
||||||
|
|
||||||
// max number of avatarBytes per frame
|
// max number of avatarBytes per frame (13 900, typical)
|
||||||
int maxAvatarBytesPerFrame = int(_maxKbpsPerNode * BYTES_PER_KILOBIT / AVATAR_MIXER_BROADCAST_FRAMES_PER_SECOND);
|
const int maxAvatarBytesPerFrame = int(_maxKbpsPerNode * BYTES_PER_KILOBIT / AVATAR_MIXER_BROADCAST_FRAMES_PER_SECOND);
|
||||||
|
const int maxHeroBytesPerFrame = int(maxAvatarBytesPerFrame * AVATAR_HERO_FRACTION); // 5555, typical
|
||||||
|
|
||||||
// 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;
|
||||||
|
@ -339,34 +367,21 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node)
|
||||||
const float MY_AVATAR_BUBBLE_EXPANSION_FACTOR = 4.0f; // magic number determined emperically
|
const float MY_AVATAR_BUBBLE_EXPANSION_FACTOR = 4.0f; // magic number determined emperically
|
||||||
AABox nodeBox = computeBubbleBox(avatar, MY_AVATAR_BUBBLE_EXPANSION_FACTOR);
|
AABox nodeBox = computeBubbleBox(avatar, MY_AVATAR_BUBBLE_EXPANSION_FACTOR);
|
||||||
|
|
||||||
class SortableAvatar: public PrioritySortUtil::Sortable {
|
|
||||||
public:
|
|
||||||
SortableAvatar() = delete;
|
|
||||||
SortableAvatar(const AvatarData* avatar, const Node* avatarNode, uint64_t lastEncodeTime)
|
|
||||||
: _avatar(avatar), _node(avatarNode), _lastEncodeTime(lastEncodeTime) {}
|
|
||||||
glm::vec3 getPosition() const override { return _avatar->getClientGlobalPosition(); }
|
|
||||||
float getRadius() const override {
|
|
||||||
glm::vec3 nodeBoxScale = _avatar->getGlobalBoundingBox().getScale();
|
|
||||||
return 0.5f * glm::max(nodeBoxScale.x, glm::max(nodeBoxScale.y, nodeBoxScale.z));
|
|
||||||
}
|
|
||||||
uint64_t getTimestamp() const override {
|
|
||||||
return _lastEncodeTime;
|
|
||||||
}
|
|
||||||
const Node* getNode() const { return _node; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
const AvatarData* _avatar;
|
|
||||||
const Node* _node;
|
|
||||||
uint64_t _lastEncodeTime;
|
|
||||||
};
|
|
||||||
|
|
||||||
// prepare to sort
|
// prepare to sort
|
||||||
const auto& cameraViews = nodeData->getViewFrustums();
|
const auto& cameraViews = nodeData->getViewFrustums();
|
||||||
PrioritySortUtil::PriorityQueue<SortableAvatar> sortedAvatars(cameraViews,
|
|
||||||
AvatarData::_avatarSortCoefficientSize,
|
using AvatarPriorityQueue = PrioritySortUtil::PriorityQueue<SortableAvatar>;
|
||||||
AvatarData::_avatarSortCoefficientCenter,
|
// Keep two independent queues, one for heroes and one for the riff-raff.
|
||||||
AvatarData::_avatarSortCoefficientAge);
|
enum PriorityVariants { kHero, kNonhero };
|
||||||
sortedAvatars.reserve(_end - _begin);
|
AvatarPriorityQueue avatarPriorityQueues[2] =
|
||||||
|
{
|
||||||
|
{cameraViews, AvatarData::_avatarSortCoefficientSize,
|
||||||
|
AvatarData::_avatarSortCoefficientCenter, AvatarData::_avatarSortCoefficientAge},
|
||||||
|
{cameraViews, AvatarData::_avatarSortCoefficientSize,
|
||||||
|
AvatarData::_avatarSortCoefficientCenter, AvatarData::_avatarSortCoefficientAge}
|
||||||
|
};
|
||||||
|
|
||||||
|
avatarPriorityQueues[kNonhero].reserve(_end - _begin);
|
||||||
|
|
||||||
for (auto listedNode = _begin; listedNode != _end; ++listedNode) {
|
for (auto listedNode = _begin; listedNode != _end; ++listedNode) {
|
||||||
Node* otherNodeRaw = (*listedNode).data();
|
Node* otherNodeRaw = (*listedNode).data();
|
||||||
|
@ -446,10 +461,11 @@ 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();
|
const MixerAvatar* avatarNodeData = avatarClientNodeData->getConstAvatarData();
|
||||||
auto lastEncodeTime = nodeData->getLastOtherAvatarEncodeTime(avatarNode->getLocalID());
|
auto lastEncodeTime = nodeData->getLastOtherAvatarEncodeTime(avatarNode->getLocalID());
|
||||||
|
|
||||||
sortedAvatars.push(SortableAvatar(avatarNodeData, avatarNode, lastEncodeTime));
|
avatarPriorityQueues[avatarNodeData->getPriorityAvatar() ? kHero : kNonhero].push(
|
||||||
|
SortableAvatar(avatarNodeData, avatarNode, lastEncodeTime));
|
||||||
}
|
}
|
||||||
|
|
||||||
// If Avatar A's PAL WAS open but is no longer open, AND
|
// If Avatar A's PAL WAS open but is no longer open, AND
|
||||||
|
@ -475,115 +491,132 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node)
|
||||||
|
|
||||||
// loop through our sorted avatars and allocate our bandwidth to them accordingly
|
// loop through our sorted avatars and allocate our bandwidth to them accordingly
|
||||||
|
|
||||||
int remainingAvatars = (int)sortedAvatars.size();
|
int remainingAvatars = (int)avatarPriorityQueues[kHero].size() + (int)avatarPriorityQueues[kNonhero].size();
|
||||||
auto traitsPacketList = NLPacketList::create(PacketType::BulkAvatarTraits, QByteArray(), true, true);
|
auto traitsPacketList = NLPacketList::create(PacketType::BulkAvatarTraits, QByteArray(), true, true);
|
||||||
|
|
||||||
auto avatarPacket = NLPacket::create(PacketType::BulkAvatarData);
|
auto avatarPacket = NLPacket::create(PacketType::BulkAvatarData);
|
||||||
const int avatarPacketCapacity = avatarPacket->getPayloadCapacity();
|
const int avatarPacketCapacity = avatarPacket->getPayloadCapacity();
|
||||||
int avatarSpaceAvailable = avatarPacketCapacity;
|
int avatarSpaceAvailable = avatarPacketCapacity;
|
||||||
int numPacketsSent = 0;
|
int numPacketsSent = 0;
|
||||||
|
int numAvatarsSent = 0;
|
||||||
auto identityPacketList = NLPacketList::create(PacketType::AvatarIdentity, QByteArray(), true, true);
|
auto identityPacketList = NLPacketList::create(PacketType::AvatarIdentity, QByteArray(), true, true);
|
||||||
|
|
||||||
const auto& sortedAvatarVector = sortedAvatars.getSortedVector(numToSendEst);
|
for (PriorityVariants currentVariant = kHero; currentVariant <= kNonhero; ++((int&)currentVariant)) {
|
||||||
for (const auto& sortedAvatar : sortedAvatarVector) {
|
const auto& sortedAvatarVector = avatarPriorityQueues[currentVariant].getSortedVector(numToSendEst);
|
||||||
const Node* otherNode = sortedAvatar.getNode();
|
for (const auto& sortedAvatar : sortedAvatarVector) {
|
||||||
auto lastEncodeForOther = sortedAvatar.getTimestamp();
|
const Node* otherNode = sortedAvatar.getNode();
|
||||||
|
auto lastEncodeForOther = sortedAvatar.getTimestamp();
|
||||||
|
|
||||||
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;
|
AvatarData::AvatarDataDetail detail = AvatarData::NoData;
|
||||||
|
|
||||||
// NOTE: Here's where we determine if we are over budget and drop remaining avatars,
|
// 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.
|
// or send minimal avatar data in uncommon case of PALIsOpen.
|
||||||
int minimRemainingAvatarBytes = minimumBytesPerAvatar * remainingAvatars;
|
int minimRemainingAvatarBytes = minimumBytesPerAvatar * remainingAvatars;
|
||||||
auto frameByteEstimate = identityBytesSent + traitBytesSent + numAvatarDataBytes + minimRemainingAvatarBytes;
|
auto frameByteEstimate = identityBytesSent + traitBytesSent + numAvatarDataBytes + minimRemainingAvatarBytes;
|
||||||
bool overBudget = frameByteEstimate > maxAvatarBytesPerFrame;
|
bool overBudget = frameByteEstimate > maxAvatarBytesPerFrame;
|
||||||
if (overBudget) {
|
if (overBudget) {
|
||||||
if (PALIsOpen) {
|
if (PALIsOpen) {
|
||||||
_stats.overBudgetAvatars++;
|
_stats.overBudgetAvatars++;
|
||||||
detail = AvatarData::PALMinimum;
|
detail = AvatarData::PALMinimum;
|
||||||
} else {
|
} else {
|
||||||
_stats.overBudgetAvatars += remainingAvatars;
|
_stats.overBudgetAvatars += remainingAvatars;
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool overHeroBudget = currentVariant == kHero && numAvatarDataBytes > maxHeroBytesPerFrame;
|
||||||
|
if (overHeroBudget) {
|
||||||
|
break; // No more heroes (this frame).
|
||||||
|
}
|
||||||
|
|
||||||
|
auto startAvatarDataPacking = chrono::high_resolution_clock::now();
|
||||||
|
|
||||||
|
const AvatarMixerClientData* otherNodeData = reinterpret_cast<const AvatarMixerClientData*>(otherNode->getLinkedData());
|
||||||
|
const MixerAvatar* otherAvatar = otherNodeData->getConstAvatarData();
|
||||||
|
|
||||||
|
// Typically all out-of-view avatars but such avatars' priorities will rise with time:
|
||||||
|
bool isLowerPriority = sortedAvatar.getPriority() <= OUT_OF_VIEW_THRESHOLD;
|
||||||
|
|
||||||
|
if (isLowerPriority) {
|
||||||
|
detail = PALIsOpen ? AvatarData::PALMinimum : AvatarData::MinimumData;
|
||||||
|
nodeData->incrementAvatarOutOfView();
|
||||||
|
} else if (!overBudget) {
|
||||||
|
detail = distribution(generator) < AVATAR_SEND_FULL_UPDATE_RATIO ? AvatarData::SendAllData : AvatarData::CullSmallData;
|
||||||
|
nodeData->incrementAvatarInView();
|
||||||
|
|
||||||
|
// If the time that the mixer sent AVATAR DATA about Avatar B to Avatar A is BEFORE OR EQUAL TO
|
||||||
|
// the time that Avatar B flagged an IDENTITY DATA change, send IDENTITY DATA about Avatar B to Avatar A.
|
||||||
|
if (otherAvatar->hasProcessedFirstIdentity()
|
||||||
|
&& nodeData->getLastBroadcastTime(otherNode->getLocalID()) <= otherNodeData->getIdentityChangeTimestamp()) {
|
||||||
|
identityBytesSent += sendIdentityPacket(*identityPacketList, otherNodeData, *destinationNode);
|
||||||
|
|
||||||
|
// remember the last time we sent identity details about this other node to the receiver
|
||||||
|
nodeData->setLastBroadcastTime(otherNode->getLocalID(), usecTimestampNow());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QVector<JointData>& lastSentJointsForOther = nodeData->getLastOtherAvatarSentJoints(otherNode->getLocalID());
|
||||||
|
|
||||||
|
const bool distanceAdjust = true;
|
||||||
|
const bool dropFaceTracking = false;
|
||||||
|
AvatarDataPacket::SendStatus sendStatus;
|
||||||
|
sendStatus.sendUUID = true;
|
||||||
|
|
||||||
|
do {
|
||||||
|
auto startSerialize = chrono::high_resolution_clock::now();
|
||||||
|
QByteArray bytes = otherAvatar->toByteArray(detail, lastEncodeForOther, lastSentJointsForOther,
|
||||||
|
sendStatus, dropFaceTracking, distanceAdjust, myPosition,
|
||||||
|
&lastSentJointsForOther, avatarSpaceAvailable);
|
||||||
|
auto endSerialize = chrono::high_resolution_clock::now();
|
||||||
|
_stats.toByteArrayElapsedTime +=
|
||||||
|
(quint64)chrono::duration_cast<chrono::microseconds>(endSerialize - startSerialize).count();
|
||||||
|
|
||||||
|
avatarPacket->write(bytes);
|
||||||
|
avatarSpaceAvailable -= bytes.size();
|
||||||
|
numAvatarDataBytes += bytes.size();
|
||||||
|
if (!sendStatus || avatarSpaceAvailable < (int)AvatarDataPacket::MIN_BULK_PACKET_SIZE) {
|
||||||
|
// Weren't able to fit everything.
|
||||||
|
nodeList->sendPacket(std::move(avatarPacket), *destinationNode);
|
||||||
|
++numPacketsSent;
|
||||||
|
avatarPacket = NLPacket::create(PacketType::BulkAvatarData);
|
||||||
|
avatarSpaceAvailable = avatarPacketCapacity;
|
||||||
|
}
|
||||||
|
} while (!sendStatus);
|
||||||
|
|
||||||
|
if (detail != AvatarData::NoData) {
|
||||||
|
_stats.numOthersIncluded++;
|
||||||
|
if (otherAvatar->getPriorityAvatar()) {
|
||||||
|
_stats.numHeroesIncluded++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// increment the number of avatars sent to this receiver
|
||||||
|
nodeData->incrementNumAvatarsSentLastFrame();
|
||||||
|
|
||||||
|
// set the last sent sequence number for this sender on the receiver
|
||||||
|
nodeData->setLastBroadcastSequenceNumber(otherNode->getLocalID(),
|
||||||
|
otherNodeData->getLastReceivedSequenceNumber());
|
||||||
|
nodeData->setLastOtherAvatarEncodeTime(otherNode->getLocalID(), usecTimestampNow());
|
||||||
|
}
|
||||||
|
|
||||||
|
auto endAvatarDataPacking = chrono::high_resolution_clock::now();
|
||||||
|
_stats.avatarDataPackingElapsedTime +=
|
||||||
|
(quint64)chrono::duration_cast<chrono::microseconds>(endAvatarDataPacking - startAvatarDataPacking).count();
|
||||||
|
|
||||||
|
if (!overBudget) {
|
||||||
|
// use helper to add any changed traits to our packet list
|
||||||
|
traitBytesSent += addChangedTraitsToBulkPacket(nodeData, otherNodeData, *traitsPacketList);
|
||||||
|
}
|
||||||
|
numAvatarsSent++;
|
||||||
|
remainingAvatars--;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentVariant == kHero) { // Dump any remaining heroes into the commoners.
|
||||||
|
for (auto avIter = sortedAvatarVector.begin() + numAvatarsSent; avIter < sortedAvatarVector.end(); ++avIter) {
|
||||||
|
avatarPriorityQueues[kNonhero].push(*avIter);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto startAvatarDataPacking = chrono::high_resolution_clock::now();
|
|
||||||
|
|
||||||
const AvatarMixerClientData* otherNodeData = reinterpret_cast<const AvatarMixerClientData*>(otherNode->getLinkedData());
|
|
||||||
const AvatarData* otherAvatar = otherNodeData->getConstAvatarData();
|
|
||||||
|
|
||||||
// Typically all out-of-view avatars but such avatars' priorities will rise with time:
|
|
||||||
bool isLowerPriority = sortedAvatar.getPriority() <= OUT_OF_VIEW_THRESHOLD;
|
|
||||||
|
|
||||||
if (isLowerPriority) {
|
|
||||||
detail = PALIsOpen ? AvatarData::PALMinimum : AvatarData::MinimumData;
|
|
||||||
nodeData->incrementAvatarOutOfView();
|
|
||||||
} else if (!overBudget) {
|
|
||||||
detail = distribution(generator) < AVATAR_SEND_FULL_UPDATE_RATIO ? AvatarData::SendAllData : AvatarData::CullSmallData;
|
|
||||||
nodeData->incrementAvatarInView();
|
|
||||||
|
|
||||||
// If the time that the mixer sent AVATAR DATA about Avatar B to Avatar A is BEFORE OR EQUAL TO
|
|
||||||
// the time that Avatar B flagged an IDENTITY DATA change, send IDENTITY DATA about Avatar B to Avatar A.
|
|
||||||
if (otherAvatar->hasProcessedFirstIdentity()
|
|
||||||
&& nodeData->getLastBroadcastTime(otherNode->getLocalID()) <= otherNodeData->getIdentityChangeTimestamp()) {
|
|
||||||
identityBytesSent += sendIdentityPacket(*identityPacketList, otherNodeData, *destinationNode);
|
|
||||||
|
|
||||||
// remember the last time we sent identity details about this other node to the receiver
|
|
||||||
nodeData->setLastBroadcastTime(otherNode->getLocalID(), usecTimestampNow());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QVector<JointData>& lastSentJointsForOther = nodeData->getLastOtherAvatarSentJoints(otherNode->getLocalID());
|
|
||||||
|
|
||||||
const bool distanceAdjust = true;
|
|
||||||
const bool dropFaceTracking = false;
|
|
||||||
AvatarDataPacket::SendStatus sendStatus;
|
|
||||||
sendStatus.sendUUID = true;
|
|
||||||
|
|
||||||
do {
|
|
||||||
auto startSerialize = chrono::high_resolution_clock::now();
|
|
||||||
QByteArray bytes = otherAvatar->toByteArray(detail, lastEncodeForOther, lastSentJointsForOther,
|
|
||||||
sendStatus, dropFaceTracking, distanceAdjust, myPosition,
|
|
||||||
&lastSentJointsForOther, avatarSpaceAvailable);
|
|
||||||
auto endSerialize = chrono::high_resolution_clock::now();
|
|
||||||
_stats.toByteArrayElapsedTime +=
|
|
||||||
(quint64)chrono::duration_cast<chrono::microseconds>(endSerialize - startSerialize).count();
|
|
||||||
|
|
||||||
avatarPacket->write(bytes);
|
|
||||||
avatarSpaceAvailable -= bytes.size();
|
|
||||||
numAvatarDataBytes += bytes.size();
|
|
||||||
if (!sendStatus || avatarSpaceAvailable < (int)AvatarDataPacket::MIN_BULK_PACKET_SIZE) {
|
|
||||||
// Weren't able to fit everything.
|
|
||||||
nodeList->sendPacket(std::move(avatarPacket), *destinationNode);
|
|
||||||
++numPacketsSent;
|
|
||||||
avatarPacket = NLPacket::create(PacketType::BulkAvatarData);
|
|
||||||
avatarSpaceAvailable = avatarPacketCapacity;
|
|
||||||
}
|
|
||||||
} while (!sendStatus);
|
|
||||||
|
|
||||||
if (detail != AvatarData::NoData) {
|
|
||||||
_stats.numOthersIncluded++;
|
|
||||||
|
|
||||||
// increment the number of avatars sent to this receiver
|
|
||||||
nodeData->incrementNumAvatarsSentLastFrame();
|
|
||||||
|
|
||||||
// set the last sent sequence number for this sender on the receiver
|
|
||||||
nodeData->setLastBroadcastSequenceNumber(otherNode->getLocalID(),
|
|
||||||
otherNodeData->getLastReceivedSequenceNumber());
|
|
||||||
nodeData->setLastOtherAvatarEncodeTime(otherNode->getLocalID(), usecTimestampNow());
|
|
||||||
}
|
|
||||||
|
|
||||||
auto endAvatarDataPacking = chrono::high_resolution_clock::now();
|
|
||||||
_stats.avatarDataPackingElapsedTime +=
|
|
||||||
(quint64) chrono::duration_cast<chrono::microseconds>(endAvatarDataPacking - startAvatarDataPacking).count();
|
|
||||||
|
|
||||||
if (!overBudget) {
|
|
||||||
// use helper to add any changed traits to our packet list
|
|
||||||
traitBytesSent += addChangedTraitsToBulkPacket(nodeData, otherNodeData, *traitsPacketList);
|
|
||||||
}
|
|
||||||
|
|
||||||
remainingAvatars--;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nodeData->getNumAvatarsSentLastFrame() > numToSendEst) {
|
if (nodeData->getNumAvatarsSentLastFrame() > numToSendEst) {
|
||||||
|
|
|
@ -32,6 +32,7 @@ public:
|
||||||
int numIdentityPacketsSent { 0 };
|
int numIdentityPacketsSent { 0 };
|
||||||
int numOthersIncluded { 0 };
|
int numOthersIncluded { 0 };
|
||||||
int overBudgetAvatars { 0 };
|
int overBudgetAvatars { 0 };
|
||||||
|
int numHeroesIncluded { 0 };
|
||||||
|
|
||||||
quint64 ignoreCalculationElapsedTime { 0 };
|
quint64 ignoreCalculationElapsedTime { 0 };
|
||||||
quint64 avatarDataPackingElapsedTime { 0 };
|
quint64 avatarDataPackingElapsedTime { 0 };
|
||||||
|
@ -57,6 +58,7 @@ public:
|
||||||
numIdentityPacketsSent = 0;
|
numIdentityPacketsSent = 0;
|
||||||
numOthersIncluded = 0;
|
numOthersIncluded = 0;
|
||||||
overBudgetAvatars = 0;
|
overBudgetAvatars = 0;
|
||||||
|
numHeroesIncluded = 0;
|
||||||
|
|
||||||
ignoreCalculationElapsedTime = 0;
|
ignoreCalculationElapsedTime = 0;
|
||||||
avatarDataPackingElapsedTime = 0;
|
avatarDataPackingElapsedTime = 0;
|
||||||
|
@ -80,6 +82,7 @@ public:
|
||||||
numIdentityPacketsSent += rhs.numIdentityPacketsSent;
|
numIdentityPacketsSent += rhs.numIdentityPacketsSent;
|
||||||
numOthersIncluded += rhs.numOthersIncluded;
|
numOthersIncluded += rhs.numOthersIncluded;
|
||||||
overBudgetAvatars += rhs.overBudgetAvatars;
|
overBudgetAvatars += rhs.overBudgetAvatars;
|
||||||
|
numHeroesIncluded += rhs.numHeroesIncluded;
|
||||||
|
|
||||||
ignoreCalculationElapsedTime += rhs.ignoreCalculationElapsedTime;
|
ignoreCalculationElapsedTime += rhs.ignoreCalculationElapsedTime;
|
||||||
avatarDataPackingElapsedTime += rhs.avatarDataPackingElapsedTime;
|
avatarDataPackingElapsedTime += rhs.avatarDataPackingElapsedTime;
|
||||||
|
@ -90,9 +93,13 @@ public:
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class EntityTree;
|
||||||
|
using EntityTreePointer = std::shared_ptr<EntityTree>;
|
||||||
|
|
||||||
struct SlaveSharedData {
|
struct SlaveSharedData {
|
||||||
QStringList skeletonURLWhitelist;
|
QStringList skeletonURLWhitelist;
|
||||||
QUrl skeletonReplacementURL;
|
QUrl skeletonReplacementURL;
|
||||||
|
EntityTreePointer entityTree;
|
||||||
};
|
};
|
||||||
|
|
||||||
class AvatarMixerSlave {
|
class AvatarMixerSlave {
|
||||||
|
|
31
assignment-client/src/avatars/MixerAvatar.h
Normal file
31
assignment-client/src/avatars/MixerAvatar.h
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
//
|
||||||
|
// MixerAvatar.h
|
||||||
|
// assignment-client/src/avatars
|
||||||
|
//
|
||||||
|
// Created by Simon Walton Feb 2019.
|
||||||
|
// Copyright 2019 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
// Avatar class for use within the avatar mixer - encapsulates data required only for
|
||||||
|
// sorting priorities within the mixer.
|
||||||
|
|
||||||
|
#ifndef hifi_MixerAvatar_h
|
||||||
|
#define hifi_MixerAvatar_h
|
||||||
|
|
||||||
|
#include <AvatarData.h>
|
||||||
|
|
||||||
|
class MixerAvatar : public AvatarData {
|
||||||
|
public:
|
||||||
|
bool getPriorityAvatar() const { return _bPriorityAvatar; }
|
||||||
|
void setPriorityAvatar(bool bPriorityAvatar) { _bPriorityAvatar = bPriorityAvatar; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool _bPriorityAvatar { false };
|
||||||
|
};
|
||||||
|
|
||||||
|
using MixerAvatarSharedPointer = std::shared_ptr<MixerAvatar>;
|
||||||
|
|
||||||
|
#endif // hifi_MixerAvatar_h
|
|
@ -1203,7 +1203,8 @@ void OctreeServer::beginRunning() {
|
||||||
auto nodeList = DependencyManager::get<NodeList>();
|
auto nodeList = DependencyManager::get<NodeList>();
|
||||||
|
|
||||||
// we need to ask the DS about agents so we can ping/reply with them
|
// we need to ask the DS about agents so we can ping/reply with them
|
||||||
nodeList->addSetOfNodeTypesToNodeInterestSet({ NodeType::Agent, NodeType::EntityScriptServer });
|
nodeList->addSetOfNodeTypesToNodeInterestSet({ NodeType::Agent, NodeType::EntityScriptServer,
|
||||||
|
NodeType::AvatarMixer });
|
||||||
|
|
||||||
beforeRun(); // after payload has been processed
|
beforeRun(); // after payload has been processed
|
||||||
|
|
||||||
|
|
|
@ -503,6 +503,7 @@ void OtherAvatar::handleChangedAvatarEntityData() {
|
||||||
// then set the the original ID for the changes to take effect
|
// then set the the original ID for the changes to take effect
|
||||||
// TODO: This is a horrible hack and once properties.constructFromBuffer no longer causes
|
// TODO: This is a horrible hack and once properties.constructFromBuffer no longer causes
|
||||||
// side effects...remove the following three lines
|
// side effects...remove the following three lines
|
||||||
|
|
||||||
const QUuid NULL_ID = QUuid("{00000000-0000-0000-0000-000000000005}");
|
const QUuid NULL_ID = QUuid("{00000000-0000-0000-0000-000000000005}");
|
||||||
entity->setParentID(NULL_ID);
|
entity->setParentID(NULL_ID);
|
||||||
entity->setParentID(oldParentID);
|
entity->setParentID(oldParentID);
|
||||||
|
|
|
@ -41,6 +41,7 @@
|
||||||
#include "EntitySimulation.h"
|
#include "EntitySimulation.h"
|
||||||
#include "EntityDynamicFactoryInterface.h"
|
#include "EntityDynamicFactoryInterface.h"
|
||||||
|
|
||||||
|
//#define WANT_DEBUG
|
||||||
|
|
||||||
Q_DECLARE_METATYPE(EntityItemPointer);
|
Q_DECLARE_METATYPE(EntityItemPointer);
|
||||||
int entityItemPointernMetaTypeId = qRegisterMetaType<EntityItemPointer>();
|
int entityItemPointernMetaTypeId = qRegisterMetaType<EntityItemPointer>();
|
||||||
|
@ -95,6 +96,8 @@ EntityPropertyFlags EntityItem::getEntityProperties(EncodeBitstreamParams& param
|
||||||
requestedProperties += PROP_LAST_EDITED_BY;
|
requestedProperties += PROP_LAST_EDITED_BY;
|
||||||
requestedProperties += PROP_ENTITY_HOST_TYPE;
|
requestedProperties += PROP_ENTITY_HOST_TYPE;
|
||||||
requestedProperties += PROP_OWNING_AVATAR_ID;
|
requestedProperties += PROP_OWNING_AVATAR_ID;
|
||||||
|
requestedProperties += PROP_PARENT_ID;
|
||||||
|
requestedProperties += PROP_PARENT_JOINT_INDEX;
|
||||||
requestedProperties += PROP_QUERY_AA_CUBE;
|
requestedProperties += PROP_QUERY_AA_CUBE;
|
||||||
requestedProperties += PROP_CAN_CAST_SHADOW;
|
requestedProperties += PROP_CAN_CAST_SHADOW;
|
||||||
requestedProperties += PROP_VISIBLE_IN_SECONDARY_CAMERA;
|
requestedProperties += PROP_VISIBLE_IN_SECONDARY_CAMERA;
|
||||||
|
@ -502,6 +505,7 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef WANT_DEBUG
|
#ifdef WANT_DEBUG
|
||||||
|
{
|
||||||
quint64 lastEdited = getLastEdited();
|
quint64 lastEdited = getLastEdited();
|
||||||
float editedAgo = getEditedAgo();
|
float editedAgo = getEditedAgo();
|
||||||
QString agoAsString = formatSecondsElapsed(editedAgo);
|
QString agoAsString = formatSecondsElapsed(editedAgo);
|
||||||
|
@ -515,6 +519,7 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef
|
||||||
qCDebug(entities) << " age=" << getAge() << "seconds - " << ageAsString;
|
qCDebug(entities) << " age=" << getAge() << "seconds - " << ageAsString;
|
||||||
qCDebug(entities) << " lastEdited =" << lastEdited;
|
qCDebug(entities) << " lastEdited =" << lastEdited;
|
||||||
qCDebug(entities) << " ago=" << editedAgo << "seconds - " << agoAsString;
|
qCDebug(entities) << " ago=" << editedAgo << "seconds - " << agoAsString;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
quint64 lastEditedFromBuffer = 0;
|
quint64 lastEditedFromBuffer = 0;
|
||||||
|
@ -1099,7 +1104,7 @@ void EntityItem::simulate(const quint64& now) {
|
||||||
qCDebug(entities) << " hasGravity=" << hasGravity();
|
qCDebug(entities) << " hasGravity=" << hasGravity();
|
||||||
qCDebug(entities) << " hasAcceleration=" << hasAcceleration();
|
qCDebug(entities) << " hasAcceleration=" << hasAcceleration();
|
||||||
qCDebug(entities) << " hasAngularVelocity=" << hasAngularVelocity();
|
qCDebug(entities) << " hasAngularVelocity=" << hasAngularVelocity();
|
||||||
qCDebug(entities) << " getAngularVelocity=" << getAngularVelocity();
|
qCDebug(entities) << " getAngularVelocity=" << getLocalAngularVelocity();
|
||||||
qCDebug(entities) << " isMortal=" << isMortal();
|
qCDebug(entities) << " isMortal=" << isMortal();
|
||||||
qCDebug(entities) << " getAge()=" << getAge();
|
qCDebug(entities) << " getAge()=" << getAge();
|
||||||
qCDebug(entities) << " getLifetime()=" << getLifetime();
|
qCDebug(entities) << " getLifetime()=" << getLifetime();
|
||||||
|
@ -1111,12 +1116,12 @@ void EntityItem::simulate(const quint64& now) {
|
||||||
qCDebug(entities) << " hasGravity=" << hasGravity();
|
qCDebug(entities) << " hasGravity=" << hasGravity();
|
||||||
qCDebug(entities) << " hasAcceleration=" << hasAcceleration();
|
qCDebug(entities) << " hasAcceleration=" << hasAcceleration();
|
||||||
qCDebug(entities) << " hasAngularVelocity=" << hasAngularVelocity();
|
qCDebug(entities) << " hasAngularVelocity=" << hasAngularVelocity();
|
||||||
qCDebug(entities) << " getAngularVelocity=" << getAngularVelocity();
|
qCDebug(entities) << " getAngularVelocity=" << getLocalAngularVelocity();
|
||||||
}
|
}
|
||||||
if (hasAngularVelocity()) {
|
if (hasAngularVelocity()) {
|
||||||
qCDebug(entities) << " CHANGING...=";
|
qCDebug(entities) << " CHANGING...=";
|
||||||
qCDebug(entities) << " hasAngularVelocity=" << hasAngularVelocity();
|
qCDebug(entities) << " hasAngularVelocity=" << hasAngularVelocity();
|
||||||
qCDebug(entities) << " getAngularVelocity=" << getAngularVelocity();
|
qCDebug(entities) << " getAngularVelocity=" << getLocalAngularVelocity();
|
||||||
}
|
}
|
||||||
if (isMortal()) {
|
if (isMortal()) {
|
||||||
qCDebug(entities) << " MORTAL...=";
|
qCDebug(entities) << " MORTAL...=";
|
||||||
|
@ -1738,7 +1743,7 @@ bool EntityItem::contains(const glm::vec3& point) const {
|
||||||
// the above cases not yet supported --> fall through to BOX case
|
// the above cases not yet supported --> fall through to BOX case
|
||||||
case SHAPE_TYPE_BOX: {
|
case SHAPE_TYPE_BOX: {
|
||||||
localPoint = glm::abs(localPoint);
|
localPoint = glm::abs(localPoint);
|
||||||
return glm::any(glm::lessThanEqual(localPoint, glm::vec3(NORMALIZED_HALF_SIDE)));
|
return glm::all(glm::lessThanEqual(localPoint, glm::vec3(NORMALIZED_HALF_SIDE)));
|
||||||
}
|
}
|
||||||
case SHAPE_TYPE_ELLIPSOID: {
|
case SHAPE_TYPE_ELLIPSOID: {
|
||||||
// since we've transformed into the normalized space this is just a sphere-point intersection test
|
// since we've transformed into the normalized space this is just a sphere-point intersection test
|
||||||
|
@ -2652,13 +2657,23 @@ bool EntityItem::matchesJSONFilters(const QJsonObject& jsonFilters) const {
|
||||||
// ALL entity properties. Some work will need to be done to the property system so that it can be more flexible
|
// ALL entity properties. Some work will need to be done to the property system so that it can be more flexible
|
||||||
// (to grab the value and default value of a property given the string representation of that property, for example)
|
// (to grab the value and default value of a property given the string representation of that property, for example)
|
||||||
|
|
||||||
// currently the only property filter we handle is '+' for serverScripts
|
// currently the only property filter we handle in EntityItem is '+' for serverScripts
|
||||||
// which means that we only handle a filtered query asking for entities where the serverScripts property is non-default
|
// which means that we only handle a filtered query asking for entities where the serverScripts property is non-default
|
||||||
|
|
||||||
static const QString SERVER_SCRIPTS_PROPERTY = "serverScripts";
|
static const QString SERVER_SCRIPTS_PROPERTY = "serverScripts";
|
||||||
|
static const QString ENTITY_TYPE_PROPERTY = "type";
|
||||||
|
|
||||||
if (jsonFilters[SERVER_SCRIPTS_PROPERTY] == EntityQueryFilterSymbol::NonDefault) {
|
foreach(const auto& property, jsonFilters.keys()) {
|
||||||
return _serverScripts != ENTITY_ITEM_DEFAULT_SERVER_SCRIPTS;
|
if (property == SERVER_SCRIPTS_PROPERTY && jsonFilters[property] == EntityQueryFilterSymbol::NonDefault) {
|
||||||
|
// check if this entity has a non-default value for serverScripts
|
||||||
|
if (_serverScripts != ENTITY_ITEM_DEFAULT_SERVER_SCRIPTS) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else if (property == ENTITY_TYPE_PROPERTY) {
|
||||||
|
return (jsonFilters[property] == EntityTypes::getEntityTypeName(getType()) );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// the json filter syntax did not match what we expected, return a match
|
// the json filter syntax did not match what we expected, return a match
|
||||||
|
|
|
@ -514,7 +514,7 @@ public:
|
||||||
QUuid getLastEditedBy() const { return _lastEditedBy; }
|
QUuid getLastEditedBy() const { return _lastEditedBy; }
|
||||||
void setLastEditedBy(QUuid value) { _lastEditedBy = value; }
|
void setLastEditedBy(QUuid value) { _lastEditedBy = value; }
|
||||||
|
|
||||||
bool matchesJSONFilters(const QJsonObject& jsonFilters) const;
|
virtual bool matchesJSONFilters(const QJsonObject& jsonFilters) const;
|
||||||
|
|
||||||
virtual bool getMeshes(MeshProxyList& result) { return true; }
|
virtual bool getMeshes(MeshProxyList& result) { return true; }
|
||||||
|
|
||||||
|
|
|
@ -617,6 +617,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
|
||||||
CHECK_PROPERTY_CHANGE(PROP_FLYING_ALLOWED, flyingAllowed);
|
CHECK_PROPERTY_CHANGE(PROP_FLYING_ALLOWED, flyingAllowed);
|
||||||
CHECK_PROPERTY_CHANGE(PROP_GHOSTING_ALLOWED, ghostingAllowed);
|
CHECK_PROPERTY_CHANGE(PROP_GHOSTING_ALLOWED, ghostingAllowed);
|
||||||
CHECK_PROPERTY_CHANGE(PROP_FILTER_URL, filterURL);
|
CHECK_PROPERTY_CHANGE(PROP_FILTER_URL, filterURL);
|
||||||
|
CHECK_PROPERTY_CHANGE(PROP_AVATAR_PRIORITY, avatarPriority);
|
||||||
CHECK_PROPERTY_CHANGE(PROP_KEY_LIGHT_MODE, keyLightMode);
|
CHECK_PROPERTY_CHANGE(PROP_KEY_LIGHT_MODE, keyLightMode);
|
||||||
CHECK_PROPERTY_CHANGE(PROP_AMBIENT_LIGHT_MODE, ambientLightMode);
|
CHECK_PROPERTY_CHANGE(PROP_AMBIENT_LIGHT_MODE, ambientLightMode);
|
||||||
CHECK_PROPERTY_CHANGE(PROP_SKYBOX_MODE, skyboxMode);
|
CHECK_PROPERTY_CHANGE(PROP_SKYBOX_MODE, skyboxMode);
|
||||||
|
@ -1426,7 +1427,11 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
|
||||||
* @property {string} filterURL="" - The URL of a JavaScript file that filters changes to properties of entities within the
|
* @property {string} filterURL="" - The URL of a JavaScript file that filters changes to properties of entities within the
|
||||||
* zone. It is periodically executed for each entity in the zone. It can, for example, be used to not allow changes to
|
* zone. It is periodically executed for each entity in the zone. It can, for example, be used to not allow changes to
|
||||||
* certain properties.<br />
|
* certain properties.<br />
|
||||||
|
*
|
||||||
|
* @property {boolean} avatarPriority=false - If <code>true</code> avatars within this zone will have their movements distributed to other
|
||||||
|
* clients with priority over other avatars. Use, for example, on a performance stage with a few presenters.
|
||||||
* <pre>
|
* <pre>
|
||||||
|
*
|
||||||
* function filter(properties) {
|
* function filter(properties) {
|
||||||
* // Test and edit properties object values,
|
* // Test and edit properties object values,
|
||||||
* // e.g., properties.modelURL, as required.
|
* // e.g., properties.modelURL, as required.
|
||||||
|
@ -1755,6 +1760,7 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool
|
||||||
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_FLYING_ALLOWED, flyingAllowed);
|
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_FLYING_ALLOWED, flyingAllowed);
|
||||||
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_GHOSTING_ALLOWED, ghostingAllowed);
|
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_GHOSTING_ALLOWED, ghostingAllowed);
|
||||||
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_FILTER_URL, filterURL);
|
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_FILTER_URL, filterURL);
|
||||||
|
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_AVATAR_PRIORITY, avatarPriority);
|
||||||
|
|
||||||
COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_KEY_LIGHT_MODE, keyLightMode, getKeyLightModeAsString());
|
COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_KEY_LIGHT_MODE, keyLightMode, getKeyLightModeAsString());
|
||||||
COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_AMBIENT_LIGHT_MODE, ambientLightMode, getAmbientLightModeAsString());
|
COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_AMBIENT_LIGHT_MODE, ambientLightMode, getAmbientLightModeAsString());
|
||||||
|
@ -2118,6 +2124,7 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object, bool
|
||||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(flyingAllowed, bool, setFlyingAllowed);
|
COPY_PROPERTY_FROM_QSCRIPTVALUE(flyingAllowed, bool, setFlyingAllowed);
|
||||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(ghostingAllowed, bool, setGhostingAllowed);
|
COPY_PROPERTY_FROM_QSCRIPTVALUE(ghostingAllowed, bool, setGhostingAllowed);
|
||||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(filterURL, QString, setFilterURL);
|
COPY_PROPERTY_FROM_QSCRIPTVALUE(filterURL, QString, setFilterURL);
|
||||||
|
COPY_PROPERTY_FROM_QSCRIPTVALUE(avatarPriority, bool, setAvatarPriority);
|
||||||
COPY_PROPERTY_FROM_QSCRIPTVALUE_ENUM(keyLightMode, KeyLightMode);
|
COPY_PROPERTY_FROM_QSCRIPTVALUE_ENUM(keyLightMode, KeyLightMode);
|
||||||
COPY_PROPERTY_FROM_QSCRIPTVALUE_ENUM(ambientLightMode, AmbientLightMode);
|
COPY_PROPERTY_FROM_QSCRIPTVALUE_ENUM(ambientLightMode, AmbientLightMode);
|
||||||
COPY_PROPERTY_FROM_QSCRIPTVALUE_ENUM(skyboxMode, SkyboxMode);
|
COPY_PROPERTY_FROM_QSCRIPTVALUE_ENUM(skyboxMode, SkyboxMode);
|
||||||
|
@ -2398,6 +2405,7 @@ void EntityItemProperties::merge(const EntityItemProperties& other) {
|
||||||
COPY_PROPERTY_IF_CHANGED(flyingAllowed);
|
COPY_PROPERTY_IF_CHANGED(flyingAllowed);
|
||||||
COPY_PROPERTY_IF_CHANGED(ghostingAllowed);
|
COPY_PROPERTY_IF_CHANGED(ghostingAllowed);
|
||||||
COPY_PROPERTY_IF_CHANGED(filterURL);
|
COPY_PROPERTY_IF_CHANGED(filterURL);
|
||||||
|
COPY_PROPERTY_IF_CHANGED(avatarPriority);
|
||||||
COPY_PROPERTY_IF_CHANGED(keyLightMode);
|
COPY_PROPERTY_IF_CHANGED(keyLightMode);
|
||||||
COPY_PROPERTY_IF_CHANGED(ambientLightMode);
|
COPY_PROPERTY_IF_CHANGED(ambientLightMode);
|
||||||
COPY_PROPERTY_IF_CHANGED(skyboxMode);
|
COPY_PROPERTY_IF_CHANGED(skyboxMode);
|
||||||
|
@ -2784,6 +2792,7 @@ bool EntityItemProperties::getPropertyInfo(const QString& propertyName, EntityPr
|
||||||
ADD_PROPERTY_TO_MAP(PROP_FLYING_ALLOWED, FlyingAllowed, flyingAllowed, bool);
|
ADD_PROPERTY_TO_MAP(PROP_FLYING_ALLOWED, FlyingAllowed, flyingAllowed, bool);
|
||||||
ADD_PROPERTY_TO_MAP(PROP_GHOSTING_ALLOWED, GhostingAllowed, ghostingAllowed, bool);
|
ADD_PROPERTY_TO_MAP(PROP_GHOSTING_ALLOWED, GhostingAllowed, ghostingAllowed, bool);
|
||||||
ADD_PROPERTY_TO_MAP(PROP_FILTER_URL, FilterURL, filterURL, QString);
|
ADD_PROPERTY_TO_MAP(PROP_FILTER_URL, FilterURL, filterURL, QString);
|
||||||
|
ADD_PROPERTY_TO_MAP(PROP_AVATAR_PRIORITY, AvatarPriority, avatarPriority, bool);
|
||||||
ADD_PROPERTY_TO_MAP(PROP_KEY_LIGHT_MODE, KeyLightMode, keyLightMode, uint32_t);
|
ADD_PROPERTY_TO_MAP(PROP_KEY_LIGHT_MODE, KeyLightMode, keyLightMode, uint32_t);
|
||||||
ADD_PROPERTY_TO_MAP(PROP_AMBIENT_LIGHT_MODE, AmbientLightMode, ambientLightMode, uint32_t);
|
ADD_PROPERTY_TO_MAP(PROP_AMBIENT_LIGHT_MODE, AmbientLightMode, ambientLightMode, uint32_t);
|
||||||
ADD_PROPERTY_TO_MAP(PROP_SKYBOX_MODE, SkyboxMode, skyboxMode, uint32_t);
|
ADD_PROPERTY_TO_MAP(PROP_SKYBOX_MODE, SkyboxMode, skyboxMode, uint32_t);
|
||||||
|
@ -3191,6 +3200,7 @@ OctreeElement::AppendState EntityItemProperties::encodeEntityEditPacket(PacketTy
|
||||||
APPEND_ENTITY_PROPERTY(PROP_SKYBOX_MODE, (uint32_t)properties.getSkyboxMode());
|
APPEND_ENTITY_PROPERTY(PROP_SKYBOX_MODE, (uint32_t)properties.getSkyboxMode());
|
||||||
APPEND_ENTITY_PROPERTY(PROP_HAZE_MODE, (uint32_t)properties.getHazeMode());
|
APPEND_ENTITY_PROPERTY(PROP_HAZE_MODE, (uint32_t)properties.getHazeMode());
|
||||||
APPEND_ENTITY_PROPERTY(PROP_BLOOM_MODE, (uint32_t)properties.getBloomMode());
|
APPEND_ENTITY_PROPERTY(PROP_BLOOM_MODE, (uint32_t)properties.getBloomMode());
|
||||||
|
APPEND_ENTITY_PROPERTY(PROP_AVATAR_PRIORITY, properties.getAvatarPriority());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (properties.getType() == EntityTypes::PolyVox) {
|
if (properties.getType() == EntityTypes::PolyVox) {
|
||||||
|
@ -3650,6 +3660,7 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int
|
||||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_FLYING_ALLOWED, bool, setFlyingAllowed);
|
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_FLYING_ALLOWED, bool, setFlyingAllowed);
|
||||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_GHOSTING_ALLOWED, bool, setGhostingAllowed);
|
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_GHOSTING_ALLOWED, bool, setGhostingAllowed);
|
||||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_FILTER_URL, QString, setFilterURL);
|
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_FILTER_URL, QString, setFilterURL);
|
||||||
|
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_AVATAR_PRIORITY, bool, setAvatarPriority);
|
||||||
|
|
||||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_KEY_LIGHT_MODE, uint32_t, setKeyLightMode);
|
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_KEY_LIGHT_MODE, uint32_t, setKeyLightMode);
|
||||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_AMBIENT_LIGHT_MODE, uint32_t, setAmbientLightMode);
|
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_AMBIENT_LIGHT_MODE, uint32_t, setAmbientLightMode);
|
||||||
|
@ -4033,6 +4044,7 @@ void EntityItemProperties::markAllChanged() {
|
||||||
_bloom.markAllChanged();
|
_bloom.markAllChanged();
|
||||||
_flyingAllowedChanged = true;
|
_flyingAllowedChanged = true;
|
||||||
_ghostingAllowedChanged = true;
|
_ghostingAllowedChanged = true;
|
||||||
|
_avatarPriorityChanged = true;
|
||||||
_filterURLChanged = true;
|
_filterURLChanged = true;
|
||||||
_keyLightModeChanged = true;
|
_keyLightModeChanged = true;
|
||||||
_ambientLightModeChanged = true;
|
_ambientLightModeChanged = true;
|
||||||
|
@ -4622,6 +4634,9 @@ QList<QString> EntityItemProperties::listChangedProperties() {
|
||||||
if (filterURLChanged()) {
|
if (filterURLChanged()) {
|
||||||
out += "filterURL";
|
out += "filterURL";
|
||||||
}
|
}
|
||||||
|
if (avatarPriorityChanged()) {
|
||||||
|
out += "avatarPriority";
|
||||||
|
}
|
||||||
if (keyLightModeChanged()) {
|
if (keyLightModeChanged()) {
|
||||||
out += "keyLightMode";
|
out += "keyLightMode";
|
||||||
}
|
}
|
||||||
|
|
|
@ -316,6 +316,7 @@ public:
|
||||||
DEFINE_PROPERTY(PROP_FLYING_ALLOWED, FlyingAllowed, flyingAllowed, bool, ZoneEntityItem::DEFAULT_FLYING_ALLOWED);
|
DEFINE_PROPERTY(PROP_FLYING_ALLOWED, FlyingAllowed, flyingAllowed, bool, ZoneEntityItem::DEFAULT_FLYING_ALLOWED);
|
||||||
DEFINE_PROPERTY(PROP_GHOSTING_ALLOWED, GhostingAllowed, ghostingAllowed, bool, ZoneEntityItem::DEFAULT_GHOSTING_ALLOWED);
|
DEFINE_PROPERTY(PROP_GHOSTING_ALLOWED, GhostingAllowed, ghostingAllowed, bool, ZoneEntityItem::DEFAULT_GHOSTING_ALLOWED);
|
||||||
DEFINE_PROPERTY(PROP_FILTER_URL, FilterURL, filterURL, QString, ZoneEntityItem::DEFAULT_FILTER_URL);
|
DEFINE_PROPERTY(PROP_FILTER_URL, FilterURL, filterURL, QString, ZoneEntityItem::DEFAULT_FILTER_URL);
|
||||||
|
DEFINE_PROPERTY(PROP_AVATAR_PRIORITY, AvatarPriority, avatarPriority, bool, ZoneEntityItem::DEFAULT_AVATAR_PRIORITY);
|
||||||
DEFINE_PROPERTY_REF_ENUM(PROP_KEY_LIGHT_MODE, KeyLightMode, keyLightMode, uint32_t, (uint32_t)COMPONENT_MODE_INHERIT);
|
DEFINE_PROPERTY_REF_ENUM(PROP_KEY_LIGHT_MODE, KeyLightMode, keyLightMode, uint32_t, (uint32_t)COMPONENT_MODE_INHERIT);
|
||||||
DEFINE_PROPERTY_REF_ENUM(PROP_SKYBOX_MODE, SkyboxMode, skyboxMode, uint32_t, (uint32_t)COMPONENT_MODE_INHERIT);
|
DEFINE_PROPERTY_REF_ENUM(PROP_SKYBOX_MODE, SkyboxMode, skyboxMode, uint32_t, (uint32_t)COMPONENT_MODE_INHERIT);
|
||||||
DEFINE_PROPERTY_REF_ENUM(PROP_AMBIENT_LIGHT_MODE, AmbientLightMode, ambientLightMode, uint32_t, (uint32_t)COMPONENT_MODE_INHERIT);
|
DEFINE_PROPERTY_REF_ENUM(PROP_AMBIENT_LIGHT_MODE, AmbientLightMode, ambientLightMode, uint32_t, (uint32_t)COMPONENT_MODE_INHERIT);
|
||||||
|
@ -681,6 +682,8 @@ inline QDebug operator<<(QDebug debug, const EntityItemProperties& properties) {
|
||||||
DEBUG_PROPERTY_IF_CHANGED(debug, properties, GhostingAllowed, ghostingAllowed, "");
|
DEBUG_PROPERTY_IF_CHANGED(debug, properties, GhostingAllowed, ghostingAllowed, "");
|
||||||
DEBUG_PROPERTY_IF_CHANGED(debug, properties, FilterURL, filterURL, "");
|
DEBUG_PROPERTY_IF_CHANGED(debug, properties, FilterURL, filterURL, "");
|
||||||
|
|
||||||
|
DEBUG_PROPERTY_IF_CHANGED(debug, properties, AvatarPriority, avatarPriority, "");
|
||||||
|
|
||||||
DEBUG_PROPERTY_IF_CHANGED(debug, properties, EntityHostTypeAsString, entityHostType, "");
|
DEBUG_PROPERTY_IF_CHANGED(debug, properties, EntityHostTypeAsString, entityHostType, "");
|
||||||
DEBUG_PROPERTY_IF_CHANGED(debug, properties, OwningAvatarID, owningAvatarID, "");
|
DEBUG_PROPERTY_IF_CHANGED(debug, properties, OwningAvatarID, owningAvatarID, "");
|
||||||
|
|
||||||
|
|
|
@ -156,6 +156,7 @@ enum EntityPropertyList {
|
||||||
PROP_DERIVED_28,
|
PROP_DERIVED_28,
|
||||||
PROP_DERIVED_29,
|
PROP_DERIVED_29,
|
||||||
PROP_DERIVED_30,
|
PROP_DERIVED_30,
|
||||||
|
PROP_DERIVED_31,
|
||||||
|
|
||||||
PROP_AFTER_LAST_ITEM,
|
PROP_AFTER_LAST_ITEM,
|
||||||
|
|
||||||
|
@ -276,6 +277,8 @@ enum EntityPropertyList {
|
||||||
PROP_SKYBOX_MODE = PROP_DERIVED_28,
|
PROP_SKYBOX_MODE = PROP_DERIVED_28,
|
||||||
PROP_HAZE_MODE = PROP_DERIVED_29,
|
PROP_HAZE_MODE = PROP_DERIVED_29,
|
||||||
PROP_BLOOM_MODE = PROP_DERIVED_30,
|
PROP_BLOOM_MODE = PROP_DERIVED_30,
|
||||||
|
// Avatar priority
|
||||||
|
PROP_AVATAR_PRIORITY = PROP_DERIVED_31,
|
||||||
|
|
||||||
// Polyvox
|
// Polyvox
|
||||||
PROP_VOXEL_VOLUME_SIZE = PROP_DERIVED_0,
|
PROP_VOXEL_VOLUME_SIZE = PROP_DERIVED_0,
|
||||||
|
|
|
@ -65,6 +65,7 @@ EntityItemProperties ZoneEntityItem::getProperties(const EntityPropertyFlags& de
|
||||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(flyingAllowed, getFlyingAllowed);
|
COPY_ENTITY_PROPERTY_TO_PROPERTIES(flyingAllowed, getFlyingAllowed);
|
||||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(ghostingAllowed, getGhostingAllowed);
|
COPY_ENTITY_PROPERTY_TO_PROPERTIES(ghostingAllowed, getGhostingAllowed);
|
||||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(filterURL, getFilterURL);
|
COPY_ENTITY_PROPERTY_TO_PROPERTIES(filterURL, getFilterURL);
|
||||||
|
COPY_ENTITY_PROPERTY_TO_PROPERTIES(avatarPriority, getAvatarPriority);
|
||||||
|
|
||||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(keyLightMode, getKeyLightMode);
|
COPY_ENTITY_PROPERTY_TO_PROPERTIES(keyLightMode, getKeyLightMode);
|
||||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(ambientLightMode, getAmbientLightMode);
|
COPY_ENTITY_PROPERTY_TO_PROPERTIES(ambientLightMode, getAmbientLightMode);
|
||||||
|
@ -117,6 +118,7 @@ bool ZoneEntityItem::setSubClassProperties(const EntityItemProperties& propertie
|
||||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(skyboxMode, setSkyboxMode);
|
SET_ENTITY_PROPERTY_FROM_PROPERTIES(skyboxMode, setSkyboxMode);
|
||||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(hazeMode, setHazeMode);
|
SET_ENTITY_PROPERTY_FROM_PROPERTIES(hazeMode, setHazeMode);
|
||||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(bloomMode, setBloomMode);
|
SET_ENTITY_PROPERTY_FROM_PROPERTIES(bloomMode, setBloomMode);
|
||||||
|
SET_ENTITY_PROPERTY_FROM_PROPERTIES(avatarPriority, setAvatarPriority);
|
||||||
|
|
||||||
somethingChanged = somethingChanged || _keyLightPropertiesChanged || _ambientLightPropertiesChanged ||
|
somethingChanged = somethingChanged || _keyLightPropertiesChanged || _ambientLightPropertiesChanged ||
|
||||||
_skyboxPropertiesChanged || _hazePropertiesChanged || _bloomPropertiesChanged;
|
_skyboxPropertiesChanged || _hazePropertiesChanged || _bloomPropertiesChanged;
|
||||||
|
@ -192,6 +194,7 @@ int ZoneEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data,
|
||||||
READ_ENTITY_PROPERTY(PROP_SKYBOX_MODE, uint32_t, setSkyboxMode);
|
READ_ENTITY_PROPERTY(PROP_SKYBOX_MODE, uint32_t, setSkyboxMode);
|
||||||
READ_ENTITY_PROPERTY(PROP_HAZE_MODE, uint32_t, setHazeMode);
|
READ_ENTITY_PROPERTY(PROP_HAZE_MODE, uint32_t, setHazeMode);
|
||||||
READ_ENTITY_PROPERTY(PROP_BLOOM_MODE, uint32_t, setBloomMode);
|
READ_ENTITY_PROPERTY(PROP_BLOOM_MODE, uint32_t, setBloomMode);
|
||||||
|
READ_ENTITY_PROPERTY(PROP_AVATAR_PRIORITY, bool, setAvatarPriority);
|
||||||
|
|
||||||
return bytesRead;
|
return bytesRead;
|
||||||
}
|
}
|
||||||
|
@ -211,6 +214,7 @@ EntityPropertyFlags ZoneEntityItem::getEntityProperties(EncodeBitstreamParams& p
|
||||||
requestedProperties += PROP_FLYING_ALLOWED;
|
requestedProperties += PROP_FLYING_ALLOWED;
|
||||||
requestedProperties += PROP_GHOSTING_ALLOWED;
|
requestedProperties += PROP_GHOSTING_ALLOWED;
|
||||||
requestedProperties += PROP_FILTER_URL;
|
requestedProperties += PROP_FILTER_URL;
|
||||||
|
requestedProperties += PROP_AVATAR_PRIORITY;
|
||||||
|
|
||||||
requestedProperties += PROP_KEY_LIGHT_MODE;
|
requestedProperties += PROP_KEY_LIGHT_MODE;
|
||||||
requestedProperties += PROP_AMBIENT_LIGHT_MODE;
|
requestedProperties += PROP_AMBIENT_LIGHT_MODE;
|
||||||
|
@ -256,6 +260,7 @@ void ZoneEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBits
|
||||||
APPEND_ENTITY_PROPERTY(PROP_SKYBOX_MODE, (uint32_t)getSkyboxMode());
|
APPEND_ENTITY_PROPERTY(PROP_SKYBOX_MODE, (uint32_t)getSkyboxMode());
|
||||||
APPEND_ENTITY_PROPERTY(PROP_HAZE_MODE, (uint32_t)getHazeMode());
|
APPEND_ENTITY_PROPERTY(PROP_HAZE_MODE, (uint32_t)getHazeMode());
|
||||||
APPEND_ENTITY_PROPERTY(PROP_BLOOM_MODE, (uint32_t)getBloomMode());
|
APPEND_ENTITY_PROPERTY(PROP_BLOOM_MODE, (uint32_t)getBloomMode());
|
||||||
|
APPEND_ENTITY_PROPERTY(PROP_AVATAR_PRIORITY, getAvatarPriority());
|
||||||
}
|
}
|
||||||
|
|
||||||
void ZoneEntityItem::debugDump() const {
|
void ZoneEntityItem::debugDump() const {
|
||||||
|
@ -269,6 +274,7 @@ void ZoneEntityItem::debugDump() const {
|
||||||
qCDebug(entities) << " _ambientLightMode:" << EntityItemProperties::getComponentModeAsString(_ambientLightMode);
|
qCDebug(entities) << " _ambientLightMode:" << EntityItemProperties::getComponentModeAsString(_ambientLightMode);
|
||||||
qCDebug(entities) << " _skyboxMode:" << EntityItemProperties::getComponentModeAsString(_skyboxMode);
|
qCDebug(entities) << " _skyboxMode:" << EntityItemProperties::getComponentModeAsString(_skyboxMode);
|
||||||
qCDebug(entities) << " _bloomMode:" << EntityItemProperties::getComponentModeAsString(_bloomMode);
|
qCDebug(entities) << " _bloomMode:" << EntityItemProperties::getComponentModeAsString(_bloomMode);
|
||||||
|
qCDebug(entities) << " _avatarPriority:" << getAvatarPriority();
|
||||||
|
|
||||||
_keyLightProperties.debugDump();
|
_keyLightProperties.debugDump();
|
||||||
_ambientLightProperties.debugDump();
|
_ambientLightProperties.debugDump();
|
||||||
|
@ -463,3 +469,16 @@ void ZoneEntityItem::fetchCollisionGeometryResource() {
|
||||||
_shapeResource = DependencyManager::get<ModelCache>()->getCollisionGeometryResource(hullURL);
|
_shapeResource = DependencyManager::get<ModelCache>()->getCollisionGeometryResource(hullURL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ZoneEntityItem::matchesJSONFilters(const QJsonObject& jsonFilters) const {
|
||||||
|
// currently the only property filter we handle in ZoneEntityItem is value of avatarPriority
|
||||||
|
|
||||||
|
static const QString AVATAR_PRIORITY_PROPERTY = "avatarPriority";
|
||||||
|
|
||||||
|
if (jsonFilters.contains(AVATAR_PRIORITY_PROPERTY)) {
|
||||||
|
return (jsonFilters[AVATAR_PRIORITY_PROPERTY].toBool() == _avatarPriority);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Chain to base:
|
||||||
|
return EntityItem::matchesJSONFilters(jsonFilters);
|
||||||
|
}
|
||||||
|
|
|
@ -66,6 +66,8 @@ public:
|
||||||
QString getCompoundShapeURL() const;
|
QString getCompoundShapeURL() const;
|
||||||
virtual void setCompoundShapeURL(const QString& url);
|
virtual void setCompoundShapeURL(const QString& url);
|
||||||
|
|
||||||
|
virtual bool matchesJSONFilters(const QJsonObject& jsonFilters) const override;
|
||||||
|
|
||||||
KeyLightPropertyGroup getKeyLightProperties() const { return resultWithReadLock<KeyLightPropertyGroup>([&] { return _keyLightProperties; }); }
|
KeyLightPropertyGroup getKeyLightProperties() const { return resultWithReadLock<KeyLightPropertyGroup>([&] { return _keyLightProperties; }); }
|
||||||
AmbientLightPropertyGroup getAmbientLightProperties() const { return resultWithReadLock<AmbientLightPropertyGroup>([&] { return _ambientLightProperties; }); }
|
AmbientLightPropertyGroup getAmbientLightProperties() const { return resultWithReadLock<AmbientLightPropertyGroup>([&] { return _ambientLightProperties; }); }
|
||||||
|
|
||||||
|
@ -96,6 +98,9 @@ public:
|
||||||
QString getFilterURL() const;
|
QString getFilterURL() const;
|
||||||
void setFilterURL(const QString url);
|
void setFilterURL(const QString url);
|
||||||
|
|
||||||
|
bool getAvatarPriority() const { return _avatarPriority; }
|
||||||
|
void setAvatarPriority(bool value) { _avatarPriority = value; }
|
||||||
|
|
||||||
bool keyLightPropertiesChanged() const { return _keyLightPropertiesChanged; }
|
bool keyLightPropertiesChanged() const { return _keyLightPropertiesChanged; }
|
||||||
bool ambientLightPropertiesChanged() const { return _ambientLightPropertiesChanged; }
|
bool ambientLightPropertiesChanged() const { return _ambientLightPropertiesChanged; }
|
||||||
bool skyboxPropertiesChanged() const { return _skyboxPropertiesChanged; }
|
bool skyboxPropertiesChanged() const { return _skyboxPropertiesChanged; }
|
||||||
|
@ -123,6 +128,7 @@ public:
|
||||||
static const bool DEFAULT_FLYING_ALLOWED;
|
static const bool DEFAULT_FLYING_ALLOWED;
|
||||||
static const bool DEFAULT_GHOSTING_ALLOWED;
|
static const bool DEFAULT_GHOSTING_ALLOWED;
|
||||||
static const QString DEFAULT_FILTER_URL;
|
static const QString DEFAULT_FILTER_URL;
|
||||||
|
static const bool DEFAULT_AVATAR_PRIORITY = false;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
KeyLightPropertyGroup _keyLightProperties;
|
KeyLightPropertyGroup _keyLightProperties;
|
||||||
|
@ -147,6 +153,9 @@ protected:
|
||||||
bool _ghostingAllowed { DEFAULT_GHOSTING_ALLOWED };
|
bool _ghostingAllowed { DEFAULT_GHOSTING_ALLOWED };
|
||||||
QString _filterURL { DEFAULT_FILTER_URL };
|
QString _filterURL { DEFAULT_FILTER_URL };
|
||||||
|
|
||||||
|
// Avatar-updates priority
|
||||||
|
bool _avatarPriority { DEFAULT_AVATAR_PRIORITY };
|
||||||
|
|
||||||
// Dirty flags turn true when either keylight properties is changing values.
|
// Dirty flags turn true when either keylight properties is changing values.
|
||||||
bool _keyLightPropertiesChanged { false };
|
bool _keyLightPropertiesChanged { false };
|
||||||
bool _ambientLightPropertiesChanged { false };
|
bool _ambientLightPropertiesChanged { false };
|
||||||
|
|
|
@ -123,6 +123,9 @@ void ThreadedAssignment::checkInWithDomainServerOrExit() {
|
||||||
|
|
||||||
// increase the number of queued check ins
|
// increase the number of queued check ins
|
||||||
_numQueuedCheckIns++;
|
_numQueuedCheckIns++;
|
||||||
|
if (_numQueuedCheckIns > 1) {
|
||||||
|
qCDebug(networking) << "Number of queued checkins = " << _numQueuedCheckIns;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -260,6 +260,7 @@ enum class EntityVersion : PacketVersion {
|
||||||
MissingWebEntityProperties,
|
MissingWebEntityProperties,
|
||||||
PulseProperties,
|
PulseProperties,
|
||||||
RingGizmoEntities,
|
RingGizmoEntities,
|
||||||
|
AvatarPriorityZone,
|
||||||
ShowKeyboardFocusHighlight,
|
ShowKeyboardFocusHighlight,
|
||||||
WebBillboardMode,
|
WebBillboardMode,
|
||||||
ModelScale,
|
ModelScale,
|
||||||
|
|
|
@ -134,6 +134,9 @@
|
||||||
"bloom.bloomSize": {
|
"bloom.bloomSize": {
|
||||||
"tooltip": "The radius of bloom. The higher the value, the larger the bloom."
|
"tooltip": "The radius of bloom. The higher the value, the larger the bloom."
|
||||||
},
|
},
|
||||||
|
"avatarPriority": {
|
||||||
|
"tooltip": "Avatars in this zone will have a higher update priority."
|
||||||
|
},
|
||||||
"modelURL": {
|
"modelURL": {
|
||||||
"tooltip": "A mesh model from an FBX or OBJ file."
|
"tooltip": "A mesh model from an FBX or OBJ file."
|
||||||
},
|
},
|
||||||
|
|
|
@ -382,7 +382,8 @@ const DEFAULT_ENTITY_PROPERTIES = {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
shapeType: "box",
|
shapeType: "box",
|
||||||
bloomMode: "inherit"
|
bloomMode: "inherit",
|
||||||
|
avatarPriority: false
|
||||||
},
|
},
|
||||||
Model: {
|
Model: {
|
||||||
collisionShape: "none",
|
collisionShape: "none",
|
||||||
|
|
|
@ -428,6 +428,12 @@ const GROUPS = [
|
||||||
propertyID: "bloom.bloomSize",
|
propertyID: "bloom.bloomSize",
|
||||||
showPropertyRule: { "bloomMode": "enabled" },
|
showPropertyRule: { "bloomMode": "enabled" },
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
label: "Avatar Priority",
|
||||||
|
type: "bool",
|
||||||
|
propertyID: "avatarPriority",
|
||||||
|
},
|
||||||
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in a new issue