From e88895935bc2c4aeeaa10ffd7263173ef90e4e89 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Fri, 24 Mar 2017 17:56:48 -0700 Subject: [PATCH] Revert "Actually merge from master" This reverts commit 056d6fbe4f384386f3c35d0f142a1cf9a6dd532a. --- BUILD_WIN.md | 153 +-- assignment-client/src/Agent.cpp | 33 +- assignment-client/src/Agent.h | 8 - assignment-client/src/assets/AssetServer.cpp | 4 +- assignment-client/src/octree/OctreeServer.cpp | 9 +- .../src/scripts/EntityScriptServer.cpp | 30 +- .../PackageLibrariesForDeployment.cmake | 28 +- .../SymlinkOrCopyDirectoryBesideTarget.cmake | 6 +- domain-server/src/DomainServer.cpp | 4 +- .../src/DomainServerSettingsManager.cpp | 37 +- interface/CMakeLists.txt | 10 +- interface/resources/controllers/standard.json | 44 +- interface/resources/html/img/devices.png | Bin 0 -> 7492 bytes interface/resources/html/img/models.png | Bin 0 -> 8664 bytes interface/resources/html/img/move.png | Bin 0 -> 6121 bytes interface/resources/html/img/run-script.png | Bin 0 -> 4873 bytes interface/resources/html/img/talk.png | Bin 0 -> 2611 bytes interface/resources/html/img/write-script.png | Bin 0 -> 2006 bytes .../resources/html/interface-welcome.html | 187 +++ interface/resources/icons/load-script.svg | 125 ++ interface/resources/icons/new-script.svg | 129 ++ interface/resources/icons/save-script.svg | 674 +++++++++++ interface/resources/icons/start-script.svg | 550 +++++++++ interface/resources/icons/stop-script.svg | 163 +++ interface/resources/qml/AssetServer.qml | 2 +- interface/resources/qml/AvatarInputs.qml | 57 +- interface/resources/qml/Stats.qml | 12 +- interface/resources/styles/log_dialog.qss | 4 +- interface/src/Application.cpp | 218 ++-- interface/src/Application.h | 7 +- interface/src/Menu.cpp | 21 +- interface/src/Menu.h | 6 +- interface/src/avatar/Avatar.h | 1 + interface/src/avatar/AvatarManager.cpp | 2 +- interface/src/avatar/AvatarManager.h | 2 +- .../src/avatar/CauterizedMeshPartPayload.cpp | 53 +- .../src/avatar/CauterizedMeshPartPayload.h | 7 +- interface/src/avatar/CauterizedModel.cpp | 44 +- interface/src/avatar/Head.cpp | 4 +- interface/src/avatar/Head.h | 6 +- interface/src/avatar/MyAvatar.cpp | 115 +- interface/src/avatar/MyAvatar.h | 56 +- interface/src/ui/ApplicationOverlay.cpp | 49 +- interface/src/ui/ApplicationOverlay.h | 3 + interface/src/ui/AvatarInputs.cpp | 20 + interface/src/ui/AvatarInputs.h | 6 + interface/src/ui/BaseLogDialog.cpp | 48 +- interface/src/ui/BaseLogDialog.h | 4 +- interface/src/ui/CachesSizeDialog.cpp | 84 ++ interface/src/ui/CachesSizeDialog.h | 45 + interface/src/ui/DialogsManager.cpp | 24 + interface/src/ui/DialogsManager.h | 6 + interface/src/ui/DiskCacheEditor.cpp | 146 +++ interface/src/ui/DiskCacheEditor.h | 49 + interface/src/ui/ScriptEditBox.cpp | 111 ++ interface/src/ui/ScriptEditBox.h | 38 + interface/src/ui/ScriptEditorWidget.cpp | 256 ++++ interface/src/ui/ScriptEditorWidget.h | 64 + interface/src/ui/ScriptEditorWindow.cpp | 259 +++++ interface/src/ui/ScriptEditorWindow.h | 64 + interface/src/ui/ScriptLineNumberArea.cpp | 28 + interface/src/ui/ScriptLineNumberArea.h | 32 + interface/src/ui/ScriptsTableWidget.cpp | 49 + interface/src/ui/ScriptsTableWidget.h | 28 + interface/src/ui/Stats.cpp | 10 +- interface/src/ui/Stats.h | 8 +- interface/src/ui/overlays/Overlays.cpp | 4 +- interface/src/ui/overlays/Web3DOverlay.cpp | 2 +- interface/ui/scriptEditorWidget.ui | 142 +++ interface/ui/scriptEditorWindow.ui | 324 ++++++ libraries/animation/src/Rig.cpp | 8 +- libraries/animation/src/Rig.h | 2 +- libraries/audio-client/src/AudioClient.cpp | 244 ++-- libraries/audio-client/src/AudioClient.h | 16 +- libraries/avatars/src/HeadData.cpp | 4 +- .../display-plugins/OpenGLDisplayPlugin.cpp | 6 +- .../display-plugins/hmd/HmdDisplayPlugin.cpp | 35 +- .../src/EntityTreeRenderer.cpp | 5 +- .../src/RenderablePolyVoxEntityItem.cpp | 78 +- .../src/RenderablePolyVoxEntityItem.h | 9 +- .../src/RenderableShapeEntityItem.cpp | 13 +- .../src/RenderableWebEntityItem.cpp | 2 +- .../src/EntitiesScriptEngineProvider.h | 4 +- libraries/entities/src/EntityItem.cpp | 8 +- .../entities/src/EntityItemProperties.cpp | 21 + libraries/entities/src/EntityItemProperties.h | 4 + .../entities/src/EntityScriptingInterface.cpp | 176 +-- .../entities/src/EntityScriptingInterface.h | 54 +- libraries/entities/src/PolyVoxEntityItem.cpp | 4 - libraries/entities/src/PolyVoxEntityItem.h | 6 +- libraries/entities/src/PropertyGroup.h | 29 +- libraries/fbx/src/FBXReader.cpp | 19 +- libraries/fbx/src/FBXReader.h | 20 + libraries/fbx/src/FBXReader_Node.cpp | 3 +- libraries/fbx/src/OBJReader.cpp | 10 +- libraries/fbx/src/OBJReader.h | 2 +- libraries/fbx/src/OBJWriter.cpp | 148 --- libraries/fbx/src/OBJWriter.h | 26 - libraries/gpu-gl/src/gpu/gl/GLBackend.cpp | 11 +- libraries/gpu-gl/src/gpu/gl/GLBackend.h | 13 +- .../gpu-gl/src/gpu/gl/GLBackendTexture.cpp | 54 +- libraries/gpu-gl/src/gpu/gl/GLFramebuffer.cpp | 9 +- libraries/gpu-gl/src/gpu/gl/GLFramebuffer.h | 2 +- libraries/gpu-gl/src/gpu/gl/GLTexelFormat.cpp | 5 - libraries/gpu-gl/src/gpu/gl/GLTexture.cpp | 233 +++- libraries/gpu-gl/src/gpu/gl/GLTexture.h | 203 +++- .../gpu-gl/src/gpu/gl/GLTextureTransfer.cpp | 208 ++++ .../gpu-gl/src/gpu/gl/GLTextureTransfer.h | 78 ++ libraries/gpu-gl/src/gpu/gl41/GL41Backend.h | 31 +- .../gpu-gl/src/gpu/gl41/GL41BackendOutput.cpp | 10 +- .../src/gpu/gl41/GL41BackendTexture.cpp | 192 ++- libraries/gpu-gl/src/gpu/gl45/GL45Backend.cpp | 11 +- libraries/gpu-gl/src/gpu/gl45/GL45Backend.h | 254 +--- .../gpu-gl/src/gpu/gl45/GL45BackendOutput.cpp | 10 +- .../src/gpu/gl45/GL45BackendTexture.cpp | 542 +++++++-- .../gpu/gl45/GL45BackendVariableTexture.cpp | 1033 ----------------- libraries/gpu/CMakeLists.txt | 2 +- libraries/gpu/src/gpu/Batch.cpp | 7 + libraries/gpu/src/gpu/Buffer.h | 2 +- libraries/gpu/src/gpu/Context.cpp | 17 - libraries/gpu/src/gpu/Context.h | 4 - libraries/gpu/src/gpu/Format.cpp | 7 - libraries/gpu/src/gpu/Format.h | 6 - libraries/gpu/src/gpu/Framebuffer.cpp | 12 +- libraries/gpu/src/gpu/Texture.cpp | 261 +++-- libraries/gpu/src/gpu/Texture.h | 178 +-- libraries/gpu/src/gpu/Texture_ktx.cpp | 289 ----- libraries/ktx/CMakeLists.txt | 3 - libraries/ktx/src/ktx/KTX.cpp | 165 --- libraries/ktx/src/ktx/KTX.h | 494 -------- libraries/ktx/src/ktx/Reader.cpp | 195 ---- libraries/ktx/src/ktx/Writer.cpp | 171 --- libraries/model-networking/CMakeLists.txt | 2 +- .../src/model-networking/KTXCache.cpp | 47 - .../src/model-networking/KTXCache.h | 51 - .../src/model-networking/TextureCache.cpp | 492 +++----- .../src/model-networking/TextureCache.h | 26 +- libraries/model/CMakeLists.txt | 2 +- libraries/model/src/model/Geometry.cpp | 112 +- libraries/model/src/model/Geometry.h | 14 +- libraries/model/src/model/TextureMap.cpp | 149 +-- libraries/model/src/model/TextureMap.h | 3 +- libraries/networking/src/Assignment.cpp | 1 + libraries/networking/src/FileCache.cpp | 243 ---- libraries/networking/src/FileCache.h | 158 --- libraries/networking/src/NodePermissions.h | 48 +- libraries/networking/src/udt/PacketQueue.cpp | 23 +- libraries/networking/src/udt/PacketQueue.h | 5 +- libraries/octree/src/OctreeQuery.cpp | 2 +- libraries/physics/src/EntityMotionState.cpp | 24 +- libraries/physics/src/EntityMotionState.h | 1 - .../physics/src/PhysicalEntitySimulation.cpp | 18 +- .../physics/src/PhysicalEntitySimulation.h | 5 +- libraries/physics/src/PhysicsEngine.cpp | 2 +- libraries/physics/src/PhysicsEngine.h | 3 +- .../physics/src/ThreadSafeDynamicsWorld.cpp | 27 +- .../physics/src/ThreadSafeDynamicsWorld.h | 4 - libraries/recording/src/recording/Deck.cpp | 5 +- libraries/render-utils/CMakeLists.txt | 2 +- .../render-utils/src/AntialiasingEffect.cpp | 2 +- .../render-utils/src/DeferredFramebuffer.cpp | 10 +- .../src/DeferredLightingEffect.cpp | 4 +- .../render-utils/src/FramebufferCache.cpp | 16 + libraries/render-utils/src/FramebufferCache.h | 5 + libraries/render-utils/src/LightAmbient.slh | 13 +- libraries/render-utils/src/LightStage.cpp | 4 +- libraries/render-utils/src/LightingModel.cpp | 10 - libraries/render-utils/src/LightingModel.h | 12 +- libraries/render-utils/src/LightingModel.slh | 9 +- .../render-utils/src/MaterialTextures.slh | 2 +- .../render-utils/src/MeshPartPayload.cpp | 26 +- libraries/render-utils/src/MeshPartPayload.h | 6 +- libraries/render-utils/src/Model.cpp | 98 +- libraries/render-utils/src/Model.h | 10 +- .../render-utils/src/RenderDeferredTask.cpp | 27 +- .../render-utils/src/RenderPipelines.cpp | 2 +- .../render-utils/src/SubsurfaceScattering.cpp | 6 +- .../render-utils/src/SurfaceGeometryPass.cpp | 12 +- libraries/render-utils/src/text/Font.cpp | 3 +- libraries/render/CMakeLists.txt | 2 +- libraries/render/src/render/DrawTask.cpp | 12 +- libraries/render/src/render/DrawTask.h | 4 +- libraries/render/src/render/ShapePipeline.h | 8 +- .../render/src/render/drawItemStatus.slv | 4 +- .../src/AudioScriptingInterface.cpp | 5 + .../src/AudioScriptingInterface.h | 9 +- .../src/BaseScriptEngine.cpp | 130 +-- .../script-engine/src/BaseScriptEngine.h | 67 ++ libraries/script-engine/src/Mat4.cpp | 2 +- libraries/script-engine/src/Mat4.h | 4 +- libraries/script-engine/src/MeshProxy.h | 41 - .../src/ModelScriptingInterface.cpp | 159 --- .../src/ModelScriptingInterface.h | 45 - libraries/script-engine/src/Quat.cpp | 2 +- libraries/script-engine/src/Quat.h | 4 +- libraries/script-engine/src/ScriptEngine.cpp | 561 +-------- libraries/script-engine/src/ScriptEngine.h | 37 +- .../script-engine/src/ScriptEngineLogging.cpp | 1 - .../script-engine/src/ScriptEngineLogging.h | 1 - libraries/script-engine/src/ScriptEngines.cpp | 20 +- libraries/script-engine/src/ScriptEngines.h | 10 +- libraries/shared/src/BaseScriptEngine.h | 90 -- libraries/shared/src/GLMHelpers.h | 2 +- libraries/shared/src/HifiConfigVariantMap.cpp | 6 +- libraries/shared/src/PathUtils.cpp | 22 +- libraries/shared/src/PathUtils.h | 7 +- libraries/shared/src/RenderArgs.h | 1 - libraries/shared/src/ServerPathUtils.cpp | 31 + libraries/shared/src/ServerPathUtils.h | 22 + libraries/shared/src/ViewFrustum.cpp | 2 +- libraries/shared/src/ViewFrustum.h | 2 +- libraries/shared/src/shared/Storage.cpp | 92 -- libraries/shared/src/shared/Storage.h | 82 -- libraries/ui/src/ui/Menu.cpp | 2 +- .../src/OculusLegacyDisplayPlugin.cpp | 2 +- plugins/openvr/src/OpenVrDisplayPlugin.cpp | 8 +- .../developer/libraries/jasmine/hifi-boot.js | 13 +- scripts/developer/tests/.gitignore | 1 - scripts/developer/tests/ambientSoundTest.js | 2 +- .../tests/basicEntityTest/entitySpawner.js | 2 +- .../batonSoundTestEntitySpawner.js | 2 +- .../tests/entityServerStampedeTest.js | 2 +- scripts/developer/tests/entityStampedeTest.js | 2 +- scripts/developer/tests/lodTest.js | 2 +- scripts/developer/tests/mat4test.js | 8 +- .../developer/tests/performance/tribbles.js | 2 +- .../rapidProceduralChangeTest.js | 4 +- scripts/developer/tests/scaling.png | Bin 3172 -> 0 bytes scripts/developer/tests/sphereLODTest.js | 2 +- scripts/developer/tests/testInterval.js | 2 +- .../tests/unit_tests/entityUnitTests.js | 2 +- .../tests/unit_tests/moduleTests/cycles/a.js | 10 - .../tests/unit_tests/moduleTests/cycles/b.js | 10 - .../unit_tests/moduleTests/cycles/main.js | 17 - .../entity/entityConstructorAPIException.js | 13 - .../entity/entityConstructorModule.js | 23 - .../entity/entityConstructorNested.js | 14 - .../entity/entityConstructorNested2.js | 25 - .../entityConstructorRequireException.js | 10 - .../entity/entityPreloadAPIError.js | 13 - .../entity/entityPreloadRequire.js | 11 - .../tests/unit_tests/moduleTests/example.json | 9 - .../moduleTests/exceptions/exception.js | 4 - .../exceptions/exceptionInFunction.js | 38 - .../tests/unit_tests/moduleUnitTests.js | 378 ------ .../developer/tests/unit_tests/package.json | 6 - .../tests/unit_tests/scriptUnitTests.js | 18 +- scripts/developer/tests/viveTouchpadTest.js | 8 +- .../developer/utilities/record/recorder.js | 97 +- .../utilities/render/deferredLighting.qml | 3 +- .../utilities/render/photobooth/photobooth.js | 9 +- scripts/modules/vec3.js | 69 -- .../system/assets/images/icon-particles.svg | 29 - .../system/assets/images/icon-point-light.svg | 57 - .../system/assets/images/icon-spot-light.svg | 37 - scripts/system/away.js | 4 +- scripts/system/controllers/grab.js | 4 +- .../system/controllers/handControllerGrab.js | 2 +- .../controllers/handControllerPointer.js | 2 +- scripts/system/controllers/teleport.js | 19 +- ...oggleAdvancedMovementForHandControllers.js | 13 +- scripts/system/edit.js | 146 ++- scripts/system/libraries/WebTablet.js | 6 +- scripts/system/libraries/entityCameraTool.js | 4 +- .../system/libraries/entitySelectionTool.js | 10 +- ...erlayManager.js => lightOverlayManager.js} | 51 +- scripts/system/libraries/soundArray.js | 2 +- scripts/system/libraries/toolBars.js | 4 - scripts/system/nameTag.js | 4 +- scripts/system/pal.js | 6 +- scripts/system/voxels.js | 2 +- scripts/tutorials/NBody/makePlanets.js | 2 +- scripts/tutorials/butterflies.js | 6 +- scripts/tutorials/createCow.js | 2 +- scripts/tutorials/createDice.js | 4 +- scripts/tutorials/createFlashlight.js | 2 +- scripts/tutorials/createGolfClub.js | 2 +- scripts/tutorials/createPictureFrame.js | 2 +- scripts/tutorials/createPingPongGun.js | 2 +- scripts/tutorials/createPistol.js | 2 +- scripts/tutorials/createSoundMaker.js | 2 +- scripts/tutorials/entity_scripts/golfClub.js | 4 +- .../tutorials/entity_scripts/pingPongGun.js | 14 +- scripts/tutorials/entity_scripts/pistol.js | 2 +- scripts/tutorials/entity_scripts/sit.js | 230 ++-- scripts/tutorials/makeBlocks.js | 8 +- tests/ktx/CMakeLists.txt | 15 - tests/ktx/src/main.cpp | 150 --- tests/render-perf/CMakeLists.txt | 2 +- tests/render-perf/src/Camera.hpp | 6 +- tests/render-perf/src/main.cpp | 1 + tests/render-texture-load/src/main.cpp | 1 - tests/shared/src/StorageTests.cpp | 75 -- tests/shared/src/StorageTests.h | 32 - tools/CMakeLists.txt | 2 - tools/atp-get/CMakeLists.txt | 3 - tools/atp-get/src/ATPGetApp.cpp | 269 ----- tools/atp-get/src/ATPGetApp.h | 52 - tools/atp-get/src/main.cpp | 31 - .../marketplace/boppo/boppoClownEntity.js | 80 -- .../marketplace/boppo/boppoServer.js | 303 ----- .../marketplace/boppo/clownGloveDispenser.js | 154 --- .../marketplace/boppo/createElBoppo.js | 430 ------- .../marketplace/boppo/lookAtEntity.js | 98 -- 304 files changed, 6966 insertions(+), 9882 deletions(-) create mode 100644 interface/resources/html/img/devices.png create mode 100644 interface/resources/html/img/models.png create mode 100644 interface/resources/html/img/move.png create mode 100644 interface/resources/html/img/run-script.png create mode 100644 interface/resources/html/img/talk.png create mode 100644 interface/resources/html/img/write-script.png create mode 100644 interface/resources/html/interface-welcome.html create mode 100644 interface/resources/icons/load-script.svg create mode 100644 interface/resources/icons/new-script.svg create mode 100644 interface/resources/icons/save-script.svg create mode 100644 interface/resources/icons/start-script.svg create mode 100644 interface/resources/icons/stop-script.svg create mode 100644 interface/src/ui/CachesSizeDialog.cpp create mode 100644 interface/src/ui/CachesSizeDialog.h create mode 100644 interface/src/ui/DiskCacheEditor.cpp create mode 100644 interface/src/ui/DiskCacheEditor.h create mode 100644 interface/src/ui/ScriptEditBox.cpp create mode 100644 interface/src/ui/ScriptEditBox.h create mode 100644 interface/src/ui/ScriptEditorWidget.cpp create mode 100644 interface/src/ui/ScriptEditorWidget.h create mode 100644 interface/src/ui/ScriptEditorWindow.cpp create mode 100644 interface/src/ui/ScriptEditorWindow.h create mode 100644 interface/src/ui/ScriptLineNumberArea.cpp create mode 100644 interface/src/ui/ScriptLineNumberArea.h create mode 100644 interface/src/ui/ScriptsTableWidget.cpp create mode 100644 interface/src/ui/ScriptsTableWidget.h create mode 100644 interface/ui/scriptEditorWidget.ui create mode 100644 interface/ui/scriptEditorWindow.ui delete mode 100644 libraries/fbx/src/OBJWriter.cpp delete mode 100644 libraries/fbx/src/OBJWriter.h create mode 100644 libraries/gpu-gl/src/gpu/gl/GLTextureTransfer.cpp create mode 100644 libraries/gpu-gl/src/gpu/gl/GLTextureTransfer.h delete mode 100644 libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp delete mode 100644 libraries/gpu/src/gpu/Texture_ktx.cpp delete mode 100644 libraries/ktx/CMakeLists.txt delete mode 100644 libraries/ktx/src/ktx/KTX.cpp delete mode 100644 libraries/ktx/src/ktx/KTX.h delete mode 100644 libraries/ktx/src/ktx/Reader.cpp delete mode 100644 libraries/ktx/src/ktx/Writer.cpp delete mode 100644 libraries/model-networking/src/model-networking/KTXCache.cpp delete mode 100644 libraries/model-networking/src/model-networking/KTXCache.h delete mode 100644 libraries/networking/src/FileCache.cpp delete mode 100644 libraries/networking/src/FileCache.h rename libraries/{shared => script-engine}/src/BaseScriptEngine.cpp (68%) create mode 100644 libraries/script-engine/src/BaseScriptEngine.h delete mode 100644 libraries/script-engine/src/MeshProxy.h delete mode 100644 libraries/script-engine/src/ModelScriptingInterface.cpp delete mode 100644 libraries/script-engine/src/ModelScriptingInterface.h delete mode 100644 libraries/shared/src/BaseScriptEngine.h create mode 100644 libraries/shared/src/ServerPathUtils.cpp create mode 100644 libraries/shared/src/ServerPathUtils.h delete mode 100644 libraries/shared/src/shared/Storage.cpp delete mode 100644 libraries/shared/src/shared/Storage.h delete mode 100644 scripts/developer/tests/.gitignore delete mode 100644 scripts/developer/tests/scaling.png delete mode 100644 scripts/developer/tests/unit_tests/moduleTests/cycles/a.js delete mode 100644 scripts/developer/tests/unit_tests/moduleTests/cycles/b.js delete mode 100644 scripts/developer/tests/unit_tests/moduleTests/cycles/main.js delete mode 100644 scripts/developer/tests/unit_tests/moduleTests/entity/entityConstructorAPIException.js delete mode 100644 scripts/developer/tests/unit_tests/moduleTests/entity/entityConstructorModule.js delete mode 100644 scripts/developer/tests/unit_tests/moduleTests/entity/entityConstructorNested.js delete mode 100644 scripts/developer/tests/unit_tests/moduleTests/entity/entityConstructorNested2.js delete mode 100644 scripts/developer/tests/unit_tests/moduleTests/entity/entityConstructorRequireException.js delete mode 100644 scripts/developer/tests/unit_tests/moduleTests/entity/entityPreloadAPIError.js delete mode 100644 scripts/developer/tests/unit_tests/moduleTests/entity/entityPreloadRequire.js delete mode 100644 scripts/developer/tests/unit_tests/moduleTests/example.json delete mode 100644 scripts/developer/tests/unit_tests/moduleTests/exceptions/exception.js delete mode 100644 scripts/developer/tests/unit_tests/moduleTests/exceptions/exceptionInFunction.js delete mode 100644 scripts/developer/tests/unit_tests/moduleUnitTests.js delete mode 100644 scripts/developer/tests/unit_tests/package.json delete mode 100644 scripts/modules/vec3.js delete mode 100644 scripts/system/assets/images/icon-particles.svg delete mode 100644 scripts/system/assets/images/icon-point-light.svg delete mode 100644 scripts/system/assets/images/icon-spot-light.svg rename scripts/system/libraries/{entityIconOverlayManager.js => lightOverlayManager.js} (67%) delete mode 100644 tests/ktx/CMakeLists.txt delete mode 100644 tests/ktx/src/main.cpp delete mode 100644 tests/shared/src/StorageTests.cpp delete mode 100644 tests/shared/src/StorageTests.h delete mode 100644 tools/atp-get/CMakeLists.txt delete mode 100644 tools/atp-get/src/ATPGetApp.cpp delete mode 100644 tools/atp-get/src/ATPGetApp.h delete mode 100644 tools/atp-get/src/main.cpp delete mode 100644 unpublishedScripts/marketplace/boppo/boppoClownEntity.js delete mode 100644 unpublishedScripts/marketplace/boppo/boppoServer.js delete mode 100644 unpublishedScripts/marketplace/boppo/clownGloveDispenser.js delete mode 100644 unpublishedScripts/marketplace/boppo/createElBoppo.js delete mode 100644 unpublishedScripts/marketplace/boppo/lookAtEntity.js diff --git a/BUILD_WIN.md b/BUILD_WIN.md index e37bf27503..45373d3093 100644 --- a/BUILD_WIN.md +++ b/BUILD_WIN.md @@ -1,81 +1,104 @@ -This is a stand-alone guide for creating your first High Fidelity build for Windows 64-bit. +Please read the [general build guide](BUILD.md) for information on dependencies required for all platforms. Only Windows specific instructions are found in this file. -###Step 1. Installing Visual Studio 2013 +Interface can be built as 32 or 64 bit. -If you don't already have the Community or Professional edition of Visual Studio 2013, download and install [Visual Studio Community 2013](https://www.visualstudio.com/en-us/news/releasenotes/vs2013-community-vs). You do not need to install any of the optional components when going through the installer. +###Visual Studio 2013 -Note: Newer versions of Visual Studio are not yet compatible. +You can use the Community or Professional editions of Visual Studio 2013. -###Step 2. Installing CMake +You can start a Visual Studio 2013 command prompt using the shortcut provided in the Visual Studio Tools folder installed as part of Visual Studio 2013. -Download and install the CMake 3.8.0-rc2 "win64-x64 Installer" from the [CMake Website](https://cmake.org/download/). Make sure "Add CMake to system PATH for all users" is checked when going through the installer. +Or you can start a regular command prompt and then run: -###Step 3. Installing Qt + "%VS120COMNTOOLS%\vsvars32.bat" -Download and install the [Qt 5.6.1 Installer](https://download.qt.io/official_releases/qt/5.6/5.6.1-1/qt-opensource-windows-x86-msvc2013_64-5.6.1-1.exe). Please note that the download file is large (850MB) and may take some time. +####Windows SDK 8.1 -Make sure to select all components when going through the installer. +If using Visual Studio 2013 and building as a Visual Studio 2013 project you need the Windows 8 SDK which you should already have as part of installing Visual Studio 2013. You should be able to see it at `C:\Program Files (x86)\Windows Kits\8.1\Lib\winv6.3\um\x86`. -###Step 4. Setting Qt Environment Variable +####nmake -Go to "Control Panel > System > Advanced System Settings > Environment Variables > New..." (or search “Environment Variables” in Start Search). -* Set "Variable name": QT_CMAKE_PREFIX_PATH -* Set "Variable value": `C:\Qt\Qt5.6.1\5.6\msvc2013_64\lib\cmake` +Some of the external projects may require nmake to compile and install. If it is not installed at the location listed below, please ensure that it is in your PATH so CMake can find it when required. -###Step 5. Installing OpenSSL +We expect nmake.exe to be located at the following path. -Download and install the "Win64 OpenSSL v1.0.2k" Installer from [this website](https://slproweb.com/products/Win32OpenSSL.html). - -###Step 6. Running CMake to Generate Build Files - -Run Command Prompt from Start and run the following commands: - cd "%HIFI_DIR%" - mkdir build - cd build - cmake .. -G "Visual Studio 12 Win64" - -Where %HIFI_DIR% is the directory for the highfidelity repository. - -###Step 7. Making a Build - -Open '%HIFI_DIR%\build\hifi.sln' using Visual Studio. - -Change the Solution Configuration (next to the green play button) from "Debug" to "Release" for best performance. - -Run Build > Build Solution. - -###Step 8. Testing Interface - -Create another environment variable (see Step #4) -* Set "Variable name": _NO_DEBUG_HEAP -* Set "Variable value": 1 - -In Visual Studio, right+click "interface" under the Apps folder in Solution Explorer and select "Set as Startup Project". Run Debug > Start Debugging. - -Now, you should have a full build of High Fidelity and be able to run the Interface using Visual Studio. Please check our [Docs](https://wiki.highfidelity.com/wiki/Main_Page) for more information regarding the programming workflow. - -Note: You can also run Interface by launching it from command line or File Explorer from %HIFI_DIR%\build\interface\Release\interface.exe - -###Troubleshooting - -For any problems after Step #6, first try this: -* Delete your locally cloned copy of the highfidelity repository -* Restart your computer -* Redownload the [repository](https://github.com/highfidelity/hifi) -* Restart directions from Step #6 - -####CMake gives you the same error message repeatedly after the build fails - -Remove `CMakeCache.txt` found in the '%HIFI_DIR%\build' directory - -####nmake cannot be found - -Make sure nmake.exe is located at the following path: C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\bin - -If not, add the directory where nmake is located to the PATH environment variable. -####Qt is throwing an error +###Qt +You can use the online installer or the offline installer. If you use the offline installer, be sure to select the "OpenGL" version. -Make sure you have the correct version (5.6.1-1) installed and 'QT_CMAKE_PREFIX_PATH' environment variable is set correctly. +* [Download the online installer](http://www.qt.io/download-open-source/#section-2) + * When it asks you to select components, ONLY select one of the following, 32- or 64-bit to match your build preference: + * Qt > Qt 5.6.1 > **msvc2013 32-bit** + * Qt > Qt 5.6.1 > **msvc2013 64-bit** +* Download the offline installer, 32- or 64-bit to match your build preference: + * [32-bit](https://download.qt.io/official_releases/qt/5.6/5.6.1-1/qt-opensource-windows-x86-msvc2013-5.6.1-1.exe) + * [64-bit](https://download.qt.io/official_releases/qt/5.6/5.6.1-1/qt-opensource-windows-x86-msvc2013_64-5.6.1-1.exe) + +Once Qt is installed, you need to manually configure the following: +* Set the QT_CMAKE_PREFIX_PATH environment variable to your `Qt\5.6.1\msvc2013\lib\cmake` or `Qt\5.6.1\msvc2013_64\lib\cmake` directory. + * You can set an environment variable from Control Panel > System > Advanced System Settings > Environment Variables > New + +###External Libraries + +All libraries should be 32- or 64-bit to match your build preference. + +CMake will need to know where the headers and libraries for required external dependencies are. + +We use CMake's `fixup_bundle` to find the DLLs all of our executable targets require, and then copy them beside the executable in a post-build step. If `fixup_bundle` is having problems finding a DLL, you can fix it manually on your end by adding the folder containing that DLL to your path. Let us know which DLL CMake had trouble finding, as it is possible a tweak to our CMake files is required. + +The recommended route for CMake to find the external dependencies is to place all of the dependencies in one folder and set one ENV variable - HIFI_LIB_DIR. That ENV variable should point to a directory with the following structure: + + root_lib_dir + -> openssl + -> bin + -> include + -> lib + +For many of the external libraries where precompiled binaries are readily available you should be able to simply copy the extracted folder that you get from the download links provided at the top of the guide. Otherwise you may need to build from source and install the built product to this directory. The `root_lib_dir` in the above example can be wherever you choose on your system - as long as the environment variable HIFI_LIB_DIR is set to it. From here on, whenever you see %HIFI_LIB_DIR% you should substitute the directory that you chose. + +####OpenSSL + +Qt will use OpenSSL if it's available, but it doesn't install it, so you must install it separately. + +Your system may already have several versions of the OpenSSL DLL's (ssleay32.dll, libeay32.dll) lying around, but they may be the wrong version. If these DLL's are in the PATH then QT will try to use them, and if they're the wrong version then you will see the following errors in the console: + + QSslSocket: cannot resolve TLSv1_1_client_method + QSslSocket: cannot resolve TLSv1_2_client_method + QSslSocket: cannot resolve TLSv1_1_server_method + QSslSocket: cannot resolve TLSv1_2_server_method + QSslSocket: cannot resolve SSL_select_next_proto + QSslSocket: cannot resolve SSL_CTX_set_next_proto_select_cb + QSslSocket: cannot resolve SSL_get0_next_proto_negotiated + +To prevent these problems, install OpenSSL yourself. Download one of the following binary packages [from this website](https://slproweb.com/products/Win32OpenSSL.html): +* Win32 OpenSSL v1.0.1q +* Win64 OpenSSL v1.0.1q + +Install OpenSSL into the Windows system directory, to make sure that Qt uses the version that you've just installed, and not some other version. + +###Build High Fidelity using Visual Studio +Follow the same build steps from the CMake section of [BUILD.md](BUILD.md), but pass a different generator to CMake. + +For 32-bit builds: + + cmake .. -G "Visual Studio 12" + +For 64-bit builds: + + cmake .. -G "Visual Studio 12 Win64" + +Open %HIFI_DIR%\build\hifi.sln and compile. + +###Running Interface +If you need to debug Interface, you can run interface from within Visual Studio (see the section below). You can also run Interface by launching it from command line or File Explorer from %HIFI_DIR%\build\interface\Debug\interface.exe + +###Debugging Interface +* In the Solution Explorer, right click interface and click Set as StartUp Project +* Set the "Working Directory" for the Interface debugging sessions to the Debug output directory so that your application can load resources. Do this: right click interface and click Properties, choose Debugging from Configuration Properties, set Working Directory to .\Debug +* Now you can run and debug interface through Visual Studio + +For better performance when running debug builds, set the environment variable ```_NO_DEBUG_HEAP``` to ```1``` + +http://preshing.com/20110717/the-windows-heap-is-slow-when-launched-from-the-debugger/ diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index a5063b09b6..be23dcfa25 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -62,7 +62,6 @@ Agent::Agent(ReceivedMessage& message) : DependencyManager::set(); DependencyManager::set(); - DependencyManager::set(); DependencyManager::set(); DependencyManager::set(); DependencyManager::set(); @@ -372,39 +371,25 @@ void Agent::executeScript() { using namespace recording; static const FrameType AUDIO_FRAME_TYPE = Frame::registerFrameType(AudioConstants::getAudioFrameName()); Frame::registerFrameHandler(AUDIO_FRAME_TYPE, [this, &scriptedAvatar](Frame::ConstPointer frame) { + const QByteArray& audio = frame->data; static quint16 audioSequenceNumber{ 0 }; - - QByteArray audio(frame->data); - - if (_isNoiseGateEnabled) { - static int numSamples = AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL; - _noiseGate.gateSamples(reinterpret_cast(audio.data()), numSamples); - } - - computeLoudness(&audio, scriptedAvatar); - - // the codec needs a flush frame before sending silent packets, so - // do not send one if the gate closed in this block (eventually this can be crossfaded). - auto packetType = PacketType::MicrophoneAudioNoEcho; - if (scriptedAvatar->getAudioLoudness() == 0.0f && !_noiseGate.closedInLastBlock()) { - packetType = PacketType::SilentAudioFrame; - } - Transform audioTransform; + auto headOrientation = scriptedAvatar->getHeadOrientation(); audioTransform.setTranslation(scriptedAvatar->getPosition()); audioTransform.setRotation(headOrientation); + computeLoudness(&audio, scriptedAvatar); + QByteArray encodedBuffer; if (_encoder) { _encoder->encode(audio, encodedBuffer); } else { encodedBuffer = audio; } - AbstractAudioInterface::emitAudioPacket(encodedBuffer.data(), encodedBuffer.size(), audioSequenceNumber, audioTransform, scriptedAvatar->getPosition(), glm::vec3(0), - packetType, _selectedCodecName); + PacketType::MicrophoneAudioNoEcho, _selectedCodecName); }); auto avatarHashMap = DependencyManager::set(); @@ -498,14 +483,6 @@ void Agent::setIsListeningToAudioStream(bool isListeningToAudioStream) { _isListeningToAudioStream = isListeningToAudioStream; } -void Agent::setIsNoiseGateEnabled(bool isNoiseGateEnabled) { - if (QThread::currentThread() != thread()) { - QMetaObject::invokeMethod(this, "setIsNoiseGateEnabled", Q_ARG(bool, isNoiseGateEnabled)); - return; - } - _isNoiseGateEnabled = isNoiseGateEnabled; -} - void Agent::setIsAvatar(bool isAvatar) { // this must happen on Agent's main thread if (QThread::currentThread() != thread()) { diff --git a/assignment-client/src/Agent.h b/assignment-client/src/Agent.h index 620ac8e047..0ce7b71d5d 100644 --- a/assignment-client/src/Agent.h +++ b/assignment-client/src/Agent.h @@ -29,7 +29,6 @@ #include -#include "AudioNoiseGate.h" #include "MixedAudioStream.h" #include "avatars/ScriptableAvatar.h" @@ -39,7 +38,6 @@ class Agent : public ThreadedAssignment { Q_PROPERTY(bool isAvatar READ isAvatar WRITE setIsAvatar) Q_PROPERTY(bool isPlayingAvatarSound READ isPlayingAvatarSound) Q_PROPERTY(bool isListeningToAudioStream READ isListeningToAudioStream WRITE setIsListeningToAudioStream) - Q_PROPERTY(bool isNoiseGateEnabled READ isNoiseGateEnabled WRITE setIsNoiseGateEnabled) Q_PROPERTY(float lastReceivedAudioLoudness READ getLastReceivedAudioLoudness) Q_PROPERTY(QUuid sessionUUID READ getSessionUUID) @@ -54,9 +52,6 @@ public: bool isListeningToAudioStream() const { return _isListeningToAudioStream; } void setIsListeningToAudioStream(bool isListeningToAudioStream); - bool isNoiseGateEnabled() const { return _isNoiseGateEnabled; } - void setIsNoiseGateEnabled(bool isNoiseGateEnabled); - float getLastReceivedAudioLoudness() const { return _lastReceivedAudioLoudness; } QUuid getSessionUUID() const; @@ -111,9 +106,6 @@ private: QTimer* _avatarIdentityTimer = nullptr; QHash _outgoingScriptAudioSequenceNumbers; - AudioNoiseGate _noiseGate; - bool _isNoiseGateEnabled { false }; - CodecPluginPointer _codec; QString _selectedCodecName; Encoder* _encoder { nullptr }; diff --git a/assignment-client/src/assets/AssetServer.cpp b/assignment-client/src/assets/AssetServer.cpp index 3886ff8d92..82dd23a9de 100644 --- a/assignment-client/src/assets/AssetServer.cpp +++ b/assignment-client/src/assets/AssetServer.cpp @@ -24,7 +24,7 @@ #include #include -#include +#include #include "NetworkLogging.h" #include "NodeType.h" @@ -162,7 +162,7 @@ void AssetServer::completeSetup() { if (assetsPath.isRelative()) { // if the domain settings passed us a relative path, make an absolute path that is relative to the // default data directory - absoluteFilePath = PathUtils::getAppDataFilePath("assets/" + assetsPathString); + absoluteFilePath = ServerPathUtils::getDataFilePath("assets/" + assetsPathString); } _resourcesDirectory = QDir(absoluteFilePath); diff --git a/assignment-client/src/octree/OctreeServer.cpp b/assignment-client/src/octree/OctreeServer.cpp index f2dbe5d1d2..2eee2ee229 100644 --- a/assignment-client/src/octree/OctreeServer.cpp +++ b/assignment-client/src/octree/OctreeServer.cpp @@ -29,7 +29,7 @@ #include "OctreeQueryNode.h" #include "OctreeServerConsts.h" #include -#include +#include #include int OctreeServer::_clientCount = 0; @@ -279,7 +279,8 @@ OctreeServer::~OctreeServer() { void OctreeServer::initHTTPManager(int port) { // setup the embedded web server - QString documentRoot = QString("%1/web").arg(PathUtils::getAppDataPath()); + + QString documentRoot = QString("%1/web").arg(ServerPathUtils::getDataDirectory()); // setup an httpManager with us as the request handler and the parent _httpManager = new HTTPManager(QHostAddress::AnyIPv4, port, documentRoot, this, this); @@ -1178,7 +1179,7 @@ void OctreeServer::domainSettingsRequestComplete() { if (persistPath.isRelative()) { // if the domain settings passed us a relative path, make an absolute path that is relative to the // default data directory - persistAbsoluteFilePath = QDir(PathUtils::getAppDataFilePath("entities/")).absoluteFilePath(_persistFilePath); + persistAbsoluteFilePath = QDir(ServerPathUtils::getDataFilePath("entities/")).absoluteFilePath(_persistFilePath); } static const QString ENTITY_PERSIST_EXTENSION = ".json.gz"; @@ -1244,7 +1245,7 @@ void OctreeServer::domainSettingsRequestComplete() { QDir backupDirectory { _backupDirectoryPath }; QString absoluteBackupDirectory; if (backupDirectory.isRelative()) { - absoluteBackupDirectory = QDir(PathUtils::getAppDataFilePath("entities/")).absoluteFilePath(_backupDirectoryPath); + absoluteBackupDirectory = QDir(ServerPathUtils::getDataFilePath("entities/")).absoluteFilePath(_backupDirectoryPath); absoluteBackupDirectory = QDir(absoluteBackupDirectory).absolutePath(); } else { absoluteBackupDirectory = backupDirectory.absolutePath(); diff --git a/assignment-client/src/scripts/EntityScriptServer.cpp b/assignment-client/src/scripts/EntityScriptServer.cpp index 954c25a342..47071b10b7 100644 --- a/assignment-client/src/scripts/EntityScriptServer.cpp +++ b/assignment-client/src/scripts/EntityScriptServer.cpp @@ -58,8 +58,6 @@ EntityScriptServer::EntityScriptServer(ReceivedMessage& message) : ThreadedAssig DependencyManager::registerInheritance(); - DependencyManager::set(); - DependencyManager::set(); DependencyManager::set(); DependencyManager::set(); @@ -326,26 +324,7 @@ void EntityScriptServer::nodeActivated(SharedNodePointer activatedNode) { void EntityScriptServer::nodeKilled(SharedNodePointer killedNode) { switch (killedNode->getType()) { case NodeType::EntityServer: { - // Before we clear, make sure this was our only entity server. - // Otherwise we're assuming that we have "trading" entity servers - // (an old one going away and a new one coming onboard) - // and that we shouldn't clear here because we're still doing work. - bool hasAnotherEntityServer = false; - auto nodeList = DependencyManager::get(); - - nodeList->eachNodeBreakable([&hasAnotherEntityServer, &killedNode](const SharedNodePointer& node){ - if (node->getType() == NodeType::EntityServer && node->getUUID() != killedNode->getUUID()) { - // we're talking to > 1 entity servers, we know we won't clear - hasAnotherEntityServer = true; - return false; - } - - return true; - }); - - if (!hasAnotherEntityServer) { - clear(); - } + clear(); break; } @@ -416,8 +395,7 @@ void EntityScriptServer::selectAudioFormat(const QString& selectedCodecName) { void EntityScriptServer::resetEntitiesScriptEngine() { auto engineName = QString("about:Entities %1").arg(++_entitiesScriptEngineCount); - auto newEngine = QSharedPointer(new ScriptEngine(ScriptEngine::ENTITY_SERVER_SCRIPT, NO_SCRIPT, engineName), - &ScriptEngine::deleteLater); + auto newEngine = QSharedPointer(new ScriptEngine(ScriptEngine::ENTITY_SERVER_SCRIPT, NO_SCRIPT, engineName)); auto webSocketServerConstructorValue = newEngine->newFunction(WebSocketServerClass::constructor); newEngine->globalObject().setProperty("WebSocketServer", webSocketServerConstructorValue); @@ -477,13 +455,13 @@ void EntityScriptServer::addingEntity(const EntityItemID& entityID) { void EntityScriptServer::deletingEntity(const EntityItemID& entityID) { if (_entityViewer.getTree() && !_shuttingDown && _entitiesScriptEngine) { - _entitiesScriptEngine->unloadEntityScript(entityID, true); + _entitiesScriptEngine->unloadEntityScript(entityID); } } void EntityScriptServer::entityServerScriptChanging(const EntityItemID& entityID, const bool reload) { if (_entityViewer.getTree() && !_shuttingDown) { - _entitiesScriptEngine->unloadEntityScript(entityID, true); + _entitiesScriptEngine->unloadEntityScript(entityID); checkAndCallPreload(entityID, reload); } } diff --git a/cmake/macros/PackageLibrariesForDeployment.cmake b/cmake/macros/PackageLibrariesForDeployment.cmake index d324776572..795e3642a5 100644 --- a/cmake/macros/PackageLibrariesForDeployment.cmake +++ b/cmake/macros/PackageLibrariesForDeployment.cmake @@ -24,9 +24,9 @@ macro(PACKAGE_LIBRARIES_FOR_DEPLOYMENT) TARGET ${TARGET_NAME} POST_BUILD COMMAND ${CMAKE_COMMAND} - -DBUNDLE_EXECUTABLE="$" - -DBUNDLE_PLUGIN_DIR="$/${PLUGIN_PATH}" - -P "${CMAKE_CURRENT_BINARY_DIR}/FixupBundlePostBuild.cmake" + -DBUNDLE_EXECUTABLE=$ + -DBUNDLE_PLUGIN_DIR=$/${PLUGIN_PATH} + -P ${CMAKE_CURRENT_BINARY_DIR}/FixupBundlePostBuild.cmake ) find_program(WINDEPLOYQT_COMMAND windeployqt PATHS ${QT_DIR}/bin NO_DEFAULT_PATH) @@ -39,27 +39,27 @@ macro(PACKAGE_LIBRARIES_FOR_DEPLOYMENT) add_custom_command( TARGET ${TARGET_NAME} POST_BUILD - COMMAND CMD /C "SET PATH=%PATH%;${QT_DIR}/bin && ${WINDEPLOYQT_COMMAND} ${EXTRA_DEPLOY_OPTIONS} $<$,$,$>:--release> \"$\"" + COMMAND CMD /C "SET PATH=%PATH%;${QT_DIR}/bin && ${WINDEPLOYQT_COMMAND} ${EXTRA_DEPLOY_OPTIONS} $<$,$,$>:--release> $" ) - set(QTAUDIO_PATH "$/audio") - set(QTAUDIO_WIN7_PATH "$/audioWin7/audio") - set(QTAUDIO_WIN8_PATH "$/audioWin8/audio") + set(QTAUDIO_PATH $/audio) + set(QTAUDIO_WIN7_PATH $/audioWin7/audio) + set(QTAUDIO_WIN8_PATH $/audioWin8/audio) # copy qtaudio_wasapi.dll and qtaudio_windows.dll in the correct directories for runtime selection add_custom_command( TARGET ${TARGET_NAME} POST_BUILD - COMMAND ${CMAKE_COMMAND} -E make_directory "${QTAUDIO_WIN7_PATH}" - COMMAND ${CMAKE_COMMAND} -E make_directory "${QTAUDIO_WIN8_PATH}" + COMMAND ${CMAKE_COMMAND} -E make_directory ${QTAUDIO_WIN7_PATH} + COMMAND ${CMAKE_COMMAND} -E make_directory ${QTAUDIO_WIN8_PATH} # copy release DLLs - COMMAND if exist "${QTAUDIO_PATH}/qtaudio_windows.dll" ( ${CMAKE_COMMAND} -E copy "${QTAUDIO_PATH}/qtaudio_windows.dll" "${QTAUDIO_WIN7_PATH}" ) - COMMAND if exist "${QTAUDIO_PATH}/qtaudio_windows.dll" ( ${CMAKE_COMMAND} -E copy "${WASAPI_DLL_PATH}/qtaudio_wasapi.dll" "${QTAUDIO_WIN8_PATH}" ) + COMMAND if exist ${QTAUDIO_PATH}/qtaudio_windows.dll ( ${CMAKE_COMMAND} -E copy ${QTAUDIO_PATH}/qtaudio_windows.dll ${QTAUDIO_WIN7_PATH} ) + COMMAND if exist ${QTAUDIO_PATH}/qtaudio_windows.dll ( ${CMAKE_COMMAND} -E copy ${WASAPI_DLL_PATH}/qtaudio_wasapi.dll ${QTAUDIO_WIN8_PATH} ) # copy debug DLLs - COMMAND if exist "${QTAUDIO_PATH}/qtaudio_windowsd.dll" ( ${CMAKE_COMMAND} -E copy "${QTAUDIO_PATH}/qtaudio_windowsd.dll" "${QTAUDIO_WIN7_PATH}" ) - COMMAND if exist "${QTAUDIO_PATH}/qtaudio_windowsd.dll" ( ${CMAKE_COMMAND} -E copy "${WASAPI_DLL_PATH}/qtaudio_wasapid.dll" "${QTAUDIO_WIN8_PATH}" ) + COMMAND if exist ${QTAUDIO_PATH}/qtaudio_windowsd.dll ( ${CMAKE_COMMAND} -E copy ${QTAUDIO_PATH}/qtaudio_windowsd.dll ${QTAUDIO_WIN7_PATH} ) + COMMAND if exist ${QTAUDIO_PATH}/qtaudio_windowsd.dll ( ${CMAKE_COMMAND} -E copy ${WASAPI_DLL_PATH}/qtaudio_wasapid.dll ${QTAUDIO_WIN8_PATH} ) # remove directory - COMMAND ${CMAKE_COMMAND} -E remove_directory "${QTAUDIO_PATH}" + COMMAND ${CMAKE_COMMAND} -E remove_directory ${QTAUDIO_PATH} ) endif () diff --git a/cmake/macros/SymlinkOrCopyDirectoryBesideTarget.cmake b/cmake/macros/SymlinkOrCopyDirectoryBesideTarget.cmake index 9ae47aad82..37a7a9caa0 100644 --- a/cmake/macros/SymlinkOrCopyDirectoryBesideTarget.cmake +++ b/cmake/macros/SymlinkOrCopyDirectoryBesideTarget.cmake @@ -14,7 +14,7 @@ macro(SYMLINK_OR_COPY_DIRECTORY_BESIDE_TARGET _SHOULD_SYMLINK _DIRECTORY _DESTIN # remove the current directory add_custom_command( TARGET ${TARGET_NAME} POST_BUILD - COMMAND "${CMAKE_COMMAND}" -E remove_directory "$/${_DESTINATION}" + COMMAND "${CMAKE_COMMAND}" -E remove_directory $/${_DESTINATION} ) if (${_SHOULD_SYMLINK}) @@ -48,8 +48,8 @@ macro(SYMLINK_OR_COPY_DIRECTORY_BESIDE_TARGET _SHOULD_SYMLINK _DIRECTORY _DESTIN # copy the directory add_custom_command( TARGET ${TARGET_NAME} POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy_directory "${_DIRECTORY}" - "$/${_DESTINATION}" + COMMAND ${CMAKE_COMMAND} -E copy_directory ${_DIRECTORY} + $/${_DESTINATION} ) endif () # glob everything in this directory - add a custom command to copy any files diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index 620b11d8ad..c741c22b83 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -38,7 +38,7 @@ #include #include #include -#include +#include #include #include "DomainServerNodeData.h" @@ -1618,7 +1618,7 @@ QJsonObject DomainServer::jsonObjectForNode(const SharedNodePointer& node) { QDir pathForAssignmentScriptsDirectory() { static const QString SCRIPTS_DIRECTORY_NAME = "/scripts/"; - QDir directory(PathUtils::getAppDataPath() + SCRIPTS_DIRECTORY_NAME); + QDir directory(ServerPathUtils::getDataDirectory() + SCRIPTS_DIRECTORY_NAME); if (!directory.exists()) { directory.mkpath("."); qInfo() << "Created path to " << directory.path(); diff --git a/domain-server/src/DomainServerSettingsManager.cpp b/domain-server/src/DomainServerSettingsManager.cpp index d6b57b450a..661a6213b8 100644 --- a/domain-server/src/DomainServerSettingsManager.cpp +++ b/domain-server/src/DomainServerSettingsManager.cpp @@ -246,13 +246,10 @@ void DomainServerSettingsManager::setupConfigMap(const QStringList& argumentList _agentPermissions[editorKey]->set(NodePermissions::Permission::canAdjustLocks); } - std::list> permissionsSets{ - _standardAgentPermissions.get(), - _agentPermissions.get() - }; + QList> permissionsSets; + permissionsSets << _standardAgentPermissions.get() << _agentPermissions.get(); foreach (auto permissionsSet, permissionsSets) { - for (auto entry : permissionsSet) { - const auto& userKey = entry.first; + foreach (NodePermissionsKey userKey, permissionsSet.keys()) { if (onlyEditorsAreRezzers) { if (permissionsSet[userKey]->can(NodePermissions::Permission::canAdjustLocks)) { permissionsSet[userKey]->set(NodePermissions::Permission::canRezPermanentEntities); @@ -303,6 +300,7 @@ void DomainServerSettingsManager::setupConfigMap(const QStringList& argumentList } QVariantMap& DomainServerSettingsManager::getDescriptorsMap() { + static const QString DESCRIPTORS{ "descriptors" }; auto& settingsMap = getSettingsMap(); @@ -1357,12 +1355,18 @@ QStringList DomainServerSettingsManager::getAllKnownGroupNames() { // extract all the group names from the group-permissions and group-forbiddens settings QSet result; - for (const auto& entry : _groupPermissions.get()) { - result += entry.first.first; + QHashIterator i(_groupPermissions.get()); + while (i.hasNext()) { + i.next(); + NodePermissionsKey key = i.key(); + result += key.first; } - for (const auto& entry : _groupForbiddens.get()) { - result += entry.first.first; + QHashIterator j(_groupForbiddens.get()); + while (j.hasNext()) { + j.next(); + NodePermissionsKey key = j.key(); + result += key.first; } return result.toList(); @@ -1373,17 +1377,20 @@ bool DomainServerSettingsManager::setGroupID(const QString& groupName, const QUu _groupIDs[groupName.toLower()] = groupID; _groupNames[groupID] = groupName; - - for (const auto& entry : _groupPermissions.get()) { - auto& perms = entry.second; + QHashIterator i(_groupPermissions.get()); + while (i.hasNext()) { + i.next(); + NodePermissionsPointer perms = i.value(); if (perms->getID().toLower() == groupName.toLower() && !perms->isGroup()) { changed = true; perms->setGroupID(groupID); } } - for (const auto& entry : _groupForbiddens.get()) { - auto& perms = entry.second; + QHashIterator j(_groupForbiddens.get()); + while (j.hasNext()) { + j.next(); + NodePermissionsPointer perms = j.value(); if (perms->getID().toLower() == groupName.toLower() && !perms->isGroup()) { changed = true; perms->setGroupID(groupID); diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index 726aa7ef84..dbc484d0b9 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -189,7 +189,7 @@ endif() # link required hifi libraries link_hifi_libraries( - shared octree ktx gpu gl gpu-gl procedural model render + shared octree gpu gl gpu-gl procedural model render recording fbx networking model-networking entities avatars audio audio-client animation script-engine physics render-utils entities-renderer ui auto-updater @@ -288,7 +288,7 @@ if (APPLE) add_custom_command(TARGET ${TARGET_NAME} POST_BUILD COMMAND "${CMAKE_COMMAND}" -E copy_directory "${CMAKE_SOURCE_DIR}/scripts" - "$/../Resources/scripts" + $/../Resources/scripts ) # call the fixup_interface macro to add required bundling commands for installation @@ -299,10 +299,10 @@ else (APPLE) add_custom_command(TARGET ${TARGET_NAME} POST_BUILD COMMAND "${CMAKE_COMMAND}" -E copy_directory "${PROJECT_SOURCE_DIR}/resources" - "$/resources" + $/resources COMMAND "${CMAKE_COMMAND}" -E copy_directory "${CMAKE_SOURCE_DIR}/scripts" - "$/scripts" + $/scripts ) # link target to external libraries @@ -337,7 +337,7 @@ endif() add_bugsplat() if (WIN32) - set(EXTRA_DEPLOY_OPTIONS "--qmldir \"${PROJECT_SOURCE_DIR}/resources/qml\"") + set(EXTRA_DEPLOY_OPTIONS "--qmldir ${PROJECT_SOURCE_DIR}/resources/qml") set(TARGET_INSTALL_DIR ${INTERFACE_INSTALL_DIR}) set(TARGET_INSTALL_COMPONENT ${CLIENT_COMPONENT}) diff --git a/interface/resources/controllers/standard.json b/interface/resources/controllers/standard.json index 9e3b2f4d13..04a3f560b6 100644 --- a/interface/resources/controllers/standard.json +++ b/interface/resources/controllers/standard.json @@ -2,27 +2,7 @@ "name": "Standard to Action", "channels": [ { "from": "Standard.LY", "to": "Actions.TranslateZ" }, - - { "from": "Standard.LX", - "when": [ - "Application.InHMD", "!Application.AdvancedMovement", - "Application.SnapTurn", "!Standard.RX" - ], - "to": "Actions.StepYaw", - "filters": - [ - { "type": "deadZone", "min": 0.15 }, - "constrainToInteger", - { "type": "pulse", "interval": 0.25 }, - { "type": "scale", "scale": 22.5 } - ] - }, - { "from": "Standard.LX", "to": "Actions.TranslateX", - "when": [ "Application.AdvancedMovement" ] - }, - { "from": "Standard.LX", "to": "Actions.Yaw", - "when": [ "!Application.AdvancedMovement", "!Application.SnapTurn" ] - }, + { "from": "Standard.LX", "to": "Actions.TranslateX" }, { "from": "Standard.RX", "when": [ "Application.InHMD", "Application.SnapTurn" ], @@ -35,29 +15,29 @@ { "type": "scale", "scale": 22.5 } ] }, - { "from": "Standard.RX", "to": "Actions.Yaw", - "when": [ "!Application.SnapTurn" ] - }, - { "from": "Standard.RY", - "when": "Application.Grounded", - "to": "Actions.Up", - "filters": + { "from": "Standard.RX", "to": "Actions.Yaw" }, + { "from": "Standard.RY", + "when": "Application.Grounded", + "to": "Actions.Up", + "filters": [ { "type": "deadZone", "min": 0.6 }, "invert" ] - }, + }, - { "from": "Standard.RY", "to": "Actions.Up", "filters": "invert"}, + { "from": "Standard.RY", "to": "Actions.Up", "filters": "invert"}, { "from": "Standard.Back", "to": "Actions.CycleCamera" }, { "from": "Standard.Start", "to": "Actions.ContextMenu" }, - { "from": "Standard.LT", "to": "Actions.LeftHandClick" }, + { "from": "Standard.LT", "to": "Actions.LeftHandClick" }, { "from": "Standard.RT", "to": "Actions.RightHandClick" }, - { "from": "Standard.LeftHand", "to": "Actions.LeftHand" }, + { "from": "Standard.LeftHand", "to": "Actions.LeftHand" }, { "from": "Standard.RightHand", "to": "Actions.RightHand" } ] } + + diff --git a/interface/resources/html/img/devices.png b/interface/resources/html/img/devices.png new file mode 100644 index 0000000000000000000000000000000000000000..fc4231e96e25732a0659c911e7c15ded5b54911b GIT binary patch literal 7492 zcmchb=QkUU!^K0jVkgv|iCJ2E)fNf0XVoaG_TGE8Qi8U2X-N=bD{5Db8b#IKloYjv zT2U0g^ZgT^H_z*P&b@E$J)d)KqLG0X4J8{T005xTegroG07zf}00}AZ4gdg1K3)C; z003A65f*`_KF)z5_Wn))bw{7)PCVLP_AX8)PWFyreuGX*0075^HeB5-bYTyz@A>q} zc|wiGU!eztleTiTo9&Xv%w->6n+2^WettGN7I=%4$q$*?RY5_Zg!HN3*IxVRT3=qT z-A5{en}6BQZegj{C+x{WwnMIO%}jZ_V&>+uO~pqZGxBU{i94M~z9Pm~ON|tkL_nhe4vGl>maKpN^ch-AXrn#w!>8D4t zZnO#+C7K_^&>yf}6@j-KF%2AaDMCs|DaR1jh;MfJ#&$b%PJPHZ&(780Kyeu~7sZEI zvkkxS@3UM)w$i21(l8YsSgc8?(-@sU>wb2+ZScKQ@j-98Py#_^`>bV@+{7WaRijW2 z1cuRbu%E=?aJU(j&dPkc$^?(r)j!cSOTL||!^b3G(oC3C_KzLFVW#mearN~L_oFu? zIxBjj9SKMM$@AU`$nd1m%*+d}VY|z-^R z|Mk|lD3w$;jps&CQWVI2R6(-pwg(FI4Diu*U)wQ9nh>dIoj|9<4$CZq|;nr8h1zS=>7x{|49O|K$9I=Z9+<&H-?zgxxvNlX~*dJrwlJy?QR9talftP&$;yyMih`p4t{{jSf6J#VkW>oqMAl|#4ih07x@e;S-g&dZ zJ@tX-LuL>fCk1f>Z;(#uJaaiB+)ZFmV%v`|BSc1HTqUPukibFQxAVbj99v8ZnLSxx z{ZHRbYBe@!pkM>jzPCS%J)9{G!p95CruA{WpGuQ~`ytjA0OZ+0{Xy^nO~(SuCDrnv zfmDAu*2lhP=J{vc0T^o{`{g~2=nd@H#2H|pfaXx_fyHi=iL~aCL$?iD8?FHmtJ`KJk zG`rkf9mIg4)QkmavDFMimBH)lvrcB#S7=i(@K4_|>frkFKujfDnRtD}?(-$B{rY0J z&!KH;Mh1(&iMkFy19NKLhppw`{Bt3$Bycmqrkr6@i4P#c?qpknYG}oUlOQruv)&LD zy!lDH%FtShDyGru!Ifv#?8ORQOxe~Gm}kqZ^%`~JQ)IA-Zn#2u*1nK_69Ip79ddXB zM*+n(NfanB{`b5z^4Ayf*THq?X?XdqGd@E^JgZF0HfoG{l}HSRAUHh}|4`Zeh1I1= z@^%4l&-uI6POR(=r18!tG6;UsNT5B9_&j0#USDc2kco*&ff$EIqY>FUmGCmR?K+}i zw0^@wYkG#l!oWFX=%l`!liW|=9*is;H+&*ln&=(b`oi>H z?a0CK@UW+chwf(NrJ(BhpvS!T=AeXNXMBc(99~xJX^XFX%+G;W3;))*Z_4nhxi_Lv zy~oH75I*MSY88372x@F?vrkj#_kW=lV@6(mIitDe zpLAYKdxnLbZ?y(&CRsgN`okUi>&0GS0@uWw!u4n`#Q1Nq=)>gwot+A8->mx5JJ1tT zXAg}NF_x^QEteh7^IvgvTNa7boEn2LeQtA$8V42!8#mFfkF!5Bc?GAN`zRKu%+`;p zR4x-%Bha16$;o}uf0quTn>SBS+Oca6{cjmZB|1*4ePm*ebMae4Qs?+Dc|)~5@68y% z7qAAQuL`DjIjtJXlC?~VIVC)&u1nYWN!Z+v#)kF4(T=1~{frAHR$dTdldh*GJqY+= z=i~6pQj^Vou88}W-=B1t{M^bSUSG_1igLwVALqNM{{2cv95AYhIr=^L1Ba&z(45vo zs>~RaCF=;L;3qweZ@UG|ZP##WEzOyMINt-ZzG37@Dy|G)?mVI7rBFHkAw`UsFE=gy z)Y!O`l%188wKuNCy>uL@ZJ3MgK0Q6%TkT7Jbw9{^(ZhA9=qmz&Egn$bwBl54z^VpS zeL0=6e)W$}MK|~0^X|vhaZPTuQ&;?AUCa7A>m&yUxe7UaWW9VaSjQ-BZYaF~dD2ZK zcGp^!VkbbDzQNUaG{8x$@2s8vWPFqlzI5L3nI$ zoC~GH_PS$LaqT)WSGh)%%GaWdw#MNR8$aG-dBRyfhBSv_Fk%X513v>|FqS&K5EFB{ zGp#&vf`#d((FJTkx;xzJMPVMvp9bLc4N$@yG;OTTnX>bk_v=Vix3lWy{HP`ycoqIQ zFfWItFT3;KdfIgkuIJ#(@knWO$!uT^-eeexlSb8w(W6ajmVtdK^wn{%g#08kcii#B zrYoGq!S;8D4`<5}2Xj5ECyFq+BtLPI3SJyD;(fTy8}*UkMUid!L5p8eJn4C&1I_s8 zHdoz-1!-+#g8A9c0K?!#uV%t_AM%Qqkx<`u&=Fy{m`|)G9yWT+n%oZ;`|M5rnwmA~ z>MgP)s&t^%E^{_HYoV;Zn48IMr@gg-6a0ya*q_u;UAX*RhELsQE*;8wKhD&Sz`m#z zyf&s1dA1ZnMdFSxu^n|AUn)D%(R(!kQ||8ZX1Zu~ycc${E~TPOc>Uw=TVTJIU||f2 z$YXQ1YDFSCWZ4p~m_hs6aHIJ9SprmrK=mectYiGf&2F9saVg$((MjQ^PY)(yH1Pj6 z`)($pQ&DL8=l>LOc&vGswZ+-eMRT+~$Zssy2CRMm*qI}_1FYq%u z@!Pfjyi3m-d>C*NZg3&n@NHZxDuSCt_NV6}yMTJ`pXn&u>Tk_pYa|M&)ny{@vG&rx zmI$+Umr)mZ_rlRtH^iI;tkRHA7DZAK<>%nAcJb*J8*Y`aW6N<)4Vn0?vNZ8*IU;2w zjoz5OlBO*KXzDH|O`&In!OA3gNt{*i&OuzEvxq^1dM}UXHaB-lh~IRxMbF1qhEU=p znUt4=m-o*tRhr0N;{TwtegNzyAjg{5babYB`^8JG{vH z;{!1G8#UqO^s{4y1)Z0PV-0sh*dDq?rCH`j*iz^h*(v1S;KOti8tE}5=d z_+Ffm#J0t^FlX+u*UO8g>FH_1!G(xnA%OrjWK?aC_8uwD)|V&*ZAjKGGN5NkQ8K0} zENqy}2@3yt%$2K5Z>IBfVE;e-oxTMJrEi__g*UoHpS9ta7+AyYe7^bCmhtYz;an<0 zifn5Yrp(Jm?paf5p1!KnLT0>`GQZe1|IoG^p;U=E#hlFr(tDoM@7Wky9w5r=3h$o( z9?aaNd{KlW!OwA$Nu;b=N}YEt`+1JiKbYq?1UP%9>48p2yfYQ2PAXZ z4SHuH)*C_XP(+mNL6lFw9fiTNVx;%2Eh_&m}tz zwB|_$@@fcH>sRCaX>-Jyc5I689X-#_4A?2FtvInbLYuu*hVJ$HcY*BlGg%&`l{!{% zt*ILq3g%4IO)YA^(Th6`-8{&ilK+-zVGP`Ti;Vjq2-Qzxj+huOuMPfe*~nmJ+`v~x zo8rdnJG(f%CskL{`eAdvl7D~AKyTBs8nIQ{oB2Hz zn*Cx|!aEb9-Wk`sKsltB5=9|Qe}7H6pG|Md3fbg+r@eQjbijPhn>QMkaz|$2Ios5T zfN|6sJ6>pme~PV_-rSN?pgy(W&xX++wEL0CbCEm&ep>Fz-KoC^U9$@J;PP&e#=?6a zJu~B-BgR3p?IjENe=8vnZ6m^K3{VW(-DRB1^J?-CH!u@fNG1Pk`?JnA-BM)BV*wP%haplz~oGME@|T8j3#XhbF0U=F>6_% zW(N9#ll8*NcuD^K>*W-zPNU+x2kM1>-##|CWa$10^xQNjz{^OkWZRiuO&;2tVz=ch z>P6HPoRY@ILhXjHu?7r@ddBDlIvM^Mafp@s75X5u6qNP5boQR<%jG=e#a@DgQjI%5 z3_(9L=k3G_Vvf%BMj01cw^xg7XD$&v88E`IDUf`72iz5QvPgT{c=cm}uop6PUt*zA~W4!+N_bd^npr+%|R;J5yGb(U}@sh%$GFCnEpuC?^XG@3=|5g&X z?*zn|Zlfl~KPl?POC;ZoWO-6!+bEW=yYjRG;l4L2^@vmiY>tA|byJaV{Bti0{mar$QA^HPL*j0yDo?=+M*DnPMjG;+gcjFO3p!uS;$_tV>_hhyy+=A3f4W zdcj%bb<(O<@(z1Xzb;VT68=H=S(oa*jU*d}_OGvudw zgFHzv?N+?hCf9ERLutkb_ui&!8z3QCai3ZE)rQ|L8AdaGJ&C}luM(Q^wm^wkX)!j^ z9rfz~VS4S>V~NBm3&9A~c)+We$)RxAFp2xaa1 z^(0J(w{N=zWUpr7(6TTOV=?ul=McDOH}_7Zl4h(XB7;n3_eeU1JlID;R%)1IQ^d2B zi4oe}B_4(x^A@=G1}fOer#W6FY9w;{r)VI0+pmWDmhF}ji=JA*U3)NN$)N%7@?a6-m2C29!A276F&h+)lZA5VEs-Mrz&u;L$(l_=i~&f!c3+1Qw212YbUEHG~Bv?$PO5W?|}-s(EkqhI)QS`)GPR~(q>E-uxT`cbuufM_YzFp_~dOGY=cS@ zv&MRtHFNygT{6q-$eyG5Acj3YX}P^Ki?aMHZa?ZU4(&bf_Rn_3>!uf29!sKuUQ=(I zl6(XRksjMDcUta9mHSmePY(V-cwe}YC2A&HxNj6unJ~x8B81!NrAe*{J~0|E`Jb`& za_G33+$-EJ4z+~~!V;kZ;EVBejFcvhH0$ zvF$C^EFgE-@3i>*Wq=+-VrAm`a>PNk4xh0W64RB7@*Ro1+O>;;W;L*MqbNJi+7FbC zoy=G~;9C?N;E`O#{gxtJf3%YjAOrOW6S4317|qqs!kErR-up&wIn+{6yh!+&l_zmW zi!1DRRt18^+RP$BV!}A_&sTD8rK|n61IZ=I%P-N9{dfVX~2 zW{dYmiuS!@^YQlHKaX~EE6-X;1Qh|r{I6izN>2{)yWg7P_y~7r91RSR*4EZuSeVd@ zAQ886E2ISOn^^ma$zjd~99(D66QraY_=DF>pm_55^{3}*-O;DhPm*g4^WVUxkr7~(D_8N}`aYYI|L;e^H&ha?0e4%q_#pBN}v>0eU059Qj z`fwHu^}$ec&_}b1q5ZgN8lyZRf$V z(_jS@s6fGcTvbakl@7{7a-|oEkqMcxgr9TLw z7AdWf41DllB4k+;y$~J>|7^h$ZGpQb!MpR6U{Lon_ zmsJd!Ni7y3sgytTN2!hCndi$2Zayf7DbgT*7ej5hE*bg$6I<*d6qYrN95cq+2YIg$ ztwg6E)&VaEjO?%T{aHWO4gumbv|fA-Ob5{z+=Cg$(`{PIEmliJN@PP|WGo{L7lN6e zSa-WbA6m2h1zHZyfHiGQz?C&Sm!UF#Ov1hk! z?izn@f;5PiJ%T%==kBS*wtIFSYRzdmhV~zIlG*B#3bS_+lZTdhr2Q3dciwGpnCKM^YR(#XZItJB#K*stD1 z2E}mI^O@Bx>iE7~!1s)TPuP-)Rgh-JUod3S8v={12o>&akT3GQ112>a@Q}kt9?LpO z9r~r(0E|9_&IAVs&m4=%+z)z%66Y!tACoq6X)!OIh z=Pieu&zvnEEx9))!Q6?mV2D~7c-6g0v2s#x!pY}a{Xa8M19e-yPIh}kOAkg936KER z5wJb=PMSzs1sxDXO~q52h67WvAVpD|nWXf~lFtovq-qm;dwbGHgnzX# zcRBXch`k3;Sgb( ppolieo?-#G(+ve1e7m6%2Xur8Bb=O5)BpegKpSBI{|I~b@_)VVBlZ9Q literal 0 HcmV?d00001 diff --git a/interface/resources/html/img/models.png b/interface/resources/html/img/models.png new file mode 100644 index 0000000000000000000000000000000000000000..b09c36011d540759da5326ed70bda97592e9636b GIT binary patch literal 8664 zcmc(c=Q|q?z_k++V#nSjR@GK*?UA5X)hKEfvG?9ZklNH9MJZ}iQhV>ccWczFtxDDA zxqi?4{15NPbFOpl^Wj8kzEUC~WFQ0p03<5P3fcew5D5SP_91ux0AL~YWjO!_?@}CwG%5*|J#)=hJGJde(nViANFk7W$7D z3`;#tKRO_8 z_VnrOh9JpmS_!{^fcF$HR1Pvn5K0p)zIn~kw%97(YC?tf6oaKtT0rF>nRs2{jG@td zAA%=9Or<%SmF05Nh!02rTt_kPY(Dnt;i=-ccNNN^n^=HlI_~rfQF#Dd$2$EI^UIWX zUF^_8QJ~4#Nk0)P2T7gg97k5O!O_qs-JIj9S`~v6AR%+b|4@eMVr0DT#vxlt=q)UW zt3nw9!wm0J*nB}uE$bD=qI#k+0AvY;fE$fHgor|q0|Z5;dPl`z*#Z)_T(kRafe^UI zv-bj!S~gB5GIzT8Jfl&XXo1-ohHU6Ol&Xs6_g9f!JT&wPQ7EX>?XQ9(-6uIw046~^ z)QeKR{@~!TRw_OY1m4(N2Tf&>95r1!Rsjit{xiI$=#PoO#{Cab6j{HhetD;vz2r{!|DaqB ze21);ovzUTlY_q!4iv5;fI?C+&i}irt5?z1ixWPuXy~dq+^r{thK8yc>#9F0)!@F* z?Gd0unLd$&ba7T?OU+oJI$ekG!%Gfvp-3pp`${(uXK|b?&9sQ{5KRm1er;DXoPva@Gb-Pjsfgqn9fntM< zP2Ie=pN-IJOa>u0QO|l3?q4l|dqfta@uA4am9ndbt!@1sh4cQ$O*;&?A@Vy4Z>ox? z{8D~*w_Bp2s*uj2+WSRAxx$^dfq$oz4l`{&A@CK`O?rXHXt*5Y3ogtcmR%uKR9inV^cslOpQq`S>+$44FFY#BKgOO zi_t`%`-Fl)vTGCrG(gwp-5AYbAMyXteYN+?p=#NK42Nm!$X>5T{?|+_WYBpEbWdwe z9O|x%^A*3-O_T@?=?8p>gT9Fh6Y)Ukv=|b)VzZ!x{hi{yym6i0M7H#bKc;3Tv`dV=?fN z_1y_#QuZ@bPmfi&*pVZOQB5N4v_O71;QES5UD>GKaW5@!6o9$Li{M-t}t z*EKx>Nc~{fsAof1zOpgfX(fi%gQ^w*K|a0=-L&bJ}??&=rzQBm@bd{i~ha-)-}N@UnxKtlrtO>Gf#T zqjH_>A7RzR^QIJuOE1+8<0#^%?NiVdVL|`C<<6l9Ld=vY*$g_KC3AlK7;S-z!7~b$ zSljh3U!E^e$zO}KfBfbsNCiD}I`Bm;9r?ie!&-%=S^_w)?7WbfCsxqoRmBV`-J&*9 z3#{2c@gtzJ=Sa<;D%)1(AI8%_ais1 zxC3J%X!Pin)-SESreXiR-ncB|7>lLP@;pU)|1*hm-o{b9?@J(kr(dFzEyGr(vV{Td6}ylc(-coE*t=L%eZH zNZ}aex+qEIyyYzeKz`c_eFD<5WOwhdPw(48NHD{O*cKnkhB#erFPz?G}|CDh0 z_bVG}Oh{h09OQsZmDdLvzHIj_mt2os#}L|m9ftKTo`Vs8nS~88@Q^?hhj{gaHIMZy zbiKBRsUzU8^Qlz5vL87GY{v0OOlqau01%Mqs@(g(#q76M=mz1{v`KbqT)07S>s09s zucOoE6Js-oDony9tif*e_)VT|fsHC5lI&mtc|l!v+#Yzto-T$4Khz z#uEMQ#U%q@$A%(Ve^zT-4R+f1?5JT1j<>h9H&a`yfaos}Y`@;Welk<2fXx9W=oK zk$yG?lOm!IeRJhGtV|NM98w~gY2GQH*OS6jlRd>{L!g% zqAdQq6$eHp!h2KS=KZcqimpN)xVjT^ro=KMjX4z{pMCvv)ZJz@Pg=3Sn$vnQpGP>!N9 zmVTaqaxaGWP4m~cjgyxvS7(RQruWF7_EULMe%H00F=ixbFHcJVO*`KYj8l1-o&?)_ zd#mWbkCSxnXcMjt4&Gd~PIgjKAMAxGY>9o^Vi><26J42IpL8I6^M&@DI#9(a`oS?U z5UR;HK3(7+MG>S+!>W;n0#_Apyt^EFG@E^uZd(6v)$%hZc=x!)s<(MQ4P^`!*mR2` zmA!yZ@ft_39;bYf#)yggt^du@7b3wn5VCVW#`2sKv(dbRdO`*4 z-p&2aJC%t8vk=fX$j@1qW%u!mC!2glleRI|uo8~7slIkk9@2b?)7=bu$#!(=+BGaK zwtFY^&`z8OrK)S1Fb(?C=N8T_Q_+sI6^h!NnUHZXxxYR8b7y*E$eG-bY=|iuPV7`o z+C3zvX3;g?p?P*6s1$6r{3knq5#IIfj7BkIi_e5^*S^JS>C=!ocPXB25Te$d=5sA!IbQdw zdcaK>S=z&hfe}1^yREwr9$B-KsN^m=_E3VKy?z1aN0X^{U@?>ea*b z)r1J9tQ&G(1kmR3zvh>Gmb&FuU{BJ zyM29qoY_Z2cIe!`=WKH|743yQMqjcEVTI?he<*$~d5O;G&gy{45ze6>=^*?6*!aam zg!7aa*uwaZD_`;C1-v(``2_g_GED{-?{ZWnW+)U}+c;g_uUdao1jCQ%>2|?yhe^Ai z$V7fVAp(%EKqO(0Tvdk%YDK(r78)e{03W$>-8cuQ>vIEXG@etv5`X_M?_|QTcNo8m&hwo?D|G220O};_uVkzOP`c?#=Emg_`hNz zTK$WH4=1(qZWS_F+u8XAzLIZ+-cl?)eWRci*`5N$p0;=CjQp0=Y_%k3P8}xT7 zifLs|i*>}2tJt8H*VZgzg!=XqR5fXZ?kuI|23aED4w#kyyZoV9g|c-^%!i? zT~|g8{tOt$tzQ5eIDfAxX!liP4Y=B6&SX0&npA+-0a)!KcXL9Yo5N|VWIfz>Y4-_#{KWYT?X z_y9rj6+z##ACo#<@OPwH5((A$Y#YrTXzT5*H}c#6SEh|We|(8faJKuMhRl`aS3rg4 z?-Q==AOjZHWb)-OxCMtsxklDVR_X>)SQygB+EPRi^ocshVl^XeAi`UpR6K-yjSqhy z+c&p&U+srrR=pTG)sR=wzpEctj{)W(u)i5P#mKarG|a%%czN!bt6vNTWe63hwmiU; zuG6A&;`~Q-N>x!`(34a|@l`ud0*u9HGUsqGRM_2EV{P0%`(tb`&iq*nzyabEsR(5<&?>)O~996VkWg33SBd0(PxQs#aD*NmCL7f zn{17*7QhCU)6eVJ7ARxz*2!1`Vx`}04N3sc=-7pS8_^Lv7%}OCUg149%VNNAsmSdT zpOLvLr~e$cm~8Z}X$*P(s7fi|s4*eJVaCuQQDsRnG-3XN+b*YG3~$hy{)M+RSGQ2Q z&nGK7*HXysGUbh2C4z1$(9z$(8occqhh5|L^_RaM~g1&bq$&J}&wD5G={i z;^Tt7zU8w`KeuxDn4zuc=LI(A0d!P5`5yfU5sJDgVed?WSIiyITYyupFZNlRK4o}K zd$Yh&CfNb*&=;Q^k#g*nU-c1Dd3yblN8MRhsIHaff;)+y?y%wmt#$l9(YEu#!7MR2qAji>P& z9VVd#lW}cO_eWY}kSiM3F~WHjBDum^Qk;b(sd(016{Tr!9Vo>v$KTbh;NM@f_-Cq6 zbAsDaLHzaPnA7C3=Qv&~p}7q(BHdT*nP6ju`%|r=8WwXtXf*%h*U{7pqBd9vjK*#4 zWiN1}VZ!fi>jcTXIT&_}R~C=?BbJsxj7_*<@{tyXU{VH(f1UeDRg)|PD%U!v!!=v4 z+jL3={J_RonYG7HAc~7ud}r~^mZZH{W--cje*&3Jporcl^0%_(UTue&ml8E|>G>bD zIi1R3DLcA1hg)W&1)6r~m@?*Qw}Mu3w#gq0c+b?^4RHkgJ~`78V7jTK8P`!vz~1^7 z{kzk#4K#L8QR`b`lH;dV>Qsc^z|3k1yI<9NJbK7v+ zYK{YOZ3gcX!81yN=rZm~tCK{f+7F2kO)1*y+6224(OxWZtX%q(a^9xo!=I&}^*&BN zWd;c)pyRX{mV*!#DnbR^%682&*<%?S^(y@}!`w>)0WgfB;&WxXfAT>T)q0!N0BOC& z@3%Hfl?~PsFW3}(x368`+vP|eW1kF-?+^J3js`)$+x{@tJ4+*=d|^@uIxI^DU8JYT ztFiItFYB)|J!cp~c44UTfcu>A$xWi9a#qGY3Xu&;-)ma<@($_&hT_ zTW|qE-mHMKm(iIn7v*fX?+_WT(n91=T0vvix26X4SAPmV{IJ@MluD|$FjPRiw`8Wt zmY}jd=WH#aA>sxk0PyY3~bCug|ki3{+v@7CA~2Ys$`!RIeQ9ChB;rkBq6OMt^DvW~pRz0M&1 z{21xI;As%AwF(J|?Jj6hr5Px4SZ=dW+EMy>vwHgP{=RyM9>&KIiDwY_dEujHt_NOj z7izsJ>c{+NZR4FxG=4^vOYAmPbvc9zU;MWJ;^@1`?OF(~<3v33s7@+9CJ}90Hka;- z-^0CR?1*Uga*E4EfK6ux_Oomlt-1|gz6M5Os{eTS(VvlGw?2x}Q>=_JM;A4<6`?hm zp|#<*3q$*Am!d|alHE(zL-kQ4Jb(r%nJ`c;Gjv^D2fzj{WN&H(rWrWv)eT^tXNyvK zrGCSKcu*WY4s>&yGEpz2(KGFiDC)k^5r#AA{-+i1iq7g8|$HbFp#FYK^7 znoFKX4^vNJrdxZh9fWtW_2jiC!z(IHT5P%~EK};wwS%5!AqtO-MXcIWbB(oM!Gpv_;0I$)X#JbHX{KUKr8b_e7>tBh!(OCQ$I}f-`y6 zwtis$j>qNE=wR!mv)22U>iFjJ`tP~CY{?>@(yiQNQpXHgmQPp{jC{N*Y%S!m&9f79 ziLv7YB!(FMX0)72oFKTgB1TRPQ92$~kj%{To}B!ikag(@*waSEl;Y^+_B>60re>u4 zyqw!*8V4$l)H0ke<=KSb_Tk*RgFf$GQd{rhlq;$C_R=I1=$VE(5b$$kanu;hcwHT# zhVC3QB1cF_&%S%>&$;Urg(Nutpih<+L!1n!o4yilKTREKRYUi=s*%+sv6w@(mFqKm zx96692BZPg*4x}^`v&wjJ5CK_cOzuMi{M*T93-#@Ow$^*%8{PQlJN4s>e;P*bLzM| z$zvwiv#}-1km`T#8iD*Zt&dvnotEN*J$|pQ2Ygi5OFNSUbRQs;p~fW!!BFU0c}HIKKRmmeD1|FXq9F$AnVA$??D&rsY+_5Y6Rk5x$4AxqciiCBN$=gR*u37dco# zH4HI1Kdn!C6QR?`M z=LA}yY4^RNT|vMFPtPXCN?-Z;dfk;l^(U1nv?+-kx3j0fz&kyHNI?OCPXT}C-cZu= zJnPz?^EKfws!4Fft2!%^Ui{g7Ha`BL{o&54a{}VxB0vEF6UV7zU-5iVcHBUI;c9IC zSUF2GVxH@flgAydjLV^74CTSjYb>hbl*jjR3UOD2dxc#kDdkiY( z3m->}yI#Lab9>FBUJjTeVL24{(dsp*$!*3BZHpZ#Cy!L8VeiDbq%S_tQMHSWTZB=C z*@eP~3?-ol`=|Dp{5Nx%$BFnXyG-;9D0HsYM51S49UV{w5y|18E+_R&atS?B&KwyR z@w;i~aCOP0;h3JzOsu1a?tAyS)4_dTVASqVB|T}6u(j`4H?iUgu#@5G$I-cU7kqK_`=!2|G+S&BK!5lniU9{fM1*l+Ho%{9n8-J&(wu|QA_IFo{ z7c9P=FD81!&63Smemt$q)EGCV`SJ1efwkvux**K-!GCjbauP>zyDyAHF*jvMJ8#pr zc#1KV#!rsD)oJI#lS2?vg!j2oQ(wq`Zo>&I7b{KE(i%qqL%q~fG7%10nfT3G=S{9; z<2ZLB`NCCD1iWET(?%X`%Y5pc(edS1o{}mrAr8ytMRY}nu*uZVZaMDdr02&D*7@&) z-7{$cy}ql2E|$#k1m(Cq`sr`pe7_wLoLK?}xx{m-yc77QI4HE}o(P6<;cPtZxi+7w za=Gd2>l+Hb>k~|S>wkCtDlww)s#IgftiY3DI7U9nOGd8NH+X4OmxJb%HTS7OjIe+B zXIEvGe78LTZy~Mun3b|WK2xy=Yxg_O@Ty3UlFzO%)SZx#cqs1HHH!exi554c5C2oI z{-B9E)*aBRZ$W(!;m?W7ipzGQ)uduw{tGy7t!x5-mg6c^uyQRVWhmFD^PgymLEf8D zr$Hi72lP~HL$C)StYxN1=9#Do^@6AmG`jDGwEv|zdT`#I{1Kpc(kmnUHxjR3aGQ7TNOmi{&EX-=ZcvNxSJ$jUGZP**&kB@u9Lk7Jss~#9*;^6SPPVP-xb;i1}Ms8tpF`MJ9#=nTf)AWaF^dFzp3C z9K}V=&66?roqM_B69M33QYh03E1^SMR}-q=EsGCEM09C^cbYdqC4jlVx*i<{%}fPS zV4$@!lr8-4B0wq-10oUjs8?O4tw*muF~jGi->hgnmshH}U%O6QWJ}RApMvR_??l#P zGAXAay;2KgLyZ|qYXwLcmCAgwI5BA_DGx)qK^3b&P248Ga1pO9d6I3zPzhcGVnq?S zh_C{)#i=nLL_pqUfVwmAdn`uiED51QYAV>EMe-^a#x~nDMeYU7L1||kZ_P0@oz>l> zma(Z6eo?3}kZ5+UBP zmxo(>Ghw@=kWhOPWH_NP6f{SNnSL6f2tjB%AsYEsS&=?+P@Ol>;lSwmp_yVWJ=V~? z>yVSZ;!pcL$8JO>I0#KkoN(6mECThL?DD$ zEktD|T30NjDg;&{@FPE{3#iQ4xgF(k;h{#p6byru(L%t)1el&~2_~qs2neCcZ){OY zUZ3_#Ui>}LAU_6H;tivsS&OWO;Dd>?c6lJ7_L~g60`6p?-A_2USvC`Dr>vl$IXnzb zxFx|SD@HIqH&!ajOCAzxkBbD#VjM%wB%E83eI~5D~e+L_VFTFZISLD8wSHPa01z?^dyhI^`{FNdA_u$=wo|DHGtm zHSnq6CJRekm_}T0Ec1p{p5k;+o2FF>e>k&&#bB-!3-cpC(MDJNAXl~TpWirl)#0yS zzxEuvg{*s48aL|~32B^*WsFkBen1&H9jp$o9&OFckUcd$ZE+61_Nv^h_Dg9H6uwT( z35n}cbGjrZCQki~Y4JOy39QnuaG*i^-@9~Ix^X{uLw7KIW5FX!zplY;ZKP*^qRK>J z=V$BA!QFGAYXVYICQI*Fg{czqOTvt0a2bf_(_zo{YSC$QeKcgIDh0VNp~`Q|EoVp@ zwqzry?ENk;PWKDuebPn=kQB-7x&dEMtp}fTb6R~4Z_g(^oDsv|-ePKX%f*^nbvM2p zEeKP4A8k&lvYnjn;|u0nzOG5zAa37HMN~kK{Lh5w#c{dD=O=sS2Wz9nR!&X=SH?ij>F$l@4bqS8CeRw(<%$XJD9N=*PspJ zpUu7uukzy0YuQdasijWXu=a;<`}_Otyeq(oBDa(N^ao5Pf3MDWh9{~_n&7#N#n;f_ z44AJP%o%B2od_{d+L1<=P6>dyVw<92>b&o@mOe}>(F(_v zjM@Lay0rAbs=eNl)6SSP-C_L&=3jqWGB1rY9zUJj3F8kLI6HsjcQWL0+xPU(a`?8! z;O^d1uaRqh5QM&t%t}kM65Xp>ub#HDOLQ+0vMn2*H5CtWUmq*2*K2}jhhj$Ax;uTd zJRc<*jCJ0c{#ZYu5%>P4uijlT3DSHyHI1s5d|f`3Y!qQMTINn5>91tIr;B(WZ4)-h zhz&XT$F76p{@D(l|g|90{TOhNhP`EiXLJrd}IQHJaBH zSXRf3RJ>!YE-5i9?+4!WaH`{KMf_~KCC}2>X0F=g&5n<@W@bZ*j+Oq#V)x>tuG%Qs zuo^u{qE#gy*>f!A_dRb%3fa0_>%chO1~6(>8|x3sM2XN~Y*Clol|%ZFU*6ee2AwpA zL*t_b56Mt^VU3r8gN@RfM36yZgt5v*-DT?HM9=*AD56!XO6FnkU8U zHI2yiC#mIMX!z`1TxlfUw)rGUe{El@{y~T{e7haXEu)Q!0SM8n*2=S+&3+|p-)`E} z7Vy+CI48S~9W&{qjP4Lf+iZIO+cK*-#CS-tPBuDVSg=A!0Ch-fEzUs`#)U#62E3nQ zhSm%`^o3FtZ&SSKiOtJwvJKs`9#du)nOrSW*Ox4Cy9prOV3mCJ50r7WTS7GA6?6_C z7BLlq0EqP3A?6H<@qy`ZIvhXx0@3%wL!Pju(MdG4%TxYPj`-Ocs6GTBVS(>czkVHA zHSY3>d3aMAY*!IU%O?5|lG;L)o#R^Q@rB$KtKpM$_TpY^m6)_q=ulC+?!~hQ6d=;8 zZ~0p)Q&H<08YBeY^fbS{HF;IJ^};SgSUt5~#wX?pA(A3t=*6w~)_S&I|V+@!~x4H1Hhr@R{B0Amd!Qr$&MmP}J4c zUC&0bq>e0F1n%#al>9ZU5aetr{=ygwAt|!0k7{{eX=Yx7&D>%iec z&C7p)mGl;P)Dq$J;d@(KTkeYK7ktuWefz7ei^a*wN!5LUnZcd12#w5?kz}vA_lAE+ zv5*3&Da3d>gQ`y1G%>gl>@~T45ZpZYyzgF)6FD3MBBStR?req!ZpZvw=M%%en1CZg zH72-)1BApT-55tXt691?_64|O7Ss5BRx%g{0#u!Uvt!@uP%sR1T2rfe`CpCpUD2XP z)LhyA6p|XLIVG{*LSnbF^VLo=3Y(z7E4HlXM1t?QiMnMK%&!LqWU->b3Y_VjT`_yz zO3%8|ZNJ8tfObRcnkfg)&@jGzTbD03EL!gMJ@9RY58pRwh=_<9Z&o=}>k1pZ#jw~5 zI|s$ZoE&mkGBYu4yc#!%|e!Mz#KHDZPQ7-mVv=5!Gw2t_+%HH{M34{OGUlq^nTDtc^2fhCU;y*mia< z~PUn=HH}Vl{&%8BId%uh(JVBAw(hnvjg6;A2$45rDbcO4>Lx%4J3_g z(*Ax+Ug|j-QIq>s1#uB3sKD@ee4$C;rSLogZ7;(v=J+~?@8LwH(JK*gMMYZ2pDh(k ztld>&0vgdPqEuxSRCy?4#)Tn4p=ih_i;+8)`zdna;&gv_AOC$xfutdRA8+8m{(4mj zwlmvs-Kg0|m|m(|aqy+hSDdR=38k~u&F-b&eThsnp(NCcUPcWr-C>gl?)pxlG~}j9 zt^o>b^9`=}#n5N5#rhSE^h+sHPWKf*NLV_Z>@I}!{_O96oOMflF^z8yPf3&vZ_B11^aR*H$9ReJ>|9l2#PX1)wJ$_6wppO~au<;M~Zhx2M1 z+V~~pj|Gs?+*h1_Yr0?qYjZ4A*{eXM;EMGbD_SF>WK_?Qz_BowAgH%j0$-45lPrcn zY}mhaN=b|_si&8hmnZi`wV>59bKU5gLG{oG*|ndQ1!dL5RJ~`KNNwa8DL;Xokc(~Q z5WOZ^zEV9?-|~0-GHB0|-!+s^yja;NCC+2DJGP?n;|7`m&HN74$CQ7d{&Q5xk-ODE zE(|8ddpO?^OH8^Iu>$*N&Qz;5J`VNuNE5v*k6Cev4dK0keP3)hAyGl3M_J`&4Xpwl8PL z6ID6lc^OJtP;V7!sq&t9TJh}#L8xWI!1JxT@TW)us|jY*Q~!5? zrX0#K;VVOu-emaU+S=Olr64Hf2HmqU6q*qscYeI%=)+s4rnxNaMld8@9xb~c-k3%d z&zNeKG_G$TCWCQerc-YeY^X|nba2)6PJAqA>DQU(t`eABP!ciN{>{~$nbb7lhsE12 zZMDoM*%c~yM^1yZeSO;st#`0)GlR;gQv`_Q7nr0?PrBh538jE^7^mQ-OtwUtIy}|Z zeMxDMTwx>nV@nFH{jrvX)}kMeZsSK-C<%H}=fkI?UY032ccpc-iW+wc=c> zQW?0Me8fASM#82+=qxwGHp{T3<&19uI48t)gAj)PzNj0BtYg^=bq!7AHC;Hk^rC=?2W6*pBW!jt~gS*h2pK z-O5f|ClR+*CN^A*ij3Kz>l~&c9?rm5H&`AeMmd9MtE(ZE9P!*?Dzn;^Q)y>D=a%fQ z@u&IEY|K&D5FXg^(-Tpo#Pk}*)8+n2+O4}8{=So@cI(*`LY9MU5_9*k@2}xqrymF+ z?%H5Pc3i(bn79K++~s+wl7kB$3?6;cWs2(faHDblVU6w^uxPO`aV~Toywzv956^=@ zHf$!9%>c81t|%)56-BlmLn2Yb{PJYU-0yP^W;YpVqwnV)rf!0i@92z)BQ%UWq{Q4; z5H~=3f6VvjasR+&IX87r?{U&rCnW_1MS2AJi*gQwn^eySmRCnKRhl<1M5;9-Kj;%Kilc!c=ANFO zvx39rxJym3l*sYILj&yi3Lf7eNQ(xCbO;c*@rmH4Di=37qZ*3`3*gCFBz}@L3OG7waAX1b-DBhV>qT}f@V*e#l~jz# z)KtRULc)TWpDY$3P;NXZ-y=J>?b(LavC=2QE&k_*Gp(6QEEdp^0vP|3G8VJ3Z%x_Y zck1G{(x1NC8A1Iopcb0<#&(B@iij{Y2mYOZYm^;L*svfG3*|`$TSd<1eo9YIU!8CE z-CLS#@+|zGE2Yrc*;%=TSLCULkgzX!V!oLM8L?J|d&ZcZ`Q{lf)yjVXbgwX!_m2h` z_!JAVvix}QbLey4r->>P$I~1{{%n4i6Zh?8Wg&5Krn%NYsesm7t^`O}(4Ej3(tNsR zwV{i}`DR}cGJZ?C04&66*f?)@u{Bc{gTK7Iv`nMgZE0sj!g#(QOO@|vb2?yWjrdBp z*Vorm+?sm5S~sbXFdlH=Z>eIz*eA_tzqOIKDVV!PRyds!Eo4223=)?JypnLp)jXWk zjf`WFx@FWFAW2{U?u#(mHG14=`e(#8T2@#(SaEesv?50aXL@ufvEzEMI(*`NbA=R+ z2EK%?oP4mZ)c9c{_W5(N#bp4hTh}KmNBp0Va1Xhl#F{*&tq*+H)&_O ztzwmmL&`1NM>L8t1fS7_lke8w4K#jplPMZjrk4f@vgBN&=yAr4bMpIGo8bgtdcvR- zO@s!2(XO3h2H<5eTtRYhHla?~FV*#0Zv@}Q%f~FiPqb;|8 z!7cOHB%c^U1CB#kd1M?%W|T9-UoX}3gsn=5dEiHjWcp$w{qVBV#<^EwKrEDZ@aEor zuCdH(`6?9-I2sGJiT@_d#Idkqf%XQOIVeD!BTc*lMSz9c#5TL>lK!lZ=+7L2%0hZN zArPTgMDtXl{i_7N48UrJ3O&)(T;tSFPAmj4 z3Nn8QARf(&gb06oh8+Ti`Ln-h(I7D6%=f3M0L3`Mavce?`c7w4xof8Vns}o??h$rg z1`Flg5HwWH%2&UWap`ko`X>z%w)*%#5llTRKkJuOWS{~T%5xReX7pxrsXMLb7o;90 zWy1p9QTb)>T*3iQ8I>N;x!>PD;8+VeW(y@*xjv@#ts9p@elx@K_Cbs1X!`o zbk=4LmCB+3t2Xk3KgJ(R1fc0Up7!krbPxbC#XZA5BoctM)8??$bywHFTqc2kpZk4Q zdj_xyDe!Dk>N`L*0H_0Qsp`u|i%Tdz16W0oLTm34`C4YUU#cC101|~Zb=36gtnYY) zCIZ??^PR(94WH3@ER!*SlmK=W46CdxC*CM?4ts(n0Ya{Kym9ZDPqrP2TmX@TK|_dq z)r2ioLwZOS*v*z4XfcIp>|=FdoUwJ$iY|df!0u0YQC9v6xjm;|$VoWg7$B_?E3iz# z84xJH@xwy%!A#+sV+d#qL_Y4)U=zJ*@9;@Cg=;tB-15S!Wj{pDw literal 0 HcmV?d00001 diff --git a/interface/resources/html/img/run-script.png b/interface/resources/html/img/run-script.png new file mode 100644 index 0000000000000000000000000000000000000000..941b8ee9f13664fc2b2ec51a75273ba3e553d3a5 GIT binary patch literal 4873 zcmcK8c{tQ>zrgV?q0!GWLkk-FGG(jyg&5hUg)+9nWM78L*s>=}j9nB+un6J+0DzWuyA%KbLEmeZzGj{-z5x!8(14D!rz2WS-`&9#ZHjhq4)X3o z!vTP=R3D*pCvbE*R}%vr6pHSQ<&g3XxyH^{wH@UC!>xW2H;-voM z3h|)4jo9HWC5cTvJyjG`L+g)JNtWJfOe^clAh;x}4Z(h2Q*_h_Jm~!{;!UuDLc|k` zp&mD&5xeKQHNL53sY&Px#IRX)h1wY{%a2%sJ1=!WAgwhu2+(!DKfVoruoP(XnH?KaM>Ki@gK|@D(9lGBm95(ku8y-BI5)xsZK5>su1 z>a9$bT;1_1D=N`|SNAo6oR{b1ynjS&s+67kjJ%HG0&pGGj&uW$exv)d85SbcJ^DG4 z@=__(BkiWI`Y^udWc~N)h>)O>NzWnIiD2OhxJ9t)=AldWvpTxW1bI#kNP&phzn7P* zqPUC-&Xx6?fUSTt3lqIAfYbT!cgp3Gu9}G!MNf>KIK1jEdFAT67rl;OJY(|Z06_pR9Ae;FfVRJcM|euweT5=bG$caQb; z^|gFc3s@cVfvG|lFH~JJRJ3jQS;hO=^9JAGZc=siWn>~yydmB_eD>c&UP?tWFh2+#EW~WQUxFq!a};IG=5a8 z8y#q$V3`<)tqN7M#1i z1MugSA=6hio`Pb2p=)ZnuT?CNTJH2A_ajZw_cDm{v$1R?Q7rv8Zn%E+`*I$dsJMad zqP8T$V=r=cPsQnZ1c%1@S)@ji263V3#8rezdRAi4Zl>|$Zp1#LwS~I3Jd>>T{iC|0 z`IDW+!4||R3Pw5JJBQfHJy8Hg3mjT%qh%H4p0iOR?^4|2y@Zd5JPCp_tJ7+Ig}04e zM;Gj*C9zJ38IA*DL^r&$GMV5Y;_%?0Pj#{e6X3P3OXNBQLyyH5h~DTjvKV_~l=4Ub z(#rXgXODW7N$^7I2*0}rLkJe~%zA048VkgKMopRU7_lNiH*f{{xmXf)mix)j)BP@3 zK3Dex3!ZvRhbDt}yO3Yu@bPqUFmS`cG017A>*YSmI!6rJ*Zy&d(At5?hOp0Rinbbbqznt7A=Ti(HH8`0JqfxVbc@S@6lX1{iWgsfZ+aSaLh zW?AvurT1WqKFXcS$;rXC-V)blI__`UK~={{QXYc4eH65W4%8_o5e$IE#s5Eb{wMSy z>k{Iz_^7C;^q?B&^d!_MH}kP3smP_4SX(;+OWH}66ckU?i=g#VoJjJQ*QX4Z1HJ?V zW18i>3Et4ozP8Pd4t`CPopp{juL;QO3w+FNUdJkZr1!s&4G*+&@kv%2QcQ{kcbM|L zm*mk2J~=1s4K@5dLA{7F=jIBSe>z4!c1B<(7UW=UEx{%k$L@`>mj^q*eL*cZ9d)2* z1^z$?@Ve_>d|?UhpnVEb!k~3|Qgy!@mxrqM~3eRtJ53eQ(9dp3+Pb zC$p&JnPBLNn3*}e9daUsF+SUysT}#E`Ebu|bN=(IBH9x7AhLd3q-vw%tfy4i(E;P- zGtsbp?{z2uIX$5C$nEU&yPAvNf5JqC`KDq-L)E}9^^}5-4j9KJn0reVx2~1AYaMJ4 zwD=bYP;)i#PZ;9?$HaA2R6Qr)>l5t>XE<+Ti#MXghjQogpZM}E``1(T`)@ebKbkXG z{eg_Q2gAKQs@*LJVG(PEI0x;D@CzSy-7w>k7DS&4%>T$j%>Qdr#8E>ffY|nOa#@UeizND;dvrdm!3fixH?ic7Rt!PpgI4tZmDIoij zhdl=-VLIYjq?VCHCl40GP+p+(k%PAPUcycNE@apdjIw?{ysgInExX*M!m4*3(Tbaq3Gxt zKtf(d;e0Z$5BaCN+?!wWoR1~s49xbM=dRmokSl!pTvE4QOV8Ii&Mpp?cJXTMd&%Ql z-Wpbyg?MEO8w=rm=;@Wn$5vN#M@t|1(^Ac*>&OHJS>j~(bHAa(^8Ahuw~U2$3#FkG z-|&<@c(2Iiq7G49V^h=N_AjlYeM%QZ8>^SzxwvIU_S3$-J4<(2}eqV^aJ z`x{?Z%_>Y4^pJ?Ac<_dUN{Q~IR{b4Lp$)YK8#M%KneE)$aw+Tdd{ru@CaaDA?b6Xev1e%vj;6O(JG7KmweW=B)>8|u)CYKF-{NBG;_7HhBwvF7N@6Qy5&CiHw$i8c_IU~7C~yW;5ziL zZS+c0KaG~4_=Qfs!%LpiiX7OXOv2dlJ0q8(S3QLY`N~uEOPo(EA0*$6$hvcvWFp#$ z1wAx##~ZGWB6K%g(O*%hs#F<74#dF!x-8usS*DlXjzp9v8(Tbvlh+QYO{%%GIf$+r z_i%%;VDi_Y$w6Y+%mv%;i+Gyb;fJNVPgf;sgIkv6 zszVH}Yi|P2DJpWr$INC_E40}Nx$0L~_{Ld3r#Roxw!dOVK#5M|H7#qyCJ__!DgwepN<*1*9Yh2 zAOvmCj1?zj#bYwuGjbxn{8XEx8%1Y)uG0-}l!3kp6+CN-_RzI4T7JbZ;c$BPJwH9u zi6MGB?W{#DR~El?EEoyiVbmUKaOax9n!><)6!ci%s9sgmRiERvWRtiR*W5#{!AnVU zIIf+>$>6m{9h&yAbb-KMf9swM9^*6u($Vh8M*EbeaLx;Se%C4CqP^Z7zXuw^^N)?_ zOg>+y6HbUrJD$<(vEYYygknN0Q4B?sjzX5tLG#L8)SV;!a1=v_hkEl^V%8ZD3sgl; zUfqiDq8wv-SGCs7cBGVA&1;|~CW*b*vp!9UG3vRO*6p#{^4H9Wl*ThQvh|>q z1t7*>^b?%k?BJNY1hm0gciP*VEJUafJ|OmzQt74|sbXfN@E0$P6>)IaC1o(gyBaRI{5 z88~$YSxMZArhy;;0uLD_81p@`xAVwG=kePzXw6X4Tzr``+Qju}XXpCuuXpf_u>CM|4;wt`CoT;bwtRi$N>O= zaKfKC2LKW<003N21^|GuJ1)Ni0Dz8h@QLw^BFDr9MUw!#kf>mimeb{+P|`V4P)Gu; zhlB%wy`xTN?7ZS9KY#e(q3WxWI#aAaU3JXoWE#o=P(;b+v~VaZsnLKf;Mbxa#{gSU zTC%oAXdiU8TI-7FZmB~Xc>J3^cV+P@S5nGi_-4Y%C-tM>zAlfBUJEt=RJiBu0T4<> zO8(zqOGq-6Zg}$H!w1(y_F2CZ*FI^6FT03_yIyYh^A8MLB*w?bW79zBhb(_ba>oxDwR%6Wfg zI|`;7n%hKwrx=@yS$2g~ol3CmCW5f`9Z2 z`mk?c;-S8eR?^?C^j)rVqumy@NGSA!3C_~>35q^7z9T8c>ajMxAjHyEcJ2}5vYwUJ z25g8@wCSrgVCT8b1}JU=HyxnTe+t%>+H6h^>j)^BdMn+ywbpo1jL1NuvdgK9>dlvN z7z$R9LJXNH=@4F@;BmHHhgUk1-DY1L$%GkTW#5bVx?f$0hBLI{#XXCQCS8h3Q4A^d z<;f`Y^sK*pMjn}4sh@^wE;nK1?NTDQU#hzh$A7EI)XOQgUY&-)@Vvv!`oje&f7FF$ z$wzN}yHwXWYpU_&oqCNZ#KHMf;hvShekBMkBl<0kWfNp-vb)r7HU8v4?d9bd3?yY2 zapWkwj;O=1ieU*{{i)(8`JBMz#)FraL!3^c+F2YC(BJT8+YJdbRa>VH_HpWQ`P`_s zWT%>0Y~w@^i=EIBj(xJ@2tor{fojS=Yjln!e?|C8J?KW?M%QO-0n%TdA5xWW+rm462 zY{Tamat{b8FcF>Cdr0T+EG;Het~#VR4LnMFQZVoaekfK8%Ii zfe9pwaRX6xy3BsUM8#^>t__U8LQYpA_#AXAQrLf%oWa}rtV+*g4$4eNr#$e5V0`(V zfv|6cCgt#Ag*Qw@oY3SDJx|Wy-oJ?|kA}@wIjb-}qo9Kpl;O)Ns@Gjny>DS)EZX8{ z;=!l<$kk4yjZOOF!sMZ{<@+#HR!25k;w)W>%lx$4bSJ6l5`&({HOhL2>ErzD!MudC z`fsba8r^@weyw1hHsOFdg~mW-i79^3G9lh!-|Nx6rYbbw-z@F$9AGNEQZq%t+&{7F z!*|%*L{7mz7=MF&>TV~tQ`X-7f`~!AuOieRJU1!wq}jBxPY@$^o_G-B@7i?FUM)Fo z(cA39`IvubBqJ{l4BP@V6#Guk_hnP-9JJr|{+!Lj*?hnK2vVy36T8qTrEJX|6dHyo z3|>`7k2d>xG$e+LZ)%t*{iFbUqg&N%Py37>WZ9cYV(1m7X*u5Bo`YGod!fEVtz2t? zRS(?FVYPaqD%|7|KO?KhZ*30YN4_7ca0iP5)RYA=m5G8Z0$7x!xtVh9OBQ2~lI zSrcQZGUkt>@*28xRs+h>HXxbjWGRZCdpH(*_N~Hc!=pyB%g{&Q)_P>{UYv`7rFkBlpl`KpZ1#5X z$^s%?Hl|jOqh5QK{*b(ml7z!R0Fp++f5%bo33~t@kGX6Q^wGW+MWD-ga~)k)_4V~U zg1^`}dDlCURq|?@{gx@L>QacN5S!$nkm4|xL#VoOcj>E310qQYwdKb=y=`lhXs~i2 zyWGrt9{-*o8kv#NiElh39Uvoc?5EfagAnO)W2&e1p1HLLI^zQx%iu15==V*+XpEz#n7g;dqd1!{24lZS$JzF&Ml}Y}^J{N$yEsSm+ zASXFh#am(KE%w`1tOoYh+3US*FgW5hrj@Br6|7X0)`}AhzKl`ot#b{@bi-rjTSS-X zP@m&8O=Fv9Ae7Z^O7_saWO~nPYEJkV1wYel^Oy`K*Pd_H>U3Gv8l>2PzE+HMwe~`& z^X7c3qRGA4Sck3HGkFBsi?S)ZY|Mg#8I8m=vTmk^p|g?IHzyu>0L~ZxZkPYbY(a#C z79D`Wf6h<~GNrLD0=@s?Q%G%Y!#YzcECO_dL97jZS|~1&mmOLwr;Sm88}c*FJDam+ zjp~n|fBnjR7K%^ReFL@;Qp6p3009wntpVrCf}KMiCO|HIvpy;TbGa8(6(3$Ve@*qq zJvhsDWxe*am%?_Kh7@u3PNxhp)& zGlKJ1*G4diHKvDqD~tDT__tH1-6aT2lxx<^b16OB3){z*^F_nN#qj0%0|<8PM=iOb zLZqv-+1<{ugjk=sA`WSC<;!Q(!zt)~p}IynkE+dpa w8mdJ$F@o>^$TljjB#j>>_U`-F!(BO3fT`8&WfDTw1ONbVa&SFU2?wVA2MC*sc>n+a literal 0 HcmV?d00001 diff --git a/interface/resources/html/img/write-script.png b/interface/resources/html/img/write-script.png new file mode 100644 index 0000000000000000000000000000000000000000..dae97e59b14bae50013b583cd9cb3b5bdf6ad464 GIT binary patch literal 2006 zcmb`HX*Ao38pi)h(Ww^WC~;|$Yo=Oy6}h!H%2>+{#aNnJBh9EKC6-vKrnL4lqlR8f z+CxLd)*4blsO1QWsHU;S(vqMgmZY&S_c%TCW#-;9=gheuo-gltK0NP--!H=jVRz~) zwXXmGIOSk(?Fs;Z5CDKBS!n`!Um!t4q*Wpv@1Fw zC@!KOZ4Ll3cn51scl-o@sspQp0s1~_21*qu6P+5Qx8)(@64A${yN;nQG-&a!vK46|YxiCC+UCW# z?78c1pDn_!(s`qA{2QK}Osh^O6w)&nq|Y#8K%!6GZ2QSKTb`%$ z3fZ>{+WbzhbKE8?t5!s}?(f#PZ^l`Cy89ua>7VGH@y;Zlu%JU%-FpS2``0zP)S?M~ zYMcE+3MG7Uj=OMBeYTuBMD$bP8*mLO*t0C(S41!!jdI!=H4CS}wHew%@uOnH0Z!6q zos_8@nng~FU`KAV#e8;03_6sDa8IZg`}c(Z%?KG9_pr}Qda$=!#5pMW2uHg4cCb2Rtjhd*rqmR@Ph0qaA_#**$5H#z#5Q?MJ=)Ym$FZY9?% zS|;<*8MQ}Nr2o58>dbfJE24Op%Qi~X%CMlTv`)|WljBXHaM;DiJZ4^&B916Q8tH`B z-RQPdyR4W#97+6;nfFACb8g?Ma&$S41a8QYlLaXlK!T2Pm6n`mNlVU?fJ$0PA0<^z z7IYK^1HETh;ov5s#Q3_pMbh3JFLrfW89PH2b8Kmi@!LWkWApCXhc|bgSl5s0FiXum zOH6P?>2-|Y#)3HGd^xH%B$d%Dc>UAX!Tv6x z))ph)9&g0hbj*MckS!%+&ZVZ86Ki7DK>eB|4uq?38_etv!)W7hRZ-DI{NAdSd0U7JdT(r6szh|#Qp-YgZ1M|+?autgHT9)N;V z*=+WZE+#tuUAJQfygZURO})$U1FYaQ#g_IR^PIiC=Z=LB^;7cX?RSkMaK*8%=h7iu z$&u;eD}s+&3PSZCR;TJP(Ld&?*l!q;$yn^<>-AoQo`O)_u}9EtJ04EOEg&L9^MlNm z5eV1hasGwKu@{1_2Pb>;wVWSXVm4G{EX(7_nALToi@caH&RYn`95W@l$;-i zzxg}V%;`a%N@yzc?8*kM#E35mz2wCZcn2UTS7|4!+U+>gA*tq?>zzglk-I){VpB1! zv@IRN)o`N7;Ek}O`C$`Ii$&tzr`v{YKBS2?pR^Js(v*Jz`me?trK~fp%{Z8;)?)BvYwS>KaG-Vg!;)==52-4{=z4A@{ucqO zr$jL4JyU49Nq10&i%vjed#)3d+T8q2FnBT)9piD=py#!GLdYRlBx)gCVJAACE-2@9 z_|1WtF~PR8^M)a_FACc|?wxgSmv$j-rr)dWLl4yybP%YO0g=3!ZBa!thC}mtHPA*# zi(Yc}ks^)!S2T~f{lcyUH02Assd8V~RWSGh_g^XeM*g3w;SU8j + + + + + Welcome to Interface + + + + + +
+
+

Move around

+ Move around +

+ Move around with WASD & fly
+ up or down with E & C.
+ Cmnd/Ctrl+G will send you
+ home. Hitting Enter will let you
+ teleport to a user or location. +

+
+
+

Listen & talk

+ Talk +

+ Use your best headphones
+ and microphone for high
+ fidelity audio. +

+
+
+

Connect devices

+ Connect devices +

+ Have an Oculus Rift, a Razer
+ Hydra, or a PrimeSense 3D
+ camera? We support them all. +

+
+
+

Run a script

+ Run a script +

+ Cmnd/Cntrl+J will launch a
+ Running Scripts dialog to help
+ manage your scripts and search
+ for new ones to run. +

+
+
+

Script something

+ Write a script +

+ Write a script; we're always
+ adding new features.
+ Cmnd/Cntrl+J will launch a
+ Running Scripts dialog to help
+ manage your scripts. +

+
+
+

Import models

+ Import models +

+ Use the edit.js script to
+ add FBX models in-world. You
+ can use grids and fine tune
+ placement-related parameters
+ with ease. +

+
+
+
+

Read the docs

+

+ We are writing documentation on
+ just about everything. Please,
+ devour all we've written and make
+ suggestions where necessary.
+ Documentation is always at
+ docs.highfidelity.com +

+
+
+
+ + + + + diff --git a/interface/resources/icons/load-script.svg b/interface/resources/icons/load-script.svg new file mode 100644 index 0000000000..21be61c321 --- /dev/null +++ b/interface/resources/icons/load-script.svg @@ -0,0 +1,125 @@ + + + + + + + + + + image/svg+xml + + + + + T.Hofmeister + + + + + + + + + + + + + + + + + + + + + + diff --git a/interface/resources/icons/new-script.svg b/interface/resources/icons/new-script.svg new file mode 100644 index 0000000000..f68fcfa967 --- /dev/null +++ b/interface/resources/icons/new-script.svg @@ -0,0 +1,129 @@ + + + + + + + + + + image/svg+xml + + + + + T.Hofmeister + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/interface/resources/icons/save-script.svg b/interface/resources/icons/save-script.svg new file mode 100644 index 0000000000..04d41b8302 --- /dev/null +++ b/interface/resources/icons/save-script.svg @@ -0,0 +1,674 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + T.Hofmeister + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/interface/resources/icons/start-script.svg b/interface/resources/icons/start-script.svg new file mode 100644 index 0000000000..994eb61efe --- /dev/null +++ b/interface/resources/icons/start-script.svg @@ -0,0 +1,550 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + Maximillian Merlin + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/interface/resources/icons/stop-script.svg b/interface/resources/icons/stop-script.svg new file mode 100644 index 0000000000..31cdcee749 --- /dev/null +++ b/interface/resources/icons/stop-script.svg @@ -0,0 +1,163 @@ + + + + + + + + + + image/svg+xml + + + + + Maximillian Merlin + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/interface/resources/qml/AssetServer.qml b/interface/resources/qml/AssetServer.qml index 6f3076b408..cf61a2ae4a 100644 --- a/interface/resources/qml/AssetServer.qml +++ b/interface/resources/qml/AssetServer.qml @@ -206,7 +206,7 @@ ScrollingWindow { print("Error: model cannot be both static mesh and dynamic. This should never happen."); } else if (url) { var name = assetProxyModel.data(treeView.selection.currentIndex); - var addPosition = Vec3.sum(MyAvatar.position, Vec3.multiply(2, Quat.getForward(MyAvatar.orientation))); + var addPosition = Vec3.sum(MyAvatar.position, Vec3.multiply(2, Quat.getFront(MyAvatar.orientation))); var gravity; if (dynamic) { // Create a vector <0, -10, 0>. { x: 0, y: -10, z: 0 } won't work because Qt is dumb and this is a diff --git a/interface/resources/qml/AvatarInputs.qml b/interface/resources/qml/AvatarInputs.qml index 28f3c0c7b9..384504aaa0 100644 --- a/interface/resources/qml/AvatarInputs.qml +++ b/interface/resources/qml/AvatarInputs.qml @@ -15,11 +15,12 @@ import Qt.labs.settings 1.0 Hifi.AvatarInputs { id: root objectName: "AvatarInputs" - width: rootWidth - height: controls.height + width: mirrorWidth + height: controls.height + mirror.height x: 10; y: 5 - readonly property int rootWidth: 265 + readonly property int mirrorHeight: 215 + readonly property int mirrorWidth: 265 readonly property int iconSize: 24 readonly property int iconPadding: 5 @@ -38,15 +39,61 @@ Hifi.AvatarInputs { anchors.fill: parent } + Item { + id: mirror + width: root.mirrorWidth + height: root.mirrorVisible ? root.mirrorHeight : 0 + visible: root.mirrorVisible + anchors.left: parent.left + clip: true + + Image { + id: closeMirror + visible: hover.containsMouse + width: root.iconSize + height: root.iconSize + anchors.top: parent.top + anchors.topMargin: root.iconPadding + anchors.left: parent.left + anchors.leftMargin: root.iconPadding + source: "../images/close.svg" + MouseArea { + anchors.fill: parent + onClicked: { + root.closeMirror(); + } + } + } + + Image { + id: zoomIn + visible: hover.containsMouse + width: root.iconSize + height: root.iconSize + anchors.bottom: parent.bottom + anchors.bottomMargin: root.iconPadding + anchors.left: parent.left + anchors.leftMargin: root.iconPadding + source: root.mirrorZoomed ? "../images/minus.svg" : "../images/plus.svg" + MouseArea { + anchors.fill: parent + onClicked: { + root.toggleZoom(); + } + } + } + } + Item { id: controls - width: root.rootWidth + width: root.mirrorWidth height: 44 visible: root.showAudioTools + anchors.top: mirror.bottom Rectangle { anchors.fill: parent - color: "#00000000" + color: root.mirrorVisible ? (root.audioClipping ? "red" : "#696969") : "#00000000" Item { id: audioMeter diff --git a/interface/resources/qml/Stats.qml b/interface/resources/qml/Stats.qml index 17e6578e4d..564c74b526 100644 --- a/interface/resources/qml/Stats.qml +++ b/interface/resources/qml/Stats.qml @@ -198,7 +198,7 @@ Item { } StatText { visible: root.expanded; - text: "Audio Out Mic: " + root.audioOutboundPPS + " pps, " + + text: "Audio Out Mic: " + root.audioMicOutboundPPS + " pps, " + "Silent: " + root.audioSilentOutboundPPS + " pps"; } StatText { @@ -266,7 +266,7 @@ Item { text: "GPU Textures: "; } StatText { - text: " Pressure State: " + root.gpuTextureMemoryPressureState; + text: " Sparse Enabled: " + (0 == root.gpuSparseTextureEnabled ? "false" : "true"); } StatText { text: " Count: " + root.gpuTextures; @@ -278,10 +278,14 @@ Item { text: " Decimated: " + root.decimatedTextureCount; } StatText { - text: " Pending Transfer: " + root.texturePendingTransfers + " MB"; + text: " Sparse Count: " + root.gpuTexturesSparse; + visible: 0 != root.gpuSparseTextureEnabled; } StatText { - text: " Resource Memory: " + root.gpuTextureMemory + " MB"; + text: " Virtual Memory: " + root.gpuTextureVirtualMemory + " MB"; + } + StatText { + text: " Commited Memory: " + root.gpuTextureMemory + " MB"; } StatText { text: " Framebuffer Memory: " + root.gpuTextureFramebufferMemory + " MB"; diff --git a/interface/resources/styles/log_dialog.qss b/interface/resources/styles/log_dialog.qss index d3ae4e0a00..1fc4df0717 100644 --- a/interface/resources/styles/log_dialog.qss +++ b/interface/resources/styles/log_dialog.qss @@ -1,6 +1,6 @@ QPlainTextEdit { - font-family: Inconsolata, Consolas, Courier New, monospace; + font-family: Inconsolata, Lucida Console, Andale Mono, Monaco; font-size: 16px; padding-left: 28px; padding-top: 7px; @@ -11,7 +11,7 @@ QPlainTextEdit { } QLineEdit { - font-family: Inconsolata, Consolas, Courier New, monospace; + font-family: Inconsolata, Lucida Console, Andale Mono, Monaco; padding-left: 7px; background-color: #CCCCCC; border-width: 0; diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 78707ee635..1bb4c64884 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -177,8 +177,6 @@ #include "FrameTimingsScriptingInterface.h" #include #include -#include -#include // On Windows PC, NVidia Optimus laptop, we want to enable NVIDIA GPU // FIXME seems to be broken. @@ -215,10 +213,18 @@ static const QString FBX_EXTENSION = ".fbx"; static const QString OBJ_EXTENSION = ".obj"; static const QString AVA_JSON_EXTENSION = ".ava.json"; +static const int MIRROR_VIEW_TOP_PADDING = 5; +static const int MIRROR_VIEW_LEFT_PADDING = 10; +static const int MIRROR_VIEW_WIDTH = 265; +static const int MIRROR_VIEW_HEIGHT = 215; static const float MIRROR_FULLSCREEN_DISTANCE = 0.389f; +static const float MIRROR_REARVIEW_DISTANCE = 0.722f; +static const float MIRROR_REARVIEW_BODY_DISTANCE = 2.56f; +static const float MIRROR_FIELD_OF_VIEW = 30.0f; static const quint64 TOO_LONG_SINCE_LAST_SEND_DOWNSTREAM_AUDIO_STATS = 1 * USECS_PER_SECOND; +static const QString INFO_WELCOME_PATH = "html/interface-welcome.html"; static const QString INFO_EDIT_ENTITIES_PATH = "html/edit-commands.html"; static const QString INFO_HELP_PATH = "html/help.html"; @@ -417,7 +423,6 @@ static const QString STATE_CAMERA_THIRD_PERSON = "CameraThirdPerson"; static const QString STATE_CAMERA_ENTITY = "CameraEntity"; static const QString STATE_CAMERA_INDEPENDENT = "CameraIndependent"; static const QString STATE_SNAP_TURN = "SnapTurn"; -static const QString STATE_ADVANCED_MOVEMENT_CONTROLS = "AdvancedMovement"; static const QString STATE_GROUNDED = "Grounded"; static const QString STATE_NAV_FOCUSED = "NavigationFocused"; @@ -508,7 +513,7 @@ bool setupEssentials(int& argc, char** argv) { DependencyManager::set(); controller::StateController::setStateVariables({ { STATE_IN_HMD, STATE_CAMERA_FULL_SCREEN_MIRROR, STATE_CAMERA_FIRST_PERSON, STATE_CAMERA_THIRD_PERSON, STATE_CAMERA_ENTITY, STATE_CAMERA_INDEPENDENT, - STATE_SNAP_TURN, STATE_ADVANCED_MOVEMENT_CONTROLS, STATE_GROUNDED, STATE_NAV_FOCUSED } }); + STATE_SNAP_TURN, STATE_GROUNDED, STATE_NAV_FOCUSED } }); DependencyManager::set(); DependencyManager::set(); DependencyManager::set(); @@ -560,6 +565,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo _entityClipboardRenderer(false, this, this), _entityClipboard(new EntityTree()), _lastQueriedTime(usecTimestampNow()), + _mirrorViewRect(QRect(MIRROR_VIEW_LEFT_PADDING, MIRROR_VIEW_TOP_PADDING, MIRROR_VIEW_WIDTH, MIRROR_VIEW_HEIGHT)), _previousScriptLocation("LastScriptLocation", DESKTOP_LOCATION), _fieldOfView("fieldOfView", DEFAULT_FIELD_OF_VIEW_DEGREES), _hmdTabletScale("hmdTabletScale", DEFAULT_HMD_TABLET_SCALE_PERCENT), @@ -740,24 +746,23 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo } }); - auto audioScriptingInterface = DependencyManager::set(); + auto& audioScriptingInterface = AudioScriptingInterface::getInstance(); connect(audioThread, &QThread::started, audioIO.data(), &AudioClient::start); connect(audioIO.data(), &AudioClient::destroyed, audioThread, &QThread::quit); connect(audioThread, &QThread::finished, audioThread, &QThread::deleteLater); connect(audioIO.data(), &AudioClient::muteToggled, this, &Application::audioMuteToggled); - connect(audioIO.data(), &AudioClient::mutedByMixer, audioScriptingInterface.data(), &AudioScriptingInterface::mutedByMixer); - connect(audioIO.data(), &AudioClient::receivedFirstPacket, audioScriptingInterface.data(), &AudioScriptingInterface::receivedFirstPacket); - connect(audioIO.data(), &AudioClient::disconnected, audioScriptingInterface.data(), &AudioScriptingInterface::disconnected); + connect(audioIO.data(), &AudioClient::mutedByMixer, &audioScriptingInterface, &AudioScriptingInterface::mutedByMixer); + connect(audioIO.data(), &AudioClient::receivedFirstPacket, &audioScriptingInterface, &AudioScriptingInterface::receivedFirstPacket); + connect(audioIO.data(), &AudioClient::disconnected, &audioScriptingInterface, &AudioScriptingInterface::disconnected); connect(audioIO.data(), &AudioClient::muteEnvironmentRequested, [](glm::vec3 position, float radius) { auto audioClient = DependencyManager::get(); - auto audioScriptingInterface = DependencyManager::get(); auto myAvatarPosition = DependencyManager::get()->getMyAvatar()->getPosition(); float distance = glm::distance(myAvatarPosition, position); bool shouldMute = !audioClient->isMuted() && (distance < radius); if (shouldMute) { audioClient->toggleMute(); - audioScriptingInterface->environmentMuted(); + AudioScriptingInterface::getInstance().environmentMuted(); } }); @@ -1124,10 +1129,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo _applicationStateDevice->setInputVariant(STATE_SNAP_TURN, []() -> float { return qApp->getMyAvatar()->getSnapTurn() ? 1 : 0; }); - _applicationStateDevice->setInputVariant(STATE_ADVANCED_MOVEMENT_CONTROLS, []() -> float { - return qApp->getMyAvatar()->useAdvancedMovementControls() ? 1 : 0; - }); - _applicationStateDevice->setInputVariant(STATE_GROUNDED, []() -> float { return qApp->getMyAvatar()->getCharacterController()->onGround() ? 1 : 0; }); @@ -1182,10 +1183,10 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo // set the local loopback interface for local sounds AudioInjector::setLocalAudioInterface(audioIO.data()); - audioScriptingInterface->setLocalAudioInterface(audioIO.data()); - connect(audioIO.data(), &AudioClient::noiseGateOpened, audioScriptingInterface.data(), &AudioScriptingInterface::noiseGateOpened); - connect(audioIO.data(), &AudioClient::noiseGateClosed, audioScriptingInterface.data(), &AudioScriptingInterface::noiseGateClosed); - connect(audioIO.data(), &AudioClient::inputReceived, audioScriptingInterface.data(), &AudioScriptingInterface::inputReceived); + AudioScriptingInterface::getInstance().setLocalAudioInterface(audioIO.data()); + connect(audioIO.data(), &AudioClient::noiseGateOpened, &AudioScriptingInterface::getInstance(), &AudioScriptingInterface::noiseGateOpened); + connect(audioIO.data(), &AudioClient::noiseGateClosed, &AudioScriptingInterface::getInstance(), &AudioScriptingInterface::noiseGateClosed); + connect(audioIO.data(), &AudioClient::inputReceived, &AudioScriptingInterface::getInstance(), &AudioScriptingInterface::inputReceived); this->installEventFilter(this); @@ -1444,7 +1445,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo scriptEngines->loadScript(testScript, false); } else { // Get sandbox content set version, if available - auto acDirPath = PathUtils::getAppDataPath() + "../../" + BuildInfo::MODIFIED_ORGANIZATION + "/assignment-client/"; + auto acDirPath = PathUtils::getRootDataDirectory() + BuildInfo::MODIFIED_ORGANIZATION + "/assignment-client/"; auto contentVersionPath = acDirPath + "content-version.txt"; qCDebug(interfaceapp) << "Checking " << contentVersionPath << " for content version"; auto contentVersion = 0; @@ -1950,7 +1951,7 @@ void Application::initializeUi() { // For some reason there is already an "Application" object in the QML context, // though I can't find it. Hence, "ApplicationInterface" rootContext->setContextProperty("ApplicationInterface", this); - rootContext->setContextProperty("Audio", DependencyManager::get().data()); + rootContext->setContextProperty("Audio", &AudioScriptingInterface::getInstance()); rootContext->setContextProperty("AudioStats", DependencyManager::get()->getStats().data()); rootContext->setContextProperty("AudioScope", DependencyManager::get().data()); @@ -2118,6 +2119,21 @@ void Application::paintGL() { batch.resetStages(); }); + auto inputs = AvatarInputs::getInstance(); + if (inputs->mirrorVisible()) { + PerformanceTimer perfTimer("Mirror"); + + renderArgs._renderMode = RenderArgs::MIRROR_RENDER_MODE; + renderArgs._blitFramebuffer = DependencyManager::get()->getSelfieFramebuffer(); + + _mirrorViewRect.moveTo(inputs->x(), inputs->y()); + + renderRearViewMirror(&renderArgs, _mirrorViewRect, inputs->mirrorZoomed()); + + renderArgs._blitFramebuffer.reset(); + renderArgs._renderMode = RenderArgs::DEFAULT_RENDER_MODE; + } + { PerformanceTimer perfTimer("renderOverlay"); // NOTE: There is no batch associated with this renderArgs @@ -2132,7 +2148,7 @@ void Application::paintGL() { PerformanceTimer perfTimer("CameraUpdates"); auto myAvatar = getMyAvatar(); - boomOffset = myAvatar->getScale() * myAvatar->getBoomLength() * -IDENTITY_FORWARD; + boomOffset = myAvatar->getScale() * myAvatar->getBoomLength() * -IDENTITY_FRONT; if (_myCamera.getMode() == CAMERA_MODE_FIRST_PERSON || _myCamera.getMode() == CAMERA_MODE_THIRD_PERSON) { Menu::getInstance()->setIsOptionChecked(MenuOption::FirstPerson, myAvatar->getBoomLength() <= MyAvatar::ZOOM_MIN); @@ -2365,6 +2381,10 @@ void Application::setSettingConstrainToolbarPosition(bool setting) { DependencyManager::get()->setConstrainToolbarToCenterX(setting); } +void Application::aboutApp() { + InfoView::show(INFO_WELCOME_PATH); +} + void Application::showHelp() { static const QString HAND_CONTROLLER_NAME_VIVE = "vive"; static const QString HAND_CONTROLLER_NAME_OCULUS_TOUCH = "oculus"; @@ -2746,6 +2766,8 @@ void Application::keyPressEvent(QKeyEvent* event) { case Qt::Key_S: if (isShifted && isMeta && !isOption) { Menu::getInstance()->triggerOption(MenuOption::SuppressShortTimings); + } else if (isOption && !isShifted && !isMeta) { + Menu::getInstance()->triggerOption(MenuOption::ScriptEditor); } else if (!isOption && !isShifted && isMeta) { takeSnapshot(true); } @@ -2864,49 +2886,51 @@ void Application::keyPressEvent(QKeyEvent* event) { break; #endif - case Qt::Key_H: { - // whenever switching to/from full screen mirror from the keyboard, remember - // the state you were in before full screen mirror, and return to that. - auto previousMode = _myCamera.getMode(); - if (previousMode != CAMERA_MODE_MIRROR) { - switch (previousMode) { - case CAMERA_MODE_FIRST_PERSON: - _returnFromFullScreenMirrorTo = MenuOption::FirstPerson; - break; - case CAMERA_MODE_THIRD_PERSON: - _returnFromFullScreenMirrorTo = MenuOption::ThirdPerson; - break; + case Qt::Key_H: + if (isShifted) { + Menu::getInstance()->triggerOption(MenuOption::MiniMirror); + } else { + // whenever switching to/from full screen mirror from the keyboard, remember + // the state you were in before full screen mirror, and return to that. + auto previousMode = _myCamera.getMode(); + if (previousMode != CAMERA_MODE_MIRROR) { + switch (previousMode) { + case CAMERA_MODE_FIRST_PERSON: + _returnFromFullScreenMirrorTo = MenuOption::FirstPerson; + break; + case CAMERA_MODE_THIRD_PERSON: + _returnFromFullScreenMirrorTo = MenuOption::ThirdPerson; + break; - // FIXME - it's not clear that these modes make sense to return to... - case CAMERA_MODE_INDEPENDENT: - _returnFromFullScreenMirrorTo = MenuOption::IndependentMode; - break; - case CAMERA_MODE_ENTITY: - _returnFromFullScreenMirrorTo = MenuOption::CameraEntityMode; - break; + // FIXME - it's not clear that these modes make sense to return to... + case CAMERA_MODE_INDEPENDENT: + _returnFromFullScreenMirrorTo = MenuOption::IndependentMode; + break; + case CAMERA_MODE_ENTITY: + _returnFromFullScreenMirrorTo = MenuOption::CameraEntityMode; + break; - default: - _returnFromFullScreenMirrorTo = MenuOption::ThirdPerson; - break; + default: + _returnFromFullScreenMirrorTo = MenuOption::ThirdPerson; + break; + } } - } - bool isMirrorChecked = Menu::getInstance()->isOptionChecked(MenuOption::FullscreenMirror); - Menu::getInstance()->setIsOptionChecked(MenuOption::FullscreenMirror, !isMirrorChecked); - if (isMirrorChecked) { + bool isMirrorChecked = Menu::getInstance()->isOptionChecked(MenuOption::FullscreenMirror); + Menu::getInstance()->setIsOptionChecked(MenuOption::FullscreenMirror, !isMirrorChecked); + if (isMirrorChecked) { - // if we got here without coming in from a non-Full Screen mirror case, then our - // _returnFromFullScreenMirrorTo is unknown. In that case we'll go to the old - // behavior of returning to ThirdPerson - if (_returnFromFullScreenMirrorTo.isEmpty()) { - _returnFromFullScreenMirrorTo = MenuOption::ThirdPerson; + // if we got here without coming in from a non-Full Screen mirror case, then our + // _returnFromFullScreenMirrorTo is unknown. In that case we'll go to the old + // behavior of returning to ThirdPerson + if (_returnFromFullScreenMirrorTo.isEmpty()) { + _returnFromFullScreenMirrorTo = MenuOption::ThirdPerson; + } + Menu::getInstance()->setIsOptionChecked(_returnFromFullScreenMirrorTo, true); } - Menu::getInstance()->setIsOptionChecked(_returnFromFullScreenMirrorTo, true); + cameraMenuChanged(); } - cameraMenuChanged(); break; - } - case Qt::Key_P: { if (!(isShifted || isMeta || isOption)) { bool isFirstPersonChecked = Menu::getInstance()->isOptionChecked(MenuOption::FirstPerson); @@ -3821,6 +3845,8 @@ void Application::init() { DependencyManager::get()->init(); _myCamera.setMode(CAMERA_MODE_FIRST_PERSON); + _mirrorCamera.setMode(CAMERA_MODE_MIRROR); + _timerStart.start(); _lastTimeUpdated.start(); @@ -3955,7 +3981,7 @@ void Application::updateMyAvatarLookAtPosition() { auto lookingAtHead = static_pointer_cast(lookingAt)->getHead(); const float MAXIMUM_FACE_ANGLE = 65.0f * RADIANS_PER_DEGREE; - glm::vec3 lookingAtFaceOrientation = lookingAtHead->getFinalOrientationInWorldFrame() * IDENTITY_FORWARD; + glm::vec3 lookingAtFaceOrientation = lookingAtHead->getFinalOrientationInWorldFrame() * IDENTITY_FRONT; glm::vec3 fromLookingAtToMe = glm::normalize(myAvatar->getHead()->getEyePosition() - lookingAtHead->getEyePosition()); float faceAngle = glm::angle(lookingAtFaceOrientation, fromLookingAtToMe); @@ -4357,16 +4383,16 @@ void Application::update(float deltaTime) { myAvatar->clearDriveKeys(); if (_myCamera.getMode() != CAMERA_MODE_INDEPENDENT) { if (!_controllerScriptingInterface->areActionsCaptured()) { - myAvatar->setDriveKey(MyAvatar::TRANSLATE_Z, -1.0f * userInputMapper->getActionState(controller::Action::TRANSLATE_Z)); - myAvatar->setDriveKey(MyAvatar::TRANSLATE_Y, userInputMapper->getActionState(controller::Action::TRANSLATE_Y)); - myAvatar->setDriveKey(MyAvatar::TRANSLATE_X, userInputMapper->getActionState(controller::Action::TRANSLATE_X)); + myAvatar->setDriveKeys(TRANSLATE_Z, -1.0f * userInputMapper->getActionState(controller::Action::TRANSLATE_Z)); + myAvatar->setDriveKeys(TRANSLATE_Y, userInputMapper->getActionState(controller::Action::TRANSLATE_Y)); + myAvatar->setDriveKeys(TRANSLATE_X, userInputMapper->getActionState(controller::Action::TRANSLATE_X)); if (deltaTime > FLT_EPSILON) { - myAvatar->setDriveKey(MyAvatar::PITCH, -1.0f * userInputMapper->getActionState(controller::Action::PITCH)); - myAvatar->setDriveKey(MyAvatar::YAW, -1.0f * userInputMapper->getActionState(controller::Action::YAW)); - myAvatar->setDriveKey(MyAvatar::STEP_YAW, -1.0f * userInputMapper->getActionState(controller::Action::STEP_YAW)); + myAvatar->setDriveKeys(PITCH, -1.0f * userInputMapper->getActionState(controller::Action::PITCH)); + myAvatar->setDriveKeys(YAW, -1.0f * userInputMapper->getActionState(controller::Action::YAW)); + myAvatar->setDriveKeys(STEP_YAW, -1.0f * userInputMapper->getActionState(controller::Action::STEP_YAW)); } } - myAvatar->setDriveKey(MyAvatar::ZOOM, userInputMapper->getActionState(controller::Action::TRANSLATE_CAMERA_Z)); + myAvatar->setDriveKeys(ZOOM, userInputMapper->getActionState(controller::Action::TRANSLATE_CAMERA_Z)); } controller::Pose leftHandPose = userInputMapper->getPoseState(controller::Action::LEFT_HAND); @@ -4437,12 +4463,9 @@ void Application::update(float deltaTime) { getEntities()->getTree()->withWriteLock([&] { PerformanceTimer perfTimer("handleOutgoingChanges"); - const VectorOfMotionStates& deactivations = _physicsEngine->getDeactivatedMotionStates(); - _entitySimulation->handleDeactivatedMotionStates(deactivations); - - const VectorOfMotionStates& outgoingChanges = _physicsEngine->getChangedMotionStates(); - _entitySimulation->handleChangedMotionStates(outgoingChanges); - avatarManager->handleChangedMotionStates(outgoingChanges); + const VectorOfMotionStates& outgoingChanges = _physicsEngine->getOutgoingChanges(); + _entitySimulation->handleOutgoingChanges(outgoingChanges); + avatarManager->handleOutgoingChanges(outgoingChanges); }); if (!_aboutToQuit) { @@ -5099,6 +5122,58 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se activeRenderingThread = nullptr; } +void Application::renderRearViewMirror(RenderArgs* renderArgs, const QRect& region, bool isZoomed) { + auto originalViewport = renderArgs->_viewport; + // Grab current viewport to reset it at the end + + float aspect = (float)region.width() / region.height(); + float fov = MIRROR_FIELD_OF_VIEW; + + auto myAvatar = getMyAvatar(); + + // bool eyeRelativeCamera = false; + if (!isZoomed) { + _mirrorCamera.setPosition(myAvatar->getChestPosition() + + myAvatar->getOrientation() * glm::vec3(0.0f, 0.0f, -1.0f) * MIRROR_REARVIEW_BODY_DISTANCE * myAvatar->getScale()); + + } else { // HEAD zoom level + // FIXME note that the positioning of the camera relative to the avatar can suffer limited + // precision as the user's position moves further away from the origin. Thus at + // /1e7,1e7,1e7 (well outside the buildable volume) the mirror camera veers and sways + // wildly as you rotate your avatar because the floating point values are becoming + // larger, squeezing out the available digits of precision you have available at the + // human scale for camera positioning. + + // Previously there was a hack to correct this using the mechanism of repositioning + // the avatar at the origin of the world for the purposes of rendering the mirror, + // but it resulted in failing to render the avatar's head model in the mirror view + // when in first person mode. Presumably this was because of some missed culling logic + // that was not accounted for in the hack. + + // This was removed in commit 71e59cfa88c6563749594e25494102fe01db38e9 but could be further + // investigated in order to adapt the technique while fixing the head rendering issue, + // but the complexity of the hack suggests that a better approach + _mirrorCamera.setPosition(myAvatar->getDefaultEyePosition() + + myAvatar->getOrientation() * glm::vec3(0.0f, 0.0f, -1.0f) * MIRROR_REARVIEW_DISTANCE * myAvatar->getScale()); + } + _mirrorCamera.setProjection(glm::perspective(glm::radians(fov), aspect, DEFAULT_NEAR_CLIP, DEFAULT_FAR_CLIP)); + _mirrorCamera.setOrientation(myAvatar->getWorldAlignedOrientation() * glm::quat(glm::vec3(0.0f, PI, 0.0f))); + + + // set the bounds of rear mirror view + // the region is in device independent coordinates; must convert to device + float ratio = (float)QApplication::desktop()->windowHandle()->devicePixelRatio() * getRenderResolutionScale(); + int width = region.width() * ratio; + int height = region.height() * ratio; + gpu::Vec4i viewport = gpu::Vec4i(0, 0, width, height); + renderArgs->_viewport = viewport; + + // render rear mirror view + displaySide(renderArgs, _mirrorCamera, true); + + renderArgs->_viewport = originalViewport; +} + void Application::resetSensors(bool andReload) { DependencyManager::get()->reset(); DependencyManager::get()->reset(); @@ -5428,7 +5503,8 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri scriptEngine->registerGlobalObject("Rates", new RatesScriptingInterface(this)); // hook our avatar and avatar hash map object into this script engine - getMyAvatar()->registerMetaTypes(scriptEngine); + scriptEngine->registerGlobalObject("MyAvatar", getMyAvatar().get()); + qScriptRegisterMetaType(scriptEngine, audioListenModeToScriptValue, audioListenModeFromScriptValue); scriptEngine->registerGlobalObject("AvatarList", DependencyManager::get().data()); diff --git a/interface/src/Application.h b/interface/src/Application.h index 7ae4160f8b..98080783a6 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -72,8 +72,6 @@ #include #include -#include - class OffscreenGLCanvas; class GLCanvas; @@ -278,6 +276,8 @@ public: virtual void pushPostUpdateLambda(void* key, std::function func) override; + const QRect& getMirrorViewRect() const { return _mirrorViewRect; } + void updateMyAvatarLookAtPosition(); float getAvatarSimrate() const { return _avatarSimCounter.rate(); } @@ -368,6 +368,7 @@ public slots: void calibrateEyeTracker5Points(); #endif + void aboutApp(); static void showHelp(); void cycleCamera(); @@ -556,6 +557,8 @@ private: int _avatarSimsPerSecondReport {0}; quint64 _lastAvatarSimsPerSecondUpdate {0}; Camera _myCamera; // My view onto the world + Camera _mirrorCamera; // Camera for mirror view + QRect _mirrorViewRect; Setting::Handle _previousScriptLocation; Setting::Handle _fieldOfView; diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index a48ee4e7db..beacbaccab 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -74,6 +74,9 @@ Menu::Menu() { // File > Help addActionToQMenuAndActionHash(fileMenu, MenuOption::Help, 0, qApp, SLOT(showHelp())); + // File > About + addActionToQMenuAndActionHash(fileMenu, MenuOption::AboutApp, 0, qApp, SLOT(aboutApp()), QAction::AboutRole); + // File > Quit addActionToQMenuAndActionHash(fileMenu, MenuOption::Quit, Qt::CTRL | Qt::Key_Q, qApp, SLOT(quit()), QAction::QuitRole); @@ -117,6 +120,11 @@ Menu::Menu() { scriptEngines.data(), SLOT(reloadAllScripts()), QAction::NoRole, UNSPECIFIED_POSITION, "Advanced"); + // Edit > Scripts Editor... [advanced] + addActionToQMenuAndActionHash(editMenu, MenuOption::ScriptEditor, Qt::ALT | Qt::Key_S, + dialogsManager.data(), SLOT(showScriptEditor()), + QAction::NoRole, UNSPECIFIED_POSITION, "Advanced"); + // Edit > Console... [advanced] addActionToQMenuAndActionHash(editMenu, MenuOption::Console, Qt::CTRL | Qt::ALT | Qt::Key_J, DependencyManager::get().data(), @@ -241,6 +249,9 @@ Menu::Menu() { viewMenu->addSeparator(); + // View > Mini Mirror + addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::MiniMirror, 0, false); + // View > Center Player In View addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::CenterPlayerInView, 0, true, qApp, SLOT(rotationModeChanged()), @@ -406,9 +417,6 @@ Menu::Menu() { } // Developer > Assets >>> - // Menu item is not currently needed but code should be kept in case it proves useful again at some stage. -//#define WANT_ASSET_MIGRATION -#ifdef WANT_ASSET_MIGRATION MenuWrapper* assetDeveloperMenu = developerMenu->addMenu("Assets"); auto& atpMigrator = ATPAssetMigrator::getInstance(); atpMigrator.setDialogParent(this); @@ -416,7 +424,6 @@ Menu::Menu() { addActionToQMenuAndActionHash(assetDeveloperMenu, MenuOption::AssetMigration, 0, &atpMigrator, SLOT(loadEntityServerFile())); -#endif // Developer > Avatar >>> MenuWrapper* avatarDebugMenu = developerMenu->addMenu("Avatar"); @@ -547,14 +554,16 @@ Menu::Menu() { "NetworkingPreferencesDialog"); }); addActionToQMenuAndActionHash(networkMenu, MenuOption::ReloadContent, 0, qApp, SLOT(reloadResourceCaches())); - addActionToQMenuAndActionHash(networkMenu, MenuOption::ClearDiskCache, 0, - DependencyManager::get().data(), SLOT(clearCache())); addCheckableActionToQMenuAndActionHash(networkMenu, MenuOption::DisableActivityLogger, 0, false, &UserActivityLogger::getInstance(), SLOT(disable(bool))); + addActionToQMenuAndActionHash(networkMenu, MenuOption::CachesSize, 0, + dialogsManager.data(), SLOT(cachesSizeDialog())); + addActionToQMenuAndActionHash(networkMenu, MenuOption::DiskCacheEditor, 0, + dialogsManager.data(), SLOT(toggleDiskCacheEditor())); addActionToQMenuAndActionHash(networkMenu, MenuOption::ShowDSConnectTable, 0, dialogsManager.data(), SLOT(showDomainConnectionDialog())); diff --git a/interface/src/Menu.h b/interface/src/Menu.h index b4eaf56758..c806ffa9ee 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -26,6 +26,7 @@ public: }; namespace MenuOption { + const QString AboutApp = "About Interface"; const QString AddRemoveFriends = "Add/Remove Friends..."; const QString AddressBar = "Show Address Bar"; const QString Animations = "Animations..."; @@ -51,11 +52,11 @@ namespace MenuOption { const QString BinaryEyelidControl = "Binary Eyelid Control"; const QString BookmarkLocation = "Bookmark Location"; const QString Bookmarks = "Bookmarks"; + const QString CachesSize = "RAM Caches Size"; const QString CalibrateCamera = "Calibrate Camera"; const QString CameraEntityMode = "Entity Mode"; const QString CenterPlayerInView = "Center Player In View"; const QString Chat = "Chat..."; - const QString ClearDiskCache = "Clear Disk Cache"; const QString Collisions = "Collisions"; const QString Connexion = "Activate 3D Connexion Devices"; const QString Console = "Console..."; @@ -82,6 +83,7 @@ namespace MenuOption { const QString DisableActivityLogger = "Disable Activity Logger"; const QString DisableEyelidAdjustment = "Disable Eyelid Adjustment"; const QString DisableLightEntities = "Disable Light Entities"; + const QString DiskCacheEditor = "Disk Cache Editor"; const QString DisplayCrashOptions = "Display Crash Options"; const QString DisplayHandTargets = "Show Hand Targets"; const QString DisplayModelBounds = "Display Model Bounds"; @@ -122,6 +124,7 @@ namespace MenuOption { const QString LogExtraTimings = "Log Extra Timing Details"; const QString LowVelocityFilter = "Low Velocity Filter"; const QString MeshVisible = "Draw Mesh"; + const QString MiniMirror = "Mini Mirror"; const QString MuteAudio = "Mute Microphone"; const QString MuteEnvironment = "Mute Environment"; const QString MuteFaceTracking = "Mute Face Tracking"; @@ -166,6 +169,7 @@ namespace MenuOption { const QString RunningScripts = "Running Scripts..."; const QString RunClientScriptTests = "Run Client Script Tests"; const QString RunTimingTests = "Run Timing Tests"; + const QString ScriptEditor = "Script Editor..."; const QString ScriptedMotorControl = "Enable Scripted Motor Control"; const QString SendWrongDSConnectVersion = "Send wrong DS connect version"; const QString SendWrongProtocolVersion = "Send wrong protocol version"; diff --git a/interface/src/avatar/Avatar.h b/interface/src/avatar/Avatar.h index d4bd03367e..ca4dbd2af8 100644 --- a/interface/src/avatar/Avatar.h +++ b/interface/src/avatar/Avatar.h @@ -236,6 +236,7 @@ protected: glm::vec3 getBodyRightDirection() const { return getOrientation() * IDENTITY_RIGHT; } glm::vec3 getBodyUpDirection() const { return getOrientation() * IDENTITY_UP; } + glm::vec3 getBodyFrontDirection() const { return getOrientation() * IDENTITY_FRONT; } glm::quat computeRotationFromBodyToWorldUp(float proportion = 1.0f) const; void measureMotionDerivatives(float deltaTime); diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index 6152148887..94ce444416 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -424,7 +424,7 @@ void AvatarManager::getObjectsToChange(VectorOfMotionStates& result) { } } -void AvatarManager::handleChangedMotionStates(const VectorOfMotionStates& motionStates) { +void AvatarManager::handleOutgoingChanges(const VectorOfMotionStates& motionStates) { // TODO: extract the MyAvatar results once we use a MotionState for it. } diff --git a/interface/src/avatar/AvatarManager.h b/interface/src/avatar/AvatarManager.h index b94f9e6a96..e1f5a3b411 100644 --- a/interface/src/avatar/AvatarManager.h +++ b/interface/src/avatar/AvatarManager.h @@ -70,7 +70,7 @@ public: void getObjectsToRemoveFromPhysics(VectorOfMotionStates& motionStates); void getObjectsToAddToPhysics(VectorOfMotionStates& motionStates); void getObjectsToChange(VectorOfMotionStates& motionStates); - void handleChangedMotionStates(const VectorOfMotionStates& motionStates); + void handleOutgoingChanges(const VectorOfMotionStates& motionStates); void handleCollisionEvents(const CollisionEvents& collisionEvents); Q_INVOKABLE float getAvatarDataRate(const QUuid& sessionID, const QString& rateName = QString("")) const; diff --git a/interface/src/avatar/CauterizedMeshPartPayload.cpp b/interface/src/avatar/CauterizedMeshPartPayload.cpp index c11f92083b..c8ec90dcee 100644 --- a/interface/src/avatar/CauterizedMeshPartPayload.cpp +++ b/interface/src/avatar/CauterizedMeshPartPayload.cpp @@ -20,28 +20,55 @@ using namespace render; CauterizedMeshPartPayload::CauterizedMeshPartPayload(Model* model, int meshIndex, int partIndex, int shapeIndex, const Transform& transform, const Transform& offsetTransform) : ModelMeshPartPayload(model, meshIndex, partIndex, shapeIndex, transform, offsetTransform) {} -void CauterizedMeshPartPayload::updateTransformForCauterizedMesh( - const Transform& renderTransform, - const gpu::BufferPointer& buffer) { - _cauterizedTransform = renderTransform; - _cauterizedClusterBuffer = buffer; +void CauterizedMeshPartPayload::updateTransformForSkinnedCauterizedMesh(const Transform& transform, + const QVector& clusterMatrices, + const QVector& cauterizedClusterMatrices) { + _transform = transform; + _cauterizedTransform = transform; + + if (clusterMatrices.size() > 0) { + _worldBound = AABox(); + for (auto& clusterMatrix : clusterMatrices) { + AABox clusterBound = _localBound; + clusterBound.transform(clusterMatrix); + _worldBound += clusterBound; + } + + _worldBound.transform(transform); + if (clusterMatrices.size() == 1) { + _transform = _transform.worldTransform(Transform(clusterMatrices[0])); + if (cauterizedClusterMatrices.size() != 0) { + _cauterizedTransform = _cauterizedTransform.worldTransform(Transform(cauterizedClusterMatrices[0])); + } else { + _cauterizedTransform = _transform; + } + } + } else { + _worldBound = _localBound; + _worldBound.transform(_drawTransform); + } } void CauterizedMeshPartPayload::bindTransform(gpu::Batch& batch, const render::ShapePipeline::LocationsPointer locations, RenderArgs::RenderMode renderMode) const { // Still relying on the raw data from the model + const Model::MeshState& state = _model->getMeshState(_meshIndex); SkeletonModel* skeleton = static_cast(_model); bool useCauterizedMesh = (renderMode != RenderArgs::RenderMode::SHADOW_RENDER_MODE) && skeleton->getEnableCauterization(); - if (useCauterizedMesh) { - if (_cauterizedClusterBuffer) { - batch.setUniformBuffer(ShapePipeline::Slot::BUFFER::SKINNING, _cauterizedClusterBuffer); - } - batch.setModelTransform(_cauterizedTransform); - } else { - if (_clusterBuffer) { - batch.setUniformBuffer(ShapePipeline::Slot::BUFFER::SKINNING, _clusterBuffer); + if (state.clusterBuffer) { + if (useCauterizedMesh) { + const Model::MeshState& cState = skeleton->getCauterizeMeshState(_meshIndex); + batch.setUniformBuffer(ShapePipeline::Slot::BUFFER::SKINNING, cState.clusterBuffer); + } else { + batch.setUniformBuffer(ShapePipeline::Slot::BUFFER::SKINNING, state.clusterBuffer); } batch.setModelTransform(_transform); + } else { + if (useCauterizedMesh) { + batch.setModelTransform(_cauterizedTransform); + } else { + batch.setModelTransform(_transform); + } } } diff --git a/interface/src/avatar/CauterizedMeshPartPayload.h b/interface/src/avatar/CauterizedMeshPartPayload.h index dc88e950c1..f4319ead6f 100644 --- a/interface/src/avatar/CauterizedMeshPartPayload.h +++ b/interface/src/avatar/CauterizedMeshPartPayload.h @@ -17,13 +17,12 @@ class CauterizedMeshPartPayload : public ModelMeshPartPayload { public: CauterizedMeshPartPayload(Model* model, int meshIndex, int partIndex, int shapeIndex, const Transform& transform, const Transform& offsetTransform); - - void updateTransformForCauterizedMesh(const Transform& renderTransform, const gpu::BufferPointer& buffer); + void updateTransformForSkinnedCauterizedMesh(const Transform& transform, + const QVector& clusterMatrices, + const QVector& cauterizedClusterMatrices); void bindTransform(gpu::Batch& batch, const render::ShapePipeline::LocationsPointer locations, RenderArgs::RenderMode renderMode) const override; - private: - gpu::BufferPointer _cauterizedClusterBuffer; Transform _cauterizedTransform; }; diff --git a/interface/src/avatar/CauterizedModel.cpp b/interface/src/avatar/CauterizedModel.cpp index f479ed9a35..1ca87a498a 100644 --- a/interface/src/avatar/CauterizedModel.cpp +++ b/interface/src/avatar/CauterizedModel.cpp @@ -26,8 +26,8 @@ CauterizedModel::~CauterizedModel() { } void CauterizedModel::deleteGeometry() { - Model::deleteGeometry(); - _cauterizeMeshStates.clear(); + Model::deleteGeometry(); + _cauterizeMeshStates.clear(); } bool CauterizedModel::updateGeometry() { @@ -41,7 +41,7 @@ bool CauterizedModel::updateGeometry() { _cauterizeMeshStates.append(state); } } - return needsFullUpdate; + return needsFullUpdate; } void CauterizedModel::createVisibleRenderItemSet() { @@ -56,9 +56,9 @@ void CauterizedModel::createVisibleRenderItemSet() { } // We should not have any existing renderItems if we enter this section of code - Q_ASSERT(_modelMeshRenderItems.isEmpty()); + Q_ASSERT(_modelMeshRenderItemsSet.isEmpty()); - _modelMeshRenderItems.clear(); + _modelMeshRenderItemsSet.clear(); Transform transform; transform.setTranslation(_translation); @@ -81,18 +81,18 @@ void CauterizedModel::createVisibleRenderItemSet() { int numParts = (int)mesh->getNumParts(); for (int partIndex = 0; partIndex < numParts; partIndex++) { auto ptr = std::make_shared(this, i, partIndex, shapeID, transform, offset); - _modelMeshRenderItems << std::static_pointer_cast(ptr); + _modelMeshRenderItemsSet << std::static_pointer_cast(ptr); shapeID++; } } } else { - Model::createVisibleRenderItemSet(); + Model::createVisibleRenderItemSet(); } } void CauterizedModel::createCollisionRenderItemSet() { // Temporary HACK: use base class method for now - Model::createCollisionRenderItemSet(); + Model::createCollisionRenderItemSet(); } void CauterizedModel::updateClusterMatrices() { @@ -122,8 +122,8 @@ void CauterizedModel::updateClusterMatrices() { state.clusterBuffer->setSubData(0, state.clusterMatrices.size() * sizeof(glm::mat4), (const gpu::Byte*) state.clusterMatrices.constData()); } - } - } + } + } // as an optimization, don't build cautrizedClusterMatrices if the boneSet is empty. if (!_cauterizeBoneSet.empty()) { @@ -191,9 +191,6 @@ void CauterizedModel::updateRenderItems() { return; } - // lazy update of cluster matrices used for rendering. We need to update them here, so we can correctly update the bounding box. - self->updateClusterMatrices(); - render::ScenePointer scene = AbstractViewStateInterface::instance()->getMain3DScene(); Transform modelTransform; @@ -212,22 +209,15 @@ void CauterizedModel::updateRenderItems() { if (data._model && data._model->isLoaded()) { // Ensure the model geometry was not reset between frames if (deleteGeometryCounter == data._model->getGeometryCounter()) { - // this stuff identical to what happens in regular Model - const Model::MeshState& state = data._model->getMeshState(data._meshIndex); - Transform renderTransform = modelTransform; - if (state.clusterMatrices.size() == 1) { - renderTransform = modelTransform.worldTransform(Transform(state.clusterMatrices[0])); - } - data.updateTransformForSkinnedMesh(renderTransform, modelTransform, state.clusterBuffer); + // lazy update of cluster matrices used for rendering. We need to update them here, so we can correctly update the bounding box. + data._model->updateClusterMatrices(); - // this stuff for cauterized mesh + // update the model transform and bounding box for this render item. + const Model::MeshState& state = data._model->getMeshState(data._meshIndex); CauterizedModel* cModel = static_cast(data._model); - const Model::MeshState& cState = cModel->getCauterizeMeshState(data._meshIndex); - renderTransform = modelTransform; - if (cState.clusterMatrices.size() == 1) { - renderTransform = modelTransform.worldTransform(Transform(cState.clusterMatrices[0])); - } - data.updateTransformForCauterizedMesh(renderTransform, cState.clusterBuffer); + assert(data._meshIndex < cModel->_cauterizeMeshStates.size()); + const Model::MeshState& cState = cModel->_cauterizeMeshStates.at(data._meshIndex); + data.updateTransformForSkinnedCauterizedMesh(modelTransform, state.clusterMatrices, cState.clusterMatrices); } } }); diff --git a/interface/src/avatar/Head.cpp b/interface/src/avatar/Head.cpp index f4fb844d9b..d7bf2b79bf 100644 --- a/interface/src/avatar/Head.cpp +++ b/interface/src/avatar/Head.cpp @@ -268,7 +268,7 @@ void Head::applyEyelidOffset(glm::quat headOrientation) { return; } - glm::quat eyeRotation = rotationBetween(headOrientation * IDENTITY_FORWARD, getLookAtPosition() - _eyePosition); + glm::quat eyeRotation = rotationBetween(headOrientation * IDENTITY_FRONT, getLookAtPosition() - _eyePosition); eyeRotation = eyeRotation * glm::angleAxis(safeEulerAngles(headOrientation).y, IDENTITY_UP); // Rotation w.r.t. head float eyePitch = safeEulerAngles(eyeRotation).x; @@ -375,7 +375,7 @@ glm::quat Head::getCameraOrientation() const { glm::quat Head::getEyeRotation(const glm::vec3& eyePosition) const { glm::quat orientation = getOrientation(); glm::vec3 lookAtDelta = _lookAtPosition - eyePosition; - return rotationBetween(orientation * IDENTITY_FORWARD, lookAtDelta + glm::length(lookAtDelta) * _saccade) * orientation; + return rotationBetween(orientation * IDENTITY_FRONT, lookAtDelta + glm::length(lookAtDelta) * _saccade) * orientation; } void Head::setFinalPitch(float finalPitch) { diff --git a/interface/src/avatar/Head.h b/interface/src/avatar/Head.h index aa801e5eb5..3d25c79087 100644 --- a/interface/src/avatar/Head.h +++ b/interface/src/avatar/Head.h @@ -58,14 +58,14 @@ public: const glm::vec3& getSaccade() const { return _saccade; } glm::vec3 getRightDirection() const { return getOrientation() * IDENTITY_RIGHT; } glm::vec3 getUpDirection() const { return getOrientation() * IDENTITY_UP; } - glm::vec3 getForwardDirection() const { return getOrientation() * IDENTITY_FORWARD; } + glm::vec3 getFrontDirection() const { return getOrientation() * IDENTITY_FRONT; } glm::quat getEyeRotation(const glm::vec3& eyePosition) const; const glm::vec3& getRightEyePosition() const { return _rightEyePosition; } const glm::vec3& getLeftEyePosition() const { return _leftEyePosition; } - glm::vec3 getRightEarPosition() const { return _rightEyePosition + (getRightDirection() * EYE_EAR_GAP) + (getForwardDirection() * -EYE_EAR_GAP); } - glm::vec3 getLeftEarPosition() const { return _leftEyePosition + (getRightDirection() * -EYE_EAR_GAP) + (getForwardDirection() * -EYE_EAR_GAP); } + glm::vec3 getRightEarPosition() const { return _rightEyePosition + (getRightDirection() * EYE_EAR_GAP) + (getFrontDirection() * -EYE_EAR_GAP); } + glm::vec3 getLeftEarPosition() const { return _leftEyePosition + (getRightDirection() * -EYE_EAR_GAP) + (getFrontDirection() * -EYE_EAR_GAP); } glm::vec3 getMouthPosition() const { return _eyePosition - getUpDirection() * glm::length(_rightEyePosition - _leftEyePosition); } bool getReturnToCenter() const { return _returnHeadToCenter; } // Do you want head to try to return to center (depends on interface detected) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index e0f4b55393..969268c549 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -104,7 +104,6 @@ MyAvatar::MyAvatar(RigPointer rig) : _eyeContactTarget(LEFT_EYE), _realWorldFieldOfView("realWorldFieldOfView", DEFAULT_REAL_WORLD_FIELD_OF_VIEW_DEGREES), - _useAdvancedMovementControls("advancedMovementForHandControllersIsChecked", false), _hmdSensorMatrix(), _hmdSensorOrientation(), _hmdSensorPosition(), @@ -120,7 +119,9 @@ MyAvatar::MyAvatar(RigPointer rig) : using namespace recording; _skeletonModel->flagAsCauterized(); - clearDriveKeys(); + for (int i = 0; i < MAX_DRIVE_KEYS; i++) { + _driveKeys[i] = 0.0f; + } // Necessary to select the correct slot using SlotType = void(MyAvatar::*)(const glm::vec3&, bool, const glm::quat&, bool); @@ -153,12 +154,9 @@ MyAvatar::MyAvatar(RigPointer rig) : if (recordingInterface->getPlayFromCurrentLocation()) { setRecordingBasis(); } - _wasCharacterControllerEnabled = _characterController.isEnabled(); - _characterController.setEnabled(false); } else { clearRecordingBasis(); useFullAvatarURL(_fullAvatarURLFromPreferences, _fullAvatarModelName); - _characterController.setEnabled(_wasCharacterControllerEnabled); } auto audioIO = DependencyManager::get(); @@ -229,21 +227,6 @@ MyAvatar::~MyAvatar() { _lookAtTargetAvatar.reset(); } -void MyAvatar::registerMetaTypes(QScriptEngine* engine) { - QScriptValue value = engine->newQObject(this, QScriptEngine::QtOwnership, QScriptEngine::ExcludeDeleteLater | QScriptEngine::ExcludeChildObjects); - engine->globalObject().setProperty("MyAvatar", value); - - QScriptValue driveKeys = engine->newObject(); - auto metaEnum = QMetaEnum::fromType(); - for (int i = 0; i < MAX_DRIVE_KEYS; ++i) { - driveKeys.setProperty(metaEnum.key(i), metaEnum.value(i)); - } - engine->globalObject().setProperty("DriveKeys", driveKeys); - - qScriptRegisterMetaType(engine, audioListenModeToScriptValue, audioListenModeFromScriptValue); - qScriptRegisterMetaType(engine, driveKeysToScriptValue, driveKeysFromScriptValue); -} - void MyAvatar::setOrientationVar(const QVariant& newOrientationVar) { Avatar::setOrientation(quatFromVariant(newOrientationVar)); } @@ -476,7 +459,7 @@ void MyAvatar::simulate(float deltaTime) { // When there are no step values, we zero out the last step pulse. // This allows a user to do faster snapping by tapping a control for (int i = STEP_TRANSLATE_X; !stepAction && i <= STEP_YAW; ++i) { - if (getDriveKey((DriveKeys)i) != 0.0f) { + if (_driveKeys[i] != 0.0f) { stepAction = true; } } @@ -1068,7 +1051,7 @@ void MyAvatar::updateLookAtTargetAvatar() { _lookAtTargetAvatar.reset(); _targetAvatarPosition = glm::vec3(0.0f); - glm::vec3 lookForward = getHead()->getFinalOrientationInWorldFrame() * IDENTITY_FORWARD; + glm::vec3 lookForward = getHead()->getFinalOrientationInWorldFrame() * IDENTITY_FRONT; glm::vec3 cameraPosition = qApp->getCamera()->getPosition(); float smallestAngleTo = glm::radians(DEFAULT_FIELD_OF_VIEW_DEGREES) / 2.0f; @@ -1669,7 +1652,7 @@ bool MyAvatar::shouldRenderHead(const RenderArgs* renderArgs) const { void MyAvatar::updateOrientation(float deltaTime) { // Smoothly rotate body with arrow keys - float targetSpeed = getDriveKey(YAW) * _yawSpeed; + float targetSpeed = _driveKeys[YAW] * _yawSpeed; if (targetSpeed != 0.0f) { const float ROTATION_RAMP_TIMESCALE = 0.1f; float blend = deltaTime / ROTATION_RAMP_TIMESCALE; @@ -1698,8 +1681,8 @@ void MyAvatar::updateOrientation(float deltaTime) { // Comfort Mode: If you press any of the left/right rotation drive keys or input, you'll // get an instantaneous 15 degree turn. If you keep holding the key down you'll get another // snap turn every half second. - if (getDriveKey(STEP_YAW) != 0.0f) { - totalBodyYaw += getDriveKey(STEP_YAW); + if (_driveKeys[STEP_YAW] != 0.0f) { + totalBodyYaw += _driveKeys[STEP_YAW]; } // use head/HMD orientation to turn while flying @@ -1736,7 +1719,7 @@ void MyAvatar::updateOrientation(float deltaTime) { // update body orientation by movement inputs setOrientation(getOrientation() * glm::quat(glm::radians(glm::vec3(0.0f, totalBodyYaw, 0.0f)))); - getHead()->setBasePitch(getHead()->getBasePitch() + getDriveKey(PITCH) * _pitchSpeed * deltaTime); + getHead()->setBasePitch(getHead()->getBasePitch() + _driveKeys[PITCH] * _pitchSpeed * deltaTime); if (qApp->isHMDMode()) { glm::quat orientation = glm::quat_cast(getSensorToWorldMatrix()) * getHMDSensorOrientation(); @@ -1770,14 +1753,14 @@ void MyAvatar::updateActionMotor(float deltaTime) { } // compute action input - glm::vec3 forward = (getDriveKey(TRANSLATE_Z)) * IDENTITY_FORWARD; - glm::vec3 right = (getDriveKey(TRANSLATE_X)) * IDENTITY_RIGHT; + glm::vec3 front = (_driveKeys[TRANSLATE_Z]) * IDENTITY_FRONT; + glm::vec3 right = (_driveKeys[TRANSLATE_X]) * IDENTITY_RIGHT; - glm::vec3 direction = forward + right; + glm::vec3 direction = front + right; CharacterController::State state = _characterController.getState(); if (state == CharacterController::State::Hover) { // we're flying --> support vertical motion - glm::vec3 up = (getDriveKey(TRANSLATE_Y)) * IDENTITY_UP; + glm::vec3 up = (_driveKeys[TRANSLATE_Y]) * IDENTITY_UP; direction += up; } @@ -1816,7 +1799,7 @@ void MyAvatar::updateActionMotor(float deltaTime) { _actionMotorVelocity = MAX_WALKING_SPEED * direction; } - float boomChange = getDriveKey(ZOOM); + float boomChange = _driveKeys[ZOOM]; _boomLength += 2.0f * _boomLength * boomChange + boomChange * boomChange; _boomLength = glm::clamp(_boomLength, ZOOM_MIN, ZOOM_MAX); } @@ -1847,11 +1830,11 @@ void MyAvatar::updatePosition(float deltaTime) { } // capture the head rotation, in sensor space, when the user first indicates they would like to move/fly. - if (!_hoverReferenceCameraFacingIsCaptured && (fabs(getDriveKey(TRANSLATE_Z)) > 0.1f || fabs(getDriveKey(TRANSLATE_X)) > 0.1f)) { + if (!_hoverReferenceCameraFacingIsCaptured && (fabs(_driveKeys[TRANSLATE_Z]) > 0.1f || fabs(_driveKeys[TRANSLATE_X]) > 0.1f)) { _hoverReferenceCameraFacingIsCaptured = true; // transform the camera facing vector into sensor space. _hoverReferenceCameraFacing = transformVectorFast(glm::inverse(_sensorToWorldMatrix), getHead()->getCameraOrientation() * Vectors::UNIT_Z); - } else if (_hoverReferenceCameraFacingIsCaptured && (fabs(getDriveKey(TRANSLATE_Z)) <= 0.1f && fabs(getDriveKey(TRANSLATE_X)) <= 0.1f)) { + } else if (_hoverReferenceCameraFacingIsCaptured && (fabs(_driveKeys[TRANSLATE_Z]) <= 0.1f && fabs(_driveKeys[TRANSLATE_X]) <= 0.1f)) { _hoverReferenceCameraFacingIsCaptured = false; } } @@ -2053,7 +2036,7 @@ void MyAvatar::goToLocation(const glm::vec3& newPosition, // move the user a couple units away const float DISTANCE_TO_USER = 2.0f; - _goToPosition = newPosition - quatOrientation * IDENTITY_FORWARD * DISTANCE_TO_USER; + _goToPosition = newPosition - quatOrientation * IDENTITY_FRONT * DISTANCE_TO_USER; } _goToOrientation = quatOrientation; @@ -2107,61 +2090,17 @@ bool MyAvatar::getCharacterControllerEnabled() { } void MyAvatar::clearDriveKeys() { - _driveKeys.fill(0.0f); -} - -void MyAvatar::setDriveKey(DriveKeys key, float val) { - try { - _driveKeys.at(key) = val; - } catch (const std::exception&) { - qCCritical(interfaceapp) << Q_FUNC_INFO << ": Index out of bounds"; - } -} - -float MyAvatar::getDriveKey(DriveKeys key) const { - return isDriveKeyDisabled(key) ? 0.0f : getRawDriveKey(key); -} - -float MyAvatar::getRawDriveKey(DriveKeys key) const { - try { - return _driveKeys.at(key); - } catch (const std::exception&) { - qCCritical(interfaceapp) << Q_FUNC_INFO << ": Index out of bounds"; - return 0.0f; + for (int i = 0; i < MAX_DRIVE_KEYS; ++i) { + _driveKeys[i] = 0.0f; } } void MyAvatar::relayDriveKeysToCharacterController() { - if (getDriveKey(TRANSLATE_Y) > 0.0f) { + if (_driveKeys[TRANSLATE_Y] > 0.0f) { _characterController.jump(); } } -void MyAvatar::disableDriveKey(DriveKeys key) { - try { - _disabledDriveKeys.set(key); - } catch (const std::exception&) { - qCCritical(interfaceapp) << Q_FUNC_INFO << ": Index out of bounds"; - } -} - -void MyAvatar::enableDriveKey(DriveKeys key) { - try { - _disabledDriveKeys.reset(key); - } catch (const std::exception&) { - qCCritical(interfaceapp) << Q_FUNC_INFO << ": Index out of bounds"; - } -} - -bool MyAvatar::isDriveKeyDisabled(DriveKeys key) const { - try { - return _disabledDriveKeys.test(key); - } catch (const std::exception&) { - qCCritical(interfaceapp) << Q_FUNC_INFO << ": Index out of bounds"; - return true; - } -} - glm::vec3 MyAvatar::getWorldBodyPosition() const { return transformPoint(_sensorToWorldMatrix, extractTranslation(_bodySensorMatrix)); } @@ -2247,15 +2186,7 @@ QScriptValue audioListenModeToScriptValue(QScriptEngine* engine, const AudioList } void audioListenModeFromScriptValue(const QScriptValue& object, AudioListenerMode& audioListenerMode) { - audioListenerMode = static_cast(object.toUInt16()); -} - -QScriptValue driveKeysToScriptValue(QScriptEngine* engine, const MyAvatar::DriveKeys& driveKeys) { - return driveKeys; -} - -void driveKeysFromScriptValue(const QScriptValue& object, MyAvatar::DriveKeys& driveKeys) { - driveKeys = static_cast(object.toUInt16()); + audioListenerMode = (AudioListenerMode)object.toUInt16(); } @@ -2448,7 +2379,7 @@ bool MyAvatar::didTeleport() { } bool MyAvatar::hasDriveInput() const { - return fabsf(getDriveKey(TRANSLATE_X)) > 0.0f || fabsf(getDriveKey(TRANSLATE_Y)) > 0.0f || fabsf(getDriveKey(TRANSLATE_Z)) > 0.0f; + return fabsf(_driveKeys[TRANSLATE_X]) > 0.0f || fabsf(_driveKeys[TRANSLATE_Y]) > 0.0f || fabsf(_driveKeys[TRANSLATE_Z]) > 0.0f; } void MyAvatar::setAway(bool value) { @@ -2564,7 +2495,7 @@ bool MyAvatar::pinJoint(int index, const glm::vec3& position, const glm::quat& o return false; } - slamPosition(position); + setPosition(position); setOrientation(orientation); _rig->setMaxHipsOffsetLength(0.05f); diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 5f812f1f99..3cc665b533 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -12,8 +12,6 @@ #ifndef hifi_MyAvatar_h #define hifi_MyAvatar_h -#include - #include #include @@ -31,6 +29,20 @@ class AvatarActionHold; class ModelItemID; +enum DriveKeys { + TRANSLATE_X = 0, + TRANSLATE_Y, + TRANSLATE_Z, + YAW, + STEP_TRANSLATE_X, + STEP_TRANSLATE_Y, + STEP_TRANSLATE_Z, + STEP_YAW, + PITCH, + ZOOM, + MAX_DRIVE_KEYS +}; + enum eyeContactTarget { LEFT_EYE, RIGHT_EYE, @@ -74,29 +86,11 @@ class MyAvatar : public Avatar { Q_PROPERTY(bool hmdLeanRecenterEnabled READ getHMDLeanRecenterEnabled WRITE setHMDLeanRecenterEnabled) Q_PROPERTY(bool characterControllerEnabled READ getCharacterControllerEnabled WRITE setCharacterControllerEnabled) - Q_PROPERTY(bool useAdvancedMovementControls READ useAdvancedMovementControls WRITE setUseAdvancedMovementControls) public: - enum DriveKeys { - TRANSLATE_X = 0, - TRANSLATE_Y, - TRANSLATE_Z, - YAW, - STEP_TRANSLATE_X, - STEP_TRANSLATE_Y, - STEP_TRANSLATE_Z, - STEP_YAW, - PITCH, - ZOOM, - MAX_DRIVE_KEYS - }; - Q_ENUM(DriveKeys) - explicit MyAvatar(RigPointer rig); ~MyAvatar(); - void registerMetaTypes(QScriptEngine* engine); - virtual void simulateAttachments(float deltaTime) override; AudioListenerMode getAudioListenerModeHead() const { return FROM_HEAD; } @@ -177,10 +171,6 @@ public: Q_INVOKABLE void setHMDLeanRecenterEnabled(bool value) { _hmdLeanRecenterEnabled = value; } Q_INVOKABLE bool getHMDLeanRecenterEnabled() const { return _hmdLeanRecenterEnabled; } - bool useAdvancedMovementControls() const { return _useAdvancedMovementControls.get(); } - void setUseAdvancedMovementControls(bool useAdvancedMovementControls) - { _useAdvancedMovementControls.set(useAdvancedMovementControls); } - // get/set avatar data void saveData(); void loadData(); @@ -190,15 +180,9 @@ public: // Set what driving keys are being pressed to control thrust levels void clearDriveKeys(); - void setDriveKey(DriveKeys key, float val); - float getDriveKey(DriveKeys key) const; - Q_INVOKABLE float getRawDriveKey(DriveKeys key) const; + void setDriveKeys(int key, float val) { _driveKeys[key] = val; }; void relayDriveKeysToCharacterController(); - Q_INVOKABLE void disableDriveKey(DriveKeys key); - Q_INVOKABLE void enableDriveKey(DriveKeys key); - Q_INVOKABLE bool isDriveKeyDisabled(DriveKeys key) const; - eyeContactTarget getEyeContactTarget(); Q_INVOKABLE glm::vec3 getTrackedHeadPosition() const { return _trackedHeadPosition; } @@ -368,6 +352,7 @@ private: virtual bool shouldRenderHead(const RenderArgs* renderArgs) const override; void setShouldRenderLocally(bool shouldRender) { _shouldRender = shouldRender; setEnableMeshVisible(shouldRender); } bool getShouldRenderLocally() const { return _shouldRender; } + bool getDriveKeys(int key) { return _driveKeys[key] != 0.0f; }; bool isMyAvatar() const override { return true; } virtual int parseDataFromBuffer(const QByteArray& buffer) override; virtual glm::vec3 getSkeletonPosition() const override; @@ -403,9 +388,7 @@ private: void clampScaleChangeToDomainLimits(float desiredScale); glm::mat4 computeCameraRelativeHandControllerMatrix(const glm::mat4& controllerSensorMatrix) const; - std::array _driveKeys; - std::bitset _disabledDriveKeys; - + float _driveKeys[MAX_DRIVE_KEYS]; bool _wasPushing; bool _isPushing; bool _isBeingPushed; @@ -428,7 +411,6 @@ private: SharedSoundPointer _collisionSound; MyCharacterController _characterController; - bool _wasCharacterControllerEnabled { true }; AvatarWeakPointer _lookAtTargetAvatar; glm::vec3 _targetAvatarPosition; @@ -441,7 +423,6 @@ private: glm::vec3 _trackedHeadPosition; Setting::Handle _realWorldFieldOfView; - Setting::Handle _useAdvancedMovementControls; // private methods void updateOrientation(float deltaTime); @@ -559,7 +540,4 @@ private: QScriptValue audioListenModeToScriptValue(QScriptEngine* engine, const AudioListenerMode& audioListenerMode); void audioListenModeFromScriptValue(const QScriptValue& object, AudioListenerMode& audioListenerMode); -QScriptValue driveKeysToScriptValue(QScriptEngine* engine, const MyAvatar::DriveKeys& driveKeys); -void driveKeysFromScriptValue(const QScriptValue& object, MyAvatar::DriveKeys& driveKeys); - #endif // hifi_MyAvatar_h diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp index f2d97a0137..364dff52a3 100644 --- a/interface/src/ui/ApplicationOverlay.cpp +++ b/interface/src/ui/ApplicationOverlay.cpp @@ -13,6 +13,7 @@ #include #include +#include #include #include #include @@ -41,6 +42,7 @@ ApplicationOverlay::ApplicationOverlay() _domainStatusBorder = geometryCache->allocateID(); _magnifierBorder = geometryCache->allocateID(); _qmlGeometryId = geometryCache->allocateID(); + _rearViewGeometryId = geometryCache->allocateID(); } ApplicationOverlay::~ApplicationOverlay() { @@ -49,6 +51,7 @@ ApplicationOverlay::~ApplicationOverlay() { geometryCache->releaseID(_domainStatusBorder); geometryCache->releaseID(_magnifierBorder); geometryCache->releaseID(_qmlGeometryId); + geometryCache->releaseID(_rearViewGeometryId); } } @@ -83,6 +86,7 @@ void ApplicationOverlay::renderOverlay(RenderArgs* renderArgs) { // Now render the overlay components together into a single texture renderDomainConnectionStatusBorder(renderArgs); // renders the connected domain line renderAudioScope(renderArgs); // audio scope in the very back - NOTE: this is the debug audio scope, not the VU meter + renderRearView(renderArgs); // renders the mirror view selfie renderOverlays(renderArgs); // renders Scripts Overlay and AudioScope renderQmlUi(renderArgs); // renders a unit quad with the QML UI texture, and the text overlays from scripts renderStatsAndLogs(renderArgs); // currently renders nothing @@ -95,7 +99,7 @@ void ApplicationOverlay::renderQmlUi(RenderArgs* renderArgs) { PROFILE_RANGE(app, __FUNCTION__); if (!_uiTexture) { - _uiTexture = gpu::TexturePointer(gpu::Texture::createExternal(OffscreenQmlSurface::getDiscardLambda())); + _uiTexture = gpu::TexturePointer(gpu::Texture::createExternal2D(OffscreenQmlSurface::getDiscardLambda())); _uiTexture->setSource(__FUNCTION__); } // Once we move UI rendering and screen rendering to different @@ -159,6 +163,45 @@ void ApplicationOverlay::renderOverlays(RenderArgs* renderArgs) { qApp->getOverlays().renderHUD(renderArgs); } +void ApplicationOverlay::renderRearViewToFbo(RenderArgs* renderArgs) { +} + +void ApplicationOverlay::renderRearView(RenderArgs* renderArgs) { + if (!qApp->isHMDMode() && Menu::getInstance()->isOptionChecked(MenuOption::MiniMirror) && + !Menu::getInstance()->isOptionChecked(MenuOption::FullscreenMirror)) { + gpu::Batch& batch = *renderArgs->_batch; + + auto geometryCache = DependencyManager::get(); + + auto framebuffer = DependencyManager::get(); + auto selfieTexture = framebuffer->getSelfieFramebuffer()->getRenderBuffer(0); + + int width = renderArgs->_viewport.z; + int height = renderArgs->_viewport.w; + mat4 legacyProjection = glm::ortho(0, width, height, 0, ORTHO_NEAR_CLIP, ORTHO_FAR_CLIP); + batch.setProjectionTransform(legacyProjection); + batch.setModelTransform(Transform()); + batch.resetViewTransform(); + + float screenRatio = ((float)qApp->getDevicePixelRatio()); + float renderRatio = ((float)qApp->getRenderResolutionScale()); + + auto viewport = qApp->getMirrorViewRect(); + glm::vec2 bottomLeft(viewport.left(), viewport.top() + viewport.height()); + glm::vec2 topRight(viewport.left() + viewport.width(), viewport.top()); + bottomLeft *= screenRatio; + topRight *= screenRatio; + glm::vec2 texCoordMinCorner(0.0f, 0.0f); + glm::vec2 texCoordMaxCorner(viewport.width() * renderRatio / float(selfieTexture->getWidth()), viewport.height() * renderRatio / float(selfieTexture->getHeight())); + + batch.setResourceTexture(0, selfieTexture); + float alpha = DependencyManager::get()->getDesktop()->property("unpinnedAlpha").toFloat(); + geometryCache->renderQuad(batch, bottomLeft, topRight, texCoordMinCorner, texCoordMaxCorner, glm::vec4(1.0f, 1.0f, 1.0f, alpha), _rearViewGeometryId); + + batch.setResourceTexture(0, renderArgs->_whiteTexture); + } +} + void ApplicationOverlay::renderStatsAndLogs(RenderArgs* renderArgs) { // Display stats and log text onscreen @@ -229,13 +272,13 @@ void ApplicationOverlay::buildFramebufferObject() { auto width = uiSize.x; auto height = uiSize.y; if (!_overlayFramebuffer->getDepthStencilBuffer()) { - auto overlayDepthTexture = gpu::TexturePointer(gpu::Texture::createRenderBuffer(DEPTH_FORMAT, width, height, DEFAULT_SAMPLER)); + auto overlayDepthTexture = gpu::TexturePointer(gpu::Texture::create2D(DEPTH_FORMAT, width, height, DEFAULT_SAMPLER)); _overlayFramebuffer->setDepthStencilBuffer(overlayDepthTexture, DEPTH_FORMAT); } if (!_overlayFramebuffer->getRenderBuffer(0)) { const gpu::Sampler OVERLAY_SAMPLER(gpu::Sampler::FILTER_MIN_MAG_LINEAR, gpu::Sampler::WRAP_CLAMP); - auto colorBuffer = gpu::TexturePointer(gpu::Texture::createRenderBuffer(COLOR_FORMAT, width, height, OVERLAY_SAMPLER)); + auto colorBuffer = gpu::TexturePointer(gpu::Texture::create2D(COLOR_FORMAT, width, height, OVERLAY_SAMPLER)); _overlayFramebuffer->setRenderBuffer(0, colorBuffer); } } diff --git a/interface/src/ui/ApplicationOverlay.h b/interface/src/ui/ApplicationOverlay.h index af4d8779d4..7ace5ee885 100644 --- a/interface/src/ui/ApplicationOverlay.h +++ b/interface/src/ui/ApplicationOverlay.h @@ -31,6 +31,8 @@ public: private: void renderStatsAndLogs(RenderArgs* renderArgs); void renderDomainConnectionStatusBorder(RenderArgs* renderArgs); + void renderRearViewToFbo(RenderArgs* renderArgs); + void renderRearView(RenderArgs* renderArgs); void renderQmlUi(RenderArgs* renderArgs); void renderAudioScope(RenderArgs* renderArgs); void renderOverlays(RenderArgs* renderArgs); @@ -49,6 +51,7 @@ private: gpu::TexturePointer _overlayColorTexture; gpu::FramebufferPointer _overlayFramebuffer; int _qmlGeometryId { 0 }; + int _rearViewGeometryId { 0 }; }; #endif // hifi_ApplicationOverlay_h diff --git a/interface/src/ui/AvatarInputs.cpp b/interface/src/ui/AvatarInputs.cpp index 944be4bf9e..b09289c78a 100644 --- a/interface/src/ui/AvatarInputs.cpp +++ b/interface/src/ui/AvatarInputs.cpp @@ -20,6 +20,10 @@ HIFI_QML_DEF(AvatarInputs) static AvatarInputs* INSTANCE{ nullptr }; +static const char SETTINGS_GROUP_NAME[] = "Rear View Tools"; +static const char ZOOM_LEVEL_SETTINGS[] = "ZoomLevel"; + +static Setting::Handle rearViewZoomLevel(QStringList() << SETTINGS_GROUP_NAME << ZOOM_LEVEL_SETTINGS, 0); AvatarInputs* AvatarInputs::getInstance() { if (!INSTANCE) { @@ -32,6 +36,8 @@ AvatarInputs* AvatarInputs::getInstance() { AvatarInputs::AvatarInputs(QQuickItem* parent) : QQuickItem(parent) { INSTANCE = this; + int zoomSetting = rearViewZoomLevel.get(); + _mirrorZoomed = zoomSetting == 0; } #define AI_UPDATE(name, src) \ @@ -56,6 +62,8 @@ void AvatarInputs::update() { if (!Menu::getInstance()) { return; } + AI_UPDATE(mirrorVisible, Menu::getInstance()->isOptionChecked(MenuOption::MiniMirror) && !qApp->isHMDMode() + && !Menu::getInstance()->isOptionChecked(MenuOption::FullscreenMirror)); AI_UPDATE(cameraEnabled, !Menu::getInstance()->isOptionChecked(MenuOption::NoFaceTracking)); AI_UPDATE(cameraMuted, Menu::getInstance()->isOptionChecked(MenuOption::MuteFaceTracking)); AI_UPDATE(isHMD, qApp->isHMDMode()); @@ -114,3 +122,15 @@ void AvatarInputs::toggleAudioMute() { void AvatarInputs::resetSensors() { qApp->resetSensors(); } + +void AvatarInputs::toggleZoom() { + _mirrorZoomed = !_mirrorZoomed; + rearViewZoomLevel.set(_mirrorZoomed ? 0 : 1); + emit mirrorZoomedChanged(); +} + +void AvatarInputs::closeMirror() { + if (Menu::getInstance()->isOptionChecked(MenuOption::MiniMirror)) { + Menu::getInstance()->triggerOption(MenuOption::MiniMirror); + } +} diff --git a/interface/src/ui/AvatarInputs.h b/interface/src/ui/AvatarInputs.h index 5535469445..85570ecd3c 100644 --- a/interface/src/ui/AvatarInputs.h +++ b/interface/src/ui/AvatarInputs.h @@ -28,6 +28,8 @@ class AvatarInputs : public QQuickItem { AI_PROPERTY(bool, audioMuted, false) AI_PROPERTY(bool, audioClipping, false) AI_PROPERTY(float, audioLevel, 0) + AI_PROPERTY(bool, mirrorVisible, false) + AI_PROPERTY(bool, mirrorZoomed, true) AI_PROPERTY(bool, isHMD, false) AI_PROPERTY(bool, showAudioTools, true) @@ -42,6 +44,8 @@ signals: void audioMutedChanged(); void audioClippingChanged(); void audioLevelChanged(); + void mirrorVisibleChanged(); + void mirrorZoomedChanged(); void isHMDChanged(); void showAudioToolsChanged(); @@ -49,6 +53,8 @@ protected: Q_INVOKABLE void resetSensors(); Q_INVOKABLE void toggleCameraMute(); Q_INVOKABLE void toggleAudioMute(); + Q_INVOKABLE void toggleZoom(); + Q_INVOKABLE void closeMirror(); private: float _trailingAudioLoudness{ 0 }; diff --git a/interface/src/ui/BaseLogDialog.cpp b/interface/src/ui/BaseLogDialog.cpp index 571d3ac403..7e0027e0a8 100644 --- a/interface/src/ui/BaseLogDialog.cpp +++ b/interface/src/ui/BaseLogDialog.cpp @@ -28,23 +28,17 @@ const int SEARCH_BUTTON_LEFT = 25; const int SEARCH_BUTTON_WIDTH = 20; const int SEARCH_TOGGLE_BUTTON_WIDTH = 50; const int SEARCH_TEXT_WIDTH = 240; -const int TIME_STAMP_LENGTH = 16; -const int FONT_WEIGHT = 75; const QColor HIGHLIGHT_COLOR = QColor("#3366CC"); -const QColor BOLD_COLOR = QColor("#445c8c"); -const QString BOLD_PATTERN = "\\[\\d*\\/.*:\\d*:\\d*\\]"; -class Highlighter : public QSyntaxHighlighter { +class KeywordHighlighter : public QSyntaxHighlighter { public: - Highlighter(QTextDocument* parent = nullptr); - void setBold(int indexToBold); + KeywordHighlighter(QTextDocument* parent = nullptr); QString keyword; protected: void highlightBlock(const QString& text) override; private: - QTextCharFormat boldFormat; QTextCharFormat keywordFormat; }; @@ -95,7 +89,7 @@ void BaseLogDialog::initControls() { _leftPad += SEARCH_TOGGLE_BUTTON_WIDTH + BUTTON_MARGIN; _searchPrevButton->show(); connect(_searchPrevButton, SIGNAL(clicked()), SLOT(toggleSearchPrev())); - + _searchNextButton = new QPushButton(this); _searchNextButton->setObjectName("searchNextButton"); _searchNextButton->setGeometry(_leftPad, ELEMENT_MARGIN, SEARCH_TOGGLE_BUTTON_WIDTH, ELEMENT_HEIGHT); @@ -107,8 +101,9 @@ void BaseLogDialog::initControls() { _logTextBox = new QPlainTextEdit(this); _logTextBox->setReadOnly(true); _logTextBox->show(); - _highlighter = new Highlighter(_logTextBox->document()); + _highlighter = new KeywordHighlighter(_logTextBox->document()); connect(_logTextBox, SIGNAL(selectionChanged()), SLOT(updateSelection())); + } void BaseLogDialog::showEvent(QShowEvent* event) { @@ -121,9 +116,7 @@ void BaseLogDialog::resizeEvent(QResizeEvent* event) { void BaseLogDialog::appendLogLine(QString logLine) { if (logLine.contains(_searchTerm, Qt::CaseInsensitive)) { - int indexToBold = _logTextBox->document()->characterCount(); _logTextBox->appendPlainText(logLine.trimmed()); - _highlighter->setBold(indexToBold); } } @@ -135,7 +128,7 @@ void BaseLogDialog::handleSearchTextChanged(QString searchText) { if (searchText.isEmpty()) { return; } - + QTextCursor cursor = _logTextBox->textCursor(); if (cursor.hasSelection()) { QString selectedTerm = cursor.selectedText(); @@ -143,16 +136,16 @@ void BaseLogDialog::handleSearchTextChanged(QString searchText) { return; } } - + cursor.setPosition(0, QTextCursor::MoveAnchor); _logTextBox->setTextCursor(cursor); bool foundTerm = _logTextBox->find(searchText); - + if (!foundTerm) { cursor.movePosition(QTextCursor::End, QTextCursor::MoveAnchor); _logTextBox->setTextCursor(cursor); } - + _searchTerm = searchText; _highlighter->keyword = searchText; _highlighter->rehighlight(); @@ -182,7 +175,6 @@ void BaseLogDialog::showLogData() { _logTextBox->clear(); _logTextBox->appendPlainText(getCurrentLog()); _logTextBox->ensureCursorVisible(); - _highlighter->rehighlight(); } void BaseLogDialog::updateSelection() { @@ -195,28 +187,16 @@ void BaseLogDialog::updateSelection() { } } -Highlighter::Highlighter(QTextDocument* parent) : QSyntaxHighlighter(parent) { - boldFormat.setFontWeight(FONT_WEIGHT); - boldFormat.setForeground(BOLD_COLOR); +KeywordHighlighter::KeywordHighlighter(QTextDocument* parent) : QSyntaxHighlighter(parent) { keywordFormat.setForeground(HIGHLIGHT_COLOR); } -void Highlighter::highlightBlock(const QString& text) { - QRegExp expression(BOLD_PATTERN); - - int index = text.indexOf(expression, 0); - - while (index >= 0) { - int length = expression.matchedLength(); - setFormat(index, length, boldFormat); - index = text.indexOf(expression, index + length); - } - +void KeywordHighlighter::highlightBlock(const QString& text) { if (keyword.isNull() || keyword.isEmpty()) { return; } - index = text.indexOf(keyword, 0, Qt::CaseInsensitive); + int index = text.indexOf(keyword, 0, Qt::CaseInsensitive); int length = keyword.length(); while (index >= 0) { @@ -224,7 +204,3 @@ void Highlighter::highlightBlock(const QString& text) { index = text.indexOf(keyword, index + length, Qt::CaseInsensitive); } } - -void Highlighter::setBold(int indexToBold) { - setFormat(indexToBold, TIME_STAMP_LENGTH, boldFormat); -} diff --git a/interface/src/ui/BaseLogDialog.h b/interface/src/ui/BaseLogDialog.h index e18d23937f..d097010bae 100644 --- a/interface/src/ui/BaseLogDialog.h +++ b/interface/src/ui/BaseLogDialog.h @@ -23,7 +23,7 @@ const int BUTTON_MARGIN = 8; class QPushButton; class QLineEdit; class QPlainTextEdit; -class Highlighter; +class KeywordHighlighter; class BaseLogDialog : public QDialog { Q_OBJECT @@ -56,7 +56,7 @@ private: QPushButton* _searchPrevButton { nullptr }; QPushButton* _searchNextButton { nullptr }; QString _searchTerm; - Highlighter* _highlighter { nullptr }; + KeywordHighlighter* _highlighter { nullptr }; void initControls(); void showLogData(); diff --git a/interface/src/ui/CachesSizeDialog.cpp b/interface/src/ui/CachesSizeDialog.cpp new file mode 100644 index 0000000000..935a6d126e --- /dev/null +++ b/interface/src/ui/CachesSizeDialog.cpp @@ -0,0 +1,84 @@ +// +// CachesSizeDialog.cpp +// +// +// Created by Clement on 1/12/15. +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "CachesSizeDialog.h" + + +QDoubleSpinBox* createDoubleSpinBox(QWidget* parent) { + QDoubleSpinBox* box = new QDoubleSpinBox(parent); + box->setDecimals(0); + box->setRange(MIN_UNUSED_MAX_SIZE / BYTES_PER_MEGABYTES, MAX_UNUSED_MAX_SIZE / BYTES_PER_MEGABYTES); + + return box; +} + +CachesSizeDialog::CachesSizeDialog(QWidget* parent) : + QDialog(parent, Qt::Window | Qt::WindowCloseButtonHint) +{ + setWindowTitle("Caches Size"); + + // Create layouter + QFormLayout* form = new QFormLayout(this); + setLayout(form); + + form->addRow("Animations cache size (MB):", _animations = createDoubleSpinBox(this)); + form->addRow("Geometries cache size (MB):", _geometries = createDoubleSpinBox(this)); + form->addRow("Sounds cache size (MB):", _sounds = createDoubleSpinBox(this)); + form->addRow("Textures cache size (MB):", _textures = createDoubleSpinBox(this)); + + resetClicked(true); + + // Add a button to reset + QPushButton* confirmButton = new QPushButton("Confirm", this); + QPushButton* resetButton = new QPushButton("Reset", this); + form->addRow(confirmButton, resetButton); + connect(confirmButton, SIGNAL(clicked(bool)), this, SLOT(confirmClicked(bool))); + connect(resetButton, SIGNAL(clicked(bool)), this, SLOT(resetClicked(bool))); +} + +void CachesSizeDialog::confirmClicked(bool checked) { + DependencyManager::get()->setUnusedResourceCacheSize(_animations->value() * BYTES_PER_MEGABYTES); + DependencyManager::get()->setUnusedResourceCacheSize(_geometries->value() * BYTES_PER_MEGABYTES); + DependencyManager::get()->setUnusedResourceCacheSize(_sounds->value() * BYTES_PER_MEGABYTES); + // Disabling the texture cache because it's a liability in cases where we're overcommiting GPU memory +#if 0 + DependencyManager::get()->setUnusedResourceCacheSize(_textures->value() * BYTES_PER_MEGABYTES); +#endif + + QDialog::close(); +} + +void CachesSizeDialog::resetClicked(bool checked) { + _animations->setValue(DependencyManager::get()->getUnusedResourceCacheSize() / BYTES_PER_MEGABYTES); + _geometries->setValue(DependencyManager::get()->getUnusedResourceCacheSize() / BYTES_PER_MEGABYTES); + _sounds->setValue(DependencyManager::get()->getUnusedResourceCacheSize() / BYTES_PER_MEGABYTES); + _textures->setValue(DependencyManager::get()->getUnusedResourceCacheSize() / BYTES_PER_MEGABYTES); +} + +void CachesSizeDialog::reject() { + // Just regularly close upon ESC + QDialog::close(); +} + +void CachesSizeDialog::closeEvent(QCloseEvent* event) { + QDialog::closeEvent(event); + emit closed(); +} diff --git a/interface/src/ui/CachesSizeDialog.h b/interface/src/ui/CachesSizeDialog.h new file mode 100644 index 0000000000..025d0f2bac --- /dev/null +++ b/interface/src/ui/CachesSizeDialog.h @@ -0,0 +1,45 @@ +// +// CachesSizeDialog.h +// +// +// Created by Clement on 1/12/15. +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_CachesSizeDialog_h +#define hifi_CachesSizeDialog_h + +#include + +class QDoubleSpinBox; + +class CachesSizeDialog : public QDialog { + Q_OBJECT +public: + // Sets up the UI + CachesSizeDialog(QWidget* parent); + +signals: + void closed(); + +public slots: + void reject() override; + void confirmClicked(bool checked); + void resetClicked(bool checked); + +protected: + // Emits a 'closed' signal when this dialog is closed. + void closeEvent(QCloseEvent* event) override; + +private: + QDoubleSpinBox* _animations = nullptr; + QDoubleSpinBox* _geometries = nullptr; + QDoubleSpinBox* _scripts = nullptr; + QDoubleSpinBox* _sounds = nullptr; + QDoubleSpinBox* _textures = nullptr; +}; + +#endif // hifi_CachesSizeDialog_h diff --git a/interface/src/ui/DialogsManager.cpp b/interface/src/ui/DialogsManager.cpp index f1d6f585d7..3252fef4f0 100644 --- a/interface/src/ui/DialogsManager.cpp +++ b/interface/src/ui/DialogsManager.cpp @@ -19,13 +19,16 @@ #include #include "AddressBarDialog.h" +#include "CachesSizeDialog.h" #include "ConnectionFailureDialog.h" +#include "DiskCacheEditor.h" #include "DomainConnectionDialog.h" #include "HMDToolsDialog.h" #include "LodToolsDialog.h" #include "LoginDialog.h" #include "OctreeStatsDialog.h" #include "PreferencesDialog.h" +#include "ScriptEditorWindow.h" #include "UpdateDialog.h" template @@ -64,6 +67,11 @@ void DialogsManager::setDomainConnectionFailureVisibility(bool visible) { } } +void DialogsManager::toggleDiskCacheEditor() { + maybeCreateDialog(_diskCacheEditor); + _diskCacheEditor->toggle(); +} + void DialogsManager::toggleLoginDialog() { LoginDialog::toggleAction(); } @@ -89,6 +97,16 @@ void DialogsManager::octreeStatsDetails() { _octreeStatsDialog->raise(); } +void DialogsManager::cachesSizeDialog() { + if (!_cachesSizeDialog) { + maybeCreateDialog(_cachesSizeDialog); + + connect(_cachesSizeDialog, SIGNAL(closed()), _cachesSizeDialog, SLOT(deleteLater())); + _cachesSizeDialog->show(); + } + _cachesSizeDialog->raise(); +} + void DialogsManager::lodTools() { if (!_lodToolsDialog) { maybeCreateDialog(_lodToolsDialog); @@ -119,6 +137,12 @@ void DialogsManager::hmdToolsClosed() { } } +void DialogsManager::showScriptEditor() { + maybeCreateDialog(_scriptEditor); + _scriptEditor->show(); + _scriptEditor->raise(); +} + void DialogsManager::showTestingResults() { if (!_testingDialog) { _testingDialog = new TestingDialog(qApp->getWindow()); diff --git a/interface/src/ui/DialogsManager.h b/interface/src/ui/DialogsManager.h index 608195aca7..54aef38984 100644 --- a/interface/src/ui/DialogsManager.h +++ b/interface/src/ui/DialogsManager.h @@ -22,6 +22,7 @@ class AnimationsDialog; class AttachmentsDialog; class CachesSizeDialog; +class DiskCacheEditor; class LodToolsDialog; class OctreeStatsDialog; class ScriptEditorWindow; @@ -45,11 +46,14 @@ public slots: void showAddressBar(); void showFeed(); void setDomainConnectionFailureVisibility(bool visible); + void toggleDiskCacheEditor(); void toggleLoginDialog(); void showLoginDialog(); void octreeStatsDetails(); + void cachesSizeDialog(); void lodTools(); void hmdTools(bool showTools); + void showScriptEditor(); void showDomainConnectionDialog(); void showTestingResults(); @@ -73,10 +77,12 @@ private: QPointer _animationsDialog; QPointer _attachmentsDialog; QPointer _cachesSizeDialog; + QPointer _diskCacheEditor; QPointer _ircInfoBox; QPointer _hmdToolsDialog; QPointer _lodToolsDialog; QPointer _octreeStatsDialog; + QPointer _scriptEditor; QPointer _testingDialog; QPointer _domainConnectionDialog; }; diff --git a/interface/src/ui/DiskCacheEditor.cpp b/interface/src/ui/DiskCacheEditor.cpp new file mode 100644 index 0000000000..1a7be8642b --- /dev/null +++ b/interface/src/ui/DiskCacheEditor.cpp @@ -0,0 +1,146 @@ +// +// DiskCacheEditor.cpp +// +// +// Created by Clement on 3/4/15. +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "DiskCacheEditor.h" + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "OffscreenUi.h" + +DiskCacheEditor::DiskCacheEditor(QWidget* parent) : QObject(parent) { +} + +QWindow* DiskCacheEditor::windowHandle() { + return (_dialog) ? _dialog->windowHandle() : nullptr; +} + +void DiskCacheEditor::toggle() { + if (!_dialog) { + makeDialog(); + } + + if (!_dialog->isActiveWindow()) { + _dialog->show(); + _dialog->raise(); + _dialog->activateWindow(); + } else { + _dialog->close(); + } +} + +void DiskCacheEditor::makeDialog() { + _dialog = new QDialog(static_cast(parent())); + Q_CHECK_PTR(_dialog); + _dialog->setAttribute(Qt::WA_DeleteOnClose); + _dialog->setWindowTitle("Disk Cache Editor"); + + QGridLayout* layout = new QGridLayout(_dialog); + Q_CHECK_PTR(layout); + _dialog->setLayout(layout); + + + QLabel* path = new QLabel("Path : ", _dialog); + Q_CHECK_PTR(path); + path->setAlignment(Qt::AlignRight); + layout->addWidget(path, 0, 0); + + QLabel* size = new QLabel("Current Size : ", _dialog); + Q_CHECK_PTR(size); + size->setAlignment(Qt::AlignRight); + layout->addWidget(size, 1, 0); + + QLabel* maxSize = new QLabel("Max Size : ", _dialog); + Q_CHECK_PTR(maxSize); + maxSize->setAlignment(Qt::AlignRight); + layout->addWidget(maxSize, 2, 0); + + + _path = new QLabel(_dialog); + Q_CHECK_PTR(_path); + _path->setAlignment(Qt::AlignLeft); + layout->addWidget(_path, 0, 1, 1, 3); + + _size = new QLabel(_dialog); + Q_CHECK_PTR(_size); + _size->setAlignment(Qt::AlignLeft); + layout->addWidget(_size, 1, 1, 1, 3); + + _maxSize = new QLabel(_dialog); + Q_CHECK_PTR(_maxSize); + _maxSize->setAlignment(Qt::AlignLeft); + layout->addWidget(_maxSize, 2, 1, 1, 3); + + refresh(); + + + static const int REFRESH_INTERVAL = 100; // msec + _refreshTimer = new QTimer(_dialog); + _refreshTimer->setInterval(REFRESH_INTERVAL); // Qt::CoarseTimer acceptable, no need for real time accuracy + _refreshTimer->setSingleShot(false); + QObject::connect(_refreshTimer.data(), &QTimer::timeout, this, &DiskCacheEditor::refresh); + _refreshTimer->start(); + + QPushButton* clearCacheButton = new QPushButton(_dialog); + Q_CHECK_PTR(clearCacheButton); + clearCacheButton->setText("Clear"); + clearCacheButton->setToolTip("Erases the entire content of the disk cache."); + connect(clearCacheButton, SIGNAL(clicked()), SLOT(clear())); + layout->addWidget(clearCacheButton, 3, 3); +} + +void DiskCacheEditor::refresh() { + DependencyManager::get()->cacheInfoRequest(this, "cacheInfoCallback"); +} + +void DiskCacheEditor::cacheInfoCallback(QString cacheDirectory, qint64 cacheSize, qint64 maximumCacheSize) { + static const auto stringify = [](qint64 number) { + static const QStringList UNITS = QStringList() << "B" << "KB" << "MB" << "GB"; + static const qint64 CHUNK = 1024; + QString unit; + int i = 0; + for (i = 0; i < 4; ++i) { + if (number / CHUNK > 0) { + number /= CHUNK; + } else { + break; + } + } + return QString("%0 %1").arg(number).arg(UNITS[i]); + }; + + if (_path) { + _path->setText(cacheDirectory); + } + if (_size) { + _size->setText(stringify(cacheSize)); + } + if (_maxSize) { + _maxSize->setText(stringify(maximumCacheSize)); + } +} + +void DiskCacheEditor::clear() { + auto buttonClicked = OffscreenUi::question(_dialog, "Clearing disk cache", + "You are about to erase all the content of the disk cache, " + "are you sure you want to do that?", + QMessageBox::Ok | QMessageBox::Cancel); + if (buttonClicked == QMessageBox::Ok) { + DependencyManager::get()->clearCache(); + } +} diff --git a/interface/src/ui/DiskCacheEditor.h b/interface/src/ui/DiskCacheEditor.h new file mode 100644 index 0000000000..3f8fa1a883 --- /dev/null +++ b/interface/src/ui/DiskCacheEditor.h @@ -0,0 +1,49 @@ +// +// DiskCacheEditor.h +// +// +// Created by Clement on 3/4/15. +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_DiskCacheEditor_h +#define hifi_DiskCacheEditor_h + +#include +#include + +class QDialog; +class QLabel; +class QWindow; +class QTimer; + +class DiskCacheEditor : public QObject { + Q_OBJECT + +public: + DiskCacheEditor(QWidget* parent = nullptr); + + QWindow* windowHandle(); + +public slots: + void toggle(); + +private slots: + void refresh(); + void cacheInfoCallback(QString cacheDirectory, qint64 cacheSize, qint64 maximumCacheSize); + void clear(); + +private: + void makeDialog(); + + QPointer _dialog; + QPointer _path; + QPointer _size; + QPointer _maxSize; + QPointer _refreshTimer; +}; + +#endif // hifi_DiskCacheEditor_h \ No newline at end of file diff --git a/interface/src/ui/ScriptEditBox.cpp b/interface/src/ui/ScriptEditBox.cpp new file mode 100644 index 0000000000..2aea225b17 --- /dev/null +++ b/interface/src/ui/ScriptEditBox.cpp @@ -0,0 +1,111 @@ +// +// ScriptEditBox.cpp +// interface/src/ui +// +// Created by Thijs Wenker on 4/30/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 "ScriptEditBox.h" + +#include +#include + +#include "ScriptLineNumberArea.h" + +ScriptEditBox::ScriptEditBox(QWidget* parent) : + QPlainTextEdit(parent) +{ + _scriptLineNumberArea = new ScriptLineNumberArea(this); + + connect(this, &ScriptEditBox::blockCountChanged, this, &ScriptEditBox::updateLineNumberAreaWidth); + connect(this, &ScriptEditBox::updateRequest, this, &ScriptEditBox::updateLineNumberArea); + connect(this, &ScriptEditBox::cursorPositionChanged, this, &ScriptEditBox::highlightCurrentLine); + + updateLineNumberAreaWidth(0); + highlightCurrentLine(); +} + +int ScriptEditBox::lineNumberAreaWidth() { + int digits = 1; + const int SPACER_PIXELS = 3; + const int BASE_TEN = 10; + int max = qMax(1, blockCount()); + while (max >= BASE_TEN) { + max /= BASE_TEN; + digits++; + } + return SPACER_PIXELS + fontMetrics().width(QLatin1Char('H')) * digits; +} + +void ScriptEditBox::updateLineNumberAreaWidth(int blockCount) { + setViewportMargins(lineNumberAreaWidth(), 0, 0, 0); +} + +void ScriptEditBox::updateLineNumberArea(const QRect& rect, int deltaY) { + if (deltaY) { + _scriptLineNumberArea->scroll(0, deltaY); + } else { + _scriptLineNumberArea->update(0, rect.y(), _scriptLineNumberArea->width(), rect.height()); + } + + if (rect.contains(viewport()->rect())) { + updateLineNumberAreaWidth(0); + } +} + +void ScriptEditBox::resizeEvent(QResizeEvent* event) { + QPlainTextEdit::resizeEvent(event); + + QRect localContentsRect = contentsRect(); + _scriptLineNumberArea->setGeometry(QRect(localContentsRect.left(), localContentsRect.top(), lineNumberAreaWidth(), + localContentsRect.height())); +} + +void ScriptEditBox::highlightCurrentLine() { + QList extraSelections; + + if (!isReadOnly()) { + QTextEdit::ExtraSelection selection; + + QColor lineColor = QColor(Qt::gray).lighter(); + + selection.format.setBackground(lineColor); + selection.format.setProperty(QTextFormat::FullWidthSelection, true); + selection.cursor = textCursor(); + selection.cursor.clearSelection(); + extraSelections.append(selection); + } + + setExtraSelections(extraSelections); +} + +void ScriptEditBox::lineNumberAreaPaintEvent(QPaintEvent* event) +{ + QPainter painter(_scriptLineNumberArea); + painter.fillRect(event->rect(), Qt::lightGray); + QTextBlock block = firstVisibleBlock(); + int blockNumber = block.blockNumber(); + int top = (int) blockBoundingGeometry(block).translated(contentOffset()).top(); + int bottom = top + (int) blockBoundingRect(block).height(); + + while (block.isValid() && top <= event->rect().bottom()) { + if (block.isVisible() && bottom >= event->rect().top()) { + QFont font = painter.font(); + font.setBold(this->textCursor().blockNumber() == block.blockNumber()); + painter.setFont(font); + QString number = QString::number(blockNumber + 1); + painter.setPen(Qt::black); + painter.drawText(0, top, _scriptLineNumberArea->width(), fontMetrics().height(), + Qt::AlignRight, number); + } + + block = block.next(); + top = bottom; + bottom = top + (int) blockBoundingRect(block).height(); + blockNumber++; + } +} diff --git a/interface/src/ui/ScriptEditBox.h b/interface/src/ui/ScriptEditBox.h new file mode 100644 index 0000000000..0b037db16a --- /dev/null +++ b/interface/src/ui/ScriptEditBox.h @@ -0,0 +1,38 @@ +// +// ScriptEditBox.h +// interface/src/ui +// +// Created by Thijs Wenker on 4/30/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_ScriptEditBox_h +#define hifi_ScriptEditBox_h + +#include + +class ScriptEditBox : public QPlainTextEdit { + Q_OBJECT + +public: + ScriptEditBox(QWidget* parent = NULL); + + void lineNumberAreaPaintEvent(QPaintEvent* event); + int lineNumberAreaWidth(); + +protected: + void resizeEvent(QResizeEvent* event) override; + +private slots: + void updateLineNumberAreaWidth(int blockCount); + void highlightCurrentLine(); + void updateLineNumberArea(const QRect& rect, int deltaY); + +private: + QWidget* _scriptLineNumberArea; +}; + +#endif // hifi_ScriptEditBox_h diff --git a/interface/src/ui/ScriptEditorWidget.cpp b/interface/src/ui/ScriptEditorWidget.cpp new file mode 100644 index 0000000000..ada6b11355 --- /dev/null +++ b/interface/src/ui/ScriptEditorWidget.cpp @@ -0,0 +1,256 @@ +// +// ScriptEditorWidget.cpp +// interface/src/ui +// +// Created by Thijs Wenker on 4/14/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 "ui_scriptEditorWidget.h" +#include "ScriptEditorWidget.h" +#include "ScriptEditorWindow.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "Application.h" +#include "ScriptHighlighting.h" + +ScriptEditorWidget::ScriptEditorWidget() : + _scriptEditorWidgetUI(new Ui::ScriptEditorWidget), + _scriptEngine(NULL), + _isRestarting(false), + _isReloading(false) +{ + setAttribute(Qt::WA_DeleteOnClose); + + _scriptEditorWidgetUI->setupUi(this); + + connect(_scriptEditorWidgetUI->scriptEdit->document(), &QTextDocument::modificationChanged, this, + &ScriptEditorWidget::scriptModified); + connect(_scriptEditorWidgetUI->scriptEdit->document(), &QTextDocument::contentsChanged, this, + &ScriptEditorWidget::onScriptModified); + + // remove the title bar (see the Qt docs on setTitleBarWidget) + setTitleBarWidget(new QWidget()); + QFontMetrics fm(_scriptEditorWidgetUI->scriptEdit->font()); + _scriptEditorWidgetUI->scriptEdit->setTabStopWidth(fm.width('0') * 4); + // We create a new ScriptHighligting QObject and provide it with a parent so this is NOT a memory leak. + new ScriptHighlighting(_scriptEditorWidgetUI->scriptEdit->document()); + QTimer::singleShot(0, _scriptEditorWidgetUI->scriptEdit, SLOT(setFocus())); + + _console = new JSConsole(this); + _console->setFixedHeight(CONSOLE_HEIGHT); + _scriptEditorWidgetUI->verticalLayout->addWidget(_console); + connect(_scriptEditorWidgetUI->clearButton, &QPushButton::clicked, _console, &JSConsole::clear); +} + +ScriptEditorWidget::~ScriptEditorWidget() { + delete _scriptEditorWidgetUI; + delete _console; +} + +void ScriptEditorWidget::onScriptModified() { + if(_scriptEditorWidgetUI->onTheFlyCheckBox->isChecked() && isModified() && isRunning() && !_isReloading) { + _isRestarting = true; + setRunning(false); + // Script is restarted once current script instance finishes. + } +} + +void ScriptEditorWidget::onScriptFinished(const QString& scriptPath) { + _scriptEngine = NULL; + _console->setScriptEngine(NULL); + if (_isRestarting) { + _isRestarting = false; + setRunning(true); + } +} + +bool ScriptEditorWidget::isModified() { + return _scriptEditorWidgetUI->scriptEdit->document()->isModified(); +} + +bool ScriptEditorWidget::isRunning() { + return (_scriptEngine != NULL) ? _scriptEngine->isRunning() : false; +} + +bool ScriptEditorWidget::setRunning(bool run) { + if (run && isModified() && !save()) { + return false; + } + + if (_scriptEngine != NULL) { + disconnect(_scriptEngine, &ScriptEngine::runningStateChanged, this, &ScriptEditorWidget::runningStateChanged); + disconnect(_scriptEngine, &ScriptEngine::update, this, &ScriptEditorWidget::onScriptModified); + disconnect(_scriptEngine, &ScriptEngine::finished, this, &ScriptEditorWidget::onScriptFinished); + } + + auto scriptEngines = DependencyManager::get(); + if (run) { + const QString& scriptURLString = QUrl(_currentScript).toString(); + // Reload script so that an out of date copy is not retrieved from the cache + _scriptEngine = scriptEngines->loadScript(scriptURLString, true, true, false, true); + connect(_scriptEngine, &ScriptEngine::runningStateChanged, this, &ScriptEditorWidget::runningStateChanged); + connect(_scriptEngine, &ScriptEngine::update, this, &ScriptEditorWidget::onScriptModified); + connect(_scriptEngine, &ScriptEngine::finished, this, &ScriptEditorWidget::onScriptFinished); + } else { + connect(_scriptEngine, &ScriptEngine::finished, this, &ScriptEditorWidget::onScriptFinished); + const QString& scriptURLString = QUrl(_currentScript).toString(); + scriptEngines->stopScript(scriptURLString); + _scriptEngine = NULL; + } + _console->setScriptEngine(_scriptEngine); + return true; +} + +bool ScriptEditorWidget::saveFile(const QString &scriptPath) { + QFile file(scriptPath); + if (!file.open(QFile::WriteOnly | QFile::Text)) { + OffscreenUi::warning(this, tr("Interface"), tr("Cannot write script %1:\n%2.").arg(scriptPath) + .arg(file.errorString())); + return false; + } + + QTextStream out(&file); + out << _scriptEditorWidgetUI->scriptEdit->toPlainText(); + file.close(); + + setScriptFile(scriptPath); + return true; +} + +void ScriptEditorWidget::loadFile(const QString& scriptPath) { + QUrl url(scriptPath); + + // if the scheme length is one or lower, maybe they typed in a file, let's try + const int WINDOWS_DRIVE_LETTER_SIZE = 1; + if (url.scheme().size() <= WINDOWS_DRIVE_LETTER_SIZE) { + QFile file(scriptPath); + if (!file.open(QFile::ReadOnly | QFile::Text)) { + OffscreenUi::warning(this, tr("Interface"), tr("Cannot read script %1:\n%2.").arg(scriptPath) + .arg(file.errorString())); + return; + } + QTextStream in(&file); + _scriptEditorWidgetUI->scriptEdit->setPlainText(in.readAll()); + file.close(); + setScriptFile(scriptPath); + + if (_scriptEngine != NULL) { + disconnect(_scriptEngine, &ScriptEngine::runningStateChanged, this, &ScriptEditorWidget::runningStateChanged); + disconnect(_scriptEngine, &ScriptEngine::update, this, &ScriptEditorWidget::onScriptModified); + disconnect(_scriptEngine, &ScriptEngine::finished, this, &ScriptEditorWidget::onScriptFinished); + } + } else { + QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); + QNetworkRequest networkRequest = QNetworkRequest(url); + networkRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); + networkRequest.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT); + QNetworkReply* reply = networkAccessManager.get(networkRequest); + qDebug() << "Downloading included script at" << scriptPath; + QEventLoop loop; + QObject::connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit); + loop.exec(); + _scriptEditorWidgetUI->scriptEdit->setPlainText(reply->readAll()); + delete reply; + + if (!saveAs()) { + static_cast(this->parent()->parent()->parent())->terminateCurrentTab(); + } + } + const QString& scriptURLString = QUrl(_currentScript).toString(); + _scriptEngine = DependencyManager::get()->getScriptEngine(scriptURLString); + if (_scriptEngine != NULL) { + connect(_scriptEngine, &ScriptEngine::runningStateChanged, this, &ScriptEditorWidget::runningStateChanged); + connect(_scriptEngine, &ScriptEngine::update, this, &ScriptEditorWidget::onScriptModified); + connect(_scriptEngine, &ScriptEngine::finished, this, &ScriptEditorWidget::onScriptFinished); + } + _console->setScriptEngine(_scriptEngine); +} + +bool ScriptEditorWidget::save() { + return _currentScript.isEmpty() ? saveAs() : saveFile(_currentScript); +} + +bool ScriptEditorWidget::saveAs() { + auto scriptEngines = DependencyManager::get(); + QString fileName = QFileDialog::getSaveFileName(this, tr("Save script"), + qApp->getPreviousScriptLocation(), + tr("JavaScript Files (*.js)")); + if (!fileName.isEmpty()) { + qApp->setPreviousScriptLocation(fileName); + return saveFile(fileName); + } else { + return false; + } +} + +void ScriptEditorWidget::setScriptFile(const QString& scriptPath) { + _currentScript = scriptPath; + _currentScriptModified = QFileInfo(_currentScript).lastModified(); + _scriptEditorWidgetUI->scriptEdit->document()->setModified(false); + setWindowModified(false); + + emit scriptnameChanged(); +} + +bool ScriptEditorWidget::questionSave() { + if (_scriptEditorWidgetUI->scriptEdit->document()->isModified()) { + QMessageBox::StandardButton button = OffscreenUi::warning(this, tr("Interface"), + tr("The script has been modified.\nDo you want to save your changes?"), QMessageBox::Save | QMessageBox::Discard | + QMessageBox::Cancel, QMessageBox::Save); + return button == QMessageBox::Save ? save() : (button == QMessageBox::Discard); + } + return true; +} + +void ScriptEditorWidget::onWindowActivated() { + if (!_isReloading) { + _isReloading = true; + + QDateTime fileStamp = QFileInfo(_currentScript).lastModified(); + if (fileStamp > _currentScriptModified) { + bool doReload = false; + auto window = static_cast(this->parent()->parent()->parent()); + window->inModalDialog = true; + if (window->autoReloadScripts() + || OffscreenUi::question(this, tr("Reload Script"), + tr("The following file has been modified outside of the Interface editor:") + "\n" + _currentScript + "\n" + + (isModified() + ? tr("Do you want to reload it and lose the changes you've made in the Interface editor?") + : tr("Do you want to reload it?")), + QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) { + doReload = true; + } + window->inModalDialog = false; + if (doReload) { + loadFile(_currentScript); + if (_scriptEditorWidgetUI->onTheFlyCheckBox->isChecked() && isRunning()) { + _isRestarting = true; + setRunning(false); + // Script is restarted once current script instance finishes. + } + } else { + _currentScriptModified = fileStamp; // Asked and answered. Don't ask again until the external file is changed again. + } + } + _isReloading = false; + } +} diff --git a/interface/src/ui/ScriptEditorWidget.h b/interface/src/ui/ScriptEditorWidget.h new file mode 100644 index 0000000000..f53fd7b718 --- /dev/null +++ b/interface/src/ui/ScriptEditorWidget.h @@ -0,0 +1,64 @@ +// +// ScriptEditorWidget.h +// interface/src/ui +// +// Created by Thijs Wenker on 4/14/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_ScriptEditorWidget_h +#define hifi_ScriptEditorWidget_h + +#include + +#include "JSConsole.h" +#include "ScriptEngine.h" + +namespace Ui { + class ScriptEditorWidget; +} + +class ScriptEditorWidget : public QDockWidget { + Q_OBJECT + +public: + ScriptEditorWidget(); + ~ScriptEditorWidget(); + + bool isModified(); + bool isRunning(); + bool setRunning(bool run); + bool saveFile(const QString& scriptPath); + void loadFile(const QString& scriptPath); + void setScriptFile(const QString& scriptPath); + bool save(); + bool saveAs(); + bool questionSave(); + const QString getScriptName() const { return _currentScript; }; + +signals: + void runningStateChanged(); + void scriptnameChanged(); + void scriptModified(); + +public slots: + void onWindowActivated(); + +private slots: + void onScriptModified(); + void onScriptFinished(const QString& scriptName); + +private: + JSConsole* _console; + Ui::ScriptEditorWidget* _scriptEditorWidgetUI; + ScriptEngine* _scriptEngine; + QString _currentScript; + QDateTime _currentScriptModified; + bool _isRestarting; + bool _isReloading; +}; + +#endif // hifi_ScriptEditorWidget_h diff --git a/interface/src/ui/ScriptEditorWindow.cpp b/interface/src/ui/ScriptEditorWindow.cpp new file mode 100644 index 0000000000..58abd23979 --- /dev/null +++ b/interface/src/ui/ScriptEditorWindow.cpp @@ -0,0 +1,259 @@ +// +// ScriptEditorWindow.cpp +// interface/src/ui +// +// Created by Thijs Wenker on 4/14/14. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include + +#include "ui_scriptEditorWindow.h" +#include "ScriptEditorWindow.h" +#include "ScriptEditorWidget.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "Application.h" +#include "PathUtils.h" + +ScriptEditorWindow::ScriptEditorWindow(QWidget* parent) : + QWidget(parent), + _ScriptEditorWindowUI(new Ui::ScriptEditorWindow), + _loadMenu(new QMenu), + _saveMenu(new QMenu) +{ + setAttribute(Qt::WA_DeleteOnClose); + + _ScriptEditorWindowUI->setupUi(this); + + this->setWindowFlags(Qt::Tool); + addScriptEditorWidget("New script"); + connect(_loadMenu, &QMenu::aboutToShow, this, &ScriptEditorWindow::loadMenuAboutToShow); + _ScriptEditorWindowUI->loadButton->setMenu(_loadMenu); + + _saveMenu->addAction("Save as..", this, SLOT(saveScriptAsClicked()), Qt::CTRL | Qt::SHIFT | Qt::Key_S); + + _ScriptEditorWindowUI->saveButton->setMenu(_saveMenu); + + connect(new QShortcut(QKeySequence("Ctrl+N"), this), &QShortcut::activated, this, &ScriptEditorWindow::newScriptClicked); + connect(new QShortcut(QKeySequence("Ctrl+S"), this), &QShortcut::activated, this,&ScriptEditorWindow::saveScriptClicked); + connect(new QShortcut(QKeySequence("Ctrl+O"), this), &QShortcut::activated, this, &ScriptEditorWindow::loadScriptClicked); + connect(new QShortcut(QKeySequence("F5"), this), &QShortcut::activated, this, &ScriptEditorWindow::toggleRunScriptClicked); + + _ScriptEditorWindowUI->loadButton->setIcon(QIcon(QPixmap(PathUtils::resourcesPath() + "icons/load-script.svg"))); + _ScriptEditorWindowUI->newButton->setIcon(QIcon(QPixmap(PathUtils::resourcesPath() + "icons/new-script.svg"))); + _ScriptEditorWindowUI->saveButton->setIcon(QIcon(QPixmap(PathUtils::resourcesPath() + "icons/save-script.svg"))); + _ScriptEditorWindowUI->toggleRunButton->setIcon(QIcon(QPixmap(PathUtils::resourcesPath() + "icons/start-script.svg"))); +} + +ScriptEditorWindow::~ScriptEditorWindow() { + delete _ScriptEditorWindowUI; +} + +void ScriptEditorWindow::setRunningState(bool run) { + if (_ScriptEditorWindowUI->tabWidget->currentIndex() != -1) { + static_cast(_ScriptEditorWindowUI->tabWidget->currentWidget())->setRunning(run); + } + this->updateButtons(); +} + +void ScriptEditorWindow::updateButtons() { + bool isRunning = _ScriptEditorWindowUI->tabWidget->currentIndex() != -1 && + static_cast(_ScriptEditorWindowUI->tabWidget->currentWidget())->isRunning(); + _ScriptEditorWindowUI->toggleRunButton->setEnabled(_ScriptEditorWindowUI->tabWidget->currentIndex() != -1); + _ScriptEditorWindowUI->toggleRunButton->setIcon(QIcon(QPixmap(PathUtils::resourcesPath() + ((isRunning ? + "icons/stop-script.svg" : "icons/start-script.svg"))))); +} + +void ScriptEditorWindow::loadScriptMenu(const QString& scriptName) { + addScriptEditorWidget("loading...")->loadFile(scriptName); + updateButtons(); +} + +void ScriptEditorWindow::loadScriptClicked() { + QString scriptName = QFileDialog::getOpenFileName(this, tr("Interface"), + qApp->getPreviousScriptLocation(), + tr("JavaScript Files (*.js)")); + if (!scriptName.isEmpty()) { + qApp->setPreviousScriptLocation(scriptName); + addScriptEditorWidget("loading...")->loadFile(scriptName); + updateButtons(); + } +} + +void ScriptEditorWindow::loadMenuAboutToShow() { + _loadMenu->clear(); + QStringList runningScripts = DependencyManager::get()->getRunningScripts(); + if (runningScripts.count() > 0) { + QSignalMapper* signalMapper = new QSignalMapper(this); + foreach (const QString& runningScript, runningScripts) { + QAction* runningScriptAction = new QAction(runningScript, _loadMenu); + connect(runningScriptAction, SIGNAL(triggered()), signalMapper, SLOT(map())); + signalMapper->setMapping(runningScriptAction, runningScript); + _loadMenu->addAction(runningScriptAction); + } + connect(signalMapper, SIGNAL(mapped(const QString &)), this, SLOT(loadScriptMenu(const QString&))); + } else { + QAction* naAction = new QAction("(no running scripts)", _loadMenu); + naAction->setDisabled(true); + _loadMenu->addAction(naAction); + } +} + +void ScriptEditorWindow::newScriptClicked() { + addScriptEditorWidget(QString("New script")); +} + +void ScriptEditorWindow::toggleRunScriptClicked() { + this->setRunningState(!(_ScriptEditorWindowUI->tabWidget->currentIndex() !=-1 + && static_cast(_ScriptEditorWindowUI->tabWidget->currentWidget())->isRunning())); +} + +void ScriptEditorWindow::saveScriptClicked() { + if (_ScriptEditorWindowUI->tabWidget->currentIndex() != -1) { + ScriptEditorWidget* currentScriptWidget = static_cast(_ScriptEditorWindowUI->tabWidget + ->currentWidget()); + currentScriptWidget->save(); + } +} + +void ScriptEditorWindow::saveScriptAsClicked() { + if (_ScriptEditorWindowUI->tabWidget->currentIndex() != -1) { + ScriptEditorWidget* currentScriptWidget = static_cast(_ScriptEditorWindowUI->tabWidget + ->currentWidget()); + currentScriptWidget->saveAs(); + } +} + +ScriptEditorWidget* ScriptEditorWindow::addScriptEditorWidget(QString title) { + ScriptEditorWidget* newScriptEditorWidget = new ScriptEditorWidget(); + connect(newScriptEditorWidget, &ScriptEditorWidget::scriptnameChanged, this, &ScriptEditorWindow::updateScriptNameOrStatus); + connect(newScriptEditorWidget, &ScriptEditorWidget::scriptModified, this, &ScriptEditorWindow::updateScriptNameOrStatus); + connect(newScriptEditorWidget, &ScriptEditorWidget::runningStateChanged, this, &ScriptEditorWindow::updateButtons); + connect(this, &ScriptEditorWindow::windowActivated, newScriptEditorWidget, &ScriptEditorWidget::onWindowActivated); + _ScriptEditorWindowUI->tabWidget->addTab(newScriptEditorWidget, title); + _ScriptEditorWindowUI->tabWidget->setCurrentWidget(newScriptEditorWidget); + newScriptEditorWidget->setUpdatesEnabled(true); + newScriptEditorWidget->adjustSize(); + return newScriptEditorWidget; +} + +void ScriptEditorWindow::tabSwitched(int tabIndex) { + this->updateButtons(); + if (_ScriptEditorWindowUI->tabWidget->currentIndex() != -1) { + ScriptEditorWidget* currentScriptWidget = static_cast(_ScriptEditorWindowUI->tabWidget + ->currentWidget()); + QString modifiedStar = (currentScriptWidget->isModified() ? "*" : ""); + if (currentScriptWidget->getScriptName().length() > 0) { + this->setWindowTitle("Script Editor [" + currentScriptWidget->getScriptName() + modifiedStar + "]"); + } else { + this->setWindowTitle("Script Editor [New script" + modifiedStar + "]"); + } + } else { + this->setWindowTitle("Script Editor"); + } +} + +void ScriptEditorWindow::tabCloseRequested(int tabIndex) { + if (ignoreCloseForModal(nullptr)) { + return; + } + ScriptEditorWidget* closingScriptWidget = static_cast(_ScriptEditorWindowUI->tabWidget + ->widget(tabIndex)); + if(closingScriptWidget->questionSave()) { + _ScriptEditorWindowUI->tabWidget->removeTab(tabIndex); + } +} + +// If this operating system window causes a qml overlay modal dialog (which might not even be seen by the user), closing this window +// will crash the code that was waiting on the dialog result. So that code whousl set inModalDialog to true while the question is up. +// This code will not be necessary when switch out all operating system windows for qml overlays. +bool ScriptEditorWindow::ignoreCloseForModal(QCloseEvent* event) { + if (!inModalDialog) { + return false; + } + // Deliberately not using OffscreenUi, so that the dialog is seen. + QMessageBox::information(this, tr("Interface"), tr("There is a modal dialog that must be answered before closing."), + QMessageBox::Discard, QMessageBox::Discard); + if (event) { + event->ignore(); // don't close + } + return true; +} + +void ScriptEditorWindow::closeEvent(QCloseEvent *event) { + if (ignoreCloseForModal(event)) { + return; + } + bool unsaved_docs_warning = false; + for (int i = 0; i < _ScriptEditorWindowUI->tabWidget->count(); i++){ + if(static_cast(_ScriptEditorWindowUI->tabWidget->widget(i))->isModified()){ + unsaved_docs_warning = true; + break; + } + } + + if (!unsaved_docs_warning || QMessageBox::warning(this, tr("Interface"), + tr("There are some unsaved scripts, are you sure you want to close the editor? Changes will be lost!"), + QMessageBox::Discard | QMessageBox::Cancel, QMessageBox::Cancel) == QMessageBox::Discard) { + event->accept(); + } else { + event->ignore(); + } +} + +void ScriptEditorWindow::updateScriptNameOrStatus() { + ScriptEditorWidget* source = static_cast(QObject::sender()); + QString modifiedStar = (source->isModified()? "*" : ""); + if (source->getScriptName().length() > 0) { + for (int i = 0; i < _ScriptEditorWindowUI->tabWidget->count(); i++){ + if (_ScriptEditorWindowUI->tabWidget->widget(i) == source) { + _ScriptEditorWindowUI->tabWidget->setTabText(i, modifiedStar + QFileInfo(source->getScriptName()).fileName()); + _ScriptEditorWindowUI->tabWidget->setTabToolTip(i, source->getScriptName()); + } + } + } + + if (_ScriptEditorWindowUI->tabWidget->currentWidget() == source) { + if (source->getScriptName().length() > 0) { + this->setWindowTitle("Script Editor [" + source->getScriptName() + modifiedStar + "]"); + } else { + this->setWindowTitle("Script Editor [New script" + modifiedStar + "]"); + } + } +} + +void ScriptEditorWindow::terminateCurrentTab() { + if (_ScriptEditorWindowUI->tabWidget->currentIndex() != -1) { + _ScriptEditorWindowUI->tabWidget->removeTab(_ScriptEditorWindowUI->tabWidget->currentIndex()); + this->raise(); + } +} + +bool ScriptEditorWindow::autoReloadScripts() { + return _ScriptEditorWindowUI->autoReloadCheckBox->isChecked(); +} + +bool ScriptEditorWindow::event(QEvent* event) { + if (event->type() == QEvent::WindowActivate) { + emit windowActivated(); + } + return QWidget::event(event); +} + diff --git a/interface/src/ui/ScriptEditorWindow.h b/interface/src/ui/ScriptEditorWindow.h new file mode 100644 index 0000000000..af9863d136 --- /dev/null +++ b/interface/src/ui/ScriptEditorWindow.h @@ -0,0 +1,64 @@ +// +// ScriptEditorWindow.h +// interface/src/ui +// +// Created by Thijs Wenker on 4/14/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_ScriptEditorWindow_h +#define hifi_ScriptEditorWindow_h + +#include "ScriptEditorWidget.h" + +namespace Ui { + class ScriptEditorWindow; +} + +class ScriptEditorWindow : public QWidget { + Q_OBJECT + +public: + ScriptEditorWindow(QWidget* parent = nullptr); + ~ScriptEditorWindow(); + + void terminateCurrentTab(); + bool autoReloadScripts(); + + bool inModalDialog { false }; + bool ignoreCloseForModal(QCloseEvent* event); + +signals: + void windowActivated(); + +protected: + void closeEvent(QCloseEvent* event) override; + virtual bool event(QEvent* event) override; + +private: + Ui::ScriptEditorWindow* _ScriptEditorWindowUI; + QMenu* _loadMenu; + QMenu* _saveMenu; + + ScriptEditorWidget* addScriptEditorWidget(QString title); + void setRunningState(bool run); + void setScriptName(const QString& scriptName); + +private slots: + void loadScriptMenu(const QString& scriptName); + void loadScriptClicked(); + void newScriptClicked(); + void toggleRunScriptClicked(); + void saveScriptClicked(); + void saveScriptAsClicked(); + void loadMenuAboutToShow(); + void tabSwitched(int tabIndex); + void tabCloseRequested(int tabIndex); + void updateScriptNameOrStatus(); + void updateButtons(); +}; + +#endif // hifi_ScriptEditorWindow_h diff --git a/interface/src/ui/ScriptLineNumberArea.cpp b/interface/src/ui/ScriptLineNumberArea.cpp new file mode 100644 index 0000000000..6d7e9185ea --- /dev/null +++ b/interface/src/ui/ScriptLineNumberArea.cpp @@ -0,0 +1,28 @@ +// +// ScriptLineNumberArea.cpp +// interface/src/ui +// +// Created by Thijs Wenker on 4/30/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 "ScriptLineNumberArea.h" + +#include "ScriptEditBox.h" + +ScriptLineNumberArea::ScriptLineNumberArea(ScriptEditBox* scriptEditBox) : + QWidget(scriptEditBox) +{ + _scriptEditBox = scriptEditBox; +} + +QSize ScriptLineNumberArea::sizeHint() const { + return QSize(_scriptEditBox->lineNumberAreaWidth(), 0); +} + +void ScriptLineNumberArea::paintEvent(QPaintEvent* event) { + _scriptEditBox->lineNumberAreaPaintEvent(event); +} diff --git a/interface/src/ui/ScriptLineNumberArea.h b/interface/src/ui/ScriptLineNumberArea.h new file mode 100644 index 0000000000..77de8244ce --- /dev/null +++ b/interface/src/ui/ScriptLineNumberArea.h @@ -0,0 +1,32 @@ +// +// ScriptLineNumberArea.h +// interface/src/ui +// +// Created by Thijs Wenker on 4/30/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_ScriptLineNumberArea_h +#define hifi_ScriptLineNumberArea_h + +#include + +class ScriptEditBox; + +class ScriptLineNumberArea : public QWidget { + +public: + ScriptLineNumberArea(ScriptEditBox* scriptEditBox); + QSize sizeHint() const override; + +protected: + void paintEvent(QPaintEvent* event) override; + +private: + ScriptEditBox* _scriptEditBox; +}; + +#endif // hifi_ScriptLineNumberArea_h diff --git a/interface/src/ui/ScriptsTableWidget.cpp b/interface/src/ui/ScriptsTableWidget.cpp new file mode 100644 index 0000000000..7b4f9e6b1f --- /dev/null +++ b/interface/src/ui/ScriptsTableWidget.cpp @@ -0,0 +1,49 @@ +// +// ScriptsTableWidget.cpp +// interface +// +// Created by Mohammed Nafees on 04/03/2014. +// Copyright (c) 2014 High Fidelity, Inc. All rights reserved. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include +#include +#include +#include + +#include "ScriptsTableWidget.h" + +ScriptsTableWidget::ScriptsTableWidget(QWidget* parent) : + QTableWidget(parent) { + verticalHeader()->setVisible(false); + horizontalHeader()->setVisible(false); + setShowGrid(false); + setSelectionMode(QAbstractItemView::NoSelection); + setEditTriggers(QAbstractItemView::NoEditTriggers); + setStyleSheet("QTableWidget { border: none; background: transparent; color: #333333; } QToolTip { color: #000000; background: #f9f6e4; padding: 2px; }"); + setToolTipDuration(200); + setWordWrap(true); + setGeometry(0, 0, parent->width(), parent->height()); +} + +void ScriptsTableWidget::paintEvent(QPaintEvent* event) { + QPainter painter(viewport()); + painter.setPen(QColor::fromRgb(225, 225, 225)); // #e1e1e1 + + int y = 0; + for (int i = 0; i < rowCount(); i++) { + painter.drawLine(5, rowHeight(i) + y, width(), rowHeight(i) + y); + y += rowHeight(i); + } + painter.end(); + + QTableWidget::paintEvent(event); +} + +void ScriptsTableWidget::keyPressEvent(QKeyEvent* event) { + // Ignore keys so they will propagate correctly + event->ignore(); +} diff --git a/interface/src/ui/ScriptsTableWidget.h b/interface/src/ui/ScriptsTableWidget.h new file mode 100644 index 0000000000..f5e3407e97 --- /dev/null +++ b/interface/src/ui/ScriptsTableWidget.h @@ -0,0 +1,28 @@ +// +// ScriptsTableWidget.h +// interface +// +// Created by Mohammed Nafees on 04/03/2014. +// Copyright (c) 2014 High Fidelity, Inc. All rights reserved. +// +// 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__ScriptsTableWidget_h +#define hifi__ScriptsTableWidget_h + +#include +#include + +class ScriptsTableWidget : public QTableWidget { + Q_OBJECT +public: + explicit ScriptsTableWidget(QWidget* parent); + +protected: + virtual void paintEvent(QPaintEvent* event) override; + virtual void keyPressEvent(QKeyEvent* event) override; +}; + +#endif // hifi__ScriptsTableWidget_h diff --git a/interface/src/ui/Stats.cpp b/interface/src/ui/Stats.cpp index cedcb923d9..923d9f642d 100644 --- a/interface/src/ui/Stats.cpp +++ b/interface/src/ui/Stats.cpp @@ -38,8 +38,6 @@ using namespace std; static Stats* INSTANCE{ nullptr }; -QString getTextureMemoryPressureModeString(); - Stats* Stats::getInstance() { if (!INSTANCE) { Stats::registerType(); @@ -222,10 +220,10 @@ void Stats::updateStats(bool force) { STAT_UPDATE(audioMixerInPps, roundf(bandwidthRecorder->getAverageInputPacketsPerSecond(NodeType::AudioMixer))); STAT_UPDATE(audioMixerOutKbps, roundf(bandwidthRecorder->getAverageOutputKilobitsPerSecond(NodeType::AudioMixer))); STAT_UPDATE(audioMixerOutPps, roundf(bandwidthRecorder->getAverageOutputPacketsPerSecond(NodeType::AudioMixer))); + STAT_UPDATE(audioMicOutboundPPS, audioClient->getMicAudioOutboundPPS()); + STAT_UPDATE(audioSilentOutboundPPS, audioClient->getSilentOutboundPPS()); STAT_UPDATE(audioAudioInboundPPS, audioClient->getAudioInboundPPS()); STAT_UPDATE(audioSilentInboundPPS, audioClient->getSilentInboundPPS()); - STAT_UPDATE(audioOutboundPPS, audioClient->getAudioOutboundPPS()); - STAT_UPDATE(audioSilentOutboundPPS, audioClient->getSilentOutboundPPS()); } else { STAT_UPDATE(audioMixerKbps, -1); STAT_UPDATE(audioMixerPps, -1); @@ -233,7 +231,7 @@ void Stats::updateStats(bool force) { STAT_UPDATE(audioMixerInPps, -1); STAT_UPDATE(audioMixerOutKbps, -1); STAT_UPDATE(audioMixerOutPps, -1); - STAT_UPDATE(audioOutboundPPS, -1); + STAT_UPDATE(audioMicOutboundPPS, -1); STAT_UPDATE(audioSilentOutboundPPS, -1); STAT_UPDATE(audioAudioInboundPPS, -1); STAT_UPDATE(audioSilentInboundPPS, -1); @@ -342,12 +340,10 @@ void Stats::updateStats(bool force) { STAT_UPDATE(glContextSwapchainMemory, (int)BYTES_TO_MB(gl::Context::getSwapchainMemoryUsage())); STAT_UPDATE(qmlTextureMemory, (int)BYTES_TO_MB(OffscreenQmlSurface::getUsedTextureMemory())); - STAT_UPDATE(texturePendingTransfers, (int)BYTES_TO_MB(gpu::Texture::getTextureTransferPendingSize())); STAT_UPDATE(gpuTextureMemory, (int)BYTES_TO_MB(gpu::Texture::getTextureGPUMemoryUsage())); STAT_UPDATE(gpuTextureVirtualMemory, (int)BYTES_TO_MB(gpu::Texture::getTextureGPUVirtualMemoryUsage())); STAT_UPDATE(gpuTextureFramebufferMemory, (int)BYTES_TO_MB(gpu::Texture::getTextureGPUFramebufferMemoryUsage())); STAT_UPDATE(gpuTextureSparseMemory, (int)BYTES_TO_MB(gpu::Texture::getTextureGPUSparseMemoryUsage())); - STAT_UPDATE(gpuTextureMemoryPressureState, getTextureMemoryPressureModeString()); STAT_UPDATE(gpuSparseTextureEnabled, gpuContext->getBackend()->isTextureManagementSparseEnabled() ? 1 : 0); STAT_UPDATE(gpuFreeMemory, (int)BYTES_TO_MB(gpu::Context::getFreeGPUMemory())); STAT_UPDATE(rectifiedTextureCount, (int)RECTIFIED_TEXTURE_COUNT.load()); diff --git a/interface/src/ui/Stats.h b/interface/src/ui/Stats.h index a93a255a06..0ce113e0a0 100644 --- a/interface/src/ui/Stats.h +++ b/interface/src/ui/Stats.h @@ -77,7 +77,7 @@ class Stats : public QQuickItem { STATS_PROPERTY(int, audioMixerOutPps, 0) STATS_PROPERTY(int, audioMixerKbps, 0) STATS_PROPERTY(int, audioMixerPps, 0) - STATS_PROPERTY(int, audioOutboundPPS, 0) + STATS_PROPERTY(int, audioMicOutboundPPS, 0) STATS_PROPERTY(int, audioSilentOutboundPPS, 0) STATS_PROPERTY(int, audioAudioInboundPPS, 0) STATS_PROPERTY(int, audioSilentInboundPPS, 0) @@ -117,13 +117,11 @@ class Stats : public QQuickItem { STATS_PROPERTY(int, gpuTexturesSparse, 0) STATS_PROPERTY(int, glContextSwapchainMemory, 0) STATS_PROPERTY(int, qmlTextureMemory, 0) - STATS_PROPERTY(int, texturePendingTransfers, 0) STATS_PROPERTY(int, gpuTextureMemory, 0) STATS_PROPERTY(int, gpuTextureVirtualMemory, 0) STATS_PROPERTY(int, gpuTextureFramebufferMemory, 0) STATS_PROPERTY(int, gpuTextureSparseMemory, 0) STATS_PROPERTY(int, gpuSparseTextureEnabled, 0) - STATS_PROPERTY(QString, gpuTextureMemoryPressureState, QString()) STATS_PROPERTY(int, gpuFreeMemory, 0) STATS_PROPERTY(float, gpuFrameTime, 0) STATS_PROPERTY(float, batchFrameTime, 0) @@ -200,7 +198,7 @@ signals: void audioMixerOutPpsChanged(); void audioMixerKbpsChanged(); void audioMixerPpsChanged(); - void audioOutboundPPSChanged(); + void audioMicOutboundPPSChanged(); void audioSilentOutboundPPSChanged(); void audioAudioInboundPPSChanged(); void audioSilentInboundPPSChanged(); @@ -234,7 +232,6 @@ signals: void timingStatsChanged(); void glContextSwapchainMemoryChanged(); void qmlTextureMemoryChanged(); - void texturePendingTransfersChanged(); void gpuBuffersChanged(); void gpuBufferMemoryChanged(); void gpuTexturesChanged(); @@ -243,7 +240,6 @@ signals: void gpuTextureVirtualMemoryChanged(); void gpuTextureFramebufferMemoryChanged(); void gpuTextureSparseMemoryChanged(); - void gpuTextureMemoryPressureStateChanged(); void gpuSparseTextureEnabledChanged(); void gpuFreeMemoryChanged(); void gpuFrameTimeChanged(); diff --git a/interface/src/ui/overlays/Overlays.cpp b/interface/src/ui/overlays/Overlays.cpp index 6514052d26..f40dd522c4 100644 --- a/interface/src/ui/overlays/Overlays.cpp +++ b/interface/src/ui/overlays/Overlays.cpp @@ -431,9 +431,7 @@ RayToOverlayIntersectionResult Overlays::findRayIntersectionInternal(const PickR if (thisOverlay->findRayIntersectionExtraInfo(ray.origin, ray.direction, thisDistance, thisFace, thisSurfaceNormal, thisExtraInfo)) { bool isDrawInFront = thisOverlay->getDrawInFront(); - if ((bestIsFront && isDrawInFront && thisDistance < bestDistance) - || (!bestIsFront && (isDrawInFront || thisDistance < bestDistance))) { - + if (thisDistance < bestDistance && (!bestIsFront || isDrawInFront)) { bestIsFront = isDrawInFront; bestDistance = thisDistance; result.intersects = true; diff --git a/interface/src/ui/overlays/Web3DOverlay.cpp b/interface/src/ui/overlays/Web3DOverlay.cpp index 97e5344062..ba864d2c5c 100644 --- a/interface/src/ui/overlays/Web3DOverlay.cpp +++ b/interface/src/ui/overlays/Web3DOverlay.cpp @@ -270,7 +270,7 @@ void Web3DOverlay::render(RenderArgs* args) { if (!_texture) { auto webSurface = _webSurface; - _texture = gpu::TexturePointer(gpu::Texture::createExternal(OffscreenQmlSurface::getDiscardLambda())); + _texture = gpu::TexturePointer(gpu::Texture::createExternal2D(OffscreenQmlSurface::getDiscardLambda())); _texture->setSource(__FUNCTION__); } OffscreenQmlSurface::TextureAndFence newTextureAndFence; diff --git a/interface/ui/scriptEditorWidget.ui b/interface/ui/scriptEditorWidget.ui new file mode 100644 index 0000000000..e2e538a595 --- /dev/null +++ b/interface/ui/scriptEditorWidget.ui @@ -0,0 +1,142 @@ + + + ScriptEditorWidget + + + + 0 + 0 + 691 + 549 + + + + + 0 + 0 + + + + + 690 + 328 + + + + font-family: Helvetica, Arial, sans-serif; + + + QDockWidget::DockWidgetFloatable|QDockWidget::DockWidgetMovable + + + Qt::NoDockWidgetArea + + + Edit Script + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + Courier + -1 + 50 + false + false + + + + font: 16px "Courier"; + + + + + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + font: 13px "Helvetica","Arial","sans-serif"; + + + Debug Log: + + + + + + + + Helvetica,Arial,sans-serif + -1 + 50 + false + false + + + + font: 13px "Helvetica","Arial","sans-serif"; + + + Run on the fly (Careful: Any valid change made to the code will run immediately) + + + + + + + Clear + + + + 16 + 16 + + + + + + + + + + + + ScriptEditBox + QTextEdit +
ui/ScriptEditBox.h
+
+
+ +
diff --git a/interface/ui/scriptEditorWindow.ui b/interface/ui/scriptEditorWindow.ui new file mode 100644 index 0000000000..1e50aaef0b --- /dev/null +++ b/interface/ui/scriptEditorWindow.ui @@ -0,0 +1,324 @@ + + + ScriptEditorWindow + + + Qt::NonModal + + + + 0 + 0 + 780 + 717 + + + + + 400 + 250 + + + + Script Editor + + + font-family: Helvetica, Arial, sans-serif; + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 3 + + + QLayout::SetNoConstraint + + + 0 + + + 0 + + + + + New Script (Ctrl+N) + + + New + + + + 32 + 32 + + + + + + + + + 30 + 0 + + + + + 25 + 0 + + + + Load Script (Ctrl+O) + + + Load + + + + 32 + 32 + + + + false + + + QToolButton::MenuButtonPopup + + + Qt::ToolButtonIconOnly + + + + + + + + 30 + 0 + + + + + 32 + 0 + + + + Qt::NoFocus + + + Qt::NoContextMenu + + + Save Script (Ctrl+S) + + + Save + + + + 32 + 32 + + + + 316 + + + QToolButton::MenuButtonPopup + + + + + + + Toggle Run Script (F5) + + + Run/Stop + + + + 32 + 32 + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + font: 13px "Helvetica","Arial","sans-serif"; + + + Automatically reload externally changed files + + + + + + + + + true + + + + 250 + 80 + + + + QTabWidget::West + + + QTabWidget::Triangular + + + -1 + + + Qt::ElideNone + + + true + + + true + + + + + + + + + saveButton + clicked() + ScriptEditorWindow + saveScriptClicked() + + + 236 + 10 + + + 199 + 264 + + + + + toggleRunButton + clicked() + ScriptEditorWindow + toggleRunScriptClicked() + + + 330 + 10 + + + 199 + 264 + + + + + newButton + clicked() + ScriptEditorWindow + newScriptClicked() + + + 58 + 10 + + + 199 + 264 + + + + + loadButton + clicked() + ScriptEditorWindow + loadScriptClicked() + + + 85 + 10 + + + 199 + 264 + + + + + tabWidget + currentChanged(int) + ScriptEditorWindow + tabSwitched(int) + + + 352 + 360 + + + 352 + 340 + + + + + tabWidget + tabCloseRequested(int) + ScriptEditorWindow + tabCloseRequested(int) + + + 352 + 360 + + + 352 + 340 + + + + + diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 0520e5c5a1..9ecd0f6352 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -558,15 +558,15 @@ static const std::vector LATERAL_SPEEDS = { 0.2f, 0.65f }; // m/s void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPosition, const glm::vec3& worldVelocity, const glm::quat& worldRotation, CharacterControllerState ccState) { - glm::vec3 forward = worldRotation * IDENTITY_FORWARD; + glm::vec3 front = worldRotation * IDENTITY_FRONT; glm::vec3 workingVelocity = worldVelocity; { glm::vec3 localVel = glm::inverse(worldRotation) * workingVelocity; - float forwardSpeed = glm::dot(localVel, IDENTITY_FORWARD); + float forwardSpeed = glm::dot(localVel, IDENTITY_FRONT); float lateralSpeed = glm::dot(localVel, IDENTITY_RIGHT); - float turningSpeed = glm::orientedAngle(forward, _lastForward, IDENTITY_UP) / deltaTime; + float turningSpeed = glm::orientedAngle(front, _lastFront, IDENTITY_UP) / deltaTime; // filter speeds using a simple moving average. _averageForwardSpeed.updateAverage(forwardSpeed); @@ -852,7 +852,7 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos _lastEnableInverseKinematics = _enableInverseKinematics; } - _lastForward = forward; + _lastFront = front; _lastPosition = worldPosition; _lastVelocity = workingVelocity; } diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index 41cc5cabc6..b2cc877460 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -267,7 +267,7 @@ protected: int _rightElbowJointIndex { -1 }; int _rightShoulderJointIndex { -1 }; - glm::vec3 _lastForward; + glm::vec3 _lastFront; glm::vec3 _lastPosition; glm::vec3 _lastVelocity; diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp index 4a2de0a64b..c32b5600d9 100644 --- a/libraries/audio-client/src/AudioClient.cpp +++ b/libraries/audio-client/src/AudioClient.cpp @@ -160,7 +160,7 @@ AudioClient::AudioClient() : _loopbackAudioOutput(NULL), _loopbackOutputDevice(NULL), _inputRingBuffer(0), - _localInjectorsStream(0, 1), + _localInjectorsStream(0), _receivedAudioStream(RECEIVED_AUDIO_STREAM_CAPACITY_FRAMES), _isStereoInput(false), _outputStarveDetectionStartTimeMsec(0), @@ -184,6 +184,7 @@ AudioClient::AudioClient() : _outgoingAvatarAudioSequenceNumber(0), _audioOutputIODevice(_localInjectorsStream, _receivedAudioStream, this), _stats(&_receivedAudioStream), + _inputGate(), _positionGetter(DEFAULT_POSITION_GETTER), _orientationGetter(DEFAULT_ORIENTATION_GETTER) { // avoid putting a lock in the device callback @@ -970,87 +971,14 @@ void AudioClient::handleLocalEchoAndReverb(QByteArray& inputByteArray) { } } -void AudioClient::handleAudioInput(QByteArray& audioBuffer) { - if (_muted) { - _lastInputLoudness = 0.0f; - _timeSinceLastClip = 0.0f; - } else { - int16_t* samples = reinterpret_cast(audioBuffer.data()); - int numSamples = audioBuffer.size() / sizeof(AudioConstants::SAMPLE_SIZE); - bool didClip = false; - - bool shouldRemoveDCOffset = !_isPlayingBackRecording && !_isStereoInput; - if (shouldRemoveDCOffset) { - _noiseGate.removeDCOffset(samples, numSamples); - } - - bool shouldNoiseGate = (_isPlayingBackRecording || !_isStereoInput) && _isNoiseGateEnabled; - if (shouldNoiseGate) { - _noiseGate.gateSamples(samples, numSamples); - _lastInputLoudness = _noiseGate.getLastLoudness(); - didClip = _noiseGate.clippedInLastBlock(); - } else { - float loudness = 0.0f; - for (int i = 0; i < numSamples; ++i) { - int16_t sample = std::abs(samples[i]); - loudness += (float)sample; - didClip = didClip || - (sample > (AudioConstants::MAX_SAMPLE_VALUE * AudioNoiseGate::CLIPPING_THRESHOLD)); - } - _lastInputLoudness = fabs(loudness / numSamples); - } - - if (didClip) { - _timeSinceLastClip = 0.0f; - } else if (_timeSinceLastClip >= 0.0f) { - _timeSinceLastClip += (float)numSamples / (float)AudioConstants::SAMPLE_RATE; - } - - emit inputReceived({ audioBuffer.data(), numSamples }); - - if (_noiseGate.openedInLastBlock()) { - emit noiseGateOpened(); - } else if (_noiseGate.closedInLastBlock()) { - emit noiseGateClosed(); - } - } - - // the codec needs a flush frame before sending silent packets, so - // do not send one if the gate closed in this block (eventually this can be crossfaded). - auto packetType = _shouldEchoToServer ? - PacketType::MicrophoneAudioWithEcho : PacketType::MicrophoneAudioNoEcho; - if (_lastInputLoudness == 0.0f && !_noiseGate.closedInLastBlock()) { - packetType = PacketType::SilentAudioFrame; - _silentOutbound.increment(); - } else { - _audioOutbound.increment(); - } - - Transform audioTransform; - audioTransform.setTranslation(_positionGetter()); - audioTransform.setRotation(_orientationGetter()); - - QByteArray encodedBuffer; - if (_encoder) { - _encoder->encode(audioBuffer, encodedBuffer); - } else { - encodedBuffer = audioBuffer; - } - - emitAudioPacket(encodedBuffer.data(), encodedBuffer.size(), _outgoingAvatarAudioSequenceNumber, - audioTransform, avatarBoundingBoxCorner, avatarBoundingBoxScale, - packetType, _selectedCodecName); - _stats.sentPacket(); -} - -void AudioClient::handleMicAudioInput() { +void AudioClient::handleAudioInput() { if (!_inputDevice || _isPlayingBackRecording) { return; } // input samples required to produce exactly NETWORK_FRAME_SAMPLES of output - const int inputSamplesRequired = (_inputToNetworkResampler ? - _inputToNetworkResampler->getMinInput(AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL) : + const int inputSamplesRequired = (_inputToNetworkResampler ? + _inputToNetworkResampler->getMinInput(AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL) : AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL) * _inputFormat.channelCount(); const auto inputAudioSamples = std::unique_ptr(new int16_t[inputSamplesRequired]); @@ -1073,27 +1001,126 @@ void AudioClient::handleMicAudioInput() { static int16_t networkAudioSamples[AudioConstants::NETWORK_FRAME_SAMPLES_STEREO]; while (_inputRingBuffer.samplesAvailable() >= inputSamplesRequired) { - if (_muted) { - _inputRingBuffer.shiftReadPosition(inputSamplesRequired); - } else { + + if (!_muted) { + + + // Increment the time since the last clip + if (_timeSinceLastClip >= 0.0f) { + _timeSinceLastClip += (float)numNetworkSamples / (float)AudioConstants::SAMPLE_RATE; + } + _inputRingBuffer.readSamples(inputAudioSamples.get(), inputSamplesRequired); possibleResampling(_inputToNetworkResampler, inputAudioSamples.get(), networkAudioSamples, inputSamplesRequired, numNetworkSamples, _inputFormat.channelCount(), _desiredInputFormat.channelCount()); + + // Remove DC offset + if (!_isStereoInput) { + _inputGate.removeDCOffset(networkAudioSamples, numNetworkSamples); + } + + // only impose the noise gate and perform tone injection if we are sending mono audio + if (!_isStereoInput && _isNoiseGateEnabled) { + _inputGate.gateSamples(networkAudioSamples, numNetworkSamples); + + // if we performed the noise gate we can get values from it instead of enumerating the samples again + _lastInputLoudness = _inputGate.getLastLoudness(); + + if (_inputGate.clippedInLastBlock()) { + _timeSinceLastClip = 0.0f; + } + + } else { + float loudness = 0.0f; + + for (int i = 0; i < numNetworkSamples; i++) { + int thisSample = std::abs(networkAudioSamples[i]); + loudness += (float)thisSample; + + if (thisSample > (AudioConstants::MAX_SAMPLE_VALUE * AudioNoiseGate::CLIPPING_THRESHOLD)) { + _timeSinceLastClip = 0.0f; + } + } + + _lastInputLoudness = fabs(loudness / numNetworkSamples); + } + + emit inputReceived({ reinterpret_cast(networkAudioSamples), numNetworkBytes }); + + if (_inputGate.openedInLastBlock()) { + emit noiseGateOpened(); + } else if (_inputGate.closedInLastBlock()) { + emit noiseGateClosed(); + } + + } else { + // our input loudness is 0, since we're muted + _lastInputLoudness = 0; + _timeSinceLastClip = 0.0f; + + _inputRingBuffer.shiftReadPosition(inputSamplesRequired); } + + auto packetType = _shouldEchoToServer ? + PacketType::MicrophoneAudioWithEcho : PacketType::MicrophoneAudioNoEcho; + + // if the _inputGate closed in this last frame, then we don't actually want + // to send a silent packet, instead, we want to go ahead and encode and send + // the output from the input gate (eventually, this could be crossfaded) + // and allow the codec to properly encode down to silent/zero. If we still + // have _lastInputLoudness of 0 in our NEXT frame, we will send a silent packet + if (_lastInputLoudness == 0 && !_inputGate.closedInLastBlock()) { + packetType = PacketType::SilentAudioFrame; + _silentOutbound.increment(); + } else { + _micAudioOutbound.increment(); + } + + Transform audioTransform; + audioTransform.setTranslation(_positionGetter()); + audioTransform.setRotation(_orientationGetter()); + // FIXME find a way to properly handle both playback audio and user audio concurrently + + QByteArray decodedBuffer(reinterpret_cast(networkAudioSamples), numNetworkBytes); + QByteArray encodedBuffer; + if (_encoder) { + _encoder->encode(decodedBuffer, encodedBuffer); + } else { + encodedBuffer = decodedBuffer; + } + + emitAudioPacket(encodedBuffer.constData(), encodedBuffer.size(), _outgoingAvatarAudioSequenceNumber, + audioTransform, avatarBoundingBoxCorner, avatarBoundingBoxScale, + packetType, _selectedCodecName); + _stats.sentPacket(); + int bytesInInputRingBuffer = _inputRingBuffer.samplesAvailable() * AudioConstants::SAMPLE_SIZE; float msecsInInputRingBuffer = bytesInInputRingBuffer / (float)(_inputFormat.bytesForDuration(USECS_PER_MSEC)); _stats.updateInputMsUnplayed(msecsInInputRingBuffer); - - QByteArray audioBuffer(reinterpret_cast(networkAudioSamples), numNetworkBytes); - handleAudioInput(audioBuffer); } } +// FIXME - should this go through the noise gate and honor mute and echo? void AudioClient::handleRecordedAudioInput(const QByteArray& audio) { - QByteArray audioBuffer(audio); - handleAudioInput(audioBuffer); + Transform audioTransform; + audioTransform.setTranslation(_positionGetter()); + audioTransform.setRotation(_orientationGetter()); + + QByteArray encodedBuffer; + if (_encoder) { + _encoder->encode(audio, encodedBuffer); + } else { + encodedBuffer = audio; + } + + _micAudioOutbound.increment(); + + // FIXME check a flag to see if we should echo audio? + emitAudioPacket(encodedBuffer.data(), encodedBuffer.size(), _outgoingAvatarAudioSequenceNumber, + audioTransform, avatarBoundingBoxCorner, avatarBoundingBoxScale, + PacketType::MicrophoneAudioWithEcho, _selectedCodecName); } void AudioClient::prepareLocalAudioInjectors() { @@ -1407,7 +1434,7 @@ bool AudioClient::switchInputToAudioDevice(const QAudioDeviceInfo& inputDeviceIn lock.unlock(); if (_inputDevice) { - connect(_inputDevice, SIGNAL(readyRead()), this, SLOT(handleMicAudioInput())); + connect(_inputDevice, SIGNAL(readyRead()), this, SLOT(handleAudioInput())); supportedFormat = true; } else { qCDebug(audioclient) << "Error starting audio input -" << _audioInput->error(); @@ -1513,39 +1540,12 @@ bool AudioClient::switchOutputToAudioDevice(const QAudioDeviceInfo& outputDevice // setup our general output device for audio-mixer audio _audioOutput = new QAudioOutput(outputDeviceInfo, _outputFormat, this); + int osDefaultBufferSize = _audioOutput->bufferSize(); int deviceChannelCount = _outputFormat.channelCount(); - int frameSize = (AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL * deviceChannelCount * _outputFormat.sampleRate()) / _desiredOutputFormat.sampleRate(); - int requestedSize = _sessionOutputBufferSizeFrames * frameSize * AudioConstants::SAMPLE_SIZE; + int deviceFrameSize = (AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL * deviceChannelCount * _outputFormat.sampleRate()) / _desiredOutputFormat.sampleRate(); + int requestedSize = _sessionOutputBufferSizeFrames * deviceFrameSize * AudioConstants::SAMPLE_SIZE; _audioOutput->setBufferSize(requestedSize); - // initialize mix buffers on the _audioOutput thread to avoid races - connect(_audioOutput, &QAudioOutput::stateChanged, [&, frameSize, requestedSize](QAudio::State state) { - if (state == QAudio::ActiveState) { - // restrict device callback to _outputPeriod samples - _outputPeriod = (_audioOutput->periodSize() / AudioConstants::SAMPLE_SIZE) * 2; - _outputMixBuffer = new float[_outputPeriod]; - _outputScratchBuffer = new int16_t[_outputPeriod]; - - // size local output mix buffer based on resampled network frame size - _networkPeriod = _localToOutputResampler->getMaxOutput(AudioConstants::NETWORK_FRAME_SAMPLES_STEREO); - _localOutputMixBuffer = new float[_networkPeriod]; - int localPeriod = _outputPeriod * 2; - _localInjectorsStream.resizeForFrameSize(localPeriod); - - int bufferSize = _audioOutput->bufferSize(); - int bufferSamples = bufferSize / AudioConstants::SAMPLE_SIZE; - int bufferFrames = bufferSamples / (float)frameSize; - qCDebug(audioclient) << "frame (samples):" << frameSize; - qCDebug(audioclient) << "buffer (frames):" << bufferFrames; - qCDebug(audioclient) << "buffer (samples):" << bufferSamples; - qCDebug(audioclient) << "buffer (bytes):" << bufferSize; - qCDebug(audioclient) << "requested (bytes):" << requestedSize; - qCDebug(audioclient) << "period (samples):" << _outputPeriod; - qCDebug(audioclient) << "local buffer (samples):" << localPeriod; - - disconnect(_audioOutput, &QAudioOutput::stateChanged, 0, 0); - } - }); connect(_audioOutput, &QAudioOutput::notify, this, &AudioClient::outputNotify); _audioOutputIODevice.start(); @@ -1555,6 +1555,18 @@ bool AudioClient::switchOutputToAudioDevice(const QAudioDeviceInfo& outputDevice _audioOutput->start(&_audioOutputIODevice); lock.unlock(); + int periodSampleSize = _audioOutput->periodSize() / AudioConstants::SAMPLE_SIZE; + // device callback is not restricted to periodSampleSize, so double the mix/scratch buffer sizes + _outputPeriod = periodSampleSize * 2; + _outputMixBuffer = new float[_outputPeriod]; + _outputScratchBuffer = new int16_t[_outputPeriod]; + _localOutputMixBuffer = new float[_outputPeriod]; + _localInjectorsStream.resizeForFrameSize(_outputPeriod * 2); + + qCDebug(audioclient) << "Output Buffer capacity in frames: " << _audioOutput->bufferSize() / AudioConstants::SAMPLE_SIZE / (float)deviceFrameSize << + "requested bytes:" << requestedSize << "actual bytes:" << _audioOutput->bufferSize() << + "os default:" << osDefaultBufferSize << "period size:" << _audioOutput->periodSize(); + // setup a loopback audio output device _loopbackAudioOutput = new QAudioOutput(outputDeviceInfo, _outputFormat, this); diff --git a/libraries/audio-client/src/AudioClient.h b/libraries/audio-client/src/AudioClient.h index 139749e8e8..7e9acc0586 100644 --- a/libraries/audio-client/src/AudioClient.h +++ b/libraries/audio-client/src/AudioClient.h @@ -124,16 +124,16 @@ public: void selectAudioFormat(const QString& selectedCodecName); Q_INVOKABLE QString getSelectedAudioFormat() const { return _selectedCodecName; } - Q_INVOKABLE bool getNoiseGateOpen() const { return _noiseGate.isOpen(); } + Q_INVOKABLE bool getNoiseGateOpen() const { return _inputGate.isOpen(); } + Q_INVOKABLE float getSilentOutboundPPS() const { return _silentOutbound.rate(); } + Q_INVOKABLE float getMicAudioOutboundPPS() const { return _micAudioOutbound.rate(); } Q_INVOKABLE float getSilentInboundPPS() const { return _silentInbound.rate(); } Q_INVOKABLE float getAudioInboundPPS() const { return _audioInbound.rate(); } - Q_INVOKABLE float getSilentOutboundPPS() const { return _silentOutbound.rate(); } - Q_INVOKABLE float getAudioOutboundPPS() const { return _audioOutbound.rate(); } const MixedProcessedAudioStream& getReceivedAudioStream() const { return _receivedAudioStream; } MixedProcessedAudioStream& getReceivedAudioStream() { return _receivedAudioStream; } - float getLastInputLoudness() const { return glm::max(_lastInputLoudness - _noiseGate.getMeasuredFloor(), 0.0f); } + float getLastInputLoudness() const { return glm::max(_lastInputLoudness - _inputGate.getMeasuredFloor(), 0.0f); } float getTimeSinceLastClip() const { return _timeSinceLastClip; } float getAudioAverageInputLoudness() const { return _lastInputLoudness; } @@ -180,7 +180,7 @@ public slots: void handleMismatchAudioFormat(SharedNodePointer node, const QString& currentCodec, const QString& recievedCodec); void sendDownstreamAudioStatsPacket() { _stats.publish(); } - void handleMicAudioInput(); + void handleAudioInput(); void handleRecordedAudioInput(const QByteArray& audio); void reset(); void audioMixerKilled(); @@ -250,7 +250,6 @@ protected: private: void outputFormatChanged(); - void handleAudioInput(QByteArray& audioBuffer); bool mixLocalAudioInjectors(float* mixBuffer); float azimuthForSource(const glm::vec3& relativePosition); float gainForSource(float distance, float volume); @@ -340,7 +339,6 @@ private: int16_t _networkScratchBuffer[AudioConstants::NETWORK_FRAME_SAMPLES_AMBISONIC]; // for local audio (used by audio injectors thread) - int _networkPeriod { 0 }; float _localMixBuffer[AudioConstants::NETWORK_FRAME_SAMPLES_STEREO]; int16_t _localScratchBuffer[AudioConstants::NETWORK_FRAME_SAMPLES_AMBISONIC]; float* _localOutputMixBuffer { NULL }; @@ -373,7 +371,7 @@ private: AudioIOStats _stats; - AudioNoiseGate _noiseGate; + AudioNoiseGate _inputGate; AudioPositionGetter _positionGetter; AudioOrientationGetter _orientationGetter; @@ -397,7 +395,7 @@ private: QThread* _checkDevicesThread { nullptr }; RateCounter<> _silentOutbound; - RateCounter<> _audioOutbound; + RateCounter<> _micAudioOutbound; RateCounter<> _silentInbound; RateCounter<> _audioInbound; }; diff --git a/libraries/avatars/src/HeadData.cpp b/libraries/avatars/src/HeadData.cpp index bf8593f1d9..72516d9740 100644 --- a/libraries/avatars/src/HeadData.cpp +++ b/libraries/avatars/src/HeadData.cpp @@ -65,8 +65,8 @@ glm::quat HeadData::getOrientation() const { void HeadData::setOrientation(const glm::quat& orientation) { // rotate body about vertical axis glm::quat bodyOrientation = _owningAvatar->getOrientation(); - glm::vec3 newForward = glm::inverse(bodyOrientation) * (orientation * IDENTITY_FORWARD); - bodyOrientation = bodyOrientation * glm::angleAxis(atan2f(-newForward.x, -newForward.z), glm::vec3(0.0f, 1.0f, 0.0f)); + glm::vec3 newFront = glm::inverse(bodyOrientation) * (orientation * IDENTITY_FRONT); + bodyOrientation = bodyOrientation * glm::angleAxis(atan2f(-newFront.x, -newFront.z), glm::vec3(0.0f, 1.0f, 0.0f)); _owningAvatar->setOrientation(bodyOrientation); // the rest goes to the head diff --git a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp index 5a317f64bc..b23b59d3f0 100644 --- a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp @@ -355,16 +355,14 @@ void OpenGLDisplayPlugin::customizeContext() { if ((image.width() > 0) && (image.height() > 0)) { cursorData.texture.reset( - gpu::Texture::createStrict( + gpu::Texture::create2D( gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA), image.width(), image.height(), gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR))); cursorData.texture->setSource("cursor texture"); auto usage = gpu::Texture::Usage::Builder().withColor().withAlpha(); cursorData.texture->setUsage(usage.build()); - cursorData.texture->setStoredMipFormat(gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA)); - cursorData.texture->assignStoredMip(0, image.byteCount(), image.constBits()); - cursorData.texture->autoGenerateMips(-1); + cursorData.texture->assignStoredMip(0, gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA), image.byteCount(), image.constBits()); } } } diff --git a/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp index c55d985a62..a8b8ba3618 100644 --- a/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp @@ -296,32 +296,33 @@ void HmdDisplayPlugin::internalPresent() { image = image.convertToFormat(QImage::Format_RGBA8888); if (!_previewTexture) { _previewTexture.reset( - gpu::Texture::createStrict( + gpu::Texture::create2D( gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA), image.width(), image.height(), gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR))); _previewTexture->setSource("HMD Preview Texture"); _previewTexture->setUsage(gpu::Texture::Usage::Builder().withColor().build()); - _previewTexture->setStoredMipFormat(gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA)); - _previewTexture->assignStoredMip(0, image.byteCount(), image.constBits()); + _previewTexture->assignStoredMip(0, gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA), image.byteCount(), image.constBits()); _previewTexture->autoGenerateMips(-1); } - auto viewport = getViewportForSourceSize(uvec2(_previewTexture->getDimensions())); + if (getGLBackend()->isTextureReady(_previewTexture)) { + 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); - }); - _clearPreviewFlag = false; - swapBuffers(); + 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); + }); + _clearPreviewFlag = false; + swapBuffers(); + } } postPreview(); diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index fb6054a514..27e00b47c6 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -146,7 +146,6 @@ void EntityTreeRenderer::clear() { void EntityTreeRenderer::reloadEntityScripts() { _entitiesScriptEngine->unloadAllEntityScripts(); - _entitiesScriptEngine->resetModuleCache(); foreach(auto entity, _entitiesInScene) { if (!entity->getScript().isEmpty()) { _entitiesScriptEngine->loadEntityScript(entity->getEntityItemID(), entity->getScript(), true); @@ -941,7 +940,7 @@ void EntityTreeRenderer::mouseMoveEvent(QMouseEvent* event) { void EntityTreeRenderer::deletingEntity(const EntityItemID& entityID) { if (_tree && !_shuttingDown && _entitiesScriptEngine) { - _entitiesScriptEngine->unloadEntityScript(entityID, true); + _entitiesScriptEngine->unloadEntityScript(entityID); } forceRecheckEntities(); // reset our state to force checking our inside/outsideness of entities @@ -996,7 +995,7 @@ void EntityTreeRenderer::checkAndCallPreload(const EntityItemID& entityID, const } bool shouldLoad = entity->shouldPreloadScript() && _entitiesScriptEngine; QString scriptUrl = entity->getScript(); - if (shouldLoad && (unloadFirst || scriptUrl.isEmpty())) { + if ((unloadFirst && shouldLoad) || scriptUrl.isEmpty()) { _entitiesScriptEngine->unloadEntityScript(entityID); entity->scriptHasUnloaded(); } diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp index 1d58527427..7359a548fc 100644 --- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp @@ -14,7 +14,6 @@ #include #include #include -#include "ModelScriptingInterface.h" #if defined(__GNUC__) && !defined(__clang__) #pragma GCC diagnostic push @@ -54,8 +53,6 @@ #include "PhysicalEntitySimulation.h" gpu::PipelinePointer RenderablePolyVoxEntityItem::_pipeline = nullptr; -gpu::PipelinePointer RenderablePolyVoxEntityItem::_wireframePipeline = nullptr; - const float MARCHING_CUBE_COLLISION_HULL_OFFSET = 0.5; @@ -76,7 +73,7 @@ const float MARCHING_CUBE_COLLISION_HULL_OFFSET = 0.5; _meshDirty In RenderablePolyVoxEntityItem::render, these flags are checked and changes are propagated along the chain. - decompressVolumeData() is called to decompress _voxelData into _volData. recomputeMesh() is called to invoke the + decompressVolumeData() is called to decompress _voxelData into _volData. getMesh() is called to invoke the polyVox surface extractor to create _mesh (as well as set Simulation _dirtyFlags). Because Simulation::DIRTY_SHAPE is set, isReadyToComputeShape() gets called and _shape is created either from _volData or _shape, depending on the surface style. @@ -84,7 +81,7 @@ const float MARCHING_CUBE_COLLISION_HULL_OFFSET = 0.5; When a script changes _volData, compressVolumeDataAndSendEditPacket is called to update _voxelData and to send a packet to the entity-server. - decompressVolumeData, recomputeMesh, computeShapeInfoWorker, and compressVolumeDataAndSendEditPacket are too expensive + decompressVolumeData, getMesh, computeShapeInfoWorker, and compressVolumeDataAndSendEditPacket are too expensive to run on a thread that has other things to do. These use QtConcurrent::run to spawn a thread. As each thread finishes, it adjusts the dirty flags so that the next call to render() will kick off the next step. @@ -666,8 +663,11 @@ void RenderablePolyVoxEntityItem::setZTextureURL(QString zTextureURL) { } } +void RenderablePolyVoxEntityItem::render(RenderArgs* args) { + PerformanceTimer perfTimer("RenderablePolyVoxEntityItem::render"); + assert(getType() == EntityTypes::PolyVox); + Q_ASSERT(args->_batch); -bool RenderablePolyVoxEntityItem::updateDependents() { bool voxelDataDirty; bool volDataDirty; withWriteLock([&] { @@ -682,20 +682,9 @@ bool RenderablePolyVoxEntityItem::updateDependents() { if (voxelDataDirty) { decompressVolumeData(); } else if (volDataDirty) { - recomputeMesh(); + getMesh(); } - return !volDataDirty; -} - - -void RenderablePolyVoxEntityItem::render(RenderArgs* args) { - PerformanceTimer perfTimer("RenderablePolyVoxEntityItem::render"); - assert(getType() == EntityTypes::PolyVox); - Q_ASSERT(args->_batch); - - updateDependents(); - model::MeshPointer mesh; glm::vec3 voxelVolumeSize; withReadLock([&] { @@ -707,7 +696,7 @@ void RenderablePolyVoxEntityItem::render(RenderArgs* args) { !mesh->getIndexBuffer()._buffer) { return; } - + if (!_pipeline) { gpu::ShaderPointer vertexShader = gpu::Shader::createVertex(std::string(polyvox_vert)); gpu::ShaderPointer pixelShader = gpu::Shader::createPixel(std::string(polyvox_frag)); @@ -726,13 +715,6 @@ void RenderablePolyVoxEntityItem::render(RenderArgs* args) { state->setDepthTest(true, true, gpu::LESS_EQUAL); _pipeline = gpu::Pipeline::create(program, state); - - auto wireframeState = std::make_shared(); - wireframeState->setCullMode(gpu::State::CULL_BACK); - wireframeState->setDepthTest(true, true, gpu::LESS_EQUAL); - wireframeState->setFillMode(gpu::State::FILL_LINE); - - _wireframePipeline = gpu::Pipeline::create(program, wireframeState); } if (!_vertexFormat) { @@ -743,11 +725,7 @@ void RenderablePolyVoxEntityItem::render(RenderArgs* args) { } gpu::Batch& batch = *args->_batch; - - // Pick correct Pipeline - bool wireframe = (render::ShapeKey(args->_globalShapeKey).isWireframe()); - auto pipeline = (wireframe ? _wireframePipeline : _pipeline); - batch.setPipeline(pipeline); + batch.setPipeline(_pipeline); Transform transform(voxelToWorldMatrix()); batch.setModelTransform(transform); @@ -784,7 +762,7 @@ void RenderablePolyVoxEntityItem::render(RenderArgs* args) { batch.setResourceTexture(2, DependencyManager::get()->getWhiteTexture()); } - int voxelVolumeSizeLocation = pipeline->getProgram()->getUniforms().findLocation("voxelVolumeSize"); + int voxelVolumeSizeLocation = _pipeline->getProgram()->getUniforms().findLocation("voxelVolumeSize"); batch._glUniform3f(voxelVolumeSizeLocation, voxelVolumeSize.x, voxelVolumeSize.y, voxelVolumeSize.z); batch.drawIndexed(gpu::TRIANGLES, (gpu::uint32)mesh->getNumIndices(), 0); @@ -1221,7 +1199,7 @@ void RenderablePolyVoxEntityItem::copyUpperEdgesFromNeighbors() { } } -void RenderablePolyVoxEntityItem::recomputeMesh() { +void RenderablePolyVoxEntityItem::getMesh() { // use _volData to make a renderable mesh PolyVoxSurfaceStyle voxelSurfaceStyle; withReadLock([&] { @@ -1291,20 +1269,12 @@ void RenderablePolyVoxEntityItem::recomputeMesh() { vertexBufferPtr->getSize() , sizeof(PolyVox::PositionMaterialNormal), gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::RAW))); - - std::vector parts; - parts.emplace_back(model::Mesh::Part((model::Index)0, // startIndex - (model::Index)vecIndices.size(), // numIndices - (model::Index)0, // baseVertex - model::Mesh::TRIANGLES)); // topology - mesh->setPartBuffer(gpu::BufferView(new gpu::Buffer(parts.size() * sizeof(model::Mesh::Part), - (gpu::Byte*) parts.data()), gpu::Element::PART_DRAWCALL)); entity->setMesh(mesh); }); } void RenderablePolyVoxEntityItem::setMesh(model::MeshPointer mesh) { - // this catches the payload from recomputeMesh + // this catches the payload from getMesh bool neighborsNeedUpdate; withWriteLock([&] { if (!_collisionless) { @@ -1561,6 +1531,7 @@ std::shared_ptr RenderablePolyVoxEntityItem::getZPN return std::dynamic_pointer_cast(_zPNeighbor.lock()); } + void RenderablePolyVoxEntityItem::bonkNeighbors() { // flag neighbors to the negative of this entity as needing to rebake their meshes. cacheNeighbors(); @@ -1580,6 +1551,7 @@ void RenderablePolyVoxEntityItem::bonkNeighbors() { } } + void RenderablePolyVoxEntityItem::locationChanged(bool tellPhysics) { EntityItem::locationChanged(tellPhysics); if (!_pipeline || !render::Item::isValidID(_myItem)) { @@ -1591,25 +1563,3 @@ void RenderablePolyVoxEntityItem::locationChanged(bool tellPhysics) { scene->enqueuePendingChanges(pendingChanges); } - -bool RenderablePolyVoxEntityItem::getMeshAsScriptValue(QScriptEngine *engine, QScriptValue& result) { - if (!updateDependents()) { - return false; - } - - bool success = false; - MeshProxy* meshProxy = nullptr; - glm::mat4 transform = voxelToLocalMatrix(); - withReadLock([&] { - if (_meshInitialized) { - success = true; - // the mesh will be in voxel-space. transform it into object-space - meshProxy = new MeshProxy( - _mesh->map([=](glm::vec3 position){ return glm::vec3(transform * glm::vec4(position, 1.0f)); }, - [=](glm::vec3 normal){ return glm::vec3(transform * glm::vec4(normal, 0.0f)); }, - [](uint32_t index){ return index; })); - } - }); - result = meshToScriptValue(engine, meshProxy); - return success; -} diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h index cf4672f068..45842c2fb9 100644 --- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h +++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h @@ -61,8 +61,6 @@ public: virtual uint8_t getVoxel(int x, int y, int z) override; virtual bool setVoxel(int x, int y, int z, uint8_t toValue) override; - int getOnCount() const override { return _onCount; } - void render(RenderArgs* args) override; virtual bool supportsDetailedRayIntersection() const override { return true; } virtual bool findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction, @@ -135,7 +133,6 @@ public: QByteArray volDataToArray(quint16 voxelXSize, quint16 voxelYSize, quint16 voxelZSize) const; void setMesh(model::MeshPointer mesh); - bool getMeshAsScriptValue(QScriptEngine *engine, QScriptValue& result) override; void setCollisionPoints(ShapeInfo::PointCollection points, AABox box); PolyVox::SimpleVolume* getVolData() { return _volData; } @@ -166,12 +163,11 @@ private: const int MATERIAL_GPU_SLOT = 3; render::ItemID _myItem{ render::Item::INVALID_ITEM_ID }; static gpu::PipelinePointer _pipeline; - static gpu::PipelinePointer _wireframePipeline; ShapeInfo _shapeInfo; PolyVox::SimpleVolume* _volData = nullptr; - bool _volDataDirty = false; // does recomputeMesh need to be called? + bool _volDataDirty = false; // does getMesh need to be called? int _onCount; // how many non-zero voxels are in _volData bool _neighborsNeedUpdate { false }; @@ -182,7 +178,7 @@ private: // these are run off the main thread void decompressVolumeData(); void compressVolumeDataAndSendEditPacket(); - virtual void recomputeMesh() override; // recompute mesh + virtual void getMesh() override; // recompute mesh void computeShapeInfoWorker(); // these are cached lookups of _xNNeighborID, _yNNeighborID, _zNNeighborID, _xPNeighborID, _yPNeighborID, _zPNeighborID @@ -195,7 +191,6 @@ private: void cacheNeighbors(); void copyUpperEdgesFromNeighbors(); void bonkNeighbors(); - bool updateDependents(); }; bool inUserBounds(const PolyVox::SimpleVolume* vol, PolyVoxEntityItem::PolyVoxSurfaceStyle surfaceStyle, diff --git a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp index 1ad60bf7c6..c3e097382c 100644 --- a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp @@ -114,22 +114,13 @@ void RenderableShapeEntityItem::render(RenderArgs* args) { auto outColor = _procedural->getColor(color); outColor.a *= _procedural->isFading() ? Interpolate::calculateFadeRatio(_procedural->getFadeStartTime()) : 1.0f; batch._glColor4f(outColor.r, outColor.g, outColor.b, outColor.a); - if (render::ShapeKey(args->_globalShapeKey).isWireframe()) { - DependencyManager::get()->renderWireShape(batch, MAPPING[_shape]); - } else { - DependencyManager::get()->renderShape(batch, MAPPING[_shape]); - } + DependencyManager::get()->renderShape(batch, MAPPING[_shape]); } else { // FIXME, support instanced multi-shape rendering using multidraw indirect color.a *= _isFading ? Interpolate::calculateFadeRatio(_fadeStartTime) : 1.0f; auto geometryCache = DependencyManager::get(); auto pipeline = color.a < 1.0f ? geometryCache->getTransparentShapePipeline() : geometryCache->getOpaqueShapePipeline(); - - if (render::ShapeKey(args->_globalShapeKey).isWireframe()) { - geometryCache->renderWireShapeInstance(batch, MAPPING[_shape], color, pipeline); - } else { - geometryCache->renderSolidShapeInstance(batch, MAPPING[_shape], color, pipeline); - } + geometryCache->renderSolidShapeInstance(batch, MAPPING[_shape], color, pipeline); } static const auto triCount = DependencyManager::get()->getShapeTriangleCount(MAPPING[_shape]); diff --git a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp index c4ae0db1aa..d7d7013f59 100644 --- a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp @@ -216,7 +216,7 @@ void RenderableWebEntityItem::render(RenderArgs* args) { if (!_texture) { auto webSurface = _webSurface; - _texture = gpu::TexturePointer(gpu::Texture::createExternal(OffscreenQmlSurface::getDiscardLambda())); + _texture = gpu::TexturePointer(gpu::Texture::createExternal2D(OffscreenQmlSurface::getDiscardLambda())); _texture->setSource(__FUNCTION__); } OffscreenQmlSurface::TextureAndFence newTextureAndFence; diff --git a/libraries/entities/src/EntitiesScriptEngineProvider.h b/libraries/entities/src/EntitiesScriptEngineProvider.h index d87dd105c2..69bf73e688 100644 --- a/libraries/entities/src/EntitiesScriptEngineProvider.h +++ b/libraries/entities/src/EntitiesScriptEngineProvider.h @@ -15,13 +15,11 @@ #define hifi_EntitiesScriptEngineProvider_h #include -#include #include "EntityItemID.h" class EntitiesScriptEngineProvider { public: virtual void callEntityScriptMethod(const EntityItemID& entityID, const QString& methodName, const QStringList& params = QStringList()) = 0; - virtual QFuture getLocalEntityScriptDetails(const EntityItemID& entityID) = 0; }; -#endif // hifi_EntitiesScriptEngineProvider_h +#endif // hifi_EntitiesScriptEngineProvider_h \ No newline at end of file diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 0bb085459e..3ef1648fae 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -655,11 +655,13 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef // 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. bool filterRejection = false; if (propertyFlags.getHasProperty(PROP_SIMULATION_OWNER)) { + QByteArray simOwnerData; int bytes = OctreePacketData::unpackDataFromBytes(dataAt, simOwnerData); SimulationOwner newSimOwner; @@ -1877,7 +1879,6 @@ void EntityItem::setSimulationOwner(const SimulationOwner& owner) { } void EntityItem::updateSimulationOwner(const SimulationOwner& owner) { - // NOTE: this method only used by EntityServer. The Interface uses special code in readEntityDataFromBuffer(). if (wantTerseEditLogging() && _simulationOwner != owner) { qCDebug(entities) << "sim ownership for" << getDebugName() << "is now" << owner; } @@ -1893,9 +1894,8 @@ void EntityItem::clearSimulationOwnership() { } _simulationOwner.clear(); - // don't bother setting the DIRTY_SIMULATOR_ID flag because: - // (a) when entity-server calls clearSimulationOwnership() the dirty-flags are meaningless (only used by interface) - // (b) the interface only calls clearSimulationOwnership() in a context that already knows best about dirty flags + // don't bother setting the DIRTY_SIMULATOR_ID flag because clearSimulationOwnership() + // is only ever called on the entity-server and the flags are only used client-side //_dirtyFlags |= Simulation::DIRTY_SIMULATOR_ID; } diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index 1ed020e592..ea81df3801 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -49,6 +49,13 @@ EntityItemProperties::EntityItemProperties(EntityPropertyFlags desiredProperties } +void EntityItemProperties::setSittingPoints(const QVector& sittingPoints) { + _sittingPoints.clear(); + foreach (SittingPoint sitPoint, sittingPoints) { + _sittingPoints.append(sitPoint); + } +} + void EntityItemProperties::calculateNaturalPosition(const glm::vec3& min, const glm::vec3& max) { glm::vec3 halfDimension = (max - min) / 2.0f; _naturalPosition = max - halfDimension; @@ -539,6 +546,20 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_TEXTURES, textures); } + // Sitting properties support + if (!skipDefaults && !strictSemantics) { + QScriptValue sittingPoints = engine->newObject(); + for (int i = 0; i < _sittingPoints.size(); ++i) { + QScriptValue sittingPoint = engine->newObject(); + sittingPoint.setProperty("name", _sittingPoints.at(i).name); + sittingPoint.setProperty("position", vec3toScriptValue(engine, _sittingPoints.at(i).position)); + sittingPoint.setProperty("rotation", quatToScriptValue(engine, _sittingPoints.at(i).rotation)); + sittingPoints.setProperty(i, sittingPoint); + } + sittingPoints.setProperty("length", _sittingPoints.size()); + COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER_ALWAYS(sittingPoints, sittingPoints); // gettable, but not settable + } + if (!skipDefaults && !strictSemantics) { AABox aaBox = getAABox(); QScriptValue boundingBox = engine->newObject(); diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h index 590298e102..419740e4ea 100644 --- a/libraries/entities/src/EntityItemProperties.h +++ b/libraries/entities/src/EntityItemProperties.h @@ -22,6 +22,7 @@ #include #include +#include // for SittingPoint #include #include #include @@ -254,6 +255,8 @@ public: void clearID() { _id = UNKNOWN_ENTITY_ID; _idSet = false; } void markAllChanged(); + void setSittingPoints(const QVector& sittingPoints); + const glm::vec3& getNaturalDimensions() const { return _naturalDimensions; } void setNaturalDimensions(const glm::vec3& value) { _naturalDimensions = value; } @@ -322,6 +325,7 @@ private: // NOTE: The following are pseudo client only properties. They are only used in clients which can access // properties of model geometry. But these properties are not serialized like other properties. + QVector _sittingPoints; QVariantMap _textureNames; glm::vec3 _naturalDimensions; glm::vec3 _naturalPosition; diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index 1ab5438e53..540eba4511 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -8,15 +8,8 @@ // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // - -#include -#include - #include "EntityScriptingInterface.h" -#include -#include - #include "EntityItemID.h" #include #include @@ -296,11 +289,13 @@ EntityItemProperties EntityScriptingInterface::getEntityProperties(QUuid identit results = entity->getProperties(desiredProperties); - // TODO: improve naturalDimensions in the future, - // for now we've added this hack for setting natural dimensions of models + // TODO: improve sitting points and naturalDimensions in the future, + // for now we've included the old sitting points model behavior for entity types that are models + // we've also added this hack for setting natural dimensions of models if (entity->getType() == EntityTypes::Model) { const FBXGeometry* geometry = _entityTree->getGeometryForEntity(entity); if (geometry) { + results.setSittingPoints(geometry->sittingPoints); Extents meshExtents = geometry->getUnscaledMeshExtents(); results.setNaturalDimensions(meshExtents.maximum - meshExtents.minimum); results.calculateNaturalPosition(meshExtents.minimum, meshExtents.maximum); @@ -685,118 +680,6 @@ bool EntityScriptingInterface::reloadServerScripts(QUuid entityID) { return client->reloadServerScript(entityID); } -bool EntityPropertyMetadataRequest::script(EntityItemID entityID, QScriptValue handler) { - using LocalScriptStatusRequest = QFutureWatcher; - - LocalScriptStatusRequest* request = new LocalScriptStatusRequest; - QObject::connect(request, &LocalScriptStatusRequest::finished, _engine, [=]() mutable { - auto details = request->result().toMap(); - QScriptValue err, result; - if (details.contains("isError")) { - if (!details.contains("message")) { - details["message"] = details["errorInfo"]; - } - err = _engine->makeError(_engine->toScriptValue(details)); - } else { - details["success"] = true; - result = _engine->toScriptValue(details); - } - callScopedHandlerObject(handler, err, result); - request->deleteLater(); - }); - auto entityScriptingInterface = DependencyManager::get(); - entityScriptingInterface->withEntitiesScriptEngine([&](EntitiesScriptEngineProvider* entitiesScriptEngine) { - if (entitiesScriptEngine) { - request->setFuture(entitiesScriptEngine->getLocalEntityScriptDetails(entityID)); - } - }); - if (!request->isStarted()) { - request->deleteLater(); - callScopedHandlerObject(handler, _engine->makeError("Entities Scripting Provider unavailable", "InternalError"), QScriptValue()); - return false; - } - return true; -} - -bool EntityPropertyMetadataRequest::serverScripts(EntityItemID entityID, QScriptValue handler) { - auto client = DependencyManager::get(); - auto request = client->createScriptStatusRequest(entityID); - QPointer engine = _engine; - QObject::connect(request, &GetScriptStatusRequest::finished, _engine, [=](GetScriptStatusRequest* request) mutable { - auto engine = _engine; - if (!engine) { - qCDebug(entities) << __FUNCTION__ << " -- engine destroyed while inflight" << entityID; - return; - } - QVariantMap details; - details["success"] = request->getResponseReceived(); - details["isRunning"] = request->getIsRunning(); - details["status"] = EntityScriptStatus_::valueToKey(request->getStatus()).toLower(); - details["errorInfo"] = request->getErrorInfo(); - - QScriptValue err, result; - if (!details["success"].toBool()) { - if (!details.contains("message") && details.contains("errorInfo")) { - details["message"] = details["errorInfo"]; - } - if (details["message"].toString().isEmpty()) { - details["message"] = "entity server script details not found"; - } - err = engine->makeError(engine->toScriptValue(details)); - } else { - result = engine->toScriptValue(details); - } - callScopedHandlerObject(handler, err, result); - request->deleteLater(); - }); - request->start(); - return true; -} - -bool EntityScriptingInterface::queryPropertyMetadata(QUuid entityID, QScriptValue property, QScriptValue scopeOrCallback, QScriptValue methodOrName) { - auto name = property.toString(); - auto handler = makeScopedHandlerObject(scopeOrCallback, methodOrName); - QPointer engine = dynamic_cast(handler.engine()); - if (!engine) { - qCDebug(entities) << "queryPropertyMetadata without detectable engine" << entityID << name; - return false; - } -#ifdef DEBUG_ENGINE_STATE - connect(engine, &QObject::destroyed, this, [=]() { - qDebug() << "queryPropertyMetadata -- engine destroyed!" << (!engine ? "nullptr" : "engine"); - }); -#endif - if (!handler.property("callback").isFunction()) { - qDebug() << "!handler.callback.isFunction" << engine; - engine->raiseException(engine->makeError("callback is not a function", "TypeError")); - return false; - } - - // NOTE: this approach is a work-in-progress and for now just meant to work 100% correctly and provide - // some initial structure for organizing metadata adapters around. - - // The extra layer of indirection is *essential* because in real world conditions errors are often introduced - // by accident and sometimes without exact memory of "what just changed." - - // Here the scripter only needs to know an entityID and a property name -- which means all scripters can - // level this method when stuck in dead-end scenarios or to learn more about "magic" Entity properties - // like .script that work in terms of side-effects. - - // This is an async callback pattern -- so if needed C++ can easily throttle or restrict queries later. - - EntityPropertyMetadataRequest request(engine); - - if (name == "script") { - return request.script(entityID, handler); - } else if (name == "serverScripts") { - return request.serverScripts(entityID, handler); - } else { - engine->raiseException(engine->makeError("metadata for property " + name + " is not yet queryable")); - engine->maybeEmitUncaughtException(__FUNCTION__); - return false; - } -} - bool EntityScriptingInterface::getServerScriptStatus(QUuid entityID, QScriptValue callback) { auto client = DependencyManager::get(); auto request = client->createScriptStatusRequest(entityID); @@ -932,7 +815,8 @@ void RayToEntityIntersectionResultFromScriptValue(const QScriptValue& object, Ra } } -bool EntityScriptingInterface::polyVoxWorker(QUuid entityID, std::function actor) { +bool EntityScriptingInterface::setVoxels(QUuid entityID, + std::function actor) { PROFILE_RANGE(script_entities, __FUNCTION__); if (!_entityTree) { @@ -998,9 +882,11 @@ bool EntityScriptingInterface::setPoints(QUuid entityID, std::function& points) { PROFILE_RANGE(script_entities, __FUNCTION__); @@ -1674,20 +1541,3 @@ bool EntityScriptingInterface::AABoxIntersectsCapsule(const glm::vec3& low, cons AABox aaBox(low, dimensions); return aaBox.findCapsulePenetration(start, end, radius, penetration); } - -glm::mat4 EntityScriptingInterface::getEntityTransform(const QUuid& entityID) { - glm::mat4 result; - if (_entityTree) { - _entityTree->withReadLock([&] { - EntityItemPointer entity = _entityTree->findEntityByEntityItemID(EntityItemID(entityID)); - if (entity) { - glm::mat4 translation = glm::translate(entity->getPosition()); - glm::mat4 rotation = glm::mat4_cast(entity->getRotation()); - glm::mat4 registration = glm::translate(ENTITY_ITEM_DEFAULT_REGISTRATION_POINT - - entity->getRegistrationPoint()); - result = translation * rotation * registration; - } - }); - } - return result; -} diff --git a/libraries/entities/src/EntityScriptingInterface.h b/libraries/entities/src/EntityScriptingInterface.h index 63b5771e60..e9f0637830 100644 --- a/libraries/entities/src/EntityScriptingInterface.h +++ b/libraries/entities/src/EntityScriptingInterface.h @@ -34,23 +34,7 @@ #include "EntitiesScriptEngineProvider.h" #include "EntityItemProperties.h" -#include "BaseScriptEngine.h" - class EntityTree; -class MeshProxy; - -// helper factory to compose standardized, async metadata queries for "magic" Entity properties -// like .script and .serverScripts. This is used for automated testing of core scripting features -// as well as to provide early adopters a self-discoverable, consistent way to diagnose common -// problems with their own Entity scripts. -class EntityPropertyMetadataRequest { -public: - EntityPropertyMetadataRequest(BaseScriptEngine* engine) : _engine(engine) {}; - bool script(EntityItemID entityID, QScriptValue handler); - bool serverScripts(EntityItemID entityID, QScriptValue handler); -private: - QPointer _engine; -}; class RayToEntityIntersectionResult { public: @@ -83,7 +67,6 @@ class EntityScriptingInterface : public OctreeScriptingInterface, public Depende Q_PROPERTY(float costMultiplier READ getCostMultiplier WRITE setCostMultiplier) Q_PROPERTY(QUuid keyboardFocusEntity READ getKeyboardFocusEntity WRITE setKeyboardFocusEntity) - friend EntityPropertyMetadataRequest; public: EntityScriptingInterface(bool bidOnSimulationOwnership); @@ -228,26 +211,6 @@ public slots: Q_INVOKABLE RayToEntityIntersectionResult findRayIntersectionBlocking(const PickRay& ray, bool precisionPicking = false, const QScriptValue& entityIdsToInclude = QScriptValue(), const QScriptValue& entityIdsToDiscard = QScriptValue()); Q_INVOKABLE bool reloadServerScripts(QUuid entityID); - - /**jsdoc - * Query additional metadata for "magic" Entity properties like `script` and `serverScripts`. - * - * @function Entities.queryPropertyMetadata - * @param {EntityID} entityID The ID of the entity. - * @param {string} property The name of the property extended metadata is wanted for. - * @param {ResultCallback} callback Executes callback(err, result) with the query results. - */ - /**jsdoc - * Query additional metadata for "magic" Entity properties like `script` and `serverScripts`. - * - * @function Entities.queryPropertyMetadata - * @param {EntityID} entityID The ID of the entity. - * @param {string} property The name of the property extended metadata is wanted for. - * @param {Object} thisObject The scoping "this" context that callback will be executed within. - * @param {ResultCallback} callbackOrMethodName Executes thisObject[callbackOrMethodName](err, result) with the query results. - */ - Q_INVOKABLE bool queryPropertyMetadata(QUuid entityID, QScriptValue property, QScriptValue scopeOrCallback, QScriptValue methodOrName = QScriptValue()); - Q_INVOKABLE bool getServerScriptStatus(QUuid entityID, QScriptValue callback); Q_INVOKABLE void setLightsArePickable(bool value); @@ -266,7 +229,6 @@ public slots: Q_INVOKABLE bool setAllVoxels(QUuid entityID, int value); Q_INVOKABLE bool setVoxelsInCuboid(QUuid entityID, const glm::vec3& lowPosition, const glm::vec3& cuboidSize, int value); - Q_INVOKABLE void voxelsToMesh(QUuid entityID, QScriptValue callback); Q_INVOKABLE bool setAllPoints(QUuid entityID, const QVector& points); Q_INVOKABLE bool appendPoint(QUuid entityID, const glm::vec3& point); @@ -331,15 +293,6 @@ public slots: const glm::vec3& start, const glm::vec3& end, float radius); - /**jsdoc - * Returns object to world transform, excluding scale - * - * @function Entities.getEntityTransform - * @param {EntityID} entityID The ID of the entity whose transform is to be returned - * @return {Mat4} Entity's object to world transform, excluding scale - */ - Q_INVOKABLE glm::mat4 getEntityTransform(const QUuid& entityID); - signals: void collisionWithEntity(const EntityItemID& idA, const EntityItemID& idB, const Collision& collision); @@ -370,14 +323,9 @@ signals: void webEventReceived(const EntityItemID& entityItemID, const QVariant& message); -protected: - void withEntitiesScriptEngine(std::function function) { - std::lock_guard lock(_entitiesScriptEngineLock); - function(_entitiesScriptEngine); - }; private: bool actionWorker(const QUuid& entityID, std::function actor); - bool polyVoxWorker(QUuid entityID, std::function actor); + bool setVoxels(QUuid entityID, std::function actor); bool setPoints(QUuid entityID, std::function actor); void queueEntityMessage(PacketType packetType, EntityItemID entityID, const EntityItemProperties& properties); diff --git a/libraries/entities/src/PolyVoxEntityItem.cpp b/libraries/entities/src/PolyVoxEntityItem.cpp index 90344d6c4b..2a374c1d17 100644 --- a/libraries/entities/src/PolyVoxEntityItem.cpp +++ b/libraries/entities/src/PolyVoxEntityItem.cpp @@ -242,7 +242,3 @@ const QByteArray PolyVoxEntityItem::getVoxelData() const { }); return voxelDataCopy; } - -bool PolyVoxEntityItem::getMeshAsScriptValue(QScriptEngine *engine, QScriptValue& result) { - return false; -} diff --git a/libraries/entities/src/PolyVoxEntityItem.h b/libraries/entities/src/PolyVoxEntityItem.h index 311a002a4a..910d8eff88 100644 --- a/libraries/entities/src/PolyVoxEntityItem.h +++ b/libraries/entities/src/PolyVoxEntityItem.h @@ -57,8 +57,6 @@ class PolyVoxEntityItem : public EntityItem { virtual void setVoxelData(QByteArray voxelData); virtual const QByteArray getVoxelData() const; - virtual int getOnCount() const { return 0; } - enum PolyVoxSurfaceStyle { SURFACE_MARCHING_CUBES, SURFACE_CUBIC, @@ -133,9 +131,7 @@ class PolyVoxEntityItem : public EntityItem { virtual void rebakeMesh() {}; void setVoxelDataDirty(bool value) { withWriteLock([&] { _voxelDataDirty = value; }); } - virtual void recomputeMesh() {}; - - virtual bool getMeshAsScriptValue(QScriptEngine *engine, QScriptValue& result); + virtual void getMesh() {}; // recompute mesh protected: glm::vec3 _voxelVolumeSize; // this is always 3 bytes diff --git a/libraries/entities/src/PropertyGroup.h b/libraries/entities/src/PropertyGroup.h index f45d19f5eb..38b1e5f599 100644 --- a/libraries/entities/src/PropertyGroup.h +++ b/libraries/entities/src/PropertyGroup.h @@ -14,11 +14,9 @@ #include -#include - +//#include "EntityItemProperties.h" #include "EntityPropertyFlags.h" - class EntityItemProperties; class EncodeBitstreamParams; class OctreePacketData; @@ -26,6 +24,31 @@ class EntityTreeElementExtraEncodeData; class ReadBitstreamToTreeParams; using EntityTreeElementExtraEncodeDataPointer = std::shared_ptr; +#include + +/* +#include + +#include +#include + +#include +#include +#include + +#include +#include // for SittingPoint +#include +#include +#include + +#include "EntityItemID.h" +#include "PropertyGroupMacros.h" +#include "EntityTypes.h" +*/ + +//typedef PropertyFlags EntityPropertyFlags; + class PropertyGroup { public: diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index 718793fefa..fcaef90527 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -1468,9 +1468,6 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS // Create the Material Library consolidateFBXMaterials(mapping); - // We can't allow the scaling of a given image to different sizes, because the hash used for the KTX cache is based on the original image - // Allowing scaling of the same image to different sizes would cause different KTX files to target the same cache key -#if 0 // HACK: until we get proper LOD management we're going to cap model textures // according to how many unique textures the model uses: // 1 - 8 textures --> 2048 @@ -1484,7 +1481,6 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS int numTextures = uniqueTextures.size(); const int MAX_NUM_TEXTURES_AT_MAX_RESOLUTION = 8; int maxWidth = sqrt(MAX_NUM_PIXELS_FOR_FBX_TEXTURE); - if (numTextures > MAX_NUM_TEXTURES_AT_MAX_RESOLUTION) { int numTextureThreshold = MAX_NUM_TEXTURES_AT_MAX_RESOLUTION; const int MIN_MIP_TEXTURE_WIDTH = 64; @@ -1498,7 +1494,7 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS material.setMaxNumPixelsPerTexture(maxWidth * maxWidth); } } -#endif + geometry.materials = _fbxMaterials; // see if any materials have texture children @@ -1799,6 +1795,19 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS } geometry.palmDirection = parseVec3(mapping.value("palmDirection", "0, -1, 0").toString()); + // Add sitting points + QVariantHash sittingPoints = mapping.value("sit").toHash(); + for (QVariantHash::const_iterator it = sittingPoints.constBegin(); it != sittingPoints.constEnd(); it++) { + SittingPoint sittingPoint; + sittingPoint.name = it.key(); + + QVariantList properties = it->toList(); + sittingPoint.position = parseVec3(properties.at(0).toString()); + sittingPoint.rotation = glm::quat(glm::radians(parseVec3(properties.at(1).toString()))); + + geometry.sittingPoints.append(sittingPoint); + } + // attempt to map any meshes to a named model for (QHash::const_iterator m = meshIDsToMeshIndices.constBegin(); m != meshIDsToMeshIndices.constEnd(); m++) { diff --git a/libraries/fbx/src/FBXReader.h b/libraries/fbx/src/FBXReader.h index fa047e512f..6e51c413dc 100644 --- a/libraries/fbx/src/FBXReader.h +++ b/libraries/fbx/src/FBXReader.h @@ -265,6 +265,24 @@ public: Q_DECLARE_METATYPE(FBXAnimationFrame) Q_DECLARE_METATYPE(QVector) +/// A point where an avatar can sit +class SittingPoint { +public: + QString name; + glm::vec3 position; // relative postion + glm::quat rotation; // relative orientation +}; + +inline bool operator==(const SittingPoint& lhs, const SittingPoint& rhs) +{ + return (lhs.name == rhs.name) && (lhs.position == rhs.position) && (lhs.rotation == rhs.rotation); +} + +inline bool operator!=(const SittingPoint& lhs, const SittingPoint& rhs) +{ + return (lhs.name != rhs.name) || (lhs.position != rhs.position) || (lhs.rotation != rhs.rotation); +} + /// A set of meshes extracted from an FBX document. class FBXGeometry { public: @@ -302,6 +320,8 @@ public: glm::vec3 palmDirection; + QVector sittingPoints; + glm::vec3 neckPivot; Extents bindExtents; diff --git a/libraries/fbx/src/FBXReader_Node.cpp b/libraries/fbx/src/FBXReader_Node.cpp index d987f885eb..d814f58dab 100644 --- a/libraries/fbx/src/FBXReader_Node.cpp +++ b/libraries/fbx/src/FBXReader_Node.cpp @@ -54,8 +54,7 @@ template QVariant readBinaryArray(QDataStream& in, int& position) { in.readRawData(compressed.data() + sizeof(quint32), compressedLength); position += compressedLength; arrayData = qUncompress(compressed); - if (arrayData.isEmpty() || - (unsigned int)arrayData.size() != (sizeof(T) * arrayLength)) { // answers empty byte array if corrupt + if (arrayData.isEmpty() || arrayData.size() != (sizeof(T) * arrayLength)) { // answers empty byte array if corrupt throw QString("corrupt fbx file"); } } else { diff --git a/libraries/fbx/src/OBJReader.cpp b/libraries/fbx/src/OBJReader.cpp index c1bb72dff8..73cf7a520e 100644 --- a/libraries/fbx/src/OBJReader.cpp +++ b/libraries/fbx/src/OBJReader.cpp @@ -267,7 +267,7 @@ void OBJReader::parseMaterialLibrary(QIODevice* device) { } if (token == "map_Kd") { currentMaterial.diffuseTextureFilename = filename; - } else if( token == "map_Ks" ) { + } else { currentMaterial.specularTextureFilename = filename; } } @@ -546,7 +546,6 @@ FBXGeometry* OBJReader::readOBJ(QByteArray& model, const QVariantHash& mapping, QString queryPart = _url.query(); bool suppressMaterialsHack = queryPart.contains("hifiusemat"); // If this appears in query string, don't fetch mtl even if used. OBJMaterial& preDefinedMaterial = materials[SMART_DEFAULT_MATERIAL_NAME]; - preDefinedMaterial.used = true; if (suppressMaterialsHack) { needsMaterialLibrary = preDefinedMaterial.userSpecifiesUV = false; // I said it was a hack... } @@ -595,8 +594,8 @@ FBXGeometry* OBJReader::readOBJ(QByteArray& model, const QVariantHash& mapping, } foreach (QString materialID, materials.keys()) { - OBJMaterial& objMaterial = materials[materialID]; - if (!objMaterial.used) { + OBJMaterial& objMaterial = materials[materialID]; + if (!objMaterial.used) { continue; } geometry.materials[materialID] = FBXMaterial(objMaterial.diffuseColor, @@ -612,9 +611,6 @@ FBXGeometry* OBJReader::readOBJ(QByteArray& model, const QVariantHash& mapping, if (!objMaterial.diffuseTextureFilename.isEmpty()) { fbxMaterial.albedoTexture.filename = objMaterial.diffuseTextureFilename; } - if (!objMaterial.specularTextureFilename.isEmpty()) { - fbxMaterial.specularTexture.filename = objMaterial.specularTextureFilename; - } modelMaterial->setEmissive(fbxMaterial.emissiveColor); modelMaterial->setAlbedo(fbxMaterial.diffuseColor); diff --git a/libraries/fbx/src/OBJReader.h b/libraries/fbx/src/OBJReader.h index b4a48c570e..200f11548d 100644 --- a/libraries/fbx/src/OBJReader.h +++ b/libraries/fbx/src/OBJReader.h @@ -58,7 +58,7 @@ public: QByteArray specularTextureFilename; bool used { false }; bool userSpecifiesUV { false }; - OBJMaterial() : shininess(0.0f), opacity(1.0f), diffuseColor(0.9f), specularColor(0.9f) {} + OBJMaterial() : shininess(96.0f), opacity(1.0f), diffuseColor(1.0f), specularColor(1.0f) {} }; class OBJReader: public QObject { // QObject so we can make network requests. diff --git a/libraries/fbx/src/OBJWriter.cpp b/libraries/fbx/src/OBJWriter.cpp deleted file mode 100644 index 5ee04c5718..0000000000 --- a/libraries/fbx/src/OBJWriter.cpp +++ /dev/null @@ -1,148 +0,0 @@ -// -// OBJWriter.cpp -// libraries/fbx/src/ -// -// Created by Seth Alves on 2017-1-27. -// Copyright 2017 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#include -#include -#include "model/Geometry.h" -#include "OBJWriter.h" -#include "ModelFormatLogging.h" - -static QString formatFloat(double n) { - // limit precision to 6, but don't output trailing zeros. - QString s = QString::number(n, 'f', 6); - while (s.endsWith("0")) { - s.remove(s.size() - 1, 1); - } - if (s.endsWith(".")) { - s.remove(s.size() - 1, 1); - } - - // check for non-numbers. if we get NaN or inf or scientific notation, just return 0 - for (int i = 0; i < s.length(); i++) { - auto c = s.at(i).toLatin1(); - if (c != '-' && - c != '.' && - (c < '0' || c > '9')) { - qCDebug(modelformat) << "OBJWriter zeroing bad vertex coordinate:" << s << "because of" << c; - return QString("0"); - } - } - - return s; -} - -bool writeOBJToTextStream(QTextStream& out, QList meshes) { - // each mesh's vertices are numbered from zero. We're combining all their vertices into one list here, - // so keep track of the start index for each mesh. - QList meshVertexStartOffset; - int currentVertexStartOffset = 0; - - // write out all vertices - foreach (const MeshPointer& mesh, meshes) { - meshVertexStartOffset.append(currentVertexStartOffset); - const gpu::BufferView& vertexBuffer = mesh->getVertexBuffer(); - int vertexCount = 0; - gpu::BufferView::Iterator vertexItr = vertexBuffer.cbegin(); - while (vertexItr != vertexBuffer.cend()) { - glm::vec3 v = *vertexItr; - out << "v "; - out << formatFloat(v[0]) << " "; - out << formatFloat(v[1]) << " "; - out << formatFloat(v[2]) << "\n"; - vertexItr++; - vertexCount++; - } - currentVertexStartOffset += vertexCount; - } - out << "\n"; - - // write out faces - int nth = 0; - foreach (const MeshPointer& mesh, meshes) { - currentVertexStartOffset = meshVertexStartOffset.takeFirst(); - - const gpu::BufferView& partBuffer = mesh->getPartBuffer(); - const gpu::BufferView& indexBuffer = mesh->getIndexBuffer(); - - model::Index partCount = (model::Index)mesh->getNumParts(); - for (int partIndex = 0; partIndex < partCount; partIndex++) { - const model::Mesh::Part& part = partBuffer.get(partIndex); - - out << "g part-" << nth++ << "\n"; - - // model::Mesh::TRIANGLES - // TODO -- handle other formats - gpu::BufferView::Iterator indexItr = indexBuffer.cbegin(); - indexItr += part._startIndex; - - int indexCount = 0; - while (indexItr != indexBuffer.cend() && indexCount < part._numIndices) { - uint32_t index0 = *indexItr; - indexItr++; - indexCount++; - if (indexItr == indexBuffer.cend() || indexCount >= part._numIndices) { - qCDebug(modelformat) << "OBJWriter -- index buffer length isn't multiple of 3"; - break; - } - uint32_t index1 = *indexItr; - indexItr++; - indexCount++; - if (indexItr == indexBuffer.cend() || indexCount >= part._numIndices) { - qCDebug(modelformat) << "OBJWriter -- index buffer length isn't multiple of 3"; - break; - } - uint32_t index2 = *indexItr; - indexItr++; - indexCount++; - - out << "f "; - out << currentVertexStartOffset + index0 + 1 << " "; - out << currentVertexStartOffset + index1 + 1 << " "; - out << currentVertexStartOffset + index2 + 1 << "\n"; - } - out << "\n"; - } - } - - return true; -} - -bool writeOBJToFile(QString path, QList meshes) { - if (QFileInfo(path).exists() && !QFile::remove(path)) { - qCDebug(modelformat) << "OBJ writer failed, file exists:" << path; - return false; - } - - QFile file(path); - if (!file.open(QIODevice::WriteOnly)) { - qCDebug(modelformat) << "OBJ writer failed to open output file:" << path; - return false; - } - - QTextStream outStream(&file); - - bool success; - success = writeOBJToTextStream(outStream, meshes); - - file.close(); - return success; -} - -QString writeOBJToString(QList meshes) { - QString result; - QTextStream outStream(&result, QIODevice::ReadWrite); - bool success; - success = writeOBJToTextStream(outStream, meshes); - if (success) { - return result; - } - return QString(""); -} diff --git a/libraries/fbx/src/OBJWriter.h b/libraries/fbx/src/OBJWriter.h deleted file mode 100644 index b6e20e1ae6..0000000000 --- a/libraries/fbx/src/OBJWriter.h +++ /dev/null @@ -1,26 +0,0 @@ -// -// OBJWriter.h -// libraries/fbx/src/ -// -// Created by Seth Alves on 2017-1-27. -// Copyright 2017 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#ifndef hifi_objwriter_h -#define hifi_objwriter_h - - -#include -#include -#include - -using MeshPointer = std::shared_ptr; - -bool writeOBJToTextStream(QTextStream& out, QList meshes); -bool writeOBJToFile(QString path, QList meshes); -QString writeOBJToString(QList meshes); - -#endif // hifi_objwriter_h diff --git a/libraries/gpu-gl/src/gpu/gl/GLBackend.cpp b/libraries/gpu-gl/src/gpu/gl/GLBackend.cpp index 0800c27839..c51f468908 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLBackend.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLBackend.cpp @@ -62,6 +62,8 @@ BackendPointer GLBackend::createBackend() { INSTANCE = result.get(); void* voidInstance = &(*result); qApp->setProperty(hifi::properties::gl::BACKEND, QVariant::fromValue(voidInstance)); + + gl::GLTexture::initTextureTransferHelper(); return result; } @@ -207,7 +209,7 @@ void GLBackend::renderPassTransfer(const Batch& batch) { } } - { // Sync all the transform states + { // Sync all the buffers PROFILE_RANGE(render_gpu_gl_detail, "syncCPUTransform"); _transform._cameras.clear(); _transform._cameraOffsets.clear(); @@ -275,7 +277,7 @@ void GLBackend::renderPassDraw(const Batch& batch) { updateInput(); updateTransform(batch); updatePipeline(); - + CommandCall call = _commandCalls[(*command)]; (this->*(call))(batch, *offset); break; @@ -621,7 +623,6 @@ void GLBackend::queueLambda(const std::function lambda) const { } void GLBackend::recycle() const { - PROFILE_RANGE(render_gpu_gl, __FUNCTION__) { std::list> lamdbasTrash; { @@ -744,6 +745,10 @@ void GLBackend::recycle() const { glDeleteQueries((GLsizei)ids.size(), ids.data()); } } + +#ifndef THREADED_TEXTURE_TRANSFER + gl::GLTexture::_textureTransferHelper->process(); +#endif } void GLBackend::setCameraCorrection(const Mat4& correction) { diff --git a/libraries/gpu-gl/src/gpu/gl/GLBackend.h b/libraries/gpu-gl/src/gpu/gl/GLBackend.h index 76c950ec2b..950ac65a3f 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLBackend.h +++ b/libraries/gpu-gl/src/gpu/gl/GLBackend.h @@ -187,15 +187,10 @@ public: virtual void do_setStateScissorRect(const Batch& batch, size_t paramOffset) final; virtual GLuint getFramebufferID(const FramebufferPointer& framebuffer) = 0; - virtual GLuint getTextureID(const TexturePointer& texture) final; + virtual GLuint getTextureID(const TexturePointer& texture, bool needTransfer = true) = 0; virtual GLuint getBufferID(const Buffer& buffer) = 0; virtual GLuint getQueryID(const QueryPointer& query) = 0; - - virtual GLFramebuffer* syncGPUObject(const Framebuffer& framebuffer) = 0; - virtual GLBuffer* syncGPUObject(const Buffer& buffer) = 0; - virtual GLTexture* syncGPUObject(const TexturePointer& texture); - virtual GLQuery* syncGPUObject(const Query& query) = 0; - //virtual bool isTextureReady(const TexturePointer& texture); + virtual bool isTextureReady(const TexturePointer& texture); virtual void releaseBuffer(GLuint id, Size size) const; virtual void releaseExternalTexture(GLuint id, const Texture::ExternalRecycler& recycler) const; @@ -211,6 +206,10 @@ public: protected: void recycle() const override; + virtual GLFramebuffer* syncGPUObject(const Framebuffer& framebuffer) = 0; + virtual GLBuffer* syncGPUObject(const Buffer& buffer) = 0; + virtual GLTexture* syncGPUObject(const TexturePointer& texture, bool sync = true) = 0; + virtual GLQuery* syncGPUObject(const Query& query) = 0; static const size_t INVALID_OFFSET = (size_t)-1; bool _inRenderTransferPass { false }; diff --git a/libraries/gpu-gl/src/gpu/gl/GLBackendTexture.cpp b/libraries/gpu-gl/src/gpu/gl/GLBackendTexture.cpp index ca4e328612..f51eac0e33 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLBackendTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLBackendTexture.cpp @@ -14,56 +14,12 @@ using namespace gpu; using namespace gpu::gl; - -GLuint GLBackend::getTextureID(const TexturePointer& texture) { - GLTexture* object = syncGPUObject(texture); - - if (!object) { - return 0; - } - - return object->_id; +bool GLBackend::isTextureReady(const TexturePointer& texture) { + // DO not transfer the texture, this call is expected for rendering texture + GLTexture* object = syncGPUObject(texture, true); + return object && object->isReady(); } -GLTexture* GLBackend::syncGPUObject(const TexturePointer& texturePointer) { - const Texture& texture = *texturePointer; - // Special case external textures - if (TextureUsageType::EXTERNAL == texture.getUsageType()) { - Texture::ExternalUpdates updates = texture.getUpdates(); - if (!updates.empty()) { - Texture::ExternalRecycler recycler = texture.getExternalRecycler(); - Q_ASSERT(recycler); - // Discard any superfluous updates - while (updates.size() > 1) { - const auto& update = updates.front(); - // Superfluous updates will never have been read, but we want to ensure the previous - // writes to them are complete before they're written again, so return them with the - // same fences they arrived with. This can happen on any thread because no GL context - // work is involved - recycler(update.first, update.second); - updates.pop_front(); - } - - // The last texture remaining is the one we'll use to create the GLTexture - const auto& update = updates.front(); - // Check for a fence, and if it exists, inject a wait into the command stream, then destroy the fence - if (update.second) { - GLsync fence = static_cast(update.second); - glWaitSync(fence, 0, GL_TIMEOUT_IGNORED); - glDeleteSync(fence); - } - - // Create the new texture object (replaces any previous texture object) - new GLExternalTexture(shared_from_this(), texture, update.first); - } - - // Return the texture object (if any) associated with the texture, without extensive logic - // (external textures are - return Backend::getGPUObject(texture); - } - - return nullptr; -} void GLBackend::do_generateTextureMips(const Batch& batch, size_t paramOffset) { TexturePointer resourceTexture = batch._textures.get(batch._params[paramOffset + 0]._uint); @@ -72,7 +28,7 @@ void GLBackend::do_generateTextureMips(const Batch& batch, size_t paramOffset) { } // DO not transfer the texture, this call is expected for rendering texture - GLTexture* object = syncGPUObject(resourceTexture); + GLTexture* object = syncGPUObject(resourceTexture, false); if (!object) { return; } diff --git a/libraries/gpu-gl/src/gpu/gl/GLFramebuffer.cpp b/libraries/gpu-gl/src/gpu/gl/GLFramebuffer.cpp index 2ac7e9d060..85cf069062 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLFramebuffer.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLFramebuffer.cpp @@ -21,12 +21,13 @@ GLFramebuffer::~GLFramebuffer() { } } -bool GLFramebuffer::checkStatus() const { +bool GLFramebuffer::checkStatus(GLenum target) const { + bool result = false; switch (_status) { case GL_FRAMEBUFFER_COMPLETE: // Success ! - return true; - + result = true; + break; case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: qCWarning(gpugllogging) << "GLFramebuffer::syncGPUObject : Framebuffer not valid, GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT."; break; @@ -43,5 +44,5 @@ bool GLFramebuffer::checkStatus() const { qCWarning(gpugllogging) << "GLFramebuffer::syncGPUObject : Framebuffer not valid, GL_FRAMEBUFFER_UNSUPPORTED."; break; } - return false; + return result; } diff --git a/libraries/gpu-gl/src/gpu/gl/GLFramebuffer.h b/libraries/gpu-gl/src/gpu/gl/GLFramebuffer.h index c0633cfdef..9b4f9703fc 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLFramebuffer.h +++ b/libraries/gpu-gl/src/gpu/gl/GLFramebuffer.h @@ -64,7 +64,7 @@ public: protected: GLenum _status { GL_FRAMEBUFFER_COMPLETE }; virtual void update() = 0; - bool checkStatus() const; + bool checkStatus(GLenum target) const; GLFramebuffer(const std::weak_ptr& backend, const Framebuffer& framebuffer, GLuint id) : GLObject(backend, framebuffer, id) {} ~GLFramebuffer(); diff --git a/libraries/gpu-gl/src/gpu/gl/GLTexelFormat.cpp b/libraries/gpu-gl/src/gpu/gl/GLTexelFormat.cpp index 7e26e65e02..bd945cbaaa 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLTexelFormat.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLTexelFormat.cpp @@ -17,7 +17,6 @@ GLenum GLTexelFormat::evalGLTexelFormatInternal(const gpu::Element& dstFormat) { switch (dstFormat.getDimension()) { case gpu::SCALAR: { switch (dstFormat.getSemantic()) { - case gpu::RED: case gpu::RGB: case gpu::RGBA: case gpu::SRGB: @@ -263,7 +262,6 @@ GLTexelFormat GLTexelFormat::evalGLTexelFormat(const Element& dstFormat, const E texel.type = ELEMENT_TYPE_TO_GL[dstFormat.getType()]; switch (dstFormat.getSemantic()) { - case gpu::RED: case gpu::RGB: case gpu::RGBA: texel.internalFormat = GL_R8; @@ -274,10 +272,8 @@ GLTexelFormat GLTexelFormat::evalGLTexelFormat(const Element& dstFormat, const E break; case gpu::DEPTH: - texel.format = GL_DEPTH_COMPONENT; texel.internalFormat = GL_DEPTH_COMPONENT32; break; - case gpu::DEPTH_STENCIL: texel.type = GL_UNSIGNED_INT_24_8; texel.format = GL_DEPTH_STENCIL; @@ -407,7 +403,6 @@ GLTexelFormat GLTexelFormat::evalGLTexelFormat(const Element& dstFormat, const E texel.internalFormat = GL_COMPRESSED_RED_RGTC1; break; } - case gpu::RED: case gpu::RGB: case gpu::RGBA: case gpu::SRGB: diff --git a/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp b/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp index 1de820e1df..1e0dd08ae1 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp @@ -10,13 +10,15 @@ #include +#include "GLTextureTransfer.h" #include "GLBackend.h" using namespace gpu; using namespace gpu::gl; +std::shared_ptr GLTexture::_textureTransferHelper; -const GLenum GLTexture::CUBE_FACE_LAYOUT[GLTexture::TEXTURE_CUBE_NUM_FACES] = { +const GLenum GLTexture::CUBE_FACE_LAYOUT[6] = { GL_TEXTURE_CUBE_MAP_POSITIVE_X, GL_TEXTURE_CUBE_MAP_NEGATIVE_X, GL_TEXTURE_CUBE_MAP_POSITIVE_Y, GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, GL_TEXTURE_CUBE_MAP_POSITIVE_Z, GL_TEXTURE_CUBE_MAP_NEGATIVE_Z @@ -65,17 +67,6 @@ GLenum GLTexture::getGLTextureType(const Texture& texture) { } -uint8_t GLTexture::getFaceCount(GLenum target) { - switch (target) { - case GL_TEXTURE_2D: - return TEXTURE_2D_NUM_FACES; - case GL_TEXTURE_CUBE_MAP: - return TEXTURE_CUBE_NUM_FACES; - default: - Q_UNREACHABLE(); - break; - } -} const std::vector& GLTexture::getFaceTargets(GLenum target) { static std::vector cubeFaceTargets { GL_TEXTURE_CUBE_MAP_POSITIVE_X, GL_TEXTURE_CUBE_MAP_NEGATIVE_X, @@ -98,34 +89,216 @@ const std::vector& GLTexture::getFaceTargets(GLenum target) { return faceTargets; } +// Default texture memory = GPU total memory - 2GB +#define GPU_MEMORY_RESERVE_BYTES MB_TO_BYTES(2048) +// Minimum texture memory = 1GB +#define TEXTURE_MEMORY_MIN_BYTES MB_TO_BYTES(1024) + + +float GLTexture::getMemoryPressure() { + // Check for an explicit memory limit + auto availableTextureMemory = Texture::getAllowedGPUMemoryUsage(); + + + // If no memory limit has been set, use a percentage of the total dedicated memory + if (!availableTextureMemory) { +#if 0 + auto totalMemory = getDedicatedMemory(); + if ((GPU_MEMORY_RESERVE_BYTES + TEXTURE_MEMORY_MIN_BYTES) > totalMemory) { + availableTextureMemory = TEXTURE_MEMORY_MIN_BYTES; + } else { + availableTextureMemory = totalMemory - GPU_MEMORY_RESERVE_BYTES; + } +#else + // Hardcode texture limit for sparse textures at 1 GB for now + availableTextureMemory = TEXTURE_MEMORY_MIN_BYTES; +#endif + } + + // Return the consumed texture memory divided by the available texture memory. + auto consumedGpuMemory = Context::getTextureGPUMemoryUsage() - Context::getTextureGPUFramebufferMemoryUsage(); + float memoryPressure = (float)consumedGpuMemory / (float)availableTextureMemory; + static Context::Size lastConsumedGpuMemory = 0; + if (memoryPressure > 1.0f && lastConsumedGpuMemory != consumedGpuMemory) { + lastConsumedGpuMemory = consumedGpuMemory; + qCDebug(gpugllogging) << "Exceeded max allowed texture memory: " << consumedGpuMemory << " / " << availableTextureMemory; + } + return memoryPressure; +} + + +// Create the texture and allocate storage +GLTexture::GLTexture(const std::weak_ptr& backend, const Texture& texture, GLuint id, bool transferrable) : + GLObject(backend, texture, id), + _external(false), + _source(texture.source()), + _storageStamp(texture.getStamp()), + _target(getGLTextureType(texture)), + _internalFormat(gl::GLTexelFormat::evalGLTexelFormatInternal(texture.getTexelFormat())), + _maxMip(texture.maxMip()), + _minMip(texture.minMip()), + _virtualSize(texture.evalTotalSize()), + _transferrable(transferrable) +{ + auto strongBackend = _backend.lock(); + strongBackend->recycle(); + Backend::incrementTextureGPUCount(); + Backend::updateTextureGPUVirtualMemoryUsage(0, _virtualSize); + Backend::setGPUObject(texture, this); +} + GLTexture::GLTexture(const std::weak_ptr& backend, const Texture& texture, GLuint id) : GLObject(backend, texture, id), + _external(true), _source(texture.source()), - _target(getGLTextureType(texture)) + _storageStamp(0), + _target(getGLTextureType(texture)), + _internalFormat(GL_RGBA8), + // FIXME force mips to 0? + _maxMip(texture.maxMip()), + _minMip(texture.minMip()), + _virtualSize(0), + _transferrable(false) { Backend::setGPUObject(texture, this); + + // FIXME Is this necessary? + //withPreservedTexture([this] { + // syncSampler(); + // if (_gpuObject.isAutogenerateMips()) { + // generateMips(); + // } + //}); } GLTexture::~GLTexture() { - auto backend = _backend.lock(); - if (backend && _id) { - backend->releaseTexture(_id, 0); - } -} - - -GLExternalTexture::GLExternalTexture(const std::weak_ptr& backend, const Texture& texture, GLuint id) - : Parent(backend, texture, id) { } - -GLExternalTexture::~GLExternalTexture() { auto backend = _backend.lock(); if (backend) { - auto recycler = _gpuObject.getExternalRecycler(); - if (recycler) { - backend->releaseExternalTexture(_id, recycler); - } else { - qWarning() << "No recycler available for texture " << _id << " possible leak"; + if (_external) { + auto recycler = _gpuObject.getExternalRecycler(); + if (recycler) { + backend->releaseExternalTexture(_id, recycler); + } else { + qWarning() << "No recycler available for texture " << _id << " possible leak"; + } + } else if (_id) { + // WARNING! Sparse textures do not use this code path. See GL45BackendTexture for + // the GL45Texture destructor for doing any required work tracking GPU stats + backend->releaseTexture(_id, _size); } - const_cast(_id) = 0; + + if (!_external && !_transferrable) { + Backend::updateTextureGPUFramebufferMemoryUsage(_size, 0); + } + } + Backend::updateTextureGPUVirtualMemoryUsage(_virtualSize, 0); +} + +void GLTexture::createTexture() { + withPreservedTexture([&] { + allocateStorage(); + (void)CHECK_GL_ERROR(); + syncSampler(); + (void)CHECK_GL_ERROR(); + }); +} + +void GLTexture::withPreservedTexture(std::function f) const { + GLint boundTex = -1; + switch (_target) { + case GL_TEXTURE_2D: + glGetIntegerv(GL_TEXTURE_BINDING_2D, &boundTex); + break; + + case GL_TEXTURE_CUBE_MAP: + glGetIntegerv(GL_TEXTURE_BINDING_CUBE_MAP, &boundTex); + break; + + default: + qFatal("Unsupported texture type"); + } + (void)CHECK_GL_ERROR(); + + glBindTexture(_target, _texture); + f(); + glBindTexture(_target, boundTex); + (void)CHECK_GL_ERROR(); +} + +void GLTexture::setSize(GLuint size) const { + if (!_external && !_transferrable) { + Backend::updateTextureGPUFramebufferMemoryUsage(_size, size); + } + Backend::updateTextureGPUMemoryUsage(_size, size); + const_cast(_size) = size; +} + +bool GLTexture::isInvalid() const { + return _storageStamp < _gpuObject.getStamp(); +} + +bool GLTexture::isOutdated() const { + return GLSyncState::Idle == _syncState && _contentStamp < _gpuObject.getDataStamp(); +} + +bool GLTexture::isReady() const { + // If we have an invalid texture, we're never ready + if (isInvalid()) { + return false; + } + + auto syncState = _syncState.load(); + if (isOutdated() || Idle != syncState) { + return false; + } + + return true; +} + + +// Do any post-transfer operations that might be required on the main context / rendering thread +void GLTexture::postTransfer() { + setSyncState(GLSyncState::Idle); + ++_transferCount; + + // At this point the mip pixels have been loaded, we can notify the gpu texture to abandon it's memory + switch (_gpuObject.getType()) { + case Texture::TEX_2D: + for (uint16_t i = 0; i < Sampler::MAX_MIP_LEVEL; ++i) { + if (_gpuObject.isStoredMipFaceAvailable(i)) { + _gpuObject.notifyMipFaceGPULoaded(i); + } + } + break; + + case Texture::TEX_CUBE: + // transfer pixels from each faces + for (uint8_t f = 0; f < CUBE_NUM_FACES; f++) { + for (uint16_t i = 0; i < Sampler::MAX_MIP_LEVEL; ++i) { + if (_gpuObject.isStoredMipFaceAvailable(i, f)) { + _gpuObject.notifyMipFaceGPULoaded(i, f); + } + } + } + break; + + default: + qCWarning(gpugllogging) << __FUNCTION__ << " case for Texture Type " << _gpuObject.getType() << " not supported"; + break; } } + +void GLTexture::initTextureTransferHelper() { + _textureTransferHelper = std::make_shared(); +} + +void GLTexture::startTransfer() { + createTexture(); +} + +void GLTexture::finishTransfer() { + if (_gpuObject.isAutogenerateMips()) { + generateMips(); + } +} + diff --git a/libraries/gpu-gl/src/gpu/gl/GLTexture.h b/libraries/gpu-gl/src/gpu/gl/GLTexture.h index 1f91e17157..0f75a6fe51 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLTexture.h +++ b/libraries/gpu-gl/src/gpu/gl/GLTexture.h @@ -9,6 +9,7 @@ #define hifi_gpu_gl_GLTexture_h #include "GLShared.h" +#include "GLTextureTransfer.h" #include "GLBackend.h" #include "GLTexelFormat.h" @@ -19,47 +20,209 @@ struct GLFilterMode { GLint magFilter; }; + class GLTexture : public GLObject { - using Parent = GLObject; - friend class GLBackend; public: static const uint16_t INVALID_MIP { (uint16_t)-1 }; static const uint8_t INVALID_FACE { (uint8_t)-1 }; + static void initTextureTransferHelper(); + static std::shared_ptr _textureTransferHelper; + + template + static GLTexture* sync(GLBackend& backend, const TexturePointer& texturePointer, bool needTransfer) { + const Texture& texture = *texturePointer; + + // Special case external textures + if (texture.getUsage().isExternal()) { + Texture::ExternalUpdates updates = texture.getUpdates(); + if (!updates.empty()) { + Texture::ExternalRecycler recycler = texture.getExternalRecycler(); + Q_ASSERT(recycler); + // Discard any superfluous updates + while (updates.size() > 1) { + const auto& update = updates.front(); + // Superfluous updates will never have been read, but we want to ensure the previous + // writes to them are complete before they're written again, so return them with the + // same fences they arrived with. This can happen on any thread because no GL context + // work is involved + recycler(update.first, update.second); + updates.pop_front(); + } + + // The last texture remaining is the one we'll use to create the GLTexture + const auto& update = updates.front(); + // Check for a fence, and if it exists, inject a wait into the command stream, then destroy the fence + if (update.second) { + GLsync fence = static_cast(update.second); + glWaitSync(fence, 0, GL_TIMEOUT_IGNORED); + glDeleteSync(fence); + } + + // Create the new texture object (replaces any previous texture object) + new GLTextureType(backend.shared_from_this(), texture, update.first); + } + + // Return the texture object (if any) associated with the texture, without extensive logic + // (external textures are + return Backend::getGPUObject(texture); + } + + if (!texture.isDefined()) { + // NO texture definition yet so let's avoid thinking + return nullptr; + } + + // If the object hasn't been created, or the object definition is out of date, drop and re-create + GLTexture* object = Backend::getGPUObject(texture); + + // Create the texture if need be (force re-creation if the storage stamp changes + // for easier use of immutable storage) + if (!object || object->isInvalid()) { + // This automatically any previous texture + object = new GLTextureType(backend.shared_from_this(), texture, needTransfer); + if (!object->_transferrable) { + object->createTexture(); + object->_contentStamp = texture.getDataStamp(); + object->updateSize(); + object->postTransfer(); + } + } + + // Object maybe doens't neet to be tranasferred after creation + if (!object->_transferrable) { + return object; + } + + // If we just did a transfer, return the object after doing post-transfer work + if (GLSyncState::Transferred == object->getSyncState()) { + object->postTransfer(); + } + + if (object->isOutdated()) { + // Object might be outdated, if so, start the transfer + // (outdated objects that are already in transfer will have reported 'true' for ready() + _textureTransferHelper->transferTexture(texturePointer); + return nullptr; + } + + if (!object->isReady()) { + return nullptr; + } + + ((GLTexture*)object)->updateMips(); + + return object; + } + + template + static GLuint getId(GLBackend& backend, const TexturePointer& texture, bool shouldSync) { + if (!texture) { + return 0; + } + GLTexture* object { nullptr }; + if (shouldSync) { + object = sync(backend, texture, shouldSync); + } else { + object = Backend::getGPUObject(*texture); + } + + if (!object) { + return 0; + } + + if (!shouldSync) { + return object->_id; + } + + // Don't return textures that are in transfer state + if ((object->getSyncState() != GLSyncState::Idle) || + // Don't return transferrable textures that have never completed transfer + (!object->_transferrable || 0 != object->_transferCount)) { + return 0; + } + + return object->_id; + } + ~GLTexture(); + // Is this texture generated outside the GPU library? + const bool _external; const GLuint& _texture { _id }; const std::string _source; + const Stamp _storageStamp; const GLenum _target; + const GLenum _internalFormat; + const uint16 _maxMip; + uint16 _minMip; + const GLuint _virtualSize; // theoretical size as expected + Stamp _contentStamp { 0 }; + const bool _transferrable; + Size _transferCount { 0 }; + GLuint size() const { return _size; } + GLSyncState getSyncState() const { return _syncState; } - static const std::vector& getFaceTargets(GLenum textureType); - static uint8_t getFaceCount(GLenum textureType); - static GLenum getGLTextureType(const Texture& texture); + // Is the storage out of date relative to the gpu texture? + bool isInvalid() const; - static const uint8_t TEXTURE_2D_NUM_FACES = 1; - static const uint8_t TEXTURE_CUBE_NUM_FACES = 6; - static const GLenum CUBE_FACE_LAYOUT[TEXTURE_CUBE_NUM_FACES]; + // Is the content out of date relative to the gpu texture? + bool isOutdated() const; + + // Is the texture in a state where it can be rendered with no work? + bool isReady() const; + + // Execute any post-move operations that must occur only on the main thread + virtual void postTransfer(); + + uint16 usedMipLevels() const { return (_maxMip - _minMip) + 1; } + + static const size_t CUBE_NUM_FACES = 6; + static const GLenum CUBE_FACE_LAYOUT[6]; static const GLFilterMode FILTER_MODES[Sampler::NUM_FILTERS]; static const GLenum WRAP_MODES[Sampler::NUM_WRAP_MODES]; + // Return a floating point value indicating how much of the allowed + // texture memory we are currently consuming. A value of 0 indicates + // no texture memory usage, while a value of 1 indicates all available / allowed memory + // is consumed. A value above 1 indicates that there is a problem. + static float getMemoryPressure(); protected: - virtual uint32 size() const = 0; - virtual void generateMips() const = 0; + static const std::vector& getFaceTargets(GLenum textureType); + + static GLenum getGLTextureType(const Texture& texture); + + + const GLuint _size { 0 }; // true size as reported by the gl api + std::atomic _syncState { GLSyncState::Idle }; + + GLTexture(const std::weak_ptr& backend, const Texture& texture, GLuint id, bool transferrable); GLTexture(const std::weak_ptr& backend, const Texture& texture, GLuint id); -}; -class GLExternalTexture : public GLTexture { - using Parent = GLTexture; - friend class GLBackend; -public: - ~GLExternalTexture(); + void setSyncState(GLSyncState syncState) { _syncState = syncState; } + + void createTexture(); + + virtual void updateMips() {} + virtual void allocateStorage() const = 0; + virtual void updateSize() const = 0; + virtual void syncSampler() const = 0; + virtual void generateMips() const = 0; + virtual void withPreservedTexture(std::function f) const; + protected: - GLExternalTexture(const std::weak_ptr& backend, const Texture& texture, GLuint id); - void generateMips() const override {} - uint32 size() const override { return 0; } -}; + void setSize(GLuint size) const; + virtual void startTransfer(); + // Returns true if this is the last block required to complete transfer + virtual bool continueTransfer() { return false; } + virtual void finishTransfer(); + +private: + friend class GLTextureTransferHelper; + friend class GLBackend; +}; } } diff --git a/libraries/gpu-gl/src/gpu/gl/GLTextureTransfer.cpp b/libraries/gpu-gl/src/gpu/gl/GLTextureTransfer.cpp new file mode 100644 index 0000000000..9dac2986e3 --- /dev/null +++ b/libraries/gpu-gl/src/gpu/gl/GLTextureTransfer.cpp @@ -0,0 +1,208 @@ +// +// Created by Bradley Austin Davis on 2016/04/03 +// Copyright 2013-2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +#include "GLTextureTransfer.h" + +#include +#include + +#include + +#include "GLShared.h" +#include "GLTexture.h" + +#ifdef HAVE_NSIGHT +#include "nvToolsExt.h" +std::unordered_map _map; +#endif + + +#ifdef TEXTURE_TRANSFER_PBOS +#define TEXTURE_TRANSFER_BLOCK_SIZE (64 * 1024) +#define TEXTURE_TRANSFER_PBO_COUNT 128 +#endif + +using namespace gpu; +using namespace gpu::gl; + +GLTextureTransferHelper::GLTextureTransferHelper() { +#ifdef THREADED_TEXTURE_TRANSFER + setObjectName("TextureTransferThread"); + _context.create(); + initialize(true, QThread::LowPriority); + // Clean shutdown on UNIX, otherwise _canvas is freed early + connect(qApp, &QCoreApplication::aboutToQuit, [&] { terminate(); }); +#else + initialize(false, QThread::LowPriority); +#endif +} + +GLTextureTransferHelper::~GLTextureTransferHelper() { +#ifdef THREADED_TEXTURE_TRANSFER + if (isStillRunning()) { + terminate(); + } +#else + terminate(); +#endif +} + +void GLTextureTransferHelper::transferTexture(const gpu::TexturePointer& texturePointer) { + GLTexture* object = Backend::getGPUObject(*texturePointer); + + Backend::incrementTextureGPUTransferCount(); + object->setSyncState(GLSyncState::Pending); + Lock lock(_mutex); + _pendingTextures.push_back(texturePointer); +} + +void GLTextureTransferHelper::setup() { +#ifdef THREADED_TEXTURE_TRANSFER + _context.makeCurrent(); + +#ifdef TEXTURE_TRANSFER_FORCE_DRAW + // FIXME don't use opengl 4.5 DSA functionality without verifying it's present + glCreateRenderbuffers(1, &_drawRenderbuffer); + glNamedRenderbufferStorage(_drawRenderbuffer, GL_RGBA8, 128, 128); + glCreateFramebuffers(1, &_drawFramebuffer); + glNamedFramebufferRenderbuffer(_drawFramebuffer, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, _drawRenderbuffer); + glCreateFramebuffers(1, &_readFramebuffer); +#endif + +#ifdef TEXTURE_TRANSFER_PBOS + std::array pbos; + glCreateBuffers(TEXTURE_TRANSFER_PBO_COUNT, &pbos[0]); + for (uint32_t i = 0; i < TEXTURE_TRANSFER_PBO_COUNT; ++i) { + TextureTransferBlock newBlock; + newBlock._pbo = pbos[i]; + glNamedBufferStorage(newBlock._pbo, TEXTURE_TRANSFER_BLOCK_SIZE, 0, GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | GL_MAP_COHERENT_BIT); + newBlock._mapped = glMapNamedBufferRange(newBlock._pbo, 0, TEXTURE_TRANSFER_BLOCK_SIZE, GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | GL_MAP_COHERENT_BIT); + _readyQueue.push(newBlock); + } +#endif +#endif +} + +void GLTextureTransferHelper::shutdown() { +#ifdef THREADED_TEXTURE_TRANSFER + _context.makeCurrent(); +#endif + +#ifdef TEXTURE_TRANSFER_FORCE_DRAW + glNamedFramebufferRenderbuffer(_drawFramebuffer, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, 0); + glDeleteFramebuffers(1, &_drawFramebuffer); + _drawFramebuffer = 0; + glDeleteFramebuffers(1, &_readFramebuffer); + _readFramebuffer = 0; + + glNamedFramebufferTexture(_readFramebuffer, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0); + glDeleteRenderbuffers(1, &_drawRenderbuffer); + _drawRenderbuffer = 0; +#endif +} + +void GLTextureTransferHelper::queueExecution(VoidLambda lambda) { + Lock lock(_mutex); + _pendingCommands.push_back(lambda); +} + +#define MAX_TRANSFERS_PER_PASS 2 + +bool GLTextureTransferHelper::process() { + // Take any new textures or commands off the queue + VoidLambdaList pendingCommands; + TextureList newTransferTextures; + { + Lock lock(_mutex); + newTransferTextures.swap(_pendingTextures); + pendingCommands.swap(_pendingCommands); + } + + if (!pendingCommands.empty()) { + for (auto command : pendingCommands) { + command(); + } + glFlush(); + } + + if (!newTransferTextures.empty()) { + for (auto& texturePointer : newTransferTextures) { +#ifdef HAVE_NSIGHT + _map[texturePointer] = nvtxRangeStart("TextureTansfer"); +#endif + GLTexture* object = Backend::getGPUObject(*texturePointer); + object->startTransfer(); + _transferringTextures.push_back(texturePointer); + _textureIterator = _transferringTextures.begin(); + } + _transferringTextures.sort([](const gpu::TexturePointer& a, const gpu::TexturePointer& b)->bool { + return a->getSize() < b->getSize(); + }); + } + + // No transfers in progress, sleep + if (_transferringTextures.empty()) { +#ifdef THREADED_TEXTURE_TRANSFER + QThread::usleep(1); +#endif + return true; + } + PROFILE_COUNTER_IF_CHANGED(render_gpu_gl, "transferringTextures", int, (int) _transferringTextures.size()) + + static auto lastReport = usecTimestampNow(); + auto now = usecTimestampNow(); + auto lastReportInterval = now - lastReport; + if (lastReportInterval > USECS_PER_SECOND * 4) { + lastReport = now; + qCDebug(gpulogging) << "Texture list " << _transferringTextures.size(); + } + + size_t transferCount = 0; + for (_textureIterator = _transferringTextures.begin(); _textureIterator != _transferringTextures.end();) { + if (++transferCount > MAX_TRANSFERS_PER_PASS) { + break; + } + auto texture = *_textureIterator; + GLTexture* gltexture = Backend::getGPUObject(*texture); + if (gltexture->continueTransfer()) { + ++_textureIterator; + continue; + } + + gltexture->finishTransfer(); + +#ifdef TEXTURE_TRANSFER_FORCE_DRAW + // FIXME force a draw on the texture transfer thread before passing the texture to the main thread for use +#endif + +#ifdef THREADED_TEXTURE_TRANSFER + clientWait(); +#endif + gltexture->_contentStamp = gltexture->_gpuObject.getDataStamp(); + gltexture->updateSize(); + gltexture->setSyncState(gpu::gl::GLSyncState::Transferred); + Backend::decrementTextureGPUTransferCount(); +#ifdef HAVE_NSIGHT + // Mark the texture as transferred + nvtxRangeEnd(_map[texture]); + _map.erase(texture); +#endif + _textureIterator = _transferringTextures.erase(_textureIterator); + } + +#ifdef THREADED_TEXTURE_TRANSFER + if (!_transferringTextures.empty()) { + // Don't saturate the GPU + clientWait(); + } else { + // Don't saturate the CPU + QThread::msleep(1); + } +#endif + + return true; +} diff --git a/libraries/gpu-gl/src/gpu/gl/GLTextureTransfer.h b/libraries/gpu-gl/src/gpu/gl/GLTextureTransfer.h new file mode 100644 index 0000000000..a23c282fd4 --- /dev/null +++ b/libraries/gpu-gl/src/gpu/gl/GLTextureTransfer.h @@ -0,0 +1,78 @@ +// +// Created by Bradley Austin Davis on 2016/04/03 +// Copyright 2013-2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +#ifndef hifi_gpu_gl_GLTextureTransfer_h +#define hifi_gpu_gl_GLTextureTransfer_h + +#include +#include + +#include + +#include + +#include "GLShared.h" + +#ifdef Q_OS_WIN +#define THREADED_TEXTURE_TRANSFER +#endif + +#ifdef THREADED_TEXTURE_TRANSFER +// FIXME when sparse textures are enabled, it's harder to force a draw on the transfer thread +// also, the current draw code is implicitly using OpenGL 4.5 functionality +//#define TEXTURE_TRANSFER_FORCE_DRAW +// FIXME PBO's increase the complexity and don't seem to work reliably +//#define TEXTURE_TRANSFER_PBOS +#endif + +namespace gpu { namespace gl { + +using TextureList = std::list; +using TextureListIterator = TextureList::iterator; + +class GLTextureTransferHelper : public GenericThread { +public: + using VoidLambda = std::function; + using VoidLambdaList = std::list; + using Pointer = std::shared_ptr; + GLTextureTransferHelper(); + ~GLTextureTransferHelper(); + void transferTexture(const gpu::TexturePointer& texturePointer); + void queueExecution(VoidLambda lambda); + + void setup() override; + void shutdown() override; + bool process() override; + +private: +#ifdef THREADED_TEXTURE_TRANSFER + ::gl::OffscreenContext _context; +#endif + +#ifdef TEXTURE_TRANSFER_FORCE_DRAW + // Framebuffers / renderbuffers for forcing access to the texture on the transfer thread + GLuint _drawRenderbuffer { 0 }; + GLuint _drawFramebuffer { 0 }; + GLuint _readFramebuffer { 0 }; +#endif + + // A mutex for protecting items access on the render and transfer threads + Mutex _mutex; + // Commands that have been submitted for execution on the texture transfer thread + VoidLambdaList _pendingCommands; + // Textures that have been submitted for transfer + TextureList _pendingTextures; + // Textures currently in the transfer process + // Only used on the transfer thread + TextureList _transferringTextures; + TextureListIterator _textureIterator; + +}; + +} } + +#endif \ No newline at end of file diff --git a/libraries/gpu-gl/src/gpu/gl41/GL41Backend.h b/libraries/gpu-gl/src/gpu/gl41/GL41Backend.h index 6d2f91c436..72e2f5a804 100644 --- a/libraries/gpu-gl/src/gpu/gl41/GL41Backend.h +++ b/libraries/gpu-gl/src/gpu/gl41/GL41Backend.h @@ -40,28 +40,18 @@ public: class GL41Texture : public GLTexture { using Parent = GLTexture; - static GLuint allocate(); - + GLuint allocate(); public: - ~GL41Texture(); - - private: - GL41Texture(const std::weak_ptr& backend, const Texture& buffer); + GL41Texture(const std::weak_ptr& backend, const Texture& buffer, GLuint externalId); + GL41Texture(const std::weak_ptr& backend, const Texture& buffer, bool transferrable); + protected: + void transferMip(uint16_t mipLevel, uint8_t face) const; + void startTransfer() override; + void allocateStorage() const override; + void updateSize() const override; + void syncSampler() const override; void generateMips() const override; - uint32 size() const override; - - friend class GL41Backend; - const Stamp _storageStamp; - mutable Stamp _contentStamp { 0 }; - mutable Stamp _samplerStamp { 0 }; - const uint32 _size; - - - bool isOutdated() const; - void withPreservedTexture(std::function f) const; - void syncContent() const; - void syncSampler() const; }; @@ -72,7 +62,8 @@ protected: GLuint getBufferID(const Buffer& buffer) override; GLBuffer* syncGPUObject(const Buffer& buffer) override; - GLTexture* syncGPUObject(const TexturePointer& texture) override; + GLuint getTextureID(const TexturePointer& texture, bool needTransfer = true) override; + GLTexture* syncGPUObject(const TexturePointer& texture, bool sync = true) override; GLuint getQueryID(const QueryPointer& query) override; GLQuery* syncGPUObject(const Query& query) override; diff --git a/libraries/gpu-gl/src/gpu/gl41/GL41BackendOutput.cpp b/libraries/gpu-gl/src/gpu/gl41/GL41BackendOutput.cpp index 195b155bf3..6d11a52035 100644 --- a/libraries/gpu-gl/src/gpu/gl41/GL41BackendOutput.cpp +++ b/libraries/gpu-gl/src/gpu/gl41/GL41BackendOutput.cpp @@ -53,12 +53,10 @@ public: GL_COLOR_ATTACHMENT15 }; int unit = 0; - auto backend = _backend.lock(); for (auto& b : _gpuObject.getRenderBuffers()) { surface = b._texture; if (surface) { - Q_ASSERT(TextureUsageType::RENDERBUFFER == surface->getUsageType()); - gltexture = backend->syncGPUObject(surface); + gltexture = gl::GLTexture::sync(*_backend.lock().get(), surface, false); // Grab the gltexture and don't transfer } else { gltexture = nullptr; } @@ -83,11 +81,9 @@ public: } if (_gpuObject.getDepthStamp() != _depthStamp) { - auto backend = _backend.lock(); auto surface = _gpuObject.getDepthStencilBuffer(); if (_gpuObject.hasDepthStencil() && surface) { - Q_ASSERT(TextureUsageType::RENDERBUFFER == surface->getUsageType()); - gltexture = backend->syncGPUObject(surface); + gltexture = gl::GLTexture::sync(*_backend.lock().get(), surface, false); // Grab the gltexture and don't transfer } if (gltexture) { @@ -114,7 +110,7 @@ public: glBindFramebuffer(GL_DRAW_FRAMEBUFFER, currentFBO); } - checkStatus(); + checkStatus(GL_DRAW_FRAMEBUFFER); } diff --git a/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp b/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp index 8dbef09f06..65c45111db 100644 --- a/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp @@ -29,102 +29,20 @@ GLuint GL41Texture::allocate() { return result; } -GLTexture* GL41Backend::syncGPUObject(const TexturePointer& texturePointer) { - if (!texturePointer) { - return nullptr; - } - const Texture& texture = *texturePointer; - if (TextureUsageType::EXTERNAL == texture.getUsageType()) { - return Parent::syncGPUObject(texturePointer); - } - - if (!texture.isDefined()) { - // NO texture definition yet so let's avoid thinking - return nullptr; - } - - // If the object hasn't been created, or the object definition is out of date, drop and re-create - GL41Texture* object = Backend::getGPUObject(texture); - if (!object || object->_storageStamp < texture.getStamp()) { - // This automatically any previous texture - object = new GL41Texture(shared_from_this(), texture); - } - - // FIXME internalize to GL41Texture 'sync' function - if (object->isOutdated()) { - object->withPreservedTexture([&] { - if (object->_contentStamp <= texture.getDataStamp()) { - // FIXME implement synchronous texture transfer here - object->syncContent(); - } - - if (object->_samplerStamp <= texture.getSamplerStamp()) { - object->syncSampler(); - } - }); - } - - return object; +GLuint GL41Backend::getTextureID(const TexturePointer& texture, bool transfer) { + return GL41Texture::getId(*this, texture, transfer); } -GL41Texture::GL41Texture(const std::weak_ptr& backend, const Texture& texture) - : GLTexture(backend, texture, allocate()), _storageStamp { texture.getStamp() }, _size(texture.evalTotalSize()) { - incrementTextureGPUCount(); - withPreservedTexture([&] { - GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(_gpuObject.getTexelFormat(), _gpuObject.getStoredMipFormat()); - auto numMips = _gpuObject.evalNumMips(); - for (uint16_t mipLevel = 0; mipLevel < numMips; ++mipLevel) { - // Get the mip level dimensions, accounting for the downgrade level - Vec3u dimensions = _gpuObject.evalMipDimensions(mipLevel); - uint8_t face = 0; - for (GLenum target : getFaceTargets(_target)) { - const Byte* mipData = nullptr; - if (_gpuObject.isStoredMipFaceAvailable(mipLevel, face)) { - auto mip = _gpuObject.accessStoredMipFace(mipLevel, face); - mipData = mip->readData(); - } - glTexImage2D(target, mipLevel, texelFormat.internalFormat, dimensions.x, dimensions.y, 0, texelFormat.format, texelFormat.type, mipData); - (void)CHECK_GL_ERROR(); - ++face; - } - } - }); +GLTexture* GL41Backend::syncGPUObject(const TexturePointer& texture, bool transfer) { + return GL41Texture::sync(*this, texture, transfer); } -GL41Texture::~GL41Texture() { - +GL41Texture::GL41Texture(const std::weak_ptr& backend, const Texture& texture, GLuint externalId) + : GLTexture(backend, texture, externalId) { } -bool GL41Texture::isOutdated() const { - if (_samplerStamp <= _gpuObject.getSamplerStamp()) { - return true; - } - if (TextureUsageType::RESOURCE == _gpuObject.getUsageType() && _contentStamp <= _gpuObject.getDataStamp()) { - return true; - } - return false; -} - -void GL41Texture::withPreservedTexture(std::function f) const { - GLint boundTex = -1; - switch (_target) { - case GL_TEXTURE_2D: - glGetIntegerv(GL_TEXTURE_BINDING_2D, &boundTex); - break; - - case GL_TEXTURE_CUBE_MAP: - glGetIntegerv(GL_TEXTURE_BINDING_CUBE_MAP, &boundTex); - break; - - default: - qFatal("Unsupported texture type"); - } - (void)CHECK_GL_ERROR(); - - glBindTexture(_target, _texture); - f(); - glBindTexture(_target, boundTex); - (void)CHECK_GL_ERROR(); +GL41Texture::GL41Texture(const std::weak_ptr& backend, const Texture& texture, bool transferrable) + : GLTexture(backend, texture, allocate(), transferrable) { } void GL41Texture::generateMips() const { @@ -134,12 +52,94 @@ void GL41Texture::generateMips() const { (void)CHECK_GL_ERROR(); } -void GL41Texture::syncContent() const { - // FIXME actually copy the texture data - _contentStamp = _gpuObject.getDataStamp() + 1; +void GL41Texture::allocateStorage() const { + GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(_gpuObject.getTexelFormat()); + glTexParameteri(_target, GL_TEXTURE_BASE_LEVEL, 0); + (void)CHECK_GL_ERROR(); + glTexParameteri(_target, GL_TEXTURE_MAX_LEVEL, _maxMip - _minMip); + (void)CHECK_GL_ERROR(); + if (GLEW_VERSION_4_2 && !_gpuObject.getTexelFormat().isCompressed()) { + // Get the dimensions, accounting for the downgrade level + Vec3u dimensions = _gpuObject.evalMipDimensions(_minMip); + glTexStorage2D(_target, usedMipLevels(), texelFormat.internalFormat, dimensions.x, dimensions.y); + (void)CHECK_GL_ERROR(); + } else { + for (uint16_t l = _minMip; l <= _maxMip; l++) { + // Get the mip level dimensions, accounting for the downgrade level + Vec3u dimensions = _gpuObject.evalMipDimensions(l); + for (GLenum target : getFaceTargets(_target)) { + glTexImage2D(target, l - _minMip, texelFormat.internalFormat, dimensions.x, dimensions.y, 0, texelFormat.format, texelFormat.type, NULL); + (void)CHECK_GL_ERROR(); + } + } + } } -void GL41Texture::syncSampler() const { +void GL41Texture::updateSize() const { + setSize(_virtualSize); + if (!_id) { + return; + } + + if (_gpuObject.getTexelFormat().isCompressed()) { + GLenum proxyType = GL_TEXTURE_2D; + GLuint numFaces = 1; + if (_gpuObject.getType() == gpu::Texture::TEX_CUBE) { + proxyType = CUBE_FACE_LAYOUT[0]; + numFaces = (GLuint)CUBE_NUM_FACES; + } + GLint gpuSize{ 0 }; + glGetTexLevelParameteriv(proxyType, 0, GL_TEXTURE_COMPRESSED, &gpuSize); + (void)CHECK_GL_ERROR(); + + if (gpuSize) { + for (GLuint level = _minMip; level < _maxMip; level++) { + GLint levelSize{ 0 }; + glGetTexLevelParameteriv(proxyType, level, GL_TEXTURE_COMPRESSED_IMAGE_SIZE, &levelSize); + levelSize *= numFaces; + + if (levelSize <= 0) { + break; + } + gpuSize += levelSize; + } + (void)CHECK_GL_ERROR(); + setSize(gpuSize); + return; + } + } +} + +// Move content bits from the CPU to the GPU for a given mip / face +void GL41Texture::transferMip(uint16_t mipLevel, uint8_t face) const { + auto mip = _gpuObject.accessStoredMipFace(mipLevel, face); + GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(_gpuObject.getTexelFormat(), mip->getFormat()); + //GLenum target = getFaceTargets()[face]; + GLenum target = _target == GL_TEXTURE_2D ? GL_TEXTURE_2D : CUBE_FACE_LAYOUT[face]; + auto size = _gpuObject.evalMipDimensions(mipLevel); + glTexSubImage2D(target, mipLevel, 0, 0, size.x, size.y, texelFormat.format, texelFormat.type, mip->readData()); + (void)CHECK_GL_ERROR(); +} + +void GL41Texture::startTransfer() { + PROFILE_RANGE(render_gpu_gl, __FUNCTION__); + Parent::startTransfer(); + + glBindTexture(_target, _id); + (void)CHECK_GL_ERROR(); + + // transfer pixels from each faces + uint8_t numFaces = (Texture::TEX_CUBE == _gpuObject.getType()) ? CUBE_NUM_FACES : 1; + for (uint8_t f = 0; f < numFaces; f++) { + for (uint16_t i = 0; i < Sampler::MAX_MIP_LEVEL; ++i) { + if (_gpuObject.isStoredMipFaceAvailable(i, f)) { + transferMip(i, f); + } + } + } +} + +void GL41Backend::GL41Texture::syncSampler() const { const Sampler& sampler = _gpuObject.getSampler(); const auto& fm = FILTER_MODES[sampler.getFilter()]; glTexParameteri(_target, GL_TEXTURE_MIN_FILTER, fm.minFilter); @@ -161,9 +161,5 @@ void GL41Texture::syncSampler() const { glTexParameterf(_target, GL_TEXTURE_MIN_LOD, (float)sampler.getMinMip()); glTexParameterf(_target, GL_TEXTURE_MAX_LOD, (sampler.getMaxMip() == Sampler::MAX_MIP_LEVEL ? 1000.f : sampler.getMaxMip())); glTexParameterf(_target, GL_TEXTURE_MAX_ANISOTROPY_EXT, sampler.getMaxAnisotropy()); - _samplerStamp = _gpuObject.getSamplerStamp() + 1; } -uint32 GL41Texture::size() const { - return _size; -} diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.cpp index 12c4b818f7..d7dde8b7d6 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.cpp @@ -18,12 +18,6 @@ Q_LOGGING_CATEGORY(gpugl45logging, "hifi.gpu.gl45") using namespace gpu; using namespace gpu::gl45; -void GL45Backend::recycle() const { - Parent::recycle(); - GL45VariableAllocationTexture::manageMemory(); - GL45VariableAllocationTexture::_frameTexturesCreated = 0; -} - void GL45Backend::do_draw(const Batch& batch, size_t paramOffset) { Primitive primitiveType = (Primitive)batch._params[paramOffset + 2]._uint; GLenum mode = gl::PRIMITIVE_TO_GL[primitiveType]; @@ -169,3 +163,8 @@ void GL45Backend::do_multiDrawIndexedIndirect(const Batch& batch, size_t paramOf _stats._DSNumAPIDrawcalls++; (void)CHECK_GL_ERROR(); } + +void GL45Backend::recycle() const { + Parent::recycle(); + derezTextures(); +} diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h index 6a9811b055..2242bba5d9 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h +++ b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h @@ -8,21 +8,17 @@ // 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_gpu_45_GL45Backend_h #define hifi_gpu_45_GL45Backend_h #include "../gl/GLBackend.h" #include "../gl/GLTexture.h" -#include #define INCREMENTAL_TRANSFER 0 -#define THREADED_TEXTURE_BUFFERING 1 namespace gpu { namespace gl45 { using namespace gpu::gl; -using TextureWeakPointer = std::weak_ptr; class GL45Backend : public GLBackend { using Parent = GLBackend; @@ -35,219 +31,60 @@ public: class GL45Texture : public GLTexture { using Parent = GLTexture; - friend class GL45Backend; static GLuint allocate(const Texture& texture); - protected: - GL45Texture(const std::weak_ptr& backend, const Texture& texture); - void generateMips() const override; - void copyMipFaceFromTexture(uint16_t sourceMip, uint16_t targetMip, uint8_t face) const; - void copyMipFaceLinesFromTexture(uint16_t mip, uint8_t face, const uvec3& size, uint32_t yOffset, GLenum format, GLenum type, const void* sourcePointer) const; - virtual void syncSampler() const; - }; - - // - // Textures that have fixed allocation sizes and cannot be managed at runtime - // - - class GL45FixedAllocationTexture : public GL45Texture { - using Parent = GL45Texture; - friend class GL45Backend; - - public: - GL45FixedAllocationTexture(const std::weak_ptr& backend, const Texture& texture); - ~GL45FixedAllocationTexture(); - - protected: - uint32 size() const override { return _size; } - void allocateStorage() const; - void syncSampler() const override; - const uint32 _size { 0 }; - }; - - class GL45AttachmentTexture : public GL45FixedAllocationTexture { - using Parent = GL45FixedAllocationTexture; - friend class GL45Backend; - protected: - GL45AttachmentTexture(const std::weak_ptr& backend, const Texture& texture); - ~GL45AttachmentTexture(); - }; - - class GL45StrictResourceTexture : public GL45FixedAllocationTexture { - using Parent = GL45FixedAllocationTexture; - friend class GL45Backend; - protected: - GL45StrictResourceTexture(const std::weak_ptr& backend, const Texture& texture); - }; - - // - // Textures that can be managed at runtime to increase or decrease their memory load - // - - class GL45VariableAllocationTexture : public GL45Texture { - using Parent = GL45Texture; - friend class GL45Backend; - using PromoteLambda = std::function; - - public: - enum class MemoryPressureState { - Idle, - Transfer, - Oversubscribed, - Undersubscribed, - }; - - using QueuePair = std::pair; - struct QueuePairLess { - bool operator()(const QueuePair& a, const QueuePair& b) { - return a.second < b.second; - } - }; - using WorkQueue = std::priority_queue, QueuePairLess>; - - class TransferJob { - using VoidLambda = std::function; - using VoidLambdaQueue = std::queue; - using ThreadPointer = std::shared_ptr; - const GL45VariableAllocationTexture& _parent; - // Holds the contents to transfer to the GPU in CPU memory - std::vector _buffer; - // Indicates if a transfer from backing storage to interal storage has started - bool _bufferingStarted { false }; - bool _bufferingCompleted { false }; - VoidLambda _transferLambda; - VoidLambda _bufferingLambda; -#if THREADED_TEXTURE_BUFFERING - static Mutex _mutex; - static VoidLambdaQueue _bufferLambdaQueue; - static ThreadPointer _bufferThread; - static std::atomic _shutdownBufferingThread; - static void bufferLoop(); -#endif - - public: - TransferJob(const TransferJob& other) = delete; - TransferJob(const GL45VariableAllocationTexture& parent, std::function transferLambda); - TransferJob(const GL45VariableAllocationTexture& parent, uint16_t sourceMip, uint16_t targetMip, uint8_t face, uint32_t lines = 0, uint32_t lineOffset = 0); - ~TransferJob(); - bool tryTransfer(); - -#if THREADED_TEXTURE_BUFFERING - static void startTransferLoop(); - static void stopTransferLoop(); -#endif - - private: - size_t _transferSize { 0 }; -#if THREADED_TEXTURE_BUFFERING - void startBuffering(); -#endif - void transfer(); - }; - - using TransferQueue = std::queue>; - static MemoryPressureState _memoryPressureState; - protected: - static size_t _frameTexturesCreated; - static std::atomic _memoryPressureStateStale; - static std::list _memoryManagedTextures; - static WorkQueue _transferQueue; - static WorkQueue _promoteQueue; - static WorkQueue _demoteQueue; - static TexturePointer _currentTransferTexture; - static const uvec3 INITIAL_MIP_TRANSFER_DIMENSIONS; - - - static void updateMemoryPressure(); - static void processWorkQueues(); - static void addMemoryManagedTexture(const TexturePointer& texturePointer); - static void addToWorkQueue(const TexturePointer& texture); - static WorkQueue& getActiveWorkQueue(); - - static void manageMemory(); - - protected: - GL45VariableAllocationTexture(const std::weak_ptr& backend, const Texture& texture); - ~GL45VariableAllocationTexture(); - //bool canPromoteNoAllocate() const { return _allocatedMip < _populatedMip; } - bool canPromote() const { return _allocatedMip > 0; } - bool canDemote() const { return _allocatedMip < _maxAllocatedMip; } - bool hasPendingTransfers() const { return _populatedMip > _allocatedMip; } - void executeNextTransfer(const TexturePointer& currentTexture); - uint32 size() const override { return _size; } - virtual void populateTransferQueue() = 0; - virtual void promote() = 0; - virtual void demote() = 0; - - // The allocated mip level, relative to the number of mips in the gpu::Texture object - // The relationship between a given glMip to the original gpu::Texture mip is always - // glMip + _allocatedMip - uint16 _allocatedMip { 0 }; - // The populated mip level, relative to the number of mips in the gpu::Texture object - // This must always be >= the allocated mip - uint16 _populatedMip { 0 }; - // The highest (lowest resolution) mip that we will support, relative to the number - // of mips in the gpu::Texture object - uint16 _maxAllocatedMip { 0 }; - uint32 _size { 0 }; - // Contains a series of lambdas that when executed will transfer data to the GPU, modify - // the _populatedMip and update the sampler in order to fully populate the allocated texture - // until _populatedMip == _allocatedMip - TransferQueue _pendingTransfers; - }; - - class GL45ResourceTexture : public GL45VariableAllocationTexture { - using Parent = GL45VariableAllocationTexture; - friend class GL45Backend; - protected: - GL45ResourceTexture(const std::weak_ptr& backend, const Texture& texture); - - void syncSampler() const override; - void promote() override; - void demote() override; - void populateTransferQueue() override; - - void allocateStorage(uint16 mip); - void copyMipsFromTexture(); - }; - -#if 0 - class GL45SparseResourceTexture : public GL45VariableAllocationTexture { - using Parent = GL45VariableAllocationTexture; - friend class GL45Backend; - using TextureTypeFormat = std::pair; - using PageDimensions = std::vector; - using PageDimensionsMap = std::map; - static PageDimensionsMap pageDimensionsByFormat; - static Mutex pageDimensionsMutex; - - static bool isSparseEligible(const Texture& texture); - static PageDimensions getPageDimensionsForFormat(const TextureTypeFormat& typeFormat); - static PageDimensions getPageDimensionsForFormat(GLenum type, GLenum format); static const uint32_t DEFAULT_PAGE_DIMENSION = 128; static const uint32_t DEFAULT_MAX_SPARSE_LEVEL = 0xFFFF; + public: + GL45Texture(const std::weak_ptr& backend, const Texture& texture, GLuint externalId); + GL45Texture(const std::weak_ptr& backend, const Texture& texture, bool transferrable); + ~GL45Texture(); + + void postTransfer() override; + + struct SparseInfo { + SparseInfo(GL45Texture& texture); + void maybeMakeSparse(); + void update(); + uvec3 getPageCounts(const uvec3& dimensions) const; + uint32_t getPageCount(const uvec3& dimensions) const; + uint32_t getSize() const; + + GL45Texture& texture; + bool sparse { false }; + uvec3 pageDimensions { DEFAULT_PAGE_DIMENSION }; + GLuint maxSparseLevel { DEFAULT_MAX_SPARSE_LEVEL }; + uint32_t allocatedPages { 0 }; + uint32_t maxPages { 0 }; + uint32_t pageBytes { 0 }; + GLint pageDimensionsIndex { 0 }; + }; + protected: - GL45SparseResourceTexture(const std::weak_ptr& backend, const Texture& texture); - ~GL45SparseResourceTexture(); - uint32 size() const override { return _allocatedPages * _pageBytes; } - void promote() override; - void demote() override; + void updateMips() override; + void stripToMip(uint16_t newMinMip); + void startTransfer() override; + bool continueTransfer() override; + void finishTransfer() override; + void incrementalTransfer(const uvec3& size, const gpu::Texture::PixelsPointer& mip, std::function f) const; + void transferMip(uint16_t mipLevel, uint8_t face = 0) const; + void allocateMip(uint16_t mipLevel, uint8_t face = 0) const; + void allocateStorage() const override; + void updateSize() const override; + void syncSampler() const override; + void generateMips() const override; + void withPreservedTexture(std::function f) const override; + void derez(); - private: - uvec3 getPageCounts(const uvec3& dimensions) const; - uint32_t getPageCount(const uvec3& dimensions) const; - - uint32_t _allocatedPages { 0 }; - uint32_t _pageBytes { 0 }; - uvec3 _pageDimensions { DEFAULT_PAGE_DIMENSION }; - GLuint _maxSparseLevel { DEFAULT_MAX_SPARSE_LEVEL }; + SparseInfo _sparseInfo; + uint16_t _mipOffset { 0 }; + friend class GL45Backend; }; -#endif protected: - void recycle() const override; + void derezTextures() const; GLuint getFramebufferID(const FramebufferPointer& framebuffer) override; GLFramebuffer* syncGPUObject(const Framebuffer& framebuffer) override; @@ -255,7 +92,8 @@ protected: GLuint getBufferID(const Buffer& buffer) override; GLBuffer* syncGPUObject(const Buffer& buffer) override; - GLTexture* syncGPUObject(const TexturePointer& texture) override; + GLuint getTextureID(const TexturePointer& texture, bool needTransfer = true) override; + GLTexture* syncGPUObject(const TexturePointer& texture, bool sync = true) override; GLuint getQueryID(const QueryPointer& query) override; GLQuery* syncGPUObject(const Query& query) override; @@ -288,5 +126,5 @@ protected: Q_DECLARE_LOGGING_CATEGORY(gpugl45logging) -#endif +#endif diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendOutput.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendOutput.cpp index 9648af9b21..c5b84b7deb 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendOutput.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendOutput.cpp @@ -49,12 +49,10 @@ public: GL_COLOR_ATTACHMENT15 }; int unit = 0; - auto backend = _backend.lock(); for (auto& b : _gpuObject.getRenderBuffers()) { surface = b._texture; if (surface) { - Q_ASSERT(TextureUsageType::RENDERBUFFER == surface->getUsageType()); - gltexture = backend->syncGPUObject(surface); + gltexture = gl::GLTexture::sync(*_backend.lock().get(), surface, false); // Grab the gltexture and don't transfer } else { gltexture = nullptr; } @@ -80,10 +78,8 @@ public: if (_gpuObject.getDepthStamp() != _depthStamp) { auto surface = _gpuObject.getDepthStencilBuffer(); - auto backend = _backend.lock(); if (_gpuObject.hasDepthStencil() && surface) { - Q_ASSERT(TextureUsageType::RENDERBUFFER == surface->getUsageType()); - gltexture = backend->syncGPUObject(surface); + gltexture = gl::GLTexture::sync(*_backend.lock().get(), surface, false); // Grab the gltexture and don't transfer } if (gltexture) { @@ -106,7 +102,7 @@ public: _status = glCheckNamedFramebufferStatus(_id, GL_DRAW_FRAMEBUFFER); // restore the current framebuffer - checkStatus(); + checkStatus(GL_DRAW_FRAMEBUFFER); } diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp index 36aaf75e81..6948a045a2 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp @@ -8,10 +8,9 @@ // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // - #include "GL45Backend.h" + #include -#include #include #include #include @@ -20,70 +19,142 @@ #include #include -#include #include "../gl/GLTexelFormat.h" using namespace gpu; using namespace gpu::gl; using namespace gpu::gl45; -#define SPARSE_PAGE_SIZE_OVERHEAD_ESTIMATE 1.3f -#define MAX_RESOURCE_TEXTURES_PER_FRAME 2 +// Allocate 1 MB of buffer space for paged transfers +#define DEFAULT_PAGE_BUFFER_SIZE (1024*1024) +#define DEFAULT_GL_PIXEL_ALIGNMENT 4 -GLTexture* GL45Backend::syncGPUObject(const TexturePointer& texturePointer) { - if (!texturePointer) { - return nullptr; +using GL45Texture = GL45Backend::GL45Texture; + +static std::map> texturesByMipCounts; +static Mutex texturesByMipCountsMutex; +using TextureTypeFormat = std::pair; +std::map> sparsePageDimensionsByFormat; +Mutex sparsePageDimensionsByFormatMutex; + +static std::vector getPageDimensionsForFormat(const TextureTypeFormat& typeFormat) { + { + Lock lock(sparsePageDimensionsByFormatMutex); + if (sparsePageDimensionsByFormat.count(typeFormat)) { + return sparsePageDimensionsByFormat[typeFormat]; + } } + GLint count = 0; + glGetInternalformativ(typeFormat.first, typeFormat.second, GL_NUM_VIRTUAL_PAGE_SIZES_ARB, 1, &count); - const Texture& texture = *texturePointer; - if (TextureUsageType::EXTERNAL == texture.getUsageType()) { - return Parent::syncGPUObject(texturePointer); - } + std::vector result; + if (count > 0) { + std::vector x, y, z; + x.resize(count); + glGetInternalformativ(typeFormat.first, typeFormat.second, GL_VIRTUAL_PAGE_SIZE_X_ARB, 1, &x[0]); + y.resize(count); + glGetInternalformativ(typeFormat.first, typeFormat.second, GL_VIRTUAL_PAGE_SIZE_Y_ARB, 1, &y[0]); + z.resize(count); + glGetInternalformativ(typeFormat.first, typeFormat.second, GL_VIRTUAL_PAGE_SIZE_Z_ARB, 1, &z[0]); - if (!texture.isDefined()) { - // NO texture definition yet so let's avoid thinking - return nullptr; - } - - GL45Texture* object = Backend::getGPUObject(texture); - if (!object) { - switch (texture.getUsageType()) { - case TextureUsageType::RENDERBUFFER: - object = new GL45AttachmentTexture(shared_from_this(), texture); - break; - - case TextureUsageType::STRICT_RESOURCE: - qCDebug(gpugllogging) << "Strict texture " << texture.source().c_str(); - object = new GL45StrictResourceTexture(shared_from_this(), texture); - break; - - case TextureUsageType::RESOURCE: { - if (GL45VariableAllocationTexture::_frameTexturesCreated < MAX_RESOURCE_TEXTURES_PER_FRAME) { -#if 0 - if (isTextureManagementSparseEnabled() && GL45Texture::isSparseEligible(texture)) { - object = new GL45SparseResourceTexture(shared_from_this(), texture); - } else { - object = new GL45ResourceTexture(shared_from_this(), texture); - } -#else - object = new GL45ResourceTexture(shared_from_this(), texture); -#endif - GL45VariableAllocationTexture::addMemoryManagedTexture(texturePointer); - } else { - auto fallback = texturePointer->getFallbackTexture(); - if (fallback) { - object = static_cast(syncGPUObject(fallback)); - } - } - break; - } - - default: - Q_UNREACHABLE(); + result.resize(count); + for (GLint i = 0; i < count; ++i) { + result[i] = uvec3(x[i], y[i], z[i]); } } - return object; + { + Lock lock(sparsePageDimensionsByFormatMutex); + if (0 == sparsePageDimensionsByFormat.count(typeFormat)) { + sparsePageDimensionsByFormat[typeFormat] = result; + } + } + + return result; +} + +static std::vector getPageDimensionsForFormat(GLenum target, GLenum format) { + return getPageDimensionsForFormat({ target, format }); +} + +GLTexture* GL45Backend::syncGPUObject(const TexturePointer& texture, bool transfer) { + return GL45Texture::sync(*this, texture, transfer); +} + +using SparseInfo = GL45Backend::GL45Texture::SparseInfo; + +SparseInfo::SparseInfo(GL45Texture& texture) + : texture(texture) { +} + +void SparseInfo::maybeMakeSparse() { + // Don't enable sparse for objects with explicitly managed mip levels + if (!texture._gpuObject.isAutogenerateMips()) { + return; + } + return; + + const uvec3 dimensions = texture._gpuObject.getDimensions(); + auto allowedPageDimensions = getPageDimensionsForFormat(texture._target, texture._internalFormat); + // In order to enable sparse the texture size must be an integer multiple of the page size + for (size_t i = 0; i < allowedPageDimensions.size(); ++i) { + pageDimensionsIndex = (uint32_t) i; + pageDimensions = allowedPageDimensions[i]; + // Is this texture an integer multiple of page dimensions? + if (uvec3(0) == (dimensions % pageDimensions)) { + qCDebug(gpugl45logging) << "Enabling sparse for texture " << texture._source.c_str(); + sparse = true; + break; + } + } + + if (sparse) { + glTextureParameteri(texture._id, GL_TEXTURE_SPARSE_ARB, GL_TRUE); + glTextureParameteri(texture._id, GL_VIRTUAL_PAGE_SIZE_INDEX_ARB, pageDimensionsIndex); + } else { + qCDebug(gpugl45logging) << "Size " << dimensions.x << " x " << dimensions.y << + " is not supported by any sparse page size for texture" << texture._source.c_str(); + } +} + +#define SPARSE_PAGE_SIZE_OVERHEAD_ESTIMATE 1.3f + +// This can only be called after we've established our storage size +void SparseInfo::update() { + if (!sparse) { + return; + } + glGetTextureParameterIuiv(texture._id, GL_NUM_SPARSE_LEVELS_ARB, &maxSparseLevel); + pageBytes = texture._gpuObject.getTexelFormat().getSize(); + pageBytes *= pageDimensions.x * pageDimensions.y * pageDimensions.z; + // Testing with a simple texture allocating app shows an estimated 20% GPU memory overhead for + // sparse textures as compared to non-sparse, so we acount for that here. + pageBytes = (uint32_t)(pageBytes * SPARSE_PAGE_SIZE_OVERHEAD_ESTIMATE); + + for (uint16_t mipLevel = 0; mipLevel <= maxSparseLevel; ++mipLevel) { + auto mipDimensions = texture._gpuObject.evalMipDimensions(mipLevel); + auto mipPageCount = getPageCount(mipDimensions); + maxPages += mipPageCount; + } + if (texture._target == GL_TEXTURE_CUBE_MAP) { + maxPages *= GLTexture::CUBE_NUM_FACES; + } +} + +uvec3 SparseInfo::getPageCounts(const uvec3& dimensions) const { + auto result = (dimensions / pageDimensions) + + glm::clamp(dimensions % pageDimensions, glm::uvec3(0), glm::uvec3(1)); + return result; +} + +uint32_t SparseInfo::getPageCount(const uvec3& dimensions) const { + auto pageCounts = getPageCounts(dimensions); + return pageCounts.x * pageCounts.y * pageCounts.z; +} + + +uint32_t SparseInfo::getSize() const { + return allocatedPages * pageBytes; } void GL45Backend::initTextureManagementStage() { @@ -100,12 +171,6 @@ void GL45Backend::initTextureManagementStage() { } } -using GL45Texture = GL45Backend::GL45Texture; - -GL45Texture::GL45Texture(const std::weak_ptr& backend, const Texture& texture) - : GLTexture(backend, texture, allocate(texture)) { - incrementTextureGPUCount(); -} GLuint GL45Texture::allocate(const Texture& texture) { GLuint result; @@ -113,43 +178,164 @@ GLuint GL45Texture::allocate(const Texture& texture) { return result; } +GLuint GL45Backend::getTextureID(const TexturePointer& texture, bool transfer) { + return GL45Texture::getId(*this, texture, transfer); +} + +GL45Texture::GL45Texture(const std::weak_ptr& backend, const Texture& texture, GLuint externalId) + : GLTexture(backend, texture, externalId), _sparseInfo(*this) +{ +} + +GL45Texture::GL45Texture(const std::weak_ptr& backend, const Texture& texture, bool transferrable) + : GLTexture(backend, texture, allocate(texture), transferrable), _sparseInfo(*this) + { + + auto theBackend = _backend.lock(); + if (_transferrable && theBackend && theBackend->isTextureManagementSparseEnabled()) { + _sparseInfo.maybeMakeSparse(); + if (_sparseInfo.sparse) { + Backend::incrementTextureGPUSparseCount(); + } + } +} + +GL45Texture::~GL45Texture() { + // Remove this texture from the candidate list of derezzable textures + if (_transferrable) { + auto mipLevels = usedMipLevels(); + Lock lock(texturesByMipCountsMutex); + if (texturesByMipCounts.count(mipLevels)) { + auto& textures = texturesByMipCounts[mipLevels]; + textures.erase(this); + if (textures.empty()) { + texturesByMipCounts.erase(mipLevels); + } + } + } + + if (_sparseInfo.sparse) { + Backend::decrementTextureGPUSparseCount(); + + // Experimenation suggests that allocating sparse textures on one context/thread and deallocating + // them on another is buggy. So for sparse textures we need to queue a lambda with the deallocation + // callls to the transfer thread + auto id = _id; + // Set the class _id to 0 so we don't try to double delete + const_cast(_id) = 0; + std::list> destructionFunctions; + + uint8_t maxFace = (uint8_t)((_target == GL_TEXTURE_CUBE_MAP) ? GLTexture::CUBE_NUM_FACES : 1); + auto maxSparseMip = std::min(_maxMip, _sparseInfo.maxSparseLevel); + for (uint16_t mipLevel = _minMip; mipLevel <= maxSparseMip; ++mipLevel) { + auto mipDimensions = _gpuObject.evalMipDimensions(mipLevel); + destructionFunctions.push_back([id, maxFace, mipLevel, mipDimensions] { + glTexturePageCommitmentEXT(id, mipLevel, 0, 0, 0, mipDimensions.x, mipDimensions.y, maxFace, GL_FALSE); + }); + + auto deallocatedPages = _sparseInfo.getPageCount(mipDimensions) * maxFace; + assert(deallocatedPages <= _sparseInfo.allocatedPages); + _sparseInfo.allocatedPages -= deallocatedPages; + } + + if (0 != _sparseInfo.allocatedPages) { + qCWarning(gpugl45logging) << "Allocated pages remaining " << _id << " " << _sparseInfo.allocatedPages; + } + + auto size = _size; + const_cast(_size) = 0; + _textureTransferHelper->queueExecution([id, size, destructionFunctions] { + for (auto function : destructionFunctions) { + function(); + } + glDeleteTextures(1, &id); + Backend::decrementTextureGPUCount(); + Backend::updateTextureGPUMemoryUsage(size, 0); + Backend::updateTextureGPUSparseMemoryUsage(size, 0); + }); + } +} + +void GL45Texture::withPreservedTexture(std::function f) const { + f(); +} + void GL45Texture::generateMips() const { glGenerateTextureMipmap(_id); (void)CHECK_GL_ERROR(); } -void GL45Texture::copyMipFaceLinesFromTexture(uint16_t mip, uint8_t face, const uvec3& size, uint32_t yOffset, GLenum format, GLenum type, const void* sourcePointer) const { - if (GL_TEXTURE_2D == _target) { - glTextureSubImage2D(_id, mip, 0, yOffset, size.x, size.y, format, type, sourcePointer); - } else if (GL_TEXTURE_CUBE_MAP == _target) { - // DSA ARB does not work on AMD, so use EXT - // unless EXT is not available on the driver - if (glTextureSubImage2DEXT) { - auto target = GLTexture::CUBE_FACE_LAYOUT[face]; - glTextureSubImage2DEXT(_id, target, mip, 0, yOffset, size.x, size.y, format, type, sourcePointer); - } else { - glTextureSubImage3D(_id, mip, 0, yOffset, face, size.x, size.y, 1, format, type, sourcePointer); - } - } else { - Q_ASSERT(false); +void GL45Texture::allocateStorage() const { + if (_gpuObject.getTexelFormat().isCompressed()) { + qFatal("Compressed textures not yet supported"); } + glTextureParameteri(_id, GL_TEXTURE_BASE_LEVEL, 0); + glTextureParameteri(_id, GL_TEXTURE_MAX_LEVEL, _maxMip - _minMip); + // Get the dimensions, accounting for the downgrade level + Vec3u dimensions = _gpuObject.evalMipDimensions(_minMip + _mipOffset); + glTextureStorage2D(_id, usedMipLevels(), _internalFormat, dimensions.x, dimensions.y); (void)CHECK_GL_ERROR(); } -void GL45Texture::copyMipFaceFromTexture(uint16_t sourceMip, uint16_t targetMip, uint8_t face) const { - if (!_gpuObject.isStoredMipFaceAvailable(sourceMip)) { - return; +void GL45Texture::updateSize() const { + if (_gpuObject.getTexelFormat().isCompressed()) { + qFatal("Compressed textures not yet supported"); } - auto size = _gpuObject.evalMipDimensions(sourceMip); - auto mipData = _gpuObject.accessStoredMipFace(sourceMip, face); - if (mipData) { - GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(_gpuObject.getTexelFormat(), _gpuObject.getStoredMipFormat()); - copyMipFaceLinesFromTexture(targetMip, face, size, 0, texelFormat.format, texelFormat.type, mipData->readData()); + + if (_transferrable && _sparseInfo.sparse) { + auto size = _sparseInfo.getSize(); + Backend::updateTextureGPUSparseMemoryUsage(_size, size); + setSize(size); } else { - qCDebug(gpugllogging) << "Missing mipData level=" << sourceMip << " face=" << (int)face << " for texture " << _gpuObject.source().c_str(); + setSize(_gpuObject.evalTotalSize(_mipOffset)); } } +void GL45Texture::startTransfer() { + Parent::startTransfer(); + _sparseInfo.update(); +} + +bool GL45Texture::continueTransfer() { + PROFILE_RANGE(render_gpu_gl, "continueTransfer") + size_t maxFace = GL_TEXTURE_CUBE_MAP == _target ? CUBE_NUM_FACES : 1; + for (uint8_t face = 0; face < maxFace; ++face) { + for (uint16_t mipLevel = _minMip; mipLevel <= _maxMip; ++mipLevel) { + auto size = _gpuObject.evalMipDimensions(mipLevel); + if (_sparseInfo.sparse && mipLevel <= _sparseInfo.maxSparseLevel) { + glTexturePageCommitmentEXT(_id, mipLevel, 0, 0, face, size.x, size.y, 1, GL_TRUE); + _sparseInfo.allocatedPages += _sparseInfo.getPageCount(size); + } + if (_gpuObject.isStoredMipFaceAvailable(mipLevel, face)) { + PROFILE_RANGE_EX(render_gpu_gl, "texSubImage", 0x0000ffff, (size.x * size.y * maxFace / 1024)); + + auto mip = _gpuObject.accessStoredMipFace(mipLevel, face); + GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(_gpuObject.getTexelFormat(), mip->getFormat()); + if (GL_TEXTURE_2D == _target) { + glTextureSubImage2D(_id, mipLevel, 0, 0, size.x, size.y, texelFormat.format, texelFormat.type, mip->readData()); + } else if (GL_TEXTURE_CUBE_MAP == _target) { + // DSA ARB does not work on AMD, so use EXT + // unless EXT is not available on the driver + if (glTextureSubImage2DEXT) { + auto target = CUBE_FACE_LAYOUT[face]; + glTextureSubImage2DEXT(_id, target, mipLevel, 0, 0, size.x, size.y, texelFormat.format, texelFormat.type, mip->readData()); + } else { + glTextureSubImage3D(_id, mipLevel, 0, 0, face, size.x, size.y, 1, texelFormat.format, texelFormat.type, mip->readData()); + } + } else { + Q_ASSERT(false); + } + (void)CHECK_GL_ERROR(); + } + } + } + return false; +} + +void GL45Texture::finishTransfer() { + Parent::finishTransfer(); +} + void GL45Texture::syncSampler() const { const Sampler& sampler = _gpuObject.getSampler(); @@ -167,63 +353,163 @@ void GL45Texture::syncSampler() const { glTextureParameteri(_id, GL_TEXTURE_WRAP_S, WRAP_MODES[sampler.getWrapModeU()]); glTextureParameteri(_id, GL_TEXTURE_WRAP_T, WRAP_MODES[sampler.getWrapModeV()]); glTextureParameteri(_id, GL_TEXTURE_WRAP_R, WRAP_MODES[sampler.getWrapModeW()]); - glTextureParameterf(_id, GL_TEXTURE_MAX_ANISOTROPY_EXT, sampler.getMaxAnisotropy()); glTextureParameterfv(_id, GL_TEXTURE_BORDER_COLOR, (const float*)&sampler.getBorderColor()); - glTextureParameterf(_id, GL_TEXTURE_MIN_LOD, sampler.getMinMip()); - glTextureParameterf(_id, GL_TEXTURE_MAX_LOD, (sampler.getMaxMip() == Sampler::MAX_MIP_LEVEL ? 1000.f : sampler.getMaxMip())); -} - -using GL45FixedAllocationTexture = GL45Backend::GL45FixedAllocationTexture; - -GL45FixedAllocationTexture::GL45FixedAllocationTexture(const std::weak_ptr& backend, const Texture& texture) : GL45Texture(backend, texture), _size(texture.evalTotalSize()) { - allocateStorage(); - syncSampler(); -} - -GL45FixedAllocationTexture::~GL45FixedAllocationTexture() { -} - -void GL45FixedAllocationTexture::allocateStorage() const { - const GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(_gpuObject.getTexelFormat()); - const auto dimensions = _gpuObject.getDimensions(); - const auto mips = _gpuObject.evalNumMips(); - glTextureStorage2D(_id, mips, texelFormat.internalFormat, dimensions.x, dimensions.y); -} - -void GL45FixedAllocationTexture::syncSampler() const { - Parent::syncSampler(); - const Sampler& sampler = _gpuObject.getSampler(); - auto baseMip = std::max(sampler.getMipOffset(), sampler.getMinMip()); + // FIXME account for mip offsets here + auto baseMip = std::max(sampler.getMipOffset(), _minMip); glTextureParameteri(_id, GL_TEXTURE_BASE_LEVEL, baseMip); glTextureParameterf(_id, GL_TEXTURE_MIN_LOD, (float)sampler.getMinMip()); - glTextureParameterf(_id, GL_TEXTURE_MAX_LOD, (sampler.getMaxMip() == Sampler::MAX_MIP_LEVEL ? 1000.f : sampler.getMaxMip())); + glTextureParameterf(_id, GL_TEXTURE_MAX_LOD, (sampler.getMaxMip() == Sampler::MAX_MIP_LEVEL ? 1000.f : sampler.getMaxMip() - _mipOffset)); + glTextureParameterf(_id, GL_TEXTURE_MAX_ANISOTROPY_EXT, sampler.getMaxAnisotropy()); } -// Renderbuffer attachment textures -using GL45AttachmentTexture = GL45Backend::GL45AttachmentTexture; - -GL45AttachmentTexture::GL45AttachmentTexture(const std::weak_ptr& backend, const Texture& texture) : GL45FixedAllocationTexture(backend, texture) { - Backend::updateTextureGPUFramebufferMemoryUsage(0, size()); +void GL45Texture::postTransfer() { + Parent::postTransfer(); + auto mipLevels = usedMipLevels(); + if (_transferrable && mipLevels > 1 && _minMip < _sparseInfo.maxSparseLevel) { + Lock lock(texturesByMipCountsMutex); + texturesByMipCounts[mipLevels].insert(this); + } } -GL45AttachmentTexture::~GL45AttachmentTexture() { - Backend::updateTextureGPUFramebufferMemoryUsage(size(), 0); -} +void GL45Texture::stripToMip(uint16_t newMinMip) { + if (newMinMip < _minMip) { + qCWarning(gpugl45logging) << "Cannot decrease the min mip"; + return; + } -// Strict resource textures -using GL45StrictResourceTexture = GL45Backend::GL45StrictResourceTexture; + if (_sparseInfo.sparse && newMinMip > _sparseInfo.maxSparseLevel) { + qCWarning(gpugl45logging) << "Cannot increase the min mip into the mip tail"; + return; + } -GL45StrictResourceTexture::GL45StrictResourceTexture(const std::weak_ptr& backend, const Texture& texture) : GL45FixedAllocationTexture(backend, texture) { - auto mipLevels = _gpuObject.evalNumMips(); - for (uint16_t sourceMip = 0; sourceMip < mipLevels; ++sourceMip) { - uint16_t targetMip = sourceMip; - size_t maxFace = GLTexture::getFaceCount(_target); - for (uint8_t face = 0; face < maxFace; ++face) { - copyMipFaceFromTexture(sourceMip, targetMip, face); + PROFILE_RANGE(render_gpu_gl, "GL45Texture::stripToMip"); + + auto mipLevels = usedMipLevels(); + { + Lock lock(texturesByMipCountsMutex); + assert(0 != texturesByMipCounts.count(mipLevels)); + assert(0 != texturesByMipCounts[mipLevels].count(this)); + texturesByMipCounts[mipLevels].erase(this); + if (texturesByMipCounts[mipLevels].empty()) { + texturesByMipCounts.erase(mipLevels); } } - if (texture.isAutogenerateMips()) { - generateMips(); + + // If we weren't generating mips before, we need to now that we're stripping down mip levels. + if (!_gpuObject.isAutogenerateMips()) { + qCDebug(gpugl45logging) << "Force mip generation for texture"; + glGenerateTextureMipmap(_id); + } + + + uint8_t maxFace = (uint8_t)((_target == GL_TEXTURE_CUBE_MAP) ? GLTexture::CUBE_NUM_FACES : 1); + if (_sparseInfo.sparse) { + for (uint16_t mip = _minMip; mip < newMinMip; ++mip) { + auto id = _id; + auto mipDimensions = _gpuObject.evalMipDimensions(mip); + _textureTransferHelper->queueExecution([id, mip, mipDimensions, maxFace] { + glTexturePageCommitmentEXT(id, mip, 0, 0, 0, mipDimensions.x, mipDimensions.y, maxFace, GL_FALSE); + }); + + auto deallocatedPages = _sparseInfo.getPageCount(mipDimensions) * maxFace; + assert(deallocatedPages < _sparseInfo.allocatedPages); + _sparseInfo.allocatedPages -= deallocatedPages; + } + _minMip = newMinMip; + } else { + GLuint oldId = _id; + // Find the distance between the old min mip and the new one + uint16 mipDelta = newMinMip - _minMip; + _mipOffset += mipDelta; + const_cast(_maxMip) -= mipDelta; + auto newLevels = usedMipLevels(); + + // Create and setup the new texture (allocate) + { + Vec3u newDimensions = _gpuObject.evalMipDimensions(_mipOffset); + PROFILE_RANGE_EX(render_gpu_gl, "Re-Allocate", 0xff0000ff, (newDimensions.x * newDimensions.y)); + + glCreateTextures(_target, 1, &const_cast(_id)); + glTextureParameteri(_id, GL_TEXTURE_BASE_LEVEL, 0); + glTextureParameteri(_id, GL_TEXTURE_MAX_LEVEL, _maxMip - _minMip); + glTextureStorage2D(_id, newLevels, _internalFormat, newDimensions.x, newDimensions.y); + } + + // Copy the contents of the old texture to the new + { + PROFILE_RANGE(render_gpu_gl, "Blit"); + // Preferred path only available in 4.3 + for (uint16 targetMip = _minMip; targetMip <= _maxMip; ++targetMip) { + uint16 sourceMip = targetMip + mipDelta; + Vec3u mipDimensions = _gpuObject.evalMipDimensions(targetMip + _mipOffset); + for (GLenum target : getFaceTargets(_target)) { + glCopyImageSubData( + oldId, target, sourceMip, 0, 0, 0, + _id, target, targetMip, 0, 0, 0, + mipDimensions.x, mipDimensions.y, 1 + ); + (void)CHECK_GL_ERROR(); + } + } + + glDeleteTextures(1, &oldId); + } + } + + // Re-sync the sampler to force access to the new mip level + syncSampler(); + updateSize(); + + // Re-insert into the texture-by-mips map if appropriate + mipLevels = usedMipLevels(); + if (mipLevels > 1 && (!_sparseInfo.sparse || _minMip < _sparseInfo.maxSparseLevel)) { + Lock lock(texturesByMipCountsMutex); + texturesByMipCounts[mipLevels].insert(this); } } +void GL45Texture::updateMips() { + if (!_sparseInfo.sparse) { + return; + } + auto newMinMip = std::min(_gpuObject.minMip(), _sparseInfo.maxSparseLevel); + if (_minMip < newMinMip) { + stripToMip(newMinMip); + } +} + +void GL45Texture::derez() { + if (_sparseInfo.sparse) { + assert(_minMip < _sparseInfo.maxSparseLevel); + } + assert(_minMip < _maxMip); + assert(_transferrable); + stripToMip(_minMip + 1); +} + +void GL45Backend::derezTextures() const { + if (GLTexture::getMemoryPressure() < 1.0f) { + return; + } + + Lock lock(texturesByMipCountsMutex); + if (texturesByMipCounts.empty()) { + // No available textures to derez + return; + } + + auto mipLevel = texturesByMipCounts.rbegin()->first; + if (mipLevel <= 1) { + // No mips available to remove + return; + } + + GL45Texture* targetTexture = nullptr; + { + auto& textures = texturesByMipCounts[mipLevel]; + assert(!textures.empty()); + targetTexture = *textures.begin(); + } + lock.unlock(); + targetTexture->derez(); +} diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp deleted file mode 100644 index d54ad1ea4b..0000000000 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp +++ /dev/null @@ -1,1033 +0,0 @@ -// -// GL45BackendTexture.cpp -// libraries/gpu/src/gpu -// -// Created by Sam Gateau on 1/19/2015. -// 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 "GL45Backend.h" -#include -#include -#include -#include -#include -#include - -#include -#include - -#include -#include "../gl/GLTexelFormat.h" - -using namespace gpu; -using namespace gpu::gl; -using namespace gpu::gl45; - -// Variable sized textures -using GL45VariableAllocationTexture = GL45Backend::GL45VariableAllocationTexture; -using MemoryPressureState = GL45VariableAllocationTexture::MemoryPressureState; -using WorkQueue = GL45VariableAllocationTexture::WorkQueue; - -std::list GL45VariableAllocationTexture::_memoryManagedTextures; -MemoryPressureState GL45VariableAllocationTexture::_memoryPressureState = MemoryPressureState::Idle; -std::atomic GL45VariableAllocationTexture::_memoryPressureStateStale { false }; -const uvec3 GL45VariableAllocationTexture::INITIAL_MIP_TRANSFER_DIMENSIONS { 64, 64, 1 }; -WorkQueue GL45VariableAllocationTexture::_transferQueue; -WorkQueue GL45VariableAllocationTexture::_promoteQueue; -WorkQueue GL45VariableAllocationTexture::_demoteQueue; -TexturePointer GL45VariableAllocationTexture::_currentTransferTexture; - -#define OVERSUBSCRIBED_PRESSURE_VALUE 0.95f -#define UNDERSUBSCRIBED_PRESSURE_VALUE 0.85f -#define DEFAULT_ALLOWED_TEXTURE_MEMORY_MB ((size_t)1024) - -static const size_t DEFAULT_ALLOWED_TEXTURE_MEMORY = MB_TO_BYTES(DEFAULT_ALLOWED_TEXTURE_MEMORY_MB); - -using TransferJob = GL45VariableAllocationTexture::TransferJob; - -static const uvec3 MAX_TRANSFER_DIMENSIONS { 1024, 1024, 1 }; -static const size_t MAX_TRANSFER_SIZE = MAX_TRANSFER_DIMENSIONS.x * MAX_TRANSFER_DIMENSIONS.y * 4; - -#if THREADED_TEXTURE_BUFFERING -std::shared_ptr TransferJob::_bufferThread { nullptr }; -std::atomic TransferJob::_shutdownBufferingThread { false }; -Mutex TransferJob::_mutex; -TransferJob::VoidLambdaQueue TransferJob::_bufferLambdaQueue; - -void TransferJob::startTransferLoop() { - if (_bufferThread) { - return; - } - _shutdownBufferingThread = false; - _bufferThread = std::make_shared([] { - TransferJob::bufferLoop(); - }); -} - -void TransferJob::stopTransferLoop() { - if (!_bufferThread) { - return; - } - _shutdownBufferingThread = true; - _bufferThread->join(); - _bufferThread.reset(); - _shutdownBufferingThread = false; -} -#endif - -TransferJob::TransferJob(const GL45VariableAllocationTexture& parent, uint16_t sourceMip, uint16_t targetMip, uint8_t face, uint32_t lines, uint32_t lineOffset) - : _parent(parent) { - - auto transferDimensions = _parent._gpuObject.evalMipDimensions(sourceMip); - GLenum format; - GLenum type; - auto mipData = _parent._gpuObject.accessStoredMipFace(sourceMip, face); - GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(_parent._gpuObject.getTexelFormat(), _parent._gpuObject.getStoredMipFormat()); - format = texelFormat.format; - type = texelFormat.type; - - if (0 == lines) { - _transferSize = mipData->getSize(); - _bufferingLambda = [=] { - _buffer.resize(_transferSize); - memcpy(&_buffer[0], mipData->readData(), _transferSize); - _bufferingCompleted = true; - }; - - } else { - transferDimensions.y = lines; - auto dimensions = _parent._gpuObject.evalMipDimensions(sourceMip); - auto mipSize = mipData->getSize(); - auto bytesPerLine = (uint32_t)mipSize / dimensions.y; - _transferSize = bytesPerLine * lines; - auto sourceOffset = bytesPerLine * lineOffset; - _bufferingLambda = [=] { - _buffer.resize(_transferSize); - memcpy(&_buffer[0], mipData->readData() + sourceOffset, _transferSize); - _bufferingCompleted = true; - }; - } - - Backend::updateTextureTransferPendingSize(0, _transferSize); - - _transferLambda = [=] { - _parent.copyMipFaceLinesFromTexture(targetMip, face, transferDimensions, lineOffset, format, type, _buffer.data()); - std::vector emptyVector; - _buffer.swap(emptyVector); - }; -} - -TransferJob::TransferJob(const GL45VariableAllocationTexture& parent, std::function transferLambda) - : _parent(parent), _bufferingCompleted(true), _transferLambda(transferLambda) { -} - -TransferJob::~TransferJob() { - Backend::updateTextureTransferPendingSize(_transferSize, 0); -} - - -bool TransferJob::tryTransfer() { - // Disable threaded texture transfer for now -#if THREADED_TEXTURE_BUFFERING - // Are we ready to transfer - if (_bufferingCompleted) { - _transferLambda(); - return true; - } - - startBuffering(); - return false; -#else - if (!_bufferingCompleted) { - _bufferingLambda(); - _bufferingCompleted = true; - } - _transferLambda(); - return true; -#endif -} - -#if THREADED_TEXTURE_BUFFERING - -void TransferJob::startBuffering() { - if (_bufferingStarted) { - return; - } - _bufferingStarted = true; - { - Lock lock(_mutex); - _bufferLambdaQueue.push(_bufferingLambda); - } -} - -void TransferJob::bufferLoop() { - while (!_shutdownBufferingThread) { - VoidLambdaQueue workingQueue; - { - Lock lock(_mutex); - _bufferLambdaQueue.swap(workingQueue); - } - - if (workingQueue.empty()) { - QThread::msleep(5); - continue; - } - - while (!workingQueue.empty()) { - workingQueue.front()(); - workingQueue.pop(); - } - } -} -#endif - - -void GL45VariableAllocationTexture::addMemoryManagedTexture(const TexturePointer& texturePointer) { - _memoryManagedTextures.push_back(texturePointer); - addToWorkQueue(texturePointer); -} - -void GL45VariableAllocationTexture::addToWorkQueue(const TexturePointer& texturePointer) { - GL45VariableAllocationTexture* object = Backend::getGPUObject(*texturePointer); - switch (_memoryPressureState) { - case MemoryPressureState::Oversubscribed: - if (object->canDemote()) { - // Demote largest first - _demoteQueue.push({ texturePointer, (float)object->size() }); - } - break; - - case MemoryPressureState::Undersubscribed: - if (object->canPromote()) { - // Promote smallest first - _promoteQueue.push({ texturePointer, 1.0f / (float)object->size() }); - } - break; - - case MemoryPressureState::Transfer: - if (object->hasPendingTransfers()) { - // Transfer priority given to smaller mips first - _transferQueue.push({ texturePointer, 1.0f / (float)object->_gpuObject.evalMipSize(object->_populatedMip) }); - } - break; - - case MemoryPressureState::Idle: - break; - - default: - Q_UNREACHABLE(); - } -} - -WorkQueue& GL45VariableAllocationTexture::getActiveWorkQueue() { - static WorkQueue empty; - switch (_memoryPressureState) { - case MemoryPressureState::Oversubscribed: - return _demoteQueue; - - case MemoryPressureState::Undersubscribed: - return _promoteQueue; - - case MemoryPressureState::Transfer: - return _transferQueue; - - default: - break; - } - Q_UNREACHABLE(); - return empty; -} - -// FIXME hack for stats display -QString getTextureMemoryPressureModeString() { - switch (GL45VariableAllocationTexture::_memoryPressureState) { - case MemoryPressureState::Oversubscribed: - return "Oversubscribed"; - - case MemoryPressureState::Undersubscribed: - return "Undersubscribed"; - - case MemoryPressureState::Transfer: - return "Transfer"; - - case MemoryPressureState::Idle: - return "Idle"; - } - Q_UNREACHABLE(); - return "Unknown"; -} - -void GL45VariableAllocationTexture::updateMemoryPressure() { - static size_t lastAllowedMemoryAllocation = gpu::Texture::getAllowedGPUMemoryUsage(); - - size_t allowedMemoryAllocation = gpu::Texture::getAllowedGPUMemoryUsage(); - if (0 == allowedMemoryAllocation) { - allowedMemoryAllocation = DEFAULT_ALLOWED_TEXTURE_MEMORY; - } - - // If the user explicitly changed the allowed memory usage, we need to mark ourselves stale - // so that we react - if (allowedMemoryAllocation != lastAllowedMemoryAllocation) { - _memoryPressureStateStale = true; - lastAllowedMemoryAllocation = allowedMemoryAllocation; - } - - if (!_memoryPressureStateStale.exchange(false)) { - return; - } - - PROFILE_RANGE(render_gpu_gl, __FUNCTION__); - - // Clear any defunct textures (weak pointers that no longer have a valid texture) - _memoryManagedTextures.remove_if([&](const TextureWeakPointer& weakPointer) { - return weakPointer.expired(); - }); - - // Convert weak pointers to strong. This new list may still contain nulls if a texture was - // deleted on another thread between the previous line and this one - std::vector strongTextures; { - strongTextures.reserve(_memoryManagedTextures.size()); - std::transform( - _memoryManagedTextures.begin(), _memoryManagedTextures.end(), - std::back_inserter(strongTextures), - [](const TextureWeakPointer& p) { return p.lock(); }); - } - - size_t totalVariableMemoryAllocation = 0; - size_t idealMemoryAllocation = 0; - bool canDemote = false; - bool canPromote = false; - bool hasTransfers = false; - for (const auto& texture : strongTextures) { - // Race conditions can still leave nulls in the list, so we need to check - if (!texture) { - continue; - } - GL45VariableAllocationTexture* object = Backend::getGPUObject(*texture); - // Track how much the texture thinks it should be using - idealMemoryAllocation += texture->evalTotalSize(); - // Track how much we're actually using - totalVariableMemoryAllocation += object->size(); - canDemote |= object->canDemote(); - canPromote |= object->canPromote(); - hasTransfers |= object->hasPendingTransfers(); - } - - size_t unallocated = idealMemoryAllocation - totalVariableMemoryAllocation; - float pressure = (float)totalVariableMemoryAllocation / (float)allowedMemoryAllocation; - - auto newState = MemoryPressureState::Idle; - if (pressure > OVERSUBSCRIBED_PRESSURE_VALUE && canDemote) { - newState = MemoryPressureState::Oversubscribed; - } else if (pressure < UNDERSUBSCRIBED_PRESSURE_VALUE && unallocated != 0 && canPromote) { - newState = MemoryPressureState::Undersubscribed; - } else if (hasTransfers) { - newState = MemoryPressureState::Transfer; - } - - if (newState != _memoryPressureState) { -#if THREADED_TEXTURE_BUFFERING - if (MemoryPressureState::Transfer == _memoryPressureState) { - TransferJob::stopTransferLoop(); - } - _memoryPressureState = newState; - if (MemoryPressureState::Transfer == _memoryPressureState) { - TransferJob::startTransferLoop(); - } -#else - _memoryPressureState = newState; -#endif - // Clear the existing queue - _transferQueue = WorkQueue(); - _promoteQueue = WorkQueue(); - _demoteQueue = WorkQueue(); - - // Populate the existing textures into the queue - for (const auto& texture : strongTextures) { - addToWorkQueue(texture); - } - } -} - -void GL45VariableAllocationTexture::processWorkQueues() { - if (MemoryPressureState::Idle == _memoryPressureState) { - return; - } - - auto& workQueue = getActiveWorkQueue(); - PROFILE_RANGE(render_gpu_gl, __FUNCTION__); - while (!workQueue.empty()) { - auto workTarget = workQueue.top(); - workQueue.pop(); - auto texture = workTarget.first.lock(); - if (!texture) { - continue; - } - - // Grab the first item off the demote queue - GL45VariableAllocationTexture* object = Backend::getGPUObject(*texture); - if (MemoryPressureState::Oversubscribed == _memoryPressureState) { - if (!object->canDemote()) { - continue; - } - object->demote(); - } else if (MemoryPressureState::Undersubscribed == _memoryPressureState) { - if (!object->canPromote()) { - continue; - } - object->promote(); - } else if (MemoryPressureState::Transfer == _memoryPressureState) { - if (!object->hasPendingTransfers()) { - continue; - } - object->executeNextTransfer(texture); - } else { - Q_UNREACHABLE(); - } - - // Reinject into the queue if more work to be done - addToWorkQueue(texture); - break; - } - - if (workQueue.empty()) { - _memoryPressureStateStale = true; - } -} - -void GL45VariableAllocationTexture::manageMemory() { - PROFILE_RANGE(render_gpu_gl, __FUNCTION__); - updateMemoryPressure(); - processWorkQueues(); -} - -size_t GL45VariableAllocationTexture::_frameTexturesCreated { 0 }; - -GL45VariableAllocationTexture::GL45VariableAllocationTexture(const std::weak_ptr& backend, const Texture& texture) : GL45Texture(backend, texture) { - ++_frameTexturesCreated; -} - -GL45VariableAllocationTexture::~GL45VariableAllocationTexture() { - _memoryPressureStateStale = true; - Backend::updateTextureGPUMemoryUsage(_size, 0); -} - -void GL45VariableAllocationTexture::executeNextTransfer(const TexturePointer& currentTexture) { - if (_populatedMip <= _allocatedMip) { - return; - } - - if (_pendingTransfers.empty()) { - populateTransferQueue(); - } - - if (!_pendingTransfers.empty()) { - // Keeping hold of a strong pointer during the transfer ensures that the transfer thread cannot try to access a destroyed texture - _currentTransferTexture = currentTexture; - if (_pendingTransfers.front()->tryTransfer()) { - _pendingTransfers.pop(); - _currentTransferTexture.reset(); - } - } -} - -// Managed size resource textures -using GL45ResourceTexture = GL45Backend::GL45ResourceTexture; - -GL45ResourceTexture::GL45ResourceTexture(const std::weak_ptr& backend, const Texture& texture) : GL45VariableAllocationTexture(backend, texture) { - auto mipLevels = texture.evalNumMips(); - _allocatedMip = mipLevels; - uvec3 mipDimensions; - for (uint16_t mip = 0; mip < mipLevels; ++mip) { - if (glm::all(glm::lessThanEqual(texture.evalMipDimensions(mip), INITIAL_MIP_TRANSFER_DIMENSIONS))) { - _maxAllocatedMip = _populatedMip = mip; - break; - } - } - - uint16_t allocatedMip = _populatedMip - std::min(_populatedMip, 2); - allocateStorage(allocatedMip); - _memoryPressureStateStale = true; - copyMipsFromTexture(); - syncSampler(); - -} - -void GL45ResourceTexture::allocateStorage(uint16 allocatedMip) { - _allocatedMip = allocatedMip; - const GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(_gpuObject.getTexelFormat()); - const auto dimensions = _gpuObject.evalMipDimensions(_allocatedMip); - const auto totalMips = _gpuObject.evalNumMips(); - const auto mips = totalMips - _allocatedMip; - glTextureStorage2D(_id, mips, texelFormat.internalFormat, dimensions.x, dimensions.y); - auto mipLevels = _gpuObject.evalNumMips(); - _size = 0; - for (uint16_t mip = _allocatedMip; mip < mipLevels; ++mip) { - _size += _gpuObject.evalMipSize(mip); - } - Backend::updateTextureGPUMemoryUsage(0, _size); - -} - -void GL45ResourceTexture::copyMipsFromTexture() { - auto mipLevels = _gpuObject.evalNumMips(); - size_t maxFace = GLTexture::getFaceCount(_target); - for (uint16_t sourceMip = _populatedMip; sourceMip < mipLevels; ++sourceMip) { - uint16_t targetMip = sourceMip - _allocatedMip; - for (uint8_t face = 0; face < maxFace; ++face) { - copyMipFaceFromTexture(sourceMip, targetMip, face); - } - } -} - -void GL45ResourceTexture::syncSampler() const { - Parent::syncSampler(); - glTextureParameteri(_id, GL_TEXTURE_BASE_LEVEL, _populatedMip - _allocatedMip); -} - -void GL45ResourceTexture::promote() { - PROFILE_RANGE(render_gpu_gl, __FUNCTION__); - Q_ASSERT(_allocatedMip > 0); - GLuint oldId = _id; - uint32_t oldSize = _size; - // create new texture - const_cast(_id) = allocate(_gpuObject); - uint16_t oldAllocatedMip = _allocatedMip; - // allocate storage for new level - allocateStorage(_allocatedMip - std::min(_allocatedMip, 2)); - uint16_t mips = _gpuObject.evalNumMips(); - // copy pre-existing mips - for (uint16_t mip = _populatedMip; mip < mips; ++mip) { - auto mipDimensions = _gpuObject.evalMipDimensions(mip); - uint16_t targetMip = mip - _allocatedMip; - uint16_t sourceMip = mip - oldAllocatedMip; - auto faces = getFaceCount(_target); - for (uint8_t face = 0; face < faces; ++face) { - glCopyImageSubData( - oldId, _target, sourceMip, 0, 0, face, - _id, _target, targetMip, 0, 0, face, - mipDimensions.x, mipDimensions.y, 1 - ); - (void)CHECK_GL_ERROR(); - } - } - // destroy the old texture - glDeleteTextures(1, &oldId); - // update the memory usage - Backend::updateTextureGPUMemoryUsage(oldSize, 0); - _memoryPressureStateStale = true; - syncSampler(); - populateTransferQueue(); -} - -void GL45ResourceTexture::demote() { - PROFILE_RANGE(render_gpu_gl, __FUNCTION__); - Q_ASSERT(_allocatedMip < _maxAllocatedMip); - auto oldId = _id; - auto oldSize = _size; - const_cast(_id) = allocate(_gpuObject); - allocateStorage(_allocatedMip + 1); - _populatedMip = std::max(_populatedMip, _allocatedMip); - uint16_t mips = _gpuObject.evalNumMips(); - // copy pre-existing mips - for (uint16_t mip = _populatedMip; mip < mips; ++mip) { - auto mipDimensions = _gpuObject.evalMipDimensions(mip); - uint16_t targetMip = mip - _allocatedMip; - uint16_t sourceMip = targetMip + 1; - auto faces = getFaceCount(_target); - for (uint8_t face = 0; face < faces; ++face) { - glCopyImageSubData( - oldId, _target, sourceMip, 0, 0, face, - _id, _target, targetMip, 0, 0, face, - mipDimensions.x, mipDimensions.y, 1 - ); - (void)CHECK_GL_ERROR(); - } - } - // destroy the old texture - glDeleteTextures(1, &oldId); - // update the memory usage - Backend::updateTextureGPUMemoryUsage(oldSize, 0); - _memoryPressureStateStale = true; - syncSampler(); - populateTransferQueue(); -} - - -void GL45ResourceTexture::populateTransferQueue() { - PROFILE_RANGE(render_gpu_gl, __FUNCTION__); - if (_populatedMip <= _allocatedMip) { - return; - } - _pendingTransfers = TransferQueue(); - - const uint8_t maxFace = GLTexture::getFaceCount(_target); - uint16_t sourceMip = _populatedMip; - do { - --sourceMip; - auto targetMip = sourceMip - _allocatedMip; - auto mipDimensions = _gpuObject.evalMipDimensions(sourceMip); - for (uint8_t face = 0; face < maxFace; ++face) { - if (!_gpuObject.isStoredMipFaceAvailable(sourceMip, face)) { - continue; - } - - // If the mip is less than the max transfer size, then just do it in one transfer - if (glm::all(glm::lessThanEqual(mipDimensions, MAX_TRANSFER_DIMENSIONS))) { - // Can the mip be transferred in one go - _pendingTransfers.emplace(new TransferJob(*this, sourceMip, targetMip, face)); - continue; - } - - // break down the transfers into chunks so that no single transfer is - // consuming more than X bandwidth - auto mipData = _gpuObject.accessStoredMipFace(sourceMip, face); - const auto lines = mipDimensions.y; - auto bytesPerLine = (uint32_t)mipData->getSize() / lines; - Q_ASSERT(0 == (mipData->getSize() % lines)); - uint32_t linesPerTransfer = (uint32_t)(MAX_TRANSFER_SIZE / bytesPerLine); - uint32_t lineOffset = 0; - while (lineOffset < lines) { - uint32_t linesToCopy = std::min(lines - lineOffset, linesPerTransfer); - _pendingTransfers.emplace(new TransferJob(*this, sourceMip, targetMip, face, linesToCopy, lineOffset)); - lineOffset += linesToCopy; - } - } - - // queue up the sampler and populated mip change for after the transfer has completed - _pendingTransfers.emplace(new TransferJob(*this, [=] { - _populatedMip = sourceMip; - syncSampler(); - })); - } while (sourceMip != _allocatedMip); -} - -// Sparsely allocated, managed size resource textures -#if 0 -#define SPARSE_PAGE_SIZE_OVERHEAD_ESTIMATE 1.3f - -using GL45SparseResourceTexture = GL45Backend::GL45SparseResourceTexture; - -GL45Texture::PageDimensionsMap GL45Texture::pageDimensionsByFormat; -Mutex GL45Texture::pageDimensionsMutex; - -GL45Texture::PageDimensions GL45Texture::getPageDimensionsForFormat(const TextureTypeFormat& typeFormat) { - { - Lock lock(pageDimensionsMutex); - if (pageDimensionsByFormat.count(typeFormat)) { - return pageDimensionsByFormat[typeFormat]; - } - } - - GLint count = 0; - glGetInternalformativ(typeFormat.first, typeFormat.second, GL_NUM_VIRTUAL_PAGE_SIZES_ARB, 1, &count); - - std::vector result; - if (count > 0) { - std::vector x, y, z; - x.resize(count); - glGetInternalformativ(typeFormat.first, typeFormat.second, GL_VIRTUAL_PAGE_SIZE_X_ARB, 1, &x[0]); - y.resize(count); - glGetInternalformativ(typeFormat.first, typeFormat.second, GL_VIRTUAL_PAGE_SIZE_Y_ARB, 1, &y[0]); - z.resize(count); - glGetInternalformativ(typeFormat.first, typeFormat.second, GL_VIRTUAL_PAGE_SIZE_Z_ARB, 1, &z[0]); - - result.resize(count); - for (GLint i = 0; i < count; ++i) { - result[i] = uvec3(x[i], y[i], z[i]); - } - } - - { - Lock lock(pageDimensionsMutex); - if (0 == pageDimensionsByFormat.count(typeFormat)) { - pageDimensionsByFormat[typeFormat] = result; - } - } - - return result; -} - -GL45Texture::PageDimensions GL45Texture::getPageDimensionsForFormat(GLenum target, GLenum format) { - return getPageDimensionsForFormat({ target, format }); -} -bool GL45Texture::isSparseEligible(const Texture& texture) { - Q_ASSERT(TextureUsageType::RESOURCE == texture.getUsageType()); - - // Disabling sparse for the momemnt - return false; - - const auto allowedPageDimensions = getPageDimensionsForFormat(getGLTextureType(texture), - gl::GLTexelFormat::evalGLTexelFormatInternal(texture.getTexelFormat())); - const auto textureDimensions = texture.getDimensions(); - for (const auto& pageDimensions : allowedPageDimensions) { - if (uvec3(0) == (textureDimensions % pageDimensions)) { - return true; - } - } - - return false; -} - - -GL45SparseResourceTexture::GL45SparseResourceTexture(const std::weak_ptr& backend, const Texture& texture) : GL45VariableAllocationTexture(backend, texture) { - const GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(_gpuObject.getTexelFormat()); - const uvec3 dimensions = _gpuObject.getDimensions(); - auto allowedPageDimensions = getPageDimensionsForFormat(_target, texelFormat.internalFormat); - uint32_t pageDimensionsIndex = 0; - // In order to enable sparse the texture size must be an integer multiple of the page size - for (size_t i = 0; i < allowedPageDimensions.size(); ++i) { - pageDimensionsIndex = (uint32_t)i; - _pageDimensions = allowedPageDimensions[i]; - // Is this texture an integer multiple of page dimensions? - if (uvec3(0) == (dimensions % _pageDimensions)) { - qCDebug(gpugl45logging) << "Enabling sparse for texture " << _gpuObject.source().c_str(); - break; - } - } - glTextureParameteri(_id, GL_TEXTURE_SPARSE_ARB, GL_TRUE); - glTextureParameteri(_id, GL_VIRTUAL_PAGE_SIZE_INDEX_ARB, pageDimensionsIndex); - glGetTextureParameterIuiv(_id, GL_NUM_SPARSE_LEVELS_ARB, &_maxSparseLevel); - - _pageBytes = _gpuObject.getTexelFormat().getSize(); - _pageBytes *= _pageDimensions.x * _pageDimensions.y * _pageDimensions.z; - // Testing with a simple texture allocating app shows an estimated 20% GPU memory overhead for - // sparse textures as compared to non-sparse, so we acount for that here. - _pageBytes = (uint32_t)(_pageBytes * SPARSE_PAGE_SIZE_OVERHEAD_ESTIMATE); - - //allocateStorage(); - syncSampler(); -} - -GL45SparseResourceTexture::~GL45SparseResourceTexture() { - Backend::updateTextureGPUVirtualMemoryUsage(size(), 0); -} - -uvec3 GL45SparseResourceTexture::getPageCounts(const uvec3& dimensions) const { - auto result = (dimensions / _pageDimensions) + - glm::clamp(dimensions % _pageDimensions, glm::uvec3(0), glm::uvec3(1)); - return result; -} - -uint32_t GL45SparseResourceTexture::getPageCount(const uvec3& dimensions) const { - auto pageCounts = getPageCounts(dimensions); - return pageCounts.x * pageCounts.y * pageCounts.z; -} - -void GL45SparseResourceTexture::promote() { -} - -void GL45SparseResourceTexture::demote() { -} - -SparseInfo::SparseInfo(GL45Texture& texture) - : texture(texture) { -} - -void SparseInfo::maybeMakeSparse() { - // Don't enable sparse for objects with explicitly managed mip levels - if (!texture._gpuObject.isAutogenerateMips()) { - return; - } - - const uvec3 dimensions = texture._gpuObject.getDimensions(); - auto allowedPageDimensions = getPageDimensionsForFormat(texture._target, texture._internalFormat); - // In order to enable sparse the texture size must be an integer multiple of the page size - for (size_t i = 0; i < allowedPageDimensions.size(); ++i) { - pageDimensionsIndex = (uint32_t)i; - pageDimensions = allowedPageDimensions[i]; - // Is this texture an integer multiple of page dimensions? - if (uvec3(0) == (dimensions % pageDimensions)) { - qCDebug(gpugl45logging) << "Enabling sparse for texture " << texture._source.c_str(); - sparse = true; - break; - } - } - - if (sparse) { - glTextureParameteri(texture._id, GL_TEXTURE_SPARSE_ARB, GL_TRUE); - glTextureParameteri(texture._id, GL_VIRTUAL_PAGE_SIZE_INDEX_ARB, pageDimensionsIndex); - } else { - qCDebug(gpugl45logging) << "Size " << dimensions.x << " x " << dimensions.y << - " is not supported by any sparse page size for texture" << texture._source.c_str(); - } -} - - -// This can only be called after we've established our storage size -void SparseInfo::update() { - if (!sparse) { - return; - } - glGetTextureParameterIuiv(texture._id, GL_NUM_SPARSE_LEVELS_ARB, &maxSparseLevel); - - for (uint16_t mipLevel = 0; mipLevel <= maxSparseLevel; ++mipLevel) { - auto mipDimensions = texture._gpuObject.evalMipDimensions(mipLevel); - auto mipPageCount = getPageCount(mipDimensions); - maxPages += mipPageCount; - } - if (texture._target == GL_TEXTURE_CUBE_MAP) { - maxPages *= GLTexture::CUBE_NUM_FACES; - } -} - - -void SparseInfo::allocateToMip(uint16_t targetMip) { - // Not sparse, do nothing - if (!sparse) { - return; - } - - if (allocatedMip == INVALID_MIP) { - allocatedMip = maxSparseLevel + 1; - } - - // Don't try to allocate below the maximum sparse level - if (targetMip > maxSparseLevel) { - targetMip = maxSparseLevel; - } - - // Already allocated this level - if (allocatedMip <= targetMip) { - return; - } - - uint32_t maxFace = (uint32_t)(GL_TEXTURE_CUBE_MAP == texture._target ? CUBE_NUM_FACES : 1); - for (uint16_t mip = targetMip; mip < allocatedMip; ++mip) { - auto size = texture._gpuObject.evalMipDimensions(mip); - glTexturePageCommitmentEXT(texture._id, mip, 0, 0, 0, size.x, size.y, maxFace, GL_TRUE); - allocatedPages += getPageCount(size); - } - allocatedMip = targetMip; -} - -uint32_t SparseInfo::getSize() const { - return allocatedPages * pageBytes; -} -using SparseInfo = GL45Backend::GL45Texture::SparseInfo; - -void GL45Texture::updateSize() const { - if (_gpuObject.getTexelFormat().isCompressed()) { - qFatal("Compressed textures not yet supported"); - } - - if (_transferrable && _sparseInfo.sparse) { - auto size = _sparseInfo.getSize(); - Backend::updateTextureGPUSparseMemoryUsage(_size, size); - setSize(size); - } else { - setSize(_gpuObject.evalTotalSize(_mipOffset)); - } -} - -void GL45Texture::startTransfer() { - Parent::startTransfer(); - _sparseInfo.update(); - _populatedMip = _maxMip + 1; -} - -bool GL45Texture::continueTransfer() { - size_t maxFace = GL_TEXTURE_CUBE_MAP == _target ? CUBE_NUM_FACES : 1; - if (_populatedMip == _minMip) { - return false; - } - - uint16_t targetMip = _populatedMip - 1; - while (targetMip > 0 && !_gpuObject.isStoredMipFaceAvailable(targetMip)) { - --targetMip; - } - - _sparseInfo.allocateToMip(targetMip); - for (uint8_t face = 0; face < maxFace; ++face) { - auto size = _gpuObject.evalMipDimensions(targetMip); - if (_gpuObject.isStoredMipFaceAvailable(targetMip, face)) { - auto mip = _gpuObject.accessStoredMipFace(targetMip, face); - GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(_gpuObject.getTexelFormat(), mip->getFormat()); - if (GL_TEXTURE_2D == _target) { - glTextureSubImage2D(_id, targetMip, 0, 0, size.x, size.y, texelFormat.format, texelFormat.type, mip->readData()); - } else if (GL_TEXTURE_CUBE_MAP == _target) { - // DSA ARB does not work on AMD, so use EXT - // unless EXT is not available on the driver - if (glTextureSubImage2DEXT) { - auto target = CUBE_FACE_LAYOUT[face]; - glTextureSubImage2DEXT(_id, target, targetMip, 0, 0, size.x, size.y, texelFormat.format, texelFormat.type, mip->readData()); - } else { - glTextureSubImage3D(_id, targetMip, 0, 0, face, size.x, size.y, 1, texelFormat.format, texelFormat.type, mip->readData()); - } - } else { - Q_ASSERT(false); - } - (void)CHECK_GL_ERROR(); - break; - } - } - _populatedMip = targetMip; - return _populatedMip != _minMip; -} - -void GL45Texture::finishTransfer() { - Parent::finishTransfer(); -} - -void GL45Texture::postTransfer() { - Parent::postTransfer(); -} - -void GL45Texture::stripToMip(uint16_t newMinMip) { - if (newMinMip < _minMip) { - qCWarning(gpugl45logging) << "Cannot decrease the min mip"; - return; - } - - if (_sparseInfo.sparse && newMinMip > _sparseInfo.maxSparseLevel) { - qCWarning(gpugl45logging) << "Cannot increase the min mip into the mip tail"; - return; - } - - // If we weren't generating mips before, we need to now that we're stripping down mip levels. - if (!_gpuObject.isAutogenerateMips()) { - qCDebug(gpugl45logging) << "Force mip generation for texture"; - glGenerateTextureMipmap(_id); - } - - - uint8_t maxFace = (uint8_t)((_target == GL_TEXTURE_CUBE_MAP) ? GLTexture::CUBE_NUM_FACES : 1); - if (_sparseInfo.sparse) { - for (uint16_t mip = _minMip; mip < newMinMip; ++mip) { - auto id = _id; - auto mipDimensions = _gpuObject.evalMipDimensions(mip); - glTexturePageCommitmentEXT(id, mip, 0, 0, 0, mipDimensions.x, mipDimensions.y, maxFace, GL_FALSE); - auto deallocatedPages = _sparseInfo.getPageCount(mipDimensions) * maxFace; - assert(deallocatedPages < _sparseInfo.allocatedPages); - _sparseInfo.allocatedPages -= deallocatedPages; - } - _minMip = newMinMip; - } else { - GLuint oldId = _id; - // Find the distance between the old min mip and the new one - uint16 mipDelta = newMinMip - _minMip; - _mipOffset += mipDelta; - const_cast(_maxMip) -= mipDelta; - auto newLevels = usedMipLevels(); - - // Create and setup the new texture (allocate) - glCreateTextures(_target, 1, &const_cast(_id)); - glTextureParameteri(_id, GL_TEXTURE_BASE_LEVEL, 0); - glTextureParameteri(_id, GL_TEXTURE_MAX_LEVEL, _maxMip - _minMip); - Vec3u newDimensions = _gpuObject.evalMipDimensions(_mipOffset); - glTextureStorage2D(_id, newLevels, _internalFormat, newDimensions.x, newDimensions.y); - - // Copy the contents of the old texture to the new - GLuint fbo { 0 }; - glCreateFramebuffers(1, &fbo); - glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo); - for (uint16 targetMip = _minMip; targetMip <= _maxMip; ++targetMip) { - uint16 sourceMip = targetMip + mipDelta; - Vec3u mipDimensions = _gpuObject.evalMipDimensions(targetMip + _mipOffset); - for (GLenum target : getFaceTargets(_target)) { - glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, target, oldId, sourceMip); - (void)CHECK_GL_ERROR(); - glCopyTextureSubImage2D(_id, targetMip, 0, 0, 0, 0, mipDimensions.x, mipDimensions.y); - (void)CHECK_GL_ERROR(); - } - } - glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); - glDeleteFramebuffers(1, &fbo); - glDeleteTextures(1, &oldId); - } - - // Re-sync the sampler to force access to the new mip level - syncSampler(); - updateSize(); -} - -bool GL45Texture::derezable() const { - if (_external) { - return false; - } - auto maxMinMip = _sparseInfo.sparse ? _sparseInfo.maxSparseLevel : _maxMip; - return _transferrable && (_targetMinMip < maxMinMip); -} - -size_t GL45Texture::getMipByteCount(uint16_t mip) const { - if (!_sparseInfo.sparse) { - return Parent::getMipByteCount(mip); - } - - auto dimensions = _gpuObject.evalMipDimensions(_targetMinMip); - return _sparseInfo.getPageCount(dimensions) * _sparseInfo.pageBytes; -} - -std::pair GL45Texture::preDerez() { - assert(!_sparseInfo.sparse || _targetMinMip < _sparseInfo.maxSparseLevel); - size_t freedMemory = getMipByteCount(_targetMinMip); - bool liveMip = _populatedMip != INVALID_MIP && _populatedMip <= _targetMinMip; - ++_targetMinMip; - return { freedMemory, liveMip }; -} - -void GL45Texture::derez() { - if (_sparseInfo.sparse) { - assert(_minMip < _sparseInfo.maxSparseLevel); - } - assert(_minMip < _maxMip); - assert(_transferrable); - stripToMip(_minMip + 1); -} - -size_t GL45Texture::getCurrentGpuSize() const { - if (!_sparseInfo.sparse) { - return Parent::getCurrentGpuSize(); - } - - return _sparseInfo.getSize(); -} - -size_t GL45Texture::getTargetGpuSize() const { - if (!_sparseInfo.sparse) { - return Parent::getTargetGpuSize(); - } - - size_t result = 0; - for (auto mip = _targetMinMip; mip <= _sparseInfo.maxSparseLevel; ++mip) { - result += (_sparseInfo.pageBytes * _sparseInfo.getPageCount(_gpuObject.evalMipDimensions(mip))); - } - - return result; -} - -GL45Texture::~GL45Texture() { - if (_sparseInfo.sparse) { - uint8_t maxFace = (uint8_t)((_target == GL_TEXTURE_CUBE_MAP) ? GLTexture::CUBE_NUM_FACES : 1); - auto maxSparseMip = std::min(_maxMip, _sparseInfo.maxSparseLevel); - for (uint16_t mipLevel = _minMip; mipLevel <= maxSparseMip; ++mipLevel) { - auto mipDimensions = _gpuObject.evalMipDimensions(mipLevel); - glTexturePageCommitmentEXT(_texture, mipLevel, 0, 0, 0, mipDimensions.x, mipDimensions.y, maxFace, GL_FALSE); - auto deallocatedPages = _sparseInfo.getPageCount(mipDimensions) * maxFace; - assert(deallocatedPages <= _sparseInfo.allocatedPages); - _sparseInfo.allocatedPages -= deallocatedPages; - } - - if (0 != _sparseInfo.allocatedPages) { - qCWarning(gpugl45logging) << "Allocated pages remaining " << _id << " " << _sparseInfo.allocatedPages; - } - Backend::decrementTextureGPUSparseCount(); - } -} -GL45Texture::GL45Texture(const std::weak_ptr& backend, const Texture& texture) - : GLTexture(backend, texture, allocate(texture)), _sparseInfo(*this), _targetMinMip(_minMip) -{ - - auto theBackend = _backend.lock(); - if (_transferrable && theBackend && theBackend->isTextureManagementSparseEnabled()) { - _sparseInfo.maybeMakeSparse(); - if (_sparseInfo.sparse) { - Backend::incrementTextureGPUSparseCount(); - } - } -} -#endif diff --git a/libraries/gpu/CMakeLists.txt b/libraries/gpu/CMakeLists.txt index 207431d8c7..384c5709ee 100644 --- a/libraries/gpu/CMakeLists.txt +++ b/libraries/gpu/CMakeLists.txt @@ -1,6 +1,6 @@ set(TARGET_NAME gpu) autoscribe_shader_lib(gpu) setup_hifi_library() -link_hifi_libraries(shared ktx) +link_hifi_libraries(shared) target_nsight() diff --git a/libraries/gpu/src/gpu/Batch.cpp b/libraries/gpu/src/gpu/Batch.cpp index f822da129b..c15da61800 100644 --- a/libraries/gpu/src/gpu/Batch.cpp +++ b/libraries/gpu/src/gpu/Batch.cpp @@ -292,8 +292,15 @@ void Batch::setUniformBuffer(uint32 slot, const BufferView& view) { setUniformBuffer(slot, view._buffer, view._offset, view._size); } + void Batch::setResourceTexture(uint32 slot, const TexturePointer& texture) { + if (texture && texture->getUsage().isExternal()) { + auto recycler = texture->getExternalRecycler(); + Q_ASSERT(recycler); + } + ADD_COMMAND(setResourceTexture); + _params.emplace_back(_textures.cache(texture)); _params.emplace_back(slot); } diff --git a/libraries/gpu/src/gpu/Buffer.h b/libraries/gpu/src/gpu/Buffer.h index 290b84bef0..2507e8e0a6 100644 --- a/libraries/gpu/src/gpu/Buffer.h +++ b/libraries/gpu/src/gpu/Buffer.h @@ -198,7 +198,7 @@ public: BufferView(const BufferPointer& buffer, Size offset, Size size, const Element& element = DEFAULT_ELEMENT); BufferView(const BufferPointer& buffer, Size offset, Size size, uint16 stride, const Element& element = DEFAULT_ELEMENT); - Size getNumElements() const { return (_size - _offset) / _stride; } + Size getNumElements() const { return _size / _element.getSize(); } //Template iterator with random access on the buffer sysmem template diff --git a/libraries/gpu/src/gpu/Context.cpp b/libraries/gpu/src/gpu/Context.cpp index cc570f696f..78b472bdae 100644 --- a/libraries/gpu/src/gpu/Context.cpp +++ b/libraries/gpu/src/gpu/Context.cpp @@ -241,7 +241,6 @@ std::atomic Context::_bufferGPUMemoryUsage { 0 }; std::atomic Context::_textureGPUCount{ 0 }; std::atomic Context::_textureGPUSparseCount { 0 }; -std::atomic Context::_textureTransferPendingSize { 0 }; std::atomic Context::_textureGPUMemoryUsage { 0 }; std::atomic Context::_textureGPUVirtualMemoryUsage { 0 }; std::atomic Context::_textureGPUFramebufferMemoryUsage { 0 }; @@ -318,17 +317,6 @@ void Context::decrementTextureGPUSparseCount() { --_textureGPUSparseCount; } -void Context::updateTextureTransferPendingSize(Size prevObjectSize, Size newObjectSize) { - if (prevObjectSize == newObjectSize) { - return; - } - if (newObjectSize > prevObjectSize) { - _textureTransferPendingSize.fetch_add(newObjectSize - prevObjectSize); - } else { - _textureTransferPendingSize.fetch_sub(prevObjectSize - newObjectSize); - } -} - void Context::updateTextureGPUMemoryUsage(Size prevObjectSize, Size newObjectSize) { if (prevObjectSize == newObjectSize) { return; @@ -402,10 +390,6 @@ uint32_t Context::getTextureGPUSparseCount() { return _textureGPUSparseCount.load(); } -Context::Size Context::getTextureTransferPendingSize() { - return _textureTransferPendingSize.load(); -} - Context::Size Context::getTextureGPUMemoryUsage() { return _textureGPUMemoryUsage.load(); } @@ -435,7 +419,6 @@ void Backend::incrementTextureGPUCount() { Context::incrementTextureGPUCount(); void Backend::decrementTextureGPUCount() { Context::decrementTextureGPUCount(); } void Backend::incrementTextureGPUSparseCount() { Context::incrementTextureGPUSparseCount(); } void Backend::decrementTextureGPUSparseCount() { Context::decrementTextureGPUSparseCount(); } -void Backend::updateTextureTransferPendingSize(Resource::Size prevObjectSize, Resource::Size newObjectSize) { Context::updateTextureTransferPendingSize(prevObjectSize, newObjectSize); } void Backend::updateTextureGPUMemoryUsage(Resource::Size prevObjectSize, Resource::Size newObjectSize) { Context::updateTextureGPUMemoryUsage(prevObjectSize, newObjectSize); } void Backend::updateTextureGPUVirtualMemoryUsage(Resource::Size prevObjectSize, Resource::Size newObjectSize) { Context::updateTextureGPUVirtualMemoryUsage(prevObjectSize, newObjectSize); } void Backend::updateTextureGPUFramebufferMemoryUsage(Resource::Size prevObjectSize, Resource::Size newObjectSize) { Context::updateTextureGPUFramebufferMemoryUsage(prevObjectSize, newObjectSize); } diff --git a/libraries/gpu/src/gpu/Context.h b/libraries/gpu/src/gpu/Context.h index 102c754cd7..01c841992d 100644 --- a/libraries/gpu/src/gpu/Context.h +++ b/libraries/gpu/src/gpu/Context.h @@ -101,7 +101,6 @@ public: static void decrementTextureGPUCount(); static void incrementTextureGPUSparseCount(); static void decrementTextureGPUSparseCount(); - static void updateTextureTransferPendingSize(Resource::Size prevObjectSize, Resource::Size newObjectSize); static void updateTextureGPUMemoryUsage(Resource::Size prevObjectSize, Resource::Size newObjectSize); static void updateTextureGPUSparseMemoryUsage(Resource::Size prevObjectSize, Resource::Size newObjectSize); static void updateTextureGPUVirtualMemoryUsage(Resource::Size prevObjectSize, Resource::Size newObjectSize); @@ -221,7 +220,6 @@ public: static uint32_t getTextureGPUSparseCount(); static Size getFreeGPUMemory(); static Size getUsedGPUMemory(); - static Size getTextureTransferPendingSize(); static Size getTextureGPUMemoryUsage(); static Size getTextureGPUVirtualMemoryUsage(); static Size getTextureGPUFramebufferMemoryUsage(); @@ -265,7 +263,6 @@ protected: static void decrementTextureGPUCount(); static void incrementTextureGPUSparseCount(); static void decrementTextureGPUSparseCount(); - static void updateTextureTransferPendingSize(Size prevObjectSize, Size newObjectSize); static void updateTextureGPUMemoryUsage(Size prevObjectSize, Size newObjectSize); static void updateTextureGPUSparseMemoryUsage(Size prevObjectSize, Size newObjectSize); static void updateTextureGPUVirtualMemoryUsage(Size prevObjectSize, Size newObjectSize); @@ -282,7 +279,6 @@ protected: static std::atomic _textureGPUCount; static std::atomic _textureGPUSparseCount; - static std::atomic _textureTransferPendingSize; static std::atomic _textureGPUMemoryUsage; static std::atomic _textureGPUSparseMemoryUsage; static std::atomic _textureGPUVirtualMemoryUsage; diff --git a/libraries/gpu/src/gpu/Format.cpp b/libraries/gpu/src/gpu/Format.cpp index de202911e3..2a8185bf94 100644 --- a/libraries/gpu/src/gpu/Format.cpp +++ b/libraries/gpu/src/gpu/Format.cpp @@ -10,15 +10,8 @@ using namespace gpu; -const Element Element::COLOR_R_8 { SCALAR, NUINT8, RED }; -const Element Element::COLOR_SR_8 { SCALAR, NUINT8, SRED }; - const Element Element::COLOR_RGBA_32{ VEC4, NUINT8, RGBA }; const Element Element::COLOR_SRGBA_32{ VEC4, NUINT8, SRGBA }; - -const Element Element::COLOR_BGRA_32{ VEC4, NUINT8, BGRA }; -const Element Element::COLOR_SBGRA_32{ VEC4, NUINT8, SBGRA }; - const Element Element::COLOR_R11G11B10{ SCALAR, FLOAT, R11G11B10 }; const Element Element::VEC4F_COLOR_RGBA{ VEC4, FLOAT, RGBA }; const Element Element::VEC2F_UV{ VEC2, FLOAT, UV }; diff --git a/libraries/gpu/src/gpu/Format.h b/libraries/gpu/src/gpu/Format.h index 493a2de3c2..13809a41e6 100644 --- a/libraries/gpu/src/gpu/Format.h +++ b/libraries/gpu/src/gpu/Format.h @@ -133,7 +133,6 @@ static const int SCALAR_COUNT[NUM_DIMENSIONS] = { enum Semantic { RAW = 0, // used as RAW memory - RED, RGB, RGBA, BGRA, @@ -150,7 +149,6 @@ enum Semantic { STENCIL, // Stencil only buffer DEPTH_STENCIL, // Depth Stencil buffer - SRED, SRGB, SRGBA, SBGRA, @@ -229,12 +227,8 @@ public: return getRaw() != right.getRaw(); } - static const Element COLOR_R_8; - static const Element COLOR_SR_8; static const Element COLOR_RGBA_32; static const Element COLOR_SRGBA_32; - static const Element COLOR_BGRA_32; - static const Element COLOR_SBGRA_32; static const Element COLOR_R11G11B10; static const Element VEC4F_COLOR_RGBA; static const Element VEC2F_UV; diff --git a/libraries/gpu/src/gpu/Framebuffer.cpp b/libraries/gpu/src/gpu/Framebuffer.cpp index 0d3291a74d..e8ccfce3b2 100755 --- a/libraries/gpu/src/gpu/Framebuffer.cpp +++ b/libraries/gpu/src/gpu/Framebuffer.cpp @@ -32,7 +32,7 @@ Framebuffer* Framebuffer::create(const std::string& name) { Framebuffer* Framebuffer::create(const std::string& name, const Format& colorBufferFormat, uint16 width, uint16 height) { auto framebuffer = Framebuffer::create(name); - auto colorTexture = TexturePointer(Texture::createRenderBuffer(colorBufferFormat, width, height, Sampler(Sampler::FILTER_MIN_MAG_POINT))); + auto colorTexture = TexturePointer(Texture::create2D(colorBufferFormat, width, height, Sampler(Sampler::FILTER_MIN_MAG_POINT))); colorTexture->setSource("Framebuffer::colorTexture"); framebuffer->setRenderBuffer(0, colorTexture); @@ -43,8 +43,8 @@ Framebuffer* Framebuffer::create(const std::string& name, const Format& colorBuf Framebuffer* Framebuffer::create(const std::string& name, const Format& colorBufferFormat, const Format& depthStencilBufferFormat, uint16 width, uint16 height) { auto framebuffer = Framebuffer::create(name); - auto colorTexture = TexturePointer(Texture::createRenderBuffer(colorBufferFormat, width, height, Sampler(Sampler::FILTER_MIN_MAG_POINT))); - auto depthTexture = TexturePointer(Texture::createRenderBuffer(depthStencilBufferFormat, width, height, Sampler(Sampler::FILTER_MIN_MAG_POINT))); + auto colorTexture = TexturePointer(Texture::create2D(colorBufferFormat, width, height, Sampler(Sampler::FILTER_MIN_MAG_POINT))); + auto depthTexture = TexturePointer(Texture::create2D(depthStencilBufferFormat, width, height, Sampler(Sampler::FILTER_MIN_MAG_POINT))); framebuffer->setRenderBuffer(0, colorTexture); framebuffer->setDepthStencilBuffer(depthTexture, depthStencilBufferFormat); @@ -55,7 +55,7 @@ Framebuffer* Framebuffer::createShadowmap(uint16 width) { auto framebuffer = Framebuffer::create("Shadowmap"); auto depthFormat = Element(gpu::SCALAR, gpu::FLOAT, gpu::DEPTH); // Depth32 texel format - auto depthTexture = TexturePointer(Texture::createRenderBuffer(depthFormat, width, width)); + auto depthTexture = TexturePointer(Texture::create2D(depthFormat, width, width)); Sampler::Desc samplerDesc; samplerDesc._borderColor = glm::vec4(1.0f); samplerDesc._wrapModeU = Sampler::WRAP_BORDER; @@ -143,8 +143,6 @@ int Framebuffer::setRenderBuffer(uint32 slot, const TexturePointer& texture, uin return -1; } - Q_ASSERT(!texture || TextureUsageType::RENDERBUFFER == texture->getUsageType()); - // Check for the slot if (slot >= getMaxNumRenderBuffers()) { return -1; @@ -224,8 +222,6 @@ bool Framebuffer::setDepthStencilBuffer(const TexturePointer& texture, const For return false; } - Q_ASSERT(!texture || TextureUsageType::RENDERBUFFER == texture->getUsageType()); - // Check for the compatibility of size if (texture) { if (!validateTargetCompatibility(*texture)) { diff --git a/libraries/gpu/src/gpu/Texture.cpp b/libraries/gpu/src/gpu/Texture.cpp index 1f66b2900e..5b0c4c876a 100755 --- a/libraries/gpu/src/gpu/Texture.cpp +++ b/libraries/gpu/src/gpu/Texture.cpp @@ -19,7 +19,6 @@ #include #include -#include #include #include "GPULogging.h" @@ -89,10 +88,6 @@ uint32_t Texture::getTextureGPUSparseCount() { return Context::getTextureGPUSparseCount(); } -Texture::Size Texture::getTextureTransferPendingSize() { - return Context::getTextureTransferPendingSize(); -} - Texture::Size Texture::getTextureGPUMemoryUsage() { return Context::getTextureGPUMemoryUsage(); } @@ -125,23 +120,62 @@ void Texture::setAllowedGPUMemoryUsage(Size size) { uint8 Texture::NUM_FACES_PER_TYPE[NUM_TYPES] = { 1, 1, 1, 6 }; -using Storage = Texture::Storage; -using PixelsPointer = Texture::PixelsPointer; -using MemoryStorage = Texture::MemoryStorage; +Texture::Pixels::Pixels(const Element& format, Size size, const Byte* bytes) : + _format(format), + _sysmem(size, bytes), + _isGPULoaded(false) { + Texture::updateTextureCPUMemoryUsage(0, _sysmem.getSize()); +} -void Storage::assignTexture(Texture* texture) { +Texture::Pixels::~Pixels() { + Texture::updateTextureCPUMemoryUsage(_sysmem.getSize(), 0); +} + +Texture::Size Texture::Pixels::resize(Size pSize) { + auto prevSize = _sysmem.getSize(); + auto newSize = _sysmem.resize(pSize); + Texture::updateTextureCPUMemoryUsage(prevSize, newSize); + return newSize; +} + +Texture::Size Texture::Pixels::setData(const Element& format, Size size, const Byte* bytes ) { + _format = format; + auto prevSize = _sysmem.getSize(); + auto newSize = _sysmem.setData(size, bytes); + Texture::updateTextureCPUMemoryUsage(prevSize, newSize); + _isGPULoaded = false; + return newSize; +} + +void Texture::Pixels::notifyGPULoaded() { + _isGPULoaded = true; + auto prevSize = _sysmem.getSize(); + auto newSize = _sysmem.resize(0); + Texture::updateTextureCPUMemoryUsage(prevSize, newSize); +} + +void Texture::Storage::assignTexture(Texture* texture) { _texture = texture; if (_texture) { _type = _texture->getType(); } } -void MemoryStorage::reset() { +void Texture::Storage::reset() { _mips.clear(); bumpStamp(); } -PixelsPointer MemoryStorage::getMipFace(uint16 level, uint8 face) const { +Texture::PixelsPointer Texture::Storage::editMipFace(uint16 level, uint8 face) { + if (level < _mips.size()) { + assert(face < _mips[level].size()); + bumpStamp(); + return _mips[level][face]; + } + return PixelsPointer(); +} + +const Texture::PixelsPointer Texture::Storage::getMipFace(uint16 level, uint8 face) const { if (level < _mips.size()) { assert(face < _mips[level].size()); return _mips[level][face]; @@ -149,12 +183,20 @@ PixelsPointer MemoryStorage::getMipFace(uint16 level, uint8 face) const { return PixelsPointer(); } -bool MemoryStorage::isMipAvailable(uint16 level, uint8 face) const { +void Texture::Storage::notifyMipFaceGPULoaded(uint16 level, uint8 face) const { + PixelsPointer mipFace = getMipFace(level, face); + // Free the mips + if (mipFace) { + mipFace->notifyGPULoaded(); + } +} + +bool Texture::Storage::isMipAvailable(uint16 level, uint8 face) const { PixelsPointer mipFace = getMipFace(level, face); return (mipFace && mipFace->getSize()); } -bool MemoryStorage::allocateMip(uint16 level) { +bool Texture::Storage::allocateMip(uint16 level) { bool changed = false; if (level >= _mips.size()) { _mips.resize(level+1, std::vector(Texture::NUM_FACES_PER_TYPE[getType()])); @@ -164,6 +206,7 @@ bool MemoryStorage::allocateMip(uint16 level) { auto& mip = _mips[level]; for (auto& face : mip) { if (!face) { + face = std::make_shared(); changed = true; } } @@ -173,7 +216,7 @@ bool MemoryStorage::allocateMip(uint16 level) { return changed; } -void MemoryStorage::assignMipData(uint16 level, const storage::StoragePointer& storagePointer) { +bool Texture::Storage::assignMipData(uint16 level, const Element& format, Size size, const Byte* bytes) { allocateMip(level); auto& mip = _mips[level]; @@ -182,63 +225,64 @@ void MemoryStorage::assignMipData(uint16 level, const storage::StoragePointer& s // The bytes assigned here are supposed to contain all the faces bytes of the mip. // For tex1D, 2D, 3D there is only one face // For Cube, we expect the 6 faces in the order X+, X-, Y+, Y-, Z+, Z- - auto sizePerFace = storagePointer->size() / mip.size(); - size_t offset = 0; + auto sizePerFace = size / mip.size(); + auto faceBytes = bytes; + Size allocated = 0; for (auto& face : mip) { - face = storagePointer->createView(sizePerFace, offset); - offset += sizePerFace; + allocated += face->setData(format, sizePerFace, faceBytes); + faceBytes += sizePerFace; } bumpStamp(); + + return allocated == size; } -void Texture::MemoryStorage::assignMipFaceData(uint16 level, uint8 face, const storage::StoragePointer& storagePointer) { +bool Texture::Storage::assignMipFaceData(uint16 level, const Element& format, Size size, const Byte* bytes, uint8 face) { + allocateMip(level); - auto& mip = _mips[level]; + auto mip = _mips[level]; + Size allocated = 0; if (face < mip.size()) { - mip[face] = storagePointer; + auto mipFace = mip[face]; + allocated += mipFace->setData(format, size, bytes); bumpStamp(); } + + return allocated == size; } -Texture* Texture::createExternal(const ExternalRecycler& recycler, const Sampler& sampler) { - Texture* tex = new Texture(TextureUsageType::EXTERNAL); +Texture* Texture::createExternal2D(const ExternalRecycler& recycler, const Sampler& sampler) { + Texture* tex = new Texture(); tex->_type = TEX_2D; tex->_maxMip = 0; tex->_sampler = sampler; + tex->setUsage(Usage::Builder().withExternal().withColor()); tex->setExternalRecycler(recycler); return tex; } -Texture* Texture::createRenderBuffer(const Element& texelFormat, uint16 width, uint16 height, const Sampler& sampler) { - return create(TextureUsageType::RENDERBUFFER, TEX_2D, texelFormat, width, height, 1, 1, 0, sampler); -} - Texture* Texture::create1D(const Element& texelFormat, uint16 width, const Sampler& sampler) { - return create(TextureUsageType::RESOURCE, TEX_1D, texelFormat, width, 1, 1, 1, 0, sampler); + return create(TEX_1D, texelFormat, width, 1, 1, 1, 1, sampler); } Texture* Texture::create2D(const Element& texelFormat, uint16 width, uint16 height, const Sampler& sampler) { - return create(TextureUsageType::RESOURCE, TEX_2D, texelFormat, width, height, 1, 1, 0, sampler); -} - -Texture* Texture::createStrict(const Element& texelFormat, uint16 width, uint16 height, const Sampler& sampler) { - return create(TextureUsageType::STRICT_RESOURCE, TEX_2D, texelFormat, width, height, 1, 1, 0, sampler); + return create(TEX_2D, texelFormat, width, height, 1, 1, 1, sampler); } Texture* Texture::create3D(const Element& texelFormat, uint16 width, uint16 height, uint16 depth, const Sampler& sampler) { - return create(TextureUsageType::RESOURCE, TEX_3D, texelFormat, width, height, depth, 1, 0, sampler); + return create(TEX_3D, texelFormat, width, height, depth, 1, 1, sampler); } Texture* Texture::createCube(const Element& texelFormat, uint16 width, const Sampler& sampler) { - return create(TextureUsageType::RESOURCE, TEX_CUBE, texelFormat, width, width, 1, 1, 0, sampler); + return create(TEX_CUBE, texelFormat, width, width, 1, 1, 1, sampler); } -Texture* Texture::create(TextureUsageType usageType, Type type, const Element& texelFormat, uint16 width, uint16 height, uint16 depth, uint16 numSamples, uint16 numSlices, const Sampler& sampler) +Texture* Texture::create(Type type, const Element& texelFormat, uint16 width, uint16 height, uint16 depth, uint16 numSamples, uint16 numSlices, const Sampler& sampler) { - Texture* tex = new Texture(usageType); - tex->_storage.reset(new MemoryStorage()); + Texture* tex = new Texture(); + tex->_storage.reset(new Storage()); tex->_type = type; tex->_storage->assignTexture(tex); tex->_maxMip = 0; @@ -249,14 +293,16 @@ Texture* Texture::create(TextureUsageType usageType, Type type, const Element& t return tex; } -Texture::Texture(TextureUsageType usageType) : - Resource(), _usageType(usageType) { +Texture::Texture(): + Resource() +{ _textureCPUCount++; } -Texture::~Texture() { +Texture::~Texture() +{ _textureCPUCount--; - if (_usageType == TextureUsageType::EXTERNAL) { + if (getUsage().isExternal()) { Texture::ExternalUpdates externalUpdates; { Lock lock(_externalMutex); @@ -275,7 +321,7 @@ Texture::~Texture() { } Texture::Size Texture::resize(Type type, const Element& texelFormat, uint16 width, uint16 height, uint16 depth, uint16 numSamples, uint16 numSlices) { - if (width && height && depth && numSamples) { + if (width && height && depth && numSamples && numSlices) { bool changed = false; if ( _type != type) { @@ -336,20 +382,20 @@ Texture::Size Texture::resize(Type type, const Element& texelFormat, uint16 widt } Texture::Size Texture::resize1D(uint16 width, uint16 numSamples) { - return resize(TEX_1D, getTexelFormat(), width, 1, 1, numSamples, 0); + return resize(TEX_1D, getTexelFormat(), width, 1, 1, numSamples, 1); } Texture::Size Texture::resize2D(uint16 width, uint16 height, uint16 numSamples) { - return resize(TEX_2D, getTexelFormat(), width, height, 1, numSamples, 0); + return resize(TEX_2D, getTexelFormat(), width, height, 1, numSamples, 1); } Texture::Size Texture::resize3D(uint16 width, uint16 height, uint16 depth, uint16 numSamples) { - return resize(TEX_3D, getTexelFormat(), width, height, depth, numSamples, 0); + return resize(TEX_3D, getTexelFormat(), width, height, depth, numSamples, 1); } Texture::Size Texture::resizeCube(uint16 width, uint16 numSamples) { - return resize(TEX_CUBE, getTexelFormat(), width, 1, 1, numSamples, 0); + return resize(TEX_CUBE, getTexelFormat(), width, 1, 1, numSamples, 1); } Texture::Size Texture::reformat(const Element& texelFormat) { - return resize(_type, texelFormat, getWidth(), getHeight(), getDepth(), getNumSamples(), _numSlices); + return resize(_type, texelFormat, getWidth(), getHeight(), getDepth(), getNumSamples(), getNumSlices()); } bool Texture::isColorRenderTarget() const { @@ -380,83 +426,69 @@ uint16 Texture::evalNumMips() const { return evalNumMips({ _width, _height, _depth }); } -void Texture::setStoredMipFormat(const Element& format) { - _storage->setFormat(format); -} - -const Element& Texture::getStoredMipFormat() const { - return _storage->getFormat(); -} - -void Texture::assignStoredMip(uint16 level, Size size, const Byte* bytes) { - storage::StoragePointer storage = std::make_shared(size, bytes); - assignStoredMip(level, storage); -} - -void Texture::assignStoredMipFace(uint16 level, uint8 face, Size size, const Byte* bytes) { - storage::StoragePointer storage = std::make_shared(size, bytes); - assignStoredMipFace(level, face, storage); -} - -void Texture::assignStoredMip(uint16 level, storage::StoragePointer& storage) { +bool Texture::assignStoredMip(uint16 level, const Element& format, Size size, const Byte* bytes) { // Check that level accessed make sense if (level != 0) { if (_autoGenerateMips) { - return; + return false; } if (level >= evalNumMips()) { - return; + return false; } } // THen check that the mem texture passed make sense with its format - Size expectedSize = evalStoredMipSize(level, getStoredMipFormat()); - auto size = storage->size(); - if (storage->size() == expectedSize) { - _storage->assignMipData(level, storage); - _maxMip = std::max(_maxMip, level); - _stamp++; - } else if (size > expectedSize) { - // NOTE: We are facing this case sometime because apparently QImage (from where we get the bits) is generating images - // and alligning the line of pixels to 32 bits. - // We should probably consider something a bit more smart to get the correct result but for now (UI elements) - // it seems to work... - _storage->assignMipData(level, storage); - _maxMip = std::max(_maxMip, level); - _stamp++; - } -} - -void Texture::assignStoredMipFace(uint16 level, uint8 face, storage::StoragePointer& storage) { - // Check that level accessed make sense - if (level != 0) { - if (_autoGenerateMips) { - return; - } - if (level >= evalNumMips()) { - return; - } - } - - // THen check that the mem texture passed make sense with its format - Size expectedSize = evalStoredMipFaceSize(level, getStoredMipFormat()); - auto size = storage->size(); + Size expectedSize = evalStoredMipSize(level, format); if (size == expectedSize) { - _storage->assignMipFaceData(level, face, storage); + _storage->assignMipData(level, format, size, bytes); _maxMip = std::max(_maxMip, level); _stamp++; + return true; } else if (size > expectedSize) { // NOTE: We are facing this case sometime because apparently QImage (from where we get the bits) is generating images // and alligning the line of pixels to 32 bits. // We should probably consider something a bit more smart to get the correct result but for now (UI elements) // it seems to work... - _storage->assignMipFaceData(level, face, storage); + _storage->assignMipData(level, format, size, bytes); _maxMip = std::max(_maxMip, level); _stamp++; + return true; } + + return false; } +bool Texture::assignStoredMipFace(uint16 level, const Element& format, Size size, const Byte* bytes, uint8 face) { + // Check that level accessed make sense + if (level != 0) { + if (_autoGenerateMips) { + return false; + } + if (level >= evalNumMips()) { + return false; + } + } + + // THen check that the mem texture passed make sense with its format + Size expectedSize = evalStoredMipFaceSize(level, format); + if (size == expectedSize) { + _storage->assignMipFaceData(level, format, size, bytes, face); + _stamp++; + return true; + } else if (size > expectedSize) { + // NOTE: We are facing this case sometime because apparently QImage (from where we get the bits) is generating images + // and alligning the line of pixels to 32 bits. + // We should probably consider something a bit more smart to get the correct result but for now (UI elements) + // it seems to work... + _storage->assignMipFaceData(level, format, size, bytes, face); + _stamp++; + return true; + } + + return false; +} + uint16 Texture::autoGenerateMips(uint16 maxMip) { bool changed = false; if (!_autoGenerateMips) { @@ -490,7 +522,7 @@ uint16 Texture::getStoredMipHeight(uint16 level) const { if (mip && mip->getSize()) { return evalMipHeight(level); } - return 0; + return 0; } uint16 Texture::getStoredMipDepth(uint16 level) const { @@ -762,16 +794,7 @@ bool sphericalHarmonicsFromTexture(const gpu::Texture& cubeTexture, std::vector< for(int face=0; face < gpu::Texture::NUM_CUBE_FACES; face++) { PROFILE_RANGE(render_gpu, "ProcessFace"); - auto mipFormat = cubeTexture.getStoredMipFormat(); - auto numComponents = mipFormat.getScalarCount(); - int roffset { 0 }; - int goffset { 1 }; - int boffset { 2 }; - if ((mipFormat.getSemantic() == gpu::BGRA) || (mipFormat.getSemantic() == gpu::SBGRA)) { - roffset = 2; - boffset = 0; - } - + auto numComponents = cubeTexture.accessStoredMipFace(0,face)->getFormat().getScalarCount(); auto data = cubeTexture.accessStoredMipFace(0,face)->readData(); if (data == nullptr) { continue; @@ -859,9 +882,9 @@ bool sphericalHarmonicsFromTexture(const gpu::Texture& cubeTexture, std::vector< for (int i = 0; i < stride; ++i) { for (int j = 0; j < stride; ++j) { int k = (int)(x + i - halfStride + (y + j - halfStride) * width) * numComponents; - red += ColorUtils::sRGB8ToLinearFloat(data[k + roffset]); - green += ColorUtils::sRGB8ToLinearFloat(data[k + goffset]); - blue += ColorUtils::sRGB8ToLinearFloat(data[k + boffset]); + red += ColorUtils::sRGB8ToLinearFloat(data[k]); + green += ColorUtils::sRGB8ToLinearFloat(data[k + 1]); + blue += ColorUtils::sRGB8ToLinearFloat(data[k + 2]); } } glm::vec3 clr(red, green, blue); @@ -888,6 +911,8 @@ bool sphericalHarmonicsFromTexture(const gpu::Texture& cubeTexture, std::vector< // save result for(uint i=0; i < sqOrder; i++) { + // gamma Correct + // output[i] = linearTosRGB(glm::vec3(resultR[i], resultG[i], resultB[i])); output[i] = glm::vec3(resultR[i], resultG[i], resultB[i]); } @@ -976,7 +1001,3 @@ Texture::ExternalUpdates Texture::getUpdates() const { } return result; } - -void Texture::setStorage(std::unique_ptr& newStorage) { - _storage.swap(newStorage); -} diff --git a/libraries/gpu/src/gpu/Texture.h b/libraries/gpu/src/gpu/Texture.h index f7297b3280..856bd4983d 100755 --- a/libraries/gpu/src/gpu/Texture.h +++ b/libraries/gpu/src/gpu/Texture.h @@ -17,17 +17,9 @@ #include #include -#include - #include "Forward.h" #include "Resource.h" -namespace ktx { - class KTX; - using KTXUniquePointer = std::unique_ptr; - struct Header; -} - namespace gpu { // THe spherical harmonics is a nice tool for cubemap, so if required, the irradiance SH can be automatically generated @@ -143,18 +135,10 @@ public: uint8 getMinMip() const { return _desc._minMip; } uint8 getMaxMip() const { return _desc._maxMip; } - const Desc& getDesc() const { return _desc; } protected: Desc _desc; }; -enum class TextureUsageType { - RENDERBUFFER, // Used as attachments to a framebuffer - RESOURCE, // Resource textures, like materials... subject to memory manipulation - STRICT_RESOURCE, // Resource textures not subject to manipulation, like the normal fitting texture - EXTERNAL, -}; - class Texture : public Resource { static std::atomic _textureCPUCount; static std::atomic _textureCPUMemoryUsage; @@ -163,12 +147,10 @@ class Texture : public Resource { static void updateTextureCPUMemoryUsage(Size prevObjectSize, Size newObjectSize); public: - static const uint32_t CUBE_FACE_COUNT { 6 }; static uint32_t getTextureCPUCount(); static Size getTextureCPUMemoryUsage(); static uint32_t getTextureGPUCount(); static uint32_t getTextureGPUSparseCount(); - static Size getTextureTransferPendingSize(); static Size getTextureGPUMemoryUsage(); static Size getTextureGPUVirtualMemoryUsage(); static Size getTextureGPUFramebufferMemoryUsage(); @@ -191,9 +173,9 @@ public: NORMAL, // Texture is a normal map ALPHA, // Texture has an alpha channel ALPHA_MASK, // Texture alpha channel is a Mask 0/1 + EXTERNAL, NUM_FLAGS, }; - typedef std::bitset Flags; // The key is the Flags @@ -217,6 +199,7 @@ public: Builder& withNormal() { _flags.set(NORMAL); return (*this); } Builder& withAlpha() { _flags.set(ALPHA); return (*this); } Builder& withAlphaMask() { _flags.set(ALPHA_MASK); return (*this); } + Builder& withExternal() { _flags.set(EXTERNAL); return (*this); } }; Usage(const Builder& builder) : Usage(builder._flags) {} @@ -225,12 +208,37 @@ public: bool isAlpha() const { return _flags[ALPHA]; } bool isAlphaMask() const { return _flags[ALPHA_MASK]; } + bool isExternal() const { return _flags[EXTERNAL]; } + bool operator==(const Usage& usage) { return (_flags == usage._flags); } bool operator!=(const Usage& usage) { return (_flags != usage._flags); } }; - using PixelsPointer = storage::StoragePointer; + class Pixels { + public: + Pixels() {} + Pixels(const Pixels& pixels) = default; + Pixels(const Element& format, Size size, const Byte* bytes); + ~Pixels(); + + const Byte* readData() const { return _sysmem.readData(); } + Size getSize() const { return _sysmem.getSize(); } + Size resize(Size pSize); + Size setData(const Element& format, Size size, const Byte* bytes ); + + const Element& getFormat() const { return _format; } + + void notifyGPULoaded(); + + protected: + Element _format; + Sysmem _sysmem; + bool _isGPULoaded; + + friend class Texture; + }; + typedef std::shared_ptr< Pixels > PixelsPointer; enum Type { TEX_1D = 0, @@ -253,78 +261,46 @@ public: NUM_CUBE_FACES, // Not a valid vace index }; - class Storage { public: Storage() {} virtual ~Storage() {} + virtual void reset(); + virtual PixelsPointer editMipFace(uint16 level, uint8 face = 0); + virtual const PixelsPointer getMipFace(uint16 level, uint8 face = 0) const; + virtual bool allocateMip(uint16 level); + virtual bool assignMipData(uint16 level, const Element& format, Size size, const Byte* bytes); + virtual bool assignMipFaceData(uint16 level, const Element& format, Size size, const Byte* bytes, uint8 face); + virtual bool isMipAvailable(uint16 level, uint8 face = 0) const; - virtual void reset() = 0; - virtual PixelsPointer getMipFace(uint16 level, uint8 face = 0) const = 0; - virtual void assignMipData(uint16 level, const storage::StoragePointer& storage) = 0; - virtual void assignMipFaceData(uint16 level, uint8 face, const storage::StoragePointer& storage) = 0; - virtual bool isMipAvailable(uint16 level, uint8 face = 0) const = 0; Texture::Type getType() const { return _type; } - + Stamp getStamp() const { return _stamp; } Stamp bumpStamp() { return ++_stamp; } + protected: + Stamp _stamp = 0; + Texture* _texture = nullptr; // Points to the parent texture (not owned) + Texture::Type _type = Texture::TEX_2D; // The type of texture is needed to know the number of faces to expect + std::vector> _mips; // an array of mips, each mip is an array of faces - void setFormat(const Element& format) { _format = format; } - const Element& getFormat() const { return _format; } - - private: - Stamp _stamp { 0 }; - Element _format; - Texture::Type _type { Texture::TEX_2D }; // The type of texture is needed to know the number of faces to expect - Texture* _texture { nullptr }; // Points to the parent texture (not owned) virtual void assignTexture(Texture* tex); // Texture storage is pointing to ONE corrresponding Texture. const Texture* getTexture() const { return _texture; } + friend class Texture; + + // THis should be only called by the Texture from the Backend to notify the storage that the specified mip face pixels + // have been uploaded to the GPU memory. IT is possible for the storage to free the system memory then + virtual void notifyMipFaceGPULoaded(uint16 level, uint8 face) const; }; - class MemoryStorage : public Storage { - public: - void reset() override; - PixelsPointer getMipFace(uint16 level, uint8 face = 0) const override; - void assignMipData(uint16 level, const storage::StoragePointer& storage) override; - void assignMipFaceData(uint16 level, uint8 face, const storage::StoragePointer& storage) override; - bool isMipAvailable(uint16 level, uint8 face = 0) const override; - - protected: - bool allocateMip(uint16 level); - std::vector> _mips; // an array of mips, each mip is an array of faces - }; - - class KtxStorage : public Storage { - public: - KtxStorage(ktx::KTXUniquePointer& ktxData); - PixelsPointer getMipFace(uint16 level, uint8 face = 0) const override; - // By convention, all mip levels and faces MUST be populated when using KTX backing - bool isMipAvailable(uint16 level, uint8 face = 0) const override { return true; } - - void assignMipData(uint16 level, const storage::StoragePointer& storage) override { - throw std::runtime_error("Invalid call"); - } - - void assignMipFaceData(uint16 level, uint8 face, const storage::StoragePointer& storage) override { - throw std::runtime_error("Invalid call"); - } - void reset() override { } - - protected: - ktx::KTXUniquePointer _ktxData; - friend class Texture; - }; - + static Texture* create1D(const Element& texelFormat, uint16 width, const Sampler& sampler = Sampler()); static Texture* create2D(const Element& texelFormat, uint16 width, uint16 height, const Sampler& sampler = Sampler()); static Texture* create3D(const Element& texelFormat, uint16 width, uint16 height, uint16 depth, const Sampler& sampler = Sampler()); static Texture* createCube(const Element& texelFormat, uint16 width, const Sampler& sampler = Sampler()); - static Texture* createRenderBuffer(const Element& texelFormat, uint16 width, uint16 height, const Sampler& sampler = Sampler()); - static Texture* createStrict(const Element& texelFormat, uint16 width, uint16 height, const Sampler& sampler = Sampler()); - static Texture* createExternal(const ExternalRecycler& recycler, const Sampler& sampler = Sampler()); + static Texture* createExternal2D(const ExternalRecycler& recycler, const Sampler& sampler = Sampler()); - Texture(TextureUsageType usageType); + Texture(); Texture(const Texture& buf); // deep copy of the sysmem texture Texture& operator=(const Texture& buf); // deep copy of the sysmem texture ~Texture(); @@ -349,7 +325,6 @@ public: // Size and format Type getType() const { return _type; } - TextureUsageType getUsageType() const { return _usageType; } bool isColorRenderTarget() const; bool isDepthStencilRenderTarget() const; @@ -372,12 +347,7 @@ public: uint32 getNumTexels() const { return _width * _height * _depth * getNumFaces(); } - // The texture is an array if the _numSlices is not 0. - // otherwise, if _numSLices is 0, then the texture is NOT an array - // The number of slices returned is 1 at the minimum (if not an array) or the actual _numSlices. - bool isArray() const { return _numSlices > 0; } - uint16 getNumSlices() const { return (isArray() ? _numSlices : 1); } - + uint16 getNumSlices() const { return _numSlices; } uint16 getNumSamples() const { return _numSamples; } @@ -459,29 +429,18 @@ public: // Managing Storage and mips - // Mip storage format is constant across all mips - void setStoredMipFormat(const Element& format); - const Element& getStoredMipFormat() const; - // Manually allocate the mips down until the specified maxMip // this is just allocating the sysmem version of it // in case autoGen is on, this doesn't allocate // Explicitely assign mip data for a certain level // If Bytes is NULL then simply allocate the space so mip sysmem can be accessed - - void assignStoredMip(uint16 level, Size size, const Byte* bytes); - void assignStoredMipFace(uint16 level, uint8 face, Size size, const Byte* bytes); - - void assignStoredMip(uint16 level, storage::StoragePointer& storage); - void assignStoredMipFace(uint16 level, uint8 face, storage::StoragePointer& storage); + bool assignStoredMip(uint16 level, const Element& format, Size size, const Byte* bytes); + bool assignStoredMipFace(uint16 level, const Element& format, Size size, const Byte* bytes, uint8 face); // Access the the sub mips bool isStoredMipFaceAvailable(uint16 level, uint8 face = 0) const { return _storage->isMipAvailable(level, face); } const PixelsPointer accessStoredMipFace(uint16 level, uint8 face = 0) const { return _storage->getMipFace(level, face); } - void setStorage(std::unique_ptr& newStorage); - void setKtxBacking(ktx::KTXUniquePointer& newBacking); - // access sizes for the stored mips uint16 getStoredMipWidth(uint16 level) const; uint16 getStoredMipHeight(uint16 level) const; @@ -505,8 +464,8 @@ public: const Sampler& getSampler() const { return _sampler; } Stamp getSamplerStamp() const { return _samplerStamp; } - void setFallbackTexture(const TexturePointer& fallback) { _fallback = fallback; } - TexturePointer getFallbackTexture() const { return _fallback.lock(); } + // Only callable by the Backend + void notifyMipFaceGPULoaded(uint16 level, uint8 face = 0) const { return _storage->notifyMipFaceGPULoaded(level, face); } void setExternalTexture(uint32 externalId, void* externalFence); void setExternalRecycler(const ExternalRecycler& recycler); @@ -516,45 +475,36 @@ public: ExternalUpdates getUpdates() const; - // Textures can be serialized directly to ktx data file, here is how - static ktx::KTXUniquePointer serialize(const Texture& texture); - static Texture* unserialize(const ktx::KTXUniquePointer& srcData, TextureUsageType usageType = TextureUsageType::RESOURCE, Usage usage = Usage(), const Sampler::Desc& sampler = Sampler::Desc()); - static bool evalKTXFormat(const Element& mipFormat, const Element& texelFormat, ktx::Header& header); - static bool evalTextureFormat(const ktx::Header& header, Element& mipFormat, Element& texelFormat); - protected: - const TextureUsageType _usageType; - // Should only be accessed internally or by the backend sync function mutable Mutex _externalMutex; mutable std::list _externalUpdates; ExternalRecycler _externalRecycler; - std::weak_ptr _fallback; // Not strictly necessary, but incredibly useful for debugging std::string _source; std::unique_ptr< Storage > _storage; - Stamp _stamp { 0 }; + Stamp _stamp = 0; Sampler _sampler; - Stamp _samplerStamp { 0 }; + Stamp _samplerStamp; - uint32 _size { 0 }; + uint32 _size = 0; Element _texelFormat; - uint16 _width { 1 }; - uint16 _height { 1 }; - uint16 _depth { 1 }; + uint16 _width = 1; + uint16 _height = 1; + uint16 _depth = 1; - uint16 _numSamples { 1 }; - uint16 _numSlices { 0 }; // if _numSlices is 0, the texture is not an "Array", the getNumSlices reported is 1 + uint16 _numSamples = 1; + uint16 _numSlices = 1; uint16 _maxMip { 0 }; uint16 _minMip { 0 }; - Type _type { TEX_1D }; + Type _type = TEX_1D; Usage _usage; @@ -563,7 +513,7 @@ protected: bool _isIrradianceValid = false; bool _defined = false; - static Texture* create(TextureUsageType usageType, Type type, const Element& texelFormat, uint16 width, uint16 height, uint16 depth, uint16 numSamples, uint16 numSlices, const Sampler& sampler); + static Texture* create(Type type, const Element& texelFormat, uint16 width, uint16 height, uint16 depth, uint16 numSamples, uint16 numSlices, const Sampler& sampler); Size resize(Type type, const Element& texelFormat, uint16 width, uint16 height, uint16 depth, uint16 numSamples, uint16 numSlices); }; diff --git a/libraries/gpu/src/gpu/Texture_ktx.cpp b/libraries/gpu/src/gpu/Texture_ktx.cpp deleted file mode 100644 index 5f0ededee7..0000000000 --- a/libraries/gpu/src/gpu/Texture_ktx.cpp +++ /dev/null @@ -1,289 +0,0 @@ -// -// Texture_ktx.cpp -// libraries/gpu/src/gpu -// -// Created by Sam Gateau on 2/16/2017. -// 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 "Texture.h" - -#include -using namespace gpu; - -using PixelsPointer = Texture::PixelsPointer; -using KtxStorage = Texture::KtxStorage; - -struct GPUKTXPayload { - Sampler::Desc _samplerDesc; - Texture::Usage _usage; - TextureUsageType _usageType; - - static std::string KEY; - static bool isGPUKTX(const ktx::KeyValue& val) { - return (val._key.compare(KEY) == 0); - } - - static bool findInKeyValues(const ktx::KeyValues& keyValues, GPUKTXPayload& payload) { - auto found = std::find_if(keyValues.begin(), keyValues.end(), isGPUKTX); - if (found != keyValues.end()) { - if ((*found)._value.size() == sizeof(GPUKTXPayload)) { - memcpy(&payload, (*found)._value.data(), sizeof(GPUKTXPayload)); - return true; - } - } - return false; - } -}; - -std::string GPUKTXPayload::KEY { "hifi.gpu" }; - -KtxStorage::KtxStorage(ktx::KTXUniquePointer& ktxData) { - - // if the source ktx is valid let's config this KtxStorage correctly - if (ktxData && ktxData->getHeader()) { - - // now that we know the ktx, let's get the header info to configure this Texture::Storage: - Format mipFormat = Format::COLOR_BGRA_32; - Format texelFormat = Format::COLOR_SRGBA_32; - if (Texture::evalTextureFormat(*ktxData->getHeader(), mipFormat, texelFormat)) { - _format = mipFormat; - } - - - } - - _ktxData.reset(ktxData.release()); -} - -PixelsPointer KtxStorage::getMipFace(uint16 level, uint8 face) const { - return _ktxData->getMipFaceTexelsData(level, face); -} - -void Texture::setKtxBacking(ktx::KTXUniquePointer& ktxBacking) { - auto newBacking = std::unique_ptr(new KtxStorage(ktxBacking)); - setStorage(newBacking); -} - -ktx::KTXUniquePointer Texture::serialize(const Texture& texture) { - ktx::Header header; - - // From texture format to ktx format description - auto texelFormat = texture.getTexelFormat(); - auto mipFormat = texture.getStoredMipFormat(); - - if (!Texture::evalKTXFormat(mipFormat, texelFormat, header)) { - return nullptr; - } - - // Set Dimensions - uint32_t numFaces = 1; - switch (texture.getType()) { - case TEX_1D: { - if (texture.isArray()) { - header.set1DArray(texture.getWidth(), texture.getNumSlices()); - } else { - header.set1D(texture.getWidth()); - } - break; - } - case TEX_2D: { - if (texture.isArray()) { - header.set2DArray(texture.getWidth(), texture.getHeight(), texture.getNumSlices()); - } else { - header.set2D(texture.getWidth(), texture.getHeight()); - } - break; - } - case TEX_3D: { - if (texture.isArray()) { - header.set3DArray(texture.getWidth(), texture.getHeight(), texture.getDepth(), texture.getNumSlices()); - } else { - header.set3D(texture.getWidth(), texture.getHeight(), texture.getDepth()); - } - break; - } - case TEX_CUBE: { - if (texture.isArray()) { - header.setCubeArray(texture.getWidth(), texture.getHeight(), texture.getNumSlices()); - } else { - header.setCube(texture.getWidth(), texture.getHeight()); - } - numFaces = Texture::CUBE_FACE_COUNT; - break; - } - default: - return nullptr; - } - - // Number level of mips coming - header.numberOfMipmapLevels = texture.maxMip() + 1; - - ktx::Images images; - for (uint32_t level = 0; level < header.numberOfMipmapLevels; level++) { - auto mip = texture.accessStoredMipFace(level); - if (mip) { - if (numFaces == 1) { - images.emplace_back(ktx::Image((uint32_t)mip->getSize(), 0, mip->readData())); - } else { - ktx::Image::FaceBytes cubeFaces(Texture::CUBE_FACE_COUNT); - cubeFaces[0] = mip->readData(); - for (uint32_t face = 1; face < Texture::CUBE_FACE_COUNT; face++) { - cubeFaces[face] = texture.accessStoredMipFace(level, face)->readData(); - } - images.emplace_back(ktx::Image((uint32_t)mip->getSize(), 0, cubeFaces)); - } - } - } - - GPUKTXPayload keyval; - keyval._samplerDesc = texture.getSampler().getDesc(); - keyval._usage = texture.getUsage(); - keyval._usageType = texture.getUsageType(); - ktx::KeyValues keyValues; - keyValues.emplace_back(ktx::KeyValue(GPUKTXPayload::KEY, sizeof(GPUKTXPayload), (ktx::Byte*) &keyval)); - - auto ktxBuffer = ktx::KTX::create(header, images, keyValues); -#if 0 - auto expectedMipCount = texture.evalNumMips(); - assert(expectedMipCount == ktxBuffer->_images.size()); - assert(expectedMipCount == header.numberOfMipmapLevels); - - assert(0 == memcmp(&header, ktxBuffer->getHeader(), sizeof(ktx::Header))); - assert(ktxBuffer->_images.size() == images.size()); - auto start = ktxBuffer->_storage->data(); - for (size_t i = 0; i < images.size(); ++i) { - auto expected = images[i]; - auto actual = ktxBuffer->_images[i]; - assert(expected._padding == actual._padding); - assert(expected._numFaces == actual._numFaces); - assert(expected._imageSize == actual._imageSize); - assert(expected._faceSize == actual._faceSize); - assert(actual._faceBytes.size() == actual._numFaces); - for (uint32_t face = 0; face < expected._numFaces; ++face) { - auto expectedFace = expected._faceBytes[face]; - auto actualFace = actual._faceBytes[face]; - auto offset = actualFace - start; - assert(offset % 4 == 0); - assert(expectedFace != actualFace); - assert(0 == memcmp(expectedFace, actualFace, expected._faceSize)); - } - } -#endif - return ktxBuffer; -} - -Texture* Texture::unserialize(const ktx::KTXUniquePointer& srcData, TextureUsageType usageType, Usage usage, const Sampler::Desc& sampler) { - if (!srcData) { - return nullptr; - } - const auto& header = *srcData->getHeader(); - - Format mipFormat = Format::COLOR_BGRA_32; - Format texelFormat = Format::COLOR_SRGBA_32; - - if (!Texture::evalTextureFormat(header, mipFormat, texelFormat)) { - return nullptr; - } - - // Find Texture Type based on dimensions - Type type = TEX_1D; - if (header.pixelWidth == 0) { - return nullptr; - } else if (header.pixelHeight == 0) { - type = TEX_1D; - } else if (header.pixelDepth == 0) { - if (header.numberOfFaces == ktx::NUM_CUBEMAPFACES) { - type = TEX_CUBE; - } else { - type = TEX_2D; - } - } else { - type = TEX_3D; - } - - - // If found, use the - GPUKTXPayload gpuktxKeyValue; - bool isGPUKTXPayload = GPUKTXPayload::findInKeyValues(srcData->_keyValues, gpuktxKeyValue); - - auto tex = Texture::create( (isGPUKTXPayload ? gpuktxKeyValue._usageType : usageType), - type, - texelFormat, - header.getPixelWidth(), - header.getPixelHeight(), - header.getPixelDepth(), - 1, // num Samples - header.getNumberOfSlices(), - (isGPUKTXPayload ? gpuktxKeyValue._samplerDesc : sampler)); - - tex->setUsage((isGPUKTXPayload ? gpuktxKeyValue._usage : usage)); - - // Assing the mips availables - tex->setStoredMipFormat(mipFormat); - uint16_t level = 0; - for (auto& image : srcData->_images) { - for (uint32_t face = 0; face < image._numFaces; face++) { - tex->assignStoredMipFace(level, face, image._faceSize, image._faceBytes[face]); - } - level++; - } - - return tex; -} - -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_Uncompressed::RGBA8, ktx::GLBaseInternalFormat::RGBA); - } else if (texelFormat == Format::COLOR_RGBA_32 && mipFormat == Format::COLOR_RGBA_32) { - header.setUncompressed(ktx::GLType::UNSIGNED_BYTE, 1, ktx::GLFormat::RGBA, ktx::GLInternalFormat_Uncompressed::RGBA8, ktx::GLBaseInternalFormat::RGBA); - } else if (texelFormat == Format::COLOR_SRGBA_32 && mipFormat == Format::COLOR_SBGRA_32) { - header.setUncompressed(ktx::GLType::UNSIGNED_BYTE, 1, ktx::GLFormat::BGRA, ktx::GLInternalFormat_Uncompressed::SRGB8_ALPHA8, ktx::GLBaseInternalFormat::RGBA); - } else if (texelFormat == Format::COLOR_SRGBA_32 && mipFormat == Format::COLOR_SRGBA_32) { - header.setUncompressed(ktx::GLType::UNSIGNED_BYTE, 1, ktx::GLFormat::RGBA, ktx::GLInternalFormat_Uncompressed::SRGB8_ALPHA8, ktx::GLBaseInternalFormat::RGBA); - } else if (texelFormat == Format::COLOR_R_8 && mipFormat == Format::COLOR_R_8) { - header.setUncompressed(ktx::GLType::UNSIGNED_BYTE, 1, ktx::GLFormat::RED, ktx::GLInternalFormat_Uncompressed::R8, ktx::GLBaseInternalFormat::RED); - } else { - return false; - } - - return true; -} - -bool Texture::evalTextureFormat(const ktx::Header& header, Element& mipFormat, Element& texelFormat) { - if (header.getGLFormat() == ktx::GLFormat::BGRA && header.getGLType() == ktx::GLType::UNSIGNED_BYTE && header.getTypeSize() == 1) { - if (header.getGLInternaFormat_Uncompressed() == ktx::GLInternalFormat_Uncompressed::RGBA8) { - mipFormat = Format::COLOR_BGRA_32; - texelFormat = Format::COLOR_RGBA_32; - } else if (header.getGLInternaFormat_Uncompressed() == ktx::GLInternalFormat_Uncompressed::SRGB8_ALPHA8) { - mipFormat = Format::COLOR_SBGRA_32; - texelFormat = Format::COLOR_SRGBA_32; - } else { - return false; - } - } else if (header.getGLFormat() == ktx::GLFormat::RGBA && header.getGLType() == ktx::GLType::UNSIGNED_BYTE && header.getTypeSize() == 1) { - if (header.getGLInternaFormat_Uncompressed() == ktx::GLInternalFormat_Uncompressed::RGBA8) { - mipFormat = Format::COLOR_RGBA_32; - texelFormat = Format::COLOR_RGBA_32; - } else if (header.getGLInternaFormat_Uncompressed() == ktx::GLInternalFormat_Uncompressed::SRGB8_ALPHA8) { - mipFormat = Format::COLOR_SRGBA_32; - texelFormat = Format::COLOR_SRGBA_32; - } else { - return false; - } - } else if (header.getGLFormat() == ktx::GLFormat::RED && header.getGLType() == ktx::GLType::UNSIGNED_BYTE && header.getTypeSize() == 1) { - mipFormat = Format::COLOR_R_8; - if (header.getGLInternaFormat_Uncompressed() == ktx::GLInternalFormat_Uncompressed::R8) { - texelFormat = Format::COLOR_R_8; - } else { - return false; - } - } else { - return false; - } - return true; -} diff --git a/libraries/ktx/CMakeLists.txt b/libraries/ktx/CMakeLists.txt deleted file mode 100644 index 404660b247..0000000000 --- a/libraries/ktx/CMakeLists.txt +++ /dev/null @@ -1,3 +0,0 @@ -set(TARGET_NAME ktx) -setup_hifi_library() -link_hifi_libraries() \ No newline at end of file diff --git a/libraries/ktx/src/ktx/KTX.cpp b/libraries/ktx/src/ktx/KTX.cpp deleted file mode 100644 index bbd4e1bc86..0000000000 --- a/libraries/ktx/src/ktx/KTX.cpp +++ /dev/null @@ -1,165 +0,0 @@ -// -// KTX.cpp -// ktx/src/ktx -// -// Created by Zach Pomerantz on 2/08/2017. -// Copyright 2017 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#include "KTX.h" - -#include //min max and more - -using namespace ktx; - -uint32_t Header::evalPadding(size_t byteSize) { - //auto padding = byteSize % PACKING_SIZE; - // return (uint32_t) (padding ? PACKING_SIZE - padding : 0); - return (uint32_t) (3 - (byteSize + 3) % PACKING_SIZE);// padding ? PACKING_SIZE - padding : 0); -} - - -const Header::Identifier ktx::Header::IDENTIFIER {{ - 0xAB, 0x4B, 0x54, 0x58, 0x20, 0x31, 0x31, 0xBB, 0x0D, 0x0A, 0x1A, 0x0A -}}; - -Header::Header() { - memcpy(identifier, IDENTIFIER.data(), IDENTIFIER_LENGTH); -} - -uint32_t Header::evalMaxDimension() const { - return std::max(getPixelWidth(), std::max(getPixelHeight(), getPixelDepth())); -} - -uint32_t Header::evalPixelWidth(uint32_t level) const { - return std::max(getPixelWidth() >> level, 1U); -} -uint32_t Header::evalPixelHeight(uint32_t level) const { - return std::max(getPixelHeight() >> level, 1U); -} -uint32_t Header::evalPixelDepth(uint32_t level) const { - return std::max(getPixelDepth() >> level, 1U); -} - -size_t Header::evalPixelSize() const { - return glTypeSize; // Really we should generate the size from the FOrmat etc -} - -size_t Header::evalRowSize(uint32_t level) const { - auto pixWidth = evalPixelWidth(level); - auto pixSize = evalPixelSize(); - auto netSize = pixWidth * pixSize; - auto padding = evalPadding(netSize); - return netSize + padding; -} -size_t Header::evalFaceSize(uint32_t level) const { - auto pixHeight = evalPixelHeight(level); - auto pixDepth = evalPixelDepth(level); - auto rowSize = evalRowSize(level); - return pixDepth * pixHeight * rowSize; -} -size_t Header::evalImageSize(uint32_t level) const { - auto faceSize = evalFaceSize(level); - if (numberOfFaces == NUM_CUBEMAPFACES && numberOfArrayElements == 0) { - return faceSize; - } else { - return (getNumberOfSlices() * numberOfFaces * faceSize); - } -} - - -KeyValue::KeyValue(const std::string& key, uint32_t valueByteSize, const Byte* value) : - _byteSize((uint32_t) key.size() + 1 + valueByteSize), // keyString size + '\0' ending char + the value size - _key(key), - _value(valueByteSize) -{ - if (_value.size() && value) { - memcpy(_value.data(), value, valueByteSize); - } -} - -KeyValue::KeyValue(const std::string& key, const std::string& value) : - KeyValue(key, (uint32_t) value.size(), (const Byte*) value.data()) -{ - -} - -uint32_t KeyValue::serializedByteSize() const { - return (uint32_t) (sizeof(uint32_t) + _byteSize + Header::evalPadding(_byteSize)); -} - -uint32_t KeyValue::serializedKeyValuesByteSize(const KeyValues& keyValues) { - uint32_t keyValuesSize = 0; - for (auto& keyval : keyValues) { - keyValuesSize += keyval.serializedByteSize(); - } - return (keyValuesSize + Header::evalPadding(keyValuesSize)); -} - - -KTX::KTX() { -} - -KTX::~KTX() { -} - -void KTX::resetStorage(const StoragePointer& storage) { - _storage = storage; -} - -const Header* KTX::getHeader() const { - if (!_storage) { - return nullptr; - } - return reinterpret_cast(_storage->data()); -} - - -size_t KTX::getKeyValueDataSize() const { - if (_storage) { - return getHeader()->bytesOfKeyValueData; - } else { - return 0; - } -} - -size_t KTX::getTexelsDataSize() const { - if (_storage) { - //return _storage->size() - (sizeof(Header) + getKeyValueDataSize()); - return (_storage->data() + _storage->size()) - getTexelsData(); - } else { - return 0; - } -} - -const Byte* KTX::getKeyValueData() const { - if (_storage) { - return (_storage->data() + sizeof(Header)); - } else { - return nullptr; - } -} - -const Byte* KTX::getTexelsData() const { - if (_storage) { - return (_storage->data() + sizeof(Header) + getKeyValueDataSize()); - } else { - return nullptr; - } -} - -storage::StoragePointer KTX::getMipFaceTexelsData(uint16_t mip, uint8_t face) const { - storage::StoragePointer result; - if (mip < _images.size()) { - const auto& faces = _images[mip]; - if (face < faces._numFaces) { - auto faceOffset = faces._faceBytes[face] - _storage->data(); - auto faceSize = faces._faceSize; - result = _storage->createView(faceSize, faceOffset); - } - } - return result; -} diff --git a/libraries/ktx/src/ktx/KTX.h b/libraries/ktx/src/ktx/KTX.h deleted file mode 100644 index 8e901b1105..0000000000 --- a/libraries/ktx/src/ktx/KTX.h +++ /dev/null @@ -1,494 +0,0 @@ -// -// KTX.h -// ktx/src/ktx -// -// Created by Zach Pomerantz on 2/08/2017. -// Copyright 2017 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// -#pragma once -#ifndef hifi_ktx_KTX_h -#define hifi_ktx_KTX_h - -#include -#include -#include -#include -#include -#include -#include - -#include - -/* KTX Spec: - -Byte[12] identifier -UInt32 endianness -UInt32 glType -UInt32 glTypeSize -UInt32 glFormat -Uint32 glInternalFormat -Uint32 glBaseInternalFormat -UInt32 pixelWidth -UInt32 pixelHeight -UInt32 pixelDepth -UInt32 numberOfArrayElements -UInt32 numberOfFaces -UInt32 numberOfMipmapLevels -UInt32 bytesOfKeyValueData - -for each keyValuePair that fits in bytesOfKeyValueData - UInt32 keyAndValueByteSize - Byte keyAndValue[keyAndValueByteSize] - Byte valuePadding[3 - ((keyAndValueByteSize + 3) % 4)] -end - -for each mipmap_level in numberOfMipmapLevels* - UInt32 imageSize; - for each array_element in numberOfArrayElements* - for each face in numberOfFaces - for each z_slice in pixelDepth* - for each row or row_of_blocks in pixelHeight* - for each pixel or block_of_pixels in pixelWidth - Byte data[format-specific-number-of-bytes]** - end - end - end - Byte cubePadding[0-3] - end - end - Byte mipPadding[3 - ((imageSize + 3) % 4)] -end - -* Replace with 1 if this field is 0. - -** Uncompressed texture data matches a GL_UNPACK_ALIGNMENT of 4. -*/ - - - -namespace ktx { - const uint32_t PACKING_SIZE { sizeof(uint32_t) }; - using Byte = uint8_t; - - enum class GLType : uint32_t { - COMPRESSED_TYPE = 0, - - // GL 4.4 Table 8.2 - UNSIGNED_BYTE = 0x1401, - BYTE = 0x1400, - UNSIGNED_SHORT = 0x1403, - SHORT = 0x1402, - UNSIGNED_INT = 0x1405, - INT = 0x1404, - HALF_FLOAT = 0x140B, - FLOAT = 0x1406, - UNSIGNED_BYTE_3_3_2 = 0x8032, - UNSIGNED_BYTE_2_3_3_REV = 0x8362, - UNSIGNED_SHORT_5_6_5 = 0x8363, - UNSIGNED_SHORT_5_6_5_REV = 0x8364, - UNSIGNED_SHORT_4_4_4_4 = 0x8033, - UNSIGNED_SHORT_4_4_4_4_REV = 0x8365, - UNSIGNED_SHORT_5_5_5_1 = 0x8034, - UNSIGNED_SHORT_1_5_5_5_REV = 0x8366, - UNSIGNED_INT_8_8_8_8 = 0x8035, - UNSIGNED_INT_8_8_8_8_REV = 0x8367, - UNSIGNED_INT_10_10_10_2 = 0x8036, - UNSIGNED_INT_2_10_10_10_REV = 0x8368, - UNSIGNED_INT_24_8 = 0x84FA, - UNSIGNED_INT_10F_11F_11F_REV = 0x8C3B, - UNSIGNED_INT_5_9_9_9_REV = 0x8C3E, - FLOAT_32_UNSIGNED_INT_24_8_REV = 0x8DAD, - - NUM_GLTYPES = 25, - }; - - enum class GLFormat : uint32_t { - COMPRESSED_FORMAT = 0, - - // GL 4.4 Table 8.3 - STENCIL_INDEX = 0x1901, - DEPTH_COMPONENT = 0x1902, - DEPTH_STENCIL = 0x84F9, - - RED = 0x1903, - GREEN = 0x1904, - BLUE = 0x1905, - RG = 0x8227, - RGB = 0x1907, - RGBA = 0x1908, - BGR = 0x80E0, - BGRA = 0x80E1, - - RG_INTEGER = 0x8228, - RED_INTEGER = 0x8D94, - GREEN_INTEGER = 0x8D95, - BLUE_INTEGER = 0x8D96, - RGB_INTEGER = 0x8D98, - RGBA_INTEGER = 0x8D99, - BGR_INTEGER = 0x8D9A, - BGRA_INTEGER = 0x8D9B, - - NUM_GLFORMATS = 20, - }; - - enum class GLInternalFormat_Uncompressed : uint32_t { - // GL 4.4 Table 8.12 - R8 = 0x8229, - R8_SNORM = 0x8F94, - - R16 = 0x822A, - R16_SNORM = 0x8F98, - - RG8 = 0x822B, - RG8_SNORM = 0x8F95, - - RG16 = 0x822C, - RG16_SNORM = 0x8F99, - - R3_G3_B2 = 0x2A10, - RGB4 = 0x804F, - RGB5 = 0x8050, - RGB565 = 0x8D62, - - RGB8 = 0x8051, - RGB8_SNORM = 0x8F96, - RGB10 = 0x8052, - RGB12 = 0x8053, - - RGB16 = 0x8054, - RGB16_SNORM = 0x8F9A, - - RGBA2 = 0x8055, - RGBA4 = 0x8056, - RGB5_A1 = 0x8057, - RGBA8 = 0x8058, - RGBA8_SNORM = 0x8F97, - - RGB10_A2 = 0x8059, - RGB10_A2UI = 0x906F, - - RGBA12 = 0x805A, - RGBA16 = 0x805B, - RGBA16_SNORM = 0x8F9B, - - SRGB8 = 0x8C41, - SRGB8_ALPHA8 = 0x8C43, - - R16F = 0x822D, - RG16F = 0x822F, - RGB16F = 0x881B, - RGBA16F = 0x881A, - - R32F = 0x822E, - RG32F = 0x8230, - RGB32F = 0x8815, - RGBA32F = 0x8814, - - R11F_G11F_B10F = 0x8C3A, - RGB9_E5 = 0x8C3D, - - - R8I = 0x8231, - R8UI = 0x8232, - R16I = 0x8233, - R16UI = 0x8234, - R32I = 0x8235, - R32UI = 0x8236, - RG8I = 0x8237, - RG8UI = 0x8238, - RG16I = 0x8239, - RG16UI = 0x823A, - RG32I = 0x823B, - RG32UI = 0x823C, - - RGB8I = 0x8D8F, - RGB8UI = 0x8D7D, - RGB16I = 0x8D89, - RGB16UI = 0x8D77, - - RGB32I = 0x8D83, - RGB32UI = 0x8D71, - RGBA8I = 0x8D8E, - RGBA8UI = 0x8D7C, - RGBA16I = 0x8D88, - RGBA16UI = 0x8D76, - RGBA32I = 0x8D82, - - RGBA32UI = 0x8D70, - - // GL 4.4 Table 8.13 - DEPTH_COMPONENT16 = 0x81A5, - DEPTH_COMPONENT24 = 0x81A6, - DEPTH_COMPONENT32 = 0x81A7, - - DEPTH_COMPONENT32F = 0x8CAC, - DEPTH24_STENCIL8 = 0x88F0, - DEPTH32F_STENCIL8 = 0x8CAD, - - STENCIL_INDEX1 = 0x8D46, - STENCIL_INDEX4 = 0x8D47, - STENCIL_INDEX8 = 0x8D48, - STENCIL_INDEX16 = 0x8D49, - - NUM_UNCOMPRESSED_GLINTERNALFORMATS = 74, - }; - - enum class GLInternalFormat_Compressed : uint32_t { - // GL 4.4 Table 8.14 - COMPRESSED_RED = 0x8225, - COMPRESSED_RG = 0x8226, - COMPRESSED_RGB = 0x84ED, - COMPRESSED_RGBA = 0x84EE, - - COMPRESSED_SRGB = 0x8C48, - COMPRESSED_SRGB_ALPHA = 0x8C49, - - COMPRESSED_RED_RGTC1 = 0x8DBB, - COMPRESSED_SIGNED_RED_RGTC1 = 0x8DBC, - COMPRESSED_RG_RGTC2 = 0x8DBD, - COMPRESSED_SIGNED_RG_RGTC2 = 0x8DBE, - - COMPRESSED_RGBA_BPTC_UNORM = 0x8E8C, - COMPRESSED_SRGB_ALPHA_BPTC_UNORM = 0x8E8D, - COMPRESSED_RGB_BPTC_SIGNED_FLOAT = 0x8E8E, - COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT = 0x8E8F, - - COMPRESSED_RGB8_ETC2 = 0x9274, - COMPRESSED_SRGB8_ETC2 = 0x9275, - COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2 = 0x9276, - COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2 = 0x9277, - COMPRESSED_RGBA8_ETC2_EAC = 0x9278, - COMPRESSED_SRGB8_ALPHA8_ETC2_EAC = 0x9279, - - COMPRESSED_R11_EAC = 0x9270, - COMPRESSED_SIGNED_R11_EAC = 0x9271, - COMPRESSED_RG11_EAC = 0x9272, - COMPRESSED_SIGNED_RG11_EAC = 0x9273, - - NUM_COMPRESSED_GLINTERNALFORMATS = 24, - }; - - enum class GLBaseInternalFormat : uint32_t { - // GL 4.4 Table 8.11 - DEPTH_COMPONENT = 0x1902, - DEPTH_STENCIL = 0x84F9, - RED = 0x1903, - RG = 0x8227, - RGB = 0x1907, - RGBA = 0x1908, - STENCIL_INDEX = 0x1901, - - NUM_GLBASEINTERNALFORMATS = 7, - }; - - enum CubeMapFace { - POS_X = 0, - NEG_X = 1, - POS_Y = 2, - NEG_Y = 3, - POS_Z = 4, - NEG_Z = 5, - NUM_CUBEMAPFACES = 6, - }; - - using Storage = storage::Storage; - using StoragePointer = std::shared_ptr; - - // Header - struct Header { - static const size_t IDENTIFIER_LENGTH = 12; - using Identifier = std::array; - static const Identifier IDENTIFIER; - - static const uint32_t ENDIAN_TEST = 0x04030201; - static const uint32_t REVERSE_ENDIAN_TEST = 0x01020304; - - static uint32_t evalPadding(size_t byteSize); - - Header(); - - Byte identifier[IDENTIFIER_LENGTH]; - uint32_t endianness { ENDIAN_TEST }; - - uint32_t glType; - uint32_t glTypeSize { 0 }; - uint32_t glFormat; - uint32_t glInternalFormat; - uint32_t glBaseInternalFormat; - - uint32_t pixelWidth { 1 }; - uint32_t pixelHeight { 0 }; - uint32_t pixelDepth { 0 }; - uint32_t numberOfArrayElements { 0 }; - uint32_t numberOfFaces { 1 }; - uint32_t numberOfMipmapLevels { 1 }; - - uint32_t bytesOfKeyValueData { 0 }; - - uint32_t getPixelWidth() const { return (pixelWidth ? pixelWidth : 1); } - uint32_t getPixelHeight() const { return (pixelHeight ? pixelHeight : 1); } - uint32_t getPixelDepth() const { return (pixelDepth ? pixelDepth : 1); } - uint32_t getNumberOfSlices() const { return (numberOfArrayElements ? numberOfArrayElements : 1); } - uint32_t getNumberOfLevels() const { return (numberOfMipmapLevels ? numberOfMipmapLevels : 1); } - - uint32_t evalMaxDimension() const; - uint32_t evalPixelWidth(uint32_t level) const; - uint32_t evalPixelHeight(uint32_t level) const; - uint32_t evalPixelDepth(uint32_t level) const; - - size_t evalPixelSize() const; - size_t evalRowSize(uint32_t level) const; - size_t evalFaceSize(uint32_t level) const; - size_t evalImageSize(uint32_t level) const; - - void setUncompressed(GLType type, uint32_t typeSize, GLFormat format, GLInternalFormat_Uncompressed internalFormat, GLBaseInternalFormat baseInternalFormat) { - glType = (uint32_t) type; - glTypeSize = typeSize; - glFormat = (uint32_t) format; - glInternalFormat = (uint32_t) internalFormat; - glBaseInternalFormat = (uint32_t) baseInternalFormat; - } - void setCompressed(GLInternalFormat_Compressed internalFormat, GLBaseInternalFormat baseInternalFormat) { - glType = (uint32_t) GLType::COMPRESSED_TYPE; - glTypeSize = 1; - glFormat = (uint32_t) GLFormat::COMPRESSED_FORMAT; - glInternalFormat = (uint32_t) internalFormat; - glBaseInternalFormat = (uint32_t) baseInternalFormat; - } - - GLType getGLType() const { return (GLType)glType; } - uint32_t getTypeSize() const { return glTypeSize; } - GLFormat getGLFormat() const { return (GLFormat)glFormat; } - GLInternalFormat_Uncompressed getGLInternaFormat_Uncompressed() const { return (GLInternalFormat_Uncompressed)glInternalFormat; } - GLInternalFormat_Compressed getGLInternaFormat_Compressed() const { return (GLInternalFormat_Compressed)glInternalFormat; } - GLBaseInternalFormat getGLBaseInternalFormat() const { return (GLBaseInternalFormat)glBaseInternalFormat; } - - - void setDimensions(uint32_t width, uint32_t height = 0, uint32_t depth = 0, uint32_t numSlices = 0, uint32_t numFaces = 1) { - pixelWidth = (width > 0 ? width : 1); - pixelHeight = height; - pixelDepth = depth; - numberOfArrayElements = numSlices; - numberOfFaces = ((numFaces == 1) || (numFaces == NUM_CUBEMAPFACES) ? numFaces : 1); - } - void set1D(uint32_t width) { setDimensions(width); } - void set1DArray(uint32_t width, uint32_t numSlices) { setDimensions(width, 0, 0, (numSlices > 0 ? numSlices : 1)); } - void set2D(uint32_t width, uint32_t height) { setDimensions(width, height); } - void set2DArray(uint32_t width, uint32_t height, uint32_t numSlices) { setDimensions(width, height, 0, (numSlices > 0 ? numSlices : 1)); } - void set3D(uint32_t width, uint32_t height, uint32_t depth) { setDimensions(width, height, depth); } - void set3DArray(uint32_t width, uint32_t height, uint32_t depth, uint32_t numSlices) { setDimensions(width, height, depth, (numSlices > 0 ? numSlices : 1)); } - void setCube(uint32_t width, uint32_t height) { setDimensions(width, height, 0, 0, NUM_CUBEMAPFACES); } - void setCubeArray(uint32_t width, uint32_t height, uint32_t numSlices) { setDimensions(width, height, 0, (numSlices > 0 ? numSlices : 1), NUM_CUBEMAPFACES); } - - }; - - // Key Values - struct KeyValue { - uint32_t _byteSize { 0 }; - std::string _key; - std::vector _value; - - - KeyValue(const std::string& key, uint32_t valueByteSize, const Byte* value); - - KeyValue(const std::string& key, const std::string& value); - - uint32_t serializedByteSize() const; - - static KeyValue parseSerializedKeyAndValue(uint32_t srcSize, const Byte* srcBytes); - static uint32_t writeSerializedKeyAndValue(Byte* destBytes, uint32_t destByteSize, const KeyValue& keyval); - - using KeyValues = std::list; - static uint32_t serializedKeyValuesByteSize(const KeyValues& keyValues); - - }; - using KeyValues = KeyValue::KeyValues; - - - struct Image { - using FaceBytes = std::vector; - - uint32_t _numFaces{ 1 }; - uint32_t _imageSize; - uint32_t _faceSize; - uint32_t _padding; - FaceBytes _faceBytes; - - - Image(uint32_t imageSize, uint32_t padding, const Byte* bytes) : - _numFaces(1), - _imageSize(imageSize), - _faceSize(imageSize), - _padding(padding), - _faceBytes(1, bytes) {} - - Image(uint32_t pageSize, uint32_t padding, const FaceBytes& cubeFaceBytes) : - _numFaces(NUM_CUBEMAPFACES), - _imageSize(pageSize * NUM_CUBEMAPFACES), - _faceSize(pageSize), - _padding(padding) - { - if (cubeFaceBytes.size() == NUM_CUBEMAPFACES) { - _faceBytes = cubeFaceBytes; - } - } - }; - using Images = std::vector; - - class KTX { - void resetStorage(const StoragePointer& src); - - KTX(); - public: - - ~KTX(); - - // Define a KTX object manually to write it somewhere (in a file on disk?) - // This path allocate the Storage where to store header, keyvalues and copy mips - // Then COPY all the data - static std::unique_ptr create(const Header& header, const Images& images, const KeyValues& keyValues = KeyValues()); - - // Instead of creating a full Copy of the src data in a KTX object, the write serialization can be performed with the - // following two functions - // size_t sizeNeeded = KTX::evalStorageSize(header, images); - // - // //allocate a buffer of size "sizeNeeded" or map a file with enough capacity - // Byte* destBytes = new Byte[sizeNeeded]; - // - // // THen perform the writing of the src data to the destinnation buffer - // write(destBytes, sizeNeeded, header, images); - // - // This is exactly what is done in the create function - static size_t evalStorageSize(const Header& header, const Images& images, const KeyValues& keyValues = KeyValues()); - static size_t write(Byte* destBytes, size_t destByteSize, const Header& header, const Images& images, const KeyValues& keyValues = KeyValues()); - static size_t writeKeyValues(Byte* destBytes, size_t destByteSize, const KeyValues& keyValues); - static Images writeImages(Byte* destBytes, size_t destByteSize, const Images& images); - - // Parse a block of memory and create a KTX object from it - static std::unique_ptr create(const StoragePointer& src); - - static bool checkHeaderFromStorage(size_t srcSize, const Byte* srcBytes); - static KeyValues parseKeyValues(size_t srcSize, const Byte* srcBytes); - static Images parseImages(const Header& header, size_t srcSize, const Byte* srcBytes); - - // Access raw pointers to the main sections of the KTX - const Header* getHeader() const; - const Byte* getKeyValueData() const; - const Byte* getTexelsData() const; - storage::StoragePointer getMipFaceTexelsData(uint16_t mip = 0, uint8_t face = 0) const; - const StoragePointer& getStorage() const { return _storage; } - - size_t getKeyValueDataSize() const; - size_t getTexelsDataSize() const; - - StoragePointer _storage; - KeyValues _keyValues; - Images _images; - }; - -} - -#endif // hifi_ktx_KTX_h diff --git a/libraries/ktx/src/ktx/Reader.cpp b/libraries/ktx/src/ktx/Reader.cpp deleted file mode 100644 index 277ce42e69..0000000000 --- a/libraries/ktx/src/ktx/Reader.cpp +++ /dev/null @@ -1,195 +0,0 @@ -// -// Reader.cpp -// ktx/src/ktx -// -// Created by Zach Pomerantz on 2/08/2017. -// Copyright 2017 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// -#include "KTX.h" - -#include -#include -#include - -#ifndef _MSC_VER -#define NOEXCEPT noexcept -#else -#define NOEXCEPT -#endif - -namespace ktx { - class ReaderException: public std::exception { - public: - ReaderException(const std::string& explanation) : _explanation("KTX deserialization error: " + explanation) {} - const char* what() const NOEXCEPT override { return _explanation.c_str(); } - private: - const std::string _explanation; - }; - - bool checkEndianness(uint32_t endianness, bool& matching) { - switch (endianness) { - case Header::ENDIAN_TEST: { - matching = true; - return true; - } - break; - case Header::REVERSE_ENDIAN_TEST: - { - matching = false; - return true; - } - break; - default: - throw ReaderException("endianness field has invalid value"); - return false; - } - } - - bool checkIdentifier(const Byte* identifier) { - if (!(0 == memcmp(identifier, Header::IDENTIFIER.data(), Header::IDENTIFIER_LENGTH))) { - throw ReaderException("identifier field invalid"); - return false; - } - return true; - } - - bool KTX::checkHeaderFromStorage(size_t srcSize, const Byte* srcBytes) { - try { - // validation - if (srcSize < sizeof(Header)) { - throw ReaderException("length is too short for header"); - } - const Header* header = reinterpret_cast(srcBytes); - - checkIdentifier(header->identifier); - - bool endianMatch { true }; - checkEndianness(header->endianness, endianMatch); - - // TODO: endian conversion if !endianMatch - for now, this is for local use and is unnecessary - - - // TODO: calculated bytesOfTexData - if (srcSize < (sizeof(Header) + header->bytesOfKeyValueData)) { - throw ReaderException("length is too short for metadata"); - } - - size_t bytesOfTexData = 0; - if (srcSize < (sizeof(Header) + header->bytesOfKeyValueData + bytesOfTexData)) { - - throw ReaderException("length is too short for data"); - } - - return true; - } - catch (const ReaderException& e) { - qWarning() << e.what(); - return false; - } - } - - KeyValue KeyValue::parseSerializedKeyAndValue(uint32_t srcSize, const Byte* srcBytes) { - uint32_t keyAndValueByteSize; - memcpy(&keyAndValueByteSize, srcBytes, sizeof(uint32_t)); - if (keyAndValueByteSize + sizeof(uint32_t) > srcSize) { - throw ReaderException("invalid key-value size"); - } - auto keyValueBytes = srcBytes + sizeof(uint32_t); - - // find the first null character \0 and extract the key - uint32_t keyLength = 0; - while (reinterpret_cast(keyValueBytes)[++keyLength] != '\0') { - if (keyLength == keyAndValueByteSize) { - // key must be null-terminated, and there must be space for the value - throw ReaderException("invalid key-value " + std::string(reinterpret_cast(keyValueBytes), keyLength)); - } - } - uint32_t valueStartOffset = keyLength + 1; - - // parse the key-value - return KeyValue(std::string(reinterpret_cast(keyValueBytes), keyLength), - keyAndValueByteSize - valueStartOffset, keyValueBytes + valueStartOffset); - } - - KeyValues KTX::parseKeyValues(size_t srcSize, const Byte* srcBytes) { - KeyValues keyValues; - try { - auto src = srcBytes; - uint32_t length = (uint32_t) srcSize; - uint32_t offset = 0; - while (offset < length) { - auto keyValue = KeyValue::parseSerializedKeyAndValue(length - offset, src); - keyValues.emplace_back(keyValue); - - // advance offset/src - offset += keyValue.serializedByteSize(); - src += keyValue.serializedByteSize(); - } - } - catch (const ReaderException& e) { - qWarning() << e.what(); - } - return keyValues; - } - - Images KTX::parseImages(const Header& header, size_t srcSize, const Byte* srcBytes) { - Images images; - auto currentPtr = srcBytes; - auto numFaces = header.numberOfFaces; - - // Keep identifying new mip as long as we can at list query the next imageSize - while ((currentPtr - srcBytes) + sizeof(uint32_t) <= (srcSize)) { - - // Grab the imageSize coming up - size_t imageSize = *reinterpret_cast(currentPtr); - currentPtr += sizeof(uint32_t); - - // If enough data ahead then capture the pointer - if ((currentPtr - srcBytes) + imageSize <= (srcSize)) { - auto padding = Header::evalPadding(imageSize); - - if (numFaces == NUM_CUBEMAPFACES) { - size_t faceSize = imageSize / NUM_CUBEMAPFACES; - Image::FaceBytes faces(NUM_CUBEMAPFACES); - for (uint32_t face = 0; face < NUM_CUBEMAPFACES; face++) { - faces[face] = currentPtr; - currentPtr += faceSize; - } - images.emplace_back(Image((uint32_t) faceSize, padding, faces)); - currentPtr += padding; - } else { - images.emplace_back(Image((uint32_t) imageSize, padding, currentPtr)); - currentPtr += imageSize + padding; - } - } else { - break; - } - } - - return images; - } - - std::unique_ptr KTX::create(const StoragePointer& src) { - if (!src) { - return nullptr; - } - - if (!checkHeaderFromStorage(src->size(), src->data())) { - return nullptr; - } - - std::unique_ptr result(new KTX()); - result->resetStorage(src); - - // read metadata - result->_keyValues = parseKeyValues(result->getHeader()->bytesOfKeyValueData, result->getKeyValueData()); - - // populate image table - result->_images = parseImages(*result->getHeader(), result->getTexelsDataSize(), result->getTexelsData()); - - return result; - } -} diff --git a/libraries/ktx/src/ktx/Writer.cpp b/libraries/ktx/src/ktx/Writer.cpp deleted file mode 100644 index 25b363d31b..0000000000 --- a/libraries/ktx/src/ktx/Writer.cpp +++ /dev/null @@ -1,171 +0,0 @@ -// -// Writer.cpp -// ktx/src/ktx -// -// Created by Zach Pomerantz on 2/08/2017. -// Copyright 2017 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// -#include "KTX.h" - - -#include -#include -#ifndef _MSC_VER -#define NOEXCEPT noexcept -#else -#define NOEXCEPT -#endif - -namespace ktx { - - class WriterException : public std::exception { - public: - WriterException(const std::string& explanation) : _explanation("KTX serialization error: " + explanation) {} - const char* what() const NOEXCEPT override { return _explanation.c_str(); } - private: - const std::string _explanation; - }; - - std::unique_ptr KTX::create(const Header& header, const Images& images, const KeyValues& keyValues) { - StoragePointer storagePointer; - { - auto storageSize = ktx::KTX::evalStorageSize(header, images, keyValues); - auto memoryStorage = new storage::MemoryStorage(storageSize); - ktx::KTX::write(memoryStorage->data(), memoryStorage->size(), header, images, keyValues); - storagePointer.reset(memoryStorage); - } - return create(storagePointer); - } - - size_t KTX::evalStorageSize(const Header& header, const Images& images, const KeyValues& keyValues) { - size_t storageSize = sizeof(Header); - - if (!keyValues.empty()) { - size_t keyValuesSize = KeyValue::serializedKeyValuesByteSize(keyValues); - storageSize += keyValuesSize; - } - - auto numMips = header.getNumberOfLevels(); - for (uint32_t l = 0; l < numMips; l++) { - if (images.size() > l) { - storageSize += sizeof(uint32_t); - storageSize += images[l]._imageSize; - storageSize += Header::evalPadding(images[l]._imageSize); - } - } - return storageSize; - } - - size_t KTX::write(Byte* destBytes, size_t destByteSize, const Header& header, const Images& srcImages, const KeyValues& keyValues) { - // Check again that we have enough destination capacity - if (!destBytes || (destByteSize < evalStorageSize(header, srcImages, keyValues))) { - return 0; - } - - auto currentDestPtr = destBytes; - // Header - auto destHeader = reinterpret_cast(currentDestPtr); - memcpy(currentDestPtr, &header, sizeof(Header)); - currentDestPtr += sizeof(Header); - - // KeyValues - if (!keyValues.empty()) { - destHeader->bytesOfKeyValueData = (uint32_t) writeKeyValues(currentDestPtr, destByteSize - sizeof(Header), keyValues); - } else { - // Make sure the header contains the right bytesOfKeyValueData size - destHeader->bytesOfKeyValueData = 0; - } - currentDestPtr += destHeader->bytesOfKeyValueData; - - // Images - auto destImages = writeImages(currentDestPtr, destByteSize - sizeof(Header) - destHeader->bytesOfKeyValueData, srcImages); - // We chould check here that the amoutn of dest IMages generated is the same as the source - - return destByteSize; - } - - uint32_t KeyValue::writeSerializedKeyAndValue(Byte* destBytes, uint32_t destByteSize, const KeyValue& keyval) { - uint32_t keyvalSize = keyval.serializedByteSize(); - if (keyvalSize > destByteSize) { - throw WriterException("invalid key-value size"); - } - - *((uint32_t*) destBytes) = keyval._byteSize; - - auto dest = destBytes + sizeof(uint32_t); - - auto keySize = keyval._key.size() + 1; // Add 1 for the '\0' character at the end of the string - memcpy(dest, keyval._key.data(), keySize); - dest += keySize; - - memcpy(dest, keyval._value.data(), keyval._value.size()); - - return keyvalSize; - } - - size_t KTX::writeKeyValues(Byte* destBytes, size_t destByteSize, const KeyValues& keyValues) { - size_t writtenByteSize = 0; - try { - auto dest = destBytes; - for (auto& keyval : keyValues) { - size_t keyvalSize = KeyValue::writeSerializedKeyAndValue(dest, (uint32_t) (destByteSize - writtenByteSize), keyval); - writtenByteSize += keyvalSize; - dest += keyvalSize; - } - } - catch (const WriterException& e) { - qWarning() << e.what(); - } - return writtenByteSize; - } - - Images KTX::writeImages(Byte* destBytes, size_t destByteSize, const Images& srcImages) { - Images destImages; - auto imagesDataPtr = destBytes; - if (!imagesDataPtr) { - return destImages; - } - auto allocatedImagesDataSize = destByteSize; - size_t currentDataSize = 0; - auto currentPtr = imagesDataPtr; - - for (uint32_t l = 0; l < srcImages.size(); l++) { - if (currentDataSize + sizeof(uint32_t) < allocatedImagesDataSize) { - size_t imageSize = srcImages[l]._imageSize; - *(reinterpret_cast (currentPtr)) = (uint32_t) imageSize; - currentPtr += sizeof(uint32_t); - currentDataSize += sizeof(uint32_t); - - // If enough data ahead then capture the copy source pointer - if (currentDataSize + imageSize <= (allocatedImagesDataSize)) { - auto padding = Header::evalPadding(imageSize); - - // Single face vs cubes - if (srcImages[l]._numFaces == 1) { - memcpy(currentPtr, srcImages[l]._faceBytes[0], imageSize); - destImages.emplace_back(Image((uint32_t) imageSize, padding, currentPtr)); - currentPtr += imageSize; - } else { - Image::FaceBytes faceBytes(NUM_CUBEMAPFACES); - auto faceSize = srcImages[l]._faceSize; - for (int face = 0; face < NUM_CUBEMAPFACES; face++) { - memcpy(currentPtr, srcImages[l]._faceBytes[face], faceSize); - faceBytes[face] = currentPtr; - currentPtr += faceSize; - } - destImages.emplace_back(Image(faceSize, padding, faceBytes)); - } - - currentPtr += padding; - currentDataSize += imageSize + padding; - } - } - } - - return destImages; - } - -} diff --git a/libraries/model-networking/CMakeLists.txt b/libraries/model-networking/CMakeLists.txt index 00aa17ff57..ed8cd7b5f9 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) +link_hifi_libraries(shared networking model fbx) diff --git a/libraries/model-networking/src/model-networking/KTXCache.cpp b/libraries/model-networking/src/model-networking/KTXCache.cpp deleted file mode 100644 index 63d35fe4a4..0000000000 --- a/libraries/model-networking/src/model-networking/KTXCache.cpp +++ /dev/null @@ -1,47 +0,0 @@ -// -// KTXCache.cpp -// libraries/model-networking/src -// -// Created by Zach Pomerantz on 2/22/2017. -// Copyright 2017 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#include "KTXCache.h" - -#include - -using File = cache::File; -using FilePointer = cache::FilePointer; - -KTXCache::KTXCache(const std::string& dir, const std::string& ext) : - FileCache(dir, ext) { - initialize(); -} - -KTXFilePointer KTXCache::writeFile(const char* data, Metadata&& metadata) { - FilePointer file = FileCache::writeFile(data, std::move(metadata)); - 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)); -} - -KTXFile::KTXFile(Metadata&& metadata, const std::string& filepath) : - cache::File(std::move(metadata), filepath) {} - -std::unique_ptr KTXFile::getKTX() const { - ktx::StoragePointer storage = std::make_shared(getFilepath().c_str()); - if (*storage) { - return ktx::KTX::create(storage); - } - return {}; -} diff --git a/libraries/model-networking/src/model-networking/KTXCache.h b/libraries/model-networking/src/model-networking/KTXCache.h deleted file mode 100644 index 4ef5e52721..0000000000 --- a/libraries/model-networking/src/model-networking/KTXCache.h +++ /dev/null @@ -1,51 +0,0 @@ -// -// KTXCache.h -// libraries/model-networking/src -// -// Created by Zach Pomerantz 2/22/2017. -// Copyright 2017 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#ifndef hifi_KTXCache_h -#define hifi_KTXCache_h - -#include - -#include - -namespace ktx { - class KTX; -} - -class KTXFile; -using KTXFilePointer = std::shared_ptr; - -class KTXCache : public cache::FileCache { - Q_OBJECT - -public: - KTXCache(const std::string& dir, const std::string& ext); - - KTXFilePointer writeFile(const char* data, Metadata&& metadata); - KTXFilePointer getFile(const Key& key); - -protected: - std::unique_ptr createFile(Metadata&& metadata, const std::string& filepath) override final; -}; - -class KTXFile : public cache::File { - Q_OBJECT - -public: - std::unique_ptr getKTX() const; - -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 5dfaddd471..8a4e85cfe6 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -18,37 +18,27 @@ #include #include #include - -#if DEBUG_DUMP_TEXTURE_LOADS #include #include -#endif #include #include #include -#include - #include #include #include +#include #include "ModelNetworkingLogging.h" #include #include Q_LOGGING_CATEGORY(trace_resource_parse_image, "trace.resource.parse.image") -Q_LOGGING_CATEGORY(trace_resource_parse_image_raw, "trace.resource.parse.image.raw") -Q_LOGGING_CATEGORY(trace_resource_parse_image_ktx, "trace.resource.parse.image.ktx") -const std::string TextureCache::KTX_DIRNAME { "ktx_cache" }; -const std::string TextureCache::KTX_EXT { "ktx" }; - -TextureCache::TextureCache() : - _ktxCache(KTX_DIRNAME, KTX_EXT) { +TextureCache::TextureCache() { setUnusedResourceCacheSize(0); setObjectName("TextureCache"); @@ -71,7 +61,7 @@ TextureCache::~TextureCache() { // this list taken from Ken Perlin's Improved Noise reference implementation (orig. in Java) at // http://mrl.nyu.edu/~perlin/noise/ -const int permutation[256] = +const int permutation[256] = { 151, 160, 137, 91, 90, 15, 131, 13, 201, 95, 96, 53, 194, 233, 7, 225, 140, 36, 103, 30, 69, 142, 8, 99, 37, 240, 21, 10, 23, 190, 6, 148, @@ -118,8 +108,7 @@ const gpu::TexturePointer& TextureCache::getPermutationNormalTexture() { } _permutationNormalTexture = gpu::TexturePointer(gpu::Texture::create2D(gpu::Element(gpu::VEC3, gpu::NUINT8, gpu::RGB), 256, 2)); - _permutationNormalTexture->setStoredMipFormat(_permutationNormalTexture->getTexelFormat()); - _permutationNormalTexture->assignStoredMip(0, sizeof(data), data); + _permutationNormalTexture->assignStoredMip(0, _blueTexture->getTexelFormat(), sizeof(data), data); } return _permutationNormalTexture; } @@ -131,40 +120,36 @@ const unsigned char OPAQUE_BLACK[] = { 0x00, 0x00, 0x00, 0xFF }; const gpu::TexturePointer& TextureCache::getWhiteTexture() { if (!_whiteTexture) { - _whiteTexture = gpu::TexturePointer(gpu::Texture::createStrict(gpu::Element::COLOR_RGBA_32, 1, 1)); + _whiteTexture = gpu::TexturePointer(gpu::Texture::create2D(gpu::Element::COLOR_RGBA_32, 1, 1)); _whiteTexture->setSource("TextureCache::_whiteTexture"); - _whiteTexture->setStoredMipFormat(_whiteTexture->getTexelFormat()); - _whiteTexture->assignStoredMip(0, sizeof(OPAQUE_WHITE), OPAQUE_WHITE); + _whiteTexture->assignStoredMip(0, _whiteTexture->getTexelFormat(), sizeof(OPAQUE_WHITE), OPAQUE_WHITE); } return _whiteTexture; } const gpu::TexturePointer& TextureCache::getGrayTexture() { if (!_grayTexture) { - _grayTexture = gpu::TexturePointer(gpu::Texture::createStrict(gpu::Element::COLOR_RGBA_32, 1, 1)); + _grayTexture = gpu::TexturePointer(gpu::Texture::create2D(gpu::Element::COLOR_RGBA_32, 1, 1)); _grayTexture->setSource("TextureCache::_grayTexture"); - _grayTexture->setStoredMipFormat(_grayTexture->getTexelFormat()); - _grayTexture->assignStoredMip(0, sizeof(OPAQUE_GRAY), OPAQUE_GRAY); + _grayTexture->assignStoredMip(0, _grayTexture->getTexelFormat(), sizeof(OPAQUE_GRAY), OPAQUE_GRAY); } return _grayTexture; } const gpu::TexturePointer& TextureCache::getBlueTexture() { if (!_blueTexture) { - _blueTexture = gpu::TexturePointer(gpu::Texture::createStrict(gpu::Element::COLOR_RGBA_32, 1, 1)); + _blueTexture = gpu::TexturePointer(gpu::Texture::create2D(gpu::Element::COLOR_RGBA_32, 1, 1)); _blueTexture->setSource("TextureCache::_blueTexture"); - _blueTexture->setStoredMipFormat(_blueTexture->getTexelFormat()); - _blueTexture->assignStoredMip(0, sizeof(OPAQUE_BLUE), OPAQUE_BLUE); + _blueTexture->assignStoredMip(0, _blueTexture->getTexelFormat(), sizeof(OPAQUE_BLUE), OPAQUE_BLUE); } return _blueTexture; } const gpu::TexturePointer& TextureCache::getBlackTexture() { if (!_blackTexture) { - _blackTexture = gpu::TexturePointer(gpu::Texture::createStrict(gpu::Element::COLOR_RGBA_32, 1, 1)); + _blackTexture = gpu::TexturePointer(gpu::Texture::create2D(gpu::Element::COLOR_RGBA_32, 1, 1)); _blackTexture->setSource("TextureCache::_blackTexture"); - _blackTexture->setStoredMipFormat(_blackTexture->getTexelFormat()); - _blackTexture->assignStoredMip(0, sizeof(OPAQUE_BLACK), OPAQUE_BLACK); + _blackTexture->assignStoredMip(0, _blackTexture->getTexelFormat(), sizeof(OPAQUE_BLACK), OPAQUE_BLACK); } return _blackTexture; } @@ -188,72 +173,6 @@ NetworkTexturePointer TextureCache::getTexture(const QUrl& url, Type type, const return ResourceCache::getResource(url, QUrl(), &extra).staticCast(); } -gpu::TexturePointer TextureCache::getTextureByHash(const std::string& hash) { - std::weak_ptr weakPointer; - { - std::unique_lock lock(_texturesByHashesMutex); - weakPointer = _texturesByHashes[hash]; - } - auto result = weakPointer.lock(); - if (result) { - qCWarning(modelnetworking) << "QQQ Returning live texture for hash " << hash.c_str(); - } - return result; -} - -gpu::TexturePointer TextureCache::cacheTextureByHash(const std::string& hash, const gpu::TexturePointer& texture) { - gpu::TexturePointer result; - { - std::unique_lock lock(_texturesByHashesMutex); - result = _texturesByHashes[hash].lock(); - if (!result) { - _texturesByHashes[hash] = texture; - result = texture; - } else { - qCWarning(modelnetworking) << "QQQ Swapping out texture with previous live texture in hash " << hash.c_str(); - } - } - return result; -} - - -gpu::TexturePointer getFallbackTextureForType(NetworkTexture::Type type) { - gpu::TexturePointer result; - auto textureCache = DependencyManager::get(); - // Since this can be called on a background thread, there's a chance that the cache - // will be destroyed by the time we request it - if (!textureCache) { - return result; - } - switch (type) { - case NetworkTexture::DEFAULT_TEXTURE: - case NetworkTexture::ALBEDO_TEXTURE: - case NetworkTexture::ROUGHNESS_TEXTURE: - case NetworkTexture::OCCLUSION_TEXTURE: - result = textureCache->getWhiteTexture(); - break; - - case NetworkTexture::NORMAL_TEXTURE: - result = textureCache->getBlueTexture(); - break; - - case NetworkTexture::EMISSIVE_TEXTURE: - case NetworkTexture::LIGHTMAP_TEXTURE: - result = textureCache->getBlackTexture(); - break; - - case NetworkTexture::BUMP_TEXTURE: - case NetworkTexture::SPECULAR_TEXTURE: - case NetworkTexture::GLOSS_TEXTURE: - case NetworkTexture::CUBE_TEXTURE: - case NetworkTexture::CUSTOM_TEXTURE: - case NetworkTexture::STRICT_TEXTURE: - default: - break; - } - return result; -} - NetworkTexture::TextureLoaderFunc getTextureLoaderForType(NetworkTexture::Type type, const QVariantMap& options = QVariantMap()) { @@ -300,16 +219,11 @@ NetworkTexture::TextureLoaderFunc getTextureLoaderForType(NetworkTexture::Type t return model::TextureUsage::createMetallicTextureFromImage; break; } - case Type::STRICT_TEXTURE: { - return model::TextureUsage::createStrict2DTextureFromImage; - break; - } case Type::CUSTOM_TEXTURE: { Q_ASSERT(false); return NetworkTexture::TextureLoaderFunc(); break; } - case Type::DEFAULT_TEXTURE: default: { return model::TextureUsage::create2DTextureFromImage; @@ -331,8 +245,8 @@ QSharedPointer TextureCache::createResource(const QUrl& url, const QSh auto type = textureExtra ? textureExtra->type : Type::DEFAULT_TEXTURE; auto content = textureExtra ? textureExtra->content : QByteArray(); auto maxNumPixels = textureExtra ? textureExtra->maxNumPixels : ABSOLUTE_MAX_TEXTURE_NUM_PIXELS; - NetworkTexture* texture = new NetworkTexture(url, type, content, maxNumPixels); - return QSharedPointer(texture, &Resource::deleter); + return QSharedPointer(new NetworkTexture(url, type, content, maxNumPixels), + &Resource::deleter); } NetworkTexture::NetworkTexture(const QUrl& url, Type type, const QByteArray& content, int maxNumPixels) : @@ -346,6 +260,7 @@ NetworkTexture::NetworkTexture(const QUrl& url, Type type, const QByteArray& con _loaded = true; } + std::string theName = url.toString().toStdString(); // if we have content, load it after we have our self pointer if (!content.isEmpty()) { _startedLoading = true; @@ -353,6 +268,12 @@ NetworkTexture::NetworkTexture(const QUrl& url, Type type, const QByteArray& con } } +NetworkTexture::NetworkTexture(const QUrl& url, const TextureLoaderFunc& textureLoader, const QByteArray& content) : + NetworkTexture(url, CUSTOM_TEXTURE, content, ABSOLUTE_MAX_TEXTURE_NUM_PIXELS) +{ + _textureLoader = textureLoader; +} + NetworkTexture::TextureLoaderFunc NetworkTexture::getTextureLoader() const { if (_type == CUSTOM_TEXTURE) { return _textureLoader; @@ -360,6 +281,149 @@ NetworkTexture::TextureLoaderFunc NetworkTexture::getTextureLoader() const { return getTextureLoaderForType(_type); } + +class ImageReader : public QRunnable { +public: + + ImageReader(const QWeakPointer& resource, const QByteArray& data, + const QUrl& url = QUrl(), int maxNumPixels = ABSOLUTE_MAX_TEXTURE_NUM_PIXELS); + + virtual void run() override; + +private: + static void listSupportedImageFormats(); + + QWeakPointer _resource; + QUrl _url; + QByteArray _content; + int _maxNumPixels; +}; + +void NetworkTexture::downloadFinished(const QByteArray& data) { + // send the reader off to the thread pool + QThreadPool::globalInstance()->start(new ImageReader(_self, data, _url)); +} + +void NetworkTexture::loadContent(const QByteArray& content) { + QThreadPool::globalInstance()->start(new ImageReader(_self, content, _url, _maxNumPixels)); +} + +ImageReader::ImageReader(const QWeakPointer& resource, const QByteArray& data, + const QUrl& url, int maxNumPixels) : + _resource(resource), + _url(url), + _content(data), + _maxNumPixels(maxNumPixels) +{ +#if DEBUG_DUMP_TEXTURE_LOADS + static auto start = usecTimestampNow() / USECS_PER_MSEC; + auto now = usecTimestampNow() / USECS_PER_MSEC - start; + QString urlStr = _url.toString(); + auto dot = urlStr.lastIndexOf("."); + QString outFileName = QString(QCryptographicHash::hash(urlStr.toLocal8Bit(), QCryptographicHash::Md5).toHex()) + urlStr.right(urlStr.length() - dot); + QFile loadRecord("h:/textures/loads.txt"); + loadRecord.open(QFile::Text | QFile::Append | QFile::ReadWrite); + loadRecord.write(QString("%1 %2\n").arg(now).arg(outFileName).toLocal8Bit()); + outFileName = "h:/textures/" + outFileName; + QFileInfo outInfo(outFileName); + if (!outInfo.exists()) { + QFile outFile(outFileName); + outFile.open(QFile::WriteOnly | QFile::Truncate); + outFile.write(data); + outFile.close(); + } +#endif + DependencyManager::get()->incrementStat("PendingProcessing"); +} + +void ImageReader::listSupportedImageFormats() { + static std::once_flag once; + std::call_once(once, []{ + auto supportedFormats = QImageReader::supportedImageFormats(); + qCDebug(modelnetworking) << "List of supported Image formats:" << supportedFormats.join(", "); + }); +} + +void ImageReader::run() { + DependencyManager::get()->decrementStat("PendingProcessing"); + + CounterStat counter("Processing"); + + PROFILE_RANGE_EX(resource_parse_image, __FUNCTION__, 0xffff0000, 0, { { "url", _url.toString() } }); + auto originalPriority = QThread::currentThread()->priority(); + if (originalPriority == QThread::InheritPriority) { + originalPriority = QThread::NormalPriority; + } + QThread::currentThread()->setPriority(QThread::LowPriority); + Finally restorePriority([originalPriority]{ + QThread::currentThread()->setPriority(originalPriority); + }); + + if (!_resource.data()) { + qCWarning(modelnetworking) << "Abandoning load of" << _url << "; could not get strong ref"; + return; + } + listSupportedImageFormats(); + + // Help the QImage loader by extracting the image file format from the url filename ext. + // Some tga are not created properly without it. + auto filename = _url.fileName().toStdString(); + auto filenameExtension = filename.substr(filename.find_last_of('.') + 1); + QImage image = QImage::fromData(_content, filenameExtension.c_str()); + + // Note that QImage.format is the pixel format which is different from the "format" of the image file... + auto imageFormat = image.format(); + int imageWidth = image.width(); + int imageHeight = image.height(); + + if (imageWidth == 0 || imageHeight == 0 || imageFormat == QImage::Format_Invalid) { + if (filenameExtension.empty()) { + qCDebug(modelnetworking) << "QImage failed to create from content, no file extension:" << _url; + } else { + qCDebug(modelnetworking) << "QImage failed to create from content" << _url; + } + return; + } + + if (imageWidth * imageHeight > _maxNumPixels) { + float scaleFactor = sqrtf(_maxNumPixels / (float)(imageWidth * imageHeight)); + int originalWidth = imageWidth; + int originalHeight = imageHeight; + imageWidth = (int)(scaleFactor * (float)imageWidth + 0.5f); + imageHeight = (int)(scaleFactor * (float)imageHeight + 0.5f); + QImage newImage = image.scaled(QSize(imageWidth, imageHeight), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); + image.swap(newImage); + qCDebug(modelnetworking) << "Downscale image" << _url + << "from" << originalWidth << "x" << originalHeight + << "to" << imageWidth << "x" << imageHeight; + } + + gpu::TexturePointer texture = nullptr; + { + // Double-check the resource still exists between long operations. + auto resource = _resource.toStrongRef(); + if (!resource) { + qCWarning(modelnetworking) << "Abandoning load of" << _url << "; could not get strong ref"; + return; + } + + auto url = _url.toString().toStdString(); + + PROFILE_RANGE_EX(resource_parse_image, __FUNCTION__, 0xffffff00, 0); + texture.reset(resource.dynamicCast()->getTextureLoader()(image, url)); + } + + // Ensure the resource has not been deleted + auto resource = _resource.toStrongRef(); + if (!resource) { + qCWarning(modelnetworking) << "Abandoning load of" << _url << "; could not get strong ref"; + } else { + QMetaObject::invokeMethod(resource.data(), "setImage", + Q_ARG(gpu::TexturePointer, texture), + Q_ARG(int, imageWidth), Q_ARG(int, imageHeight)); + } +} + void NetworkTexture::setImage(gpu::TexturePointer texture, int originalWidth, int originalHeight) { _originalWidth = originalWidth; @@ -382,231 +446,3 @@ void NetworkTexture::setImage(gpu::TexturePointer texture, int originalWidth, emit networkTextureCreated(qWeakPointerCast (_self)); } - -gpu::TexturePointer NetworkTexture::getFallbackTexture() const { - if (_type == CUSTOM_TEXTURE) { - return gpu::TexturePointer(); - } - return getFallbackTextureForType(_type); -} - -class Reader : public QRunnable { -public: - Reader(const QWeakPointer& resource, const QUrl& url); - void run() override final; - virtual void read() = 0; - -protected: - QWeakPointer _resource; - QUrl _url; -}; - -class ImageReader : public Reader { -public: - ImageReader(const QWeakPointer& resource, const QUrl& url, - const QByteArray& data, const std::string& hash, int maxNumPixels); - void read() override final; - -private: - static void listSupportedImageFormats(); - - QByteArray _content; - std::string _hash; - int _maxNumPixels; -}; - -void NetworkTexture::downloadFinished(const QByteArray& data) { - loadContent(data); -} - -void NetworkTexture::loadContent(const QByteArray& content) { - // Hash the source image to for KTX caching - std::string hash; - { - QCryptographicHash hasher(QCryptographicHash::Md5); - hasher.addData(content); - hash = hasher.result().toHex().toStdString(); - } - - auto textureCache = static_cast(_cache.data()); - - if (textureCache != nullptr) { - // If we already have a live texture with the same hash, use it - auto texture = textureCache->getTextureByHash(hash); - - // If there is no live texture, check if there's an existing KTX file - if (!texture) { - KTXFilePointer ktxFile = textureCache->_ktxCache.getFile(hash); - if (ktxFile) { - // Ensure that the KTX deserialization worked - auto ktx = ktxFile->getKTX(); - if (ktx) { - texture.reset(gpu::Texture::unserialize(ktx)); - // Ensure that the texture population worked - if (texture) { - texture->setKtxBacking(ktx); - texture = textureCache->cacheTextureByHash(hash, texture); - } - } - } - } - - // If we found the texture either because it's in use or via KTX deserialization, - // set the image and return immediately. - if (texture) { - setImage(texture, texture->getWidth(), texture->getHeight()); - return; - } - } - - // We failed to find an existing live or KTX texture, so trigger an image reader - QThreadPool::globalInstance()->start(new ImageReader(_self, _url, content, hash, _maxNumPixels)); -} - -Reader::Reader(const QWeakPointer& resource, const QUrl& url) : - _resource(resource), _url(url) { - DependencyManager::get()->incrementStat("PendingProcessing"); -} - -void Reader::run() { - PROFILE_RANGE_EX(resource_parse_image, __FUNCTION__, 0xffff0000, 0, { { "url", _url.toString() } }); - DependencyManager::get()->decrementStat("PendingProcessing"); - CounterStat counter("Processing"); - - auto originalPriority = QThread::currentThread()->priority(); - if (originalPriority == QThread::InheritPriority) { - originalPriority = QThread::NormalPriority; - } - QThread::currentThread()->setPriority(QThread::LowPriority); - Finally restorePriority([originalPriority]{ QThread::currentThread()->setPriority(originalPriority); }); - - if (!_resource.data()) { - qCWarning(modelnetworking) << "Abandoning load of" << _url << "; could not get strong ref"; - return; - } - - read(); -} - -ImageReader::ImageReader(const QWeakPointer& resource, const QUrl& url, - const QByteArray& data, const std::string& hash, int maxNumPixels) : - Reader(resource, url), _content(data), _hash(hash), _maxNumPixels(maxNumPixels) { - listSupportedImageFormats(); - -#if DEBUG_DUMP_TEXTURE_LOADS - static auto start = usecTimestampNow() / USECS_PER_MSEC; - auto now = usecTimestampNow() / USECS_PER_MSEC - start; - QString urlStr = _url.toString(); - auto dot = urlStr.lastIndexOf("."); - QString outFileName = QString(QCryptographicHash::hash(urlStr.toLocal8Bit(), QCryptographicHash::Md5).toHex()) + urlStr.right(urlStr.length() - dot); - QFile loadRecord("h:/textures/loads.txt"); - loadRecord.open(QFile::Text | QFile::Append | QFile::ReadWrite); - loadRecord.write(QString("%1 %2\n").arg(now).arg(outFileName).toLocal8Bit()); - outFileName = "h:/textures/" + outFileName; - QFileInfo outInfo(outFileName); - if (!outInfo.exists()) { - QFile outFile(outFileName); - outFile.open(QFile::WriteOnly | QFile::Truncate); - outFile.write(data); - outFile.close(); - } -#endif -} - -void ImageReader::listSupportedImageFormats() { - static std::once_flag once; - std::call_once(once, []{ - auto supportedFormats = QImageReader::supportedImageFormats(); - qCDebug(modelnetworking) << "List of supported Image formats:" << supportedFormats.join(", "); - }); -} - -void ImageReader::read() { - // Help the QImage loader by extracting the image file format from the url filename ext. - // Some tga are not created properly without it. - auto filename = _url.fileName().toStdString(); - auto filenameExtension = filename.substr(filename.find_last_of('.') + 1); - QImage image = QImage::fromData(_content, filenameExtension.c_str()); - int imageWidth = image.width(); - int imageHeight = image.height(); - - // Validate that the image loaded - if (imageWidth == 0 || imageHeight == 0 || image.format() == QImage::Format_Invalid) { - QString reason(filenameExtension.empty() ? "" : "(no file extension)"); - qCWarning(modelnetworking) << "Failed to load" << _url << reason; - return; - } - - // Validate the image is less than _maxNumPixels, and downscale if necessary - if (imageWidth * imageHeight > _maxNumPixels) { - float scaleFactor = sqrtf(_maxNumPixels / (float)(imageWidth * imageHeight)); - int originalWidth = imageWidth; - int originalHeight = imageHeight; - imageWidth = (int)(scaleFactor * (float)imageWidth + 0.5f); - imageHeight = (int)(scaleFactor * (float)imageHeight + 0.5f); - QImage newImage = image.scaled(QSize(imageWidth, imageHeight), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); - image.swap(newImage); - qCDebug(modelnetworking).nospace() << "Downscaled " << _url << " (" << - QSize(originalWidth, originalHeight) << " to " << - QSize(imageWidth, imageHeight) << ")"; - } - - gpu::TexturePointer texture = nullptr; - { - auto resource = _resource.lock(); // to ensure the resource is still needed - if (!resource) { - qCDebug(modelnetworking) << _url << "loading stopped; resource out of scope"; - return; - } - - auto url = _url.toString().toStdString(); - - PROFILE_RANGE_EX(resource_parse_image_raw, __FUNCTION__, 0xffff0000, 0); - // Load the image into a gpu::Texture - auto networkTexture = resource.staticCast(); - texture.reset(networkTexture->getTextureLoader()(image, url)); - texture->setSource(url); - if (texture) { - texture->setFallbackTexture(networkTexture->getFallbackTexture()); - } - - auto textureCache = DependencyManager::get(); - // Save the image into a KTXFile - auto memKtx = gpu::Texture::serialize(*texture); - if (!memKtx) { - qCWarning(modelnetworking) << "Unable to serialize texture to KTX " << _url; - } - - if (memKtx && textureCache) { - 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(_hash, length)))) { - qCWarning(modelnetworking) << _url << "file cache failed"; - } else { - resource.staticCast()->_file = file; - auto fileKtx = file->getKTX(); - if (fileKtx) { - texture->setKtxBacking(fileKtx); - } - } - } - - // 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 - if (textureCache) { - texture = textureCache->cacheTextureByHash(_hash, texture); - } - } - - auto resource = _resource.lock(); // to ensure the resource is still needed - if (resource) { - QMetaObject::invokeMethod(resource.data(), "setImage", - Q_ARG(gpu::TexturePointer, texture), - Q_ARG(int, imageWidth), Q_ARG(int, imageHeight)); - } else { - qCDebug(modelnetworking) << _url << "loading stopped; resource out of scope"; - } -} diff --git a/libraries/model-networking/src/model-networking/TextureCache.h b/libraries/model-networking/src/model-networking/TextureCache.h index 6005cc1226..77311afae6 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.h +++ b/libraries/model-networking/src/model-networking/TextureCache.h @@ -23,8 +23,6 @@ #include #include -#include "KTXCache.h" - const int ABSOLUTE_MAX_TEXTURE_NUM_PIXELS = 8192 * 8192; namespace gpu { @@ -45,7 +43,6 @@ class NetworkTexture : public Resource, public Texture { public: enum Type { DEFAULT_TEXTURE, - STRICT_TEXTURE, ALBEDO_TEXTURE, NORMAL_TEXTURE, BUMP_TEXTURE, @@ -66,6 +63,7 @@ public: using TextureLoaderFunc = std::function; NetworkTexture(const QUrl& url, Type type, const QByteArray& content, int maxNumPixels); + NetworkTexture(const QUrl& url, const TextureLoaderFunc& textureLoader, const QByteArray& content); QString getType() const override { return "NetworkTexture"; } @@ -76,12 +74,12 @@ public: Type getTextureType() const { return _type; } TextureLoaderFunc getTextureLoader() const; - gpu::TexturePointer getFallbackTexture() const; signals: void networkTextureCreated(const QWeakPointer& self); protected: + virtual bool isCacheable() const override { return _loaded; } virtual void downloadFinished(const QByteArray& data) override; @@ -90,12 +88,8 @@ protected: Q_INVOKABLE void setImage(gpu::TexturePointer texture, int originalWidth, int originalHeight); private: - friend class KTXReader; - friend class ImageReader; - Type _type; TextureLoaderFunc _textureLoader { [](const QImage&, const std::string&){ return nullptr; } }; - KTXFilePointer _file; int _originalWidth { 0 }; int _originalHeight { 0 }; int _width { 0 }; @@ -137,10 +131,6 @@ public: NetworkTexturePointer getTexture(const QUrl& url, Type type = Type::DEFAULT_TEXTURE, const QByteArray& content = QByteArray(), int maxNumPixels = ABSOLUTE_MAX_TEXTURE_NUM_PIXELS); - - gpu::TexturePointer getTextureByHash(const std::string& hash); - gpu::TexturePointer cacheTextureByHash(const std::string& hash, const gpu::TexturePointer& texture); - 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); @@ -149,19 +139,9 @@ protected: const void* extra) override; private: - friend class ImageReader; - friend class NetworkTexture; - friend class DilatableNetworkTexture; - TextureCache(); virtual ~TextureCache(); - - static const std::string KTX_DIRNAME; - static const std::string KTX_EXT; - KTXCache _ktxCache; - // Map from image hashes to texture weak pointers - std::unordered_map> _texturesByHashes; - std::mutex _texturesByHashesMutex; + friend class DilatableNetworkTexture; gpu::TexturePointer _permutationNormalTexture; gpu::TexturePointer _whiteTexture; diff --git a/libraries/model/CMakeLists.txt b/libraries/model/CMakeLists.txt index 021aa3d027..63f632e484 100755 --- a/libraries/model/CMakeLists.txt +++ b/libraries/model/CMakeLists.txt @@ -1,5 +1,5 @@ set(TARGET_NAME model) AUTOSCRIBE_SHADER_LIB(gpu model) setup_hifi_library() -link_hifi_libraries(shared ktx gpu) +link_hifi_libraries(shared gpu) diff --git a/libraries/model/src/model/Geometry.cpp b/libraries/model/src/model/Geometry.cpp index 04b0db92d3..2bb6cfa436 100755 --- a/libraries/model/src/model/Geometry.cpp +++ b/libraries/model/src/model/Geometry.cpp @@ -117,7 +117,7 @@ Box Mesh::evalPartsBound(int partStart, int partEnd) const { auto partItEnd = _partBuffer.cbegin() + partEnd; for (;part != partItEnd; part++) { - + Box partBound; auto index = _indexBuffer.cbegin() + (*part)._startIndex; auto endIndex = index + (*part)._numIndices; @@ -134,115 +134,6 @@ Box Mesh::evalPartsBound(int partStart, int partEnd) const { return totalBound; } - -model::MeshPointer Mesh::map(std::function vertexFunc, - std::function normalFunc, - std::function indexFunc) { - // vertex data - const gpu::BufferView& vertexBufferView = getVertexBuffer(); - gpu::BufferView::Index numVertices = (gpu::BufferView::Index)getNumVertices(); - gpu::Resource::Size vertexSize = numVertices * sizeof(glm::vec3); - unsigned char* resultVertexData = new unsigned char[vertexSize]; - unsigned char* vertexDataCursor = resultVertexData; - - for (gpu::BufferView::Index i = 0; i < numVertices; i ++) { - glm::vec3 pos = vertexFunc(vertexBufferView.get(i)); - memcpy(vertexDataCursor, &pos, sizeof(pos)); - vertexDataCursor += sizeof(pos); - } - - // normal data - int attributeTypeNormal = gpu::Stream::InputSlot::NORMAL; // libraries/gpu/src/gpu/Stream.h - const gpu::BufferView& normalsBufferView = getAttributeBuffer(attributeTypeNormal); - gpu::BufferView::Index numNormals = (gpu::BufferView::Index)normalsBufferView.getNumElements(); - gpu::Resource::Size normalSize = numNormals * sizeof(glm::vec3); - unsigned char* resultNormalData = new unsigned char[normalSize]; - unsigned char* normalDataCursor = resultNormalData; - - for (gpu::BufferView::Index i = 0; i < numNormals; i ++) { - glm::vec3 normal = normalFunc(normalsBufferView.get(i)); - memcpy(normalDataCursor, &normal, sizeof(normal)); - normalDataCursor += sizeof(normal); - } - // TODO -- other attributes - - // face data - const gpu::BufferView& indexBufferView = getIndexBuffer(); - gpu::BufferView::Index numIndexes = (gpu::BufferView::Index)getNumIndices(); - gpu::Resource::Size indexSize = numIndexes * sizeof(uint32_t); - unsigned char* resultIndexData = new unsigned char[indexSize]; - unsigned char* indexDataCursor = resultIndexData; - - for (gpu::BufferView::Index i = 0; i < numIndexes; i ++) { - uint32_t index = indexFunc(indexBufferView.get(i)); - memcpy(indexDataCursor, &index, sizeof(index)); - indexDataCursor += sizeof(index); - } - - model::MeshPointer result(new model::Mesh()); - - gpu::Element vertexElement = gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ); - gpu::Buffer* resultVertexBuffer = new gpu::Buffer(vertexSize, resultVertexData); - gpu::BufferPointer resultVertexBufferPointer(resultVertexBuffer); - gpu::BufferView resultVertexBufferView(resultVertexBufferPointer, vertexElement); - result->setVertexBuffer(resultVertexBufferView); - - gpu::Element normalElement = gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ); - gpu::Buffer* resultNormalsBuffer = new gpu::Buffer(normalSize, resultNormalData); - gpu::BufferPointer resultNormalsBufferPointer(resultNormalsBuffer); - gpu::BufferView resultNormalsBufferView(resultNormalsBufferPointer, normalElement); - result->addAttribute(attributeTypeNormal, resultNormalsBufferView); - - gpu::Element indexElement = gpu::Element(gpu::SCALAR, gpu::UINT32, gpu::RAW); - gpu::Buffer* resultIndexesBuffer = new gpu::Buffer(indexSize, resultIndexData); - gpu::BufferPointer resultIndexesBufferPointer(resultIndexesBuffer); - gpu::BufferView resultIndexesBufferView(resultIndexesBufferPointer, indexElement); - result->setIndexBuffer(resultIndexesBufferView); - - - // TODO -- shouldn't assume just one part - - std::vector parts; - parts.emplace_back(model::Mesh::Part((model::Index)0, // startIndex - (model::Index)result->getNumIndices(), // numIndices - (model::Index)0, // baseVertex - model::Mesh::TRIANGLES)); // topology - result->setPartBuffer(gpu::BufferView(new gpu::Buffer(parts.size() * sizeof(model::Mesh::Part), - (gpu::Byte*) parts.data()), gpu::Element::PART_DRAWCALL)); - - return result; -} - - -void Mesh::forEach(std::function vertexFunc, - std::function normalFunc, - std::function indexFunc) { - int attributeTypeNormal = gpu::Stream::InputSlot::NORMAL; // libraries/gpu/src/gpu/Stream.h - - // vertex data - const gpu::BufferView& vertexBufferView = getVertexBuffer(); - gpu::BufferView::Index numVertices = (gpu::BufferView::Index)getNumVertices(); - for (gpu::BufferView::Index i = 0; i < numVertices; i ++) { - vertexFunc(vertexBufferView.get(i)); - } - - // normal data - const gpu::BufferView& normalsBufferView = getAttributeBuffer(attributeTypeNormal); - gpu::BufferView::Index numNormals = (gpu::BufferView::Index) normalsBufferView.getNumElements(); - for (gpu::BufferView::Index i = 0; i < numNormals; i ++) { - normalFunc(normalsBufferView.get(i)); - } - // TODO -- other attributes - - // face data - const gpu::BufferView& indexBufferView = getIndexBuffer(); - gpu::BufferView::Index numIndexes = (gpu::BufferView::Index)getNumIndices(); - for (gpu::BufferView::Index i = 0; i < numIndexes; i ++) { - indexFunc(indexBufferView.get(i)); - } -} - - Geometry::Geometry() { } @@ -257,3 +148,4 @@ Geometry::~Geometry() { void Geometry::setMesh(const MeshPointer& mesh) { _mesh = mesh; } + diff --git a/libraries/model/src/model/Geometry.h b/libraries/model/src/model/Geometry.h index 7ba3e83407..4256f0be03 100755 --- a/libraries/model/src/model/Geometry.h +++ b/libraries/model/src/model/Geometry.h @@ -25,10 +25,6 @@ typedef AABox Box; typedef std::vector< Box > Boxes; typedef glm::vec3 Vec3; -class Mesh; -using MeshPointer = std::shared_ptr< Mesh >; - - class Mesh { public: const static Index PRIMITIVE_RESTART_INDEX = -1; @@ -118,15 +114,6 @@ public: static gpu::Primitive topologyToPrimitive(Topology topo) { return static_cast(topo); } - // create a copy of this mesh after passing its vertices, normals, and indexes though the provided functions - MeshPointer map(std::function vertexFunc, - std::function normalFunc, - std::function indexFunc); - - void forEach(std::function vertexFunc, - std::function normalFunc, - std::function indexFunc); - protected: gpu::Stream::FormatPointer _vertexFormat; @@ -143,6 +130,7 @@ protected: void evalVertexStream(); }; +using MeshPointer = std::shared_ptr< Mesh >; class Geometry { diff --git a/libraries/model/src/model/TextureMap.cpp b/libraries/model/src/model/TextureMap.cpp index d07eae2166..7ac8083d9c 100755 --- a/libraries/model/src/model/TextureMap.cpp +++ b/libraries/model/src/model/TextureMap.cpp @@ -10,15 +10,10 @@ // #include "TextureMap.h" -#include - #include #include #include -#include -#include -#include -#include + #include #include "ModelLogging.h" @@ -154,7 +149,7 @@ const QImage TextureUsage::process2DImageColor(const QImage& srcImage, bool& val return image; } -void TextureUsage::defineColorTexelFormats(gpu::Element& formatGPU, gpu::Element& formatMip, +void TextureUsage::defineColorTexelFormats(gpu::Element& formatGPU, gpu::Element& formatMip, const QImage& image, bool isLinear, bool doCompress) { #ifdef COMPRESS_TEXTURES @@ -207,7 +202,7 @@ const QImage& image, bool isLinear, bool doCompress) { #define CPU_MIPMAPS 1 -void generateMips(gpu::Texture* texture, QImage& image, bool fastResize) { +void generateMips(gpu::Texture* texture, QImage& image, gpu::Element formatMip, bool fastResize) { #if CPU_MIPMAPS PROFILE_RANGE(resource_parse, "generateMips"); auto numMips = texture->evalNumMips(); @@ -215,33 +210,32 @@ void generateMips(gpu::Texture* texture, QImage& image, bool fastResize) { QSize mipSize(texture->evalMipWidth(level), texture->evalMipHeight(level)); if (fastResize) { image = image.scaled(mipSize); - texture->assignStoredMip(level, image.byteCount(), image.constBits()); + texture->assignStoredMip(level, formatMip, image.byteCount(), image.constBits()); } else { QImage mipImage = image.scaled(mipSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); - texture->assignStoredMip(level, mipImage.byteCount(), mipImage.constBits()); + texture->assignStoredMip(level, formatMip, mipImage.byteCount(), mipImage.constBits()); } } - #else texture->autoGenerateMips(-1); #endif } -void generateFaceMips(gpu::Texture* texture, QImage& image, uint8 face) { +void generateFaceMips(gpu::Texture* texture, QImage& image, gpu::Element formatMip, uint8 face) { #if CPU_MIPMAPS PROFILE_RANGE(resource_parse, "generateFaceMips"); auto numMips = texture->evalNumMips(); for (uint16 level = 1; level < numMips; ++level) { QSize mipSize(texture->evalMipWidth(level), texture->evalMipHeight(level)); QImage mipImage = image.scaled(mipSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); - texture->assignStoredMipFace(level, face, mipImage.byteCount(), mipImage.constBits()); + texture->assignStoredMipFace(level, formatMip, mipImage.byteCount(), mipImage.constBits(), face); } #else texture->autoGenerateMips(-1); #endif } -gpu::Texture* TextureUsage::process2DTextureColorFromImage(const QImage& srcImage, const std::string& srcImageName, bool isLinear, bool doCompress, bool generateMips, bool isStrict) { +gpu::Texture* TextureUsage::process2DTextureColorFromImage(const QImage& srcImage, const std::string& srcImageName, bool isLinear, bool doCompress, bool generateMips) { PROFILE_RANGE(resource_parse, "process2DTextureColorFromImage"); bool validAlpha = false; bool alphaAsMask = true; @@ -254,11 +248,7 @@ gpu::Texture* TextureUsage::process2DTextureColorFromImage(const QImage& srcImag gpu::Element formatMip; defineColorTexelFormats(formatGPU, formatMip, image, isLinear, doCompress); - if (isStrict) { - theTexture = (gpu::Texture::createStrict(formatGPU, image.width(), image.height(), gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR))); - } else { - theTexture = (gpu::Texture::create2D(formatGPU, image.width(), image.height(), gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR))); - } + theTexture = (gpu::Texture::create2D(formatGPU, image.width(), image.height(), gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR))); theTexture->setSource(srcImageName); auto usage = gpu::Texture::Usage::Builder().withColor(); if (validAlpha) { @@ -268,26 +258,22 @@ gpu::Texture* TextureUsage::process2DTextureColorFromImage(const QImage& srcImag } } theTexture->setUsage(usage.build()); - theTexture->setStoredMipFormat(formatMip); - theTexture->assignStoredMip(0, image.byteCount(), image.constBits()); + + theTexture->assignStoredMip(0, formatMip, image.byteCount(), image.constBits()); if (generateMips) { - ::generateMips(theTexture, image, false); + ::generateMips(theTexture, image, formatMip, false); } - theTexture->setSource(srcImageName); } return theTexture; } -gpu::Texture* TextureUsage::createStrict2DTextureFromImage(const QImage& srcImage, const std::string& srcImageName) { - return process2DTextureColorFromImage(srcImage, srcImageName, false, false, true, true); -} - gpu::Texture* TextureUsage::create2DTextureFromImage(const QImage& srcImage, const std::string& srcImageName) { return process2DTextureColorFromImage(srcImage, srcImageName, false, false, true); } + gpu::Texture* TextureUsage::createAlbedoTextureFromImage(const QImage& srcImage, const std::string& srcImageName) { return process2DTextureColorFromImage(srcImage, srcImageName, false, true, true); } @@ -305,25 +291,21 @@ gpu::Texture* TextureUsage::createNormalTextureFromNormalImage(const QImage& src PROFILE_RANGE(resource_parse, "createNormalTextureFromNormalImage"); QImage image = processSourceImage(srcImage, false); - // Make sure the normal map source image is ARGB32 - if (image.format() != QImage::Format_ARGB32) { - image = image.convertToFormat(QImage::Format_ARGB32); + // Make sure the normal map source image is RGBA32 + if (image.format() != QImage::Format_RGBA8888) { + image = image.convertToFormat(QImage::Format_RGBA8888); } - gpu::Texture* theTexture = nullptr; if ((image.width() > 0) && (image.height() > 0)) { - gpu::Element formatMip = gpu::Element::COLOR_BGRA_32; - gpu::Element formatGPU = gpu::Element::COLOR_RGBA_32; + gpu::Element formatGPU = gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA); + gpu::Element formatMip = gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA); theTexture = (gpu::Texture::create2D(formatGPU, image.width(), image.height(), gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR))); theTexture->setSource(srcImageName); - theTexture->setStoredMipFormat(formatMip); - theTexture->assignStoredMip(0, image.byteCount(), image.constBits()); - generateMips(theTexture, image, true); - - theTexture->setSource(srcImageName); + theTexture->assignStoredMip(0, formatMip, image.byteCount(), image.constBits()); + generateMips(theTexture, image, formatMip, true); } return theTexture; @@ -354,17 +336,16 @@ gpu::Texture* TextureUsage::createNormalTextureFromBumpImage(const QImage& srcIm const double pStrength = 2.0; int width = image.width(); int height = image.height(); - - QImage result(width, height, QImage::Format_ARGB32); - + QImage result(width, height, QImage::Format_RGB888); + for (int i = 0; i < width; i++) { const int iNextClamped = clampPixelCoordinate(i + 1, width - 1); const int iPrevClamped = clampPixelCoordinate(i - 1, width - 1); - + for (int j = 0; j < height; j++) { const int jNextClamped = clampPixelCoordinate(j + 1, height - 1); const int jPrevClamped = clampPixelCoordinate(j - 1, height - 1); - + // surrounding pixels const QRgb topLeft = image.pixel(iPrevClamped, jPrevClamped); const QRgb top = image.pixel(iPrevClamped, j); @@ -374,7 +355,7 @@ gpu::Texture* TextureUsage::createNormalTextureFromBumpImage(const QImage& srcIm const QRgb bottom = image.pixel(iNextClamped, j); const QRgb bottomLeft = image.pixel(iNextClamped, jPrevClamped); const QRgb left = image.pixel(i, jPrevClamped); - + // take their gray intensities // since it's a grayscale image, the value of each component RGB is the same const double tl = qRed(topLeft); @@ -385,15 +366,15 @@ gpu::Texture* TextureUsage::createNormalTextureFromBumpImage(const QImage& srcIm const double b = qRed(bottom); const double bl = qRed(bottomLeft); const double l = qRed(left); - + // apply the sobel filter const double dX = (tr + pStrength * r + br) - (tl + pStrength * l + bl); const double dY = (bl + pStrength * b + br) - (tl + pStrength * t + tr); const double dZ = RGBA_MAX / pStrength; - + glm::vec3 v(dX, dY, dZ); glm::normalize(v); - + // convert to rgb from the value obtained computing the filter QRgb qRgbValue = qRgba(mapComponent(v.x), mapComponent(v.y), mapComponent(v.z), 1.0); result.setPixel(i, j, qRgbValue); @@ -401,19 +382,13 @@ gpu::Texture* TextureUsage::createNormalTextureFromBumpImage(const QImage& srcIm } gpu::Texture* theTexture = nullptr; - if ((result.width() > 0) && (result.height() > 0)) { - - gpu::Element formatMip = gpu::Element::COLOR_BGRA_32; - gpu::Element formatGPU = gpu::Element::COLOR_RGBA_32; - - - theTexture = (gpu::Texture::create2D(formatGPU, result.width(), result.height(), gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR))); - theTexture->setSource(srcImageName); - theTexture->setStoredMipFormat(formatMip); - theTexture->assignStoredMip(0, result.byteCount(), result.constBits()); - generateMips(theTexture, result, true); + if ((image.width() > 0) && (image.height() > 0)) { + gpu::Element formatGPU = gpu::Element(gpu::VEC3, gpu::NUINT8, gpu::RGB); + gpu::Element formatMip = gpu::Element(gpu::VEC3, gpu::NUINT8, gpu::RGB); + theTexture = (gpu::Texture::create2D(formatGPU, image.width(), image.height(), gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR))); theTexture->setSource(srcImageName); + theTexture->assignStoredMip(0, formatMip, image.byteCount(), image.constBits()); } return theTexture; @@ -439,17 +414,16 @@ gpu::Texture* TextureUsage::createRoughnessTextureFromImage(const QImage& srcIma #ifdef COMPRESS_TEXTURES gpu::Element formatGPU = gpu::Element(gpu::SCALAR, gpu::NUINT8, gpu::COMPRESSED_R); #else - gpu::Element formatGPU = gpu::Element::COLOR_R_8; + gpu::Element formatGPU = gpu::Element(gpu::SCALAR, gpu::NUINT8, gpu::RGB); #endif - gpu::Element formatMip = gpu::Element::COLOR_R_8; + gpu::Element formatMip = gpu::Element(gpu::SCALAR, gpu::NUINT8, gpu::RGB); theTexture = (gpu::Texture::create2D(formatGPU, image.width(), image.height(), gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR))); theTexture->setSource(srcImageName); - theTexture->setStoredMipFormat(formatMip); - theTexture->assignStoredMip(0, image.byteCount(), image.constBits()); - generateMips(theTexture, image, true); + theTexture->assignStoredMip(0, formatMip, image.byteCount(), image.constBits()); + generateMips(theTexture, image, formatMip, true); - theTexture->setSource(srcImageName); + // FIXME queue for transfer to GPU and block on completion } return theTexture; @@ -470,28 +444,27 @@ gpu::Texture* TextureUsage::createRoughnessTextureFromGlossImage(const QImage& s // Gloss turned into Rough image.invertPixels(QImage::InvertRgba); - + image = image.convertToFormat(QImage::Format_Grayscale8); - + gpu::Texture* theTexture = nullptr; if ((image.width() > 0) && (image.height() > 0)) { - + #ifdef COMPRESS_TEXTURES gpu::Element formatGPU = gpu::Element(gpu::SCALAR, gpu::NUINT8, gpu::COMPRESSED_R); #else - gpu::Element formatGPU = gpu::Element::COLOR_R_8; + gpu::Element formatGPU = gpu::Element(gpu::SCALAR, gpu::NUINT8, gpu::RGB); #endif - gpu::Element formatMip = gpu::Element::COLOR_R_8; + gpu::Element formatMip = gpu::Element(gpu::SCALAR, gpu::NUINT8, gpu::RGB); theTexture = (gpu::Texture::create2D(formatGPU, image.width(), image.height(), gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR))); theTexture->setSource(srcImageName); - theTexture->setStoredMipFormat(formatMip); - theTexture->assignStoredMip(0, image.byteCount(), image.constBits()); - generateMips(theTexture, image, true); - - theTexture->setSource(srcImageName); + theTexture->assignStoredMip(0, formatMip, image.byteCount(), image.constBits()); + generateMips(theTexture, image, formatMip, true); + + // FIXME queue for transfer to GPU and block on completion } - + return theTexture; } @@ -516,17 +489,16 @@ gpu::Texture* TextureUsage::createMetallicTextureFromImage(const QImage& srcImag #ifdef COMPRESS_TEXTURES gpu::Element formatGPU = gpu::Element(gpu::SCALAR, gpu::NUINT8, gpu::COMPRESSED_R); #else - gpu::Element formatGPU = gpu::Element::COLOR_R_8; + gpu::Element formatGPU = gpu::Element(gpu::SCALAR, gpu::NUINT8, gpu::RGB); #endif - gpu::Element formatMip = gpu::Element::COLOR_R_8; + gpu::Element formatMip = gpu::Element(gpu::SCALAR, gpu::NUINT8, gpu::RGB); theTexture = (gpu::Texture::create2D(formatGPU, image.width(), image.height(), gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR))); theTexture->setSource(srcImageName); - theTexture->setStoredMipFormat(formatMip); - theTexture->assignStoredMip(0, image.byteCount(), image.constBits()); - generateMips(theTexture, image, true); + theTexture->assignStoredMip(0, formatMip, image.byteCount(), image.constBits()); + generateMips(theTexture, image, formatMip, true); - theTexture->setSource(srcImageName); + // FIXME queue for transfer to GPU and block on completion } return theTexture; @@ -549,18 +521,18 @@ public: int _y = 0; bool _horizontalMirror = false; bool _verticalMirror = false; - + Face() {} Face(int x, int y, bool horizontalMirror, bool verticalMirror) : _x(x), _y(y), _horizontalMirror(horizontalMirror), _verticalMirror(verticalMirror) {} }; - + Face _faceXPos; Face _faceXNeg; Face _faceYPos; Face _faceYNeg; Face _faceZPos; Face _faceZNeg; - + CubeLayout(int wr, int hr, Face fXP, Face fXN, Face fYP, Face fYN, Face fZP, Face fZN) : _type(FLAT), _widthRatio(wr), @@ -803,7 +775,7 @@ gpu::Texture* TextureUsage::processCubeTextureColorFromImage(const QImage& srcIm defineColorTexelFormats(formatGPU, formatMip, image, isLinear, doCompress); // Find the layout of the cubemap in the 2D image - // Use the original image size since processSourceImage may have altered the size / aspect ratio + // Use the original image size since processSourceImage may have altered the size / aspect ratio int foundLayout = CubeLayout::findLayout(srcImage.width(), srcImage.height()); std::vector faces; @@ -838,12 +810,11 @@ gpu::Texture* TextureUsage::processCubeTextureColorFromImage(const QImage& srcIm if (faces.size() == gpu::Texture::NUM_FACES_PER_TYPE[gpu::Texture::TEX_CUBE]) { theTexture = gpu::Texture::createCube(formatGPU, faces[0].width(), gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR, gpu::Sampler::WRAP_CLAMP)); theTexture->setSource(srcImageName); - theTexture->setStoredMipFormat(formatMip); int f = 0; for (auto& face : faces) { - theTexture->assignStoredMipFace(0, f, face.byteCount(), face.constBits()); + theTexture->assignStoredMipFace(0, formatMip, face.byteCount(), face.constBits(), f); if (generateMips) { - generateFaceMips(theTexture, face, f); + generateFaceMips(theTexture, face, formatMip, f); } f++; } @@ -858,8 +829,6 @@ gpu::Texture* TextureUsage::processCubeTextureColorFromImage(const QImage& srcIm PROFILE_RANGE(resource_parse, "generateIrradiance"); theTexture->generateIrradiance(); } - - theTexture->setSource(srcImageName); } } diff --git a/libraries/model/src/model/TextureMap.h b/libraries/model/src/model/TextureMap.h index a4bb861502..220ee57a97 100755 --- a/libraries/model/src/model/TextureMap.h +++ b/libraries/model/src/model/TextureMap.h @@ -32,7 +32,6 @@ public: int _environmentUsage = 0; static gpu::Texture* create2DTextureFromImage(const QImage& image, const std::string& srcImageName); - static gpu::Texture* createStrict2DTextureFromImage(const QImage& image, const std::string& srcImageName); static gpu::Texture* createAlbedoTextureFromImage(const QImage& image, const std::string& srcImageName); static gpu::Texture* createEmissiveTextureFromImage(const QImage& image, const std::string& srcImageName); static gpu::Texture* createNormalTextureFromNormalImage(const QImage& image, const std::string& srcImageName); @@ -48,7 +47,7 @@ public: static const QImage process2DImageColor(const QImage& srcImage, bool& validAlpha, bool& alphaAsMask); static void defineColorTexelFormats(gpu::Element& formatGPU, gpu::Element& formatMip, const QImage& srcImage, bool isLinear, bool doCompress); - static gpu::Texture* process2DTextureColorFromImage(const QImage& srcImage, const std::string& srcImageName, bool isLinear, bool doCompress, bool generateMips, bool isStrict = false); + static gpu::Texture* process2DTextureColorFromImage(const QImage& srcImage, const std::string& srcImageName, bool isLinear, bool doCompress, bool generateMips); static gpu::Texture* processCubeTextureColorFromImage(const QImage& srcImage, const std::string& srcImageName, bool isLinear, bool doCompress, bool generateMips, bool generateIrradiance); }; diff --git a/libraries/networking/src/Assignment.cpp b/libraries/networking/src/Assignment.cpp index 27d4a31ccf..9efad15398 100644 --- a/libraries/networking/src/Assignment.cpp +++ b/libraries/networking/src/Assignment.cpp @@ -12,6 +12,7 @@ #include "udt/PacketHeaders.h" #include "SharedUtil.h" #include "UUID.h" +#include "ServerPathUtils.h" #include diff --git a/libraries/networking/src/FileCache.cpp b/libraries/networking/src/FileCache.cpp deleted file mode 100644 index f8a86903cb..0000000000 --- a/libraries/networking/src/FileCache.cpp +++ /dev/null @@ -1,243 +0,0 @@ -// -// FileCache.cpp -// libraries/model-networking/src -// -// Created by Zach Pomerantz on 2/21/2017. -// Copyright 2017 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#include "FileCache.h" - -#include -#include -#include -#include - -#include - -#include - -Q_LOGGING_CATEGORY(file_cache, "hifi.file_cache", QtWarningMsg) - -using namespace cache; - -static const std::string MANIFEST_NAME = "manifest"; - -static const size_t BYTES_PER_MEGABYTES = 1024 * 1024; -static const size_t BYTES_PER_GIGABYTES = 1024 * BYTES_PER_MEGABYTES; -const size_t FileCache::DEFAULT_UNUSED_MAX_SIZE = 5 * BYTES_PER_GIGABYTES; // 5GB -const size_t FileCache::MAX_UNUSED_MAX_SIZE = 100 * BYTES_PER_GIGABYTES; // 100GB -const size_t FileCache::DEFAULT_OFFLINE_MAX_SIZE = 2 * BYTES_PER_GIGABYTES; // 2GB - -void FileCache::setUnusedFileCacheSize(size_t unusedFilesMaxSize) { - _unusedFilesMaxSize = std::min(unusedFilesMaxSize, MAX_UNUSED_MAX_SIZE); - reserve(0); - emit dirty(); -} - -void FileCache::setOfflineFileCacheSize(size_t offlineFilesMaxSize) { - _offlineFilesMaxSize = std::min(offlineFilesMaxSize, MAX_UNUSED_MAX_SIZE); -} - -FileCache::FileCache(const std::string& dirname, const std::string& ext, QObject* parent) : - QObject(parent), - _ext(ext), - _dirname(dirname), - _dirpath(PathUtils::getAppLocalDataFilePath(dirname.c_str()).toStdString()) {} - -FileCache::~FileCache() { - clear(); -} - -void fileDeleter(File* file) { - file->deleter(); -} - -void FileCache::initialize() { - QDir dir(_dirpath.c_str()); - - if (dir.exists()) { - auto nameFilters = QStringList(("*." + _ext).c_str()); - auto filters = QDir::Filters(QDir::NoDotAndDotDot | QDir::Files); - auto sort = QDir::SortFlags(QDir::Time); - auto files = dir.entryList(nameFilters, filters, sort); - - // load persisted files - foreach(QString filename, files) { - const Key key = filename.section('.', 0, 1).toStdString(); - const std::string filepath = dir.filePath(filename).toStdString(); - const size_t length = std::ifstream(filepath, std::ios::binary | std::ios::ate).tellg(); - addFile(Metadata(key, length), filepath); - } - - qCDebug(file_cache, "[%s] Initialized %s", _dirname.c_str(), _dirpath.c_str()); - } else { - dir.mkpath(_dirpath.c_str()); - qCDebug(file_cache, "[%s] Created %s", _dirname.c_str(), _dirpath.c_str()); - } - - _initialized = true; -} - -FilePointer FileCache::addFile(Metadata&& metadata, const std::string& filepath) { - FilePointer file(createFile(std::move(metadata), filepath).release(), &fileDeleter); - if (file) { - _numTotalFiles += 1; - _totalFilesSize += file->getLength(); - file->_cache = this; - emit dirty(); - - Lock lock(_filesMutex); - _files[file->getKey()] = file; - } - return file; -} - -FilePointer FileCache::writeFile(const char* data, File::Metadata&& metadata) { - assert(_initialized); - - std::string filepath = getFilepath(metadata.key); - - Lock lock(_filesMutex); - - // if file already exists, return it - FilePointer file = getFile(metadata.key); - if (file) { - qCWarning(file_cache, "[%s] Attempted to overwrite %s", _dirname.c_str(), metadata.key.c_str()); - return file; - } - - // write the new file - FILE* saveFile = fopen(filepath.c_str(), "wb"); - if (saveFile != nullptr && fwrite(data, metadata.length, 1, saveFile) && fclose(saveFile) == 0) { - file = addFile(std::move(metadata), filepath); - } else { - qCWarning(file_cache, "[%s] Failed to write %s (%s)", _dirname.c_str(), metadata.key.c_str(), strerror(errno)); - errno = 0; - } - - return file; -} - -FilePointer FileCache::getFile(const Key& key) { - assert(_initialized); - - FilePointer file; - - Lock lock(_filesMutex); - - // check if file exists - const auto it = _files.find(key); - if (it != _files.cend()) { - file = it->second.lock(); - if (file) { - // if it exists, it is active - remove it from the cache - removeUnusedFile(file); - qCDebug(file_cache, "[%s] Found %s", _dirname.c_str(), key.c_str()); - emit dirty(); - } else { - // if not, remove the weak_ptr - _files.erase(it); - } - } - - return file; -} - -std::string FileCache::getFilepath(const Key& key) { - return _dirpath + '/' + key + '.' + _ext; -} - -void FileCache::addUnusedFile(const FilePointer file) { - { - Lock lock(_filesMutex); - _files[file->getKey()] = file; - } - - reserve(file->getLength()); - file->_LRUKey = ++_lastLRUKey; - - { - Lock lock(_unusedFilesMutex); - _unusedFiles.insert({ file->_LRUKey, file }); - _numUnusedFiles += 1; - _unusedFilesSize += file->getLength(); - } - - emit dirty(); -} - -void FileCache::removeUnusedFile(const FilePointer file) { - Lock lock(_unusedFilesMutex); - const auto it = _unusedFiles.find(file->_LRUKey); - if (it != _unusedFiles.cend()) { - _unusedFiles.erase(it); - _numUnusedFiles -= 1; - _unusedFilesSize -= file->getLength(); - } -} - -void FileCache::reserve(size_t length) { - Lock unusedLock(_unusedFilesMutex); - while (!_unusedFiles.empty() && - _unusedFilesSize + length > _unusedFilesMaxSize) { - auto it = _unusedFiles.begin(); - auto file = it->second; - auto length = file->getLength(); - - unusedLock.unlock(); - { - file->_cache = nullptr; - Lock lock(_filesMutex); - _files.erase(file->getKey()); - } - unusedLock.lock(); - - _unusedFiles.erase(it); - _numTotalFiles -= 1; - _numUnusedFiles -= 1; - _totalFilesSize -= length; - _unusedFilesSize -= length; - } -} - -void FileCache::clear() { - Lock unusedFilesLock(_unusedFilesMutex); - for (const auto& pair : _unusedFiles) { - auto& file = pair.second; - file->_cache = nullptr; - - if (_totalFilesSize > _offlineFilesMaxSize) { - _totalFilesSize -= file->getLength(); - } else { - file->_shouldPersist = true; - qCDebug(file_cache, "[%s] Persisting %s", _dirname.c_str(), file->getKey().c_str()); - } - } - _unusedFiles.clear(); -} - -void File::deleter() { - if (_cache) { - FilePointer self(this, &fileDeleter); - _cache->addUnusedFile(self); - } else { - deleteLater(); - } -} - -File::File(Metadata&& metadata, const std::string& filepath) : - _key(std::move(metadata.key)), - _length(metadata.length), - _filepath(filepath) {} - -File::~File() { - QFile file(getFilepath().c_str()); - if (file.exists() && !_shouldPersist) { - qCInfo(file_cache, "Unlinked %s", getFilepath().c_str()); - file.remove(); - } -} diff --git a/libraries/networking/src/FileCache.h b/libraries/networking/src/FileCache.h deleted file mode 100644 index f77db555bc..0000000000 --- a/libraries/networking/src/FileCache.h +++ /dev/null @@ -1,158 +0,0 @@ -// -// FileCache.h -// libraries/networking/src -// -// Created by Zach Pomerantz on 2/21/2017. -// Copyright 2017 High Fidelity, Inc. // // Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#ifndef hifi_FileCache_h -#define hifi_FileCache_h - -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -Q_DECLARE_LOGGING_CATEGORY(file_cache) - -namespace cache { - -class File; -using FilePointer = std::shared_ptr; - -class FileCache : public QObject { - Q_OBJECT - Q_PROPERTY(size_t numTotal READ getNumTotalFiles NOTIFY dirty) - Q_PROPERTY(size_t numCached READ getNumCachedFiles NOTIFY dirty) - Q_PROPERTY(size_t sizeTotal READ getSizeTotalFiles NOTIFY dirty) - Q_PROPERTY(size_t sizeCached READ getSizeCachedFiles NOTIFY dirty) - - static const size_t DEFAULT_UNUSED_MAX_SIZE; - static const size_t MAX_UNUSED_MAX_SIZE; - static const size_t DEFAULT_OFFLINE_MAX_SIZE; - -public: - size_t getNumTotalFiles() const { return _numTotalFiles; } - size_t getNumCachedFiles() const { return _numUnusedFiles; } - size_t getSizeTotalFiles() const { return _totalFilesSize; } - size_t getSizeCachedFiles() const { return _unusedFilesSize; } - - void setUnusedFileCacheSize(size_t unusedFilesMaxSize); - size_t getUnusedFileCacheSize() const { return _unusedFilesSize; } - - void setOfflineFileCacheSize(size_t offlineFilesMaxSize); - - // initialize FileCache with a directory name (not a path, ex.: "temp_jpgs") and an ext (ex.: "jpg") - FileCache(const std::string& dirname, const std::string& ext, QObject* parent = nullptr); - virtual ~FileCache(); - - using Key = std::string; - struct Metadata { - Metadata(const Key& key, size_t length) : - key(key), length(length) {} - Key key; - size_t length; - }; - - // derived classes should implement a setter/getter, for example, for a FileCache backing a network cache: - // - // DerivedFilePointer writeFile(const char* data, DerivedMetadata&& metadata) { - // return writeFile(data, std::forward(metadata)); - // } - // - // DerivedFilePointer getFile(const QUrl& url) { - // auto key = lookup_hash_for(url); // assuming hashing url in create/evictedFile overrides - // return getFile(key); - // } - -signals: - void dirty(); - -protected: - /// must be called after construction to create the cache on the fs and restore persisted files - void initialize(); - - FilePointer writeFile(const char* data, Metadata&& metadata); - FilePointer getFile(const Key& key); - - /// create a file - virtual std::unique_ptr createFile(Metadata&& metadata, const std::string& filepath) = 0; - -private: - using Mutex = std::recursive_mutex; - using Lock = std::unique_lock; - - friend class File; - - std::string getFilepath(const Key& key); - - FilePointer addFile(Metadata&& metadata, const std::string& filepath); - void addUnusedFile(const FilePointer file); - void removeUnusedFile(const FilePointer file); - void reserve(size_t length); - void clear(); - - std::atomic _numTotalFiles { 0 }; - std::atomic _numUnusedFiles { 0 }; - std::atomic _totalFilesSize { 0 }; - std::atomic _unusedFilesSize { 0 }; - - std::string _ext; - std::string _dirname; - std::string _dirpath; - bool _initialized { false }; - - std::unordered_map> _files; - Mutex _filesMutex; - - std::map _unusedFiles; - Mutex _unusedFilesMutex; - size_t _unusedFilesMaxSize { DEFAULT_UNUSED_MAX_SIZE }; - int _lastLRUKey { 0 }; - - size_t _offlineFilesMaxSize { DEFAULT_OFFLINE_MAX_SIZE }; -}; - -class File : public QObject { - Q_OBJECT - -public: - using Key = FileCache::Key; - using Metadata = FileCache::Metadata; - - Key getKey() const { return _key; } - size_t getLength() const { return _length; } - std::string getFilepath() const { return _filepath; } - - virtual ~File(); - /// overrides should call File::deleter to maintain caching behavior - virtual void deleter(); - -protected: - /// when constructed, the file has already been created/written - File(Metadata&& metadata, const std::string& filepath); - -private: - friend class FileCache; - - const Key _key; - const size_t _length; - const std::string _filepath; - - FileCache* _cache; - int _LRUKey { 0 }; - - bool _shouldPersist { false }; -}; - -} - -#endif // hifi_FileCache_h diff --git a/libraries/networking/src/NodePermissions.h b/libraries/networking/src/NodePermissions.h index 6fa005e360..5d2755f9b5 100644 --- a/libraries/networking/src/NodePermissions.h +++ b/libraries/networking/src/NodePermissions.h @@ -13,31 +13,18 @@ #define hifi_NodePermissions_h #include -#include #include #include #include #include -#include -#include + #include "GroupRank.h" class NodePermissions; using NodePermissionsPointer = std::shared_ptr; -using NodePermissionsKey = std::pair; // name, rankID +using NodePermissionsKey = QPair; // name, rankID using NodePermissionsKeyList = QList>; -namespace std { - template<> - struct hash { - size_t operator()(const NodePermissionsKey& key) const { - size_t result = qHash(key.first); - result <<= 32; - result |= qHash(key.second); - return result; - } - }; -} class NodePermissions { public: @@ -113,40 +100,27 @@ public: NodePermissionsMap() { } NodePermissionsPointer& operator[](const NodePermissionsKey& key) { NodePermissionsKey dataKey(key.first.toLower(), key.second); - if (0 == _data.count(dataKey)) { + if (!_data.contains(dataKey)) { _data[dataKey] = NodePermissionsPointer(new NodePermissions(key)); } return _data[dataKey]; } NodePermissionsPointer operator[](const NodePermissionsKey& key) const { - NodePermissionsPointer result; - auto itr = _data.find(NodePermissionsKey(key.first.toLower(), key.second)); - if (_data.end() != itr) { - result = itr->second; - } - return result; + return _data.value(NodePermissionsKey(key.first.toLower(), key.second)); } bool contains(const NodePermissionsKey& key) const { - return 0 != _data.count(NodePermissionsKey(key.first.toLower(), key.second)); + return _data.contains(NodePermissionsKey(key.first.toLower(), key.second)); } - bool contains(const QString& keyFirst, const QUuid& keySecond) const { - return 0 != _data.count(NodePermissionsKey(keyFirst.toLower(), keySecond)); + bool contains(const QString& keyFirst, QUuid keySecond) const { + return _data.contains(NodePermissionsKey(keyFirst.toLower(), keySecond)); } - - QList keys() const { - QList result; - for (const auto& entry : _data) { - result.push_back(entry.first); - } - return result; - } - - const std::unordered_map& get() { return _data; } + QList keys() const { return _data.keys(); } + QHash get() { return _data; } void clear() { _data.clear(); } - void remove(const NodePermissionsKey& key) { _data.erase(key); } + void remove(const NodePermissionsKey& key) { _data.remove(key); } private: - std::unordered_map _data; + QHash _data; }; diff --git a/libraries/networking/src/udt/PacketQueue.cpp b/libraries/networking/src/udt/PacketQueue.cpp index 9560f2f187..bb20982ca4 100644 --- a/libraries/networking/src/udt/PacketQueue.cpp +++ b/libraries/networking/src/udt/PacketQueue.cpp @@ -15,10 +15,6 @@ using namespace udt; -PacketQueue::PacketQueue() { - _channels.emplace_back(new std::list()); -} - MessageNumber PacketQueue::getNextMessageNumber() { static const MessageNumber MAX_MESSAGE_NUMBER = MessageNumber(1) << MESSAGE_NUMBER_SIZE; _currentMessageNumber = (_currentMessageNumber + 1) % MAX_MESSAGE_NUMBER; @@ -28,7 +24,7 @@ MessageNumber PacketQueue::getNextMessageNumber() { bool PacketQueue::isEmpty() const { LockGuard locker(_packetsLock); // Only the main channel and it is empty - return (_channels.size() == 1) && _channels.front()->empty(); + return (_channels.size() == 1) && _channels.front().empty(); } PacketQueue::PacketPointer PacketQueue::takePacket() { @@ -38,19 +34,19 @@ PacketQueue::PacketPointer PacketQueue::takePacket() { } // Find next non empty channel - if (_channels[nextIndex()]->empty()) { + if (_channels[nextIndex()].empty()) { nextIndex(); } auto& channel = _channels[_currentIndex]; - Q_ASSERT(!channel->empty()); + Q_ASSERT(!channel.empty()); // Take front packet - auto packet = std::move(channel->front()); - channel->pop_front(); + auto packet = std::move(channel.front()); + channel.pop_front(); // Remove now empty channel (Don't remove the main channel) - if (channel->empty() && _currentIndex != 0) { - channel->swap(*_channels.back()); + if (channel.empty() && _currentIndex != 0) { + channel.swap(_channels.back()); _channels.pop_back(); --_currentIndex; } @@ -65,7 +61,7 @@ unsigned int PacketQueue::nextIndex() { void PacketQueue::queuePacket(PacketPointer packet) { LockGuard locker(_packetsLock); - _channels.front()->push_back(std::move(packet)); + _channels.front().push_back(std::move(packet)); } void PacketQueue::queuePacketList(PacketListPointer packetList) { @@ -74,6 +70,5 @@ void PacketQueue::queuePacketList(PacketListPointer packetList) { } LockGuard locker(_packetsLock); - _channels.emplace_back(new std::list()); - _channels.back()->swap(packetList->_packets); + _channels.push_back(std::move(packetList->_packets)); } diff --git a/libraries/networking/src/udt/PacketQueue.h b/libraries/networking/src/udt/PacketQueue.h index 2b3d3a4b5b..69784fd8db 100644 --- a/libraries/networking/src/udt/PacketQueue.h +++ b/libraries/networking/src/udt/PacketQueue.h @@ -30,11 +30,10 @@ class PacketQueue { using LockGuard = std::lock_guard; using PacketPointer = std::unique_ptr; using PacketListPointer = std::unique_ptr; - using Channel = std::unique_ptr>; + using Channel = std::list; using Channels = std::vector; public: - PacketQueue(); void queuePacket(PacketPointer packet); void queuePacketList(PacketListPointer packetList); @@ -50,7 +49,7 @@ private: MessageNumber _currentMessageNumber { 0 }; mutable Mutex _packetsLock; // Protects the packets to be sent. - Channels _channels; // One channel per packet list + Main channel + Channels _channels = Channels(1); // One channel per packet list + Main channel unsigned int _currentIndex { 0 }; }; diff --git a/libraries/octree/src/OctreeQuery.cpp b/libraries/octree/src/OctreeQuery.cpp index 7d9fc7d08c..a639eccaba 100644 --- a/libraries/octree/src/OctreeQuery.cpp +++ b/libraries/octree/src/OctreeQuery.cpp @@ -142,6 +142,6 @@ int OctreeQuery::parseData(ReceivedMessage& message) { } glm::vec3 OctreeQuery::calculateCameraDirection() const { - glm::vec3 direction = glm::vec3(_cameraOrientation * glm::vec4(IDENTITY_FORWARD, 0.0f)); + glm::vec3 direction = glm::vec3(_cameraOrientation * glm::vec4(IDENTITY_FRONT, 0.0f)); return direction; } diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp index d383f4c199..c175a836cc 100644 --- a/libraries/physics/src/EntityMotionState.cpp +++ b/libraries/physics/src/EntityMotionState.cpp @@ -97,21 +97,6 @@ void EntityMotionState::updateServerPhysicsVariables() { _serverActionData = _entity->getActionData(); } -void EntityMotionState::handleDeactivation() { - // copy _server data to entity - bool success; - _entity->setPosition(_serverPosition, success, false); - _entity->setOrientation(_serverRotation, success, false); - _entity->setVelocity(ENTITY_ITEM_ZERO_VEC3); - _entity->setAngularVelocity(ENTITY_ITEM_ZERO_VEC3); - // and also to RigidBody - btTransform worldTrans; - worldTrans.setOrigin(glmToBullet(_serverPosition)); - worldTrans.setRotation(glmToBullet(_serverRotation)); - _body->setWorldTransform(worldTrans); - // no need to update velocities... should already be zero -} - // virtual void EntityMotionState::handleEasyChanges(uint32_t& flags) { assert(entityTreeIsLocked()); @@ -126,8 +111,6 @@ void EntityMotionState::handleEasyChanges(uint32_t& flags) { flags &= ~Simulation::DIRTY_PHYSICS_ACTIVATION; _body->setActivationState(WANTS_DEACTIVATION); _outgoingPriority = 0; - const float ACTIVATION_EXPIRY = 3.0f; // something larger than the 2.0 hard coded in Bullet - _body->setDeactivationTime(ACTIVATION_EXPIRY); } else { // disowned object is still moving --> start timer for ownership bid // TODO? put a delay in here proportional to distance from object? @@ -238,9 +221,12 @@ void EntityMotionState::getWorldTransform(btTransform& worldTrans) const { } // This callback is invoked by the physics simulation at the end of each simulation step... -// iff the corresponding RigidBody is DYNAMIC and ACTIVE. +// iff the corresponding RigidBody is DYNAMIC and has moved. void EntityMotionState::setWorldTransform(const btTransform& worldTrans) { - assert(_entity); + if (!_entity) { + return; + } + assert(entityTreeIsLocked()); measureBodyAcceleration(); bool positionSuccess; diff --git a/libraries/physics/src/EntityMotionState.h b/libraries/physics/src/EntityMotionState.h index 380edf3927..feac47d8ec 100644 --- a/libraries/physics/src/EntityMotionState.h +++ b/libraries/physics/src/EntityMotionState.h @@ -29,7 +29,6 @@ public: virtual ~EntityMotionState(); void updateServerPhysicsVariables(); - void handleDeactivation(); virtual void handleEasyChanges(uint32_t& flags) override; virtual bool handleHardAndEasyChanges(uint32_t& flags, PhysicsEngine* engine) override; diff --git a/libraries/physics/src/PhysicalEntitySimulation.cpp b/libraries/physics/src/PhysicalEntitySimulation.cpp index bd76b2d70f..903b160a5e 100644 --- a/libraries/physics/src/PhysicalEntitySimulation.cpp +++ b/libraries/physics/src/PhysicalEntitySimulation.cpp @@ -259,27 +259,13 @@ void PhysicalEntitySimulation::getObjectsToChange(VectorOfMotionStates& result) _pendingChanges.clear(); } -void PhysicalEntitySimulation::handleDeactivatedMotionStates(const VectorOfMotionStates& motionStates) { - for (auto stateItr : motionStates) { - ObjectMotionState* state = &(*stateItr); - assert(state); - if (state->getType() == MOTIONSTATE_TYPE_ENTITY) { - EntityMotionState* entityState = static_cast(state); - entityState->handleDeactivation(); - EntityItemPointer entity = entityState->getEntity(); - _entitiesToSort.insert(entity); - } - } -} - -void PhysicalEntitySimulation::handleChangedMotionStates(const VectorOfMotionStates& motionStates) { +void PhysicalEntitySimulation::handleOutgoingChanges(const VectorOfMotionStates& motionStates) { QMutexLocker lock(&_mutex); // walk the motionStates looking for those that correspond to entities for (auto stateItr : motionStates) { ObjectMotionState* state = &(*stateItr); - assert(state); - if (state->getType() == MOTIONSTATE_TYPE_ENTITY) { + if (state && state->getType() == MOTIONSTATE_TYPE_ENTITY) { EntityMotionState* entityState = static_cast(state); EntityItemPointer entity = entityState->getEntity(); assert(entity.get()); diff --git a/libraries/physics/src/PhysicalEntitySimulation.h b/libraries/physics/src/PhysicalEntitySimulation.h index 5f6185add3..af5def9775 100644 --- a/libraries/physics/src/PhysicalEntitySimulation.h +++ b/libraries/physics/src/PhysicalEntitySimulation.h @@ -56,8 +56,7 @@ public: void setObjectsToChange(const VectorOfMotionStates& objectsToChange); void getObjectsToChange(VectorOfMotionStates& result); - void handleDeactivatedMotionStates(const VectorOfMotionStates& motionStates); - void handleChangedMotionStates(const VectorOfMotionStates& motionStates); + void handleOutgoingChanges(const VectorOfMotionStates& motionStates); void handleCollisionEvents(const CollisionEvents& collisionEvents); EntityEditPacketSender* getPacketSender() { return _entityPacketSender; } @@ -68,7 +67,7 @@ private: SetOfEntities _entitiesToAddToPhysics; SetOfEntityMotionStates _pendingChanges; // EntityMotionStates already in PhysicsEngine that need their physics changed - SetOfEntityMotionStates _outgoingChanges; // EntityMotionStates for which we may need to send updates to entity-server + SetOfEntityMotionStates _outgoingChanges; // EntityMotionStates for which we need to send updates to entity-server SetOfMotionStates _physicalObjects; // MotionStates of entities in PhysicsEngine diff --git a/libraries/physics/src/PhysicsEngine.cpp b/libraries/physics/src/PhysicsEngine.cpp index a8a8e6acfd..363887de25 100644 --- a/libraries/physics/src/PhysicsEngine.cpp +++ b/libraries/physics/src/PhysicsEngine.cpp @@ -472,7 +472,7 @@ const CollisionEvents& PhysicsEngine::getCollisionEvents() { return _collisionEvents; } -const VectorOfMotionStates& PhysicsEngine::getChangedMotionStates() { +const VectorOfMotionStates& PhysicsEngine::getOutgoingChanges() { BT_PROFILE("copyOutgoingChanges"); // Bullet will not deactivate static objects (it doesn't expect them to be active) // so we must deactivate them ourselves diff --git a/libraries/physics/src/PhysicsEngine.h b/libraries/physics/src/PhysicsEngine.h index b2ebe58f08..bbafbb06b6 100644 --- a/libraries/physics/src/PhysicsEngine.h +++ b/libraries/physics/src/PhysicsEngine.h @@ -65,8 +65,7 @@ public: bool hasOutgoingChanges() const { return _hasOutgoingChanges; } /// \return reference to list of changed MotionStates. The list is only valid until beginning of next simulation loop. - const VectorOfMotionStates& getChangedMotionStates(); - const VectorOfMotionStates& getDeactivatedMotionStates() const { return _dynamicsWorld->getDeactivatedMotionStates(); } + const VectorOfMotionStates& getOutgoingChanges(); /// \return reference to list of Collision events. The list is only valid until beginning of next simulation loop. const CollisionEvents& getCollisionEvents(); diff --git a/libraries/physics/src/ThreadSafeDynamicsWorld.cpp b/libraries/physics/src/ThreadSafeDynamicsWorld.cpp index 24cfbc2609..5fe99f137c 100644 --- a/libraries/physics/src/ThreadSafeDynamicsWorld.cpp +++ b/libraries/physics/src/ThreadSafeDynamicsWorld.cpp @@ -120,41 +120,30 @@ void ThreadSafeDynamicsWorld::synchronizeMotionState(btRigidBody* body) { void ThreadSafeDynamicsWorld::synchronizeMotionStates() { BT_PROFILE("synchronizeMotionStates"); _changedMotionStates.clear(); - - // NOTE: m_synchronizeAllMotionStates is 'false' by default for optimization. - // See PhysicsEngine::init() where we call _dynamicsWorld->setForceUpdateAllAabbs(false) if (m_synchronizeAllMotionStates) { //iterate over all collision objects for (int i=0;igetMotionState()) { - synchronizeMotionState(body); - _changedMotionStates.push_back(static_cast(body->getMotionState())); + if (body) { + if (body->getMotionState()) { + synchronizeMotionState(body); + _changedMotionStates.push_back(static_cast(body->getMotionState())); + } } } } else { //iterate over all active rigid bodies - // TODO? if this becomes a performance bottleneck we could derive our own SimulationIslandManager - // that remembers a list of objects deactivated last step - _activeStates.clear(); - _deactivatedStates.clear(); for (int i=0;i(body->getMotionState()); - if (motionState) { - if (body->isActive()) { + if (body->isActive()) { + if (body->getMotionState()) { synchronizeMotionState(body); - _changedMotionStates.push_back(motionState); - _activeStates.insert(motionState); - } else if (_lastActiveStates.find(motionState) != _lastActiveStates.end()) { - // this object was active last frame but is no longer - _deactivatedStates.push_back(motionState); + _changedMotionStates.push_back(static_cast(body->getMotionState())); } } } } - _activeStates.swap(_lastActiveStates); } void ThreadSafeDynamicsWorld::saveKinematicState(btScalar timeStep) { diff --git a/libraries/physics/src/ThreadSafeDynamicsWorld.h b/libraries/physics/src/ThreadSafeDynamicsWorld.h index b4fcca8cdb..68062d8d29 100644 --- a/libraries/physics/src/ThreadSafeDynamicsWorld.h +++ b/libraries/physics/src/ThreadSafeDynamicsWorld.h @@ -49,16 +49,12 @@ public: float getLocalTimeAccumulation() const { return m_localTime; } const VectorOfMotionStates& getChangedMotionStates() const { return _changedMotionStates; } - const VectorOfMotionStates& getDeactivatedMotionStates() const { return _deactivatedStates; } private: // call this instead of non-virtual btDiscreteDynamicsWorld::synchronizeSingleMotionState() void synchronizeMotionState(btRigidBody* body); VectorOfMotionStates _changedMotionStates; - VectorOfMotionStates _deactivatedStates; - SetOfMotionStates _activeStates; - SetOfMotionStates _lastActiveStates; }; #endif // hifi_ThreadSafeDynamicsWorld_h diff --git a/libraries/recording/src/recording/Deck.cpp b/libraries/recording/src/recording/Deck.cpp index 186516e01c..61eb86c91f 100644 --- a/libraries/recording/src/recording/Deck.cpp +++ b/libraries/recording/src/recording/Deck.cpp @@ -33,7 +33,6 @@ void Deck::queueClip(ClipPointer clip, float timeOffset) { // FIXME disabling multiple clips for now _clips.clear(); - _length = 0.0f; // if the time offset is not zero, wrap in an OffsetClip if (timeOffset != 0.0f) { @@ -154,8 +153,8 @@ void Deck::processFrames() { // if doing relative movement emit looped(); } else { - // otherwise stop playback - stop(); + // otherwise pause playback + pause(); } return; } diff --git a/libraries/render-utils/CMakeLists.txt b/libraries/render-utils/CMakeLists.txt index 3bf389973a..ecafb8f565 100644 --- a/libraries/render-utils/CMakeLists.txt +++ b/libraries/render-utils/CMakeLists.txt @@ -3,7 +3,7 @@ AUTOSCRIBE_SHADER_LIB(gpu model render) # pull in the resources.qrc file 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) +link_hifi_libraries(shared gpu model model-networking render animation fbx entities) if (NOT ANDROID) target_nsight() diff --git a/libraries/render-utils/src/AntialiasingEffect.cpp b/libraries/render-utils/src/AntialiasingEffect.cpp index f95d45de04..2941197e6d 100644 --- a/libraries/render-utils/src/AntialiasingEffect.cpp +++ b/libraries/render-utils/src/AntialiasingEffect.cpp @@ -52,7 +52,7 @@ const gpu::PipelinePointer& Antialiasing::getAntialiasingPipeline() { _antialiasingBuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("antialiasing")); auto format = gpu::Element::COLOR_SRGBA_32; // DependencyManager::get()->getLightingTexture()->getTexelFormat(); auto defaultSampler = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_POINT); - _antialiasingTexture = gpu::TexturePointer(gpu::Texture::createRenderBuffer(format, width, height, defaultSampler)); + _antialiasingTexture = gpu::TexturePointer(gpu::Texture::create2D(format, width, height, defaultSampler)); _antialiasingBuffer->setRenderBuffer(0, _antialiasingTexture); } diff --git a/libraries/render-utils/src/DeferredFramebuffer.cpp b/libraries/render-utils/src/DeferredFramebuffer.cpp index 40c22beba4..e8783e0e0d 100644 --- a/libraries/render-utils/src/DeferredFramebuffer.cpp +++ b/libraries/render-utils/src/DeferredFramebuffer.cpp @@ -53,9 +53,9 @@ void DeferredFramebuffer::allocate() { auto defaultSampler = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_POINT); - _deferredColorTexture = gpu::TexturePointer(gpu::Texture::createRenderBuffer(colorFormat, width, height, defaultSampler)); - _deferredNormalTexture = gpu::TexturePointer(gpu::Texture::createRenderBuffer(linearFormat, width, height, defaultSampler)); - _deferredSpecularTexture = gpu::TexturePointer(gpu::Texture::createRenderBuffer(colorFormat, width, height, defaultSampler)); + _deferredColorTexture = gpu::TexturePointer(gpu::Texture::create2D(colorFormat, width, height, defaultSampler)); + _deferredNormalTexture = gpu::TexturePointer(gpu::Texture::create2D(linearFormat, width, height, defaultSampler)); + _deferredSpecularTexture = gpu::TexturePointer(gpu::Texture::create2D(colorFormat, width, height, defaultSampler)); _deferredFramebuffer->setRenderBuffer(0, _deferredColorTexture); _deferredFramebuffer->setRenderBuffer(1, _deferredNormalTexture); @@ -65,7 +65,7 @@ void DeferredFramebuffer::allocate() { auto depthFormat = gpu::Element(gpu::SCALAR, gpu::UINT32, gpu::DEPTH_STENCIL); // Depth24_Stencil8 texel format if (!_primaryDepthTexture) { - _primaryDepthTexture = gpu::TexturePointer(gpu::Texture::createRenderBuffer(depthFormat, width, height, defaultSampler)); + _primaryDepthTexture = gpu::TexturePointer(gpu::Texture::create2D(depthFormat, width, height, defaultSampler)); } _deferredFramebuffer->setDepthStencilBuffer(_primaryDepthTexture, depthFormat); @@ -75,7 +75,7 @@ void DeferredFramebuffer::allocate() { auto smoothSampler = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR); - _lightingTexture = gpu::TexturePointer(gpu::Texture::createRenderBuffer(gpu::Element(gpu::SCALAR, gpu::FLOAT, gpu::R11G11B10), width, height, defaultSampler)); + _lightingTexture = gpu::TexturePointer(gpu::Texture::create2D(gpu::Element(gpu::SCALAR, gpu::FLOAT, gpu::R11G11B10), width, height, defaultSampler)); _lightingFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("lighting")); _lightingFramebuffer->setRenderBuffer(0, _lightingTexture); _lightingFramebuffer->setDepthStencilBuffer(_primaryDepthTexture, depthFormat); diff --git a/libraries/render-utils/src/DeferredLightingEffect.cpp b/libraries/render-utils/src/DeferredLightingEffect.cpp index ce340583ee..6f1152ac16 100644 --- a/libraries/render-utils/src/DeferredLightingEffect.cpp +++ b/libraries/render-utils/src/DeferredLightingEffect.cpp @@ -496,14 +496,14 @@ void PreparePrimaryFramebuffer::run(const SceneContextPointer& sceneContext, con auto colorFormat = gpu::Element::COLOR_SRGBA_32; auto defaultSampler = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_POINT); - auto primaryColorTexture = gpu::TexturePointer(gpu::Texture::createRenderBuffer(colorFormat, frameSize.x, frameSize.y, defaultSampler)); + auto primaryColorTexture = gpu::TexturePointer(gpu::Texture::create2D(colorFormat, frameSize.x, frameSize.y, defaultSampler)); _primaryFramebuffer->setRenderBuffer(0, primaryColorTexture); auto depthFormat = gpu::Element(gpu::SCALAR, gpu::UINT32, gpu::DEPTH_STENCIL); // Depth24_Stencil8 texel format - auto primaryDepthTexture = gpu::TexturePointer(gpu::Texture::createRenderBuffer(depthFormat, frameSize.x, frameSize.y, defaultSampler)); + auto primaryDepthTexture = gpu::TexturePointer(gpu::Texture::create2D(depthFormat, frameSize.x, frameSize.y, defaultSampler)); _primaryFramebuffer->setDepthStencilBuffer(primaryDepthTexture, depthFormat); } diff --git a/libraries/render-utils/src/FramebufferCache.cpp b/libraries/render-utils/src/FramebufferCache.cpp index 72b3c2ceb4..27429595b4 100644 --- a/libraries/render-utils/src/FramebufferCache.cpp +++ b/libraries/render-utils/src/FramebufferCache.cpp @@ -21,6 +21,7 @@ void FramebufferCache::setFrameBufferSize(QSize frameBufferSize) { //If the size changed, we need to delete our FBOs if (_frameBufferSize != frameBufferSize) { _frameBufferSize = frameBufferSize; + _selfieFramebuffer.reset(); { std::unique_lock lock(_mutex); _cachedFramebuffers.clear(); @@ -29,8 +30,16 @@ void FramebufferCache::setFrameBufferSize(QSize frameBufferSize) { } void FramebufferCache::createPrimaryFramebuffer() { + auto colorFormat = gpu::Element::COLOR_SRGBA_32; + auto width = _frameBufferSize.width(); + auto height = _frameBufferSize.height(); + auto defaultSampler = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_POINT); + _selfieFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("selfie")); + auto tex = gpu::TexturePointer(gpu::Texture::create2D(colorFormat, width * 0.5, height * 0.5, defaultSampler)); + _selfieFramebuffer->setRenderBuffer(0, tex); + auto smoothSampler = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR); } @@ -51,3 +60,10 @@ void FramebufferCache::releaseFramebuffer(const gpu::FramebufferPointer& framebu _cachedFramebuffers.push_back(framebuffer); } } + +gpu::FramebufferPointer FramebufferCache::getSelfieFramebuffer() { + if (!_selfieFramebuffer) { + createPrimaryFramebuffer(); + } + return _selfieFramebuffer; +} diff --git a/libraries/render-utils/src/FramebufferCache.h b/libraries/render-utils/src/FramebufferCache.h index 8065357615..f74d224a61 100644 --- a/libraries/render-utils/src/FramebufferCache.h +++ b/libraries/render-utils/src/FramebufferCache.h @@ -27,6 +27,9 @@ public: void setFrameBufferSize(QSize frameBufferSize); const QSize& getFrameBufferSize() const { return _frameBufferSize; } + /// Returns the framebuffer object used to render selfie maps; + gpu::FramebufferPointer getSelfieFramebuffer(); + /// Returns a free framebuffer with a single color attachment for temp or intra-frame operations gpu::FramebufferPointer getFramebuffer(); @@ -39,6 +42,8 @@ private: gpu::FramebufferPointer _shadowFramebuffer; + gpu::FramebufferPointer _selfieFramebuffer; + QSize _frameBufferSize{ 100, 100 }; std::mutex _mutex; diff --git a/libraries/render-utils/src/LightAmbient.slh b/libraries/render-utils/src/LightAmbient.slh index e343d8c239..15e23015cb 100644 --- a/libraries/render-utils/src/LightAmbient.slh +++ b/libraries/render-utils/src/LightAmbient.slh @@ -30,8 +30,9 @@ vec3 fresnelSchlickAmbient(vec3 fresnelColor, vec3 lightDir, vec3 halfDir, float <$declareSkyboxMap()$> <@endif@> -vec3 evalAmbientSpecularIrradiance(LightAmbient ambient, vec3 fragEyeDir, vec3 fragNormal, float roughness) { +vec3 evalAmbientSpecularIrradiance(LightAmbient ambient, vec3 fragEyeDir, vec3 fragNormal, float roughness, vec3 fresnel) { vec3 direction = -reflect(fragEyeDir, fragNormal); + vec3 ambientFresnel = fresnelSchlickAmbient(fresnel, fragEyeDir, fragNormal, 1.0 - roughness); vec3 specularLight; <@if supportIfAmbientMapElseAmbientSphere@> if (getLightHasAmbientMap(ambient)) @@ -52,7 +53,7 @@ vec3 evalAmbientSpecularIrradiance(LightAmbient ambient, vec3 fragEyeDir, vec3 f } <@endif@> - return specularLight; + return specularLight * ambientFresnel; } <@endfunc@> @@ -73,14 +74,12 @@ void evalLightingAmbient(out vec3 diffuse, out vec3 specular, LightAmbient ambie <@endif@> ) { - // Fresnel - vec3 ambientFresnel = fresnelSchlickAmbient(fresnel, eyeDir, normal, 1.0 - roughness); - // Diffuse from ambient - diffuse = (1.0 - metallic) * (vec3(1.0) - ambientFresnel) * sphericalHarmonics_evalSphericalLight(getLightAmbientSphere(ambient), normal).xyz; + diffuse = (1.0 - metallic) * sphericalHarmonics_evalSphericalLight(getLightAmbientSphere(ambient), normal).xyz; // Specular highlight from ambient - specular = evalAmbientSpecularIrradiance(ambient, eyeDir, normal, roughness) * ambientFresnel; + specular = evalAmbientSpecularIrradiance(ambient, eyeDir, normal, roughness, fresnel) * obscurance * getLightAmbientIntensity(ambient); + <@if supportScattering@> float ambientOcclusion = curvatureAO(lowNormalCurvature.w * 20.0f) * 0.5f; diff --git a/libraries/render-utils/src/LightStage.cpp b/libraries/render-utils/src/LightStage.cpp index dd6a046dea..66a9797d3c 100644 --- a/libraries/render-utils/src/LightStage.cpp +++ b/libraries/render-utils/src/LightStage.cpp @@ -27,9 +27,9 @@ void LightStage::Shadow::setKeylightFrustum(const ViewFrustum& viewFrustum, floa const auto& direction = glm::normalize(_light->getDirection()); glm::quat orientation; if (direction == IDENTITY_UP) { - orientation = glm::quat(glm::mat3(-IDENTITY_RIGHT, IDENTITY_FORWARD, -IDENTITY_UP)); + orientation = glm::quat(glm::mat3(-IDENTITY_RIGHT, IDENTITY_FRONT, -IDENTITY_UP)); } else if (direction == -IDENTITY_UP) { - orientation = glm::quat(glm::mat3(IDENTITY_RIGHT, IDENTITY_FORWARD, IDENTITY_UP)); + orientation = glm::quat(glm::mat3(IDENTITY_RIGHT, IDENTITY_FRONT, IDENTITY_UP)); } else { auto side = glm::normalize(glm::cross(direction, IDENTITY_UP)); auto up = glm::normalize(glm::cross(side, direction)); diff --git a/libraries/render-utils/src/LightingModel.cpp b/libraries/render-utils/src/LightingModel.cpp index bd321bad95..47af83da36 100644 --- a/libraries/render-utils/src/LightingModel.cpp +++ b/libraries/render-utils/src/LightingModel.cpp @@ -133,7 +133,6 @@ void LightingModel::setSpotLight(bool enable) { bool LightingModel::isSpotLightEnabled() const { return (bool)_parametersBuffer.get().enableSpotLight; } - void LightingModel::setShowLightContour(bool enable) { if (enable != isShowLightContourEnabled()) { _parametersBuffer.edit().showLightContour = (float)enable; @@ -143,14 +142,6 @@ bool LightingModel::isShowLightContourEnabled() const { return (bool)_parametersBuffer.get().showLightContour; } -void LightingModel::setWireframe(bool enable) { - if (enable != isWireframeEnabled()) { - _parametersBuffer.edit().enableWireframe = (float)enable; - } -} -bool LightingModel::isWireframeEnabled() const { - return (bool)_parametersBuffer.get().enableWireframe; -} MakeLightingModel::MakeLightingModel() { _lightingModel = std::make_shared(); } @@ -176,7 +167,6 @@ void MakeLightingModel::configure(const Config& config) { _lightingModel->setSpotLight(config.enableSpotLight); _lightingModel->setShowLightContour(config.showLightContour); - _lightingModel->setWireframe(config.enableWireframe); } void MakeLightingModel::run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext, LightingModelPointer& lightingModel) { diff --git a/libraries/render-utils/src/LightingModel.h b/libraries/render-utils/src/LightingModel.h index c1189d5160..45514654f2 100644 --- a/libraries/render-utils/src/LightingModel.h +++ b/libraries/render-utils/src/LightingModel.h @@ -64,9 +64,6 @@ public: void setShowLightContour(bool enable); bool isShowLightContourEnabled() const; - void setWireframe(bool enable); - bool isWireframeEnabled() const; - UniformBufferView getParametersBuffer() const { return _parametersBuffer; } protected: @@ -92,12 +89,13 @@ protected: float enablePointLight{ 1.0f }; float enableSpotLight{ 1.0f }; - float showLightContour { 0.0f }; // false by default + float showLightContour{ 0.0f }; // false by default float enableObscurance{ 1.0f }; float enableMaterialTexturing { 1.0f }; - float enableWireframe { 0.0f }; // false by default + + float spares{ 0.0f }; Parameters() {} }; @@ -131,7 +129,6 @@ class MakeLightingModelConfig : public render::Job::Config { Q_PROPERTY(bool enablePointLight MEMBER enablePointLight NOTIFY dirty) Q_PROPERTY(bool enableSpotLight MEMBER enableSpotLight NOTIFY dirty) - Q_PROPERTY(bool enableWireframe MEMBER enableWireframe NOTIFY dirty) Q_PROPERTY(bool showLightContour MEMBER showLightContour NOTIFY dirty) public: @@ -155,9 +152,8 @@ public: bool enablePointLight{ true }; bool enableSpotLight{ true }; - bool showLightContour { false }; // false by default - bool enableWireframe { false }; // false by default + bool showLightContour { false }; // false by default signals: void dirty(); diff --git a/libraries/render-utils/src/LightingModel.slh b/libraries/render-utils/src/LightingModel.slh index 209a1f38d6..74285aa6a9 100644 --- a/libraries/render-utils/src/LightingModel.slh +++ b/libraries/render-utils/src/LightingModel.slh @@ -17,7 +17,7 @@ struct LightingModel { vec4 _UnlitEmissiveLightmapBackground; vec4 _ScatteringDiffuseSpecularAlbedo; vec4 _AmbientDirectionalPointSpot; - vec4 _ShowContourObscuranceWireframe; + vec4 _ShowContourObscuranceSpare2; }; uniform lightingModelBuffer{ @@ -37,7 +37,7 @@ float isBackgroundEnabled() { return lightingModel._UnlitEmissiveLightmapBackground.w; } float isObscuranceEnabled() { - return lightingModel._ShowContourObscuranceWireframe.y; + return lightingModel._ShowContourObscuranceSpare2.y; } float isScatteringEnabled() { @@ -67,12 +67,9 @@ float isSpotEnabled() { } float isShowLightContour() { - return lightingModel._ShowContourObscuranceWireframe.x; + return lightingModel._ShowContourObscuranceSpare2.x; } -float isWireframeEnabled() { - return lightingModel._ShowContourObscuranceWireframe.z; -} <@endfunc@> <$declareLightingModel()$> diff --git a/libraries/render-utils/src/MaterialTextures.slh b/libraries/render-utils/src/MaterialTextures.slh index 7b73896cc5..6d2ad23c21 100644 --- a/libraries/render-utils/src/MaterialTextures.slh +++ b/libraries/render-utils/src/MaterialTextures.slh @@ -64,7 +64,7 @@ float fetchRoughnessMap(vec2 uv) { uniform sampler2D normalMap; vec3 fetchNormalMap(vec2 uv) { // unpack normal, swizzle to get into hifi tangent space with Y axis pointing out - return normalize(texture(normalMap, uv).rbg -vec3(0.5, 0.5, 0.5)); + return normalize(texture(normalMap, uv).xzy -vec3(0.5, 0.5, 0.5)); } <@endif@> diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp index 41a1bb4c74..5b3d285b47 100644 --- a/libraries/render-utils/src/MeshPartPayload.cpp +++ b/libraries/render-utils/src/MeshPartPayload.cpp @@ -372,12 +372,19 @@ void ModelMeshPartPayload::notifyLocationChanged() { } -void ModelMeshPartPayload::updateTransformForSkinnedMesh(const Transform& renderTransform, const Transform& boundTransform, - const gpu::BufferPointer& buffer) { - _transform = renderTransform; - _worldBound = _adjustedLocalBound; - _worldBound.transform(boundTransform); - _clusterBuffer = buffer; +void ModelMeshPartPayload::updateTransformForSkinnedMesh(const Transform& transform, const QVector& clusterMatrices) { + _transform = transform; + + if (clusterMatrices.size() > 0) { + _worldBound = _adjustedLocalBound; + _worldBound.transform(_transform); + if (clusterMatrices.size() == 1) { + _transform = _transform.worldTransform(Transform(clusterMatrices[0])); + } + } else { + _worldBound = _localBound; + _worldBound.transform(_transform); + } } ItemKey ModelMeshPartPayload::getKey() const { @@ -525,8 +532,9 @@ void ModelMeshPartPayload::bindMesh(gpu::Batch& batch) const { void ModelMeshPartPayload::bindTransform(gpu::Batch& batch, const ShapePipeline::LocationsPointer locations, RenderArgs::RenderMode renderMode) const { // Still relying on the raw data from the model - if (_clusterBuffer) { - batch.setUniformBuffer(ShapePipeline::Slot::BUFFER::SKINNING, _clusterBuffer); + const Model::MeshState& state = _model->getMeshState(_meshIndex); + if (state.clusterBuffer) { + batch.setUniformBuffer(ShapePipeline::Slot::BUFFER::SKINNING, state.clusterBuffer); } batch.setModelTransform(_transform); } @@ -582,6 +590,8 @@ void ModelMeshPartPayload::render(RenderArgs* args) const { auto locations = args->_pipeline->locations; assert(locations); + // Bind the model transform and the skinCLusterMatrices if needed + _model->updateClusterMatrices(); bindTransform(batch, locations, args->_renderMode); //Bind the index buffer and vertex buffer and Blend shapes if needed diff --git a/libraries/render-utils/src/MeshPartPayload.h b/libraries/render-utils/src/MeshPartPayload.h index ef74011c40..c585c95025 100644 --- a/libraries/render-utils/src/MeshPartPayload.h +++ b/libraries/render-utils/src/MeshPartPayload.h @@ -89,9 +89,8 @@ public: typedef Payload::DataPointer Pointer; void notifyLocationChanged() override; - void updateTransformForSkinnedMesh(const Transform& renderTransform, - const Transform& boundTransform, - const gpu::BufferPointer& buffer); + void updateTransformForSkinnedMesh(const Transform& transform, + const QVector& clusterMatrices); float computeFadeAlpha() const; @@ -109,7 +108,6 @@ public: void computeAdjustedLocalBound(const QVector& clusterMatrices); - gpu::BufferPointer _clusterBuffer; Model* _model; int _meshIndex; diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 3448c9e8da..48c1d29b68 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -176,11 +176,11 @@ void Model::setOffset(const glm::vec3& offset) { } void Model::calculateTextureInfo() { - if (!_hasCalculatedTextureInfo && isLoaded() && getGeometry()->areTexturesLoaded() && !_modelMeshRenderItemsMap.isEmpty()) { + if (!_hasCalculatedTextureInfo && isLoaded() && getGeometry()->areTexturesLoaded() && !_modelMeshRenderItems.isEmpty()) { size_t textureSize = 0; int textureCount = 0; bool allTexturesLoaded = true; - foreach(auto renderItem, _modelMeshRenderItems) { + foreach(auto renderItem, _modelMeshRenderItemsSet) { auto meshPart = renderItem.get(); textureSize += meshPart->getMaterialTextureSize(); textureCount += meshPart->getMaterialTextureCount(); @@ -227,16 +227,12 @@ void Model::updateRenderItems() { return; } - // lazy update of cluster matrices used for rendering. - // We need to update them here so we can correctly update the bounding box. - self->updateClusterMatrices(); - render::ScenePointer scene = AbstractViewStateInterface::instance()->getMain3DScene(); uint32_t deleteGeometryCounter = self->_deleteGeometryCounter; render::PendingChanges pendingChanges; - foreach (auto itemID, self->_modelMeshRenderItemsMap.keys()) { + foreach (auto itemID, self->_modelMeshRenderItems.keys()) { pendingChanges.updateItem(itemID, [deleteGeometryCounter](ModelMeshPartPayload& data) { if (data._model && data._model->isLoaded()) { // Ensure the model geometry was not reset between frames @@ -244,12 +240,12 @@ void Model::updateRenderItems() { Transform modelTransform = data._model->getTransform(); modelTransform.setScale(glm::vec3(1.0f)); - const Model::MeshState& state = data._model->getMeshState(data._meshIndex); - Transform renderTransform = modelTransform; - if (state.clusterMatrices.size() == 1) { - renderTransform = modelTransform.worldTransform(Transform(state.clusterMatrices[0])); - } - data.updateTransformForSkinnedMesh(renderTransform, modelTransform, state.clusterBuffer); + // lazy update of cluster matrices used for rendering. We need to update them here, so we can correctly update the bounding box. + data._model->updateClusterMatrices(); + + // update the model transform and bounding box for this render item. + const Model::MeshState& state = data._model->_meshStates.at(data._meshIndex); + data.updateTransformForSkinnedMesh(modelTransform, state.clusterMatrices); } } }); @@ -259,7 +255,7 @@ void Model::updateRenderItems() { Transform collisionMeshOffset; collisionMeshOffset.setIdentity(); Transform modelTransform = self->getTransform(); - foreach(auto itemID, self->_collisionRenderItemsMap.keys()) { + foreach (auto itemID, self->_collisionRenderItems.keys()) { pendingChanges.updateItem(itemID, [modelTransform, collisionMeshOffset](MeshPartPayload& data) { // update the model transform for this render item. data.updateTransform(modelTransform, collisionMeshOffset); @@ -539,11 +535,11 @@ void Model::setVisibleInScene(bool newValue, std::shared_ptr scen _isVisible = newValue; render::PendingChanges pendingChanges; - foreach (auto item, _modelMeshRenderItemsMap.keys()) { - pendingChanges.resetItem(item, _modelMeshRenderItemsMap[item]); + foreach (auto item, _modelMeshRenderItems.keys()) { + pendingChanges.resetItem(item, _modelMeshRenderItems[item]); } - foreach(auto item, _collisionRenderItemsMap.keys()) { - pendingChanges.resetItem(item, _collisionRenderItemsMap[item]); + foreach (auto item, _collisionRenderItems.keys()) { + pendingChanges.resetItem(item, _collisionRenderItems[item]); } scene->enqueuePendingChanges(pendingChanges); } @@ -555,11 +551,11 @@ void Model::setLayeredInFront(bool layered, std::shared_ptr scene _isLayeredInFront = layered; render::PendingChanges pendingChanges; - foreach(auto item, _modelMeshRenderItemsMap.keys()) { - pendingChanges.resetItem(item, _modelMeshRenderItemsMap[item]); + foreach(auto item, _modelMeshRenderItems.keys()) { + pendingChanges.resetItem(item, _modelMeshRenderItems[item]); } - foreach(auto item, _collisionRenderItemsMap.keys()) { - pendingChanges.resetItem(item, _collisionRenderItemsMap[item]); + foreach(auto item, _collisionRenderItems.keys()) { + pendingChanges.resetItem(item, _collisionRenderItems[item]); } scene->enqueuePendingChanges(pendingChanges); } @@ -576,39 +572,39 @@ bool Model::addToScene(std::shared_ptr scene, bool somethingAdded = false; if (_collisionGeometry) { if (_collisionRenderItems.empty()) { - foreach (auto renderItem, _collisionRenderItems) { + foreach (auto renderItem, _collisionRenderItemsSet) { auto item = scene->allocateID(); auto renderPayload = std::make_shared(renderItem); - if (_collisionRenderItems.empty() && statusGetters.size()) { + if (statusGetters.size()) { renderPayload->addStatusGetters(statusGetters); } pendingChanges.resetItem(item, renderPayload); - _collisionRenderItemsMap.insert(item, renderPayload); + _collisionRenderItems.insert(item, renderPayload); } somethingAdded = !_collisionRenderItems.empty(); } } else { - if (_modelMeshRenderItemsMap.empty()) { + if (_modelMeshRenderItems.empty()) { bool hasTransparent = false; size_t verticesCount = 0; - foreach(auto renderItem, _modelMeshRenderItems) { + foreach(auto renderItem, _modelMeshRenderItemsSet) { auto item = scene->allocateID(); auto renderPayload = std::make_shared(renderItem); - if (_modelMeshRenderItemsMap.empty() && statusGetters.size()) { + if (statusGetters.size()) { renderPayload->addStatusGetters(statusGetters); } pendingChanges.resetItem(item, renderPayload); hasTransparent = hasTransparent || renderItem.get()->getShapeKey().isTranslucent(); verticesCount += renderItem.get()->getVerticesCount(); - _modelMeshRenderItemsMap.insert(item, renderPayload); + _modelMeshRenderItems.insert(item, renderPayload); _modelMeshRenderItemIDs.emplace_back(item); } - somethingAdded = !_modelMeshRenderItemsMap.empty(); + somethingAdded = !_modelMeshRenderItems.empty(); _renderInfoVertexCount = verticesCount; - _renderInfoDrawCalls = _modelMeshRenderItemsMap.count(); + _renderInfoDrawCalls = _modelMeshRenderItems.count(); _renderInfoHasTransparent = hasTransparent; } } @@ -623,18 +619,18 @@ bool Model::addToScene(std::shared_ptr scene, } void Model::removeFromScene(std::shared_ptr scene, render::PendingChanges& pendingChanges) { - foreach (auto item, _modelMeshRenderItemsMap.keys()) { + foreach (auto item, _modelMeshRenderItems.keys()) { pendingChanges.removeItem(item); } _modelMeshRenderItemIDs.clear(); - _modelMeshRenderItemsMap.clear(); _modelMeshRenderItems.clear(); + _modelMeshRenderItemsSet.clear(); - foreach(auto item, _collisionRenderItemsMap.keys()) { + foreach (auto item, _collisionRenderItems.keys()) { pendingChanges.removeItem(item); } _collisionRenderItems.clear(); - _collisionRenderItems.clear(); + _collisionRenderItemsSet.clear(); _addedToScene = false; _renderInfoVertexCount = 0; @@ -1052,8 +1048,8 @@ void Model::updateRig(float deltaTime, glm::mat4 parentTransform) { } void Model::computeMeshPartLocalBounds() { - for (auto& part : _modelMeshRenderItems) { - assert(part->_meshIndex < _modelMeshRenderItems.size()); + for (auto& part : _modelMeshRenderItemsSet) { + assert(part->_meshIndex < _modelMeshRenderItemsSet.size()); const Model::MeshState& state = _meshStates.at(part->_meshIndex); part->computeAdjustedLocalBound(state.clusterMatrices); } @@ -1167,7 +1163,7 @@ AABox Model::getRenderableMeshBound() const { } else { // Build a bound using the last known bound from all the renderItems. AABox totalBound; - for (auto& renderItem : _modelMeshRenderItems) { + for (auto& renderItem : _modelMeshRenderItemsSet) { totalBound += renderItem->getBound(); } return totalBound; @@ -1180,11 +1176,11 @@ const render::ItemIDs& Model::fetchRenderItemIDs() const { void Model::createRenderItemSet() { if (_collisionGeometry) { - if (_collisionRenderItems.empty()) { + if (_collisionRenderItemsSet.empty()) { createCollisionRenderItemSet(); } } else { - if (_modelMeshRenderItems.empty()) { + if (_modelMeshRenderItemsSet.empty()) { createVisibleRenderItemSet(); } } @@ -1201,9 +1197,9 @@ void Model::createVisibleRenderItemSet() { } // We should not have any existing renderItems if we enter this section of code - Q_ASSERT(_modelMeshRenderItems.isEmpty()); + Q_ASSERT(_modelMeshRenderItemsSet.isEmpty()); - _modelMeshRenderItems.clear(); + _modelMeshRenderItemsSet.clear(); Transform transform; transform.setTranslation(_translation); @@ -1225,7 +1221,7 @@ void Model::createVisibleRenderItemSet() { // Create the render payloads int numParts = (int)mesh->getNumParts(); for (int partIndex = 0; partIndex < numParts; partIndex++) { - _modelMeshRenderItems << std::make_shared(this, i, partIndex, shapeID, transform, offset); + _modelMeshRenderItemsSet << std::make_shared(this, i, partIndex, shapeID, transform, offset); shapeID++; } } @@ -1241,7 +1237,7 @@ void Model::createCollisionRenderItemSet() { const auto& meshes = _collisionGeometry->getMeshes(); // We should not have any existing renderItems if we enter this section of code - Q_ASSERT(_collisionRenderItems.isEmpty()); + Q_ASSERT(_collisionRenderItemsSet.isEmpty()); Transform identity; identity.setIdentity(); @@ -1262,7 +1258,7 @@ void Model::createCollisionRenderItemSet() { model::MaterialPointer& material = _collisionMaterials[partIndex % NUM_COLLISION_HULL_COLORS]; auto payload = std::make_shared(mesh, partIndex, material); payload->updateTransform(identity, offset); - _collisionRenderItems << payload; + _collisionRenderItemsSet << payload; } } } @@ -1283,28 +1279,28 @@ bool Model::initWhenReady(render::ScenePointer scene) { bool addedPendingChanges = false; if (_collisionGeometry) { - foreach (auto renderItem, _collisionRenderItems) { + foreach (auto renderItem, _collisionRenderItemsSet) { auto item = scene->allocateID(); auto renderPayload = std::make_shared(renderItem); - _collisionRenderItemsMap.insert(item, renderPayload); + _collisionRenderItems.insert(item, renderPayload); pendingChanges.resetItem(item, renderPayload); } addedPendingChanges = !_collisionRenderItems.empty(); } else { bool hasTransparent = false; size_t verticesCount = 0; - foreach (auto renderItem, _modelMeshRenderItems) { + foreach (auto renderItem, _modelMeshRenderItemsSet) { auto item = scene->allocateID(); auto renderPayload = std::make_shared(renderItem); hasTransparent = hasTransparent || renderItem.get()->getShapeKey().isTranslucent(); verticesCount += renderItem.get()->getVerticesCount(); - _modelMeshRenderItemsMap.insert(item, renderPayload); + _modelMeshRenderItems.insert(item, renderPayload); pendingChanges.resetItem(item, renderPayload); } - addedPendingChanges = !_modelMeshRenderItemsMap.empty(); + addedPendingChanges = !_modelMeshRenderItems.empty(); _renderInfoVertexCount = verticesCount; - _renderInfoDrawCalls = _modelMeshRenderItemsMap.count(); + _renderInfoDrawCalls = _modelMeshRenderItems.count(); _renderInfoHasTransparent = hasTransparent; } _addedToScene = addedPendingChanges; diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index bb283cce1f..41821736f7 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -248,7 +248,7 @@ public: const MeshState& getMeshState(int index) { return _meshStates.at(index); } uint32_t getGeometryCounter() const { return _deleteGeometryCounter; } - const QMap& getRenderItems() const { return _modelMeshRenderItemsMap; } + const QMap& getRenderItems() const { return _modelMeshRenderItems; } void renderDebugMeshBoxes(gpu::Batch& batch); @@ -373,11 +373,11 @@ protected: static AbstractViewStateInterface* _viewState; - QVector> _collisionRenderItems; - QMap _collisionRenderItemsMap; + QSet> _collisionRenderItemsSet; + QMap _collisionRenderItems; - QVector> _modelMeshRenderItems; - QMap _modelMeshRenderItemsMap; + QSet> _modelMeshRenderItemsSet; + QMap _modelMeshRenderItems; render::ItemIDs _modelMeshRenderItemIDs; diff --git a/libraries/render-utils/src/RenderDeferredTask.cpp b/libraries/render-utils/src/RenderDeferredTask.cpp index 22aa95090c..676d176cca 100644 --- a/libraries/render-utils/src/RenderDeferredTask.cpp +++ b/libraries/render-utils/src/RenderDeferredTask.cpp @@ -194,7 +194,7 @@ RenderDeferredTask::RenderDeferredTask(RenderFetchCullSortTask::Output items) { { // Grab a texture map representing the different status icons and assign that to the drawStatsuJob auto iconMapPath = PathUtils::resourcesPath() + "icons/statusIconAtlas.svg"; - auto statusIconMap = DependencyManager::get()->getImageTexture(iconMapPath, NetworkTexture::STRICT_TEXTURE); + auto statusIconMap = DependencyManager::get()->getImageTexture(iconMapPath); addJob("DrawStatus", opaques, DrawStatus(statusIconMap)); } } @@ -259,18 +259,8 @@ void DrawDeferred::run(const SceneContextPointer& sceneContext, const RenderCont // Setup lighting model for all items; batch.setUniformBuffer(render::ShapePipeline::Slot::LIGHTING_MODEL, lightingModel->getParametersBuffer()); - // From the lighting model define a global shapKey ORED with individiual keys - ShapeKey::Builder keyBuilder; - if (lightingModel->isWireframeEnabled()) { - keyBuilder.withWireframe(); - } - ShapeKey globalKey = keyBuilder.build(); - args->_globalShapeKey = globalKey._flags.to_ulong(); - - renderShapes(sceneContext, renderContext, _shapePlumber, inItems, _maxDrawn, globalKey); - + renderShapes(sceneContext, renderContext, _shapePlumber, inItems, _maxDrawn); args->_batch = nullptr; - args->_globalShapeKey = 0; }); config->setNumDrawn((int)inItems.size()); @@ -305,21 +295,12 @@ void DrawStateSortDeferred::run(const SceneContextPointer& sceneContext, const R // Setup lighting model for all items; batch.setUniformBuffer(render::ShapePipeline::Slot::LIGHTING_MODEL, lightingModel->getParametersBuffer()); - // From the lighting model define a global shapKey ORED with individiual keys - ShapeKey::Builder keyBuilder; - if (lightingModel->isWireframeEnabled()) { - keyBuilder.withWireframe(); - } - ShapeKey globalKey = keyBuilder.build(); - args->_globalShapeKey = globalKey._flags.to_ulong(); - if (_stateSort) { - renderStateSortShapes(sceneContext, renderContext, _shapePlumber, inItems, _maxDrawn, globalKey); + renderStateSortShapes(sceneContext, renderContext, _shapePlumber, inItems, _maxDrawn); } else { - renderShapes(sceneContext, renderContext, _shapePlumber, inItems, _maxDrawn, globalKey); + renderShapes(sceneContext, renderContext, _shapePlumber, inItems, _maxDrawn); } args->_batch = nullptr; - args->_globalShapeKey = 0; }); config->setNumDrawn((int)inItems.size()); diff --git a/libraries/render-utils/src/RenderPipelines.cpp b/libraries/render-utils/src/RenderPipelines.cpp index 414bcf0d63..4fbac4170e 100644 --- a/libraries/render-utils/src/RenderPipelines.cpp +++ b/libraries/render-utils/src/RenderPipelines.cpp @@ -307,7 +307,7 @@ void initForwardPipelines(render::ShapePlumber& plumber) { void addPlumberPipeline(ShapePlumber& plumber, const ShapeKey& key, const gpu::ShaderPointer& vertex, const gpu::ShaderPointer& pixel) { // These key-values' pipelines are added by this functor in addition to the key passed - assert(!key.isWireframe()); + assert(!key.isWireFrame()); assert(!key.isDepthBiased()); assert(key.isCullFace()); diff --git a/libraries/render-utils/src/SubsurfaceScattering.cpp b/libraries/render-utils/src/SubsurfaceScattering.cpp index 25a01bff1b..188381b822 100644 --- a/libraries/render-utils/src/SubsurfaceScattering.cpp +++ b/libraries/render-utils/src/SubsurfaceScattering.cpp @@ -414,7 +414,7 @@ gpu::TexturePointer SubsurfaceScatteringResource::generateScatteringProfile(Rend const int PROFILE_RESOLUTION = 512; // const auto pixelFormat = gpu::Element::COLOR_SRGBA_32; const auto pixelFormat = gpu::Element::COLOR_R11G11B10; - auto profileMap = gpu::TexturePointer(gpu::Texture::createRenderBuffer(pixelFormat, PROFILE_RESOLUTION, 1, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR, gpu::Sampler::WRAP_CLAMP))); + auto profileMap = gpu::TexturePointer(gpu::Texture::create2D(pixelFormat, PROFILE_RESOLUTION, 1, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR, gpu::Sampler::WRAP_CLAMP))); profileMap->setSource("Generated Scattering Profile"); diffuseProfileGPU(profileMap, args); return profileMap; @@ -425,7 +425,7 @@ gpu::TexturePointer SubsurfaceScatteringResource::generatePreIntegratedScatterin const int TABLE_RESOLUTION = 512; // const auto pixelFormat = gpu::Element::COLOR_SRGBA_32; const auto pixelFormat = gpu::Element::COLOR_R11G11B10; - auto scatteringLUT = gpu::TexturePointer(gpu::Texture::createRenderBuffer(pixelFormat, TABLE_RESOLUTION, TABLE_RESOLUTION, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR, gpu::Sampler::WRAP_CLAMP))); + auto scatteringLUT = gpu::TexturePointer(gpu::Texture::create2D(pixelFormat, TABLE_RESOLUTION, TABLE_RESOLUTION, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR, gpu::Sampler::WRAP_CLAMP))); //diffuseScatter(scatteringLUT); scatteringLUT->setSource("Generated pre-integrated scattering"); diffuseScatterGPU(profile, scatteringLUT, args); @@ -434,7 +434,7 @@ gpu::TexturePointer SubsurfaceScatteringResource::generatePreIntegratedScatterin gpu::TexturePointer SubsurfaceScatteringResource::generateScatteringSpecularBeckmann(RenderArgs* args) { const int SPECULAR_RESOLUTION = 256; - auto beckmannMap = gpu::TexturePointer(gpu::Texture::createRenderBuffer(gpu::Element::COLOR_RGBA_32 /*gpu::Element(gpu::SCALAR, gpu::HALF, gpu::RGB)*/, SPECULAR_RESOLUTION, SPECULAR_RESOLUTION, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR, gpu::Sampler::WRAP_CLAMP))); + auto beckmannMap = gpu::TexturePointer(gpu::Texture::create2D(gpu::Element::COLOR_RGBA_32 /*gpu::Element(gpu::SCALAR, gpu::HALF, gpu::RGB)*/, SPECULAR_RESOLUTION, SPECULAR_RESOLUTION, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR, gpu::Sampler::WRAP_CLAMP))); beckmannMap->setSource("Generated beckmannMap"); computeSpecularBeckmannGPU(beckmannMap, args); return beckmannMap; diff --git a/libraries/render-utils/src/SurfaceGeometryPass.cpp b/libraries/render-utils/src/SurfaceGeometryPass.cpp index 3a23e70664..f0ac56ac26 100644 --- a/libraries/render-utils/src/SurfaceGeometryPass.cpp +++ b/libraries/render-utils/src/SurfaceGeometryPass.cpp @@ -72,18 +72,18 @@ void LinearDepthFramebuffer::allocate() { auto height = _frameSize.y; // For Linear Depth: - _linearDepthTexture = gpu::TexturePointer(gpu::Texture::createRenderBuffer(gpu::Element(gpu::SCALAR, gpu::FLOAT, gpu::RGB), width, height, + _linearDepthTexture = gpu::TexturePointer(gpu::Texture::create2D(gpu::Element(gpu::SCALAR, gpu::FLOAT, gpu::RGB), width, height, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR_MIP_POINT))); _linearDepthFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("linearDepth")); _linearDepthFramebuffer->setRenderBuffer(0, _linearDepthTexture); _linearDepthFramebuffer->setDepthStencilBuffer(_primaryDepthTexture, _primaryDepthTexture->getTexelFormat()); // For Downsampling: - _halfLinearDepthTexture = gpu::TexturePointer(gpu::Texture::createRenderBuffer(gpu::Element(gpu::SCALAR, gpu::FLOAT, gpu::RGB), _halfFrameSize.x, _halfFrameSize.y, + _halfLinearDepthTexture = gpu::TexturePointer(gpu::Texture::create2D(gpu::Element(gpu::SCALAR, gpu::FLOAT, gpu::RGB), _halfFrameSize.x, _halfFrameSize.y, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR_MIP_POINT))); _halfLinearDepthTexture->autoGenerateMips(5); - _halfNormalTexture = gpu::TexturePointer(gpu::Texture::createRenderBuffer(gpu::Element(gpu::VEC3, gpu::NUINT8, gpu::RGB), _halfFrameSize.x, _halfFrameSize.y, + _halfNormalTexture = gpu::TexturePointer(gpu::Texture::create2D(gpu::Element(gpu::VEC3, gpu::NUINT8, gpu::RGB), _halfFrameSize.x, _halfFrameSize.y, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR_MIP_POINT))); _downsampleFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("halfLinearDepth")); @@ -304,15 +304,15 @@ void SurfaceGeometryFramebuffer::allocate() { auto width = _frameSize.x; auto height = _frameSize.y; - _curvatureTexture = gpu::TexturePointer(gpu::Texture::createRenderBuffer(gpu::Element::COLOR_RGBA_32, width, height, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR_MIP_POINT))); + _curvatureTexture = gpu::TexturePointer(gpu::Texture::create2D(gpu::Element::COLOR_RGBA_32, width, height, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR_MIP_POINT))); _curvatureFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("surfaceGeometry::curvature")); _curvatureFramebuffer->setRenderBuffer(0, _curvatureTexture); - _lowCurvatureTexture = gpu::TexturePointer(gpu::Texture::createRenderBuffer(gpu::Element::COLOR_RGBA_32, width, height, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR_MIP_POINT))); + _lowCurvatureTexture = gpu::TexturePointer(gpu::Texture::create2D(gpu::Element::COLOR_RGBA_32, width, height, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR_MIP_POINT))); _lowCurvatureFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("surfaceGeometry::lowCurvature")); _lowCurvatureFramebuffer->setRenderBuffer(0, _lowCurvatureTexture); - _blurringTexture = gpu::TexturePointer(gpu::Texture::createRenderBuffer(gpu::Element::COLOR_RGBA_32, width, height, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR_MIP_POINT))); + _blurringTexture = gpu::TexturePointer(gpu::Texture::create2D(gpu::Element::COLOR_RGBA_32, width, height, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR_MIP_POINT))); _blurringFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("surfaceGeometry::blurring")); _blurringFramebuffer->setRenderBuffer(0, _blurringTexture); } diff --git a/libraries/render-utils/src/text/Font.cpp b/libraries/render-utils/src/text/Font.cpp index c405f6d6ae..4f4ee12622 100644 --- a/libraries/render-utils/src/text/Font.cpp +++ b/libraries/render-utils/src/text/Font.cpp @@ -209,8 +209,7 @@ void Font::read(QIODevice& in) { } _texture = gpu::TexturePointer(gpu::Texture::create2D(formatGPU, image.width(), image.height(), gpu::Sampler(gpu::Sampler::FILTER_MIN_POINT_MAG_LINEAR))); - _texture->setStoredMipFormat(formatMip); - _texture->assignStoredMip(0, image.byteCount(), image.constBits()); + _texture->assignStoredMip(0, formatMip, image.byteCount(), image.constBits()); } void Font::setupGPU() { diff --git a/libraries/render/CMakeLists.txt b/libraries/render/CMakeLists.txt index 8fd05bd320..735bb7f086 100644 --- a/libraries/render/CMakeLists.txt +++ b/libraries/render/CMakeLists.txt @@ -3,6 +3,6 @@ AUTOSCRIBE_SHADER_LIB(gpu model) setup_hifi_library() # render needs octree only for getAccuracyAngle(float, int) -link_hifi_libraries(shared ktx gpu model octree) +link_hifi_libraries(shared gpu model octree) target_nsight() diff --git a/libraries/render/src/render/DrawTask.cpp b/libraries/render/src/render/DrawTask.cpp index e8537e3452..2829c6f8e7 100755 --- a/libraries/render/src/render/DrawTask.cpp +++ b/libraries/render/src/render/DrawTask.cpp @@ -39,9 +39,9 @@ void render::renderItems(const SceneContextPointer& sceneContext, const RenderCo } } -void renderShape(RenderArgs* args, const ShapePlumberPointer& shapeContext, const Item& item, const ShapeKey& globalKey) { +void renderShape(RenderArgs* args, const ShapePlumberPointer& shapeContext, const Item& item) { assert(item.getKey().isShape()); - auto key = item.getShapeKey() | globalKey; + const auto& key = item.getShapeKey(); if (key.isValid() && !key.hasOwnPipeline()) { args->_pipeline = shapeContext->pickPipeline(args, key); if (args->_pipeline) { @@ -56,7 +56,7 @@ void renderShape(RenderArgs* args, const ShapePlumberPointer& shapeContext, cons } void render::renderShapes(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, - const ShapePlumberPointer& shapeContext, const ItemBounds& inItems, int maxDrawnItems, const ShapeKey& globalKey) { + const ShapePlumberPointer& shapeContext, const ItemBounds& inItems, int maxDrawnItems) { auto& scene = sceneContext->_scene; RenderArgs* args = renderContext->args; @@ -66,12 +66,12 @@ void render::renderShapes(const SceneContextPointer& sceneContext, const RenderC } for (auto i = 0; i < numItemsToDraw; ++i) { auto& item = scene->getItem(inItems[i].id); - renderShape(args, shapeContext, item, globalKey); + renderShape(args, shapeContext, item); } } void render::renderStateSortShapes(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, - const ShapePlumberPointer& shapeContext, const ItemBounds& inItems, int maxDrawnItems, const ShapeKey& globalKey) { + const ShapePlumberPointer& shapeContext, const ItemBounds& inItems, int maxDrawnItems) { auto& scene = sceneContext->_scene; RenderArgs* args = renderContext->args; @@ -91,7 +91,7 @@ void render::renderStateSortShapes(const SceneContextPointer& sceneContext, cons { assert(item.getKey().isShape()); - auto key = item.getShapeKey() | globalKey; + const auto key = item.getShapeKey(); if (key.isValid() && !key.hasOwnPipeline()) { auto& bucket = sortedShapes[key]; if (bucket.empty()) { diff --git a/libraries/render/src/render/DrawTask.h b/libraries/render/src/render/DrawTask.h index a9c5f3a4d8..6e0e5ba10b 100755 --- a/libraries/render/src/render/DrawTask.h +++ b/libraries/render/src/render/DrawTask.h @@ -17,8 +17,8 @@ namespace render { void renderItems(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemBounds& inItems, int maxDrawnItems = -1); -void renderShapes(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ShapePlumberPointer& shapeContext, const ItemBounds& inItems, int maxDrawnItems = -1, const ShapeKey& globalKey = ShapeKey()); -void renderStateSortShapes(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ShapePlumberPointer& shapeContext, const ItemBounds& inItems, int maxDrawnItems = -1, const ShapeKey& globalKey = ShapeKey()); +void renderShapes(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ShapePlumberPointer& shapeContext, const ItemBounds& inItems, int maxDrawnItems = -1); +void renderStateSortShapes(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ShapePlumberPointer& shapeContext, const ItemBounds& inItems, int maxDrawnItems = -1); class DrawLightConfig : public Job::Config { Q_OBJECT diff --git a/libraries/render/src/render/ShapePipeline.h b/libraries/render/src/render/ShapePipeline.h index 73e8f82f24..0c77a15184 100644 --- a/libraries/render/src/render/ShapePipeline.h +++ b/libraries/render/src/render/ShapePipeline.h @@ -46,10 +46,6 @@ public: ShapeKey() : _flags{ 0 } {} ShapeKey(const Flags& flags) : _flags{flags} {} - friend ShapeKey operator&(const ShapeKey& _Left, const ShapeKey& _Right) { return ShapeKey(_Left._flags & _Right._flags); } - friend ShapeKey operator|(const ShapeKey& _Left, const ShapeKey& _Right) { return ShapeKey(_Left._flags | _Right._flags); } - friend ShapeKey operator^(const ShapeKey& _Left, const ShapeKey& _Right) { return ShapeKey(_Left._flags ^ _Right._flags); } - class Builder { public: Builder() {} @@ -148,7 +144,7 @@ public: bool isSkinned() const { return _flags[SKINNED]; } bool isDepthOnly() const { return _flags[DEPTH_ONLY]; } bool isDepthBiased() const { return _flags[DEPTH_BIAS]; } - bool isWireframe() const { return _flags[WIREFRAME]; } + bool isWireFrame() const { return _flags[WIREFRAME]; } bool isCullFace() const { return !_flags[NO_CULL_FACE]; } bool hasOwnPipeline() const { return _flags[OWN_PIPELINE]; } @@ -184,7 +180,7 @@ inline QDebug operator<<(QDebug debug, const ShapeKey& key) { << "isSkinned:" << key.isSkinned() << "isDepthOnly:" << key.isDepthOnly() << "isDepthBiased:" << key.isDepthBiased() - << "isWireframe:" << key.isWireframe() + << "isWireFrame:" << key.isWireFrame() << "isCullFace:" << key.isCullFace() << "]"; } diff --git a/libraries/render/src/render/drawItemStatus.slv b/libraries/render/src/render/drawItemStatus.slv index 792f2733c5..cb4ae7ebd2 100644 --- a/libraries/render/src/render/drawItemStatus.slv +++ b/libraries/render/src/render/drawItemStatus.slv @@ -75,7 +75,7 @@ void main(void) { vec4(1.0, 1.0, 0.0, 1.0) ); - const vec2 ICON_PIXEL_SIZE = vec2(36, 36); + const vec2 ICON_PIXEL_SIZE = vec2(20, 20); const vec2 MARGIN_PIXEL_SIZE = vec2(2, 2); const vec2 ICON_GRID_SLOTS[MAX_NUM_ICONS] = vec2[MAX_NUM_ICONS](vec2(-1.5, 0.5), vec2(-0.5, 0.5), @@ -114,7 +114,7 @@ void main(void) { varColor = vec4(paintRainbow(abs(iconStatus.y)), 1.0); // Pass the texcoord and the z texcoord is representing the texture icon - varTexcoord = vec3( (quadPos.x + 1.0) * 0.5, (quadPos.y + 1.0) * -0.5, iconStatus.z); + varTexcoord = vec3((quadPos.xy + 1.0) * 0.5, iconStatus.z); // Also changes the size of the notification vec2 iconScale = ICON_PIXEL_SIZE; diff --git a/libraries/script-engine/src/AudioScriptingInterface.cpp b/libraries/script-engine/src/AudioScriptingInterface.cpp index 8452494d95..fcc1f201f9 100644 --- a/libraries/script-engine/src/AudioScriptingInterface.cpp +++ b/libraries/script-engine/src/AudioScriptingInterface.cpp @@ -19,6 +19,11 @@ void registerAudioMetaTypes(QScriptEngine* engine) { qScriptRegisterMetaType(engine, soundSharedPointerToScriptValue, soundSharedPointerFromScriptValue); } +AudioScriptingInterface& AudioScriptingInterface::getInstance() { + static AudioScriptingInterface staticInstance; + return staticInstance; +} + AudioScriptingInterface::AudioScriptingInterface() : _localAudioInterface(NULL) { diff --git a/libraries/script-engine/src/AudioScriptingInterface.h b/libraries/script-engine/src/AudioScriptingInterface.h index e97bc329c6..6cce78d48f 100644 --- a/libraries/script-engine/src/AudioScriptingInterface.h +++ b/libraries/script-engine/src/AudioScriptingInterface.h @@ -14,20 +14,18 @@ #include #include -#include #include class ScriptAudioInjector; -class AudioScriptingInterface : public QObject, public Dependency { +class AudioScriptingInterface : public QObject { Q_OBJECT - SINGLETON_DEPENDENCY - public: + static AudioScriptingInterface& getInstance(); + void setLocalAudioInterface(AbstractAudioInterface* audioInterface) { _localAudioInterface = audioInterface; } protected: - // this method is protected to stop C++ callers from calling, but invokable from script Q_INVOKABLE ScriptAudioInjector* playSound(SharedSoundPointer sound, const AudioInjectorOptions& injectorOptions = AudioInjectorOptions()); @@ -44,7 +42,6 @@ signals: private: AudioScriptingInterface(); - AbstractAudioInterface* _localAudioInterface; }; diff --git a/libraries/shared/src/BaseScriptEngine.cpp b/libraries/script-engine/src/BaseScriptEngine.cpp similarity index 68% rename from libraries/shared/src/BaseScriptEngine.cpp rename to libraries/script-engine/src/BaseScriptEngine.cpp index c92d629b75..16308c0650 100644 --- a/libraries/shared/src/BaseScriptEngine.cpp +++ b/libraries/script-engine/src/BaseScriptEngine.cpp @@ -10,7 +10,6 @@ // #include "BaseScriptEngine.h" -#include "SharedLogging.h" #include #include @@ -19,27 +18,18 @@ #include #include +#include "ScriptEngineLogging.h" #include "Profile.h" +const QString BaseScriptEngine::_SETTINGS_ENABLE_EXTENDED_EXCEPTIONS { + "com.highfidelity.experimental.enableExtendedJSExceptions" +}; + const QString BaseScriptEngine::SCRIPT_EXCEPTION_FORMAT { "[%0] %1 in %2:%3" }; const QString BaseScriptEngine::SCRIPT_BACKTRACE_SEP { "\n " }; -bool BaseScriptEngine::IS_THREADSAFE_INVOCATION(const QThread *thread, const QString& method) { - if (QThread::currentThread() == thread) { - return true; - } - qCCritical(shared) << QString("Scripting::%1 @ %2 -- ignoring thread-unsafe call from %3") - .arg(method).arg(thread ? thread->objectName() : "(!thread)").arg(QThread::currentThread()->objectName()); - qCDebug(shared) << "(please resolve on the calling side by using invokeMethod, executeOnScriptThread, etc.)"; - Q_ASSERT(false); - return false; -} - // engine-aware JS Error copier and factory QScriptValue BaseScriptEngine::makeError(const QScriptValue& _other, const QString& type) { - if (!IS_THREADSAFE_INVOCATION(thread(), __FUNCTION__)) { - return unboundNullValue(); - } auto other = _other; if (other.isString()) { other = newObject(); @@ -51,7 +41,7 @@ QScriptValue BaseScriptEngine::makeError(const QScriptValue& _other, const QStri } if (!proto.isFunction()) { #ifdef DEBUG_JS_EXCEPTIONS - qCDebug(shared) << "BaseScriptEngine::makeError -- couldn't find constructor for" << type << " -- using Error instead"; + qCDebug(scriptengine) << "BaseScriptEngine::makeError -- couldn't find constructor for" << type << " -- using Error instead"; #endif proto = globalObject().property("Error"); } @@ -74,9 +64,6 @@ QScriptValue BaseScriptEngine::makeError(const QScriptValue& _other, const QStri // check syntax and when there are issues returns an actual "SyntaxError" with the details QScriptValue BaseScriptEngine::lintScript(const QString& sourceCode, const QString& fileName, const int lineNumber) { - if (!IS_THREADSAFE_INVOCATION(thread(), __FUNCTION__)) { - return unboundNullValue(); - } const auto syntaxCheck = checkSyntax(sourceCode); if (syntaxCheck.state() != QScriptSyntaxCheckResult::Valid) { auto err = globalObject().property("SyntaxError") @@ -95,16 +82,13 @@ QScriptValue BaseScriptEngine::lintScript(const QString& sourceCode, const QStri } return err; } - return QScriptValue(); + return undefinedValue(); } // this pulls from the best available information to create a detailed snapshot of the current exception QScriptValue BaseScriptEngine::cloneUncaughtException(const QString& extraDetail) { - if (!IS_THREADSAFE_INVOCATION(thread(), __FUNCTION__)) { - return unboundNullValue(); - } if (!hasUncaughtException()) { - return unboundNullValue(); + return QScriptValue(); } auto exception = uncaughtException(); // ensure the error object is engine-local @@ -160,10 +144,7 @@ QScriptValue BaseScriptEngine::cloneUncaughtException(const QString& extraDetail return err; } -QString BaseScriptEngine::formatException(const QScriptValue& exception, bool includeExtendedDetails) { - if (!IS_THREADSAFE_INVOCATION(thread(), __FUNCTION__)) { - return QString(); - } +QString BaseScriptEngine::formatException(const QScriptValue& exception) { QString note { "UncaughtException" }; QString result; @@ -175,8 +156,8 @@ QString BaseScriptEngine::formatException(const QScriptValue& exception, bool in const auto lineNumber = exception.property("lineNumber").toString(); const auto stacktrace = exception.property("stack").toString(); - if (includeExtendedDetails) { - // Display additional exception / troubleshooting hints that can be added via the custom Error .detail property + if (_enableExtendedJSExceptions.get()) { + // This setting toggles display of the hints now being added during the loading process. // Example difference: // [UncaughtExceptions] Error: Can't find variable: foobar in atp:/myentity.js\n... // [UncaughtException (construct {1eb5d3fa-23b1-411c-af83-163af7220e3f})] Error: Can't find variable: foobar in atp:/myentity.js\n... @@ -192,39 +173,14 @@ QString BaseScriptEngine::formatException(const QScriptValue& exception, bool in return result; } -bool BaseScriptEngine::raiseException(const QScriptValue& exception) { - if (!IS_THREADSAFE_INVOCATION(thread(), __FUNCTION__)) { - return false; - } - if (currentContext()) { - // we have an active context / JS stack frame so throw the exception per usual - currentContext()->throwValue(makeError(exception)); - return true; - } else { - // we are within a pure C++ stack frame (ie: being called directly by other C++ code) - // in this case no context information is available so just emit the exception for reporting - emit unhandledException(makeError(exception)); - } - return false; -} - -bool BaseScriptEngine::maybeEmitUncaughtException(const QString& debugHint) { - if (!IS_THREADSAFE_INVOCATION(thread(), __FUNCTION__)) { - return false; - } - if (!isEvaluating() && hasUncaughtException()) { - emit unhandledException(cloneUncaughtException(debugHint)); - clearExceptions(); - return true; - } - return false; -} - QScriptValue BaseScriptEngine::evaluateInClosure(const QScriptValue& closure, const QScriptProgram& program) { PROFILE_RANGE(script, "evaluateInClosure"); - if (!IS_THREADSAFE_INVOCATION(thread(), __FUNCTION__)) { - return unboundNullValue(); + if (QThread::currentThread() != thread()) { + qCCritical(scriptengine) << "*** CRITICAL *** ScriptEngine::evaluateInClosure() is meant to be called from engine thread only."; + // note: a recursive mutex might be needed around below code if this method ever becomes Q_INVOKABLE + return QScriptValue(); } + const auto fileName = program.fileName(); const auto shortName = QUrl(fileName).fileName(); @@ -233,7 +189,7 @@ QScriptValue BaseScriptEngine::evaluateInClosure(const QScriptValue& closure, co auto global = closure.property("global"); if (global.isObject()) { #ifdef DEBUG_JS - qCDebug(shared) << " setting global = closure.global" << shortName; + qCDebug(scriptengine) << " setting global = closure.global" << shortName; #endif oldGlobal = globalObject(); setGlobalObject(global); @@ -244,34 +200,34 @@ QScriptValue BaseScriptEngine::evaluateInClosure(const QScriptValue& closure, co auto thiz = closure.property("this"); if (thiz.isObject()) { #ifdef DEBUG_JS - qCDebug(shared) << " setting this = closure.this" << shortName; + qCDebug(scriptengine) << " setting this = closure.this" << shortName; #endif context->setThisObject(thiz); } context->pushScope(closure); #ifdef DEBUG_JS - qCDebug(shared) << QString("[%1] evaluateInClosure %2").arg(isEvaluating()).arg(shortName); + qCDebug(scriptengine) << QString("[%1] evaluateInClosure %2").arg(isEvaluating()).arg(shortName); #endif { result = BaseScriptEngine::evaluate(program); if (hasUncaughtException()) { auto err = cloneUncaughtException(__FUNCTION__); #ifdef DEBUG_JS_EXCEPTIONS - qCWarning(shared) << __FUNCTION__ << "---------- hasCaught:" << err.toString() << result.toString(); + qCWarning(scriptengine) << __FUNCTION__ << "---------- hasCaught:" << err.toString() << result.toString(); err.setProperty("_result", result); #endif result = err; } } #ifdef DEBUG_JS - qCDebug(shared) << QString("[%1] //evaluateInClosure %2").arg(isEvaluating()).arg(shortName); + qCDebug(scriptengine) << QString("[%1] //evaluateInClosure %2").arg(isEvaluating()).arg(shortName); #endif popContext(); if (oldGlobal.isValid()) { #ifdef DEBUG_JS - qCDebug(shared) << " restoring global" << shortName; + qCDebug(scriptengine) << " restoring global" << shortName; #endif setGlobalObject(oldGlobal); } @@ -280,6 +236,7 @@ QScriptValue BaseScriptEngine::evaluateInClosure(const QScriptValue& closure, co } // Lambda + QScriptValue BaseScriptEngine::newLambdaFunction(std::function operation, const QScriptValue& data, const QScriptEngine::ValueOwnership& ownership) { auto lambda = new Lambda(this, operation, data); auto object = newQObject(lambda, ownership); @@ -305,57 +262,26 @@ Lambda::Lambda(QScriptEngine *engine, std::functionthread(), __FUNCTION__)) { - return BaseScriptEngine::unboundNullValue(); - } return operation(engine->currentContext(), engine); } -QScriptValue makeScopedHandlerObject(QScriptValue scopeOrCallback, QScriptValue methodOrName) { - auto engine = scopeOrCallback.engine(); - if (!engine) { - return scopeOrCallback; - } - auto scope = QScriptValue(); - auto callback = scopeOrCallback; - if (scopeOrCallback.isObject()) { - if (methodOrName.isString()) { - scope = scopeOrCallback; - callback = scope.property(methodOrName.toString()); - } else if (methodOrName.isFunction()) { - scope = scopeOrCallback; - callback = methodOrName; - } - } - auto handler = engine->newObject(); - handler.setProperty("scope", scope); - handler.setProperty("callback", callback); - return handler; -} - -QScriptValue callScopedHandlerObject(QScriptValue handler, QScriptValue err, QScriptValue result) { - return handler.property("callback").call(handler.property("scope"), QScriptValueList({ err, result })); -} - #ifdef DEBUG_JS void BaseScriptEngine::_debugDump(const QString& header, const QScriptValue& object, const QString& footer) { - if (!IS_THREADSAFE_INVOCATION(thread(), __FUNCTION__)) { - return; - } if (!header.isEmpty()) { - qCDebug(shared) << header; + qCDebug(scriptengine) << header; } if (!object.isObject()) { - qCDebug(shared) << "(!isObject)" << object.toVariant().toString() << object.toString(); + qCDebug(scriptengine) << "(!isObject)" << object.toVariant().toString() << object.toString(); return; } QScriptValueIterator it(object); while (it.hasNext()) { it.next(); - qCDebug(shared) << it.name() << ":" << it.value().toString(); + qCDebug(scriptengine) << it.name() << ":" << it.value().toString(); } if (!footer.isEmpty()) { - qCDebug(shared) << footer; + qCDebug(scriptengine) << footer; } } #endif + diff --git a/libraries/script-engine/src/BaseScriptEngine.h b/libraries/script-engine/src/BaseScriptEngine.h new file mode 100644 index 0000000000..27a6eff33d --- /dev/null +++ b/libraries/script-engine/src/BaseScriptEngine.h @@ -0,0 +1,67 @@ +// +// BaseScriptEngine.h +// libraries/script-engine/src +// +// Created by Timothy Dedischew on 02/01/17. +// Copyright 2017 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_BaseScriptEngine_h +#define hifi_BaseScriptEngine_h + +#include +#include +#include + +#include "SettingHandle.h" + +// common base class for extending QScriptEngine itself +class BaseScriptEngine : public QScriptEngine { + Q_OBJECT +public: + static const QString SCRIPT_EXCEPTION_FORMAT; + static const QString SCRIPT_BACKTRACE_SEP; + + BaseScriptEngine() {} + + Q_INVOKABLE QScriptValue evaluateInClosure(const QScriptValue& locals, const QScriptProgram& program); + + Q_INVOKABLE QScriptValue lintScript(const QString& sourceCode, const QString& fileName, const int lineNumber = 1); + Q_INVOKABLE QScriptValue makeError(const QScriptValue& other = QScriptValue(), const QString& type = "Error"); + Q_INVOKABLE QString formatException(const QScriptValue& exception); + QScriptValue cloneUncaughtException(const QString& detail = QString()); + +signals: + void unhandledException(const QScriptValue& exception); + +protected: + void _emitUnhandledException(const QScriptValue& exception); + QScriptValue newLambdaFunction(std::function operation, const QScriptValue& data = QScriptValue(), const QScriptEngine::ValueOwnership& ownership = QScriptEngine::AutoOwnership); + + static const QString _SETTINGS_ENABLE_EXTENDED_EXCEPTIONS; + Setting::Handle _enableExtendedJSExceptions { _SETTINGS_ENABLE_EXTENDED_EXCEPTIONS, true }; +#ifdef DEBUG_JS + static void _debugDump(const QString& header, const QScriptValue& object, const QString& footer = QString()); +#endif +}; + +// Lambda helps create callable QScriptValues out of std::functions: +// (just meant for use from within the script engine itself) +class Lambda : public QObject { + Q_OBJECT +public: + Lambda(QScriptEngine *engine, std::function operation, QScriptValue data); + ~Lambda(); + public slots: + QScriptValue call(); + QString toString() const; +private: + QScriptEngine* engine; + std::function operation; + QScriptValue data; +}; + +#endif // hifi_BaseScriptEngine_h diff --git a/libraries/script-engine/src/Mat4.cpp b/libraries/script-engine/src/Mat4.cpp index 6676d0cde1..52b9690321 100644 --- a/libraries/script-engine/src/Mat4.cpp +++ b/libraries/script-engine/src/Mat4.cpp @@ -54,7 +54,7 @@ glm::mat4 Mat4::inverse(const glm::mat4& m) const { return glm::inverse(m); } -glm::vec3 Mat4::getForward(const glm::mat4& m) const { +glm::vec3 Mat4::getFront(const glm::mat4& m) const { return glm::vec3(-m[0][2], -m[1][2], -m[2][2]); } diff --git a/libraries/script-engine/src/Mat4.h b/libraries/script-engine/src/Mat4.h index 19bbbe178a..8b2a8aa8c1 100644 --- a/libraries/script-engine/src/Mat4.h +++ b/libraries/script-engine/src/Mat4.h @@ -37,9 +37,7 @@ public slots: glm::mat4 inverse(const glm::mat4& m) const; - // redundant, calls getForward which better describes the returned vector as a direction - glm::vec3 getFront(const glm::mat4& m) const { return getForward(m); } - glm::vec3 getForward(const glm::mat4& m) const; + glm::vec3 getFront(const glm::mat4& m) const; glm::vec3 getRight(const glm::mat4& m) const; glm::vec3 getUp(const glm::mat4& m) const; diff --git a/libraries/script-engine/src/MeshProxy.h b/libraries/script-engine/src/MeshProxy.h deleted file mode 100644 index 82f5038348..0000000000 --- a/libraries/script-engine/src/MeshProxy.h +++ /dev/null @@ -1,41 +0,0 @@ -// -// MeshProxy.h -// libraries/script-engine/src -// -// Created by Seth Alves on 2017-1-27. -// Copyright 2017 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#ifndef hifi_MeshProxy_h -#define hifi_MeshProxy_h - -#include - -using MeshPointer = std::shared_ptr; - -class MeshProxy : public QObject { - Q_OBJECT - -public: - MeshProxy(MeshPointer mesh) : _mesh(mesh) {} - ~MeshProxy() {} - - MeshPointer getMeshPointer() const { return _mesh; } - - Q_INVOKABLE int getNumVertices() const { return (int)_mesh->getNumVertices(); } - Q_INVOKABLE glm::vec3 getPos3(int index) const { return _mesh->getPos3(index); } - - -protected: - MeshPointer _mesh; -}; - -Q_DECLARE_METATYPE(MeshProxy*); - -class MeshProxyList : public QList {}; // typedef and using fight with the Qt macros/templates, do this instead -Q_DECLARE_METATYPE(MeshProxyList); - -#endif // hifi_MeshProxy_h diff --git a/libraries/script-engine/src/ModelScriptingInterface.cpp b/libraries/script-engine/src/ModelScriptingInterface.cpp deleted file mode 100644 index 833ac5b64d..0000000000 --- a/libraries/script-engine/src/ModelScriptingInterface.cpp +++ /dev/null @@ -1,159 +0,0 @@ -// -// ModelScriptingInterface.cpp -// libraries/script-engine/src -// -// Created by Seth Alves on 2017-1-27. -// Copyright 2017 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#include -#include -#include -#include "ScriptEngine.h" -#include "ModelScriptingInterface.h" -#include "OBJWriter.h" - -ModelScriptingInterface::ModelScriptingInterface(QObject* parent) : QObject(parent) { - _modelScriptEngine = qobject_cast(parent); -} - -QScriptValue meshToScriptValue(QScriptEngine* engine, MeshProxy* const &in) { - return engine->newQObject(in, QScriptEngine::QtOwnership, - QScriptEngine::ExcludeDeleteLater | QScriptEngine::ExcludeChildObjects); -} - -void meshFromScriptValue(const QScriptValue& value, MeshProxy* &out) { - out = qobject_cast(value.toQObject()); -} - -QScriptValue meshesToScriptValue(QScriptEngine* engine, const MeshProxyList &in) { - return engine->toScriptValue(in); -} - -void meshesFromScriptValue(const QScriptValue& value, MeshProxyList &out) { - QScriptValueIterator itr(value); - while(itr.hasNext()) { - itr.next(); - MeshProxy* meshProxy = qscriptvalue_cast(itr.value()); - if (meshProxy) { - out.append(meshProxy); - } - } -} - -QString ModelScriptingInterface::meshToOBJ(MeshProxyList in) { - QList meshes; - foreach (const MeshProxy* meshProxy, in) { - meshes.append(meshProxy->getMeshPointer()); - } - - return writeOBJToString(meshes); -} - -QScriptValue ModelScriptingInterface::appendMeshes(MeshProxyList in) { - // figure out the size of the resulting mesh - size_t totalVertexCount { 0 }; - size_t totalAttributeCount { 0 }; - size_t totalIndexCount { 0 }; - foreach (const MeshProxy* meshProxy, in) { - MeshPointer mesh = meshProxy->getMeshPointer(); - totalVertexCount += mesh->getNumVertices(); - - int attributeTypeNormal = gpu::Stream::InputSlot::NORMAL; // libraries/gpu/src/gpu/Stream.h - const gpu::BufferView& normalsBufferView = mesh->getAttributeBuffer(attributeTypeNormal); - gpu::BufferView::Index numNormals = (gpu::BufferView::Index)normalsBufferView.getNumElements(); - totalAttributeCount += numNormals; - - totalIndexCount += mesh->getNumIndices(); - } - - // alloc the resulting mesh - gpu::Resource::Size combinedVertexSize = totalVertexCount * sizeof(glm::vec3); - unsigned char* combinedVertexData = new unsigned char[combinedVertexSize]; - unsigned char* combinedVertexDataCursor = combinedVertexData; - - gpu::Resource::Size combinedNormalSize = totalAttributeCount * sizeof(glm::vec3); - unsigned char* combinedNormalData = new unsigned char[combinedNormalSize]; - unsigned char* combinedNormalDataCursor = combinedNormalData; - - gpu::Resource::Size combinedIndexSize = totalIndexCount * sizeof(uint32_t); - unsigned char* combinedIndexData = new unsigned char[combinedIndexSize]; - unsigned char* combinedIndexDataCursor = combinedIndexData; - - uint32_t indexStartOffset { 0 }; - - foreach (const MeshProxy* meshProxy, in) { - MeshPointer mesh = meshProxy->getMeshPointer(); - mesh->forEach( - [&](glm::vec3 position){ - memcpy(combinedVertexDataCursor, &position, sizeof(position)); - combinedVertexDataCursor += sizeof(position); - }, - [&](glm::vec3 normal){ - memcpy(combinedNormalDataCursor, &normal, sizeof(normal)); - combinedNormalDataCursor += sizeof(normal); - }, - [&](uint32_t index){ - index += indexStartOffset; - memcpy(combinedIndexDataCursor, &index, sizeof(index)); - combinedIndexDataCursor += sizeof(index); - }); - - gpu::BufferView::Index numVertices = (gpu::BufferView::Index)mesh->getNumVertices(); - indexStartOffset += numVertices; - } - - model::MeshPointer result(new model::Mesh()); - - gpu::Element vertexElement = gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ); - gpu::Buffer* combinedVertexBuffer = new gpu::Buffer(combinedVertexSize, combinedVertexData); - gpu::BufferPointer combinedVertexBufferPointer(combinedVertexBuffer); - gpu::BufferView combinedVertexBufferView(combinedVertexBufferPointer, vertexElement); - result->setVertexBuffer(combinedVertexBufferView); - - int attributeTypeNormal = gpu::Stream::InputSlot::NORMAL; // libraries/gpu/src/gpu/Stream.h - gpu::Element normalElement = gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ); - gpu::Buffer* combinedNormalsBuffer = new gpu::Buffer(combinedNormalSize, combinedNormalData); - gpu::BufferPointer combinedNormalsBufferPointer(combinedNormalsBuffer); - gpu::BufferView combinedNormalsBufferView(combinedNormalsBufferPointer, normalElement); - result->addAttribute(attributeTypeNormal, combinedNormalsBufferView); - - gpu::Element indexElement = gpu::Element(gpu::SCALAR, gpu::UINT32, gpu::RAW); - gpu::Buffer* combinedIndexesBuffer = new gpu::Buffer(combinedIndexSize, combinedIndexData); - gpu::BufferPointer combinedIndexesBufferPointer(combinedIndexesBuffer); - gpu::BufferView combinedIndexesBufferView(combinedIndexesBufferPointer, indexElement); - result->setIndexBuffer(combinedIndexesBufferView); - - std::vector parts; - parts.emplace_back(model::Mesh::Part((model::Index)0, // startIndex - (model::Index)result->getNumIndices(), // numIndices - (model::Index)0, // baseVertex - model::Mesh::TRIANGLES)); // topology - result->setPartBuffer(gpu::BufferView(new gpu::Buffer(parts.size() * sizeof(model::Mesh::Part), - (gpu::Byte*) parts.data()), gpu::Element::PART_DRAWCALL)); - - - MeshProxy* resultProxy = new MeshProxy(result); - return meshToScriptValue(_modelScriptEngine, resultProxy); -} - - - -QScriptValue ModelScriptingInterface::transformMesh(glm::mat4 transform, MeshProxy* meshProxy) { - if (!meshProxy) { - return QScriptValue(false); - } - MeshPointer mesh = meshProxy->getMeshPointer(); - if (!mesh) { - return QScriptValue(false); - } - - model::MeshPointer result = mesh->map([&](glm::vec3 position){ return glm::vec3(transform * glm::vec4(position, 1.0f)); }, - [&](glm::vec3 normal){ return glm::vec3(transform * glm::vec4(normal, 0.0f)); }, - [&](uint32_t index){ return index; }); - MeshProxy* resultProxy = new MeshProxy(result); - return meshToScriptValue(_modelScriptEngine, resultProxy); -} diff --git a/libraries/script-engine/src/ModelScriptingInterface.h b/libraries/script-engine/src/ModelScriptingInterface.h deleted file mode 100644 index 14789943e3..0000000000 --- a/libraries/script-engine/src/ModelScriptingInterface.h +++ /dev/null @@ -1,45 +0,0 @@ -// -// ModelScriptingInterface.h -// libraries/script-engine/src -// -// Created by Seth Alves on 2017-1-27. -// Copyright 2017 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - - -#ifndef hifi_ModelScriptingInterface_h -#define hifi_ModelScriptingInterface_h - -#include -#include -#include -#include -#include "MeshProxy.h" - -using MeshPointer = std::shared_ptr; -class ScriptEngine; - -class ModelScriptingInterface : public QObject { - Q_OBJECT - -public: - ModelScriptingInterface(QObject* parent); - - Q_INVOKABLE QString meshToOBJ(MeshProxyList in); - Q_INVOKABLE QScriptValue appendMeshes(MeshProxyList in); - Q_INVOKABLE QScriptValue transformMesh(glm::mat4 transform, MeshProxy* meshProxy); - -private: - ScriptEngine* _modelScriptEngine { nullptr }; -}; - -QScriptValue meshToScriptValue(QScriptEngine* engine, MeshProxy* const &in); -void meshFromScriptValue(const QScriptValue& value, MeshProxy* &out); - -QScriptValue meshesToScriptValue(QScriptEngine* engine, const MeshProxyList &in); -void meshesFromScriptValue(const QScriptValue& value, MeshProxyList &out); - -#endif // hifi_ModelScriptingInterface_h diff --git a/libraries/script-engine/src/Quat.cpp b/libraries/script-engine/src/Quat.cpp index 6d49ed27c1..6c2e7a349e 100644 --- a/libraries/script-engine/src/Quat.cpp +++ b/libraries/script-engine/src/Quat.cpp @@ -68,7 +68,7 @@ glm::quat Quat::inverse(const glm::quat& q) { return glm::inverse(q); } -glm::vec3 Quat::getForward(const glm::quat& orientation) { +glm::vec3 Quat::getFront(const glm::quat& orientation) { return orientation * Vectors::FRONT; } diff --git a/libraries/script-engine/src/Quat.h b/libraries/script-engine/src/Quat.h index 8a88767a41..b51f1cb47e 100644 --- a/libraries/script-engine/src/Quat.h +++ b/libraries/script-engine/src/Quat.h @@ -45,9 +45,7 @@ public slots: glm::quat fromPitchYawRollDegrees(float pitch, float yaw, float roll); // degrees glm::quat fromPitchYawRollRadians(float pitch, float yaw, float roll); // radians glm::quat inverse(const glm::quat& q); - // redundant, calls getForward which better describes the returned vector as a direction - glm::vec3 getFront(const glm::quat& orientation) { return getForward(orientation); } - glm::vec3 getForward(const glm::quat& orientation); + glm::vec3 getFront(const glm::quat& orientation); glm::vec3 getRight(const glm::quat& orientation); glm::vec3 getUp(const glm::quat& orientation); glm::vec3 safeEulerAngles(const glm::quat& orientation); // degrees diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index a5c94c1bb4..d721d1c86f 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -19,9 +19,6 @@ #include #include -#include -#include - #include #include @@ -68,25 +65,18 @@ #include "RecordingScriptingInterface.h" #include "ScriptEngines.h" #include "TabletScriptingInterface.h" -#include "ModelScriptingInterface.h" - #include #include "MIDIEvent.h" -const QString ScriptEngine::_SETTINGS_ENABLE_EXTENDED_EXCEPTIONS { - "com.highfidelity.experimental.enableExtendedJSExceptions" -}; - -static const int MAX_MODULE_ID_LENGTH { 4096 }; -static const int MAX_DEBUG_VALUE_LENGTH { 80 }; - static const QScriptEngine::QObjectWrapOptions DEFAULT_QOBJECT_WRAP_OPTIONS = QScriptEngine::ExcludeDeleteLater | QScriptEngine::ExcludeChildObjects; static const QScriptValue::PropertyFlags READONLY_PROP_FLAGS { QScriptValue::ReadOnly | QScriptValue::Undeletable }; static const QScriptValue::PropertyFlags READONLY_HIDDEN_PROP_FLAGS { READONLY_PROP_FLAGS | QScriptValue::SkipInEnumeration }; + + static const bool HIFI_AUTOREFRESH_FILE_SCRIPTS { true }; Q_DECLARE_METATYPE(QScriptEngine::FunctionSignature) @@ -94,7 +84,7 @@ int functionSignatureMetaID = qRegisterMetaTypeargumentCount(); i++) { if (i > 0) { @@ -151,7 +141,7 @@ QString encodeEntityIdIntoEntityUrl(const QString& url, const QString& entityID) } QString ScriptEngine::logException(const QScriptValue& exception) { - auto message = formatException(exception, _enableExtendedJSExceptions.get()); + auto message = formatException(exception); scriptErrorMessage(message); return message; } @@ -343,7 +333,7 @@ void ScriptEngine::runInThread() { // The thread interface cannot live on itself, and we want to move this into the thread, so // the thread cannot have this as a parent. QThread* workerThread = new QThread(); - workerThread->setObjectName(QString("js:") + getFilename().replace("about:","")); + workerThread->setObjectName(QString("Script Thread:") + getFilename()); moveToThread(workerThread); // NOTE: If you connect any essential signals for proper shutdown or cleanup of @@ -464,17 +454,17 @@ void ScriptEngine::loadURL(const QUrl& scriptURL, bool reload) { void ScriptEngine::scriptErrorMessage(const QString& message) { qCCritical(scriptengine) << qPrintable(message); - emit errorMessage(message, getFilename()); + emit errorMessage(message); } void ScriptEngine::scriptWarningMessage(const QString& message) { qCWarning(scriptengine) << message; - emit warningMessage(message, getFilename()); + emit warningMessage(message); } void ScriptEngine::scriptInfoMessage(const QString& message) { qCInfo(scriptengine) << message; - emit infoMessage(message, getFilename()); + emit infoMessage(message); } // Even though we never pass AnimVariantMap directly to and from javascript, the queued invokeMethod of @@ -542,40 +532,6 @@ static QScriptValue createScriptableResourcePrototype(QScriptEngine* engine) { return prototype; } -void ScriptEngine::resetModuleCache(bool deleteScriptCache) { - if (QThread::currentThread() != thread()) { - executeOnScriptThread([=]() { resetModuleCache(deleteScriptCache); }); - return; - } - auto jsRequire = globalObject().property("Script").property("require"); - auto cache = jsRequire.property("cache"); - auto cacheMeta = jsRequire.data(); - - if (deleteScriptCache) { - QScriptValueIterator it(cache); - while (it.hasNext()) { - it.next(); - if (it.flags() & QScriptValue::SkipInEnumeration) { - continue; - } - qCDebug(scriptengine) << "resetModuleCache(true) -- staging " << it.name() << " for cache reset at next require"; - cacheMeta.setProperty(it.name(), true); - } - } - cache = newObject(); - if (!cacheMeta.isObject()) { - cacheMeta = newObject(); - cacheMeta.setProperty("id", "Script.require.cacheMeta"); - cacheMeta.setProperty("type", "cacheMeta"); - jsRequire.setData(cacheMeta); - } - cache.setProperty("__created__", (double)QDateTime::currentMSecsSinceEpoch(), QScriptValue::SkipInEnumeration); -#if DEBUG_JS_MODULES - cache.setProperty("__meta__", cacheMeta, READONLY_HIDDEN_PROP_FLAGS); -#endif - jsRequire.setProperty("cache", cache, READONLY_PROP_FLAGS); -} - void ScriptEngine::init() { if (_isInitialized) { return; // only initialize once @@ -585,6 +541,16 @@ void ScriptEngine::init() { auto entityScriptingInterface = DependencyManager::get(); entityScriptingInterface->init(); + connect(entityScriptingInterface.data(), &EntityScriptingInterface::deletingEntity, this, [this](const EntityItemID& entityID) { + if (_entityScripts.contains(entityID)) { + if (isEntityScriptRunning(entityID)) { + qCWarning(scriptengine) << "deletingEntity while entity script is still running!" << entityID; + } + _entityScripts.remove(entityID); + emit entityScriptDetailsUpdated(); + } + }); + // register various meta-types registerMetaTypes(this); @@ -627,22 +593,9 @@ void ScriptEngine::init() { qScriptRegisterMetaType(this, qWSCloseCodeToScriptValue, qWSCloseCodeFromScriptValue); qScriptRegisterMetaType(this, wscReadyStateToScriptValue, wscReadyStateFromScriptValue); - // NOTE: You do not want to end up creating new instances of singletons here. They will be on the ScriptEngine thread - // and are likely to be unusable if we "reset" the ScriptEngine by creating a new one (on a whole new thread). - registerGlobalObject("Script", this); - { - // set up Script.require.resolve and Script.require.cache - auto Script = globalObject().property("Script"); - auto require = Script.property("require"); - auto resolve = Script.property("_requireResolve"); - require.setProperty("resolve", resolve, READONLY_PROP_FLAGS); - resetModuleCache(); - } - - registerGlobalObject("Audio", DependencyManager::get().data()); - + registerGlobalObject("Audio", &AudioScriptingInterface::getInstance()); registerGlobalObject("Entities", entityScriptingInterface.data()); registerGlobalObject("Quat", &_quatLibrary); registerGlobalObject("Vec3", &_vec3Library); @@ -651,7 +604,7 @@ void ScriptEngine::init() { registerGlobalObject("Messages", DependencyManager::get().data()); registerGlobalObject("File", new FileScriptingInterface(this)); - + qScriptRegisterMetaType(this, animVarMapToScriptValue, animVarMapFromScriptValue); qScriptRegisterMetaType(this, resultHandlerToScriptValue, resultHandlerFromScriptValue); @@ -669,10 +622,6 @@ void ScriptEngine::init() { registerGlobalObject("Resources", DependencyManager::get().data()); registerGlobalObject("DebugDraw", &DebugDraw::getInstance()); - - registerGlobalObject("Model", new ModelScriptingInterface(this)); - qScriptRegisterMetaType(this, meshToScriptValue, meshFromScriptValue); - qScriptRegisterMetaType(this, meshesToScriptValue, meshesFromScriptValue); } void ScriptEngine::registerValue(const QString& valueName, QScriptValue value) { @@ -914,11 +863,6 @@ void ScriptEngine::addEventHandler(const EntityItemID& entityID, const QString& handlersForEvent << handlerData; // Note that the same handler can be added many times. See removeEntityEventHandler(). } -// this is not redundant -- the version in BaseScriptEngine is specifically not Q_INVOKABLE -QScriptValue ScriptEngine::evaluateInClosure(const QScriptValue& closure, const QScriptProgram& program) { - return BaseScriptEngine::evaluateInClosure(closure, program); -} - QScriptValue ScriptEngine::evaluate(const QString& sourceCode, const QString& fileName, int lineNumber) { if (DependencyManager::get()->isStopped()) { return QScriptValue(); // bail early @@ -941,26 +885,29 @@ QScriptValue ScriptEngine::evaluate(const QString& sourceCode, const QString& fi // Check syntax auto syntaxError = lintScript(sourceCode, fileName); if (syntaxError.isError()) { - if (!isEvaluating()) { + if (isEvaluating()) { + currentContext()->throwValue(syntaxError); + } else { syntaxError.setProperty("detail", "evaluate"); + emit unhandledException(syntaxError); } - raiseException(syntaxError); - maybeEmitUncaughtException("lint"); return syntaxError; } QScriptProgram program { sourceCode, fileName, lineNumber }; if (program.isNull()) { // can this happen? auto err = makeError("could not create QScriptProgram for " + fileName); - raiseException(err); - maybeEmitUncaughtException("compile"); + emit unhandledException(err); return err; } QScriptValue result; { result = BaseScriptEngine::evaluate(program); - maybeEmitUncaughtException("evaluate"); + if (!isEvaluating() && hasUncaughtException()) { + emit unhandledException(cloneUncaughtException(__FUNCTION__)); + clearExceptions(); + } } return result; } @@ -983,7 +930,10 @@ void ScriptEngine::run() { { evaluate(_scriptContents, _fileNameString); - maybeEmitUncaughtException(__FUNCTION__); + if (!isEvaluating() && hasUncaughtException()) { + emit unhandledException(cloneUncaughtException(__FUNCTION__)); + clearExceptions(); + } } #ifdef _WIN32 // VS13 does not sleep_until unless it uses the system_clock, see: @@ -1351,354 +1301,7 @@ QUrl ScriptEngine::resourcesPath() const { } void ScriptEngine::print(const QString& message) { - emit printedMessage(message, getFilename()); -} - -// Script.require.resolve -- like resolvePath, but performs more validation and throws exceptions on invalid module identifiers (for consistency with Node.js) -QString ScriptEngine::_requireResolve(const QString& moduleId, const QString& relativeTo) { - if (!IS_THREADSAFE_INVOCATION(thread(), __FUNCTION__)) { - return QString(); - } - QUrl defaultScriptsLoc = defaultScriptsLocation(); - QUrl url(moduleId); - - auto displayId = moduleId; - if (displayId.length() > MAX_DEBUG_VALUE_LENGTH) { - displayId = displayId.mid(0, MAX_DEBUG_VALUE_LENGTH) + "..."; - } - auto message = QString("Cannot find module '%1' (%2)").arg(displayId); - - auto throwResolveError = [&](const QScriptValue& error) -> QString { - raiseException(error); - maybeEmitUncaughtException("require.resolve"); - return QString(); - }; - - // de-fuzz the input a little by restricting to rational sizes - auto idLength = url.toString().length(); - if (idLength < 1 || idLength > MAX_MODULE_ID_LENGTH) { - auto details = QString("rejecting invalid module id size (%1 chars [1,%2])") - .arg(idLength).arg(MAX_MODULE_ID_LENGTH); - return throwResolveError(makeError(message.arg(details), "RangeError")); - } - - // this regex matches: absolute, dotted or path-like URLs - // (ie: the kind of stuff ScriptEngine::resolvePath already handles) - QRegularExpression qualified ("^\\w+:|^/|^[.]{1,2}(/|$)"); - - // this is for module.require (which is a bound version of require that's always relative to the module path) - if (!relativeTo.isEmpty()) { - url = QUrl(relativeTo).resolved(moduleId); - url = resolvePath(url.toString()); - } else if (qualified.match(moduleId).hasMatch()) { - url = resolvePath(moduleId); - } else { - // check if the moduleId refers to a "system" module - QString systemPath = defaultScriptsLoc.path(); - QString systemModulePath = QString("%1/modules/%2.js").arg(systemPath).arg(moduleId); - url = defaultScriptsLoc; - url.setPath(systemModulePath); - if (!QFileInfo(url.toLocalFile()).isFile()) { - if (!moduleId.contains("./")) { - // the user might be trying to refer to a relative file without anchoring it - // let's do them a favor and test for that case -- offering specific advice if detected - auto unanchoredUrl = resolvePath("./" + moduleId); - if (QFileInfo(unanchoredUrl.toLocalFile()).isFile()) { - auto msg = QString("relative module ids must be anchored; use './%1' instead") - .arg(moduleId); - return throwResolveError(makeError(message.arg(msg))); - } - } - return throwResolveError(makeError(message.arg("system module not found"))); - } - } - - if (url.isRelative()) { - return throwResolveError(makeError(message.arg("could not resolve module id"))); - } - - // if it looks like a local file, verify that it's an allowed path and really a file - if (url.isLocalFile()) { - QFileInfo file(url.toLocalFile()); - QUrl canonical = url; - if (file.exists()) { - canonical.setPath(file.canonicalFilePath()); - } - - bool disallowOutsideFiles = !defaultScriptsLocation().isParentOf(canonical) && !currentSandboxURL.isLocalFile(); - if (disallowOutsideFiles && !PathUtils::isDescendantOf(canonical, currentSandboxURL)) { - return throwResolveError(makeError(message.arg( - QString("path '%1' outside of origin script '%2' '%3'") - .arg(PathUtils::stripFilename(url)) - .arg(PathUtils::stripFilename(currentSandboxURL)) - .arg(canonical.toString()) - ))); - } - if (!file.exists()) { - return throwResolveError(makeError(message.arg("path does not exist: " + url.toLocalFile()))); - } - if (!file.isFile()) { - return throwResolveError(makeError(message.arg("path is not a file: " + url.toLocalFile()))); - } - } - - maybeEmitUncaughtException(__FUNCTION__); - return url.toString(); -} - -// retrieves the current parent module from the JS scope chain -QScriptValue ScriptEngine::currentModule() { - if (!IS_THREADSAFE_INVOCATION(thread(), __FUNCTION__)) { - return unboundNullValue(); - } - auto jsRequire = globalObject().property("Script").property("require"); - auto cache = jsRequire.property("cache"); - auto candidate = QScriptValue(); - for (auto c = currentContext(); c && !candidate.isObject(); c = c->parentContext()) { - QScriptContextInfo contextInfo { c }; - candidate = cache.property(contextInfo.fileName()); - } - if (!candidate.isObject()) { - return QScriptValue(); - } - return candidate; -} - -// replaces or adds "module" to "parent.children[]" array -// (for consistency with Node.js and userscript cache invalidation without "cache busters") -bool ScriptEngine::registerModuleWithParent(const QScriptValue& module, const QScriptValue& parent) { - auto children = parent.property("children"); - if (children.isArray()) { - auto key = module.property("id"); - auto length = children.property("length").toInt32(); - for (int i = 0; i < length; i++) { - if (children.property(i).property("id").strictlyEquals(key)) { - qCDebug(scriptengine_module) << key.toString() << " updating parent.children[" << i << "] = module"; - children.setProperty(i, module); - return true; - } - } - qCDebug(scriptengine_module) << key.toString() << " appending parent.children[" << length << "] = module"; - children.setProperty(length, module); - return true; - } else if (parent.isValid()) { - qCDebug(scriptengine_module) << "registerModuleWithParent -- unrecognized parent" << parent.toVariant().toString(); - } - return false; -} - -// creates a new JS "module" Object with default metadata properties -QScriptValue ScriptEngine::newModule(const QString& modulePath, const QScriptValue& parent) { - auto closure = newObject(); - auto exports = newObject(); - auto module = newObject(); - qCDebug(scriptengine_module) << "newModule" << modulePath << parent.property("filename").toString(); - - closure.setProperty("module", module, READONLY_PROP_FLAGS); - - // note: this becomes the "exports" free variable, so should not be set read only - closure.setProperty("exports", exports); - - // make the closure available to module instantiation - module.setProperty("__closure__", closure, READONLY_HIDDEN_PROP_FLAGS); - - // for consistency with Node.js Module - module.setProperty("id", modulePath, READONLY_PROP_FLAGS); - module.setProperty("filename", modulePath, READONLY_PROP_FLAGS); - module.setProperty("exports", exports); // not readonly - module.setProperty("loaded", false, READONLY_PROP_FLAGS); - module.setProperty("parent", parent, READONLY_PROP_FLAGS); - module.setProperty("children", newArray(), READONLY_PROP_FLAGS); - - // module.require is a bound version of require that always resolves relative to that module's path - auto boundRequire = QScriptEngine::evaluate("(function(id) { return Script.require(Script.require.resolve(id, this.filename)); })", "(boundRequire)"); - module.setProperty("require", boundRequire, READONLY_PROP_FLAGS); - - return module; -} - -// synchronously fetch a module's source code using BatchLoader -QVariantMap ScriptEngine::fetchModuleSource(const QString& modulePath, const bool forceDownload) { - using UrlMap = QMap; - auto scriptCache = DependencyManager::get(); - QVariantMap req; - qCDebug(scriptengine_module) << "require.fetchModuleSource: " << QUrl(modulePath).fileName() << QThread::currentThread(); - - auto onload = [=, &req](const UrlMap& data, const UrlMap& _status) { - auto url = modulePath; - auto status = _status[url]; - auto contents = data[url]; - qCDebug(scriptengine_module) << "require.fetchModuleSource.onload: " << QUrl(url).fileName() << status << QThread::currentThread(); - if (isStopping()) { - req["status"] = "Stopped"; - req["success"] = false; - } else { - req["url"] = url; - req["status"] = status; - req["success"] = ScriptCache::isSuccessStatus(status); - req["contents"] = contents; - } - }; - - if (forceDownload) { - qCDebug(scriptengine_module) << "require.requestScript -- clearing cache for" << modulePath; - scriptCache->deleteScript(modulePath); - } - BatchLoader* loader = new BatchLoader(QList({ modulePath })); - connect(loader, &BatchLoader::finished, this, onload); - connect(this, &QObject::destroyed, loader, &QObject::deleteLater); - // fail faster? (since require() blocks the engine thread while resolving dependencies) - const int MAX_RETRIES = 1; - - loader->start(MAX_RETRIES); - - if (!loader->isFinished()) { - QTimer monitor; - QEventLoop loop; - QObject::connect(loader, &BatchLoader::finished, this, [this, &monitor, &loop]{ - monitor.stop(); - loop.quit(); - }); - - // this helps detect the case where stop() is invoked during the download - // but not seen in time to abort processing in onload()... - connect(&monitor, &QTimer::timeout, this, [this, &loop, &loader]{ - if (isStopping()) { - loop.exit(-1); - } - }); - monitor.start(500); - loop.exec(); - } - loader->deleteLater(); - return req; -} - -// evaluate a pending module object using the fetched source code -QScriptValue ScriptEngine::instantiateModule(const QScriptValue& module, const QString& sourceCode) { - QScriptValue result; - auto modulePath = module.property("filename").toString(); - auto closure = module.property("__closure__"); - - qCDebug(scriptengine_module) << QString("require.instantiateModule: %1 / %2 bytes") - .arg(QUrl(modulePath).fileName()).arg(sourceCode.length()); - - if (module.property("content-type").toString() == "application/json") { - qCDebug(scriptengine_module) << "... parsing as JSON"; - closure.setProperty("__json", sourceCode); - result = evaluateInClosure(closure, { "module.exports = JSON.parse(__json)", modulePath }); - } else { - // scoped vars for consistency with Node.js - closure.setProperty("require", module.property("require")); - closure.setProperty("__filename", modulePath, READONLY_HIDDEN_PROP_FLAGS); - closure.setProperty("__dirname", QString(modulePath).replace(QRegExp("/[^/]*$"), ""), READONLY_HIDDEN_PROP_FLAGS); - result = evaluateInClosure(closure, { sourceCode, modulePath }); - } - maybeEmitUncaughtException(__FUNCTION__); - return result; -} - -// CommonJS/Node.js like require/module support -QScriptValue ScriptEngine::require(const QString& moduleId) { - qCDebug(scriptengine_module) << "ScriptEngine::require(" << moduleId.left(MAX_DEBUG_VALUE_LENGTH) << ")"; - if (!IS_THREADSAFE_INVOCATION(thread(), __FUNCTION__)) { - return unboundNullValue(); - } - - auto jsRequire = globalObject().property("Script").property("require"); - auto cacheMeta = jsRequire.data(); - auto cache = jsRequire.property("cache"); - auto parent = currentModule(); - - auto throwModuleError = [&](const QString& modulePath, const QScriptValue& error) { - cache.setProperty(modulePath, nullValue()); - if (!error.isNull()) { -#ifdef DEBUG_JS_MODULES - qCWarning(scriptengine_module) << "throwing module error:" << error.toString() << modulePath << error.property("stack").toString(); -#endif - raiseException(error); - } - maybeEmitUncaughtException("module"); - return unboundNullValue(); - }; - - // start by resolving the moduleId into a fully-qualified path/URL - QString modulePath = _requireResolve(moduleId); - if (modulePath.isNull() || hasUncaughtException()) { - // the resolver already threw an exception -- bail early - maybeEmitUncaughtException(__FUNCTION__); - return unboundNullValue(); - } - - // check the resolved path against the cache - auto module = cache.property(modulePath); - - // modules get cached in `Script.require.cache` and (similar to Node.js) users can access it - // to inspect particular entries and invalidate them by deleting the key: - // `delete Script.require.cache[Script.require.resolve(moduleId)];` - - // cacheMeta is just used right now to tell deleted keys apart from undefined ones - bool invalidateCache = module.isUndefined() && cacheMeta.property(moduleId).isValid(); - - // reset the cacheMeta record so invalidation won't apply next time, even if the module fails to load - cacheMeta.setProperty(modulePath, QScriptValue()); - - auto exports = module.property("exports"); - if (!invalidateCache && exports.isObject()) { - // we have found a cached module -- just need to possibly register it with current parent - qCDebug(scriptengine_module) << QString("require - using cached module '%1' for '%2' (loaded: %3)") - .arg(modulePath).arg(moduleId).arg(module.property("loaded").toString()); - registerModuleWithParent(module, parent); - maybeEmitUncaughtException("cached module"); - return exports; - } - - // bootstrap / register new empty module - module = newModule(modulePath, parent); - registerModuleWithParent(module, parent); - - // add it to the cache (this is done early so any cyclic dependencies pick up) - cache.setProperty(modulePath, module); - - // download the module source - auto req = fetchModuleSource(modulePath, invalidateCache); - - if (!req.contains("success") || !req["success"].toBool()) { - auto error = QString("error retrieving script (%1)").arg(req["status"].toString()); - return throwModuleError(modulePath, error); - } - -#if DEBUG_JS_MODULES - qCDebug(scriptengine_module) << "require.loaded: " << - QUrl(req["url"].toString()).fileName() << req["status"].toString(); -#endif - - auto sourceCode = req["contents"].toString(); - - if (QUrl(modulePath).fileName().endsWith(".json", Qt::CaseInsensitive)) { - module.setProperty("content-type", "application/json"); - } else { - module.setProperty("content-type", "application/javascript"); - } - - // evaluate the module - auto result = instantiateModule(module, sourceCode); - - if (result.isError() && !result.strictlyEquals(module.property("exports"))) { - qCWarning(scriptengine_module) << "-- result.isError --" << result.toString(); - return throwModuleError(modulePath, result); - } - - // mark as fully-loaded - module.setProperty("loaded", true, READONLY_PROP_FLAGS); - - // set up a new reference point for detecting cache key deletion - cacheMeta.setProperty(modulePath, module); - - qCDebug(scriptengine_module) << "//ScriptEngine::require(" << moduleId << ")"; - - maybeEmitUncaughtException(__FUNCTION__); - return module.property("exports"); + emit printedMessage(message); } // If a callback is specified, the included files will be loaded asynchronously and the callback will be called @@ -1706,9 +1309,6 @@ QScriptValue ScriptEngine::require(const QString& moduleId) { // If no callback is specified, the included files will be loaded synchronously and will block execution until // all of the files have finished loading. void ScriptEngine::include(const QStringList& includeFiles, QScriptValue callback) { - if (!IS_THREADSAFE_INVOCATION(thread(), __FUNCTION__)) { - return; - } if (DependencyManager::get()->isStopped()) { scriptWarningMessage("Script.include() while shutting down is ignored... includeFiles:" + includeFiles.join(",") + "parent script:" + getFilename()); @@ -1771,7 +1371,7 @@ void ScriptEngine::include(const QStringList& includeFiles, QScriptValue callbac doWithEnvironment(capturedEntityIdentifier, capturedSandboxURL, operation); if (hasUncaughtException()) { - emit unhandledException(cloneUncaughtException("evaluateInclude")); + emit unhandledException(cloneUncaughtException(__FUNCTION__)); clearExceptions(); } } else { @@ -1818,9 +1418,6 @@ void ScriptEngine::include(const QString& includeFile, QScriptValue callback) { // as a stand-alone script. To accomplish this, the ScriptEngine class just emits a signal which // the Application or other context will connect to in order to know to actually load the script void ScriptEngine::load(const QString& loadFile) { - if (!IS_THREADSAFE_INVOCATION(thread(), __FUNCTION__)) { - return; - } if (DependencyManager::get()->isStopped()) { scriptWarningMessage("Script.load() while shutting down is ignored... loadFile:" + loadFile + "parent script:" + getFilename()); @@ -1890,52 +1487,6 @@ void ScriptEngine::updateEntityScriptStatus(const EntityItemID& entityID, const emit entityScriptDetailsUpdated(); } -QVariant ScriptEngine::cloneEntityScriptDetails(const EntityItemID& entityID) { - static const QVariant NULL_VARIANT { qVariantFromValue((QObject*)nullptr) }; - QVariantMap map; - if (entityID.isNull()) { - // TODO: find better way to report JS Error across thread/process boundaries - map["isError"] = true; - map["errorInfo"] = "Error: getEntityScriptDetails -- invalid entityID"; - } else { -#ifdef DEBUG_ENTITY_STATES - qDebug() << "cloneEntityScriptDetails" << entityID << QThread::currentThread(); -#endif - EntityScriptDetails scriptDetails; - if (getEntityScriptDetails(entityID, scriptDetails)) { -#ifdef DEBUG_ENTITY_STATES - qDebug() << "gotEntityScriptDetails" << scriptDetails.status << QThread::currentThread(); -#endif - map["isRunning"] = isEntityScriptRunning(entityID); - map["status"] = EntityScriptStatus_::valueToKey(scriptDetails.status).toLower(); - map["errorInfo"] = scriptDetails.errorInfo; - map["entityID"] = entityID.toString(); -#ifdef DEBUG_ENTITY_STATES - { - auto debug = QVariantMap(); - debug["script"] = scriptDetails.scriptText; - debug["scriptObject"] = scriptDetails.scriptObject.toVariant(); - debug["lastModified"] = (qlonglong)scriptDetails.lastModified; - debug["sandboxURL"] = scriptDetails.definingSandboxURL; - map["debug"] = debug; - } -#endif - } else { -#ifdef DEBUG_ENTITY_STATES - qDebug() << "!gotEntityScriptDetails" << QThread::currentThread(); -#endif - map["isError"] = true; - map["errorInfo"] = "Entity script details unavailable"; - map["entityID"] = entityID.toString(); - } - } - return map; -} - -QFuture ScriptEngine::getLocalEntityScriptDetails(const EntityItemID& entityID) { - return QtConcurrent::run(this, &ScriptEngine::cloneEntityScriptDetails, entityID); -} - bool ScriptEngine::getEntityScriptDetails(const EntityItemID& entityID, EntityScriptDetails &details) const { auto it = _entityScripts.constFind(entityID); if (it == _entityScripts.constEnd()) { @@ -2074,10 +1625,10 @@ void ScriptEngine::loadEntityScript(const EntityItemID& entityID, const QString& auto scriptCache = DependencyManager::get(); // note: see EntityTreeRenderer.cpp for shared pointer lifecycle management - QWeakPointer weakRef(sharedFromThis()); + QWeakPointer weakRef(sharedFromThis()); scriptCache->getScriptContents(entityScript, [this, weakRef, entityScript, entityID](const QString& url, const QString& contents, bool isURL, bool success, const QString& status) { - QSharedPointer strongRef(weakRef); + QSharedPointer strongRef(weakRef); if (!strongRef) { qCWarning(scriptengine) << "loadEntityScript.contentAvailable -- ScriptEngine was deleted during getScriptContents!!"; return; @@ -2196,12 +1747,13 @@ void ScriptEngine::entityScriptContentAvailable(const EntityItemID& entityID, co timeout.setSingleShot(true); timeout.start(SANDBOX_TIMEOUT); connect(&timeout, &QTimer::timeout, [&sandbox, SANDBOX_TIMEOUT, scriptOrURL]{ + auto context = sandbox.currentContext(); + if (context) { qCDebug(scriptengine) << "ScriptEngine::entityScriptContentAvailable timeout(" << scriptOrURL << ")"; // Guard against infinite loops and non-performant code - sandbox.raiseException( - sandbox.makeError(QString("Timed out (entity constructors are limited to %1ms)").arg(SANDBOX_TIMEOUT)) - ); + context->throwError(QString("Timed out (entity constructors are limited to %1ms)").arg(SANDBOX_TIMEOUT)); + } }); testConstructor = sandbox.evaluate(program); @@ -2217,7 +1769,7 @@ void ScriptEngine::entityScriptContentAvailable(const EntityItemID& entityID, co if (exception.isError()) { // create a local copy using makeError to decouple from the sandbox engine exception = makeError(exception); - setError(formatException(exception, _enableExtendedJSExceptions.get()), EntityScriptStatus::ERROR_RUNNING_SCRIPT); + setError(formatException(exception), EntityScriptStatus::ERROR_RUNNING_SCRIPT); emit unhandledException(exception); return; } @@ -2229,8 +1781,9 @@ void ScriptEngine::entityScriptContentAvailable(const EntityItemID& entityID, co testConstructorType = "empty"; } QString testConstructorValue = testConstructor.toString(); - if (testConstructorValue.size() > MAX_DEBUG_VALUE_LENGTH) { - testConstructorValue = testConstructorValue.mid(0, MAX_DEBUG_VALUE_LENGTH) + "..."; + const int maxTestConstructorValueSize = 80; + if (testConstructorValue.size() > maxTestConstructorValueSize) { + testConstructorValue = testConstructorValue.mid(0, maxTestConstructorValueSize) + "..."; } auto message = QString("failed to load entity script -- expected a function, got %1, %2") .arg(testConstructorType).arg(testConstructorValue); @@ -2268,7 +1821,7 @@ void ScriptEngine::entityScriptContentAvailable(const EntityItemID& entityID, co if (entityScriptObject.isError()) { auto exception = entityScriptObject; - setError(formatException(exception, _enableExtendedJSExceptions.get()), EntityScriptStatus::ERROR_RUNNING_SCRIPT); + setError(formatException(exception), EntityScriptStatus::ERROR_RUNNING_SCRIPT); emit unhandledException(exception); return; } @@ -2291,7 +1844,7 @@ void ScriptEngine::entityScriptContentAvailable(const EntityItemID& entityID, co processDeferredEntityLoads(entityScript, entityID); } -void ScriptEngine::unloadEntityScript(const EntityItemID& entityID, bool shouldRemoveFromMap) { +void ScriptEngine::unloadEntityScript(const EntityItemID& entityID) { if (QThread::currentThread() != thread()) { #ifdef THREAD_DEBUGGING qCDebug(scriptengine) << "*** WARNING *** ScriptEngine::unloadEntityScript() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] " @@ -2299,8 +1852,7 @@ void ScriptEngine::unloadEntityScript(const EntityItemID& entityID, bool shouldR #endif QMetaObject::invokeMethod(this, "unloadEntityScript", - Q_ARG(const EntityItemID&, entityID), - Q_ARG(bool, shouldRemoveFromMap)); + Q_ARG(const EntityItemID&, entityID)); return; } #ifdef THREAD_DEBUGGING @@ -2312,17 +1864,10 @@ void ScriptEngine::unloadEntityScript(const EntityItemID& entityID, bool shouldR const EntityScriptDetails &oldDetails = _entityScripts[entityID]; if (isEntityScriptRunning(entityID)) { callEntityScriptMethod(entityID, "unload"); - } -#ifdef DEBUG_ENTITY_STATES - else { + } else { qCDebug(scriptengine) << "unload called while !running" << entityID << oldDetails.status; } -#endif - if (shouldRemoveFromMap) { - // this was a deleted entity, we've been asked to remove it from the map - _entityScripts.remove(entityID); - emit entityScriptDetailsUpdated(); - } else if (oldDetails.status != EntityScriptStatus::UNLOADED) { + if (oldDetails.status != EntityScriptStatus::UNLOADED) { EntityScriptDetails newDetails; newDetails.status = EntityScriptStatus::UNLOADED; newDetails.lastModified = QDateTime::currentMSecsSinceEpoch(); @@ -2330,7 +1875,6 @@ void ScriptEngine::unloadEntityScript(const EntityItemID& entityID, bool shouldR newDetails.scriptText = oldDetails.scriptText; setEntityScriptDetails(entityID, newDetails); } - stopAllTimersForEntityScript(entityID); { // FIXME: shouldn't have to do this here, but currently something seems to be firing unloads moments after firing initial load requests @@ -2409,7 +1953,10 @@ void ScriptEngine::doWithEnvironment(const EntityItemID& entityID, const QUrl& s #else operation(); #endif - maybeEmitUncaughtException(!entityID.isNull() ? entityID.toString() : __FUNCTION__); + if (!isEvaluating() && hasUncaughtException()) { + emit unhandledException(cloneUncaughtException(!entityID.isNull() ? entityID.toString() : __FUNCTION__)); + clearExceptions(); + } currentEntityIdentifier = oldIdentifier; currentSandboxURL = oldSandboxURL; } diff --git a/libraries/script-engine/src/ScriptEngine.h b/libraries/script-engine/src/ScriptEngine.h index 5ea8d052e9..b988ccfe90 100644 --- a/libraries/script-engine/src/ScriptEngine.h +++ b/libraries/script-engine/src/ScriptEngine.h @@ -41,7 +41,6 @@ #include "ScriptCache.h" #include "ScriptUUID.h" #include "Vec3.h" -#include "SettingHandle.h" class QScriptEngineDebugger; @@ -79,7 +78,7 @@ public: QUrl definingSandboxURL { QUrl("about:EntityScript") }; }; -class ScriptEngine : public BaseScriptEngine, public EntitiesScriptEngineProvider { +class ScriptEngine : public BaseScriptEngine, public EntitiesScriptEngineProvider, public QEnableSharedFromThis { Q_OBJECT Q_PROPERTY(QString context READ getContext) public: @@ -138,8 +137,6 @@ public: /// evaluate some code in the context of the ScriptEngine and return the result Q_INVOKABLE QScriptValue evaluate(const QString& program, const QString& fileName, int lineNumber = 1); // this is also used by the script tool widget - Q_INVOKABLE QScriptValue evaluateInClosure(const QScriptValue& locals, const QScriptProgram& program); - /// if the script engine is not already running, this will download the URL and start the process of seting it up /// to run... NOTE - this is used by Application currently to load the url. We don't really want it to be exposed /// to scripts. we may not need this to be invokable @@ -160,16 +157,6 @@ public: Q_INVOKABLE void include(const QStringList& includeFiles, QScriptValue callback = QScriptValue()); Q_INVOKABLE void include(const QString& includeFile, QScriptValue callback = QScriptValue()); - //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // MODULE related methods - Q_INVOKABLE QScriptValue require(const QString& moduleId); - Q_INVOKABLE void resetModuleCache(bool deleteScriptCache = false); - QScriptValue currentModule(); - bool registerModuleWithParent(const QScriptValue& module, const QScriptValue& parent); - QScriptValue newModule(const QString& modulePath, const QScriptValue& parent = QScriptValue()); - QVariantMap fetchModuleSource(const QString& modulePath, const bool forceDownload = false); - QScriptValue instantiateModule(const QScriptValue& module, const QString& sourceCode); - Q_INVOKABLE QObject* setInterval(const QScriptValue& function, int intervalMS); Q_INVOKABLE QObject* setTimeout(const QScriptValue& function, int timeoutMS); Q_INVOKABLE void clearInterval(QObject* timer) { stopTimer(reinterpret_cast(timer)); } @@ -183,10 +170,8 @@ public: Q_INVOKABLE bool isEntityScriptRunning(const EntityItemID& entityID) { return _entityScripts.contains(entityID) && _entityScripts[entityID].status == EntityScriptStatus::RUNNING; } - QVariant cloneEntityScriptDetails(const EntityItemID& entityID); - QFuture getLocalEntityScriptDetails(const EntityItemID& entityID) override; Q_INVOKABLE void loadEntityScript(const EntityItemID& entityID, const QString& entityScript, bool forceRedownload); - Q_INVOKABLE void unloadEntityScript(const EntityItemID& entityID, bool shouldRemoveFromMap = false); // will call unload method + Q_INVOKABLE void unloadEntityScript(const EntityItemID& entityID); // will call unload method Q_INVOKABLE void unloadAllEntityScripts(); Q_INVOKABLE void callEntityScriptMethod(const EntityItemID& entityID, const QString& methodName, const QStringList& params = QStringList()) override; @@ -236,10 +221,10 @@ signals: void scriptEnding(); void finished(const QString& fileNameString, ScriptEngine* engine); void cleanupMenuItem(const QString& menuItemString); - void printedMessage(const QString& message, const QString& scriptName); - void errorMessage(const QString& message, const QString& scriptName); - void warningMessage(const QString& message, const QString& scriptName); - void infoMessage(const QString& message, const QString& scriptName); + void printedMessage(const QString& message); + void errorMessage(const QString& message); + void warningMessage(const QString& message); + void infoMessage(const QString& message); void runningStateChanged(); void loadScript(const QString& scriptName, bool isUserLoaded); void reloadScript(const QString& scriptName, bool isUserLoaded); @@ -252,9 +237,6 @@ signals: protected: void init(); Q_INVOKABLE void executeOnScriptThread(std::function function, const Qt::ConnectionType& type = Qt::QueuedConnection ); - // note: this is not meant to be called directly, but just to have QMetaObject take care of wiring it up in general; - // then inside of init() we just have to do "Script.require.resolve = Script._requireResolve;" - Q_INVOKABLE QString _requireResolve(const QString& moduleId, const QString& relativeTo = QString()); QString logException(const QScriptValue& exception); void timerFired(); @@ -308,16 +290,11 @@ protected: AssetScriptingInterface _assetScriptingInterface{ this }; - std::function _emitScriptUpdates{ []() { return true; } }; + std::function _emitScriptUpdates{ [](){ return true; } }; std::recursive_mutex _lock; std::chrono::microseconds _totalTimerExecution { 0 }; - - static const QString _SETTINGS_ENABLE_EXTENDED_MODULE_COMPAT; - static const QString _SETTINGS_ENABLE_EXTENDED_EXCEPTIONS; - - Setting::Handle _enableExtendedJSExceptions { _SETTINGS_ENABLE_EXTENDED_EXCEPTIONS, true }; }; #endif // hifi_ScriptEngine_h diff --git a/libraries/script-engine/src/ScriptEngineLogging.cpp b/libraries/script-engine/src/ScriptEngineLogging.cpp index 392bc05129..2e5d293728 100644 --- a/libraries/script-engine/src/ScriptEngineLogging.cpp +++ b/libraries/script-engine/src/ScriptEngineLogging.cpp @@ -12,4 +12,3 @@ #include "ScriptEngineLogging.h" Q_LOGGING_CATEGORY(scriptengine, "hifi.scriptengine") -Q_LOGGING_CATEGORY(scriptengine_module, "hifi.scriptengine.module") diff --git a/libraries/script-engine/src/ScriptEngineLogging.h b/libraries/script-engine/src/ScriptEngineLogging.h index 62e46632a6..0e614dd5bf 100644 --- a/libraries/script-engine/src/ScriptEngineLogging.h +++ b/libraries/script-engine/src/ScriptEngineLogging.h @@ -15,7 +15,6 @@ #include Q_DECLARE_LOGGING_CATEGORY(scriptengine) -Q_DECLARE_LOGGING_CATEGORY(scriptengine_module) #endif // hifi_ScriptEngineLogging_h diff --git a/libraries/script-engine/src/ScriptEngines.cpp b/libraries/script-engine/src/ScriptEngines.cpp index 88b0e0b7b5..57887d2d96 100644 --- a/libraries/script-engine/src/ScriptEngines.cpp +++ b/libraries/script-engine/src/ScriptEngines.cpp @@ -34,24 +34,34 @@ ScriptsModel& getScriptsModel() { return scriptsModel; } -void ScriptEngines::onPrintedMessage(const QString& message, const QString& scriptName) { +void ScriptEngines::onPrintedMessage(const QString& message) { + auto scriptEngine = qobject_cast(sender()); + auto scriptName = scriptEngine ? scriptEngine->getFilename() : ""; emit printedMessage(message, scriptName); } -void ScriptEngines::onErrorMessage(const QString& message, const QString& scriptName) { +void ScriptEngines::onErrorMessage(const QString& message) { + auto scriptEngine = qobject_cast(sender()); + auto scriptName = scriptEngine ? scriptEngine->getFilename() : ""; emit errorMessage(message, scriptName); } -void ScriptEngines::onWarningMessage(const QString& message, const QString& scriptName) { +void ScriptEngines::onWarningMessage(const QString& message) { + auto scriptEngine = qobject_cast(sender()); + auto scriptName = scriptEngine ? scriptEngine->getFilename() : ""; emit warningMessage(message, scriptName); } -void ScriptEngines::onInfoMessage(const QString& message, const QString& scriptName) { +void ScriptEngines::onInfoMessage(const QString& message) { + auto scriptEngine = qobject_cast(sender()); + auto scriptName = scriptEngine ? scriptEngine->getFilename() : ""; emit infoMessage(message, scriptName); } void ScriptEngines::onErrorLoadingScript(const QString& url) { - emit errorLoadingScript(url); + auto scriptEngine = qobject_cast(sender()); + auto scriptName = scriptEngine ? scriptEngine->getFilename() : ""; + emit errorLoadingScript(url, scriptName); } ScriptEngines::ScriptEngines(ScriptEngine::Context context) diff --git a/libraries/script-engine/src/ScriptEngines.h b/libraries/script-engine/src/ScriptEngines.h index 63b7e8f11c..2fadfc81f8 100644 --- a/libraries/script-engine/src/ScriptEngines.h +++ b/libraries/script-engine/src/ScriptEngines.h @@ -79,13 +79,13 @@ signals: void errorMessage(const QString& message, const QString& engineName); void warningMessage(const QString& message, const QString& engineName); void infoMessage(const QString& message, const QString& engineName); - void errorLoadingScript(const QString& url); + void errorLoadingScript(const QString& url, const QString& engineName); public slots: - void onPrintedMessage(const QString& message, const QString& scriptName); - void onErrorMessage(const QString& message, const QString& scriptName); - void onWarningMessage(const QString& message, const QString& scriptName); - void onInfoMessage(const QString& message, const QString& scriptName); + void onPrintedMessage(const QString& message); + void onErrorMessage(const QString& message); + void onWarningMessage(const QString& message); + void onInfoMessage(const QString& message); void onErrorLoadingScript(const QString& url); protected slots: diff --git a/libraries/shared/src/BaseScriptEngine.h b/libraries/shared/src/BaseScriptEngine.h deleted file mode 100644 index 138e46fafa..0000000000 --- a/libraries/shared/src/BaseScriptEngine.h +++ /dev/null @@ -1,90 +0,0 @@ -// -// BaseScriptEngine.h -// libraries/script-engine/src -// -// Created by Timothy Dedischew on 02/01/17. -// Copyright 2017 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#ifndef hifi_BaseScriptEngine_h -#define hifi_BaseScriptEngine_h - -#include -#include -#include - -// common base class for extending QScriptEngine itself -class BaseScriptEngine : public QScriptEngine, public QEnableSharedFromThis { - Q_OBJECT -public: - static const QString SCRIPT_EXCEPTION_FORMAT; - static const QString SCRIPT_BACKTRACE_SEP; - - // threadsafe "unbound" version of QScriptEngine::nullValue() - static const QScriptValue unboundNullValue() { return QScriptValue(0, QScriptValue::NullValue); } - - BaseScriptEngine() {} - - Q_INVOKABLE QScriptValue lintScript(const QString& sourceCode, const QString& fileName, const int lineNumber = 1); - Q_INVOKABLE QScriptValue makeError(const QScriptValue& other = QScriptValue(), const QString& type = "Error"); - Q_INVOKABLE QString formatException(const QScriptValue& exception, bool includeExtendedDetails); - - QScriptValue cloneUncaughtException(const QString& detail = QString()); - QScriptValue evaluateInClosure(const QScriptValue& locals, const QScriptProgram& program); - - // if there is a pending exception and we are at the top level (non-recursive) stack frame, this emits and resets it - bool maybeEmitUncaughtException(const QString& debugHint = QString()); - - // if the currentContext() is valid then throw the passed exception; otherwise, immediately emit it. - // note: this is used in cases where C++ code might call into JS API methods directly - bool raiseException(const QScriptValue& exception); - - // helper to detect and log warnings when other code invokes QScriptEngine/BaseScriptEngine in thread-unsafe ways - static bool IS_THREADSAFE_INVOCATION(const QThread *thread, const QString& method); -signals: - void unhandledException(const QScriptValue& exception); - -protected: - // like `newFunction`, but allows mapping inline C++ lambdas with captures as callable QScriptValues - // even though the context/engine parameters are redundant in most cases, the function signature matches `newFunction` - // anyway so that newLambdaFunction can be used to rapidly prototype / test utility APIs and then if becoming - // permanent more easily promoted into regular static newFunction scenarios. - QScriptValue newLambdaFunction(std::function operation, const QScriptValue& data = QScriptValue(), const QScriptEngine::ValueOwnership& ownership = QScriptEngine::AutoOwnership); - -#ifdef DEBUG_JS - static void _debugDump(const QString& header, const QScriptValue& object, const QString& footer = QString()); -#endif -}; - -// Standardized CPS callback helpers (see: http://fredkschott.com/post/2014/03/understanding-error-first-callbacks-in-node-js/) -// These two helpers allow async JS APIs that use a callback parameter to be more friendly to scripters by accepting thisObject -// context and adopting a consistent and intuitable callback signature: -// function callback(err, result) { if (err) { ... } else { /* do stuff with result */ } } -// -// To use, first pass the user-specified callback args in the same order used with optionally-scoped Qt signal connections: -// auto handler = makeScopedHandlerObject(scopeOrCallback, optionalMethodOrName); -// And then invoke the scoped handler later per CPS conventions: -// auto result = callScopedHandlerObject(handler, err, result); -QScriptValue makeScopedHandlerObject(QScriptValue scopeOrCallback, QScriptValue methodOrName); -QScriptValue callScopedHandlerObject(QScriptValue handler, QScriptValue err, QScriptValue result); - -// Lambda helps create callable QScriptValues out of std::functions: -// (just meant for use from within the script engine itself) -class Lambda : public QObject { - Q_OBJECT -public: - Lambda(QScriptEngine *engine, std::function operation, QScriptValue data); - ~Lambda(); - public slots: - QScriptValue call(); - QString toString() const; -private: - QScriptEngine* engine; - std::function operation; - QScriptValue data; -}; - -#endif // hifi_BaseScriptEngine_h diff --git a/libraries/shared/src/GLMHelpers.h b/libraries/shared/src/GLMHelpers.h index deb87930fc..609c3ab08b 100644 --- a/libraries/shared/src/GLMHelpers.h +++ b/libraries/shared/src/GLMHelpers.h @@ -50,7 +50,7 @@ using glm::quat; // this is where the coordinate system is represented const glm::vec3 IDENTITY_RIGHT = glm::vec3( 1.0f, 0.0f, 0.0f); const glm::vec3 IDENTITY_UP = glm::vec3( 0.0f, 1.0f, 0.0f); -const glm::vec3 IDENTITY_FORWARD = glm::vec3( 0.0f, 0.0f,-1.0f); +const glm::vec3 IDENTITY_FRONT = glm::vec3( 0.0f, 0.0f,-1.0f); glm::quat safeMix(const glm::quat& q1, const glm::quat& q2, float alpha); diff --git a/libraries/shared/src/HifiConfigVariantMap.cpp b/libraries/shared/src/HifiConfigVariantMap.cpp index d0fb14e104..5be6b2cd74 100644 --- a/libraries/shared/src/HifiConfigVariantMap.cpp +++ b/libraries/shared/src/HifiConfigVariantMap.cpp @@ -21,7 +21,7 @@ #include #include -#include "PathUtils.h" +#include "ServerPathUtils.h" #include "SharedLogging.h" QVariantMap HifiConfigVariantMap::mergeCLParametersWithJSONConfig(const QStringList& argumentList) { @@ -127,7 +127,7 @@ void HifiConfigVariantMap::loadConfig(const QStringList& argumentList) { _userConfigFilename = argumentList[userConfigIndex + 1]; } else { // we weren't passed a user config path - _userConfigFilename = PathUtils::getAppDataFilePath(USER_CONFIG_FILE_NAME); + _userConfigFilename = ServerPathUtils::getDataFilePath(USER_CONFIG_FILE_NAME); // as of 1/19/2016 this path was moved so we attempt a migration for first run post migration here @@ -153,7 +153,7 @@ void HifiConfigVariantMap::loadConfig(const QStringList& argumentList) { // we have the old file and not the new file - time to copy the file // make the destination directory if it doesn't exist - auto dataDirectory = PathUtils::getAppDataPath(); + auto dataDirectory = ServerPathUtils::getDataDirectory(); if (QDir().mkpath(dataDirectory)) { if (oldConfigFile.copy(_userConfigFilename)) { qCDebug(shared) << "Migrated config file from" << oldConfigFilename << "to" << _userConfigFilename; diff --git a/libraries/shared/src/PathUtils.cpp b/libraries/shared/src/PathUtils.cpp index 6e3acc5e99..265eaaa5b6 100644 --- a/libraries/shared/src/PathUtils.cpp +++ b/libraries/shared/src/PathUtils.cpp @@ -30,20 +30,18 @@ const QString& PathUtils::resourcesPath() { return staticResourcePath; } -QString PathUtils::getAppDataPath() { - return QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/"; -} +QString PathUtils::getRootDataDirectory() { + auto dataPath = QStandardPaths::writableLocation(QStandardPaths::HomeLocation); -QString PathUtils::getAppLocalDataPath() { - return QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation) + "/"; -} +#ifdef Q_OS_WIN + dataPath += "/AppData/Roaming/"; +#elif defined(Q_OS_OSX) + dataPath += "/Library/Application Support/"; +#else + dataPath += "/.local/share/"; +#endif -QString PathUtils::getAppDataFilePath(const QString& filename) { - return QDir(getAppDataPath()).absoluteFilePath(filename); -} - -QString PathUtils::getAppLocalDataFilePath(const QString& filename) { - return QDir(getAppLocalDataPath()).absoluteFilePath(filename); + return dataPath; } QString fileNameWithoutExtension(const QString& fileName, const QVector possibleExtensions) { diff --git a/libraries/shared/src/PathUtils.h b/libraries/shared/src/PathUtils.h index a7af44221c..1f7dcbe466 100644 --- a/libraries/shared/src/PathUtils.h +++ b/libraries/shared/src/PathUtils.h @@ -27,12 +27,7 @@ class PathUtils : public QObject, public Dependency { Q_PROPERTY(QString resources READ resourcesPath) public: static const QString& resourcesPath(); - - static QString getAppDataPath(); - static QString getAppLocalDataPath(); - - static QString getAppDataFilePath(const QString& filename); - static QString getAppLocalDataFilePath(const QString& filename); + static QString getRootDataDirectory(); static Qt::CaseSensitivity getFSCaseSensitivity(); static QString stripFilename(const QUrl& url); diff --git a/libraries/shared/src/RenderArgs.h b/libraries/shared/src/RenderArgs.h index 50722c0deb..b2c05b0548 100644 --- a/libraries/shared/src/RenderArgs.h +++ b/libraries/shared/src/RenderArgs.h @@ -122,7 +122,6 @@ public: gpu::Batch* _batch = nullptr; std::shared_ptr _whiteTexture; - uint32_t _globalShapeKey { 0 }; bool _enableTexturing { true }; RenderDetails _details; diff --git a/libraries/shared/src/ServerPathUtils.cpp b/libraries/shared/src/ServerPathUtils.cpp new file mode 100644 index 0000000000..cf52875c5f --- /dev/null +++ b/libraries/shared/src/ServerPathUtils.cpp @@ -0,0 +1,31 @@ +// +// ServerPathUtils.cpp +// libraries/shared/src +// +// Created by Ryan Huffman on 01/12/16. +// Copyright 2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +#include "ServerPathUtils.h" + +#include +#include +#include +#include + +#include "PathUtils.h" + +QString ServerPathUtils::getDataDirectory() { + auto dataPath = PathUtils::getRootDataDirectory(); + + dataPath += qApp->organizationName() + "/" + qApp->applicationName(); + + return QDir::cleanPath(dataPath); +} + +QString ServerPathUtils::getDataFilePath(QString filename) { + return QDir(getDataDirectory()).absoluteFilePath(filename); +} + diff --git a/libraries/shared/src/ServerPathUtils.h b/libraries/shared/src/ServerPathUtils.h new file mode 100644 index 0000000000..28a9a71f0d --- /dev/null +++ b/libraries/shared/src/ServerPathUtils.h @@ -0,0 +1,22 @@ +// +// ServerPathUtils.h +// libraries/shared/src +// +// Created by Ryan Huffman on 01/12/16. +// Copyright 2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_ServerPathUtils_h +#define hifi_ServerPathUtils_h + +#include + +namespace ServerPathUtils { + QString getDataDirectory(); + QString getDataFilePath(QString filename); +} + +#endif // hifi_ServerPathUtils_h \ No newline at end of file diff --git a/libraries/shared/src/ViewFrustum.cpp b/libraries/shared/src/ViewFrustum.cpp index 7e4f64686b..a0b7d17e46 100644 --- a/libraries/shared/src/ViewFrustum.cpp +++ b/libraries/shared/src/ViewFrustum.cpp @@ -31,7 +31,7 @@ void ViewFrustum::setOrientation(const glm::quat& orientationAsQuaternion) { _orientation = orientationAsQuaternion; _right = glm::vec3(orientationAsQuaternion * glm::vec4(IDENTITY_RIGHT, 0.0f)); _up = glm::vec3(orientationAsQuaternion * glm::vec4(IDENTITY_UP, 0.0f)); - _direction = glm::vec3(orientationAsQuaternion * glm::vec4(IDENTITY_FORWARD, 0.0f)); + _direction = glm::vec3(orientationAsQuaternion * glm::vec4(IDENTITY_FRONT, 0.0f)); _view = glm::translate(mat4(), _position) * glm::mat4_cast(_orientation); } diff --git a/libraries/shared/src/ViewFrustum.h b/libraries/shared/src/ViewFrustum.h index 221b0b5a07..9a6cb9ab68 100644 --- a/libraries/shared/src/ViewFrustum.h +++ b/libraries/shared/src/ViewFrustum.h @@ -153,7 +153,7 @@ private: glm::quat _orientation; // orientation in world-frame // calculated from orientation - glm::vec3 _direction = IDENTITY_FORWARD; + glm::vec3 _direction = IDENTITY_FRONT; glm::vec3 _up = IDENTITY_UP; glm::vec3 _right = IDENTITY_RIGHT; diff --git a/libraries/shared/src/shared/Storage.cpp b/libraries/shared/src/shared/Storage.cpp deleted file mode 100644 index 3c46347a49..0000000000 --- a/libraries/shared/src/shared/Storage.cpp +++ /dev/null @@ -1,92 +0,0 @@ -// -// Created by Bradley Austin Davis on 2016/02/17 -// 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 -// - -#include "Storage.h" - -#include -#include -#include - -Q_LOGGING_CATEGORY(storagelogging, "hifi.core.storage") - -using namespace storage; - -ViewStorage::ViewStorage(const storage::StoragePointer& owner, size_t size, const uint8_t* data) - : _owner(owner), _size(size), _data(data) {} - -StoragePointer Storage::createView(size_t viewSize, size_t offset) const { - auto selfSize = size(); - if (0 == viewSize) { - viewSize = selfSize; - } - if ((viewSize + offset) > selfSize) { - throw std::runtime_error("Invalid mapping range"); - } - return std::make_shared(shared_from_this(), viewSize, data() + offset); -} - -StoragePointer Storage::toMemoryStorage() const { - return std::make_shared(size(), data()); -} - -StoragePointer Storage::toFileStorage(const QString& filename) const { - return FileStorage::create(filename, size(), data()); -} - -MemoryStorage::MemoryStorage(size_t size, const uint8_t* data) { - _data.resize(size); - if (data) { - memcpy(_data.data(), data, size); - } -} - -StoragePointer FileStorage::create(const QString& filename, size_t size, const uint8_t* data) { - QFile file(filename); - if (!file.open(QFile::ReadWrite | QIODevice::Truncate)) { - throw std::runtime_error("Unable to open file for writing"); - } - if (!file.resize(size)) { - throw std::runtime_error("Unable to resize file"); - } - { - auto mapped = file.map(0, size); - if (!mapped) { - throw std::runtime_error("Unable to map file"); - } - memcpy(mapped, data, size); - if (!file.unmap(mapped)) { - throw std::runtime_error("Unable to unmap file"); - } - } - file.close(); - return std::make_shared(filename); -} - -FileStorage::FileStorage(const QString& filename) : _file(filename) { - if (_file.open(QFile::ReadOnly)) { - _mapped = _file.map(0, _file.size()); - if (_mapped) { - _valid = true; - } else { - qCWarning(storagelogging) << "Failed to map file " << filename; - } - } else { - qCWarning(storagelogging) << "Failed to open file " << filename; - } -} - -FileStorage::~FileStorage() { - if (_mapped) { - if (!_file.unmap(_mapped)) { - throw std::runtime_error("Unable to unmap file"); - } - } - if (_file.isOpen()) { - _file.close(); - } -} diff --git a/libraries/shared/src/shared/Storage.h b/libraries/shared/src/shared/Storage.h deleted file mode 100644 index 306984040f..0000000000 --- a/libraries/shared/src/shared/Storage.h +++ /dev/null @@ -1,82 +0,0 @@ -// -// Created by Bradley Austin Davis on 2016/02/17 -// 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_Storage_h -#define hifi_Storage_h - -#include -#include -#include -#include -#include - -namespace storage { - class Storage; - using StoragePointer = std::shared_ptr; - - class Storage : public std::enable_shared_from_this { - public: - virtual ~Storage() {} - virtual const uint8_t* data() const = 0; - virtual size_t size() const = 0; - virtual operator bool() const { return true; } - - StoragePointer createView(size_t size = 0, size_t offset = 0) const; - StoragePointer toFileStorage(const QString& filename) const; - StoragePointer toMemoryStorage() const; - - // Aliases to prevent having to re-write a ton of code - inline size_t getSize() const { return size(); } - inline const uint8_t* readData() const { return data(); } - }; - - class MemoryStorage : public Storage { - public: - MemoryStorage(size_t size, const uint8_t* data = nullptr); - const uint8_t* data() const override { return _data.data(); } - uint8_t* data() { return _data.data(); } - size_t size() const override { return _data.size(); } - operator bool() const override { return true; } - private: - std::vector _data; - }; - - class FileStorage : public Storage { - public: - static StoragePointer create(const QString& filename, size_t size, const uint8_t* data); - FileStorage(const QString& filename); - ~FileStorage(); - // Prevent copying - FileStorage(const FileStorage& other) = delete; - FileStorage& operator=(const FileStorage& other) = delete; - - const uint8_t* data() const override { return _mapped; } - size_t size() const override { return _file.size(); } - operator bool() const override { return _valid; } - private: - bool _valid { false }; - QFile _file; - uint8_t* _mapped { nullptr }; - }; - - class ViewStorage : public Storage { - public: - ViewStorage(const storage::StoragePointer& owner, size_t size, const uint8_t* data); - const uint8_t* data() const override { return _data; } - size_t size() const override { return _size; } - operator bool() const override { return *_owner; } - private: - const storage::StoragePointer _owner; - const size_t _size; - const uint8_t* _data; - }; - -} - -#endif // hifi_Storage_h diff --git a/libraries/ui/src/ui/Menu.cpp b/libraries/ui/src/ui/Menu.cpp index a793942056..f68fff0204 100644 --- a/libraries/ui/src/ui/Menu.cpp +++ b/libraries/ui/src/ui/Menu.cpp @@ -470,8 +470,8 @@ void Menu::removeSeparator(const QString& menuName, const QString& separatorName if (menu) { int textAt = findPositionOfMenuItem(menu, separatorName); QList menuActions = menu->actions(); + QAction* separatorText = menuActions[textAt]; if (textAt > 0 && textAt < menuActions.size()) { - QAction* separatorText = menuActions[textAt]; QAction* separatorLine = menuActions[textAt - 1]; if (separatorLine) { if (separatorLine->isSeparator()) { diff --git a/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.cpp b/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.cpp index b759a06aee..09f3e6dc8c 100644 --- a/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.cpp +++ b/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.cpp @@ -255,7 +255,7 @@ void OculusLegacyDisplayPlugin::hmdPresent() { memset(eyePoses, 0, sizeof(ovrPosef) * 2); eyePoses[0].Orientation = eyePoses[1].Orientation = ovrRotation; - GLint texture = getGLBackend()->getTextureID(_compositeFramebuffer->getRenderBuffer(0)); + GLint texture = getGLBackend()->getTextureID(_compositeFramebuffer->getRenderBuffer(0), false); auto sync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); glFlush(); if (_hmdWindow->makeCurrent()) { diff --git a/plugins/openvr/src/OpenVrDisplayPlugin.cpp b/plugins/openvr/src/OpenVrDisplayPlugin.cpp index 46c2cf3ff2..6d503a208a 100644 --- a/plugins/openvr/src/OpenVrDisplayPlugin.cpp +++ b/plugins/openvr/src/OpenVrDisplayPlugin.cpp @@ -494,9 +494,9 @@ void OpenVrDisplayPlugin::customizeContext() { _compositeInfos[0].texture = _compositeFramebuffer->getRenderBuffer(0); for (size_t i = 0; i < COMPOSITING_BUFFER_SIZE; ++i) { if (0 != i) { - _compositeInfos[i].texture = gpu::TexturePointer(gpu::Texture::createRenderBuffer(gpu::Element::COLOR_RGBA_32, _renderTargetSize.x, _renderTargetSize.y, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_POINT))); + _compositeInfos[i].texture = gpu::TexturePointer(gpu::Texture::create2D(gpu::Element::COLOR_RGBA_32, _renderTargetSize.x, _renderTargetSize.y, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_POINT))); } - _compositeInfos[i].textureID = getGLBackend()->getTextureID(_compositeInfos[i].texture); + _compositeInfos[i].textureID = getGLBackend()->getTextureID(_compositeInfos[i].texture, false); } _submitThread->_canvas = _submitCanvas; _submitThread->start(QThread::HighPriority); @@ -624,7 +624,7 @@ void OpenVrDisplayPlugin::compositeLayers() { glFlush(); if (!newComposite.textureID) { - newComposite.textureID = getGLBackend()->getTextureID(newComposite.texture); + newComposite.textureID = getGLBackend()->getTextureID(newComposite.texture, false); } withPresentThreadLock([&] { _submitThread->update(newComposite); @@ -638,7 +638,7 @@ void OpenVrDisplayPlugin::hmdPresent() { if (_threadedSubmit) { _submitThread->waitForPresent(); } else { - GLuint glTexId = getGLBackend()->getTextureID(_compositeFramebuffer->getRenderBuffer(0)); + GLuint glTexId = getGLBackend()->getTextureID(_compositeFramebuffer->getRenderBuffer(0), false); vr::Texture_t vrTexture { (void*)glTexId, vr::API_OpenGL, vr::ColorSpace_Auto }; vr::VRCompositor()->Submit(vr::Eye_Left, &vrTexture, &OPENVR_TEXTURE_BOUNDS_LEFT); vr::VRCompositor()->Submit(vr::Eye_Right, &vrTexture, &OPENVR_TEXTURE_BOUNDS_RIGHT); diff --git a/scripts/developer/libraries/jasmine/hifi-boot.js b/scripts/developer/libraries/jasmine/hifi-boot.js index 772dd8c17e..f490a3618f 100644 --- a/scripts/developer/libraries/jasmine/hifi-boot.js +++ b/scripts/developer/libraries/jasmine/hifi-boot.js @@ -6,7 +6,7 @@ var lastSpecStartTime; function ConsoleReporter(options) { var startTime = new Date().getTime(); - var errorCount = 0, pending = []; + var errorCount = 0; this.jasmineStarted = function (obj) { print('Jasmine started with ' + obj.totalSpecsDefined + ' tests.'); }; @@ -15,14 +15,11 @@ var endTime = new Date().getTime(); print('
'); if (errorCount === 0) { - print ('All enabled tests passed!'); + print ('All tests passed!'); } else { print('Tests completed with ' + errorCount + ' ' + ERROR + '.'); } - if (pending.length) - print ('disabled:
   '+ - pending.join('
   ')+'
'); print('Tests completed in ' + (endTime - startTime) + 'ms.'); }; this.suiteStarted = function(obj) { @@ -35,10 +32,6 @@ lastSpecStartTime = new Date().getTime(); }; this.specDone = function(obj) { - if (obj.status === 'pending') { - pending.push(obj.fullName); - return print('...(pending ' + obj.fullName +')'); - } var specEndTime = new Date().getTime(); var symbol = obj.status === PASSED ? '' + CHECKMARK + '' : @@ -62,7 +55,7 @@ clearTimeout = Script.clearTimeout; clearInterval = Script.clearInterval; - var jasmine = this.jasmine = jasmineRequire.core(jasmineRequire); + var jasmine = jasmineRequire.core(jasmineRequire); var env = jasmine.getEnv(); diff --git a/scripts/developer/tests/.gitignore b/scripts/developer/tests/.gitignore deleted file mode 100644 index 7cacbf042c..0000000000 --- a/scripts/developer/tests/.gitignore +++ /dev/null @@ -1 +0,0 @@ -cube_texture.ktx \ No newline at end of file diff --git a/scripts/developer/tests/ambientSoundTest.js b/scripts/developer/tests/ambientSoundTest.js index d048d5f73d..5b373715c0 100644 --- a/scripts/developer/tests/ambientSoundTest.js +++ b/scripts/developer/tests/ambientSoundTest.js @@ -4,7 +4,7 @@ var uuid = Entities.addEntity({ shape: "Icosahedron", dimensions: Vec3.HALF, script: Script.resolvePath('../../tutorials/entity_scripts/ambientSound.js'), - position: Vec3.sum(Vec3.multiply(5, Quat.getForward(MyAvatar.orientation)), MyAvatar.position), + position: Vec3.sum(Vec3.multiply(5, Quat.getFront(MyAvatar.orientation)), MyAvatar.position), userData: JSON.stringify({ soundURL: WAVE, maxVolume: 0.1, diff --git a/scripts/developer/tests/basicEntityTest/entitySpawner.js b/scripts/developer/tests/basicEntityTest/entitySpawner.js index 538e9145f5..a2f38f59eb 100644 --- a/scripts/developer/tests/basicEntityTest/entitySpawner.js +++ b/scripts/developer/tests/basicEntityTest/entitySpawner.js @@ -2,7 +2,7 @@ orientation = Quat.safeEulerAngles(orientation); orientation.x = 0; orientation = Quat.fromVec3Degrees(orientation); - var center = Vec3.sum(MyAvatar.position, Vec3.multiply(3, Quat.getForward(orientation))); + var center = Vec3.sum(MyAvatar.position, Vec3.multiply(3, Quat.getFront(orientation))); // Math.random ensures no caching of script var SCRIPT_URL = Script.resolvePath("myEntityScript.js") diff --git a/scripts/developer/tests/batonSoundEntityTest/batonSoundTestEntitySpawner.js b/scripts/developer/tests/batonSoundEntityTest/batonSoundTestEntitySpawner.js index f5fc35a1de..fdcef8d32c 100644 --- a/scripts/developer/tests/batonSoundEntityTest/batonSoundTestEntitySpawner.js +++ b/scripts/developer/tests/batonSoundEntityTest/batonSoundTestEntitySpawner.js @@ -2,7 +2,7 @@ orientation = Quat.safeEulerAngles(orientation); orientation.x = 0; orientation = Quat.fromVec3Degrees(orientation); - var center = Vec3.sum(MyAvatar.position, Vec3.multiply(3, Quat.getForward(orientation))); + var center = Vec3.sum(MyAvatar.position, Vec3.multiply(3, Quat.getFront(orientation))); // Math.random ensures no caching of script var SCRIPT_URL = Script.resolvePath("batonSoundTestEntityScript.js") diff --git a/scripts/developer/tests/entityServerStampedeTest.js b/scripts/developer/tests/entityServerStampedeTest.js index 33aa53f9b1..3fcf01bb34 100644 --- a/scripts/developer/tests/entityServerStampedeTest.js +++ b/scripts/developer/tests/entityServerStampedeTest.js @@ -4,7 +4,7 @@ var DIV = NUM_ENTITIES / Math.PI / 2; var PASS_SCRIPT_URL = Script.resolvePath('entityServerStampedeTest-entity.js'); var FAIL_SCRIPT_URL = Script.resolvePath('entityStampedeTest-entity-fail.js'); -var origin = Vec3.sum(MyAvatar.position, Vec3.multiply(5, Quat.getForward(MyAvatar.orientation))); +var origin = Vec3.sum(MyAvatar.position, Vec3.multiply(5, Quat.getFront(MyAvatar.orientation))); origin.y += HMD.eyeHeight; var uuids = []; diff --git a/scripts/developer/tests/entityStampedeTest.js b/scripts/developer/tests/entityStampedeTest.js index 644bf0a216..c5040a9796 100644 --- a/scripts/developer/tests/entityStampedeTest.js +++ b/scripts/developer/tests/entityStampedeTest.js @@ -4,7 +4,7 @@ var DIV = NUM_ENTITIES / Math.PI / 2; var PASS_SCRIPT_URL = Script.resolvePath('').replace('.js', '-entity.js'); var FAIL_SCRIPT_URL = Script.resolvePath('').replace('.js', '-entity-fail.js'); -var origin = Vec3.sum(MyAvatar.position, Vec3.multiply(5, Quat.getForward(MyAvatar.orientation))); +var origin = Vec3.sum(MyAvatar.position, Vec3.multiply(5, Quat.getFront(MyAvatar.orientation))); origin.y += HMD.eyeHeight; var uuids = []; diff --git a/scripts/developer/tests/lodTest.js b/scripts/developer/tests/lodTest.js index ce91b54d0f..4b6706cd70 100644 --- a/scripts/developer/tests/lodTest.js +++ b/scripts/developer/tests/lodTest.js @@ -19,7 +19,7 @@ var WIDTH = MAX_DIM * NUM_SPHERES; var entities = []; var right = Quat.getRight(Camera.orientation); // Starting position will be 30 meters in front of the camera -var position = Vec3.sum(Camera.position, Vec3.multiply(30, Quat.getForward(Camera.orientation))); +var position = Vec3.sum(Camera.position, Vec3.multiply(30, Quat.getFront(Camera.orientation))); position = Vec3.sum(position, Vec3.multiply(-WIDTH/2, right)); for (var i = 0; i < NUM_SPHERES; ++i) { diff --git a/scripts/developer/tests/mat4test.js b/scripts/developer/tests/mat4test.js index 4e835ec82f..ebce420dcb 100644 --- a/scripts/developer/tests/mat4test.js +++ b/scripts/developer/tests/mat4test.js @@ -141,12 +141,12 @@ function testInverse() { assert(mat4FuzzyEqual(IDENTITY, Mat4.multiply(test2, Mat4.inverse(test2)))); } -function testForward() { +function testFront() { var test0 = IDENTITY; - assert(mat4FuzzyEqual({x: 0, y: 0, z: -1}, Mat4.getForward(test0))); + assert(mat4FuzzyEqual({x: 0, y: 0, z: -1}, Mat4.getFront(test0))); var test1 = Mat4.createFromScaleRotAndTrans(ONE_HALF, ROT_Y_180, ONE_TWO_THREE); - assert(mat4FuzzyEqual({x: 0, y: 0, z: 1}, Mat4.getForward(test1))); + assert(mat4FuzzyEqual({x: 0, y: 0, z: 1}, Mat4.getFront(test1))); } function testMat4() { @@ -157,7 +157,7 @@ function testMat4() { testTransformPoint(); testTransformVector(); testInverse(); - testForward(); + testFront(); print("MAT4 TEST complete! (" + (testCount - failureCount) + "/" + testCount + ") tests passed!"); } diff --git a/scripts/developer/tests/performance/tribbles.js b/scripts/developer/tests/performance/tribbles.js index c5735b7359..4c04f8b5b7 100644 --- a/scripts/developer/tests/performance/tribbles.js +++ b/scripts/developer/tests/performance/tribbles.js @@ -43,7 +43,7 @@ var HOW_FAR_UP = RANGE / 1.5; // higher (for uneven ground) above range/2 (for var totalCreated = 0; var offset = Vec3.sum(Vec3.multiply(HOW_FAR_UP, Vec3.UNIT_Y), - Vec3.multiply(HOW_FAR_IN_FRONT_OF_ME, Quat.getForward(Camera.orientation))); + Vec3.multiply(HOW_FAR_IN_FRONT_OF_ME, Quat.getFront(Camera.orientation))); var center = Vec3.sum(MyAvatar.position, offset); function randomVector(range) { diff --git a/scripts/developer/tests/rapidProceduralChange/rapidProceduralChangeTest.js b/scripts/developer/tests/rapidProceduralChange/rapidProceduralChangeTest.js index e28a7b01e2..6897a1b70f 100644 --- a/scripts/developer/tests/rapidProceduralChange/rapidProceduralChangeTest.js +++ b/scripts/developer/tests/rapidProceduralChange/rapidProceduralChangeTest.js @@ -20,9 +20,9 @@ orientation = Quat.safeEulerAngles(orientation); orientation.x = 0; orientation = Quat.fromVec3Degrees(orientation); -var centerUp = Vec3.sum(MyAvatar.position, Vec3.multiply(3, Quat.getForward(orientation))); +var centerUp = Vec3.sum(MyAvatar.position, Vec3.multiply(3, Quat.getFront(orientation))); centerUp.y += 0.5; -var centerDown = Vec3.sum(MyAvatar.position, Vec3.multiply(3, Quat.getForward(orientation))); +var centerDown = Vec3.sum(MyAvatar.position, Vec3.multiply(3, Quat.getFront(orientation))); centerDown.y -= 0.5; var ENTITY_SHADER_URL = "https://s3-us-west-1.amazonaws.com/hifi-content/eric/shaders/uniformTest.fs"; diff --git a/scripts/developer/tests/scaling.png b/scripts/developer/tests/scaling.png deleted file mode 100644 index 1e6a7df45d8440cc52d3b45501b909419fed2f1b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3172 zcmd5;c~p~E7XQ63A%rEG0zw6aOe>MPK~IBJisaL=qAd_A)CCaGwo@pwR8gZC_|%SD z7o4J$st8ugBC@Cz1PuvBYq45TM5xH3-~b|o9YRPlCwSUFX3m*2?O*fPeed4;yT5ne zyYHTRFu>o3XKrr}fVXnRvQ+>DfPl*ZaO3bV9|M+iS1wx;Bqh%uyiNeFQgFJcjPsAZ zQ+zK$=}JukxPSm)@JBWj{=Z@I&oh>M-7es>42Jw255u%;9b7_Ar+anY4|u#h;LQ2T zXWDU%Msp|eqeX1oR5;Ed z%2}=L#||eS>yaPipfV=$G*|spnb+iuN)}3+#M!v_fuM66=g`gr46y9i{f9JUb}sR=GCq% zR(P3YRJ{JO!mW$0%9Jz@eYk?q7U{t?rcyCMhi z6{Z`TtYfCa`{>n@WsR%lQ;+N=tEm?r;P=t9QTaV#{6N zh1Pmom+C}u<3gMv&9tVn=P0E-_pG{G3+9@Qtto#hLS%otuAK(%ys9nwp}|Zmc#fx{ z@4e;VgALMJ*0VC@D40W;6Y8yVt(VamH@OrP?uF{bEryGZ>yr$b7q$3XH8k5~RT|1?GJ0JWv*9}?nbf>SGfY8XT`;`MnhRsYzrz*~= z*A&XdI*PFZ)w71p)L&UCq1xqkU@kKXfq;M)EJ!w0C{PvwOE%?t%W$GO* zQv5VVq%*X-k;SH=>(8gm)6xC*3s9bQ+sx>5RT7 zd4@9yS6et(OIvWcdDMMG9_j>>&9K$pDm%-Ru&KU$#B5$#Qtn?aEa-mH*Htd;G0_(7mw3bheDNh9_>{ z^Qy-bHmNZfzG!*8rbotKzSn=mCMS0TmwUqEoo9E0*ZsPG0VuUUW9$J8a9P8m5Z~r{ znNSEYOmn9J|Bl24<@*GEGdIz|qp>9b*Z}=!1OfPyUG2Y#)@lD4`*&c4Ogob4Bu+Xq z_pKif4jc5(+ssD9x+{L8cDah|fwsBUq1F8w&M`yF?qNW+?YVekiy}GmSVryVJ^eS| zh;`M+4z^hYRd^Q_9@K?wTb$A{7Xri|9nTYI6X~FIpsJ+#E>F2LwJt$rXE|;LH{VE| z;Xo7yWI3rZm-dT5(ROTHRc9U&zROcKs$;mhfnn zo9B75R?*7_8w>h>xgX>%Gl`By(!f8X>z@^CVZb)o~%L;_f%|NAB(nv;`jxp6AniR}RWt zOD#%0&=$Nt`J&_#-1=(EQqWKi8Lkb8!WuZfDcvVI0&)GgVqLa^bHM_2#0)@K3 zdjq1dUpco+wDwFy&qUnnvkH^scz73k? z*0^wSGR<<^RCVlhggmC)f!R|PKFR46xws_6p1H3d{JG8pRmZZE`8aUG%TL(o%%i$p34%Ee%*y;?&!bVsIIHVCU`&x67@V=oz80bPkRU#~ z>gCI6fJ#&z?)_yH7DU1RsqTCaah zxRduvIgME=l>hJbq$|g`^ed--g)kOKgQTUv^iBC-opi?Po#V+ zm|^$~;%#`!C&53@XE@4QRL7AQRrS7~vVf;t!Q#)z7t-jQjVzM8iRz8TkG3?+?X(#m z<7SA&gQxS2ubgGVr}+3Khj9P?+aBJ>0aLF{GTn+ZpK29FWaXm}S4>*R@TAF$j? zP`PuvJC1>5odjZPH}|b$E>yXWsgLIvw%Rj3?!mY;P72a{OGk~S zzi8qDb@f4?vTADBHjg5jCSF2k=JgQ|XpW2w2Br6wKArRmOB~8C&>lM*hdIYVKp2Y| z6JeT!ai>eA(2mL#^M^8v9P}5>P@G3x7M0vnt4+sq z^pA=!`D%4M<-@eFMq`Z_C+h!Q7-w(pixK<}i+|%%4w63`O{v~IOI3Pm{_;5hu<~vH KWra&4_WTP0c9{JD diff --git a/scripts/developer/tests/sphereLODTest.js b/scripts/developer/tests/sphereLODTest.js index d0cb35eaa1..dc19094664 100644 --- a/scripts/developer/tests/sphereLODTest.js +++ b/scripts/developer/tests/sphereLODTest.js @@ -15,7 +15,7 @@ MyAvatar.orientation = Quat.fromPitchYawRollDegrees(0, 0, 0); orientation = Quat.safeEulerAngles(MyAvatar.orientation); orientation.x = 0; orientation = Quat.fromVec3Degrees(orientation); -var tablePosition = Vec3.sum(MyAvatar.position, Quat.getForward(orientation)); +var tablePosition = Vec3.sum(MyAvatar.position, Quat.getFront(orientation)); tablePosition.y += 0.5; diff --git a/scripts/developer/tests/testInterval.js b/scripts/developer/tests/testInterval.js index 7898610c6d..94a5fe1fa5 100644 --- a/scripts/developer/tests/testInterval.js +++ b/scripts/developer/tests/testInterval.js @@ -12,7 +12,7 @@ var UPDATE_HZ = 60; // standard script update rate var UPDATE_INTERVAL = 1000/UPDATE_HZ; // standard script update interval var UPDATE_WORK_EFFORT = 0; // 1000 is light work, 1000000 ~= 30ms -var basePosition = Vec3.sum(Camera.getPosition(), Quat.getForward(Camera.getOrientation())); +var basePosition = Vec3.sum(Camera.getPosition(), Quat.getFront(Camera.getOrientation())); var timerBox = Entities.addEntity( { type: "Box", diff --git a/scripts/developer/tests/unit_tests/entityUnitTests.js b/scripts/developer/tests/unit_tests/entityUnitTests.js index 1372676901..033a484663 100644 --- a/scripts/developer/tests/unit_tests/entityUnitTests.js +++ b/scripts/developer/tests/unit_tests/entityUnitTests.js @@ -1,7 +1,7 @@ describe('Entity', function() { var center = Vec3.sum( MyAvatar.position, - Vec3.multiply(3, Quat.getForward(Camera.getOrientation())) + Vec3.multiply(3, Quat.getFront(Camera.getOrientation())) ); var boxEntity; var boxProps = { diff --git a/scripts/developer/tests/unit_tests/moduleTests/cycles/a.js b/scripts/developer/tests/unit_tests/moduleTests/cycles/a.js deleted file mode 100644 index 265cfaa2df..0000000000 --- a/scripts/developer/tests/unit_tests/moduleTests/cycles/a.js +++ /dev/null @@ -1,10 +0,0 @@ -/* eslint-env node */ -var a = exports; -a.done = false; -var b = require('./b.js'); -a.done = true; -a.name = 'a'; -a['a.done?'] = a.done; -a['b.done?'] = b.done; - -print('from a.js a.done =', a.done, '/ b.done =', b.done); diff --git a/scripts/developer/tests/unit_tests/moduleTests/cycles/b.js b/scripts/developer/tests/unit_tests/moduleTests/cycles/b.js deleted file mode 100644 index c46c872828..0000000000 --- a/scripts/developer/tests/unit_tests/moduleTests/cycles/b.js +++ /dev/null @@ -1,10 +0,0 @@ -/* eslint-env node */ -var b = exports; -b.done = false; -var a = require('./a.js'); -b.done = true; -b.name = 'b'; -b['a.done?'] = a.done; -b['b.done?'] = b.done; - -print('from b.js a.done =', a.done, '/ b.done =', b.done); diff --git a/scripts/developer/tests/unit_tests/moduleTests/cycles/main.js b/scripts/developer/tests/unit_tests/moduleTests/cycles/main.js deleted file mode 100644 index 0ec39cd656..0000000000 --- a/scripts/developer/tests/unit_tests/moduleTests/cycles/main.js +++ /dev/null @@ -1,17 +0,0 @@ -/* eslint-env node */ -/* global print */ -/* eslint-disable comma-dangle */ - -print('main.js'); -var a = require('./a.js'), - b = require('./b.js'); - -print('from main.js a.done =', a.done, 'and b.done =', b.done); - -module.exports = { - name: 'main', - a: a, - b: b, - 'a.done?': a.done, - 'b.done?': b.done, -}; diff --git a/scripts/developer/tests/unit_tests/moduleTests/entity/entityConstructorAPIException.js b/scripts/developer/tests/unit_tests/moduleTests/entity/entityConstructorAPIException.js deleted file mode 100644 index bbe694b578..0000000000 --- a/scripts/developer/tests/unit_tests/moduleTests/entity/entityConstructorAPIException.js +++ /dev/null @@ -1,13 +0,0 @@ -/* eslint-disable comma-dangle */ -// test module method exception being thrown within main constructor -(function() { - var apiMethod = Script.require('../exceptions/exceptionInFunction.js'); - print(Script.resolvePath(''), "apiMethod", apiMethod); - // this next line throws from within apiMethod - print(apiMethod()); - return { - preload: function(uuid) { - print("entityConstructorAPIException::preload -- never seen --", uuid, Script.resolvePath('')); - }, - }; -}); diff --git a/scripts/developer/tests/unit_tests/moduleTests/entity/entityConstructorModule.js b/scripts/developer/tests/unit_tests/moduleTests/entity/entityConstructorModule.js deleted file mode 100644 index a4e8c17ab6..0000000000 --- a/scripts/developer/tests/unit_tests/moduleTests/entity/entityConstructorModule.js +++ /dev/null @@ -1,23 +0,0 @@ -/* global module */ -/* eslint-disable comma-dangle */ -// test dual-purpose module and standalone Entity script -function MyEntity(filename) { - return { - preload: function(uuid) { - print("entityConstructorModule.js::preload"); - if (typeof module === 'object') { - print("module.filename", module.filename); - print("module.parent.filename", module.parent && module.parent.filename); - } - }, - clickDownOnEntity: function(uuid, evt) { - print("entityConstructorModule.js::clickDownOnEntity"); - }, - }; -} - -try { - module.exports = MyEntity; -} catch (e) {} // eslint-disable-line no-empty -print('entityConstructorModule::MyEntity', typeof MyEntity); -(MyEntity); diff --git a/scripts/developer/tests/unit_tests/moduleTests/entity/entityConstructorNested.js b/scripts/developer/tests/unit_tests/moduleTests/entity/entityConstructorNested.js deleted file mode 100644 index a90d979877..0000000000 --- a/scripts/developer/tests/unit_tests/moduleTests/entity/entityConstructorNested.js +++ /dev/null @@ -1,14 +0,0 @@ -/* global module */ -// test Entity constructor based on inherited constructor from a module -function constructor() { - print("entityConstructorNested::constructor"); - var MyEntity = Script.require('./entityConstructorModule.js'); - return new MyEntity("-- created from entityConstructorNested --"); -} - -try { - module.exports = constructor; -} catch (e) { - constructor; -} - diff --git a/scripts/developer/tests/unit_tests/moduleTests/entity/entityConstructorNested2.js b/scripts/developer/tests/unit_tests/moduleTests/entity/entityConstructorNested2.js deleted file mode 100644 index 29e0ed65b1..0000000000 --- a/scripts/developer/tests/unit_tests/moduleTests/entity/entityConstructorNested2.js +++ /dev/null @@ -1,25 +0,0 @@ -/* global module */ -// test Entity constructor based on nested, inherited module constructors -function constructor() { - print("entityConstructorNested2::constructor"); - - // inherit from entityConstructorNested - var MyEntity = Script.require('./entityConstructorNested.js'); - function SubEntity() {} - SubEntity.prototype = new MyEntity('-- created from entityConstructorNested2 --'); - - // create new instance - var entity = new SubEntity(); - // "override" clickDownOnEntity for just this new instance - entity.clickDownOnEntity = function(uuid, evt) { - print("entityConstructorNested2::clickDownOnEntity"); - SubEntity.prototype.clickDownOnEntity.apply(this, arguments); - }; - return entity; -} - -try { - module.exports = constructor; -} catch (e) { - constructor; -} diff --git a/scripts/developer/tests/unit_tests/moduleTests/entity/entityConstructorRequireException.js b/scripts/developer/tests/unit_tests/moduleTests/entity/entityConstructorRequireException.js deleted file mode 100644 index 5872bce529..0000000000 --- a/scripts/developer/tests/unit_tests/moduleTests/entity/entityConstructorRequireException.js +++ /dev/null @@ -1,10 +0,0 @@ -/* eslint-disable comma-dangle */ -// test module-related exception from within "require" evaluation itself -(function() { - var mod = Script.require('../exceptions/exception.js'); - return { - preload: function(uuid) { - print("entityConstructorRequireException::preload (never happens)", uuid, Script.resolvePath(''), mod); - }, - }; -}); diff --git a/scripts/developer/tests/unit_tests/moduleTests/entity/entityPreloadAPIError.js b/scripts/developer/tests/unit_tests/moduleTests/entity/entityPreloadAPIError.js deleted file mode 100644 index eaee178b0a..0000000000 --- a/scripts/developer/tests/unit_tests/moduleTests/entity/entityPreloadAPIError.js +++ /dev/null @@ -1,13 +0,0 @@ -/* eslint-disable comma-dangle */ -// test module method exception being thrown within preload -(function() { - var apiMethod = Script.require('../exceptions/exceptionInFunction.js'); - print(Script.resolvePath(''), "apiMethod", apiMethod); - return { - preload: function(uuid) { - // this next line throws from within apiMethod - print(apiMethod()); - print("entityPreloadAPIException::preload -- never seen --", uuid, Script.resolvePath('')); - }, - }; -}); diff --git a/scripts/developer/tests/unit_tests/moduleTests/entity/entityPreloadRequire.js b/scripts/developer/tests/unit_tests/moduleTests/entity/entityPreloadRequire.js deleted file mode 100644 index 50dab9fa7c..0000000000 --- a/scripts/developer/tests/unit_tests/moduleTests/entity/entityPreloadRequire.js +++ /dev/null @@ -1,11 +0,0 @@ -/* eslint-disable comma-dangle */ -// test requiring a module from within preload -(function constructor() { - return { - preload: function(uuid) { - print("entityPreloadRequire::preload"); - var example = Script.require('../example.json'); - print("entityPreloadRequire::example::name", example.name); - }, - }; -}); diff --git a/scripts/developer/tests/unit_tests/moduleTests/example.json b/scripts/developer/tests/unit_tests/moduleTests/example.json deleted file mode 100644 index 42d7fe07da..0000000000 --- a/scripts/developer/tests/unit_tests/moduleTests/example.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "name": "Example JSON Module", - "last-modified": 1485789862, - "config": { - "title": "My Title", - "width": 800, - "height": 600 - } -} diff --git a/scripts/developer/tests/unit_tests/moduleTests/exceptions/exception.js b/scripts/developer/tests/unit_tests/moduleTests/exceptions/exception.js deleted file mode 100644 index 8d25d6b7a4..0000000000 --- a/scripts/developer/tests/unit_tests/moduleTests/exceptions/exception.js +++ /dev/null @@ -1,4 +0,0 @@ -/* eslint-env node */ -module.exports = "n/a"; -throw new Error('exception on line 2'); - diff --git a/scripts/developer/tests/unit_tests/moduleTests/exceptions/exceptionInFunction.js b/scripts/developer/tests/unit_tests/moduleTests/exceptions/exceptionInFunction.js deleted file mode 100644 index 69415a0741..0000000000 --- a/scripts/developer/tests/unit_tests/moduleTests/exceptions/exceptionInFunction.js +++ /dev/null @@ -1,38 +0,0 @@ -/* eslint-env node */ -// dummy lines to make sure exception line number is well below parent test script -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// - - -function myfunc() { - throw new Error('exception on line 32 in myfunc'); -} -module.exports = myfunc; -if (Script[module.filename] === 'throw') { - myfunc(); -} diff --git a/scripts/developer/tests/unit_tests/moduleUnitTests.js b/scripts/developer/tests/unit_tests/moduleUnitTests.js deleted file mode 100644 index 6810dd8b6d..0000000000 --- a/scripts/developer/tests/unit_tests/moduleUnitTests.js +++ /dev/null @@ -1,378 +0,0 @@ -/* eslint-env jasmine, node */ -/* global print:true, Script:true, global:true, require:true */ -/* eslint-disable comma-dangle */ -var isNode = instrumentTestrunner(), - runInterfaceTests = !isNode, - runNetworkTests = true; - -// describe wrappers (note: `xdescribe` indicates a disabled or "pending" jasmine test) -var INTERFACE = { describe: runInterfaceTests ? describe : xdescribe }, - NETWORK = { describe: runNetworkTests ? describe : xdescribe }; - -describe('require', function() { - describe('resolve', function() { - it('should resolve relative filenames', function() { - var expected = Script.resolvePath('./moduleTests/example.json'); - expect(require.resolve('./moduleTests/example.json')).toEqual(expected); - }); - describe('exceptions', function() { - it('should reject blank "" module identifiers', function() { - expect(function() { - require.resolve(''); - }).toThrowError(/Cannot find/); - }); - it('should reject excessive identifier sizes', function() { - expect(function() { - require.resolve(new Array(8193).toString()); - }).toThrowError(/Cannot find/); - }); - it('should reject implicitly-relative filenames', function() { - expect(function() { - var mod = require.resolve('example.js'); - mod.exists; - }).toThrowError(/Cannot find/); - }); - it('should reject unanchored, existing filenames with advice', function() { - expect(function() { - var mod = require.resolve('moduleTests/example.json'); - mod.exists; - }).toThrowError(/use '.\/moduleTests\/example\.json'/); - }); - it('should reject unanchored, non-existing filenames', function() { - expect(function() { - var mod = require.resolve('asdfssdf/example.json'); - mod.exists; - }).toThrowError(/Cannot find.*system module not found/); - }); - it('should reject non-existent filenames', function() { - expect(function() { - require.resolve('./404error.js'); - }).toThrowError(/Cannot find/); - }); - it('should reject identifiers resolving to a directory', function() { - expect(function() { - var mod = require.resolve('.'); - mod.exists; - // console.warn('resolved(.)', mod); - }).toThrowError(/Cannot find/); - expect(function() { - var mod = require.resolve('..'); - mod.exists; - // console.warn('resolved(..)', mod); - }).toThrowError(/Cannot find/); - expect(function() { - var mod = require.resolve('../'); - mod.exists; - // console.warn('resolved(../)', mod); - }).toThrowError(/Cannot find/); - }); - (isNode ? xit : it)('should reject non-system, extensionless identifiers', function() { - expect(function() { - require.resolve('./example'); - }).toThrowError(/Cannot find/); - }); - }); - }); - - describe('JSON', function() { - it('should import .json modules', function() { - var example = require('./moduleTests/example.json'); - expect(example.name).toEqual('Example JSON Module'); - }); - // noet: support for loading JSON via content type workarounds reverted - // (leaving these tests intact in case ever revisited later) - // INTERFACE.describe('interface', function() { - // NETWORK.describe('network', function() { - // xit('should import #content-type=application/json modules', function() { - // var results = require('https://jsonip.com#content-type=application/json'); - // expect(results.ip).toMatch(/^[.0-9]+$/); - // }); - // xit('should import content-type: application/json modules', function() { - // var scope = { 'content-type': 'application/json' }; - // var results = require.call(scope, 'https://jsonip.com'); - // expect(results.ip).toMatch(/^[.0-9]+$/); - // }); - // }); - // }); - - }); - - INTERFACE.describe('system', function() { - it('require("vec3")', function() { - expect(require('vec3')).toEqual(jasmine.any(Function)); - }); - it('require("vec3").method', function() { - expect(require('vec3')().isValid).toEqual(jasmine.any(Function)); - }); - it('require("vec3") as constructor', function() { - var vec3 = require('vec3'); - var v = vec3(1.1, 2.2, 3.3); - expect(v).toEqual(jasmine.any(Object)); - expect(v.isValid).toEqual(jasmine.any(Function)); - expect(v.isValid()).toBe(true); - expect(v.toString()).toEqual('[Vec3 (1.100,2.200,3.300)]'); - }); - }); - - describe('cache', function() { - it('should cache modules by resolved module id', function() { - var value = new Date; - var example = require('./moduleTests/example.json'); - // earmark the module object with a unique value - example['.test'] = value; - var example2 = require('../../tests/unit_tests/moduleTests/example.json'); - expect(example2).toBe(example); - // verify earmark is still the same after a second require() - expect(example2['.test']).toBe(example['.test']); - }); - it('should reload cached modules set to null', function() { - var value = new Date; - var example = require('./moduleTests/example.json'); - example['.test'] = value; - require.cache[require.resolve('../../tests/unit_tests/moduleTests/example.json')] = null; - var example2 = require('../../tests/unit_tests/moduleTests/example.json'); - // verify the earmark is *not* the same as before - expect(example2).not.toBe(example); - expect(example2['.test']).not.toBe(example['.test']); - }); - it('should reload when module property is deleted', function() { - var value = new Date; - var example = require('./moduleTests/example.json'); - example['.test'] = value; - delete require.cache[require.resolve('../../tests/unit_tests/moduleTests/example.json')]; - var example2 = require('../../tests/unit_tests/moduleTests/example.json'); - // verify the earmark is *not* the same as before - expect(example2).not.toBe(example); - expect(example2['.test']).not.toBe(example['.test']); - }); - }); - - describe('cyclic dependencies', function() { - describe('should allow lazy-ref cyclic module resolution', function() { - var main; - beforeEach(function() { - // eslint-disable-next-line - try { this._print = print; } catch (e) {} - // during these tests print() is no-op'd so that it doesn't disrupt the reporter output - print = function() {}; - Script.resetModuleCache(); - }); - afterEach(function() { - print = this._print; - }); - it('main is requirable', function() { - main = require('./moduleTests/cycles/main.js'); - expect(main).toEqual(jasmine.any(Object)); - }); - it('transient a and b done values', function() { - expect(main.a['b.done?']).toBe(true); - expect(main.b['a.done?']).toBe(false); - }); - it('ultimate a.done?', function() { - expect(main['a.done?']).toBe(true); - }); - it('ultimate b.done?', function() { - expect(main['b.done?']).toBe(true); - }); - }); - }); - - describe('JS', function() { - it('should throw catchable local file errors', function() { - expect(function() { - require('file:///dev/null/non-existent-file.js'); - }).toThrowError(/path not found|Cannot find.*non-existent-file/); - }); - it('should throw catchable invalid id errors', function() { - expect(function() { - require(new Array(4096 * 2).toString()); - }).toThrowError(/invalid.*size|Cannot find.*,{30}/); - }); - it('should throw catchable unresolved id errors', function() { - expect(function() { - require('foobar:/baz.js'); - }).toThrowError(/could not resolve|Cannot find.*foobar:/); - }); - - NETWORK.describe('network', function() { - // note: depending on retries these tests can take up to 60 seconds each to timeout - var timeout = 75 * 1000; - it('should throw catchable host errors', function() { - expect(function() { - var mod = require('http://non.existent.highfidelity.io/moduleUnitTest.js'); - print("mod", Object.keys(mod)); - }).toThrowError(/error retrieving script .ServerUnavailable.|Cannot find.*non.existent/); - }, timeout); - it('should throw catchable network timeouts', function() { - expect(function() { - require('http://ping.highfidelity.io:1024'); - }).toThrowError(/error retrieving script .Timeout.|Cannot find.*ping.highfidelity/); - }, timeout); - }); - }); - - INTERFACE.describe('entity', function() { - var sampleScripts = [ - 'entityConstructorAPIException.js', - 'entityConstructorModule.js', - 'entityConstructorNested2.js', - 'entityConstructorNested.js', - 'entityConstructorRequireException.js', - 'entityPreloadAPIError.js', - 'entityPreloadRequire.js', - ].filter(Boolean).map(function(id) { - return Script.require.resolve('./moduleTests/entity/'+id); - }); - - var uuids = []; - function cleanup() { - uuids.splice(0,uuids.length).forEach(function(uuid) { - Entities.deleteEntity(uuid); - }); - } - afterAll(cleanup); - // extra sanity check to avoid lingering entities - Script.scriptEnding.connect(cleanup); - - for (var i=0; i < sampleScripts.length; i++) { - maketest(i); - } - - function maketest(i) { - var script = sampleScripts[ i % sampleScripts.length ]; - var shortname = '['+i+'] ' + script.split('/').pop(); - var position = MyAvatar.position; - position.y -= i/2; - // define a unique jasmine test for the current entity script - it(shortname, function(done) { - var uuid = Entities.addEntity({ - text: shortname, - description: Script.resolvePath('').split('/').pop(), - type: 'Text', - position: position, - rotation: MyAvatar.orientation, - script: script, - scriptTimestamp: +new Date, - lifetime: 20, - lineHeight: 1/8, - dimensions: { x: 2, y: 0.5, z: 0.01 }, - backgroundColor: { red: 0, green: 0, blue: 0 }, - color: { red: 0xff, green: 0xff, blue: 0xff }, - }, !Entities.serversExist() || !Entities.canRezTmp()); - uuids.push(uuid); - function stopChecking() { - if (ii) { - Script.clearInterval(ii); - ii = 0; - } - } - var ii = Script.setInterval(function() { - Entities.queryPropertyMetadata(uuid, "script", function(err, result) { - if (err) { - stopChecking(); - throw new Error(err); - } - if (result.success) { - stopChecking(); - if (/Exception/.test(script)) { - expect(result.status).toMatch(/^error_(loading|running)_script$/); - } else { - expect(result.status).toEqual("running"); - } - Entities.deleteEntity(uuid); - done(); - } else { - print('!result.success', JSON.stringify(result)); - } - }); - }, 100); - Script.setTimeout(stopChecking, 4900); - }, 5000 /* jasmine async timeout */); - } - }); -}); - -// support for isomorphic Node.js / Interface unit testing -// note: run `npm install` from unit_tests/ and then `node moduleUnitTests.js` -function run() {} -function instrumentTestrunner() { - var isNode = typeof process === 'object' && process.title === 'node'; - if (typeof describe === 'function') { - // already running within a test runner; assume jasmine is ready-to-go - return isNode; - } - if (isNode) { - /* eslint-disable no-console */ - // Node.js test mode - // to keep things consistent Node.js uses the local jasmine.js library (instead of an npm version) - var jasmineRequire = require('../../libraries/jasmine/jasmine.js'); - var jasmine = jasmineRequire.core(jasmineRequire); - var env = jasmine.getEnv(); - var jasmineInterface = jasmineRequire.interface(jasmine, env); - for (var p in jasmineInterface) { - global[p] = jasmineInterface[p]; - } - env.addReporter(new (require('jasmine-console-reporter'))); - // testing mocks - Script = { - resetModuleCache: function() { - module.require.cache = {}; - }, - setTimeout: setTimeout, - clearTimeout: clearTimeout, - resolvePath: function(id) { - // this attempts to accurately emulate how Script.resolvePath works - var trace = {}; Error.captureStackTrace(trace); - var base = trace.stack.split('\n')[2].replace(/^.*[(]|[)].*$/g,'').replace(/:[0-9]+:[0-9]+.*$/,''); - if (!id) { - return base; - } - var rel = base.replace(/[^\/]+$/, id); - console.info('rel', rel); - return require.resolve(rel); - }, - require: function(mod) { - return require(Script.require.resolve(mod)); - }, - }; - Script.require.cache = require.cache; - Script.require.resolve = function(mod) { - if (mod === '.' || /^\.\.($|\/)/.test(mod)) { - throw new Error("Cannot find module '"+mod+"' (is dir)"); - } - var path = require.resolve(mod); - // console.info('node-require-reoslved', mod, path); - try { - if (require('fs').lstatSync(path).isDirectory()) { - throw new Error("Cannot find module '"+path+"' (is directory)"); - } - // console.info('!path', path); - } catch (e) { - console.error(e); - } - return path; - }; - print = console.info.bind(console, '[print]'); - /* eslint-enable no-console */ - } else { - // Interface test mode - global = this; - Script.require('../../../system/libraries/utils.js'); - this.jasmineRequire = Script.require('../../libraries/jasmine/jasmine.js'); - Script.require('../../libraries/jasmine/hifi-boot.js'); - require = Script.require; - // polyfill console - /* global console:true */ - console = { - log: print, - info: print.bind(this, '[info]'), - warn: print.bind(this, '[warn]'), - error: print.bind(this, '[error]'), - debug: print.bind(this, '[debug]'), - }; - } - // eslint-disable-next-line - run = function() { global.jasmine.getEnv().execute(); }; - return isNode; -} -run(); diff --git a/scripts/developer/tests/unit_tests/package.json b/scripts/developer/tests/unit_tests/package.json deleted file mode 100644 index 91d719b687..0000000000 --- a/scripts/developer/tests/unit_tests/package.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "name": "unit_tests", - "devDependencies": { - "jasmine-console-reporter": "^1.2.7" - } -} diff --git a/scripts/developer/tests/unit_tests/scriptUnitTests.js b/scripts/developer/tests/unit_tests/scriptUnitTests.js index fa8cb44608..63b451e97f 100644 --- a/scripts/developer/tests/unit_tests/scriptUnitTests.js +++ b/scripts/developer/tests/unit_tests/scriptUnitTests.js @@ -15,20 +15,10 @@ describe('Script', function () { // characterization tests // initially these are just to capture how the app works currently var testCases = { - // special relative resolves '': filename, '.': dirname, '..': parentdir, - - // local file "magic" tilde path expansion - '/~/defaultScripts.js': ScriptDiscoveryService.defaultScriptsPath + '/defaultScripts.js', - - // these schemes appear to always get resolved to empty URLs - 'qrc://test': '', 'about:Entities 1': '', - 'ftp://host:port/path': '', - 'data:text/html;text,foo': '', - 'Entities 1': dirname + 'Entities 1', './file.js': dirname + 'file.js', 'c:/temp/': 'file:///c:/temp/', @@ -41,12 +31,6 @@ describe('Script', function () { '/~/libraries/utils.js': 'file:///~/libraries/utils.js', '/temp/file.js': 'file:///temp/file.js', '/~/': 'file:///~/', - - // these schemes appear to always get resolved to the same URL again - 'http://highfidelity.com': 'http://highfidelity.com', - 'atp:/highfidelity': 'atp:/highfidelity', - 'atp:c2d7e3a48cadf9ba75e4f8d9f4d80e75276774880405a093fdee36543aa04f': - 'atp:c2d7e3a48cadf9ba75e4f8d9f4d80e75276774880405a093fdee36543aa04f', }; describe('resolvePath', function () { Object.keys(testCases).forEach(function(input) { @@ -58,7 +42,7 @@ describe('Script', function () { describe('include', function () { var old_cache_buster; - var cache_buster = '#' + new Date().getTime().toString(36); + var cache_buster = '#' + +new Date; beforeAll(function() { old_cache_buster = Settings.getValue('cache_buster'); Settings.setValue('cache_buster', cache_buster); diff --git a/scripts/developer/tests/viveTouchpadTest.js b/scripts/developer/tests/viveTouchpadTest.js index b5d9575adf..913da5888d 100644 --- a/scripts/developer/tests/viveTouchpadTest.js +++ b/scripts/developer/tests/viveTouchpadTest.js @@ -24,10 +24,10 @@ var boxZAxis, boxYAxis; var prevThumbDown = false; function init() { - boxPosition = Vec3.sum(MyAvatar.position, Vec3.multiply(3, Quat.getForward(Camera.getOrientation()))); - var forward = Quat.getForward(Camera.getOrientation()); - boxZAxis = Vec3.normalize(Vec3.cross(forward, Y_AXIS)); - boxYAxis = Vec3.normalize(Vec3.cross(boxZAxis, forward)); + boxPosition = Vec3.sum(MyAvatar.position, Vec3.multiply(3, Quat.getFront(Camera.getOrientation()))); + var front = Quat.getFront(Camera.getOrientation()); + boxZAxis = Vec3.normalize(Vec3.cross(front, Y_AXIS)); + boxYAxis = Vec3.normalize(Vec3.cross(boxZAxis, front)); boxEntity = Entities.addEntity({ type: "Box", diff --git a/scripts/developer/utilities/record/recorder.js b/scripts/developer/utilities/record/recorder.js index ba1c8b0393..0e335116d5 100644 --- a/scripts/developer/utilities/record/recorder.js +++ b/scripts/developer/utilities/record/recorder.js @@ -9,14 +9,12 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -/* globals HIFI_PUBLIC_BUCKET:true, Tool, ToolBar */ - HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/"; Script.include("/~/system/libraries/toolBars.js"); var recordingFile = "recording.hfr"; -function setDefaultPlayerOptions() { +function setPlayerOptions() { Recording.setPlayFromCurrentLocation(true); Recording.setPlayerUseDisplayName(false); Recording.setPlayerUseAttachments(false); @@ -40,16 +38,16 @@ var saveIcon; var loadIcon; var spacing; var timerOffset; +setupToolBar(); + var timer = null; var slider = null; - -setupToolBar(); setupTimer(); var watchStop = false; function setupToolBar() { - if (toolBar !== null) { + if (toolBar != null) { print("Multiple calls to Recorder.js:setupToolBar()"); return; } @@ -58,8 +56,6 @@ function setupToolBar() { toolBar = new ToolBar(0, 0, ToolBar.HORIZONTAL); - toolBar.onMove = onToolbarMove; - toolBar.setBack(COLOR_TOOL_BAR, ALPHA_OFF); recordIcon = toolBar.addTool({ @@ -90,7 +86,7 @@ function setupToolBar() { visible: true }, false); - timerOffset = toolBar.width + ToolBar.SPACING; + timerOffset = toolBar.width; spacing = toolBar.addSpacing(0); saveIcon = toolBar.addTool({ @@ -116,15 +112,15 @@ function setupTimer() { text: (0.00).toFixed(3), backgroundColor: COLOR_OFF, x: 0, y: 0, - width: 200, height: 25, - leftMargin: 5, topMargin: 3, + width: 0, height: 0, + leftMargin: 10, topMargin: 10, alpha: 1.0, backgroundAlpha: 1.0, visible: true }); slider = { x: 0, y: 0, w: 200, h: 20, - pos: 0.0 // 0.0 <= pos <= 1.0 + pos: 0.0, // 0.0 <= pos <= 1.0 }; slider.background = Overlays.addOverlay("text", { text: "", @@ -148,40 +144,20 @@ function setupTimer() { }); } -function onToolbarMove(newX, newY, deltaX, deltaY) { - Overlays.editOverlay(timer, { - x: newX + timerOffset - ToolBar.SPACING, - y: newY - }); - - slider.x = newX - ToolBar.SPACING; - slider.y = newY - slider.h - ToolBar.SPACING; - - Overlays.editOverlay(slider.background, { - x: slider.x, - y: slider.y - }); - Overlays.editOverlay(slider.foreground, { - x: slider.x, - y: slider.y - }); -} - function updateTimer() { var text = ""; if (Recording.isRecording()) { text = formatTime(Recording.recorderElapsed()); + } else { - text = formatTime(Recording.playerElapsed()) + " / " + formatTime(Recording.playerLength()); + text = formatTime(Recording.playerElapsed()) + " / " + + formatTime(Recording.playerLength()); } - var timerWidth = text.length * 8 + ((Recording.isRecording()) ? 15 : 0); - Overlays.editOverlay(timer, { - text: text, - width: timerWidth - }); - toolBar.changeSpacing(timerWidth + ToolBar.SPACING, spacing); + text: text + }) + toolBar.changeSpacing(text.length * 8 + ((Recording.isRecording()) ? 15 : 0), spacing); if (Recording.isRecording()) { slider.pos = 1.0; @@ -197,7 +173,7 @@ function updateTimer() { function formatTime(time) { var MIN_PER_HOUR = 60; var SEC_PER_MIN = 60; - var MSEC_DIGITS = 3; + var MSEC_PER_SEC = 1000; var hours = Math.floor(time / (SEC_PER_MIN * MIN_PER_HOUR)); time -= hours * (SEC_PER_MIN * MIN_PER_HOUR); @@ -208,19 +184,37 @@ function formatTime(time) { var seconds = time; var text = ""; - text += (hours > 0) ? hours + ":" : ""; - text += (minutes > 0) ? ((minutes < 10 && text !== "") ? "0" : "") + minutes + ":" : ""; - text += ((seconds < 10 && text !== "") ? "0" : "") + seconds.toFixed(MSEC_DIGITS); + text += (hours > 0) ? hours + ":" : + ""; + text += (minutes > 0) ? ((minutes < 10 && text != "") ? "0" : "") + minutes + ":" : + ""; + text += ((seconds < 10 && text != "") ? "0" : "") + seconds.toFixed(3); return text; } function moveUI() { var relative = { x: 70, y: 40 }; toolBar.move(relative.x, windowDimensions.y - relative.y); + Overlays.editOverlay(timer, { + x: relative.x + timerOffset - ToolBar.SPACING, + y: windowDimensions.y - relative.y - ToolBar.SPACING + }); + + slider.x = relative.x - ToolBar.SPACING; + slider.y = windowDimensions.y - relative.y - slider.h - ToolBar.SPACING; + + Overlays.editOverlay(slider.background, { + x: slider.x, + y: slider.y, + }); + Overlays.editOverlay(slider.foreground, { + x: slider.x, + y: slider.y, + }); } function mousePressEvent(event) { - var clickedOverlay = Overlays.getOverlayAtPoint({ x: event.x, y: event.y }); + clickedOverlay = Overlays.getOverlayAtPoint({ x: event.x, y: event.y }); if (recordIcon === toolBar.clicked(clickedOverlay, false) && !Recording.isPlaying()) { if (!Recording.isRecording()) { @@ -232,11 +226,7 @@ function mousePressEvent(event) { toolBar.setAlpha(ALPHA_OFF, loadIcon); } else { Recording.stopRecording(); - toolBar.selectTool(recordIcon, true); - setDefaultPlayerOptions(); - // Plays the recording at the same spot as you recorded it - Recording.setPlayFromCurrentLocation(false); - Recording.setPlayerTime(0); + toolBar.selectTool(recordIcon, true ); Recording.loadLastRecording(); toolBar.setAlpha(ALPHA_ON, playIcon); toolBar.setAlpha(ALPHA_ON, playLoopIcon); @@ -250,6 +240,7 @@ function mousePressEvent(event) { toolBar.setAlpha(ALPHA_ON, saveIcon); toolBar.setAlpha(ALPHA_ON, loadIcon); } else if (Recording.playerLength() > 0) { + setPlayerOptions(); Recording.setPlayerLoop(false); Recording.startPlaying(); toolBar.setAlpha(ALPHA_OFF, recordIcon); @@ -264,6 +255,7 @@ function mousePressEvent(event) { toolBar.setAlpha(ALPHA_ON, saveIcon); toolBar.setAlpha(ALPHA_ON, loadIcon); } else if (Recording.playerLength() > 0) { + setPlayerOptions(); Recording.setPlayerLoop(true); Recording.startPlaying(); toolBar.setAlpha(ALPHA_OFF, recordIcon); @@ -271,7 +263,7 @@ function mousePressEvent(event) { toolBar.setAlpha(ALPHA_OFF, loadIcon); } } else if (saveIcon === toolBar.clicked(clickedOverlay)) { - if (!Recording.isRecording() && !Recording.isPlaying() && Recording.playerLength() !== 0) { + if (!Recording.isRecording() && !Recording.isPlaying() && Recording.playerLength() != 0) { recordingFile = Window.save("Save recording to file", ".", "Recordings (*.hfr)"); if (!(recordingFile === "null" || recordingFile === null || recordingFile === "")) { Recording.saveRecording(recordingFile); @@ -282,7 +274,6 @@ function mousePressEvent(event) { recordingFile = Window.browse("Load recording from file", ".", "Recordings (*.hfr *.rec *.HFR *.REC)"); if (!(recordingFile === "null" || recordingFile === null || recordingFile === "")) { Recording.loadRecording(recordingFile); - setDefaultPlayerOptions(); } if (Recording.playerLength() > 0) { toolBar.setAlpha(ALPHA_ON, playIcon); @@ -291,8 +282,8 @@ function mousePressEvent(event) { } } } else if (Recording.playerLength() > 0 && - slider.x < event.x && event.x < slider.x + slider.w && - slider.y < event.y && event.y < slider.y + slider.h) { + slider.x < event.x && event.x < slider.x + slider.w && + slider.y < event.y && event.y < slider.y + slider.h) { isSliding = true; slider.pos = (event.x - slider.x) / slider.w; Recording.setPlayerTime(slider.pos * Recording.playerLength()); @@ -317,7 +308,7 @@ function mouseReleaseEvent(event) { function update() { var newDimensions = Controller.getViewportDimensions(); - if (windowDimensions.x !== newDimensions.x || windowDimensions.y !== newDimensions.y) { + if (windowDimensions.x != newDimensions.x || windowDimensions.y != newDimensions.y) { windowDimensions = newDimensions; moveUI(); } diff --git a/scripts/developer/utilities/render/deferredLighting.qml b/scripts/developer/utilities/render/deferredLighting.qml index c7ec8e1153..99a9f258e3 100644 --- a/scripts/developer/utilities/render/deferredLighting.qml +++ b/scripts/developer/utilities/render/deferredLighting.qml @@ -25,7 +25,7 @@ Column { "Lightmap:LightingModel:enableLightmap", "Background:LightingModel:enableBackground", "ssao:AmbientOcclusion:enabled", - "Textures:LightingModel:enableMaterialTexturing" + "Textures:LightingModel:enableMaterialTexturing", ] CheckBox { text: modelData.split(":")[0] @@ -45,7 +45,6 @@ Column { "Diffuse:LightingModel:enableDiffuse", "Specular:LightingModel:enableSpecular", "Albedo:LightingModel:enableAlbedo", - "Wireframe:LightingModel:enableWireframe" ] CheckBox { text: modelData.split(":")[0] diff --git a/scripts/developer/utilities/render/photobooth/photobooth.js b/scripts/developer/utilities/render/photobooth/photobooth.js index b78986be1a..3e86d83a98 100644 --- a/scripts/developer/utilities/render/photobooth/photobooth.js +++ b/scripts/developer/utilities/render/photobooth/photobooth.js @@ -8,13 +8,12 @@ var PhotoBooth = {}; PhotoBooth.init = function () { var success = Clipboard.importEntities(PHOTOBOOTH_SETUP_JSON_URL); - var forwardFactor = 10; - var forwardUnitVector = Vec3.normalize(Quat.getForward(MyAvatar.orientation)); - var forwardOffset = Vec3.multiply(forwardUnitVector,forwardFactor); + var frontFactor = 10; + var frontUnitVec = Vec3.normalize(Quat.getFront(MyAvatar.orientation)); + var frontOffset = Vec3.multiply(frontUnitVec,frontFactor); var rightFactor = 3; - // TODO: rightUnitVec is unused and spawnLocation declaration is incorrect var rightUnitVec = Vec3.normalize(Quat.getRight(MyAvatar.orientation)); - var spawnLocation = Vec3.sum(Vec3.sum(MyAvatar.position,forwardOffset),rightFactor); + var spawnLocation = Vec3.sum(Vec3.sum(MyAvatar.position,frontOffset),rightFactor); if (success) { this.pastedEntityIDs = Clipboard.pasteEntities(spawnLocation); this.processPastedEntities(); diff --git a/scripts/modules/vec3.js b/scripts/modules/vec3.js deleted file mode 100644 index f164f01374..0000000000 --- a/scripts/modules/vec3.js +++ /dev/null @@ -1,69 +0,0 @@ -// Example of using a "system module" to decouple Vec3's implementation details. -// -// Users would bring Vec3 support in as a module: -// var vec3 = Script.require('vec3'); -// - -// (this example is compatible with using as a Script.include and as a Script.require module) -try { - // Script.require - module.exports = vec3; -} catch(e) { - // Script.include - Script.registerValue("vec3", vec3); -} - -vec3.fromObject = function(v) { - //return new vec3(v.x, v.y, v.z); - //... this is even faster and achieves the same effect - v.__proto__ = vec3.prototype; - return v; -}; - -vec3.prototype = { - multiply: function(v2) { - // later on could support overrides like so: - // if (v2 instanceof quat) { [...] } - // which of the below is faster (C++ or JS)? - // (dunno -- but could systematically find out and go with that version) - - // pure JS option - // return new vec3(this.x * v2.x, this.y * v2.y, this.z * v2.z); - - // hybrid C++ option - return vec3.fromObject(Vec3.multiply(this, v2)); - }, - // detects any NaN and Infinity values - isValid: function() { - return isFinite(this.x) && isFinite(this.y) && isFinite(this.z); - }, - // format Vec3's, eg: - // var v = vec3(); - // print(v); // outputs [Vec3 (0.000, 0.000, 0.000)] - toString: function() { - if (this === vec3.prototype) { - return "{Vec3 prototype}"; - } - function fixed(n) { return n.toFixed(3); } - return "[Vec3 (" + [this.x, this.y, this.z].map(fixed) + ")]"; - }, -}; - -vec3.DEBUG = true; - -function vec3(x, y, z) { - if (!(this instanceof vec3)) { - // if vec3 is called as a function then re-invoke as a constructor - // (so that `value instanceof vec3` holds true for created values) - return new vec3(x, y, z); - } - - // unfold default arguments (vec3(), vec3(.5), vec3(0,1), etc.) - this.x = x !== undefined ? x : 0; - this.y = y !== undefined ? y : this.x; - this.z = z !== undefined ? z : this.y; - - if (vec3.DEBUG && !this.isValid()) - throw new Error('vec3() -- invalid initial values ['+[].slice.call(arguments)+']'); -}; - diff --git a/scripts/system/assets/images/icon-particles.svg b/scripts/system/assets/images/icon-particles.svg deleted file mode 100644 index 5e0105d7cd..0000000000 --- a/scripts/system/assets/images/icon-particles.svg +++ /dev/null @@ -1,29 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/scripts/system/assets/images/icon-point-light.svg b/scripts/system/assets/images/icon-point-light.svg deleted file mode 100644 index 896c35b63b..0000000000 --- a/scripts/system/assets/images/icon-point-light.svg +++ /dev/null @@ -1,57 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/scripts/system/assets/images/icon-spot-light.svg b/scripts/system/assets/images/icon-spot-light.svg deleted file mode 100644 index ac2f87bb27..0000000000 --- a/scripts/system/assets/images/icon-spot-light.svg +++ /dev/null @@ -1,37 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/scripts/system/away.js b/scripts/system/away.js index 4ca938d492..541fe6f679 100644 --- a/scripts/system/away.js +++ b/scripts/system/away.js @@ -87,8 +87,8 @@ function moveCloserToCamera(positionAtHUD) { // we don't actually want to render at the slerped look at... instead, we want to render // slightly closer to the camera than that. var MOVE_CLOSER_TO_CAMERA_BY = -0.25; - var cameraForward = Quat.getForward(Camera.orientation); - var closerToCamera = Vec3.multiply(cameraForward, MOVE_CLOSER_TO_CAMERA_BY); // slightly closer to camera + var cameraFront = Quat.getFront(Camera.orientation); + var closerToCamera = Vec3.multiply(cameraFront, MOVE_CLOSER_TO_CAMERA_BY); // slightly closer to camera var slightlyCloserPosition = Vec3.sum(positionAtHUD, closerToCamera); return slightlyCloserPosition; diff --git a/scripts/system/controllers/grab.js b/scripts/system/controllers/grab.js index 05b2eefeb5..f0b6663bec 100644 --- a/scripts/system/controllers/grab.js +++ b/scripts/system/controllers/grab.js @@ -463,7 +463,7 @@ Grabber.prototype.moveEvent = function(event) { var orientation = Camera.getOrientation(); var dragOffset = Vec3.multiply(drag.x, Quat.getRight(orientation)); dragOffset = Vec3.sum(dragOffset, Vec3.multiply(-drag.y, Quat.getUp(orientation))); - var axis = Vec3.cross(dragOffset, Quat.getForward(orientation)); + var axis = Vec3.cross(dragOffset, Quat.getFront(orientation)); axis = Vec3.normalize(axis); var ROTATE_STRENGTH = 0.4; // magic number tuned by hand var angle = ROTATE_STRENGTH * Math.sqrt((drag.x * drag.x) + (drag.y * drag.y)); @@ -487,7 +487,7 @@ Grabber.prototype.moveEvent = function(event) { if (this.mode === "verticalCylinder") { // for this mode we recompute the plane based on current Camera - var planeNormal = Quat.getForward(Camera.getOrientation()); + var planeNormal = Quat.getFront(Camera.getOrientation()); planeNormal.y = 0; planeNormal = Vec3.normalize(planeNormal); var pointOnCylinder = Vec3.multiply(planeNormal, this.xzDistanceToGrab); diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index e83e31aaa5..51b01e60a2 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -1481,7 +1481,7 @@ function MyController(hand) { var pickRay = { origin: PICK_WITH_HAND_RAY ? worldHandPosition : Camera.position, direction: PICK_WITH_HAND_RAY ? Quat.getUp(worldHandRotation) : Vec3.mix(Quat.getUp(worldHandRotation), - Quat.getForward(Camera.orientation), + Quat.getFront(Camera.orientation), HAND_HEAD_MIX_RATIO), length: PICK_MAX_DISTANCE }; diff --git a/scripts/system/controllers/handControllerPointer.js b/scripts/system/controllers/handControllerPointer.js index eb94428100..f8a336a017 100644 --- a/scripts/system/controllers/handControllerPointer.js +++ b/scripts/system/controllers/handControllerPointer.js @@ -174,7 +174,7 @@ function calculateRayUICollisionPoint(position, direction) { // interect HUD plane, 1m in front of camera, using formula: // scale = hudNormal dot (hudPoint - position) / hudNormal dot direction // intersection = postion + scale*direction - var hudNormal = Quat.getForward(Camera.getOrientation()); + var hudNormal = Quat.getFront(Camera.getOrientation()); var hudPoint = Vec3.sum(Camera.getPosition(), hudNormal); // must also scale if PLANAR_PERPENDICULAR_HUD_DISTANCE!=1 var denominator = Vec3.dot(hudNormal, direction); if (denominator === 0) { diff --git a/scripts/system/controllers/teleport.js b/scripts/system/controllers/teleport.js index 1c6c9af272..c058f046db 100644 --- a/scripts/system/controllers/teleport.js +++ b/scripts/system/controllers/teleport.js @@ -85,7 +85,6 @@ function Trigger(hand) { } var coolInTimeout = null; -var ignoredEntities = []; var TELEPORTER_STATES = { IDLE: 'idle', @@ -240,11 +239,11 @@ function Teleporter() { // We might hit an invisible entity that is not a seat, so we need to do a second pass. // * In the second pass we pick against visible entities only. // - var intersection = Entities.findRayIntersection(pickRay, true, [], [this.targetEntity].concat(ignoredEntities), false, true); + var intersection = Entities.findRayIntersection(pickRay, true, [], [this.targetEntity], false, true); var teleportLocationType = getTeleportTargetType(intersection); if (teleportLocationType === TARGET.INVISIBLE) { - intersection = Entities.findRayIntersection(pickRay, true, [], [this.targetEntity].concat(ignoredEntities), true, true); + intersection = Entities.findRayIntersection(pickRay, true, [], [this.targetEntity], true, true); teleportLocationType = getTeleportTargetType(intersection); } @@ -514,7 +513,7 @@ function cleanup() { Script.scriptEnding.connect(cleanup); var isDisabled = false; -var handleTeleportMessages = function(channel, message, sender) { +var handleHandMessages = function(channel, message, sender) { var data; if (sender === MyAvatar.sessionUUID) { if (channel === 'Hifi-Teleport-Disabler') { @@ -530,20 +529,12 @@ var handleTeleportMessages = function(channel, message, sender) { if (message === 'none') { isDisabled = false; } - } else if (channel === 'Hifi-Teleport-Ignore-Add' && !Uuid.isNull(message) && ignoredEntities.indexOf(message) === -1) { - ignoredEntities.push(message); - } else if (channel === 'Hifi-Teleport-Ignore-Remove' && !Uuid.isNull(message)) { - var removeIndex = ignoredEntities.indexOf(message); - if (removeIndex > -1) { - ignoredEntities.splice(removeIndex, 1); - } + } } } Messages.subscribe('Hifi-Teleport-Disabler'); -Messages.subscribe('Hifi-Teleport-Ignore-Add'); -Messages.subscribe('Hifi-Teleport-Ignore-Remove'); -Messages.messageReceived.connect(handleTeleportMessages); +Messages.messageReceived.connect(handleHandMessages); }()); // END LOCAL_SCOPE diff --git a/scripts/system/controllers/toggleAdvancedMovementForHandControllers.js b/scripts/system/controllers/toggleAdvancedMovementForHandControllers.js index e6c9b0aee0..46464dc2e1 100644 --- a/scripts/system/controllers/toggleAdvancedMovementForHandControllers.js +++ b/scripts/system/controllers/toggleAdvancedMovementForHandControllers.js @@ -17,14 +17,15 @@ var mappingName, basicMapping, isChecked; var TURN_RATE = 1000; var MENU_ITEM_NAME = "Advanced Movement For Hand Controllers"; +var SETTINGS_KEY = 'advancedMovementForHandControllersIsChecked'; var isDisabled = false; -var previousSetting = MyAvatar.useAdvancedMovementControls; -if (previousSetting === false) { +var previousSetting = Settings.getValue(SETTINGS_KEY); +if (previousSetting === '' || previousSetting === false || previousSetting === 'false') { previousSetting = false; isChecked = false; } -if (previousSetting === true) { +if (previousSetting === true || previousSetting === 'true') { previousSetting = true; isChecked = true; } @@ -36,6 +37,7 @@ function addAdvancedMovementItemToSettingsMenu() { isCheckable: true, isChecked: previousSetting }); + } function rotate180() { @@ -70,6 +72,7 @@ function registerBasicMapping() { } return; }); + basicMapping.from(Controller.Standard.LX).to(Controller.Standard.RX); basicMapping.from(Controller.Standard.RY).to(function(value) { if (isDisabled) { return; @@ -109,10 +112,10 @@ function menuItemEvent(menuItem) { if (menuItem == MENU_ITEM_NAME) { isChecked = Menu.isOptionChecked(MENU_ITEM_NAME); if (isChecked === true) { - MyAvatar.useAdvancedMovementControls = true; + Settings.setValue(SETTINGS_KEY, true); disableMappings(); } else if (isChecked === false) { - MyAvatar.useAdvancedMovementControls = false; + Settings.setValue(SETTINGS_KEY, false); enableMappings(); } } diff --git a/scripts/system/edit.js b/scripts/system/edit.js index 8b02eb1550..a440fec1ac 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -33,27 +33,13 @@ Script.include([ "libraries/gridTool.js", "libraries/entityList.js", "particle_explorer/particleExplorerTool.js", - "libraries/entityIconOverlayManager.js" + "libraries/lightOverlayManager.js" ]); var selectionDisplay = SelectionDisplay; var selectionManager = SelectionManager; -const PARTICLE_SYSTEM_URL = Script.resolvePath("assets/images/icon-particles.svg"); -const POINT_LIGHT_URL = Script.resolvePath("assets/images/icon-point-light.svg"); -const SPOT_LIGHT_URL = Script.resolvePath("assets/images/icon-spot-light.svg"); -entityIconOverlayManager = new EntityIconOverlayManager(['Light', 'ParticleEffect'], function(entityID) { - var properties = Entities.getEntityProperties(entityID, ['type', 'isSpotlight']); - if (properties.type === 'Light') { - return { - url: properties.isSpotlight ? SPOT_LIGHT_URL : POINT_LIGHT_URL, - } - } else { - return { - url: PARTICLE_SYSTEM_URL, - } - } -}); +var lightOverlayManager = new LightOverlayManager(); var cameraManager = new CameraManager(); @@ -67,45 +53,7 @@ var entityListTool = new EntityListTool(); selectionManager.addEventListener(function () { selectionDisplay.updateHandles(); - entityIconOverlayManager.updatePositions(); - - // Update particle explorer - var needToDestroyParticleExplorer = false; - if (selectionManager.selections.length === 1) { - var selectedEntityID = selectionManager.selections[0]; - if (selectedEntityID === selectedParticleEntityID) { - return; - } - var type = Entities.getEntityProperties(selectedEntityID, "type").type; - if (type === "ParticleEffect") { - // Destroy the old particles web view first - particleExplorerTool.destroyWebView(); - particleExplorerTool.createWebView(); - var properties = Entities.getEntityProperties(selectedEntityID); - var particleData = { - messageType: "particle_settings", - currentProperties: properties - }; - selectedParticleEntityID = selectedEntityID; - particleExplorerTool.setActiveParticleEntity(selectedParticleEntityID); - - particleExplorerTool.webView.webEventReceived.connect(function (data) { - data = JSON.parse(data); - if (data.messageType === "page_loaded") { - particleExplorerTool.webView.emitScriptEvent(JSON.stringify(particleData)); - } - }); - } else { - needToDestroyParticleExplorer = true; - } - } else { - needToDestroyParticleExplorer = true; - } - - if (needToDestroyParticleExplorer && selectedParticleEntityID !== null) { - selectedParticleEntityID = null; - particleExplorerTool.destroyWebView(); - } + lightOverlayManager.updatePositions(); }); const KEY_P = 80; //Key code for letter p used for Parenting hotkey. @@ -134,13 +82,13 @@ var DEFAULT_LIGHT_DIMENSIONS = Vec3.multiply(20, DEFAULT_DIMENSIONS); var MENU_AUTO_FOCUS_ON_SELECT = "Auto Focus on Select"; var MENU_EASE_ON_FOCUS = "Ease Orientation on Focus"; -var MENU_SHOW_LIGHTS_AND_PARTICLES_IN_EDIT_MODE = "Show Lights and Particle Systems in Edit Mode"; +var MENU_SHOW_LIGHTS_IN_EDIT_MODE = "Show Lights in Edit Mode"; var MENU_SHOW_ZONES_IN_EDIT_MODE = "Show Zones in Edit Mode"; var SETTING_INSPECT_TOOL_ENABLED = "inspectToolEnabled"; var SETTING_AUTO_FOCUS_ON_SELECT = "autoFocusOnSelect"; var SETTING_EASE_ON_FOCUS = "cameraEaseOnFocus"; -var SETTING_SHOW_LIGHTS_AND_PARTICLES_IN_EDIT_MODE = "showLightsAndParticlesInEditMode"; +var SETTING_SHOW_LIGHTS_IN_EDIT_MODE = "showLightsInEditMode"; var SETTING_SHOW_ZONES_IN_EDIT_MODE = "showZonesInEditMode"; @@ -558,7 +506,7 @@ var toolBar = (function () { toolBar.writeProperty("shown", false); toolBar.writeProperty("shown", true); } - entityIconOverlayManager.setVisible(isActive && Menu.isOptionChecked(MENU_SHOW_LIGHTS_AND_PARTICLES_IN_EDIT_MODE)); + lightOverlayManager.setVisible(isActive && Menu.isOptionChecked(MENU_SHOW_LIGHTS_IN_EDIT_MODE)); Entities.setDrawZoneBoundaries(isActive && Menu.isOptionChecked(MENU_SHOW_ZONES_IN_EDIT_MODE)); }; @@ -623,8 +571,8 @@ function findClickedEntity(event) { } var entityResult = Entities.findRayIntersection(pickRay, true); // want precision picking - var iconResult = entityIconOverlayManager.findRayIntersection(pickRay); - iconResult.accurate = true; + var lightResult = lightOverlayManager.findRayIntersection(pickRay); + lightResult.accurate = true; if (pickZones) { Entities.setZonesArePickable(false); @@ -632,12 +580,18 @@ function findClickedEntity(event) { var result; - if (iconResult.intersects) { - result = iconResult; - } else if (entityResult.intersects) { - result = entityResult; - } else { + if (!entityResult.intersects && !lightResult.intersects) { return null; + } else if (entityResult.intersects && !lightResult.intersects) { + result = entityResult; + } else if (!entityResult.intersects && lightResult.intersects) { + result = lightResult; + } else { + if (entityResult.distance < lightResult.distance) { + result = entityResult; + } else { + result = lightResult; + } } if (!result.accurate) { @@ -816,7 +770,7 @@ function mouseClickEvent(event) { if (0 < x && sizeOK) { selectedEntityID = foundEntity; orientation = MyAvatar.orientation; - intersection = rayPlaneIntersection(pickRay, P, Quat.getForward(orientation)); + intersection = rayPlaneIntersection(pickRay, P, Quat.getFront(orientation)); if (!event.isShifted) { @@ -991,18 +945,18 @@ function setupModelMenus() { }); Menu.addMenuItem({ menuName: "Edit", - menuItemName: MENU_SHOW_LIGHTS_AND_PARTICLES_IN_EDIT_MODE, + menuItemName: MENU_SHOW_LIGHTS_IN_EDIT_MODE, afterItem: MENU_EASE_ON_FOCUS, isCheckable: true, - isChecked: Settings.getValue(SETTING_SHOW_LIGHTS_AND_PARTICLES_IN_EDIT_MODE) !== "false", + isChecked: Settings.getValue(SETTING_SHOW_LIGHTS_IN_EDIT_MODE) === "true", grouping: "Advanced" }); Menu.addMenuItem({ menuName: "Edit", menuItemName: MENU_SHOW_ZONES_IN_EDIT_MODE, - afterItem: MENU_SHOW_LIGHTS_AND_PARTICLES_IN_EDIT_MODE, + afterItem: MENU_SHOW_LIGHTS_IN_EDIT_MODE, isCheckable: true, - isChecked: Settings.getValue(SETTING_SHOW_ZONES_IN_EDIT_MODE) !== "false", + isChecked: Settings.getValue(SETTING_SHOW_ZONES_IN_EDIT_MODE) === "true", grouping: "Advanced" }); @@ -1033,7 +987,7 @@ function cleanupModelMenus() { Menu.removeMenuItem("Edit", MENU_AUTO_FOCUS_ON_SELECT); Menu.removeMenuItem("Edit", MENU_EASE_ON_FOCUS); - Menu.removeMenuItem("Edit", MENU_SHOW_LIGHTS_AND_PARTICLES_IN_EDIT_MODE); + Menu.removeMenuItem("Edit", MENU_SHOW_LIGHTS_IN_EDIT_MODE); Menu.removeMenuItem("Edit", MENU_SHOW_ZONES_IN_EDIT_MODE); } @@ -1041,7 +995,7 @@ Script.scriptEnding.connect(function () { toolBar.setActive(false); Settings.setValue(SETTING_AUTO_FOCUS_ON_SELECT, Menu.isOptionChecked(MENU_AUTO_FOCUS_ON_SELECT)); Settings.setValue(SETTING_EASE_ON_FOCUS, Menu.isOptionChecked(MENU_EASE_ON_FOCUS)); - Settings.setValue(SETTING_SHOW_LIGHTS_AND_PARTICLES_IN_EDIT_MODE, Menu.isOptionChecked(MENU_SHOW_LIGHTS_AND_PARTICLES_IN_EDIT_MODE)); + Settings.setValue(SETTING_SHOW_LIGHTS_IN_EDIT_MODE, Menu.isOptionChecked(MENU_SHOW_LIGHTS_IN_EDIT_MODE)); Settings.setValue(SETTING_SHOW_ZONES_IN_EDIT_MODE, Menu.isOptionChecked(MENU_SHOW_ZONES_IN_EDIT_MODE)); progressDialog.cleanup(); @@ -1230,7 +1184,7 @@ function parentSelectedEntities() { } function deleteSelectedEntities() { if (SelectionManager.hasSelection()) { - selectedParticleEntityID = null; + selectedParticleEntity = 0; particleExplorerTool.destroyWebView(); SelectionManager.saveProperties(); var savedProperties = []; @@ -1329,8 +1283,8 @@ function handeMenuEvent(menuItem) { selectAllEtitiesInCurrentSelectionBox(false); } else if (menuItem === "Select All Entities Touching Box") { selectAllEtitiesInCurrentSelectionBox(true); - } else if (menuItem === MENU_SHOW_LIGHTS_AND_PARTICLES_IN_EDIT_MODE) { - entityIconOverlayManager.setVisible(isActive && Menu.isOptionChecked(MENU_SHOW_LIGHTS_AND_PARTICLES_IN_EDIT_MODE)); + } else if (menuItem === MENU_SHOW_LIGHTS_IN_EDIT_MODE) { + lightOverlayManager.setVisible(isActive && Menu.isOptionChecked(MENU_SHOW_LIGHTS_IN_EDIT_MODE)); } else if (menuItem === MENU_SHOW_ZONES_IN_EDIT_MODE) { Entities.setDrawZoneBoundaries(isActive && Menu.isOptionChecked(MENU_SHOW_ZONES_IN_EDIT_MODE)); } @@ -1338,12 +1292,12 @@ function handeMenuEvent(menuItem) { } function getPositionToCreateEntity() { var HALF_TREE_SCALE = 16384; - var direction = Quat.getForward(MyAvatar.orientation); + var direction = Quat.getFront(MyAvatar.orientation); var distance = 1; var position = Vec3.sum(MyAvatar.position, Vec3.multiply(direction, distance)); if (Camera.mode === "entity" || Camera.mode === "independent") { - position = Vec3.sum(Camera.position, Vec3.multiply(Quat.getForward(Camera.orientation), distance)) + position = Vec3.sum(Camera.position, Vec3.multiply(Quat.getFront(Camera.orientation), distance)) } position.y += 0.5; if (position.x > HALF_TREE_SCALE || position.y > HALF_TREE_SCALE || position.z > HALF_TREE_SCALE) { @@ -1355,13 +1309,13 @@ function getPositionToCreateEntity() { function getPositionToImportEntity() { var dimensions = Clipboard.getContentsDimensions(); var HALF_TREE_SCALE = 16384; - var direction = Quat.getForward(MyAvatar.orientation); + var direction = Quat.getFront(MyAvatar.orientation); var longest = 1; longest = Math.sqrt(Math.pow(dimensions.x, 2) + Math.pow(dimensions.z, 2)); var position = Vec3.sum(MyAvatar.position, Vec3.multiply(direction, longest)); if (Camera.mode === "entity" || Camera.mode === "independent") { - position = Vec3.sum(Camera.position, Vec3.multiply(Quat.getForward(Camera.orientation), longest)) + position = Vec3.sum(Camera.position, Vec3.multiply(Quat.getFront(Camera.orientation), longest)) } if (position.x > HALF_TREE_SCALE || position.y > HALF_TREE_SCALE || position.z > HALF_TREE_SCALE) { @@ -2005,13 +1959,43 @@ var showMenuItem = propertyMenu.addMenuItem("Show in Marketplace"); var propertiesTool = new PropertiesTool(); var particleExplorerTool = new ParticleExplorerTool(); -var selectedParticleEntityID = null; +var selectedParticleEntity = 0; entityListTool.webView.webEventReceived.connect(function (data) { data = JSON.parse(data); - if (data.type === 'parent') { + if(data.type === 'parent') { parentSelectedEntities(); } else if(data.type === 'unparent') { unparentSelectedEntities(); + } else if (data.type === "selectionUpdate") { + var ids = data.entityIds; + if (ids.length === 1) { + if (Entities.getEntityProperties(ids[0], "type").type === "ParticleEffect") { + if (JSON.stringify(selectedParticleEntity) === JSON.stringify(ids[0])) { + // This particle entity is already selected, so return + return; + } + // Destroy the old particles web view first + particleExplorerTool.destroyWebView(); + particleExplorerTool.createWebView(); + var properties = Entities.getEntityProperties(ids[0]); + var particleData = { + messageType: "particle_settings", + currentProperties: properties + }; + selectedParticleEntity = ids[0]; + particleExplorerTool.setActiveParticleEntity(ids[0]); + + particleExplorerTool.webView.webEventReceived.connect(function (data) { + data = JSON.parse(data); + if (data.messageType === "page_loaded") { + particleExplorerTool.webView.emitScriptEvent(JSON.stringify(particleData)); + } + }); + } else { + selectedParticleEntity = 0; + particleExplorerTool.destroyWebView(); + } + } } }); diff --git a/scripts/system/libraries/WebTablet.js b/scripts/system/libraries/WebTablet.js index 32f45188dc..dd2aaf346b 100644 --- a/scripts/system/libraries/WebTablet.js +++ b/scripts/system/libraries/WebTablet.js @@ -78,9 +78,9 @@ function calcSpawnInfo(hand, height) { rotation: lookAtRot }; } else { - var forward = Quat.getForward(headRot); - finalPosition = Vec3.sum(headPos, Vec3.multiply(0.6, forward)); - var orientation = Quat.lookAt({x: 0, y: 0, z: 0}, forward, {x: 0, y: 1, z: 0}); + var front = Quat.getFront(headRot); + finalPosition = Vec3.sum(headPos, Vec3.multiply(0.6, front)); + var orientation = Quat.lookAt({x: 0, y: 0, z: 0}, front, {x: 0, y: 1, z: 0}); return { position: finalPosition, rotation: Quat.multiply(orientation, {x: 0, y: 1, z: 0, w: 0}) diff --git a/scripts/system/libraries/entityCameraTool.js b/scripts/system/libraries/entityCameraTool.js index 6becc81d9b..301b60f550 100644 --- a/scripts/system/libraries/entityCameraTool.js +++ b/scripts/system/libraries/entityCameraTool.js @@ -158,7 +158,7 @@ CameraManager = function() { that.zoomDistance = INITIAL_ZOOM_DISTANCE; that.targetZoomDistance = that.zoomDistance + 3.0; var focalPoint = Vec3.sum(Camera.getPosition(), - Vec3.multiply(that.zoomDistance, Quat.getForward(Camera.getOrientation()))); + Vec3.multiply(that.zoomDistance, Quat.getFront(Camera.getOrientation()))); // Determine the correct yaw and pitch to keep the camera in the same location var dPos = Vec3.subtract(focalPoint, Camera.getPosition()); @@ -435,7 +435,7 @@ CameraManager = function() { }); var q = Quat.multiply(yRot, xRot); - var pos = Vec3.multiply(Quat.getForward(q), that.zoomDistance); + var pos = Vec3.multiply(Quat.getFront(q), that.zoomDistance); Camera.setPosition(Vec3.sum(that.focalPoint, pos)); yRot = Quat.angleAxis(that.yaw - 180, { diff --git a/scripts/system/libraries/entitySelectionTool.js b/scripts/system/libraries/entitySelectionTool.js index 9d4bf7d9a8..d68a525458 100644 --- a/scripts/system/libraries/entitySelectionTool.js +++ b/scripts/system/libraries/entitySelectionTool.js @@ -1032,12 +1032,10 @@ SelectionDisplay = (function() { var pickRay = controllerComputePickRay(); if (pickRay) { var entityIntersection = Entities.findRayIntersection(pickRay, true); - var iconIntersection = entityIconOverlayManager.findRayIntersection(pickRay); - var overlayIntersection = Overlays.findRayIntersection(pickRay); - if (iconIntersection.intersects) { - selectionManager.setSelections([iconIntersection.entityID]); - } else if (entityIntersection.intersects && + + var overlayIntersection = Overlays.findRayIntersection(pickRay); + if (entityIntersection.intersects && (!overlayIntersection.intersects || (entityIntersection.distance < overlayIntersection.distance))) { if (HMD.tabletID === entityIntersection.entityID) { @@ -2517,7 +2515,7 @@ SelectionDisplay = (function() { onBegin: function(event) { pickRay = generalComputePickRay(event.x, event.y); - upDownPickNormal = Quat.getForward(lastCameraOrientation); + upDownPickNormal = Quat.getFront(lastCameraOrientation); // Remove y component so the y-axis lies along the plane we picking on - this will // give movements that follow the mouse. upDownPickNormal.y = 0; diff --git a/scripts/system/libraries/entityIconOverlayManager.js b/scripts/system/libraries/lightOverlayManager.js similarity index 67% rename from scripts/system/libraries/entityIconOverlayManager.js rename to scripts/system/libraries/lightOverlayManager.js index 7f7a293bc3..2d3618096b 100644 --- a/scripts/system/libraries/entityIconOverlayManager.js +++ b/scripts/system/libraries/lightOverlayManager.js @@ -1,6 +1,9 @@ -/* globals EntityIconOverlayManager:true */ +var POINT_LIGHT_URL = "http://s3.amazonaws.com/hifi-public/images/tools/point-light.svg"; +var SPOT_LIGHT_URL = "http://s3.amazonaws.com/hifi-public/images/tools/spot-light.svg"; + +LightOverlayManager = function() { + var self = this; -EntityIconOverlayManager = function(entityTypes, getOverlayPropertiesFunc) { var visible = false; // List of all created overlays @@ -19,16 +22,9 @@ EntityIconOverlayManager = function(entityTypes, getOverlayPropertiesFunc) { for (var id in entityIDs) { var entityID = entityIDs[id]; var properties = Entities.getEntityProperties(entityID); - var overlayProperties = { + Overlays.editOverlay(entityOverlays[entityID], { position: properties.position - }; - if (getOverlayPropertiesFunc) { - var customProperties = getOverlayPropertiesFunc(entityID, properties); - for (var key in customProperties) { - overlayProperties[key] = customProperties[key]; - } - } - Overlays.editOverlay(entityOverlays[entityID], overlayProperties); + }); } }; @@ -38,7 +34,7 @@ EntityIconOverlayManager = function(entityTypes, getOverlayPropertiesFunc) { if (result.intersects) { for (var id in entityOverlays) { - if (result.overlayID === entityOverlays[id]) { + if (result.overlayID == entityOverlays[id]) { result.entityID = entityIDs[id]; found = true; break; @@ -54,7 +50,7 @@ EntityIconOverlayManager = function(entityTypes, getOverlayPropertiesFunc) { }; this.setVisible = function(isVisible) { - if (visible !== isVisible) { + if (visible != isVisible) { visible = isVisible; for (var id in entityOverlays) { Overlays.editOverlay(entityOverlays[id], { @@ -66,13 +62,12 @@ EntityIconOverlayManager = function(entityTypes, getOverlayPropertiesFunc) { // Allocate or get an unused overlay function getOverlay() { - var overlay; - if (unusedOverlays.length === 0) { - overlay = Overlays.addOverlay("image3d", {}); + if (unusedOverlays.length == 0) { + var overlay = Overlays.addOverlay("image3d", {}); allOverlays.push(overlay); } else { - overlay = unusedOverlays.pop(); - } + var overlay = unusedOverlays.pop(); + }; return overlay; } @@ -84,32 +79,24 @@ EntityIconOverlayManager = function(entityTypes, getOverlayPropertiesFunc) { } function addEntity(entityID) { - var properties = Entities.getEntityProperties(entityID, ['position', 'type']); - if (entityTypes.indexOf(properties.type) > -1 && !(entityID in entityOverlays)) { + var properties = Entities.getEntityProperties(entityID); + if (properties.type == "Light" && !(entityID in entityOverlays)) { var overlay = getOverlay(); entityOverlays[entityID] = overlay; entityIDs[entityID] = entityID; - var overlayProperties = { + Overlays.editOverlay(overlay, { position: properties.position, + url: properties.isSpotlight ? SPOT_LIGHT_URL : POINT_LIGHT_URL, rotation: Quat.fromPitchYawRollDegrees(0, 0, 270), visible: visible, alpha: 0.9, scale: 0.5, - drawInFront: true, - isFacingAvatar: true, color: { red: 255, green: 255, blue: 255 } - }; - if (getOverlayPropertiesFunc) { - var customProperties = getOverlayPropertiesFunc(entityID, properties); - for (var key in customProperties) { - overlayProperties[key] = customProperties[key]; - } - } - Overlays.editOverlay(overlay, overlayProperties); + }); } } @@ -143,4 +130,4 @@ EntityIconOverlayManager = function(entityTypes, getOverlayPropertiesFunc) { Overlays.deleteOverlay(allOverlays[i]); } }); -}; +}; \ No newline at end of file diff --git a/scripts/system/libraries/soundArray.js b/scripts/system/libraries/soundArray.js index 7e5da11948..f59c88a723 100644 --- a/scripts/system/libraries/soundArray.js +++ b/scripts/system/libraries/soundArray.js @@ -36,7 +36,7 @@ SoundArray = function(audioOptions, autoUpdateAudioPosition) { }; this.updateAudioPosition = function() { var position = MyAvatar.position; - var forwardVector = Quat.getForward(MyAvatar.orientation); + var forwardVector = Quat.getFront(MyAvatar.orientation); this.audioOptions.position = Vec3.sum(position, forwardVector); }; }; diff --git a/scripts/system/libraries/toolBars.js b/scripts/system/libraries/toolBars.js index 351f10e7bd..e49f8c4004 100644 --- a/scripts/system/libraries/toolBars.js +++ b/scripts/system/libraries/toolBars.js @@ -160,7 +160,6 @@ ToolBar = function(x, y, direction, optionalPersistenceKey, optionalInitialPosit visible: false }); this.spacing = []; - this.onMove = null; this.addTool = function(properties, selectable, selected) { if (direction == ToolBar.HORIZONTAL) { @@ -255,9 +254,6 @@ ToolBar = function(x, y, direction, optionalPersistenceKey, optionalInitialPosit y: y - ToolBar.SPACING }); } - if (this.onMove !== null) { - this.onMove(x, y, dx, dy); - }; } this.setAlpha = function(alpha, tool) { diff --git a/scripts/system/nameTag.js b/scripts/system/nameTag.js index 17944bcf85..e25db69064 100644 --- a/scripts/system/nameTag.js +++ b/scripts/system/nameTag.js @@ -33,7 +33,7 @@ Script.setTimeout(function() { }, STARTUP_DELAY); function addNameTag() { - var nameTagPosition = Vec3.sum(MyAvatar.getHeadPosition(), Vec3.multiply(HEAD_OFFSET, Quat.getForward(MyAvatar.orientation))); + var nameTagPosition = Vec3.sum(MyAvatar.getHeadPosition(), Vec3.multiply(HEAD_OFFSET, Quat.getFront(MyAvatar.orientation))); nameTagPosition.y += HEIGHT_ABOVE_HEAD; var nameTagProperties = { name: MyAvatar.displayName + ' Name Tag', @@ -49,7 +49,7 @@ function addNameTag() { function updateNameTag() { var nameTagProps = Entities.getEntityProperties(nameTagEntityID); - var nameTagPosition = Vec3.sum(MyAvatar.getHeadPosition(), Vec3.multiply(HEAD_OFFSET, Quat.getForward(MyAvatar.orientation))); + var nameTagPosition = Vec3.sum(MyAvatar.getHeadPosition(), Vec3.multiply(HEAD_OFFSET, Quat.getFront(MyAvatar.orientation))); nameTagPosition.y += HEIGHT_ABOVE_HEAD; Entities.editEntity(nameTagEntityID, { diff --git a/scripts/system/pal.js b/scripts/system/pal.js index a7c4f56ea6..d9734850e5 100644 --- a/scripts/system/pal.js +++ b/scripts/system/pal.js @@ -444,7 +444,7 @@ function populateNearbyUserList(selectData, oldAudioData) { verticalHalfAngle = filter && (frustum.fieldOfView / 2), horizontalHalfAngle = filter && (verticalHalfAngle * frustum.aspectRatio), orientation = filter && Camera.orientation, - forward = filter && Quat.getForward(orientation), + front = filter && Quat.getFront(orientation), verticalAngleNormal = filter && Quat.getRight(orientation), horizontalAngleNormal = filter && Quat.getUp(orientation); avatarsOfInterest = {}; @@ -463,8 +463,8 @@ function populateNearbyUserList(selectData, oldAudioData) { return; } var normal = id && filter && Vec3.normalize(Vec3.subtract(avatar.position, myPosition)); - var horizontal = normal && angleBetweenVectorsInPlane(normal, forward, horizontalAngleNormal); - var vertical = normal && angleBetweenVectorsInPlane(normal, forward, verticalAngleNormal); + var horizontal = normal && angleBetweenVectorsInPlane(normal, front, horizontalAngleNormal); + var vertical = normal && angleBetweenVectorsInPlane(normal, front, verticalAngleNormal); if (id && filter && ((Math.abs(horizontal) > horizontalHalfAngle) || (Math.abs(vertical) > verticalHalfAngle))) { return; } diff --git a/scripts/system/voxels.js b/scripts/system/voxels.js index 2f1d0eced9..3c219ebc7a 100644 --- a/scripts/system/voxels.js +++ b/scripts/system/voxels.js @@ -253,7 +253,7 @@ function addTerrainBlock() { if (alreadyThere) { // there is already a terrain block under MyAvatar. // try in front of the avatar. - facingPosition = Vec3.sum(MyAvatar.position, Vec3.multiply(8.0, Quat.getForward(Camera.getOrientation()))); + facingPosition = Vec3.sum(MyAvatar.position, Vec3.multiply(8.0, Quat.getFront(Camera.getOrientation()))); facingPosition = Vec3.sum(facingPosition, { x: 8, y: 8, diff --git a/scripts/tutorials/NBody/makePlanets.js b/scripts/tutorials/NBody/makePlanets.js index 21415ccdc2..58a3c7cc2d 100644 --- a/scripts/tutorials/NBody/makePlanets.js +++ b/scripts/tutorials/NBody/makePlanets.js @@ -53,7 +53,7 @@ var deleteButton = toolBar.addOverlay("image", { }); function inFrontOfMe(distance) { - return Vec3.sum(Camera.getPosition(), Vec3.multiply(distance, Quat.getForward(Camera.getOrientation()))); + return Vec3.sum(Camera.getPosition(), Vec3.multiply(distance, Quat.getFront(Camera.getOrientation()))); } function onButtonClick() { diff --git a/scripts/tutorials/butterflies.js b/scripts/tutorials/butterflies.js index 9d8d1de52c..55bafc0a27 100644 --- a/scripts/tutorials/butterflies.js +++ b/scripts/tutorials/butterflies.js @@ -44,8 +44,8 @@ var FIXED_LOCATION = false; if (!FIXED_LOCATION) { var flockPosition = Vec3.sum(MyAvatar.position,Vec3.sum( - Vec3.multiply(Quat.getForward(MyAvatar.orientation), DISTANCE_ABOVE_ME), - Vec3.multiply(Quat.getForward(MyAvatar.orientation), DISTANCE_IN_FRONT_OF_ME))); + Vec3.multiply(Quat.getFront(MyAvatar.orientation), DISTANCE_ABOVE_ME), + Vec3.multiply(Quat.getFront(MyAvatar.orientation), DISTANCE_IN_FRONT_OF_ME))); } else { var flockPosition = { x: 4999.6, y: 4986.5, z: 5003.5 }; } @@ -119,7 +119,7 @@ function updateButterflies(deltaTime) { var HORIZ_SCALE = 0.50; var VERT_SCALE = 0.50; var newHeading = Math.random() * 360.0; - var newVelocity = Vec3.multiply(HORIZ_SCALE, Quat.getForward(Quat.fromPitchYawRollDegrees(0.0, newHeading, 0.0))); + var newVelocity = Vec3.multiply(HORIZ_SCALE, Quat.getFront(Quat.fromPitchYawRollDegrees(0.0, newHeading, 0.0))); newVelocity.y = (Math.random() + 0.5) * VERT_SCALE; Entities.editEntity(butterflies[i], { rotation: Quat.fromPitchYawRollDegrees(-80 + Math.random() * 20, newHeading, (Math.random() - 0.5) * 10), velocity: newVelocity } ); diff --git a/scripts/tutorials/createCow.js b/scripts/tutorials/createCow.js index 16498e0e8c..7446aa0fd0 100644 --- a/scripts/tutorials/createCow.js +++ b/scripts/tutorials/createCow.js @@ -18,7 +18,7 @@ var orientation = MyAvatar.orientation; orientation = Quat.safeEulerAngles(orientation); orientation.x = 0; orientation = Quat.fromVec3Degrees(orientation); -var center = Vec3.sum(MyAvatar.getHeadPosition(), Vec3.multiply(2, Quat.getForward(orientation))); +var center = Vec3.sum(MyAvatar.getHeadPosition(), Vec3.multiply(2, Quat.getFront(orientation))); // An entity is described and created by specifying a map of properties var cow = Entities.addEntity({ diff --git a/scripts/tutorials/createDice.js b/scripts/tutorials/createDice.js index 46ad0172aa..0d39d11d48 100644 --- a/scripts/tutorials/createDice.js +++ b/scripts/tutorials/createDice.js @@ -127,8 +127,8 @@ function mousePressEvent(event) { deleteDice(); } else if (clickedOverlay == diceButton) { var HOW_HARD = 2.0; - var position = Vec3.sum(Camera.getPosition(), Quat.getForward(Camera.getOrientation())); - var velocity = Vec3.multiply(HOW_HARD, Quat.getForward(Camera.getOrientation())); + var position = Vec3.sum(Camera.getPosition(), Quat.getFront(Camera.getOrientation())); + var velocity = Vec3.multiply(HOW_HARD, Quat.getFront(Camera.getOrientation())); shootDice(position, velocity); madeSound = false; } diff --git a/scripts/tutorials/createFlashlight.js b/scripts/tutorials/createFlashlight.js index f3e1e72182..0e3581a435 100644 --- a/scripts/tutorials/createFlashlight.js +++ b/scripts/tutorials/createFlashlight.js @@ -16,7 +16,7 @@ var center = Vec3.sum(Vec3.sum(MyAvatar.position, { x: 0, y: 0.5, z: 0 -}), Vec3.multiply(0.5, Quat.getForward(Camera.getOrientation()))); +}), Vec3.multiply(0.5, Quat.getFront(Camera.getOrientation()))); var flashlight = Entities.addEntity({ type: "Model", diff --git a/scripts/tutorials/createGolfClub.js b/scripts/tutorials/createGolfClub.js index 21e60f26ef..aa9834276a 100644 --- a/scripts/tutorials/createGolfClub.js +++ b/scripts/tutorials/createGolfClub.js @@ -15,7 +15,7 @@ var orientation = MyAvatar.orientation; orientation = Quat.safeEulerAngles(orientation); orientation.x = 0; orientation = Quat.fromVec3Degrees(orientation); -var center = Vec3.sum(MyAvatar.getHeadPosition(), Vec3.multiply(2, Quat.getForward(orientation))); +var center = Vec3.sum(MyAvatar.getHeadPosition(), Vec3.multiply(2, Quat.getFront(orientation))); var CLUB_MODEL = "http://hifi-production.s3.amazonaws.com/tutorials/golfClub/putter_VR.fbx"; var CLUB_COLLISION_HULL = "http://hifi-production.s3.amazonaws.com/tutorials/golfClub/club_collision_hull.obj"; diff --git a/scripts/tutorials/createPictureFrame.js b/scripts/tutorials/createPictureFrame.js index 873b604bfa..4a1e5b16a7 100644 --- a/scripts/tutorials/createPictureFrame.js +++ b/scripts/tutorials/createPictureFrame.js @@ -14,7 +14,7 @@ var center = Vec3.sum(Vec3.sum(MyAvatar.position, { x: 0, y: 0.5, z: 0 -}), Vec3.multiply(1, Quat.getForward(Camera.getOrientation()))); +}), Vec3.multiply(1, Quat.getFront(Camera.getOrientation()))); // this is just a model exported from blender with a texture named 'Picture' on one face. also made it emissive so it doesn't require lighting. var MODEL_URL = "http://hifi-production.s3.amazonaws.com/tutorials/pictureFrame/finalFrame.fbx"; diff --git a/scripts/tutorials/createPingPongGun.js b/scripts/tutorials/createPingPongGun.js index c86a78e96d..a077e5308d 100644 --- a/scripts/tutorials/createPingPongGun.js +++ b/scripts/tutorials/createPingPongGun.js @@ -14,7 +14,7 @@ var center = Vec3.sum(Vec3.sum(MyAvatar.position, { x: 0, y: 0.5, z: 0 -}), Vec3.multiply(0.5, Quat.getForward(Camera.getOrientation()))); +}), Vec3.multiply(0.5, Quat.getFront(Camera.getOrientation()))); var pingPongGunProperties = { diff --git a/scripts/tutorials/createPistol.js b/scripts/tutorials/createPistol.js index 8851f53d09..ae2f398840 100644 --- a/scripts/tutorials/createPistol.js +++ b/scripts/tutorials/createPistol.js @@ -6,7 +6,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -var center = Vec3.sum(MyAvatar.position, Vec3.multiply(1.5, Quat.getForward(Camera.getOrientation()))); +var center = Vec3.sum(MyAvatar.position, Vec3.multiply(1.5, Quat.getFront(Camera.getOrientation()))); var SCRIPT_URL = "http://hifi-production.s3.amazonaws.com/tutorials/entity_scripts/pistol.js"; var MODEL_URL = "http://hifi-production.s3.amazonaws.com/tutorials/pistol/gun.fbx"; var COLLISION_SOUND_URL = 'http://hifi-production.s3.amazonaws.com/tutorials/pistol/drop.wav' diff --git a/scripts/tutorials/createSoundMaker.js b/scripts/tutorials/createSoundMaker.js index 2d86864982..b79c650e27 100644 --- a/scripts/tutorials/createSoundMaker.js +++ b/scripts/tutorials/createSoundMaker.js @@ -13,7 +13,7 @@ var center = Vec3.sum(Vec3.sum(MyAvatar.position, { x: 0, y: 0.5, z: 0 -}), Vec3.multiply(1, Quat.getForward(Camera.getOrientation()))); +}), Vec3.multiply(1, Quat.getFront(Camera.getOrientation()))); function makeBell() { var soundMakerProperties = { diff --git a/scripts/tutorials/entity_scripts/golfClub.js b/scripts/tutorials/entity_scripts/golfClub.js index 6342838aa4..2df3be8b60 100644 --- a/scripts/tutorials/entity_scripts/golfClub.js +++ b/scripts/tutorials/entity_scripts/golfClub.js @@ -57,7 +57,7 @@ // Position yourself facing in the direction you were originally facing, but with a // point on the ground *away* meters from *position* and in front of you. - var offset = Quat.getForward(MyAvatar.orientation); + var offset = Quat.getFront(MyAvatar.orientation); offset.y = 0.0; offset = Vec3.multiply(-away, Vec3.normalize(offset)); var newAvatarPosition = Vec3.sum(position, offset); @@ -72,7 +72,7 @@ } function inFrontOfMe() { - return Vec3.sum(MyAvatar.position, Vec3.multiply(BALL_DROP_DISTANCE, Quat.getForward(MyAvatar.orientation))); + return Vec3.sum(MyAvatar.position, Vec3.multiply(BALL_DROP_DISTANCE, Quat.getFront(MyAvatar.orientation))); } function avatarHalfHeight() { diff --git a/scripts/tutorials/entity_scripts/pingPongGun.js b/scripts/tutorials/entity_scripts/pingPongGun.js index 5ba4b15ea7..4ec0254747 100644 --- a/scripts/tutorials/entity_scripts/pingPongGun.js +++ b/scripts/tutorials/entity_scripts/pingPongGun.js @@ -94,9 +94,9 @@ }, shootBall: function(gunProperties) { - var forwardVector = Quat.getForward(Quat.multiply(gunProperties.rotation, Quat.fromPitchYawRollDegrees(0, 180, 0))); - forwardVector = Vec3.normalize(forwardVector); - forwardVector = Vec3.multiply(forwardVector, GUN_FORCE); + var forwardVec = Quat.getFront(Quat.multiply(gunProperties.rotation, Quat.fromPitchYawRollDegrees(0, 180, 0))); + forwardVec = Vec3.normalize(forwardVec); + forwardVec = Vec3.multiply(forwardVec, GUN_FORCE); var properties = { name: 'Tutorial Ping Pong Ball', @@ -111,7 +111,7 @@ rotation: gunProperties.rotation, position: this.getGunTipPosition(gunProperties), gravity: PING_PONG_GUN_GRAVITY, - velocity: forwardVector, + velocity: forwardVec, lifetime: 10 }; @@ -131,12 +131,12 @@ getGunTipPosition: function(properties) { //the tip of the gun is going to be in a different place than the center, so we move in space relative to the model to find that position - var forwardVector = Quat.getForward(properties.rotation); - var forwardOffset = Vec3.multiply(forwardVector, GUN_TIP_FWD_OFFSET); + var frontVector = Quat.getFront(properties.rotation); + var frontOffset = Vec3.multiply(frontVector, GUN_TIP_FWD_OFFSET); var upVector = Quat.getUp(properties.rotation); var upOffset = Vec3.multiply(upVector, GUN_TIP_UP_OFFSET); - var gunTipPosition = Vec3.sum(properties.position, forwardOffset); + var gunTipPosition = Vec3.sum(properties.position, frontOffset); gunTipPosition = Vec3.sum(gunTipPosition, upOffset); return gunTipPosition; diff --git a/scripts/tutorials/entity_scripts/pistol.js b/scripts/tutorials/entity_scripts/pistol.js index 38eb929177..73a6daab93 100644 --- a/scripts/tutorials/entity_scripts/pistol.js +++ b/scripts/tutorials/entity_scripts/pistol.js @@ -69,7 +69,7 @@ var gunProps = Entities.getEntityProperties(this.entityID, ['position', 'rotation']); this.position = gunProps.position; this.rotation = gunProps.rotation; - this.firingDirection = Quat.getForward(this.rotation); + this.firingDirection = Quat.getFront(this.rotation); var upVec = Quat.getUp(this.rotation); this.barrelPoint = Vec3.sum(this.position, Vec3.multiply(upVec, this.laserOffsets.y)); this.laserTip = Vec3.sum(this.barrelPoint, Vec3.multiply(this.firingDirection, this.laserLength)); diff --git a/scripts/tutorials/entity_scripts/sit.js b/scripts/tutorials/entity_scripts/sit.js index 82afdc8974..2ba19231e0 100644 --- a/scripts/tutorials/entity_scripts/sit.js +++ b/scripts/tutorials/entity_scripts/sit.js @@ -2,41 +2,31 @@ Script.include("/~/system/libraries/utils.js"); var SETTING_KEY = "com.highfidelity.avatar.isSitting"; + var ROLE = "fly"; var ANIMATION_URL = "https://s3-us-west-1.amazonaws.com/hifi-content/clement/production/animations/sitting_idle.fbx"; var ANIMATION_FPS = 30; var ANIMATION_FIRST_FRAME = 1; var ANIMATION_LAST_FRAME = 10; + var RELEASE_KEYS = ['w', 'a', 's', 'd', 'UP', 'LEFT', 'DOWN', 'RIGHT']; var RELEASE_TIME = 500; // ms var RELEASE_DISTANCE = 0.2; // meters - var MAX_IK_ERROR = 30; - var IK_SETTLE_TIME = 250; // ms - var DESKTOP_UI_CHECK_INTERVAL = 100; + var MAX_IK_ERROR = 20; + var DESKTOP_UI_CHECK_INTERVAL = 250; var DESKTOP_MAX_DISTANCE = 5; - var SIT_DELAY = 25; - var MAX_RESET_DISTANCE = 0.5; // meters - var OVERRIDEN_DRIVE_KEYS = [ - DriveKeys.TRANSLATE_X, - DriveKeys.TRANSLATE_Y, - DriveKeys.TRANSLATE_Z, - DriveKeys.STEP_TRANSLATE_X, - DriveKeys.STEP_TRANSLATE_Y, - DriveKeys.STEP_TRANSLATE_Z, - ]; + var SIT_DELAY = 25 this.entityID = null; + this.timers = {}; this.animStateHandlerID = null; - this.interval = null; - this.sitDownSettlePeriod = null; - this.lastTimeNoDriveKeys = null; this.preload = function(entityID) { this.entityID = entityID; } this.unload = function() { - if (Settings.getValue(SETTING_KEY) === this.entityID) { - this.standUp(); + if (MyAvatar.sessionUUID === this.getSeatUser()) { + this.sitUp(this.entityID); } - if (this.interval !== null) { + if (this.interval) { Script.clearInterval(this.interval); this.interval = null; } @@ -44,60 +34,42 @@ } this.setSeatUser = function(user) { - try { - var userData = Entities.getEntityProperties(this.entityID, ["userData"]).userData; - userData = JSON.parse(userData); + var userData = Entities.getEntityProperties(this.entityID, ["userData"]).userData; + userData = JSON.parse(userData); - if (user !== null) { - userData.seat.user = user; - } else { - delete userData.seat.user; - } - - Entities.editEntity(this.entityID, { - userData: JSON.stringify(userData) - }); - } catch (e) { - // Do Nothing + if (user) { + userData.seat.user = user; + } else { + delete userData.seat.user; } + + Entities.editEntity(this.entityID, { + userData: JSON.stringify(userData) + }); } this.getSeatUser = function() { - try { - var properties = Entities.getEntityProperties(this.entityID, ["userData", "position"]); - var userData = JSON.parse(properties.userData); + var properties = Entities.getEntityProperties(this.entityID, ["userData", "position"]); + var userData = JSON.parse(properties.userData); - // If MyAvatar return my uuid - if (userData.seat.user === MyAvatar.sessionUUID) { - return userData.seat.user; + if (userData.seat.user && userData.seat.user !== MyAvatar.sessionUUID) { + var avatar = AvatarList.getAvatar(userData.seat.user); + if (avatar && Vec3.distance(avatar.position, properties.position) > RELEASE_DISTANCE) { + return null; } - - - // If Avatar appears to be sitting - if (userData.seat.user) { - var avatar = AvatarList.getAvatar(userData.seat.user); - if (avatar && (Vec3.distance(avatar.position, properties.position) < RELEASE_DISTANCE)) { - return userData.seat.user; - } - } - } catch (e) { - // Do nothing } - - // Nobody on the seat - return null; + return userData.seat.user; } - // Is the seat used this.checkSeatForAvatar = function() { var seatUser = this.getSeatUser(); - - // If MyAvatar appears to be sitting - if (seatUser === MyAvatar.sessionUUID) { - var properties = Entities.getEntityProperties(this.entityID, ["position"]); - return Vec3.distance(MyAvatar.position, properties.position) < RELEASE_DISTANCE; + var avatarIdentifiers = AvatarList.getAvatarIdentifiers(); + for (var i in avatarIdentifiers) { + var avatar = AvatarList.getAvatar(avatarIdentifiers[i]); + if (avatar && avatar.sessionUUID === seatUser) { + return true; + } } - - return seatUser !== null; + return false; } this.sitDown = function() { @@ -105,53 +77,41 @@ print("Someone is already sitting in that chair."); return; } - print("Sitting down (" + this.entityID + ")"); - var now = Date.now(); - this.sitDownSettlePeriod = now + IK_SETTLE_TIME; - this.lastTimeNoDriveKeys = now; + this.setSeatUser(MyAvatar.sessionUUID); var previousValue = Settings.getValue(SETTING_KEY); Settings.setValue(SETTING_KEY, this.entityID); - this.setSeatUser(MyAvatar.sessionUUID); if (previousValue === "") { MyAvatar.characterControllerEnabled = false; MyAvatar.hmdLeanRecenterEnabled = false; - var ROLES = MyAvatar.getAnimationRoles(); - for (i in ROLES) { - MyAvatar.overrideRoleAnimation(ROLES[i], ANIMATION_URL, ANIMATION_FPS, true, ANIMATION_FIRST_FRAME, ANIMATION_LAST_FRAME); - } + MyAvatar.overrideRoleAnimation(ROLE, ANIMATION_URL, ANIMATION_FPS, true, ANIMATION_FIRST_FRAME, ANIMATION_LAST_FRAME); MyAvatar.resetSensorsAndBody(); } - var properties = Entities.getEntityProperties(this.entityID, ["position", "rotation"]); - var index = MyAvatar.getJointIndex("Hips"); - MyAvatar.pinJoint(index, properties.position, properties.rotation); + var that = this; + Script.setTimeout(function() { + var properties = Entities.getEntityProperties(that.entityID, ["position", "rotation"]); + var index = MyAvatar.getJointIndex("Hips"); + MyAvatar.pinJoint(index, properties.position, properties.rotation); - this.animStateHandlerID = MyAvatar.addAnimationStateHandler(function(properties) { - return { headType: 0 }; - }, ["headType"]); - Script.update.connect(this, this.update); - for (var i in OVERRIDEN_DRIVE_KEYS) { - MyAvatar.disableDriveKey(OVERRIDEN_DRIVE_KEYS[i]); - } + that.animStateHandlerID = MyAvatar.addAnimationStateHandler(function(properties) { + return { headType: 0 }; + }, ["headType"]); + Script.update.connect(that, that.update); + Controller.keyPressEvent.connect(that, that.keyPressed); + Controller.keyReleaseEvent.connect(that, that.keyReleased); + for (var i in RELEASE_KEYS) { + Controller.captureKeyEvents({ text: RELEASE_KEYS[i] }); + } + }, SIT_DELAY); } - this.standUp = function() { - print("Standing up (" + this.entityID + ")"); - MyAvatar.removeAnimationStateHandler(this.animStateHandlerID); - Script.update.disconnect(this, this.update); - for (var i in OVERRIDEN_DRIVE_KEYS) { - MyAvatar.enableDriveKey(OVERRIDEN_DRIVE_KEYS[i]); - } - + this.sitUp = function() { this.setSeatUser(null); + if (Settings.getValue(SETTING_KEY) === this.entityID) { - Settings.setValue(SETTING_KEY, ""); - var ROLES = MyAvatar.getAnimationRoles(); - for (i in ROLES) { - MyAvatar.restoreRoleAnimation(ROLES[i]); - } + MyAvatar.restoreRoleAnimation(ROLE); MyAvatar.characterControllerEnabled = true; MyAvatar.hmdLeanRecenterEnabled = true; @@ -164,10 +124,19 @@ MyAvatar.bodyPitch = 0.0; MyAvatar.bodyRoll = 0.0; }, SIT_DELAY); + + Settings.setValue(SETTING_KEY, ""); + } + + MyAvatar.removeAnimationStateHandler(this.animStateHandlerID); + Script.update.disconnect(this, this.update); + Controller.keyPressEvent.disconnect(this, this.keyPressed); + Controller.keyReleaseEvent.disconnect(this, this.keyReleased); + for (var i in RELEASE_KEYS) { + Controller.releaseKeyEvents({ text: RELEASE_KEYS[i] }); } } - // function called by teleport.js if it detects the appropriate userData this.sit = function () { this.sitDown(); } @@ -214,52 +183,39 @@ } } + this.update = function(dt) { if (MyAvatar.sessionUUID === this.getSeatUser()) { - var properties = Entities.getEntityProperties(this.entityID); + var properties = Entities.getEntityProperties(this.entityID, ["position"]); var avatarDistance = Vec3.distance(MyAvatar.position, properties.position); var ikError = MyAvatar.getIKErrorOnLastSolve(); - var now = Date.now(); - var shouldStandUp = false; - - // Check if a drive key is pressed - var hasActiveDriveKey = false; - for (var i in OVERRIDEN_DRIVE_KEYS) { - if (MyAvatar.getRawDriveKey(OVERRIDEN_DRIVE_KEYS[i]) != 0.0) { - hasActiveDriveKey = true; - break; - } - } - - // Only standup if user has been pushing a drive key for RELEASE_TIME - if (hasActiveDriveKey) { - var elapsed = now - this.lastTimeNoDriveKeys; - shouldStandUp = elapsed > RELEASE_TIME; - } else { - this.lastTimeNoDriveKeys = Date.now(); - } - - // Allow some time for the IK to settle - if (ikError > MAX_IK_ERROR && now > this.sitDownSettlePeriod) { - shouldStandUp = true; - } - - - if (shouldStandUp || avatarDistance > RELEASE_DISTANCE) { + if (avatarDistance > RELEASE_DISTANCE || ikError > MAX_IK_ERROR) { print("IK error: " + ikError + ", distance from chair: " + avatarDistance); - - // Move avatar in front of the chair to avoid getting stuck in collision hulls - if (avatarDistance < MAX_RESET_DISTANCE) { - var offset = { x: 0, y: 1.0, z: -0.5 - properties.dimensions.z * properties.registrationPoint.z }; - var position = Vec3.sum(properties.position, Vec3.multiplyQbyV(properties.rotation, offset)); - MyAvatar.position = position; - print("Moving Avatar in front of the chair.") - } - - this.standUp(); + this.sitUp(this.entityID); } } } + this.keyPressed = function(event) { + if (isInEditMode()) { + return; + } + + if (RELEASE_KEYS.indexOf(event.text) !== -1) { + var that = this; + this.timers[event.text] = Script.setTimeout(function() { + that.sitUp(); + }, RELEASE_TIME); + } + } + this.keyReleased = function(event) { + if (RELEASE_KEYS.indexOf(event.text) !== -1) { + if (this.timers[event.text]) { + Script.clearTimeout(this.timers[event.text]); + delete this.timers[event.text]; + } + } + } + this.canSitDesktop = function() { var properties = Entities.getEntityProperties(this.entityID, ["position"]); var distanceFromSeat = Vec3.distance(MyAvatar.position, properties.position); @@ -267,7 +223,7 @@ } this.hoverEnterEntity = function(event) { - if (isInEditMode() || this.interval !== null) { + if (isInEditMode() || (MyAvatar.sessionUUID === this.getSeatUser())) { return; } @@ -283,18 +239,18 @@ }, DESKTOP_UI_CHECK_INTERVAL); } this.hoverLeaveEntity = function(event) { - if (this.interval !== null) { + if (this.interval) { Script.clearInterval(this.interval); this.interval = null; } this.cleanupOverlay(); } - this.clickDownOnEntity = function (id, event) { - if (isInEditMode()) { + this.clickDownOnEntity = function () { + if (isInEditMode() || (MyAvatar.sessionUUID === this.getSeatUser())) { return; } - if (event.isPrimaryButton && this.canSitDesktop()) { + if (this.canSitDesktop()) { this.sitDown(); } } diff --git a/scripts/tutorials/makeBlocks.js b/scripts/tutorials/makeBlocks.js index 432f7444c4..54bdead792 100644 --- a/scripts/tutorials/makeBlocks.js +++ b/scripts/tutorials/makeBlocks.js @@ -34,12 +34,12 @@ var SCRIPT_URL = Script.resolvePath("./entity_scripts/magneticBlock.js"); - var forwardVector = Quat.getForward(MyAvatar.orientation); - forwardVector.y += VERTICAL_OFFSET; + var frontVector = Quat.getFront(MyAvatar.orientation); + frontVector.y += VERTICAL_OFFSET; for (var x = 0; x < COLUMNS; x++) { for (var y = 0; y < ROWS; y++) { - var forwardOffset = { + var frontOffset = { x: 0, y: SIZE * y + SIZE, z: SIZE * x + SIZE @@ -61,7 +61,7 @@ cloneLimit: 9999 } }), - position: Vec3.sum(MyAvatar.position, Vec3.sum(forwardOffset, forwardVector)), + position: Vec3.sum(MyAvatar.position, Vec3.sum(frontOffset, frontVector)), color: newColor(), script: SCRIPT_URL }); diff --git a/tests/ktx/CMakeLists.txt b/tests/ktx/CMakeLists.txt deleted file mode 100644 index d72379efd6..0000000000 --- a/tests/ktx/CMakeLists.txt +++ /dev/null @@ -1,15 +0,0 @@ - -set(TARGET_NAME ktx-test) - -if (WIN32) - SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /ignore:4049 /ignore:4217") -endif() - -# This is not a testcase -- just set it up as a regular hifi project -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) - -package_libraries_for_deployment() diff --git a/tests/ktx/src/main.cpp b/tests/ktx/src/main.cpp deleted file mode 100644 index aa6795e17b..0000000000 --- a/tests/ktx/src/main.cpp +++ /dev/null @@ -1,150 +0,0 @@ -// -// Created by Bradley Austin Davis on 2016/07/01 -// Copyright 2014 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#include -#include -#include -#include - -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - - -#include -#include -#include -#include - - -QSharedPointer logger; - -gpu::Texture* cacheTexture(const std::string& name, gpu::Texture* srcTexture, bool write = true, bool read = true); - - -void messageHandler(QtMsgType type, const QMessageLogContext& context, const QString& message) { - QString logMessage = LogHandler::getInstance().printMessage((LogMsgType)type, context, message); - - if (!logMessage.isEmpty()) { -#ifdef Q_OS_WIN - OutputDebugStringA(logMessage.toLocal8Bit().constData()); - OutputDebugStringA("\n"); -#endif - logger->addMessage(qPrintable(logMessage + "\n")); - } -} - -const char * LOG_FILTER_RULES = R"V0G0N( -hifi.gpu=true -)V0G0N"; - -QString getRootPath() { - static std::once_flag once; - static QString result; - std::call_once(once, [&] { - QFileInfo file(__FILE__); - QDir parent = file.absolutePath(); - result = QDir::cleanPath(parent.currentPath() + "/../../.."); - }); - return result; -} - -const QString TEST_IMAGE = getRootPath() + "/scripts/developer/tests/cube_texture.png"; -const QString TEST_IMAGE_KTX = getRootPath() + "/scripts/developer/tests/cube_texture.ktx"; - -int main(int argc, char** argv) { - QApplication app(argc, argv); - QCoreApplication::setApplicationName("KTX"); - QCoreApplication::setOrganizationName("High Fidelity"); - QCoreApplication::setOrganizationDomain("highfidelity.com"); - logger.reset(new FileLogger()); - - Q_ASSERT(sizeof(ktx::Header) == 12 + (sizeof(uint32_t) * 13)); - - DependencyManager::set(); - qInstallMessageHandler(messageHandler); - QLoggingCategory::setFilterRules(LOG_FILTER_RULES); - - QImage image(TEST_IMAGE); - gpu::Texture* testTexture = model::TextureUsage::process2DTextureColorFromImage(image, TEST_IMAGE.toStdString(), true, false, true); - - auto ktxMemory = gpu::Texture::serialize(*testTexture); - { - const auto& ktxStorage = ktxMemory->getStorage(); - QFile outFile(TEST_IMAGE_KTX); - if (!outFile.open(QFile::Truncate | QFile::ReadWrite)) { - throw std::runtime_error("Unable to open file"); - } - auto ktxSize = ktxStorage->size(); - outFile.resize(ktxSize); - auto dest = outFile.map(0, ktxSize); - memcpy(dest, ktxStorage->data(), ktxSize); - outFile.unmap(dest); - outFile.close(); - } - - auto ktxFile = ktx::KTX::create(std::shared_ptr(new storage::FileStorage(TEST_IMAGE_KTX))); - { - const auto& memStorage = ktxMemory->getStorage(); - const auto& fileStorage = ktxFile->getStorage(); - Q_ASSERT(memStorage->size() == fileStorage->size()); - Q_ASSERT(memStorage->data() != fileStorage->data()); - Q_ASSERT(0 == memcmp(memStorage->data(), fileStorage->data(), memStorage->size())); - Q_ASSERT(ktxFile->_images.size() == ktxMemory->_images.size()); - auto imageCount = ktxFile->_images.size(); - auto startMemory = ktxMemory->_storage->data(); - auto startFile = ktxFile->_storage->data(); - for (size_t i = 0; i < imageCount; ++i) { - auto memImages = ktxMemory->_images[i]; - auto fileImages = ktxFile->_images[i]; - Q_ASSERT(memImages._padding == fileImages._padding); - Q_ASSERT(memImages._numFaces == fileImages._numFaces); - Q_ASSERT(memImages._imageSize == fileImages._imageSize); - Q_ASSERT(memImages._faceSize == fileImages._faceSize); - Q_ASSERT(memImages._faceBytes.size() == memImages._numFaces); - Q_ASSERT(fileImages._faceBytes.size() == fileImages._numFaces); - auto faceCount = fileImages._numFaces; - for (uint32_t face = 0; face < faceCount; ++face) { - auto memFace = memImages._faceBytes[face]; - auto memOffset = memFace - startMemory; - auto fileFace = fileImages._faceBytes[face]; - auto fileOffset = fileFace - startFile; - Q_ASSERT(memOffset % 4 == 0); - Q_ASSERT(memOffset == fileOffset); - } - } - } - testTexture->setKtxBacking(ktxFile); - return 0; -} - -#include "main.moc" - diff --git a/tests/render-perf/CMakeLists.txt b/tests/render-perf/CMakeLists.txt index 96cede9c43..d4f90fdace 100644 --- a/tests/render-perf/CMakeLists.txt +++ b/tests/render-perf/CMakeLists.txt @@ -10,7 +10,7 @@ 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) +link_hifi_libraries(shared octree gl gpu gpu-gl render model model-networking networking render-utils fbx entities entities-renderer animation audio avatars script-engine physics) package_libraries_for_deployment() diff --git a/tests/render-perf/src/Camera.hpp b/tests/render-perf/src/Camera.hpp index ada1277c47..a3b33ceb14 100644 --- a/tests/render-perf/src/Camera.hpp +++ b/tests/render-perf/src/Camera.hpp @@ -123,16 +123,16 @@ public: void update(float deltaTime) { if (moving()) { - glm::vec3 camForward = getOrientation() * Vectors::FRONT; + glm::vec3 camFront = getOrientation() * Vectors::FRONT; glm::vec3 camRight = getOrientation() * Vectors::RIGHT; glm::vec3 camUp = getOrientation() * Vectors::UP; float moveSpeed = deltaTime * movementSpeed; if (keys[FORWARD]) { - position += camForward * moveSpeed; + position += camFront * moveSpeed; } if (keys[BACK]) { - position -= camForward * moveSpeed; + position -= camFront * moveSpeed; } if (keys[LEFT]) { position -= camRight * moveSpeed; diff --git a/tests/render-perf/src/main.cpp b/tests/render-perf/src/main.cpp index 522fe79b10..7e9d2c426f 100644 --- a/tests/render-perf/src/main.cpp +++ b/tests/render-perf/src/main.cpp @@ -642,6 +642,7 @@ protected: gpu::Texture::setAllowedGPUMemoryUsage(MB_TO_BYTES(64)); return; + default: break; } diff --git a/tests/render-texture-load/src/main.cpp b/tests/render-texture-load/src/main.cpp index d924f76232..09a420f018 100644 --- a/tests/render-texture-load/src/main.cpp +++ b/tests/render-texture-load/src/main.cpp @@ -48,7 +48,6 @@ #include #include -#include #include #include #include diff --git a/tests/shared/src/StorageTests.cpp b/tests/shared/src/StorageTests.cpp deleted file mode 100644 index fa538f6911..0000000000 --- a/tests/shared/src/StorageTests.cpp +++ /dev/null @@ -1,75 +0,0 @@ -// -// Created by Bradley Austin Davis on 2016/02/17 -// 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 -// - -#include "StorageTests.h" - -QTEST_MAIN(StorageTests) - -using namespace storage; - -StorageTests::StorageTests() { - for (size_t i = 0; i < _testData.size(); ++i) { - _testData[i] = (uint8_t)rand(); - } - _testFile = QDir::tempPath() + "/" + QUuid::createUuid().toString(); -} - -StorageTests::~StorageTests() { - QFileInfo fileInfo(_testFile); - if (fileInfo.exists()) { - QFile(_testFile).remove(); - } -} - - -void StorageTests::testConversion() { - { - QFileInfo fileInfo(_testFile); - QCOMPARE(fileInfo.exists(), false); - } - StoragePointer storagePointer = std::make_unique(_testData.size(), _testData.data()); - QCOMPARE(storagePointer->size(), (quint64)_testData.size()); - QCOMPARE(memcmp(_testData.data(), storagePointer->data(), _testData.size()), 0); - // Convert to a file - storagePointer = storagePointer->toFileStorage(_testFile); - { - QFileInfo fileInfo(_testFile); - QCOMPARE(fileInfo.exists(), true); - QCOMPARE(fileInfo.size(), (qint64)_testData.size()); - } - QCOMPARE(storagePointer->size(), (quint64)_testData.size()); - QCOMPARE(memcmp(_testData.data(), storagePointer->data(), _testData.size()), 0); - - // Convert to memory - storagePointer = storagePointer->toMemoryStorage(); - QCOMPARE(storagePointer->size(), (quint64)_testData.size()); - QCOMPARE(memcmp(_testData.data(), storagePointer->data(), _testData.size()), 0); - { - // ensure the file is unaffected - QFileInfo fileInfo(_testFile); - QCOMPARE(fileInfo.exists(), true); - QCOMPARE(fileInfo.size(), (qint64)_testData.size()); - } - - // truncate the data as a new memory object - auto newSize = _testData.size() / 2; - storagePointer = std::make_unique(newSize, storagePointer->data()); - QCOMPARE(storagePointer->size(), (quint64)newSize); - QCOMPARE(memcmp(_testData.data(), storagePointer->data(), newSize), 0); - - // Convert back to file - storagePointer = storagePointer->toFileStorage(_testFile); - QCOMPARE(storagePointer->size(), (quint64)newSize); - QCOMPARE(memcmp(_testData.data(), storagePointer->data(), newSize), 0); - { - // ensure the file is truncated - QFileInfo fileInfo(_testFile); - QCOMPARE(fileInfo.exists(), true); - QCOMPARE(fileInfo.size(), (qint64)newSize); - } -} diff --git a/tests/shared/src/StorageTests.h b/tests/shared/src/StorageTests.h deleted file mode 100644 index 6a2c153223..0000000000 --- a/tests/shared/src/StorageTests.h +++ /dev/null @@ -1,32 +0,0 @@ -// -// Created by Bradley Austin Davis on 2016/02/17 -// 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 -// - -#ifndef hifi_StorageTests_h -#define hifi_StorageTests_h - -#include - -#include -#include - -class StorageTests : public QObject { - Q_OBJECT - -public: - StorageTests(); - ~StorageTests(); - -private slots: - void testConversion(); - -private: - std::array _testData; - QString _testFile; -}; - -#endif // hifi_StorageTests_h diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt index 8dc993e6fe..a85a112bf5 100644 --- a/tools/CMakeLists.txt +++ b/tools/CMakeLists.txt @@ -17,5 +17,3 @@ set_target_properties(ac-client PROPERTIES FOLDER "Tools") add_subdirectory(skeleton-dump) set_target_properties(skeleton-dump PROPERTIES FOLDER "Tools") -add_subdirectory(atp-get) -set_target_properties(atp-get PROPERTIES FOLDER "Tools") diff --git a/tools/atp-get/CMakeLists.txt b/tools/atp-get/CMakeLists.txt deleted file mode 100644 index b1646dc023..0000000000 --- a/tools/atp-get/CMakeLists.txt +++ /dev/null @@ -1,3 +0,0 @@ -set(TARGET_NAME atp-get) -setup_hifi_project(Core Widgets) -link_hifi_libraries(shared networking) diff --git a/tools/atp-get/src/ATPGetApp.cpp b/tools/atp-get/src/ATPGetApp.cpp deleted file mode 100644 index 30054fffea..0000000000 --- a/tools/atp-get/src/ATPGetApp.cpp +++ /dev/null @@ -1,269 +0,0 @@ -// -// ATPGetApp.cpp -// tools/atp-get/src -// -// Created by Seth Alves on 2017-3-15 -// Copyright 2017 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "ATPGetApp.h" - -ATPGetApp::ATPGetApp(int argc, char* argv[]) : - QCoreApplication(argc, argv) -{ - // parse command-line - QCommandLineParser parser; - parser.setApplicationDescription("High Fidelity ATP-Get"); - - const QCommandLineOption helpOption = parser.addHelpOption(); - - const QCommandLineOption verboseOutput("v", "verbose output"); - parser.addOption(verboseOutput); - - const QCommandLineOption domainAddressOption("d", "domain-server address", "127.0.0.1"); - parser.addOption(domainAddressOption); - - const QCommandLineOption cacheSTUNOption("s", "cache stun-server response"); - parser.addOption(cacheSTUNOption); - - const QCommandLineOption listenPortOption("listenPort", "listen port", QString::number(INVALID_PORT)); - parser.addOption(listenPortOption); - - - if (!parser.parse(QCoreApplication::arguments())) { - qCritical() << parser.errorText() << endl; - parser.showHelp(); - Q_UNREACHABLE(); - } - - if (parser.isSet(helpOption)) { - parser.showHelp(); - Q_UNREACHABLE(); - } - - _verbose = parser.isSet(verboseOutput); - if (!_verbose) { - QLoggingCategory::setFilterRules("qt.network.ssl.warning=false"); - - const_cast(&networking())->setEnabled(QtDebugMsg, false); - const_cast(&networking())->setEnabled(QtInfoMsg, false); - const_cast(&networking())->setEnabled(QtWarningMsg, false); - - const_cast(&shared())->setEnabled(QtDebugMsg, false); - const_cast(&shared())->setEnabled(QtInfoMsg, false); - const_cast(&shared())->setEnabled(QtWarningMsg, false); - } - - - QStringList filenames = parser.positionalArguments(); - if (filenames.empty() || filenames.size() > 2) { - qDebug() << "give remote url and optional local filename as arguments"; - parser.showHelp(); - Q_UNREACHABLE(); - } - - _url = QUrl(filenames[0]); - if (_url.scheme() != "atp") { - qDebug() << "url should start with atp:"; - parser.showHelp(); - Q_UNREACHABLE(); - } - - if (filenames.size() == 2) { - _localOutputFile = filenames[1]; - } - - QString domainServerAddress = "127.0.0.1:40103"; - if (parser.isSet(domainAddressOption)) { - domainServerAddress = parser.value(domainAddressOption); - } - - if (_verbose) { - qDebug() << "domain-server address is" << domainServerAddress; - } - - int listenPort = INVALID_PORT; - if (parser.isSet(listenPortOption)) { - listenPort = parser.value(listenPortOption).toInt(); - } - - Setting::init(); - DependencyManager::registerInheritance(); - - DependencyManager::set([&]{ return QString("Mozilla/5.0 (HighFidelityATPGet)"); }); - DependencyManager::set(); - DependencyManager::set(NodeType::Agent, listenPort); - - - auto nodeList = DependencyManager::get(); - - // start the nodeThread so its event loop is running - QThread* nodeThread = new QThread(this); - nodeThread->setObjectName("NodeList Thread"); - nodeThread->start(); - - // make sure the node thread is given highest priority - nodeThread->setPriority(QThread::TimeCriticalPriority); - - // setup a timer for domain-server check ins - QTimer* domainCheckInTimer = new QTimer(nodeList.data()); - connect(domainCheckInTimer, &QTimer::timeout, nodeList.data(), &NodeList::sendDomainServerCheckIn); - domainCheckInTimer->start(DOMAIN_SERVER_CHECK_IN_MSECS); - - // put the NodeList and datagram processing on the node thread - nodeList->moveToThread(nodeThread); - - const DomainHandler& domainHandler = nodeList->getDomainHandler(); - - connect(&domainHandler, SIGNAL(hostnameChanged(const QString&)), SLOT(domainChanged(const QString&))); - // connect(&domainHandler, SIGNAL(resetting()), SLOT(resettingDomain())); - // connect(&domainHandler, SIGNAL(disconnectedFromDomain()), SLOT(clearDomainOctreeDetails())); - connect(&domainHandler, &DomainHandler::domainConnectionRefused, this, &ATPGetApp::domainConnectionRefused); - - connect(nodeList.data(), &NodeList::nodeAdded, this, &ATPGetApp::nodeAdded); - connect(nodeList.data(), &NodeList::nodeKilled, this, &ATPGetApp::nodeKilled); - connect(nodeList.data(), &NodeList::nodeActivated, this, &ATPGetApp::nodeActivated); - // connect(nodeList.data(), &NodeList::uuidChanged, getMyAvatar(), &MyAvatar::setSessionUUID); - // connect(nodeList.data(), &NodeList::uuidChanged, this, &ATPGetApp::setSessionUUID); - connect(nodeList.data(), &NodeList::packetVersionMismatch, this, &ATPGetApp::notifyPacketVersionMismatch); - - nodeList->addSetOfNodeTypesToNodeInterestSet(NodeSet() << NodeType::AudioMixer << NodeType::AvatarMixer - << NodeType::EntityServer << NodeType::AssetServer << NodeType::MessagesMixer); - - DependencyManager::get()->handleLookupString(domainServerAddress, false); - - auto assetClient = DependencyManager::set(); - assetClient->init(); - - QTimer* doTimer = new QTimer(this); - doTimer->setSingleShot(true); - connect(doTimer, &QTimer::timeout, this, &ATPGetApp::timedOut); - doTimer->start(4000); -} - -ATPGetApp::~ATPGetApp() { -} - - -void ATPGetApp::domainConnectionRefused(const QString& reasonMessage, int reasonCodeInt, const QString& extraInfo) { - qDebug() << "domainConnectionRefused"; -} - -void ATPGetApp::domainChanged(const QString& domainHostname) { - if (_verbose) { - qDebug() << "domainChanged"; - } -} - -void ATPGetApp::nodeAdded(SharedNodePointer node) { - if (_verbose) { - qDebug() << "node added: " << node->getType(); - } -} - -void ATPGetApp::nodeActivated(SharedNodePointer node) { - if (node->getType() == NodeType::AssetServer) { - lookup(); - } -} - -void ATPGetApp::nodeKilled(SharedNodePointer node) { - qDebug() << "nodeKilled"; -} - -void ATPGetApp::timedOut() { - finish(1); -} - -void ATPGetApp::notifyPacketVersionMismatch() { - if (_verbose) { - qDebug() << "packet version mismatch"; - } - finish(1); -} - -void ATPGetApp::lookup() { - - auto path = _url.path(); - qDebug() << "path is " << path; - - auto request = DependencyManager::get()->createGetMappingRequest(path); - QObject::connect(request, &GetMappingRequest::finished, this, [=](GetMappingRequest* request) mutable { - auto result = request->getError(); - if (result == GetMappingRequest::NotFound) { - qDebug() << "not found"; - } else if (result == GetMappingRequest::NoError) { - qDebug() << "found, hash is " << request->getHash(); - download(request->getHash()); - } else { - qDebug() << "error -- " << request->getError() << " -- " << request->getErrorString(); - } - request->deleteLater(); - }); - request->start(); -} - -void ATPGetApp::download(AssetHash hash) { - auto assetClient = DependencyManager::get(); - auto assetRequest = new AssetRequest(hash); - - connect(assetRequest, &AssetRequest::finished, this, [this](AssetRequest* request) mutable { - Q_ASSERT(request->getState() == AssetRequest::Finished); - - if (request->getError() == AssetRequest::Error::NoError) { - QString data = QString::fromUtf8(request->getData()); - if (_localOutputFile == "") { - QTextStream cout(stdout); - cout << data; - } else { - QFile outputHandle(_localOutputFile); - if (outputHandle.open(QIODevice::ReadWrite)) { - QTextStream stream( &outputHandle ); - stream << data; - } else { - qDebug() << "couldn't open output file:" << _localOutputFile; - } - } - } - - request->deleteLater(); - finish(0); - }); - - assetRequest->start(); -} - -void ATPGetApp::finish(int exitCode) { - auto nodeList = DependencyManager::get(); - - // send the domain a disconnect packet, force stoppage of domain-server check-ins - nodeList->getDomainHandler().disconnect(); - nodeList->setIsShuttingDown(true); - - // tell the packet receiver we're shutting down, so it can drop packets - nodeList->getPacketReceiver().setShouldDropPackets(true); - - QThread* nodeThread = DependencyManager::get()->thread(); - // remove the NodeList from the DependencyManager - DependencyManager::destroy(); - // ask the node thread to quit and wait until it is done - nodeThread->quit(); - nodeThread->wait(); - - QCoreApplication::exit(exitCode); -} diff --git a/tools/atp-get/src/ATPGetApp.h b/tools/atp-get/src/ATPGetApp.h deleted file mode 100644 index 5507d2aa62..0000000000 --- a/tools/atp-get/src/ATPGetApp.h +++ /dev/null @@ -1,52 +0,0 @@ -// -// ATPGetApp.h -// tools/atp-get/src -// -// Created by Seth Alves on 2017-3-15 -// Copyright 2017 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - - -#ifndef hifi_ATPGetApp_h -#define hifi_ATPGetApp_h - -#include -#include -#include -#include -#include -#include -#include -#include - - -class ATPGetApp : public QCoreApplication { - Q_OBJECT -public: - ATPGetApp(int argc, char* argv[]); - ~ATPGetApp(); - -private slots: - void domainConnectionRefused(const QString& reasonMessage, int reasonCodeInt, const QString& extraInfo); - void domainChanged(const QString& domainHostname); - void nodeAdded(SharedNodePointer node); - void nodeActivated(SharedNodePointer node); - void nodeKilled(SharedNodePointer node); - void notifyPacketVersionMismatch(); - -private: - NodeList* _nodeList; - void timedOut(); - void lookup(); - void download(AssetHash hash); - void finish(int exitCode); - bool _verbose; - - QUrl _url; - QString _localOutputFile; -}; - -#endif // hifi_ATPGetApp_h diff --git a/tools/atp-get/src/main.cpp b/tools/atp-get/src/main.cpp deleted file mode 100644 index bddf30c666..0000000000 --- a/tools/atp-get/src/main.cpp +++ /dev/null @@ -1,31 +0,0 @@ -// -// main.cpp -// tools/atp-get/src -// -// Created by Seth Alves on 2017-3-15 -// Copyright 2017 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html - -#include -#include -#include -#include - -#include - -#include "ATPGetApp.h" - -using namespace std; - -int main(int argc, char * argv[]) { - QCoreApplication::setApplicationName(BuildInfo::AC_CLIENT_SERVER_NAME); - QCoreApplication::setOrganizationName(BuildInfo::MODIFIED_ORGANIZATION); - QCoreApplication::setOrganizationDomain(BuildInfo::ORGANIZATION_DOMAIN); - QCoreApplication::setApplicationVersion(BuildInfo::VERSION); - - ATPGetApp app(argc, argv); - - return app.exec(); -} diff --git a/unpublishedScripts/marketplace/boppo/boppoClownEntity.js b/unpublishedScripts/marketplace/boppo/boppoClownEntity.js deleted file mode 100644 index 36f2bf5ab0..0000000000 --- a/unpublishedScripts/marketplace/boppo/boppoClownEntity.js +++ /dev/null @@ -1,80 +0,0 @@ -// -// boppoClownEntity.js -// -// Created by Thijs Wenker on 3/15/17. -// 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 -// - -/* globals LookAtTarget */ - -(function() { - var SFX_PREFIX = 'https://hifi-content.s3-us-west-1.amazonaws.com/caitlyn/production/elBoppo/sfx/'; - var CHANNEL_PREFIX = 'io.highfidelity.boppo_server_'; - var PUNCH_SOUNDS = [ - 'punch_1.wav', - 'punch_2.wav' - ]; - var PUNCH_COOLDOWN = 300; - - Script.include('lookAtEntity.js'); - - var createBoppoClownEntity = function() { - var _this, - _entityID, - _boppoUserData, - _lookAtTarget, - _punchSounds = [], - _lastPlayedPunch = {}; - - var getOwnBoppoUserData = function() { - try { - return JSON.parse(Entities.getEntityProperties(_entityID, ['userData']).userData).Boppo; - } catch (e) { - // e - } - return {}; - }; - - var BoppoClownEntity = function () { - _this = this; - PUNCH_SOUNDS.forEach(function(punch) { - _punchSounds.push(SoundCache.getSound(SFX_PREFIX + punch)); - }); - }; - - BoppoClownEntity.prototype = { - preload: function(entityID) { - _entityID = entityID; - _boppoUserData = getOwnBoppoUserData(); - _lookAtTarget = new LookAtTarget(_entityID); - }, - collisionWithEntity: function(boppoEntity, collidingEntity, collisionInfo) { - if (collisionInfo.type === 0 && - Entities.getEntityProperties(collidingEntity, ['name']).name.indexOf('Boxing Glove ') === 0) { - - if (_lastPlayedPunch[collidingEntity] === undefined || - Date.now() - _lastPlayedPunch[collidingEntity] > PUNCH_COOLDOWN) { - - // If boxing glove detected here: - Messages.sendMessage(CHANNEL_PREFIX + _boppoUserData.gameParentID, 'hit'); - - _lookAtTarget.lookAtByAction(); - var randomPunchIndex = Math.floor(Math.random() * _punchSounds.length); - Audio.playSound(_punchSounds[randomPunchIndex], { - position: collisionInfo.contactPoint - }); - _lastPlayedPunch[collidingEntity] = Date.now(); - } - } - } - - }; - - return new BoppoClownEntity(); - }; - - return createBoppoClownEntity(); -}); diff --git a/unpublishedScripts/marketplace/boppo/boppoServer.js b/unpublishedScripts/marketplace/boppo/boppoServer.js deleted file mode 100644 index f03154573c..0000000000 --- a/unpublishedScripts/marketplace/boppo/boppoServer.js +++ /dev/null @@ -1,303 +0,0 @@ -// -// boppoServer.js -// -// Created by Thijs Wenker on 3/15/17. -// 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 SFX_PREFIX = 'https://hifi-content.s3-us-west-1.amazonaws.com/caitlyn/production/elBoppo/sfx/'; - var CLOWN_LAUGHS = [ - 'clown_laugh_1.wav', - 'clown_laugh_2.wav', - 'clown_laugh_3.wav', - 'clown_laugh_4.wav' - ]; - var TICK_TOCK_SOUND = 'ticktock%20-%20tock.wav'; - var BOXING_RING_BELL_START = 'boxingRingBell.wav'; - var BOXING_RING_BELL_END = 'boxingRingBell-end.wav'; - var BOPPO_MUSIC = 'boppoMusic.wav'; - var CHANNEL_PREFIX = 'io.highfidelity.boppo_server_'; - var MESSAGE_HIT = 'hit'; - var MESSAGE_ENTER_ZONE = 'enter-zone'; - var MESSAGE_UNLOAD_FIX = 'unload-fix'; - - var DEFAULT_SOUND_VOLUME = 0.6; - - // don't set the search radius too high, it might remove boppo's from other nearby instances - var BOPPO_SEARCH_RADIUS = 4.0; - - var MILLISECONDS_PER_SECOND = 1000; - // Make sure the entities are loaded at startup (TODO: more solid fix) - var LOAD_TIMEOUT = 5000; - var SECONDS_PER_MINUTE = 60; - var DEFAULT_PLAYTIME = 30; // seconds - var BASE_TEN = 10; - var TICK_TOCK_FROM = 3; // seconds - var COOLDOWN_TIME_MS = MILLISECONDS_PER_SECOND * 3; - - var createBoppoServer = function() { - var _this, - _isInitialized = false, - _clownLaughs = [], - _musicInjector, - _music, - _laughingInjector, - _tickTockSound, - _boxingBellRingStart, - _boxingBellRingEnd, - _entityID, - _boppoClownID, - _channel, - _boppoEntities, - _isGameRunning, - _updateInterval, - _timeLeft, - _hits, - _coolDown; - - var getOwnBoppoUserData = function() { - try { - return JSON.parse(Entities.getEntityProperties(_entityID, ['userData']).userData).Boppo; - } catch (e) { - // e - } - return {}; - }; - - var updateBoppoEntities = function() { - Entities.getChildrenIDs(_entityID).forEach(function(entityID) { - try { - var userData = JSON.parse(Entities.getEntityProperties(entityID, ['userData']).userData); - if (userData.Boppo.type !== undefined) { - _boppoEntities[userData.Boppo.type] = entityID; - } - } catch (e) { - // e - } - }); - }; - - var clearUntrackedBoppos = function() { - var position = Entities.getEntityProperties(_entityID, ['position']).position; - Entities.findEntities(position, BOPPO_SEARCH_RADIUS).forEach(function(entityID) { - try { - if (JSON.parse(Entities.getEntityProperties(entityID, ['userData']).userData).Boppo.type === 'boppo') { - Entities.deleteEntity(entityID); - } - } catch (e) { - // e - } - }); - }; - - var updateTimerDisplay = function() { - if (_boppoEntities['timer']) { - var secondsString = _timeLeft % SECONDS_PER_MINUTE; - if (secondsString < BASE_TEN) { - secondsString = '0' + secondsString; - } - var minutesString = Math.floor(_timeLeft / SECONDS_PER_MINUTE); - Entities.editEntity(_boppoEntities['timer'], { - text: minutesString + ':' + secondsString - }); - } - }; - - var updateScoreDisplay = function() { - if (_boppoEntities['score']) { - Entities.editEntity(_boppoEntities['score'], { - text: 'SCORE: ' + _hits - }); - } - }; - - var playSoundAtBoxingRing = function(sound, properties) { - var _properties = properties ? properties : {}; - if (_properties['volume'] === undefined) { - _properties['volume'] = DEFAULT_SOUND_VOLUME; - } - _properties['position'] = Entities.getEntityProperties(_entityID, ['position']).position; - // play beep - return Audio.playSound(sound, _properties); - }; - - var onUpdate = function() { - _timeLeft--; - - if (_timeLeft > 0 && _timeLeft <= TICK_TOCK_FROM) { - // play beep - playSoundAtBoxingRing(_tickTockSound); - } - if (_timeLeft === 0) { - if (_musicInjector !== undefined && _musicInjector.isPlaying()) { - _musicInjector.stop(); - _musicInjector = undefined; - } - playSoundAtBoxingRing(_boxingBellRingEnd); - _isGameRunning = false; - Script.clearInterval(_updateInterval); - _updateInterval = null; - _coolDown = true; - Script.setTimeout(function() { - _coolDown = false; - _this.resetBoppo(); - }, COOLDOWN_TIME_MS); - } - updateTimerDisplay(); - }; - - var onMessage = function(channel, message, sender) { - if (channel === _channel) { - if (message === MESSAGE_HIT) { - _this.hit(); - } else if (message === MESSAGE_ENTER_ZONE && !_isGameRunning) { - _this.resetBoppo(); - } else if (message === MESSAGE_UNLOAD_FIX && _isInitialized) { - _this.unload(); - } - } - }; - - var BoppoServer = function () { - _this = this; - _hits = 0; - _boppoClownID = null; - _coolDown = false; - CLOWN_LAUGHS.forEach(function(clownLaugh) { - _clownLaughs.push(SoundCache.getSound(SFX_PREFIX + clownLaugh)); - }); - _tickTockSound = SoundCache.getSound(SFX_PREFIX + TICK_TOCK_SOUND); - _boxingBellRingStart = SoundCache.getSound(SFX_PREFIX + BOXING_RING_BELL_START); - _boxingBellRingEnd = SoundCache.getSound(SFX_PREFIX + BOXING_RING_BELL_END); - _music = SoundCache.getSound(SFX_PREFIX + BOPPO_MUSIC); - _boppoEntities = {}; - }; - - BoppoServer.prototype = { - preload: function(entityID) { - _entityID = entityID; - _channel = CHANNEL_PREFIX + entityID; - - Messages.sendLocalMessage(_channel, MESSAGE_UNLOAD_FIX); - Script.setTimeout(function() { - clearUntrackedBoppos(); - updateBoppoEntities(); - Messages.subscribe(_channel); - Messages.messageReceived.connect(onMessage); - _this.resetBoppo(); - _isInitialized = true; - }, LOAD_TIMEOUT); - }, - resetBoppo: function() { - if (_boppoClownID !== null) { - print('deleting boppo: ' + _boppoClownID); - Entities.deleteEntity(_boppoClownID); - } - var boppoBaseProperties = Entities.getEntityProperties(_entityID, ['position', 'rotation']); - _boppoClownID = Entities.addEntity({ - angularDamping: 0.0, - collisionSoundURL: 'https://hifi-content.s3.amazonaws.com/caitlyn/production/elBoppo/51460__andre-rocha-nascimento__basket-ball-01-bounce.wav', - collisionsWillMove: true, - compoundShapeURL: 'https://hifi-content.s3.amazonaws.com/caitlyn/production/elBoppo/bopo_phys.obj', - damping: 1.0, - density: 10000, - dimensions: { - x: 1.2668079137802124, - y: 2.0568051338195801, - z: 0.88563752174377441 - }, - dynamic: 1.0, - friction: 1.0, - gravity: { - x: 0, - y: -25, - z: 0 - }, - modelURL: 'https://hifi-content.s3.amazonaws.com/caitlyn/production/elBoppo/elBoppo3_VR.fbx', - name: 'El Boppo the Punching Bag Clown', - registrationPoint: { - x: 0.5, - y: 0, - z: 0.3 - }, - restitution: 0.99, - rotation: boppoBaseProperties.rotation, - position: Vec3.sum(boppoBaseProperties.position, - Vec3.multiplyQbyV(boppoBaseProperties.rotation, { - x: 0.08666179329156876, - y: -1.5698202848434448, - z: 0.1847127377986908 - })), - script: Script.resolvePath('boppoClownEntity.js'), - shapeType: 'compound', - type: 'Model', - userData: JSON.stringify({ - lookAt: { - targetID: _boppoEntities['lookAtThis'], - disablePitch: true, - disableYaw: false, - disableRoll: true, - clearDisabledAxis: true, - rotationOffset: { x: 0.0, y: 180.0, z: 0.0} - }, - Boppo: { - type: 'boppo', - gameParentID: _entityID - }, - grabbableKey: { - grabbable: false - } - }) - }); - updateBoppoEntities(); - _boppoEntities['boppo'] = _boppoClownID; - }, - laugh: function() { - if (_laughingInjector !== undefined && _laughingInjector.isPlaying()) { - return; - } - var randomLaughIndex = Math.floor(Math.random() * _clownLaughs.length); - _laughingInjector = Audio.playSound(_clownLaughs[randomLaughIndex], { - position: Entities.getEntityProperties(_boppoClownID, ['position']).position - }); - }, - hit: function() { - if (_coolDown) { - return; - } - if (!_isGameRunning) { - var boxingRingBoppoData = getOwnBoppoUserData(); - _updateInterval = Script.setInterval(onUpdate, MILLISECONDS_PER_SECOND); - _timeLeft = boxingRingBoppoData.playTimeSeconds ? parseInt(boxingRingBoppoData.playTimeSeconds) : - DEFAULT_PLAYTIME; - _isGameRunning = true; - _hits = 0; - playSoundAtBoxingRing(_boxingBellRingStart); - _musicInjector = playSoundAtBoxingRing(_music, {loop: true, volume: 0.6}); - } - _hits++; - updateTimerDisplay(); - updateScoreDisplay(); - _this.laugh(); - }, - unload: function() { - print('unload called'); - if (_updateInterval) { - Script.clearInterval(_updateInterval); - } - Messages.messageReceived.disconnect(onMessage); - Messages.unsubscribe(_channel); - Entities.deleteEntity(_boppoClownID); - print('endOfUnload'); - } - }; - - return new BoppoServer(); - }; - - return createBoppoServer(); -}); diff --git a/unpublishedScripts/marketplace/boppo/clownGloveDispenser.js b/unpublishedScripts/marketplace/boppo/clownGloveDispenser.js deleted file mode 100644 index cd0a0c0614..0000000000 --- a/unpublishedScripts/marketplace/boppo/clownGloveDispenser.js +++ /dev/null @@ -1,154 +0,0 @@ -// -// clownGloveDispenser.js -// -// Created by Thijs Wenker on 8/2/16. -// Copyright 2016 High Fidelity, Inc. -// -// Based on examples/winterSmashUp/targetPractice/shooterPlatform.js -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -(function() { - var _this = this; - - var CHANNEL_PREFIX = 'io.highfidelity.boppo_server_'; - - var leftBoxingGlove = undefined; - var rightBoxingGlove = undefined; - - var inZone = false; - - var wearGloves = function() { - leftBoxingGlove = Entities.addEntity({ - position: MyAvatar.position, - collisionsWillMove: true, - dimensions: { - x: 0.24890634417533875, - y: 0.28214839100837708, - z: 0.21127720177173615 - }, - dynamic: true, - gravity: { - x: 0, - y: -9.8, - z: 0 - }, - modelURL: "https://hifi-content.s3.amazonaws.com/caitlyn/production/elBoppo/LFT_glove_VR3.fbx", - name: "Boxing Glove - Left", - registrationPoint: { - x: 0.5, - y: 0, - z: 0.5 - }, - shapeType: "simple-hull", - type: "Model", - userData: JSON.stringify({ - grabbableKey: { - invertSolidWhileHeld: true - }, - wearable: { - joints: { - LeftHand: [ - {x: 0, y: 0.0, z: 0.02 }, - Quat.fromVec3Degrees({x: 0, y: 0, z: 0}) - ] - } - } - }) - }); - Messages.sendLocalMessage('Hifi-Hand-Grab', JSON.stringify({hand: 'left', entityID: leftBoxingGlove})); - // Allows teleporting while glove is wielded - Messages.sendLocalMessage('Hifi-Teleport-Ignore-Add', leftBoxingGlove); - - rightBoxingGlove = Entities.addEntity({ - position: MyAvatar.position, - collisionsWillMove: true, - dimensions: { - x: 0.24890634417533875, - y: 0.28214839100837708, - z: 0.21127720177173615 - }, - dynamic: true, - gravity: { - x: 0, - y: -9.8, - z: 0 - }, - modelURL: "https://hifi-content.s3.amazonaws.com/caitlyn/production/elBoppo/RT_glove_VR2.fbx", - name: "Boxing Glove - Right", - registrationPoint: { - x: 0.5, - y: 0, - z: 0.5 - }, - shapeType: "simple-hull", - type: "Model", - userData: JSON.stringify({ - grabbableKey: { - invertSolidWhileHeld: true - }, - wearable: { - joints: { - RightHand: [ - {x: 0, y: 0.0, z: 0.02 }, - Quat.fromVec3Degrees({x: 0, y: 0, z: 0}) - ] - } - } - }) - }); - Messages.sendLocalMessage('Hifi-Hand-Grab', JSON.stringify({hand: 'right', entityID: rightBoxingGlove})); - // Allows teleporting while glove is wielded - Messages.sendLocalMessage('Hifi-Teleport-Ignore-Add', rightBoxingGlove); - }; - - var cleanUpGloves = function() { - if (leftBoxingGlove !== undefined) { - Entities.deleteEntity(leftBoxingGlove); - leftBoxingGlove = undefined; - } - if (rightBoxingGlove !== undefined) { - Entities.deleteEntity(rightBoxingGlove); - rightBoxingGlove = undefined; - } - }; - - var wearGlovesIfHMD = function() { - // cleanup your old gloves if they're still there (unlikely) - cleanUpGloves(); - if (HMD.active) { - wearGloves(); - } - }; - - _this.preload = function(entityID) { - HMD.displayModeChanged.connect(function() { - if (inZone) { - wearGlovesIfHMD(); - } - }); - }; - - _this.unload = function() { - cleanUpGloves(); - }; - - _this.enterEntity = function(entityID) { - inZone = true; - print('entered boxing glove dispenser entity'); - wearGlovesIfHMD(); - - // Reset boppo if game is not running: - var parentID = Entities.getEntityProperties(entityID, ['parentID']).parentID; - Messages.sendMessage(CHANNEL_PREFIX + parentID, 'enter-zone'); - }; - - _this.leaveEntity = function(entityID) { - inZone = false; - cleanUpGloves(); - }; - - _this.unload = _this.leaveEntity; -}); diff --git a/unpublishedScripts/marketplace/boppo/createElBoppo.js b/unpublishedScripts/marketplace/boppo/createElBoppo.js deleted file mode 100644 index 4df6a2acda..0000000000 --- a/unpublishedScripts/marketplace/boppo/createElBoppo.js +++ /dev/null @@ -1,430 +0,0 @@ -// -// createElBoppo.js -// -// Created by Thijs Wenker on 3/17/17. -// 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 -// - -/* globals SCRIPT_IMPORT_PROPERTIES */ - -var MODELS_PATH = 'https://hifi-content.s3.amazonaws.com/DomainContent/Welcome%20Area/production/models/boxingRing/'; -var WANT_CLEANUP_ON_SCRIPT_ENDING = false; - -var getScriptPath = function(localPath) { - if (this.isCleanupAndSpawnScript) { - return 'https://hifi-content.s3.amazonaws.com/DomainContent/Welcome%20Area/Scripts/boppo/' + localPath; - } - return Script.resolvePath(localPath); -}; - -var getCreatePosition = function() { - // can either return position defined by resetScript or avatar position - if (this.isCleanupAndSpawnScript) { - return SCRIPT_IMPORT_PROPERTIES.rootPosition; - } - return Vec3.sum(MyAvatar.position, {x: 1, z: -2}); -}; - -var boxingRing = Entities.addEntity({ - dimensions: { - x: 4.0584001541137695, - y: 4.0418000221252441, - z: 3.0490000247955322 - }, - modelURL: MODELS_PATH + 'assembled/boppoBoxingRingAssembly.fbx', - name: 'Boxing Ring Assembly', - rotation: { - w: 0.9996337890625, - x: -1.52587890625e-05, - y: -0.026230275630950928, - z: -4.57763671875e-05 - }, - position: getCreatePosition(), - scriptTimestamp: 1489612158459, - serverScripts: getScriptPath('boppoServer.js'), - shapeType: 'static-mesh', - type: 'Model', - userData: JSON.stringify({ - Boppo: { - type: 'boxingring', - playTimeSeconds: 15 - } - }) -}); - -var boppoEntities = [ - { - dimensions: { - x: 0.36947935819625854, - y: 0.25536194443702698, - z: 0.059455446898937225 - }, - modelURL: MODELS_PATH + 'boxingGameSign/boppoSignFrame.fbx', - parentID: boxingRing, - localPosition: { - x: -1.0251024961471558, - y: 0.51661628484725952, - z: -1.1176263093948364 - }, - rotation: { - w: 0.996856689453125, - x: 0.013321161270141602, - y: 0.0024566650390625, - z: 0.078049898147583008 - }, - shapeType: 'box', - type: 'Model' - }, - { - dimensions: { - x: 0.33255371451377869, - y: 0.1812121719121933, - z: 0.0099999997764825821 - }, - lineHeight: 0.125, - name: 'Boxing Ring - High Score Board', - parentID: boxingRing, - localPosition: { - x: -1.0239436626434326, - y: 0.52212876081466675, - z: -1.0971509218215942 - }, - rotation: { - w: 0.9876401424407959, - x: 0.013046503067016602, - y: 0.0012359619140625, - z: 0.15605401992797852 - }, - text: '0:00', - textColor: { - blue: 0, - green: 0, - red: 255 - }, - type: 'Text', - userData: JSON.stringify({ - Boppo: { - type: 'timer' - } - }) - }, - { - dimensions: { - x: 0.50491130352020264, - y: 0.13274604082107544, - z: 0.0099999997764825821 - }, - lineHeight: 0.090000003576278687, - name: 'Boxing Ring - Score Board', - parentID: boxingRing, - localPosition: { - x: -0.77596306800842285, - y: 0.37797555327415466, - z: -1.0910623073577881 - }, - rotation: { - w: 0.9518122673034668, - x: 0.004237703513354063, - y: -0.0010041374480351806, - z: 0.30455198884010315 - }, - text: 'SCORE: 0', - textColor: { - blue: 0, - green: 0, - red: 255 - }, - type: 'Text', - userData: JSON.stringify({ - Boppo: { - type: 'score' - } - }) - }, - { - dimensions: { - x: 0.58153259754180908, - y: 0.1884911060333252, - z: 0.059455446898937225 - }, - modelURL: MODELS_PATH + 'boxingGameSign/boppoSignFrame.fbx', - parentID: boxingRing, - localPosition: { - x: -0.78200173377990723, - y: 0.35684797167778015, - z: -1.108180046081543 - }, - rotation: { - w: 0.97814905643463135, - x: 0.0040436983108520508, - y: -0.0005645751953125, - z: 0.20778214931488037 - }, - shapeType: 'box', - type: 'Model' - }, - { - dimensions: { - x: 4.1867804527282715, - y: 3.5065803527832031, - z: 5.6845207214355469 - }, - name: 'El Boppo the Clown boxing area & glove maker', - parentID: boxingRing, - localPosition: { - x: -0.012308252975344658, - y: 0.054641719907522202, - z: 0.98782551288604736 - }, - rotation: { - w: 1, - x: -1.52587890625e-05, - y: -1.52587890625e-05, - z: -1.52587890625e-05 - }, - script: getScriptPath('clownGloveDispenser.js'), - shapeType: 'box', - type: 'Zone', - visible: false - }, - { - color: { - blue: 255, - green: 5, - red: 255 - }, - dimensions: { - x: 0.20000000298023224, - y: 0.20000000298023224, - z: 0.20000000298023224 - }, - name: 'LookAtBox', - parentID: boxingRing, - localPosition: { - x: -0.1772226095199585, - y: -1.7072629928588867, - z: 1.3122396469116211 - }, - rotation: { - w: 0.999969482421875, - x: 1.52587890625e-05, - y: 0.0043793916702270508, - z: 1.52587890625e-05 - }, - shape: 'Cube', - type: 'Box', - userData: JSON.stringify({ - Boppo: { - type: 'lookAtThis' - } - }) - }, - { - color: { - blue: 209, - green: 157, - red: 209 - }, - dimensions: { - x: 1.6913000345230103, - y: 1.2124500274658203, - z: 0.2572999894618988 - }, - name: 'boppoBackBoard', - parentID: boxingRing, - localPosition: { - x: -0.19500596821308136, - y: -1.1044719219207764, - z: -0.55993378162384033 - }, - rotation: { - w: 0.9807126522064209, - x: -0.19511711597442627, - y: 0.0085297822952270508, - z: 0.0016937255859375 - }, - shape: 'Cube', - type: 'Box', - visible: false - }, - { - color: { - blue: 0, - green: 0, - red: 255 - }, - dimensions: { - x: 1.8155574798583984, - y: 0.92306196689605713, - z: 0.51203572750091553 - }, - name: 'boppoBackBoard', - parentID: boxingRing, - localPosition: { - x: -0.11036647111177444, - y: -0.051978692412376404, - z: -0.79054081439971924 - }, - rotation: { - w: 0.9807431697845459, - x: 0.19505608081817627, - y: 0.0085602998733520508, - z: -0.0017547607421875 - }, - shape: 'Cube', - type: 'Box', - visible: false - }, - { - color: { - blue: 209, - green: 157, - red: 209 - }, - dimensions: { - x: 1.9941408634185791, - y: 1.2124500274658203, - z: 0.2572999894618988 - }, - name: 'boppoBackBoard', - localPosition: { - x: 0.69560068845748901, - y: -1.3840068578720093, - z: 0.059689953923225403 - }, - rotation: { - w: 0.73458456993103027, - x: -0.24113833904266357, - y: -0.56545358896255493, - z: -0.28734266757965088 - }, - shape: 'Cube', - type: 'Box', - visible: false - }, - { - color: { - blue: 82, - green: 82, - red: 82 - }, - dimensions: { - x: 8.3777303695678711, - y: 0.87573593854904175, - z: 7.9759469032287598 - }, - parentID: boxingRing, - localPosition: { - x: -0.38302639126777649, - y: -2.121284008026123, - z: 0.3699878454208374 - }, - rotation: { - w: 0.70711839199066162, - x: -7.62939453125e-05, - y: 0.70705735683441162, - z: -1.52587890625e-05 - }, - shape: 'Triangle', - type: 'Shape' - }, - { - color: { - blue: 209, - green: 157, - red: 209 - }, - dimensions: { - x: 1.889795184135437, - y: 0.86068248748779297, - z: 0.2572999894618988 - }, - name: 'boppoBackBoard', - parentID: boxingRing, - localPosition: { - x: -0.95167744159698486, - y: -1.4756947755813599, - z: -0.042313352227210999 - }, - rotation: { - w: 0.74004733562469482, - x: -0.24461740255355835, - y: 0.56044864654541016, - z: 0.27998781204223633 - }, - shape: 'Cube', - type: 'Box', - visible: false - }, - { - color: { - blue: 0, - green: 0, - red: 255 - }, - dimensions: { - x: 4.0720257759094238, - y: 0.50657749176025391, - z: 1.4769613742828369 - }, - name: 'boppo-stepsRamp', - parentID: boxingRing, - localPosition: { - x: -0.002939039608463645, - y: -1.9770187139511108, - z: 2.2165381908416748 - }, - rotation: { - w: 0.99252307415008545, - x: 0.12184333801269531, - y: -1.52587890625e-05, - z: -1.52587890625e-05 - }, - shape: 'Cube', - type: 'Box', - visible: false - }, - { - color: { - blue: 150, - green: 150, - red: 150 - }, - cutoff: 90, - dimensions: { - x: 5.2220535278320312, - y: 5.2220535278320312, - z: 5.2220535278320312 - }, - falloffRadius: 2, - intensity: 15, - name: 'boxing ring light', - parentID: boxingRing, - localPosition: { - x: -1.4094564914703369, - y: -0.36021926999092102, - z: 0.81797939538955688 - }, - rotation: { - w: 0.9807431697845459, - x: 1.52587890625e-05, - y: -0.19520866870880127, - z: -1.52587890625e-05 - }, - type: 'Light' - } -]; - -boppoEntities.forEach(function(entityProperties) { - entityProperties['parentID'] = boxingRing; - Entities.addEntity(entityProperties); -}); - -if (WANT_CLEANUP_ON_SCRIPT_ENDING) { - Script.scriptEnding.connect(function() { - Entities.deleteEntity(boxingRing); - }); -} diff --git a/unpublishedScripts/marketplace/boppo/lookAtEntity.js b/unpublishedScripts/marketplace/boppo/lookAtEntity.js deleted file mode 100644 index ba072814f2..0000000000 --- a/unpublishedScripts/marketplace/boppo/lookAtEntity.js +++ /dev/null @@ -1,98 +0,0 @@ -// -// lookAtTarget.js -// -// Created by Thijs Wenker on 3/15/17. -// 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 -// - -/* globals LookAtTarget:true */ - -LookAtTarget = function(sourceEntityID) { - /* private variables */ - var _this, - _options, - _sourceEntityID, - _sourceEntityProperties, - REQUIRED_PROPERTIES = ['position', 'rotation', 'userData'], - LOOK_AT_TAG = 'lookAtTarget'; - - LookAtTarget = function(sourceEntityID) { - _this = this; - _sourceEntityID = sourceEntityID; - _this.updateOptions(); - }; - - /* private functions */ - var updateEntitySourceProperties = function() { - _sourceEntityProperties = Entities.getEntityProperties(_sourceEntityID, REQUIRED_PROPERTIES); - }; - - var getUpdatedActionProperties = function() { - return { - targetRotation: _this.getLookAtRotation(), - angularTimeScale: 0.1, - ttl: 10 - }; - }; - - var getNewActionProperties = function() { - var newActionProperties = getUpdatedActionProperties(); - newActionProperties.tag = LOOK_AT_TAG; - return newActionProperties; - }; - - LookAtTarget.prototype = { - /* public functions */ - updateOptions: function() { - updateEntitySourceProperties(); - _options = JSON.parse(_sourceEntityProperties.userData).lookAt; - }, - getTargetPosition: function() { - return Entities.getEntityProperties(_options.targetID).position; - }, - getLookAtRotation: function() { - _this.updateOptions(); - - var newRotation = Quat.lookAt(_sourceEntityProperties.position, _this.getTargetPosition(), Vec3.UP); - if (_options.rotationOffset !== undefined) { - newRotation = Quat.multiply(newRotation, Quat.fromVec3Degrees(_options.rotationOffset)); - } - if (_options.disablePitch || _options.disableYaw || _options.disablePitch) { - var disabledAxis = _options.clearDisabledAxis ? Vec3.ZERO : - Quat.safeEulerAngles(_sourceEntityProperties.rotation); - var newEulers = Quat.safeEulerAngles(newRotation); - newRotation = Quat.fromVec3Degrees({ - x: _options.disablePitch ? disabledAxis.x : newEulers.x, - y: _options.disableYaw ? disabledAxis.y : newEulers.y, - z: _options.disableRoll ? disabledAxis.z : newEulers.z - }); - } - return newRotation; - }, - lookAtDirectly: function() { - Entities.editEntity(_sourceEntityID, {rotation: _this.getLookAtRotation()}); - }, - lookAtByAction: function() { - var actionIDs = Entities.getActionIDs(_sourceEntityID); - var actionFound = false; - actionIDs.forEach(function(actionID) { - if (actionFound) { - return; - } - var actionArguments = Entities.getActionArguments(_sourceEntityID, actionID); - if (actionArguments.tag === LOOK_AT_TAG) { - actionFound = true; - Entities.updateAction(_sourceEntityID, actionID, getUpdatedActionProperties()); - } - }); - if (!actionFound) { - Entities.addAction('spring', _sourceEntityID, getNewActionProperties()); - } - } - }; - - return new LookAtTarget(sourceEntityID); -};