From 6a61762659e8578b3072d9189311a4d2cfe5fb93 Mon Sep 17 00:00:00 2001 From: David Kelly Date: Wed, 26 Oct 2016 17:41:34 -0700 Subject: [PATCH 01/49] First try AudioMixer has same issue transitioning from silence to encoded audio and back, it seems. This makes that go away, but I don't like it. Better to support a flush function in the encoder but for now lets do this and be sure it is the entire issue. --- assignment-client/src/audio/AudioMixer.cpp | 6 ++++++ assignment-client/src/audio/AudioMixerClientData.cpp | 10 ++++++++++ assignment-client/src/audio/AudioMixerClientData.h | 4 ++++ 3 files changed, 20 insertions(+) diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp index 0252c037bf..e580c62905 100644 --- a/assignment-client/src/audio/AudioMixer.cpp +++ b/assignment-client/src/audio/AudioMixer.cpp @@ -819,6 +819,9 @@ void AudioMixer::broadcastMixes() { std::unique_ptr mixPacket; if (mixHasAudio) { + // may need to flush, this checks + nodeData->flushEncoder(); + int mixPacketBytes = sizeof(quint16) + AudioConstants::MAX_CODEC_NAME_LENGTH_ON_WIRE + AudioConstants::NETWORK_FRAME_BYTES_STEREO; mixPacket = NLPacket::create(PacketType::MixedAudio, mixPacketBytes); @@ -852,6 +855,9 @@ void AudioMixer::broadcastMixes() { // pack number of silent audio samples quint16 numSilentSamples = AudioConstants::NETWORK_FRAME_SAMPLES_STEREO; mixPacket->writePrimitive(numSilentSamples); + + // we will need to flush encoder since we are sending silent packets outside it + nodeData->shouldFlushEncoder(); } // Send audio environment diff --git a/assignment-client/src/audio/AudioMixerClientData.cpp b/assignment-client/src/audio/AudioMixerClientData.cpp index 1eb36cd8a7..a0b96ff39b 100644 --- a/assignment-client/src/audio/AudioMixerClientData.cpp +++ b/assignment-client/src/audio/AudioMixerClientData.cpp @@ -366,6 +366,16 @@ void AudioMixerClientData::sendSelectAudioFormat(SharedNodePointer node, const Q nodeList->sendPacket(std::move(replyPacket), *node); } +void AudioMixerClientData::flushEncoder() { + static QByteArray zeros(AudioConstants::NETWORK_FRAME_BYTES_STEREO, 0); + static QByteArray encodedZeros; + if (_shouldFlushEncoder) { + _shouldFlushEncoder = false; + if (_encoder) { + _encoder->encode(zeros, encodedZeros); + } + } +} void AudioMixerClientData::setupCodec(CodecPluginPointer codec, const QString& codecName) { cleanupCodec(); // cleanup any previously allocated coders first diff --git a/assignment-client/src/audio/AudioMixerClientData.h b/assignment-client/src/audio/AudioMixerClientData.h index 2f8ff4d049..51f06b41c4 100644 --- a/assignment-client/src/audio/AudioMixerClientData.h +++ b/assignment-client/src/audio/AudioMixerClientData.h @@ -77,6 +77,8 @@ public: encodedBuffer = decodedBuffer; } } + void flushEncoder(); + void shouldFlushEncoder() { _shouldFlushEncoder = true; } QString getCodecName() { return _selectedCodecName; } @@ -105,6 +107,8 @@ private: QString _selectedCodecName; Encoder* _encoder{ nullptr }; // for outbound mixed stream Decoder* _decoder{ nullptr }; // for mic stream + + bool _shouldFlushEncoder { false }; }; #endif // hifi_AudioMixerClientData_h From 3e16dabb75c2796b900ac983469c85d082d1272f Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Tue, 25 Oct 2016 17:42:57 -0700 Subject: [PATCH 02/49] fix audio mixer throttling --- assignment-client/src/audio/AudioMixer.cpp | 45 +++++++++++----------- 1 file changed, 23 insertions(+), 22 deletions(-) diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp index 0252c037bf..0f3318bf24 100644 --- a/assignment-client/src/audio/AudioMixer.cpp +++ b/assignment-client/src/audio/AudioMixer.cpp @@ -730,30 +730,25 @@ void AudioMixer::domainSettingsRequestComplete() { } void AudioMixer::broadcastMixes() { + const int TRAILING_AVERAGE_FRAMES = 100; + const float CURRENT_FRAME_RATIO = 1.0f / TRAILING_AVERAGE_FRAMES; + const float PREVIOUS_FRAMES_RATIO = 1.0f - CURRENT_FRAME_RATIO; + + const float STRUGGLE_TRIGGER_SLEEP_PERCENTAGE_THRESHOLD = 0.10f; + const float BACK_OFF_TRIGGER_SLEEP_PERCENTAGE_THRESHOLD = 0.20f; + + const float RATIO_BACK_OFF = 0.02f; + auto nodeList = DependencyManager::get(); auto nextFrameTimestamp = p_high_resolution_clock::now(); auto timeToSleep = std::chrono::microseconds(0); - const int TRAILING_AVERAGE_FRAMES = 100; + int currentFrame = 1; + int numFramesPerSecond = (int) ceil(AudioConstants::NETWORK_FRAMES_PER_SEC); int framesSinceCutoffEvent = TRAILING_AVERAGE_FRAMES; - int currentFrame { 1 }; - int numFramesPerSecond { (int) ceil(AudioConstants::NETWORK_FRAMES_PER_SEC) }; - while (!_isFinished) { - const float STRUGGLE_TRIGGER_SLEEP_PERCENTAGE_THRESHOLD = 0.10f; - const float BACK_OFF_TRIGGER_SLEEP_PERCENTAGE_THRESHOLD = 0.20f; - - const float RATIO_BACK_OFF = 0.02f; - - const float CURRENT_FRAME_RATIO = 1.0f / TRAILING_AVERAGE_FRAMES; - const float PREVIOUS_FRAMES_RATIO = 1.0f - CURRENT_FRAME_RATIO; - - if (timeToSleep.count() < 0) { - timeToSleep = std::chrono::microseconds(0); - } - _trailingSleepRatio = (PREVIOUS_FRAMES_RATIO * _trailingSleepRatio) + (timeToSleep.count() * CURRENT_FRAME_RATIO / (float) AudioConstants::NETWORK_FRAME_USECS); @@ -886,14 +881,20 @@ void AudioMixer::broadcastMixes() { break; } - // push the next frame timestamp to when we should send the next - nextFrameTimestamp += std::chrono::microseconds(AudioConstants::NETWORK_FRAME_USECS); + // sleep until the next frame, if necessary + { + nextFrameTimestamp += std::chrono::microseconds(AudioConstants::NETWORK_FRAME_USECS); - // sleep as long as we need until next frame, if we can - auto now = p_high_resolution_clock::now(); - timeToSleep = std::chrono::duration_cast(nextFrameTimestamp - now); + auto now = p_high_resolution_clock::now(); + timeToSleep = std::chrono::duration_cast(nextFrameTimestamp - now); - std::this_thread::sleep_for(timeToSleep); + if (timeToSleep.count() < 0) { + nextFrameTimestamp = now; + timeToSleep = std::chrono::microseconds(0); + } + + std::this_thread::sleep_for(timeToSleep); + } } } From 04e5d8e302af110ff3b2c558ce8c729ac98704c2 Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Wed, 26 Oct 2016 12:18:15 -0700 Subject: [PATCH 03/49] initialize audio client data --- assignment-client/src/audio/AudioMixer.cpp | 34 ++++++++++------------ assignment-client/src/audio/AudioMixer.h | 1 + 2 files changed, 17 insertions(+), 18 deletions(-) diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp index 0f3318bf24..771ad77aa6 100644 --- a/assignment-client/src/audio/AudioMixer.cpp +++ b/assignment-client/src/audio/AudioMixer.cpp @@ -90,8 +90,8 @@ AudioMixer::AudioMixer(ReceivedMessage& message) : PacketType::InjectAudio, PacketType::SilentAudioFrame, PacketType::AudioStreamStats }, this, "handleNodeAudioPacket"); - packetReceiver.registerListener(PacketType::MuteEnvironment, this, "handleMuteEnvironmentPacket"); packetReceiver.registerListener(PacketType::NegotiateAudioFormat, this, "handleNegotiateAudioFormat"); + packetReceiver.registerListener(PacketType::MuteEnvironment, this, "handleMuteEnvironmentPacket"); packetReceiver.registerListener(PacketType::NodeIgnoreRequest, this, "handleNodeIgnoreRequestPacket"); connect(nodeList.data(), &NodeList::nodeKilled, this, &AudioMixer::handleNodeKilled); @@ -481,6 +481,7 @@ void AudioMixer::sendAudioEnvironmentPacket(SharedNodePointer node) { } void AudioMixer::handleNodeAudioPacket(QSharedPointer message, SharedNodePointer sendingNode) { + getOrCreateClientData(sendingNode.data()); DependencyManager::get()->updateNodeWithDataFromPacket(message, sendingNode); } @@ -579,18 +580,8 @@ void AudioMixer::handleNegotiateAudioFormat(QSharedPointer mess } } - auto clientData = dynamic_cast(sendingNode->getLinkedData()); - - // FIXME - why would we not have client data at this point?? - if (!clientData) { - qDebug() << "UNEXPECTED -- didn't have node linked data in " << __FUNCTION__; - sendingNode->setLinkedData(std::unique_ptr { new AudioMixerClientData(sendingNode->getUUID()) }); - clientData = dynamic_cast(sendingNode->getLinkedData()); - connect(clientData, &AudioMixerClientData::injectorStreamFinished, this, &AudioMixer::removeHRTFsForFinishedInjector); - } - + auto clientData = getOrCreateClientData(sendingNode.data()); clientData->setupCodec(selectedCodec, selectedCodecName); - qDebug() << "selectedCodecName:" << selectedCodecName; clientData->sendSelectAudioFormat(sendingNode, selectedCodecName); } @@ -707,17 +698,24 @@ void AudioMixer::run() { ThreadedAssignment::commonInit(AUDIO_MIXER_LOGGING_TARGET_NAME, NodeType::AudioMixer); } +AudioMixerClientData* AudioMixer::getOrCreateClientData(Node* node) { + auto clientData = dynamic_cast(node->getLinkedData()); + + if (!clientData) { + node->setLinkedData(std::unique_ptr { new AudioMixerClientData(node->getUUID()) }); + clientData = dynamic_cast(node->getLinkedData()); + connect(clientData, &AudioMixerClientData::injectorStreamFinished, this, &AudioMixer::removeHRTFsForFinishedInjector); + } + + return clientData; +} + void AudioMixer::domainSettingsRequestComplete() { auto nodeList = DependencyManager::get(); nodeList->addNodeTypeToInterestSet(NodeType::Agent); - nodeList->linkedDataCreateCallback = [&](Node* node) { - node->setLinkedData(std::unique_ptr { new AudioMixerClientData(node->getUUID()) }); - auto clientData = dynamic_cast(node->getLinkedData()); - - connect(clientData, &AudioMixerClientData::injectorStreamFinished, this, &AudioMixer::removeHRTFsForFinishedInjector); - }; + nodeList->linkedDataCreateCallback = [&](Node* node) { getOrCreateClientData(node); }; DomainHandler& domainHandler = nodeList->getDomainHandler(); const QJsonObject& settingsObject = domainHandler.getSettingsObject(); diff --git a/assignment-client/src/audio/AudioMixer.h b/assignment-client/src/audio/AudioMixer.h index bccac529c1..9caa39736b 100644 --- a/assignment-client/src/audio/AudioMixer.h +++ b/assignment-client/src/audio/AudioMixer.h @@ -52,6 +52,7 @@ private slots: void removeHRTFsForFinishedInjector(const QUuid& streamID); private: + AudioMixerClientData* getOrCreateClientData(Node* node); void domainSettingsRequestComplete(); /// adds one stream to the mix for a listening node From 9e9f8666cec5d5e8ad8131d78d7abd93923e8a9a Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Thu, 27 Oct 2016 13:36:14 -0700 Subject: [PATCH 04/49] clean up handleMismatchAudioFormat comment --- assignment-client/src/audio/AudioMixerClientData.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/assignment-client/src/audio/AudioMixerClientData.cpp b/assignment-client/src/audio/AudioMixerClientData.cpp index 1eb36cd8a7..d4866d2fa6 100644 --- a/assignment-client/src/audio/AudioMixerClientData.cpp +++ b/assignment-client/src/audio/AudioMixerClientData.cpp @@ -355,7 +355,10 @@ QJsonObject AudioMixerClientData::getAudioStreamStats() { } void AudioMixerClientData::handleMismatchAudioFormat(SharedNodePointer node, const QString& currentCodec, const QString& recievedCodec) { - qDebug() << __FUNCTION__ << "sendingNode:" << *node << "currentCodec:" << currentCodec << "recievedCodec:" << recievedCodec; + qDebug() << __FUNCTION__ << + "sendingNode:" << *node << + "currentCodec:" << currentCodec << + "receivedCodec:" << recievedCodec; sendSelectAudioFormat(node, currentCodec); } From 20bac5aeef00a7c8aab71626c59f5e1274087daa Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Thu, 27 Oct 2016 13:38:16 -0700 Subject: [PATCH 05/49] clean up broadcastMixes --- assignment-client/src/audio/AudioMixer.cpp | 78 +++++++++++----------- 1 file changed, 39 insertions(+), 39 deletions(-) diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp index 771ad77aa6..b4db08ada0 100644 --- a/assignment-client/src/audio/AudioMixer.cpp +++ b/assignment-client/src/audio/AudioMixer.cpp @@ -747,48 +747,46 @@ void AudioMixer::broadcastMixes() { int framesSinceCutoffEvent = TRAILING_AVERAGE_FRAMES; while (!_isFinished) { - _trailingSleepRatio = (PREVIOUS_FRAMES_RATIO * _trailingSleepRatio) - + (timeToSleep.count() * CURRENT_FRAME_RATIO / (float) AudioConstants::NETWORK_FRAME_USECS); + // manage mixer load + { + _trailingSleepRatio = (PREVIOUS_FRAMES_RATIO * _trailingSleepRatio) + + // ratio of frame spent sleeping / total frame time + ((CURRENT_FRAME_RATIO * timeToSleep.count()) / (float) AudioConstants::NETWORK_FRAME_USECS); - float lastCutoffRatio = _performanceThrottlingRatio; - bool hasRatioChanged = false; + float lastCutoffRatio = _performanceThrottlingRatio; + bool hasRatioChanged = false; - if (framesSinceCutoffEvent >= TRAILING_AVERAGE_FRAMES) { - if (_trailingSleepRatio <= STRUGGLE_TRIGGER_SLEEP_PERCENTAGE_THRESHOLD) { - // we're struggling - change our min required loudness to reduce some load - _performanceThrottlingRatio = _performanceThrottlingRatio + (0.5f * (1.0f - _performanceThrottlingRatio)); - - qDebug() << "Mixer is struggling, sleeping" << _trailingSleepRatio * 100 << "% of frame time. Old cutoff was" - << lastCutoffRatio << "and is now" << _performanceThrottlingRatio; - hasRatioChanged = true; - } else if (_trailingSleepRatio >= BACK_OFF_TRIGGER_SLEEP_PERCENTAGE_THRESHOLD && _performanceThrottlingRatio != 0) { - // we've recovered and can back off the required loudness - _performanceThrottlingRatio = _performanceThrottlingRatio - RATIO_BACK_OFF; - - if (_performanceThrottlingRatio < 0) { - _performanceThrottlingRatio = 0; + if (framesSinceCutoffEvent >= TRAILING_AVERAGE_FRAMES) { + if (_trailingSleepRatio <= STRUGGLE_TRIGGER_SLEEP_PERCENTAGE_THRESHOLD) { + qDebug() << "Mixer is struggling"; + // change our min required loudness to reduce some load + _performanceThrottlingRatio = _performanceThrottlingRatio + (0.5f * (1.0f - _performanceThrottlingRatio)); + hasRatioChanged = true; + } else if (_trailingSleepRatio >= BACK_OFF_TRIGGER_SLEEP_PERCENTAGE_THRESHOLD && _performanceThrottlingRatio != 0) { + qDebug() << "Mixer is recovering"; + // back off the required loudness + _performanceThrottlingRatio = std::max(0.0f, _performanceThrottlingRatio - RATIO_BACK_OFF); + hasRatioChanged = true; } - qDebug() << "Mixer is recovering, sleeping" << _trailingSleepRatio * 100 << "% of frame time. Old cutoff was" - << lastCutoffRatio << "and is now" << _performanceThrottlingRatio; - hasRatioChanged = true; + if (hasRatioChanged) { + // set out min audability threshold from the new ratio + _minAudibilityThreshold = LOUDNESS_TO_DISTANCE_RATIO / (2.0f * (1.0f - _performanceThrottlingRatio)); + framesSinceCutoffEvent = 0; + + qDebug() << "Sleeping" << _trailingSleepRatio << "of frame"; + qDebug() << "Cutoff is" << _performanceThrottlingRatio; + qDebug() << "Minimum audibility to be mixed is" << _minAudibilityThreshold; + } } - if (hasRatioChanged) { - // set out min audability threshold from the new ratio - _minAudibilityThreshold = LOUDNESS_TO_DISTANCE_RATIO / (2.0f * (1.0f - _performanceThrottlingRatio)); - qDebug() << "Minimum audability required to be mixed is now" << _minAudibilityThreshold; - - framesSinceCutoffEvent = 0; + if (!hasRatioChanged) { + ++framesSinceCutoffEvent; } } - if (!hasRatioChanged) { - ++framesSinceCutoffEvent; - } - + // mix nodeList->eachNode([&](const SharedNodePointer& node) { - if (node->getLinkedData()) { AudioMixerClientData* nodeData = (AudioMixerClientData*)node->getLinkedData(); @@ -869,14 +867,16 @@ void AudioMixer::broadcastMixes() { ++_numStatFrames; - // since we're a while loop we need to help Qt's event processing - QCoreApplication::processEvents(); + // play nice with qt event-looping + { + // since we're a while loop we need to help qt's event processing + QCoreApplication::processEvents(); - if (_isFinished) { - // at this point the audio-mixer is done - // check if we have a deferred delete event to process (which we should once finished) - QCoreApplication::sendPostedEvents(this, QEvent::DeferredDelete); - break; + if (_isFinished) { + // alert qt that this is finished + QCoreApplication::sendPostedEvents(this, QEvent::DeferredDelete); + break; + } } // sleep until the next frame, if necessary From 68f05bee8767bc04945dd2ddeb1d12c68be40272 Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Thu, 27 Oct 2016 13:33:57 -0700 Subject: [PATCH 06/49] fix underflow for agent audio timer --- assignment-client/src/AvatarAudioTimer.cpp | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/assignment-client/src/AvatarAudioTimer.cpp b/assignment-client/src/AvatarAudioTimer.cpp index 857209df7c..77dd61043e 100644 --- a/assignment-client/src/AvatarAudioTimer.cpp +++ b/assignment-client/src/AvatarAudioTimer.cpp @@ -15,19 +15,23 @@ // this should send a signal every 10ms, with pretty good precision. Hardcoding // to 10ms since that's what you'd want for audio. void AvatarAudioTimer::start() { - qDebug() << "AvatarAudioTimer::start called"; + qDebug() << __FUNCTION__; auto startTime = usecTimestampNow(); quint64 frameCounter = 0; const int TARGET_INTERVAL_USEC = 10000; // 10ms while (!_quit) { - frameCounter++; - // simplest possible timer + ++frameCounter; + + // tick every 10ms from startTime quint64 targetTime = startTime + frameCounter * TARGET_INTERVAL_USEC; - quint64 interval = std::max((quint64)0, targetTime - usecTimestampNow()); - usleep(interval); + quint64 now = usecTimestampNow(); + + // avoid quint64 underflow + if (now < targetTime) { + usleep(targetTime - now); + } + emit avatarTick(); } qDebug() << "AvatarAudioTimer is finished"; } - - From 32040a904740c8c2fede6c41ddc217cd8fdf554e Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Thu, 27 Oct 2016 13:55:17 -0700 Subject: [PATCH 07/49] add stat for streams in audio mixer --- assignment-client/src/audio/AudioMixer.cpp | 6 ++++-- assignment-client/src/audio/AudioMixer.h | 1 + assignment-client/src/audio/AudioMixerClientData.cpp | 4 +++- assignment-client/src/audio/AudioMixerClientData.h | 3 ++- 4 files changed, 10 insertions(+), 4 deletions(-) diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp index 0252c037bf..0ee81910a4 100644 --- a/assignment-client/src/audio/AudioMixer.cpp +++ b/assignment-client/src/audio/AudioMixer.cpp @@ -646,7 +646,8 @@ void AudioMixer::sendStatsPacket() { statsObject["trailing_sleep_percentage"] = _trailingSleepRatio * 100.0f; statsObject["performance_throttling_ratio"] = _performanceThrottlingRatio; - statsObject["avg_listeners_per_frame"] = (float) _sumListeners / (float) _numStatFrames; + statsObject["avg_streams_per_frame"] = (float)_sumStreams / (float)_numStatFrames; + statsObject["avg_listeners_per_frame"] = (float)_sumListeners / (float)_numStatFrames; QJsonObject mixStats; mixStats["%_hrtf_mixes"] = percentageForMixStats(_hrtfRenders); @@ -660,6 +661,7 @@ void AudioMixer::sendStatsPacket() { statsObject["mix_stats"] = mixStats; + _sumStreams = 0; _sumListeners = 0; _hrtfRenders = 0; _hrtfSilentRenders = 0; @@ -802,7 +804,7 @@ void AudioMixer::broadcastMixes() { // this function will attempt to pop a frame from each audio stream. // a pointer to the popped data is stored as a member in InboundAudioStream. // That's how the popped audio data will be read for mixing (but only if the pop was successful) - nodeData->checkBuffersBeforeFrameSend(); + _sumStreams += nodeData->checkBuffersBeforeFrameSend(); // if the stream should be muted, send mute packet if (nodeData->getAvatarAudioStream() diff --git a/assignment-client/src/audio/AudioMixer.h b/assignment-client/src/audio/AudioMixer.h index bccac529c1..ef0cb5bd89 100644 --- a/assignment-client/src/audio/AudioMixer.h +++ b/assignment-client/src/audio/AudioMixer.h @@ -85,6 +85,7 @@ private: float _attenuationPerDoublingInDistance; float _noiseMutingThreshold; int _numStatFrames { 0 }; + int _sumStreams { 0 }; int _sumListeners { 0 }; int _hrtfRenders { 0 }; int _hrtfSilentRenders { 0 }; diff --git a/assignment-client/src/audio/AudioMixerClientData.cpp b/assignment-client/src/audio/AudioMixerClientData.cpp index 1eb36cd8a7..9fb698cf19 100644 --- a/assignment-client/src/audio/AudioMixerClientData.cpp +++ b/assignment-client/src/audio/AudioMixerClientData.cpp @@ -180,7 +180,7 @@ int AudioMixerClientData::parseData(ReceivedMessage& message) { return 0; } -void AudioMixerClientData::checkBuffersBeforeFrameSend() { +int AudioMixerClientData::checkBuffersBeforeFrameSend() { QWriteLocker writeLocker { &_streamsLock }; auto it = _audioStreams.begin(); @@ -208,6 +208,8 @@ void AudioMixerClientData::checkBuffersBeforeFrameSend() { ++it; } } + + return _audioStreams.size(); } bool AudioMixerClientData::shouldSendStats(int frameNumber) { diff --git a/assignment-client/src/audio/AudioMixerClientData.h b/assignment-client/src/audio/AudioMixerClientData.h index 2f8ff4d049..f12df5bd82 100644 --- a/assignment-client/src/audio/AudioMixerClientData.h +++ b/assignment-client/src/audio/AudioMixerClientData.h @@ -52,7 +52,8 @@ public: int parseData(ReceivedMessage& message) override; - void checkBuffersBeforeFrameSend(); + // attempt to pop a frame from each audio stream, and return the number of streams from this client + int checkBuffersBeforeFrameSend(); void removeDeadInjectedStreams(); From d00a73dde1c937e6e43ea54a1607f131fd4ee9c7 Mon Sep 17 00:00:00 2001 From: David Kelly Date: Thu, 27 Oct 2016 14:27:15 -0700 Subject: [PATCH 08/49] Play the flushed encoder sound at end Basically 0-padding the end of the sound buffer. Next do that in AudioMixer and we should be good. --- assignment-client/src/Agent.cpp | 31 +++++++++++++++---------------- assignment-client/src/Agent.h | 2 +- 2 files changed, 16 insertions(+), 17 deletions(-) diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index 9e6ec1209c..5459a0a148 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -474,10 +474,9 @@ void Agent::processAgentAvatar() { nodeList->broadcastToNodes(std::move(avatarPacket), NodeSet() << NodeType::AvatarMixer); } } -void Agent::flushEncoder() { +void Agent::flushEncoder(QByteArray& encodedZeros) { _flushEncoder = false; - static QByteArray zeros(AudioConstants::NETWORK_FRAME_BYTES_PER_CHANNEL, 0); - static QByteArray encodedZeros; + static const QByteArray zeros(AudioConstants::NETWORK_FRAME_BYTES_PER_CHANNEL, 0); if (_encoder) { _encoder->encode(zeros, encodedZeros); } @@ -485,13 +484,6 @@ void Agent::flushEncoder() { void Agent::processAgentAvatarAudio() { if (_isAvatar && (_isListeningToAudioStream || _avatarSound)) { - // after sound is done playing, encoder has a bit of state in it, - // and needs some 0s to forget or you get a little click next time - // you play something - if (_flushEncoder) { - flushEncoder(); - } - // if we have an avatar audio stream then send it out to our audio-mixer auto scriptedAvatar = DependencyManager::get(); bool silentFrame = true; @@ -528,7 +520,7 @@ void Agent::processAgentAvatarAudio() { } } - auto audioPacket = NLPacket::create(silentFrame + auto audioPacket = NLPacket::create(silentFrame && !_flushEncoder ? PacketType::SilentAudioFrame : PacketType::MicrophoneAudioNoEcho); @@ -564,13 +556,20 @@ void Agent::processAgentAvatarAudio() { glm::quat headOrientation = scriptedAvatar->getHeadOrientation(); audioPacket->writePrimitive(headOrientation); - QByteArray decodedBuffer(reinterpret_cast(nextSoundOutput), numAvailableSamples*sizeof(int16_t)); QByteArray encodedBuffer; - // encode it - if(_encoder) { - _encoder->encode(decodedBuffer, encodedBuffer); + if (_flushEncoder) { + // after sound is done playing, encoder has a bit of state in it, + // and needs some 0s to forget or you get a little click next time + // you play something. So, basically 0-pad the end of the sound buffer + flushEncoder(encodedBuffer); } else { - encodedBuffer = decodedBuffer; + // encode it + QByteArray decodedBuffer(reinterpret_cast(nextSoundOutput), numAvailableSamples*sizeof(int16_t)); + if (_encoder) { + _encoder->encode(decodedBuffer, encodedBuffer); + } else { + encodedBuffer = decodedBuffer; + } } audioPacket->write(encodedBuffer.constData(), encodedBuffer.size()); } diff --git a/assignment-client/src/Agent.h b/assignment-client/src/Agent.h index b882ac3125..b11e492136 100644 --- a/assignment-client/src/Agent.h +++ b/assignment-client/src/Agent.h @@ -81,7 +81,7 @@ signals: private: void negotiateAudioFormat(); void selectAudioFormat(const QString& selectedCodecName); - void flushEncoder(); + void flushEncoder(QByteArray& encodedZeros); std::unique_ptr _scriptEngine; EntityEditPacketSender _entityEditSender; From c14a928db5957f9338529b6fd0126ccb557cf57e Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Thu, 27 Oct 2016 14:34:10 -0700 Subject: [PATCH 09/49] convert size_t to int --- assignment-client/src/audio/AudioMixerClientData.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assignment-client/src/audio/AudioMixerClientData.cpp b/assignment-client/src/audio/AudioMixerClientData.cpp index 9fb698cf19..b2d88ebac1 100644 --- a/assignment-client/src/audio/AudioMixerClientData.cpp +++ b/assignment-client/src/audio/AudioMixerClientData.cpp @@ -209,7 +209,7 @@ int AudioMixerClientData::checkBuffersBeforeFrameSend() { } } - return _audioStreams.size(); + return (int)_audioStreams.size(); } bool AudioMixerClientData::shouldSendStats(int frameNumber) { From a7d54b8e0ada88e481df912c0a92a617f8581816 Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Thu, 27 Oct 2016 14:38:17 -0700 Subject: [PATCH 10/49] rm unused lastCutoffRatio --- assignment-client/src/audio/AudioMixer.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp index b4db08ada0..8463c282a0 100644 --- a/assignment-client/src/audio/AudioMixer.cpp +++ b/assignment-client/src/audio/AudioMixer.cpp @@ -753,7 +753,6 @@ void AudioMixer::broadcastMixes() { // ratio of frame spent sleeping / total frame time ((CURRENT_FRAME_RATIO * timeToSleep.count()) / (float) AudioConstants::NETWORK_FRAME_USECS); - float lastCutoffRatio = _performanceThrottlingRatio; bool hasRatioChanged = false; if (framesSinceCutoffEvent >= TRAILING_AVERAGE_FRAMES) { From 428bb8e518774d65a884daf5cc267c5d5b7631d0 Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Thu, 27 Oct 2016 14:40:51 -0700 Subject: [PATCH 11/49] rm unused OffscreenQmlSurface const --- libraries/gl/src/gl/OffscreenQmlSurface.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/libraries/gl/src/gl/OffscreenQmlSurface.cpp b/libraries/gl/src/gl/OffscreenQmlSurface.cpp index c82c2b4a32..06f755c1dd 100644 --- a/libraries/gl/src/gl/OffscreenQmlSurface.cpp +++ b/libraries/gl/src/gl/OffscreenQmlSurface.cpp @@ -344,7 +344,6 @@ bool OffscreenQmlSurface::allowNewFrame(uint8_t fps) { OffscreenQmlSurface::OffscreenQmlSurface() { } -static const uint64_t MAX_SHUTDOWN_WAIT_SECS = 2; OffscreenQmlSurface::~OffscreenQmlSurface() { QObject::disconnect(&_updateTimer); QObject::disconnect(qApp); From 5e4f5391442ecf233934a744c213cc2e7fe22154 Mon Sep 17 00:00:00 2001 From: David Kelly Date: Thu, 27 Oct 2016 14:46:04 -0700 Subject: [PATCH 12/49] AudioMixer flushes now And protected agent from case with no codec --- assignment-client/src/Agent.cpp | 2 ++ assignment-client/src/audio/AudioMixer.cpp | 14 ++++++++------ .../src/audio/AudioMixerClientData.cpp | 7 ++++--- assignment-client/src/audio/AudioMixerClientData.h | 6 ++++-- 4 files changed, 18 insertions(+), 11 deletions(-) diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index 5459a0a148..5b299ea6a9 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -479,6 +479,8 @@ void Agent::flushEncoder(QByteArray& encodedZeros) { static const QByteArray zeros(AudioConstants::NETWORK_FRAME_BYTES_PER_CHANNEL, 0); if (_encoder) { _encoder->encode(zeros, encodedZeros); + } else { + encodedZeros = zeros; } } diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp index e580c62905..ad4e6c5138 100644 --- a/assignment-client/src/audio/AudioMixer.cpp +++ b/assignment-client/src/audio/AudioMixer.cpp @@ -818,9 +818,7 @@ void AudioMixer::broadcastMixes() { std::unique_ptr mixPacket; - if (mixHasAudio) { - // may need to flush, this checks - nodeData->flushEncoder(); + if (mixHasAudio || nodeData->shouldFlushEncoder()) { int mixPacketBytes = sizeof(quint16) + AudioConstants::MAX_CODEC_NAME_LENGTH_ON_WIRE + AudioConstants::NETWORK_FRAME_BYTES_STEREO; @@ -834,10 +832,14 @@ void AudioMixer::broadcastMixes() { QString codecInPacket = nodeData->getCodecName(); mixPacket->writeString(codecInPacket); - QByteArray decodedBuffer(reinterpret_cast(_clampedSamples), AudioConstants::NETWORK_FRAME_BYTES_STEREO); QByteArray encodedBuffer; - nodeData->encode(decodedBuffer, encodedBuffer); - + if ( mixHasAudio) { + QByteArray decodedBuffer(reinterpret_cast(_clampedSamples), AudioConstants::NETWORK_FRAME_BYTES_STEREO); + nodeData->encode(decodedBuffer, encodedBuffer); + } else { + // time to flush, which resets the shouldFlush until next time we encode something + nodeData->flushEncoder(encodedBuffer); + } // pack mixed audio samples mixPacket->write(encodedBuffer.constData(), encodedBuffer.size()); } else { diff --git a/assignment-client/src/audio/AudioMixerClientData.cpp b/assignment-client/src/audio/AudioMixerClientData.cpp index a0b96ff39b..4084df7090 100644 --- a/assignment-client/src/audio/AudioMixerClientData.cpp +++ b/assignment-client/src/audio/AudioMixerClientData.cpp @@ -366,15 +366,16 @@ void AudioMixerClientData::sendSelectAudioFormat(SharedNodePointer node, const Q nodeList->sendPacket(std::move(replyPacket), *node); } -void AudioMixerClientData::flushEncoder() { +void AudioMixerClientData::flushEncoder(QByteArray& encodedZeros) { static QByteArray zeros(AudioConstants::NETWORK_FRAME_BYTES_STEREO, 0); - static QByteArray encodedZeros; if (_shouldFlushEncoder) { - _shouldFlushEncoder = false; if (_encoder) { _encoder->encode(zeros, encodedZeros); + } else { + encodedZeros = zeros; } } + _shouldFlushEncoder = false; } void AudioMixerClientData::setupCodec(CodecPluginPointer codec, const QString& codecName) { diff --git a/assignment-client/src/audio/AudioMixerClientData.h b/assignment-client/src/audio/AudioMixerClientData.h index 51f06b41c4..ba8e5c021b 100644 --- a/assignment-client/src/audio/AudioMixerClientData.h +++ b/assignment-client/src/audio/AudioMixerClientData.h @@ -76,9 +76,11 @@ public: } else { encodedBuffer = decodedBuffer; } + // once you have encoded, you need to flush eventually. + _shouldFlushEncoder = true; } - void flushEncoder(); - void shouldFlushEncoder() { _shouldFlushEncoder = true; } + void flushEncoder(QByteArray& encodedZeros); + bool shouldFlushEncoder() { return _shouldFlushEncoder; } QString getCodecName() { return _selectedCodecName; } From e9099b77a51020fd9e214471d04d8010c5585a1d Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Tue, 11 Oct 2016 16:44:08 -0700 Subject: [PATCH 13/49] Add more debug to tutorial, new firecrackers, and adjust fuse --- .../system/controllers/controllerDisplay.js | 20 ++- tutorial/entityData.js | 33 ++-- tutorial/firePit/fire.js | 29 ++- tutorial/fuse.js | 33 +++- tutorial/fuseCollider.js | 5 +- tutorial/tutorial.js | 170 +++++++++++------- tutorial/tutorialEntityIDs.js | 166 ++++++++++++++++- 7 files changed, 356 insertions(+), 100 deletions(-) diff --git a/scripts/system/controllers/controllerDisplay.js b/scripts/system/controllers/controllerDisplay.js index f42ac3aeda..96ee955d19 100644 --- a/scripts/system/controllers/controllerDisplay.js +++ b/scripts/system/controllers/controllerDisplay.js @@ -31,14 +31,26 @@ function resolveHardware(path) { return resolveInner(Controller.Hardware, parts, 0); } +var DEBUG = true; +function debug() { + if (DEBUG) { + var args = Array.prototype.slice.call(arguments); + args.unshift("controllerDisplay.js | "); + print.apply(this, args); + } +} + createControllerDisplay = function(config) { var controllerDisplay = { overlays: [], - partOverlays: {}, - parts: {}, - mappingName: "mapping-display", + partOverlays: { + }, + parts: { + }, + mappingName: "mapping-display-" + Math.random(), setVisible: function(visible) { + debug("Setting visible", this.overlays.length); for (var i = 0; i < this.overlays.length; ++i) { Overlays.editOverlay(this.overlays[i], { visible: visible @@ -166,7 +178,7 @@ createControllerDisplay = function(config) { } else if (part.type === "static") { // do nothing } else { - print("TYPE NOT SUPPORTED: ", part.type); + debug("TYPE NOT SUPPORTED: ", part.type); } controllerDisplay.overlays.push(overlayID); diff --git a/tutorial/entityData.js b/tutorial/entityData.js index 76eb4d98ed..407e6e6b00 100644 --- a/tutorial/entityData.js +++ b/tutorial/entityData.js @@ -1,12 +1,13 @@ -birdFirework1 = { - "clientOnly": 0, - "collisionsWillMove": 1, - "created": "2016-09-13T23:05:08Z", - "dimensions": { - "x": 0.10120716691017151, - "y": 0.12002291530370712, - "z": 0.18833979964256287 - }, +fireworkURLs = [ + "atp:/tutorial_models/bomb1.fbx", + "atp:/tutorial_models/bomb2.fbx", + "atp:/tutorial_models/bomb3.fbx", + "atp:/tutorial_models/bomb4.fbx", + "atp:/tutorial_models/bomb5.fbx", + "atp:/tutorial_models/bomb6.fbx", +]; + +fireworkBaseProps = { "collisionsWillMove": 1, velocity: { x: 0, @@ -20,7 +21,7 @@ birdFirework1 = { "z": 0 }, "id": "{1c4061bc-b2e7-4435-bc47-3fcc39ae6624}", - "modelURL": "atp:/tutorial_models/birdStatue15.fbx", + "modelURL": "atp:/tutorial_models/bomb1.fbx", "owningAvatarID": "{00000000-0000-0000-0000-000000000000}", "position": { "x": 0.11612319946289062, @@ -44,15 +45,8 @@ birdFirework1 = { "userData": "{\n \"hifiHomeKey\": {\n \"reset\": true\n }\n}" } ; + birdFirework2 = { - "clientOnly": 0, - "collisionsWillMove": 1, - "created": "2016-09-12T22:56:48Z", - "dimensions": { - "x": 0.098819166421890259, - "y": 0.11143554747104645, - "z": 0.18833979964256287 - }, "collisionsWillMove": 1, velocity: { x: 0, @@ -66,7 +60,8 @@ birdFirework2 = { "z": 0 }, "id": "{ba067084-8d0f-4eeb-a8a1-c6814527c1bb}", - "modelURL": "atp:/tutorial_models/statuebird4.fbx", + //"modelURL": "atp:/tutorial_models/statuebird4.fbx", + "modelURL": "atp:/tutorial_models/bomb2.fbx", "owningAvatarID": "{00000000-0000-0000-0000-000000000000}", "position": { "x": 0, diff --git a/tutorial/firePit/fire.js b/tutorial/firePit/fire.js index 077d79a42a..4565975351 100644 --- a/tutorial/firePit/fire.js +++ b/tutorial/firePit/fire.js @@ -4,6 +4,12 @@ (function() { + function debug() { + var args = Array.prototype.slice.call(arguments); + args.unshift("fire.js | "); + print.apply(this, args); + } + var _this = this; function Fire() { @@ -54,30 +60,45 @@ var colors = [RED, ORANGE, YELLOW, GREEN, BLUE, INDIGO, VIOLET]; + var firePitSoundURL = Script.resolvePath("fire_burst.wav"); + debug("Firepit burst sound url is: ", firePitSoundURL); + + var explodeTextureURL = Script.resolvePath("explode.png"); + debug("Firepit explode texture url is: ", explodeTextureURL); + Fire.prototype = { preload: function(entityID) { + debug("Preload"); this.entityID = entityID; - this.EXPLOSION_SOUND = SoundCache.getSound("atp:/firepit/fire_burst.wav"); + this.EXPLOSION_SOUND = SoundCache.getSound(firePitSoundURL); }, collisionWithEntity: function(myID, otherID, collisionInfo) { + debug("Collided with entity: ", myID, otherID); var otherProps = Entities.getEntityProperties(otherID); var data = null; try { - data = JSON.parse(otherProps.userData) + data = JSON.parse(otherProps.userData); } catch (err) { - print('ERROR GETTING USERDATA!'); + debug('ERROR GETTING USERDATA!'); } if (data === null || "") { + debug("Data is null or empty", data); return; } else { + debug("Got data", data); if (data.hasOwnProperty('hifiHomeKey')) { + debug("Has hifiHomeKey"); if (data.hifiHomeKey.reset === true) { + debug("Reset is true"); _this.playSoundAtCurrentPosition(); _this.explodeWithColor(); Entities.deleteEntity(otherID) + debug("Sending local message"); Messages.sendLocalMessage('Entity-Exploded', JSON.stringify({ entityID: otherID, + position: Entities.getEntityProperties(this.entityID).position })); + debug("Done sending local message"); } } } @@ -137,7 +158,7 @@ "alphaStart": -0.2, "alphaFinish": 0.5, "emitterShouldTrail": 0, - "textures": "atp:/firepit/explode.png", + "textures": explodeTextureURL, "type": "ParticleEffect", lifetime: 1, position: myProps.position diff --git a/tutorial/fuse.js b/tutorial/fuse.js index 842695d85c..30afd49620 100644 --- a/tutorial/fuse.js +++ b/tutorial/fuse.js @@ -14,10 +14,14 @@ var DEBUG = false; function debug() { if (DEBUG) { - print.apply(self, arguments); + var args = Array.prototype.slice.call(arguments); + args.unshift("fuse.js | "); + print.apply(this, args); } } + var active = false; + var fuseSound = SoundCache.getSound("atp:/tutorial_sounds/fuse.wav"); function getChildProperties(entityID, propertyNames) { var childEntityIDs = Entities.getChildrenIDs(entityID); @@ -33,12 +37,20 @@ }; Fuse.prototype = { light: function() { - debug("LIT", this.entityID); - var anim = Entities.getEntityProperties(this.entityID, ['animation']).animation; + debug("Received light()", this.entityID); - if (anim.currentFrame < 140) { + var visible = Entities.getEntityProperties(this.entityID, ['visible']).visible; + if (!visible) { + debug("Fuse is not visible, returning"); return; } + + if (active) { + debug("Fuse is active, returning"); + return; + } + active = true; + Entities.editEntity(this.entityID, { animation: { currentFrame: 1, @@ -56,6 +68,7 @@ var childrenProps = getChildProperties(this.entityID, ['type']); for (var childEntityID in childrenProps) { + debug("Updating: ", childEntityID); var props = childrenProps[childEntityID]; if (props.type == "ParticleEffect") { Entities.editEntity(childEntityID, { @@ -70,13 +83,15 @@ var self = this; Script.setTimeout(function() { - debug("BLOW UP"); - var spinnerID = Utils.findEntity({ name: "tutorial/equip/spinner" }, 20); + debug("Setting off fireworks"); + //var spinnerID = Utils.findEntity({ name: "tutorial/equip/spinner" }, 20); + var spinnerID = "{dd13fcd5-616f-4749-ab28-2e1e8bc512e9}"; Entities.callEntityMethod(spinnerID, "onLit"); injector.stop(); var childrenProps = getChildProperties(self.entityID, ['type']); for (var childEntityID in childrenProps) { + debug("Updating: ", childEntityID); var props = childrenProps[childEntityID]; if (props.type == "ParticleEffect") { Entities.editEntity(childEntityID, { @@ -90,8 +105,14 @@ } }, 4900); + + Script.setTimeout(function() { + debug("Setting fuse to inactive"); + active = false; + }, 10000); }, preload: function(entityID) { + debug("Preload"); this.entityID = entityID; }, }; diff --git a/tutorial/fuseCollider.js b/tutorial/fuseCollider.js index 0ad5cfb371..7e000290d6 100644 --- a/tutorial/fuseCollider.js +++ b/tutorial/fuseCollider.js @@ -5,8 +5,9 @@ }; Fuse.prototype = { onLit: function() { - print("LIT", this.entityID); - var fuseID = Utils.findEntity({ name: "tutorial/equip/fuse" }, 20); + print("fuseCollider.js | Lit", this.entityID); + //var fuseID = Utils.findEntity({ name: "tutorial/equip/fuse" }, 20); + var fuseID = "{c8944a13-9acb-4d77-b1ee-851845e98357}" Entities.callEntityMethod(fuseID, "light"); }, preload: function(entityID) { diff --git a/tutorial/tutorial.js b/tutorial/tutorial.js index 8c74bddc29..b2258c42fc 100644 --- a/tutorial/tutorial.js +++ b/tutorial/tutorial.js @@ -32,7 +32,7 @@ if (!Function.prototype.bind) { if (this.prototype) { // Function.prototype doesn't have a prototype property - fNOP.prototype = this.prototype; + fNOP.prototype = this.prototype; } fBound.prototype = new fNOP(); @@ -40,26 +40,36 @@ if (!Function.prototype.bind) { }; } -var DEBUG = false; +var DEBUG = true; function debug() { if (DEBUG) { - print.apply(this, arguments); + var args = Array.prototype.slice.call(arguments); + args.unshift("tutorial.js | "); + print.apply(this, args); } } var INFO = true; function info() { if (INFO) { - print.apply(this, arguments); + var args = Array.prototype.slice.call(arguments); + args.unshift("tutorial.js | "); + print.apply(this, args); } } +// Return a number between min (inclusive) and max (exclusive) +function randomInt(min, max) { + return min + Math.floor(Math.random() * (max - min)) +} + var NEAR_BOX_SPAWN_NAME = "tutorial/nearGrab/box_spawn"; var FAR_BOX_SPAWN_NAME = "tutorial/farGrab/box_spawn"; var GUN_SPAWN_NAME = "tutorial/gun_spawn"; var TELEPORT_PAD_NAME = "tutorial/teleport/pad" var successSound = SoundCache.getSound("atp:/tutorial_sounds/good_one.L.wav"); +var firecrackerSound = SoundCache.getSound("atp:/tutorial_sounds/Pops_Firecracker.wav"); var CHANNEL_AWAY_ENABLE = "Hifi-Away-Enable"; @@ -205,7 +215,7 @@ function findEntitiesWithTag(tag) { return findEntities({ userData: "" }, 10000, function(properties, key, value) { data = parseJSON(value); return data.tag == tag; - }); + }); } // From http://stackoverflow.com/questions/5999998/how-can-i-check-if-a-javascript-variable-is-function-type @@ -222,6 +232,15 @@ function playSuccessSound() { }); } + +function playFirecrackerSound(position) { + Audio.playSound(firecrackerSound, { + position: position, + volume: 0.7, + loop: false + }); +} + /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// // // @@ -340,13 +359,11 @@ stepOrient.prototype = { var tag = this.tag; // Spawn content set - debug("raise hands...", this.tag); editEntitiesWithTag(this.tag, { visible: true }); - this.checkIntervalID = null; function checkForHandsAboveHead() { - debug("Orient: Checking for hands above head..."); + debug("Orient | Checking for hands above head"); if (MyAvatar.getLeftPalmPosition().y > (MyAvatar.getHeadPosition().y + 0.1)) { Script.clearInterval(this.checkIntervalID); this.checkIntervalID = null; @@ -359,6 +376,7 @@ stepOrient.prototype = { this.checkIntervalID = Script.setInterval(checkForHandsAboveHead.bind(this), 500); }, cleanup: function() { + debug("Orient | Cleanup"); if (this.active) { this.active = false; } @@ -394,13 +412,12 @@ stepRaiseAboveHead.prototype = { var STATE_HANDS_UP = 2; this.state = STATE_START; - debug("raise hands...", this.tag); editEntitiesWithTag(this.tag, { visible: true }); // Wait 2 seconds before starting to check for hands this.checkIntervalID = null; function checkForHandsAboveHead() { - debug("Raise above head: Checking hands..."); + debug("RaiseAboveHead | Checking hands"); if (this.state == STATE_START) { if (MyAvatar.getLeftPalmPosition().y < (MyAvatar.getHeadPosition().y - 0.1)) { this.state = STATE_HANDS_DOWN; @@ -418,6 +435,7 @@ stepRaiseAboveHead.prototype = { this.checkIntervalID = Script.setInterval(checkForHandsAboveHead.bind(this), 500); }, cleanup: function() { + debug("RaiseAboveHead | Cleanup"); if (this.checkIntervalID) { Script.clearInterval(this.checkIntervalID); this.checkIntervalID = null @@ -459,22 +477,18 @@ stepNearGrab.prototype = { showEntitiesWithTag(this.tag, { visible: true }); showEntitiesWithTag('bothGrab', { visible: true }); - var boxSpawnID = findEntity({ name: NEAR_BOX_SPAWN_NAME }, 10000); - if (!boxSpawnID) { - info("Error creating block, cannot find spawn"); - return null; - } - var boxSpawnPosition = Entities.getEntityProperties(boxSpawnID, 'position').position; - function createBlock() { - //Step1BlockData.position = boxSpawnPosition; - birdFirework1.position = boxSpawnPosition; - return spawnWithTag([birdFirework1], null, this.tempTag)[0]; + var boxSpawnPosition = getEntityWithName(NEAR_BOX_SPAWN_NAME).position; + function createBlock(fireworkNumber) { + fireworkBaseProps.position = boxSpawnPosition; + fireworkBaseProps.modelURL = fireworkURLs[fireworkNumber % fireworkURLs.length]; + debug("Creating firework with url: ", fireworkBaseProps.modelURL); + return spawnWithTag([fireworkBaseProps], null, this.tempTag)[0]; } this.birdIDs = []; - this.birdIDs.push(createBlock.bind(this)()); - this.birdIDs.push(createBlock.bind(this)()); - this.birdIDs.push(createBlock.bind(this)()); + this.birdIDs.push(createBlock.bind(this)(0)); + this.birdIDs.push(createBlock.bind(this)(1)); + this.birdIDs.push(createBlock.bind(this)(2)); this.positionWatcher = new PositionWatcher(this.birdIDs, boxSpawnPosition, -0.4, 4); }, onMessage: function(channel, message, seneder) { @@ -482,10 +496,12 @@ stepNearGrab.prototype = { return; } if (channel == "Entity-Exploded") { - debug("TUTORIAL: Got entity-exploded message"); + debug("NearGrab | Got entity-exploded message: ", message); var data = parseJSON(message); if (this.birdIDs.indexOf(data.entityID) >= 0) { + debug("NearGrab | It's one of the firecrackers"); + playFirecrackerSound(data.position); playSuccessSound(); this.finished = true; this.onFinish(); @@ -493,7 +509,7 @@ stepNearGrab.prototype = { } }, cleanup: function() { - debug("cleaning up near grab"); + debug("NearGrab | Cleanup"); this.finished = true; setControllerVisible("trigger", false); setControllerPartLayer('tips', 'blank'); @@ -540,21 +556,18 @@ stepFarGrab.prototype = { // Spawn content set showEntitiesWithTag(this.tag); - var boxSpawnID = findEntity({ name: FAR_BOX_SPAWN_NAME }, 10000); - if (!boxSpawnID) { - debug("Error creating block, cannot find spawn"); - return null; - } - var boxSpawnPosition = Entities.getEntityProperties(boxSpawnID, 'position').position; - function createBlock() { - birdFirework1.position = boxSpawnPosition; - return spawnWithTag([birdFirework1], null, this.tempTag)[0]; + var boxSpawnPosition = getEntityWithName(FAR_BOX_SPAWN_NAME).position; + function createBlock(fireworkNumber) { + fireworkBaseProps.position = boxSpawnPosition; + fireworkBaseProps.modelURL = fireworkURLs[fireworkNumber % fireworkURLs.length]; + debug("Creating firework with url: ", fireworkBaseProps.modelURL); + return spawnWithTag([fireworkBaseProps], null, this.tempTag)[0]; } this.birdIDs = []; - this.birdIDs.push(createBlock.bind(this)()); - this.birdIDs.push(createBlock.bind(this)()); - this.birdIDs.push(createBlock.bind(this)()); + this.birdIDs.push(createBlock.bind(this)(3)); + this.birdIDs.push(createBlock.bind(this)(4)); + this.birdIDs.push(createBlock.bind(this)(5)); this.positionWatcher = new PositionWatcher(this.birdIDs, boxSpawnPosition, -0.4, 4); }, onMessage: function(channel, message, seneder) { @@ -562,9 +575,11 @@ stepFarGrab.prototype = { return; } if (channel == "Entity-Exploded") { - debug("TUTORIAL: Got entity-exploded message"); + debug("FarGrab | Got entity-exploded message: ", message); var data = parseJSON(message); if (this.birdIDs.indexOf(data.entityID) >= 0) { + debug("FarGrab | It's one of the firecrackers"); + playFirecrackerSound(data.position); playSuccessSound(); this.finished = true; this.onFinish(); @@ -572,6 +587,7 @@ stepFarGrab.prototype = { } }, cleanup: function() { + debug("FarGrab | Cleanup"); this.finished = true; setControllerVisible("trigger", false); setControllerPartLayer('tips', 'blank'); @@ -586,12 +602,13 @@ stepFarGrab.prototype = { }; function PositionWatcher(entityIDs, originalPosition, minY, maxDistance) { + debug("Creating position watcher"); this.watcherIntervalID = Script.setInterval(function() { for (var i = 0; i < entityIDs.length; ++i) { var entityID = entityIDs[i]; var props = Entities.getEntityProperties(entityID, ['position']); if (props.position.y < minY || Vec3.distance(originalPosition, props.position) > maxDistance) { - Entities.editEntity(entityID, { + Entities.editEntity(entityID, { position: originalPosition, velocity: { x: 0, y: -0.01, z: 0 }, angularVelocity: { x: 0, y: 0, z: 0 } @@ -603,6 +620,7 @@ function PositionWatcher(entityIDs, originalPosition, minY, maxDistance) { PositionWatcher.prototype = { destroy: function() { + debug("Destroying position watcher"); Script.clearInterval(this.watcherIntervalID); } }; @@ -644,40 +662,47 @@ stepEquip.prototype = { this.currentPart = this.PART1; - function createGun() { - var boxSpawnID = findEntity({ name: GUN_SPAWN_NAME }, 10000); - if (!boxSpawnID) { - info("Error creating block, cannot find spawn"); - return null; - } + function createLighter() { + //var boxSpawnID = findEntity({ name: GUN_SPAWN_NAME }, 10000); + //if (!boxSpawnID) { + //info("Error creating block, cannot find spawn"); + //return null; + //} var transform = {}; - transform.position = Entities.getEntityProperties(boxSpawnID, 'position').position; - transform.rotation = Entities.getEntityProperties(boxSpawnID, 'rotation').rotation; + var boxSpawnProps = getEntityWithName(GUN_SPAWN_NAME); + transform.position = boxSpawnProps.position; + transform.rotation = boxSpawnProps.rotation; + transform.velocity = { x: 0, y: -0.01, z: 0 }; + transform.angularVelocity = { x: 0, y: 0, z: 0 }; this.spawnTransform = transform; return doCreateButaneLighter(transform).id; } - this.gunID = createGun.bind(this)(); - this.startWatchingGun(); - debug("Created", this.gunID); + this.lighterID = createLighter.bind(this)(); + this.startWatchingLighter(); + debug("Created lighter", this.lighterID); this.onFinish = onFinish; }, - startWatchingGun: function() { + startWatchingLighter: function() { if (!this.watcherIntervalID) { + debug("Starting to watch lighter position"); this.watcherIntervalID = Script.setInterval(function() { - var props = Entities.getEntityProperties(this.gunID, ['position']); - if (props.position.y < -0.4 + debug("Checking lighter position"); + var props = Entities.getEntityProperties(this.lighterID, ['position']); + if (props.position.y < -0.4 || Vec3.distance(this.spawnTransform.position, props.position) > 4) { - Entities.editEntity(this.gunID, this.spawnTransform); + debug("Moving lighter back to table"); + Entities.editEntity(this.lighterID, this.spawnTransform); } }.bind(this), 1000); } }, stopWatchingGun: function() { if (this.watcherIntervalID) { + debug("Stopping watch of lighter position"); Script.clearInterval(this.watcherIntervalID); this.watcherIntervalID = null; } @@ -687,24 +712,27 @@ stepEquip.prototype = { return; } - debug("Got message", channel, message, sender, MyAvatar.sessionUUID); + debug("Equip | Got message", channel, message, sender, MyAvatar.sessionUUID); if (channel == "Tutorial-Spinner") { if (this.currentPart == this.PART1 && message == "wasLit") { this.currentPart = this.PART2; + debug("Equip | Starting part 2"); Script.setTimeout(function() { + debug("Equip | Starting part 3"); this.currentPart = this.PART3; hideEntitiesWithTag(this.tagPart1); showEntitiesWithTag(this.tagPart2); setControllerPartLayer('tips', 'grip'); Messages.subscribe('Hifi-Object-Manipulation'); + debug("Equip | Finished starting part 3"); }.bind(this), 9000); } } else if (channel == "Hifi-Object-Manipulation") { if (this.currentPart == this.PART3) { var data = parseJSON(message); - if (data.action == 'release' && data.grabbedEntity == this.gunID) { - info("got release"); + if (data.action == 'release' && data.grabbedEntity == this.lighterID) { + debug("Equip | Got release, finishing step"); this.stopWatchingGun(); this.currentPart = this.COMPLETE; playSuccessSound(); @@ -714,6 +742,7 @@ stepEquip.prototype = { } }, cleanup: function() { + debug("Equip | Got yaw action"); if (this.watcherIntervalID) { Script.clearInterval(this.watcherIntervalID); this.watcherIntervalID = null; @@ -764,6 +793,7 @@ stepTurnAround.prototype = { Controller.actionEvent.connect(this.onActionBound); this.interval = Script.setInterval(function() { + debug("TurnAround | Checking if finished", this.numTimesTurnPressed); var FORWARD_THRESHOLD = 30; var REQ_NUM_TIMES_PRESSED = 6; @@ -782,10 +812,12 @@ stepTurnAround.prototype = { onAction: function(action, value) { var STEP_YAW_ACTION = 6; if (action == STEP_YAW_ACTION && value != 0) { + debug("TurnAround | Got yaw action"); this.numTimesTurnPressed += 1; } }, cleanup: function() { + debug("TurnAround | Cleanup"); try { Controller.actionEvent.disconnect(this.onActionBound); } catch (e) { @@ -826,22 +858,23 @@ stepTeleport.prototype = { Messages.sendLocalMessage('Hifi-Teleport-Disabler', 'none'); // Wait until touching teleport pad... - var padID = findEntity({ name: TELEPORT_PAD_NAME }, 100); - var padProps = Entities.getEntityProperties(padID, ["position", "dimensions"]); + //var padID = findEntity({ name: TELEPORT_PAD_NAME }, 100); + //var padProps = Entities.getEntityProperties(padID, ["position", "dimensions"]); + var padProps = getEntityWithName(TELEPORT_PAD_NAME); var xMin = padProps.position.x - padProps.dimensions.x / 2; var xMax = padProps.position.x + padProps.dimensions.x / 2; var zMin = padProps.position.z - padProps.dimensions.z / 2; var zMax = padProps.position.z + padProps.dimensions.z / 2; function checkCollides() { - debug("Checking if on pad..."); + debug("Teleport | Checking if on pad..."); var pos = MyAvatar.position; - debug('x', pos.x, xMin, xMax); - debug('z', pos.z, zMin, zMax); + debug('Teleport | x', pos.x, xMin, xMax); + debug('Teleport | z', pos.z, zMin, zMax); if (pos.x > xMin && pos.x < xMax && pos.z > zMin && pos.z < zMax) { - debug("On teleport pad"); + debug("Teleport | On teleport pad"); Script.clearInterval(this.checkCollidesTimer); this.checkCollidesTimer = null; playSuccessSound(); @@ -853,6 +886,7 @@ stepTeleport.prototype = { showEntitiesWithTag(this.tag); }, cleanup: function() { + debug("Teleport | Cleanup"); setControllerPartLayer('touchpad', 'blank'); setControllerPartLayer('tips', 'blank'); @@ -982,6 +1016,13 @@ function hideEntitiesWithTag(tag) { }); } +// Return the entity properties for an entity with a given name if it is in our +// cached list of entities. Otherwise, return undefined. +function getEntityWithName(name) { + debug("Getting entity with name:", name); + return TUTORIAL_NAME_TO_ENTITY_PROPERTIES_MAP[name]; +} + TutorialManager = function() { var STEPS; @@ -1000,7 +1041,7 @@ TutorialManager = function() { STEPS = [ new stepDisableControllers("step0"), new stepOrient("orient"), - new stepRaiseAboveHead("raiseHands"), + //new stepRaiseAboveHead("raiseHands"), new stepNearGrab("nearGrab"), new stepFarGrab("farGrab"), new stepEquip("equip"), @@ -1017,6 +1058,7 @@ TutorialManager = function() { } this.onFinish = function() { + debug("onFinish", currentStepNum); if (currentStep && currentStep.shouldLog !== false) { var timeToFinishStep = (Date.now() - startedLastStepAt) / 1000; var tutorialTimeElapsed = (Date.now() - startedTutorialAt) / 1000; diff --git a/tutorial/tutorialEntityIDs.js b/tutorial/tutorialEntityIDs.js index 38bd06e5ff..886913a838 100644 --- a/tutorial/tutorialEntityIDs.js +++ b/tutorial/tutorialEntityIDs.js @@ -148,4 +148,168 @@ TUTORIAL_TAG_TO_ENTITY_IDS_MAP = { "tag": "orient" } } -} +}; + +TUTORIAL_NAME_TO_ENTITY_PROPERTIES_MAP = { + "tutorial/gun_spawn": { + "userData": "{\"tag\":\"equip\",\"visible\":false}", + "dimensions": { + "y": 0.0649842768907547, + "x": 0.0649842768907547, + "z": 0.0649842768907547 + }, + "collisionless": 1, + "created": "2016-09-08T18:38:24Z", + "color": { + "blue": 0, + "green": 0, + "red": 255 + }, + "queryAACube": { + "y": 0.6283726096153259, + "x": 0.6865367293357849, + "scale": 0.11255607008934021, + "z": 0.3359576463699341 + }, + "visible": 0, + "shape": "Cube", + "clientOnly": 0, + "owningAvatarID": "{00000000-0000-0000-0000-000000000000}", + "position": { + "y": 0.6846506595611572, + "x": 0.7428147792816162, + "z": 0.3922356963157654 + }, + "rotation": { + "y": 0.7066605091094971, + "x": 0.7066605091094971, + "z": -0.025131583213806152, + "w": -0.025101065635681152 + }, + "ignoreForCollisions": 1, + "type": "Box", + "id": "{9df518da-9e65-4b76-8a79-eeefdb0b7310}", + "name": "tutorial/gun_spawn" + }, + "tutorial/nearGrab/box_spawn": { + "userData": "{\"tag\":\"nearGrab\",\"visible\":false}", + "dimensions": { + "y": 0.08225371688604355, + "x": 0.08225371688604355, + "z": 0.08225371688604355 + }, + "collisionless": 1, + "created": "2016-09-08T18:38:24Z", + "color": { + "blue": 255, + "green": 0, + "red": 255 + }, + "queryAACube": { + "y": 0.738319456577301, + "x": 0.8985498547554016, + "scale": 0.14246761798858643, + "z": 0.29067665338516235 + }, + "visible": 0, + "shape": "Cube", + "clientOnly": 0, + "owningAvatarID": "{00000000-0000-0000-0000-000000000000}", + "position": { + "y": 0.8095532655715942, + "x": 0.9697836637496948, + "z": 0.36191046237945557 + }, + "rotation": { + "y": -1.52587890625e-05, + "x": -1.52587890625e-05, + "z": -1.52587890625e-05, + "w": 1 + }, + "ignoreForCollisions": 1, + "type": "Box", + "id": "{5cf22b9c-fb22-4854-8821-554422980b24}", + "name": "tutorial/nearGrab/box_spawn" + }, + "tutorial/farGrab/box_spawn": { + "userData": "{\"tag\":\"farGrab\",\"visible\":false}", + "dimensions": { + "y": 0.37358683347702026, + "x": 0.37358683347702026, + "z": 0.37358683347702026 + }, + "collisionless": 1, + "created": "2016-09-08T18:38:24Z", + "color": { + "blue": 255, + "green": 0, + "red": 255 + }, + "queryAACube": { + "y": 0.3304251432418823, + "x": 3.0951309204101562, + "scale": 0.647071361541748, + "z": 0.18027013540267944 + }, + "visible": 0, + "shape": "Cube", + "clientOnly": 0, + "owningAvatarID": "{00000000-0000-0000-0000-000000000000}", + "position": { + x: 3.4866, + y: 0.6716, + z: 0.4789 + }, + "rotation": { + "y": -1.52587890625e-05, + "x": -1.52587890625e-05, + "z": -1.52587890625e-05, + "w": 1 + }, + "ignoreForCollisions": 1, + "type": "Box", + "id": "{70fcd96c-cd59-4f23-9ca5-a167f2f85680}", + "name": "tutorial/farGrab/box_spawn" + }, + "tutorial/teleport/pad": { + "userData": "{\"tag\":\"teleport\"}", + "rotation": { + "y": -0.9702650308609009, + "x": -2.1246911273919977e-05, + "z": -4.222852112434339e-06, + "w": 0.2420452982187271 + }, + "dimensions": { + "y": 0.4365682601928711, + "x": 2.1751723289489746, + "z": 2.175173044204712 + }, + "collisionless": 1, + "created": "2016-09-08T18:38:24Z", + "queryAACube": { + "y": -1.7979401350021362, + "x": 7.5136213302612305, + "scale": 3.106983184814453, + "z": -1.4602710008621216 + }, + "visible": 0, + "angularVelocity": { + "y": -0.5235987901687622, + "x": 0, + "z": 0 + }, + "clientOnly": 0, + "owningAvatarID": "{00000000-0000-0000-0000-000000000000}", + "angularDamping": 0, + "position": { + "y": -0.2444484978914261, + "x": 9.067112922668457, + "z": 0.09322060644626617 + }, + "modelURL": "atp:/alan/dev/Teleport-Pad.fbx", + "ignoreForCollisions": 1, + "type": "Model", + "id": "{4478f7b5-d3ac-4213-9a7b-ad8cd69575b8}", + "name": "tutorial/teleport/pad" + } +}; From d9e6491843eaa7494466e5789d6cc8bb544c744d Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Wed, 12 Oct 2016 10:37:38 -0700 Subject: [PATCH 14/49] Add more debug to fuseCollider and spinner --- tutorial/fuseCollider.js | 1 + tutorial/spinner.js | 7 +++++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/tutorial/fuseCollider.js b/tutorial/fuseCollider.js index 7e000290d6..147444ac1d 100644 --- a/tutorial/fuseCollider.js +++ b/tutorial/fuseCollider.js @@ -11,6 +11,7 @@ Entities.callEntityMethod(fuseID, "light"); }, preload: function(entityID) { + print("fuseCollider.js | preload"); this.entityID = entityID; }, }; diff --git a/tutorial/spinner.js b/tutorial/spinner.js index b50db2704e..b27022fce2 100644 --- a/tutorial/spinner.js +++ b/tutorial/spinner.js @@ -31,7 +31,7 @@ } Spinner.prototype = { onLit: function() { - debug("LIT SPINNER", this.entityID); + debug("spinner.js | Spinner lit"); Entities.editEntity(this.entityID, { "angularDamping": 0.1, "angularVelocity": { @@ -50,6 +50,7 @@ for (var childEntityID in childrenProps) { var props = childrenProps[childEntityID]; if (props.type == "ParticleEffect") { + debug("spinner.js | Modifying: ", childEntityID); Entities.editEntity(childEntityID, { emitRate: 35, }); @@ -59,13 +60,14 @@ var self = this; Script.setTimeout(function() { - debug("BLOW UP"); + debug("spinner.js | Finishing spinner"); injector.stop(); var childrenProps = getChildProperties(self.entityID, ['type']); for (var childEntityID in childrenProps) { var props = childrenProps[childEntityID]; if (props.type == "ParticleEffect") { + debug("spinner.js | Modifying: ", childEntityID); Entities.editEntity(childEntityID, { emitRate: 0, }); @@ -74,6 +76,7 @@ }, 4900); }, preload: function(entityID) { + debug("spinner.js | Preload"); this.entityID = entityID; }, }; From 1f2b555a2647b74fb5fc44a90fd9ba6fa82e8a92 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 13 Oct 2016 14:16:04 -0700 Subject: [PATCH 15/49] Add logging of show/hide/getByName in tutorial --- tutorial/tutorial.js | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/tutorial/tutorial.js b/tutorial/tutorial.js index b2258c42fc..871d16a218 100644 --- a/tutorial/tutorial.js +++ b/tutorial/tutorial.js @@ -937,6 +937,10 @@ stepCleanupFinish.prototype = { +function isEntityInLocalTree(entityID) { + return Entities.getEntityProperties(entityID, 'visible').visible !== undefined; +} + function showEntitiesWithTag(tag) { var entities = TUTORIAL_TAG_TO_ENTITY_IDS_MAP[tag]; if (entities) { @@ -955,6 +959,7 @@ function showEntitiesWithTag(tag) { collisionless: collisionless, userData: JSON.stringify(data), }; + debug("Showing: ", entityID, ", Is in local tree: ", isEntityInLocalTree(entityID)); Entities.editEntity(entityID, newProperties); } } @@ -979,6 +984,7 @@ function showEntitiesWithTag(tag) { Entities.editEntity(entityID, newProperties); }); } + function hideEntitiesWithTag(tag) { var entities = TUTORIAL_TAG_TO_ENTITY_IDS_MAP[tag]; if (entities) { @@ -994,6 +1000,8 @@ function hideEntitiesWithTag(tag) { ignoreForCollisions: 1, userData: JSON.stringify(data), }; + + debug("Hiding: ", entityID, ", Is in local tree: ", isEntityInLocalTree(entityID)); Entities.editEntity(entityID, newProperties); } } @@ -1020,7 +1028,9 @@ function hideEntitiesWithTag(tag) { // cached list of entities. Otherwise, return undefined. function getEntityWithName(name) { debug("Getting entity with name:", name); - return TUTORIAL_NAME_TO_ENTITY_PROPERTIES_MAP[name]; + var entityID = TUTORIAL_NAME_TO_ENTITY_PROPERTIES_MAP[name]; + debug("Entity id: ", entityID, ", Is in local tree: ", isEntityInLocalTree(entityID)); + return entityID; } From e58cd53fe57786715378de0e319550d434d955bf Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 13 Oct 2016 15:39:32 -0700 Subject: [PATCH 16/49] Fix entity id for grip button model --- tutorial/tutorialEntityIDs.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tutorial/tutorialEntityIDs.js b/tutorial/tutorialEntityIDs.js index 886913a838..14b2a69892 100644 --- a/tutorial/tutorialEntityIDs.js +++ b/tutorial/tutorialEntityIDs.js @@ -83,7 +83,7 @@ TUTORIAL_TAG_TO_ENTITY_IDS_MAP = { } }, "equip-part2": { - "{8b92eec5-aeed-4368-bce0-432cc9ad4c51}": { + "{b5d17eda-90ab-40cf-b973-efcecb2e992e}": { "tag": "equip-part2" }, "{6307cd16-dd1d-4988-a339-578178436b45}": { From 97fbdc7c2089ea9df4dd40d048076ba8f27bb327 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 13 Oct 2016 16:52:43 -0700 Subject: [PATCH 17/49] Fix issues in several tutorial scripts --- tutorial/fuse.js | 4 ++-- tutorial/spinner.js | 4 ++-- tutorial/tutorialStartZone.js | 8 +++++++- tutorial/tutorialZone.js | 15 +++++++++++++-- 4 files changed, 24 insertions(+), 7 deletions(-) diff --git a/tutorial/fuse.js b/tutorial/fuse.js index 30afd49620..73a3a697bd 100644 --- a/tutorial/fuse.js +++ b/tutorial/fuse.js @@ -11,7 +11,7 @@ (function() { Script.include('utils.js'); - var DEBUG = false; + var DEBUG = true; function debug() { if (DEBUG) { var args = Array.prototype.slice.call(arguments); @@ -109,7 +109,7 @@ Script.setTimeout(function() { debug("Setting fuse to inactive"); active = false; - }, 10000); + }, 14000); }, preload: function(entityID) { debug("Preload"); diff --git a/tutorial/spinner.js b/tutorial/spinner.js index b27022fce2..2edbb43700 100644 --- a/tutorial/spinner.js +++ b/tutorial/spinner.js @@ -9,10 +9,10 @@ // (function() { - var DEBUG = false; + var DEBUG = true; function debug() { if (DEBUG) { - print.apply(self, arguments); + print.apply(this, arguments); } } diff --git a/tutorial/tutorialStartZone.js b/tutorial/tutorialStartZone.js index 5cff1a4e99..81d4892919 100644 --- a/tutorial/tutorialStartZone.js +++ b/tutorial/tutorialStartZone.js @@ -20,7 +20,7 @@ print("TutorialStartZone | Parent ID is: ", parentID); if (parentID) { print("TutorialStartZone | Sending start"); - Entities.callEntityMethod(parentID, 'start'); + Entities.callEntityMethod(parentID, 'onEnteredStartZone'); } else { print("TutorialStartZone | ERROR: No parent id found on tutorial start zone"); } @@ -38,6 +38,12 @@ if (this.sendStartIntervalID) { Script.clearInterval(this.sendStartIntervalID); } + var parentID = Entities.getEntityProperties(this.entityID, 'parentID').parentID; + print("TutorialStartZone | Parent ID is: ", parentID); + if (parentID) { + print("TutorialStartZone | Sending onLeftStartZone"); + Entities.callEntityMethod(parentID, 'on'); + } } }; diff --git a/tutorial/tutorialZone.js b/tutorial/tutorialZone.js index db7306a529..6c78d678b4 100644 --- a/tutorial/tutorialZone.js +++ b/tutorial/tutorialZone.js @@ -59,11 +59,13 @@ if (!Function.prototype.bind) { print("TutorialZone | Preload"); this.entityID = entityID; }, - start: function() { - print("TutorialZone | Got start"); + onEnteredStartZone: function() { + print("TutorialZone | Got onEnteredStartZone"); var self = this; if (!this.token) { print("TutorialZone | Creating token"); + // The start zone has been entered, hide the overlays immediately + Menu.setIsOptionChecked("Overlays", false); this.token = new OwnershipToken(Math.random() * 100000, this.entityID, { onGainedOwnership: function(token) { print("TutorialZone | GOT OWNERSHIP"); @@ -91,6 +93,15 @@ if (!Function.prototype.bind) { }); } }, + onLeftStartZone: function() { + print("TutorialZone | Got onLeftStartZone"); + + // If the start zone was exited, and the tutorial hasn't started, go ahead and + // re-enable the HUD/Overlays + if (!self.tutorialManager) { + Menu.setIsOptionChecked("Overlays", true); + } + }, enterEntity: function() { print("TutorialZone | ENTERED THE TUTORIAL AREA"); From eb541e09f96d8a5efc738d3e0efa94491249d0a9 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Fri, 14 Oct 2016 13:15:23 -0700 Subject: [PATCH 18/49] Update tutorial error text to say 'Vive headset' instead of 'VR headset' --- tutorial/tutorialStartZone.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tutorial/tutorialStartZone.js b/tutorial/tutorialStartZone.js index 81d4892919..cb0d223200 100644 --- a/tutorial/tutorialStartZone.js +++ b/tutorial/tutorialStartZone.js @@ -29,7 +29,7 @@ sendStart(); } else { print("TutorialStartZone | User tried to go to tutorial with HMD and hand controllers, sending back to /"); - Window.alert("To proceed with this tutorial, please connect your VR headset and hand controllers."); + Window.alert("To proceed with this tutorial, please connect your Vive headset and hand controllers."); location = "/"; } }, From 1f25ba8f1ffa654365fd676cfbf7f892a0e9d5b3 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Fri, 14 Oct 2016 14:05:06 -0700 Subject: [PATCH 19/49] Update controller tips and trigger highlight in tutorial --- .../viveControllerConfiguration.js | 29 +++++++++++++++++-- tutorial/tutorial.js | 11 +++++++ 2 files changed, 37 insertions(+), 3 deletions(-) diff --git a/scripts/system/controllers/viveControllerConfiguration.js b/scripts/system/controllers/viveControllerConfiguration.js index 341b8256f7..11fc774376 100644 --- a/scripts/system/controllers/viveControllerConfiguration.js +++ b/scripts/system/controllers/viveControllerConfiguration.js @@ -62,6 +62,7 @@ var TIP_TEXTURE_BASE_URL = BASE_URL + "meshes/controller/vive_tips.fbm/"; var viveModelURL = BASE_URL + "meshes/controller/vive_body.fbx"; var viveTipsModelURL = BASE_URL + "meshes/controller/vive_tips.fbx"; +var viveTriggerModelURL = "meshes/controller/vive_trigger.fbx" VIVE_CONTROLLER_CONFIGURATION_LEFT = { name: "Vive", @@ -86,7 +87,7 @@ VIVE_CONTROLLER_CONFIGURATION_LEFT = { defaultTextureLayer: "blank", textureLayers: { blank: { - defaultTextureURL: TIP_TEXTURE_BASE_URL + "Blank.png" + defaultTextureURL: TIP_TEXTURE_BASE_URL + "/Blank.png" }, trigger: { defaultTextureURL: TIP_TEXTURE_BASE_URL + "/Trigger.png" @@ -141,7 +142,18 @@ VIVE_CONTROLLER_CONFIGURATION_LEFT = { minValue: 0.0, maxValue: 1.0, axis: { x: -1, y: 0, z: 0 }, - maxAngle: 20 + maxAngle: 25, + + textureName: "Tex.black-trigger", + defaultTextureLayer: "normal", + textureLayers: { + normal: { + defaultTextureURL: viveTriggerModelURL + "/Trigger.fbm/black.jpg", + }, + highlight: { + defaultTextureURL: viveTriggerModelURL + "/Trigger.fbm/yellow.jpg", + } + } }, l_grip: { @@ -262,7 +274,18 @@ VIVE_CONTROLLER_CONFIGURATION_RIGHT = { minValue: 0.0, maxValue: 1.0, axis: { x: -1, y: 0, z: 0 }, - maxAngle: 25 + maxAngle: 25, + + textureName: "Tex.black-trigger", + defaultTextureLayer: "normal", + textureLayers: { + normal: { + defaultTextureURL: viveTriggerModelURL + "/Trigger.fbm/black.jpg", + }, + highlight: { + defaultTextureURL: viveTriggerModelURL + "/Trigger.fbm/yellow.jpg", + } + } }, l_grip: { diff --git a/tutorial/tutorial.js b/tutorial/tutorial.js index 871d16a218..d666c39aae 100644 --- a/tutorial/tutorial.js +++ b/tutorial/tutorial.js @@ -254,6 +254,7 @@ var stepDisableControllers = function(name) { stepDisableControllers.prototype = { start: function(onFinish) { HMD.requestShowHandControllers(); + disableEverything(); onFinish(); @@ -277,6 +278,9 @@ function disableEverything() { setControllerPartLayer('tips', 'blank'); hideEntitiesWithTag('finish'); + + controllerDisplayManager = new ControllerDisplayManager(); + setAwayEnabled(false); } @@ -471,6 +475,7 @@ stepNearGrab.prototype = { setControllerVisible("trigger", true); setControllerPartLayer('tips', 'trigger'); + setControllerPartLayer('trigger', 'highlight'); var tag = this.tag; // Spawn content set @@ -513,6 +518,7 @@ stepNearGrab.prototype = { this.finished = true; setControllerVisible("trigger", false); setControllerPartLayer('tips', 'blank'); + setControllerPartLayer('trigger', 'normal'); hideEntitiesWithTag(this.tag, { visible: false}); deleteEntitiesWithTag(this.tempTag); if (this.positionWatcher) { @@ -548,6 +554,7 @@ stepFarGrab.prototype = { setControllerVisible("trigger", true); setControllerPartLayer('tips', 'trigger'); + setControllerPartLayer('trigger', 'highlight'); Messages.sendLocalMessage('Hifi-Grab-Disable', JSON.stringify({ farGrabEnabled: true, })); @@ -591,6 +598,7 @@ stepFarGrab.prototype = { this.finished = true; setControllerVisible("trigger", false); setControllerPartLayer('tips', 'blank'); + setControllerPartLayer('trigger', 'normal'); hideEntitiesWithTag(this.tag, { visible: false}); hideEntitiesWithTag('bothGrab', { visible: false}); deleteEntitiesWithTag(this.tempTag); @@ -650,6 +658,7 @@ stepEquip.prototype = { start: function(onFinish) { setControllerVisible("trigger", true); setControllerPartLayer('tips', 'trigger'); + setControllerPartLayer('trigger', 'highlight'); Messages.sendLocalMessage('Hifi-Grab-Disable', JSON.stringify({ holdEnabled: true, })); @@ -723,6 +732,7 @@ stepEquip.prototype = { this.currentPart = this.PART3; hideEntitiesWithTag(this.tagPart1); showEntitiesWithTag(this.tagPart2); + setControllerPartLayer('trigger', 'normal'); setControllerPartLayer('tips', 'grip'); Messages.subscribe('Hifi-Object-Manipulation'); debug("Equip | Finished starting part 3"); @@ -750,6 +760,7 @@ stepEquip.prototype = { setControllerVisible("trigger", false); setControllerPartLayer('tips', 'blank'); + setControllerPartLayer('trigger', 'normal'); this.stopWatchingGun(); this.currentPart = this.COMPLETE; From 0234747ec3f478dfb414ed8ca75fa7d7497c784f Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Mon, 17 Oct 2016 16:25:59 -0700 Subject: [PATCH 20/49] Update editEntitiesWithTag to use static lookup table --- tutorial/tutorial.js | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/tutorial/tutorial.js b/tutorial/tutorial.js index d666c39aae..0f0a24356a 100644 --- a/tutorial/tutorial.js +++ b/tutorial/tutorial.js @@ -201,12 +201,18 @@ function deleteEntitiesWithTag(tag) { } } function editEntitiesWithTag(tag, propertiesOrFn) { - var entityIDs = findEntitiesWithTag(tag); - for (var i = 0; i < entityIDs.length; ++i) { - if (isFunction(propertiesOrFn)) { - Entities.editEntity(entityIDs[i], propertiesOrFn(entityIDs[i])); - } else { - Entities.editEntity(entityIDs[i], propertiesOrFn); + //var entityIDs = findEntitiesWithTag(tag); + var entities = TUTORIAL_TAG_TO_ENTITY_IDS_MAP[tag]; + + debug("Editing tag: ", tag); + if (entities) { + for (entityID in entities) { + debug("Editing: ", entityID, ", ", propertiesOrFn, ", Is in local tree: ", isEntityInLocalTree(entityID)); + if (isFunction(propertiesOrFn)) { + Entities.editEntity(entityID, propertiesOrFn(entityIDs[i])); + } else { + Entities.editEntity(entityID, propertiesOrFn); + } } } } From b9bc9bb802ae7bdbb5da391b96bcd97339726c78 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Mon, 17 Oct 2016 16:26:20 -0700 Subject: [PATCH 21/49] Update tutorial to disable away.js right away --- tutorial/ownershipToken.js | 2 ++ tutorial/tutorialZone.js | 16 ++++++++++++---- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/tutorial/ownershipToken.js b/tutorial/ownershipToken.js index 745eee44e4..b76f3af1c2 100644 --- a/tutorial/ownershipToken.js +++ b/tutorial/ownershipToken.js @@ -185,3 +185,5 @@ OwnershipToken.prototype = { Script.setTimeout(checkOwnershipRequest.bind(this), 2000); }, }; + +debug("Returning from ownershipToken"); diff --git a/tutorial/tutorialZone.js b/tutorial/tutorialZone.js index 6c78d678b4..42565aa99b 100644 --- a/tutorial/tutorialZone.js +++ b/tutorial/tutorialZone.js @@ -27,10 +27,14 @@ if (!Function.prototype.bind) { } (function() { - var ownershipTokenPath = Script.resolvePath("ownershipToken.js"); - var tutorialPath = Script.resolvePath("tutorial.js"); - Script.include(ownershipTokenPath); - Script.include(tutorialPath); + Script.include("ownershipToken.js"); + Script.include("tutorial.js"); + + var CHANNEL_AWAY_ENABLE = "Hifi-Away-Enable"; + function setAwayEnabled(value) { + var message = value ? 'enable' : 'disable'; + Messages.sendLocalMessage(CHANNEL_AWAY_ENABLE, message); + } var TutorialZone = function() { print("TutorialZone | Creating"); @@ -65,7 +69,9 @@ if (!Function.prototype.bind) { if (!this.token) { print("TutorialZone | Creating token"); // The start zone has been entered, hide the overlays immediately + setAwayEnabled(false); Menu.setIsOptionChecked("Overlays", false); + MyAvatar.shouldRenderLocally = false; this.token = new OwnershipToken(Math.random() * 100000, this.entityID, { onGainedOwnership: function(token) { print("TutorialZone | GOT OWNERSHIP"); @@ -100,6 +106,8 @@ if (!Function.prototype.bind) { // re-enable the HUD/Overlays if (!self.tutorialManager) { Menu.setIsOptionChecked("Overlays", true); + MyAvatar.shouldRenderLocally = true; + setAwayEnabled(true); } }, From c0e372c3802b9f550d786dc076865afe7bbf340a Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Tue, 18 Oct 2016 08:49:00 -0700 Subject: [PATCH 22/49] Remove annotation API from controller display --- .../controllers/controllerDisplayManager.js | 1 - tutorial/tutorial.js | 20 ------------------- 2 files changed, 21 deletions(-) diff --git a/scripts/system/controllers/controllerDisplayManager.js b/scripts/system/controllers/controllerDisplayManager.js index 2c621f7e90..550357d659 100644 --- a/scripts/system/controllers/controllerDisplayManager.js +++ b/scripts/system/controllers/controllerDisplayManager.js @@ -69,7 +69,6 @@ ControllerDisplayManager = function() { } } - Messages.subscribe('Controller-Display'); var handleMessages = function(channel, message, sender) { var i, data, name, visible; if (!controllerLeft && !controllerRight) { diff --git a/tutorial/tutorial.js b/tutorial/tutorial.js index 0f0a24356a..ea8dd792de 100644 --- a/tutorial/tutorial.js +++ b/tutorial/tutorial.js @@ -116,14 +116,6 @@ findEntities = function(properties, searchRadius, filterFn) { return matchedEntities; } -function setControllerVisible(name, visible) { - return; - Messages.sendLocalMessage('Controller-Display', JSON.stringify({ - name: name, - visible: visible, - })); -} - function setControllerPartsVisible(parts) { Messages.sendLocalMessage('Controller-Display-Parts', JSON.stringify(parts)); } @@ -479,7 +471,6 @@ stepNearGrab.prototype = { this.finished = false; this.onFinish = onFinish; - setControllerVisible("trigger", true); setControllerPartLayer('tips', 'trigger'); setControllerPartLayer('trigger', 'highlight'); var tag = this.tag; @@ -522,7 +513,6 @@ stepNearGrab.prototype = { cleanup: function() { debug("NearGrab | Cleanup"); this.finished = true; - setControllerVisible("trigger", false); setControllerPartLayer('tips', 'blank'); setControllerPartLayer('trigger', 'normal'); hideEntitiesWithTag(this.tag, { visible: false}); @@ -558,7 +548,6 @@ stepFarGrab.prototype = { showEntitiesWithTag('bothGrab', { visible: true }); - setControllerVisible("trigger", true); setControllerPartLayer('tips', 'trigger'); setControllerPartLayer('trigger', 'highlight'); Messages.sendLocalMessage('Hifi-Grab-Disable', JSON.stringify({ @@ -602,7 +591,6 @@ stepFarGrab.prototype = { cleanup: function() { debug("FarGrab | Cleanup"); this.finished = true; - setControllerVisible("trigger", false); setControllerPartLayer('tips', 'blank'); setControllerPartLayer('trigger', 'normal'); hideEntitiesWithTag(this.tag, { visible: false}); @@ -662,7 +650,6 @@ var stepEquip = function(name) { } stepEquip.prototype = { start: function(onFinish) { - setControllerVisible("trigger", true); setControllerPartLayer('tips', 'trigger'); setControllerPartLayer('trigger', 'highlight'); Messages.sendLocalMessage('Hifi-Grab-Disable', JSON.stringify({ @@ -764,7 +751,6 @@ stepEquip.prototype = { this.watcherIntervalID = null; } - setControllerVisible("trigger", false); setControllerPartLayer('tips', 'blank'); setControllerPartLayer('trigger', 'normal'); this.stopWatchingGun(); @@ -798,9 +784,6 @@ var stepTurnAround = function(name) { } stepTurnAround.prototype = { start: function(onFinish) { - setControllerVisible("left", true); - setControllerVisible("right", true); - setControllerPartLayer('touchpad', 'arrows'); setControllerPartLayer('tips', 'arrows'); @@ -840,9 +823,6 @@ stepTurnAround.prototype = { } catch (e) { } - setControllerVisible("left", false); - setControllerVisible("right", false); - setControllerPartLayer('touchpad', 'blank'); setControllerPartLayer('tips', 'blank'); From acb44d021cbbf9e9a5d15985c9b201889860b84b Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Wed, 19 Oct 2016 10:45:51 -0700 Subject: [PATCH 23/49] Add HUD hiding to tutorial --- tutorial/tutorialZone.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tutorial/tutorialZone.js b/tutorial/tutorialZone.js index 42565aa99b..1538ebba95 100644 --- a/tutorial/tutorialZone.js +++ b/tutorial/tutorialZone.js @@ -72,6 +72,7 @@ if (!Function.prototype.bind) { setAwayEnabled(false); Menu.setIsOptionChecked("Overlays", false); MyAvatar.shouldRenderLocally = false; + Toolbars.getToolbar("com.highfidelity.interface.toolbar.system").writeProperty("visible", false); this.token = new OwnershipToken(Math.random() * 100000, this.entityID, { onGainedOwnership: function(token) { print("TutorialZone | GOT OWNERSHIP"); @@ -108,6 +109,7 @@ if (!Function.prototype.bind) { Menu.setIsOptionChecked("Overlays", true); MyAvatar.shouldRenderLocally = true; setAwayEnabled(true); + Toolbars.getToolbar("com.highfidelity.interface.toolbar.system").writeProperty("visible", true); } }, @@ -121,6 +123,10 @@ if (!Function.prototype.bind) { this.token.destroy(); this.token = null; } + if (self.tutorialManager) { + self.tutorialManager.stopTutorial(); + self.tutorialManager = null; + } } }; From 9207e882b19efe2fb84c4d3969601bab679997d6 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Wed, 19 Oct 2016 15:48:17 -0700 Subject: [PATCH 24/49] Update butane lighter equip position in tutorial --- tutorial/lighter/createButaneLighter.js | 58 ++++++++++++++----------- 1 file changed, 32 insertions(+), 26 deletions(-) diff --git a/tutorial/lighter/createButaneLighter.js b/tutorial/lighter/createButaneLighter.js index caf3188b14..1a6b94d0f6 100644 --- a/tutorial/lighter/createButaneLighter.js +++ b/tutorial/lighter/createButaneLighter.js @@ -53,33 +53,39 @@ createButaneLighter = function(transform) { shapeType: 'simple-compound', type: 'Model', userData: JSON.stringify({ - tag: "equip-temporary", - grabbableKey: { - invertSolidWhileHeld: true + "tag": "equip-temporary", + "grabbableKey": { + "invertSolidWhileHeld": true }, - wearable: { - joints: { - RightHand: [{ - x: 0.029085848480463028, - y: 0.09807153046131134, - z: 0.03062543272972107 - }, { - x: 0.5929139256477356, - y: 0.3207578659057617, - z: 0.7151655554771423, - w: -0.18468326330184937 - }], - LeftHand: [{ - x: -0.029085848480463028, - y: 0.09807153046131134, - z: 0.03062543272972107 - }, { - x: -0.5929139256477356, - y: 0.3207578659057617, - z: 0.7151655554771423, - w: -0.18468326330184937 - }] - } + "wearable": { + "joints": { + "RightHand": [ + { + "x": 0.049671292304992676, + "y": 0.09825992584228516, + "z": 0.03760027885437012 + }, + { + "x": 0.6562752723693848, + "y": 0.27598991990089417, + "z": 0.6638742685317993, + "w": -0.22890058159828186 + } + ], + "LeftHand": [ + { + "x": -0.028073370456695557, + "y": 0.09609812498092651, + "z": 0.039550721645355225 + }, + { + "x": -0.6697965264320374, + "y": 0.22050897777080536, + "z": 0.6544681191444397, + "w": 0.27283111214637756 + } + ] + } } }), script: SCRIPT_URL From 639911e11f4d1b419802aea8fd50578da4c142d4 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 20 Oct 2016 16:29:25 -0700 Subject: [PATCH 25/49] Add extra logging to ownershipToken --- tutorial/ownershipToken.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tutorial/ownershipToken.js b/tutorial/ownershipToken.js index b76f3af1c2..4a970af66d 100644 --- a/tutorial/ownershipToken.js +++ b/tutorial/ownershipToken.js @@ -81,7 +81,6 @@ var TOKEN_STATE_OWNED = 2; OwnershipToken = function(name, parentEntityID, options) { this.name = MyAvatar.sessionUUID + "-" + Math.floor(Math.random() * 10000000); - this.name = Math.floor(Math.random() * 10000000); this.parentEntityID = parentEntityID; // How often to check whether the token is available if we don't currently own it @@ -160,7 +159,7 @@ OwnershipToken.prototype = { var ownerID = getOwnershipTokenID(this.parentEntityID); if (ownerID !== null) { // Already owned, return - debug(this.name, "Token already owned by another client, return"); + debug(this.name, "Token already owned by another client, returning. Owner: " + owenerID + ", Us: " + this.name); return; } From d577493d5efdcafd58a886988513865a8f8c16c6 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 20 Oct 2016 16:39:35 -0700 Subject: [PATCH 26/49] Remove unneeded comments in tutorial scripts --- tutorial/entityData.js | 1 - tutorial/fuse.js | 1 - tutorial/fuseCollider.js | 1 - tutorial/tutorial.js | 9 --------- 4 files changed, 12 deletions(-) diff --git a/tutorial/entityData.js b/tutorial/entityData.js index 407e6e6b00..b14185e78f 100644 --- a/tutorial/entityData.js +++ b/tutorial/entityData.js @@ -60,7 +60,6 @@ birdFirework2 = { "z": 0 }, "id": "{ba067084-8d0f-4eeb-a8a1-c6814527c1bb}", - //"modelURL": "atp:/tutorial_models/statuebird4.fbx", "modelURL": "atp:/tutorial_models/bomb2.fbx", "owningAvatarID": "{00000000-0000-0000-0000-000000000000}", "position": { diff --git a/tutorial/fuse.js b/tutorial/fuse.js index 73a3a697bd..59306f4113 100644 --- a/tutorial/fuse.js +++ b/tutorial/fuse.js @@ -84,7 +84,6 @@ var self = this; Script.setTimeout(function() { debug("Setting off fireworks"); - //var spinnerID = Utils.findEntity({ name: "tutorial/equip/spinner" }, 20); var spinnerID = "{dd13fcd5-616f-4749-ab28-2e1e8bc512e9}"; Entities.callEntityMethod(spinnerID, "onLit"); injector.stop(); diff --git a/tutorial/fuseCollider.js b/tutorial/fuseCollider.js index 147444ac1d..953fcd316d 100644 --- a/tutorial/fuseCollider.js +++ b/tutorial/fuseCollider.js @@ -6,7 +6,6 @@ Fuse.prototype = { onLit: function() { print("fuseCollider.js | Lit", this.entityID); - //var fuseID = Utils.findEntity({ name: "tutorial/equip/fuse" }, 20); var fuseID = "{c8944a13-9acb-4d77-b1ee-851845e98357}" Entities.callEntityMethod(fuseID, "light"); }, diff --git a/tutorial/tutorial.js b/tutorial/tutorial.js index ea8dd792de..361d345011 100644 --- a/tutorial/tutorial.js +++ b/tutorial/tutorial.js @@ -193,7 +193,6 @@ function deleteEntitiesWithTag(tag) { } } function editEntitiesWithTag(tag, propertiesOrFn) { - //var entityIDs = findEntitiesWithTag(tag); var entities = TUTORIAL_TAG_TO_ENTITY_IDS_MAP[tag]; debug("Editing tag: ", tag); @@ -665,12 +664,6 @@ stepEquip.prototype = { this.currentPart = this.PART1; function createLighter() { - //var boxSpawnID = findEntity({ name: GUN_SPAWN_NAME }, 10000); - //if (!boxSpawnID) { - //info("Error creating block, cannot find spawn"); - //return null; - //} - var transform = {}; var boxSpawnProps = getEntityWithName(GUN_SPAWN_NAME); @@ -855,8 +848,6 @@ stepTeleport.prototype = { Messages.sendLocalMessage('Hifi-Teleport-Disabler', 'none'); // Wait until touching teleport pad... - //var padID = findEntity({ name: TELEPORT_PAD_NAME }, 100); - //var padProps = Entities.getEntityProperties(padID, ["position", "dimensions"]); var padProps = getEntityWithName(TELEPORT_PAD_NAME); var xMin = padProps.position.x - padProps.dimensions.x / 2; var xMax = padProps.position.x + padProps.dimensions.x / 2; From 8a7f67c8497780c6309e142ae63e23ccaeb6fd62 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 27 Oct 2016 15:01:11 -0700 Subject: [PATCH 27/49] Cleanup client controller display implementation in tutorial --- tutorial/tutorial.js | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/tutorial/tutorial.js b/tutorial/tutorial.js index 361d345011..7b258611c7 100644 --- a/tutorial/tutorial.js +++ b/tutorial/tutorial.js @@ -250,10 +250,10 @@ var stepDisableControllers = function(name) { } stepDisableControllers.prototype = { start: function(onFinish) { - HMD.requestShowHandControllers(); - disableEverything(); + HMD.requestShowHandControllers(); + onFinish(); }, cleanup: function() { @@ -276,8 +276,6 @@ function disableEverything() { hideEntitiesWithTag('finish'); - controllerDisplayManager = new ControllerDisplayManager(); - setAwayEnabled(false); } @@ -295,7 +293,6 @@ function reenableEverything() { setControllerPartLayer('touchpad', 'blank'); setControllerPartLayer('tips', 'blank'); MyAvatar.shouldRenderLocally = true; - HMD.requestHideHandControllers(); setAwayEnabled(true); } @@ -313,6 +310,7 @@ var stepEnableControllers = function(name) { stepEnableControllers.prototype = { start: function(onFinish) { reenableEverything(); + HMD.requestHideHandControllers(); onFinish(); }, cleanup: function() { @@ -1099,6 +1097,7 @@ TutorialManager = function() { this.stopTutorial = function() { if (currentStep) { currentStep.cleanup(); + HMD.requestHideHandControllers(); } reenableEverything(); currentStepNum = -1; From 7b881a6be07e2275d37da74ad2809def4c46addf Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 27 Oct 2016 15:02:21 -0700 Subject: [PATCH 28/49] Add begin step to tutorial --- tutorial/tutorial.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/tutorial/tutorial.js b/tutorial/tutorial.js index 7b258611c7..1045599262 100644 --- a/tutorial/tutorial.js +++ b/tutorial/tutorial.js @@ -244,11 +244,10 @@ function playFirecrackerSound(position) { // STEP: DISABLE CONTROLLERS // // // /////////////////////////////////////////////////////////////////////////////// -var stepDisableControllers = function(name) { +var stepStart = function(name) { this.tag = name; - this.shouldLog = false; } -stepDisableControllers.prototype = { +stepStart.prototype = { start: function(onFinish) { disableEverything(); @@ -1035,7 +1034,7 @@ TutorialManager = function() { currentStep = null; startedTutorialAt = Date.now(); STEPS = [ - new stepDisableControllers("step0"), + new stepStart("start"), new stepOrient("orient"), //new stepRaiseAboveHead("raiseHands"), new stepNearGrab("nearGrab"), From e6d2900becdc2eb7767441a79d3549a554b15fe2 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 27 Oct 2016 15:02:35 -0700 Subject: [PATCH 29/49] Loosen up the requirements for the turn step in the tutorial --- tutorial/tutorial.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tutorial/tutorial.js b/tutorial/tutorial.js index 1045599262..0fb86d7247 100644 --- a/tutorial/tutorial.js +++ b/tutorial/tutorial.js @@ -784,8 +784,8 @@ stepTurnAround.prototype = { this.interval = Script.setInterval(function() { debug("TurnAround | Checking if finished", this.numTimesTurnPressed); - var FORWARD_THRESHOLD = 30; - var REQ_NUM_TIMES_PRESSED = 6; + var FORWARD_THRESHOLD = 90; + var REQ_NUM_TIMES_PRESSED = 3; var dir = Quat.getFront(MyAvatar.orientation); var angle = Math.atan2(dir.z, dir.x); From 7227e5770a229c2c1e1025e02962a52275fbba36 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 27 Oct 2016 15:02:57 -0700 Subject: [PATCH 30/49] Fix bug in tutorialZone using self instead of this --- tutorial/tutorialZone.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/tutorial/tutorialZone.js b/tutorial/tutorialZone.js index 1538ebba95..a40065e5d3 100644 --- a/tutorial/tutorialZone.js +++ b/tutorial/tutorialZone.js @@ -105,7 +105,7 @@ if (!Function.prototype.bind) { // If the start zone was exited, and the tutorial hasn't started, go ahead and // re-enable the HUD/Overlays - if (!self.tutorialManager) { + if (!this.tutorialManager) { Menu.setIsOptionChecked("Overlays", true); MyAvatar.shouldRenderLocally = true; setAwayEnabled(true); @@ -120,12 +120,13 @@ if (!Function.prototype.bind) { print("TutorialZone | EXITED THE TUTORIAL AREA"); if (this.token) { print("TutorialZone | Destroying token"); + //HMD.requestHideHandControllers(); this.token.destroy(); this.token = null; } - if (self.tutorialManager) { - self.tutorialManager.stopTutorial(); - self.tutorialManager = null; + if (this.tutorialManager) { + this.tutorialManager.stopTutorial(); + this.tutorialManager = null; } } }; From 47d7e89699f084e810eaff75df3840bca09276c8 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 27 Oct 2016 15:03:15 -0700 Subject: [PATCH 31/49] Update portal to ensure that properties exist --- .../DomainContent/Home/portal.js | 41 +++++++++++-------- 1 file changed, 24 insertions(+), 17 deletions(-) diff --git a/unpublishedScripts/DomainContent/Home/portal.js b/unpublishedScripts/DomainContent/Home/portal.js index 0ea090a6f7..ea6241265c 100644 --- a/unpublishedScripts/DomainContent/Home/portal.js +++ b/unpublishedScripts/DomainContent/Home/portal.js @@ -1,40 +1,47 @@ (function(){ var teleport; var portalDestination; + var thisEntityID; function playSound() { - Audio.playSound(teleport, { volume: 0.40, localOnly: true }); + var properties = Entities.getEntityProperties(thisEntityID, 'position'); + if (properties) { + Audio.playSound(teleport, { position: properties.position, volume: 0.40, localOnly: true }); + } }; this.preload = function(entityID) { + thisEntityID = entityID; teleport = SoundCache.getSound("atp:/sounds/teleport.raw"); - var properties = Entities.getEntityProperties(entityID); - portalDestination = properties.userData; - - print("portal.js | The portal destination is " + portalDestination); + var properties = Entities.getEntityProperties(entityID, 'userData'); + if (properties) { + portalDestination = properties.userData; + print("portal.js | The portal destination is " + portalDestination); + } } this.enterEntity = function(entityID) { print("portal.js | enterEntity"); - var properties = Entities.getEntityProperties(entityID); // in case the userData/portalURL has changed - portalDestination = properties.userData; + var properties = Entities.getEntityProperties(entityID, 'userData'); // in case the userData/portalURL has changed + if (properties) { + portalDestination = properties.userData; - print("portal.js | enterEntity() .... The portal destination is " + portalDestination); + print("portal.js | enterEntity() .... The portal destination is " + portalDestination); - if (portalDestination.length > 0) { - if (portalDestination[0] == '/') { - print("Teleporting to " + portalDestination); - Window.location = portalDestination; + if (portalDestination.length > 0) { + if (portalDestination[0] == '/') { + print("Teleporting to " + portalDestination); + Window.location = portalDestination; + } else { + print("Teleporting to hifi://" + portalDestination); + Window.location = "hifi://" + portalDestination; + } } else { - print("Teleporting to hifi://" + portalDestination); - Window.location = "hifi://" + portalDestination; + location.goToEntry(); // going forward: no data means go to appropriate entry point } - } else { - location.goToEntry(); // going forward: no data means go to appropriate entry point } - }; this.leaveEntity = function(entityID) { From 5eef9867b4a50201f793def079f590d9f12e06c3 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 27 Oct 2016 15:04:00 -0700 Subject: [PATCH 32/49] Remove comment in tutorialZone --- tutorial/tutorialZone.js | 1 - 1 file changed, 1 deletion(-) diff --git a/tutorial/tutorialZone.js b/tutorial/tutorialZone.js index a40065e5d3..40028c9cd7 100644 --- a/tutorial/tutorialZone.js +++ b/tutorial/tutorialZone.js @@ -120,7 +120,6 @@ if (!Function.prototype.bind) { print("TutorialZone | EXITED THE TUTORIAL AREA"); if (this.token) { print("TutorialZone | Destroying token"); - //HMD.requestHideHandControllers(); this.token.destroy(); this.token = null; } From 69e7dababc58634a00acc08fed8050f65a26797a Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 27 Oct 2016 15:16:34 -0700 Subject: [PATCH 33/49] Collapse multiline dict to one line in controllerDisplay --- scripts/system/controllers/controllerDisplay.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/scripts/system/controllers/controllerDisplay.js b/scripts/system/controllers/controllerDisplay.js index 96ee955d19..6135f18426 100644 --- a/scripts/system/controllers/controllerDisplay.js +++ b/scripts/system/controllers/controllerDisplay.js @@ -43,10 +43,8 @@ function debug() { createControllerDisplay = function(config) { var controllerDisplay = { overlays: [], - partOverlays: { - }, - parts: { - }, + partOverlays: {}, + parts: {}, mappingName: "mapping-display-" + Math.random(), setVisible: function(visible) { From 4befab11511fcb7b2b8679768459e72128769892 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Fri, 28 Oct 2016 11:34:00 +1300 Subject: [PATCH 34/49] JSLint progress bar code --- scripts/system/progress.js | 117 +++++++++++++++++-------------------- 1 file changed, 55 insertions(+), 62 deletions(-) diff --git a/scripts/system/progress.js b/scripts/system/progress.js index d7f6713c69..b93cdb9218 100644 --- a/scripts/system/progress.js +++ b/scripts/system/progress.js @@ -13,18 +13,14 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -(function() { // BEGIN LOCAL_SCOPE +(function () { // BEGIN LOCAL_SCOPE function debug() { - return; - print.apply(null, arguments); + //print.apply(null, arguments); } var rawProgress = 100, // % raw value. displayProgress = 100, // % smoothed value to display. - DISPLAY_PROGRESS_MINOR_MAXIMUM = 8, // % displayed progress bar goes up to while 0% raw progress. - DISPLAY_PROGRESS_MINOR_INCREMENT = 0.1, // % amount to increment display value each update when 0% raw progress. - DISPLAY_PROGRESS_MAJOR_INCREMENT = 5, // % maximum amount to increment display value when >0% raw progress. alpha = 0.0, alphaDelta = 0.0, // > 0 if fading in; < 0 if fading out. ALPHA_DELTA_IN = 0.15, @@ -46,7 +42,28 @@ windowHeight = 0, background2D = {}, bar2D = {}, - SCALE_2D = 0.35; // Scale the SVGs for 2D display. + SCALE_2D = 0.35, // Scale the SVGs for 2D display. + + // Max seen since downloads started. This is reset when all downloads have completed. + maxSeen = 0, + + // Progress is defined as: (pending_downloads + active_downloads) / max_seen + // We keep track of both the current progress (rawProgress) and the + // best progress we've seen (bestRawProgress). As you are downloading, you may + // encounter new assets that require downloads, increasing the number of + // pending downloads and thus decreasing your overall progress. + bestRawProgress = 0, + + // True if we have known active downloads + isDownloading = false, + + // Entities are streamed to users, so you don't receive them all at once; instead, you + // receive them over a period of time. In many cases we end up in a situation where + // + // The initial delay cooldown keeps us from tracking progress before the allotted time + // has passed. + INITIAL_DELAY_COOLDOWN_TIME = 1000, + initialDelayCooldown = 0; function fade() { @@ -77,35 +94,14 @@ }); } - Window.domainChanged.connect(function() { + Window.domainChanged.connect(function () { isDownloading = false; bestRawProgress = 100; rawProgress = 100; displayProgress = 100; }); - // Max seen since downloads started. This is reset when all downloads have completed. - var maxSeen = 0; - - // Progress is defined as: (pending_downloads + active_downloads) / max_seen - // We keep track of both the current progress (rawProgress) and the - // best progress we've seen (bestRawProgress). As you are downloading, you may - // encounter new assets that require downloads, increasing the number of - // pending downloads and thus decreasing your overall progress. - var bestRawProgress = 0; - - // True if we have known active downloads - var isDownloading = false; - - // Entities are streamed to users, so you don't receive them all at once; instead, you - // receive them over a period of time. In many cases we end up in a situation where - // - // The initial delay cooldown keeps us from tracking progress before the allotted time - // has passed. - var INITIAL_DELAY_COOLDOWN_TIME = 1000; - var initialDelayCooldown = 0; function onDownloadInfoChanged(info) { - var i; debug("PROGRESS: Download info changed ", info.downloading.length, info.pending, maxSeen); @@ -167,16 +163,36 @@ Overlays.deleteOverlay(bar2D.overlay); } - var b = 0; - var currentOrientation = null; + function updateProgressBarLocation() { + var viewport = Controller.getViewportDimensions(), + yOffset; + windowWidth = viewport.x; + windowHeight = viewport.y; + + yOffset = HMD.active ? BAR_Y_OFFSET_HMD : BAR_Y_OFFSET_2D; + + background2D.width = SCALE_2D * BACKGROUND_WIDTH; + background2D.height = SCALE_2D * BACKGROUND_HEIGHT; + bar2D.width = SCALE_2D * BAR_WIDTH; + bar2D.height = SCALE_2D * BAR_HEIGHT; + + Overlays.editOverlay(background2D.overlay, { + x: windowWidth / 2 - background2D.width / 2, + y: windowHeight - background2D.height - bar2D.height + yOffset + }); + + Overlays.editOverlay(bar2D.overlay, { + x: windowWidth / 2 - bar2D.width / 2, + y: windowHeight - background2D.height - bar2D.height + (background2D.height - bar2D.height) / 2 + yOffset + }); + } + function update() { initialDelayCooldown -= 30; - var viewport, - eyePosition, - avatarOrientation; + var viewport, diff; if (displayProgress < rawProgress) { - var diff = rawProgress - displayProgress; + diff = rawProgress - displayProgress; if (diff < 0.5) { displayProgress = rawProgress; } else { @@ -204,7 +220,7 @@ } else { // Fully visible because downloading or recently so if (fadeWaitTimer === null) { if (rawProgress === 100) { // Was downloading but have finished so fade out soon - fadeWaitTimer = Script.setTimeout(function() { + fadeWaitTimer = Script.setTimeout(function () { alphaDelta = ALPHA_DELTA_OUT; fadeTimer = Script.setInterval(fade, FADE_INTERVAL); fadeWaitTimer = null; @@ -228,11 +244,11 @@ y: 0, width: BAR_WIDTH, height: BAR_HEIGHT - }, + } }); Overlays.editOverlay(background2D.overlay, { - visible: true, + visible: true }); // Update 2D overlays to maintain positions at bottom middle of window @@ -244,29 +260,6 @@ } } - function updateProgressBarLocation() { - var viewport = Controller.getViewportDimensions(); - windowWidth = viewport.x; - windowHeight = viewport.y; - - var yOffset = HMD.active ? BAR_Y_OFFSET_HMD : BAR_Y_OFFSET_2D; - - background2D.width = SCALE_2D * BACKGROUND_WIDTH; - background2D.height = SCALE_2D * BACKGROUND_HEIGHT; - bar2D.width = SCALE_2D * BAR_WIDTH; - bar2D.height = SCALE_2D * BAR_HEIGHT; - - Overlays.editOverlay(background2D.overlay, { - x: windowWidth / 2 - background2D.width / 2, - y: windowHeight - background2D.height - bar2D.height + yOffset - }); - - Overlays.editOverlay(bar2D.overlay, { - x: windowWidth / 2 - bar2D.width / 2, - y: windowHeight - background2D.height - bar2D.height + (background2D.height - bar2D.height) / 2 + yOffset - }); - } - function setUp() { background2D.width = SCALE_2D * BACKGROUND_WIDTH; background2D.height = SCALE_2D * BACKGROUND_HEIGHT; @@ -283,7 +276,7 @@ setUp(); GlobalServices.downloadInfoChanged.connect(onDownloadInfoChanged); GlobalServices.updateDownloadInfo(); - Script.setInterval(update, 1000/60); + Script.setInterval(update, 1000 / 60); Script.scriptEnding.connect(tearDown); }()); // END LOCAL_SCOPE From 018d945b85b258ecb2d62295e259113518f0b9f7 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Wed, 26 Oct 2016 14:13:02 -0700 Subject: [PATCH 35/49] Display warning overlay instead of scene when below min spec --- .../images/steam-min-spec-failed.png | Bin 0 -> 73490 bytes interface/src/main.cpp | 25 +++++- plugins/openvr/src/OpenVrDisplayPlugin.cpp | 3 - plugins/openvr/src/OpenVrHelpers.cpp | 82 ++++++++++++++++++ plugins/openvr/src/OpenVrHelpers.h | 2 + 5 files changed, 108 insertions(+), 4 deletions(-) create mode 100644 interface/resources/images/steam-min-spec-failed.png diff --git a/interface/resources/images/steam-min-spec-failed.png b/interface/resources/images/steam-min-spec-failed.png new file mode 100644 index 0000000000000000000000000000000000000000..adb380ee8be5d6a820eb35eb740d64b33c4bac24 GIT binary patch literal 73490 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 literal 0 HcmV?d00001 diff --git a/interface/src/main.cpp b/interface/src/main.cpp index 5b0da4f578..c33f7f8799 100644 --- a/interface/src/main.cpp +++ b/interface/src/main.cpp @@ -37,7 +37,9 @@ #include #endif - +extern "C" { + typedef int(__stdcall * CHECKMINSPECPROC) (); +} int main(int argc, const char* argv[]) { #if HAS_BUGSPLAT @@ -155,6 +157,27 @@ int main(int argc, const char* argv[]) { SteamClient::init(); +#ifdef Q_OS_WIN + // If we're running in steam mode, we need to do an explicit check to ensure we're up to the required min spec + if (SteamClient::isRunning()) { + QString appPath; + { + char filename[MAX_PATH]; + GetModuleFileName(NULL, filename, MAX_PATH); + QFileInfo appInfo(filename); + appPath = appInfo.absolutePath(); + } + QString openvrDllPath = appPath + "/plugins/openvr.dll"; + HMODULE openvrDll; + CHECKMINSPECPROC checkMinSpecPtr; + if ((openvrDll = LoadLibrary(openvrDllPath.toLocal8Bit().data())) && + (checkMinSpecPtr = (CHECKMINSPECPROC)GetProcAddress(openvrDll, "CheckMinSpec"))) { + if (!checkMinSpecPtr()) { + return -1; + } + } + } +#endif int exitCode; { diff --git a/plugins/openvr/src/OpenVrDisplayPlugin.cpp b/plugins/openvr/src/OpenVrDisplayPlugin.cpp index 25160c35e6..1a4067a847 100644 --- a/plugins/openvr/src/OpenVrDisplayPlugin.cpp +++ b/plugins/openvr/src/OpenVrDisplayPlugin.cpp @@ -379,9 +379,6 @@ void OpenVrDisplayPlugin::init() { emit deviceConnected(getName()); } -// FIXME remove once OpenVR header is updated -#define VRCompositor_ReprojectionAsync 0x04 - bool OpenVrDisplayPlugin::internalActivate() { if (!_system) { _system = acquireOpenVrSystem(); diff --git a/plugins/openvr/src/OpenVrHelpers.cpp b/plugins/openvr/src/OpenVrHelpers.cpp index 91cad67d46..35730a0d5e 100644 --- a/plugins/openvr/src/OpenVrHelpers.cpp +++ b/plugins/openvr/src/OpenVrHelpers.cpp @@ -12,11 +12,13 @@ #include #include +#include #include #include #include #include +#include #include #include #include @@ -324,3 +326,83 @@ controller::Pose openVrControllerPoseToHandPose(bool isLeftHand, const mat4& mat result.angularVelocity = angularVelocity; return result; } + +#define FAILED_MIN_SPEC_OVERLAY_NAME "FailedMinSpecOverlay" +#define FAILED_MIN_SPEC_OVERLAY_FRIENDLY_NAME "Minimum specifications for SteamVR not met" +#define OVERLAY_UPDATE_INTERVAL_MS 10 +#define OVERLAY_DISPLAY_INTERVAL_MS (MSECS_PER_SECOND * 10) +#define MIN_CORES_SPEC 5 + +void showMinSpecWarning() { + auto vrSystem = acquireOpenVrSystem(); + auto vrOverlay = vr::VROverlay(); + if (!vrOverlay) { + qFatal("Unable to initialize SteamVR overlay manager"); + } + + vr::VROverlayHandle_t minSpecFailedOverlay = 0; + if (vr::VROverlayError_None != vrOverlay->CreateOverlay(FAILED_MIN_SPEC_OVERLAY_NAME, FAILED_MIN_SPEC_OVERLAY_FRIENDLY_NAME, &minSpecFailedOverlay)) { + qFatal("Unable to create overlay"); + } + + // Needed here for PathUtils + QCoreApplication miniApp(__argc, __argv); + + vrSystem->ResetSeatedZeroPose(); + QString imagePath = PathUtils::resourcesPath() + "/images/steam-min-spec-failed.png"; + vrOverlay->SetOverlayFromFile(minSpecFailedOverlay, imagePath.toLocal8Bit().toStdString().c_str()); + vrOverlay->SetHighQualityOverlay(minSpecFailedOverlay); + vrOverlay->ShowOverlay(minSpecFailedOverlay); + + QTimer* positionTimer = new QTimer(&miniApp); + positionTimer->setInterval(OVERLAY_UPDATE_INTERVAL_MS); + QObject::connect(positionTimer, &QTimer::timeout, [&] { + vr::TrackedDevicePose_t vrPoses[vr::k_unMaxTrackedDeviceCount]; + vrSystem->GetDeviceToAbsoluteTrackingPose(vr::TrackingUniverseSeated, 0, vrPoses, vr::k_unMaxTrackedDeviceCount); + auto headPose = toGlm(vrPoses[vr::k_unTrackedDeviceIndex_Hmd].mDeviceToAbsoluteTracking); + auto overlayPose = toOpenVr(headPose * glm::translate(glm::mat4(), vec3(0, 0, -1))); + vrOverlay->SetOverlayTransformAbsolute(minSpecFailedOverlay, vr::TrackingUniverseSeated, &overlayPose); + }); + positionTimer->start(); + QTimer::singleShot(OVERLAY_DISPLAY_INTERVAL_MS, &miniApp, &QCoreApplication::quit); + miniApp.exec(); +} + + +bool checkMinSpecImpl() { + // If OpenVR isn't supported, we have no min spec, so pass + if (!openVrSupported()) { + return true; + } + + // If we have at least 5 cores, pass + auto coreCount = QThread::idealThreadCount(); + if (coreCount >= MIN_CORES_SPEC) { + return true; + } + + // Even if we have too few cores... if the compositor is using async reprojection, pass + auto system = acquireOpenVrSystem(); + auto compositor = vr::VRCompositor(); + if (system && compositor) { + vr::Compositor_FrameTiming timing; + memset(&timing, 0, sizeof(timing)); + timing.m_nSize = sizeof(vr::Compositor_FrameTiming); + compositor->GetFrameTiming(&timing); + releaseOpenVrSystem(); + if (timing.m_nReprojectionFlags & VRCompositor_ReprojectionAsync) { + return true; + } + } + + // We're using OpenVR and we don't have enough cores... + showMinSpecWarning(); + + return false; +} + +extern "C" { + __declspec(dllexport) int __stdcall CheckMinSpec() { + return checkMinSpecImpl() ? 1 : 0; + } +} diff --git a/plugins/openvr/src/OpenVrHelpers.h b/plugins/openvr/src/OpenVrHelpers.h index 4279e6a6ac..be79dd1155 100644 --- a/plugins/openvr/src/OpenVrHelpers.h +++ b/plugins/openvr/src/OpenVrHelpers.h @@ -82,5 +82,7 @@ struct PoseData { } }; +// FIXME remove once OpenVR header is updated +#define VRCompositor_ReprojectionAsync 0x04 controller::Pose openVrControllerPoseToHandPose(bool isLeftHand, const mat4& mat, const vec3& linearVelocity, const vec3& angularVelocity); From 292dd674335867cd070e09332aeb3dcca793f98e Mon Sep 17 00:00:00 2001 From: David Kelly Date: Thu, 27 Oct 2016 16:04:30 -0700 Subject: [PATCH 36/49] cleanup --- assignment-client/src/audio/AudioMixer.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp index ad4e6c5138..7861f69b2e 100644 --- a/assignment-client/src/audio/AudioMixer.cpp +++ b/assignment-client/src/audio/AudioMixer.cpp @@ -857,9 +857,6 @@ void AudioMixer::broadcastMixes() { // pack number of silent audio samples quint16 numSilentSamples = AudioConstants::NETWORK_FRAME_SAMPLES_STEREO; mixPacket->writePrimitive(numSilentSamples); - - // we will need to flush encoder since we are sending silent packets outside it - nodeData->shouldFlushEncoder(); } // Send audio environment From e9db8be43510754410af2fbb0540ac3191fb3b76 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Thu, 27 Oct 2016 16:51:52 -0700 Subject: [PATCH 37/49] Fix linux build --- interface/src/main.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/interface/src/main.cpp b/interface/src/main.cpp index c33f7f8799..a88388050b 100644 --- a/interface/src/main.cpp +++ b/interface/src/main.cpp @@ -37,9 +37,11 @@ #include #endif +#ifdef Q_OS_WIN extern "C" { typedef int(__stdcall * CHECKMINSPECPROC) (); } +#endif int main(int argc, const char* argv[]) { #if HAS_BUGSPLAT From e3a22618d1abaea263fc5f87cd0d0e6e1273e3be Mon Sep 17 00:00:00 2001 From: David Kelly Date: Thu, 27 Oct 2016 17:49:43 -0700 Subject: [PATCH 38/49] CR feedback --- assignment-client/src/Agent.cpp | 9 +++------ assignment-client/src/Agent.h | 2 +- assignment-client/src/audio/AudioMixer.cpp | 5 +++-- assignment-client/src/audio/AudioMixerClientData.cpp | 2 +- assignment-client/src/audio/AudioMixerClientData.h | 2 +- 5 files changed, 9 insertions(+), 11 deletions(-) diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index 5b299ea6a9..23bee2c91a 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -474,7 +474,7 @@ void Agent::processAgentAvatar() { nodeList->broadcastToNodes(std::move(avatarPacket), NodeSet() << NodeType::AvatarMixer); } } -void Agent::flushEncoder(QByteArray& encodedZeros) { +void Agent::encodeFrameOfZeros(QByteArray& encodedZeros) { _flushEncoder = false; static const QByteArray zeros(AudioConstants::NETWORK_FRAME_BYTES_PER_CHANNEL, 0); if (_encoder) { @@ -560,14 +560,11 @@ void Agent::processAgentAvatarAudio() { QByteArray encodedBuffer; if (_flushEncoder) { - // after sound is done playing, encoder has a bit of state in it, - // and needs some 0s to forget or you get a little click next time - // you play something. So, basically 0-pad the end of the sound buffer - flushEncoder(encodedBuffer); + encodeFrameOfZeros(encodedBuffer); } else { - // encode it QByteArray decodedBuffer(reinterpret_cast(nextSoundOutput), numAvailableSamples*sizeof(int16_t)); if (_encoder) { + // encode it _encoder->encode(decodedBuffer, encodedBuffer); } else { encodedBuffer = decodedBuffer; diff --git a/assignment-client/src/Agent.h b/assignment-client/src/Agent.h index b11e492136..939b51625a 100644 --- a/assignment-client/src/Agent.h +++ b/assignment-client/src/Agent.h @@ -81,7 +81,7 @@ signals: private: void negotiateAudioFormat(); void selectAudioFormat(const QString& selectedCodecName); - void flushEncoder(QByteArray& encodedZeros); + void encodeFrameOfZeros(QByteArray& encodedZeros); std::unique_ptr _scriptEngine; EntityEditPacketSender _entityEditSender; diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp index 2f26042266..bc356b8ce1 100644 --- a/assignment-client/src/audio/AudioMixer.cpp +++ b/assignment-client/src/audio/AudioMixer.cpp @@ -825,15 +825,16 @@ void AudioMixer::broadcastMixes() { mixPacket->writeString(codecInPacket); QByteArray encodedBuffer; - if ( mixHasAudio) { + if (mixHasAudio) { QByteArray decodedBuffer(reinterpret_cast(_clampedSamples), AudioConstants::NETWORK_FRAME_BYTES_STEREO); nodeData->encode(decodedBuffer, encodedBuffer); } else { // time to flush, which resets the shouldFlush until next time we encode something - nodeData->flushEncoder(encodedBuffer); + nodeData->encodeFrameOfZeros(encodedBuffer); } // pack mixed audio samples mixPacket->write(encodedBuffer.constData(), encodedBuffer.size()); + } else { int silentPacketBytes = sizeof(quint16) + sizeof(quint16) + AudioConstants::MAX_CODEC_NAME_LENGTH_ON_WIRE; mixPacket = NLPacket::create(PacketType::SilentAudioFrame, silentPacketBytes); diff --git a/assignment-client/src/audio/AudioMixerClientData.cpp b/assignment-client/src/audio/AudioMixerClientData.cpp index 836cf9321b..58d89697af 100644 --- a/assignment-client/src/audio/AudioMixerClientData.cpp +++ b/assignment-client/src/audio/AudioMixerClientData.cpp @@ -371,7 +371,7 @@ void AudioMixerClientData::sendSelectAudioFormat(SharedNodePointer node, const Q nodeList->sendPacket(std::move(replyPacket), *node); } -void AudioMixerClientData::flushEncoder(QByteArray& encodedZeros) { +void AudioMixerClientData::encodeFrameOfZeros(QByteArray& encodedZeros) { static QByteArray zeros(AudioConstants::NETWORK_FRAME_BYTES_STEREO, 0); if (_shouldFlushEncoder) { if (_encoder) { diff --git a/assignment-client/src/audio/AudioMixerClientData.h b/assignment-client/src/audio/AudioMixerClientData.h index 682c99584e..34263f9cbe 100644 --- a/assignment-client/src/audio/AudioMixerClientData.h +++ b/assignment-client/src/audio/AudioMixerClientData.h @@ -80,7 +80,7 @@ public: // once you have encoded, you need to flush eventually. _shouldFlushEncoder = true; } - void flushEncoder(QByteArray& encodedZeros); + void encodeFrameOfZeros(QByteArray& encodedZeros); bool shouldFlushEncoder() { return _shouldFlushEncoder; } QString getCodecName() { return _selectedCodecName; } From 082414298796b9f9a996dbc91f98c1fa8ccda747 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Fri, 28 Oct 2016 14:12:24 +1300 Subject: [PATCH 39/49] Add "loading" text --- .../assets/images/progress-bar-text.svg | 103 ++++++++++++++++++ scripts/system/progress.js | 75 +++++++++++-- 2 files changed, 167 insertions(+), 11 deletions(-) create mode 100644 scripts/system/assets/images/progress-bar-text.svg diff --git a/scripts/system/assets/images/progress-bar-text.svg b/scripts/system/assets/images/progress-bar-text.svg new file mode 100644 index 0000000000..05ebb3f637 --- /dev/null +++ b/scripts/system/assets/images/progress-bar-text.svg @@ -0,0 +1,103 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/scripts/system/progress.js b/scripts/system/progress.js index b93cdb9218..082ede95c4 100644 --- a/scripts/system/progress.js +++ b/scripts/system/progress.js @@ -35,14 +35,22 @@ BAR_Y_OFFSET_2D = -10, // Offset of progress bar while in desktop mode BAR_Y_OFFSET_HMD = -300, // Offset of progress bar while in HMD BAR_URL = Script.resolvePath("assets/images/progress-bar.svg"), + TEXT_HEIGHT = 32, + TEXT_WIDTH = 256, + TEXT_URL = Script.resolvePath("assets/images/progress-bar-text.svg"), BACKGROUND_WIDTH = 520, BACKGROUND_HEIGHT = 50, BACKGROUND_URL = Script.resolvePath("assets/images/progress-bar-background.svg"), windowWidth = 0, windowHeight = 0, - background2D = {}, bar2D = {}, + background2D = {}, + textDesktop = {}, // Separate desktop and HMD overlays because can't change text size after overlay created. + textHMD = {}, SCALE_2D = 0.35, // Scale the SVGs for 2D display. + SCALE_TEXT_DESKTOP = 0.6, + SCALE_TEXT_HMD = 1.0, + isHMD = false, // Max seen since downloads started. This is reset when all downloads have completed. maxSeen = 0, @@ -92,6 +100,14 @@ alpha: alpha, visible: visible }); + Overlays.editOverlay(textDesktop.overlay, { + alpha: alpha, + visible: visible && !isHMD + }); + Overlays.editOverlay(textHMD.overlay, { + alpha: alpha, + visible: visible && isHMD + }); } Window.domainChanged.connect(function () { @@ -156,6 +172,20 @@ visible: false, alpha: 0.0 }); + textDesktop.overlay = Overlays.addOverlay("image", { + imageURL: TEXT_URL, + width: textDesktop.width, + height: textDesktop.height, + visible: false, + alpha: 0.0 + }); + textHMD.overlay = Overlays.addOverlay("image", { + imageURL: TEXT_URL, + width: textHMD.width, + height: textHMD.height, + visible: false, + alpha: 0.0 + }); } function deleteOverlays() { @@ -166,15 +196,11 @@ function updateProgressBarLocation() { var viewport = Controller.getViewportDimensions(), yOffset; + windowWidth = viewport.x; windowHeight = viewport.y; - - yOffset = HMD.active ? BAR_Y_OFFSET_HMD : BAR_Y_OFFSET_2D; - - background2D.width = SCALE_2D * BACKGROUND_WIDTH; - background2D.height = SCALE_2D * BACKGROUND_HEIGHT; - bar2D.width = SCALE_2D * BAR_WIDTH; - bar2D.height = SCALE_2D * BAR_HEIGHT; + isHMD = HMD.active; + yOffset = isHMD ? BAR_Y_OFFSET_HMD : BAR_Y_OFFSET_2D; Overlays.editOverlay(background2D.overlay, { x: windowWidth / 2 - background2D.width / 2, @@ -185,12 +211,23 @@ x: windowWidth / 2 - bar2D.width / 2, y: windowHeight - background2D.height - bar2D.height + (background2D.height - bar2D.height) / 2 + yOffset }); + + Overlays.editOverlay(textDesktop.overlay, { + x: windowWidth / 2 - textDesktop.width / 2, + y: windowHeight - 2 * textDesktop.height + yOffset + }); + + Overlays.editOverlay(textHMD.overlay, { + x: windowWidth / 2 - textHMD.width / 2, + y: windowHeight - 2 * textHMD.height + yOffset + }); } function update() { - initialDelayCooldown -= 30; var viewport, diff; + initialDelayCooldown -= 30; + if (displayProgress < rawProgress) { diff = rawProgress - displayProgress; if (diff < 0.5) { @@ -235,11 +272,13 @@ } if (visible) { - // Update progress bar Overlays.editOverlay(bar2D.overlay, { visible: true, subImage: { + + // $$$$$$$ + x: BAR_WIDTH * (1 - displayProgress / 100), y: 0, width: BAR_WIDTH, @@ -251,20 +290,34 @@ visible: true }); + Overlays.editOverlay(textDesktop.overlay, { + visible: !isHMD + }); + + Overlays.editOverlay(textHMD.overlay, { + visible: isHMD + }); + // Update 2D overlays to maintain positions at bottom middle of window viewport = Controller.getViewportDimensions(); - if (viewport.x !== windowWidth || viewport.y !== windowHeight) { + if (viewport.x !== windowWidth || viewport.y !== windowHeight || isHMD !== HMD.active) { updateProgressBarLocation(); } } } function setUp() { + isHMD = HMD.active; + background2D.width = SCALE_2D * BACKGROUND_WIDTH; background2D.height = SCALE_2D * BACKGROUND_HEIGHT; bar2D.width = SCALE_2D * BAR_WIDTH; bar2D.height = SCALE_2D * BAR_HEIGHT; + textDesktop.width = SCALE_TEXT_DESKTOP * TEXT_WIDTH; + textDesktop.height = SCALE_TEXT_DESKTOP * TEXT_HEIGHT; + textHMD.width = SCALE_TEXT_HMD * TEXT_WIDTH; + textHMD.height = SCALE_TEXT_HMD * TEXT_HEIGHT; createOverlays(); } From c9e4fddeabe234973135d97cd8e2c31f96ed260c Mon Sep 17 00:00:00 2001 From: David Rowe Date: Fri, 28 Oct 2016 14:17:18 +1300 Subject: [PATCH 40/49] Remove background image --- .../assets/images/progress-bar-background.svg | 5 --- scripts/system/progress.js | 34 ++----------------- 2 files changed, 3 insertions(+), 36 deletions(-) delete mode 100644 scripts/system/assets/images/progress-bar-background.svg diff --git a/scripts/system/assets/images/progress-bar-background.svg b/scripts/system/assets/images/progress-bar-background.svg deleted file mode 100644 index a8b4e1aab5..0000000000 --- a/scripts/system/assets/images/progress-bar-background.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/scripts/system/progress.js b/scripts/system/progress.js index 082ede95c4..11aabcfc22 100644 --- a/scripts/system/progress.js +++ b/scripts/system/progress.js @@ -38,13 +38,9 @@ TEXT_HEIGHT = 32, TEXT_WIDTH = 256, TEXT_URL = Script.resolvePath("assets/images/progress-bar-text.svg"), - BACKGROUND_WIDTH = 520, - BACKGROUND_HEIGHT = 50, - BACKGROUND_URL = Script.resolvePath("assets/images/progress-bar-background.svg"), windowWidth = 0, windowHeight = 0, bar2D = {}, - background2D = {}, textDesktop = {}, // Separate desktop and HMD overlays because can't change text size after overlay created. textHMD = {}, SCALE_2D = 0.35, // Scale the SVGs for 2D display. @@ -92,10 +88,6 @@ visible = false; } - Overlays.editOverlay(background2D.overlay, { - alpha: alpha, - visible: visible - }); Overlays.editOverlay(bar2D.overlay, { alpha: alpha, visible: visible @@ -152,13 +144,6 @@ } function createOverlays() { - background2D.overlay = Overlays.addOverlay("image", { - imageURL: BACKGROUND_URL, - width: background2D.width, - height: background2D.height, - visible: false, - alpha: 0.0 - }); bar2D.overlay = Overlays.addOverlay("image", { imageURL: BAR_URL, subImage: { @@ -189,8 +174,9 @@ } function deleteOverlays() { - Overlays.deleteOverlay(background2D.overlay); Overlays.deleteOverlay(bar2D.overlay); + Overlays.deleteOverlay(textDesktop.overlay); + Overlays.deleteOverlay(textHMD.overlay); } function updateProgressBarLocation() { @@ -202,14 +188,9 @@ isHMD = HMD.active; yOffset = isHMD ? BAR_Y_OFFSET_HMD : BAR_Y_OFFSET_2D; - Overlays.editOverlay(background2D.overlay, { - x: windowWidth / 2 - background2D.width / 2, - y: windowHeight - background2D.height - bar2D.height + yOffset - }); - Overlays.editOverlay(bar2D.overlay, { x: windowWidth / 2 - bar2D.width / 2, - y: windowHeight - background2D.height - bar2D.height + (background2D.height - bar2D.height) / 2 + yOffset + y: windowHeight - 2 * bar2D.height + yOffset }); Overlays.editOverlay(textDesktop.overlay, { @@ -276,9 +257,6 @@ Overlays.editOverlay(bar2D.overlay, { visible: true, subImage: { - - // $$$$$$$ - x: BAR_WIDTH * (1 - displayProgress / 100), y: 0, width: BAR_WIDTH, @@ -286,10 +264,6 @@ } }); - Overlays.editOverlay(background2D.overlay, { - visible: true - }); - Overlays.editOverlay(textDesktop.overlay, { visible: !isHMD }); @@ -310,8 +284,6 @@ function setUp() { isHMD = HMD.active; - background2D.width = SCALE_2D * BACKGROUND_WIDTH; - background2D.height = SCALE_2D * BACKGROUND_HEIGHT; bar2D.width = SCALE_2D * BAR_WIDTH; bar2D.height = SCALE_2D * BAR_HEIGHT; textDesktop.width = SCALE_TEXT_DESKTOP * TEXT_WIDTH; From 1fc310434e789e6b7ad5b3e3ede7a840a821cbc1 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Thu, 27 Oct 2016 18:55:24 -0700 Subject: [PATCH 41/49] Fix for crash in QtWebEngineCore when rapidly switching domains Call stop on the QWebEngineView before destroying OffscreenQMLSurface. --- interface/resources/qml/controls/WebView.qml | 1 + .../entities-renderer/src/RenderableWebEntityItem.cpp | 10 ++++++++++ 2 files changed, 11 insertions(+) diff --git a/interface/resources/qml/controls/WebView.qml b/interface/resources/qml/controls/WebView.qml index abaf11a8e2..f388e5c120 100644 --- a/interface/resources/qml/controls/WebView.qml +++ b/interface/resources/qml/controls/WebView.qml @@ -27,6 +27,7 @@ Item { WebEngineView { id: root + objectName: "webEngineView" x: 0 y: 0 width: parent.width diff --git a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp index b71fab9439..e3b86b0ea0 100644 --- a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp @@ -326,7 +326,17 @@ void RenderableWebEntityItem::handlePointerEvent(const PointerEvent& event) { void RenderableWebEntityItem::destroyWebSurface() { if (_webSurface) { --_currentWebCount; + + QQuickItem* rootItem = _webSurface->getRootItem(); + if (rootItem) { + QObject* obj = rootItem->findChild("webEngineView"); + if (obj) { + QMetaObject::invokeMethod(obj, "stop"); + } + } + _webSurface->pause(); + _webSurface->disconnect(_connection); QObject::disconnect(_mousePressConnection); _mousePressConnection = QMetaObject::Connection(); From 23fc865d405e1f2e831bb40c7faf3d4df6f7e779 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Thu, 27 Oct 2016 19:28:46 -0700 Subject: [PATCH 42/49] Updates for min spec check --- .../images/steam-min-spec-failed.png | Bin 73490 -> 81938 bytes plugins/openvr/src/OpenVrHelpers.cpp | 38 ++++++++++++++---- 2 files changed, 31 insertions(+), 7 deletions(-) diff --git a/interface/resources/images/steam-min-spec-failed.png b/interface/resources/images/steam-min-spec-failed.png index adb380ee8be5d6a820eb35eb740d64b33c4bac24..2d3c85bda0a462637f4f9be878da93ff63bcd3f7 100644 GIT binary patch literal 81938 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 literal 73490 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 diff --git a/plugins/openvr/src/OpenVrHelpers.cpp b/plugins/openvr/src/OpenVrHelpers.cpp index 35730a0d5e..2803ca424e 100644 --- a/plugins/openvr/src/OpenVrHelpers.cpp +++ b/plugins/openvr/src/OpenVrHelpers.cpp @@ -329,8 +329,8 @@ controller::Pose openVrControllerPoseToHandPose(bool isLeftHand, const mat4& mat #define FAILED_MIN_SPEC_OVERLAY_NAME "FailedMinSpecOverlay" #define FAILED_MIN_SPEC_OVERLAY_FRIENDLY_NAME "Minimum specifications for SteamVR not met" -#define OVERLAY_UPDATE_INTERVAL_MS 10 -#define OVERLAY_DISPLAY_INTERVAL_MS (MSECS_PER_SECOND * 10) +#define FAILED_MIN_SPEC_UPDATE_INTERVAL_MS 10 +#define FAILED_MIN_SPEC_AUTO_QUIT_INTERVAL_MS (MSECS_PER_SECOND * 30) #define MIN_CORES_SPEC 5 void showMinSpecWarning() { @@ -352,19 +352,43 @@ void showMinSpecWarning() { QString imagePath = PathUtils::resourcesPath() + "/images/steam-min-spec-failed.png"; vrOverlay->SetOverlayFromFile(minSpecFailedOverlay, imagePath.toLocal8Bit().toStdString().c_str()); vrOverlay->SetHighQualityOverlay(minSpecFailedOverlay); + vrOverlay->SetOverlayWidthInMeters(minSpecFailedOverlay, 1.4f); + vrOverlay->SetOverlayInputMethod(minSpecFailedOverlay, vr::VROverlayInputMethod_Mouse); vrOverlay->ShowOverlay(minSpecFailedOverlay); - QTimer* positionTimer = new QTimer(&miniApp); - positionTimer->setInterval(OVERLAY_UPDATE_INTERVAL_MS); - QObject::connect(positionTimer, &QTimer::timeout, [&] { + QTimer* timer = new QTimer(&miniApp); + timer->setInterval(FAILED_MIN_SPEC_UPDATE_INTERVAL_MS); + QObject::connect(timer, &QTimer::timeout, [&] { vr::TrackedDevicePose_t vrPoses[vr::k_unMaxTrackedDeviceCount]; vrSystem->GetDeviceToAbsoluteTrackingPose(vr::TrackingUniverseSeated, 0, vrPoses, vr::k_unMaxTrackedDeviceCount); auto headPose = toGlm(vrPoses[vr::k_unTrackedDeviceIndex_Hmd].mDeviceToAbsoluteTracking); auto overlayPose = toOpenVr(headPose * glm::translate(glm::mat4(), vec3(0, 0, -1))); vrOverlay->SetOverlayTransformAbsolute(minSpecFailedOverlay, vr::TrackingUniverseSeated, &overlayPose); + + vr::VREvent_t event; + while (vrSystem->PollNextEvent(&event, sizeof(event))) { + switch (event.eventType) { + case vr::VREvent_Quit: + vrSystem->AcknowledgeQuit_Exiting(); + QCoreApplication::quit(); + break; + + case vr::VREvent_ButtonPress: + // Quit on any button press except for 'putting on the headset' + if (event.data.controller.button != vr::k_EButton_ProximitySensor) { + QCoreApplication::quit(); + } + break; + + default: + break; + } + } + }); - positionTimer->start(); - QTimer::singleShot(OVERLAY_DISPLAY_INTERVAL_MS, &miniApp, &QCoreApplication::quit); + timer->start(); + + QTimer::singleShot(FAILED_MIN_SPEC_AUTO_QUIT_INTERVAL_MS, &miniApp, &QCoreApplication::quit); miniApp.exec(); } From edc9b1fa074a8f3fdc9e6aa18f449c7c77310dbe Mon Sep 17 00:00:00 2001 From: David Rowe Date: Fri, 28 Oct 2016 15:50:50 +1300 Subject: [PATCH 43/49] Update bar graph for desktop and HMD displays --- .../system/assets/images/progress-bar-2k.svg | 26 ++++ .../system/assets/images/progress-bar-hmd.svg | 26 ++++ scripts/system/assets/images/progress-bar.svg | 12 -- scripts/system/progress.js | 123 +++++++++++++----- 4 files changed, 139 insertions(+), 48 deletions(-) create mode 100644 scripts/system/assets/images/progress-bar-2k.svg create mode 100644 scripts/system/assets/images/progress-bar-hmd.svg delete mode 100644 scripts/system/assets/images/progress-bar.svg diff --git a/scripts/system/assets/images/progress-bar-2k.svg b/scripts/system/assets/images/progress-bar-2k.svg new file mode 100644 index 0000000000..45758c7c68 --- /dev/null +++ b/scripts/system/assets/images/progress-bar-2k.svg @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/scripts/system/assets/images/progress-bar-hmd.svg b/scripts/system/assets/images/progress-bar-hmd.svg new file mode 100644 index 0000000000..a6b555691f --- /dev/null +++ b/scripts/system/assets/images/progress-bar-hmd.svg @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/scripts/system/assets/images/progress-bar.svg b/scripts/system/assets/images/progress-bar.svg deleted file mode 100644 index e24a2cbff4..0000000000 --- a/scripts/system/assets/images/progress-bar.svg +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - diff --git a/scripts/system/progress.js b/scripts/system/progress.js index 11aabcfc22..34dbdea7da 100644 --- a/scripts/system/progress.js +++ b/scripts/system/progress.js @@ -30,20 +30,29 @@ fadeWaitTimer = null, FADE_OUT_WAIT = 1000, // Wait before starting to fade out after progress 100%. visible = false, - BAR_WIDTH = 480, // Dimension of SVG in pixels of visible portion (half) of the bar. - BAR_HEIGHT = 10, + + BAR_DESKTOP_WIDTH = 2240, // Width of SVG image in pixels. Sized for 1920 x 1080 display with 6 visible repeats. + BAR_DESKTOP_REPEAT = 320, // Length of repeat in bar = 2240 / 7. + BAR_DESKTOP_HEIGHT = 3, // Display height of SVG + BAR_DESKTOP_URL = Script.resolvePath("assets/images/progress-bar-2k.svg"), + + BAR_HMD_WIDTH = 4430, // Width of SVG image in pixels. Sized for Rift with 6 visible repeats. + BAR_HMD_REPEAT = 585, // Length of repeat in bar = 4430 / 7. + BAR_HMD_HEIGHT = 4, // Display height of SVG + BAR_HMD_URL = Script.resolvePath("assets/images/progress-bar-hmd.svg"), + BAR_Y_OFFSET_2D = -10, // Offset of progress bar while in desktop mode BAR_Y_OFFSET_HMD = -300, // Offset of progress bar while in HMD - BAR_URL = Script.resolvePath("assets/images/progress-bar.svg"), + TEXT_HEIGHT = 32, TEXT_WIDTH = 256, TEXT_URL = Script.resolvePath("assets/images/progress-bar-text.svg"), windowWidth = 0, windowHeight = 0, - bar2D = {}, + barDesktop = {}, + barHMD = {}, textDesktop = {}, // Separate desktop and HMD overlays because can't change text size after overlay created. textHMD = {}, - SCALE_2D = 0.35, // Scale the SVGs for 2D display. SCALE_TEXT_DESKTOP = 0.6, SCALE_TEXT_HMD = 1.0, isHMD = false, @@ -88,9 +97,13 @@ visible = false; } - Overlays.editOverlay(bar2D.overlay, { + Overlays.editOverlay(barDesktop.overlay, { alpha: alpha, - visible: visible + visible: visible && !isHMD + }); + Overlays.editOverlay(barHMD.overlay, { + alpha: alpha, + visible: visible && isHMD }); Overlays.editOverlay(textDesktop.overlay, { alpha: alpha, @@ -144,16 +157,29 @@ } function createOverlays() { - bar2D.overlay = Overlays.addOverlay("image", { - imageURL: BAR_URL, + barDesktop.overlay = Overlays.addOverlay("image", { + imageURL: BAR_DESKTOP_URL, subImage: { x: 0, y: 0, - width: BAR_WIDTH, - height: BAR_HEIGHT + width: BAR_DESKTOP_WIDTH - BAR_DESKTOP_REPEAT, + height: BAR_DESKTOP_HEIGHT }, - width: bar2D.width, - height: bar2D.height, + width: barDesktop.width, + height: barDesktop.height, + visible: false, + alpha: 0.0 + }); + barHMD.overlay = Overlays.addOverlay("image", { + imageURL: BAR_HMD_URL, + subImage: { + x: 0, + y: 0, + width: BAR_HMD_WIDTH - BAR_HMD_REPEAT, + height: BAR_HMD_HEIGHT + }, + width: barHMD.width, + height: barHMD.height, visible: false, alpha: 0.0 }); @@ -174,34 +200,46 @@ } function deleteOverlays() { - Overlays.deleteOverlay(bar2D.overlay); + Overlays.deleteOverlay(barDesktop.overlay); + Overlays.deleteOverlay(barHMD.overlay); Overlays.deleteOverlay(textDesktop.overlay); Overlays.deleteOverlay(textHMD.overlay); } function updateProgressBarLocation() { - var viewport = Controller.getViewportDimensions(), - yOffset; + var viewport = Controller.getViewportDimensions(); windowWidth = viewport.x; windowHeight = viewport.y; isHMD = HMD.active; - yOffset = isHMD ? BAR_Y_OFFSET_HMD : BAR_Y_OFFSET_2D; - Overlays.editOverlay(bar2D.overlay, { - x: windowWidth / 2 - bar2D.width / 2, - y: windowHeight - 2 * bar2D.height + yOffset - }); + if (isHMD) { + barHMD.width = windowWidth; - Overlays.editOverlay(textDesktop.overlay, { - x: windowWidth / 2 - textDesktop.width / 2, - y: windowHeight - 2 * textDesktop.height + yOffset - }); + Overlays.editOverlay(barHMD.overlay, { + x: windowWidth / 2 - barHMD.width / 2, + y: windowHeight - 2 * barHMD.height + BAR_Y_OFFSET_HMD, + width: barHMD.width + }); - Overlays.editOverlay(textHMD.overlay, { - x: windowWidth / 2 - textHMD.width / 2, - y: windowHeight - 2 * textHMD.height + yOffset - }); + Overlays.editOverlay(textHMD.overlay, { + x: windowWidth / 2 - textHMD.width / 2, + y: windowHeight - 2 * textHMD.height + BAR_Y_OFFSET_HMD + }); + } else { + barDesktop.width = windowWidth; + + Overlays.editOverlay(barDesktop.overlay, { + x: windowWidth / 2 - barDesktop.width / 2, + y: windowHeight - 2 * barDesktop.height + BAR_Y_OFFSET_2D, + width: barDesktop.width + }); + + Overlays.editOverlay(textDesktop.overlay, { + x: windowWidth / 2 - textDesktop.width / 2, + y: windowHeight - 2 * textDesktop.height + BAR_Y_OFFSET_2D + }); + } } function update() { @@ -254,13 +292,23 @@ if (visible) { // Update progress bar - Overlays.editOverlay(bar2D.overlay, { - visible: true, + Overlays.editOverlay(barDesktop.overlay, { + visible: !isHMD, subImage: { - x: BAR_WIDTH * (1 - displayProgress / 100), + x: BAR_DESKTOP_REPEAT * (1 - displayProgress / 100), y: 0, - width: BAR_WIDTH, - height: BAR_HEIGHT + width: BAR_DESKTOP_WIDTH - BAR_DESKTOP_REPEAT, + height: BAR_DESKTOP_HEIGHT + } + }); + + Overlays.editOverlay(barHMD.overlay, { + visible: isHMD, + subImage: { + x: BAR_HMD_REPEAT * (1 - displayProgress / 100), + y: 0, + width: BAR_HMD_WIDTH - BAR_HMD_REPEAT, + height: BAR_HMD_HEIGHT } }); @@ -284,8 +332,11 @@ function setUp() { isHMD = HMD.active; - bar2D.width = SCALE_2D * BAR_WIDTH; - bar2D.height = SCALE_2D * BAR_HEIGHT; + barDesktop.width = BAR_DESKTOP_WIDTH; + barDesktop.height = BAR_DESKTOP_HEIGHT; + barHMD.width = BAR_HMD_WIDTH; + barHMD.height = BAR_HMD_HEIGHT; + textDesktop.width = SCALE_TEXT_DESKTOP * TEXT_WIDTH; textDesktop.height = SCALE_TEXT_DESKTOP * TEXT_HEIGHT; textHMD.width = SCALE_TEXT_HMD * TEXT_WIDTH; From d933155c71f354b278c5e1fb8cc272aa200288fb Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 27 Oct 2016 20:16:34 -0700 Subject: [PATCH 44/49] Fix trigger highlight configuration --- scripts/system/controllers/viveControllerConfiguration.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/system/controllers/viveControllerConfiguration.js b/scripts/system/controllers/viveControllerConfiguration.js index 11fc774376..b49c3e1d04 100644 --- a/scripts/system/controllers/viveControllerConfiguration.js +++ b/scripts/system/controllers/viveControllerConfiguration.js @@ -148,10 +148,10 @@ VIVE_CONTROLLER_CONFIGURATION_LEFT = { defaultTextureLayer: "normal", textureLayers: { normal: { - defaultTextureURL: viveTriggerModelURL + "/Trigger.fbm/black.jpg", + defaultTextureURL: BASE_URL + viveTriggerModelURL + "/Trigger.fbm/black.jpg", }, highlight: { - defaultTextureURL: viveTriggerModelURL + "/Trigger.fbm/yellow.jpg", + defaultTextureURL: BASE_URL + viveTriggerModelURL + "/Trigger.fbm/yellow.jpg", } } }, @@ -280,10 +280,10 @@ VIVE_CONTROLLER_CONFIGURATION_RIGHT = { defaultTextureLayer: "normal", textureLayers: { normal: { - defaultTextureURL: viveTriggerModelURL + "/Trigger.fbm/black.jpg", + defaultTextureURL: BASE_URL + viveTriggerModelURL + "/Trigger.fbm/black.jpg", }, highlight: { - defaultTextureURL: viveTriggerModelURL + "/Trigger.fbm/yellow.jpg", + defaultTextureURL: BASE_URL + viveTriggerModelURL + "/Trigger.fbm/yellow.jpg", } } }, From db7f5724f7830ecb027f87b6181fc4ace1bab89b Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 27 Oct 2016 21:50:29 -0700 Subject: [PATCH 45/49] output last seq number and last socket before readyRead sticks --- libraries/networking/src/udt/Socket.cpp | 9 +++++++++ libraries/networking/src/udt/Socket.h | 3 +++ 2 files changed, 12 insertions(+) diff --git a/libraries/networking/src/udt/Socket.cpp b/libraries/networking/src/udt/Socket.cpp index 43bd7dd973..bf2a3fe6d2 100644 --- a/libraries/networking/src/udt/Socket.cpp +++ b/libraries/networking/src/udt/Socket.cpp @@ -306,6 +306,11 @@ void Socket::checkForReadyReadBackup() { if (_udpSocket.hasPendingDatagrams()) { qCDebug(networking) << "Socket::checkForReadyReadBackup() detected blocked readyRead signal. Flushing pending datagrams."; + // so that birarda can possibly figure out how the heck we get into this state in the first place + // output the sequence number and socket address of the last processed packet + qCDebug(networking) << "Socket::checkForReadyReadyBackup() last sequence number" + << (uint32_t) _lastReceivedSequenceNumber << "from" << _lastPacketSockAddr; + // drop all of the pending datagrams on the floor while (_udpSocket.hasPendingDatagrams()) { _udpSocket.readDatagram(nullptr, 0); @@ -373,6 +378,10 @@ void Socket::readPendingDatagrams() { auto packet = Packet::fromReceivedPacket(std::move(buffer), packetSizeWithHeader, senderSockAddr); packet->setReceiveTime(receiveTime); + // save the sequence number and socket for this packet, in case this is the packet that sticks readyRead + _lastPacketSockAddr = senderSockAddr; + _lastReceivedSequenceNumber = packet->getSequenceNumber(); + // call our verification operator to see if this packet is verified if (!_packetFilterOperator || _packetFilterOperator(*packet)) { if (packet->isReliable()) { diff --git a/libraries/networking/src/udt/Socket.h b/libraries/networking/src/udt/Socket.h index a811d78958..24f0b8afaf 100644 --- a/libraries/networking/src/udt/Socket.h +++ b/libraries/networking/src/udt/Socket.h @@ -144,6 +144,9 @@ private: std::unique_ptr _ccFactory { new CongestionControlFactory() }; bool _shouldChangeSocketOptions { true }; + + SequenceNumber _lastReceivedSequenceNumber; + HifiSockAddr _lastPacketSockAddr; friend UDTTest; }; From 73b2bc433dcaaf745d18846f8033628d6dbd85c8 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Fri, 28 Oct 2016 18:03:13 +1300 Subject: [PATCH 46/49] Improve bar positions --- .../system/assets/images/progress-bar-hmd.svg | 26 ------------------- scripts/system/progress.js | 24 ++++++++--------- 2 files changed, 11 insertions(+), 39 deletions(-) delete mode 100644 scripts/system/assets/images/progress-bar-hmd.svg diff --git a/scripts/system/assets/images/progress-bar-hmd.svg b/scripts/system/assets/images/progress-bar-hmd.svg deleted file mode 100644 index a6b555691f..0000000000 --- a/scripts/system/assets/images/progress-bar-hmd.svg +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - diff --git a/scripts/system/progress.js b/scripts/system/progress.js index 34dbdea7da..20023b0352 100644 --- a/scripts/system/progress.js +++ b/scripts/system/progress.js @@ -36,13 +36,13 @@ BAR_DESKTOP_HEIGHT = 3, // Display height of SVG BAR_DESKTOP_URL = Script.resolvePath("assets/images/progress-bar-2k.svg"), - BAR_HMD_WIDTH = 4430, // Width of SVG image in pixels. Sized for Rift with 6 visible repeats. - BAR_HMD_REPEAT = 585, // Length of repeat in bar = 4430 / 7. - BAR_HMD_HEIGHT = 4, // Display height of SVG - BAR_HMD_URL = Script.resolvePath("assets/images/progress-bar-hmd.svg"), + BAR_HMD_WIDTH = 2240, // Desktop image works with HMD well. + BAR_HMD_REPEAT = 320, + BAR_HMD_HEIGHT = 3, + BAR_HMD_URL = Script.resolvePath("assets/images/progress-bar-2k.svg"), - BAR_Y_OFFSET_2D = -10, // Offset of progress bar while in desktop mode - BAR_Y_OFFSET_HMD = -300, // Offset of progress bar while in HMD + BAR_Y_OFFSET_DESKTOP = 0, // Offset of progress bar while in desktop mode + BAR_Y_OFFSET_HMD = -100, // Offset of progress bar while in HMD TEXT_HEIGHT = 32, TEXT_WIDTH = 256, @@ -214,30 +214,28 @@ isHMD = HMD.active; if (isHMD) { - barHMD.width = windowWidth; Overlays.editOverlay(barHMD.overlay, { x: windowWidth / 2 - barHMD.width / 2, - y: windowHeight - 2 * barHMD.height + BAR_Y_OFFSET_HMD, - width: barHMD.width + y: windowHeight - 2 * barHMD.height + BAR_Y_OFFSET_HMD }); Overlays.editOverlay(textHMD.overlay, { x: windowWidth / 2 - textHMD.width / 2, - y: windowHeight - 2 * textHMD.height + BAR_Y_OFFSET_HMD + y: windowHeight - 2 * barHMD.height - textHMD.height + BAR_Y_OFFSET_HMD }); + } else { - barDesktop.width = windowWidth; Overlays.editOverlay(barDesktop.overlay, { x: windowWidth / 2 - barDesktop.width / 2, - y: windowHeight - 2 * barDesktop.height + BAR_Y_OFFSET_2D, + y: windowHeight - 2 * barDesktop.height + BAR_Y_OFFSET_DESKTOP, width: barDesktop.width }); Overlays.editOverlay(textDesktop.overlay, { x: windowWidth / 2 - textDesktop.width / 2, - y: windowHeight - 2 * textDesktop.height + BAR_Y_OFFSET_2D + y: windowHeight - 2 * barDesktop.height - textDesktop.height + BAR_Y_OFFSET_DESKTOP }); } } From 20f042a15a134455e36dbb837a8a465a432c1161 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 27 Oct 2016 22:10:32 -0700 Subject: [PATCH 47/49] add slightly more information to last packet debug in Socket --- libraries/networking/src/udt/Socket.cpp | 11 ++++++++--- libraries/networking/src/udt/Socket.h | 1 + 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/libraries/networking/src/udt/Socket.cpp b/libraries/networking/src/udt/Socket.cpp index bf2a3fe6d2..fbf2a1c86a 100644 --- a/libraries/networking/src/udt/Socket.cpp +++ b/libraries/networking/src/udt/Socket.cpp @@ -309,7 +309,9 @@ void Socket::checkForReadyReadBackup() { // so that birarda can possibly figure out how the heck we get into this state in the first place // output the sequence number and socket address of the last processed packet qCDebug(networking) << "Socket::checkForReadyReadyBackup() last sequence number" - << (uint32_t) _lastReceivedSequenceNumber << "from" << _lastPacketSockAddr; + << (uint32_t) _lastReceivedSequenceNumber << "from" << _lastPacketSockAddr << "-" + << _lastPacketSizeRead << "bytes"; + // drop all of the pending datagrams on the floor while (_udpSocket.hasPendingDatagrams()) { @@ -339,6 +341,10 @@ void Socket::readPendingDatagrams() { auto sizeRead = _udpSocket.readDatagram(buffer.get(), packetSizeWithHeader, senderSockAddr.getAddressPointer(), senderSockAddr.getPortPointer()); + // save information for this packet, in case it is the one that sticks readyRead + _lastPacketSizeRead = sizeRead; + _lastPacketSockAddr = senderSockAddr; + if (sizeRead <= 0) { // we either didn't pull anything for this packet or there was an error reading (this seems to trigger // on windows even if there's not a packet available) @@ -378,8 +384,7 @@ void Socket::readPendingDatagrams() { auto packet = Packet::fromReceivedPacket(std::move(buffer), packetSizeWithHeader, senderSockAddr); packet->setReceiveTime(receiveTime); - // save the sequence number and socket for this packet, in case this is the packet that sticks readyRead - _lastPacketSockAddr = senderSockAddr; + // save the sequence number in case this is the packet that sticks readyRead _lastReceivedSequenceNumber = packet->getSequenceNumber(); // call our verification operator to see if this packet is verified diff --git a/libraries/networking/src/udt/Socket.h b/libraries/networking/src/udt/Socket.h index 24f0b8afaf..1919e00b41 100644 --- a/libraries/networking/src/udt/Socket.h +++ b/libraries/networking/src/udt/Socket.h @@ -145,6 +145,7 @@ private: bool _shouldChangeSocketOptions { true }; + int _lastPacketSizeRead { 0 }; SequenceNumber _lastReceivedSequenceNumber; HifiSockAddr _lastPacketSockAddr; From 610159fcbe44c0079d262ab2999a6de758fb27e8 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Fri, 28 Oct 2016 18:20:33 +1300 Subject: [PATCH 48/49] Improve bar for 4K displays --- .../system/assets/images/progress-bar-4k.svg | 26 +++++++++++++++ scripts/system/progress.js | 33 ++++++++++++------- 2 files changed, 47 insertions(+), 12 deletions(-) create mode 100644 scripts/system/assets/images/progress-bar-4k.svg diff --git a/scripts/system/assets/images/progress-bar-4k.svg b/scripts/system/assets/images/progress-bar-4k.svg new file mode 100644 index 0000000000..609ab9610b --- /dev/null +++ b/scripts/system/assets/images/progress-bar-4k.svg @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/scripts/system/progress.js b/scripts/system/progress.js index 20023b0352..89f64639e7 100644 --- a/scripts/system/progress.js +++ b/scripts/system/progress.js @@ -31,10 +31,15 @@ FADE_OUT_WAIT = 1000, // Wait before starting to fade out after progress 100%. visible = false, - BAR_DESKTOP_WIDTH = 2240, // Width of SVG image in pixels. Sized for 1920 x 1080 display with 6 visible repeats. - BAR_DESKTOP_REPEAT = 320, // Length of repeat in bar = 2240 / 7. - BAR_DESKTOP_HEIGHT = 3, // Display height of SVG - BAR_DESKTOP_URL = Script.resolvePath("assets/images/progress-bar-2k.svg"), + BAR_DESKTOP_2K_WIDTH = 2240, // Width of SVG image in pixels. Sized for 1920 x 1080 display with 6 visible repeats. + BAR_DESKTOP_2K_REPEAT = 320, // Length of repeat in bar = 2240 / 7. + BAR_DESKTOP_2K_HEIGHT = 3, // Display height of SVG + BAR_DESKTOP_2K_URL = Script.resolvePath("assets/images/progress-bar-2k.svg"), + + BAR_DESKTOP_4K_WIDTH = 4480, // Width of SVG image in pixels. Sized for 1920 x 1080 display with 6 visible repeats. + BAR_DESKTOP_4K_REPEAT = 640, // Length of repeat in bar = 2240 / 7. + BAR_DESKTOP_4K_HEIGHT = 6, // Display height of SVG + BAR_DESKTOP_4K_URL = Script.resolvePath("assets/images/progress-bar-4k.svg"), BAR_HMD_WIDTH = 2240, // Desktop image works with HMD well. BAR_HMD_REPEAT = 320, @@ -158,12 +163,12 @@ function createOverlays() { barDesktop.overlay = Overlays.addOverlay("image", { - imageURL: BAR_DESKTOP_URL, + imageURL: barDesktop.url, subImage: { x: 0, y: 0, - width: BAR_DESKTOP_WIDTH - BAR_DESKTOP_REPEAT, - height: BAR_DESKTOP_HEIGHT + width: barDesktop.width - barDesktop.repeat, + height: barDesktop.height }, width: barDesktop.width, height: barDesktop.height, @@ -293,10 +298,10 @@ Overlays.editOverlay(barDesktop.overlay, { visible: !isHMD, subImage: { - x: BAR_DESKTOP_REPEAT * (1 - displayProgress / 100), + x: barDesktop.repeat * (1 - displayProgress / 100), y: 0, - width: BAR_DESKTOP_WIDTH - BAR_DESKTOP_REPEAT, - height: BAR_DESKTOP_HEIGHT + width: barDesktop.width - barDesktop.repeat, + height: barDesktop.height } }); @@ -328,10 +333,14 @@ } function setUp() { + var is4k = Window.innerWidth > 3000; + isHMD = HMD.active; - barDesktop.width = BAR_DESKTOP_WIDTH; - barDesktop.height = BAR_DESKTOP_HEIGHT; + barDesktop.width = is4k ? BAR_DESKTOP_4K_WIDTH : BAR_DESKTOP_2K_WIDTH; + barDesktop.height = is4k ? BAR_DESKTOP_4K_HEIGHT : BAR_DESKTOP_2K_HEIGHT; + barDesktop.repeat = is4k ? BAR_DESKTOP_4K_REPEAT : BAR_DESKTOP_2K_REPEAT; + barDesktop.url = is4k ? BAR_DESKTOP_4K_URL : BAR_DESKTOP_2K_URL; barHMD.width = BAR_HMD_WIDTH; barHMD.height = BAR_HMD_HEIGHT; From b548e6441c5a15961bdb14628e35de049e51986c Mon Sep 17 00:00:00 2001 From: David Rowe Date: Fri, 28 Oct 2016 18:57:54 +1300 Subject: [PATCH 49/49] Animate progress bar without regard for amount of content left to load --- scripts/system/progress.js | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/scripts/system/progress.js b/scripts/system/progress.js index 89f64639e7..c0b7143a11 100644 --- a/scripts/system/progress.js +++ b/scripts/system/progress.js @@ -49,6 +49,8 @@ BAR_Y_OFFSET_DESKTOP = 0, // Offset of progress bar while in desktop mode BAR_Y_OFFSET_HMD = -100, // Offset of progress bar while in HMD + ANIMATION_SECONDS_PER_REPEAT = 4, // Speed of bar animation + TEXT_HEIGHT = 32, TEXT_WIDTH = 256, TEXT_URL = Script.resolvePath("assets/images/progress-bar-text.svg"), @@ -246,7 +248,7 @@ } function update() { - var viewport, diff; + var viewport, diff, x; initialDelayCooldown -= 30; @@ -294,11 +296,18 @@ } if (visible) { + x = ((Date.now() / 1000) % ANIMATION_SECONDS_PER_REPEAT) / ANIMATION_SECONDS_PER_REPEAT; + if (isHMD) { + x = x * barDesktop.repeat; + } else { + x = x * BAR_HMD_REPEAT; + } + // Update progress bar Overlays.editOverlay(barDesktop.overlay, { visible: !isHMD, subImage: { - x: barDesktop.repeat * (1 - displayProgress / 100), + x: barDesktop.repeat - x, y: 0, width: barDesktop.width - barDesktop.repeat, height: barDesktop.height @@ -308,7 +317,7 @@ Overlays.editOverlay(barHMD.overlay, { visible: isHMD, subImage: { - x: BAR_HMD_REPEAT * (1 - displayProgress / 100), + x: BAR_HMD_REPEAT - x, y: 0, width: BAR_HMD_WIDTH - BAR_HMD_REPEAT, height: BAR_HMD_HEIGHT @@ -337,11 +346,11 @@ isHMD = HMD.active; - barDesktop.width = is4k ? BAR_DESKTOP_4K_WIDTH : BAR_DESKTOP_2K_WIDTH; + barDesktop.width = is4k ? BAR_DESKTOP_4K_WIDTH - BAR_DESKTOP_4K_REPEAT : BAR_DESKTOP_2K_WIDTH - BAR_DESKTOP_2K_REPEAT; barDesktop.height = is4k ? BAR_DESKTOP_4K_HEIGHT : BAR_DESKTOP_2K_HEIGHT; barDesktop.repeat = is4k ? BAR_DESKTOP_4K_REPEAT : BAR_DESKTOP_2K_REPEAT; barDesktop.url = is4k ? BAR_DESKTOP_4K_URL : BAR_DESKTOP_2K_URL; - barHMD.width = BAR_HMD_WIDTH; + barHMD.width = BAR_HMD_WIDTH - BAR_HMD_REPEAT; barHMD.height = BAR_HMD_HEIGHT; textDesktop.width = SCALE_TEXT_DESKTOP * TEXT_WIDTH;