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

This commit is contained in:
ZappoMan 2017-11-19 14:40:31 -08:00
commit 04ee1adabd
312 changed files with 9632 additions and 3422 deletions

View file

@ -68,7 +68,7 @@ module.exports = {
"eqeqeq": ["error", "always"],
"indent": ["error", 4, { "SwitchCase": 1 }],
"keyword-spacing": ["error", { "before": true, "after": true }],
"max-len": ["error", 192, 4],
"max-len": ["error", 128, 4],
"new-cap": ["error"],
"no-floating-decimal": ["error"],
//"no-magic-numbers": ["error", { "ignore": [0, 1], "ignoreArrayIndexes": true }],

4
.gitignore vendored
View file

@ -64,6 +64,10 @@ gvr-interface/libs/*
# ignore files for various dev environments
TAGS
*.sw[po]
*.qmlc
# ignore QML compilation output
*.qmlc
# ignore node files for the console
node_modules

View file

@ -19,6 +19,10 @@ Documentation is available at [docs.highfidelity.com](https://docs.highfidelity.
There is also detailed [documentation on our coding standards](https://wiki.highfidelity.com/wiki/Coding_Standards).
Contributor License Agreement (CLA)
=========
Technology companies frequently receive and use code from contributors outside the company's development team. Outside code can be a tremendous resource, but it also carries responsibility. Best practice for accepting outside contributions consists of an Apache-type Contributor License Agreement (CLA). We have modeled the High Fidelity CLA after the CLA that Google presents to developers for contributions to their projects. This CLA does not transfer ownership of code, instead simply granting a non-exclusive right for High Fidelity to use the code youve contributed. In that regard, you should be sure you have permission if the work relates to or uses the resources of a company that you work for. You will be asked to sign our CLA when you create your first PR or when the CLA is updated. You can also [review it here](https://gist.githubusercontent.com/hifi-gustavo/fef8f06a8233d42a0040d45c3efb97a9/raw/9981827eb94f0b18666083670b6f6a02929fb402/High%2520Fidelity%2520CLA). We sincerely appreciate your contribution and efforts toward the success of the platform.
Build Instructions
=========
All information required to build is found in the [build guide](BUILD.md).

View file

@ -28,6 +28,10 @@
const QString ASSIGNMENT_CLIENT_MONITOR_TARGET_NAME = "assignment-client-monitor";
const int WAIT_FOR_CHILD_MSECS = 1000;
#ifdef Q_OS_WIN
HANDLE PROCESS_GROUP = createProcessGroup();
#endif
AssignmentClientMonitor::AssignmentClientMonitor(const unsigned int numAssignmentClientForks,
const unsigned int minAssignmentClientForks,
const unsigned int maxAssignmentClientForks,
@ -202,6 +206,10 @@ void AssignmentClientMonitor::spawnChildClient() {
assignmentClient->setProcessChannelMode(QProcess::ForwardedChannels);
assignmentClient->start(QCoreApplication::applicationFilePath(), _childArguments);
#ifdef Q_OS_WIN
addProcessToGroup(PROCESS_GROUP, assignmentClient->processId());
#endif
QString stdoutPath, stderrPath;
if (_wantsChildFileLogging) {

View file

@ -50,9 +50,9 @@ static const int INTERFACE_RUNNING_CHECK_FREQUENCY_MS = 1000;
const QString ASSET_SERVER_LOGGING_TARGET_NAME = "asset-server";
static const QStringList BAKEABLE_MODEL_EXTENSIONS = {"fbx"};
static const QStringList BAKEABLE_MODEL_EXTENSIONS = { "fbx" };
static QStringList BAKEABLE_TEXTURE_EXTENSIONS;
static const QStringList BAKEABLE_SCRIPT_EXTENSIONS = {"js"};
static const QStringList BAKEABLE_SCRIPT_EXTENSIONS = {};
static const QString BAKED_MODEL_SIMPLE_NAME = "asset.fbx";
static const QString BAKED_TEXTURE_SIMPLE_NAME = "texture.ktx";
static const QString BAKED_SCRIPT_SIMPLE_NAME = "asset.js";

View file

@ -29,6 +29,7 @@
#include <UUID.h>
#include <CPUDetect.h>
#include "AudioLogging.h"
#include "AudioHelpers.h"
#include "AudioRingBuffer.h"
#include "AudioMixerClientData.h"
@ -130,7 +131,7 @@ void AudioMixer::queueReplicatedAudioPacket(QSharedPointer<ReceivedMessage> mess
PacketType rewrittenType = PacketTypeEnum::getReplicatedPacketMapping().key(message->getType());
if (rewrittenType == PacketType::Unknown) {
qDebug() << "Cannot unwrap replicated packet type not present in REPLICATED_PACKET_WRAPPING";
qCDebug(audio) << "Cannot unwrap replicated packet type not present in REPLICATED_PACKET_WRAPPING";
}
auto replicatedMessage = QSharedPointer<ReceivedMessage>::create(audioData, rewrittenType,
@ -345,7 +346,7 @@ void AudioMixer::sendStatsPacket() {
void AudioMixer::run() {
qDebug() << "Waiting for connection to domain to request settings from domain-server.";
qCDebug(audio) << "Waiting for connection to domain to request settings from domain-server.";
// wait until we have the domain-server settings, otherwise we bail
DomainHandler& domainHandler = DependencyManager::get<NodeList>()->getDomainHandler();
@ -502,14 +503,14 @@ void AudioMixer::throttle(std::chrono::microseconds duration, int frame) {
int proportionalTerm = 1 + (_trailingMixRatio - TARGET) / 0.1f;
_throttlingRatio += THROTTLE_RATE * proportionalTerm;
_throttlingRatio = std::min(_throttlingRatio, 1.0f);
qDebug("audio-mixer is struggling (%f mix/sleep) - throttling %f of streams",
(double)_trailingMixRatio, (double)_throttlingRatio);
qCDebug(audio) << "audio-mixer is struggling (" << _trailingMixRatio << "mix/sleep) - throttling"
<< _throttlingRatio << "of streams";
} else if (_throttlingRatio > 0.0f && _trailingMixRatio <= BACKOFF_TARGET) {
int proportionalTerm = 1 + (TARGET - _trailingMixRatio) / 0.2f;
_throttlingRatio -= BACKOFF_RATE * proportionalTerm;
_throttlingRatio = std::max(_throttlingRatio, 0.0f);
qDebug("audio-mixer is recovering (%f mix/sleep) - throttling %f of streams",
(double)_trailingMixRatio, (double)_throttlingRatio);
qCDebug(audio) << "audio-mixer is recovering (" << _trailingMixRatio << "mix/sleep) - throttling"
<< _throttlingRatio << "of streams";
}
}
}
@ -534,7 +535,7 @@ void AudioMixer::clearDomainSettings() {
}
void AudioMixer::parseSettingsObject(const QJsonObject& settingsObject) {
qDebug() << "AVX2 Support:" << (cpuSupportsAVX2() ? "enabled" : "disabled");
qCDebug(audio) << "AVX2 Support:" << (cpuSupportsAVX2() ? "enabled" : "disabled");
if (settingsObject.contains(AUDIO_THREADING_GROUP_KEY)) {
QJsonObject audioThreadingGroupObject = settingsObject[AUDIO_THREADING_GROUP_KEY].toObject();
@ -557,7 +558,7 @@ void AudioMixer::parseSettingsObject(const QJsonObject& settingsObject) {
const QString DYNAMIC_JITTER_BUFFER_JSON_KEY = "dynamic_jitter_buffer";
bool enableDynamicJitterBuffer = audioBufferGroupObject[DYNAMIC_JITTER_BUFFER_JSON_KEY].toBool();
if (enableDynamicJitterBuffer) {
qDebug() << "Enabling dynamic jitter buffers.";
qCDebug(audio) << "Enabling dynamic jitter buffers.";
bool ok;
const QString DESIRED_JITTER_BUFFER_FRAMES_KEY = "static_desired_jitter_buffer_frames";
@ -565,9 +566,9 @@ void AudioMixer::parseSettingsObject(const QJsonObject& settingsObject) {
if (!ok) {
_numStaticJitterFrames = InboundAudioStream::DEFAULT_STATIC_JITTER_FRAMES;
}
qDebug() << "Static desired jitter buffer frames:" << _numStaticJitterFrames;
qCDebug(audio) << "Static desired jitter buffer frames:" << _numStaticJitterFrames;
} else {
qDebug() << "Disabling dynamic jitter buffers.";
qCDebug(audio) << "Disabling dynamic jitter buffers.";
_numStaticJitterFrames = DISABLE_STATIC_JITTER_FRAMES;
}
@ -621,7 +622,7 @@ void AudioMixer::parseSettingsObject(const QJsonObject& settingsObject) {
if (audioEnvGroupObject[CODEC_PREFERENCE_ORDER].isString()) {
QString codecPreferenceOrder = audioEnvGroupObject[CODEC_PREFERENCE_ORDER].toString();
_codecPreferenceOrder = codecPreferenceOrder.split(",");
qDebug() << "Codec preference order changed to" << _codecPreferenceOrder;
qCDebug(audio) << "Codec preference order changed to" << _codecPreferenceOrder;
}
const QString ATTENATION_PER_DOULING_IN_DISTANCE = "attenuation_per_doubling_in_distance";
@ -630,7 +631,7 @@ void AudioMixer::parseSettingsObject(const QJsonObject& settingsObject) {
float attenuation = audioEnvGroupObject[ATTENATION_PER_DOULING_IN_DISTANCE].toString().toFloat(&ok);
if (ok) {
_attenuationPerDoublingInDistance = attenuation;
qDebug() << "Attenuation per doubling in distance changed to" << _attenuationPerDoublingInDistance;
qCDebug(audio) << "Attenuation per doubling in distance changed to" << _attenuationPerDoublingInDistance;
}
}
@ -640,7 +641,7 @@ void AudioMixer::parseSettingsObject(const QJsonObject& settingsObject) {
float noiseMutingThreshold = audioEnvGroupObject[NOISE_MUTING_THRESHOLD].toString().toFloat(&ok);
if (ok) {
_noiseMutingThreshold = noiseMutingThreshold;
qDebug() << "Noise muting threshold changed to" << _noiseMutingThreshold;
qCDebug(audio) << "Noise muting threshold changed to" << _noiseMutingThreshold;
}
}
@ -680,8 +681,7 @@ void AudioMixer::parseSettingsObject(const QJsonObject& settingsObject) {
glm::vec3 dimensions(xMax - xMin, yMax - yMin, zMax - zMin);
AABox zoneAABox(corner, dimensions);
_audioZones.insert(zone, zoneAABox);
qDebug() << "Added zone:" << zone << "(corner:" << corner
<< ", dimensions:" << dimensions << ")";
qCDebug(audio) << "Added zone:" << zone << "(corner:" << corner << ", dimensions:" << dimensions << ")";
}
}
}
@ -712,7 +712,7 @@ void AudioMixer::parseSettingsObject(const QJsonObject& settingsObject) {
_audioZones.contains(settings.source) && _audioZones.contains(settings.listener)) {
_zoneSettings.push_back(settings);
qDebug() << "Added Coefficient:" << settings.source << settings.listener << settings.coefficient;
qCDebug(audio) << "Added Coefficient:" << settings.source << settings.listener << settings.coefficient;
}
}
}
@ -745,7 +745,7 @@ void AudioMixer::parseSettingsObject(const QJsonObject& settingsObject) {
_zoneReverbSettings.push_back(settings);
qDebug() << "Added Reverb:" << zone << reverbTime << wetLevel;
qCDebug(audio) << "Added Reverb:" << zone << reverbTime << wetLevel;
}
}
}

View file

@ -19,6 +19,7 @@
#include "InjectedAudioStream.h"
#include "AudioLogging.h"
#include "AudioHelpers.h"
#include "AudioMixer.h"
#include "AudioMixerClientData.h"
@ -132,7 +133,7 @@ void AudioMixerClientData::optionallyReplicatePacket(ReceivedMessage& message, c
if (PacketTypeEnum::getReplicatedPacketMapping().key(message.getType()) != PacketType::Unknown) {
mirroredType = message.getType();
} else {
qDebug() << "Packet passed to optionallyReplicatePacket was not a replicatable type - returning";
qCDebug(audio) << "Packet passed to optionallyReplicatePacket was not a replicatable type - returning";
return;
}
}
@ -189,8 +190,16 @@ void AudioMixerClientData::parsePerAvatarGainSet(ReceivedMessage& message, const
uint8_t packedGain;
message.readPrimitive(&packedGain);
float gain = unpackFloatGainFromByte(packedGain);
hrtfForStream(avatarUuid, QUuid()).setGainAdjustment(gain);
qDebug() << "Setting gain adjustment for hrtf[" << uuid << "][" << avatarUuid << "] to " << gain;
if (avatarUuid.isNull()) {
// set the MASTER avatar gain
setMasterAvatarGain(gain);
qCDebug(audio) << "Setting MASTER avatar gain for " << uuid << " to " << gain;
} else {
// set the per-source avatar gain
hrtfForStream(avatarUuid, QUuid()).setGainAdjustment(gain);
qCDebug(audio) << "Setting avatar gain adjustment for hrtf[" << uuid << "][" << avatarUuid << "] to " << gain;
}
}
void AudioMixerClientData::parseNodeIgnoreRequest(QSharedPointer<ReceivedMessage> message, const SharedNodePointer& node) {
@ -276,7 +285,7 @@ int AudioMixerClientData::parseData(ReceivedMessage& message) {
auto avatarAudioStream = new AvatarAudioStream(isStereo, AudioMixer::getStaticJitterFrames());
avatarAudioStream->setupCodec(_codec, _selectedCodecName, AudioConstants::MONO);
qDebug() << "creating new AvatarAudioStream... codec:" << _selectedCodecName;
qCDebug(audio) << "creating new AvatarAudioStream... codec:" << _selectedCodecName;
connect(avatarAudioStream, &InboundAudioStream::mismatchedAudioCodec,
this, &AudioMixerClientData::handleMismatchAudioFormat);
@ -315,7 +324,7 @@ int AudioMixerClientData::parseData(ReceivedMessage& message) {
#if INJECTORS_SUPPORT_CODECS
injectorStream->setupCodec(_codec, _selectedCodecName, isStereo ? AudioConstants::STEREO : AudioConstants::MONO);
qDebug() << "creating new injectorStream... codec:" << _selectedCodecName;
qCDebug(audio) << "creating new injectorStream... codec:" << _selectedCodecName;
#endif
auto emplaced = _audioStreams.emplace(
@ -339,8 +348,8 @@ int AudioMixerClientData::parseData(ReceivedMessage& message) {
auto parseResult = matchingStream->parseData(message);
if (matchingStream->getOverflowCount() > overflowBefore) {
qDebug() << "Just overflowed on stream from" << message.getSourceID() << "at" << message.getSenderSockAddr();
qDebug() << "This stream is for" << (isMicStream ? "microphone audio" : "injected audio");
qCDebug(audio) << "Just overflowed on stream from" << message.getSourceID() << "at" << message.getSenderSockAddr();
qCDebug(audio) << "This stream is for" << (isMicStream ? "microphone audio" : "injected audio");
}
return parseResult;
@ -689,7 +698,7 @@ void AudioMixerClientData::setupCodecForReplicatedAgent(QSharedPointer<ReceivedM
auto codecString = message->readString();
if (codecString != _selectedCodecName) {
qDebug() << "Manually setting codec for replicated agent" << uuidStringWithoutCurlyBraces(getNodeID())
qCDebug(audio) << "Manually setting codec for replicated agent" << uuidStringWithoutCurlyBraces(getNodeID())
<< "-" << codecString;
const std::pair<QString, CodecPluginPointer> codec = AudioMixer::negotiateCodec({ codecString });

View file

@ -83,6 +83,9 @@ public:
// uses randomization to have the AudioMixer send a stats packet to this node around every second
bool shouldSendStats(int frameNumber);
float getMasterAvatarGain() const { return _masterAvatarGain; }
void setMasterAvatarGain(float gain) { _masterAvatarGain = gain; }
AudioLimiter audioLimiter;
void setupCodec(CodecPluginPointer codec, const QString& codecName);
@ -175,6 +178,8 @@ private:
int _frameToSendStats { 0 };
float _masterAvatarGain { 1.0f }; // per-listener mixing gain, applied only to avatars
CodecPluginPointer _codec;
QString _selectedCodecName;
Encoder* _encoder{ nullptr }; // for outbound mixed stream

View file

@ -48,8 +48,8 @@ void sendEnvironmentPacket(const SharedNodePointer& node, AudioMixerClientData&
// mix helpers
inline float approximateGain(const AvatarAudioStream& listeningNodeStream, const PositionalAudioStream& streamToAdd,
const glm::vec3& relativePosition);
inline float computeGain(const AvatarAudioStream& listeningNodeStream, const PositionalAudioStream& streamToAdd,
const glm::vec3& relativePosition, bool isEcho);
inline float computeGain(const AudioMixerClientData& listenerNodeData, const AvatarAudioStream& listeningNodeStream,
const PositionalAudioStream& streamToAdd, const glm::vec3& relativePosition, bool isEcho);
inline float computeAzimuth(const AvatarAudioStream& listeningNodeStream, const PositionalAudioStream& streamToAdd,
const glm::vec3& relativePosition);
@ -266,7 +266,7 @@ void AudioMixerSlave::addStream(AudioMixerClientData& listenerNodeData, const QU
glm::vec3 relativePosition = streamToAdd.getPosition() - listeningNodeStream.getPosition();
float distance = glm::max(glm::length(relativePosition), EPSILON);
float gain = computeGain(listeningNodeStream, streamToAdd, relativePosition, isEcho);
float gain = computeGain(listenerNodeData, listeningNodeStream, streamToAdd, relativePosition, isEcho);
float azimuth = isEcho ? 0.0f : computeAzimuth(listeningNodeStream, listeningNodeStream, relativePosition);
const int HRTF_DATASET_INDEX = 1;
@ -484,10 +484,12 @@ float approximateGain(const AvatarAudioStream& listeningNodeStream, const Positi
// when throttling, as close streams are expected to be heard by a user
float distance = glm::length(relativePosition);
return gain / distance;
// avatar: skip master gain - it is constant for all streams
}
float computeGain(const AvatarAudioStream& listeningNodeStream, const PositionalAudioStream& streamToAdd,
const glm::vec3& relativePosition, bool isEcho) {
float computeGain(const AudioMixerClientData& listenerNodeData, const AvatarAudioStream& listeningNodeStream,
const PositionalAudioStream& streamToAdd, const glm::vec3& relativePosition, bool isEcho) {
float gain = 1.0f;
// injector: apply attenuation
@ -507,6 +509,9 @@ float computeGain(const AvatarAudioStream& listeningNodeStream, const Positional
float offAxisCoefficient = MAX_OFF_AXIS_ATTENUATION + (angleOfDelivery * (OFF_AXIS_ATTENUATION_STEP / PI_OVER_TWO));
gain *= offAxisCoefficient;
// apply master gain, only to avatars
gain *= listenerNodeData.getMasterAvatarGain();
}
auto& audioZones = AudioMixer::getAudioZones();

View file

@ -41,8 +41,15 @@ EntityServer::EntityServer(ReceivedMessage& message) :
DependencyManager::set<ScriptCache>();
auto& packetReceiver = DependencyManager::get<NodeList>()->getPacketReceiver();
packetReceiver.registerListenerForTypes({ PacketType::EntityAdd, PacketType::EntityEdit, PacketType::EntityErase, PacketType::EntityPhysics, PacketType::ChallengeOwnership },
this, "handleEntityPacket");
packetReceiver.registerListenerForTypes({ PacketType::EntityAdd,
PacketType::EntityEdit,
PacketType::EntityErase,
PacketType::EntityPhysics,
PacketType::ChallengeOwnership,
PacketType::ChallengeOwnershipRequest,
PacketType::ChallengeOwnershipReply },
this,
"handleEntityPacket");
connect(&_dynamicDomainVerificationTimer, &QTimer::timeout, this, &EntityServer::startDynamicDomainVerification);
_dynamicDomainVerificationTimer.setSingleShot(true);
@ -459,7 +466,7 @@ void EntityServer::startDynamicDomainVerification() {
EntityItemPointer entity = tree->findEntityByEntityItemID(i.value());
if (entity) {
if (!entity->verifyStaticCertificateProperties()) {
if (!entity->getProperties().verifyStaticCertificateProperties()) {
qCDebug(entities) << "During Dynamic Domain Verification, a certified entity with ID" << i.value() << "failed"
<< "static certificate verification.";
// Delete the entity if it doesn't pass static certificate verification

View file

@ -23,6 +23,17 @@ EntityTreeSendThread::EntityTreeSendThread(OctreeServer* myServer, const SharedN
{
connect(std::static_pointer_cast<EntityTree>(myServer->getOctree()).get(), &EntityTree::editingEntityPointer, this, &EntityTreeSendThread::editingEntityPointer, Qt::QueuedConnection);
connect(std::static_pointer_cast<EntityTree>(myServer->getOctree()).get(), &EntityTree::deletingEntityPointer, this, &EntityTreeSendThread::deletingEntityPointer, Qt::QueuedConnection);
// connect to connection ID change on EntityNodeData so we can clear state for this receiver
auto nodeData = static_cast<EntityNodeData*>(node->getLinkedData());
connect(nodeData, &EntityNodeData::incomingConnectionIDChanged, this, &EntityTreeSendThread::resetState);
}
void EntityTreeSendThread::resetState() {
qCDebug(entities) << "Clearing known EntityTreeSendThread state for" << _nodeUuid;
_knownState.clear();
_traversal.reset();
}
void EntityTreeSendThread::preDistributionProcessing() {
@ -175,7 +186,7 @@ bool EntityTreeSendThread::addAncestorsToExtraFlaggedEntities(const QUuid& filte
return parentWasNew || ancestorsWereNew;
}
// since we didn't have a parent niether of our parents or ancestors could be new additions
// since we didn't have a parent, neither of our parents or ancestors could be new additions
return false;
}
@ -204,7 +215,9 @@ bool EntityTreeSendThread::addDescendantsToExtraFlaggedEntities(const QUuid& fil
return hasNewChild || hasNewDescendants;
}
void EntityTreeSendThread::startNewTraversal(const ViewFrustum& view, EntityTreeElementPointer root, int32_t lodLevelOffset, bool usesViewFrustum) {
void EntityTreeSendThread::startNewTraversal(const ViewFrustum& view, EntityTreeElementPointer root, int32_t lodLevelOffset,
bool usesViewFrustum) {
DiffTraversal::Type type = _traversal.prepareNewTraversal(view, root, lodLevelOffset, usesViewFrustum);
// there are three types of traversal:
//
@ -423,12 +436,19 @@ bool EntityTreeSendThread::traverseTreeAndBuildNextPacketPayload(EncodeBitstream
uint64_t sendTime = usecTimestampNow();
auto nodeData = static_cast<OctreeQueryNode*>(params.nodeData);
nodeData->stats.encodeStarted();
auto entityNode = _node.toStrongRef();
auto entityNodeData = static_cast<EntityNodeData*>(entityNode->getLinkedData());
while(!_sendQueue.empty()) {
PrioritizedEntity queuedItem = _sendQueue.top();
EntityItemPointer entity = queuedItem.getEntity();
if (entity) {
// Only send entities that match the jsonFilters, but keep track of everything we've tried to send so we don't try to send it again
if (entity->matchesJSONFilters(jsonFilters)) {
bool entityMatchesFilters = entity->matchesJSONFilters(jsonFilters);
if (entityMatchesFilters || entityNodeData->isEntityFlaggedAsExtra(entity->getID())) {
if (!jsonFilters.isEmpty() && entityMatchesFilters) {
// Record explicitly filtered-in entity so that extra entities can be flagged.
entityNodeData->insertSentFilteredEntity(entity->getID());
}
OctreeElement::AppendState appendEntityState = entity->appendEntityData(&_packetData, params, _extraEncodeData);
if (appendEntityState != OctreeElement::COMPLETED) {

View file

@ -33,12 +33,16 @@ protected:
void traverseTreeAndSendContents(SharedNodePointer node, OctreeQueryNode* nodeData,
bool viewFrustumChanged, bool isFullScene) override;
private slots:
void resetState(); // clears our known state forcing entities to appear unsent
private:
// the following two methods return booleans to indicate if any extra flagged entities were new additions to set
bool addAncestorsToExtraFlaggedEntities(const QUuid& filteredEntityID, EntityItem& entityItem, EntityNodeData& nodeData);
bool addDescendantsToExtraFlaggedEntities(const QUuid& filteredEntityID, EntityItem& entityItem, EntityNodeData& nodeData);
void startNewTraversal(const ViewFrustum& viewFrustum, EntityTreeElementPointer root, int32_t lodLevelOffset, bool usesViewFrustum);
void startNewTraversal(const ViewFrustum& viewFrustum, EntityTreeElementPointer root, int32_t lodLevelOffset,
bool usesViewFrustum);
bool traverseTreeAndBuildNextPacketPayload(EncodeBitstreamParams& params, const QJsonObject& jsonFilters) override;
void preDistributionProcessing() override;

View file

@ -96,6 +96,14 @@ void OctreeInboundPacketProcessor::processPacket(QSharedPointer<ReceivedMessage>
_myServer->getOctree()->withWriteLock([&] {
_myServer->getOctree()->processChallengeOwnershipPacket(*message, sendingNode);
});
} else if (packetType == PacketType::ChallengeOwnershipRequest) {
_myServer->getOctree()->withWriteLock([&] {
_myServer->getOctree()->processChallengeOwnershipRequestPacket(*message, sendingNode);
});
} else if (packetType == PacketType::ChallengeOwnershipReply) {
_myServer->getOctree()->withWriteLock([&] {
_myServer->getOctree()->processChallengeOwnershipReplyPacket(*message, sendingNode);
});
} else if (_myServer->getOctree()->handlesEditPacketType(packetType)) {
PerformanceWarning warn(debugProcessPacket, "processPacket KNOWN TYPE", debugProcessPacket);
_receivedPacketCount++;

View file

@ -82,8 +82,12 @@ bool OctreeSendThread::process() {
if (auto node = _node.lock()) {
OctreeQueryNode* nodeData = static_cast<OctreeQueryNode*>(node->getLinkedData());
// Sometimes the node data has not yet been linked, in which case we can't really do anything
if (nodeData && !nodeData->isShuttingDown()) {
// If we don't have the OctreeQueryNode at all
// or it's uninitialized because we haven't received a query yet from the client
// or we don't know where we should send packets for this node
// or we're shutting down
// then we can't send an entity data packet
if (nodeData && nodeData->hasReceivedFirstQuery() && node->getActiveSocket() && !nodeData->isShuttingDown()) {
bool viewFrustumChanged = nodeData->updateCurrentViewFrustum();
packetDistributor(node, nodeData, viewFrustumChanged);
}

View file

@ -59,7 +59,8 @@ protected:
OctreePacketData _packetData;
QWeakPointer<Node> _node;
OctreeServer* _myServer { nullptr };
QUuid _nodeUuid;
private:
/// Called before a packetDistributor pass to allow for pre-distribution processing
virtual void preDistributionProcessing() {};
@ -71,8 +72,6 @@ private:
virtual void preStartNewScene(OctreeQueryNode* nodeData, bool isFullScene);
virtual bool shouldTraverseAndSend(OctreeQueryNode* nodeData) { return hasSomethingToSend(nodeData); }
QUuid _nodeUuid;
int _truePacketsSent { 0 }; // available for debug stats
int _trueBytesSent { 0 }; // available for debug stats
int _packetsSentThisInterval { 0 }; // used for bandwidth throttle condition

View file

@ -5,43 +5,41 @@ set(EXTERNAL_NAME hifiAudioCodec)
string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER)
if (NOT ANDROID)
if (WIN32 OR APPLE)
ExternalProject_Add(
${EXTERNAL_NAME}
URL http://s3.amazonaws.com/hifi-public/dependencies/codecSDK-1.zip
URL_MD5 23ec3fe51eaa155ea159a4971856fc13
CONFIGURE_COMMAND ""
BUILD_COMMAND ""
INSTALL_COMMAND ""
LOG_DOWNLOAD 1
)
else ()
ExternalProject_Add(
${EXTERNAL_NAME}
URL http://s3.amazonaws.com/hifi-public/dependencies/codecSDK-linux.zip
URL_MD5 7d37914a18aa4de971d2f45dd3043bde
CONFIGURE_COMMAND ""
BUILD_COMMAND ""
INSTALL_COMMAND ""
LOG_DOWNLOAD 1
)
endif()
# Hide this external target (for ide users)
set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals")
ExternalProject_Get_Property(${EXTERNAL_NAME} SOURCE_DIR)
set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${SOURCE_DIR}/include CACHE TYPE INTERNAL)
if (WIN32)
set(${EXTERNAL_NAME_UPPER}_LIBRARIES ${SOURCE_DIR}/Release/audio.lib CACHE TYPE INTERNAL)
elseif(APPLE)
set(${EXTERNAL_NAME_UPPER}_LIBRARIES ${SOURCE_DIR}/Release/libaudio.a CACHE TYPE INTERNAL)
elseif(NOT ANDROID)
set(${EXTERNAL_NAME_UPPER}_LIBRARIES ${SOURCE_DIR}/Release/libaudio.a CACHE TYPE INTERNAL)
endif()
if (WIN32)
set(DOWNLOAD_URL http://s3.amazonaws.com/hifi-public/dependencies/codecSDK-win-2.0.zip)
set(DOWNLOAD_MD5 9199d4dbd6b16bed736b235efe980e67)
elseif (APPLE)
set(DOWNLOAD_URL http://s3.amazonaws.com/hifi-public/dependencies/codecSDK-mac-2.0.zip)
set(DOWNLOAD_MD5 21649881e7d5dc94f922179be96f76ba)
elseif (ANDROID)
set(DOWNLOAD_URL http://s3.amazonaws.com/hifi-public/dependencies/codecSDK-android-2.0.zip)
set(DOWNLOAD_MD5 aef2a852600d498d58aa586668191683)
elseif (UNIX)
set(DOWNLOAD_URL http://s3.amazonaws.com/hifi-public/dependencies/codecSDK-linux-2.0.zip)
set(DOWNLOAD_MD5 67fb7755f9bcafb98a9fceea53bc7481)
else()
return()
endif()
ExternalProject_Add(
${EXTERNAL_NAME}
URL ${DOWNLOAD_URL}
URL_MD5 ${DOWNLOAD_MD5}
CONFIGURE_COMMAND ""
BUILD_COMMAND ""
INSTALL_COMMAND ""
LOG_DOWNLOAD 1
)
# Hide this external target (for ide users)
set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals")
ExternalProject_Get_Property(${EXTERNAL_NAME} SOURCE_DIR)
set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${SOURCE_DIR}/include CACHE TYPE INTERNAL)
if (WIN32)
set(${EXTERNAL_NAME_UPPER}_LIBRARIES ${SOURCE_DIR}/Release/audio.lib CACHE TYPE INTERNAL)
else()
set(${EXTERNAL_NAME_UPPER}_LIBRARIES ${SOURCE_DIR}/Release/libaudio.a CACHE TYPE INTERNAL)
endif()

View file

@ -6,8 +6,8 @@ if (WIN32)
include(ExternalProject)
ExternalProject_Add(
${EXTERNAL_NAME}
URL http://hifi-public.s3.amazonaws.com/dependencies/qtaudio_wasapi9.zip
URL_MD5 94f4765bdbcd53cd099f349ae031e769
URL http://hifi-public.s3.amazonaws.com/dependencies/qtaudio_wasapi10.zip
URL_MD5 4f40e49715a420fb67b45b9cee19052c
CONFIGURE_COMMAND ""
BUILD_COMMAND ""
INSTALL_COMMAND ""

View file

@ -60,7 +60,7 @@ if (WIN32 AND NOT CYGWIN)
select_library_configurations(LIB_EAY)
select_library_configurations(SSL_EAY)
set(OPENSSL_LIBRARIES ${SSL_EAY_LIBRARY} ${LIB_EAY_LIBRARY})
find_path(OPENSSL_DLL_PATH NAMES ssleay32.dll PATH_SUFFIXES "bin" ${_OPENSSL_ROOT_HINTS_AND_PATHS})
find_path(OPENSSL_DLL_PATH NAMES ssleay32.dll PATH_SUFFIXES "bin" HINTS ${_OPENSSL_ROOT_HINTS_AND_PATHS} NO_DEFAULT_PATH)
endif()
else()

View file

@ -19,12 +19,13 @@
Upload an entities file (e.g.: models.json.gz) to replace the content of this domain.<br>
Note: <strong>Your domain's content will be replaced by the content you upload</strong>, but the backup files of your domain's content will not immediately be changed.
</p>
<p>
If your domain has any content that you would like to re-use at a later date, save a manual backup of your models.json.gz file, which is usually stored at the following paths:<br>
<pre>C:/Users/[username]/AppData/Roaming/High Fidelity/assignment-client/entities/models.json.gz</pre>
<pre>/Users/[username]/Library/Application Support/High Fidelity/assignment-client/entities/models.json.gz</pre>
<pre>/home/[username]/.local/share/High Fidelity/assignment-client/entities/models.json.gz</pre>
</p>
<p>If your domain has any content that you would like to re-use at a later date, save a manual backup of your models.json.gz file, which is usually stored at the following paths:</p>
<label class="control-label">Windows</label>
<pre>C:/Users/[username]/AppData/Roaming/High Fidelity/assignment-client/entities/models.json.gz</pre>
<label class="control-label">OSX</label>
<pre>/Users/[username]/Library/Application Support/High Fidelity/assignment-client/entities/models.json.gz</pre>
<label class="control-label">Linux</label>
<pre>/home/[username]/.local/share/High Fidelity/assignment-client/entities/models.json.gz</pre>
<br>
<input type="file" name="entities-file" class="form-control-file" accept=".json, .gz">
<br>

View file

@ -80,11 +80,23 @@ span.port {
display: none;
}
#setup-sidebar.affix {
/* This overrides a case where going to the bottom of the page,
* then scrolling up, causes `position: relative` to be added to the style
*/
position: fixed !important;
@media (min-width: 768px) {
#setup-sidebar.affix {
/* This overrides a case where going to the bottom of the page,
* then scrolling up, causes `position: relative` to be added to the style
*/
position: fixed !important;
}
}
@media (max-width: 767px) {
#setup-sidebar.affix {
position: static !important;
}
#setup-sidebar {
margin-bottom: 20px;
}
}
#setup-sidebar button {
@ -302,12 +314,12 @@ table .headers + .headers td {
}
.account-connected-header {
vertical-align: middle;
color: #6FCF97;
font-size: 30px;
margin-right: 20px;
}
#visit-domain-link,
.blue-link {
font-size: 14px;
text-decoration-line: underline;

View file

@ -39,7 +39,7 @@
<li><a href="/settings/">Settings</a></li>
</ul>
<ul class="nav navbar-right navbar-nav">
<li><a id="visit-domain-link" target="_blank" style="display: none;">Visit domain in VR</a></li>
<li><a id="visit-domain-link" class="blue-link" target="_blank" style="display: none;">Visit domain in VR</a></li>
<li><a href="#" id="restart-server"><span class="glyphicon glyphicon-refresh"></span> Restart</a></li>
</ul>
</div>

View file

@ -62,26 +62,25 @@ var Strings = {
// dialog with new path still set, allowing them to retry immediately, and without
// having to type the new path in again.
EDIT_PLACE_TITLE: "Modify Viewpoint or Path",
EDIT_PLACE_ERROR: "Failed to update place path. Please try again.",
EDIT_PLACE_ERROR: "Failed to update Viewpoint or Path for this Place Name. Please try again.",
EDIT_PLACE_CONFIRM_BUTTON: "Save",
EDIT_PLACE_CONFIRM_BUTTON_PENDING: "Saving...",
EDIT_PLACE_CANCEL_BUTTON: "Cancel",
REMOVE_PLACE_TITLE: "Are you sure you want to remove <strong>{{place}}</strong>?",
REMOVE_PLACE_ERROR: "Failed to remove place. Please try again.",
REMOVE_PLACE_DELETE_BUTTON: "Delete",
REMOVE_PLACE_TITLE: "Are you sure you want to remove <strong>{{place}}</strong> and its path information?",
REMOVE_PLACE_ERROR: "Failed to remove Place Name and its Path information.",
REMOVE_PLACE_DELETE_BUTTON: "This action removes your Place Name",
REMOVE_PLACE_DELETE_BUTTON_PENDING: "Deleting...",
REMOVE_PLACE_CANCEL_BUTTON: "Cancel",
ADD_PLACE_TITLE: "Choose a place",
ADD_PLACE_MESSAGE: "Choose the High Fidelity place to point at this domain server.",
ADD_PLACE_CONFIRM_BUTTON: "Choose place",
ADD_PLACE_MESSAGE: "Choose a Place Name that you own or register a new Place Name.",
ADD_PLACE_CONFIRM_BUTTON: "Save",
ADD_PLACE_CONFIRM_BUTTON_PENDING: "Saving...",
ADD_PLACE_CANCEL_BUTTON: "Cancel",
ADD_PLACE_UNKNOWN_ERROR: "There was an error adding this place name.",
ADD_PLACE_UNKNOWN_ERROR: "There was an error adding this Place Name. Try saving again",
ADD_PLACE_NO_PLACES_MESSAGE: "<p>You do not have any places in your High Fidelity account."
+ "<br/><br/>Go to your <a href='https://metaverse.highfidelity.com/user/places/new'>places page</a> to create a new one. Once your place is created re-open this dialog to select it.</p>",
ADD_PLACE_NO_PLACES_MESSAGE: "You don't have any Place Names registered. Once you have a Place Name, reopen this window to select it.",
ADD_PLACE_NO_PLACES_BUTTON: "Create new place",
ADD_PLACE_UNABLE_TO_LOAD_ERROR: "We were unable to load your place names. Please try again later.",
ADD_PLACE_LOADING_DIALOG: "Loading your places...",
@ -146,187 +145,256 @@ function sendUpdatePlaceRequest(id, path, domainID, clearDomainID, onSuccess, on
});
}
var pendingDomainRequest = null;
function getDomainFromAPI(callback) {
if (pendingDomainRequest !== null) {
pendingDomainRequest.success(callback);
pendingDomainRequest.error(function() { callback({ status: 'fail' }) });
return pendingDomainRequest;
}
if (callback === undefined) {
callback = function() {};
}
var domainID = Settings.data.values.metaverse.id;
if (domainID === null || domainID === undefined || domainID === '') {
callback({ status: 'fail' });
return null;
}
pendingDomainRequest = $.ajax({
url: "/api/domains/" + domainID,
dataType: 'json',
success: function(data) {
pendingDomainRequest = null;
if (data.status === 'success') {
DomainInfo = data.domain;
} else {
DomainInfo = null;
}
callback(data);
},
error: function() {
pendingDomainRequest = null;
DomainInfo = null;
callback({ status: 'fail' });
}
});
return pendingDomainRequest;
}
function chooseFromHighFidelityPlaces(accessToken, forcePathTo, onSuccessfullyAdded) {
if (accessToken) {
var loadingDialog = showLoadingDialog(Strings.ADD_PLACE_LOADING_DIALOG);
$.ajax("/api/places", {
dataType: 'json',
jsonp: false,
success: function(data) {
if (data.status == 'success') {
var modal_buttons = {
cancel: {
label: Strings.ADD_PLACE_CANCEL_BUTTON,
className: 'add-place-cancel-button btn-default'
}
};
var dialog;
var modal_body;
if (data.data.places.length) {
var places_by_id = {};
modal_body = $('<div>');
modal_body.append($("<p>Choose a place name that you own or <a href='" + URLs.METAVERSE_URL + "/user/places' target='_blank'>register a new place name</a></p>"));
var currentDomainIDType = getCurrentDomainIDType();
if (currentDomainIDType === DOMAIN_ID_TYPE_TEMP) {
var warning = "<div class='domain-loading-error alert alert-warning'>";
warning += "If you choose a place name it will replace your current temporary place name.";
warning += "</div>";
modal_body.append(warning);
}
// setup a select box for the returned places
modal_body.append($("<label for='place-name-select'>Places</label>"));
place_select = $("<select id='place-name-select' class='form-control'></select>");
_.each(data.data.places, function(place) {
places_by_id[place.id] = place;
place_select.append("<option value='" + place.id + "'>" + place.name + "</option>");
})
modal_body.append(place_select);
modal_body.append($("<p id='place-name-warning' class='warning-text' style='display: none'>This place name already points to a place or path. Saving this would overwrite the previous settings associated with it.</p>"));
if (forcePathTo === undefined || forcePathTo === null) {
var path = "<div class='form-group'>";
path += "<label for='place-path-input' class='control-label'>Path</label>";
path += "<input type='text' id='place-path-input' class='form-control' value='/'>";
path += "</div>";
modal_body.append($(path));
}
var place_select = modal_body.find("#place-name-select")
place_select.change(function(ev) {
var warning = modal_body.find("#place-name-warning");
var place = places_by_id[$(this).val()];
if (place === undefined || place.pointee === null) {
warning.hide();
} else {
warning.show();
function loadPlaces() {
$.ajax("/api/places", {
dataType: 'json',
jsonp: false,
success: function(data) {
if (data.status == 'success') {
var modal_buttons = {
cancel: {
label: Strings.ADD_PLACE_CANCEL_BUTTON,
className: 'add-place-cancel-button btn-default'
}
});
place_select.trigger('change');
};
modal_buttons["success"] = {
label: Strings.ADD_PLACE_CONFIRM_BUTTON,
className: 'add-place-confirm-button btn btn-primary',
callback: function() {
var placeID = $('#place-name-select').val();
// set the place ID on the form
$(Settings.place_ID_SELECTOR).val(placeID).change();
var dialog;
var modal_body;
if (forcePathTo === undefined || forcePathTo === null) {
var placePath = $('#place-path-input').val();
if (data.data.places.length) {
var places_by_id = {};
modal_body = $('<div>');
modal_body.append($("<p>Choose a place name that you own or <a href='" + URLs.METAVERSE_URL + "/user/places' target='_blank'>register a new place name</a></p>"));
var currentDomainIDType = getCurrentDomainIDType();
if (currentDomainIDType === DOMAIN_ID_TYPE_TEMP) {
var warning = "<div class='domain-loading-error alert alert-warning'>";
warning += "If you choose a place name it will replace your current temporary place name.";
warning += "</div>";
modal_body.append(warning);
}
// setup a select box for the returned places
modal_body.append($("<label for='place-name-select'>Places</label>"));
place_select = $("<select id='place-name-select' class='form-control'></select>");
_.each(data.data.places, function(place) {
places_by_id[place.id] = place;
place_select.append("<option value='" + place.id + "'>" + place.name + "</option>");
})
modal_body.append(place_select);
modal_body.append($("<p id='place-name-warning' class='warning-text' style='display: none'>This place name already points to a place or path. Saving this would overwrite the previous settings associated with it.</p>"));
if (forcePathTo === undefined || forcePathTo === null) {
var path = "<div class='form-group'>";
path += "<label for='place-path-input' class='control-label'>Path or Viewpoint</label>";
path += "<input type='text' id='place-path-input' class='form-control' value='/'>";
path += "</div>";
modal_body.append($(path));
}
var place_select = modal_body.find("#place-name-select")
place_select.change(function(ev) {
var warning = modal_body.find("#place-name-warning");
var place = places_by_id[$(this).val()];
if (place === undefined || place.pointee === null) {
warning.hide();
} else {
var placePath = forcePathTo;
warning.show();
}
});
place_select.trigger('change');
$('.add-place-confirm-button').attr('disabled', 'disabled');
$('.add-place-confirm-button').html(Strings.ADD_PLACE_CONFIRM_BUTTON_PENDING);
$('.add-place-cancel-button').attr('disabled', 'disabled');
modal_buttons["success"] = {
label: Strings.ADD_PLACE_CONFIRM_BUTTON,
className: 'add-place-confirm-button btn btn-primary',
callback: function() {
var placeID = $('#place-name-select').val();
// set the place ID on the form
$(Settings.place_ID_SELECTOR).val(placeID).change();
function finalizeSaveDomainID(domainID) {
var jsonSettings = {
metaverse: {
id: domainID
if (forcePathTo === undefined || forcePathTo === null) {
var placePath = $('#place-path-input').val();
} else {
var placePath = forcePathTo;
}
$('.add-place-confirm-button').attr('disabled', 'disabled');
$('.add-place-confirm-button').html(Strings.ADD_PLACE_CONFIRM_BUTTON_PENDING);
$('.add-place-cancel-button').attr('disabled', 'disabled');
function finalizeSaveDomainID(domainID) {
var jsonSettings = {
metaverse: {
id: domainID
}
}
var dialog = showLoadingDialog("Waiting for Domain Server to restart...");
$.ajax('/settings.json', {
data: JSON.stringify(jsonSettings),
contentType: 'application/json',
type: 'POST'
}).done(function(data) {
if (data.status == "success") {
waitForDomainServerRestart(function() {
dialog.modal('hide');
if (onSuccessfullyAdded) {
onSuccessfullyAdded(places_by_id[placeID].name, domainID);
}
});
} else {
bootbox.alert("Failed to add place");
}
}).fail(function() {
bootbox.alert("Failed to add place");
});
}
// If domainID is not specified, the current domain id will be used.
function finishSettingUpPlace(domainID) {
sendUpdatePlaceRequest(
placeID,
placePath,
domainID,
false,
function(data) {
dialog.modal('hide')
if (domainID) {
$(Settings.DOMAIN_ID_SELECTOR).val(domainID).change();
finalizeSaveDomainID(domainID);
} else {
if (onSuccessfullyAdded) {
onSuccessfullyAdded(places_by_id[placeID].name);
}
}
},
function(data) {
$('.add-place-confirm-button').removeAttr('disabled');
$('.add-place-confirm-button').html(Strings.ADD_PLACE_CONFIRM_BUTTON);
$('.add-place-cancel-button').removeAttr('disabled');
bootbox.alert(Strings.ADD_PLACE_UNKNOWN_ERROR);
}
);
}
function maybeCreateNewDomainID() {
console.log("Maybe creating domain id", currentDomainIDType)
if (currentDomainIDType === DOMAIN_ID_TYPE_FULL) {
finishSettingUpPlace();
} else {
sendCreateDomainRequest(function(domainID) {
console.log("Created domain", domainID);
finishSettingUpPlace(domainID);
}, function() {
$('.add-place-confirm-button').removeAttr('disabled');
$('.add-place-confirm-button').html(Strings.ADD_PLACE_CONFIRM_BUTTON);
$('.add-place-cancel-button').removeAttr('disabled');
bootbox.alert(Strings.ADD_PLACE_UNKNOWN_ERROR);
});
}
}
var dialog = showLoadingDialog("Waiting for Domain Server to restart...");
$.ajax('/settings.json', {
data: JSON.stringify(jsonSettings),
contentType: 'application/json',
type: 'POST'
}).done(function(data) {
if (data.status == "success") {
waitForDomainServerRestart(function() {
dialog.modal('hide');
if (onSuccessfullyAdded) {
onSuccessfullyAdded(places_by_id[placeID].name, domainID);
}
});
} else {
bootbox.alert("Failed to add place");
}
}).fail(function() {
bootbox.alert("Failed to add place");
});
}
function finishSettingUpPlace(domainID) {
sendUpdatePlaceRequest(
placeID,
placePath,
domainID,
false,
function(data) {
$(Settings.DOMAIN_ID_SELECTOR).val(domainID).change();
dialog.modal('hide')
if (domainID) {
finalizeSaveDomainID(domainID);
} else {
if (onSuccessfullyAdded) {
onSuccessfullyAdded(places_by_id[placeID].name);
}
}
},
function(data) {
$('.add-place-confirm-button').removeAttr('disabled');
$('.add-place-confirm-button').html(Strings.ADD_PLACE_CONFIRM_BUTTON);
$('.add-place-cancel-button').removeAttr('disabled');
bootbox.alert(Strings.ADD_PLACE_UNKNOWN_ERROR);
}
);
}
maybeCreateNewDomainID();
if (currentDomainIDType === DOMAIN_ID_TYPE_FULL) {
finishSettingUpPlace();
} else {
sendCreateDomainRequest(function(domainID) {
console.log("Created domain", domainID);
finishSettingUpPlace(domainID);
}, function() {
$('.add-place-confirm-button').removeAttr('disabled');
$('.add-place-confirm-button').html(Strings.ADD_PLACE_CONFIRM_BUTTON);
$('.add-place-cancel-button').removeAttr('disabled');
bootbox.alert(Strings.ADD_PLACE_UNKNOWN_ERROR);
bootbox.alert("FAIL");
});
return false;
}
return false;
}
} else {
modal_buttons["success"] = {
label: Strings.ADD_PLACE_NO_PLACES_BUTTON,
callback: function() {
window.open(URLs.METAVERSE_URL + "/user/places", '_blank');
}
}
modal_body = Strings.ADD_PLACE_NO_PLACES_MESSAGE;
}
dialog = bootbox.dialog({
title: Strings.ADD_PLACE_TITLE,
message: modal_body,
closeButton: false,
buttons: modal_buttons,
onEscape: true
});
} else {
modal_buttons["success"] = {
label: Strings.ADD_PLACE_NO_PLACES_BUTTON,
callback: function() {
window.open(URLs.METAVERSE_URL + "/user/places", '_blank');
}
}
modal_body = Strings.ADD_PLACE_NO_PLACES_MESSAGE;
bootbox.alert(Strings.ADD_PLACE_UNABLE_TO_LOAD_ERROR);
}
dialog = bootbox.dialog({
title: Strings.ADD_PLACE_TITLE,
message: modal_body,
closeButton: false,
buttons: modal_buttons
});
} else {
},
error: function() {
bootbox.alert(Strings.ADD_PLACE_UNABLE_TO_LOAD_ERROR);
},
complete: function() {
loadingDialog.modal('hide');
}
},
error: function() {
bootbox.alert(Strings.ADD_PLACE_UNABLE_TO_LOAD_ERROR);
},
complete: function() {
loadingDialog.modal('hide');
}
});
});
}
var domainType = getCurrentDomainIDType();
if (domainType !== DOMAIN_ID_TYPE_UNKNOWN) {
loadPlaces();
} else {
getDomainFromAPI(function(data) {
if (data.status === 'success') {
var domainType = getCurrentDomainIDType();
loadPlaces();
} else {
loadingDialog.modal('hide');
bootbox.confirm("We were not able to load your domain information from the Metaverse. Would you like to retry?", function(response) {
if (response) {
chooseFromHighFidelityPlaces(accessToken, forcePathTo, onSuccessfullyAdded);
}
});
}
})
}
} else {
bootbox.alert({

View file

@ -36,11 +36,6 @@
</div>
<div class="col-md-9 col-sm-9 col-xs-12">
<div id="xs-advanced-container" class="col-xs-12 hidden-sm hidden-md hidden-lg">
<button id="advanced-toggle-button-xs" class="btn btn-info advanced-toggle">Show advanced</button>
</div>
<div class="col-xs-12">
<div id="cloud-domains-alert" class="alert alert-info alert-dismissible" role="alert" style="display: none;">

View file

@ -503,7 +503,7 @@ function showDomainCreationAlert(justConnected) {
swal({
title: 'Create new domain ID',
type: 'input',
text: 'Enter a short description for this machine.</br></br>This will help you identify which domain ID belongs to which machine.</br></br>',
text: 'Enter a label this machine.</br></br>This will help you identify which domain ID belongs to which machine.</br></br>',
showCancelButton: true,
confirmButtonText: "Create",
closeOnConfirm: false,
@ -527,13 +527,12 @@ function showDomainCreationAlert(justConnected) {
function createNewDomainID(label, justConnected) {
// get the JSON object ready that we'll use to create a new domain
var domainJSON = {
"label": label
//"access_token": $(Settings.ACCESS_TOKEN_SELECTOR).val()
"label": label
}
$.post("/api/domains", domainJSON, function(data){
// we successfully created a domain ID, set it on that field
var domainID = data.domain_id;
var domainID = data.domain.id;
console.log("Setting domain id to ", data, domainID);
$(Settings.DOMAIN_ID_SELECTOR).val(domainID).change();
@ -620,18 +619,14 @@ function parseJSONResponse(xhr) {
function showOrHideLabel() {
var type = getCurrentDomainIDType();
if (!accessTokenIsSet() || (type !== DOMAIN_ID_TYPE_FULL && type !== DOMAIN_ID_TYPE_UNKNOWN)) {
$(".panel#label").hide();
return false;
}
$(".panel#label").show();
return true;
var shouldShow = accessTokenIsSet() && (type === DOMAIN_ID_TYPE_FULL || type === DOMAIN_ID_TYPE_UNKNOWN);
$(".panel#label").toggle(shouldShow);
$("li a[href='#label']").parent().toggle(shouldShow);
return shouldShow;
}
function setupDomainLabelSetting() {
if (!showOrHideLabel()) {
return;
}
showOrHideLabel();
var html = "<div>"
html += "<label class='control-label'>Specify a label for your domain</label> <a class='domain-loading-hide' href='#'>Edit</a>";
@ -654,6 +649,7 @@ function setupDomainLabelSetting() {
title: 'Edit Label',
message: modal_body,
closeButton: false,
onEscape: true,
buttons: [
{
label: 'Cancel',
@ -742,7 +738,7 @@ function setupDomainNetworkingSettings() {
var includeAddress = autoNetworkingSetting === 'disabled';
if (includeAddress) {
var label = "Network Address and Port";
var label = "Network Address:Port";
} else {
var label = "Network Port";
}
@ -777,6 +773,7 @@ function setupDomainNetworkingSettings() {
title: 'Edit Network',
message: modal_body,
closeButton: false,
onEscape: true,
buttons: [
{
label: 'Cancel',
@ -924,6 +921,7 @@ function placeTableRow(name, path, isTemporary, placeID) {
var dialog = bootbox.dialog({
message: confirmString,
closeButton: false,
onEscape: true,
buttons: [
{
label: Strings.REMOVE_PLACE_CANCEL_BUTTON,
@ -980,20 +978,6 @@ function placeTableRowForPlaceObject(place) {
return placeTableRow(place.name, placePathOrIndex, false, place.id);
}
function getDomainFromAPI(callback) {
var domainID = Settings.data.values.metaverse.id;
$.ajax({
url: "/api/domains/" + domainID,
dataType: 'json',
success: function(data) {
callback(data);
},
error: function() {
callback({ status: 'fail' });
}
});
}
function reloadDomainInfo() {
$('#' + Settings.PLACES_TABLE_ID + " tbody tr").not('.headers').remove();
@ -1010,7 +994,6 @@ function reloadDomainInfo() {
// check if we have owner_places (for a real domain) or a name (for a temporary domain)
if (data.status == "success") {
$('.domain-loading-hide').show();
DomainInfo = data.domain;
if (data.domain.owner_places) {
// add a table row for each of these names
_.each(data.domain.owner_places, function(place){
@ -1040,10 +1023,11 @@ function reloadDomainInfo() {
}
}
appendAddButtonToPlacesTable();
if (accessTokenIsSet()) {
appendAddButtonToPlacesTable();
}
} else {
DomainInfo = null;
$('.domain-loading-error').show();
}
})
@ -1114,6 +1098,7 @@ function editHighFidelityPlace(placeID, name, path) {
dialog = bootbox.dialog({
title: Strings.EDIT_PLACE_TITLE,
closeButton: false,
onEscape: true,
message: modal_body,
buttons: modal_buttons
})
@ -1196,6 +1181,7 @@ function chooseFromHighFidelityDomains(clickedButton) {
bootbox.dialog({
title: "Choose matching domain",
onEscape: true,
message: modal_body,
buttons: modal_buttons
})

View file

@ -31,6 +31,12 @@ label {
color: #373A3C;
}
.wizard-link {
font-size: 16px;
font-weight: normal;
color: #2F80ED;
}
#admin-row {
margin-top: 20px;
margin-bottom: 20px;
@ -84,6 +90,14 @@ label {
height: 169px;
}
#visit-domain-row {
margin-bottom: 68px;
#congratulation-text {
margin-bottom: 59px;
}
#visit-domain-checkbox {
margin-bottom: 23px;
}
#visit-domain-checkbox label {
margin: 0 0;
}

View file

@ -60,8 +60,7 @@
</div>
<div id="admin-row" class="row">
<p class="col-md-6">
<span id="admin-description" class="step-info"><b>Add your High Fidelity username</b> and any other usernames to grant administrator privileges.</span>
<span class='glyphicon glyphicon-info-sign' data-toggle="tooltip" title="Users who will have all the permissions for this domain."></span>
<span class="step-info"><span id="admin-description"><b>Add your High Fidelity username</b> and any other usernames</span> to grant <span class='wizard-link' data-toggle="tooltip" title="Users who will have all the permissions for this domain.">administrator privileges</span></span>
</p>
<div class="col-md-6">
<input id="admin-usernames" type="text" class="form-control">
@ -78,7 +77,7 @@
<div class="row">
<div class="col-md-12">
<p id="connect-question" class="step-info">
Who can connect to your domain?
Who can <a href='#' class='wizard-link perms-link'>connect</a> to your domain?
<span class='glyphicon glyphicon-info-sign' data-toggle='tooltip' title='You can set this to allow a user to connect to this domain.'></span>
</p>
</div>
@ -87,25 +86,21 @@
<p class="col-md-2">
<label>
<input id="connect-none" name="connect-radio" type="radio" value="none" checked> None
<span class='glyphicon glyphicon-info-sign' data-toggle='tooltip' title='Only the admins of this domain'></span>
</label>
</p>
<p class="col-md-3">
<label>
<input id="connect-friends" name="connect-radio" type="radio" value="friends"> Friends
<span class='glyphicon glyphicon-info-sign' data-toggle='tooltip' title='Users who are your Friends in High Fidelity'></span>
</label>
</p>
<p class="col-md-5">
<label>
<input id="connect-logged-in" name="connect-radio" type="radio" value="logged-in"> Users logged in to High Fidelity
<span class='glyphicon glyphicon-info-sign' data-toggle='tooltip' title='Users who are currently logged into High Fidelity'></span>
<input id="connect-logged-in" name="connect-radio" type="radio" value="logged-in"> Users logged into High Fidelity
</label>
</p>
<p class="col-md-2">
<label>
<input id="connect-everyone" name="connect-radio" type="radio" value="everyone"> Everyone
<span class='glyphicon glyphicon-info-sign' data-toggle='tooltip' title="Users who aren't logged into High Fidelity"></span>
</label>
</p>
</div>
@ -113,7 +108,7 @@
<div class="row">
<div class="col-md-12">
<p class="step-info">
Who can rez items in your domain?
Who can <a href='#' class='wizard-link perms-link'>rez items</a> in your domain?
<span class='glyphicon glyphicon-info-sign' data-toggle='tooltip' title='You can set this to allow a user to create entities in this domain.'></span>
</p>
</div>
@ -122,33 +117,32 @@
<p class="col-md-2">
<label>
<input id="rez-none" name="rez-radio" type="radio" value="none" checked> None
<span class='glyphicon glyphicon-info-sign' data-toggle='tooltip' title='Only the admins of this domain'></span>
</label>
</p>
<p class="col-md-3">
<label>
<input id="rez-friends" name="rez-radio" type="radio" value="friends"> Friends
<span class='glyphicon glyphicon-info-sign' data-toggle='tooltip' title='Users who are your Friends in High Fidelity'></span>
<input id="rez-friends" name="rez-radio" type="radio" value="friends" disabled> Friends
</label>
</p>
<p class="col-md-5">
<label>
<input id="rez-logged-in" name="rez-radio" type="radio" value="logged-in"> Users logged in to High Fidelity
<span class='glyphicon glyphicon-info-sign' data-toggle='tooltip' title='Users who are currently logged into High Fidelity'></span>
<input id="rez-logged-in" name="rez-radio" type="radio" value="logged-in" disabled> Users logged into High Fidelity
</label>
</p>
<p class="col-md-2">
<label>
<input id="rez-everyone" name="rez-radio" type="radio" value="everyone"> Everyone
<span class='glyphicon glyphicon-info-sign' data-toggle='tooltip' title="Users who aren't logged into High Fidelity"></span>
<input id="rez-everyone" name="rez-radio" type="radio" value="everyone" disabled> Everyone
</label>
</p>
</div>
<div class="row">
<div class="col-md-3 col-md-offset-9">
<dd class="col-md-3">
<button type="button" class="btn btn-md btn-block btn-default back-button">Back</button>
</dd>
<dd class="col-md-3 col-md-offset-6">
<button id="save-permissions" type="button" class="btn btn-md btn-block btn-primary">Next</button>
</div>
</dd>
</div>
</div>
@ -188,35 +182,37 @@
</dl>
<dl class="row">
<dd class="col-md-3 col-md-offset-9">
<dd class="col-md-3">
<button type="button" class="btn btn-md btn-block btn-default back-button">Back</button>
</dd>
<dd class="col-md-3 col-md-offset-6">
<button id="save-username-password" type="button" class="btn btn-md btn-block btn-primary">Finish</button>
</dd>
</dl>
</div>
<div class="wizard-step cloud-only col-md-7 col-centered" style="display: none;">
<div class="wizard-step cloud-only col-xs-12 col-sm-12 col-md-9 col-lg-7 col-centered" style="display: none;">
<div class="row">
<div class="col-xs-4 col-centered">
<div class="col-xs-12" align="center">
<img id="checkmark-image" src="../images/checkmark.svg">
</div>
</div>
<div class="row">
<div class="col-md-12">
<div id="congratulation-text" class="row">
<div class="col-xs-12">
<p class="step-info">Congratulations! You have successfully setup and configured your cloud hosted domain.</p>
</div>
</div>
<div id="visit-domain-row" class="row">
<div class="col-md-12">
<label><input id="go-to-domain" class="form-check-input" type="checkbox"> Visit domain in VR now</label>
</div>
</div>
<dl class="row">
<dd class="col-md-5 col-md-offset-7">
<button id="explore-settings" type="button" class="btn btn-md btn-block btn-primary">Explore all domain server settings</button>
</dd>
<div class="col-xs-12">
<div class="pull-right">
<div id="visit-domain-checkbox">
<label><input id="go-to-domain" class="form-check-input" type="checkbox"> Visit domain in VR now</label>
</div>
<button id="explore-settings" type="button" class="btn btn-md btn-primary">Explore all domain server settings</button>
</div>
</div>
</dl>
</div>

View file

@ -2,6 +2,8 @@ var Metaverse = {
accessToken: null
}
var currentStepNumber;
$(document).ready(function(){
Strings.ADD_PLACE_NOT_CONNECTED_MESSAGE = "You must have an access token to query your High Fidelity places.<br><br>" +
"Please go back and connect your account.";
@ -9,6 +11,22 @@ $(document).ready(function(){
$('#connect-account-btn').attr('href', URLs.METAVERSE_URL + "/user/tokens/new?for_domain_server=true");
$('[data-toggle="tooltip"]').tooltip();
$('.perms-link').on('click', function() {
var modal_body = '<div>';
modal_body += '<b>None</b> - No one will have permissions. Only you and the users your have given administrator privileges to will have permissions.</br></br>';
modal_body += '<b>Friends</b> - Users who are your Friends in High Fidelity.</br></br>';
modal_body += '<b>Users logged into High Fidelity</b> - Users who are currently logged into High Fidelity.</br></br>';
modal_body += '<b>Everyone</b> - Anyone who uses High Fidelity.';
modal_body += '</div>';
dialog = bootbox.dialog({
title: "User definition",
message: modal_body,
closeButton: true
});
return false;
});
$('body').on('click', '.next-button', function() {
goToNextStep();
@ -56,8 +74,39 @@ $(document).ready(function(){
exploreSettings();
});
$('input[type=radio][name=connect-radio]').change(function() {
var inputs = $('input[type=radio][name=rez-radio]');
var disabled = [];
switch (this.value) {
case 'none':
disabled = inputs.splice(1);
break;
case 'friends':
disabled = inputs.splice(2);
break;
case 'logged-in':
disabled = inputs.splice(3);
break;
case 'everyone':
disabled = inputs.splice(4);
break;
}
$.each(inputs, function() {
$(this).prop('disabled', false);
});
$.each(disabled, function() {
if ($(this).prop('checked')) {
$(inputs.last()).prop('checked', true);
}
$(this).prop('disabled', true);
});
});
reloadSettings(function(success) {
if (success) {
getDomainFromAPI();
setupWizardSteps();
updatePlaceNameDisplay();
updateUsernameDisplay();
@ -72,12 +121,12 @@ $(document).ready(function(){
});
function setupWizardSteps() {
var stepsCompleted = Settings.data.values.wizard.steps_completed;
currentStepNumber = Settings.data.values.wizard.steps_completed;
var steps = null;
if (Settings.data.values.wizard.cloud_domain) {
$('.desktop-only').remove();
$('.wizard-step').find('.back-button').hide();
$('.wizard-step:first').find('.back-button').hide();
steps = $('.wizard-step');
$(steps).each(function(i) {
@ -85,7 +134,7 @@ function setupWizardSteps() {
});
$('#permissions-description').html('You <span id="username-display"></span>have been assigned administrator privileges to this domain.');
$('#admin-description').html('Add more High Fidelity usernames to grant administrator privileges.');
$('#admin-description').html('Add more High Fidelity usernames');
} else {
$('.cloud-only').remove();
$('#save-permissions').text("Finish");
@ -95,12 +144,12 @@ function setupWizardSteps() {
$(this).children(".step-title").text("Step " + (i + 1) + " of " + steps.length);
});
if (stepsCompleted == 0) {
if (currentStepNumber == 0) {
$('#skip-wizard-button').show();
}
}
var currentStep = steps[stepsCompleted];
var currentStep = steps[currentStepNumber];
$(currentStep).show();
}
@ -203,7 +252,7 @@ function goToNextStep() {
currentStep.hide();
nextStep.show();
var currentStepNumber = parseInt(Settings.data.values.wizard.steps_completed) + 1;
currentStepNumber += 1;
postSettings({
"wizard": {
@ -232,7 +281,7 @@ function goToPreviousStep() {
currentStep.hide();
previousStep.show();
var currentStepNumber = parseInt(Settings.data.values.wizard.steps_completed) - 1;
currentStepNumber -= 1;
postSettings({
"wizard": {
@ -438,7 +487,7 @@ function saveUsernamePassword() {
return;
}
var currentStepNumber = parseInt(Settings.data.values.wizard.steps_completed) + 1;
currentStepNumber += 1;
var formJSON = {
"security": {

View file

@ -830,26 +830,6 @@ void DomainServer::setupICEHeartbeatForFullNetworking() {
void DomainServer::updateICEServerAddresses() {
if (_iceAddressLookupID == INVALID_ICE_LOOKUP_ID) {
_iceAddressLookupID = QHostInfo::lookupHost(_iceServerAddr, this, SLOT(handleICEHostInfo(QHostInfo)));
// there seems to be a 5.9 bug where lookupHost never calls our slot
// so we add a single shot manual "timeout" to fire it off again if it hasn't called back yet
static const int ICE_ADDRESS_LOOKUP_TIMEOUT_MS = 5000;
QTimer::singleShot(ICE_ADDRESS_LOOKUP_TIMEOUT_MS, this, &DomainServer::timeoutICEAddressLookup);
}
}
void DomainServer::timeoutICEAddressLookup() {
if (_iceAddressLookupID != INVALID_ICE_LOOKUP_ID) {
// we waited 5s and didn't hear back for our ICE DNS lookup
// so time that one out and kick off another
qDebug() << "IP address lookup timed out for" << _iceServerAddr << "- retrying";
QHostInfo::abortHostLookup(_iceAddressLookupID);
_iceAddressLookupID = INVALID_ICE_LOOKUP_ID;
updateICEServerAddresses();
}
}
@ -3007,9 +2987,20 @@ void DomainServer::handleKeypairChange() {
void DomainServer::handleICEHostInfo(const QHostInfo& hostInfo) {
// clear the ICE address lookup ID so that it can fire again
_iceAddressLookupID = -1;
_iceAddressLookupID = INVALID_ICE_LOOKUP_ID;
if (hostInfo.error() != QHostInfo::NoError) {
// enumerate the returned addresses and collect only valid IPv4 addresses
QList<QHostAddress> sanitizedAddresses = hostInfo.addresses();
auto it = sanitizedAddresses.begin();
while (it != sanitizedAddresses.end()) {
if (!it->isNull() && it->protocol() == QAbstractSocket::IPv4Protocol) {
++it;
} else {
it = sanitizedAddresses.erase(it);
}
}
if (hostInfo.error() != QHostInfo::NoError || sanitizedAddresses.empty()) {
qWarning() << "IP address lookup failed for" << _iceServerAddr << ":" << hostInfo.errorString();
// if we don't have an ICE server to use yet, trigger a retry
@ -3022,7 +3013,7 @@ void DomainServer::handleICEHostInfo(const QHostInfo& hostInfo) {
} else {
int countBefore = _iceServerAddresses.count();
_iceServerAddresses = hostInfo.addresses();
_iceServerAddresses = sanitizedAddresses;
if (countBefore == 0) {
qInfo() << "Found" << _iceServerAddresses.count() << "ice-server IP addresses for" << _iceServerAddr;

View file

@ -116,8 +116,6 @@ private slots:
void tokenGrantFinished();
void profileRequestFinished();
void timeoutICEAddressLookup();
signals:
void iceServerChanged();
void userConnected();

View file

@ -29,6 +29,7 @@
#include <NLPacketList.h>
#include <NumericalConstants.h>
#include <SettingHandle.h>
#include <SettingHelpers.h>
#include <AvatarData.h> //for KillAvatarReason
#include <FingerprintUtils.h>
#include "DomainServerNodeData.h"
@ -43,12 +44,7 @@ const QString DESCRIPTION_COLUMNS_KEY = "columns";
const QString SETTINGS_VIEWPOINT_KEY = "viewpoint";
static Setting::Handle<double> JSON_SETTING_VERSION("json-settings/version", 0.0);
DomainServerSettingsManager::DomainServerSettingsManager() :
_descriptionArray(),
_configMap()
{
DomainServerSettingsManager::DomainServerSettingsManager() {
// load the description object from the settings description
QFile descriptionFile(QCoreApplication::applicationDirPath() + SETTINGS_DESCRIPTION_RELATIVE_PATH);
descriptionFile.open(QIODevice::ReadOnly);
@ -100,12 +96,34 @@ void DomainServerSettingsManager::processSettingsRequestPacket(QSharedPointer<Re
void DomainServerSettingsManager::setupConfigMap(const QStringList& argumentList) {
_argumentList = argumentList;
// after 1.7 we no longer use the master or merged configs - this is kept in place for migration
_configMap.loadMasterAndUserConfig(_argumentList);
_configMap.loadConfig(_argumentList);
static const auto VERSION_SETTINGS_KEYPATH = "version";
QVariant* versionVariant = _configMap.valueForKeyPath(VERSION_SETTINGS_KEYPATH);
if (!versionVariant) {
versionVariant = _configMap.valueForKeyPath(VERSION_SETTINGS_KEYPATH, true);
*versionVariant = _descriptionVersion;
persistToFile();
qDebug() << "No version in config file, setting to current version" << _descriptionVersion;
}
{
// Backward compatibility migration code
// The config version used to be stored in a different file
// This moves it to the actual config file.
Setting::Handle<double> JSON_SETTING_VERSION("json-settings/version", 0.0);
if (JSON_SETTING_VERSION.isSet()) {
auto version = JSON_SETTING_VERSION.get();
*versionVariant = version;
persistToFile();
QFile::remove(settingsFilename());
}
}
// What settings version were we before and what are we using now?
// Do we need to do any re-mapping?
double oldVersion = JSON_SETTING_VERSION.get();
double oldVersion = versionVariant->toDouble();
if (oldVersion != _descriptionVersion) {
const QString ALLOWED_USERS_SETTINGS_KEYPATH = "security.allowed_users";
@ -137,12 +155,6 @@ void DomainServerSettingsManager::setupConfigMap(const QStringList& argumentList
QVariant* restrictedAccess = _configMap.valueForKeyPath(RESTRICTED_ACCESS_SETTINGS_KEYPATH, true);
*restrictedAccess = QVariant(true);
// write the new settings to the json file
persistToFile();
// reload the master and user config so that the merged config is right
_configMap.loadMasterAndUserConfig(_argumentList);
}
}
@ -172,12 +184,6 @@ void DomainServerSettingsManager::setupConfigMap(const QStringList& argumentList
*entityServerVariant = entityServerMap;
}
// write the new settings to the json file
persistToFile();
// reload the master and user config so that the merged config is right
_configMap.loadMasterAndUserConfig(_argumentList);
}
}
@ -195,12 +201,6 @@ void DomainServerSettingsManager::setupConfigMap(const QStringList& argumentList
qDebug() << "Migrating plaintext password to SHA256 hash in domain-server settings.";
*passwordVariant = QCryptographicHash::hash(plaintextPassword.toUtf8(), QCryptographicHash::Sha256).toHex();
// write the new settings to file
persistToFile();
// reload the master and user config so the merged config is correct
_configMap.loadMasterAndUserConfig(_argumentList);
}
}
@ -283,19 +283,6 @@ void DomainServerSettingsManager::setupConfigMap(const QStringList& argumentList
packPermissions();
}
if (oldVersion < 1.7) {
// This was prior to the removal of the master config file
// So we write the merged config to the user config file, and stop reading from the user config file
qDebug() << "Migrating merged config to user config file. The master config file is deprecated.";
// replace the user config by the merged config
_configMap.getConfig() = _configMap.getMergedConfig();
// persist the new config so the user config file has the correctly merged config
persistToFile();
}
if (oldVersion < 1.8) {
unpackPermissions();
// This was prior to addition of domain content replacement, add that to localhost permissions by default
@ -316,16 +303,16 @@ void DomainServerSettingsManager::setupConfigMap(const QStringList& argumentList
QVariant* wizardCompletedOnce = _configMap.valueForKeyPath(WIZARD_COMPLETED_ONCE, true);
*wizardCompletedOnce = QVariant(true);
// write the new settings to the json file
persistToFile();
}
// write the current description version to our settings
*versionVariant = _descriptionVersion;
// write the new settings to the json file
persistToFile();
}
unpackPermissions();
// write the current description version to our settings
JSON_SETTING_VERSION.set(_descriptionVersion);
}
QVariantMap& DomainServerSettingsManager::getDescriptorsMap() {
@ -1289,9 +1276,6 @@ bool DomainServerSettingsManager::recurseJSONObjectAndOverwriteSettings(const QJ
}
}
// re-merge the user and master configs after a settings change
_configMap.mergeMasterAndUserConfigs();
return needRestart;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1 KiB

View file

@ -1,532 +0,0 @@
//
// AddressBarDialog.qml
//
// Created by Austin Davis on 2015/04/14
// Copyright 2015 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
//
import Hifi 1.0
import QtQuick 2.4
import "controls"
import "styles"
import "windows"
import "hifi"
import "hifi/toolbars"
import "styles-uit" as HifiStyles
import "controls-uit" as HifiControls
Window {
id: root
HifiConstants { id: hifi }
HifiStyles.HifiConstants { id: hifiStyleConstants }
objectName: "AddressBarDialog"
title: "Go To:"
shown: false
destroyOnHidden: false
resizable: false
pinnable: false;
width: addressBarDialog.implicitWidth
height: addressBarDialog.implicitHeight
property int gap: 14
onShownChanged: {
addressBarDialog.keyboardEnabled = HMD.active;
addressBarDialog.observeShownChanged(shown);
}
Component.onCompleted: {
root.parentChanged.connect(center);
center();
}
Component.onDestruction: {
root.parentChanged.disconnect(center);
}
function center() {
// Explicitly center in order to avoid warnings at shutdown
anchors.centerIn = parent;
}
function resetAfterTeleport() {
storyCardFrame.shown = root.shown = false;
}
function goCard(targetString) {
if (0 !== targetString.indexOf('hifi://')) {
storyCardHTML.url = addressBarDialog.metaverseServerUrl + targetString;
storyCardFrame.shown = true;
return;
}
addressLine.text = targetString;
toggleOrGo(true);
clearAddressLineTimer.start();
}
property var allStories: [];
property int cardWidth: 212;
property int cardHeight: 152;
property string metaverseBase: addressBarDialog.metaverseServerUrl + "/api/v1/";
property bool isCursorVisible: false // Override default cursor visibility.
AddressBarDialog {
id: addressBarDialog
property bool keyboardEnabled: false
property bool keyboardRaised: false
property bool punctuationMode: false
implicitWidth: backgroundImage.width
implicitHeight: scroll.height + gap + backgroundImage.height + (keyboardEnabled ? keyboard.height : 0);
// The buttons have their button state changed on hover, so we have to manually fix them up here
onBackEnabledChanged: backArrow.buttonState = addressBarDialog.backEnabled ? 1 : 0;
onForwardEnabledChanged: forwardArrow.buttonState = addressBarDialog.forwardEnabled ? 1 : 0;
onReceivedHifiSchemeURL: resetAfterTeleport();
// Update location after using back and forward buttons.
onHostChanged: updateLocationTextTimer.start();
ListModel { id: suggestions }
ListView {
id: scroll
height: cardHeight + scroll.stackedCardShadowHeight
property int stackedCardShadowHeight: 10;
spacing: gap;
clip: true;
anchors {
left: backgroundImage.left
right: swipe.left
bottom: backgroundImage.top
}
model: suggestions;
orientation: ListView.Horizontal;
delegate: Card {
width: cardWidth;
height: cardHeight;
goFunction: goCard;
userName: model.username;
placeName: model.place_name;
hifiUrl: model.place_name + model.path;
thumbnail: model.thumbnail_url;
imageUrl: model.image_url;
action: model.action;
timestamp: model.created_at;
onlineUsers: model.online_users;
storyId: model.metaverseId;
drillDownToPlace: model.drillDownToPlace;
shadowHeight: scroll.stackedCardShadowHeight;
hoverThunk: function () { ListView.view.currentIndex = index; }
unhoverThunk: function () { ListView.view.currentIndex = -1; }
}
highlightMoveDuration: -1;
highlightMoveVelocity: -1;
highlight: Rectangle { color: "transparent"; border.width: 4; border.color: hifiStyleConstants.colors.blueHighlight; z: 1; }
}
Image { // Just a visual indicator that the user can swipe the cards over to see more.
id: swipe;
source: "../images/swipe-chevron.svg";
width: 72;
visible: suggestions.count > 3;
anchors {
right: backgroundImage.right;
top: scroll.top;
}
MouseArea {
anchors.fill: parent
onClicked: scroll.currentIndex = (scroll.currentIndex < 0) ? 3 : (scroll.currentIndex + 3)
}
}
Row {
spacing: 2 * hifi.layout.spacing;
anchors {
top: parent.top;
left: parent.left;
leftMargin: 150;
topMargin: -30;
}
property var selected: allTab;
TextButton {
id: allTab;
text: "ALL";
property string includeActions: 'snapshot,concurrency';
selected: allTab === selectedTab;
action: tabSelect;
}
TextButton {
id: placeTab;
text: "PLACES";
property string includeActions: 'concurrency';
selected: placeTab === selectedTab;
action: tabSelect;
}
TextButton {
id: snapsTab;
text: "SNAPS";
property string includeActions: 'snapshot';
selected: snapsTab === selectedTab;
action: tabSelect;
}
}
Image {
id: backgroundImage
source: "../images/address-bar-856.svg"
width: 856
height: 100
anchors {
bottom: parent.keyboardEnabled ? keyboard.top : parent.bottom;
}
property int inputAreaHeight: 70
property int inputAreaStep: (height - inputAreaHeight) / 2
ToolbarButton {
id: homeButton
imageURL: "../images/home.svg"
onClicked: {
addressBarDialog.loadHome();
root.shown = false;
}
anchors {
left: parent.left
leftMargin: homeButton.width / 2
verticalCenter: parent.verticalCenter
}
}
ToolbarButton {
id: backArrow;
imageURL: "../images/backward.svg";
onClicked: addressBarDialog.loadBack();
anchors {
left: homeButton.right
verticalCenter: parent.verticalCenter
}
}
ToolbarButton {
id: forwardArrow;
imageURL: "../images/forward.svg";
onClicked: addressBarDialog.loadForward();
anchors {
left: backArrow.right
verticalCenter: parent.verticalCenter
}
}
HifiStyles.RalewayLight {
id: notice;
font.pixelSize: hifi.fonts.pixelSize * 0.50;
anchors {
top: parent.top
topMargin: parent.inputAreaStep + 12
left: addressLine.left
right: addressLine.right
}
}
HifiStyles.FiraSansRegular {
id: location;
font.pixelSize: addressLine.font.pixelSize;
color: "gray";
clip: true;
anchors.fill: addressLine;
visible: addressLine.text.length === 0
}
TextInput {
id: addressLine
focus: true
anchors {
top: parent.top
bottom: parent.bottom
left: forwardArrow.right
right: parent.right
leftMargin: forwardArrow.width
rightMargin: forwardArrow.width / 2
topMargin: parent.inputAreaStep + (2 * hifi.layout.spacing)
bottomMargin: parent.inputAreaStep
}
font.pixelSize: hifi.fonts.pixelSize * 0.75
cursorVisible: false
onTextChanged: {
filterChoicesByText();
updateLocationText(text.length > 0);
if (!isCursorVisible && text.length > 0) {
isCursorVisible = true;
cursorVisible = true;
}
}
onActiveFocusChanged: {
cursorVisible = isCursorVisible && focus;
}
MouseArea {
// If user clicks in address bar show cursor to indicate ability to enter address.
anchors.fill: parent
onClicked: {
isCursorVisible = true;
parent.cursorVisible = true;
parent.forceActiveFocus();
}
}
}
}
Timer {
// Delay updating location text a bit to avoid flicker of content and so that connection status is valid.
id: updateLocationTextTimer
running: false
interval: 500 // ms
repeat: false
onTriggered: updateLocationText(false);
}
Timer {
// Delay clearing address line so as to avoid flicker of "not connected" being displayed after entering an address.
id: clearAddressLineTimer
running: false
interval: 100 // ms
repeat: false
onTriggered: {
addressLine.text = "";
isCursorVisible = false;
}
}
Window {
width: 938
height: 625
HifiControls.WebView {
anchors.fill: parent;
id: storyCardHTML;
}
id: storyCardFrame;
shown: false;
destroyOnCloseButton: false;
pinnable: false;
anchors {
verticalCenter: backgroundImage.verticalCenter;
horizontalCenter: scroll.horizontalCenter;
}
z: 100
}
HifiControls.Keyboard {
id: keyboard
raised: parent.keyboardEnabled // Ignore keyboardRaised; keep keyboard raised if enabled (i.e., in HMD).
numeric: parent.punctuationMode
anchors {
bottom: parent.bottom
left: parent.left
right: parent.right
}
}
}
function getRequest(url, cb) { // cb(error, responseOfCorrectContentType) of url. General for 'get' text/html/json, but without redirects.
// TODO: make available to other .qml.
var request = new XMLHttpRequest();
// QT bug: apparently doesn't handle onload. Workaround using readyState.
request.onreadystatechange = function () {
var READY_STATE_DONE = 4;
var HTTP_OK = 200;
if (request.readyState >= READY_STATE_DONE) {
var error = (request.status !== HTTP_OK) && request.status.toString() + ':' + request.statusText,
response = !error && request.responseText,
contentType = !error && request.getResponseHeader('content-type');
if (!error && contentType.indexOf('application/json') === 0) {
try {
response = JSON.parse(response);
} catch (e) {
error = e;
}
}
cb(error, response);
}
};
request.open("GET", url, true);
request.send();
}
function identity(x) {
return x;
}
function handleError(url, error, data, cb) { // cb(error) and answer truthy if needed, else falsey
if (!error && (data.status === 'success')) {
return;
}
if (!error) { // Create a message from the data
error = data.status + ': ' + data.error;
}
if (typeof(error) === 'string') { // Make a proper Error object
error = new Error(error);
}
error.message += ' in ' + url; // Include the url.
cb(error);
return true;
}
function resolveUrl(url) {
return (url.indexOf('/') === 0) ? (addressBarDialog.metaverseServerUrl + url) : url;
}
function makeModelData(data) { // create a new obj from data
// ListModel elements will only ever have those properties that are defined by the first obj that is added.
// So here we make sure that we have all the properties we need, regardless of whether it is a place data or user story.
var name = data.place_name,
tags = data.tags || [data.action, data.username],
description = data.description || "",
thumbnail_url = data.thumbnail_url || "";
return {
place_name: name,
username: data.username || "",
path: data.path || "",
created_at: data.created_at || "",
action: data.action || "",
thumbnail_url: resolveUrl(thumbnail_url),
image_url: resolveUrl(data.details.image_url),
metaverseId: (data.id || "").toString(), // Some are strings from server while others are numbers. Model objects require uniformity.
tags: tags,
description: description,
online_users: data.details.concurrency || 0,
drillDownToPlace: false,
searchText: [name].concat(tags, description || []).join(' ').toUpperCase()
}
}
function suggestable(place) {
if (place.action === 'snapshot') {
return true;
}
return (place.place_name !== AddressManager.placename); // Not our entry, but do show other entry points to current domain.
}
property var selectedTab: allTab;
function tabSelect(textButton) {
selectedTab = textButton;
fillDestinations();
}
property var placeMap: ({});
function addToSuggestions(place) {
var collapse = allTab.selected && (place.action !== 'concurrency');
if (collapse) {
var existing = placeMap[place.place_name];
if (existing) {
existing.drillDownToPlace = true;
return;
}
}
suggestions.append(place);
if (collapse) {
placeMap[place.place_name] = suggestions.get(suggestions.count - 1);
} else if (place.action === 'concurrency') {
suggestions.get(suggestions.count - 1).drillDownToPlace = true; // Don't change raw place object (in allStories).
}
}
property int requestId: 0;
function getUserStoryPage(pageNumber, cb) { // cb(error) after all pages of domain data have been added to model
var options = [
'now=' + new Date().toISOString(),
'include_actions=' + selectedTab.includeActions,
'restriction=' + (Account.isLoggedIn() ? 'open,hifi' : 'open'),
'require_online=true',
'protocol=' + encodeURIComponent(AddressManager.protocolVersion()),
'page=' + pageNumber
];
var url = metaverseBase + 'user_stories?' + options.join('&');
var thisRequestId = ++requestId;
getRequest(url, function (error, data) {
if ((thisRequestId !== requestId) || handleError(url, error, data, cb)) {
return;
}
var stories = data.user_stories.map(function (story) { // explicit single-argument function
return makeModelData(story, url);
});
allStories = allStories.concat(stories);
stories.forEach(makeFilteredPlaceProcessor());
if ((data.current_page < data.total_pages) && (data.current_page <= 10)) { // just 10 pages = 100 stories for now
return getUserStoryPage(pageNumber + 1, cb);
}
cb();
});
}
function makeFilteredPlaceProcessor() { // answer a function(placeData) that adds it to suggestions if it matches
var words = addressLine.text.toUpperCase().split(/\s+/).filter(identity),
data = allStories;
function matches(place) {
if (!words.length) {
return suggestable(place);
}
return words.every(function (word) {
return place.searchText.indexOf(word) >= 0;
});
}
return function (place) {
if (matches(place)) {
addToSuggestions(place);
}
};
}
function filterChoicesByText() {
suggestions.clear();
placeMap = {};
allStories.forEach(makeFilteredPlaceProcessor());
}
function fillDestinations() {
allStories = [];
suggestions.clear();
placeMap = {};
getUserStoryPage(1, function (error) {
console.log('user stories query', error || 'ok', allStories.length);
});
}
function updateLocationText(enteringAddress) {
if (enteringAddress) {
notice.text = "Go to a place, @user, path or network address";
notice.color = hifiStyleConstants.colors.baseGrayHighlight;
} else {
notice.text = AddressManager.isConnected ? "Your location:" : "Not Connected";
notice.color = AddressManager.isConnected ? hifiStyleConstants.colors.baseGrayHighlight : hifiStyleConstants.colors.redHighlight;
// Display hostname, which includes ip address, localhost, and other non-placenames.
location.text = (AddressManager.placename || AddressManager.hostname || '') + (AddressManager.pathname ? AddressManager.pathname.match(/\/[^\/]+/)[0] : '');
}
}
onVisibleChanged: {
updateLocationText(false);
if (visible) {
addressLine.forceActiveFocus();
fillDestinations();
}
}
function toggleOrGo(fromSuggestions) {
if (addressLine.text !== "") {
addressBarDialog.loadAddress(addressLine.text, fromSuggestions)
}
root.shown = false;
}
Keys.onPressed: {
switch (event.key) {
case Qt.Key_Escape:
case Qt.Key_Back:
root.shown = false
clearAddressLineTimer.start();
event.accepted = true
break
case Qt.Key_Enter:
case Qt.Key_Return:
toggleOrGo()
clearAddressLineTimer.start();
event.accepted = true
break
}
}
}

View file

@ -1,7 +1,7 @@
import QtQuick 2.5
import QtQuick.Controls 1.2
import QtWebChannel 1.0
import QtWebEngine 1.2
import QtWebEngine 1.5
import "controls-uit"
import "styles" as HifiStyles

View file

@ -8,7 +8,7 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
import QtQuick 2.5
import QtQuick 2.7
import QtWebEngine 1.5
WebEngineView {

View file

@ -5,10 +5,16 @@ Item {
id: keyItem
width: 45
height: 50
property int contentPadding: 4
property string glyph: "a"
property bool toggle: false // does this button have the toggle behaivor?
property bool toggled: false // is this button currently toggled?
property alias mouseArea: mouseArea1
property alias fontFamily: letter.font.family;
property alias fontPixelSize: letter.font.pixelSize
property alias verticalAlignment: letter.verticalAlignment
property alias letterAnchors: letter.anchors
function resetToggledMode(mode) {
toggled = mode;
@ -105,14 +111,8 @@ Item {
color: "#121212"
radius: 2
border.color: "#00000000"
anchors.right: parent.right
anchors.rightMargin: 4
anchors.left: parent.left
anchors.leftMargin: 4
anchors.bottom: parent.bottom
anchors.bottomMargin: 4
anchors.top: parent.top
anchors.topMargin: 4
anchors.fill: parent
anchors.margins: contentPadding
}
Text {

View file

@ -8,7 +8,8 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
import QtQuick 2.0
import QtQuick 2.7
import QtGraphicalEffects 1.0
import "."
Rectangle {
@ -55,6 +56,8 @@ Rectangle {
return ">";
} else if (str === "/") {
return "?";
} else if (str === "-") {
return "_";
} else {
return str.toUpperCase(str);
}
@ -67,6 +70,8 @@ Rectangle {
return ".";
} else if (str === "?") {
return "/";
} else if (str === "_") {
return "-";
} else {
return str.toLowerCase(str);
}
@ -85,7 +90,7 @@ Rectangle {
onShiftModeChanged: {
forEachKey(function (key) {
if (/[a-z]/i.test(key.glyph)) {
if (/[a-z-_]/i.test(key.glyph)) {
if (shiftMode) {
key.glyph = keyboardBase.toUpper(key.glyph);
} else {
@ -112,8 +117,6 @@ Rectangle {
}
Rectangle {
y: 0
x: 0
height: showMirrorText ? mirrorTextHeight : 0
width: keyboardWidth
color: "#252525"
@ -122,13 +125,18 @@ Rectangle {
TextInput {
id: mirrorText
visible: showMirrorText
FontLoader { id: ralewaySemiBold; source: "../../fonts/Raleway-SemiBold.ttf"; }
font.family: ralewaySemiBold.name
font.pointSize: 13.5
FontLoader { id: font; source: "../../fonts/FiraSans-Regular.ttf"; }
font.family: font.name
font.pixelSize: 20
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
color: "#FFFFFF";
anchors.fill: parent
color: "#00B4EF";
anchors.left: parent.left
anchors.leftMargin: 10
anchors.right: parent.right
anchors.top: parent.top
anchors.bottom: parent.bottom
wrapMode: Text.WordWrap
readOnly: false // we need this to allow control to accept QKeyEvent
selectByMouse: false
@ -140,16 +148,15 @@ Rectangle {
event.accepted = true;
}
}
}
MouseArea { // ... and we need this mouse area to prevent mirrorText from getting mouse events to ensure it will never get focus
anchors.fill: parent
MouseArea { // ... and we need this mouse area to prevent mirrorText from getting mouse events to ensure it will never get focus
anchors.fill: parent
}
}
}
Rectangle {
id: keyboardRect
x: 0
y: showMirrorText ? mirrorTextHeight : 0
width: keyboardWidth
height: raisedHeight
@ -158,6 +165,8 @@ Rectangle {
anchors.bottom: parent.bottom
anchors.bottomMargin: 0
FontLoader { id: hiFiGlyphs; source: pathToFonts + "fonts/hifi-glyphs.ttf"; }
Column {
id: columnAlpha
width: keyboardWidth
@ -221,7 +230,7 @@ Rectangle {
Key { width: 43; glyph: "b"; }
Key { width: 43; glyph: "n"; }
Key { width: 43; glyph: "m"; }
Key { width: 43; glyph: "_"; }
Key { width: 43; glyph: "-"; }
Key { width: 43; glyph: "/"; }
Key { width: 43; glyph: "?"; }
}
@ -240,8 +249,13 @@ Rectangle {
Key { width: 231; glyph: " "; }
Key { width: 43; glyph: ","; }
Key { width: 43; glyph: "."; }
Key { width: 43; glyph: "\u276C"; }
Key { width: 43; glyph: "\u276D"; }
Key {
fontFamily: hiFiGlyphs.name;
fontPixelSize: 48;
letterAnchors.topMargin: -4;
verticalAlignment: Text.AlignVCenter;
width: 86; glyph: "\ue02b";
}
}
}
@ -328,8 +342,13 @@ Rectangle {
Key { width: 231; glyph: " "; }
Key { width: 43; glyph: ","; }
Key { width: 43; glyph: "."; }
Key { width: 43; glyph: "\u276C"; }
Key { width: 43; glyph: "\u276D"; }
Key {
fontFamily: hiFiGlyphs.name;
fontPixelSize: 48;
letterAnchors.topMargin: -4;
verticalAlignment: Text.AlignVCenter;
width: 86; glyph: "\ue02b";
}
}
}
}

View file

@ -27,6 +27,12 @@ Item {
id: hifi
}
function unfocus() {
webViewCore.runJavaScript("if (document.activeElement) document.activeElement.blur();", function(result) {
console.log('unfocus completed: ', result);
});
}
function onLoadingChanged(loadRequest) {
if (WebEngineView.LoadStartedStatus === loadRequest.status) {
@ -129,4 +135,10 @@ Item {
playing: visible
z: 10000
}
Keys.onPressed: {
if ((event.modifiers & Qt.ShiftModifier) && (event.modifiers & Qt.ControlModifier)) {
webViewCore.focus = false;
}
}
}

View file

@ -10,6 +10,11 @@ Item {
property alias urlTag: webroot.urlTag
property bool keyboardEnabled: true // FIXME - Keyboard HMD only: Default to false
property bool keyboardRaised: false
onKeyboardRaisedChanged: {
if(!keyboardRaised) {
webroot.unfocus();
}
}
property bool punctuationMode: false
// FIXME - Keyboard HMD only: Make Interface either set keyboardRaised property directly in OffscreenQmlSurface

View file

@ -15,6 +15,11 @@ Item {
property string scriptURL
property bool keyboardEnabled: false
property bool keyboardRaised: false
onKeyboardRaisedChanged: {
if(!keyboardRaised) {
webroot.unfocus();
}
}
property bool punctuationMode: false
property bool passwordField: false
property bool isDesktop: false

View file

@ -12,6 +12,11 @@ Item {
property alias urlTag: webroot.urlTag
property bool keyboardEnabled: true // FIXME - Keyboard HMD only: Default to false
property bool keyboardRaised: false
onKeyboardRaisedChanged: {
if(!keyboardRaised) {
webroot.unfocus();
}
}
property bool punctuationMode: false
property bool passwordField: false
property alias flickable: webroot.interactive

View file

@ -51,19 +51,23 @@ FocusScope {
// The VR version of the primary menu
property var rootMenu: Menu {
id: rootMenuId
objectName: "rootMenu"
// for some reasons it is not possible to use just '({})' here as it gets empty when passed to TableRoot/DesktopRoot
property var exclusionGroupsByMenuItem : ListModel {}
property var exclusionGroups: ({});
property Component exclusiveGroupMaker: Component {
ExclusiveGroup {
}
}
function addExclusionGroup(menuItem, exclusionGroup)
{
exclusionGroupsByMenuItem.append(
{
'menuItem' : menuItem.toString(),
'exclusionGroup' : exclusionGroup.toString()
}
);
function addExclusionGroup(qmlAction, exclusionGroup) {
var exclusionGroupId = exclusionGroup.toString();
if(!exclusionGroups[exclusionGroupId]) {
exclusionGroups[exclusionGroupId] = exclusiveGroupMaker.createObject(rootMenuId);
}
qmlAction.exclusiveGroup = exclusionGroups[exclusionGroupId]
}
}

View file

@ -16,10 +16,10 @@ import Qt.labs.settings 1.0
import "../styles-uit"
import "../controls-uit" as HifiControls
import "../windows"
import "../windows" as Windows
import "../dialogs"
ScrollingWindow {
Windows.ScrollingWindow {
id: root
objectName: "AssetServer"
title: "Asset Browser"

View file

@ -1,6 +1,6 @@
import QtQuick 2.5
import QtQuick.Controls 1.4
import QtWebEngine 1.1;
import QtWebEngine 1.5;
import Qt.labs.settings 1.0
import "../desktop" as OriginalDesktop

View file

@ -432,7 +432,8 @@ Item {
anchors.verticalCenter: nameCardRemoveConnectionImage.verticalCenter
x: 240
onClicked: {
AddressManager.goToUser(thisNameCard.userName);
console.log("Vist user button clicked."); // Remove after debugging.
AddressManager.goToUser(thisNameCard.userName, false);
UserActivityLogger.palAction("go_to_user", thisNameCard.userName);
}
}
@ -441,7 +442,7 @@ Item {
Rectangle {
id: nameCardVUMeter
// Size
width: isMyCard ? myDisplayName.width - 20 : ((gainSlider.value - gainSlider.minimumValue)/(gainSlider.maximumValue - gainSlider.minimumValue)) * (gainSlider.width);
width: ((gainSlider.value - gainSlider.minimumValue)/(gainSlider.maximumValue - gainSlider.minimumValue)) * (gainSlider.width);
height: 8
// Anchors
anchors.bottom: isMyCard ? avatarImage.bottom : parent.bottom;
@ -525,16 +526,14 @@ Item {
anchors.verticalCenter: nameCardVUMeter.verticalCenter;
anchors.left: nameCardVUMeter.left;
// Properties
visible: !isMyCard && selected && pal.activeTab == "nearbyTab" && isPresent;
visible: (isMyCard || (selected && pal.activeTab == "nearbyTab")) && isPresent;
value: Users.getAvatarGain(uuid)
minimumValue: -60.0
maximumValue: 20.0
stepSize: 5
updateValueWhileDragging: true
onValueChanged: {
if (uuid !== "") {
updateGainFromQML(uuid, value, false);
}
updateGainFromQML(uuid, value, false);
}
onPressedChanged: {
if (!pressed) {
@ -574,7 +573,19 @@ Item {
implicitHeight: 16
}
}
}
RalewayRegular {
// The slider for my card is special, it controls the master gain
id: gainSliderText;
visible: isMyCard;
text: "master volume";
size: hifi.fontSizes.tabularData;
anchors.left: parent.right;
anchors.leftMargin: 8;
color: hifi.colors.baseGrayHighlight;
horizontalAlignment: Text.AlignLeft;
verticalAlignment: Text.AlignTop;
}
}
function updateGainFromQML(avatarUuid, sliderValue, isReleased) {
Users.setAvatarGain(avatarUuid, sliderValue);
@ -594,7 +605,10 @@ Item {
// the avatar goes into fly mode rather than falling. However, that is not exposed to Javascript right now.
// FIXME: it would be nice if this used the same teleport steps and smoothing as in the teleport.js script.
// Note, however, that this script allows teleporting to a person in the air, while teleport.js is going to a grounded target.
// Position avatar 2 metres from the target in the direction that target avatar was facing.
MyAvatar.position = Vec3.sum(avatar.position, Vec3.multiplyQbyV(avatar.orientation, {x: 0, y: 0, z: -2}));
MyAvatar.orientation = Quat.multiply(avatar.orientation, {y: 1});
// Rotate avatar on Y axis to face target avatar and cancel out any inherited roll and pitch.
MyAvatar.orientation = Quat.cancelOutRollAndPitch(Quat.multiply(avatar.orientation, {y: 1}));
}
}

View file

@ -827,7 +827,7 @@ Rectangle {
hoverEnabled: enabled
enabled: connectionsNameCard.selected && pal.activeTab == "connectionsTab"
onClicked: {
AddressManager.goToUser(model.userName);
AddressManager.goToUser(model.userName, false);
UserActivityLogger.palAction("go_to_user", model.userName);
}
onEntered: connectionsLocationData.color = hifi.colors.blueHighlight;

View file

@ -1,3 +1,4 @@
//
// WebBrowser.qml
//
@ -9,12 +10,12 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
import QtQuick 2.5
import QtQuick.Controls 1.5 as QQControls
import QtQuick 2.7
import QtQuick.Controls 2.2 as QQControls
import QtQuick.Layouts 1.3
import QtQuick.Controls.Styles 1.4
import QtGraphicalEffects 1.0
import QtWebEngine 1.2
import QtWebEngine 1.5
import QtWebChannel 1.0
import "../styles-uit"
@ -22,6 +23,8 @@ import "../controls-uit" as HifiControls
import "../windows"
import "../controls"
import HifiWeb 1.0
Rectangle {
id: root;
@ -32,214 +35,401 @@ Rectangle {
property bool keyboardEnabled: true // FIXME - Keyboard HMD only: Default to false
property bool keyboardRaised: false
property bool punctuationMode: false
property var suggestionsList: []
readonly property string searchUrlTemplate: "https://www.google.com/search?client=hifibrowser&q=";
WebBrowserSuggestionsEngine {
id: searchEngine
onSuggestions: {
if (suggestions.length > 0) {
suggestionsList = []
suggestionsList.push(addressBarInput.text); //do not overwrite edit text
for(var i = 0; i < suggestions.length; i++) {
suggestionsList.push(suggestions[i]);
}
addressBar.model = suggestionsList
if (!addressBar.popup.visible) {
addressBar.popup.open();
}
}
}
}
Timer {
id: suggestionRequestTimer
interval: 200
repeat: false
onTriggered: {
if (addressBar.editText !== "") {
searchEngine.querySuggestions(addressBarInput.text);
}
}
}
color: hifi.colors.baseGray;
// only show the title if loaded through a "loader"
function goTo(url) {
//must be valid attempt to open an site with dot
var urlNew = url
if (url.indexOf(".") > 0) {
if (url.indexOf("http") < 0) {
urlNew = "http://" + url;
}
} else {
urlNew = searchUrlTemplate + url
}
addressBar.model = []
//need to rebind if binfing was broken by selecting from suggestions
addressBar.editText = Qt.binding( function() { return webStack.currentItem.webEngineView.url; });
webStack.currentItem.webEngineView.url = urlNew
suggestionRequestTimer.stop();
addressBar.popup.close();
}
Column {
spacing: 2
width: parent.width;
RowLayout {
id: addressBarRow
width: parent.width;
height: 48
HifiControls.WebGlyphButton {
enabled: webEngineView.canGoBack
enabled: webStack.currentItem.webEngineView.canGoBack || webStack.depth > 1
glyph: hifi.glyphs.backward;
anchors.verticalCenter: parent.verticalCenter;
size: 38;
onClicked: {
webEngineView.goBack()
if (webStack.currentItem.webEngineView.canGoBack) {
webStack.currentItem.webEngineView.goBack();
} else if (webStack.depth > 1) {
webStack.pop();
}
}
}
HifiControls.WebGlyphButton {
enabled: webEngineView.canGoForward
enabled: webStack.currentItem.webEngineView.canGoForward
glyph: hifi.glyphs.forward;
anchors.verticalCenter: parent.verticalCenter;
size: 38;
onClicked: {
webEngineView.goForward()
webStack.currentItem.webEngineView.goForward();
}
}
QQControls.TextField {
QQControls.ComboBox {
id: addressBar
Image {
anchors.verticalCenter: addressBar.verticalCenter;
x: 5
z: 2
id: faviconImage
width: 16; height: 16
sourceSize: Qt.size(width, height)
source: webEngineView.icon
//selectByMouse: true
focus: true
editable: true
//flat: true
indicator: Item {}
background: Item {}
onActivated: {
goTo(textAt(index));
}
HifiControls.WebGlyphButton {
glyph: webEngineView.loading ? hifi.glyphs.closeSmall : hifi.glyphs.reloadSmall;
anchors.verticalCenter: parent.verticalCenter;
width: hifi.dimensions.controlLineHeight
z: 2
x: addressBar.width - 28
onClicked: {
if (webEngineView.loading) {
webEngineView.stop()
} else {
reloadTimer.start()
onHighlightedIndexChanged: {
if (highlightedIndex >= 0) {
addressBar.editText = textAt(highlightedIndex)
}
}
popup.height: webStack.height
onFocusChanged: {
if (focus) {
addressBarInput.selectAll();
}
}
contentItem: QQControls.TextField {
id: addressBarInput
leftPadding: 26
rightPadding: hifi.dimensions.controlLineHeight + 5
text: addressBar.editText
placeholderText: qsTr("Enter URL")
font: addressBar.font
selectByMouse: true
horizontalAlignment: Text.AlignLeft
verticalAlignment: Text.AlignVCenter
onFocusChanged: {
if (focus) {
selectAll();
}
}
Keys.onDeletePressed: {
addressBarInput.text = ""
}
Keys.onPressed: {
if (event.key === Qt.Key_Return) {
goTo(addressBarInput.text);
event.accepted = true;
}
}
Image {
anchors.verticalCenter: parent.verticalCenter;
x: 5
z: 2
id: faviconImage
width: 16; height: 16
sourceSize: Qt.size(width, height)
source: webStack.currentItem.webEngineView.icon
}
HifiControls.WebGlyphButton {
glyph: webStack.currentItem.webEngineView.loading ? hifi.glyphs.closeSmall : hifi.glyphs.reloadSmall;
anchors.verticalCenter: parent.verticalCenter;
width: hifi.dimensions.controlLineHeight
z: 2
x: addressBarInput.width - implicitWidth
onClicked: {
if (webStack.currentItem.webEngineView.loading) {
webStack.currentItem.webEngineView.stop();
} else {
webStack.currentItem.reloadTimer.start();
}
}
}
}
style: TextFieldStyle {
padding {
left: 26;
right: 26
Component.onCompleted: ScriptDiscoveryService.scriptsModelFilter.filterRegExp = new RegExp("^.*$", "i");
Keys.onPressed: {
if (event.key === Qt.Key_Return) {
goTo(addressBarInput.text);
event.accepted = true;
}
}
focus: true
onEditTextChanged: {
if (addressBar.editText !== "" && addressBar.editText !== webStack.currentItem.webEngineView.url.toString()) {
suggestionRequestTimer.restart();
} else {
addressBar.model = []
addressBar.popup.close();
}
}
Layout.fillWidth: true
text: webEngineView.url
onAccepted: webEngineView.url = text
editText: webStack.currentItem.webEngineView.url
onAccepted: goTo(addressBarInput.text);
}
HifiControls.WebGlyphButton {
checkable: true
//only QtWebEngine 1.3
//checked: webEngineView.audioMuted
checked: webStack.currentItem.webEngineView.audioMuted
glyph: checked ? hifi.glyphs.unmuted : hifi.glyphs.muted
anchors.verticalCenter: parent.verticalCenter;
width: hifi.dimensions.controlLineHeight
onClicked: {
webEngineView.triggerWebAction(WebEngineView.ToggleMediaMute)
webStack.currentItem.webEngineView.audioMuted = !webStack.currentItem.webEngineView.audioMuted
}
}
}
QQControls.ProgressBar {
id: loadProgressBar
style: ProgressBarStyle {
background: Rectangle {
color: "#6A6A6A"
}
progress: Rectangle{
background: Rectangle {
implicitHeight: 2
color: "#6A6A6A"
}
contentItem: Item {
implicitHeight: 2
Rectangle {
width: loadProgressBar.visualPosition * parent.width
height: parent.height
color: "#00B4EF"
}
}
width: parent.width;
minimumValue: 0
maximumValue: 100
value: webEngineView.loadProgress
from: 0
to: 100
value: webStack.currentItem.webEngineView.loadProgress
height: 2
}
HifiControls.BaseWebView {
id: webEngineView
focus: true
objectName: "tabletWebEngineView"
Component {
id: webViewComponent
Rectangle {
property alias webEngineView: webEngineView
property alias reloadTimer: reloadTimer
url: "http://www.highfidelity.com"
property real webViewHeight: root.height - loadProgressBar.height - 48 - 4
property WebEngineNewViewRequest request: null
width: parent.width;
height: keyboardEnabled && keyboardRaised ? webViewHeight - keyboard.height : webViewHeight
property bool isDialog: QQControls.StackView.index > 0
property real margins: isDialog ? 10 : 0
profile: HFWebEngineProfile;
color: "#d1d1d1"
property string userScriptUrl: ""
// creates a global EventBridge object.
WebEngineScript {
id: createGlobalEventBridge
sourceCode: eventBridgeJavaScriptToInject
injectionPoint: WebEngineScript.DocumentCreation
worldId: WebEngineScript.MainWorld
}
// detects when to raise and lower virtual keyboard
WebEngineScript {
id: raiseAndLowerKeyboard
injectionPoint: WebEngineScript.Deferred
sourceUrl: resourceDirectoryUrl + "/html/raiseAndLowerKeyboard.js"
worldId: WebEngineScript.MainWorld
}
// User script.
WebEngineScript {
id: userScript
sourceUrl: webEngineView.userScriptUrl
injectionPoint: WebEngineScript.DocumentReady // DOM ready but page load may not be finished.
worldId: WebEngineScript.MainWorld
}
userScripts: [ createGlobalEventBridge, raiseAndLowerKeyboard, userScript ]
settings.autoLoadImages: true
settings.javascriptEnabled: true
settings.errorPageEnabled: true
settings.pluginsEnabled: true
settings.fullScreenSupportEnabled: false
//from WebEngine 1.3
// settings.autoLoadIconsForPage: false
// settings.touchIconsEnabled: false
onCertificateError: {
error.defer();
}
Component.onCompleted: {
webChannel.registerObject("eventBridge", eventBridge);
webChannel.registerObject("eventBridgeWrapper", eventBridgeWrapper);
webEngineView.profile.httpUserAgent = "Mozilla/5.0 Chrome (HighFidelityInterface)";
}
onFeaturePermissionRequested: {
grantFeaturePermission(securityOrigin, feature, true);
}
onNewViewRequested: {
if (!request.userInitiated) {
print("Warning: Blocked a popup window.");
}
}
onRenderProcessTerminated: {
var status = "";
switch (terminationStatus) {
case WebEngineView.NormalTerminationStatus:
status = "(normal exit)";
break;
case WebEngineView.AbnormalTerminationStatus:
status = "(abnormal exit)";
break;
case WebEngineView.CrashedTerminationStatus:
status = "(crashed)";
break;
case WebEngineView.KilledTerminationStatus:
status = "(killed)";
break;
QQControls.StackView.onActivated: {
addressBar.editText = Qt.binding( function() { return webStack.currentItem.webEngineView.url; });
}
print("Render process exited with code " + exitCode + " " + status);
reloadTimer.running = true;
}
onRequestChanged: {
if (isDialog && request !== null && request !== undefined) {//is Dialog ?
request.openIn(webEngineView);
}
}
onWindowCloseRequested: {
}
HifiControls.BaseWebView {
id: webEngineView
anchors.fill: parent
anchors.margins: parent.margins
Timer {
id: reloadTimer
interval: 0
running: false
repeat: false
onTriggered: webEngineView.reload()
layer.enabled: parent.isDialog
layer.effect: DropShadow {
verticalOffset: 8
horizontalOffset: 8
color: "#330066ff"
samples: 10
spread: 0.5
}
focus: true
objectName: "tabletWebEngineView"
//profile: HFWebEngineProfile;
profile.httpUserAgent: "Mozilla/5.0 (Android; Mobile; rv:13.0) Gecko/13.0 Firefox/13.0"
property string userScriptUrl: ""
onLoadingChanged: {
if (!loading) {
addressBarInput.cursorPosition = 0 //set input field cursot to beginning
suggestionRequestTimer.stop();
addressBar.popup.close();
}
}
onLinkHovered: {
//TODO: change cursor shape?
}
// creates a global EventBridge object.
WebEngineScript {
id: createGlobalEventBridge
sourceCode: eventBridgeJavaScriptToInject
injectionPoint: WebEngineScript.Deferred
worldId: WebEngineScript.MainWorld
}
// detects when to raise and lower virtual keyboard
WebEngineScript {
id: raiseAndLowerKeyboard
injectionPoint: WebEngineScript.Deferred
sourceUrl: resourceDirectoryUrl + "/html/raiseAndLowerKeyboard.js"
worldId: WebEngineScript.MainWorld
}
// User script.
WebEngineScript {
id: userScript
sourceUrl: webEngineView.userScriptUrl
injectionPoint: WebEngineScript.DocumentReady // DOM ready but page load may not be finished.
worldId: WebEngineScript.MainWorld
}
userScripts: [ createGlobalEventBridge, raiseAndLowerKeyboard, userScript ]
settings.autoLoadImages: true
settings.javascriptEnabled: true
settings.errorPageEnabled: true
settings.pluginsEnabled: true
settings.fullScreenSupportEnabled: true
settings.autoLoadIconsForPage: true
settings.touchIconsEnabled: true
onCertificateError: {
error.defer();
}
Component.onCompleted: {
webChannel.registerObject("eventBridge", eventBridge);
webChannel.registerObject("eventBridgeWrapper", eventBridgeWrapper);
}
onFeaturePermissionRequested: {
grantFeaturePermission(securityOrigin, feature, true);
}
onNewViewRequested: {
if (request.destination == WebEngineView.NewViewInDialog) {
webStack.push(webViewComponent, {"request": request});
} else {
request.openIn(webEngineView);
}
}
onRenderProcessTerminated: {
var status = "";
switch (terminationStatus) {
case WebEngineView.NormalTerminationStatus:
status = "(normal exit)";
break;
case WebEngineView.AbnormalTerminationStatus:
status = "(abnormal exit)";
break;
case WebEngineView.CrashedTerminationStatus:
status = "(crashed)";
break;
case WebEngineView.KilledTerminationStatus:
status = "(killed)";
break;
}
console.error("Render process exited with code " + exitCode + " " + status);
reloadTimer.running = true;
}
onFullScreenRequested: {
if (request.toggleOn) {
webEngineView.state = "FullScreen";
} else {
webEngineView.state = "";
}
request.accept();
}
onWindowCloseRequested: {
webStack.pop();
}
}
Timer {
id: reloadTimer
interval: 0
running: false
repeat: false
onTriggered: webEngineView.reload()
}
}
}
QQControls.StackView {
id: webStack
width: parent.width;
property real webViewHeight: root.height - loadProgressBar.height - 48 - 4
height: keyboardEnabled && keyboardRaised ? webViewHeight - keyboard.height : webViewHeight
Component.onCompleted: webStack.push(webViewComponent, {"webEngineView.url": "https://www.highfidelity.com"});
}
}
HifiControls.Keyboard {
id: keyboard
raised: parent.keyboardEnabled && parent.keyboardRaised

View file

@ -213,8 +213,8 @@ Rectangle {
anchors.right: parent.right
peak: model.peak;
anchors.verticalCenter: parent.verticalCenter
visible: (bar.currentIndex === 1 && selectedHMD && isVR) ||
(bar.currentIndex === 0 && selectedDesktop && !isVR) &&
visible: ((bar.currentIndex === 1 && isVR) ||
(bar.currentIndex === 0 && !isVR)) &&
Audio.devices.input.peakValuesAvailable;
}
}

View file

@ -596,9 +596,7 @@ Rectangle {
anchors.right: parent.right;
text: root.isWearable ? "Wear It" : "Rez It"
onClicked: {
if (urlHandler.canHandleUrl(root.itemHref)) {
urlHandler.handleUrl(root.itemHref);
}
sendToScript({method: 'checkout_rezClicked', itemHref: root.itemHref, isWearable: root.isWearable});
rezzedNotifContainer.visible = true;
rezzedNotifContainerTimer.start();
}

View file

@ -33,6 +33,7 @@ Rectangle {
property string dateOfPurchase: "--";
property bool isLightbox: false;
property bool isMyCert: false;
property bool isCertificateInvalid: false;
// Style
color: hifi.colors.faintGray;
Hifi.QmlCommerce {
@ -44,10 +45,11 @@ Rectangle {
} else {
root.marketplaceUrl = result.data.marketplace_item_url;
root.isMyCert = result.isMyCert ? result.isMyCert : false;
root.itemOwner = root.isMyCert ? Account.username :
"\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022";
root.itemEdition = result.data.edition_number + "/" + (result.data.limited_run === -1 ? "\u221e" : result.data.limited_run);
root.dateOfPurchase = getFormattedDate(result.data.transfer_created_at * 1000);
root.itemOwner = root.isCertificateInvalid ? "--" : (root.isMyCert ? Account.username :
"\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022");
root.itemEdition = root.isCertificateInvalid ? "Uncertified Copy" :
(result.data.edition_number + "/" + (result.data.limited_run === -1 ? "\u221e" : result.data.limited_run));
root.dateOfPurchase = root.isCertificateInvalid ? "" : getFormattedDate(result.data.transfer_created_at * 1000);
root.itemName = result.data.marketplace_item_name;
if (result.data.invalid_reason || result.data.transfer_status[0] === "failed") {
@ -65,6 +67,44 @@ Rectangle {
}
}
}
onUpdateCertificateStatus: {
if (certStatus === 1) { // CERTIFICATE_STATUS_VERIFICATION_SUCCESS
// NOP
} else if (certStatus === 2) { // CERTIFICATE_STATUS_VERIFICATION_TIMEOUT
root.isCertificateInvalid = true;
errorText.text = "Verification of this certificate timed out.";
errorText.color = hifi.colors.redHighlight;
} else if (certStatus === 3) { // CERTIFICATE_STATUS_STATIC_VERIFICATION_FAILED
root.isCertificateInvalid = true;
titleBarText.text = "Invalid Certificate";
titleBarText.color = hifi.colors.redHighlight;
popText.text = "";
root.itemOwner = "";
dateOfPurchaseHeader.text = "";
root.dateOfPurchase = "";
root.itemEdition = "Uncertified Copy";
errorText.text = "The information associated with this item has been modified and it no longer matches the original certified item.";
errorText.color = hifi.colors.baseGray;
} else if (certStatus === 4) { // CERTIFICATE_STATUS_OWNER_VERIFICATION_FAILED
root.isCertificateInvalid = true;
titleBarText.text = "Invalid Certificate";
titleBarText.color = hifi.colors.redHighlight;
popText.text = "";
root.itemOwner = "";
dateOfPurchaseHeader.text = "";
root.dateOfPurchase = "";
root.itemEdition = "Uncertified Copy";
errorText.text = "The avatar who rezzed this item doesn't own it.";
errorText.color = hifi.colors.baseGray;
} else {
console.log("Unknown certificate status received from ledger signal!");
}
}
}
onCertificateIdChanged: {
@ -73,21 +113,6 @@ Rectangle {
}
}
onVisibleChanged: {
if (!visible) {
titleBarText.text = "Certificate";
popText.text = "PROOF OF PURCHASE";
root.certificateId = "";
root.itemName = "--";
root.itemOwner = "--";
root.itemEdition = "--";
root.dateOfPurchase = "--";
root.marketplaceUrl = "";
root.isMyCert = false;
errorText.text = "";
}
}
// This object is always used in a popup.
// This MouseArea is used to prevent a user from being
// able to click on a button/mouseArea underneath the popup.
@ -216,7 +241,7 @@ Rectangle {
}
AnonymousProRegular {
id: isMyCertText;
visible: root.isMyCert;
visible: root.isMyCert && !root.isCertificateInvalid;
text: "(Private)";
size: 18;
// Anchors
@ -380,6 +405,18 @@ Rectangle {
case 'inspectionCertificate_setCertificateId':
root.certificateId = message.certificateId;
break;
case 'inspectionCertificate_resetCert':
titleBarText.text = "Certificate";
popText.text = "PROOF OF PURCHASE";
root.certificateId = "";
root.itemName = "--";
root.itemOwner = "--";
root.itemEdition = "--";
root.dateOfPurchase = "--";
root.marketplaceUrl = "";
root.isMyCert = false;
errorText.text = "";
break;
default:
console.log('Unrecognized message from marketplaces.js:', JSON.stringify(message));
}

View file

@ -346,9 +346,7 @@ Item {
enabled: (root.canRezCertifiedItems || root.isWearable) && root.purchaseStatus !== "invalidated";
onClicked: {
if (urlHandler.canHandleUrl(root.itemHref)) {
urlHandler.handleUrl(root.itemHref);
}
sendToPurchases({method: 'purchases_rezClicked', itemHref: root.itemHref, isWearable: root.isWearable});
rezzedNotifContainer.visible = true;
rezzedNotifContainerTimer.start();
}

View file

@ -442,6 +442,8 @@ Rectangle {
onSendToPurchases: {
if (msg.method === 'purchases_itemInfoClicked') {
sendToScript({method: 'purchases_itemInfoClicked', itemId: itemId});
} else if (msg.method === "purchases_rezClicked") {
sendToScript({method: 'purchases_rezClicked', itemHref: itemHref, isWearable: isWearable});
} else if (msg.method === 'purchases_itemCertificateClicked') {
inspectionCertificate.visible = true;
inspectionCertificate.isLightbox = true;
@ -638,7 +640,8 @@ Rectangle {
if (purchasesModel.get(i).title.toLowerCase().indexOf(filterBar.text.toLowerCase()) !== -1) {
if (purchasesModel.get(i).status !== "confirmed" && !root.isShowingMyItems) {
filteredPurchasesModel.insert(0, purchasesModel.get(i));
} else if ((root.isShowingMyItems && purchasesModel.get(i).edition_number === -1) || !root.isShowingMyItems) {
} else if ((root.isShowingMyItems && purchasesModel.get(i).edition_number === "0") ||
(!root.isShowingMyItems && purchasesModel.get(i).edition_number !== "0")) {
filteredPurchasesModel.append(purchasesModel.get(i));
}
}

View file

@ -196,7 +196,38 @@ Item {
anchors.bottom: parent.bottom;
anchors.left: parent.left;
anchors.right: parent.right;
anchors.rightMargin: 24;
Item {
visible: transactionHistoryModel.count === 0 && root.historyReceived;
anchors.centerIn: parent;
width: parent.width - 12;
height: parent.height;
HifiControlsUit.Separator {
colorScheme: 1;
anchors.left: parent.left;
anchors.right: parent.right;
anchors.top: parent.top;
}
RalewayRegular {
id: noActivityText;
text: "<b>The Wallet app is in closed Beta.</b><br><br>To request entry and <b>receive free HFC</b>, please contact " +
"<b>info@highfidelity.com</b> with your High Fidelity account username and the email address registered to that account.";
// Text size
size: 24;
// Style
color: hifi.colors.blueAccent;
anchors.left: parent.left;
anchors.leftMargin: 12;
anchors.right: parent.right;
anchors.rightMargin: 12;
anchors.verticalCenter: parent.verticalCenter;
height: paintedHeight;
wrapMode: Text.WordWrap;
horizontalAlignment: Text.AlignHCenter;
}
}
ListView {
id: transactionHistory;
@ -294,17 +325,6 @@ Item {
}
}
}
// This should never be visible (since you immediately get 100 HFC)
FiraSansRegular {
id: emptyTransationHistory;
size: 24;
visible: !transactionHistory.visible && root.historyReceived;
text: "Recent Activity Unavailable";
anchors.fill: parent;
horizontalAlignment: Text.AlignHCenter;
verticalAlignment: Text.AlignVCenter;
}
}
}
@ -350,7 +370,9 @@ Item {
}
root.pendingCount = pendingCount;
transactionHistoryModel.insert(0, {"transaction_type": "pendingCount"});
if (pendingCount > 0) {
transactionHistoryModel.insert(0, {"transaction_type": "pendingCount"});
}
}
//

View file

@ -53,8 +53,6 @@ Item {
onWalletAuthenticatedStatusResult: {
if (isAuthenticated) {
root.activeView = "step_4";
} else {
root.activeView = "step_3";
}
}

View file

@ -32,6 +32,8 @@ Rectangle {
color: hifi.colors.baseGray
property bool keyboardEnabled: HMD.active
property bool keyboardRaised: false
LetterboxMessage {
id: letterBoxMessage
@ -380,7 +382,7 @@ Rectangle {
Component.onCompleted: scriptsModel.filterRegExp = new RegExp("^.*$", "i")
onActiveFocusChanged: {
// raise the keyboard
keyboard.raised = activeFocus;
root.keyboardRaised = activeFocus;
// scroll to the bottom of the content area.
if (activeFocus) {
@ -481,7 +483,7 @@ Rectangle {
HifiControls.Keyboard {
id: keyboard
raised: false
raised: parent.keyboardEnabled && parent.keyboardRaised
numeric: false
anchors {
bottom: parent.bottom

View file

@ -11,8 +11,11 @@
import QtQuick 2.5
import QtQuick.Controls 1.4
import QtQuick.Dialogs 1.2 as OriginalDialogs
import "../../styles-uit"
import "../../controls-uit"
import "../dialogs"
Rectangle {
id: newModelDialog
@ -25,6 +28,15 @@ Rectangle {
property bool punctuationMode: false
property bool keyboardRasied: false
function errorMessageBox(message) {
return desktop.messageBox({
icon: hifi.icons.warning,
defaultButton: OriginalDialogs.StandardButton.Ok,
title: "Error",
text: message
});
}
Item {
id: column1
anchors.rightMargin: 10
@ -98,7 +110,6 @@ Rectangle {
CheckBox {
id: dynamic
text: qsTr("Dynamic")
}
Row {
@ -117,6 +128,7 @@ Rectangle {
Text {
id: text2
width: 160
x: dynamic.width / 2
color: "#ffffff"
text: qsTr("Models with automatic collisions set to 'Exact' cannot be dynamic, and should not be used as floors")
wrapMode: Text.WordWrap
@ -139,15 +151,40 @@ Rectangle {
ComboBox {
id: collisionType
property int priorIndex: 0
property string staticMeshCollisionText: "Exact - All polygons"
property var collisionArray: ["No Collision",
"Basic - Whole model",
"Good - Sub-meshes",
staticMeshCollisionText,
"Box",
"Sphere"]
width: 200
z: 100
transformOrigin: Item.Center
model: ["No Collision",
"Basic - Whole model",
"Good - Sub-meshes",
"Exact - All polygons",
"Box",
"Sphere"]
model: collisionArray
onCurrentIndexChanged: {
if (collisionArray[currentIndex] === staticMeshCollisionText) {
if (dynamic.checked) {
currentIndex = priorIndex;
errorMessageBox("Models with Automatic Collisions set to \""
+ staticMeshCollisionText + "\" cannot be dynamic.");
//--EARLY EXIT--( Can't have a static mesh model that's dynamic )
return;
}
dynamic.enabled = false;
} else {
dynamic.enabled = true;
}
priorIndex = currentIndex;
}
}
Row {
@ -155,10 +192,10 @@ Rectangle {
width: 200
height: 400
spacing: 5
anchors {
rightMargin: 15
}
anchors.horizontalCenter: column3.horizontalCenter
anchors.horizontalCenterOffset: -20
Button {
id: button1
text: qsTr("Add")

View file

@ -8,8 +8,8 @@ import "../audio" as HifiAudio
Item {
id: tablet
objectName: "tablet"
property int rowIndex: 0
property int columnIndex: 0
property int rowIndex: 6 // by default
property int columnIndex: 1 // point to 'go to location'
property int count: (flowMain.children.length - 1)
// used to look up a button by its uuid

View file

@ -11,6 +11,7 @@
import Hifi 1.0
import QtQuick 2.5
import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4
import QtGraphicalEffects 1.0
import "../../controls"
import "../../styles"
@ -83,7 +84,6 @@ StackView {
anchors.centerIn = parent;
}
function resetAfterTeleport() {
//storyCardFrame.shown = root.shown = false;
}
@ -134,7 +134,8 @@ StackView {
bottom: parent.bottom
}
onHostChanged: updateLocationTextTimer.start();
onHostChanged: updateLocationTextTimer.restart();
Rectangle {
id: navBar
width: parent.width
@ -205,16 +206,16 @@ StackView {
anchors {
top: parent.top;
left: addressLineContainer.left;
right: addressLineContainer.right;
}
}
HifiStyles.FiraSansRegular {
id: location;
anchors {
left: addressLineContainer.left;
leftMargin: 8;
verticalCenter: addressLineContainer.verticalCenter;
left: notice.right
leftMargin: 8
right: addressLineContainer.right
verticalCenter: notice.verticalCenter
}
font.pixelSize: addressLine.font.pixelSize;
color: "gray";
@ -222,7 +223,7 @@ StackView {
visible: addressLine.text.length === 0
}
TextInput {
TextField {
id: addressLine
width: addressLineContainer.width - addressLineContainer.anchors.leftMargin - addressLineContainer.anchors.rightMargin;
anchors {
@ -230,7 +231,6 @@ StackView {
leftMargin: 8;
verticalCenter: addressLineContainer.verticalCenter;
}
font.pixelSize: hifi.fonts.pixelSize * 0.75
onTextChanged: {
updateLocationText(text.length > 0);
}
@ -238,6 +238,17 @@ StackView {
addressBarDialog.keyboardEnabled = false;
toggleOrGo();
}
placeholderText: "Type domain address here"
verticalAlignment: TextInput.AlignBottom
style: TextFieldStyle {
textColor: hifi.colors.text
placeholderTextColor: "gray"
font {
family: hifi.fonts.fontFamily
pixelSize: hifi.fonts.pixelSize * 0.75
}
background: Item {}
}
}
Rectangle {
@ -347,7 +358,7 @@ StackView {
// Delay updating location text a bit to avoid flicker of content and so that connection status is valid.
id: updateLocationTextTimer
running: false
interval: 500 // ms
interval: 1000 // ms
repeat: false
onTriggered: updateLocationText(false);
}
@ -366,7 +377,7 @@ StackView {
HifiControls.Keyboard {
id: keyboard
raised: parent.keyboardEnabled
raised: parent.keyboardEnabled && parent.keyboardRaised
numeric: parent.punctuationMode
anchors {
bottom: parent.bottom

View file

@ -5,7 +5,9 @@ import TabletScriptingInterface 1.0
Item {
id: tabletButton
property string captionColorOverride: ""
property color defaultCaptionColor: "#ffffff"
property color captionColor: defaultCaptionColor
property var uuid;
property string icon: "icons/tablet-icons/edit-i.svg"
property string hoverIcon: tabletButton.icon
@ -105,7 +107,7 @@ Item {
Text {
id: text
color: captionColorOverride !== "" ? captionColorOverride: "#ffffff"
color: captionColor
text: tabletButton.text
font.bold: true
font.pixelSize: 18
@ -171,7 +173,7 @@ Item {
PropertyChanges {
target: text
color: captionColorOverride !== "" ? captionColorOverride: "#ffffff"
color: captionColor
text: tabletButton.hoverText
}
@ -197,7 +199,7 @@ Item {
PropertyChanges {
target: text
color: captionColorOverride !== "" ? captionColorOverride: "#333333"
color: captionColor !== defaultCaptionColor ? captionColor : "#333333"
text: tabletButton.activeText
}
@ -228,7 +230,7 @@ Item {
PropertyChanges {
target: text
color: captionColorOverride !== "" ? captionColorOverride: "#333333"
color: captionColor !== defaultCaptionColor ? captionColor : "#333333"
text: tabletButton.activeHoverText
}

View file

@ -40,37 +40,29 @@ Item {
CheckBox {
id: checkbox
// FIXME: Should use radio buttons if source.exclusiveGroup.
width: 20
visible: source !== null ?
source.visible && source.type === 1 && source.checkable && !source.exclusiveGroup :
false
checked: setChecked()
function setChecked() {
if (!source || source.type !== 1 || !source.checkable) {
return false;
}
// FIXME this works for native QML menus but I don't think it will
// for proxied QML menus
return source.checked;
Binding on checked {
value: source.checked;
when: source && source.type === 1 && source.checkable && !source.exclusiveGroup;
}
}
RadioButton {
id: radiobutton
// FIXME: Should use radio buttons if source.exclusiveGroup.
width: 20
visible: source !== null ?
source.visible && source.type === 1 && source.checkable && source.exclusiveGroup :
false
checked: setChecked()
function setChecked() {
if (!source || source.type !== 1 || !source.checkable) {
return false;
}
// FIXME this works for native QML menus but I don't think it will
// for proxied QML menus
return source.checked;
Binding on checked {
value: source.checked;
when: source && source.type === 1 && source.checkable && source.exclusiveGroup;
}
}
}

View file

@ -66,7 +66,6 @@ Item {
function toModel(items, newMenu) {
var result = modelMaker.createObject(tabletMenu);
var exclusionGroups = {};
for (var i = 0; i < items.length; ++i) {
var item = items[i];
@ -78,28 +77,6 @@ Item {
if (item.text !== "Users Online") {
result.append({"name": item.text, "item": item})
}
for(var j = 0; j < tabletMenu.rootMenu.exclusionGroupsByMenuItem.count; ++j)
{
var entry = tabletMenu.rootMenu.exclusionGroupsByMenuItem.get(j);
if(entry.menuItem == item.toString())
{
var exclusionGroupId = entry.exclusionGroup;
console.debug('item exclusionGroupId: ', exclusionGroupId)
if(!exclusionGroups[exclusionGroupId])
{
exclusionGroups[exclusionGroupId] = exclusiveGroupMaker.createObject(newMenu);
console.debug('new exclusion group created: ', exclusionGroups[exclusionGroupId])
}
var exclusionGroup = exclusionGroups[exclusionGroupId];
item.exclusiveGroup = exclusionGroup
console.debug('item.exclusiveGroup: ', item.exclusiveGroup)
}
}
break;
case MenuItemType.Separator:
result.append({"name": "", "item": item})

View file

@ -4,7 +4,9 @@ import QtQuick.Controls 1.4
StateImage {
id: button
property string captionColorOverride: ""
property color defaultCaptionColor: "#ffffff"
property color captionColor: defaultCaptionColor
property bool buttonEnabled: true
property bool isActive: false
property bool isEntered: false
@ -98,7 +100,7 @@ StateImage {
Text {
id: caption
color: captionColorOverride !== "" ? captionColorOverride: (button.isActive ? "#000000" : "#ffffff")
color: button.isActive ? "#000000" : captionColor
text: button.isActive ? (button.isEntered ? button.activeHoverText : button.activeText) : (button.isEntered ? button.hoverText : button.text)
font.bold: false
font.pixelSize: 9

View file

@ -203,6 +203,8 @@
#include "commerce/Wallet.h"
#include "commerce/QmlCommerce.h"
#include "webbrowser/WebBrowserSuggestionsEngine.h"
// On Windows PC, NVidia Optimus laptop, we want to enable NVIDIA GPU
// FIXME seems to be broken.
#if defined(Q_OS_WIN)
@ -1392,7 +1394,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
// Make sure we don't time out during slow operations at startup
updateHeartbeat();
QTimer* settingsTimer = new QTimer();
moveToNewNamedThread(settingsTimer, "Settings Thread", [this, settingsTimer]{
connect(qApp, &Application::beforeAboutToQuit, [this, settingsTimer]{
@ -1700,8 +1701,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
lastLeftHandPose = leftHandPose;
lastRightHandPose = rightHandPose;
properties["local_socket_changes"] = DependencyManager::get<StatTracker>()->getStat(LOCAL_SOCKET_CHANGE_STAT).toInt();
UserActivityLogger::getInstance().logAction("stats", properties);
});
sendStatsTimer->start();
@ -1825,6 +1824,9 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
// Preload Tablet sounds
DependencyManager::get<TabletScriptingInterface>()->preloadSounds();
_pendingIdleEvent = false;
_pendingRenderEvent = false;
qCDebug(interfaceapp) << "Metaverse session ID is" << uuidStringWithoutCurlyBraces(accountManager->getSessionID());
}
@ -2223,6 +2225,7 @@ void Application::initializeUi() {
QmlCommerce::registerType();
qmlRegisterType<ResourceImageItem>("Hifi", 1, 0, "ResourceImageItem");
qmlRegisterType<Preference>("Hifi", 1, 0, "Preference");
qmlRegisterType<WebBrowserSuggestionsEngine>("HifiWeb", 1, 0, "WebBrowserSuggestionsEngine");
auto offscreenUi = DependencyManager::get<OffscreenUi>();
offscreenUi->create();
@ -2693,7 +2696,7 @@ bool Application::importFromZIP(const QString& filePath) {
qDebug() << "A zip file has been dropped in: " << filePath;
QUrl empty;
// handle Blocks download from Marketplace
if (filePath.contains("vr.google.com/downloads")) {
if (filePath.contains("poly.google.com/downloads")) {
addAssetToWorldFromURL(filePath);
} else {
qApp->getFileDownloadInterface()->runUnzip(filePath, empty, true, true, false);
@ -4441,7 +4444,7 @@ void Application::cameraModeChanged() {
void Application::cameraMenuChanged() {
auto menu = Menu::getInstance();
if (menu->isOptionChecked(MenuOption::FullscreenMirror)) {
if (_myCamera.getMode() != CAMERA_MODE_MIRROR) {
if (!isHMDMode() && _myCamera.getMode() != CAMERA_MODE_MIRROR) {
_myCamera.setMode(CAMERA_MODE_MIRROR);
getMyAvatar()->reset(false, false, false); // to reset any active MyAvatar::FollowHelpers
}
@ -4481,8 +4484,11 @@ void Application::resetPhysicsReadyInformation() {
void Application::reloadResourceCaches() {
resetPhysicsReadyInformation();
// Query the octree to refresh everything in view
_lastQueriedTime = 0;
_octreeQuery.incrementConnectionID();
queryOctree(NodeType::EntityServer, PacketType::EntityQuery, _entityServerJurisdictions);
DependencyManager::get<AssetClient>()->clearCache();
@ -5543,6 +5549,7 @@ void Application::nodeActivated(SharedNodePointer node) {
// so we will do a proper query during update
if (node->getType() == NodeType::EntityServer) {
_lastQueriedTime = 0;
_octreeQuery.incrementConnectionID();
}
if (node->getType() == NodeType::AudioMixer) {
@ -6235,7 +6242,7 @@ void Application::addAssetToWorldFromURL(QString url) {
if (url.contains("filename")) {
filename = url.section("filename=", 1, 1); // Filename is in "?filename=" parameter at end of URL.
}
if (url.contains("vr.google.com/downloads")) {
if (url.contains("poly.google.com/downloads")) {
filename = url.section('/', -1);
if (url.contains("noDownload")) {
filename.remove(".zip?noDownload=false");
@ -6270,7 +6277,7 @@ void Application::addAssetToWorldFromURLRequestFinished() {
if (url.contains("filename")) {
filename = url.section("filename=", 1, 1); // Filename is in "?filename=" parameter at end of URL.
}
if (url.contains("vr.google.com/downloads")) {
if (url.contains("poly.google.com/downloads")) {
filename = url.section('/', -1);
if (url.contains("noDownload")) {
filename.remove(".zip?noDownload=false");
@ -7082,6 +7089,7 @@ DisplayPluginPointer Application::getActiveDisplayPlugin() const {
return _displayPlugin;
}
static const char* EXCLUSION_GROUP_KEY = "exclusionGroup";
static void addDisplayPluginToMenu(DisplayPluginPointer displayPlugin, bool active = false) {
auto menu = Menu::getInstance();
@ -7117,6 +7125,8 @@ static void addDisplayPluginToMenu(DisplayPluginPointer displayPlugin, bool acti
action->setCheckable(true);
action->setChecked(active);
displayPluginGroup->addAction(action);
action->setProperty(EXCLUSION_GROUP_KEY, QVariant::fromValue(displayPluginGroup));
Q_ASSERT(menu->menuItemExists(MenuOption::OutputMenu, name));
}
@ -7271,6 +7281,10 @@ void Application::updateDisplayMode() {
menu->setIsOptionChecked(MenuOption::FirstPerson, true);
cameraMenuChanged();
}
// Remove the mirror camera option from menu if in HMD mode
auto mirrorAction = menu->getActionForOption(MenuOption::FullscreenMirror);
mirrorAction->setVisible(!isHmd);
Q_ASSERT_X(_displayPlugin, "Application::updateDisplayMode", "could not find an activated display plugin");
}

View file

@ -543,7 +543,7 @@ private:
ViewFrustum _displayViewFrustum;
quint64 _lastQueriedTime;
OctreeQuery _octreeQuery; // NodeData derived class for querying octee cells from octree servers
OctreeQuery _octreeQuery { true }; // NodeData derived class for querying octee cells from octree servers
std::shared_ptr<controller::StateController> _applicationStateDevice; // Default ApplicationDevice reflecting the state of different properties of the session
std::shared_ptr<KeyboardMouseDevice> _keyboardMouseDevice; // Default input device, the good old keyboard mouse and maybe touchpad
@ -708,7 +708,7 @@ private:
friend class RenderEventHandler;
std::atomic<bool> _pendingIdleEvent { false };
std::atomic<bool> _pendingRenderEvent { false };
std::atomic<bool> _pendingIdleEvent { true };
std::atomic<bool> _pendingRenderEvent { true };
};
#endif // hifi_Application_h

View file

@ -54,7 +54,9 @@ void LODManager::autoAdjustLOD(float batchTime, float engineRunTime, float delta
float renderTime = batchTime + OVERLAY_AND_SWAP_TIME_BUDGET;
float maxTime = glm::max(renderTime, engineRunTime);
const float BLEND_TIMESCALE = 0.3f; // sec
float blend = BLEND_TIMESCALE / deltaTimeSec;
const float MIN_DELTA_TIME = 0.001f;
const float safeDeltaTime = glm::max(deltaTimeSec, MIN_DELTA_TIME);
float blend = BLEND_TIMESCALE / safeDeltaTime;
if (blend > 1.0f) {
blend = 1.0f;
}

View file

@ -56,7 +56,7 @@ Menu* Menu::getInstance() {
return dynamic_cast<Menu*>(qApp->getWindow()->menuBar());
}
const char* exclusionGroupKey = "exclusionGroup";
const char* EXCLUSION_GROUP_KEY = "exclusionGroup";
Menu::Menu() {
auto dialogsManager = DependencyManager::get<DialogsManager>();
@ -228,21 +228,21 @@ Menu::Menu() {
viewMenu, MenuOption::FirstPerson, Qt::CTRL | Qt::Key_F,
true, qApp, SLOT(cameraMenuChanged())));
firstPersonAction->setProperty(exclusionGroupKey, QVariant::fromValue(cameraModeGroup));
firstPersonAction->setProperty(EXCLUSION_GROUP_KEY, QVariant::fromValue(cameraModeGroup));
// View > Third Person
auto thirdPersonAction = cameraModeGroup->addAction(addCheckableActionToQMenuAndActionHash(
viewMenu, MenuOption::ThirdPerson, Qt::CTRL | Qt::Key_G,
false, qApp, SLOT(cameraMenuChanged())));
thirdPersonAction->setProperty(exclusionGroupKey, QVariant::fromValue(cameraModeGroup));
thirdPersonAction->setProperty(EXCLUSION_GROUP_KEY, QVariant::fromValue(cameraModeGroup));
// View > Mirror
auto viewMirrorAction = cameraModeGroup->addAction(addCheckableActionToQMenuAndActionHash(
viewMenu, MenuOption::FullscreenMirror, Qt::CTRL | Qt::Key_H,
false, qApp, SLOT(cameraMenuChanged())));
viewMirrorAction->setProperty(exclusionGroupKey, QVariant::fromValue(cameraModeGroup));
viewMirrorAction->setProperty(EXCLUSION_GROUP_KEY, QVariant::fromValue(cameraModeGroup));
// View > Independent [advanced]
auto viewIndependentAction = cameraModeGroup->addAction(addCheckableActionToQMenuAndActionHash(viewMenu,
@ -250,7 +250,7 @@ Menu::Menu() {
false, qApp, SLOT(cameraMenuChanged()),
UNSPECIFIED_POSITION, "Advanced"));
viewIndependentAction->setProperty(exclusionGroupKey, QVariant::fromValue(cameraModeGroup));
viewIndependentAction->setProperty(EXCLUSION_GROUP_KEY, QVariant::fromValue(cameraModeGroup));
// View > Entity Camera [advanced]
auto viewEntityCameraAction = cameraModeGroup->addAction(addCheckableActionToQMenuAndActionHash(viewMenu,
@ -258,7 +258,7 @@ Menu::Menu() {
false, qApp, SLOT(cameraMenuChanged()),
UNSPECIFIED_POSITION, "Advanced"));
viewEntityCameraAction->setProperty(exclusionGroupKey, QVariant::fromValue(cameraModeGroup));
viewEntityCameraAction->setProperty(EXCLUSION_GROUP_KEY, QVariant::fromValue(cameraModeGroup));
viewMenu->addSeparator();

View file

@ -179,6 +179,12 @@ void AvatarManager::updateOtherAvatars(float deltaTime) {
const AvatarPriority& sortData = sortedAvatars.top();
const auto& avatar = std::static_pointer_cast<Avatar>(sortData.avatar);
bool ignoring = DependencyManager::get<NodeList>()->isPersonalMutingNode(avatar->getID());
if (ignoring) {
sortedAvatars.pop();
continue;
}
// for ALL avatars...
if (_shouldRender) {
avatar->ensureInScene(avatar, qApp->getMain3DScene());

View file

@ -135,7 +135,7 @@ MyAvatar::MyAvatar(QThread* thread) :
connect(&domainHandler, &DomainHandler::settingsReceived, this, &MyAvatar::restrictScaleFromDomainSettings);
// when we leave a domain we lift whatever restrictions that domain may have placed on our scale
connect(&domainHandler, &DomainHandler::disconnectedFromDomain, this, &MyAvatar::clearScaleRestriction);
connect(&domainHandler, &DomainHandler::disconnectedFromDomain, this, &MyAvatar::leaveDomain);
_bodySensorMatrix = deriveBodyFromHMDSensor();
@ -2189,6 +2189,7 @@ void MyAvatar::clampScaleChangeToDomainLimits(float desiredScale) {
setTargetScale(clampedTargetScale);
qCDebug(interfaceapp, "Changed scale to %f", (double)_targetScale);
emit(scaleChanged());
}
float MyAvatar::getDomainMinScale() {
@ -2278,6 +2279,18 @@ void MyAvatar::restrictScaleFromDomainSettings(const QJsonObject& domainSettings
settings.endGroup();
}
void MyAvatar::leaveDomain() {
clearScaleRestriction();
saveAvatarScale();
}
void MyAvatar::saveAvatarScale() {
Settings settings;
settings.beginGroup("Avatar");
settings.setValue("scale", _targetScale);
settings.endGroup();
}
void MyAvatar::clearScaleRestriction() {
_domainMinimumScale = MIN_AVATAR_SCALE;
_domainMaximumScale = MAX_AVATAR_SCALE;

View file

@ -620,6 +620,10 @@ signals:
void dominantHandChanged(const QString& hand);
void sensorToWorldScaleChanged(float sensorToWorldScale);
void attachmentsChanged();
void scaleChanged();
private slots:
void leaveDomain();
private:
@ -637,6 +641,8 @@ private:
virtual int parseDataFromBuffer(const QByteArray& buffer) override;
virtual glm::vec3 getSkeletonPosition() const override;
void saveAvatarScale();
glm::vec3 getScriptedMotorVelocity() const { return _scriptedMotorVelocity; }
float getScriptedMotorTimescale() const { return _scriptedMotorTimescale; }
QString getScriptedMotorFrame() const;

View file

@ -35,6 +35,14 @@ public:
void updateLocation(const QString& asset_id, const QString location, const bool controlledFailure = false);
void certificateInfo(const QString& certificateId);
enum CertificateStatus {
CERTIFICATE_STATUS_UNKNOWN = 0,
CERTIFICATE_STATUS_VERIFICATION_SUCCESS,
CERTIFICATE_STATUS_VERIFICATION_TIMEOUT,
CERTIFICATE_STATUS_STATIC_VERIFICATION_FAILED,
CERTIFICATE_STATUS_OWNER_VERIFICATION_FAILED,
};
signals:
void buyResult(QJsonObject result);
void receiveAtResult(QJsonObject result);
@ -45,6 +53,8 @@ signals:
void locationUpdateResult(QJsonObject result);
void certificateInfoResult(QJsonObject result);
void updateCertificateStatus(const QString& certID, uint certStatus);
public slots:
void buySuccess(QNetworkReply& reply);
void buyFailure(QNetworkReply& reply);

View file

@ -30,6 +30,12 @@ QmlCommerce::QmlCommerce(QQuickItem* parent) : OffscreenQmlDialog(parent) {
connect(ledger.data(), &Ledger::accountResult, this, &QmlCommerce::accountResult);
connect(wallet.data(), &Wallet::walletStatusResult, this, &QmlCommerce::walletStatusResult);
connect(ledger.data(), &Ledger::certificateInfoResult, this, &QmlCommerce::certificateInfoResult);
connect(ledger.data(), &Ledger::updateCertificateStatus, this, &QmlCommerce::updateCertificateStatus);
auto accountManager = DependencyManager::get<AccountManager>();
connect(accountManager.data(), &AccountManager::usernameChanged, this, [&]() {
setPassphrase("");
});
}
void QmlCommerce::getWalletStatus() {

View file

@ -45,6 +45,8 @@ signals:
void accountResult(QJsonObject result);
void certificateInfoResult(QJsonObject result);
void updateCertificateStatus(const QString& certID, uint certStatus);
protected:
Q_INVOKABLE void getWalletStatus();

View file

@ -319,6 +319,7 @@ Wallet::Wallet() {
auto& packetReceiver = nodeList->getPacketReceiver();
packetReceiver.registerListener(PacketType::ChallengeOwnership, this, "handleChallengeOwnershipPacket");
packetReceiver.registerListener(PacketType::ChallengeOwnershipRequest, this, "handleChallengeOwnershipPacket");
connect(ledger.data(), &Ledger::accountResult, this, [&]() {
auto wallet = DependencyManager::get<Wallet>();
@ -717,50 +718,86 @@ bool Wallet::changePassphrase(const QString& newPassphrase) {
}
void Wallet::handleChallengeOwnershipPacket(QSharedPointer<ReceivedMessage> packet, SharedNodePointer sendingNode) {
auto nodeList = DependencyManager::get<NodeList>();
bool challengeOriginatedFromClient = packet->getType() == PacketType::ChallengeOwnershipRequest;
unsigned char decryptedText[64];
int certIDByteArraySize;
int encryptedTextByteArraySize;
int challengingNodeUUIDByteArraySize;
packet->readPrimitive(&certIDByteArraySize);
packet->readPrimitive(&encryptedTextByteArraySize);
if (challengeOriginatedFromClient) {
packet->readPrimitive(&challengingNodeUUIDByteArraySize);
}
QByteArray certID = packet->read(certIDByteArraySize);
QByteArray encryptedText = packet->read(encryptedTextByteArraySize);
QByteArray challengingNodeUUID;
if (challengeOriginatedFromClient) {
challengingNodeUUID = packet->read(challengingNodeUUIDByteArraySize);
}
RSA* rsa = readKeys(keyFilePath().toStdString().c_str());
int decryptionStatus = -1;
if (rsa) {
const int decryptionStatus = RSA_private_decrypt(encryptedTextByteArraySize,
ERR_clear_error();
decryptionStatus = RSA_private_decrypt(encryptedTextByteArraySize,
reinterpret_cast<const unsigned char*>(encryptedText.constData()),
decryptedText,
rsa,
RSA_PKCS1_OAEP_PADDING);
RSA_free(rsa);
if (decryptionStatus != -1) {
auto nodeList = DependencyManager::get<NodeList>();
QByteArray decryptedTextByteArray = QByteArray(reinterpret_cast<const char*>(decryptedText), decryptionStatus);
int decryptedTextByteArraySize = decryptedTextByteArray.size();
int certIDSize = certID.size();
// setup the packet
auto decryptedTextPacket = NLPacket::create(PacketType::ChallengeOwnership, certIDSize + decryptedTextByteArraySize + 2 * sizeof(int), true);
decryptedTextPacket->writePrimitive(certIDSize);
decryptedTextPacket->writePrimitive(decryptedTextByteArraySize);
decryptedTextPacket->write(certID);
decryptedTextPacket->write(decryptedTextByteArray);
qCDebug(commerce) << "Sending ChallengeOwnership Packet containing decrypted text" << decryptedTextByteArray << "for CertID" << certID;
nodeList->sendPacket(std::move(decryptedTextPacket), *sendingNode);
} else {
qCDebug(commerce) << "During entity ownership challenge, decrypting the encrypted text failed.";
}
} else {
qCDebug(commerce) << "During entity ownership challenge, creating the RSA object failed.";
}
QByteArray decryptedTextByteArray;
if (decryptionStatus > -1) {
decryptedTextByteArray = QByteArray(reinterpret_cast<const char*>(decryptedText), decryptionStatus);
}
int decryptedTextByteArraySize = decryptedTextByteArray.size();
int certIDSize = certID.size();
// setup the packet
if (challengeOriginatedFromClient) {
auto decryptedTextPacket = NLPacket::create(PacketType::ChallengeOwnershipReply,
certIDSize + decryptedTextByteArraySize + challengingNodeUUIDByteArraySize + 3 * sizeof(int),
true);
decryptedTextPacket->writePrimitive(certIDSize);
decryptedTextPacket->writePrimitive(decryptedTextByteArraySize);
decryptedTextPacket->writePrimitive(challengingNodeUUIDByteArraySize);
decryptedTextPacket->write(certID);
decryptedTextPacket->write(decryptedTextByteArray);
decryptedTextPacket->write(challengingNodeUUID);
qCDebug(commerce) << "Sending ChallengeOwnershipReply Packet containing decrypted text" << decryptedTextByteArray << "for CertID" << certID;
nodeList->sendPacket(std::move(decryptedTextPacket), *sendingNode);
} else {
auto decryptedTextPacket = NLPacket::create(PacketType::ChallengeOwnership, certIDSize + decryptedTextByteArraySize + 2 * sizeof(int), true);
decryptedTextPacket->writePrimitive(certIDSize);
decryptedTextPacket->writePrimitive(decryptedTextByteArraySize);
decryptedTextPacket->write(certID);
decryptedTextPacket->write(decryptedTextByteArray);
qCDebug(commerce) << "Sending ChallengeOwnership Packet containing decrypted text" << decryptedTextByteArray << "for CertID" << certID;
nodeList->sendPacket(std::move(decryptedTextPacket), *sendingNode);
}
if (decryptionStatus == -1) {
qCDebug(commerce) << "During entity ownership challenge, decrypting the encrypted text failed.";
long error = ERR_get_error();
if (error != 0) {
const char* error_str = ERR_error_string(error, NULL);
qCWarning(entities) << "RSA error:" << error_str;
}
}
}
void Wallet::account() {

View file

@ -15,7 +15,7 @@
#include "RayPickScriptingInterface.h"
LaserPointer::LaserPointer(const QVariant& rayProps, const RenderStateMap& renderStates, const DefaultRenderStateMap& defaultRenderStates,
const bool faceAvatar, const bool centerEndY, const bool lockEnd, const bool distanceScaleEnd, const bool enabled) :
const bool faceAvatar, const bool centerEndY, const bool lockEnd, const bool distanceScaleEnd, const bool scaleWithAvatar, const bool enabled) :
_renderingEnabled(enabled),
_renderStates(renderStates),
_defaultRenderStates(defaultRenderStates),
@ -23,9 +23,9 @@ LaserPointer::LaserPointer(const QVariant& rayProps, const RenderStateMap& rende
_centerEndY(centerEndY),
_lockEnd(lockEnd),
_distanceScaleEnd(distanceScaleEnd),
_scaleWithAvatar(scaleWithAvatar),
_rayPickUID(DependencyManager::get<RayPickScriptingInterface>()->createRayPick(rayProps))
{
for (auto& state : _renderStates) {
if (!enabled || state.first != _currentRenderState) {
@ -95,6 +95,10 @@ void LaserPointer::editRenderState(const std::string& state, const QVariant& sta
if (endDim.isValid()) {
_renderStates[state].setEndDim(vec3FromVariant(endDim));
}
QVariant lineWidth = pathProps.toMap()["lineWidth"];
if (lineWidth.isValid()) {
_renderStates[state].setLineWidth(lineWidth.toFloat());
}
});
}
@ -119,23 +123,25 @@ void LaserPointer::updateRenderState(const RenderState& renderState, const Inter
qApp->getOverlays().editOverlay(renderState.getStartID(), startProps);
}
glm::vec3 endVec;
if (((defaultState || !_lockEnd) && _objectLockEnd.first.isNull()) || type == IntersectionType::HUD) {
if (((defaultState || !_lockEnd) && _lockEndObject.id.isNull()) || type == IntersectionType::HUD) {
endVec = pickRay.origin + pickRay.direction * distance;
} else {
if (!_objectLockEnd.first.isNull()) {
if (!_lockEndObject.id.isNull()) {
glm::vec3 pos;
glm::quat rot;
glm::vec3 dim;
glm::vec3 registrationPoint;
if (_objectLockEnd.second) {
pos = vec3FromVariant(qApp->getOverlays().getProperty(_objectLockEnd.first, "position").value);
rot = quatFromVariant(qApp->getOverlays().getProperty(_objectLockEnd.first, "rotation").value);
dim = vec3FromVariant(qApp->getOverlays().getProperty(_objectLockEnd.first, "dimensions").value);
if (_lockEndObject.isOverlay) {
pos = vec3FromVariant(qApp->getOverlays().getProperty(_lockEndObject.id, "position").value);
rot = quatFromVariant(qApp->getOverlays().getProperty(_lockEndObject.id, "rotation").value);
dim = vec3FromVariant(qApp->getOverlays().getProperty(_lockEndObject.id, "dimensions").value);
registrationPoint = glm::vec3(0.5f);
} else {
EntityItemProperties props = DependencyManager::get<EntityScriptingInterface>()->getEntityProperties(_objectLockEnd.first);
pos = props.getPosition();
rot = props.getRotation();
EntityItemProperties props = DependencyManager::get<EntityScriptingInterface>()->getEntityProperties(_lockEndObject.id);
glm::mat4 entityMat = createMatFromQuatAndPos(props.getRotation(), props.getPosition());
glm::mat4 finalPosAndRotMat = entityMat * _lockEndObject.offsetMat;
pos = extractTranslation(finalPosAndRotMat);
rot = glmExtractRotation(finalPosAndRotMat);
dim = props.getDimensions();
registrationPoint = props.getRegistrationPoint();
}
@ -151,6 +157,7 @@ void LaserPointer::updateRenderState(const RenderState& renderState, const Inter
}
}
}
QVariant end = vec3toVariant(endVec);
if (!renderState.getPathID().isNull()) {
QVariantMap pathProps;
@ -158,6 +165,9 @@ void LaserPointer::updateRenderState(const RenderState& renderState, const Inter
pathProps.insert("end", end);
pathProps.insert("visible", true);
pathProps.insert("ignoreRayIntersection", renderState.doesPathIgnoreRays());
if (_scaleWithAvatar) {
pathProps.insert("lineWidth", renderState.getLineWidth() * DependencyManager::get<AvatarManager>()->getMyAvatar()->getSensorToWorldScale());
}
qApp->getOverlays().editOverlay(renderState.getPathID(), pathProps);
}
if (!renderState.getEndID().isNull()) {
@ -165,7 +175,7 @@ void LaserPointer::updateRenderState(const RenderState& renderState, const Inter
glm::quat faceAvatarRotation = DependencyManager::get<AvatarManager>()->getMyAvatar()->getWorldOrientation() * glm::quat(glm::radians(glm::vec3(0.0f, 180.0f, 0.0f)));
glm::vec3 dim = vec3FromVariant(qApp->getOverlays().getProperty(renderState.getEndID(), "dimensions").value);
if (_distanceScaleEnd) {
dim = renderState.getEndDim() * glm::distance(pickRay.origin, endVec) * DependencyManager::get<AvatarManager>()->getMyAvatar()->getSensorToWorldScale();
dim = renderState.getEndDim() * glm::distance(pickRay.origin, endVec);
endProps.insert("dimensions", vec3toVariant(dim));
}
if (_centerEndY) {
@ -209,7 +219,7 @@ void LaserPointer::update() {
withReadLock([&] {
RayPickResult prevRayPickResult = qApp->getRayPickManager().getPrevRayPickResult(_rayPickUID);
if (_renderingEnabled && !_currentRenderState.empty() && _renderStates.find(_currentRenderState) != _renderStates.end() &&
(prevRayPickResult.type != IntersectionType::NONE || _laserLength > 0.0f || !_objectLockEnd.first.isNull())) {
(prevRayPickResult.type != IntersectionType::NONE || _laserLength > 0.0f || !_lockEndObject.id.isNull())) {
float distance = _laserLength > 0.0f ? _laserLength : prevRayPickResult.distance;
updateRenderState(_renderStates[_currentRenderState], prevRayPickResult.type, distance, prevRayPickResult.objectID, prevRayPickResult.searchRay, false);
disableRenderState(_defaultRenderStates[_currentRenderState].second);
@ -233,9 +243,11 @@ void LaserPointer::setLaserLength(const float laserLength) {
});
}
void LaserPointer::setLockEndUUID(QUuid objectID, const bool isOverlay) {
void LaserPointer::setLockEndUUID(QUuid objectID, const bool isOverlay, const glm::mat4& offsetMat) {
withWriteLock([&] {
_objectLockEnd = std::pair<QUuid, bool>(objectID, isOverlay);
_lockEndObject.id = objectID;
_lockEndObject.isOverlay = isOverlay;
_lockEndObject.offsetMat = offsetMat;
});
}
@ -255,6 +267,7 @@ RenderState::RenderState(const OverlayID& startID, const OverlayID& pathID, cons
}
if (!_pathID.isNull()) {
_pathIgnoreRays = qApp->getOverlays().getProperty(_pathID, "ignoreRayIntersection").value.toBool();
_lineWidth = qApp->getOverlays().getProperty(_pathID, "lineWidth").value.toFloat();
}
if (!_endID.isNull()) {
_endDim = vec3FromVariant(qApp->getOverlays().getProperty(_endID, "dimensions").value);

View file

@ -21,6 +21,12 @@
class RayPickResult;
struct LockEndObject {
QUuid id { QUuid() };
bool isOverlay { false };
glm::mat4 offsetMat { glm::mat4() };
};
class RenderState {
public:
@ -37,6 +43,9 @@ public:
void setEndDim(const glm::vec3& endDim) { _endDim = endDim; }
const glm::vec3& getEndDim() const { return _endDim; }
void setLineWidth(const float& lineWidth) { _lineWidth = lineWidth; }
const float& getLineWidth() const { return _lineWidth; }
void deleteOverlays();
private:
@ -48,6 +57,7 @@ private:
bool _endIgnoreRays;
glm::vec3 _endDim;
float _lineWidth;
};
@ -60,7 +70,7 @@ public:
typedef std::unordered_map<std::string, std::pair<float, RenderState>> DefaultRenderStateMap;
LaserPointer(const QVariant& rayProps, const RenderStateMap& renderStates, const DefaultRenderStateMap& defaultRenderStates,
const bool faceAvatar, const bool centerEndY, const bool lockEnd, const bool distanceScaleEnd, const bool enabled);
const bool faceAvatar, const bool centerEndY, const bool lockEnd, const bool distanceScaleEnd, const bool scaleWithAvatar, const bool enabled);
~LaserPointer();
QUuid getRayUID() { return _rayPickUID; }
@ -74,7 +84,7 @@ public:
void setPrecisionPicking(const bool precisionPicking);
void setLaserLength(const float laserLength);
void setLockEndUUID(QUuid objectID, const bool isOverlay);
void setLockEndUUID(QUuid objectID, const bool isOverlay, const glm::mat4& offsetMat = glm::mat4());
void setIgnoreItems(const QVector<QUuid>& ignoreItems) const;
void setIncludeItems(const QVector<QUuid>& includeItems) const;
@ -91,7 +101,8 @@ private:
bool _centerEndY;
bool _lockEnd;
bool _distanceScaleEnd;
std::pair<QUuid, bool> _objectLockEnd { std::pair<QUuid, bool>(QUuid(), false)};
bool _scaleWithAvatar;
LockEndObject _lockEndObject;
const QUuid _rayPickUID;

View file

@ -11,9 +11,9 @@
#include "LaserPointerManager.h"
QUuid LaserPointerManager::createLaserPointer(const QVariant& rayProps, const LaserPointer::RenderStateMap& renderStates, const LaserPointer::DefaultRenderStateMap& defaultRenderStates,
const bool faceAvatar, const bool centerEndY, const bool lockEnd, const bool distanceScaleEnd, const bool enabled) {
const bool faceAvatar, const bool centerEndY, const bool lockEnd, const bool distanceScaleEnd, const bool scaleWithAvatar, const bool enabled) {
QUuid result;
std::shared_ptr<LaserPointer> laserPointer = std::make_shared<LaserPointer>(rayProps, renderStates, defaultRenderStates, faceAvatar, centerEndY, lockEnd, distanceScaleEnd, enabled);
std::shared_ptr<LaserPointer> laserPointer = std::make_shared<LaserPointer>(rayProps, renderStates, defaultRenderStates, faceAvatar, centerEndY, lockEnd, distanceScaleEnd, scaleWithAvatar, enabled);
if (!laserPointer->getRayUID().isNull()) {
result = QUuid::createUuid();
withWriteLock([&] { _laserPointers[result] = laserPointer; });
@ -113,9 +113,9 @@ void LaserPointerManager::setIncludeItems(const QUuid& uid, const QVector<QUuid>
}
}
void LaserPointerManager::setLockEndUUID(const QUuid& uid, const QUuid& objectID, const bool isOverlay) const {
void LaserPointerManager::setLockEndUUID(const QUuid& uid, const QUuid& objectID, const bool isOverlay, const glm::mat4& offsetMat) const {
auto laserPointer = find(uid);
if (laserPointer) {
laserPointer->setLockEndUUID(objectID, isOverlay);
laserPointer->setLockEndUUID(objectID, isOverlay, offsetMat);
}
}

View file

@ -25,7 +25,7 @@ class LaserPointerManager : protected ReadWriteLockable {
public:
QUuid createLaserPointer(const QVariant& rayProps, const LaserPointer::RenderStateMap& renderStates, const LaserPointer::DefaultRenderStateMap& defaultRenderStates,
const bool faceAvatar, const bool centerEndY, const bool lockEnd, const bool distanceScaleEnd, const bool enabled);
const bool faceAvatar, const bool centerEndY, const bool lockEnd, const bool distanceScaleEnd, const bool scaleWithAvatar, const bool enabled);
void removeLaserPointer(const QUuid& uid);
void enableLaserPointer(const QUuid& uid) const;
@ -39,7 +39,7 @@ public:
void setIgnoreItems(const QUuid& uid, const QVector<QUuid>& ignoreEntities) const;
void setIncludeItems(const QUuid& uid, const QVector<QUuid>& includeEntities) const;
void setLockEndUUID(const QUuid& uid, const QUuid& objectID, const bool isOverlay) const;
void setLockEndUUID(const QUuid& uid, const QUuid& objectID, const bool isOverlay, const glm::mat4& offsetMat = glm::mat4()) const;
void update();

View file

@ -51,6 +51,11 @@ QUuid LaserPointerScriptingInterface::createLaserPointer(const QVariant& propert
enabled = propertyMap["enabled"].toBool();
}
bool scaleWithAvatar = false;
if (propertyMap["scaleWithAvatar"].isValid()) {
scaleWithAvatar = propertyMap["scaleWithAvatar"].toBool();
}
LaserPointer::RenderStateMap renderStates;
if (propertyMap["renderStates"].isValid()) {
QList<QVariant> renderStateVariants = propertyMap["renderStates"].toList();
@ -80,7 +85,7 @@ QUuid LaserPointerScriptingInterface::createLaserPointer(const QVariant& propert
}
}
return qApp->getLaserPointerManager().createLaserPointer(properties, renderStates, defaultRenderStates, faceAvatar, centerEndY, lockEnd, distanceScaleEnd, enabled);
return qApp->getLaserPointerManager().createLaserPointer(properties, renderStates, defaultRenderStates, faceAvatar, centerEndY, lockEnd, distanceScaleEnd, scaleWithAvatar, enabled);
}
void LaserPointerScriptingInterface::editRenderState(const QUuid& uid, const QString& renderState, const QVariant& properties) const {

View file

@ -35,7 +35,7 @@ public slots:
Q_INVOKABLE void setIgnoreItems(const QUuid& uid, const QScriptValue& ignoreEntities) const;
Q_INVOKABLE void setIncludeItems(const QUuid& uid, const QScriptValue& includeEntities) const;
Q_INVOKABLE void setLockEndUUID(const QUuid& uid, const QUuid& objectID, bool isOverlay) const { qApp->getLaserPointerManager().setLockEndUUID(uid, objectID, isOverlay); }
Q_INVOKABLE void setLockEndUUID(const QUuid& uid, const QUuid& objectID, bool isOverlay, const glm::mat4& offsetMat = glm::mat4()) const { qApp->getLaserPointerManager().setLockEndUUID(uid, objectID, isOverlay, offsetMat); }
private:
static RenderState buildRenderState(const QVariantMap& propMap);

View file

@ -71,6 +71,12 @@ bool SelectionScriptingInterface::removeFromSelectedItemsList(const QString& lis
return false;
}
bool SelectionScriptingInterface::clearSelectedItemsList(const QString& listName) {
_selectedItemsListMap.insert(listName, GameplayObjects());
emit selectedItemsListChanged(listName);
return true;
}
template <class T> bool SelectionScriptingInterface::addToGameplayObjects(const QString& listName, T idToAdd) {
GameplayObjects currentList = _selectedItemsListMap.value(listName);
currentList.addToGameplayObjects(idToAdd);

View file

@ -56,11 +56,45 @@ public:
GameplayObjects getList(const QString& listName);
/**jsdoc
* Prints out the list of avatars, entities and overlays stored in a particular selection.
* @function Selection.printList
* @param listName {string} name of the selection
*/
Q_INVOKABLE void printList(const QString& listName);
/**jsdoc
* Removes a named selection from the list of selections.
* @function Selection.removeListFromMap
* @param listName {string} name of the selection
* @returns {bool} true if the selection existed and was successfully removed.
*/
Q_INVOKABLE bool removeListFromMap(const QString& listName);
/**jsdoc
* Add an item in a selection.
* @function Selection.addToSelectedItemsList
* @param listName {string} name of the selection
* @param itemType {string} the type of the item (one of "avatar", "entity" or "overlay")
* @param id {EntityID} the Id of the item to add to the selection
* @returns {bool} true if the item was successfully added.
*/
Q_INVOKABLE bool addToSelectedItemsList(const QString& listName, const QString& itemType, const QUuid& id);
/**jsdoc
* Remove an item from a selection.
* @function Selection.removeFromSelectedItemsList
* @param listName {string} name of the selection
* @param itemType {string} the type of the item (one of "avatar", "entity" or "overlay")
* @param id {EntityID} the Id of the item to remove
* @returns {bool} true if the item was successfully removed.
*/
Q_INVOKABLE bool removeFromSelectedItemsList(const QString& listName, const QString& itemType, const QUuid& id);
/**jsdoc
* Remove all items from a selection.
* @function Selection.clearSelectedItemsList
* @param listName {string} name of the selection
* @returns {bool} true if the item was successfully cleared.
*/
Q_INVOKABLE bool clearSelectedItemsList(const QString& listName);
signals:
void selectedItemsListChanged(const QString& listName);

View file

@ -40,6 +40,10 @@ AddressBarDialog::AddressBarDialog(QQuickItem* parent) : OffscreenQmlDialog(pare
_backEnabled = !(DependencyManager::get<AddressManager>()->getBackStack().isEmpty());
_forwardEnabled = !(DependencyManager::get<AddressManager>()->getForwardStack().isEmpty());
connect(addressManager.data(), &AddressManager::hostChanged, this, &AddressBarDialog::hostChanged);
auto nodeList = DependencyManager::get<NodeList>();
const DomainHandler& domainHandler = nodeList->getDomainHandler();
connect(&domainHandler, &DomainHandler::connectedToDomain, this, &AddressBarDialog::hostChanged);
connect(&domainHandler, &DomainHandler::disconnectedFromDomain, this, &AddressBarDialog::hostChanged);
connect(DependencyManager::get<DialogsManager>().data(), &DialogsManager::setUseFeed, this, &AddressBarDialog::setUseFeed);
connect(qApp, &Application::receivedHifiSchemeURL, this, &AddressBarDialog::receivedHifiSchemeURL);
}

View file

@ -16,13 +16,11 @@
#include "Application.h"
const float DEFAULT_LINE_WIDTH = 1.0f;
const bool DEFAULT_IS_SOLID = false;
const bool DEFAULT_IS_DASHED_LINE = false;
Base3DOverlay::Base3DOverlay() :
SpatiallyNestable(NestableType::Overlay, QUuid::createUuid()),
_lineWidth(DEFAULT_LINE_WIDTH),
_isSolid(DEFAULT_IS_SOLID),
_isDashedLine(DEFAULT_IS_DASHED_LINE),
_ignoreRayIntersection(false),
@ -34,7 +32,6 @@ Base3DOverlay::Base3DOverlay() :
Base3DOverlay::Base3DOverlay(const Base3DOverlay* base3DOverlay) :
Overlay(base3DOverlay),
SpatiallyNestable(NestableType::Overlay, QUuid::createUuid()),
_lineWidth(base3DOverlay->_lineWidth),
_isSolid(base3DOverlay->_isSolid),
_isDashedLine(base3DOverlay->_isDashedLine),
_ignoreRayIntersection(base3DOverlay->_ignoreRayIntersection),
@ -153,12 +150,6 @@ void Base3DOverlay::setProperties(const QVariantMap& originalProperties) {
setLocalOrientation(quatFromVariant(properties["orientation"]));
needRenderItemUpdate = true;
}
if (properties["lineWidth"].isValid()) {
setLineWidth(properties["lineWidth"].toFloat());
needRenderItemUpdate = true;
}
if (properties["isSolid"].isValid()) {
setIsSolid(properties["isSolid"].toBool());
}
@ -225,10 +216,7 @@ QVariant Base3DOverlay::getProperty(const QString& property) {
if (property == "localRotation" || property == "localOrientation") {
return quatToVariant(getLocalOrientation());
}
if (property == "lineWidth") {
return _lineWidth;
}
if (property == "isSolid" || property == "isFilled" || property == "solid" || property == "filed") {
if (property == "isSolid" || property == "isFilled" || property == "solid" || property == "filled" || property == "filed") {
return _isSolid;
}
if (property == "isWire" || property == "wire") {
@ -265,7 +253,7 @@ void Base3DOverlay::locationChanged(bool tellPhysics) {
SpatiallyNestable::locationChanged(tellPhysics);
// Force the actual update of the render transform through the notify call
notifyRenderTransformChange();
notifyRenderVariableChange();
}
void Base3DOverlay::parentDeleted() {
@ -275,18 +263,21 @@ void Base3DOverlay::parentDeleted() {
void Base3DOverlay::update(float duration) {
// In Base3DOverlay, if its location or bound changed, the renderTrasnformDirty flag is true.
// then the correct transform used for rendering is computed in the update transaction and assigned.
if (_renderTransformDirty) {
if (_renderVariableDirty) {
auto itemID = getRenderItemID();
if (render::Item::isValidID(itemID)) {
// Capture the render transform value in game loop before
auto latestTransform = evalRenderTransform();
_renderTransformDirty = false;
bool latestVisible = getVisible();
_renderVariableDirty = false;
render::ScenePointer scene = qApp->getMain3DScene();
render::Transaction transaction;
transaction.updateItem<Overlay>(itemID, [latestTransform](Overlay& data) {
transaction.updateItem<Overlay>(itemID, [latestTransform, latestVisible](Overlay& data) {
auto overlay3D = dynamic_cast<Base3DOverlay*>(&data);
if (overlay3D) {
// TODO: overlays need to communicate all relavent render properties through transactions
overlay3D->setRenderTransform(latestTransform);
overlay3D->setRenderVisible(latestVisible);
}
});
scene->enqueueTransaction(transaction);
@ -294,8 +285,8 @@ void Base3DOverlay::update(float duration) {
}
}
void Base3DOverlay::notifyRenderTransformChange() const {
_renderTransformDirty = true;
void Base3DOverlay::notifyRenderVariableChange() const {
_renderVariableDirty = true;
}
Transform Base3DOverlay::evalRenderTransform() {
@ -306,8 +297,17 @@ void Base3DOverlay::setRenderTransform(const Transform& transform) {
_renderTransform = transform;
}
void Base3DOverlay::setRenderVisible(bool visible) {
_renderVisible = visible;
}
SpatialParentTree* Base3DOverlay::getParentTree() const {
auto entityTreeRenderer = qApp->getEntities();
EntityTreePointer entityTree = entityTreeRenderer ? entityTreeRenderer->getTree() : nullptr;
return entityTree.get();
}
void Base3DOverlay::setVisible(bool visible) {
Parent::setVisible(visible);
notifyRenderVariableChange();
}

View file

@ -18,11 +18,14 @@
class Base3DOverlay : public Overlay, public SpatiallyNestable {
Q_OBJECT
using Parent = Overlay;
public:
Base3DOverlay();
Base3DOverlay(const Base3DOverlay* base3DOverlay);
void setVisible(bool visible) override;
virtual OverlayID getOverlayID() const override { return OverlayID(getID().toString()); }
void setOverlayID(OverlayID overlayID) override { setID(overlayID); }
@ -35,7 +38,6 @@ public:
// TODO: consider implementing registration points in this class
glm::vec3 getCenter() const { return getWorldPosition(); }
float getLineWidth() const { return _lineWidth; }
bool getIsSolid() const { return _isSolid; }
bool getIsDashedLine() const { return _isDashedLine; }
bool getIsSolidLine() const { return !_isDashedLine; }
@ -44,7 +46,6 @@ public:
bool getDrawHUDLayer() const { return _drawHUDLayer; }
bool getIsGrabbable() const { return _isGrabbable; }
void setLineWidth(float lineWidth) { _lineWidth = lineWidth; }
void setIsSolid(bool isSolid) { _isSolid = isSolid; }
void setIsDashedLine(bool isDashedLine) { _isDashedLine = isDashedLine; }
void setIgnoreRayIntersection(bool value) { _ignoreRayIntersection = value; }
@ -56,7 +57,7 @@ public:
void update(float deltatime) override;
void notifyRenderTransformChange() const;
void notifyRenderVariableChange() const;
void setProperties(const QVariantMap& properties) override;
QVariant getProperty(const QString& property) override;
@ -76,18 +77,19 @@ protected:
virtual void parentDeleted() override;
mutable Transform _renderTransform;
mutable bool _renderVisible;
virtual Transform evalRenderTransform();
virtual void setRenderTransform(const Transform& transform);
void setRenderVisible(bool visible);
const Transform& getRenderTransform() const { return _renderTransform; }
float _lineWidth;
bool _isSolid;
bool _isDashedLine;
bool _ignoreRayIntersection;
bool _drawInFront;
bool _drawHUDLayer;
bool _isGrabbable { false };
mutable bool _renderTransformDirty{ true };
mutable bool _renderVariableDirty { true };
QString _name;
};

View file

@ -59,7 +59,7 @@ Circle3DOverlay::~Circle3DOverlay() {
}
}
void Circle3DOverlay::render(RenderArgs* args) {
if (!_visible) {
if (!_renderVisible) {
return; // do nothing if we're not visible
}
@ -259,7 +259,7 @@ void Circle3DOverlay::render(RenderArgs* args) {
}
const render::ShapeKey Circle3DOverlay::getShapeKey() {
auto builder = render::ShapeKey::Builder().withoutCullFace().withUnlit();
auto builder = render::ShapeKey::Builder().withoutCullFace();
if (isTransparent()) {
builder.withTranslucent();
}

View file

@ -14,6 +14,10 @@
#include <EntityTreeRenderer.h>
#include <NetworkingConstants.h>
#include <NetworkAccessManager.h>
#include <QtNetwork/QNetworkRequest>
#include <QtNetwork/QNetworkReply>
#include <commerce/Ledger.h>
#ifndef MIN
#define MIN(a,b) ((a) < (b) ? (a) : (b))
@ -34,14 +38,14 @@ ContextOverlayInterface::ContextOverlayInterface() {
_tabletScriptingInterface = DependencyManager::get<TabletScriptingInterface>();
_selectionScriptingInterface = DependencyManager::get<SelectionScriptingInterface>();
_selectionToSceneHandler.initialize("contextOverlayHighlightList");
_entityPropertyFlags += PROP_POSITION;
_entityPropertyFlags += PROP_ROTATION;
_entityPropertyFlags += PROP_MARKETPLACE_ID;
_entityPropertyFlags += PROP_DIMENSIONS;
_entityPropertyFlags += PROP_REGISTRATION_POINT;
_entityPropertyFlags += PROP_CERTIFICATE_ID;
_entityPropertyFlags += PROP_CLIENT_ONLY;
_entityPropertyFlags += PROP_OWNING_AVATAR_ID;
auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>().data();
connect(entityScriptingInterface, &EntityScriptingInterface::mousePressOnEntity, this, &ContextOverlayInterface::createOrDestroyContextOverlay);
@ -53,19 +57,39 @@ ContextOverlayInterface::ContextOverlayInterface() {
auto myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
glm::quat cameraOrientation = qApp->getCamera().getOrientation();
QVariantMap props;
props.insert("position", vec3toVariant(myAvatar->getEyePosition() + glm::quat(glm::radians(glm::vec3(0.0f, CONTEXT_OVERLAY_TABLET_OFFSET, 0.0f))) * (CONTEXT_OVERLAY_TABLET_DISTANCE * (cameraOrientation * Vectors::FRONT))));
float sensorToWorldScale = myAvatar->getSensorToWorldScale();
props.insert("position", vec3toVariant(myAvatar->getEyePosition() + glm::quat(glm::radians(glm::vec3(0.0f, CONTEXT_OVERLAY_TABLET_OFFSET, 0.0f))) * ((CONTEXT_OVERLAY_TABLET_DISTANCE * sensorToWorldScale) * (cameraOrientation * Vectors::FRONT))));
props.insert("orientation", quatToVariant(cameraOrientation * glm::quat(glm::radians(glm::vec3(0.0f, CONTEXT_OVERLAY_TABLET_ORIENTATION, 0.0f)))));
qApp->getOverlays().editOverlay(tabletFrameID, props);
_contextOverlayJustClicked = false;
}
});
connect(entityScriptingInterface, &EntityScriptingInterface::deletingEntity, this, &ContextOverlayInterface::deletingEntity);
connect(&qApp->getOverlays(), &Overlays::mousePressOnOverlay, this, &ContextOverlayInterface::contextOverlays_mousePressOnOverlay);
connect(&qApp->getOverlays(), &Overlays::hoverEnterOverlay, this, &ContextOverlayInterface::contextOverlays_hoverEnterOverlay);
connect(&qApp->getOverlays(), &Overlays::hoverLeaveOverlay, this, &ContextOverlayInterface::contextOverlays_hoverLeaveOverlay);
connect(_selectionScriptingInterface.data(), &SelectionScriptingInterface::selectedItemsListChanged, &_selectionToSceneHandler, &SelectionToSceneHandler::selectedItemsListChanged);
{
render::Transaction transaction;
initializeSelectionToSceneHandler(_selectionToSceneHandlers[0], "contextOverlayHighlightList", transaction);
for (auto i = 1; i < MAX_SELECTION_COUNT; i++) {
auto selectionName = QString("highlightList") + QString::number(i);
initializeSelectionToSceneHandler(_selectionToSceneHandlers[i], selectionName, transaction);
}
const render::ScenePointer& scene = qApp->getMain3DScene();
scene->enqueueTransaction(transaction);
}
auto nodeList = DependencyManager::get<NodeList>();
auto& packetReceiver = nodeList->getPacketReceiver();
packetReceiver.registerListener(PacketType::ChallengeOwnershipReply, this, "handleChallengeOwnershipReplyPacket");
_challengeOwnershipTimeoutTimer.setSingleShot(true);
}
void ContextOverlayInterface::initializeSelectionToSceneHandler(SelectionToSceneHandler& handler, const QString& selectionName, render::Transaction& transaction) {
handler.initialize(selectionName);
connect(_selectionScriptingInterface.data(), &SelectionScriptingInterface::selectedItemsListChanged, &handler, &SelectionToSceneHandler::selectedItemsListChanged);
transaction.resetSelectionHighlight(selectionName.toStdString());
}
static const uint32_t MOUSE_HW_ID = 0;
@ -260,6 +284,89 @@ void ContextOverlayInterface::openInspectionCertificate() {
auto tablet = dynamic_cast<TabletProxy*>(_tabletScriptingInterface->getTablet("com.highfidelity.interface.tablet.system"));
tablet->loadQMLSource(INSPECTION_CERTIFICATE_QML_PATH);
_hmdScriptingInterface->openTablet();
setLastInspectedEntity(_currentEntityWithContextOverlay);
EntityItemProperties entityProperties = _entityScriptingInterface->getEntityProperties(_lastInspectedEntity, _entityPropertyFlags);
auto nodeList = DependencyManager::get<NodeList>();
if (entityProperties.getClientOnly()) {
if (entityProperties.verifyStaticCertificateProperties()) {
SharedNodePointer entityServer = nodeList->soloNodeOfType(NodeType::EntityServer);
if (entityServer) {
QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
QNetworkRequest networkRequest;
networkRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
QUrl requestURL = NetworkingConstants::METAVERSE_SERVER_URL;
requestURL.setPath("/api/v1/commerce/proof_of_purchase_status/transfer");
QJsonObject request;
request["certificate_id"] = entityProperties.getCertificateID();
networkRequest.setUrl(requestURL);
QNetworkReply* networkReply = NULL;
networkReply = networkAccessManager.put(networkRequest, QJsonDocument(request).toJson());
connect(networkReply, &QNetworkReply::finished, [=]() {
QJsonObject jsonObject = QJsonDocument::fromJson(networkReply->readAll()).object();
jsonObject = jsonObject["data"].toObject();
if (networkReply->error() == QNetworkReply::NoError) {
if (!jsonObject["invalid_reason"].toString().isEmpty()) {
qCDebug(entities) << "invalid_reason not empty";
} else if (jsonObject["transfer_status"].toArray().first().toString() == "failed") {
qCDebug(entities) << "'transfer_status' is 'failed'";
} else if (jsonObject["transfer_status"].toArray().first().toString() == "pending") {
qCDebug(entities) << "'transfer_status' is 'pending'";
} else {
QString ownerKey = jsonObject["transfer_recipient_key"].toString();
QByteArray certID = entityProperties.getCertificateID().toUtf8();
QByteArray encryptedText = DependencyManager::get<EntityTreeRenderer>()->getTree()->computeEncryptedNonce(certID, ownerKey);
QByteArray nodeToChallengeByteArray = entityProperties.getOwningAvatarID().toRfc4122();
int certIDByteArraySize = certID.length();
int encryptedTextByteArraySize = encryptedText.length();
int nodeToChallengeByteArraySize = nodeToChallengeByteArray.length();
auto challengeOwnershipPacket = NLPacket::create(PacketType::ChallengeOwnershipRequest,
certIDByteArraySize + encryptedTextByteArraySize + nodeToChallengeByteArraySize + 3 * sizeof(int),
true);
challengeOwnershipPacket->writePrimitive(certIDByteArraySize);
challengeOwnershipPacket->writePrimitive(encryptedTextByteArraySize);
challengeOwnershipPacket->writePrimitive(nodeToChallengeByteArraySize);
challengeOwnershipPacket->write(certID);
challengeOwnershipPacket->write(encryptedText);
challengeOwnershipPacket->write(nodeToChallengeByteArray);
nodeList->sendPacket(std::move(challengeOwnershipPacket), *entityServer);
// Kickoff a 10-second timeout timer that marks the cert if we don't get an ownership response in time
if (thread() != QThread::currentThread()) {
QMetaObject::invokeMethod(this, "startChallengeOwnershipTimer");
return;
} else {
startChallengeOwnershipTimer();
}
}
} else {
qCDebug(entities) << "Call to" << networkReply->url() << "failed with error" << networkReply->error() <<
"More info:" << networkReply->readAll();
}
networkReply->deleteLater();
});
} else {
qCWarning(context_overlay) << "Couldn't get Entity Server!";
}
} else {
auto ledger = DependencyManager::get<Ledger>();
_challengeOwnershipTimeoutTimer.stop();
emit ledger->updateCertificateStatus(entityProperties.getCertificateID(), (uint)(ledger->CERTIFICATE_STATUS_STATIC_VERIFICATION_FAILED));
qCDebug(context_overlay) << "Entity" << _lastInspectedEntity << "failed static certificate verification!";
}
}
}
}
@ -293,3 +400,39 @@ void ContextOverlayInterface::deletingEntity(const EntityItemID& entityID) {
destroyContextOverlay(_currentEntityWithContextOverlay, PointerEvent());
}
}
void ContextOverlayInterface::startChallengeOwnershipTimer() {
auto ledger = DependencyManager::get<Ledger>();
EntityItemProperties entityProperties = _entityScriptingInterface->getEntityProperties(_lastInspectedEntity, _entityPropertyFlags);
connect(&_challengeOwnershipTimeoutTimer, &QTimer::timeout, this, [=]() {
qCDebug(entities) << "Ownership challenge timed out for" << _lastInspectedEntity;
emit ledger->updateCertificateStatus(entityProperties.getCertificateID(), (uint)(ledger->CERTIFICATE_STATUS_VERIFICATION_TIMEOUT));
});
_challengeOwnershipTimeoutTimer.start(5000);
}
void ContextOverlayInterface::handleChallengeOwnershipReplyPacket(QSharedPointer<ReceivedMessage> packet, SharedNodePointer sendingNode) {
auto ledger = DependencyManager::get<Ledger>();
_challengeOwnershipTimeoutTimer.stop();
int certIDByteArraySize;
int decryptedTextByteArraySize;
packet->readPrimitive(&certIDByteArraySize);
packet->readPrimitive(&decryptedTextByteArraySize);
QString certID(packet->read(certIDByteArraySize));
QString decryptedText(packet->read(decryptedTextByteArraySize));
EntityItemID id;
bool verificationSuccess = DependencyManager::get<EntityTreeRenderer>()->getTree()->verifyDecryptedNonce(certID, decryptedText, id);
if (verificationSuccess) {
emit ledger->updateCertificateStatus(certID, (uint)(ledger->CERTIFICATE_STATUS_VERIFICATION_SUCCESS));
} else {
emit ledger->updateCertificateStatus(certID, (uint)(ledger->CERTIFICATE_STATUS_OWNER_VERIFICATION_FAILED));
}
}

View file

@ -47,10 +47,12 @@ class ContextOverlayInterface : public QObject, public Dependency {
OverlayID _contextOverlayID { UNKNOWN_OVERLAY_ID };
std::shared_ptr<Image3DOverlay> _contextOverlay { nullptr };
public:
ContextOverlayInterface();
Q_INVOKABLE QUuid getCurrentEntityWithContextOverlay() { return _currentEntityWithContextOverlay; }
void setCurrentEntityWithContextOverlay(const QUuid& entityID) { _currentEntityWithContextOverlay = entityID; }
void setLastInspectedEntity(const QUuid& entityID) { _challengeOwnershipTimeoutTimer.stop(); _lastInspectedEntity = entityID; }
void setEnabled(bool enabled);
bool getEnabled() { return _enabled; }
bool getIsInMarketplaceInspectionMode() { return _isInMarketplaceInspectionMode; }
@ -70,10 +72,19 @@ public slots:
void contextOverlays_hoverLeaveEntity(const EntityItemID& entityID, const PointerEvent& event);
bool contextOverlayFilterPassed(const EntityItemID& entityItemID);
private slots:
void handleChallengeOwnershipReplyPacket(QSharedPointer<ReceivedMessage> packet, SharedNodePointer sendingNode);
private:
enum {
MAX_SELECTION_COUNT = 16
};
bool _verboseLogging { true };
bool _enabled { true };
QUuid _currentEntityWithContextOverlay{};
EntityItemID _currentEntityWithContextOverlay{};
EntityItemID _lastInspectedEntity{};
QString _entityMarketplaceID;
bool _contextOverlayJustClicked { false };
@ -85,8 +96,12 @@ private:
void disableEntityHighlight(const EntityItemID& entityItemID);
void deletingEntity(const EntityItemID& entityItemID);
void initializeSelectionToSceneHandler(SelectionToSceneHandler& handler, const QString& selectionName, render::Transaction& transaction);
SelectionToSceneHandler _selectionToSceneHandler;
SelectionToSceneHandler _selectionToSceneHandlers[MAX_SELECTION_COUNT];
Q_INVOKABLE void startChallengeOwnershipTimer();
QTimer _challengeOwnershipTimeoutTimer;
};
#endif // hifi_ContextOverlayInterface_h

View file

@ -44,7 +44,7 @@ Cube3DOverlay::~Cube3DOverlay() {
}
void Cube3DOverlay::render(RenderArgs* args) {
if (!_visible) {
if (!_renderVisible) {
return; // do nothing if we're not visible
}

View file

@ -53,7 +53,7 @@ AABox Grid3DOverlay::getBounds() const {
}
void Grid3DOverlay::render(RenderArgs* args) {
if (!_visible) {
if (!_renderVisible) {
return; // do nothing if we're not visible
}

View file

@ -60,7 +60,7 @@ void Image3DOverlay::update(float deltatime) {
}
void Image3DOverlay::render(RenderArgs* args) {
if (!_visible || !getParentVisible() || !_texture || !_texture->isLoaded()) {
if (!_renderVisible || !getParentVisible() || !_texture || !_texture->isLoaded()) {
return;
}

View file

@ -33,8 +33,8 @@ Line3DOverlay::Line3DOverlay(const Line3DOverlay* line3DOverlay) :
_length = line3DOverlay->getLength();
_endParentID = line3DOverlay->getEndParentID();
_endParentJointIndex = line3DOverlay->getEndJointIndex();
_lineWidth = line3DOverlay->getLineWidth();
_glow = line3DOverlay->getGlow();
_glowWidth = line3DOverlay->getGlowWidth();
}
Line3DOverlay::~Line3DOverlay() {
@ -96,7 +96,7 @@ void Line3DOverlay::setEnd(const glm::vec3& end) {
} else {
_direction = glm::vec3(0.0f);
}
notifyRenderTransformChange();
notifyRenderVariableChange();
}
void Line3DOverlay::setLocalEnd(const glm::vec3& localEnd) {
@ -123,7 +123,7 @@ AABox Line3DOverlay::getBounds() const {
}
void Line3DOverlay::render(RenderArgs* args) {
if (!_visible) {
if (!_renderVisible) {
return; // do nothing if we're not visible
}
@ -145,7 +145,7 @@ void Line3DOverlay::render(RenderArgs* args) {
geometryCache->renderDashedLine(*batch, start, end, colorv4, _geometryCacheID);
} else {
// renderGlowLine handles both glow = 0 and glow > 0 cases
geometryCache->renderGlowLine(*batch, start, end, colorv4, _glow, _glowWidth, _geometryCacheID);
geometryCache->renderGlowLine(*batch, start, end, colorv4, _glow, _lineWidth, _geometryCacheID);
}
}
}
@ -239,11 +239,10 @@ void Line3DOverlay::setProperties(const QVariantMap& originalProperties) {
}
}
auto glowWidth = properties["glowWidth"];
if (glowWidth.isValid()) {
setGlowWidth(glowWidth.toFloat());
auto lineWidth = properties["lineWidth"];
if (lineWidth.isValid()) {
setLineWidth(lineWidth.toFloat());
}
}
QVariant Line3DOverlay::getProperty(const QString& property) {
@ -262,6 +261,9 @@ QVariant Line3DOverlay::getProperty(const QString& property) {
if (property == "length") {
return QVariant(getLength());
}
if (property == "lineWidth") {
return _lineWidth;
}
return Base3DOverlay::getProperty(property);
}

View file

@ -31,8 +31,8 @@ public:
// getters
glm::vec3 getStart() const;
glm::vec3 getEnd() const;
const float& getLineWidth() const { return _lineWidth; }
const float& getGlow() const { return _glow; }
const float& getGlowWidth() const { return _glowWidth; }
// setters
void setStart(const glm::vec3& start);
@ -41,8 +41,8 @@ public:
void setLocalStart(const glm::vec3& localStart) { setLocalPosition(localStart); }
void setLocalEnd(const glm::vec3& localEnd);
void setLineWidth(const float& lineWidth) { _lineWidth = lineWidth; }
void setGlow(const float& glow) { _glow = glow; }
void setGlowWidth(const float& glowWidth) { _glowWidth = glowWidth; }
void setProperties(const QVariantMap& properties) override;
QVariant getProperty(const QString& property) override;
@ -70,8 +70,9 @@ private:
glm::vec3 _direction; // in parent frame
float _length { 1.0 }; // in parent frame
const float DEFAULT_LINE_WIDTH = 0.02f;
float _lineWidth { DEFAULT_LINE_WIDTH };
float _glow { 0.0 };
float _glowWidth { 0.0 };
int _geometryCacheID;
};

View file

@ -37,7 +37,7 @@ AABox Planar3DOverlay::getBounds() const {
void Planar3DOverlay::setDimensions(const glm::vec2& value) {
_dimensions = value;
notifyRenderTransformChange();
notifyRenderVariableChange();
}
void Planar3DOverlay::setProperties(const QVariantMap& properties) {

View file

@ -23,7 +23,6 @@ Rectangle3DOverlay::Rectangle3DOverlay() :
for (size_t i = 0; i < _rectGeometryIds.size(); ++i) {
_rectGeometryIds[i] = geometryCache->allocateID();
}
qDebug() << "Building rect3d overlay";
}
Rectangle3DOverlay::Rectangle3DOverlay(const Rectangle3DOverlay* rectangle3DOverlay) :
@ -34,11 +33,9 @@ Rectangle3DOverlay::Rectangle3DOverlay(const Rectangle3DOverlay* rectangle3DOver
for (size_t i = 0; i < _rectGeometryIds.size(); ++i) {
_rectGeometryIds[i] = geometryCache->allocateID();
}
qDebug() << "Building rect3d overlay";
}
Rectangle3DOverlay::~Rectangle3DOverlay() {
qDebug() << "Destryoing rect3d overlay";
auto geometryCache = DependencyManager::get<GeometryCache>();
if (geometryCache) {
geometryCache->releaseID(_geometryCacheID);
@ -49,7 +46,7 @@ Rectangle3DOverlay::~Rectangle3DOverlay() {
}
void Rectangle3DOverlay::render(RenderArgs* args) {
if (!_visible) {
if (!_renderVisible) {
return; // do nothing if we're not visible
}
@ -58,18 +55,11 @@ void Rectangle3DOverlay::render(RenderArgs* args) {
const float MAX_COLOR = 255.0f;
glm::vec4 rectangleColor(color.red / MAX_COLOR, color.green / MAX_COLOR, color.blue / MAX_COLOR, alpha);
glm::vec3 position = getWorldPosition();
glm::vec2 dimensions = getDimensions();
glm::vec2 halfDimensions = dimensions * 0.5f;
glm::quat rotation = getWorldOrientation();
auto batch = args->_batch;
if (batch) {
// FIXME Start using the _renderTransform instead of calling for Transform and Dimensions from here, do the custom things needed in evalRenderTransform()
Transform transform;
transform.setTranslation(position);
transform.setRotation(rotation);
Transform transform = getRenderTransform();
glm::vec2 halfDimensions = transform.getScale() * 0.5f;
transform.setScale(1.0f);
batch->setModelTransform(transform);
auto geometryCache = DependencyManager::get<GeometryCache>();
@ -124,8 +114,3 @@ void Rectangle3DOverlay::setProperties(const QVariantMap& properties) {
Rectangle3DOverlay* Rectangle3DOverlay::createClone() const {
return new Rectangle3DOverlay(this);
}
Transform Rectangle3DOverlay::evalRenderTransform() {
return getTransform();
}

View file

@ -29,9 +29,6 @@ public:
virtual Rectangle3DOverlay* createClone() const override;
protected:
Transform evalRenderTransform() override;
private:
int _geometryCacheID;
std::array<int, 4> _rectGeometryIds;

View file

@ -24,7 +24,7 @@ Shape3DOverlay::Shape3DOverlay(const Shape3DOverlay* Shape3DOverlay) :
}
void Shape3DOverlay::render(RenderArgs* args) {
if (!_visible) {
if (!_renderVisible) {
return; // do nothing if we're not visible
}

View file

@ -27,7 +27,7 @@ Sphere3DOverlay::Sphere3DOverlay(const Sphere3DOverlay* Sphere3DOverlay) :
}
void Sphere3DOverlay::render(RenderArgs* args) {
if (!_visible) {
if (!_renderVisible) {
return; // do nothing if we're not visible
}

View file

@ -92,7 +92,7 @@ void Text3DOverlay::update(float deltatime) {
}
void Text3DOverlay::render(RenderArgs* args) {
if (!_visible || !getParentVisible()) {
if (!_renderVisible || !getParentVisible()) {
return; // do nothing if we're not visible
}

View file

@ -28,7 +28,7 @@ AABox Volume3DOverlay::getBounds() const {
void Volume3DOverlay::setDimensions(const glm::vec3& value) {
_localBoundingBox.setBox(-value / 2.0f, value);
notifyRenderTransformChange();
notifyRenderVariableChange();
}
void Volume3DOverlay::setProperties(const QVariantMap& properties) {

View file

@ -268,7 +268,7 @@ Q_INVOKABLE int Web3DOverlay::deviceIdByTouchPoint(qreal x, qreal y) {
}
void Web3DOverlay::render(RenderArgs* args) {
if (!_visible || !getParentVisible()) {
if (!_renderVisible || !getParentVisible()) {
return;
}

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