Merge branch 'replicants' of https://github.com/highfidelity/hifi into feat/downstream-in-nodelist

This commit is contained in:
Stephen Birarda 2017-06-14 16:57:28 -07:00
commit 1ed0b693da
108 changed files with 2356 additions and 1395 deletions

View file

@ -143,6 +143,8 @@ void AudioMixer::queueReplicatedAudioPacket(QSharedPointer<ReceivedMessage> mess
rewrittenType = PacketType::InjectAudio; rewrittenType = PacketType::InjectAudio;
} else if (message->getType() == PacketType::ReplicatedSilentAudioFrame) { } else if (message->getType() == PacketType::ReplicatedSilentAudioFrame) {
rewrittenType = PacketType::SilentAudioFrame; rewrittenType = PacketType::SilentAudioFrame;
} else {
return;
} }
auto replicatedMessage = QSharedPointer<ReceivedMessage>::create(audioData, rewrittenType, auto replicatedMessage = QSharedPointer<ReceivedMessage>::create(audioData, rewrittenType,

View file

@ -691,9 +691,10 @@ bool AudioMixerClientData::shouldIgnore(const SharedNodePointer self, const Shar
} }
void AudioMixerClientData::setupCodecForReplicatedAgent(QSharedPointer<ReceivedMessage> message) { void AudioMixerClientData::setupCodecForReplicatedAgent(QSharedPointer<ReceivedMessage> message) {
// pull the codec string from the packet // hop past the sequence number that leads the packet
message->seek(sizeof(quint16)); message->seek(sizeof(quint16));
// pull the codec string from the packet
auto codecString = message->readString(); auto codecString = message->readString();
qDebug() << "Manually setting codec for replicated agent" << uuidStringWithoutCurlyBraces(getNodeID()) qDebug() << "Manually setting codec for replicated agent" << uuidStringWithoutCurlyBraces(getNodeID())

View file

@ -85,7 +85,7 @@ SharedNodePointer addOrUpdateReplicatedNode(const QUuid& nodeID, const HifiSockA
void AvatarMixer::handleReplicatedPackets(QSharedPointer<ReceivedMessage> message) { void AvatarMixer::handleReplicatedPackets(QSharedPointer<ReceivedMessage> message) {
auto nodeList = DependencyManager::get<NodeList>(); auto nodeList = DependencyManager::get<NodeList>();
auto nodeID = QUuid::fromRfc4122(message->readWithoutCopy(NUM_BYTES_RFC4122_UUID)); auto nodeID = QUuid::fromRfc4122(message->peek(NUM_BYTES_RFC4122_UUID));
auto replicatedNode = addOrUpdateReplicatedNode(nodeID, message->getSenderSockAddr()); auto replicatedNode = addOrUpdateReplicatedNode(nodeID, message->getSenderSockAddr());
@ -516,12 +516,9 @@ void AvatarMixer::handleAvatarIdentityPacket(QSharedPointer<ReceivedMessage> mes
AvatarData& avatar = nodeData->getAvatar(); AvatarData& avatar = nodeData->getAvatar();
// parse the identity packet and update the change timestamp if appropriate // parse the identity packet and update the change timestamp if appropriate
AvatarData::Identity identity;
AvatarData::parseAvatarIdentityPacket(message->getMessage(), identity);
bool identityChanged = false; bool identityChanged = false;
bool displayNameChanged = false; bool displayNameChanged = false;
avatar.processAvatarIdentity(identity, identityChanged, displayNameChanged); avatar.processAvatarIdentity(message->getMessage(), identityChanged, displayNameChanged);
if (identityChanged) { if (identityChanged) {
QMutexLocker nodeDataLocker(&nodeData->getMutex()); QMutexLocker nodeDataLocker(&nodeData->getMutex());
nodeData->flagIdentityChange(); nodeData->flagIdentityChange();

View file

@ -67,7 +67,7 @@ void AvatarMixerSlave::processIncomingPackets(const SharedNodePointer& node) {
int AvatarMixerSlave::sendIdentityPacket(const AvatarMixerClientData* nodeData, const SharedNodePointer& destinationNode) { int AvatarMixerSlave::sendIdentityPacket(const AvatarMixerClientData* nodeData, const SharedNodePointer& destinationNode) {
if (destinationNode->getType() == NodeType::Agent && !destinationNode->isUpstream()) { if (destinationNode->getType() == NodeType::Agent && !destinationNode->isUpstream()) {
QByteArray individualData = nodeData->getConstAvatarData()->identityByteArray(); QByteArray individualData = nodeData->getConstAvatarData()->identityByteArray(true);
individualData.replace(0, NUM_BYTES_RFC4122_UUID, nodeData->getNodeID().toRfc4122()); // FIXME, this looks suspicious individualData.replace(0, NUM_BYTES_RFC4122_UUID, nodeData->getNodeID().toRfc4122()); // FIXME, this looks suspicious
auto identityPackets = NLPacketList::create(PacketType::AvatarIdentity, QByteArray(), true, true); auto identityPackets = NLPacketList::create(PacketType::AvatarIdentity, QByteArray(), true, true);
identityPackets->write(individualData); identityPackets->write(individualData);
@ -81,7 +81,7 @@ int AvatarMixerSlave::sendIdentityPacket(const AvatarMixerClientData* nodeData,
int AvatarMixerSlave::sendReplicatedIdentityPacket(const AvatarMixerClientData* nodeData, const SharedNodePointer& destinationNode) { int AvatarMixerSlave::sendReplicatedIdentityPacket(const AvatarMixerClientData* nodeData, const SharedNodePointer& destinationNode) {
if (destinationNode->getType() == NodeType::DownstreamAvatarMixer) { if (destinationNode->getType() == NodeType::DownstreamAvatarMixer) {
QByteArray individualData = nodeData->getConstAvatarData()->identityByteArray(); QByteArray individualData = nodeData->getConstAvatarData()->identityByteArray(true);
individualData.replace(0, NUM_BYTES_RFC4122_UUID, nodeData->getNodeID().toRfc4122()); // FIXME, this looks suspicious individualData.replace(0, NUM_BYTES_RFC4122_UUID, nodeData->getNodeID().toRfc4122()); // FIXME, this looks suspicious
auto identityPacket = NLPacket::create(PacketType::ReplicatedAvatarIdentity); auto identityPacket = NLPacket::create(PacketType::ReplicatedAvatarIdentity);
identityPacket->write(individualData); identityPacket->write(individualData);
@ -204,216 +204,212 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node)
ViewFrustum cameraView = nodeData->getViewFrustom(); ViewFrustum cameraView = nodeData->getViewFrustom();
std::priority_queue<AvatarPriority> sortedAvatars; std::priority_queue<AvatarPriority> sortedAvatars;
AvatarData::sortAvatars(avatarList, cameraView, sortedAvatars, AvatarData::sortAvatars(avatarList, cameraView, sortedAvatars,
[&](AvatarSharedPointer avatar)->uint64_t{ [&](AvatarSharedPointer avatar)->uint64_t {
auto avatarNode = avatarDataToNodes[avatar]; auto avatarNode = avatarDataToNodes[avatar];
assert(avatarNode); // we can't have gotten here without the avatarData being a valid key in the map assert(avatarNode); // we can't have gotten here without the avatarData being a valid key in the map
return nodeData->getLastBroadcastTime(avatarNode->getUUID()); return nodeData->getLastBroadcastTime(avatarNode->getUUID());
}, }, [&](AvatarSharedPointer avatar)->float{
glm::vec3 nodeBoxHalfScale = (avatar->getPosition() - avatar->getGlobalBoundingBoxCorner());
return glm::max(nodeBoxHalfScale.x, glm::max(nodeBoxHalfScale.y, nodeBoxHalfScale.z));
}, [&](AvatarSharedPointer avatar)->bool {
if (avatar == thisAvatar) {
return true; // ignore ourselves...
}
[&](AvatarSharedPointer avatar)->float{ bool shouldIgnore = false;
glm::vec3 nodeBoxHalfScale = (avatar->getPosition() - avatar->getGlobalBoundingBoxCorner());
return glm::max(nodeBoxHalfScale.x, glm::max(nodeBoxHalfScale.y, nodeBoxHalfScale.z));
},
[&](AvatarSharedPointer avatar)->bool{ // We will also ignore other nodes for a couple of different reasons:
if (avatar == thisAvatar) { // 1) ignore bubbles and ignore specific node
return true; // ignore ourselves... // 2) the node hasn't really updated it's frame data recently, this can
} // happen if for example the avatar is connected on a desktop and sending
// updates at ~30hz. So every 3 frames we skip a frame.
auto avatarNode = avatarDataToNodes[avatar];
bool shouldIgnore = false; assert(avatarNode); // we can't have gotten here without the avatarData being a valid key in the map
// We will also ignore other nodes for a couple of different reasons: const AvatarMixerClientData* avatarNodeData = reinterpret_cast<const AvatarMixerClientData*>(avatarNode->getLinkedData());
// 1) ignore bubbles and ignore specific node assert(avatarNodeData); // we can't have gotten here without avatarNode having valid data
// 2) the node hasn't really updated it's frame data recently, this can quint64 startIgnoreCalculation = usecTimestampNow();
// 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 // 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)) {
shouldIgnore = true;
} else {
const AvatarMixerClientData* avatarNodeData = reinterpret_cast<const AvatarMixerClientData*>(avatarNode->getLinkedData()); // Check to see if the space bubble is enabled
assert(avatarNodeData); // we can't have gotten here without avatarNode having valid data // Don't bother with these checks if the other avatar has their bubble enabled and we're gettingAnyIgnored
quint64 startIgnoreCalculation = usecTimestampNow(); if (node->isIgnoreRadiusEnabled() || (avatarNode->isIgnoreRadiusEnabled() && !getsAnyIgnored)) {
// make sure we have data for this avatar, that it isn't the same node, // Define the scale of the box for the current other node
// and isn't an avatar that the viewing node has ignored glm::vec3 otherNodeBoxScale = (avatarNodeData->getPosition() - avatarNodeData->getGlobalBoundingBoxCorner()) * 2.0f;
// or that has ignored the viewing node // Set up the bounding box for the current other node
if (!avatarNode->getLinkedData() AABox otherNodeBox(avatarNodeData->getGlobalBoundingBoxCorner(), otherNodeBoxScale);
|| avatarNode->getUUID() == node->getUUID() // Clamp the size of the bounding box to a minimum scale
|| (node->isIgnoringNodeWithID(avatarNode->getUUID()) && !PALIsOpen) if (glm::any(glm::lessThan(otherNodeBoxScale, minBubbleSize))) {
|| (avatarNode->isIgnoringNodeWithID(node->getUUID()) && !getsAnyIgnored)) { otherNodeBox.setScaleStayCentered(minBubbleSize);
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)) {
// Define the scale of the box for the current other node
glm::vec3 otherNodeBoxScale = (avatarNodeData->getPosition() - avatarNodeData->getGlobalBoundingBoxCorner()) * 2.0f;
// 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);
}
// Quadruple the scale of both bounding boxes
otherNodeBox.embiggen(4.0f);
// Perform the collision check between the two bounding boxes
if (nodeBox.touches(otherNodeBox)) {
nodeData->ignoreOther(node, avatarNode);
shouldIgnore = !getsAnyIgnored;
}
} }
// Not close enough to ignore // Quadruple the scale of both bounding boxes
if (!shouldIgnore) { otherNodeBox.embiggen(4.0f);
nodeData->removeFromRadiusIgnoringSet(node, avatarNode->getUUID());
// Perform the collision check between the two bounding boxes
if (nodeBox.touches(otherNodeBox)) {
nodeData->ignoreOther(node, avatarNode);
shouldIgnore = !getsAnyIgnored;
} }
} }
quint64 endIgnoreCalculation = usecTimestampNow(); // Not close enough to ignore
_stats.ignoreCalculationElapsedTime += (endIgnoreCalculation - startIgnoreCalculation);
if (!shouldIgnore) { if (!shouldIgnore) {
AvatarDataSequenceNumber lastSeqToReceiver = nodeData->getLastBroadcastSequenceNumber(avatarNode->getUUID()); nodeData->removeFromRadiusIgnoringSet(node, avatarNode->getUUID());
AvatarDataSequenceNumber lastSeqFromSender = avatarNodeData->getLastReceivedSequenceNumber();
// FIXME - This code does appear to be working. But it seems brittle.
// It supports determining if the frame of data for this "other"
// avatar has already been sent to the reciever. This has been
// verified to work on a desktop display that renders at 60hz and
// therefore sends to mixer at 30hz. Each second you'd expect to
// have 15 (45hz-30hz) duplicate frames. In this case, the stat
// avg_other_av_skips_per_second does report 15.
//
// make sure we haven't already sent this data from this sender to this receiver
// or that somehow we haven't sent
if (lastSeqToReceiver == lastSeqFromSender && lastSeqToReceiver != 0) {
++numAvatarsHeldBack;
shouldIgnore = true;
} else if (lastSeqFromSender - lastSeqToReceiver > 1) {
// this is a skip - we still send the packet but capture the presence of the skip so we see it happening
++numAvatarsWithSkippedFrames;
}
} }
return shouldIgnore; }
}); quint64 endIgnoreCalculation = usecTimestampNow();
_stats.ignoreCalculationElapsedTime += (endIgnoreCalculation - startIgnoreCalculation);
// loop through our sorted avatars and allocate our bandwidth to them accordingly if (!shouldIgnore) {
int avatarRank = 0; AvatarDataSequenceNumber lastSeqToReceiver = nodeData->getLastBroadcastSequenceNumber(avatarNode->getUUID());
AvatarDataSequenceNumber lastSeqFromSender = avatarNodeData->getLastReceivedSequenceNumber();
// this is overly conservative, because it includes some avatars we might not consider // FIXME - This code does appear to be working. But it seems brittle.
int remainingAvatars = (int)sortedAvatars.size(); // It supports determining if the frame of data for this "other"
// avatar has already been sent to the reciever. This has been
while (!sortedAvatars.empty()) { // verified to work on a desktop display that renders at 60hz and
AvatarPriority sortData = sortedAvatars.top(); // therefore sends to mixer at 30hz. Each second you'd expect to
sortedAvatars.pop(); // have 15 (45hz-30hz) duplicate frames. In this case, the stat
const auto& avatarData = sortData.avatar; // avg_other_av_skips_per_second does report 15.
avatarRank++; //
remainingAvatars--; // make sure we haven't already sent this data from this sender to this receiver
// or that somehow we haven't sent
auto otherNode = avatarDataToNodes[avatarData]; if (lastSeqToReceiver == lastSeqFromSender && lastSeqToReceiver != 0) {
assert(otherNode); // we can't have gotten here without the avatarData being a valid key in the map ++numAvatarsHeldBack;
shouldIgnore = true;
// NOTE: Here's where we determine if we are over budget and drop to bare minimum data } else if (lastSeqFromSender - lastSeqToReceiver > 1) {
int minimRemainingAvatarBytes = minimumBytesPerAvatar * remainingAvatars; // this is a skip - we still send the packet but capture the presence of the skip so we see it happening
bool overBudget = (identityBytesSent + numAvatarDataBytes + minimRemainingAvatarBytes) > maxAvatarBytesPerFrame; ++numAvatarsWithSkippedFrames;
quint64 startAvatarDataPacking = usecTimestampNow();
++numOtherAvatars;
const AvatarMixerClientData* otherNodeData = reinterpret_cast<const AvatarMixerClientData*>(otherNode->getLinkedData());
// If the time that the mixer sent AVATAR DATA about Avatar B to Avatar A is BEFORE OR EQUAL TO
// the time that Avatar B flagged an IDENTITY DATA change, send IDENTITY DATA about Avatar B to Avatar A.
if (nodeData->getLastBroadcastTime(otherNode->getUUID()) <= otherNodeData->getIdentityChangeTimestamp()) {
identityBytesSent += sendIdentityPacket(otherNodeData, node);
} }
}
return shouldIgnore;
});
const AvatarData* otherAvatar = otherNodeData->getConstAvatarData(); // loop through our sorted avatars and allocate our bandwidth to them accordingly
glm::vec3 otherPosition = otherAvatar->getClientGlobalPosition(); int avatarRank = 0;
// determine if avatar is in view, to determine how much data to include... // this is overly conservative, because it includes some avatars we might not consider
glm::vec3 otherNodeBoxScale = (otherPosition - otherNodeData->getGlobalBoundingBoxCorner()) * 2.0f; int remainingAvatars = (int)sortedAvatars.size();
AABox otherNodeBox(otherNodeData->getGlobalBoundingBoxCorner(), otherNodeBoxScale);
bool isInView = nodeData->otherAvatarInView(otherNodeBox);
// start a new segment in the PacketList for this avatar while (!sortedAvatars.empty()) {
avatarPacketList->startSegment(); AvatarPriority sortData = sortedAvatars.top();
sortedAvatars.pop();
const auto& avatarData = sortData.avatar;
avatarRank++;
remainingAvatars--;
AvatarData::AvatarDataDetail detail; auto otherNode = avatarDataToNodes[avatarData];
assert(otherNode); // we can't have gotten here without the avatarData being a valid key in the map
if (overBudget) { // NOTE: Here's where we determine if we are over budget and drop to bare minimum data
overBudgetAvatars++; int minimRemainingAvatarBytes = minimumBytesPerAvatar * remainingAvatars;
_stats.overBudgetAvatars++; bool overBudget = (identityBytesSent + numAvatarDataBytes + minimRemainingAvatarBytes) > maxAvatarBytesPerFrame;
detail = PALIsOpen ? AvatarData::PALMinimum : AvatarData::NoData;
} else if (!isInView) {
detail = PALIsOpen ? AvatarData::PALMinimum : AvatarData::MinimumData;
nodeData->incrementAvatarOutOfView();
} else {
detail = distribution(generator) < AVATAR_SEND_FULL_UPDATE_RATIO
? AvatarData::SendAllData : AvatarData::CullSmallData;
nodeData->incrementAvatarInView();
}
bool includeThisAvatar = true; quint64 startAvatarDataPacking = usecTimestampNow();
auto lastEncodeForOther = nodeData->getLastOtherAvatarEncodeTime(otherNode->getUUID());
QVector<JointData>& lastSentJointsForOther = nodeData->getLastOtherAvatarSentJoints(otherNode->getUUID());
bool distanceAdjust = true;
glm::vec3 viewerPosition = myPosition;
AvatarDataPacket::HasFlags hasFlagsOut; // the result of the toByteArray
bool dropFaceTracking = false;
quint64 start = usecTimestampNow(); ++numOtherAvatars;
QByteArray bytes = otherAvatar->toByteArray(detail, lastEncodeForOther, lastSentJointsForOther,
hasFlagsOut, dropFaceTracking, distanceAdjust, viewerPosition, &lastSentJointsForOther); const AvatarMixerClientData* otherNodeData = reinterpret_cast<const AvatarMixerClientData*>(otherNode->getLinkedData());
quint64 end = usecTimestampNow();
_stats.toByteArrayElapsedTime += (end - start); // If the time that the mixer sent AVATAR DATA about Avatar B to Avatar A is BEFORE OR EQUAL TO
// the time that Avatar B flagged an IDENTITY DATA change, send IDENTITY DATA about Avatar B to Avatar A.
if (nodeData->getLastBroadcastTime(otherNode->getUUID()) <= otherNodeData->getIdentityChangeTimestamp()) {
identityBytesSent += sendIdentityPacket(otherNodeData, node);
}
const AvatarData* otherAvatar = otherNodeData->getConstAvatarData();
glm::vec3 otherPosition = otherAvatar->getClientGlobalPosition();
// determine if avatar is in view, to determine how much data to include...
glm::vec3 otherNodeBoxScale = (otherPosition - otherNodeData->getGlobalBoundingBoxCorner()) * 2.0f;
AABox otherNodeBox(otherNodeData->getGlobalBoundingBoxCorner(), otherNodeBoxScale);
bool isInView = nodeData->otherAvatarInView(otherNodeBox);
// start a new segment in the PacketList for this avatar
avatarPacketList->startSegment();
AvatarData::AvatarDataDetail detail;
if (overBudget) {
overBudgetAvatars++;
_stats.overBudgetAvatars++;
detail = PALIsOpen ? AvatarData::PALMinimum : AvatarData::NoData;
} else if (!isInView) {
detail = PALIsOpen ? AvatarData::PALMinimum : AvatarData::MinimumData;
nodeData->incrementAvatarOutOfView();
} else {
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());
bool distanceAdjust = true;
glm::vec3 viewerPosition = myPosition;
AvatarDataPacket::HasFlags hasFlagsOut; // the result of the toByteArray
bool dropFaceTracking = false;
quint64 start = usecTimestampNow();
QByteArray bytes = otherAvatar->toByteArray(detail, lastEncodeForOther, lastSentJointsForOther,
hasFlagsOut, dropFaceTracking, distanceAdjust, viewerPosition, &lastSentJointsForOther);
quint64 end = usecTimestampNow();
_stats.toByteArrayElapsedTime += (end - start);
static const int MAX_ALLOWED_AVATAR_DATA = (1400 - NUM_BYTES_RFC4122_UUID);
if (bytes.size() > MAX_ALLOWED_AVATAR_DATA) {
qCWarning(avatars) << "otherAvatar.toByteArray() resulted in very large buffer:" << bytes.size() << "... attempt to drop facial data";
dropFaceTracking = true; // first try dropping the facial data
bytes = otherAvatar->toByteArray(detail, lastEncodeForOther, lastSentJointsForOther,
hasFlagsOut, dropFaceTracking, distanceAdjust, viewerPosition, &lastSentJointsForOther);
static const int MAX_ALLOWED_AVATAR_DATA = (1400 - NUM_BYTES_RFC4122_UUID);
if (bytes.size() > MAX_ALLOWED_AVATAR_DATA) { if (bytes.size() > MAX_ALLOWED_AVATAR_DATA) {
qCWarning(avatars) << "otherAvatar.toByteArray() resulted in very large buffer:" << bytes.size() << "... attempt to drop facial data"; qCWarning(avatars) << "otherAvatar.toByteArray() without facial data resulted in very large buffer:" << bytes.size() << "... reduce to MinimumData";
bytes = otherAvatar->toByteArray(AvatarData::MinimumData, lastEncodeForOther, lastSentJointsForOther,
dropFaceTracking = true; // first try dropping the facial data
bytes = otherAvatar->toByteArray(detail, lastEncodeForOther, lastSentJointsForOther,
hasFlagsOut, dropFaceTracking, distanceAdjust, viewerPosition, &lastSentJointsForOther); hasFlagsOut, dropFaceTracking, distanceAdjust, viewerPosition, &lastSentJointsForOther);
if (bytes.size() > MAX_ALLOWED_AVATAR_DATA) {
qCWarning(avatars) << "otherAvatar.toByteArray() without facial data resulted in very large buffer:" << bytes.size() << "... reduce to MinimumData";
bytes = otherAvatar->toByteArray(AvatarData::MinimumData, lastEncodeForOther, lastSentJointsForOther,
hasFlagsOut, dropFaceTracking, distanceAdjust, viewerPosition, &lastSentJointsForOther);
}
if (bytes.size() > MAX_ALLOWED_AVATAR_DATA) {
qCWarning(avatars) << "otherAvatar.toByteArray() MinimumData resulted in very large buffer:" << bytes.size() << "... FAIL!!";
includeThisAvatar = false;
}
} }
if (includeThisAvatar) { if (bytes.size() > MAX_ALLOWED_AVATAR_DATA) {
numAvatarDataBytes += avatarPacketList->write(otherNode->getUUID().toRfc4122()); qCWarning(avatars) << "otherAvatar.toByteArray() MinimumData resulted in very large buffer:" << bytes.size() << "... FAIL!!";
numAvatarDataBytes += avatarPacketList->write(bytes); includeThisAvatar = false;
if (detail != AvatarData::NoData) {
_stats.numOthersIncluded++;
// increment the number of avatars sent to this reciever
nodeData->incrementNumAvatarsSentLastFrame();
// set the last sent sequence number for this sender on the receiver
nodeData->setLastBroadcastSequenceNumber(otherNode->getUUID(),
otherNodeData->getLastReceivedSequenceNumber());
// remember the last time we sent details about this other node to the receiver
nodeData->setLastBroadcastTime(otherNode->getUUID(), start);
}
} }
}
avatarPacketList->endSegment(); if (includeThisAvatar) {
numAvatarDataBytes += avatarPacketList->write(otherNode->getUUID().toRfc4122());
numAvatarDataBytes += avatarPacketList->write(bytes);
quint64 endAvatarDataPacking = usecTimestampNow(); if (detail != AvatarData::NoData) {
_stats.avatarDataPackingElapsedTime += (endAvatarDataPacking - startAvatarDataPacking); _stats.numOthersIncluded++;
// increment the number of avatars sent to this reciever
nodeData->incrementNumAvatarsSentLastFrame();
// set the last sent sequence number for this sender on the receiver
nodeData->setLastBroadcastSequenceNumber(otherNode->getUUID(),
otherNodeData->getLastReceivedSequenceNumber());
// remember the last time we sent details about this other node to the receiver
nodeData->setLastBroadcastTime(otherNode->getUUID(), start);
}
}
avatarPacketList->endSegment();
quint64 endAvatarDataPacking = usecTimestampNow();
_stats.avatarDataPackingElapsedTime += (endAvatarDataPacking - startAvatarDataPacking);
}; };
quint64 startPacketSending = usecTimestampNow(); quint64 startPacketSending = usecTimestampNow();

View file

@ -76,8 +76,8 @@ void AvatarMixerSlavePool::processIncomingPackets(ConstIter begin, ConstIter end
} }
void AvatarMixerSlavePool::broadcastAvatarData(ConstIter begin, ConstIter end, void AvatarMixerSlavePool::broadcastAvatarData(ConstIter begin, ConstIter end,
p_high_resolution_clock::time_point lastFrameTimestamp, p_high_resolution_clock::time_point lastFrameTimestamp,
float maxKbpsPerNode, float throttlingRatio) { float maxKbpsPerNode, float throttlingRatio) {
_function = &AvatarMixerSlave::broadcastAvatarData; _function = &AvatarMixerSlave::broadcastAvatarData;
_configure = [&](AvatarMixerSlave& slave) { _configure = [&](AvatarMixerSlave& slave) {
slave.configureBroadcast(begin, end, lastFrameTimestamp, maxKbpsPerNode, throttlingRatio); slave.configureBroadcast(begin, end, lastFrameTimestamp, maxKbpsPerNode, throttlingRatio);

View file

@ -47,7 +47,7 @@ void OctreeInboundPacketProcessor::resetStats() {
_singleSenderStats.clear(); _singleSenderStats.clear();
} }
unsigned long OctreeInboundPacketProcessor::getMaxWait() const { uint32_t OctreeInboundPacketProcessor::getMaxWait() const {
// calculate time until next sendNackPackets() // calculate time until next sendNackPackets()
quint64 nextNackTime = _lastNackTime + TOO_LONG_SINCE_LAST_NACK; quint64 nextNackTime = _lastNackTime + TOO_LONG_SINCE_LAST_NACK;
quint64 now = usecTimestampNow(); quint64 now = usecTimestampNow();

View file

@ -80,7 +80,7 @@ protected:
virtual void processPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer sendingNode) override; virtual void processPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer sendingNode) override;
virtual unsigned long getMaxWait() const override; virtual uint32_t getMaxWait() const override;
virtual void preProcess() override; virtual void preProcess() override;
virtual void midProcess() override; virtual void midProcess() override;

View file

@ -1360,8 +1360,19 @@
{ {
"name": "server_type", "name": "server_type",
"label": "Server Type", "label": "Server Type",
"type": "select",
"placeholder": "Audio Mixer", "placeholder": "Audio Mixer",
"can_set": true "can_set": true,
"options": [
{
"value": "Audio Mixer",
"label": "Audio Mixer"
},
{
"value": "Avatar Mixer",
"label": "Avatar Mixer"
}
]
} }
] ]
} }

View file

@ -223,6 +223,14 @@ $(document).ready(function(){
// set focus to the first input in the new row // set focus to the first input in the new row
$target.closest('table').find('tr.inputs input:first').focus(); $target.closest('table').find('tr.inputs input:first').focus();
} }
var tableRows = sibling.parent();
var tableBody = tableRows.parent();
// if theres no more siblings, we should jump to a new row
if (sibling.next().length == 0 && tableRows.nextAll().length == 1) {
tableBody.find("." + Settings.ADD_ROW_BUTTON_CLASS).click();
}
} }
} else if ($target.is('input')) { } else if ($target.is('input')) {
@ -1281,6 +1289,17 @@ function makeTableHiddenInputs(setting, initialValues, categoryValue) {
"<input type='checkbox' style='display: none;' class='form-control table-checkbox' " + "<input type='checkbox' style='display: none;' class='form-control table-checkbox' " +
"name='" + col.name + "'" + (defaultValue ? " checked" : "") + "/>" + "name='" + col.name + "'" + (defaultValue ? " checked" : "") + "/>" +
"</td>"; "</td>";
} else if (col.type === "select") {
html += "<td class='" + Settings.DATA_COL_CLASS + "'name='" + col.name + "'>"
html += "<select style='display: none;' class='form-control' data-hidden-input='" + col.name + "'>'"
for (var i in col.options) {
var option = col.options[i];
html += "<option value='" + option.value + "' " + (option.value == defaultValue ? 'selected' : '') + ">" + option.label + "</option>";
}
html += "</select>";
html += "<input type='hidden' class='table-dropdown form-control trigger-change' name='" + col.name + "' value='" + option.value + "'></td>";
} else { } else {
html += html +=
"<td " + (col.hidden ? "style='display: none;'" : "") + " class='" + Settings.DATA_COL_CLASS + "' " + "<td " + (col.hidden ? "style='display: none;'" : "") + " class='" + Settings.DATA_COL_CLASS + "' " +
@ -1408,6 +1427,7 @@ function addTableRow(row) {
input.show(); input.show();
var isCheckbox = input.hasClass("table-checkbox"); var isCheckbox = input.hasClass("table-checkbox");
var isDropdown = input.hasClass("table-dropdown");
if (isArray) { if (isArray) {
var row_index = row.siblings('.' + Settings.DATA_ROW_CLASS).length var row_index = row.siblings('.' + Settings.DATA_ROW_CLASS).length
@ -1416,11 +1436,15 @@ function addTableRow(row) {
// are there multiple columns or just one? // are there multiple columns or just one?
// with multiple we have an array of Objects, with one we have an array of whatever the value type is // with multiple we have an array of Objects, with one we have an array of whatever the value type is
var num_columns = row.children('.' + Settings.DATA_COL_CLASS).length var num_columns = row.children('.' + Settings.DATA_COL_CLASS).length
var newName = setting_name + "[" + row_index + "]" + (num_columns > 1 ? "." + key : "");
if (isCheckbox) { if (isCheckbox) {
input.attr("name", setting_name + "[" + row_index + "]" + (num_columns > 1 ? "." + key : "")) input.attr("name", newName)
} else { } else {
input.attr("name", setting_name + "[" + row_index + "]" + (num_columns > 1 ? "." + key : "")) if (isDropdown) {
$(element).children("select").attr("data-hidden-input", newName);
}
input.attr("name", newName);
} }
} else { } else {
// because the name of the setting in question requires the key // because the name of the setting in question requires the key
@ -1435,6 +1459,12 @@ function addTableRow(row) {
input.focus(); input.focus();
focusChanged = true; focusChanged = true;
} }
// if we are adding a dropdown, we should go ahead and make its select
// element is visible
if (isDropdown) {
$(element).children("select").attr("style", "");
}
if (isCheckbox) { if (isCheckbox) {
$(input).find("input").attr("data-changed", "true"); $(input).find("input").attr("data-changed", "true");

View file

@ -2322,6 +2322,11 @@ void DomainServer::updateReplicatedNodes() {
qDebug() << "Setting node to NOT be replicated:" << otherNode->getUUID(); qDebug() << "Setting node to NOT be replicated:" << otherNode->getUUID();
} else if (!isReplicated && shouldReplicate) { } else if (!isReplicated && shouldReplicate) {
qDebug() << "Setting node to replicated:" << otherNode->getUUID(); qDebug() << "Setting node to replicated:" << otherNode->getUUID();
qDebug() << "Setting node to NOT be replicated:"
<< otherNode->getPermissions().getVerifiedUserName() << otherNode->getUUID();
} else if (!isReplicated && shouldReplicate) {
qDebug() << "Setting node to replicated:"
<< otherNode->getPermissions().getVerifiedUserName() << otherNode->getUUID();
} }
otherNode->setIsReplicated(shouldReplicate); otherNode->setIsReplicated(shouldReplicate);
} }
@ -2330,7 +2335,7 @@ void DomainServer::updateReplicatedNodes() {
bool DomainServer::shouldReplicateNode(const Node& node) { bool DomainServer::shouldReplicateNode(const Node& node) {
QString verifiedUsername = node.getPermissions().getVerifiedUserName(); QString verifiedUsername = node.getPermissions().getVerifiedUserName();
// Both he verified username and usernames in _replicatedUsernames are lowercase, so // Both the verified username and usernames in _replicatedUsernames are lowercase, so
// comparisons here are case-insensitive. // comparisons here are case-insensitive.
auto it = find(_replicatedUsernames.cbegin(), _replicatedUsernames.cend(), verifiedUsername); auto it = find(_replicatedUsernames.cbegin(), _replicatedUsernames.cend(), verifiedUsername);
return it != _replicatedUsernames.end() && node.getType() == NodeType::Agent; return it != _replicatedUsernames.end() && node.getType() == NodeType::Agent;

View file

@ -1,14 +1,16 @@
{ {
"RenderShadowTask": { "RenderMainView": {
"Enabled": { "RenderShadowTask": {
"enabled": true
}
},
"RenderDeferredTask": {
"AmbientOcclusion": {
"Enabled": { "Enabled": {
"enabled": true "enabled": true
} }
},
"RenderDeferredTask": {
"AmbientOcclusion": {
"Enabled": {
"enabled": true
}
}
} }
} }
} }

View file

@ -32,7 +32,7 @@ var EventBridge;
var webChannel = new QWebChannel(qt.webChannelTransport, function (channel) { var webChannel = new QWebChannel(qt.webChannelTransport, function (channel) {
// replace the TempEventBridge with the real one. // replace the TempEventBridge with the real one.
var tempEventBridge = EventBridge; var tempEventBridge = EventBridge;
EventBridge = channel.objects.eventBridgeWrapper.eventBridge; EventBridge = channel.objects.eventBridge;
tempEventBridge._callbacks.forEach(function (callback) { tempEventBridge._callbacks.forEach(function (callback) {
EventBridge.scriptEventReceived.connect(callback); EventBridge.scriptEventReceived.connect(callback);
}); });

View file

@ -1,85 +1,89 @@
name = Jointy3 name = mannequin
type = body+head type = body+head
scale = 1 scale = 1
filename = Jointy3/Jointy3.fbx filename = mannequin/mannequin.baked.fbx
texdir = Jointy3/textures joint = jointEyeLeft = LeftEye
joint = jointRightHand = RightHand
joint = jointHead = Head
joint = jointEyeRight = RightEye
joint = jointLean = Spine
joint = jointNeck = Neck joint = jointNeck = Neck
joint = jointLeftHand = LeftHand joint = jointLeftHand = LeftHand
joint = jointEyeRight = RightEye
joint = jointHead = Head
joint = jointRightHand = RightHand
joint = jointRoot = Hips joint = jointRoot = Hips
joint = jointLean = Spine
joint = jointEyeLeft = LeftEye
freeJoint = LeftArm freeJoint = LeftArm
freeJoint = LeftForeArm freeJoint = LeftForeArm
freeJoint = RightArm freeJoint = RightArm
freeJoint = RightForeArm freeJoint = RightForeArm
jointIndex = RightHand = 17 bs = EyeBlink_L = blink = 1
jointIndex = LeftHandIndex3 = 56 bs = JawOpen = mouth_Open = 1
jointIndex = Hips = 0 bs = LipsFunnel = Oo = 1
jointIndex = LeftHandRing2 = 47 bs = BrowsU_L = brow_Up = 1
jointIndex = LeftHandThumb3 = 60 jointIndex = RightHandIndex2 = 27
jointIndex = RightShoulder = 14 jointIndex = LeftHandIndex2 = 51
jointIndex = RightHandRing1 = 30 jointIndex = RightUpLeg = 6
jointIndex = RightHandRing3 = 32 jointIndex = RightToe_End = 10
jointIndex = LeftHandPinky4 = 45 jointIndex = RightEye = 65
jointIndex = LeftHandRing1 = 46
jointIndex = LeftFoot = 8
jointIndex = RightHandIndex2 = 23
jointIndex = RightToeBase = 4
jointIndex = RightHandMiddle4 = 29
jointIndex = RightHandPinky4 = 37
jointIndex = LeftToe_End = 10
jointIndex = RightEye = 66
jointIndex = RightHandPinky2 = 35
jointIndex = RightHandRing2 = 31
jointIndex = LeftHand = 41
jointIndex = RightToe_End = 5
jointIndex = LeftEye = 65
jointIndex = LeftHandThumb2 = 59
jointIndex = pCylinder73Shape1 = 67
jointIndex = LeftShoulder = 38
jointIndex = LeftHandIndex2 = 55
jointIndex = RightForeArm = 16
jointIndex = LeftHandMiddle2 = 51
jointIndex = RightHandRing4 = 33
jointIndex = LeftLeg = 7
jointIndex = LeftHandThumb4 = 61
jointIndex = LeftForeArm = 40
jointIndex = HeadTop_End = 64
jointIndex = RightHandPinky1 = 34
jointIndex = RightHandIndex1 = 22
jointIndex = LeftHandIndex1 = 54
jointIndex = RightLeg = 2
jointIndex = RightHandIndex4 = 25
jointIndex = Neck = 62
jointIndex = LeftHandMiddle1 = 50
jointIndex = RightHandPinky3 = 36
jointIndex = LeftHandPinky2 = 43
jointIndex = RightHandMiddle3 = 28
jointIndex = RightHandThumb4 = 21
jointIndex = LeftUpLeg = 6
jointIndex = RightFoot = 3
jointIndex = LeftHandThumb1 = 58
jointIndex = LeftArm = 39
jointIndex = RightHandMiddle1 = 26
jointIndex = LeftHandRing3 = 48
jointIndex = LeftHandMiddle4 = 53
jointIndex = RightUpLeg = 1
jointIndex = RightHandMiddle2 = 27
jointIndex = LeftToeBase = 9
jointIndex = RightHandThumb2 = 19
jointIndex = Spine2 = 13
jointIndex = Spine = 11
jointIndex = LeftHandRing4 = 49
jointIndex = Head = 63
jointIndex = LeftHandPinky3 = 44
jointIndex = LeftHandPinky1 = 42 jointIndex = LeftHandPinky1 = 42
jointIndex = RightHandThumb1 = 18 jointIndex = RightHandRing1 = 22
jointIndex = LeftHandIndex4 = 57 jointIndex = face = 67
jointIndex = LeftHandMiddle3 = 52 jointIndex = LeftUpLeg = 1
jointIndex = RightHandIndex3 = 24 jointIndex = LeftHand = 41
jointIndex = Spine1 = 12 jointIndex = LeftHandMiddle1 = 58
jointIndex = LeftHandIndex1 = 50
jointIndex = LeftEye = 64
jointIndex = RightHandIndex1 = 26
jointIndex = LeftHandPinky4 = 45
jointIndex = RightArm = 15 jointIndex = RightArm = 15
jointIndex = RightHandThumb3 = 20 jointIndex = LeftShoulder = 38
jointIndex = RightHandPinky2 = 19
jointIndex = RightHandThumb1 = 30
jointIndex = RightForeArm = 16
jointIndex = LeftHandMiddle3 = 60
jointIndex = Neck = 62
jointIndex = LeftHandThumb1 = 54
jointIndex = RightHandMiddle2 = 35
jointIndex = LeftHandMiddle4 = 61
jointIndex = mannequin = 68
jointIndex = Spine1 = 12
jointIndex = RightFoot = 8
jointIndex = RightHand = 17
jointIndex = LeftHandIndex3 = 52
jointIndex = RightHandIndex3 = 28
jointIndex = RightHandMiddle4 = 37
jointIndex = LeftLeg = 2
jointIndex = RightHandMiddle1 = 34
jointIndex = Spine2 = 13
jointIndex = LeftHandMiddle2 = 59
jointIndex = LeftHandPinky3 = 44
jointIndex = LeftHandThumb3 = 56
jointIndex = LeftHandRing4 = 49
jointIndex = RightHandThumb2 = 31
jointIndex = LeftHandRing3 = 48
jointIndex = HeadTop_End = 66
jointIndex = LeftHandThumb4 = 57
jointIndex = RightHandThumb3 = 32
jointIndex = RightHandPinky1 = 18
jointIndex = RightLeg = 7
jointIndex = RightHandMiddle3 = 36
jointIndex = RightHandPinky3 = 20
jointIndex = LeftToeBase = 4
jointIndex = LeftForeArm = 40
jointIndex = RightShoulder = 14
jointIndex = LeftHandRing2 = 47
jointIndex = LeftHandThumb2 = 55
jointIndex = Head = 63
jointIndex = RightHandRing4 = 25
jointIndex = LeftHandRing1 = 46
jointIndex = LeftFoot = 3
jointIndex = RightHandRing3 = 24
jointIndex = RightHandThumb4 = 33
jointIndex = LeftArm = 39
jointIndex = LeftToe_End = 5
jointIndex = RightToeBase = 9
jointIndex = RightHandPinky4 = 21
jointIndex = Spine = 11
jointIndex = LeftHandIndex4 = 53
jointIndex = LeftHandPinky2 = 43
jointIndex = RightHandIndex4 = 29
jointIndex = Hips = 0
jointIndex = RightHandRing2 = 23

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -21,8 +21,6 @@ ScrollingWindow {
property alias url: webview.url property alias url: webview.url
property alias webView: webview property alias webView: webview
property alias eventBridge: eventBridgeWrapper.eventBridge
signal loadingChanged(int status) signal loadingChanged(int status)
x: 100 x: 100
@ -210,17 +208,6 @@ ScrollingWindow {
url: "https://highfidelity.com/" url: "https://highfidelity.com/"
profile: FileTypeProfile; profile: FileTypeProfile;
property alias eventBridgeWrapper: eventBridgeWrapper
QtObject {
id: eventBridgeWrapper
WebChannel.id: "eventBridgeWrapper"
property var eventBridge;
}
webChannel.registeredObjects: [eventBridgeWrapper]
// Create a global EventBridge object for raiseAndLowerKeyboard. // Create a global EventBridge object for raiseAndLowerKeyboard.
WebEngineScript { WebEngineScript {
id: createGlobalEventBridge id: createGlobalEventBridge
@ -267,6 +254,8 @@ ScrollingWindow {
} }
Component.onCompleted: { Component.onCompleted: {
webChannel.registerObject("eventBridge", eventBridge);
webChannel.registerObject("eventBridgeWrapper", eventBridgeWrapper);
desktop.initWebviewProfileHandlers(webview.profile); desktop.initWebviewProfileHandlers(webview.profile);
} }
} }

View file

@ -26,15 +26,8 @@ Windows.ScrollingWindow {
// Don't destroy on close... otherwise the JS/C++ will have a dangling pointer // Don't destroy on close... otherwise the JS/C++ will have a dangling pointer
destroyOnCloseButton: false destroyOnCloseButton: false
property alias source: webview.url property alias source: webview.url
property alias eventBridge: eventBridgeWrapper.eventBridge;
property alias scriptUrl: webview.userScriptUrl property alias scriptUrl: webview.userScriptUrl
QtObject {
id: eventBridgeWrapper
WebChannel.id: "eventBridgeWrapper"
property var eventBridge;
}
// This is for JS/QML communication, which is unused in a WebWindow, // This is for JS/QML communication, which is unused in a WebWindow,
// but not having this here results in spurious warnings about a // but not having this here results in spurious warnings about a
// missing signal // missing signal
@ -70,7 +63,6 @@ Windows.ScrollingWindow {
url: "about:blank" url: "about:blank"
anchors.fill: parent anchors.fill: parent
focus: true focus: true
webChannel.registeredObjects: [eventBridgeWrapper]
property string userScriptUrl: "" property string userScriptUrl: ""
@ -107,6 +99,8 @@ Windows.ScrollingWindow {
} }
Component.onCompleted: { Component.onCompleted: {
webChannel.registerObject("eventBridge", eventBridge);
webChannel.registerObject("eventBridgeWrapper", eventBridgeWrapper);
eventBridge.webEventReceived.connect(onWebEventReceived); eventBridge.webEventReceived.connect(onWebEventReceived);
} }
} }

View file

@ -30,15 +30,6 @@ Windows.Window {
property bool keyboardRaised: false property bool keyboardRaised: false
property bool punctuationMode: false property bool punctuationMode: false
// JavaScript event bridge object in case QML content includes Web content.
property alias eventBridge: eventBridgeWrapper.eventBridge;
QtObject {
id: eventBridgeWrapper
WebChannel.id: "eventBridgeWrapper"
property var eventBridge;
}
onSourceChanged: { onSourceChanged: {
if (dynamicContent) { if (dynamicContent) {
dynamicContent.destroy(); dynamicContent.destroy();

View file

@ -18,7 +18,6 @@ Item {
property variant permissionsBar: {'securityOrigin':'none','feature':'none'} property variant permissionsBar: {'securityOrigin':'none','feature':'none'}
property alias url: webview.url property alias url: webview.url
property WebEngineView webView: webview property WebEngineView webView: webview
property alias eventBridge: eventBridgeWrapper.eventBridge
property bool canGoBack: webview.canGoBack property bool canGoBack: webview.canGoBack
property bool canGoForward: webview.canGoForward property bool canGoForward: webview.canGoForward
@ -32,12 +31,6 @@ Item {
webview.profile = profile; webview.profile = profile;
} }
QtObject {
id: eventBridgeWrapper
WebChannel.id: "eventBridgeWrapper"
property var eventBridge;
}
WebEngineView { WebEngineView {
id: webview id: webview
objectName: "webEngineView" objectName: "webEngineView"
@ -78,9 +71,10 @@ Item {
property string newUrl: "" property string newUrl: ""
webChannel.registeredObjects: [eventBridgeWrapper]
Component.onCompleted: { Component.onCompleted: {
webChannel.registerObject("eventBridge", eventBridge);
webChannel.registerObject("eventBridgeWrapper", eventBridgeWrapper);
// Ensure the JS from the web-engine makes it to our logging // Ensure the JS from the web-engine makes it to our logging
webview.javaScriptConsoleMessage.connect(function(level, message, lineNumber, sourceID) { webview.javaScriptConsoleMessage.connect(function(level, message, lineNumber, sourceID) {
console.log("Web Entity JS message: " + sourceID + " " + lineNumber + " " + message); console.log("Web Entity JS message: " + sourceID + " " + lineNumber + " " + message);

View file

@ -79,15 +79,11 @@ ScrollingWindow {
id: webView id: webView
anchors.fill: parent anchors.fill: parent
enabled: false enabled: false
property alias eventBridgeWrapper: eventBridgeWrapper Component.onCompleted: {
webChannel.registerObject("eventBridge", eventBridge);
QtObject { webChannel.registerObject("eventBridgeWrapper", eventBridgeWrapper);
id: eventBridgeWrapper
WebChannel.id: "eventBridgeWrapper"
property var eventBridge
} }
webChannel.registeredObjects: [eventBridgeWrapper]
onEnabledChanged: toolWindow.updateVisiblity() onEnabledChanged: toolWindow.updateVisiblity()
} }
} }
@ -251,12 +247,9 @@ ScrollingWindow {
tab.enabled = true; tab.enabled = true;
tab.originalUrl = properties.source; tab.originalUrl = properties.source;
var eventBridge = properties.eventBridge;
var result = tab.item; var result = tab.item;
result.enabled = true; result.enabled = true;
tabView.tabCount++; tabView.tabCount++;
result.eventBridgeWrapper.eventBridge = eventBridge;
result.url = properties.source; result.url = properties.source;
return result; return result;
} }

View file

@ -6,7 +6,6 @@ import "../controls-uit" as HiFiControls
Item { Item {
property alias url: root.url property alias url: root.url
property alias scriptURL: root.userScriptUrl property alias scriptURL: root.userScriptUrl
property alias eventBridge: eventBridgeWrapper.eventBridge
property alias canGoBack: root.canGoBack; property alias canGoBack: root.canGoBack;
property var goBack: root.goBack; property var goBack: root.goBack;
property alias urlTag: root.urlTag property alias urlTag: root.urlTag
@ -22,12 +21,6 @@ Item {
} }
*/ */
QtObject {
id: eventBridgeWrapper
WebChannel.id: "eventBridgeWrapper"
property var eventBridge;
}
property alias viewProfile: root.profile property alias viewProfile: root.profile
WebEngineView { WebEngineView {
@ -71,10 +64,11 @@ Item {
userScripts: [ createGlobalEventBridge, raiseAndLowerKeyboard, userScript ] userScripts: [ createGlobalEventBridge, raiseAndLowerKeyboard, userScript ]
property string newUrl: "" property string newUrl: ""
webChannel.registeredObjects: [eventBridgeWrapper]
Component.onCompleted: { Component.onCompleted: {
webChannel.registerObject("eventBridge", eventBridge);
webChannel.registerObject("eventBridgeWrapper", eventBridgeWrapper);
// Ensure the JS from the web-engine makes it to our logging // Ensure the JS from the web-engine makes it to our logging
root.javaScriptConsoleMessage.connect(function(level, message, lineNumber, sourceID) { root.javaScriptConsoleMessage.connect(function(level, message, lineNumber, sourceID) {
console.log("Web Entity JS message: " + sourceID + " " + lineNumber + " " + message); console.log("Web Entity JS message: " + sourceID + " " + lineNumber + " " + message);

View file

@ -17,7 +17,6 @@ Item {
property int headerHeight: 70 property int headerHeight: 70
property string url property string url
property string scriptURL property string scriptURL
property alias eventBridge: eventBridgeWrapper.eventBridge
property bool keyboardEnabled: false property bool keyboardEnabled: false
property bool keyboardRaised: false property bool keyboardRaised: false
property bool punctuationMode: false property bool punctuationMode: false
@ -135,12 +134,6 @@ Item {
loadUrl(url); loadUrl(url);
} }
QtObject {
id: eventBridgeWrapper
WebChannel.id: "eventBridgeWrapper"
property var eventBridge;
}
WebEngineView { WebEngineView {
id: webview id: webview
objectName: "webEngineView" objectName: "webEngineView"
@ -182,9 +175,9 @@ Item {
property string newUrl: "" property string newUrl: ""
webChannel.registeredObjects: [eventBridgeWrapper]
Component.onCompleted: { Component.onCompleted: {
webChannel.registerObject("eventBridge", eventBridge);
webChannel.registerObject("eventBridgeWrapper", eventBridgeWrapper);
// Ensure the JS from the web-engine makes it to our logging // Ensure the JS from the web-engine makes it to our logging
webview.javaScriptConsoleMessage.connect(function(level, message, lineNumber, sourceID) { webview.javaScriptConsoleMessage.connect(function(level, message, lineNumber, sourceID) {
console.log("Web Entity JS message: " + sourceID + " " + lineNumber + " " + message); console.log("Web Entity JS message: " + sourceID + " " + lineNumber + " " + message);

View file

@ -6,7 +6,6 @@ import "../controls-uit" as HiFiControls
Item { Item {
property alias url: root.url property alias url: root.url
property alias scriptURL: root.userScriptUrl property alias scriptURL: root.userScriptUrl
property alias eventBridge: eventBridgeWrapper.eventBridge
property alias canGoBack: root.canGoBack; property alias canGoBack: root.canGoBack;
property var goBack: root.goBack; property var goBack: root.goBack;
property alias urlTag: root.urlTag property alias urlTag: root.urlTag
@ -22,12 +21,6 @@ Item {
} }
*/ */
QtObject {
id: eventBridgeWrapper
WebChannel.id: "eventBridgeWrapper"
property var eventBridge;
}
property alias viewProfile: root.profile property alias viewProfile: root.profile
WebEngineView { WebEngineView {
@ -72,9 +65,9 @@ Item {
property string newUrl: "" property string newUrl: ""
webChannel.registeredObjects: [eventBridgeWrapper]
Component.onCompleted: { Component.onCompleted: {
webChannel.registerObject("eventBridge", eventBridge);
webChannel.registerObject("eventBridgeWrapper", eventBridgeWrapper);
// Ensure the JS from the web-engine makes it to our logging // Ensure the JS from the web-engine makes it to our logging
root.javaScriptConsoleMessage.connect(function(level, message, lineNumber, sourceID) { root.javaScriptConsoleMessage.connect(function(level, message, lineNumber, sourceID) {
console.log("Web Entity JS message: " + sourceID + " " + lineNumber + " " + message); console.log("Web Entity JS message: " + sourceID + " " + lineNumber + " " + message);

View file

@ -20,7 +20,6 @@ TabletModalWindow {
id: loginDialogRoot id: loginDialogRoot
objectName: "LoginDialog" objectName: "LoginDialog"
property var eventBridge;
signal sendToScript(var message); signal sendToScript(var message);
property bool isHMD: false property bool isHMD: false
property bool gotoPreviousApp: false; property bool gotoPreviousApp: false;

View file

@ -24,8 +24,6 @@ Window {
resizable: true resizable: true
modality: Qt.ApplicationModal modality: Qt.ApplicationModal
property alias eventBridge: eventBridgeWrapper.eventBridge
Item { Item {
anchors.fill: parent anchors.fill: parent
@ -45,16 +43,6 @@ Window {
bottom: keyboard.top bottom: keyboard.top
} }
property alias eventBridgeWrapper: eventBridgeWrapper
QtObject {
id: eventBridgeWrapper
WebChannel.id: "eventBridgeWrapper"
property var eventBridge;
}
webChannel.registeredObjects: [eventBridgeWrapper]
// Create a global EventBridge object for raiseAndLowerKeyboard. // Create a global EventBridge object for raiseAndLowerKeyboard.
WebEngineScript { WebEngineScript {
id: createGlobalEventBridge id: createGlobalEventBridge
@ -73,6 +61,10 @@ Window {
userScripts: [ createGlobalEventBridge, raiseAndLowerKeyboard ] userScripts: [ createGlobalEventBridge, raiseAndLowerKeyboard ]
Component.onCompleted: {
webChannel.registerObject("eventBridge", eventBridge);
webChannel.registerObject("eventBridgeWrapper", eventBridgeWrapper);
}
} }
Keyboard { Keyboard {

View file

@ -116,9 +116,7 @@ Preference {
Component { Component {
id: tabletAvatarBrowserBuilder; id: tabletAvatarBrowserBuilder;
TabletAvatarBrowser { TabletAvatarBrowser { }
eventBridge: tabletRoot.eventBridge
}
} }
} }

View file

@ -31,7 +31,6 @@ Rectangle {
HifiConstants { id: hifi; } HifiConstants { id: hifi; }
objectName: "AudioWindow" objectName: "AudioWindow"
property var eventBridge;
property string title: "Audio Options" property string title: "Audio Options"
signal sendToScript(var message); signal sendToScript(var message);

View file

@ -44,7 +44,6 @@ Rectangle {
property var activeTab: "nearbyTab"; property var activeTab: "nearbyTab";
property bool currentlyEditingDisplayName: false property bool currentlyEditingDisplayName: false
property bool punctuationMode: false; property bool punctuationMode: false;
property var eventBridge;
HifiConstants { id: hifi; } HifiConstants { id: hifi; }
@ -129,7 +128,7 @@ Rectangle {
pal.sendToScript({method: 'refreshNearby', params: params}); pal.sendToScript({method: 'refreshNearby', params: params});
} }
Item { Rectangle {
id: palTabContainer; id: palTabContainer;
// Anchors // Anchors
anchors { anchors {
@ -138,6 +137,7 @@ Rectangle {
left: parent.left; left: parent.left;
right: parent.right; right: parent.right;
} }
color: "white";
Rectangle { Rectangle {
id: tabSelectorContainer; id: tabSelectorContainer;
// Anchors // Anchors
@ -1043,7 +1043,6 @@ Rectangle {
} // Keyboard } // Keyboard
HifiControls.TabletWebView { HifiControls.TabletWebView {
eventBridge: pal.eventBridge;
id: userInfoViewer; id: userInfoViewer;
anchors { anchors {
top: parent.top; top: parent.top;

View file

@ -24,7 +24,6 @@ Rectangle {
property string title: "Asset Browser" property string title: "Asset Browser"
property bool keyboardRaised: false property bool keyboardRaised: false
property var eventBridge;
signal sendToScript(var message); signal sendToScript(var message);
property bool isHMD: false property bool isHMD: false

View file

@ -20,7 +20,6 @@ Rectangle {
id: root id: root
objectName: "DCConectionTiming" objectName: "DCConectionTiming"
property var eventBridge;
signal sendToScript(var message); signal sendToScript(var message);
property bool isHMD: false property bool isHMD: false

View file

@ -19,7 +19,6 @@ Rectangle {
id: root id: root
objectName: "DebugWindow" objectName: "DebugWindow"
property var eventBridge;
property var title: "Debug Window" property var title: "Debug Window"
property bool isHMD: false property bool isHMD: false

View file

@ -20,7 +20,6 @@ Rectangle {
id: root id: root
objectName: "EntityStatistics" objectName: "EntityStatistics"
property var eventBridge;
signal sendToScript(var message); signal sendToScript(var message);
property bool isHMD: false property bool isHMD: false

View file

@ -20,7 +20,6 @@ Rectangle {
id: root id: root
objectName: "LODTools" objectName: "LODTools"
property var eventBridge;
signal sendToScript(var message); signal sendToScript(var message);
property bool isHMD: false property bool isHMD: false

View file

@ -23,7 +23,6 @@ Rectangle {
property string title: "Running Scripts" property string title: "Running Scripts"
HifiConstants { id: hifi } HifiConstants { id: hifi }
signal sendToScript(var message); signal sendToScript(var message);
property var eventBridge;
property var scripts: ScriptDiscoveryService; property var scripts: ScriptDiscoveryService;
property var scriptsModel: scripts.scriptsModelFilter property var scriptsModel: scripts.scriptsModelFilter
property var runningScriptsModel: ListModel { } property var runningScriptsModel: ListModel { }

View file

@ -7,14 +7,12 @@ StackView {
objectName: "stack" objectName: "stack"
initialItem: Qt.resolvedUrl('EditTabView.qml') initialItem: Qt.resolvedUrl('EditTabView.qml')
property var eventBridge;
signal sendToScript(var message); signal sendToScript(var message);
HifiConstants { id: hifi } HifiConstants { id: hifi }
function pushSource(path) { function pushSource(path) {
editRoot.push(Qt.resolvedUrl(path)); editRoot.push(Qt.resolvedUrl(path));
editRoot.currentItem.eventBridge = editRoot.eventBridge;
editRoot.currentItem.sendToScript.connect(editRoot.sendToScript); editRoot.currentItem.sendToScript.connect(editRoot.sendToScript);
} }

View file

@ -181,7 +181,6 @@ TabView {
WebView { WebView {
id: entityListToolWebView id: entityListToolWebView
url: "../../../../../scripts/system/html/entityList.html" url: "../../../../../scripts/system/html/entityList.html"
eventBridge: editRoot.eventBridge
anchors.fill: parent anchors.fill: parent
enabled: true enabled: true
} }
@ -196,7 +195,6 @@ TabView {
WebView { WebView {
id: entityPropertiesWebView id: entityPropertiesWebView
url: "../../../../../scripts/system/html/entityProperties.html" url: "../../../../../scripts/system/html/entityProperties.html"
eventBridge: editRoot.eventBridge
anchors.fill: parent anchors.fill: parent
enabled: true enabled: true
} }
@ -211,7 +209,6 @@ TabView {
WebView { WebView {
id: gridControlsWebView id: gridControlsWebView
url: "../../../../../scripts/system/html/gridControls.html" url: "../../../../../scripts/system/html/gridControls.html"
eventBridge: editRoot.eventBridge
anchors.fill: parent anchors.fill: parent
enabled: true enabled: true
} }
@ -226,7 +223,6 @@ TabView {
WebView { WebView {
id: particleExplorerWebView id: particleExplorerWebView
url: "../../../../../scripts/system/particle_explorer/particleExplorer.html" url: "../../../../../scripts/system/particle_explorer/particleExplorer.html"
eventBridge: editRoot.eventBridge
anchors.fill: parent anchors.fill: parent
enabled: true enabled: true
} }
@ -289,7 +285,7 @@ TabView {
editTabView.currentIndex = id; editTabView.currentIndex = id;
} else { } else {
console.warn('Attempt to switch to invalid tab:', id); console.warn('Attempt to switch to invalid tab:', id);
} }
} else if (typeof id === 'string'){ } else if (typeof id === 'string'){
switch (id.toLowerCase()) { switch (id.toLowerCase()) {
case 'create': case 'create':

View file

@ -18,7 +18,6 @@ import "../../dialogs"
Rectangle { Rectangle {
id: inputRecorder id: inputRecorder
property var eventBridge;
HifiConstants { id: hifi } HifiConstants { id: hifi }
signal sendToScript(var message); signal sendToScript(var message);
color: hifi.colors.baseGray; color: hifi.colors.baseGray;

View file

@ -20,7 +20,6 @@ Rectangle {
// height: parent.height // height: parent.height
HifiConstants { id: hifi } HifiConstants { id: hifi }
color: hifi.colors.baseGray; color: hifi.colors.baseGray;
property var eventBridge;
signal sendToScript(var message); signal sendToScript(var message);
property bool keyboardEnabled: false property bool keyboardEnabled: false
property bool punctuationMode: false property bool punctuationMode: false

View file

@ -29,7 +29,6 @@ StackView {
initialItem: addressBarDialog initialItem: addressBarDialog
width: parent !== null ? parent.width : undefined width: parent !== null ? parent.width : undefined
height: parent !== null ? parent.height : undefined height: parent !== null ? parent.height : undefined
property var eventBridge;
property int cardWidth: 212; property int cardWidth: 212;
property int cardHeight: 152; property int cardHeight: 152;
property string metaverseBase: addressBarDialog.metaverseServerUrl + "/api/v1/"; property string metaverseBase: addressBarDialog.metaverseServerUrl + "/api/v1/";
@ -80,7 +79,6 @@ StackView {
var card = tabletWebView.createObject(); var card = tabletWebView.createObject();
card.url = addressBarDialog.metaverseServerUrl + targetString; card.url = addressBarDialog.metaverseServerUrl + targetString;
card.parentStackItem = root; card.parentStackItem = root;
card.eventBridge = root.eventBridge;
root.push(card); root.push(card);
return; return;
} }

View file

@ -25,7 +25,6 @@ Item {
property bool keyboardRaised: false property bool keyboardRaised: false
property bool punctuationMode: false property bool punctuationMode: false
property var eventBridge;
signal sendToScript(var message); signal sendToScript(var message);
anchors.fill: parent anchors.fill: parent

View file

@ -19,7 +19,6 @@ StackView {
objectName: "stack" objectName: "stack"
property string title: "Audio Settings" property string title: "Audio Settings"
property var eventBridge;
signal sendToScript(var message); signal sendToScript(var message);
function pushSource(path) { function pushSource(path) {

View file

@ -19,7 +19,6 @@ StackView {
objectName: "stack" objectName: "stack"
property string title: "Avatar Settings" property string title: "Avatar Settings"
property var eventBridge;
signal sendToScript(var message); signal sendToScript(var message);
function pushSource(path) { function pushSource(path) {

View file

@ -19,7 +19,6 @@ StackView {
objectName: "stack" objectName: "stack"
property string title: "General Settings" property string title: "General Settings"
property alias gotoPreviousApp: root.gotoPreviousApp; property alias gotoPreviousApp: root.gotoPreviousApp;
property var eventBridge;
signal sendToScript(var message); signal sendToScript(var message);
function pushSource(path) { function pushSource(path) {

View file

@ -19,7 +19,6 @@ StackView {
objectName: "stack" objectName: "stack"
property string title: "Graphics Settings" property string title: "Graphics Settings"
property var eventBridge;
signal sendToScript(var message); signal sendToScript(var message);
function pushSource(path) { function pushSource(path) {

View file

@ -19,7 +19,6 @@ StackView {
objectName: "stack" objectName: "stack"
property string title: "LOD Settings" property string title: "LOD Settings"
property var eventBridge;
signal sendToScript(var message); signal sendToScript(var message);
function pushSource(path) { function pushSource(path) {

View file

@ -21,7 +21,6 @@ FocusScope {
property var point: Qt.point(50, 50); property var point: Qt.point(50, 50);
TabletMenuStack { id: menuPopperUpper } TabletMenuStack { id: menuPopperUpper }
property string subMenu: "" property string subMenu: ""
property var eventBridge;
signal sendToScript(var message); signal sendToScript(var message);
Rectangle { Rectangle {

View file

@ -49,7 +49,6 @@ Item {
function pushSource(path) { function pushSource(path) {
d.push(Qt.resolvedUrl(path)); d.push(Qt.resolvedUrl(path));
d.currentItem.eventBridge = tabletMenu.eventBridge
d.currentItem.sendToScript.connect(tabletMenu.sendToScript); d.currentItem.sendToScript.connect(tabletMenu.sendToScript);
d.currentItem.focus = true; d.currentItem.focus = true;
d.currentItem.forceActiveFocus(); d.currentItem.forceActiveFocus();

View file

@ -19,7 +19,6 @@ StackView {
objectName: "stack" objectName: "stack"
property var title: "Networking Settings" property var title: "Networking Settings"
property var eventBridge;
signal sendToScript(var message); signal sendToScript(var message);
function pushSource(path) { function pushSource(path) {

View file

@ -8,7 +8,6 @@ Item {
id: tabletRoot id: tabletRoot
objectName: "tabletRoot" objectName: "tabletRoot"
property string username: "Unknown user" property string username: "Unknown user"
property var eventBridge;
property var rootMenu; property var rootMenu;
property var openModal: null; property var openModal: null;
property var openMessage: null; property var openMessage: null;
@ -111,7 +110,6 @@ Item {
function openBrowserWindow(request, profile) { function openBrowserWindow(request, profile) {
var component = Qt.createComponent("../../controls/TabletWebView.qml"); var component = Qt.createComponent("../../controls/TabletWebView.qml");
var newWindow = component.createObject(tabletRoot); var newWindow = component.createObject(tabletRoot);
newWindow.eventBridge = tabletRoot.eventBridge;
newWindow.remove = true; newWindow.remove = true;
newWindow.profile = profile; newWindow.profile = profile;
request.openIn(newWindow.webView); request.openIn(newWindow.webView);
@ -175,7 +173,7 @@ Item {
// Hook up callback for clara.io download from the marketplace. // Hook up callback for clara.io download from the marketplace.
Connections { Connections {
id: eventBridgeConnection id: eventBridgeConnection
target: null target: eventBridge
onWebEventReceived: { onWebEventReceived: {
if (message.slice(0, 17) === "CLARA.IO DOWNLOAD") { if (message.slice(0, 17) === "CLARA.IO DOWNLOAD") {
ApplicationInterface.addAssetToWorldFromURL(message.slice(18)); ApplicationInterface.addAssetToWorldFromURL(message.slice(18));
@ -184,10 +182,6 @@ Item {
} }
onLoaded: { onLoaded: {
if (loader.item.hasOwnProperty("eventBridge")) {
loader.item.eventBridge = eventBridge;
eventBridgeConnection.target = eventBridge
}
if (loader.item.hasOwnProperty("sendToScript")) { if (loader.item.hasOwnProperty("sendToScript")) {
loader.item.sendToScript.connect(tabletRoot.sendToScript); loader.item.sendToScript.connect(tabletRoot.sendToScript);
} }

View file

@ -18,7 +18,6 @@ Windows.ScrollingWindow {
id: tabletRoot id: tabletRoot
objectName: "tabletRoot" objectName: "tabletRoot"
property string username: "Unknown user" property string username: "Unknown user"
property var eventBridge;
property var rootMenu; property var rootMenu;
property string subMenu: "" property string subMenu: ""
@ -93,7 +92,7 @@ Windows.ScrollingWindow {
// Hook up callback for clara.io download from the marketplace. // Hook up callback for clara.io download from the marketplace.
Connections { Connections {
id: eventBridgeConnection id: eventBridgeConnection
target: null target: eventBridge
onWebEventReceived: { onWebEventReceived: {
if (message.slice(0, 17) === "CLARA.IO DOWNLOAD") { if (message.slice(0, 17) === "CLARA.IO DOWNLOAD") {
ApplicationInterface.addAssetToWorldFromURL(message.slice(18)); ApplicationInterface.addAssetToWorldFromURL(message.slice(18));
@ -102,10 +101,6 @@ Windows.ScrollingWindow {
} }
onLoaded: { onLoaded: {
if (loader.item.hasOwnProperty("eventBridge")) {
loader.item.eventBridge = eventBridge;
eventBridgeConnection.target = eventBridge
}
if (loader.item.hasOwnProperty("sendToScript")) { if (loader.item.hasOwnProperty("sendToScript")) {
loader.item.sendToScript.connect(tabletRoot.sendToScript); loader.item.sendToScript.connect(tabletRoot.sendToScript);
} }

View file

@ -27,8 +27,6 @@ Item {
property bool keyboardRaised: false property bool keyboardRaised: false
property bool punctuationMode: false property bool punctuationMode: false
property alias eventBridge: eventBridgeWrapper.eventBridge
anchors.fill: parent anchors.fill: parent
BaseWebView { BaseWebView {
@ -43,14 +41,6 @@ Item {
bottom: footer.top bottom: footer.top
} }
QtObject {
id: eventBridgeWrapper
WebChannel.id: "eventBridgeWrapper"
property var eventBridge;
}
webChannel.registeredObjects: [eventBridgeWrapper]
// Create a global EventBridge object for raiseAndLowerKeyboard. // Create a global EventBridge object for raiseAndLowerKeyboard.
WebEngineScript { WebEngineScript {
id: createGlobalEventBridge id: createGlobalEventBridge
@ -68,6 +58,11 @@ Item {
} }
userScripts: [ createGlobalEventBridge, raiseAndLowerKeyboard ] userScripts: [ createGlobalEventBridge, raiseAndLowerKeyboard ]
Component.onCompleted: {
webChannel.registerObject("eventBridge", eventBridge);
webChannel.registerObject("eventBridgeWrapper", eventBridgeWrapper);
}
} }
Rectangle { Rectangle {

View file

@ -114,6 +114,7 @@
#include <render/RenderFetchCullSortTask.h> #include <render/RenderFetchCullSortTask.h>
#include <RenderDeferredTask.h> #include <RenderDeferredTask.h>
#include <RenderForwardTask.h> #include <RenderForwardTask.h>
#include <RenderViewTask.h>
#include <ResourceCache.h> #include <ResourceCache.h>
#include <ResourceRequest.h> #include <ResourceRequest.h>
#include <SandboxUtils.h> #include <SandboxUtils.h>
@ -1866,15 +1867,9 @@ void Application::initializeGL() {
// Set up the render engine // Set up the render engine
render::CullFunctor cullFunctor = LODManager::shouldRender; render::CullFunctor cullFunctor = LODManager::shouldRender;
_renderEngine->addJob<RenderShadowTask>("RenderShadowTask", cullFunctor);
const auto items = _renderEngine->addJob<RenderFetchCullSortTask>("FetchCullSort", cullFunctor);
assert(items.canCast<RenderFetchCullSortTask::Output>());
static const QString RENDER_FORWARD = "HIFI_RENDER_FORWARD"; static const QString RENDER_FORWARD = "HIFI_RENDER_FORWARD";
if (QProcessEnvironment::systemEnvironment().contains(RENDER_FORWARD)) { bool isDeferred = !QProcessEnvironment::systemEnvironment().contains(RENDER_FORWARD);
_renderEngine->addJob<RenderForwardTask>("Forward", items); _renderEngine->addJob<RenderViewTask>("RenderMainView", cullFunctor, isDeferred);
} else {
_renderEngine->addJob<RenderDeferredTask>("RenderDeferredTask", items);
}
_renderEngine->load(); _renderEngine->load();
_renderEngine->registerScene(_main3DScene); _renderEngine->registerScene(_main3DScene);
@ -5076,7 +5071,7 @@ namespace render {
template <> const ItemKey payloadGetKey(const WorldBoxRenderData::Pointer& stuff) { return ItemKey::Builder::opaqueShape(); } template <> const ItemKey payloadGetKey(const WorldBoxRenderData::Pointer& stuff) { return ItemKey::Builder::opaqueShape(); }
template <> const Item::Bound payloadGetBound(const WorldBoxRenderData::Pointer& stuff) { return Item::Bound(); } template <> const Item::Bound payloadGetBound(const WorldBoxRenderData::Pointer& stuff) { return Item::Bound(); }
template <> void payloadRender(const WorldBoxRenderData::Pointer& stuff, RenderArgs* args) { template <> void payloadRender(const WorldBoxRenderData::Pointer& stuff, RenderArgs* args) {
if (args->_renderMode != RenderArgs::MIRROR_RENDER_MODE && Menu::getInstance()->isOptionChecked(MenuOption::WorldAxes)) { if (Menu::getInstance()->isOptionChecked(MenuOption::WorldAxes)) {
PerformanceTimer perfTimer("worldBox"); PerformanceTimer perfTimer("worldBox");
auto& batch = *args->_batch; auto& batch = *args->_batch;

View file

@ -73,7 +73,7 @@ CrashHandler::Action CrashHandler::promptUserForAction(bool showCrashMessage) {
layout->addWidget(label); layout->addWidget(label);
QRadioButton* option1 = new QRadioButton("Reset all my settings"); QRadioButton* option1 = new QRadioButton("Reset all my settings");
QRadioButton* option2 = new QRadioButton("Reset my settings but retain avatar info."); QRadioButton* option2 = new QRadioButton("Reset my settings but keep essential info");
QRadioButton* option3 = new QRadioButton("Continue with my current settings"); QRadioButton* option3 = new QRadioButton("Continue with my current settings");
option3->setChecked(true); option3->setChecked(true);
layout->addWidget(option1); layout->addWidget(option1);
@ -95,7 +95,7 @@ CrashHandler::Action CrashHandler::promptUserForAction(bool showCrashMessage) {
return CrashHandler::DELETE_INTERFACE_INI; return CrashHandler::DELETE_INTERFACE_INI;
} }
if (option2->isChecked()) { if (option2->isChecked()) {
return CrashHandler::RETAIN_AVATAR_INFO; return CrashHandler::RETAIN_IMPORTANT_INFO;
} }
} }
@ -104,7 +104,7 @@ CrashHandler::Action CrashHandler::promptUserForAction(bool showCrashMessage) {
} }
void CrashHandler::handleCrash(CrashHandler::Action action) { void CrashHandler::handleCrash(CrashHandler::Action action) {
if (action != CrashHandler::DELETE_INTERFACE_INI && action != CrashHandler::RETAIN_AVATAR_INFO) { if (action != CrashHandler::DELETE_INTERFACE_INI && action != CrashHandler::RETAIN_IMPORTANT_INFO) {
// CrashHandler::DO_NOTHING or unexpected value // CrashHandler::DO_NOTHING or unexpected value
return; return;
} }
@ -116,12 +116,15 @@ void CrashHandler::handleCrash(CrashHandler::Action action) {
const QString DISPLAY_NAME_KEY = "displayName"; const QString DISPLAY_NAME_KEY = "displayName";
const QString FULL_AVATAR_URL_KEY = "fullAvatarURL"; const QString FULL_AVATAR_URL_KEY = "fullAvatarURL";
const QString FULL_AVATAR_MODEL_NAME_KEY = "fullAvatarModelName"; const QString FULL_AVATAR_MODEL_NAME_KEY = "fullAvatarModelName";
const QString TUTORIAL_COMPLETE_FLAG_KEY = "tutorialComplete";
QString displayName; QString displayName;
QUrl fullAvatarURL; QUrl fullAvatarURL;
QString fullAvatarModelName; QString fullAvatarModelName;
QUrl address; QUrl address;
bool tutorialComplete = false;
if (action == CrashHandler::RETAIN_AVATAR_INFO) { if (action == CrashHandler::RETAIN_IMPORTANT_INFO) {
// Read avatar info // Read avatar info
// Location and orientation // Location and orientation
@ -135,6 +138,9 @@ void CrashHandler::handleCrash(CrashHandler::Action action) {
fullAvatarURL = settings.value(FULL_AVATAR_URL_KEY).toUrl(); fullAvatarURL = settings.value(FULL_AVATAR_URL_KEY).toUrl();
fullAvatarModelName = settings.value(FULL_AVATAR_MODEL_NAME_KEY).toString(); fullAvatarModelName = settings.value(FULL_AVATAR_MODEL_NAME_KEY).toString();
settings.endGroup(); settings.endGroup();
// Tutorial complete
tutorialComplete = settings.value(TUTORIAL_COMPLETE_FLAG_KEY).toBool();
} }
// Delete Interface.ini // Delete Interface.ini
@ -143,7 +149,7 @@ void CrashHandler::handleCrash(CrashHandler::Action action) {
settingsFile.remove(); settingsFile.remove();
} }
if (action == CrashHandler::RETAIN_AVATAR_INFO) { if (action == CrashHandler::RETAIN_IMPORTANT_INFO) {
// Write avatar info // Write avatar info
// Location and orientation // Location and orientation
@ -157,6 +163,9 @@ void CrashHandler::handleCrash(CrashHandler::Action action) {
settings.setValue(FULL_AVATAR_URL_KEY, fullAvatarURL); settings.setValue(FULL_AVATAR_URL_KEY, fullAvatarURL);
settings.setValue(FULL_AVATAR_MODEL_NAME_KEY, fullAvatarModelName); settings.setValue(FULL_AVATAR_MODEL_NAME_KEY, fullAvatarModelName);
settings.endGroup(); settings.endGroup();
// Tutorial complete
settings.setValue(TUTORIAL_COMPLETE_FLAG_KEY, tutorialComplete);
} }
} }

View file

@ -22,7 +22,7 @@ public:
private: private:
enum Action { enum Action {
DELETE_INTERFACE_INI, DELETE_INTERFACE_INI,
RETAIN_AVATAR_INFO, RETAIN_IMPORTANT_INFO,
DO_NOTHING DO_NOTHING
}; };

View file

@ -794,6 +794,77 @@ controller::Pose MyAvatar::getRightHandTipPose() const {
return pose; return pose;
} }
glm::vec3 MyAvatar::worldToJointPoint(const glm::vec3& position, const int jointIndex) const {
glm::vec3 jointPos = getPosition();//default value if no or invalid joint specified
glm::quat jointRot = getRotation();//default value if no or invalid joint specified
if (jointIndex != -1) {
if (_skeletonModel->getJointPositionInWorldFrame(jointIndex, jointPos)) {
_skeletonModel->getJointRotationInWorldFrame(jointIndex, jointRot);
} else {
qWarning() << "Invalid joint index specified: " << jointIndex;
}
}
glm::vec3 modelOffset = position - jointPos;
glm::vec3 jointSpacePosition = glm::inverse(jointRot) * modelOffset;
return jointSpacePosition;
}
glm::vec3 MyAvatar::worldToJointDirection(const glm::vec3& worldDir, const int jointIndex) const {
glm::quat jointRot = getRotation();//default value if no or invalid joint specified
if ((jointIndex != -1) && (!_skeletonModel->getJointRotationInWorldFrame(jointIndex, jointRot))) {
qWarning() << "Invalid joint index specified: " << jointIndex;
}
glm::vec3 jointSpaceDir = glm::inverse(jointRot) * worldDir;
return jointSpaceDir;
}
glm::quat MyAvatar::worldToJointRotation(const glm::quat& worldRot, const int jointIndex) const {
glm::quat jointRot = getRotation();//default value if no or invalid joint specified
if ((jointIndex != -1) && (!_skeletonModel->getJointRotationInWorldFrame(jointIndex, jointRot))) {
qWarning() << "Invalid joint index specified: " << jointIndex;
}
glm::quat jointSpaceRot = glm::inverse(jointRot) * worldRot;
return jointSpaceRot;
}
glm::vec3 MyAvatar::jointToWorldPoint(const glm::vec3& jointSpacePos, const int jointIndex) const {
glm::vec3 jointPos = getPosition();//default value if no or invalid joint specified
glm::quat jointRot = getRotation();//default value if no or invalid joint specified
if (jointIndex != -1) {
if (_skeletonModel->getJointPositionInWorldFrame(jointIndex, jointPos)) {
_skeletonModel->getJointRotationInWorldFrame(jointIndex, jointRot);
} else {
qWarning() << "Invalid joint index specified: " << jointIndex;
}
}
glm::vec3 worldOffset = jointRot * jointSpacePos;
glm::vec3 worldPos = jointPos + worldOffset;
return worldPos;
}
glm::vec3 MyAvatar::jointToWorldDirection(const glm::vec3& jointSpaceDir, const int jointIndex) const {
glm::quat jointRot = getRotation();//default value if no or invalid joint specified
if ((jointIndex != -1) && (!_skeletonModel->getJointRotationInWorldFrame(jointIndex, jointRot))) {
qWarning() << "Invalid joint index specified: " << jointIndex;
}
glm::vec3 worldDir = jointRot * jointSpaceDir;
return worldDir;
}
glm::quat MyAvatar::jointToWorldRotation(const glm::quat& jointSpaceRot, const int jointIndex) const {
glm::quat jointRot = getRotation();//default value if no or invalid joint specified
if ((jointIndex != -1) && (!_skeletonModel->getJointRotationInWorldFrame(jointIndex, jointRot))) {
qWarning() << "Invalid joint index specified: " << jointIndex;
}
glm::quat worldRot = jointRot * jointSpaceRot;
return worldRot;
}
// virtual // virtual
void MyAvatar::render(RenderArgs* renderArgs) { void MyAvatar::render(RenderArgs* renderArgs) {
// don't render if we've been asked to disable local rendering // don't render if we've been asked to disable local rendering

View file

@ -378,6 +378,15 @@ public:
Q_INVOKABLE controller::Pose getLeftHandTipPose() const; Q_INVOKABLE controller::Pose getLeftHandTipPose() const;
Q_INVOKABLE controller::Pose getRightHandTipPose() const; Q_INVOKABLE controller::Pose getRightHandTipPose() const;
// world-space to avatar-space rigconversion functions
Q_INVOKABLE glm::vec3 worldToJointPoint(const glm::vec3& position, const int jointIndex = -1) const;
Q_INVOKABLE glm::vec3 worldToJointDirection(const glm::vec3& direction, const int jointIndex = -1) const;
Q_INVOKABLE glm::quat worldToJointRotation(const glm::quat& rotation, const int jointIndex = -1) const;
Q_INVOKABLE glm::vec3 jointToWorldPoint(const glm::vec3& position, const int jointIndex = -1) const;
Q_INVOKABLE glm::vec3 jointToWorldDirection(const glm::vec3& direction, const int jointIndex = -1) const;
Q_INVOKABLE glm::quat jointToWorldRotation(const glm::quat& rotation, const int jointIndex = -1) const;
AvatarWeakPointer getLookAtTargetAvatar() const { return _lookAtTargetAvatar; } AvatarWeakPointer getLookAtTargetAvatar() const { return _lookAtTargetAvatar; }
void updateLookAtTargetAvatar(); void updateLookAtTargetAvatar();
void clearLookAtTargetAvatar(); void clearLookAtTargetAvatar();

View file

@ -184,6 +184,8 @@ AudioClient::AudioClient() :
checkDevices(); checkDevices();
}); });
}); });
const unsigned long DEVICE_CHECK_INTERVAL_MSECS = 2 * 1000;
_checkDevicesTimer->start(DEVICE_CHECK_INTERVAL_MSECS);
configureReverb(); configureReverb();

View file

@ -1473,27 +1473,6 @@ QStringList AvatarData::getJointNames() const {
return _jointNames; return _jointNames;
} }
void AvatarData::parseAvatarIdentityPacket(const QByteArray& data, Identity& identityOut) {
QDataStream packetStream(data);
packetStream >> identityOut.uuid
>> identityOut.skeletonModelURL
>> identityOut.attachmentData
>> identityOut.displayName
>> identityOut.sessionDisplayName
>> identityOut.avatarEntityData
>> identityOut.sequenceId;
#ifdef WANT_DEBUG
qCDebug(avatars) << __FUNCTION__
<< "identityOut.uuid:" << identityOut.uuid
<< "identityOut.skeletonModelURL:" << identityOut.skeletonModelURL
<< "identityOut.displayName:" << identityOut.displayName
<< "identityOut.sessionDisplayName:" << identityOut.sessionDisplayName;
#endif
}
glm::quat AvatarData::getOrientationOutbound() const { glm::quat AvatarData::getOrientationOutbound() const {
return (getLocalOrientation()); return (getLocalOrientation());
} }
@ -1504,61 +1483,106 @@ QUrl AvatarData::cannonicalSkeletonModelURL(const QUrl& emptyURL) const {
return _skeletonModelURL.scheme() == "file" ? emptyURL : _skeletonModelURL; return _skeletonModelURL.scheme() == "file" ? emptyURL : _skeletonModelURL;
} }
void AvatarData::processAvatarIdentity(const Identity& identity, bool& identityChanged, bool& displayNameChanged) { void AvatarData::processAvatarIdentity(const QByteArray& identityData, bool& identityChanged, bool& displayNameChanged) {
if (identity.sequenceId < _identitySequenceId) { QDataStream packetStream(identityData);
qCDebug(avatars) << "Ignoring older identity packet for avatar" << getSessionUUID()
<< "_identitySequenceId (" << _identitySequenceId << ") is greater than" << identity.sequenceId; QUuid avatarSessionID;
return;
// peek the sequence number, this will tell us if we should be processing this identity packet at all
udt::SequenceNumber::Type incomingSequenceNumberType;
packetStream >> avatarSessionID >> incomingSequenceNumberType;
udt::SequenceNumber incomingSequenceNumber(incomingSequenceNumberType);
if (!_hasProcessedFirstIdentity) {
_lastIncomingSequenceNumber = incomingSequenceNumber - 1;
_hasProcessedFirstIdentity = true;
qCDebug(avatars) << "Processing first identity packet for" << avatarSessionID << "-"
<< (udt::SequenceNumber::Type) incomingSequenceNumber;
} }
// otherwise, set the identitySequenceId to match the incoming identity
_identitySequenceId = identity.sequenceId;
if (_firstSkeletonCheck || (identity.skeletonModelURL != cannonicalSkeletonModelURL(emptyURL))) { if (incomingSequenceNumber > _lastIncomingSequenceNumber) {
setSkeletonModelURL(identity.skeletonModelURL); Identity identity;
identityChanged = true;
if (_firstSkeletonCheck) { packetStream >> identity.skeletonModelURL
>> identity.attachmentData
>> identity.displayName
>> identity.sessionDisplayName
>> identity.avatarEntityData;
// set the store identity sequence number to match the incoming identity
_lastIncomingSequenceNumber = incomingSequenceNumber;
if (_firstSkeletonCheck || (identity.skeletonModelURL != cannonicalSkeletonModelURL(emptyURL))) {
setSkeletonModelURL(identity.skeletonModelURL);
identityChanged = true;
if (_firstSkeletonCheck) {
displayNameChanged = true;
}
_firstSkeletonCheck = false;
}
if (identity.displayName != _displayName) {
_displayName = identity.displayName;
identityChanged = true;
displayNameChanged = true; displayNameChanged = true;
} }
_firstSkeletonCheck = false; maybeUpdateSessionDisplayNameFromTransport(identity.sessionDisplayName);
}
if (identity.displayName != _displayName) { if (identity.attachmentData != _attachmentData) {
_displayName = identity.displayName; setAttachmentData(identity.attachmentData);
identityChanged = true; identityChanged = true;
displayNameChanged = true; }
}
maybeUpdateSessionDisplayNameFromTransport(identity.sessionDisplayName);
if (identity.attachmentData != _attachmentData) { bool avatarEntityDataChanged = false;
setAttachmentData(identity.attachmentData); _avatarEntitiesLock.withReadLock([&] {
identityChanged = true; avatarEntityDataChanged = (identity.avatarEntityData != _avatarEntityData);
} });
if (avatarEntityDataChanged) {
setAvatarEntityData(identity.avatarEntityData);
identityChanged = true;
}
bool avatarEntityDataChanged = false; #ifdef WANT_DEBUG
_avatarEntitiesLock.withReadLock([&] { qCDebug(avatars) << __FUNCTION__
avatarEntityDataChanged = (identity.avatarEntityData != _avatarEntityData); << "identity.uuid:" << identity.uuid
}); << "identity.skeletonModelURL:" << identity.skeletonModelURL
if (avatarEntityDataChanged) { << "identity.displayName:" << identity.displayName
setAvatarEntityData(identity.avatarEntityData); << "identity.sessionDisplayName:" << identity.sessionDisplayName;
identityChanged = true; #endif
}
} else {
#ifdef WANT_DEBUG
qCDebug(avatars) << "Refusing to process identity for" << uuidStringWithoutCurlyBraces(avatarSessionID) << "since"
<< (udt::SequenceNumber::Type) _lastIncomingSequenceNumber
<< "is >=" << (udt::SequenceNumber::Type) incomingSequenceNumber;
#endif
}
} }
QByteArray AvatarData::identityByteArray() const { QByteArray AvatarData::identityByteArray(bool shouldForwardIncomingSequenceNumber) const {
QByteArray identityData; QByteArray identityData;
QDataStream identityStream(&identityData, QIODevice::Append); QDataStream identityStream(&identityData, QIODevice::Append);
const QUrl& urlToSend = cannonicalSkeletonModelURL(emptyURL); // depends on _skeletonModelURL const QUrl& urlToSend = cannonicalSkeletonModelURL(emptyURL); // depends on _skeletonModelURL
// we use the boolean flag to determine if this is an identity byte array for a mixer to send to an agent
// or an agent to send to a mixer
// when mixers send identity packets to agents, they simply forward along the last incoming sequence number they received
// whereas agents send a fresh outgoing sequence number when identity data has changed
udt::SequenceNumber identitySequenceNumber =
shouldForwardIncomingSequenceNumber ? _lastIncomingSequenceNumber : _lastOutgoingSequenceNumber;
_avatarEntitiesLock.withReadLock([&] { _avatarEntitiesLock.withReadLock([&] {
identityStream << getSessionUUID() identityStream << getSessionUUID()
<< urlToSend << (udt::SequenceNumber::Type) identitySequenceNumber
<< _attachmentData << urlToSend
<< _displayName << _attachmentData
<< getSessionDisplayNameForTransport() // depends on _sessionDisplayName << _displayName
<< _avatarEntityData << getSessionDisplayNameForTransport() // depends on _sessionDisplayName
<< _identitySequenceId; << _avatarEntityData;
}); });
return identityData; return identityData;
@ -1734,6 +1758,12 @@ void AvatarData::sendAvatarDataPacket() {
void AvatarData::sendIdentityPacket() { void AvatarData::sendIdentityPacket() {
auto nodeList = DependencyManager::get<NodeList>(); auto nodeList = DependencyManager::get<NodeList>();
if (_identityDataChanged) {
// if the identity data has changed, push the sequence number forwards
++_lastOutgoingSequenceNumber;
}
QByteArray identityData = identityByteArray(); QByteArray identityData = identityByteArray();
auto packetList = NLPacketList::create(PacketType::AvatarIdentity, QByteArray(), true, true); auto packetList = NLPacketList::create(PacketType::AvatarIdentity, QByteArray(), true, true);
@ -1744,7 +1774,7 @@ void AvatarData::sendIdentityPacket() {
}, },
[&](const SharedNodePointer& node) { [&](const SharedNodePointer& node) {
nodeList->sendPacketList(std::move(packetList), *node); nodeList->sendPacketList(std::move(packetList), *node);
}); });
_avatarEntityDataLocallyEdited = false; _avatarEntityDataLocallyEdited = false;
_identityDataChanged = false; _identityDataChanged = false;

View file

@ -52,15 +52,16 @@ typedef unsigned long long quint64;
#include <JointData.h> #include <JointData.h>
#include <NLPacket.h> #include <NLPacket.h>
#include <Node.h> #include <Node.h>
#include <RegisteredMetaTypes.h>
#include <SimpleMovingAverage.h>
#include <SpatiallyNestable.h>
#include <NumericalConstants.h> #include <NumericalConstants.h>
#include <Packed.h> #include <Packed.h>
#include <ThreadSafeValueCache.h> #include <RegisteredMetaTypes.h>
#include <SharedUtil.h> #include <SharedUtil.h>
#include <shared/RateCounter.h> #include <SimpleMovingAverage.h>
#include <SpatiallyNestable.h>
#include <ThreadSafeValueCache.h>
#include <ViewFrustum.h> #include <ViewFrustum.h>
#include <shared/RateCounter.h>
#include <udt/SequenceNumber.h>
#include "AABox.h" #include "AABox.h"
#include "HeadData.h" #include "HeadData.h"
@ -525,22 +526,18 @@ public:
const HeadData* getHeadData() const { return _headData; } const HeadData* getHeadData() const { return _headData; }
struct Identity { struct Identity {
QUuid uuid;
QUrl skeletonModelURL; QUrl skeletonModelURL;
QVector<AttachmentData> attachmentData; QVector<AttachmentData> attachmentData;
QString displayName; QString displayName;
QString sessionDisplayName; QString sessionDisplayName;
AvatarEntityMap avatarEntityData; AvatarEntityMap avatarEntityData;
quint64 sequenceId;
}; };
static void parseAvatarIdentityPacket(const QByteArray& data, Identity& identityOut);
// identityChanged returns true if identity has changed, false otherwise. // identityChanged returns true if identity has changed, false otherwise.
// displayNameChanged returns true if displayName has changed, false otherwise. // displayNameChanged returns true if displayName has changed, false otherwise.
void processAvatarIdentity(const Identity& identity, bool& identityChanged, bool& displayNameChanged); void processAvatarIdentity(const QByteArray& identityData, bool& identityChanged, bool& displayNameChanged);
QByteArray identityByteArray() const; QByteArray identityByteArray(bool shouldForwardIncomingSequenceNumber = false) const;
const QUrl& getSkeletonModelURL() const { return _skeletonModelURL; } const QUrl& getSkeletonModelURL() const { return _skeletonModelURL; }
const QString& getDisplayName() const { return _displayName; } const QString& getDisplayName() const { return _displayName; }
@ -624,10 +621,7 @@ public:
static float _avatarSortCoefficientAge; static float _avatarSortCoefficientAge;
bool getIdentityDataChanged() const { return _identityDataChanged; } // has the identity data changed since the last time sendIdentityPacket() was called bool getIdentityDataChanged() const { return _identityDataChanged; } // has the identity data changed since the last time sendIdentityPacket() was called
void markIdentityDataChanged() { void markIdentityDataChanged() { _identityDataChanged = true; }
_identityDataChanged = true;
_identitySequenceId++;
}
float getDensity() const { return _density; } float getDensity() const { return _density; }
@ -786,7 +780,9 @@ protected:
float _audioAverageLoudness { 0.0f }; float _audioAverageLoudness { 0.0f };
bool _identityDataChanged { false }; bool _identityDataChanged { false };
quint64 _identitySequenceId { 0 }; udt::SequenceNumber _lastIncomingSequenceNumber { 0 };
udt::SequenceNumber _lastOutgoingSequenceNumber { 0 };
bool _hasProcessedFirstIdentity { false };
float _density; float _density;
private: private:

View file

@ -126,8 +126,9 @@ AvatarSharedPointer AvatarHashMap::parseAvatarData(QSharedPointer<ReceivedMessag
} }
void AvatarHashMap::processAvatarIdentityPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer sendingNode) { void AvatarHashMap::processAvatarIdentityPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer sendingNode) {
AvatarData::Identity identity;
AvatarData::parseAvatarIdentityPacket(message->getMessage(), identity); // peek the avatar UUID from the incoming packet
QUuid identityUUID = message->peek(NUM_BYTES_RFC4122_UUID);
// make sure this isn't for an ignored avatar // make sure this isn't for an ignored avatar
auto nodeList = DependencyManager::get<NodeList>(); auto nodeList = DependencyManager::get<NodeList>();
@ -136,20 +137,21 @@ void AvatarHashMap::processAvatarIdentityPacket(QSharedPointer<ReceivedMessage>
{ {
QReadLocker locker(&_hashLock); QReadLocker locker(&_hashLock);
auto me = _avatarHash.find(EMPTY); auto me = _avatarHash.find(EMPTY);
if ((me != _avatarHash.end()) && (identity.uuid == me.value()->getSessionUUID())) { if ((me != _avatarHash.end()) && (identityUUID == me.value()->getSessionUUID())) {
// We add MyAvatar to _avatarHash with an empty UUID. Code relies on this. In order to correctly handle an // We add MyAvatar to _avatarHash with an empty UUID. Code relies on this. In order to correctly handle an
// identity packet for ourself (such as when we are assigned a sessionDisplayName by the mixer upon joining), // identity packet for ourself (such as when we are assigned a sessionDisplayName by the mixer upon joining),
// we make things match here. // we make things match here.
identity.uuid = EMPTY; identityUUID = EMPTY;
} }
} }
if (!nodeList->isIgnoringNode(identity.uuid) || nodeList->getRequestsDomainListData()) {
if (!nodeList->isIgnoringNode(identityUUID) || nodeList->getRequestsDomainListData()) {
// mesh URL for a UUID, find avatar in our list // mesh URL for a UUID, find avatar in our list
auto avatar = newOrExistingAvatar(identity.uuid, sendingNode); auto avatar = newOrExistingAvatar(identityUUID, sendingNode);
bool identityChanged = false; bool identityChanged = false;
bool displayNameChanged = false; bool displayNameChanged = false;
// In this case, the "sendingNode" is the Avatar Mixer. // In this case, the "sendingNode" is the Avatar Mixer.
avatar->processAvatarIdentity(identity, identityChanged, displayNameChanged); avatar->processAvatarIdentity(message->getMessage(), identityChanged, displayNameChanged);
} }
} }

View file

@ -605,7 +605,7 @@ bool RenderableModelEntityItem::findDetailedRayIntersection(const glm::vec3& ori
QString extraInfo; QString extraInfo;
return _model->findRayIntersectionAgainstSubMeshes(origin, direction, distance, return _model->findRayIntersectionAgainstSubMeshes(origin, direction, distance,
face, surfaceNormal, extraInfo, precisionPicking, precisionPicking); face, surfaceNormal, extraInfo, precisionPicking, false);
} }
void RenderableModelEntityItem::getCollisionGeometryResource() { void RenderableModelEntityItem::getCollisionGeometryResource() {

View file

@ -460,17 +460,11 @@ FBXLight extractLight(const FBXNode& object) {
} }
QByteArray fileOnUrl(const QByteArray& filepath, const QString& url) { QByteArray fileOnUrl(const QByteArray& filepath, const QString& url) {
QString path = QFileInfo(url).path(); // in order to match the behaviour when loading models from remote URLs
QByteArray filename = filepath; // we assume that all external textures are right beside the loaded model
QFileInfo checkFile(path + "/" + filepath); // ignoring any relative paths or absolute paths inside of models
// check if the file exists at the RelativeFilename return filepath.mid(filepath.lastIndexOf('/') + 1);
if (!(checkFile.exists() && checkFile.isFile())) {
// if not, assume it is in the fbx directory
filename = filename.mid(filename.lastIndexOf('/') + 1);
}
return filename;
} }
FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QString& url) { FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QString& url) {

View file

@ -277,6 +277,23 @@ QString getEventBridgeJavascript() {
return javaScriptToInject; return javaScriptToInject;
} }
class EventBridgeWrapper : public QObject {
Q_OBJECT
Q_PROPERTY(QObject* eventBridge READ getEventBridge CONSTANT);
public:
EventBridgeWrapper(QObject* eventBridge, QObject* parent = nullptr) : QObject(parent), _eventBridge(eventBridge) {
}
QObject* getEventBridge() {
return _eventBridge;
}
private:
QObject* _eventBridge;
};
QQmlEngine* acquireEngine(QQuickWindow* window) { QQmlEngine* acquireEngine(QQuickWindow* window) {
Q_ASSERT(QThread::currentThread() == qApp->thread()); Q_ASSERT(QThread::currentThread() == qApp->thread());
@ -430,7 +447,6 @@ OffscreenQmlSurface::~OffscreenQmlSurface() {
_canvas->deleteLater(); _canvas->deleteLater();
_rootItem->deleteLater(); _rootItem->deleteLater();
_qmlComponent->deleteLater();
_quickWindow->deleteLater(); _quickWindow->deleteLater();
releaseEngine(); releaseEngine();
} }
@ -473,11 +489,12 @@ void OffscreenQmlSurface::create(QOpenGLContext* shareContext) {
_qmlContext = new QQmlContext(qmlEngine->rootContext()); _qmlContext = new QQmlContext(qmlEngine->rootContext());
_qmlContext->setContextProperty("offscreenWindow", QVariant::fromValue(getWindow())); _qmlContext->setContextProperty("offscreenWindow", QVariant::fromValue(getWindow()));
_qmlContext->setContextProperty("globalEventBridge", this); _qmlContext->setContextProperty("eventBridge", this);
_qmlContext->setContextProperty("webEntity", this); _qmlContext->setContextProperty("webEntity", this);
_qmlComponent = new QQmlComponent(qmlEngine); // FIXME Compatibility mechanism for existing HTML and JS that uses eventBridgeWrapper
// Find a way to flag older scripts using this mechanism and wanr that this is deprecated
_qmlContext->setContextProperty("eventBridgeWrapper", new EventBridgeWrapper(this, _qmlContext));
if (!_canvas->makeCurrent()) { if (!_canvas->makeCurrent()) {
qWarning("Failed to make context current for QML Renderer"); qWarning("Failed to make context current for QML Renderer");
@ -577,71 +594,79 @@ void OffscreenQmlSurface::setBaseUrl(const QUrl& baseUrl) {
_qmlContext->setBaseUrl(baseUrl); _qmlContext->setBaseUrl(baseUrl);
} }
QObject* OffscreenQmlSurface::load(const QUrl& qmlSource, std::function<void(QQmlContext*, QObject*)> f) { QObject* OffscreenQmlSurface::load(const QUrl& qmlSource, bool createNewContext, std::function<void(QQmlContext*, QObject*)> f) {
// Synchronous loading may take a while; restart the deadlock timer // Synchronous loading may take a while; restart the deadlock timer
QMetaObject::invokeMethod(qApp, "updateHeartbeat", Qt::DirectConnection); QMetaObject::invokeMethod(qApp, "updateHeartbeat", Qt::DirectConnection);
if ((qmlSource.isRelative() && !qmlSource.isEmpty()) || qmlSource.scheme() == QLatin1String("file")) { QQmlContext* targetContext = _qmlContext;
_qmlComponent->loadUrl(_qmlContext->resolvedUrl(qmlSource), QQmlComponent::PreferSynchronous); if (_rootItem && createNewContext) {
} else { targetContext = new QQmlContext(targetContext);
_qmlComponent->loadUrl(qmlSource, QQmlComponent::PreferSynchronous);
} }
QUrl finalQmlSource = qmlSource;
if ((qmlSource.isRelative() && !qmlSource.isEmpty()) || qmlSource.scheme() == QLatin1String("file")) {
finalQmlSource = _qmlContext->resolvedUrl(qmlSource);
}
if (_qmlComponent->isLoading()) { auto qmlComponent = new QQmlComponent(_qmlContext->engine(), finalQmlSource, QQmlComponent::PreferSynchronous);
connect(_qmlComponent, &QQmlComponent::statusChanged, this, if (qmlComponent->isLoading()) {
[this, f](QQmlComponent::Status){ connect(qmlComponent, &QQmlComponent::statusChanged, this,
finishQmlLoad(f); [this, qmlComponent, targetContext, f](QQmlComponent::Status) {
}); finishQmlLoad(qmlComponent, targetContext, f);
});
return nullptr; return nullptr;
} }
return finishQmlLoad(f); return finishQmlLoad(qmlComponent, targetContext, f);
}
QObject* OffscreenQmlSurface::loadInNewContext(const QUrl& qmlSource, std::function<void(QQmlContext*, QObject*)> f) {
return load(qmlSource, true, f);
}
QObject* OffscreenQmlSurface::load(const QUrl& qmlSource, std::function<void(QQmlContext*, QObject*)> f) {
return load(qmlSource, false, f);
} }
void OffscreenQmlSurface::clearCache() { void OffscreenQmlSurface::clearCache() {
_qmlContext->engine()->clearComponentCache(); _qmlContext->engine()->clearComponentCache();
} }
QObject* OffscreenQmlSurface::finishQmlLoad(std::function<void(QQmlContext*, QObject*)> f) { QObject* OffscreenQmlSurface::finishQmlLoad(QQmlComponent* qmlComponent, QQmlContext* qmlContext, std::function<void(QQmlContext*, QObject*)> f) {
#if 0 disconnect(qmlComponent, &QQmlComponent::statusChanged, this, 0);
if (!_rootItem) { if (qmlComponent->isError()) {
QQmlComponent component(_qmlContext->engine()); for (const auto& error : qmlComponent->errors()) {
component.setData(R"QML( qCWarning(glLogging) << error.url() << error.line() << error;
import QtQuick 2.0
import QtWebChannel 1.0
Item { Component.onCompleted: globalEventBridge.WebChannel.id = "globalEventBridge"; }
)QML", QUrl());
QObject *helper = component.create(_qmlContext);
qDebug() << "Created helper";
}
#endif
disconnect(_qmlComponent, &QQmlComponent::statusChanged, this, 0);
if (_qmlComponent->isError()) {
QList<QQmlError> errorList = _qmlComponent->errors();
foreach(const QQmlError& error, errorList) {
qWarning() << error.url() << error.line() << error;
} }
qmlComponent->deleteLater();
return nullptr; return nullptr;
} }
QObject* newObject = _qmlComponent->beginCreate(_qmlContext); QObject* newObject = qmlComponent->beginCreate(qmlContext);
if (_qmlComponent->isError()) { if (qmlComponent->isError()) {
QList<QQmlError> errorList = _qmlComponent->errors(); for (const auto& error : qmlComponent->errors()) {
foreach(const QQmlError& error, errorList)
qCWarning(glLogging) << error.url() << error.line() << error; qCWarning(glLogging) << error.url() << error.line() << error;
}
if (!_rootItem) { if (!_rootItem) {
qFatal("Unable to finish loading QML root"); qFatal("Unable to finish loading QML root");
} }
qmlComponent->deleteLater();
return nullptr; return nullptr;
} }
_qmlContext->engine()->setObjectOwnership(this, QQmlEngine::CppOwnership); qmlContext->engine()->setObjectOwnership(this, QQmlEngine::CppOwnership);
newObject->setProperty("eventBridge", QVariant::fromValue(this)); f(qmlContext, newObject);
f(_qmlContext, newObject); QObject* eventBridge = qmlContext->contextProperty("eventBridge").value<QObject*>();
_qmlComponent->completeCreate(); if (qmlContext != _qmlContext && eventBridge && eventBridge != this) {
// FIXME Compatibility mechanism for existing HTML and JS that uses eventBridgeWrapper
// Find a way to flag older scripts using this mechanism and wanr that this is deprecated
qmlContext->setContextProperty("eventBridgeWrapper", new EventBridgeWrapper(eventBridge, qmlContext));
}
qmlComponent->completeCreate();
qmlComponent->deleteLater();
// All quick items should be focusable // All quick items should be focusable

View file

@ -48,6 +48,8 @@ public:
void resize(const QSize& size, bool forceResize = false); void resize(const QSize& size, bool forceResize = false);
QSize size() const; QSize size() const;
Q_INVOKABLE QObject* load(const QUrl& qmlSource, bool createNewContext, std::function<void(QQmlContext*, QObject*)> f = [](QQmlContext*, QObject*) {});
Q_INVOKABLE QObject* loadInNewContext(const QUrl& qmlSource, std::function<void(QQmlContext*, QObject*)> f = [](QQmlContext*, QObject*) {});
Q_INVOKABLE QObject* load(const QUrl& qmlSource, std::function<void(QQmlContext*, QObject*)> f = [](QQmlContext*, QObject*) {}); Q_INVOKABLE QObject* load(const QUrl& qmlSource, std::function<void(QQmlContext*, QObject*)> f = [](QQmlContext*, QObject*) {});
Q_INVOKABLE QObject* load(const QString& qmlSourceFile, std::function<void(QQmlContext*, QObject*)> f = [](QQmlContext*, QObject*) {}) { Q_INVOKABLE QObject* load(const QString& qmlSourceFile, std::function<void(QQmlContext*, QObject*)> f = [](QQmlContext*, QObject*) {}) {
return load(QUrl(qmlSourceFile), f); return load(QUrl(qmlSourceFile), f);
@ -118,7 +120,7 @@ protected:
void setFocusText(bool newFocusText); void setFocusText(bool newFocusText);
private: private:
QObject* finishQmlLoad(std::function<void(QQmlContext*, QObject*)> f); QObject* finishQmlLoad(QQmlComponent* qmlComponent, QQmlContext* qmlContext, std::function<void(QQmlContext*, QObject*)> f);
QPointF mapWindowToUi(const QPointF& sourcePosition, QObject* sourceObject); QPointF mapWindowToUi(const QPointF& sourcePosition, QObject* sourceObject);
void setupFbo(); void setupFbo();
bool allowNewFrame(uint8_t fps); bool allowNewFrame(uint8_t fps);
@ -134,7 +136,6 @@ private:
QQuickWindow* _quickWindow { nullptr }; QQuickWindow* _quickWindow { nullptr };
QMyQuickRenderControl* _renderControl{ nullptr }; QMyQuickRenderControl* _renderControl{ nullptr };
QQmlContext* _qmlContext { nullptr }; QQmlContext* _qmlContext { nullptr };
QQmlComponent* _qmlComponent { nullptr };
QQuickItem* _rootItem { nullptr }; QQuickItem* _rootItem { nullptr };
OffscreenGLCanvas* _canvas { nullptr }; OffscreenGLCanvas* _canvas { nullptr };

View file

@ -63,11 +63,17 @@ void GLBackend::do_clearFramebuffer(const Batch& batch, size_t paramOffset) {
int useScissor = batch._params[paramOffset + 0]._int; int useScissor = batch._params[paramOffset + 0]._int;
GLuint glmask = 0; GLuint glmask = 0;
bool restoreStencilMask = false;
uint8_t cacheStencilMask = 0xFF;
if (masks & Framebuffer::BUFFER_STENCIL) { if (masks & Framebuffer::BUFFER_STENCIL) {
glClearStencil(stencil); glClearStencil(stencil);
glmask |= GL_STENCIL_BUFFER_BIT; glmask |= GL_STENCIL_BUFFER_BIT;
// TODO: we will probably need to also check the write mask of stencil like we do
// for depth buffer, but as would say a famous Fez owner "We'll cross that bridge when we come to it" cacheStencilMask = _pipeline._stateCache.stencilActivation.getWriteMaskFront();
if (cacheStencilMask != 0xFF) {
restoreStencilMask = true;
glStencilMask( 0xFF);
}
} }
bool restoreDepthMask = false; bool restoreDepthMask = false;
@ -121,6 +127,11 @@ void GLBackend::do_clearFramebuffer(const Batch& batch, size_t paramOffset) {
glDisable(GL_SCISSOR_TEST); glDisable(GL_SCISSOR_TEST);
} }
// Restore Stencil write mask
if (restoreStencilMask) {
glStencilMask(cacheStencilMask);
}
// Restore write mask meaning turn back off // Restore write mask meaning turn back off
if (restoreDepthMask) { if (restoreDepthMask) {
glDepthMask(GL_FALSE); glDepthMask(GL_FALSE);

View file

@ -20,7 +20,7 @@
class ReceivedPacketProcessor : public GenericThread { class ReceivedPacketProcessor : public GenericThread {
Q_OBJECT Q_OBJECT
public: public:
static const unsigned long MAX_WAIT_TIME { 100 }; static const uint64_t MAX_WAIT_TIME { 100 }; // Max wait time in ms
ReceivedPacketProcessor(); ReceivedPacketProcessor();
@ -66,7 +66,7 @@ protected:
virtual bool process() override; virtual bool process() override;
/// Determines the timeout of the wait when there are no packets to process. Default value is 100ms to allow for regular event processing. /// Determines the timeout of the wait when there are no packets to process. Default value is 100ms to allow for regular event processing.
virtual unsigned long getMaxWait() const { return MAX_WAIT_TIME; } virtual uint32_t getMaxWait() const { return MAX_WAIT_TIME; }
/// Override to do work before the packets processing loop. Default does nothing. /// Override to do work before the packets processing loop. Default does nothing.
virtual void preProcess() { } virtual void preProcess() { }

View file

@ -59,7 +59,7 @@ PacketVersion versionForPacketType(PacketType packetType) {
case PacketType::AvatarData: case PacketType::AvatarData:
case PacketType::BulkAvatarData: case PacketType::BulkAvatarData:
case PacketType::KillAvatar: case PacketType::KillAvatar:
return static_cast<PacketVersion>(AvatarMixerPacketVersion::AvatarIdentitySequenceId); return static_cast<PacketVersion>(AvatarMixerPacketVersion::AvatarIdentitySequenceFront);
case PacketType::MessagesData: case PacketType::MessagesData:
return static_cast<PacketVersion>(MessageDataVersion::TextOrBinaryData); return static_cast<PacketVersion>(MessageDataVersion::TextOrBinaryData);
case PacketType::ICEServerHeartbeat: case PacketType::ICEServerHeartbeat:

View file

@ -243,7 +243,9 @@ enum class AvatarMixerPacketVersion : PacketVersion {
AvatarAsChildFixes, AvatarAsChildFixes,
StickAndBallDefaultAvatar, StickAndBallDefaultAvatar,
IdentityPacketsIncludeUpdateTime, IdentityPacketsIncludeUpdateTime,
AvatarIdentitySequenceId AvatarIdentitySequenceId,
MannequinDefaultAvatar,
AvatarIdentitySequenceFront
}; };
enum class DomainConnectRequestVersion : PacketVersion { enum class DomainConnectRequestVersion : PacketVersion {

View file

@ -35,8 +35,8 @@ public:
explicit SequenceNumber(char* value) { _value = (*reinterpret_cast<int32_t*>(value)) & MAX; } explicit SequenceNumber(char* value) { _value = (*reinterpret_cast<int32_t*>(value)) & MAX; }
explicit SequenceNumber(Type value) { _value = (value <= MAX) ? ((value >= 0) ? value : 0) : MAX; } explicit SequenceNumber(Type value) { _value = (value <= MAX) ? ((value >= 0) ? value : 0) : MAX; }
explicit SequenceNumber(UType value) { _value = (value <= MAX) ? value : MAX; } explicit SequenceNumber(UType value) { _value = (value <= MAX) ? value : MAX; }
explicit operator Type() { return _value; } explicit operator Type() const { return _value; }
explicit operator UType() { return static_cast<UType>(_value); } explicit operator UType() const { return static_cast<UType>(_value); }
inline SequenceNumber& operator++() { inline SequenceNumber& operator++() {
_value = (_value + 1) % (MAX + 1); _value = (_value + 1) % (MAX + 1);

View file

@ -35,14 +35,13 @@ void JurisdictionListener::nodeKilled(SharedNodePointer node) {
} }
bool JurisdictionListener::queueJurisdictionRequest() { bool JurisdictionListener::queueJurisdictionRequest() {
auto packet = NLPacket::create(PacketType::JurisdictionRequest, 0);
auto nodeList = DependencyManager::get<NodeList>(); auto nodeList = DependencyManager::get<NodeList>();
int nodeCount = 0; int nodeCount = 0;
nodeList->eachNode([&](const SharedNodePointer& node) { nodeList->eachNode([&](const SharedNodePointer& node) {
if (node->getType() == getNodeType() && node->getActiveSocket()) { if (node->getType() == getNodeType() && node->getActiveSocket()) {
auto packet = NLPacket::create(PacketType::JurisdictionRequest, 0);
_packetSender.queuePacketForSending(node, std::move(packet)); _packetSender.queuePacketForSending(node, std::move(packet));
nodeCount++; nodeCount++;
} }

View file

@ -41,8 +41,6 @@ bool JurisdictionSender::process() {
// call our ReceivedPacketProcessor base class process so we'll get any pending packets // call our ReceivedPacketProcessor base class process so we'll get any pending packets
if (continueProcessing && (continueProcessing = ReceivedPacketProcessor::process())) { if (continueProcessing && (continueProcessing = ReceivedPacketProcessor::process())) {
auto packet = (_jurisdictionMap) ? _jurisdictionMap->packIntoPacket()
: JurisdictionMap::packEmptyJurisdictionIntoMessage(getNodeType());
int nodeCount = 0; int nodeCount = 0;
lockRequestingNodes(); lockRequestingNodes();
@ -53,6 +51,8 @@ bool JurisdictionSender::process() {
SharedNodePointer node = DependencyManager::get<NodeList>()->nodeWithUUID(nodeUUID); SharedNodePointer node = DependencyManager::get<NodeList>()->nodeWithUUID(nodeUUID);
if (node && node->getActiveSocket()) { if (node && node->getActiveSocket()) {
auto packet = (_jurisdictionMap) ? _jurisdictionMap->packIntoPacket()
: JurisdictionMap::packEmptyJurisdictionIntoMessage(getNodeType());
_packetSender.queuePacketForSending(node, std::move(packet)); _packetSender.queuePacketForSending(node, std::move(packet));
nodeCount++; nodeCount++;
} }

View file

@ -19,7 +19,6 @@
#include "AntialiasingEffect.h" #include "AntialiasingEffect.h"
#include "StencilMaskPass.h" #include "StencilMaskPass.h"
#include "TextureCache.h" #include "TextureCache.h"
#include "FramebufferCache.h"
#include "DependencyManager.h" #include "DependencyManager.h"
#include "ViewFrustum.h" #include "ViewFrustum.h"
#include "GeometryCache.h" #include "GeometryCache.h"
@ -40,9 +39,9 @@ Antialiasing::~Antialiasing() {
} }
} }
const gpu::PipelinePointer& Antialiasing::getAntialiasingPipeline() { const gpu::PipelinePointer& Antialiasing::getAntialiasingPipeline(RenderArgs* args) {
int width = DependencyManager::get<FramebufferCache>()->getFrameBufferSize().width(); int width = args->_viewport.z;
int height = DependencyManager::get<FramebufferCache>()->getFrameBufferSize().height(); int height = args->_viewport.w;
if (_antialiasingBuffer && _antialiasingBuffer->getSize() != uvec2(width, height)) { if (_antialiasingBuffer && _antialiasingBuffer->getSize() != uvec2(width, height)) {
_antialiasingBuffer.reset(); _antialiasingBuffer.reset();
@ -51,7 +50,7 @@ const gpu::PipelinePointer& Antialiasing::getAntialiasingPipeline() {
if (!_antialiasingBuffer) { if (!_antialiasingBuffer) {
// Link the antialiasing FBO to texture // Link the antialiasing FBO to texture
_antialiasingBuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("antialiasing")); _antialiasingBuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("antialiasing"));
auto format = gpu::Element::COLOR_SRGBA_32; // DependencyManager::get<FramebufferCache>()->getLightingTexture()->getTexelFormat(); auto format = gpu::Element::COLOR_SRGBA_32;
auto defaultSampler = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_POINT); auto defaultSampler = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_POINT);
_antialiasingTexture = gpu::Texture::createRenderBuffer(format, width, height, gpu::Texture::SINGLE_MIP, defaultSampler); _antialiasingTexture = gpu::Texture::createRenderBuffer(format, width, height, gpu::Texture::SINGLE_MIP, defaultSampler);
_antialiasingBuffer->setRenderBuffer(0, _antialiasingTexture); _antialiasingBuffer->setRenderBuffer(0, _antialiasingTexture);
@ -110,19 +109,13 @@ void Antialiasing::run(const render::RenderContextPointer& renderContext, const
RenderArgs* args = renderContext->args; RenderArgs* args = renderContext->args;
if (args->_renderMode == RenderArgs::MIRROR_RENDER_MODE) {
return;
}
gpu::doInBatch(args->_context, [&](gpu::Batch& batch) { gpu::doInBatch(args->_context, [&](gpu::Batch& batch) {
batch.enableStereo(false); batch.enableStereo(false);
batch.setViewportTransform(args->_viewport); batch.setViewportTransform(args->_viewport);
// FIXME: NEED to simplify that code to avoid all the GeometryCahce call, this is purely pixel manipulation // FIXME: NEED to simplify that code to avoid all the GeometryCahce call, this is purely pixel manipulation
auto framebufferCache = DependencyManager::get<FramebufferCache>(); float fbWidth = renderContext->args->_viewport.z;
QSize framebufferSize = framebufferCache->getFrameBufferSize(); float fbHeight = renderContext->args->_viewport.w;
float fbWidth = framebufferSize.width();
float fbHeight = framebufferSize.height();
// float sMin = args->_viewport.x / fbWidth; // float sMin = args->_viewport.x / fbWidth;
// float sWidth = args->_viewport.z / fbWidth; // float sWidth = args->_viewport.z / fbWidth;
// float tMin = args->_viewport.y / fbHeight; // float tMin = args->_viewport.y / fbHeight;
@ -137,10 +130,10 @@ void Antialiasing::run(const render::RenderContextPointer& renderContext, const
batch.setModelTransform(Transform()); batch.setModelTransform(Transform());
// FXAA step // FXAA step
getAntialiasingPipeline(); auto pipeline = getAntialiasingPipeline(renderContext->args);
batch.setResourceTexture(0, sourceBuffer->getRenderBuffer(0)); batch.setResourceTexture(0, sourceBuffer->getRenderBuffer(0));
batch.setFramebuffer(_antialiasingBuffer); batch.setFramebuffer(_antialiasingBuffer);
batch.setPipeline(getAntialiasingPipeline()); batch.setPipeline(pipeline);
// initialize the view-space unpacking uniforms using frustum data // initialize the view-space unpacking uniforms using frustum data
float left, right, bottom, top, nearVal, farVal; float left, right, bottom, top, nearVal, farVal;

View file

@ -33,7 +33,7 @@ public:
void configure(const Config& config) {} void configure(const Config& config) {}
void run(const render::RenderContextPointer& renderContext, const gpu::FramebufferPointer& sourceBuffer); void run(const render::RenderContextPointer& renderContext, const gpu::FramebufferPointer& sourceBuffer);
const gpu::PipelinePointer& getAntialiasingPipeline(); const gpu::PipelinePointer& getAntialiasingPipeline(RenderArgs* args);
const gpu::PipelinePointer& getBlendPipeline(); const gpu::PipelinePointer& getBlendPipeline();
private: private:

View file

@ -19,7 +19,6 @@
#include <ViewFrustum.h> #include <ViewFrustum.h>
#include "GeometryCache.h" #include "GeometryCache.h"
#include "FramebufferCache.h"
#include "TextureCache.h" #include "TextureCache.h"
#include "DeferredLightingEffect.h" #include "DeferredLightingEffect.h"
@ -410,7 +409,6 @@ void DebugDeferredBuffer::run(const RenderContextPointer& renderContext, const I
batch.setViewportTransform(args->_viewport); batch.setViewportTransform(args->_viewport);
const auto geometryBuffer = DependencyManager::get<GeometryCache>(); const auto geometryBuffer = DependencyManager::get<GeometryCache>();
const auto framebufferCache = DependencyManager::get<FramebufferCache>();
const auto textureCache = DependencyManager::get<TextureCache>(); const auto textureCache = DependencyManager::get<TextureCache>();
glm::mat4 projMat; glm::mat4 projMat;

View file

@ -418,10 +418,7 @@ model::MeshPointer DeferredLightingEffect::getSpotLightMesh() {
} }
void PreparePrimaryFramebuffer::run(const RenderContextPointer& renderContext, gpu::FramebufferPointer& primaryFramebuffer) { void PreparePrimaryFramebuffer::run(const RenderContextPointer& renderContext, gpu::FramebufferPointer& primaryFramebuffer) {
glm::uvec2 frameSize(renderContext->args->_viewport.z, renderContext->args->_viewport.w);
auto framebufferCache = DependencyManager::get<FramebufferCache>();
auto framebufferSize = framebufferCache->getFrameBufferSize();
glm::uvec2 frameSize(framebufferSize.width(), framebufferSize.height());
// Resizing framebuffers instead of re-building them seems to cause issues with threaded // Resizing framebuffers instead of re-building them seems to cause issues with threaded
// rendering // rendering
@ -504,10 +501,7 @@ void RenderDeferredSetup::run(const render::RenderContextPointer& renderContext,
{ {
// Framebuffer copy operations cannot function as multipass stereo operations. // Framebuffer copy operations cannot function as multipass stereo operations.
batch.enableStereo(false); batch.enableStereo(false);
// perform deferred lighting, rendering to free fbo
auto framebufferCache = DependencyManager::get<FramebufferCache>();
auto textureCache = DependencyManager::get<TextureCache>(); auto textureCache = DependencyManager::get<TextureCache>();
auto deferredLightingEffect = DependencyManager::get<DeferredLightingEffect>(); auto deferredLightingEffect = DependencyManager::get<DeferredLightingEffect>();

View file

@ -152,9 +152,9 @@ public:
int numInputLights { 0 }; int numInputLights { 0 };
int numClusteredLights { 0 }; int numClusteredLights { 0 };
void setNumClusteredLightReferences(int numRefs) { numClusteredLightReferences = numRefs; emit dirty(); } void setNumClusteredLightReferences(int numRefs) { numClusteredLightReferences = numRefs; }
void setNumInputLights(int numLights) { numInputLights = numLights; emit dirty(); } void setNumInputLights(int numLights) { numInputLights = numLights; }
void setNumClusteredLights(int numLights) { numClusteredLights = numLights; emit dirty(); } void setNumClusteredLights(int numLights) { numClusteredLights = numLights; }
int numSceneLights { 0 }; int numSceneLights { 0 };
int numFreeSceneLights { 0 }; int numFreeSceneLights { 0 };

View file

@ -48,6 +48,7 @@ void RenderShadowMap::run(const render::RenderContextPointer& renderContext,
RenderArgs* args = renderContext->args; RenderArgs* args = renderContext->args;
gpu::doInBatch(args->_context, [&](gpu::Batch& batch) { gpu::doInBatch(args->_context, [&](gpu::Batch& batch) {
args->_batch = &batch; args->_batch = &batch;
batch.enableStereo(false);
glm::ivec4 viewport{0, 0, fbo->getWidth(), fbo->getHeight()}; glm::ivec4 viewport{0, 0, fbo->getWidth(), fbo->getHeight()};
batch.setViewportTransform(viewport); batch.setViewportTransform(viewport);
@ -114,7 +115,7 @@ void RenderShadowTask::build(JobModel& task, const render::Varying& input, rende
skinProgram, state); skinProgram, state);
} }
const auto cachedMode = task.addJob<RenderShadowSetup>("Setup"); const auto cachedMode = task.addJob<RenderShadowSetup>("ShadowSetup");
// CPU jobs: // CPU jobs:
// Fetch and cull the items from the scene // Fetch and cull the items from the scene
@ -129,7 +130,7 @@ void RenderShadowTask::build(JobModel& task, const render::Varying& input, rende
// GPU jobs: Render to shadow map // GPU jobs: Render to shadow map
task.addJob<RenderShadowMap>("RenderShadowMap", sortedShapes, shapePlumber); task.addJob<RenderShadowMap>("RenderShadowMap", sortedShapes, shapePlumber);
task.addJob<RenderShadowTeardown>("Teardown", cachedMode); task.addJob<RenderShadowTeardown>("ShadowTeardown", cachedMode);
} }
void RenderShadowTask::configure(const Config& configuration) { void RenderShadowTask::configure(const Config& configuration) {

View file

@ -0,0 +1,33 @@
//
// RenderViewTask.cpp
// render-utils/src/
//
// Created by Sam Gateau on 5/25/2017.
// Copyright 2017 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "RenderViewTask.h"
#include "RenderShadowTask.h"
#include "RenderDeferredTask.h"
#include "RenderForwardTask.h"
void RenderViewTask::build(JobModel& task, const render::Varying& input, render::Varying& output, render::CullFunctor cullFunctor, bool isDeferred) {
// auto items = input.get<Input>();
task.addJob<RenderShadowTask>("RenderShadowTask", cullFunctor);
const auto items = task.addJob<RenderFetchCullSortTask>("FetchCullSort", cullFunctor);
assert(items.canCast<RenderFetchCullSortTask::Output>());
if (isDeferred) {
task.addJob<RenderDeferredTask>("RenderDeferredTask", items);
} else {
task.addJob<RenderForwardTask>("Forward", items);
}
}

View file

@ -0,0 +1,31 @@
//
// RenderViewTask.h
// render-utils/src/
//
// Created by Sam Gateau on 5/25/2017.
// Copyright 2017 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#pragma once
#ifndef hifi_RenderViewTask_h
#define hifi_RenderViewTask_h
#include <render/Engine.h>
#include <render/RenderFetchCullSortTask.h>
class RenderViewTask {
public:
using Input = RenderFetchCullSortTask::Output;
using JobModel = render::Task::ModelI<RenderViewTask, Input>;
RenderViewTask() {}
void build(JobModel& task, const render::Varying& inputs, render::Varying& outputs, render::CullFunctor cullFunctor, bool isDeferred);
};
#endif // hifi_RenderViewTask_h

View file

@ -14,6 +14,8 @@
#include "../RenderUtilsLogging.h" #include "../RenderUtilsLogging.h"
#include "FontFamilies.h" #include "FontFamilies.h"
static std::mutex fontMutex;
struct TextureVertex { struct TextureVertex {
glm::vec2 pos; glm::vec2 pos;
glm::vec2 tex; glm::vec2 tex;
@ -56,6 +58,7 @@ Font::Pointer Font::load(QIODevice& fontFile) {
} }
Font::Pointer Font::load(const QString& family) { Font::Pointer Font::load(const QString& family) {
std::lock_guard<std::mutex> lock(fontMutex);
if (!LOADED_FONTS.contains(family)) { if (!LOADED_FONTS.contains(family)) {
static const QString SDFF_COURIER_PRIME_FILENAME{ ":/CourierPrime.sdff" }; static const QString SDFF_COURIER_PRIME_FILENAME{ ":/CourierPrime.sdff" };

View file

@ -31,10 +31,10 @@ public:
const glm::vec4* color, EffectType effectType, const glm::vec4* color, EffectType effectType,
const glm::vec2& bound, bool layered = false); const glm::vec2& bound, bool layered = false);
static Pointer load(QIODevice& fontFile);
static Pointer load(const QString& family); static Pointer load(const QString& family);
private: private:
static Pointer load(QIODevice& fontFile);
QStringList tokenizeForWrapping(const QString& str) const; QStringList tokenizeForWrapping(const QString& str) const;
QStringList splitLines(const QString& str) const; QStringList splitLines(const QString& str) const;
glm::vec2 computeTokenExtent(const QString& str) const; glm::vec2 computeTokenExtent(const QString& str) const;

View file

@ -63,6 +63,4 @@ void EngineStats::run(const RenderContextPointer& renderContext) {
config->frameSetPipelineCount = _gpuStats._PSNumSetPipelines; config->frameSetPipelineCount = _gpuStats._PSNumSetPipelines;
config->frameSetInputFormatCount = _gpuStats._ISNumFormatChanges; config->frameSetInputFormatCount = _gpuStats._ISNumFormatChanges;
config->emitDirty();
} }

View file

@ -34,6 +34,7 @@ void TaskConfig::connectChildConfig(QConfigPointer childConfig, const std::strin
if (childConfig->metaObject()->indexOfSignal(DIRTY_SIGNAL) != -1) { if (childConfig->metaObject()->indexOfSignal(DIRTY_SIGNAL) != -1) {
// Connect dirty->refresh if defined // Connect dirty->refresh if defined
QObject::connect(childConfig.get(), SIGNAL(dirty()), this, SLOT(refresh())); QObject::connect(childConfig.get(), SIGNAL(dirty()), this, SLOT(refresh()));
QObject::connect(childConfig.get(), SIGNAL(dirtyEnabled()), this, SLOT(refresh()));
} }
} }
@ -50,6 +51,7 @@ void TaskConfig::transferChildrenConfigs(QConfigPointer source) {
if (child->metaObject()->indexOfSignal(DIRTY_SIGNAL) != -1) { if (child->metaObject()->indexOfSignal(DIRTY_SIGNAL) != -1) {
// Connect dirty->refresh if defined // Connect dirty->refresh if defined
QObject::connect(child, SIGNAL(dirty()), this, SLOT(refresh())); QObject::connect(child, SIGNAL(dirty()), this, SLOT(refresh()));
QObject::connect(child, SIGNAL(dirtyEnabled()), this, SLOT(refresh()));
} }
} }
} }

View file

@ -89,7 +89,7 @@ protected:
class JobConfig : public QObject { class JobConfig : public QObject {
Q_OBJECT Q_OBJECT
Q_PROPERTY(double cpuRunTime READ getCPURunTime NOTIFY newStats()) //ms Q_PROPERTY(double cpuRunTime READ getCPURunTime NOTIFY newStats()) //ms
Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled) Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled NOTIFY dirtyEnabled())
double _msCPURunTime{ 0.0 }; double _msCPURunTime{ 0.0 };
public: public:
@ -99,7 +99,7 @@ public:
JobConfig(bool enabled) : alwaysEnabled{ false }, enabled{ enabled } {} JobConfig(bool enabled) : alwaysEnabled{ false }, enabled{ enabled } {}
bool isEnabled() { return alwaysEnabled || enabled; } bool isEnabled() { return alwaysEnabled || enabled; }
void setEnabled(bool enable) { enabled = alwaysEnabled || enable; } void setEnabled(bool enable) { enabled = alwaysEnabled || enable; emit dirtyEnabled(); }
bool alwaysEnabled{ true }; bool alwaysEnabled{ true };
bool enabled{ true }; bool enabled{ true };
@ -121,6 +121,7 @@ public slots:
signals: signals:
void loaded(); void loaded();
void newStats(); void newStats();
void dirtyEnabled();
}; };
class TaskConfig : public JobConfig { class TaskConfig : public JobConfig {

View file

@ -170,6 +170,7 @@ protected:
std::string _name = ""; std::string _name = "";
}; };
// A task is a specialized job to run a collection of other jobs // A task is a specialized job to run a collection of other jobs
// It can be created on any type T by aliasing the type JobModel in the class T // It can be created on any type T by aliasing the type JobModel in the class T
// using JobModel = Task::Model<T> // using JobModel = Task::Model<T>

View file

@ -540,7 +540,7 @@ void TabletProxy::gotoWebScreen(const QString& url, const QString& injectedJavaS
QObject* TabletProxy::addButton(const QVariant& properties) { QObject* TabletProxy::addButton(const QVariant& properties) {
auto tabletButtonProxy = QSharedPointer<TabletButtonProxy>(new TabletButtonProxy(properties.toMap())); auto tabletButtonProxy = QSharedPointer<TabletButtonProxy>(new TabletButtonProxy(properties.toMap()));
std::lock_guard<std::mutex> guard(_tabletMutex); std::unique_lock<std::mutex> guard(_tabletMutex);
_tabletButtonProxies.push_back(tabletButtonProxy); _tabletButtonProxies.push_back(tabletButtonProxy);
if (!_toolbarMode && _qmlTabletRoot) { if (!_toolbarMode && _qmlTabletRoot) {
auto tablet = getQmlTablet(); auto tablet = getQmlTablet();
@ -550,7 +550,6 @@ QObject* TabletProxy::addButton(const QVariant& properties) {
qCCritical(scriptengine) << "Could not find tablet in TabletRoot.qml"; qCCritical(scriptengine) << "Could not find tablet in TabletRoot.qml";
} }
} else if (_toolbarMode) { } else if (_toolbarMode) {
auto tabletScriptingInterface = DependencyManager::get<TabletScriptingInterface>(); auto tabletScriptingInterface = DependencyManager::get<TabletScriptingInterface>();
QObject* toolbarProxy = tabletScriptingInterface->getSystemToolbarProxy(); QObject* toolbarProxy = tabletScriptingInterface->getSystemToolbarProxy();
@ -559,6 +558,8 @@ QObject* TabletProxy::addButton(const QVariant& properties) {
connectionType = Qt::BlockingQueuedConnection; connectionType = Qt::BlockingQueuedConnection;
} }
guard.unlock();
// copy properties from tablet button proxy to toolbar button proxy. // copy properties from tablet button proxy to toolbar button proxy.
QObject* toolbarButtonProxy = nullptr; QObject* toolbarButtonProxy = nullptr;
bool hasResult = QMetaObject::invokeMethod(toolbarProxy, "addButton", connectionType, Q_RETURN_ARG(QObject*, toolbarButtonProxy), Q_ARG(QVariant, tabletButtonProxy->getProperties())); bool hasResult = QMetaObject::invokeMethod(toolbarProxy, "addButton", connectionType, Q_RETURN_ARG(QObject*, toolbarButtonProxy), Q_ARG(QVariant, tabletButtonProxy->getProperties()));
@ -576,31 +577,38 @@ bool TabletProxy::onHomeScreen() {
} }
void TabletProxy::removeButton(QObject* tabletButtonProxy) { void TabletProxy::removeButton(QObject* tabletButtonProxy) {
std::lock_guard<std::mutex> guard(_tabletMutex); std::unique_lock<std::mutex> guard(_tabletMutex);
auto tablet = getQmlTablet(); auto tablet = getQmlTablet();
if (!tablet) { if (!tablet) {
qCCritical(scriptengine) << "Could not find tablet in TabletRoot.qml"; qCCritical(scriptengine) << "Could not find tablet in TabletRoot.qml";
} }
auto iter = std::find(_tabletButtonProxies.begin(), _tabletButtonProxies.end(), tabletButtonProxy); QSharedPointer<TabletButtonProxy> buttonProxy;
if (iter != _tabletButtonProxies.end()) { {
if (!_toolbarMode && _qmlTabletRoot) { auto iter = std::find(_tabletButtonProxies.begin(), _tabletButtonProxies.end(), tabletButtonProxy);
(*iter)->setQmlButton(nullptr); if (iter == _tabletButtonProxies.end()) {
if (tablet) { qCWarning(scriptengine) << "TabletProxy::removeButton() could not find button " << tabletButtonProxy;
QMetaObject::invokeMethod(tablet, "removeButtonProxy", Qt::AutoConnection, Q_ARG(QVariant, (*iter)->getProperties())); return;
}
} else if (_toolbarMode) {
auto tabletScriptingInterface = DependencyManager::get<TabletScriptingInterface>();
QObject* toolbarProxy = tabletScriptingInterface->getSystemToolbarProxy();
// remove button from toolbarProxy
QMetaObject::invokeMethod(toolbarProxy, "removeButton", Qt::AutoConnection, Q_ARG(QVariant, (*iter)->getUuid().toString()));
(*iter)->setToolbarButtonProxy(nullptr);
} }
buttonProxy = *iter;
_tabletButtonProxies.erase(iter); _tabletButtonProxies.erase(iter);
} else { }
qCWarning(scriptengine) << "TabletProxy::removeButton() could not find button " << tabletButtonProxy;
if (!_toolbarMode && _qmlTabletRoot) {
buttonProxy->setQmlButton(nullptr);
if (tablet) {
guard.unlock();
QMetaObject::invokeMethod(tablet, "removeButtonProxy", Qt::AutoConnection, Q_ARG(QVariant, buttonProxy->getProperties()));
}
} else if (_toolbarMode) {
auto tabletScriptingInterface = DependencyManager::get<TabletScriptingInterface>();
QObject* toolbarProxy = tabletScriptingInterface->getSystemToolbarProxy();
// remove button from toolbarProxy
guard.unlock();
QMetaObject::invokeMethod(toolbarProxy, "removeButton", Qt::AutoConnection, Q_ARG(QVariant, buttonProxy->getUuid().toString()));
buttonProxy->setToolbarButtonProxy(nullptr);
} }
} }

View file

@ -104,9 +104,9 @@ void QmlWindowClass::initQml(QVariantMap properties) {
Q_ASSERT(invokeResult); Q_ASSERT(invokeResult);
} else { } else {
// Build the event bridge and wrapper on the main thread // Build the event bridge and wrapper on the main thread
offscreenUi->load(qmlSource(), [&](QQmlContext* context, QObject* object) { offscreenUi->loadInNewContext(qmlSource(), [&](QQmlContext* context, QObject* object) {
_qmlWindow = object; _qmlWindow = object;
_qmlWindow->setProperty("eventBridge", QVariant::fromValue(this)); context->setContextProperty("eventBridge", this);
context->engine()->setObjectOwnership(this, QQmlEngine::CppOwnership); context->engine()->setObjectOwnership(this, QQmlEngine::CppOwnership);
context->engine()->setObjectOwnership(object, QQmlEngine::CppOwnership); context->engine()->setObjectOwnership(object, QQmlEngine::CppOwnership);
if (properties.contains(TITLE_PROPERTY)) { if (properties.contains(TITLE_PROPERTY)) {

View file

@ -10,10 +10,10 @@ if (WIN32)
# we're using static GLEW, so define GLEW_STATIC # we're using static GLEW, so define GLEW_STATIC
add_definitions(-DGLEW_STATIC) add_definitions(-DGLEW_STATIC)
set(TARGET_NAME openvr) set(TARGET_NAME openvr)
setup_hifi_plugin(OpenGL Script Qml Widgets) setup_hifi_plugin(OpenGL Script Qml Widgets Multimedia)
link_hifi_libraries(shared gl networking controllers ui link_hifi_libraries(shared gl networking controllers ui
plugins display-plugins ui-plugins input-plugins script-engine plugins display-plugins ui-plugins input-plugins script-engine
render-utils model gpu gpu-gl render model-networking fbx ktx image procedural) audio-client render-utils model gpu gpu-gl render model-networking fbx ktx image procedural)
include_hifi_library_headers(octree) include_hifi_library_headers(octree)
@ -21,4 +21,5 @@ if (WIN32)
find_package(OpenVR REQUIRED) find_package(OpenVR REQUIRED)
target_include_directories(${TARGET_NAME} PRIVATE ${OPENVR_INCLUDE_DIRS}) target_include_directories(${TARGET_NAME} PRIVATE ${OPENVR_INCLUDE_DIRS})
target_link_libraries(${TARGET_NAME} ${OPENVR_LIBRARIES}) target_link_libraries(${TARGET_NAME} ${OPENVR_LIBRARIES})
target_link_libraries(${TARGET_NAME} Winmm.lib)
endif() endif()

View file

@ -7,6 +7,9 @@
// //
#include "OpenVrDisplayPlugin.h" #include "OpenVrDisplayPlugin.h"
// Odd ordering of header is required to avoid 'macro redinition warnings'
#include <AudioClient.h>
#include <QtCore/QThread> #include <QtCore/QThread>
#include <QtCore/QLoggingCategory> #include <QtCore/QLoggingCategory>
#include <QtCore/QFileInfo> #include <QtCore/QFileInfo>
@ -713,3 +716,30 @@ bool OpenVrDisplayPlugin::isKeyboardVisible() {
int OpenVrDisplayPlugin::getRequiredThreadCount() const { int OpenVrDisplayPlugin::getRequiredThreadCount() const {
return Parent::getRequiredThreadCount() + (_threadedSubmit ? 1 : 0); return Parent::getRequiredThreadCount() + (_threadedSubmit ? 1 : 0);
} }
QString OpenVrDisplayPlugin::getPreferredAudioInDevice() const {
QString device = getVrSettingString(vr::k_pch_audio_Section, vr::k_pch_audio_OnPlaybackDevice_String);
if (!device.isEmpty()) {
static const WCHAR INIT = 0;
size_t size = device.size() + 1;
std::vector<WCHAR> deviceW;
deviceW.assign(size, INIT);
device.toWCharArray(deviceW.data());
device = AudioClient::friendlyNameForAudioDevice(deviceW.data());
}
return device;
}
QString OpenVrDisplayPlugin::getPreferredAudioOutDevice() const {
QString device = getVrSettingString(vr::k_pch_audio_Section, vr::k_pch_audio_OnRecordDevice_String);
if (!device.isEmpty()) {
static const WCHAR INIT = 0;
size_t size = device.size() + 1;
std::vector<WCHAR> deviceW;
deviceW.assign(size, INIT);
device.toWCharArray(deviceW.data());
device = AudioClient::friendlyNameForAudioDevice(deviceW.data());
}
return device;
}

View file

@ -58,6 +58,9 @@ public:
// Possibly needs an additional thread for VR submission // Possibly needs an additional thread for VR submission
int getRequiredThreadCount() const override; int getRequiredThreadCount() const override;
QString getPreferredAudioInDevice() const override;
QString getPreferredAudioOutDevice() const override;
protected: protected:
bool internalActivate() override; bool internalActivate() override;
void internalDeactivate() override; void internalDeactivate() override;

View file

@ -72,6 +72,21 @@ bool openVrSupported() {
return (enableDebugOpenVR || !isOculusPresent()) && vr::VR_IsHmdPresent(); return (enableDebugOpenVR || !isOculusPresent()) && vr::VR_IsHmdPresent();
} }
QString getVrSettingString(const char* section, const char* setting) {
QString result;
static const uint32_t BUFFER_SIZE = 1024;
static char BUFFER[BUFFER_SIZE];
vr::IVRSettings * vrSettings = vr::VRSettings();
if (vrSettings) {
vr::EVRSettingsError error = vr::VRSettingsError_None;
vrSettings->GetString(vr::k_pch_audio_Section, vr::k_pch_audio_OnPlaybackDevice_String, BUFFER, BUFFER_SIZE, &error);
if (error == vr::VRSettingsError_None) {
result = BUFFER;
}
}
return result;
}
vr::IVRSystem* acquireOpenVrSystem() { vr::IVRSystem* acquireOpenVrSystem() {
bool hmdPresent = vr::VR_IsHmdPresent(); bool hmdPresent = vr::VR_IsHmdPresent();
if (hmdPresent) { if (hmdPresent) {
@ -82,6 +97,7 @@ vr::IVRSystem* acquireOpenVrSystem() {
#endif #endif
vr::EVRInitError eError = vr::VRInitError_None; vr::EVRInitError eError = vr::VRInitError_None;
activeHmd = vr::VR_Init(&eError, vr::VRApplication_Scene); activeHmd = vr::VR_Init(&eError, vr::VRApplication_Scene);
#if DEV_BUILD #if DEV_BUILD
qCDebug(displayplugins) << "OpenVR display: HMD is " << activeHmd << " error is " << eError; qCDebug(displayplugins) << "OpenVR display: HMD is " << activeHmd << " error is " << eError;
#endif #endif

View file

@ -25,6 +25,7 @@ bool openVrQuitRequested();
void enableOpenVrKeyboard(PluginContainer* container); void enableOpenVrKeyboard(PluginContainer* container);
void disableOpenVrKeyboard(); void disableOpenVrKeyboard();
bool isOpenVrKeyboardShown(); bool isOpenVrKeyboardShown();
QString getVrSettingString(const char* section, const char* setting);
template<typename F> template<typename F>

View file

@ -123,15 +123,18 @@ bool ViveControllerManager::isSupported() const {
bool ViveControllerManager::activate() { bool ViveControllerManager::activate() {
InputPlugin::activate(); InputPlugin::activate();
_container->addMenu(MENU_PATH);
_container->addMenuItem(PluginType::INPUT_PLUGIN, MENU_PATH, RENDER_CONTROLLERS,
[this] (bool clicked) { this->setRenderControllers(clicked); },
true, true);
if (!_system) { if (!_system) {
_system = acquireOpenVrSystem(); _system = acquireOpenVrSystem();
} }
Q_ASSERT(_system);
if (!_system) {
return false;
}
_container->addMenu(MENU_PATH);
_container->addMenuItem(PluginType::INPUT_PLUGIN, MENU_PATH, RENDER_CONTROLLERS,
[this](bool clicked) { this->setRenderControllers(clicked); },
true, true);
enableOpenVrKeyboard(_container); enableOpenVrKeyboard(_container);

View file

@ -0,0 +1,127 @@
var AVATAR_SELF_ID = "{00000000-0000-0000-0000-000000000001}";
var debugSphereBaseProperties = {
type: "Sphere",
dimensions: { x: 0.2, y: 0.2, z: 0.2 },
dynamic: false,
collisionless: true,
gravity: { x: 0, y: 0, z: 0 },
lifetime: 10.0,
userData: "{ \"grabbableKey\": { \"grabbable\": false, \"kinematic\": false } }"
};
var debugBoxBaseProperties = {
type: "Box",
dimensions: { x: 0.2, y: 0.2, z: 0.2 },
dynamic: false,
collisionless: true,
gravity: { x: 0, y: 0, z: 0 },
lifetime: 10.0,
userData: "{ \"grabbableKey\": { \"grabbable\": false, \"kinematic\": false } }"
};
//jointToWorldPoint
// create sphere for finger on left hand
// each frame, calculate world position of finger, with some offset
// update sphere to match this position
var jointToWorldPointTest_sphereEntity;
function jointToWorldPointTest() {
var jointIndex = MyAvatar.getJointIndex("LeftHandPinky4");
var jointOffset_WorldSpace = { x: 0.1, y: 0, z: 0 };
var worldPos = MyAvatar.jointToWorldPoint(jointOffset_WorldSpace, jointIndex);
var jointSphereProps = Object.create(debugSphereBaseProperties);
jointSphereProps.name = "jointToWorldPointTest_Sphere";
jointSphereProps.color = { blue: 240, green: 150, red: 150 };
jointSphereProps.position = worldPos;
jointToWorldPointTest_sphereEntity = Entities.addEntity(jointSphereProps);
}
function jointToWorldPointTest_update(deltaTime) {
var jointIndex = MyAvatar.getJointIndex("LeftHandPinky4");
var jointOffset_WorldSpace = { x: 0.1, y: 0, z: 0 };
var worldPos = MyAvatar.jointToWorldPoint(jointOffset_WorldSpace, jointIndex);
var newProperties = { position: worldPos };
Entities.editEntity(jointToWorldPointTest_sphereEntity, newProperties);
}
//jointToWorldDirection
// create line in world space
// each frame calculate world space direction of players head z axis
// update line to match
var jointToWorldDirectionTest_lineEntity;
function jointToWorldDirectionTest() {
var jointIndex = MyAvatar.getJointIndex("Head");
var avatarPos = MyAvatar.getJointPosition(jointIndex);
var jointDir = { x: 1, y: 0, z: 1 };
var worldDir = MyAvatar.jointToWorldDirection(jointDir, jointIndex);
print(worldDir.x);
print(worldDir.y);
print(worldDir.z);
jointToWorldDirectionTest_lineEntity = Entities.addEntity({
type: "Line",
color: {red: 250, green: 0, blue: 0},
dimensions: {x: 5, y: 5, z: 5},
lifetime: 10.0,
linePoints: [{
x: 0,
y: 0,
z: 0
}, worldDir
],
position : avatarPos,
});
}
function jointToWorldDirection_update(deltaTime) {
var jointIndex = MyAvatar.getJointIndex("Head");
var avatarPos = MyAvatar.getJointPosition(jointIndex);
var jointDir = { x: 1, y: 0, z: 0 };
var worldDir = MyAvatar.jointToWorldDirection(jointDir, jointIndex);
var newProperties = {
linePoints: [{
x: 0,
y: 0,
z: 0
}, worldDir
],
position : avatarPos
};
Entities.editEntity(jointToWorldDirectionTest_lineEntity, newProperties);
}
//jointToWorldRotation
// create box in world space
// each frame calculate world space rotation of players head
// update box rotation to match
var jointToWorldRotationTest_boxEntity;
function jointToWorldRotationTest() {
var jointIndex = MyAvatar.getJointIndex("Head");
var jointPosition_WorldSpace = MyAvatar.getJointPosition(jointIndex);
var jointRot = MyAvatar.getJointRotation(jointIndex);
var jointRot_WorldSpace = MyAvatar.jointToWorldRotation(jointRot, jointIndex);
var boxProps = Object.create(debugBoxBaseProperties);
boxProps.name = "jointToWorldRotationTest_Box";
boxProps.color = { blue: 250, green: 250, red: 250 };
boxProps.position = jointPosition_WorldSpace;
boxProps.rotation = jointRot_WorldSpace;
jointToWorldRotationTest_boxEntity = Entities.addEntity(boxProps);
}
function jointToWorldRotationTest_update(deltaTime) {
var jointIndex = MyAvatar.getJointIndex("Head");
var jointPosition_WorldSpace = MyAvatar.getJointPosition(jointIndex);
var jointRot = MyAvatar.getJointRotation(jointIndex);
var jointRot_WorldSpace = MyAvatar.jointToWorldRotation(jointRot, jointIndex);
var newProperties = { position: jointPosition_WorldSpace, rotation: jointRot_WorldSpace };
Entities.editEntity(jointToWorldRotationTest_boxEntity, newProperties);
}
jointToWorldPointTest();
Script.update.connect(jointToWorldPointTest_update);
jointToWorldDirectionTest();
Script.update.connect(jointToWorldDirection_update);
jointToWorldRotationTest();
Script.update.connect(jointToWorldRotationTest_update);

View file

@ -0,0 +1,134 @@
var AVATAR_SELF_ID = "{00000000-0000-0000-0000-000000000001}";
var debugSphereBaseProperties = {
type: "Sphere",
dimensions: { x: 0.2, y: 0.2, z: 0.2 },
dynamic: false,
collisionless: true,
gravity: { x: 0, y: 0, z: 0 },
lifetime: 10.0,
userData: "{ \"grabbableKey\": { \"grabbable\": false, \"kinematic\": false } }"
};
var debugBoxBaseProperties = {
type: "Box",
dimensions: { x: 0.2, y: 0.2, z: 0.2 },
dynamic: false,
collisionless: true,
gravity: { x: 0, y: 0, z: 0 },
lifetime: 10.0,
userData: "{ \"grabbableKey\": { \"grabbable\": false, \"kinematic\": false } }"
};
//worldToJointPoint
// calculate position offset from joint using getJointPosition
// pass through worldToJointPoint to get offset in joint space of players joint
// create a blue sphere and attach it to players joint using the joint space offset
// The two spheres should appear in the same place, but the blue sphere will follow the avatar
function worldToJointPointTest() {
var jointIndex = MyAvatar.getJointIndex("LeftHandPinky4");
var avatarPos = MyAvatar.position;
var jointPosition_WorldSpace = MyAvatar.getJointPosition(jointIndex);
var jointOffset_WorldSpace = { x: 0.1, y: 0, z: 0 };
var jointPosition_WorldSpaceOffset = Vec3.sum(jointPosition_WorldSpace, jointOffset_WorldSpace);
var jointPosition_JointSpaceOffset = MyAvatar.worldToJointPoint(jointPosition_WorldSpaceOffset, jointIndex);
var jointSphereProps = Object.create(debugSphereBaseProperties);
jointSphereProps.name = "worldToJointPointTest_Joint";
jointSphereProps.color = { blue: 240, green: 150, red: 150 };
jointSphereProps.localPosition = jointPosition_JointSpaceOffset;
jointSphereProps.parentID = AVATAR_SELF_ID;
jointSphereProps.parentJointIndex = jointIndex;
Entities.addEntity(jointSphereProps);
var worldSphereProps = Object.create(debugSphereBaseProperties);
worldSphereProps.name = "worldToJointPointTest_World";
worldSphereProps.color = { blue: 150, green: 250, red: 150 };
worldSphereProps.position = jointPosition_WorldSpaceOffset;
Entities.addEntity(worldSphereProps);
}
//worldToJointDirection
// create line and attach to avatars head
// each frame calculate direction of world x axis in joint space of players head
// update arrow orientation to match
var worldToJointDirectionTest_lineEntity;
function worldToJointDirectionTest() {
var jointIndex = MyAvatar.getJointIndex("Head");
var jointPosition_WorldSpace = MyAvatar.getJointPosition(jointIndex);
var jointOffset_WorldSpace = { x: 0, y: 0, z: 0 };
var jointPosition_WorldSpaceOffset = Vec3.sum(jointPosition_WorldSpace, jointOffset_WorldSpace);
var jointPosition_JointSpaceOffset = MyAvatar.worldToJointPoint(jointPosition_WorldSpaceOffset, jointIndex);
var worldDir = { x: 1, y: 0, z: 0 };
var avatarDir = MyAvatar.worldToJointDirection(worldDir, jointIndex);
worldToJointDirectionTest_lineEntity = Entities.addEntity({
type: "Line",
color: {red: 200, green: 250, blue: 0},
dimensions: {x: 5, y: 5, z: 5},
lifetime: 10.0,
linePoints: [{
x: 0,
y: 0,
z: 0
}, avatarDir
],
localPosition : jointOffset_WorldSpace,
parentID : AVATAR_SELF_ID,
parentJointIndex : jointIndex
});
}
function worldToJointDirectionTest_update(deltaTime) {
var jointIndex = MyAvatar.getJointIndex("Head");
var worldDir = { x: 1, y: 0, z: 0 };
var avatarDir = MyAvatar.worldToJointDirection(worldDir, jointIndex);
var newProperties = { linePoints: [{
x: 0,
y: 0,
z: 0
}, avatarDir
]};
Entities.editEntity(worldToJointDirectionTest_lineEntity, newProperties);
}
//worldToJointRotation
// create box and parent to some player joint
// convert world identity rotation to joint space rotation
// each frame, update box with new orientation
var worldToJointRotationTest_boxEntity;
function worldToJointRotationTest() {
var jointIndex = MyAvatar.getJointIndex("RightHandPinky4");
var avatarPos = MyAvatar.position;
var jointPosition_WorldSpace = MyAvatar.getJointPosition(jointIndex);
var jointOffset_WorldSpace = { x: 0.0, y: 0, z: 0 };
var jointPosition_WorldSpaceOffset = Vec3.sum(jointPosition_WorldSpace, jointOffset_WorldSpace);
var jointPosition_JointSpaceOffset = MyAvatar.worldToJointPoint(jointPosition_WorldSpaceOffset, jointIndex);
var jointBoxProps = Object.create(debugBoxBaseProperties);
jointBoxProps.name = "worldToJointRotationTest_Box";
jointBoxProps.color = { blue: 0, green: 0, red: 250 };
jointBoxProps.localPosition = jointPosition_JointSpaceOffset;
jointBoxProps.parentID = AVATAR_SELF_ID;
jointBoxProps.parentJointIndex = jointIndex;
worldToJointRotationTest_boxEntity = Entities.addEntity(jointBoxProps);
}
function worldToJointRotationTest_update(deltaTime) {
var jointIndex = MyAvatar.getJointIndex("RightHandPinky4");
var worldRot = Quat.fromPitchYawRollDegrees(0,0,0);
var avatarRot = MyAvatar.worldToJointRotation(worldRot, jointIndex);
var newProperties = { localRotation: avatarRot };
Entities.editEntity(worldToJointRotationTest_boxEntity, newProperties);
}
worldToJointPointTest();
worldToJointDirectionTest();
worldToJointRotationTest();
Script.update.connect(worldToJointDirectionTest_update);
Script.update.connect(worldToJointRotationTest_update);

View file

@ -12,7 +12,7 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
// //
/* global Script, SelectionDisplay, LightOverlayManager, CameraManager, Grid, GridTool, EntityListTool, Vec3, SelectionManager, Overlays, OverlayWebWindow, UserActivityLogger, /* global Script, SelectionDisplay, LightOverlayManager, CameraManager, Grid, GridTool, EntityListTool, Vec3, SelectionManager, Overlays, OverlayWebWindow, UserActivityLogger,
Settings, Entities, Tablet, Toolbars, Messages, Menu, Camera, progressDialog, tooltip, MyAvatar, Quat, Controller, Clipboard, HMD, UndoStack, ParticleExplorerTool */ Settings, Entities, Tablet, Toolbars, Messages, Menu, Camera, progressDialog, tooltip, MyAvatar, Quat, Controller, Clipboard, HMD, UndoStack, ParticleExplorerTool */
(function() { // BEGIN LOCAL_SCOPE (function() { // BEGIN LOCAL_SCOPE
@ -80,27 +80,7 @@ selectionManager.addEventListener(function () {
} }
var type = Entities.getEntityProperties(selectedEntityID, "type").type; var type = Entities.getEntityProperties(selectedEntityID, "type").type;
if (type === "ParticleEffect") { if (type === "ParticleEffect") {
// Destroy the old particles web view first selectParticleEntity(selectedEntityID);
particleExplorerTool.destroyWebView();
particleExplorerTool.createWebView();
var properties = Entities.getEntityProperties(selectedEntityID);
var particleData = {
messageType: "particle_settings",
currentProperties: properties
};
selectedParticleEntityID = selectedEntityID;
particleExplorerTool.setActiveParticleEntity(selectedParticleEntityID);
particleExplorerTool.webView.webEventReceived.connect(function (data) {
data = JSON.parse(data);
if (data.messageType === "page_loaded") {
particleExplorerTool.webView.emitScriptEvent(JSON.stringify(particleData));
}
});
// Switch to particle explorer
var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system");
tablet.sendToQml({method: 'selectTab', params: {id: 'particle'}});
} else { } else {
needToDestroyParticleExplorer = true; needToDestroyParticleExplorer = true;
} }
@ -218,7 +198,7 @@ function hideMarketplace() {
// } // }
function adjustPositionPerBoundingBox(position, direction, registration, dimensions, orientation) { function adjustPositionPerBoundingBox(position, direction, registration, dimensions, orientation) {
// Adjust the position such that the bounding box (registration, dimenions, and orientation) lies behind the original // Adjust the position such that the bounding box (registration, dimenions, and orientation) lies behind the original
// position in the given direction. // position in the given direction.
var CORNERS = [ var CORNERS = [
{ x: 0, y: 0, z: 0 }, { x: 0, y: 0, z: 0 },
@ -1373,7 +1353,7 @@ function parentSelectedEntities() {
} }
}); });
if(parentCheck) { if (parentCheck) {
Window.notify("Entities parented"); Window.notify("Entities parented");
}else { }else {
Window.notify("Entities are already parented to last"); Window.notify("Entities are already parented to last");
@ -1575,7 +1555,7 @@ function importSVO(importURL) {
var properties = Entities.getEntityProperties(pastedEntityIDs[0], ["position", "dimensions", var properties = Entities.getEntityProperties(pastedEntityIDs[0], ["position", "dimensions",
"registrationPoint"]); "registrationPoint"]);
var position = Vec3.sum(deltaPosition, properties.position); var position = Vec3.sum(deltaPosition, properties.position);
position = grid.snapToSurface(grid.snapToGrid(position, false, properties.dimensions, position = grid.snapToSurface(grid.snapToGrid(position, false, properties.dimensions,
properties.registrationPoint), properties.dimensions, properties.registrationPoint); properties.registrationPoint), properties.dimensions, properties.registrationPoint);
deltaPosition = Vec3.subtract(position, properties.position); deltaPosition = Vec3.subtract(position, properties.position);
} }
@ -1907,11 +1887,11 @@ var PropertiesTool = function (opts) {
} }
pushCommandForSelections(); pushCommandForSelections();
selectionManager._update(); selectionManager._update();
} else if(data.type === 'parent') { } else if (data.type === 'parent') {
parentSelectedEntities(); parentSelectedEntities();
} else if(data.type === 'unparent') { } else if (data.type === 'unparent') {
unparentSelectedEntities(); unparentSelectedEntities();
} else if(data.type === 'saveUserData'){ } else if (data.type === 'saveUserData'){
//the event bridge and json parsing handle our avatar id string differently. //the event bridge and json parsing handle our avatar id string differently.
var actualID = data.id.split('"')[1]; var actualID = data.id.split('"')[1];
Entities.editEntity(actualID, data.properties); Entities.editEntity(actualID, data.properties);
@ -2203,6 +2183,10 @@ var selectedParticleEntityID = null;
function selectParticleEntity(entityID) { function selectParticleEntity(entityID) {
var properties = Entities.getEntityProperties(entityID); var properties = Entities.getEntityProperties(entityID);
if (properties.emitOrientation) {
properties.emitOrientation = Quat.safeEulerAngles(properties.emitOrientation);
}
var particleData = { var particleData = {
messageType: "particle_settings", messageType: "particle_settings",
currentProperties: properties currentProperties: properties
@ -2212,6 +2196,7 @@ function selectParticleEntity(entityID) {
selectedParticleEntity = entityID; selectedParticleEntity = entityID;
particleExplorerTool.setActiveParticleEntity(entityID); particleExplorerTool.setActiveParticleEntity(entityID);
particleExplorerTool.webView.emitScriptEvent(JSON.stringify(particleData)); particleExplorerTool.webView.emitScriptEvent(JSON.stringify(particleData));
// Switch to particle explorer // Switch to particle explorer
@ -2229,7 +2214,7 @@ entityListTool.webView.webEventReceived.connect(function (data) {
if (data.type === 'parent') { if (data.type === 'parent') {
parentSelectedEntities(); parentSelectedEntities();
} else if(data.type === 'unparent') { } else if (data.type === 'unparent') {
unparentSelectedEntities(); unparentSelectedEntities();
} else if (data.type === "selectionUpdate") { } else if (data.type === "selectionUpdate") {
var ids = data.entityIds; var ids = data.entityIds;
@ -2250,4 +2235,3 @@ entityListTool.webView.webEventReceived.connect(function (data) {
}); });
}()); // END LOCAL_SCOPE }()); // END LOCAL_SCOPE

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