diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index 1dc3aefb61..260a6d6825 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -453,6 +454,9 @@ void Agent::executeScript() { _scriptEngine->registerGlobalObject("EntityViewer", &_entityViewer); + _scriptEngine->registerGetterSetter("location", LocationScriptingInterface::locationGetter, + LocationScriptingInterface::locationSetter); + auto recordingInterface = DependencyManager::get(); _scriptEngine->registerGlobalObject("Recording", recordingInterface.data()); 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/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/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 c5e9b08143..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", diff --git a/domain-server/resources/web/header.html b/domain-server/resources/web/header.html index 965f86b0a1..0dc08e6e31 100644 --- a/domain-server/resources/web/header.html +++ b/domain-server/resources/web/header.html @@ -10,7 +10,6 @@ - + + +
diff --git a/domain-server/resources/web/js/domain-server.js b/domain-server/resources/web/js/domain-server.js index 3f78d8f466..88ab7b1470 100644 --- a/domain-server/resources/web/js/domain-server.js +++ b/domain-server/resources/web/js/domain-server.js @@ -1,3 +1,28 @@ +function showRestartModal() { + $('#restart-modal').modal({ + backdrop: 'static', + keyboard: false + }); + + var secondsElapsed = 0; + var numberOfSecondsToWait = 3; + + var refreshSpan = $('span#refresh-time') + refreshSpan.html(numberOfSecondsToWait + " seconds"); + + // call ourselves every 1 second to countdown + var refreshCountdown = setInterval(function(){ + secondsElapsed++; + secondsLeft = numberOfSecondsToWait - secondsElapsed + refreshSpan.html(secondsLeft + (secondsLeft == 1 ? " second" : " seconds")) + + if (secondsElapsed == numberOfSecondsToWait) { + location.reload(true); + clearInterval(refreshCountdown); + } + }, 1000); +} + $(document).ready(function(){ var url = window.location; // Will only work if string in href matches with location @@ -7,4 +32,10 @@ $(document).ready(function(){ $('ul.nav a').filter(function() { return this.href == url; }).parent().addClass('active'); + + $('body').on('click', '#restart-server', function(e){ + $.get("/restart"); + showRestartModal(); + return false; + }); }); \ No newline at end of file diff --git a/domain-server/resources/web/settings/index.shtml b/domain-server/resources/web/settings/index.shtml index 1812c52dad..13f5668010 100644 --- a/domain-server/resources/web/settings/index.shtml +++ b/domain-server/resources/web/settings/index.shtml @@ -26,7 +26,7 @@ - +
@@ -77,23 +77,10 @@ - - diff --git a/domain-server/resources/web/settings/js/settings.js b/domain-server/resources/web/settings/js/settings.js index 39628ebb11..0057df721a 100644 --- a/domain-server/resources/web/settings/js/settings.js +++ b/domain-server/resources/web/settings/js/settings.js @@ -1328,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'); @@ -1350,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; }); + } } } @@ -1365,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); } @@ -1656,31 +1680,6 @@ function updateDataChangedForSiblingRows(row, forceTrue) { }) } -function showRestartModal() { - $('#restart-modal').modal({ - backdrop: 'static', - keyboard: false - }); - - var secondsElapsed = 0; - var numberOfSecondsToWait = 3; - - var refreshSpan = $('span#refresh-time') - refreshSpan.html(numberOfSecondsToWait + " seconds"); - - // call ourselves every 1 second to countdown - var refreshCountdown = setInterval(function(){ - secondsElapsed++; - secondsLeft = numberOfSecondsToWait - secondsElapsed - refreshSpan.html(secondsLeft + (secondsLeft == 1 ? " second" : " seconds")) - - if (secondsElapsed == numberOfSecondsToWait) { - location.reload(true); - clearInterval(refreshCountdown); - } - }, 1000); -} - function cleanupFormValues(node) { if (node.type && node.type === 'checkbox') { return { name: node.name, value: node.checked ? true : false }; diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index d637a20454..0c1ebbf189 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -1650,6 +1650,7 @@ bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url const QString URI_NODES = "/nodes"; const QString URI_SETTINGS = "/settings"; const QString URI_ENTITY_FILE_UPLOAD = "/content/upload"; + const QString URI_RESTART = "/restart"; const QString UUID_REGEX_STRING = "[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}"; @@ -1804,6 +1805,10 @@ bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url // send the response connection->respond(HTTPConnection::StatusCode200, nodesDocument.toJson(), qPrintable(JSON_MIME_TYPE)); + return true; + } else if (url.path() == URI_RESTART) { + connection->respond(HTTPConnection::StatusCode200); + restart(); return true; } else { // check if this is for json stats for a node diff --git a/interface/resources/fonts/hifi-glyphs.ttf b/interface/resources/fonts/hifi-glyphs.ttf index 548f61e1de..ddb0743bf8 100644 Binary files a/interface/resources/fonts/hifi-glyphs.ttf and b/interface/resources/fonts/hifi-glyphs.ttf differ diff --git a/interface/resources/icons/tablet-icons/mic-mute-a.svg b/interface/resources/icons/tablet-icons/mic-mute-a.svg index 4b199c8e01..9dc2c53443 100644 --- a/interface/resources/icons/tablet-icons/mic-mute-a.svg +++ b/interface/resources/icons/tablet-icons/mic-mute-a.svg @@ -1,70 +1,25 @@ - + - -image/svg+xml \ No newline at end of file + + + + + + + + + + + + + + diff --git a/interface/resources/icons/tablet-icons/mic-mute-i.svg b/interface/resources/icons/tablet-icons/mic-mute-i.svg index 69feec7c17..9dc2c53443 100644 --- a/interface/resources/icons/tablet-icons/mic-mute-i.svg +++ b/interface/resources/icons/tablet-icons/mic-mute-i.svg @@ -1,21 +1,25 @@ - + + + - - - + + - - + diff --git a/interface/resources/qml/AvatarInputs.qml b/interface/resources/qml/AvatarInputs.qml index 28f3c0c7b9..d86d4fd022 100644 --- a/interface/resources/qml/AvatarInputs.qml +++ b/interface/resources/qml/AvatarInputs.qml @@ -12,84 +12,21 @@ import QtQuick.Controls 1.3 import QtGraphicalEffects 1.0 import Qt.labs.settings 1.0 +import "./hifi/audio" as HifiAudio + Hifi.AvatarInputs { - id: root + id: root; objectName: "AvatarInputs" - width: rootWidth - height: controls.height - x: 10; y: 5 + width: audio.width; + height: audio.height; + x: 10; y: 5; - readonly property int rootWidth: 265 - readonly property int iconSize: 24 - readonly property int iconPadding: 5 + readonly property bool shouldReposition: true; - readonly property bool shouldReposition: true - - Settings { - category: "Overlay.AvatarInputs" - property alias x: root.x - property alias y: root.y - } - - MouseArea { - id: hover - hoverEnabled: true - drag.target: parent - anchors.fill: parent - } - - Item { - id: controls - width: root.rootWidth - height: 44 - visible: root.showAudioTools - - Rectangle { - anchors.fill: parent - color: "#00000000" - - Item { - id: audioMeter - anchors.verticalCenter: parent.verticalCenter - anchors.left: parent.left - anchors.leftMargin: root.iconPadding - anchors.right: parent.right - anchors.rightMargin: root.iconPadding - height: 8 - Rectangle { - id: blueRect - color: "blue" - anchors.top: parent.top - anchors.bottom: parent.bottom - anchors.left: parent.left - width: parent.width / 4 - } - Rectangle { - id: greenRect - color: "green" - anchors.top: parent.top - anchors.bottom: parent.bottom - anchors.left: blueRect.right - anchors.right: redRect.left - } - Rectangle { - id: redRect - color: "red" - anchors.top: parent.top - anchors.bottom: parent.bottom - anchors.right: parent.right - width: parent.width / 5 - } - Rectangle { - z: 100 - anchors.top: parent.top - anchors.bottom: parent.bottom - anchors.right: parent.right - width: (1.0 - root.audioLevel) * parent.width - color: "black" - } - } - } + HifiAudio.MicBar { + id: audio; + visible: root.showAudioTools; + standalone: true; + dragTarget: parent; } } - diff --git a/interface/resources/qml/QmlWindow.qml b/interface/resources/qml/QmlWindow.qml index 9ef151b32e..9a84418b3a 100644 --- a/interface/resources/qml/QmlWindow.qml +++ b/interface/resources/qml/QmlWindow.qml @@ -65,6 +65,12 @@ Windows.Window { root.dynamicContent.fromScript(message); } } + + function clearDebugWindow() { + if (root.dynamicContent && root.dynamicContent.clearWindow) { + root.dynamicContent.clearWindow(); + } + } // Handle message traffic from our loaded QML to the script that launched us signal sendToScript(var message); diff --git a/interface/resources/qml/controls-uit/CheckBox.qml b/interface/resources/qml/controls-uit/CheckBox.qml index d6dc5d2736..a637207781 100644 --- a/interface/resources/qml/controls-uit/CheckBox.qml +++ b/interface/resources/qml/controls-uit/CheckBox.qml @@ -22,6 +22,7 @@ Original.CheckBox { readonly property bool isLightColorScheme: colorScheme == hifi.colorSchemes.light property bool isRedCheck: false property int boxSize: 14 + property bool wrap: true; readonly property int boxRadius: 3 readonly property int checkSize: Math.max(boxSize - 8, 10) readonly property int checkRadius: 2 @@ -92,7 +93,8 @@ Original.CheckBox { text: control.text color: control.color x: 2 - wrapMode: Text.Wrap + wrapMode: checkBox.wrap ? Text.Wrap : Text.NoWrap + elide: checkBox.wrap ? Text.ElideNone : Text.ElideRight 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/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/audio/Audio.qml b/interface/resources/qml/hifi/audio/Audio.qml index a30aba2a6b..c13bd3281a 100644 --- a/interface/resources/qml/hifi/audio/Audio.qml +++ b/interface/resources/qml/hifi/audio/Audio.qml @@ -18,7 +18,7 @@ import QtQuick.Layouts 1.3 import "../../styles-uit" import "../../controls-uit" as HifiControls import "../../windows" -import "./" as Audio +import "./" as AudioControls Rectangle { id: root; @@ -52,33 +52,40 @@ Rectangle { Separator { visible: root.showTitle() } - Grid { - columns: 2; + ColumnLayout { 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 + // mute is in its own row + RowLayout { + AudioControls.CheckBox { + text: qsTr("Mute microphone"); + isRedCheck: true; + 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 + + RowLayout { + spacing: 16; + AudioControls.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 + AudioControls.CheckBox { + text: qsTr("Show audio level meter"); + checked: AvatarInputs.showAudioTools; + onClicked: { + AvatarInputs.showAudioTools = checked; + checked = Qt.binding(function() { return AvatarInputs.showAudioTools; }); // restore binding + } } } } @@ -110,12 +117,25 @@ Rectangle { delegate: Item { width: parent.width; height: 36; - Audio.CheckBox { - text: display; - checked: selected; - onClicked: { - selected = checked; - checked = Qt.binding(function() { return selected; }); // restore binding + + RowLayout { + width: parent.width; + + AudioControls.CheckBox { + Layout.maximumWidth: parent.width - level.width - 40; + text: display; + wrap: false; + checked: selected; + onClicked: { + selected = checked; + checked = Qt.binding(function() { return selected; }); // restore binding + } + } + InputLevel { + id: level; + Layout.alignment: Qt.AlignRight; + Layout.rightMargin: 30; + visible: selected; } } } @@ -124,17 +144,23 @@ Rectangle { 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"); + Column { + 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"); + } + } + + PlaySampleSound { anchors { left: parent.left; leftMargin: 60 }} } } @@ -148,7 +174,7 @@ Rectangle { delegate: Item { width: parent.width; height: 36; - Audio.CheckBox { + AudioControls.CheckBox { text: display; checked: selected; onClicked: { diff --git a/interface/resources/qml/hifi/audio/InputLevel.qml b/interface/resources/qml/hifi/audio/InputLevel.qml new file mode 100644 index 0000000000..58ce3a1ec7 --- /dev/null +++ b/interface/resources/qml/hifi/audio/InputLevel.qml @@ -0,0 +1,105 @@ +// +// InputLevel.qml +// qml/hifi/audio +// +// Created by Zach Pomerantz on 6/20/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 QtGraphicalEffects 1.0 + +Rectangle { + readonly property var level: Audio.inputLevel; + + width: 70; + height: 8; + + color: "transparent"; + + Item { + id: colors; + + readonly property string muted: "#E2334D"; + readonly property string gutter: "#575757"; + readonly property string greenStart: "#39A38F"; + readonly property string greenEnd: "#1FC6A6"; + readonly property string red: colors.muted; + } + + Text { + id: status; + + anchors { + horizontalCenter: parent.horizontalCenter; + verticalCenter: parent.verticalCenter; + } + + visible: Audio.muted; + color: colors.muted; + + text: "MUTED"; + font.pointSize: 10; + } + + Item { + id: bar; + + width: parent.width; + height: parent.height; + + anchors { fill: parent } + + visible: !status.visible; + + Rectangle { // base + radius: 4; + anchors { fill: parent } + color: colors.gutter; + } + + Rectangle { // mask + id: mask; + width: parent.width * level; + radius: 5; + anchors { + bottom: parent.bottom; + bottomMargin: 0; + top: parent.top; + topMargin: 0; + left: parent.left; + leftMargin: 0; + } + } + + LinearGradient { + anchors { fill: mask } + source: mask + start: Qt.point(0, 0); + end: Qt.point(70, 0); + gradient: Gradient { + GradientStop { + position: 0; + color: colors.greenStart; + } + GradientStop { + position: 0.8; + color: colors.greenEnd; + } + GradientStop { + position: 0.801; + color: colors.red; + } + GradientStop { + position: 1; + color: colors.red; + } + } + } + } +} diff --git a/interface/resources/qml/hifi/audio/MicBar.qml b/interface/resources/qml/hifi/audio/MicBar.qml new file mode 100644 index 0000000000..10e12551b7 --- /dev/null +++ b/interface/resources/qml/hifi/audio/MicBar.qml @@ -0,0 +1,223 @@ +// +// MicBar.qml +// qml/hifi/audio +// +// Created by Zach Pomerantz on 6/14/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 QtGraphicalEffects 1.0 + +Rectangle { + readonly property var level: Audio.inputLevel; + + property bool standalone: false; + property var dragTarget: null; + + width: 240; + height: 50; + + radius: 5; + + color: "#00000000"; + border { + width: (standalone || Audio.muted || mouseArea.containsMouse) ? 2 : 0; + color: colors.border; + } + + // borders are painted over fill, so reduce the fill to fit inside the border + Rectangle { + color: standalone ? colors.fill : "#00000000"; + width: 236; + height: 46; + + radius: 5; + + anchors { + verticalCenter: parent.verticalCenter; + horizontalCenter: parent.horizontalCenter; + } + } + + MouseArea { + id: mouseArea; + + anchors { + left: icon.left; + right: bar.right; + top: icon.top; + bottom: icon.bottom; + } + + hoverEnabled: true; + scrollGestureEnabled: false; + onClicked: { Audio.muted = !Audio.muted; } + drag.target: dragTarget; + } + + Item { + id: colors; + + readonly property string unmuted: "#FFF"; + readonly property string muted: "#E2334D"; + readonly property string gutter: "#575757"; + readonly property string greenStart: "#39A38F"; + readonly property string greenEnd: "#1FC6A6"; + readonly property string red: colors.muted; + readonly property string fill: "#55000000"; + readonly property string border: standalone ? "#80FFFFFF" : "#55FFFFFF"; + readonly property string icon: (Audio.muted && !mouseArea.containsMouse) ? muted : unmuted; + } + + Item { + id: icon; + + anchors { + left: parent.left; + leftMargin: 5; + verticalCenter: parent.verticalCenter; + } + + width: 40; + height: 40; + + Item { + Image { + readonly property string unmutedIcon: "../../../icons/tablet-icons/mic-unmute-i.svg"; + readonly property string mutedIcon: "../../../icons/tablet-icons/mic-mute-i.svg"; + + function exclusiveOr(a, b) { return (a || b) && !(a && b); } + + id: image; + source: exclusiveOr(Audio.muted, mouseArea.containsMouse) ? mutedIcon : unmutedIcon; + + width: 30; + height: 30; + anchors { + left: parent.left; + leftMargin: 5; + top: parent.top; + topMargin: 5; + } + } + + ColorOverlay { + anchors { fill: image } + source: image; + color: colors.icon; + } + } + } + + Item { + id: status; + + readonly property string color: (Audio.muted && !mouseArea.containsMouse) ? colors.muted : colors.unmuted; + + visible: Audio.muted || mouseArea.containsMouse; + + anchors { + left: parent.left; + leftMargin: 50; + verticalCenter: parent.verticalCenter; + } + + width: 170; + height: 8 + + Text { + anchors { + horizontalCenter: parent.horizontalCenter; + verticalCenter: parent.verticalCenter; + } + + color: parent.color; + + text: Audio.muted ? (mouseArea.containsMouse ? "UNMUTE" : "MUTED") : "MUTE"; + font.pointSize: 12; + } + + Rectangle { + anchors { + left: parent.left; + verticalCenter: parent.verticalCenter; + } + + width: 50; + height: 4; + color: parent.color; + } + + Rectangle { + anchors { + right: parent.right; + verticalCenter: parent.verticalCenter; + } + + width: 50; + height: 4; + color: parent.color; + } + } + + Item { + id: bar; + + visible: !status.visible; + + anchors.fill: status; + + width: status.width; + + Rectangle { // base + radius: 4; + anchors { fill: parent } + color: colors.gutter; + } + + Rectangle { // mask + id: mask; + width: parent.width * level; + radius: 5; + anchors { + bottom: parent.bottom; + bottomMargin: 0; + top: parent.top; + topMargin: 0; + left: parent.left; + leftMargin: 0; + } + } + + LinearGradient { + anchors { fill: mask } + source: mask + start: Qt.point(0, 0); + end: Qt.point(170, 0); + gradient: Gradient { + GradientStop { + position: 0; + color: colors.greenStart; + } + GradientStop { + position: 0.8; + color: colors.greenEnd; + } + GradientStop { + position: 0.81; + color: colors.red; + } + GradientStop { + position: 1; + color: colors.red; + } + } + } + } +} diff --git a/interface/resources/qml/hifi/audio/PlaySampleSound.qml b/interface/resources/qml/hifi/audio/PlaySampleSound.qml new file mode 100644 index 0000000000..99f3648ec3 --- /dev/null +++ b/interface/resources/qml/hifi/audio/PlaySampleSound.qml @@ -0,0 +1,86 @@ +// +// PlaySampleSound.qml +// qml/hifi/audio +// +// Created by Zach Pomerantz on 6/13/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.Controls.Styles 1.4 +import QtQuick.Layouts 1.3 + +import "../../styles-uit" +import "../../controls-uit" as HifiControls + +RowLayout { + property var sound: null; + property var sample: null; + property bool isPlaying: false; + function createSampleSound() { + var SOUND = Qt.resolvedUrl("../../../sounds/sample.wav"); + sound = SoundCache.getSound(SOUND); + sample = null; + } + function playSound() { + // FIXME: MyAvatar is not properly exposed to QML; MyAvatar.qmlPosition is a stopgap + // FIXME: Audio.playSystemSound should not require position + sample = Audio.playSystemSound(sound, MyAvatar.qmlPosition); + isPlaying = true; + sample.finished.connect(function() { isPlaying = false; sample = null; }); + } + function stopSound() { + sample && sample.stop(); + } + + Component.onCompleted: createSampleSound(); + Component.onDestruction: stopSound(); + onVisibleChanged: { + if (!visible) { + stopSound(); + } + } + + HifiConstants { id: hifi; } + + Button { + style: ButtonStyle { + background: Rectangle { + implicitWidth: 20; + implicitHeight: 20; + radius: hifi.buttons.radius; + gradient: Gradient { + GradientStop { + position: 0.2; + color: isPlaying ? hifi.buttons.colorStart[hifi.buttons.blue] : hifi.buttons.colorStart[hifi.buttons.black]; + } + GradientStop { + position: 1.0; + color: isPlaying ? hifi.buttons.colorFinish[hifi.buttons.blue] : hifi.buttons.colorFinish[hifi.buttons.black]; + } + } + } + label: HiFiGlyphs { + // absolutely position due to asymmetry in glyph + x: isPlaying ? 0 : 1; + y: 1; + size: 14; + color: (control.pressed || control.hovered) ? (isPlaying ? "black" : hifi.colors.primaryHighlight) : "white"; + text: isPlaying ? hifi.glyphs.stop_square : hifi.glyphs.playback_play; + } + } + onClicked: isPlaying ? stopSound() : playSound(); + } + + RalewayRegular { + Layout.leftMargin: 2; + size: 14; + color: "white"; + text: isPlaying ? qsTr("Stop sample sound") : qsTr("Play sample sound"); + } + +} diff --git a/interface/resources/qml/hifi/dialogs/RunningScripts.qml b/interface/resources/qml/hifi/dialogs/RunningScripts.qml index 0514bfbffd..b8ba66b73f 100644 --- a/interface/resources/qml/hifi/dialogs/RunningScripts.qml +++ b/interface/resources/qml/hifi/dialogs/RunningScripts.qml @@ -51,7 +51,20 @@ ScrollingWindow { } function updateRunningScripts() { + function simplify(path) { + // trim URI querystring/fragment + path = (path+'').replace(/[#?].*$/,''); + // normalize separators and grab last path segment (ie: just the filename) + path = path.replace(/\\/g, '/').split('/').pop(); + // return lowercased because we want to sort mnemonically + return path.toLowerCase(); + } var runningScripts = ScriptDiscoveryService.getRunning(); + runningScripts.sort(function(a,b) { + a = simplify(a.path); + b = simplify(b.path); + return a < b ? -1 : a > b ? 1 : 0; + }); runningScriptsModel.clear() for (var i = 0; i < runningScripts.length; ++i) { runningScriptsModel.append(runningScripts[i]); diff --git a/interface/resources/qml/hifi/tablet/Tablet.qml b/interface/resources/qml/hifi/tablet/Tablet.qml index 25cbd02426..43672190dd 100644 --- a/interface/resources/qml/hifi/tablet/Tablet.qml +++ b/interface/resources/qml/hifi/tablet/Tablet.qml @@ -1,20 +1,16 @@ import QtQuick 2.5 import QtGraphicalEffects 1.0 + import "../../styles-uit" +import "../audio" as HifiAudio Item { id: tablet objectName: "tablet" - property double micLevel: 0.8 property int rowIndex: 0 property int columnIndex: 0 property int count: (flowMain.children.length - 1) - // called by C++ code to keep audio bar updated - function setMicLevel(newMicLevel) { - tablet.micLevel = newMicLevel; - } - // used to look up a button by its uuid function findButtonIndex(uuid) { if (!uuid) { @@ -83,6 +79,16 @@ Item { Rectangle { id: bgTopBar height: 90 + + anchors { + top: parent.top + topMargin: 0 + left: parent.left + leftMargin: 0 + right: parent.right + rightMargin: 0 + } + gradient: Gradient { GradientStop { position: 0 @@ -94,108 +100,13 @@ Item { color: "#1e1e1e" } } - anchors.right: parent.right - anchors.rightMargin: 0 - anchors.left: parent.left - anchors.leftMargin: 0 - anchors.topMargin: 0 - anchors.top: parent.top - Item { - id: audioIcon - anchors.verticalCenter: parent.verticalCenter - width: 40 - height: 40 - anchors.left: parent.left - anchors.leftMargin: 5 - - Image { - id: micIcon - source: "../../../icons/tablet-icons/mic.svg" - } - - Item { - visible: (Audio.muted && !toggleMuteMouseArea.containsMouse) - || (!Audio.muted && toggleMuteMouseArea.containsMouse) - - Image { - id: muteIcon - source: "../../../icons/tablet-icons/mic-mute.svg" - } - - ColorOverlay { - anchors.fill: muteIcon - source: muteIcon - color: toggleMuteMouseArea.containsMouse ? "#a0a0a0" : "#ff0000" - } - } - } - - Item { - id: audioBar - width: 170 - height: 10 - anchors.left: parent.left - anchors.leftMargin: 50 - anchors.verticalCenter: parent.verticalCenter - Rectangle { - id: audioBarBase - color: "#333333" - radius: 5 - anchors.fill: parent - } - Rectangle { - id: audioBarMask - width: parent.width * tablet.micLevel - color: "#333333" - radius: 5 - anchors.bottom: parent.bottom - anchors.bottomMargin: 0 - anchors.top: parent.top - anchors.topMargin: 0 - anchors.left: parent.left - anchors.leftMargin: 0 - } - LinearGradient { - anchors.fill: audioBarMask - source: audioBarMask - start: Qt.point(0, 0) - end: Qt.point(170, 0) - gradient: Gradient { - GradientStop { - position: 0 - color: "#2c8e72" - } - GradientStop { - position: 0.8 - color: "#1fc6a6" - } - GradientStop { - position: 0.81 - color: "#ea4c5f" - } - GradientStop { - position: 1 - color: "#ea4c5f" - } - } - } - } - - MouseArea { - id: toggleMuteMouseArea + HifiAudio.MicBar { anchors { - left: audioIcon.left - right: audioBar.right - top: audioIcon.top - bottom: audioIcon.bottom + left: parent.left + leftMargin: 30 + verticalCenter: parent.verticalCenter } - - hoverEnabled: true - preventStealing: true - propagateComposedEvents: false - scrollGestureEnabled: false - onClicked: { Audio.muted = !Audio.muted } } RalewaySemiBold { @@ -254,27 +165,6 @@ Item { } } - states: [ - State { - name: "muted state" - - PropertyChanges { - target: muteText - text: "UNMUTE" - } - - PropertyChanges { - target: muteIcon - visible: !Audio.muted - } - - PropertyChanges { - target: tablet - micLevel: 0 - } - } - ] - function setCurrentItemState(state) { var index = rowIndex + columnIndex; diff --git a/interface/resources/qml/styles-uit/HifiConstants.qml b/interface/resources/qml/styles-uit/HifiConstants.qml index 7b6efbd573..f321f49478 100644 --- a/interface/resources/qml/styles-uit/HifiConstants.qml +++ b/interface/resources/qml/styles-uit/HifiConstants.qml @@ -333,5 +333,7 @@ Item { readonly property string vol_x_2: "\ue015" readonly property string vol_x_3: "\ue016" readonly property string vol_x_4: "\ue017" + readonly property string playback_play: "\ue01d" + readonly property string stop_square: "\ue01e" } } diff --git a/interface/resources/sounds/sample.wav b/interface/resources/sounds/sample.wav new file mode 100644 index 0000000000..d461ab9186 Binary files /dev/null and b/interface/resources/sounds/sample.wav differ diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 7577b35bca..9b8358fb46 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -88,6 +88,7 @@ #include #include #include "LocationBookmarks.h" +#include #include #include #include @@ -115,6 +116,7 @@ #include #include #include +#include #include #include #include @@ -159,7 +161,6 @@ #include "scripting/DesktopScriptingInterface.h" #include "scripting/GlobalServicesScriptingInterface.h" #include "scripting/HMDScriptingInterface.h" -#include "scripting/LocationScriptingInterface.h" #include "scripting/MenuScriptingInterface.h" #include "scripting/SettingsScriptingInterface.h" #include "scripting/WindowScriptingInterface.h" @@ -580,7 +581,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), @@ -955,7 +955,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(); @@ -1449,7 +1449,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); @@ -1644,7 +1643,7 @@ void Application::updateHeartbeat() const { static_cast(_deadlockWatchdogThread)->updateHeartbeat(); } -void Application::aboutToQuit() { +void Application::onAboutToQuit() { emit beforeAboutToQuit(); foreach(auto inputPlugin, PluginManager::getInstance()->getInputPlugins()) { @@ -1875,6 +1874,7 @@ void Application::initializeGL() { render::CullFunctor cullFunctor = LODManager::shouldRender; static const QString RENDER_FORWARD = "HIFI_RENDER_FORWARD"; bool isDeferred = !QProcessEnvironment::systemEnvironment().contains(RENDER_FORWARD); + _renderEngine->addJob("SecondaryCameraFrame", cullFunctor); _renderEngine->addJob("RenderMainView", cullFunctor, isDeferred); _renderEngine->load(); _renderEngine->registerScene(_main3DScene); @@ -3988,11 +3988,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); @@ -4022,13 +4017,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(); @@ -4132,7 +4120,7 @@ void Application::updateMyAvatarLookAtPosition() { lookAtSpot = transformPoint(worldHeadMat, glm::vec3(0.0f, 0.0f, TREE_SCALE)); } else { lookAtSpot = myAvatar->getHead()->getEyePosition() + - (myAvatar->getHead()->getFinalOrientationInWorldFrame() * glm::vec3(0.0f, 0.0f, -TREE_SCALE)); + (myAvatar->getHead()->getFinalOrientationInWorldFrame() * glm::vec3(0.0f, 0.0f, TREE_SCALE)); } } @@ -5021,9 +5009,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; @@ -5094,7 +5079,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()"); @@ -5167,8 +5151,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) { @@ -5587,6 +5569,7 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri connect(scriptEngine, &ScriptEngine::errorMessage, DependencyManager::get().data(), &ScriptEngines::onErrorMessage); connect(scriptEngine, &ScriptEngine::warningMessage, DependencyManager::get().data(), &ScriptEngines::onWarningMessage); connect(scriptEngine, &ScriptEngine::infoMessage, DependencyManager::get().data(), &ScriptEngines::onInfoMessage); + connect(scriptEngine, &ScriptEngine::clearDebugWindow, DependencyManager::get().data(), &ScriptEngines::onClearDebugWindow); } bool Application::canAcceptURL(const QString& urlString) const { diff --git a/interface/src/Application.h b/interface/src/Application.h index 9db492b177..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; @@ -399,7 +398,7 @@ private slots: void showDesktop(); void clearDomainOctreeDetails(); void clearDomainAvatars(); - void aboutToQuit(); + void onAboutToQuit(); void resettingDomain(); @@ -532,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/SecondaryCamera.cpp b/interface/src/SecondaryCamera.cpp new file mode 100644 index 0000000000..f6ee8caa61 --- /dev/null +++ b/interface/src/SecondaryCamera.cpp @@ -0,0 +1,125 @@ +// +// SecondaryCamera.cpp +// interface/src +// +// Created by Samuel Gateau, Howard Stearns, and Zach Fox on 2017-06-08. +// Copyright 2013 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 "SecondaryCamera.h" +#include +#include + +using RenderArgsPointer = std::shared_ptr; + +void MainRenderTask::build(JobModel& task, const render::Varying& inputs, render::Varying& outputs, render::CullFunctor cullFunctor, bool isDeferred) { + + task.addJob("RenderShadowTask", cullFunctor); + const auto items = task.addJob("FetchCullSort", cullFunctor); + assert(items.canCast()); + if (!isDeferred) { + task.addJob("Forward", items); + } else { + task.addJob("RenderDeferredTask", items); + } +} + +void SecondaryCameraRenderTaskConfig::resetSize(int width, int height) { // FIXME: Add an arg here for "destinationFramebuffer" + bool wasEnabled = isEnabled(); + setEnabled(false); + auto textureCache = DependencyManager::get(); + textureCache->resetSpectatorCameraFramebuffer(width, height); // FIXME: Call the correct reset function based on the "destinationFramebuffer" arg + setEnabled(wasEnabled); +} + +void SecondaryCameraRenderTaskConfig::resetSizeSpectatorCamera(int width, int height) { // Carefully adjust the framebuffer / texture. + resetSize(width, height); +} + +class BeginSecondaryCameraFrame { // Changes renderContext for our framebuffer and and view. + glm::vec3 _position{}; + glm::quat _orientation{}; + float _vFoV{}; + float _nearClipPlaneDistance{}; + float _farClipPlaneDistance{}; +public: + using Config = BeginSecondaryCameraFrameConfig; + using JobModel = render::Job::ModelO; + BeginSecondaryCameraFrame() { + _cachedArgsPointer = std::make_shared(_cachedArgs); + } + + void configure(const Config& config) { + if (config.enabled || config.alwaysEnabled) { + _position = config.position; + _orientation = config.orientation; + _vFoV = config.vFoV; + _nearClipPlaneDistance = config.nearClipPlaneDistance; + _farClipPlaneDistance = config.farClipPlaneDistance; + } + } + + void run(const render::RenderContextPointer& renderContext, RenderArgsPointer& cachedArgs) { + auto args = renderContext->args; + auto textureCache = DependencyManager::get(); + gpu::FramebufferPointer destFramebuffer; + destFramebuffer = textureCache->getSpectatorCameraFramebuffer(); // FIXME: Change the destination based on some unimplemented config var + if (destFramebuffer) { + _cachedArgsPointer->_blitFramebuffer = args->_blitFramebuffer; + _cachedArgsPointer->_viewport = args->_viewport; + _cachedArgsPointer->_displayMode = args->_displayMode; + _cachedArgsPointer->_renderMode = args->_renderMode; + args->_blitFramebuffer = destFramebuffer; + args->_viewport = glm::ivec4(0, 0, destFramebuffer->getWidth(), destFramebuffer->getHeight()); + args->_displayMode = RenderArgs::MONO; + args->_renderMode = RenderArgs::RenderMode::SECONDARY_CAMERA_RENDER_MODE; + + gpu::doInBatch(args->_context, [&](gpu::Batch& batch) { + batch.disableContextStereo(); + }); + + auto srcViewFrustum = args->getViewFrustum(); + srcViewFrustum.setPosition(_position); + srcViewFrustum.setOrientation(_orientation); + srcViewFrustum.setProjection(glm::perspective(glm::radians(_vFoV), ((float)args->_viewport.z / (float)args->_viewport.w), _nearClipPlaneDistance, _farClipPlaneDistance)); + // Without calculating the bound planes, the secondary camera will use the same culling frustum as the main camera, + // which is not what we want here. + srcViewFrustum.calculate(); + args->pushViewFrustum(srcViewFrustum); + cachedArgs = _cachedArgsPointer; + } + } + +protected: + RenderArgs _cachedArgs; + RenderArgsPointer _cachedArgsPointer; +}; + +class EndSecondaryCameraFrame { // Restores renderContext. +public: + using JobModel = render::Job::ModelI; + + void run(const render::RenderContextPointer& renderContext, const RenderArgsPointer& cachedArgs) { + auto args = renderContext->args; + args->_blitFramebuffer = cachedArgs->_blitFramebuffer; + args->_viewport = cachedArgs->_viewport; + args->popViewFrustum(); + args->_displayMode = cachedArgs->_displayMode; + args->_renderMode = cachedArgs->_renderMode; + + gpu::doInBatch(args->_context, [&](gpu::Batch& batch) { + batch.restoreContextStereo(); + }); + } +}; + +void SecondaryCameraRenderTask::build(JobModel& task, const render::Varying& inputs, render::Varying& outputs, render::CullFunctor cullFunctor) { + const auto cachedArg = task.addJob("BeginSecondaryCamera"); + const auto items = task.addJob("FetchCullSort", cullFunctor); + assert(items.canCast()); + task.addJob("RenderDeferredTask", items); + task.addJob("EndSecondaryCamera", cachedArg); +} \ No newline at end of file diff --git a/interface/src/SecondaryCamera.h b/interface/src/SecondaryCamera.h new file mode 100644 index 0000000000..5ad19c9614 --- /dev/null +++ b/interface/src/SecondaryCamera.h @@ -0,0 +1,70 @@ +// +// SecondaryCamera.h +// interface/src +// +// Created by Samuel Gateau, Howard Stearns, and Zach Fox on 2017-06-08. +// Copyright 2013 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_SecondaryCamera_h +#define hifi_SecondaryCamera_h + +#include +#include +#include +#include + + +class MainRenderTask { +public: + using JobModel = render::Task::Model; + + MainRenderTask() {} + + void build(JobModel& task, const render::Varying& inputs, render::Varying& outputs, render::CullFunctor cullFunctor, bool isDeferred = true); +}; + +class BeginSecondaryCameraFrameConfig : public render::Task::Config { // Exposes secondary camera parameters to JavaScript. + Q_OBJECT + Q_PROPERTY(glm::vec3 position MEMBER position NOTIFY dirty) // of viewpoint to render from + Q_PROPERTY(glm::quat orientation MEMBER orientation NOTIFY dirty) // of viewpoint to render from + Q_PROPERTY(float vFoV MEMBER vFoV NOTIFY dirty) // Secondary camera's vertical field of view. In degrees. + Q_PROPERTY(float nearClipPlaneDistance MEMBER nearClipPlaneDistance NOTIFY dirty) // Secondary camera's near clip plane distance. In meters. + Q_PROPERTY(float farClipPlaneDistance MEMBER farClipPlaneDistance NOTIFY dirty) // Secondary camera's far clip plane distance. In meters. +public: + glm::vec3 position{}; + glm::quat orientation{}; + float vFoV{ 45.0f }; + float nearClipPlaneDistance{ 0.1f }; + float farClipPlaneDistance{ 100.0f }; + BeginSecondaryCameraFrameConfig() : render::Task::Config(false) {} +signals: + void dirty(); +}; + +class SecondaryCameraRenderTaskConfig : public render::Task::Config { + Q_OBJECT +public: + SecondaryCameraRenderTaskConfig() : render::Task::Config(false) {} +private: + void resetSize(int width, int height); +signals: + void dirty(); +public slots: + void resetSizeSpectatorCamera(int width, int height); +}; + +class SecondaryCameraRenderTask { +public: + using Config = SecondaryCameraRenderTaskConfig; + using JobModel = render::Task::Model; + SecondaryCameraRenderTask() {} + void configure(const Config& config) {} + void build(JobModel& task, const render::Varying& inputs, render::Varying& outputs, render::CullFunctor cullFunctor); +}; + +#endif 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 1adcfbd345..e96f626082 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -87,9 +87,10 @@ const float MyAvatar::ZOOM_MAX = 25.0f; const float MyAvatar::ZOOM_DEFAULT = 1.5f; // default values, used when avatar is missing joints... (avatar space) -// static const glm::quat DEFAULT_AVATAR_MIDDLE_EYE_ROT { Quaternions::Y_180 }; +static const glm::quat DEFAULT_AVATAR_MIDDLE_EYE_ROT { Quaternions::Y_180 }; static const glm::vec3 DEFAULT_AVATAR_MIDDLE_EYE_POS { 0.0f, 0.6f, 0.0f }; static const glm::vec3 DEFAULT_AVATAR_HEAD_POS { 0.0f, 0.53f, 0.0f }; +static const glm::quat DEFAULT_AVATAR_HEAD_ROT { Quaternions::Y_180 }; static const glm::vec3 DEFAULT_AVATAR_RIGHTARM_POS { -0.134824f, 0.396348f, -0.0515777f }; static const glm::quat DEFAULT_AVATAR_RIGHTARM_ROT { -0.536241f, 0.536241f, -0.460918f, -0.460918f }; static const glm::vec3 DEFAULT_AVATAR_LEFTARM_POS { 0.134795f, 0.396349f, -0.0515881f }; @@ -100,7 +101,9 @@ static const glm::vec3 DEFAULT_AVATAR_LEFTHAND_POS { 0.727588f, 0.39635f, -0.051 static const glm::quat DEFAULT_AVATAR_LEFTHAND_ROT { -0.479181f, -0.52001f, 0.52254f, -0.476369f }; static const glm::vec3 DEFAULT_AVATAR_NECK_POS { 0.0f, 0.445f, 0.025f }; static const glm::vec3 DEFAULT_AVATAR_SPINE2_POS { 0.0f, 0.32f, 0.02f }; +static const glm::quat DEFAULT_AVATAR_SPINE2_ROT { Quaternions::Y_180 }; static const glm::vec3 DEFAULT_AVATAR_HIPS_POS { 0.0f, 0.0f, 0.0f }; +static const glm::quat DEFAULT_AVATAR_HIPS_ROT { Quaternions::Y_180 }; static const glm::vec3 DEFAULT_AVATAR_LEFTFOOT_POS { -0.08f, -0.96f, 0.029f}; static const glm::quat DEFAULT_AVATAR_LEFTFOOT_ROT { -0.40167322754859924f, 0.9154590368270874f, -0.005437685176730156f, -0.023744143545627594f }; static const glm::vec3 DEFAULT_AVATAR_RIGHTFOOT_POS { 0.08f, -0.96f, 0.029f }; @@ -1881,15 +1884,14 @@ void MyAvatar::preDisplaySide(RenderArgs* renderArgs) { const float RENDER_HEAD_CUTOFF_DISTANCE = 0.3f; -bool MyAvatar::cameraInsideHead() const { - const glm::vec3 cameraPosition = qApp->getCamera().getPosition(); +bool MyAvatar::cameraInsideHead(const glm::vec3& cameraPosition) const { return glm::length(cameraPosition - getHeadPosition()) < (RENDER_HEAD_CUTOFF_DISTANCE * getUniformScale()); } bool MyAvatar::shouldRenderHead(const RenderArgs* renderArgs) const { bool defaultMode = renderArgs->_renderMode == RenderArgs::DEFAULT_RENDER_MODE; bool firstPerson = qApp->getCamera().getMode() == CAMERA_MODE_FIRST_PERSON; - bool insideHead = cameraInsideHead(); + bool insideHead = cameraInsideHead(renderArgs->getViewFrustum().getPosition()); return !defaultMode || !firstPerson || !insideHead; } @@ -2765,7 +2767,7 @@ void MyAvatar::FollowHelper::prePhysicsUpdate(MyAvatar& myAvatar, const glm::mat const glm::mat4& currentBodyMatrix, bool hasDriveInput) { if (myAvatar.getHMDLeanRecenterEnabled()) { - if (!isActive(Rotation) && shouldActivateRotation(myAvatar, desiredBodyMatrix, currentBodyMatrix)) { + if (!isActive(Rotation) && (shouldActivateRotation(myAvatar, desiredBodyMatrix, currentBodyMatrix) || hasDriveInput)) { activate(Rotation); } if (!isActive(Horizontal) && shouldActivateHorizontal(myAvatar, desiredBodyMatrix, currentBodyMatrix)) { @@ -2967,7 +2969,7 @@ glm::mat4 MyAvatar::getCenterEyeCalibrationMat() const { auto centerEyeRot = Quaternions::Y_180; return createMatFromQuatAndPos(centerEyeRot, centerEyePos); } else { - return createMatFromQuatAndPos(DEFAULT_AVATAR_MIDDLE_EYE_POS, DEFAULT_AVATAR_MIDDLE_EYE_POS); + return createMatFromQuatAndPos(DEFAULT_AVATAR_MIDDLE_EYE_ROT, DEFAULT_AVATAR_MIDDLE_EYE_POS); } } @@ -2979,7 +2981,7 @@ glm::mat4 MyAvatar::getHeadCalibrationMat() const { auto headRot = getAbsoluteDefaultJointRotationInObjectFrame(headIndex); return createMatFromQuatAndPos(headRot, headPos); } else { - return createMatFromQuatAndPos(DEFAULT_AVATAR_HEAD_POS, DEFAULT_AVATAR_HEAD_POS); + return createMatFromQuatAndPos(DEFAULT_AVATAR_HEAD_ROT, DEFAULT_AVATAR_HEAD_POS); } } @@ -2991,7 +2993,7 @@ glm::mat4 MyAvatar::getSpine2CalibrationMat() const { auto spine2Rot = getAbsoluteDefaultJointRotationInObjectFrame(spine2Index); return createMatFromQuatAndPos(spine2Rot, spine2Pos); } else { - return createMatFromQuatAndPos(DEFAULT_AVATAR_SPINE2_POS, DEFAULT_AVATAR_SPINE2_POS); + return createMatFromQuatAndPos(DEFAULT_AVATAR_SPINE2_ROT, DEFAULT_AVATAR_SPINE2_POS); } } @@ -3003,7 +3005,7 @@ glm::mat4 MyAvatar::getHipsCalibrationMat() const { auto hipsRot = getAbsoluteDefaultJointRotationInObjectFrame(hipsIndex); return createMatFromQuatAndPos(hipsRot, hipsPos); } else { - return createMatFromQuatAndPos(DEFAULT_AVATAR_HIPS_POS, DEFAULT_AVATAR_HIPS_POS); + return createMatFromQuatAndPos(DEFAULT_AVATAR_HIPS_ROT, DEFAULT_AVATAR_HIPS_POS); } } diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index f61f24fb11..473bcb0de2 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -56,6 +56,7 @@ class MyAvatar : public Avatar { * * @namespace MyAvatar * @augments Avatar + * @property qmlPosition {Vec3} Used as a stopgap for position access by QML, as glm::vec3 is unavailable outside of scripts * @property shouldRenderLocally {bool} Set it to true if you would like to see MyAvatar in your local interface, * and false if you would not like to see MyAvatar in your local interface. * @property motorVelocity {Vec3} Can be used to move the avatar with this velocity. @@ -101,6 +102,10 @@ class MyAvatar : public Avatar { * "scripts/system/controllers/toggleAdvancedMovementForHandControllers.js". */ + // FIXME: `glm::vec3 position` is not accessible from QML, so this exposes position in a QML-native type + Q_PROPERTY(QVector3D qmlPosition READ getQmlPosition) + QVector3D getQmlPosition() { auto p = getPosition(); return QVector3D(p.x, p.y, p.z); } + Q_PROPERTY(bool shouldRenderLocally READ getShouldRenderLocally WRITE setShouldRenderLocally) Q_PROPERTY(glm::vec3 motorVelocity READ getScriptedMotorVelocity WRITE setScriptedMotorVelocity) Q_PROPERTY(float motorTimescale READ getScriptedMotorTimescale WRITE setScriptedMotorTimescale) @@ -615,7 +620,7 @@ private: float scale = 1.0f, bool isSoft = false, bool allowDuplicates = false, bool useSaved = true) override; - bool cameraInsideHead() const; + bool cameraInsideHead(const glm::vec3& cameraPosition) const; void updateEyeContactTarget(float deltaTime); diff --git a/interface/src/scripting/Audio.cpp b/interface/src/scripting/Audio.cpp index 0b99e60c89..4576190413 100644 --- a/interface/src/scripting/Audio.cpp +++ b/interface/src/scripting/Audio.cpp @@ -23,9 +23,33 @@ QString Audio::HMD { "VR" }; Setting::Handle enableNoiseReductionSetting { QStringList { Audio::AUDIO, "NoiseReduction" }, true }; +float Audio::loudnessToLevel(float loudness) { + const float LOG2 = log(2.0f); + const float METER_LOUDNESS_SCALE = 2.8f / 5.0f; + const float LOG2_LOUDNESS_FLOOR = 11.0f; + + float level = 0.0f; + + loudness += 1.0f; + float log2loudness = logf(loudness) / LOG2; + + if (log2loudness <= LOG2_LOUDNESS_FLOOR) { + level = (log2loudness / LOG2_LOUDNESS_FLOOR) * METER_LOUDNESS_SCALE; + } else { + level = (log2loudness - (LOG2_LOUDNESS_FLOOR - 1.0f)) * METER_LOUDNESS_SCALE; + } + + if (level > 1.0f) { + level = 1.0; + } + + return level; +} + Audio::Audio() : _devices(_contextIsHMD) { - auto client = DependencyManager::get(); - connect(client.data(), &AudioClient::muteToggled, this, &Audio::onMutedChanged); + auto client = DependencyManager::get().data(); + connect(client, &AudioClient::muteToggled, this, &Audio::onMutedChanged); + connect(client, &AudioClient::inputLoudnessChanged, this, &Audio::onInputLoudnessChanged); connect(this, &Audio::contextChanged, &_devices, &AudioDevices::onContextChanged); connect(&_devices._inputs, &AudioDeviceList::deviceChanged, this, &Audio::onInputChanged); enableNoiseReduction(enableNoiseReductionSetting.get()); @@ -88,6 +112,15 @@ void Audio::onInputChanged() { } } +void Audio::onInputLoudnessChanged(float loudness) { + float level = loudnessToLevel(loudness); + + if (_inputLevel != level) { + _inputLevel = level; + emit inputLevelChanged(_inputLevel); + } +} + QString Audio::getContext() const { return _contextIsHMD ? Audio::HMD : Audio::DESKTOP; } diff --git a/interface/src/scripting/Audio.h b/interface/src/scripting/Audio.h index d438df41ca..953727ede8 100644 --- a/interface/src/scripting/Audio.h +++ b/interface/src/scripting/Audio.h @@ -26,6 +26,7 @@ class Audio : public AudioScriptingInterface { 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(float inputLevel READ getInputLevel NOTIFY inputLevelChanged) Q_PROPERTY(QString context READ getContext NOTIFY contextChanged) Q_PROPERTY(AudioDevices* devices READ getDevices NOTIFY nop) @@ -34,11 +35,14 @@ public: static QString HMD; static QString DESKTOP; + static float loudnessToLevel(float loudness); + virtual ~Audio() {} bool isMuted() const { return _isMuted; } bool noiseReductionEnabled() const { return _enableNoiseReduction; } float getInputVolume() const { return _inputVolume; } + float getInputLevel() const { return _inputLevel; } QString getContext() const; void setMuted(bool muted); @@ -54,12 +58,14 @@ signals: void mutedChanged(bool isMuted); void noiseReductionChanged(bool isEnabled); void inputVolumeChanged(float volume); + void inputLevelChanged(float level); void contextChanged(const QString& context); public slots: void onMutedChanged(); void onContextChanged(); void onInputChanged(); + void onInputLoudnessChanged(float loudness); protected: // Audio must live on a separate thread from AudioClient to avoid deadlocks @@ -68,6 +74,7 @@ protected: private: float _inputVolume { 1.0f }; + float _inputLevel { 0.0f }; bool _isMuted { false }; bool _enableNoiseReduction; bool _contextIsHMD { false }; 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/scripting/WindowScriptingInterface.cpp b/interface/src/scripting/WindowScriptingInterface.cpp index 1e14c24da3..38f467f22b 100644 --- a/interface/src/scripting/WindowScriptingInterface.cpp +++ b/interface/src/scripting/WindowScriptingInterface.cpp @@ -284,6 +284,11 @@ void WindowScriptingInterface::copyToClipboard(const QString& text) { QApplication::clipboard()->setText(text); } + +bool WindowScriptingInterface::setDisplayTexture(const QString& name) { + return qApp->getActiveDisplayPlugin()->setDisplayTexture(name); // Plugins that don't know how, answer false. +} + void WindowScriptingInterface::takeSnapshot(bool notify, bool includeAnimated, float aspectRatio) { qApp->takeSnapshot(notify, includeAnimated, aspectRatio); } diff --git a/interface/src/scripting/WindowScriptingInterface.h b/interface/src/scripting/WindowScriptingInterface.h index 2b1e48d918..4fb4829636 100644 --- a/interface/src/scripting/WindowScriptingInterface.h +++ b/interface/src/scripting/WindowScriptingInterface.h @@ -62,6 +62,7 @@ public slots: void displayAnnouncement(const QString& message); void shareSnapshot(const QString& path, const QUrl& href = QUrl("")); bool isPhysicsEnabled(); + bool setDisplayTexture(const QString& name); int openMessageBox(QString title, QString text, int buttons, int defaultButton); void updateMessageBox(int id, QString title, QString text, int buttons, int defaultButton); diff --git a/interface/src/ui/AvatarInputs.cpp b/interface/src/ui/AvatarInputs.cpp index 7a46aded77..ccac6d7207 100644 --- a/interface/src/ui/AvatarInputs.cpp +++ b/interface/src/ui/AvatarInputs.cpp @@ -45,15 +45,6 @@ AvatarInputs::AvatarInputs(QQuickItem* parent) : QQuickItem(parent) { } \ } -#define AI_UPDATE_FLOAT(name, src, epsilon) \ - { \ - float val = src; \ - if (fabsf(_##name - val) >= epsilon) { \ - _##name = val; \ - emit name##Changed(); \ - } \ - } - float AvatarInputs::loudnessToAudioLevel(float loudness) { const float AUDIO_METER_AVERAGING = 0.5; const float LOG2 = log(2.0f); @@ -85,27 +76,6 @@ void AvatarInputs::update() { AI_UPDATE(cameraEnabled, !Menu::getInstance()->isOptionChecked(MenuOption::NoFaceTracking)); AI_UPDATE(cameraMuted, Menu::getInstance()->isOptionChecked(MenuOption::MuteFaceTracking)); AI_UPDATE(isHMD, qApp->isHMDMode()); - - auto audioIO = DependencyManager::get(); - - const float audioLevel = loudnessToAudioLevel(DependencyManager::get()->getLastInputLoudness()); - - AI_UPDATE_FLOAT(audioLevel, audioLevel, 0.01f); - AI_UPDATE(audioClipping, ((audioIO->getTimeSinceLastClip() > 0.0f) && (audioIO->getTimeSinceLastClip() < 1.0f))); - AI_UPDATE(audioMuted, audioIO->isMuted()); - - //// Make muted icon pulsate - //static const float PULSE_MIN = 0.4f; - //static const float PULSE_MAX = 1.0f; - //static const float PULSE_FREQUENCY = 1.0f; // in Hz - //qint64 now = usecTimestampNow(); - //if (now - _iconPulseTimeReference > (qint64)USECS_PER_SECOND) { - // // Prevents t from getting too big, which would diminish glm::cos precision - // _iconPulseTimeReference = now - ((now - _iconPulseTimeReference) % USECS_PER_SECOND); - //} - //float t = (float)(now - _iconPulseTimeReference) / (float)USECS_PER_SECOND; - //float pulseFactor = (glm::cos(t * PULSE_FREQUENCY * 2.0f * PI) + 1.0f) / 2.0f; - //iconColor = PULSE_MIN + (PULSE_MAX - PULSE_MIN) * pulseFactor; } void AvatarInputs::setShowAudioTools(bool showAudioTools) { @@ -124,10 +94,6 @@ void AvatarInputs::toggleCameraMute() { } } -void AvatarInputs::toggleAudioMute() { - DependencyManager::get()->toggleMute(); -} - void AvatarInputs::resetSensors() { qApp->resetSensors(); } diff --git a/interface/src/ui/AvatarInputs.h b/interface/src/ui/AvatarInputs.h index 247cc54995..fb48df9d73 100644 --- a/interface/src/ui/AvatarInputs.h +++ b/interface/src/ui/AvatarInputs.h @@ -25,9 +25,6 @@ class AvatarInputs : public QQuickItem { AI_PROPERTY(bool, cameraEnabled, false) AI_PROPERTY(bool, cameraMuted, false) - AI_PROPERTY(bool, audioMuted, false) - AI_PROPERTY(bool, audioClipping, false) - AI_PROPERTY(float, audioLevel, 0) AI_PROPERTY(bool, isHMD, false) Q_PROPERTY(bool showAudioTools READ showAudioTools WRITE setShowAudioTools NOTIFY showAudioToolsChanged) @@ -45,16 +42,12 @@ public slots: signals: void cameraEnabledChanged(); void cameraMutedChanged(); - void audioMutedChanged(); - void audioClippingChanged(); - void audioLevelChanged(); void isHMDChanged(); void showAudioToolsChanged(bool show); protected: Q_INVOKABLE void resetSensors(); Q_INVOKABLE void toggleCameraMute(); - Q_INVOKABLE void toggleAudioMute(); private: float _trailingAudioLoudness{ 0 }; 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 4f939b6046..a532e9d8fd 100644 --- a/interface/src/ui/overlays/Web3DOverlay.cpp +++ b/interface/src/ui/overlays/Web3DOverlay.cpp @@ -51,6 +51,7 @@ #include "avatar/AvatarManager.h" #include "scripting/GlobalServicesScriptingInterface.h" #include "ui/Snapshot.h" +#include "SoundCache.h" static const float DPI = 30.47f; static const float INCHES_TO_METERS = 1.0f / 39.3701f; @@ -199,6 +200,7 @@ void Web3DOverlay::loadSourceURL() { _webSurface->getSurfaceContext()->setContextProperty("GlobalServices", GlobalServicesScriptingInterface::getInstance()); _webSurface->getSurfaceContext()->setContextProperty("AvatarList", DependencyManager::get().data()); _webSurface->getSurfaceContext()->setContextProperty("DialogsManager", DialogsManagerScriptingInterface::getInstance()); + _webSurface->getSurfaceContext()->setContextProperty("SoundCache", DependencyManager::get().data()); _webSurface->getSurfaceContext()->setContextProperty("pathToFonts", "../../"); tabletScriptingInterface->setQmlTabletRoot("com.highfidelity.interface.tablet.system", _webSurface->getRootItem(), _webSurface.data()); @@ -318,7 +320,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() { @@ -440,17 +442,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/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 f32d37562a..fc54a04a5e 100644 --- a/libraries/audio-client/src/AudioClient.cpp +++ b/libraries/audio-client/src/AudioClient.cpp @@ -1023,6 +1023,8 @@ void AudioClient::handleAudioInput(QByteArray& audioBuffer) { emit inputReceived(audioBuffer); } + emit inputLoudnessChanged(_lastInputLoudness); + // state machine to detect gate opening and closing bool audioGateOpen = (_lastInputLoudness != 0.0f); bool openedInLastBlock = !_audioGateOpen && audioGateOpen; // the gate just opened diff --git a/libraries/audio-client/src/AudioClient.h b/libraries/audio-client/src/AudioClient.h index 01241938d9..54ce3aa6c2 100644 --- a/libraries/audio-client/src/AudioClient.h +++ b/libraries/audio-client/src/AudioClient.h @@ -210,6 +210,7 @@ signals: bool muteToggled(); void mutedByMixer(); void inputReceived(const QByteArray& inputSamples); + void inputLoudnessChanged(float loudness); void outputBytesToNetwork(int numBytes); void inputBytesFromNetwork(int numBytes); void noiseGateOpened(); 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-renderer/src/avatars-renderer/Avatar.cpp b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp index d78287a0e7..a0a348388e 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp @@ -288,6 +288,13 @@ void Avatar::updateAvatarEntities() { properties.setScript(noScript); } + // When grabbing avatar entities, they are parented to the joint moving them, then when un-grabbed + // they go back to the default parent (null uuid). When un-gripped, others saw the entity disappear. + // The thinking here is the local position was noticed as changing, but not the parentID (since it is now + // back to the default), and the entity flew off somewhere. Marking all changed definitely fixes this, + // and seems safe (per Seth). + properties.markAllChanged(); + // try to build the entity EntityItemPointer entity = entityTree->findEntityByEntityItemID(EntityItemID(entityID)); bool success = true; @@ -1067,15 +1074,15 @@ void Avatar::setModelURLFinished(bool success) { const int MAX_SKELETON_DOWNLOAD_ATTEMPTS = 4; // NOTE: we don't want to be as generous as ResourceCache is, we only want 4 attempts if (_skeletonModel->getResourceDownloadAttemptsRemaining() <= 0 || _skeletonModel->getResourceDownloadAttempts() > MAX_SKELETON_DOWNLOAD_ATTEMPTS) { - qCWarning(avatars_renderer) << "Using default after failing to load Avatar model: " << _skeletonModelURL + qCWarning(avatars_renderer) << "Using default after failing to load Avatar model: " << _skeletonModelURL << "after" << _skeletonModel->getResourceDownloadAttempts() << "attempts."; // call _skeletonModel.setURL, but leave our copy of _skeletonModelURL alone. This is so that // we don't redo this every time we receive an identity packet from the avatar with the bad url. QMetaObject::invokeMethod(_skeletonModel.get(), "setURL", Qt::QueuedConnection, Q_ARG(QUrl, AvatarData::defaultFullAvatarModelUrl())); } else { - qCWarning(avatars_renderer) << "Avatar model: " << _skeletonModelURL - << "failed to load... attempts:" << _skeletonModel->getResourceDownloadAttempts() + qCWarning(avatars_renderer) << "Avatar model: " << _skeletonModelURL + << "failed to load... attempts:" << _skeletonModel->getResourceDownloadAttempts() << "out of:" << MAX_SKELETON_DOWNLOAD_ATTEMPTS; } } 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/OpenGLDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp index bfd158ffb5..67bbb452ca 100644 --- a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp @@ -496,6 +496,17 @@ void OpenGLDisplayPlugin::submitFrame(const gpu::FramePointer& newFrame) { _newFrameQueue.push(newFrame); }); } +void OpenGLDisplayPlugin::renderFromTexture(gpu::Batch& batch, const gpu::TexturePointer texture, glm::ivec4 viewport, const glm::ivec4 scissor) { + batch.enableStereo(false); + batch.resetViewTransform(); + batch.setFramebuffer(gpu::FramebufferPointer()); + batch.clearColorFramebuffer(gpu::Framebuffer::BUFFER_COLOR0, vec4(0)); + batch.setStateScissorRect(scissor); + batch.setViewportTransform(viewport); + batch.setResourceTexture(0, texture); + batch.setPipeline(_presentPipeline); + batch.draw(gpu::TRIANGLE_STRIP, 4); +} void OpenGLDisplayPlugin::updateFrameData() { PROFILE_RANGE(render, __FUNCTION__) @@ -605,14 +616,11 @@ void OpenGLDisplayPlugin::compositeLayers() { void OpenGLDisplayPlugin::internalPresent() { render([&](gpu::Batch& batch) { - batch.enableStereo(false); - batch.resetViewTransform(); - batch.setFramebuffer(gpu::FramebufferPointer()); - batch.setViewportTransform(ivec4(uvec2(0), getSurfacePixels())); - batch.setResourceTexture(0, _compositeFramebuffer->getRenderBuffer(0)); - batch.setPipeline(_presentPipeline); - batch.draw(gpu::TRIANGLE_STRIP, 4); - }); + // Note: _displayTexture must currently be the same size as the display. + uvec2 dims = _displayTexture ? uvec2(_displayTexture->getDimensions()) : getSurfacePixels(); + auto viewport = ivec4(uvec2(0), dims); + renderFromTexture(batch, _displayTexture ? _displayTexture : _compositeFramebuffer->getRenderBuffer(0), viewport, viewport); + }); swapBuffers(); _presentRate.increment(); } @@ -694,6 +702,22 @@ void OpenGLDisplayPlugin::withMainThreadContext(std::function f) const { _container->makeRenderingContextCurrent(); } +bool OpenGLDisplayPlugin::setDisplayTexture(const QString& name) { + // Note: it is the caller's responsibility to keep the network texture in cache. + if (name.isEmpty()) { + _displayTexture.reset(); + onDisplayTextureReset(); + return true; + } + auto textureCache = DependencyManager::get(); + auto displayNetworkTexture = textureCache->getTexture(name); + if (!displayNetworkTexture) { + return false; + } + _displayTexture = displayNetworkTexture->getGPUTexture(); + return !!_displayTexture; +} + QImage OpenGLDisplayPlugin::getScreenshot(float aspectRatio) const { auto size = _compositeFramebuffer->getSize(); if (isHmd()) { diff --git a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h index 10a7558398..7e7889ff47 100644 --- a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h @@ -57,6 +57,8 @@ public: return getSurfaceSize(); } + virtual bool setDisplayTexture(const QString& name) override; + virtual bool onDisplayTextureReset() { return false; }; QImage getScreenshot(float aspectRatio = 0.0f) const override; float presentRate() const override; @@ -109,6 +111,7 @@ protected: // Plugin specific functionality to send the composed scene to the output window or device virtual void internalPresent(); + void renderFromTexture(gpu::Batch& batch, const gpu::TexturePointer texture, glm::ivec4 viewport, const glm::ivec4 scissor); virtual void updateFrameData(); void withMainThreadContext(std::function f) const; @@ -134,6 +137,7 @@ protected: gpu::PipelinePointer _simplePipeline; gpu::PipelinePointer _presentPipeline; gpu::PipelinePointer _cursorPipeline; + gpu::TexturePointer _displayTexture{}; float _compositeOverlayAlpha { 1.0f }; struct CursorData { 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/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp index 08c8d4f754..ea91890f33 100644 --- a/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp @@ -27,6 +27,7 @@ #include #include +#include #include #include "../Logging.h" @@ -211,7 +212,15 @@ void HmdDisplayPlugin::internalPresent() { // Composite together the scene, overlay and mouse cursor hmdPresent(); - if (!_disablePreview) { + if (_displayTexture) { + // Note: _displayTexture must currently be the same size as the display. + uvec2 dims = uvec2(_displayTexture->getDimensions()); + auto viewport = ivec4(uvec2(0), dims); + render([&](gpu::Batch& batch) { + renderFromTexture(batch, _displayTexture, viewport, viewport); + }); + swapBuffers(); + } else if (!_disablePreview) { // screen preview mirroring auto sourceSize = _renderTargetSize; if (_monoPreview) { @@ -278,16 +287,7 @@ void HmdDisplayPlugin::internalPresent() { viewport.z *= 2; } - - batch.enableStereo(false); - batch.resetViewTransform(); - batch.setFramebuffer(gpu::FramebufferPointer()); - batch.clearColorFramebuffer(gpu::Framebuffer::BUFFER_COLOR0, vec4(0)); - batch.setStateScissorRect(scissor); // was viewport - batch.setViewportTransform(viewport); - batch.setResourceTexture(0, _compositeFramebuffer->getRenderBuffer(0)); - batch.setPipeline(_presentPipeline); - batch.draw(gpu::TRIANGLE_STRIP, 4); + renderFromTexture(batch, _compositeFramebuffer->getRenderBuffer(0), viewport, scissor); }); swapBuffers(); } else if (_clearPreviewFlag) { @@ -316,15 +316,7 @@ void HmdDisplayPlugin::internalPresent() { auto viewport = getViewportForSourceSize(uvec2(_previewTexture->getDimensions())); render([&](gpu::Batch& batch) { - batch.enableStereo(false); - batch.resetViewTransform(); - batch.setFramebuffer(gpu::FramebufferPointer()); - batch.clearColorFramebuffer(gpu::Framebuffer::BUFFER_COLOR0, vec4(0)); - batch.setStateScissorRect(viewport); - batch.setViewportTransform(viewport); - batch.setResourceTexture(0, _previewTexture); - batch.setPipeline(_presentPipeline); - batch.draw(gpu::TRIANGLE_STRIP, 4); + renderFromTexture(batch, _previewTexture, viewport, viewport); }); _clearPreviewFlag = false; swapBuffers(); diff --git a/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.h index aaa6e347e0..055328ee21 100644 --- a/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.h @@ -46,6 +46,8 @@ public: float stutterRate() const override; + virtual bool onDisplayTextureReset() override { _clearPreviewFlag = true; return true; }; + protected: virtual void hmdPresent() = 0; virtual bool isHmdMounted() const = 0; 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..ffaac3bf3e 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); } @@ -240,8 +246,13 @@ bool EntityTreeRenderer::findBestZoneAndMaybeContainingEntities(QVectorgetVisible()) { - auto zone = std::dynamic_pointer_cast(entity); - _layeredZones.insert(zone); + auto renderID = std::dynamic_pointer_cast(entity)->getRenderItemID(); + bool isValidRenderID = (renderID != render::Item::INVALID_ITEM_ID); + + if (isValidRenderID) { + auto zone = std::dynamic_pointer_cast(entity); + _layeredZones.insert(zone); + } } } } @@ -348,6 +359,7 @@ bool EntityTreeRenderer::applyLayeredZones() { for (auto& zone : _layeredZones) { auto id = std::dynamic_pointer_cast(zone.zone)->getRenderItemID(); + Q_ASSERT(id != render::Item::INVALID_ITEM_ID); list.push_back(id); } render::Selection selection("RankedZones", list); @@ -791,24 +803,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 +841,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 +1078,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 e89646d838..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 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.cpp b/libraries/entities/src/EntityItem.cpp index 530e356137..17de15e32b 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -386,7 +386,13 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef return 0; } - qint64 clockSkew = args.sourceNode ? args.sourceNode->getClockSkewUsec() : 0; + int64_t clockSkew = 0; + uint64_t maxPingRoundTrip = 33333; // two frames periods at 60 fps + if (args.sourceNode) { + clockSkew = args.sourceNode->getClockSkewUsec(); + const float MSECS_PER_USEC = 1000; + maxPingRoundTrip += args.sourceNode->getPingMs() * MSECS_PER_USEC; + } BufferParser parser(data, bytesLeftToRead); @@ -653,7 +659,6 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef const QUuid& myNodeID = nodeList->getSessionUUID(); bool weOwnSimulation = _simulationOwner.matchesValidID(myNodeID); - // pack SimulationOwner and terse update properties near each other // NOTE: the server is authoritative for changes to simOwnerID so we always unpack ownership data // even when we would otherwise ignore the rest of the packet. @@ -678,7 +683,7 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef if (newSimOwner.getID().isNull() && !_simulationOwner.pendingRelease(lastEditedFromBufferAdjusted)) { // entity-server is trying to clear our ownership (probably at our own request) // but we actually want to own it, therefore we ignore this clear event - // and pretend that we own it (we assume we'll recover it soon) + // and pretend that we own it (e.g. we assume we'll receive ownership soon) // However, for now, when the server uses a newer time than what we sent, listen to what we're told. if (overwriteLocalData) { @@ -690,16 +695,19 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef // recompute weOwnSimulation for later weOwnSimulation = _simulationOwner.matchesValidID(myNodeID); } - } else if (newSimOwner.getID().isNull() && _simulationOwner.pendingTake(lastEditedFromBufferAdjusted)) { - // entity-server is trying to clear someone else's ownership - // but we want to own it, therefore we ignore this clear event - // and pretend that we own it (we assume we'll get it soon) + } else if (_simulationOwner.pendingTake(now - maxPingRoundTrip)) { + // we sent a bid before this packet could have been sent from the server + // so we ignore it and pretend we own the object's simulation weOwnSimulation = true; - if (!_simulationOwner.isNull()) { - // someone else really did own it - markDirtyFlags(Simulation::DIRTY_SIMULATOR_ID); - somethingChanged = true; - _simulationOwner.clearCurrentOwner(); + if (newSimOwner.getID().isNull()) { + // entity-server is trying to clear someone else's ownership + // but we want to own it, therefore we ignore this clear event + if (!_simulationOwner.isNull()) { + // someone else really did own it + markDirtyFlags(Simulation::DIRTY_SIMULATOR_ID); + somethingChanged = true; + _simulationOwner.clearCurrentOwner(); + } } } else if (newSimOwner.matchesValidID(myNodeID) && !_hasBidOnSimulation) { // entity-server tells us that we have simulation ownership while we never requested this for this EntityItem, 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/GLBackend.cpp b/libraries/gpu-gl/src/gpu/gl/GLBackend.cpp index ed81b502fc..791130ef6e 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLBackend.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLBackend.cpp @@ -115,6 +115,9 @@ GLBackend::CommandCall GLBackend::_commandCalls[Batch::NUM_COMMANDS] = (&::gpu::gl::GLBackend::do_resetStages), + (&::gpu::gl::GLBackend::do_disableContextStereo), + (&::gpu::gl::GLBackend::do_restoreContextStereo), + (&::gpu::gl::GLBackend::do_runLambda), (&::gpu::gl::GLBackend::do_startNamedCall), @@ -224,6 +227,14 @@ void GLBackend::renderPassTransfer(const Batch& batch) { _transform.preUpdate(_commandIndex, _stereo); break; + case Batch::COMMAND_disableContextStereo: + _stereo._contextDisable = true; + break; + + case Batch::COMMAND_restoreContextStereo: + _stereo._contextDisable = false; + break; + case Batch::COMMAND_setViewportTransform: case Batch::COMMAND_setViewTransform: case Batch::COMMAND_setProjectionTransform: { @@ -308,16 +319,16 @@ void GLBackend::render(const Batch& batch) { } #ifdef GPU_STEREO_DRAWCALL_INSTANCED - if (_stereo._enable) { + if (_stereo.isStereo()) { glEnable(GL_CLIP_DISTANCE0); } #endif { - PROFILE_RANGE(render_gpu_gl_detail, _stereo._enable ? "Render Stereo" : "Render"); + PROFILE_RANGE(render_gpu_gl_detail, _stereo.isStereo() ? "Render Stereo" : "Render"); renderPassDraw(batch); } #ifdef GPU_STEREO_DRAWCALL_INSTANCED - if (_stereo._enable) { + if (_stereo.isStereo()) { glDisable(GL_CLIP_DISTANCE0); } #endif @@ -358,6 +369,15 @@ void GLBackend::do_resetStages(const Batch& batch, size_t paramOffset) { resetStages(); } + +void GLBackend::do_disableContextStereo(const Batch& batch, size_t paramOffset) { + +} + +void GLBackend::do_restoreContextStereo(const Batch& batch, size_t paramOffset) { + +} + void GLBackend::do_runLambda(const Batch& batch, size_t paramOffset) { std::function f = batch._lambdas.get(batch._params[paramOffset]._uint); f(); diff --git a/libraries/gpu-gl/src/gpu/gl/GLBackend.h b/libraries/gpu-gl/src/gpu/gl/GLBackend.h index 1c6cffb575..96217555e1 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLBackend.h +++ b/libraries/gpu-gl/src/gpu/gl/GLBackend.h @@ -143,6 +143,9 @@ public: // Reset stages virtual void do_resetStages(const Batch& batch, size_t paramOffset) final; + virtual void do_disableContextStereo(const Batch& batch, size_t paramOffset) final; + virtual void do_restoreContextStereo(const Batch& batch, size_t paramOffset) final; + virtual void do_runLambda(const Batch& batch, size_t paramOffset) final; virtual void do_startNamedCall(const Batch& batch, size_t paramOffset) final; diff --git a/libraries/gpu-gl/src/gpu/gl/GLBackendOutput.cpp b/libraries/gpu-gl/src/gpu/gl/GLBackendOutput.cpp index 5c6a18d7af..358b90ed8b 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLBackendOutput.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLBackendOutput.cpp @@ -48,7 +48,7 @@ void GLBackend::do_setFramebuffer(const Batch& batch, size_t paramOffset) { } void GLBackend::do_clearFramebuffer(const Batch& batch, size_t paramOffset) { - if (_stereo._enable && !_pipeline._stateCache.scissorEnable) { + if (_stereo.isStereo() && !_pipeline._stateCache.scissorEnable) { qWarning("Clear without scissor in stereo mode"); } diff --git a/libraries/gpu-gl/src/gpu/gl/GLBackendState.cpp b/libraries/gpu-gl/src/gpu/gl/GLBackendState.cpp index a7d4a7ff7c..24f90395d7 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLBackendState.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLBackendState.cpp @@ -322,7 +322,7 @@ void GLBackend::do_setStateScissorRect(const Batch& batch, size_t paramOffset) { Vec4i rect; memcpy(&rect, batch.readData(batch._params[paramOffset]._uint), sizeof(Vec4i)); - if (_stereo._enable) { + if (_stereo.isStereo()) { rect.z /= 2; if (_stereo._pass) { rect.x += rect.z; diff --git a/libraries/gpu-gl/src/gpu/gl/GLBackendTransform.cpp b/libraries/gpu-gl/src/gpu/gl/GLBackendTransform.cpp index f3da18ba1d..01f055e0d9 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLBackendTransform.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLBackendTransform.cpp @@ -37,7 +37,7 @@ void GLBackend::do_setViewportTransform(const Batch& batch, size_t paramOffset) glViewport(vp.x, vp.y, vp.z, vp.w); // Where we assign the GL viewport - if (_stereo._enable) { + if (_stereo.isStereo()) { vp.z /= 2; if (_stereo._pass) { vp.x += vp.z; @@ -119,7 +119,7 @@ void GLBackend::TransformStageState::preUpdate(size_t commandIndex, const Stereo size_t offset = _cameraUboSize * _cameras.size(); _cameraOffsets.push_back(TransformStageState::Pair(commandIndex, offset)); - if (stereo._enable) { + if (stereo.isStereo()) { #ifdef GPU_STEREO_CAMERA_BUFFER _cameras.push_back(CameraBufferElement(_camera.getEyeCamera(0, stereo, _view), _camera.getEyeCamera(1, stereo, _view))); #else @@ -151,7 +151,7 @@ void GLBackend::TransformStageState::update(size_t commandIndex, const StereoSta #ifdef GPU_STEREO_CAMERA_BUFFER bindCurrentCamera(0); #else - if (!stereo._enable) { + if (!stereo.isStereo()) { bindCurrentCamera(0); } #endif 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/Batch.cpp b/libraries/gpu/src/gpu/Batch.cpp index 0d5036c202..15c0dfce49 100644 --- a/libraries/gpu/src/gpu/Batch.cpp +++ b/libraries/gpu/src/gpu/Batch.cpp @@ -390,6 +390,15 @@ void Batch::resetStages() { ADD_COMMAND(resetStages); } + +void Batch::disableContextStereo() { + ADD_COMMAND(disableContextStereo); +} + +void Batch::restoreContextStereo() { + ADD_COMMAND(restoreContextStereo); +} + void Batch::runLambda(std::function f) { ADD_COMMAND(runLambda); _params.emplace_back(_lambdas.cache(f)); diff --git a/libraries/gpu/src/gpu/Batch.h b/libraries/gpu/src/gpu/Batch.h index 85e99951cb..27c9402131 100644 --- a/libraries/gpu/src/gpu/Batch.h +++ b/libraries/gpu/src/gpu/Batch.h @@ -217,6 +217,9 @@ public: // Reset the stage caches and states void resetStages(); + void disableContextStereo(); + void restoreContextStereo(); + // Debugging void pushProfileRange(const char* name); void popProfileRange(); @@ -301,6 +304,9 @@ public: COMMAND_resetStages, + COMMAND_disableContextStereo, + COMMAND_restoreContextStereo, + COMMAND_runLambda, COMMAND_startNamedCall, @@ -467,7 +473,7 @@ public: NamedBatchDataMap _namedData; bool _enableStereo{ true }; - bool _enableSkybox{ false }; + bool _enableSkybox { false }; protected: friend class Context; diff --git a/libraries/gpu/src/gpu/Context.cpp b/libraries/gpu/src/gpu/Context.cpp index 2116ffd6fe..24128524da 100644 --- a/libraries/gpu/src/gpu/Context.cpp +++ b/libraries/gpu/src/gpu/Context.cpp @@ -145,7 +145,7 @@ void Context::enableStereo(bool enable) { } bool Context::isStereo() { - return _stereo._enable; + return _stereo.isStereo(); } void Context::setStereoProjections(const mat4 eyeProjections[2]) { diff --git a/libraries/gpu/src/gpu/Context.h b/libraries/gpu/src/gpu/Context.h index de818ddcb0..7b7575e9ed 100644 --- a/libraries/gpu/src/gpu/Context.h +++ b/libraries/gpu/src/gpu/Context.h @@ -118,7 +118,7 @@ public: protected: virtual bool isStereo() { - return _stereo._enable; + return _stereo.isStereo(); } void getStereoProjections(mat4* eyeProjections) const { diff --git a/libraries/gpu/src/gpu/Forward.h b/libraries/gpu/src/gpu/Forward.h index ce95e8657e..88800652a5 100644 --- a/libraries/gpu/src/gpu/Forward.h +++ b/libraries/gpu/src/gpu/Forward.h @@ -93,7 +93,11 @@ namespace gpu { using TextureViews = std::vector; struct StereoState { + bool isStereo() const { + return _enable && !_contextDisable; + } bool _enable{ false }; + bool _contextDisable { false }; bool _skybox{ false }; // 0 for left eye, 1 for right eye uint8 _pass{ 0 }; 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/ktx/src/ktx/KTX.cpp b/libraries/ktx/src/ktx/KTX.cpp index 788ec54a47..3b1a12cba7 100644 --- a/libraries/ktx/src/ktx/KTX.cpp +++ b/libraries/ktx/src/ktx/KTX.cpp @@ -16,6 +16,8 @@ using namespace ktx; +int ktxDescriptorMetaTypeId = qRegisterMetaType(); + const Header::Identifier ktx::Header::IDENTIFIER {{ 0xAB, 0x4B, 0x54, 0x58, 0x20, 0x31, 0x31, 0xBB, 0x0D, 0x0A, 0x1A, 0x0A }}; diff --git a/libraries/ktx/src/ktx/KTX.h b/libraries/ktx/src/ktx/KTX.h index b02e2ada75..8dc4ec7a47 100644 --- a/libraries/ktx/src/ktx/KTX.h +++ b/libraries/ktx/src/ktx/KTX.h @@ -387,4 +387,6 @@ namespace ktx { } +Q_DECLARE_METATYPE(ktx::KTXDescriptor*); + #endif // hifi_ktx_KTX_h 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.cpp b/libraries/model-networking/src/model-networking/KTXCache.cpp index 6443748920..edb09addb8 100644 --- a/libraries/model-networking/src/model-networking/KTXCache.cpp +++ b/libraries/model-networking/src/model-networking/KTXCache.cpp @@ -24,9 +24,10 @@ const int KTXCache::INVALID_VERSION = 0x00; const char* KTXCache::SETTING_VERSION_NAME = "hifi.ktx.cache_version"; KTXCache::KTXCache(const std::string& dir, const std::string& ext) : - FileCache(dir, ext) { - initialize(); + FileCache(dir, ext) { } +void KTXCache::initialize() { + FileCache::initialize(); Setting::Handle cacheVersionHandle(SETTING_VERSION_NAME, INVALID_VERSION); auto cacheVersion = cacheVersionHandle.get(); if (cacheVersion != CURRENT_VERSION) { @@ -35,20 +36,9 @@ KTXCache::KTXCache(const std::string& dir, const std::string& ext) : } } -KTXFilePointer KTXCache::writeFile(const char* data, Metadata&& metadata) { - FilePointer file = FileCache::writeFile(data, std::move(metadata), true); - return std::static_pointer_cast(file); -} - -KTXFilePointer KTXCache::getFile(const Key& key) { - return std::static_pointer_cast(FileCache::getFile(key)); -} std::unique_ptr KTXCache::createFile(Metadata&& metadata, const std::string& filepath) { qCInfo(file_cache) << "Wrote KTX" << metadata.key.c_str(); - return std::unique_ptr(new KTXFile(std::move(metadata), filepath)); + return FileCache::createFile(std::move(metadata), filepath); } -KTXFile::KTXFile(Metadata&& metadata, const std::string& filepath) : - cache::File(std::move(metadata), filepath) {} - diff --git a/libraries/model-networking/src/model-networking/KTXCache.h b/libraries/model-networking/src/model-networking/KTXCache.h index 5617019c52..079f1ac5b1 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; @@ -35,20 +35,10 @@ public: KTXCache(const std::string& dir, const std::string& ext); - KTXFilePointer writeFile(const char* data, Metadata&& metadata); - KTXFilePointer getFile(const Key& key); + void initialize() override; protected: std::unique_ptr createFile(Metadata&& metadata, const std::string& filepath) override final; }; -class KTXFile : public cache::File { - Q_OBJECT - -protected: - friend class KTXCache; - - KTXFile(Metadata&& metadata, const std::string& filepath); -}; - #endif // hifi_KTXCache_h diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index 8683d56b6b..674e4dbe3b 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -13,6 +13,8 @@ #include +#include + #include #include #include @@ -50,11 +52,14 @@ Q_LOGGING_CATEGORY(trace_resource_parse_image_ktx, "trace.resource.parse.image.k const std::string TextureCache::KTX_DIRNAME { "ktx_cache" }; const std::string TextureCache::KTX_EXT { "ktx" }; +static const QString RESOURCE_SCHEME = "resource"; +static const QUrl SPECTATOR_CAMERA_FRAME_URL("resource://spectatorCameraFrame"); + static const float SKYBOX_LOAD_PRIORITY { 10.0f }; // Make sure skybox loads first static const float HIGH_MIPS_LOAD_PRIORITY { 9.0f }; // Make sure high mips loads after skybox but before models -TextureCache::TextureCache() : - _ktxCache(KTX_DIRNAME, KTX_EXT) { +TextureCache::TextureCache() { + _ktxCache->initialize(); setUnusedResourceCacheSize(0); setObjectName("TextureCache"); } @@ -180,6 +185,9 @@ ScriptableResource* TextureCache::prefetch(const QUrl& url, int type, int maxNum } NetworkTexturePointer TextureCache::getTexture(const QUrl& url, image::TextureUsage::Type type, const QByteArray& content, int maxNumPixels) { + if (url.scheme() == RESOURCE_SCHEME) { + return getResourceTexture(url); + } TextureExtra extra = { type, content, maxNumPixels }; return ResourceCache::getResource(url, QUrl(), &extra).staticCast(); } @@ -265,6 +273,18 @@ QSharedPointer TextureCache::createResource(const QUrl& url, const QSh return QSharedPointer(texture, &Resource::deleter); } +NetworkTexture::NetworkTexture(const QUrl& url) : +Resource(url), +_type(), +_sourceIsKTX(false), +_maxNumPixels(100) +{ + _textureSource = std::make_shared(); + _lowestRequestedMipLevel = 0; + _loaded = true; +} + + NetworkTexture::NetworkTexture(const QUrl& url, image::TextureUsage::Type type, const QByteArray& content, int maxNumPixels) : Resource(url), _type(type), @@ -303,14 +323,12 @@ void NetworkTexture::setImage(gpu::TexturePointer texture, int originalWidth, _width = texture->getWidth(); _height = texture->getHeight(); setSize(texture->getStoredSize()); + finishedLoading(true); } else { - // FIXME: If !gpuTexture, we failed to load! _width = _height = 0; - qWarning() << "Texture did not load"; + finishedLoading(false); } - finishedLoading(true); - emit networkTextureCreated(qWeakPointerCast (_self)); } @@ -382,8 +400,7 @@ void NetworkTexture::makeRequest() { emit loading(); - connect(_ktxHeaderRequest, &ResourceRequest::progress, this, &NetworkTexture::ktxHeaderRequestProgress); - connect(_ktxHeaderRequest, &ResourceRequest::finished, this, &NetworkTexture::ktxHeaderRequestFinished); + connect(_ktxHeaderRequest, &ResourceRequest::finished, this, &NetworkTexture::ktxInitialDataRequestFinished); _bytesReceived = _bytesTotal = _bytes = 0; @@ -407,18 +424,18 @@ void NetworkTexture::makeRequest() { } void NetworkTexture::startRequestForNextMipLevel() { - if (_lowestKnownPopulatedMip == 0) { - qWarning(networking) << "Requesting next mip level but all have been fulfilled: " << _lowestKnownPopulatedMip - << " " << _textureSource->getGPUTexture()->minAvailableMipLevel() << " " << _url; + auto self = _self.lock(); + if (!self) { return; } - if (_ktxResourceState == WAITING_FOR_MIP_REQUEST) { - auto self = _self.lock(); - if (!self) { - return; - } + auto texture = _textureSource->getGPUTexture(); + if (!texture || _ktxResourceState != WAITING_FOR_MIP_REQUEST) { + return; + } + _lowestKnownPopulatedMip = texture->minAvailableMipLevel(); + if (_lowestRequestedMipLevel < _lowestKnownPopulatedMip) { _ktxResourceState = PENDING_MIP_REQUEST; init(false); @@ -453,6 +470,8 @@ void NetworkTexture::startMipRangeRequest(uint16_t low, uint16_t high) { ByteRange range; range.fromInclusive = -HIGH_MIP_MAX_SIZE; _ktxMipRequest->setByteRange(range); + + connect(_ktxMipRequest, &ResourceRequest::finished, this, &NetworkTexture::ktxInitialDataRequestFinished); } else { ByteRange range; range.fromInclusive = ktx::KTX_HEADER_SIZE + _originalKtxDescriptor->header.bytesOfKeyValueData @@ -460,228 +479,315 @@ void NetworkTexture::startMipRangeRequest(uint16_t low, uint16_t high) { range.toExclusive = ktx::KTX_HEADER_SIZE + _originalKtxDescriptor->header.bytesOfKeyValueData + _originalKtxDescriptor->images[high + 1]._imageOffset; _ktxMipRequest->setByteRange(range); - } - connect(_ktxMipRequest, &ResourceRequest::progress, this, &NetworkTexture::ktxMipRequestProgress); - connect(_ktxMipRequest, &ResourceRequest::finished, this, &NetworkTexture::ktxMipRequestFinished); + connect(_ktxMipRequest, &ResourceRequest::finished, this, &NetworkTexture::ktxMipRequestFinished); + } _ktxMipRequest->send(); } -void NetworkTexture::ktxHeaderRequestFinished() { - Q_ASSERT(_ktxResourceState == LOADING_INITIAL_DATA); - - if (!_ktxHeaderRequest) { +// This is called when the header or top mips have been loaded +void NetworkTexture::ktxInitialDataRequestFinished() { + if (!_ktxHeaderRequest || _ktxHeaderRequest->getState() != ResourceRequest::Finished || + !_ktxMipRequest || _ktxMipRequest->getState() != ResourceRequest::Finished) { + // Wait for both request to be finished return; } - _ktxHeaderRequestFinished = true; - maybeHandleFinishedInitialLoad(); + Q_ASSERT(_ktxResourceState == LOADING_INITIAL_DATA); + Q_ASSERT_X(_ktxHeaderRequest && _ktxMipRequest, __FUNCTION__, "Request should not be null while in ktxInitialDataRequestFinished"); + + PROFILE_ASYNC_END(resource, "Resource:" + getType(), QString::number(_requestID), { + { "from_cache", _ktxHeaderRequest->loadedFromCache() }, + { "size_mb", _bytesTotal / 1000000.0 } + }); + + PROFILE_RANGE_EX(resource_parse_image, __FUNCTION__, 0xffff0000, 0, { { "url", _url.toString() } }); + + setSize(_bytesTotal); + + TextureCache::requestCompleted(_self); + + auto result = _ktxHeaderRequest->getResult(); + if (result == ResourceRequest::Success) { + result = _ktxMipRequest->getResult(); + } + + if (result == ResourceRequest::Success) { + auto extraInfo = _url == _activeUrl ? "" : QString(", %1").arg(_activeUrl.toDisplayString()); + qCDebug(networking).noquote() << QString("Request finished for %1%2").arg(_url.toDisplayString(), extraInfo); + + _ktxHeaderData = _ktxHeaderRequest->getData(); + _ktxHighMipData = _ktxMipRequest->getData(); + handleFinishedInitialLoad(); + } else { + if (handleFailedRequest(result)) { + _ktxResourceState = PENDING_INITIAL_LOAD; + } else { + _ktxResourceState = FAILED_TO_LOAD; + } + } + + _ktxHeaderRequest->disconnect(this); + _ktxHeaderRequest->deleteLater(); + _ktxHeaderRequest = nullptr; + _ktxMipRequest->disconnect(this); + _ktxMipRequest->deleteLater(); + _ktxMipRequest = nullptr; } void NetworkTexture::ktxMipRequestFinished() { - Q_ASSERT(_ktxResourceState == LOADING_INITIAL_DATA || _ktxResourceState == REQUESTING_MIP); + Q_ASSERT_X(_ktxMipRequest, __FUNCTION__, "Request should not be null while in ktxMipRequestFinished"); + Q_ASSERT(_ktxResourceState == REQUESTING_MIP); - if (!_ktxMipRequest) { + PROFILE_ASYNC_END(resource, "Resource:" + getType(), QString::number(_requestID), { + { "from_cache", _ktxMipRequest->loadedFromCache() }, + { "size_mb", _bytesTotal / 1000000.0 } + }); + + PROFILE_RANGE_EX(resource_parse_image, __FUNCTION__, 0xffff0000, 0, { { "url", _url.toString() } }); + + + setSize(_bytesTotal); + + if (!_ktxMipRequest || _ktxMipRequest != sender()) { + // This can happen in the edge case that a request is timed out, but a `finished` signal is emitted before it is deleted. + qWarning(networking) << "Received signal NetworkTexture::ktxMipRequestFinished from ResourceRequest that is not the current" + << " request: " << sender() << ", " << _ktxMipRequest; return; } - if (_ktxResourceState == LOADING_INITIAL_DATA) { - _ktxHighMipRequestFinished = true; - maybeHandleFinishedInitialLoad(); - } else if (_ktxResourceState == REQUESTING_MIP) { - Q_ASSERT(_ktxMipLevelRangeInFlight.first != NULL_MIP_LEVEL); - TextureCache::requestCompleted(_self); + TextureCache::requestCompleted(_self); - if (_ktxMipRequest->getResult() == ResourceRequest::Success) { + auto result = _ktxMipRequest->getResult(); + if (result == ResourceRequest::Success) { + auto extraInfo = _url == _activeUrl ? "" : QString(", %1").arg(_activeUrl.toDisplayString()); + qCDebug(networking).noquote() << QString("Request finished for %1%2").arg(_url.toDisplayString(), extraInfo); + + if (_ktxResourceState == REQUESTING_MIP) { + Q_ASSERT(_ktxMipLevelRangeInFlight.first != NULL_MIP_LEVEL); Q_ASSERT(_ktxMipLevelRangeInFlight.second - _ktxMipLevelRangeInFlight.first == 0); + _ktxResourceState = WAITING_FOR_MIP_REQUEST; + + auto self = _self; + auto url = _url; + auto data = _ktxMipRequest->getData(); + auto mipLevel = _ktxMipLevelRangeInFlight.first; auto texture = _textureSource->getGPUTexture(); - if (texture) { - texture->assignStoredMip(_ktxMipLevelRangeInFlight.first, - _ktxMipRequest->getData().size(), reinterpret_cast(_ktxMipRequest->getData().data())); + DependencyManager::get()->incrementStat("PendingProcessing"); + QtConcurrent::run(QThreadPool::globalInstance(), [self, data, mipLevel, url, texture] { + PROFILE_RANGE_EX(resource_parse_image, "NetworkTexture - Processing Mip Data", 0xffff0000, 0, { { "url", url.toString() } }); + DependencyManager::get()->decrementStat("PendingProcessing"); + CounterStat counter("Processing"); - if (texture->minAvailableMipLevel() <= _ktxMipLevelRangeInFlight.first) { - _lowestKnownPopulatedMip = texture->minAvailableMipLevel(); - _ktxResourceState = WAITING_FOR_MIP_REQUEST; - } else { - qWarning(networking) << "Failed to load mip: " << _url << ":" << _ktxMipLevelRangeInFlight.first; - _ktxResourceState = FAILED_TO_LOAD; + auto originalPriority = QThread::currentThread()->priority(); + if (originalPriority == QThread::InheritPriority) { + originalPriority = QThread::NormalPriority; } - } else { - _ktxResourceState = WAITING_FOR_MIP_REQUEST; - qWarning(networking) << "Trying to update mips but texture is null"; - } - finishedLoading(true); + QThread::currentThread()->setPriority(QThread::LowPriority); + Finally restorePriority([originalPriority] { QThread::currentThread()->setPriority(originalPriority); }); + + auto resource = self.lock(); + if (!resource) { + // Resource no longer exists, bail + return; + } + + Q_ASSERT_X(texture, "Async - NetworkTexture::ktxMipRequestFinished", "NetworkTexture should have been assigned a GPU texture by now."); + + texture->assignStoredMip(mipLevel, data.size(), reinterpret_cast(data.data())); + + QMetaObject::invokeMethod(resource.data(), "setImage", + Q_ARG(gpu::TexturePointer, texture), + Q_ARG(int, texture->getWidth()), + Q_ARG(int, texture->getHeight())); + + QMetaObject::invokeMethod(resource.data(), "startRequestForNextMipLevel"); + }); } else { + qWarning(networking) << "Mip request finished in an unexpected state: " << _ktxResourceState; finishedLoading(false); - if (handleFailedRequest(_ktxMipRequest->getResult())) { - _ktxResourceState = PENDING_MIP_REQUEST; - } else { - qWarning(networking) << "Failed to load mip: " << _url; - _ktxResourceState = FAILED_TO_LOAD; - } - } - - _ktxMipRequest->deleteLater(); - _ktxMipRequest = nullptr; - - if (_ktxResourceState == WAITING_FOR_MIP_REQUEST && _lowestRequestedMipLevel < _lowestKnownPopulatedMip) { - startRequestForNextMipLevel(); } } else { - qWarning() << "Mip request finished in an unexpected state: " << _ktxResourceState; + if (handleFailedRequest(result)) { + _ktxResourceState = PENDING_MIP_REQUEST; + } else { + _ktxResourceState = FAILED_TO_LOAD; + } } + + _ktxMipRequest->disconnect(this); + _ktxMipRequest->deleteLater(); + _ktxMipRequest = nullptr; } -// This is called when the header or top mips have been loaded -void NetworkTexture::maybeHandleFinishedInitialLoad() { +// This is called when the header and top mips have been loaded +void NetworkTexture::handleFinishedInitialLoad() { Q_ASSERT(_ktxResourceState == LOADING_INITIAL_DATA); + Q_ASSERT(!_ktxHeaderData.isEmpty() && !_ktxHighMipData.isEmpty()); - if (_ktxHeaderRequestFinished && _ktxHighMipRequestFinished) { + // create ktx... + auto ktxHeaderData = _ktxHeaderData; + auto ktxHighMipData = _ktxHighMipData; + _ktxHeaderData.clear(); + _ktxHighMipData.clear(); - TextureCache::requestCompleted(_self); + _ktxResourceState = WAITING_FOR_MIP_REQUEST; - if (_ktxHeaderRequest->getResult() != ResourceRequest::Success || _ktxMipRequest->getResult() != ResourceRequest::Success) { - if (handleFailedRequest(_ktxMipRequest->getResult())) { - _ktxResourceState = PENDING_INITIAL_LOAD; - } - else { - _ktxResourceState = FAILED_TO_LOAD; - } + auto self = _self; + auto url = _url; + DependencyManager::get()->incrementStat("PendingProcessing"); + QtConcurrent::run(QThreadPool::globalInstance(), [self, ktxHeaderData, ktxHighMipData, url] { + PROFILE_RANGE_EX(resource_parse_image, "NetworkTexture - Processing Initial Data", 0xffff0000, 0, { { "url", url.toString() } }); + DependencyManager::get()->decrementStat("PendingProcessing"); + CounterStat counter("Processing"); - _ktxHeaderRequest->deleteLater(); - _ktxHeaderRequest = nullptr; - _ktxMipRequest->deleteLater(); - _ktxMipRequest = nullptr; - } else { - // create ktx... - auto ktxHeaderData = _ktxHeaderRequest->getData(); - auto ktxHighMipData = _ktxMipRequest->getData(); - - auto header = reinterpret_cast(ktxHeaderData.data()); - - if (!ktx::checkIdentifier(header->identifier)) { - qWarning() << "Cannot load " << _url << ", invalid header identifier"; - _ktxResourceState = FAILED_TO_LOAD; - finishedLoading(false); - return; - } - - auto kvSize = header->bytesOfKeyValueData; - if (kvSize > (ktxHeaderData.size() - ktx::KTX_HEADER_SIZE)) { - qWarning() << "Cannot load " << _url << ", did not receive all kv data with initial request"; - _ktxResourceState = FAILED_TO_LOAD; - finishedLoading(false); - return; - } - - auto keyValues = ktx::KTX::parseKeyValues(header->bytesOfKeyValueData, reinterpret_cast(ktxHeaderData.data()) + ktx::KTX_HEADER_SIZE); - - auto imageDescriptors = header->generateImageDescriptors(); - if (imageDescriptors.size() == 0) { - qWarning(networking) << "Failed to process ktx file " << _url; - _ktxResourceState = FAILED_TO_LOAD; - finishedLoading(false); - } - _originalKtxDescriptor.reset(new ktx::KTXDescriptor(*header, keyValues, imageDescriptors)); - - // Create bare ktx in memory - auto found = std::find_if(keyValues.begin(), keyValues.end(), [](const ktx::KeyValue& val) -> bool { - return val._key.compare(gpu::SOURCE_HASH_KEY) == 0; - }); - std::string filename; - std::string hash; - if (found == keyValues.end() || found->_value.size() != gpu::SOURCE_HASH_BYTES) { - qWarning("Invalid source hash key found, bailing"); - _ktxResourceState = FAILED_TO_LOAD; - finishedLoading(false); - return; - } else { - // at this point the source hash is in binary 16-byte form - // and we need it in a hexadecimal string - auto binaryHash = QByteArray(reinterpret_cast(found->_value.data()), gpu::SOURCE_HASH_BYTES); - hash = filename = binaryHash.toHex().toStdString(); - } - - auto textureCache = DependencyManager::get(); - - gpu::TexturePointer texture = textureCache->getTextureByHash(hash); - - if (!texture) { - KTXFilePointer ktxFile = textureCache->_ktxCache.getFile(hash); - if (ktxFile) { - texture = gpu::Texture::unserialize(ktxFile->getFilepath()); - if (texture) { - texture = textureCache->cacheTextureByHash(hash, texture); - } - } - } - - if (!texture) { - - auto memKtx = ktx::KTX::createBare(*header, keyValues); - if (!memKtx) { - qWarning() << " Ktx could not be created, bailing"; - finishedLoading(false); - return; - } - - // Move ktx to file - const char* data = reinterpret_cast(memKtx->_storage->data()); - size_t length = memKtx->_storage->size(); - KTXFilePointer file; - auto& ktxCache = textureCache->_ktxCache; - if (!memKtx || !(file = ktxCache.writeFile(data, KTXCache::Metadata(filename, length)))) { - qCWarning(modelnetworking) << _url << " failed to write cache file"; - _ktxResourceState = FAILED_TO_LOAD; - finishedLoading(false); - return; - } else { - _file = file; - } - - auto newKtxDescriptor = memKtx->toDescriptor(); - - texture = gpu::Texture::unserialize(_file->getFilepath(), newKtxDescriptor); - texture->setKtxBacking(file->getFilepath()); - texture->setSource(filename); - - auto& images = _originalKtxDescriptor->images; - size_t imageSizeRemaining = ktxHighMipData.size(); - uint8_t* ktxData = reinterpret_cast(ktxHighMipData.data()); - ktxData += ktxHighMipData.size(); - // TODO Move image offset calculation to ktx ImageDescriptor - for (int level = static_cast(images.size()) - 1; level >= 0; --level) { - auto& image = images[level]; - if (image._imageSize > imageSizeRemaining) { - break; - } - ktxData -= image._imageSize; - texture->assignStoredMip(static_cast(level), image._imageSize, ktxData); - ktxData -= ktx::IMAGE_SIZE_WIDTH; - imageSizeRemaining -= (image._imageSize + ktx::IMAGE_SIZE_WIDTH); - } - - // We replace the texture with the one stored in the cache. This deals with the possible race condition of two different - // images with the same hash being loaded concurrently. Only one of them will make it into the cache by hash first and will - // be the winner - texture = textureCache->cacheTextureByHash(filename, texture); - } - - _lowestKnownPopulatedMip = texture->minAvailableMipLevel(); - - _ktxResourceState = WAITING_FOR_MIP_REQUEST; - setImage(texture, header->getPixelWidth(), header->getPixelHeight()); - - _ktxHeaderRequest->deleteLater(); - _ktxHeaderRequest = nullptr; - _ktxMipRequest->deleteLater(); - _ktxMipRequest = nullptr; + auto originalPriority = QThread::currentThread()->priority(); + if (originalPriority == QThread::InheritPriority) { + originalPriority = QThread::NormalPriority; } - startRequestForNextMipLevel(); - } + QThread::currentThread()->setPriority(QThread::LowPriority); + Finally restorePriority([originalPriority] { QThread::currentThread()->setPriority(originalPriority); }); + + auto resource = self.lock(); + if (!resource) { + // Resource no longer exists, bail + return; + } + + auto header = reinterpret_cast(ktxHeaderData.data()); + + if (!ktx::checkIdentifier(header->identifier)) { + qWarning() << "Cannot load " << url << ", invalid header identifier"; + QMetaObject::invokeMethod(resource.data(), "setImage", + Q_ARG(gpu::TexturePointer, nullptr), + Q_ARG(int, 0), + Q_ARG(int, 0)); + return; + } + + auto kvSize = header->bytesOfKeyValueData; + if (kvSize > (ktxHeaderData.size() - ktx::KTX_HEADER_SIZE)) { + qWarning() << "Cannot load " << url << ", did not receive all kv data with initial request"; + QMetaObject::invokeMethod(resource.data(), "setImage", + Q_ARG(gpu::TexturePointer, nullptr), + Q_ARG(int, 0), + Q_ARG(int, 0)); + return; + } + + auto keyValues = ktx::KTX::parseKeyValues(header->bytesOfKeyValueData, reinterpret_cast(ktxHeaderData.data()) + ktx::KTX_HEADER_SIZE); + + auto imageDescriptors = header->generateImageDescriptors(); + if (imageDescriptors.size() == 0) { + qWarning(networking) << "Failed to process ktx file " << url; + QMetaObject::invokeMethod(resource.data(), "setImage", + Q_ARG(gpu::TexturePointer, nullptr), + Q_ARG(int, 0), + Q_ARG(int, 0)); + return; + } + auto originalKtxDescriptor = new ktx::KTXDescriptor(*header, keyValues, imageDescriptors); + QMetaObject::invokeMethod(resource.data(), "setOriginalDescriptor", + Q_ARG(ktx::KTXDescriptor*, originalKtxDescriptor)); + + // Create bare ktx in memory + auto found = std::find_if(keyValues.begin(), keyValues.end(), [](const ktx::KeyValue& val) -> bool { + return val._key.compare(gpu::SOURCE_HASH_KEY) == 0; + }); + std::string filename; + std::string hash; + if (found == keyValues.end() || found->_value.size() != gpu::SOURCE_HASH_BYTES) { + qWarning("Invalid source hash key found, bailing"); + QMetaObject::invokeMethod(resource.data(), "setImage", + Q_ARG(gpu::TexturePointer, nullptr), + Q_ARG(int, 0), + Q_ARG(int, 0)); + return; + } else { + // at this point the source hash is in binary 16-byte form + // and we need it in a hexadecimal string + auto binaryHash = QByteArray(reinterpret_cast(found->_value.data()), gpu::SOURCE_HASH_BYTES); + hash = filename = binaryHash.toHex().toStdString(); + } + + auto textureCache = DependencyManager::get(); + + gpu::TexturePointer texture = textureCache->getTextureByHash(hash); + + if (!texture) { + auto ktxFile = textureCache->_ktxCache->getFile(hash); + if (ktxFile) { + texture = gpu::Texture::unserialize(ktxFile); + if (texture) { + texture = textureCache->cacheTextureByHash(hash, texture); + } + } + } + + if (!texture) { + + auto memKtx = ktx::KTX::createBare(*header, keyValues); + if (!memKtx) { + qWarning() << " Ktx could not be created, bailing"; + QMetaObject::invokeMethod(resource.data(), "setImage", + Q_ARG(gpu::TexturePointer, nullptr), + Q_ARG(int, 0), + Q_ARG(int, 0)); + return; + } + + // Move ktx to file + const char* data = reinterpret_cast(memKtx->_storage->data()); + size_t length = memKtx->_storage->size(); + cache::FilePointer file; + auto& ktxCache = textureCache->_ktxCache; + if (!memKtx || !(file = ktxCache->writeFile(data, KTXCache::Metadata(filename, length)))) { + qCWarning(modelnetworking) << url << " failed to write cache file"; + QMetaObject::invokeMethod(resource.data(), "setImage", + Q_ARG(gpu::TexturePointer, nullptr), + Q_ARG(int, 0), + Q_ARG(int, 0)); + return; + } + + auto newKtxDescriptor = memKtx->toDescriptor(); + + texture = gpu::Texture::build(newKtxDescriptor); + texture->setKtxBacking(file); + texture->setSource(filename); + + auto& images = originalKtxDescriptor->images; + size_t imageSizeRemaining = ktxHighMipData.size(); + const uint8_t* ktxData = reinterpret_cast(ktxHighMipData.data()); + ktxData += ktxHighMipData.size(); + // TODO Move image offset calculation to ktx ImageDescriptor + for (int level = static_cast(images.size()) - 1; level >= 0; --level) { + auto& image = images[level]; + if (image._imageSize > imageSizeRemaining) { + break; + } + ktxData -= image._imageSize; + texture->assignStoredMip(static_cast(level), image._imageSize, ktxData); + ktxData -= ktx::IMAGE_SIZE_WIDTH; + imageSizeRemaining -= (image._imageSize + ktx::IMAGE_SIZE_WIDTH); + } + + // We replace the texture with the one stored in the cache. This deals with the possible race condition of two different + // images with the same hash being loaded concurrently. Only one of them will make it into the cache by hash first and will + // be the winner + texture = textureCache->cacheTextureByHash(filename, texture); + } + + QMetaObject::invokeMethod(resource.data(), "setImage", + Q_ARG(gpu::TexturePointer, texture), + Q_ARG(int, texture->getWidth()), + Q_ARG(int, texture->getHeight())); + + QMetaObject::invokeMethod(resource.data(), "startRequestForNextMipLevel"); + }); } void NetworkTexture::downloadFinished(const QByteArray& data) { @@ -794,9 +900,9 @@ void ImageReader::read() { // If there is no live texture, check if there's an existing KTX file if (!texture) { - KTXFilePointer ktxFile = textureCache->_ktxCache.getFile(hash); + auto 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 { @@ -844,11 +950,11 @@ void ImageReader::read() { const char* data = reinterpret_cast(memKtx->_storage->data()); size_t length = memKtx->_storage->size(); auto& ktxCache = textureCache->_ktxCache; - networkTexture->_file = ktxCache.writeFile(data, KTXCache::Metadata(hash, length)); // - if (!networkTexture->_file) { + auto file = ktxCache->writeFile(data, KTXCache::Metadata(hash, length)); + if (!file) { qCWarning(modelnetworking) << _url << "file cache failed"; } else { - texture->setKtxBacking(networkTexture->_file->getFilepath()); + texture->setKtxBacking(file); } } else { qCWarning(modelnetworking) << "Unable to serialize texture to KTX " << _url; @@ -865,3 +971,32 @@ void ImageReader::read() { Q_ARG(int, texture->getWidth()), Q_ARG(int, texture->getHeight())); } + + +NetworkTexturePointer TextureCache::getResourceTexture(QUrl resourceTextureUrl) { + gpu::TexturePointer texture; + if (resourceTextureUrl == SPECTATOR_CAMERA_FRAME_URL) { + if (!_spectatorCameraNetworkTexture) { + _spectatorCameraNetworkTexture.reset(new NetworkTexture(resourceTextureUrl)); + } + texture = _spectatorCameraFramebuffer->getRenderBuffer(0); + if (texture) { + _spectatorCameraNetworkTexture->setImage(texture, texture->getWidth(), texture->getHeight()); + return _spectatorCameraNetworkTexture; + } + } + + return NetworkTexturePointer(); +} + +const gpu::FramebufferPointer& TextureCache::getSpectatorCameraFramebuffer() { + if (!_spectatorCameraFramebuffer) { + resetSpectatorCameraFramebuffer(2048, 1024); + } + return _spectatorCameraFramebuffer; +} + +void TextureCache::resetSpectatorCameraFramebuffer(int width, int height) { + _spectatorCameraFramebuffer.reset(gpu::Framebuffer::create("spectatorCamera", gpu::Element::COLOR_SRGBA_32, width, height)); + _spectatorCameraNetworkTexture.reset(); +} diff --git a/libraries/model-networking/src/model-networking/TextureCache.h b/libraries/model-networking/src/model-networking/TextureCache.h index 7dab18d457..43edc3593d 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.h +++ b/libraries/model-networking/src/model-networking/TextureCache.h @@ -43,6 +43,7 @@ class NetworkTexture : public Resource, public Texture { Q_OBJECT public: + NetworkTexture(const QUrl& url); NetworkTexture(const QUrl& url, image::TextureUsage::Type type, const QByteArray& content, int maxNumPixels); ~NetworkTexture() override; @@ -58,14 +59,13 @@ public: void refresh() override; + Q_INVOKABLE void setOriginalDescriptor(ktx::KTXDescriptor* descriptor) { _originalKtxDescriptor.reset(descriptor); } + signals: void networkTextureCreated(const QWeakPointer& self); public slots: - void ktxHeaderRequestProgress(uint64_t bytesReceived, uint64_t bytesTotal) { } - void ktxHeaderRequestFinished(); - - void ktxMipRequestProgress(uint64_t bytesReceived, uint64_t bytesTotal) { } + void ktxInitialDataRequestFinished(); void ktxMipRequestFinished(); protected: @@ -74,14 +74,14 @@ protected: virtual bool isCacheable() const override { return _loaded; } virtual void downloadFinished(const QByteArray& data) override; - + Q_INVOKABLE void loadContent(const QByteArray& content); Q_INVOKABLE void setImage(gpu::TexturePointer texture, int originalWidth, int originalHeight); - void startRequestForNextMipLevel(); + Q_INVOKABLE void startRequestForNextMipLevel(); void startMipRangeRequest(uint16_t low, uint16_t high); - void maybeHandleFinishedInitialLoad(); + void handleFinishedInitialLoad(); private: friend class KTXReader; @@ -102,16 +102,13 @@ private: bool _sourceIsKTX { false }; KTXResourceState _ktxResourceState { PENDING_INITIAL_LOAD }; - // TODO Can this be removed? - KTXFilePointer _file; - // The current mips that are currently being requested w/ _ktxMipRequest std::pair _ktxMipLevelRangeInFlight{ NULL_MIP_LEVEL, NULL_MIP_LEVEL }; ResourceRequest* _ktxHeaderRequest { nullptr }; ResourceRequest* _ktxMipRequest { nullptr }; - bool _ktxHeaderRequestFinished{ false }; - bool _ktxHighMipRequestFinished{ false }; + QByteArray _ktxHeaderData; + QByteArray _ktxHighMipData; uint16_t _lowestRequestedMipLevel { NULL_MIP_LEVEL }; uint16_t _lowestKnownPopulatedMip { NULL_MIP_LEVEL }; @@ -128,6 +125,8 @@ private: int _width { 0 }; int _height { 0 }; int _maxNumPixels { ABSOLUTE_MAX_TEXTURE_NUM_PIXELS }; + + friend class TextureCache; }; using NetworkTexturePointer = QSharedPointer; @@ -166,6 +165,12 @@ public: gpu::TexturePointer getTextureByHash(const std::string& hash); gpu::TexturePointer cacheTextureByHash(const std::string& hash, const gpu::TexturePointer& texture); + + /// SpectatorCamera rendering targets. + NetworkTexturePointer getResourceTexture(QUrl resourceTextureUrl); + const gpu::FramebufferPointer& getSpectatorCameraFramebuffer(); + void resetSpectatorCameraFramebuffer(int width, int height); + protected: // Overload ResourceCache::prefetch to allow specifying texture type for loads Q_INVOKABLE ScriptableResource* prefetch(const QUrl& url, int type, int maxNumPixels = ABSOLUTE_MAX_TEXTURE_NUM_PIXELS); @@ -183,7 +188,8 @@ private: static const std::string KTX_DIRNAME; static const std::string KTX_EXT; - KTXCache _ktxCache; + + std::shared_ptr _ktxCache { std::make_shared(KTX_DIRNAME, KTX_EXT) }; // Map from image hashes to texture weak pointers std::unordered_map> _texturesByHashes; std::mutex _texturesByHashesMutex; @@ -193,6 +199,9 @@ private: gpu::TexturePointer _grayTexture; gpu::TexturePointer _blueTexture; gpu::TexturePointer _blackTexture; + + NetworkTexturePointer _spectatorCameraNetworkTexture; + gpu::FramebufferPointer _spectatorCameraFramebuffer; }; #endif // hifi_TextureCache_h 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/AssetClient.cpp b/libraries/networking/src/AssetClient.cpp index 054557e920..3faa9981dc 100644 --- a/libraries/networking/src/AssetClient.cpp +++ b/libraries/networking/src/AssetClient.cpp @@ -353,13 +353,20 @@ void AssetClient::handleAssetGetReply(QSharedPointer message, S connect(message.data(), &ReceivedMessage::progress, this, [this, weakNode, messageID, length](qint64 size) { handleProgressCallback(weakNode, messageID, size, length); }); - connect(message.data(), &ReceivedMessage::completed, this, [this, weakNode, messageID]() { - handleCompleteCallback(weakNode, messageID); + connect(message.data(), &ReceivedMessage::completed, this, [this, weakNode, messageID, length]() { + handleCompleteCallback(weakNode, messageID, length); }); if (message->isComplete()) { disconnect(message.data(), nullptr, this, nullptr); - callbacks.completeCallback(true, error, message->readAll()); + + if (length != message->getBytesLeftToRead()) { + callbacks.completeCallback(false, error, QByteArray()); + } else { + callbacks.completeCallback(true, error, message->readAll()); + } + + messageCallbackMap.erase(requestIt); } } @@ -391,7 +398,7 @@ void AssetClient::handleProgressCallback(const QWeakPointer& node, Message callbacks.progressCallback(size, length); } -void AssetClient::handleCompleteCallback(const QWeakPointer& node, MessageID messageID) { +void AssetClient::handleCompleteCallback(const QWeakPointer& node, MessageID messageID, DataOffset length) { auto senderNode = node.toStrongRef(); if (!senderNode) { @@ -424,8 +431,7 @@ void AssetClient::handleCompleteCallback(const QWeakPointer& node, Message return; } - - if (message->failed()) { + if (message->failed() || length != message->getBytesLeftToRead()) { callbacks.completeCallback(false, AssetServerError::NoError, QByteArray()); } else { callbacks.completeCallback(true, AssetServerError::NoError, message->readAll()); diff --git a/libraries/networking/src/AssetClient.h b/libraries/networking/src/AssetClient.h index 6f9cc3cd31..cab4a4f5b0 100644 --- a/libraries/networking/src/AssetClient.h +++ b/libraries/networking/src/AssetClient.h @@ -93,7 +93,7 @@ private: bool cancelUploadAssetRequest(MessageID id); void handleProgressCallback(const QWeakPointer& node, MessageID messageID, qint64 size, DataOffset length); - void handleCompleteCallback(const QWeakPointer& node, MessageID messageID); + void handleCompleteCallback(const QWeakPointer& node, MessageID messageID, DataOffset length); void forceFailureOfPendingRequests(SharedNodePointer node); diff --git a/libraries/networking/src/AssetRequest.cpp b/libraries/networking/src/AssetRequest.cpp index 00fa3d9f2f..7fa563d4ad 100644 --- a/libraries/networking/src/AssetRequest.cpp +++ b/libraries/networking/src/AssetRequest.cpp @@ -104,12 +104,7 @@ void AssetRequest::start() { break; } } else { - if (_byteRange.isSet()) { - // we had a byte range, the size of the data does not match what we expect, so we return an error - if (data.size() != _byteRange.size()) { - _error = SizeVerificationFailed; - } - } else if (hashData(data).toHex() != _hash) { + if (!_byteRange.isSet() && hashData(data).toHex() != _hash) { // the hash of the received data does not match what we expect, so we return an error _error = HashVerificationFailed; } diff --git a/interface/src/scripting/LocationScriptingInterface.cpp b/libraries/networking/src/LocationScriptingInterface.cpp similarity index 95% rename from interface/src/scripting/LocationScriptingInterface.cpp rename to libraries/networking/src/LocationScriptingInterface.cpp index c0a9a62ff3..aae1da73ba 100644 --- a/interface/src/scripting/LocationScriptingInterface.cpp +++ b/libraries/networking/src/LocationScriptingInterface.cpp @@ -1,6 +1,6 @@ // // LocationScriptingInterface.cpp -// interface/src/scripting +// libraries/networking/src // // Created by Ryan Huffman on 4/29/14. // Copyright 2014 High Fidelity, Inc. @@ -9,7 +9,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#include +#include "AddressManager.h" #include "LocationScriptingInterface.h" diff --git a/interface/src/scripting/LocationScriptingInterface.h b/libraries/networking/src/LocationScriptingInterface.h similarity index 96% rename from interface/src/scripting/LocationScriptingInterface.h rename to libraries/networking/src/LocationScriptingInterface.h index 18390aa1b3..987c4ccd0d 100644 --- a/interface/src/scripting/LocationScriptingInterface.h +++ b/libraries/networking/src/LocationScriptingInterface.h @@ -1,6 +1,6 @@ // // LocationScriptingInterface.h -// interface/src/scripting +// libraries/networking/src // // Created by Ryan Huffman on 4/29/14. // Copyright 2014 High Fidelity, Inc. 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/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/physics/src/ObjectActionTractor.cpp b/libraries/physics/src/ObjectActionTractor.cpp index 4bb5d850a9..1a1790b0e0 100644 --- a/libraries/physics/src/ObjectActionTractor.cpp +++ b/libraries/physics/src/ObjectActionTractor.cpp @@ -23,14 +23,16 @@ const uint16_t ObjectActionTractor::tractorVersion = 1; ObjectActionTractor::ObjectActionTractor(const QUuid& id, EntityItemPointer ownerEntity) : ObjectAction(DYNAMIC_TYPE_TRACTOR, id, ownerEntity), - _positionalTarget(glm::vec3(0.0f)), - _desiredPositionalTarget(glm::vec3(0.0f)), + _positionalTarget(0.0f), + _desiredPositionalTarget(0.0f), _linearTimeScale(FLT_MAX), - _positionalTargetSet(true), - _rotationalTarget(glm::quat()), - _desiredRotationalTarget(glm::quat()), + _positionalTargetSet(false), + _rotationalTarget(), + _desiredRotationalTarget(), _angularTimeScale(FLT_MAX), - _rotationalTargetSet(true) { + _rotationalTargetSet(true), + _linearVelocityTarget(0.0f) +{ #if WANT_DEBUG qCDebug(physics) << "ObjectActionTractor::ObjectActionTractor"; #endif @@ -77,7 +79,6 @@ bool ObjectActionTractor::prepareForTractorUpdate(btScalar deltaTimeStep) { glm::quat rotation; glm::vec3 position; - glm::vec3 linearVelocity; glm::vec3 angularVelocity; bool linearValid = false; @@ -117,7 +118,6 @@ bool ObjectActionTractor::prepareForTractorUpdate(btScalar deltaTimeStep) { linearValid = true; linearTractorCount++; position += positionForAction; - linearVelocity += linearVelocityForAction; } } } @@ -126,9 +126,18 @@ bool ObjectActionTractor::prepareForTractorUpdate(btScalar deltaTimeStep) { withWriteLock([&]{ if (linearValid && linearTractorCount > 0) { position /= linearTractorCount; - linearVelocity /= linearTractorCount; + if (_positionalTargetSet) { + _lastPositionTarget = _positionalTarget; + } else { + _lastPositionTarget = position; + } _positionalTarget = position; - _linearVelocityTarget = linearVelocity; + if (deltaTimeStep > EPSILON) { + // blend the new velocity with the old (low-pass filter) + glm::vec3 newVelocity = (1.0f / deltaTimeStep) * (position - _lastPositionTarget); + const float blend = 0.25f; + _linearVelocityTarget = (1.0f - blend) * _linearVelocityTarget + blend * newVelocity; + } _positionalTargetSet = true; _active = true; } @@ -169,19 +178,19 @@ void ObjectActionTractor::updateActionWorker(btScalar deltaTimeStep) { } if (_linearTimeScale < MAX_TRACTOR_TIMESCALE) { - btVector3 targetVelocity(0.0f, 0.0f, 0.0f); + btVector3 offsetVelocity(0.0f, 0.0f, 0.0f); btVector3 offset = rigidBody->getCenterOfMassPosition() - glmToBullet(_positionalTarget); float offsetLength = offset.length(); if (offsetLength > FLT_EPSILON) { float speed = glm::min(offsetLength / _linearTimeScale, TRACTOR_MAX_SPEED); - targetVelocity = (-speed / offsetLength) * offset; + offsetVelocity = (-speed / offsetLength) * offset; if (speed > rigidBody->getLinearSleepingThreshold()) { forceBodyNonStatic(); rigidBody->activate(); } } // this action is aggresively critically damped and defeats the current velocity - rigidBody->setLinearVelocity(targetVelocity); + rigidBody->setLinearVelocity(glmToBullet(_linearVelocityTarget) + offsetVelocity); } if (_angularTimeScale < MAX_TRACTOR_TIMESCALE) { diff --git a/libraries/physics/src/ObjectActionTractor.h b/libraries/physics/src/ObjectActionTractor.h index c629d84998..a17526f5f9 100644 --- a/libraries/physics/src/ObjectActionTractor.h +++ b/libraries/physics/src/ObjectActionTractor.h @@ -36,6 +36,7 @@ protected: glm::vec3 _positionalTarget; glm::vec3 _desiredPositionalTarget; + glm::vec3 _lastPositionTarget; float _linearTimeScale; bool _positionalTargetSet; diff --git a/libraries/plugins/src/plugins/DisplayPlugin.h b/libraries/plugins/src/plugins/DisplayPlugin.h index 297bdb2cca..7bfdbddbc5 100644 --- a/libraries/plugins/src/plugins/DisplayPlugin.h +++ b/libraries/plugins/src/plugins/DisplayPlugin.h @@ -184,6 +184,9 @@ public: // will query the underlying hmd api to compute the most recent head pose virtual bool beginFrameRender(uint32_t frameIndex) { return true; } + // Set the texture to display on the monitor and return true, if allowed. Empty string resets. + virtual bool setDisplayTexture(const QString& name) { return false; } + virtual float devicePixelRatio() { return 1.0f; } // Rate at which we render frames virtual float renderRate() const { return -1.0f; } 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/CauterizedMeshPartPayload.cpp b/libraries/render-utils/src/CauterizedMeshPartPayload.cpp index f3ee846d39..07628904f1 100644 --- a/libraries/render-utils/src/CauterizedMeshPartPayload.cpp +++ b/libraries/render-utils/src/CauterizedMeshPartPayload.cpp @@ -29,7 +29,7 @@ void CauterizedMeshPartPayload::updateTransformForCauterizedMesh( void CauterizedMeshPartPayload::bindTransform(gpu::Batch& batch, const render::ShapePipeline::LocationsPointer locations, RenderArgs::RenderMode renderMode) const { // Still relying on the raw data from the model - bool useCauterizedMesh = (renderMode != RenderArgs::RenderMode::SHADOW_RENDER_MODE); + bool useCauterizedMesh = (renderMode != RenderArgs::RenderMode::SHADOW_RENDER_MODE && renderMode != RenderArgs::RenderMode::SECONDARY_CAMERA_RENDER_MODE); if (useCauterizedMesh) { ModelPointer model = _model.lock(); if (model) { 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.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/LightClusters.cpp b/libraries/render-utils/src/LightClusters.cpp index 7e04b1c2a4..e35120eb5b 100644 --- a/libraries/render-utils/src/LightClusters.cpp +++ b/libraries/render-utils/src/LightClusters.cpp @@ -548,6 +548,7 @@ glm::ivec3 LightClusters::updateClusters() { LightClusteringPass::LightClusteringPass() { + _lightClusters = std::make_shared(); } @@ -566,12 +567,7 @@ void LightClusteringPass::run(const render::RenderContextPointer& renderContext, auto deferredTransform = inputs.get0(); auto lightingModel = inputs.get1(); auto surfaceGeometryFramebuffer = inputs.get2(); - - - if (!_lightClusters) { - _lightClusters = std::make_shared(); - } - + // first update the Grid with the new frustum if (!_freeze) { _lightClusters->updateFrustum(args->getViewFrustum()); 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 8953ea94f0..67452c5d33 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 a0da8a7a8c..3eb796b763 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..a75488ce7a --- /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, SECONDARY_CAMERA_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/render/src/task/Config.h b/libraries/render/src/task/Config.h index 40a3abbd18..7632d4e85d 100644 --- a/libraries/render/src/task/Config.h +++ b/libraries/render/src/task/Config.h @@ -124,6 +124,11 @@ signals: void dirtyEnabled(); }; +class TConfigProxy { +public: + using Config = JobConfig; +}; + class TaskConfig : public JobConfig { Q_OBJECT public: @@ -134,12 +139,37 @@ public: TaskConfig() = default ; TaskConfig(bool enabled) : JobConfig(enabled) {} + + + // Get a sub job config through task.getConfig(path) + // where path can be: + // - search for the first job named job_name traversing the the sub graph of task and jobs (from this task as root) + // - .[.] + // Allowing to first look for the parent_name job (from this task as root) and then search from there for the + // optional sub_parent_names and finally from there looking for the job_name (assuming every job in the path were found) + // // getter for qml integration, prefer the templated getter - Q_INVOKABLE QObject* getConfig(const QString& name) { return QObject::findChild(name); } + Q_INVOKABLE QObject* getConfig(const QString& name) { return getConfig(name.toStdString()); } // getter for cpp (strictly typed), prefer this getter template typename T::Config* getConfig(std::string job = "") const { - QString name = job.empty() ? QString() : QString(job.c_str()); // an empty string is not a null string - return findChild(name); + const TaskConfig* root = this; + QString path = (job.empty() ? QString() : QString(job.c_str())); // an empty string is not a null string + auto tokens = path.split('.', QString::SkipEmptyParts); + + if (tokens.empty()) { + tokens.push_back(QString()); + } else { + while (tokens.size() > 1) { + auto name = tokens.front(); + tokens.pop_front(); + root = QObject::findChild(name); + if (!root) { + return nullptr; + } + } + } + + return root->findChild(tokens.front()); } void connectChildConfig(QConfigPointer childConfig, const std::string& name); diff --git a/libraries/script-engine/src/AudioScriptingInterface.cpp b/libraries/script-engine/src/AudioScriptingInterface.cpp index b5dd60d03b..ecaffaf35c 100644 --- a/libraries/script-engine/src/AudioScriptingInterface.cpp +++ b/libraries/script-engine/src/AudioScriptingInterface.cpp @@ -11,6 +11,8 @@ #include "AudioScriptingInterface.h" +#include + #include "ScriptAudioInjector.h" #include "ScriptEngineLogging.h" @@ -19,6 +21,13 @@ void registerAudioMetaTypes(QScriptEngine* engine) { qScriptRegisterMetaType(engine, soundSharedPointerToScriptValue, soundSharedPointerFromScriptValue); } +ScriptAudioInjector* AudioScriptingInterface::playSystemSound(SharedSoundPointer sound, const QVector3D& position) { + AudioInjectorOptions options; + options.position = glm::vec3(position.x(), position.y(), position.z()); + options.localOnly = true; + return playSound(sound, options); +} + 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 9ee2af7738..23a0861acd 100644 --- a/libraries/script-engine/src/AudioScriptingInterface.h +++ b/libraries/script-engine/src/AudioScriptingInterface.h @@ -30,8 +30,10 @@ public: protected: AudioScriptingInterface() {} - // this method is protected to stop C++ callers from calling, but invokable from script + // these methods are protected to stop C++ callers from calling, but invokable from script Q_INVOKABLE ScriptAudioInjector* playSound(SharedSoundPointer sound, const AudioInjectorOptions& injectorOptions = AudioInjectorOptions()); + // FIXME: there is no way to play a positionless sound + Q_INVOKABLE ScriptAudioInjector* playSystemSound(SharedSoundPointer sound, const QVector3D& position); Q_INVOKABLE void setStereoInput(bool stereo); diff --git a/libraries/script-engine/src/ConsoleScriptingInterface.cpp b/libraries/script-engine/src/ConsoleScriptingInterface.cpp new file mode 100644 index 0000000000..f3ceee63f7 --- /dev/null +++ b/libraries/script-engine/src/ConsoleScriptingInterface.cpp @@ -0,0 +1,182 @@ +// +// ConsoleScriptingInterface.cpp +// libraries/script-engine/src +// +// Created by NeetBhagat on 6/1/17. +// Copyright 2014 High Fidelity, Inc. +// +// ConsoleScriptingInterface is responsible for following functionality +// Printing logs with various tags and grouping on debug Window and Logs/log file. +// Debugging functionalities like Timer start-end, assertion, trace. +// To use these functionalities, use "console" object in Javascript files. +// For examples please refer "scripts/developer/tests/consoleObjectTest.js" +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "ConsoleScriptingInterface.h" +#include "ScriptEngine.h" + +#define INDENTATION 4 // 1 Tab - 4 spaces +const QString LINE_SEPARATOR = "\n "; +const QString SPACE_SEPARATOR = " "; +const QString STACK_TRACE_FORMAT = "\n[Stacktrace]%1%2"; +QList ConsoleScriptingInterface::_groupDetails = QList(); + +QScriptValue ConsoleScriptingInterface::info(QScriptContext* context, QScriptEngine* engine) { + if (ScriptEngine* scriptEngine = qobject_cast(engine)) { + scriptEngine->scriptInfoMessage(appendArguments(context)); + } + return QScriptValue::NullValue; +} + +QScriptValue ConsoleScriptingInterface::log(QScriptContext* context, QScriptEngine* engine) { + QString message = appendArguments(context); + if (_groupDetails.count() == 0) { + if (ScriptEngine* scriptEngine = qobject_cast(engine)) { + scriptEngine->scriptPrintedMessage(message); + } + } else { + logGroupMessage(message, engine); + } + return QScriptValue::NullValue; +} + +QScriptValue ConsoleScriptingInterface::debug(QScriptContext* context, QScriptEngine* engine) { + if (ScriptEngine* scriptEngine = qobject_cast(engine)) { + scriptEngine->scriptPrintedMessage(appendArguments(context)); + } + return QScriptValue::NullValue; +} + +QScriptValue ConsoleScriptingInterface::warn(QScriptContext* context, QScriptEngine* engine) { + if (ScriptEngine* scriptEngine = qobject_cast(engine)) { + scriptEngine->scriptWarningMessage(appendArguments(context)); + } + return QScriptValue::NullValue; +} + +QScriptValue ConsoleScriptingInterface::error(QScriptContext* context, QScriptEngine* engine) { + if (ScriptEngine* scriptEngine = qobject_cast(engine)) { + scriptEngine->scriptErrorMessage(appendArguments(context)); + } + return QScriptValue::NullValue; +} + +QScriptValue ConsoleScriptingInterface::exception(QScriptContext* context, QScriptEngine* engine) { + if (ScriptEngine* scriptEngine = qobject_cast(engine)) { + scriptEngine->scriptErrorMessage(appendArguments(context)); + } + return QScriptValue::NullValue; +} + +void ConsoleScriptingInterface::time(QString labelName) { + _timerDetails.insert(labelName, QDateTime::currentDateTime().toUTC()); + QString message = QString("%1: Timer started").arg(labelName); + if (ScriptEngine* scriptEngine = qobject_cast(engine())) { + scriptEngine->scriptPrintedMessage(message); + } +} + +void ConsoleScriptingInterface::timeEnd(QString labelName) { + if (ScriptEngine* scriptEngine = qobject_cast(engine())) { + if (!_timerDetails.contains(labelName)) { + scriptEngine->scriptErrorMessage("No such label found " + labelName); + return; + } + + if (_timerDetails.value(labelName).isNull()) { + _timerDetails.remove(labelName); + scriptEngine->scriptErrorMessage("Invalid start time for " + labelName); + return; + } + QDateTime _startTime = _timerDetails.value(labelName); + QDateTime _endTime = QDateTime::currentDateTime().toUTC(); + qint64 diffInMS = _startTime.msecsTo(_endTime); + + QString message = QString("%1: %2ms").arg(labelName).arg(QString::number(diffInMS)); + _timerDetails.remove(labelName); + + scriptEngine->scriptPrintedMessage(message); + } +} + +QScriptValue ConsoleScriptingInterface::assertion(QScriptContext* context, QScriptEngine* engine) { + QString message; + bool condition = false; + for (int i = 0; i < context->argumentCount(); i++) { + if (i == 0) { + condition = context->argument(i).toBool(); // accept first value as condition + } else { + message += SPACE_SEPARATOR + context->argument(i).toString(); // accept other parameters as message + } + } + + QString assertionResult; + if (!condition) { + if (message.isEmpty()) { + assertionResult = "Assertion failed"; + } else { + assertionResult = QString("Assertion failed : %1").arg(message); + } + if (ScriptEngine* scriptEngine = qobject_cast(engine)) { + scriptEngine->scriptErrorMessage(assertionResult); + } + } + return QScriptValue::NullValue; +} + +void ConsoleScriptingInterface::trace() { + if (ScriptEngine* scriptEngine = qobject_cast(engine())) { + scriptEngine->scriptPrintedMessage + (QString(STACK_TRACE_FORMAT).arg(LINE_SEPARATOR, + scriptEngine->currentContext()->backtrace().join(LINE_SEPARATOR))); + } +} + +void ConsoleScriptingInterface::clear() { + if (ScriptEngine* scriptEngine = qobject_cast(engine())) { + scriptEngine->clearDebugLogWindow(); + } +} + +QScriptValue ConsoleScriptingInterface::group(QScriptContext* context, QScriptEngine* engine) { + logGroupMessage(context->argument(0).toString(), engine); // accept first parameter as label + _groupDetails.push_back(context->argument(0).toString()); + return QScriptValue::NullValue; +} + +QScriptValue ConsoleScriptingInterface::groupCollapsed(QScriptContext* context, QScriptEngine* engine) { + logGroupMessage(context->argument(0).toString(), engine); // accept first parameter as label + _groupDetails.push_back(context->argument(0).toString()); + return QScriptValue::NullValue; +} + +QScriptValue ConsoleScriptingInterface::groupEnd(QScriptContext* context, QScriptEngine* engine) { + ConsoleScriptingInterface::_groupDetails.removeLast(); + return QScriptValue::NullValue; +} + +QString ConsoleScriptingInterface::appendArguments(QScriptContext* context) { + QString message; + for (int i = 0; i < context->argumentCount(); i++) { + if (i > 0) { + message += SPACE_SEPARATOR; + } + message += context->argument(i).toString(); + } + return message; +} + +void ConsoleScriptingInterface::logGroupMessage(QString message, QScriptEngine* engine) { + int _addSpaces = _groupDetails.count() * INDENTATION; + QString logMessage; + for (int i = 0; i < _addSpaces; i++) { + logMessage.append(SPACE_SEPARATOR); + } + logMessage.append(message); + if (ScriptEngine* scriptEngine = qobject_cast(engine)) { + scriptEngine->scriptPrintedMessage(logMessage); + } +} diff --git a/libraries/script-engine/src/ConsoleScriptingInterface.h b/libraries/script-engine/src/ConsoleScriptingInterface.h new file mode 100644 index 0000000000..444ea93504 --- /dev/null +++ b/libraries/script-engine/src/ConsoleScriptingInterface.h @@ -0,0 +1,56 @@ +// +// ConsoleScriptingInterface.h +// libraries/script-engine/src +// +// Created by NeetBhagat on 6/1/17. +// Copyright 2014 High Fidelity, Inc. +// +// ConsoleScriptingInterface is responsible for following functionality +// Printing logs with various tags and grouping on debug Window and Logs/log file. +// Debugging functionalities like Timer start-end, assertion, trace. +// To use these functionalities, use "console" object in Javascript files. +// For examples please refer "scripts/developer/tests/consoleObjectTest.js" +// +// 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_ConsoleScriptingInterface_h +#define hifi_ConsoleScriptingInterface_h + +#include +#include +#include +#include +#include + +// Scriptable interface of "console" object. Used exclusively in the JavaScript API +class ConsoleScriptingInterface : public QObject, protected QScriptable { + Q_OBJECT +public: + static QScriptValue info(QScriptContext* context, QScriptEngine* engine); + static QScriptValue log(QScriptContext* context, QScriptEngine* engine); + static QScriptValue debug(QScriptContext* context, QScriptEngine* engine); + static QScriptValue warn(QScriptContext* context, QScriptEngine* engine); + static QScriptValue error(QScriptContext* context, QScriptEngine* engine); + static QScriptValue exception(QScriptContext* context, QScriptEngine* engine); + static QScriptValue assertion(QScriptContext* context, QScriptEngine* engine); + static QScriptValue group(QScriptContext* context, QScriptEngine* engine); + static QScriptValue groupCollapsed(QScriptContext* context, QScriptEngine* engine); + static QScriptValue groupEnd(QScriptContext* context, QScriptEngine* engine); + +public slots: + void time(QString labelName); + void timeEnd(QString labelName); + void trace(); + void clear(); + +private: + QHash _timerDetails; + static QList _groupDetails; + static void logGroupMessage(QString message, QScriptEngine* engine); + static QString appendArguments(QScriptContext* context); +}; + +#endif // hifi_ConsoleScriptingInterface_h diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 61bf601019..67b16df1ce 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -472,20 +472,24 @@ void ScriptEngine::scriptErrorMessage(const QString& message) { } void ScriptEngine::scriptWarningMessage(const QString& message) { - qCWarning(scriptengine) << message; + qCWarning(scriptengine) << qPrintable(message); emit warningMessage(message, getFilename()); } void ScriptEngine::scriptInfoMessage(const QString& message) { - qCInfo(scriptengine) << message; + qCInfo(scriptengine) << qPrintable(message); emit infoMessage(message, getFilename()); } void ScriptEngine::scriptPrintedMessage(const QString& message) { - qCDebug(scriptengine) << message; + qCDebug(scriptengine) << qPrintable(message); emit printedMessage(message, getFilename()); } +void ScriptEngine::clearDebugLogWindow() { + emit clearDebugWindow(); +} + // Even though we never pass AnimVariantMap directly to and from javascript, the queued invokeMethod of // callAnimationStateHandler requires that the type be registered. // These two are meaningful, if we ever do want to use them... @@ -668,8 +672,18 @@ void ScriptEngine::init() { registerGlobalObject("Mat4", &_mat4Library); registerGlobalObject("Uuid", &_uuidLibrary); registerGlobalObject("Messages", DependencyManager::get().data()); - registerGlobalObject("File", new FileScriptingInterface(this)); + registerGlobalObject("console", &_consoleScriptingInterface); + registerFunction("console", "info", ConsoleScriptingInterface::info, currentContext()->argumentCount()); + registerFunction("console", "log", ConsoleScriptingInterface::log, currentContext()->argumentCount()); + registerFunction("console", "debug", ConsoleScriptingInterface::debug, currentContext()->argumentCount()); + registerFunction("console", "warn", ConsoleScriptingInterface::warn, currentContext()->argumentCount()); + registerFunction("console", "error", ConsoleScriptingInterface::error, currentContext()->argumentCount()); + registerFunction("console", "exception", ConsoleScriptingInterface::exception, currentContext()->argumentCount()); + registerFunction("console", "assert", ConsoleScriptingInterface::assertion, currentContext()->argumentCount()); + registerFunction("console", "group", ConsoleScriptingInterface::group, 1); + registerFunction("console", "groupCollapsed", ConsoleScriptingInterface::groupCollapsed, 1); + registerFunction("console", "groupEnd", ConsoleScriptingInterface::groupEnd, 0); qScriptRegisterMetaType(this, animVarMapToScriptValue, animVarMapFromScriptValue); qScriptRegisterMetaType(this, resultHandlerToScriptValue, resultHandlerFromScriptValue); @@ -1006,6 +1020,7 @@ void ScriptEngine::run() { emit runningStateChanged(); { + PROFILE_RANGE(script, _fileNameString); evaluate(_scriptContents, _fileNameString); maybeEmitUncaughtException(__FUNCTION__); } @@ -1269,6 +1284,7 @@ void ScriptEngine::timerFired() { // call the associated JS function, if it exists if (timerData.function.isValid()) { + PROFILE_RANGE(script, __FUNCTION__); auto preTimer = p_high_resolution_clock::now(); callWithEnvironment(timerData.definingEntityIdentifier, timerData.definingSandboxURL, timerData.function, timerData.function, QScriptValueList()); auto postTimer = p_high_resolution_clock::now(); diff --git a/libraries/script-engine/src/ScriptEngine.h b/libraries/script-engine/src/ScriptEngine.h index 010cdfbc75..9da8603814 100644 --- a/libraries/script-engine/src/ScriptEngine.h +++ b/libraries/script-engine/src/ScriptEngine.h @@ -41,6 +41,7 @@ #include "ScriptCache.h" #include "ScriptUUID.h" #include "Vec3.h" +#include "ConsoleScriptingInterface.h" #include "SettingHandle.h" class QScriptEngineDebugger; @@ -225,7 +226,7 @@ public: void scriptWarningMessage(const QString& message); void scriptInfoMessage(const QString& message); void scriptPrintedMessage(const QString& message); - + void clearDebugLogWindow(); int getNumRunningEntityScripts() const; bool getEntityScriptDetails(const EntityItemID& entityID, EntityScriptDetails &details) const; @@ -245,6 +246,7 @@ signals: void warningMessage(const QString& message, const QString& scriptName); void infoMessage(const QString& message, const QString& scriptName); void runningStateChanged(); + void clearDebugWindow(); void loadScript(const QString& scriptName, bool isUserLoaded); void reloadScript(const QString& scriptName, bool isUserLoaded); void doneRunning(); @@ -305,6 +307,7 @@ protected: Vec3 _vec3Library; Mat4 _mat4Library; ScriptUUID _uuidLibrary; + ConsoleScriptingInterface _consoleScriptingInterface; std::atomic _isUserLoaded { false }; bool _isReloading { false }; diff --git a/libraries/script-engine/src/ScriptEngines.cpp b/libraries/script-engine/src/ScriptEngines.cpp index 1cb0c117da..69de067d10 100644 --- a/libraries/script-engine/src/ScriptEngines.cpp +++ b/libraries/script-engine/src/ScriptEngines.cpp @@ -54,6 +54,10 @@ void ScriptEngines::onInfoMessage(const QString& message, const QString& scriptN emit infoMessage(message, scriptName); } +void ScriptEngines::onClearDebugWindow() { + emit clearDebugWindow(); +} + void ScriptEngines::onErrorLoadingScript(const QString& url) { emit errorLoadingScript(url); } @@ -529,7 +533,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/ScriptEngines.h b/libraries/script-engine/src/ScriptEngines.h index 5152c3952a..91dc54a0ec 100644 --- a/libraries/script-engine/src/ScriptEngines.h +++ b/libraries/script-engine/src/ScriptEngines.h @@ -79,6 +79,7 @@ signals: void warningMessage(const QString& message, const QString& engineName); void infoMessage(const QString& message, const QString& engineName); void errorLoadingScript(const QString& url); + void clearDebugWindow(); public slots: void onPrintedMessage(const QString& message, const QString& scriptName); @@ -86,6 +87,7 @@ public slots: void onWarningMessage(const QString& message, const QString& scriptName); void onInfoMessage(const QString& message, const QString& scriptName); void onErrorLoadingScript(const QString& url); + void onClearDebugWindow(); protected slots: void onScriptFinished(const QString& fileNameString, ScriptEngine* engine); diff --git a/libraries/script-engine/src/TabletScriptingInterface.cpp b/libraries/script-engine/src/TabletScriptingInterface.cpp index b41c548e57..ef2887fdfb 100644 --- a/libraries/script-engine/src/TabletScriptingInterface.cpp +++ b/libraries/script-engine/src/TabletScriptingInterface.cpp @@ -614,15 +614,6 @@ void TabletProxy::removeButton(QObject* tabletButtonProxy) { } } -void TabletProxy::updateAudioBar(const double micLevel) { - auto tablet = getQmlTablet(); - if (!tablet) { - //qCCritical(scriptengine) << "Could not find tablet in TabletRoot.qml"; - } else { - QMetaObject::invokeMethod(tablet, "setMicLevel", Qt::AutoConnection, Q_ARG(QVariant, QVariant(micLevel))); - } -} - void TabletProxy::emitScriptEvent(QVariant msg) { if (!_toolbarMode && _qmlOffscreenSurface) { QMetaObject::invokeMethod(_qmlOffscreenSurface, "emitScriptEvent", Qt::AutoConnection, Q_ARG(QVariant, msg)); diff --git a/libraries/script-engine/src/TabletScriptingInterface.h b/libraries/script-engine/src/TabletScriptingInterface.h index 2ae9e70846..23d7ecaea4 100644 --- a/libraries/script-engine/src/TabletScriptingInterface.h +++ b/libraries/script-engine/src/TabletScriptingInterface.h @@ -152,13 +152,6 @@ public: */ Q_INVOKABLE void removeButton(QObject* tabletButtonProxy); - /**jsdoc - * Updates the audio bar in tablet to reflect latest mic level - * @function TabletProxy#updateAudioBar - * @param micLevel {double} mic level value between 0 and 1 - */ - Q_INVOKABLE void updateAudioBar(const double micLevel); - /**jsdoc * Used to send an event to the html/js embedded in the tablet * @function TabletProxy#emitScriptEvent 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 72% rename from libraries/networking/src/FileCache.cpp rename to libraries/shared/src/shared/FileCache.cpp index 95304e3866..695336847e 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()) { @@ -112,29 +118,45 @@ void FileCache::initialize() { _initialized = true; } +std::unique_ptr FileCache::createFile(Metadata&& metadata, const std::string& filepath) { + return std::unique_ptr(new cache::File(std::move(metadata), filepath)); +} + FilePointer FileCache::addFile(Metadata&& metadata, const std::string& filepath) { - FilePointer file(createFile(std::move(metadata), filepath).release(), std::mem_fn(&File::deleter)); + File* rawFile = createFile(std::move(metadata), filepath).release(); + FilePointer file(rawFile, std::bind(&File::deleter, rawFile)); if (file) { _numTotalFiles += 1; _totalFilesSize += file->getLength(); - file->_cache = this; + file->_parent = shared_from_this(); + file->_locked = true; 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()); @@ -154,16 +176,19 @@ FilePointer FileCache::writeFile(const char* data, File::Metadata&& metadata, bo } else { qCWarning(file_cache, "[%s] Failed to write %s", _dirname.c_str(), metadata.key.c_str()); } - + assert(!file || (file->_locked && file->_parent.lock())); 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 +197,14 @@ 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)) { + assert(!file->_locked); + file->_locked = true; + _numUnusedFiles -= 1; + _unusedFilesSize -= file->getLength(); + } else { + assert(file->_locked); + } qCDebug(file_cache, "[%s] Found %s", _dirname.c_str(), key.c_str()); emit dirty(); } else { @@ -181,6 +213,7 @@ FilePointer FileCache::getFile(const Key& key) { } } + assert(!file || (file->_locked && file->_parent.lock())); return file; } @@ -188,31 +221,20 @@ 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(); - } + assert(file->_locked); + file->_locked = false; + _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; @@ -237,24 +259,17 @@ namespace cache { } void FileCache::eject(const FilePointer& file) { - file->_cache = nullptr; + file->_locked = false; 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 +281,6 @@ void FileCache::clean() { return; } - Lock unusedLock(_unusedFilesMutex); using Queue = std::priority_queue, FilePointerComparator>; Queue queue; for (const auto& file : _unusedFiles) { @@ -283,34 +297,49 @@ 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; + file->_parent.reset(); qCDebug(file_cache, "[%s] Persisting %s", _dirname.c_str(), file->getKey().c_str()); } _unusedFiles.clear(); } -void File::deleter() { - if (_cache) { - _cache->addUnusedFile(FilePointer(this, std::mem_fn(&File::deleter))); +void FileCache::releaseFile(File* file) { + Lock lock(_mutex); + if (file->_locked) { + addUnusedFile(FilePointer(file, std::bind(&File::deleter, file))); } else { - deleteLater(); + delete file; } } +void File::deleter(File* file) { + // If the cache shut down before the file was destroyed, then we should leave the file alone (prevents crash on shutdown) + FileCachePointer cache = file->_parent.lock(); + if (!cache) { + file->_shouldPersist = true; + delete file; + return; + } + + // Any other operations we might do should be done inside a locked section, so we need to delegate to a FileCache member function + cache->releaseFile(file); +} + File::File(Metadata&& metadata, const std::string& filepath) : _key(std::move(metadata.key)), _length(metadata.length), @@ -329,4 +358,5 @@ 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 88% rename from libraries/networking/src/FileCache.h rename to libraries/shared/src/shared/FileCache.h index f29d75f779..1580674ca0 100644 --- a/libraries/networking/src/FileCache.h +++ b/libraries/shared/src/shared/FileCache.h @@ -24,12 +24,17 @@ Q_DECLARE_LOGGING_CATEGORY(file_cache) +class FileCacheTests; + namespace cache { class File; using FilePointer = std::shared_ptr; +class FileCache; +using FileCachePointer = std::shared_ptr; +using FileCacheWeakPointer = std::weak_ptr; -class FileCache : public QObject { +class FileCache : public QObject, public std::enable_shared_from_this { Q_OBJECT Q_PROPERTY(size_t numTotal READ getNumTotalFiles NOTIFY dirty) Q_PROPERTY(size_t numCached READ getNumCachedFiles NOTIFY dirty) @@ -40,6 +45,8 @@ class FileCache : public QObject { static const size_t MAX_MAX_SIZE; static const size_t DEFAULT_MIN_FREE_STORAGE_SPACE; + friend class ::FileCacheTests; + public: // You can initialize the FileCache with a directory name (ex.: "temp_jpgs") that will be relative to the application local data, OR with a full path // The file cache will ignore any file that doesn't match the extension provided @@ -86,14 +93,14 @@ signals: public: /// must be called after construction to create the cache on the fs and restore persisted files - void initialize(); + virtual void initialize(); // Add file to the cache and return the cache entry. FilePointer writeFile(const char* data, Metadata&& metadata, bool overwrite = false); FilePointer getFile(const Key& key); /// create a file - virtual std::unique_ptr createFile(Metadata&& metadata, const std::string& filepath) = 0; + virtual std::unique_ptr createFile(Metadata&& metadata, const std::string& filepath); private: using Mutex = std::recursive_mutex; @@ -108,7 +115,7 @@ private: FilePointer addFile(Metadata&& metadata, const std::string& filepath); void addUnusedFile(const FilePointer& file); - void removeUnusedFile(const FilePointer& file); + void releaseFile(File* file); void clean(); void clear(); // Remove a file from the cache @@ -125,32 +132,28 @@ 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 { - Q_OBJECT - +class File { public: using Key = FileCache::Key; using Metadata = FileCache::Metadata; 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 - virtual void deleter(); + static void deleter(File* file); protected: /// when constructed, the file has already been created/written @@ -159,14 +162,16 @@ protected: private: friend class FileCache; friend struct FilePointerComparator; + friend class ::FileCacheTests; const Key _key; const size_t _length; const std::string _filepath; void touch(); - FileCache* _cache { nullptr }; + FileCacheWeakPointer _parent; int64_t _modified { 0 }; + bool _locked { false }; bool _shouldPersist { false }; }; 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/libraries/ui/src/QmlWindowClass.cpp b/libraries/ui/src/QmlWindowClass.cpp index 58d39448ac..f5bb880957 100644 --- a/libraries/ui/src/QmlWindowClass.cpp +++ b/libraries/ui/src/QmlWindowClass.cpp @@ -153,6 +153,9 @@ void QmlWindowClass::sendToQml(const QVariant& message) { QMetaObject::invokeMethod(asQuickItem(), "fromScript", Qt::QueuedConnection, Q_ARG(QVariant, message)); } +void QmlWindowClass::clearDebugWindow() { + QMetaObject::invokeMethod(asQuickItem(), "clearDebugWindow", Qt::QueuedConnection); +} void QmlWindowClass::emitScriptEvent(const QVariant& scriptMessage) { if (QThread::currentThread() != thread()) { diff --git a/libraries/ui/src/QmlWindowClass.h b/libraries/ui/src/QmlWindowClass.h index 95777718bf..4f604133a5 100644 --- a/libraries/ui/src/QmlWindowClass.h +++ b/libraries/ui/src/QmlWindowClass.h @@ -53,6 +53,7 @@ public slots: // Scripts can use this to send a message to the QML object void sendToQml(const QVariant& message); + void clearDebugWindow(); // QmlWindow content may include WebView requiring EventBridge. void emitScriptEvent(const QVariant& scriptMessage); 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/gracefulControls.js b/script-archive/gracefulControls.js index ab5de48ae2..91e31cd243 100644 --- a/script-archive/gracefulControls.js +++ b/script-archive/gracefulControls.js @@ -12,16 +12,16 @@ var DEFAULT_PARAMETERS = { // Coefficient to use for linear drag. Higher numbers will cause motion to // slow down more quickly. - DRAG_COEFFICIENT: 0.9, - MAX_SPEED: 40.0, - ACCELERATION: 1.0, + DRAG_COEFFICIENT: 60.0, + MAX_SPEED: 10.0, + ACCELERATION: 10.0, MOUSE_YAW_SCALE: -0.125, MOUSE_PITCH_SCALE: -0.125, MOUSE_SENSITIVITY: 0.5, // Damping frequency, adjust to change mouse look behavior - W: 4.2, + W: 2.2, } var BRAKE_PARAMETERS = { @@ -35,18 +35,29 @@ var BRAKE_PARAMETERS = { MOUSE_SENSITIVITY: 0.5, } +var DRIVE_AVATAR_ENABLED = true; +var UPDATE_RATE = 90; +var USE_INTERVAL = true; + var movementParameters = DEFAULT_PARAMETERS; + // Movement keys -var KEY_BRAKE = "q"; -var KEY_FORWARD = "w"; -var KEY_BACKWARD = "s"; -var KEY_LEFT = "a"; -var KEY_RIGHT = "d"; -var KEY_UP = "e"; -var KEY_DOWN = "c"; -var KEY_ENABLE = "SPACE"; -var CAPTURED_KEYS = [KEY_BRAKE, KEY_FORWARD, KEY_BACKWARD, KEY_LEFT, KEY_RIGHT, KEY_UP, KEY_DOWN, KEY_ENABLE]; +var KEY_BRAKE = "Q"; +var KEY_FORWARD = "W"; +var KEY_BACKWARD = "S"; +var KEY_LEFT = "A"; +var KEY_RIGHT = "D"; +var KEY_UP = "E"; +var KEY_DOWN = "C"; +var KEY_TOGGLE= "Space"; + +var KEYS; +if (DRIVE_AVATAR_ENABLED) { + KEYS = [KEY_BRAKE, KEY_FORWARD, KEY_BACKWARD, KEY_LEFT, KEY_RIGHT, KEY_UP, KEY_DOWN]; +} else { + KEYS = []; +} // Global Variables var keys = {}; @@ -54,66 +65,27 @@ var velocity = { x: 0, y: 0, z: 0 }; var velocityVertical = 0; var enabled = false; -var lastX = Reticle.getPosition().x; -var lastY = Reticle.getPosition().y; +var pos = Reticle.getPosition(); +var lastX = pos.x; +var lastY = pos.y; var yawFromMouse = 0; var pitchFromMouse = 0; var yawSpeed = 0; var pitchSpeed = 0; -function keyPressEvent(event) { - if (event.text == "ESC") { - disable(); - } else if (event.text == KEY_ENABLE) { - if (Window.hasFocus()) { - enable(); - } - } else if (event.text == KEY_BRAKE) { - movementParameters = BRAKE_PARAMETERS; - } - keys[event.text] = true; -} - -function keyReleaseEvent(event) { - if (event.text == KEY_BRAKE) { - movementParameters = DEFAULT_PARAMETERS; - } - - delete keys[event.text]; -} function update(dt) { - var maxMove = 3.0 * dt; - var targetVelocity = { x: 0, y: 0, z: 0 }; - var targetVelocityVertical = 0; - var acceleration = movementParameters.ACCELERATION; - - if (keys[KEY_FORWARD]) { - targetVelocity.z -= acceleration * dt; - } - if (keys[KEY_LEFT]) { - targetVelocity.x -= acceleration * dt; - } - if (keys[KEY_BACKWARD]) { - targetVelocity.z += acceleration * dt; - } - if (keys[KEY_RIGHT]) { - targetVelocity.x += acceleration * dt; - } - if (keys[KEY_UP]) { - targetVelocityVertical += acceleration * dt; - } - if (keys[KEY_DOWN]) { - targetVelocityVertical -= acceleration * dt; - } - if (enabled && Window.hasFocus()) { - var x = Reticle.getPosition().x; - var y = Reticle.getPosition().y; + var pos = Reticle.getPosition(); + var x = pos.x; + var y = pos.y; - yawFromMouse += ((x - lastX) * movementParameters.MOUSE_YAW_SCALE * movementParameters.MOUSE_SENSITIVITY); - pitchFromMouse += ((y - lastY) * movementParameters.MOUSE_PITCH_SCALE * movementParameters.MOUSE_SENSITIVITY); + var dx = x - lastX; + var dy = y - lastY; + + yawFromMouse += (dx * movementParameters.MOUSE_YAW_SCALE * movementParameters.MOUSE_SENSITIVITY); + pitchFromMouse += (dy * movementParameters.MOUSE_PITCH_SCALE * movementParameters.MOUSE_SENSITIVITY); pitchFromMouse = Math.max(-180, Math.min(180, pitchFromMouse)); resetCursorPosition(); @@ -138,48 +110,82 @@ function update(dt) { MyAvatar.headPitch = newPitch; pitchFromMouse -= pitchMove; - // If force isn't being applied in a direction, add drag; - if (targetVelocity.x == 0) { - targetVelocity.x -= (velocity.x * movementParameters.DRAG_COEFFICIENT * dt); - } - if (targetVelocity.z == 0) { - targetVelocity.z -= (velocity.z * movementParameters.DRAG_COEFFICIENT * dt); - } - velocity = Vec3.sum(velocity, targetVelocity); - var maxSpeed = movementParameters.MAX_SPEED; - velocity.x = Math.max(-maxSpeed, Math.min(maxSpeed, velocity.x)); - velocity.z = Math.max(-maxSpeed, Math.min(maxSpeed, velocity.z)); - var v = Vec3.multiplyQbyV(MyAvatar.headOrientation, velocity); + if (DRIVE_AVATAR_ENABLED) { + var targetVelocity = { x: 0, y: 0, z: 0 }; + var targetVelocityVertical = 0; + var acceleration = movementParameters.ACCELERATION; - if (targetVelocityVertical == 0) { - targetVelocityVertical -= (velocityVertical * movementParameters.DRAG_COEFFICIENT * dt); + if (keys[KEY_FORWARD]) { + targetVelocity.z -= acceleration * dt; + } + if (keys[KEY_LEFT]) { + targetVelocity.x -= acceleration * dt; + } + if (keys[KEY_BACKWARD]) { + targetVelocity.z += acceleration * dt; + } + if (keys[KEY_RIGHT]) { + targetVelocity.x += acceleration * dt; + } + if (keys[KEY_UP]) { + targetVelocityVertical += acceleration * dt; + } + if (keys[KEY_DOWN]) { + targetVelocityVertical -= acceleration * dt; + } + + // If force isn't being applied in a direction, add drag; + var drag = Math.max(movementParameters.DRAG_COEFFICIENT * dt, 1.0); + if (targetVelocity.x == 0) { + targetVelocity.x = -velocity.x * drag; + } + if (targetVelocity.z == 0) { + targetVelocity.z = -velocity.z * drag; + } + velocity = Vec3.sum(velocity, targetVelocity); + + var maxSpeed = movementParameters.MAX_SPEED; + velocity.x = Math.max(-maxSpeed, Math.min(maxSpeed, velocity.x)); + velocity.z = Math.max(-maxSpeed, Math.min(maxSpeed, velocity.z)); + var v = Vec3.multiplyQbyV(MyAvatar.headOrientation, velocity); + + if (targetVelocityVertical == 0) { + targetVelocityVertical -= (velocityVertical * movementParameters.DRAG_COEFFICIENT * dt); + } + velocityVertical += targetVelocityVertical; + velocityVertical = Math.max(-maxSpeed, Math.min(maxSpeed, velocityVertical)); + v.y += velocityVertical; + + MyAvatar.motorVelocity = v; } - velocityVertical += targetVelocityVertical; - velocityVertical = Math.max(-maxSpeed, Math.min(maxSpeed, velocityVertical)); - v.y += velocityVertical; - - MyAvatar.setVelocity(v); } function vecToString(vec) { return vec.x + ", " + vec.y + ", " + vec.z; } -function scriptEnding() { - disable(); -} - function resetCursorPosition() { - var newX = Window.x + Window.innerWidth / 2; - var newY = Window.y + Window.innerHeight / 2; - Reticle.setPosition({ x: newX, y: newY}); + var newX = Math.floor(Window.innerWidth / 2); + var newY = Math.floor(Window.innerHeight / 2); + Reticle.setPosition({ x: newX, y: newY }); lastX = newX; lastY = newY; } + +function toggleEnabled() { + if (enabled) { + disable(); + } else { + enable(); + } +} + + +var timerID = null; function enable() { - if (!enabled) { + if (!enabled && Window.hasFocus()) { enabled = true; resetCursorPosition(); @@ -190,27 +196,94 @@ function enable() { yawSpeed = 0; pitchSpeed = 0; velocityVertical = 0; + velocity = { x: 0, y: 0, z: 0 }; + + MyAvatar.motorReferenceFrame = 'world'; + MyAvatar.motorVelocity = { x: 0, y: 0, z: 0 }; + MyAvatar.motorTimescale = 1; + + Controller.enableMapping(MAPPING_KEYS_NAME); - for (var i = 0; i < CAPTURED_KEYS.length; i++) { - Controller.captureKeyEvents({ text: CAPTURED_KEYS[i] }); - } Reticle.setVisible(false); - Script.update.connect(update); + if (USE_INTERVAL) { + var lastTime = Date.now(); + timerID = Script.setInterval(function() { + var now = Date.now(); + var dt = now - lastTime; + lastTime = now; + update(dt / 1000); + }, (1.0 / UPDATE_RATE) * 1000); + } else { + Script.update.connect(update); + } } } function disable() { if (enabled) { enabled = false; - for (var i = 0; i < CAPTURED_KEYS.length; i++) { - Controller.releaseKeyEvents({ text: CAPTURED_KEYS[i] }); - } Reticle.setVisible(true); - Script.update.disconnect(update); + + MyAvatar.motorVelocity = { x: 0, y: 0, z: 0 }; + + Controller.disableMapping(MAPPING_KEYS_NAME); + + if (USE_INTERVAL) { + Script.clearInterval(timerID); + timerID = null; + } else { + Script.update.disconnect(update); + } } } -Controller.keyPressEvent.connect(keyPressEvent); -Controller.keyReleaseEvent.connect(keyReleaseEvent); +function scriptEnding() { + disable(); + Controller.disableMapping(MAPPING_ENABLE_NAME); + Controller.disableMapping(MAPPING_KEYS_NAME); +} + + +var MAPPING_ENABLE_NAME = 'io.highfidelity.gracefulControls.toggle'; +var MAPPING_KEYS_NAME = 'io.highfidelity.gracefulControls.keys'; +var keyControllerMapping = Controller.newMapping(MAPPING_KEYS_NAME); +var enableControllerMapping = Controller.newMapping(MAPPING_ENABLE_NAME); + +function onKeyPress(key, value) { + print(key, value); + keys[key] = value > 0; + + if (value > 0) { + if (key == KEY_TOGGLE) { + toggleEnabled(); + } else if (key == KEY_BRAKE) { + movementParameters = BRAKE_PARAMETERS; + } + } else { + if (key == KEY_BRAKE) { + movementParameters = DEFAULT_PARAMETERS; + } + } +} + +for (var i = 0; i < KEYS.length; ++i) { + var key = KEYS[i]; + var hw = Controller.Hardware.Keyboard[key]; + if (hw) { + keyControllerMapping.from(hw).to(function(key) { + return function(value) { + onKeyPress(key, value); + }; + }(key)); + } else { + print("Unknown key: ", key); + } +} + +enableControllerMapping.from(Controller.Hardware.Keyboard[KEY_TOGGLE]).to(function(value) { + onKeyPress(KEY_TOGGLE, value); +}); + +Controller.enableMapping(MAPPING_ENABLE_NAME); Script.scriptEnding.connect(scriptEnding); 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/developer/debugging/debugWindow.js b/scripts/developer/debugging/debugWindow.js index 30a050e667..6dd116089a 100644 --- a/scripts/developer/debugging/debugWindow.js +++ b/scripts/developer/debugging/debugWindow.js @@ -49,4 +49,8 @@ ScriptDiscoveryService.infoMessage.connect(function(message, scriptFileName) { sendToLogWindow("INFO", message, scriptFileName); }); -}()); // END LOCAL_SCOPE \ No newline at end of file +ScriptDiscoveryService.clearDebugWindow.connect(function() { + window.clearDebugWindow(); +}); + +}()); diff --git a/scripts/developer/debugging/debugWindow.qml b/scripts/developer/debugging/debugWindow.qml index 20fa24358d..2370803335 100644 --- a/scripts/developer/debugging/debugWindow.qml +++ b/scripts/developer/debugging/debugWindow.qml @@ -40,6 +40,10 @@ Rectangle { } textArea.append(message); } + + function clearWindow() { + textArea.remove(0,textArea.length); + } } diff --git a/scripts/developer/tests/consoleObjectTest.js b/scripts/developer/tests/consoleObjectTest.js new file mode 100644 index 0000000000..a59652dc09 --- /dev/null +++ b/scripts/developer/tests/consoleObjectTest.js @@ -0,0 +1,114 @@ +// Examples and understanding of console object. Include console methods like +// info, log, debug, warn, error, exception, trace, clear, asserts, group, groupCollapsed, groupEnd, time, timeEnd. +// Useful in debugging and exclusively made for JavaScript files. +// To view the logs click on Developer -> script logs [logs on debug window] and for text file logs go to Logs/log file. + +main(); + +function main() { + + var someObject = { str: "Some text", id: 5 }; + var someValue = 5; + + // console.info examples + console.info("[console.info] Hello World."); + console.info(5 + 6); + console.info(someObject.str); + console.info(a = 2 * 6); + console.info(someValue); + console.info('someObject id ' + someObject.id); + console.info('Hello World ', 'someObject.id = ', someObject.id, "a = 2 * 6 = ", a = 2 * 6); + + // console.log examples + console.log("[console.log] Hello World"); + console.log(5 + 6); + console.log(someObject.str); + console.log(a = 2 * 6); + console.log(someValue); + console.log('someObject id ' + someObject.id); + console.log('Hello World ', 'someObject.id = ', someObject.id, "a = 2 * 6 = ", a = 2 * 6); + + // console.debug examples + console.debug("[console.debug] Hello World."); + console.debug(5 + 6); + console.debug(someObject.str); + console.debug(a = 2 * 6); + console.debug(someValue); + console.debug('someObject id ' + someObject.id); + console.debug('Hello World ', 'someObject.id = ', someObject.id, "a = 2 * 6 = ", a = 2 * 6); + + // console.warn examples + console.warn("[console.warn] This is warning message."); + console.warn(5 + 6); + console.warn(someObject.str); + console.warn(a = 2 * 6); + console.warn(someValue); + console.warn('someObject id ' + someObject.id); + console.warn('Hello World ', 'someObject.id = ', someObject.id, "a = 2 * 6 = ", a = 2 * 6); + + // console.error examples + console.error('An error occurred!'); + console.error('An error occurred! ', 'Value = ', someValue); + console.error('An error occurred! ' + 'Value = ' + someValue); + + // console.exception examples + console.exception('An exception occurred!'); + console.exception('An exception occurred! ', 'Value = ', someValue); + console.exception('An exception occurred! ' + 'Value = ' + someValue); + + // console.trace examples + function fooA() { + function fooB() { + function fooC() { + console.trace(); + } + fooC(); + } + fooB(); + } + fooA(); + + // console.assert() examples + var valA = 1, valB = "1"; + console.assert(valA === valB, "Value A doesn't equal to B"); + console.assert(valA === valB); + console.assert(5 === 5, "5 equals to 5"); + console.assert(5 === 5); + console.assert(5 > 6, "5 is not greater than 6"); + console.assert(5 > 6, "5 is not greater than 6", "This assertion will fail"); + + // console.group() examples. + console.group("Group 1"); + console.log("Sentence 1"); + console.log("Sentence 2"); + console.group("Group 2"); + console.log("Sentence 3"); + console.log("Sentence 4"); + console.groupCollapsed("Group 3"); + console.log("Sentence 5"); + console.log("Sentence 6"); + console.groupEnd(); + console.log("Sentence 7"); + console.groupEnd(); + console.log("Sentence 8"); + console.groupEnd(); + console.log("Sentence 9"); + + // console.time(),console.timeEnd() examples + console.time('MyTimer'); + // Do some process + sleep(1000); + console.timeEnd('MyTimer'); + + // use console.clear() to clean Debug Window logs + // console.clear(); +} + +function sleep(milliseconds) { + var start = new Date().getTime(); + for (var i = 0; i < 1e7; i++) { + if ((new Date().getTime() - start) > milliseconds){ + break; + } + } +} diff --git a/scripts/developer/utilities/lib/plotperf/PlotPerf.qml b/scripts/developer/utilities/lib/plotperf/PlotPerf.qml index 13d9053adf..916c9cae55 100644 --- a/scripts/developer/utilities/lib/plotperf/PlotPerf.qml +++ b/scripts/developer/utilities/lib/plotperf/PlotPerf.qml @@ -49,11 +49,15 @@ Item { property var valueMax : 1 - property var _values : new Array() + property var _values property var tick : 0 function createValues() { + if (!_values) { + _values = new Array(); + } for (var i =0; i < plots.length; i++) { + var plot = plots[i]; var object = plot["object"] || root.object; var value = plot["prop"]; @@ -93,7 +97,7 @@ Item { var VALUE_HISTORY_SIZE = 100; tick++; - + var currentValueMax = 0 for (var i = 0; i < _values.length; i++) { @@ -128,7 +132,6 @@ Item { if ((valueMax < currentValueMax) || (tick % VALUE_HISTORY_SIZE == 0)) { valueMax = currentValueMax; } - mycanvas.requestPaint() } diff --git a/scripts/developer/utilities/render/ambientOcclusionPass.qml b/scripts/developer/utilities/render/ambientOcclusionPass.qml index 3ebc80d2f1..08334cf2aa 100644 --- a/scripts/developer/utilities/render/ambientOcclusionPass.qml +++ b/scripts/developer/utilities/render/ambientOcclusionPass.qml @@ -32,7 +32,7 @@ Column { ConfigSlider { label: qsTr(modelData.split(":")[0]) integral: (modelData.split(":")[3] == 'true') - config: Render.getConfig("AmbientOcclusion") + config: Render.getConfig("RenderMainView.AmbientOcclusion") property: modelData.split(":")[1] max: modelData.split(":")[2] min: 0.0 @@ -50,8 +50,8 @@ Column { ] CheckBox { text: qsTr(modelData.split(":")[0]) - checked: Render.getConfig("AmbientOcclusion")[modelData.split(":")[1]] - onCheckedChanged: { Render.getConfig("AmbientOcclusion")[modelData.split(":")[1]] = checked } + checked: Render.getConfig("RenderMainView.AmbientOcclusion")[modelData.split(":")[1]] + onCheckedChanged: { Render.getConfig("RenderMainView.AmbientOcclusion")[modelData.split(":")[1]] = checked } } } } @@ -62,8 +62,8 @@ Column { ] CheckBox { text: qsTr(modelData.split(":")[0]) - checked: Render.getConfig("DebugAmbientOcclusion")[modelData.split(":")[1]] - onCheckedChanged: { Render.getConfig("DebugAmbientOcclusion")[modelData.split(":")[1]] = checked } + checked: Render.getConfig("RenderMainView.DebugAmbientOcclusion")[modelData.split(":")[1]] + onCheckedChanged: { Render.getConfig("RenderMainView.DebugAmbientOcclusion")[modelData.split(":")[1]] = checked } } } } @@ -72,7 +72,7 @@ Column { PlotPerf { title: "Timing" height: 50 - object: Render.getConfig("AmbientOcclusion") + object: Render.getConfig("RenderMainView.AmbientOcclusion") valueUnit: "ms" valueScale: 1 valueNumDigits: "3" diff --git a/scripts/developer/utilities/render/culling.qml b/scripts/developer/utilities/render/culling.qml index e3f5e67bbe..2ce3cc1dea 100644 --- a/scripts/developer/utilities/render/culling.qml +++ b/scripts/developer/utilities/render/culling.qml @@ -14,8 +14,8 @@ import "configSlider" Column { id: root spacing: 8 - property var sceneOctree: Render.getConfig("DrawSceneOctree"); - property var itemSelection: Render.getConfig("DrawItemSelection"); + property var sceneOctree: Render.getConfig("RenderMainView.DrawSceneOctree"); + property var itemSelection: Render.getConfig("RenderMainView.DrawItemSelection"); Component.onCompleted: { sceneOctree.enabled = true; @@ -30,8 +30,8 @@ Column { Component.onDestruction: { sceneOctree.enabled = false; itemSelection.enabled = false; - Render.getConfig("FetchSceneSelection").freezeFrustum = false; - Render.getConfig("CullSceneSelection").freezeFrustum = false; + Render.getConfig("RenderMainView.FetchSceneSelection").freezeFrustum = false; + Render.getConfig("RenderMainView.CullSceneSelection").freezeFrustum = false; } GroupBox { @@ -45,8 +45,8 @@ Column { text: "Freeze Culling Frustum" checked: false onCheckedChanged: { - Render.getConfig("FetchSceneSelection").freezeFrustum = checked; - Render.getConfig("CullSceneSelection").freezeFrustum = checked; + Render.getConfig("RenderMainView.FetchSceneSelection").freezeFrustum = checked; + Render.getConfig("RenderMainView.CullSceneSelection").freezeFrustum = checked; } } Label { @@ -98,8 +98,8 @@ Column { Column{ Repeater { - model: [ "Opaque:DrawOpaqueDeferred", "Transparent:DrawTransparentDeferred", "Light:DrawLight", - "Opaque Overlays:DrawOverlay3DOpaque", "Transparent Overlays:DrawOverlay3DTransparent" ] + model: [ "Opaque:RenderMainView.DrawOpaqueDeferred", "Transparent:RenderMainView.DrawTransparentDeferred", "Light:RenderMainView.DrawLight", + "Opaque Overlays:RenderMainView.DrawOverlay3DOpaque", "Transparent Overlays:RenderMainView.DrawOverlay3DTransparent" ] ConfigSlider { label: qsTr(modelData.split(":")[0]) integral: true diff --git a/scripts/developer/utilities/render/debugAmbientOcclusionPass.js b/scripts/developer/utilities/render/debugAmbientOcclusionPass.js index c57fdf0526..f70b3a5cc9 100644 --- a/scripts/developer/utilities/render/debugAmbientOcclusionPass.js +++ b/scripts/developer/utilities/render/debugAmbientOcclusionPass.js @@ -13,7 +13,7 @@ var qml = Script.resolvePath('ambientOcclusionPass.qml'); var window = new OverlayWindow({ title: 'Ambient Occlusion Pass', source: qml, - width: 400, height: 250, + width: 400, height: 300, }); window.setPosition(Window.innerWidth - 420, 50 + 550 + 50); window.closed.connect(function() { Script.stop(); }); @@ -34,5 +34,5 @@ function setDebugCursor(x, y) { nx = (x / Window.innerWidth); ny = 1.0 - ((y) / (Window.innerHeight - 32)); - Render.getConfig("DebugAmbientOcclusion").debugCursorTexcoord = { x: nx, y: ny }; + Render.getConfig("RenderMainView").getConfig("DebugAmbientOcclusion").debugCursorTexcoord = { x: nx, y: ny }; } diff --git a/scripts/developer/utilities/render/debugDeferredLighting.js b/scripts/developer/utilities/render/debugDeferredLighting.js index bffa3a2e15..2a9b1a1067 100644 --- a/scripts/developer/utilities/render/debugDeferredLighting.js +++ b/scripts/developer/utilities/render/debugDeferredLighting.js @@ -13,7 +13,7 @@ var qml = Script.resolvePath('deferredLighting.qml'); var window = new OverlayWindow({ title: 'Lighting', source: qml, - width: 400, height:350, + width: 400, height:400, }); window.setPosition(Window.innerWidth - 420, 50); window.closed.connect(function() { Script.stop(); }); diff --git a/scripts/developer/utilities/render/debugSubsurfaceScattering.js b/scripts/developer/utilities/render/debugSubsurfaceScattering.js index 72b15546e0..c578ef0f06 100644 --- a/scripts/developer/utilities/render/debugSubsurfaceScattering.js +++ b/scripts/developer/utilities/render/debugSubsurfaceScattering.js @@ -33,5 +33,5 @@ function setDebugCursor(x, y) { nx = (x / Window.innerWidth); ny = 1.0 - ((y) / (Window.innerHeight - 32)); - Render.getConfig("DebugScattering").debugCursorTexcoord = { x: nx, y: ny }; + Render.getConfig("RenderMainView").getConfig("DebugScattering").debugCursorTexcoord = { x: nx, y: ny }; } diff --git a/scripts/developer/utilities/render/deferredLighting.qml b/scripts/developer/utilities/render/deferredLighting.qml index ff4621a87a..2254b6d95f 100644 --- a/scripts/developer/utilities/render/deferredLighting.qml +++ b/scripts/developer/utilities/render/deferredLighting.qml @@ -13,6 +13,7 @@ import "configSlider" Column { spacing: 8 + property var mainViewTask: Render.getConfig("RenderMainView") Row { spacing: 8 @@ -29,8 +30,8 @@ Column { ] CheckBox { text: modelData.split(":")[0] - checked: Render.getConfig(modelData.split(":")[1])[modelData.split(":")[2]] - onCheckedChanged: { Render.getConfig(modelData.split(":")[1])[modelData.split(":")[2]] = checked } + checked: mainViewTask.getConfig(modelData.split(":")[1])[modelData.split(":")[2]] + onCheckedChanged: { mainViewTask.getConfig(modelData.split(":")[1])[modelData.split(":")[2]] = checked } } } } @@ -49,8 +50,8 @@ Column { ] CheckBox { text: modelData.split(":")[0] - checked: Render.getConfig(modelData.split(":")[1])[modelData.split(":")[2]] - onCheckedChanged: { Render.getConfig(modelData.split(":")[1])[modelData.split(":")[2]] = checked } + checked: mainViewTask.getConfig(modelData.split(":")[1])[modelData.split(":")[2]] + onCheckedChanged: { mainViewTask.getConfig(modelData.split(":")[1])[modelData.split(":")[2]] = checked } } } } @@ -69,8 +70,8 @@ Column { ] CheckBox { text: modelData.split(":")[0] - checked: Render.getConfig(modelData.split(":")[1])[modelData.split(":")[2]] - onCheckedChanged: { Render.getConfig(modelData.split(":")[1])[modelData.split(":")[2]] = checked } + checked: mainViewTask.getConfig(modelData.split(":")[1])[modelData.split(":")[2]] + onCheckedChanged: { mainViewTask.getConfig(modelData.split(":")[1])[modelData.split(":")[2]] = checked } } } } @@ -83,7 +84,7 @@ Column { ConfigSlider { label: qsTr(modelData.split(":")[0]) integral: false - config: Render.getConfig(modelData.split(":")[1]) + config: mainViewTask.getConfig(modelData.split(":")[1]) property: modelData.split(":")[2] max: modelData.split(":")[3] min: modelData.split(":")[4] @@ -107,7 +108,7 @@ Column { ListElement { text: "Filmic"; color: "White" } } width: 200 - onCurrentIndexChanged: { Render.getConfig("ToneMapping")["curve"] = currentIndex } + onCurrentIndexChanged: { mainViewTask.getConfig("ToneMapping")["curve"] = currentIndex } } } } @@ -120,7 +121,7 @@ Column { anchors.left: root.left } - property var config: Render.getConfig("DebugDeferredBuffer") + property var config: mainViewTask.getConfig("DebugDeferredBuffer") function setDebugMode(mode) { framebuffer.config.enabled = (mode != 0); @@ -168,40 +169,40 @@ Column { CheckBox { text: "Opaques" - checked: Render.getConfig("DrawOpaqueBounds")["enabled"] - onCheckedChanged: { Render.getConfig("DrawOpaqueBounds")["enabled"] = checked } + checked: mainViewTask.getConfig("DrawOpaqueBounds")["enabled"] + onCheckedChanged: { mainViewTask.getConfig("DrawOpaqueBounds")["enabled"] = checked } } CheckBox { text: "Transparents" - checked: Render.getConfig("DrawTransparentBounds")["enabled"] - onCheckedChanged: { Render.getConfig("DrawTransparentBounds")["enabled"] = checked } + checked: mainViewTask.getConfig("DrawTransparentBounds")["enabled"] + onCheckedChanged: { mainViewTask.getConfig("DrawTransparentBounds")["enabled"] = checked } } CheckBox { text: "Overlay Opaques" - checked: Render.getConfig("DrawOverlayOpaqueBounds")["enabled"] - onCheckedChanged: { Render.getConfig("DrawOverlayOpaqueBounds")["enabled"] = checked } + checked: mainViewTask.getConfig("DrawOverlayOpaqueBounds")["enabled"] + onCheckedChanged: { mainViewTask.getConfig("DrawOverlayOpaqueBounds")["enabled"] = checked } } CheckBox { text: "Overlay Transparents" - checked: Render.getConfig("DrawOverlayTransparentBounds")["enabled"] - onCheckedChanged: { Render.getConfig("DrawOverlayTransparentBounds")["enabled"] = checked } + checked: mainViewTask.getConfig("DrawOverlayTransparentBounds")["enabled"] + onCheckedChanged: { mainViewTask.getConfig("DrawOverlayTransparentBounds")["enabled"] = checked } } } Column { CheckBox { text: "Metas" - checked: Render.getConfig("DrawMetaBounds")["enabled"] - onCheckedChanged: { Render.getConfig("DrawMetaBounds")["enabled"] = checked } + checked: mainViewTask.getConfig("DrawMetaBounds")["enabled"] + onCheckedChanged: { mainViewTask.getConfig("DrawMetaBounds")["enabled"] = checked } } CheckBox { text: "Lights" - checked: Render.getConfig("DrawLightBounds")["enabled"] - onCheckedChanged: { Render.getConfig("DrawLightBounds")["enabled"] = checked; } + checked: mainViewTask.getConfig("DrawLightBounds")["enabled"] + onCheckedChanged: { mainViewTask.getConfig("DrawLightBounds")["enabled"] = checked; } } CheckBox { text: "Zones" - checked: Render.getConfig("DrawZones")["enabled"] - onCheckedChanged: { Render.getConfig("ZoneRenderer")["enabled"] = checked; Render.getConfig("DrawZones")["enabled"] = checked; } + checked: mainViewTask.getConfig("DrawZones")["enabled"] + onCheckedChanged: { mainViewTask.getConfig("ZoneRenderer")["enabled"] = checked; mainViewTask.getConfig("DrawZones")["enabled"] = checked; } } } } diff --git a/scripts/developer/utilities/render/lightClustering.js b/scripts/developer/utilities/render/lightClustering.js index 7fb9416d40..f81110b16e 100644 --- a/scripts/developer/utilities/render/lightClustering.js +++ b/scripts/developer/utilities/render/lightClustering.js @@ -15,7 +15,7 @@ var window = new OverlayWindow({ title: 'Light Clustering', source: qml, width: 400, - height: 300 + height: 400 }); window.setPosition(Window.innerWidth - 420, 50 + 250 + 50 + 250 + 50 ); window.closed.connect(function() { Script.stop(); }); \ No newline at end of file diff --git a/scripts/developer/utilities/render/lightClustering.qml b/scripts/developer/utilities/render/lightClustering.qml index 4db7aa8c39..69cf1a6064 100644 --- a/scripts/developer/utilities/render/lightClustering.qml +++ b/scripts/developer/utilities/render/lightClustering.qml @@ -22,13 +22,13 @@ Column { PlotPerf { title: "Light CLustering Timing" height: 50 - object: Render.getConfig("LightClustering") + object: Render.getConfig("RenderMainView.LightClustering") valueUnit: "ms" valueScale: 1 valueNumDigits: "4" plots: [ { - object: Render.getConfig("LightClustering"), + object: Render.getConfig("RenderMainView.LightClustering"), prop: "cpuRunTime", label: "time", scale: 1, @@ -40,19 +40,19 @@ Column { PlotPerf { title: "Lights" height: 50 - object: Render.getConfig("LightClustering") + object: Render.getConfig("RenderMainView.LightClustering") valueUnit: "" valueScale: 1 valueNumDigits: "0" plots: [ { - object: Render.getConfig("LightClustering"), + object: Render.getConfig("RenderMainView.LightClustering"), prop: "numClusteredLights", label: "visible", color: "#D959FE" }, { - object: Render.getConfig("LightClustering"), + object: Render.getConfig("RenderMainView.LightClustering"), prop: "numInputLights", label: "input", color: "#FED959" @@ -63,25 +63,25 @@ Column { PlotPerf { title: "Scene Lights" height: 80 - object: Render.getConfig("LightClustering") + object: Render.getConfig("RenderMainView.LightClustering") valueUnit: "" valueScale: 1 valueNumDigits: "0" plots: [ { - object: Render.getConfig("LightClustering"), + object: Render.getConfig("RenderMainView.LightClustering"), prop: "numSceneLights", label: "current", color: "#00B4EF" }, { - object: Render.getConfig("LightClustering"), + object: Render.getConfig("RenderMainView.LightClustering"), prop: "numFreeSceneLights", label: "free", color: "#1AC567" }, { - object: Render.getConfig("LightClustering"), + object: Render.getConfig("RenderMainView.LightClustering"), prop: "numAllocatedSceneLights", label: "allocated", color: "#9495FF" @@ -92,7 +92,7 @@ Column { ConfigSlider { label: qsTr("Range Near [m]") integral: false - config: Render.getConfig("LightClustering") + config: Render.getConfig("RenderMainView.LightClustering") property: "rangeNear" max: 20.0 min: 0.1 @@ -100,7 +100,7 @@ Column { ConfigSlider { label: qsTr("Range Far [m]") integral: false - config: Render.getConfig("LightClustering") + config: Render.getConfig("RenderMainView.LightClustering") property: "rangeFar" max: 500.0 min: 100.0 @@ -108,7 +108,7 @@ Column { ConfigSlider { label: qsTr("Grid X") integral: true - config: Render.getConfig("LightClustering") + config: Render.getConfig("RenderMainView.LightClustering") property: "dimX" max: 32 min: 1 @@ -116,7 +116,7 @@ Column { ConfigSlider { label: qsTr("Grid Y") integral: true - config: Render.getConfig("LightClustering") + config: Render.getConfig("RenderMainView.LightClustering") property: "dimY" max: 32 min: 1 @@ -124,33 +124,33 @@ Column { ConfigSlider { label: qsTr("Grid Z") integral: true - config: Render.getConfig("LightClustering") + config: Render.getConfig("RenderMainView.LightClustering") property: "dimZ" max: 31 min: 1 } CheckBox { text: "Freeze" - checked: Render.getConfig("LightClustering")["freeze"] - onCheckedChanged: { Render.getConfig("LightClustering")["freeze"] = checked } + checked: Render.getConfig("RenderMainView.LightClustering")["freeze"] + onCheckedChanged: { Render.getConfig("RenderMainView.LightClustering")["freeze"] = checked } } CheckBox { text: "Draw Grid" - checked: Render.getConfig("DebugLightClusters")["doDrawGrid"] - onCheckedChanged: { Render.getConfig("DebugLightClusters")["doDrawGrid"] = checked } + checked: Render.getConfig("RenderMainView.DebugLightClusters")["doDrawGrid"] + onCheckedChanged: { Render.getConfig("RenderMainView.DebugLightClusters")["doDrawGrid"] = checked } } CheckBox { text: "Draw Cluster From Depth" - checked: Render.getConfig("DebugLightClusters")["doDrawClusterFromDepth"] - onCheckedChanged: { Render.getConfig("DebugLightClusters")["doDrawClusterFromDepth"] = checked } + checked: Render.getConfig("RenderMainView.DebugLightClusters")["doDrawClusterFromDepth"] + onCheckedChanged: { Render.getConfig("RenderMainView.DebugLightClusters")["doDrawClusterFromDepth"] = checked } } CheckBox { text: "Draw Content" - checked: Render.getConfig("DebugLightClusters")["doDrawContent"] - onCheckedChanged: { Render.getConfig("DebugLightClusters")["doDrawContent"] = checked } + checked: Render.getConfig("RenderMainView.DebugLightClusters")["doDrawContent"] + onCheckedChanged: { Render.getConfig("RenderMainView.DebugLightClusters")["doDrawContent"] = checked } } Label { - text: "Num Cluster Items = " + Render.getConfig("LightClustering")["numClusteredLightReferences"].toFixed(0) + text: "Num Cluster Items = " + Render.getConfig("RenderMainView.LightClustering")["numClusteredLightReferences"].toFixed(0) } } diff --git a/scripts/developer/utilities/render/renderStats.js b/scripts/developer/utilities/render/renderStats.js index d2904c06ba..965431ab3b 100644 --- a/scripts/developer/utilities/render/renderStats.js +++ b/scripts/developer/utilities/render/renderStats.js @@ -14,8 +14,8 @@ var qml = Script.resolvePath('stats.qml'); var window = new OverlayWindow({ title: 'Render Stats', source: qml, - width: 320, - height: 720 + width: 400, + height: 400 }); window.setPosition(500, 50); window.closed.connect(function() { Script.stop(); }); \ No newline at end of file diff --git a/scripts/developer/utilities/render/stats.qml b/scripts/developer/utilities/render/stats.qml index 54e0dc4ce8..25c108884a 100644 --- a/scripts/developer/utilities/render/stats.qml +++ b/scripts/developer/utilities/render/stats.qml @@ -28,91 +28,6 @@ Item { return (height - spacing * (children.length - 1)) / children.length } - PlotPerf { - title: "Num Buffers" - height: parent.evalEvenHeight() - object: stats.config - plots: [ - { - prop: "bufferCPUCount", - label: "CPU", - color: "#00B4EF" - }, - { - prop: "bufferGPUCount", - label: "GPU", - color: "#1AC567" - } - ] - } - PlotPerf { - title: "gpu::Buffer Memory" - height: parent.evalEvenHeight() - object: stats.config - valueScale: 1048576 - valueUnit: "Mb" - valueNumDigits: "1" - plots: [ - { - prop: "bufferCPUMemoryUsage", - label: "CPU", - color: "#00B4EF" - }, - { - prop: "bufferGPUMemoryUsage", - label: "GPU", - color: "#1AC567" - } - ] - } - PlotPerf { - title: "Num Textures" - height: parent.evalEvenHeight() - object: stats.config - plots: [ - { - prop: "textureCPUCount", - label: "CPU", - color: "#00B4EF" - }, - { - prop: "textureGPUCount", - label: "GPU", - color: "#1AC567" - }, - { - prop: "texturePendingGPUTransferCount", - label: "Transfer", - color: "#9495FF" - } - ] - } - PlotPerf { - title: "gpu::Texture Memory" - height: parent.evalEvenHeight() - object: stats.config - valueScale: 1048576 - valueUnit: "Mb" - valueNumDigits: "1" - plots: [ - { - prop: "textureCPUMemoryUsage", - label: "CPU", - color: "#00B4EF" - }, - { - prop: "textureGPUMemoryUsage", - label: "GPU", - color: "#1AC567" - }, - { - prop: "textureGPUVirtualMemoryUsage", - label: "GPU Virtual", - color: "#9495FF" - } - ] - } - PlotPerf { title: "Triangles" height: parent.evalEvenHeight() @@ -182,9 +97,9 @@ Item { ] } - property var drawOpaqueConfig: Render.getConfig("DrawOpaqueDeferred") - property var drawTransparentConfig: Render.getConfig("DrawTransparentDeferred") - property var drawLightConfig: Render.getConfig("DrawLight") + property var drawOpaqueConfig: Render.getConfig("RenderMainView.DrawOpaqueDeferred") + property var drawTransparentConfig: Render.getConfig("RenderMainView.DrawTransparentDeferred") + property var drawLightConfig: Render.getConfig("RenderMainView.DrawLight") PlotPerf { title: "Items" @@ -199,13 +114,13 @@ Item { color: "#1AC567" }, { - object: Render.getConfig("DrawTransparentDeferred"), + object: Render.getConfig("RenderMainView.DrawTransparentDeferred"), prop: "numDrawn", label: "Translucents", color: "#00B4EF" }, { - object: Render.getConfig("DrawLight"), + object: Render.getConfig("RenderMainView.DrawLight"), prop: "numDrawn", label: "Lights", color: "#FED959" @@ -222,25 +137,25 @@ Item { valueNumDigits: "2" plots: [ { - object: Render.getConfig("DrawOpaqueDeferred"), + object: Render.getConfig("RenderMainView.DrawOpaqueDeferred"), prop: "cpuRunTime", label: "Opaques", color: "#1AC567" }, { - object: Render.getConfig("DrawTransparentDeferred"), + object: Render.getConfig("RenderMainView.DrawTransparentDeferred"), prop: "cpuRunTime", label: "Translucents", color: "#00B4EF" }, { - object: Render.getConfig("RenderDeferred"), + object: Render.getConfig("RenderMainView.RenderDeferred"), prop: "cpuRunTime", label: "Lighting", color: "#FED959" }, { - object: Render.getConfig("RenderDeferredTask"), + object: Render.getConfig("RenderMainView.RenderDeferredTask"), prop: "cpuRunTime", label: "RenderFrame", color: "#E2334D" diff --git a/scripts/developer/utilities/render/statsGPU.qml b/scripts/developer/utilities/render/statsGPU.qml index 3d23c2c6dc..6b80f00af3 100644 --- a/scripts/developer/utilities/render/statsGPU.qml +++ b/scripts/developer/utilities/render/statsGPU.qml @@ -20,9 +20,7 @@ Item { id: stats spacing: 8 anchors.fill:parent - - property var config: Render.getConfig("Stats") - + function evalEvenHeight() { // Why do we have to do that manually ? cannot seem to find a qml / anchor / layout mode that does that ? return (height - spacing * (children.length - 1)) / children.length @@ -38,31 +36,31 @@ Item { valueNumDigits: "4" plots: [ { - object: Render.getConfig("OpaqueRangeTimer"), + object: Render.getConfig("RenderMainView.OpaqueRangeTimer"), prop: "gpuRunTime", label: "Opaque", color: "#FFFFFF" }, { - object: Render.getConfig("LinearDepth"), + object: Render.getConfig("RenderMainView.LinearDepth"), prop: "gpuRunTime", label: "LinearDepth", color: "#00FF00" },{ - object: Render.getConfig("SurfaceGeometry"), + object: Render.getConfig("RenderMainView.SurfaceGeometry"), prop: "gpuRunTime", label: "SurfaceGeometry", color: "#00FFFF" }, { - object: Render.getConfig("RenderDeferred"), + object: Render.getConfig("RenderMainView.RenderDeferred"), prop: "gpuRunTime", label: "DeferredLighting", color: "#FF00FF" } , { - object: Render.getConfig("ToneAndPostRangeTimer"), + object: Render.getConfig("RenderMainView.ToneAndPostRangeTimer"), prop: "gpuRunTime", label: "tone and post", color: "#FF0000" @@ -78,31 +76,31 @@ Item { valueNumDigits: "3" plots: [ { - object: Render.getConfig("OpaqueRangeTimer"), + object: Render.getConfig("RenderMainView.OpaqueRangeTimer"), prop: "batchRunTime", label: "Opaque", color: "#FFFFFF" }, { - object: Render.getConfig("LinearDepth"), + object: Render.getConfig("RenderMainView.LinearDepth"), prop: "batchRunTime", label: "LinearDepth", color: "#00FF00" },{ - object: Render.getConfig("SurfaceGeometry"), + object: Render.getConfig("RenderMainView.SurfaceGeometry"), prop: "batchRunTime", label: "SurfaceGeometry", color: "#00FFFF" }, { - object: Render.getConfig("RenderDeferred"), + object: Render.getConfig("RenderMainView.RenderDeferred"), prop: "batchRunTime", label: "DeferredLighting", color: "#FF00FF" } , { - object: Render.getConfig("ToneAndPostRangeTimer"), + object: Render.getConfig("RenderMainView.ToneAndPostRangeTimer"), prop: "batchRunTime", label: "tone and post", color: "#FF0000" diff --git a/scripts/developer/utilities/render/subsurfaceScattering.qml b/scripts/developer/utilities/render/subsurfaceScattering.qml index 47b960c98b..ec7367217e 100644 --- a/scripts/developer/utilities/render/subsurfaceScattering.qml +++ b/scripts/developer/utilities/render/subsurfaceScattering.qml @@ -17,27 +17,27 @@ Column { id: scattering spacing: 10 - Column{ + Column{ CheckBox { text: "Scattering" - checked: Render.getConfig("Scattering").enableScattering - onCheckedChanged: { Render.getConfig("Scattering").enableScattering = checked } + checked: Render.getConfig("RenderMainView.Scattering").enableScattering + onCheckedChanged: { Render.getConfig("RenderMainView.Scattering").enableScattering = checked } } CheckBox { text: "Show Scattering BRDF" - checked: Render.getConfig("Scattering").showScatteringBRDF - onCheckedChanged: { Render.getConfig("Scattering").showScatteringBRDF = checked } + checked: Render.getConfig("RenderMainView.Scattering").showScatteringBRDF + onCheckedChanged: { Render.getConfig("RenderMainView.Scattering").showScatteringBRDF = checked } } CheckBox { text: "Show Curvature" - checked: Render.getConfig("Scattering").showCurvature - onCheckedChanged: { Render.getConfig("Scattering").showCurvature = checked } + checked: Render.getConfig("RenderMainView.Scattering").showCurvature + onCheckedChanged: { Render.getConfig("RenderMainView.Scattering").showCurvature = checked } } CheckBox { text: "Show Diffused Normal" - checked: Render.getConfig("Scattering").showDiffusedNormal - onCheckedChanged: { Render.getConfig("Scattering").showDiffusedNormal = checked } + checked: Render.getConfig("RenderMainView.Scattering").showDiffusedNormal + onCheckedChanged: { Render.getConfig("RenderMainView.Scattering").showDiffusedNormal = checked } } Repeater { model: [ "Scattering Bent Red:Scattering:bentRed:2.0", @@ -50,7 +50,7 @@ Column { ConfigSlider { label: qsTr(modelData.split(":")[0]) integral: false - config: Render.getConfig(modelData.split(":")[1]) + config: mainViewTask.getConfig(modelData.split(":")[1]) property: modelData.split(":")[2] max: modelData.split(":")[3] min: 0.0 @@ -58,23 +58,23 @@ Column { } CheckBox { text: "Scattering Profile" - checked: Render.getConfig("DebugScattering").showProfile - onCheckedChanged: { Render.getConfig("DebugScattering").showProfile = checked } + checked: Render.getConfig("RenderMainView.DebugScattering").showProfile + onCheckedChanged: { Render.getConfig("RenderMainView.DebugScattering").showProfile = checked } } CheckBox { text: "Scattering Table" - checked: Render.getConfig("DebugScattering").showLUT - onCheckedChanged: { Render.getConfig("DebugScattering").showLUT = checked } + checked: Render.getConfig("RenderMainView.DebugScattering").showLUT + onCheckedChanged: { Render.getConfig("RenderMainView.DebugScattering").showLUT = checked } } CheckBox { text: "Cursor Pixel" - checked: Render.getConfig("DebugScattering").showCursorPixel - onCheckedChanged: { Render.getConfig("DebugScattering").showCursorPixel = checked } + checked: Render.getConfig("RenderMainView.DebugScattering").showCursorPixel + onCheckedChanged: { Render.getConfig("RenderMainView.DebugScattering").showCursorPixel = checked } } CheckBox { text: "Skin Specular Beckmann" - checked: Render.getConfig("DebugScattering").showSpecularTable - onCheckedChanged: { Render.getConfig("DebugScattering").showSpecularTable = checked } + checked: Render.getConfig("RenderMainView.DebugScattering").showSpecularTable + onCheckedChanged: { Render.getConfig("RenderMainView.DebugScattering").showSpecularTable = checked } } } } diff --git a/scripts/developer/utilities/render/surfaceGeometryPass.qml b/scripts/developer/utilities/render/surfaceGeometryPass.qml index 1ff0efa15d..ba1db66d16 100644 --- a/scripts/developer/utilities/render/surfaceGeometryPass.qml +++ b/scripts/developer/utilities/render/surfaceGeometryPass.qml @@ -21,7 +21,7 @@ Column { ConfigSlider { label: qsTr("Depth Threshold [cm]") integral: false - config: Render.getConfig("SurfaceGeometry") + config: Render.getConfig("RenderMainView.SurfaceGeometry") property: "depthThreshold" max: 5.0 min: 0.0 @@ -34,7 +34,7 @@ Column { ConfigSlider { label: qsTr(modelData.split(":")[0]) integral: (modelData.split(":")[3] == 'true') - config: Render.getConfig("SurfaceGeometry") + config: Render.getConfig("RenderMainView.SurfaceGeometry") property: modelData.split(":")[1] max: modelData.split(":")[2] min: 0.0 @@ -42,13 +42,13 @@ Column { } CheckBox { text: "Half Resolution" - checked: Render.getConfig("SurfaceGeometry")["resolutionLevel"] - onCheckedChanged: { Render.getConfig("SurfaceGeometry")["resolutionLevel"] = checked } + checked: Render.getConfig("RenderMainView.SurfaceGeometry")["resolutionLevel"] + onCheckedChanged: { Render.getConfig("RenderMainView.SurfaceGeometry")["resolutionLevel"] = checked } } Repeater { - model: [ "Diffusion Scale:SurfaceGeometry:diffuseFilterScale:2.0", - "Diffusion Depth Threshold:SurfaceGeometry:diffuseDepthThreshold:1.0" + model: [ "Diffusion Scale:RenderMainView.SurfaceGeometry:diffuseFilterScale:2.0", + "Diffusion Depth Threshold:RenderMainView.SurfaceGeometry:diffuseDepthThreshold:1.0" ] ConfigSlider { label: qsTr(modelData.split(":")[0]) diff --git a/scripts/system/edit.js b/scripts/system/edit.js index a3583e7808..a83d2159bb 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -622,7 +622,7 @@ var toolBar = (function () { })); isActive = active; activeButton.editProperties({isActive: isActive}); - + var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); if (!isActive) { @@ -1519,6 +1519,8 @@ function importSVO(importURL) { // entities after they're imported so that they're all the correct distance in front of and with geometric mean // centered on the avatar/camera direction. var deltaPosition = Vec3.ZERO; + var entityPositions = []; + var entityParentIDs = []; var properties = Entities.getEntityProperties(pastedEntityIDs[0], ["type"]); var NO_ADJUST_ENTITY_TYPES = ["Zone", "Light", "ParticleEffect"]; @@ -1534,10 +1536,9 @@ function importSVO(importURL) { var targetPosition = getPositionToCreateEntity(); var deltaParallel = HALF_TREE_SCALE; // Distance to move entities parallel to targetDirection. var deltaPerpendicular = Vec3.ZERO; // Distance to move entities perpendicular to targetDirection. - var entityPositions = []; for (var i = 0, length = pastedEntityIDs.length; i < length; i++) { var properties = Entities.getEntityProperties(pastedEntityIDs[i], ["position", "dimensions", - "registrationPoint", "rotation"]); + "registrationPoint", "rotation", "parentID"]); var adjustedPosition = adjustPositionPerBoundingBox(targetPosition, targetDirection, properties.registrationPoint, properties.dimensions, properties.rotation); var delta = Vec3.subtract(adjustedPosition, properties.position); @@ -1546,6 +1547,7 @@ function importSVO(importURL) { deltaPerpendicular = Vec3.sum(Vec3.subtract(delta, Vec3.multiply(distance, targetDirection)), deltaPerpendicular); entityPositions[i] = properties.position; + entityParentIDs[i] = properties.parentID; } deltaPerpendicular = Vec3.multiply(1 / pastedEntityIDs.length, deltaPerpendicular); deltaPosition = Vec3.sum(Vec3.multiply(deltaParallel, targetDirection), deltaPerpendicular); @@ -1562,9 +1564,11 @@ function importSVO(importURL) { if (!Vec3.equal(deltaPosition, Vec3.ZERO)) { for (var i = 0, length = pastedEntityIDs.length; i < length; i++) { - Entities.editEntity(pastedEntityIDs[i], { - position: Vec3.sum(deltaPosition, entityPositions[i]) - }); + if (Uuid.isNull(entityParentIDs[i])) { + Entities.editEntity(pastedEntityIDs[i], { + position: Vec3.sum(deltaPosition, entityPositions[i]) + }); + } } } } diff --git a/scripts/system/tablet-ui/tabletUI.js b/scripts/system/tablet-ui/tabletUI.js index b7324ed28c..9188f39a2e 100644 --- a/scripts/system/tablet-ui/tabletUI.js +++ b/scripts/system/tablet-ui/tabletUI.js @@ -183,11 +183,6 @@ return; } - //TODO: move to tablet qml? - if (tabletShown) { - gTablet.updateAudioBar(getMicLevel()); - } - if (now - validCheckTime > MSECS_PER_SEC) { validCheckTime = now; updateTabletWidthFromSettings(); @@ -268,12 +263,6 @@ Script.setInterval(updateShowTablet, 100); - // Calculate microphone level with the same scaling equation (log scale, exponentially averaged) in AvatarInputs and pal.js - function getMicLevel() { - //reuse already existing C++ code - return AvatarInputs.loudnessToAudioLevel(MyAvatar.audioLoudness) - } - Script.scriptEnding.connect(function () { // if we reload scripts in tablet mode make sure we close the currently open window, by calling gotoHomeScreen diff --git a/scripts/tutorials/entity_scripts/balloonSpawner/balloonParty.json b/scripts/tutorials/entity_scripts/balloonSpawner/balloonParty.json new file mode 100644 index 0000000000..c0457dec6d --- /dev/null +++ b/scripts/tutorials/entity_scripts/balloonSpawner/balloonParty.json @@ -0,0 +1,20 @@ +{ + "Entities": [ + { + "description": "Spawns balloons", + "dimensions": { + "x": 2, + "y": 0.1, + "z": 2 + }, + "name": "Balloon Spawner", + "serverScripts": "http://mpassets.highfidelity.com/8410ef73-9506-4dc7-b364-0174998a859e-v1/Scripts/spawnBalloons.js", + "type": "Shape", + "shape": "Cube", + "visible": "false", + "collisionless": "true", + "userData": "{\"spawnRate\":1000,\"xRangeMax\":1,\"zRangeMax\":1,\"gravityCoefficient\":0.25,\"balloonLifetime\":10,\"spawnDuration\":300,\"spawnMusicURL\":\"http://mpassets.highfidelity.com/8410ef73-9506-4dc7-b364-0174998a859e-v1/Audio/Blue_Skies.wav\",\"spawnMusicVolume\":0.1}" + } + ], + "Version": 63 +} \ No newline at end of file diff --git a/scripts/tutorials/entity_scripts/balloonSpawner/spawnBalloons.js b/scripts/tutorials/entity_scripts/balloonSpawner/spawnBalloons.js new file mode 100644 index 0000000000..3aa956491e --- /dev/null +++ b/scripts/tutorials/entity_scripts/balloonSpawner/spawnBalloons.js @@ -0,0 +1,224 @@ +"use strict"; + +// +// spawnBalloons.js +// +// Created by Johnathan Franck on 3 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 +// + +(function () { + var RED_BALLOON_URL = Script.resolvePath("../Models/redBalloon.fbx"); + var BLUE_BALLOON_URL = Script.resolvePath("../Models/blueBalloon.fbx"); + var GREEN_BALLOON_URL = Script.resolvePath("../Models/greenBalloon.fbx"); + var YELLOW_BALLOON_URL = Script.resolvePath("../Models/yellowBalloon.fbx"); + var ORANGE_BALLOON_URL = Script.resolvePath("../Models/orangeBalloon.fbx"); + var CYAN_BALLOON_URL = Script.resolvePath("../Models/cyanBalloon.fbx"); + var PURPLE_BALLOON_URL = Script.resolvePath("../Models/purpleBalloon.fbx"); + //'Blue Skies' by Silent Partner from youtube audio library. Listed as attribution not required + var SPAWN_MUSIC_URL = Script.resolvePath("../Audio/Blue_Skies.wav"); + + var BALLOON_COLORS = ["red", "blue", "green", "yellow", "orange", "cyan", "purple"]; + var BALLOON_URLS = [RED_BALLOON_URL, BLUE_BALLOON_URL, GREEN_BALLOON_URL, + YELLOW_BALLOON_URL, ORANGE_BALLOON_URL, CYAN_BALLOON_URL, PURPLE_BALLOON_URL]; + + var NUM_COLORS = 7; + var COUNTDOWN_SECONDS = 9; + //Lowering the spawn rate below 10 spawns so many balloons that the interface slows down + var MIN_SPAWN_RATE = 10; + var MILLISECONDS_IN_SECOND = 1000; + + var _this = this; + var spawnRate = 2000; + var xRangeMax = 1; + var zRangeMax = 1; + var gravityCoefficient = 0.25; + var balloonLifetime = 10; + var spawnDuration = 300; + var musicInjector; + var spawnIntervalID; + var spawnMusic; + var countdownIntervalID; + var countdownEntityID; + + _this.preload = function (pEntityID) { + var parentProperties = Entities.getEntityProperties(pEntityID, ["userData"]), + spawnMusicURL, + spawnerSettings; + if (parentProperties.userData) { + spawnerSettings = JSON.parse(parentProperties.userData); + } + spawnMusicURL = spawnerSettings.spawnMusicURL ? spawnerSettings.spawnMusicURL : SPAWN_MUSIC_URL; + spawnMusic = SoundCache.getSound(spawnMusicURL); + + _this.startCountdown(pEntityID); + }; + + _this.startCountdown = function (pEntityID) { + var countdownSeconds = COUNTDOWN_SECONDS, + parentProperties = Entities.getEntityProperties(pEntityID, ["position"]), + countdownEntityProperties; + + countdownEntityProperties = { + type: "Text", + text: countdownSeconds, + lineHeight: 0.71, + dimensions: { + x: 0.5, + y: 1, + z: 0.01 + }, + textColor: { + red: 0, + blue: 255, + green: 0 + }, + backgroundColor: { + red: 255, + blue: 255, + green: 255 + }, + parentID: pEntityID, + position: parentProperties.position + }; + + countdownEntityID = Entities.addEntity(countdownEntityProperties); + countdownIntervalID = Script.setInterval(function () { + countdownSeconds -= 1; + if (countdownSeconds < 0) { + Script.clearInterval(countdownIntervalID); + Entities.deleteEntity(countdownEntityID); + } else { + Entities.editEntity(countdownEntityID, {"text": countdownSeconds}); + if (countdownSeconds === 0) { + _this.spawnBalloons(pEntityID); + } + } + }, 1000); + }; + + _this.spawnBalloons = function (pEntityID) { + var parentProperties = Entities.getEntityProperties(pEntityID, ["position", "userData"]), + spawnerSettings, + spawnMusicVolume, + spawnCount = 0; + + if (parentProperties.userData) { + spawnerSettings = JSON.parse(parentProperties.userData); + } + + xRangeMax = !isNaN(spawnerSettings.xRangeMax) ? spawnerSettings.xRangeMax : xRangeMax; + zRangeMax = !isNaN(spawnerSettings.zRangeMax) ? spawnerSettings.zRangeMax : zRangeMax; + gravityCoefficient = !isNaN(spawnerSettings.gravityCoefficient) ? spawnerSettings.gravityCoefficient : gravityCoefficient; + spawnDuration = !isNaN(spawnerSettings.spawnDuration) ? spawnerSettings.spawnDuration : spawnDuration; + balloonLifetime = !isNaN(spawnerSettings.balloonLifetime) ? spawnerSettings.balloonLifetime : balloonLifetime; + spawnRate = !isNaN(spawnerSettings.spawnRate) ? spawnerSettings.spawnRate : spawnRate; + spawnMusicVolume = !isNaN(spawnerSettings.spawnMusicVolume) ? spawnerSettings.spawnMusicVolume : 0.1; + + if (spawnRate < MIN_SPAWN_RATE) { + spawnRate = MIN_SPAWN_RATE; + print("The lowest balloon spawn rate allowed is " + MIN_SPAWN_RATE); + } + + if (spawnMusic.downloaded) { + musicInjector = Audio.playSound(spawnMusic, { + position: parentProperties.position, + volume: spawnMusicVolume, + loop: true + }); + } + + spawnIntervalID = Script.setInterval(function () { + var colorID = Math.floor(Math.random() * NUM_COLORS), + color = BALLOON_COLORS[colorID], + balloonURL = BALLOON_URLS[colorID], + balloonPosition = {}, + balloonProperties; + + spawnCount ++; + //Randomize balloon spawn position + balloonPosition.y = parentProperties.position.y + 0.5; + balloonPosition.x = parentProperties.position.x + (Math.random() - 0.5) * 2 * xRangeMax; + balloonPosition.z = parentProperties.position.z + (Math.random() - 0.5) * 2 * zRangeMax; + + balloonProperties = { + position: balloonPosition, + lifetime: balloonLifetime, + angularVelocity: { + x: -0.03654664754867554, + y: -0.4030083637684583664 + Math.random(), + z: 0.02576472796499729 + }, + collisionsWillMove: 1, + density: 100, + description: "A happy " + color + " balloon", + dimensions: { + x: 0.3074322044849396, + y: 0.40930506587028503, + z: 0.30704551935195923 + }, + dynamic: 1, + gravity: { + x: 0, + y: gravityCoefficient, + z: 0 + }, + modelURL: balloonURL, + name: color + " balloon", + queryAACube: { + scale: 0.596927285194397, + x: -0.2984636425971985, + y: -0.2984636425971985, + z: -0.2984636425971985 + }, + restitution: 0.9900000095367432, + rotation: { + w: -0.10368101298809052, + x: 0.5171623826026917, + y: 0.1211432576179504, + z: -0.670971691608429 + Math.random() + }, + shapeType: "sphere", + type : "Model", + velocity: { + x: 0.003623033408075571, + y: 0.0005839366931468248, + z: -0.01512028019875288 + } + }; + Entities.addEntity(balloonProperties); + + //Clean up after spawnDuration + if (spawnCount * spawnRate / MILLISECONDS_IN_SECOND > spawnDuration) { + _this.cleanUp(pEntityID); + } + }, spawnRate); + }; + + _this.unload = function () { + _this.cleanUp(); + }; + + _this.cleanUp = function (pEntityID) { + if (spawnIntervalID ) { + Script.clearInterval(spawnIntervalID); + } + if (countdownIntervalID) { + Script.clearInterval(countdownIntervalID); + } + if (countdownEntityID) { + Entities.deleteEntity(countdownEntityID); + } + if (musicInjector !== undefined && musicInjector.isPlaying) { + musicInjector.stop(); + musicInjector = undefined; + } + if (pEntityID) { + Entities.deleteEntity(pEntityID); + } + }; + +}); 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 69% rename from tests/networking/src/FileCacheTests.cpp rename to tests/shared/src/FileCacheTests.cpp index 79fe9dee54..3f1c5e1a01 100644 --- a/tests/networking/src/FileCacheTests.cpp +++ b/tests/shared/src/FileCacheTests.cpp @@ -10,7 +10,7 @@ #include "FileCacheTests.h" -#include +#include QTEST_GUILESS_MAIN(FileCacheTests) @@ -24,40 +24,6 @@ static std::string getFileKey(int i) { return QString(QByteArray { 1, (char)i }.toHex()).toStdString(); } -class TestFile : public File { - using Parent = File; -public: - TestFile(Metadata&& metadata, const std::string& filepath) - : Parent(std::move(metadata), filepath) { - } -}; - -class TestFileCache : public FileCache { - using Parent = FileCache; - -public: - TestFileCache(const std::string& dirname, const std::string& ext, QObject* parent = nullptr) : Parent(dirname, ext, nullptr) { - initialize(); - } - - std::unique_ptr createFile(Metadata&& metadata, const std::string& filepath) override { - qCInfo(file_cache) << "Wrote KTX" << metadata.key.c_str(); - return std::unique_ptr(new TestFile(std::move(metadata), filepath)); - } -}; - -using CachePointer = std::shared_ptr; - -// The FileCache relies on deleteLater to clear unused files, but QTest classes don't run a conventional event loop -// so we need to call this function to force any pending deletes to occur in the File destructor -static void forceDeletes() { - while (QCoreApplication::hasPendingEvents()) { - QCoreApplication::sendPostedEvents(); - QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); - QCoreApplication::processEvents(); - } -} - size_t FileCacheTests::getCacheDirectorySize() const { size_t result = 0; QDir dir(_testDir.path()); @@ -67,8 +33,9 @@ size_t FileCacheTests::getCacheDirectorySize() const { return result; } -CachePointer makeFileCache(QString& location) { - auto result = std::make_shared(location.toStdString(), "tmp"); +FileCachePointer makeFileCache(QString& location) { + auto result = std::make_shared(location.toStdString(), "tmp"); + result->initialize(); result->setMaxSize(MAX_UNUSED_SIZE); return result; } @@ -83,26 +50,38 @@ void FileCacheTests::testUnusedFiles() { { for (int i = 0; i < 100; ++i) { std::string key = getFileKey(i); - auto file = cache->writeFile(TEST_DATA.data(), TestFileCache::Metadata(key, TEST_DATA.size())); + auto file = cache->writeFile(TEST_DATA.data(), FileCache::Metadata(key, TEST_DATA.size())); + QVERIFY(file->_locked); inUseFiles.push_back(file); - forceDeletes(); + QThread::msleep(10); } QCOMPARE(cache->getNumCachedFiles(), (size_t)0); QCOMPARE(cache->getNumTotalFiles(), (size_t)100); // Release the in-use files inUseFiles.clear(); - // Cache state is updated, but the directory state is unchanged, - // because the file deletes are triggered by an event loop - QCOMPARE(cache->getNumCachedFiles(), (size_t)10); - QCOMPARE(cache->getNumTotalFiles(), (size_t)10); - QVERIFY(getCacheDirectorySize() > MAX_UNUSED_SIZE); - forceDeletes(); QCOMPARE(cache->getNumCachedFiles(), (size_t)10); QCOMPARE(cache->getNumTotalFiles(), (size_t)10); QVERIFY(getCacheDirectorySize() <= MAX_UNUSED_SIZE); } + // Check behavior when destroying the cache BEFORE releasing the files + cache = makeFileCache(_testDir.path()); + { + auto directorySize = getCacheDirectorySize(); + + // Test files 90 to 99 are present + for (int i = 90; i < 100; ++i) { + std::string key = getFileKey(i); + auto file = cache->getFile(key); + QVERIFY(file.get()); + inUseFiles.push_back(file); + } + cache.reset(); + inUseFiles.clear(); + QCOMPARE(getCacheDirectorySize(), directorySize); + } + // Reset the cache cache = makeFileCache(_testDir.path()); { @@ -151,8 +130,6 @@ void FileCacheTests::testFreeSpacePreservation() { auto cache = makeFileCache(_testDir.path()); // Setting the min free space causes it to eject the oldest files that cause the cache to exceed the minimum space cache->setMinFreeSize(targetFreeSpace); - QVERIFY(getFreeSpace() < targetFreeSpace); - forceDeletes(); QCOMPARE(cache->getNumCachedFiles(), (size_t)5); QCOMPARE(cache->getNumTotalFiles(), (size_t)5); QVERIFY(getFreeSpace() >= targetFreeSpace); @@ -176,8 +153,6 @@ void FileCacheTests::testWipe() { cache->wipe(); QCOMPARE(cache->getNumCachedFiles(), (size_t)0); QCOMPARE(cache->getNumTotalFiles(), (size_t)0); - QVERIFY(getCacheDirectorySize() > 0); - forceDeletes(); QCOMPARE(getCacheDirectorySize(), (size_t)0); } 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/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",