Merge commit '743d1a58e2c4d07fa26f61b1512190263225fa42'

This commit is contained in:
Roxanne Skelly 2019-03-05 15:11:25 -08:00
commit fae6393663
11 changed files with 144 additions and 103 deletions

View file

@ -1030,7 +1030,20 @@ void AvatarMixer::handleOctreePacket(QSharedPointer<ReceivedMessage> message, Sh
switch (packetType) { switch (packetType) {
case PacketType::OctreeStats: case PacketType::OctreeStats:
{ // Ignore stats, but may have a different Entity packet appended.
OctreeHeadlessViewer::parseOctreeStats(message, senderNode);
const auto piggyBackedSizeWithHeader = message->getBytesLeftToRead();
if (piggyBackedSizeWithHeader > 0) {
// pull out the piggybacked packet and create a new QSharedPointer<NLPacket> for it
auto buffer = std::unique_ptr<char[]>(new char[piggyBackedSizeWithHeader]);
memcpy(buffer.get(), message->getRawMessage() + message->getPosition(), piggyBackedSizeWithHeader);
auto newPacket = NLPacket::fromReceivedPacket(std::move(buffer), piggyBackedSizeWithHeader, message->getSenderSockAddr());
auto newMessage = QSharedPointer<ReceivedMessage>::create(*newPacket);
handleOctreePacket(newMessage, senderNode);
}
break; break;
}
case PacketType::EntityData: case PacketType::EntityData:
_entityViewer.processDatagram(*message, senderNode); _entityViewer.processDatagram(*message, senderNode);

View file

@ -91,22 +91,26 @@ namespace {
struct FindPriorityZone { struct FindPriorityZone {
glm::vec3 position; glm::vec3 position;
bool isInPriorityZone { false }; bool isInPriorityZone { false };
float zoneVolume { std::numeric_limits<float>::max() };
static bool operation(const OctreeElementPointer& element, void* extraData) { static bool operation(const OctreeElementPointer& element, void* extraData) {
auto findPriorityZone = static_cast<FindPriorityZone*>(extraData); auto findPriorityZone = static_cast<FindPriorityZone*>(extraData);
if (element->getAACube().contains(findPriorityZone->position)) { if (element->getAACube().contains(findPriorityZone->position)) {
const EntityTreeElementPointer entityTreeElement = static_pointer_cast<EntityTreeElement>(element); const EntityTreeElementPointer entityTreeElement = static_pointer_cast<EntityTreeElement>(element);
entityTreeElement->forEachEntity([&findPriorityZone](EntityItemPointer item) { entityTreeElement->forEachEntity([&findPriorityZone](EntityItemPointer item) {
if (item->getType() == EntityTypes::Zone if (item->getType() == EntityTypes::Zone
&& item->contains(findPriorityZone->position) && item->contains(findPriorityZone->position)) {
&& static_pointer_cast<ZoneEntityItem>(item)->getAvatarPriority()) { auto zoneItem = static_pointer_cast<ZoneEntityItem>(item);
findPriorityZone->isInPriorityZone = true; if (zoneItem->getAvatarPriority() != COMPONENT_MODE_INHERIT) {
float volume = zoneItem->getVolumeEstimate();
if (volume < findPriorityZone->zoneVolume) { // Smaller volume wins
findPriorityZone->isInPriorityZone = zoneItem->getAvatarPriority() == COMPONENT_MODE_ENABLED;
findPriorityZone->zoneVolume = volume;
}
}
} }
}); });
if (findPriorityZone->isInPriorityZone) { return true; // Keep recursing
return false; // For now, stop at first hit.
} else {
return true;
}
} else { // Position isn't within this subspace, so end recursion. } else { // Position isn't within this subspace, so end recursion.
return false; return false;
} }
@ -144,7 +148,7 @@ int AvatarMixerClientData::parseData(ReceivedMessage& message, const SlaveShared
EntityTree& entityTree = *slaveSharedData.entityTree; EntityTree& entityTree = *slaveSharedData.entityTree;
FindPriorityZone findPriorityZone { newPosition, false } ; FindPriorityZone findPriorityZone { newPosition, false } ;
entityTree.recurseTreeWithOperation(&FindPriorityZone::operation, &findPriorityZone); entityTree.recurseTreeWithOperation(&FindPriorityZone::operation, &findPriorityZone);
_avatar->setPriorityAvatar(findPriorityZone.isInPriorityZone); _avatar->setHasPriority(findPriorityZone.isInPriorityZone);
//if (findPriorityZone.isInPriorityZone) { //if (findPriorityZone.isInPriorityZone) {
// qCWarning(avatars) << "Avatar" << _avatar->getSessionDisplayName() << "in hero zone"; // qCWarning(avatars) << "Avatar" << _avatar->getSessionDisplayName() << "in hero zone";
//} //}

View file

@ -320,21 +320,21 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node)
_stats.nodesBroadcastedTo++; _stats.nodesBroadcastedTo++;
AvatarMixerClientData* nodeData = reinterpret_cast<AvatarMixerClientData*>(destinationNode->getLinkedData()); AvatarMixerClientData* destinationNodeData = reinterpret_cast<AvatarMixerClientData*>(destinationNode->getLinkedData());
nodeData->resetInViewStats(); destinationNodeData->resetInViewStats();
const AvatarData& avatar = nodeData->getAvatar(); const AvatarData& avatar = destinationNodeData->getAvatar();
glm::vec3 myPosition = avatar.getClientGlobalPosition(); glm::vec3 destinationPosition = avatar.getClientGlobalPosition();
// reset the internal state for correct random number distribution // reset the internal state for correct random number distribution
distribution.reset(); distribution.reset();
// Estimate number to sort on number sent last frame (with min. of 20). // Estimate number to sort on number sent last frame (with min. of 20).
const int numToSendEst = std::max(int(nodeData->getNumAvatarsSentLastFrame() * 2.5f), 20); const int numToSendEst = std::max(int(destinationNodeData->getNumAvatarsSentLastFrame() * 2.5f), 20);
// reset the number of sent avatars // reset the number of sent avatars
nodeData->resetNumAvatarsSentLastFrame(); destinationNodeData->resetNumAvatarsSentLastFrame();
// keep track of outbound data rate specifically for avatar data // keep track of outbound data rate specifically for avatar data
int numAvatarDataBytes = 0; int numAvatarDataBytes = 0;
@ -353,8 +353,8 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node)
// When this is true, the AvatarMixer will send Avatar data to a client // When this is true, the AvatarMixer will send Avatar data to a client
// about avatars they've ignored or that are out of view // about avatars they've ignored or that are out of view
bool PALIsOpen = nodeData->getRequestsDomainListData(); bool PALIsOpen = destinationNodeData->getRequestsDomainListData();
bool PALWasOpen = nodeData->getPrevRequestsDomainListData(); bool PALWasOpen = destinationNodeData->getPrevRequestsDomainListData();
// 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 && destinationNode->getCanKick();
@ -365,10 +365,10 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node)
// compute node bounding box // compute node bounding box
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 destinationNodeBox = computeBubbleBox(avatar, MY_AVATAR_BUBBLE_EXPANSION_FACTOR);
// prepare to sort // prepare to sort
const auto& cameraViews = nodeData->getViewFrustums(); const auto& cameraViews = destinationNodeData->getViewFrustums();
using AvatarPriorityQueue = PrioritySortUtil::PriorityQueue<SortableAvatar>; using AvatarPriorityQueue = PrioritySortUtil::PriorityQueue<SortableAvatar>;
// Keep two independent queues, one for heroes and one for the riff-raff. // Keep two independent queues, one for heroes and one for the riff-raff.
@ -391,47 +391,47 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node)
continue; continue;
} }
auto avatarNode = otherNodeRaw; auto sourceAvatarNode = otherNodeRaw;
bool shouldIgnore = false; bool sendAvatar = true; // We will consider this source avatar for sending.
// 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
// 2) the node hasn't really updated it's frame data recently, this can // 2) the node hasn't really updated it's frame data recently, this can
// 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.
assert(avatarNode); // we can't have gotten here without the avatarData being a valid key in the map assert(sourceAvatarNode); // 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* sourceAvatarNodeData = reinterpret_cast<const AvatarMixerClientData*>(sourceAvatarNode->getLinkedData());
assert(avatarClientNodeData); // we can't have gotten here without avatarNode having valid data assert(sourceAvatarNodeData); // we can't have gotten here without sourceAvatarNode 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 ((destinationNode->isIgnoringNodeWithID(sourceAvatarNode->getUUID()) && !PALIsOpen)
|| (avatarNode->isIgnoringNodeWithID(destinationNode->getUUID()) && !getsAnyIgnored)) { || (sourceAvatarNode->isIgnoringNodeWithID(destinationNode->getUUID()) && !getsAnyIgnored)) {
shouldIgnore = true; sendAvatar = false;
} 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 (nodeData->isIgnoreRadiusEnabled() || (avatarClientNodeData->isIgnoreRadiusEnabled() && !getsAnyIgnored)) { if (destinationNodeData->isIgnoreRadiusEnabled() || (sourceAvatarNodeData->isIgnoreRadiusEnabled() && !getsAnyIgnored)) {
// Perform the collision check between the two bounding boxes // Perform the collision check between the two bounding boxes
AABox otherNodeBox = avatarClientNodeData->getAvatar().getDefaultBubbleBox(); AABox sourceNodeBox = sourceAvatarNodeData->getAvatar().getDefaultBubbleBox();
if (nodeBox.touches(otherNodeBox)) { if (destinationNodeBox.touches(sourceNodeBox)) {
nodeData->ignoreOther(destinationNode, avatarNode); destinationNodeData->ignoreOther(destinationNode, sourceAvatarNode);
shouldIgnore = !getsAnyIgnored; sendAvatar = getsAnyIgnored;
} }
} }
// Not close enough to ignore // Not close enough to ignore
if (!shouldIgnore) { if (sendAvatar) {
nodeData->removeFromRadiusIgnoringSet(avatarNode->getUUID()); destinationNodeData->removeFromRadiusIgnoringSet(sourceAvatarNode->getUUID());
} }
} }
if (!shouldIgnore) { if (sendAvatar) {
AvatarDataSequenceNumber lastSeqToReceiver = nodeData->getLastBroadcastSequenceNumber(avatarNode->getLocalID()); AvatarDataSequenceNumber lastSeqToReceiver = destinationNodeData->getLastBroadcastSequenceNumber(sourceAvatarNode->getLocalID());
AvatarDataSequenceNumber lastSeqFromSender = avatarClientNodeData->getLastReceivedSequenceNumber(); AvatarDataSequenceNumber lastSeqFromSender = sourceAvatarNodeData->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"
@ -445,27 +445,28 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node)
// or that somehow we haven't sent // or that somehow we haven't sent
if (lastSeqToReceiver == lastSeqFromSender && lastSeqToReceiver != 0) { if (lastSeqToReceiver == lastSeqFromSender && lastSeqToReceiver != 0) {
++numAvatarsHeldBack; ++numAvatarsHeldBack;
shouldIgnore = true; sendAvatar = false;
} else if (lastSeqFromSender == 0) { } else if (lastSeqFromSender == 0) {
// We have have not yet recieved any data about this avatar. Ignore it for now // We have have not yet received any data about this avatar. Ignore it for now
// This is important for Agent scripts that are not avatar // This is important for Agent scripts that are not avatar
// so that they don't appear to be an avatar at the origin // so that they don't appear to be an avatar at the origin
shouldIgnore = true; sendAvatar = false;
} else if (lastSeqFromSender - lastSeqToReceiver > 1) { } else if (lastSeqFromSender - lastSeqToReceiver > 1) {
// this is a skip - we still send the packet but capture the presence of the skip so we see it happening // this is a skip - we still send the packet but capture the presence of the skip so we see it happening
++numAvatarsWithSkippedFrames; ++numAvatarsWithSkippedFrames;
} }
} }
quint64 endIgnoreCalculation = usecTimestampNow(); quint64 endIgnoreCalculation = usecTimestampNow();
_stats.ignoreCalculationElapsedTime += (endIgnoreCalculation - startIgnoreCalculation); _stats.ignoreCalculationElapsedTime += (endIgnoreCalculation - startIgnoreCalculation);
if (!shouldIgnore) { if (sendAvatar) {
// sort this one for later // sort this one for later
const MixerAvatar* avatarNodeData = avatarClientNodeData->getConstAvatarData(); const MixerAvatar* avatarNodeData = sourceAvatarNodeData->getConstAvatarData();
auto lastEncodeTime = nodeData->getLastOtherAvatarEncodeTime(avatarNode->getLocalID()); auto lastEncodeTime = destinationNodeData->getLastOtherAvatarEncodeTime(sourceAvatarNode->getLocalID());
avatarPriorityQueues[avatarNodeData->getPriorityAvatar() ? kHero : kNonhero].push( avatarPriorityQueues[avatarNodeData->getHasPriority() ? kHero : kNonhero].push(
SortableAvatar(avatarNodeData, avatarNode, lastEncodeTime)); SortableAvatar(avatarNodeData, sourceAvatarNode, 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,18 +476,18 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node)
// will be sent when it doesn't need to be (but where it _should_ be OK to send). // will be sent when it doesn't need to be (but where it _should_ be OK to send).
// However, it's less heavy-handed than using `shouldIgnore`. // However, it's less heavy-handed than using `shouldIgnore`.
if (PALWasOpen && !PALIsOpen && if (PALWasOpen && !PALIsOpen &&
(destinationNode->isIgnoringNodeWithID(avatarNode->getUUID()) || (destinationNode->isIgnoringNodeWithID(sourceAvatarNode->getUUID()) ||
avatarNode->isIgnoringNodeWithID(destinationNode->getUUID()))) { sourceAvatarNode->isIgnoringNodeWithID(destinationNode->getUUID()))) {
// ...send a Kill Packet to Node A, instructing Node A to kill Avatar B, // ...send a Kill Packet to Node A, instructing Node A to kill Avatar B,
// then have Node A cleanup the killed Node B. // then have Node A cleanup the killed Node B.
auto packet = NLPacket::create(PacketType::KillAvatar, NUM_BYTES_RFC4122_UUID + sizeof(KillAvatarReason), true); auto packet = NLPacket::create(PacketType::KillAvatar, NUM_BYTES_RFC4122_UUID + sizeof(KillAvatarReason), true);
packet->write(avatarNode->getUUID().toRfc4122()); packet->write(sourceAvatarNode->getUUID().toRfc4122());
packet->writePrimitive(KillAvatarReason::AvatarIgnored); packet->writePrimitive(KillAvatarReason::AvatarIgnored);
nodeList->sendPacket(std::move(packet), *destinationNode); nodeList->sendPacket(std::move(packet), *destinationNode);
nodeData->cleanupKilledNode(avatarNode->getUUID(), avatarNode->getLocalID()); destinationNodeData->cleanupKilledNode(sourceAvatarNode->getUUID(), sourceAvatarNode->getLocalID());
} }
nodeData->setPrevRequestsDomainListData(PALIsOpen); destinationNodeData->setPrevRequestsDomainListData(PALIsOpen);
} }
// loop through our sorted avatars and allocate our bandwidth to them accordingly // loop through our sorted avatars and allocate our bandwidth to them accordingly
@ -501,13 +502,14 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node)
int numAvatarsSent = 0; int numAvatarsSent = 0;
auto identityPacketList = NLPacketList::create(PacketType::AvatarIdentity, QByteArray(), true, true); auto identityPacketList = NLPacketList::create(PacketType::AvatarIdentity, QByteArray(), true, true);
// Loop over two priorities - hero avatars then everyone else:
for (PriorityVariants currentVariant = kHero; currentVariant <= kNonhero; ++((int&)currentVariant)) { for (PriorityVariants currentVariant = kHero; currentVariant <= kNonhero; ++((int&)currentVariant)) {
const auto& sortedAvatarVector = avatarPriorityQueues[currentVariant].getSortedVector(numToSendEst); const auto& sortedAvatarVector = avatarPriorityQueues[currentVariant].getSortedVector(numToSendEst);
for (const auto& sortedAvatar : sortedAvatarVector) { for (const auto& sortedAvatar : sortedAvatarVector) {
const Node* otherNode = sortedAvatar.getNode(); const Node* sourceNode = sortedAvatar.getNode();
auto lastEncodeForOther = sortedAvatar.getTimestamp(); auto lastEncodeForOther = sortedAvatar.getTimestamp();
assert(otherNode); // we can't have gotten here without the avatarData being a valid key in the map assert(sourceNode); // 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;
@ -533,31 +535,31 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node)
auto startAvatarDataPacking = chrono::high_resolution_clock::now(); auto startAvatarDataPacking = chrono::high_resolution_clock::now();
const AvatarMixerClientData* otherNodeData = reinterpret_cast<const AvatarMixerClientData*>(otherNode->getLinkedData()); const AvatarMixerClientData* sourceNodeData = reinterpret_cast<const AvatarMixerClientData*>(sourceNode->getLinkedData());
const MixerAvatar* otherAvatar = otherNodeData->getConstAvatarData(); const MixerAvatar* sourceAvatar = sourceNodeData->getConstAvatarData();
// Typically all out-of-view avatars but such avatars' priorities will rise with time: // Typically all out-of-view avatars but such avatars' priorities will rise with time:
bool isLowerPriority = sortedAvatar.getPriority() <= OUT_OF_VIEW_THRESHOLD; bool isLowerPriority = currentVariant != kHero && sortedAvatar.getPriority() <= OUT_OF_VIEW_THRESHOLD; // XXX: hero handling?
if (isLowerPriority) { if (isLowerPriority) {
detail = PALIsOpen ? AvatarData::PALMinimum : AvatarData::MinimumData; detail = PALIsOpen ? AvatarData::PALMinimum : AvatarData::MinimumData;
nodeData->incrementAvatarOutOfView(); destinationNodeData->incrementAvatarOutOfView();
} else if (!overBudget) { } else if (!overBudget) {
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(); destinationNodeData->incrementAvatarInView();
// If the time that the mixer sent AVATAR DATA about Avatar B to Avatar A is BEFORE OR EQUAL TO // 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. // the time that Avatar B flagged an IDENTITY DATA change, send IDENTITY DATA about Avatar B to Avatar A.
if (otherAvatar->hasProcessedFirstIdentity() if (sourceAvatar->hasProcessedFirstIdentity()
&& nodeData->getLastBroadcastTime(otherNode->getLocalID()) <= otherNodeData->getIdentityChangeTimestamp()) { && destinationNodeData->getLastBroadcastTime(sourceNode->getLocalID()) <= sourceNodeData->getIdentityChangeTimestamp()) {
identityBytesSent += sendIdentityPacket(*identityPacketList, otherNodeData, *destinationNode); identityBytesSent += sendIdentityPacket(*identityPacketList, sourceNodeData, *destinationNode);
// remember the last time we sent identity details about this other node to the receiver // remember the last time we sent identity details about this other node to the receiver
nodeData->setLastBroadcastTime(otherNode->getLocalID(), usecTimestampNow()); destinationNodeData->setLastBroadcastTime(sourceNode->getLocalID(), usecTimestampNow());
} }
} }
QVector<JointData>& lastSentJointsForOther = nodeData->getLastOtherAvatarSentJoints(otherNode->getLocalID()); QVector<JointData>& lastSentJointsForOther = destinationNodeData->getLastOtherAvatarSentJoints(sourceNode->getLocalID());
const bool distanceAdjust = true; const bool distanceAdjust = true;
const bool dropFaceTracking = false; const bool dropFaceTracking = false;
@ -566,8 +568,8 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node)
do { do {
auto startSerialize = chrono::high_resolution_clock::now(); auto startSerialize = chrono::high_resolution_clock::now();
QByteArray bytes = otherAvatar->toByteArray(detail, lastEncodeForOther, lastSentJointsForOther, QByteArray bytes = sourceAvatar->toByteArray(detail, lastEncodeForOther, lastSentJointsForOther,
sendStatus, dropFaceTracking, distanceAdjust, myPosition, sendStatus, dropFaceTracking, distanceAdjust, destinationPosition,
&lastSentJointsForOther, avatarSpaceAvailable); &lastSentJointsForOther, avatarSpaceAvailable);
auto endSerialize = chrono::high_resolution_clock::now(); auto endSerialize = chrono::high_resolution_clock::now();
_stats.toByteArrayElapsedTime += _stats.toByteArrayElapsedTime +=
@ -587,17 +589,17 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node)
if (detail != AvatarData::NoData) { if (detail != AvatarData::NoData) {
_stats.numOthersIncluded++; _stats.numOthersIncluded++;
if (otherAvatar->getPriorityAvatar()) { if (sourceAvatar->getHasPriority()) {
_stats.numHeroesIncluded++; _stats.numHeroesIncluded++;
} }
// increment the number of avatars sent to this receiver // increment the number of avatars sent to this receiver
nodeData->incrementNumAvatarsSentLastFrame(); destinationNodeData->incrementNumAvatarsSentLastFrame();
// set the last sent sequence number for this sender on the receiver // set the last sent sequence number for this sender on the receiver
nodeData->setLastBroadcastSequenceNumber(otherNode->getLocalID(), destinationNodeData->setLastBroadcastSequenceNumber(sourceNode->getLocalID(),
otherNodeData->getLastReceivedSequenceNumber()); sourceNodeData->getLastReceivedSequenceNumber());
nodeData->setLastOtherAvatarEncodeTime(otherNode->getLocalID(), usecTimestampNow()); destinationNodeData->setLastOtherAvatarEncodeTime(sourceNode->getLocalID(), usecTimestampNow());
} }
auto endAvatarDataPacking = chrono::high_resolution_clock::now(); auto endAvatarDataPacking = chrono::high_resolution_clock::now();
@ -606,7 +608,7 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node)
if (!overBudget) { if (!overBudget) {
// 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(destinationNodeData, sourceNodeData, *traitsPacketList);
} }
numAvatarsSent++; numAvatarsSent++;
remainingAvatars--; remainingAvatars--;
@ -619,8 +621,8 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node)
} }
} }
if (nodeData->getNumAvatarsSentLastFrame() > numToSendEst) { if (destinationNodeData->getNumAvatarsSentLastFrame() > numToSendEst) {
qCWarning(avatars) << "More avatars sent than upper estimate" << nodeData->getNumAvatarsSentLastFrame() qCWarning(avatars) << "More avatars sent than upper estimate" << destinationNodeData->getNumAvatarsSentLastFrame()
<< " / " << numToSendEst; << " / " << numToSendEst;
} }
@ -651,12 +653,12 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& 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, traitBytesSent); destinationNodeData->recordSentAvatarData(numAvatarDataBytes, traitBytesSent);
// record the number of avatars held back this frame // record the number of avatars held back this frame
nodeData->recordNumOtherAvatarStarves(numAvatarsHeldBack); destinationNodeData->recordNumOtherAvatarStarves(numAvatarsHeldBack);
nodeData->recordNumOtherAvatarSkips(numAvatarsWithSkippedFrames); destinationNodeData->recordNumOtherAvatarSkips(numAvatarsWithSkippedFrames);
quint64 endPacketSending = usecTimestampNow(); quint64 endPacketSending = usecTimestampNow();
_stats.packetSendingElapsedTime += (endPacketSending - startPacketSending); _stats.packetSendingElapsedTime += (endPacketSending - startPacketSending);

View file

@ -19,11 +19,11 @@
class MixerAvatar : public AvatarData { class MixerAvatar : public AvatarData {
public: public:
bool getPriorityAvatar() const { return _bPriorityAvatar; } bool getHasPriority() const { return _hasPriority; }
void setPriorityAvatar(bool bPriorityAvatar) { _bPriorityAvatar = bPriorityAvatar; } void setHasPriority(bool hasPriority) { _hasPriority = hasPriority; }
private: private:
bool _bPriorityAvatar { false }; bool _hasPriority { false };
}; };
using MixerAvatarSharedPointer = std::shared_ptr<MixerAvatar>; using MixerAvatarSharedPointer = std::shared_ptr<MixerAvatar>;

View file

@ -225,6 +225,15 @@ QString EntityItemProperties::getBloomModeAsString() const {
return getComponentModeAsString(_bloomMode); return getComponentModeAsString(_bloomMode);
} }
namespace {
const QStringList AVATAR_PRIORITIES_AS_STRING
{ "inherit", "crowd", "hero" };
}
QString EntityItemProperties::getAvatarPriorityAsString() const {
return AVATAR_PRIORITIES_AS_STRING.value(_avatarPriority);
}
std::array<ComponentPair, COMPONENT_MODE_ITEM_COUNT>::const_iterator EntityItemProperties::findComponent(const QString& mode) { std::array<ComponentPair, COMPONENT_MODE_ITEM_COUNT>::const_iterator EntityItemProperties::findComponent(const QString& mode) {
return std::find_if(COMPONENT_MODES.begin(), COMPONENT_MODES.end(), [&](const ComponentPair& pair) { return std::find_if(COMPONENT_MODES.begin(), COMPONENT_MODES.end(), [&](const ComponentPair& pair) {
return (pair.second == mode); return (pair.second == mode);
@ -249,6 +258,15 @@ void EntityItemProperties::setBloomModeFromString(const QString& bloomMode) {
} }
} }
void EntityItemProperties::setAvatarPriorityFromString(QString const& avatarPriority) {
auto result = AVATAR_PRIORITIES_AS_STRING.indexOf(avatarPriority);
if (result != -1) {
_avatarPriority = result;
_avatarPriorityChanged = true;
}
}
QString EntityItemProperties::getKeyLightModeAsString() const { QString EntityItemProperties::getKeyLightModeAsString() const {
return getComponentModeAsString(_keyLightMode); return getComponentModeAsString(_keyLightMode);
} }
@ -617,12 +635,12 @@ 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);
CHECK_PROPERTY_CHANGE(PROP_HAZE_MODE, hazeMode); CHECK_PROPERTY_CHANGE(PROP_HAZE_MODE, hazeMode);
CHECK_PROPERTY_CHANGE(PROP_BLOOM_MODE, bloomMode); CHECK_PROPERTY_CHANGE(PROP_BLOOM_MODE, bloomMode);
CHECK_PROPERTY_CHANGE(PROP_AVATAR_PRIORITY, avatarPriority);
// Polyvox // Polyvox
CHECK_PROPERTY_CHANGE(PROP_VOXEL_VOLUME_SIZE, voxelVolumeSize); CHECK_PROPERTY_CHANGE(PROP_VOXEL_VOLUME_SIZE, voxelVolumeSize);
@ -1428,8 +1446,10 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
* 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 * @property {string} avatarPriority="inherit" - Configures the update priority of contained avatars to other clients.<br />
* clients with priority over other avatars. Use, for example, on a performance stage with a few presenters. * <code>"inherit"</code>: Priority from enclosing zones is unchanged.<br />
* <code>"crowd"</code>: Priority in this zone is the normal priority.<br />
* <code>"hero"</code>: Avatars in this zone will have an increased update priority
* <pre> * <pre>
* *
* function filter(properties) { * function filter(properties) {
@ -1760,13 +1780,13 @@ 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());
COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_SKYBOX_MODE, skyboxMode, getSkyboxModeAsString()); COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_SKYBOX_MODE, skyboxMode, getSkyboxModeAsString());
COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_HAZE_MODE, hazeMode, getHazeModeAsString()); COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_HAZE_MODE, hazeMode, getHazeModeAsString());
COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_BLOOM_MODE, bloomMode, getBloomModeAsString()); COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_BLOOM_MODE, bloomMode, getBloomModeAsString());
COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_AVATAR_PRIORITY, avatarPriority, getAvatarPriorityAsString());
} }
// Web only // Web only
@ -2124,12 +2144,12 @@ 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);
COPY_PROPERTY_FROM_QSCRIPTVALUE_ENUM(hazeMode, HazeMode); COPY_PROPERTY_FROM_QSCRIPTVALUE_ENUM(hazeMode, HazeMode);
COPY_PROPERTY_FROM_QSCRIPTVALUE_ENUM(bloomMode, BloomMode); COPY_PROPERTY_FROM_QSCRIPTVALUE_ENUM(bloomMode, BloomMode);
COPY_PROPERTY_FROM_QSCRIPTVALUE_ENUM(avatarPriority, AvatarPriority);
// Polyvox // Polyvox
COPY_PROPERTY_FROM_QSCRIPTVALUE(voxelVolumeSize, vec3, setVoxelVolumeSize); COPY_PROPERTY_FROM_QSCRIPTVALUE(voxelVolumeSize, vec3, setVoxelVolumeSize);
@ -2405,12 +2425,12 @@ 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);
COPY_PROPERTY_IF_CHANGED(hazeMode); COPY_PROPERTY_IF_CHANGED(hazeMode);
COPY_PROPERTY_IF_CHANGED(bloomMode); COPY_PROPERTY_IF_CHANGED(bloomMode);
COPY_PROPERTY_IF_CHANGED(avatarPriority);
// Polyvox // Polyvox
COPY_PROPERTY_IF_CHANGED(voxelVolumeSize); COPY_PROPERTY_IF_CHANGED(voxelVolumeSize);
@ -2792,12 +2812,12 @@ 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);
ADD_PROPERTY_TO_MAP(PROP_HAZE_MODE, HazeMode, hazeMode, uint32_t); ADD_PROPERTY_TO_MAP(PROP_HAZE_MODE, HazeMode, hazeMode, uint32_t);
ADD_PROPERTY_TO_MAP(PROP_BLOOM_MODE, BloomMode, bloomMode, uint32_t); ADD_PROPERTY_TO_MAP(PROP_BLOOM_MODE, BloomMode, bloomMode, uint32_t);
ADD_PROPERTY_TO_MAP(PROP_AVATAR_PRIORITY, AvatarPriority, avatarPriority, uint32_t);
// Polyvox // Polyvox
ADD_PROPERTY_TO_MAP(PROP_VOXEL_VOLUME_SIZE, VoxelVolumeSize, voxelVolumeSize, vec3); ADD_PROPERTY_TO_MAP(PROP_VOXEL_VOLUME_SIZE, VoxelVolumeSize, voxelVolumeSize, vec3);
@ -3200,7 +3220,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()); APPEND_ENTITY_PROPERTY(PROP_AVATAR_PRIORITY, (uint32_t)properties.getAvatarPriority());
} }
if (properties.getType() == EntityTypes::PolyVox) { if (properties.getType() == EntityTypes::PolyVox) {
@ -3660,13 +3680,13 @@ 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);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SKYBOX_MODE, uint32_t, setSkyboxMode); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SKYBOX_MODE, uint32_t, setSkyboxMode);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_HAZE_MODE, uint32_t, setHazeMode); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_HAZE_MODE, uint32_t, setHazeMode);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_BLOOM_MODE, uint32_t, setBloomMode); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_BLOOM_MODE, uint32_t, setBloomMode);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_AVATAR_PRIORITY, uint32_t, setAvatarPriority);
} }
if (properties.getType() == EntityTypes::PolyVox) { if (properties.getType() == EntityTypes::PolyVox) {
@ -4044,13 +4064,13 @@ 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;
_skyboxModeChanged = true; _skyboxModeChanged = true;
_hazeModeChanged = true; _hazeModeChanged = true;
_bloomModeChanged = true; _bloomModeChanged = true;
_avatarPriorityChanged = true;
// Polyvox // Polyvox
_voxelVolumeSizeChanged = true; _voxelVolumeSizeChanged = true;
@ -4634,9 +4654,6 @@ QList<QString> EntityItemProperties::listChangedProperties() {
if (filterURLChanged()) { if (filterURLChanged()) {
out += "filterURL"; out += "filterURL";
} }
if (avatarPriorityChanged()) {
out += "avatarPriority";
}
if (keyLightModeChanged()) { if (keyLightModeChanged()) {
out += "keyLightMode"; out += "keyLightMode";
} }
@ -4652,6 +4669,9 @@ QList<QString> EntityItemProperties::listChangedProperties() {
if (bloomModeChanged()) { if (bloomModeChanged()) {
out += "bloomMode"; out += "bloomMode";
} }
if (avatarPriorityChanged()) {
out += "avatarPriority";
}
// Polyvox // Polyvox
if (voxelVolumeSizeChanged()) { if (voxelVolumeSizeChanged()) {

View file

@ -316,12 +316,12 @@ 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);
DEFINE_PROPERTY_REF_ENUM(PROP_HAZE_MODE, HazeMode, hazeMode, uint32_t, (uint32_t)COMPONENT_MODE_INHERIT); DEFINE_PROPERTY_REF_ENUM(PROP_HAZE_MODE, HazeMode, hazeMode, uint32_t, (uint32_t)COMPONENT_MODE_INHERIT);
DEFINE_PROPERTY_REF_ENUM(PROP_BLOOM_MODE, BloomMode, bloomMode, uint32_t, (uint32_t)COMPONENT_MODE_INHERIT); DEFINE_PROPERTY_REF_ENUM(PROP_BLOOM_MODE, BloomMode, bloomMode, uint32_t, (uint32_t)COMPONENT_MODE_INHERIT);
DEFINE_PROPERTY_REF_ENUM(PROP_AVATAR_PRIORITY, AvatarPriority, avatarPriority, uint32_t, (uint32_t)COMPONENT_MODE_INHERIT);
// Polyvox // Polyvox
DEFINE_PROPERTY_REF(PROP_VOXEL_VOLUME_SIZE, VoxelVolumeSize, voxelVolumeSize, glm::vec3, PolyVoxEntityItem::DEFAULT_VOXEL_VOLUME_SIZE); DEFINE_PROPERTY_REF(PROP_VOXEL_VOLUME_SIZE, VoxelVolumeSize, voxelVolumeSize, glm::vec3, PolyVoxEntityItem::DEFAULT_VOXEL_VOLUME_SIZE);

View file

@ -65,13 +65,13 @@ 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);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(skyboxMode, getSkyboxMode); COPY_ENTITY_PROPERTY_TO_PROPERTIES(skyboxMode, getSkyboxMode);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(hazeMode, getHazeMode); COPY_ENTITY_PROPERTY_TO_PROPERTIES(hazeMode, getHazeMode);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(bloomMode, getBloomMode); COPY_ENTITY_PROPERTY_TO_PROPERTIES(bloomMode, getBloomMode);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(avatarPriority, getAvatarPriority);
return properties; return properties;
} }
@ -194,7 +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); READ_ENTITY_PROPERTY(PROP_AVATAR_PRIORITY, uint32_t, setAvatarPriority);
return bytesRead; return bytesRead;
} }
@ -475,8 +475,10 @@ bool ZoneEntityItem::matchesJSONFilters(const QJsonObject& jsonFilters) const {
static const QString AVATAR_PRIORITY_PROPERTY = "avatarPriority"; static const QString AVATAR_PRIORITY_PROPERTY = "avatarPriority";
if (jsonFilters.contains(AVATAR_PRIORITY_PROPERTY)) { // If set ignore only priority-inherit zones:
return (jsonFilters[AVATAR_PRIORITY_PROPERTY].toBool() == _avatarPriority); if (jsonFilters.contains(AVATAR_PRIORITY_PROPERTY) && jsonFilters[AVATAR_PRIORITY_PROPERTY].toBool()
&& _avatarPriority != COMPONENT_MODE_INHERIT) {
return true;
} }
// Chain to base: // Chain to base:

View file

@ -98,8 +98,8 @@ public:
QString getFilterURL() const; QString getFilterURL() const;
void setFilterURL(const QString url); void setFilterURL(const QString url);
bool getAvatarPriority() const { return _avatarPriority; } uint32_t getAvatarPriority() const { return _avatarPriority; }
void setAvatarPriority(bool value) { _avatarPriority = value; } void setAvatarPriority(uint32_t value) { _avatarPriority = value; }
bool keyLightPropertiesChanged() const { return _keyLightPropertiesChanged; } bool keyLightPropertiesChanged() const { return _keyLightPropertiesChanged; }
bool ambientLightPropertiesChanged() const { return _ambientLightPropertiesChanged; } bool ambientLightPropertiesChanged() const { return _ambientLightPropertiesChanged; }
@ -128,7 +128,6 @@ 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;
@ -154,7 +153,7 @@ protected:
QString _filterURL { DEFAULT_FILTER_URL }; QString _filterURL { DEFAULT_FILTER_URL };
// Avatar-updates priority // Avatar-updates priority
bool _avatarPriority { DEFAULT_AVATAR_PRIORITY }; uint32_t _avatarPriority { COMPONENT_MODE_INHERIT };
// 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 };

View file

@ -135,7 +135,7 @@
"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": { "avatarPriority": {
"tooltip": "Avatars in this zone will have a higher update priority." "tooltip": "Alter Avatars' update priorities."
}, },
"modelURL": { "modelURL": {
"tooltip": "A mesh model from an FBX or OBJ file." "tooltip": "A mesh model from an FBX or OBJ file."

View file

@ -383,7 +383,7 @@ const DEFAULT_ENTITY_PROPERTIES = {
}, },
shapeType: "box", shapeType: "box",
bloomMode: "inherit", bloomMode: "inherit",
avatarPriority: false avatarPriority: "inherit"
}, },
Model: { Model: {
collisionShape: "none", collisionShape: "none",

View file

@ -430,7 +430,8 @@ const GROUPS = [
}, },
{ {
label: "Avatar Priority", label: "Avatar Priority",
type: "bool", type: "dropdown",
options: { inherit: "Inherit", crowd: "Crowd", hero: "Hero" },
propertyID: "avatarPriority", propertyID: "avatarPriority",
}, },