diff --git a/assignment-client/src/Agent.h b/assignment-client/src/Agent.h index edba366601..549a0858b7 100644 --- a/assignment-client/src/Agent.h +++ b/assignment-client/src/Agent.h @@ -23,7 +23,6 @@ #include #include -#include #include #include @@ -31,6 +30,7 @@ #include "AudioGate.h" #include "MixedAudioStream.h" +#include "entities/EntityTreeHeadlessViewer.h" #include "avatars/ScriptableAvatar.h" class Agent : public ThreadedAssignment { diff --git a/assignment-client/src/AssignmentClientApp.cpp b/assignment-client/src/AssignmentClientApp.cpp index 90d759dffa..7e9042e609 100644 --- a/assignment-client/src/AssignmentClientApp.cpp +++ b/assignment-client/src/AssignmentClientApp.cpp @@ -186,6 +186,10 @@ AssignmentClientApp::AssignmentClientApp(int argc, char* argv[]) : listenPort = argumentVariantMap.value(ASSIGNMENT_CLIENT_LISTEN_PORT_OPTION).toUInt(); } + if (parser.isSet(portOption)) { + listenPort = parser.value(portOption).toUInt(); + } + if (parser.isSet(numChildsOption)) { if (minForks && minForks > numForks) { qCritical() << "--min can't be more than -n"; diff --git a/assignment-client/src/avatars/AvatarMixer.cpp b/assignment-client/src/avatars/AvatarMixer.cpp index 870149f1bc..c8b68a740c 100644 --- a/assignment-client/src/avatars/AvatarMixer.cpp +++ b/assignment-client/src/avatars/AvatarMixer.cpp @@ -132,7 +132,7 @@ void AvatarMixer::start() { auto start = usecTimestampNow(); nodeList->nestedEach([&](NodeList::const_iterator cbegin, NodeList::const_iterator cend) { std::for_each(cbegin, cend, [&](const SharedNodePointer& node) { - manageDisplayName(node); + manageIdentityData(node); ++_sumListeners; }); }, &lockWait, &nodeTransform, &functor); @@ -183,8 +183,9 @@ void AvatarMixer::start() { // NOTE: nodeData->getAvatar() might be side effected, must be called when access to node/nodeData // is guaranteed to not be accessed by other thread -void AvatarMixer::manageDisplayName(const SharedNodePointer& node) { +void AvatarMixer::manageIdentityData(const SharedNodePointer& node) { AvatarMixerClientData* nodeData = reinterpret_cast(node->getLinkedData()); + bool sendIdentity = false; if (nodeData && nodeData->getAvatarSessionDisplayNameMustChange()) { AvatarData& avatar = nodeData->getAvatar(); const QString& existingBaseDisplayName = nodeData->getBaseDisplayName(); @@ -210,9 +211,39 @@ void AvatarMixer::manageDisplayName(const SharedNodePointer& node) { soFar.second++; // refcount nodeData->flagIdentityChange(); nodeData->setAvatarSessionDisplayNameMustChange(false); - sendIdentityPacket(nodeData, node); // Tell node whose name changed about its new session display name. + sendIdentity = true; qCDebug(avatars) << "Giving session display name" << sessionDisplayName << "to node with ID" << node->getUUID(); } + if (nodeData && nodeData->getAvatarSkeletonModelUrlMustChange()) { // never true for an empty _avatarWhitelist + nodeData->setAvatarSkeletonModelUrlMustChange(false); + AvatarData& avatar = nodeData->getAvatar(); + static const QUrl emptyURL(""); + QUrl url = avatar.cannonicalSkeletonModelURL(emptyURL); + if (!isAvatarInWhitelist(url)) { + qCDebug(avatars) << "Forbidden avatar" << nodeData->getNodeID() << avatar.getSkeletonModelURL() << "replaced with" << (_replacementAvatar.isEmpty() ? "default" : _replacementAvatar); + avatar.setSkeletonModelURL(_replacementAvatar); + sendIdentity = true; + } + } + if (sendIdentity) { + sendIdentityPacket(nodeData, node); // Tell node whose name changed about its new session display name or avatar. + } +} + +bool AvatarMixer::isAvatarInWhitelist(const QUrl& url) { + // The avatar is in the whitelist if: + // 1. The avatar's URL's host matches one of the hosts of the URLs in the whitelist AND + // 2. The avatar's URL's path starts with the path of that same URL in the whitelist + for (const auto& whiteListedPrefix : _avatarWhitelist) { + auto whiteListURL = QUrl::fromUserInput(whiteListedPrefix); + // check if this script URL matches the whitelist domain and, optionally, is beneath the path + if (url.host().compare(whiteListURL.host(), Qt::CaseInsensitive) == 0 && + url.path().startsWith(whiteListURL.path(), Qt::CaseInsensitive)) { + return true; + } + } + + return false; } void AvatarMixer::throttle(std::chrono::microseconds duration, int frame) { @@ -402,13 +433,17 @@ void AvatarMixer::handleAvatarIdentityPacket(QSharedPointer mes AvatarData::parseAvatarIdentityPacket(message->getMessage(), identity); bool identityChanged = false; bool displayNameChanged = false; - avatar.processAvatarIdentity(identity, identityChanged, displayNameChanged); + bool skeletonModelUrlChanged = false; + avatar.processAvatarIdentity(identity, identityChanged, displayNameChanged, skeletonModelUrlChanged); if (identityChanged) { QMutexLocker nodeDataLocker(&nodeData->getMutex()); nodeData->flagIdentityChange(); if (displayNameChanged) { nodeData->setAvatarSessionDisplayNameMustChange(true); } + if (skeletonModelUrlChanged && !_avatarWhitelist.isEmpty()) { + nodeData->setAvatarSkeletonModelUrlMustChange(true); + } } } } @@ -764,4 +799,19 @@ void AvatarMixer::parseDomainServerSettings(const QJsonObject& domainSettings) { qCDebug(avatars) << "This domain requires a minimum avatar scale of" << _domainMinimumScale << "and a maximum avatar scale of" << _domainMaximumScale; + const QString AVATAR_WHITELIST_DEFAULT{ "" }; + static const QString AVATAR_WHITELIST_OPTION = "avatar_whitelist"; + _avatarWhitelist = domainSettings[AVATARS_SETTINGS_KEY].toObject()[AVATAR_WHITELIST_OPTION].toString(AVATAR_WHITELIST_DEFAULT).split(',', QString::KeepEmptyParts); + + static const QString REPLACEMENT_AVATAR_OPTION = "replacement_avatar"; + _replacementAvatar = domainSettings[AVATARS_SETTINGS_KEY].toObject()[REPLACEMENT_AVATAR_OPTION].toString(REPLACEMENT_AVATAR_DEFAULT); + + if ((_avatarWhitelist.count() == 1) && _avatarWhitelist[0].isEmpty()) { + _avatarWhitelist.clear(); // KeepEmptyParts above will parse "," as ["", ""] (which is ok), but "" as [""] (which is not ok). + } + if (_avatarWhitelist.isEmpty()) { + qCDebug(avatars) << "All avatars are allowed."; + } else { + qCDebug(avatars) << "Avatars other than" << _avatarWhitelist << "will be replaced by" << (_replacementAvatar.isEmpty() ? "default" : _replacementAvatar); + } } diff --git a/assignment-client/src/avatars/AvatarMixer.h b/assignment-client/src/avatars/AvatarMixer.h index 1925ec1ebd..f8ebe419a9 100644 --- a/assignment-client/src/avatars/AvatarMixer.h +++ b/assignment-client/src/avatars/AvatarMixer.h @@ -59,7 +59,12 @@ private: void parseDomainServerSettings(const QJsonObject& domainSettings); void sendIdentityPacket(AvatarMixerClientData* nodeData, const SharedNodePointer& destinationNode); - void manageDisplayName(const SharedNodePointer& node); + void manageIdentityData(const SharedNodePointer& node); + bool isAvatarInWhitelist(const QUrl& url); + + const QString REPLACEMENT_AVATAR_DEFAULT{ "" }; + QStringList _avatarWhitelist { }; + QString _replacementAvatar { REPLACEMENT_AVATAR_DEFAULT }; p_high_resolution_clock::time_point _lastFrameTimestamp; diff --git a/assignment-client/src/avatars/AvatarMixerClientData.h b/assignment-client/src/avatars/AvatarMixerClientData.h index c905b10251..12b0286088 100644 --- a/assignment-client/src/avatars/AvatarMixerClientData.h +++ b/assignment-client/src/avatars/AvatarMixerClientData.h @@ -65,6 +65,8 @@ public: void flagIdentityChange() { _identityChangeTimestamp = usecTimestampNow(); } bool getAvatarSessionDisplayNameMustChange() const { return _avatarSessionDisplayNameMustChange; } void setAvatarSessionDisplayNameMustChange(bool set = true) { _avatarSessionDisplayNameMustChange = set; } + bool getAvatarSkeletonModelUrlMustChange() const { return _avatarSkeletonModelUrlMustChange; } + void setAvatarSkeletonModelUrlMustChange(bool set = true) { _avatarSkeletonModelUrlMustChange = set; } void resetNumAvatarsSentLastFrame() { _numAvatarsSentLastFrame = 0; } void incrementNumAvatarsSentLastFrame() { ++_numAvatarsSentLastFrame; } @@ -146,6 +148,7 @@ private: uint64_t _identityChangeTimestamp; bool _avatarSessionDisplayNameMustChange{ true }; + bool _avatarSkeletonModelUrlMustChange{ false }; int _numAvatarsSentLastFrame = 0; int _numFramesSinceAdjustment = 0; diff --git a/libraries/entities/src/EntityTreeHeadlessViewer.cpp b/assignment-client/src/entities/EntityTreeHeadlessViewer.cpp similarity index 100% rename from libraries/entities/src/EntityTreeHeadlessViewer.cpp rename to assignment-client/src/entities/EntityTreeHeadlessViewer.cpp diff --git a/libraries/entities/src/EntityTreeHeadlessViewer.h b/assignment-client/src/entities/EntityTreeHeadlessViewer.h similarity index 97% rename from libraries/entities/src/EntityTreeHeadlessViewer.h rename to assignment-client/src/entities/EntityTreeHeadlessViewer.h index fe05323a0b..17bb37186a 100644 --- a/libraries/entities/src/EntityTreeHeadlessViewer.h +++ b/assignment-client/src/entities/EntityTreeHeadlessViewer.h @@ -16,9 +16,9 @@ #include #include #include -#include #include +#include "../octree/OctreeHeadlessViewer.h" #include "EntityTree.h" class EntitySimulation; diff --git a/libraries/octree/src/OctreeHeadlessViewer.cpp b/assignment-client/src/octree/OctreeHeadlessViewer.cpp similarity index 98% rename from libraries/octree/src/OctreeHeadlessViewer.cpp rename to assignment-client/src/octree/OctreeHeadlessViewer.cpp index 28d3794a54..4e885da7a5 100644 --- a/libraries/octree/src/OctreeHeadlessViewer.cpp +++ b/assignment-client/src/octree/OctreeHeadlessViewer.cpp @@ -9,17 +9,14 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#include - -#include "OctreeLogging.h" #include "OctreeHeadlessViewer.h" -OctreeHeadlessViewer::OctreeHeadlessViewer() : OctreeRenderer() { - _viewFrustum.setProjection(glm::perspective(glm::radians(DEFAULT_FIELD_OF_VIEW_DEGREES), DEFAULT_ASPECT_RATIO, DEFAULT_NEAR_CLIP, DEFAULT_FAR_CLIP)); -} +#include +#include -void OctreeHeadlessViewer::init() { - OctreeRenderer::init(); + +OctreeHeadlessViewer::OctreeHeadlessViewer() { + _viewFrustum.setProjection(glm::perspective(glm::radians(DEFAULT_FIELD_OF_VIEW_DEGREES), DEFAULT_ASPECT_RATIO, DEFAULT_NEAR_CLIP, DEFAULT_FAR_CLIP)); } void OctreeHeadlessViewer::queryOctree() { diff --git a/libraries/octree/src/OctreeHeadlessViewer.h b/assignment-client/src/octree/OctreeHeadlessViewer.h similarity index 82% rename from libraries/octree/src/OctreeHeadlessViewer.h rename to assignment-client/src/octree/OctreeHeadlessViewer.h index a502844fa5..5a7544498d 100644 --- a/libraries/octree/src/OctreeHeadlessViewer.h +++ b/assignment-client/src/octree/OctreeHeadlessViewer.h @@ -12,28 +12,17 @@ #ifndef hifi_OctreeHeadlessViewer_h #define hifi_OctreeHeadlessViewer_h -#include -#include -#include +#include +#include +#include -#include "JurisdictionListener.h" -#include "Octree.h" -#include "OctreeConstants.h" -#include "OctreeQuery.h" -#include "OctreeRenderer.h" -#include "OctreeSceneStats.h" -#include "Octree.h" // Generic client side Octree renderer class. -class OctreeHeadlessViewer : public OctreeRenderer { +class OctreeHeadlessViewer : public OctreeProcessor { Q_OBJECT public: OctreeHeadlessViewer(); virtual ~OctreeHeadlessViewer() {}; - virtual void renderElement(OctreeElementPointer element, RenderArgs* args) override { /* swallow these */ } - - virtual void init() override ; - virtual void render(RenderArgs* renderArgs) override { /* swallow these */ } void setJurisdictionListener(JurisdictionListener* jurisdictionListener) { _jurisdictionListener = jurisdictionListener; } @@ -71,6 +60,7 @@ private: JurisdictionListener* _jurisdictionListener = nullptr; OctreeQuery _octreeQuery; + ViewFrustum _viewFrustum; float _voxelSizeScale { DEFAULT_OCTREE_SIZE_SCALE }; int _boundaryLevelAdjust { 0 }; int _maxPacketsPerSecond { DEFAULT_MAX_OCTREE_PPS }; diff --git a/assignment-client/src/octree/OctreeInboundPacketProcessor.cpp b/assignment-client/src/octree/OctreeInboundPacketProcessor.cpp index d2fef4dfbd..04409b3b21 100644 --- a/assignment-client/src/octree/OctreeInboundPacketProcessor.cpp +++ b/assignment-client/src/octree/OctreeInboundPacketProcessor.cpp @@ -47,7 +47,7 @@ void OctreeInboundPacketProcessor::resetStats() { _singleSenderStats.clear(); } -unsigned long OctreeInboundPacketProcessor::getMaxWait() const { +uint32_t OctreeInboundPacketProcessor::getMaxWait() const { // calculate time until next sendNackPackets() quint64 nextNackTime = _lastNackTime + TOO_LONG_SINCE_LAST_NACK; quint64 now = usecTimestampNow(); diff --git a/assignment-client/src/octree/OctreeInboundPacketProcessor.h b/assignment-client/src/octree/OctreeInboundPacketProcessor.h index 4611fcada0..a7fa297d24 100644 --- a/assignment-client/src/octree/OctreeInboundPacketProcessor.h +++ b/assignment-client/src/octree/OctreeInboundPacketProcessor.h @@ -80,7 +80,7 @@ protected: virtual void processPacket(QSharedPointer message, SharedNodePointer sendingNode) override; - virtual unsigned long getMaxWait() const override; + virtual uint32_t getMaxWait() const override; virtual void preProcess() override; virtual void midProcess() override; diff --git a/assignment-client/src/scripts/EntityScriptServer.h b/assignment-client/src/scripts/EntityScriptServer.h index 696b082467..84454375e5 100644 --- a/assignment-client/src/scripts/EntityScriptServer.h +++ b/assignment-client/src/scripts/EntityScriptServer.h @@ -19,10 +19,10 @@ #include #include -#include #include #include #include +#include "../entities/EntityTreeHeadlessViewer.h" class EntityScriptServer : public ThreadedAssignment { Q_OBJECT diff --git a/cmake/externals/quazip/CMakeLists.txt b/cmake/externals/quazip/CMakeLists.txt index 3a86852d76..7af13dafa7 100644 --- a/cmake/externals/quazip/CMakeLists.txt +++ b/cmake/externals/quazip/CMakeLists.txt @@ -12,12 +12,19 @@ elseif ($ENV{QT_CMAKE_PREFIX_PATH}) set(QT_CMAKE_PREFIX_PATH $ENV{QT_CMAKE_PREFIX_PATH}) endif () +set(QUAZIP_CMAKE_ARGS -DCMAKE_INSTALL_PREFIX:PATH= -DCMAKE_PREFIX_PATH=${QT_CMAKE_PREFIX_PATH} -DCMAKE_INSTALL_NAME_DIR:PATH=/lib -DZLIB_ROOT=${ZLIB_ROOT} -DCMAKE_POSITION_INDEPENDENT_CODE=ON) + +if (APPLE) +else () + set(QUAZIP_CMAKE_ARGS ${QUAZIP_CMAKE_ARGS} -DCMAKE_CXX_STANDARD=11) +endif () + ExternalProject_Add( ${EXTERNAL_NAME} URL https://s3-us-west-1.amazonaws.com/hifi-production/dependencies/quazip-0.7.2.zip URL_MD5 2955176048a31262c09259ca8d309d19 BINARY_DIR ${EXTERNAL_PROJECT_PREFIX}/build - CMAKE_ARGS -DCMAKE_INSTALL_PREFIX:PATH= -DCMAKE_PREFIX_PATH=${QT_CMAKE_PREFIX_PATH} -DCMAKE_INSTALL_NAME_DIR:PATH=/lib -DZLIB_ROOT=${ZLIB_ROOT} -DCMAKE_POSITION_INDEPENDENT_CODE=ON + CMAKE_ARGS ${QUAZIP_CMAKE_ARGS} LOG_DOWNLOAD 1 LOG_CONFIGURE 1 LOG_BUILD 1 diff --git a/cmake/macros/LinkHifiLibraries.cmake b/cmake/macros/LinkHifiLibraries.cmake index de4ff23863..395af01f8d 100644 --- a/cmake/macros/LinkHifiLibraries.cmake +++ b/cmake/macros/LinkHifiLibraries.cmake @@ -7,16 +7,18 @@ # See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html # -macro(LINK_HIFI_LIBRARIES) +function(LINK_HIFI_LIBRARIES) file(RELATIVE_PATH RELATIVE_LIBRARY_DIR_PATH ${CMAKE_CURRENT_SOURCE_DIR} "${HIFI_LIBRARY_DIR}") set(LIBRARIES_TO_LINK ${ARGN}) - - foreach(HIFI_LIBRARY ${LIBRARIES_TO_LINK}) + foreach(HIFI_LIBRARY ${LIBRARIES_TO_LINK}) if (NOT TARGET ${HIFI_LIBRARY}) add_subdirectory("${RELATIVE_LIBRARY_DIR_PATH}/${HIFI_LIBRARY}" "${RELATIVE_LIBRARY_DIR_PATH}/${HIFI_LIBRARY}") endif () + endforeach() + + foreach(HIFI_LIBRARY ${LIBRARIES_TO_LINK}) include_directories("${HIFI_LIBRARY_DIR}/${HIFI_LIBRARY}/src") include_directories("${CMAKE_BINARY_DIR}/libraries/${HIFI_LIBRARY}/shaders") @@ -29,4 +31,4 @@ macro(LINK_HIFI_LIBRARIES) setup_memory_debugger() -endmacro(LINK_HIFI_LIBRARIES) +endfunction() diff --git a/domain-server/resources/describe-settings.json b/domain-server/resources/describe-settings.json index a8c6dd84e7..817333bb72 100644 --- a/domain-server/resources/describe-settings.json +++ b/domain-server/resources/describe-settings.json @@ -147,6 +147,7 @@ { "name": "security", "label": "Security", + "restart": false, "settings": [ { "name": "http_username", @@ -866,6 +867,22 @@ "help": "Limits the scale of avatars in your domain. Cannot be greater than 1000.", "placeholder": 3.0, "default": 3.0 + }, + { + "name": "avatar_whitelist", + "label": "Avatars Allowed from:", + "help": "Comma separated list of URLs (with optional paths) that avatar .fst files are allowed from. If someone attempts to use an avatar with a different domain, it will be rejected and the replacement avatar will be used. If left blank, any domain is allowed.", + "placeholder": "", + "default": "", + "advanced": true + }, + { + "name": "replacement_avatar", + "label": "Replacement Avatar for disallowed avatars", + "help": "A URL for an avatar .fst to be used when someone tries to use an avatar that is not allowed. If left blank, the generic default avatar is used.", + "placeholder": "", + "default": "", + "advanced": true } ] }, diff --git a/domain-server/resources/web/settings/index.shtml b/domain-server/resources/web/settings/index.shtml index 1812c52dad..9d38539b42 100644 --- a/domain-server/resources/web/settings/index.shtml +++ b/domain-server/resources/web/settings/index.shtml @@ -26,7 +26,7 @@ - + @@ -77,7 +77,7 @@ diff --git a/domain-server/resources/web/settings/js/settings.js b/domain-server/resources/web/settings/js/settings.js index 8066223318..dd577701a5 100644 --- a/domain-server/resources/web/settings/js/settings.js +++ b/domain-server/resources/web/settings/js/settings.js @@ -223,6 +223,14 @@ $(document).ready(function(){ // set focus to the first input in the new row $target.closest('table').find('tr.inputs input:first').focus(); } + + var tableRows = sibling.parent(); + var tableBody = tableRows.parent(); + + // if theres no more siblings, we should jump to a new row + if (sibling.next().length == 0 && tableRows.nextAll().length == 1) { + tableBody.find("." + Settings.ADD_ROW_BUTTON_CLASS).click(); + } } } else if ($target.is('input')) { @@ -1320,6 +1328,18 @@ function makeTableCategoryInput(setting, numVisibleColumns) { return html; } +function getDescriptionForKey(key) { + for (var i in Settings.data.descriptions) { + if (Settings.data.descriptions[i].name === key) { + return Settings.data.descriptions[i]; + } + } +} + +var SAVE_BUTTON_LABEL_SAVE = "Save"; +var SAVE_BUTTON_LABEL_RESTART = "Save and restart"; +var reasonsForRestart = []; + function badgeSidebarForDifferences(changedElement) { // figure out which group this input is in var panelParentID = changedElement.closest('.panel').attr('id'); @@ -1342,13 +1362,24 @@ function badgeSidebarForDifferences(changedElement) { } var badgeValue = 0 + var description = getDescriptionForKey(panelParentID); // badge for any settings we have that are not the same or are not present in initialValues for (var setting in panelJSON) { if ((!_.has(initialPanelJSON, setting) && panelJSON[setting] !== "") || (!_.isEqual(panelJSON[setting], initialPanelJSON[setting]) && (panelJSON[setting] !== "" || _.has(initialPanelJSON, setting)))) { - badgeValue += 1 + badgeValue += 1; + + // add a reason to restart + if (description && description.restart != false) { + reasonsForRestart.push(setting); + } + } else { + // remove a reason to restart + if (description && description.restart != false) { + reasonsForRestart = $.grep(reasonsForRestart, function(v) { return v != setting; }); + } } } @@ -1357,6 +1388,7 @@ function badgeSidebarForDifferences(changedElement) { badgeValue = "" } + $(".save-button").html(reasonsForRestart.length > 0 ? SAVE_BUTTON_LABEL_RESTART : SAVE_BUTTON_LABEL_SAVE); $("a[href='#" + panelParentID + "'] .badge").html(badgeValue); } diff --git a/interface/resources/qml/controls-uit/CheckBox.qml b/interface/resources/qml/controls-uit/CheckBox.qml index 916a7d4889..d6dc5d2736 100644 --- a/interface/resources/qml/controls-uit/CheckBox.qml +++ b/interface/resources/qml/controls-uit/CheckBox.qml @@ -18,6 +18,7 @@ Original.CheckBox { id: checkBox property int colorScheme: hifi.colorSchemes.light + property string color: hifi.colors.lightGray readonly property bool isLightColorScheme: colorScheme == hifi.colorSchemes.light property bool isRedCheck: false property int boxSize: 14 @@ -89,7 +90,7 @@ Original.CheckBox { label: Label { text: control.text - colorScheme: checkBox.colorScheme + color: control.color x: 2 wrapMode: Text.Wrap enabled: checkBox.enabled diff --git a/interface/resources/qml/desktop/Desktop.qml b/interface/resources/qml/desktop/Desktop.qml index 42db16aa72..3dcf747113 100644 --- a/interface/resources/qml/desktop/Desktop.qml +++ b/interface/resources/qml/desktop/Desktop.qml @@ -353,6 +353,14 @@ FocusScope { showDesktop(); } + function ensureTitleBarVisible(targetWindow) { + // Reposition window to ensure that title bar is vertically inside window. + if (targetWindow.frame && targetWindow.frame.decoration) { + var topMargin = -targetWindow.frame.decoration.anchors.topMargin; // Frame's topMargin is a negative value. + targetWindow.y = Math.max(targetWindow.y, topMargin); + } + } + function centerOnVisible(item) { var targetWindow = d.getDesktopWindow(item); if (!targetWindow) { @@ -375,11 +383,12 @@ FocusScope { targetWindow.x = newX; targetWindow.y = newY; + ensureTitleBarVisible(targetWindow); + // If we've noticed that our recommended desktop rect has changed, record that change here. if (recommendedRect != newRecommendedRect) { recommendedRect = newRecommendedRect; } - } function repositionOnVisible(item) { @@ -394,7 +403,6 @@ FocusScope { return; } - var oldRecommendedRect = recommendedRect; var oldRecommendedDimmensions = { x: oldRecommendedRect.width, y: oldRecommendedRect.height }; var newRecommendedRect = Controller.getRecommendedOverlayRect(); @@ -426,7 +434,6 @@ FocusScope { newPosition.y = -1 } - if (newPosition.x === -1 && newPosition.y === -1) { var originRelativeX = (targetWindow.x - oldRecommendedRect.x); var originRelativeY = (targetWindow.y - oldRecommendedRect.y); @@ -444,6 +451,8 @@ FocusScope { } targetWindow.x = newPosition.x; targetWindow.y = newPosition.y; + + ensureTitleBarVisible(targetWindow); } Component { id: messageDialogBuilder; MessageDialog { } } diff --git a/interface/resources/qml/hifi/Audio.qml b/interface/resources/qml/hifi/Audio.qml deleted file mode 100644 index 48de891733..0000000000 --- a/interface/resources/qml/hifi/Audio.qml +++ /dev/null @@ -1,266 +0,0 @@ -// -// Audio.qml -// qml/hifi -// -// Audio setup -// -// Created by Vlad Stelmahovsky on 03/22/2017 -// Copyright 2017 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -import QtQuick 2.5 -import QtQuick.Controls 1.4 -import QtGraphicalEffects 1.0 - -import "../styles-uit" -import "../controls-uit" as HifiControls - -import "components" - -Rectangle { - id: audio; - - //put info text here - property alias infoText: infoArea.text - - color: "#404040"; - - HifiConstants { id: hifi; } - objectName: "AudioWindow" - - property string title: "Audio Options" - signal sendToScript(var message); - - - Component { - id: separator - LinearGradient { - start: Qt.point(0, 0) - end: Qt.point(0, 4) - gradient: Gradient { - GradientStop { position: 0.0; color: "#303030" } - GradientStop { position: 0.33; color: "#252525" } // Equivalent of darkGray0 over baseGray background. - GradientStop { position: 0.5; color: "#303030" } - GradientStop { position: 0.6; color: "#454a49" } - GradientStop { position: 1.0; color: "#454a49" } - } - cached: true - } - } - - Column { - anchors { left: parent.left; right: parent.right } - spacing: 8 - - RalewayRegular { - anchors { left: parent.left; right: parent.right; leftMargin: 30 } - height: 45 - size: 20 - color: "white" - text: audio.title - } - - Loader { - width: parent.width - height: 5 - sourceComponent: separator - } - - //connections required to syncronize with Menu - Connections { - target: AudioDevice - onMuteToggled: { - audioMute.checkbox.checked = AudioDevice.getMuted() - } - } - - Connections { - target: AvatarInputs !== undefined ? AvatarInputs : null - onShowAudioToolsChanged: { - audioTools.checkbox.checked = showAudioTools - } - } - - AudioCheckbox { - id: audioMute - width: parent.width - anchors { left: parent.left; right: parent.right; leftMargin: 30 } - checkbox.checked: AudioDevice.muted - text.text: qsTr("Mute microphone") - onCheckBoxClicked: { - AudioDevice.muted = checked - } - } - - AudioCheckbox { - id: audioTools - width: parent.width - anchors { left: parent.left; right: parent.right; leftMargin: 30 } - checkbox.checked: AvatarInputs !== undefined ? AvatarInputs.showAudioTools : false - text.text: qsTr("Show audio level meter") - onCheckBoxClicked: { - if (AvatarInputs !== undefined) { - AvatarInputs.showAudioTools = checked - } - } - } - - Loader { - width: parent.width - height: 5 - sourceComponent: separator - } - - Row { - anchors { left: parent.left; right: parent.right; leftMargin: 30 } - height: 40 - spacing: 8 - - HiFiGlyphs { - text: hifi.glyphs.mic - color: hifi.colors.primaryHighlight - anchors.verticalCenter: parent.verticalCenter - size: 32 - } - RalewayRegular { - anchors.verticalCenter: parent.verticalCenter - size: 16 - color: "#AFAFAF" - text: qsTr("CHOOSE INPUT DEVICE") - } - } - - ListView { - id: inputAudioListView - anchors { left: parent.left; right: parent.right; leftMargin: 70 } - height: 125 - spacing: 0 - clip: true - snapMode: ListView.SnapToItem - model: AudioDevice - delegate: Item { - width: parent.width - visible: devicemode === 0 - height: visible ? 36 : 0 - - AudioCheckbox { - id: cbin - anchors.verticalCenter: parent.verticalCenter - Binding { - target: cbin.checkbox - property: 'checked' - value: devicechecked - } - - width: parent.width - cbchecked: devicechecked - text.text: devicename - onCheckBoxClicked: { - if (checked) { - if (devicename.length > 0) { - console.log("Audio.qml about to call AudioDevice.setInputDeviceAsync().devicename:" + devicename); - AudioDevice.setInputDeviceAsync(devicename); - } else { - console.log("Audio.qml attempted to set input device to empty device name."); - } - } - } - } - } - } - - Loader { - width: parent.width - height: 5 - sourceComponent: separator - } - - Row { - anchors { left: parent.left; right: parent.right; leftMargin: 30 } - height: 40 - spacing: 8 - - HiFiGlyphs { - text: hifi.glyphs.unmuted - color: hifi.colors.primaryHighlight - anchors.verticalCenter: parent.verticalCenter - size: 32 - } - RalewayRegular { - anchors.verticalCenter: parent.verticalCenter - size: 16 - color: "#AFAFAF" - text: qsTr("CHOOSE OUTPUT DEVICE") - } - } - - ListView { - id: outputAudioListView - anchors { left: parent.left; right: parent.right; leftMargin: 70 } - height: 250 - spacing: 0 - clip: true - snapMode: ListView.SnapToItem - model: AudioDevice - delegate: Item { - width: parent.width - visible: devicemode === 1 - height: visible ? 36 : 0 - AudioCheckbox { - id: cbout - width: parent.width - anchors.verticalCenter: parent.verticalCenter - Binding { - target: cbout.checkbox - property: 'checked' - value: devicechecked - } - text.text: devicename - onCheckBoxClicked: { - if (checked) { - if (devicename.length > 0) { - console.log("Audio.qml about to call AudioDevice.setOutputDeviceAsync().devicename:" + devicename); - AudioDevice.setOutputDeviceAsync(devicename); - } else { - console.log("Audio.qml attempted to set output device to empty device name."); - } - - } - } - } - } - } - - Loader { - id: lastSeparator - width: parent.width - height: 6 - sourceComponent: separator - } - - Row { - anchors { left: parent.left; right: parent.right; leftMargin: 30 } - height: 40 - spacing: 8 - - HiFiGlyphs { - id: infoSign - text: hifi.glyphs.info - color: "#AFAFAF" - anchors.verticalCenter: parent.verticalCenter - size: 60 - } - RalewayRegular { - id: infoArea - width: parent.width - infoSign.implicitWidth - parent.spacing - 10 - wrapMode: Text.WordWrap - anchors.verticalCenter: parent.verticalCenter - size: 12 - color: hifi.colors.baseGrayHighlight - } - } - } -} diff --git a/interface/resources/qml/hifi/NameCard.qml b/interface/resources/qml/hifi/NameCard.qml index a86defdfd7..91c1d99cf5 100644 --- a/interface/resources/qml/hifi/NameCard.qml +++ b/interface/resources/qml/hifi/NameCard.qml @@ -590,14 +590,11 @@ Item { console.log("This avatar is no longer present. goToUserInDomain() failed."); return; } - var vector = Vec3.subtract(avatar.position, MyAvatar.position); - var distance = Vec3.length(vector); - var target = Vec3.multiply(Vec3.normalize(vector), distance - 2.0); // FIXME: We would like the avatar to recompute the avatar's "maybe fly" test at the new position, so that if high enough up, // the avatar goes into fly mode rather than falling. However, that is not exposed to Javascript right now. // FIXME: it would be nice if this used the same teleport steps and smoothing as in the teleport.js script. // Note, however, that this script allows teleporting to a person in the air, while teleport.js is going to a grounded target. - MyAvatar.orientation = Quat.lookAtSimple(MyAvatar.position, avatar.position); - MyAvatar.position = Vec3.sum(MyAvatar.position, target); + MyAvatar.position = Vec3.sum(avatar.position, Vec3.multiplyQbyV(avatar.orientation, {x: 0, y: 0, z: -2})); + MyAvatar.orientation = Quat.multiply(avatar.orientation, {y: 1}); } } diff --git a/interface/resources/qml/hifi/Pal.qml b/interface/resources/qml/hifi/Pal.qml index 250015bab9..bbb42e61ac 100644 --- a/interface/resources/qml/hifi/Pal.qml +++ b/interface/resources/qml/hifi/Pal.qml @@ -128,7 +128,7 @@ Rectangle { pal.sendToScript({method: 'refreshNearby', params: params}); } - Item { + Rectangle { id: palTabContainer; // Anchors anchors { @@ -137,6 +137,7 @@ Rectangle { left: parent.left; right: parent.right; } + color: "white"; Rectangle { id: tabSelectorContainer; // Anchors diff --git a/interface/resources/qml/hifi/audio/Audio.qml b/interface/resources/qml/hifi/audio/Audio.qml new file mode 100644 index 0000000000..a30aba2a6b --- /dev/null +++ b/interface/resources/qml/hifi/audio/Audio.qml @@ -0,0 +1,162 @@ +// +// Audio.qml +// qml/hifi/audio +// +// Audio setup +// +// Created by Vlad Stelmahovsky on 03/22/2017 +// Copyright 2017 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +import QtQuick 2.5 +import QtQuick.Controls 1.4 +import QtQuick.Layouts 1.3 + +import "../../styles-uit" +import "../../controls-uit" as HifiControls +import "../../windows" +import "./" as Audio + +Rectangle { + id: root; + + HifiConstants { id: hifi; } + + property var eventBridge; + property string title: "Audio Settings - " + Audio.context; + signal sendToScript(var message); + + color: hifi.colors.baseGray; + + // only show the title if loaded through a "loader" + function showTitle() { + return root.parent.objectName == "loader"; + } + + Column { + y: 16; // padding does not work + spacing: 16; + width: parent.width; + + RalewayRegular { + x: 16; // padding does not work + size: 16; + color: "white"; + text: root.title; + + visible: root.showTitle(); + } + + Separator { visible: root.showTitle() } + + Grid { + columns: 2; + x: 16; // padding does not work + spacing: 16; + + Audio.CheckBox { + text: qsTr("Mute microphone"); + checked: Audio.muted; + onClicked: { + Audio.muted = checked; + checked = Qt.binding(function() { return Audio.muted; }); // restore binding + } + } + Audio.CheckBox { + text: qsTr("Enable noise reduction"); + checked: Audio.noiseReduction; + onClicked: { + Audio.noiseReduction = checked; + checked = Qt.binding(function() { return Audio.noiseReduction; }); // restore binding + } + } + Audio.CheckBox { + text: qsTr("Show audio level meter"); + checked: AvatarInputs.showAudioTools; + onClicked: { + AvatarInputs.showAudioTools = checked; + checked = Qt.binding(function() { return AvatarInputs.showAudioTools; }); // restore binding + } + } + } + + Separator {} + + RowLayout { + HiFiGlyphs { + text: hifi.glyphs.mic; + color: hifi.colors.primaryHighlight; + anchors.verticalCenter: parent.verticalCenter; + size: 28; + } + RalewayRegular { + anchors.verticalCenter: parent.verticalCenter; + size: 16; + color: hifi.colors.lightGrayText; + text: qsTr("CHOOSE INPUT DEVICE"); + } + } + + ListView { + anchors { left: parent.left; right: parent.right; leftMargin: 70 } + height: 125; + spacing: 0; + snapMode: ListView.SnapToItem; + clip: true; + model: Audio.devices.input; + delegate: Item { + width: parent.width; + height: 36; + Audio.CheckBox { + text: display; + checked: selected; + onClicked: { + selected = checked; + checked = Qt.binding(function() { return selected; }); // restore binding + } + } + } + } + + Separator {} + + RowLayout { + HiFiGlyphs { + text: hifi.glyphs.unmuted; + color: hifi.colors.primaryHighlight; + anchors.verticalCenter: parent.verticalCenter; + size: 36; + } + RalewayRegular { + anchors.verticalCenter: parent.verticalCenter; + size: 16; + color: hifi.colors.lightGrayText; + text: qsTr("CHOOSE OUTPUT DEVICE"); + } + } + + ListView { + anchors { left: parent.left; right: parent.right; leftMargin: 70 } + height: 125; + spacing: 0; + snapMode: ListView.SnapToItem; + clip: true; + model: Audio.devices.output; + delegate: Item { + width: parent.width; + height: 36; + Audio.CheckBox { + text: display; + checked: selected; + onClicked: { + selected = checked; + checked = Qt.binding(function() { return selected; }); // restore binding + } + } + } + } + } +} diff --git a/interface/resources/qml/hifi/audio/CheckBox.qml b/interface/resources/qml/hifi/audio/CheckBox.qml new file mode 100644 index 0000000000..1f632ac479 --- /dev/null +++ b/interface/resources/qml/hifi/audio/CheckBox.qml @@ -0,0 +1,18 @@ +// +// CheckBox.qml +// qml/hifi/audio +// +// Created by Zach Pomerantz on 6/12/2017 +// Copyright 2017 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +import QtQuick 2.5 + +import "../../controls-uit" as HifiControls + +HifiControls.CheckBox { + color: "white" +} diff --git a/interface/resources/qml/hifi/components/AudioCheckbox.qml b/interface/resources/qml/hifi/components/AudioCheckbox.qml deleted file mode 100644 index b037fe4c7d..0000000000 --- a/interface/resources/qml/hifi/components/AudioCheckbox.qml +++ /dev/null @@ -1,30 +0,0 @@ -import QtQuick 2.5 -import QtQuick.Controls 1.4 - -import "../../styles-uit" -import "../../controls-uit" as HifiControls - -Row { - id: row - spacing: 16 - property alias checkbox: cb - property alias cbchecked: cb.checked - property alias text: txt - signal checkBoxClicked(bool checked) - - HifiControls.CheckBox { - id: cb - boxSize: 20 - colorScheme: hifi.colorSchemes.dark - anchors.verticalCenter: parent.verticalCenter - onClicked: checkBoxClicked(cb.checked) - } - RalewayBold { - id: txt - wrapMode: Text.WordWrap - width: parent.width - cb.boxSize - 30 - anchors.verticalCenter: parent.verticalCenter - size: 16 - color: "white" - } -} diff --git a/interface/resources/qml/hifi/dialogs/Audio.qml b/interface/resources/qml/hifi/dialogs/Audio.qml new file mode 100644 index 0000000000..4ce9e14c42 --- /dev/null +++ b/interface/resources/qml/hifi/dialogs/Audio.qml @@ -0,0 +1,27 @@ +// +// Audio.qml +// +// Created by Zach Pomerantz on 6/12/2017 +// Copyright 2017 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +import "../../windows" +import "../audio" + +ScrollingWindow { + id: root; + + resizable: true; + destroyOnHidden: true; + width: 400; + height: 577; + minSize: Qt.vector2d(400, 500); + + Audio { id: audio; width: root.width } + + objectName: "AudioDialog"; + title: audio.title; +} diff --git a/interface/resources/qml/hifi/dialogs/AudioPreferencesDialog.qml b/interface/resources/qml/hifi/dialogs/AudioBuffers.qml similarity index 82% rename from interface/resources/qml/hifi/dialogs/AudioPreferencesDialog.qml rename to interface/resources/qml/hifi/dialogs/AudioBuffers.qml index 0c7c881bbf..42e0781adc 100644 --- a/interface/resources/qml/hifi/dialogs/AudioPreferencesDialog.qml +++ b/interface/resources/qml/hifi/dialogs/AudioBuffers.qml @@ -5,9 +5,9 @@ import "../../dialogs" PreferencesDialog { id: root - objectName: "AudioPreferencesDialog" + objectName: "AudioBuffersDialog" title: "Audio Settings" - showCategories: ["Audio"] + showCategories: ["Audio Buffers"] property var settings: Settings { category: root.objectName property alias x: root.x @@ -16,4 +16,3 @@ PreferencesDialog { property alias height: root.height } } - diff --git a/interface/resources/qml/hifi/tablet/Tablet.qml b/interface/resources/qml/hifi/tablet/Tablet.qml index 18f88b7718..25cbd02426 100644 --- a/interface/resources/qml/hifi/tablet/Tablet.qml +++ b/interface/resources/qml/hifi/tablet/Tablet.qml @@ -6,16 +6,10 @@ Item { id: tablet objectName: "tablet" property double micLevel: 0.8 - property bool micEnabled: true property int rowIndex: 0 property int columnIndex: 0 property int count: (flowMain.children.length - 1) - // called by C++ code to keep mic state updated - function setMicEnabled(newMicEnabled) { - tablet.micEnabled = newMicEnabled; - } - // called by C++ code to keep audio bar updated function setMicLevel(newMicLevel) { tablet.micLevel = newMicLevel; @@ -121,8 +115,8 @@ Item { } Item { - visible: (!tablet.micEnabled && !toggleMuteMouseArea.containsMouse) - || (tablet.micEnabled && toggleMuteMouseArea.containsMouse) + visible: (Audio.muted && !toggleMuteMouseArea.containsMouse) + || (!Audio.muted && toggleMuteMouseArea.containsMouse) Image { id: muteIcon @@ -201,7 +195,7 @@ Item { preventStealing: true propagateComposedEvents: false scrollGestureEnabled: false - onClicked: tabletRoot.toggleMicEnabled() + onClicked: { Audio.muted = !Audio.muted } } RalewaySemiBold { @@ -271,7 +265,7 @@ Item { PropertyChanges { target: muteIcon - visible: micEnabled + visible: !Audio.muted } PropertyChanges { diff --git a/interface/resources/qml/hifi/tablet/TabletAudioPreferences.qml b/interface/resources/qml/hifi/tablet/TabletAudioBuffers.qml similarity index 67% rename from interface/resources/qml/hifi/tablet/TabletAudioPreferences.qml rename to interface/resources/qml/hifi/tablet/TabletAudioBuffers.qml index b30e741be9..1b4d0feaca 100644 --- a/interface/resources/qml/hifi/tablet/TabletAudioPreferences.qml +++ b/interface/resources/qml/hifi/tablet/TabletAudioBuffers.qml @@ -1,7 +1,7 @@ // -// TabletAudioPreferences.qml +// TabletAudioBuffers.qml // -// Created by Davd Rowe on 7 Mar 2017. +// Created by Zach Pomerantz on 6/5/2017. // Copyright 2017 High Fidelity, Inc. // // Distributed under the Apache License, Version 2.0. @@ -17,7 +17,9 @@ StackView { id: profileRoot initialItem: root objectName: "stack" - property string title: "Audio Settings" + property string title: "Audio Buffers" + property alias gotoPreviousApp: root.gotoPreviousApp; + property var eventBridge; signal sendToScript(var message); @@ -31,7 +33,7 @@ StackView { TabletPreferencesDialog { id: root - objectName: "TabletAudioPreferences" - showCategories: ["Audio"] + objectName: "TabletAudioBuffersDialog" + showCategories: ["Audio Buffers"] } } diff --git a/interface/resources/qml/hifi/tablet/TabletRoot.qml b/interface/resources/qml/hifi/tablet/TabletRoot.qml index b6cdce0853..faa4013bce 100644 --- a/interface/resources/qml/hifi/tablet/TabletRoot.qml +++ b/interface/resources/qml/hifi/tablet/TabletRoot.qml @@ -147,10 +147,6 @@ Item { } } - function toggleMicEnabled() { - ApplicationInterface.toggleMuteAudio(); - } - function setUsername(newUsername) { username = newUsername; } diff --git a/interface/resources/qml/hifi/tablet/WindowRoot.qml b/interface/resources/qml/hifi/tablet/WindowRoot.qml index 12f302d60a..ea0c2844a1 100644 --- a/interface/resources/qml/hifi/tablet/WindowRoot.qml +++ b/interface/resources/qml/hifi/tablet/WindowRoot.qml @@ -71,10 +71,6 @@ Windows.ScrollingWindow { } } - function toggleMicEnabled() { - ApplicationInterface.toggleMuteAudio(); - } - function setUsername(newUsername) { username = newUsername; } diff --git a/interface/resources/qml/styles-uit/Separator.qml b/interface/resources/qml/styles-uit/Separator.qml new file mode 100644 index 0000000000..4134b928a7 --- /dev/null +++ b/interface/resources/qml/styles-uit/Separator.qml @@ -0,0 +1,41 @@ +// +// Separator.qml +// +// Created by Zach Fox on 2017-06-06 +// Copyright 2017 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +import QtQuick 2.5 +import "../styles-uit" + +Item { + // Size + height: 2; + width: parent.width; + + Rectangle { + // Size + width: parent.width; + height: 1; + // Anchors + anchors.left: parent.left; + anchors.bottom: parent.bottom; + anchors.bottomMargin: height; + // Style + color: hifi.colors.baseGrayShadow; + } + + Rectangle { + // Size + width: parent.width; + height: 1; + // Anchors + anchors.left: parent.left; + anchors.bottom: parent.bottom; + // Style + color: hifi.colors.baseGrayHighlight; + } +} diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 1e1095c85a..c2d4360639 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -150,11 +150,11 @@ #include "InterfaceLogging.h" #include "LODManager.h" #include "ModelPackager.h" +#include "scripting/Audio.h" #include "networking/CloseEventSender.h" #include "scripting/TestScriptingInterface.h" #include "scripting/AccountScriptingInterface.h" #include "scripting/AssetMappingsScriptingInterface.h" -#include "scripting/AudioDeviceScriptingInterface.h" #include "scripting/ClipboardScriptingInterface.h" #include "scripting/DesktopScriptingInterface.h" #include "scripting/GlobalServicesScriptingInterface.h" @@ -251,6 +251,8 @@ static const QString MARKETPLACE_CDN_HOSTNAME = "mpassets.highfidelity.com"; static const int INTERVAL_TO_CHECK_HMD_WORN_STATUS = 500; // milliseconds static const QString DESKTOP_DISPLAY_PLUGIN_NAME = "Desktop"; +static const QString SYSTEM_TABLET = "com.highfidelity.interface.tablet.system"; + const QHash Application::_acceptedExtensions { { SVO_EXTENSION, &Application::importSVOFromURL }, { SVO_JSON_EXTENSION, &Application::importSVOFromURL }, @@ -604,7 +606,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo _undoStackScriptingInterface(&_undoStack), _entitySimulation(new PhysicalEntitySimulation()), _physicsEngine(new PhysicsEngine(Vectors::ZERO)), - _entityClipboardRenderer(false, this, this), _entityClipboard(new EntityTree()), _lastQueriedTime(usecTimestampNow()), _previousScriptLocation("LastScriptLocation", DESKTOP_LOCATION), @@ -737,9 +738,9 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo recorder->recordFrame(AUDIO_FRAME_TYPE, audio); } }); + audioIO->startThread(); - auto audioScriptingInterface = DependencyManager::set(); - connect(audioIO.data(), &AudioClient::muteToggled, this, &Application::audioMuteToggled); + auto audioScriptingInterface = DependencyManager::set(); connect(audioIO.data(), &AudioClient::mutedByMixer, audioScriptingInterface.data(), &AudioScriptingInterface::mutedByMixer); connect(audioIO.data(), &AudioClient::receivedFirstPacket, audioScriptingInterface.data(), &AudioScriptingInterface::receivedFirstPacket); connect(audioIO.data(), &AudioClient::disconnected, audioScriptingInterface.data(), &AudioScriptingInterface::disconnected); @@ -755,8 +756,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo audioScriptingInterface->environmentMuted(); } }); - - audioIO->startThread(); + connect(this, &Application::activeDisplayPluginChanged, + reinterpret_cast(audioScriptingInterface.data()), &scripting::Audio::onContextChanged); ResourceManager::init(); // Make sure we don't time out during slow operations at startup @@ -979,7 +980,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo // Make sure we don't time out during slow operations at startup updateHeartbeat(); - connect(this, SIGNAL(aboutToQuit()), this, SLOT(aboutToQuit())); + connect(this, SIGNAL(aboutToQuit()), this, SLOT(onAboutToQuit())); // hook up bandwidth estimator QSharedPointer bandwidthRecorder = DependencyManager::get(); @@ -1473,7 +1474,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo }); sendStatsTimer->start(); - // Periodically check for count of nearby avatars static int lastCountOfNearbyAvatars = -1; QTimer* checkNearbyAvatarsTimer = new QTimer(this); @@ -1631,12 +1631,12 @@ QString Application::getUserAgent() { return userAgent; } -void Application::toggleTabletUI() const { +void Application::toggleTabletUI(bool shouldOpen) const { auto tabletScriptingInterface = DependencyManager::get(); auto hmd = DependencyManager::get(); - TabletProxy* tablet = dynamic_cast(tabletScriptingInterface->getTablet("com.highfidelity.interface.tablet.system")); + TabletProxy* tablet = dynamic_cast(tabletScriptingInterface->getTablet(SYSTEM_TABLET)); bool messageOpen = tablet->isMessageDialogOpen(); - if (!messageOpen || (messageOpen && !hmd->getShouldShowTablet())) { + if ((!messageOpen || (messageOpen && !hmd->getShouldShowTablet())) && !(shouldOpen && hmd->getShouldShowTablet())) { auto HMD = DependencyManager::get(); HMD->toggleShouldShowTablet(); } @@ -1668,7 +1668,7 @@ void Application::updateHeartbeat() const { static_cast(_deadlockWatchdogThread)->updateHeartbeat(); } -void Application::aboutToQuit() { +void Application::onAboutToQuit() { emit beforeAboutToQuit(); foreach(auto inputPlugin, PluginManager::getInstance()->getInputPlugins()) { @@ -1747,6 +1747,10 @@ void Application::cleanupBeforeQuit() { // Cleanup all overlays after the scripts, as scripts might add more _overlays.cleanupAllOverlays(); + // The cleanup process enqueues the transactions but does not process them. Calling this here will force the actual + // removal of the items. + // See https://highfidelity.fogbugz.com/f/cases/5328 + _main3DScene->processTransactionQueue(); // first stop all timers directly or by invokeMethod // depending on what thread they run in @@ -2009,7 +2013,6 @@ void Application::initializeUi() { surfaceContext->setContextProperty("Stats", Stats::getInstance()); surfaceContext->setContextProperty("Settings", SettingsScriptingInterface::getInstance()); surfaceContext->setContextProperty("ScriptDiscoveryService", DependencyManager::get().data()); - surfaceContext->setContextProperty("AudioDevice", AudioDeviceScriptingInterface::getInstance()); surfaceContext->setContextProperty("AvatarBookmarks", DependencyManager::get().data()); surfaceContext->setContextProperty("LocationBookmarks", DependencyManager::get().data()); @@ -2338,12 +2341,6 @@ void Application::runTests() { runUnitTests(); } -void Application::audioMuteToggled() const { - QAction* muteAction = Menu::getInstance()->getActionForOption(MenuOption::MuteAudio); - Q_CHECK_PTR(muteAction); - muteAction->setChecked(DependencyManager::get()->isMuted()); -} - void Application::faceTrackerMuteToggled() { QAction* muteAction = Menu::getInstance()->getActionForOption(MenuOption::MuteFaceTracking); @@ -2413,7 +2410,7 @@ void Application::showHelp() { queryString.addQueryItem("handControllerName", handControllerName); queryString.addQueryItem("defaultTab", defaultTab); auto tabletScriptingInterface = DependencyManager::get(); - TabletProxy* tablet = dynamic_cast(tabletScriptingInterface->getTablet("com.highfidelity.interface.tablet.system")); + TabletProxy* tablet = dynamic_cast(tabletScriptingInterface->getTablet(SYSTEM_TABLET)); tablet->gotoWebScreen(INFO_HELP_PATH + "?" + queryString.toString()); //InfoView::show(INFO_HELP_PATH, false, queryString.toString()); } @@ -2869,7 +2866,7 @@ void Application::keyPressEvent(QKeyEvent* event) { if (isShifted && isMeta) { auto offscreenUi = DependencyManager::get(); offscreenUi->togglePinned(); - //offscreenUi->getRootContext()->engine()->clearComponentCache(); + //offscreenUi->getSurfaceContext()->engine()->clearComponentCache(); //OffscreenUi::information("Debugging", "Component cache cleared"); // placeholder for dialogs being converted to QML. } @@ -2910,6 +2907,12 @@ void Application::keyPressEvent(QKeyEvent* event) { Menu::getInstance()->triggerOption(MenuOption::DefaultSkybox); break; + case Qt::Key_M: + if (isMeta) { + DependencyManager::get()->toggleMute(); + } + break; + case Qt::Key_N: if (!isOption && !isShifted && isMeta) { DependencyManager::get()->toggleIgnoreRadius(); @@ -4009,11 +4012,6 @@ void Application::init() { DependencyManager::get()->sendDomainServerCheckIn(); getEntities()->init(); - { - QMutexLocker viewLocker(&_viewMutex); - getEntities()->setViewFrustum(_viewFrustum); - } - getEntities()->setEntityLoadingPriorityFunction([this](const EntityItem& item) { auto dims = item.getDimensions(); auto maxSize = glm::compMax(dims); @@ -4043,13 +4041,6 @@ void Application::init() { // of events related clicking, hovering over, and entering entities getEntities()->connectSignalsToSlots(entityScriptingInterface.data()); - _entityClipboardRenderer.init(); - { - QMutexLocker viewLocker(&_viewMutex); - _entityClipboardRenderer.setViewFrustum(_viewFrustum); - } - _entityClipboardRenderer.setTree(_entityClipboard); - // Make sure any new sounds are loaded as soon as know about them. connect(tree.get(), &EntityTree::newCollisionSoundURL, this, [this](QUrl newURL, EntityItemID id) { EntityTreePointer tree = getEntities()->getTree(); @@ -4494,10 +4485,11 @@ void Application::update(float deltaTime) { } else { const quint64 MUTE_MICROPHONE_AFTER_USECS = 5000000; //5 secs Menu* menu = Menu::getInstance(); - if (menu->isOptionChecked(MenuOption::AutoMuteAudio) && !menu->isOptionChecked(MenuOption::MuteAudio)) { + auto audioClient = DependencyManager::get(); + if (menu->isOptionChecked(MenuOption::AutoMuteAudio) && !audioClient->isMuted()) { if (_lastFaceTrackerUpdate > 0 && ((usecTimestampNow() - _lastFaceTrackerUpdate) > MUTE_MICROPHONE_AFTER_USECS)) { - menu->triggerOption(MenuOption::MuteAudio); + audioClient->toggleMute(); _lastFaceTrackerUpdate = 0; } } else { @@ -5041,9 +5033,6 @@ QRect Application::getDesirableApplicationGeometry() const { return applicationGeometry; } -// FIXME, preprocessor guard this check to occur only in DEBUG builds -static QThread * activeRenderingThread = nullptr; - PickRay Application::computePickRay(float x, float y) const { vec2 pickPoint { x, y }; PickRay result; @@ -5114,7 +5103,6 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se auto myAvatar = getMyAvatar(); myAvatar->preDisplaySide(renderArgs); - activeRenderingThread = QThread::currentThread(); PROFILE_RANGE(render, __FUNCTION__); PerformanceTimer perfTimer("display"); PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "Application::displaySide()"); @@ -5187,8 +5175,6 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se // Before the deferred pass, let's try to use the render engine _renderEngine->run(); } - - activeRenderingThread = nullptr; } void Application::resetSensors(bool andReload) { @@ -5318,6 +5304,11 @@ void Application::nodeActivated(SharedNodePointer node) { if (node->getType() == NodeType::AvatarMixer) { // new avatar mixer, send off our identity packet on next update loop + // Reset skeletonModelUrl if the last server modified our choice. + static const QUrl empty{}; + if (getMyAvatar()->getFullAvatarURLFromPreferences() != getMyAvatar()->cannonicalSkeletonModelURL(empty)) { + getMyAvatar()->resetFullAvatarURL(); + } getMyAvatar()->markIdentityDataChanged(); getMyAvatar()->resetLastSent(); } @@ -5538,7 +5529,6 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri scriptEngine->registerGlobalObject("Stats", Stats::getInstance()); scriptEngine->registerGlobalObject("Settings", SettingsScriptingInterface::getInstance()); scriptEngine->registerGlobalObject("Snapshot", DependencyManager::get().data()); - scriptEngine->registerGlobalObject("AudioDevice", AudioDeviceScriptingInterface::getInstance()); scriptEngine->registerGlobalObject("AudioStats", DependencyManager::get()->getStats().data()); scriptEngine->registerGlobalObject("AudioScope", DependencyManager::get().data()); scriptEngine->registerGlobalObject("AvatarBookmarks", DependencyManager::get().data()); @@ -5817,38 +5807,24 @@ bool Application::displayAvatarAttachmentConfirmationDialog(const QString& name) } } -void Application::toggleRunningScriptsWidget() const { - auto scriptEngines = DependencyManager::get(); - bool scriptsRunning = !scriptEngines->getRunningScripts().isEmpty(); - auto tabletScriptingInterface = DependencyManager::get(); - auto tablet = dynamic_cast(tabletScriptingInterface->getTablet("com.highfidelity.interface.tablet.system")); +void Application::showDialog(const QUrl& widgetUrl, const QUrl& tabletUrl, const QString& name) const { + auto tablet = DependencyManager::get()->getTablet(SYSTEM_TABLET); + auto hmd = DependencyManager::get(); + bool onTablet = false; - if (tablet->getToolbarMode() || false == scriptsRunning) { - static const QUrl url("hifi/dialogs/RunningScripts.qml"); - DependencyManager::get()->show(url, "RunningScripts"); - } else { - auto hmd = DependencyManager::get(); - if (!hmd->getShouldShowTablet() && !isHMDMode()) { - static const QUrl url("hifi/dialogs/RunningScripts.qml"); - DependencyManager::get()->show(url, "RunningScripts"); - } else { - static const QUrl url("../../hifi/dialogs/TabletRunningScripts.qml"); - tablet->pushOntoStack(url); + if (!tablet->getToolbarMode()) { + onTablet = tablet->pushOntoStack(tabletUrl); + if (onTablet) { + toggleTabletUI(true); } } - //DependencyManager::get()->show(url, "RunningScripts"); - //if (_runningScriptsWidget->isVisible()) { - // if (_runningScriptsWidget->hasFocus()) { - // _runningScriptsWidget->hide(); - // } else { - // _runningScriptsWidget->raise(); - // setActiveWindow(_runningScriptsWidget); - // _runningScriptsWidget->setFocus(); - // } - //} else { - // _runningScriptsWidget->show(); - // _runningScriptsWidget->setFocus(); - //} + + if (!onTablet) { + DependencyManager::get()->show(widgetUrl, name); + } + if (tablet->getToolbarMode()) { + DependencyManager::get()->show(widgetUrl, name); + } } void Application::showScriptLogs() { @@ -5870,7 +5846,7 @@ void Application::showAssetServerWidget(QString filePath) { } }; auto tabletScriptingInterface = DependencyManager::get(); - auto tablet = dynamic_cast(tabletScriptingInterface->getTablet("com.highfidelity.interface.tablet.system")); + auto tablet = dynamic_cast(tabletScriptingInterface->getTablet(SYSTEM_TABLET)); auto hmd = DependencyManager::get(); if (tablet->getToolbarMode()) { DependencyManager::get()->show(url, "AssetServer", startUpload); @@ -5905,21 +5881,6 @@ void Application::addAssetToWorldFromURL(QString url) { request->send(); } -void Application::showDialog(const QString& desktopURL, const QString& tabletURL, const QString& name) const { - auto tabletScriptingInterface = DependencyManager::get(); - auto tablet = dynamic_cast(tabletScriptingInterface->getTablet("com.highfidelity.interface.tablet.system")); - auto hmd = DependencyManager::get(); - if (tablet->getToolbarMode()) { - DependencyManager::get()->show(desktopURL, name); - } else { - tablet->pushOntoStack(tabletURL); - if (!hmd->getShouldShowTablet() && !isHMDMode()) { - hmd->openTablet(); - } - - } -} - void Application::addAssetToWorldFromURLRequestFinished() { auto request = qobject_cast(sender()); auto url = request->getUrl().toString(); @@ -6403,7 +6364,7 @@ void Application::loadScriptURLDialog() const { void Application::loadLODToolsDialog() { auto tabletScriptingInterface = DependencyManager::get(); - auto tablet = dynamic_cast(tabletScriptingInterface->getTablet("com.highfidelity.interface.tablet.system")); + auto tablet = dynamic_cast(tabletScriptingInterface->getTablet(SYSTEM_TABLET)); if (tablet->getToolbarMode() || (!tablet->getTabletRoot() && !isHMDMode())) { auto dialogsManager = DependencyManager::get(); dialogsManager->lodTools(); @@ -6415,7 +6376,7 @@ void Application::loadLODToolsDialog() { void Application::loadEntityStatisticsDialog() { auto tabletScriptingInterface = DependencyManager::get(); - auto tablet = dynamic_cast(tabletScriptingInterface->getTablet("com.highfidelity.interface.tablet.system")); + auto tablet = dynamic_cast(tabletScriptingInterface->getTablet(SYSTEM_TABLET)); if (tablet->getToolbarMode() || (!tablet->getTabletRoot() && !isHMDMode())) { auto dialogsManager = DependencyManager::get(); dialogsManager->octreeStatsDetails(); @@ -6426,7 +6387,7 @@ void Application::loadEntityStatisticsDialog() { void Application::loadDomainConnectionDialog() { auto tabletScriptingInterface = DependencyManager::get(); - auto tablet = dynamic_cast(tabletScriptingInterface->getTablet("com.highfidelity.interface.tablet.system")); + auto tablet = dynamic_cast(tabletScriptingInterface->getTablet(SYSTEM_TABLET)); if (tablet->getToolbarMode() || (!tablet->getTabletRoot() && !isHMDMode())) { auto dialogsManager = DependencyManager::get(); dialogsManager->showDomainConnectionDialog(); @@ -7083,11 +7044,6 @@ void Application::updateSystemTabletMode() { } } -void Application::toggleMuteAudio() { - auto menu = Menu::getInstance(); - menu->setIsOptionChecked(MenuOption::MuteAudio, !menu->isOptionChecked(MenuOption::MuteAudio)); -} - OverlayID Application::getTabletScreenID() const { auto HMD = DependencyManager::get(); return HMD->getCurrentTabletScreenID(); diff --git a/interface/src/Application.h b/interface/src/Application.h index 46e5e882a4..dadedde7bd 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -181,7 +181,6 @@ public: QUndoStack* getUndoStack() { return &_undoStack; } MainWindow* getWindow() const { return _window; } EntityTreePointer getEntityClipboard() const { return _entityClipboard; } - EntityTreeRenderer* getEntityClipboardRenderer() { return &_entityClipboardRenderer; } EntityEditPacketSender* getEntityEditPacketSender() { return &_entityEditSender; } ivec2 getMouse() const; @@ -318,11 +317,10 @@ public slots: Q_INVOKABLE void loadScriptURLDialog() const; void toggleLogDialog(); void toggleEntityScriptServerLogDialog(); - void toggleRunningScriptsWidget() const; Q_INVOKABLE void showAssetServerWidget(QString filePath = ""); Q_INVOKABLE void loadAddAvatarBookmarkDialog() const; - void showDialog(const QString& desktopURL, const QString& tabletURL, const QString& name) const; + void showDialog(const QUrl& widgetUrl, const QUrl& tabletUrl, const QString& name) const; // FIXME: Move addAssetToWorld* methods to own class? void addAssetToWorldFromURL(QString url); @@ -391,7 +389,6 @@ public slots: void addAssetToWorldMessageClose(); - Q_INVOKABLE void toggleMuteAudio(); void loadLODToolsDialog(); void loadEntityStatisticsDialog(); void loadDomainConnectionDialog(); @@ -401,11 +398,10 @@ private slots: void showDesktop(); void clearDomainOctreeDetails(); void clearDomainAvatars(); - void aboutToQuit(); + void onAboutToQuit(); void resettingDomain(); - void audioMuteToggled() const; void faceTrackerMuteToggled(); void activeChanged(Qt::ApplicationState state); @@ -503,7 +499,7 @@ private: static void dragEnterEvent(QDragEnterEvent* event); void maybeToggleMenuVisible(QMouseEvent* event) const; - void toggleTabletUI() const; + void toggleTabletUI(bool shouldOpen = false) const; MainWindow* _window; QElapsedTimer& _sessionRunTimer; @@ -535,7 +531,6 @@ private: PhysicalEntitySimulationPointer _entitySimulation; PhysicsEnginePointer _physicsEngine; - EntityTreeRenderer _entityClipboardRenderer; EntityTreePointer _entityClipboard; mutable QMutex _viewMutex { QMutex::Recursive }; diff --git a/interface/src/LODManager.h b/interface/src/LODManager.h index d25546ca2d..3d5161298d 100644 --- a/interface/src/LODManager.h +++ b/interface/src/LODManager.h @@ -17,6 +17,7 @@ #include #include #include +#include const float DEFAULT_DESKTOP_LOD_DOWN_FPS = 20.0; const float DEFAULT_HMD_LOD_DOWN_FPS = 20.0; @@ -45,7 +46,6 @@ const float ADJUST_LOD_MAX_SIZE_SCALE = DEFAULT_OCTREE_SIZE_SCALE; // This controls how low the auto-adjust LOD will go. We want a minimum vision of ~20:500 or 0.04 of default const float ADJUST_LOD_MIN_SIZE_SCALE = DEFAULT_OCTREE_SIZE_SCALE * 0.04f; -class RenderArgs; class AABox; class LODManager : public QObject, public Dependency { diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 463069430d..bc771594a9 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -94,8 +94,13 @@ Menu::Menu() { addActionToQMenuAndActionHash(editMenu, redoAction); // Edit > Running Scripts - addActionToQMenuAndActionHash(editMenu, MenuOption::RunningScripts, Qt::CTRL | Qt::Key_J, - qApp, SLOT(toggleRunningScriptsWidget())); + auto action = addActionToQMenuAndActionHash(editMenu, MenuOption::RunningScripts, Qt::CTRL | Qt::Key_J); + connect(action, &QAction::triggered, [] { + static const QUrl widgetUrl("hifi/dialogs/RunningScripts.qml"); + static const QUrl tabletUrl("../../hifi/dialogs/TabletRunningScripts.qml"); + static const QString name("RunningScripts"); + qApp->showDialog(widgetUrl, tabletUrl, name); + }); // Edit > Open and Run Script from File... [advanced] addActionToQMenuAndActionHash(editMenu, MenuOption::LoadScript, Qt::CTRL | Qt::Key_O, @@ -143,28 +148,13 @@ Menu::Menu() { addActionToQMenuAndActionHash(editMenu, MenuOption::ReloadContent, 0, qApp, SLOT(reloadResourceCaches()), QAction::NoRole, UNSPECIFIED_POSITION, "Advanced"); - - // Audio menu ---------------------------------- - MenuWrapper* audioMenu = addMenu("Audio"); - auto audioIO = DependencyManager::get(); - - // Audio > Mute - addCheckableActionToQMenuAndActionHash(audioMenu, MenuOption::MuteAudio, Qt::CTRL | Qt::Key_M, false, - audioIO.data(), SLOT(toggleMute())); - - // Audio > Show Level Meter - addCheckableActionToQMenuAndActionHash(audioMenu, MenuOption::AudioTools, 0, false); - - addCheckableActionToQMenuAndActionHash(audioMenu, MenuOption::AudioNoiseReduction, 0, true, - audioIO.data(), SLOT(toggleAudioNoiseReduction())); - // Avatar menu ---------------------------------- MenuWrapper* avatarMenu = addMenu("Avatar"); auto avatarManager = DependencyManager::get(); auto avatar = avatarManager->getMyAvatar(); // Avatar > Attachments... - auto action = addActionToQMenuAndActionHash(avatarMenu, MenuOption::Attachments); + action = addActionToQMenuAndActionHash(avatarMenu, MenuOption::Attachments); connect(action, &QAction::triggered, [] { qApp->showDialog(QString("hifi/dialogs/AttachmentsDialog.qml"), QString("../../hifi/tablet/TabletAttachmentsDialog.qml"), "AttachmentsDialog"); @@ -297,6 +287,14 @@ Menu::Menu() { QString("../../hifi/tablet/TabletGeneralPreferences.qml"), "GeneralPreferencesDialog"); }); + action = addActionToQMenuAndActionHash(settingsMenu, "Audio..."); + connect(action, &QAction::triggered, [] { + static const QUrl widgetUrl("hifi/dialogs/Audio.qml"); + static const QUrl tabletUrl("../../hifi/audio/Audio.qml"); + static const QString name("AudioDialog"); + qApp->showDialog(widgetUrl, tabletUrl, name); + }); + // Settings > Avatar... action = addActionToQMenuAndActionHash(settingsMenu, "Avatar..."); connect(action, &QAction::triggered, [] { @@ -622,10 +620,11 @@ Menu::Menu() { action = addActionToQMenuAndActionHash(audioDebugMenu, "Buffers..."); connect(action, &QAction::triggered, [] { - qApp->showDialog(QString("hifi/dialogs/AudioPreferencesDialog.qml"), - QString("../../hifi/tablet/TabletAudioPreferences.qml"), "AudioPreferencesDialog"); + qApp->showDialog(QString("hifi/dialogs/AudioBuffers.qml"), + QString("../../hifi/tablet/TabletAudioBuffers.qml"), "AudioBuffersDialog"); }); + auto audioIO = DependencyManager::get(); addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::EchoServerAudio, 0, false, audioIO.data(), SLOT(toggleServerEcho())); addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::EchoLocalAudio, 0, false, diff --git a/interface/src/Menu.h b/interface/src/Menu.h index 41bf89a20a..f69860099d 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -36,7 +36,6 @@ namespace MenuOption { const QString AssetMigration = "ATP Asset Migration"; const QString AssetServer = "Asset Browser"; const QString Attachments = "Attachments..."; - const QString AudioNoiseReduction = "Noise Reduction"; const QString AudioScope = "Show Scope"; const QString AudioScopeFiftyFrames = "Fifty"; const QString AudioScopeFiveFrames = "Five"; @@ -44,7 +43,6 @@ namespace MenuOption { const QString AudioScopePause = "Pause Scope"; const QString AudioScopeTwentyFrames = "Twenty"; const QString AudioStatsShowInjectedStreams = "Audio Stats Show Injected Streams"; - const QString AudioTools = "Show Level Meter"; const QString AutoMuteAudio = "Auto Mute Microphone"; const QString AvatarReceiveStats = "Show Receive Stats"; const QString AvatarBookmarks = "Avatar Bookmarks"; @@ -124,7 +122,6 @@ namespace MenuOption { const QString LogExtraTimings = "Log Extra Timing Details"; const QString LowVelocityFilter = "Low Velocity Filter"; const QString MeshVisible = "Draw Mesh"; - const QString MuteAudio = "Mute Microphone"; const QString MuteEnvironment = "Mute Environment"; const QString MuteFaceTracking = "Mute Face Tracking"; const QString NamesAboveHeads = "Names Above Heads"; diff --git a/interface/src/audio/AudioScope.h b/interface/src/audio/AudioScope.h index 615bdaf17f..e0c8840bb2 100644 --- a/interface/src/audio/AudioScope.h +++ b/interface/src/audio/AudioScope.h @@ -19,7 +19,6 @@ #include #include -#include class AudioScope : public QObject, public Dependency { diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 24a25f314d..1adcfbd345 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1961,6 +1961,32 @@ void MyAvatar::updateOrientation(float deltaTime) { totalBodyYaw += (speedFactor * deltaAngle * (180.0f / PI)); } + // Use head/HMD roll to turn while walking or flying. + if (qApp->isHMDMode() && _hmdRollControlEnabled) { + // Turn with head roll. + const float MIN_CONTROL_SPEED = 0.01f; + float speed = glm::length(getVelocity()); + if (speed >= MIN_CONTROL_SPEED) { + // Feather turn when stopping moving. + float speedFactor; + if (getDriveKey(TRANSLATE_Z) != 0.0f || _lastDrivenSpeed == 0.0f) { + _lastDrivenSpeed = speed; + speedFactor = 1.0f; + } else { + speedFactor = glm::min(speed / _lastDrivenSpeed, 1.0f); + } + + float direction = glm::dot(getVelocity(), getRotation() * Vectors::UNIT_NEG_Z) > 0.0f ? 1.0f : -1.0f; + + float rollAngle = glm::degrees(asinf(glm::dot(IDENTITY_UP, _hmdSensorOrientation * IDENTITY_RIGHT))); + float rollSign = rollAngle < 0.0f ? -1.0f : 1.0f; + rollAngle = fabsf(rollAngle); + rollAngle = rollAngle > _hmdRollControlDeadZone ? rollSign * (rollAngle - _hmdRollControlDeadZone) : 0.0f; + + totalBodyYaw += speedFactor * direction * rollAngle * deltaTime * _hmdRollControlRate; + } + } + // update body orientation by movement inputs glm::quat initialOrientation = getOrientationOutbound(); setOrientation(getOrientation() * glm::quat(glm::radians(glm::vec3(0.0f, totalBodyYaw, 0.0f)))); diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index cfe66eb10e..f61f24fb11 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -132,6 +132,10 @@ class MyAvatar : public Avatar { Q_PROPERTY(bool characterControllerEnabled READ getCharacterControllerEnabled WRITE setCharacterControllerEnabled) Q_PROPERTY(bool useAdvancedMovementControls READ useAdvancedMovementControls WRITE setUseAdvancedMovementControls) + Q_PROPERTY(bool hmdRollControlEnabled READ getHMDRollControlEnabled WRITE setHMDRollControlEnabled) + Q_PROPERTY(float hmdRollControlDeadZone READ getHMDRollControlDeadZone WRITE setHMDRollControlDeadZone) + Q_PROPERTY(float hmdRollControlRate READ getHMDRollControlRate WRITE setHMDRollControlRate) + public: enum DriveKeys { TRANSLATE_X = 0, @@ -337,6 +341,13 @@ public: void setUseAdvancedMovementControls(bool useAdvancedMovementControls) { _useAdvancedMovementControls.set(useAdvancedMovementControls); } + void setHMDRollControlEnabled(bool value) { _hmdRollControlEnabled = value; } + bool getHMDRollControlEnabled() const { return _hmdRollControlEnabled; } + void setHMDRollControlDeadZone(float value) { _hmdRollControlDeadZone = value; } + float getHMDRollControlDeadZone() const { return _hmdRollControlDeadZone; } + void setHMDRollControlRate(float value) { _hmdRollControlRate = value; } + float getHMDRollControlRate() const { return _hmdRollControlRate; } + // get/set avatar data void saveData(); void loadData(); @@ -687,6 +698,13 @@ private: bool _useSnapTurn { true }; bool _clearOverlayWhenMoving { true }; + const float ROLL_CONTROL_DEAD_ZONE_DEFAULT = 8.0f; // deg + const float ROLL_CONTROL_RATE_DEFAULT = 2.5f; // deg/sec/deg + bool _hmdRollControlEnabled { true }; + float _hmdRollControlDeadZone { ROLL_CONTROL_DEAD_ZONE_DEFAULT }; + float _hmdRollControlRate { ROLL_CONTROL_RATE_DEFAULT }; + float _lastDrivenSpeed { 0.0f }; + // working copies -- see AvatarData for thread-safe _sensorToWorldMatrixCache, used for outward facing access glm::mat4 _sensorToWorldMatrix { glm::mat4() }; diff --git a/interface/src/scripting/Audio.cpp b/interface/src/scripting/Audio.cpp new file mode 100644 index 0000000000..0b99e60c89 --- /dev/null +++ b/interface/src/scripting/Audio.cpp @@ -0,0 +1,109 @@ +// +// Audio.cpp +// interface/src/scripting +// +// Created by Zach Pomerantz on 28/5/2017. +// Copyright 2017 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "Audio.h" + +#include "Application.h" +#include "AudioClient.h" +#include "ui/AvatarInputs.h" + +using namespace scripting; + +QString Audio::AUDIO { "Audio" }; +QString Audio::DESKTOP { "Desktop" }; +QString Audio::HMD { "VR" }; + +Setting::Handle enableNoiseReductionSetting { QStringList { Audio::AUDIO, "NoiseReduction" }, true }; + +Audio::Audio() : _devices(_contextIsHMD) { + auto client = DependencyManager::get(); + connect(client.data(), &AudioClient::muteToggled, this, &Audio::onMutedChanged); + connect(this, &Audio::contextChanged, &_devices, &AudioDevices::onContextChanged); + connect(&_devices._inputs, &AudioDeviceList::deviceChanged, this, &Audio::onInputChanged); + enableNoiseReduction(enableNoiseReductionSetting.get()); +} + +void Audio::setMuted(bool isMuted) { + if (_isMuted != isMuted) { + auto client = DependencyManager::get().data(); + QMetaObject::invokeMethod(client, "toggleMute", Qt::BlockingQueuedConnection); + + _isMuted = isMuted; + emit mutedChanged(_isMuted); + } +} + +void Audio::onMutedChanged() { + auto client = DependencyManager::get().data(); + bool isMuted; + QMetaObject::invokeMethod(client, "isMuted", Qt::BlockingQueuedConnection, Q_RETURN_ARG(bool, isMuted)); + + if (_isMuted != isMuted) { + _isMuted = isMuted; + emit mutedChanged(_isMuted); + } +} + +void Audio::enableNoiseReduction(bool enable) { + if (_enableNoiseReduction != enable) { + auto client = DependencyManager::get().data(); + QMetaObject::invokeMethod(client, "setNoiseReduction", Qt::BlockingQueuedConnection, Q_ARG(bool, enable)); + + enableNoiseReductionSetting.set(enable); + _enableNoiseReduction = enable; + emit noiseReductionChanged(enable); + } +} + +void Audio::setInputVolume(float volume) { + // getInputVolume will not reflect changes synchronously, so clamp beforehand + volume = glm::clamp(volume, 0.0f, 1.0f); + + if (_inputVolume != volume) { + auto client = DependencyManager::get().data(); + QMetaObject::invokeMethod(client, "setInputVolume", Qt::BlockingQueuedConnection, Q_ARG(float, volume)); + + _inputVolume = volume; + emit inputVolumeChanged(_inputVolume); + } +} + +// different audio input devices may have different volumes +void Audio::onInputChanged() { + auto client = DependencyManager::get().data(); + float volume; + QMetaObject::invokeMethod(client, "getInputVolume", Qt::BlockingQueuedConnection, Q_RETURN_ARG(float, volume)); + + if (_inputVolume != volume) { + _inputVolume = volume; + emit inputVolumeChanged(_inputVolume); + } +} + +QString Audio::getContext() const { + return _contextIsHMD ? Audio::HMD : Audio::DESKTOP; +} + +void Audio::onContextChanged() { + bool isHMD = qApp->isHMDMode(); + if (_contextIsHMD != isHMD) { + _contextIsHMD = isHMD; + emit contextChanged(getContext()); + } +} + +void Audio::setReverb(bool enable) { + DependencyManager::get()->setReverb(enable); +} + +void Audio::setReverbOptions(const AudioEffectOptions* options) { + DependencyManager::get()->setReverbOptions(options); +} \ No newline at end of file diff --git a/interface/src/scripting/Audio.h b/interface/src/scripting/Audio.h new file mode 100644 index 0000000000..d438df41ca --- /dev/null +++ b/interface/src/scripting/Audio.h @@ -0,0 +1,81 @@ +// +// Audio.h +// interface/src/scripting +// +// Created by Zach Pomerantz on 28/5/2017. +// Copyright 2017 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_scripting_Audio_h +#define hifi_scripting_Audio_h + +#include "AudioScriptingInterface.h" +#include "AudioDevices.h" +#include "AudioEffectOptions.h" +#include "SettingHandle.h" + +namespace scripting { + +class Audio : public AudioScriptingInterface { + Q_OBJECT + SINGLETON_DEPENDENCY + + Q_PROPERTY(bool muted READ isMuted WRITE setMuted NOTIFY mutedChanged) + Q_PROPERTY(bool noiseReduction READ noiseReductionEnabled WRITE enableNoiseReduction NOTIFY noiseReductionChanged) + Q_PROPERTY(float inputVolume READ getInputVolume WRITE setInputVolume NOTIFY inputVolumeChanged) + Q_PROPERTY(QString context READ getContext NOTIFY contextChanged) + Q_PROPERTY(AudioDevices* devices READ getDevices NOTIFY nop) + +public: + static QString AUDIO; + static QString HMD; + static QString DESKTOP; + + virtual ~Audio() {} + + bool isMuted() const { return _isMuted; } + bool noiseReductionEnabled() const { return _enableNoiseReduction; } + float getInputVolume() const { return _inputVolume; } + QString getContext() const; + + void setMuted(bool muted); + void enableNoiseReduction(bool enable); + void showMicMeter(bool show); + void setInputVolume(float volume); + + Q_INVOKABLE void setReverb(bool enable); + Q_INVOKABLE void setReverbOptions(const AudioEffectOptions* options); + +signals: + void nop(); + void mutedChanged(bool isMuted); + void noiseReductionChanged(bool isEnabled); + void inputVolumeChanged(float volume); + void contextChanged(const QString& context); + +public slots: + void onMutedChanged(); + void onContextChanged(); + void onInputChanged(); + +protected: + // Audio must live on a separate thread from AudioClient to avoid deadlocks + Audio(); + +private: + + float _inputVolume { 1.0f }; + bool _isMuted { false }; + bool _enableNoiseReduction; + bool _contextIsHMD { false }; + + AudioDevices* getDevices() { return &_devices; } + AudioDevices _devices; +}; + +}; + +#endif // hifi_scripting_Audio_h diff --git a/interface/src/scripting/AudioDeviceScriptingInterface.cpp b/interface/src/scripting/AudioDeviceScriptingInterface.cpp deleted file mode 100644 index d22f948344..0000000000 --- a/interface/src/scripting/AudioDeviceScriptingInterface.cpp +++ /dev/null @@ -1,301 +0,0 @@ -// -// AudioDeviceScriptingInterface.cpp -// interface/src/scripting -// -// Created by Brad Hefta-Gaub on 3/23/14. -// Copyright 2014 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#include -#include - -#include "AudioDeviceScriptingInterface.h" -#include "SettingsScriptingInterface.h" - -AudioDeviceScriptingInterface* AudioDeviceScriptingInterface::getInstance() { - static AudioDeviceScriptingInterface sharedInstance; - return &sharedInstance; -} - -QStringList AudioDeviceScriptingInterface::inputAudioDevices() const { - return _inputAudioDevices; -} - -QStringList AudioDeviceScriptingInterface::outputAudioDevices() const { - return _outputAudioDevices; -} - -bool AudioDeviceScriptingInterface::muted() -{ - return getMuted(); -} - -AudioDeviceScriptingInterface::AudioDeviceScriptingInterface(): QAbstractListModel(nullptr) { - connect(DependencyManager::get().data(), &AudioClient::muteToggled, - this, &AudioDeviceScriptingInterface::muteToggled); - connect(DependencyManager::get().data(), &AudioClient::deviceChanged, - this, &AudioDeviceScriptingInterface::onDeviceChanged, Qt::QueuedConnection); - connect(DependencyManager::get().data(), &AudioClient::currentInputDeviceChanged, - this, &AudioDeviceScriptingInterface::onCurrentInputDeviceChanged, Qt::QueuedConnection); - connect(DependencyManager::get().data(), &AudioClient::currentOutputDeviceChanged, - this, &AudioDeviceScriptingInterface::onCurrentOutputDeviceChanged, Qt::QueuedConnection); - //fill up model - onDeviceChanged(); - //set up previously saved device - SettingsScriptingInterface* settings = SettingsScriptingInterface::getInstance(); - const QString inDevice = settings->getValue("audio_input_device", _currentInputDevice).toString(); - if (inDevice != _currentInputDevice) { - qCDebug(audioclient) << __FUNCTION__ << "about to call setInputDeviceAsync() device: [" << inDevice << "] _currentInputDevice:" << _currentInputDevice; - setInputDeviceAsync(inDevice); - } - - // If the audio_output_device setting is not available, use the _currentOutputDevice - auto outDevice = settings->getValue("audio_output_device", _currentOutputDevice).toString(); - if (outDevice != _currentOutputDevice) { - qCDebug(audioclient) << __FUNCTION__ << "about to call setOutputDeviceAsync() outDevice: [" << outDevice << "] _currentOutputDevice:" << _currentOutputDevice; - setOutputDeviceAsync(outDevice); - } -} - -bool AudioDeviceScriptingInterface::setInputDevice(const QString& deviceName) { - qCDebug(audioclient) << __FUNCTION__ << "deviceName:" << deviceName; - - bool result; - QMetaObject::invokeMethod(DependencyManager::get().data(), "switchInputToAudioDevice", - Qt::BlockingQueuedConnection, - Q_RETURN_ARG(bool, result), - Q_ARG(const QString&, deviceName)); - return result; -} - -bool AudioDeviceScriptingInterface::setOutputDevice(const QString& deviceName) { - - qCDebug(audioclient) << __FUNCTION__ << "deviceName:" << deviceName; - - bool result; - QMetaObject::invokeMethod(DependencyManager::get().data(), "switchOutputToAudioDevice", - Qt::BlockingQueuedConnection, - Q_RETURN_ARG(bool, result), - Q_ARG(const QString&, deviceName)); - return result; -} - -bool AudioDeviceScriptingInterface::setDeviceFromMenu(const QString& deviceMenuName) { - QAudio::Mode mode; - - if (deviceMenuName.indexOf("for Output") != -1) { - mode = QAudio::AudioOutput; - } else if (deviceMenuName.indexOf("for Input") != -1) { - mode = QAudio::AudioInput; - } else { - return false; - } - - for (ScriptingAudioDeviceInfo di: _devices) { - if (mode == di.mode && deviceMenuName.contains(di.name)) { - if (mode == QAudio::AudioOutput) { - qCDebug(audioclient) << __FUNCTION__ << "about to call setOutputDeviceAsync() device: [" << di.name << "]"; - setOutputDeviceAsync(di.name); - } else { - qCDebug(audioclient) << __FUNCTION__ << "about to call setInputDeviceAsync() device: [" << di.name << "]"; - setInputDeviceAsync(di.name); - } - return true; - } - } - - return false; -} - -void AudioDeviceScriptingInterface::setInputDeviceAsync(const QString& deviceName) { - qCDebug(audioclient) << __FUNCTION__ << "deviceName:" << deviceName; - - if (deviceName.isEmpty()) { - qCDebug(audioclient) << __FUNCTION__ << "attempt to set empty deviceName:" << deviceName << "... ignoring!"; - return; - } - - QMetaObject::invokeMethod(DependencyManager::get().data(), "switchInputToAudioDevice", - Qt::QueuedConnection, - Q_ARG(const QString&, deviceName)); -} - -void AudioDeviceScriptingInterface::setOutputDeviceAsync(const QString& deviceName) { - qCDebug(audioclient) << __FUNCTION__ << "deviceName:" << deviceName; - - if (deviceName.isEmpty()) { - qCDebug(audioclient) << __FUNCTION__ << "attempt to set empty deviceName:" << deviceName << "... ignoring!"; - return; - } - - QMetaObject::invokeMethod(DependencyManager::get().data(), "switchOutputToAudioDevice", - Qt::QueuedConnection, - Q_ARG(const QString&, deviceName)); -} - -QString AudioDeviceScriptingInterface::getInputDevice() { - return DependencyManager::get()->getDeviceName(QAudio::AudioInput); -} - -QString AudioDeviceScriptingInterface::getOutputDevice() { - return DependencyManager::get()->getDeviceName(QAudio::AudioOutput); -} - -QString AudioDeviceScriptingInterface::getDefaultInputDevice() { - return DependencyManager::get()->getDefaultDeviceName(QAudio::AudioInput); -} - -QString AudioDeviceScriptingInterface::getDefaultOutputDevice() { - return DependencyManager::get()->getDefaultDeviceName(QAudio::AudioOutput); -} - -QVector AudioDeviceScriptingInterface::getInputDevices() { - return DependencyManager::get()->getDeviceNames(QAudio::AudioInput); -} - -QVector AudioDeviceScriptingInterface::getOutputDevices() { - return DependencyManager::get()->getDeviceNames(QAudio::AudioOutput); -} - -float AudioDeviceScriptingInterface::getInputVolume() { - return DependencyManager::get()->getInputVolume(); -} - -void AudioDeviceScriptingInterface::setInputVolume(float volume) { - DependencyManager::get()->setInputVolume(volume); -} - -void AudioDeviceScriptingInterface::setReverb(bool reverb) { - DependencyManager::get()->setReverb(reverb); -} - -void AudioDeviceScriptingInterface::setReverbOptions(const AudioEffectOptions* options) { - DependencyManager::get()->setReverbOptions(options); -} - -void AudioDeviceScriptingInterface::toggleMute() { - DependencyManager::get()->toggleMute(); -} - -void AudioDeviceScriptingInterface::setMuted(bool muted) -{ - bool lMuted = getMuted(); - if (lMuted == muted) - return; - - toggleMute(); - lMuted = getMuted(); - emit mutedChanged(lMuted); -} - -bool AudioDeviceScriptingInterface::getMuted() { - return DependencyManager::get()->isMuted(); -} - -QVariant AudioDeviceScriptingInterface::data(const QModelIndex& index, int role) const { - //sanity - if (!index.isValid() || index.row() >= _devices.size()) - return QVariant(); - - - if (role == Qt::DisplayRole || role == DisplayNameRole) { - return _devices.at(index.row()).name; - } else if (role == SelectedRole) { - return _devices.at(index.row()).selected; - } else if (role == AudioModeRole) { - return (int)_devices.at(index.row()).mode; - } - return QVariant(); -} - -int AudioDeviceScriptingInterface::rowCount(const QModelIndex& parent) const { - Q_UNUSED(parent) - return _devices.size(); -} - -QHash AudioDeviceScriptingInterface::roleNames() const { - QHash roles; - roles.insert(DisplayNameRole, "devicename"); - roles.insert(SelectedRole, "devicechecked"); - roles.insert(AudioModeRole, "devicemode"); - return roles; -} - -void AudioDeviceScriptingInterface::onDeviceChanged() -{ - beginResetModel(); - _outputAudioDevices.clear(); - _devices.clear(); - _currentOutputDevice = getOutputDevice(); - for (QString name: getOutputDevices()) { - ScriptingAudioDeviceInfo di; - di.name = name; - di.selected = (name == _currentOutputDevice); - di.mode = QAudio::AudioOutput; - _devices.append(di); - _outputAudioDevices.append(name); - } - emit outputAudioDevicesChanged(_outputAudioDevices); - - _inputAudioDevices.clear(); - _currentInputDevice = getInputDevice(); - for (QString name: getInputDevices()) { - ScriptingAudioDeviceInfo di; - di.name = name; - di.selected = (name == _currentInputDevice); - di.mode = QAudio::AudioInput; - _devices.append(di); - _inputAudioDevices.append(name); - } - emit inputAudioDevicesChanged(_inputAudioDevices); - - endResetModel(); - emit deviceChanged(); -} - -void AudioDeviceScriptingInterface::onCurrentInputDeviceChanged(const QString& name) -{ - currentDeviceUpdate(name, QAudio::AudioInput); - //we got a signal that device changed. Save it now - SettingsScriptingInterface* settings = SettingsScriptingInterface::getInstance(); - settings->setValue("audio_input_device", name); - emit currentInputDeviceChanged(name); -} - -void AudioDeviceScriptingInterface::onCurrentOutputDeviceChanged(const QString& name) -{ - currentDeviceUpdate(name, QAudio::AudioOutput); - - // FIXME - this is kinda janky to set the setting on the result of a signal - //we got a signal that device changed. Save it now - SettingsScriptingInterface* settings = SettingsScriptingInterface::getInstance(); - qCDebug(audioclient) << __FUNCTION__ << "about to call settings->setValue('audio_output_device', name); name:" << name; - settings->setValue("audio_output_device", name); - emit currentOutputDeviceChanged(name); -} - -void AudioDeviceScriptingInterface::currentDeviceUpdate(const QString& name, QAudio::Mode mode) -{ - QVector role; - role.append(SelectedRole); - - for (int i = 0; i < _devices.size(); i++) { - ScriptingAudioDeviceInfo di = _devices.at(i); - if (di.mode != mode) { - continue; - } - if (di.selected && di.name != name ) { - di.selected = false; - _devices[i] = di; - emit dataChanged(index(i, 0), index(i, 0), role); - } - if (di.name == name) { - di.selected = true; - _devices[i] = di; - emit dataChanged(index(i, 0), index(i, 0), role); - } - } -} diff --git a/interface/src/scripting/AudioDeviceScriptingInterface.h b/interface/src/scripting/AudioDeviceScriptingInterface.h deleted file mode 100644 index f912c35288..0000000000 --- a/interface/src/scripting/AudioDeviceScriptingInterface.h +++ /dev/null @@ -1,107 +0,0 @@ -// -// AudioDeviceScriptingInterface.h -// interface/src/scripting -// -// Created by Brad Hefta-Gaub on 3/22/14. -// Copyright 2014 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#ifndef hifi_AudioDeviceScriptingInterface_h -#define hifi_AudioDeviceScriptingInterface_h - -#include -#include -#include -#include -#include - -class AudioEffectOptions; - -struct ScriptingAudioDeviceInfo { - QString name; - bool selected; - QAudio::Mode mode; -}; - -class AudioDeviceScriptingInterface : public QAbstractListModel { - Q_OBJECT - - Q_PROPERTY(QStringList inputAudioDevices READ inputAudioDevices NOTIFY inputAudioDevicesChanged) - Q_PROPERTY(QStringList outputAudioDevices READ outputAudioDevices NOTIFY outputAudioDevicesChanged) - Q_PROPERTY(bool muted READ muted WRITE setMuted NOTIFY mutedChanged) - -public: - static AudioDeviceScriptingInterface* getInstance(); - - QStringList inputAudioDevices() const; - QStringList outputAudioDevices() const; - bool muted(); - - QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; - int rowCount(const QModelIndex& parent = QModelIndex()) const override; - QHash roleNames() const override; - - enum Roles { - DisplayNameRole = Qt::UserRole, - SelectedRole, - AudioModeRole - }; - -private slots: - void onDeviceChanged(); - void onCurrentInputDeviceChanged(const QString& name); - void onCurrentOutputDeviceChanged(const QString& name); - void currentDeviceUpdate(const QString& name, QAudio::Mode mode); - -public slots: - bool setInputDevice(const QString& deviceName); - bool setOutputDevice(const QString& deviceName); - bool setDeviceFromMenu(const QString& deviceMenuName); - - QString getInputDevice(); - QString getOutputDevice(); - - QString getDefaultInputDevice(); - QString getDefaultOutputDevice(); - - QVector getInputDevices(); - QVector getOutputDevices(); - - float getInputVolume(); - void setInputVolume(float volume); - void setReverb(bool reverb); - void setReverbOptions(const AudioEffectOptions* options); - - bool getMuted(); - void toggleMute(); - - void setMuted(bool muted); - - void setInputDeviceAsync(const QString& deviceName); - void setOutputDeviceAsync(const QString& deviceName); -private: - AudioDeviceScriptingInterface(); - -signals: - void muteToggled(); - void deviceChanged(); - void currentInputDeviceChanged(const QString& name); - void currentOutputDeviceChanged(const QString& name); - void mutedChanged(bool muted); - void inputAudioDevicesChanged(QStringList inputAudioDevices); - void outputAudioDevicesChanged(QStringList outputAudioDevices); - -private: - QVector _devices; - - QStringList _inputAudioDevices; - QStringList _outputAudioDevices; - - QString _currentInputDevice; - QString _currentOutputDevice; -}; - -#endif // hifi_AudioDeviceScriptingInterface_h diff --git a/interface/src/scripting/AudioDevices.cpp b/interface/src/scripting/AudioDevices.cpp new file mode 100644 index 0000000000..055d59efe3 --- /dev/null +++ b/interface/src/scripting/AudioDevices.cpp @@ -0,0 +1,242 @@ +// +// AudioDevices.cpp +// interface/src/scripting +// +// Created by Zach Pomerantz on 28/5/2017. +// Copyright 2017 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "AudioDevices.h" + +#include "Application.h" +#include "AudioClient.h" +#include "Audio.h" + +using namespace scripting; + +Setting::Handle inputDeviceDesktop { QStringList { Audio::AUDIO, Audio::DESKTOP, "INPUT" }}; +Setting::Handle outputDeviceDesktop { QStringList { Audio::AUDIO, Audio::DESKTOP, "OUTPUT" }}; +Setting::Handle inputDeviceHMD { QStringList { Audio::AUDIO, Audio::HMD, "INPUT" }}; +Setting::Handle outputDeviceHMD { QStringList { Audio::AUDIO, Audio::HMD, "OUTPUT" }}; + +QHash AudioDeviceList::_roles { + { Qt::DisplayRole, "display" }, + { Qt::CheckStateRole, "selected" } +}; +Qt::ItemFlags AudioDeviceList::_flags { Qt::ItemIsSelectable | Qt::ItemIsEnabled }; + +QVariant AudioDeviceList::data(const QModelIndex& index, int role) const { + if (!index.isValid() || index.row() >= _devices.size()) { + return QVariant(); + } + + if (role == Qt::DisplayRole) { + return _devices.at(index.row()).display; + } else if (role == Qt::CheckStateRole) { + return _devices.at(index.row()).selected; + } else { + return QVariant(); + } +} + +bool AudioDeviceList::setData(const QModelIndex& index, const QVariant& value, int role) { + if (!index.isValid() || index.row() >= _devices.size()) { + return false; + } + + bool success = false; + + if (role == Qt::CheckStateRole) { + auto selected = value.toBool(); + auto& device = _devices[index.row()]; + + // only allow switching to a new device, not deactivating an in-use device + if (selected + // skip if already selected + && selected != device.selected) { + + auto client = DependencyManager::get(); + QMetaObject::invokeMethod(client.data(), "switchAudioDevice", Qt::BlockingQueuedConnection, + Q_RETURN_ARG(bool, success), + Q_ARG(QAudio::Mode, _mode), + Q_ARG(const QAudioDeviceInfo&, device.info)); + + if (success) { + device.selected = true; + emit deviceSelected(device.info); + emit deviceChanged(device.info); + } + } + } + + emit dataChanged(createIndex(0, 0), createIndex(rowCount() - 1, 0)); + return success; +} + +void AudioDeviceList::resetDevice(bool contextIsHMD, const QString& device) { + bool success { false }; + + // try to set the last selected device + if (!device.isNull()) { + auto i = 0; + for (; i < rowCount(); ++i) { + if (device == _devices[i].info.deviceName()) { + break; + } + } + if (i < rowCount()) { + success = setData(createIndex(i, 0), true, Qt::CheckStateRole); + } + + // the selection failed - reset it + if (!success) { + emit deviceSelected(QAudioDeviceInfo()); + } + } + + // try to set to the default device for this mode + if (!success) { + auto client = DependencyManager::get().data(); + if (contextIsHMD) { + QString deviceName; + if (_mode == QAudio::AudioInput) { + deviceName = qApp->getActiveDisplayPlugin()->getPreferredAudioInDevice(); + } else { // if (_mode == QAudio::AudioOutput) + deviceName = qApp->getActiveDisplayPlugin()->getPreferredAudioOutDevice(); + } + if (!deviceName.isNull()) { + QMetaObject::invokeMethod(client, "switchAudioDevice", Q_ARG(QAudio::Mode, _mode), Q_ARG(QString, deviceName)); + } + } else { + // use the system default + QMetaObject::invokeMethod(client, "switchAudioDevice", Q_ARG(QAudio::Mode, _mode)); + } + } +} + +void AudioDeviceList::onDeviceChanged(const QAudioDeviceInfo& device) { + _selectedDevice = device; + QModelIndex index; + + for (auto i = 0; i < _devices.size(); ++i) { + AudioDevice& device = _devices[i]; + + if (device.selected && device.info != _selectedDevice) { + device.selected = false; + } else if (device.info == _selectedDevice) { + device.selected = true; + index = createIndex(i, 0); + } + } + + emit deviceChanged(_selectedDevice); + emit dataChanged(createIndex(0, 0), createIndex(rowCount() - 1, 0)); +} + +void AudioDeviceList::onDevicesChanged(const QList& devices) { + beginResetModel(); + + _devices.clear(); + + foreach(const QAudioDeviceInfo& deviceInfo, devices) { + AudioDevice device; + device.info = deviceInfo; + device.display = device.info.deviceName() + .replace("High Definition", "HD") + .remove("Device") + .replace(" )", ")"); + device.selected = (device.info == _selectedDevice); + _devices.push_back(device); + } + + endResetModel(); +} + +AudioDevices::AudioDevices(bool& contextIsHMD) : _contextIsHMD(contextIsHMD) { + auto client = DependencyManager::get(); + + connect(client.data(), &AudioClient::deviceChanged, this, &AudioDevices::onDeviceChanged, Qt::QueuedConnection); + connect(client.data(), &AudioClient::devicesChanged, this, &AudioDevices::onDevicesChanged, Qt::QueuedConnection); + + // connections are made after client is initialized, so we must also fetch the devices + _inputs.onDeviceChanged(client->getActiveAudioDevice(QAudio::AudioInput)); + _outputs.onDeviceChanged(client->getActiveAudioDevice(QAudio::AudioOutput)); + _inputs.onDevicesChanged(client->getAudioDevices(QAudio::AudioInput)); + _outputs.onDevicesChanged(client->getAudioDevices(QAudio::AudioOutput)); + + connect(&_inputs, &AudioDeviceList::deviceSelected, this, &AudioDevices::onInputDeviceSelected); + connect(&_outputs, &AudioDeviceList::deviceSelected, this, &AudioDevices::onOutputDeviceSelected); +} + +void AudioDevices::onContextChanged(const QString& context) { + QString input; + QString output; + if (_contextIsHMD) { + input = inputDeviceHMD.get(); + output = outputDeviceHMD.get(); + } else { + input = inputDeviceDesktop.get(); + output = outputDeviceDesktop.get(); + } + + _inputs.resetDevice(_contextIsHMD, input); + _outputs.resetDevice(_contextIsHMD, output); +} + +void AudioDevices::onInputDeviceSelected(const QAudioDeviceInfo& device) { + QString deviceName; + if (!device.isNull()) { + deviceName = device.deviceName(); + } + + if (_contextIsHMD) { + inputDeviceHMD.set(deviceName); + } else { + inputDeviceDesktop.set(deviceName); + } +} + +void AudioDevices::onOutputDeviceSelected(const QAudioDeviceInfo& device) { + QString deviceName; + if (!device.isNull()) { + deviceName = device.deviceName(); + } + + if (_contextIsHMD) { + outputDeviceHMD.set(deviceName); + } else { + outputDeviceDesktop.set(deviceName); + } +} + +void AudioDevices::onDeviceChanged(QAudio::Mode mode, const QAudioDeviceInfo& device) { + if (mode == QAudio::AudioInput) { + _inputs.onDeviceChanged(device); + } else { // if (mode == QAudio::AudioOutput) + _outputs.onDeviceChanged(device); + } +} + +void AudioDevices::onDevicesChanged(QAudio::Mode mode, const QList& devices) { + static bool initialized { false }; + auto initialize = [&]{ + if (initialized) { + onContextChanged(QString()); + } else { + initialized = true; + } + }; + + if (mode == QAudio::AudioInput) { + _inputs.onDevicesChanged(devices); + static std::once_flag inputFlag; + std::call_once(inputFlag, initialize); + } else { // if (mode == QAudio::AudioOutput) + _outputs.onDevicesChanged(devices); + static std::once_flag outputFlag; + std::call_once(outputFlag, initialize); + } +} \ No newline at end of file diff --git a/interface/src/scripting/AudioDevices.h b/interface/src/scripting/AudioDevices.h new file mode 100644 index 0000000000..ec7022e721 --- /dev/null +++ b/interface/src/scripting/AudioDevices.h @@ -0,0 +1,98 @@ +// +// AudioDevices.h +// interface/src/scripting +// +// Created by Zach Pomerantz on 28/5/2017. +// Copyright 2017 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_scripting_AudioDevices_h +#define hifi_scripting_AudioDevices_h + +#include +#include +#include + +namespace scripting { + +class AudioDevice { +public: + QAudioDeviceInfo info; + QString display; + bool selected { false }; +}; + +class AudioDeviceList : public QAbstractListModel { + Q_OBJECT + +public: + AudioDeviceList(QAudio::Mode mode) : _mode(mode) {} + + int rowCount(const QModelIndex& parent = QModelIndex()) const override { Q_UNUSED(parent); return _devices.size(); } + QHash roleNames() const override { return _roles; } + Qt::ItemFlags flags(const QModelIndex& index) const override { return _flags; } + + // get/set devices through a QML ListView + QVariant data(const QModelIndex& index, int role) const override; + bool setData(const QModelIndex& index, const QVariant &value, int role) override; + + // reset device to the last selected device in this context, or the default + void resetDevice(bool contextIsHMD, const QString& device); + +signals: + void deviceSelected(const QAudioDeviceInfo& device); + void deviceChanged(const QAudioDeviceInfo& device); + +private slots: + void onDeviceChanged(const QAudioDeviceInfo& device); + void onDevicesChanged(const QList& devices); + +private: + friend class AudioDevices; + + static QHash _roles; + static Qt::ItemFlags _flags; + + QAudio::Mode _mode; + QAudioDeviceInfo _selectedDevice; + QList _devices; +}; + +class Audio; + +class AudioDevices : public QObject { + Q_OBJECT + Q_PROPERTY(AudioDeviceList* input READ getInputList NOTIFY nop) + Q_PROPERTY(AudioDeviceList* output READ getOutputList NOTIFY nop) + +public: + AudioDevices(bool& contextIsHMD); + +signals: + void nop(); + +private slots: + void onContextChanged(const QString& context); + void onInputDeviceSelected(const QAudioDeviceInfo& device); + void onOutputDeviceSelected(const QAudioDeviceInfo& device); + void onDeviceChanged(QAudio::Mode mode, const QAudioDeviceInfo& device); + void onDevicesChanged(QAudio::Mode mode, const QList& devices); + +private: + friend class Audio; + + AudioDeviceList* getInputList() { return &_inputs; } + AudioDeviceList* getOutputList() { return &_outputs; } + + AudioDeviceList _inputs { QAudio::AudioInput }; + AudioDeviceList _outputs { QAudio::AudioOutput }; + + bool& _contextIsHMD; +}; + +}; + +#endif // hifi_scripting_AudioDevices_h diff --git a/interface/src/scripting/MenuScriptingInterface.cpp b/interface/src/scripting/MenuScriptingInterface.cpp index cf186271d2..91fba1ce9e 100644 --- a/interface/src/scripting/MenuScriptingInterface.cpp +++ b/interface/src/scripting/MenuScriptingInterface.cpp @@ -152,9 +152,13 @@ void MenuScriptingInterface::closeInfoView(const QString& path) { } bool MenuScriptingInterface::isInfoViewVisible(const QString& path) { + if (QThread::currentThread() == qApp->thread()) { + return Menu::getInstance()->isInfoViewVisible(path); + } + bool result; QMetaObject::invokeMethod(Menu::getInstance(), "isInfoViewVisible", Qt::BlockingQueuedConnection, - Q_RETURN_ARG(bool, result), Q_ARG(const QString&, path)); + Q_RETURN_ARG(bool, result), Q_ARG(const QString&, path)); return result; } diff --git a/interface/src/ui/AvatarInputs.cpp b/interface/src/ui/AvatarInputs.cpp index 2b715eac9d..7a46aded77 100644 --- a/interface/src/ui/AvatarInputs.cpp +++ b/interface/src/ui/AvatarInputs.cpp @@ -18,9 +18,10 @@ HIFI_QML_DEF(AvatarInputs) - static AvatarInputs* INSTANCE{ nullptr }; +Setting::Handle showAudioToolsSetting { QStringList { "AvatarInputs", "showAudioTools" }, false }; + AvatarInputs* AvatarInputs::getInstance() { if (!INSTANCE) { AvatarInputs::registerType(); @@ -32,6 +33,7 @@ AvatarInputs* AvatarInputs::getInstance() { AvatarInputs::AvatarInputs(QQuickItem* parent) : QQuickItem(parent) { INSTANCE = this; + _showAudioTools = showAudioToolsSetting.get(); } #define AI_UPDATE(name, src) \ @@ -43,16 +45,6 @@ AvatarInputs::AvatarInputs(QQuickItem* parent) : QQuickItem(parent) { } \ } -#define AI_UPDATE_WRITABLE(name, src) \ - { \ - auto val = src; \ - if (_##name != val) { \ - _##name = val; \ - qDebug() << "AvatarInputs" << val; \ - emit name##Changed(val); \ - } \ - } - #define AI_UPDATE_FLOAT(name, src, epsilon) \ { \ float val = src; \ @@ -94,8 +86,6 @@ void AvatarInputs::update() { AI_UPDATE(cameraMuted, Menu::getInstance()->isOptionChecked(MenuOption::MuteFaceTracking)); AI_UPDATE(isHMD, qApp->isHMDMode()); - AI_UPDATE_WRITABLE(showAudioTools, Menu::getInstance()->isOptionChecked(MenuOption::AudioTools)); - auto audioIO = DependencyManager::get(); const float audioLevel = loudnessToAudioLevel(DependencyManager::get()->getLastInputLoudness()); @@ -122,8 +112,9 @@ void AvatarInputs::setShowAudioTools(bool showAudioTools) { if (_showAudioTools == showAudioTools) return; - Menu::getInstance()->setIsOptionChecked(MenuOption::AudioTools, showAudioTools); - update(); + _showAudioTools = showAudioTools; + showAudioToolsSetting.set(_showAudioTools); + emit showAudioToolsChanged(_showAudioTools); } void AvatarInputs::toggleCameraMute() { diff --git a/interface/src/ui/AvatarInputs.h b/interface/src/ui/AvatarInputs.h index bc9955a24f..247cc54995 100644 --- a/interface/src/ui/AvatarInputs.h +++ b/interface/src/ui/AvatarInputs.h @@ -49,7 +49,7 @@ signals: void audioClippingChanged(); void audioLevelChanged(); void isHMDChanged(); - void showAudioToolsChanged(bool showAudioTools); + void showAudioToolsChanged(bool show); protected: Q_INVOKABLE void resetSensors(); diff --git a/interface/src/ui/PreferencesDialog.cpp b/interface/src/ui/PreferencesDialog.cpp index 767c122bb6..41a4ebdf68 100644 --- a/interface/src/ui/PreferencesDialog.cpp +++ b/interface/src/ui/PreferencesDialog.cpp @@ -227,17 +227,17 @@ void setupPreferences() { preferences->addPreference(preference); } - static const QString AUDIO("Audio"); + static const QString AUDIO_BUFFERS("Audio Buffers"); { auto getter = []()->bool { return !DependencyManager::get()->getReceivedAudioStream().dynamicJitterBufferEnabled(); }; auto setter = [](bool value) { DependencyManager::get()->getReceivedAudioStream().setDynamicJitterBufferEnabled(!value); }; - auto preference = new CheckPreference(AUDIO, "Disable dynamic jitter buffer", getter, setter); + auto preference = new CheckPreference(AUDIO_BUFFERS, "Disable dynamic jitter buffer", getter, setter); preferences->addPreference(preference); } { auto getter = []()->float { return DependencyManager::get()->getReceivedAudioStream().getStaticJitterBufferFrames(); }; auto setter = [](float value) { DependencyManager::get()->getReceivedAudioStream().setStaticJitterBufferFrames(value); }; - auto preference = new SpinnerPreference(AUDIO, "Static jitter buffer frames", getter, setter); + auto preference = new SpinnerPreference(AUDIO_BUFFERS, "Static jitter buffer frames", getter, setter); preference->setMin(0); preference->setMax(2000); preference->setStep(1); @@ -246,13 +246,13 @@ void setupPreferences() { { auto getter = []()->bool { return !DependencyManager::get()->getOutputStarveDetectionEnabled(); }; auto setter = [](bool value) { DependencyManager::get()->setOutputStarveDetectionEnabled(!value); }; - auto preference = new CheckPreference(AUDIO, "Disable output starve detection", getter, setter); + auto preference = new CheckPreference(AUDIO_BUFFERS, "Disable output starve detection", getter, setter); preferences->addPreference(preference); } { auto getter = []()->float { return DependencyManager::get()->getOutputBufferSize(); }; auto setter = [](float value) { DependencyManager::get()->setOutputBufferSize(value); }; - auto preference = new SpinnerPreference(AUDIO, "Output buffer initial frames", getter, setter); + auto preference = new SpinnerPreference(AUDIO_BUFFERS, "Output buffer initial frames", getter, setter); preference->setMin(AudioClient::MIN_BUFFER_FRAMES); preference->setMax(AudioClient::MAX_BUFFER_FRAMES); preference->setStep(1); @@ -262,13 +262,13 @@ void setupPreferences() { { auto getter = []()->bool { return DependencyManager::get()->isSimulatingJitter(); }; auto setter = [](bool value) { return DependencyManager::get()->setIsSimulatingJitter(value); }; - auto preference = new CheckPreference(AUDIO, "Packet jitter simulator", getter, setter); + auto preference = new CheckPreference(AUDIO_BUFFERS, "Packet jitter simulator", getter, setter); preferences->addPreference(preference); } { auto getter = []()->float { return DependencyManager::get()->getGateThreshold(); }; auto setter = [](float value) { return DependencyManager::get()->setGateThreshold(value); }; - auto preference = new SpinnerPreference(AUDIO, "Packet throttle threshold", getter, setter); + auto preference = new SpinnerPreference(AUDIO_BUFFERS, "Packet throttle threshold", getter, setter); preference->setMin(1); preference->setMax(200); preference->setStep(1); diff --git a/interface/src/ui/Stats.cpp b/interface/src/ui/Stats.cpp index 161c407a45..8e3636dd7e 100644 --- a/interface/src/ui/Stats.cpp +++ b/interface/src/ui/Stats.cpp @@ -16,6 +16,7 @@ #include #include +#include #include #include #include @@ -33,6 +34,7 @@ #include "SequenceNumberStats.h" #include "StatTracker.h" + HIFI_QML_DEF(Stats) using namespace std; @@ -454,7 +456,7 @@ void Stats::updateStats(bool force) { } } -void Stats::setRenderDetails(const RenderDetails& details) { +void Stats::setRenderDetails(const render::RenderDetails& details) { STAT_UPDATE(triangles, details._trianglesRendered); STAT_UPDATE(materialSwitches, details._materialSwitches); if (_expanded) { diff --git a/interface/src/ui/Stats.h b/interface/src/ui/Stats.h index 2abb84faea..74d2589c35 100644 --- a/interface/src/ui/Stats.h +++ b/interface/src/ui/Stats.h @@ -9,10 +9,11 @@ #ifndef hifi_Stats_h #define hifi_Stats_h +#include + #include -#include -#include #include +#include #define STATS_PROPERTY(type, name, initialValue) \ Q_PROPERTY(type name READ name NOTIFY name##Changed) \ @@ -138,7 +139,7 @@ public: Stats(QQuickItem* parent = nullptr); bool includeTimingRecord(const QString& name); - void setRenderDetails(const RenderDetails& details); + void setRenderDetails(const render::RenderDetails& details); const QString& monospaceFont() { return _monospaceFont; } diff --git a/interface/src/ui/overlays/Image3DOverlay.cpp b/interface/src/ui/overlays/Image3DOverlay.cpp index 45d63d9cf1..3e0bb74bc4 100644 --- a/interface/src/ui/overlays/Image3DOverlay.cpp +++ b/interface/src/ui/overlays/Image3DOverlay.cpp @@ -112,7 +112,7 @@ void Image3DOverlay::render(RenderArgs* args) { _geometryId ); - batch->setResourceTexture(0, args->_whiteTexture); // restore default white color after me + batch->setResourceTexture(0, nullptr); // restore default white color after me } const render::ShapeKey Image3DOverlay::getShapeKey() { diff --git a/interface/src/ui/overlays/LocalModelsOverlay.cpp b/interface/src/ui/overlays/LocalModelsOverlay.cpp deleted file mode 100644 index ba82ba780a..0000000000 --- a/interface/src/ui/overlays/LocalModelsOverlay.cpp +++ /dev/null @@ -1,50 +0,0 @@ -// -// LocalModelsOverlay.cpp -// interface/src/ui/overlays -// -// Created by Ryan Huffman on 07/08/14. -// Copyright 2014 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#include "LocalModelsOverlay.h" - -#include -#include - - -QString const LocalModelsOverlay::TYPE = "localmodels"; - -LocalModelsOverlay::LocalModelsOverlay(EntityTreeRenderer* entityTreeRenderer) : - Volume3DOverlay(), - _entityTreeRenderer(entityTreeRenderer) { -} - -LocalModelsOverlay::LocalModelsOverlay(const LocalModelsOverlay* localModelsOverlay) : - Volume3DOverlay(localModelsOverlay), - _entityTreeRenderer(localModelsOverlay->_entityTreeRenderer) -{ -} - -void LocalModelsOverlay::update(float deltatime) { - _entityTreeRenderer->update(); -} - -void LocalModelsOverlay::render(RenderArgs* args) { - if (_visible) { - auto batch = args ->_batch; - - Transform transform = Transform(); - transform.setTranslation(args->getViewFrustum().getPosition() + getPosition()); - batch->setViewTransform(transform); - _entityTreeRenderer->render(args); - transform.setTranslation(args->getViewFrustum().getPosition()); - batch->setViewTransform(transform); - } -} - -LocalModelsOverlay* LocalModelsOverlay::createClone() const { - return new LocalModelsOverlay(this); -} diff --git a/interface/src/ui/overlays/LocalModelsOverlay.h b/interface/src/ui/overlays/LocalModelsOverlay.h deleted file mode 100644 index 6bab7f5f86..0000000000 --- a/interface/src/ui/overlays/LocalModelsOverlay.h +++ /dev/null @@ -1,37 +0,0 @@ -// -// LocalModelsOverlay.h -// interface/src/ui/overlays -// -// Created by Ryan Huffman on 07/08/14. -// Copyright 2014 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#ifndef hifi_LocalModelsOverlay_h -#define hifi_LocalModelsOverlay_h - -#include "Volume3DOverlay.h" - -class EntityTreeRenderer; - -class LocalModelsOverlay : public Volume3DOverlay { - Q_OBJECT -public: - static QString const TYPE; - virtual QString getType() const override { return TYPE; } - - LocalModelsOverlay(EntityTreeRenderer* entityTreeRenderer); - LocalModelsOverlay(const LocalModelsOverlay* localModelsOverlay); - - virtual void update(float deltatime) override; - virtual void render(RenderArgs* args) override; - - virtual LocalModelsOverlay* createClone() const override; - -private: - EntityTreeRenderer* _entityTreeRenderer; -}; - -#endif // hifi_LocalModelsOverlay_h diff --git a/interface/src/ui/overlays/Overlays.cpp b/interface/src/ui/overlays/Overlays.cpp index 4970112405..a9efd51a3e 100644 --- a/interface/src/ui/overlays/Overlays.cpp +++ b/interface/src/ui/overlays/Overlays.cpp @@ -26,7 +26,6 @@ #include "Shape3DOverlay.h" #include "ImageOverlay.h" #include "Line3DOverlay.h" -#include "LocalModelsOverlay.h" #include "ModelOverlay.h" #include "Rectangle3DOverlay.h" #include "Sphere3DOverlay.h" @@ -171,8 +170,6 @@ OverlayID Overlays::addOverlay(const QString& type, const QVariant& properties) thisOverlay = std::make_shared(); } else if (type == Grid3DOverlay::TYPE) { thisOverlay = std::make_shared(); - } else if (type == LocalModelsOverlay::TYPE) { - thisOverlay = std::make_shared(qApp->getEntityClipboardRenderer()); } else if (type == ModelOverlay::TYPE) { thisOverlay = std::make_shared(); } else if (type == Web3DOverlay::TYPE) { diff --git a/interface/src/ui/overlays/OverlaysPayload.cpp b/interface/src/ui/overlays/OverlaysPayload.cpp index f1beeea7f5..d14805c1ba 100644 --- a/interface/src/ui/overlays/OverlaysPayload.cpp +++ b/interface/src/ui/overlays/OverlaysPayload.cpp @@ -21,7 +21,6 @@ #include "Cube3DOverlay.h" #include "ImageOverlay.h" #include "Line3DOverlay.h" -#include "LocalModelsOverlay.h" #include "ModelOverlay.h" #include "Overlays.h" #include "Rectangle3DOverlay.h" diff --git a/interface/src/ui/overlays/Web3DOverlay.cpp b/interface/src/ui/overlays/Web3DOverlay.cpp index e26a641206..aa3fefce09 100644 --- a/interface/src/ui/overlays/Web3DOverlay.cpp +++ b/interface/src/ui/overlays/Web3DOverlay.cpp @@ -47,7 +47,6 @@ #include "LODManager.h" #include "ui/OctreeStatsProvider.h" #include "ui/DomainConnectionModel.h" -#include "scripting/AudioDeviceScriptingInterface.h" #include "ui/AvatarInputs.h" #include "avatar/AvatarManager.h" #include "scripting/GlobalServicesScriptingInterface.h" @@ -185,6 +184,7 @@ void Web3DOverlay::loadSourceURL() { _webSurface->getSurfaceContext()->setContextProperty("offscreenFlags", flags); _webSurface->getSurfaceContext()->setContextProperty("AddressManager", DependencyManager::get().data()); _webSurface->getSurfaceContext()->setContextProperty("Account", AccountScriptingInterface::getInstance()); + _webSurface->getSurfaceContext()->setContextProperty("Audio", DependencyManager::get().data()); _webSurface->getSurfaceContext()->setContextProperty("AudioStats", DependencyManager::get()->getStats().data()); _webSurface->getSurfaceContext()->setContextProperty("HMD", DependencyManager::get().data()); _webSurface->getSurfaceContext()->setContextProperty("fileDialogHelper", new FileDialogHelper()); @@ -195,7 +195,6 @@ void Web3DOverlay::loadSourceURL() { _webSurface->getSurfaceContext()->setContextProperty("LODManager", DependencyManager::get().data()); _webSurface->getSurfaceContext()->setContextProperty("OctreeStats", DependencyManager::get().data()); _webSurface->getSurfaceContext()->setContextProperty("DCModel", DependencyManager::get().data()); - _webSurface->getSurfaceContext()->setContextProperty("AudioDevice", AudioDeviceScriptingInterface::getInstance()); _webSurface->getSurfaceContext()->setContextProperty("AvatarInputs", AvatarInputs::getInstance()); _webSurface->getSurfaceContext()->setContextProperty("GlobalServices", GlobalServicesScriptingInterface::getInstance()); _webSurface->getSurfaceContext()->setContextProperty("AvatarList", DependencyManager::get().data()); @@ -319,7 +318,7 @@ void Web3DOverlay::render(RenderArgs* args) { geometryCache->bindOpaqueWebBrowserProgram(batch, _isAA); } geometryCache->renderQuad(batch, halfSize * -1.0f, halfSize, vec2(0), vec2(1), color, _geometryId); - batch.setResourceTexture(0, args->_whiteTexture); // restore default white color after me + batch.setResourceTexture(0, nullptr); // restore default white color after me } const render::ShapeKey Web3DOverlay::getShapeKey() { @@ -441,17 +440,27 @@ void Web3DOverlay::handlePointerEventAsTouch(const PointerEvent& event) { touchEvent->setTouchPoints(touchPoints); touchEvent->setTouchPointStates(touchPointState); + // Send mouse events to the Web surface so that HTML dialog elements work with mouse press and hover. + // FIXME: Scroll bar dragging is a bit unstable in the tablet (content can jump up and down at times). + // This may be improved in Qt 5.8. Release notes: "Cleaned up touch and mouse event delivery". + // + // In Qt 5.9 mouse events must be sent before touch events to make sure some QtQuick components will + // receive mouse events +#if QT_VERSION >= QT_VERSION_CHECK(5, 9, 0) + if (!(this->_pressed && event.getType() == PointerEvent::Move)) { + QMouseEvent* mouseEvent = new QMouseEvent(mouseType, windowPoint, windowPoint, windowPoint, button, buttons, Qt::NoModifier); + QCoreApplication::postEvent(_webSurface->getWindow(), mouseEvent); + } +#endif QCoreApplication::postEvent(_webSurface->getWindow(), touchEvent); - if (this->_pressed && event.getType() == PointerEvent::Move) { +#if QT_VERSION < QT_VERSION_CHECK(5, 9, 0) + if (this->_pressed && event.getType() == PointerEvent::Move) { return; } - // Send mouse events to the Web surface so that HTML dialog elements work with mouse press and hover. - // FIXME: Scroll bar dragging is a bit unstable in the tablet (content can jump up and down at times). - // This may be improved in Qt 5.8. Release notes: "Cleaned up touch and mouse event delivery". - QMouseEvent* mouseEvent = new QMouseEvent(mouseType, windowPoint, windowPoint, windowPoint, button, buttons, Qt::NoModifier); QCoreApplication::postEvent(_webSurface->getWindow(), mouseEvent); +#endif } void Web3DOverlay::handlePointerEventAsMouse(const PointerEvent& event) { diff --git a/libraries/animation/CMakeLists.txt b/libraries/animation/CMakeLists.txt index cc2f3858a3..b9fef19fbb 100644 --- a/libraries/animation/CMakeLists.txt +++ b/libraries/animation/CMakeLists.txt @@ -1,5 +1,7 @@ set(TARGET_NAME animation) setup_hifi_library(Network Script) link_hifi_libraries(shared model fbx) +include_hifi_library_headers(networking) +include_hifi_library_headers(gpu) target_nsight() diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 20a2aab2b6..fbb3d24298 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -1164,9 +1164,32 @@ void Rig::updateFromHandAndFeetParameters(const HandAndFeetParameters& params, f bool bodySensorTrackingEnabled = params.isLeftFootEnabled || params.isRightFootEnabled; const float RELAX_DURATION = 0.6f; + const float CONTROL_DURATION = 0.4f; + const bool TO_CONTROLLED = true; + const bool FROM_CONTROLLED = false; + const bool LEFT_HAND = true; + const bool RIGHT_HAND = false; if (params.isLeftEnabled) { + if (!_isLeftHandControlled) { + _leftHandControlTimeRemaining = CONTROL_DURATION; + _isLeftHandControlled = true; + } + glm::vec3 handPosition = params.leftPosition; + glm::quat handRotation = params.leftOrientation; + + if (_leftHandControlTimeRemaining > 0.0f) { + // Move hand from non-controlled position to controlled position. + _leftHandControlTimeRemaining = std::max(_leftHandControlTimeRemaining - dt, 0.0f); + AnimPose handPose(Vectors::ONE, handRotation, handPosition); + if (transitionHandPose(_leftHandControlTimeRemaining, CONTROL_DURATION, handPose, LEFT_HAND, TO_CONTROLLED, + handPose)) { + handPosition = handPose.trans(); + handRotation = handPose.rot(); + } + } + if (!bodySensorTrackingEnabled) { // prevent the hand IK targets from intersecting the body capsule glm::vec3 displacement; @@ -1176,27 +1199,22 @@ void Rig::updateFromHandAndFeetParameters(const HandAndFeetParameters& params, f } _animVars.set("leftHandPosition", handPosition); - _animVars.set("leftHandRotation", params.leftOrientation); + _animVars.set("leftHandRotation", handRotation); _animVars.set("leftHandType", (int)IKTarget::Type::RotationAndPosition); - _isLeftHandControlled = true; - _lastLeftHandControlledPose = AnimPose(glm::vec3(1.0f), params.leftOrientation, handPosition); + _lastLeftHandControlledPose = AnimPose(Vectors::ONE, handRotation, handPosition); } else { if (_isLeftHandControlled) { - _leftHandRelaxDuration = RELAX_DURATION; + _leftHandRelaxTimeRemaining = RELAX_DURATION; _isLeftHandControlled = false; } - if (_leftHandRelaxDuration > 0) { + if (_leftHandRelaxTimeRemaining > 0.0f) { // Move hand from controlled position to non-controlled position. - _leftHandRelaxDuration = std::max(_leftHandRelaxDuration - dt, 0.0f); - auto ikNode = getAnimInverseKinematicsNode(); - if (ikNode) { - float alpha = 1.0f - _leftHandRelaxDuration / RELAX_DURATION; - const AnimPose geometryToRigTransform(_geometryToRigTransform); - AnimPose uncontrolledHandPose = geometryToRigTransform * ikNode->getUncontrolledLeftHandPose(); - AnimPose handPose; - ::blend(1, &_lastLeftHandControlledPose, &uncontrolledHandPose, alpha, &handPose); + _leftHandRelaxTimeRemaining = std::max(_leftHandRelaxTimeRemaining - dt, 0.0f); + AnimPose handPose; + if (transitionHandPose(_leftHandRelaxTimeRemaining, RELAX_DURATION, _lastLeftHandControlledPose, LEFT_HAND, + FROM_CONTROLLED, handPose)) { _animVars.set("leftHandPosition", handPose.trans()); _animVars.set("leftHandRotation", handPose.rot()); _animVars.set("leftHandType", (int)IKTarget::Type::RotationAndPosition); @@ -1209,7 +1227,25 @@ void Rig::updateFromHandAndFeetParameters(const HandAndFeetParameters& params, f } if (params.isRightEnabled) { + if (!_isRightHandControlled) { + _rightHandControlTimeRemaining = CONTROL_DURATION; + _isRightHandControlled = true; + } + glm::vec3 handPosition = params.rightPosition; + glm::quat handRotation = params.rightOrientation; + + if (_rightHandControlTimeRemaining > 0.0f) { + // Move hand from non-controlled position to controlled position. + _rightHandControlTimeRemaining = std::max(_rightHandControlTimeRemaining - dt, 0.0f); + AnimPose handPose(Vectors::ONE, handRotation, handPosition); + if (transitionHandPose(_rightHandControlTimeRemaining, CONTROL_DURATION, handPose, RIGHT_HAND, TO_CONTROLLED, + handPose)) { + handPosition = handPose.trans(); + handRotation = handPose.rot(); + } + } + if (!bodySensorTrackingEnabled) { // prevent the hand IK targets from intersecting the body capsule glm::vec3 displacement; @@ -1219,27 +1255,22 @@ void Rig::updateFromHandAndFeetParameters(const HandAndFeetParameters& params, f } _animVars.set("rightHandPosition", handPosition); - _animVars.set("rightHandRotation", params.rightOrientation); + _animVars.set("rightHandRotation", handRotation); _animVars.set("rightHandType", (int)IKTarget::Type::RotationAndPosition); - _isRightHandControlled = true; - _lastRightHandControlledPose = AnimPose(glm::vec3(1.0f), params.rightOrientation, handPosition); + _lastRightHandControlledPose = AnimPose(Vectors::ONE, handRotation, handPosition); } else { if (_isRightHandControlled) { - _rightHandRelaxDuration = RELAX_DURATION; + _rightHandRelaxTimeRemaining = RELAX_DURATION; _isRightHandControlled = false; } - if (_rightHandRelaxDuration > 0) { + if (_rightHandRelaxTimeRemaining > 0.0f) { // Move hand from controlled position to non-controlled position. - _rightHandRelaxDuration = std::max(_rightHandRelaxDuration - dt, 0.0f); - auto ikNode = getAnimInverseKinematicsNode(); - if (ikNode) { - float alpha = 1.0f - _rightHandRelaxDuration / RELAX_DURATION; - const AnimPose geometryToRigTransform(_geometryToRigTransform); - AnimPose uncontrolledHandPose = geometryToRigTransform * ikNode->getUncontrolledRightHandPose(); - AnimPose handPose; - ::blend(1, &_lastRightHandControlledPose, &uncontrolledHandPose, alpha, &handPose); + _rightHandRelaxTimeRemaining = std::max(_rightHandRelaxTimeRemaining - dt, 0.0f); + AnimPose handPose; + if (transitionHandPose(_rightHandRelaxTimeRemaining, RELAX_DURATION, _lastRightHandControlledPose, RIGHT_HAND, + FROM_CONTROLLED, handPose)) { _animVars.set("rightHandPosition", handPose.trans()); _animVars.set("rightHandRotation", handPose.rot()); _animVars.set("rightHandType", (int)IKTarget::Type::RotationAndPosition); @@ -1545,3 +1576,25 @@ void Rig::computeAvatarBoundingCapsule( glm::vec3 rigCenter = (geometryToRig * (0.5f * (totalExtents.maximum + totalExtents.minimum))); localOffsetOut = rigCenter - (geometryToRig * rootPosition); } + +bool Rig::transitionHandPose(float deltaTime, float totalDuration, AnimPose& controlledHandPose, bool isLeftHand, + bool isToControlled, AnimPose& returnHandPose) { + auto ikNode = getAnimInverseKinematicsNode(); + if (ikNode) { + float alpha = 1.0f - deltaTime / totalDuration; + const AnimPose geometryToRigTransform(_geometryToRigTransform); + AnimPose uncontrolledHandPose; + if (isLeftHand) { + uncontrolledHandPose = geometryToRigTransform * ikNode->getUncontrolledLeftHandPose(); + } else { + uncontrolledHandPose = geometryToRigTransform * ikNode->getUncontrolledRightHandPose(); + } + if (isToControlled) { + ::blend(1, &uncontrolledHandPose, &controlledHandPose, alpha, &returnHandPose); + } else { + ::blend(1, &controlledHandPose, &uncontrolledHandPose, alpha, &returnHandPose); + } + return true; + } + return false; +} diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index 994bd4b074..b5b69fc018 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -352,10 +352,15 @@ private: int _nextStateHandlerId { 0 }; QMutex _stateMutex; + bool transitionHandPose(float deltaTime, float totalDuration, AnimPose& controlledHandPose, bool isLeftHand, + bool isToControlled, AnimPose& returnHandPose); + bool _isLeftHandControlled { false }; bool _isRightHandControlled { false }; - float _leftHandRelaxDuration { 0.0f }; - float _rightHandRelaxDuration { 0.0f }; + float _leftHandControlTimeRemaining { 0.0f }; + float _rightHandControlTimeRemaining { 0.0f }; + float _leftHandRelaxTimeRemaining { 0.0f }; + float _rightHandRelaxTimeRemaining { 0.0f }; AnimPose _lastLeftHandControlledPose; AnimPose _lastRightHandControlledPose; }; diff --git a/libraries/audio-client/CMakeLists.txt b/libraries/audio-client/CMakeLists.txt index 9c298ce664..d419a2fb7a 100644 --- a/libraries/audio-client/CMakeLists.txt +++ b/libraries/audio-client/CMakeLists.txt @@ -1,6 +1,8 @@ set(TARGET_NAME audio-client) setup_hifi_library(Network Multimedia) link_hifi_libraries(audio plugins) +include_hifi_library_headers(shared) +include_hifi_library_headers(networking) # append audio includes to our list of includes to bubble target_include_directories(${TARGET_NAME} PUBLIC "${HIFI_LIBRARY_DIR}/audio/src") diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp index 2f812724c5..f32d37562a 100644 --- a/libraries/audio-client/src/AudioClient.cpp +++ b/libraries/audio-client/src/AudioClient.cpp @@ -79,6 +79,49 @@ using Mutex = std::mutex; using Lock = std::unique_lock; static Mutex _deviceMutex; +// thread-safe +QList getAvailableDevices(QAudio::Mode mode) { + // NOTE: availableDevices() clobbers the Qt internal device list + Lock lock(_deviceMutex); + return QAudioDeviceInfo::availableDevices(mode); +} + +// now called from a background thread, to keep blocking operations off the audio thread +void AudioClient::checkDevices() { + auto inputDevices = getAvailableDevices(QAudio::AudioInput); + auto outputDevices = getAvailableDevices(QAudio::AudioOutput); + + if (inputDevices != _inputDevices) { + _inputDevices.swap(inputDevices); + emit devicesChanged(QAudio::AudioInput, _inputDevices); + } + + if (outputDevices != _outputDevices) { + _outputDevices.swap(outputDevices); + emit devicesChanged(QAudio::AudioOutput, _outputDevices); + } +} + +QAudioDeviceInfo AudioClient::getActiveAudioDevice(QAudio::Mode mode) const { + Lock lock(_deviceMutex); + + if (mode == QAudio::AudioInput) { + return _inputDeviceInfo; + } else { // if (mode == QAudio::AudioOutput) + return _outputDeviceInfo; + } +} + +QList AudioClient::getAudioDevices(QAudio::Mode mode) const { + Lock lock(_deviceMutex); + + if (mode == QAudio::AudioInput) { + return _inputDevices; + } else { // if (mode == QAudio::AudioOutput) + return _outputDevices; + } +} + static void channelUpmix(int16_t* source, int16_t* dest, int numSamples, int numExtraChannels) { for (int i = 0; i < numSamples/2; i++) { @@ -173,8 +216,9 @@ AudioClient::AudioClient() : connect(&_receivedAudioStream, &InboundAudioStream::mismatchedAudioCodec, this, &AudioClient::handleMismatchAudioFormat); - _inputDevices = getDeviceNames(QAudio::AudioInput); - _outputDevices = getDeviceNames(QAudio::AudioOutput); + // initialize wasapi; if getAvailableDevices is called from the CheckDevicesThread before this, it will crash + getAvailableDevices(QAudio::AudioInput); + getAvailableDevices(QAudio::AudioOutput); // start a thread to detect any device changes @@ -184,6 +228,8 @@ AudioClient::AudioClient() : checkDevices(); }); }); + const unsigned long DEVICE_CHECK_INTERVAL_MSECS = 2 * 1000; + _checkDevicesTimer->start(DEVICE_CHECK_INTERVAL_MSECS); configureReverb(); @@ -239,13 +285,6 @@ void AudioClient::audioMixerKilled() { emit disconnected(); } -// thread-safe -QList getAvailableDevices(QAudio::Mode mode) { - // NOTE: availableDevices() clobbers the Qt internal device list - Lock lock(_deviceMutex); - return QAudioDeviceInfo::availableDevices(mode); -} - QAudioDeviceInfo getNamedAudioDeviceForMode(QAudio::Mode mode, const QString& deviceName) { QAudioDeviceInfo result; foreach(QAudioDeviceInfo audioDevice, getAvailableDevices(mode)) { @@ -259,7 +298,7 @@ QAudioDeviceInfo getNamedAudioDeviceForMode(QAudio::Mode mode, const QString& de } #ifdef Q_OS_WIN -QString friendlyNameForAudioDevice(IMMDevice* pEndpoint) { +QString getWinDeviceName(IMMDevice* pEndpoint) { QString deviceName; IPropertyStore* pPropertyStore; pEndpoint->OpenPropertyStore(STGM_READ, &pPropertyStore); @@ -280,7 +319,7 @@ QString friendlyNameForAudioDevice(IMMDevice* pEndpoint) { return deviceName; } -QString AudioClient::friendlyNameForAudioDevice(wchar_t* guid) { +QString AudioClient::getWinDeviceName(wchar_t* guid) { QString deviceName; HRESULT hr = S_OK; CoInitialize(nullptr); @@ -292,7 +331,7 @@ QString AudioClient::friendlyNameForAudioDevice(wchar_t* guid) { printf("Audio Error: device not found\n"); deviceName = QString("NONE"); } else { - deviceName = ::friendlyNameForAudioDevice(pEndpoint); + deviceName = ::getWinDeviceName(pEndpoint); } pMMDeviceEnumerator->Release(); pMMDeviceEnumerator = nullptr; @@ -375,7 +414,7 @@ QAudioDeviceInfo defaultAudioDeviceForMode(QAudio::Mode mode) { printf("Audio Error: device not found\n"); deviceName = QString("NONE"); } else { - deviceName = friendlyNameForAudioDevice(pEndpoint); + deviceName = getWinDeviceName(pEndpoint); } pMMDeviceEnumerator->Release(); pMMDeviceEnumerator = NULL; @@ -393,6 +432,11 @@ QAudioDeviceInfo defaultAudioDeviceForMode(QAudio::Mode mode) { return (mode == QAudio::AudioInput) ? QAudioDeviceInfo::defaultInputDevice() : QAudioDeviceInfo::defaultOutputDevice(); } +bool AudioClient::getNamedAudioDeviceForModeExists(QAudio::Mode mode, const QString& deviceName) { + return (getNamedAudioDeviceForMode(mode, deviceName).deviceName() == deviceName); +} + + // attempt to use the native sample rate and channel count bool nativeFormatForAudioDevice(const QAudioDeviceInfo& audioDevice, QAudioFormat& audioFormat) { @@ -740,29 +784,22 @@ void AudioClient::selectAudioFormat(const QString& selectedCodecName) { } +bool AudioClient::switchAudioDevice(QAudio::Mode mode, const QAudioDeviceInfo& deviceInfo) { + auto device = deviceInfo; -QString AudioClient::getDefaultDeviceName(QAudio::Mode mode) { - QAudioDeviceInfo deviceInfo = defaultAudioDeviceForMode(mode); - return deviceInfo.deviceName(); -} - -QVector AudioClient::getDeviceNames(QAudio::Mode mode) { - QVector deviceNames; - const QList &availableDevice = getAvailableDevices(mode); - foreach(const QAudioDeviceInfo &audioDevice, availableDevice) { - deviceNames << audioDevice.deviceName().trimmed(); + if (device.isNull()) { + device = defaultAudioDeviceForMode(mode); + } + + if (mode == QAudio::AudioInput) { + return switchInputToAudioDevice(device); + } else { // if (mode == QAudio::AudioOutput) + return switchOutputToAudioDevice(device); } - return deviceNames; } -bool AudioClient::switchInputToAudioDevice(const QString& inputDeviceName) { - qCDebug(audioclient) << "switchInputToAudioDevice [" << inputDeviceName << "] [" << getNamedAudioDeviceForMode(QAudio::AudioInput, inputDeviceName).deviceName() << "]"; - return switchInputToAudioDevice(getNamedAudioDeviceForMode(QAudio::AudioInput, inputDeviceName)); -} - -bool AudioClient::switchOutputToAudioDevice(const QString& outputDeviceName) { - qCDebug(audioclient) << "switchOutputToAudioDevice [" << outputDeviceName << "] [" << getNamedAudioDeviceForMode(QAudio::AudioOutput, outputDeviceName).deviceName() << "]"; - return switchOutputToAudioDevice(getNamedAudioDeviceForMode(QAudio::AudioOutput, outputDeviceName)); +bool AudioClient::switchAudioDevice(QAudio::Mode mode, const QString& deviceName) { + return switchAudioDevice(mode, getNamedAudioDeviceForMode(mode, deviceName)); } void AudioClient::configureReverb() { @@ -1306,8 +1343,7 @@ void AudioClient::setIsStereoInput(bool isStereoInput) { } // change in channel count for desired input format, restart the input device - qCDebug(audioclient) << __FUNCTION__ << "about to call switchInputToAudioDevice:" << _inputAudioDeviceName; - switchInputToAudioDevice(_inputAudioDeviceName); + switchInputToAudioDevice(_inputDeviceInfo); } } @@ -1346,6 +1382,9 @@ bool AudioClient::switchInputToAudioDevice(const QAudioDeviceInfo& inputDeviceIn qCDebug(audioclient) << __FUNCTION__ << "inputDeviceInfo: [" << inputDeviceInfo.deviceName() << "]"; bool supportedFormat = false; + // NOTE: device start() uses the Qt internal device list + Lock lock(_deviceMutex); + // cleanup any previously initialized device if (_audioInput) { // The call to stop() causes _inputDevice to be destructed. @@ -1358,7 +1397,7 @@ bool AudioClient::switchInputToAudioDevice(const QAudioDeviceInfo& inputDeviceIn _audioInput = NULL; _numInputCallbackBytes = 0; - _inputAudioDeviceName = ""; + _inputDeviceInfo = QAudioDeviceInfo(); } if (_inputToNetworkResampler) { @@ -1373,8 +1412,8 @@ bool AudioClient::switchInputToAudioDevice(const QAudioDeviceInfo& inputDeviceIn if (!inputDeviceInfo.isNull()) { qCDebug(audioclient) << "The audio input device " << inputDeviceInfo.deviceName() << "is available."; - _inputAudioDeviceName = inputDeviceInfo.deviceName().trimmed(); - emit currentInputDeviceChanged(_inputAudioDeviceName); + _inputDeviceInfo = inputDeviceInfo; + emit deviceChanged(QAudio::AudioInput, inputDeviceInfo); if (adjustedFormatForAudioDevice(inputDeviceInfo, _desiredInputFormat, _inputFormat)) { qCDebug(audioclient) << "The format to be used for audio input is" << _inputFormat; @@ -1409,10 +1448,7 @@ bool AudioClient::switchInputToAudioDevice(const QAudioDeviceInfo& inputDeviceIn int numFrameSamples = calculateNumberOfFrameSamples(_numInputCallbackBytes); _inputRingBuffer.resizeForFrameSize(numFrameSamples); - // NOTE: device start() uses the Qt internal device list - Lock lock(_deviceMutex); _inputDevice = _audioInput->start(); - lock.unlock(); if (_inputDevice) { connect(_inputDevice, SIGNAL(readyRead()), this, SLOT(handleMicAudioInput())); @@ -1462,6 +1498,9 @@ bool AudioClient::switchOutputToAudioDevice(const QAudioDeviceInfo& outputDevice bool supportedFormat = false; + // NOTE: device start() uses the Qt internal device list + Lock lock(_deviceMutex); + Lock localAudioLock(_localAudioMutex); _localSamplesAvailable.exchange(0, std::memory_order_release); @@ -1487,6 +1526,8 @@ bool AudioClient::switchOutputToAudioDevice(const QAudioDeviceInfo& outputDevice delete[] _localOutputMixBuffer; _localOutputMixBuffer = NULL; + + _outputDeviceInfo = QAudioDeviceInfo(); } if (_networkToOutputResampler) { @@ -1500,8 +1541,8 @@ bool AudioClient::switchOutputToAudioDevice(const QAudioDeviceInfo& outputDevice if (!outputDeviceInfo.isNull()) { qCDebug(audioclient) << "The audio output device " << outputDeviceInfo.deviceName() << "is available."; - _outputAudioDeviceName = outputDeviceInfo.deviceName().trimmed(); - emit currentOutputDeviceChanged(_outputAudioDeviceName); + _outputDeviceInfo = outputDeviceInfo; + emit deviceChanged(QAudio::AudioOutput, outputDeviceInfo); if (adjustedFormatForAudioDevice(outputDeviceInfo, _desiredOutputFormat, _outputFormat)) { qCDebug(audioclient) << "The format to be used for audio output is" << _outputFormat; @@ -1576,10 +1617,7 @@ bool AudioClient::switchOutputToAudioDevice(const QAudioDeviceInfo& outputDevice _audioOutputIODevice.start(); - // NOTE: device start() uses the Qt internal device list - Lock lock(_deviceMutex); _audioOutput->start(&_audioOutputIODevice); - lock.unlock(); // setup a loopback audio output device _loopbackAudioOutput = new QAudioOutput(outputDeviceInfo, _outputFormat, this); @@ -1779,19 +1817,6 @@ qint64 AudioClient::AudioOutputIODevice::readData(char * data, qint64 maxSize) { return bytesWritten; } -// now called from a background thread, to keep blocking operations off the audio thread -void AudioClient::checkDevices() { - QVector inputDevices = getDeviceNames(QAudio::AudioInput); - QVector outputDevices = getDeviceNames(QAudio::AudioOutput); - - if (inputDevices != _inputDevices || outputDevices != _outputDevices) { - _inputDevices = inputDevices; - _outputDevices = outputDevices; - - emit deviceChanged(); - } -} - void AudioClient::loadSettings() { _receivedAudioStream.setDynamicJitterBufferEnabled(dynamicJitterBufferEnabled.get()); _receivedAudioStream.setStaticJitterBufferFrames(staticJitterBufferFrames.get()); diff --git a/libraries/audio-client/src/AudioClient.h b/libraries/audio-client/src/AudioClient.h index bec2fd2cc9..01241938d9 100644 --- a/libraries/audio-client/src/AudioClient.h +++ b/libraries/audio-client/src/AudioClient.h @@ -123,8 +123,6 @@ public: float getTimeSinceLastClip() const { return _timeSinceLastClip; } float getAudioAverageInputLoudness() const { return _lastInputLoudness; } - bool isMuted() { return _muted; } - const AudioIOStats& getStats() const { return _stats; } int getOutputBufferSize() { return _outputBufferSizeFrames.get(); } @@ -147,10 +145,15 @@ public: bool outputLocalInjector(AudioInjector* injector) override; + QAudioDeviceInfo getActiveAudioDevice(QAudio::Mode mode) const; + QList getAudioDevices(QAudio::Mode mode) const; + static const float CALLBACK_ACCELERATOR_RATIO; + bool getNamedAudioDeviceForModeExists(QAudio::Mode mode, const QString& deviceName); + #ifdef Q_OS_WIN - static QString friendlyNameForAudioDevice(wchar_t* guid); + static QString getWinDeviceName(wchar_t* guid); #endif public slots: @@ -170,11 +173,14 @@ public slots: void handleRecordedAudioInput(const QByteArray& audio); void reset(); void audioMixerKilled(); + void toggleMute(); + bool isMuted() { return _muted; } + virtual void setIsStereoInput(bool stereo) override; - void toggleAudioNoiseReduction() { _isNoiseGateEnabled = !_isNoiseGateEnabled; } + void setNoiseReduction(bool isNoiseGateEnabled) { _isNoiseGateEnabled = isNoiseGateEnabled; } void toggleLocalEcho() { _shouldEchoLocally = !_shouldEchoLocally; } void toggleServerEcho() { _shouldEchoToServer = !_shouldEchoToServer; } @@ -186,12 +192,9 @@ public slots: bool shouldLoopbackInjectors() override { return _shouldEchoToServer; } - bool switchInputToAudioDevice(const QString& inputDeviceName); - bool switchOutputToAudioDevice(const QString& outputDeviceName); - QString getDeviceName(QAudio::Mode mode) const { return (mode == QAudio::AudioInput) ? - _inputAudioDeviceName : _outputAudioDeviceName; } - QString getDefaultDeviceName(QAudio::Mode mode); - QVector getDeviceNames(QAudio::Mode mode); + // calling with a null QAudioDevice will use the system default + bool switchAudioDevice(QAudio::Mode mode, const QAudioDeviceInfo& deviceInfo = QAudioDeviceInfo()); + bool switchAudioDevice(QAudio::Mode mode, const QString& deviceName); float getInputVolume() const { return (_audioInput) ? (float)_audioInput->volume() : 0.0f; } void setInputVolume(float volume) { if (_audioInput) _audioInput->setVolume(volume); } @@ -213,7 +216,9 @@ signals: void noiseGateClosed(); void changeDevice(const QAudioDeviceInfo& outputDeviceInfo); - void deviceChanged(); + + void deviceChanged(QAudio::Mode mode, const QAudioDeviceInfo& device); + void devicesChanged(QAudio::Mode mode, const QList& devices); void receivedFirstPacket(); void disconnected(); @@ -222,9 +227,6 @@ signals: void muteEnvironmentRequested(glm::vec3 position, float radius); - void currentOutputDeviceChanged(const QString& name); - void currentInputDeviceChanged(const QString& name); - protected: AudioClient(); ~AudioClient(); @@ -291,9 +293,6 @@ private: MixedProcessedAudioStream _receivedAudioStream; bool _isStereoInput; - QString _inputAudioDeviceName; - QString _outputAudioDeviceName; - quint64 _outputStarveDetectionStartTimeMsec; int _outputStarveDetectionCount; @@ -369,8 +368,11 @@ private: glm::vec3 avatarBoundingBoxCorner; glm::vec3 avatarBoundingBoxScale; - QVector _inputDevices; - QVector _outputDevices; + QAudioDeviceInfo _inputDeviceInfo; + QAudioDeviceInfo _outputDeviceInfo; + + QList _inputDevices; + QList _outputDevices; bool _hasReceivedFirstPacket { false }; diff --git a/libraries/avatars-renderer/CMakeLists.txt b/libraries/avatars-renderer/CMakeLists.txt index 2ac5e6766d..bfbeab086d 100644 --- a/libraries/avatars-renderer/CMakeLists.txt +++ b/libraries/avatars-renderer/CMakeLists.txt @@ -2,5 +2,17 @@ set(TARGET_NAME avatars-renderer) AUTOSCRIBE_SHADER_LIB(gpu model render render-utils) setup_hifi_library(Widgets Network Script) link_hifi_libraries(shared gpu model animation model-networking script-engine render image render-utils) +include_hifi_library_headers(avatars) +include_hifi_library_headers(networking) +include_hifi_library_headers(fbx) +include_hifi_library_headers(recording) +include_hifi_library_headers(trackers) +include_hifi_library_headers(ktx) +include_hifi_library_headers(procedural) +include_hifi_library_headers(physics) +include_hifi_library_headers(entities-renderer) +include_hifi_library_headers(audio) +include_hifi_library_headers(entities) +include_hifi_library_headers(octree) target_bullet() diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 4407e12295..7731d53ec3 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -1504,7 +1504,7 @@ QUrl AvatarData::cannonicalSkeletonModelURL(const QUrl& emptyURL) const { return _skeletonModelURL.scheme() == "file" ? emptyURL : _skeletonModelURL; } -void AvatarData::processAvatarIdentity(const Identity& identity, bool& identityChanged, bool& displayNameChanged) { +void AvatarData::processAvatarIdentity(const Identity& identity, bool& identityChanged, bool& displayNameChanged, bool& skeletonModelUrlChanged) { if (identity.sequenceId < _identitySequenceId) { qCDebug(avatars) << "Ignoring older identity packet for avatar" << getSessionUUID() @@ -1517,6 +1517,7 @@ void AvatarData::processAvatarIdentity(const Identity& identity, bool& identityC if (_firstSkeletonCheck || (identity.skeletonModelURL != cannonicalSkeletonModelURL(emptyURL))) { setSkeletonModelURL(identity.skeletonModelURL); identityChanged = true; + skeletonModelUrlChanged = true; if (_firstSkeletonCheck) { displayNameChanged = true; } diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index 4104615cfe..8941d9d95f 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -368,6 +368,7 @@ public: virtual ~AvatarData(); static const QUrl& defaultFullAvatarModelUrl(); + QUrl cannonicalSkeletonModelURL(const QUrl& empty) const; virtual bool isMyAvatar() const { return false; } @@ -536,9 +537,8 @@ public: static void parseAvatarIdentityPacket(const QByteArray& data, Identity& identityOut); - // identityChanged returns true if identity has changed, false otherwise. - // displayNameChanged returns true if displayName has changed, false otherwise. - void processAvatarIdentity(const Identity& identity, bool& identityChanged, bool& displayNameChanged); + // identityChanged returns true if identity has changed, false otherwise. Similarly for displayNameChanged and skeletonModelUrlChange. + void processAvatarIdentity(const Identity& identity, bool& identityChanged, bool& displayNameChanged, bool& skeletonModelUrlChanged); QByteArray identityByteArray() const; @@ -697,7 +697,6 @@ protected: QVector _attachmentData; QString _displayName; QString _sessionDisplayName { }; - QUrl cannonicalSkeletonModelURL(const QUrl& empty) const; QHash _jointIndices; ///< 1-based, since zero is returned for missing keys QStringList _jointNames; ///< in order of depth-first traversal diff --git a/libraries/avatars/src/AvatarHashMap.cpp b/libraries/avatars/src/AvatarHashMap.cpp index 2ccc64fee2..fb954f4731 100644 --- a/libraries/avatars/src/AvatarHashMap.cpp +++ b/libraries/avatars/src/AvatarHashMap.cpp @@ -148,8 +148,9 @@ void AvatarHashMap::processAvatarIdentityPacket(QSharedPointer auto avatar = newOrExistingAvatar(identity.uuid, sendingNode); bool identityChanged = false; bool displayNameChanged = false; + bool skeletonModelUrlChanged = false; // In this case, the "sendingNode" is the Avatar Mixer. - avatar->processAvatarIdentity(identity, identityChanged, displayNameChanged); + avatar->processAvatarIdentity(identity, identityChanged, displayNameChanged, skeletonModelUrlChanged); } } diff --git a/libraries/controllers/CMakeLists.txt b/libraries/controllers/CMakeLists.txt index bf226f2647..6b1ab72c60 100644 --- a/libraries/controllers/CMakeLists.txt +++ b/libraries/controllers/CMakeLists.txt @@ -5,6 +5,7 @@ setup_hifi_library(Script Qml) # use setup_hifi_library macro to setup our project and link appropriate Qt modules link_hifi_libraries(shared) +include_hifi_library_headers(networking) GroupSources("src/controllers") diff --git a/libraries/display-plugins/CMakeLists.txt b/libraries/display-plugins/CMakeLists.txt index b7542c1d86..83a1c635ca 100644 --- a/libraries/display-plugins/CMakeLists.txt +++ b/libraries/display-plugins/CMakeLists.txt @@ -2,6 +2,14 @@ set(TARGET_NAME display-plugins) AUTOSCRIBE_SHADER_LIB(gpu display-plugins) setup_hifi_library(OpenGL) link_hifi_libraries(shared plugins ui-plugins gl gpu-gl ui render-utils) +include_hifi_library_headers(gpu) +include_hifi_library_headers(model-networking) +include_hifi_library_headers(networking) +include_hifi_library_headers(model) +include_hifi_library_headers(fbx) +include_hifi_library_headers(image) +include_hifi_library_headers(ktx) +include_hifi_library_headers(render) target_opengl() diff --git a/libraries/display-plugins/src/display-plugins/hmd/DebugHmdDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/hmd/DebugHmdDisplayPlugin.cpp index 5a3e5afc86..fd45398236 100644 --- a/libraries/display-plugins/src/display-plugins/hmd/DebugHmdDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/hmd/DebugHmdDisplayPlugin.cpp @@ -10,7 +10,6 @@ #include #include -#include #include const QString DebugHmdDisplayPlugin::NAME("HMD Simulator"); diff --git a/libraries/entities-renderer/CMakeLists.txt b/libraries/entities-renderer/CMakeLists.txt index 8ef28bbc7b..3df33b6a5d 100644 --- a/libraries/entities-renderer/CMakeLists.txt +++ b/libraries/entities-renderer/CMakeLists.txt @@ -2,6 +2,16 @@ set(TARGET_NAME entities-renderer) AUTOSCRIBE_SHADER_LIB(gpu model procedural render render-utils) setup_hifi_library(Widgets Network Script) link_hifi_libraries(shared gpu procedural model model-networking script-engine render render-utils image) +include_hifi_library_headers(networking) +include_hifi_library_headers(gl) +include_hifi_library_headers(ktx) +include_hifi_library_headers(octree) +include_hifi_library_headers(audio) +include_hifi_library_headers(physics) +include_hifi_library_headers(animation) +include_hifi_library_headers(fbx) +include_hifi_library_headers(entities) +include_hifi_library_headers(avatars) target_bullet() diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index 1b92adbc37..5b258db46b 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -9,6 +9,8 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include "EntityTreeRenderer.h" + #include #include @@ -24,7 +26,6 @@ #include #include -#include "EntityTreeRenderer.h" #include "RenderableEntityItem.h" @@ -130,7 +131,12 @@ void EntityTreeRenderer::clear() { if (scene) { render::Transaction transaction; foreach(auto entity, _entitiesInScene) { - entity->removeFromScene(entity, scene, transaction); + auto renderable = entity->getRenderableInterface(); + if (!renderable) { + qCWarning(entitiesrenderer) << "EntityTreeRenderer::deletingEntity(), trying to remove non-renderable entity"; + continue; + } + renderable->removeFromScene(entity, scene, transaction); } scene->enqueueTransaction(transaction); } else { @@ -141,7 +147,7 @@ void EntityTreeRenderer::clear() { // reset the zone to the default (while we load the next scene) _layeredZones.clear(); - OctreeRenderer::clear(); + OctreeProcessor::clear(); } void EntityTreeRenderer::reloadEntityScripts() { @@ -155,7 +161,7 @@ void EntityTreeRenderer::reloadEntityScripts() { } void EntityTreeRenderer::init() { - OctreeRenderer::init(); + OctreeProcessor::init(); EntityTreePointer entityTree = std::static_pointer_cast(_tree); entityTree->setFBXService(this); @@ -181,7 +187,7 @@ void EntityTreeRenderer::shutdown() { } void EntityTreeRenderer::setTree(OctreePointer newTree) { - OctreeRenderer::setTree(newTree); + OctreeProcessor::setTree(newTree); std::static_pointer_cast(_tree)->setFBXService(this); } @@ -791,24 +797,33 @@ void EntityTreeRenderer::mouseMoveEvent(QMouseEvent* event) { } void EntityTreeRenderer::deletingEntity(const EntityItemID& entityID) { + if (!_entitiesInScene.contains(entityID)) { + return; + } + if (_tree && !_shuttingDown && _entitiesScriptEngine) { _entitiesScriptEngine->unloadEntityScript(entityID, true); } + auto scene = _viewState->getMain3DScene(); + if (!scene) { + qCWarning(entitiesrenderer) << "EntityTreeRenderer::deletingEntity(), Unexpected null scene, possibly during application shutdown"; + return; + } + + auto entity = _entitiesInScene.take(entityID); + auto renderable = entity->getRenderableInterface(); + if (!renderable) { + qCWarning(entitiesrenderer) << "EntityTreeRenderer::deletingEntity(), trying to remove non-renderable entity"; + return; + } + forceRecheckEntities(); // reset our state to force checking our inside/outsideness of entities // here's where we remove the entity payload from the scene - if (_entitiesInScene.contains(entityID)) { - auto entity = _entitiesInScene.take(entityID); - render::Transaction transaction; - auto scene = _viewState->getMain3DScene(); - if (scene) { - entity->removeFromScene(entity, scene, transaction); - scene->enqueueTransaction(transaction); - } else { - qCWarning(entitiesrenderer) << "EntityTreeRenderer::deletingEntity(), Unexpected null scene, possibly during application shutdown"; - } - } + render::Transaction transaction; + renderable->removeFromScene(entity, scene, transaction); + scene->enqueueTransaction(transaction); } void EntityTreeRenderer::addingEntity(const EntityItemID& entityID) { @@ -820,18 +835,25 @@ void EntityTreeRenderer::addingEntity(const EntityItemID& entityID) { } } -void EntityTreeRenderer::addEntityToScene(EntityItemPointer entity) { +void EntityTreeRenderer::addEntityToScene(const EntityItemPointer& entity) { // here's where we add the entity payload to the scene - render::Transaction transaction; auto scene = _viewState->getMain3DScene(); - if (scene) { - if (entity->addToScene(entity, scene, transaction)) { - _entitiesInScene.insert(entity->getEntityItemID(), entity); - } - scene->enqueueTransaction(transaction); - } else { + if (!scene) { qCWarning(entitiesrenderer) << "EntityTreeRenderer::addEntityToScene(), Unexpected null scene, possibly during application shutdown"; + return; } + + auto renderable = entity->getRenderableInterface(); + if (!renderable) { + qCWarning(entitiesrenderer) << "EntityTreeRenderer::addEntityToScene(), Unexpected non-renderable entity"; + return; + } + + render::Transaction transaction; + if (renderable->addToScene(entity, scene, transaction)) { + _entitiesInScene.insert(entity->getEntityItemID(), entity); + } + scene->enqueueTransaction(transaction); } @@ -1050,3 +1072,4 @@ bool EntityTreeRenderer::LayeredZones::contains(const LayeredZones& other) { } return result; } + diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.h b/libraries/entities-renderer/src/EntityTreeRenderer.h index bf4148212b..5dcbd1aeb9 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.h +++ b/libraries/entities-renderer/src/EntityTreeRenderer.h @@ -20,9 +20,10 @@ #include #include #include -#include #include #include +#include +#include class AbstractScriptingServicesInterface; class AbstractViewStateInterface; @@ -38,7 +39,7 @@ using ModelWeakPointer = std::weak_ptr; using CalculateEntityLoadingPriority = std::function; // Generic client side Octree renderer class. -class EntityTreeRenderer : public OctreeRenderer, public EntityItemFBXService, public Dependency { +class EntityTreeRenderer : public OctreeProcessor, public EntityItemFBXService, public Dependency { Q_OBJECT public: EntityTreeRenderer(bool wantScripts, AbstractViewStateInterface* viewState, @@ -144,7 +145,7 @@ protected: private: void resetEntitiesScriptEngine(); - void addEntityToScene(EntityItemPointer entity); + void addEntityToScene(const EntityItemPointer& entity); bool findBestZoneAndMaybeContainingEntities(QVector* entitiesContainingAvatar = nullptr); bool applyLayeredZones(); diff --git a/libraries/entities-renderer/src/RenderableEntityItem.cpp b/libraries/entities-renderer/src/RenderableEntityItem.cpp index be9ef08c0b..f8d155600a 100644 --- a/libraries/entities-renderer/src/RenderableEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableEntityItem.cpp @@ -40,7 +40,7 @@ namespace render { template <> void payloadRender(const RenderableEntityItemProxy::Pointer& payload, RenderArgs* args) { if (args) { if (payload && payload->_entity && payload->_entity->getVisible()) { - payload->_entity->render(args); + payload->_entity->getRenderableInterface()->render(args); } } } diff --git a/libraries/entities-renderer/src/RenderableEntityItem.h b/libraries/entities-renderer/src/RenderableEntityItem.h index 1c08707e82..244a850d67 100644 --- a/libraries/entities-renderer/src/RenderableEntityItem.h +++ b/libraries/entities-renderer/src/RenderableEntityItem.h @@ -34,13 +34,23 @@ enum class RenderItemStatusIcon { void makeEntityItemStatusGetters(EntityItemPointer entity, render::Item::Status::Getters& statusGetters); +// Renderable entity item interface +class RenderableEntityInterface { +public: + virtual void render(RenderArgs* args) {}; + virtual bool addToScene(const EntityItemPointer& self, const render::ScenePointer& scene, render::Transaction& transaction) = 0; + virtual void removeFromScene(const EntityItemPointer& self, const render::ScenePointer& scene, render::Transaction& transaction) = 0; + virtual RenderableEntityInterface* getRenderableInterface() { return nullptr; } +}; + class RenderableEntityItemProxy { public: - RenderableEntityItemProxy(EntityItemPointer entity, render::ItemID metaID) : _entity(entity), _metaID(metaID) { } + RenderableEntityItemProxy(const EntityItemPointer& entity, render::ItemID metaID) + : _entity(entity), _metaID(metaID) {} typedef render::Payload Payload; typedef Payload::DataPointer Pointer; - EntityItemPointer _entity; + const EntityItemPointer _entity; render::ItemID _metaID; }; @@ -51,10 +61,11 @@ namespace render { template <> uint32_t metaFetchMetaSubItems(const RenderableEntityItemProxy::Pointer& payload, ItemIDs& subItems); } + // Mixin class for implementing basic single item rendering -class SimpleRenderableEntityItem { +class SimplerRenderableEntitySupport : public RenderableEntityInterface { public: - bool addToScene(EntityItemPointer self, const render::ScenePointer& scene, render::Transaction& transaction) { + bool addToScene(const EntityItemPointer& self, const render::ScenePointer& scene, render::Transaction& transaction) override { _myItem = scene->allocateID(); auto renderData = std::make_shared(self, _myItem); @@ -69,7 +80,7 @@ public: return true; } - void removeFromScene(EntityItemPointer self, const render::ScenePointer& scene, render::Transaction& transaction) { + void removeFromScene(const EntityItemPointer& self, const render::ScenePointer& scene, render::Transaction& transaction) override { transaction.removeItem(_myItem); render::Item::clearID(_myItem); } @@ -91,7 +102,6 @@ public: qCWarning(entitiesrenderer) << "SimpleRenderableEntityItem::notifyChanged(), Unexpected null scene, possibly during application shutdown"; } } - private: render::ItemID _myItem { render::Item::INVALID_ITEM_ID }; }; @@ -99,20 +109,18 @@ private: #define SIMPLE_RENDERABLE() \ public: \ - virtual bool addToScene(EntityItemPointer self, const render::ScenePointer& scene, render::Transaction& transaction) override { return _renderHelper.addToScene(self, scene, transaction); } \ - virtual void removeFromScene(EntityItemPointer self, const render::ScenePointer& scene, render::Transaction& transaction) override { _renderHelper.removeFromScene(self, scene, transaction); } \ - virtual void locationChanged(bool tellPhysics = true) override { EntityItem::locationChanged(tellPhysics); _renderHelper.notifyChanged(); } \ - virtual void dimensionsChanged() override { EntityItem::dimensionsChanged(); _renderHelper.notifyChanged(); } \ + virtual void locationChanged(bool tellPhysics = true) override { EntityItem::locationChanged(tellPhysics); notifyChanged(); } \ + virtual void dimensionsChanged() override { EntityItem::dimensionsChanged(); notifyChanged(); } \ + virtual RenderableEntityInterface* getRenderableInterface() override { return this; } \ void checkFading() { \ bool transparent = isTransparent(); \ if (transparent != _prevIsTransparent) { \ - _renderHelper.notifyChanged(); \ + notifyChanged(); \ _isFading = false; \ _prevIsTransparent = transparent; \ } \ } \ private: \ - SimpleRenderableEntityItem _renderHelper; \ bool _prevIsTransparent { isTransparent() }; diff --git a/libraries/entities-renderer/src/RenderableLightEntityItem.cpp b/libraries/entities-renderer/src/RenderableLightEntityItem.cpp index cc292759f0..09cbe3dcf2 100644 --- a/libraries/entities-renderer/src/RenderableLightEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableLightEntityItem.cpp @@ -27,7 +27,7 @@ RenderableLightEntityItem::RenderableLightEntityItem(const EntityItemID& entityI { } -bool RenderableLightEntityItem::addToScene(EntityItemPointer self, const render::ScenePointer& scene, render::Transaction& transaction) { +bool RenderableLightEntityItem::addToScene(const EntityItemPointer& self, const render::ScenePointer& scene, render::Transaction& transaction) { _myItem = scene->allocateID(); auto renderItem = std::make_shared(); @@ -51,7 +51,7 @@ void RenderableLightEntityItem::somethingChangedNotification() { LightEntityItem::somethingChangedNotification(); } -void RenderableLightEntityItem::removeFromScene(EntityItemPointer self, const render::ScenePointer& scene, render::Transaction& transaction) { +void RenderableLightEntityItem::removeFromScene(const EntityItemPointer& self, const render::ScenePointer& scene, render::Transaction& transaction) { transaction.removeItem(_myItem); render::Item::clearID(_myItem); } diff --git a/libraries/entities-renderer/src/RenderableLightEntityItem.h b/libraries/entities-renderer/src/RenderableLightEntityItem.h index 3676023bed..a9f4ba84b6 100644 --- a/libraries/entities-renderer/src/RenderableLightEntityItem.h +++ b/libraries/entities-renderer/src/RenderableLightEntityItem.h @@ -17,11 +17,13 @@ #include "RenderableEntityItem.h" -class RenderableLightEntityItem : public LightEntityItem { +class RenderableLightEntityItem : public LightEntityItem, public RenderableEntityInterface { public: static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties); RenderableLightEntityItem(const EntityItemID& entityItemID); + RenderableEntityInterface* getRenderableInterface() override { return this; } + virtual bool supportsDetailedRayIntersection() const override { return true; } virtual bool findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction, bool& keepSearching, OctreeElementPointer& element, float& distance, @@ -30,10 +32,10 @@ public: void updateLightFromEntity(render::Transaction& transaction); - virtual bool addToScene(EntityItemPointer self, const render::ScenePointer& scene, render::Transaction& transaction) override; + virtual bool addToScene(const EntityItemPointer& self, const render::ScenePointer& scene, render::Transaction& transaction) override; virtual void somethingChangedNotification() override; - virtual void removeFromScene(EntityItemPointer self, const render::ScenePointer& scene, render::Transaction& transaction) override; + virtual void removeFromScene(const EntityItemPointer& self, const render::ScenePointer& scene, render::Transaction& transaction) override; virtual void locationChanged(bool tellPhysics = true) override; diff --git a/libraries/entities-renderer/src/RenderableLineEntityItem.h b/libraries/entities-renderer/src/RenderableLineEntityItem.h index 6282bbbfc0..b8663c0055 100644 --- a/libraries/entities-renderer/src/RenderableLineEntityItem.h +++ b/libraries/entities-renderer/src/RenderableLineEntityItem.h @@ -16,7 +16,7 @@ #include "RenderableEntityItem.h" #include -class RenderableLineEntityItem : public LineEntityItem { +class RenderableLineEntityItem : public LineEntityItem, public SimplerRenderableEntitySupport { public: static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties); RenderableLineEntityItem(const EntityItemID& entityItemID) : diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index eab0e3011e..f343fdb155 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -213,7 +213,7 @@ namespace render { if (args) { if (payload && payload->entity) { PROFILE_RANGE(render_detail, "MetaModelRender"); - payload->entity->render(args); + payload->entity->getRenderableInterface()->render(args); } } } @@ -228,7 +228,7 @@ namespace render { } } -bool RenderableModelEntityItem::addToScene(EntityItemPointer self, const render::ScenePointer& scene, +bool RenderableModelEntityItem::addToScene(const EntityItemPointer& self, const render::ScenePointer& scene, render::Transaction& transaction) { _myMetaItem = scene->allocateID(); @@ -249,7 +249,7 @@ bool RenderableModelEntityItem::addToScene(EntityItemPointer self, const render: return true; } -void RenderableModelEntityItem::removeFromScene(EntityItemPointer self, const render::ScenePointer& scene, +void RenderableModelEntityItem::removeFromScene(const EntityItemPointer& self, const render::ScenePointer& scene, render::Transaction& transaction) { transaction.removeItem(_myMetaItem); render::Item::clearID(_myMetaItem); @@ -390,7 +390,7 @@ void RenderableModelEntityItem::render(RenderArgs* args) { if (!_model || _needsModelReload) { // TODO: this getModel() appears to be about 3% of model render time. We should optimize PerformanceTimer perfTimer("getModel"); - auto renderer = qSharedPointerCast(args->_renderer); + auto renderer = qSharedPointerCast(args->_renderData); getModel(renderer); // Remap textures immediately after loading to avoid flicker @@ -605,7 +605,7 @@ bool RenderableModelEntityItem::findDetailedRayIntersection(const glm::vec3& ori QString extraInfo; return _model->findRayIntersectionAgainstSubMeshes(origin, direction, distance, - face, surfaceNormal, extraInfo, precisionPicking, precisionPicking); + face, surfaceNormal, extraInfo, precisionPicking, false); } void RenderableModelEntityItem::getCollisionGeometryResource() { diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.h b/libraries/entities-renderer/src/RenderableModelEntityItem.h index b440aacd7e..2bbb51b3f0 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.h +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.h @@ -20,7 +20,7 @@ class Model; class EntityTreeRenderer; -class RenderableModelEntityItem : public ModelEntityItem { +class RenderableModelEntityItem : public ModelEntityItem, RenderableEntityInterface { public: static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties); @@ -28,6 +28,8 @@ public: virtual ~RenderableModelEntityItem(); + RenderableEntityInterface* getRenderableInterface() override { return this; } + virtual void setDimensions(const glm::vec3& value) override; virtual void setModelURL(const QString& url) override; @@ -40,8 +42,8 @@ public: void doInitialModelSimulation(); - virtual bool addToScene(EntityItemPointer self, const render::ScenePointer& scene, render::Transaction& transaction) override; - virtual void removeFromScene(EntityItemPointer self, const render::ScenePointer& scene, render::Transaction& transaction) override; + virtual bool addToScene(const EntityItemPointer& self, const render::ScenePointer& scene, render::Transaction& transaction) override; + virtual void removeFromScene(const EntityItemPointer& self, const render::ScenePointer& scene, render::Transaction& transaction) override; void updateModelBounds(); diff --git a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp index d813a73773..10bd70be13 100644 --- a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp @@ -161,7 +161,7 @@ RenderableParticleEffectEntityItem::RenderableParticleEffectEntityItem(const Ent } } -bool RenderableParticleEffectEntityItem::addToScene(EntityItemPointer self, +bool RenderableParticleEffectEntityItem::addToScene(const EntityItemPointer& self, const render::ScenePointer& scene, render::Transaction& transaction) { _scene = scene; @@ -176,7 +176,7 @@ bool RenderableParticleEffectEntityItem::addToScene(EntityItemPointer self, return true; } -void RenderableParticleEffectEntityItem::removeFromScene(EntityItemPointer self, +void RenderableParticleEffectEntityItem::removeFromScene(const EntityItemPointer& self, const render::ScenePointer& scene, render::Transaction& transaction) { transaction.removeItem(_renderItemId); diff --git a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.h b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.h index daa6ba0691..678b582b41 100644 --- a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.h +++ b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.h @@ -15,18 +15,20 @@ #include #include "RenderableEntityItem.h" -class RenderableParticleEffectEntityItem : public ParticleEffectEntityItem { +class RenderableParticleEffectEntityItem : public ParticleEffectEntityItem, public RenderableEntityInterface { friend class ParticlePayloadData; public: static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties); RenderableParticleEffectEntityItem(const EntityItemID& entityItemID); + RenderableEntityInterface* getRenderableInterface() override { return this; } + virtual void update(const quint64& now) override; void updateRenderItem(); - virtual bool addToScene(EntityItemPointer self, const render::ScenePointer& scene, render::Transaction& transaction) override; - virtual void removeFromScene(EntityItemPointer self, const render::ScenePointer& scene, render::Transaction& transaction) override; + virtual bool addToScene(const EntityItemPointer& self, const render::ScenePointer& scene, render::Transaction& transaction) override; + virtual void removeFromScene(const EntityItemPointer& self, const render::ScenePointer& scene, render::Transaction& transaction) override; protected: virtual void locationChanged(bool tellPhysics = true) override { EntityItem::locationChanged(tellPhysics); notifyBoundChanged(); } diff --git a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp index 1e20956301..d2bf8e3532 100644 --- a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp @@ -206,7 +206,7 @@ void RenderablePolyLineEntityItem::render(RenderArgs* args) { if (_texture->isLoaded()) { batch.setResourceTexture(PAINTSTROKE_TEXTURE_SLOT, _texture->getGPUTexture()); } else { - batch.setResourceTexture(PAINTSTROKE_TEXTURE_SLOT, args->_whiteTexture); + batch.setResourceTexture(PAINTSTROKE_TEXTURE_SLOT, nullptr); } batch.setInputFormat(_format); diff --git a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.h b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.h index eca6b7035a..8a62a761e0 100644 --- a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.h +++ b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.h @@ -22,7 +22,7 @@ #include -class RenderablePolyLineEntityItem : public PolyLineEntityItem { +class RenderablePolyLineEntityItem : public PolyLineEntityItem, public SimplerRenderableEntitySupport { public: static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties); static void createPipeline(); diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp index 7567566919..6cda472d96 100644 --- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp @@ -820,7 +820,7 @@ void RenderablePolyVoxEntityItem::render(RenderArgs* args) { batch.drawIndexed(gpu::TRIANGLES, (gpu::uint32)mesh->getNumIndices(), 0); } -bool RenderablePolyVoxEntityItem::addToScene(EntityItemPointer self, +bool RenderablePolyVoxEntityItem::addToScene(const EntityItemPointer& self, const render::ScenePointer& scene, render::Transaction& transaction) { _myItem = scene->allocateID(); @@ -838,7 +838,7 @@ bool RenderablePolyVoxEntityItem::addToScene(EntityItemPointer self, return true; } -void RenderablePolyVoxEntityItem::removeFromScene(EntityItemPointer self, +void RenderablePolyVoxEntityItem::removeFromScene(const EntityItemPointer& self, const render::ScenePointer& scene, render::Transaction& transaction) { transaction.removeItem(_myItem); @@ -865,7 +865,7 @@ namespace render { template <> void payloadRender(const PolyVoxPayload::Pointer& payload, RenderArgs* args) { if (args && payload && payload->_owner) { - payload->_owner->render(args); + payload->_owner->getRenderableInterface()->render(args); } } } diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h index ff97f09ee1..174d6338d3 100644 --- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h +++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h @@ -41,13 +41,15 @@ namespace render { } -class RenderablePolyVoxEntityItem : public PolyVoxEntityItem { +class RenderablePolyVoxEntityItem : public PolyVoxEntityItem, public RenderableEntityInterface { public: static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties); RenderablePolyVoxEntityItem(const EntityItemID& entityItemID); virtual ~RenderablePolyVoxEntityItem(); + RenderableEntityInterface* getRenderableInterface() override { return this; } + void initializePolyVox(); virtual void somethingChangedNotification() override { @@ -105,10 +107,10 @@ public: virtual void setYTextureURL(const QString& yTextureURL) override; virtual void setZTextureURL(const QString& zTextureURL) override; - virtual bool addToScene(EntityItemPointer self, + virtual bool addToScene(const EntityItemPointer& self, const render::ScenePointer& scene, render::Transaction& transaction) override; - virtual void removeFromScene(EntityItemPointer self, + virtual void removeFromScene(const EntityItemPointer& self, const render::ScenePointer& scene, render::Transaction& transaction) override; diff --git a/libraries/entities-renderer/src/RenderableShapeEntityItem.h b/libraries/entities-renderer/src/RenderableShapeEntityItem.h index 7eefe0e7a4..0cc6a54f81 100644 --- a/libraries/entities-renderer/src/RenderableShapeEntityItem.h +++ b/libraries/entities-renderer/src/RenderableShapeEntityItem.h @@ -14,7 +14,7 @@ #include "RenderableEntityItem.h" -class RenderableShapeEntityItem : public ShapeEntityItem { +class RenderableShapeEntityItem : public ShapeEntityItem, private SimplerRenderableEntitySupport { using Pointer = std::shared_ptr; static Pointer baseFactory(const EntityItemID& entityID, const EntityItemProperties& properties); public: diff --git a/libraries/entities-renderer/src/RenderableTextEntityItem.h b/libraries/entities-renderer/src/RenderableTextEntityItem.h index ee75931513..3ca1260b7b 100644 --- a/libraries/entities-renderer/src/RenderableTextEntityItem.h +++ b/libraries/entities-renderer/src/RenderableTextEntityItem.h @@ -19,7 +19,7 @@ const int FIXED_FONT_POINT_SIZE = 40; -class RenderableTextEntityItem : public TextEntityItem { +class RenderableTextEntityItem : public TextEntityItem, public SimplerRenderableEntitySupport { public: static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties); RenderableTextEntityItem(const EntityItemID& entityItemID) : TextEntityItem(entityItemID) { } diff --git a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp index 3b3480443d..948a219831 100644 --- a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp @@ -198,7 +198,7 @@ void RenderableWebEntityItem::render(RenderArgs* args) { #endif if (!_webSurface) { - auto renderer = qSharedPointerCast(args->_renderer); + auto renderer = qSharedPointerCast(args->_renderData); if (!buildWebSurface(renderer)) { return; } diff --git a/libraries/entities-renderer/src/RenderableWebEntityItem.h b/libraries/entities-renderer/src/RenderableWebEntityItem.h index e47e6bdfd3..bbd59a03d6 100644 --- a/libraries/entities-renderer/src/RenderableWebEntityItem.h +++ b/libraries/entities-renderer/src/RenderableWebEntityItem.h @@ -27,7 +27,7 @@ class EntityTreeRenderer; class RenderableWebEntityItem; -class RenderableWebEntityItem : public WebEntityItem { +class RenderableWebEntityItem : public WebEntityItem, SimplerRenderableEntitySupport { public: static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties); RenderableWebEntityItem(const EntityItemID& entityItemID); diff --git a/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp b/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp index d3fd9a0980..2d4dd50e88 100644 --- a/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp @@ -257,7 +257,7 @@ bool RenderableZoneEntityItem::contains(const glm::vec3& point) const { return false; } -bool RenderableZoneEntityItem::addToScene(EntityItemPointer self, const render::ScenePointer& scene, +bool RenderableZoneEntityItem::addToScene(const EntityItemPointer& self, const render::ScenePointer& scene, render::Transaction& transaction) { _myMetaItem = scene->allocateID(); @@ -277,7 +277,7 @@ bool RenderableZoneEntityItem::addToScene(EntityItemPointer self, const render:: return true; } -void RenderableZoneEntityItem::removeFromScene(EntityItemPointer self, const render::ScenePointer& scene, +void RenderableZoneEntityItem::removeFromScene(const EntityItemPointer& self, const render::ScenePointer& scene, render::Transaction& transaction) { transaction.removeItem(_myMetaItem); render::Item::clearID(_myMetaItem); diff --git a/libraries/entities-renderer/src/RenderableZoneEntityItem.h b/libraries/entities-renderer/src/RenderableZoneEntityItem.h index d0c7f64fbb..4685a0d3e1 100644 --- a/libraries/entities-renderer/src/RenderableZoneEntityItem.h +++ b/libraries/entities-renderer/src/RenderableZoneEntityItem.h @@ -14,13 +14,14 @@ #include #include +#include "RenderableEntityItem.h" class NetworkGeometry; class KeyLightPayload; class RenderableZoneEntityItemMeta; -class RenderableZoneEntityItem : public ZoneEntityItem { +class RenderableZoneEntityItem : public ZoneEntityItem, public RenderableEntityInterface { public: static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties); @@ -30,6 +31,8 @@ public: _needsInitialSimulation(true) { } + RenderableEntityInterface* getRenderableInterface() override { return this; } + virtual bool setProperties(const EntityItemProperties& properties) override; virtual void somethingChangedNotification() override; @@ -41,8 +44,8 @@ public: virtual void render(RenderArgs* args) override; virtual bool contains(const glm::vec3& point) const override; - virtual bool addToScene(EntityItemPointer self, const render::ScenePointer& scene, render::Transaction& transaction) override; - virtual void removeFromScene(EntityItemPointer self, const render::ScenePointer& scene, render::Transaction& transaction) override; + virtual bool addToScene(const EntityItemPointer& self, const render::ScenePointer& scene, render::Transaction& transaction) override; + virtual void removeFromScene(const EntityItemPointer& self, const render::ScenePointer& scene, render::Transaction& transaction) override; render::ItemID getRenderItemID() const { return _myMetaItem; } diff --git a/libraries/entities/CMakeLists.txt b/libraries/entities/CMakeLists.txt index b2ae0f0ab7..ddb5fbaf73 100644 --- a/libraries/entities/CMakeLists.txt +++ b/libraries/entities/CMakeLists.txt @@ -1,6 +1,8 @@ set(TARGET_NAME entities) setup_hifi_library(Network Script) link_hifi_libraries(avatars shared audio octree model model-networking fbx networking animation) +include_hifi_library_headers(networking) +include_hifi_library_headers(gpu) target_bullet() diff --git a/libraries/entities/src/AddEntityOperator.cpp b/libraries/entities/src/AddEntityOperator.cpp index 7cf4e4a472..e86e70dd80 100644 --- a/libraries/entities/src/AddEntityOperator.cpp +++ b/libraries/entities/src/AddEntityOperator.cpp @@ -30,7 +30,7 @@ AddEntityOperator::AddEntityOperator(EntityTreePointer tree, EntityItemPointer n _newEntityBox = queryCube.clamp((float)(-HALF_TREE_SCALE), (float)HALF_TREE_SCALE); } -bool AddEntityOperator::preRecursion(OctreeElementPointer element) { +bool AddEntityOperator::preRecursion(const OctreeElementPointer& element) { EntityTreeElementPointer entityTreeElement = std::static_pointer_cast(element); // In Pre-recursion, we're generally deciding whether or not we want to recurse this @@ -60,7 +60,7 @@ bool AddEntityOperator::preRecursion(OctreeElementPointer element) { return keepSearching; // if we haven't yet found it, keep looking } -bool AddEntityOperator::postRecursion(OctreeElementPointer element) { +bool AddEntityOperator::postRecursion(const OctreeElementPointer& element) { // Post-recursion is the unwinding process. For this operation, while we // unwind we want to mark the path as being dirty if we changed it below. // We might have two paths, one for the old entity and one for the new entity. @@ -74,7 +74,7 @@ bool AddEntityOperator::postRecursion(OctreeElementPointer element) { return keepSearching; // if we haven't yet found it, keep looking } -OctreeElementPointer AddEntityOperator::possiblyCreateChildAt(OctreeElementPointer element, int childIndex) { +OctreeElementPointer AddEntityOperator::possiblyCreateChildAt(const OctreeElementPointer& element, int childIndex) { // If we're getting called, it's because there was no child element at this index while recursing. // We only care if this happens while still searching for the new entity location. // Check to see if diff --git a/libraries/entities/src/AddEntityOperator.h b/libraries/entities/src/AddEntityOperator.h index 0f33cacae3..48ee49f4d1 100644 --- a/libraries/entities/src/AddEntityOperator.h +++ b/libraries/entities/src/AddEntityOperator.h @@ -16,9 +16,9 @@ class AddEntityOperator : public RecurseOctreeOperator { public: AddEntityOperator(EntityTreePointer tree, EntityItemPointer newEntity); - virtual bool preRecursion(OctreeElementPointer element) override; - virtual bool postRecursion(OctreeElementPointer element) override; - virtual OctreeElementPointer possiblyCreateChildAt(OctreeElementPointer element, int childIndex) override; + virtual bool preRecursion(const OctreeElementPointer& element) override; + virtual bool postRecursion(const OctreeElementPointer& element) override; + virtual OctreeElementPointer possiblyCreateChildAt(const OctreeElementPointer& element, int childIndex) override; private: EntityTreePointer _tree; EntityItemPointer _newEntity; diff --git a/libraries/entities/src/DeleteEntityOperator.cpp b/libraries/entities/src/DeleteEntityOperator.cpp index 48335c22b8..709c281341 100644 --- a/libraries/entities/src/DeleteEntityOperator.cpp +++ b/libraries/entities/src/DeleteEntityOperator.cpp @@ -55,7 +55,7 @@ void DeleteEntityOperator::addEntityIDToDeleteList(const EntityItemID& searchEnt // does this entity tree element contain the old entity -bool DeleteEntityOperator::subTreeContainsSomeEntitiesToDelete(OctreeElementPointer element) { +bool DeleteEntityOperator::subTreeContainsSomeEntitiesToDelete(const OctreeElementPointer& element) { bool containsEntity = false; // If we don't have an old entity, then we don't contain the entity, otherwise @@ -72,7 +72,7 @@ bool DeleteEntityOperator::subTreeContainsSomeEntitiesToDelete(OctreeElementPoin return containsEntity; } -bool DeleteEntityOperator::preRecursion(OctreeElementPointer element) { +bool DeleteEntityOperator::preRecursion(const OctreeElementPointer& element) { EntityTreeElementPointer entityTreeElement = std::static_pointer_cast(element); // In Pre-recursion, we're generally deciding whether or not we want to recurse this @@ -108,7 +108,7 @@ bool DeleteEntityOperator::preRecursion(OctreeElementPointer element) { return keepSearching; // if we haven't yet found it, keep looking } -bool DeleteEntityOperator::postRecursion(OctreeElementPointer element) { +bool DeleteEntityOperator::postRecursion(const OctreeElementPointer& element) { // Post-recursion is the unwinding process. For this operation, while we // unwind we want to mark the path as being dirty if we changed it below. // We might have two paths, one for the old entity and one for the new entity. diff --git a/libraries/entities/src/DeleteEntityOperator.h b/libraries/entities/src/DeleteEntityOperator.h index 245d331743..135949a53d 100644 --- a/libraries/entities/src/DeleteEntityOperator.h +++ b/libraries/entities/src/DeleteEntityOperator.h @@ -36,8 +36,8 @@ public: ~DeleteEntityOperator(); void addEntityIDToDeleteList(const EntityItemID& searchEntityID); - virtual bool preRecursion(OctreeElementPointer element) override; - virtual bool postRecursion(OctreeElementPointer element) override; + virtual bool preRecursion(const OctreeElementPointer& element) override; + virtual bool postRecursion(const OctreeElementPointer& element) override; const RemovedEntities& getEntities() const { return _entitiesToDelete; } private: @@ -46,7 +46,7 @@ private: quint64 _changeTime; int _foundCount; int _lookingCount; - bool subTreeContainsSomeEntitiesToDelete(OctreeElementPointer element); + bool subTreeContainsSomeEntitiesToDelete(const OctreeElementPointer& element); }; #endif // hifi_DeleteEntityOperator_h diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index 7c08137a1c..0318c72991 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -51,10 +51,6 @@ typedef std::shared_ptr EntityTreeElementPointer; using EntityTreeElementExtraEncodeDataPointer = std::shared_ptr; -namespace render { - class Scene; - class Transaction; -} #define DONT_ALLOW_INSTANTIATION virtual void pureVirtualFunctionPlaceHolder() = 0; #define ALLOW_INSTANTIATION virtual void pureVirtualFunctionPlaceHolder() override { }; @@ -65,6 +61,8 @@ namespace render { class MeshProxyList; +class RenderableEntityInterface; + /// EntityItem class this is the base class for all entity types. It handles the basic properties and functionality available /// to all other entity types. In particular: postion, size, rotation, age, lifetime, velocity, gravity. You can not instantiate @@ -83,6 +81,8 @@ public: EntityItem(const EntityItemID& entityItemID); virtual ~EntityItem(); + virtual RenderableEntityInterface* getRenderableInterface() { return nullptr; } + inline EntityItemPointer getThisPointer() const { return std::static_pointer_cast(std::const_pointer_cast(shared_from_this())); } @@ -150,13 +150,6 @@ public: EntityPropertyFlags& propertyFlags, bool overwriteLocalData, bool& somethingChanged) { somethingChanged = false; return 0; } - - virtual bool addToScene(EntityItemPointer self, const render::ScenePointer& scene, - render::Transaction& transaction) { return false; } // by default entity items don't add to scene - virtual void removeFromScene(EntityItemPointer self, const render::ScenePointer& scene, - render::Transaction& transaction) { } // by default entity items don't add to scene - virtual void render(RenderArgs* args) { } // by default entity items don't know how to render - static int expectedBytes(); static void adjustEditPacketForClockSkew(QByteArray& buffer, qint64 clockSkew); diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 2c37b8679d..11694c4cea 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -554,7 +554,7 @@ public: }; -bool EntityTree::findNearPointOperation(OctreeElementPointer element, void* extraData) { +bool EntityTree::findNearPointOperation(const OctreeElementPointer& element, void* extraData) { FindNearPointArgs* args = static_cast(extraData); EntityTreeElementPointer entityTreeElement = std::static_pointer_cast(element); @@ -589,7 +589,7 @@ bool EntityTree::findNearPointOperation(OctreeElementPointer element, void* extr return false; } -bool findRayIntersectionOp(OctreeElementPointer element, void* extraData) { +bool findRayIntersectionOp(const OctreeElementPointer& element, void* extraData) { RayArgs* args = static_cast(extraData); bool keepSearching = true; EntityTreeElementPointer entityTreeElementPointer = std::dynamic_pointer_cast(element); @@ -625,7 +625,7 @@ bool EntityTree::findRayIntersection(const glm::vec3& origin, const glm::vec3& d } -EntityItemPointer EntityTree::findClosestEntity(glm::vec3 position, float targetRadius) { +EntityItemPointer EntityTree::findClosestEntity(const glm::vec3& position, float targetRadius) { FindNearPointArgs args = { position, targetRadius, false, NULL, FLT_MAX }; withReadLock([&] { // NOTE: This should use recursion, since this is a spatial operation @@ -642,7 +642,7 @@ public: }; -bool EntityTree::findInSphereOperation(OctreeElementPointer element, void* extraData) { +bool EntityTree::findInSphereOperation(const OctreeElementPointer& element, void* extraData) { FindAllNearPointArgs* args = static_cast(extraData); glm::vec3 penetration; bool sphereIntersection = element->getAACube().findSpherePenetration(args->position, args->targetRadius, penetration); @@ -678,7 +678,7 @@ public: QVector _foundEntities; }; -bool EntityTree::findInCubeOperation(OctreeElementPointer element, void* extraData) { +bool EntityTree::findInCubeOperation(const OctreeElementPointer& element, void* extraData) { FindEntitiesInCubeArgs* args = static_cast(extraData); if (element->getAACube().touches(args->_cube)) { EntityTreeElementPointer entityTreeElement = std::static_pointer_cast(element); @@ -707,7 +707,7 @@ public: QVector _foundEntities; }; -bool EntityTree::findInBoxOperation(OctreeElementPointer element, void* extraData) { +bool EntityTree::findInBoxOperation(const OctreeElementPointer& element, void* extraData) { FindEntitiesInBoxArgs* args = static_cast(extraData); if (element->getAACube().touches(args->_box)) { EntityTreeElementPointer entityTreeElement = std::static_pointer_cast(element); @@ -732,7 +732,7 @@ public: QVector entities; }; -bool EntityTree::findInFrustumOperation(OctreeElementPointer element, void* extraData) { +bool EntityTree::findInFrustumOperation(const OctreeElementPointer& element, void* extraData) { FindInFrustumArgs* args = static_cast(extraData); if (element->isInView(args->frustum)) { EntityTreeElementPointer entityTreeElement = std::static_pointer_cast(element); @@ -1527,15 +1527,15 @@ void EntityTree::debugDumpMap() { class ContentsDimensionOperator : public RecurseOctreeOperator { public: - virtual bool preRecursion(OctreeElementPointer element) override; - virtual bool postRecursion(OctreeElementPointer element) override { return true; } + virtual bool preRecursion(const OctreeElementPointer& element) override; + virtual bool postRecursion(const OctreeElementPointer& element) override { return true; } glm::vec3 getDimensions() const { return _contentExtents.size(); } float getLargestDimension() const { return _contentExtents.largestDimension(); } private: Extents _contentExtents; }; -bool ContentsDimensionOperator::preRecursion(OctreeElementPointer element) { +bool ContentsDimensionOperator::preRecursion(const OctreeElementPointer& element) { EntityTreeElementPointer entityTreeElement = std::static_pointer_cast(element); entityTreeElement->expandExtentsToContents(_contentExtents); return true; @@ -1555,11 +1555,11 @@ float EntityTree::getContentsLargestDimension() { class DebugOperator : public RecurseOctreeOperator { public: - virtual bool preRecursion(OctreeElementPointer element) override; - virtual bool postRecursion(OctreeElementPointer element) override { return true; } + virtual bool preRecursion(const OctreeElementPointer& element) override; + virtual bool postRecursion(const OctreeElementPointer& element) override { return true; } }; -bool DebugOperator::preRecursion(OctreeElementPointer element) { +bool DebugOperator::preRecursion(const OctreeElementPointer& element) { EntityTreeElementPointer entityTreeElement = std::static_pointer_cast(element); qCDebug(entities) << "EntityTreeElement [" << entityTreeElement.get() << "]"; entityTreeElement->debugDump(); @@ -1573,11 +1573,11 @@ void EntityTree::dumpTree() { class PruneOperator : public RecurseOctreeOperator { public: - virtual bool preRecursion(OctreeElementPointer element) override { return true; } - virtual bool postRecursion(OctreeElementPointer element) override; + virtual bool preRecursion(const OctreeElementPointer& element) override { return true; } + virtual bool postRecursion(const OctreeElementPointer& element) override; }; -bool PruneOperator::postRecursion(OctreeElementPointer element) { +bool PruneOperator::postRecursion(const OctreeElementPointer& element) { EntityTreeElementPointer entityTreeElement = std::static_pointer_cast(element); entityTreeElement->pruneChildren(); return true; @@ -1639,6 +1639,7 @@ QVector EntityTree::sendEntities(EntityEditPacketSender* packetSen // If this is called repeatedly (e.g., multiple pastes with the same data), the new elements will clash unless we // use new identifiers. We need to keep a map so that we can map parent identifiers correctly. QHash map; + args.map = ↦ withReadLock([&] { recurseTreeWithOperation(sendEntitiesOperation, &args); @@ -1692,7 +1693,7 @@ QVector EntityTree::sendEntities(EntityEditPacketSender* packetSen return map.values().toVector(); } -bool EntityTree::sendEntitiesOperation(OctreeElementPointer element, void* extraData) { +bool EntityTree::sendEntitiesOperation(const OctreeElementPointer& element, void* extraData) { SendEntitiesOperationArgs* args = static_cast(extraData); EntityTreeElementPointer entityTreeElement = std::static_pointer_cast(element); diff --git a/libraries/entities/src/EntityTree.h b/libraries/entities/src/EntityTree.h index 00ded171db..8e3c9f5412 100644 --- a/libraries/entities/src/EntityTree.h +++ b/libraries/entities/src/EntityTree.h @@ -136,7 +136,7 @@ public: /// \param position point of query in world-frame (meters) /// \param targetRadius radius of query (meters) - EntityItemPointer findClosestEntity(glm::vec3 position, float targetRadius); + EntityItemPointer findClosestEntity(const glm::vec3& position, float targetRadius); EntityItemPointer findEntityByID(const QUuid& id); EntityItemPointer findEntityByEntityItemID(const EntityItemID& entityID); virtual SpatiallyNestablePointer findByID(const QUuid& id) override { return findEntityByID(id); } @@ -294,12 +294,12 @@ protected: bool updateEntityWithElement(EntityItemPointer entity, const EntityItemProperties& properties, EntityTreeElementPointer containingElement, const SharedNodePointer& senderNode = SharedNodePointer(nullptr)); - static bool findNearPointOperation(OctreeElementPointer element, void* extraData); - static bool findInSphereOperation(OctreeElementPointer element, void* extraData); - static bool findInCubeOperation(OctreeElementPointer element, void* extraData); - static bool findInBoxOperation(OctreeElementPointer element, void* extraData); - static bool findInFrustumOperation(OctreeElementPointer element, void* extraData); - static bool sendEntitiesOperation(OctreeElementPointer element, void* extraData); + static bool findNearPointOperation(const OctreeElementPointer& element, void* extraData); + static bool findInSphereOperation(const OctreeElementPointer& element, void* extraData); + static bool findInCubeOperation(const OctreeElementPointer& element, void* extraData); + static bool findInBoxOperation(const OctreeElementPointer& element, void* extraData); + static bool findInFrustumOperation(const OctreeElementPointer& element, void* extraData); + static bool sendEntitiesOperation(const OctreeElementPointer& element, void* extraData); static void bumpTimestamp(EntityItemProperties& properties); void notifyNewlyCreatedEntity(const EntityItem& newEntity, const SharedNodePointer& senderNode); diff --git a/libraries/entities/src/EntityTypes.h b/libraries/entities/src/EntityTypes.h index fd09d4e67b..316bf2b813 100644 --- a/libraries/entities/src/EntityTypes.h +++ b/libraries/entities/src/EntityTypes.h @@ -17,8 +17,6 @@ #include #include -#include // for RenderArgs - #include "EntitiesLogging.h" class EntityItem; diff --git a/libraries/entities/src/MovingEntitiesOperator.cpp b/libraries/entities/src/MovingEntitiesOperator.cpp index 0002ebb570..ab97c67aa2 100644 --- a/libraries/entities/src/MovingEntitiesOperator.cpp +++ b/libraries/entities/src/MovingEntitiesOperator.cpp @@ -109,7 +109,7 @@ void MovingEntitiesOperator::addEntityToMoveList(EntityItemPointer entity, const } // does this entity tree element contain the old entity -bool MovingEntitiesOperator::shouldRecurseSubTree(OctreeElementPointer element) { +bool MovingEntitiesOperator::shouldRecurseSubTree(const OctreeElementPointer& element) { bool containsEntity = false; // If we don't have an old entity, then we don't contain the entity, otherwise @@ -141,7 +141,7 @@ bool MovingEntitiesOperator::shouldRecurseSubTree(OctreeElementPointer element) return containsEntity; } -bool MovingEntitiesOperator::preRecursion(OctreeElementPointer element) { +bool MovingEntitiesOperator::preRecursion(const OctreeElementPointer& element) { EntityTreeElementPointer entityTreeElement = std::static_pointer_cast(element); // In Pre-recursion, we're generally deciding whether or not we want to recurse this @@ -221,7 +221,7 @@ bool MovingEntitiesOperator::preRecursion(OctreeElementPointer element) { return keepSearching; // if we haven't yet found it, keep looking } -bool MovingEntitiesOperator::postRecursion(OctreeElementPointer element) { +bool MovingEntitiesOperator::postRecursion(const OctreeElementPointer& element) { // Post-recursion is the unwinding process. For this operation, while we // unwind we want to mark the path as being dirty if we changed it below. // We might have two paths, one for the old entity and one for the new entity. @@ -261,7 +261,7 @@ bool MovingEntitiesOperator::postRecursion(OctreeElementPointer element) { return keepSearching; // if we haven't yet found it, keep looking } -OctreeElementPointer MovingEntitiesOperator::possiblyCreateChildAt(OctreeElementPointer element, int childIndex) { +OctreeElementPointer MovingEntitiesOperator::possiblyCreateChildAt(const OctreeElementPointer& element, int childIndex) { // If we're getting called, it's because there was no child element at this index while recursing. // We only care if this happens while still searching for the new entity locations. if (_foundNewCount < _lookingCount) { diff --git a/libraries/entities/src/MovingEntitiesOperator.h b/libraries/entities/src/MovingEntitiesOperator.h index 27ecb340a8..fc6ccf2513 100644 --- a/libraries/entities/src/MovingEntitiesOperator.h +++ b/libraries/entities/src/MovingEntitiesOperator.h @@ -38,9 +38,9 @@ public: ~MovingEntitiesOperator(); void addEntityToMoveList(EntityItemPointer entity, const AACube& newCube); - virtual bool preRecursion(OctreeElementPointer element) override; - virtual bool postRecursion(OctreeElementPointer element) override; - virtual OctreeElementPointer possiblyCreateChildAt(OctreeElementPointer element, int childIndex) override; + virtual bool preRecursion(const OctreeElementPointer& element) override; + virtual bool postRecursion(const OctreeElementPointer& element) override; + virtual OctreeElementPointer possiblyCreateChildAt(const OctreeElementPointer& element, int childIndex) override; bool hasMovingEntities() const { return _entitiesToMove.size() > 0; } private: EntityTreePointer _tree; @@ -49,7 +49,7 @@ private: int _foundOldCount; int _foundNewCount; int _lookingCount; - bool shouldRecurseSubTree(OctreeElementPointer element); + bool shouldRecurseSubTree(const OctreeElementPointer& element); bool _wantDebug; }; diff --git a/libraries/entities/src/RecurseOctreeToMapOperator.cpp b/libraries/entities/src/RecurseOctreeToMapOperator.cpp index e930d5ef5f..217dc0db12 100644 --- a/libraries/entities/src/RecurseOctreeToMapOperator.cpp +++ b/libraries/entities/src/RecurseOctreeToMapOperator.cpp @@ -14,7 +14,7 @@ #include "EntityItemProperties.h" RecurseOctreeToMapOperator::RecurseOctreeToMapOperator(QVariantMap& map, - OctreeElementPointer top, + const OctreeElementPointer& top, QScriptEngine* engine, bool skipDefaultValues, bool skipThoseWithBadParents) : @@ -34,14 +34,14 @@ RecurseOctreeToMapOperator::RecurseOctreeToMapOperator(QVariantMap& map, } }; -bool RecurseOctreeToMapOperator::preRecursion(OctreeElementPointer element) { +bool RecurseOctreeToMapOperator::preRecursion(const OctreeElementPointer& element) { if (element == _top) { _withinTop = true; } return true; } -bool RecurseOctreeToMapOperator::postRecursion(OctreeElementPointer element) { +bool RecurseOctreeToMapOperator::postRecursion(const OctreeElementPointer& element) { EntityItemProperties defaultProperties; diff --git a/libraries/entities/src/RecurseOctreeToMapOperator.h b/libraries/entities/src/RecurseOctreeToMapOperator.h index dbf8dbd15b..c661badd88 100644 --- a/libraries/entities/src/RecurseOctreeToMapOperator.h +++ b/libraries/entities/src/RecurseOctreeToMapOperator.h @@ -13,10 +13,10 @@ class RecurseOctreeToMapOperator : public RecurseOctreeOperator { public: - RecurseOctreeToMapOperator(QVariantMap& map, OctreeElementPointer top, QScriptEngine* engine, bool skipDefaultValues, + RecurseOctreeToMapOperator(QVariantMap& map, const OctreeElementPointer& top, QScriptEngine* engine, bool skipDefaultValues, bool skipThoseWithBadParents); - bool preRecursion(OctreeElementPointer element) override; - bool postRecursion(OctreeElementPointer element) override; + bool preRecursion(const OctreeElementPointer& element) override; + bool postRecursion(const OctreeElementPointer& element) override; private: QVariantMap& _map; OctreeElementPointer _top; diff --git a/libraries/entities/src/UpdateEntityOperator.cpp b/libraries/entities/src/UpdateEntityOperator.cpp index 84f801b059..ec6051af04 100644 --- a/libraries/entities/src/UpdateEntityOperator.cpp +++ b/libraries/entities/src/UpdateEntityOperator.cpp @@ -77,7 +77,7 @@ UpdateEntityOperator::~UpdateEntityOperator() { // does this entity tree element contain the old entity -bool UpdateEntityOperator::subTreeContainsOldEntity(OctreeElementPointer element) { +bool UpdateEntityOperator::subTreeContainsOldEntity(const OctreeElementPointer& element) { // We've found cases where the old entity might be placed in an element that is not actually the best fit // so when we're searching the tree for the old element, we use the known cube for the known containing element @@ -95,7 +95,7 @@ bool UpdateEntityOperator::subTreeContainsOldEntity(OctreeElementPointer element return elementContainsOldBox; } -bool UpdateEntityOperator::subTreeContainsNewEntity(OctreeElementPointer element) { +bool UpdateEntityOperator::subTreeContainsNewEntity(const OctreeElementPointer& element) { bool elementContainsNewBox = element->getAACube().contains(_newEntityBox); if (_wantDebug) { @@ -112,7 +112,7 @@ bool UpdateEntityOperator::subTreeContainsNewEntity(OctreeElementPointer element } -bool UpdateEntityOperator::preRecursion(OctreeElementPointer element) { +bool UpdateEntityOperator::preRecursion(const OctreeElementPointer& element) { EntityTreeElementPointer entityTreeElement = std::static_pointer_cast(element); // In Pre-recursion, we're generally deciding whether or not we want to recurse this @@ -238,7 +238,7 @@ bool UpdateEntityOperator::preRecursion(OctreeElementPointer element) { return keepSearching; // if we haven't yet found it, keep looking } -bool UpdateEntityOperator::postRecursion(OctreeElementPointer element) { +bool UpdateEntityOperator::postRecursion(const OctreeElementPointer& element) { // Post-recursion is the unwinding process. For this operation, while we // unwind we want to mark the path as being dirty if we changed it below. // We might have two paths, one for the old entity and one for the new entity. @@ -270,7 +270,7 @@ bool UpdateEntityOperator::postRecursion(OctreeElementPointer element) { return keepSearching; // if we haven't yet found it, keep looking } -OctreeElementPointer UpdateEntityOperator::possiblyCreateChildAt(OctreeElementPointer element, int childIndex) { +OctreeElementPointer UpdateEntityOperator::possiblyCreateChildAt(const OctreeElementPointer& element, int childIndex) { // If we're getting called, it's because there was no child element at this index while recursing. // We only care if this happens while still searching for the new entity location. // Check to see if diff --git a/libraries/entities/src/UpdateEntityOperator.h b/libraries/entities/src/UpdateEntityOperator.h index b33d6c6c3b..5597f93012 100644 --- a/libraries/entities/src/UpdateEntityOperator.h +++ b/libraries/entities/src/UpdateEntityOperator.h @@ -25,9 +25,9 @@ public: ~UpdateEntityOperator(); - virtual bool preRecursion(OctreeElementPointer element) override; - virtual bool postRecursion(OctreeElementPointer element) override; - virtual OctreeElementPointer possiblyCreateChildAt(OctreeElementPointer element, int childIndex) override; + virtual bool preRecursion(const OctreeElementPointer& element) override; + virtual bool postRecursion(const OctreeElementPointer& element) override; + virtual OctreeElementPointer possiblyCreateChildAt(const OctreeElementPointer& element, int childIndex) override; private: EntityTreePointer _tree; EntityItemPointer _existingEntity; @@ -45,8 +45,8 @@ private: AABox _oldEntityBox; // clamped to domain AABox _newEntityBox; // clamped to domain - bool subTreeContainsOldEntity(OctreeElementPointer element); - bool subTreeContainsNewEntity(OctreeElementPointer element); + bool subTreeContainsOldEntity(const OctreeElementPointer& element); + bool subTreeContainsNewEntity(const OctreeElementPointer& element); bool _wantDebug; }; diff --git a/libraries/fbx/CMakeLists.txt b/libraries/fbx/CMakeLists.txt index d9f4aaf03e..d9c073f213 100644 --- a/libraries/fbx/CMakeLists.txt +++ b/libraries/fbx/CMakeLists.txt @@ -1,3 +1,4 @@ set(TARGET_NAME fbx) setup_hifi_library() link_hifi_libraries(shared model networking) +include_hifi_library_headers(gpu) diff --git a/libraries/gpu-gl/src/gpu/gl/GLBackendOutput.cpp b/libraries/gpu-gl/src/gpu/gl/GLBackendOutput.cpp index 1e6691538b..5c6a18d7af 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLBackendOutput.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLBackendOutput.cpp @@ -63,11 +63,17 @@ void GLBackend::do_clearFramebuffer(const Batch& batch, size_t paramOffset) { int useScissor = batch._params[paramOffset + 0]._int; GLuint glmask = 0; + bool restoreStencilMask = false; + uint8_t cacheStencilMask = 0xFF; if (masks & Framebuffer::BUFFER_STENCIL) { glClearStencil(stencil); glmask |= GL_STENCIL_BUFFER_BIT; - // TODO: we will probably need to also check the write mask of stencil like we do - // for depth buffer, but as would say a famous Fez owner "We'll cross that bridge when we come to it" + + cacheStencilMask = _pipeline._stateCache.stencilActivation.getWriteMaskFront(); + if (cacheStencilMask != 0xFF) { + restoreStencilMask = true; + glStencilMask( 0xFF); + } } bool restoreDepthMask = false; @@ -121,6 +127,11 @@ void GLBackend::do_clearFramebuffer(const Batch& batch, size_t paramOffset) { glDisable(GL_SCISSOR_TEST); } + // Restore Stencil write mask + if (restoreStencilMask) { + glStencilMask(cacheStencilMask); + } + // Restore write mask meaning turn back off if (restoreDepthMask) { glDepthMask(GL_FALSE); diff --git a/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp b/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp index 88e5cde330..4161242a24 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp @@ -223,12 +223,21 @@ TransferJob::TransferJob(const GLTexture& parent, uint16_t sourceMip, uint16_t t // Buffering can invoke disk IO, so it should be off of the main and render threads _bufferingLambda = [=] { - _mipData = _parent._gpuObject.accessStoredMipFace(sourceMip, face)->createView(_transferSize, _transferOffset); + auto mipStorage = _parent._gpuObject.accessStoredMipFace(sourceMip, face); + if (mipStorage) { + _mipData = mipStorage->createView(_transferSize, _transferOffset); + } else { + qCWarning(gpugllogging) << "Buffering failed because mip could not be retrieved from texture " << _parent._source.c_str() ; + } }; _transferLambda = [=] { - _parent.copyMipFaceLinesFromTexture(targetMip, face, transferDimensions, lineOffset, internalFormat, format, type, _mipData->size(), _mipData->readData()); - _mipData.reset(); + if (_mipData) { + _parent.copyMipFaceLinesFromTexture(targetMip, face, transferDimensions, lineOffset, internalFormat, format, type, _mipData->size(), _mipData->readData()); + _mipData.reset(); + } else { + qCWarning(gpugllogging) << "Transfer failed because mip could not be retrieved from texture " << _parent._source.c_str(); + } }; } diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp index ad54ccc3e9..d8b3968ed8 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp @@ -123,7 +123,7 @@ GLuint GL45Texture::allocate(const Texture& texture) { glCreateTextures(getGLTextureType(texture), 1, &result); #ifdef DEBUG auto source = texture.source(); - glObjectLabel(GL_TEXTURE, result, source.length(), source.data()); + glObjectLabel(GL_TEXTURE, result, (GLsizei)source.length(), source.data()); #endif return result; } diff --git a/libraries/gpu/src/gpu/Texture.h b/libraries/gpu/src/gpu/Texture.h index 211dc7b8ce..1877b494cf 100755 --- a/libraries/gpu/src/gpu/Texture.h +++ b/libraries/gpu/src/gpu/Texture.h @@ -18,7 +18,7 @@ #include #include - +#include #include "Forward.h" #include "Resource.h" #include "Metric.h" @@ -311,6 +311,7 @@ public: class KtxStorage : public Storage { public: KtxStorage(const std::string& filename); + KtxStorage(const cache::FilePointer& file); PixelsPointer getMipFace(uint16 level, uint8 face = 0) const override; Size getMipFaceSize(uint16 level, uint8 face = 0) const override; bool isMipAvailable(uint16 level, uint8 face = 0) const override; @@ -328,6 +329,7 @@ public: mutable std::weak_ptr _cacheFile; std::string _filename; + cache::FilePointer _cacheEntry; std::atomic _minMipLevelAvailable; size_t _offsetToMinMipKV; @@ -499,6 +501,7 @@ public: void setStorage(std::unique_ptr& newStorage); void setKtxBacking(const std::string& filename); + void setKtxBacking(const cache::FilePointer& cacheEntry); // Usage is a a set of flags providing Semantic about the usage of the Texture. void setUsage(const Usage& usage) { _usage = usage; } @@ -529,8 +532,9 @@ public: // Serialize a texture into a KTX file static ktx::KTXUniquePointer serialize(const Texture& texture); + static TexturePointer build(const ktx::KTXDescriptor& descriptor); static TexturePointer unserialize(const std::string& ktxFile); - static TexturePointer unserialize(const std::string& ktxFile, const ktx::KTXDescriptor& descriptor); + static TexturePointer unserialize(const cache::FilePointer& cacheEntry); static bool evalKTXFormat(const Element& mipFormat, const Element& texelFormat, ktx::Header& header); static bool evalTextureFormat(const ktx::Header& header, Element& mipFormat, Element& texelFormat); diff --git a/libraries/gpu/src/gpu/Texture_ktx.cpp b/libraries/gpu/src/gpu/Texture_ktx.cpp index f455fde009..14fd983ec2 100644 --- a/libraries/gpu/src/gpu/Texture_ktx.cpp +++ b/libraries/gpu/src/gpu/Texture_ktx.cpp @@ -154,6 +154,10 @@ struct IrradianceKTXPayload { }; const std::string IrradianceKTXPayload::KEY{ "hifi.irradianceSH" }; +KtxStorage::KtxStorage(const cache::FilePointer& cacheEntry) : KtxStorage(cacheEntry->getFilepath()) { + _cacheEntry = cacheEntry; +} + KtxStorage::KtxStorage(const std::string& filename) : _filename(filename) { { // We are doing a lot of work here just to get descriptor data @@ -295,20 +299,35 @@ void KtxStorage::assignMipFaceData(uint16 level, uint8 face, const storage::Stor throw std::runtime_error("Invalid call"); } +bool validKtx(const std::string& filename) { + ktx::StoragePointer storage { new storage::FileStorage(filename.c_str()) }; + auto ktxPointer = ktx::KTX::create(storage); + if (!ktxPointer) { + return false; + } + return true; +} + void Texture::setKtxBacking(const std::string& filename) { // Check the KTX file for validity before using it as backing storage - { - ktx::StoragePointer storage { new storage::FileStorage(filename.c_str()) }; - auto ktxPointer = ktx::KTX::create(storage); - if (!ktxPointer) { - return; - } + if (!validKtx(filename)) { + return; } auto newBacking = std::unique_ptr(new KtxStorage(filename)); setStorage(newBacking); } +void Texture::setKtxBacking(const cache::FilePointer& cacheEntry) { + // Check the KTX file for validity before using it as backing storage + if (!validKtx(cacheEntry->getFilepath())) { + return; + } + + auto newBacking = std::unique_ptr(new KtxStorage(cacheEntry)); + setStorage(newBacking); +} + ktx::KTXUniquePointer Texture::serialize(const Texture& texture) { ktx::Header header; @@ -442,21 +461,10 @@ ktx::KTXUniquePointer Texture::serialize(const Texture& texture) { return ktxBuffer; } -TexturePointer Texture::unserialize(const std::string& ktxfile) { - std::unique_ptr ktxPointer = ktx::KTX::create(std::make_shared(ktxfile.c_str())); - if (!ktxPointer) { - return nullptr; - } - - ktx::KTXDescriptor descriptor { ktxPointer->toDescriptor() }; - return unserialize(ktxfile, ktxPointer->toDescriptor()); -} - -TexturePointer Texture::unserialize(const std::string& ktxfile, const ktx::KTXDescriptor& descriptor) { - const auto& header = descriptor.header; - +TexturePointer Texture::build(const ktx::KTXDescriptor& descriptor) { Format mipFormat = Format::COLOR_BGRA_32; Format texelFormat = Format::COLOR_SRGBA_32; + const auto& header = descriptor.header; if (!Texture::evalTextureFormat(header, mipFormat, texelFormat)) { return nullptr; @@ -485,20 +493,19 @@ TexturePointer Texture::unserialize(const std::string& ktxfile, const ktx::KTXDe } auto texture = create(gpuktxKeyValue._usageType, - type, - texelFormat, - header.getPixelWidth(), - header.getPixelHeight(), - header.getPixelDepth(), - 1, // num Samples - header.getNumberOfSlices(), - header.getNumberOfLevels(), - gpuktxKeyValue._samplerDesc); + type, + texelFormat, + header.getPixelWidth(), + header.getPixelHeight(), + header.getPixelDepth(), + 1, // num Samples + header.getNumberOfSlices(), + header.getNumberOfLevels(), + gpuktxKeyValue._samplerDesc); texture->setUsage(gpuktxKeyValue._usage); // Assing the mips availables texture->setStoredMipFormat(mipFormat); - texture->setKtxBacking(ktxfile); IrradianceKTXPayload irradianceKtxKeyValue; if (IrradianceKTXPayload::findInKeyValues(descriptor.keyValues, irradianceKtxKeyValue)) { @@ -508,6 +515,36 @@ TexturePointer Texture::unserialize(const std::string& ktxfile, const ktx::KTXDe return texture; } + + +TexturePointer Texture::unserialize(const cache::FilePointer& cacheEntry) { + std::unique_ptr ktxPointer = ktx::KTX::create(std::make_shared(cacheEntry->getFilepath().c_str())); + if (!ktxPointer) { + return nullptr; + } + + auto texture = build(ktxPointer->toDescriptor()); + if (texture) { + texture->setKtxBacking(cacheEntry); + } + + return texture; +} + +TexturePointer Texture::unserialize(const std::string& ktxfile) { + std::unique_ptr ktxPointer = ktx::KTX::create(std::make_shared(ktxfile.c_str())); + if (!ktxPointer) { + return nullptr; + } + + auto texture = build(ktxPointer->toDescriptor()); + if (texture) { + texture->setKtxBacking(ktxfile); + } + + return texture; +} + bool Texture::evalKTXFormat(const Element& mipFormat, const Element& texelFormat, ktx::Header& header) { if (texelFormat == Format::COLOR_RGBA_32 && mipFormat == Format::COLOR_BGRA_32) { header.setUncompressed(ktx::GLType::UNSIGNED_BYTE, 1, ktx::GLFormat::BGRA, ktx::GLInternalFormat::RGBA8, ktx::GLBaseInternalFormat::RGBA); diff --git a/libraries/ktx/CMakeLists.txt b/libraries/ktx/CMakeLists.txt index 404660b247..967ea71eb6 100644 --- a/libraries/ktx/CMakeLists.txt +++ b/libraries/ktx/CMakeLists.txt @@ -1,3 +1,3 @@ set(TARGET_NAME ktx) setup_hifi_library() -link_hifi_libraries() \ No newline at end of file +include_hifi_library_headers(shared) \ No newline at end of file diff --git a/libraries/model-networking/CMakeLists.txt b/libraries/model-networking/CMakeLists.txt index f7175bc533..db5563d7ea 100644 --- a/libraries/model-networking/CMakeLists.txt +++ b/libraries/model-networking/CMakeLists.txt @@ -1,4 +1,4 @@ set(TARGET_NAME model-networking) setup_hifi_library() link_hifi_libraries(shared networking model fbx ktx image) - +include_hifi_library_headers(gpu) diff --git a/libraries/model-networking/src/model-networking/KTXCache.h b/libraries/model-networking/src/model-networking/KTXCache.h index 5617019c52..a919c88bd7 100644 --- a/libraries/model-networking/src/model-networking/KTXCache.h +++ b/libraries/model-networking/src/model-networking/KTXCache.h @@ -14,7 +14,7 @@ #include -#include +#include namespace ktx { class KTX; diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index 8683d56b6b..15eb4be839 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -612,9 +612,10 @@ void NetworkTexture::maybeHandleFinishedInitialLoad() { if (!texture) { KTXFilePointer ktxFile = textureCache->_ktxCache.getFile(hash); if (ktxFile) { - texture = gpu::Texture::unserialize(ktxFile->getFilepath()); + texture = gpu::Texture::unserialize(ktxFile); if (texture) { texture = textureCache->cacheTextureByHash(hash, texture); + _file = ktxFile; } } } @@ -644,8 +645,8 @@ void NetworkTexture::maybeHandleFinishedInitialLoad() { auto newKtxDescriptor = memKtx->toDescriptor(); - texture = gpu::Texture::unserialize(_file->getFilepath(), newKtxDescriptor); - texture->setKtxBacking(file->getFilepath()); + texture = gpu::Texture::build(newKtxDescriptor); + texture->setKtxBacking(file); texture->setSource(filename); auto& images = _originalKtxDescriptor->images; @@ -796,7 +797,7 @@ void ImageReader::read() { if (!texture) { KTXFilePointer ktxFile = textureCache->_ktxCache.getFile(hash); if (ktxFile) { - texture = gpu::Texture::unserialize(ktxFile->getFilepath()); + texture = gpu::Texture::unserialize(ktxFile); if (texture) { texture = textureCache->cacheTextureByHash(hash, texture); } else { @@ -848,7 +849,7 @@ void ImageReader::read() { if (!networkTexture->_file) { qCWarning(modelnetworking) << _url << "file cache failed"; } else { - texture->setKtxBacking(networkTexture->_file->getFilepath()); + texture->setKtxBacking(networkTexture->_file); } } else { qCWarning(modelnetworking) << "Unable to serialize texture to KTX " << _url; diff --git a/libraries/model/src/model/Geometry.h b/libraries/model/src/model/Geometry.h index 2375944f04..a3198eed26 100755 --- a/libraries/model/src/model/Geometry.h +++ b/libraries/model/src/model/Geometry.h @@ -13,10 +13,10 @@ #include -#include "AABox.h" +#include -#include "gpu/Resource.h" -#include "gpu/Stream.h" +#include +#include namespace model { typedef gpu::BufferView::Index Index; diff --git a/libraries/networking/src/LimitedNodeList.cpp b/libraries/networking/src/LimitedNodeList.cpp index 93ae941f1e..f9baff0daf 100644 --- a/libraries/networking/src/LimitedNodeList.cpp +++ b/libraries/networking/src/LimitedNodeList.cpp @@ -584,7 +584,7 @@ SharedNodePointer LimitedNodeList::addOrUpdateNode(const QUuid& uuid, NodeType_t return matchingNode; } else { // we didn't have this node, so add them - Node* newNode = new Node(uuid, nodeType, publicSocket, localSocket, permissions, connectionSecret, this); + Node* newNode = new Node(uuid, nodeType, publicSocket, localSocket, permissions, connectionSecret); if (nodeType == NodeType::AudioMixer) { LimitedNodeList::flagTimeForConnectionStep(LimitedNodeList::AddedAudioMixer); @@ -617,24 +617,28 @@ SharedNodePointer LimitedNodeList::addOrUpdateNode(const QUuid& uuid, NodeType_t } // insert the new node and release our read lock - _nodeHash.insert(UUIDNodePair(newNode->getUUID(), newNodePointer)); + _nodeHash.emplace(newNode->getUUID(), newNodePointer); readLocker.unlock(); qCDebug(networking) << "Added" << *newNode; + auto weakPtr = newNodePointer.toWeakRef(); // We don't want the lambdas to hold a strong ref + emit nodeAdded(newNodePointer); if (newNodePointer->getActiveSocket()) { emit nodeActivated(newNodePointer); } else { - connect(newNodePointer.data(), &NetworkPeer::socketActivated, this, [=] { - emit nodeActivated(newNodePointer); - disconnect(newNodePointer.data(), &NetworkPeer::socketActivated, this, 0); + connect(newNodePointer.data(), &NetworkPeer::socketActivated, this, [this, weakPtr] { + auto sharedPtr = weakPtr.lock(); + if (sharedPtr) { + emit nodeActivated(sharedPtr); + disconnect(sharedPtr.data(), &NetworkPeer::socketActivated, this, 0); + } }); } // Signal when a socket changes, so we can start the hole punch over. - auto weakPtr = newNodePointer.toWeakRef(); // We don't want the lambda to hold a strong ref - connect(newNodePointer.data(), &NetworkPeer::socketUpdated, this, [=] { + connect(newNodePointer.data(), &NetworkPeer::socketUpdated, this, [this, weakPtr] { emit nodeSocketUpdated(weakPtr); }); diff --git a/libraries/networking/src/Node.h b/libraries/networking/src/Node.h index 1092fcc7fa..1bb3a0cdc8 100644 --- a/libraries/networking/src/Node.h +++ b/libraries/networking/src/Node.h @@ -40,7 +40,7 @@ public: Node(const QUuid& uuid, NodeType_t type, const HifiSockAddr& publicSocket, const HifiSockAddr& localSocket, const NodePermissions& permissions, const QUuid& connectionSecret = QUuid(), - QObject* parent = 0); + QObject* parent = nullptr); bool operator==(const Node& otherNode) const { return _uuid == otherNode._uuid; } bool operator!=(const Node& otherNode) const { return !(*this == otherNode); } diff --git a/libraries/networking/src/ReceivedPacketProcessor.h b/libraries/networking/src/ReceivedPacketProcessor.h index dd790a9b3d..f71abce1f1 100644 --- a/libraries/networking/src/ReceivedPacketProcessor.h +++ b/libraries/networking/src/ReceivedPacketProcessor.h @@ -20,6 +20,8 @@ class ReceivedPacketProcessor : public GenericThread { Q_OBJECT public: + static const uint64_t MAX_WAIT_TIME { 100 }; // Max wait time in ms + ReceivedPacketProcessor(); /// Add packet from network receive thread to the processing queue. @@ -63,8 +65,8 @@ protected: /// Implements generic processing behavior for this thread. virtual bool process() override; - /// Determines the timeout of the wait when there are no packets to process. Default value means no timeout - virtual unsigned long getMaxWait() const { return ULONG_MAX; } + /// Determines the timeout of the wait when there are no packets to process. Default value is 100ms to allow for regular event processing. + virtual uint32_t getMaxWait() const { return MAX_WAIT_TIME; } /// Override to do work before the packets processing loop. Default does nothing. virtual void preProcess() { } diff --git a/libraries/octree/src/DirtyOctreeElementOperator.cpp b/libraries/octree/src/DirtyOctreeElementOperator.cpp index 0f7e6d9f04..ed8d26cf72 100644 --- a/libraries/octree/src/DirtyOctreeElementOperator.cpp +++ b/libraries/octree/src/DirtyOctreeElementOperator.cpp @@ -11,20 +11,20 @@ #include "DirtyOctreeElementOperator.h" -DirtyOctreeElementOperator::DirtyOctreeElementOperator(OctreeElementPointer element) +DirtyOctreeElementOperator::DirtyOctreeElementOperator(const OctreeElementPointer& element) : _element(element) { assert(_element.get()); _point = _element->getAACube().calcCenter(); } -bool DirtyOctreeElementOperator::preRecursion(OctreeElementPointer element) { +bool DirtyOctreeElementOperator::preRecursion(const OctreeElementPointer& element) { if (element == _element) { return false; } return element->getAACube().contains(_point); } -bool DirtyOctreeElementOperator::postRecursion(OctreeElementPointer element) { +bool DirtyOctreeElementOperator::postRecursion(const OctreeElementPointer& element) { element->markWithChangedTime(); return true; } diff --git a/libraries/octree/src/DirtyOctreeElementOperator.h b/libraries/octree/src/DirtyOctreeElementOperator.h index a8d00a13f0..c28dbbf324 100644 --- a/libraries/octree/src/DirtyOctreeElementOperator.h +++ b/libraries/octree/src/DirtyOctreeElementOperator.h @@ -16,12 +16,12 @@ class DirtyOctreeElementOperator : public RecurseOctreeOperator { public: - DirtyOctreeElementOperator(OctreeElementPointer element); + DirtyOctreeElementOperator(const OctreeElementPointer& element); ~DirtyOctreeElementOperator() {} - virtual bool preRecursion(OctreeElementPointer element) override; - virtual bool postRecursion(OctreeElementPointer element) override; + virtual bool preRecursion(const OctreeElementPointer& element) override; + virtual bool postRecursion(const OctreeElementPointer& element) override; private: glm::vec3 _point; OctreeElementPointer _element; diff --git a/libraries/octree/src/JurisdictionListener.cpp b/libraries/octree/src/JurisdictionListener.cpp index dbbd146f4e..76c5069006 100644 --- a/libraries/octree/src/JurisdictionListener.cpp +++ b/libraries/octree/src/JurisdictionListener.cpp @@ -35,14 +35,13 @@ void JurisdictionListener::nodeKilled(SharedNodePointer node) { } bool JurisdictionListener::queueJurisdictionRequest() { - auto packet = NLPacket::create(PacketType::JurisdictionRequest, 0); - auto nodeList = DependencyManager::get(); int nodeCount = 0; nodeList->eachNode([&](const SharedNodePointer& node) { if (node->getType() == getNodeType() && node->getActiveSocket()) { + auto packet = NLPacket::create(PacketType::JurisdictionRequest, 0); _packetSender.queuePacketForSending(node, std::move(packet)); nodeCount++; } diff --git a/libraries/octree/src/JurisdictionSender.cpp b/libraries/octree/src/JurisdictionSender.cpp index ed3d59cebc..dfe1a6d872 100644 --- a/libraries/octree/src/JurisdictionSender.cpp +++ b/libraries/octree/src/JurisdictionSender.cpp @@ -41,8 +41,6 @@ bool JurisdictionSender::process() { // call our ReceivedPacketProcessor base class process so we'll get any pending packets if (continueProcessing && (continueProcessing = ReceivedPacketProcessor::process())) { - auto packet = (_jurisdictionMap) ? _jurisdictionMap->packIntoPacket() - : JurisdictionMap::packEmptyJurisdictionIntoMessage(getNodeType()); int nodeCount = 0; lockRequestingNodes(); @@ -53,6 +51,8 @@ bool JurisdictionSender::process() { SharedNodePointer node = DependencyManager::get()->nodeWithUUID(nodeUUID); if (node && node->getActiveSocket()) { + auto packet = (_jurisdictionMap) ? _jurisdictionMap->packIntoPacket() + : JurisdictionMap::packEmptyJurisdictionIntoMessage(getNodeType()); _packetSender.queuePacketForSending(node, std::move(packet)); nodeCount++; } diff --git a/libraries/octree/src/Octree.cpp b/libraries/octree/src/Octree.cpp index dfc6195f95..180f25f106 100644 --- a/libraries/octree/src/Octree.cpp +++ b/libraries/octree/src/Octree.cpp @@ -74,7 +74,7 @@ Octree::~Octree() { // non-sorted array // returns -1 if size exceeded // originalIndexArray is optional -int insertOctreeElementIntoSortedArrays(OctreeElementPointer value, float key, int originalIndex, +int insertOctreeElementIntoSortedArrays(const OctreeElementPointer& value, float key, int originalIndex, OctreeElementPointer* valueArray, float* keyArray, int* originalIndexArray, int currentCount, int maxCount) { @@ -108,17 +108,17 @@ int insertOctreeElementIntoSortedArrays(OctreeElementPointer value, float key, i // Recurses voxel tree calling the RecurseOctreeOperation function for each element. // stops recursion if operation function returns false. -void Octree::recurseTreeWithOperation(RecurseOctreeOperation operation, void* extraData) { +void Octree::recurseTreeWithOperation(const RecurseOctreeOperation& operation, void* extraData) { recurseElementWithOperation(_rootElement, operation, extraData); } // Recurses voxel tree calling the RecurseOctreePostFixOperation function for each element in post-fix order. -void Octree::recurseTreeWithPostOperation(RecurseOctreeOperation operation, void* extraData) { +void Octree::recurseTreeWithPostOperation(const RecurseOctreeOperation& operation, void* extraData) { recurseElementWithPostOperation(_rootElement, operation, extraData); } // Recurses voxel element with an operation function -void Octree::recurseElementWithOperation(OctreeElementPointer element, RecurseOctreeOperation operation, void* extraData, +void Octree::recurseElementWithOperation(const OctreeElementPointer& element, const RecurseOctreeOperation& operation, void* extraData, int recursionCount) { if (recursionCount > DANGEROUSLY_DEEP_RECURSION) { static QString repeatedMessage @@ -140,7 +140,7 @@ void Octree::recurseElementWithOperation(OctreeElementPointer element, RecurseOc } // Recurses voxel element with an operation function -void Octree::recurseElementWithPostOperation(OctreeElementPointer element, RecurseOctreeOperation operation, +void Octree::recurseElementWithPostOperation(const OctreeElementPointer& element, const RecurseOctreeOperation& operation, void* extraData, int recursionCount) { if (recursionCount > DANGEROUSLY_DEEP_RECURSION) { static QString repeatedMessage @@ -162,14 +162,14 @@ void Octree::recurseElementWithPostOperation(OctreeElementPointer element, Recur // Recurses voxel tree calling the RecurseOctreeOperation function for each element. // stops recursion if operation function returns false. -void Octree::recurseTreeWithOperationDistanceSorted(RecurseOctreeOperation operation, +void Octree::recurseTreeWithOperationDistanceSorted(const RecurseOctreeOperation& operation, const glm::vec3& point, void* extraData) { recurseElementWithOperationDistanceSorted(_rootElement, operation, point, extraData); } // Recurses voxel element with an operation function -void Octree::recurseElementWithOperationDistanceSorted(OctreeElementPointer element, RecurseOctreeOperation operation, +void Octree::recurseElementWithOperationDistanceSorted(const OctreeElementPointer& element, const RecurseOctreeOperation& operation, const glm::vec3& point, void* extraData, int recursionCount) { if (recursionCount > DANGEROUSLY_DEEP_RECURSION) { @@ -212,7 +212,7 @@ void Octree::recurseTreeWithOperator(RecurseOctreeOperator* operatorObject) { recurseElementWithOperator(_rootElement, operatorObject); } -bool Octree::recurseElementWithOperator(OctreeElementPointer element, +bool Octree::recurseElementWithOperator(const OctreeElementPointer& element, RecurseOctreeOperator* operatorObject, int recursionCount) { if (recursionCount > DANGEROUSLY_DEEP_RECURSION) { static QString repeatedMessage @@ -245,7 +245,7 @@ bool Octree::recurseElementWithOperator(OctreeElementPointer element, } -OctreeElementPointer Octree::nodeForOctalCode(OctreeElementPointer ancestorElement, const unsigned char* needleCode, +OctreeElementPointer Octree::nodeForOctalCode(const OctreeElementPointer& ancestorElement, const unsigned char* needleCode, OctreeElementPointer* parentOfFoundElement) const { // special case for NULL octcode if (!needleCode) { @@ -281,7 +281,7 @@ OctreeElementPointer Octree::nodeForOctalCode(OctreeElementPointer ancestorEleme } // returns the element created! -OctreeElementPointer Octree::createMissingElement(OctreeElementPointer lastParentElement, +OctreeElementPointer Octree::createMissingElement(const OctreeElementPointer& lastParentElement, const unsigned char* codeToReach, int recursionCount) { if (recursionCount > DANGEROUSLY_DEEP_RECURSION) { @@ -311,7 +311,7 @@ OctreeElementPointer Octree::createMissingElement(OctreeElementPointer lastParen } } -int Octree::readElementData(OctreeElementPointer destinationElement, const unsigned char* nodeData, int bytesAvailable, +int Octree::readElementData(const OctreeElementPointer& destinationElement, const unsigned char* nodeData, int bytesAvailable, ReadBitstreamToTreeParams& args) { int bytesLeftToRead = bytesAvailable; @@ -529,7 +529,7 @@ void Octree::deleteOctalCodeFromTree(const unsigned char* codeBuffer, bool colla }); } -void Octree::deleteOctalCodeFromTreeRecursion(OctreeElementPointer element, void* extraData) { +void Octree::deleteOctalCodeFromTreeRecursion(const OctreeElementPointer& element, void* extraData) { DeleteOctalCodeFromTreeArgs* args = (DeleteOctalCodeFromTreeArgs*)extraData; int lengthOfElementCode = numberOfThreeBitSectionsInCode(element->getOctalCode()); @@ -703,7 +703,7 @@ public: void* penetratedObject; /// the type is defined by the type of Octree, the caller is assumed to know the type }; -bool findSpherePenetrationOp(OctreeElementPointer element, void* extraData) { +bool findSpherePenetrationOp(const OctreeElementPointer& element, void* extraData) { SphereArgs* args = static_cast(extraData); // coarse check against bounds @@ -765,7 +765,7 @@ public: CubeList* cubes; }; -bool findCapsulePenetrationOp(OctreeElementPointer element, void* extraData) { +bool findCapsulePenetrationOp(const OctreeElementPointer& element, void* extraData) { CapsuleArgs* args = static_cast(extraData); // coarse check against bounds @@ -798,7 +798,7 @@ uint qHash(const glm::vec3& point) { (((quint64)(point.z * RESOLUTION_PER_METER)) % MAX_SCALED_COMPONENT << 2 * BITS_PER_COMPONENT)); } -bool findContentInCubeOp(OctreeElementPointer element, void* extraData) { +bool findContentInCubeOp(const OctreeElementPointer& element, void* extraData) { ContentArgs* args = static_cast(extraData); // coarse check against bounds @@ -851,7 +851,7 @@ public: }; // Find the smallest colored voxel enclosing a point (if there is one) -bool getElementEnclosingOperation(OctreeElementPointer element, void* extraData) { +bool getElementEnclosingOperation(const OctreeElementPointer& element, void* extraData) { GetElementEnclosingArgs* args = static_cast(extraData); if (element->getAACube().contains(args->point)) { if (element->hasContent() && element->isLeaf()) { @@ -885,7 +885,7 @@ OctreeElementPointer Octree::getElementEnclosingPoint(const glm::vec3& point, Oc -int Octree::encodeTreeBitstream(OctreeElementPointer element, +int Octree::encodeTreeBitstream(const OctreeElementPointer& element, OctreePacketData* packetData, OctreeElementBag& bag, EncodeBitstreamParams& params) { @@ -979,7 +979,7 @@ int Octree::encodeTreeBitstream(OctreeElementPointer element, return bytesWritten; } -int Octree::encodeTreeBitstreamRecursion(OctreeElementPointer element, +int Octree::encodeTreeBitstreamRecursion(const OctreeElementPointer& element, OctreePacketData* packetData, OctreeElementBag& bag, EncodeBitstreamParams& params, int& currentEncodeLevel, const ViewFrustum::intersection& parentLocationThisView) const { @@ -1866,7 +1866,7 @@ bool Octree::readJSONFromStream(unsigned long streamLength, QDataStream& inputSt return success; } -bool Octree::writeToFile(const char* fileName, OctreeElementPointer element, QString persistAsFileType) { +bool Octree::writeToFile(const char* fileName, const OctreeElementPointer& element, QString persistAsFileType) { // make the sure file extension makes sense QString qFileName = fileNameWithoutExtension(QString(fileName), PERSIST_EXTENSIONS) + "." + persistAsFileType; QByteArray byteArray = qFileName.toUtf8(); @@ -1883,7 +1883,7 @@ bool Octree::writeToFile(const char* fileName, OctreeElementPointer element, QSt return success; } -bool Octree::writeToJSONFile(const char* fileName, OctreeElementPointer element, bool doGzip) { +bool Octree::writeToJSONFile(const char* fileName, const OctreeElementPointer& element, bool doGzip) { QVariantMap entityDescription; qCDebug(octree, "Saving JSON SVO to file %s...", fileName); @@ -1937,7 +1937,7 @@ unsigned long Octree::getOctreeElementsCount() { return nodeCount; } -bool Octree::countOctreeElementsOperation(OctreeElementPointer element, void* extraData) { +bool Octree::countOctreeElementsOperation(const OctreeElementPointer& element, void* extraData) { (*(unsigned long*)extraData)++; return true; // keep going } diff --git a/libraries/octree/src/Octree.h b/libraries/octree/src/Octree.h index caae31eaa5..512a0ab64e 100644 --- a/libraries/octree/src/Octree.h +++ b/libraries/octree/src/Octree.h @@ -41,13 +41,13 @@ extern QVector PERSIST_EXTENSIONS; /// derive from this class to use the Octree::recurseTreeWithOperator() method class RecurseOctreeOperator { public: - virtual bool preRecursion(OctreeElementPointer element) = 0; - virtual bool postRecursion(OctreeElementPointer element) = 0; - virtual OctreeElementPointer possiblyCreateChildAt(OctreeElementPointer element, int childIndex) { return NULL; } + virtual bool preRecursion(const OctreeElementPointer& element) = 0; + virtual bool postRecursion(const OctreeElementPointer& element) = 0; + virtual OctreeElementPointer possiblyCreateChildAt(const OctreeElementPointer& element, int childIndex) { return NULL; } }; // Callback function, for recuseTreeWithOperation -typedef bool (*RecurseOctreeOperation)(OctreeElementPointer element, void* extraData); +using RecurseOctreeOperation = std::function; typedef enum {GRADIENT, RANDOM, NATURAL} creationMode; typedef QHash CubeList; @@ -233,7 +233,7 @@ public: void readBitstreamToTree(const unsigned char* bitstream, unsigned long int bufferSizeBytes, ReadBitstreamToTreeParams& args); void deleteOctalCodeFromTree(const unsigned char* codeBuffer, bool collapseEmptyTrees = DONT_COLLAPSE); - void reaverageOctreeElements(OctreeElementPointer startElement = NULL); + void reaverageOctreeElements(OctreeElementPointer startElement = OctreeElementPointer()); void deleteOctreeElementAt(float x, float y, float z, float s); @@ -248,18 +248,18 @@ public: OctreeElementPointer getOrCreateChildElementAt(float x, float y, float z, float s); OctreeElementPointer getOrCreateChildElementContaining(const AACube& box); - void recurseTreeWithOperation(RecurseOctreeOperation operation, void* extraData = NULL); - void recurseTreeWithPostOperation(RecurseOctreeOperation operation, void* extraData = NULL); + void recurseTreeWithOperation(const RecurseOctreeOperation& operation, void* extraData = NULL); + void recurseTreeWithPostOperation(const RecurseOctreeOperation& operation, void* extraData = NULL); /// \param operation type of operation /// \param point point in world-frame (meters) /// \param extraData hook for user data to be interpreted by special context - void recurseTreeWithOperationDistanceSorted(RecurseOctreeOperation operation, + void recurseTreeWithOperationDistanceSorted(const RecurseOctreeOperation& operation, const glm::vec3& point, void* extraData = NULL); void recurseTreeWithOperator(RecurseOctreeOperator* operatorObject); - int encodeTreeBitstream(OctreeElementPointer element, OctreePacketData* packetData, OctreeElementBag& bag, + int encodeTreeBitstream(const OctreeElementPointer& element, OctreePacketData* packetData, OctreeElementBag& bag, EncodeBitstreamParams& params) ; bool isDirty() const { return _isDirty; } @@ -293,8 +293,8 @@ public: void loadOctreeFile(const char* fileName); // Octree exporters - bool writeToFile(const char* filename, OctreeElementPointer element = NULL, QString persistAsFileType = "json.gz"); - bool writeToJSONFile(const char* filename, OctreeElementPointer element = NULL, bool doGzip = false); + bool writeToFile(const char* filename, const OctreeElementPointer& element = NULL, QString persistAsFileType = "json.gz"); + bool writeToJSONFile(const char* filename, const OctreeElementPointer& element = NULL, bool doGzip = false); virtual bool writeToMap(QVariantMap& entityDescription, OctreeElementPointer element, bool skipDefaultValues, bool skipThoseWithBadParents) = 0; @@ -311,18 +311,18 @@ public: bool getShouldReaverage() const { return _shouldReaverage; } - void recurseElementWithOperation(OctreeElementPointer element, RecurseOctreeOperation operation, + void recurseElementWithOperation(const OctreeElementPointer& element, const RecurseOctreeOperation& operation, void* extraData, int recursionCount = 0); /// Traverse child nodes of node applying operation in post-fix order /// - void recurseElementWithPostOperation(OctreeElementPointer element, RecurseOctreeOperation operation, + void recurseElementWithPostOperation(const OctreeElementPointer& element, const RecurseOctreeOperation& operation, void* extraData, int recursionCount = 0); - void recurseElementWithOperationDistanceSorted(OctreeElementPointer element, RecurseOctreeOperation operation, + void recurseElementWithOperationDistanceSorted(const OctreeElementPointer& element, const RecurseOctreeOperation& operation, const glm::vec3& point, void* extraData, int recursionCount = 0); - bool recurseElementWithOperator(OctreeElementPointer element, RecurseOctreeOperator* operatorObject, int recursionCount = 0); + bool recurseElementWithOperator(const OctreeElementPointer& element, RecurseOctreeOperator* operatorObject, int recursionCount = 0); bool getIsViewing() const { return _isViewing; } /// This tree is receiving inbound viewer datagrams. void setIsViewing(bool isViewing) { _isViewing = isViewing; } @@ -353,18 +353,18 @@ public slots: protected: - void deleteOctalCodeFromTreeRecursion(OctreeElementPointer element, void* extraData); + void deleteOctalCodeFromTreeRecursion(const OctreeElementPointer& element, void* extraData); - int encodeTreeBitstreamRecursion(OctreeElementPointer element, + int encodeTreeBitstreamRecursion(const OctreeElementPointer& element, OctreePacketData* packetData, OctreeElementBag& bag, EncodeBitstreamParams& params, int& currentEncodeLevel, const ViewFrustum::intersection& parentLocationThisView) const; - static bool countOctreeElementsOperation(OctreeElementPointer element, void* extraData); + static bool countOctreeElementsOperation(const OctreeElementPointer& element, void* extraData); - OctreeElementPointer nodeForOctalCode(OctreeElementPointer ancestorElement, const unsigned char* needleCode, OctreeElementPointer* parentOfFoundElement) const; - OctreeElementPointer createMissingElement(OctreeElementPointer lastParentElement, const unsigned char* codeToReach, int recursionCount = 0); - int readElementData(OctreeElementPointer destinationElement, const unsigned char* nodeData, + OctreeElementPointer nodeForOctalCode(const OctreeElementPointer& ancestorElement, const unsigned char* needleCode, OctreeElementPointer* parentOfFoundElement) const; + OctreeElementPointer createMissingElement(const OctreeElementPointer& lastParentElement, const unsigned char* codeToReach, int recursionCount = 0); + int readElementData(const OctreeElementPointer& destinationElement, const unsigned char* nodeData, int bufferSizeBytes, ReadBitstreamToTreeParams& args); OctreeElementPointer _rootElement = nullptr; diff --git a/libraries/octree/src/OctreeElement.cpp b/libraries/octree/src/OctreeElement.cpp index b56edcfcc6..989951b661 100644 --- a/libraries/octree/src/OctreeElement.cpp +++ b/libraries/octree/src/OctreeElement.cpp @@ -233,7 +233,7 @@ OctreeElementPointer OctreeElement::removeChildAtIndex(int childIndex) { return returnedChild; } -bool OctreeElement::isParentOf(OctreeElementPointer possibleChild) const { +bool OctreeElement::isParentOf(const OctreeElementPointer& possibleChild) const { if (possibleChild) { for (int childIndex = 0; childIndex < NUMBER_OF_CHILDREN; childIndex++) { OctreeElementPointer childAt = getChildAtIndex(childIndex); @@ -300,7 +300,7 @@ void OctreeElement::deleteAllChildren() { } } -void OctreeElement::setChildAtIndex(int childIndex, OctreeElementPointer child) { +void OctreeElement::setChildAtIndex(int childIndex, const OctreeElementPointer& child) { #ifdef SIMPLE_CHILD_ARRAY int previousChildCount = getChildCount(); if (child) { diff --git a/libraries/octree/src/OctreeElement.h b/libraries/octree/src/OctreeElement.h index 350ec9e5fa..9ab5d9429d 100644 --- a/libraries/octree/src/OctreeElement.h +++ b/libraries/octree/src/OctreeElement.h @@ -122,7 +122,7 @@ public: OctreeElementPointer getChildAtIndex(int childIndex) const; void deleteChildAtIndex(int childIndex); OctreeElementPointer removeChildAtIndex(int childIndex); - bool isParentOf(OctreeElementPointer possibleChild) const; + bool isParentOf(const OctreeElementPointer& possibleChild) const; /// handles deletion of all descendants, returns false if delete not approved bool safeDeepDeleteChildAtIndex(int childIndex, int recursionCount = 0); @@ -222,7 +222,7 @@ public: protected: void deleteAllChildren(); - void setChildAtIndex(int childIndex, OctreeElementPointer child); + void setChildAtIndex(int childIndex, const OctreeElementPointer& child); void calculateAACube(); diff --git a/libraries/octree/src/OctreeElementBag.cpp b/libraries/octree/src/OctreeElementBag.cpp index 10d80e5799..afd2d5cdc3 100644 --- a/libraries/octree/src/OctreeElementBag.cpp +++ b/libraries/octree/src/OctreeElementBag.cpp @@ -23,7 +23,7 @@ bool OctreeElementBag::isEmpty() { return _bagElements.empty(); } -void OctreeElementBag::insert(OctreeElementPointer element) { +void OctreeElementBag::insert(const OctreeElementPointer& element) { _bagElements[element.get()] = element; } diff --git a/libraries/octree/src/OctreeElementBag.h b/libraries/octree/src/OctreeElementBag.h index 5e671df78b..34c49f1e60 100644 --- a/libraries/octree/src/OctreeElementBag.h +++ b/libraries/octree/src/OctreeElementBag.h @@ -24,7 +24,7 @@ class OctreeElementBag { using Bag = std::unordered_map; public: - void insert(OctreeElementPointer element); // put a element into the bag + void insert(const OctreeElementPointer& element); // put a element into the bag OctreeElementPointer extract(); /// pull a element out of the bag (could come in any order) and if all of the /// elements have expired, a single null pointer will be returned diff --git a/libraries/octree/src/OctreeRenderer.cpp b/libraries/octree/src/OctreeProcessor.cpp similarity index 77% rename from libraries/octree/src/OctreeRenderer.cpp rename to libraries/octree/src/OctreeProcessor.cpp index 06c0ff1f12..65b30dd197 100644 --- a/libraries/octree/src/OctreeRenderer.cpp +++ b/libraries/octree/src/OctreeProcessor.cpp @@ -1,5 +1,5 @@ // -// OctreeRenderer.cpp +// OctreeProcessor.cpp // libraries/octree/src // // Created by Brad Hefta-Gaub on 12/6/13. @@ -14,49 +14,48 @@ #include #include -#include #include #include "OctreeLogging.h" -#include "OctreeRenderer.h" +#include "OctreeProcessor.h" -OctreeRenderer::OctreeRenderer() : +OctreeProcessor::OctreeProcessor() : _tree(NULL), _managedTree(false) { } -void OctreeRenderer::init() { +void OctreeProcessor::init() { if (!_tree) { _tree = createTree(); _managedTree = true; } } -OctreeRenderer::~OctreeRenderer() { +OctreeProcessor::~OctreeProcessor() { } -void OctreeRenderer::setTree(OctreePointer newTree) { +void OctreeProcessor::setTree(OctreePointer newTree) { _tree = newTree; } -void OctreeRenderer::processDatagram(ReceivedMessage& message, SharedNodePointer sourceNode) { +void OctreeProcessor::processDatagram(ReceivedMessage& message, SharedNodePointer sourceNode) { bool extraDebugging = false; if (extraDebugging) { - qCDebug(octree) << "OctreeRenderer::processDatagram()"; + qCDebug(octree) << "OctreeProcessor::processDatagram()"; } if (!_tree) { - qCDebug(octree) << "OctreeRenderer::processDatagram() called before init, calling init()..."; + qCDebug(octree) << "OctreeProcessor::processDatagram() called before init, calling init()..."; this->init(); } bool showTimingDetails = false; // Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); - PerformanceWarning warn(showTimingDetails, "OctreeRenderer::processDatagram()", showTimingDetails); + PerformanceWarning warn(showTimingDetails, "OctreeProcessor::processDatagram()", showTimingDetails); if (message.getType() == getExpectedPacketType()) { - PerformanceWarning warn(showTimingDetails, "OctreeRenderer::processDatagram expected PacketType", showTimingDetails); + PerformanceWarning warn(showTimingDetails, "OctreeProcessor::processDatagram expected PacketType", showTimingDetails); // if we are getting inbound packets, then our tree is also viewing, and we should remember that fact. _tree->setIsViewing(true); @@ -79,7 +78,7 @@ void OctreeRenderer::processDatagram(ReceivedMessage& message, SharedNodePointer OCTREE_PACKET_INTERNAL_SECTION_SIZE sectionLength = 0; if (extraDebugging) { - qCDebug(octree) << "OctreeRenderer::processDatagram() ... " + qCDebug(octree) << "OctreeProcessor::processDatagram() ... " "Got Packet Section color:" << packetIsColored << "compressed:" << packetIsCompressed << "sequence: " << sequence << @@ -130,7 +129,7 @@ void OctreeRenderer::processDatagram(ReceivedMessage& message, SharedNodePointer packetData.loadFinalizedContent(reinterpret_cast(message.getRawMessage() + message.getPosition()), sectionLength); if (extraDebugging) { - qCDebug(octree) << "OctreeRenderer::processDatagram() ... " + qCDebug(octree) << "OctreeProcessor::processDatagram() ... " "Got Packet Section color:" << packetIsColored << "compressed:" << packetIsCompressed << "sequence: " << sequence << @@ -143,13 +142,13 @@ void OctreeRenderer::processDatagram(ReceivedMessage& message, SharedNodePointer } if (extraDebugging) { - qCDebug(octree) << "OctreeRenderer::processDatagram() ******* START _tree->readBitstreamToTree()..."; + qCDebug(octree) << "OctreeProcessor::processDatagram() ******* START _tree->readBitstreamToTree()..."; } startReadBitsteam = usecTimestampNow(); _tree->readBitstreamToTree(packetData.getUncompressedData(), packetData.getUncompressedSize(), args); endReadBitsteam = usecTimestampNow(); if (extraDebugging) { - qCDebug(octree) << "OctreeRenderer::processDatagram() ******* END _tree->readBitstreamToTree()..."; + qCDebug(octree) << "OctreeProcessor::processDatagram() ******* END _tree->readBitstreamToTree()..."; } }); @@ -198,32 +197,7 @@ void OctreeRenderer::processDatagram(ReceivedMessage& message, SharedNodePointer } } -bool OctreeRenderer::renderOperation(OctreeElementPointer element, void* extraData) { - RenderArgs* args = static_cast(extraData); - if (element->isInView(args->getViewFrustum())) { - if (element->hasContent()) { - if (element->calculateShouldRender(args->getViewFrustum(), args->_sizeScale, args->_boundaryLevelAdjust)) { - args->_renderer->renderElement(element, args); - } else { - return false; // if we shouldn't render, then we also should stop recursing. - } - } - return true; // continue recursing - } - // if not in view stop recursing - return false; -} - -void OctreeRenderer::render(RenderArgs* renderArgs) { - if (_tree) { - renderArgs->_renderer = sharedFromThis(); - _tree->withReadLock([&] { - _tree->recurseTreeWithOperation(renderOperation, renderArgs); - }); - } -} - -void OctreeRenderer::clear() { +void OctreeProcessor::clear() { if (_tree) { _tree->withWriteLock([&] { _tree->eraseAllOctreeElements(); diff --git a/libraries/octree/src/OctreeRenderer.h b/libraries/octree/src/OctreeProcessor.h similarity index 75% rename from libraries/octree/src/OctreeRenderer.h rename to libraries/octree/src/OctreeProcessor.h index c18464b7ea..25e280abca 100644 --- a/libraries/octree/src/OctreeRenderer.h +++ b/libraries/octree/src/OctreeProcessor.h @@ -1,5 +1,5 @@ // -// OctreeRenderer.h +// OctreeProcessor.h // libraries/octree/src // // Created by Brad Hefta-Gaub on 12/6/13. @@ -9,8 +9,8 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#ifndef hifi_OctreeRenderer_h -#define hifi_OctreeRenderer_h +#ifndef hifi_OctreeProcessor_h +#define hifi_OctreeProcessor_h #include #include @@ -18,27 +18,22 @@ #include #include -#include #include -#include #include "Octree.h" #include "OctreePacketData.h" -class OctreeRenderer; - // Generic client side Octree renderer class. -class OctreeRenderer : public QObject, public QEnableSharedFromThis { +class OctreeProcessor : public QObject, public QEnableSharedFromThis { Q_OBJECT public: - OctreeRenderer(); - virtual ~OctreeRenderer(); + OctreeProcessor(); + virtual ~OctreeProcessor(); virtual char getMyNodeType() const = 0; virtual PacketType getMyQueryMessageType() const = 0; virtual PacketType getExpectedPacketType() const = 0; - virtual void renderElement(OctreeElementPointer element, RenderArgs* args) { } virtual void setTree(OctreePointer newTree); @@ -48,14 +43,6 @@ public: /// initialize and GPU/rendering related resources virtual void init(); - /// render the content of the octree - virtual void render(RenderArgs* renderArgs); - - const ViewFrustum& getViewFrustum() const { return _viewFrustum; } - void setViewFrustum(const ViewFrustum& viewFrustum) { _viewFrustum = viewFrustum; } - - static bool renderOperation(OctreeElementPointer element, void* extraData); - /// clears the tree virtual void clear(); @@ -75,7 +62,6 @@ protected: OctreePointer _tree; bool _managedTree; - ViewFrustum _viewFrustum; SimpleMovingAverage _elementsPerPacket; SimpleMovingAverage _entitiesPerPacket; @@ -95,4 +81,4 @@ protected: }; -#endif // hifi_OctreeRenderer_h +#endif // hifi_OctreeProcessor_h diff --git a/libraries/octree/src/OctreeSceneStats.cpp b/libraries/octree/src/OctreeSceneStats.cpp index fdaa6b4928..09b2d6ddf5 100644 --- a/libraries/octree/src/OctreeSceneStats.cpp +++ b/libraries/octree/src/OctreeSceneStats.cpp @@ -141,7 +141,7 @@ OctreeSceneStats::~OctreeSceneStats() { reset(); } -void OctreeSceneStats::sceneStarted(bool isFullScene, bool isMoving, OctreeElementPointer root, +void OctreeSceneStats::sceneStarted(bool isFullScene, bool isMoving, const OctreeElementPointer& root, JurisdictionMap* jurisdictionMap) { reset(); // resets packet and octree stats _isStarted = true; @@ -246,7 +246,7 @@ void OctreeSceneStats::packetSent(int bytes) { _bytes += bytes; } -void OctreeSceneStats::traversed(const OctreeElementPointer element) { +void OctreeSceneStats::traversed(const OctreeElementPointer& element) { _traversed++; if (element->isLeaf()) { _leaves++; @@ -255,7 +255,7 @@ void OctreeSceneStats::traversed(const OctreeElementPointer element) { } } -void OctreeSceneStats::skippedDistance(const OctreeElementPointer element) { +void OctreeSceneStats::skippedDistance(const OctreeElementPointer& element) { _skippedDistance++; if (element->isLeaf()) { _leavesSkippedDistance++; @@ -264,7 +264,7 @@ void OctreeSceneStats::skippedDistance(const OctreeElementPointer element) { } } -void OctreeSceneStats::skippedOutOfView(const OctreeElementPointer element) { +void OctreeSceneStats::skippedOutOfView(const OctreeElementPointer& element) { _skippedOutOfView++; if (element->isLeaf()) { _leavesSkippedOutOfView++; @@ -273,7 +273,7 @@ void OctreeSceneStats::skippedOutOfView(const OctreeElementPointer element) { } } -void OctreeSceneStats::skippedWasInView(const OctreeElementPointer element) { +void OctreeSceneStats::skippedWasInView(const OctreeElementPointer& element) { _skippedWasInView++; if (element->isLeaf()) { _leavesSkippedWasInView++; @@ -282,7 +282,7 @@ void OctreeSceneStats::skippedWasInView(const OctreeElementPointer element) { } } -void OctreeSceneStats::skippedNoChange(const OctreeElementPointer element) { +void OctreeSceneStats::skippedNoChange(const OctreeElementPointer& element) { _skippedNoChange++; if (element->isLeaf()) { _leavesSkippedNoChange++; @@ -291,7 +291,7 @@ void OctreeSceneStats::skippedNoChange(const OctreeElementPointer element) { } } -void OctreeSceneStats::skippedOccluded(const OctreeElementPointer element) { +void OctreeSceneStats::skippedOccluded(const OctreeElementPointer& element) { _skippedOccluded++; if (element->isLeaf()) { _leavesSkippedOccluded++; @@ -300,7 +300,7 @@ void OctreeSceneStats::skippedOccluded(const OctreeElementPointer element) { } } -void OctreeSceneStats::colorSent(const OctreeElementPointer element) { +void OctreeSceneStats::colorSent(const OctreeElementPointer& element) { _colorSent++; if (element->isLeaf()) { _leavesColorSent++; @@ -309,7 +309,7 @@ void OctreeSceneStats::colorSent(const OctreeElementPointer element) { } } -void OctreeSceneStats::didntFit(const OctreeElementPointer element) { +void OctreeSceneStats::didntFit(const OctreeElementPointer& element) { _didntFit++; if (element->isLeaf()) { _leavesDidntFit++; diff --git a/libraries/octree/src/OctreeSceneStats.h b/libraries/octree/src/OctreeSceneStats.h index e7d697f785..3774d4287d 100644 --- a/libraries/octree/src/OctreeSceneStats.h +++ b/libraries/octree/src/OctreeSceneStats.h @@ -39,7 +39,7 @@ public: OctreeSceneStats& operator= (const OctreeSceneStats& other); // copy assignment /// Call when beginning the computation of a scene. Initializes internal structures - void sceneStarted(bool fullScene, bool moving, OctreeElementPointer root, JurisdictionMap* jurisdictionMap); + void sceneStarted(bool fullScene, bool moving, const OctreeElementPointer& root, JurisdictionMap* jurisdictionMap); bool getIsSceneStarted() const { return _isStarted; } /// Call when the computation of a scene is completed. Finalizes internal structures @@ -57,28 +57,28 @@ public: void encodeStopped(); /// Track that a element was traversed as part of computation of a scene. - void traversed(const OctreeElementPointer element); + void traversed(const OctreeElementPointer& element); /// Track that a element was skipped as part of computation of a scene due to being beyond the LOD distance. - void skippedDistance(const OctreeElementPointer element); + void skippedDistance(const OctreeElementPointer& element); /// Track that a element was skipped as part of computation of a scene due to being out of view. - void skippedOutOfView(const OctreeElementPointer element); + void skippedOutOfView(const OctreeElementPointer& element); /// Track that a element was skipped as part of computation of a scene due to previously being in view while in delta sending - void skippedWasInView(const OctreeElementPointer element); + void skippedWasInView(const OctreeElementPointer& element); /// Track that a element was skipped as part of computation of a scene due to not having changed since last full scene sent - void skippedNoChange(const OctreeElementPointer element); + void skippedNoChange(const OctreeElementPointer& element); /// Track that a element was skipped as part of computation of a scene due to being occluded - void skippedOccluded(const OctreeElementPointer element); + void skippedOccluded(const OctreeElementPointer& element); /// Track that a element's color was was sent as part of computation of a scene - void colorSent(const OctreeElementPointer element); + void colorSent(const OctreeElementPointer& element); /// Track that a element was due to be sent, but didn't fit in the packet and was moved to next packet - void didntFit(const OctreeElementPointer element); + void didntFit(const OctreeElementPointer& element); /// Track that the color bitmask was was sent as part of computation of a scene void colorBitsWritten(); diff --git a/libraries/physics/CMakeLists.txt b/libraries/physics/CMakeLists.txt index 7733c019e0..e219d3dbcd 100644 --- a/libraries/physics/CMakeLists.txt +++ b/libraries/physics/CMakeLists.txt @@ -1,5 +1,11 @@ set(TARGET_NAME physics) setup_hifi_library() link_hifi_libraries(shared fbx entities model) +include_hifi_library_headers(networking) +include_hifi_library_headers(gpu) +include_hifi_library_headers(avatars) +include_hifi_library_headers(audio) +include_hifi_library_headers(octree) +include_hifi_library_headers(animation) target_bullet() diff --git a/libraries/render-utils/CMakeLists.txt b/libraries/render-utils/CMakeLists.txt index c359924e0d..0079ffae66 100644 --- a/libraries/render-utils/CMakeLists.txt +++ b/libraries/render-utils/CMakeLists.txt @@ -4,6 +4,9 @@ AUTOSCRIBE_SHADER_LIB(gpu model render) qt5_add_resources(QT_RESOURCES_FILE "${CMAKE_CURRENT_SOURCE_DIR}/res/fonts/fonts.qrc") setup_hifi_library(Widgets OpenGL Network Qml Quick Script) link_hifi_libraries(shared ktx gpu model model-networking render animation fbx entities image procedural) +include_hifi_library_headers(networking) +include_hifi_library_headers(octree) +include_hifi_library_headers(audio) if (NOT ANDROID) target_nsight() diff --git a/libraries/render-utils/src/AntialiasingEffect.cpp b/libraries/render-utils/src/AntialiasingEffect.cpp index f7881b0333..55a46a526f 100644 --- a/libraries/render-utils/src/AntialiasingEffect.cpp +++ b/libraries/render-utils/src/AntialiasingEffect.cpp @@ -19,7 +19,6 @@ #include "AntialiasingEffect.h" #include "StencilMaskPass.h" #include "TextureCache.h" -#include "FramebufferCache.h" #include "DependencyManager.h" #include "ViewFrustum.h" #include "GeometryCache.h" @@ -40,9 +39,9 @@ Antialiasing::~Antialiasing() { } } -const gpu::PipelinePointer& Antialiasing::getAntialiasingPipeline() { - int width = DependencyManager::get()->getFrameBufferSize().width(); - int height = DependencyManager::get()->getFrameBufferSize().height(); +const gpu::PipelinePointer& Antialiasing::getAntialiasingPipeline(RenderArgs* args) { + int width = args->_viewport.z; + int height = args->_viewport.w; if (_antialiasingBuffer && _antialiasingBuffer->getSize() != uvec2(width, height)) { _antialiasingBuffer.reset(); @@ -51,7 +50,7 @@ const gpu::PipelinePointer& Antialiasing::getAntialiasingPipeline() { if (!_antialiasingBuffer) { // Link the antialiasing FBO to texture _antialiasingBuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("antialiasing")); - auto format = gpu::Element::COLOR_SRGBA_32; // DependencyManager::get()->getLightingTexture()->getTexelFormat(); + auto format = gpu::Element::COLOR_SRGBA_32; auto defaultSampler = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_POINT); _antialiasingTexture = gpu::Texture::createRenderBuffer(format, width, height, gpu::Texture::SINGLE_MIP, defaultSampler); _antialiasingBuffer->setRenderBuffer(0, _antialiasingTexture); @@ -115,10 +114,8 @@ void Antialiasing::run(const render::RenderContextPointer& renderContext, const batch.setViewportTransform(args->_viewport); // FIXME: NEED to simplify that code to avoid all the GeometryCahce call, this is purely pixel manipulation - auto framebufferCache = DependencyManager::get(); - QSize framebufferSize = framebufferCache->getFrameBufferSize(); - float fbWidth = framebufferSize.width(); - float fbHeight = framebufferSize.height(); + float fbWidth = renderContext->args->_viewport.z; + float fbHeight = renderContext->args->_viewport.w; // float sMin = args->_viewport.x / fbWidth; // float sWidth = args->_viewport.z / fbWidth; // float tMin = args->_viewport.y / fbHeight; @@ -133,10 +130,10 @@ void Antialiasing::run(const render::RenderContextPointer& renderContext, const batch.setModelTransform(Transform()); // FXAA step - getAntialiasingPipeline(); + auto pipeline = getAntialiasingPipeline(renderContext->args); batch.setResourceTexture(0, sourceBuffer->getRenderBuffer(0)); batch.setFramebuffer(_antialiasingBuffer); - batch.setPipeline(getAntialiasingPipeline()); + batch.setPipeline(pipeline); // initialize the view-space unpacking uniforms using frustum data float left, right, bottom, top, nearVal, farVal; diff --git a/libraries/render-utils/src/AntialiasingEffect.h b/libraries/render-utils/src/AntialiasingEffect.h index e403032b4e..cec2554a3b 100644 --- a/libraries/render-utils/src/AntialiasingEffect.h +++ b/libraries/render-utils/src/AntialiasingEffect.h @@ -33,7 +33,7 @@ public: void configure(const Config& config) {} void run(const render::RenderContextPointer& renderContext, const gpu::FramebufferPointer& sourceBuffer); - const gpu::PipelinePointer& getAntialiasingPipeline(); + const gpu::PipelinePointer& getAntialiasingPipeline(RenderArgs* args); const gpu::PipelinePointer& getBlendPipeline(); private: diff --git a/libraries/render-utils/src/DebugDeferredBuffer.cpp b/libraries/render-utils/src/DebugDeferredBuffer.cpp index a67d20c6b0..3359b3a12d 100644 --- a/libraries/render-utils/src/DebugDeferredBuffer.cpp +++ b/libraries/render-utils/src/DebugDeferredBuffer.cpp @@ -19,7 +19,6 @@ #include #include "GeometryCache.h" -#include "FramebufferCache.h" #include "TextureCache.h" #include "DeferredLightingEffect.h" @@ -410,7 +409,6 @@ void DebugDeferredBuffer::run(const RenderContextPointer& renderContext, const I batch.setViewportTransform(args->_viewport); const auto geometryBuffer = DependencyManager::get(); - const auto framebufferCache = DependencyManager::get(); const auto textureCache = DependencyManager::get(); glm::mat4 projMat; diff --git a/libraries/render-utils/src/DeferredFrameTransform.h b/libraries/render-utils/src/DeferredFrameTransform.h index 84be943998..93e194f052 100644 --- a/libraries/render-utils/src/DeferredFrameTransform.h +++ b/libraries/render-utils/src/DeferredFrameTransform.h @@ -12,10 +12,10 @@ #ifndef hifi_DeferredFrameTransform_h #define hifi_DeferredFrameTransform_h -#include "gpu/Resource.h" -#include "render/DrawTask.h" +#include -class RenderArgs; +#include +#include // DeferredFrameTransform is a helper class gathering in one place the needed camera transform // and frame resolution needed for all the deferred rendering passes taking advantage of the Deferred buffers diff --git a/libraries/render-utils/src/DeferredFramebuffer.h b/libraries/render-utils/src/DeferredFramebuffer.h index 6c83fbb91f..6002bf6494 100644 --- a/libraries/render-utils/src/DeferredFramebuffer.h +++ b/libraries/render-utils/src/DeferredFramebuffer.h @@ -15,7 +15,6 @@ #include "gpu/Resource.h" #include "gpu/Framebuffer.h" -class RenderArgs; // DeferredFramebuffer is a helper class gathering in one place the GBuffer (Framebuffer) and lighting framebuffer class DeferredFramebuffer { diff --git a/libraries/render-utils/src/DeferredLightingEffect.cpp b/libraries/render-utils/src/DeferredLightingEffect.cpp index 4b3ee9fec7..0b4eee125b 100644 --- a/libraries/render-utils/src/DeferredLightingEffect.cpp +++ b/libraries/render-utils/src/DeferredLightingEffect.cpp @@ -418,10 +418,7 @@ model::MeshPointer DeferredLightingEffect::getSpotLightMesh() { } void PreparePrimaryFramebuffer::run(const RenderContextPointer& renderContext, gpu::FramebufferPointer& primaryFramebuffer) { - - auto framebufferCache = DependencyManager::get(); - auto framebufferSize = framebufferCache->getFrameBufferSize(); - glm::uvec2 frameSize(framebufferSize.width(), framebufferSize.height()); + glm::uvec2 frameSize(renderContext->args->_viewport.z, renderContext->args->_viewport.w); // Resizing framebuffers instead of re-building them seems to cause issues with threaded // rendering diff --git a/libraries/render-utils/src/DeferredLightingEffect.h b/libraries/render-utils/src/DeferredLightingEffect.h index c171973216..d69c72e97d 100644 --- a/libraries/render-utils/src/DeferredLightingEffect.h +++ b/libraries/render-utils/src/DeferredLightingEffect.h @@ -37,7 +37,6 @@ #include "AmbientOcclusionEffect.h" -class RenderArgs; struct LightLocations; using LightLocationsPtr = std::shared_ptr; diff --git a/libraries/render-utils/src/LightingModel.h b/libraries/render-utils/src/LightingModel.h index 26fb4faac5..e058b10921 100644 --- a/libraries/render-utils/src/LightingModel.h +++ b/libraries/render-utils/src/LightingModel.h @@ -12,10 +12,10 @@ #ifndef hifi_LightingModel_h #define hifi_LightingModel_h -#include "gpu/Resource.h" -#include "render/DrawTask.h" +#include -class RenderArgs; +#include +#include // LightingModel is a helper class gathering in one place the flags to enable the lighting contributions class LightingModel { diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp index b4fd7e7d2a..b16134db5f 100644 --- a/libraries/render-utils/src/MeshPartPayload.cpp +++ b/libraries/render-utils/src/MeshPartPayload.cpp @@ -557,6 +557,7 @@ void ModelMeshPartPayload::render(RenderArgs* args) { if (_fadeState == FADE_WAITING_TO_START) { if (model->isLoaded()) { + // FIXME as far as I can tell this is the ONLY reason render-util depends on entities. if (EntityItem::getEntitiesShouldFadeFunction()()) { _fadeStartTime = usecTimestampNow(); _fadeState = FADE_IN_PROGRESS; diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 447d0e37bd..73f0e37d6c 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -9,6 +9,8 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include "Model.h" + #include #include #include @@ -24,7 +26,6 @@ #include "AbstractViewStateInterface.h" #include "MeshPartPayload.h" -#include "Model.h" #include "RenderUtilsLogging.h" #include diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index 53d446d306..adee4d57b1 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -34,10 +35,10 @@ #include "TextureCache.h" #include "Rig.h" + class AbstractViewStateInterface; class QScriptEngine; -#include "RenderArgs.h" class ViewFrustum; namespace render { diff --git a/libraries/render-utils/src/RenderDeferredTask.cpp b/libraries/render-utils/src/RenderDeferredTask.cpp index 1b99fe92ee..b49a066bf7 100644 --- a/libraries/render-utils/src/RenderDeferredTask.cpp +++ b/libraries/render-utils/src/RenderDeferredTask.cpp @@ -14,7 +14,6 @@ #include #include -#include #include #include diff --git a/libraries/render-utils/src/RenderForwardTask.cpp b/libraries/render-utils/src/RenderForwardTask.cpp index 84514eeb1a..a77d741aa5 100755 --- a/libraries/render-utils/src/RenderForwardTask.cpp +++ b/libraries/render-utils/src/RenderForwardTask.cpp @@ -15,7 +15,6 @@ #include #include -#include #include #include diff --git a/libraries/render-utils/src/StencilMaskPass.cpp b/libraries/render-utils/src/StencilMaskPass.cpp index 2374f24211..2d4efc0573 100644 --- a/libraries/render-utils/src/StencilMaskPass.cpp +++ b/libraries/render-utils/src/StencilMaskPass.cpp @@ -11,7 +11,6 @@ #include "StencilMaskPass.h" -#include #include #include diff --git a/libraries/render-utils/src/ToneMappingEffect.cpp b/libraries/render-utils/src/ToneMappingEffect.cpp index ce41cf16fa..72d692c5b2 100644 --- a/libraries/render-utils/src/ToneMappingEffect.cpp +++ b/libraries/render-utils/src/ToneMappingEffect.cpp @@ -14,7 +14,6 @@ #include #include -#include #include "StencilMaskPass.h" #include "FramebufferCache.h" diff --git a/libraries/render-utils/src/ToneMappingEffect.h b/libraries/render-utils/src/ToneMappingEffect.h index 13dffd16a7..2d1414e1ef 100644 --- a/libraries/render-utils/src/ToneMappingEffect.h +++ b/libraries/render-utils/src/ToneMappingEffect.h @@ -17,11 +17,10 @@ #include #include +#include #include -class RenderArgs; - class ToneMappingEffect { public: ToneMappingEffect(); diff --git a/libraries/render/CMakeLists.txt b/libraries/render/CMakeLists.txt index 8fd05bd320..561dff4290 100644 --- a/libraries/render/CMakeLists.txt +++ b/libraries/render/CMakeLists.txt @@ -2,7 +2,8 @@ set(TARGET_NAME render) AUTOSCRIBE_SHADER_LIB(gpu model) setup_hifi_library() +link_hifi_libraries(shared ktx gpu model) # render needs octree only for getAccuracyAngle(float, int) -link_hifi_libraries(shared ktx gpu model octree) +include_hifi_library_headers(octree) target_nsight() diff --git a/libraries/render/src/render/Args.h b/libraries/render/src/render/Args.h new file mode 100644 index 0000000000..901e0db22f --- /dev/null +++ b/libraries/render/src/render/Args.h @@ -0,0 +1,128 @@ +// +// Created by Brad Hefta-Gaub on 10/29/14. +// Copyright 2013-2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#pragma once +#ifndef hifi_render_Args_h +#define hifi_render_Args_h + +#include +#include +#include + +#include +#include + +#include +#include "Forward.h" + + +class AABox; + +namespace render { + class RenderDetails { + public: + enum Type { + ITEM, + SHADOW, + OTHER + }; + + struct Item { + int _considered = 0; + int _outOfView = 0; + int _tooSmall = 0; + int _rendered = 0; + }; + + int _materialSwitches = 0; + int _trianglesRendered = 0; + + Item _item; + Item _shadow; + Item _other; + + Item& edit(Type type) { + switch (type) { + case SHADOW: + return _shadow; + case ITEM: + return _item; + default: + return _other; + } + } + }; + + + class Args { + public: + enum RenderMode { DEFAULT_RENDER_MODE, SHADOW_RENDER_MODE, DIFFUSE_RENDER_MODE, NORMAL_RENDER_MODE, MIRROR_RENDER_MODE }; + enum DisplayMode { MONO, STEREO_MONITOR, STEREO_HMD }; + enum DebugFlags { + RENDER_DEBUG_NONE = 0, + RENDER_DEBUG_HULLS = 1 + }; + + Args() {} + + Args(const gpu::ContextPointer& context, + QSharedPointer renderData = QSharedPointer(nullptr), + float sizeScale = 1.0f, + int boundaryLevelAdjust = 0, + RenderMode renderMode = DEFAULT_RENDER_MODE, + DisplayMode displayMode = MONO, + DebugFlags debugFlags = RENDER_DEBUG_NONE, + gpu::Batch* batch = nullptr) : + _context(context), + _renderData(renderData), + _sizeScale(sizeScale), + _boundaryLevelAdjust(boundaryLevelAdjust), + _renderMode(renderMode), + _displayMode(displayMode), + _debugFlags(debugFlags), + _batch(batch) { + } + + bool hasViewFrustum() const { return _viewFrustums.size() > 0; } + void setViewFrustum(const ViewFrustum& viewFrustum) { + while (_viewFrustums.size() > 0) { + _viewFrustums.pop(); + } + _viewFrustums.push(viewFrustum); + } + const ViewFrustum& getViewFrustum() const { assert(_viewFrustums.size() > 0); return _viewFrustums.top(); } + void pushViewFrustum(const ViewFrustum& viewFrustum) { _viewFrustums.push(viewFrustum); } + void popViewFrustum() { _viewFrustums.pop(); } + + std::shared_ptr _context; + std::shared_ptr _blitFramebuffer; + std::shared_ptr _pipeline; + QSharedPointer _renderData; + std::stack _viewFrustums; + glm::ivec4 _viewport { 0.0f, 0.0f, 1.0f, 1.0f }; + glm::vec3 _boomOffset { 0.0f, 0.0f, 1.0f }; + float _sizeScale { 1.0f }; + int _boundaryLevelAdjust { 0 }; + RenderMode _renderMode { DEFAULT_RENDER_MODE }; + DisplayMode _displayMode { MONO }; + DebugFlags _debugFlags { RENDER_DEBUG_NONE }; + gpu::Batch* _batch = nullptr; + + uint32_t _globalShapeKey { 0 }; + bool _enableTexturing { true }; + + RenderDetails _details; + render::ScenePointer _scene; + int8_t _cameraMode { -1 }; + }; + +} + +using RenderArgs = render::Args; + +#endif // hifi_render_Args_h diff --git a/libraries/render/src/render/DrawSceneOctree.cpp b/libraries/render/src/render/DrawSceneOctree.cpp index c48a9ddbe3..de5ff1f74c 100644 --- a/libraries/render/src/render/DrawSceneOctree.cpp +++ b/libraries/render/src/render/DrawSceneOctree.cpp @@ -16,11 +16,11 @@ #include #include -#include #include #include +#include "Args.h" #include "drawCellBounds_vert.h" #include "drawCellBounds_frag.h" diff --git a/libraries/render/src/render/DrawSceneOctree.h b/libraries/render/src/render/DrawSceneOctree.h index 4838e34c57..9c416262ca 100644 --- a/libraries/render/src/render/DrawSceneOctree.h +++ b/libraries/render/src/render/DrawSceneOctree.h @@ -12,9 +12,10 @@ #ifndef hifi_render_DrawSceneOctree_h #define hifi_render_DrawSceneOctree_h -#include "DrawTask.h" -#include "gpu/Batch.h" #include +#include + +#include "DrawTask.h" namespace render { class DrawSceneOctreeConfig : public Job::Config { diff --git a/libraries/render/src/render/DrawStatus.cpp b/libraries/render/src/render/DrawStatus.cpp index d6275a8d7a..148e104453 100644 --- a/libraries/render/src/render/DrawStatus.cpp +++ b/libraries/render/src/render/DrawStatus.cpp @@ -16,10 +16,11 @@ #include #include -#include #include +#include "Args.h" + #include "drawItemBounds_vert.h" #include "drawItemBounds_frag.h" #include "drawItemStatus_vert.h" diff --git a/libraries/render/src/render/Forward.h b/libraries/render/src/render/Forward.h new file mode 100644 index 0000000000..4aba074d1e --- /dev/null +++ b/libraries/render/src/render/Forward.h @@ -0,0 +1,25 @@ +// +// Created by Bradley Austin Davis on 2017/06/15 +// Copyright 2013-2017 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#pragma once +#ifndef hifi_render_Forward_h +#define hifi_render_Forward_h + +#include + +namespace render { + class Scene; + using ScenePointer = std::shared_ptr; + class ShapePipeline; + class Args; +} + +using RenderArgs = render::Args; + + +#endif // hifi_render_Forward_h diff --git a/libraries/render/src/render/Item.h b/libraries/render/src/render/Item.h index b0d84d5379..007b34395d 100644 --- a/libraries/render/src/render/Item.h +++ b/libraries/render/src/render/Item.h @@ -22,7 +22,8 @@ #include #include -#include + +#include "Args.h" #include "model/Material.h" #include "ShapePipeline.h" diff --git a/libraries/render/src/render/ShapePipeline.h b/libraries/render/src/render/ShapePipeline.h index 73e8f82f24..434c909198 100644 --- a/libraries/render/src/render/ShapePipeline.h +++ b/libraries/render/src/render/ShapePipeline.h @@ -15,7 +15,8 @@ #include #include -#include + +#include "Args.h" namespace render { diff --git a/libraries/script-engine/src/AudioScriptingInterface.cpp b/libraries/script-engine/src/AudioScriptingInterface.cpp index 8452494d95..b5dd60d03b 100644 --- a/libraries/script-engine/src/AudioScriptingInterface.cpp +++ b/libraries/script-engine/src/AudioScriptingInterface.cpp @@ -19,12 +19,6 @@ void registerAudioMetaTypes(QScriptEngine* engine) { qScriptRegisterMetaType(engine, soundSharedPointerToScriptValue, soundSharedPointerFromScriptValue); } -AudioScriptingInterface::AudioScriptingInterface() : - _localAudioInterface(NULL) -{ - -} - ScriptAudioInjector* AudioScriptingInterface::playSound(SharedSoundPointer sound, const AudioInjectorOptions& injectorOptions) { if (QThread::currentThread() != thread()) { ScriptAudioInjector* injector = NULL; diff --git a/libraries/script-engine/src/AudioScriptingInterface.h b/libraries/script-engine/src/AudioScriptingInterface.h index 5ec8ce4b12..9ee2af7738 100644 --- a/libraries/script-engine/src/AudioScriptingInterface.h +++ b/libraries/script-engine/src/AudioScriptingInterface.h @@ -28,6 +28,7 @@ public: void setLocalAudioInterface(AbstractAudioInterface* audioInterface) { _localAudioInterface = audioInterface; } protected: + AudioScriptingInterface() {} // this method is protected to stop C++ callers from calling, but invokable from script Q_INVOKABLE ScriptAudioInjector* playSound(SharedSoundPointer sound, const AudioInjectorOptions& injectorOptions = AudioInjectorOptions()); @@ -44,9 +45,7 @@ signals: void inputReceived(const QByteArray& inputSamples); /// a frame of mic input audio has been received and processed private: - AudioScriptingInterface(); - - AbstractAudioInterface* _localAudioInterface; + AbstractAudioInterface* _localAudioInterface { nullptr }; }; void registerAudioMetaTypes(QScriptEngine* engine); diff --git a/libraries/script-engine/src/ScriptEngines.cpp b/libraries/script-engine/src/ScriptEngines.cpp index 1cb0c117da..b310e137b7 100644 --- a/libraries/script-engine/src/ScriptEngines.cpp +++ b/libraries/script-engine/src/ScriptEngines.cpp @@ -529,7 +529,11 @@ void ScriptEngines::launchScriptEngine(ScriptEngine* scriptEngine) { initializer(scriptEngine); } - auto const wantDebug = scriptEngine->isDebuggable() || (qApp->queryKeyboardModifiers() & Qt::ShiftModifier); + // FIXME disabling 'shift key' debugging for now. If you start up the application with + // the shift key held down, it triggers a deadlock because of script interfaces running + // on the main thread + auto const wantDebug = scriptEngine->isDebuggable(); // || (qApp->queryKeyboardModifiers() & Qt::ShiftModifier); + if (HIFI_SCRIPT_DEBUGGABLES && wantDebug) { scriptEngine->runDebuggable(); } else { diff --git a/libraries/script-engine/src/TabletScriptingInterface.cpp b/libraries/script-engine/src/TabletScriptingInterface.cpp index dadf436ea5..b41c548e57 100644 --- a/libraries/script-engine/src/TabletScriptingInterface.cpp +++ b/libraries/script-engine/src/TabletScriptingInterface.cpp @@ -439,7 +439,7 @@ void TabletProxy::loadQMLSource(const QVariant& path) { } } -void TabletProxy::pushOntoStack(const QVariant& path) { +bool TabletProxy::pushOntoStack(const QVariant& path) { QObject* root = nullptr; if (!_toolbarMode && _qmlTabletRoot) { root = _qmlTabletRoot; @@ -457,6 +457,8 @@ void TabletProxy::pushOntoStack(const QVariant& path) { } else { qCDebug(scriptengine) << "tablet cannot push QML because _qmlTabletRoot or _desktopWindow is null"; } + + return root; } void TabletProxy::popFromStack() { @@ -612,15 +614,6 @@ void TabletProxy::removeButton(QObject* tabletButtonProxy) { } } -void TabletProxy::updateMicEnabled(const bool micOn) { - auto tablet = getQmlTablet(); - if (!tablet) { - //qCCritical(scriptengine) << "Could not find tablet in TabletRoot.qml"; - } else { - QMetaObject::invokeMethod(tablet, "setMicEnabled", Qt::AutoConnection, Q_ARG(QVariant, QVariant(micOn))); - } -} - void TabletProxy::updateAudioBar(const double micLevel) { auto tablet = getQmlTablet(); if (!tablet) { diff --git a/libraries/script-engine/src/TabletScriptingInterface.h b/libraries/script-engine/src/TabletScriptingInterface.h index 85c1fdaf9a..2ae9e70846 100644 --- a/libraries/script-engine/src/TabletScriptingInterface.h +++ b/libraries/script-engine/src/TabletScriptingInterface.h @@ -119,7 +119,9 @@ public: Q_INVOKABLE void gotoWebScreen(const QString& url, const QString& injectedJavaScriptUrl); Q_INVOKABLE void loadQMLSource(const QVariant& path); - Q_INVOKABLE void pushOntoStack(const QVariant& path); + // FIXME: This currently relies on a script initializing the tablet (hence the bool denoting success); + // it should be initialized internally so it cannot fail + Q_INVOKABLE bool pushOntoStack(const QVariant& path); Q_INVOKABLE void popFromStack(); Q_INVOKABLE void loadQMLOnTop(const QVariant& path); @@ -150,13 +152,6 @@ public: */ Q_INVOKABLE void removeButton(QObject* tabletButtonProxy); - /**jsdoc - * Updates the tablet's mic enabled state - * @function TabletProxy#updateMicEnabled - * @param micEnabled {bool} mic enabled state - */ - Q_INVOKABLE void updateMicEnabled(const bool micEnabled); - /**jsdoc * Updates the audio bar in tablet to reflect latest mic level * @function TabletProxy#updateAudioBar diff --git a/libraries/shared/src/GenericThread.cpp b/libraries/shared/src/GenericThread.cpp index 00a80a2864..2e126f12c9 100644 --- a/libraries/shared/src/GenericThread.cpp +++ b/libraries/shared/src/GenericThread.cpp @@ -10,6 +10,7 @@ // #include +#include #include "GenericThread.h" @@ -73,6 +74,7 @@ void GenericThread::threadRoutine() { } while (!_stopThread) { + QCoreApplication::processEvents(); // override this function to do whatever your class actually does, return false to exit thread early if (!process()) { diff --git a/libraries/shared/src/RenderArgs.h b/libraries/shared/src/RenderArgs.h deleted file mode 100644 index d4d88c26a8..0000000000 --- a/libraries/shared/src/RenderArgs.h +++ /dev/null @@ -1,137 +0,0 @@ -// -// RenderArgs.h -// libraries/shared -// -// Created by Brad Hefta-Gaub on 10/29/14. -// Copyright 2013-2014 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#ifndef hifi_RenderArgs_h -#define hifi_RenderArgs_h - -#include -#include -#include - -#include -#include - - - -class AABox; -class OctreeRenderer; -namespace render { - class Scene; - using ScenePointer = std::shared_ptr; -} - -namespace gpu { -class Batch; -class Context; -class Texture; -class Framebuffer; -} - -namespace render { -class ShapePipeline; -} - -class RenderDetails { -public: - enum Type { - ITEM, - SHADOW, - OTHER - }; - - struct Item { - int _considered = 0; - int _outOfView = 0; - int _tooSmall = 0; - int _rendered = 0; - }; - - int _materialSwitches = 0; - int _trianglesRendered = 0; - - Item _item; - Item _shadow; - Item _other; - - Item& edit(Type type) { - switch (type) { - case SHADOW: - return _shadow; - case ITEM: - return _item; - default: - return _other; - } - } -}; - -class RenderArgs { -public: - enum RenderMode { DEFAULT_RENDER_MODE, SHADOW_RENDER_MODE, DIFFUSE_RENDER_MODE, NORMAL_RENDER_MODE, MIRROR_RENDER_MODE }; - enum DisplayMode { MONO, STEREO_MONITOR, STEREO_HMD }; - enum DebugFlags { - RENDER_DEBUG_NONE = 0, - RENDER_DEBUG_HULLS = 1 - }; - - RenderArgs(std::shared_ptr context = nullptr, - QSharedPointer renderer = QSharedPointer(nullptr), - float sizeScale = 1.0f, - int boundaryLevelAdjust = 0, - RenderMode renderMode = DEFAULT_RENDER_MODE, - DisplayMode displayMode = MONO, - DebugFlags debugFlags = RENDER_DEBUG_NONE, - gpu::Batch* batch = nullptr) : - _context(context), - _renderer(renderer), - _sizeScale(sizeScale), - _boundaryLevelAdjust(boundaryLevelAdjust), - _renderMode(renderMode), - _displayMode(displayMode), - _debugFlags(debugFlags), - _batch(batch) { - } - - bool hasViewFrustum() const { return _viewFrustums.size() > 0; } - void setViewFrustum(const ViewFrustum& viewFrustum) { - while (_viewFrustums.size() > 0) { - _viewFrustums.pop(); - } - _viewFrustums.push(viewFrustum); - } - const ViewFrustum& getViewFrustum() const { assert(_viewFrustums.size() > 0); return _viewFrustums.top(); } - void pushViewFrustum(const ViewFrustum& viewFrustum) { _viewFrustums.push(viewFrustum); } - void popViewFrustum() { _viewFrustums.pop(); } - - std::shared_ptr _context = nullptr; - std::shared_ptr _blitFramebuffer = nullptr; - std::shared_ptr _pipeline = nullptr; - QSharedPointer _renderer; - std::stack _viewFrustums; - glm::ivec4 _viewport{ 0.0f, 0.0f, 1.0f, 1.0f }; - glm::vec3 _boomOffset{ 0.0f, 0.0f, 1.0f }; - float _sizeScale = 1.0f; - int _boundaryLevelAdjust = 0; - RenderMode _renderMode = DEFAULT_RENDER_MODE; - DisplayMode _displayMode = MONO; - DebugFlags _debugFlags = RENDER_DEBUG_NONE; - gpu::Batch* _batch = nullptr; - - std::shared_ptr _whiteTexture; - uint32_t _globalShapeKey { 0 }; - bool _enableTexturing { true }; - - RenderDetails _details; - render::ScenePointer _scene; - int8_t _cameraMode { -1 }; -}; - -#endif // hifi_RenderArgs_h diff --git a/libraries/networking/src/FileCache.cpp b/libraries/shared/src/shared/FileCache.cpp similarity index 84% rename from libraries/networking/src/FileCache.cpp rename to libraries/shared/src/shared/FileCache.cpp index 95304e3866..801c68e302 100644 --- a/libraries/networking/src/FileCache.cpp +++ b/libraries/shared/src/shared/FileCache.cpp @@ -21,8 +21,8 @@ #include #include -#include -#include +#include "../PathUtils.h" +#include "../NumericalConstants.h" #ifdef Q_OS_WIN #include @@ -87,6 +87,12 @@ FileCache::~FileCache() { } void FileCache::initialize() { + Lock lock(_mutex); + if (_initialized) { + qCWarning(file_cache) << "File cache already initialized"; + return; + } + QDir dir(_dirpath.c_str()); if (dir.exists()) { @@ -120,21 +126,31 @@ FilePointer FileCache::addFile(Metadata&& metadata, const std::string& filepath) file->_cache = this; emit dirty(); - Lock lock(_filesMutex); _files[file->getKey()] = file; } return file; } FilePointer FileCache::writeFile(const char* data, File::Metadata&& metadata, bool overwrite) { - assert(_initialized); + FilePointer file; + + if (0 == metadata.length) { + qCWarning(file_cache) << "Cannot store empty files in the cache"; + return file; + } + + + Lock lock(_mutex); + + if (!_initialized) { + qCWarning(file_cache) << "File cache used before initialization"; + return file; + } std::string filepath = getFilepath(metadata.key); - Lock lock(_filesMutex); - // if file already exists, return it - FilePointer file = getFile(metadata.key); + file = getFile(metadata.key); if (file) { if (!overwrite) { qCWarning(file_cache, "[%s] Attempted to overwrite %s", _dirname.c_str(), metadata.key.c_str()); @@ -158,12 +174,15 @@ FilePointer FileCache::writeFile(const char* data, File::Metadata&& metadata, bo return file; } + FilePointer FileCache::getFile(const Key& key) { - assert(_initialized); + Lock lock(_mutex); FilePointer file; - - Lock lock(_filesMutex); + if (!_initialized) { + qCWarning(file_cache) << "File cache used before initialization"; + return file; + } // check if file exists const auto it = _files.find(key); @@ -172,7 +191,10 @@ FilePointer FileCache::getFile(const Key& key) { if (file) { file->touch(); // if it exists, it is active - remove it from the cache - removeUnusedFile(file); + if (_unusedFiles.erase(file)) { + _numUnusedFiles -= 1; + _unusedFilesSize -= file->getLength(); + } qCDebug(file_cache, "[%s] Found %s", _dirname.c_str(), key.c_str()); emit dirty(); } else { @@ -188,31 +210,19 @@ std::string FileCache::getFilepath(const Key& key) { return _dirpath + DIR_SEP + key + EXT_SEP + _ext; } +// This is a non-public function that uses the mutex because it's +// essentially a public function specifically to a File object void FileCache::addUnusedFile(const FilePointer& file) { - { - Lock lock(_filesMutex); - _files[file->getKey()] = file; - } - - { - Lock lock(_unusedFilesMutex); - _unusedFiles.insert(file); - _numUnusedFiles += 1; - _unusedFilesSize += file->getLength(); - } + Lock lock(_mutex); + _files[file->getKey()] = file; + _unusedFiles.insert(file); + _numUnusedFiles += 1; + _unusedFilesSize += file->getLength(); clean(); emit dirty(); } -void FileCache::removeUnusedFile(const FilePointer& file) { - Lock lock(_unusedFilesMutex); - if (_unusedFiles.erase(file)) { - _numUnusedFiles -= 1; - _unusedFilesSize -= file->getLength(); - } -} - size_t FileCache::getOverbudgetAmount() const { size_t result = 0; @@ -241,20 +251,13 @@ void FileCache::eject(const FilePointer& file) { const auto& length = file->getLength(); const auto& key = file->getKey(); - { - Lock lock(_filesMutex); - if (0 != _files.erase(key)) { - _numTotalFiles -= 1; - _totalFilesSize -= length; - } + if (0 != _files.erase(key)) { + _numTotalFiles -= 1; + _totalFilesSize -= length; } - - { - Lock unusedLock(_unusedFilesMutex); - if (0 != _unusedFiles.erase(file)) { - _numUnusedFiles -= 1; - _unusedFilesSize -= length; - } + if (0 != _unusedFiles.erase(file)) { + _numUnusedFiles -= 1; + _unusedFilesSize -= length; } } @@ -266,7 +269,6 @@ void FileCache::clean() { return; } - Lock unusedLock(_unusedFilesMutex); using Queue = std::priority_queue, FilePointerComparator>; Queue queue; for (const auto& file : _unusedFiles) { @@ -283,18 +285,19 @@ void FileCache::clean() { } void FileCache::wipe() { - Lock unusedFilesLock(_unusedFilesMutex); + Lock lock(_mutex); while (!_unusedFiles.empty()) { eject(*_unusedFiles.begin()); } } void FileCache::clear() { + Lock lock(_mutex); + // Eliminate any overbudget files clean(); - // Mark everything remaining as persisted - Lock unusedFilesLock(_unusedFilesMutex); + // Mark everything remaining as persisted while effectively ejecting from the cache for (auto& file : _unusedFiles) { file->_shouldPersist = true; file->_cache = nullptr; @@ -329,4 +332,4 @@ File::~File() { void File::touch() { utime(_filepath.c_str(), nullptr); _modified = std::max(QFileInfo(_filepath.c_str()).lastRead().toMSecsSinceEpoch(), _modified); -} \ No newline at end of file +} diff --git a/libraries/networking/src/FileCache.h b/libraries/shared/src/shared/FileCache.h similarity index 95% rename from libraries/networking/src/FileCache.h rename to libraries/shared/src/shared/FileCache.h index f29d75f779..25210af1da 100644 --- a/libraries/networking/src/FileCache.h +++ b/libraries/shared/src/shared/FileCache.h @@ -108,7 +108,6 @@ private: FilePointer addFile(Metadata&& metadata, const std::string& filepath); void addUnusedFile(const FilePointer& file); - void removeUnusedFile(const FilePointer& file); void clean(); void clear(); // Remove a file from the cache @@ -125,16 +124,14 @@ private: std::atomic _totalFilesSize { 0 }; std::atomic _unusedFilesSize { 0 }; - std::string _ext; - std::string _dirname; - std::string _dirpath; + const std::string _ext; + const std::string _dirname; + const std::string _dirpath; bool _initialized { false }; + Mutex _mutex; Map _files; - Mutex _filesMutex; - Set _unusedFiles; - Mutex _unusedFilesMutex; }; class File : public QObject { @@ -146,7 +143,7 @@ public: const Key& getKey() const { return _key; } const size_t& getLength() const { return _length; } - std::string getFilepath() const { return _filepath; } + const std::string& getFilepath() const { return _filepath; } virtual ~File(); /// overrides should call File::deleter to maintain caching behavior diff --git a/libraries/trackers/CMakeLists.txt b/libraries/trackers/CMakeLists.txt index 0999a45b59..6943f1a197 100644 --- a/libraries/trackers/CMakeLists.txt +++ b/libraries/trackers/CMakeLists.txt @@ -2,5 +2,6 @@ set(TARGET_NAME trackers) setup_hifi_library() GroupSources("src") link_hifi_libraries(shared) +include_hifi_library_headers(octree) target_bullet() diff --git a/libraries/ui-plugins/CMakeLists.txt b/libraries/ui-plugins/CMakeLists.txt index 9ce189b117..8da0815082 100644 --- a/libraries/ui-plugins/CMakeLists.txt +++ b/libraries/ui-plugins/CMakeLists.txt @@ -1,3 +1,4 @@ set(TARGET_NAME ui-plugins) setup_hifi_library(OpenGL) link_hifi_libraries(shared plugins ui) +include_hifi_library_headers(gpu) \ No newline at end of file diff --git a/plugins/oculus/src/OculusDisplayPlugin.cpp b/plugins/oculus/src/OculusDisplayPlugin.cpp index db8c92ac23..0fd8467e38 100644 --- a/plugins/oculus/src/OculusDisplayPlugin.cpp +++ b/plugins/oculus/src/OculusDisplayPlugin.cpp @@ -244,7 +244,7 @@ QString OculusDisplayPlugin::getPreferredAudioInDevice() const { if (!OVR_SUCCESS(ovr_GetAudioDeviceInGuidStr(buffer))) { return QString(); } - return AudioClient::friendlyNameForAudioDevice(buffer); + return AudioClient::getWinDeviceName(buffer); } QString OculusDisplayPlugin::getPreferredAudioOutDevice() const { @@ -252,7 +252,7 @@ QString OculusDisplayPlugin::getPreferredAudioOutDevice() const { if (!OVR_SUCCESS(ovr_GetAudioDeviceOutGuidStr(buffer))) { return QString(); } - return AudioClient::friendlyNameForAudioDevice(buffer); + return AudioClient::getWinDeviceName(buffer); } OculusDisplayPlugin::~OculusDisplayPlugin() { diff --git a/plugins/openvr/CMakeLists.txt b/plugins/openvr/CMakeLists.txt index af65f3dbf7..6a95ef6d76 100644 --- a/plugins/openvr/CMakeLists.txt +++ b/plugins/openvr/CMakeLists.txt @@ -10,10 +10,10 @@ if (WIN32) # we're using static GLEW, so define GLEW_STATIC add_definitions(-DGLEW_STATIC) set(TARGET_NAME openvr) - setup_hifi_plugin(OpenGL Script Qml Widgets) + setup_hifi_plugin(OpenGL Script Qml Widgets Multimedia) link_hifi_libraries(shared gl networking controllers ui plugins display-plugins ui-plugins input-plugins script-engine - render-utils model gpu gpu-gl render model-networking fbx ktx image procedural) + audio-client render-utils model gpu gpu-gl render model-networking fbx ktx image procedural) include_hifi_library_headers(octree) @@ -21,4 +21,5 @@ if (WIN32) find_package(OpenVR REQUIRED) target_include_directories(${TARGET_NAME} PRIVATE ${OPENVR_INCLUDE_DIRS}) target_link_libraries(${TARGET_NAME} ${OPENVR_LIBRARIES}) + target_link_libraries(${TARGET_NAME} Winmm.lib) endif() diff --git a/plugins/openvr/src/OpenVrDisplayPlugin.cpp b/plugins/openvr/src/OpenVrDisplayPlugin.cpp index 8105de7a13..7a73c91c7d 100644 --- a/plugins/openvr/src/OpenVrDisplayPlugin.cpp +++ b/plugins/openvr/src/OpenVrDisplayPlugin.cpp @@ -7,6 +7,9 @@ // #include "OpenVrDisplayPlugin.h" +// Odd ordering of header is required to avoid 'macro redinition warnings' +#include + #include #include #include @@ -713,3 +716,30 @@ bool OpenVrDisplayPlugin::isKeyboardVisible() { int OpenVrDisplayPlugin::getRequiredThreadCount() const { return Parent::getRequiredThreadCount() + (_threadedSubmit ? 1 : 0); } + +QString OpenVrDisplayPlugin::getPreferredAudioInDevice() const { + QString device = getVrSettingString(vr::k_pch_audio_Section, vr::k_pch_audio_OnPlaybackDevice_String); + if (!device.isEmpty()) { + static const WCHAR INIT = 0; + size_t size = device.size() + 1; + std::vector deviceW; + deviceW.assign(size, INIT); + device.toWCharArray(deviceW.data()); + device = AudioClient::getWinDeviceName(deviceW.data()); + } + return device; +} + +QString OpenVrDisplayPlugin::getPreferredAudioOutDevice() const { + QString device = getVrSettingString(vr::k_pch_audio_Section, vr::k_pch_audio_OnRecordDevice_String); + if (!device.isEmpty()) { + static const WCHAR INIT = 0; + size_t size = device.size() + 1; + std::vector deviceW; + deviceW.assign(size, INIT); + device.toWCharArray(deviceW.data()); + device = AudioClient::getWinDeviceName(deviceW.data()); + } + return device; +} + diff --git a/plugins/openvr/src/OpenVrDisplayPlugin.h b/plugins/openvr/src/OpenVrDisplayPlugin.h index 01e02c9892..a1bbed8754 100644 --- a/plugins/openvr/src/OpenVrDisplayPlugin.h +++ b/plugins/openvr/src/OpenVrDisplayPlugin.h @@ -58,6 +58,9 @@ public: // Possibly needs an additional thread for VR submission int getRequiredThreadCount() const override; + QString getPreferredAudioInDevice() const override; + QString getPreferredAudioOutDevice() const override; + protected: bool internalActivate() override; void internalDeactivate() override; diff --git a/plugins/openvr/src/OpenVrHelpers.cpp b/plugins/openvr/src/OpenVrHelpers.cpp index d9db757b2f..7e287a16c3 100644 --- a/plugins/openvr/src/OpenVrHelpers.cpp +++ b/plugins/openvr/src/OpenVrHelpers.cpp @@ -72,6 +72,21 @@ bool openVrSupported() { return (enableDebugOpenVR || !isOculusPresent()) && vr::VR_IsHmdPresent(); } +QString getVrSettingString(const char* section, const char* setting) { + QString result; + static const uint32_t BUFFER_SIZE = 1024; + static char BUFFER[BUFFER_SIZE]; + vr::IVRSettings * vrSettings = vr::VRSettings(); + if (vrSettings) { + vr::EVRSettingsError error = vr::VRSettingsError_None; + vrSettings->GetString(vr::k_pch_audio_Section, vr::k_pch_audio_OnPlaybackDevice_String, BUFFER, BUFFER_SIZE, &error); + if (error == vr::VRSettingsError_None) { + result = BUFFER; + } + } + return result; +} + vr::IVRSystem* acquireOpenVrSystem() { bool hmdPresent = vr::VR_IsHmdPresent(); if (hmdPresent) { @@ -82,6 +97,7 @@ vr::IVRSystem* acquireOpenVrSystem() { #endif vr::EVRInitError eError = vr::VRInitError_None; activeHmd = vr::VR_Init(&eError, vr::VRApplication_Scene); + #if DEV_BUILD qCDebug(displayplugins) << "OpenVR display: HMD is " << activeHmd << " error is " << eError; #endif diff --git a/plugins/openvr/src/OpenVrHelpers.h b/plugins/openvr/src/OpenVrHelpers.h index f00cd9e117..f4253899a2 100644 --- a/plugins/openvr/src/OpenVrHelpers.h +++ b/plugins/openvr/src/OpenVrHelpers.h @@ -25,6 +25,7 @@ bool openVrQuitRequested(); void enableOpenVrKeyboard(PluginContainer* container); void disableOpenVrKeyboard(); bool isOpenVrKeyboardShown(); +QString getVrSettingString(const char* section, const char* setting); template diff --git a/plugins/openvr/src/ViveControllerManager.h b/plugins/openvr/src/ViveControllerManager.h index 9a27543e8f..dadaeb37db 100644 --- a/plugins/openvr/src/ViveControllerManager.h +++ b/plugins/openvr/src/ViveControllerManager.h @@ -23,8 +23,6 @@ #include #include #include -#include -#include #include "OpenVrHelpers.h" using PuckPosePair = std::pair; diff --git a/script-archive/libraries/overlayManager.js b/script-archive/libraries/overlayManager.js index 7e25cc04ec..cca9be8f75 100644 --- a/script-archive/libraries/overlayManager.js +++ b/script-archive/libraries/overlayManager.js @@ -269,9 +269,6 @@ "minorGridWidth", "majorGridEvery" ]); - LocalModelsOverlay = generateOverlayClass(Volume3DOverlay, "localmodels", [ - ]); - ModelOverlay = generateOverlayClass(Volume3DOverlay, "model", [ "url", "dimensions", "textures" ]); diff --git a/scripts/defaultScripts.js b/scripts/defaultScripts.js index aef8d9d85b..2270118861 100644 --- a/scripts/defaultScripts.js +++ b/scripts/defaultScripts.js @@ -25,7 +25,6 @@ var DEFAULT_SCRIPTS_COMBINED = [ "system/tablet-goto.js", "system/marketplaces/marketplaces.js", "system/edit.js", - "system/selectAudioDevice.js", "system/notifications.js", "system/dialTone.js", "system/firstPersonHMD.js", @@ -33,7 +32,7 @@ var DEFAULT_SCRIPTS_COMBINED = [ ]; var DEFAULT_SCRIPTS_SEPARATE = [ "system/controllers/controllerScripts.js", - "system/chat.js" + // "system/chat.js" ]; // add a menu item for debugging diff --git a/scripts/developer/hmdRollControlDisable.js b/scripts/developer/hmdRollControlDisable.js new file mode 100644 index 0000000000..fe8a85e3e5 --- /dev/null +++ b/scripts/developer/hmdRollControlDisable.js @@ -0,0 +1,17 @@ +// +// hmdRollControlDisable.js +// +// Created by David Rowe on 4 Jun 2017. +// Copyright 2017 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0 +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +var hmdRollControlEnabled = false; + +//print("HMD roll control: " + hmdRollControlEnabled); + +MyAvatar.hmdRollControlEnabled = hmdRollControlEnabled; + +Script.stop(); diff --git a/scripts/developer/hmdRollControlEnable.js b/scripts/developer/hmdRollControlEnable.js new file mode 100644 index 0000000000..81318b7ddd --- /dev/null +++ b/scripts/developer/hmdRollControlEnable.js @@ -0,0 +1,21 @@ +// +// hmdRollControlEnable.js +// +// Created by David Rowe on 4 Jun 2017. +// Copyright 2017 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0 +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +var hmdRollControlEnabled = true; +var hmdRollControlDeadZone = 8.0; // deg +var hmdRollControlRate = 2.5; // deg/sec/deg + +//print("HMD roll control: " + hmdRollControlEnabled + ", " + hmdRollControlDeadZone + ", " + hmdRollControlRate); + +MyAvatar.hmdRollControlEnabled = hmdRollControlEnabled; +MyAvatar.hmdRollControlDeadZone = hmdRollControlDeadZone; +MyAvatar.hmdRollControlRate = hmdRollControlRate; + +Script.stop(); diff --git a/scripts/system/audio.js b/scripts/system/audio.js index 7bc8676a2e..cb9589ae9e 100644 --- a/scripts/system/audio.js +++ b/scripts/system/audio.js @@ -27,7 +27,7 @@ var UNMUTE_ICONS = { }; function onMuteToggled() { - if (AudioDevice.getMuted()) { + if (Audio.muted) { button.editProperties(MUTE_ICONS); } else { button.editProperties(UNMUTE_ICONS); @@ -46,7 +46,7 @@ function onClicked() { Entities.editEntity(entity, { textures: JSON.stringify({ "tex.close": HOME_BUTTON_TEXTURE }) }); shouldActivateButton = true; shouldActivateButton = true; - tablet.loadQMLSource("../Audio.qml"); + tablet.loadQMLSource("../audio/Audio.qml"); onAudioScreen = true; } } @@ -60,8 +60,8 @@ function onScreenChanged(type, url) { var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); var button = tablet.addButton({ - icon: AudioDevice.getMuted() ? MUTE_ICONS.icon : UNMUTE_ICONS.icon, - activeIcon: AudioDevice.getMuted() ? MUTE_ICONS.activeIcon : UNMUTE_ICONS.activeIcon, + icon: Audio.muted ? MUTE_ICONS.icon : UNMUTE_ICONS.icon, + activeIcon: Audio.muted ? MUTE_ICONS.activeIcon : UNMUTE_ICONS.activeIcon, text: TABLET_BUTTON_NAME, sortOrder: 1 }); @@ -70,7 +70,7 @@ onMuteToggled(); button.clicked.connect(onClicked); tablet.screenChanged.connect(onScreenChanged); -AudioDevice.muteToggled.connect(onMuteToggled); +Audio.mutedChanged.connect(onMuteToggled); Script.scriptEnding.connect(function () { if (onAudioScreen) { @@ -78,7 +78,7 @@ Script.scriptEnding.connect(function () { } button.clicked.disconnect(onClicked); tablet.screenChanged.disconnect(onScreenChanged); - AudioDevice.muteToggled.disconnect(onMuteToggled); + Audio.mutedChanged.disconnect(onMuteToggled); tablet.removeButton(button); }); diff --git a/scripts/system/audioMuteOverlay.js b/scripts/system/audioMuteOverlay.js index cf07402d64..731d62017d 100644 --- a/scripts/system/audioMuteOverlay.js +++ b/scripts/system/audioMuteOverlay.js @@ -39,7 +39,7 @@ Script.scriptEnding.connect(cleanup); function update(dt) { - if (!AudioDevice.getMuted()) { + if (!Audio.muted) { if (hasOverlay()) { deleteOverlay(); } @@ -47,7 +47,7 @@ createOverlay(); } else { updateOverlay(); - } + } } function getOffsetPosition() { @@ -98,7 +98,7 @@ function cleanup() { deleteOverlay(); - AudioDevice.muteToggled.disconnect(onMuteToggled); + Audio.muted.disconnect(onMuteToggled); Script.update.disconnect(update); } }()); // END LOCAL_SCOPE diff --git a/scripts/system/away.js b/scripts/system/away.js index 4ca938d492..f98408376d 100644 --- a/scripts/system/away.js +++ b/scripts/system/away.js @@ -196,9 +196,9 @@ MyAvatar.wentActive.connect(setActiveProperties) function setAwayProperties() { isAway = true; - wasMuted = AudioDevice.getMuted(); + wasMuted = Audio.muted; if (!wasMuted) { - AudioDevice.toggleMute(); + Audio.muted = !Audio.muted; } MyAvatar.setEnableMeshVisible(false); // just for our own display, without changing point of view playAwayAnimation(); // animation is still seen by others @@ -221,7 +221,7 @@ function setAwayProperties() { function setActiveProperties() { isAway = false; if (!wasMuted) { - AudioDevice.toggleMute(); + Audio.muted = !Audio.muted; } MyAvatar.setEnableMeshVisible(true); // IWBNI we respected Developer->Avatar->Draw Mesh setting. stopAwayAnimation(); diff --git a/scripts/system/dialTone.js b/scripts/system/dialTone.js index 02624645d5..7b693aa2de 100644 --- a/scripts/system/dialTone.js +++ b/scripts/system/dialTone.js @@ -33,8 +33,8 @@ Audio.disconnected.connect(function(){ Audio.playSound(disconnectSound, soundOptions); }); -AudioDevice.muteToggled.connect(function () { - if (AudioDevice.getMuted()) { +Audio.mutedChanged.connect(function () { + if (Audio.muted) { Audio.playSound(micMutedSound, soundOptions); } }); diff --git a/scripts/system/edit.js b/scripts/system/edit.js index 2c3cc496dc..a3583e7808 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -12,7 +12,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -/* global Script, SelectionDisplay, LightOverlayManager, CameraManager, Grid, GridTool, EntityListTool, Vec3, SelectionManager, Overlays, OverlayWebWindow, UserActivityLogger, +/* global Script, SelectionDisplay, LightOverlayManager, CameraManager, Grid, GridTool, EntityListTool, Vec3, SelectionManager, Overlays, OverlayWebWindow, UserActivityLogger, Settings, Entities, Tablet, Toolbars, Messages, Menu, Camera, progressDialog, tooltip, MyAvatar, Quat, Controller, Clipboard, HMD, UndoStack, ParticleExplorerTool */ (function() { // BEGIN LOCAL_SCOPE @@ -80,27 +80,7 @@ selectionManager.addEventListener(function () { } var type = Entities.getEntityProperties(selectedEntityID, "type").type; if (type === "ParticleEffect") { - // Destroy the old particles web view first - particleExplorerTool.destroyWebView(); - particleExplorerTool.createWebView(); - var properties = Entities.getEntityProperties(selectedEntityID); - var particleData = { - messageType: "particle_settings", - currentProperties: properties - }; - selectedParticleEntityID = selectedEntityID; - particleExplorerTool.setActiveParticleEntity(selectedParticleEntityID); - - particleExplorerTool.webView.webEventReceived.connect(function (data) { - data = JSON.parse(data); - if (data.messageType === "page_loaded") { - particleExplorerTool.webView.emitScriptEvent(JSON.stringify(particleData)); - } - }); - - // Switch to particle explorer - var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); - tablet.sendToQml({method: 'selectTab', params: {id: 'particle'}}); + selectParticleEntity(selectedEntityID); } else { needToDestroyParticleExplorer = true; } @@ -218,7 +198,7 @@ function hideMarketplace() { // } function adjustPositionPerBoundingBox(position, direction, registration, dimensions, orientation) { - // Adjust the position such that the bounding box (registration, dimenions, and orientation) lies behind the original + // Adjust the position such that the bounding box (registration, dimenions, and orientation) lies behind the original // position in the given direction. var CORNERS = [ { x: 0, y: 0, z: 0 }, @@ -1373,7 +1353,7 @@ function parentSelectedEntities() { } }); - if(parentCheck) { + if (parentCheck) { Window.notify("Entities parented"); }else { Window.notify("Entities are already parented to last"); @@ -1575,7 +1555,7 @@ function importSVO(importURL) { var properties = Entities.getEntityProperties(pastedEntityIDs[0], ["position", "dimensions", "registrationPoint"]); var position = Vec3.sum(deltaPosition, properties.position); - position = grid.snapToSurface(grid.snapToGrid(position, false, properties.dimensions, + position = grid.snapToSurface(grid.snapToGrid(position, false, properties.dimensions, properties.registrationPoint), properties.dimensions, properties.registrationPoint); deltaPosition = Vec3.subtract(position, properties.position); } @@ -1907,11 +1887,11 @@ var PropertiesTool = function (opts) { } pushCommandForSelections(); selectionManager._update(); - } else if(data.type === 'parent') { + } else if (data.type === 'parent') { parentSelectedEntities(); - } else if(data.type === 'unparent') { + } else if (data.type === 'unparent') { unparentSelectedEntities(); - } else if(data.type === 'saveUserData'){ + } else if (data.type === 'saveUserData'){ //the event bridge and json parsing handle our avatar id string differently. var actualID = data.id.split('"')[1]; Entities.editEntity(actualID, data.properties); @@ -2203,6 +2183,10 @@ var selectedParticleEntityID = null; function selectParticleEntity(entityID) { var properties = Entities.getEntityProperties(entityID); + + if (properties.emitOrientation) { + properties.emitOrientation = Quat.safeEulerAngles(properties.emitOrientation); + } var particleData = { messageType: "particle_settings", currentProperties: properties @@ -2212,6 +2196,7 @@ function selectParticleEntity(entityID) { selectedParticleEntity = entityID; particleExplorerTool.setActiveParticleEntity(entityID); + particleExplorerTool.webView.emitScriptEvent(JSON.stringify(particleData)); // Switch to particle explorer @@ -2229,7 +2214,7 @@ entityListTool.webView.webEventReceived.connect(function (data) { if (data.type === 'parent') { parentSelectedEntities(); - } else if(data.type === 'unparent') { + } else if (data.type === 'unparent') { unparentSelectedEntities(); } else if (data.type === "selectionUpdate") { var ids = data.entityIds; @@ -2250,4 +2235,3 @@ entityListTool.webView.webEventReceived.connect(function (data) { }); }()); // END LOCAL_SCOPE - diff --git a/scripts/system/html/css/hifi-style.css b/scripts/system/html/css/hifi-style.css index ec6cd1a402..e1e4f67723 100644 --- a/scripts/system/html/css/hifi-style.css +++ b/scripts/system/html/css/hifi-style.css @@ -172,4 +172,4 @@ input[type=radio]:active + label > span > span{ } .blueButton:disabled { background-image: linear-gradient(#FFFFFF, #AFAFAF); -} \ No newline at end of file +} diff --git a/scripts/system/notifications.js b/scripts/system/notifications.js index 6429d6e0c6..b34630f3f8 100644 --- a/scripts/system/notifications.js +++ b/scripts/system/notifications.js @@ -26,7 +26,7 @@ // // 1. Set the Event Connector at the bottom of the script. // example: -// AudioDevice.muteToggled.connect(onMuteStateChanged); +// Audio.mutedChanged.connect(onMuteStateChanged); // // 2. Create a new function to produce a text string, do not include new line returns. // example: @@ -34,7 +34,7 @@ // var muteState, // muteString; // -// muteState = AudioDevice.getMuted() ? "muted" : "unmuted"; +// muteState = Audio.muted ? "muted" : "unmuted"; // muteString = "Microphone is now " + muteState; // createNotification(muteString, NotificationType.MUTE_TOGGLE); // } diff --git a/scripts/system/particle_explorer/dat.gui.min.js b/scripts/system/particle_explorer/dat.gui.min.js deleted file mode 100644 index 8ea141a966..0000000000 --- a/scripts/system/particle_explorer/dat.gui.min.js +++ /dev/null @@ -1,95 +0,0 @@ -/** - * dat-gui JavaScript Controller Library - * http://code.google.com/p/dat-gui - * - * Copyright 2011 Data Arts Team, Google Creative Lab - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - */ -var dat=dat||{};dat.gui=dat.gui||{};dat.utils=dat.utils||{};dat.controllers=dat.controllers||{};dat.dom=dat.dom||{};dat.color=dat.color||{};dat.utils.css=function(){return{load:function(f,a){a=a||document;var d=a.createElement("link");d.type="text/css";d.rel="stylesheet";d.href=f;a.getElementsByTagName("head")[0].appendChild(d)},inject:function(f,a){a=a||document;var d=document.createElement("style");d.type="text/css";d.innerHTML=f;a.getElementsByTagName("head")[0].appendChild(d)}}}(); -dat.utils.common=function(){var f=Array.prototype.forEach,a=Array.prototype.slice;return{BREAK:{},extend:function(d){this.each(a.call(arguments,1),function(a){for(var c in a)this.isUndefined(a[c])||(d[c]=a[c])},this);return d},defaults:function(d){this.each(a.call(arguments,1),function(a){for(var c in a)this.isUndefined(d[c])&&(d[c]=a[c])},this);return d},compose:function(){var d=a.call(arguments);return function(){for(var e=a.call(arguments),c=d.length-1;0<=c;c--)e=[d[c].apply(this,e)];return e[0]}}, -each:function(a,e,c){if(a)if(f&&a.forEach&&a.forEach===f)a.forEach(e,c);else if(a.length===a.length+0)for(var b=0,p=a.length;bthis.__max&&(a=this.__max);void 0!==this.__step&&0!=a%this.__step&&(a=Math.round(a/this.__step)*this.__step);return e.superclass.prototype.setValue.call(this,a)},min:function(a){this.__min=a;return this},max:function(a){this.__max=a;return this},step:function(a){this.__impliedStep=this.__step=a;this.__precision=d(a);return this}});return e}(dat.controllers.Controller,dat.utils.common); -dat.controllers.NumberControllerBox=function(f,a,d){var e=function(c,b,f){function q(){var a=parseFloat(n.__input.value);d.isNaN(a)||n.setValue(a)}function l(a){var b=u-a.clientY;n.setValue(n.getValue()+b*n.__impliedStep);u=a.clientY}function r(){a.unbind(window,"mousemove",l);a.unbind(window,"mouseup",r)}this.__truncationSuspended=!1;e.superclass.call(this,c,b,f);var n=this,u;this.__input=document.createElement("input");this.__input.setAttribute("type","text");a.bind(this.__input,"change",q);a.bind(this.__input, -"blur",function(){q();n.__onFinishChange&&n.__onFinishChange.call(n,n.getValue())});a.bind(this.__input,"mousedown",function(b){a.bind(window,"mousemove",l);a.bind(window,"mouseup",r);u=b.clientY});a.bind(this.__input,"keydown",function(a){13===a.keyCode&&(n.__truncationSuspended=!0,this.blur(),n.__truncationSuspended=!1)});this.updateDisplay();this.domElement.appendChild(this.__input)};e.superclass=f;d.extend(e.prototype,f.prototype,{updateDisplay:function(){var a=this.__input,b;if(this.__truncationSuspended)b= -this.getValue();else{b=this.getValue();var d=Math.pow(10,this.__precision);b=Math.round(b*d)/d}a.value=b;return e.superclass.prototype.updateDisplay.call(this)}});return e}(dat.controllers.NumberController,dat.dom.dom,dat.utils.common); -dat.controllers.NumberControllerSlider=function(f,a,d,e,c){function b(a,b,c,e,d){return e+(a-b)/(c-b)*(d-e)}var p=function(c,e,d,f,u){function A(c){c.preventDefault();var e=a.getOffset(k.__background),d=a.getWidth(k.__background);k.setValue(b(c.clientX,e.left,e.left+d,k.__min,k.__max));return!1}function g(){a.unbind(window,"mousemove",A);a.unbind(window,"mouseup",g);k.__onFinishChange&&k.__onFinishChange.call(k,k.getValue())}p.superclass.call(this,c,e,{min:d,max:f,step:u});var k=this;this.__background= -document.createElement("div");this.__foreground=document.createElement("div");a.bind(this.__background,"mousedown",function(b){a.bind(window,"mousemove",A);a.bind(window,"mouseup",g);A(b)});a.addClass(this.__background,"slider");a.addClass(this.__foreground,"slider-fg");this.updateDisplay();this.__background.appendChild(this.__foreground);this.domElement.appendChild(this.__background)};p.superclass=f;p.useDefaultStyles=function(){d.inject(c)};e.extend(p.prototype,f.prototype,{updateDisplay:function(){var a= -(this.getValue()-this.__min)/(this.__max-this.__min);this.__foreground.style.width=100*a+"%";return p.superclass.prototype.updateDisplay.call(this)}});return p}(dat.controllers.NumberController,dat.dom.dom,dat.utils.css,dat.utils.common,"/**\n * dat-gui JavaScript Controller Library\n * http://code.google.com/p/dat-gui\n *\n * Copyright 2011 Data Arts Team, Google Creative Lab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n */\n\n.slider {\n box-shadow: inset 0 2px 4px rgba(0,0,0,0.15);\n height: 1em;\n border-radius: 1em;\n background-color: #eee;\n padding: 0 0.5em;\n overflow: hidden;\n}\n\n.slider-fg {\n padding: 1px 0 2px 0;\n background-color: #aaa;\n height: 1em;\n margin-left: -0.5em;\n padding-right: 0.5em;\n border-radius: 1em 0 0 1em;\n}\n\n.slider-fg:after {\n display: inline-block;\n border-radius: 1em;\n background-color: #fff;\n border: 1px solid #aaa;\n content: '';\n float: right;\n margin-right: -1em;\n margin-top: -1px;\n height: 0.9em;\n width: 0.9em;\n}"); -dat.controllers.FunctionController=function(f,a,d){var e=function(c,b,d){e.superclass.call(this,c,b);var f=this;this.__button=document.createElement("div");this.__button.innerHTML=void 0===d?"Fire":d;a.bind(this.__button,"click",function(a){a.preventDefault();f.fire();return!1});a.addClass(this.__button,"button");this.domElement.appendChild(this.__button)};e.superclass=f;d.extend(e.prototype,f.prototype,{fire:function(){this.__onChange&&this.__onChange.call(this);this.getValue().call(this.object); -this.__onFinishChange&&this.__onFinishChange.call(this,this.getValue())}});return e}(dat.controllers.Controller,dat.dom.dom,dat.utils.common); -dat.controllers.BooleanController=function(f,a,d){var e=function(c,b){e.superclass.call(this,c,b);var d=this;this.__prev=this.getValue();this.__checkbox=document.createElement("input");this.__checkbox.setAttribute("type","checkbox");a.bind(this.__checkbox,"change",function(){d.setValue(!d.__prev)},!1);this.domElement.appendChild(this.__checkbox);this.updateDisplay()};e.superclass=f;d.extend(e.prototype,f.prototype,{setValue:function(a){a=e.superclass.prototype.setValue.call(this,a);this.__onFinishChange&& -this.__onFinishChange.call(this,this.getValue());this.__prev=this.getValue();return a},updateDisplay:function(){!0===this.getValue()?(this.__checkbox.setAttribute("checked","checked"),this.__checkbox.checked=!0):this.__checkbox.checked=!1;return e.superclass.prototype.updateDisplay.call(this)}});return e}(dat.controllers.Controller,dat.dom.dom,dat.utils.common); -dat.color.toString=function(f){return function(a){if(1==a.a||f.isUndefined(a.a)){for(a=a.hex.toString(16);6>a.length;)a="0"+a;return"#"+a}return"rgba("+Math.round(a.r)+","+Math.round(a.g)+","+Math.round(a.b)+","+a.a+")"}}(dat.utils.common); -dat.color.interpret=function(f,a){var d,e,c=[{litmus:a.isString,conversions:{THREE_CHAR_HEX:{read:function(a){a=a.match(/^#([A-F0-9])([A-F0-9])([A-F0-9])$/i);return null===a?!1:{space:"HEX",hex:parseInt("0x"+a[1].toString()+a[1].toString()+a[2].toString()+a[2].toString()+a[3].toString()+a[3].toString())}},write:f},SIX_CHAR_HEX:{read:function(a){a=a.match(/^#([A-F0-9]{6})$/i);return null===a?!1:{space:"HEX",hex:parseInt("0x"+a[1].toString())}},write:f},CSS_RGB:{read:function(a){a=a.match(/^rgb\(\s*(.+)\s*,\s*(.+)\s*,\s*(.+)\s*\)/); -return null===a?!1:{space:"RGB",r:parseFloat(a[1]),g:parseFloat(a[2]),b:parseFloat(a[3])}},write:f},CSS_RGBA:{read:function(a){a=a.match(/^rgba\(\s*(.+)\s*,\s*(.+)\s*,\s*(.+)\s*\,\s*(.+)\s*\)/);return null===a?!1:{space:"RGB",r:parseFloat(a[1]),g:parseFloat(a[2]),b:parseFloat(a[3]),a:parseFloat(a[4])}},write:f}}},{litmus:a.isNumber,conversions:{HEX:{read:function(a){return{space:"HEX",hex:a,conversionName:"HEX"}},write:function(a){return a.hex}}}},{litmus:a.isArray,conversions:{RGB_ARRAY:{read:function(a){return 3!= -a.length?!1:{space:"RGB",r:a[0],g:a[1],b:a[2]}},write:function(a){return[a.r,a.g,a.b]}},RGBA_ARRAY:{read:function(a){return 4!=a.length?!1:{space:"RGB",r:a[0],g:a[1],b:a[2],a:a[3]}},write:function(a){return[a.r,a.g,a.b,a.a]}}}},{litmus:a.isObject,conversions:{RGBA_OBJ:{read:function(b){return a.isNumber(b.r)&&a.isNumber(b.g)&&a.isNumber(b.b)&&a.isNumber(b.a)?{space:"RGB",r:b.r,g:b.g,b:b.b,a:b.a}:!1},write:function(a){return{r:a.r,g:a.g,b:a.b,a:a.a}}},RGB_OBJ:{read:function(b){return a.isNumber(b.r)&& -a.isNumber(b.g)&&a.isNumber(b.b)?{space:"RGB",r:b.r,g:b.g,b:b.b}:!1},write:function(a){return{r:a.r,g:a.g,b:a.b}}},HSVA_OBJ:{read:function(b){return a.isNumber(b.h)&&a.isNumber(b.s)&&a.isNumber(b.v)&&a.isNumber(b.a)?{space:"HSV",h:b.h,s:b.s,v:b.v,a:b.a}:!1},write:function(a){return{h:a.h,s:a.s,v:a.v,a:a.a}}},HSV_OBJ:{read:function(b){return a.isNumber(b.h)&&a.isNumber(b.s)&&a.isNumber(b.v)?{space:"HSV",h:b.h,s:b.s,v:b.v}:!1},write:function(a){return{h:a.h,s:a.s,v:a.v}}}}}];return function(){e=!1; -var b=1\n\n Here\'s the new load parameter for your GUI\'s constructor:\n\n \n\n
\n\n Automatically save\n values to localStorage on exit.\n\n
The values saved to localStorage will\n override those passed to dat.GUI\'s constructor. This makes it\n easier to work incrementally, but localStorage is fragile,\n and your friends may not see the same values you do.\n \n
\n \n
\n\n', -".dg {\n /** Clear list styles */\n /* Auto-place container */\n /* Auto-placed GUI's */\n /* Line items that don't contain folders. */\n /** Folder names */\n /** Hides closed items */\n /** Controller row */\n /** Name-half (left) */\n /** Controller-half (right) */\n /** Controller placement */\n /** Shorter number boxes when slider is present. */\n /** Ensure the entire boolean and function row shows a hand */ }\n .dg ul {\n list-style: none;\n margin: 0;\n padding: 0;\n width: 100%;\n clear: both; }\n .dg.ac {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n height: 0;\n z-index: 0; }\n .dg:not(.ac) .main {\n /** Exclude mains in ac so that we don't hide close button */\n overflow: hidden; }\n .dg.main {\n -webkit-transition: opacity 0.1s linear;\n -o-transition: opacity 0.1s linear;\n -moz-transition: opacity 0.1s linear;\n transition: opacity 0.1s linear; }\n .dg.main.taller-than-window {\n overflow-y: auto; }\n .dg.main.taller-than-window .close-button {\n opacity: 1;\n /* TODO, these are style notes */\n margin-top: -1px;\n border-top: 1px solid #2c2c2c; }\n .dg.main ul.closed .close-button {\n opacity: 1 !important; }\n .dg.main:hover .close-button,\n .dg.main .close-button.drag {\n opacity: 1; }\n .dg.main .close-button {\n /*opacity: 0;*/\n -webkit-transition: opacity 0.1s linear;\n -o-transition: opacity 0.1s linear;\n -moz-transition: opacity 0.1s linear;\n transition: opacity 0.1s linear;\n border: 0;\n position: absolute;\n line-height: 19px;\n height: 20px;\n /* TODO, these are style notes */\n cursor: pointer;\n text-align: center;\n background-color: #000; }\n .dg.main .close-button:hover {\n background-color: #111; }\n .dg.a {\n float: right;\n margin-right: 15px;\n overflow-x: hidden; }\n .dg.a.has-save > ul {\n margin-top: 27px; }\n .dg.a.has-save > ul.closed {\n margin-top: 0; }\n .dg.a .save-row {\n position: fixed;\n top: 0;\n z-index: 1002; }\n .dg li {\n -webkit-transition: height 0.1s ease-out;\n -o-transition: height 0.1s ease-out;\n -moz-transition: height 0.1s ease-out;\n transition: height 0.1s ease-out; }\n .dg li:not(.folder) {\n cursor: auto;\n height: 27px;\n line-height: 27px;\n overflow: hidden;\n padding: 0 4px 0 5px; }\n .dg li.folder {\n padding: 0;\n border-left: 4px solid rgba(0, 0, 0, 0); }\n .dg li.title {\n cursor: pointer;\n margin-left: -4px; }\n .dg .closed li:not(.title),\n .dg .closed ul li,\n .dg .closed ul li > * {\n height: 0;\n overflow: hidden;\n border: 0; }\n .dg .cr {\n clear: both;\n padding-left: 3px;\n height: 27px; }\n .dg .property-name {\n cursor: default;\n float: left;\n clear: left;\n width: 40%;\n overflow: hidden;\n text-overflow: ellipsis; }\n .dg .c {\n float: left;\n width: 60%; }\n .dg .c input[type=text] {\n border: 0;\n margin-top: 4px;\n padding: 3px;\n width: 100%;\n float: right; }\n .dg .has-slider input[type=text] {\n width: 30%;\n /*display: none;*/\n margin-left: 0; }\n .dg .slider {\n float: left;\n width: 66%;\n margin-left: -5px;\n margin-right: 0;\n height: 19px;\n margin-top: 4px; }\n .dg .slider-fg {\n height: 100%; }\n .dg .c input[type=checkbox] {\n margin-top: 9px; }\n .dg .c select {\n margin-top: 5px; }\n .dg .cr.function,\n .dg .cr.function .property-name,\n .dg .cr.function *,\n .dg .cr.boolean,\n .dg .cr.boolean * {\n cursor: pointer; }\n .dg .selector {\n display: none;\n position: absolute;\n margin-left: -9px;\n margin-top: 23px;\n z-index: 10; }\n .dg .c:hover .selector,\n .dg .selector.drag {\n display: block; }\n .dg li.save-row {\n padding: 0; }\n .dg li.save-row .button {\n display: inline-block;\n padding: 0px 6px; }\n .dg.dialogue {\n background-color: #222;\n width: 460px;\n padding: 15px;\n font-size: 13px;\n line-height: 15px; }\n\n/* TODO Separate style and structure */\n#dg-new-constructor {\n padding: 10px;\n color: #222;\n font-family: Monaco, monospace;\n font-size: 10px;\n border: 0;\n resize: none;\n box-shadow: inset 1px 1px 1px #888;\n word-wrap: break-word;\n margin: 12px 0;\n display: block;\n width: 440px;\n overflow-y: scroll;\n height: 100px;\n position: relative; }\n\n#dg-local-explain {\n display: none;\n font-size: 11px;\n line-height: 17px;\n border-radius: 3px;\n background-color: #333;\n padding: 8px;\n margin-top: 10px; }\n #dg-local-explain code {\n font-size: 10px; }\n\n#dat-gui-save-locally {\n display: none; }\n\n/** Main type */\n.dg {\n color: #eee;\n font: 11px 'Lucida Grande', sans-serif;\n text-shadow: 0 -1px 0 #111;\n /** Auto place */\n /* Controller row,
  • */\n /** Controllers */ }\n .dg.main {\n /** Scrollbar */ }\n .dg.main::-webkit-scrollbar {\n width: 5px;\n background: #1a1a1a; }\n .dg.main::-webkit-scrollbar-corner {\n height: 0;\n display: none; }\n .dg.main::-webkit-scrollbar-thumb {\n border-radius: 5px;\n background: #676767; }\n .dg li:not(.folder) {\n background: #1a1a1a;\n border-bottom: 1px solid #2c2c2c; }\n .dg li.save-row {\n line-height: 25px;\n background: #dad5cb;\n border: 0; }\n .dg li.save-row select {\n margin-left: 5px;\n width: 108px; }\n .dg li.save-row .button {\n margin-left: 5px;\n margin-top: 1px;\n border-radius: 2px;\n font-size: 9px;\n line-height: 7px;\n padding: 4px 4px 5px 4px;\n background: #c5bdad;\n color: #fff;\n text-shadow: 0 1px 0 #b0a58f;\n box-shadow: 0 -1px 0 #b0a58f;\n cursor: pointer; }\n .dg li.save-row .button.gears {\n background: #c5bdad url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAsAAAANCAYAAAB/9ZQ7AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAQJJREFUeNpiYKAU/P//PwGIC/ApCABiBSAW+I8AClAcgKxQ4T9hoMAEUrxx2QSGN6+egDX+/vWT4e7N82AMYoPAx/evwWoYoSYbACX2s7KxCxzcsezDh3evFoDEBYTEEqycggWAzA9AuUSQQgeYPa9fPv6/YWm/Acx5IPb7ty/fw+QZblw67vDs8R0YHyQhgObx+yAJkBqmG5dPPDh1aPOGR/eugW0G4vlIoTIfyFcA+QekhhHJhPdQxbiAIguMBTQZrPD7108M6roWYDFQiIAAv6Aow/1bFwXgis+f2LUAynwoIaNcz8XNx3Dl7MEJUDGQpx9gtQ8YCueB+D26OECAAQDadt7e46D42QAAAABJRU5ErkJggg==) 2px 1px no-repeat;\n height: 7px;\n width: 8px; }\n .dg li.save-row .button:hover {\n background-color: #bab19e;\n box-shadow: 0 -1px 0 #b0a58f; }\n .dg li.folder {\n border-bottom: 0; }\n .dg li.title {\n padding-left: 16px;\n background: black url(data:image/gif;base64,R0lGODlhBQAFAJEAAP////Pz8////////yH5BAEAAAIALAAAAAAFAAUAAAIIlI+hKgFxoCgAOw==) 6px 10px no-repeat;\n cursor: pointer;\n border-bottom: 1px solid rgba(255, 255, 255, 0.2); }\n .dg .closed li.title {\n background-image: url(data:image/gif;base64,R0lGODlhBQAFAJEAAP////Pz8////////yH5BAEAAAIALAAAAAAFAAUAAAIIlGIWqMCbWAEAOw==); }\n .dg .cr.boolean {\n border-left: 3px solid #806787; }\n .dg .cr.function {\n border-left: 3px solid #e61d5f; }\n .dg .cr.number {\n border-left: 3px solid #2fa1d6; }\n .dg .cr.number input[type=text] {\n color: #2fa1d6; }\n .dg .cr.string {\n border-left: 3px solid #1ed36f; }\n .dg .cr.string input[type=text] {\n color: #1ed36f; }\n .dg .cr.function:hover, .dg .cr.boolean:hover {\n background: #111; }\n .dg .c input[type=text] {\n background: #303030;\n outline: none; }\n .dg .c input[type=text]:hover {\n background: #3c3c3c; }\n .dg .c input[type=text]:focus {\n background: #494949;\n color: #fff; }\n .dg .c .slider {\n background: #303030;\n cursor: ew-resize; }\n .dg .c .slider-fg {\n background: #2fa1d6; }\n .dg .c .slider:hover {\n background: #3c3c3c; }\n .dg .c .slider:hover .slider-fg {\n background: #44abda; }\n", -dat.controllers.factory=function(f,a,d,e,c,b,p){return function(q,l,r,n){var u=q[l];if(p.isArray(r)||p.isObject(r))return new f(q,l,r);if(p.isNumber(u))return p.isNumber(r)&&p.isNumber(n)?new d(q,l,r,n):new a(q,l,{min:r,max:n});if(p.isString(u))return new e(q,l);if(p.isFunction(u))return new c(q,l,"");if(p.isBoolean(u))return new b(q,l)}}(dat.controllers.OptionController,dat.controllers.NumberControllerBox,dat.controllers.NumberControllerSlider,dat.controllers.StringController=function(f,a,d){var e= -function(c,b){function d(){f.setValue(f.__input.value)}e.superclass.call(this,c,b);var f=this;this.__input=document.createElement("input");this.__input.setAttribute("type","text");a.bind(this.__input,"keyup",d);a.bind(this.__input,"change",d);a.bind(this.__input,"blur",function(){f.__onFinishChange&&f.__onFinishChange.call(f,f.getValue())});a.bind(this.__input,"keydown",function(a){13===a.keyCode&&this.blur()});this.updateDisplay();this.domElement.appendChild(this.__input)};e.superclass=f;d.extend(e.prototype, -f.prototype,{updateDisplay:function(){a.isActive(this.__input)||(this.__input.value=this.getValue());return e.superclass.prototype.updateDisplay.call(this)}});return e}(dat.controllers.Controller,dat.dom.dom,dat.utils.common),dat.controllers.FunctionController,dat.controllers.BooleanController,dat.utils.common),dat.controllers.Controller,dat.controllers.BooleanController,dat.controllers.FunctionController,dat.controllers.NumberControllerBox,dat.controllers.NumberControllerSlider,dat.controllers.OptionController, -dat.controllers.ColorController=function(f,a,d,e,c){function b(a,b,d,e){a.style.background="";c.each(l,function(c){a.style.cssText+="background: "+c+"linear-gradient("+b+", "+d+" 0%, "+e+" 100%); "})}function p(a){a.style.background="";a.style.cssText+="background: -moz-linear-gradient(top, #ff0000 0%, #ff00ff 17%, #0000ff 34%, #00ffff 50%, #00ff00 67%, #ffff00 84%, #ff0000 100%);";a.style.cssText+="background: -webkit-linear-gradient(top, #ff0000 0%,#ff00ff 17%,#0000ff 34%,#00ffff 50%,#00ff00 67%,#ffff00 84%,#ff0000 100%);"; -a.style.cssText+="background: -o-linear-gradient(top, #ff0000 0%,#ff00ff 17%,#0000ff 34%,#00ffff 50%,#00ff00 67%,#ffff00 84%,#ff0000 100%);";a.style.cssText+="background: -ms-linear-gradient(top, #ff0000 0%,#ff00ff 17%,#0000ff 34%,#00ffff 50%,#00ff00 67%,#ffff00 84%,#ff0000 100%);";a.style.cssText+="background: linear-gradient(top, #ff0000 0%,#ff00ff 17%,#0000ff 34%,#00ffff 50%,#00ff00 67%,#ffff00 84%,#ff0000 100%);"}var q=function(f,n){function u(b){v(b);a.bind(window,"mousemove",v);a.bind(window, -"mouseup",l)}function l(){a.unbind(window,"mousemove",v);a.unbind(window,"mouseup",l)}function g(){var a=e(this.value);!1!==a?(t.__color.__state=a,t.setValue(t.__color.toOriginal())):this.value=t.__color.toString()}function k(){a.unbind(window,"mousemove",w);a.unbind(window,"mouseup",k)}function v(b){b.preventDefault();var c=a.getWidth(t.__saturation_field),d=a.getOffset(t.__saturation_field),e=(b.clientX-d.left+document.body.scrollLeft)/c;b=1-(b.clientY-d.top+document.body.scrollTop)/c;1 -b&&(b=0);1e&&(e=0);t.__color.v=b;t.__color.s=e;t.setValue(t.__color.toOriginal());return!1}function w(b){b.preventDefault();var c=a.getHeight(t.__hue_field),d=a.getOffset(t.__hue_field);b=1-(b.clientY-d.top+document.body.scrollTop)/c;1b&&(b=0);t.__color.h=360*b;t.setValue(t.__color.toOriginal());return!1}q.superclass.call(this,f,n);this.__color=new d(this.getValue());this.__temp=new d(0);var t=this;this.domElement=document.createElement("div");a.makeSelectable(this.domElement,!1); -this.__selector=document.createElement("div");this.__selector.className="selector";this.__saturation_field=document.createElement("div");this.__saturation_field.className="saturation-field";this.__field_knob=document.createElement("div");this.__field_knob.className="field-knob";this.__field_knob_border="2px solid ";this.__hue_knob=document.createElement("div");this.__hue_knob.className="hue-knob";this.__hue_field=document.createElement("div");this.__hue_field.className="hue-field";this.__input=document.createElement("input"); -this.__input.type="text";this.__input_textShadow="0 1px 1px ";a.bind(this.__input,"keydown",function(a){13===a.keyCode&&g.call(this)});a.bind(this.__input,"blur",g);a.bind(this.__selector,"mousedown",function(b){a.addClass(this,"drag").bind(window,"mouseup",function(b){a.removeClass(t.__selector,"drag")})});var y=document.createElement("div");c.extend(this.__selector.style,{width:"122px",height:"102px",padding:"3px",backgroundColor:"#222",boxShadow:"0px 1px 3px rgba(0,0,0,0.3)"});c.extend(this.__field_knob.style, -{position:"absolute",width:"12px",height:"12px",border:this.__field_knob_border+(.5>this.__color.v?"#fff":"#000"),boxShadow:"0px 1px 3px rgba(0,0,0,0.5)",borderRadius:"12px",zIndex:1});c.extend(this.__hue_knob.style,{position:"absolute",width:"15px",height:"2px",borderRight:"4px solid #fff",zIndex:1});c.extend(this.__saturation_field.style,{width:"100px",height:"100px",border:"1px solid #555",marginRight:"3px",display:"inline-block",cursor:"pointer"});c.extend(y.style,{width:"100%",height:"100%", -background:"none"});b(y,"top","rgba(0,0,0,0)","#000");c.extend(this.__hue_field.style,{width:"15px",height:"100px",display:"inline-block",border:"1px solid #555",cursor:"ns-resize"});p(this.__hue_field);c.extend(this.__input.style,{outline:"none",textAlign:"center",color:"#fff",border:0,fontWeight:"bold",textShadow:this.__input_textShadow+"rgba(0,0,0,0.7)"});a.bind(this.__saturation_field,"mousedown",u);a.bind(this.__field_knob,"mousedown",u);a.bind(this.__hue_field,"mousedown",function(b){w(b);a.bind(window, -"mousemove",w);a.bind(window,"mouseup",k)});this.__saturation_field.appendChild(y);this.__selector.appendChild(this.__field_knob);this.__selector.appendChild(this.__saturation_field);this.__selector.appendChild(this.__hue_field);this.__hue_field.appendChild(this.__hue_knob);this.domElement.appendChild(this.__input);this.domElement.appendChild(this.__selector);this.updateDisplay()};q.superclass=f;c.extend(q.prototype,f.prototype,{updateDisplay:function(){var a=e(this.getValue());if(!1!==a){var f=!1; -c.each(d.COMPONENTS,function(b){if(!c.isUndefined(a[b])&&!c.isUndefined(this.__color.__state[b])&&a[b]!==this.__color.__state[b])return f=!0,{}},this);f&&c.extend(this.__color.__state,a)}c.extend(this.__temp.__state,this.__color.__state);this.__temp.a=1;var l=.5>this.__color.v||.5a&&(a+=1);return{h:360*a,s:c/b,v:b/255}},rgb_to_hex:function(a,d,e){a=this.hex_with_component(0,2,a);a=this.hex_with_component(a,1,d);return a=this.hex_with_component(a,0,e)},component_from_hex:function(a,d){return a>>8*d&255},hex_with_component:function(a,d,e){return e<<(f=8*d)|a&~(255< 0) { + json[key] = document.getElementById(key) + .value; + } + } + + return json; + }, + fillFields: function (currentProperties) { + var self = this; + var fields = document.getElementsByTagName("input"); + + if (!currentProperties.locked) { + for (var i = 0; i < fields.length; i++) { + fields[i].removeAttribute("disabled"); + } + } + if (self.onSelect) { + self.onSelect(); + } + var keys = Object.keys(currentProperties); + + + for (var e in keys) { + if (keys.hasOwnProperty(e)) { + var value = keys[e]; + + var property = currentProperties[value]; + var field = self.builtRows[value]; + if (field) { + var el = document.getElementById(value); + + if (field.className.indexOf("radian") !== -1) { + el.value = property / RADIANS_PER_DEGREE; + el.onchange({ + target: el + }); + } else if (field.className.indexOf("range") !== -1 || field.className.indexOf("texture") !== -1) { + el.value = property; + el.onchange({ + target: el + }); + } else if (field.className.indexOf("checkbox") !== -1) { + if (property) { + el.setAttribute("checked", property); + } else { + el.removeAttribute("checked"); + } + } else if (field.className.indexOf("vector-section") !== -1) { + if (field.className.indexOf("rgb") !== -1) { + var red = document.getElementById(value + "-red"); + var blue = document.getElementById(value + "-blue"); + var green = document.getElementById(value + "-green"); + red.value = parseInt(property.red); + blue.value = parseInt(property.blue); + green.value = parseInt(property.green); + + red.oninput({ + target: red + }); + } else if (field.className.indexOf("xyz") !== -1) { + var x = document.getElementById(value + "-x"); + var y = document.getElementById(value + "-y"); + var z = document.getElementById(value + "-z"); + + x.value = roundFloat(property.x, 100); + y.value = roundFloat(property.y, 100); + z.value = roundFloat(property.z, 100); + } else if (field.className.indexOf("pyr") !== -1) { + var pitch = document.getElementById(value + "-Pitch"); + var yaw = document.getElementById(value + "-Yaw"); + var roll = document.getElementById(value + "-Roll"); + + pitch.value = roundFloat(property.x, 100); + yaw.value = roundFloat(property.y, 100); + roll.value = roundFloat(property.z, 100); + + } + } + } + } + } + }, + connect: function (EventBridge) { + this.EventBridge = EventBridge; + + var self = this; + + EventBridge.emitWebEvent(JSON.stringify({ + messageType: 'page_loaded' + })); + + EventBridge.scriptEventReceived.connect(function (data) { + data = JSON.parse(data); + + if (data.messageType === 'particle_settings') { + // Update settings + var currentProperties = data.currentProperties; + self.fillFields(currentProperties); + // Do expected property match with structure; + } else if (data.messageType === 'particle_close') { + self.disableFields(); + } + }); + }, + build: function () { + var self = this; + var sections = Object.keys(this.structure); + this.builtRows = {}; + sections.forEach(function (section, index) { + var properties = self.structure[section]; + self.addSection(self.parent, section, properties, index); + }); + }, + addSection: function (parent, section, properties, index) { + var self = this; + + var sectionDivHeader = document.createElement("div"); + var title = document.createElement("label"); + var dropDown = document.createElement("span"); + + dropDown.className = "arrow"; + sectionDivHeader.className = "section-header"; + title.innerHTML = section; + sectionDivHeader.appendChild(title); + sectionDivHeader.appendChild(dropDown); + var collapsed = index !== 0; + + dropDown.innerHTML = collapsed ? "L" : "M"; + sectionDivHeader.setAttribute("collapsed", collapsed); + parent.appendChild(sectionDivHeader); + + var sectionDivBody = document.createElement("div"); + sectionDivBody.className = "property-group"; + + var animationWrapper = document.createElement("div"); + animationWrapper.className = "section-wrap"; + + for (var property in properties) { + if (properties.hasOwnProperty(property)) { + var builtRow = self.addElement(animationWrapper, properties[property]); + var id = properties[property].id; + if (id) { + self.builtRows[id] = builtRow; + } + } + } + sectionDivBody.appendChild(animationWrapper); + parent.appendChild(sectionDivBody); + _.defer(function () { + var height = (animationWrapper.clientHeight) + "px"; + if (collapsed) { + sectionDivBody.classList.remove("visible"); + sectionDivBody.style.maxHeight = "0px"; + } else { + sectionDivBody.classList.add("visible"); + sectionDivBody.style.maxHeight = height; + } + + sectionDivHeader.onclick = function () { + collapsed = !collapsed; + if (collapsed) { + sectionDivBody.classList.remove("visible"); + sectionDivBody.style.maxHeight = "0px"; + } else { + sectionDivBody.classList.add("visible"); + sectionDivBody.style.maxHeight = (animationWrapper.clientHeight) + "px"; + } + // sectionDivBody.style.display = collapsed ? "none": "block"; + dropDown.innerHTML = collapsed ? "L" : "M"; + sectionDivHeader.setAttribute("collapsed", collapsed); + }; + }); + }, + addLabel: function (parent, group) { + var label = document.createElement("label"); + label.innerHTML = group.name; + parent.appendChild(label); + if (group.unit) { + var span = document.createElement("span"); + span.innerHTML = group.unit; + span.className = "unit"; + label.appendChild(span); + } + return label; + }, + addVector: function (parent, group, labels, domArray) { + var self = this; + var inputs = labels ? labels : ["x", "y", "z"]; + domArray = domArray ? domArray : []; + parent.id = group.id; + for (var index in inputs) { + var element = document.createElement("input"); + + element.setAttribute("type", "number"); + element.className = inputs[index]; + element.id = group.id + "-" + inputs[index]; + + if (group.defaultRange) { + if (group.defaultRange.min) { + element.setAttribute("min", group.defaultRange.min); + } + if (group.defaultRange.max) { + element.setAttribute("max", group.defaultRange.max); + } + if (group.defaultRange.step) { + element.setAttribute("step", group.defaultRange.step); + } + } + if (group.oninput) { + element.oninput = group.oninput; + } else { + element.oninput = function (event) { + self.webBridgeSync(group.id, { + x: domArray[0].value, + y: domArray[1].value, + z: domArray[2].value + }); + }; + } + element.onchange = element.oninput; + domArray.push(element); + } + + this.addLabel(parent, group); + var className = ""; + for (var i = 0; i < inputs.length; i++) { + className += inputs[i].charAt(0) + .toLowerCase(); + } + parent.className += " property vector-section " + className; + + // Add Tuple and the rest + var tupleContainer = document.createElement("div"); + tupleContainer.className = "tuple"; + for (var domIndex in domArray) { + var container = domArray[domIndex]; + var div = document.createElement("div"); + var label = document.createElement("label"); + label.innerHTML = inputs[domIndex] + ":"; + label.setAttribute("for", container.id); + div.appendChild(container); + div.appendChild(label); + tupleContainer.appendChild(div); + } + parent.appendChild(tupleContainer); + }, + addVectorQuaternion: function (parent, group) { + this.addVector(parent, group, ["Pitch", "Yaw", "Roll"]); + }, + addColorPicker: function (parent, group) { + var self = this; + var $colPickContainer = $('
    ', { + id: group.id, + class: "color-picker" + }); + var updateColors = function (red, green, blue) { + $colPickContainer.css('background-color', "rgb(" + + red + "," + + green + "," + + blue + ")"); + }; + + var inputs = ["red", "green", "blue"]; + var domArray = []; + group.oninput = function (event) { + $colPickContainer.colpickSetColor( + { + r: domArray[0].value, + g: domArray[1].value, + b: domArray[2].value + }, + true); + }; + group.defaultRange = { + min: 0, + max: 255, + step: 1 + }; + + parent.appendChild($colPickContainer[0]); + self.addVector(parent, group, inputs, domArray); + + updateColors(domArray[0].value, domArray[1].value, domArray[2].value); + + // Could probably write a custom one for this to completely write out jquery, + // but for now, using the same as earlier. + + /* Color Picker Logic Here */ + + + $colPickContainer.colpick({ + colorScheme: 'dark', + layout: 'hex', + color: { + r: domArray[0].value, + g: domArray[1].value, + b: domArray[2].value + }, + onChange: function (hsb, hex, rgb, el) { + updateColors(rgb.r, rgb.g, rgb.b); + + domArray[0].value = rgb.r; + domArray[1].value = rgb.g; + domArray[2].value = rgb.b; + self.webBridgeSync(group.id, { + red: rgb.r, + green: rgb.g, + blue: rgb.b + }); + }, + onSubmit: function (hsb, hex, rgb, el) { + $(el) + .css('background-color', '#' + hex); + $(el) + .colpickHide(); + domArray[0].value = rgb.r; + domArray[1].value = rgb.g; + domArray[2].value = rgb.b; + self.webBridgeSync(group.id, { + red: rgb.r, + green: rgb.g, + blue: rgb.b + }); + } + }); + }, + addTextureField: function (parent, group) { + var self = this; + this.addLabel(parent, group); + parent.className += " property texture"; + var textureImage = document.createElement("div"); + var textureUrl = document.createElement("input"); + textureUrl.setAttribute("type", "text"); + textureUrl.id = group.id; + textureImage.className = "texture-image no-texture"; + var image = document.createElement("img"); + var imageLoad = _.debounce(function (url) { + if (url.length > 0) { + textureImage.classList.remove("no-texture"); + textureImage.classList.add("with-texture"); + image.src = url; + image.style.display = "block"; + } else { + image.src = ""; + image.style.display = "none"; + textureImage.classList.add("no-texture"); + } + self.webBridgeSync(group.id, url); + }, 250); + + textureUrl.oninput = function (event) { + // Add throttle + var url = event.target.value; + imageLoad(url); + }; + textureUrl.onchange = textureUrl.oninput; + textureImage.appendChild(image); + parent.appendChild(textureImage); + parent.appendChild(textureUrl); + }, + addSlider: function (parent, group) { + var self = this; + this.addLabel(parent, group); + parent.className += " property range"; + var container = document.createElement("div"); + container.className = "slider-wrapper"; + var slider = document.createElement("input"); + slider.setAttribute("type", "range"); + + var inputField = document.createElement("input"); + inputField.setAttribute("type", "number"); + + container.appendChild(slider); + container.appendChild(inputField); + parent.appendChild(container); + + if (group.type === "SliderInteger") { + inputField.setAttribute("min", group.min !== undefined ? group.min : 0); + inputField.setAttribute("step", 1); + + slider.setAttribute("min", group.min !== undefined ? group.min : 0); + slider.setAttribute("max", group.max !== undefined ? group.max : 10000); + slider.setAttribute("step", 1); + + inputField.oninput = function (event) { + + if (parseInt(event.target.value) > parseInt(slider.getAttribute("max")) && group.max !== 1) { + slider.setAttribute("max", event.target.value); + } + slider.value = event.target.value; + + self.webBridgeSync(group.id, slider.value); + }; + inputField.onchange = inputField.oninput; + slider.oninput = function (event) { + inputField.value = event.target.value; + self.webBridgeSync(group.id, slider.value); + }; + + inputField.id = group.id; + } else if (group.type === "SliderRadian") { + slider.setAttribute("min", group.min !== undefined ? group.min : 0); + slider.setAttribute("max", group.max !== undefined ? group.max : 180); + slider.setAttribute("step", 1); + parent.className += " radian"; + inputField.setAttribute("min", (group.min !== undefined ? group.min : 0)); + inputField.setAttribute("max", (group.max !== undefined ? group.max : 180)); + + inputField.oninput = function (event) { + slider.value = event.target.value; + self.webBridgeSync(group.id, slider.value * RADIANS_PER_DEGREE); + }; + inputField.onchange = inputField.oninput; + + inputField.id = group.id; + slider.oninput = function (event) { + if (event.target.value > 0) { + inputField.value = Math.floor(event.target.value); + } else { + inputField.value = Math.ceil(event.target.value); + } + self.webBridgeSync(group.id, slider.value * RADIANS_PER_DEGREE); + }; + var degrees = document.createElement("label"); + degrees.innerHTML = "°"; + degrees.style.fontSize = "1.4rem"; + degrees.style.display = "inline"; + degrees.style.verticalAlign = "top"; + degrees.style.paddingLeft = "0.4rem"; + container.appendChild(degrees); + + } else { + // Must then be Float + inputField.setAttribute("min", group.min !== undefined ? group.min : 0); + slider.setAttribute("step", 0.01); + + slider.setAttribute("min", group.min !== undefined ? group.min : 0); + slider.setAttribute("max", group.max !== undefined ? group.max : 1); + slider.setAttribute("step", 0.01); + + inputField.oninput = function (event) { + if (parseFloat(event.target.value) > parseFloat(slider.getAttribute("max")) && group.max !== 1) { + slider.setAttribute("max", event.target.value); + } + + slider.value = event.target.value; + self.webBridgeSync(group.id, slider.value); + // bind web sock update here. + }; + inputField.onchange = inputField.oninput; + slider.oninput = function (event) { + inputField.value = event.target.value; + self.webBridgeSync(group.id, inputField.value); + }; + + inputField.id = group.id; + } + + // UpdateBinding + }, + addCheckBox: function (parent, group) { + var checkBox = document.createElement("input"); + checkBox.setAttribute("type", "checkbox"); + var self = this; + checkBox.onchange = function (event) { + self.webBridgeSync(group.id, event.target.checked); + }; + checkBox.id = group.id; + parent.appendChild(checkBox); + var label = this.addLabel(parent, group); + label.setAttribute("for", checkBox.id); + parent.className += " property checkbox"; + }, + addElement: function (parent, group) { + var self = this; + var property = document.createElement("div"); + property.id = group.id; + + var row = document.createElement("div"); + switch (group.type) { + case "Button": + var button = document.createElement("input"); + button.setAttribute("type", "button"); + button.id = group.id; + if (group.disabled) { + button.disabled = group.disabled; + } + button.className = group.class; + button.value = group.name; + + button.onclick = group.callback; + parent.appendChild(button); + break; + case "Row": + var hr = document.createElement("hr"); + hr.className = "splitter"; + if (group.id) { + hr.id = group.id; + } + parent.appendChild(hr); + break; + case "Boolean": + self.addCheckBox(row, group); + parent.appendChild(row); + break; + case "SliderFloat": + case "SliderInteger": + case "SliderRadian": + self.addSlider(row, group); + parent.appendChild(row); + break; + case "Texture": + self.addTextureField(row, group); + parent.appendChild(row); + break; + case "Color": + self.addColorPicker(row, group); + parent.appendChild(row); + break; + case "Vector": + self.addVector(row, group); + parent.appendChild(row); + break; + case "VectorQuaternion": + self.addVectorQuaternion(row, group); + parent.appendChild(row); + break; + default: + console.log("not defined"); + } + return row; + } +}; diff --git a/scripts/system/particle_explorer/particle-style.css b/scripts/system/particle_explorer/particle-style.css new file mode 100644 index 0000000000..e8b71fdba0 --- /dev/null +++ b/scripts/system/particle_explorer/particle-style.css @@ -0,0 +1,124 @@ +/* +// particle-style.css +// +// Created by Matti 'Menithal' Lahtinen on 21 May 2017 +// Copyright 2017 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +*/ + + +.property-group { + max-height: 0; + -webkit-transition: max-height 0.15s ease-out; + transition: max-height 0.15s ease-out; + overflow: hidden; +} +.property-group.visible { + transition: max-height 0.25s ease-in; +} +.section-wrap { + width: 100%; +} +.property { + padding: 0.4rem 0; + margin: 0; +} +.property.checkbox { + margin: 0; +} +.property.range label{ + display: block; +} + +input[type="button"] { + margin: 0.4rem; + min-width: 6rem; +} +input[type="text"] { + margin: 0; +} +.property.range input[type=number]{ + margin-left: 0.8rem; + width: 5.4rem; + height: 1.8rem; +} +input[type=range] { + -webkit-appearance: none; + background: #2e2e2e; + height: 1.8rem; + border-radius: 1rem; +} +input[type=range]::-webkit-slider-thumb { + -webkit-appearance:none; + width: 0.6rem; + height: 1.8rem; + padding:0; + margin: 0; + background-color: #696969; + border-radius: 1rem; +} +input[type=range]::-webkit-slider-thumb:hover { + background-color: white; +} +input[type=range]:focus { /*#252525*/ + outline: none; +} +.tuple label { + text-transform: capitalize; +} +.slider-wrapper { + display: table; + padding: 0.4rem 0; +} +hr.splitter{ + width: 100%; + padding: 0.2rem 0 0 0; + margin: 0; + position: relative; + clear: both; +} +hr.splitter:last-of-type{ + padding:0; +} +#rem { + height: 1rem; + width: 1rem; +} +.property { + min-height: 2rem; +} +.property.vector-section{ + + width: 24rem; +} + +.property.texture { + display: block; +} +.property.texture input{ + margin: 0.4rem 0; +} +.texture-image img{ + padding: 0; + margin: 0; + width: 100%; + height: 100%; + display: none; +} +.texture-image { + display: block; + position: relative; + background-repeat: no-repeat; + background-position: center; + background-size: 100% 100%; + margin-top: 0.4rem; + height:128px; + width: 128px; + background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAYAAADDPmHLAAAACXBIWXMAAAsTAAALEwEAmpwYAAAKT2lDQ1BQaG90b3Nob3AgSUNDIHByb2ZpbGUAAHjanVNnVFPpFj333vRCS4iAlEtvUhUIIFJCi4AUkSYqIQkQSoghodkVUcERRUUEG8igiAOOjoCMFVEsDIoK2AfkIaKOg6OIisr74Xuja9a89+bN/rXXPues852zzwfACAyWSDNRNYAMqUIeEeCDx8TG4eQuQIEKJHAAEAizZCFz/SMBAPh+PDwrIsAHvgABeNMLCADATZvAMByH/w/qQplcAYCEAcB0kThLCIAUAEB6jkKmAEBGAYCdmCZTAKAEAGDLY2LjAFAtAGAnf+bTAICd+Jl7AQBblCEVAaCRACATZYhEAGg7AKzPVopFAFgwABRmS8Q5ANgtADBJV2ZIALC3AMDOEAuyAAgMADBRiIUpAAR7AGDIIyN4AISZABRG8lc88SuuEOcqAAB4mbI8uSQ5RYFbCC1xB1dXLh4ozkkXKxQ2YQJhmkAuwnmZGTKBNA/g88wAAKCRFRHgg/P9eM4Ors7ONo62Dl8t6r8G/yJiYuP+5c+rcEAAAOF0ftH+LC+zGoA7BoBt/qIl7gRoXgugdfeLZrIPQLUAoOnaV/Nw+H48PEWhkLnZ2eXk5NhKxEJbYcpXff5nwl/AV/1s+X48/Pf14L7iJIEyXYFHBPjgwsz0TKUcz5IJhGLc5o9H/LcL//wd0yLESWK5WCoU41EScY5EmozzMqUiiUKSKcUl0v9k4t8s+wM+3zUAsGo+AXuRLahdYwP2SycQWHTA4vcAAPK7b8HUKAgDgGiD4c93/+8//UegJQCAZkmScQAAXkQkLlTKsz/HCAAARKCBKrBBG/TBGCzABhzBBdzBC/xgNoRCJMTCQhBCCmSAHHJgKayCQiiGzbAdKmAv1EAdNMBRaIaTcA4uwlW4Dj1wD/phCJ7BKLyBCQRByAgTYSHaiAFiilgjjggXmYX4IcFIBBKLJCDJiBRRIkuRNUgxUopUIFVIHfI9cgI5h1xGupE7yAAygvyGvEcxlIGyUT3UDLVDuag3GoRGogvQZHQxmo8WoJvQcrQaPYw2oefQq2gP2o8+Q8cwwOgYBzPEbDAuxsNCsTgsCZNjy7EirAyrxhqwVqwDu4n1Y8+xdwQSgUXACTYEd0IgYR5BSFhMWE7YSKggHCQ0EdoJNwkDhFHCJyKTqEu0JroR+cQYYjIxh1hILCPWEo8TLxB7iEPENyQSiUMyJ7mQAkmxpFTSEtJG0m5SI+ksqZs0SBojk8naZGuyBzmULCAryIXkneTD5DPkG+Qh8lsKnWJAcaT4U+IoUspqShnlEOU05QZlmDJBVaOaUt2ooVQRNY9aQq2htlKvUYeoEzR1mjnNgxZJS6WtopXTGmgXaPdpr+h0uhHdlR5Ol9BX0svpR+iX6AP0dwwNhhWDx4hnKBmbGAcYZxl3GK+YTKYZ04sZx1QwNzHrmOeZD5lvVVgqtip8FZHKCpVKlSaVGyovVKmqpqreqgtV81XLVI+pXlN9rkZVM1PjqQnUlqtVqp1Q61MbU2epO6iHqmeob1Q/pH5Z/YkGWcNMw09DpFGgsV/jvMYgC2MZs3gsIWsNq4Z1gTXEJrHN2Xx2KruY/R27iz2qqaE5QzNKM1ezUvOUZj8H45hx+Jx0TgnnKKeX836K3hTvKeIpG6Y0TLkxZVxrqpaXllirSKtRq0frvTau7aedpr1Fu1n7gQ5Bx0onXCdHZ4/OBZ3nU9lT3acKpxZNPTr1ri6qa6UbobtEd79up+6Ynr5egJ5Mb6feeb3n+hx9L/1U/W36p/VHDFgGswwkBtsMzhg8xTVxbzwdL8fb8VFDXcNAQ6VhlWGX4YSRudE8o9VGjUYPjGnGXOMk423GbcajJgYmISZLTepN7ppSTbmmKaY7TDtMx83MzaLN1pk1mz0x1zLnm+eb15vft2BaeFostqi2uGVJsuRaplnutrxuhVo5WaVYVVpds0atna0l1rutu6cRp7lOk06rntZnw7Dxtsm2qbcZsOXYBtuutm22fWFnYhdnt8Wuw+6TvZN9un2N/T0HDYfZDqsdWh1+c7RyFDpWOt6azpzuP33F9JbpL2dYzxDP2DPjthPLKcRpnVOb00dnF2e5c4PziIuJS4LLLpc+Lpsbxt3IveRKdPVxXeF60vWdm7Obwu2o26/uNu5p7ofcn8w0nymeWTNz0MPIQ+BR5dE/C5+VMGvfrH5PQ0+BZ7XnIy9jL5FXrdewt6V3qvdh7xc+9j5yn+M+4zw33jLeWV/MN8C3yLfLT8Nvnl+F30N/I/9k/3r/0QCngCUBZwOJgUGBWwL7+Hp8Ib+OPzrbZfay2e1BjKC5QRVBj4KtguXBrSFoyOyQrSH355jOkc5pDoVQfujW0Adh5mGLw34MJ4WHhVeGP45wiFga0TGXNXfR3ENz30T6RJZE3ptnMU85ry1KNSo+qi5qPNo3ujS6P8YuZlnM1VidWElsSxw5LiquNm5svt/87fOH4p3iC+N7F5gvyF1weaHOwvSFpxapLhIsOpZATIhOOJTwQRAqqBaMJfITdyWOCnnCHcJnIi/RNtGI2ENcKh5O8kgqTXqS7JG8NXkkxTOlLOW5hCepkLxMDUzdmzqeFpp2IG0yPTq9MYOSkZBxQqohTZO2Z+pn5mZ2y6xlhbL+xW6Lty8elQfJa7OQrAVZLQq2QqboVFoo1yoHsmdlV2a/zYnKOZarnivN7cyzytuQN5zvn//tEsIS4ZK2pYZLVy0dWOa9rGo5sjxxedsK4xUFK4ZWBqw8uIq2Km3VT6vtV5eufr0mek1rgV7ByoLBtQFr6wtVCuWFfevc1+1dT1gvWd+1YfqGnRs+FYmKrhTbF5cVf9go3HjlG4dvyr+Z3JS0qavEuWTPZtJm6ebeLZ5bDpaql+aXDm4N2dq0Dd9WtO319kXbL5fNKNu7g7ZDuaO/PLi8ZafJzs07P1SkVPRU+lQ27tLdtWHX+G7R7ht7vPY07NXbW7z3/T7JvttVAVVN1WbVZftJ+7P3P66Jqun4lvttXa1ObXHtxwPSA/0HIw6217nU1R3SPVRSj9Yr60cOxx++/p3vdy0NNg1VjZzG4iNwRHnk6fcJ3/ceDTradox7rOEH0x92HWcdL2pCmvKaRptTmvtbYlu6T8w+0dbq3nr8R9sfD5w0PFl5SvNUyWna6YLTk2fyz4ydlZ19fi753GDborZ752PO32oPb++6EHTh0kX/i+c7vDvOXPK4dPKy2+UTV7hXmq86X23qdOo8/pPTT8e7nLuarrlca7nuer21e2b36RueN87d9L158Rb/1tWeOT3dvfN6b/fF9/XfFt1+cif9zsu72Xcn7q28T7xf9EDtQdlD3YfVP1v+3Njv3H9qwHeg89HcR/cGhYPP/pH1jw9DBY+Zj8uGDYbrnjg+OTniP3L96fynQ89kzyaeF/6i/suuFxYvfvjV69fO0ZjRoZfyl5O/bXyl/erA6xmv28bCxh6+yXgzMV70VvvtwXfcdx3vo98PT+R8IH8o/2j5sfVT0Kf7kxmTk/8EA5jz/GMzLdsAAAAgY0hSTQAAeiUAAICDAAD5/wAAgOkAAHUwAADqYAAAOpgAABdvkl/FRgAABhNJREFUeNrsnVFy4joQRVsSCwAqBMwqsrRsIavMEkICoeAf2+8j1R5ZGDBgpLzoUDVVmTT2dc8It/paOpi3t7faOSciImVZyn6/l6qqRETEWivj8VistYPFd7ud1HUtIiLGGBmPx5JKX0RkMplIzvmPnHNijBERaS7Ef1lrB40bY1oXgH5a/ZH+8P7+LlVVycfHR/MGa60URdGcYOi4MUaKomhGaGx9EZHlcplMP2X+Ly8vPwOgLEtxzklVVVJVVfOznqAsy9YFXhuvqqq5AF/Lj+srtr7+LpV+yvz1mNF+vxcRkdVqJdZaeXp6ap1ws9m0TjibzVoj6lJ8vV6fjJdlKev1ujViU+j7t8tc8p9Op1KWpYw06L9JL0Av0r9l+jXl3nhd11JV1VE8tn5YM3PI3xjzoxVOGvyDU7zQj6s/0tGmI++amtNV087F9Wf/FnVPzRtCXz8RdV1nlb/efUbaJy4Wi0FqzjU1yRgjs9ls0Jp3jb6IyPPzczL9lPkvFot/dwCtB/om/x9oyJoXxps65NW8mPpdNTeX/JtBEtYE/+AUL/Tj6g/qA3TVnD41a6g++Bp9rYOp9FPnH80HOBcvy1I2m81D++BL+o/2AX5r/vgA+AD4AOif8AH8EdpVcy71sX3jWp/8W2AKff/TkUv+Oufr9AF0YuKc66xJ18T7eNP3nP9WfZ0EzufzJPqp8y+KQuq67vYBdETqCDpVU/rEw5oUnr+rD46h73/qUuinzh8fAP22D6AjxznXcqq6akrf+KmaFB6vf4+t7/sAelfIJf/GB9jtdmKMkdVq1dQM3zg4VVNU/NY+1Bgjh8Oh6YM1+dj6X19fzXwgp/wbH0DFtS7oyf0RdKqmhPFr+1RdseKfP7a+Px/IKX98APTbPoDOJrv60L417d54TH3V8lfS5pT/yfUA6/X6qOZcqkm3xrUm6X9CTH3fB0ihnzr/Ix9A/3T1qbfWpGvjMfX9T0UK/dT54wOg/88H8EfGPTVr6D740frhLDmn/Hv5AH1qku9t31KTzh3/aP1LPsBfzr+XDxCO0K6ack/N6qp5MfUv+QB/Of/ePsCQfWmfc6EfV3/kjzZrrRwOh9YtKHSm/LjOH3yrMTzej4c1y//51PHoP0a/tR7AOSdFURw9rz5VU049zw7jl2qWrosP++BY+iI/+wJS6afMv9kXoA6gvimsieHzZr/m6MTp3PPuc3G9SP95OPpx9JtOgT4cHwA+QCJ9+ADwAeADsC+AfQHo/4b1APAB4APAB4APAB8APgB9OD4AfAD4AFFqEnwA+AD4APgA6P86HwA+AHyAZhIBHwA+AHwA+AD04X/eB4APAB8APgB8APgA8AHow/P0AeADwAeADwAfAD4AfAD68Px8APgA8AHgA8AHgA8AH0DO70/v6lHvjaOfVn8U/iLcXx5OUML96X49vRTX3/nPw9FPo9+sB5hMJuKck+VyeVRTrLWtdfNdcf95eldNCuOfn5+tSYy/Pz+2voi0fICc8p/P5z93gJAPEN4+wufN4evaePj99eH+ePTj6p/1Abp60kt9Ksf/v46HDwAfAD6A/6gUPgD7AtgXwPP4DNcDwAeADwAfAD4AfAD4ADyPz289AHyA+Pqp84cPIPAB8AHwAfAB8AHgA7Q+HfAB4APAB4APAB+APjw3HwA+AHwA+ADwAeADwAegD8/TB4APAB8APgB8APgA8AHow/PzAeADwAeADwAfAD4AfACJ//316KfVH/mjLeb31+vx/kWhH0+/tR7AOSdFUUT9/nq9oK4+OJa+iLT25+eUf7MvIOQDxPr+en2F++PRj6PfdAr04fgA8AES6cMHgA8AH4B9AewLQP83rAeADwAfAD4AfAD4APAB6MPxAeADwAeIUpPgA8AHwAfAB0D/1/kA8AHgAzSTCPgA8AHgA8AHoA//8z4AfAD4APAB4APAB4APQB+epw8AHwA+AHwA+ADwAeAD0Ifn5wPAB4APAB8APgB8gBz5AOb19bX2TYLpdNpqQ7bbbctJGjJeVZVst9vWLSu2/vf3t+Sc/yicFIRr0C7Fu76f/lw8XBePflr9/wYAqWwWUSLcO54AAAAASUVORK5CYII='); +} + +.texture-image.no-texture{ + background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAYAAADDPmHLAAAACXBIWXMAAAsTAAALEwEAmpwYAAAKT2lDQ1BQaG90b3Nob3AgSUNDIHByb2ZpbGUAAHjanVNnVFPpFj333vRCS4iAlEtvUhUIIFJCi4AUkSYqIQkQSoghodkVUcERRUUEG8igiAOOjoCMFVEsDIoK2AfkIaKOg6OIisr74Xuja9a89+bN/rXXPues852zzwfACAyWSDNRNYAMqUIeEeCDx8TG4eQuQIEKJHAAEAizZCFz/SMBAPh+PDwrIsAHvgABeNMLCADATZvAMByH/w/qQplcAYCEAcB0kThLCIAUAEB6jkKmAEBGAYCdmCZTAKAEAGDLY2LjAFAtAGAnf+bTAICd+Jl7AQBblCEVAaCRACATZYhEAGg7AKzPVopFAFgwABRmS8Q5ANgtADBJV2ZIALC3AMDOEAuyAAgMADBRiIUpAAR7AGDIIyN4AISZABRG8lc88SuuEOcqAAB4mbI8uSQ5RYFbCC1xB1dXLh4ozkkXKxQ2YQJhmkAuwnmZGTKBNA/g88wAAKCRFRHgg/P9eM4Ors7ONo62Dl8t6r8G/yJiYuP+5c+rcEAAAOF0ftH+LC+zGoA7BoBt/qIl7gRoXgugdfeLZrIPQLUAoOnaV/Nw+H48PEWhkLnZ2eXk5NhKxEJbYcpXff5nwl/AV/1s+X48/Pf14L7iJIEyXYFHBPjgwsz0TKUcz5IJhGLc5o9H/LcL//wd0yLESWK5WCoU41EScY5EmozzMqUiiUKSKcUl0v9k4t8s+wM+3zUAsGo+AXuRLahdYwP2SycQWHTA4vcAAPK7b8HUKAgDgGiD4c93/+8//UegJQCAZkmScQAAXkQkLlTKsz/HCAAARKCBKrBBG/TBGCzABhzBBdzBC/xgNoRCJMTCQhBCCmSAHHJgKayCQiiGzbAdKmAv1EAdNMBRaIaTcA4uwlW4Dj1wD/phCJ7BKLyBCQRByAgTYSHaiAFiilgjjggXmYX4IcFIBBKLJCDJiBRRIkuRNUgxUopUIFVIHfI9cgI5h1xGupE7yAAygvyGvEcxlIGyUT3UDLVDuag3GoRGogvQZHQxmo8WoJvQcrQaPYw2oefQq2gP2o8+Q8cwwOgYBzPEbDAuxsNCsTgsCZNjy7EirAyrxhqwVqwDu4n1Y8+xdwQSgUXACTYEd0IgYR5BSFhMWE7YSKggHCQ0EdoJNwkDhFHCJyKTqEu0JroR+cQYYjIxh1hILCPWEo8TLxB7iEPENyQSiUMyJ7mQAkmxpFTSEtJG0m5SI+ksqZs0SBojk8naZGuyBzmULCAryIXkneTD5DPkG+Qh8lsKnWJAcaT4U+IoUspqShnlEOU05QZlmDJBVaOaUt2ooVQRNY9aQq2htlKvUYeoEzR1mjnNgxZJS6WtopXTGmgXaPdpr+h0uhHdlR5Ol9BX0svpR+iX6AP0dwwNhhWDx4hnKBmbGAcYZxl3GK+YTKYZ04sZx1QwNzHrmOeZD5lvVVgqtip8FZHKCpVKlSaVGyovVKmqpqreqgtV81XLVI+pXlN9rkZVM1PjqQnUlqtVqp1Q61MbU2epO6iHqmeob1Q/pH5Z/YkGWcNMw09DpFGgsV/jvMYgC2MZs3gsIWsNq4Z1gTXEJrHN2Xx2KruY/R27iz2qqaE5QzNKM1ezUvOUZj8H45hx+Jx0TgnnKKeX836K3hTvKeIpG6Y0TLkxZVxrqpaXllirSKtRq0frvTau7aedpr1Fu1n7gQ5Bx0onXCdHZ4/OBZ3nU9lT3acKpxZNPTr1ri6qa6UbobtEd79up+6Ynr5egJ5Mb6feeb3n+hx9L/1U/W36p/VHDFgGswwkBtsMzhg8xTVxbzwdL8fb8VFDXcNAQ6VhlWGX4YSRudE8o9VGjUYPjGnGXOMk423GbcajJgYmISZLTepN7ppSTbmmKaY7TDtMx83MzaLN1pk1mz0x1zLnm+eb15vft2BaeFostqi2uGVJsuRaplnutrxuhVo5WaVYVVpds0atna0l1rutu6cRp7lOk06rntZnw7Dxtsm2qbcZsOXYBtuutm22fWFnYhdnt8Wuw+6TvZN9un2N/T0HDYfZDqsdWh1+c7RyFDpWOt6azpzuP33F9JbpL2dYzxDP2DPjthPLKcRpnVOb00dnF2e5c4PziIuJS4LLLpc+Lpsbxt3IveRKdPVxXeF60vWdm7Obwu2o26/uNu5p7ofcn8w0nymeWTNz0MPIQ+BR5dE/C5+VMGvfrH5PQ0+BZ7XnIy9jL5FXrdewt6V3qvdh7xc+9j5yn+M+4zw33jLeWV/MN8C3yLfLT8Nvnl+F30N/I/9k/3r/0QCngCUBZwOJgUGBWwL7+Hp8Ib+OPzrbZfay2e1BjKC5QRVBj4KtguXBrSFoyOyQrSH355jOkc5pDoVQfujW0Adh5mGLw34MJ4WHhVeGP45wiFga0TGXNXfR3ENz30T6RJZE3ptnMU85ry1KNSo+qi5qPNo3ujS6P8YuZlnM1VidWElsSxw5LiquNm5svt/87fOH4p3iC+N7F5gvyF1weaHOwvSFpxapLhIsOpZATIhOOJTwQRAqqBaMJfITdyWOCnnCHcJnIi/RNtGI2ENcKh5O8kgqTXqS7JG8NXkkxTOlLOW5hCepkLxMDUzdmzqeFpp2IG0yPTq9MYOSkZBxQqohTZO2Z+pn5mZ2y6xlhbL+xW6Lty8elQfJa7OQrAVZLQq2QqboVFoo1yoHsmdlV2a/zYnKOZarnivN7cyzytuQN5zvn//tEsIS4ZK2pYZLVy0dWOa9rGo5sjxxedsK4xUFK4ZWBqw8uIq2Km3VT6vtV5eufr0mek1rgV7ByoLBtQFr6wtVCuWFfevc1+1dT1gvWd+1YfqGnRs+FYmKrhTbF5cVf9go3HjlG4dvyr+Z3JS0qavEuWTPZtJm6ebeLZ5bDpaql+aXDm4N2dq0Dd9WtO319kXbL5fNKNu7g7ZDuaO/PLi8ZafJzs07P1SkVPRU+lQ27tLdtWHX+G7R7ht7vPY07NXbW7z3/T7JvttVAVVN1WbVZftJ+7P3P66Jqun4lvttXa1ObXHtxwPSA/0HIw6217nU1R3SPVRSj9Yr60cOxx++/p3vdy0NNg1VjZzG4iNwRHnk6fcJ3/ceDTradox7rOEH0x92HWcdL2pCmvKaRptTmvtbYlu6T8w+0dbq3nr8R9sfD5w0PFl5SvNUyWna6YLTk2fyz4ydlZ19fi753GDborZ752PO32oPb++6EHTh0kX/i+c7vDvOXPK4dPKy2+UTV7hXmq86X23qdOo8/pPTT8e7nLuarrlca7nuer21e2b36RueN87d9L158Rb/1tWeOT3dvfN6b/fF9/XfFt1+cif9zsu72Xcn7q28T7xf9EDtQdlD3YfVP1v+3Njv3H9qwHeg89HcR/cGhYPP/pH1jw9DBY+Zj8uGDYbrnjg+OTniP3L96fynQ89kzyaeF/6i/suuFxYvfvjV69fO0ZjRoZfyl5O/bXyl/erA6xmv28bCxh6+yXgzMV70VvvtwXfcdx3vo98PT+R8IH8o/2j5sfVT0Kf7kxmTk/8EA5jz/GMzLdsAAAAgY0hSTQAAeiUAAICDAAD5/wAAgOkAAHUwAADqYAAAOpgAABdvkl/FRgAAB81JREFUeNrsnTGPm0oXht97FWm2Ch2pTEeHpUihsyvTuXO67Ta/IPkr+Qfp3MWdO7Zad0SKZDo6XIWOrTzV9xVXZ8SygGHXG4/t96lW68GGw8vMmZlzDv98+/btfyBXy780wXXzTv74/fs3rXFFfPz4kT0AoQAoAJqAAiAUAKEACAVAKABCARAKgFAAhAIgFAChAAgFQCgAQgEQCoBQAIQCIBQAoQAIBUAoAHLmvDv3C7i7u4PjOMiyDOv1+mC75XKJoiga2wRBAN/34TgOHMdBWZYoigJpmiLPcwrARhzHAQD4vg/P81pvlLRrwvM8zGYz00ZrbY5xHAe+7yPPc9zf36MsSwrAVmazGX78+DHoGM/zsFgsAAB5nmOz2ZgeQimF8XiMMAxNu+VyaQRCH8Ai8jyH4zgIw7D3MUopzOdzAECaplitVk+GB601kiTBz58/obWG4ziIoohOoI38+vULABCGYWd3X2U6nUIphbIsEcdxa7uiKPDw8GCGGtd1KQDbKMsSWZZBKYXJZNLrGN/3zdN/iDRNTdcvx1EAFqGUwmazeeIQduG6LpRSAIAsy3r9hrRjD2BxL5AkiXEI+8wetNa9PXtp13eIoQBOQJIkxmHrcgjlJkov8JKpJwVgIVpr47CFYdh6g/f7/ZM5/9CehgKwmDRNURQFlFKYTqeNN/rx8dH0AH2faBn7KYAzQKZ1QRCYZd0qf/78MX+PRqNe3ymO5W63owBsR9bwZShoGirEq++zeBQEweBZAwVwYh4eHqC1RhAErQ6jOHVdK3yu65qhJE1TDgHn5BDKTW6auxdFYdYOgiDAYrF40k4phTAM8fnzZyilUBRF54rhOfIOF06SJMYPaPt8v99jOp3C8zx4nget9bPZQ5ZlF3fzL0IAZVke9OLv7+/Njl/brCHLMozHY4xGI3z48MH0EEVRIMuyi40H+EdqBbNS6HXBSqGEAiAUAAVAE1AAhAIgFAChAAgFQCgAQgGQq+Eom0GLxeJgGHYVSdCUhM02yrI0qV5hGGIymaAsy9b0LNd1cXt7CwDYbDa98wOA/zKLVquVSQGr/nYTbe2iKDIh53JtZVmiLEvsdjtst9tn5z7EDmfXA3QFXdaTMbvYbrdm568tgkdueJ7njbt3QwJA+8YJ1tsFQQDXdXFzc2N2E0Uwk8kEX758eXbMEDtY2QOsVqtn//v69SsAYL1eH9xK7dNGgjuiKMJ4PH4WmSN7+QBMFu/3798bn1oAzz47NvVrqmYgz2azRpv1scNV+wDVaN969y6JIEmSWBmyJenlIgZbcgvOzgmUqJxqkmY18ldCvGwkz/MntQcogBcgETrVMV98Aptvfh1JTKEAXsBms4HWGp7nYT6fw3Ec5Hlufbi253lQSkFr3VqmhgLoQVmW2G63ZigQx8/2my/FKCR17WLWAV7LfD5vzOFLkqS1W0/T1HT9RVFY5/jNZjMz3ouvorVGHMet9QheYoer7AGq478Y2LaiDTc3N3Bd90megSwG2YQVPcDQ+a/ccK01ttutWSWsetl/i7bfq16TzP1lGFgul0exw9X2AJLGJV3joRXCl3rnXbUDhmQKl2WJ9XoNrbV1vdXZCUCWWqvVQGR8HFIgqmuaKUiCSJcA+nrzWmvzdA/ZN6EAKlTz/eXmA3iSuXOoNEzfBRsA+PTpU+PnUjxSfnvo9/ZNR6cAakjFj2rqd3VtQJ6u1z5h1e+SdYbqdK5aWHLImC0OoFQgpRN4YPoD/LfRVC8C2TQlkhVC3/dfVDG0/l1xHCOKIvi+b572atJoURSdtYnbfAHxV0aj0TP/oY8dzqYH6OscHXK26tO+rqcujmNTIKqtJkDfc0vTFMvl8smu436/R57niOO4NSbh0HfLkFHtpYbY4dgwOfRKYXIooQAIBUAB0AQUAKEACAVAKABCARAKgFAA5Gp4s93AKIrw/v17ExsnFEWB/X6P3W6HLMtaN0+GJkwOad+W2FlPLq3GHFSRdq85h2PYyGoByG6cvJOnHiEryZJSg7e+s1ZNmOyzSza0ffWYJsIwbMzk7Tp+6Dm81kZWC0BoCnSU7dowDE2K12q1alT60EDJYwVWKqUQRdHgPf9jnfMQG52dDyA5fLKnLlGztiB5Bn1eP3fuNvr31IaWZM9jhHIdEwk5G1Jk4hxtdPJZQJZlJrLWlnBpx3FMmrnrup3RReduIyumgXJxtryRUxw4mQXIO4Yv0UZWCMDWN3I2vX7u0mxk1RtDmp6yoQmTbe27kjK7iOMYt7e3CIIA2+22VyLIWyZ5Hrsnsmol0Jac+fo51QtSXJKNrOgBuvLsTrUOUO8FxAP3ff/gTXiLc3irt5aevAdQSpmpja0vZqq+fm4ymfz18i5vaaOTC0DSvapv8rQRmRY6joPxeHwxNjqpAGSpUwx8ikKJQ5AyNFKb4BJsdBIfwPM8BEFgFjXSNG3debMJSUv7GyuWf8tGby6Aaq2c+qvaJce/a3p2ioTJQ73A3d3di6aBbef8WhtZKQDJ6K1fTJ7neHx8PFjWTcbbvvPePm8QbVtc6ft/+UwKUdfbDT3n19roGDA59EphciihAAgFQAHQBBQAoQAIBUAoAEIBEAqAUACEAiAUAKEACAVAKABCARAKgFAAhAIgFAChAAgFQC4CkxgiceKEPQC5Iv4/APgB2O7x8IXXAAAAAElFTkSuQmCC'); +} diff --git a/scripts/system/particle_explorer/particleExplorer.html b/scripts/system/particle_explorer/particleExplorer.html index d0d86d79da..0f014e9fa8 100644 --- a/scripts/system/particle_explorer/particleExplorer.html +++ b/scripts/system/particle_explorer/particleExplorer.html @@ -1,95 +1,42 @@ +// + --> - - - - - - - - - - + + + - -
    - -
    -
    -
    -
    -
    - + +
    +
    + +
    + +
    +
    + diff --git a/scripts/system/particle_explorer/particleExplorer.js b/scripts/system/particle_explorer/particleExplorer.js index 5f66fe7ae6..ca6a873b73 100644 --- a/scripts/system/particle_explorer/particleExplorer.js +++ b/scripts/system/particle_explorer/particleExplorer.js @@ -2,550 +2,410 @@ // particleExplorer.js // // Created by James B. Pollack @imgntn on 9/26/2015 -// Copyright 2015 High Fidelity, Inc. +// Copyright 2017 High Fidelity, Inc. +// +// Reworked by Menithal on 20/5/2017 +// // Web app side of the App - contains GUI. -// This is an example of a new, easy way to do two way bindings between dynamically created GUI and in-world entities. +// This is an example of a new, easy way to do two way bindings between dynamically created GUI and in-world entities. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -/* global window, alert, EventBridge, dat, listenForSettingsUpdates, createVec3Folder, createQuatFolder, writeVec3ToInterface, writeDataToInterface, - $, document, _, openEventBridge */ +/* global HifiEntityUI, openEventBridge, console, EventBridge, document, window */ +/* eslint no-console: 0, no-global-assign: 0 */ -var Settings = function () { - this.exportSettings = function () { - // copyExportSettingsToClipboard(); - showPreselectedPrompt(); - }; - this.importSettings = function () { - importSettings(); - }; -}; +(function () { -// 2-way bindings-aren't quite ready yet. see bottom of file. -var AUTO_UPDATE = false; -var UPDATE_ALL_FREQUENCY = 100; + var root = document.getElementById("particle-explorer"); -var controllers = []; -var colpickKeys = []; -var folders = []; -var gui = null; -var settings = new Settings(); -var updateInterval; - -var active = false; - -var currentInputField; -var storedController; -// CHANGE TO WHITELIST -var keysToAllow = [ - 'isEmitting', - 'maxParticles', - 'lifespan', - 'emitRate', - 'emitSpeed', - 'speedSpread', - 'emitOrientation', - 'emitDimensions', - 'polarStart', - 'polarFinish', - 'azimuthStart', - 'azimuthFinish', - 'emitAcceleration', - 'accelerationSpread', - 'particleRadius', - 'radiusSpread', - 'radiusStart', - 'radiusFinish', - 'color', - 'colorSpread', - 'colorStart', - 'colorFinish', - 'alpha', - 'alphaSpread', - 'alphaStart', - 'alphaFinish', - 'emitterShouldTrail', - 'textures' -]; - -var individualKeys = []; -var vec3Keys = []; -var quatKeys = []; -var colorKeys = []; - -window.onload = function () { - openEventBridge(function () { - var stringifiedData = JSON.stringify({ - messageType: 'page_loaded' + window.onload = function () { + var ui = new HifiEntityUI(root); + var textarea = document.createElement("textarea"); + var properties = ""; + var menuStructure = { + General: [ + { + type: "Row", + id: "export-import-field" + }, + { + id: "show-properties-button", + name: "Show Properties", + type: "Button", + class: "blue", + disabled: true, + callback: function (event) { + var insertZone = document.getElementById("export-import-field"); + var json = ui.getSettings(); + properties = JSON.stringify(json); + textarea.value = properties; + if (!insertZone.contains(textarea)) { + insertZone.appendChild(textarea); + insertZone.parentNode.parentNode.style.maxHeight = + insertZone.parentNode.clientHeight + "px"; + document.getElementById("export-properties-button").removeAttribute("disabled"); + textarea.onchange = function (e) { + if (e.target.value !== properties) { + document.getElementById("import-properties-button").removeAttribute("disabled"); + } + }; + textarea.oninput = textarea.onchange; + } else { + textarea.onchange = function () {}; + textarea.oninput = textarea.onchange; + textarea.value = ""; + textarea.remove(); + insertZone.parentNode.parentNode.style.maxHeight = + insertZone.parentNode.clientHeight + "px"; + document.getElementById("export-properties-button").setAttribute("disabled", true); + document.getElementById("import-properties-button").setAttribute("disabled", true); + } + } + }, + { + id: "import-properties-button", + name: "Import", + type: "Button", + class: "blue", + disabled: true, + callback: function (event) { + ui.fillFields(JSON.parse(textarea.value)); + ui.submitChanges(JSON.parse(textarea.value)); + } + }, + { + id: "export-properties-button", + name: "Export", + type: "Button", + class: "red", + disabled: true, + callback: function (event) { + textarea.select(); + try { + var success = document.execCommand('copy'); + if (!success) { + throw "Not success :("; + } + } catch (e) { + print("couldnt copy field"); + } + } + }, + { + type: "Row" + }, + { + id: "isEmitting", + name: "Is Emitting", + type: "Boolean" + }, + { + type: "Row" + }, + { + id: "lifespan", + name: "Lifespan", + type: "SliderFloat", + min: 0.01, + max: 10 + }, + { + type: "Row" + }, + { + id: "maxParticles", + name: "Max Particles", + type: "SliderInteger", + min: 1, + max: 10000 + }, + { + type: "Row" + }, + { + id: "textures", + name: "Textures", + type: "Texture" + }, + { + type: "Row" + } + ], + Emit: [ + { + id: "emitRate", + name: "Emit Rate", + type: "SliderInteger", + max: 1000, + min: 1 + }, + { + type: "Row" + }, + { + id: "emitSpeed", + name: "Emit Speed", + type: "SliderFloat", + max: 5 + }, + { + type: "Row" + }, + { + id: "emitDimensions", + name: "Emit Dimension", + type: "Vector", + defaultRange: { + min: 0, + step: 0.01 + } + }, + { + type: "Row" + }, + { + id: "emitOrientation", + unit: "deg", + name: "Emit Orientation", + type: "VectorQuaternion", + defaultRange: { + min: 0, + step: 0.01 + } + }, + { + type: "Row" + }, + { + id: "emitShouldTrail", + name: "Emit Should Trail", + type: "Boolean" + }, + { + type: "Row" + } + ], + Radius: [ + { + id: "particleRadius", + name: "Particle Radius", + type: "SliderFloat", + max: 4.0 + }, + { + type: "Row" + }, + { + id: "radiusSpread", + name: "Radius Spread", + type: "SliderFloat", + max: 4.0 + }, + { + type: "Row" + }, + { + id: "radiusStart", + name: "Radius Start", + type: "SliderFloat", + max: 4.0 + }, + { + type: "Row" + }, + { + id: "radiusFinish", + name: "Radius Finish", + type: "SliderFloat", + max: 4.0 + }, + { + type: "Row" + } + ], + Color: [ + { + id: "color", + name: "Color", + type: "Color", + defaultColor: { + red: 255, + green: 255, + blue: 255 + } + }, + { + type: "Row" + }, + { + id: "colorSpread", + name: "Color Spread", + type: "Color", + defaultColor: { + red: 0, + green: 0, + blue: 0 + } + }, + { + type: "Row" + }, + { + id: "colorStart", + name: "Color Start", + type: "Color", + defaultColor: { + red: 255, + green: 255, + blue: 255 + } + }, + { + type: "Row" + }, + { + id: "colorFinish", + name: "Color Finish", + type: "Color", + defaultColor: { + red: 255, + green: 255, + blue: 255 + } + }, + { + type: "Row" + } + ], + Acceleration: [ + { + id: "emitAcceleration", + name: "Emit Acceleration", + type: "Vector", + defaultRange: { + step: 0.01 + } + }, + { + type: "Row" + }, + { + id: "accelerationSpread", + name: "Acceleration Spread", + type: "Vector", + defaultRange: { + step: 0.01 + } + }, + { + type: "Row" + } + ], + Alpha: [ + { + id: "alpha", + name: "Alpha", + type: "SliderFloat" + }, + { + type: "Row" + }, + { + id: "alphaSpread", + name: "Alpha Spread", + type: "SliderFloat" + }, + { + type: "Row" + }, + { + id: "alphaStart", + name: "Alpha Start", + type: "SliderFloat" + }, + { + type: "Row" + }, + { + id: "alphaFinish", + name: "Alpha Finish", + type: "SliderFloat" + }, + { + type: "Row" + } + ], + Polar: [ + { + id: "polarStart", + name: "Polar Start", + unit: "deg", + type: "SliderRadian" + }, + { + type: "Row" + }, + { + id: "polarFinish", + name: "Polar Finish", + unit: "deg", + type: "SliderRadian" + }, + { + type: "Row" + } + ], + Azimuth: [ + { + id: "azimuthStart", + name: "Azimuth Start", + unit: "deg", + type: "SliderRadian", + min: -180, + max: 0 + }, + { + type: "Row" + }, + { + id: "azimuthFinish", + name: "Azimuth Finish", + unit: "deg", + type: "SliderRadian" + }, + { + type: "Row" + } + ] + }; + ui.setUI(menuStructure); + ui.setOnSelect(function () { + document.getElementById("show-properties-button").removeAttribute("disabled"); + document.getElementById("export-properties-button").setAttribute("disabled", true); + document.getElementById("import-properties-button").setAttribute("disabled", true); }); + ui.build(); + var overrideLoad = false; + if (openEventBridge === undefined) { + overrideLoad = true, + openEventBridge = function (callback) { + callback({ + emitWebEvent: function () {}, + submitChanges: function () {}, + scriptEventReceived: { + connect: function () { - EventBridge.emitWebEvent( - stringifiedData - ); - - listenForSettingsUpdates(); - window.onresize = setGUIWidthToWindowWidth; - }); - -}; - -function loadGUI() { - // whether or not to autoplace - gui = new dat.GUI({ - autoPlace: false - }); - - // if not autoplacing, put gui in a custom container - if (gui.autoPlace === false) { - var customContainer = document.getElementById('my-gui-container'); - customContainer.appendChild(gui.domElement); - } - - // presets for the GUI itself. a little confusing and import/export is mostly what we want to do at the moment. - // gui.remember(settings); - - colpickKeys = []; - var keys = _.keys(settings); - - _.each(keys, function(key) { - var shouldAllow = _.contains(keysToAllow, key); - - if (shouldAllow) { - var subKeys = _.keys(settings[key]); - var hasX = _.contains(subKeys, 'x'); - var hasY = _.contains(subKeys, 'y'); - var hasZ = _.contains(subKeys, 'z'); - var hasW = _.contains(subKeys, 'w'); - var hasRed = _.contains(subKeys, 'red'); - var hasGreen = _.contains(subKeys, 'green'); - var hasBlue = _.contains(subKeys, 'blue'); - - if ((hasX && hasY && hasZ) && hasW === false) { - vec3Keys.push(key); - } else if (hasX && hasY && hasZ && hasW) { - quatKeys.push(key); - } else if (hasRed || hasGreen || hasBlue) { - colorKeys.push(key); - - } else { - individualKeys.push(key); - } + } + } + }); + }; } - }); - - // alphabetize our keys - individualKeys.sort(); - vec3Keys.sort(); - quatKeys.sort(); - colorKeys.sort(); - - // add to gui in the order they should appear - gui.add(settings, 'importSettings'); - gui.add(settings, 'exportSettings'); - addIndividualKeys(); - addFolders(); - - // set the gui width to match the web window width - gui.width = window.innerWidth; - - // 2-way binding stuff - // if (AUTO_UPDATE) { - // setInterval(manuallyUpdateDisplay, UPDATE_ALL_FREQUENCY); - // registerDOMElementsForListenerBlocking(); - // } - -} - -function addIndividualKeys() { - _.each(individualKeys, function(key) { - // temporary patch for not crashing when this goes below 0 - var controller; - - if (key.indexOf('emitRate') > -1) { - controller = gui.add(settings, key).min(0); - } else { - controller = gui.add(settings, key); - } - - // 2-way - need to fix not being able to input exact values if constantly listening - // controller.listen(); - - // keep track of our controller - controllers.push(controller); - - // hook into change events for this gui controller - controller.onChange(function(value) { - // Fires on every change, drag, keypress, etc. - writeDataToInterface(this.property, value); + openEventBridge(function (EventBridge) { + ui.connect(EventBridge); }); - - }); -} - -function addFolders() { - _.each(colorKeys, function(key) { - createColorPicker(key); - }); - _.each(vec3Keys, function(key) { - createVec3Folder(key); - }); - _.each(quatKeys, function(key) { - createQuatFolder(key); - }); -} - -function createColorPicker(key) { - var colorObject = settings[key]; - - // Embed colpick's color picker into dat.GUI - var name = document.createElement('span'); - name.className = 'property-name'; - name.innerHTML = key; - - var container = document.createElement('div'); - container.appendChild(name); - - var $colPickContainer = $('
    ', { - id: key.toString(), - class: "color-box" - }); - $colPickContainer.css('background-color', "rgb(" + colorObject.red + "," + colorObject.green + "," + colorObject.blue + ")"); - container.appendChild($colPickContainer[0]); - - var $li = $('
  • ', { class: 'cr object color' }); - $li.append(container); - gui.__ul.appendChild($li[0]); - gui.onResize(); - - $colPickContainer.colpick({ - colorScheme: 'dark', - layout: 'hex', - color: { r: colorObject.red, g: colorObject.green, b: colorObject.blue }, - onSubmit: function (hsb, hex, rgb, el) { - $(el).css('background-color', '#' + hex); - $(el).colpickHide(); - - var obj = {}; - obj[key] = { red: rgb.r, green: rgb.g, blue: rgb.b }; - writeVec3ToInterface(obj); + if (overrideLoad) { + openEventBridge(); } - }); - - colpickKeys.push(key); -} - -function createVec3Folder(category) { - var folder = gui.addFolder(category); - - folder.add(settings[category], 'x').step(0.1).onChange(function(value) { - // Fires when a controller loses focus. - var obj = {}; - obj[category] = {}; - obj[category][this.property] = value; - obj[category].y = settings[category].y; - obj[category].z = settings[category].z; - writeVec3ToInterface(obj); - }); - - folder.add(settings[category], 'y').step(0.1).onChange(function(value) { - // Fires when a controller loses focus. - var obj = {}; - obj[category] = {}; - obj[category].x = settings[category].x; - obj[category][this.property] = value; - obj[category].z = settings[category].z; - writeVec3ToInterface(obj); - }); - - folder.add(settings[category], 'z').step(0.1).onChange(function(value) { - // Fires when a controller loses focus. - var obj = {}; - obj[category] = {}; - obj[category].y = settings[category].y; - obj[category].x = settings[category].x; - obj[category][this.property] = value; - writeVec3ToInterface(obj); - }); - - folders.push(folder); - folder.open(); -} - -function createQuatFolder(category) { - var folder = gui.addFolder(category); - - folder.add(settings[category], 'x').step(0.1).onChange(function(value) { - // Fires when a controller loses focus. - var obj = {}; - obj[category] = {}; - obj[category][this.property] = value; - obj[category].y = settings[category].y; - obj[category].z = settings[category].z; - obj[category].w = settings[category].w; - writeVec3ToInterface(obj); - }); - - folder.add(settings[category], 'y').step(0.1).onChange(function(value) { - // Fires when a controller loses focus. - var obj = {}; - obj[category] = {}; - obj[category].x = settings[category].x; - obj[category][this.property] = value; - obj[category].z = settings[category].z; - obj[category].w = settings[category].w; - writeVec3ToInterface(obj); - }); - - folder.add(settings[category], 'z').step(0.1).onChange(function(value) { - // Fires when a controller loses focus. - var obj = {}; - obj[category] = {}; - obj[category].x = settings[category].x; - obj[category].y = settings[category].y; - obj[category][this.property] = value; - obj[category].w = settings[category].w; - writeVec3ToInterface(obj); - }); - - folder.add(settings[category], 'w').step(0.1).onChange(function(value) { - // Fires when a controller loses focus. - var obj = {}; - obj[category] = {}; - obj[category].x = settings[category].x; - obj[category].y = settings[category].y; - obj[category].z = settings[category].z; - obj[category][this.property] = value; - writeVec3ToInterface(obj); - }); - - folders.push(folder); - folder.open(); -} - -function convertColorObjectToArray(colorObject) { - var colorArray = []; - - _.each(colorObject, function(singleColor) { - colorArray.push(singleColor); - }); - - return colorArray; -} - -function convertColorArrayToObject(colorArray) { - var colorObject = { - red: colorArray[0], - green: colorArray[1], - blue: colorArray[2] }; - - return colorObject; -} - -function writeDataToInterface(property, value) { - var data = {}; - data[property] = value; - - var sendData = { - messageType: "settings_update", - updatedSettings: data - }; - - var stringifiedData = JSON.stringify(sendData); - - EventBridge.emitWebEvent(stringifiedData); -} - -function writeVec3ToInterface(obj) { - var sendData = { - messageType: "settings_update", - updatedSettings: obj - }; - - var stringifiedData = JSON.stringify(sendData); - - EventBridge.emitWebEvent(stringifiedData); -} - -function listenForSettingsUpdates() { - EventBridge.scriptEventReceived.connect(function(data) { - data = JSON.parse(data); - if (data.messageType === 'particle_settings') { - _.each(data.currentProperties, function(value, key) { - settings[key] = {}; - settings[key] = value; - }); - - if (gui) { - manuallyUpdateDisplay(); - } else { - loadGUI(); - } - if (!active) { - // gui.toggleHide(); - gui.closed = false; - } - active = true; - - } else if (data.messageType === "particle_close") { - // none of this seems to work. - // if (active) { - // gui.toggleHide(); - // } - active = false; - gui.closed = true; - } - }); -} - -function manuallyUpdateDisplay() { - // Iterate over all controllers - // this is expensive, write a method for indiviudal controllers and use it when the value is different than a cached value, perhaps. - var i; - for (i in gui.__controllers) { - gui.__controllers[i].updateDisplay(); - } - - // Update color pickers - for (i in colpickKeys) { - var color = settings[colpickKeys[i]]; - var $object = $('#' + colpickKeys[i]); - $object.css('background-color', "rgb(" + color.red + "," + color.green + "," + color.blue + ")"); - $object.colpickSetColor({ r: color.red, g: color.green, b: color.blue }, true); - } -} - -function setGUIWidthToWindowWidth() { - if (gui !== null) { - gui.width = window.innerWidth; - } -} - -function handleInputKeyPress(e) { - if (e.keyCode === 13) { - importSettings(); - } - return false; -} - -function importSettings() { - var importInput = document.getElementById('importer-input'); - - try { - var importedSettings = JSON.parse(importInput.value); - - var keys = _.keys(importedSettings); - - _.each(keys, function(key) { - var shouldAllow = _.contains(keysToAllow, key); - - if (!shouldAllow) { - return; - } - - settings[key] = importedSettings[key]; - }); - - writeVec3ToInterface(settings); - - manuallyUpdateDisplay(); - } catch (e) { - alert('Not properly formatted JSON'); - } -} - -function prepareSettingsForExport() { - var keys = _.keys(settings); - - var exportSettings = {}; - - _.each(keys, function(key) { - var shouldAllow = _.contains(keysToAllow, key); - - if (!shouldAllow) { - return; - } - - if (key.indexOf('color') > -1) { - var colorObject = convertColorArrayToObject(settings[key]); - settings[key] = colorObject; - } - - exportSettings[key] = settings[key]; - }); - - return JSON.stringify(exportSettings, null, 4); -} - -function showPreselectedPrompt() { - var elem = document.getElementById("exported-props"); - var exportSettings = prepareSettingsForExport(); - elem.innerHTML = ""; - var buttonnode = document.createElement('input'); - buttonnode.setAttribute('type', 'button'); - buttonnode.setAttribute('value', 'close'); - elem.appendChild(document.createTextNode("COPY THE BELOW FIELD TO CLIPBOARD:")); - elem.appendChild(document.createElement("br")); - var textAreaNode = document.createElement("textarea"); - textAreaNode.value = exportSettings; - elem.appendChild(textAreaNode); - elem.appendChild(document.createElement("br")); - elem.appendChild(buttonnode); - - buttonnode.onclick = function() { - console.log("click"); - elem.innerHTML = ""; - }; - - // window.alert("Ctrl-C to copy, then Enter.", prepareSettingsForExport()); -} - -function removeContainerDomElement() { - var elem = document.getElementById("my-gui-container"); - elem.parentNode.removeChild(elem); -} - -function removeListenerFromGUI(key) { - _.each(gui.__listening, function(controller, index) { - if (controller.property === key) { - storedController = controller; - gui.__listening.splice(index, 1); - } - }); -} - -// the section below is to try to work at achieving two way bindings; - -function addListenersBackToGUI() { - gui.__listening.push(storedController); - storedController = null; -} - -function registerDOMElementsForListenerBlocking() { - _.each(gui.__controllers, function(controller) { - var input = controller.domElement.childNodes[0]; - input.addEventListener('focus', function() { - console.log('INPUT ELEMENT GOT FOCUS!' + controller.property); - removeListenerFromGUI(controller.property); - }); - }); - - _.each(gui.__controllers, function(controller) { - var input = controller.domElement.childNodes[0]; - input.addEventListener('blur', function() { - console.log('INPUT ELEMENT GOT BLUR!' + controller.property); - addListenersBackToGUI(); - }); - }); - - // also listen to inputs inside of folders - _.each(gui.__folders, function(folder) { - _.each(folder.__controllers, function(controller) { - var input = controller.__input; - input.addEventListener('focus', function() { - console.log('FOLDER ELEMENT GOT FOCUS!' + controller.property); - }); - }); - }); -} +})(); diff --git a/scripts/system/particle_explorer/particleExplorerTool.js b/scripts/system/particle_explorer/particleExplorerTool.js index b3db475ab0..d85fc169b1 100644 --- a/scripts/system/particle_explorer/particleExplorerTool.js +++ b/scripts/system/particle_explorer/particleExplorerTool.js @@ -4,23 +4,22 @@ // Created by Eric Levin on 2/15/16 // Copyright 2016 High Fidelity, Inc. // Adds particleExplorer tool to the edit panel when a user selects a particle entity from the edit tool window -// This is an example of a new, easy way to do two way bindings between dynamically created GUI and in-world entities. +// This is an example of a new, easy way to do two way bindings between dynamically created GUI and in-world entities. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -/*global window, alert, EventBridge, dat, listenForSettingsUpdates,createVec3Folder,createQuatFolder,writeVec3ToInterface,writeDataToInterface*/ +/* global window, alert, ParticleExplorerTool, EventBridge, dat, listenForSettingsUpdates,createVec3Folder,createQuatFolder,writeVec3ToInterface,writeDataToInterface*/ var PARTICLE_EXPLORER_HTML_URL = Script.resolvePath('particleExplorer.html'); ParticleExplorerTool = function() { var that = {}; - that.createWebView = function() { that.webView = Tablet.getTablet("com.highfidelity.interface.tablet.system"); that.webView.setVisible = function(value) {}; - that.webView.webEventReceived.connect(that.webEventReceived); + that.webView.webEventReceived.connect(that.webEventReceived); } that.destroyWebView = function() { @@ -38,6 +37,9 @@ ParticleExplorerTool = function() { that.webEventReceived = function(data) { var data = JSON.parse(data); if (data.messageType === "settings_update") { + if (data.updatedSettings.emitOrientation) { + data.updatedSettings.emitOrientation = Quat.fromVec3Degrees(data.updatedSettings.emitOrientation); + } Entities.editEntity(that.activeParticleEntity, data.updatedSettings); } } diff --git a/scripts/system/selectAudioDevice.js b/scripts/system/selectAudioDevice.js deleted file mode 100644 index 2d40795692..0000000000 --- a/scripts/system/selectAudioDevice.js +++ /dev/null @@ -1,215 +0,0 @@ -"use strict"; - -// -// audioDeviceExample.js -// examples -// -// Created by Brad Hefta-Gaub on 3/22/14 -// Copyright 2013 High Fidelity, Inc. -// -// This is an example script that demonstrates use of the Menu object -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -(function() { // BEGIN LOCAL_SCOPE - -const INPUT = "Input"; -const OUTPUT = "Output"; -const SELECT_AUDIO_SCRIPT_STARTUP_TIMEOUT = 300; -// -// VAR DEFINITIONS -// -var debugPrintStatements = true; -const INPUT_DEVICE_SETTING = "audio_input_device"; -const OUTPUT_DEVICE_SETTING = "audio_output_device"; -var audioDevicesList = []; // placeholder for menu items -var wasHmdActive = false; // assume it's not active to start -var switchedAudioInputToHMD = false; -var switchedAudioOutputToHMD = false; -var previousSelectedInputAudioDevice = ""; -var previousSelectedOutputAudioDevice = ""; - -var interfaceInputDevice = ""; -var interfaceOutputDevice = ""; - -// -// BEGIN FUNCTION DEFINITIONS -// -function debug() { - if (debugPrintStatements) { - print.apply(null, [].concat.apply(["selectAudioDevice.js:"], [].map.call(arguments, JSON.stringify))); - } -} - - -function setupAudioMenus() { - // menu events can be triggered asynchronously; skip them for 200ms to avoid recursion and false switches - removeAudioMenus(); - - // Setup audio input devices - Menu.addSeparator("Audio", "Input Audio Device"); - var currentInputDevice = AudioDevice.getInputDevice() - for (var i = 0; i < AudioDevice.inputAudioDevices.length; i++) { - var audioDeviceMenuString = "Use " + AudioDevice.inputAudioDevices[i] + " for Input"; - Menu.addMenuItem({ - menuName: "Audio", - menuItemName: audioDeviceMenuString, - isCheckable: true, - isChecked: AudioDevice.inputAudioDevices[i] == currentInputDevice - }); - audioDevicesList.push(audioDeviceMenuString); - } - - // Setup audio output devices - Menu.addSeparator("Audio", "Output Audio Device"); - var currentOutputDevice = AudioDevice.getOutputDevice() - for (var i = 0; i < AudioDevice.outputAudioDevices.length; i++) { - var audioDeviceMenuString = "Use " + AudioDevice.outputAudioDevices[i] + " for Output"; - Menu.addMenuItem({ - menuName: "Audio", - menuItemName: audioDeviceMenuString, - isCheckable: true, - isChecked: AudioDevice.outputAudioDevices[i] == currentOutputDevice - }); - audioDevicesList.push(audioDeviceMenuString); - } -} - -function removeAudioMenus() { - Menu.removeSeparator("Audio", "Input Audio Device"); - Menu.removeSeparator("Audio", "Output Audio Device"); - - for (var index = 0; index < audioDevicesList.length; index++) { - if (Menu.menuItemExists("Audio", audioDevicesList[index])) { - Menu.removeMenuItem("Audio", audioDevicesList[index]); - } - } - - Menu.removeMenu("Audio > Devices"); - - audioDevicesList = []; -} - -function onDevicechanged() { - debug("System audio devices changed. Removing and replacing Audio Menus..."); - setupAudioMenus(); -} - -function onMenuEvent(audioDeviceMenuString) { - if (Menu.isOptionChecked(audioDeviceMenuString) && - (audioDeviceMenuString !== interfaceInputDevice && - audioDeviceMenuString !== interfaceOutputDevice)) { - AudioDevice.setDeviceFromMenu(audioDeviceMenuString) - } -} - -function onCurrentDeviceChanged() { - debug("System audio device switched. "); - interfaceInputDevice = "Use " + AudioDevice.getInputDevice() + " for Input"; - interfaceOutputDevice = "Use " + AudioDevice.getOutputDevice() + " for Output"; - for (var index = 0; index < audioDevicesList.length; index++) { - if (audioDevicesList[index] === interfaceInputDevice || - audioDevicesList[index] === interfaceOutputDevice) { - if (Menu.isOptionChecked(audioDevicesList[index]) === false) - Menu.setIsOptionChecked(audioDevicesList[index], true); - } else { - if (Menu.isOptionChecked(audioDevicesList[index]) === true) - Menu.setIsOptionChecked(audioDevicesList[index], false); - } - } -} - -function restoreAudio() { - if (switchedAudioInputToHMD) { - debug("Switching back from HMD preferred audio input to: " + previousSelectedInputAudioDevice); - AudioDevice.setInputDeviceAsync(previousSelectedInputAudioDevice) - switchedAudioInputToHMD = false; - } - if (switchedAudioOutputToHMD) { - debug("Switching back from HMD preferred audio output to: " + previousSelectedOutputAudioDevice); - AudioDevice.setOutputDeviceAsync(previousSelectedOutputAudioDevice) - switchedAudioOutputToHMD = false; - } -} - -// Some HMDs (like Oculus CV1) have a built in audio device. If they -// do, then this function will handle switching to that device automatically -// when you goActive with the HMD active. -function checkHMDAudio() { - // HMD Active state is changing; handle switching - if (HMD.active != wasHmdActive) { - debug("HMD Active state changed!"); - - // We're putting the HMD on; switch to those devices - if (HMD.active) { - debug("HMD is now Active."); - var hmdPreferredAudioInput = HMD.preferredAudioInput(); - var hmdPreferredAudioOutput = HMD.preferredAudioOutput(); - debug("hmdPreferredAudioInput: " + hmdPreferredAudioInput); - debug("hmdPreferredAudioOutput: " + hmdPreferredAudioOutput); - - if (hmdPreferredAudioInput !== "") { - debug("HMD has preferred audio input device."); - previousSelectedInputAudioDevice = Settings.getValue(INPUT_DEVICE_SETTING); - debug("previousSelectedInputAudioDevice: " + previousSelectedInputAudioDevice); - if (hmdPreferredAudioInput != previousSelectedInputAudioDevice) { - switchedAudioInputToHMD = true; - AudioDevice.setInputDeviceAsync(hmdPreferredAudioInput) - } - } - if (hmdPreferredAudioOutput !== "") { - debug("HMD has preferred audio output device."); - previousSelectedOutputAudioDevice = Settings.getValue(OUTPUT_DEVICE_SETTING); - debug("previousSelectedOutputAudioDevice: " + previousSelectedOutputAudioDevice); - if (hmdPreferredAudioOutput != previousSelectedOutputAudioDevice) { - switchedAudioOutputToHMD = true; - AudioDevice.setOutputDeviceAsync(hmdPreferredAudioOutput) - } - } - } else { - debug("HMD no longer active. Restoring audio I/O devices..."); - restoreAudio(); - } - } - wasHmdActive = HMD.active; -} - -// -// END FUNCTION DEFINITIONS -// - -// -// BEGIN SCRIPT BODY -// -// Wait for the C++ systems to fire up before trying to do anything with audio devices -Script.setTimeout(function () { - debug("Connecting deviceChanged(), displayModeChanged(), and switchAudioDevice()..."); - AudioDevice.deviceChanged.connect(onDevicechanged); - AudioDevice.currentInputDeviceChanged.connect(onCurrentDeviceChanged); - AudioDevice.currentOutputDeviceChanged.connect(onCurrentDeviceChanged); - HMD.displayModeChanged.connect(checkHMDAudio); - Menu.menuItemEvent.connect(onMenuEvent); - debug("Setting up Audio I/O menu for the first time..."); - setupAudioMenus(); - debug("Checking HMD audio status...") - checkHMDAudio(); -}, SELECT_AUDIO_SCRIPT_STARTUP_TIMEOUT); - -debug("Connecting scriptEnding()"); -Script.scriptEnding.connect(function () { - restoreAudio(); - removeAudioMenus(); - Menu.menuItemEvent.disconnect(onMenuEvent); - HMD.displayModeChanged.disconnect(checkHMDAudio); - AudioDevice.currentInputDeviceChanged.disconnect(onCurrentDeviceChanged); - AudioDevice.currentOutputDeviceChanged.disconnect(onCurrentDeviceChanged); - AudioDevice.deviceChanged.disconnect(onDevicechanged); -}); - -// -// END SCRIPT BODY -// - -}()); // END LOCAL_SCOPE diff --git a/scripts/system/snapshot.js b/scripts/system/snapshot.js index 4e3fb95140..b045e64c6a 100644 --- a/scripts/system/snapshot.js +++ b/scripts/system/snapshot.js @@ -144,7 +144,9 @@ function onMessage(message) { isDomainOpen(Settings.getValue("previousSnapshotDomainID"), function (canShare) { var isGif = fileExtensionMatches(message.data, "gif"); isLoggedIn = Account.isLoggedIn(); - isUploadingPrintableStill = canShare && Account.isLoggedIn() && !isGif; + if (!isGif) { + isUploadingPrintableStill = canShare && Account.isLoggedIn(); + } if (canShare) { if (isLoggedIn) { print('Sharing snapshot with audience "for_url":', message.data); diff --git a/scripts/system/tablet-ui/tabletUI.js b/scripts/system/tablet-ui/tabletUI.js index e45fc8d87b..b7324ed28c 100644 --- a/scripts/system/tablet-ui/tabletUI.js +++ b/scripts/system/tablet-ui/tabletUI.js @@ -185,10 +185,7 @@ //TODO: move to tablet qml? if (tabletShown) { - var currentMicEnabled = !Menu.isOptionChecked(MUTE_MICROPHONE_MENU_ITEM); - var currentMicLevel = getMicLevel(); - gTablet.updateMicEnabled(currentMicEnabled); - gTablet.updateAudioBar(currentMicLevel); + gTablet.updateAudioBar(getMicLevel()); } if (now - validCheckTime > MSECS_PER_SEC) { diff --git a/server-console/src/main.js b/server-console/src/main.js index 408a17bd56..95fb0d81b2 100644 --- a/server-console/src/main.js +++ b/server-console/src/main.js @@ -101,6 +101,10 @@ function getApplicationDataDirectory() { return path.join(getRootHifiDataDirectory(), '/Server Console'); } +// Update lock filepath +const UPDATER_LOCK_FILENAME = ".updating"; +const UPDATER_LOCK_FULL_PATH = getRootHifiDataDirectory() + "/" + UPDATER_LOCK_FILENAME; + // Configure log global.log = require('electron-log'); const logFile = getApplicationDataDirectory() + '/log.txt'; @@ -630,11 +634,22 @@ function checkNewContent() { userConfig.save(configPath); } }); + } else if (fs.existsSync(UPDATER_LOCK_FULL_PATH)) { + backupResourceDirectoriesAndRestart(); } } }); } +function removeIncompleteUpdate(acResourceDirectory, dsResourceDirectory) { + if (fs.existsSync(UPDATER_LOCK_FULL_PATH)) { + log.debug('Removing incomplete content update files before copying new update'); + fs.emptyDirSync(dsResourceDirectory); + fs.emptyDirSync(acResourceDirectory); + } else { + fs.ensureFileSync(UPDATER_LOCK_FULL_PATH); + } +} function maybeInstallDefaultContentSet(onComplete) { // Check for existing data @@ -673,7 +688,11 @@ function maybeInstallDefaultContentSet(onComplete) { } log.debug("Found contentPath:" + argv.contentPath); + if (argv.contentPath) { + // check if we're updating a data folder whose update is incomplete + removeIncompleteUpdate(acResourceDirectory, dsResourceDirectory); + fs.copy(argv.contentPath, getRootHifiDataDirectory(), function (err) { if (err) { log.debug('Could not copy home content: ' + err); @@ -682,12 +701,12 @@ function maybeInstallDefaultContentSet(onComplete) { log.debug('Copied home content over to: ' + getRootHifiDataDirectory()); userConfig.set('homeContentLastModified', new Date()); userConfig.save(configPath); + fs.removeSync(UPDATER_LOCK_FULL_PATH); onComplete(); }); return; } - // Show popup var window = new BrowserWindow({ icon: appIcon, @@ -718,6 +737,9 @@ function maybeInstallDefaultContentSet(onComplete) { var aborted = false; + // check if we're updating a data folder whose update is incomplete + removeIncompleteUpdate(acResourceDirectory, dsResourceDirectory); + // Start downloading content set var req = progress(request.get({ url: HOME_CONTENT_URL @@ -763,6 +785,7 @@ function maybeInstallDefaultContentSet(onComplete) { log.debug("Finished unarchiving home content set"); userConfig.set('homeContentLastModified', new Date()); userConfig.save(configPath); + fs.removeSync(UPDATER_LOCK_FULL_PATH); sendStateUpdate('complete'); }); diff --git a/tests/gpu-test/src/TestFbx.cpp b/tests/gpu-test/src/TestFbx.cpp index 11cd60c3f7..538bb0a973 100644 --- a/tests/gpu-test/src/TestFbx.cpp +++ b/tests/gpu-test/src/TestFbx.cpp @@ -13,7 +13,6 @@ #include #include -#include struct MyVertex { vec3 position; diff --git a/tests/gpu-test/src/TestWindow.cpp b/tests/gpu-test/src/TestWindow.cpp index d62467f510..6436ff1ef4 100644 --- a/tests/gpu-test/src/TestWindow.cpp +++ b/tests/gpu-test/src/TestWindow.cpp @@ -22,7 +22,6 @@ #include #include #include -#include #ifdef DEFERRED_LIGHTING extern void initDeferredPipelines(render::ShapePlumber& plumber); diff --git a/tests/gpu-test/src/main.cpp b/tests/gpu-test/src/main.cpp index 975dbf175c..d37be7c790 100644 --- a/tests/gpu-test/src/main.cpp +++ b/tests/gpu-test/src/main.cpp @@ -45,8 +45,6 @@ #include #include #include -#include -#include #include #include diff --git a/tests/render-perf/CMakeLists.txt b/tests/render-perf/CMakeLists.txt index 553e7c06e7..a8e4ab286c 100644 --- a/tests/render-perf/CMakeLists.txt +++ b/tests/render-perf/CMakeLists.txt @@ -10,7 +10,8 @@ setup_hifi_project(Quick Gui OpenGL) set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "Tests/manual-tests/") # link in the shared libraries -link_hifi_libraries(shared octree ktx gl gpu gpu-gl render model model-networking networking render-utils fbx entities entities-renderer animation audio avatars script-engine physics image procedural) +link_hifi_libraries(shared networking model fbx ktx image octree gl gpu gpu-gl render model-networking networking render-utils entities entities-renderer animation audio avatars script-engine physics procedural) + package_libraries_for_deployment() diff --git a/tests/render-perf/src/main.cpp b/tests/render-perf/src/main.cpp index 1e81ee590e..52592cd202 100644 --- a/tests/render-perf/src/main.cpp +++ b/tests/render-perf/src/main.cpp @@ -507,7 +507,6 @@ public: REGISTER_ENTITY_TYPE_WITH_FACTORY(Web, WebEntityItem::factory); DependencyManager::set(_octree->getTree()); - getEntities()->setViewFrustum(_viewFrustum); auto nodeList = DependencyManager::get(); NodePermissions permissions; permissions.setAll(true); @@ -735,8 +734,8 @@ private: class EntityUpdateOperator : public RecurseOctreeOperator { public: EntityUpdateOperator(const qint64& now) : now(now) {} - bool preRecursion(OctreeElementPointer element) override { return true; } - bool postRecursion(OctreeElementPointer element) override { + bool preRecursion(const OctreeElementPointer& element) override { return true; } + bool postRecursion(const OctreeElementPointer& element) override { EntityTreeElementPointer entityTreeElement = std::static_pointer_cast(element); entityTreeElement->forEachEntity([&](EntityItemPointer entityItem) { if (!entityItem->isParentIDValid()) { @@ -865,7 +864,6 @@ private: } } - getEntities()->setViewFrustum(_viewFrustum); EntityUpdateOperator updateOperator(now); //getEntities()->getTree()->recurseTreeWithOperator(&updateOperator); { diff --git a/tests/networking/src/FileCacheTests.cpp b/tests/shared/src/FileCacheTests.cpp similarity index 100% rename from tests/networking/src/FileCacheTests.cpp rename to tests/shared/src/FileCacheTests.cpp diff --git a/tests/networking/src/FileCacheTests.h b/tests/shared/src/FileCacheTests.h similarity index 100% rename from tests/networking/src/FileCacheTests.h rename to tests/shared/src/FileCacheTests.h diff --git a/tutorial/ACAudioSearchAndInject_tutorial.js b/tutorial/ACAudioSearchAndInject_tutorial.js deleted file mode 100644 index 5e2998ff1e..0000000000 --- a/tutorial/ACAudioSearchAndInject_tutorial.js +++ /dev/null @@ -1,308 +0,0 @@ -"use strict"; - -/*jslint nomen: true, plusplus: true, vars: true*/ -/*global AvatarList, Entities, EntityViewer, Script, SoundCache, Audio, print, randFloat*/ -// -// ACAudioSearchAndInject.js -// audio -// -// Created by Eric Levin and Howard Stearns 2/1/2016 -// Copyright 2016 High Fidelity, Inc. -// -// Keeps track of all sounds within QUERY_RADIUS of an avatar, where a "sound" is specified in entity userData. -// Inject as many as practical into the audio mixer. -// See acAudioSearchAndCompatibilityEntitySpawner.js. -// -// This implementation takes some precautions to scale well: -// - It doesn't hastle the entity server because it issues at most one octree query every UPDATE_TIME period, regardless of the number of avatars. -// - It does not load itself because it only gathers entities once every UPDATE_TIME period, and only -// checks entity properties for those small number of entities that are currently playing (plus a RECHECK_TIME period examination of all entities). -// This implementation tries to use all the available injectors. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html - -var MSEC_PER_SEC = 1000; -var SOUND_DATA_KEY = "io.highfidelity.soundKey"; // Sound data is specified in userData under this key. -var old_sound_data_key = "soundKey"; // For backwards compatibility. -var QUERY_RADIUS = 50; // meters -var UPDATE_TIME = 100; // ms. We'll update just one thing on this period. -var EXPIRATION_TIME = 5 * MSEC_PER_SEC; // ms. Remove sounds that have been out of range for this time. -var RECHECK_TIME = 10 * MSEC_PER_SEC; // ms. Check for new userData properties this often when not currently playing. -// (By not checking most of the time when not playing, we can efficiently go through all entities without getEntityProperties.) -var UPDATES_PER_STATS_LOG = RECHECK_TIME / UPDATE_TIME; // (It's nice to smooth out the results by straddling a recheck.) - -var DEFAULT_SOUND_DATA = { - volume: 0.5, // userData cannot specify zero volume with our current method of defaulting. - loop: false, // Default must be false with our current method of defaulting, else there's no way to get a false value. - playbackGap: MSEC_PER_SEC, // in ms - playbackGapRange: 0 // in ms -}; - -//var AGENT_AVATAR_POSITION = { x: -1.5327, y: 0.672515, z: 5.91573 }; -var AGENT_AVATAR_POSITION = { x: -2.83785, y: 1.45243, z: -13.6042 }; - -//var isACScript = this.EntityViewer !== undefined; -var isACScript = true; - -if (isACScript) { - Agent.isAvatar = true; // This puts a robot at 0,0,0, but is currently necessary in order to use AvatarList. - Avatar.skeletonModelURL = "http://hifi-content.s3.amazonaws.com/ozan/dev/avatars/invisible_avatar/invisible_avatar.fst"; - Avatar.position = AGENT_AVATAR_POSITION; - Agent.isListeningToAudioStream = true; -} -function ignore() {} -function debug() { // Display the arguments not just [Object object]. - //print.apply(null, [].map.call(arguments, JSON.stringify)); -} - -function randFloat(low, high) { - return low + Math.random() * (high - low); -} - -if (isACScript) { - EntityViewer.setCenterRadius(QUERY_RADIUS); -} - -// ENTITY DATA CACHE -// -var entityCache = {}; // A dictionary of unexpired EntityData objects. -var entityInvalidUserDataCache = {}; // A cache containing the entity IDs that have - // previously been identified as containing non-JSON userData. - // We use a dictionary here so id lookups are constant time. -var examinationCount = 0; -function EntityDatum(entityIdentifier) { // Just the data of an entity that we need to know about. - // This data is only use for our sound injection. There is no need to store such info in the replicated entity on everyone's computer. - var that = this; - that.lastUserDataUpdate = 0; // new entity is in need of rechecking user data - // State Transitions: - // no data => no data | sound data | expired - // expired => stop => remove - // sound data => downloading - // downloading => downloading | waiting - // waiting => playing | waiting (if too many already playing) - // playing => update position etc | no data - that.stop = function stop() { - if (!that.sound) { - return; - } - print("stopping sound", entityIdentifier, that.url); - delete that.sound; - delete that.url; - if (!that.injector) { - return; - } - that.injector.stop(); - delete that.injector; - }; - this.update = function stateTransitions(expirationCutoff, userDataCutoff, now) { - if (that.timestamp < expirationCutoff) { // EXPIRED => STOP => REMOVE - that.stop(); // Alternatively, we could fade out and then stop... - delete entityCache[entityIdentifier]; - return; - } - var properties, soundData; // Latest data, pulled from local octree. - - // getEntityProperties locks the tree, which competes with the asynchronous processing of queryOctree results. - // Most entity updates are fast and only a very few do getEntityProperties. - function ensureSoundData() { // We only getEntityProperities when we need to. - if (properties) { - return; - } - properties = Entities.getEntityProperties(entityIdentifier, ['userData', 'position']); - examinationCount++; // Collect statistics on how many getEntityProperties we do. - debug("updating", that, properties); - try { - var userData = properties.userData && JSON.parse(properties.userData); - soundData = userData && (userData[SOUND_DATA_KEY] || userData[old_sound_data_key]); // Don't store soundData yet. Let state changes compare. - that.lastUserDataUpdate = now; // But do update these ... - that.url = soundData && soundData.url; - that.playAfter = that.url && now; - } catch (err) { - if (!(entityIdentifier in entityInvalidUserDataCache)) { - print(err, properties.userData); - entityInvalidUserDataCache[entityIdentifier] = true; - } - } - } - - // Stumbling on big new pile of entities will do a lot of getEntityProperties. Once. - if (that.lastUserDataUpdate < userDataCutoff) { // NO DATA => SOUND DATA - ensureSoundData(); - } - - if (!that.url) { // NO DATA => NO DATA - return that.stop(); - } - - if (!that.sound) { // SOUND DATA => DOWNLOADING - that.sound = SoundCache.getSound(soundData.url); // SoundCache can manage duplicates better than we can. - } - - if (!that.sound.downloaded) { // DOWNLOADING => DOWNLOADING - return; - } - - if (that.playAfter > now) { // DOWNLOADING | WAITING => WAITING - return; - } - - ensureSoundData(); // We'll try to play/setOptions and will need position, so we might as well get soundData, too. - if (soundData.url !== that.url) { // WAITING => NO DATA (update next time around) - return that.stop(); - } - - var options = { - position: properties.position, - loop: soundData.loop || DEFAULT_SOUND_DATA.loop, - volume: soundData.volume || DEFAULT_SOUND_DATA.volume - }; - - function repeat() { - return !options.loop && (soundData.playbackGap >= 0); - } - - function randomizedNextPlay() { // time of next play or recheck, randomized to distribute the work - var range = soundData.playbackGapRange || DEFAULT_SOUND_DATA.playbackGapRange, - base = repeat() ? ((that.sound.duration * MSEC_PER_SEC) + (soundData.playbackGap || DEFAULT_SOUND_DATA.playbackGap)) : RECHECK_TIME; - return now + base + randFloat(-Math.min(base, range), range); - } - - if (that.injector && soundData.playing === false) { - that.injector.stop(); - that.injector = null; - } - - if (!that.injector) { - if (soundData.playing === false) { // WAITING => PLAYING | WAITING - return; - } - debug("starting", that, options); - that.injector = Audio.playSound(that.sound, options); // Might be null if at at injector limit. Will try again later. - if (that.injector) { - print("started", entityIdentifier, that.url); - } else { // Don't hammer ensureSoundData or injector manager. - that.playAfter = randomizedNextPlay(); - } - return; - } - - that.injector.setOptions(options); // PLAYING => UPDATE POSITION ETC - if (!that.injector.playing) { // Subtle: a looping sound will not check playbackGap. - if (repeat()) { // WAITING => PLAYING - // Setup next play just once, now. Changes won't be looked at while we wait. - that.playAfter = randomizedNextPlay(); - // Subtle: if the restart fails b/c we're at injector limit, we won't try again until next playAfter. - that.injector.restart(); - } else { // PLAYING => NO DATA - that.playAfter = Infinity; // was one-shot and we're finished - } - } - }; -} - -function internEntityDatum(entityIdentifier, timestamp, avatarPosition, avatar) { - ignore(avatarPosition, avatar); // We could use avatars and/or avatarPositions to prioritize which ones to play. - var entitySound = entityCache[entityIdentifier]; - if (!entitySound) { - entitySound = entityCache[entityIdentifier] = new EntityDatum(entityIdentifier); - } - entitySound.timestamp = timestamp; // Might be updated for multiple avatars. That's fine. -} - -var nUpdates = UPDATES_PER_STATS_LOG, lastStats = Date.now(); - -function updateAllEntityData() { // A fast update of all entities we know about. A few make sounds. - var now = Date.now(), - expirationCutoff = now - EXPIRATION_TIME, - userDataRecheckCutoff = now - RECHECK_TIME; - Object.keys(entityCache).forEach(function (entityIdentifier) { - entityCache[entityIdentifier].update(expirationCutoff, userDataRecheckCutoff, now); - }); - if (nUpdates-- <= 0) { // Report statistics. - // For example, with: - // injector-limit = 40 (in C++ code) - // N_SOUNDS = 1000 (from userData in, e.g., acAudioSearchCompatibleEntitySpawner.js) - // replay-period = 3 + 20 = 23 (seconds, ditto) - // stats-period = UPDATES_PER_STATS_LOG * UPDATE_TIME / MSEC_PER_SEC = 10 seconds - // The log should show between each stats report: - // "start" lines ~= injector-limit * P(finish) = injector-limit * stats-period/replay-period = 17 ? - // total attempts at starting ("start" lines + "could not thread" lines) ~= N_SOUNDS = 1000 ? - // entities > N_SOUNDS * (1+ N_SILENT_ENTITIES_PER_SOUND) = 11000 + whatever was in the scene before running spawner - // sounds = N_SOUNDS = 1000 - // getEntityPropertiesPerUpdate ~= playing + failed-starts/UPDATES_PER_STATS_LOG + other-rechecks-each-update - // = injector-limit + (total attempts - "start" lines)/UPDATES_PER_STATS__LOG - // + (entities - playing - failed-starts/UPDATES_PER_STATS_LOG) * P(recheck-in-update) - // where failed-starts/UPDATES_PER_STATS_LOG = (1000-17)/100 = 10 - // = 40 + 10 + (11000 - 40 - 10)*UPDATE_TIME/RECHECK_TIME - // = 40 + 10 + 10950*0.01 = 159 (mostly proportional to enties/RECHECK_TIME) - // millisecondsPerUpdate ~= UPDATE_TIME = 100 (+ some timer machinery time) - // this assignment client activity monitor < 100% cpu - var stats = { - entities: 0, - sounds: 0, - playing: 0, - getEntityPropertiesPerUpdate: examinationCount / UPDATES_PER_STATS_LOG, - millisecondsPerUpdate: (now - lastStats) / UPDATES_PER_STATS_LOG - }; - nUpdates = UPDATES_PER_STATS_LOG; - lastStats = now; - examinationCount = 0; - Object.keys(entityCache).forEach(function (entityIdentifier) { - var datum = entityCache[entityIdentifier]; - stats.entities++; - if (datum.url) { - stats.sounds++; - if (datum.injector && datum.injector.playing) { - stats.playing++; - } - } - }); - print(JSON.stringify(stats)); - } -} - -// Update the set of which EntityData we know about. -// -function updateEntiesForAvatar(avatarIdentifier) { // Just one piece of update work. - // This does at most: - // one queryOctree request of the entity server, and - // one findEntities geometry query of our own octree, and - // a quick internEntityDatum of each of what may be a large number of entityIdentifiers. - // The idea is that this is a nice bounded piece of work that should not be done too frequently. - // However, it means that we won't learn about new entities until, on average (nAvatars * UPDATE_TIME) + query round trip. - var avatar = AvatarList.getAvatar(avatarIdentifier), avatarPosition = avatar && avatar.position; - if (!avatarPosition) { // No longer here. - return; - } - var timestamp = Date.now(); - if (isACScript) { - EntityViewer.setPosition(avatarPosition); - EntityViewer.queryOctree(); // Requests an update, but there's no telling when we'll actually see different results. - } - var entities = Entities.findEntities(avatarPosition, QUERY_RADIUS); - debug("found", entities.length, "entities near", avatar.name || "unknown", "at", avatarPosition); - entities.forEach(function (entityIdentifier) { - internEntityDatum(entityIdentifier, timestamp, avatarPosition, avatar); - }); -} - -// Slowly update the set of data we have to work with. -// -var workQueue = []; -function updateWorkQueueForAvatarsPresent() { // when nothing else to do, fill queue with individual avatar updates - workQueue = AvatarList.getAvatarIdentifiers().map(function (avatarIdentifier) { - return function () { - updateEntiesForAvatar(avatarIdentifier); - }; - }); -} -Script.setInterval(function () { - // There might be thousands of EntityData known to us, but only a few will require any work to update. - updateAllEntityData(); // i.e., this better be pretty fast. - // Each interval, we do no more than one updateEntitiesforAvatar. - if (!workQueue.length) { - workQueue = [updateWorkQueueForAvatarsPresent]; - } - workQueue.pop()(); // There's always one -}, UPDATE_TIME); diff --git a/tutorial/Changelog.md b/tutorial/Changelog.md deleted file mode 100644 index bd923b6841..0000000000 --- a/tutorial/Changelog.md +++ /dev/null @@ -1,3 +0,0 @@ - * home-tutorial-34 - * Update tutorial to only start if `HMD.active` - * Update builder's grid to use "Good - Sub-meshes" for collision shape type diff --git a/tutorial/entityData.js b/tutorial/entityData.js deleted file mode 100644 index b14185e78f..0000000000 --- a/tutorial/entityData.js +++ /dev/null @@ -1,131 +0,0 @@ -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, - y: -0.2, - z: 0 - }, - "dynamic": 1, - "gravity": { - "x": 0, - "y": -10, - "z": 0 - }, - "id": "{1c4061bc-b2e7-4435-bc47-3fcc39ae6624}", - "modelURL": "atp:/tutorial_models/bomb1.fbx", - "owningAvatarID": "{00000000-0000-0000-0000-000000000000}", - "position": { - "x": 0.11612319946289062, - "y": 0, - "z": 0.21749019622802734 - }, - "queryAACube": { - "scale": 0.24519434571266174, - "x": -0.0064739733934402466, - "y": -0.12259717285633087, - "z": 0.094893023371696472 - }, - "rotation": { - "w": -0.083054840564727783, - "x": 0.93615627288818359, - "y": 0.34154272079467773, - "z": -0.0073701143264770508 - }, - "shapeType": "simple-hull", - "type": "Model", - "userData": "{\n \"hifiHomeKey\": {\n \"reset\": true\n }\n}" -} ; - - -birdFirework2 = { - "collisionsWillMove": 1, - velocity: { - x: 0, - y: -0.01, - z: 0 - }, - "dynamic": 1, - "gravity": { - "x": 0, - "y": -10, - "z": 0 - }, - "id": "{ba067084-8d0f-4eeb-a8a1-c6814527c1bb}", - "modelURL": "atp:/tutorial_models/bomb2.fbx", - "owningAvatarID": "{00000000-0000-0000-0000-000000000000}", - "position": { - "x": 0, - "y": 0.014694660902023315, - "z": 0 - }, - "queryAACube": { - "scale": 0.24011452496051788, - "x": -0.12005726248025894, - "y": -0.10536260157823563, - "z": -0.12005726248025894 - }, - "rotation": { - "w": 0.55410087108612061, - "x": 0.36000609397888184, - "y": -0.33641564846038818, - "z": -0.67092394828796387 - }, - "shapeType": "simple-compound", - "type": "Model", - "userData": "{\n \"hifiHomeKey\": {\n \"reset\": true\n }\n}" -}; - - -Step1BlockData = { - "clientOnly": 0, - "color": { - "blue": 0, - "green": 0, - "red": 255 - }, - "created": "2016-08-22T22:54:07Z", - "dimensions": { - "x": 0.20000000298023224, - "y": 0.20000000298023224, - "z": 0.20000000298023224 - }, - name: "tutorial/block", - "collisionsWillMove": 1, - velocity: { - x: 0, - y: -0.2, - z: 0 - }, - "dynamic": 1, - "gravity": { - "x": 0, - "y": -10, - "z": 0 - }, - "id": "{5c7223f8-3bc5-4cb4-913c-0e93f5994ca2}", - "owningAvatarID": "{00000000-0000-0000-0000-000000000000}", - "queryAACube": { - "scale": 0.34641015529632568, - "x": -0.17320507764816284, - "y": -0.17320507764816284, - "z": -0.17320507764816284 - }, - "rotation": { - "w": 1, - "x": -0.0001373291015625, - "y": -7.62939453125e-05, - "z": -0.0003204345703125 - }, - "shape": "Cube", - "type": "Box", - "userData": JSON.stringify({ hifiHomeKey: { reset: true } }), -}; diff --git a/tutorial/firePit/fire.js b/tutorial/firePit/fire.js deleted file mode 100644 index 4565975351..0000000000 --- a/tutorial/firePit/fire.js +++ /dev/null @@ -1,181 +0,0 @@ -// Originally written by James Pollack, modified by Ryan Huffman for the tutorial -// -// this script turns an entity into an exploder -- anything that collides with it will be vaporized! - -(function() { - - function debug() { - var args = Array.prototype.slice.call(arguments); - args.unshift("fire.js | "); - print.apply(this, args); - } - - var _this = this; - - function Fire() { - _this = this; - } - - var RED = { - red: 255, - green: 0, - blue: 0 - }; - - var ORANGE = { - red: 255, - green: 165, - blue: 0 - }; - - var YELLOW = { - red: 255, - green: 255, - blue: 0 - }; - - var GREEN = { - red: 0, - green: 255, - blue: 0 - }; - - var BLUE = { - red: 0, - green: 0, - blue: 255 - }; - - var INDIGO = { - red: 128, - green: 0, - blue: 128 - }; - - var VIOLET = { - red: 75, - green: 0, - blue: 130 - }; - - 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(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); - } catch (err) { - 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"); - } - } - } - }, - explodeWithColor: function() { - var myProps = Entities.getEntityProperties(this.entityID); - var color = colors[Math.floor(Math.random() * colors.length)]; - var explosionParticleProperties = { - "color": color, - "isEmitting": 1, - "maxParticles": 1000, - "lifespan": 0.25, - "emitRate": 1, - "emitSpeed": 0.1, - "speedSpread": 1, - "emitOrientation": Quat.getUp(myProps.rotation), - "emitDimensions": { - "x": 0, - "y": 0, - "z": 0 - }, - "polarStart": 0, - "polarFinish": 0, - "azimuthStart": 0, - "azimuthFinish": 0, - "emitAcceleration": { - "x": 0, - "y": 0, - "z": 0 - }, - "accelerationSpread": { - "x": 0, - "y": 0, - "z": 0 - }, - "particleRadius": 0.829, - "radiusSpread": 0, - "radiusStart": 0.361, - "radiusFinish": 0.294, - "colorSpread": { - "red": 0, - "green": 0, - "blue": 0 - }, - "colorStart": { - "red": 255, - "green": 255, - "blue": 255 - }, - "colorFinish": { - "red": 255, - "green": 255, - "blue": 255 - }, - "alpha": 1, - "alphaSpread": 0, - "alphaStart": -0.2, - "alphaFinish": 0.5, - "emitterShouldTrail": 0, - "textures": explodeTextureURL, - "type": "ParticleEffect", - lifetime: 1, - position: myProps.position - }; - - var explosion = Entities.addEntity(explosionParticleProperties); - }, - playSoundAtCurrentPosition: function() { - - var audioProperties = { - volume: 0.5, - position: Entities.getEntityProperties(this.entityID).position - }; - - Audio.playSound(this.EXPLOSION_SOUND, audioProperties); - }, - } - - return new Fire(); -}); diff --git a/tutorial/firePit/flicker.js b/tutorial/firePit/flicker.js deleted file mode 100644 index f4406286c4..0000000000 --- a/tutorial/firePit/flicker.js +++ /dev/null @@ -1,52 +0,0 @@ -// Originally written for the Home content set. Pulled into the tutorial by Ryan Huffman -(function() { - - var MINIMUM_LIGHT_INTENSITY = 50.0; - var MAXIMUM_LIGHT_INTENSITY = 200.0; - var LIGHT_FALLOFF_RADIUS = 0.1; - var LIGHT_INTENSITY_RANDOMNESS = 0.1; - - function randFloat(low, high) { - return low + Math.random() * (high - low); - } - - var _this; - - function FlickeringFlame() { - _this = this; - } - - var totalTime = 0; - var spacer = 2; - FlickeringFlame.prototype = { - preload: function(entityID) { - this.entityID = entityID; - Script.update.connect(this.update); - }, - update: function(deltaTime) { - - totalTime += deltaTime; - if (totalTime > spacer) { - var howManyAvatars = AvatarList.getAvatarIdentifiers().length; - var intensity = (MINIMUM_LIGHT_INTENSITY + (MAXIMUM_LIGHT_INTENSITY + (Math.sin(totalTime) * MAXIMUM_LIGHT_INTENSITY))); - intensity += randFloat(-LIGHT_INTENSITY_RANDOMNESS, LIGHT_INTENSITY_RANDOMNESS); - - Entities.editEntity(_this.entityID, { - intensity: intensity - }); - - spacer = Math.random(0, 100) * (2 / howManyAvatars); - totalTime = 0; - } else { - //just keep counting - } - }, - unload: function() { - Script.update.disconnect(this.update) - } - } - - return new FlickeringFlame - - -}); diff --git a/tutorial/fuse.js b/tutorial/fuse.js deleted file mode 100644 index 59306f4113..0000000000 --- a/tutorial/fuse.js +++ /dev/null @@ -1,119 +0,0 @@ -// -// fuse.js -// -// Created by Ryan Huffman on 9/1/16. -// Copyright 2016 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -(function() { - Script.include('utils.js'); - - var DEBUG = true; - function debug() { - if (DEBUG) { - 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); - var results = {} - for (var i = 0; i < childEntityIDs.length; ++i) { - var childEntityID = childEntityIDs[i]; - var properties = Entities.getEntityProperties(childEntityID, propertyNames); - results[childEntityID] = properties; - } - return results; - } - var Fuse = function() { - }; - Fuse.prototype = { - light: function() { - debug("Received light()", this.entityID); - - 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, - lastFrame: 150, - running: 1, - url: "atp:/tutorial_models/fuse.fbx", - loop: 0 - }, - }); - var injector = Audio.playSound(fuseSound, { - position: Entities.getEntityProperties(this.entityID, 'position').position, - volume: 0.7, - loop: true - }); - - 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, { - emitRate: 140, - }); - } else if (props.type == "Light") { - Entities.editEntity(childEntityID, { - visible: true, - }); - } - } - - var self = this; - Script.setTimeout(function() { - debug("Setting off fireworks"); - 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, { - emitRate: 0, - }); - } else if (props.type == "Light") { - Entities.editEntity(childEntityID, { - visible: false, - }); - } - } - - }, 4900); - - Script.setTimeout(function() { - debug("Setting fuse to inactive"); - active = false; - }, 14000); - }, - preload: function(entityID) { - debug("Preload"); - this.entityID = entityID; - }, - }; - return new Fuse(); -}); diff --git a/tutorial/fuseCollider.js b/tutorial/fuseCollider.js deleted file mode 100644 index 953fcd316d..0000000000 --- a/tutorial/fuseCollider.js +++ /dev/null @@ -1,18 +0,0 @@ -(function() { - Script.include('utils.js'); - - var Fuse = function() { - }; - Fuse.prototype = { - onLit: function() { - print("fuseCollider.js | Lit", this.entityID); - var fuseID = "{c8944a13-9acb-4d77-b1ee-851845e98357}" - Entities.callEntityMethod(fuseID, "light"); - }, - preload: function(entityID) { - print("fuseCollider.js | preload"); - this.entityID = entityID; - }, - }; - return new Fuse(); -}); diff --git a/tutorial/lighter/butaneLighter.js b/tutorial/lighter/butaneLighter.js deleted file mode 100644 index f37769f867..0000000000 --- a/tutorial/lighter/butaneLighter.js +++ /dev/null @@ -1,192 +0,0 @@ -// -// Created by Thijs Wenker on September 14, 2016. -// Copyright 2016 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -(function() { - var _this; - - function getResourceURL(file) { - return 'atp:/' + file; - }; - - const LIGHTER_ON_SOUND_URL = getResourceURL('tutorial_sounds/lighter_on.wav'); - const BUTANE_SOUND_URL = getResourceURL('tutorial_sounds/butane.wav'); - - // TODO: fix this in the client, changing the sound volume while a sound is playing doesn't seem to work right now - const DYNAMIC_SOUND_VOLUME = false; - const BUTANE_MIN_SOUND_VOLUME = 0.05; - - const FLAME_LENGTH = 0.05; - - const BUTANE_SOUND_SETTINGS = { - volume: 0.4, - loop: true, - playbackGap: 1000, - playbackGapRange: 1000 - }; - - const LIGHTER_ON_SOUND_SETTINGS = { - volume: 0.5, - loop: false - }; - - function RemoteLogSender(channel, identifier) { - this.channel = channel; - this.identifier = identifier; - } - - RemoteLogSender.prototype = { - channel: null, - identifier: null, - debug: function(message) { - Messages.sendLocalMessage(this.channel, JSON.stringify({ - message: '[DEBUG ' + this.identifier + '] ' + message - })); - } - }; - - var remoteLogSender = null; - - function debugPrint(message) { - if (remoteLogSender !== null) { - remoteLogSender.debug(message); - } - } - - ButaneLighter = function() { - _this = this; - _this.triggerValue = 0.0; // be sure to set this value in the constructor, otherwise it will be a shared value - _this.isFiring = false; - } - - ButaneLighter.prototype = { - entityID: null, - lighterOnSound: null, - butaneSound: null, - lighterOnSoundInjector: null, - butaneSoundInjector: null, - butaneSoundInjectorOptions: null, - lighterParticleEntity: null, - buttonBeingPressed: null, - triggerValue: null, - isFiring: null, - getLighterParticleEntity: function() { - var result = null; - Entities.getChildrenIDs(_this.entityID).forEach(function(entity) { - var name = Entities.getEntityProperties(entity, ['name']).name; - if (name === 'lighter_particle') { - result = entity; - } - }); - return result; - }, - preload: function(entityID) { - _this.entityID = entityID; - _this.lighterOnSound = SoundCache.getSound(LIGHTER_ON_SOUND_URL); - _this.butaneSound = SoundCache.getSound(BUTANE_SOUND_URL); - var properties = Entities.getEntityProperties(_this.entityID, ['userData']); - try { - var userData = JSON.parse(properties.userData); - if (userData['debug'] !== undefined && userData['debug']['sessionUUID'] === MyAvatar.sessionUUID && - userData['debug']['channel'] !== undefined) - { - remoteLogSender = new RemoteLogSender(userData['debug']['channel'], _this.entityID); - remoteLogSender.debug('Debugger initialized'); - } - } catch (e) { - //failed to detect if we have a debugger - } - debugPrint(_this.getLighterParticleEntity()); - }, - startEquip: function(entityID, args) { - _this.lighterParticleEntity = _this.getLighterParticleEntity(); - }, - continueEquip: function(entityID, args) { - _this.triggerValue = Controller.getValue(args[0] === 'left' ? Controller.Standard.LT : Controller.Standard.RT); - if (_this.triggerValue > 0.2) { - if (!_this.isFiring) { - _this.startFiring(); - } - _this.tryToIgnite(); - _this.updateButaneSound() - return; - } - _this.stopFiring(); - }, - startFiring: function() { - if (_this.isFiring) { - return; - } - _this.isFiring = true; - if (_this.lighterOnSound.downloaded) { - // We don't want to override the default volume setting, so lets clone the default SETTINGS object - var lighterOnOptions = JSON.parse(JSON.stringify(LIGHTER_ON_SOUND_SETTINGS)); - lighterOnOptions['position'] = Entities.getEntityProperties(_this.entityID, ['position']).position; - _this.lighterOnSoundInjector = Audio.playSound(_this.lighterOnSound, lighterOnOptions); - } - if (_this.butaneSound.downloaded) { - _this.butaneSoundInjectorOptions = JSON.parse(JSON.stringify(BUTANE_SOUND_SETTINGS)); - _this.butaneSoundInjectorOptions['position'] = Entities.getEntityProperties(_this.lighterParticleEntity, ['position']).position; - if (DYNAMIC_SOUND_VOLUME) { - _this.butaneSoundInjectorOptions['volume'] = BUTANE_MIN_SOUND_VOLUME; - } - _this.butaneSoundInjector = Audio.playSound(_this.butaneSound, _this.butaneSoundInjectorOptions); - } - Entities.editEntity(_this.lighterParticleEntity, {isEmitting: _this.isFiring}); - - }, - stopFiring: function() { - if (!_this.isFiring) { - return; - } - _this.isFiring = false; - Entities.editEntity(_this.lighterParticleEntity, {isEmitting: _this.isFiring}); - _this.stopButaneSound(); - }, - tryToIgnite: function() { - var flameProperties = Entities.getEntityProperties(_this.lighterParticleEntity, ['position', 'rotation']); - var pickRay = { - origin: flameProperties.position, - direction: Quat.inverse(Quat.getFront(flameProperties.rotation)) - } - var intersection = Entities.findRayIntersection(pickRay, true, [], [_this.entityID, _this.lighterParticleEntity]); - if (intersection.intersects && intersection.distance <= FLAME_LENGTH) { - var properties = Entities.getEntityProperties(intersection.entityID, 'script'); - if (properties.script !== '') { - Entities.callEntityMethod(intersection.entityID, 'onLit', [_this.triggerValue]); - debugPrint('Light it up! found: ' + intersection.entityID); - } - } - }, - releaseEquip: function(entityID, args) { - _this.stopFiring(); - // reset trigger value; - _this.triggerValue = 0.0; - }, - updateButaneSound: function() { - if (_this.butaneSoundInjector !== null && _this.butaneSoundInjector.isPlaying()) { - _this.butaneSoundInjectorOptions = _this.butaneSoundInjector.options; - _this.butaneSoundInjectorOptions['position'] = Entities.getEntityProperties(_this.entityID, ['position']).position; - if (DYNAMIC_SOUND_VOLUME) { - _this.butaneSoundInjectorOptions['volume'] = ((BUTANE_SOUND_SETTINGS.volume - BUTANE_MIN_SOUND_VOLUME) * - _this.triggerValue) + BUTANE_MIN_SOUND_VOLUME; - } - _this.butaneSoundInjector.options = _this.butaneSoundInjectorOptions; - } - }, - stopButaneSound: function() { - if (_this.butaneSoundInjector !== null && _this.butaneSoundInjector.isPlaying()) { - _this.butaneSoundInjector.stop(); - } - _this.butaneSoundInjector = null; - }, - unload: function() { - _this.stopButaneSound(); - }, - }; - return new ButaneLighter(); -}) diff --git a/tutorial/lighter/createButaneLighter.js b/tutorial/lighter/createButaneLighter.js deleted file mode 100644 index 1a6b94d0f6..0000000000 --- a/tutorial/lighter/createButaneLighter.js +++ /dev/null @@ -1,220 +0,0 @@ -// -// Created by Thijs Wenker on September 14, 2016. -// Copyright 2016 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -const TEST_MODE = false; -const SCRIPT_URL = 'atp:/tutorial/lighter/butaneLighter.js'; - -//Creates an entity and returns a mixed object of the creation properties and the assigned entityID -var createEntity = function(entityProperties, parent) { - if (parent.rotation !== undefined) { - if (entityProperties.rotation !== undefined) { - entityProperties.rotation = Quat.multiply(parent.rotation, entityProperties.rotation); - } else { - entityProperties.rotation = parent.rotation; - } - } - if (parent.position !== undefined) { - var localPosition = (parent.rotation !== undefined) ? Vec3.multiplyQbyV(parent.rotation, entityProperties.position) : entityProperties.position; - entityProperties.position = Vec3.sum(localPosition, parent.position) - } - if (parent.id !== undefined) { - entityProperties.parentID = parent.id; - } - entityProperties.id = Entities.addEntity(entityProperties); - return entityProperties; -}; - -createButaneLighter = function(transform) { - var entityProperties = { - collisionsWillMove: true, - dimensions: { - x: 0.025599999353289604, - y: 0.057399999350309372, - z: 0.37419998645782471 - }, - dynamic: true, - gravity: { - x: 0, - y: -9.8, - z: 0 - }, - velocity: { - x: 0, - y: -0.01, - z: 0 - }, - modelURL: 'atp:/tutorial_models/lighterIceCreamSandwich.fbx', - name: 'BrutaneLighter', - shapeType: 'simple-compound', - type: 'Model', - userData: JSON.stringify({ - "tag": "equip-temporary", - "grabbableKey": { - "invertSolidWhileHeld": true - }, - "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 - }; - return createEntity(entityProperties, transform); -} - -function createFireParticle(butaneLighter) { - var entityProperties = { - userData: JSON.stringify({ tag: "equip-temporary" }), - accelerationSpread: { - x: 0.1, - y: 0, - z: 0.1 - }, - alpha: 0.039999999105930328, - alphaFinish: 0.039999999105930328, - alphaStart: 0.039999999105930328, - azimuthFinish: 0.039999999105930328, - azimuthStart: 0, - dimensions: { - x: 0.49194091558456421, - y: 0.49194091558456421, - z: 0.49194091558456421 - }, - emitAcceleration: { - x: 0, - y: 0, - z: 0 - }, - emitOrientation: { - w: 1, - x: -1.52587890625e-05, - y: -1.52587890625e-05, - z: -1.52587890625e-05 - }, - emitRate: 770, - emitSpeed: 0.014000000432133675, - isEmitting: false, - lifespan: 0.37000000476837158, - maxParticles: 820, - name: 'lighter_particle', - particleRadius: 0.0027000000700354576, - position: { - x: -0.00044769048690795898, - y: 0.016354814171791077, - z: 0.19217036664485931 - }, - radiusFinish: 0.0027000000700354576, - radiusSpread: 3, - radiusStart: 0.0027000000700354576, - rotation: { - w: 1, - x: -0.0001678466796875, - y: -1.52587890625e-05, - z: -1.52587890625e-05 - }, - speedSpread: 0.56999999284744263, - textures: 'atp:/textures/fire3.png', - type: 'ParticleEffect', - - - "color": { - "red": 255, - "green": 255, - "blue": 255 - }, - "isEmitting": 0, - "maxParticles": 820, - "lifespan": 0.28, - "emitRate": 1100, - "emitSpeed": 0.007, - "speedSpread": 0.5699999928474426, - "emitOrientation": { - "x": -0.0000152587890625, - "y": -0.0000152587890625, - "z": -0.0000152587890625, - "w": 1 - }, - "emitDimensions": { - "x": 0, - "y": 0, - "z": 0 - }, - "polarStart": 0, - "polarFinish": 0, - "azimuthStart": 0, - "azimuthFinish": 0.03999999910593033, - "emitAcceleration": { - "x": 0, - "y": 0, - "z": 0 - }, - "accelerationSpread": { - "x": 0, - "y": 0, - "z": 0 - }, - "particleRadius": 0.0037, - "radiusSpread": 3, - "radiusStart": 0.008, - "radiusFinish": 0.0004, - "colorSpread": { - "red": 0, - "green": 0, - "blue": 0 - }, - "colorStart": { - "red": 255, - "green": 255, - "blue": 255 - }, - "colorFinish": { - "red": 255, - "green": 255, - "blue": 255 - }, - "alpha": 0.03999999910593033, - "alphaSpread": 0, - "alphaStart": 0.141, - "alphaFinish": 0.02, - "emitterShouldTrail": 0, - "textures": "atp:/textures/fire3.png" - }; - return createEntity(entityProperties, butaneLighter); -} - -doCreateButaneLighter = function(transform) { - var butaneLighter = createButaneLighter(transform); - createFireParticle(butaneLighter); - return butaneLighter; -} diff --git a/tutorial/ownershipToken.js b/tutorial/ownershipToken.js deleted file mode 100644 index 4a970af66d..0000000000 --- a/tutorial/ownershipToken.js +++ /dev/null @@ -1,188 +0,0 @@ -if (!Function.prototype.bind) { - Function.prototype.bind = function(oThis) { - if (typeof this !== 'function') { - // closest thing possible to the ECMAScript 5 - // internal IsCallable function - throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable'); - } - - var aArgs = Array.prototype.slice.call(arguments, 1), - fToBind = this, - fNOP = function() {}, - fBound = function() { - return fToBind.apply(this instanceof fNOP - ? this - : oThis, - aArgs.concat(Array.prototype.slice.call(arguments))); - }; - - if (this.prototype) { - // Function.prototype doesn't have a prototype property - fNOP.prototype = this.prototype; - } - fBound.prototype = new fNOP(); - - return fBound; - }; -} - -function getOption(options, key, defaultValue) { - if (options.hasOwnProperty(key)) { - return options[key]; - } - return defaultValue; -} - -var TOKEN_NAME_PREFIX = "ownership_token-"; - -function getOwnershipTokenID(parentEntityID) { - var childEntityIDs = Entities.getChildrenIDs(parentEntityID); - var ownerID = null; - var ownerName = ''; - for (var i = 0; i < childEntityIDs.length; ++i) { - var childID = childEntityIDs[i]; - var properties = Entities.getEntityProperties(childID, ['name', 'userData', 'lifetime', 'age']); - var childName = properties.name; - if (childName.indexOf(TOKEN_NAME_PREFIX) == 0) { - if (ownerID === null || childName < ownerName) { - ownerID = childID; - ownerName = childName; - } - } - } - return ownerID; -} - -function createOwnershipToken(name, parentEntityID) { - return Entities.addEntity({ - type: "Box", - name: TOKEN_NAME_PREFIX + name, - visible: false, - parentID: parentEntityID, - locationPosition: { x: 0, y: 0, z: 0 }, - dimensions: { x: 100, y: 100, z: 100 }, - collisionless: true, - lifetime: 5 - }); -} - -var DEBUG = true; -function debug() { - if (DEBUG) { - var args = Array.prototype.slice.call(arguments); - print.apply(this, args); - } -} - -var TOKEN_STATE_DESTROYED = -1; -var TOKEN_STATE_UNOWNED = 0; -var TOKEN_STATE_REQUESTING_OWNERSHIP = 1; -var TOKEN_STATE_OWNED = 2; - -OwnershipToken = function(name, parentEntityID, options) { - this.name = MyAvatar.sessionUUID + "-" + Math.floor(Math.random() * 10000000); - this.parentEntityID = parentEntityID; - - // How often to check whether the token is available if we don't currently own it - this.checkEverySeconds = getOption(options, 'checkEverySeconds', 1000); - this.updateTokenLifetimeEvery = getOption(options, 'updateTokenLifetimeEvery', 2000); - - this.onGainedOwnership = getOption(options, 'onGainedOwnership', function() { }); - this.onLostOwnership = getOption(options, 'onLostOwnership', function() { }); - - this.ownershipTokenID = null; - this.setState(TOKEN_STATE_UNOWNED); -}; - -OwnershipToken.prototype = { - destroy: function() { - debug(this.name, "Destroying token"); - this.setState(TOKEN_STATE_DESTROYED); - }, - - setState: function(newState) { - if (this.state == newState) { - debug(this.name, "Warning: Trying to set state to the current state"); - return; - } - - if (this.updateLifetimeID) { - debug(this.name, "Clearing update lifetime interval"); - Script.clearInterval(this.updateLifetimeID); - this.updateLifetimeID = null; - } - - if (this.checkOwnershipAvailableID) { - Script.clearInterval(this.checkOwnershipAvailableID); - this.checkOwnershipAvailableID = null; - } - - if (this.state == TOKEN_STATE_OWNED) { - this.onLostOwnership(this); - } - - if (newState == TOKEN_STATE_UNOWNED) { - this.checkOwnershipAvailableID = Script.setInterval( - this.tryRequestingOwnership.bind(this), this.checkEverySeconds); - - } else if (newState == TOKEN_STATE_REQUESTING_OWNERSHIP) { - - } else if (newState == TOKEN_STATE_OWNED) { - this.onGainedOwnership(this); - this.updateLifetimeID = Script.setInterval( - this.updateTokenLifetime.bind(this), this.updateTokenLifetimeEvery); - } else if (newState == TOKEN_STATE_DESTROYED) { - Entities.deleteEntity(this.ownershipTokenID); - } - - debug(this.name, "Info: Switching to state:", newState); - this.state = newState; - }, - updateTokenLifetime: function() { - if (this.state != TOKEN_STATE_OWNED) { - debug(this.name, "Error: Trying to update token while it is unowned"); - return; - } - - debug(this.name, "Updating entity lifetime"); - var age = Entities.getEntityProperties(this.ownershipTokenID, 'age').age; - Entities.editEntity(this.ownershipTokenID, { - lifetime: age + 5 - }); - }, - tryRequestingOwnership: function() { - if (this.state == TOKEN_STATE_REQUESTING_OWNERSHIP || this.state == TOKEN_STATE_OWNED) { - debug(this.name, "We already have or are requesting ownership"); - return; - } - - var ownerID = getOwnershipTokenID(this.parentEntityID); - if (ownerID !== null) { - // Already owned, return - debug(this.name, "Token already owned by another client, returning. Owner: " + owenerID + ", Us: " + this.name); - return; - } - - this.ownershipTokenID = createOwnershipToken(this.name, this.parentEntityID); - this.setState(TOKEN_STATE_REQUESTING_OWNERSHIP); - - function checkOwnershipRequest() { - var ownerID = getOwnershipTokenID(this.parentEntityID); - if (ownerID == this.ownershipTokenID) { - debug(this.name, "Info: Obtained ownership"); - this.setState(TOKEN_STATE_OWNED); - } else { - if (ownerID === null) { - debug(this.name, "Warning: Checked ownership request and no tokens existed"); - } - debug(this.name, "Info: Lost ownership request") - this.ownershipTokenID = null; - this.setState(TOKEN_STATE_UNOWNED); - } - } - - Script.setTimeout(checkOwnershipRequest.bind(this), 2000); - }, -}; - -debug("Returning from ownershipToken"); diff --git a/tutorial/spinner.js b/tutorial/spinner.js deleted file mode 100644 index 2edbb43700..0000000000 --- a/tutorial/spinner.js +++ /dev/null @@ -1,84 +0,0 @@ -// -// spinner.js -// -// Created by Ryan Huffman on 9/1/16. -// Copyright 2016 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -(function() { - var DEBUG = true; - function debug() { - if (DEBUG) { - print.apply(this, arguments); - } - } - - var spinnerSound = SoundCache.getSound("atp:/tutorial_sounds/Pinwheel.L.wav"); - var Spinner = function() { - }; - function getChildProperties(entityID, propertyNames) { - var childEntityIDs = Entities.getChildrenIDs(entityID); - var results = {} - for (var i = 0; i < childEntityIDs.length; ++i) { - var childEntityID = childEntityIDs[i]; - var properties = Entities.getEntityProperties(childEntityID, propertyNames); - results[childEntityID] = properties; - } - return results; - } - Spinner.prototype = { - onLit: function() { - debug("spinner.js | Spinner lit"); - Entities.editEntity(this.entityID, { - "angularDamping": 0.1, - "angularVelocity": { - "x": 20.471975326538086, - "y": 0, - "z": 0 - }, - }); - var injector = Audio.playSound(spinnerSound, { - position: Entities.getEntityProperties(this.entityID, 'position').position, - volume: 1.0, - loop: false - }); - - var childrenProps = getChildProperties(this.entityID, ['type']); - for (var childEntityID in childrenProps) { - var props = childrenProps[childEntityID]; - if (props.type == "ParticleEffect") { - debug("spinner.js | Modifying: ", childEntityID); - Entities.editEntity(childEntityID, { - emitRate: 35, - }); - } - } - Messages.sendLocalMessage("Tutorial-Spinner", "wasLit"); - - var self = this; - Script.setTimeout(function() { - 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, - }); - } - } - }, 4900); - }, - preload: function(entityID) { - debug("spinner.js | Preload"); - this.entityID = entityID; - }, - }; - return new Spinner(); -}); diff --git a/tutorial/success.wav b/tutorial/success.wav deleted file mode 100644 index 597e2b91b6..0000000000 Binary files a/tutorial/success.wav and /dev/null differ diff --git a/tutorial/success48.wav b/tutorial/success48.wav deleted file mode 100644 index 802d0a777f..0000000000 Binary files a/tutorial/success48.wav and /dev/null differ diff --git a/tutorial/tutorial.js b/tutorial/tutorial.js deleted file mode 100644 index 97528b9b3b..0000000000 --- a/tutorial/tutorial.js +++ /dev/null @@ -1,1202 +0,0 @@ -// -// tutorial.js -// -// Created by Ryan Huffman on 9/1/16. -// Copyright 2016 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -Script.include("entityData.js"); -Script.include("lighter/createButaneLighter.js"); -Script.include("tutorialEntityIDs.js"); - -if (!Function.prototype.bind) { - Function.prototype.bind = function(oThis) { - if (typeof this !== 'function') { - // closest thing possible to the ECMAScript 5 - // internal IsCallable function - throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable'); - } - - var aArgs = Array.prototype.slice.call(arguments, 1), - fToBind = this, - fNOP = function() {}, - fBound = function() { - return fToBind.apply(this instanceof fNOP - ? this - : oThis, - aArgs.concat(Array.prototype.slice.call(arguments))); - }; - - if (this.prototype) { - // Function.prototype doesn't have a prototype property - fNOP.prototype = this.prototype; - } - fBound.prototype = new fNOP(); - - return fBound; - }; -} - -var DEBUG = true; -function debug() { - if (DEBUG) { - var args = Array.prototype.slice.call(arguments); - args.unshift("tutorial.js | "); - print.apply(this, args); - } -} - -var INFO = true; -function info() { - if (INFO) { - var args = Array.prototype.slice.call(arguments); - args.unshift("tutorial.js | "); - print.apply(this, args); - } -} - -const CONTROLLER_TOUCH = 'touch'; -const CONTROLLER_VIVE = 'vive'; - -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"; -function setAwayEnabled(value) { - var message = value ? 'enable' : 'disable'; - Messages.sendLocalMessage(CHANNEL_AWAY_ENABLE, message); -} - -findEntity = function(properties, searchRadius, filterFn) { - var entities = findEntities(properties, searchRadius, filterFn); - return entities.length > 0 ? entities[0] : null; -} - -// Return all entities with properties `properties` within radius `searchRadius` -findEntities = function(properties, searchRadius, filterFn) { - if (!filterFn) { - filterFn = function(properties, key, value) { - return value == properties[key]; - } - } - searchRadius = searchRadius ? searchRadius : 100000; - var entities = Entities.findEntities({ x: 0, y: 0, z: 0 }, searchRadius); - var matchedEntities = []; - var keys = Object.keys(properties); - for (var i = 0; i < entities.length; ++i) { - var match = true; - var candidateProperties = Entities.getEntityProperties(entities[i], keys); - for (var key in properties) { - if (!filterFn(properties, key, candidateProperties[key])) { - // This isn't a match, move to next entity - match = false; - break; - } - } - if (match) { - matchedEntities.push(entities[i]); - } - } - - return matchedEntities; -} - -function findEntitiesWithTag(tag) { - return findEntities({ userData: "" }, 10000, function(properties, key, value) { - data = parseJSON(value); - return data.tag === tag; - }); -} - -/** - * A controller is made up of parts, and each part can have multiple "layers," - * which are really just different texures. For example, the "trigger" part - * has "normal" and "highlight" layers. - */ -function setControllerPartLayer(part, layer) { - data = {}; - data[part] = layer; - Messages.sendLocalMessage('Controller-Set-Part-Layer', JSON.stringify(data)); -} - -/** - * Spawn entities and return the newly created entity's ids. - * @param {object[]} entityPropertiesList A list of properties of the entities - * to spawn. - */ -function spawn(entityPropertiesList, transform, modifyFn) { - if (!transform) { - transform = { - position: { x: 0, y: 0, z: 0 }, - rotation: { x: 0, y: 0, z: 0, w: 1 } - } - } - var ids = []; - for (var i = 0; i < entityPropertiesList.length; ++i) { - var data = entityPropertiesList[i]; - data.position = Vec3.sum(transform.position, data.position); - data.rotation = Quat.multiply(data.rotation, transform.rotation); - if (modifyFn) { - data = modifyFn(data); - } - var id = Entities.addEntity(data); - ids.push(id); - } - return ids; -} - -/** - * @function parseJSON - * @param {string} jsonString The string to parse. - * @return {object} Return an empty if the string was not valid JSON, otherwise - * the parsed object is returned. - */ -function parseJSON(jsonString) { - var data; - try { - data = JSON.parse(jsonString); - } catch(e) { - data = {}; - } - return data; -} - -/** - * Spawn entities with `tag` in the userData. - * @function spawnWithTag - */ -function spawnWithTag(entityData, transform, tag) { - function modifyFn(data) { - var userData = parseJSON(data.userData); - userData.tag = tag; - data.userData = JSON.stringify(userData); - debug("In modify", tag, userData, data.userData); - return data; - } - return spawn(entityData, transform, modifyFn); -} - -/** - * Delete all entities with the tag `tag` in their userData. - * @function deleteEntitiesWithTag - */ -function deleteEntitiesWithTag(tag) { - debug("searching for...:", tag); - var entityIDs = findEntitiesWithTag(tag); - for (var i = 0; i < entityIDs.length; ++i) { - Entities.deleteEntity(entityIDs[i]); - } -} - -function editEntitiesWithTag(tag, propertiesOrFn) { - 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 http://stackoverflow.com/questions/5999998/how-can-i-check-if-a-javascript-variable-is-function-type -function isFunction(functionToCheck) { - var getType = {}; - return functionToCheck && getType.toString.call(functionToCheck) === '[object Function]'; -} - -/** - * Return `true` if `entityID` can be found in the local entity tree, otherwise `false`. - */ -function isEntityInLocalTree(entityID) { - return Entities.getEntityProperties(entityID, 'visible').visible !== undefined; -} - -/** - * - */ -function showEntitiesWithTags(tags) { - for (var i = 0; i < tags.length; ++i) { - showEntitiesWithTag(tags[i]); - } -} - -function showEntitiesWithTag(tag) { - var entities = TUTORIAL_TAG_TO_ENTITY_IDS_MAP[tag]; - if (entities) { - for (entityID in entities) { - var data = entities[entityID]; - - var collisionless = data.visible === false ? true : false; - if (data.collidable !== undefined) { - collisionless = data.collidable === true ? false : true; - } - if (data.soundKey) { - data.soundKey.playing = true; - } - var newProperties = { - visible: data.visible == false ? false : true, - collisionless: collisionless, - userData: JSON.stringify(data), - }; - debug("Showing: ", entityID, ", Is in local tree: ", isEntityInLocalTree(entityID)); - Entities.editEntity(entityID, newProperties); - } - } else { - debug("ERROR | No entities for tag: ", tag); - } - - return; - // Dynamic method, suppressed for now - //editEntitiesWithTag(tag, function(entityID) { - // var userData = Entities.getEntityProperties(entityID, "userData").userData; - // var data = parseJSON(userData); - // var collisionless = data.visible === false ? true : false; - // if (data.collidable !== undefined) { - // collisionless = data.collidable === true ? false : true; - // } - // if (data.soundKey) { - // data.soundKey.playing = true; - // } - // var newProperties = { - // visible: data.visible == false ? false : true, - // collisionless: collisionless, - // userData: JSON.stringify(data), - // }; - // Entities.editEntity(entityID, newProperties); - //}); -} - -function hideEntitiesWithTags(tags) { - for (var i = 0; i < tags.length; ++i) { - hideEntitiesWithTag(tags[i]); - } -} - -function hideEntitiesWithTag(tag) { - var entities = TUTORIAL_TAG_TO_ENTITY_IDS_MAP[tag]; - if (entities) { - for (entityID in entities) { - var data = entities[entityID]; - - if (data.soundKey) { - data.soundKey.playing = false; - } - var newProperties = { - visible: false, - collisionless: 1, - ignoreForCollisions: 1, - userData: JSON.stringify(data), - }; - - debug("Hiding: ", entityID, ", Is in local tree: ", isEntityInLocalTree(entityID)); - Entities.editEntity(entityID, newProperties); - } - } - - return; - // Dynamic method, suppressed for now - //editEntitiesWithTag(tag, function(entityID) { - // var userData = Entities.getEntityProperties(entityID, "userData").userData; - // var data = parseJSON(userData); - // if (data.soundKey) { - // data.soundKey.playing = false; - // } - // var newProperties = { - // visible: false, - // collisionless: 1, - // ignoreForCollisions: 1, - // userData: JSON.stringify(data), - // }; - // Entities.editEntity(entityID, newProperties); - //}); -} - -/** - * 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); - var entityID = TUTORIAL_NAME_TO_ENTITY_PROPERTIES_MAP[name]; - debug("Entity id: ", entityID, ", Is in local tree: ", isEntityInLocalTree(entityID)); - return entityID; -} - -function playSuccessSound() { - Audio.playSound(successSound, { - position: MyAvatar.position, - volume: 0.7, - loop: false - }); -} - -function playFirecrackerSound(position) { - Audio.playSound(firecrackerSound, { - position: position, - volume: 0.5, - loop: false - }); -} - -/** - * This disables everything, including: - * - * - The door to leave the tutorial - * - Overlays - * - Hand controlelrs - * - Teleportation - * - Advanced movement - * - Equip and far grab - * - Away mode - */ -function disableEverything() { - editEntitiesWithTag('door', { visible: true, collisionless: false }); - Menu.setIsOptionChecked("Overlays", false); - Controller.disableMapping('handControllerPointer-click'); - Messages.sendLocalMessage('Hifi-Advanced-Movement-Disabler', 'disable'); - Messages.sendLocalMessage('Hifi-Teleport-Disabler', 'both'); - Messages.sendLocalMessage('Hifi-Grab-Disable', JSON.stringify({ - nearGrabEnabled: true, - holdEnabled: false, - farGrabEnabled: false, - myAvatarScalingEnabled: false, - objectScalingEnabled: false, - })); - setControllerPartLayer('touchpad', 'blank'); - setControllerPartLayer('trigger', 'blank'); - setControllerPartLayer('joystick', 'blank'); - setControllerPartLayer('grip', 'blank'); - setControllerPartLayer('button_a', 'blank'); - setControllerPartLayer('button_b', 'blank'); - setControllerPartLayer('tips', 'blank'); - - hideEntitiesWithTag('finish'); - - setAwayEnabled(false); -} - -/** - * This reenables everything that disableEverything() disables. This can be - * used when leaving the tutorial to ensure that nothing is left disabled. - */ -function reenableEverything() { - editEntitiesWithTag('door', { visible: false, collisionless: true }); - Menu.setIsOptionChecked("Overlays", true); - Controller.enableMapping('handControllerPointer-click'); - Messages.sendLocalMessage('Hifi-Advanced-Movement-Disabler', 'enable'); - Messages.sendLocalMessage('Hifi-Teleport-Disabler', 'none'); - Messages.sendLocalMessage('Hifi-Grab-Disable', JSON.stringify({ - nearGrabEnabled: true, - holdEnabled: true, - farGrabEnabled: true, - myAvatarScalingEnabled: true, - objectScalingEnabled: true, - })); - setControllerPartLayer('touchpad', 'blank'); - setControllerPartLayer('trigger', 'blank'); - setControllerPartLayer('joystick', 'blank'); - setControllerPartLayer('grip', 'blank'); - setControllerPartLayer('button_a', 'blank'); - setControllerPartLayer('button_b', 'blank'); - setControllerPartLayer('tips', 'blank'); - MyAvatar.shouldRenderLocally = true; - setAwayEnabled(true); -} - -/////////////////////////////////////////////////////////////////////////////// -/////////////////////////////////////////////////////////////////////////////// -// // -// STEP: DISABLE CONTROLLERS // -// // -/////////////////////////////////////////////////////////////////////////////// -var stepStart = function() { - this.name = 'start'; -}; -stepStart.prototype = { - start: function(onFinish) { - disableEverything(); - - HMD.requestShowHandControllers(); - - onFinish(); - }, - cleanup: function() { - } -}; - - - -/////////////////////////////////////////////////////////////////////////////// -/////////////////////////////////////////////////////////////////////////////// -// // -// STEP: ENABLE CONTROLLERS // -// // -/////////////////////////////////////////////////////////////////////////////// - -var stepEnableControllers = function() { - this.shouldLog = false; -}; -stepEnableControllers.prototype = { - start: function(onFinish) { - reenableEverything(); - HMD.requestHideHandControllers(); - onFinish(); - }, - cleanup: function() { - } -}; - - - -/////////////////////////////////////////////////////////////////////////////// -/////////////////////////////////////////////////////////////////////////////// -// // -// STEP: Orient and raise hands above head // -// // -/////////////////////////////////////////////////////////////////////////////// -var stepOrient = function(tutorialManager) { - this.name = 'orient'; - this.tags = ["orient", "orient-" + tutorialManager.controllerName]; -} -stepOrient.prototype = { - start: function(onFinish) { - this.active = true; - - var tag = this.tag; - - // Spawn content set - //editEntitiesWithTag(this.tag, { visible: true }); - showEntitiesWithTags(this.tags); - - this.checkIntervalID = null; - function checkForHandsAboveHead() { - debug("Orient | Checking for hands above head"); - if (MyAvatar.getLeftPalmPosition().y > (MyAvatar.getHeadPosition().y + 0.1)) { - Script.clearInterval(this.checkIntervalID); - this.checkIntervalID = null; - location = "/tutorial"; - Script.setTimeout(playSuccessSound, 150); - this.active = false; - onFinish(); - } - } - this.checkIntervalID = Script.setInterval(checkForHandsAboveHead.bind(this), 500); - }, - cleanup: function() { - debug("Orient | Cleanup"); - if (this.active) { - this.active = false; - } - if (this.overlay) { - this.overlay.destroy(); - this.overlay = null; - } - if (this.checkIntervalID) { - Script.clearInterval(this.checkIntervalID); - this.checkIntervalID = null; - } - //editEntitiesWithTag(this.tag, { visible: false, collisionless: 1 }); - hideEntitiesWithTags(this.tags); - } -}; - - - -/////////////////////////////////////////////////////////////////////////////// -/////////////////////////////////////////////////////////////////////////////// -// // -// STEP: Near Grab // -// // -/////////////////////////////////////////////////////////////////////////////// -var stepNearGrab = function(tutorialManager) { - this.name = 'nearGrab'; - this.tags = ["bothGrab", "nearGrab", "nearGrab-" + tutorialManager.controllerName]; - this.tempTag = "nearGrab-temporary"; - this.birdIDs = []; - - this.controllerName = tutorialManager.controllerName; - - Messages.subscribe("Entity-Exploded"); - Messages.messageReceived.connect(this.onMessage.bind(this)); -} -stepNearGrab.prototype = { - start: function(onFinish) { - this.finished = false; - this.onFinish = onFinish; - - if (this.controllerName === CONTROLLER_TOUCH) { - setControllerPartLayer('tips', 'both_triggers'); - setControllerPartLayer('trigger', 'highlight'); - setControllerPartLayer('grip', 'highlight'); - } else { - setControllerPartLayer('tips', 'trigger'); - setControllerPartLayer('trigger', 'highlight'); - } - - // Show content set - showEntitiesWithTags(this.tags); - - 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)(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) { - if (this.finished) { - return; - } - if (channel == "Entity-Exploded") { - 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(); - } - } - }, - cleanup: function() { - debug("NearGrab | Cleanup"); - this.finished = true; - setControllerPartLayer('tips', 'blank'); - setControllerPartLayer('trigger', 'normal'); - setControllerPartLayer('grip', 'normal'); - hideEntitiesWithTags(this.tags); - deleteEntitiesWithTag(this.tempTag); - if (this.positionWatcher) { - this.positionWatcher.destroy(); - this.positionWatcher = null; - } - } -}; - - - -/////////////////////////////////////////////////////////////////////////////// -/////////////////////////////////////////////////////////////////////////////// -// // -// STEP: Far Grab // -// // -/////////////////////////////////////////////////////////////////////////////// -var stepFarGrab = function() { - this.name = 'farGrab'; - this.tag = "farGrab"; - this.tempTag = "farGrab-temporary"; - this.finished = true; - this.birdIDs = []; - - Messages.subscribe("Entity-Exploded"); - Messages.messageReceived.connect(this.onMessage.bind(this)); -} -stepFarGrab.prototype = { - start: function(onFinish) { - this.finished = false; - this.onFinish = onFinish; - - showEntitiesWithTag('bothGrab', { visible: true }); - - setControllerPartLayer('tips', 'trigger'); - setControllerPartLayer('trigger', 'highlight'); - Messages.sendLocalMessage('Hifi-Grab-Disable', JSON.stringify({ - farGrabEnabled: true, - })); - var tag = this.tag; - - // Spawn content set - showEntitiesWithTag(this.tag); - - 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)(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) { - if (this.finished) { - return; - } - if (channel == "Entity-Exploded") { - 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(); - } - } - }, - cleanup: function() { - debug("FarGrab | Cleanup"); - this.finished = true; - setControllerPartLayer('tips', 'blank'); - setControllerPartLayer('trigger', 'normal'); - hideEntitiesWithTag(this.tag, { visible: false}); - deleteEntitiesWithTag(this.tempTag); - if (this.positionWatcher) { - this.positionWatcher.destroy(); - this.positionWatcher = null; - } - } -}; - -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, { - position: originalPosition, - velocity: { x: 0, y: -0.01, z: 0 }, - angularVelocity: { x: 0, y: 0, z: 0 } - }); - } - } - }, 1000); -} - -PositionWatcher.prototype = { - destroy: function() { - debug("Destroying position watcher"); - Script.clearInterval(this.watcherIntervalID); - } -}; - - - -/////////////////////////////////////////////////////////////////////////////// -/////////////////////////////////////////////////////////////////////////////// -// // -// STEP: Equip // -// // -/////////////////////////////////////////////////////////////////////////////// -var stepEquip = function(tutorialManager) { - const controllerName = tutorialManager.controllerName; - this.controllerName = controllerName; - - this.name = 'equip'; - - this.tags = ["equip", "equip-" + controllerName]; - this.tagsPart1 = ["equip-part1", "equip-part1-" + controllerName]; - this.tagsPart2 = ["equip-part2", "equip-part2-" + controllerName]; - this.tempTag = "equip-temporary"; - - this.PART1 = 0; - this.PART2 = 1; - this.PART3 = 2; - this.COMPLETE = 3; - - Messages.subscribe('Tutorial-Spinner'); - Messages.messageReceived.connect(this.onMessage.bind(this)); -} -stepEquip.prototype = { - start: function(onFinish) { - if (this.controllerName === CONTROLLER_TOUCH) { - setControllerPartLayer('tips', 'grip'); - setControllerPartLayer('grip', 'highlight'); - } else { - setControllerPartLayer('tips', 'trigger'); - setControllerPartLayer('trigger', 'highlight'); - } - - Messages.sendLocalMessage('Hifi-Grab-Disable', JSON.stringify({ - holdEnabled: true, - })); - - var tag = this.tag; - - // Spawn content set - showEntitiesWithTags(this.tags); - showEntitiesWithTags(this.tagsPart1); - - this.currentPart = this.PART1; - - function createLighter() { - var transform = {}; - - 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.lighterID = createLighter.bind(this)(); - this.startWatchingLighter(); - debug("Created lighter", this.lighterID); - this.onFinish = onFinish; - }, - startWatchingLighter: function() { - if (!this.watcherIntervalID) { - debug("Starting to watch lighter position"); - this.watcherIntervalID = Script.setInterval(function() { - 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) { - 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; - } - }, - onMessage: function(channel, message, sender) { - if (this.currentPart == this.COMPLETE) { - return; - } - - 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; - hideEntitiesWithTags(this.tagsPart1); - showEntitiesWithTags(this.tagsPart2); - setControllerPartLayer('trigger', 'normal'); - setControllerPartLayer('grip', 'highlight'); - 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.lighterID) { - debug("Equip | Got release, finishing step"); - this.stopWatchingGun(); - this.currentPart = this.COMPLETE; - playSuccessSound(); - Script.setTimeout(this.onFinish.bind(this), 1500); - } - } - } - }, - cleanup: function() { - debug("Equip | Got yaw action"); - if (this.watcherIntervalID) { - Script.clearInterval(this.watcherIntervalID); - this.watcherIntervalID = null; - } - - setControllerPartLayer('tips', 'blank'); - setControllerPartLayer('grip', 'normal'); - setControllerPartLayer('trigger', 'normal'); - this.stopWatchingGun(); - this.currentPart = this.COMPLETE; - - if (this.checkCollidesTimer) { - Script.clearInterval(this.checkCollidesTimer); - this.checkColllidesTimer = null; - } - - hideEntitiesWithTags(this.tagsPart1); - hideEntitiesWithTags(this.tagsPart2); - hideEntitiesWithTags(this.tags); - deleteEntitiesWithTag(this.tempTag); - } -}; - - - - -/////////////////////////////////////////////////////////////////////////////// -/////////////////////////////////////////////////////////////////////////////// -// // -// STEP: Turn Around // -// // -/////////////////////////////////////////////////////////////////////////////// -var stepTurnAround = function(tutorialManager) { - this.name = 'turnAround'; - - this.tags = ["turnAround", "turnAround-" + tutorialManager.controllerName]; - this.tempTag = "turnAround-temporary"; - - this.onActionBound = this.onAction.bind(this); - this.numTimesSnapTurnPressed = 0; - this.numTimesSmoothTurnPressed = 0; -} -stepTurnAround.prototype = { - start: function(onFinish) { - setControllerPartLayer('joystick', 'highlight'); - setControllerPartLayer('touchpad', 'arrows'); - setControllerPartLayer('tips', 'arrows'); - - showEntitiesWithTags(this.tags); - - this.numTimesSnapTurnPressed = 0; - this.numTimesSmoothTurnPressed = 0; - this.smoothTurnDown = false; - Controller.actionEvent.connect(this.onActionBound); - - this.interval = Script.setInterval(function() { - debug("TurnAround | Checking if finished", - this.numTimesSnapTurnPressed, this.numTimesSmoothTurnPressed); - var FORWARD_THRESHOLD = 90; - var REQ_NUM_TIMES_SNAP_TURN_PRESSED = 3; - var REQ_NUM_TIMES_SMOOTH_TURN_PRESSED = 2; - - var dir = Quat.getFront(MyAvatar.orientation); - var angle = Math.atan2(dir.z, dir.x); - var angleDegrees = ((angle / Math.PI) * 180); - - var hasTurnedEnough = this.numTimesSnapTurnPressed >= REQ_NUM_TIMES_SNAP_TURN_PRESSED - || this.numTimesSmoothTurnPressed >= REQ_NUM_TIMES_SMOOTH_TURN_PRESSED; - var facingForward = Math.abs(angleDegrees) < FORWARD_THRESHOLD - if (hasTurnedEnough && facingForward) { - Script.clearInterval(this.interval); - this.interval = null; - playSuccessSound(); - onFinish(); - } - }.bind(this), 100); - }, - onAction: function(action, value) { - var STEP_YAW_ACTION = 6; - var SMOOTH_YAW_ACTION = 4; - - if (action == STEP_YAW_ACTION && value != 0) { - debug("TurnAround | Got step yaw action"); - ++this.numTimesSnapTurnPressed; - } else if (action == SMOOTH_YAW_ACTION) { - debug("TurnAround | Got smooth yaw action"); - if (this.smoothTurnDown && value === 0) { - this.smoothTurnDown = false; - ++this.numTimesSmoothTurnPressed; - } else if (!this.smoothTurnDown && value !== 0) { - this.smoothTurnDown = true; - } - } - }, - cleanup: function() { - debug("TurnAround | Cleanup"); - try { - Controller.actionEvent.disconnect(this.onActionBound); - } catch (e) { - } - - setControllerPartLayer('joystick', 'normal'); - setControllerPartLayer('touchpad', 'blank'); - setControllerPartLayer('tips', 'blank'); - - if (this.interval) { - Script.clearInterval(this.interval); - } - hideEntitiesWithTags(this.tags); - deleteEntitiesWithTag(this.tempTag); - } -}; - - - - -/////////////////////////////////////////////////////////////////////////////// -/////////////////////////////////////////////////////////////////////////////// -// // -// STEP: Teleport // -// // -/////////////////////////////////////////////////////////////////////////////// -var stepTeleport = function(tutorialManager) { - this.name = 'teleport'; - - this.tags = ["teleport", "teleport-" + tutorialManager.controllerName]; - this.tempTag = "teleport-temporary"; -} -stepTeleport.prototype = { - start: function(onFinish) { - setControllerPartLayer('button_a', 'highlight'); - setControllerPartLayer('touchpad', 'teleport'); - setControllerPartLayer('tips', 'teleport'); - - Messages.sendLocalMessage('Hifi-Teleport-Disabler', 'none'); - - // Wait until touching teleport pad... - 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("Teleport | Checking if on pad..."); - - var pos = MyAvatar.position; - - 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("Teleport | On teleport pad"); - Script.clearInterval(this.checkCollidesTimer); - this.checkCollidesTimer = null; - playSuccessSound(); - onFinish(); - } - } - this.checkCollidesTimer = Script.setInterval(checkCollides.bind(this), 500); - - showEntitiesWithTags(this.tags); - }, - cleanup: function() { - debug("Teleport | Cleanup"); - setControllerPartLayer('button_a', 'normal'); - setControllerPartLayer('touchpad', 'blank'); - setControllerPartLayer('tips', 'blank'); - - if (this.checkCollidesTimer) { - Script.clearInterval(this.checkCollidesTimer); - } - hideEntitiesWithTags(this.tags); - deleteEntitiesWithTag(this.tempTag); - } -}; - - - - - -/////////////////////////////////////////////////////////////////////////////// -/////////////////////////////////////////////////////////////////////////////// -// // -// STEP: Finish // -// // -/////////////////////////////////////////////////////////////////////////////// -var stepFinish = function() { - this.name = 'finish'; - - this.tag = "finish"; - this.tempTag = "finish-temporary"; -} -stepFinish.prototype = { - start: function(onFinish) { - editEntitiesWithTag('door', { visible: false, collisonless: true }); - showEntitiesWithTag(this.tag); - Settings.setValue("tutorialComplete", true); - onFinish(); - }, - cleanup: function() { - } -}; - -var stepCleanupFinish = function() { - this.name = 'cleanup'; - - this.shouldLog = false; -} -stepCleanupFinish.prototype = { - start: function(onFinish) { - hideEntitiesWithTag('finish'); - onFinish(); - }, - cleanup: function() { - } -}; - - - - - -TutorialManager = function() { - var STEPS; - - var currentStepNum = -1; - var currentStep = null; - var startedTutorialAt = 0; - var startedLastStepAt = 0; - var didFinishTutorial = false; - - var wentToEntryStepNum; - var VERSION = 2; - var tutorialID; - - var self = this; - - // The real controller name is the actual detected controller name, or 'unknown' - // if one is not found. - if (HMD.isSubdeviceContainingNameAvailable("OculusTouch")) { - this.controllerName = "touch"; - this.realControllerName = "touch"; - } else if (HMD.isHandControllerAvailable("OpenVR")) { - this.controllerName = "vive"; - this.realControllerName = "vive"; - } else { - info("ERROR, no known hand controller found, defaulting to Vive"); - this.controllerName = "vive"; - this.realControllerName = "unknown"; - } - - this.startTutorial = function() { - currentStepNum = -1; - currentStep = null; - startedTutorialAt = Date.now(); - - // Old versions of interface do not have the Script.generateUUID function. - // If Script.generateUUID is not available, default to an empty string. - tutorialID = Script.generateUUID ? Script.generateUUID() : ""; - STEPS = [ - new stepStart(this), - new stepOrient(this), - new stepFarGrab(this), - new stepNearGrab(this), - new stepEquip(this), - new stepTurnAround(this), - new stepTeleport(this), - new stepFinish(this), - new stepEnableControllers(this), - ]; - wentToEntryStepNum = STEPS.length; - for (var i = 0; i < STEPS.length; ++i) { - STEPS[i].cleanup(); - } - MyAvatar.shouldRenderLocally = false; - this.startNextStep(); - } - - this.onFinish = function() { - debug("onFinish", currentStepNum); - if (currentStep && currentStep.shouldLog !== false) { - self.trackStep(currentStep.name, currentStepNum); - } - - self.startNextStep(); - } - - this.startNextStep = function() { - if (currentStep) { - currentStep.cleanup(); - } - - ++currentStepNum; - - // This always needs to be set because we use this value when - // tracking that the user has gone through the entry portal. When the - // tutorial finishes, there is a last "pseudo" step that the user - // finishes when stepping into the portal. - startedLastStepAt = Date.now(); - - if (currentStepNum >= STEPS.length) { - // Done - info("DONE WITH TUTORIAL"); - currentStepNum = -1; - currentStep = null; - didFinishTutorial = true; - return false; - } else { - info("Starting step", currentStepNum); - currentStep = STEPS[currentStepNum]; - currentStep.start(this.onFinish); - return true; - } - }.bind(this); - - this.restartStep = function() { - if (currentStep) { - currentStep.cleanup(); - currentStep.start(this.onFinish); - } - } - - this.stopTutorial = function() { - if (currentStep) { - currentStep.cleanup(); - HMD.requestHideHandControllers(); - } - reenableEverything(); - currentStepNum = -1; - currentStep = null; - } - - this.trackStep = function(name, stepNum) { - var timeToFinishStep = (Date.now() - startedLastStepAt) / 1000; - var tutorialTimeElapsed = (Date.now() - startedTutorialAt) / 1000; - UserActivityLogger.tutorialProgress( - name, stepNum, timeToFinishStep, tutorialTimeElapsed, - tutorialID, VERSION, this.realControllerName); - } - - // This is a message sent from the "entry" portal in the courtyard, - // after the tutorial has finished. - this.enteredEntryPortal = function() { - info("Got enteredEntryPortal"); - if (didFinishTutorial) { - info("Tracking wentToEntry"); - this.trackStep("wentToEntry", wentToEntryStepNum); - } - } -} - -// To run the tutorial: -// -//var tutorialManager = new TutorialManager(); -//tutorialManager.startTutorial(); -// -// -//var keyReleaseHandler = function(event) { -// if (event.isShifted && event.isAlt) { -// print('here', event.text); -// if (event.text == "F12") { -// if (!tutorialManager.startNextStep()) { -// tutorialManager.startTutorial(); -// } -// } else if (event.text == "F11") { -// tutorialManager.restartStep(); -// } else if (event.text == "F10") { -// MyAvatar.shouldRenderLocally = !MyAvatar.shouldRenderLocally; -// } else if (event.text == "r") { -// tutorialManager.stopTutorial(); -// tutorialManager.startTutorial(); -// } -// } -//}; -//Controller.keyReleaseEvent.connect(keyReleaseHandler); diff --git a/tutorial/tutorialEntityIDs.js b/tutorial/tutorialEntityIDs.js deleted file mode 100644 index f53ac1a56c..0000000000 --- a/tutorial/tutorialEntityIDs.js +++ /dev/null @@ -1,360 +0,0 @@ -TUTORIAL_TAG_TO_ENTITY_IDS_MAP = { - "teleport-vive": { - "{7df1abc4-1b7c-4352-985c-f3f6ad8d65b7}": { - "tag": "teleport-vive" - } - }, - "teleport-touch": { - "{ff064b9e-7fa4-4693-a386-a67b9f92a948}": { - "tag": "teleport-touch" - } - }, - "turnAround-vive": { - "{9b14f224-b2f6-447f-bb86-f5d875cf4c33}": { - "tag": "turnAround-vive" - } - }, - "turnAround-touch": { - "{ce74b3ca-d1c7-4980-bd98-2d488095a39e}": { - "tag": "turnAround-touch" - } - }, - "teleport": { - "{4478f7b5-d3ac-4213-9a7b-ad8cd69575b8}": { - "tag": "teleport" - } - }, - "finish": { - "{340e05b5-88df-4b2b-b43c-756dd714d6d8}": { - "tag": "finish" - } - }, - "door": { - "{9c5b0fee-e695-4516-94cd-153371e3857b}": { - "tag": "door" - } - }, - "farGrab": { - "{70fcd96c-cd59-4f23-9ca5-a167f2f85680}": { - "visible": false, - "tag": "farGrab" - }, - "{ff7b9793-0d94-4f18-bc09-4ab589126e60}": { - "tag": "farGrab" - }, - "{fdd77d2c-af36-41c1-ba57-74b7ae79d996}": { - "tag": "farGrab" - }, - "{e11700f6-bc9a-411f-9ddc-bf265d4e3ccf}": { - "tag": "farGrab" - }, - "{95850c56-cd1c-42b9-ab6b-a163a6f2878f}": { - "tag": "farGrab" - } - }, - "nearGrab-vive": { - "{88221a22-b710-4d35-852b-5257b0aa77dc}": { - "tag": "nearGrab-vive" - } - }, - "nearGrab-touch": { - "{7c0f2fde-6c5c-459b-bf82-421979cebf2e}": { - "tag": "nearGrab-touch" - } - }, - "nearGrab": { - "{55c861ef-60ca-4722-a6c5-9c6967966ec5}": { - "tag": "nearGrab" - }, - "{644d655b-ae66-43b1-9bab-a44b9a8ad632}": { - "tag": "nearGrab" - }, - "{8bf0baa1-88d0-448a-a782-100d4413bd82}": { - "tag": "nearGrab" - }, - "{5cf22b9c-fb22-4854-8821-554422980b24}": { - "visible": false, - "tag": "nearGrab" - } - }, - "equip-part1-touch": { - "{470f0634-8be7-4b52-a8bd-5183d489fcb6}": { - "tag": "equip-part1-touch" - } - }, - "equip-part1-vive": { - "{97ced5e7-fc81-40f9-a9e8-f85b4b30f24c}": { - "tag": "equip-part1-vive" - } - }, - "equip-part1": { - "{d73822ca-0a34-4cf4-a530-3258ac459a14}": { - "tag": "equip-part1" - }, - "{8572d991-5777-45df-97bf-7243d7b12f81}": { - "tag": "equip-part1" - }, - "{da5ea72e-54b6-41ac-b711-742b062b6968}": { - "tag": "equip-part1" - }, - "{c8944a13-9acb-4d77-b1ee-851845e98357}": { - "tag": "equip-part1" - }, - "{e9481c78-1a21-43f7-b54c-58f2efdf3c8f}": { - "tag": "equip-part1" - }, - "{ca3c28f3-15fc-4349-a85e-eaca0fad6434}": { - "tag": "equip-part1" - }, - "{09ddcb94-52a7-4f50-a5a2-db9db28fc519}": { - "tag": "equip-part1" - }, - "{dd13fcd5-616f-4749-ab28-2e1e8bc512e9}": { - "tag": "equip-part1" - } - }, - "equip-part2-vive": { - "{b5d17eda-90ab-40cf-b973-efcecb2e992e}": { - "tag": "equip-part2-vive" - }, - "{6307cd16-dd1d-4988-a339-578178436b45}": { - "tag": "equip-part2-vive" - } - }, - "equip-part2-touch": { - "{69195139-e020-4739-bb2c-50faebc6860a}": { - "tag": "equip-part2-touch" - }, - "{9b0a99ae-221b-4e59-ba3c-d8e64a083774}": { - "tag": "equip-part2-touch" - } - }, - "bothGrab": { - "{14792a6e-dc6f-4e7a-843f-4b109b06b5a4}": { - "visible": false, - "tag": "bothGrab", - "collidable": true - }, - "{215dcd14-88fc-4604-9033-cbd2a660178a}": { - "tag": "bothGrab" - }, - "{fbc2e40d-0633-45ac-b1c9-97fc8465f93b}": { - "tag": "bothGrab" - }, - "{6752dad6-109d-4dc5-aef7-dc8509468cf4}": { - "tag": "bothGrab" - }, - "{178e2c71-dff5-4231-8d28-df47fddf4709}": { - "soundKey": { - "playbackGapRange": 0, - "url": "atp:/sounds/crackling_fire.L.wav", - "volume": 0.5, - "playbackGap": 5, - "playing": false, - "loop": true - }, - "tag": "bothGrab" - }, - "{52445ac5-8730-4457-827e-6c076d2c609c}": { - "tag": "bothGrab" - } - }, - "raiseHands": { - "{7139e45d-25cf-470b-b133-c0fda0099d2b}": { - "tag": "raiseHands" - } - }, - "equip": { - "{e7897c9c-f4fa-4989-a383-28af56c2e544}": { - "visible": false, - "tag": "equip" - }, - "{9df518da-9e65-4b76-8a79-eeefdb0b7310}": { - "visible": false, - "tag": "equip" - }, - "{1a77c20e-5d9b-4b54-bf20-1416141a7ca8}": { - "tag": "equip" - } - }, - "orient-vive": { - "{95d233ab-ed0a-46e1-b047-1c542688ef3f}": { - "tag": "orient-vive" - } - }, - "orient-touch": { - "{1c95f945-ec46-4aac-b0f1-e64e073dbfaa}": { - "tag": "orient-touch" - } - } -}; - -TUTORIAL_NAME_TO_ENTITY_PROPERTIES_MAP = { - "tutorial/gun_spawn": { - "clientOnly": 0, - "collisionless": 1, - "color": { - "blue": 0, - "green": 0, - "red": 255 - }, - "created": "2016-09-08T18:38:24Z", - "dimensions": { - "x": 0.0649842768907547, - "y": 0.0649842768907547, - "z": 0.0649842768907547 - }, - "id": "{9df518da-9e65-4b76-8a79-eeefdb0b7310}", - "ignoreForCollisions": 1, - "lastEdited": 1481926907366120, - "lastEditedBy": "{b80185ea-0936-4397-a5a4-3a64004f545f}", - "name": "tutorial/gun_spawn", - "owningAvatarID": "{00000000-0000-0000-0000-000000000000}", - "position": { - "x": 0.60231781005859375, - "y": 0.68465065956115723, - "z": 0.39223569631576538 - }, - "queryAACube": { - "scale": 0.11255607008934021, - "x": 0.54603976011276245, - "y": 0.62837260961532593, - "z": 0.33595764636993408 - }, - "rotation": { - "w": -0.025101065635681152, - "x": 0.70666050910949707, - "y": 0.70666050910949707, - "z": -0.025131583213806152 - }, - "shape": "Cube", - "type": "Box", - "userData": "{\"visible\":false,\"tag\":\"equip\"}", - "visible": 0 - }, - "tutorial/nearGrab/box_spawn": { - "clientOnly": 0, - "collisionless": 1, - "color": { - "blue": 255, - "green": 0, - "red": 255 - }, - "created": "2016-09-08T18:38:24Z", - "dimensions": { - "x": 0.082253716886043549, - "y": 0.082253716886043549, - "z": 0.082253716886043549 - }, - "id": "{5cf22b9c-fb22-4854-8821-554422980b24}", - "ignoreForCollisions": 1, - "lastEdited": 1481926907334206, - "lastEditedBy": "{b80185ea-0936-4397-a5a4-3a64004f545f}", - "name": "tutorial/nearGrab/box_spawn", - "owningAvatarID": "{00000000-0000-0000-0000-000000000000}", - "position": { - "x": 0.61857688426971436, - "y": 0.80955326557159424, - "z": 0.36191046237945557 - }, - "queryAACube": { - "scale": 0.14246761798858643, - "x": 0.54734307527542114, - "y": 0.73831945657730103, - "z": 0.29067665338516235 - }, - "rotation": { - "w": 1, - "x": -1.52587890625e-05, - "y": -1.52587890625e-05, - "z": -1.52587890625e-05 - }, - "shape": "Cube", - "type": "Box", - "userData": "{\"visible\":false,\"tag\":\"nearGrab\"}", - "visible": 0 - }, - "tutorial/farGrab/box_spawn": { - "clientOnly": 0, - "collisionless": 1, - "color": { - "blue": 255, - "green": 0, - "red": 255 - }, - "created": "2016-09-08T18:38:24Z", - "dimensions": { - "x": 0.16850528120994568, - "y": 0.16850528120994568, - "z": 0.16850528120994568 - }, - "id": "{70fcd96c-cd59-4f23-9ca5-a167f2f85680}", - "ignoreForCollisions": 1, - "lastEdited": 1481926908795578, - "lastEditedBy": "{b80185ea-0936-4397-a5a4-3a64004f545f}", - "name": "tutorial/farGrab/box_spawn", - "owningAvatarID": "{00000000-0000-0000-0000-000000000000}", - "position": { - "x": 3.4866282939910889, - "y": 0.67159509658813477, - "z": 0.47892442345619202 - }, - "queryAACube": { - "scale": 0.64707136154174805, - "x": 3.2037394046783447, - "y": 0.33042514324188232, - "z": 0.14542555809020996 - }, - "rotation": { - "w": 1, - "x": -1.52587890625e-05, - "y": -1.52587890625e-05, - "z": -1.52587890625e-05 - }, - "shape": "Cube", - "type": "Box", - "userData": "{\"visible\":false,\"tag\":\"farGrab\"}", - "visible": 0 - }, - "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" - } -}; diff --git a/tutorial/tutorialStartZone.js b/tutorial/tutorialStartZone.js deleted file mode 100644 index 13e5ac89d0..0000000000 --- a/tutorial/tutorialStartZone.js +++ /dev/null @@ -1,51 +0,0 @@ -(function() { - var TutorialStartZone = function() { - print("TutorialStartZone | Creating"); - }; - - TutorialStartZone.prototype = { - preload: function(entityID) { - print("TutorialStartZone | Preload"); - this.entityID = entityID; - this.sendStartIntervalID = null; - }, - enterEntity: function() { - var self = this; - // send message to outer zone - print("TutorialStartZone | Entered the tutorial start area"); - if (HMD.isHMDAvailable() && HMD.isHandControllerAvailable() && HMD.active) { - function sendStart() { - print("TutorialStartZone | Checking parent ID"); - var parentID = Entities.getEntityProperties(self.entityID, 'parentID').parentID; - print("TutorialStartZone | Parent ID is: ", parentID); - if (parentID) { - print("TutorialStartZone | Sending start"); - Entities.callEntityMethod(parentID, 'onEnteredStartZone'); - } else { - print("TutorialStartZone | ERROR: No parent id found on tutorial start zone"); - } - } - this.sendStartIntervalID = Script.setInterval(sendStart, 1500); - sendStart(); - } else { - print("TutorialStartZone | User tried to go to tutorial without active HMD and hand controllers, sending back to /"); - Window.alert("To proceed with this tutorial, please connect your Vive or Oculus headset and hand controllers."); - location = "/"; - } - }, - leaveEntity: function() { - print("TutorialStartZone | Exited the tutorial start area"); - 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'); - } - } - }; - - return new TutorialStartZone(); -}); diff --git a/tutorial/tutorialZone.js b/tutorial/tutorialZone.js deleted file mode 100644 index 78443ef7df..0000000000 --- a/tutorial/tutorialZone.js +++ /dev/null @@ -1,142 +0,0 @@ -if (!Function.prototype.bind) { - Function.prototype.bind = function(oThis) { - if (typeof this !== 'function') { - // closest thing possible to the ECMAScript 5 - // internal IsCallable function - throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable'); - } - - var aArgs = Array.prototype.slice.call(arguments, 1), - fToBind = this, - fNOP = function() {}, - fBound = function() { - return fToBind.apply(this instanceof fNOP - ? this - : oThis, - aArgs.concat(Array.prototype.slice.call(arguments))); - }; - - if (this.prototype) { - // Function.prototype doesn't have a prototype property - fNOP.prototype = this.prototype; - } - fBound.prototype = new fNOP(); - - return fBound; - }; -} - -(function() { - 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"); - this.token = null; - }; - - TutorialZone.prototype = { - keyReleaseHandler: function(event) { - print(event.text); - if (event.isShifted && event.isAlt) { - if (event.text == "F12") { - if (!this.tutorialManager.startNextStep()) { - this.tutorialManager.startTutorial(); - } - } else if (event.text == "F11") { - this.tutorialManager.restartStep(); - } else if (event.text == "F10") { - MyAvatar.shouldRenderLocally = !MyAvatar.shouldRenderLocally; - } else if (event.text == "r") { - this.tutorialManager.stopTutorial(); - this.tutorialManager.startTutorial(); - } - } - }, - preload: function(entityID) { - print("TutorialZone | Preload"); - this.entityID = entityID; - }, - 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 - 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"); - if (!self.tutorialManager) { - self.tutorialManager = new TutorialManager(); - } - self.tutorialManager.startTutorial(); - print("TutorialZone | making bound release handler"); - self.keyReleaseHandlerBound = self.keyReleaseHandler.bind(self); - print("TutorialZone | binding"); - Controller.keyReleaseEvent.connect(self.keyReleaseHandlerBound); - print("TutorialZone | done"); - }, - onLostOwnership: function(token) { - print("TutorialZone | LOST OWNERSHIP"); - if (self.tutorialManager) { - print("TutorialZone | stopping tutorial.."); - self.tutorialManager.stopTutorial(); - print("TutorialZone | done"); - Controller.keyReleaseEvent.disconnect(self.keyReleaseHandlerBound); - } else { - print("TutorialZone | no tutorial manager..."); - } - } - }); - } - }, - 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 (!this.tutorialManager) { - Menu.setIsOptionChecked("Overlays", true); - MyAvatar.shouldRenderLocally = true; - setAwayEnabled(true); - Toolbars.getToolbar("com.highfidelity.interface.toolbar.system").writeProperty("visible", true); - } - }, - - onEnteredEntryPortal: function() { - print("TutorialZone | Got onEnteredEntryPortal"); - if (this.tutorialManager) { - print("TutorialZone | Calling enteredEntryPortal"); - this.tutorialManager.enteredEntryPortal(); - } - }, - - enterEntity: function() { - print("TutorialZone | ENTERED THE TUTORIAL AREA"); - }, - leaveEntity: function() { - print("TutorialZone | EXITED THE TUTORIAL AREA"); - if (this.token) { - print("TutorialZone | Destroying token"); - this.token.destroy(); - this.token = null; - } - if (this.tutorialManager) { - this.tutorialManager.stopTutorial(); - //this.tutorialManager = null; - } - } - }; - - return new TutorialZone(); -}); diff --git a/unpublishedScripts/marketplace/shortbow/bow/bow.js b/unpublishedScripts/marketplace/shortbow/bow/bow.js index f8ef025728..a8e76f76fd 100644 --- a/unpublishedScripts/marketplace/shortbow/bow/bow.js +++ b/unpublishedScripts/marketplace/shortbow/bow/bow.js @@ -103,7 +103,7 @@ function getControllerLocation(controllerHand) { const STRING_PULL_SOUND_URL = Script.resolvePath('Bow_draw.1.L.wav'); const ARROW_HIT_SOUND_URL = Script.resolvePath('Arrow_impact1.L.wav'); - const ARROW_MODEL_URL = Script.resolvePath('arrow.fbx'); + const ARROW_MODEL_URL = Script.resolvePath('models/arrow.baked.fbx'); const ARROW_DIMENSIONS = { x: 0.20, y: 0.19, diff --git a/unpublishedScripts/marketplace/shortbow/bow/models/Arrow-texture.ktx b/unpublishedScripts/marketplace/shortbow/bow/models/Arrow-texture.ktx new file mode 100644 index 0000000000..ea8c420d28 Binary files /dev/null and b/unpublishedScripts/marketplace/shortbow/bow/models/Arrow-texture.ktx differ diff --git a/unpublishedScripts/marketplace/shortbow/bow/models/arrow.baked.fbx b/unpublishedScripts/marketplace/shortbow/bow/models/arrow.baked.fbx new file mode 100644 index 0000000000..f29172bdc2 Binary files /dev/null and b/unpublishedScripts/marketplace/shortbow/bow/models/arrow.baked.fbx differ diff --git a/unpublishedScripts/marketplace/shortbow/bow/arrow.fbx b/unpublishedScripts/marketplace/shortbow/bow/models/arrow.fbx similarity index 100% rename from unpublishedScripts/marketplace/shortbow/bow/arrow.fbx rename to unpublishedScripts/marketplace/shortbow/bow/models/arrow.fbx diff --git a/unpublishedScripts/marketplace/shortbow/bow/models/bow-deadly.baked.fbx b/unpublishedScripts/marketplace/shortbow/bow/models/bow-deadly.baked.fbx new file mode 100644 index 0000000000..830300896a Binary files /dev/null and b/unpublishedScripts/marketplace/shortbow/bow/models/bow-deadly.baked.fbx differ diff --git a/unpublishedScripts/marketplace/shortbow/bow/bow-deadly.fbx b/unpublishedScripts/marketplace/shortbow/bow/models/bow-deadly.fbx similarity index 100% rename from unpublishedScripts/marketplace/shortbow/bow/bow-deadly.fbx rename to unpublishedScripts/marketplace/shortbow/bow/models/bow-deadly.fbx diff --git a/unpublishedScripts/marketplace/shortbow/bow/models/bow_bump-SM.ktx b/unpublishedScripts/marketplace/shortbow/bow/models/bow_bump-SM.ktx new file mode 100644 index 0000000000..6a5cc84e0d Binary files /dev/null and b/unpublishedScripts/marketplace/shortbow/bow/models/bow_bump-SM.ktx differ diff --git a/unpublishedScripts/marketplace/shortbow/bow/bow_collision_hull.obj b/unpublishedScripts/marketplace/shortbow/bow/models/bow_collision_hull.obj similarity index 100% rename from unpublishedScripts/marketplace/shortbow/bow/bow_collision_hull.obj rename to unpublishedScripts/marketplace/shortbow/bow/models/bow_collision_hull.obj diff --git a/unpublishedScripts/marketplace/shortbow/bow/models/bow_diff-SM.ktx b/unpublishedScripts/marketplace/shortbow/bow/models/bow_diff-SM.ktx new file mode 100644 index 0000000000..a7bd1dd0b7 Binary files /dev/null and b/unpublishedScripts/marketplace/shortbow/bow/models/bow_diff-SM.ktx differ diff --git a/unpublishedScripts/marketplace/shortbow/bow/spawnBow.js b/unpublishedScripts/marketplace/shortbow/bow/spawnBow.js index cb94b05556..ca8b24fe41 100644 --- a/unpublishedScripts/marketplace/shortbow/bow/spawnBow.js +++ b/unpublishedScripts/marketplace/shortbow/bow/spawnBow.js @@ -37,7 +37,7 @@ var userData = { var id = Entities.addEntity({ "position": MyAvatar.position, "collisionsWillMove": 1, - "compoundShapeURL": Script.resolvePath("bow_collision_hull.obj"), + "compoundShapeURL": Script.resolvePath("models/bow_collision_hull.obj"), "created": "2016-09-01T23:57:55Z", "dimensions": { "x": 0.039999999105930328, @@ -50,7 +50,7 @@ var id = Entities.addEntity({ "y": -9.8, "z": 0 }, - "modelURL": Script.resolvePath("bow-deadly.fbx"), + "modelURL": Script.resolvePath("models/bow-deadly.baked.fbx"), "name": "Hifi-Bow", "rotation": { "w": 0.9718012809753418, diff --git a/unpublishedScripts/marketplace/shortbow/models/Amber.baked.fbx b/unpublishedScripts/marketplace/shortbow/models/Amber.baked.fbx new file mode 100644 index 0000000000..5deec8ec79 Binary files /dev/null and b/unpublishedScripts/marketplace/shortbow/models/Amber.baked.fbx differ diff --git a/unpublishedScripts/marketplace/shortbow/models/Ball_C_9.ktx b/unpublishedScripts/marketplace/shortbow/models/Ball_C_9.ktx new file mode 100644 index 0000000000..a180d2314b Binary files /dev/null and b/unpublishedScripts/marketplace/shortbow/models/Ball_C_9.ktx differ diff --git a/unpublishedScripts/marketplace/shortbow/models/Metallic.ktx b/unpublishedScripts/marketplace/shortbow/models/Metallic.ktx new file mode 100644 index 0000000000..b9d7fa81fe Binary files /dev/null and b/unpublishedScripts/marketplace/shortbow/models/Metallic.ktx differ diff --git a/unpublishedScripts/marketplace/shortbow/models/Roughness.ktx b/unpublishedScripts/marketplace/shortbow/models/Roughness.ktx new file mode 100644 index 0000000000..187373a4ad Binary files /dev/null and b/unpublishedScripts/marketplace/shortbow/models/Roughness.ktx differ diff --git a/unpublishedScripts/marketplace/shortbow/models/mat.ground Diffuse Color.ktx b/unpublishedScripts/marketplace/shortbow/models/mat.ground Diffuse Color.ktx new file mode 100644 index 0000000000..de9b9d3926 Binary files /dev/null and b/unpublishedScripts/marketplace/shortbow/models/mat.ground Diffuse Color.ktx differ diff --git a/unpublishedScripts/marketplace/shortbow/models/mat.platform Diffuse Color.ktx b/unpublishedScripts/marketplace/shortbow/models/mat.platform Diffuse Color.ktx new file mode 100644 index 0000000000..19ecc14bc1 Binary files /dev/null and b/unpublishedScripts/marketplace/shortbow/models/mat.platform Diffuse Color.ktx differ diff --git a/unpublishedScripts/marketplace/shortbow/models/mat.scoreboard Diffuse Color.ktx b/unpublishedScripts/marketplace/shortbow/models/mat.scoreboard Diffuse Color.ktx new file mode 100644 index 0000000000..ed7236ef59 Binary files /dev/null and b/unpublishedScripts/marketplace/shortbow/models/mat.scoreboard Diffuse Color.ktx differ diff --git a/unpublishedScripts/marketplace/shortbow/models/mat.spawner Diffuse Color.ktx b/unpublishedScripts/marketplace/shortbow/models/mat.spawner Diffuse Color.ktx new file mode 100644 index 0000000000..4c501cf18d Binary files /dev/null and b/unpublishedScripts/marketplace/shortbow/models/mat.spawner Diffuse Color.ktx differ diff --git a/unpublishedScripts/marketplace/shortbow/models/mat.target Diffuse Color.ktx b/unpublishedScripts/marketplace/shortbow/models/mat.target Diffuse Color.ktx new file mode 100644 index 0000000000..1ea63bd64b Binary files /dev/null and b/unpublishedScripts/marketplace/shortbow/models/mat.target Diffuse Color.ktx differ diff --git a/unpublishedScripts/marketplace/shortbow/models/mat.turret Diffuse Color.ktx b/unpublishedScripts/marketplace/shortbow/models/mat.turret Diffuse Color.ktx new file mode 100644 index 0000000000..378dc69d74 Binary files /dev/null and b/unpublishedScripts/marketplace/shortbow/models/mat.turret Diffuse Color.ktx differ diff --git a/unpublishedScripts/marketplace/shortbow/models/play-area-Diffuse.ktx b/unpublishedScripts/marketplace/shortbow/models/play-area-Diffuse.ktx new file mode 100644 index 0000000000..d4fe5e5cc5 Binary files /dev/null and b/unpublishedScripts/marketplace/shortbow/models/play-area-Diffuse.ktx differ diff --git a/unpublishedScripts/marketplace/shortbow/models/shortbow-button.baked.fbx b/unpublishedScripts/marketplace/shortbow/models/shortbow-button.baked.fbx new file mode 100644 index 0000000000..8793cf5a30 Binary files /dev/null and b/unpublishedScripts/marketplace/shortbow/models/shortbow-button.baked.fbx differ diff --git a/unpublishedScripts/marketplace/shortbow/models/shortbow-platform.baked.fbx b/unpublishedScripts/marketplace/shortbow/models/shortbow-platform.baked.fbx new file mode 100644 index 0000000000..afc8d95e29 Binary files /dev/null and b/unpublishedScripts/marketplace/shortbow/models/shortbow-platform.baked.fbx differ diff --git a/unpublishedScripts/marketplace/shortbow/models/shortbow-scoreboard.baked.fbx b/unpublishedScripts/marketplace/shortbow/models/shortbow-scoreboard.baked.fbx new file mode 100644 index 0000000000..03c84f091c Binary files /dev/null and b/unpublishedScripts/marketplace/shortbow/models/shortbow-scoreboard.baked.fbx differ diff --git a/unpublishedScripts/marketplace/shortbow/shortbow.js b/unpublishedScripts/marketplace/shortbow/shortbow.js index 641e9c45a6..a81108e2b3 100644 --- a/unpublishedScripts/marketplace/shortbow/shortbow.js +++ b/unpublishedScripts/marketplace/shortbow/shortbow.js @@ -114,7 +114,7 @@ SHORTBOW_ENTITIES = "id": "{04288f77-64df-4323-ac38-9c1960a393a5}", "lastEdited": 1487893058314990, "lastEditedBy": "{fce8028a-4bac-43e8-96ff-4c7286ea4ab3}", - "modelURL": "file:///c:/Users/ryanh/dev/hifi/unpublishedScripts/marketplace/shortbow/models/shortbow-button.fbx", + "modelURL": "file:///c:/Users/ryanh/dev/hifi/unpublishedScripts/marketplace/shortbow/models/shortbow-button.baked.fbx", "name": "SB.StartButton", "owningAvatarID": "{00000000-0000-0000-0000-000000000000}", "parentID": "{0cd1f1f7-53b9-4c15-bf25-42c0760d16f0}", @@ -758,7 +758,7 @@ SHORTBOW_ENTITIES = "id": "{d4c8f577-944d-4d50-ac85-e56387c0ef0a}", "lastEdited": 1487892440231278, "lastEditedBy": "{91f193dd-829a-4b33-ab27-e9a26160634a}", - "modelURL": "file:///c:/Users/ryanh/dev/hifi/unpublishedScripts/marketplace/shortbow/models/shortbow-platform.fbx", + "modelURL": "file:///c:/Users/ryanh/dev/hifi/unpublishedScripts/marketplace/shortbow/models/shortbow-platform.baked.fbx", "name": "SB.Platform", "owningAvatarID": "{00000000-0000-0000-0000-000000000000}", "parentID": "{0cd1f1f7-53b9-4c15-bf25-42c0760d16f0}", @@ -794,7 +794,7 @@ SHORTBOW_ENTITIES = "id": "{0cd1f1f7-53b9-4c15-bf25-42c0760d16f0}", "lastEdited": 1487892440231832, "lastEditedBy": "{91f193dd-829a-4b33-ab27-e9a26160634a}", - "modelURL": "file:///c:/Users/ryanh/dev/hifi/unpublishedScripts/marketplace/shortbow/models/shortbow-scoreboard.fbx", + "modelURL": "file:///c:/Users/ryanh/dev/hifi/unpublishedScripts/marketplace/shortbow/models/shortbow-scoreboard.baked.fbx", "name": "SB.Scoreboard", "owningAvatarID": "{00000000-0000-0000-0000-000000000000}", "queryAACube": { diff --git a/unpublishedScripts/marketplace/shortbow/shortbow.json b/unpublishedScripts/marketplace/shortbow/shortbow.json index 47934baea5..f29ef5f528 100644 --- a/unpublishedScripts/marketplace/shortbow/shortbow.json +++ b/unpublishedScripts/marketplace/shortbow/shortbow.json @@ -96,7 +96,7 @@ "id": "{04288f77-64df-4323-ac38-9c1960a393a5}", "lastEdited": 1487893058314990, "lastEditedBy": "{fce8028a-4bac-43e8-96ff-4c7286ea4ab3}", - "modelURL": "file:///c:/Users/ryanh/dev/hifi/unpublishedScripts/marketplace/shortbow/models/shortbow-button.fbx", + "modelURL": "file:///c:/Users/ryanh/dev/hifi/unpublishedScripts/marketplace/shortbow/models/shortbow-button.baked.fbx", "name": "SB.StartButton", "owningAvatarID": "{00000000-0000-0000-0000-000000000000}", "parentID": "{0cd1f1f7-53b9-4c15-bf25-42c0760d16f0}", @@ -740,7 +740,7 @@ "id": "{d4c8f577-944d-4d50-ac85-e56387c0ef0a}", "lastEdited": 1487892440231278, "lastEditedBy": "{91f193dd-829a-4b33-ab27-e9a26160634a}", - "modelURL": "file:///c:/Users/ryanh/dev/hifi/unpublishedScripts/marketplace/shortbow/models/shortbow-platform.fbx", + "modelURL": "file:///c:/Users/ryanh/dev/hifi/unpublishedScripts/marketplace/shortbow/models/shortbow-platform.baked.fbx", "name": "SB.Platform", "owningAvatarID": "{00000000-0000-0000-0000-000000000000}", "parentID": "{0cd1f1f7-53b9-4c15-bf25-42c0760d16f0}", @@ -776,7 +776,7 @@ "id": "{0cd1f1f7-53b9-4c15-bf25-42c0760d16f0}", "lastEdited": 1487892440231832, "lastEditedBy": "{91f193dd-829a-4b33-ab27-e9a26160634a}", - "modelURL": "file:///c:/Users/ryanh/dev/hifi/unpublishedScripts/marketplace/shortbow/models/shortbow-scoreboard.fbx", + "modelURL": "file:///c:/Users/ryanh/dev/hifi/unpublishedScripts/marketplace/shortbow/models/shortbow-scoreboard.baked.fbx", "name": "SB.Scoreboard", "owningAvatarID": "{00000000-0000-0000-0000-000000000000}", "queryAACube": { diff --git a/unpublishedScripts/marketplace/shortbow/shortbowGameManager.js b/unpublishedScripts/marketplace/shortbow/shortbowGameManager.js index bd42e40427..9f0d8c93f3 100644 --- a/unpublishedScripts/marketplace/shortbow/shortbowGameManager.js +++ b/unpublishedScripts/marketplace/shortbow/shortbowGameManager.js @@ -131,7 +131,7 @@ var baseEnemyProperties = { }, "lifetime": 30, "id": "{ed8f7339-8bbd-4750-968e-c3ceb9d64721}", - "modelURL": Script.resolvePath("models/Amber.fbx"), + "modelURL": Script.resolvePath("models/Amber.baked.fbx"), "owningAvatarID": "{00000000-0000-0000-0000-000000000000}", "queryAACube": { "scale": 1.0999215841293335, @@ -290,7 +290,7 @@ ShortbowGameManager.prototype = { "position": props.position, "rotation": props.rotation, "collisionsWillMove": 1, - "compoundShapeURL": Script.resolvePath("bow/bow_collision_hull.obj"), + "compoundShapeURL": Script.resolvePath("bow/models/bow_collision_hull.obj"), "created": "2016-09-01T23:57:55Z", "dimensions": { "x": 0.039999999105930328, @@ -303,7 +303,7 @@ ShortbowGameManager.prototype = { "y": -9.8, "z": 0 }, - "modelURL": Script.resolvePath("bow/bow-deadly.fbx"), + "modelURL": Script.resolvePath("bow/models/bow-deadly.baked.fbx"), "name": "WG.Hifi-Bow", "script": Script.resolvePath("bow/bow.js"), "shapeType": "compound",