3
0
Fork 0
mirror of https://github.com/JulianGro/overte.git synced 2025-04-29 14:43:02 +02:00

Merge branch 'master' of https://github.com/highfidelity/hifi into smoothTeleport

This commit is contained in:
luiscuenca 2018-09-22 05:47:05 -07:00
commit 82b14b6e9b
267 changed files with 7654 additions and 4862 deletions
assignment-client/src
cmake/macros
interface
CMakeLists.txt
resources
src
libraries

View file

@ -395,8 +395,18 @@ void Agent::executeScript() {
if (recordingInterface->getPlayFromCurrentLocation()) {
scriptedAvatar->setRecordingBasis();
}
// these procedural movements are included in the recordings
scriptedAvatar->setHasProceduralEyeFaceMovement(false);
scriptedAvatar->setHasProceduralBlinkFaceMovement(false);
scriptedAvatar->setHasAudioEnabledFaceMovement(false);
} else {
scriptedAvatar->clearRecordingBasis();
// restore procedural blendshape movement
scriptedAvatar->setHasProceduralEyeFaceMovement(true);
scriptedAvatar->setHasProceduralBlinkFaceMovement(true);
scriptedAvatar->setHasAudioEnabledFaceMovement(true);
}
});

View file

@ -11,6 +11,7 @@
#include "AvatarMixerClientData.h"
#include <algorithm>
#include <udt/PacketHeaders.h>
#include <DependencyManager.h>
@ -218,6 +219,10 @@ uint16_t AvatarMixerClientData::getLastBroadcastSequenceNumber(const QUuid& node
}
void AvatarMixerClientData::ignoreOther(SharedNodePointer self, SharedNodePointer other) {
ignoreOther(self.data(), other.data());
}
void AvatarMixerClientData::ignoreOther(const Node* self, const Node* other) {
if (!isRadiusIgnoring(other->getUUID())) {
addToRadiusIgnoringSet(other->getUUID());
auto killPacket = NLPacket::create(PacketType::KillAvatar, NUM_BYTES_RFC4122_UUID + sizeof(KillAvatarReason), true);
@ -235,9 +240,20 @@ void AvatarMixerClientData::ignoreOther(SharedNodePointer self, SharedNodePointe
}
}
void AvatarMixerClientData::removeFromRadiusIgnoringSet(SharedNodePointer self, const QUuid& other) {
if (isRadiusIgnoring(other)) {
_radiusIgnoredOthers.erase(other);
bool AvatarMixerClientData::isRadiusIgnoring(const QUuid& other) const {
return std::find(_radiusIgnoredOthers.cbegin(), _radiusIgnoredOthers.cend(), other) != _radiusIgnoredOthers.cend();
}
void AvatarMixerClientData::addToRadiusIgnoringSet(const QUuid& other) {
if (!isRadiusIgnoring(other)) {
_radiusIgnoredOthers.push_back(other);
}
}
void AvatarMixerClientData::removeFromRadiusIgnoringSet(const QUuid& other) {
auto ignoredOtherIter = std::find(_radiusIgnoredOthers.cbegin(), _radiusIgnoredOthers.cend(), other);
if (ignoredOtherIter != _radiusIgnoredOthers.cend()) {
_radiusIgnoredOthers.erase(ignoredOtherIter);
}
}

View file

@ -15,7 +15,7 @@
#include <algorithm>
#include <cfloat>
#include <unordered_map>
#include <unordered_set>
#include <vector>
#include <queue>
#include <QtCore/QJsonObject>
@ -45,6 +45,7 @@ public:
int parseData(ReceivedMessage& message) override;
AvatarData& getAvatar() { return *_avatar; }
const AvatarData& getAvatar() const { return *_avatar; }
const AvatarData* getConstAvatarData() const { return _avatar.get(); }
AvatarSharedPointer getAvatarSharedPointer() const { return _avatar; }
@ -90,11 +91,11 @@ public:
void loadJSONStats(QJsonObject& jsonObject) const;
glm::vec3 getPosition() const { return _avatar ? _avatar->getWorldPosition() : glm::vec3(0); }
glm::vec3 getGlobalBoundingBoxCorner() const { return _avatar ? _avatar->getGlobalBoundingBoxCorner() : glm::vec3(0); }
bool isRadiusIgnoring(const QUuid& other) const { return _radiusIgnoredOthers.find(other) != _radiusIgnoredOthers.end(); }
void addToRadiusIgnoringSet(const QUuid& other) { _radiusIgnoredOthers.insert(other); }
void removeFromRadiusIgnoringSet(SharedNodePointer self, const QUuid& other);
bool isRadiusIgnoring(const QUuid& other) const;
void addToRadiusIgnoringSet(const QUuid& other);
void removeFromRadiusIgnoringSet(const QUuid& other);
void ignoreOther(SharedNodePointer self, SharedNodePointer other);
void ignoreOther(const Node* self, const Node* other);
void readViewFrustumPacket(const QByteArray& message);
@ -166,7 +167,7 @@ private:
int _numOutOfOrderSends = 0;
SimpleMovingAverage _avgOtherAvatarDataRate;
std::unordered_set<QUuid> _radiusIgnoredOthers;
std::vector<QUuid> _radiusIgnoredOthers;
ConicalViewFrustums _currentViewFrustums;
int _recentOtherAvatarsInView { 0 };

View file

@ -13,6 +13,7 @@
#include <algorithm>
#include <random>
#include <chrono>
#include <glm/glm.hpp>
#include <glm/gtx/norm.hpp>
@ -33,6 +34,8 @@
#include "AvatarMixer.h"
#include "AvatarMixerClientData.h"
namespace chrono = std::chrono;
void AvatarMixerSlave::configure(ConstIter begin, ConstIter end) {
_begin = begin;
_end = end;
@ -209,7 +212,18 @@ void AvatarMixerSlave::broadcastAvatarData(const SharedNodePointer& node) {
_stats.jobElapsedTime += (end - start);
}
AABox computeBubbleBox(const AvatarData& avatar, float bubbleExpansionFactor) {
AABox box = avatar.getGlobalBoundingBox();
glm::vec3 scale = box.getScale();
scale *= bubbleExpansionFactor;
const glm::vec3 MIN_BUBBLE_SCALE(0.3f, 1.3f, 0.3);
scale = glm::max(scale, MIN_BUBBLE_SCALE);
box.setScaleStayCentered(glm::max(scale, MIN_BUBBLE_SCALE));
return box;
}
void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node) {
const Node* destinationNode = node.data();
auto nodeList = DependencyManager::get<NodeList>();
@ -220,7 +234,7 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node)
_stats.nodesBroadcastedTo++;
AvatarMixerClientData* nodeData = reinterpret_cast<AvatarMixerClientData*>(node->getLinkedData());
AvatarMixerClientData* nodeData = reinterpret_cast<AvatarMixerClientData*>(destinationNode->getLinkedData());
nodeData->resetInViewStats();
@ -242,12 +256,8 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node)
int traitBytesSent = 0;
// max number of avatarBytes per frame
auto maxAvatarBytesPerFrame = (_maxKbpsPerNode * BYTES_PER_KILOBIT) / AVATAR_MIXER_BROADCAST_FRAMES_PER_SECOND;
int maxAvatarBytesPerFrame = int(_maxKbpsPerNode * BYTES_PER_KILOBIT / AVATAR_MIXER_BROADCAST_FRAMES_PER_SECOND);
// FIXME - find a way to not send the sessionID for every avatar
int minimumBytesPerAvatar = AvatarDataPacket::AVATAR_HAS_FLAGS_SIZE + NUM_BYTES_RFC4122_UUID;
int overBudgetAvatars = 0;
// keep track of the number of other avatars held back in this frame
int numAvatarsHeldBack = 0;
@ -260,66 +270,38 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node)
bool PALIsOpen = nodeData->getRequestsDomainListData();
// When this is true, the AvatarMixer will send Avatar data to a client about avatars that have ignored them
bool getsAnyIgnored = PALIsOpen && node->getCanKick();
bool getsAnyIgnored = PALIsOpen && destinationNode->getCanKick();
if (PALIsOpen) {
// Increase minimumBytesPerAvatar if the PAL is open
minimumBytesPerAvatar += sizeof(AvatarDataPacket::AvatarGlobalPosition) +
sizeof(AvatarDataPacket::AudioLoudness);
}
// Bandwidth allowance for data that must be sent.
int minimumBytesPerAvatar = PALIsOpen ? AvatarDataPacket::AVATAR_HAS_FLAGS_SIZE + NUM_BYTES_RFC4122_UUID +
sizeof(AvatarDataPacket::AvatarGlobalPosition) + sizeof(AvatarDataPacket::AudioLoudness) : 0;
// setup a PacketList for the avatarPackets
auto avatarPacketList = NLPacketList::create(PacketType::BulkAvatarData);
static auto maxAvatarDataBytes = avatarPacketList->getMaxSegmentSize() - NUM_BYTES_RFC4122_UUID;
// Define the minimum bubble size
static const glm::vec3 minBubbleSize = avatar.getSensorToWorldScale() * glm::vec3(0.3f, 1.3f, 0.3f);
// Define the scale of the box for the current node
glm::vec3 nodeBoxScale = (nodeData->getPosition() - nodeData->getGlobalBoundingBoxCorner()) * 2.0f * avatar.getSensorToWorldScale();
// Set up the bounding box for the current node
AABox nodeBox(nodeData->getGlobalBoundingBoxCorner(), nodeBoxScale);
// Clamp the size of the bounding box to a minimum scale
if (glm::any(glm::lessThan(nodeBoxScale, minBubbleSize))) {
nodeBox.setScaleStayCentered(minBubbleSize);
}
// Quadruple the scale of both bounding boxes
nodeBox.embiggen(4.0f);
// setup list of AvatarData as well as maps to map betweeen the AvatarData and the original nodes
std::vector<AvatarSharedPointer> avatarsToSort;
std::unordered_map<AvatarSharedPointer, SharedNodePointer> avatarDataToNodes;
std::unordered_map<QUuid, uint64_t> avatarEncodeTimes;
std::for_each(_begin, _end, [&](const SharedNodePointer& otherNode) {
// make sure this is an agent that we have avatar data for before considering it for inclusion
if (otherNode->getType() == NodeType::Agent
&& otherNode->getLinkedData()) {
const AvatarMixerClientData* otherNodeData = reinterpret_cast<const AvatarMixerClientData*>(otherNode->getLinkedData());
AvatarSharedPointer otherAvatar = otherNodeData->getAvatarSharedPointer();
avatarsToSort.push_back(otherAvatar);
avatarDataToNodes[otherAvatar] = otherNode;
QUuid id = otherAvatar->getSessionUUID();
avatarEncodeTimes[id] = nodeData->getLastOtherAvatarEncodeTime(id);
}
});
// compute node bounding box
const float MY_AVATAR_BUBBLE_EXPANSION_FACTOR = 4.0f; // magic number determined emperically
AABox nodeBox = computeBubbleBox(avatar, MY_AVATAR_BUBBLE_EXPANSION_FACTOR);
class SortableAvatar: public PrioritySortUtil::Sortable {
public:
SortableAvatar() = delete;
SortableAvatar(const AvatarSharedPointer& avatar, uint64_t lastEncodeTime)
: _avatar(avatar), _lastEncodeTime(lastEncodeTime) {}
glm::vec3 getPosition() const override { return _avatar->getWorldPosition(); }
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 nodeBoxHalfScale = (_avatar->getWorldPosition() - _avatar->getGlobalBoundingBoxCorner() * _avatar->getSensorToWorldScale());
return glm::max(nodeBoxHalfScale.x, glm::max(nodeBoxHalfScale.y, nodeBoxHalfScale.z));
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;
}
AvatarSharedPointer getAvatar() const { return _avatar; }
const Node* getNode() const { return _node; }
private:
AvatarSharedPointer _avatar;
const AvatarData* _avatar;
const Node* _node;
uint64_t _lastEncodeTime;
};
@ -329,16 +311,18 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node)
AvatarData::_avatarSortCoefficientSize,
AvatarData::_avatarSortCoefficientCenter,
AvatarData::_avatarSortCoefficientAge);
sortedAvatars.reserve(avatarsToSort.size());
sortedAvatars.reserve(_end - _begin);
// ignore or sort
const AvatarSharedPointer& thisAvatar = nodeData->getAvatarSharedPointer();
for (const auto& avatar : avatarsToSort) {
if (avatar == thisAvatar) {
// don't echo updates to self
for (auto listedNode = _begin; listedNode != _end; ++listedNode) {
Node* otherNodeRaw = (*listedNode).data();
if (otherNodeRaw->getType() != NodeType::Agent
|| !otherNodeRaw->getLinkedData()
|| otherNodeRaw == destinationNode) {
continue;
}
auto avatarNode = otherNodeRaw;
bool shouldIgnore = false;
// We ignore other nodes for a couple of reasons:
// 1) ignore bubbles and ignore specific node
@ -346,53 +330,39 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node)
// happen if for example the avatar is connected on a desktop and sending
// updates at ~30hz. So every 3 frames we skip a frame.
auto avatarNode = avatarDataToNodes[avatar];
assert(avatarNode); // we can't have gotten here without the avatarData being a valid key in the map
const AvatarMixerClientData* avatarNodeData = reinterpret_cast<const AvatarMixerClientData*>(avatarNode->getLinkedData());
assert(avatarNodeData); // we can't have gotten here without avatarNode having valid data
const AvatarMixerClientData* avatarClientNodeData = reinterpret_cast<const AvatarMixerClientData*>(avatarNode->getLinkedData());
assert(avatarClientNodeData); // we can't have gotten here without avatarNode having valid data
quint64 startIgnoreCalculation = usecTimestampNow();
// 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
// or that has ignored the viewing node
if (!avatarNode->getLinkedData()
|| avatarNode->getUUID() == node->getUUID()
|| (node->isIgnoringNodeWithID(avatarNode->getUUID()) && !PALIsOpen)
|| (avatarNode->isIgnoringNodeWithID(node->getUUID()) && !getsAnyIgnored)) {
if ((destinationNode->isIgnoringNodeWithID(avatarNode->getUUID()) && !PALIsOpen)
|| (avatarNode->isIgnoringNodeWithID(destinationNode->getUUID()) && !getsAnyIgnored)) {
shouldIgnore = true;
} else {
// 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
if (node->isIgnoreRadiusEnabled() || (avatarNode->isIgnoreRadiusEnabled() && !getsAnyIgnored)) {
float sensorToWorldScale = avatarNodeData->getAvatarSharedPointer()->getSensorToWorldScale();
// Define the scale of the box for the current other node
glm::vec3 otherNodeBoxScale = (avatarNodeData->getPosition() - avatarNodeData->getGlobalBoundingBoxCorner()) * 2.0f * sensorToWorldScale;
// Set up the bounding box for the current other node
AABox otherNodeBox(avatarNodeData->getGlobalBoundingBoxCorner(), otherNodeBoxScale);
// Clamp the size of the bounding box to a minimum scale
if (glm::any(glm::lessThan(otherNodeBoxScale, minBubbleSize))) {
otherNodeBox.setScaleStayCentered(minBubbleSize);
}
// Change the scale of both bounding boxes
// (This is an arbitrary number determined empirically)
otherNodeBox.embiggen(2.4f);
if (destinationNode->isIgnoreRadiusEnabled() || (avatarNode->isIgnoreRadiusEnabled() && !getsAnyIgnored)) {
// Perform the collision check between the two bounding boxes
const float OTHER_AVATAR_BUBBLE_EXPANSION_FACTOR = 2.4f; // magic number determined empirically
AABox otherNodeBox = computeBubbleBox(avatarClientNodeData->getAvatar(), OTHER_AVATAR_BUBBLE_EXPANSION_FACTOR);
if (nodeBox.touches(otherNodeBox)) {
nodeData->ignoreOther(node, avatarNode);
nodeData->ignoreOther(destinationNode, avatarNode);
shouldIgnore = !getsAnyIgnored;
}
}
// Not close enough to ignore
if (!shouldIgnore) {
nodeData->removeFromRadiusIgnoringSet(node, avatarNode->getUUID());
nodeData->removeFromRadiusIgnoringSet(avatarNode->getUUID());
}
}
if (!shouldIgnore) {
AvatarDataSequenceNumber lastSeqToReceiver = nodeData->getLastBroadcastSequenceNumber(avatarNode->getUUID());
AvatarDataSequenceNumber lastSeqFromSender = avatarNodeData->getLastReceivedSequenceNumber();
AvatarDataSequenceNumber lastSeqFromSender = avatarClientNodeData->getLastReceivedSequenceNumber();
// FIXME - This code does appear to be working. But it seems brittle.
// It supports determining if the frame of data for this "other"
@ -417,12 +387,10 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node)
if (!shouldIgnore) {
// sort this one for later
uint64_t lastEncodeTime = 0;
std::unordered_map<QUuid, uint64_t>::const_iterator itr = avatarEncodeTimes.find(avatar->getSessionUUID());
if (itr != avatarEncodeTimes.end()) {
lastEncodeTime = itr->second;
}
sortedAvatars.push(SortableAvatar(avatar, lastEncodeTime));
const AvatarData* avatarNodeData = avatarClientNodeData->getConstAvatarData();
auto lastEncodeTime = nodeData->getLastOtherAvatarEncodeTime(avatarNodeData->getSessionUUID());
sortedAvatars.push(SortableAvatar(avatarNodeData, avatarNode, lastEncodeTime));
}
}
@ -430,19 +398,31 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node)
int remainingAvatars = (int)sortedAvatars.size();
auto traitsPacketList = NLPacketList::create(PacketType::BulkAvatarTraits, QByteArray(), true, true);
const auto& sortedAvatarVector = sortedAvatars.getSortedVector();
for (const auto& sortedAvatar : sortedAvatarVector) {
const auto& avatarData = sortedAvatar.getAvatar();
remainingAvatars--;
const Node* otherNode = sortedAvatar.getNode();
auto lastEncodeForOther = sortedAvatar.getTimestamp();
auto otherNode = avatarDataToNodes[avatarData];
assert(otherNode); // we can't have gotten here without the avatarData being a valid key in the map
// NOTE: Here's where we determine if we are over budget and drop to bare minimum data
AvatarData::AvatarDataDetail detail = AvatarData::NoData;
// NOTE: Here's where we determine if we are over budget and drop remaining avatars,
// or send minimal avatar data in uncommon case of PALIsOpen.
int minimRemainingAvatarBytes = minimumBytesPerAvatar * remainingAvatars;
bool overBudget = (identityBytesSent + numAvatarDataBytes + minimRemainingAvatarBytes) > maxAvatarBytesPerFrame;
if (overBudget) {
if (PALIsOpen) {
_stats.overBudgetAvatars++;
detail = AvatarData::PALMinimum;
} else {
_stats.overBudgetAvatars += remainingAvatars;
break;
}
}
quint64 startAvatarDataPacking = usecTimestampNow();
auto startAvatarDataPacking = chrono::high_resolution_clock::now();
++numOtherAvatars;
@ -459,32 +439,18 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node)
nodeData->setLastBroadcastTime(otherNode->getUUID(), usecTimestampNow());
}
// determine if avatar is in view which determines how much data to send
glm::vec3 otherPosition = otherAvatar->getClientGlobalPosition();
glm::vec3 otherNodeBoxScale = (otherPosition - otherNodeData->getGlobalBoundingBoxCorner()) * 2.0f * otherAvatar->getSensorToWorldScale();
AABox otherNodeBox(otherNodeData->getGlobalBoundingBoxCorner(), otherNodeBoxScale);
bool isInView = nodeData->otherAvatarInView(otherNodeBox);
// Typically all out-of-view avatars but such avatars' priorities will rise with time:
bool isLowerPriority = sortedAvatar.getPriority() <= OUT_OF_VIEW_THRESHOLD;
// start a new segment in the PacketList for this avatar
avatarPacketList->startSegment();
AvatarData::AvatarDataDetail detail;
if (overBudget) {
overBudgetAvatars++;
_stats.overBudgetAvatars++;
detail = PALIsOpen ? AvatarData::PALMinimum : AvatarData::NoData;
} else if (!isInView) {
if (isLowerPriority) {
detail = PALIsOpen ? AvatarData::PALMinimum : AvatarData::MinimumData;
nodeData->incrementAvatarOutOfView();
} else {
detail = distribution(generator) < AVATAR_SEND_FULL_UPDATE_RATIO
? AvatarData::SendAllData : AvatarData::CullSmallData;
} else if (!overBudget) {
detail = distribution(generator) < AVATAR_SEND_FULL_UPDATE_RATIO ? AvatarData::SendAllData : AvatarData::CullSmallData;
nodeData->incrementAvatarInView();
}
bool includeThisAvatar = true;
auto lastEncodeForOther = nodeData->getLastOtherAvatarEncodeTime(otherNode->getUUID());
QVector<JointData>& lastSentJointsForOther = nodeData->getLastOtherAvatarSentJoints(otherNode->getUUID());
lastSentJointsForOther.resize(otherAvatar->getJointCount());
@ -494,14 +460,14 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node)
AvatarDataPacket::HasFlags hasFlagsOut; // the result of the toByteArray
bool dropFaceTracking = false;
quint64 start = usecTimestampNow();
auto startSerialize = chrono::high_resolution_clock::now();
QByteArray bytes = otherAvatar->toByteArray(detail, lastEncodeForOther, lastSentJointsForOther,
hasFlagsOut, dropFaceTracking, distanceAdjust, viewerPosition,
&lastSentJointsForOther);
quint64 end = usecTimestampNow();
_stats.toByteArrayElapsedTime += (end - start);
auto endSerialize = chrono::high_resolution_clock::now();
_stats.toByteArrayElapsedTime +=
(quint64) chrono::duration_cast<chrono::microseconds>(endSerialize - startSerialize).count();
static auto maxAvatarDataBytes = avatarPacketList->getMaxSegmentSize() - NUM_BYTES_RFC4122_UUID;
if (bytes.size() > maxAvatarDataBytes) {
qCWarning(avatars) << "otherAvatar.toByteArray() for" << otherNode->getUUID()
<< "resulted in very large buffer of" << bytes.size() << "bytes - dropping facial data";
@ -527,8 +493,11 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node)
}
if (includeThisAvatar) {
// start a new segment in the PacketList for this avatar
avatarPacketList->startSegment();
numAvatarDataBytes += avatarPacketList->write(otherNode->getUUID().toRfc4122());
numAvatarDataBytes += avatarPacketList->write(bytes);
avatarPacketList->endSegment();
if (detail != AvatarData::NoData) {
_stats.numOthersIncluded++;
@ -546,15 +515,13 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node)
// It would be nice if we could tweak its future sort priority to put it at the back of the list.
}
avatarPacketList->endSegment();
quint64 endAvatarDataPacking = usecTimestampNow();
_stats.avatarDataPackingElapsedTime += (endAvatarDataPacking - startAvatarDataPacking);
auto endAvatarDataPacking = chrono::high_resolution_clock::now();
_stats.avatarDataPackingElapsedTime +=
(quint64) chrono::duration_cast<chrono::microseconds>(endAvatarDataPacking - startAvatarDataPacking).count();
// use helper to add any changed traits to our packet list
traitBytesSent += addChangedTraitsToBulkPacket(nodeData, otherNodeData, *traitsPacketList);
traitsPacketList->getDataSize();
remainingAvatars--;
}
quint64 startPacketSending = usecTimestampNow();
@ -566,7 +533,7 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node)
_stats.numBytesSent += numAvatarDataBytes;
// send the avatar data PacketList
nodeList->sendPacketList(std::move(avatarPacketList), *node);
nodeList->sendPacketList(std::move(avatarPacketList), *destinationNode);
// record the bytes sent for other avatar data in the AvatarMixerClientData
nodeData->recordSentAvatarData(numAvatarDataBytes);
@ -576,7 +543,7 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node)
if (traitsPacketList->getNumPackets() >= 1) {
// send the traits packet list
nodeList->sendPacketList(std::move(traitsPacketList), *node);
nodeList->sendPacketList(std::move(traitsPacketList), *destinationNode);
}
// record the number of avatars held back this frame

View file

@ -145,3 +145,15 @@ void ScriptableAvatar::update(float deltatime) {
_clientTraitsHandler->sendChangedTraitsToMixer();
}
void ScriptableAvatar::setHasProceduralBlinkFaceMovement(bool hasProceduralBlinkFaceMovement) {
_headData->setHasProceduralBlinkFaceMovement(hasProceduralBlinkFaceMovement);
}
void ScriptableAvatar::setHasProceduralEyeFaceMovement(bool hasProceduralEyeFaceMovement) {
_headData->setHasProceduralEyeFaceMovement(hasProceduralEyeFaceMovement);
}
void ScriptableAvatar::setHasAudioEnabledFaceMovement(bool hasAudioEnabledFaceMovement) {
_headData->setHasAudioEnabledFaceMovement(hasAudioEnabledFaceMovement);
}

View file

@ -157,9 +157,16 @@ public:
virtual QByteArray toByteArrayStateful(AvatarDataDetail dataDetail, bool dropFaceTracking = false) override;
void setHasProceduralBlinkFaceMovement(bool hasProceduralBlinkFaceMovement);
bool getHasProceduralBlinkFaceMovement() const override { return _headData->getHasProceduralBlinkFaceMovement(); }
void setHasProceduralEyeFaceMovement(bool hasProceduralEyeFaceMovement);
bool getHasProceduralEyeFaceMovement() const override { return _headData->getHasProceduralEyeFaceMovement(); }
void setHasAudioEnabledFaceMovement(bool hasAudioEnabledFaceMovement);
bool getHasAudioEnabledFaceMovement() const override { return _headData->getHasAudioEnabledFaceMovement(); }
private slots:
void update(float deltatime);
private:
AnimationPointer _animation;
AnimationDetails _animationDetails;

View file

@ -9,7 +9,6 @@
#
macro(AUTOSCRIBE_SHADER)
message(STATUS "Processing shader ${SHADER_FILE}")
unset(SHADER_INCLUDE_FILES)
# Grab include files
foreach(includeFile ${ARGN})

View file

@ -8,6 +8,5 @@
macro(TARGET_JSON)
add_dependency_external_projects(json)
find_package(JSON REQUIRED)
message("JSON_INCLUDE_DIRS ${JSON_INCLUDE_DIRS}")
target_include_directories(${TARGET_NAME} PUBLIC ${JSON_INCLUDE_DIRS})
endmacro()

View file

@ -332,6 +332,10 @@ if (APPLE)
COMMAND "${CMAKE_COMMAND}" -E copy_directory
"${PROJECT_SOURCE_DIR}/resources/fonts"
"${RESOURCES_DEV_DIR}/fonts"
# add redirect json to macOS builds.
COMMAND "${CMAKE_COMMAND}" -E copy_if_different
"${PROJECT_SOURCE_DIR}/resources/serverless/redirect.json"
"${RESOURCES_DEV_DIR}/serverless/redirect.json"
)
# call the fixup_interface macro to add required bundling commands for installation
@ -360,6 +364,9 @@ else()
COMMAND "${CMAKE_COMMAND}" -E copy_if_different
"${PROJECT_SOURCE_DIR}/resources/serverless/tutorial.json"
"${RESOURCES_DEV_DIR}/serverless/tutorial.json"
COMMAND "${CMAKE_COMMAND}" -E copy_if_different
"${PROJECT_SOURCE_DIR}/resources/serverless/redirect.json"
"${RESOURCES_DEV_DIR}/serverless/redirect.json"
# copy JSDoc files beside the executable
COMMAND "${CMAKE_COMMAND}" -E copy_directory
"${CMAKE_SOURCE_DIR}/tools/jsdoc/out"

Binary file not shown.

Binary file not shown.

View file

@ -585,149 +585,188 @@
"states": [
{
"id": "idle",
"interpTarget": 0,
"interpDuration": 4,
"interpTarget": 20,
"interpDuration": 8,
"interpType": "snapshotPrev",
"transitions": [
{ "var": "isMovingForward", "state": "idleToWalkFwd" },
{ "var": "isMovingBackward", "state": "walkBwd" },
{ "var": "isMovingRight", "state": "strafeRight" },
{ "var": "isMovingLeft", "state": "strafeLeft" },
{ "var": "isMovingForward", "state": "WALKFWD" },
{ "var": "isMovingBackward", "state": "WALKBWD" },
{ "var": "isMovingRight", "state": "STRAFERIGHT" },
{ "var": "isMovingLeft", "state": "STRAFELEFT" },
{ "var": "isTurningRight", "state": "turnRight" },
{ "var": "isTurningLeft", "state": "turnLeft" },
{ "var": "isFlying", "state": "fly" },
{ "var": "isTakeoffStand", "state": "takeoffStand" },
{ "var": "isTakeoffRun", "state": "takeoffRun" },
{ "var": "isTakeoffRun", "state": "TAKEOFFRUN" },
{ "var": "isInAirStand", "state": "inAirStand" },
{ "var": "isInAirRun", "state": "inAirRun" },
{ "var": "isInAirRun", "state": "INAIRRUN" },
{ "var": "isMovingRightHmd", "state": "strafeRightHmd" },
{ "var": "isMovingLeftHmd", "state": "strafeLeftHmd" }
]
},
{
"id": "idleToWalkFwd",
"interpTarget": 10,
"interpDuration": 4,
"interpType": "snapshotPrev",
"interpTarget": 12,
"interpDuration": 8,
"transitions": [
{ "var": "idleToWalkFwdOnDone", "state": "walkFwd" },
{ "var": "idleToWalkFwdOnDone", "state": "WALKFWD" },
{ "var": "isNotMoving", "state": "idle" },
{ "var": "isMovingBackward", "state": "walkBwd" },
{ "var": "isMovingRight", "state": "strafeRight" },
{ "var": "isMovingLeft", "state": "strafeLeft" },
{ "var": "isMovingBackward", "state": "WALKBWD" },
{ "var": "isMovingRight", "state": "STRAFERIGHT" },
{ "var": "isMovingLeft", "state": "STRAFELEFT" },
{ "var": "isTurningRight", "state": "turnRight" },
{ "var": "isTurningLeft", "state": "turnLeft" },
{ "var": "isFlying", "state": "fly" },
{ "var": "isTakeoffStand", "state": "takeoffStand" },
{ "var": "isTakeoffRun", "state": "takeoffRun" },
{ "var": "isTakeoffRun", "state": "TAKEOFFRUN" },
{ "var": "isInAirStand", "state": "inAirStand" },
{ "var": "isInAirRun", "state": "inAirRun" },
{ "var": "isInAirRun", "state": "INAIRRUN" },
{ "var": "isMovingRightHmd", "state": "strafeRightHmd" },
{ "var": "isMovingLeftHmd", "state": "strafeLeftHmd" }
]
},
{
"id": "idleSettle",
"interpTarget": 10,
"interpDuration": 10,
"interpTarget": 15,
"interpDuration": 8,
"interpType": "snapshotPrev",
"transitions": [
{"var": "idleSettleOnDone", "state": "idle" },
{"var": "isMovingForward", "state": "idleToWalkFwd" },
{ "var": "isMovingBackward", "state": "walkBwd" },
{ "var": "isMovingRight", "state": "strafeRight" },
{ "var": "isMovingLeft", "state": "strafeLeft" },
{"var": "isMovingForward", "state": "WALKFWD" },
{ "var": "isMovingBackward", "state": "WALKBWD" },
{ "var": "isMovingRight", "state": "STRAFERIGHT" },
{ "var": "isMovingLeft", "state": "STRAFELEFT" },
{ "var": "isMovingRightHmd", "state": "strafeRightHmd" },
{ "var": "isMovingLeftHmd", "state": "strafeLeftHmd" },
{ "var": "isTurningRight", "state": "turnRight" },
{ "var": "isTurningLeft", "state": "turnLeft" },
{ "var": "isFlying", "state": "fly" },
{ "var": "isTakeoffStand", "state": "takeoffStand" },
{ "var": "isTakeoffRun", "state": "takeoffRun" },
{ "var": "isTakeoffRun", "state": "TAKEOFFRUN" },
{ "var": "isInAirStand", "state": "inAirStand" },
{ "var": "isInAirRun", "state": "inAirRun" }
{ "var": "isInAirRun", "state": "INAIRRUN" }
]
},
{
"id": "walkFwd",
"interpTarget": 16,
"interpDuration": 6,
"id": "WALKFWD",
"interpTarget": 35,
"interpDuration": 10,
"interpType": "snapshotPrev",
"transitions": [
{ "var": "isNotMoving", "state": "idleSettle" },
{ "var": "isMovingBackward", "state": "walkBwd" },
{ "var": "isMovingRight", "state": "strafeRight" },
{ "var": "isMovingLeft", "state": "strafeLeft" },
{ "var": "isMovingBackward", "state": "WALKBWD" },
{ "var": "isMovingRight", "state": "STRAFERIGHT" },
{ "var": "isMovingLeft", "state": "STRAFELEFT" },
{ "var": "isTurningRight", "state": "turnRight" },
{ "var": "isTurningLeft", "state": "turnLeft" },
{ "var": "isFlying", "state": "fly" },
{ "var": "isTakeoffStand", "state": "takeoffStand" },
{ "var": "isTakeoffRun", "state": "takeoffRun" },
{ "var": "isTakeoffRun", "state": "TAKEOFFRUN" },
{ "var": "isInAirStand", "state": "inAirStand" },
{ "var": "isInAirRun", "state": "inAirRun" },
{ "var": "isInAirRun", "state": "INAIRRUN" },
{ "var": "isMovingRightHmd", "state": "strafeRightHmd" },
{ "var": "isMovingLeftHmd", "state": "strafeLeftHmd" }
]
},
{
"id": "walkBwd",
"interpTarget": 8,
"interpDuration": 6,
"id": "WALKBWD",
"interpTarget": 35,
"interpDuration": 10,
"interpType": "snapshotPrev",
"transitions": [
{ "var": "isNotMoving", "state": "idleSettle" },
{ "var": "isMovingForward", "state": "walkFwd" },
{ "var": "isMovingRight", "state": "strafeRight" },
{ "var": "isMovingLeft", "state": "strafeLeft" },
{ "var": "isMovingForward", "state": "WALKFWD" },
{ "var": "isMovingRight", "state": "STRAFERIGHT" },
{ "var": "isMovingLeft", "state": "STRAFELEFT" },
{ "var": "isTurningRight", "state": "turnRight" },
{ "var": "isTurningLeft", "state": "turnLeft" },
{ "var": "isFlying", "state": "fly" },
{ "var": "isTakeoffStand", "state": "takeoffStand" },
{ "var": "isTakeoffRun", "state": "takeoffRun" },
{ "var": "isTakeoffRun", "state": "TAKEOFFRUN" },
{ "var": "isInAirStand", "state": "inAirStand" },
{ "var": "isInAirRun", "state": "inAirRun" },
{ "var": "isInAirRun", "state": "INAIRRUN" },
{ "var": "isMovingRightHmd", "state": "strafeRightHmd" },
{ "var": "isMovingLeftHmd", "state": "strafeLeftHmd" }
]
},
{
"id": "strafeRight",
"interpTarget": 5,
"id": "STRAFERIGHT",
"interpTarget": 25,
"interpDuration": 8,
"interpType": "snapshotPrev",
"transitions": [
{ "var": "isNotMoving", "state": "idleSettle" },
{ "var": "isMovingForward", "state": "walkFwd" },
{ "var": "isMovingBackward", "state": "walkBwd" },
{ "var": "isMovingLeft", "state": "strafeLeft" },
{ "var": "isMovingForward", "state": "WALKFWD" },
{ "var": "isMovingBackward", "state": "WALKBWD" },
{ "var": "isMovingLeft", "state": "STRAFELEFT" },
{ "var": "isTurningRight", "state": "turnRight" },
{ "var": "isTurningLeft", "state": "turnLeft" },
{ "var": "isFlying", "state": "fly" },
{ "var": "isTakeoffStand", "state": "takeoffStand" },
{ "var": "isTakeoffRun", "state": "takeoffRun" },
{ "var": "isTakeoffRun", "state": "TAKEOFFRUN" },
{ "var": "isInAirStand", "state": "inAirStand" },
{ "var": "isInAirRun", "state": "inAirRun" },
{ "var": "isInAirRun", "state": "INAIRRUN" },
{ "var": "isMovingRightHmd", "state": "strafeRightHmd" },
{ "var": "isMovingLeftHmd", "state": "strafeLeftHmd" }
]
},
{
"id": "strafeLeft",
"interpTarget": 5,
"id": "STRAFELEFT",
"interpTarget": 25,
"interpDuration": 8,
"interpType": "snapshotPrev",
"transitions": [
{ "var": "isNotMoving", "state": "idleSettle" },
{ "var": "isMovingForward", "state": "walkFwd" },
{ "var": "isMovingBackward", "state": "walkBwd" },
{ "var": "isMovingRight", "state": "strafeRight" },
{ "var": "isMovingForward", "state": "WALKFWD" },
{ "var": "isMovingBackward", "state": "WALKBWD" },
{ "var": "isMovingRight", "state": "STRAFERIGHT" },
{ "var": "isTurningRight", "state": "turnRight" },
{ "var": "isTurningLeft", "state": "turnLeft" },
{ "var": "isFlying", "state": "fly" },
{ "var": "isTakeoffStand", "state": "takeoffStand" },
{ "var": "isTakeoffRun", "state": "takeoffRun" },
{ "var": "isTakeoffRun", "state": "TAKEOFFRUN" },
{ "var": "isInAirStand", "state": "inAirStand" },
{ "var": "isInAirRun", "state": "inAirRun" },
{ "var": "isInAirRun", "state": "INAIRRUN" },
{ "var": "isMovingRightHmd", "state": "strafeRightHmd" },
{ "var": "isMovingLeftHmd", "state": "strafeLeftHmd" }
]
},
{
"id": "turnRight",
"interpTarget": 6,
"interpDuration": 8,
"transitions": [
{ "var": "isNotTurning", "state": "idle" },
{ "var": "isMovingForward", "state": "WALKFWD" },
{ "var": "isMovingBackward", "state": "WALKBWD" },
{ "var": "isMovingRight", "state": "STRAFERIGHT" },
{ "var": "isMovingLeft", "state": "STRAFELEFT" },
{ "var": "isTurningLeft", "state": "turnLeft" },
{ "var": "isFlying", "state": "fly" },
{ "var": "isTakeoffStand", "state": "takeoffStand" },
{ "var": "isTakeoffRun", "state": "TAKEOFFRUN" },
{ "var": "isInAirStand", "state": "inAirStand" },
{ "var": "isInAirRun", "state": "INAIRRUN" },
{ "var": "isMovingRightHmd", "state": "strafeRightHmd" },
{ "var": "isMovingLeftHmd", "state": "strafeLeftHmd" }
]
},
{
"id": "turnLeft",
"interpTarget": 6,
"interpDuration": 8,
"transitions": [
{ "var": "isNotTurning", "state": "idle" },
{ "var": "isMovingForward", "state": "WALKFWD" },
{ "var": "isMovingBackward", "state": "WALKBWD" },
{ "var": "isMovingRight", "state": "STRAFERIGHT" },
{ "var": "isMovingLeft", "state": "STRAFELEFT" },
{ "var": "isTurningRight", "state": "turnRight" },
{ "var": "isFlying", "state": "fly" },
{ "var": "isTakeoffStand", "state": "takeoffStand" },
{ "var": "isTakeoffRun", "state": "TAKEOFFRUN" },
{ "var": "isInAirStand", "state": "inAirStand" },
{ "var": "isInAirRun", "state": "INAIRRUN" },
{ "var": "isMovingRightHmd", "state": "strafeRightHmd" },
{ "var": "isMovingLeftHmd", "state": "strafeLeftHmd" }
]
@ -739,18 +778,18 @@
"interpType": "snapshotPrev",
"transitions": [
{ "var": "isNotMoving", "state": "idleSettle" },
{ "var": "isMovingForward", "state": "walkFwd" },
{ "var": "isMovingBackward", "state": "walkBwd" },
{ "var": "isMovingForward", "state": "WALKFWD" },
{ "var": "isMovingBackward", "state": "WALKBWD" },
{ "var": "isMovingLeftHmd", "state": "strafeLeftHmd" },
{ "var": "isMovingRight", "state": "strafeRight" },
{ "var": "isMovingLeft", "state": "strafeLeft" },
{ "var": "isMovingRight", "state": "STRAFERIGHT" },
{ "var": "isMovingLeft", "state": "STRAFELEFT" },
{ "var": "isTurningRight", "state": "turnRight" },
{ "var": "isTurningLeft", "state": "turnLeft" },
{ "var": "isFlying", "state": "fly" },
{ "var": "isTakeoffStand", "state": "takeoffStand" },
{ "var": "isTakeoffRun", "state": "takeoffRun" },
{ "var": "isTakeoffRun", "state": "TAKEOFFRUN" },
{ "var": "isInAirStand", "state": "inAirStand" },
{ "var": "isInAirRun", "state": "inAirRun" }
{ "var": "isInAirRun", "state": "INAIRRUN" }
]
},
{
@ -760,60 +799,18 @@
"interpType": "snapshotPrev",
"transitions": [
{ "var": "isNotMoving", "state": "idleSettle" },
{ "var": "isMovingForward", "state": "walkFwd" },
{ "var": "isMovingBackward", "state": "walkBwd" },
{ "var": "isMovingForward", "state": "WALKFWD" },
{ "var": "isMovingBackward", "state": "WALKBWD" },
{ "var": "isMovingRightHmd", "state": "strafeRightHmd" },
{ "var": "isMovingRight", "state": "strafeRight" },
{ "var": "isMovingLeft", "state": "strafeLeft" },
{ "var": "isMovingRight", "state": "STRAFERIGHT" },
{ "var": "isMovingLeft", "state": "STRAFELEFT" },
{ "var": "isTurningRight", "state": "turnRight" },
{ "var": "isTurningLeft", "state": "turnLeft" },
{ "var": "isFlying", "state": "fly" },
{ "var": "isTakeoffStand", "state": "takeoffStand" },
{ "var": "isTakeoffRun", "state": "takeoffRun" },
{ "var": "isTakeoffRun", "state": "TAKEOFFRUN" },
{ "var": "isInAirStand", "state": "inAirStand" },
{ "var": "isInAirRun", "state": "inAirRun" }
]
},
{
"id": "turnRight",
"interpTarget": 6,
"interpDuration": 8,
"interpType": "snapshotPrev",
"transitions": [
{ "var": "isNotTurning", "state": "idle" },
{ "var": "isMovingForward", "state": "walkFwd" },
{ "var": "isMovingBackward", "state": "walkBwd" },
{ "var": "isMovingRight", "state": "strafeRight" },
{ "var": "isMovingLeft", "state": "strafeLeft" },
{ "var": "isTurningLeft", "state": "turnLeft" },
{ "var": "isFlying", "state": "fly" },
{ "var": "isTakeoffStand", "state": "takeoffStand" },
{ "var": "isTakeoffRun", "state": "takeoffRun" },
{ "var": "isInAirStand", "state": "inAirStand" },
{ "var": "isInAirRun", "state": "inAirRun" },
{ "var": "isMovingRightHmd", "state": "strafeRightHmd" },
{ "var": "isMovingLeftHmd", "state": "strafeLeftHmd" }
]
},
{
"id": "turnLeft",
"interpTarget": 6,
"interpDuration": 8,
"interpType": "snapshotPrev",
"transitions": [
{ "var": "isNotTurning", "state": "idle" },
{ "var": "isMovingForward", "state": "walkFwd" },
{ "var": "isMovingBackward", "state": "walkBwd" },
{ "var": "isMovingRight", "state": "strafeRight" },
{ "var": "isMovingLeft", "state": "strafeLeft" },
{ "var": "isTurningRight", "state": "turnRight" },
{ "var": "isFlying", "state": "fly" },
{ "var": "isTakeoffStand", "state": "takeoffStand" },
{ "var": "isTakeoffRun", "state": "takeoffRun" },
{ "var": "isInAirStand", "state": "inAirStand" },
{ "var": "isInAirRun", "state": "inAirRun" },
{ "var": "isMovingRightHmd", "state": "strafeRightHmd" },
{ "var": "isMovingLeftHmd", "state": "strafeLeftHmd" }
{ "var": "isInAirRun", "state": "INAIRRUN" }
]
},
{
@ -826,79 +823,79 @@
},
{
"id": "takeoffStand",
"interpTarget": 0,
"interpDuration": 6,
"interpTarget": 2,
"interpDuration": 2,
"transitions": [
{ "var": "isNotTakeoff", "state": "inAirStand" }
]
},
{
"id": "takeoffRun",
"interpTarget": 0,
"interpDuration": 6,
"id": "TAKEOFFRUN",
"interpTarget": 2,
"interpDuration": 2,
"transitions": [
{ "var": "isNotTakeoff", "state": "inAirRun" }
{ "var": "isNotTakeoff", "state": "INAIRRUN" }
]
},
{
"id": "inAirStand",
"interpTarget": 0,
"interpDuration": 6,
"interpTarget": 3,
"interpDuration": 3,
"interpType": "snapshotPrev",
"transitions": [
{ "var": "isNotInAir", "state": "landStandImpact" }
]
},
{
"id": "inAirRun",
"interpTarget": 0,
"interpDuration": 6,
"id": "INAIRRUN",
"interpTarget": 3,
"interpDuration": 3,
"interpType": "snapshotPrev",
"transitions": [
{ "var": "isNotInAir", "state": "landRun" }
{ "var": "isNotInAir", "state": "WALKFWD" }
]
},
{
"id": "landStandImpact",
"interpTarget": 6,
"interpDuration": 4,
"interpTarget": 1,
"interpDuration": 1,
"transitions": [
{ "var": "isFlying", "state": "fly" },
{ "var": "isTakeoffStand", "state": "takeoffStand" },
{ "var": "isTakeoffRun", "state": "takeoffRun" },
{ "var": "isTakeoffRun", "state": "TAKEOFFRUN" },
{ "var": "landStandImpactOnDone", "state": "landStand" }
]
},
{
"id": "landStand",
"interpTarget": 0,
"interpTarget": 1,
"interpDuration": 1,
"transitions": [
{ "var": "isMovingForward", "state": "idleToWalkFwd" },
{ "var": "isMovingBackward", "state": "walkBwd" },
{ "var": "isMovingRight", "state": "strafeRight" },
{ "var": "isMovingLeft", "state": "strafeLeft" },
{ "var": "isMovingForward", "state": "WALKFWD" },
{ "var": "isMovingBackward", "state": "WALKBWD" },
{ "var": "isMovingRight", "state": "STRAFERIGHT" },
{ "var": "isMovingLeft", "state": "STRAFELEFT" },
{ "var": "isTurningRight", "state": "turnRight" },
{ "var": "isTurningLeft", "state": "turnLeft" },
{ "var": "isFlying", "state": "fly" },
{ "var": "isTakeoffStand", "state": "takeoffStand" },
{ "var": "isTakeoffRun", "state": "takeoffRun" },
{ "var": "isTakeoffRun", "state": "TAKEOFFRUN" },
{ "var": "isInAirStand", "state": "inAirStand" },
{ "var": "isInAirRun", "state": "inAirRun" },
{ "var": "isInAirRun", "state": "INAIRRUN" },
{ "var": "landStandOnDone", "state": "idle" },
{ "var": "isMovingRightHmd", "state": "strafeRightHmd" },
{ "var": "isMovingLeftHmd", "state": "strafeLeftHmd" }
]
},
{
"id": "landRun",
"interpTarget": 1,
"interpDuration": 7,
"id": "LANDRUN",
"interpTarget": 2,
"interpDuration": 2,
"transitions": [
{ "var": "isFlying", "state": "fly" },
{ "var": "isTakeoffStand", "state": "takeoffStand" },
{ "var": "isTakeoffRun", "state": "takeoffRun" },
{ "var": "landRunOnDone", "state": "walkFwd" }
{ "var": "isTakeoffRun", "state": "TAKEOFFRUN" },
{ "var": "landRunOnDone", "state": "WALKFWD" }
]
}
]
@ -913,7 +910,7 @@
{
"id": "idleStand",
"interpTarget": 6,
"interpDuration": 6,
"interpDuration": 10,
"transitions": [
{ "var": "isTalking", "state": "idleTalk" }
]
@ -921,7 +918,7 @@
{
"id": "idleTalk",
"interpTarget": 6,
"interpDuration": 6,
"interpDuration": 10,
"transitions": [
{ "var": "notIsTalking", "state": "idleStand" }
]
@ -956,12 +953,12 @@
]
},
{
"id": "walkFwd",
"id": "WALKFWD",
"type": "blendLinearMove",
"data": {
"alpha": 0.0,
"desiredSpeed": 1.4,
"characteristicSpeeds": [0.5, 1.5, 2.5, 3.2, 4.5],
"characteristicSpeeds": [0.5, 1.8, 2.3, 3.2, 4.5],
"alphaVar": "moveForwardAlpha",
"desiredSpeedVar": "moveForwardSpeed"
},
@ -984,7 +981,7 @@
"data": {
"url": "qrc:///avatar/animations/walk_fwd.fbx",
"startFrame": 0.0,
"endFrame": 35.0,
"endFrame": 30.0,
"timeScale": 1.0,
"loopFlag": true
},
@ -1046,25 +1043,25 @@
"data": {
"url": "qrc:///avatar/animations/settle_to_idle.fbx",
"startFrame": 1.0,
"endFrame": 48.0,
"endFrame": 59.0,
"timeScale": 1.0,
"loopFlag": false
},
"children": []
},
{
"id": "walkBwd",
"id": "WALKBWD",
"type": "blendLinearMove",
"data": {
"alpha": 0.0,
"desiredSpeed": 1.4,
"characteristicSpeeds": [0.6, 1.7],
"characteristicSpeeds": [0.6, 1.6, 2.3, 3.1],
"alphaVar": "moveBackwardAlpha",
"desiredSpeedVar": "moveBackwardSpeed"
},
"children": [
{
"id": "walkBwdShort",
"id": "walkBwdShort_c",
"type": "clip",
"data": {
"url": "qrc:///avatar/animations/walk_short_bwd.fbx",
@ -1076,7 +1073,7 @@
"children": []
},
{
"id": "walkBwdNormal",
"id": "walkBwdFast_c",
"type": "clip",
"data": {
"url": "qrc:///avatar/animations/walk_bwd_fast.fbx",
@ -1086,6 +1083,30 @@
"loopFlag": true
},
"children": []
},
{
"id": "jogBwd_c",
"type": "clip",
"data": {
"url": "qrc:///avatar/animations/jog_bwd.fbx",
"startFrame": 0.0,
"endFrame": 24.0,
"timeScale": 1.0,
"loopFlag": true
},
"children": []
},
{
"id": "runBwd_c",
"type": "clip",
"data": {
"url": "qrc:///avatar/animations/run_bwd.fbx",
"startFrame": 0.0,
"endFrame": 16.0,
"timeScale": 1.0,
"loopFlag": true
},
"children": []
}
]
},
@ -1115,18 +1136,18 @@
"children": []
},
{
"id": "strafeLeft",
"id": "STRAFELEFT",
"type": "blendLinearMove",
"data": {
"alpha": 0.0,
"desiredSpeed": 1.4,
"characteristicSpeeds": [0, 0.5, 1.5, 2.6, 3.0],
"characteristicSpeeds": [0.1, 0.5, 1.0, 2.6, 3.0],
"alphaVar": "moveLateralAlpha",
"desiredSpeedVar": "moveLateralSpeed"
},
"children": [
{
"id": "strafeLeftShort_c",
"id": "strafeLeftShortStep_c",
"type": "clip",
"data": {
"url": "qrc:///avatar/animations/side_step_short_left.fbx",
@ -1138,7 +1159,7 @@
"children": []
},
{
"id": "strafeLeft_c",
"id": "strafeLeftStep_c",
"type": "clip",
"data": {
"url": "qrc:///avatar/animations/side_step_left.fbx",
@ -1150,19 +1171,19 @@
"children": []
},
{
"id": "strafeLeftAnim_c",
"id": "strafeLeftWalk_c",
"type": "clip",
"data": {
"url": "qrc:///avatar/animations/walk_left.fbx",
"startFrame": 0.0,
"endFrame": 33.0,
"endFrame": 35.0,
"timeScale": 1.0,
"loopFlag": true
},
"children": []
},
{
"id": "strafeLeftFast_c",
"id": "strafeLeftWalkFast_c",
"type": "clip",
"data": {
"url": "qrc:///avatar/animations/walk_left_fast.fbx",
@ -1188,17 +1209,17 @@
]
},
{
"id": "strafeRight",
"id": "STRAFERIGHT",
"type": "blendLinearMove",
"data": {
"alpha": 0.0,
"desiredSpeed": 1.4,
"characteristicSpeeds": [0, 0.5, 1.5, 2.6, 3.0],
"characteristicSpeeds": [0.1, 0.5, 1.0, 2.6, 3.0],
"alphaVar": "moveLateralAlpha",
"desiredSpeedVar": "moveLateralSpeed"
},
"children": [ {
"id": "stepRightShort_c",
"id": "strafeRightShortStep_c",
"type": "clip",
"data": {
"url": "qrc:///avatar/animations/side_step_short_left.fbx",
@ -1211,7 +1232,7 @@
"children": []
},
{
"id": "stepRight_c",
"id": "strafeRightStep_c",
"type": "clip",
"data": {
"url": "qrc:///avatar/animations/side_step_left.fbx",
@ -1224,12 +1245,12 @@
"children": []
},
{
"id": "strafeRight_c",
"id": "strafeRightWalk_c",
"type": "clip",
"data": {
"url": "qrc:///avatar/animations/walk_left.fbx",
"startFrame": 0.0,
"endFrame": 33.0,
"endFrame": 35.0,
"timeScale": 1.0,
"loopFlag": true,
"mirrorFlag": true
@ -1381,22 +1402,22 @@
"id": "takeoffStand",
"type": "clip",
"data": {
"url": "qrc:///avatar/animations/jump_standing_takeoff.fbx",
"startFrame": 17.0,
"endFrame": 25.0,
"url": "qrc:///avatar/animations/jump_standing_launch.fbx",
"startFrame": 2.0,
"endFrame": 16.0,
"timeScale": 1.0,
"loopFlag": false
},
"children": []
},
{
"id": "takeoffRun",
"id": "TAKEOFFRUN",
"type": "clip",
"data": {
"url": "qrc:///avatar/animations/jump_takeoff.fbx",
"startFrame": 1.0,
"endFrame": 2.5,
"timeScale": 0.01,
"url": "qrc:///avatar/animations/jump_running_launch_land.fbx",
"startFrame": 4.0,
"endFrame": 15.0,
"timeScale": 1.0,
"loopFlag": false
},
"children": []
@ -1416,7 +1437,7 @@
"url": "qrc:///avatar/animations/jump_standing_apex.fbx",
"startFrame": 0.0,
"endFrame": 0.0,
"timeScale": 0.0,
"timeScale": 1.0,
"loopFlag": false
},
"children": []
@ -1448,7 +1469,7 @@
]
},
{
"id": "inAirRun",
"id": "INAIRRUN",
"type": "blendLinear",
"data": {
"alpha": 0.0,
@ -1459,10 +1480,10 @@
"id": "inAirRunPreApex",
"type": "clip",
"data": {
"url": "qrc:///avatar/animations/jump_in_air.fbx",
"startFrame": 0.0,
"endFrame": 0.0,
"timeScale": 0.0,
"url": "qrc:///avatar/animations/jump_running_launch_land.fbx",
"startFrame": 16.0,
"endFrame": 16.0,
"timeScale": 1.0,
"loopFlag": false
},
"children": []
@ -1471,9 +1492,9 @@
"id": "inAirRunApex",
"type": "clip",
"data": {
"url": "qrc:///avatar/animations/jump_in_air.fbx",
"startFrame": 6.0,
"endFrame": 6.0,
"url": "qrc:///avatar/animations/jump_running_launch_land.fbx",
"startFrame": 22.0,
"endFrame": 22.0,
"timeScale": 1.0,
"loopFlag": false
},
@ -1483,9 +1504,9 @@
"id": "inAirRunPostApex",
"type": "clip",
"data": {
"url": "qrc:///avatar/animations/jump_in_air.fbx",
"startFrame": 11.0,
"endFrame": 11.0,
"url": "qrc:///avatar/animations/jump_running_launch_land.fbx",
"startFrame": 33.0,
"endFrame": 33.0,
"timeScale": 1.0,
"loopFlag": false
},
@ -1497,7 +1518,7 @@
"id": "landStandImpact",
"type": "clip",
"data": {
"url": "qrc:///avatar/animations/jump_standing_land.fbx",
"url": "qrc:///avatar/animations/jump_standing_land_settle.fbx",
"startFrame": 1.0,
"endFrame": 6.0,
"timeScale": 1.0,
@ -1509,22 +1530,22 @@
"id": "landStand",
"type": "clip",
"data": {
"url": "qrc:///avatar/animations/jump_standing_land.fbx",
"url": "qrc:///avatar/animations/jump_standing_land_settle.fbx",
"startFrame": 6.0,
"endFrame": 28.0,
"endFrame": 68.0,
"timeScale": 1.0,
"loopFlag": false
},
"children": []
},
{
"id": "landRun",
"id": "LANDRUN",
"type": "clip",
"data": {
"url": "qrc:///avatar/animations/jump_land.fbx",
"startFrame": 1.0,
"endFrame": 6.0,
"timeScale": 0.65,
"url": "qrc:///avatar/animations/jump_running_launch_land.fbx",
"startFrame": 29.0,
"endFrame": 40.0,
"timeScale": 1.0,
"loopFlag": false
},
"children": []

View file

@ -0,0 +1,83 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Generator: Adobe Illustrator 19.2.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
version="1.1"
x="0px"
y="0px"
viewBox="0 0 50 50"
style="enable-background:new 0 0 50 50;"
xml:space="preserve"
id="svg2"
inkscape:version="0.91 r13725"
sodipodi:docname="people-a.svg"><metadata
id="metadata24"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
id="defs22" /><sodipodi:namedview
pagecolor="#ff0000"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="852"
inkscape:window-height="480"
id="namedview20"
showgrid="false"
inkscape:zoom="4.72"
inkscape:cx="25"
inkscape:cy="25"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="0"
inkscape:current-layer="svg2" /><style
type="text/css"
id="style4">
.st0{fill:#FFFFFF;}
.st1{fill:#EF3B4E;}
</style>
<circle class="st1" cx="44.1" cy="6" r="5.6"/>
<g
id="Layer_2" /><g
id="Layer_1"
style="fill:#000000;fill-opacity:1"><circle
class="st0"
cx="25.6"
cy="14.8"
r="8.1"
id="circle8"
style="fill:#000000;fill-opacity:1" /><path
class="st0"
d="M31.4,27h-11c-4.6,0-8.2,3.9-8.2,8.5v4.3c3.8,2.8,8.1,4.5,13.1,4.5c5.6,0,10.6-2.1,14.4-5.6v-3.2 C39.6,30.9,35.9,27,31.4,27z"
id="path10"
style="fill:#000000;fill-opacity:1" /><circle
class="st0"
cx="41.6"
cy="17.1"
r="3.5"
id="circle12"
style="fill:#000000;fill-opacity:1" /><path
class="st0"
d="M43.9,23.9h-4.1c-0.9,0-1.7,0.4-2.3,1c1.2,0.6,2.3,1.6,3.1,2.5c1,1.2,1.5,2.7,1.7,4.3c0.3,0.9,0.4,1.8,0.2,2.8 v0.6c1.6-2.2,3.3-6,4-8.1v-0.3C46.5,25.7,45.3,23.9,43.9,23.9z"
id="path14"
style="fill:#000000;fill-opacity:1" /><circle
class="st0"
cx="9.4"
cy="18"
r="3.5"
id="circle16"
style="fill:#000000;fill-opacity:1" /><path
class="st0"
d="M8.5,35.7c-0.1-0.7-0.1-1.4,0-2.1l0.1-0.2c0-0.2,0-0.4,0-0.6c0-0.2,0-0.3,0.1-0.5c0-0.2,0-0.3,0-0.5 c0.2-2.1,1.6-4.4,3.2-5.7c0.4-0.4,0.9-0.6,1.4-0.9c-0.5-0.5-1.3-0.7-2-0.7H7c-1.4,0-2.6,1.8-2.4,2.7v0.3 C5.1,29.8,6.8,33.5,8.5,35.7z"
id="path18"
style="fill:#000000;fill-opacity:1" /></g></svg>

After

(image error) Size: 2.9 KiB

View file

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 19.2.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 50 50" style="enable-background:new 0 0 50 50;" xml:space="preserve">
<style type="text/css">
.st0{fill:#FFFFFF;}
.st1{fill:#EF3B4E;}
</style>
<circle class="st1" cx="44.1" cy="6" r="5.6"/>
<g id="Layer_2">
</g>
<g id="Layer_1">
<circle class="st0" cx="25.6" cy="14.8" r="8.1"/>
<path class="st0" d="M31.4,27h-11c-4.6,0-8.2,3.9-8.2,8.5v4.3c3.8,2.8,8.1,4.5,13.1,4.5c5.6,0,10.6-2.1,14.4-5.6v-3.2
C39.6,30.9,35.9,27,31.4,27z"/>
<circle class="st0" cx="41.6" cy="17.1" r="3.5"/>
<path class="st0" d="M43.9,23.9h-4.1c-0.9,0-1.7,0.4-2.3,1c1.2,0.6,2.3,1.6,3.1,2.5c1,1.2,1.5,2.7,1.7,4.3c0.3,0.9,0.4,1.8,0.2,2.8
v0.6c1.6-2.2,3.3-6,4-8.1v-0.3C46.5,25.7,45.3,23.9,43.9,23.9z"/>
<circle class="st0" cx="9.4" cy="18" r="3.5"/>
<path class="st0" d="M8.5,35.7c-0.1-0.7-0.1-1.4,0-2.1l0.1-0.2c0-0.2,0-0.4,0-0.6c0-0.2,0-0.3,0.1-0.5c0-0.2,0-0.3,0-0.5
c0.2-2.1,1.6-4.4,3.2-5.7c0.4-0.4,0.9-0.6,1.4-0.9c-0.5-0.5-1.3-0.7-2-0.7H7c-1.4,0-2.6,1.8-2.4,2.7v0.3
C5.1,29.8,6.8,33.5,8.5,35.7z"/>
</g>
</svg>

After

(image error) Size: 1.2 KiB

View file

@ -0,0 +1,5 @@
<svg width="31" height="23" viewBox="0 0 31 23" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M3.59534 11.0156C6.16042 13.4128 9.65987 15.5898 13.6042 16.1774C17.686 16.7856 22.4164 15.7196 27.3057 11.0659C22.0721 6.07309 17.0642 5.14115 12.9153 5.90073C8.99427 6.61859 5.69298 8.87688 3.59534 11.0156ZM12.455 3.27591C17.7727 2.30235 23.9836 3.74895 30.1053 10.1333L31 11.0664L30.1053 11.9994C24.3636 17.9875 18.4774 19.5983 13.2276 18.8161C8.06048 18.0463 3.70384 14.9892 0.837069 11.9994L0 11.1265L0.778477 10.1986C3.05338 7.48717 7.2318 4.23217 12.455 3.27591Z" fill="#3D3D3D"/>
<ellipse cx="15.6539" cy="10.9218" rx="3.65386" ry="3.81061" fill="#3D3D3D"/>
<line x1="25" y1="2.12132" x2="7.12132" y2="20" stroke="#3D3D3D" stroke-width="3" stroke-linecap="round"/>
</svg>

After

(image error) Size: 825 B

View file

@ -0,0 +1,4 @@
<svg width="31" height="16" viewBox="0 0 31 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M3.59534 8.01564C6.16042 10.4128 9.65987 12.5898 13.6042 13.1774C17.686 13.7856 22.4164 12.7196 27.3057 8.06585C22.0721 3.07309 17.0642 2.14115 12.9153 2.90073C8.99427 3.61859 5.69298 5.87688 3.59534 8.01564ZM12.455 0.275915C17.7727 -0.697651 23.9836 0.748949 30.1053 7.13329L31 8.06636L30.1053 8.99944C24.3636 14.9875 18.4774 16.5983 13.2276 15.8161C8.06048 15.0463 3.70384 11.9892 0.837069 8.99944L0 8.12646L0.778477 7.1986C3.05338 4.48717 7.2318 1.23217 12.455 0.275915Z" fill="#3D3D3D"/>
<ellipse cx="15.644" cy="7.92179" rx="3.65386" ry="3.81061" fill="#3D3D3D"/>
</svg>

After

(image error) Size: 721 B

Binary file not shown.

After

(image error) Size: 9 KiB

Binary file not shown.

After

(image error) Size: 2.7 KiB

Binary file not shown.

After

(image error) Size: 20 KiB

Binary file not shown.

After

(image error) Size: 4.9 KiB

Binary file not shown.

After

(image error) Size: 4.8 KiB

Binary file not shown.

After

(image error) Size: 4.4 KiB

Binary file not shown.

After

(image error) Size: 5 KiB

View file

@ -23,6 +23,7 @@ ModalWindow {
objectName: "LoginDialog"
implicitWidth: 520
implicitHeight: 320
closeButtonVisible: true
destroyOnCloseButton: true
destroyOnHidden: true
visible: true

View file

@ -117,27 +117,27 @@ Item {
}
spacing: hifi.dimensions.contentSpacing.y / 2
TextField {
id: usernameField
anchors {
horizontalCenter: parent.horizontalCenter
}
width: 1080
placeholderText: qsTr("Username or Email")
TextField {
id: usernameField
anchors {
horizontalCenter: parent.horizontalCenter
}
width: 1080
placeholderText: qsTr("Username or Email")
}
TextField {
id: passwordField
anchors {
horizontalCenter: parent.horizontalCenter
}
width: 1080
placeholderText: qsTr("Password")
echoMode: TextInput.Password
Keys.onReturnPressed: linkAccountBody.login()
TextField {
id: passwordField
anchors {
horizontalCenter: parent.horizontalCenter
}
width: 1080
placeholderText: qsTr("Password")
echoMode: TextInput.Password
Keys.onReturnPressed: linkAccountBody.login()
}
}
InfoItem {
@ -176,7 +176,7 @@ Item {
anchors {
left: parent.left
top: form.bottom
topMargin: hifi.dimensions.contentSpacing.y / 2
topMargin: hifi.dimensions.contentSpacing.y / 2
}
spacing: hifi.dimensions.contentSpacing.x
@ -201,7 +201,7 @@ Item {
anchors {
right: parent.right
top: form.bottom
topMargin: hifi.dimensions.contentSpacing.y / 2
topMargin: hifi.dimensions.contentSpacing.y / 2
}
spacing: hifi.dimensions.contentSpacing.x
onHeightChanged: d.resize(); onWidthChanged: d.resize();

View file

@ -15,7 +15,6 @@ import QtQuick.Controls.Styles 1.4 as OriginalStyles
import "../controls-uit"
import "../styles-uit"
Item {
id: linkAccountBody
clip: true
@ -87,6 +86,23 @@ Item {
height: 48
}
ShortcutText {
id: flavorText
anchors {
top: parent.top
left: parent.left
margins: 0
topMargin: hifi.dimensions.contentSpacing.y
}
text: qsTr("Sign in to High Fidelity to make friends, get HFC, and buy interesting things on the Marketplace!")
width: parent.width
wrapMode: Text.WordWrap
lineHeight: 1
lineHeightMode: Text.ProportionalHeight
horizontalAlignment: Text.AlignHCenter
}
ShortcutText {
id: mainTextContainer
anchors {
@ -97,7 +113,6 @@ Item {
}
visible: false
text: qsTr("Username or password incorrect.")
wrapMode: Text.WordWrap
color: hifi.colors.redAccent
@ -117,22 +132,21 @@ Item {
}
spacing: 2 * hifi.dimensions.contentSpacing.y
TextField {
id: usernameField
text: Settings.getValue("wallet/savedUsername", "");
width: parent.width
focus: true
label: "Username or Email"
placeholderText: "Username or Email"
activeFocusOnPress: true
ShortcutText {
z: 10
y: usernameField.height
anchors {
left: usernameField.left
top: usernameField.top
leftMargin: usernameField.textFieldLabel.contentWidth + 10
topMargin: -19
right: usernameField.right
top: usernameField.bottom
topMargin: 4
}
text: "<a href='https://highfidelity.com/users/password/new'>Forgot Username?</a>"
@ -143,26 +157,32 @@ Item {
onLinkActivated: loginDialog.openUrl(link)
}
onFocusChanged: {
root.text = "";
}
Component.onCompleted: {
var savedUsername = Settings.getValue("wallet/savedUsername", "");
usernameField.text = savedUsername === "Unknown user" ? "" : savedUsername;
}
}
TextField {
id: passwordField
width: parent.width
label: "Password"
echoMode: showPassword.checked ? TextInput.Normal : TextInput.Password
placeholderText: "Password"
activeFocusOnPress: true
echoMode: TextInput.Password
onHeightChanged: d.resize(); onWidthChanged: d.resize();
ShortcutText {
id: forgotPasswordShortcut
y: passwordField.height
z: 10
anchors {
left: passwordField.left
top: passwordField.top
leftMargin: passwordField.textFieldLabel.contentWidth + 10
topMargin: -19
right: passwordField.right
top: passwordField.bottom
topMargin: 4
}
text: "<a href='https://highfidelity.com/users/password/new'>Forgot Password?</a>"
@ -179,12 +199,45 @@ Item {
root.isPassword = true;
}
Keys.onReturnPressed: linkAccountBody.login()
}
Rectangle {
id: showPasswordHitbox
z: 10
x: passwordField.width - ((passwordField.height) * 31 / 23)
width: parent.width - (parent.width - (parent.height * 31/16))
height: parent.height
anchors {
right: parent.right
}
color: "transparent"
CheckBox {
id: showPassword
text: "Show password"
Image {
id: showPasswordImage
y: (passwordField.height - (passwordField.height * 16 / 23)) / 2
width: passwordField.width - (passwordField.width - (((passwordField.height) * 31/23)))
height: passwordField.height * 16 / 23
anchors {
right: parent.right
rightMargin: 3
}
source: "../../images/eyeOpen.svg"
}
MouseArea {
id: passwordFieldMouseArea
anchors.fill: parent
acceptedButtons: Qt.LeftButton
property bool showPassword: false
onClicked: {
showPassword = !showPassword;
passwordField.echoMode = showPassword ? TextInput.Normal : TextInput.Password;
showPasswordImage.source = showPassword ? "../../images/eyeClosed.svg" : "../../images/eyeOpen.svg";
showPasswordImage.height = showPassword ? passwordField.height : passwordField.height * 16 / 23;
showPasswordImage.y = showPassword ? 0 : (passwordField.height - showPasswordImage.height) / 2;
}
}
}
Keys.onReturnPressed: linkAccountBody.login()
}
InfoItem {
@ -206,6 +259,26 @@ Item {
onHeightChanged: d.resize(); onWidthChanged: d.resize();
anchors.horizontalCenter: parent.horizontalCenter
CheckBox {
id: autoLogoutCheckbox
checked: !Settings.getValue("wallet/autoLogout", true)
text: "Keep me signed in"
boxSize: 20;
labelFontSize: 15
color: hifi.colors.black
onCheckedChanged: {
Settings.setValue("wallet/autoLogout", !checked);
if (checked) {
Settings.setValue("wallet/savedUsername", Account.username);
} else {
Settings.setValue("wallet/savedUsername", "");
}
}
Component.onDestruction: {
Settings.setValue("wallet/autoLogout", !checked);
}
}
Button {
id: linkAccountButton
anchors.verticalCenter: parent.verticalCenter
@ -216,12 +289,6 @@ Item {
onClicked: linkAccountBody.login()
}
Button {
anchors.verticalCenter: parent.verticalCenter
text: qsTr("Cancel")
onClicked: root.tryDestroy()
}
}
Row {
@ -234,7 +301,7 @@ Item {
RalewaySemiBold {
size: hifi.fontSizes.inputLabel
anchors.verticalCenter: parent.verticalCenter
text: qsTr("Don't have an account?")
text: qsTr("New to High Fidelity?")
}
Button {
@ -279,7 +346,15 @@ Item {
target: loginDialog
onHandleLoginCompleted: {
console.log("Login Succeeded, linking steam account")
var poppedUp = Settings.getValue("loginDialogPoppedUp", false);
if (poppedUp) {
console.log("[ENCOURAGELOGINDIALOG]: logging in")
var data = {
"action": "user logged in"
};
UserActivityLogger.logAction("encourageLoginDialog", data);
Settings.setValue("loginDialogPoppedUp", false);
}
if (loginDialog.isSteamRunning()) {
loginDialog.linkSteam()
} else {
@ -290,6 +365,15 @@ Item {
}
onHandleLoginFailed: {
console.log("Login Failed")
var poppedUp = Settings.getValue("loginDialogPoppedUp", false);
if (poppedUp) {
console.log("[ENCOURAGELOGINDIALOG]: failed logging in")
var data = {
"action": "user failed logging in"
};
UserActivityLogger.logAction("encourageLoginDialog", data);
Settings.setValue("loginDialogPoppedUp", false);
}
mainTextContainer.visible = true
toggleLoading(false)
}

View file

@ -46,7 +46,7 @@ Rectangle {
}
}
property var jointNames;
property var jointNames: []
property var currentAvatarSettings;
function fetchAvatarModelName(marketId, avatar) {
@ -175,7 +175,14 @@ Rectangle {
displayNameInput.text = getAvatarsData.displayName;
currentAvatarSettings = getAvatarsData.currentAvatarSettings;
updateCurrentAvatarInBookmarks(currentAvatar);
var bookmarkAvatarIndex = allAvatars.findAvatarIndexByValue(currentAvatar);
if (bookmarkAvatarIndex === -1) {
currentAvatar.name = '';
} else {
currentAvatar.name = allAvatars.get(bookmarkAvatarIndex).name;
allAvatars.move(bookmarkAvatarIndex, 0, 1);
}
view.setPage(0);
} else if (message.method === 'updateAvatarInBookmarks') {
updateCurrentAvatarInBookmarks(currentAvatar);
} else if (message.method === 'selectAvatarEntity') {

View file

@ -271,6 +271,8 @@ Rectangle {
connectionsUserModel.getFirstPage();
}
activeTab = "connectionsTab";
connectionsOnlineDot.visible = false;
pal.sendToScript({method: 'hideNotificationDot'});
connectionsHelpText.color = hifi.colors.blueAccent;
}
}
@ -298,6 +300,16 @@ Rectangle {
}
}
}
Rectangle {
id: connectionsOnlineDot;
visible: false;
width: 10;
height: width;
radius: width;
color: "#EF3B4E"
anchors.left: parent.left;
anchors.verticalCenter: parent.verticalCenter;
}
// "CONNECTIONS" text
RalewaySemiBold {
id: connectionsTabSelectorText;
@ -305,7 +317,11 @@ Rectangle {
// Text size
size: hifi.fontSizes.tabularData;
// Anchors
anchors.fill: parent;
anchors.left: connectionsOnlineDot.visible ? connectionsOnlineDot.right : parent.left;
anchors.leftMargin: connectionsOnlineDot.visible ? 4 : 0;
anchors.top: parent.top;
anchors.bottom: parent.bottom;
anchors.right: parent.right;
// Style
font.capitalization: Font.AllUppercase;
color: activeTab === "connectionsTab" ? hifi.colors.blueAccent : hifi.colors.baseGray;
@ -326,7 +342,7 @@ Rectangle {
anchors.left: connectionsTabSelectorTextContainer.left;
anchors.top: connectionsTabSelectorTextContainer.top;
anchors.topMargin: 1;
anchors.leftMargin: connectionsTabSelectorTextMetrics.width + 42;
anchors.leftMargin: connectionsTabSelectorTextMetrics.width + 42 + connectionsOnlineDot.width + connectionsTabSelectorText.anchors.leftMargin;
RalewayRegular {
id: connectionsHelpText;
text: "[?]";
@ -1267,6 +1283,9 @@ Rectangle {
case 'http.response':
http.handleHttpResponse(message);
break;
case 'changeConnectionsDotStatus':
connectionsOnlineDot.visible = message.shouldShowDot;
break;
default:
console.log('Unrecognized message:', JSON.stringify(message));
}

View file

@ -26,7 +26,7 @@ Rectangle {
HifiConstants { id: hifi; }
property var eventBridge;
property string title: "Audio Settings - " + AudioScriptingInterface.context;
property string title: "Audio Settings"
signal sendToScript(var message);
color: hifi.colors.baseGray;

View file

@ -25,7 +25,17 @@ Rectangle {
modified = false;
}
property var jointNames;
property var jointNames: []
onJointNamesChanged: {
jointsModel.clear();
for (var i = 0; i < jointNames.length; ++i) {
var jointName = jointNames[i];
if (jointName !== 'LeftHand' && jointName !== 'RightHand') {
jointsModel.append({'text' : jointName, 'jointIndex' : i});
}
}
}
property string avatarName: ''
property var wearablesModel;
@ -268,20 +278,30 @@ Rectangle {
anchors.right: parent.right
enabled: getCurrentWearable() !== null && !isSoft.checked
comboBox.displayText: isSoft.checked ? 'Hips' : comboBox.currentText
comboBox.textRole: "text"
model: jointNames
model: ListModel {
id: jointsModel
}
property bool notify: false
function set(jointIndex) {
notify = false;
currentIndex = jointIndex;
for (var i = 0; i < jointsModel.count; ++i) {
if (jointsModel.get(i).jointIndex === jointIndex) {
currentIndex = i;
break;
}
}
notify = true;
}
function notifyJointChanged() {
modified = true;
var jointIndex = jointsModel.get(jointsCombobox.currentIndex).jointIndex;
var properties = {
parentJointIndex: currentIndex,
parentJointIndex: jointIndex,
localPosition: {
x: positionVector.xvalue,
y: positionVector.yvalue,
@ -294,7 +314,7 @@ Rectangle {
}
};
wearableUpdated(getCurrentWearable().id, wearablesCombobox.currentIndex, properties);
wearableUpdated(getCurrentWearable().id, jointIndex, properties);
}
onCurrentIndexChanged: {

View file

@ -408,9 +408,7 @@ Rectangle {
Connections {
onSendSignalToWallet: {
if (msg.method === 'walletReset' || msg.method === 'passphraseReset') {
sendToScript(msg);
} else if (msg.method === 'walletSecurity_changeSecurityImage') {
if (msg.method === 'walletSecurity_changeSecurityImage') {
securityImageChange.initModel();
root.activeView = "securityImageChange";
}

View file

@ -18,6 +18,7 @@ import "../../windows"
Rectangle {
id: root
objectName: "DCConectionTiming"
property string title: "Domain Connection Timing"
signal sendToScript(var message);
property bool isHMD: false
@ -33,7 +34,7 @@ Rectangle {
Row {
id: header
anchors.top: parent.top
anchors.topMargin: hifi.dimensions.tabletMenuHeader
anchors.topMargin: hifi.dimensions.contentMargin.y
anchors.leftMargin: 5
anchors.rightMargin: 5
anchors.left: parent.left

View file

@ -18,6 +18,7 @@ import "../../windows"
Rectangle {
id: root
objectName: "EntityStatistics"
property string title: "Entity Statistics"
signal sendToScript(var message);
property bool isHMD: false
@ -40,6 +41,7 @@ Rectangle {
id: scrollView
width: parent.width
anchors.top: parent.top
anchors.topMargin: hifi.dimensions.contentMargin.y
anchors.bottom: parent.bottom
anchors.bottomMargin: hifi.dimensions.tabletMenuHeader
contentWidth: column.implicitWidth
@ -48,10 +50,15 @@ Rectangle {
Column {
id: column
anchors.margins: 10
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
y: hifi.dimensions.tabletMenuHeader //-bgNavBar
anchors {
topMargin: 0
leftMargin: 10
rightMargin: 10
bottomMargin: 0
}
spacing: 20
TabletEntityStatisticsItem {

View file

@ -24,6 +24,8 @@ Item {
height: parent.height
width: parent.width
property string title: "Controls"
HifiConstants { id: hifi }
TabBar {

View file

@ -79,7 +79,7 @@ StackView {
return;
}
location.text = targetString;
toggleOrGo(true, targetString);
toggleOrGo(targetString, true);
clearAddressLineTimer.start();
}
@ -105,7 +105,6 @@ StackView {
propagateComposedEvents: true
onPressed: {
parent.forceActiveFocus();
addressBarDialog.keyboardEnabled = false;
mouse.accepted = false;
}
}
@ -223,7 +222,6 @@ StackView {
updateLocationText(text.length > 0);
}
onAccepted: {
addressBarDialog.keyboardEnabled = false;
toggleOrGo();
}
@ -378,7 +376,7 @@ StackView {
HifiControls.Keyboard {
id: keyboard
raised: parent.keyboardEnabled
raised: parent.keyboardEnabled && parent.keyboardRaised
numeric: parent.punctuationMode
anchors {
bottom: parent.bottom
@ -401,7 +399,7 @@ StackView {
}
}
function toggleOrGo(fromSuggestions, address) {
function toggleOrGo(address, fromSuggestions) {
if (address !== undefined && address !== "") {
addressBarDialog.loadAddress(address, fromSuggestions);
clearAddressLineTimer.start();

View file

@ -23,6 +23,8 @@ FocusScope {
property string subMenu: ""
signal sendToScript(var message);
HifiConstants { id: hifi }
Rectangle {
id: bgNavBar
height: 90
@ -45,24 +47,22 @@ FocusScope {
anchors.topMargin: 0
anchors.top: parent.top
Image {
HiFiGlyphs {
id: menuRootIcon
width: 40
height: 40
source: "../../../icons/tablet-icons/menu-i.svg"
text: breadcrumbText.text !== "Menu" ? hifi.glyphs.backward : ""
size: 72
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left
anchors.leftMargin: 15
width: breadcrumbText.text === "Menu" ? 32 : 50
visible: breadcrumbText.text !== "Menu"
MouseArea {
anchors.fill: parent
hoverEnabled: true
onEntered: iconColorOverlay.color = "#1fc6a6";
onExited: iconColorOverlay.color = "#34a2c7";
// navigate back to root level menu
onClicked: {
buildMenu();
breadcrumbText.text = "Menu";
menuPopperUpper.closeLastMenu();
tabletRoot.playButtonClickSound();
}
}
@ -79,23 +79,10 @@ FocusScope {
id: breadcrumbText
text: "Menu"
size: 26
color: "#34a2c7"
color: "#e3e3e3"
anchors.verticalCenter: parent.verticalCenter
anchors.left: menuRootIcon.right
anchors.leftMargin: 15
MouseArea {
anchors.fill: parent
hoverEnabled: true
onEntered: breadcrumbText.color = "#1fc6a6";
onExited: breadcrumbText.color = "#34a2c7";
// navigate back to parent level menu if there is one
onClicked: {
if (breadcrumbText.text !== "Menu") {
menuPopperUpper.closeLastMenu();
}
tabletRoot.playButtonClickSound();
}
}
}
}
@ -103,7 +90,6 @@ FocusScope {
menuPopperUpper.closeLastMenu();
}
function setRootMenu(rootMenu, subMenu) {
tabletMenu.subMenu = subMenu;
tabletMenu.rootMenu = rootMenu;

View file

@ -1,5 +1,5 @@
//
// MessageDialog.qml
// TabletMenuStack.qml
//
// Created by Dante Ruiz on 13 Feb 2017
// Copyright 2016 High Fidelity, Inc.
@ -66,7 +66,7 @@ Item {
function popSource() {
console.log("trying to pop page");
d.pop();
closeLastMenu();
}
function toModel(items, newMenu) {

View file

@ -41,7 +41,15 @@ Item {
section.saveAll();
}
closeDialog();
if (HMD.active) {
if (gotoPreviousApp) {
tablet.returnToPreviousApp();
} else {
tablet.popFromStack();
}
} else {
closeDialog();
}
}
function restoreAll() {
@ -50,7 +58,15 @@ Item {
section.restoreAll();
}
closeDialog();
if (HMD.active) {
if (gotoPreviousApp) {
tablet.returnToPreviousApp();
} else {
tablet.popFromStack();
}
} else {
closeDialog();
}
}
function closeDialog() {

View file

@ -94,5 +94,25 @@ Frame {
color: hifi.colors.lightGray
}
}
GlyphButton {
id: closeButton
visible: window.closeButtonVisible
width: 30
y: -hifi.dimensions.modalDialogTitleHeight
anchors {
top: parent.top
right: parent.right
topMargin: 10
rightMargin: 10
}
glyph: hifi.glyphs.close
size: 23
onClicked: {
window.clickedCloseButton = true;
window.destroy();
}
}
}
}

View file

@ -19,6 +19,9 @@ ScrollingWindow {
destroyOnHidden: true
frame: ModalFrame { }
property bool closeButtonVisible: false
// only applicable for if close button is visible.
property bool clickedCloseButton: false
property int colorScheme: hifi.colorSchemes.light
property bool draggable: false

View file

@ -0,0 +1,934 @@
{
"DataVersion": 0,
"Paths":
{
"/": "/4,1.4,4/0,0.49544,0,0.868645"
},
"Entities": [
{
"clientOnly": false,
"collidesWith": "static,dynamic,kinematic,otherAvatar,",
"collisionMask": 23,
"created": "2018-09-05T18:13:00Z",
"dimensions": {
"blue": 1.159199833869934,
"green": 2.8062009811401367,
"red": 1.6216505765914917,
"x": 1.6216505765914917,
"y": 2.8062009811401367,
"z": 1.159199833869934
},
"id": "{d0ed60b8-9174-4c56-8e78-2c5399329ae0}",
"lastEdited": 1536171372916208,
"lastEditedBy": "{151cb20e-715a-4c80-aa0d-5b58b1c8a0c9}",
"locked": true,
"name": "Try Again Zone",
"owningAvatarID": "{00000000-0000-0000-0000-000000000000}",
"position": {
"blue":4.015342712402344,
"green":1.649999976158142,
"red":2.00921893119812,
"x":2.00921893119812,
"y":1.649999976158142,
"z":4.015342712402344
},
"queryAACube": {
"scale": 3.4421300888061523,
"x": 1.6001315116882324,
"y": -0.07100248336791992,
"z": 0.14220571517944336
},
"rotation": {
"w": 0.9914448857307434,
"x": 0,
"y": -0.13052619993686676,
"z": 0
},
"script": "https://hifi-content.s3.amazonaws.com/wayne/404redirectionScripts/zoneTryAgainEntityScript.js",
"shapeType": "box",
"type": "Zone",
"userData": "{\"grabbableKey\":{\"grabbable\":false}}"
},
{
"clientOnly": false,
"color": {
"blue": 0,
"green": 0,
"red": 255
},
"created": "2018-09-05T00:40:03Z",
"dimensions": {
"blue": 8.645400047302246,
"green": 0.20000000298023224,
"red": 20.025121688842773,
"x": 20.025121688842773,
"y": 0.20000000298023224,
"z": 8.645400047302246
},
"id": "{e44fb546-b34a-4966-9b11-73556f800d21}",
"lastEdited": 1536107948776951,
"lastEditedBy": "{ce82d352-3002-44ae-9b76-66492989a1db}",
"locked": true,
"name": "ceiling",
"owningAvatarID": "{00000000-0000-0000-0000-000000000000}",
"position": {
"blue": 4.846520900726318,
"green": 2.912982940673828,
"red": 5.739595890045166,
"x": 5.739595890045166,
"y": 2.912982940673828,
"z": 4.846520900726318
},
"queryAACube": {
"scale": 21.812576293945312,
"x": -5.16669225692749,
"y": -7.993305206298828,
"z": -6.059767246246338
},
"rotation": {
"w": 0.970295786857605,
"x": 0,
"y": -0.24192190170288086,
"z": 0
},
"shape": "Cube",
"type": "Box",
"userData": "{\"grabbableKey\":{\"grabbable\":false}}",
"visible": false
},
{
"clientOnly": false,
"color": {
"blue": 0,
"green": 0,
"red": 0
},
"created": "2018-09-05T00:40:03Z",
"dimensions": {
"blue": 6.9401350021362305,
"green": 0.04553089290857315,
"red": 7.004304885864258,
"x": 7.004304885864258,
"y": 0.04553089290857315,
"z": 6.9401350021362305
},
"id": "{8cd93fe5-16c0-44b7-b1e9-e7e06c4e9228}",
"lastEdited": 1536107948774796,
"lastEditedBy": "{4eecd88f-ef9b-4a83-bb9a-7f7496209c6b}",
"locked": true,
"name": "floor",
"owningAvatarID": "{00000000-0000-0000-0000-000000000000}",
"position": {
"blue": 3.6175529956817627,
"green": 0,
"red": 4.102385997772217,
"x": 4.102385997772217,
"y": 0,
"z": 3.6175529956817627
},
"queryAACube": {
"scale": 9.860417366027832,
"x": -0.8278226852416992,
"y": -4.930208683013916,
"z": -1.3126556873321533
},
"rotation": {
"w": 0.8660253882408142,
"x": -1.5922749298624694e-05,
"y": 0.5,
"z": -4.572480611386709e-05
},
"shape": "Cube",
"type": "Box",
"userData": "{\"grabbableKey\":{\"grabbable\":false}}"
},
{
"clientOnly": false,
"color": {
"blue": 0,
"green": 0,
"red": 0
},
"created": "2018-09-05T00:40:03Z",
"dimensions": {
"blue": 11.117486953735352,
"green": 3.580313205718994,
"red": 0.20000000298023224,
"x": 0.20000000298023224,
"y": 3.580313205718994,
"z": 11.117486953735352
},
"id": "{147272dc-a344-4171-9621-efc1c2095997}",
"lastEdited": 1536107948776823,
"lastEditedBy": "{ce82d352-3002-44ae-9b76-66492989a1db}",
"locked": true,
"name": "leftWall",
"owningAvatarID": "{00000000-0000-0000-0000-000000000000}",
"position": {
"blue": 6.1806135177612305,
"green": 1.0066027641296387,
"red": 1.4690406322479248,
"x": 1.4690406322479248,
"y": 1.0066027641296387,
"z": 6.1806135177612305
},
"queryAACube": {
"scale": 11.681488037109375,
"x": -4.371703147888184,
"y": -4.834141254425049,
"z": 0.33986949920654297
},
"rotation": {
"w": 0.8637980222702026,
"x": -4.57763671875e-05,
"y": 0.5038070678710938,
"z": -1.52587890625e-05
},
"shape": "Cube",
"type": "Box",
"userData": "{\"grabbableKey\":{\"grabbable\":false}}",
"visible": false
},
{
"clientOnly": false,
"color": {
"blue": 0,
"green": 0,
"red": 0
},
"created": "2018-09-05T00:40:03Z",
"dimensions": {
"blue": 11.117486953735352,
"green": 3.580313205718994,
"red": 0.20000000298023224,
"x": 0.20000000298023224,
"y": 3.580313205718994,
"z": 11.117486953735352
},
"id": "{5f2b89b8-47e3-4915-a966-d46307a40f06}",
"lastEdited": 1536107948774605,
"lastEditedBy": "{ce82d352-3002-44ae-9b76-66492989a1db}",
"locked": true,
"name": "backWall",
"owningAvatarID": "{00000000-0000-0000-0000-000000000000}",
"position": {
"blue": 5.268576622009277,
"green": 1.0066027641296387,
"red": 6.093774318695068,
"x": 6.093774318695068,
"y": 1.0066027641296387,
"z": 5.268576622009277
},
"queryAACube": {
"scale": 11.681488037109375,
"x": 0.25303030014038086,
"y": -4.834141254425049,
"z": -0.5721673965454102
},
"rotation": {
"w": 0.9662165641784668,
"x": -4.57763671875e-05,
"y": -0.2576791048049927,
"z": 1.52587890625e-05
},
"shape": "Cube",
"type": "Box",
"userData": "{\"grabbableKey\":{\"grabbable\":false}}",
"visible": false
},
{
"clientOnly": false,
"created": "2018-09-05T00:40:03Z",
"dimensions": {
"blue": 14.40000057220459,
"green": 14.40000057220459,
"red": 14.40000057220459,
"x": 14.40000057220459,
"y": 14.40000057220459,
"z": 14.40000057220459
},
"id": "{baf96345-8f68-4068-af4c-3c690035852a}",
"lastEdited": 1536107948775591,
"lastEditedBy": "{b5bba536-25e5-4b12-a1be-5c7cd196a06a}",
"locked": true,
"owningAvatarID": "{00000000-0000-0000-0000-000000000000}",
"position": {
"blue": 2.3440732955932617,
"green": 1.6162219047546387,
"red": 1.8748211860656738,
"x": 1.8748211860656738,
"y": 1.6162219047546387,
"z": 2.3440732955932617
},
"queryAACube": {
"scale": 24.9415340423584,
"x": -10.595945358276367,
"y": -10.854545593261719,
"z": -10.126693725585938
},
"rotation": {
"w": 0.8697794675827026,
"x": -1.52587890625e-05,
"y": 0.4933699369430542,
"z": -4.57763671875e-05
},
"shapeType": "box",
"skyboxMode": "enabled",
"type": "Zone",
"userData": "{\"grabbableKey\":{\"grabbable\":false}}"
},
{
"alpha": 0,
"alphaFinish": 0,
"alphaStart": 1,
"clientOnly": false,
"color": {
"blue": 211,
"green": 227,
"red": 104
},
"colorFinish": {
"blue": 0,
"green": 0,
"red": 0,
"x": 0,
"y": 0,
"z": 0
},
"colorStart": {
"blue": 211,
"green": 227,
"red": 104,
"x": 104,
"y": 227,
"z": 211
},
"created": "2018-09-05T00:40:03Z",
"dimensions": {
"blue": 2.5,
"green": 2.5,
"red": 2.5,
"x": 2.5,
"y": 2.5,
"z": 2.5
},
"emitAcceleration": {
"blue": 0,
"green": 0,
"red": 0,
"x": 0,
"y": 0,
"z": 0
},
"emitDimensions": {
"blue": 1,
"green": 1,
"red": 1,
"x": 1,
"y": 1,
"z": 1
},
"emitOrientation": {
"w": 0.9993909597396851,
"x": 0.034897372126579285,
"y": -1.525880907138344e-05,
"z": -1.525880907138344e-05
},
"emitRate": 2,
"emitSpeed": 0,
"id": "{639a51f0-8613-4e46-bc7e-fef24597df73}",
"lastEdited": 1536107948776693,
"lastEditedBy": "{b5bba536-25e5-4b12-a1be-5c7cd196a06a}",
"lifespan": 10,
"locked": true,
"maxParticles": 40,
"name": "Rays",
"owningAvatarID": "{00000000-0000-0000-0000-000000000000}",
"particleRadius": 0.75,
"polarFinish": 3.1415927410125732,
"position": {
"blue": 1.3553659915924072,
"green": 1.2890124320983887,
"red": 2.5663273334503174,
"x": 2.5663273334503174,
"y": 1.2890124320983887,
"z": 1.3553659915924072
},
"queryAACube": {
"scale": 4.330127239227295,
"x": 0.4012637138366699,
"y": -0.8760511875152588,
"z": -0.8096976280212402
},
"radiusFinish": 0.10000000149011612,
"radiusStart": 0,
"rotation": {
"w": 0.9803768396377563,
"x": -1.52587890625e-05,
"y": 0.19707024097442627,
"z": -7.62939453125e-05
},
"speedSpread": 0,
"spinFinish": null,
"spinStart": null,
"textures": "http://hifi-content.s3.amazonaws.com/alexia/Models/Portal/stripe.png",
"type": "ParticleEffect",
"userData": "{\"grabbableKey\":{\"grabbable\":false}}"
},
{
"alpha": 0,
"alphaFinish": 0,
"alphaStart": 1,
"clientOnly": false,
"color": {
"blue": 255,
"green": 205,
"red": 3
},
"colorFinish": {
"blue": 0,
"green": 0,
"red": 0,
"x": 0,
"y": 0,
"z": 0
},
"colorStart": {
"blue": 255,
"green": 204,
"red": 0,
"x": 0,
"y": 204,
"z": 255
},
"created": "2018-09-05T00:40:03Z",
"dimensions": {
"blue": 2.5,
"green": 2.5,
"red": 2.5,
"x": 2.5,
"y": 2.5,
"z": 2.5
},
"emitAcceleration": {
"blue": 0,
"green": 0,
"red": 0,
"x": 0,
"y": 0,
"z": 0
},
"emitDimensions": {
"blue": 1,
"green": 1,
"red": 1,
"x": 1,
"y": 1,
"z": 1
},
"emitOrientation": {
"w": 0.9993909597396851,
"x": 0.034897372126579285,
"y": -1.525880907138344e-05,
"z": -1.525880907138344e-05
},
"emitRate": 2,
"emitSpeed": 0,
"emitterShouldTrail": true,
"id": "{e62ced49-fa18-4ae1-977f-abef5bc0f3ba}",
"lastEdited": 1536107948775366,
"lastEditedBy": "{b5bba536-25e5-4b12-a1be-5c7cd196a06a}",
"lifespan": 10,
"locked": true,
"maxParticles": 40,
"name": "Rays",
"owningAvatarID": "{00000000-0000-0000-0000-000000000000}",
"particleRadius": 0.75,
"polarFinish": 3.1415927410125732,
"position": {
"blue": 3.814434051513672,
"green": 1.2890124320983887,
"red": 1.2254328727722168,
"x": 1.2254328727722168,
"y": 1.2890124320983887,
"z": 3.814434051513672
},
"queryAACube": {
"scale": 4.330127239227295,
"x": -0.9396307468414307,
"y": -0.8760511875152588,
"z": 1.6493704319000244
},
"radiusFinish": 0.10000000149011612,
"radiusStart": 0,
"rotation": {
"w": 0.9594720602035522,
"x": -1.52587890625e-05,
"y": 0.28178834915161133,
"z": -4.57763671875e-05
},
"speedSpread": 0,
"spinFinish": null,
"spinStart": null,
"textures": "http://hifi-content.s3.amazonaws.com/alexia/Models/Portal/stripe.png",
"type": "ParticleEffect",
"userData": "{\"grabbableKey\":{\"grabbable\":false}}"
},
{
"alpha": 0,
"alphaFinish": 0,
"alphaStart": 0.25,
"clientOnly": false,
"colorFinish": {
"blue": 0,
"green": 0,
"red": 0,
"x": 0,
"y": 0,
"z": 0
},
"colorStart": {
"blue": 255,
"green": 255,
"red": 255,
"x": 255,
"y": 255,
"z": 255
},
"created": "2018-09-05T00:40:03Z",
"dimensions": {
"blue": 13.24000072479248,
"green": 13.24000072479248,
"red": 13.24000072479248,
"x": 13.24000072479248,
"y": 13.24000072479248,
"z": 13.24000072479248
},
"emitAcceleration": {
"blue": 0,
"green": 0.10000000149011612,
"red": 0,
"x": 0,
"y": 0.10000000149011612,
"z": 0
},
"emitDimensions": {
"blue": 1,
"green": 1,
"red": 1,
"x": 1,
"y": 1,
"z": 1
},
"emitOrientation": {
"w": 1,
"x": -1.52587890625e-05,
"y": -1.52587890625e-05,
"z": -1.52587890625e-05
},
"emitRate": 6,
"emitSpeed": 0,
"id": "{298c0571-cbd8-487b-8640-64037d6a8414}",
"lastEdited": 1536107948776382,
"lastEditedBy": "{b5bba536-25e5-4b12-a1be-5c7cd196a06a}",
"lifespan": 10,
"locked": true,
"maxParticles": 10,
"name": "Stars",
"owningAvatarID": "{00000000-0000-0000-0000-000000000000}",
"particleRadius": 0.07000000029802322,
"polarFinish": 3.1415927410125732,
"position": {
"blue": 1.3712034225463867,
"green": 0.3698839843273163,
"red": 2.6216418743133545,
"x": 2.6216418743133545,
"y": 0.3698839843273163,
"z": 1.3712034225463867
},
"queryAACube": {
"scale": 22.932353973388672,
"x": -8.844534873962402,
"y": -11.096293449401855,
"z": -10.09497356414795
},
"radiusFinish": 0,
"radiusStart": 0,
"rotation": {
"w": 0.9852597713470459,
"x": -1.52587890625e-05,
"y": -0.17106890678405762,
"z": -7.62939453125e-05
},
"speedSpread": 0,
"spinFinish": null,
"spinStart": null,
"textures": "http://hifi-content.s3.amazonaws.com/alexia/Models/Portal/star.png",
"type": "ParticleEffect",
"userData": "{\"grabbableKey\":{\"grabbable\":false}}"
},
{
"clientOnly": false,
"created": "2018-09-05T00:40:03Z",
"dimensions": {
"blue": 2.1097896099090576,
"green": 0.04847164824604988,
"red": 1.458284616470337,
"x": 1.458284616470337,
"y": 0.04847164824604988,
"z": 2.1097896099090576
},
"id": "{6625dbb8-ff25-458d-a92e-644b58460604}",
"lastEdited": 1536107948776195,
"lastEditedBy": "{b5bba536-25e5-4b12-a1be-5c7cd196a06a}",
"locked": true,
"modelURL": "http://hifi-content.s3.amazonaws.com/alexia/Models/Portal/portal1.fbx",
"name": "Try Again",
"owningAvatarID": "{00000000-0000-0000-0000-000000000000}",
"position": {
"blue": 3.946338653564453,
"green": 0.09449335932731628,
"red": 1.594836711883545,
"x": 1.594836711883545,
"y": 0.09449335932731628,
"z": 3.946338653564453
},
"queryAACube": {
"scale": 2.5651814937591553,
"x": 0.3122459650039673,
"y": -1.188097357749939,
"z": 2.663747787475586
},
"rotation": {
"w": 0.8220492601394653,
"x": -1.52587890625e-05,
"y": 0.5693598985671997,
"z": -0.0001068115234375
},
"script": "https://hifi-content.s3.amazonaws.com/wayne/404redirectionScripts/tryAgainEntityScript.js",
"shapeType": "static-mesh",
"type": "Model",
"userData": "{\"grabbableKey\":{\"grabbable\":false}}"
},
{
"clientOnly": false,
"created": "2018-09-05T00:40:03Z",
"dimensions": {
"blue": 0.06014331430196762,
"green": 2.582186460494995,
"red": 2.582186698913574,
"x": 2.582186698913574,
"y": 2.582186460494995,
"z": 0.06014331430196762
},
"id": "{dfe92dce-f09d-4e9e-b3ed-c68ecd4d476f}",
"lastEdited": 1536108160862286,
"lastEditedBy": "{4656d4a8-5e61-4230-ab34-2888d7945bd6}",
"modelURL": "",
"name": "Oops Dialog",
"owningAvatarID": "{00000000-0000-0000-0000-000000000000}",
"position": {
"blue": 1.45927095413208,
"green": 1.6763916015625,
"red": 0,
"x": 0,
"y": 1.6763916015625,
"z": 1.45927095413208
},
"queryAACube": {
"scale": 3.6522583961486816,
"x": -1.8261291980743408,
"y": -0.14973759651184082,
"z": -0.36685824394226074
},
"rotation": {
"w": 0.8684672117233276,
"x": -4.57763671875e-05,
"y": 0.4957197904586792,
"z": -7.62939453125e-05
},
"script": "https://hifi-content.s3.amazonaws.com/wayne/404redirectionScripts/oopsEntityScript.js",
"scriptTimestamp": 1536102551825,
"type": "Model",
"userData": "{\"grabbableKey\":{\"grabbable\":false}}"
},
{
"clientOnly": false,
"color": {
"blue": 0,
"green": 0,
"red": 0
},
"created": "2018-09-05T00:40:03Z",
"dimensions": {
"blue": 11.117486953735352,
"green": 3.580313205718994,
"red": 0.20000000298023224,
"x": 0.20000000298023224,
"y": 3.580313205718994,
"z": 11.117486953735352
},
"id": "{144a8cf4-b0e8-489a-9403-d74d4dc4cb3e}",
"lastEdited": 1536107948775774,
"lastEditedBy": "{ce82d352-3002-44ae-9b76-66492989a1db}",
"locked": true,
"name": "rightWall",
"owningAvatarID": "{00000000-0000-0000-0000-000000000000}",
"position": {
"blue": 0,
"green": 1.0061144828796387,
"red": 4.965089321136475,
"x": 4.965089321136475,
"y": 1.0061144828796387,
"z": 0
},
"queryAACube": {
"scale": 11.681488037109375,
"x": -0.8756546974182129,
"y": -4.834629535675049,
"z": -5.8407440185546875
},
"rotation": {
"w": 0.8637980222702026,
"x": -4.57763671875e-05,
"y": 0.5038070678710938,
"z": -1.52587890625e-05
},
"shape": "Cube",
"type": "Box",
"userData": "{\"grabbableKey\":{\"grabbable\":false}}",
"visible": false
},
{
"clientOnly": false,
"collidesWith": "static,dynamic,kinematic,otherAvatar,",
"collisionMask": 23,
"created": "2018-09-05T00:40:03Z",
"dimensions": {
"blue": 1.159199833869934,
"green": 2.8062009811401367,
"red": 1.6216505765914917,
"x": 1.6216505765914917,
"y": 2.8062009811401367,
"z": 1.159199833869934
},
"id": "{37f53408-3d0c-42a5-9891-e6c40a227349}",
"lastEdited": 1536107948775010,
"lastEditedBy": "{b5bba536-25e5-4b12-a1be-5c7cd196a06a}",
"locked": true,
"name": "Back Zone",
"owningAvatarID": "{00000000-0000-0000-0000-000000000000}",
"position": {
"blue": 1.8632707595825195,
"green": 1.6500625610351562,
"red": 3.3211965560913086,
"x": 3.3211965560913086,
"y": 1.6500625610351562,
"z": 1.8632707595825195
},
"queryAACube": {
"scale": 3.4421300888061523,
"x": 1.6001315116882324,
"y": -0.07100248336791992,
"z": 0.14220571517944336
},
"rotation": {
"w": 0.9304176568984985,
"x": 0,
"y": -0.36650121212005615,
"z": 0
},
"script": "https://hifi-content.s3.amazonaws.com/wayne/404redirectionScripts/zoneBackEntityScript.js",
"shapeType": "box",
"type": "Zone",
"userData": "{\"grabbableKey\":{\"grabbable\":false}}"
},
{
"clientOnly": false,
"color": {
"blue": 0,
"green": 0,
"red": 0
},
"created": "2018-09-05T00:40:03Z",
"dimensions": {
"blue": 11.117486953735352,
"green": 3.580313205718994,
"red": 0.20000000298023224,
"x": 0.20000000298023224,
"y": 3.580313205718994,
"z": 11.117486953735352
},
"id": "{aa6e680c-6750-4776-95bc-ef3118cace5c}",
"lastEdited": 1536107948775945,
"lastEditedBy": "{ce82d352-3002-44ae-9b76-66492989a1db}",
"locked": true,
"name": "frontWall",
"owningAvatarID": "{00000000-0000-0000-0000-000000000000}",
"position": {
"blue": 2.662257671356201,
"green": 1.0063786506652832,
"red": 1.4868733882904053,
"x": 1.4868733882904053,
"y": 1.0063786506652832,
"z": 2.662257671356201
},
"queryAACube": {
"scale": 11.681488037109375,
"x": -4.353870391845703,
"y": -4.834365367889404,
"z": -3.1784863471984863
},
"rotation": {
"w": 0.9666743278503418,
"x": -4.57763671875e-05,
"y": -0.2560006380081177,
"z": 1.52587890625e-05
},
"shape": "Cube",
"type": "Box",
"userData": "{\"grabbableKey\":{\"grabbable\":false}}",
"visible": false
},
{
"clientOnly": false,
"created": "2018-09-05T00:40:03Z",
"dimensions": {
"blue": 2.1097896099090576,
"green": 0.04847164824604988,
"red": 1.458284616470337,
"x": 1.458284616470337,
"y": 0.04847164824604988,
"z": 2.1097896099090576
},
"id": "{303631f1-04f3-42a6-b8a8-8dd4b65d1231}",
"lastEdited": 1536107948776513,
"lastEditedBy": "{b5bba536-25e5-4b12-a1be-5c7cd196a06a}",
"locked": true,
"modelURL": "http://hifi-content.s3.amazonaws.com/alexia/Models/Portal/portal2.fbx",
"name": "Back",
"owningAvatarID": "{00000000-0000-0000-0000-000000000000}",
"position": {
"blue": 1.5835940837860107,
"green": 0.09449335932731628,
"red": 3.028078079223633,
"x": 3.028078079223633,
"y": 0.09449335932731628,
"z": 1.5835940837860107
},
"queryAACube": {
"scale": 2.5651814937591553,
"x": 1.7454873323440552,
"y": -1.188097357749939,
"z": 0.3010033369064331
},
"rotation": {
"w": 0.9084458351135254,
"x": -1.52587890625e-05,
"y": 0.4179598093032837,
"z": -0.0001068115234375
},
"script": "https://hifi-content.s3.amazonaws.com/wayne/404redirectionScripts/backEntityScript.js",
"scriptTimestamp": 1535751754379,
"shapeType": "static-mesh",
"type": "Model",
"userData": "{\"grabbableKey\":{\"grabbable\":false}}"
},
{
"alpha": 0,
"alphaFinish": 0,
"alphaStart": 0.25,
"clientOnly": false,
"colorFinish": {
"blue": 0,
"green": 0,
"red": 0,
"x": 0,
"y": 0,
"z": 0
},
"colorStart": {
"blue": 255,
"green": 255,
"red": 255,
"x": 255,
"y": 255,
"z": 255
},
"created": "2018-09-05T00:40:03Z",
"dimensions": {
"blue": 13.24000072479248,
"green": 13.24000072479248,
"red": 13.24000072479248,
"x": 13.24000072479248,
"y": 13.24000072479248,
"z": 13.24000072479248
},
"emitAcceleration": {
"blue": 0,
"green": 0.10000000149011612,
"red": 0,
"x": 0,
"y": 0.10000000149011612,
"z": 0
},
"emitDimensions": {
"blue": 1,
"green": 1,
"red": 1,
"x": 1,
"y": 1,
"z": 1
},
"emitOrientation": {
"w": 1,
"x": -1.52587890625e-05,
"y": -1.52587890625e-05,
"z": -1.52587890625e-05
},
"emitRate": 6,
"emitSpeed": 0,
"emitterShouldTrail": true,
"id": "{8ded39e6-303c-48f2-be79-81b715cca9f7}",
"lastEdited": 1536107948777127,
"lastEditedBy": "{b5bba536-25e5-4b12-a1be-5c7cd196a06a}",
"lifespan": 10,
"locked": true,
"maxParticles": 10,
"name": "Stars",
"owningAvatarID": "{00000000-0000-0000-0000-000000000000}",
"particleRadius": 0.07000000029802322,
"polarFinish": 3.1415927410125732,
"position": {
"blue": 3.78922963142395,
"green": 0.3698839843273163,
"red": 1.1863799095153809,
"x": 1.1863799095153809,
"y": 0.3698839843273163,
"z": 3.78922963142395
},
"queryAACube": {
"scale": 22.932353973388672,
"x": -10.279796600341797,
"y": -11.096293449401855,
"z": -7.676947593688965
},
"radiusFinish": 0,
"radiusStart": 0,
"rotation": {
"w": 0.996429443359375,
"x": -1.52587890625e-05,
"y": -0.08442819118499756,
"z": -4.57763671875e-05
},
"speedSpread": 0,
"spinFinish": null,
"spinStart": null,
"textures": "http://hifi-content.s3.amazonaws.com/alexia/Models/Portal/star.png",
"type": "ParticleEffect",
"userData": "{\"grabbableKey\":{\"grabbable\":false}}"
}
],
"Id": "{18abccad-2d57-4176-9d89-24dc424916f5}",
"Version": 93
}

View file

@ -1186,13 +1186,13 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
const DomainHandler& domainHandler = nodeList->getDomainHandler();
connect(&domainHandler, SIGNAL(domainURLChanged(QUrl)), SLOT(domainURLChanged(QUrl)));
connect(&domainHandler, SIGNAL(redirectToErrorDomainURL(QUrl)), SLOT(goToErrorDomainURL(QUrl)));
connect(&domainHandler, &DomainHandler::domainURLChanged, [](QUrl domainURL){
setCrashAnnotation("domain", domainURL.toString().toStdString());
});
connect(&domainHandler, SIGNAL(resetting()), SLOT(resettingDomain()));
connect(&domainHandler, SIGNAL(connectedToDomain(QUrl)), SLOT(updateWindowTitle()));
connect(&domainHandler, SIGNAL(disconnectedFromDomain()), SLOT(updateWindowTitle()));
connect(&domainHandler, &DomainHandler::disconnectedFromDomain, this, &Application::clearDomainAvatars);
connect(&domainHandler, &DomainHandler::disconnectedFromDomain, this, [this]() {
getOverlays().deleteOverlay(getTabletScreenID());
getOverlays().deleteOverlay(getTabletHomeButtonID());
@ -1200,6 +1200,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
});
connect(&domainHandler, &DomainHandler::domainConnectionRefused, this, &Application::domainConnectionRefused);
nodeList->getDomainHandler().setErrorDomainURL(QUrl(REDIRECT_HIFI_ADDRESS));
// We could clear ATP assets only when changing domains, but it's possible that the domain you are connected
// to has gone down and switched to a new content set, so when you reconnect the cached ATP assets will no longer be valid.
connect(&domainHandler, &DomainHandler::disconnectedFromDomain, DependencyManager::get<ScriptCache>().data(), &ScriptCache::clearATPScriptsFromCache);
@ -1641,7 +1643,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
audioClient->setMuted(!audioClient->isMuted());
} else if (action == controller::toInt(controller::Action::CYCLE_CAMERA)) {
cycleCamera();
} else if (action == controller::toInt(controller::Action::CONTEXT_MENU)) {
} else if (action == controller::toInt(controller::Action::CONTEXT_MENU) && !isInterstitialMode()) {
toggleTabletUI();
} else if (action == controller::toInt(controller::Action::RETICLE_X)) {
auto oldPos = getApplicationCompositor().getReticlePosition();
@ -2250,6 +2252,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
connect(this, &QCoreApplication::aboutToQuit, this, &Application::addAssetToWorldMessageClose);
connect(&domainHandler, &DomainHandler::domainURLChanged, this, &Application::addAssetToWorldMessageClose);
connect(&domainHandler, &DomainHandler::redirectToErrorDomainURL, this, &Application::addAssetToWorldMessageClose);
updateSystemTabletMode();
@ -2299,6 +2302,24 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
connect(&AndroidHelper::instance(), &AndroidHelper::enterBackground, this, &Application::enterBackground);
connect(&AndroidHelper::instance(), &AndroidHelper::enterForeground, this, &Application::enterForeground);
AndroidHelper::instance().notifyLoadComplete();
#else
static int CHECK_LOGIN_TIMER = 3000;
QTimer* checkLoginTimer = new QTimer(this);
checkLoginTimer->setInterval(CHECK_LOGIN_TIMER);
checkLoginTimer->setSingleShot(true);
connect(checkLoginTimer, &QTimer::timeout, this, [this]() {
auto accountManager = DependencyManager::get<AccountManager>();
auto dialogsManager = DependencyManager::get<DialogsManager>();
if (!accountManager->isLoggedIn()) {
Setting::Handle<bool>{"loginDialogPoppedUp", false}.set(true);
dialogsManager->showLoginDialog();
QJsonObject loginData = {};
loginData["action"] = "login dialog shown";
UserActivityLogger::getInstance().logAction("encourageLoginDialog", loginData);
}
});
Setting::Handle<bool>{"loginDialogPoppedUp", false}.set(false);
checkLoginTimer->start();
#endif
}
@ -2433,6 +2454,8 @@ void Application::onAboutToQuit() {
// so its persisted explicitly here
Setting::Handle<QString>{ ACTIVE_DISPLAY_PLUGIN_SETTING_NAME }.set(getActiveDisplayPlugin()->getName());
Setting::Handle<bool>{"loginDialogPoppedUp", false}.set(false);
getActiveDisplayPlugin()->deactivate();
if (_autoSwitchDisplayModeSupportedHMDPlugin
&& _autoSwitchDisplayModeSupportedHMDPlugin->isSessionActive()) {
@ -2664,6 +2687,10 @@ Application::~Application() {
void Application::initializeGL() {
qCDebug(interfaceapp) << "Created Display Window.";
#ifdef DISABLE_QML
setAttribute(Qt::AA_DontCheckOpenGLContextThreadAffinity);
#endif
// initialize glut for shape drawing; Qt apparently initializes it on OS X
if (_isGLInitialized) {
return;
@ -2989,6 +3016,9 @@ void Application::initializeUi() {
if (_window && _window->isFullScreen()) {
setFullscreen(nullptr, true);
}
setIsInterstitialMode(true);
}
@ -3467,6 +3497,17 @@ bool Application::isServerlessMode() const {
return false;
}
void Application::setIsInterstitialMode(bool interstitialMode) {
Settings settings;
bool enableInterstitial = settings.value("enableIntersitialMode", false).toBool();
if (_interstitialMode != interstitialMode && enableInterstitial) {
_interstitialMode = interstitialMode;
DependencyManager::get<AudioClient>()->setAudioPaused(_interstitialMode);
DependencyManager::get<AvatarManager>()->setMyAvatarDataPacketsPaused(_interstitialMode);
}
}
void Application::setIsServerlessMode(bool serverlessDomain) {
auto tree = getEntities()->getTree();
if (tree) {
@ -3474,9 +3515,9 @@ void Application::setIsServerlessMode(bool serverlessDomain) {
}
}
void Application::loadServerlessDomain(QUrl domainURL) {
void Application::loadServerlessDomain(QUrl domainURL, bool errorDomain) {
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "loadServerlessDomain", Q_ARG(QUrl, domainURL));
QMetaObject::invokeMethod(this, "loadServerlessDomain", Q_ARG(QUrl, domainURL), Q_ARG(bool, errorDomain));
return;
}
@ -3508,8 +3549,11 @@ void Application::loadServerlessDomain(QUrl domainURL) {
}
std::map<QString, QString> namedPaths = tmpTree->getNamedPaths();
nodeList->getDomainHandler().connectedToServerless(namedPaths);
if (errorDomain) {
nodeList->getDomainHandler().loadedErrorDomain(namedPaths);
} else {
nodeList->getDomainHandler().connectedToServerless(namedPaths);
}
_fullSceneReceivedCounter++;
}
@ -3744,7 +3788,7 @@ void Application::keyPressEvent(QKeyEvent* event) {
_controllerScriptingInterface->emitKeyPressEvent(event); // send events to any registered scripts
// if one of our scripts have asked to capture this event, then stop processing it
if (_controllerScriptingInterface->isKeyCaptured(event)) {
if (_controllerScriptingInterface->isKeyCaptured(event) || isInterstitialMode()) {
return;
}
@ -5531,6 +5575,7 @@ void Application::update(float deltaTime) {
return;
}
if (!_physicsEnabled) {
if (!domainLoadingInProgress) {
PROFILE_ASYNC_BEGIN(app, "Scene Loading", "");
@ -5551,6 +5596,7 @@ void Application::update(float deltaTime) {
// scene is ready to compute its collision shape.
if (getMyAvatar()->isReadyForPhysics()) {
_physicsEnabled = true;
setIsInterstitialMode(false);
getMyAvatar()->updateMotionBehaviorFromMenu();
}
}
@ -5630,7 +5676,7 @@ void Application::update(float deltaTime) {
// Transfer the user inputs to the driveKeys
// FIXME can we drop drive keys and just have the avatar read the action states directly?
myAvatar->clearDriveKeys();
if (_myCamera.getMode() != CAMERA_MODE_INDEPENDENT) {
if (_myCamera.getMode() != CAMERA_MODE_INDEPENDENT && !isInterstitialMode()) {
if (!_controllerScriptingInterface->areActionsCaptured() && _myCamera.getMode() != CAMERA_MODE_MIRROR) {
myAvatar->setDriveKey(MyAvatar::TRANSLATE_Z, -1.0f * userInputMapper->getActionState(controller::Action::TRANSLATE_Z));
myAvatar->setDriveKey(MyAvatar::TRANSLATE_Y, userInputMapper->getActionState(controller::Action::TRANSLATE_Y));
@ -5946,7 +5992,7 @@ void Application::update(float deltaTime) {
// send packet containing downstream audio stats to the AudioMixer
{
quint64 sinceLastNack = now - _lastSendDownstreamAudioStats;
if (sinceLastNack > TOO_LONG_SINCE_LAST_SEND_DOWNSTREAM_AUDIO_STATS) {
if (sinceLastNack > TOO_LONG_SINCE_LAST_SEND_DOWNSTREAM_AUDIO_STATS && !isInterstitialMode()) {
_lastSendDownstreamAudioStats = now;
QMetaObject::invokeMethod(DependencyManager::get<AudioClient>().data(), "sendDownstreamAudioStatsPacket", Qt::QueuedConnection);
@ -6109,21 +6155,23 @@ void Application::updateRenderArgs(float deltaTime) {
}
void Application::queryAvatars() {
auto avatarPacket = NLPacket::create(PacketType::AvatarQuery);
auto destinationBuffer = reinterpret_cast<unsigned char*>(avatarPacket->getPayload());
unsigned char* bufferStart = destinationBuffer;
if (!isInterstitialMode()) {
auto avatarPacket = NLPacket::create(PacketType::AvatarQuery);
auto destinationBuffer = reinterpret_cast<unsigned char*>(avatarPacket->getPayload());
unsigned char* bufferStart = destinationBuffer;
uint8_t numFrustums = (uint8_t)_conicalViews.size();
memcpy(destinationBuffer, &numFrustums, sizeof(numFrustums));
destinationBuffer += sizeof(numFrustums);
uint8_t numFrustums = (uint8_t)_conicalViews.size();
memcpy(destinationBuffer, &numFrustums, sizeof(numFrustums));
destinationBuffer += sizeof(numFrustums);
for (const auto& view : _conicalViews) {
destinationBuffer += view.serialize(destinationBuffer);
for (const auto& view : _conicalViews) {
destinationBuffer += view.serialize(destinationBuffer);
}
avatarPacket->setPayloadSize(destinationBuffer - bufferStart);
DependencyManager::get<NodeList>()->broadcastToNodes(std::move(avatarPacket), NodeSet() << NodeType::AvatarMixer);
}
avatarPacket->setPayloadSize(destinationBuffer - bufferStart);
DependencyManager::get<NodeList>()->broadcastToNodes(std::move(avatarPacket), NodeSet() << NodeType::AvatarMixer);
}
@ -6300,6 +6348,7 @@ void Application::updateWindowTitle() const {
auto nodeList = DependencyManager::get<NodeList>();
auto accountManager = DependencyManager::get<AccountManager>();
auto isInErrorState = nodeList->getDomainHandler().isInErrorState();
QString buildVersion = " - "
+ (BuildInfo::BUILD_TYPE == BuildInfo::BuildType::Stable ? QString("Version") : QString("Build"))
@ -6307,14 +6356,19 @@ void Application::updateWindowTitle() const {
QString loginStatus = accountManager->isLoggedIn() ? "" : " (NOT LOGGED IN)";
QString connectionStatus = nodeList->getDomainHandler().isConnected() ? "" : " (NOT CONNECTED)";
QString connectionStatus = isInErrorState ? " (ERROR CONNECTING)" :
nodeList->getDomainHandler().isConnected() ? "" : " (NOT CONNECTED)";
QString username = accountManager->getAccountInfo().getUsername();
setCrashAnnotation("username", username.toStdString());
QString currentPlaceName;
if (isServerlessMode()) {
currentPlaceName = "serverless: " + DependencyManager::get<AddressManager>()->getDomainURL().toString();
if (isInErrorState) {
currentPlaceName = "serverless: " + nodeList->getDomainHandler().getErrorDomainURL().toString();
} else {
currentPlaceName = "serverless: " + DependencyManager::get<AddressManager>()->getDomainURL().toString();
}
} else {
currentPlaceName = DependencyManager::get<AddressManager>()->getDomainURL().host();
if (currentPlaceName.isEmpty()) {
@ -6346,6 +6400,7 @@ void Application::clearDomainOctreeDetails() {
qCDebug(interfaceapp) << "Clearing domain octree details...";
resetPhysicsReadyInformation();
setIsInterstitialMode(true);
_octreeServerSceneStats.withWriteLock([&] {
_octreeServerSceneStats.clear();
@ -6366,10 +6421,6 @@ void Application::clearDomainOctreeDetails() {
getMyAvatar()->setAvatarEntityDataChanged(true);
}
void Application::clearDomainAvatars() {
DependencyManager::get<AvatarManager>()->clearOtherAvatars();
}
void Application::domainURLChanged(QUrl domainURL) {
// disable physics until we have enough information about our new location to not cause craziness.
resetPhysicsReadyInformation();
@ -6380,6 +6431,16 @@ void Application::domainURLChanged(QUrl domainURL) {
updateWindowTitle();
}
void Application::goToErrorDomainURL(QUrl errorDomainURL) {
// disable physics until we have enough information about our new location to not cause craziness.
resetPhysicsReadyInformation();
setIsServerlessMode(errorDomainURL.scheme() != URL_SCHEME_HIFI);
if (isServerlessMode()) {
loadServerlessDomain(errorDomainURL, true);
}
updateWindowTitle();
}
void Application::resettingDomain() {
_notifiedPacketVersionMismatchThisDomain = false;
@ -6418,7 +6479,7 @@ void Application::nodeActivated(SharedNodePointer node) {
_octreeQuery.incrementConnectionID();
}
if (node->getType() == NodeType::AudioMixer) {
if (node->getType() == NodeType::AudioMixer && !isInterstitialMode()) {
DependencyManager::get<AudioClient>()->negotiateAudioFormat();
}
@ -6438,8 +6499,10 @@ void Application::nodeActivated(SharedNodePointer node) {
getMyAvatar()->markIdentityDataChanged();
getMyAvatar()->resetLastSent();
// transmit a "sendAll" packet to the AvatarMixer we just connected to.
getMyAvatar()->sendAvatarDataPacket(true);
if (!isInterstitialMode()) {
// transmit a "sendAll" packet to the AvatarMixer we just connected to.
getMyAvatar()->sendAvatarDataPacket(true);
}
}
}
@ -6457,9 +6520,6 @@ void Application::nodeKilled(SharedNodePointer node) {
} else if (node->getType() == NodeType::EntityServer) {
// we lost an entity server, clear all of the domain octree details
clearDomainOctreeDetails();
} else if (node->getType() == NodeType::AvatarMixer) {
// our avatar mixer has gone away - clear the hash of avatars
DependencyManager::get<AvatarManager>()->clearOtherAvatars();
} else if (node->getType() == NodeType::AssetServer) {
// asset server going away - check if we have the asset browser showing
@ -6872,6 +6932,9 @@ bool Application::askToLoadScript(const QString& scriptFilenameOrURL) {
shortName = shortName.mid(startIndex, endIndex - startIndex);
}
#ifdef DISABLE_QML
DependencyManager::get<ScriptEngines>()->loadScript(scriptFilenameOrURL);
#else
QString message = "Would you like to run this script:\n" + shortName;
ModalDialogListener* dlg = OffscreenUi::asyncQuestion(getWindow(), "Run Script", message,
QMessageBox::Yes | QMessageBox::No);
@ -6886,7 +6949,7 @@ bool Application::askToLoadScript(const QString& scriptFilenameOrURL) {
}
QObject::disconnect(dlg, &ModalDialogListener::response, this, nullptr);
});
#endif
return true;
}
@ -7790,7 +7853,7 @@ float Application::getRenderResolutionScale() const {
}
void Application::notifyPacketVersionMismatch() {
if (!_notifiedPacketVersionMismatchThisDomain) {
if (!_notifiedPacketVersionMismatchThisDomain && !isInterstitialMode()) {
_notifiedPacketVersionMismatchThisDomain = true;
QString message = "The location you are visiting is running an incompatible server version.\n";

View file

@ -224,6 +224,7 @@ public:
void setHmdTabletBecomesToolbarSetting(bool value);
bool getPreferStylusOverLaser() { return _preferStylusOverLaserSetting.get(); }
void setPreferStylusOverLaser(bool value);
// FIXME: Remove setting completely or make available through JavaScript API?
//bool getPreferAvatarFingerOverStylus() { return _preferAvatarFingerOverStylusSetting.get(); }
bool getPreferAvatarFingerOverStylus() { return false; }
@ -304,6 +305,7 @@ public:
void saveNextPhysicsStats(QString filename);
bool isServerlessMode() const;
bool isInterstitialMode() const { return _interstitialMode; }
void replaceDomainContent(const QString& url);
@ -331,6 +333,8 @@ signals:
void uploadRequest(QString path);
void loginDialogPoppedUp();
public slots:
QVector<EntityItemID> pasteEntities(float x, float y, float z);
bool exportEntities(const QString& filename, const QVector<EntityItemID>& entityIDs, const glm::vec3* givenOffset = nullptr);
@ -338,6 +342,7 @@ public slots:
bool importEntities(const QString& url);
void updateThreadPoolCount() const;
void updateSystemTabletMode();
void goToErrorDomainURL(QUrl errorDomainURL);
Q_INVOKABLE void loadDialog();
Q_INVOKABLE void loadScriptURLDialog() const;
@ -426,7 +431,8 @@ public slots:
void setPreferredCursor(const QString& cursor);
void setIsServerlessMode(bool serverlessDomain);
void loadServerlessDomain(QUrl domainURL);
void loadServerlessDomain(QUrl domainURL, bool errorDomain = false);
void setIsInterstitialMode(bool interstialMode);
void updateVerboseLogging();
@ -437,7 +443,6 @@ private slots:
void onDesktopRootContextCreated(QQmlContext* qmlContext);
void showDesktop();
void clearDomainOctreeDetails();
void clearDomainAvatars();
void onAboutToQuit();
void onPresent(quint32 frameCount);
@ -626,6 +631,7 @@ private:
QHash<int, QKeyEvent> _keysPressed;
bool _enableProcessOctreeThread;
bool _interstitialMode { false };
OctreePacketProcessor _octreeProcessor;
EntityEditPacketSender _entityEditSender;

View file

@ -11,16 +11,18 @@
#include "ConnectionMonitor.h"
#include "Application.h"
#include "ui/DialogsManager.h"
#include <DependencyManager.h>
#include <DomainHandler.h>
#include <AddressManager.h>
#include <NodeList.h>
// Because the connection monitor is created at startup, the time we wait on initial load
// should be longer to allow the application to initialize.
static const int ON_INITIAL_LOAD_DISPLAY_AFTER_DISCONNECTED_FOR_X_MS = 10000;
static const int DISPLAY_AFTER_DISCONNECTED_FOR_X_MS = 5000;
static const int ON_INITIAL_LOAD_REDIRECT_AFTER_DISCONNECTED_FOR_X_MS = 10000;
static const int REDIRECT_AFTER_DISCONNECTED_FOR_X_MS = 5000;
void ConnectionMonitor::init() {
// Connect to domain disconnected message
@ -30,23 +32,25 @@ void ConnectionMonitor::init() {
connect(&domainHandler, &DomainHandler::disconnectedFromDomain, this, &ConnectionMonitor::startTimer);
connect(&domainHandler, &DomainHandler::connectedToDomain, this, &ConnectionMonitor::stopTimer);
connect(&domainHandler, &DomainHandler::domainConnectionRefused, this, &ConnectionMonitor::stopTimer);
connect(&domainHandler, &DomainHandler::redirectToErrorDomainURL, this, &ConnectionMonitor::stopTimer);
connect(this, &ConnectionMonitor::setRedirectErrorState, &domainHandler, &DomainHandler::setRedirectErrorState);
_timer.setSingleShot(true);
if (!domainHandler.isConnected()) {
_timer.start(ON_INITIAL_LOAD_DISPLAY_AFTER_DISCONNECTED_FOR_X_MS);
_timer.start(ON_INITIAL_LOAD_REDIRECT_AFTER_DISCONNECTED_FOR_X_MS);
}
connect(&_timer, &QTimer::timeout, this, []() {
qDebug() << "ConnectionMonitor: Showing connection failure window";
DependencyManager::get<DialogsManager>()->setDomainConnectionFailureVisibility(true);
connect(&_timer, &QTimer::timeout, this, [this]() {
qDebug() << "ConnectionMonitor: Redirecting to 404 error domain";
// set in a timeout error
emit setRedirectErrorState(REDIRECT_HIFI_ADDRESS, 5);
});
}
void ConnectionMonitor::startTimer() {
_timer.start(DISPLAY_AFTER_DISCONNECTED_FOR_X_MS);
_timer.start(REDIRECT_AFTER_DISCONNECTED_FOR_X_MS);
}
void ConnectionMonitor::stopTimer() {
_timer.stop();
DependencyManager::get<DialogsManager>()->setDomainConnectionFailureVisibility(false);
}

View file

@ -15,6 +15,7 @@
#include <QObject>
#include <QTimer>
class QUrl;
class QString;
class ConnectionMonitor : public QObject {
@ -22,6 +23,9 @@ class ConnectionMonitor : public QObject {
public:
void init();
signals:
void setRedirectErrorState(QUrl errorURL, int reasonCode);
private slots:
void startTimer();
void stopTimer();

View file

@ -45,10 +45,11 @@ const float LOD_ADJUST_RUNNING_AVG_TIMESCALE = 0.08f; // sec
const float LOD_BATCH_TO_PRESENT_CUSHION_TIME = 3.0f; // msec
void LODManager::setRenderTimes(float presentTime, float engineRunTime, float batchTime, float gpuTime) {
_presentTime = presentTime;
_engineRunTime = engineRunTime;
_batchTime = batchTime;
_gpuTime = gpuTime;
// Make sure the sampled time are positive values
_presentTime = std::max(0.0f, presentTime);
_engineRunTime = std::max(0.0f, engineRunTime);
_batchTime = std::max(0.0f, batchTime);
_gpuTime = std::max(0.0f, gpuTime);
}
void LODManager::autoAdjustLOD(float realTimeDelta) {
@ -64,16 +65,29 @@ void LODManager::autoAdjustLOD(float realTimeDelta) {
auto presentTime = (_presentTime > _batchTime + LOD_BATCH_TO_PRESENT_CUSHION_TIME ? _batchTime + LOD_BATCH_TO_PRESENT_CUSHION_TIME : _presentTime);
float maxRenderTime = glm::max(glm::max(presentTime, _engineRunTime), _gpuTime);
// compute time-weighted running average maxRenderTime
// Note: we MUST clamp the blend to 1.0 for stability
// maxRenderTime must be a realistic valid duration in order for the regulation to work correctly.
// We make sure it s a non zero positive value (1.0ms) under 1 sec
maxRenderTime = std::max(1.0f, std::min(maxRenderTime, (float)MSECS_PER_SECOND));
// realTimeDelta must be a realistic valid duration in order for the regulation to work correctly.
// We make sure it a positive value under 1 sec
// note that if real time delta is very small we will early exit to avoid division by zero
realTimeDelta = std::max(0.0f, std::min(realTimeDelta, 1.0f));
// compute time-weighted running average render time (now and smooth)
// We MUST clamp the blend between 0.0 and 1.0 for stability
float nowBlend = (realTimeDelta < LOD_ADJUST_RUNNING_AVG_TIMESCALE) ? realTimeDelta / LOD_ADJUST_RUNNING_AVG_TIMESCALE : 1.0f;
_nowRenderTime = (1.0f - nowBlend) * _nowRenderTime + nowBlend * maxRenderTime; // msec
float smoothBlend = (realTimeDelta < LOD_ADJUST_RUNNING_AVG_TIMESCALE * _smoothScale) ? realTimeDelta / (LOD_ADJUST_RUNNING_AVG_TIMESCALE * _smoothScale) : 1.0f;
_smoothRenderTime = (1.0f - smoothBlend) * _smoothRenderTime + smoothBlend * maxRenderTime; // msec
if (!_automaticLODAdjust || _nowRenderTime == 0.0f || _smoothRenderTime == 0.0f) {
// early exit
//Evaluate the running averages for the render time
// We must sanity check for the output average evaluated to be in a valid range to avoid issues
_nowRenderTime = (1.0f - nowBlend) * _nowRenderTime + nowBlend * maxRenderTime; // msec
_nowRenderTime = std::max(0.0f, std::min(_nowRenderTime, (float)MSECS_PER_SECOND));
_smoothRenderTime = (1.0f - smoothBlend) * _smoothRenderTime + smoothBlend * maxRenderTime; // msec
_smoothRenderTime = std::max(0.0f, std::min(_smoothRenderTime, (float)MSECS_PER_SECOND));
// Early exit if not regulating or if the simulation or render times don't matter
if (!_automaticLODAdjust || realTimeDelta <= 0.0f || _nowRenderTime <= 0.0f || _smoothRenderTime <= 0.0f) {
return;
}
@ -130,7 +144,8 @@ void LODManager::autoAdjustLOD(float realTimeDelta) {
glm::clamp(integral, -1.0f, 1.0f);
// Compute derivative
auto derivative = (error - previous_error) / dt;
// dt is never zero because realTimeDelta would have early exit above, but if it ever was let's zero the derivative term
auto derivative = (dt <= 0.0f ? 0.0f : (error - previous_error) / dt);
// remember history
_pidHistory.x = error;

View file

@ -255,7 +255,7 @@ Menu::Menu() {
connect(action, &QAction::triggered, [] {
auto tablet = DependencyManager::get<TabletScriptingInterface>()->getTablet("com.highfidelity.interface.tablet.system");
auto hmd = DependencyManager::get<HMDScriptingInterface>();
tablet->loadQMLSource("hifi/tablet/ControllerSettings.qml");
tablet->pushOntoStack("hifi/tablet/ControllerSettings.qml");
if (!hmd->getShouldShowTablet()) {
hmd->toggleShouldShowTablet();

View file

@ -196,7 +196,7 @@ void AvatarManager::updateMyAvatar(float deltaTime) {
quint64 now = usecTimestampNow();
quint64 dt = now - _lastSendAvatarDataTime;
if (dt > MIN_TIME_BETWEEN_MY_AVATAR_DATA_SENDS) {
if (dt > MIN_TIME_BETWEEN_MY_AVATAR_DATA_SENDS && !_myAvatarDataPacketsPaused) {
// send head/hand data to the avatar mixer and voxel server
PerformanceTimer perfTimer("send");
_myAvatar->sendAvatarDataPacket();
@ -214,6 +214,16 @@ float AvatarManager::getAvatarDataRate(const QUuid& sessionID, const QString& ra
return avatar ? avatar->getDataRate(rateName) : 0.0f;
}
void AvatarManager::setMyAvatarDataPacketsPaused(bool pause) {
if (_myAvatarDataPacketsPaused != pause) {
_myAvatarDataPacketsPaused = pause;
if (!_myAvatarDataPacketsPaused) {
_myAvatar->sendAvatarDataPacket(true);
}
}
}
float AvatarManager::getAvatarUpdateRate(const QUuid& sessionID, const QString& rateName) const {
auto avatar = getAvatarBySessionID(sessionID);
return avatar ? avatar->getUpdateRate(rateName) : 0.0f;
@ -225,29 +235,29 @@ float AvatarManager::getAvatarSimulationRate(const QUuid& sessionID, const QStri
}
void AvatarManager::updateOtherAvatars(float deltaTime) {
// lock the hash for read to check the size
QReadLocker lock(&_hashLock);
if (_avatarHash.size() < 2 && _avatarsToFade.isEmpty()) {
return;
{
// lock the hash for read to check the size
QReadLocker lock(&_hashLock);
if (_avatarHash.size() < 2 && _avatarsToFade.isEmpty()) {
return;
}
}
lock.unlock();
PerformanceTimer perfTimer("otherAvatars");
class SortableAvatar: public PrioritySortUtil::Sortable {
public:
SortableAvatar() = delete;
SortableAvatar(const AvatarSharedPointer& avatar) : _avatar(avatar) {}
SortableAvatar(const std::shared_ptr<Avatar>& avatar) : _avatar(avatar) {}
glm::vec3 getPosition() const override { return _avatar->getWorldPosition(); }
float getRadius() const override { return std::static_pointer_cast<Avatar>(_avatar)->getBoundingRadius(); }
uint64_t getTimestamp() const override { return std::static_pointer_cast<Avatar>(_avatar)->getLastRenderUpdateTime(); }
AvatarSharedPointer getAvatar() const { return _avatar; }
float getRadius() const override { return _avatar->getBoundingRadius(); }
uint64_t getTimestamp() const override { return _avatar->getLastRenderUpdateTime(); }
std::shared_ptr<Avatar> getAvatar() const { return _avatar; }
private:
AvatarSharedPointer _avatar;
std::shared_ptr<Avatar> _avatar;
};
auto avatarMap = getHashCopy();
AvatarHash::iterator itr = avatarMap.begin();
const auto& views = qApp->getConicalViews();
PrioritySortUtil::PriorityQueue<SortableAvatar> sortedAvatars(views,
@ -256,22 +266,24 @@ void AvatarManager::updateOtherAvatars(float deltaTime) {
AvatarData::_avatarSortCoefficientAge);
sortedAvatars.reserve(avatarMap.size() - 1); // don't include MyAvatar
// sort
// Build vector and compute priorities
auto nodeList = DependencyManager::get<NodeList>();
AvatarHash::iterator itr = avatarMap.begin();
while (itr != avatarMap.end()) {
const auto& avatar = std::static_pointer_cast<Avatar>(*itr);
// DO NOT update _myAvatar! Its update has already been done earlier in the main loop.
// DO NOT update or fade out uninitialized Avatars
if (avatar != _myAvatar && avatar->isInitialized()) {
if (avatar != _myAvatar && avatar->isInitialized() && !nodeList->isPersonalMutingNode(avatar->getID())) {
sortedAvatars.push(SortableAvatar(avatar));
}
++itr;
}
// Sort
const auto& sortedAvatarVector = sortedAvatars.getSortedVector();
// process in sorted order
uint64_t startTime = usecTimestampNow();
const uint64_t UPDATE_BUDGET = 2000; // usec
uint64_t updateExpiry = startTime + UPDATE_BUDGET;
uint64_t updateExpiry = startTime + MAX_UPDATE_AVATARS_TIME_BUDGET;
int numAvatarsUpdated = 0;
int numAVatarsNotUpdated = 0;
@ -290,18 +302,12 @@ void AvatarManager::updateOtherAvatars(float deltaTime) {
avatar->updateOrbPosition();
}
bool ignoring = DependencyManager::get<NodeList>()->isPersonalMutingNode(avatar->getID());
if (ignoring) {
continue;
}
// for ALL avatars...
if (_shouldRender) {
avatar->ensureInScene(avatar, qApp->getMain3DScene());
}
avatar->animateScaleChanges(deltaTime);
const float OUT_OF_VIEW_THRESHOLD = 0.5f * AvatarData::OUT_OF_VIEW_PENALTY;
uint64_t now = usecTimestampNow();
if (now < updateExpiry) {
// we're within budget
@ -332,7 +338,7 @@ void AvatarManager::updateOtherAvatars(float deltaTime) {
// no time to simulate, but we take the time to count how many were tragically missed
while (it != sortedAvatarVector.end()) {
const SortableAvatar& newSortData = *it;
const auto newAvatar = std::static_pointer_cast<Avatar>(newSortData.getAvatar());
const auto& newAvatar = newSortData.getAvatar();
bool inView = newSortData.getPriority() > OUT_OF_VIEW_THRESHOLD;
// Once we reach an avatar that's not in view, all avatars after it will also be out of view
if (!inView) {
@ -502,6 +508,11 @@ void AvatarManager::handleRemovedAvatar(const AvatarSharedPointer& removedAvatar
avatar->die();
queuePhysicsChange(avatar);
// remove this avatar's entities from the tree now, if we wait (as we did previously) for this Avatar's destructor
// it might not fire until after we create a new instance for the same remote avatar, which creates a race
// on the creation of entities for that avatar instance and the deletion of entities for this instance
avatar->removeAvatarEntitiesFromTree();
if (removalReason == KillAvatarReason::TheirAvatarEnteredYourBubble) {
emit DependencyManager::get<UsersScriptingInterface>()->enteredIgnoreRadius();
} else if (removalReason == KillAvatarReason::AvatarDisconnected) {
@ -871,13 +882,13 @@ void AvatarManager::setAvatarSortCoefficient(const QString& name, const QScriptV
QString currentSessionUUID = avatar->getSessionUUID().toString();
if (specificAvatarIdentifiers.isEmpty() || specificAvatarIdentifiers.contains(currentSessionUUID)) {
QJsonObject thisAvatarPalData;
auto myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
if (currentSessionUUID == myAvatar->getSessionUUID().toString()) {
currentSessionUUID = "";
}
thisAvatarPalData.insert("sessionUUID", currentSessionUUID);
thisAvatarPalData.insert("sessionDisplayName", avatar->getSessionDisplayName());
thisAvatarPalData.insert("audioLoudness", avatar->getAudioLoudness());

View file

@ -91,9 +91,11 @@ public:
void updateOtherAvatars(float deltaTime);
void sendIdentityRequest(const QUuid& avatarID) const;
void setMyAvatarDataPacketsPaused(bool puase);
void postUpdate(float deltaTime, const render::ScenePointer& scene);
void clearOtherAvatars();
void clearOtherAvatars() override;
void deleteAllAvatars();
void getObjectsToRemoveFromPhysics(VectorOfMotionStates& motionStates);
@ -229,6 +231,7 @@ private:
int _numAvatarsNotUpdated { 0 };
float _avatarSimulationTime { 0.0f };
bool _shouldRender { true };
bool _myAvatarDataPacketsPaused { false };
mutable int _identityRequestsSent { 0 };
mutable std::mutex _spaceLock;

View file

@ -113,6 +113,7 @@ MyAvatar::MyAvatar(QThread* thread) :
_recentModeReadings(MODE_READINGS_RING_BUFFER_SIZE),
_bodySensorMatrix(),
_goToPending(false),
_goToSafe(true),
_goToPosition(),
_goToOrientation(),
_prevShouldDrawHead(true),
@ -148,7 +149,8 @@ MyAvatar::MyAvatar(QThread* thread) :
});
connect(_skeletonModel.get(), &Model::rigReady, this, &Avatar::rigReady);
connect(_skeletonModel.get(), &Model::rigReset, this, &Avatar::rigReset);
connect(&_skeletonModel->getRig(), &Rig::onLoadComplete, this, &MyAvatar::updateCollisionCapsuleCache);
connect(this, &MyAvatar::sensorToWorldScaleChanged, this, &MyAvatar::updateCollisionCapsuleCache);
using namespace recording;
_skeletonModel->flagAsCauterized();
@ -254,6 +256,7 @@ MyAvatar::MyAvatar(QThread* thread) :
});
connect(&(_skeletonModel->getRig()), SIGNAL(onLoadComplete()), this, SIGNAL(onLoadComplete()));
_characterController.setDensity(_density);
}
@ -509,7 +512,9 @@ void MyAvatar::update(float deltaTime) {
if (_physicsSafetyPending && qApp->isPhysicsEnabled() && _characterController.isEnabledAndReady()) {
// When needed and ready, arrange to check and fix.
_physicsSafetyPending = false;
safeLanding(_goToPosition); // no-op if already safe
if (_goToSafe) {
safeLanding(_goToPosition); // no-op if already safe
}
}
Head* head = getHead();
@ -3019,7 +3024,7 @@ void MyAvatar::goToFeetLocation(const glm::vec3& newPosition,
void MyAvatar::goToLocation(const glm::vec3& newPosition,
bool hasOrientation, const glm::quat& newOrientation,
bool shouldFaceLocation) {
bool shouldFaceLocation, bool withSafeLanding) {
// Most cases of going to a place or user go through this now. Some possible improvements to think about in the future:
// - It would be nice if this used the same teleport steps and smoothing as in the teleport.js script, as long as it
@ -3039,6 +3044,7 @@ void MyAvatar::goToLocation(const glm::vec3& newPosition,
_goToPending = true;
_goToPosition = newPosition;
_goToSafe = withSafeLanding;
_goToOrientation = getWorldOrientation();
if (hasOrientation) {
qCDebug(interfaceapp).nospace() << "MyAvatar goToLocation - new orientation is "
@ -3311,6 +3317,22 @@ bool MyAvatar::getCollisionsEnabled() {
return _characterController.computeCollisionGroup() != BULLET_COLLISION_GROUP_COLLISIONLESS;
}
void MyAvatar::updateCollisionCapsuleCache() {
glm::vec3 start, end;
float radius;
getCapsule(start, end, radius);
QVariantMap capsule;
capsule["start"] = vec3toVariant(start);
capsule["end"] = vec3toVariant(end);
capsule["radius"] = QVariant(radius);
_collisionCapsuleCache.set(capsule);
}
// thread safe
QVariantMap MyAvatar::getCollisionCapsule() const {
return _collisionCapsuleCache.get();
}
void MyAvatar::setCharacterControllerEnabled(bool enabled) {
qCDebug(interfaceapp) << "MyAvatar.characterControllerEnabled is deprecated. Use MyAvatar.collisionsEnabled instead.";
setCollisionsEnabled(enabled);

View file

@ -1017,6 +1017,12 @@ public:
*/
Q_INVOKABLE bool getCollisionsEnabled();
/**jsdoc
* @function MyAvatar.getCollisionCapsule
* @returns {object}
*/
Q_INVOKABLE QVariantMap getCollisionCapsule() const;
/**jsdoc
* @function MyAvatar.setCharacterControllerEnabled
* @param {boolean} enabled
@ -1182,11 +1188,12 @@ public slots:
* @param {boolean} [hasOrientation=false] - Set to <code>true</code> to set the orientation of the avatar.
* @param {Quat} [orientation=Quat.IDENTITY] - The new orientation for the avatar.
* @param {boolean} [shouldFaceLocation=false] - Set to <code>true</code> to position the avatar a short distance away from
* @param {boolean} [withSafeLanding=true] - Set to <code>false</code> MyAvatar::safeLanding will not be called (used when teleporting).
* the new position and orientate the avatar to face the position.
*/
void goToLocation(const glm::vec3& newPosition,
bool hasOrientation = false, const glm::quat& newOrientation = glm::quat(),
bool shouldFaceLocation = false);
bool shouldFaceLocation = false, bool withSafeLanding = true);
/**jsdoc
* @function MyAvatar.goToLocation
* @param {object} properties
@ -1500,6 +1507,7 @@ signals:
private slots:
void leaveDomain();
void updateCollisionCapsuleCache();
protected:
virtual void beParentOfChild(SpatiallyNestablePointer newChild) const override;
@ -1724,6 +1732,7 @@ private:
bool _goToPending { false };
bool _physicsSafetyPending { false };
bool _goToSafe { true };
glm::vec3 _goToPosition;
glm::quat _goToOrientation;

View file

@ -11,6 +11,25 @@
#include "AvatarMotionState.h"
static xColor getLoadingOrbColor(Avatar::LoadingStatus loadingStatus) {
const xColor NO_MODEL_COLOR(0xe3, 0xe3, 0xe3);
const xColor LOAD_MODEL_COLOR(0xef, 0x93, 0xd1);
const xColor LOAD_SUCCESS_COLOR(0x1f, 0xc6, 0xa6);
const xColor LOAD_FAILURE_COLOR(0xc6, 0x21, 0x47);
switch (loadingStatus) {
case Avatar::LoadingStatus::NoModel:
return NO_MODEL_COLOR;
case Avatar::LoadingStatus::LoadModel:
return LOAD_MODEL_COLOR;
case Avatar::LoadingStatus::LoadSuccess:
return LOAD_SUCCESS_COLOR;
case Avatar::LoadingStatus::LoadFailure:
default:
return LOAD_FAILURE_COLOR;
}
}
OtherAvatar::OtherAvatar(QThread* thread) : Avatar(thread) {
// give the pointer to our head to inherited _headData variable from AvatarData
_headData = new Head(this);
@ -48,7 +67,7 @@ void OtherAvatar::createOrb() {
if (_otherAvatarOrbMeshPlaceholderID.isNull()) {
_otherAvatarOrbMeshPlaceholder = std::make_shared<Sphere3DOverlay>();
_otherAvatarOrbMeshPlaceholder->setAlpha(1.0f);
_otherAvatarOrbMeshPlaceholder->setColor({ 0xFF, 0x00, 0xFF });
_otherAvatarOrbMeshPlaceholder->setColor(getLoadingOrbColor(_loadingStatus));
_otherAvatarOrbMeshPlaceholder->setIsSolid(false);
_otherAvatarOrbMeshPlaceholder->setPulseMin(0.5);
_otherAvatarOrbMeshPlaceholder->setPulseMax(1.0);
@ -64,6 +83,13 @@ void OtherAvatar::createOrb() {
}
}
void OtherAvatar::indicateLoadingStatus(LoadingStatus loadingStatus) {
Avatar::indicateLoadingStatus(loadingStatus);
if (_otherAvatarOrbMeshPlaceholder) {
_otherAvatarOrbMeshPlaceholder->setColor(getLoadingOrbColor(_loadingStatus));
}
}
void OtherAvatar::setSpaceIndex(int32_t index) {
assert(_spaceIndex == -1);
_spaceIndex = index;

View file

@ -28,6 +28,7 @@ public:
virtual void instantiableAvatar() override { };
virtual void createOrb() override;
virtual void indicateLoadingStatus(LoadingStatus loadingStatus) override;
void updateOrbPosition();
void removeOrb();

View file

@ -16,7 +16,6 @@
#include "Application.h"
#include "Menu.h"
#include "SceneScriptingInterface.h"
#include "SafeLanding.h"
OctreePacketProcessor::OctreePacketProcessor():
_safeLanding(new SafeLanding())
@ -133,7 +132,3 @@ void OctreePacketProcessor::processPacket(QSharedPointer<ReceivedMessage> messag
void OctreePacketProcessor::startEntitySequence() {
_safeLanding->startEntitySequence(qApp->getEntities());
}
bool OctreePacketProcessor::isLoadSequenceComplete() const {
return _safeLanding->isLoadSequenceComplete();
}

View file

@ -15,7 +15,7 @@
#include <ReceivedPacketProcessor.h>
#include <ReceivedMessage.h>
class SafeLanding;
#include "SafeLanding.h"
/// Handles processing of incoming voxel packets for the interface application. As with other ReceivedPacketProcessor classes
/// the user is responsible for reading inbound packets and adding them to the processing queue by calling queueReceivedPacket()
@ -26,7 +26,8 @@ public:
~OctreePacketProcessor();
void startEntitySequence();
bool isLoadSequenceComplete() const;
bool isLoadSequenceComplete() const { return _safeLanding->isLoadSequenceComplete(); }
float domainLoadingProgress() const { return _safeLanding->loadingProgressPercentage(); }
signals:
void packetVersionMismatch();
@ -40,4 +41,4 @@ private slots:
private:
std::unique_ptr<SafeLanding> _safeLanding;
};
#endif // hifi_OctreePacketProcessor_h
#endif // hifi_OctreePacketProcessor_h

View file

@ -13,6 +13,7 @@
#include "EntityTreeRenderer.h"
#include "ModelEntityItem.h"
#include "InterfaceLogging.h"
#include "Application.h"
const int SafeLanding::SEQUENCE_MODULO = std::numeric_limits<OCTREE_PACKET_SEQUENCE>::max() + 1;
@ -53,6 +54,7 @@ void SafeLanding::startEntitySequence(QSharedPointer<EntityTreeRenderer> entityT
void SafeLanding::stopEntitySequence() {
Locker lock(_lock);
_trackingEntities = false;
_maxTrackedEntityCount = 0;
_initialStart = INVALID_SEQUENCE;
_initialEnd = INVALID_SEQUENCE;
_trackedEntities.clear();
@ -64,20 +66,18 @@ void SafeLanding::addTrackedEntity(const EntityItemID& entityID) {
Locker lock(_lock);
EntityItemPointer entity = _entityTree->findEntityByID(entityID);
if (entity && !entity->getCollisionless()) {
const auto& entityType = entity->getType();
if (entityType == EntityTypes::Model) {
ModelEntityItem * modelEntity = std::dynamic_pointer_cast<ModelEntityItem>(entity).get();
static const std::set<ShapeType> downloadedCollisionTypes
{ SHAPE_TYPE_COMPOUND, SHAPE_TYPE_SIMPLE_COMPOUND, SHAPE_TYPE_STATIC_MESH, SHAPE_TYPE_SIMPLE_HULL };
bool hasAABox;
entity->getAABox(hasAABox);
if (hasAABox && downloadedCollisionTypes.count(modelEntity->getShapeType()) != 0) {
// Only track entities with downloaded collision bodies.
_trackedEntities.emplace(entityID, entity);
}
if (entity) {
_trackedEntities.emplace(entityID, entity);
int trackedEntityCount = (int)_trackedEntities.size();
if (trackedEntityCount > _maxTrackedEntityCount) {
_maxTrackedEntityCount = trackedEntityCount;
}
qCDebug(interfaceapp) << "Safe Landing: Tracking entity " << entity->getItemName();
}
} else {
qCDebug(interfaceapp) << "Safe Landing: Null Entity: " << entityID;
}
}
@ -102,7 +102,7 @@ void SafeLanding::noteReceivedsequenceNumber(int sequenceNumber) {
}
bool SafeLanding::isLoadSequenceComplete() {
if (isEntityPhysicsComplete() && isSequenceNumbersComplete()) {
if (isEntityLoadingComplete() && isSequenceNumbersComplete()) {
Locker lock(_lock);
_trackedEntities.clear();
_initialStart = INVALID_SEQUENCE;
@ -114,6 +114,15 @@ bool SafeLanding::isLoadSequenceComplete() {
return !_trackingEntities;
}
float SafeLanding::loadingProgressPercentage() {
Locker lock(_lock);
if (_maxTrackedEntityCount > 0) {
return ((_maxTrackedEntityCount - _trackedEntities.size()) / (float)_maxTrackedEntityCount);
}
return 0.0f;
}
bool SafeLanding::isSequenceNumbersComplete() {
if (_initialStart != INVALID_SEQUENCE) {
Locker lock(_lock);
@ -132,17 +141,53 @@ bool SafeLanding::isSequenceNumbersComplete() {
return false;
}
bool SafeLanding::isEntityPhysicsComplete() {
Locker lock(_lock);
for (auto entityMapIter = _trackedEntities.begin(); entityMapIter != _trackedEntities.end(); ++entityMapIter) {
auto entity = entityMapIter->second;
if (!entity->shouldBePhysical() || entity->isReadyToComputeShape()) {
entityMapIter = _trackedEntities.erase(entityMapIter);
if (entityMapIter == _trackedEntities.end()) {
break;
bool isEntityPhysicsReady(const EntityItemPointer& entity) {
if (entity && !entity->getCollisionless()) {
const auto& entityType = entity->getType();
if (entityType == EntityTypes::Model) {
ModelEntityItem * modelEntity = std::dynamic_pointer_cast<ModelEntityItem>(entity).get();
static const std::set<ShapeType> downloadedCollisionTypes
{ SHAPE_TYPE_COMPOUND, SHAPE_TYPE_SIMPLE_COMPOUND, SHAPE_TYPE_STATIC_MESH, SHAPE_TYPE_SIMPLE_HULL };
bool hasAABox;
entity->getAABox(hasAABox);
if (hasAABox && downloadedCollisionTypes.count(modelEntity->getShapeType()) != 0) {
return (!entity->shouldBePhysical() || entity->isReadyToComputeShape());
}
}
}
return true;
}
bool SafeLanding::isEntityLoadingComplete() {
Locker lock(_lock);
auto entityTree = qApp->getEntities();
auto entityMapIter = _trackedEntities.begin();
while (entityMapIter != _trackedEntities.end()) {
auto entity = entityMapIter->second;
bool isVisuallyReady = true;
Settings settings;
bool enableInterstitial = settings.value("enableIntersitialMode", false).toBool();
if (enableInterstitial) {
isVisuallyReady = (entity->isVisuallyReady() || !entityTree->renderableForEntityId(entityMapIter->first));
}
if (isEntityPhysicsReady(entity) && isVisuallyReady) {
entityMapIter = _trackedEntities.erase(entityMapIter);
} else {
if (!isVisuallyReady) {
entity->requestRenderUpdate();
}
entityMapIter++;
}
}
return _trackedEntities.empty();
}

View file

@ -18,6 +18,7 @@
#include <QtCore/QSharedPointer>
#include "EntityItem.h"
#include "EntityDynamicInterface.h"
class EntityTreeRenderer;
class EntityItemID;
@ -29,6 +30,7 @@ public:
void setCompletionSequenceNumbers(int first, int last); // 'last' exclusive.
void noteReceivedsequenceNumber(int sequenceNumber);
bool isLoadSequenceComplete();
float loadingProgressPercentage();
private slots:
void addTrackedEntity(const EntityItemID& entityID);
@ -37,7 +39,7 @@ private slots:
private:
bool isSequenceNumbersComplete();
void debugDumpSequenceIDs() const;
bool isEntityPhysicsComplete();
bool isEntityLoadingComplete();
std::mutex _lock;
using Locker = std::lock_guard<std::mutex>;
@ -49,6 +51,7 @@ private:
static constexpr int INVALID_SEQUENCE = -1;
int _initialStart { INVALID_SEQUENCE };
int _initialEnd { INVALID_SEQUENCE };
int _maxTrackedEntityCount { 0 };
struct SequenceLessThan {
bool operator()(const int& a, const int& b) const;

View file

@ -51,6 +51,7 @@ void buildObjectIntersectionsMap(IntersectionType intersectionType, const std::v
QVariantMap collisionPointPair;
collisionPointPair["pointOnPick"] = vec3toVariant(objectIntersection.testCollisionPoint);
collisionPointPair["pointOnObject"] = vec3toVariant(objectIntersection.foundCollisionPoint);
collisionPointPair["normalOnPick"] = vec3toVariant(objectIntersection.collisionNormal);
collisionPointPairs[objectIntersection.foundID].append(collisionPointPair);
}
@ -397,7 +398,7 @@ PickResultPointer CollisionPick::getEntityIntersection(const CollisionRegion& pi
}
getShapeInfoReady(pick);
auto entityIntersections = _physicsEngine->contactTest(USER_COLLISION_MASK_ENTITIES, *_mathPick.shapeInfo, pick.transform, USER_COLLISION_GROUP_DYNAMIC, pick.threshold);
auto entityIntersections = _physicsEngine->contactTest(USER_COLLISION_MASK_ENTITIES, *_mathPick.shapeInfo, pick.transform, pick.collisionGroup, pick.threshold);
filterIntersections(entityIntersections);
return std::make_shared<CollisionPickResult>(pick, entityIntersections, std::vector<ContactTestResult>());
}
@ -413,13 +414,13 @@ PickResultPointer CollisionPick::getAvatarIntersection(const CollisionRegion& pi
}
getShapeInfoReady(pick);
auto avatarIntersections = _physicsEngine->contactTest(USER_COLLISION_MASK_AVATARS, *_mathPick.shapeInfo, pick.transform, USER_COLLISION_GROUP_DYNAMIC, pick.threshold);
auto avatarIntersections = _physicsEngine->contactTest(USER_COLLISION_MASK_AVATARS, *_mathPick.shapeInfo, pick.transform, pick.collisionGroup, pick.threshold);
filterIntersections(avatarIntersections);
return std::make_shared<CollisionPickResult>(pick, std::vector<ContactTestResult>(), avatarIntersections);
}
PickResultPointer CollisionPick::getHUDIntersection(const CollisionRegion& pick) {
return std::make_shared<CollisionPickResult>(pick.toVariantMap(), std::vector<ContactTestResult>(), std::vector<ContactTestResult>());
return std::make_shared<CollisionPickResult>(pick, std::vector<ContactTestResult>(), std::vector<ContactTestResult>());
}
Transform CollisionPick::getResultTransform() const {

View file

@ -70,6 +70,9 @@ protected:
CollisionRegion _mathPick;
PhysicsEnginePointer _physicsEngine;
QSharedPointer<GeometryResource> _cachedResource;
// Options for what information to get from collision results
bool _includeNormals;
};
#endif // hifi_CollisionPick_h

View file

@ -382,9 +382,8 @@ void ParabolaPointer::RenderState::ParabolaRenderItem::updateBounds() {
const gpu::PipelinePointer ParabolaPointer::RenderState::ParabolaRenderItem::getParabolaPipeline() {
if (!_parabolaPipeline || !_transparentParabolaPipeline) {
gpu::ShaderPointer program = gpu::Shader::createProgram(shader::render_utils::program::parabola);
{
gpu::ShaderPointer program = gpu::Shader::createProgram(shader::render_utils::program::parabola);
auto state = std::make_shared<gpu::State>();
state->setDepthTest(true, true, gpu::LESS_EQUAL);
state->setBlendFunction(false,
@ -396,6 +395,7 @@ const gpu::PipelinePointer ParabolaPointer::RenderState::ParabolaRenderItem::get
}
{
gpu::ShaderPointer program = gpu::Shader::createProgram(shader::render_utils::program::parabola_translucent);
auto state = std::make_shared<gpu::State>();
state->setDepthTest(true, true, gpu::LESS_EQUAL);
state->setBlendFunction(true,

View file

@ -270,6 +270,8 @@ unsigned int PickScriptingInterface::createParabolaPick(const QVariant& properti
* @property {Quat} orientation - The orientation of the collision region, relative to a parent if defined.
* @property {float} threshold - The approximate minimum penetration depth for a test object to be considered in contact with the collision region.
* The depth is measured in world space, but will scale with the parent if defined.
* @property {CollisionMask} [collisionGroup=8] - The type of object this collision pick collides as. Objects whose collision masks overlap with the pick's collision group
* will be considered colliding with the pick.
* @property {Uuid} parentID - The ID of the parent, either an avatar, an entity, or an overlay.
* @property {number} parentJointIndex - The joint of the parent to parent to, for example, the joints on the model of an avatar. (default = 0, no joint)
* @property {string} joint - If "Mouse," parents the pick to the mouse. If "Avatar," parents the pick to MyAvatar's head. Otherwise, parents to the joint of the given name on MyAvatar.

View file

@ -167,6 +167,7 @@ public:
* @typedef {object} CollisionContact
* @property {Vec3} pointOnPick A point representing a penetration of the object's surface into the volume of the pick, in world space.
* @property {Vec3} pointOnObject A point representing a penetration of the pick's surface into the volume of the found object, in world space.
* @property {Vec3} normalOnPick The normalized vector pointing away from the pick, representing the direction of collision.
*/
/**jsdoc

View file

@ -409,6 +409,10 @@ glm::vec2 WindowScriptingInterface::getDeviceSize() const {
return qApp->getDeviceSize();
}
int WindowScriptingInterface::getLastDomainConnectionError() const {
return DependencyManager::get<NodeList>()->getDomainHandler().getLastDomainConnectionError();
}
int WindowScriptingInterface::getX() {
return qApp->getWindow()->geometry().x();
}
@ -584,3 +588,8 @@ void WindowScriptingInterface::onMessageBoxSelected(int button) {
_messageBoxes.remove(id);
}
}
float WindowScriptingInterface::domainLoadingProgress() {
return qApp->getOctreePacketProcessor().domainLoadingProgress();
}

View file

@ -491,6 +491,13 @@ public slots:
*/
glm::vec2 getDeviceSize() const;
/**jsdoc
* Gets the last domain connection error when a connection is refused.
* @function Window.getLastDomainConnectionError
* @returns {Window.ConnectionRefusedReason} Integer number that enumerates the last domain connection refused.
*/
int getLastDomainConnectionError() const;
/**jsdoc
* Open a non-modal message box that can have a variety of button combinations. See also,
* {@link Window.updateMessageBox|updateMessageBox} and {@link Window.closeMessageBox|closeMessageBox}.
@ -561,6 +568,8 @@ public slots:
*/
void closeMessageBox(int id);
float domainLoadingProgress();
private slots:
void onWindowGeometryChanged(const QRect& geometry);
void onMessageBoxSelected(int button);

View file

@ -19,6 +19,7 @@
#include <plugins/PluginManager.h>
#include <plugins/SteamClientPlugin.h>
#include <ui/TabletScriptingInterface.h>
#include <UserActivityLogger.h>
#include "AccountManager.h"
#include "DependencyManager.h"
@ -37,11 +38,19 @@ LoginDialog::LoginDialog(QQuickItem *parent) : OffscreenQmlDialog(parent) {
connect(accountManager.data(), &AccountManager::loginFailed,
this, &LoginDialog::handleLoginFailed);
#endif
}
void LoginDialog::showWithSelection()
{
LoginDialog::~LoginDialog() {
Setting::Handle<bool> loginDialogPoppedUp{ "loginDialogPoppedUp", false };
if (loginDialogPoppedUp.get()) {
QJsonObject data;
data["action"] = "user opted out";
UserActivityLogger::getInstance().logAction("encourageLoginDialog", data);
}
loginDialogPoppedUp.set(false);
}
void LoginDialog::showWithSelection() {
auto tabletScriptingInterface = DependencyManager::get<TabletScriptingInterface>();
auto tablet = dynamic_cast<TabletProxy*>(tabletScriptingInterface->getTablet("com.highfidelity.interface.tablet.system"));
auto hmd = DependencyManager::get<HMDScriptingInterface>();
@ -73,9 +82,7 @@ void LoginDialog::toggleAction() {
} else {
// change the menu item to login
loginAction->setText("Login / Sign Up");
connection = connect(loginAction, &QAction::triggered, [] {
LoginDialog::showWithSelection();
});
connection = connect(loginAction, &QAction::triggered, [] { LoginDialog::showWithSelection(); });
}
}
@ -158,7 +165,6 @@ void LoginDialog::createAccountFromStream(QString username) {
QJsonDocument(payload).toJson());
});
}
}
void LoginDialog::openUrl(const QString& url) const {
@ -200,25 +206,24 @@ void LoginDialog::createFailed(QNetworkReply* reply) {
}
void LoginDialog::signup(const QString& email, const QString& username, const QString& password) {
JSONCallbackParameters callbackParams;
callbackParams.callbackReceiver = this;
callbackParams.jsonCallbackMethod = "signupCompleted";
callbackParams.errorCallbackMethod = "signupFailed";
QJsonObject payload;
QJsonObject userObject;
userObject.insert("email", email);
userObject.insert("username", username);
userObject.insert("password", password);
payload.insert("user", userObject);
static const QString API_SIGNUP_PATH = "api/v1/users";
qDebug() << "Sending a request to create an account for" << username;
auto accountManager = DependencyManager::get<AccountManager>();
accountManager->sendRequest(API_SIGNUP_PATH, AccountManagerAuth::None,
QNetworkAccessManager::PostOperation, callbackParams,
@ -240,41 +245,37 @@ QString errorStringFromAPIObject(const QJsonValue& apiObject) {
}
void LoginDialog::signupFailed(QNetworkReply* reply) {
// parse the returned JSON to see what the problem was
auto jsonResponse = QJsonDocument::fromJson(reply->readAll());
static const QString RESPONSE_DATA_KEY = "data";
auto dataJsonValue = jsonResponse.object()[RESPONSE_DATA_KEY];
if (dataJsonValue.isObject()) {
auto dataObject = dataJsonValue.toObject();
static const QString EMAIL_DATA_KEY = "email";
static const QString USERNAME_DATA_KEY = "username";
static const QString PASSWORD_DATA_KEY = "password";
QStringList errorStringList;
if (dataObject.contains(EMAIL_DATA_KEY)) {
errorStringList.append(QString("Email %1.").arg(errorStringFromAPIObject(dataObject[EMAIL_DATA_KEY])));
}
if (dataObject.contains(USERNAME_DATA_KEY)) {
errorStringList.append(QString("Username %1.").arg(errorStringFromAPIObject(dataObject[USERNAME_DATA_KEY])));
}
if (dataObject.contains(PASSWORD_DATA_KEY)) {
errorStringList.append(QString("Password %1.").arg(errorStringFromAPIObject(dataObject[PASSWORD_DATA_KEY])));
}
emit handleSignupFailed(errorStringList.join('\n'));
} else {
static const QString DEFAULT_SIGN_UP_FAILURE_MESSAGE = "There was an unknown error while creating your account. Please try again later.";
emit handleSignupFailed(DEFAULT_SIGN_UP_FAILURE_MESSAGE);
}
}

View file

@ -27,7 +27,10 @@ public:
LoginDialog(QQuickItem* parent = nullptr);
~LoginDialog();
static void showWithSelection();
signals:
void handleLoginCompleted();
void handleLoginFailed();
@ -62,7 +65,6 @@ protected slots:
Q_INVOKABLE void signup(const QString& email, const QString& username, const QString& password);
Q_INVOKABLE void openUrl(const QString& url) const;
};
#endif // hifi_LoginDialog_h

View file

@ -106,6 +106,10 @@ extern std::atomic<size_t> DECIMATED_TEXTURE_COUNT;
extern std::atomic<size_t> RECTIFIED_TEXTURE_COUNT;
void Stats::updateStats(bool force) {
if (qApp->isInterstitialMode()) {
return;
}
QQuickItem* parent = parentItem();
if (!force) {
if (!Menu::getInstance()->isOptionChecked(MenuOption::Stats)) {

View file

@ -252,12 +252,6 @@ bool ContextOverlayInterface::destroyContextOverlay(const EntityItemID& entityIt
void ContextOverlayInterface::contextOverlays_mousePressOnOverlay(const OverlayID& overlayID, const PointerEvent& event) {
if (overlayID == _contextOverlayID && event.getButton() == PointerEvent::PrimaryButton) {
qCDebug(context_overlay) << "Clicked Context Overlay. Entity ID:" << _currentEntityWithContextOverlay << "Overlay ID:" << overlayID;
Setting::Handle<bool> _settingSwitch{ "commerce", true };
if (_settingSwitch.get()) {
openInspectionCertificate();
} else {
openMarketplace();
}
emit contextOverlayClicked(_currentEntityWithContextOverlay);
_contextOverlayJustClicked = true;
}
@ -390,34 +384,6 @@ void ContextOverlayInterface::requestOwnershipVerification(const QUuid& entityID
}
}
static const QString INSPECTION_CERTIFICATE_QML_PATH = "hifi/commerce/inspectionCertificate/InspectionCertificate.qml";
void ContextOverlayInterface::openInspectionCertificate() {
// lets open the tablet to the inspection certificate QML
if (!_currentEntityWithContextOverlay.isNull() && _entityMarketplaceID.length() > 0) {
setLastInspectedEntity(_currentEntityWithContextOverlay);
auto tablet = dynamic_cast<TabletProxy*>(_tabletScriptingInterface->getTablet("com.highfidelity.interface.tablet.system"));
tablet->loadQMLSource(INSPECTION_CERTIFICATE_QML_PATH);
_hmdScriptingInterface->openTablet();
}
}
static const QString MARKETPLACE_BASE_URL = NetworkingConstants::METAVERSE_SERVER_URL().toString() + "/marketplace/items/";
void ContextOverlayInterface::openMarketplace() {
// lets open the tablet and go to the current item in
// the marketplace (if the current entity has a
// marketplaceID)
if (!_currentEntityWithContextOverlay.isNull() && _entityMarketplaceID.length() > 0) {
auto tablet = dynamic_cast<TabletProxy*>(_tabletScriptingInterface->getTablet("com.highfidelity.interface.tablet.system"));
// construct the url to the marketplace item
QString url = MARKETPLACE_BASE_URL + _entityMarketplaceID;
QString MARKETPLACES_INJECT_SCRIPT_PATH = "file:///" + qApp->applicationDirPath() + "/scripts/system/html/js/marketplacesInject.js";
tablet->gotoWebScreen(url, MARKETPLACES_INJECT_SCRIPT_PATH);
_hmdScriptingInterface->openTablet();
_isInMarketplaceInspectionMode = true;
}
}
void ContextOverlayInterface::enableEntityHighlight(const EntityItemID& entityItemID) {
_selectionScriptingInterface->addToSelectedItemsList("contextOverlayHighlightList", "entity", entityItemID);
}

View file

@ -94,8 +94,6 @@ private:
bool _isInMarketplaceInspectionMode { false };
void openInspectionCertificate();
void openMarketplace();
void enableEntityHighlight(const EntityItemID& entityItemID);
void disableEntityHighlight(const EntityItemID& entityItemID);

View file

@ -132,7 +132,9 @@ void Web3DOverlay::destroyWebSurface() {
if (rootItem && rootItem->objectName() == "tabletRoot") {
auto tabletScriptingInterface = DependencyManager::get<TabletScriptingInterface>();
tabletScriptingInterface->setQmlTabletRoot("com.highfidelity.interface.tablet.system", nullptr);
if (tabletScriptingInterface) {
tabletScriptingInterface->setQmlTabletRoot("com.highfidelity.interface.tablet.system", nullptr);
}
}
// Fix for crash in QtWebEngineCore when rapidly switching domains

View file

@ -305,6 +305,16 @@ void AudioClient::audioMixerKilled() {
emit disconnected();
}
void AudioClient::setAudioPaused(bool pause) {
if (_audioPaused != pause) {
_audioPaused = pause;
if (!_audioPaused) {
negotiateAudioFormat();
}
}
}
QAudioDeviceInfo getNamedAudioDeviceForMode(QAudio::Mode mode, const QString& deviceName) {
QAudioDeviceInfo result;
foreach(QAudioDeviceInfo audioDevice, getAvailableDevices(mode)) {
@ -651,7 +661,6 @@ void AudioClient::stop() {
}
void AudioClient::handleAudioEnvironmentDataPacket(QSharedPointer<ReceivedMessage> message) {
char bitset;
message->readPrimitive(&bitset);
@ -664,11 +673,10 @@ void AudioClient::handleAudioEnvironmentDataPacket(QSharedPointer<ReceivedMessag
_receivedAudioStream.setReverb(reverbTime, wetLevel);
} else {
_receivedAudioStream.clearReverb();
}
}
}
void AudioClient::handleAudioDataPacket(QSharedPointer<ReceivedMessage> message) {
if (message->getType() == PacketType::SilentAudioFrame) {
_silentInbound.increment();
} else {
@ -1026,80 +1034,82 @@ void AudioClient::handleLocalEchoAndReverb(QByteArray& inputByteArray) {
}
void AudioClient::handleAudioInput(QByteArray& audioBuffer) {
if (_muted) {
_lastInputLoudness = 0.0f;
_timeSinceLastClip = 0.0f;
} else {
int16_t* samples = reinterpret_cast<int16_t*>(audioBuffer.data());
int numSamples = audioBuffer.size() / AudioConstants::SAMPLE_SIZE;
int numFrames = numSamples / (_isStereoInput ? AudioConstants::STEREO : AudioConstants::MONO);
if (_isNoiseGateEnabled) {
// The audio gate includes DC removal
_audioGate->render(samples, samples, numFrames);
} else {
_audioGate->removeDC(samples, samples, numFrames);
}
int32_t loudness = 0;
assert(numSamples < 65536); // int32_t loudness cannot overflow
bool didClip = false;
for (int i = 0; i < numSamples; ++i) {
const int32_t CLIPPING_THRESHOLD = (int32_t)(AudioConstants::MAX_SAMPLE_VALUE * 0.9f);
int32_t sample = std::abs((int32_t)samples[i]);
loudness += sample;
didClip |= (sample > CLIPPING_THRESHOLD);
}
_lastInputLoudness = (float)loudness / numSamples;
if (didClip) {
if (!_audioPaused) {
if (_muted) {
_lastInputLoudness = 0.0f;
_timeSinceLastClip = 0.0f;
} else if (_timeSinceLastClip >= 0.0f) {
_timeSinceLastClip += (float)numSamples / (float)AudioConstants::SAMPLE_RATE;
} else {
int16_t* samples = reinterpret_cast<int16_t*>(audioBuffer.data());
int numSamples = audioBuffer.size() / AudioConstants::SAMPLE_SIZE;
int numFrames = numSamples / (_isStereoInput ? AudioConstants::STEREO : AudioConstants::MONO);
if (_isNoiseGateEnabled) {
// The audio gate includes DC removal
_audioGate->render(samples, samples, numFrames);
} else {
_audioGate->removeDC(samples, samples, numFrames);
}
int32_t loudness = 0;
assert(numSamples < 65536); // int32_t loudness cannot overflow
bool didClip = false;
for (int i = 0; i < numSamples; ++i) {
const int32_t CLIPPING_THRESHOLD = (int32_t)(AudioConstants::MAX_SAMPLE_VALUE * 0.9f);
int32_t sample = std::abs((int32_t)samples[i]);
loudness += sample;
didClip |= (sample > CLIPPING_THRESHOLD);
}
_lastInputLoudness = (float)loudness / numSamples;
if (didClip) {
_timeSinceLastClip = 0.0f;
} else if (_timeSinceLastClip >= 0.0f) {
_timeSinceLastClip += (float)numSamples / (float)AudioConstants::SAMPLE_RATE;
}
emit inputReceived(audioBuffer);
}
emit inputReceived(audioBuffer);
emit inputLoudnessChanged(_lastInputLoudness);
// state machine to detect gate opening and closing
bool audioGateOpen = (_lastInputLoudness != 0.0f);
bool openedInLastBlock = !_audioGateOpen && audioGateOpen; // the gate just opened
bool closedInLastBlock = _audioGateOpen && !audioGateOpen; // the gate just closed
_audioGateOpen = audioGateOpen;
if (openedInLastBlock) {
emit noiseGateOpened();
} else if (closedInLastBlock) {
emit noiseGateClosed();
}
// the codec must be flushed to silence before sending silent packets,
// so delay the transition to silent packets by one packet after becoming silent.
auto packetType = _shouldEchoToServer ? PacketType::MicrophoneAudioWithEcho : PacketType::MicrophoneAudioNoEcho;
if (!audioGateOpen && !closedInLastBlock) {
packetType = PacketType::SilentAudioFrame;
_silentOutbound.increment();
} else {
_audioOutbound.increment();
}
Transform audioTransform;
audioTransform.setTranslation(_positionGetter());
audioTransform.setRotation(_orientationGetter());
QByteArray encodedBuffer;
if (_encoder) {
_encoder->encode(audioBuffer, encodedBuffer);
} else {
encodedBuffer = audioBuffer;
}
emitAudioPacket(encodedBuffer.data(), encodedBuffer.size(), _outgoingAvatarAudioSequenceNumber, _isStereoInput,
audioTransform, avatarBoundingBoxCorner, avatarBoundingBoxScale,
packetType, _selectedCodecName);
_stats.sentPacket();
}
emit inputLoudnessChanged(_lastInputLoudness);
// state machine to detect gate opening and closing
bool audioGateOpen = (_lastInputLoudness != 0.0f);
bool openedInLastBlock = !_audioGateOpen && audioGateOpen; // the gate just opened
bool closedInLastBlock = _audioGateOpen && !audioGateOpen; // the gate just closed
_audioGateOpen = audioGateOpen;
if (openedInLastBlock) {
emit noiseGateOpened();
} else if (closedInLastBlock) {
emit noiseGateClosed();
}
// the codec must be flushed to silence before sending silent packets,
// so delay the transition to silent packets by one packet after becoming silent.
auto packetType = _shouldEchoToServer ? PacketType::MicrophoneAudioWithEcho : PacketType::MicrophoneAudioNoEcho;
if (!audioGateOpen && !closedInLastBlock) {
packetType = PacketType::SilentAudioFrame;
_silentOutbound.increment();
} else {
_audioOutbound.increment();
}
Transform audioTransform;
audioTransform.setTranslation(_positionGetter());
audioTransform.setRotation(_orientationGetter());
QByteArray encodedBuffer;
if (_encoder) {
_encoder->encode(audioBuffer, encodedBuffer);
} else {
encodedBuffer = audioBuffer;
}
emitAudioPacket(encodedBuffer.data(), encodedBuffer.size(), _outgoingAvatarAudioSequenceNumber, _isStereoInput,
audioTransform, avatarBoundingBoxCorner, avatarBoundingBoxScale,
packetType, _selectedCodecName);
_stats.sentPacket();
}
void AudioClient::handleMicAudioInput() {

View file

@ -162,6 +162,7 @@ public:
bool startRecording(const QString& filename);
void stopRecording();
void setAudioPaused(bool pause);
#ifdef Q_OS_WIN
@ -416,6 +417,7 @@ private:
QVector<AudioInjectorPointer> _activeLocalAudioInjectors;
bool _isPlayingBackRecording { false };
bool _audioPaused { false };
CodecPluginPointer _codec;
QString _selectedCodecName;

View file

@ -214,19 +214,11 @@ Avatar::Avatar(QThread* thread) :
_leftPointerGeometryID = geometryCache->allocateID();
_rightPointerGeometryID = geometryCache->allocateID();
_lastRenderUpdateTime = usecTimestampNow();
indicateLoadingStatus(LoadingStatus::NoModel);
}
Avatar::~Avatar() {
auto treeRenderer = DependencyManager::get<EntityTreeRenderer>();
EntityTreePointer entityTree = treeRenderer ? treeRenderer->getTree() : nullptr;
if (entityTree) {
entityTree->withWriteLock([&] {
AvatarEntityMap avatarEntities = getAvatarEntityData();
for (auto entityID : avatarEntities.keys()) {
entityTree->deleteEntity(entityID, true, true);
}
});
}
auto geometryCache = DependencyManager::get<GeometryCache>();
if (geometryCache) {
geometryCache->releaseID(_nameRectGeometryID);
@ -471,6 +463,19 @@ void Avatar::updateAvatarEntities() {
setAvatarEntityDataChanged(false);
}
void Avatar::removeAvatarEntitiesFromTree() {
auto treeRenderer = DependencyManager::get<EntityTreeRenderer>();
EntityTreePointer entityTree = treeRenderer ? treeRenderer->getTree() : nullptr;
if (entityTree) {
entityTree->withWriteLock([&] {
AvatarEntityMap avatarEntities = getAvatarEntityData();
for (auto entityID : avatarEntities.keys()) {
entityTree->deleteEntity(entityID, true, true);
}
});
}
}
void Avatar::relayJointDataToChildren() {
forEachChild([&](SpatiallyNestablePointer child) {
if (child->getNestableType() == NestableType::Entity) {
@ -1469,12 +1474,15 @@ void Avatar::scaleVectorRelativeToPosition(glm::vec3 &positionToScale) const {
}
void Avatar::setSkeletonModelURL(const QUrl& skeletonModelURL) {
if (!isMyAvatar()) {
createOrb();
}
AvatarData::setSkeletonModelURL(skeletonModelURL);
if (QThread::currentThread() == thread()) {
if (!isMyAvatar()) {
createOrb();
}
_skeletonModel->setURL(_skeletonModelURL);
indicateLoadingStatus(LoadingStatus::LoadModel);
} else {
QMetaObject::invokeMethod(_skeletonModel.get(), "setURL", Qt::QueuedConnection, Q_ARG(QUrl, _skeletonModelURL));
}
@ -1487,11 +1495,12 @@ void Avatar::setModelURLFinished(bool success) {
_reconstructSoftEntitiesJointMap = true;
if (!success && _skeletonModelURL != AvatarData::defaultFullAvatarModelUrl()) {
indicateLoadingStatus(LoadingStatus::LoadFailure);
const int MAX_SKELETON_DOWNLOAD_ATTEMPTS = 4; // NOTE: we don't want to be as generous as ResourceCache is, we only want 4 attempts
if (_skeletonModel->getResourceDownloadAttemptsRemaining() <= 0 ||
_skeletonModel->getResourceDownloadAttempts() > MAX_SKELETON_DOWNLOAD_ATTEMPTS) {
qCWarning(avatars_renderer) << "Using default after failing to load Avatar model: " << _skeletonModelURL
<< "after" << _skeletonModel->getResourceDownloadAttempts() << "attempts.";
<< "after" << _skeletonModel->getResourceDownloadAttempts() << "attempts.";
// call _skeletonModel.setURL, but leave our copy of _skeletonModelURL alone. This is so that
// we don't redo this every time we receive an identity packet from the avatar with the bad url.
QMetaObject::invokeMethod(_skeletonModel.get(), "setURL",
@ -1502,6 +1511,9 @@ void Avatar::setModelURLFinished(bool success) {
<< "out of:" << MAX_SKELETON_DOWNLOAD_ATTEMPTS;
}
}
if (success) {
indicateLoadingStatus(LoadingStatus::LoadSuccess);
}
}
// rig is ready

View file

@ -143,6 +143,7 @@ public:
void init();
void updateAvatarEntities();
void removeAvatarEntitiesFromTree();
void simulate(float deltaTime, bool inView);
virtual void simulateAttachments(float deltaTime);
@ -177,6 +178,14 @@ public:
virtual bool isMyAvatar() const override { return false; }
virtual void createOrb() { }
enum class LoadingStatus {
NoModel,
LoadModel,
LoadSuccess,
LoadFailure
};
virtual void indicateLoadingStatus(LoadingStatus loadingStatus) { _loadingStatus = loadingStatus; }
virtual QVector<glm::quat> getJointRotations() const override;
using AvatarData::getJointRotation;
virtual glm::quat getJointRotation(int index) const override;
@ -621,6 +630,7 @@ protected:
static const float ATTACHMENT_LOADING_PRIORITY;
QUuid _transitEffectID{ QUuid::createUuid() };
LoadingStatus _loadingStatus { LoadingStatus::NoModel };
};
#endif // hifi_Avatar_h

View file

@ -363,13 +363,13 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent
memcpy(destinationBuffer, &packetStateFlags, sizeof(packetStateFlags));
destinationBuffer += sizeof(packetStateFlags);
#define AVATAR_MEMCPY(src) \
memcpy(destinationBuffer, &(src), sizeof(src)); \
destinationBuffer += sizeof(src);
if (hasAvatarGlobalPosition) {
auto startSection = destinationBuffer;
auto data = reinterpret_cast<AvatarDataPacket::AvatarGlobalPosition*>(destinationBuffer);
data->globalPosition[0] = _globalPosition.x;
data->globalPosition[1] = _globalPosition.y;
data->globalPosition[2] = _globalPosition.z;
destinationBuffer += sizeof(AvatarDataPacket::AvatarGlobalPosition);
AVATAR_MEMCPY(_globalPosition);
int numBytes = destinationBuffer - startSection;
@ -380,17 +380,8 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent
if (hasAvatarBoundingBox) {
auto startSection = destinationBuffer;
auto data = reinterpret_cast<AvatarDataPacket::AvatarBoundingBox*>(destinationBuffer);
data->avatarDimensions[0] = _globalBoundingBoxDimensions.x;
data->avatarDimensions[1] = _globalBoundingBoxDimensions.y;
data->avatarDimensions[2] = _globalBoundingBoxDimensions.z;
data->boundOriginOffset[0] = _globalBoundingBoxOffset.x;
data->boundOriginOffset[1] = _globalBoundingBoxOffset.y;
data->boundOriginOffset[2] = _globalBoundingBoxOffset.z;
destinationBuffer += sizeof(AvatarDataPacket::AvatarBoundingBox);
AVATAR_MEMCPY(_globalBoundingBoxDimensions);
AVATAR_MEMCPY(_globalBoundingBoxOffset);
int numBytes = destinationBuffer - startSection;
if (outboundDataRateOut) {
@ -424,13 +415,7 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent
if (hasLookAtPosition) {
auto startSection = destinationBuffer;
auto data = reinterpret_cast<AvatarDataPacket::LookAtPosition*>(destinationBuffer);
auto lookAt = _headData->getLookAtPosition();
data->lookAtPosition[0] = lookAt.x;
data->lookAtPosition[1] = lookAt.y;
data->lookAtPosition[2] = lookAt.z;
destinationBuffer += sizeof(AvatarDataPacket::LookAtPosition);
AVATAR_MEMCPY(_headData->getLookAtPosition());
int numBytes = destinationBuffer - startSection;
if (outboundDataRateOut) {
outboundDataRateOut->lookAtPositionRate.increment(numBytes);
@ -531,12 +516,8 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent
if (hasAvatarLocalPosition) {
auto startSection = destinationBuffer;
auto data = reinterpret_cast<AvatarDataPacket::AvatarLocalPosition*>(destinationBuffer);
auto localPosition = getLocalPosition();
data->localPosition[0] = localPosition.x;
data->localPosition[1] = localPosition.y;
data->localPosition[2] = localPosition.z;
destinationBuffer += sizeof(AvatarDataPacket::AvatarLocalPosition);
const auto localPosition = getLocalPosition();
AVATAR_MEMCPY(localPosition);
int numBytes = destinationBuffer - startSection;
if (outboundDataRateOut) {
@ -567,19 +548,24 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent
}
}
QVector<JointData> jointData;
if (hasJointData || hasJointDefaultPoseFlags) {
QReadLocker readLock(&_jointDataLock);
jointData = _jointData;
}
// If it is connected, pack up the data
if (hasJointData) {
auto startSection = destinationBuffer;
QReadLocker readLock(&_jointDataLock);
// joint rotation data
int numJoints = _jointData.size();
int numJoints = jointData.size();
*destinationBuffer++ = (uint8_t)numJoints;
unsigned char* validityPosition = destinationBuffer;
unsigned char validity = 0;
int validityBit = 0;
int numValidityBytes = (int)std::ceil(numJoints / (float)BITS_IN_BYTE);
int numValidityBytes = calcBitVectorSize(numJoints);
#ifdef WANT_DEBUG
int rotationSentCount = 0;
@ -589,43 +575,37 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent
destinationBuffer += numValidityBytes; // Move pointer past the validity bytes
// sentJointDataOut and lastSentJointData might be the same vector
// build sentJointDataOut locally and then swap it at the end.
QVector<JointData> localSentJointDataOut;
if (sentJointDataOut) {
localSentJointDataOut.resize(numJoints); // Make sure the destination is resized before using it
sentJointDataOut->resize(numJoints); // Make sure the destination is resized before using it
}
float minRotationDOT = !distanceAdjust ? AVATAR_MIN_ROTATION_DOT : getDistanceBasedMinRotationDOT(viewerPosition);
float minRotationDOT = (distanceAdjust && cullSmallChanges) ? getDistanceBasedMinRotationDOT(viewerPosition) : AVATAR_MIN_ROTATION_DOT;
for (int i = 0; i < _jointData.size(); i++) {
const JointData& data = _jointData[i];
for (int i = 0; i < jointData.size(); i++) {
const JointData& data = jointData[i];
const JointData& last = lastSentJointData[i];
if (!data.rotationIsDefaultPose) {
bool mustSend = sendAll || last.rotationIsDefaultPose;
if (mustSend || last.rotation != data.rotation) {
bool largeEnoughRotation = true;
if (cullSmallChanges) {
// The dot product for smaller rotations is a smaller number.
// So if the dot() is less than the value, then the rotation is a larger angle of rotation
largeEnoughRotation = fabsf(glm::dot(last.rotation, data.rotation)) < minRotationDOT;
}
if (mustSend || !cullSmallChanges || largeEnoughRotation) {
validity |= (1 << validityBit);
// The dot product for larger rotations is a lower number.
// So if the dot() is less than the value, then the rotation is a larger angle of rotation
if (sendAll || last.rotationIsDefaultPose || (!cullSmallChanges && last.rotation != data.rotation)
|| (cullSmallChanges && glm::dot(last.rotation, data.rotation) < minRotationDOT) ) {
validity |= (1 << validityBit);
#ifdef WANT_DEBUG
rotationSentCount++;
rotationSentCount++;
#endif
destinationBuffer += packOrientationQuatToSixBytes(destinationBuffer, data.rotation);
destinationBuffer += packOrientationQuatToSixBytes(destinationBuffer, data.rotation);
if (sentJointDataOut) {
localSentJointDataOut[i].rotation = data.rotation;
localSentJointDataOut[i].rotationIsDefaultPose = false;
}
if (sentJointDataOut) {
(*sentJointDataOut)[i].rotation = data.rotation;
}
}
}
if (sentJointDataOut) {
(*sentJointDataOut)[i].rotationIsDefaultPose = data.rotationIsDefaultPose;
}
if (++validityBit == BITS_IN_BYTE) {
*validityPosition++ = validity;
validityBit = validity = 0;
@ -647,35 +627,38 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent
destinationBuffer += numValidityBytes; // Move pointer past the validity bytes
float minTranslation = !distanceAdjust ? AVATAR_MIN_TRANSLATION : getDistanceBasedMinTranslationDistance(viewerPosition);
float minTranslation = (distanceAdjust && cullSmallChanges) ? getDistanceBasedMinTranslationDistance(viewerPosition) : AVATAR_MIN_TRANSLATION;
float maxTranslationDimension = 0.0;
for (int i = 0; i < _jointData.size(); i++) {
const JointData& data = _jointData[i];
for (int i = 0; i < jointData.size(); i++) {
const JointData& data = jointData[i];
const JointData& last = lastSentJointData[i];
if (!data.translationIsDefaultPose) {
bool mustSend = sendAll || last.translationIsDefaultPose;
if (mustSend || last.translation != data.translation) {
if (mustSend || !cullSmallChanges || glm::distance(data.translation, lastSentJointData[i].translation) > minTranslation) {
validity |= (1 << validityBit);
if (sendAll || last.translationIsDefaultPose || (!cullSmallChanges && last.translation != data.translation)
|| (cullSmallChanges && glm::distance(data.translation, lastSentJointData[i].translation) > minTranslation)) {
validity |= (1 << validityBit);
#ifdef WANT_DEBUG
translationSentCount++;
translationSentCount++;
#endif
maxTranslationDimension = glm::max(fabsf(data.translation.x), maxTranslationDimension);
maxTranslationDimension = glm::max(fabsf(data.translation.y), maxTranslationDimension);
maxTranslationDimension = glm::max(fabsf(data.translation.z), maxTranslationDimension);
maxTranslationDimension = glm::max(fabsf(data.translation.x), maxTranslationDimension);
maxTranslationDimension = glm::max(fabsf(data.translation.y), maxTranslationDimension);
maxTranslationDimension = glm::max(fabsf(data.translation.z), maxTranslationDimension);
destinationBuffer +=
packFloatVec3ToSignedTwoByteFixed(destinationBuffer, data.translation, TRANSLATION_COMPRESSION_RADIX);
destinationBuffer +=
packFloatVec3ToSignedTwoByteFixed(destinationBuffer, data.translation, TRANSLATION_COMPRESSION_RADIX);
if (sentJointDataOut) {
localSentJointDataOut[i].translation = data.translation;
localSentJointDataOut[i].translationIsDefaultPose = false;
}
if (sentJointDataOut) {
(*sentJointDataOut)[i].translation = data.translation;
}
}
}
if (sentJointDataOut) {
(*sentJointDataOut)[i].translationIsDefaultPose = data.translationIsDefaultPose;
}
if (++validityBit == BITS_IN_BYTE) {
*validityPosition++ = validity;
validityBit = validity = 0;
@ -691,6 +674,7 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent
destinationBuffer += packOrientationQuatToSixBytes(destinationBuffer, controllerLeftHandTransform.getRotation());
destinationBuffer += packFloatVec3ToSignedTwoByteFixed(destinationBuffer, controllerLeftHandTransform.getTranslation(),
TRANSLATION_COMPRESSION_RADIX);
Transform controllerRightHandTransform = Transform(getControllerRightHandMatrix());
destinationBuffer += packOrientationQuatToSixBytes(destinationBuffer, controllerRightHandTransform.getRotation());
destinationBuffer += packFloatVec3ToSignedTwoByteFixed(destinationBuffer, controllerRightHandTransform.getTranslation(),
@ -707,34 +691,27 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent
glm::vec3 mouseFarGrabPosition = extractTranslation(mouseFarGrabMatrix);
glm::quat mouseFarGrabRotation = extractRotation(mouseFarGrabMatrix);
data->leftFarGrabPosition[0] = leftFarGrabPosition.x;
data->leftFarGrabPosition[1] = leftFarGrabPosition.y;
data->leftFarGrabPosition[2] = leftFarGrabPosition.z;
AVATAR_MEMCPY(leftFarGrabPosition);
// Can't do block copy as struct order is x, y, z, w.
data->leftFarGrabRotation[0] = leftFarGrabRotation.w;
data->leftFarGrabRotation[1] = leftFarGrabRotation.x;
data->leftFarGrabRotation[2] = leftFarGrabRotation.y;
data->leftFarGrabRotation[3] = leftFarGrabRotation.z;
destinationBuffer += sizeof(data->leftFarGrabPosition);
data->rightFarGrabPosition[0] = rightFarGrabPosition.x;
data->rightFarGrabPosition[1] = rightFarGrabPosition.y;
data->rightFarGrabPosition[2] = rightFarGrabPosition.z;
AVATAR_MEMCPY(rightFarGrabPosition);
data->rightFarGrabRotation[0] = rightFarGrabRotation.w;
data->rightFarGrabRotation[1] = rightFarGrabRotation.x;
data->rightFarGrabRotation[2] = rightFarGrabRotation.y;
data->rightFarGrabRotation[3] = rightFarGrabRotation.z;
destinationBuffer += sizeof(data->rightFarGrabRotation);
data->mouseFarGrabPosition[0] = mouseFarGrabPosition.x;
data->mouseFarGrabPosition[1] = mouseFarGrabPosition.y;
data->mouseFarGrabPosition[2] = mouseFarGrabPosition.z;
AVATAR_MEMCPY(mouseFarGrabPosition);
data->mouseFarGrabRotation[0] = mouseFarGrabRotation.w;
data->mouseFarGrabRotation[1] = mouseFarGrabRotation.x;
data->mouseFarGrabRotation[2] = mouseFarGrabRotation.y;
data->mouseFarGrabRotation[3] = mouseFarGrabRotation.z;
destinationBuffer += sizeof(AvatarDataPacket::FarGrabJoints);
destinationBuffer += sizeof(data->mouseFarGrabRotation);
int numBytes = destinationBuffer - startSection;
@ -761,41 +738,23 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent
outboundDataRateOut->jointDataRate.increment(numBytes);
}
if (sentJointDataOut) {
// Mark default poses in lastSentJointData, so when they become non-default we send them.
for (int i = 0; i < _jointData.size(); i++) {
const JointData& data = _jointData[i];
JointData& local = localSentJointDataOut[i];
if (data.rotationIsDefaultPose) {
local.rotationIsDefaultPose = true;
}
if (data.translationIsDefaultPose) {
local.translationIsDefaultPose = true;
}
}
// Push new sent joint data to sentJointDataOut
sentJointDataOut->swap(localSentJointDataOut);
}
}
if (hasJointDefaultPoseFlags) {
auto startSection = destinationBuffer;
QReadLocker readLock(&_jointDataLock);
// write numJoints
int numJoints = _jointData.size();
int numJoints = jointData.size();
*destinationBuffer++ = (uint8_t)numJoints;
// write rotationIsDefaultPose bits
destinationBuffer += writeBitVector(destinationBuffer, numJoints, [&](int i) {
return _jointData[i].rotationIsDefaultPose;
return jointData[i].rotationIsDefaultPose;
});
// write translationIsDefaultPose bits
destinationBuffer += writeBitVector(destinationBuffer, numJoints, [&](int i) {
return _jointData[i].translationIsDefaultPose;
return jointData[i].translationIsDefaultPose;
});
if (outboundDataRateOut) {
@ -880,7 +839,6 @@ const unsigned char* unpackFauxJoint(const unsigned char* sourceBuffer, ThreadSa
// read data in packet starting at byte offset and return number of bytes parsed
int AvatarData::parseDataFromBuffer(const QByteArray& buffer) {
// lazily allocate memory for HeadData in case we're not an Avatar instance
lazyInitHeadData();
@ -932,7 +890,7 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) {
auto newValue = glm::vec3(data->globalPosition[0], data->globalPosition[1], data->globalPosition[2]) + offset;
if (_globalPosition != newValue) {
_globalPosition = newValue;
_globalPositionChanged = usecTimestampNow();
_globalPositionChanged = now;
}
sourceBuffer += sizeof(AvatarDataPacket::AvatarGlobalPosition);
int numBytesRead = sourceBuffer - startSection;
@ -956,11 +914,11 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) {
if (_globalBoundingBoxDimensions != newDimensions) {
_globalBoundingBoxDimensions = newDimensions;
_avatarBoundingBoxChanged = usecTimestampNow();
_avatarBoundingBoxChanged = now;
}
if (_globalBoundingBoxOffset != newOffset) {
_globalBoundingBoxOffset = newOffset;
_avatarBoundingBoxChanged = usecTimestampNow();
_avatarBoundingBoxChanged = now;
}
sourceBuffer += sizeof(AvatarDataPacket::AvatarBoundingBox);
@ -1061,7 +1019,7 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) {
glm::mat4 sensorToWorldMatrix = createMatFromScaleQuatAndPos(glm::vec3(sensorToWorldScale), sensorToWorldQuat, sensorToWorldTrans);
if (_sensorToWorldMatrixCache.get() != sensorToWorldMatrix) {
_sensorToWorldMatrixCache.set(sensorToWorldMatrix);
_sensorToWorldMatrixChanged = usecTimestampNow();
_sensorToWorldMatrixChanged = now;
}
sourceBuffer += sizeof(AvatarDataPacket::SensorToWorldMatrix);
int numBytesRead = sourceBuffer - startSection;
@ -1118,7 +1076,7 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) {
sourceBuffer += sizeof(AvatarDataPacket::AdditionalFlags);
if (somethingChanged) {
_additionalFlagsChanged = usecTimestampNow();
_additionalFlagsChanged = now;
}
int numBytesRead = sourceBuffer - startSection;
_additionalFlagsRate.increment(numBytesRead);
@ -1138,7 +1096,7 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) {
if ((getParentID() != newParentID) || (getParentJointIndex() != parentInfo->parentJointIndex)) {
SpatiallyNestable::setParentID(newParentID);
SpatiallyNestable::setParentJointIndex(parentInfo->parentJointIndex);
_parentChanged = usecTimestampNow();
_parentChanged = now;
}
int numBytesRead = sourceBuffer - startSection;
@ -1187,8 +1145,6 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) {
int numBytesRead = sourceBuffer - startSection;
_faceTrackerRate.increment(numBytesRead);
_faceTrackerUpdateRate.increment();
} else {
_headData->_blendshapeCoefficients.fill(0, _headData->_blendshapeCoefficients.size());
}
if (hasJointData) {
@ -1861,9 +1817,7 @@ qint64 AvatarData::packTrait(AvatarTraits::TraitType traitType, ExtendedIODevice
}
qint64 AvatarData::packTraitInstance(AvatarTraits::TraitType traitType, AvatarTraits::TraitInstanceID traitInstanceID,
ExtendedIODevice& destination, AvatarTraits::TraitVersion traitVersion,
AvatarTraits::TraitInstanceID wireInstanceID) {
ExtendedIODevice& destination, AvatarTraits::TraitVersion traitVersion) {
qint64 bytesWritten = 0;
bytesWritten += destination.writePrimitive(traitType);
@ -1872,11 +1826,7 @@ qint64 AvatarData::packTraitInstance(AvatarTraits::TraitType traitType, AvatarTr
bytesWritten += destination.writePrimitive(traitVersion);
}
if (!wireInstanceID.isNull()) {
bytesWritten += destination.write(wireInstanceID.toRfc4122());
} else {
bytesWritten += destination.write(traitInstanceID.toRfc4122());
}
bytesWritten += destination.write(traitInstanceID.toRfc4122());
if (traitType == AvatarTraits::AvatarEntity) {
// grab a read lock on the avatar entities and check for entity data for the given ID
@ -2879,10 +2829,10 @@ void RayToAvatarIntersectionResultFromScriptValue(const QScriptValue& object, Ra
value.extraInfo = object.property("extraInfo").toVariant().toMap();
}
const float AvatarData::OUT_OF_VIEW_PENALTY = -10.0f;
float AvatarData::_avatarSortCoefficientSize { 1.0f };
float AvatarData::_avatarSortCoefficientCenter { 0.25 };
// these coefficients can be changed via JS for experimental tuning
// use AvatatManager.setAvatarSortCoefficient("name", value) by a user with domain kick-rights
float AvatarData::_avatarSortCoefficientSize { 8.0f };
float AvatarData::_avatarSortCoefficientCenter { 0.25f };
float AvatarData::_avatarSortCoefficientAge { 1.0f };
QScriptValue AvatarEntityMapToScriptValue(QScriptEngine* engine, const AvatarEntityMap& value) {

View file

@ -962,8 +962,7 @@ public:
qint64 packTrait(AvatarTraits::TraitType traitType, ExtendedIODevice& destination,
AvatarTraits::TraitVersion traitVersion = AvatarTraits::NULL_TRAIT_VERSION);
qint64 packTraitInstance(AvatarTraits::TraitType traitType, AvatarTraits::TraitInstanceID instanceID,
ExtendedIODevice& destination, AvatarTraits::TraitVersion traitVersion = AvatarTraits::NULL_TRAIT_VERSION,
AvatarTraits::TraitInstanceID wireInstanceID = AvatarTraits::TraitInstanceID());
ExtendedIODevice& destination, AvatarTraits::TraitVersion traitVersion = AvatarTraits::NULL_TRAIT_VERSION);
void prepareResetTraitInstances();
@ -1098,7 +1097,7 @@ public:
void fromJson(const QJsonObject& json, bool useFrameSkeleton = true);
glm::vec3 getClientGlobalPosition() const { return _globalPosition; }
glm::vec3 getGlobalBoundingBoxCorner() const { return _globalPosition + _globalBoundingBoxOffset - _globalBoundingBoxDimensions; }
AABox getGlobalBoundingBox() const { return AABox(_globalPosition + _globalBoundingBoxOffset - _globalBoundingBoxDimensions, _globalBoundingBoxDimensions); }
/**jsdoc
* @function MyAvatar.getAvatarEntityData
@ -1170,8 +1169,6 @@ public:
// A method intended to be overriden by MyAvatar for polling orientation for network transmission.
virtual glm::quat getOrientationOutbound() const;
static const float OUT_OF_VIEW_PENALTY;
// TODO: remove this HACK once we settle on optimal sort coefficients
// These coefficients exposed for fine tuning the sort priority for transfering new _jointData to the render pipeline.
static float _avatarSortCoefficientSize;
@ -1193,9 +1190,6 @@ public:
void setReplicaIndex(int replicaIndex) { _replicaIndex = replicaIndex; }
int getReplicaIndex() { return _replicaIndex; }
const AvatarTraits::TraitInstanceID getTraitInstanceXORID() const { return _traitInstanceXORID; }
void cycleTraitInstanceXORID() { _traitInstanceXORID = QUuid::createUuid(); }
signals:
/**jsdoc
@ -1444,6 +1438,8 @@ protected:
ThreadSafeValueCache<glm::mat4> _farGrabLeftMatrixCache { glm::mat4() };
ThreadSafeValueCache<glm::mat4> _farGrabMouseMatrixCache { glm::mat4() };
ThreadSafeValueCache<QVariantMap> _collisionCapsuleCache{ QVariantMap() };
int getFauxJointIndex(const QString& name) const;
float _audioLoudness { 0.0f };
@ -1502,8 +1498,6 @@ private:
// privatize the copy constructor and assignment operator so they cannot be called
AvatarData(const AvatarData&);
AvatarData& operator= (const AvatarData&);
AvatarTraits::TraitInstanceID _traitInstanceXORID { QUuid::createUuid() };
};
Q_DECLARE_METATYPE(AvatarData*)

View file

@ -86,8 +86,7 @@ void AvatarReplicas::processDeletedTraitInstance(const QUuid& parentID, AvatarTr
if (_replicasMap.find(parentID) != _replicasMap.end()) {
auto &replicas = _replicasMap[parentID];
for (auto avatar : replicas) {
avatar->processDeletedTraitInstance(traitType,
AvatarTraits::xoredInstanceID(instanceID, avatar->getTraitInstanceXORID()));
avatar->processDeletedTraitInstance(traitType, instanceID);
}
}
}
@ -96,9 +95,7 @@ void AvatarReplicas::processTraitInstance(const QUuid& parentID, AvatarTraits::T
if (_replicasMap.find(parentID) != _replicasMap.end()) {
auto &replicas = _replicasMap[parentID];
for (auto avatar : replicas) {
avatar->processTraitInstance(traitType,
AvatarTraits::xoredInstanceID(instanceID, avatar->getTraitInstanceXORID()),
traitBinaryData);
avatar->processTraitInstance(traitType, instanceID, traitBinaryData);
}
}
}
@ -113,6 +110,12 @@ AvatarHashMap::AvatarHashMap() {
packetReceiver.registerListener(PacketType::BulkAvatarTraits, this, "processBulkAvatarTraits");
connect(nodeList.data(), &NodeList::uuidChanged, this, &AvatarHashMap::sessionUUIDChanged);
connect(nodeList.data(), &NodeList::nodeKilled, this, [this](SharedNodePointer killedNode){
if (killedNode->getType() == NodeType::AvatarMixer) {
clearOtherAvatars();
}
});
}
QVector<QUuid> AvatarHashMap::getAvatarIdentifiers() {
@ -341,28 +344,16 @@ void AvatarHashMap::processBulkAvatarTraits(QSharedPointer<ReceivedMessage> mess
AvatarTraits::TraitInstanceID traitInstanceID =
QUuid::fromRfc4122(message->readWithoutCopy(NUM_BYTES_RFC4122_UUID));
// XOR the incoming trait instance ID with this avatar object's personal XOR ID
// this ensures that we have separate entity instances in the local tree
// if we briefly end up with two Avatar objects for this node
// (which can occur if the shared pointer for the
// previous instance of an avatar hasn't yet gone out of scope before the
// new instance is created)
auto xoredInstanceID = AvatarTraits::xoredInstanceID(traitInstanceID, avatar->getTraitInstanceXORID());
message->readPrimitive(&traitBinarySize);
auto& processedInstanceVersion = lastProcessedVersions.getInstanceValueRef(traitType, traitInstanceID);
if (packetTraitVersion > processedInstanceVersion) {
// in order to handle re-connections to the avatar mixer when the other
if (traitBinarySize == AvatarTraits::DELETED_TRAIT_SIZE) {
avatar->processDeletedTraitInstance(traitType, xoredInstanceID);
avatar->processDeletedTraitInstance(traitType, traitInstanceID);
_replicas.processDeletedTraitInstance(avatarID, traitType, traitInstanceID);
} else {
auto traitData = message->read(traitBinarySize);
avatar->processTraitInstance(traitType, xoredInstanceID, traitData);
avatar->processTraitInstance(traitType, traitInstanceID, traitData);
_replicas.processTraitInstance(avatarID, traitType, traitInstanceID, traitData);
}
processedInstanceVersion = packetTraitVersion;
@ -430,3 +421,12 @@ void AvatarHashMap::sessionUUIDChanged(const QUuid& sessionUUID, const QUuid& ol
emit avatarSessionChangedEvent(sessionUUID, oldUUID);
}
void AvatarHashMap::clearOtherAvatars() {
QWriteLocker locker(&_hashLock);
for (auto& av : _avatarHash) {
handleRemovedAvatar(av);
}
_avatarHash.clear();
}

View file

@ -101,6 +101,8 @@ public:
void setReplicaCount(int count);
int getReplicaCount() { return _replicas.getReplicaCount(); };
virtual void clearOtherAvatars();
signals:
/**jsdoc

View file

@ -41,8 +41,7 @@ namespace AvatarTraits {
const TraitWireSize DELETED_TRAIT_SIZE = -1;
inline qint64 packInstancedTraitDelete(TraitType traitType, TraitInstanceID instanceID, ExtendedIODevice& destination,
TraitVersion traitVersion = NULL_TRAIT_VERSION,
TraitInstanceID xoredInstanceID = TraitInstanceID()) {
TraitVersion traitVersion = NULL_TRAIT_VERSION) {
qint64 bytesWritten = 0;
bytesWritten += destination.writePrimitive(traitType);
@ -51,28 +50,12 @@ namespace AvatarTraits {
bytesWritten += destination.writePrimitive(traitVersion);
}
if (xoredInstanceID.isNull()) {
bytesWritten += destination.write(instanceID.toRfc4122());
} else {
bytesWritten += destination.write(xoredInstanceID.toRfc4122());
}
bytesWritten += destination.write(instanceID.toRfc4122());
bytesWritten += destination.writePrimitive(DELETED_TRAIT_SIZE);
return bytesWritten;
}
inline TraitInstanceID xoredInstanceID(TraitInstanceID localInstanceID, TraitInstanceID xorKeyID) {
QByteArray xoredInstanceID { NUM_BYTES_RFC4122_UUID, 0 };
auto xorKeyIDBytes = xorKeyID.toRfc4122();
auto localInstanceIDBytes = localInstanceID.toRfc4122();
for (auto i = 0; i < localInstanceIDBytes.size(); ++i) {
xoredInstanceID[i] = localInstanceIDBytes[i] ^ xorKeyIDBytes[i];
}
return QUuid::fromRfc4122(xoredInstanceID);
}
};
#endif // hifi_AvatarTraits_h

View file

@ -43,9 +43,6 @@ void ClientTraitsHandler::resetForNewMixer() {
// pre-fill the instanced statuses that we will need to send next frame
_owningAvatar->prepareResetTraitInstances();
// reset the trait XOR ID since we're resetting for a new avatar mixer
_owningAvatar->cycleTraitInstanceXORID();
}
void ClientTraitsHandler::sendChangedTraitsToMixer() {
@ -96,19 +93,11 @@ void ClientTraitsHandler::sendChangedTraitsToMixer() {
|| instanceIDValuePair.value == Updated) {
// this is a changed trait we need to send or we haven't send out trait information yet
// ask the owning avatar to pack it
// since this is going to the mixer, use the XORed instance ID (to anonymize trait instance IDs
// that would typically persist across sessions)
_owningAvatar->packTraitInstance(instancedIt->traitType, instanceIDValuePair.id, *traitsPacketList,
AvatarTraits::NULL_TRAIT_VERSION,
AvatarTraits::xoredInstanceID(instanceIDValuePair.id,
_owningAvatar->getTraitInstanceXORID()));
_owningAvatar->packTraitInstance(instancedIt->traitType, instanceIDValuePair.id, *traitsPacketList);
} else if (!_shouldPerformInitialSend && instanceIDValuePair.value == Deleted) {
// pack delete for this trait instance
AvatarTraits::packInstancedTraitDelete(instancedIt->traitType, instanceIDValuePair.id,
*traitsPacketList, AvatarTraits::NULL_TRAIT_VERSION,
AvatarTraits::xoredInstanceID(instanceIDValuePair.id,
_owningAvatar->getTraitInstanceXORID()));
*traitsPacketList);
}
}

Some files were not shown because too many files have changed in this diff Show more