From 59781583584fcaa931dbc6a1491cba0e0797a6d6 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Wed, 11 Apr 2018 17:21:44 +0200 Subject: [PATCH 001/286] Updated debugAmbientOcclussionPass script to tablet app --- .../utilities/render/ambientOcclusionPass.qml | 75 +++++++---- .../render/debugAmbientOcclusionPass.js | 126 ++++++++++++++---- 2 files changed, 151 insertions(+), 50 deletions(-) diff --git a/scripts/developer/utilities/render/ambientOcclusionPass.qml b/scripts/developer/utilities/render/ambientOcclusionPass.qml index 08334cf2aa..b661f992f1 100644 --- a/scripts/developer/utilities/render/ambientOcclusionPass.qml +++ b/scripts/developer/utilities/render/ambientOcclusionPass.qml @@ -7,40 +7,60 @@ // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0.html // -import QtQuick 2.5 +import QtQuick 2.7 import QtQuick.Controls 1.4 +import QtQuick.Layouts 1.3 + +import "qrc:///qml/styles-uit" +import "qrc:///qml/controls-uit" as HifiControls + import "configSlider" import "../lib/plotperf" -Column { - spacing: 8 +Rectangle { + HifiConstants { id: hifi;} + id: render; + anchors.margins: hifi.dimensions.contentMargin.x + + color: hifi.colors.baseGray; + Column { id: surfaceGeometry spacing: 10 + anchors.left: parent.left + anchors.right: parent.right + anchors.margins: hifi.dimensions.contentMargin.x - Column{ - Repeater { - model: [ - "Radius:radius:2.0:false", - "Level:obscuranceLevel:1.0:false", - "Num Taps:numSamples:32:true", - "Taps Spiral:numSpiralTurns:10.0:false", - "Falloff Bias:falloffBias:0.2:false", - "Edge Sharpness:edgeSharpness:1.0:false", - "Blur Radius:blurRadius:10.0:false", - ] - ConfigSlider { - label: qsTr(modelData.split(":")[0]) - integral: (modelData.split(":")[3] == 'true') - config: Render.getConfig("RenderMainView.AmbientOcclusion") - property: modelData.split(":")[1] - max: modelData.split(":")[2] - min: 0.0 - } + Repeater { + model: [ + "Radius:radius:2.0:false", + "Level:obscuranceLevel:1.0:false", + "Num Taps:numSamples:32:true", + "Taps Spiral:numSpiralTurns:10.0:false", + "Falloff Bias:falloffBias:0.2:false", + "Edge Sharpness:edgeSharpness:1.0:false", + "Blur Radius:blurRadius:10.0:false", + ] + ConfigSlider { + label: qsTr(modelData.split(":")[0]) + integral: (modelData.split(":")[3] == 'true') + config: Render.getConfig("RenderMainView.AmbientOcclusion") + property: modelData.split(":")[1] + max: modelData.split(":")[2] + min: 0.0 + width: 280 + height:38 } } + Row{ + spacing: 10 + anchors.left: parent.left + anchors.right: parent.right + Column { + spacing: 10 + Repeater { model: [ "resolutionLevel:resolutionLevel", @@ -48,19 +68,24 @@ Column { "fetchMipsEnabled:fetchMipsEnabled", "borderingEnabled:borderingEnabled" ] - CheckBox { + HifiControls.CheckBox { + boxSize: 20 text: qsTr(modelData.split(":")[0]) checked: Render.getConfig("RenderMainView.AmbientOcclusion")[modelData.split(":")[1]] onCheckedChanged: { Render.getConfig("RenderMainView.AmbientOcclusion")[modelData.split(":")[1]] = checked } } } } + Column { + spacing: 10 + Repeater { model: [ "debugEnabled:showCursorPixel" ] - CheckBox { + HifiControls.CheckBox { + boxSize: 20 text: qsTr(modelData.split(":")[0]) checked: Render.getConfig("RenderMainView.DebugAmbientOcclusion")[modelData.split(":")[1]] onCheckedChanged: { Render.getConfig("RenderMainView.DebugAmbientOcclusion")[modelData.split(":")[1]] = checked } @@ -70,6 +95,8 @@ Column { } PlotPerf { + anchors.left: parent.left + anchors.right: parent.right title: "Timing" height: 50 object: Render.getConfig("RenderMainView.AmbientOcclusion") diff --git a/scripts/developer/utilities/render/debugAmbientOcclusionPass.js b/scripts/developer/utilities/render/debugAmbientOcclusionPass.js index f70b3a5cc9..776fc1b600 100644 --- a/scripts/developer/utilities/render/debugAmbientOcclusionPass.js +++ b/scripts/developer/utilities/render/debugAmbientOcclusionPass.js @@ -1,38 +1,112 @@ // -// debugSurfaceGeometryPass.js +// debugAmbientOcclusionPass.js +// developer/utilities/render // -// Created by Sam Gateau on 6/6/2016 -// Copyright 2016 High Fidelity, Inc. +// Olivier Prat, created on 11/04/2018. +// Copyright 2017 High Fidelity, Inc. // // Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0.html +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -// Set up the qml ui -var qml = Script.resolvePath('ambientOcclusionPass.qml'); -var window = new OverlayWindow({ - title: 'Ambient Occlusion Pass', - source: qml, - width: 400, height: 300, -}); -window.setPosition(Window.innerWidth - 420, 50 + 550 + 50); -window.closed.connect(function() { Script.stop(); }); +(function() { + "use strict"; + var TABLET_BUTTON_NAME = "SSAO"; + var QMLAPP_URL = Script.resolvePath("./ambientOcclusionPass.qml"); + var ICON_URL = Script.resolvePath("../../../system/assets/images/ssao-i.svg"); + var ACTIVE_ICON_URL = Script.resolvePath("../../../system/assets/images/ssao-a.svg"); -var moveDebugCursor = false; -Controller.mousePressEvent.connect(function (e) { - if (e.isMiddleButton) { - moveDebugCursor = true; - setDebugCursor(e.x, e.y); + Script.include([ + Script.resolvePath("../../../system/libraries/stringHelpers.js"), + ]); + + var onScreen = false; + + function onClicked() { + if (onScreen) { + tablet.gotoHomeScreen(); + } else { + tablet.loadQMLSource(QMLAPP_URL); + } } -}); -Controller.mouseReleaseEvent.connect(function() { moveDebugCursor = false; }); -Controller.mouseMoveEvent.connect(function (e) { if (moveDebugCursor) setDebugCursor(e.x, e.y); }); + + var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); + var button = tablet.addButton({ + text: TABLET_BUTTON_NAME, + icon: ICON_URL, + activeIcon: ACTIVE_ICON_URL + }); + + var hasEventBridge = false; + + function wireEventBridge(on) { + if (!tablet) { + print("Warning in wireEventBridge(): 'tablet' undefined!"); + return; + } + if (on) { + if (!hasEventBridge) { + tablet.fromQml.connect(fromQml); + hasEventBridge = true; + } + } else { + if (hasEventBridge) { + tablet.fromQml.disconnect(fromQml); + hasEventBridge = false; + } + } + } + + function onScreenChanged(type, url) { + if (url === QMLAPP_URL) { + onScreen = true; + } else { + onScreen = false; + } + + button.editProperties({isActive: onScreen}); + wireEventBridge(onScreen); + } + + function fromQml(message) { + } + + button.clicked.connect(onClicked); + tablet.screenChanged.connect(onScreenChanged); + + Script.scriptEnding.connect(function () { + if (onScreen) { + tablet.gotoHomeScreen(); + } + button.clicked.disconnect(onClicked); + tablet.screenChanged.disconnect(onScreenChanged); + tablet.removeButton(button); + }); + + + /* + var moveDebugCursor = false; + Controller.mousePressEvent.connect(function (e) { + if (e.isMiddleButton) { + moveDebugCursor = true; + setDebugCursor(e.x, e.y); + } + }); + Controller.mouseReleaseEvent.connect(function() { moveDebugCursor = false; }); + Controller.mouseMoveEvent.connect(function (e) { if (moveDebugCursor) setDebugCursor(e.x, e.y); }); -function setDebugCursor(x, y) { - nx = (x / Window.innerWidth); - ny = 1.0 - ((y) / (Window.innerHeight - 32)); + function setDebugCursor(x, y) { + nx = (x / Window.innerWidth); + ny = 1.0 - ((y) / (Window.innerHeight - 32)); + + Render.getConfig("RenderMainView").getConfig("DebugAmbientOcclusion").debugCursorTexcoord = { x: nx, y: ny }; + } + */ + + function cleanup() { + } + Script.scriptEnding.connect(cleanup); +}()); - Render.getConfig("RenderMainView").getConfig("DebugAmbientOcclusion").debugCursorTexcoord = { x: nx, y: ny }; -} From 644ef33730914c7c50e7dfbd5c3e93b89850b704 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Thu, 12 Apr 2018 17:58:05 +0200 Subject: [PATCH 002/286] First draft at batch generateTextureMipsWithPipeline --- .../gpu-gl-common/src/gpu/gl/GLBackend.cpp | 16 ++++- .../gpu-gl-common/src/gpu/gl/GLBackend.h | 9 ++- .../src/gpu/gl/GLBackendTexture.cpp | 62 ++++++++++++++++--- libraries/gpu-gl/src/gpu/gl41/GL41Backend.cpp | 7 +-- libraries/gpu-gl/src/gpu/gl41/GL41Backend.h | 4 +- libraries/gpu-gl/src/gpu/gl45/GL45Backend.cpp | 37 +++++------ libraries/gpu-gl/src/gpu/gl45/GL45Backend.h | 2 +- libraries/gpu/src/gpu/Batch.cpp | 9 +++ libraries/gpu/src/gpu/Batch.h | 7 ++- libraries/gpu/src/gpu/Context.cpp | 12 ++++ libraries/gpu/src/gpu/Context.h | 2 + libraries/render-utils/src/ssao.slh | 1 + 12 files changed, 124 insertions(+), 44 deletions(-) diff --git a/libraries/gpu-gl-common/src/gpu/gl/GLBackend.cpp b/libraries/gpu-gl-common/src/gpu/gl/GLBackend.cpp index 9bb02d678d..0858e36f65 100644 --- a/libraries/gpu-gl-common/src/gpu/gl/GLBackend.cpp +++ b/libraries/gpu-gl-common/src/gpu/gl/GLBackend.cpp @@ -62,7 +62,8 @@ GLBackend::CommandCall GLBackend::_commandCalls[Batch::NUM_COMMANDS] = (&::gpu::gl::GLBackend::do_setFramebufferSwapChain), (&::gpu::gl::GLBackend::do_clearFramebuffer), (&::gpu::gl::GLBackend::do_blit), - (&::gpu::gl::GLBackend::do_generateTextureMips), + (&::gpu::gl::GLBackend::do_generateTextureMips), + (&::gpu::gl::GLBackend::do_generateTextureMipsWithPipeline), (&::gpu::gl::GLBackend::do_advance), @@ -133,10 +134,23 @@ GLBackend::GLBackend() { GLBackend::~GLBackend() { + if (_mipGenerationFramebufferId) { + glDeleteFramebuffers(1, &_mipGenerationFramebufferId); + _mipGenerationFramebufferId = 0; + } killInput(); killTransform(); } +void GLBackend::do_draw(const Batch& batch, size_t paramOffset) { + Primitive primitiveType = (Primitive)batch._params[paramOffset + 2]._uint; + GLenum mode = gl::PRIMITIVE_TO_GL[primitiveType]; + uint32 numVertices = batch._params[paramOffset + 1]._uint; + uint32 startVertex = batch._params[paramOffset + 0]._uint; + + draw(mode, numVertices, startVertex); +} + void GLBackend::renderPassTransfer(const Batch& batch) { const size_t numCommands = batch.getCommands().size(); const Batch::Commands::value_type* command = batch.getCommands().data(); diff --git a/libraries/gpu-gl-common/src/gpu/gl/GLBackend.h b/libraries/gpu-gl-common/src/gpu/gl/GLBackend.h index 6355137a19..0d62e7490f 100644 --- a/libraries/gpu-gl-common/src/gpu/gl/GLBackend.h +++ b/libraries/gpu-gl-common/src/gpu/gl/GLBackend.h @@ -108,7 +108,7 @@ public: // Draw Stage - virtual void do_draw(const Batch& batch, size_t paramOffset) = 0; + virtual void do_draw(const Batch& batch, size_t paramOffset); virtual void do_drawIndexed(const Batch& batch, size_t paramOffset) = 0; virtual void do_drawInstanced(const Batch& batch, size_t paramOffset) = 0; virtual void do_drawIndexedInstanced(const Batch& batch, size_t paramOffset) = 0; @@ -120,7 +120,8 @@ public: virtual void do_setInputBuffer(const Batch& batch, size_t paramOffset) final; virtual void do_setIndexBuffer(const Batch& batch, size_t paramOffset) final; virtual void do_setIndirectBuffer(const Batch& batch, size_t paramOffset) final; - virtual void do_generateTextureMips(const Batch& batch, size_t paramOffset) final; + virtual void do_generateTextureMips(const Batch& batch, size_t paramOffset) final; + virtual void do_generateTextureMipsWithPipeline(const Batch& batch, size_t paramOffset) final; // Transform Stage virtual void do_setModelTransform(const Batch& batch, size_t paramOffset) final; @@ -234,6 +235,8 @@ public: protected: + virtual void draw(GLenum mode, uint32 numVertices, uint32 startVertex) = 0; + void recycle() const override; // FIXME instead of a single flag, create a features struct similar to @@ -492,6 +495,8 @@ protected: } _textureManagement; virtual void initTextureManagementStage() {} + GLuint _mipGenerationFramebufferId{ 0 }; + typedef void (GLBackend::*CommandCall)(const Batch&, size_t); static CommandCall _commandCalls[Batch::NUM_COMMANDS]; friend class GLState; diff --git a/libraries/gpu-gl-common/src/gpu/gl/GLBackendTexture.cpp b/libraries/gpu-gl-common/src/gpu/gl/GLBackendTexture.cpp index ca4e328612..674fdacf0f 100644 --- a/libraries/gpu-gl-common/src/gpu/gl/GLBackendTexture.cpp +++ b/libraries/gpu-gl-common/src/gpu/gl/GLBackendTexture.cpp @@ -66,16 +66,58 @@ GLTexture* GLBackend::syncGPUObject(const TexturePointer& texturePointer) { } void GLBackend::do_generateTextureMips(const Batch& batch, size_t paramOffset) { - TexturePointer resourceTexture = batch._textures.get(batch._params[paramOffset + 0]._uint); - if (!resourceTexture) { - return; - } + TexturePointer resourceTexture = batch._textures.get(batch._params[paramOffset + 0]._uint); + if (!resourceTexture) { + return; + } - // DO not transfer the texture, this call is expected for rendering texture - GLTexture* object = syncGPUObject(resourceTexture); - if (!object) { - return; - } + // DO not transfer the texture, this call is expected for rendering texture + GLTexture* object = syncGPUObject(resourceTexture); + if (!object) { + return; + } - object->generateMips(); + object->generateMips(); +} + +void GLBackend::do_generateTextureMipsWithPipeline(const Batch& batch, size_t paramOffset) { + TexturePointer resourceTexture = batch._textures.get(batch._params[paramOffset + 0]._uint); + if (!resourceTexture) { + return; + } + + // Do not transfer the texture, this call is expected for rendering texture + GLTexture* object = syncGPUObject(resourceTexture); + if (!object) { + return; + } + + auto numMips = batch._params[paramOffset + 1]._int; + if (numMips < 0) { + numMips = resourceTexture->getNumMips(); + } else { + numMips = std::min(numMips, (int)resourceTexture->getNumMips()); + } + + if (_mipGenerationFramebufferId == 0) { + glGenFramebuffers(1, &_mipGenerationFramebufferId); + Q_ASSERT(_mipGenerationFramebufferId > 0); + } + + glBindFramebuffer(GL_FRAMEBUFFER, _mipGenerationFramebufferId); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER_EXT, 0); + + for (int level = 1; level < numMips; level++) { + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, object->_id, level); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, level - 1); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, level - 1); + + const auto mipDimensions = resourceTexture->evalMipDimensions(level); + glViewport(0, 0, mipDimensions.x, mipDimensions.y); + draw(GL_TRIANGLE_STRIP, 4, 0); + } + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, numMips-1); + + resetOutputStage(); } diff --git a/libraries/gpu-gl/src/gpu/gl41/GL41Backend.cpp b/libraries/gpu-gl/src/gpu/gl41/GL41Backend.cpp index 88cf89ad99..d4376632ca 100644 --- a/libraries/gpu-gl/src/gpu/gl41/GL41Backend.cpp +++ b/libraries/gpu-gl/src/gpu/gl41/GL41Backend.cpp @@ -20,12 +20,7 @@ using namespace gpu::gl41; const std::string GL41Backend::GL41_VERSION { "GL41" }; -void GL41Backend::do_draw(const Batch& batch, size_t paramOffset) { - Primitive primitiveType = (Primitive)batch._params[paramOffset + 2]._uint; - GLenum mode = gl::PRIMITIVE_TO_GL[primitiveType]; - uint32 numVertices = batch._params[paramOffset + 1]._uint; - uint32 startVertex = batch._params[paramOffset + 0]._uint; - +void GL41Backend::draw(GLenum mode, uint32 numVertices, uint32 startVertex) { if (isStereo()) { #ifdef GPU_STEREO_DRAWCALL_INSTANCED glDrawArraysInstanced(mode, startVertex, numVertices, 2); diff --git a/libraries/gpu-gl/src/gpu/gl41/GL41Backend.h b/libraries/gpu-gl/src/gpu/gl41/GL41Backend.h index 3c59781f78..408766f566 100644 --- a/libraries/gpu-gl/src/gpu/gl41/GL41Backend.h +++ b/libraries/gpu-gl/src/gpu/gl41/GL41Backend.h @@ -133,6 +133,9 @@ public: }; protected: + + void draw(GLenum mode, uint32 numVertices, uint32 startVertex) override; + GLuint getFramebufferID(const FramebufferPointer& framebuffer) override; GLFramebuffer* syncGPUObject(const Framebuffer& framebuffer) override; @@ -146,7 +149,6 @@ protected: GLQuery* syncGPUObject(const Query& query) override; // Draw Stage - void do_draw(const Batch& batch, size_t paramOffset) override; void do_drawIndexed(const Batch& batch, size_t paramOffset) override; void do_drawInstanced(const Batch& batch, size_t paramOffset) override; void do_drawIndexedInstanced(const Batch& batch, size_t paramOffset) override; diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.cpp index c119e27d5b..9a08bbf173 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.cpp @@ -24,33 +24,28 @@ void GL45Backend::recycle() const { Parent::recycle(); } -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]; - uint32 numVertices = batch._params[paramOffset + 1]._uint; - uint32 startVertex = batch._params[paramOffset + 0]._uint; - - if (isStereo()) { +void GL45Backend::draw(GLenum mode, uint32 numVertices, uint32 startVertex) { + if (isStereo()) { #ifdef GPU_STEREO_DRAWCALL_INSTANCED - glDrawArraysInstanced(mode, startVertex, numVertices, 2); + glDrawArraysInstanced(mode, startVertex, numVertices, 2); #else - setupStereoSide(0); - glDrawArrays(mode, startVertex, numVertices); - setupStereoSide(1); - glDrawArrays(mode, startVertex, numVertices); + setupStereoSide(0); + glDrawArrays(mode, startVertex, numVertices); + setupStereoSide(1); + glDrawArrays(mode, startVertex, numVertices); #endif - _stats._DSNumTriangles += 2 * numVertices / 3; - _stats._DSNumDrawcalls += 2; + _stats._DSNumTriangles += 2 * numVertices / 3; + _stats._DSNumDrawcalls += 2; - } else { - glDrawArrays(mode, startVertex, numVertices); - _stats._DSNumTriangles += numVertices / 3; - _stats._DSNumDrawcalls++; - } - _stats._DSNumAPIDrawcalls++; + } else { + glDrawArrays(mode, startVertex, numVertices); + _stats._DSNumTriangles += numVertices / 3; + _stats._DSNumDrawcalls++; + } + _stats._DSNumAPIDrawcalls++; - (void) CHECK_GL_ERROR(); + (void)CHECK_GL_ERROR(); } void GL45Backend::do_drawIndexed(const Batch& batch, size_t paramOffset) { diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h index c23a83eaf9..1683861c89 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h +++ b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h @@ -231,6 +231,7 @@ public: protected: + void draw(GLenum mode, uint32 numVertices, uint32 startVertex) override; void recycle() const override; GLuint getFramebufferID(const FramebufferPointer& framebuffer) override; @@ -246,7 +247,6 @@ protected: // Draw Stage - void do_draw(const Batch& batch, size_t paramOffset) override; void do_drawIndexed(const Batch& batch, size_t paramOffset) override; void do_drawInstanced(const Batch& batch, size_t paramOffset) override; void do_drawIndexedInstanced(const Batch& batch, size_t paramOffset) override; diff --git a/libraries/gpu/src/gpu/Batch.cpp b/libraries/gpu/src/gpu/Batch.cpp index db4941c163..0c49901091 100644 --- a/libraries/gpu/src/gpu/Batch.cpp +++ b/libraries/gpu/src/gpu/Batch.cpp @@ -425,6 +425,15 @@ void Batch::generateTextureMips(const TexturePointer& texture) { _params.emplace_back(_textures.cache(texture)); } +void Batch::generateTextureMipsWithPipeline(const TexturePointer& texture, int numMips) { + setResourceTexture(0, texture); + + ADD_COMMAND(generateTextureMipsWithPipeline); + + _params.emplace_back(_textures.cache(texture)); + _params.emplace_back(numMips); +} + void Batch::beginQuery(const QueryPointer& query) { ADD_COMMAND(beginQuery); diff --git a/libraries/gpu/src/gpu/Batch.h b/libraries/gpu/src/gpu/Batch.h index 96b234295d..90a30f4e5e 100644 --- a/libraries/gpu/src/gpu/Batch.h +++ b/libraries/gpu/src/gpu/Batch.h @@ -211,7 +211,9 @@ public: void blit(const FramebufferPointer& src, const Vec4i& srcRect, const FramebufferPointer& dst, const Vec4i& dstRect); // Generate the mips for a texture - void generateTextureMips(const TexturePointer& texture); + void generateTextureMips(const TexturePointer& texture); + // Generate the mips for a texture using the current pipeline + void generateTextureMipsWithPipeline(const TexturePointer& texture, int numMips = -1); // Query Section void beginQuery(const QueryPointer& query); @@ -310,7 +312,8 @@ public: COMMAND_setFramebufferSwapChain, COMMAND_clearFramebuffer, COMMAND_blit, - COMMAND_generateTextureMips, + COMMAND_generateTextureMips, + COMMAND_generateTextureMipsWithPipeline, COMMAND_advance, diff --git a/libraries/gpu/src/gpu/Context.cpp b/libraries/gpu/src/gpu/Context.cpp index e1b68c88ca..657829214f 100644 --- a/libraries/gpu/src/gpu/Context.cpp +++ b/libraries/gpu/src/gpu/Context.cpp @@ -14,6 +14,7 @@ #include "Frame.h" #include "GPULogging.h" +#include "StandardShaderLib.h" using namespace gpu; @@ -317,3 +318,14 @@ Size Context::getTexturePendingGPUTransferMemSize() { Size Context::getTextureResourcePopulatedGPUMemSize() { return Backend::textureResourcePopulatedGPUMemSize.getValue(); } + +PipelinePointer Context::createMipGenerationPipeline(const ShaderPointer& ps, const gpu::Shader::BindingSet& slotBindings) { + auto vs = gpu::StandardShaderLib::getDrawViewportQuadTransformTexcoordVS(); + static gpu::StatePointer state(new gpu::State()); + + gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps); + gpu::Shader::makeProgram(*program, slotBindings); + + // Good to go add the brand new pipeline + return gpu::Pipeline::create(program, state); +} diff --git a/libraries/gpu/src/gpu/Context.h b/libraries/gpu/src/gpu/Context.h index 2df7de2331..63713832e7 100644 --- a/libraries/gpu/src/gpu/Context.h +++ b/libraries/gpu/src/gpu/Context.h @@ -220,6 +220,8 @@ public: // Same as above but grabbed at every end of a frame void getFrameStats(ContextStats& stats) const; + static PipelinePointer createMipGenerationPipeline(const ShaderPointer& pixelShader, const gpu::Shader::BindingSet& slotBindings = gpu::Shader::BindingSet()); + double getFrameTimerGPUAverage() const; double getFrameTimerBatchAverage() const; diff --git a/libraries/render-utils/src/ssao.slh b/libraries/render-utils/src/ssao.slh index c9b27bc2c8..c0039265fa 100644 --- a/libraries/render-utils/src/ssao.slh +++ b/libraries/render-utils/src/ssao.slh @@ -313,6 +313,7 @@ float evalAO(in vec3 C, in vec3 n_C, in vec3 Q) { // the source occlusion texture uniform sampler2D occlusionMap; + vec2 fetchOcclusionDepthRaw(ivec2 coords, out vec3 raw) { raw = texelFetch(occlusionMap, coords, 0).xyz; return unpackOcclusionDepth(raw); From 7e78e79685e9c61ac954aabdca3ba87f5d19b5c5 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Wed, 18 Apr 2018 17:20:16 +0200 Subject: [PATCH 003/286] Created median shader for AO depth mip creation but generateMipWithPipeline not working --- .../src/gpu/gl/GLBackendTexture.cpp | 2 +- libraries/gpu/src/gpu/Batch.h | 2 +- .../src/AmbientOcclusionEffect.cpp | 32 +++++++++++++---- .../render-utils/src/AmbientOcclusionEffect.h | 14 ++++---- .../render-utils/src/mip_depth_median.slf | 35 +++++++++++++++++++ 5 files changed, 70 insertions(+), 15 deletions(-) create mode 100644 libraries/render-utils/src/mip_depth_median.slf diff --git a/libraries/gpu-gl-common/src/gpu/gl/GLBackendTexture.cpp b/libraries/gpu-gl-common/src/gpu/gl/GLBackendTexture.cpp index 674fdacf0f..410c52a1f6 100644 --- a/libraries/gpu-gl-common/src/gpu/gl/GLBackendTexture.cpp +++ b/libraries/gpu-gl-common/src/gpu/gl/GLBackendTexture.cpp @@ -105,7 +105,7 @@ void GLBackend::do_generateTextureMipsWithPipeline(const Batch& batch, size_t pa } glBindFramebuffer(GL_FRAMEBUFFER, _mipGenerationFramebufferId); - glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER_EXT, 0); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, 0); for (int level = 1; level < numMips; level++) { glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, object->_id, level); diff --git a/libraries/gpu/src/gpu/Batch.h b/libraries/gpu/src/gpu/Batch.h index 90a30f4e5e..06b1bd860e 100644 --- a/libraries/gpu/src/gpu/Batch.h +++ b/libraries/gpu/src/gpu/Batch.h @@ -213,7 +213,7 @@ public: // Generate the mips for a texture void generateTextureMips(const TexturePointer& texture); // Generate the mips for a texture using the current pipeline - void generateTextureMipsWithPipeline(const TexturePointer& texture, int numMips = -1); + void generateTextureMipsWithPipeline(const TexturePointer& destTexture, int numMips = -1); // Query Section void beginQuery(const QueryPointer& query); diff --git a/libraries/render-utils/src/AmbientOcclusionEffect.cpp b/libraries/render-utils/src/AmbientOcclusionEffect.cpp index c526f16b75..7176135a0a 100644 --- a/libraries/render-utils/src/AmbientOcclusionEffect.cpp +++ b/libraries/render-utils/src/AmbientOcclusionEffect.cpp @@ -34,6 +34,12 @@ #include "ssao_makeHorizontalBlur_frag.h" #include "ssao_makeVerticalBlur_frag.h" +#include "mip_depth_median_frag.h" + +gpu::PipelinePointer AmbientOcclusionEffect::_occlusionPipeline; +gpu::PipelinePointer AmbientOcclusionEffect::_hBlurPipeline; +gpu::PipelinePointer AmbientOcclusionEffect::_vBlurPipeline; +gpu::PipelinePointer AmbientOcclusionEffect::_mipCreationPipeline; AmbientOcclusionFramebuffer::AmbientOcclusionFramebuffer() { } @@ -333,6 +339,15 @@ const gpu::PipelinePointer& AmbientOcclusionEffect::getVBlurPipeline() { return _vBlurPipeline; } +const gpu::PipelinePointer& AmbientOcclusionEffect::getMipCreationPipeline() { + if (!_mipCreationPipeline) { + gpu::Shader::BindingSet slotBindings; + slotBindings.insert(gpu::Shader::Binding(std::string("depthTexture"), 0)); + _mipCreationPipeline = gpu::Context::createMipGenerationPipeline(mip_depth_median_frag::getShader(), slotBindings); + } + return _mipCreationPipeline; +} + void AmbientOcclusionEffect::updateGaussianDistribution() { auto coefs = _parametersBuffer.edit()._gaussianCoefs; GaussianDistribution::evalSampling(coefs, Parameters::GAUSSIAN_COEFS_LENGTH, _parametersBuffer->getBlurRadius(), _parametersBuffer->getBlurDeviation()); @@ -384,9 +399,11 @@ void AmbientOcclusionEffect::run(const render::RenderContextPointer& renderConte auto occlusionPipeline = getOcclusionPipeline(); auto firstHBlurPipeline = getHBlurPipeline(); auto lastVBlurPipeline = getVBlurPipeline(); + auto mipCreationPipeline = getMipCreationPipeline(); gpu::doInBatch("AmbientOcclusionEffect::run", args->_context, [=](gpu::Batch& batch) { - batch.enableStereo(false); + PROFILE_RANGE_BATCH(batch, "AmbientOcclusion"); + batch.enableStereo(false); _gpuTimer->begin(batch); @@ -395,16 +412,18 @@ void AmbientOcclusionEffect::run(const render::RenderContextPointer& renderConte batch.resetViewTransform(); Transform model; - model.setTranslation(glm::vec3(sMin, tMin, 0.0f)); + + // We need this with the mips levels + batch.setModelTransform(model); + batch.setPipeline(mipCreationPipeline); + batch.generateTextureMipsWithPipeline(_framebuffer->getLinearDepthTexture()); + + model.setTranslation(glm::vec3(sMin, tMin, 0.0f)); model.setScale(glm::vec3(sWidth, tHeight, 1.0f)); batch.setModelTransform(model); batch.setUniformBuffer(AmbientOcclusionEffect_FrameTransformSlot, frameTransform->getFrameTransformBuffer()); batch.setUniformBuffer(AmbientOcclusionEffect_ParamsSlot, _parametersBuffer); - - - // We need this with the mips levels - batch.generateTextureMips(_framebuffer->getLinearDepthTexture()); // Occlusion pass batch.setFramebuffer(occlusionFBO); @@ -413,7 +432,6 @@ void AmbientOcclusionEffect::run(const render::RenderContextPointer& renderConte batch.setResourceTexture(AmbientOcclusionEffect_LinearDepthMapSlot, _framebuffer->getLinearDepthTexture()); batch.draw(gpu::TRIANGLE_STRIP, 4); - if (_parametersBuffer->getBlurRadius() > 0) { // Blur 1st pass batch.setFramebuffer(occlusionBlurredFBO); diff --git a/libraries/render-utils/src/AmbientOcclusionEffect.h b/libraries/render-utils/src/AmbientOcclusionEffect.h index 3643e608ed..99cf4b034c 100644 --- a/libraries/render-utils/src/AmbientOcclusionEffect.h +++ b/libraries/render-utils/src/AmbientOcclusionEffect.h @@ -159,13 +159,15 @@ private: ParametersBuffer _parametersBuffer; - const gpu::PipelinePointer& getOcclusionPipeline(); - const gpu::PipelinePointer& getHBlurPipeline(); // first - const gpu::PipelinePointer& getVBlurPipeline(); // second + static const gpu::PipelinePointer& getOcclusionPipeline(); + static const gpu::PipelinePointer& getHBlurPipeline(); // first + static const gpu::PipelinePointer& getVBlurPipeline(); // second + static const gpu::PipelinePointer& getMipCreationPipeline(); - gpu::PipelinePointer _occlusionPipeline; - gpu::PipelinePointer _hBlurPipeline; - gpu::PipelinePointer _vBlurPipeline; + static gpu::PipelinePointer _occlusionPipeline; + static gpu::PipelinePointer _hBlurPipeline; + static gpu::PipelinePointer _vBlurPipeline; + static gpu::PipelinePointer _mipCreationPipeline; AmbientOcclusionFramebufferPointer _framebuffer; diff --git a/libraries/render-utils/src/mip_depth_median.slf b/libraries/render-utils/src/mip_depth_median.slf new file mode 100644 index 0000000000..08813ebb5f --- /dev/null +++ b/libraries/render-utils/src/mip_depth_median.slf @@ -0,0 +1,35 @@ +<@include gpu/Config.slh@> +<$VERSION_HEADER$> +// Generated on <$_SCRIBE_DATE$> +// +// mip_depth_median.frag +// fragment shader +// +// Created by Olivier Prat on 4/18/18. +// Copyright 2018 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 +// + +uniform sampler2D depthTexture; + +in vec2 varTexCoord0; +out vec4 outFragColor; + +void main(void) { + vec4 depths = textureGather(depthTexture, varTexCoord0); + + // Order the depths from minimum to maximum + depths.xy = depths.x > depths.y ? depths.yx : depths.xy; + depths.xz = depths.x > depths.z ? depths.zx : depths.xz; + depths.xw = depths.x > depths.w ? depths.wx : depths.xw; + + depths.yz = depths.y > depths.z ? depths.zy : depths.yz; + depths.yw = depths.y > depths.w ? depths.wy : depths.yw; + + depths.zw = depths.z > depths.w ? depths.wz : depths.zw; + + float median = (depths.y + depths.z) / 2.0; + outFragColor = vec4(vec3(median), 1.0); +} From 0316df4faeb871e3273ab81ed56b052a73515a22 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Thu, 19 Apr 2018 15:03:39 +0200 Subject: [PATCH 004/286] Working generateMipWithPipeline command in batch --- libraries/render-utils/src/AmbientOcclusionEffect.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/libraries/render-utils/src/AmbientOcclusionEffect.cpp b/libraries/render-utils/src/AmbientOcclusionEffect.cpp index 7176135a0a..edd848cfc7 100644 --- a/libraries/render-utils/src/AmbientOcclusionEffect.cpp +++ b/libraries/render-utils/src/AmbientOcclusionEffect.cpp @@ -413,10 +413,12 @@ void AmbientOcclusionEffect::run(const render::RenderContextPointer& renderConte Transform model; - // We need this with the mips levels + // We need this with the mips levels + batch.pushProfileRange("Depth mip creation"); batch.setModelTransform(model); batch.setPipeline(mipCreationPipeline); batch.generateTextureMipsWithPipeline(_framebuffer->getLinearDepthTexture()); + batch.popProfileRange(); model.setTranslation(glm::vec3(sMin, tMin, 0.0f)); model.setScale(glm::vec3(sWidth, tHeight, 1.0f)); @@ -433,7 +435,8 @@ void AmbientOcclusionEffect::run(const render::RenderContextPointer& renderConte batch.draw(gpu::TRIANGLE_STRIP, 4); if (_parametersBuffer->getBlurRadius() > 0) { - // Blur 1st pass + PROFILE_RANGE_BATCH(batch, "Blur"); + // Blur 1st pass batch.setFramebuffer(occlusionBlurredFBO); batch.setPipeline(firstHBlurPipeline); batch.setResourceTexture(AmbientOcclusionEffect_OcclusionMapSlot, occlusionFBO->getRenderBuffer(0)); From 5dcb7f622a2761c16ea40a6eecaa28b5c872da91 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Thu, 19 Apr 2018 17:34:11 +0200 Subject: [PATCH 005/286] Fixed corrupted glViewport after call to generateMipWithPipeline --- libraries/gpu-gl-common/src/gpu/gl/GLBackendTexture.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/libraries/gpu-gl-common/src/gpu/gl/GLBackendTexture.cpp b/libraries/gpu-gl-common/src/gpu/gl/GLBackendTexture.cpp index 410c52a1f6..48ef49186f 100644 --- a/libraries/gpu-gl-common/src/gpu/gl/GLBackendTexture.cpp +++ b/libraries/gpu-gl-common/src/gpu/gl/GLBackendTexture.cpp @@ -118,6 +118,11 @@ void GLBackend::do_generateTextureMipsWithPipeline(const Batch& batch, size_t pa } glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, numMips-1); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0); + glBindFramebuffer(GL_FRAMEBUFFER, 0); resetOutputStage(); + // Restore viewport + ivec4& vp = _transform._viewport; + glViewport(vp.x, vp.y, vp.z, vp.w); } From 5e9355235ccbd4e6a832dc8354e37e27aa48e490 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Thu, 19 Apr 2018 17:34:28 +0200 Subject: [PATCH 006/286] First step for HBAO. Not working of course --- libraries/render-utils/src/ssao.slh | 147 ++++++++++++++---- .../render-utils/src/ssao_debugOcclusion.slf | 8 +- .../render-utils/src/ssao_makeOcclusion.slf | 70 ++++----- 3 files changed, 151 insertions(+), 74 deletions(-) diff --git a/libraries/render-utils/src/ssao.slh b/libraries/render-utils/src/ssao.slh index c0039265fa..262d6a77a2 100644 --- a/libraries/render-utils/src/ssao.slh +++ b/libraries/render-utils/src/ssao.slh @@ -11,11 +11,13 @@ <@if not SSAO_SLH@> <@def SSAO_SLH@> +#define SSAO_USE_HORIZON_BASED 1 + <@func declarePackOcclusionDepth()@> const float FAR_PLANE_Z = -300.0; -float CSZToDephtKey(float z) { +float CSZToDepthKey(float z) { return clamp(z * (1.0 / FAR_PLANE_Z), 0.0, 1.0); } vec3 packOcclusionDepth(float occlusion, float depth) { @@ -156,24 +158,25 @@ float evalDiskRadius(float Zeye, vec2 imageSize) { return ssDiskRadius; } -const float TWO_PI = 6.28; +const float PI = 3.1415926; +const float TWO_PI = 6.2831852; -vec3 getUnitTapLocation(int sampleNumber, float spinAngle){ +vec3 getUnitTapLocation(int sampleNumber, float spiralTurns, float spinAngle, float angleRange){ // Radius relative to ssR float alpha = float(sampleNumber + 0.5) * getInvNumSamples(); - float angle = alpha * (getNumSpiralTurns() * TWO_PI) + spinAngle; + float angle = alpha * (spiralTurns * angleRange) + spinAngle; return vec3(cos(angle), sin(angle), alpha); } -vec3 getTapLocation(int sampleNumber, float spinAngle, float outerRadius) { - vec3 tap = getUnitTapLocation(sampleNumber, spinAngle); +vec3 getTapLocationSSAO(int sampleNumber, float spinAngle, float outerRadius) { + vec3 tap = getUnitTapLocation(sampleNumber, getNumSpiralTurns(), spinAngle, TWO_PI); tap.xy *= tap.z; tap *= outerRadius; return tap; } -vec3 getTapLocationClamped(int sampleNumber, float spinAngle, float outerRadius, vec2 pixelPos, vec2 imageSize) { - vec3 tap = getTapLocation(sampleNumber, spinAngle, outerRadius); +vec3 getTapLocationClampedSSAO(int sampleNumber, float spinAngle, float outerRadius, vec2 pixelPos, vec2 imageSize) { + vec3 tap = getTapLocationSSAO(sampleNumber, spinAngle, outerRadius); vec2 tapPos = pixelPos + tap.xy; if (!(isBorderingEnabled() > 0.0)) { @@ -196,24 +199,7 @@ vec3 getTapLocationClamped(int sampleNumber, float spinAngle, float outerRadius, tapPos.y -= (imageSize.y - tapPos.y); redoTap = true; } -/* - if ((tapPos.x < 0.5)) { - tapPos.x = 0.5; - redoTap = true; - } else if ((tapPos.x > imageSize.x - 0.5)) { - tapPos.x = imageSize.x - 0.5; - redoTap = true; - } - if ((tapPos.y < 0.5)) { - tapPos.y = 0.5; - redoTap = true; - } else if ((tapPos.y > imageSize.y - 0.5)) { - tapPos.y = imageSize.y - 0.5; - redoTap = true; - } -*/ - if (redoTap) { tap.xy = tapPos - pixelPos; tap.z = length(tap.xy); @@ -228,14 +214,17 @@ vec3 getTapLocationClamped(int sampleNumber, float spinAngle, float outerRadius, <@func declareFetchDepthPyramidMap()@> - // the depth pyramid texture uniform sampler2D pyramidMap; -float getZEye(ivec2 pixel, int level) { +float getZEyeAtPixel(ivec2 pixel, int level) { return -texelFetch(pyramidMap, pixel, level).x; } +float getZEyeAtUV(vec2 texCoord, int level) { + return -texture(pyramidMap, texCoord, level).x; +} + const int LOG_MAX_OFFSET = 3; const int MAX_MIP_LEVEL = 5; int evalMipFromRadius(float radius) { @@ -243,7 +232,6 @@ int evalMipFromRadius(float radius) { return clamp(findMSB(int(radius)) - LOG_MAX_OFFSET, 0, MAX_MIP_LEVEL); } - vec3 fetchTapUnfiltered(ivec4 side, ivec2 ssC, vec3 tap, vec2 imageSize) { ivec2 ssP = ivec2(tap.xy) + ssC; ivec2 ssPFull = ivec2(ssP.x + side.y, ssP.y); @@ -292,10 +280,18 @@ vec3 fetchTap(ivec4 side, ivec2 ssC, vec3 tap, vec2 imageSize) { <@func declareEvalObscurance()@> -float evalAO(in vec3 C, in vec3 n_C, in vec3 Q) { - vec3 v = Q - C; +vec3 fastAcos(vec3 x) { + // [Eberly2014] GPGPU Programming for Games and Science + vec3 absX = abs(x); + vec3 res = absX * (-0.156583) + vec3(PI / 2.0); + res *= sqrt(vec3(1.0) - absX); + return mix(res, vec3(PI) - res, greaterThanEqual(x, vec3(0))); +} + +float evalVisibilitySSAO(in vec3 centerPosition, in vec3 centerNormal, in vec3 tapPosition) { + vec3 v = tapPosition - centerPosition; float vv = dot(v, v); - float vn = dot(v, n_C); + float vn = dot(v, centerNormal); // Fall off function as recommended in SAO paper const float epsilon = 0.01; @@ -303,6 +299,95 @@ float evalAO(in vec3 C, in vec3 n_C, in vec3 Q) { return f * f * f * max((vn - getFalloffBias()) / (epsilon + vv), 0.0); } +vec2 searchHorizons(int side, float centerDepth, vec2 centerPixelUV, vec2 imageSize, vec2 deltaTap, float ssDiskRadius, vec3 fragNormalES) { + vec2 searchVec = deltaTap * ssDiskRadius; + vec2 absSearchVec = abs(searchVec); + vec2 horizons = vec2(-PI/2.0, -PI/2.0); + vec3 centerPoint = vec3(centerPixelUV, centerDepth); + int stepIndex; + vec2 tapPixelUV; + + if (absSearchVec.x > absSearchVec.y) { + int stepCount = int(ceil(absSearchVec.x)); + + // Positive search for h2 + tapPixelUV = centerPixelUV; + deltaTap = (searchVec / searchVec.x) / imageSize; + stepIndex = stepCount; + while (stepIndex>0 && tapPixelUV.x < 1.0) { + tapPixelUV += deltaTap; + // Don't clamp for the moment + + float tapDepth = getZEyeAtUV(tapPixelUV, 0); + vec3 deltaVec = vec3(tapPixelUV, tapDepth) - centerPoint; + + horizons.y = max(horizons.y, normalize(deltaVec).z); + --stepIndex; + } + + // Negative search for h1 + tapPixelUV = centerPixelUV; + stepIndex = stepCount; + while (stepIndex>0 && tapPixelUV.x > 0.0) { + tapPixelUV -= deltaTap; + // Don't clamp for the moment + + float tapDepth = getZEyeAtUV(tapPixelUV, 0); + vec3 deltaVec = vec3(tapPixelUV, tapDepth) - centerPoint; + + horizons.x = max(horizons.x, normalize(deltaVec).z); + --stepIndex; + } + } else { + int stepCount = int(ceil(absSearchVec.y)); + + // Positive search for h2 + tapPixelUV = centerPixelUV; + deltaTap = (searchVec / searchVec.y) / imageSize; + stepIndex = stepCount; + while (stepIndex>0 && tapPixelUV.y < 1.0) { + tapPixelUV += deltaTap; + // Don't clamp for the moment + + float tapDepth = getZEyeAtUV(tapPixelUV, 0); + vec3 deltaVec = vec3(tapPixelUV, tapDepth) - centerPoint; + + horizons.y = max(horizons.y, normalize(deltaVec).z); + --stepIndex; + } + + // Negative search for h1 + tapPixelUV = centerPixelUV; + stepIndex = stepCount; + while (stepIndex>0 && tapPixelUV.y > 0.0) { + tapPixelUV -= deltaTap; + // Don't clamp for the moment + + float tapDepth = getZEyeAtUV(tapPixelUV, 0); + vec3 deltaVec = vec3(tapPixelUV, tapDepth) - centerPoint; + + horizons.x = max(horizons.x, normalize(deltaVec).z); + --stepIndex; + } + } + + vec3 angles = acos(vec3(horizons, fragNormalES.z))-PI/2.0; + angles.x = -angles.x; + // Clamp to limit horizon defined by normal plane + horizons.xy = angles.zz + max(angles.xy - angles.zz, vec2(-PI/2.0, PI/2.0)); + return horizons; +} + +float integrateArc(float h1, float h2) { + vec2 cosh = cos(vec2(h1, h2)); + return 2.0 - cosh.x - cosh.y; +} + +float evalVisibilityHBAO(int side, float centerDepth, vec2 centerPixelUV, vec2 imageSize, vec2 deltaTap, float ssDiskRadius, vec3 fragNormalES) { + vec2 horizonAngles = searchHorizons(side, centerDepth, centerPixelUV, imageSize, deltaTap, ssDiskRadius, fragNormalES); + return integrateArc(horizonAngles.x, horizonAngles.y); +} + <@endfunc@> <@func declareBlurPass(axis)@> diff --git a/libraries/render-utils/src/ssao_debugOcclusion.slf b/libraries/render-utils/src/ssao_debugOcclusion.slf index 6af457db67..1ba99f0aa9 100644 --- a/libraries/render-utils/src/ssao_debugOcclusion.slf +++ b/libraries/render-utils/src/ssao_debugOcclusion.slf @@ -50,7 +50,7 @@ void main(void) { ivec2 ssC = ivec2(cursorPixelPos); // Fetch the z under the pixel (stereo or not) - float Zeye = getZEye(ssC, 0); + float Zeye = getZEyeAtPixel(ssC, 0); // Stereo side info ivec4 side = getStereoSideInfo(ssC.x, getResolutionLevel()); @@ -84,7 +84,7 @@ void main(void) { bool keep = false; for (int i = 0; i < getNumSamples(); ++i) { - vec3 tap = getTapLocationClamped(i, randomPatternRotationAngle, ssDiskRadius, cursorPixelPos, imageSize); + vec3 tap = getTapLocationClampedSSAO(i, randomPatternRotationAngle, ssDiskRadius, cursorPixelPos, imageSize); // The occluding point in camera space vec2 fragToTap = vec2(ssC) + tap.xy - fragCoord.xy; @@ -97,7 +97,7 @@ void main(void) { vec3 Q = evalEyePositionFromZeye(side.x, tapUVZ.z, tapUVZ.xy); - sum += float(tap.z > 0.0) * evalAO(Cp, Cn, Q); + sum += float(tap.z > 0.0) * evalVisibilitySSAO(Cp, Cn, Q); } @@ -114,7 +114,7 @@ void main(void) { } !> - outFragColor = vec4(packOcclusionDepth(A, CSZToDephtKey(Cp.z)), 1.0); + outFragColor = vec4(packOcclusionDepth(A, CSZToDepthKey(Cp.z)), 1.0); if ((dot(fragToCursor,fragToCursor) < (100.0 * keepTapRadius * keepTapRadius) )) { // outFragColor = vec4(vec3(A), 1.0); diff --git a/libraries/render-utils/src/ssao_makeOcclusion.slf b/libraries/render-utils/src/ssao_makeOcclusion.slf index 4c808342c5..3919b3e1d1 100644 --- a/libraries/render-utils/src/ssao_makeOcclusion.slf +++ b/libraries/render-utils/src/ssao_makeOcclusion.slf @@ -24,63 +24,55 @@ void main(void) { // Pixel being shaded vec2 fragCoord = gl_FragCoord.xy; - ivec2 ssC = ivec2(fragCoord.xy); + ivec2 centerPixelPos = ivec2(fragCoord.xy); // Fetch the z under the pixel (stereo or not) - float Zeye = getZEye(ssC, 0); + float Zeye = getZEyeAtPixel(centerPixelPos, 0); // Stereo side info - ivec4 side = getStereoSideInfo(ssC.x, getResolutionLevel()); + ivec4 side = getStereoSideInfo(centerPixelPos.x, getResolutionLevel()); - // From now on, ssC is the pixel pos in the side - ssC.x -= side.y; - vec2 fragPos = (vec2(ssC) + vec2(0.5)) / imageSize; + // From now on, centerPixelPos is the pixel pos in the side + centerPixelPos.x -= side.y; + vec2 fragUVPos = (vec2(centerPixelPos) + vec2(0.5)) / imageSize; // The position and normal of the pixel fragment in Eye space - vec3 Cp = evalEyePositionFromZeye(side.x, Zeye, fragPos); - vec3 Cn = evalEyeNormal(Cp); + vec3 fragPositionES = evalEyePositionFromZeye(side.x, Zeye, fragUVPos); + vec3 fragNormalES = evalEyeNormal(fragPositionES); // Choose the screen-space sample radius - float ssDiskRadius = evalDiskRadius(Cp.z, imageSize); - + float ssDiskRadius = evalDiskRadius(fragPositionES.z, imageSize); +#if SSAO_USE_HORIZON_BASED + ssDiskRadius = min(ssDiskRadius, 3.0); +#endif // Let's make noise - float randomPatternRotationAngle = getAngleDithering(ssC); - //vec3 wCp = (getViewInverse() * vec4(Cp, 1.0)).xyz; - //float randomPatternRotationAngle = getAngleDitheringWorldPos(wCp); + float randomPatternRotationAngle = getAngleDithering(centerPixelPos); - // Accumulate the Obscurance for each samples - float sum = 0.0; + // Accumulate the visibility for each samples + float visibilitySum = 0.0; for (int i = 0; i < getNumSamples(); ++i) { - vec3 tap = getTapLocationClamped(i, randomPatternRotationAngle, ssDiskRadius, ssC, imageSize); - - vec3 tapUVZ = fetchTap(side, ssC, tap, imageSize); - - vec3 Q = evalEyePositionFromZeye(side.x, tapUVZ.z, tapUVZ.xy); - - sum += float(tap.z > 0.0) * evalAO(Cp, Cn, Q); +#if SSAO_USE_HORIZON_BASED + vec3 deltaTap = getUnitTapLocation(i, 1, randomPatternRotationAngle, PI); + visibilitySum += evalVisibilityHBAO(side.x, Zeye, fragUVPos, imageSize, deltaTap.xy, ssDiskRadius, fragNormalES); +#else + vec3 tap = getTapLocationClampedSSAO(i, randomPatternRotationAngle, ssDiskRadius, centerPixelPos, imageSize); + vec3 tapUVZ = fetchTap(side, centerPixelPos, tap, imageSize); + vec3 tapPositionES = evalEyePositionFromZeye(side.x, tapUVZ.z, tapUVZ.xy); + visibilitySum += float(tap.z > 0.0) * evalVisibilitySSAO(fragPositionES, fragNormalES, tapPositionES); +#endif } - float A = max(0.0, 1.0 - sum * getObscuranceScaling() * 5.0 * getInvNumSamples()); + float occlusion = max(0.0, 1.0 - visibilitySum * getObscuranceScaling() * 5.0 * getInvNumSamples()); // KEEP IT for Debugging // Bilateral box-filter over a quad for free, respecting depth edges // (the difference that this makes is subtle) - if (abs(dFdx(Cp.z)) < 0.02) { - A -= dFdx(A) * ((ssC.x & 1) - 0.5); +/* if (abs(dFdx(fragPositionES.z)) < 0.02) { + occlusion -= dFdx(occlusion) * ((centerPixelPos.x & 1) - 0.5); } - if (abs(dFdy(Cp.z)) < 0.02) { - A -= dFdy(A) * ((ssC.y & 1) - 0.5); - } - - - outFragColor = vec4(packOcclusionDepth(A, CSZToDephtKey(Cp.z)), 1.0); - - /* { - vec3 tap = getTapLocationClamped(2, randomPatternRotationAngle, ssDiskRadius, ssC, imageSize); - vec3 tapUVZ = fetchTap(side, ssC, tap, imageSize); - vec2 fetchUV = vec2(tapUVZ.x + side.w * 0.5 * (side.x - tapUVZ.x), tapUVZ.y); - vec3 Q = evalEyePositionFromZeye(side.x, tapUVZ.z, tapUVZ.xy); - outFragColor = vec4(fetchUV, 0.0, 1.0); + if (abs(dFdy(fragPositionES.z)) < 0.02) { + occlusion -= dFdy(occlusion) * ((centerPixelPos.y & 1) - 0.5); }*/ - + + outFragColor = vec4(packOcclusionDepth(occlusion, CSZToDepthKey(fragPositionES.z)), 1.0); } From 3497c93e9a36daf5fcfc89888949e70c0fc94295 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Thu, 19 Apr 2018 18:02:31 +0200 Subject: [PATCH 007/286] Updated ambient occlusion debug script to be a tablet app --- .../utilities/render/ambientOcclusionPass.qml | 21 ++---- .../render/debugAmbientOcclusionPass.js | 67 ++++++++----------- 2 files changed, 32 insertions(+), 56 deletions(-) diff --git a/scripts/developer/utilities/render/ambientOcclusionPass.qml b/scripts/developer/utilities/render/ambientOcclusionPass.qml index b661f992f1..179fc8341c 100644 --- a/scripts/developer/utilities/render/ambientOcclusionPass.qml +++ b/scripts/developer/utilities/render/ambientOcclusionPass.qml @@ -19,16 +19,16 @@ import "../lib/plotperf" Rectangle { HifiConstants { id: hifi;} - id: render; + id: root; anchors.margins: hifi.dimensions.contentMargin.x color: hifi.colors.baseGray; - + Column { id: surfaceGeometry - spacing: 10 + spacing: 8 anchors.left: parent.left - anchors.right: parent.right + anchors.right: parent.right anchors.margins: hifi.dimensions.contentMargin.x Repeater { @@ -48,19 +48,13 @@ Rectangle { property: modelData.split(":")[1] max: modelData.split(":")[2] min: 0.0 - width: 280 height:38 } } - Row{ + Row { spacing: 10 - anchors.left: parent.left - anchors.right: parent.right - Column { - spacing: 10 - Repeater { model: [ "resolutionLevel:resolutionLevel", @@ -76,10 +70,7 @@ Rectangle { } } } - Column { - spacing: 10 - Repeater { model: [ "debugEnabled:showCursorPixel" @@ -95,8 +86,6 @@ Rectangle { } PlotPerf { - anchors.left: parent.left - anchors.right: parent.right title: "Timing" height: 50 object: Render.getConfig("RenderMainView.AmbientOcclusion") diff --git a/scripts/developer/utilities/render/debugAmbientOcclusionPass.js b/scripts/developer/utilities/render/debugAmbientOcclusionPass.js index 776fc1b600..409473511e 100644 --- a/scripts/developer/utilities/render/debugAmbientOcclusionPass.js +++ b/scripts/developer/utilities/render/debugAmbientOcclusionPass.js @@ -1,30 +1,25 @@ +"use strict"; + // // debugAmbientOcclusionPass.js -// developer/utilities/render +// tablet-sample-app // -// Olivier Prat, created on 11/04/2018. -// Copyright 2017 High Fidelity, Inc. +// Created by Olivier Prat on April 19 2018. +// Copyright 2018 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() { - "use strict"; - - var TABLET_BUTTON_NAME = "SSAO"; + var TABLET_BUTTON_NAME = "AO"; var QMLAPP_URL = Script.resolvePath("./ambientOcclusionPass.qml"); - var ICON_URL = Script.resolvePath("../../../system/assets/images/ssao-i.svg"); - var ACTIVE_ICON_URL = Script.resolvePath("../../../system/assets/images/ssao-a.svg"); - Script.include([ - Script.resolvePath("../../../system/libraries/stringHelpers.js"), - ]); - - var onScreen = false; + + var onLuciScreen = false; function onClicked() { - if (onScreen) { + if (onLuciScreen) { tablet.gotoHomeScreen(); } else { tablet.loadQMLSource(QMLAPP_URL); @@ -33,9 +28,8 @@ var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); var button = tablet.addButton({ - text: TABLET_BUTTON_NAME, - icon: ICON_URL, - activeIcon: ACTIVE_ICON_URL + text: TABLET_BUTTON_NAME, + sortOrder: 1 }); var hasEventBridge = false; @@ -60,13 +54,13 @@ function onScreenChanged(type, url) { if (url === QMLAPP_URL) { - onScreen = true; + onLuciScreen = true; } else { - onScreen = false; + onLuciScreen = false; } - button.editProperties({isActive: onScreen}); - wireEventBridge(onScreen); + button.editProperties({isActive: onLuciScreen}); + wireEventBridge(onLuciScreen); } function fromQml(message) { @@ -74,18 +68,7 @@ button.clicked.connect(onClicked); tablet.screenChanged.connect(onScreenChanged); - - Script.scriptEnding.connect(function () { - if (onScreen) { - tablet.gotoHomeScreen(); - } - button.clicked.disconnect(onClicked); - tablet.screenChanged.disconnect(onScreenChanged); - tablet.removeButton(button); - }); - - /* var moveDebugCursor = false; Controller.mousePressEvent.connect(function (e) { if (e.isMiddleButton) { @@ -97,16 +80,20 @@ Controller.mouseMoveEvent.connect(function (e) { if (moveDebugCursor) setDebugCursor(e.x, e.y); }); + Script.scriptEnding.connect(function () { + if (onLuciScreen) { + tablet.gotoHomeScreen(); + } + button.clicked.disconnect(onClicked); + tablet.screenChanged.disconnect(onScreenChanged); + tablet.removeButton(button); + }); + function setDebugCursor(x, y) { - nx = (x / Window.innerWidth); - ny = 1.0 - ((y) / (Window.innerHeight - 32)); + nx = ((x + 0.5) / Window.innerWidth); + ny = 1.0 - ((y + 0.5) / (Window.innerHeight)); - Render.getConfig("RenderMainView").getConfig("DebugAmbientOcclusion").debugCursorTexcoord = { x: nx, y: ny }; + Render.getConfig("RenderMainView").getConfig("DebugAmbientOcclusion").debugCursorTexcoord = { x: nx, y: ny }; } - */ - function cleanup() { - } - Script.scriptEnding.connect(cleanup); }()); - From c167a711290bbe0f5e123f2d50a7649356ee1941 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Thu, 6 Sep 2018 13:56:58 +0200 Subject: [PATCH 008/286] Switched back to normal SSAO and fixed broken ambient occlusion Luci visualization --- libraries/render-utils/src/DebugDeferredBuffer.cpp | 4 +--- libraries/render-utils/src/ssao.slh | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/libraries/render-utils/src/DebugDeferredBuffer.cpp b/libraries/render-utils/src/DebugDeferredBuffer.cpp index 0e61b36280..848c10597a 100644 --- a/libraries/render-utils/src/DebugDeferredBuffer.cpp +++ b/libraries/render-utils/src/DebugDeferredBuffer.cpp @@ -220,9 +220,7 @@ static const std::string DEFAULT_DEBUG_SCATTERING_SHADER{ static const std::string DEFAULT_AMBIENT_OCCLUSION_SHADER{ "vec4 getFragmentColor() {" - " return vec4(vec3(texture(obscuranceMap, uv).x), 1.0);" - // When drawing color " return vec4(vec3(texture(debugTexture0, uv).xyz), 1.0);" - // when drawing normal" return vec4(normalize(texture(debugTexture0, uv).xyz * 2.0 - vec3(1.0)), 1.0);" + " return vec4(vec3(texture(debugTexture0, uv).x), 1.0);" " }" }; static const std::string DEFAULT_AMBIENT_OCCLUSION_BLURRED_SHADER{ diff --git a/libraries/render-utils/src/ssao.slh b/libraries/render-utils/src/ssao.slh index 89aa220e72..da8466ccdb 100644 --- a/libraries/render-utils/src/ssao.slh +++ b/libraries/render-utils/src/ssao.slh @@ -11,7 +11,7 @@ <@if not SSAO_SLH@> <@def SSAO_SLH@> -#define SSAO_USE_HORIZON_BASED 1 +#define SSAO_USE_HORIZON_BASED 0 <@include render-utils/ShaderConstants.h@> From 7a736043a6bdad0ede2dbfdfbd4a9cb3c5b3eaa4 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Thu, 6 Sep 2018 17:08:10 +0200 Subject: [PATCH 009/286] Small changes --- .../render-utils/src/AmbientOcclusionEffect.cpp | 12 +----------- libraries/render-utils/src/ssao.slh | 2 +- libraries/render-utils/src/ssao_makeOcclusion.slf | 2 +- 3 files changed, 3 insertions(+), 13 deletions(-) diff --git a/libraries/render-utils/src/AmbientOcclusionEffect.cpp b/libraries/render-utils/src/AmbientOcclusionEffect.cpp index e8eb291e2c..1bc7ec2c22 100644 --- a/libraries/render-utils/src/AmbientOcclusionEffect.cpp +++ b/libraries/render-utils/src/AmbientOcclusionEffect.cpp @@ -186,7 +186,7 @@ void AmbientOcclusionEffect::configure(const Config& config) { if (config.obscuranceLevel != _parametersBuffer->getObscuranceLevel()) { auto& current = _parametersBuffer.edit().radiusInfo; - current.w = config.obscuranceLevel; + current.w = config.obscuranceLevel * 10.0; } if (config.falloffBias != _parametersBuffer->getFalloffBias()) { @@ -343,12 +343,6 @@ void AmbientOcclusionEffect::run(const render::RenderContextPointer& renderConte auto framebufferSize = _framebuffer->getSourceFrameSize(); - float sMin = occlusionViewport.x / (float)framebufferSize.x; - float sWidth = occlusionViewport.z / (float)framebufferSize.x; - float tMin = occlusionViewport.y / (float)framebufferSize.y; - float tHeight = occlusionViewport.w / (float)framebufferSize.y; - - auto occlusionPipeline = getOcclusionPipeline(); auto firstHBlurPipeline = getHBlurPipeline(); auto lastVBlurPipeline = getVBlurPipeline(); @@ -373,10 +367,6 @@ void AmbientOcclusionEffect::run(const render::RenderContextPointer& renderConte batch.generateTextureMipsWithPipeline(_framebuffer->getLinearDepthTexture()); batch.popProfileRange(); - model.setTranslation(glm::vec3(sMin, tMin, 0.0f)); - model.setScale(glm::vec3(sWidth, tHeight, 1.0f)); - batch.setModelTransform(model); - batch.setUniformBuffer(render_utils::slot::buffer::DeferredFrameTransform, frameTransform->getFrameTransformBuffer()); batch.setUniformBuffer(render_utils::slot::buffer::SsaoParams, _parametersBuffer); diff --git a/libraries/render-utils/src/ssao.slh b/libraries/render-utils/src/ssao.slh index da8466ccdb..7b2aa66d0d 100644 --- a/libraries/render-utils/src/ssao.slh +++ b/libraries/render-utils/src/ssao.slh @@ -412,7 +412,7 @@ vec2 fetchOcclusionDepth(ivec2 coords) { const int RADIUS_SCALE = 1; const float BLUR_WEIGHT_OFFSET = 0.05; -const float BLUR_EDGE_SCALE = 2000.0; +const float BLUR_EDGE_SCALE = 5000.0; vec2 evalTapWeightedValue(ivec3 side, int r, ivec2 ssC, float key) { ivec2 tapOffset = <$axis$> * (r * RADIUS_SCALE); diff --git a/libraries/render-utils/src/ssao_makeOcclusion.slf b/libraries/render-utils/src/ssao_makeOcclusion.slf index 0a92ba7fbe..9b9380640b 100644 --- a/libraries/render-utils/src/ssao_makeOcclusion.slf +++ b/libraries/render-utils/src/ssao_makeOcclusion.slf @@ -64,7 +64,7 @@ void main(void) { #endif } - float occlusion = max(0.0, 1.0 - visibilitySum * getObscuranceScaling() * 5.0 * getInvNumSamples()); + float occlusion = clamp(1.0 - visibilitySum * getObscuranceScaling() * getInvNumSamples(), 0.0, 1.0); // KEEP IT for Debugging // Bilateral box-filter over a quad for free, respecting depth edges From fec59e8b957a8e601d03b4566a423399c168690f Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Fri, 7 Sep 2018 16:12:03 +0200 Subject: [PATCH 010/286] WIP HBAO --- .../src/AmbientOcclusionEffect.cpp | 15 +- libraries/render-utils/src/ssao.slh | 193 ++++++++++-------- .../render-utils/src/ssao_makeOcclusion.slf | 17 +- 3 files changed, 136 insertions(+), 89 deletions(-) diff --git a/libraries/render-utils/src/AmbientOcclusionEffect.cpp b/libraries/render-utils/src/AmbientOcclusionEffect.cpp index 1bc7ec2c22..12b8c62556 100644 --- a/libraries/render-utils/src/AmbientOcclusionEffect.cpp +++ b/libraries/render-utils/src/AmbientOcclusionEffect.cpp @@ -30,6 +30,9 @@ #include "DependencyManager.h" #include "ViewFrustum.h" +// Should match value in ssao_makeOcclusion.slf +#define SSAO_USE_HORIZON_BASED 1 + gpu::PipelinePointer AmbientOcclusionEffect::_occlusionPipeline; gpu::PipelinePointer AmbientOcclusionEffect::_hBlurPipeline; gpu::PipelinePointer AmbientOcclusionEffect::_vBlurPipeline; @@ -346,7 +349,9 @@ void AmbientOcclusionEffect::run(const render::RenderContextPointer& renderConte auto occlusionPipeline = getOcclusionPipeline(); auto firstHBlurPipeline = getHBlurPipeline(); auto lastVBlurPipeline = getVBlurPipeline(); - auto mipCreationPipeline = getMipCreationPipeline(); +#if SSAO_USE_HORIZON_BASED + auto mipCreationPipeline = getMipCreationPipeline(); +#endif gpu::doInBatch("AmbientOcclusionEffect::run", args->_context, [=](gpu::Batch& batch) { PROFILE_RANGE_BATCH(batch, "AmbientOcclusion"); @@ -363,8 +368,12 @@ void AmbientOcclusionEffect::run(const render::RenderContextPointer& renderConte // We need this with the mips levels batch.pushProfileRange("Depth mip creation"); batch.setModelTransform(model); - batch.setPipeline(mipCreationPipeline); - batch.generateTextureMipsWithPipeline(_framebuffer->getLinearDepthTexture()); +#if SSAO_USE_HORIZON_BASED + batch.setPipeline(mipCreationPipeline); + batch.generateTextureMipsWithPipeline(_framebuffer->getLinearDepthTexture()); +#else + batch.generateTextureMips(_framebuffer->getLinearDepthTexture()); +#endif batch.popProfileRange(); batch.setUniformBuffer(render_utils::slot::buffer::DeferredFrameTransform, frameTransform->getFrameTransformBuffer()); diff --git a/libraries/render-utils/src/ssao.slh b/libraries/render-utils/src/ssao.slh index 7b2aa66d0d..13594c68f3 100644 --- a/libraries/render-utils/src/ssao.slh +++ b/libraries/render-utils/src/ssao.slh @@ -11,8 +11,6 @@ <@if not SSAO_SLH@> <@def SSAO_SLH@> -#define SSAO_USE_HORIZON_BASED 0 - <@include render-utils/ShaderConstants.h@> <@func declarePackOcclusionDepth()@> @@ -253,23 +251,15 @@ vec3 fetchTapUnfiltered(ivec4 side, ivec2 ssC, vec3 tap, vec2 imageSize) { vec3 fetchTap(ivec4 side, ivec2 ssC, vec3 tap, vec2 imageSize) { int mipLevel = evalMipFromRadius(tap.z * doFetchMips()); - ivec2 ssP = ivec2(tap.xy) + ssC; - ivec2 ssPFull = ivec2(ssP.x + side.y, ssP.y); + vec2 ssP = tap.xy + vec2(ssC); // We need to divide by 2^mipLevel to read the appropriately scaled coordinate from a MIP-map. // Manually clamp to the texture size because texelFetch bypasses the texture unit - // ivec2 mipSize = textureSize(pyramidMap, mipLevel); - ivec2 mipSize = max(ivec2(imageSize) >> mipLevel, ivec2(1)); - - ivec2 mipP = clamp(ssPFull >> mipLevel, ivec2(0), mipSize - ivec2(1)); - vec2 tapUV = (vec2(ssP) + vec2(0.5)) / imageSize; vec2 fetchUV = vec2(tapUV.x + side.w * 0.5 * (side.x - tapUV.x), tapUV.y); - // vec2 tapUV = (vec2(mipP) + vec2(0.5)) / vec2(mipSize); vec3 P; P.xy = tapUV; - // P.z = -texelFetch(pyramidMap, mipP, mipLevel).x; P.z = -textureLod(pyramidMap, fetchUV, float(mipLevel)).x; return P; @@ -295,88 +285,126 @@ float evalVisibilitySSAO(in vec3 centerPosition, in vec3 centerNormal, in vec3 t float vv = dot(v, v); float vn = dot(v, centerNormal); - // Fall off function as recommended in SAO paper + // Fall off function as recommended in SSAO paper const float epsilon = 0.01; float f = max(getRadius2() - vv, 0.0); return f * f * f * max((vn - getFalloffBias()) / (epsilon + vv), 0.0); } -vec2 searchHorizons(int side, float centerDepth, vec2 centerPixelUV, vec2 imageSize, vec2 deltaTap, float ssDiskRadius, vec3 fragNormalES) { +<@func updateHorizon(horizon, deltaPixelTap)@> + { + int stepIndex; + vec2 tapPixelPos = vec2(0); + float radius = 0.0; + + for (stepIndex=stepCount ; stepIndex>0 ; stepIndex--) { + tapPixelPos += deltaPixelTap; + radius += deltaRadius; + + vec3 tap = vec3(tapPixelPos, radius); + vec3 tapUVZ = fetchTap(side, centerPixelPos, tap, imageSize); + vec3 tapPositionES = evalEyePositionFromZeye(side.x, tapUVZ.z, tapUVZ.xy); + vec3 deltaVec = normalize(tapPositionES - fragPositionES); + + <$horizon$> = max(<$horizon$>, dot(deltaVec, fragNormalES)); + } + } +<@endfunc@> + +<@func searchBresenhamHorizon(deltaPixelCoord)@> + { + float epsilon = 1e-8; + vec2 deltaPixelTap = searchVec / absSearchVec.<$deltaPixelCoord$>; + vec2 absDeltaPixelTap = abs(deltaPixelTap); + bvec2 nullDelta = absDeltaPixelTap < epsilon; + + pixelDelta1 = mix(pixelDelta1, vec2(1.0), pixelDelta1 < epsilon && nullDelta); + pixelDelta2 = mix(pixelDelta2, vec2(1.0), pixelDelta2 < epsilon && nullDelta); + + pixelDelta1 = ceil(pixelDelta1 / absDeltaPixelTap); + pixelDelta2 = ceil(pixelDelta2 / absDeltaPixelTap); + + int maxStepCount = max(0, int(ceil(absSearchVec.<$deltaPixelCoord$>))); + float deltaRadius = ssDiskRadius / maxStepCount; + int stepCount; + + // Forward search for h1 + stepCount = clamp(int(min(pixelDelta1.x, pixelDelta1.y)), 0, maxStepCount); + <$updateHorizon(horizons.x, deltaPixelTap)$> + + // Backward search for h2 + stepCount = clamp(int(min(pixelDelta2.x, pixelDelta2.y)), 0, maxStepCount); + <$updateHorizon(horizons.y, -deltaPixelTap)$> + } +<@endfunc@> + +vec2 clampSearchVec(vec2 imageSize, vec2 centerPixelPos, vec2 searchVec) { + vec2 clampdSearchVec = searchVec; + vec2 endPixel = centerPixelPos + clampdSearchVec; + + if (endPixel.x < 0) { + clampdSearchVec = clampdSearchVec * ((0-centerPixelPos.x) / clampdSearchVec.x); + endPixel = centerPixelPos + clampdSearchVec; + } + if (endPixel.x >= imageSize.x) { + clampdSearchVec = clampdSearchVec * ((imageSize.x-1-centerPixelPos.x) / clampdSearchVec.x); + endPixel = centerPixelPos + clampdSearchVec; + } + if (endPixel.y < 0) { + clampdSearchVec = clampdSearchVec * ((0-centerPixelPos.y) / clampdSearchVec.y); + endPixel = centerPixelPos + clampdSearchVec; + } + if (endPixel.y >= imageSize.y) { + clampdSearchVec = clampdSearchVec * ((imageSize.y-1-centerPixelPos.y) / clampdSearchVec.y); + } + + return clampdSearchVec; +} + +vec2 searchHorizons(ivec4 side, ivec2 centerPixelPos, vec2 imageSize, vec2 deltaTap, float ssDiskRadius, + vec3 fragPositionES, vec3 fragNormalES) { vec2 searchVec = deltaTap * ssDiskRadius; - vec2 absSearchVec = abs(searchVec); - vec2 horizons = vec2(-PI/2.0, -PI/2.0); - vec3 centerPoint = vec3(centerPixelUV, centerDepth); - int stepIndex; - vec2 tapPixelUV; + vec2 horizons = vec2(0.0); + // Forward search for h1 + vec2 clampedSearchVec = clampSearchVec(imageSize, vec2(centerPixelPos), searchVec); + int stepCount = int(floor(length(clampedSearchVec)+0.5)); + if (stepCount>0) { + vec2 deltaPixelTap = clampedSearchVec / float(stepCount); + float deltaRadius = length(deltaPixelTap); + <$updateHorizon(horizons.x, deltaPixelTap)$> + } + // Backward search for h2 + clampedSearchVec = clampSearchVec(imageSize, vec2(centerPixelPos), -searchVec); + stepCount = int(floor(length(clampedSearchVec)+0.5)); + if (stepCount>0) { + vec2 deltaPixelTap = clampedSearchVec / float(stepCount); + float deltaRadius = length(deltaPixelTap); + <$updateHorizon(horizons.y, deltaPixelTap)$> + } + +// +// absSearchVec.y) { - int stepCount = int(ceil(absSearchVec.x)); - - // Positive search for h2 - tapPixelUV = centerPixelUV; - deltaTap = (searchVec / searchVec.x) / imageSize; - stepIndex = stepCount; - while (stepIndex>0 && tapPixelUV.x < 1.0) { - tapPixelUV += deltaTap; - // Don't clamp for the moment - - float tapDepth = getZEyeAtUV(tapPixelUV, 0); - vec3 deltaVec = vec3(tapPixelUV, tapDepth) - centerPoint; - - horizons.y = max(horizons.y, normalize(deltaVec).z); - --stepIndex; - } - - // Negative search for h1 - tapPixelUV = centerPixelUV; - stepIndex = stepCount; - while (stepIndex>0 && tapPixelUV.x > 0.0) { - tapPixelUV -= deltaTap; - // Don't clamp for the moment - - float tapDepth = getZEyeAtUV(tapPixelUV, 0); - vec3 deltaVec = vec3(tapPixelUV, tapDepth) - centerPoint; - - horizons.x = max(horizons.x, normalize(deltaVec).z); - --stepIndex; - } + <$searchBresenhamHorizon(x)$> } else { - int stepCount = int(ceil(absSearchVec.y)); - - // Positive search for h2 - tapPixelUV = centerPixelUV; - deltaTap = (searchVec / searchVec.y) / imageSize; - stepIndex = stepCount; - while (stepIndex>0 && tapPixelUV.y < 1.0) { - tapPixelUV += deltaTap; - // Don't clamp for the moment - - float tapDepth = getZEyeAtUV(tapPixelUV, 0); - vec3 deltaVec = vec3(tapPixelUV, tapDepth) - centerPoint; - - horizons.y = max(horizons.y, normalize(deltaVec).z); - --stepIndex; - } - - // Negative search for h1 - tapPixelUV = centerPixelUV; - stepIndex = stepCount; - while (stepIndex>0 && tapPixelUV.y > 0.0) { - tapPixelUV -= deltaTap; - // Don't clamp for the moment - - float tapDepth = getZEyeAtUV(tapPixelUV, 0); - vec3 deltaVec = vec3(tapPixelUV, tapDepth) - centerPoint; - - horizons.x = max(horizons.x, normalize(deltaVec).z); - --stepIndex; - } + <$searchBresenhamHorizon(y)$> } - vec3 angles = acos(vec3(horizons, fragNormalES.z))-PI/2.0; + vec3 angles = acos(vec3(horizons, fragNormalES.z)) - PI/2.0; angles.x = -angles.x; // Clamp to limit horizon defined by normal plane horizons.xy = angles.zz + max(angles.xy - angles.zz, vec2(-PI/2.0, PI/2.0)); + */ + +// +// !> +// + return horizons; } @@ -385,9 +413,10 @@ float integrateArc(float h1, float h2) { return 2.0 - cosh.x - cosh.y; } -float evalVisibilityHBAO(int side, float centerDepth, vec2 centerPixelUV, vec2 imageSize, vec2 deltaTap, float ssDiskRadius, vec3 fragNormalES) { - vec2 horizonAngles = searchHorizons(side, centerDepth, centerPixelUV, imageSize, deltaTap, ssDiskRadius, fragNormalES); - return integrateArc(horizonAngles.x, horizonAngles.y); +float evalVisibilityHBAO(ivec4 side, ivec2 centerPixelPos, vec2 imageSize, vec2 deltaTap, float ssDiskRadius, + vec3 fragPositionES, vec3 fragNormalES) { + vec2 horizonAngles = searchHorizons(side, centerPixelPos, imageSize, deltaTap, ssDiskRadius, fragPositionES, fragNormalES); + return min(1.0, integrateArc(horizonAngles.x, horizonAngles.y) * 0.5); } <@endfunc@> diff --git a/libraries/render-utils/src/ssao_makeOcclusion.slf b/libraries/render-utils/src/ssao_makeOcclusion.slf index 9b9380640b..c5d680e0d8 100644 --- a/libraries/render-utils/src/ssao_makeOcclusion.slf +++ b/libraries/render-utils/src/ssao_makeOcclusion.slf @@ -19,6 +19,8 @@ <$declarePackOcclusionDepth()$> +#define SSAO_USE_HORIZON_BASED 1 + layout(location=0) out vec4 outFragColor; void main(void) { @@ -52,10 +54,11 @@ void main(void) { // Accumulate the visibility for each samples float visibilitySum = 0.0; - for (int i = 0; i < getNumSamples(); ++i) { + int numSamples = 1; // TEMPO OP getNumSamples() + for (int i = 0; i < numSamples; ++i) { #if SSAO_USE_HORIZON_BASED - vec3 deltaTap = getUnitTapLocation(i, 1, randomPatternRotationAngle, PI); - visibilitySum += evalVisibilityHBAO(side.x, Zeye, fragUVPos, imageSize, deltaTap.xy, ssDiskRadius, fragNormalES); + vec3 deltaTap = getUnitTapLocation(i, 1.0, randomPatternRotationAngle, PI); + visibilitySum += evalVisibilityHBAO(side, centerPixelPos, imageSize, deltaTap.xy, ssDiskRadius, fragPositionES, fragNormalES); #else vec3 tap = getTapLocationClampedSSAO(i, randomPatternRotationAngle, ssDiskRadius, centerPixelPos, imageSize); vec3 tapUVZ = fetchTap(side, centerPixelPos, tap, imageSize); @@ -64,7 +67,12 @@ void main(void) { #endif } - float occlusion = clamp(1.0 - visibilitySum * getObscuranceScaling() * getInvNumSamples(), 0.0, 1.0); +#if SSAO_USE_HORIZON_BASED + visibilitySum = 1.0 - visibilitySum * getInvNumSamples(); +#else + visibilitySum = visibilitySum * getInvNumSamples(); +#endif + float occlusion = clamp(1.0 - visibilitySum * getObscuranceScaling(), 0.0, 1.0); // KEEP IT for Debugging // Bilateral box-filter over a quad for free, respecting depth edges @@ -77,4 +85,5 @@ void main(void) { }*/ outFragColor = vec4(packOcclusionDepth(occlusion, CSZToDepthKey(fragPositionES.z)), 1.0); + } From fd9d05bdbfe9e239d59ad60a08cfd2fde44a561e Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Tue, 11 Sep 2018 15:30:18 +0200 Subject: [PATCH 011/286] First 'working' version of HBAO --- libraries/render-utils/src/ssao.slh | 9 ++------- libraries/render-utils/src/ssao_makeOcclusion.slf | 7 +------ 2 files changed, 3 insertions(+), 13 deletions(-) diff --git a/libraries/render-utils/src/ssao.slh b/libraries/render-utils/src/ssao.slh index 5c70174f8a..8e1cb27346 100644 --- a/libraries/render-utils/src/ssao.slh +++ b/libraries/render-utils/src/ssao.slh @@ -408,15 +408,10 @@ vec2 searchHorizons(ivec4 side, ivec2 centerPixelPos, vec2 imageSize, vec2 delta return horizons; } -float integrateArc(float h1, float h2) { - vec2 cosh = cos(vec2(h1, h2)); - return 2.0 - cosh.x - cosh.y; -} - float evalVisibilityHBAO(ivec4 side, ivec2 centerPixelPos, vec2 imageSize, vec2 deltaTap, float ssDiskRadius, vec3 fragPositionES, vec3 fragNormalES) { - vec2 horizonAngles = searchHorizons(side, centerPixelPos, imageSize, deltaTap, ssDiskRadius, fragPositionES, fragNormalES); - return min(1.0, integrateArc(horizonAngles.x, horizonAngles.y) * 0.5); + vec2 horizons = searchHorizons(side, centerPixelPos, imageSize, deltaTap, ssDiskRadius, fragPositionES, fragNormalES); + return (horizons.x + horizons.y) * 0.5; } <@endfunc@> diff --git a/libraries/render-utils/src/ssao_makeOcclusion.slf b/libraries/render-utils/src/ssao_makeOcclusion.slf index c5d680e0d8..89f2aadfab 100644 --- a/libraries/render-utils/src/ssao_makeOcclusion.slf +++ b/libraries/render-utils/src/ssao_makeOcclusion.slf @@ -54,7 +54,7 @@ void main(void) { // Accumulate the visibility for each samples float visibilitySum = 0.0; - int numSamples = 1; // TEMPO OP getNumSamples() + int numSamples = int(getNumSamples()); for (int i = 0; i < numSamples; ++i) { #if SSAO_USE_HORIZON_BASED vec3 deltaTap = getUnitTapLocation(i, 1.0, randomPatternRotationAngle, PI); @@ -67,11 +67,7 @@ void main(void) { #endif } -#if SSAO_USE_HORIZON_BASED - visibilitySum = 1.0 - visibilitySum * getInvNumSamples(); -#else visibilitySum = visibilitySum * getInvNumSamples(); -#endif float occlusion = clamp(1.0 - visibilitySum * getObscuranceScaling(), 0.0, 1.0); // KEEP IT for Debugging @@ -85,5 +81,4 @@ void main(void) { }*/ outFragColor = vec4(packOcclusionDepth(occlusion, CSZToDepthKey(fragPositionES.z)), 1.0); - } From 6c0309402f761a8f704e53b44a124e23f636c509 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Tue, 11 Sep 2018 16:14:54 +0200 Subject: [PATCH 012/286] Some fixes for HBAO. Need performance improvements --- .../render-utils/src/AmbientOcclusionEffect.cpp | 7 +++++-- libraries/render-utils/src/ssao.slh | 13 +++++++++---- libraries/render-utils/src/ssao_makeOcclusion.slf | 4 +--- 3 files changed, 15 insertions(+), 9 deletions(-) diff --git a/libraries/render-utils/src/AmbientOcclusionEffect.cpp b/libraries/render-utils/src/AmbientOcclusionEffect.cpp index 12b8c62556..0cc5220abf 100644 --- a/libraries/render-utils/src/AmbientOcclusionEffect.cpp +++ b/libraries/render-utils/src/AmbientOcclusionEffect.cpp @@ -184,12 +184,15 @@ void AmbientOcclusionEffect::configure(const Config& config) { auto& current = _parametersBuffer.edit().radiusInfo; current.x = radius; current.y = radius * radius; - current.z = (float)(1.0 / pow((double)radius, RADIUS_POWER)); + current.z = 10.0f; +#if !SSAO_USE_HORIZON_BASED + current.z *= (float)(1.0 / pow((double)radius, RADIUS_POWER)); +#endif } if (config.obscuranceLevel != _parametersBuffer->getObscuranceLevel()) { auto& current = _parametersBuffer.edit().radiusInfo; - current.w = config.obscuranceLevel * 10.0; + current.w = config.obscuranceLevel; } if (config.falloffBias != _parametersBuffer->getFalloffBias()) { diff --git a/libraries/render-utils/src/ssao.slh b/libraries/render-utils/src/ssao.slh index 8e1cb27346..b4dd7e6b6c 100644 --- a/libraries/render-utils/src/ssao.slh +++ b/libraries/render-utils/src/ssao.slh @@ -305,8 +305,11 @@ float evalVisibilitySSAO(in vec3 centerPosition, in vec3 centerNormal, in vec3 t vec3 tapUVZ = fetchTap(side, centerPixelPos, tap, imageSize); vec3 tapPositionES = evalEyePositionFromZeye(side.x, tapUVZ.z, tapUVZ.xy); vec3 deltaVec = normalize(tapPositionES - fragPositionES); + float rawHorizon = dot(deltaVec, fragNormalES); - <$horizon$> = max(<$horizon$>, dot(deltaVec, fragNormalES)); + rawHorizon *= 1.0 - radius / ssDiskRadius; + + <$horizon$> = max(<$horizon$>, rawHorizon); } } <@endfunc@> @@ -368,7 +371,8 @@ vec2 searchHorizons(ivec4 side, ivec2 centerPixelPos, vec2 imageSize, vec2 delta // Forward search for h1 vec2 clampedSearchVec = clampSearchVec(imageSize, vec2(centerPixelPos), searchVec); - int stepCount = int(floor(length(clampedSearchVec)+0.5)); + vec2 absClampedSearchVec = abs(clampedSearchVec); + int stepCount = int(max(absClampedSearchVec.x, absClampedSearchVec.y)); if (stepCount>0) { vec2 deltaPixelTap = clampedSearchVec / float(stepCount); float deltaRadius = length(deltaPixelTap); @@ -376,7 +380,8 @@ vec2 searchHorizons(ivec4 side, ivec2 centerPixelPos, vec2 imageSize, vec2 delta } // Backward search for h2 clampedSearchVec = clampSearchVec(imageSize, vec2(centerPixelPos), -searchVec); - stepCount = int(floor(length(clampedSearchVec)+0.5)); + absClampedSearchVec = abs(clampedSearchVec); + stepCount = int(max(absClampedSearchVec.x, absClampedSearchVec.y)); if (stepCount>0) { vec2 deltaPixelTap = clampedSearchVec / float(stepCount); float deltaRadius = length(deltaPixelTap); @@ -411,7 +416,7 @@ vec2 searchHorizons(ivec4 side, ivec2 centerPixelPos, vec2 imageSize, vec2 delta float evalVisibilityHBAO(ivec4 side, ivec2 centerPixelPos, vec2 imageSize, vec2 deltaTap, float ssDiskRadius, vec3 fragPositionES, vec3 fragNormalES) { vec2 horizons = searchHorizons(side, centerPixelPos, imageSize, deltaTap, ssDiskRadius, fragPositionES, fragNormalES); - return (horizons.x + horizons.y) * 0.5; + return (horizons.x + horizons.y) * 0.5 / PI; } <@endfunc@> diff --git a/libraries/render-utils/src/ssao_makeOcclusion.slf b/libraries/render-utils/src/ssao_makeOcclusion.slf index 89f2aadfab..e2c8a5ffa6 100644 --- a/libraries/render-utils/src/ssao_makeOcclusion.slf +++ b/libraries/render-utils/src/ssao_makeOcclusion.slf @@ -46,9 +46,7 @@ void main(void) { // Choose the screen-space sample radius float ssDiskRadius = evalDiskRadius(fragPositionES.z, imageSize); -#if SSAO_USE_HORIZON_BASED - ssDiskRadius = min(ssDiskRadius, 3.0); -#endif + // Let's make noise float randomPatternRotationAngle = getAngleDithering(centerPixelPos); From 45b4881edc75ff4d30cc006b7a5331a1fc5b7936 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Wed, 12 Sep 2018 17:59:57 +0200 Subject: [PATCH 013/286] Bilateral blur --- .../src/AmbientOcclusionEffect.cpp | 145 +++++++++++++----- .../render-utils/src/AmbientOcclusionEffect.h | 64 +++++--- .../render-utils/src/SurfaceGeometryPass.cpp | 5 +- .../src/render-utils/ShaderConstants.h | 2 + .../render-utils/ssao_makeHorizontalBlur.slp | 2 +- .../render-utils/ssao_makeVerticalBlur.slp | 2 +- libraries/render-utils/src/ssao.slh | 75 +++++---- libraries/render-utils/src/ssao_blur.slv | 42 +++++ .../src/ssao_makeHorizontalBlur.slf | 3 +- .../render-utils/src/ssao_makeOcclusion.slf | 6 +- .../src/ssao_makeVerticalBlur.slf | 4 +- 11 files changed, 251 insertions(+), 99 deletions(-) create mode 100644 libraries/render-utils/src/ssao_blur.slv diff --git a/libraries/render-utils/src/AmbientOcclusionEffect.cpp b/libraries/render-utils/src/AmbientOcclusionEffect.cpp index 0cc5220abf..8d28fae760 100644 --- a/libraries/render-utils/src/AmbientOcclusionEffect.cpp +++ b/libraries/render-utils/src/AmbientOcclusionEffect.cpp @@ -41,7 +41,7 @@ gpu::PipelinePointer AmbientOcclusionEffect::_mipCreationPipeline; AmbientOcclusionFramebuffer::AmbientOcclusionFramebuffer() { } -void AmbientOcclusionFramebuffer::updateLinearDepth(const gpu::TexturePointer& linearDepthBuffer) { +bool AmbientOcclusionFramebuffer::updateLinearDepth(const gpu::TexturePointer& linearDepthBuffer) { //If the depth buffer or size changed, we need to delete our FBOs bool reset = false; if ((_linearDepthTexture != linearDepthBuffer)) { @@ -59,6 +59,8 @@ void AmbientOcclusionFramebuffer::updateLinearDepth(const gpu::TexturePointer& l if (reset) { clear(); } + + return reset; } void AmbientOcclusionFramebuffer::clear() { @@ -170,6 +172,41 @@ public: } }; +AmbientOcclusionEffectConfig::AmbientOcclusionEffectConfig() : + render::GPUJobConfig::Persistent(QStringList() << "Render" << "Engine" << "Ambient Occlusion", false), +#if SSAO_USE_HORIZON_BASED + radius{ 0.1f }, +#else + radius{ 0.5f }, +#endif + perspectiveScale{ 1.0f }, + obscuranceLevel{ 0.5f }, + falloffBias{ 0.01f }, + edgeSharpness{ 1.0f }, + blurDeviation{ 2.5f }, + numSpiralTurns{ 7.0f }, +#if SSAO_USE_HORIZON_BASED + numSamples{ 1 }, +#else + numSamples{ 16 }, +#endif + resolutionLevel{ 1 }, + blurRadius{ 4 }, + ditheringEnabled{ true }, + borderingEnabled{ true }, + fetchMipsEnabled{ true } { + +} + +AmbientOcclusionEffect::AOParameters::AOParameters() : +resolutionInfo{ -1.0f, 0.0f, 1.0f, 0.0f }, +radiusInfo{ 0.5f, 0.5f * 0.5f, 1.0f / (0.25f * 0.25f * 0.25f), 1.0f }, +ditheringInfo{ 0.0f, 0.0f, 0.01f, 1.0f }, +sampleInfo{ 11.0f, 1.0f / 11.0f, 7.0f, 1.0f }, +blurInfo{ 1.0f, 3.0f, 2.0f, 0.0f } { + +} + AmbientOcclusionEffect::AmbientOcclusionEffect() { } @@ -177,11 +214,12 @@ void AmbientOcclusionEffect::configure(const Config& config) { DependencyManager::get()->setAmbientOcclusionEnabled(config.enabled); bool shouldUpdateGaussian = false; + bool shouldUpdateBlurs = false; const double RADIUS_POWER = 6.0; const auto& radius = config.radius; - if (radius != _parametersBuffer->getRadius()) { - auto& current = _parametersBuffer.edit().radiusInfo; + if (radius != _aoParametersBuffer->getRadius()) { + auto& current = _aoParametersBuffer.edit().radiusInfo; current.x = radius; current.y = radius * radius; current.z = 10.0f; @@ -190,74 +228,97 @@ void AmbientOcclusionEffect::configure(const Config& config) { #endif } - if (config.obscuranceLevel != _parametersBuffer->getObscuranceLevel()) { - auto& current = _parametersBuffer.edit().radiusInfo; + if (config.obscuranceLevel != _aoParametersBuffer->getObscuranceLevel()) { + auto& current = _aoParametersBuffer.edit().radiusInfo; current.w = config.obscuranceLevel; } - if (config.falloffBias != _parametersBuffer->getFalloffBias()) { - auto& current = _parametersBuffer.edit().ditheringInfo; + if (config.falloffBias != _aoParametersBuffer->getFalloffBias()) { + auto& current = _aoParametersBuffer.edit().ditheringInfo; current.z = config.falloffBias; } - if (config.edgeSharpness != _parametersBuffer->getEdgeSharpness()) { - auto& current = _parametersBuffer.edit().blurInfo; + if (config.edgeSharpness != _aoParametersBuffer->getEdgeSharpness()) { + auto& current = _aoParametersBuffer.edit().blurInfo; current.x = config.edgeSharpness; } - if (config.blurDeviation != _parametersBuffer->getBlurDeviation()) { - auto& current = _parametersBuffer.edit().blurInfo; + if (config.blurDeviation != _aoParametersBuffer->getBlurDeviation()) { + auto& current = _aoParametersBuffer.edit().blurInfo; current.z = config.blurDeviation; shouldUpdateGaussian = true; } - if (config.numSpiralTurns != _parametersBuffer->getNumSpiralTurns()) { - auto& current = _parametersBuffer.edit().sampleInfo; + if (config.numSpiralTurns != _aoParametersBuffer->getNumSpiralTurns()) { + auto& current = _aoParametersBuffer.edit().sampleInfo; current.z = config.numSpiralTurns; } - if (config.numSamples != _parametersBuffer->getNumSamples()) { - auto& current = _parametersBuffer.edit().sampleInfo; + if (config.numSamples != _aoParametersBuffer->getNumSamples()) { + auto& current = _aoParametersBuffer.edit().sampleInfo; current.x = config.numSamples; current.y = 1.0f / config.numSamples; } - if (config.fetchMipsEnabled != _parametersBuffer->isFetchMipsEnabled()) { - auto& current = _parametersBuffer.edit().sampleInfo; + if (config.fetchMipsEnabled != _aoParametersBuffer->isFetchMipsEnabled()) { + auto& current = _aoParametersBuffer.edit().sampleInfo; current.w = (float)config.fetchMipsEnabled; } if (!_framebuffer) { _framebuffer = std::make_shared(); + shouldUpdateBlurs = true; } - if (config.perspectiveScale != _parametersBuffer->getPerspectiveScale()) { - _parametersBuffer.edit().resolutionInfo.z = config.perspectiveScale; + if (config.perspectiveScale != _aoParametersBuffer->getPerspectiveScale()) { + _aoParametersBuffer.edit().resolutionInfo.z = config.perspectiveScale; } - if (config.resolutionLevel != _parametersBuffer->getResolutionLevel()) { - auto& current = _parametersBuffer.edit().resolutionInfo; + + if (config.resolutionLevel != _aoParametersBuffer->getResolutionLevel()) { + auto& current = _aoParametersBuffer.edit().resolutionInfo; current.x = (float) config.resolutionLevel; + shouldUpdateBlurs = true; } - if (config.blurRadius != _parametersBuffer->getBlurRadius()) { - auto& current = _parametersBuffer.edit().blurInfo; + if (config.blurRadius != _aoParametersBuffer->getBlurRadius()) { + auto& current = _aoParametersBuffer.edit().blurInfo; current.y = (float)config.blurRadius; shouldUpdateGaussian = true; } - if (config.ditheringEnabled != _parametersBuffer->isDitheringEnabled()) { - auto& current = _parametersBuffer.edit().ditheringInfo; + if (config.ditheringEnabled != _aoParametersBuffer->isDitheringEnabled()) { + auto& current = _aoParametersBuffer.edit().ditheringInfo; current.x = (float)config.ditheringEnabled; } - if (config.borderingEnabled != _parametersBuffer->isBorderingEnabled()) { - auto& current = _parametersBuffer.edit().ditheringInfo; + if (config.borderingEnabled != _aoParametersBuffer->isBorderingEnabled()) { + auto& current = _aoParametersBuffer.edit().ditheringInfo; current.w = (float)config.borderingEnabled; } if (shouldUpdateGaussian) { updateGaussianDistribution(); } + + if (shouldUpdateBlurs) { + updateBlurParameters(); + } +} + +void AmbientOcclusionEffect::updateBlurParameters() { + const auto resolutionLevel = _aoParametersBuffer->getResolutionLevel(); + const auto resolutionScale = 1 << resolutionLevel; + auto& vblur = _vblurParametersBuffer.edit(); + auto& hblur = _hblurParametersBuffer.edit(); + auto frameSize = _framebuffer->getSourceFrameSize(); + + hblur.scaleHeight.x = 1.0f / (frameSize.x * resolutionScale); + hblur.scaleHeight.y = 1.0f / frameSize.x; + hblur.scaleHeight.z = frameSize.y / resolutionScale; + + vblur.scaleHeight.x = 1.0f / (frameSize.y * resolutionScale); + vblur.scaleHeight.y = 1.0f / frameSize.y; + vblur.scaleHeight.z = frameSize.y; } const gpu::PipelinePointer& AmbientOcclusionEffect::getOcclusionPipeline() { @@ -309,8 +370,8 @@ const gpu::PipelinePointer& AmbientOcclusionEffect::getMipCreationPipeline() { } void AmbientOcclusionEffect::updateGaussianDistribution() { - auto coefs = _parametersBuffer.edit()._gaussianCoefs; - GaussianDistribution::evalSampling(coefs, Parameters::GAUSSIAN_COEFS_LENGTH, _parametersBuffer->getBlurRadius(), _parametersBuffer->getBlurDeviation()); + auto coefs = _aoParametersBuffer.edit()._gaussianCoefs; + GaussianDistribution::evalSampling(coefs, AOParameters::GAUSSIAN_COEFS_LENGTH, _aoParametersBuffer->getBlurRadius(), _aoParametersBuffer->getBlurDeviation()); } void AmbientOcclusionEffect::run(const render::RenderContextPointer& renderContext, const Inputs& inputs, Outputs& outputs) { @@ -325,6 +386,7 @@ void AmbientOcclusionEffect::run(const render::RenderContextPointer& renderConte auto linearDepthTexture = linearDepthFramebuffer->getLinearDepthTexture(); auto sourceViewport = args->_viewport; auto occlusionViewport = sourceViewport; + auto firstBlurViewport = sourceViewport; if (!_gpuTimer) { _gpuTimer = std::make_shared < gpu::RangeTimer>(__FUNCTION__); @@ -334,18 +396,21 @@ void AmbientOcclusionEffect::run(const render::RenderContextPointer& renderConte _framebuffer = std::make_shared(); } - if (_parametersBuffer->getResolutionLevel() > 0) { - linearDepthTexture = linearDepthFramebuffer->getHalfLinearDepthTexture(); - occlusionViewport = occlusionViewport >> _parametersBuffer->getResolutionLevel(); + const auto resolutionScale = powf(0.5f, _aoParametersBuffer->getResolutionLevel()); + if (_aoParametersBuffer->getResolutionLevel() > 0) { + occlusionViewport = occlusionViewport >> _aoParametersBuffer->getResolutionLevel(); + firstBlurViewport.w = firstBlurViewport.w >> _aoParametersBuffer->getResolutionLevel(); } - _framebuffer->updateLinearDepth(linearDepthTexture); + if (_framebuffer->updateLinearDepth(linearDepthTexture)) { + updateBlurParameters(); + } auto occlusionFBO = _framebuffer->getOcclusionFramebuffer(); auto occlusionBlurredFBO = _framebuffer->getOcclusionBlurredFramebuffer(); outputs.edit0() = _framebuffer; - outputs.edit1() = _parametersBuffer; + outputs.edit1() = _aoParametersBuffer; auto framebufferSize = _framebuffer->getSourceFrameSize(); @@ -380,7 +445,7 @@ void AmbientOcclusionEffect::run(const render::RenderContextPointer& renderConte batch.popProfileRange(); batch.setUniformBuffer(render_utils::slot::buffer::DeferredFrameTransform, frameTransform->getFrameTransformBuffer()); - batch.setUniformBuffer(render_utils::slot::buffer::SsaoParams, _parametersBuffer); + batch.setUniformBuffer(render_utils::slot::buffer::SsaoParams, _aoParametersBuffer); // Occlusion pass batch.setFramebuffer(occlusionFBO); @@ -389,16 +454,24 @@ void AmbientOcclusionEffect::run(const render::RenderContextPointer& renderConte batch.setResourceTexture(render_utils::slot::texture::SsaoPyramid, _framebuffer->getLinearDepthTexture()); batch.draw(gpu::TRIANGLE_STRIP, 4); - if (_parametersBuffer->getBlurRadius() > 0) { + /* TEMPO OP if (_aoParametersBuffer->getBlurRadius() > 0)*/ { PROFILE_RANGE_BATCH(batch, "Blur"); // Blur 1st pass + model.setScale(resolutionScale); + batch.setModelTransform(model); + batch.setViewportTransform(firstBlurViewport); batch.setFramebuffer(occlusionBlurredFBO); + batch.setUniformBuffer(render_utils::slot::buffer::SsaoBlurParams, _hblurParametersBuffer); batch.setPipeline(firstHBlurPipeline); batch.setResourceTexture(render_utils::slot::texture::SsaoOcclusion, occlusionFBO->getRenderBuffer(0)); batch.draw(gpu::TRIANGLE_STRIP, 4); // Blur 2nd pass + model.setScale(glm::vec3(1.0f, resolutionScale, 1.0f)); + batch.setModelTransform(model); + batch.setViewportTransform(sourceViewport); batch.setFramebuffer(occlusionFBO); + batch.setUniformBuffer(render_utils::slot::buffer::SsaoBlurParams, _vblurParametersBuffer); batch.setPipeline(lastVBlurPipeline); batch.setResourceTexture(render_utils::slot::texture::SsaoOcclusion, occlusionBlurredFBO->getRenderBuffer(0)); batch.draw(gpu::TRIANGLE_STRIP, 4); diff --git a/libraries/render-utils/src/AmbientOcclusionEffect.h b/libraries/render-utils/src/AmbientOcclusionEffect.h index f96830a8f2..b44569eb89 100644 --- a/libraries/render-utils/src/AmbientOcclusionEffect.h +++ b/libraries/render-utils/src/AmbientOcclusionEffect.h @@ -32,7 +32,7 @@ public: gpu::TexturePointer getOcclusionBlurredTexture(); // Update the source framebuffer size which will drive the allocation of all the other resources. - void updateLinearDepth(const gpu::TexturePointer& linearDepthBuffer); + bool updateLinearDepth(const gpu::TexturePointer& linearDepthBuffer); gpu::TexturePointer getLinearDepthTexture(); const glm::ivec2& getSourceFrameSize() const { return _frameSize; } @@ -71,7 +71,7 @@ class AmbientOcclusionEffectConfig : public render::GPUJobConfig::Persistent { Q_PROPERTY(int blurRadius MEMBER blurRadius WRITE setBlurRadius) public: - AmbientOcclusionEffectConfig() : render::GPUJobConfig::Persistent(QStringList() << "Render" << "Engine" << "Ambient Occlusion", false) {} + AmbientOcclusionEffectConfig(); const int MAX_RESOLUTION_LEVEL = 4; const int MAX_BLUR_RADIUS = 6; @@ -86,19 +86,19 @@ public: void setResolutionLevel(int level) { resolutionLevel = std::max(0, std::min(level, MAX_RESOLUTION_LEVEL)); emit dirty(); } void setBlurRadius(int radius) { blurRadius = std::max(0, std::min(MAX_BLUR_RADIUS, radius)); emit dirty(); } - float radius{ 0.5f }; - float perspectiveScale{ 1.0f }; - float obscuranceLevel{ 0.5f }; // intensify or dim down the obscurance effect - float falloffBias{ 0.01f }; - float edgeSharpness{ 1.0f }; - float blurDeviation{ 2.5f }; - float numSpiralTurns{ 7.0f }; // defining an angle span to distribute the samples ray directions - int numSamples{ 16 }; - int resolutionLevel{ 1 }; - int blurRadius{ 4 }; // 0 means no blurring - bool ditheringEnabled{ true }; // randomize the distribution of taps per pixel, should always be true - bool borderingEnabled{ true }; // avoid evaluating information from non existing pixels out of the frame, should always be true - bool fetchMipsEnabled{ true }; // fetch taps in sub mips to otpimize cache, should always be true + float radius; + float perspectiveScale; + float obscuranceLevel; // intensify or dim down the obscurance effect + float falloffBias; + float edgeSharpness; + float blurDeviation; + float numSpiralTurns; // defining an angle span to distribute the samples ray directions + int numSamples; + int resolutionLevel; + int blurRadius; // 0 means no blurring + bool ditheringEnabled; // randomize the distribution of taps per pixel, should always be true + bool borderingEnabled; // avoid evaluating information from non existing pixels out of the frame, should always be true + bool fetchMipsEnabled; // fetch taps in sub mips to otpimize cache, should always be true signals: void dirty(); @@ -118,23 +118,23 @@ public: // Class describing the uniform buffer with all the parameters common to the AO shaders - class Parameters { + class AOParameters { public: // Resolution info - glm::vec4 resolutionInfo { -1.0f, 0.0f, 1.0f, 0.0f }; + glm::vec4 resolutionInfo; // radius info is { R, R^2, 1 / R^6, ObscuranceScale} - glm::vec4 radiusInfo{ 0.5f, 0.5f * 0.5f, 1.0f / (0.25f * 0.25f * 0.25f), 1.0f }; + glm::vec4 radiusInfo; // Dithering info - glm::vec4 ditheringInfo { 0.0f, 0.0f, 0.01f, 1.0f }; + glm::vec4 ditheringInfo; // Sampling info - glm::vec4 sampleInfo { 11.0f, 1.0f/11.0f, 7.0f, 1.0f }; + glm::vec4 sampleInfo; // Blurring info - glm::vec4 blurInfo { 1.0f, 3.0f, 2.0f, 0.0f }; + glm::vec4 blurInfo; // gaussian distribution coefficients first is the sampling radius (max is 6) const static int GAUSSIAN_COEFS_LENGTH = 8; float _gaussianCoefs[GAUSSIAN_COEFS_LENGTH]; - Parameters() {} + AOParameters(); int getResolutionLevel() const { return resolutionInfo.x; } float getRadius() const { return radiusInfo.x; } @@ -152,12 +152,26 @@ public: bool isDitheringEnabled() const { return ditheringInfo.x; } bool isBorderingEnabled() const { return ditheringInfo.w; } }; - using ParametersBuffer = gpu::StructBuffer; + using AOParametersBuffer = gpu::StructBuffer; private: + + // Class describing the uniform buffer with all the parameters common to the bilateral blur shaders + class BlurParameters { + public: + glm::vec4 scaleHeight{ 0.0f }; + + BlurParameters() {} + }; + using BlurParametersBuffer = gpu::StructBuffer; + + void updateGaussianDistribution(); + void updateBlurParameters(); - ParametersBuffer _parametersBuffer; + AOParametersBuffer _aoParametersBuffer; + BlurParametersBuffer _vblurParametersBuffer; + BlurParametersBuffer _hblurParametersBuffer; static const gpu::PipelinePointer& getOcclusionPipeline(); static const gpu::PipelinePointer& getHBlurPipeline(); // first @@ -195,7 +209,7 @@ signals: class DebugAmbientOcclusion { public: - using Inputs = render::VaryingSet4; + using Inputs = render::VaryingSet4; using Config = DebugAmbientOcclusionConfig; using JobModel = render::Job::ModelI; diff --git a/libraries/render-utils/src/SurfaceGeometryPass.cpp b/libraries/render-utils/src/SurfaceGeometryPass.cpp index d32cba43db..b0f19862ce 100644 --- a/libraries/render-utils/src/SurfaceGeometryPass.cpp +++ b/libraries/render-utils/src/SurfaceGeometryPass.cpp @@ -64,14 +64,15 @@ void LinearDepthFramebuffer::allocate() { auto height = _frameSize.y; // For Linear Depth: - _linearDepthTexture = gpu::Texture::createRenderBuffer(gpu::Element(gpu::SCALAR, gpu::FLOAT, gpu::RED), width, height, gpu::Texture::SINGLE_MIP, + const uint16_t LINEAR_DEPTH_MAX_MIP_LEVEL = 5; + _linearDepthTexture = gpu::Texture::createRenderBuffer(gpu::Element(gpu::SCALAR, gpu::FLOAT, gpu::RED), width, height, LINEAR_DEPTH_MAX_MIP_LEVEL, 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: - const uint16_t HALF_LINEAR_DEPTH_MAX_MIP_LEVEL = 5; + const uint16_t HALF_LINEAR_DEPTH_MAX_MIP_LEVEL = LINEAR_DEPTH_MAX_MIP_LEVEL; _halfLinearDepthTexture = gpu::Texture::createRenderBuffer(gpu::Element(gpu::SCALAR, gpu::FLOAT, gpu::RED), _halfFrameSize.x, _halfFrameSize.y, HALF_LINEAR_DEPTH_MAX_MIP_LEVEL, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR_MIP_POINT)); diff --git a/libraries/render-utils/src/render-utils/ShaderConstants.h b/libraries/render-utils/src/render-utils/ShaderConstants.h index 330df6a2af..3aeb3a73c8 100644 --- a/libraries/render-utils/src/render-utils/ShaderConstants.h +++ b/libraries/render-utils/src/render-utils/ShaderConstants.h @@ -86,6 +86,7 @@ // Ambient occlusion #define RENDER_UTILS_BUFFER_SSAO_PARAMS 2 #define RENDER_UTILS_BUFFER_SSAO_DEBUG_PARAMS 3 +#define RENDER_UTILS_BUFFER_SSAO_BLUR_PARAMS 4 #define RENDER_UTILS_TEXTURE_SSAO_PYRAMID 1 #define RENDER_UTILS_TEXTURE_SSAO_OCCLUSION 0 @@ -153,6 +154,7 @@ enum Buffer { SsscParams = RENDER_UTILS_BUFFER_SSSC_PARAMS, SsaoParams = RENDER_UTILS_BUFFER_SSAO_PARAMS, SsaoDebugParams = RENDER_UTILS_BUFFER_SSAO_DEBUG_PARAMS, + SsaoBlurParams = RENDER_UTILS_BUFFER_SSAO_BLUR_PARAMS, LightIndex = RENDER_UTILS_BUFFER_LIGHT_INDEX, TaaParams = RENDER_UTILS_BUFFER_TAA_PARAMS, HighlightParams = RENDER_UTILS_BUFFER_HIGHLIGHT_PARAMS, diff --git a/libraries/render-utils/src/render-utils/ssao_makeHorizontalBlur.slp b/libraries/render-utils/src/render-utils/ssao_makeHorizontalBlur.slp index d4d8ec4b01..49fd3dba93 100644 --- a/libraries/render-utils/src/render-utils/ssao_makeHorizontalBlur.slp +++ b/libraries/render-utils/src/render-utils/ssao_makeHorizontalBlur.slp @@ -1 +1 @@ -VERTEX gpu::vertex::DrawViewportQuadTransformTexcoord +VERTEX ssao_blur diff --git a/libraries/render-utils/src/render-utils/ssao_makeVerticalBlur.slp b/libraries/render-utils/src/render-utils/ssao_makeVerticalBlur.slp index d4d8ec4b01..49fd3dba93 100644 --- a/libraries/render-utils/src/render-utils/ssao_makeVerticalBlur.slp +++ b/libraries/render-utils/src/render-utils/ssao_makeVerticalBlur.slp @@ -1 +1 @@ -VERTEX gpu::vertex::DrawViewportQuadTransformTexcoord +VERTEX ssao_blur diff --git a/libraries/render-utils/src/ssao.slh b/libraries/render-utils/src/ssao.slh index b4dd7e6b6c..d974a83104 100644 --- a/libraries/render-utils/src/ssao.slh +++ b/libraries/render-utils/src/ssao.slh @@ -425,73 +425,90 @@ float evalVisibilityHBAO(ivec4 side, ivec2 centerPixelPos, vec2 imageSize, vec2 <$declarePackOcclusionDepth()$> <$declareAmbientOcclusion()$> +<$declareFetchDepthPyramidMap()$> // the source occlusion texture layout(binding=RENDER_UTILS_TEXTURE_SSAO_OCCLUSION) uniform sampler2D occlusionMap; +struct BlurParams { + vec4 scaleHeight; +}; -vec2 fetchOcclusionDepthRaw(ivec2 coords, out vec3 raw) { - raw = texelFetch(occlusionMap, coords, 0).xyz; - return unpackOcclusionDepth(raw); +layout(binding=RENDER_UTILS_BUFFER_SSAO_BLUR_PARAMS) uniform blurParamsBuffer { + BlurParams blurParams; +}; + +float getOcclusionBlurScale() { + return blurParams.scaleHeight.x; } -vec2 fetchOcclusionDepth(ivec2 coords) { - return unpackOcclusionDepth(texelFetch(occlusionMap, coords, 0).xyz); +float getDepthBlurScale() { + return blurParams.scaleHeight.y; +} + +int getBlurImageHeight() { + return int(blurParams.scaleHeight.z); +} + +float fetchOcclusion(vec2 coords) { + vec3 raw = texture(occlusionMap, coords, 0).xyz; + return raw.x; } -const int RADIUS_SCALE = 1; const float BLUR_WEIGHT_OFFSET = 0.05; -const float BLUR_EDGE_SCALE = 5000.0; +const float BLUR_EDGE_SCALE = 100.0; -vec2 evalTapWeightedValue(ivec3 side, int r, ivec2 ssC, float key) { - ivec2 tapOffset = <$axis$> * (r * RADIUS_SCALE); - ivec2 ssP = (ssC + tapOffset); +vec2 evalTapWeightedValue(ivec3 side, int r, ivec2 destPixelCoord, vec2 scaledTexCoord, vec2 fullTexCoord, float key) { + ivec2 tapOffset = <$axis$> * r; + ivec2 tapPixelCoord = destPixelCoord + tapOffset; - if ((ssP.x < side.y || ssP.x >= side.z + side.y) || (ssP.y < 0 || ssP.y >= int(getWidthHeight(getResolutionLevel()).y))) { + if ((tapPixelCoord.x < side.y || tapPixelCoord.x >= side.z + side.y) || (tapPixelCoord.y < 0 || tapPixelCoord.y >= getBlurImageHeight())) { return vec2(0.0); } - vec2 tapOZ = fetchOcclusionDepth(ssC + tapOffset); + vec2 tapTexCoord = scaledTexCoord + tapOffset * getOcclusionBlurScale(); + float tapOcclusion = fetchOcclusion(tapTexCoord); + + tapTexCoord = fullTexCoord + tapOffset * getDepthBlurScale(); + float tapDepth = getZEyeAtUV(tapTexCoord, 0); // spatial domain: offset gaussian tap float weight = BLUR_WEIGHT_OFFSET + getBlurCoef(abs(r)); // range domain (the "bilateral" weight). As depth difference increases, decrease weight. - weight *= max(0.0, 1.0 - (getBlurEdgeSharpness() * BLUR_EDGE_SCALE) * abs(tapOZ.y - key)); + weight *= max(0.0, 1.0 - (getBlurEdgeSharpness() * BLUR_EDGE_SCALE) * abs(tapDepth - key)); - return vec2(tapOZ.x * weight, weight); + return vec2(tapOcclusion * weight, weight); } -vec3 getBlurredOcclusion(vec2 coord) { - ivec2 ssC = ivec2(coord); - +vec3 getBlurredOcclusion(ivec2 destPixelCoord, vec2 scaledTexCoord, vec2 fullTexCoord) { // Stereo side info - ivec4 side = getStereoSideInfo(ssC.x, getResolutionLevel()); + ivec4 side = getStereoSideInfo(destPixelCoord.x, 0); - vec3 rawSample; - vec2 occlusionDepth = fetchOcclusionDepthRaw(ssC, rawSample); - float key = occlusionDepth.y; + float pixelDepth = getZEyeAtUV(fullTexCoord, 0); + vec2 weightedSums = vec2(0.0); - // Central pixel contribution - float mainWeight = getBlurCoef(0); - vec2 weightedSums = vec2(occlusionDepth.x * mainWeight, mainWeight); - // Accumulate weighted contributions along the bluring axis in the [-radius, radius] range int blurRadius = getBlurRadius(); // negative side first for (int r = -blurRadius; r <= -1; ++r) { - weightedSums += evalTapWeightedValue(side.xyz, r, ssC, key); + weightedSums += evalTapWeightedValue(side.xyz, r, destPixelCoord, scaledTexCoord, fullTexCoord, pixelDepth); } + + // Central pixel contribution + float mainWeight = getBlurCoef(0); + float pixelOcclusion = fetchOcclusion(scaledTexCoord); + weightedSums += vec2(pixelOcclusion * mainWeight, mainWeight); + // then positive side for (int r = 1; r <= blurRadius; ++r) { - weightedSums += evalTapWeightedValue(side.xyz, r, ssC, key); + weightedSums += evalTapWeightedValue(side.xyz, r, destPixelCoord, scaledTexCoord, fullTexCoord, pixelDepth); } // Final normalization const float epsilon = 0.0001; float result = weightedSums.x / (weightedSums.y + epsilon); - rawSample.x = result; - return rawSample; + return vec3(result); } <@endfunc@> diff --git a/libraries/render-utils/src/ssao_blur.slv b/libraries/render-utils/src/ssao_blur.slv new file mode 100644 index 0000000000..aafd9adbf4 --- /dev/null +++ b/libraries/render-utils/src/ssao_blur.slv @@ -0,0 +1,42 @@ +<@include gpu/Config.slh@> +<$VERSION_HEADER$> +// Generated on <$_SCRIBE_DATE$> +// +// ssao_blur.vert +// +// Draw the unit quad [-1,-1 -> 1,1] filling in +// Simply draw a Triangle_strip of 2 triangles, no input buffers or index buffer needed +// +// Created by Olivier Prat on 9/12/2018 +// Copyright 2018 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 gpu/Transform.slh@> + +<$declareStandardTransform()$> + +layout(location=0) out vec4 varTexCoord0; + +void main(void) { + const vec4 UNIT_QUAD[4] = vec4[4]( + vec4(-1.0, -1.0, 0.0, 1.0), + vec4(1.0, -1.0, 0.0, 1.0), + vec4(-1.0, 1.0, 0.0, 1.0), + vec4(1.0, 1.0, 0.0, 1.0) + ); + vec4 pos = UNIT_QUAD[gl_VertexID]; + + // standard transform but applied to the Texcoord + vec2 fullTexCoord = (pos.xy + 1.0) * 0.5; + vec4 tc = vec4(fullTexCoord, pos.zw); + + TransformObject obj = getTransformObject(); + <$transformModelToWorldPos(obj, tc, tc)$> + + gl_Position = pos; + varTexCoord0.xy = tc.xy; + varTexCoord0.zw = fullTexCoord.xy; +} diff --git a/libraries/render-utils/src/ssao_makeHorizontalBlur.slf b/libraries/render-utils/src/ssao_makeHorizontalBlur.slf index 94dbb2b00c..2d02ed02f0 100644 --- a/libraries/render-utils/src/ssao_makeHorizontalBlur.slf +++ b/libraries/render-utils/src/ssao_makeHorizontalBlur.slf @@ -16,9 +16,10 @@ const ivec2 horizontal = ivec2(1,0); <$declareBlurPass(horizontal)$> +layout(location=0) in vec4 varTexCoord0; layout(location=0) out vec4 outFragColor; void main(void) { - outFragColor = vec4(getBlurredOcclusion(gl_FragCoord.xy), 1.0); + outFragColor = vec4(getBlurredOcclusion(ivec2(gl_FragCoord.xy), varTexCoord0.xy, varTexCoord0.zw), 1.0); } diff --git a/libraries/render-utils/src/ssao_makeOcclusion.slf b/libraries/render-utils/src/ssao_makeOcclusion.slf index e2c8a5ffa6..ba01241ce7 100644 --- a/libraries/render-utils/src/ssao_makeOcclusion.slf +++ b/libraries/render-utils/src/ssao_makeOcclusion.slf @@ -30,9 +30,6 @@ void main(void) { vec2 fragCoord = gl_FragCoord.xy; ivec2 centerPixelPos = ivec2(fragCoord.xy); - // Fetch the z under the pixel (stereo or not) - float Zeye = getZEyeAtPixel(centerPixelPos, 0); - // Stereo side info ivec4 side = getStereoSideInfo(centerPixelPos.x, getResolutionLevel()); @@ -40,6 +37,9 @@ void main(void) { centerPixelPos.x -= side.y; vec2 fragUVPos = (vec2(centerPixelPos) + vec2(0.5)) / imageSize; + // Fetch the z under the pixel (stereo or not) + float Zeye = getZEyeAtUV(fragUVPos, 0); + // The position and normal of the pixel fragment in Eye space vec3 fragPositionES = evalEyePositionFromZeye(side.x, Zeye, fragUVPos); vec3 fragNormalES = evalEyeNormal(fragPositionES); diff --git a/libraries/render-utils/src/ssao_makeVerticalBlur.slf b/libraries/render-utils/src/ssao_makeVerticalBlur.slf index 0b9b5c7eaf..1ef6666424 100644 --- a/libraries/render-utils/src/ssao_makeVerticalBlur.slf +++ b/libraries/render-utils/src/ssao_makeVerticalBlur.slf @@ -15,9 +15,11 @@ const ivec2 vertical = ivec2(0,1); <$declareBlurPass(vertical)$> +layout(location=0) in vec4 varTexCoord0; + layout(location=0) out vec4 outFragColor; void main(void) { - float occlusion = getBlurredOcclusion(gl_FragCoord.xy).x; + float occlusion = getBlurredOcclusion(ivec2(gl_FragCoord.xy), varTexCoord0.xy, varTexCoord0.zw).x; outFragColor = vec4(occlusion, 0.0, 0.0, occlusion); } From 8006d7c0522ac787f7f242a8715e195ce71e01d3 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Thu, 13 Sep 2018 15:33:44 +0200 Subject: [PATCH 014/286] Blur is now at occlusion resolution and added some bias to prevent AO on silhouettes --- .../src/AmbientOcclusionEffect.cpp | 14 +++++++++---- .../render-utils/src/AmbientOcclusionEffect.h | 4 ++++ libraries/render-utils/src/ssao.slh | 20 +++++++++++-------- .../utilities/render/ambientOcclusionPass.qml | 3 ++- 4 files changed, 28 insertions(+), 13 deletions(-) diff --git a/libraries/render-utils/src/AmbientOcclusionEffect.cpp b/libraries/render-utils/src/AmbientOcclusionEffect.cpp index 8d28fae760..fe9f4fc46b 100644 --- a/libraries/render-utils/src/AmbientOcclusionEffect.cpp +++ b/libraries/render-utils/src/AmbientOcclusionEffect.cpp @@ -182,6 +182,7 @@ AmbientOcclusionEffectConfig::AmbientOcclusionEffectConfig() : perspectiveScale{ 1.0f }, obscuranceLevel{ 0.5f }, falloffBias{ 0.01f }, + silhouetteRadius{ 0.3f }, edgeSharpness{ 1.0f }, blurDeviation{ 2.5f }, numSpiralTurns{ 7.0f }, @@ -296,6 +297,11 @@ void AmbientOcclusionEffect::configure(const Config& config) { current.w = (float)config.borderingEnabled; } + if (config.silhouetteRadius != _aoParametersBuffer->getSilhouetteRadius()) { + auto& current = _aoParametersBuffer.edit().ditheringInfo; + current.y = (float)config.silhouetteRadius; + } + if (shouldUpdateGaussian) { updateGaussianDistribution(); } @@ -312,12 +318,12 @@ void AmbientOcclusionEffect::updateBlurParameters() { auto& hblur = _hblurParametersBuffer.edit(); auto frameSize = _framebuffer->getSourceFrameSize(); - hblur.scaleHeight.x = 1.0f / (frameSize.x * resolutionScale); - hblur.scaleHeight.y = 1.0f / frameSize.x; + hblur.scaleHeight.x = 1.0f / frameSize.x; + hblur.scaleHeight.y = float(resolutionScale) / frameSize.x; hblur.scaleHeight.z = frameSize.y / resolutionScale; - vblur.scaleHeight.x = 1.0f / (frameSize.y * resolutionScale); - vblur.scaleHeight.y = 1.0f / frameSize.y; + vblur.scaleHeight.x = 1.0f / frameSize.y; + vblur.scaleHeight.y = float(resolutionScale) / frameSize.y; vblur.scaleHeight.z = frameSize.y; } diff --git a/libraries/render-utils/src/AmbientOcclusionEffect.h b/libraries/render-utils/src/AmbientOcclusionEffect.h index b44569eb89..48752ba3b6 100644 --- a/libraries/render-utils/src/AmbientOcclusionEffect.h +++ b/libraries/render-utils/src/AmbientOcclusionEffect.h @@ -63,6 +63,7 @@ class AmbientOcclusionEffectConfig : public render::GPUJobConfig::Persistent { Q_PROPERTY(float radius MEMBER radius WRITE setRadius) Q_PROPERTY(float obscuranceLevel MEMBER obscuranceLevel WRITE setObscuranceLevel) Q_PROPERTY(float falloffBias MEMBER falloffBias WRITE setFalloffBias) + Q_PROPERTY(float silhouetteRadius MEMBER silhouetteRadius WRITE setSilhouetteRadius) Q_PROPERTY(float edgeSharpness MEMBER edgeSharpness WRITE setEdgeSharpness) Q_PROPERTY(float blurDeviation MEMBER blurDeviation WRITE setBlurDeviation) Q_PROPERTY(float numSpiralTurns MEMBER numSpiralTurns WRITE setNumSpiralTurns) @@ -79,6 +80,7 @@ public: void setRadius(float newRadius) { radius = std::max(0.01f, newRadius); emit dirty(); } void setObscuranceLevel(float level) { obscuranceLevel = std::max(0.01f, level); emit dirty(); } void setFalloffBias(float bias) { falloffBias = std::max(0.0f, std::min(bias, 0.2f)); emit dirty(); } + void setSilhouetteRadius(float value) { silhouetteRadius = std::max(0.0f, value); emit dirty(); } void setEdgeSharpness(float sharpness) { edgeSharpness = std::max(0.0f, (float)sharpness); emit dirty(); } void setBlurDeviation(float deviation) { blurDeviation = std::max(0.0f, deviation); emit dirty(); } void setNumSpiralTurns(float turns) { numSpiralTurns = std::max(0.0f, (float)turns); emit dirty(); } @@ -90,6 +92,7 @@ public: float perspectiveScale; float obscuranceLevel; // intensify or dim down the obscurance effect float falloffBias; + float silhouetteRadius; float edgeSharpness; float blurDeviation; float numSpiralTurns; // defining an angle span to distribute the samples ray directions @@ -143,6 +146,7 @@ public: float getFalloffBias() const { return (float)ditheringInfo.z; } float getEdgeSharpness() const { return (float)blurInfo.x; } float getBlurDeviation() const { return blurInfo.z; } + float getSilhouetteRadius() const { return ditheringInfo.y; } float getNumSpiralTurns() const { return sampleInfo.z; } int getNumSamples() const { return (int)sampleInfo.x; } diff --git a/libraries/render-utils/src/ssao.slh b/libraries/render-utils/src/ssao.slh index d974a83104..71ad578485 100644 --- a/libraries/render-utils/src/ssao.slh +++ b/libraries/render-utils/src/ssao.slh @@ -67,6 +67,7 @@ float getRadius2() { float getInvRadius6() { return params._radiusInfo.z; } + float getObscuranceScaling() { return params._radiusInfo.z * params._radiusInfo.w; } @@ -74,7 +75,7 @@ float getObscuranceScaling() { float isDitheringEnabled() { return params._ditheringInfo.x; } -float getFrameDithering() { +float getSilhouetteRadius() { return params._ditheringInfo.y; } float isBorderingEnabled() { @@ -139,12 +140,12 @@ float getAngleDitheringWorldPos(in vec3 pixelWorldPos) { ivec3 pixelPos = ivec3(worldPosFract * 256.0); - return isDitheringEnabled() * float(((3 * pixelPos.x ^ pixelPos.y + pixelPos.x * pixelPos.y) + (3 * pixelPos.y ^ pixelPos.z + pixelPos.x * pixelPos.z)) * 10) + getFrameDithering(); + return isDitheringEnabled() * float(((3 * pixelPos.x ^ pixelPos.y + pixelPos.x * pixelPos.y) + (3 * pixelPos.y ^ pixelPos.z + pixelPos.x * pixelPos.z)) * 10); } float getAngleDithering(in ivec2 pixelPos) { // Hash function used in the AlchemyAO paper - return isDitheringEnabled() * float((3 * pixelPos.x ^ pixelPos.y + pixelPos.x * pixelPos.y) * 10) + getFrameDithering(); + return isDitheringEnabled() * float((3 * pixelPos.x ^ pixelPos.y + pixelPos.x * pixelPos.y) * 10); } float evalDiskRadius(float Zeye, vec2 imageSize) { @@ -304,10 +305,13 @@ float evalVisibilitySSAO(in vec3 centerPosition, in vec3 centerNormal, in vec3 t vec3 tap = vec3(tapPixelPos, radius); vec3 tapUVZ = fetchTap(side, centerPixelPos, tap, imageSize); vec3 tapPositionES = evalEyePositionFromZeye(side.x, tapUVZ.z, tapUVZ.xy); - vec3 deltaVec = normalize(tapPositionES - fragPositionES); - float rawHorizon = dot(deltaVec, fragNormalES); + vec3 deltaVec = tapPositionES - fragPositionES; + float deltaVecHeight = dot(deltaVec, fragNormalES); + float rawHorizon = dot(normalize(deltaVec), fragNormalES); + float falloff = max(0.0, 1.0 - deltaVecHeight / getSilhouetteRadius()); - rawHorizon *= 1.0 - radius / ssDiskRadius; + rawHorizon = rawHorizon < getFalloffBias() ? 0.0f : rawHorizon; + rawHorizon *= falloff * falloff; <$horizon$> = max(<$horizon$>, rawHorizon); } @@ -455,8 +459,8 @@ float fetchOcclusion(vec2 coords) { return raw.x; } -const float BLUR_WEIGHT_OFFSET = 0.05; -const float BLUR_EDGE_SCALE = 100.0; +const float BLUR_WEIGHT_OFFSET = 0.01; +const float BLUR_EDGE_SCALE = 10.0; vec2 evalTapWeightedValue(ivec3 side, int r, ivec2 destPixelCoord, vec2 scaledTexCoord, vec2 fullTexCoord, float key) { ivec2 tapOffset = <$axis$> * r; diff --git a/scripts/developer/utilities/render/ambientOcclusionPass.qml b/scripts/developer/utilities/render/ambientOcclusionPass.qml index 179fc8341c..1e5a46f115 100644 --- a/scripts/developer/utilities/render/ambientOcclusionPass.qml +++ b/scripts/developer/utilities/render/ambientOcclusionPass.qml @@ -38,7 +38,8 @@ Rectangle { "Num Taps:numSamples:32:true", "Taps Spiral:numSpiralTurns:10.0:false", "Falloff Bias:falloffBias:0.2:false", - "Edge Sharpness:edgeSharpness:1.0:false", + "Silhouette Radius:silhouetteRadius:1.0:false", + "Blur Edge Sharpness:edgeSharpness:1.0:false", "Blur Radius:blurRadius:10.0:false", ] ConfigSlider { From 6b8f47c75a6e644d650140e1fd16660ac1b7d7c9 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Thu, 13 Sep 2018 18:39:02 +0200 Subject: [PATCH 015/286] Added some tweaking parameters to try to limit silhouette AO --- .../src/AmbientOcclusionEffect.cpp | 27 ++-- .../render-utils/src/AmbientOcclusionEffect.h | 20 +-- libraries/render-utils/src/ssao.slh | 144 ++++++++---------- .../render-utils/src/ssao_makeOcclusion.slf | 4 + .../utilities/render/ambientOcclusionPass.qml | 6 +- 5 files changed, 97 insertions(+), 104 deletions(-) diff --git a/libraries/render-utils/src/AmbientOcclusionEffect.cpp b/libraries/render-utils/src/AmbientOcclusionEffect.cpp index fe9f4fc46b..381f015f9e 100644 --- a/libraries/render-utils/src/AmbientOcclusionEffect.cpp +++ b/libraries/render-utils/src/AmbientOcclusionEffect.cpp @@ -181,8 +181,12 @@ AmbientOcclusionEffectConfig::AmbientOcclusionEffectConfig() : #endif perspectiveScale{ 1.0f }, obscuranceLevel{ 0.5f }, - falloffBias{ 0.01f }, - silhouetteRadius{ 0.3f }, +#if SSAO_USE_HORIZON_BASED + falloffAngle{ 0.1f }, +#else + falloffAngle{ 0.01f }, +#endif + falloffDistance{ 0.3f }, edgeSharpness{ 1.0f }, blurDeviation{ 2.5f }, numSpiralTurns{ 7.0f }, @@ -234,9 +238,9 @@ void AmbientOcclusionEffect::configure(const Config& config) { current.w = config.obscuranceLevel; } - if (config.falloffBias != _aoParametersBuffer->getFalloffBias()) { + if (config.falloffAngle != _aoParametersBuffer->getFalloffAngle()) { auto& current = _aoParametersBuffer.edit().ditheringInfo; - current.z = config.falloffBias; + current.z = config.falloffAngle; } if (config.edgeSharpness != _aoParametersBuffer->getEdgeSharpness()) { @@ -297,9 +301,9 @@ void AmbientOcclusionEffect::configure(const Config& config) { current.w = (float)config.borderingEnabled; } - if (config.silhouetteRadius != _aoParametersBuffer->getSilhouetteRadius()) { + if (config.falloffDistance != _aoParametersBuffer->getFalloffDistance()) { auto& current = _aoParametersBuffer.edit().ditheringInfo; - current.y = (float)config.silhouetteRadius; + current.y = (float)config.falloffDistance; } if (shouldUpdateGaussian) { @@ -450,6 +454,7 @@ void AmbientOcclusionEffect::run(const render::RenderContextPointer& renderConte #endif batch.popProfileRange(); + batch.pushProfileRange("Occlusion"); batch.setUniformBuffer(render_utils::slot::buffer::DeferredFrameTransform, frameTransform->getFrameTransformBuffer()); batch.setUniformBuffer(render_utils::slot::buffer::SsaoParams, _aoParametersBuffer); @@ -459,10 +464,11 @@ void AmbientOcclusionEffect::run(const render::RenderContextPointer& renderConte batch.setPipeline(occlusionPipeline); batch.setResourceTexture(render_utils::slot::texture::SsaoPyramid, _framebuffer->getLinearDepthTexture()); batch.draw(gpu::TRIANGLE_STRIP, 4); + batch.popProfileRange(); - /* TEMPO OP if (_aoParametersBuffer->getBlurRadius() > 0)*/ { - PROFILE_RANGE_BATCH(batch, "Blur"); - // Blur 1st pass + { + PROFILE_RANGE_BATCH(batch, "Bilateral Blur"); + // Blur 1st pass model.setScale(resolutionScale); batch.setModelTransform(model); batch.setViewportTransform(firstBlurViewport); @@ -482,8 +488,7 @@ void AmbientOcclusionEffect::run(const render::RenderContextPointer& renderConte batch.setResourceTexture(render_utils::slot::texture::SsaoOcclusion, occlusionBlurredFBO->getRenderBuffer(0)); batch.draw(gpu::TRIANGLE_STRIP, 4); } - - + batch.setResourceTexture(render_utils::slot::texture::SsaoPyramid, nullptr); batch.setResourceTexture(render_utils::slot::texture::SsaoOcclusion, nullptr); diff --git a/libraries/render-utils/src/AmbientOcclusionEffect.h b/libraries/render-utils/src/AmbientOcclusionEffect.h index 48752ba3b6..ad56aff399 100644 --- a/libraries/render-utils/src/AmbientOcclusionEffect.h +++ b/libraries/render-utils/src/AmbientOcclusionEffect.h @@ -62,8 +62,8 @@ class AmbientOcclusionEffectConfig : public render::GPUJobConfig::Persistent { Q_PROPERTY(bool fetchMipsEnabled MEMBER fetchMipsEnabled NOTIFY dirty) Q_PROPERTY(float radius MEMBER radius WRITE setRadius) Q_PROPERTY(float obscuranceLevel MEMBER obscuranceLevel WRITE setObscuranceLevel) - Q_PROPERTY(float falloffBias MEMBER falloffBias WRITE setFalloffBias) - Q_PROPERTY(float silhouetteRadius MEMBER silhouetteRadius WRITE setSilhouetteRadius) + Q_PROPERTY(float falloffAngle MEMBER falloffAngle WRITE setFalloffAngle) + Q_PROPERTY(float falloffDistance MEMBER falloffDistance WRITE setFalloffDistance) Q_PROPERTY(float edgeSharpness MEMBER edgeSharpness WRITE setEdgeSharpness) Q_PROPERTY(float blurDeviation MEMBER blurDeviation WRITE setBlurDeviation) Q_PROPERTY(float numSpiralTurns MEMBER numSpiralTurns WRITE setNumSpiralTurns) @@ -75,12 +75,12 @@ public: AmbientOcclusionEffectConfig(); const int MAX_RESOLUTION_LEVEL = 4; - const int MAX_BLUR_RADIUS = 6; + const int MAX_BLUR_RADIUS = 15; void setRadius(float newRadius) { radius = std::max(0.01f, newRadius); emit dirty(); } void setObscuranceLevel(float level) { obscuranceLevel = std::max(0.01f, level); emit dirty(); } - void setFalloffBias(float bias) { falloffBias = std::max(0.0f, std::min(bias, 0.2f)); emit dirty(); } - void setSilhouetteRadius(float value) { silhouetteRadius = std::max(0.0f, value); emit dirty(); } + void setFalloffAngle(float bias) { falloffAngle = std::max(0.0f, std::min(bias, 0.2f)); emit dirty(); } + void setFalloffDistance(float value) { falloffDistance = std::max(0.0f, value); emit dirty(); } void setEdgeSharpness(float sharpness) { edgeSharpness = std::max(0.0f, (float)sharpness); emit dirty(); } void setBlurDeviation(float deviation) { blurDeviation = std::max(0.0f, deviation); emit dirty(); } void setNumSpiralTurns(float turns) { numSpiralTurns = std::max(0.0f, (float)turns); emit dirty(); } @@ -91,8 +91,8 @@ public: float radius; float perspectiveScale; float obscuranceLevel; // intensify or dim down the obscurance effect - float falloffBias; - float silhouetteRadius; + float falloffAngle; + float falloffDistance; float edgeSharpness; float blurDeviation; float numSpiralTurns; // defining an angle span to distribute the samples ray directions @@ -134,7 +134,7 @@ public: // Blurring info glm::vec4 blurInfo; // gaussian distribution coefficients first is the sampling radius (max is 6) - const static int GAUSSIAN_COEFS_LENGTH = 8; + const static int GAUSSIAN_COEFS_LENGTH = 16; float _gaussianCoefs[GAUSSIAN_COEFS_LENGTH]; AOParameters(); @@ -143,10 +143,10 @@ public: float getRadius() const { return radiusInfo.x; } float getPerspectiveScale() const { return resolutionInfo.z; } float getObscuranceLevel() const { return radiusInfo.w; } - float getFalloffBias() const { return (float)ditheringInfo.z; } + float getFalloffAngle() const { return (float)ditheringInfo.z; } + float getFalloffDistance() const { return ditheringInfo.y; } float getEdgeSharpness() const { return (float)blurInfo.x; } float getBlurDeviation() const { return blurInfo.z; } - float getSilhouetteRadius() const { return ditheringInfo.y; } float getNumSpiralTurns() const { return sampleInfo.z; } int getNumSamples() const { return (int)sampleInfo.x; } diff --git a/libraries/render-utils/src/ssao.slh b/libraries/render-utils/src/ssao.slh index 71ad578485..b74cb740a8 100644 --- a/libraries/render-utils/src/ssao.slh +++ b/libraries/render-utils/src/ssao.slh @@ -41,7 +41,7 @@ struct AmbientOcclusionParams { vec4 _ditheringInfo; vec4 _sampleInfo; vec4 _blurInfo; - float _gaussianCoefs[8]; + float _gaussianCoefs[16]; }; layout(binding=RENDER_UTILS_BUFFER_SSAO_PARAMS) uniform ambientOcclusionParamsBuffer { @@ -75,14 +75,14 @@ float getObscuranceScaling() { float isDitheringEnabled() { return params._ditheringInfo.x; } -float getSilhouetteRadius() { +float getFalloffDistance() { return params._ditheringInfo.y; } float isBorderingEnabled() { return params._ditheringInfo.w; } -float getFalloffBias() { +float getFalloffAngle() { return params._ditheringInfo.z; } @@ -226,7 +226,7 @@ float getZEyeAtUV(vec2 texCoord, int level) { return -texture(pyramidMap, texCoord, level).x; } -const int LOG_MAX_OFFSET = 3; +const int LOG_MAX_OFFSET = 1; const int MAX_MIP_LEVEL = 5; int evalMipFromRadius(float radius) { // mipLevel = floor(log(ssR / MAX_OFFSET)); @@ -249,7 +249,7 @@ vec3 fetchTapUnfiltered(ivec4 side, ivec2 ssC, vec3 tap, vec2 imageSize) { return P; } -vec3 fetchTap(ivec4 side, ivec2 ssC, vec3 tap, vec2 imageSize) { +vec4 fetchTap(ivec4 side, ivec2 ssC, vec3 tap, vec2 imageSize) { int mipLevel = evalMipFromRadius(tap.z * float(doFetchMips())); vec2 ssP = tap.xy + vec2(ssC); @@ -259,15 +259,13 @@ vec3 fetchTap(ivec4 side, ivec2 ssC, vec3 tap, vec2 imageSize) { vec2 tapUV = (vec2(ssP) + vec2(0.5)) / imageSize; vec2 fetchUV = vec2(tapUV.x + side.w * 0.5 * (side.x - tapUV.x), tapUV.y); - vec3 P; + vec4 P; P.xy = tapUV; - P.z = -textureLod(pyramidMap, fetchUV, float(mipLevel)).x; - + P.w = float(mipLevel); + P.z = -textureLod(pyramidMap, fetchUV, P.w).x; return P; } - - <@endfunc@> @@ -289,59 +287,63 @@ float evalVisibilitySSAO(in vec3 centerPosition, in vec3 centerNormal, in vec3 t // Fall off function as recommended in SSAO paper const float epsilon = 0.01; float f = max(getRadius2() - vv, 0.0); - return f * f * f * max((vn - getFalloffBias()) / (epsilon + vv), 0.0); + return f * f * f * max((vn - getFalloffAngle()) / (epsilon + vv), 0.0); } +float computeHorizonFromTap(vec3 tapPositionES, vec3 fragPositionES, vec3 fragNormalES, float normalizedRadius) { + vec3 deltaVec = tapPositionES - fragPositionES; + float distance = abs(deltaVec.z); + float rawHorizon = dot(normalize(deltaVec), fragNormalES); + + rawHorizon = (rawHorizon - getFalloffAngle()) / (1.0 - getFalloffAngle()); + rawHorizon *= 1.0 - normalizedRadius * normalizedRadius; + rawHorizon *= 1.0 - smoothstep(getFalloffDistance()/2, getFalloffDistance(), distance); + + return rawHorizon; +} + +<@func computeHorizon()@> + vec3 tap = vec3(tapPixelPos, radius); + vec4 tapUVZ_mip = fetchTap(side, centerPixelPos, tap, imageSize); + vec3 tapPositionES = evalEyePositionFromZeye(side.x, tapUVZ_mip.z, tapUVZ_mip.xy); + float rawHorizon = computeHorizonFromTap(tapPositionES, fragPositionES, fragNormalES, radius / searchRadius); + + <$horizon$> = max(<$horizon$>, rawHorizon); +<@endfunc@> + +#define SSAO_LINEAR_SAMPLING 0 + <@func updateHorizon(horizon, deltaPixelTap)@> { - int stepIndex; vec2 tapPixelPos = vec2(0); - float radius = 0.0; - for (stepIndex=stepCount ; stepIndex>0 ; stepIndex--) { - tapPixelPos += deltaPixelTap; +#if !SSAO_LINEAR_SAMPLING + float radius = deltaRadius; + float mipLevel = evalMipFromRadius(radius * float(doFetchMips())); + + while (radius; + + <$computeHorizon()$> + + if (tapUVZ_mip.w != mipLevel) { + mipLevel = tapUVZ_mip.w; + deltaRadius *= 2; + <$deltaPixelTap$> *= 2; + } + radius += deltaRadius; + } +#else + float radius = 0.0; + int stepIndex; + + for (stepIndex=0 ; stepIndex; radius += deltaRadius; - vec3 tap = vec3(tapPixelPos, radius); - vec3 tapUVZ = fetchTap(side, centerPixelPos, tap, imageSize); - vec3 tapPositionES = evalEyePositionFromZeye(side.x, tapUVZ.z, tapUVZ.xy); - vec3 deltaVec = tapPositionES - fragPositionES; - float deltaVecHeight = dot(deltaVec, fragNormalES); - float rawHorizon = dot(normalize(deltaVec), fragNormalES); - float falloff = max(0.0, 1.0 - deltaVecHeight / getSilhouetteRadius()); - - rawHorizon = rawHorizon < getFalloffBias() ? 0.0f : rawHorizon; - rawHorizon *= falloff * falloff; - - <$horizon$> = max(<$horizon$>, rawHorizon); + <$computeHorizon()$> } - } -<@endfunc@> - -<@func searchBresenhamHorizon(deltaPixelCoord)@> - { - float epsilon = 1e-8; - vec2 deltaPixelTap = searchVec / absSearchVec.<$deltaPixelCoord$>; - vec2 absDeltaPixelTap = abs(deltaPixelTap); - bvec2 nullDelta = absDeltaPixelTap < epsilon; - - pixelDelta1 = mix(pixelDelta1, vec2(1.0), pixelDelta1 < epsilon && nullDelta); - pixelDelta2 = mix(pixelDelta2, vec2(1.0), pixelDelta2 < epsilon && nullDelta); - - pixelDelta1 = ceil(pixelDelta1 / absDeltaPixelTap); - pixelDelta2 = ceil(pixelDelta2 / absDeltaPixelTap); - - int maxStepCount = max(0, int(ceil(absSearchVec.<$deltaPixelCoord$>))); - float deltaRadius = ssDiskRadius / maxStepCount; - int stepCount; - - // Forward search for h1 - stepCount = clamp(int(min(pixelDelta1.x, pixelDelta1.y)), 0, maxStepCount); - <$updateHorizon(horizons.x, deltaPixelTap)$> - - // Backward search for h2 - stepCount = clamp(int(min(pixelDelta2.x, pixelDelta2.y)), 0, maxStepCount); - <$updateHorizon(horizons.y, -deltaPixelTap)$> +#endif } <@endfunc@> @@ -379,7 +381,8 @@ vec2 searchHorizons(ivec4 side, ivec2 centerPixelPos, vec2 imageSize, vec2 delta int stepCount = int(max(absClampedSearchVec.x, absClampedSearchVec.y)); if (stepCount>0) { vec2 deltaPixelTap = clampedSearchVec / float(stepCount); - float deltaRadius = length(deltaPixelTap); + float searchRadius = length(clampedSearchVec); + float deltaRadius = searchRadius / float(stepCount); <$updateHorizon(horizons.x, deltaPixelTap)$> } // Backward search for h2 @@ -388,32 +391,11 @@ vec2 searchHorizons(ivec4 side, ivec2 centerPixelPos, vec2 imageSize, vec2 delta stepCount = int(max(absClampedSearchVec.x, absClampedSearchVec.y)); if (stepCount>0) { vec2 deltaPixelTap = clampedSearchVec / float(stepCount); - float deltaRadius = length(deltaPixelTap); + float searchRadius = length(clampedSearchVec); + float deltaRadius = searchRadius / float(stepCount); <$updateHorizon(horizons.y, deltaPixelTap)$> } -// -// absSearchVec.y) { - <$searchBresenhamHorizon(x)$> - } else { - <$searchBresenhamHorizon(y)$> - } - - vec3 angles = acos(vec3(horizons, fragNormalES.z)) - PI/2.0; - angles.x = -angles.x; - // Clamp to limit horizon defined by normal plane - horizons.xy = angles.zz + max(angles.xy - angles.zz, vec2(-PI/2.0, PI/2.0)); - */ - -// -// !> -// - return horizons; } @@ -460,7 +442,7 @@ float fetchOcclusion(vec2 coords) { } const float BLUR_WEIGHT_OFFSET = 0.01; -const float BLUR_EDGE_SCALE = 10.0; +const float BLUR_EDGE_SCALE = 300.0; vec2 evalTapWeightedValue(ivec3 side, int r, ivec2 destPixelCoord, vec2 scaledTexCoord, vec2 fullTexCoord, float key) { ivec2 tapOffset = <$axis$> * r; @@ -479,7 +461,9 @@ vec2 evalTapWeightedValue(ivec3 side, int r, ivec2 destPixelCoord, vec2 scaledTe float weight = BLUR_WEIGHT_OFFSET + getBlurCoef(abs(r)); // range domain (the "bilateral" weight). As depth difference increases, decrease weight. - weight *= max(0.0, 1.0 - (getBlurEdgeSharpness() * BLUR_EDGE_SCALE) * abs(tapDepth - key)); +// weight *= max(0.0, 1.0 - (getBlurEdgeSharpness() * BLUR_EDGE_SCALE) * abs(tapDepth - key)); + float zDistance = tapDepth - key; + weight *= exp(-(getBlurEdgeSharpness() * BLUR_EDGE_SCALE) * zDistance * zDistance); return vec2(tapOcclusion * weight, weight); } diff --git a/libraries/render-utils/src/ssao_makeOcclusion.slf b/libraries/render-utils/src/ssao_makeOcclusion.slf index ba01241ce7..fc974a7baa 100644 --- a/libraries/render-utils/src/ssao_makeOcclusion.slf +++ b/libraries/render-utils/src/ssao_makeOcclusion.slf @@ -20,6 +20,7 @@ <$declarePackOcclusionDepth()$> #define SSAO_USE_HORIZON_BASED 1 +#define SSAO_HBAO_MAX_RADIUS 100.0 layout(location=0) out vec4 outFragColor; @@ -46,6 +47,9 @@ void main(void) { // Choose the screen-space sample radius float ssDiskRadius = evalDiskRadius(fragPositionES.z, imageSize); +#if SSAO_USE_HORIZON_BASED + ssDiskRadius = min(ssDiskRadius, SSAO_HBAO_MAX_RADIUS); +#endif // Let's make noise float randomPatternRotationAngle = getAngleDithering(centerPixelPos); diff --git a/scripts/developer/utilities/render/ambientOcclusionPass.qml b/scripts/developer/utilities/render/ambientOcclusionPass.qml index 1e5a46f115..ca00849706 100644 --- a/scripts/developer/utilities/render/ambientOcclusionPass.qml +++ b/scripts/developer/utilities/render/ambientOcclusionPass.qml @@ -37,10 +37,10 @@ Rectangle { "Level:obscuranceLevel:1.0:false", "Num Taps:numSamples:32:true", "Taps Spiral:numSpiralTurns:10.0:false", - "Falloff Bias:falloffBias:0.2:false", - "Silhouette Radius:silhouetteRadius:1.0:false", + "Falloff Angle:falloffAngle:0.2:false", + "Falloff Distance:falloffDistance:2.0:false", "Blur Edge Sharpness:edgeSharpness:1.0:false", - "Blur Radius:blurRadius:10.0:false", + "Blur Radius:blurRadius:15.0:false", ] ConfigSlider { label: qsTr(modelData.split(":")[0]) From ad5064b045e88509797310d15b0c574e455240e7 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Mon, 17 Sep 2018 14:33:48 +0200 Subject: [PATCH 016/286] Removed falloff distance from shader --- .../render-utils/src/AmbientOcclusionEffect.h | 2 +- libraries/render-utils/src/ssao.slh | 14 +++++++------- libraries/render-utils/src/ssao_makeOcclusion.slf | 3 +-- .../utilities/render/ambientOcclusionPass.qml | 2 +- 4 files changed, 10 insertions(+), 11 deletions(-) diff --git a/libraries/render-utils/src/AmbientOcclusionEffect.h b/libraries/render-utils/src/AmbientOcclusionEffect.h index ad56aff399..81ceee9bf6 100644 --- a/libraries/render-utils/src/AmbientOcclusionEffect.h +++ b/libraries/render-utils/src/AmbientOcclusionEffect.h @@ -79,7 +79,7 @@ public: void setRadius(float newRadius) { radius = std::max(0.01f, newRadius); emit dirty(); } void setObscuranceLevel(float level) { obscuranceLevel = std::max(0.01f, level); emit dirty(); } - void setFalloffAngle(float bias) { falloffAngle = std::max(0.0f, std::min(bias, 0.2f)); emit dirty(); } + void setFalloffAngle(float bias) { falloffAngle = std::max(0.0f, std::min(bias, 1.0f)); emit dirty(); } void setFalloffDistance(float value) { falloffDistance = std::max(0.0f, value); emit dirty(); } void setEdgeSharpness(float sharpness) { edgeSharpness = std::max(0.0f, (float)sharpness); emit dirty(); } void setBlurDeviation(float deviation) { blurDeviation = std::max(0.0f, deviation); emit dirty(); } diff --git a/libraries/render-utils/src/ssao.slh b/libraries/render-utils/src/ssao.slh index b74cb740a8..88d8f08e7a 100644 --- a/libraries/render-utils/src/ssao.slh +++ b/libraries/render-utils/src/ssao.slh @@ -290,14 +290,14 @@ float evalVisibilitySSAO(in vec3 centerPosition, in vec3 centerNormal, in vec3 t return f * f * f * max((vn - getFalloffAngle()) / (epsilon + vv), 0.0); } -float computeHorizonFromTap(vec3 tapPositionES, vec3 fragPositionES, vec3 fragNormalES, float normalizedRadius) { +float computeHorizonFromTap(vec3 tapPositionES, vec3 fragPositionES, vec3 fragNormalES) { vec3 deltaVec = tapPositionES - fragPositionES; - float distance = abs(deltaVec.z); + float distanceSquared = dot(deltaVec, deltaVec); float rawHorizon = dot(normalize(deltaVec), fragNormalES); + float radiusFalloff = distanceSquared / getRadius2(); - rawHorizon = (rawHorizon - getFalloffAngle()) / (1.0 - getFalloffAngle()); - rawHorizon *= 1.0 - normalizedRadius * normalizedRadius; - rawHorizon *= 1.0 - smoothstep(getFalloffDistance()/2, getFalloffDistance(), distance); + rawHorizon = max(0.0, (rawHorizon - getFalloffAngle()) / (1.0 - getFalloffAngle())); + rawHorizon *= 1.0 - radiusFalloff; return rawHorizon; } @@ -306,12 +306,12 @@ float computeHorizonFromTap(vec3 tapPositionES, vec3 fragPositionES, vec3 fragNo vec3 tap = vec3(tapPixelPos, radius); vec4 tapUVZ_mip = fetchTap(side, centerPixelPos, tap, imageSize); vec3 tapPositionES = evalEyePositionFromZeye(side.x, tapUVZ_mip.z, tapUVZ_mip.xy); - float rawHorizon = computeHorizonFromTap(tapPositionES, fragPositionES, fragNormalES, radius / searchRadius); + float rawHorizon = computeHorizonFromTap(tapPositionES, fragPositionES, fragNormalES); <$horizon$> = max(<$horizon$>, rawHorizon); <@endfunc@> -#define SSAO_LINEAR_SAMPLING 0 +#define SSAO_LINEAR_SAMPLING 1 <@func updateHorizon(horizon, deltaPixelTap)@> { diff --git a/libraries/render-utils/src/ssao_makeOcclusion.slf b/libraries/render-utils/src/ssao_makeOcclusion.slf index fc974a7baa..c81644ce58 100644 --- a/libraries/render-utils/src/ssao_makeOcclusion.slf +++ b/libraries/render-utils/src/ssao_makeOcclusion.slf @@ -69,8 +69,7 @@ void main(void) { #endif } - visibilitySum = visibilitySum * getInvNumSamples(); - float occlusion = clamp(1.0 - visibilitySum * getObscuranceScaling(), 0.0, 1.0); + float occlusion = clamp(1.0 - visibilitySum * getObscuranceScaling() * getInvNumSamples(), 0.0, 1.0); // KEEP IT for Debugging // Bilateral box-filter over a quad for free, respecting depth edges diff --git a/scripts/developer/utilities/render/ambientOcclusionPass.qml b/scripts/developer/utilities/render/ambientOcclusionPass.qml index ca00849706..0416443139 100644 --- a/scripts/developer/utilities/render/ambientOcclusionPass.qml +++ b/scripts/developer/utilities/render/ambientOcclusionPass.qml @@ -37,7 +37,7 @@ Rectangle { "Level:obscuranceLevel:1.0:false", "Num Taps:numSamples:32:true", "Taps Spiral:numSpiralTurns:10.0:false", - "Falloff Angle:falloffAngle:0.2:false", + "Falloff Angle:falloffAngle:0.5:false", "Falloff Distance:falloffDistance:2.0:false", "Blur Edge Sharpness:edgeSharpness:1.0:false", "Blur Radius:blurRadius:15.0:false", From d9e2c8df69d8ac703033aebad1aa39450988ae1a Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Mon, 17 Sep 2018 15:06:00 +0200 Subject: [PATCH 017/286] Fixed uniform buffer slot index in DebugAmbientOcclusion --- libraries/render-utils/src/AmbientOcclusionEffect.cpp | 7 ++++--- libraries/render-utils/src/ssao.slh | 11 ++++------- libraries/render-utils/src/ssao_makeOcclusion.slf | 3 ++- 3 files changed, 10 insertions(+), 11 deletions(-) diff --git a/libraries/render-utils/src/AmbientOcclusionEffect.cpp b/libraries/render-utils/src/AmbientOcclusionEffect.cpp index 381f015f9e..44a5cca9b7 100644 --- a/libraries/render-utils/src/AmbientOcclusionEffect.cpp +++ b/libraries/render-utils/src/AmbientOcclusionEffect.cpp @@ -78,12 +78,13 @@ void AmbientOcclusionFramebuffer::allocate() { auto width = _frameSize.x; auto height = _frameSize.y; + auto format = gpu::Element::COLOR_RGBA_32; - _occlusionTexture = gpu::Texture::createRenderBuffer(gpu::Element::COLOR_RGBA_32, width, height, gpu::Texture::SINGLE_MIP, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR_MIP_POINT)); + _occlusionTexture = gpu::Texture::createRenderBuffer(format, width, height, gpu::Texture::SINGLE_MIP, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR_MIP_POINT)); _occlusionFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("occlusion")); _occlusionFramebuffer->setRenderBuffer(0, _occlusionTexture); - _occlusionBlurredTexture = gpu::Texture::createRenderBuffer(gpu::Element::COLOR_RGBA_32, width, height, gpu::Texture::SINGLE_MIP, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR_MIP_POINT)); + _occlusionBlurredTexture = gpu::Texture::createRenderBuffer(format, width, height, gpu::Texture::SINGLE_MIP, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR_MIP_POINT)); _occlusionBlurredFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("occlusionBlurred")); _occlusionBlurredFramebuffer->setRenderBuffer(0, _occlusionBlurredTexture); } @@ -582,7 +583,7 @@ void DebugAmbientOcclusion::run(const render::RenderContextPointer& renderContex batch.setUniformBuffer(render_utils::slot::buffer::DeferredFrameTransform, frameTransform->getFrameTransformBuffer()); batch.setUniformBuffer(render_utils::slot::buffer::SsaoParams, ambientOcclusionUniforms); - batch.setUniformBuffer(2, _parametersBuffer); + batch.setUniformBuffer(render_utils::slot::buffer::SsaoDebugParams, _parametersBuffer); batch.setPipeline(debugPipeline); batch.setResourceTexture(render_utils::slot::texture::SsaoPyramid, linearDepthTexture); diff --git a/libraries/render-utils/src/ssao.slh b/libraries/render-utils/src/ssao.slh index 88d8f08e7a..cea4caa8e7 100644 --- a/libraries/render-utils/src/ssao.slh +++ b/libraries/render-utils/src/ssao.slh @@ -297,7 +297,7 @@ float computeHorizonFromTap(vec3 tapPositionES, vec3 fragPositionES, vec3 fragNo float radiusFalloff = distanceSquared / getRadius2(); rawHorizon = max(0.0, (rawHorizon - getFalloffAngle()) / (1.0 - getFalloffAngle())); - rawHorizon *= 1.0 - radiusFalloff; + rawHorizon *= max(0.0, 1.0 - radiusFalloff); return rawHorizon; } @@ -315,6 +315,9 @@ float computeHorizonFromTap(vec3 tapPositionES, vec3 fragPositionES, vec3 fragNo <@func updateHorizon(horizon, deltaPixelTap)@> { + vec2 deltaPixelTap = clampedSearchVec / float(stepCount); + float searchRadius = length(clampedSearchVec); + float deltaRadius = searchRadius / float(stepCount); vec2 tapPixelPos = vec2(0); #if !SSAO_LINEAR_SAMPLING @@ -380,9 +383,6 @@ vec2 searchHorizons(ivec4 side, ivec2 centerPixelPos, vec2 imageSize, vec2 delta vec2 absClampedSearchVec = abs(clampedSearchVec); int stepCount = int(max(absClampedSearchVec.x, absClampedSearchVec.y)); if (stepCount>0) { - vec2 deltaPixelTap = clampedSearchVec / float(stepCount); - float searchRadius = length(clampedSearchVec); - float deltaRadius = searchRadius / float(stepCount); <$updateHorizon(horizons.x, deltaPixelTap)$> } // Backward search for h2 @@ -390,9 +390,6 @@ vec2 searchHorizons(ivec4 side, ivec2 centerPixelPos, vec2 imageSize, vec2 delta absClampedSearchVec = abs(clampedSearchVec); stepCount = int(max(absClampedSearchVec.x, absClampedSearchVec.y)); if (stepCount>0) { - vec2 deltaPixelTap = clampedSearchVec / float(stepCount); - float searchRadius = length(clampedSearchVec); - float deltaRadius = searchRadius / float(stepCount); <$updateHorizon(horizons.y, deltaPixelTap)$> } diff --git a/libraries/render-utils/src/ssao_makeOcclusion.slf b/libraries/render-utils/src/ssao_makeOcclusion.slf index c81644ce58..ee22590679 100644 --- a/libraries/render-utils/src/ssao_makeOcclusion.slf +++ b/libraries/render-utils/src/ssao_makeOcclusion.slf @@ -81,5 +81,6 @@ void main(void) { occlusion -= dFdy(occlusion) * ((centerPixelPos.y & 1) - 0.5); }*/ - outFragColor = vec4(packOcclusionDepth(occlusion, CSZToDepthKey(fragPositionES.z)), 1.0); + //outFragColor = vec4(packOcclusionDepth(occlusion, CSZToDepthKey(fragPositionES.z)), 1.0); + outFragColor = vec4(vec3(occlusion), 1.0); } From d0eef1b8d06769153ff9f327dba4fcf345845f4c Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Mon, 17 Sep 2018 16:12:54 +0200 Subject: [PATCH 018/286] Some cleanup --- .../src/AmbientOcclusionEffect.cpp | 4 +- libraries/render-utils/src/ssao.slh | 114 +++++++++--------- 2 files changed, 60 insertions(+), 58 deletions(-) diff --git a/libraries/render-utils/src/AmbientOcclusionEffect.cpp b/libraries/render-utils/src/AmbientOcclusionEffect.cpp index 44a5cca9b7..70f36521d8 100644 --- a/libraries/render-utils/src/AmbientOcclusionEffect.cpp +++ b/libraries/render-utils/src/AmbientOcclusionEffect.cpp @@ -80,11 +80,11 @@ void AmbientOcclusionFramebuffer::allocate() { auto height = _frameSize.y; auto format = gpu::Element::COLOR_RGBA_32; - _occlusionTexture = gpu::Texture::createRenderBuffer(format, width, height, gpu::Texture::SINGLE_MIP, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR_MIP_POINT)); + _occlusionTexture = gpu::Texture::createRenderBuffer(format, width, height, gpu::Texture::SINGLE_MIP, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR_MIP_POINT, gpu::Sampler::WRAP_CLAMP)); _occlusionFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("occlusion")); _occlusionFramebuffer->setRenderBuffer(0, _occlusionTexture); - _occlusionBlurredTexture = gpu::Texture::createRenderBuffer(format, width, height, gpu::Texture::SINGLE_MIP, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR_MIP_POINT)); + _occlusionBlurredTexture = gpu::Texture::createRenderBuffer(format, width, height, gpu::Texture::SINGLE_MIP, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR_MIP_POINT, gpu::Sampler::WRAP_CLAMP)); _occlusionBlurredFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("occlusionBlurred")); _occlusionBlurredFramebuffer->setRenderBuffer(0, _occlusionBlurredTexture); } diff --git a/libraries/render-utils/src/ssao.slh b/libraries/render-utils/src/ssao.slh index cea4caa8e7..8f27f1f445 100644 --- a/libraries/render-utils/src/ssao.slh +++ b/libraries/render-utils/src/ssao.slh @@ -293,66 +293,29 @@ float evalVisibilitySSAO(in vec3 centerPosition, in vec3 centerNormal, in vec3 t float computeHorizonFromTap(vec3 tapPositionES, vec3 fragPositionES, vec3 fragNormalES) { vec3 deltaVec = tapPositionES - fragPositionES; float distanceSquared = dot(deltaVec, deltaVec); - float rawHorizon = dot(normalize(deltaVec), fragNormalES); - float radiusFalloff = distanceSquared / getRadius2(); + float horizon = dot(normalize(deltaVec), fragNormalES); + float radiusFalloff = max(0.0, 1.0 - (distanceSquared / getRadius2())); - rawHorizon = max(0.0, (rawHorizon - getFalloffAngle()) / (1.0 - getFalloffAngle())); - rawHorizon *= max(0.0, 1.0 - radiusFalloff); + horizon = max(0.0, (horizon - getFalloffAngle()) / (1.0 - getFalloffAngle())); + horizon *= radiusFalloff; - return rawHorizon; + return horizon; } <@func computeHorizon()@> vec3 tap = vec3(tapPixelPos, radius); vec4 tapUVZ_mip = fetchTap(side, centerPixelPos, tap, imageSize); vec3 tapPositionES = evalEyePositionFromZeye(side.x, tapUVZ_mip.z, tapUVZ_mip.xy); - float rawHorizon = computeHorizonFromTap(tapPositionES, fragPositionES, fragNormalES); + float tapCosHorizonAngle = computeHorizonFromTap(tapPositionES, fragPositionES, fragNormalES); - <$horizon$> = max(<$horizon$>, rawHorizon); + cosHorizonAngle = max(cosHorizonAngle, tapCosHorizonAngle); <@endfunc@> #define SSAO_LINEAR_SAMPLING 1 -<@func updateHorizon(horizon, deltaPixelTap)@> - { - vec2 deltaPixelTap = clampedSearchVec / float(stepCount); - float searchRadius = length(clampedSearchVec); - float deltaRadius = searchRadius / float(stepCount); - vec2 tapPixelPos = vec2(0); - -#if !SSAO_LINEAR_SAMPLING - float radius = deltaRadius; - float mipLevel = evalMipFromRadius(radius * float(doFetchMips())); - - while (radius; - - <$computeHorizon()$> - - if (tapUVZ_mip.w != mipLevel) { - mipLevel = tapUVZ_mip.w; - deltaRadius *= 2; - <$deltaPixelTap$> *= 2; - } - radius += deltaRadius; - } -#else - float radius = 0.0; - int stepIndex; - - for (stepIndex=0 ; stepIndex; - radius += deltaRadius; - - <$computeHorizon()$> - } -#endif - } -<@endfunc@> - vec2 clampSearchVec(vec2 imageSize, vec2 centerPixelPos, vec2 searchVec) { vec2 clampdSearchVec = searchVec; - vec2 endPixel = centerPixelPos + clampdSearchVec; +/* TEMPO OP vec2 endPixel = centerPixelPos + clampdSearchVec; if (endPixel.x < 0) { clampdSearchVec = clampdSearchVec * ((0-centerPixelPos.x) / clampdSearchVec.x); @@ -368,11 +331,55 @@ vec2 clampSearchVec(vec2 imageSize, vec2 centerPixelPos, vec2 searchVec) { } if (endPixel.y >= imageSize.y) { clampdSearchVec = clampdSearchVec * ((imageSize.y-1-centerPixelPos.y) / clampdSearchVec.y); - } + }*/ return clampdSearchVec; } +float computeHorizon(ivec4 side, ivec2 centerPixelPos, vec2 imageSize, vec2 deltaTap, float ssDiskRadius, + vec3 fragPositionES, vec3 fragNormalES, vec2 clampedSearchVec) { + vec2 absClampedSearchVec = abs(clampedSearchVec); + int stepCount = int(max(absClampedSearchVec.x, absClampedSearchVec.y)); + float cosHorizonAngle = 0.0; + + if (stepCount>0) { + vec2 deltaPixelTap = clampedSearchVec / float(stepCount); + float searchRadius = length(clampedSearchVec); + float deltaRadius = searchRadius / float(stepCount); + vec2 tapPixelPos = vec2(0); + +#if !SSAO_LINEAR_SAMPLING + float radius = deltaRadius; + float mipLevel = evalMipFromRadius(radius * float(doFetchMips())); + + while (radius + + if (tapUVZ_mip.w != mipLevel) { + mipLevel = tapUVZ_mip.w; + deltaRadius *= 2; + deltaPixelTap *= 2; + } + radius += deltaRadius; + } +#else + float radius = 0.0; + int stepIndex; + + for (stepIndex=0 ; stepIndex + } +#endif + } + + return cosHorizonAngle; +} + vec2 searchHorizons(ivec4 side, ivec2 centerPixelPos, vec2 imageSize, vec2 deltaTap, float ssDiskRadius, vec3 fragPositionES, vec3 fragNormalES) { vec2 searchVec = deltaTap * ssDiskRadius; @@ -380,18 +387,13 @@ vec2 searchHorizons(ivec4 side, ivec2 centerPixelPos, vec2 imageSize, vec2 delta // Forward search for h1 vec2 clampedSearchVec = clampSearchVec(imageSize, vec2(centerPixelPos), searchVec); - vec2 absClampedSearchVec = abs(clampedSearchVec); - int stepCount = int(max(absClampedSearchVec.x, absClampedSearchVec.y)); - if (stepCount>0) { - <$updateHorizon(horizons.x, deltaPixelTap)$> - } + horizons.x = computeHorizon(side, centerPixelPos, imageSize, deltaTap, ssDiskRadius, + fragPositionES, fragNormalES, clampedSearchVec); + // Backward search for h2 clampedSearchVec = clampSearchVec(imageSize, vec2(centerPixelPos), -searchVec); - absClampedSearchVec = abs(clampedSearchVec); - stepCount = int(max(absClampedSearchVec.x, absClampedSearchVec.y)); - if (stepCount>0) { - <$updateHorizon(horizons.y, deltaPixelTap)$> - } + horizons.y = computeHorizon(side, centerPixelPos, imageSize, deltaTap, ssDiskRadius, + fragPositionES, fragNormalES, clampedSearchVec); return horizons; } From 0f467ceeb955da267e20c9b3fba4e78e8b8ce361 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Mon, 17 Sep 2018 17:19:07 +0200 Subject: [PATCH 019/286] Border clamping for linear depth texture --- .../render-utils/src/SurfaceGeometryPass.cpp | 4 +- libraries/render-utils/src/ssao.slh | 146 +++++++++--------- .../render-utils/src/ssao_makeOcclusion.slf | 41 ++--- 3 files changed, 93 insertions(+), 98 deletions(-) diff --git a/libraries/render-utils/src/SurfaceGeometryPass.cpp b/libraries/render-utils/src/SurfaceGeometryPass.cpp index b0f19862ce..f22b8351d4 100644 --- a/libraries/render-utils/src/SurfaceGeometryPass.cpp +++ b/libraries/render-utils/src/SurfaceGeometryPass.cpp @@ -66,7 +66,7 @@ void LinearDepthFramebuffer::allocate() { // For Linear Depth: const uint16_t LINEAR_DEPTH_MAX_MIP_LEVEL = 5; _linearDepthTexture = gpu::Texture::createRenderBuffer(gpu::Element(gpu::SCALAR, gpu::FLOAT, gpu::RED), width, height, LINEAR_DEPTH_MAX_MIP_LEVEL, - gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR_MIP_POINT)); + gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR_MIP_POINT, gpu::Sampler::WRAP_CLAMP)); _linearDepthFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("linearDepth")); _linearDepthFramebuffer->setRenderBuffer(0, _linearDepthTexture); _linearDepthFramebuffer->setDepthStencilBuffer(_primaryDepthTexture, _primaryDepthTexture->getTexelFormat()); @@ -74,7 +74,7 @@ void LinearDepthFramebuffer::allocate() { // For Downsampling: const uint16_t HALF_LINEAR_DEPTH_MAX_MIP_LEVEL = LINEAR_DEPTH_MAX_MIP_LEVEL; _halfLinearDepthTexture = gpu::Texture::createRenderBuffer(gpu::Element(gpu::SCALAR, gpu::FLOAT, gpu::RED), _halfFrameSize.x, _halfFrameSize.y, HALF_LINEAR_DEPTH_MAX_MIP_LEVEL, - gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR_MIP_POINT)); + gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR_MIP_POINT, gpu::Sampler::WRAP_CLAMP)); _halfNormalTexture = gpu::Texture::createRenderBuffer(gpu::Element::COLOR_RGBA_32, _halfFrameSize.x, _halfFrameSize.y, gpu::Texture::SINGLE_MIP, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR_MIP_POINT)); diff --git a/libraries/render-utils/src/ssao.slh b/libraries/render-utils/src/ssao.slh index 8f27f1f445..b441d7ca8c 100644 --- a/libraries/render-utils/src/ssao.slh +++ b/libraries/render-utils/src/ssao.slh @@ -148,15 +148,15 @@ float getAngleDithering(in ivec2 pixelPos) { return isDitheringEnabled() * float((3 * pixelPos.x ^ pixelPos.y + pixelPos.x * pixelPos.y) * 10); } -float evalDiskRadius(float Zeye, vec2 imageSize) { +float evalDiskRadius(float Zeye, vec2 sideImageSize) { // Choose the screen-space sample radius // proportional to the projected area of the sphere - float ssDiskRadius = -( getProjScale(getResolutionLevel()) * getRadius() / Zeye ) * getPerspectiveScale(); + float diskPixelRadius = -( getProjScale(getResolutionLevel()) * getRadius() / Zeye ) * getPerspectiveScale(); // clamp the disk to fit in the image otherwise too many unknown - ssDiskRadius = min(ssDiskRadius, imageSize.y * 0.5); + diskPixelRadius = min(diskPixelRadius, sideImageSize.y * 0.5); - return ssDiskRadius; + return diskPixelRadius; } const float PI = 3.1415926; @@ -176,7 +176,7 @@ vec3 getTapLocationSSAO(int sampleNumber, float spinAngle, float outerRadius) { return tap; } -vec3 getTapLocationClampedSSAO(int sampleNumber, float spinAngle, float outerRadius, vec2 pixelPos, vec2 imageSize) { +vec3 getTapLocationClampedSSAO(int sampleNumber, float spinAngle, float outerRadius, vec2 pixelPos, vec2 sideImageSize) { vec3 tap = getTapLocationSSAO(sampleNumber, spinAngle, outerRadius); vec2 tapPos = pixelPos + tap.xy; @@ -188,16 +188,16 @@ vec3 getTapLocationClampedSSAO(int sampleNumber, float spinAngle, float outerRad if ((tapPos.x < 0.5)) { tapPos.x = -tapPos.x; redoTap = true; - } else if ((tapPos.x > imageSize.x - 0.5)) { - tapPos.x -= (imageSize.x - tapPos.x); + } else if ((tapPos.x > sideImageSize.x - 0.5)) { + tapPos.x -= (sideImageSize.x - tapPos.x); redoTap = true; } if ((tapPos.y < 0.5)) { tapPos.y = -tapPos.y; redoTap = true; - } else if ((tapPos.y > imageSize.y - 0.5)) { - tapPos.y -= (imageSize.y - tapPos.y); + } else if ((tapPos.y > sideImageSize.y - 0.5)) { + tapPos.y -= (sideImageSize.y - tapPos.y); redoTap = true; } @@ -233,12 +233,12 @@ int evalMipFromRadius(float radius) { return clamp(findMSB(int(radius)) - LOG_MAX_OFFSET, 0, MAX_MIP_LEVEL); } -vec3 fetchTapUnfiltered(ivec4 side, ivec2 ssC, vec3 tap, vec2 imageSize) { +vec3 fetchTapUnfiltered(ivec4 side, ivec2 ssC, vec3 tap, vec2 sideImageSize) { ivec2 ssP = ivec2(tap.xy) + ssC; ivec2 ssPFull = ivec2(ssP.x + side.y, ssP.y); - vec2 tapUV = (vec2(ssP) + vec2(0.5)) / imageSize; + vec2 tapUV = (vec2(ssP) + vec2(0.5)) / sideImageSize; vec2 fetchUV = vec2(tapUV.x + float(side.w) * 0.5 * (float(side.x) - tapUV.x), tapUV.y); @@ -249,14 +249,14 @@ vec3 fetchTapUnfiltered(ivec4 side, ivec2 ssC, vec3 tap, vec2 imageSize) { return P; } -vec4 fetchTap(ivec4 side, ivec2 ssC, vec3 tap, vec2 imageSize) { +vec4 fetchTap(ivec4 side, ivec2 ssC, vec3 tap, vec2 sideImageSize) { int mipLevel = evalMipFromRadius(tap.z * float(doFetchMips())); vec2 ssP = tap.xy + vec2(ssC); // We need to divide by 2^mipLevel to read the appropriately scaled coordinate from a MIP-map. // Manually clamp to the texture size because texelFetch bypasses the texture unit - vec2 tapUV = (vec2(ssP) + vec2(0.5)) / imageSize; + vec2 tapUV = (vec2(ssP) + vec2(0.5)) / sideImageSize; vec2 fetchUV = vec2(tapUV.x + side.w * 0.5 * (side.x - tapUV.x), tapUV.y); vec4 P; @@ -302,53 +302,67 @@ float computeHorizonFromTap(vec3 tapPositionES, vec3 fragPositionES, vec3 fragNo return horizon; } -<@func computeHorizon()@> - vec3 tap = vec3(tapPixelPos, radius); - vec4 tapUVZ_mip = fetchTap(side, centerPixelPos, tap, imageSize); - vec3 tapPositionES = evalEyePositionFromZeye(side.x, tapUVZ_mip.z, tapUVZ_mip.xy); - float tapCosHorizonAngle = computeHorizonFromTap(tapPositionES, fragPositionES, fragNormalES); +vec2 clampSearchVector(vec2 sideImageSize, vec2 shadedPixelPos, vec2 searchVec) { + vec2 clampedSearchVec = searchVec; + vec2 endPixel = shadedPixelPos + clampedSearchVec; - cosHorizonAngle = max(cosHorizonAngle, tapCosHorizonAngle); + if (endPixel.x < 0) { + clampedSearchVec *= ((0-shadedPixelPos.x) / clampedSearchVec.x); + endPixel = shadedPixelPos + clampedSearchVec; + } + if (endPixel.x > (sideImageSize.x-1)) { + clampedSearchVec *= ((sideImageSize.x-1-shadedPixelPos.x) / clampedSearchVec.x); + endPixel = shadedPixelPos + clampedSearchVec; + } + if (endPixel.y < 0) { + clampedSearchVec *= ((0-shadedPixelPos.y) / clampedSearchVec.y); + endPixel = shadedPixelPos + clampedSearchVec; + } + if (endPixel.y > (sideImageSize.y-1)) { + clampedSearchVec *= ((sideImageSize.y-1-shadedPixelPos.y) / clampedSearchVec.y); + } + + return clampedSearchVec; +} + +<@func computeHorizon()@> + if (tapPixelPos.x>=0 && tapPixelPos.y>=0 && tapPixelPos.x #define SSAO_LINEAR_SAMPLING 1 -vec2 clampSearchVec(vec2 imageSize, vec2 centerPixelPos, vec2 searchVec) { - vec2 clampdSearchVec = searchVec; -/* TEMPO OP vec2 endPixel = centerPixelPos + clampdSearchVec; - - if (endPixel.x < 0) { - clampdSearchVec = clampdSearchVec * ((0-centerPixelPos.x) / clampdSearchVec.x); - endPixel = centerPixelPos + clampdSearchVec; - } - if (endPixel.x >= imageSize.x) { - clampdSearchVec = clampdSearchVec * ((imageSize.x-1-centerPixelPos.x) / clampdSearchVec.x); - endPixel = centerPixelPos + clampdSearchVec; - } - if (endPixel.y < 0) { - clampdSearchVec = clampdSearchVec * ((0-centerPixelPos.y) / clampdSearchVec.y); - endPixel = centerPixelPos + clampdSearchVec; - } - if (endPixel.y >= imageSize.y) { - clampdSearchVec = clampdSearchVec * ((imageSize.y-1-centerPixelPos.y) / clampdSearchVec.y); - }*/ - - return clampdSearchVec; -} - -float computeHorizon(ivec4 side, ivec2 centerPixelPos, vec2 imageSize, vec2 deltaTap, float ssDiskRadius, - vec3 fragPositionES, vec3 fragNormalES, vec2 clampedSearchVec) { - vec2 absClampedSearchVec = abs(clampedSearchVec); - int stepCount = int(max(absClampedSearchVec.x, absClampedSearchVec.y)); +float computeHorizon(ivec4 side, ivec2 shadedPixelPos, vec2 sideImageSize, vec2 deltaTap, + vec3 fragPositionES, vec3 fragNormalES, vec2 searchVec) { + vec2 absSearchVec = abs(searchVec); + int stepCount = int(max(absSearchVec.x, absSearchVec.y)); float cosHorizonAngle = 0.0; if (stepCount>0) { - vec2 deltaPixelTap = clampedSearchVec / float(stepCount); - float searchRadius = length(clampedSearchVec); + vec2 deltaPixelTap = searchVec / float(stepCount); + float searchRadius = length(searchVec); float deltaRadius = searchRadius / float(stepCount); vec2 tapPixelPos = vec2(0); -#if !SSAO_LINEAR_SAMPLING +#if SSAO_LINEAR_SAMPLING + float radius = 0.0; + int stepIndex; + + for (stepIndex=0 ; stepIndex + } +#else float radius = deltaRadius; float mipLevel = evalMipFromRadius(radius * float(doFetchMips())); @@ -364,44 +378,24 @@ float computeHorizon(ivec4 side, ivec2 centerPixelPos, vec2 imageSize, vec2 delt } radius += deltaRadius; } -#else - float radius = 0.0; - int stepIndex; - - for (stepIndex=0 ; stepIndex - } #endif } return cosHorizonAngle; } -vec2 searchHorizons(ivec4 side, ivec2 centerPixelPos, vec2 imageSize, vec2 deltaTap, float ssDiskRadius, - vec3 fragPositionES, vec3 fragNormalES) { - vec2 searchVec = deltaTap * ssDiskRadius; - vec2 horizons = vec2(0.0); +float evalVisibilityHBAO(ivec4 side, ivec2 shadedPixelPos, vec2 sideImageSize, vec2 deltaTap, float diskPixelRadius, + vec3 fragPositionES, vec3 fragNormalES) { + vec2 searchVec = deltaTap * diskPixelRadius; + float obscurance; // Forward search for h1 - vec2 clampedSearchVec = clampSearchVec(imageSize, vec2(centerPixelPos), searchVec); - horizons.x = computeHorizon(side, centerPixelPos, imageSize, deltaTap, ssDiskRadius, - fragPositionES, fragNormalES, clampedSearchVec); + obscurance = computeHorizon(side, shadedPixelPos, sideImageSize, deltaTap, fragPositionES, fragNormalES, searchVec); // Backward search for h2 - clampedSearchVec = clampSearchVec(imageSize, vec2(centerPixelPos), -searchVec); - horizons.y = computeHorizon(side, centerPixelPos, imageSize, deltaTap, ssDiskRadius, - fragPositionES, fragNormalES, clampedSearchVec); + obscurance += computeHorizon(side, shadedPixelPos, sideImageSize, deltaTap, fragPositionES, fragNormalES, -searchVec); - return horizons; -} - -float evalVisibilityHBAO(ivec4 side, ivec2 centerPixelPos, vec2 imageSize, vec2 deltaTap, float ssDiskRadius, - vec3 fragPositionES, vec3 fragNormalES) { - vec2 horizons = searchHorizons(side, centerPixelPos, imageSize, deltaTap, ssDiskRadius, fragPositionES, fragNormalES); - return (horizons.x + horizons.y) * 0.5 / PI; + return obscurance * 0.5 / PI; } <@endfunc@> diff --git a/libraries/render-utils/src/ssao_makeOcclusion.slf b/libraries/render-utils/src/ssao_makeOcclusion.slf index ee22590679..1a31950b7f 100644 --- a/libraries/render-utils/src/ssao_makeOcclusion.slf +++ b/libraries/render-utils/src/ssao_makeOcclusion.slf @@ -25,62 +25,63 @@ layout(location=0) out vec4 outFragColor; void main(void) { - vec2 imageSize = getSideImageSize(getResolutionLevel()); + vec2 sideImageSize = getSideImageSize(getResolutionLevel()); // Pixel being shaded vec2 fragCoord = gl_FragCoord.xy; - ivec2 centerPixelPos = ivec2(fragCoord.xy); + ivec2 shadedPixelPos = ivec2(fragCoord.xy); // Stereo side info - ivec4 side = getStereoSideInfo(centerPixelPos.x, getResolutionLevel()); + ivec4 side = getStereoSideInfo(shadedPixelPos.x, getResolutionLevel()); - // From now on, centerPixelPos is the pixel pos in the side - centerPixelPos.x -= side.y; - vec2 fragUVPos = (vec2(centerPixelPos) + vec2(0.5)) / imageSize; + // From now on, shadedPixelPos is the pixel pos in the side + shadedPixelPos.x -= side.y; + vec2 shadedUVPos = (vec2(shadedPixelPos) + vec2(0.5)) / sideImageSize; // Fetch the z under the pixel (stereo or not) - float Zeye = getZEyeAtUV(fragUVPos, 0); + float Zeye = getZEyeAtUV(shadedUVPos, 0); // The position and normal of the pixel fragment in Eye space - vec3 fragPositionES = evalEyePositionFromZeye(side.x, Zeye, fragUVPos); + vec3 fragPositionES = evalEyePositionFromZeye(side.x, Zeye, shadedUVPos); vec3 fragNormalES = evalEyeNormal(fragPositionES); // Choose the screen-space sample radius - float ssDiskRadius = evalDiskRadius(fragPositionES.z, imageSize); + float diskPixelRadius = evalDiskRadius(fragPositionES.z, sideImageSize); #if SSAO_USE_HORIZON_BASED - ssDiskRadius = min(ssDiskRadius, SSAO_HBAO_MAX_RADIUS); + diskPixelRadius = min(diskPixelRadius, SSAO_HBAO_MAX_RADIUS); #endif // Let's make noise - float randomPatternRotationAngle = getAngleDithering(centerPixelPos); + float randomPatternRotationAngle = getAngleDithering(shadedPixelPos); - // Accumulate the visibility for each samples - float visibilitySum = 0.0; + // Accumulate the obscurance for each samples + float obscuranceSum = 0.0; int numSamples = int(getNumSamples()); for (int i = 0; i < numSamples; ++i) { #if SSAO_USE_HORIZON_BASED vec3 deltaTap = getUnitTapLocation(i, 1.0, randomPatternRotationAngle, PI); - visibilitySum += evalVisibilityHBAO(side, centerPixelPos, imageSize, deltaTap.xy, ssDiskRadius, fragPositionES, fragNormalES); + obscuranceSum += evalVisibilityHBAO(side, shadedPixelPos, sideImageSize, deltaTap.xy, diskPixelRadius, fragPositionES, fragNormalES); #else - vec3 tap = getTapLocationClampedSSAO(i, randomPatternRotationAngle, ssDiskRadius, centerPixelPos, imageSize); - vec3 tapUVZ = fetchTap(side, centerPixelPos, tap, imageSize); + vec3 tap = getTapLocationClampedSSAO(i, randomPatternRotationAngle, diskPixelRadius, shadedPixelPos, sideImageSize); + vec3 tapUVZ = fetchTap(side, shadedPixelPos, tap, sideImageSize); vec3 tapPositionES = evalEyePositionFromZeye(side.x, tapUVZ.z, tapUVZ.xy); - visibilitySum += float(tap.z > 0.0) * evalVisibilitySSAO(fragPositionES, fragNormalES, tapPositionES); + obscuranceSum += float(tap.z > 0.0) * evalVisibilitySSAO(fragPositionES, fragNormalES, tapPositionES); #endif } - float occlusion = clamp(1.0 - visibilitySum * getObscuranceScaling() * getInvNumSamples(), 0.0, 1.0); + float occlusion = clamp(1.0 - obscuranceSum * getObscuranceScaling() * getInvNumSamples(), 0.0, 1.0); // KEEP IT for Debugging // Bilateral box-filter over a quad for free, respecting depth edges // (the difference that this makes is subtle) /* if (abs(dFdx(fragPositionES.z)) < 0.02) { - occlusion -= dFdx(occlusion) * ((centerPixelPos.x & 1) - 0.5); + occlusion -= dFdx(occlusion) * ((shadedPixelPos.x & 1) - 0.5); } if (abs(dFdy(fragPositionES.z)) < 0.02) { - occlusion -= dFdy(occlusion) * ((centerPixelPos.y & 1) - 0.5); + occlusion -= dFdy(occlusion) * ((shadedPixelPos.y & 1) - 0.5); }*/ //outFragColor = vec4(packOcclusionDepth(occlusion, CSZToDepthKey(fragPositionES.z)), 1.0); outFragColor = vec4(vec3(occlusion), 1.0); + } From c27f3dcafe5d367eff97ad67ac15fa44ec792c92 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Tue, 18 Sep 2018 08:45:04 +0200 Subject: [PATCH 020/286] Some cleanup, still trying to solve the problem --- .../src/AmbientOcclusionEffect.cpp | 2 +- .../render-utils/src/DebugDeferredBuffer.cpp | 2 +- .../render-utils/src/DebugDeferredBuffer.h | 4 +- libraries/render-utils/src/ssao.slh | 70 ++++++------------- .../render-utils/src/ssao_makeOcclusion.slf | 7 +- ...ip_depth_median.slf => ssao_mip_depth.slf} | 13 ++-- scripts/developer/utilities/render/luci.js | 8 +-- 7 files changed, 43 insertions(+), 63 deletions(-) rename libraries/render-utils/src/{mip_depth_median.slf => ssao_mip_depth.slf} (77%) diff --git a/libraries/render-utils/src/AmbientOcclusionEffect.cpp b/libraries/render-utils/src/AmbientOcclusionEffect.cpp index 70f36521d8..8e50eeb7c9 100644 --- a/libraries/render-utils/src/AmbientOcclusionEffect.cpp +++ b/libraries/render-utils/src/AmbientOcclusionEffect.cpp @@ -375,7 +375,7 @@ const gpu::PipelinePointer& AmbientOcclusionEffect::getVBlurPipeline() { const gpu::PipelinePointer& AmbientOcclusionEffect::getMipCreationPipeline() { if (!_mipCreationPipeline) { - _mipCreationPipeline = gpu::Context::createMipGenerationPipeline(gpu::Shader::createPixel(shader::render_utils::fragment::mip_depth_median)); + _mipCreationPipeline = gpu::Context::createMipGenerationPipeline(gpu::Shader::createPixel(shader::render_utils::fragment::ssao_mip_depth)); } return _mipCreationPipeline; } diff --git a/libraries/render-utils/src/DebugDeferredBuffer.cpp b/libraries/render-utils/src/DebugDeferredBuffer.cpp index 848c10597a..4a8fad7c6d 100644 --- a/libraries/render-utils/src/DebugDeferredBuffer.cpp +++ b/libraries/render-utils/src/DebugDeferredBuffer.cpp @@ -384,7 +384,7 @@ void DebugDeferredBuffer::configure(const Config& config) { auto& parameters = _parameters.edit(); _mode = (Mode)config.mode; - _size = config.size; + _size = glm::vec4{config.debugCursorTexcoord.x*2.0f - 1.0f, -1.0f, 1.0f, 1.0f}; parameters._shadowCascadeIndex = glm::clamp(_mode - Mode::ShadowCascade0Mode, 0, (int)SHADOW_CASCADE_MAX_COUNT - 1); } diff --git a/libraries/render-utils/src/DebugDeferredBuffer.h b/libraries/render-utils/src/DebugDeferredBuffer.h index 9daa8fd530..0fc006b452 100644 --- a/libraries/render-utils/src/DebugDeferredBuffer.h +++ b/libraries/render-utils/src/DebugDeferredBuffer.h @@ -25,14 +25,14 @@ class DebugDeferredBufferConfig : public render::Job::Config { Q_OBJECT Q_PROPERTY(bool enabled MEMBER enabled) Q_PROPERTY(int mode MEMBER mode WRITE setMode) - Q_PROPERTY(glm::vec4 size MEMBER size NOTIFY dirty) + Q_PROPERTY(glm::vec2 debugCursorTexcoord MEMBER debugCursorTexcoord NOTIFY dirty) public: DebugDeferredBufferConfig() : render::Job::Config(false) {} void setMode(int newMode); int mode{ 0 }; - glm::vec4 size{ 0.0f, -1.0f, 1.0f, 1.0f }; + glm::vec2 debugCursorTexcoord{ 0.5f, 0.5f }; signals: void dirty(); }; diff --git a/libraries/render-utils/src/ssao.slh b/libraries/render-utils/src/ssao.slh index b441d7ca8c..ab728553f1 100644 --- a/libraries/render-utils/src/ssao.slh +++ b/libraries/render-utils/src/ssao.slh @@ -249,14 +249,12 @@ vec3 fetchTapUnfiltered(ivec4 side, ivec2 ssC, vec3 tap, vec2 sideImageSize) { return P; } -vec4 fetchTap(ivec4 side, ivec2 ssC, vec3 tap, vec2 sideImageSize) { - int mipLevel = evalMipFromRadius(tap.z * float(doFetchMips())); - - vec2 ssP = tap.xy + vec2(ssC); +vec4 fetchTap(ivec4 side, vec2 tapPixelPos, float tapRadius, vec2 sideImageSize) { + int mipLevel = evalMipFromRadius(tapRadius * float(doFetchMips())); // We need to divide by 2^mipLevel to read the appropriately scaled coordinate from a MIP-map. // Manually clamp to the texture size because texelFetch bypasses the texture unit - vec2 tapUV = (vec2(ssP) + vec2(0.5)) / sideImageSize; + vec2 tapUV = (tapPixelPos + vec2(0.5)) / sideImageSize; vec2 fetchUV = vec2(tapUV.x + side.w * 0.5 * (side.x - tapUV.x), tapUV.y); vec4 P; @@ -293,44 +291,21 @@ float evalVisibilitySSAO(in vec3 centerPosition, in vec3 centerNormal, in vec3 t float computeHorizonFromTap(vec3 tapPositionES, vec3 fragPositionES, vec3 fragNormalES) { vec3 deltaVec = tapPositionES - fragPositionES; float distanceSquared = dot(deltaVec, deltaVec); - float horizon = dot(normalize(deltaVec), fragNormalES); + float cosHorizonAngle = dot(normalize(deltaVec), fragNormalES); float radiusFalloff = max(0.0, 1.0 - (distanceSquared / getRadius2())); - horizon = max(0.0, (horizon - getFalloffAngle()) / (1.0 - getFalloffAngle())); - horizon *= radiusFalloff; + cosHorizonAngle = max(0.0, (cosHorizonAngle - getFalloffAngle()) / (1.0 - getFalloffAngle())); + cosHorizonAngle *= radiusFalloff; - return horizon; -} - -vec2 clampSearchVector(vec2 sideImageSize, vec2 shadedPixelPos, vec2 searchVec) { - vec2 clampedSearchVec = searchVec; - vec2 endPixel = shadedPixelPos + clampedSearchVec; - - if (endPixel.x < 0) { - clampedSearchVec *= ((0-shadedPixelPos.x) / clampedSearchVec.x); - endPixel = shadedPixelPos + clampedSearchVec; - } - if (endPixel.x > (sideImageSize.x-1)) { - clampedSearchVec *= ((sideImageSize.x-1-shadedPixelPos.x) / clampedSearchVec.x); - endPixel = shadedPixelPos + clampedSearchVec; - } - if (endPixel.y < 0) { - clampedSearchVec *= ((0-shadedPixelPos.y) / clampedSearchVec.y); - endPixel = shadedPixelPos + clampedSearchVec; - } - if (endPixel.y > (sideImageSize.y-1)) { - clampedSearchVec *= ((sideImageSize.y-1-shadedPixelPos.y) / clampedSearchVec.y); - } - - return clampedSearchVec; + return cosHorizonAngle; } <@func computeHorizon()@> - if (tapPixelPos.x>=0 && tapPixelPos.y>=0 && tapPixelPos.x=sideImageSize.x && tapPixelPos.y>=sideImageSize.y) { break; } - vec3 tap = vec3(tapPixelPos, radius); - vec4 tapUVZ_mip = fetchTap(side, shadedPixelPos, tap, sideImageSize); + vec4 tapUVZ_mip = fetchTap(side, tapPixelPos, radius, sideImageSize); vec3 tapPositionES = evalEyePositionFromZeye(side.x, tapUVZ_mip.z, tapUVZ_mip.xy); float tapCosHorizonAngle = computeHorizonFromTap(tapPositionES, fragPositionES, fragNormalES); @@ -338,26 +313,25 @@ vec2 clampSearchVector(vec2 sideImageSize, vec2 shadedPixelPos, vec2 searchVec) <@endfunc@> -#define SSAO_LINEAR_SAMPLING 1 +#define SSAO_LINEAR_SEARCH_HORIZON 1 -float computeHorizon(ivec4 side, ivec2 shadedPixelPos, vec2 sideImageSize, vec2 deltaTap, - vec3 fragPositionES, vec3 fragNormalES, vec2 searchVec) { +float computeHorizon(ivec4 side, vec2 shadedPixelPos, vec2 sideImageSize, vec2 deltaTap, + vec3 fragPositionES, vec3 fragNormalES, vec2 searchVec, float searchRadius) { vec2 absSearchVec = abs(searchVec); - int stepCount = int(max(absSearchVec.x, absSearchVec.y)); + int stepCount = int(ceil(max(absSearchVec.x, absSearchVec.y))); float cosHorizonAngle = 0.0; if (stepCount>0) { vec2 deltaPixelTap = searchVec / float(stepCount); - float searchRadius = length(searchVec); float deltaRadius = searchRadius / float(stepCount); - vec2 tapPixelPos = vec2(0); + vec2 tapPixelOffset = vec2(0); -#if SSAO_LINEAR_SAMPLING +#if SSAO_LINEAR_SEARCH_HORIZON float radius = 0.0; int stepIndex; for (stepIndex=0 ; stepIndex @@ -366,8 +340,8 @@ float computeHorizon(ivec4 side, ivec2 shadedPixelPos, vec2 sideImageSize, vec2 float radius = deltaRadius; float mipLevel = evalMipFromRadius(radius * float(doFetchMips())); - while (radius @@ -384,16 +358,16 @@ float computeHorizon(ivec4 side, ivec2 shadedPixelPos, vec2 sideImageSize, vec2 return cosHorizonAngle; } -float evalVisibilityHBAO(ivec4 side, ivec2 shadedPixelPos, vec2 sideImageSize, vec2 deltaTap, float diskPixelRadius, +float evalVisibilityHBAO(ivec4 side, vec2 shadedPixelPos, vec2 sideImageSize, vec2 deltaTap, float diskPixelRadius, vec3 fragPositionES, vec3 fragNormalES) { vec2 searchVec = deltaTap * diskPixelRadius; float obscurance; // Forward search for h1 - obscurance = computeHorizon(side, shadedPixelPos, sideImageSize, deltaTap, fragPositionES, fragNormalES, searchVec); + obscurance = computeHorizon(side, shadedPixelPos, sideImageSize, deltaTap, fragPositionES, fragNormalES, searchVec, diskPixelRadius); // Backward search for h2 - obscurance += computeHorizon(side, shadedPixelPos, sideImageSize, deltaTap, fragPositionES, fragNormalES, -searchVec); + obscurance += computeHorizon(side, shadedPixelPos, sideImageSize, deltaTap, fragPositionES, fragNormalES, -searchVec, diskPixelRadius); return obscurance * 0.5 / PI; } diff --git a/libraries/render-utils/src/ssao_makeOcclusion.slf b/libraries/render-utils/src/ssao_makeOcclusion.slf index 1a31950b7f..a28b06cea1 100644 --- a/libraries/render-utils/src/ssao_makeOcclusion.slf +++ b/libraries/render-utils/src/ssao_makeOcclusion.slf @@ -60,10 +60,13 @@ void main(void) { for (int i = 0; i < numSamples; ++i) { #if SSAO_USE_HORIZON_BASED vec3 deltaTap = getUnitTapLocation(i, 1.0, randomPatternRotationAngle, PI); - obscuranceSum += evalVisibilityHBAO(side, shadedPixelPos, sideImageSize, deltaTap.xy, diskPixelRadius, fragPositionES, fragNormalES); + // TEMPO OP + deltaTap.xy = vec2(1,0); + obscuranceSum += evalVisibilityHBAO(side, vec2(shadedPixelPos), sideImageSize, deltaTap.xy, diskPixelRadius, fragPositionES, fragNormalES); #else vec3 tap = getTapLocationClampedSSAO(i, randomPatternRotationAngle, diskPixelRadius, shadedPixelPos, sideImageSize); - vec3 tapUVZ = fetchTap(side, shadedPixelPos, tap, sideImageSize); + vec2 tapPixelPos = vec2(shadedPixelPos) + tap.xy; + vec3 tapUVZ = fetchTap(side, tapPixelPos, tap.z, sideImageSize); vec3 tapPositionES = evalEyePositionFromZeye(side.x, tapUVZ.z, tapUVZ.xy); obscuranceSum += float(tap.z > 0.0) * evalVisibilitySSAO(fragPositionES, fragNormalES, tapPositionES); #endif diff --git a/libraries/render-utils/src/mip_depth_median.slf b/libraries/render-utils/src/ssao_mip_depth.slf similarity index 77% rename from libraries/render-utils/src/mip_depth_median.slf rename to libraries/render-utils/src/ssao_mip_depth.slf index 489573b519..6a72af704c 100644 --- a/libraries/render-utils/src/mip_depth_median.slf +++ b/libraries/render-utils/src/ssao_mip_depth.slf @@ -2,7 +2,7 @@ <$VERSION_HEADER$> // Generated on <$_SCRIBE_DATE$> // -// mip_depth_median.frag +// ssao_mip_depth.frag // fragment shader // // Created by Olivier Prat on 4/18/18. @@ -22,7 +22,7 @@ void main(void) { vec4 depths = textureGather(depthTexture, varTexCoord0); // Order the depths from minimum to maximum - depths.xy = depths.x > depths.y ? depths.yx : depths.xy; +/* depths.xy = depths.x > depths.y ? depths.yx : depths.xy; depths.xz = depths.x > depths.z ? depths.zx : depths.xz; depths.xw = depths.x > depths.w ? depths.wx : depths.xw; @@ -31,6 +31,11 @@ void main(void) { depths.zw = depths.z > depths.w ? depths.wz : depths.zw; - float median = (depths.y + depths.z) / 2.0; - outFragColor = vec4(vec3(median), 1.0); + float outZ = (depths.y + depths.z) / 2.0;*/ + + float outZ = min(depths.x, depths.y); + outZ = min(outZ, depths.z); + outZ = min(outZ, depths.w); + + outFragColor = vec4(vec3(outZ), 1.0); } diff --git a/scripts/developer/utilities/render/luci.js b/scripts/developer/utilities/render/luci.js index 005d96780a..5ad2733dcd 100644 --- a/scripts/developer/utilities/render/luci.js +++ b/scripts/developer/utilities/render/luci.js @@ -78,13 +78,11 @@ Controller.mouseReleaseEvent.connect(function() { moveDebugCursor = false; }); Controller.mouseMoveEvent.connect(function (e) { if (moveDebugCursor) setDebugCursor(e.x, e.y); }); - - function setDebugCursor(x, y) { - nx = (x / Window.innerWidth); - ny = 1.0 - ((y) / (Window.innerHeight - 32)); + nx = ((x + 0.5) / Window.innerWidth); + ny = 1.0 - ((y + 0.5) / (Window.innerHeight)); - Render.getConfig("RenderMainView").getConfig("Antialiasing").debugCursorTexcoord = { x: nx, y: ny }; + Render.getConfig("RenderMainView").getConfig("DebugDeferredBuffer").debugCursorTexcoord = { x: nx, y: ny }; } From acc6d0b79eb6b4d26d4b8c5a44e6bd874e25cb83 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Tue, 18 Sep 2018 10:22:35 +0200 Subject: [PATCH 021/286] Fixed wrong AO with far objects! --- .../render-utils/src/SurfaceGeometryPass.cpp | 4 +-- libraries/render-utils/src/ssao.slh | 12 ++++---- .../render-utils/src/ssao_makeOcclusion.slf | 28 +++++++++---------- libraries/render-utils/src/ssao_mip_depth.slf | 9 ++---- 4 files changed, 25 insertions(+), 28 deletions(-) diff --git a/libraries/render-utils/src/SurfaceGeometryPass.cpp b/libraries/render-utils/src/SurfaceGeometryPass.cpp index f22b8351d4..d2bff5b322 100644 --- a/libraries/render-utils/src/SurfaceGeometryPass.cpp +++ b/libraries/render-utils/src/SurfaceGeometryPass.cpp @@ -66,7 +66,7 @@ void LinearDepthFramebuffer::allocate() { // For Linear Depth: const uint16_t LINEAR_DEPTH_MAX_MIP_LEVEL = 5; _linearDepthTexture = gpu::Texture::createRenderBuffer(gpu::Element(gpu::SCALAR, gpu::FLOAT, gpu::RED), width, height, LINEAR_DEPTH_MAX_MIP_LEVEL, - gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR_MIP_POINT, gpu::Sampler::WRAP_CLAMP)); + gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_POINT, gpu::Sampler::WRAP_CLAMP)); _linearDepthFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("linearDepth")); _linearDepthFramebuffer->setRenderBuffer(0, _linearDepthTexture); _linearDepthFramebuffer->setDepthStencilBuffer(_primaryDepthTexture, _primaryDepthTexture->getTexelFormat()); @@ -74,7 +74,7 @@ void LinearDepthFramebuffer::allocate() { // For Downsampling: const uint16_t HALF_LINEAR_DEPTH_MAX_MIP_LEVEL = LINEAR_DEPTH_MAX_MIP_LEVEL; _halfLinearDepthTexture = gpu::Texture::createRenderBuffer(gpu::Element(gpu::SCALAR, gpu::FLOAT, gpu::RED), _halfFrameSize.x, _halfFrameSize.y, HALF_LINEAR_DEPTH_MAX_MIP_LEVEL, - gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR_MIP_POINT, gpu::Sampler::WRAP_CLAMP)); + gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_POINT, gpu::Sampler::WRAP_CLAMP)); _halfNormalTexture = gpu::Texture::createRenderBuffer(gpu::Element::COLOR_RGBA_32, _halfFrameSize.x, _halfFrameSize.y, gpu::Texture::SINGLE_MIP, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR_MIP_POINT)); diff --git a/libraries/render-utils/src/ssao.slh b/libraries/render-utils/src/ssao.slh index ab728553f1..051f685e2d 100644 --- a/libraries/render-utils/src/ssao.slh +++ b/libraries/render-utils/src/ssao.slh @@ -226,7 +226,7 @@ float getZEyeAtUV(vec2 texCoord, int level) { return -texture(pyramidMap, texCoord, level).x; } -const int LOG_MAX_OFFSET = 1; +const int LOG_MAX_OFFSET = 2; const int MAX_MIP_LEVEL = 5; int evalMipFromRadius(float radius) { // mipLevel = floor(log(ssR / MAX_OFFSET)); @@ -289,10 +289,12 @@ float evalVisibilitySSAO(in vec3 centerPosition, in vec3 centerNormal, in vec3 t } float computeHorizonFromTap(vec3 tapPositionES, vec3 fragPositionES, vec3 fragNormalES) { + const float epsilon = 0.01; + vec3 deltaVec = tapPositionES - fragPositionES; - float distanceSquared = dot(deltaVec, deltaVec); - float cosHorizonAngle = dot(normalize(deltaVec), fragNormalES); - float radiusFalloff = max(0.0, 1.0 - (distanceSquared / getRadius2())); + float distance = length(deltaVec); + float cosHorizonAngle = dot(deltaVec, fragNormalES) / (distance + epsilon); + float radiusFalloff = max(0.0, 1.0 - (distance*distance / getRadius2())); cosHorizonAngle = max(0.0, (cosHorizonAngle - getFalloffAngle()) / (1.0 - getFalloffAngle())); cosHorizonAngle *= radiusFalloff; @@ -313,7 +315,7 @@ float computeHorizonFromTap(vec3 tapPositionES, vec3 fragPositionES, vec3 fragNo <@endfunc@> -#define SSAO_LINEAR_SEARCH_HORIZON 1 +#define SSAO_LINEAR_SEARCH_HORIZON 0 float computeHorizon(ivec4 side, vec2 shadedPixelPos, vec2 sideImageSize, vec2 deltaTap, vec3 fragPositionES, vec3 fragNormalES, vec2 searchVec, float searchRadius) { diff --git a/libraries/render-utils/src/ssao_makeOcclusion.slf b/libraries/render-utils/src/ssao_makeOcclusion.slf index a28b06cea1..94e87c75e0 100644 --- a/libraries/render-utils/src/ssao_makeOcclusion.slf +++ b/libraries/render-utils/src/ssao_makeOcclusion.slf @@ -29,20 +29,20 @@ void main(void) { // Pixel being shaded vec2 fragCoord = gl_FragCoord.xy; - ivec2 shadedPixelPos = ivec2(fragCoord.xy); + ivec2 fragPixelPos = ivec2(fragCoord.xy); // Stereo side info - ivec4 side = getStereoSideInfo(shadedPixelPos.x, getResolutionLevel()); + ivec4 side = getStereoSideInfo(fragPixelPos.x, getResolutionLevel()); - // From now on, shadedPixelPos is the pixel pos in the side - shadedPixelPos.x -= side.y; - vec2 shadedUVPos = (vec2(shadedPixelPos) + vec2(0.5)) / sideImageSize; + // From now on, fragPixelPos is the pixel pos in the side + fragPixelPos.x -= side.y; + vec2 fragUVPos = (vec2(fragPixelPos) + vec2(0.5)) / sideImageSize; // Fetch the z under the pixel (stereo or not) - float Zeye = getZEyeAtUV(shadedUVPos, 0); + float Zeye = getZEyeAtUV(fragUVPos, 0); // The position and normal of the pixel fragment in Eye space - vec3 fragPositionES = evalEyePositionFromZeye(side.x, Zeye, shadedUVPos); + vec3 fragPositionES = evalEyePositionFromZeye(side.x, Zeye, fragUVPos); vec3 fragNormalES = evalEyeNormal(fragPositionES); // Choose the screen-space sample radius @@ -52,7 +52,7 @@ void main(void) { #endif // Let's make noise - float randomPatternRotationAngle = getAngleDithering(shadedPixelPos); + float randomPatternRotationAngle = getAngleDithering(fragPixelPos); // Accumulate the obscurance for each samples float obscuranceSum = 0.0; @@ -60,12 +60,10 @@ void main(void) { for (int i = 0; i < numSamples; ++i) { #if SSAO_USE_HORIZON_BASED vec3 deltaTap = getUnitTapLocation(i, 1.0, randomPatternRotationAngle, PI); - // TEMPO OP - deltaTap.xy = vec2(1,0); - obscuranceSum += evalVisibilityHBAO(side, vec2(shadedPixelPos), sideImageSize, deltaTap.xy, diskPixelRadius, fragPositionES, fragNormalES); + obscuranceSum += evalVisibilityHBAO(side, vec2(fragPixelPos), sideImageSize, deltaTap.xy, diskPixelRadius, fragPositionES, fragNormalES); #else - vec3 tap = getTapLocationClampedSSAO(i, randomPatternRotationAngle, diskPixelRadius, shadedPixelPos, sideImageSize); - vec2 tapPixelPos = vec2(shadedPixelPos) + tap.xy; + vec3 tap = getTapLocationClampedSSAO(i, randomPatternRotationAngle, diskPixelRadius, fragPixelPos, sideImageSize); + vec2 tapPixelPos = vec2(fragPixelPos) + tap.xy; vec3 tapUVZ = fetchTap(side, tapPixelPos, tap.z, sideImageSize); vec3 tapPositionES = evalEyePositionFromZeye(side.x, tapUVZ.z, tapUVZ.xy); obscuranceSum += float(tap.z > 0.0) * evalVisibilitySSAO(fragPositionES, fragNormalES, tapPositionES); @@ -78,10 +76,10 @@ void main(void) { // Bilateral box-filter over a quad for free, respecting depth edges // (the difference that this makes is subtle) /* if (abs(dFdx(fragPositionES.z)) < 0.02) { - occlusion -= dFdx(occlusion) * ((shadedPixelPos.x & 1) - 0.5); + occlusion -= dFdx(occlusion) * ((fragPixelPos.x & 1) - 0.5); } if (abs(dFdy(fragPositionES.z)) < 0.02) { - occlusion -= dFdy(occlusion) * ((shadedPixelPos.y & 1) - 0.5); + occlusion -= dFdy(occlusion) * ((fragPixelPos.y & 1) - 0.5); }*/ //outFragColor = vec4(packOcclusionDepth(occlusion, CSZToDepthKey(fragPositionES.z)), 1.0); diff --git a/libraries/render-utils/src/ssao_mip_depth.slf b/libraries/render-utils/src/ssao_mip_depth.slf index 6a72af704c..49f3d5254f 100644 --- a/libraries/render-utils/src/ssao_mip_depth.slf +++ b/libraries/render-utils/src/ssao_mip_depth.slf @@ -22,7 +22,7 @@ void main(void) { vec4 depths = textureGather(depthTexture, varTexCoord0); // Order the depths from minimum to maximum -/* depths.xy = depths.x > depths.y ? depths.yx : depths.xy; + depths.xy = depths.x > depths.y ? depths.yx : depths.xy; depths.xz = depths.x > depths.z ? depths.zx : depths.xz; depths.xw = depths.x > depths.w ? depths.wx : depths.xw; @@ -31,11 +31,8 @@ void main(void) { depths.zw = depths.z > depths.w ? depths.wz : depths.zw; - float outZ = (depths.y + depths.z) / 2.0;*/ - - float outZ = min(depths.x, depths.y); - outZ = min(outZ, depths.z); - outZ = min(outZ, depths.w); + // Take the median depth + float outZ = (depths.y + depths.z) / 2.0; outFragColor = vec4(vec3(outZ), 1.0); } From a2abf33669d944983cf997e7c713db14b1cbe515 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Tue, 18 Sep 2018 11:45:58 +0200 Subject: [PATCH 022/286] Removed falloff distance as we're directly using the radius --- libraries/render-utils/src/AmbientOcclusionEffect.cpp | 11 +++-------- libraries/render-utils/src/AmbientOcclusionEffect.h | 4 ---- libraries/render-utils/src/ssao.slh | 8 ++++---- .../utilities/render/ambientOcclusionPass.qml | 1 - 4 files changed, 7 insertions(+), 17 deletions(-) diff --git a/libraries/render-utils/src/AmbientOcclusionEffect.cpp b/libraries/render-utils/src/AmbientOcclusionEffect.cpp index 8e50eeb7c9..cca9997cf6 100644 --- a/libraries/render-utils/src/AmbientOcclusionEffect.cpp +++ b/libraries/render-utils/src/AmbientOcclusionEffect.cpp @@ -176,18 +176,17 @@ public: AmbientOcclusionEffectConfig::AmbientOcclusionEffectConfig() : render::GPUJobConfig::Persistent(QStringList() << "Render" << "Engine" << "Ambient Occlusion", false), #if SSAO_USE_HORIZON_BASED - radius{ 0.1f }, + radius{ 0.3f }, #else radius{ 0.5f }, #endif perspectiveScale{ 1.0f }, obscuranceLevel{ 0.5f }, #if SSAO_USE_HORIZON_BASED - falloffAngle{ 0.1f }, + falloffAngle{ 0.2f }, #else falloffAngle{ 0.01f }, #endif - falloffDistance{ 0.3f }, edgeSharpness{ 1.0f }, blurDeviation{ 2.5f }, numSpiralTurns{ 7.0f }, @@ -242,6 +241,7 @@ void AmbientOcclusionEffect::configure(const Config& config) { if (config.falloffAngle != _aoParametersBuffer->getFalloffAngle()) { auto& current = _aoParametersBuffer.edit().ditheringInfo; current.z = config.falloffAngle; + current.y = 1.0f / (1.0f - config.falloffAngle); } if (config.edgeSharpness != _aoParametersBuffer->getEdgeSharpness()) { @@ -302,11 +302,6 @@ void AmbientOcclusionEffect::configure(const Config& config) { current.w = (float)config.borderingEnabled; } - if (config.falloffDistance != _aoParametersBuffer->getFalloffDistance()) { - auto& current = _aoParametersBuffer.edit().ditheringInfo; - current.y = (float)config.falloffDistance; - } - if (shouldUpdateGaussian) { updateGaussianDistribution(); } diff --git a/libraries/render-utils/src/AmbientOcclusionEffect.h b/libraries/render-utils/src/AmbientOcclusionEffect.h index 81ceee9bf6..4021768995 100644 --- a/libraries/render-utils/src/AmbientOcclusionEffect.h +++ b/libraries/render-utils/src/AmbientOcclusionEffect.h @@ -63,7 +63,6 @@ class AmbientOcclusionEffectConfig : public render::GPUJobConfig::Persistent { Q_PROPERTY(float radius MEMBER radius WRITE setRadius) Q_PROPERTY(float obscuranceLevel MEMBER obscuranceLevel WRITE setObscuranceLevel) Q_PROPERTY(float falloffAngle MEMBER falloffAngle WRITE setFalloffAngle) - Q_PROPERTY(float falloffDistance MEMBER falloffDistance WRITE setFalloffDistance) Q_PROPERTY(float edgeSharpness MEMBER edgeSharpness WRITE setEdgeSharpness) Q_PROPERTY(float blurDeviation MEMBER blurDeviation WRITE setBlurDeviation) Q_PROPERTY(float numSpiralTurns MEMBER numSpiralTurns WRITE setNumSpiralTurns) @@ -80,7 +79,6 @@ public: void setRadius(float newRadius) { radius = std::max(0.01f, newRadius); emit dirty(); } void setObscuranceLevel(float level) { obscuranceLevel = std::max(0.01f, level); emit dirty(); } void setFalloffAngle(float bias) { falloffAngle = std::max(0.0f, std::min(bias, 1.0f)); emit dirty(); } - void setFalloffDistance(float value) { falloffDistance = std::max(0.0f, value); emit dirty(); } void setEdgeSharpness(float sharpness) { edgeSharpness = std::max(0.0f, (float)sharpness); emit dirty(); } void setBlurDeviation(float deviation) { blurDeviation = std::max(0.0f, deviation); emit dirty(); } void setNumSpiralTurns(float turns) { numSpiralTurns = std::max(0.0f, (float)turns); emit dirty(); } @@ -92,7 +90,6 @@ public: float perspectiveScale; float obscuranceLevel; // intensify or dim down the obscurance effect float falloffAngle; - float falloffDistance; float edgeSharpness; float blurDeviation; float numSpiralTurns; // defining an angle span to distribute the samples ray directions @@ -144,7 +141,6 @@ public: float getPerspectiveScale() const { return resolutionInfo.z; } float getObscuranceLevel() const { return radiusInfo.w; } float getFalloffAngle() const { return (float)ditheringInfo.z; } - float getFalloffDistance() const { return ditheringInfo.y; } float getEdgeSharpness() const { return (float)blurInfo.x; } float getBlurDeviation() const { return blurInfo.z; } diff --git a/libraries/render-utils/src/ssao.slh b/libraries/render-utils/src/ssao.slh index 051f685e2d..99d45647ee 100644 --- a/libraries/render-utils/src/ssao.slh +++ b/libraries/render-utils/src/ssao.slh @@ -75,9 +75,6 @@ float getObscuranceScaling() { float isDitheringEnabled() { return params._ditheringInfo.x; } -float getFalloffDistance() { - return params._ditheringInfo.y; -} float isBorderingEnabled() { return params._ditheringInfo.w; } @@ -85,6 +82,9 @@ float isBorderingEnabled() { float getFalloffAngle() { return params._ditheringInfo.z; } +float getFalloffAngleScale() { + return params._ditheringInfo.y; +} float getNumSamples() { return params._sampleInfo.x; @@ -296,7 +296,7 @@ float computeHorizonFromTap(vec3 tapPositionES, vec3 fragPositionES, vec3 fragNo float cosHorizonAngle = dot(deltaVec, fragNormalES) / (distance + epsilon); float radiusFalloff = max(0.0, 1.0 - (distance*distance / getRadius2())); - cosHorizonAngle = max(0.0, (cosHorizonAngle - getFalloffAngle()) / (1.0 - getFalloffAngle())); + cosHorizonAngle = max(0.0, (cosHorizonAngle - getFalloffAngle()) * getFalloffAngleScale()); cosHorizonAngle *= radiusFalloff; return cosHorizonAngle; diff --git a/scripts/developer/utilities/render/ambientOcclusionPass.qml b/scripts/developer/utilities/render/ambientOcclusionPass.qml index 0416443139..f92b469626 100644 --- a/scripts/developer/utilities/render/ambientOcclusionPass.qml +++ b/scripts/developer/utilities/render/ambientOcclusionPass.qml @@ -38,7 +38,6 @@ Rectangle { "Num Taps:numSamples:32:true", "Taps Spiral:numSpiralTurns:10.0:false", "Falloff Angle:falloffAngle:0.5:false", - "Falloff Distance:falloffDistance:2.0:false", "Blur Edge Sharpness:edgeSharpness:1.0:false", "Blur Radius:blurRadius:15.0:false", ] From f7379dfcc97f77e57655ddc1f2d8c96efb99955e Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Tue, 18 Sep 2018 11:54:39 +0200 Subject: [PATCH 023/286] Switched AO buffer to single R8 component --- libraries/render-utils/src/AmbientOcclusionEffect.cpp | 2 +- libraries/render-utils/src/ssao.slh | 5 +++-- libraries/render-utils/src/ssao_debugOcclusion.slf | 6 +++--- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/libraries/render-utils/src/AmbientOcclusionEffect.cpp b/libraries/render-utils/src/AmbientOcclusionEffect.cpp index cca9997cf6..dc08b9f28b 100644 --- a/libraries/render-utils/src/AmbientOcclusionEffect.cpp +++ b/libraries/render-utils/src/AmbientOcclusionEffect.cpp @@ -78,7 +78,7 @@ void AmbientOcclusionFramebuffer::allocate() { auto width = _frameSize.x; auto height = _frameSize.y; - auto format = gpu::Element::COLOR_RGBA_32; + auto format = gpu::Element::COLOR_R_8; _occlusionTexture = gpu::Texture::createRenderBuffer(format, width, height, gpu::Texture::SINGLE_MIP, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR_MIP_POINT, gpu::Sampler::WRAP_CLAMP)); _occlusionFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("occlusion")); diff --git a/libraries/render-utils/src/ssao.slh b/libraries/render-utils/src/ssao.slh index 99d45647ee..5c03afc53c 100644 --- a/libraries/render-utils/src/ssao.slh +++ b/libraries/render-utils/src/ssao.slh @@ -229,7 +229,6 @@ float getZEyeAtUV(vec2 texCoord, int level) { const int LOG_MAX_OFFSET = 2; const int MAX_MIP_LEVEL = 5; int evalMipFromRadius(float radius) { - // mipLevel = floor(log(ssR / MAX_OFFSET)); return clamp(findMSB(int(radius)) - LOG_MAX_OFFSET, 0, MAX_MIP_LEVEL); } @@ -293,12 +292,14 @@ float computeHorizonFromTap(vec3 tapPositionES, vec3 fragPositionES, vec3 fragNo vec3 deltaVec = tapPositionES - fragPositionES; float distance = length(deltaVec); - float cosHorizonAngle = dot(deltaVec, fragNormalES) / (distance + epsilon); + float cosHorizonAngle = 0.0; float radiusFalloff = max(0.0, 1.0 - (distance*distance / getRadius2())); + cosHorizonAngle = dot(deltaVec, fragNormalES) / (distance + epsilon); cosHorizonAngle = max(0.0, (cosHorizonAngle - getFalloffAngle()) * getFalloffAngleScale()); cosHorizonAngle *= radiusFalloff; + return cosHorizonAngle; } diff --git a/libraries/render-utils/src/ssao_debugOcclusion.slf b/libraries/render-utils/src/ssao_debugOcclusion.slf index 92e5a28a0b..10069582aa 100644 --- a/libraries/render-utils/src/ssao_debugOcclusion.slf +++ b/libraries/render-utils/src/ssao_debugOcclusion.slf @@ -82,7 +82,7 @@ void main(void) { // Accumulate the Obscurance for each samples float sum = 0.0; float keepTapRadius = 1.0; - int keepedMip = -1; + int keptMip = -1; bool keep = false; int sampleCount = int(getNumSamples()); for (int i = 0; i < sampleCount; ++i) { @@ -92,7 +92,7 @@ void main(void) { vec2 fragToTap = vec2(ssC) + tap.xy - fragCoord.xy; if (dot(fragToTap,fragToTap) < keepTapRadius) { keep = true; - keepedMip = evalMipFromRadius(tap.z * float(doFetchMips())); + keptMip = evalMipFromRadius(tap.z * float(doFetchMips())); } vec3 tapUVZ = fetchTap(side, ssC, tap, imageSize); @@ -127,6 +127,6 @@ void main(void) { if (!keep) { outFragColor = vec4(0.1); } else { - outFragColor.rgb = colorWheel(float(keepedMip)/float(MAX_MIP_LEVEL)); + outFragColor.rgb = colorWheel(float(keptMip)/float(MAX_MIP_LEVEL)); } } From fb7a8bdd4dc1baf40944bde4622c51fdb839195d Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Tue, 18 Sep 2018 12:13:49 +0200 Subject: [PATCH 024/286] Mip depth sampling in HBAO now works --- .../render-utils/src/SurfaceGeometryPass.cpp | 6 ++++-- libraries/render-utils/src/ssao.slh | 7 ++++--- libraries/render-utils/src/ssao_mip_depth.slf | 15 ++------------- 3 files changed, 10 insertions(+), 18 deletions(-) diff --git a/libraries/render-utils/src/SurfaceGeometryPass.cpp b/libraries/render-utils/src/SurfaceGeometryPass.cpp index d2bff5b322..6f455c2dbb 100644 --- a/libraries/render-utils/src/SurfaceGeometryPass.cpp +++ b/libraries/render-utils/src/SurfaceGeometryPass.cpp @@ -65,8 +65,10 @@ void LinearDepthFramebuffer::allocate() { // For Linear Depth: const uint16_t LINEAR_DEPTH_MAX_MIP_LEVEL = 5; + // Point sampling of the depth is need for the AmbientOcclusionEffect in HBAO, as well as the clamp to edge + const auto depthSampler = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_POINT, gpu::Sampler::WRAP_CLAMP); _linearDepthTexture = gpu::Texture::createRenderBuffer(gpu::Element(gpu::SCALAR, gpu::FLOAT, gpu::RED), width, height, LINEAR_DEPTH_MAX_MIP_LEVEL, - gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_POINT, gpu::Sampler::WRAP_CLAMP)); + depthSampler); _linearDepthFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("linearDepth")); _linearDepthFramebuffer->setRenderBuffer(0, _linearDepthTexture); _linearDepthFramebuffer->setDepthStencilBuffer(_primaryDepthTexture, _primaryDepthTexture->getTexelFormat()); @@ -74,7 +76,7 @@ void LinearDepthFramebuffer::allocate() { // For Downsampling: const uint16_t HALF_LINEAR_DEPTH_MAX_MIP_LEVEL = LINEAR_DEPTH_MAX_MIP_LEVEL; _halfLinearDepthTexture = gpu::Texture::createRenderBuffer(gpu::Element(gpu::SCALAR, gpu::FLOAT, gpu::RED), _halfFrameSize.x, _halfFrameSize.y, HALF_LINEAR_DEPTH_MAX_MIP_LEVEL, - gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_POINT, gpu::Sampler::WRAP_CLAMP)); + depthSampler); _halfNormalTexture = gpu::Texture::createRenderBuffer(gpu::Element::COLOR_RGBA_32, _halfFrameSize.x, _halfFrameSize.y, gpu::Texture::SINGLE_MIP, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR_MIP_POINT)); diff --git a/libraries/render-utils/src/ssao.slh b/libraries/render-utils/src/ssao.slh index 5c03afc53c..d9915f6de9 100644 --- a/libraries/render-utils/src/ssao.slh +++ b/libraries/render-utils/src/ssao.slh @@ -226,7 +226,7 @@ float getZEyeAtUV(vec2 texCoord, int level) { return -texture(pyramidMap, texCoord, level).x; } -const int LOG_MAX_OFFSET = 2; +const int LOG_MAX_OFFSET = 1; const int MAX_MIP_LEVEL = 5; int evalMipFromRadius(float radius) { return clamp(findMSB(int(radius)) - LOG_MAX_OFFSET, 0, MAX_MIP_LEVEL); @@ -316,7 +316,7 @@ float computeHorizonFromTap(vec3 tapPositionES, vec3 fragPositionES, vec3 fragNo <@endfunc@> -#define SSAO_LINEAR_SEARCH_HORIZON 0 +#define HBAO_HORIZON_SEARCH_CONSTANT_STEP 0 float computeHorizon(ivec4 side, vec2 shadedPixelPos, vec2 sideImageSize, vec2 deltaTap, vec3 fragPositionES, vec3 fragNormalES, vec2 searchVec, float searchRadius) { @@ -329,7 +329,7 @@ float computeHorizon(ivec4 side, vec2 shadedPixelPos, vec2 sideImageSize, vec2 d float deltaRadius = searchRadius / float(stepCount); vec2 tapPixelOffset = vec2(0); -#if SSAO_LINEAR_SEARCH_HORIZON +#if HBAO_HORIZON_SEARCH_CONSTANT_STEP float radius = 0.0; int stepIndex; @@ -339,6 +339,7 @@ float computeHorizon(ivec4 side, vec2 shadedPixelPos, vec2 sideImageSize, vec2 d <$computeHorizon()$> } +// Step is adapted to Mip level #else float radius = deltaRadius; float mipLevel = evalMipFromRadius(radius * float(doFetchMips())); diff --git a/libraries/render-utils/src/ssao_mip_depth.slf b/libraries/render-utils/src/ssao_mip_depth.slf index 49f3d5254f..c028ba3ec9 100644 --- a/libraries/render-utils/src/ssao_mip_depth.slf +++ b/libraries/render-utils/src/ssao_mip_depth.slf @@ -20,19 +20,8 @@ out vec4 outFragColor; void main(void) { vec4 depths = textureGather(depthTexture, varTexCoord0); - - // Order the depths from minimum to maximum - depths.xy = depths.x > depths.y ? depths.yx : depths.xy; - depths.xz = depths.x > depths.z ? depths.zx : depths.xz; - depths.xw = depths.x > depths.w ? depths.wx : depths.xw; - - depths.yz = depths.y > depths.z ? depths.zy : depths.yz; - depths.yw = depths.y > depths.w ? depths.wy : depths.yw; - - depths.zw = depths.z > depths.w ? depths.wz : depths.zw; - - // Take the median depth - float outZ = (depths.y + depths.z) / 2.0; + // Keep the minimum depth + float outZ = min(depths.w, min(depths.z, min(depths.x, depths.y))); outFragColor = vec4(vec3(outZ), 1.0); } From 60418b36d76da9f02db7b3236e90271e662b32aa Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Tue, 18 Sep 2018 15:18:06 +0200 Subject: [PATCH 025/286] Preparing for halton taps --- .../src/AmbientOcclusionEffect.cpp | 23 +++++++++-------- .../render-utils/src/SurfaceGeometryPass.cpp | 11 ++++---- .../render-utils/src/SurfaceGeometryPass.h | 7 +++--- .../src/render-utils/ShaderConstants.h | 4 +-- libraries/render-utils/src/ssao.slh | 25 +++++++++---------- .../surfaceGeometry_downsampleDepthNormal.slf | 2 -- 6 files changed, 36 insertions(+), 36 deletions(-) diff --git a/libraries/render-utils/src/AmbientOcclusionEffect.cpp b/libraries/render-utils/src/AmbientOcclusionEffect.cpp index dc08b9f28b..140833abec 100644 --- a/libraries/render-utils/src/AmbientOcclusionEffect.cpp +++ b/libraries/render-utils/src/AmbientOcclusionEffect.cpp @@ -183,7 +183,7 @@ AmbientOcclusionEffectConfig::AmbientOcclusionEffectConfig() : perspectiveScale{ 1.0f }, obscuranceLevel{ 0.5f }, #if SSAO_USE_HORIZON_BASED - falloffAngle{ 0.2f }, + falloffAngle{ 0.3f }, #else falloffAngle{ 0.01f }, #endif @@ -390,6 +390,7 @@ void AmbientOcclusionEffect::run(const render::RenderContextPointer& renderConte const auto& linearDepthFramebuffer = inputs.get2(); auto linearDepthTexture = linearDepthFramebuffer->getLinearDepthTexture(); + auto occlusionDepthTexture = linearDepthTexture; auto sourceViewport = args->_viewport; auto occlusionViewport = sourceViewport; auto firstBlurViewport = sourceViewport; @@ -406,6 +407,7 @@ void AmbientOcclusionEffect::run(const render::RenderContextPointer& renderConte if (_aoParametersBuffer->getResolutionLevel() > 0) { occlusionViewport = occlusionViewport >> _aoParametersBuffer->getResolutionLevel(); firstBlurViewport.w = firstBlurViewport.w >> _aoParametersBuffer->getResolutionLevel(); + occlusionDepthTexture = linearDepthFramebuffer->getHalfLinearDepthTexture(); } if (_framebuffer->updateLinearDepth(linearDepthTexture)) { @@ -444,21 +446,20 @@ void AmbientOcclusionEffect::run(const render::RenderContextPointer& renderConte batch.setModelTransform(model); #if SSAO_USE_HORIZON_BASED batch.setPipeline(mipCreationPipeline); - batch.generateTextureMipsWithPipeline(_framebuffer->getLinearDepthTexture()); + batch.generateTextureMipsWithPipeline(occlusionDepthTexture); #else - batch.generateTextureMips(_framebuffer->getLinearDepthTexture()); + batch.generateTextureMips(occlusionDepthTexture); #endif batch.popProfileRange(); + // Occlusion pass batch.pushProfileRange("Occlusion"); batch.setUniformBuffer(render_utils::slot::buffer::DeferredFrameTransform, frameTransform->getFrameTransformBuffer()); - batch.setUniformBuffer(render_utils::slot::buffer::SsaoParams, _aoParametersBuffer); - - // Occlusion pass + batch.setUniformBuffer(render_utils::slot::buffer::SsaoParams, _aoParametersBuffer); batch.setFramebuffer(occlusionFBO); batch.clearColorFramebuffer(gpu::Framebuffer::BUFFER_COLOR0, glm::vec4(1.0f)); batch.setPipeline(occlusionPipeline); - batch.setResourceTexture(render_utils::slot::texture::SsaoPyramid, _framebuffer->getLinearDepthTexture()); + batch.setResourceTexture(render_utils::slot::texture::SsaoDepth, occlusionDepthTexture); batch.draw(gpu::TRIANGLE_STRIP, 4); batch.popProfileRange(); @@ -469,6 +470,8 @@ void AmbientOcclusionEffect::run(const render::RenderContextPointer& renderConte batch.setModelTransform(model); batch.setViewportTransform(firstBlurViewport); batch.setFramebuffer(occlusionBlurredFBO); + // Use full resolution depth and normal for bilateral upscaling and blur + batch.setResourceTexture(render_utils::slot::texture::SsaoDepth, linearDepthTexture); batch.setUniformBuffer(render_utils::slot::buffer::SsaoBlurParams, _hblurParametersBuffer); batch.setPipeline(firstHBlurPipeline); batch.setResourceTexture(render_utils::slot::texture::SsaoOcclusion, occlusionFBO->getRenderBuffer(0)); @@ -485,7 +488,7 @@ void AmbientOcclusionEffect::run(const render::RenderContextPointer& renderConte batch.draw(gpu::TRIANGLE_STRIP, 4); } - batch.setResourceTexture(render_utils::slot::texture::SsaoPyramid, nullptr); + batch.setResourceTexture(render_utils::slot::texture::SsaoDepth, nullptr); batch.setResourceTexture(render_utils::slot::texture::SsaoOcclusion, nullptr); _gpuTimer->end(batch); @@ -581,11 +584,11 @@ void DebugAmbientOcclusion::run(const render::RenderContextPointer& renderContex batch.setUniformBuffer(render_utils::slot::buffer::SsaoDebugParams, _parametersBuffer); batch.setPipeline(debugPipeline); - batch.setResourceTexture(render_utils::slot::texture::SsaoPyramid, linearDepthTexture); + batch.setResourceTexture(render_utils::slot::texture::SsaoDepth, linearDepthTexture); batch.draw(gpu::TRIANGLE_STRIP, 4); - batch.setResourceTexture(render_utils::slot::texture::SsaoPyramid, nullptr); + batch.setResourceTexture(render_utils::slot::texture::SsaoDepth, nullptr); }); } diff --git a/libraries/render-utils/src/SurfaceGeometryPass.cpp b/libraries/render-utils/src/SurfaceGeometryPass.cpp index 6f455c2dbb..bf90aa3b22 100644 --- a/libraries/render-utils/src/SurfaceGeometryPass.cpp +++ b/libraries/render-utils/src/SurfaceGeometryPass.cpp @@ -28,10 +28,10 @@ namespace ru { LinearDepthFramebuffer::LinearDepthFramebuffer() { } -void LinearDepthFramebuffer::updatePrimaryDepth(const gpu::TexturePointer& depthBuffer) { +void LinearDepthFramebuffer::update(const gpu::TexturePointer& depthBuffer) { //If the depth buffer or size changed, we need to delete our FBOs bool reset = false; - if ((_primaryDepthTexture != depthBuffer)) { + if (_primaryDepthTexture != depthBuffer) { _primaryDepthTexture = depthBuffer; reset = true; } @@ -144,11 +144,12 @@ void LinearDepthPass::run(const render::RenderContextPointer& renderContext, con if (!_linearDepthFramebuffer) { _linearDepthFramebuffer = std::make_shared(); } - _linearDepthFramebuffer->updatePrimaryDepth(deferredFramebuffer->getPrimaryDepthTexture()); auto depthBuffer = deferredFramebuffer->getPrimaryDepthTexture(); auto normalTexture = deferredFramebuffer->getDeferredNormalTexture(); + _linearDepthFramebuffer->update(depthBuffer); + auto linearDepthFBO = _linearDepthFramebuffer->getLinearDepthFramebuffer(); auto linearDepthTexture = _linearDepthFramebuffer->getLinearDepthTexture(); @@ -247,7 +248,7 @@ const gpu::PipelinePointer& LinearDepthPass::getDownsamplePipeline(const render: SurfaceGeometryFramebuffer::SurfaceGeometryFramebuffer() { } -void SurfaceGeometryFramebuffer::updateLinearDepth(const gpu::TexturePointer& linearDepthBuffer) { +void SurfaceGeometryFramebuffer::update(const gpu::TexturePointer& linearDepthBuffer) { //If the depth buffer or size changed, we need to delete our FBOs bool reset = false; if ((_linearDepthTexture != linearDepthBuffer)) { @@ -414,7 +415,7 @@ void SurfaceGeometryPass::run(const render::RenderContextPointer& renderContext, if (!_surfaceGeometryFramebuffer) { _surfaceGeometryFramebuffer = std::make_shared(); } - _surfaceGeometryFramebuffer->updateLinearDepth(linearDepthTexture); + _surfaceGeometryFramebuffer->update(linearDepthTexture); auto curvatureFramebuffer = _surfaceGeometryFramebuffer->getCurvatureFramebuffer(); auto curvatureTexture = _surfaceGeometryFramebuffer->getCurvatureTexture(); diff --git a/libraries/render-utils/src/SurfaceGeometryPass.h b/libraries/render-utils/src/SurfaceGeometryPass.h index 367f599f67..1d8f77e67d 100644 --- a/libraries/render-utils/src/SurfaceGeometryPass.h +++ b/libraries/render-utils/src/SurfaceGeometryPass.h @@ -34,11 +34,10 @@ public: gpu::TexturePointer getHalfNormalTexture(); // Update the depth buffer which will drive the allocation of all the other resources according to its size. - void updatePrimaryDepth(const gpu::TexturePointer& depthBuffer); - gpu::TexturePointer getPrimaryDepthTexture(); + void update(const gpu::TexturePointer& depthBuffer); const glm::ivec2& getDepthFrameSize() const { return _frameSize; } - void setResolutionLevel(int level); + void setResolutionLevel(int level) { _resolutionLevel = std::max(0, level); } int getResolutionLevel() const { return _resolutionLevel; } protected: @@ -107,7 +106,7 @@ public: gpu::TexturePointer getBlurringTexture(); // Update the source framebuffer size which will drive the allocation of all the other resources. - void updateLinearDepth(const gpu::TexturePointer& linearDepthBuffer); + void update(const gpu::TexturePointer& linearDepthBuffer); gpu::TexturePointer getLinearDepthTexture(); const glm::ivec2& getSourceFrameSize() const { return _frameSize; } diff --git a/libraries/render-utils/src/render-utils/ShaderConstants.h b/libraries/render-utils/src/render-utils/ShaderConstants.h index 3aeb3a73c8..c6bdfb759c 100644 --- a/libraries/render-utils/src/render-utils/ShaderConstants.h +++ b/libraries/render-utils/src/render-utils/ShaderConstants.h @@ -87,7 +87,7 @@ #define RENDER_UTILS_BUFFER_SSAO_PARAMS 2 #define RENDER_UTILS_BUFFER_SSAO_DEBUG_PARAMS 3 #define RENDER_UTILS_BUFFER_SSAO_BLUR_PARAMS 4 -#define RENDER_UTILS_TEXTURE_SSAO_PYRAMID 1 +#define RENDER_UTILS_TEXTURE_SSAO_DEPTH 1 #define RENDER_UTILS_TEXTURE_SSAO_OCCLUSION 0 // Temporal anti-aliasing @@ -193,7 +193,7 @@ enum Texture { TaaDepth = RENDER_UTILS_TEXTURE_TAA_DEPTH, TaaNext = RENDER_UTILS_TEXTURE_TAA_NEXT, SsaoOcclusion = RENDER_UTILS_TEXTURE_SSAO_OCCLUSION, - SsaoPyramid = RENDER_UTILS_TEXTURE_SSAO_PYRAMID, + SsaoDepth = RENDER_UTILS_TEXTURE_SSAO_DEPTH, HighlightSceneDepth = RENDER_UTILS_TEXTURE_HIGHLIGHT_SCENE_DEPTH, HighlightDepth = RENDER_UTILS_TEXTURE_HIGHLIGHT_DEPTH, SurfaceGeometryDepth = RENDER_UTILS_TEXTURE_SG_DEPTH, diff --git a/libraries/render-utils/src/ssao.slh b/libraries/render-utils/src/ssao.slh index d9915f6de9..8a2b1ba0e2 100644 --- a/libraries/render-utils/src/ssao.slh +++ b/libraries/render-utils/src/ssao.slh @@ -216,14 +216,14 @@ vec3 getTapLocationClampedSSAO(int sampleNumber, float spinAngle, float outerRad <@func declareFetchDepthPyramidMap()@> // the depth pyramid texture -layout(binding=RENDER_UTILS_TEXTURE_SSAO_PYRAMID) uniform sampler2D pyramidMap; +layout(binding=RENDER_UTILS_TEXTURE_SSAO_DEPTH) uniform sampler2D depthPyramidTex; float getZEyeAtPixel(ivec2 pixel, int level) { - return -texelFetch(pyramidMap, pixel, level).x; + return -texelFetch(depthPyramidTex, pixel, level).x; } float getZEyeAtUV(vec2 texCoord, int level) { - return -texture(pyramidMap, texCoord, level).x; + return -texture(depthPyramidTex, texCoord, level).x; } const int LOG_MAX_OFFSET = 1; @@ -243,7 +243,7 @@ vec3 fetchTapUnfiltered(ivec4 side, ivec2 ssC, vec3 tap, vec2 sideImageSize) { vec3 P; P.xy = tapUV; - P.z = -texture(pyramidMap, fetchUV).x; + P.z = -texture(depthPyramidTex, fetchUV).x; return P; } @@ -259,7 +259,7 @@ vec4 fetchTap(ivec4 side, vec2 tapPixelPos, float tapRadius, vec2 sideImageSize) vec4 P; P.xy = tapUV; P.w = float(mipLevel); - P.z = -textureLod(pyramidMap, fetchUV, P.w).x; + P.z = -textureLod(depthPyramidTex, fetchUV, P.w).x; return P; } @@ -413,9 +413,9 @@ float fetchOcclusion(vec2 coords) { } const float BLUR_WEIGHT_OFFSET = 0.01; -const float BLUR_EDGE_SCALE = 300.0; +const float BLUR_EDGE_DISTANCE_SCALE = 300.0; -vec2 evalTapWeightedValue(ivec3 side, int r, ivec2 destPixelCoord, vec2 scaledTexCoord, vec2 fullTexCoord, float key) { +vec2 evalTapWeightedValue(ivec3 side, int r, ivec2 destPixelCoord, vec2 scaledTexCoord, vec2 fullTexCoord, float fragDepth, vec3 fragNormal) { ivec2 tapOffset = <$axis$> * r; ivec2 tapPixelCoord = destPixelCoord + tapOffset; @@ -432,9 +432,8 @@ vec2 evalTapWeightedValue(ivec3 side, int r, ivec2 destPixelCoord, vec2 scaledTe float weight = BLUR_WEIGHT_OFFSET + getBlurCoef(abs(r)); // range domain (the "bilateral" weight). As depth difference increases, decrease weight. -// weight *= max(0.0, 1.0 - (getBlurEdgeSharpness() * BLUR_EDGE_SCALE) * abs(tapDepth - key)); - float zDistance = tapDepth - key; - weight *= exp(-(getBlurEdgeSharpness() * BLUR_EDGE_SCALE) * zDistance * zDistance); + float zDistance = tapDepth - fragDepth; + weight *= exp(-getBlurEdgeSharpness() * (zDistance * zDistance * BLUR_EDGE_DISTANCE_SCALE)); return vec2(tapOcclusion * weight, weight); } @@ -443,14 +442,14 @@ vec3 getBlurredOcclusion(ivec2 destPixelCoord, vec2 scaledTexCoord, vec2 fullTex // Stereo side info ivec4 side = getStereoSideInfo(destPixelCoord.x, 0); - float pixelDepth = getZEyeAtUV(fullTexCoord, 0); + float fragDepth = getZEyeAtUV(fullTexCoord, 0); vec2 weightedSums = vec2(0.0); // Accumulate weighted contributions along the bluring axis in the [-radius, radius] range int blurRadius = getBlurRadius(); // negative side first for (int r = -blurRadius; r <= -1; ++r) { - weightedSums += evalTapWeightedValue(side.xyz, r, destPixelCoord, scaledTexCoord, fullTexCoord, pixelDepth); + weightedSums += evalTapWeightedValue(side.xyz, r, destPixelCoord, scaledTexCoord, fullTexCoord, fragDepth); } // Central pixel contribution @@ -460,7 +459,7 @@ vec3 getBlurredOcclusion(ivec2 destPixelCoord, vec2 scaledTexCoord, vec2 fullTex // then positive side for (int r = 1; r <= blurRadius; ++r) { - weightedSums += evalTapWeightedValue(side.xyz, r, destPixelCoord, scaledTexCoord, fullTexCoord, pixelDepth); + weightedSums += evalTapWeightedValue(side.xyz, r, destPixelCoord, scaledTexCoord, fullTexCoord, fragDepth); } // Final normalization diff --git a/libraries/render-utils/src/surfaceGeometry_downsampleDepthNormal.slf b/libraries/render-utils/src/surfaceGeometry_downsampleDepthNormal.slf index 34e78ea4ff..9f82f9138a 100644 --- a/libraries/render-utils/src/surfaceGeometry_downsampleDepthNormal.slf +++ b/libraries/render-utils/src/surfaceGeometry_downsampleDepthNormal.slf @@ -26,9 +26,7 @@ layout(location=1) out vec4 outNormal; void main(void) { // Gather 2 by 2 quads from texture and downsample - // Try different filters for Z vec4 Zeyes = textureGather(linearDepthMap, varTexCoord0, 0); - // float Zeye = texture(linearDepthMap, varTexCoord0).x; vec4 rawNormalsX = textureGather(normalMap, varTexCoord0, 0); vec4 rawNormalsY = textureGather(normalMap, varTexCoord0, 1); From 9141be91244ff37683f24e048e5d2fc6318014c6 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Tue, 18 Sep 2018 15:29:27 +0200 Subject: [PATCH 026/286] Shader compile fix --- .../src/AmbientOcclusionEffect.cpp | 2 +- libraries/render-utils/src/ssao.slh | 22 +++---------------- 2 files changed, 4 insertions(+), 20 deletions(-) diff --git a/libraries/render-utils/src/AmbientOcclusionEffect.cpp b/libraries/render-utils/src/AmbientOcclusionEffect.cpp index 140833abec..66e59ddad0 100644 --- a/libraries/render-utils/src/AmbientOcclusionEffect.cpp +++ b/libraries/render-utils/src/AmbientOcclusionEffect.cpp @@ -42,7 +42,7 @@ AmbientOcclusionFramebuffer::AmbientOcclusionFramebuffer() { } bool AmbientOcclusionFramebuffer::updateLinearDepth(const gpu::TexturePointer& linearDepthBuffer) { - //If the depth buffer or size changed, we need to delete our FBOs + // If the depth buffer or size changed, we need to delete our FBOs bool reset = false; if ((_linearDepthTexture != linearDepthBuffer)) { _linearDepthTexture = linearDepthBuffer; diff --git a/libraries/render-utils/src/ssao.slh b/libraries/render-utils/src/ssao.slh index 8a2b1ba0e2..63dadc9bec 100644 --- a/libraries/render-utils/src/ssao.slh +++ b/libraries/render-utils/src/ssao.slh @@ -226,28 +226,12 @@ float getZEyeAtUV(vec2 texCoord, int level) { return -texture(depthPyramidTex, texCoord, level).x; } -const int LOG_MAX_OFFSET = 1; -const int MAX_MIP_LEVEL = 5; int evalMipFromRadius(float radius) { + const int LOG_MAX_OFFSET = 1; + const int MAX_MIP_LEVEL = 5; return clamp(findMSB(int(radius)) - LOG_MAX_OFFSET, 0, MAX_MIP_LEVEL); } -vec3 fetchTapUnfiltered(ivec4 side, ivec2 ssC, vec3 tap, vec2 sideImageSize) { - ivec2 ssP = ivec2(tap.xy) + ssC; - ivec2 ssPFull = ivec2(ssP.x + side.y, ssP.y); - - - vec2 tapUV = (vec2(ssP) + vec2(0.5)) / sideImageSize; - - vec2 fetchUV = vec2(tapUV.x + float(side.w) * 0.5 * (float(side.x) - tapUV.x), tapUV.y); - - vec3 P; - P.xy = tapUV; - P.z = -texture(depthPyramidTex, fetchUV).x; - - return P; -} - vec4 fetchTap(ivec4 side, vec2 tapPixelPos, float tapRadius, vec2 sideImageSize) { int mipLevel = evalMipFromRadius(tapRadius * float(doFetchMips())); @@ -415,7 +399,7 @@ float fetchOcclusion(vec2 coords) { const float BLUR_WEIGHT_OFFSET = 0.01; const float BLUR_EDGE_DISTANCE_SCALE = 300.0; -vec2 evalTapWeightedValue(ivec3 side, int r, ivec2 destPixelCoord, vec2 scaledTexCoord, vec2 fullTexCoord, float fragDepth, vec3 fragNormal) { +vec2 evalTapWeightedValue(ivec3 side, int r, ivec2 destPixelCoord, vec2 scaledTexCoord, vec2 fullTexCoord, float fragDepth) { ivec2 tapOffset = <$axis$> * r; ivec2 tapPixelCoord = destPixelCoord + tapOffset; From 3d096d064438ff3eac10cbd89bf6b90673913dc9 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Tue, 18 Sep 2018 16:10:39 +0200 Subject: [PATCH 027/286] Added shared header --- .../src/AmbientOcclusionEffect.cpp | 44 ++++++++---------- .../render-utils/src/AmbientOcclusionEffect.h | 46 +++++++------------ libraries/render-utils/src/ssao.slh | 12 +---- .../src/ssao_makeHorizontalBlur.slf | 2 + .../render-utils/src/ssao_makeOcclusion.slf | 1 - .../src/ssao_makeVerticalBlur.slf | 2 + libraries/render-utils/src/ssao_shared.h | 44 ++++++++++++++++++ 7 files changed, 87 insertions(+), 64 deletions(-) create mode 100644 libraries/render-utils/src/ssao_shared.h diff --git a/libraries/render-utils/src/AmbientOcclusionEffect.cpp b/libraries/render-utils/src/AmbientOcclusionEffect.cpp index 66e59ddad0..0a7a15ecbd 100644 --- a/libraries/render-utils/src/AmbientOcclusionEffect.cpp +++ b/libraries/render-utils/src/AmbientOcclusionEffect.cpp @@ -30,9 +30,6 @@ #include "DependencyManager.h" #include "ViewFrustum.h" -// Should match value in ssao_makeOcclusion.slf -#define SSAO_USE_HORIZON_BASED 1 - gpu::PipelinePointer AmbientOcclusionEffect::_occlusionPipeline; gpu::PipelinePointer AmbientOcclusionEffect::_hBlurPipeline; gpu::PipelinePointer AmbientOcclusionEffect::_vBlurPipeline; @@ -203,13 +200,12 @@ AmbientOcclusionEffectConfig::AmbientOcclusionEffectConfig() : } -AmbientOcclusionEffect::AOParameters::AOParameters() : -resolutionInfo{ -1.0f, 0.0f, 1.0f, 0.0f }, -radiusInfo{ 0.5f, 0.5f * 0.5f, 1.0f / (0.25f * 0.25f * 0.25f), 1.0f }, -ditheringInfo{ 0.0f, 0.0f, 0.01f, 1.0f }, -sampleInfo{ 11.0f, 1.0f / 11.0f, 7.0f, 1.0f }, -blurInfo{ 1.0f, 3.0f, 2.0f, 0.0f } { - +AmbientOcclusionEffect::AOParameters::AOParameters() { + _resolutionInfo = { -1.0f, 0.0f, 1.0f, 0.0f }; + _radiusInfo = { 0.5f, 0.5f * 0.5f, 1.0f / (0.25f * 0.25f * 0.25f), 1.0f }; + _ditheringInfo = { 0.0f, 0.0f, 0.01f, 1.0f }; + _sampleInfo = { 11.0f, 1.0f / 11.0f, 7.0f, 1.0f }; + _blurInfo = { 1.0f, 3.0f, 2.0f, 0.0f }; } AmbientOcclusionEffect::AmbientOcclusionEffect() { @@ -224,7 +220,7 @@ void AmbientOcclusionEffect::configure(const Config& config) { const double RADIUS_POWER = 6.0; const auto& radius = config.radius; if (radius != _aoParametersBuffer->getRadius()) { - auto& current = _aoParametersBuffer.edit().radiusInfo; + auto& current = _aoParametersBuffer.edit()._radiusInfo; current.x = radius; current.y = radius * radius; current.z = 10.0f; @@ -234,40 +230,40 @@ void AmbientOcclusionEffect::configure(const Config& config) { } if (config.obscuranceLevel != _aoParametersBuffer->getObscuranceLevel()) { - auto& current = _aoParametersBuffer.edit().radiusInfo; + auto& current = _aoParametersBuffer.edit()._radiusInfo; current.w = config.obscuranceLevel; } if (config.falloffAngle != _aoParametersBuffer->getFalloffAngle()) { - auto& current = _aoParametersBuffer.edit().ditheringInfo; + auto& current = _aoParametersBuffer.edit()._ditheringInfo; current.z = config.falloffAngle; current.y = 1.0f / (1.0f - config.falloffAngle); } if (config.edgeSharpness != _aoParametersBuffer->getEdgeSharpness()) { - auto& current = _aoParametersBuffer.edit().blurInfo; + auto& current = _aoParametersBuffer.edit()._blurInfo; current.x = config.edgeSharpness; } if (config.blurDeviation != _aoParametersBuffer->getBlurDeviation()) { - auto& current = _aoParametersBuffer.edit().blurInfo; + auto& current = _aoParametersBuffer.edit()._blurInfo; current.z = config.blurDeviation; shouldUpdateGaussian = true; } if (config.numSpiralTurns != _aoParametersBuffer->getNumSpiralTurns()) { - auto& current = _aoParametersBuffer.edit().sampleInfo; + auto& current = _aoParametersBuffer.edit()._sampleInfo; current.z = config.numSpiralTurns; } if (config.numSamples != _aoParametersBuffer->getNumSamples()) { - auto& current = _aoParametersBuffer.edit().sampleInfo; + auto& current = _aoParametersBuffer.edit()._sampleInfo; current.x = config.numSamples; current.y = 1.0f / config.numSamples; } if (config.fetchMipsEnabled != _aoParametersBuffer->isFetchMipsEnabled()) { - auto& current = _aoParametersBuffer.edit().sampleInfo; + auto& current = _aoParametersBuffer.edit()._sampleInfo; current.w = (float)config.fetchMipsEnabled; } @@ -277,28 +273,28 @@ void AmbientOcclusionEffect::configure(const Config& config) { } if (config.perspectiveScale != _aoParametersBuffer->getPerspectiveScale()) { - _aoParametersBuffer.edit().resolutionInfo.z = config.perspectiveScale; + _aoParametersBuffer.edit()._resolutionInfo.z = config.perspectiveScale; } if (config.resolutionLevel != _aoParametersBuffer->getResolutionLevel()) { - auto& current = _aoParametersBuffer.edit().resolutionInfo; + auto& current = _aoParametersBuffer.edit()._resolutionInfo; current.x = (float) config.resolutionLevel; shouldUpdateBlurs = true; } if (config.blurRadius != _aoParametersBuffer->getBlurRadius()) { - auto& current = _aoParametersBuffer.edit().blurInfo; + auto& current = _aoParametersBuffer.edit()._blurInfo; current.y = (float)config.blurRadius; shouldUpdateGaussian = true; } if (config.ditheringEnabled != _aoParametersBuffer->isDitheringEnabled()) { - auto& current = _aoParametersBuffer.edit().ditheringInfo; + auto& current = _aoParametersBuffer.edit()._ditheringInfo; current.x = (float)config.ditheringEnabled; } if (config.borderingEnabled != _aoParametersBuffer->isBorderingEnabled()) { - auto& current = _aoParametersBuffer.edit().ditheringInfo; + auto& current = _aoParametersBuffer.edit()._ditheringInfo; current.w = (float)config.borderingEnabled; } @@ -377,7 +373,7 @@ const gpu::PipelinePointer& AmbientOcclusionEffect::getMipCreationPipeline() { void AmbientOcclusionEffect::updateGaussianDistribution() { auto coefs = _aoParametersBuffer.edit()._gaussianCoefs; - GaussianDistribution::evalSampling(coefs, AOParameters::GAUSSIAN_COEFS_LENGTH, _aoParametersBuffer->getBlurRadius(), _aoParametersBuffer->getBlurDeviation()); + GaussianDistribution::evalSampling(coefs, SSAO_BLUR_GAUSSIAN_COEFS_COUNT, _aoParametersBuffer->getBlurRadius(), _aoParametersBuffer->getBlurDeviation()); } void AmbientOcclusionEffect::run(const render::RenderContextPointer& renderContext, const Inputs& inputs, Outputs& outputs) { diff --git a/libraries/render-utils/src/AmbientOcclusionEffect.h b/libraries/render-utils/src/AmbientOcclusionEffect.h index 4021768995..86754d494b 100644 --- a/libraries/render-utils/src/AmbientOcclusionEffect.h +++ b/libraries/render-utils/src/AmbientOcclusionEffect.h @@ -115,42 +115,30 @@ public: void configure(const Config& config); void run(const render::RenderContextPointer& renderContext, const Inputs& inputs, Outputs& outputs); - + +#include "ssao_shared.h" // Class describing the uniform buffer with all the parameters common to the AO shaders - class AOParameters { + class AOParameters : public AmbientOcclusionParams { public: - // Resolution info - glm::vec4 resolutionInfo; - // radius info is { R, R^2, 1 / R^6, ObscuranceScale} - glm::vec4 radiusInfo; - // Dithering info - glm::vec4 ditheringInfo; - // Sampling info - glm::vec4 sampleInfo; - // Blurring info - glm::vec4 blurInfo; - // gaussian distribution coefficients first is the sampling radius (max is 6) - const static int GAUSSIAN_COEFS_LENGTH = 16; - float _gaussianCoefs[GAUSSIAN_COEFS_LENGTH]; - + AOParameters(); - int getResolutionLevel() const { return resolutionInfo.x; } - float getRadius() const { return radiusInfo.x; } - float getPerspectiveScale() const { return resolutionInfo.z; } - float getObscuranceLevel() const { return radiusInfo.w; } - float getFalloffAngle() const { return (float)ditheringInfo.z; } - float getEdgeSharpness() const { return (float)blurInfo.x; } - float getBlurDeviation() const { return blurInfo.z; } + int getResolutionLevel() const { return _resolutionInfo.x; } + float getRadius() const { return _radiusInfo.x; } + float getPerspectiveScale() const { return _resolutionInfo.z; } + float getObscuranceLevel() const { return _radiusInfo.w; } + float getFalloffAngle() const { return (float)_ditheringInfo.z; } + float getEdgeSharpness() const { return (float)_blurInfo.x; } + float getBlurDeviation() const { return _blurInfo.z; } - float getNumSpiralTurns() const { return sampleInfo.z; } - int getNumSamples() const { return (int)sampleInfo.x; } - bool isFetchMipsEnabled() const { return sampleInfo.w; } + float getNumSpiralTurns() const { return _sampleInfo.z; } + int getNumSamples() const { return (int)_sampleInfo.x; } + bool isFetchMipsEnabled() const { return _sampleInfo.w; } - int getBlurRadius() const { return (int)blurInfo.y; } - bool isDitheringEnabled() const { return ditheringInfo.x; } - bool isBorderingEnabled() const { return ditheringInfo.w; } + int getBlurRadius() const { return (int)_blurInfo.y; } + bool isDitheringEnabled() const { return _ditheringInfo.x; } + bool isBorderingEnabled() const { return _ditheringInfo.w; } }; using AOParametersBuffer = gpu::StructBuffer; diff --git a/libraries/render-utils/src/ssao.slh b/libraries/render-utils/src/ssao.slh index 63dadc9bec..38b7999924 100644 --- a/libraries/render-utils/src/ssao.slh +++ b/libraries/render-utils/src/ssao.slh @@ -12,6 +12,7 @@ <@def SSAO_SLH@> <@include render-utils/ShaderConstants.h@> +<@include ssao_shared.h@> <@func declarePackOcclusionDepth()@> @@ -35,15 +36,6 @@ vec2 unpackOcclusionDepth(vec3 raw) { <@include DeferredTransform.slh@> <$declareDeferredFrameTransform()$> -struct AmbientOcclusionParams { - vec4 _resolutionInfo; - vec4 _radiusInfo; - vec4 _ditheringInfo; - vec4 _sampleInfo; - vec4 _blurInfo; - float _gaussianCoefs[16]; -}; - layout(binding=RENDER_UTILS_BUFFER_SSAO_PARAMS) uniform ambientOcclusionParamsBuffer { AmbientOcclusionParams params; }; @@ -456,4 +448,4 @@ vec3 getBlurredOcclusion(ivec2 destPixelCoord, vec2 scaledTexCoord, vec2 fullTex <@endfunc@> -<@endif@> \ No newline at end of file +<@endif@> diff --git a/libraries/render-utils/src/ssao_makeHorizontalBlur.slf b/libraries/render-utils/src/ssao_makeHorizontalBlur.slf index 2d02ed02f0..fc90052eed 100644 --- a/libraries/render-utils/src/ssao_makeHorizontalBlur.slf +++ b/libraries/render-utils/src/ssao_makeHorizontalBlur.slf @@ -13,6 +13,8 @@ <@include ssao.slh@> +// Hack comment + const ivec2 horizontal = ivec2(1,0); <$declareBlurPass(horizontal)$> diff --git a/libraries/render-utils/src/ssao_makeOcclusion.slf b/libraries/render-utils/src/ssao_makeOcclusion.slf index 94e87c75e0..8c8832634c 100644 --- a/libraries/render-utils/src/ssao_makeOcclusion.slf +++ b/libraries/render-utils/src/ssao_makeOcclusion.slf @@ -19,7 +19,6 @@ <$declarePackOcclusionDepth()$> -#define SSAO_USE_HORIZON_BASED 1 #define SSAO_HBAO_MAX_RADIUS 100.0 layout(location=0) out vec4 outFragColor; diff --git a/libraries/render-utils/src/ssao_makeVerticalBlur.slf b/libraries/render-utils/src/ssao_makeVerticalBlur.slf index 1ef6666424..69b92000de 100644 --- a/libraries/render-utils/src/ssao_makeVerticalBlur.slf +++ b/libraries/render-utils/src/ssao_makeVerticalBlur.slf @@ -12,6 +12,8 @@ // <@include ssao.slh@> +// Hack comment + const ivec2 vertical = ivec2(0,1); <$declareBlurPass(vertical)$> diff --git a/libraries/render-utils/src/ssao_shared.h b/libraries/render-utils/src/ssao_shared.h new file mode 100644 index 0000000000..6b71c20c43 --- /dev/null +++ b/libraries/render-utils/src/ssao_shared.h @@ -0,0 +1,44 @@ +// + +// <@if not RENDER_UTILS_SSAO_SHARED_H@> +// <@def RENDER_UTILS_SSAO_SHARED_H@> + +// Hack comment to absorb the extra '//' scribe prepends + +#ifndef RENDER_UTILS_SSAO_SHARED_H +#define RENDER_UTILS_SSAO_SHARED_H + +#define SSAO_USE_HORIZON_BASED 1 +#define SSAO_BLUR_GAUSSIAN_COEFS_COUNT 16 + +// glsl / C++ compatible source as interface for Shadows +#ifdef __cplusplus +# define SSAO_VEC4 glm::vec4 +#else +# define SSAO_VEC4 vec4 +#endif + +struct AmbientOcclusionParams { + SSAO_VEC4 _resolutionInfo; + SSAO_VEC4 _radiusInfo; + SSAO_VEC4 _ditheringInfo; + SSAO_VEC4 _sampleInfo; + SSAO_VEC4 _blurInfo; + float _gaussianCoefs[SSAO_BLUR_GAUSSIAN_COEFS_COUNT]; +}; + +#endif // RENDER_UTILS_SHADER_CONSTANTS_H + +// <@if 1@> +// Trigger Scribe include +// <@endif@> + +// <@endif@> + +// Hack Comment \ No newline at end of file From 03814e7653fd8106d4209fb5abd8d67ab42902d1 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Tue, 18 Sep 2018 18:33:28 +0200 Subject: [PATCH 028/286] Preparing for split rendering of HBAO directions --- .../src/AmbientOcclusionEffect.cpp | 68 +++++++++++++++++-- .../render-utils/src/AmbientOcclusionEffect.h | 4 ++ .../src/render-utils/ShaderConstants.h | 2 + libraries/render-utils/src/ssao.slh | 14 +++- .../render-utils/src/ssao_makeOcclusion.slf | 3 +- libraries/render-utils/src/ssao_shared.h | 16 ++++- 6 files changed, 99 insertions(+), 8 deletions(-) diff --git a/libraries/render-utils/src/AmbientOcclusionEffect.cpp b/libraries/render-utils/src/AmbientOcclusionEffect.cpp index 0a7a15ecbd..c1101ad395 100644 --- a/libraries/render-utils/src/AmbientOcclusionEffect.cpp +++ b/libraries/render-utils/src/AmbientOcclusionEffect.cpp @@ -260,6 +260,23 @@ void AmbientOcclusionEffect::configure(const Config& config) { auto& current = _aoParametersBuffer.edit()._sampleInfo; current.x = config.numSamples; current.y = 1.0f / config.numSamples; + + // Regenerate halton sequence + const int B = config.numSamples + 1; + const float invB = 1.0f / (float)B; + + for (int i = 0; i < _randomSamples.size(); i++) { + int index = i+1; // Indices start at 1, not 0 + float f = 1.0f; + float r = 0.0f; + + while (index > 0) { + f = f * invB; + r = r + f * (float)(index % B); + index = index / B; + } + _randomSamples[i] = r; + } } if (config.fetchMipsEnabled != _aoParametersBuffer->isFetchMipsEnabled()) { @@ -280,6 +297,13 @@ void AmbientOcclusionEffect::configure(const Config& config) { auto& current = _aoParametersBuffer.edit()._resolutionInfo; current.x = (float) config.resolutionLevel; shouldUpdateBlurs = true; + + _aoFrameParametersBuffer[0].edit()._pixelOffsets = { 0, 0, 0, 0 }; +#if SSAO_USE_QUAD_SPLIT + _aoFrameParametersBuffer[1].edit()._pixelOffsets = { 1, 0, 0, 0 }; + _aoFrameParametersBuffer[2].edit()._pixelOffsets = { 1, 1, 0, 0 }; + _aoFrameParametersBuffer[3].edit()._pixelOffsets = { 0, 1, 0, 0 }; +#endif } if (config.blurRadius != _aoParametersBuffer->getBlurRadius()) { @@ -425,21 +449,29 @@ void AmbientOcclusionEffect::run(const render::RenderContextPointer& renderConte auto mipCreationPipeline = getMipCreationPipeline(); #endif + // Update sample rotation + const int SSAO_RANDOM_SAMPLE_COUNT = int(_randomSamples.size() / SSAO_SPLIT_COUNT); + for (int splitId=0 ; splitId < SSAO_SPLIT_COUNT ; splitId++) { + auto& sample = _aoFrameParametersBuffer[splitId].edit(); + sample._angleInfo.x = _randomSamples[_frameId + SSAO_RANDOM_SAMPLE_COUNT * splitId] * M_PI; + } + _frameId = (_frameId + 1) % SSAO_RANDOM_SAMPLE_COUNT; + gpu::doInBatch("AmbientOcclusionEffect::run", args->_context, [=](gpu::Batch& batch) { PROFILE_RANGE_BATCH(batch, "AmbientOcclusion"); batch.enableStereo(false); _gpuTimer->begin(batch); - batch.setViewportTransform(occlusionViewport); batch.setProjectionTransform(glm::mat4()); batch.resetViewTransform(); Transform model; + batch.setProjectionTransform(glm::mat4()); + batch.setModelTransform(model); // We need this with the mips levels batch.pushProfileRange("Depth mip creation"); - batch.setModelTransform(model); #if SSAO_USE_HORIZON_BASED batch.setPipeline(mipCreationPipeline); batch.generateTextureMipsWithPipeline(occlusionDepthTexture); @@ -450,13 +482,42 @@ void AmbientOcclusionEffect::run(const render::RenderContextPointer& renderConte // Occlusion pass batch.pushProfileRange("Occlusion"); + batch.setUniformBuffer(render_utils::slot::buffer::DeferredFrameTransform, frameTransform->getFrameTransformBuffer()); - batch.setUniformBuffer(render_utils::slot::buffer::SsaoParams, _aoParametersBuffer); + batch.setUniformBuffer(render_utils::slot::buffer::SsaoParams, _aoParametersBuffer); batch.setFramebuffer(occlusionFBO); batch.clearColorFramebuffer(gpu::Framebuffer::BUFFER_COLOR0, glm::vec4(1.0f)); batch.setPipeline(occlusionPipeline); batch.setResourceTexture(render_utils::slot::texture::SsaoDepth, occlusionDepthTexture); + +#if SSAO_USE_QUAD_SPLIT + { + auto splitViewport = occlusionViewport >> SSAO_USE_QUAD_SPLIT; + + batch.setViewportTransform(splitViewport); + batch.setUniformBuffer(render_utils::slot::buffer::SsaoFrameParams, _aoFrameParametersBuffer[0]); + batch.draw(gpu::TRIANGLE_STRIP, 4); + + splitViewport.x += splitViewport.z; + batch.setViewportTransform(splitViewport); + batch.setUniformBuffer(render_utils::slot::buffer::SsaoFrameParams, _aoFrameParametersBuffer[1]); + batch.draw(gpu::TRIANGLE_STRIP, 4); + + splitViewport.y += splitViewport.w; + batch.setViewportTransform(splitViewport); + batch.setUniformBuffer(render_utils::slot::buffer::SsaoFrameParams, _aoFrameParametersBuffer[2]); + batch.draw(gpu::TRIANGLE_STRIP, 4); + + splitViewport.x = 0; + batch.setViewportTransform(splitViewport); + batch.setUniformBuffer(render_utils::slot::buffer::SsaoFrameParams, _aoFrameParametersBuffer[3]); + batch.draw(gpu::TRIANGLE_STRIP, 4); + } +#else + batch.setUniformBuffer(render_utils::slot::buffer::SsaoFrameParams, _aoFrameParametersBuffer[0]); batch.draw(gpu::TRIANGLE_STRIP, 4); +#endif + batch.popProfileRange(); { @@ -552,7 +613,6 @@ void DebugAmbientOcclusion::run(const render::RenderContextPointer& renderContex linearDepthTexture = linearDepthFramebuffer->getHalfLinearDepthTexture(); occlusionViewport = occlusionViewport >> ambientOcclusionUniforms->getResolutionLevel(); } - auto framebufferSize = glm::ivec2(linearDepthTexture->getDimensions()); diff --git a/libraries/render-utils/src/AmbientOcclusionEffect.h b/libraries/render-utils/src/AmbientOcclusionEffect.h index 86754d494b..807c0b5ec8 100644 --- a/libraries/render-utils/src/AmbientOcclusionEffect.h +++ b/libraries/render-utils/src/AmbientOcclusionEffect.h @@ -153,11 +153,13 @@ private: }; using BlurParametersBuffer = gpu::StructBuffer; + using FrameParametersBuffer = gpu::StructBuffer< AmbientOcclusionFrameParams>; void updateGaussianDistribution(); void updateBlurParameters(); AOParametersBuffer _aoParametersBuffer; + FrameParametersBuffer _aoFrameParametersBuffer[SSAO_SPLIT_COUNT]; BlurParametersBuffer _vblurParametersBuffer; BlurParametersBuffer _hblurParametersBuffer; @@ -172,6 +174,8 @@ private: static gpu::PipelinePointer _mipCreationPipeline; AmbientOcclusionFramebufferPointer _framebuffer; + std::array _randomSamples; + int _frameId{ 0 }; gpu::RangeTimerPointer _gpuTimer; diff --git a/libraries/render-utils/src/render-utils/ShaderConstants.h b/libraries/render-utils/src/render-utils/ShaderConstants.h index c6bdfb759c..ce4e481ce3 100644 --- a/libraries/render-utils/src/render-utils/ShaderConstants.h +++ b/libraries/render-utils/src/render-utils/ShaderConstants.h @@ -87,6 +87,7 @@ #define RENDER_UTILS_BUFFER_SSAO_PARAMS 2 #define RENDER_UTILS_BUFFER_SSAO_DEBUG_PARAMS 3 #define RENDER_UTILS_BUFFER_SSAO_BLUR_PARAMS 4 +#define RENDER_UTILS_BUFFER_SSAO_FRAME_PARAMS 5 #define RENDER_UTILS_TEXTURE_SSAO_DEPTH 1 #define RENDER_UTILS_TEXTURE_SSAO_OCCLUSION 0 @@ -153,6 +154,7 @@ enum Buffer { LightClusterContent = RENDER_UTILS_BUFFER_LIGHT_CLUSTER_CONTENT, SsscParams = RENDER_UTILS_BUFFER_SSSC_PARAMS, SsaoParams = RENDER_UTILS_BUFFER_SSAO_PARAMS, + SsaoFrameParams = RENDER_UTILS_BUFFER_SSAO_FRAME_PARAMS, SsaoDebugParams = RENDER_UTILS_BUFFER_SSAO_DEBUG_PARAMS, SsaoBlurParams = RENDER_UTILS_BUFFER_SSAO_BLUR_PARAMS, LightIndex = RENDER_UTILS_BUFFER_LIGHT_INDEX, diff --git a/libraries/render-utils/src/ssao.slh b/libraries/render-utils/src/ssao.slh index 38b7999924..fefa424bbd 100644 --- a/libraries/render-utils/src/ssao.slh +++ b/libraries/render-utils/src/ssao.slh @@ -40,9 +40,11 @@ layout(binding=RENDER_UTILS_BUFFER_SSAO_PARAMS) uniform ambientOcclusionParamsBu AmbientOcclusionParams params; }; +layout(binding=RENDER_UTILS_BUFFER_SSAO_FRAME_PARAMS) uniform ambientOcclusionFrameParamsBuffer { + AmbientOcclusionFrameParams frameParams; +}; float getPerspectiveScale() { - return (params._resolutionInfo.z); } int getResolutionLevel() { @@ -136,8 +138,16 @@ float getAngleDitheringWorldPos(in vec3 pixelWorldPos) { } float getAngleDithering(in ivec2 pixelPos) { +#if SSAO_USE_QUAD_SPLIT + return isDitheringEnabled() * frameParams._angleInfo.x; +#else // Hash function used in the AlchemyAO paper - return isDitheringEnabled() * float((3 * pixelPos.x ^ pixelPos.y + pixelPos.x * pixelPos.y) * 10); + return isDitheringEnabled() * float((3 * pixelPos.x ^ pixelPos.y + pixelPos.x * pixelPos.y) * 10); +#endif +} + +ivec2 getPixelOffset() { + return frameParams._pixelOffsets.xy; } float evalDiskRadius(float Zeye, vec2 sideImageSize) { diff --git a/libraries/render-utils/src/ssao_makeOcclusion.slf b/libraries/render-utils/src/ssao_makeOcclusion.slf index 8c8832634c..e9e8c74f7b 100644 --- a/libraries/render-utils/src/ssao_makeOcclusion.slf +++ b/libraries/render-utils/src/ssao_makeOcclusion.slf @@ -25,10 +25,11 @@ layout(location=0) out vec4 outFragColor; void main(void) { vec2 sideImageSize = getSideImageSize(getResolutionLevel()); + ivec2 splitImageSize = ivec2(getWidthHeight(getResolutionLevel() + SSAO_USE_QUAD_SPLIT)); // Pixel being shaded vec2 fragCoord = gl_FragCoord.xy; - ivec2 fragPixelPos = ivec2(fragCoord.xy); + ivec2 fragPixelPos = ((ivec2(fragCoord.xy) - getPixelOffset()*splitImageSize) << SSAO_USE_QUAD_SPLIT) + getPixelOffset(); // Stereo side info ivec4 side = getStereoSideInfo(fragPixelPos.x, getResolutionLevel()); diff --git a/libraries/render-utils/src/ssao_shared.h b/libraries/render-utils/src/ssao_shared.h index 6b71c20c43..61bf14ccf8 100644 --- a/libraries/render-utils/src/ssao_shared.h +++ b/libraries/render-utils/src/ssao_shared.h @@ -15,13 +15,22 @@ #define RENDER_UTILS_SSAO_SHARED_H #define SSAO_USE_HORIZON_BASED 1 +#define SSAO_USE_QUAD_SPLIT 1 #define SSAO_BLUR_GAUSSIAN_COEFS_COUNT 16 -// glsl / C++ compatible source as interface for Shadows +#if SSAO_USE_QUAD_SPLIT +#define SSAO_SPLIT_COUNT 4 +#else +#define SSAO_SPLIT_COUNT 1 +#endif + +// glsl / C++ compatible source as interface for ambient occlusion #ifdef __cplusplus # define SSAO_VEC4 glm::vec4 +# define SSAO_IVEC4 glm::ivec4 #else # define SSAO_VEC4 vec4 +# define SSAO_IVEC4 ivec4 #endif struct AmbientOcclusionParams { @@ -33,6 +42,11 @@ struct AmbientOcclusionParams { float _gaussianCoefs[SSAO_BLUR_GAUSSIAN_COEFS_COUNT]; }; +struct AmbientOcclusionFrameParams { + SSAO_VEC4 _angleInfo; + SSAO_IVEC4 _pixelOffsets; +}; + #endif // RENDER_UTILS_SHADER_CONSTANTS_H // <@if 1@> From 3493d40fd74f566e69830c57aaae6cb1b76128cc Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Wed, 19 Sep 2018 10:37:27 +0200 Subject: [PATCH 029/286] Added gather to split AO --- .../src/AmbientOcclusionEffect.cpp | 34 ++++++++++++++++++- .../render-utils/src/AmbientOcclusionEffect.h | 6 ++-- .../src/render-utils/ssao_gather.slp | 1 + libraries/render-utils/src/ssao.slh | 8 +++++ libraries/render-utils/src/ssao_gather.slf | 34 +++++++++++++++++++ .../render-utils/src/ssao_makeOcclusion.slf | 21 ++++-------- 6 files changed, 86 insertions(+), 18 deletions(-) create mode 100644 libraries/render-utils/src/render-utils/ssao_gather.slp create mode 100644 libraries/render-utils/src/ssao_gather.slf diff --git a/libraries/render-utils/src/AmbientOcclusionEffect.cpp b/libraries/render-utils/src/AmbientOcclusionEffect.cpp index c1101ad395..9563192739 100644 --- a/libraries/render-utils/src/AmbientOcclusionEffect.cpp +++ b/libraries/render-utils/src/AmbientOcclusionEffect.cpp @@ -34,6 +34,7 @@ gpu::PipelinePointer AmbientOcclusionEffect::_occlusionPipeline; gpu::PipelinePointer AmbientOcclusionEffect::_hBlurPipeline; gpu::PipelinePointer AmbientOcclusionEffect::_vBlurPipeline; gpu::PipelinePointer AmbientOcclusionEffect::_mipCreationPipeline; +gpu::PipelinePointer AmbientOcclusionEffect::_gatherPipeline; AmbientOcclusionFramebuffer::AmbientOcclusionFramebuffer() { } @@ -395,6 +396,19 @@ const gpu::PipelinePointer& AmbientOcclusionEffect::getMipCreationPipeline() { return _mipCreationPipeline; } +const gpu::PipelinePointer& AmbientOcclusionEffect::getGatherPipeline() { + if (!_gatherPipeline) { + gpu::ShaderPointer program = gpu::Shader::createProgram(shader::render_utils::program::ssao_gather); + gpu::StatePointer state = gpu::StatePointer(new gpu::State()); + + state->setColorWriteMask(true, true, true, false); + + // Good to go add the brand new pipeline + _gatherPipeline = gpu::Pipeline::create(program, state); + } + return _gatherPipeline; +} + void AmbientOcclusionEffect::updateGaussianDistribution() { auto coefs = _aoParametersBuffer.edit()._gaussianCoefs; GaussianDistribution::evalSampling(coefs, SSAO_BLUR_GAUSSIAN_COEFS_COUNT, _aoParametersBuffer->getBlurRadius(), _aoParametersBuffer->getBlurDeviation()); @@ -448,7 +462,10 @@ void AmbientOcclusionEffect::run(const render::RenderContextPointer& renderConte #if SSAO_USE_HORIZON_BASED auto mipCreationPipeline = getMipCreationPipeline(); #endif - +#if SSAO_USE_QUAD_SPLIT + auto gatherPipeline = getGatherPipeline(); +#endif + // Update sample rotation const int SSAO_RANDOM_SAMPLE_COUNT = int(_randomSamples.size() / SSAO_SPLIT_COUNT); for (int splitId=0 ; splitId < SSAO_SPLIT_COUNT ; splitId++) { @@ -485,7 +502,11 @@ void AmbientOcclusionEffect::run(const render::RenderContextPointer& renderConte batch.setUniformBuffer(render_utils::slot::buffer::DeferredFrameTransform, frameTransform->getFrameTransformBuffer()); batch.setUniformBuffer(render_utils::slot::buffer::SsaoParams, _aoParametersBuffer); +#if SSAO_USE_QUAD_SPLIT + batch.setFramebuffer(occlusionBlurredFBO); +#else batch.setFramebuffer(occlusionFBO); +#endif batch.clearColorFramebuffer(gpu::Framebuffer::BUFFER_COLOR0, glm::vec4(1.0f)); batch.setPipeline(occlusionPipeline); batch.setResourceTexture(render_utils::slot::texture::SsaoDepth, occlusionDepthTexture); @@ -520,6 +541,17 @@ void AmbientOcclusionEffect::run(const render::RenderContextPointer& renderConte batch.popProfileRange(); +#if SSAO_USE_QUAD_SPLIT + // Gather back the four separate renders into one interleaved one + batch.pushProfileRange("Gather"); + batch.setViewportTransform(occlusionViewport); + batch.setFramebuffer(occlusionFBO); + batch.setPipeline(gatherPipeline); + batch.setResourceTexture(render_utils::slot::texture::SsaoOcclusion, occlusionBlurredFBO->getRenderBuffer(0)); + batch.draw(gpu::TRIANGLE_STRIP, 4); + batch.popProfileRange(); +#endif + { PROFILE_RANGE_BATCH(batch, "Bilateral Blur"); // Blur 1st pass diff --git a/libraries/render-utils/src/AmbientOcclusionEffect.h b/libraries/render-utils/src/AmbientOcclusionEffect.h index 807c0b5ec8..ee64dd3912 100644 --- a/libraries/render-utils/src/AmbientOcclusionEffect.h +++ b/libraries/render-utils/src/AmbientOcclusionEffect.h @@ -167,11 +167,13 @@ private: static const gpu::PipelinePointer& getHBlurPipeline(); // first static const gpu::PipelinePointer& getVBlurPipeline(); // second static const gpu::PipelinePointer& getMipCreationPipeline(); + static const gpu::PipelinePointer& getGatherPipeline(); - static gpu::PipelinePointer _occlusionPipeline; + static gpu::PipelinePointer _occlusionPipeline; static gpu::PipelinePointer _hBlurPipeline; static gpu::PipelinePointer _vBlurPipeline; - static gpu::PipelinePointer _mipCreationPipeline; + static gpu::PipelinePointer _mipCreationPipeline; + static gpu::PipelinePointer _gatherPipeline; AmbientOcclusionFramebufferPointer _framebuffer; std::array _randomSamples; diff --git a/libraries/render-utils/src/render-utils/ssao_gather.slp b/libraries/render-utils/src/render-utils/ssao_gather.slp new file mode 100644 index 0000000000..d4d8ec4b01 --- /dev/null +++ b/libraries/render-utils/src/render-utils/ssao_gather.slp @@ -0,0 +1 @@ +VERTEX gpu::vertex::DrawViewportQuadTransformTexcoord diff --git a/libraries/render-utils/src/ssao.slh b/libraries/render-utils/src/ssao.slh index fefa424bbd..b1103fbbac 100644 --- a/libraries/render-utils/src/ssao.slh +++ b/libraries/render-utils/src/ssao.slh @@ -364,6 +364,14 @@ float evalVisibilityHBAO(ivec4 side, vec2 shadedPixelPos, vec2 sideImageSize, ve <@endfunc@> +<@func declareGatherPass()@> +<$declareAmbientOcclusion()$> + +// the source occlusion texture +layout(binding=RENDER_UTILS_TEXTURE_SSAO_OCCLUSION) uniform sampler2D occlusionMap; + +<@endfunc@> + <@func declareBlurPass(axis)@> <$declarePackOcclusionDepth()$> diff --git a/libraries/render-utils/src/ssao_gather.slf b/libraries/render-utils/src/ssao_gather.slf new file mode 100644 index 0000000000..32edec515b --- /dev/null +++ b/libraries/render-utils/src/ssao_gather.slf @@ -0,0 +1,34 @@ +<@include gpu/Config.slh@> +<$VERSION_HEADER$> +// Generated on <$_SCRIBE_DATE$> +// +// ssao_gather.frag +// +// Created by Olivier Prat on 09/19/2018. +// Copyright 2018 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 ssao.slh@> + +// Hack comment + +<$declareGatherPass()$> + +layout(location=0) in vec4 varTexCoord0; + +layout(location=0) out vec4 outFragColor; + +void main(void) { + // Gather the four splits of the occlusion result back into an interleaved full size + // result (at the resolution level, of course) + ivec2 destPixelCoord = ivec2(gl_FragCoord.xy); + ivec2 sourcePixelCoord = destPixelCoord >> 1; + ivec2 splitImageSize = ivec2(getWidthHeight(getResolutionLevel() + 1)); + + sourcePixelCoord += (destPixelCoord & ivec2(1)) * splitImageSize; + + outFragColor = texelFetch(occlusionMap, sourcePixelCoord, 0); +} diff --git a/libraries/render-utils/src/ssao_makeOcclusion.slf b/libraries/render-utils/src/ssao_makeOcclusion.slf index e9e8c74f7b..bdaa87fff9 100644 --- a/libraries/render-utils/src/ssao_makeOcclusion.slf +++ b/libraries/render-utils/src/ssao_makeOcclusion.slf @@ -25,11 +25,14 @@ layout(location=0) out vec4 outFragColor; void main(void) { vec2 sideImageSize = getSideImageSize(getResolutionLevel()); - ivec2 splitImageSize = ivec2(getWidthHeight(getResolutionLevel() + SSAO_USE_QUAD_SPLIT)); // Pixel being shaded vec2 fragCoord = gl_FragCoord.xy; - ivec2 fragPixelPos = ((ivec2(fragCoord.xy) - getPixelOffset()*splitImageSize) << SSAO_USE_QUAD_SPLIT) + getPixelOffset(); + ivec2 fragPixelPos = ivec2(fragCoord.xy); +#if SSAO_USE_QUAD_SPLIT + ivec2 splitImageSize = ivec2(getWidthHeight(getResolutionLevel() + SSAO_USE_QUAD_SPLIT)); + fragPixelPos = ((fragPixelPos - getPixelOffset()*splitImageSize) << SSAO_USE_QUAD_SPLIT) + getPixelOffset(); +#endif // Stereo side info ivec4 side = getStereoSideInfo(fragPixelPos.x, getResolutionLevel()); @@ -41,7 +44,7 @@ void main(void) { // Fetch the z under the pixel (stereo or not) float Zeye = getZEyeAtUV(fragUVPos, 0); - // The position and normal of the pixel fragment in Eye space + // The position and normal of the pixel fragment in Eye space vec3 fragPositionES = evalEyePositionFromZeye(side.x, Zeye, fragUVPos); vec3 fragNormalES = evalEyeNormal(fragPositionES); @@ -71,18 +74,6 @@ void main(void) { } float occlusion = clamp(1.0 - obscuranceSum * getObscuranceScaling() * getInvNumSamples(), 0.0, 1.0); - - // KEEP IT for Debugging - // Bilateral box-filter over a quad for free, respecting depth edges - // (the difference that this makes is subtle) -/* if (abs(dFdx(fragPositionES.z)) < 0.02) { - occlusion -= dFdx(occlusion) * ((fragPixelPos.x & 1) - 0.5); - } - if (abs(dFdy(fragPositionES.z)) < 0.02) { - occlusion -= dFdy(occlusion) * ((fragPixelPos.y & 1) - 0.5); - }*/ - //outFragColor = vec4(packOcclusionDepth(occlusion, CSZToDepthKey(fragPositionES.z)), 1.0); outFragColor = vec4(vec3(occlusion), 1.0); - } From 8a11d18f0d750451832fbd70718219a8217a63d2 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Wed, 19 Sep 2018 17:24:31 +0200 Subject: [PATCH 030/286] Quarter resolution with split rendering --- .../src/AmbientOcclusionEffect.cpp | 159 ++++++++++++------ .../render-utils/src/AmbientOcclusionEffect.h | 14 +- .../render-utils/src/SurfaceGeometryPass.cpp | 11 +- .../render-utils/src/SurfaceGeometryPass.h | 4 +- .../src/render-utils/ShaderConstants.h | 2 + .../src/render-utils/ssao_buildNormals.slp | 1 + libraries/render-utils/src/ssao.slh | 37 ++-- .../render-utils/src/ssao_buildNormals.slf | 45 +++++ .../render-utils/src/ssao_makeOcclusion.slf | 4 + libraries/render-utils/src/ssao_shared.h | 2 +- .../utilities/render/ambientOcclusionPass.qml | 6 +- 11 files changed, 194 insertions(+), 91 deletions(-) create mode 100644 libraries/render-utils/src/render-utils/ssao_buildNormals.slp create mode 100644 libraries/render-utils/src/ssao_buildNormals.slf diff --git a/libraries/render-utils/src/AmbientOcclusionEffect.cpp b/libraries/render-utils/src/AmbientOcclusionEffect.cpp index 9563192739..667127983a 100644 --- a/libraries/render-utils/src/AmbientOcclusionEffect.cpp +++ b/libraries/render-utils/src/AmbientOcclusionEffect.cpp @@ -35,6 +35,7 @@ gpu::PipelinePointer AmbientOcclusionEffect::_hBlurPipeline; gpu::PipelinePointer AmbientOcclusionEffect::_vBlurPipeline; gpu::PipelinePointer AmbientOcclusionEffect::_mipCreationPipeline; gpu::PipelinePointer AmbientOcclusionEffect::_gatherPipeline; +gpu::PipelinePointer AmbientOcclusionEffect::_buildNormalsPipeline; AmbientOcclusionFramebuffer::AmbientOcclusionFramebuffer() { } @@ -66,6 +67,8 @@ void AmbientOcclusionFramebuffer::clear() { _occlusionTexture.reset(); _occlusionBlurredFramebuffer.reset(); _occlusionBlurredTexture.reset(); + _normalFramebuffer.reset(); + _normalTexture.reset(); } gpu::TexturePointer AmbientOcclusionFramebuffer::getLinearDepthTexture() { @@ -73,18 +76,21 @@ gpu::TexturePointer AmbientOcclusionFramebuffer::getLinearDepthTexture() { } void AmbientOcclusionFramebuffer::allocate() { - auto width = _frameSize.x; auto height = _frameSize.y; auto format = gpu::Element::COLOR_R_8; - _occlusionTexture = gpu::Texture::createRenderBuffer(format, width, height, gpu::Texture::SINGLE_MIP, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR_MIP_POINT, gpu::Sampler::WRAP_CLAMP)); + _occlusionTexture = gpu::Texture::createRenderBuffer(format, width, height, gpu::Texture::SINGLE_MIP, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR, gpu::Sampler::WRAP_CLAMP)); _occlusionFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("occlusion")); _occlusionFramebuffer->setRenderBuffer(0, _occlusionTexture); - _occlusionBlurredTexture = gpu::Texture::createRenderBuffer(format, width, height, gpu::Texture::SINGLE_MIP, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR_MIP_POINT, gpu::Sampler::WRAP_CLAMP)); + _occlusionBlurredTexture = gpu::Texture::createRenderBuffer(format, width, height, gpu::Texture::SINGLE_MIP, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR, gpu::Sampler::WRAP_CLAMP)); _occlusionBlurredFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("occlusionBlurred")); _occlusionBlurredFramebuffer->setRenderBuffer(0, _occlusionBlurredTexture); + + _normalTexture = gpu::Texture::createRenderBuffer(gpu::Element::COLOR_R11G11B10, width, height, gpu::Texture::SINGLE_MIP, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_POINT, gpu::Sampler::WRAP_CLAMP)); + _normalFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("ssaoNormals")); + _normalFramebuffer->setRenderBuffer(0, _normalTexture); } gpu::FramebufferPointer AmbientOcclusionFramebuffer::getOcclusionFramebuffer() { @@ -115,6 +121,19 @@ gpu::TexturePointer AmbientOcclusionFramebuffer::getOcclusionBlurredTexture() { return _occlusionBlurredTexture; } +gpu::FramebufferPointer AmbientOcclusionFramebuffer::getNormalFramebuffer() { + if (!_normalFramebuffer) { + allocate(); + } + return _normalFramebuffer; +} + +gpu::TexturePointer AmbientOcclusionFramebuffer::getNormalTexture() { + if (!_normalTexture) { + allocate(); + } + return _normalTexture; +} class GaussianDistribution { public: @@ -189,11 +208,11 @@ AmbientOcclusionEffectConfig::AmbientOcclusionEffectConfig() : blurDeviation{ 2.5f }, numSpiralTurns{ 7.0f }, #if SSAO_USE_HORIZON_BASED - numSamples{ 1 }, + numSamples{ 2 }, #else numSamples{ 16 }, #endif - resolutionLevel{ 1 }, + resolutionLevel{ 2 }, blurRadius{ 4 }, ditheringEnabled{ true }, borderingEnabled{ true }, @@ -262,8 +281,8 @@ void AmbientOcclusionEffect::configure(const Config& config) { current.x = config.numSamples; current.y = 1.0f / config.numSamples; - // Regenerate halton sequence - const int B = config.numSamples + 1; + // Regenerate offsets + const int B = 3; const float invB = 1.0f / (float)B; for (int i = 0; i < _randomSamples.size(); i++) { @@ -276,7 +295,7 @@ void AmbientOcclusionEffect::configure(const Config& config) { r = r + f * (float)(index % B); index = index / B; } - _randomSamples[i] = r; + _randomSamples[i] = r * M_PI / config.numSamples; } } @@ -296,7 +315,7 @@ void AmbientOcclusionEffect::configure(const Config& config) { if (config.resolutionLevel != _aoParametersBuffer->getResolutionLevel()) { auto& current = _aoParametersBuffer.edit()._resolutionInfo; - current.x = (float) config.resolutionLevel; + current.x = (float)config.resolutionLevel; shouldUpdateBlurs = true; _aoFrameParametersBuffer[0].edit()._pixelOffsets = { 0, 0, 0, 0 }; @@ -307,7 +326,7 @@ void AmbientOcclusionEffect::configure(const Config& config) { #endif } - if (config.blurRadius != _aoParametersBuffer->getBlurRadius()) { + if (config.blurRadius != _aoParametersBuffer.get().getBlurRadius()) { auto& current = _aoParametersBuffer.edit()._blurInfo; current.y = (float)config.blurRadius; shouldUpdateGaussian = true; @@ -361,7 +380,6 @@ const gpu::PipelinePointer& AmbientOcclusionEffect::getOcclusionPipeline() { return _occlusionPipeline; } - const gpu::PipelinePointer& AmbientOcclusionEffect::getHBlurPipeline() { if (!_hBlurPipeline) { gpu::ShaderPointer program = gpu::Shader::createProgram(shader::render_utils::program::ssao_makeHorizontalBlur); @@ -409,9 +427,24 @@ const gpu::PipelinePointer& AmbientOcclusionEffect::getGatherPipeline() { return _gatherPipeline; } +const gpu::PipelinePointer& AmbientOcclusionEffect::getBuildNormalsPipeline() { + if (!_buildNormalsPipeline) { + gpu::ShaderPointer program = gpu::Shader::createProgram(shader::render_utils::program::ssao_buildNormals); + gpu::StatePointer state = gpu::StatePointer(new gpu::State()); + + state->setColorWriteMask(true, true, true, false); + + // Good to go add the brand new pipeline + _buildNormalsPipeline = gpu::Pipeline::create(program, state); + } + return _buildNormalsPipeline; +} + void AmbientOcclusionEffect::updateGaussianDistribution() { - auto coefs = _aoParametersBuffer.edit()._gaussianCoefs; - GaussianDistribution::evalSampling(coefs, SSAO_BLUR_GAUSSIAN_COEFS_COUNT, _aoParametersBuffer->getBlurRadius(), _aoParametersBuffer->getBlurDeviation()); + auto filterTaps = _aoParametersBuffer.edit()._blurFilterTaps; + auto blurRadius = _aoParametersBuffer.get().getBlurRadius(); + + GaussianDistribution::evalSampling(filterTaps, SSAO_BLUR_GAUSSIAN_COEFS_COUNT, blurRadius, _aoParametersBuffer->getBlurDeviation() * 4.0f / blurRadius); } void AmbientOcclusionEffect::run(const render::RenderContextPointer& renderContext, const Inputs& inputs, Outputs& outputs) { @@ -436,7 +469,7 @@ void AmbientOcclusionEffect::run(const render::RenderContextPointer& renderConte if (!_framebuffer) { _framebuffer = std::make_shared(); } - + const auto resolutionScale = powf(0.5f, _aoParametersBuffer->getResolutionLevel()); if (_aoParametersBuffer->getResolutionLevel() > 0) { occlusionViewport = occlusionViewport >> _aoParametersBuffer->getResolutionLevel(); @@ -464,15 +497,19 @@ void AmbientOcclusionEffect::run(const render::RenderContextPointer& renderConte #endif #if SSAO_USE_QUAD_SPLIT auto gatherPipeline = getGatherPipeline(); + auto buildNormalsPipeline = getBuildNormalsPipeline(); + auto occlusionNormalFramebuffer = _framebuffer->getNormalFramebuffer(); + auto occlusionNormalTexture = _framebuffer->getNormalTexture(); #endif + auto fullNormalTexture = linearDepthFramebuffer->getNormalTexture(); // Update sample rotation const int SSAO_RANDOM_SAMPLE_COUNT = int(_randomSamples.size() / SSAO_SPLIT_COUNT); for (int splitId=0 ; splitId < SSAO_SPLIT_COUNT ; splitId++) { auto& sample = _aoFrameParametersBuffer[splitId].edit(); - sample._angleInfo.x = _randomSamples[_frameId + SSAO_RANDOM_SAMPLE_COUNT * splitId] * M_PI; + sample._angleInfo.x = _randomSamples[splitId + SSAO_RANDOM_SAMPLE_COUNT * _frameId]; } - _frameId = (_frameId + 1) % SSAO_RANDOM_SAMPLE_COUNT; + // TEMPO OP _frameId = (_frameId + 1) % SSAO_RANDOM_SAMPLE_COUNT; gpu::doInBatch("AmbientOcclusionEffect::run", args->_context, [=](gpu::Batch& batch) { PROFILE_RANGE_BATCH(batch, "AmbientOcclusion"); @@ -480,7 +517,6 @@ void AmbientOcclusionEffect::run(const render::RenderContextPointer& renderConte _gpuTimer->begin(batch); - batch.setProjectionTransform(glm::mat4()); batch.resetViewTransform(); Transform model; @@ -490,53 +526,67 @@ void AmbientOcclusionEffect::run(const render::RenderContextPointer& renderConte // We need this with the mips levels batch.pushProfileRange("Depth mip creation"); #if SSAO_USE_HORIZON_BASED - batch.setPipeline(mipCreationPipeline); - batch.generateTextureMipsWithPipeline(occlusionDepthTexture); + batch.setPipeline(mipCreationPipeline); + batch.generateTextureMipsWithPipeline(occlusionDepthTexture); #else - batch.generateTextureMips(occlusionDepthTexture); + batch.generateTextureMips(occlusionDepthTexture); #endif batch.popProfileRange(); +#if SSAO_USE_QUAD_SPLIT + // Build derivative normals pass + batch.pushProfileRange("Build Normals"); + batch.setViewportTransform(sourceViewport); + batch.setPipeline(buildNormalsPipeline); + batch.setUniformBuffer(render_utils::slot::buffer::DeferredFrameTransform, frameTransform->getFrameTransformBuffer()); + batch.setUniformBuffer(render_utils::slot::buffer::SsaoParams, _aoParametersBuffer); + batch.setFramebuffer(occlusionNormalFramebuffer); + batch.setResourceTexture(render_utils::slot::texture::SsaoDepth, linearDepthTexture); + batch.draw(gpu::TRIANGLE_STRIP, 4); + batch.popProfileRange(); +#endif + // Occlusion pass batch.pushProfileRange("Occlusion"); - batch.setUniformBuffer(render_utils::slot::buffer::DeferredFrameTransform, frameTransform->getFrameTransformBuffer()); - batch.setUniformBuffer(render_utils::slot::buffer::SsaoParams, _aoParametersBuffer); + batch.setUniformBuffer(render_utils::slot::buffer::DeferredFrameTransform, frameTransform->getFrameTransformBuffer()); + batch.setUniformBuffer(render_utils::slot::buffer::SsaoParams, _aoParametersBuffer); #if SSAO_USE_QUAD_SPLIT - batch.setFramebuffer(occlusionBlurredFBO); + batch.setFramebuffer(occlusionBlurredFBO); #else - batch.setFramebuffer(occlusionFBO); + batch.setFramebuffer(occlusionFBO); #endif - batch.clearColorFramebuffer(gpu::Framebuffer::BUFFER_COLOR0, glm::vec4(1.0f)); - batch.setPipeline(occlusionPipeline); - batch.setResourceTexture(render_utils::slot::texture::SsaoDepth, occlusionDepthTexture); + batch.clearColorFramebuffer(gpu::Framebuffer::BUFFER_COLOR0, glm::vec4(1.0f)); + batch.setPipeline(occlusionPipeline); + batch.setResourceTexture(render_utils::slot::texture::SsaoDepth, occlusionDepthTexture); #if SSAO_USE_QUAD_SPLIT - { - auto splitViewport = occlusionViewport >> SSAO_USE_QUAD_SPLIT; + batch.setResourceTexture(render_utils::slot::texture::SsaoNormal, occlusionNormalTexture); + { + auto splitViewport = occlusionViewport >> SSAO_USE_QUAD_SPLIT; - batch.setViewportTransform(splitViewport); + batch.setViewportTransform(splitViewport); + batch.setUniformBuffer(render_utils::slot::buffer::SsaoFrameParams, _aoFrameParametersBuffer[0]); + batch.draw(gpu::TRIANGLE_STRIP, 4); + + splitViewport.x += splitViewport.z; + batch.setViewportTransform(splitViewport); + batch.setUniformBuffer(render_utils::slot::buffer::SsaoFrameParams, _aoFrameParametersBuffer[1]); + batch.draw(gpu::TRIANGLE_STRIP, 4); + + splitViewport.y += splitViewport.w; + batch.setViewportTransform(splitViewport); + batch.setUniformBuffer(render_utils::slot::buffer::SsaoFrameParams, _aoFrameParametersBuffer[2]); + batch.draw(gpu::TRIANGLE_STRIP, 4); + + splitViewport.x = 0; + batch.setViewportTransform(splitViewport); + batch.setUniformBuffer(render_utils::slot::buffer::SsaoFrameParams, _aoFrameParametersBuffer[3]); + batch.draw(gpu::TRIANGLE_STRIP, 4); + } +#else batch.setUniformBuffer(render_utils::slot::buffer::SsaoFrameParams, _aoFrameParametersBuffer[0]); batch.draw(gpu::TRIANGLE_STRIP, 4); - - splitViewport.x += splitViewport.z; - batch.setViewportTransform(splitViewport); - batch.setUniformBuffer(render_utils::slot::buffer::SsaoFrameParams, _aoFrameParametersBuffer[1]); - batch.draw(gpu::TRIANGLE_STRIP, 4); - - splitViewport.y += splitViewport.w; - batch.setViewportTransform(splitViewport); - batch.setUniformBuffer(render_utils::slot::buffer::SsaoFrameParams, _aoFrameParametersBuffer[2]); - batch.draw(gpu::TRIANGLE_STRIP, 4); - - splitViewport.x = 0; - batch.setViewportTransform(splitViewport); - batch.setUniformBuffer(render_utils::slot::buffer::SsaoFrameParams, _aoFrameParametersBuffer[3]); - batch.draw(gpu::TRIANGLE_STRIP, 4); - } -#else - batch.setUniformBuffer(render_utils::slot::buffer::SsaoFrameParams, _aoFrameParametersBuffer[0]); - batch.draw(gpu::TRIANGLE_STRIP, 4); #endif batch.popProfileRange(); @@ -544,11 +594,11 @@ void AmbientOcclusionEffect::run(const render::RenderContextPointer& renderConte #if SSAO_USE_QUAD_SPLIT // Gather back the four separate renders into one interleaved one batch.pushProfileRange("Gather"); - batch.setViewportTransform(occlusionViewport); - batch.setFramebuffer(occlusionFBO); - batch.setPipeline(gatherPipeline); - batch.setResourceTexture(render_utils::slot::texture::SsaoOcclusion, occlusionBlurredFBO->getRenderBuffer(0)); - batch.draw(gpu::TRIANGLE_STRIP, 4); + batch.setViewportTransform(occlusionViewport); + batch.setFramebuffer(occlusionFBO); + batch.setPipeline(gatherPipeline); + batch.setResourceTexture(render_utils::slot::texture::SsaoOcclusion, occlusionBlurredFBO->getRenderBuffer(0)); + batch.draw(gpu::TRIANGLE_STRIP, 4); batch.popProfileRange(); #endif @@ -561,6 +611,7 @@ void AmbientOcclusionEffect::run(const render::RenderContextPointer& renderConte batch.setFramebuffer(occlusionBlurredFBO); // Use full resolution depth and normal for bilateral upscaling and blur batch.setResourceTexture(render_utils::slot::texture::SsaoDepth, linearDepthTexture); + batch.setResourceTexture(render_utils::slot::texture::SsaoNormal, fullNormalTexture); batch.setUniformBuffer(render_utils::slot::buffer::SsaoBlurParams, _hblurParametersBuffer); batch.setPipeline(firstHBlurPipeline); batch.setResourceTexture(render_utils::slot::texture::SsaoOcclusion, occlusionFBO->getRenderBuffer(0)); diff --git a/libraries/render-utils/src/AmbientOcclusionEffect.h b/libraries/render-utils/src/AmbientOcclusionEffect.h index ee64dd3912..e04e24b4f4 100644 --- a/libraries/render-utils/src/AmbientOcclusionEffect.h +++ b/libraries/render-utils/src/AmbientOcclusionEffect.h @@ -30,7 +30,10 @@ public: gpu::FramebufferPointer getOcclusionBlurredFramebuffer(); gpu::TexturePointer getOcclusionBlurredTexture(); - + + gpu::FramebufferPointer getNormalFramebuffer(); + gpu::TexturePointer getNormalTexture(); + // Update the source framebuffer size which will drive the allocation of all the other resources. bool updateLinearDepth(const gpu::TexturePointer& linearDepthBuffer); gpu::TexturePointer getLinearDepthTexture(); @@ -44,10 +47,13 @@ protected: gpu::FramebufferPointer _occlusionFramebuffer; gpu::TexturePointer _occlusionTexture; - + gpu::FramebufferPointer _occlusionBlurredFramebuffer; gpu::TexturePointer _occlusionBlurredTexture; - + + gpu::FramebufferPointer _normalFramebuffer; + gpu::TexturePointer _normalTexture; + glm::ivec2 _frameSize; }; @@ -168,12 +174,14 @@ private: static const gpu::PipelinePointer& getVBlurPipeline(); // second static const gpu::PipelinePointer& getMipCreationPipeline(); static const gpu::PipelinePointer& getGatherPipeline(); + static const gpu::PipelinePointer& getBuildNormalsPipeline(); static gpu::PipelinePointer _occlusionPipeline; static gpu::PipelinePointer _hBlurPipeline; static gpu::PipelinePointer _vBlurPipeline; static gpu::PipelinePointer _mipCreationPipeline; static gpu::PipelinePointer _gatherPipeline; + static gpu::PipelinePointer _buildNormalsPipeline; AmbientOcclusionFramebufferPointer _framebuffer; std::array _randomSamples; diff --git a/libraries/render-utils/src/SurfaceGeometryPass.cpp b/libraries/render-utils/src/SurfaceGeometryPass.cpp index bf90aa3b22..374a509c5a 100644 --- a/libraries/render-utils/src/SurfaceGeometryPass.cpp +++ b/libraries/render-utils/src/SurfaceGeometryPass.cpp @@ -28,11 +28,12 @@ namespace ru { LinearDepthFramebuffer::LinearDepthFramebuffer() { } -void LinearDepthFramebuffer::update(const gpu::TexturePointer& depthBuffer) { +void LinearDepthFramebuffer::update(const gpu::TexturePointer& depthBuffer, const gpu::TexturePointer& normalTexture) { //If the depth buffer or size changed, we need to delete our FBOs bool reset = false; - if (_primaryDepthTexture != depthBuffer) { + if (_primaryDepthTexture != depthBuffer || _normalTexture != normalTexture) { _primaryDepthTexture = depthBuffer; + _normalTexture = normalTexture; reset = true; } if (_primaryDepthTexture) { @@ -100,6 +101,10 @@ gpu::TexturePointer LinearDepthFramebuffer::getLinearDepthTexture() { return _linearDepthTexture; } +gpu::TexturePointer LinearDepthFramebuffer::getNormalTexture() { + return _normalTexture; +} + gpu::FramebufferPointer LinearDepthFramebuffer::getDownsampleFramebuffer() { if (!_downsampleFramebuffer) { allocate(); @@ -148,7 +153,7 @@ void LinearDepthPass::run(const render::RenderContextPointer& renderContext, con auto depthBuffer = deferredFramebuffer->getPrimaryDepthTexture(); auto normalTexture = deferredFramebuffer->getDeferredNormalTexture(); - _linearDepthFramebuffer->update(depthBuffer); + _linearDepthFramebuffer->update(depthBuffer, normalTexture); auto linearDepthFBO = _linearDepthFramebuffer->getLinearDepthFramebuffer(); auto linearDepthTexture = _linearDepthFramebuffer->getLinearDepthTexture(); diff --git a/libraries/render-utils/src/SurfaceGeometryPass.h b/libraries/render-utils/src/SurfaceGeometryPass.h index 1d8f77e67d..5ada56521a 100644 --- a/libraries/render-utils/src/SurfaceGeometryPass.h +++ b/libraries/render-utils/src/SurfaceGeometryPass.h @@ -28,13 +28,14 @@ public: gpu::FramebufferPointer getLinearDepthFramebuffer(); gpu::TexturePointer getLinearDepthTexture(); + gpu::TexturePointer getNormalTexture(); gpu::FramebufferPointer getDownsampleFramebuffer(); gpu::TexturePointer getHalfLinearDepthTexture(); gpu::TexturePointer getHalfNormalTexture(); // Update the depth buffer which will drive the allocation of all the other resources according to its size. - void update(const gpu::TexturePointer& depthBuffer); + void update(const gpu::TexturePointer& depthBuffer, const gpu::TexturePointer& normalTexture); const glm::ivec2& getDepthFrameSize() const { return _frameSize; } void setResolutionLevel(int level) { _resolutionLevel = std::max(0, level); } @@ -48,6 +49,7 @@ protected: gpu::FramebufferPointer _linearDepthFramebuffer; gpu::TexturePointer _linearDepthTexture; + gpu::TexturePointer _normalTexture; gpu::FramebufferPointer _downsampleFramebuffer; gpu::TexturePointer _halfLinearDepthTexture; diff --git a/libraries/render-utils/src/render-utils/ShaderConstants.h b/libraries/render-utils/src/render-utils/ShaderConstants.h index ce4e481ce3..e12cf150fd 100644 --- a/libraries/render-utils/src/render-utils/ShaderConstants.h +++ b/libraries/render-utils/src/render-utils/ShaderConstants.h @@ -89,6 +89,7 @@ #define RENDER_UTILS_BUFFER_SSAO_BLUR_PARAMS 4 #define RENDER_UTILS_BUFFER_SSAO_FRAME_PARAMS 5 #define RENDER_UTILS_TEXTURE_SSAO_DEPTH 1 +#define RENDER_UTILS_TEXTURE_SSAO_NORMAL 2 #define RENDER_UTILS_TEXTURE_SSAO_OCCLUSION 0 // Temporal anti-aliasing @@ -196,6 +197,7 @@ enum Texture { TaaNext = RENDER_UTILS_TEXTURE_TAA_NEXT, SsaoOcclusion = RENDER_UTILS_TEXTURE_SSAO_OCCLUSION, SsaoDepth = RENDER_UTILS_TEXTURE_SSAO_DEPTH, + SsaoNormal = RENDER_UTILS_TEXTURE_SSAO_NORMAL, HighlightSceneDepth = RENDER_UTILS_TEXTURE_HIGHLIGHT_SCENE_DEPTH, HighlightDepth = RENDER_UTILS_TEXTURE_HIGHLIGHT_DEPTH, SurfaceGeometryDepth = RENDER_UTILS_TEXTURE_SG_DEPTH, diff --git a/libraries/render-utils/src/render-utils/ssao_buildNormals.slp b/libraries/render-utils/src/render-utils/ssao_buildNormals.slp new file mode 100644 index 0000000000..d4d8ec4b01 --- /dev/null +++ b/libraries/render-utils/src/render-utils/ssao_buildNormals.slp @@ -0,0 +1 @@ +VERTEX gpu::vertex::DrawViewportQuadTransformTexcoord diff --git a/libraries/render-utils/src/ssao.slh b/libraries/render-utils/src/ssao.slh index b1103fbbac..bda163fb8c 100644 --- a/libraries/render-utils/src/ssao.slh +++ b/libraries/render-utils/src/ssao.slh @@ -98,32 +98,13 @@ float getBlurEdgeSharpness() { return params._blurInfo.x; } -#ifdef CONSTANT_GAUSSIAN -const int BLUR_RADIUS = 4; -const float gaussian[BLUR_RADIUS + 1] = -// KEEP this dead code for eventual performance improvment -// float[](0.356642, 0.239400, 0.072410, 0.009869); -// float[](0.398943, 0.241971, 0.053991, 0.004432, 0.000134); // stddev = 1.0 -float[](0.153170, 0.144893, 0.122649, 0.092902, 0.062970); // stddev = 2.0 -//float[](0.197413, 0.17467, 0.12098,0.065591,0.040059); -// float[](0.111220, 0.107798, 0.098151, 0.083953, 0.067458, 0.050920, 0.036108); // stddev = 3.0 - -int getBlurRadius() { - return BLUR_RADIUS; -} - -float getBlurCoef(int c) { - return gaussian[c]; -} -#else int getBlurRadius() { return int(params._blurInfo.y); } float getBlurCoef(int c) { - return params._gaussianCoefs[c]; + return params._blurFilterTaps[c]; } -#endif <@endfunc@> @@ -219,6 +200,7 @@ vec3 getTapLocationClampedSSAO(int sampleNumber, float spinAngle, float outerRad // the depth pyramid texture layout(binding=RENDER_UTILS_TEXTURE_SSAO_DEPTH) uniform sampler2D depthPyramidTex; +layout(binding=RENDER_UTILS_TEXTURE_SSAO_NORMAL) uniform sampler2D normalTex; float getZEyeAtPixel(ivec2 pixel, int level) { return -texelFetch(depthPyramidTex, pixel, level).x; @@ -228,8 +210,12 @@ float getZEyeAtUV(vec2 texCoord, int level) { return -texture(depthPyramidTex, texCoord, level).x; } +vec3 getNormalEyeAtUV(vec2 texCoord, int level) { + return normalize(texture(normalTex, texCoord, level).xyz*2.0 - vec3(1.0)); +} + int evalMipFromRadius(float radius) { - const int LOG_MAX_OFFSET = 1; + const int LOG_MAX_OFFSET = 2; const int MAX_MIP_LEVEL = 5; return clamp(findMSB(int(radius)) - LOG_MAX_OFFSET, 0, MAX_MIP_LEVEL); } @@ -406,24 +392,22 @@ float fetchOcclusion(vec2 coords) { return raw.x; } -const float BLUR_WEIGHT_OFFSET = 0.01; const float BLUR_EDGE_DISTANCE_SCALE = 300.0; vec2 evalTapWeightedValue(ivec3 side, int r, ivec2 destPixelCoord, vec2 scaledTexCoord, vec2 fullTexCoord, float fragDepth) { ivec2 tapOffset = <$axis$> * r; - ivec2 tapPixelCoord = destPixelCoord + tapOffset; + ivec2 tapPixelCoord = destPixelCoord + ivec2(tapOffset); if ((tapPixelCoord.x < side.y || tapPixelCoord.x >= side.z + side.y) || (tapPixelCoord.y < 0 || tapPixelCoord.y >= getBlurImageHeight())) { return vec2(0.0); } + vec2 tapTexCoord = scaledTexCoord + tapOffset * getOcclusionBlurScale(); float tapOcclusion = fetchOcclusion(tapTexCoord); tapTexCoord = fullTexCoord + tapOffset * getDepthBlurScale(); float tapDepth = getZEyeAtUV(tapTexCoord, 0); - - // spatial domain: offset gaussian tap - float weight = BLUR_WEIGHT_OFFSET + getBlurCoef(abs(r)); + float weight = getBlurCoef(abs(r)); // range domain (the "bilateral" weight). As depth difference increases, decrease weight. float zDistance = tapDepth - fragDepth; @@ -441,6 +425,7 @@ vec3 getBlurredOcclusion(ivec2 destPixelCoord, vec2 scaledTexCoord, vec2 fullTex // Accumulate weighted contributions along the bluring axis in the [-radius, radius] range int blurRadius = getBlurRadius(); + // negative side first for (int r = -blurRadius; r <= -1; ++r) { weightedSums += evalTapWeightedValue(side.xyz, r, destPixelCoord, scaledTexCoord, fullTexCoord, fragDepth); diff --git a/libraries/render-utils/src/ssao_buildNormals.slf b/libraries/render-utils/src/ssao_buildNormals.slf new file mode 100644 index 0000000000..dbea5028c5 --- /dev/null +++ b/libraries/render-utils/src/ssao_buildNormals.slf @@ -0,0 +1,45 @@ +<@include gpu/Config.slh@> +<$VERSION_HEADER$> +// Generated on <$_SCRIBE_DATE$> +// +// ssao_buildNormals.frag +// +// Created by Olivier Prat on 09/19/18. +// Copyright 2018 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 ssao.slh@> +<$declareAmbientOcclusion()$> +<$declareFetchDepthPyramidMap()$> + +layout(location=0) out vec4 outFragColor; + +void main(void) { + vec2 sideImageSize = getSideImageSize(0); + + // Pixel being shaded + vec2 fragCoord = gl_FragCoord.xy; + ivec2 fragPixelPos = ivec2(fragCoord.xy); + + // Stereo side info + ivec4 side = getStereoSideInfo(fragPixelPos.x, 0); + + // From now on, fragPixelPos is the pixel pos in the side + fragPixelPos.x -= side.y; + vec2 fragUVPos = (vec2(fragPixelPos) + vec2(0.5)) / sideImageSize; + + // Fetch the z under the pixel (stereo or not) + float Zeye = getZEyeAtUV(fragUVPos, 0); + + // The position and normal of the pixel fragment in Eye space + vec3 fragPositionES = evalEyePositionFromZeye(side.x, Zeye, fragUVPos); + vec3 fragNormalES = evalEyeNormal(fragPositionES); + vec3 absFragNormalES = abs(fragNormalES); + + // Maximize encoding precision + fragNormalES /= max(max(absFragNormalES.x, absFragNormalES.y), absFragNormalES.z); + outFragColor = vec4(vec3(fragNormalES)*0.5 + vec3(0.5), 1.0); +} diff --git a/libraries/render-utils/src/ssao_makeOcclusion.slf b/libraries/render-utils/src/ssao_makeOcclusion.slf index bdaa87fff9..10310e8984 100644 --- a/libraries/render-utils/src/ssao_makeOcclusion.slf +++ b/libraries/render-utils/src/ssao_makeOcclusion.slf @@ -46,7 +46,11 @@ void main(void) { // The position and normal of the pixel fragment in Eye space vec3 fragPositionES = evalEyePositionFromZeye(side.x, Zeye, fragUVPos); +#if SSAO_USE_QUAD_SPLIT + vec3 fragNormalES = getNormalEyeAtUV(fragUVPos, 0); +#else vec3 fragNormalES = evalEyeNormal(fragPositionES); +#endif // Choose the screen-space sample radius float diskPixelRadius = evalDiskRadius(fragPositionES.z, sideImageSize); diff --git a/libraries/render-utils/src/ssao_shared.h b/libraries/render-utils/src/ssao_shared.h index 61bf14ccf8..89738fcea0 100644 --- a/libraries/render-utils/src/ssao_shared.h +++ b/libraries/render-utils/src/ssao_shared.h @@ -39,7 +39,7 @@ struct AmbientOcclusionParams { SSAO_VEC4 _ditheringInfo; SSAO_VEC4 _sampleInfo; SSAO_VEC4 _blurInfo; - float _gaussianCoefs[SSAO_BLUR_GAUSSIAN_COEFS_COUNT]; + float _blurFilterTaps[SSAO_BLUR_GAUSSIAN_COEFS_COUNT]; }; struct AmbientOcclusionFrameParams { diff --git a/scripts/developer/utilities/render/ambientOcclusionPass.qml b/scripts/developer/utilities/render/ambientOcclusionPass.qml index f92b469626..141832e202 100644 --- a/scripts/developer/utilities/render/ambientOcclusionPass.qml +++ b/scripts/developer/utilities/render/ambientOcclusionPass.qml @@ -35,11 +35,12 @@ Rectangle { model: [ "Radius:radius:2.0:false", "Level:obscuranceLevel:1.0:false", - "Num Taps:numSamples:32:true", + "Num Taps:numSamples:16:true", "Taps Spiral:numSpiralTurns:10.0:false", "Falloff Angle:falloffAngle:0.5:false", "Blur Edge Sharpness:edgeSharpness:1.0:false", - "Blur Radius:blurRadius:15.0:false", + "Blur Radius:blurRadius:15.0:true", + "Resolution Downscale:resolutionLevel:2:true", ] ConfigSlider { label: qsTr(modelData.split(":")[0]) @@ -57,7 +58,6 @@ Rectangle { Column { Repeater { model: [ - "resolutionLevel:resolutionLevel", "ditheringEnabled:ditheringEnabled", "fetchMipsEnabled:fetchMipsEnabled", "borderingEnabled:borderingEnabled" From 6921e71a9aec8c2dc1a54dd0b5d5424e0826332f Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Wed, 19 Sep 2018 17:49:01 +0200 Subject: [PATCH 031/286] Added normal distance to bilateral filtering --- .../render-utils/src/AmbientOcclusionEffect.cpp | 2 +- libraries/render-utils/src/ssao.slh | 13 +++++++++---- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/libraries/render-utils/src/AmbientOcclusionEffect.cpp b/libraries/render-utils/src/AmbientOcclusionEffect.cpp index 667127983a..1e3f2d9db3 100644 --- a/libraries/render-utils/src/AmbientOcclusionEffect.cpp +++ b/libraries/render-utils/src/AmbientOcclusionEffect.cpp @@ -444,7 +444,7 @@ void AmbientOcclusionEffect::updateGaussianDistribution() { auto filterTaps = _aoParametersBuffer.edit()._blurFilterTaps; auto blurRadius = _aoParametersBuffer.get().getBlurRadius(); - GaussianDistribution::evalSampling(filterTaps, SSAO_BLUR_GAUSSIAN_COEFS_COUNT, blurRadius, _aoParametersBuffer->getBlurDeviation() * 4.0f / blurRadius); + GaussianDistribution::evalSampling(filterTaps, SSAO_BLUR_GAUSSIAN_COEFS_COUNT, blurRadius, _aoParametersBuffer->getBlurDeviation()); } void AmbientOcclusionEffect::run(const render::RenderContextPointer& renderContext, const Inputs& inputs, Outputs& outputs) { diff --git a/libraries/render-utils/src/ssao.slh b/libraries/render-utils/src/ssao.slh index bda163fb8c..3d092d5e4a 100644 --- a/libraries/render-utils/src/ssao.slh +++ b/libraries/render-utils/src/ssao.slh @@ -393,8 +393,9 @@ float fetchOcclusion(vec2 coords) { } const float BLUR_EDGE_DISTANCE_SCALE = 300.0; +const float BLUR_EDGE_NORMAL_SCALE = 0.5; -vec2 evalTapWeightedValue(ivec3 side, int r, ivec2 destPixelCoord, vec2 scaledTexCoord, vec2 fullTexCoord, float fragDepth) { +vec2 evalTapWeightedValue(ivec3 side, int r, ivec2 destPixelCoord, vec2 scaledTexCoord, vec2 fullTexCoord, float fragDepth, vec3 fragNormal) { ivec2 tapOffset = <$axis$> * r; ivec2 tapPixelCoord = destPixelCoord + ivec2(tapOffset); @@ -407,11 +408,14 @@ vec2 evalTapWeightedValue(ivec3 side, int r, ivec2 destPixelCoord, vec2 scaledTe tapTexCoord = fullTexCoord + tapOffset * getDepthBlurScale(); float tapDepth = getZEyeAtUV(tapTexCoord, 0); + vec3 tapNormal = getNormalEyeAtUV(tapTexCoord, 0); float weight = getBlurCoef(abs(r)); // range domain (the "bilateral" weight). As depth difference increases, decrease weight. float zDistance = tapDepth - fragDepth; - weight *= exp(-getBlurEdgeSharpness() * (zDistance * zDistance * BLUR_EDGE_DISTANCE_SCALE)); + tapNormal -= fragNormal; + float normalDistance = dot(tapNormal,tapNormal); + weight *= exp(-getBlurEdgeSharpness() * (zDistance * zDistance * BLUR_EDGE_DISTANCE_SCALE + normalDistance * BLUR_EDGE_NORMAL_SCALE)); return vec2(tapOcclusion * weight, weight); } @@ -421,6 +425,7 @@ vec3 getBlurredOcclusion(ivec2 destPixelCoord, vec2 scaledTexCoord, vec2 fullTex ivec4 side = getStereoSideInfo(destPixelCoord.x, 0); float fragDepth = getZEyeAtUV(fullTexCoord, 0); + vec3 fragNormal = getNormalEyeAtUV(fullTexCoord, 0); vec2 weightedSums = vec2(0.0); // Accumulate weighted contributions along the bluring axis in the [-radius, radius] range @@ -428,7 +433,7 @@ vec3 getBlurredOcclusion(ivec2 destPixelCoord, vec2 scaledTexCoord, vec2 fullTex // negative side first for (int r = -blurRadius; r <= -1; ++r) { - weightedSums += evalTapWeightedValue(side.xyz, r, destPixelCoord, scaledTexCoord, fullTexCoord, fragDepth); + weightedSums += evalTapWeightedValue(side.xyz, r, destPixelCoord, scaledTexCoord, fullTexCoord, fragDepth, fragNormal); } // Central pixel contribution @@ -438,7 +443,7 @@ vec3 getBlurredOcclusion(ivec2 destPixelCoord, vec2 scaledTexCoord, vec2 fullTex // then positive side for (int r = 1; r <= blurRadius; ++r) { - weightedSums += evalTapWeightedValue(side.xyz, r, destPixelCoord, scaledTexCoord, fullTexCoord, fragDepth); + weightedSums += evalTapWeightedValue(side.xyz, r, destPixelCoord, scaledTexCoord, fullTexCoord, fragDepth, fragNormal); } // Final normalization From b1db7ab40356044d1a9035cc5f9c273fbe098870 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Thu, 20 Sep 2018 10:18:06 +0200 Subject: [PATCH 032/286] Fixed for stereo rendering with resolution level 0 --- .../src/AmbientOcclusionEffect.cpp | 46 ++++++++++--------- .../render-utils/src/DeferredTransform.slh | 12 ++++- libraries/render-utils/src/ssao.slh | 16 ++++--- .../render-utils/src/ssao_buildNormals.slf | 8 ++-- .../render-utils/src/ssao_debugOcclusion.slf | 2 +- libraries/render-utils/src/ssao_gather.slf | 4 +- .../render-utils/src/ssao_makeOcclusion.slf | 19 ++++---- libraries/render/src/render/BlurTask.slh | 2 +- .../src/render/blurGaussianDepthAwareH.slf | 2 +- .../src/render/blurGaussianDepthAwareV.slf | 2 +- libraries/render/src/render/blurGaussianH.slf | 2 +- libraries/render/src/render/blurGaussianV.slf | 2 +- 12 files changed, 67 insertions(+), 50 deletions(-) diff --git a/libraries/render-utils/src/AmbientOcclusionEffect.cpp b/libraries/render-utils/src/AmbientOcclusionEffect.cpp index 1e3f2d9db3..2314568720 100644 --- a/libraries/render-utils/src/AmbientOcclusionEffect.cpp +++ b/libraries/render-utils/src/AmbientOcclusionEffect.cpp @@ -212,7 +212,7 @@ AmbientOcclusionEffectConfig::AmbientOcclusionEffectConfig() : #else numSamples{ 16 }, #endif - resolutionLevel{ 2 }, + resolutionLevel{ 0 }, blurRadius{ 4 }, ditheringEnabled{ true }, borderingEnabled{ true }, @@ -509,7 +509,7 @@ void AmbientOcclusionEffect::run(const render::RenderContextPointer& renderConte auto& sample = _aoFrameParametersBuffer[splitId].edit(); sample._angleInfo.x = _randomSamples[splitId + SSAO_RANDOM_SAMPLE_COUNT * _frameId]; } - // TEMPO OP _frameId = (_frameId + 1) % SSAO_RANDOM_SAMPLE_COUNT; + // _frameId = (_frameId + 1) % SSAO_RANDOM_SAMPLE_COUNT; gpu::doInBatch("AmbientOcclusionEffect::run", args->_context, [=](gpu::Batch& batch) { PROFILE_RANGE_BATCH(batch, "AmbientOcclusion"); @@ -605,27 +605,31 @@ void AmbientOcclusionEffect::run(const render::RenderContextPointer& renderConte { PROFILE_RANGE_BATCH(batch, "Bilateral Blur"); // Blur 1st pass - model.setScale(resolutionScale); - batch.setModelTransform(model); - batch.setViewportTransform(firstBlurViewport); - batch.setFramebuffer(occlusionBlurredFBO); - // Use full resolution depth and normal for bilateral upscaling and blur - batch.setResourceTexture(render_utils::slot::texture::SsaoDepth, linearDepthTexture); - batch.setResourceTexture(render_utils::slot::texture::SsaoNormal, fullNormalTexture); - batch.setUniformBuffer(render_utils::slot::buffer::SsaoBlurParams, _hblurParametersBuffer); - batch.setPipeline(firstHBlurPipeline); - batch.setResourceTexture(render_utils::slot::texture::SsaoOcclusion, occlusionFBO->getRenderBuffer(0)); - batch.draw(gpu::TRIANGLE_STRIP, 4); + batch.pushProfileRange("Horizontal"); + model.setScale(resolutionScale); + batch.setModelTransform(model); + batch.setViewportTransform(firstBlurViewport); + batch.setFramebuffer(occlusionBlurredFBO); + // Use full resolution depth and normal for bilateral upscaling and blur + batch.setResourceTexture(render_utils::slot::texture::SsaoDepth, linearDepthTexture); + batch.setResourceTexture(render_utils::slot::texture::SsaoNormal, fullNormalTexture); + batch.setUniformBuffer(render_utils::slot::buffer::SsaoBlurParams, _hblurParametersBuffer); + batch.setPipeline(firstHBlurPipeline); + batch.setResourceTexture(render_utils::slot::texture::SsaoOcclusion, occlusionFBO->getRenderBuffer(0)); + batch.draw(gpu::TRIANGLE_STRIP, 4); + batch.popProfileRange(); // Blur 2nd pass - model.setScale(glm::vec3(1.0f, resolutionScale, 1.0f)); - batch.setModelTransform(model); - batch.setViewportTransform(sourceViewport); - batch.setFramebuffer(occlusionFBO); - batch.setUniformBuffer(render_utils::slot::buffer::SsaoBlurParams, _vblurParametersBuffer); - batch.setPipeline(lastVBlurPipeline); - batch.setResourceTexture(render_utils::slot::texture::SsaoOcclusion, occlusionBlurredFBO->getRenderBuffer(0)); - batch.draw(gpu::TRIANGLE_STRIP, 4); + batch.pushProfileRange("Vertical"); + model.setScale(glm::vec3(1.0f, resolutionScale, 1.0f)); + batch.setModelTransform(model); + batch.setViewportTransform(sourceViewport); + batch.setFramebuffer(occlusionFBO); + batch.setUniformBuffer(render_utils::slot::buffer::SsaoBlurParams, _vblurParametersBuffer); + batch.setPipeline(lastVBlurPipeline); + batch.setResourceTexture(render_utils::slot::texture::SsaoOcclusion, occlusionBlurredFBO->getRenderBuffer(0)); + batch.draw(gpu::TRIANGLE_STRIP, 4); + batch.popProfileRange(); } batch.setResourceTexture(render_utils::slot::texture::SsaoDepth, nullptr); diff --git a/libraries/render-utils/src/DeferredTransform.slh b/libraries/render-utils/src/DeferredTransform.slh index 19986805f6..d9ced66edf 100644 --- a/libraries/render-utils/src/DeferredTransform.slh +++ b/libraries/render-utils/src/DeferredTransform.slh @@ -120,8 +120,8 @@ float getStereoSideHeight(int resolutionLevel) { return float(int(frameTransform._pixelInfo.w) >> resolutionLevel); } -vec2 getSideImageSize(int resolutionLevel) { - return vec2(float(int(frameTransform._stereoInfo.y) >> resolutionLevel), float(int(frameTransform._pixelInfo.w) >> resolutionLevel)); +vec2 getStereoSideSize(int resolutionLevel) { + return vec2(getStereoSideWidth(resolutionLevel), getStereoSideHeight(resolutionLevel)); } ivec4 getStereoSideInfo(int xPos, int resolutionLevel) { @@ -129,6 +129,14 @@ ivec4 getStereoSideInfo(int xPos, int resolutionLevel) { return ivec4(xPos < sideWidth ? ivec2(0, 0) : ivec2(1, sideWidth), sideWidth, isStereo()); } +int getStereoSide(ivec4 sideInfo) { + return sideInfo.x; +} + +bool isStereoFromInfo(ivec4 sideInfo) { + return sideInfo.w != 0; +} + float evalZeyeFromZdb(float depth) { return frameTransform._depthInfo.x / (depth * frameTransform._depthInfo.y + frameTransform._depthInfo.z); } diff --git a/libraries/render-utils/src/ssao.slh b/libraries/render-utils/src/ssao.slh index 3d092d5e4a..c4efb3be89 100644 --- a/libraries/render-utils/src/ssao.slh +++ b/libraries/render-utils/src/ssao.slh @@ -214,19 +214,23 @@ vec3 getNormalEyeAtUV(vec2 texCoord, int level) { return normalize(texture(normalTex, texCoord, level).xyz*2.0 - vec3(1.0)); } +vec3 getNormalEyeAtPixel(ivec2 pixel, int level) { + return normalize(texelFetch(normalTex, pixel, level).xyz*2.0 - vec3(1.0)); +} + int evalMipFromRadius(float radius) { const int LOG_MAX_OFFSET = 2; const int MAX_MIP_LEVEL = 5; return clamp(findMSB(int(radius)) - LOG_MAX_OFFSET, 0, MAX_MIP_LEVEL); } -vec4 fetchTap(ivec4 side, vec2 tapPixelPos, float tapRadius, vec2 sideImageSize) { +vec4 fetchTap(ivec4 side, vec2 tapSidePixelPos, float tapRadius, vec2 sideImageSize) { int mipLevel = evalMipFromRadius(tapRadius * float(doFetchMips())); // We need to divide by 2^mipLevel to read the appropriately scaled coordinate from a MIP-map. // Manually clamp to the texture size because texelFetch bypasses the texture unit - vec2 tapUV = (tapPixelPos + vec2(0.5)) / sideImageSize; - vec2 fetchUV = vec2(tapUV.x + side.w * 0.5 * (side.x - tapUV.x), tapUV.y); + vec2 tapUV = (tapSidePixelPos + vec2(0.5)) / sideImageSize; + vec2 fetchUV = mix(tapUV, vec2((tapUV.x + getStereoSide(side)) * 0.5, tapUV.y), isStereoFromInfo(side)); vec4 P; P.xy = tapUV; @@ -276,11 +280,11 @@ float computeHorizonFromTap(vec3 tapPositionES, vec3 fragPositionES, vec3 fragNo } <@func computeHorizon()@> - vec2 tapPixelPos = tapPixelOffset + shadedPixelPos; - if (tapPixelPos.x<0 && tapPixelPos.y<0 && tapPixelPos.x>=sideImageSize.x && tapPixelPos.y>=sideImageSize.y) { + vec2 tapSidePixelPos = tapPixelOffset + shadedPixelPos; + if (tapSidePixelPos.x<0 && tapSidePixelPos.y<0 && tapSidePixelPos.x>=sideImageSize.x && tapSidePixelPos.y>=sideImageSize.y) { break; } - vec4 tapUVZ_mip = fetchTap(side, tapPixelPos, radius, sideImageSize); + vec4 tapUVZ_mip = fetchTap(side, tapSidePixelPos, radius, sideImageSize); vec3 tapPositionES = evalEyePositionFromZeye(side.x, tapUVZ_mip.z, tapUVZ_mip.xy); float tapCosHorizonAngle = computeHorizonFromTap(tapPositionES, fragPositionES, fragNormalES); diff --git a/libraries/render-utils/src/ssao_buildNormals.slf b/libraries/render-utils/src/ssao_buildNormals.slf index dbea5028c5..4d7d6e6530 100644 --- a/libraries/render-utils/src/ssao_buildNormals.slf +++ b/libraries/render-utils/src/ssao_buildNormals.slf @@ -18,12 +18,15 @@ layout(location=0) out vec4 outFragColor; void main(void) { - vec2 sideImageSize = getSideImageSize(0); + vec2 sideImageSize = getStereoSideSize(0); // Pixel being shaded vec2 fragCoord = gl_FragCoord.xy; ivec2 fragPixelPos = ivec2(fragCoord.xy); + // Fetch the z under the pixel (stereo or not) + float Zeye = getZEyeAtPixel(fragPixelPos, 0); + // Stereo side info ivec4 side = getStereoSideInfo(fragPixelPos.x, 0); @@ -31,9 +34,6 @@ void main(void) { fragPixelPos.x -= side.y; vec2 fragUVPos = (vec2(fragPixelPos) + vec2(0.5)) / sideImageSize; - // Fetch the z under the pixel (stereo or not) - float Zeye = getZEyeAtUV(fragUVPos, 0); - // The position and normal of the pixel fragment in Eye space vec3 fragPositionES = evalEyePositionFromZeye(side.x, Zeye, fragUVPos); vec3 fragNormalES = evalEyeNormal(fragPositionES); diff --git a/libraries/render-utils/src/ssao_debugOcclusion.slf b/libraries/render-utils/src/ssao_debugOcclusion.slf index 10069582aa..2c1824dce7 100644 --- a/libraries/render-utils/src/ssao_debugOcclusion.slf +++ b/libraries/render-utils/src/ssao_debugOcclusion.slf @@ -37,7 +37,7 @@ vec2 getDebugCursorTexcoord(){ layout(location=0) out vec4 outFragColor; void main(void) { - vec2 imageSize = getSideImageSize(getResolutionLevel()); + vec2 imageSize = getStereoSideSize(getResolutionLevel()); // In debug adjust the correct frag pixel based on base resolution vec2 fragCoord = gl_FragCoord.xy; diff --git a/libraries/render-utils/src/ssao_gather.slf b/libraries/render-utils/src/ssao_gather.slf index 32edec515b..8f2282154a 100644 --- a/libraries/render-utils/src/ssao_gather.slf +++ b/libraries/render-utils/src/ssao_gather.slf @@ -25,8 +25,8 @@ void main(void) { // Gather the four splits of the occlusion result back into an interleaved full size // result (at the resolution level, of course) ivec2 destPixelCoord = ivec2(gl_FragCoord.xy); - ivec2 sourcePixelCoord = destPixelCoord >> 1; - ivec2 splitImageSize = ivec2(getWidthHeight(getResolutionLevel() + 1)); + ivec2 sourcePixelCoord = destPixelCoord / 2; + ivec2 splitImageSize = ivec2(getWidthHeight(getResolutionLevel()+1)); sourcePixelCoord += (destPixelCoord & ivec2(1)) * splitImageSize; diff --git a/libraries/render-utils/src/ssao_makeOcclusion.slf b/libraries/render-utils/src/ssao_makeOcclusion.slf index 10310e8984..fee4d6f84f 100644 --- a/libraries/render-utils/src/ssao_makeOcclusion.slf +++ b/libraries/render-utils/src/ssao_makeOcclusion.slf @@ -24,14 +24,20 @@ layout(location=0) out vec4 outFragColor; void main(void) { - vec2 sideImageSize = getSideImageSize(getResolutionLevel()); + vec2 sideImageSize = getStereoSideSize(getResolutionLevel()); // Pixel being shaded vec2 fragCoord = gl_FragCoord.xy; ivec2 fragPixelPos = ivec2(fragCoord.xy); #if SSAO_USE_QUAD_SPLIT - ivec2 splitImageSize = ivec2(getWidthHeight(getResolutionLevel() + SSAO_USE_QUAD_SPLIT)); - fragPixelPos = ((fragPixelPos - getPixelOffset()*splitImageSize) << SSAO_USE_QUAD_SPLIT) + getPixelOffset(); + ivec2 splitImageSize = ivec2(getWidthHeight(getResolutionLevel()+1)); + fragPixelPos = ((fragPixelPos - getPixelOffset()*splitImageSize) * 2) + getPixelOffset(); +#endif + + // Fetch the z under the pixel (stereo or not) + float Zeye = getZEyeAtPixel(fragPixelPos, 0); +#if SSAO_USE_QUAD_SPLIT + vec3 fragNormalES = getNormalEyeAtPixel(fragPixelPos, 0); #endif // Stereo side info @@ -41,14 +47,9 @@ void main(void) { fragPixelPos.x -= side.y; vec2 fragUVPos = (vec2(fragPixelPos) + vec2(0.5)) / sideImageSize; - // Fetch the z under the pixel (stereo or not) - float Zeye = getZEyeAtUV(fragUVPos, 0); - // The position and normal of the pixel fragment in Eye space vec3 fragPositionES = evalEyePositionFromZeye(side.x, Zeye, fragUVPos); -#if SSAO_USE_QUAD_SPLIT - vec3 fragNormalES = getNormalEyeAtUV(fragUVPos, 0); -#else +#if !SSAO_USE_QUAD_SPLIT vec3 fragNormalES = evalEyeNormal(fragPositionES); #endif diff --git a/libraries/render/src/render/BlurTask.slh b/libraries/render/src/render/BlurTask.slh index c07e71688a..a2862f123f 100644 --- a/libraries/render/src/render/BlurTask.slh +++ b/libraries/render/src/render/BlurTask.slh @@ -25,7 +25,7 @@ layout(binding=0) uniform blurParamsBuffer { BlurParameters parameters; }; -vec2 getViewportInvWidthHeight() { +vec2 getInvWidthHeight() { return parameters.resolutionInfo.zw; } diff --git a/libraries/render/src/render/blurGaussianDepthAwareH.slf b/libraries/render/src/render/blurGaussianDepthAwareH.slf index 09a92909ff..04894e3d51 100644 --- a/libraries/render/src/render/blurGaussianDepthAwareH.slf +++ b/libraries/render/src/render/blurGaussianDepthAwareH.slf @@ -19,6 +19,6 @@ layout(location=0) in vec2 varTexCoord0; layout(location=0) out vec4 outFragColor; void main(void) { - outFragColor = pixelShaderGaussianDepthAware(varTexCoord0, vec2(1.0, 0.0), getViewportInvWidthHeight()); + outFragColor = pixelShaderGaussianDepthAware(varTexCoord0, vec2(1.0, 0.0), getInvWidthHeight()); } diff --git a/libraries/render/src/render/blurGaussianDepthAwareV.slf b/libraries/render/src/render/blurGaussianDepthAwareV.slf index 4f9bc86c01..129f9f7ce3 100644 --- a/libraries/render/src/render/blurGaussianDepthAwareV.slf +++ b/libraries/render/src/render/blurGaussianDepthAwareV.slf @@ -19,6 +19,6 @@ layout(location=0) in vec2 varTexCoord0; layout(location=0) out vec4 outFragColor; void main(void) { - outFragColor = pixelShaderGaussianDepthAware(varTexCoord0, vec2(0.0, 1.0), getViewportInvWidthHeight()); + outFragColor = pixelShaderGaussianDepthAware(varTexCoord0, vec2(0.0, 1.0), getInvWidthHeight()); } diff --git a/libraries/render/src/render/blurGaussianH.slf b/libraries/render/src/render/blurGaussianH.slf index 1c13664907..251ef158f2 100644 --- a/libraries/render/src/render/blurGaussianH.slf +++ b/libraries/render/src/render/blurGaussianH.slf @@ -20,6 +20,6 @@ layout(location=0) in vec2 varTexCoord0; layout(location=0) out vec4 outFragColor; void main(void) { - outFragColor = pixelShaderGaussian(varTexCoord0, vec2(1.0, 0.0), getViewportInvWidthHeight()); + outFragColor = pixelShaderGaussian(varTexCoord0, vec2(1.0, 0.0), getInvWidthHeight()); } diff --git a/libraries/render/src/render/blurGaussianV.slf b/libraries/render/src/render/blurGaussianV.slf index f8351f9670..c1215e8553 100644 --- a/libraries/render/src/render/blurGaussianV.slf +++ b/libraries/render/src/render/blurGaussianV.slf @@ -19,6 +19,6 @@ layout(location=0) in vec2 varTexCoord0; layout(location=0) out vec4 outFragColor; void main(void) { - outFragColor = pixelShaderGaussian(varTexCoord0, vec2(0.0, 1.0), getViewportInvWidthHeight()); + outFragColor = pixelShaderGaussian(varTexCoord0, vec2(0.0, 1.0), getInvWidthHeight()); } From d5d0f056a9d89f676fd15c5372f0da3cb4320fdb Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Thu, 20 Sep 2018 11:15:25 +0200 Subject: [PATCH 033/286] Fixed for any resolution level in stereo --- .../src/AmbientOcclusionEffect.cpp | 48 +++++++++++-------- .../render-utils/src/AmbientOcclusionEffect.h | 7 ++- libraries/render-utils/src/ssao.slh | 4 ++ .../render-utils/src/ssao_buildNormals.slf | 10 ++-- .../render-utils/src/ssao_makeOcclusion.slf | 4 +- 5 files changed, 45 insertions(+), 28 deletions(-) diff --git a/libraries/render-utils/src/AmbientOcclusionEffect.cpp b/libraries/render-utils/src/AmbientOcclusionEffect.cpp index 2314568720..5cb95c01b2 100644 --- a/libraries/render-utils/src/AmbientOcclusionEffect.cpp +++ b/libraries/render-utils/src/AmbientOcclusionEffect.cpp @@ -87,10 +87,6 @@ void AmbientOcclusionFramebuffer::allocate() { _occlusionBlurredTexture = gpu::Texture::createRenderBuffer(format, width, height, gpu::Texture::SINGLE_MIP, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR, gpu::Sampler::WRAP_CLAMP)); _occlusionBlurredFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("occlusionBlurred")); _occlusionBlurredFramebuffer->setRenderBuffer(0, _occlusionBlurredTexture); - - _normalTexture = gpu::Texture::createRenderBuffer(gpu::Element::COLOR_R11G11B10, width, height, gpu::Texture::SINGLE_MIP, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_POINT, gpu::Sampler::WRAP_CLAMP)); - _normalFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("ssaoNormals")); - _normalFramebuffer->setRenderBuffer(0, _normalTexture); } gpu::FramebufferPointer AmbientOcclusionFramebuffer::getOcclusionFramebuffer() { @@ -121,16 +117,26 @@ gpu::TexturePointer AmbientOcclusionFramebuffer::getOcclusionBlurredTexture() { return _occlusionBlurredTexture; } -gpu::FramebufferPointer AmbientOcclusionFramebuffer::getNormalFramebuffer() { - if (!_normalFramebuffer) { - allocate(); +void AmbientOcclusionFramebuffer::allocate(int resolutionLevel) { + auto width = _frameSize.x >> resolutionLevel; + auto height = _frameSize.y >> resolutionLevel; + + _normalTexture = gpu::Texture::createRenderBuffer(gpu::Element::COLOR_R11G11B10, width, height, gpu::Texture::SINGLE_MIP, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_POINT, gpu::Sampler::WRAP_CLAMP)); + _normalFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("ssaoNormals")); + _normalFramebuffer->setRenderBuffer(0, _normalTexture); + _resolutionLevel = resolutionLevel; +} + +gpu::FramebufferPointer AmbientOcclusionFramebuffer::getNormalFramebuffer(int resolutionLevel) { + if (!_normalFramebuffer || resolutionLevel != _resolutionLevel) { + allocate(resolutionLevel); } return _normalFramebuffer; } -gpu::TexturePointer AmbientOcclusionFramebuffer::getNormalTexture() { - if (!_normalTexture) { - allocate(); +gpu::TexturePointer AmbientOcclusionFramebuffer::getNormalTexture(int resolutionLevel) { + if (!_normalTexture || resolutionLevel != _resolutionLevel) { + allocate(resolutionLevel); } return _normalTexture; } @@ -208,11 +214,11 @@ AmbientOcclusionEffectConfig::AmbientOcclusionEffectConfig() : blurDeviation{ 2.5f }, numSpiralTurns{ 7.0f }, #if SSAO_USE_HORIZON_BASED - numSamples{ 2 }, + numSamples{ 3 }, #else numSamples{ 16 }, #endif - resolutionLevel{ 0 }, + resolutionLevel{ 2 }, blurRadius{ 4 }, ditheringEnabled{ true }, borderingEnabled{ true }, @@ -470,10 +476,11 @@ void AmbientOcclusionEffect::run(const render::RenderContextPointer& renderConte _framebuffer = std::make_shared(); } - const auto resolutionScale = powf(0.5f, _aoParametersBuffer->getResolutionLevel()); - if (_aoParametersBuffer->getResolutionLevel() > 0) { - occlusionViewport = occlusionViewport >> _aoParametersBuffer->getResolutionLevel(); - firstBlurViewport.w = firstBlurViewport.w >> _aoParametersBuffer->getResolutionLevel(); + const int resolutionLevel = _aoParametersBuffer->getResolutionLevel(); + const auto resolutionScale = powf(0.5f, resolutionLevel); + if (resolutionLevel > 0) { + occlusionViewport = occlusionViewport >> resolutionLevel; + firstBlurViewport.w = firstBlurViewport.w >> resolutionLevel; occlusionDepthTexture = linearDepthFramebuffer->getHalfLinearDepthTexture(); } @@ -498,8 +505,8 @@ void AmbientOcclusionEffect::run(const render::RenderContextPointer& renderConte #if SSAO_USE_QUAD_SPLIT auto gatherPipeline = getGatherPipeline(); auto buildNormalsPipeline = getBuildNormalsPipeline(); - auto occlusionNormalFramebuffer = _framebuffer->getNormalFramebuffer(); - auto occlusionNormalTexture = _framebuffer->getNormalTexture(); + auto occlusionNormalFramebuffer = _framebuffer->getNormalFramebuffer(resolutionLevel); + auto occlusionNormalTexture = _framebuffer->getNormalTexture(resolutionLevel); #endif auto fullNormalTexture = linearDepthFramebuffer->getNormalTexture(); @@ -536,12 +543,13 @@ void AmbientOcclusionEffect::run(const render::RenderContextPointer& renderConte #if SSAO_USE_QUAD_SPLIT // Build derivative normals pass batch.pushProfileRange("Build Normals"); - batch.setViewportTransform(sourceViewport); + batch.setViewportTransform(occlusionViewport); batch.setPipeline(buildNormalsPipeline); + batch.setResourceTexture(render_utils::slot::texture::SsaoDepth, linearDepthTexture); + batch.setResourceTexture(render_utils::slot::texture::SsaoNormal, nullptr); batch.setUniformBuffer(render_utils::slot::buffer::DeferredFrameTransform, frameTransform->getFrameTransformBuffer()); batch.setUniformBuffer(render_utils::slot::buffer::SsaoParams, _aoParametersBuffer); batch.setFramebuffer(occlusionNormalFramebuffer); - batch.setResourceTexture(render_utils::slot::texture::SsaoDepth, linearDepthTexture); batch.draw(gpu::TRIANGLE_STRIP, 4); batch.popProfileRange(); #endif diff --git a/libraries/render-utils/src/AmbientOcclusionEffect.h b/libraries/render-utils/src/AmbientOcclusionEffect.h index e04e24b4f4..1509cf6492 100644 --- a/libraries/render-utils/src/AmbientOcclusionEffect.h +++ b/libraries/render-utils/src/AmbientOcclusionEffect.h @@ -31,8 +31,8 @@ public: gpu::FramebufferPointer getOcclusionBlurredFramebuffer(); gpu::TexturePointer getOcclusionBlurredTexture(); - gpu::FramebufferPointer getNormalFramebuffer(); - gpu::TexturePointer getNormalTexture(); + gpu::FramebufferPointer getNormalFramebuffer(int resolutionLevel); + gpu::TexturePointer getNormalTexture(int resolutionLevel); // Update the source framebuffer size which will drive the allocation of all the other resources. bool updateLinearDepth(const gpu::TexturePointer& linearDepthBuffer); @@ -40,8 +40,10 @@ public: const glm::ivec2& getSourceFrameSize() const { return _frameSize; } protected: + void clear(); void allocate(); + void allocate(int resolutionLevel); gpu::TexturePointer _linearDepthTexture; @@ -56,6 +58,7 @@ protected: glm::ivec2 _frameSize; + int _resolutionLevel{ 0 }; }; using AmbientOcclusionFramebufferPointer = std::shared_ptr; diff --git a/libraries/render-utils/src/ssao.slh b/libraries/render-utils/src/ssao.slh index c4efb3be89..dec0109922 100644 --- a/libraries/render-utils/src/ssao.slh +++ b/libraries/render-utils/src/ssao.slh @@ -202,6 +202,10 @@ vec3 getTapLocationClampedSSAO(int sampleNumber, float spinAngle, float outerRad layout(binding=RENDER_UTILS_TEXTURE_SSAO_DEPTH) uniform sampler2D depthPyramidTex; layout(binding=RENDER_UTILS_TEXTURE_SSAO_NORMAL) uniform sampler2D normalTex; +ivec2 getDepthTextureSize(int level) { + return textureSize(depthPyramidTex, level); +} + float getZEyeAtPixel(ivec2 pixel, int level) { return -texelFetch(depthPyramidTex, pixel, level).x; } diff --git a/libraries/render-utils/src/ssao_buildNormals.slf b/libraries/render-utils/src/ssao_buildNormals.slf index 4d7d6e6530..8e06d4ffc9 100644 --- a/libraries/render-utils/src/ssao_buildNormals.slf +++ b/libraries/render-utils/src/ssao_buildNormals.slf @@ -18,17 +18,19 @@ layout(location=0) out vec4 outFragColor; void main(void) { - vec2 sideImageSize = getStereoSideSize(0); + vec2 sideImageSize = getStereoSideSize(getResolutionLevel()); + ivec2 renderSize = ivec2(sideImageSize) << ivec2(int(isStereo()) & 1, 0); // Pixel being shaded vec2 fragCoord = gl_FragCoord.xy; ivec2 fragPixelPos = ivec2(fragCoord.xy); // Fetch the z under the pixel (stereo or not) - float Zeye = getZEyeAtPixel(fragPixelPos, 0); + ivec2 depthTexFragPixelPos = fragPixelPos * getDepthTextureSize(0) / renderSize ; + float Zeye = getZEyeAtPixel(depthTexFragPixelPos, 0); // Stereo side info - ivec4 side = getStereoSideInfo(fragPixelPos.x, 0); + ivec4 side = getStereoSideInfo(fragPixelPos.x, getResolutionLevel()); // From now on, fragPixelPos is the pixel pos in the side fragPixelPos.x -= side.y; @@ -39,7 +41,5 @@ void main(void) { vec3 fragNormalES = evalEyeNormal(fragPositionES); vec3 absFragNormalES = abs(fragNormalES); - // Maximize encoding precision - fragNormalES /= max(max(absFragNormalES.x, absFragNormalES.y), absFragNormalES.z); outFragColor = vec4(vec3(fragNormalES)*0.5 + vec3(0.5), 1.0); } diff --git a/libraries/render-utils/src/ssao_makeOcclusion.slf b/libraries/render-utils/src/ssao_makeOcclusion.slf index fee4d6f84f..3db497fdc7 100644 --- a/libraries/render-utils/src/ssao_makeOcclusion.slf +++ b/libraries/render-utils/src/ssao_makeOcclusion.slf @@ -25,6 +25,7 @@ layout(location=0) out vec4 outFragColor; void main(void) { vec2 sideImageSize = getStereoSideSize(getResolutionLevel()); + ivec2 renderSize = ivec2(sideImageSize) << ivec2(int(isStereo()) & 1, 0); // Pixel being shaded vec2 fragCoord = gl_FragCoord.xy; @@ -35,7 +36,8 @@ void main(void) { #endif // Fetch the z under the pixel (stereo or not) - float Zeye = getZEyeAtPixel(fragPixelPos, 0); + ivec2 depthTexFragPixelPos = fragPixelPos * getDepthTextureSize(0) / renderSize ; + float Zeye = getZEyeAtPixel(depthTexFragPixelPos, 0); #if SSAO_USE_QUAD_SPLIT vec3 fragNormalES = getNormalEyeAtPixel(fragPixelPos, 0); #endif From 64b72e353894ef407a79dd81903ad76302daee70 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Thu, 20 Sep 2018 11:27:00 +0200 Subject: [PATCH 034/286] Removed normal from bilateral blur... again --- .../render-utils/src/AmbientOcclusionEffect.cpp | 2 -- libraries/render-utils/src/ssao.slh | 13 ++++--------- 2 files changed, 4 insertions(+), 11 deletions(-) diff --git a/libraries/render-utils/src/AmbientOcclusionEffect.cpp b/libraries/render-utils/src/AmbientOcclusionEffect.cpp index 5cb95c01b2..2a3a3f9f4c 100644 --- a/libraries/render-utils/src/AmbientOcclusionEffect.cpp +++ b/libraries/render-utils/src/AmbientOcclusionEffect.cpp @@ -508,7 +508,6 @@ void AmbientOcclusionEffect::run(const render::RenderContextPointer& renderConte auto occlusionNormalFramebuffer = _framebuffer->getNormalFramebuffer(resolutionLevel); auto occlusionNormalTexture = _framebuffer->getNormalTexture(resolutionLevel); #endif - auto fullNormalTexture = linearDepthFramebuffer->getNormalTexture(); // Update sample rotation const int SSAO_RANDOM_SAMPLE_COUNT = int(_randomSamples.size() / SSAO_SPLIT_COUNT); @@ -620,7 +619,6 @@ void AmbientOcclusionEffect::run(const render::RenderContextPointer& renderConte batch.setFramebuffer(occlusionBlurredFBO); // Use full resolution depth and normal for bilateral upscaling and blur batch.setResourceTexture(render_utils::slot::texture::SsaoDepth, linearDepthTexture); - batch.setResourceTexture(render_utils::slot::texture::SsaoNormal, fullNormalTexture); batch.setUniformBuffer(render_utils::slot::buffer::SsaoBlurParams, _hblurParametersBuffer); batch.setPipeline(firstHBlurPipeline); batch.setResourceTexture(render_utils::slot::texture::SsaoOcclusion, occlusionFBO->getRenderBuffer(0)); diff --git a/libraries/render-utils/src/ssao.slh b/libraries/render-utils/src/ssao.slh index dec0109922..d34d92276b 100644 --- a/libraries/render-utils/src/ssao.slh +++ b/libraries/render-utils/src/ssao.slh @@ -401,9 +401,8 @@ float fetchOcclusion(vec2 coords) { } const float BLUR_EDGE_DISTANCE_SCALE = 300.0; -const float BLUR_EDGE_NORMAL_SCALE = 0.5; -vec2 evalTapWeightedValue(ivec3 side, int r, ivec2 destPixelCoord, vec2 scaledTexCoord, vec2 fullTexCoord, float fragDepth, vec3 fragNormal) { +vec2 evalTapWeightedValue(ivec3 side, int r, ivec2 destPixelCoord, vec2 scaledTexCoord, vec2 fullTexCoord, float fragDepth) { ivec2 tapOffset = <$axis$> * r; ivec2 tapPixelCoord = destPixelCoord + ivec2(tapOffset); @@ -416,14 +415,11 @@ vec2 evalTapWeightedValue(ivec3 side, int r, ivec2 destPixelCoord, vec2 scaledTe tapTexCoord = fullTexCoord + tapOffset * getDepthBlurScale(); float tapDepth = getZEyeAtUV(tapTexCoord, 0); - vec3 tapNormal = getNormalEyeAtUV(tapTexCoord, 0); float weight = getBlurCoef(abs(r)); // range domain (the "bilateral" weight). As depth difference increases, decrease weight. float zDistance = tapDepth - fragDepth; - tapNormal -= fragNormal; - float normalDistance = dot(tapNormal,tapNormal); - weight *= exp(-getBlurEdgeSharpness() * (zDistance * zDistance * BLUR_EDGE_DISTANCE_SCALE + normalDistance * BLUR_EDGE_NORMAL_SCALE)); + weight *= exp(-getBlurEdgeSharpness() * (zDistance * zDistance * BLUR_EDGE_DISTANCE_SCALE)); return vec2(tapOcclusion * weight, weight); } @@ -433,7 +429,6 @@ vec3 getBlurredOcclusion(ivec2 destPixelCoord, vec2 scaledTexCoord, vec2 fullTex ivec4 side = getStereoSideInfo(destPixelCoord.x, 0); float fragDepth = getZEyeAtUV(fullTexCoord, 0); - vec3 fragNormal = getNormalEyeAtUV(fullTexCoord, 0); vec2 weightedSums = vec2(0.0); // Accumulate weighted contributions along the bluring axis in the [-radius, radius] range @@ -441,7 +436,7 @@ vec3 getBlurredOcclusion(ivec2 destPixelCoord, vec2 scaledTexCoord, vec2 fullTex // negative side first for (int r = -blurRadius; r <= -1; ++r) { - weightedSums += evalTapWeightedValue(side.xyz, r, destPixelCoord, scaledTexCoord, fullTexCoord, fragDepth, fragNormal); + weightedSums += evalTapWeightedValue(side.xyz, r, destPixelCoord, scaledTexCoord, fullTexCoord, fragDepth); } // Central pixel contribution @@ -451,7 +446,7 @@ vec3 getBlurredOcclusion(ivec2 destPixelCoord, vec2 scaledTexCoord, vec2 fullTex // then positive side for (int r = 1; r <= blurRadius; ++r) { - weightedSums += evalTapWeightedValue(side.xyz, r, destPixelCoord, scaledTexCoord, fullTexCoord, fragDepth, fragNormal); + weightedSums += evalTapWeightedValue(side.xyz, r, destPixelCoord, scaledTexCoord, fullTexCoord, fragDepth); } // Final normalization From 1c2da13309b3a8859f44a50697cc64d6ef7ba4b8 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Thu, 20 Sep 2018 15:34:28 +0200 Subject: [PATCH 035/286] Much better face normal reconstruction --- libraries/render-utils/src/ssao.slh | 26 +++++++++++++++++++ .../render-utils/src/ssao_buildNormals.slf | 5 ++-- .../render-utils/src/ssao_makeOcclusion.slf | 5 ++-- 3 files changed, 32 insertions(+), 4 deletions(-) diff --git a/libraries/render-utils/src/ssao.slh b/libraries/render-utils/src/ssao.slh index d34d92276b..17ac229805 100644 --- a/libraries/render-utils/src/ssao.slh +++ b/libraries/render-utils/src/ssao.slh @@ -243,6 +243,32 @@ vec4 fetchTap(ivec4 side, vec2 tapSidePixelPos, float tapRadius, vec2 sideImageS return P; } +vec3 buildPosition(ivec4 side, vec2 fragUVPos, ivec2 depthTexFragPixelPos, ivec2 deltaDepthTexPixel, vec2 uvScale) { + depthTexFragPixelPos += deltaDepthTexPixel; + fragUVPos += deltaDepthTexPixel * uvScale; + float Zeye = getZEyeAtPixel(depthTexFragPixelPos, 0); + return evalEyePositionFromZeye(side.x, Zeye, fragUVPos); +} + +vec3 getMinDiff(vec3 centralPoint, vec3 offsetPoint0, vec3 offsetPoint1) { + vec3 delta0 = offsetPoint0 - centralPoint; + vec3 delta1 = centralPoint - offsetPoint1; + return dot(delta0, delta0) < dot(delta1, delta1) ? delta0 : delta1; +} + +vec3 buildNormal(ivec4 side, vec2 fragUVPos, ivec2 depthTexFragPixelPos, vec3 fragPositionES, ivec2 depthTextureScale, vec2 sideImageSize) { + vec2 uvScale = vec2(1.0) / (sideImageSize * depthTextureScale); + vec3 fragPositionDxPos = buildPosition(side, fragUVPos, depthTexFragPixelPos, ivec2(1,0), uvScale); + vec3 fragPositionDxNeg = buildPosition(side, fragUVPos, depthTexFragPixelPos, ivec2(-1,0), uvScale); + vec3 fragPositionDyPos = buildPosition(side, fragUVPos, depthTexFragPixelPos, ivec2(0,1), uvScale); + vec3 fragPositionDyNeg = buildPosition(side, fragUVPos, depthTexFragPixelPos, ivec2(0,-1), uvScale); + + vec3 fragPositionDx = getMinDiff(fragPositionES, fragPositionDxPos, fragPositionDxNeg); + vec3 fragPositionDy = getMinDiff(fragPositionES, fragPositionDyPos, fragPositionDyNeg); + + return normalize( cross(fragPositionDx, fragPositionDy) ); +} + <@endfunc@> diff --git a/libraries/render-utils/src/ssao_buildNormals.slf b/libraries/render-utils/src/ssao_buildNormals.slf index 8e06d4ffc9..f21a529807 100644 --- a/libraries/render-utils/src/ssao_buildNormals.slf +++ b/libraries/render-utils/src/ssao_buildNormals.slf @@ -26,7 +26,8 @@ void main(void) { ivec2 fragPixelPos = ivec2(fragCoord.xy); // Fetch the z under the pixel (stereo or not) - ivec2 depthTexFragPixelPos = fragPixelPos * getDepthTextureSize(0) / renderSize ; + ivec2 depthTextureScale = getDepthTextureSize(0) / renderSize; + ivec2 depthTexFragPixelPos = fragPixelPos * depthTextureScale; float Zeye = getZEyeAtPixel(depthTexFragPixelPos, 0); // Stereo side info @@ -38,7 +39,7 @@ void main(void) { // The position and normal of the pixel fragment in Eye space vec3 fragPositionES = evalEyePositionFromZeye(side.x, Zeye, fragUVPos); - vec3 fragNormalES = evalEyeNormal(fragPositionES); + vec3 fragNormalES = buildNormal(side, fragUVPos, depthTexFragPixelPos, fragPositionES, depthTextureScale, sideImageSize); vec3 absFragNormalES = abs(fragNormalES); outFragColor = vec4(vec3(fragNormalES)*0.5 + vec3(0.5), 1.0); diff --git a/libraries/render-utils/src/ssao_makeOcclusion.slf b/libraries/render-utils/src/ssao_makeOcclusion.slf index 3db497fdc7..cee5ef6fe3 100644 --- a/libraries/render-utils/src/ssao_makeOcclusion.slf +++ b/libraries/render-utils/src/ssao_makeOcclusion.slf @@ -36,7 +36,8 @@ void main(void) { #endif // Fetch the z under the pixel (stereo or not) - ivec2 depthTexFragPixelPos = fragPixelPos * getDepthTextureSize(0) / renderSize ; + ivec2 depthTextureScale = getDepthTextureSize(0) / renderSize; + ivec2 depthTexFragPixelPos = fragPixelPos * depthTextureScale; float Zeye = getZEyeAtPixel(depthTexFragPixelPos, 0); #if SSAO_USE_QUAD_SPLIT vec3 fragNormalES = getNormalEyeAtPixel(fragPixelPos, 0); @@ -52,7 +53,7 @@ void main(void) { // The position and normal of the pixel fragment in Eye space vec3 fragPositionES = evalEyePositionFromZeye(side.x, Zeye, fragUVPos); #if !SSAO_USE_QUAD_SPLIT - vec3 fragNormalES = evalEyeNormal(fragPositionES); + vec3 fragNormalES = buildNormal(side, fragUVPos, depthTexFragPixelPos, fragPositionES, depthTextureScale, sideImageSize); #endif // Choose the screen-space sample radius From cf739db3a5df0461e8a59f9bd50d9e22f903b9a9 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Thu, 20 Sep 2018 18:01:02 +0200 Subject: [PATCH 036/286] Added AO normal to Luci --- .../src/AmbientOcclusionEffect.cpp | 6 ++- .../render-utils/src/AmbientOcclusionEffect.h | 1 + .../render-utils/src/DebugDeferredBuffer.cpp | 4 ++ .../render-utils/src/DebugDeferredBuffer.h | 1 + libraries/render-utils/src/ssao.slh | 39 +++++++++---------- .../render-utils/src/ssao_buildNormals.slf | 3 +- .../utilities/render/deferredLighting.qml | 1 + 7 files changed, 31 insertions(+), 24 deletions(-) diff --git a/libraries/render-utils/src/AmbientOcclusionEffect.cpp b/libraries/render-utils/src/AmbientOcclusionEffect.cpp index 2a3a3f9f4c..3fcf4ef656 100644 --- a/libraries/render-utils/src/AmbientOcclusionEffect.cpp +++ b/libraries/render-utils/src/AmbientOcclusionEffect.cpp @@ -141,6 +141,10 @@ gpu::TexturePointer AmbientOcclusionFramebuffer::getNormalTexture(int resolution return _normalTexture; } +gpu::TexturePointer AmbientOcclusionFramebuffer::getNormalTexture() { + return _normalTexture; +} + class GaussianDistribution { public: @@ -540,7 +544,7 @@ void AmbientOcclusionEffect::run(const render::RenderContextPointer& renderConte batch.popProfileRange(); #if SSAO_USE_QUAD_SPLIT - // Build derivative normals pass + // Build face normals pass batch.pushProfileRange("Build Normals"); batch.setViewportTransform(occlusionViewport); batch.setPipeline(buildNormalsPipeline); diff --git a/libraries/render-utils/src/AmbientOcclusionEffect.h b/libraries/render-utils/src/AmbientOcclusionEffect.h index 1509cf6492..b0752277c2 100644 --- a/libraries/render-utils/src/AmbientOcclusionEffect.h +++ b/libraries/render-utils/src/AmbientOcclusionEffect.h @@ -33,6 +33,7 @@ public: gpu::FramebufferPointer getNormalFramebuffer(int resolutionLevel); gpu::TexturePointer getNormalTexture(int resolutionLevel); + gpu::TexturePointer getNormalTexture(); // Update the source framebuffer size which will drive the allocation of all the other resources. bool updateLinearDepth(const gpu::TexturePointer& linearDepthBuffer); diff --git a/libraries/render-utils/src/DebugDeferredBuffer.cpp b/libraries/render-utils/src/DebugDeferredBuffer.cpp index 4a8fad7c6d..3959105829 100644 --- a/libraries/render-utils/src/DebugDeferredBuffer.cpp +++ b/libraries/render-utils/src/DebugDeferredBuffer.cpp @@ -321,6 +321,8 @@ std::string DebugDeferredBuffer::getShaderSourceCode(Mode mode, std::string cust return DEFAULT_AMBIENT_OCCLUSION_SHADER; case AmbientOcclusionBlurredMode: return DEFAULT_AMBIENT_OCCLUSION_BLURRED_SHADER; + case AmbientOcclusionNormalMode: + return DEFAULT_HALF_NORMAL_SHADER; case VelocityMode: return DEFAULT_VELOCITY_SHADER; case CustomMode: @@ -469,6 +471,8 @@ void DebugDeferredBuffer::run(const RenderContextPointer& renderContext, const I batch.setResourceTexture(Textures::DebugTexture0, ambientOcclusionFramebuffer->getOcclusionTexture()); } else if (_mode == AmbientOcclusionBlurredMode) { batch.setResourceTexture(Textures::DebugTexture0, ambientOcclusionFramebuffer->getOcclusionBlurredTexture()); + } else if (_mode == AmbientOcclusionNormalMode) { + batch.setResourceTexture(Textures::DebugTexture0, ambientOcclusionFramebuffer->getNormalTexture()); } } const glm::vec4 color(1.0f, 1.0f, 1.0f, 1.0f); diff --git a/libraries/render-utils/src/DebugDeferredBuffer.h b/libraries/render-utils/src/DebugDeferredBuffer.h index 0fc006b452..2b8b288a83 100644 --- a/libraries/render-utils/src/DebugDeferredBuffer.h +++ b/libraries/render-utils/src/DebugDeferredBuffer.h @@ -88,6 +88,7 @@ protected: ScatteringDebugMode, AmbientOcclusionMode, AmbientOcclusionBlurredMode, + AmbientOcclusionNormalMode, VelocityMode, CustomMode, // Needs to stay last diff --git a/libraries/render-utils/src/ssao.slh b/libraries/render-utils/src/ssao.slh index 17ac229805..56566c9b6f 100644 --- a/libraries/render-utils/src/ssao.slh +++ b/libraries/render-utils/src/ssao.slh @@ -223,7 +223,7 @@ vec3 getNormalEyeAtPixel(ivec2 pixel, int level) { } int evalMipFromRadius(float radius) { - const int LOG_MAX_OFFSET = 2; + const int LOG_MAX_OFFSET = 1; const int MAX_MIP_LEVEL = 5; return clamp(findMSB(int(radius)) - LOG_MAX_OFFSET, 0, MAX_MIP_LEVEL); } @@ -231,10 +231,8 @@ int evalMipFromRadius(float radius) { vec4 fetchTap(ivec4 side, vec2 tapSidePixelPos, float tapRadius, vec2 sideImageSize) { int mipLevel = evalMipFromRadius(tapRadius * float(doFetchMips())); - // We need to divide by 2^mipLevel to read the appropriately scaled coordinate from a MIP-map. - // Manually clamp to the texture size because texelFetch bypasses the texture unit vec2 tapUV = (tapSidePixelPos + vec2(0.5)) / sideImageSize; - vec2 fetchUV = mix(tapUV, vec2((tapUV.x + getStereoSide(side)) * 0.5, tapUV.y), isStereoFromInfo(side)); + vec2 fetchUV = mix(tapUV, vec2((tapUV.x + getStereoSide(side)) * 0.5, tapUV.y), isStereo()); vec4 P; P.xy = tapUV; @@ -250,21 +248,21 @@ vec3 buildPosition(ivec4 side, vec2 fragUVPos, ivec2 depthTexFragPixelPos, ivec2 return evalEyePositionFromZeye(side.x, Zeye, fragUVPos); } -vec3 getMinDiff(vec3 centralPoint, vec3 offsetPoint0, vec3 offsetPoint1) { - vec3 delta0 = offsetPoint0 - centralPoint; - vec3 delta1 = centralPoint - offsetPoint1; +vec3 getMinDelta(vec3 centralPoint, vec3 offsetPointPos, vec3 offsetPointNeg) { + vec3 delta0 = offsetPointPos - centralPoint; + vec3 delta1 = centralPoint - offsetPointNeg; return dot(delta0, delta0) < dot(delta1, delta1) ? delta0 : delta1; } -vec3 buildNormal(ivec4 side, vec2 fragUVPos, ivec2 depthTexFragPixelPos, vec3 fragPositionES, ivec2 depthTextureScale, vec2 sideImageSize) { +vec3 buildNormal(ivec4 side, vec2 fragUVPos, ivec2 depthTexFragPixelPos, vec3 fragPosition, ivec2 depthTextureScale, vec2 sideImageSize) { vec2 uvScale = vec2(1.0) / (sideImageSize * depthTextureScale); - vec3 fragPositionDxPos = buildPosition(side, fragUVPos, depthTexFragPixelPos, ivec2(1,0), uvScale); - vec3 fragPositionDxNeg = buildPosition(side, fragUVPos, depthTexFragPixelPos, ivec2(-1,0), uvScale); - vec3 fragPositionDyPos = buildPosition(side, fragUVPos, depthTexFragPixelPos, ivec2(0,1), uvScale); - vec3 fragPositionDyNeg = buildPosition(side, fragUVPos, depthTexFragPixelPos, ivec2(0,-1), uvScale); + vec3 fragPositionDxPos = buildPosition(side, fragUVPos, depthTexFragPixelPos, ivec2(1, 0), uvScale); + vec3 fragPositionDyPos = buildPosition(side, fragUVPos, depthTexFragPixelPos, ivec2(0, 1), uvScale); + vec3 fragPositionDxNeg = buildPosition(side, fragUVPos, depthTexFragPixelPos, ivec2(-1, 0), uvScale); + vec3 fragPositionDyNeg = buildPosition(side, fragUVPos, depthTexFragPixelPos, ivec2(0, -1), uvScale); - vec3 fragPositionDx = getMinDiff(fragPositionES, fragPositionDxPos, fragPositionDxNeg); - vec3 fragPositionDy = getMinDiff(fragPositionES, fragPositionDyPos, fragPositionDyNeg); + vec3 fragPositionDx = getMinDelta(fragPosition, fragPositionDxPos, fragPositionDxNeg); + vec3 fragPositionDy = getMinDelta(fragPosition, fragPositionDyPos, fragPositionDyNeg); return normalize( cross(fragPositionDx, fragPositionDy) ); } @@ -287,31 +285,30 @@ float evalVisibilitySSAO(in vec3 centerPosition, in vec3 centerNormal, in vec3 t float vv = dot(v, v); float vn = dot(v, centerNormal); - // Fall off function as recommended in SSAO paper + // Falloff function as recommended in SSAO paper const float epsilon = 0.01; float f = max(getRadius2() - vv, 0.0); return f * f * f * max((vn - getFalloffAngle()) / (epsilon + vv), 0.0); } float computeHorizonFromTap(vec3 tapPositionES, vec3 fragPositionES, vec3 fragNormalES) { - const float epsilon = 0.01; + const float epsilon = 0.001; vec3 deltaVec = tapPositionES - fragPositionES; float distance = length(deltaVec); - float cosHorizonAngle = 0.0; + float cosHorizonAngle = dot(deltaVec, fragNormalES) / (distance + epsilon); float radiusFalloff = max(0.0, 1.0 - (distance*distance / getRadius2())); - cosHorizonAngle = dot(deltaVec, fragNormalES) / (distance + epsilon); cosHorizonAngle = max(0.0, (cosHorizonAngle - getFalloffAngle()) * getFalloffAngleScale()); cosHorizonAngle *= radiusFalloff; - return cosHorizonAngle; } <@func computeHorizon()@> vec2 tapSidePixelPos = tapPixelOffset + shadedPixelPos; - if (tapSidePixelPos.x<0 && tapSidePixelPos.y<0 && tapSidePixelPos.x>=sideImageSize.x && tapSidePixelPos.y>=sideImageSize.y) { + if (tapSidePixelPos.x<0 || tapSidePixelPos.y<0 || tapSidePixelPos.x>=sideImageSize.x || tapSidePixelPos.y>=sideImageSize.y) { + // Early exit because we've hit the borders of the frame break; } vec4 tapUVZ_mip = fetchTap(side, tapSidePixelPos, radius, sideImageSize); @@ -426,7 +423,7 @@ float fetchOcclusion(vec2 coords) { return raw.x; } -const float BLUR_EDGE_DISTANCE_SCALE = 300.0; +const float BLUR_EDGE_DISTANCE_SCALE = 1000.0; vec2 evalTapWeightedValue(ivec3 side, int r, ivec2 destPixelCoord, vec2 scaledTexCoord, vec2 fullTexCoord, float fragDepth) { ivec2 tapOffset = <$axis$> * r; diff --git a/libraries/render-utils/src/ssao_buildNormals.slf b/libraries/render-utils/src/ssao_buildNormals.slf index f21a529807..f3af324455 100644 --- a/libraries/render-utils/src/ssao_buildNormals.slf +++ b/libraries/render-utils/src/ssao_buildNormals.slf @@ -40,7 +40,6 @@ void main(void) { // The position and normal of the pixel fragment in Eye space vec3 fragPositionES = evalEyePositionFromZeye(side.x, Zeye, fragUVPos); vec3 fragNormalES = buildNormal(side, fragUVPos, depthTexFragPixelPos, fragPositionES, depthTextureScale, sideImageSize); - vec3 absFragNormalES = abs(fragNormalES); - + outFragColor = vec4(vec3(fragNormalES)*0.5 + vec3(0.5), 1.0); } diff --git a/scripts/developer/utilities/render/deferredLighting.qml b/scripts/developer/utilities/render/deferredLighting.qml index 8a4a8f0622..1e1e6055f8 100644 --- a/scripts/developer/utilities/render/deferredLighting.qml +++ b/scripts/developer/utilities/render/deferredLighting.qml @@ -202,6 +202,7 @@ Rectangle { ListElement { text: "Debug Scattering"; color: "White" } ListElement { text: "Ambient Occlusion"; color: "White" } ListElement { text: "Ambient Occlusion Blurred"; color: "White" } + ListElement { text: "Ambient Occlusion Normal"; color: "White" } ListElement { text: "Velocity"; color: "White" } ListElement { text: "Custom"; color: "White" } } From 9f0201878db3ef9cce0661320be107e34b4f20b5 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Fri, 21 Sep 2018 17:14:39 +0200 Subject: [PATCH 037/286] Some work to try to find the odd resolution bug --- .../src/AmbientOcclusionEffect.cpp | 121 ++++++++++++------ .../render-utils/src/AmbientOcclusionEffect.h | 11 +- .../render-utils/src/DeferredTransform.slh | 13 +- .../render-utils/src/SurfaceGeometryPass.cpp | 26 ++-- .../render-utils/src/SurfaceGeometryPass.h | 3 +- libraries/render-utils/src/ssao.slh | 53 +++++++- .../render-utils/src/ssao_buildNormals.slf | 20 +-- libraries/render-utils/src/ssao_gather.slf | 2 +- .../render-utils/src/ssao_makeOcclusion.slf | 36 ++++-- libraries/render-utils/src/ssao_shared.h | 1 + libraries/shared/src/MathUtils.h | 21 +++ 11 files changed, 216 insertions(+), 91 deletions(-) create mode 100644 libraries/shared/src/MathUtils.h diff --git a/libraries/render-utils/src/AmbientOcclusionEffect.cpp b/libraries/render-utils/src/AmbientOcclusionEffect.cpp index 3fcf4ef656..8e2a25b3e6 100644 --- a/libraries/render-utils/src/AmbientOcclusionEffect.cpp +++ b/libraries/render-utils/src/AmbientOcclusionEffect.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include "RenderUtilsLogging.h" @@ -40,13 +41,18 @@ gpu::PipelinePointer AmbientOcclusionEffect::_buildNormalsPipeline; AmbientOcclusionFramebuffer::AmbientOcclusionFramebuffer() { } -bool AmbientOcclusionFramebuffer::updateLinearDepth(const gpu::TexturePointer& linearDepthBuffer) { +bool AmbientOcclusionFramebuffer::update(const gpu::TexturePointer& linearDepthBuffer, int resolutionLevel, bool isStereo) { // If the depth buffer or size changed, we need to delete our FBOs bool reset = false; - if ((_linearDepthTexture != linearDepthBuffer)) { + if (_linearDepthTexture != linearDepthBuffer) { _linearDepthTexture = linearDepthBuffer; reset = true; } + if (_resolutionLevel != resolutionLevel || isStereo != _isStereo) { + _resolutionLevel = resolutionLevel; + _isStereo = isStereo; + reset = true; + } if (_linearDepthTexture) { auto newFrameSize = glm::ivec2(_linearDepthTexture->getDimensions()); if (_frameSize != newFrameSize) { @@ -76,17 +82,38 @@ gpu::TexturePointer AmbientOcclusionFramebuffer::getLinearDepthTexture() { } void AmbientOcclusionFramebuffer::allocate() { - auto width = _frameSize.x; - auto height = _frameSize.y; - auto format = gpu::Element::COLOR_R_8; + // Full frame + { + auto width = _frameSize.x; + auto height = _frameSize.y; + auto format = gpu::Element::COLOR_R_8; - _occlusionTexture = gpu::Texture::createRenderBuffer(format, width, height, gpu::Texture::SINGLE_MIP, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR, gpu::Sampler::WRAP_CLAMP)); - _occlusionFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("occlusion")); - _occlusionFramebuffer->setRenderBuffer(0, _occlusionTexture); + _occlusionTexture = gpu::Texture::createRenderBuffer(format, width, height, gpu::Texture::SINGLE_MIP, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR, gpu::Sampler::WRAP_CLAMP)); + _occlusionFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("occlusion")); + _occlusionFramebuffer->setRenderBuffer(0, _occlusionTexture); - _occlusionBlurredTexture = gpu::Texture::createRenderBuffer(format, width, height, gpu::Texture::SINGLE_MIP, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR, gpu::Sampler::WRAP_CLAMP)); - _occlusionBlurredFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("occlusionBlurred")); - _occlusionBlurredFramebuffer->setRenderBuffer(0, _occlusionBlurredTexture); + _occlusionBlurredTexture = gpu::Texture::createRenderBuffer(format, width, height, gpu::Texture::SINGLE_MIP, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR, gpu::Sampler::WRAP_CLAMP)); + _occlusionBlurredFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("occlusionBlurred")); + _occlusionBlurredFramebuffer->setRenderBuffer(0, _occlusionBlurredTexture); + } + + // Lower res frame + { + auto sideSize = _frameSize; + if (_isStereo) { + sideSize.x >>= 1; + } + sideSize = divideRoundUp(sideSize, 1 << _resolutionLevel); + if (_isStereo) { + sideSize.x <<= 1; + } + auto width = sideSize.x; + auto height = sideSize.y; + + _normalTexture = gpu::Texture::createRenderBuffer(gpu::Element::COLOR_RGBA_32, width, height, gpu::Texture::SINGLE_MIP, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_POINT, gpu::Sampler::WRAP_CLAMP)); + _normalFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("ssaoNormals")); + _normalFramebuffer->setRenderBuffer(0, _normalTexture); + } } gpu::FramebufferPointer AmbientOcclusionFramebuffer::getOcclusionFramebuffer() { @@ -117,31 +144,17 @@ gpu::TexturePointer AmbientOcclusionFramebuffer::getOcclusionBlurredTexture() { return _occlusionBlurredTexture; } -void AmbientOcclusionFramebuffer::allocate(int resolutionLevel) { - auto width = _frameSize.x >> resolutionLevel; - auto height = _frameSize.y >> resolutionLevel; - - _normalTexture = gpu::Texture::createRenderBuffer(gpu::Element::COLOR_R11G11B10, width, height, gpu::Texture::SINGLE_MIP, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_POINT, gpu::Sampler::WRAP_CLAMP)); - _normalFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("ssaoNormals")); - _normalFramebuffer->setRenderBuffer(0, _normalTexture); - _resolutionLevel = resolutionLevel; -} - -gpu::FramebufferPointer AmbientOcclusionFramebuffer::getNormalFramebuffer(int resolutionLevel) { - if (!_normalFramebuffer || resolutionLevel != _resolutionLevel) { - allocate(resolutionLevel); +gpu::FramebufferPointer AmbientOcclusionFramebuffer::getNormalFramebuffer() { + if (!_normalFramebuffer) { + allocate(); } return _normalFramebuffer; } -gpu::TexturePointer AmbientOcclusionFramebuffer::getNormalTexture(int resolutionLevel) { - if (!_normalTexture || resolutionLevel != _resolutionLevel) { - allocate(resolutionLevel); - } - return _normalTexture; -} - gpu::TexturePointer AmbientOcclusionFramebuffer::getNormalTexture() { + if (!_normalTexture) { + allocate(); + } return _normalTexture; } @@ -377,6 +390,28 @@ void AmbientOcclusionEffect::updateBlurParameters() { vblur.scaleHeight.z = frameSize.y; } +void AmbientOcclusionEffect::updateFramebufferSizes() { + auto& params = _aoParametersBuffer.edit(); + const int widthScale = _framebuffer->isStereo() & 1; + auto sourceFrameSize = _framebuffer->getSourceFrameSize(); + const int resolutionLevel = _aoParametersBuffer.get().getResolutionLevel(); + // Depth is at maximum half depth + const int depthResolutionLevel = std::min(1, resolutionLevel); + + sourceFrameSize.x >>= widthScale; + + params._sideSizes[0].x = _framebuffer->getNormalTexture()->getWidth() >> widthScale; + params._sideSizes[0].y = _framebuffer->getNormalTexture()->getHeight(); + params._sideSizes[0].z = resolutionLevel; + params._sideSizes[0].w = depthResolutionLevel; + + params._sideSizes[1].x = params._sideSizes[0].x; + params._sideSizes[1].y = params._sideSizes[0].y; + auto occlusionSplitSize = divideRoundUp(sourceFrameSize, 1 << (resolutionLevel + SSAO_USE_QUAD_SPLIT)); + params._sideSizes[1].z = occlusionSplitSize.x; + params._sideSizes[1].w = occlusionSplitSize.y; +} + const gpu::PipelinePointer& AmbientOcclusionEffect::getOcclusionPipeline() { if (!_occlusionPipeline) { gpu::ShaderPointer program = gpu::Shader::createProgram(shader::render_utils::program::ssao_makeOcclusion); @@ -466,11 +501,20 @@ void AmbientOcclusionEffect::run(const render::RenderContextPointer& renderConte const auto& frameTransform = inputs.get0(); const auto& linearDepthFramebuffer = inputs.get2(); + const int resolutionLevel = _aoParametersBuffer->getResolutionLevel(); + const auto resolutionScale = powf(0.5f, resolutionLevel); + auto linearDepthTexture = linearDepthFramebuffer->getLinearDepthTexture(); auto occlusionDepthTexture = linearDepthTexture; auto sourceViewport = args->_viewport; - auto occlusionViewport = sourceViewport; + // divideRoundUp is used two compute the quarter or half resolution render sizes. + // We need to take the rounded up resolution. + auto occlusionViewport = divideRoundUp(sourceViewport, 1 << resolutionLevel); auto firstBlurViewport = sourceViewport; +#if SSAO_USE_QUAD_SPLIT + auto splitViewport = divideRoundUp(sourceViewport, 1 << (resolutionLevel + SSAO_USE_QUAD_SPLIT)); +#endif + firstBlurViewport.w = divideRoundUp(firstBlurViewport.w, 1 << resolutionLevel); if (!_gpuTimer) { _gpuTimer = std::make_shared < gpu::RangeTimer>(__FUNCTION__); @@ -480,16 +524,13 @@ void AmbientOcclusionEffect::run(const render::RenderContextPointer& renderConte _framebuffer = std::make_shared(); } - const int resolutionLevel = _aoParametersBuffer->getResolutionLevel(); - const auto resolutionScale = powf(0.5f, resolutionLevel); if (resolutionLevel > 0) { - occlusionViewport = occlusionViewport >> resolutionLevel; - firstBlurViewport.w = firstBlurViewport.w >> resolutionLevel; occlusionDepthTexture = linearDepthFramebuffer->getHalfLinearDepthTexture(); } - if (_framebuffer->updateLinearDepth(linearDepthTexture)) { + if (_framebuffer->update(linearDepthTexture, resolutionLevel, args->isStereo())) { updateBlurParameters(); + updateFramebufferSizes(); } auto occlusionFBO = _framebuffer->getOcclusionFramebuffer(); @@ -499,7 +540,6 @@ void AmbientOcclusionEffect::run(const render::RenderContextPointer& renderConte outputs.edit1() = _aoParametersBuffer; auto framebufferSize = _framebuffer->getSourceFrameSize(); - auto occlusionPipeline = getOcclusionPipeline(); auto firstHBlurPipeline = getHBlurPipeline(); auto lastVBlurPipeline = getVBlurPipeline(); @@ -509,8 +549,8 @@ void AmbientOcclusionEffect::run(const render::RenderContextPointer& renderConte #if SSAO_USE_QUAD_SPLIT auto gatherPipeline = getGatherPipeline(); auto buildNormalsPipeline = getBuildNormalsPipeline(); - auto occlusionNormalFramebuffer = _framebuffer->getNormalFramebuffer(resolutionLevel); - auto occlusionNormalTexture = _framebuffer->getNormalTexture(resolutionLevel); + auto occlusionNormalFramebuffer = _framebuffer->getNormalFramebuffer(); + auto occlusionNormalTexture = _framebuffer->getNormalTexture(); #endif // Update sample rotation @@ -567,7 +607,6 @@ void AmbientOcclusionEffect::run(const render::RenderContextPointer& renderConte #else batch.setFramebuffer(occlusionFBO); #endif - batch.clearColorFramebuffer(gpu::Framebuffer::BUFFER_COLOR0, glm::vec4(1.0f)); batch.setPipeline(occlusionPipeline); batch.setResourceTexture(render_utils::slot::texture::SsaoDepth, occlusionDepthTexture); diff --git a/libraries/render-utils/src/AmbientOcclusionEffect.h b/libraries/render-utils/src/AmbientOcclusionEffect.h index b0752277c2..c9f5d42b14 100644 --- a/libraries/render-utils/src/AmbientOcclusionEffect.h +++ b/libraries/render-utils/src/AmbientOcclusionEffect.h @@ -31,20 +31,19 @@ public: gpu::FramebufferPointer getOcclusionBlurredFramebuffer(); gpu::TexturePointer getOcclusionBlurredTexture(); - gpu::FramebufferPointer getNormalFramebuffer(int resolutionLevel); - gpu::TexturePointer getNormalTexture(int resolutionLevel); + gpu::FramebufferPointer getNormalFramebuffer(); gpu::TexturePointer getNormalTexture(); // Update the source framebuffer size which will drive the allocation of all the other resources. - bool updateLinearDepth(const gpu::TexturePointer& linearDepthBuffer); + bool update(const gpu::TexturePointer& linearDepthBuffer, int resolutionLevel, bool isStereo); gpu::TexturePointer getLinearDepthTexture(); const glm::ivec2& getSourceFrameSize() const { return _frameSize; } - + bool isStereo() const { return _isStereo; } + protected: void clear(); void allocate(); - void allocate(int resolutionLevel); gpu::TexturePointer _linearDepthTexture; @@ -60,6 +59,7 @@ protected: glm::ivec2 _frameSize; int _resolutionLevel{ 0 }; + bool _isStereo{ false }; }; using AmbientOcclusionFramebufferPointer = std::shared_ptr; @@ -167,6 +167,7 @@ private: void updateGaussianDistribution(); void updateBlurParameters(); + void updateFramebufferSizes(); AOParametersBuffer _aoParametersBuffer; FrameParametersBuffer _aoFrameParametersBuffer[SSAO_SPLIT_COUNT]; diff --git a/libraries/render-utils/src/DeferredTransform.slh b/libraries/render-utils/src/DeferredTransform.slh index d9ced66edf..9b98029bd6 100644 --- a/libraries/render-utils/src/DeferredTransform.slh +++ b/libraries/render-utils/src/DeferredTransform.slh @@ -124,17 +124,18 @@ vec2 getStereoSideSize(int resolutionLevel) { return vec2(getStereoSideWidth(resolutionLevel), getStereoSideHeight(resolutionLevel)); } -ivec4 getStereoSideInfo(int xPos, int resolutionLevel) { - int sideWidth = int(getStereoSideWidth(resolutionLevel)); +ivec4 getStereoSideInfoFromWidth(int xPos, int sideWidth) { return ivec4(xPos < sideWidth ? ivec2(0, 0) : ivec2(1, sideWidth), sideWidth, isStereo()); } -int getStereoSide(ivec4 sideInfo) { - return sideInfo.x; +ivec4 getStereoSideInfo(int xPos, int resolutionLevel) { + int sideWidth = int(getStereoSideWidth(resolutionLevel)); + return getStereoSideInfoFromWidth(xPos, sideWidth); } -bool isStereoFromInfo(ivec4 sideInfo) { - return sideInfo.w != 0; + +int getStereoSide(ivec4 sideInfo) { + return sideInfo.x; } float evalZeyeFromZdb(float depth) { diff --git a/libraries/render-utils/src/SurfaceGeometryPass.cpp b/libraries/render-utils/src/SurfaceGeometryPass.cpp index 374a509c5a..8f337b66da 100644 --- a/libraries/render-utils/src/SurfaceGeometryPass.cpp +++ b/libraries/render-utils/src/SurfaceGeometryPass.cpp @@ -11,6 +11,7 @@ #include "SurfaceGeometryPass.h" #include +#include #include #include @@ -28,7 +29,7 @@ namespace ru { LinearDepthFramebuffer::LinearDepthFramebuffer() { } -void LinearDepthFramebuffer::update(const gpu::TexturePointer& depthBuffer, const gpu::TexturePointer& normalTexture) { +void LinearDepthFramebuffer::update(const gpu::TexturePointer& depthBuffer, const gpu::TexturePointer& normalTexture, bool isStereo) { //If the depth buffer or size changed, we need to delete our FBOs bool reset = false; if (_primaryDepthTexture != depthBuffer || _normalTexture != normalTexture) { @@ -38,10 +39,17 @@ void LinearDepthFramebuffer::update(const gpu::TexturePointer& depthBuffer, cons } if (_primaryDepthTexture) { auto newFrameSize = glm::ivec2(_primaryDepthTexture->getDimensions()); - if (_frameSize != newFrameSize) { + if (_frameSize != newFrameSize || _isStereo != isStereo) { _frameSize = newFrameSize; - _halfFrameSize = newFrameSize >> 1; - + _halfFrameSize = _frameSize; + if (isStereo) { + _halfFrameSize.x >>= 1; + } + _halfFrameSize = divideRoundUp(_halfFrameSize, 2); + if (isStereo) { + _halfFrameSize.x <<= 1; + } + _isStereo = isStereo; reset = true; } } @@ -66,7 +74,7 @@ void LinearDepthFramebuffer::allocate() { // For Linear Depth: const uint16_t LINEAR_DEPTH_MAX_MIP_LEVEL = 5; - // Point sampling of the depth is need for the AmbientOcclusionEffect in HBAO, as well as the clamp to edge + // Point sampling of the depth is needed for the AmbientOcclusionEffect in HBAO, as well as the clamp to edge const auto depthSampler = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_POINT, gpu::Sampler::WRAP_CLAMP); _linearDepthTexture = gpu::Texture::createRenderBuffer(gpu::Element(gpu::SCALAR, gpu::FLOAT, gpu::RED), width, height, LINEAR_DEPTH_MAX_MIP_LEVEL, depthSampler); @@ -153,7 +161,7 @@ void LinearDepthPass::run(const render::RenderContextPointer& renderContext, con auto depthBuffer = deferredFramebuffer->getPrimaryDepthTexture(); auto normalTexture = deferredFramebuffer->getDeferredNormalTexture(); - _linearDepthFramebuffer->update(depthBuffer, normalTexture); + _linearDepthFramebuffer->update(depthBuffer, normalTexture, args->isStereo()); auto linearDepthFBO = _linearDepthFramebuffer->getLinearDepthFramebuffer(); auto linearDepthTexture = _linearDepthFramebuffer->getLinearDepthTexture(); @@ -172,7 +180,7 @@ void LinearDepthPass::run(const render::RenderContextPointer& renderContext, con auto downsamplePipeline = getDownsamplePipeline(renderContext); auto depthViewport = args->_viewport; - auto halfViewport = depthViewport >> 1; + auto halfViewport = divideRoundUp(depthViewport, 2); float clearLinearDepth = args->getViewFrustum().getFarClip() * 2.0f; gpu::doInBatch("LinearDepthPass::run", args->_context, [=](gpu::Batch& batch) { @@ -195,7 +203,9 @@ void LinearDepthPass::run(const render::RenderContextPointer& renderContext, con // Downsample batch.setViewportTransform(halfViewport); - + Transform model; + model.setScale( glm::vec3((depthViewport.z >> 1) / float(halfViewport.z), (depthViewport.w >> 1) / float(halfViewport.w), 1.0f) ); + batch.setModelTransform(model); batch.setFramebuffer(downsampleFBO); batch.setResourceTexture(ru::Texture::SurfaceGeometryDepth, linearDepthTexture); batch.setResourceTexture(ru::Texture::SurfaceGeometryNormal, normalTexture); diff --git a/libraries/render-utils/src/SurfaceGeometryPass.h b/libraries/render-utils/src/SurfaceGeometryPass.h index 5ada56521a..6ea03fbda5 100644 --- a/libraries/render-utils/src/SurfaceGeometryPass.h +++ b/libraries/render-utils/src/SurfaceGeometryPass.h @@ -35,7 +35,7 @@ public: gpu::TexturePointer getHalfNormalTexture(); // Update the depth buffer which will drive the allocation of all the other resources according to its size. - void update(const gpu::TexturePointer& depthBuffer, const gpu::TexturePointer& normalTexture); + void update(const gpu::TexturePointer& depthBuffer, const gpu::TexturePointer& normalTexture, bool isStereo); const glm::ivec2& getDepthFrameSize() const { return _frameSize; } void setResolutionLevel(int level) { _resolutionLevel = std::max(0, level); } @@ -59,6 +59,7 @@ protected: glm::ivec2 _frameSize; glm::ivec2 _halfFrameSize; int _resolutionLevel{ 0 }; + bool _isStereo{ false }; }; using LinearDepthFramebufferPointer = std::shared_ptr; diff --git a/libraries/render-utils/src/ssao.slh b/libraries/render-utils/src/ssao.slh index 56566c9b6f..3f4d361531 100644 --- a/libraries/render-utils/src/ssao.slh +++ b/libraries/render-utils/src/ssao.slh @@ -47,11 +47,32 @@ layout(binding=RENDER_UTILS_BUFFER_SSAO_FRAME_PARAMS) uniform ambientOcclusionFr float getPerspectiveScale() { return (params._resolutionInfo.z); } -int getResolutionLevel() { - +int getResolutionLevel() { return int(params._resolutionInfo.x); } +vec2 getNormalsSideSize() { + return params._sideSizes[0].xy; +} +int getNormalsResolutionLevel() { + return int(params._sideSizes[0].z); +} +int getDepthResolutionLevel() { + return int(params._sideSizes[0].w); +} +vec2 getOcclusionSideSize() { + return params._sideSizes[1].xy; +} +vec2 getOcclusionSplitSideSize() { + return params._sideSizes[1].zw; +} + +ivec2 getWidthHeightRoundUp(int resolutionLevel) { + ivec2 fullRes = ivec2(getWidthHeight(0)); + int resolutionDivisor = 1 << resolutionLevel; + return (fullRes + resolutionDivisor - 1) / resolutionDivisor; +} + float getRadius() { return params._radiusInfo.x; } @@ -205,6 +226,26 @@ layout(binding=RENDER_UTILS_TEXTURE_SSAO_NORMAL) uniform sampler2D normalTex; ivec2 getDepthTextureSize(int level) { return textureSize(depthPyramidTex, level); } +ivec2 getDepthTextureSideSize(int level) { + ivec2 size = getDepthTextureSize(level); + size.x >>= int(isStereo()) & 1; + return size; +} + +ivec2 getNormalTextureSize(int level) { + return textureSize(normalTex, level); +} +ivec2 getNormalTextureSideSize(int level) { + ivec2 size = getNormalTextureSize(level); + size.x >>= int(isStereo()) & 1; + return size; +} + +vec2 getStereoSideSizeRoundUp(int resolutionLevel) { + ivec2 fullRes = ivec2(getStereoSideSize(0)); + int resolutionDivisor = 1 << resolutionLevel; + return vec2((fullRes + resolutionDivisor - 1) / resolutionDivisor); +} float getZEyeAtPixel(ivec2 pixel, int level) { return -texelFetch(depthPyramidTex, pixel, level).x; @@ -254,8 +295,8 @@ vec3 getMinDelta(vec3 centralPoint, vec3 offsetPointPos, vec3 offsetPointNeg) { return dot(delta0, delta0) < dot(delta1, delta1) ? delta0 : delta1; } -vec3 buildNormal(ivec4 side, vec2 fragUVPos, ivec2 depthTexFragPixelPos, vec3 fragPosition, ivec2 depthTextureScale, vec2 sideImageSize) { - vec2 uvScale = vec2(1.0) / (sideImageSize * depthTextureScale); +vec3 buildNormal(ivec4 side, vec2 fragUVPos, ivec2 depthTexFragPixelPos, vec3 fragPosition, vec2 depthTextureSize) { + vec2 uvScale = vec2(1.0) / depthTextureSize; vec3 fragPositionDxPos = buildPosition(side, fragUVPos, depthTexFragPixelPos, ivec2(1, 0), uvScale); vec3 fragPositionDyPos = buildPosition(side, fragUVPos, depthTexFragPixelPos, ivec2(0, 1), uvScale); vec3 fragPositionDxNeg = buildPosition(side, fragUVPos, depthTexFragPixelPos, ivec2(-1, 0), uvScale); @@ -368,7 +409,7 @@ float computeHorizon(ivec4 side, vec2 shadedPixelPos, vec2 sideImageSize, vec2 d float evalVisibilityHBAO(ivec4 side, vec2 shadedPixelPos, vec2 sideImageSize, vec2 deltaTap, float diskPixelRadius, vec3 fragPositionES, vec3 fragNormalES) { vec2 searchVec = deltaTap * diskPixelRadius; - float obscurance; + float obscurance = 0.0; // Forward search for h1 obscurance = computeHorizon(side, shadedPixelPos, sideImageSize, deltaTap, fragPositionES, fragNormalES, searchVec, diskPixelRadius); @@ -376,7 +417,7 @@ float evalVisibilityHBAO(ivec4 side, vec2 shadedPixelPos, vec2 sideImageSize, ve // Backward search for h2 obscurance += computeHorizon(side, shadedPixelPos, sideImageSize, deltaTap, fragPositionES, fragNormalES, -searchVec, diskPixelRadius); - return obscurance * 0.5 / PI; + return obscurance; } <@endfunc@> diff --git a/libraries/render-utils/src/ssao_buildNormals.slf b/libraries/render-utils/src/ssao_buildNormals.slf index f3af324455..36066b87df 100644 --- a/libraries/render-utils/src/ssao_buildNormals.slf +++ b/libraries/render-utils/src/ssao_buildNormals.slf @@ -18,28 +18,30 @@ layout(location=0) out vec4 outFragColor; void main(void) { - vec2 sideImageSize = getStereoSideSize(getResolutionLevel()); - ivec2 renderSize = ivec2(sideImageSize) << ivec2(int(isStereo()) & 1, 0); - // Pixel being shaded vec2 fragCoord = gl_FragCoord.xy; ivec2 fragPixelPos = ivec2(fragCoord.xy); - // Fetch the z under the pixel (stereo or not) - ivec2 depthTextureScale = getDepthTextureSize(0) / renderSize; - ivec2 depthTexFragPixelPos = fragPixelPos * depthTextureScale; + // Fetch the z under the pixel (stereo or not) from full res depth + int depthTextureRatio = 1 << getNormalsResolutionLevel(); + ivec2 depthTexFragPixelPos = fragPixelPos * depthTextureRatio; float Zeye = getZEyeAtPixel(depthTexFragPixelPos, 0); - // Stereo side info - ivec4 side = getStereoSideInfo(fragPixelPos.x, getResolutionLevel()); + // Stereo side info based on the real viewport size of this pass + ivec2 sideNormalsSize = ivec2( getNormalsSideSize() ); + ivec4 side = getStereoSideInfoFromWidth(fragPixelPos.x, sideNormalsSize.x); // From now on, fragPixelPos is the pixel pos in the side + vec2 depthSideSize = getDepthTextureSideSize(0); + vec2 sideImageSize = depthSideSize / float(depthTextureRatio); fragPixelPos.x -= side.y; vec2 fragUVPos = (vec2(fragPixelPos) + vec2(0.5)) / sideImageSize; // The position and normal of the pixel fragment in Eye space vec3 fragPositionES = evalEyePositionFromZeye(side.x, Zeye, fragUVPos); - vec3 fragNormalES = buildNormal(side, fragUVPos, depthTexFragPixelPos, fragPositionES, depthTextureScale, sideImageSize); + vec3 fragNormalES = buildNormal(side, fragUVPos, depthTexFragPixelPos, fragPositionES, depthSideSize); + vec3 absFragNormalES = abs(fragNormalES); + fragNormalES /= max(absFragNormalES.z, max(absFragNormalES.x, absFragNormalES.y)); outFragColor = vec4(vec3(fragNormalES)*0.5 + vec3(0.5), 1.0); } diff --git a/libraries/render-utils/src/ssao_gather.slf b/libraries/render-utils/src/ssao_gather.slf index 8f2282154a..6814498b38 100644 --- a/libraries/render-utils/src/ssao_gather.slf +++ b/libraries/render-utils/src/ssao_gather.slf @@ -26,7 +26,7 @@ void main(void) { // result (at the resolution level, of course) ivec2 destPixelCoord = ivec2(gl_FragCoord.xy); ivec2 sourcePixelCoord = destPixelCoord / 2; - ivec2 splitImageSize = ivec2(getWidthHeight(getResolutionLevel()+1)); + ivec2 splitImageSize = getWidthHeightRoundUp(getResolutionLevel()+1); sourcePixelCoord += (destPixelCoord & ivec2(1)) * splitImageSize; diff --git a/libraries/render-utils/src/ssao_makeOcclusion.slf b/libraries/render-utils/src/ssao_makeOcclusion.slf index cee5ef6fe3..f4962d8286 100644 --- a/libraries/render-utils/src/ssao_makeOcclusion.slf +++ b/libraries/render-utils/src/ssao_makeOcclusion.slf @@ -24,36 +24,36 @@ layout(location=0) out vec4 outFragColor; void main(void) { - vec2 sideImageSize = getStereoSideSize(getResolutionLevel()); - ivec2 renderSize = ivec2(sideImageSize) << ivec2(int(isStereo()) & 1, 0); - // Pixel being shaded vec2 fragCoord = gl_FragCoord.xy; ivec2 fragPixelPos = ivec2(fragCoord.xy); #if SSAO_USE_QUAD_SPLIT - ivec2 splitImageSize = ivec2(getWidthHeight(getResolutionLevel()+1)); + ivec2 splitImageSize = ivec2(getOcclusionSplitSideSize()); fragPixelPos = ((fragPixelPos - getPixelOffset()*splitImageSize) * 2) + getPixelOffset(); #endif // Fetch the z under the pixel (stereo or not) - ivec2 depthTextureScale = getDepthTextureSize(0) / renderSize; - ivec2 depthTexFragPixelPos = fragPixelPos * depthTextureScale; + int depthTextureRatio = 1 << (getResolutionLevel() - getDepthResolutionLevel()); + ivec2 depthTexFragPixelPos = fragPixelPos * depthTextureRatio; float Zeye = getZEyeAtPixel(depthTexFragPixelPos, 0); #if SSAO_USE_QUAD_SPLIT vec3 fragNormalES = getNormalEyeAtPixel(fragPixelPos, 0); #endif - // Stereo side info - ivec4 side = getStereoSideInfo(fragPixelPos.x, getResolutionLevel()); + // Stereo side info based on the real viewport size of this pass + ivec2 sideOcclusionSize = ivec2( getOcclusionSideSize() ); + ivec4 side = getStereoSideInfoFromWidth(fragPixelPos.x, sideOcclusionSize.x); // From now on, fragPixelPos is the pixel pos in the side + vec2 depthSideSize = getDepthTextureSideSize(0); + vec2 sideImageSize = depthSideSize / float(depthTextureRatio); fragPixelPos.x -= side.y; vec2 fragUVPos = (vec2(fragPixelPos) + vec2(0.5)) / sideImageSize; // The position and normal of the pixel fragment in Eye space vec3 fragPositionES = evalEyePositionFromZeye(side.x, Zeye, fragUVPos); #if !SSAO_USE_QUAD_SPLIT - vec3 fragNormalES = buildNormal(side, fragUVPos, depthTexFragPixelPos, fragPositionES, depthTextureScale, sideImageSize); + vec3 fragNormalES = buildNormal(side, fragUVPos, depthTexFragPixelPos, fragPositionES, depthSideSize); #endif // Choose the screen-space sample radius @@ -68,20 +68,28 @@ void main(void) { // Accumulate the obscurance for each samples float obscuranceSum = 0.0; int numSamples = int(getNumSamples()); + float invNumSamples = getInvNumSamples(); + + // Steps are in the depth texture resolution + depthTexFragPixelPos = fragPixelPos * depthTextureRatio; for (int i = 0; i < numSamples; ++i) { #if SSAO_USE_HORIZON_BASED vec3 deltaTap = getUnitTapLocation(i, 1.0, randomPatternRotationAngle, PI); - obscuranceSum += evalVisibilityHBAO(side, vec2(fragPixelPos), sideImageSize, deltaTap.xy, diskPixelRadius, fragPositionES, fragNormalES); + obscuranceSum += evalVisibilityHBAO(side, vec2(depthTexFragPixelPos), depthSideSize, deltaTap.xy, diskPixelRadius, fragPositionES, fragNormalES); #else - vec3 tap = getTapLocationClampedSSAO(i, randomPatternRotationAngle, diskPixelRadius, fragPixelPos, sideImageSize); - vec2 tapPixelPos = vec2(fragPixelPos) + tap.xy; - vec3 tapUVZ = fetchTap(side, tapPixelPos, tap.z, sideImageSize); + vec3 tap = getTapLocationClampedSSAO(i, randomPatternRotationAngle, diskPixelRadius, depthTexFragPixelPos, depthSideSize); + vec2 tapPixelPos = vec2(depthTexFragPixelPos) + tap.xy; + vec3 tapUVZ = fetchTap(side, tapPixelPos, tap.z, depthSideSize); vec3 tapPositionES = evalEyePositionFromZeye(side.x, tapUVZ.z, tapUVZ.xy); obscuranceSum += float(tap.z > 0.0) * evalVisibilitySSAO(fragPositionES, fragNormalES, tapPositionES); #endif } - float occlusion = clamp(1.0 - obscuranceSum * getObscuranceScaling() * getInvNumSamples(), 0.0, 1.0); +#if SSAO_USE_HORIZON_BASED + obscuranceSum *= 0.5 / PI; +#endif + + float occlusion = clamp(1.0 - obscuranceSum * getObscuranceScaling() * invNumSamples, 0.0, 1.0); outFragColor = vec4(vec3(occlusion), 1.0); } diff --git a/libraries/render-utils/src/ssao_shared.h b/libraries/render-utils/src/ssao_shared.h index 89738fcea0..109d471964 100644 --- a/libraries/render-utils/src/ssao_shared.h +++ b/libraries/render-utils/src/ssao_shared.h @@ -40,6 +40,7 @@ struct AmbientOcclusionParams { SSAO_VEC4 _sampleInfo; SSAO_VEC4 _blurInfo; float _blurFilterTaps[SSAO_BLUR_GAUSSIAN_COEFS_COUNT]; + SSAO_VEC4 _sideSizes[2]; }; struct AmbientOcclusionFrameParams { diff --git a/libraries/shared/src/MathUtils.h b/libraries/shared/src/MathUtils.h new file mode 100644 index 0000000000..799890915e --- /dev/null +++ b/libraries/shared/src/MathUtils.h @@ -0,0 +1,21 @@ +// +// MathUtils.h +// libraries/shared/src +// +// Created by Olivier Prat on 9/21/18. +// Copyright 2018 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_MathUtils_h +#define hifi_MathUtils_h + +template +T divideRoundUp(const T& numerator, int divisor) { + return (numerator + divisor - T(1)) / divisor; +} + +#endif // hifi_MathUtils_h + From 2e40a5f3ffb4ce8e29b5d0663610233b568e7fdf Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Mon, 24 Sep 2018 14:38:03 +0200 Subject: [PATCH 038/286] Using UVs for buildNormals --- .../src/AmbientOcclusionEffect.cpp | 66 +++++++++++++------ .../render-utils/src/SurfaceGeometryPass.cpp | 39 +++++++---- libraries/render-utils/src/ssao.slh | 23 +++---- .../render-utils/src/ssao_buildNormals.slf | 21 +++--- .../render-utils/src/ssao_makeOcclusion.slf | 5 +- 5 files changed, 97 insertions(+), 57 deletions(-) diff --git a/libraries/render-utils/src/AmbientOcclusionEffect.cpp b/libraries/render-utils/src/AmbientOcclusionEffect.cpp index 8e2a25b3e6..5e6bc39253 100644 --- a/libraries/render-utils/src/AmbientOcclusionEffect.cpp +++ b/libraries/render-utils/src/AmbientOcclusionEffect.cpp @@ -109,8 +109,8 @@ void AmbientOcclusionFramebuffer::allocate() { } auto width = sideSize.x; auto height = sideSize.y; - - _normalTexture = gpu::Texture::createRenderBuffer(gpu::Element::COLOR_RGBA_32, width, height, gpu::Texture::SINGLE_MIP, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_POINT, gpu::Sampler::WRAP_CLAMP)); + auto format = gpu::Element::COLOR_RGBA_32; + _normalTexture = gpu::Texture::createRenderBuffer(format, width, height, gpu::Texture::SINGLE_MIP, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_POINT, gpu::Sampler::WRAP_CLAMP)); _normalFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("ssaoNormals")); _normalFramebuffer->setRenderBuffer(0, _normalTexture); } @@ -561,8 +561,8 @@ void AmbientOcclusionEffect::run(const render::RenderContextPointer& renderConte } // _frameId = (_frameId + 1) % SSAO_RANDOM_SAMPLE_COUNT; - gpu::doInBatch("AmbientOcclusionEffect::run", args->_context, [=](gpu::Batch& batch) { - PROFILE_RANGE_BATCH(batch, "AmbientOcclusion"); + gpu::doInBatch("AmbientOcclusionEffect::Depth mip creation", args->_context, [=](gpu::Batch& batch) { + PROFILE_RANGE_BATCH(batch, "AO::Depth mip creation"); batch.enableStereo(false); _gpuTimer->begin(batch); @@ -574,29 +574,57 @@ void AmbientOcclusionEffect::run(const render::RenderContextPointer& renderConte batch.setModelTransform(model); // We need this with the mips levels - batch.pushProfileRange("Depth mip creation"); #if SSAO_USE_HORIZON_BASED - batch.setPipeline(mipCreationPipeline); - batch.generateTextureMipsWithPipeline(occlusionDepthTexture); + batch.setPipeline(mipCreationPipeline); + batch.generateTextureMipsWithPipeline(occlusionDepthTexture); #else - batch.generateTextureMips(occlusionDepthTexture); + batch.generateTextureMips(occlusionDepthTexture); #endif - batch.popProfileRange(); + + _gpuTimer->end(batch); + }); #if SSAO_USE_QUAD_SPLIT + gpu::doInBatch("AmbientOcclusionEffect::Build Normals", args->_context, [=](gpu::Batch& batch) { + PROFILE_RANGE_BATCH(batch, "AO::Build Normals"); + batch.enableStereo(false); + + _gpuTimer->begin(batch); + + batch.resetViewTransform(); + + batch.setProjectionTransform(glm::mat4()); + Transform model; + auto depthTextureSize = linearDepthTexture->getDimensions(); + model.setScale( glm::vec3(occlusionViewport.z / (depthTextureSize.x * resolutionScale), occlusionViewport.w / (depthTextureSize.y * resolutionScale), 1.0f) ); + batch.setModelTransform(model); + // Build face normals pass - batch.pushProfileRange("Build Normals"); - batch.setViewportTransform(occlusionViewport); - batch.setPipeline(buildNormalsPipeline); - batch.setResourceTexture(render_utils::slot::texture::SsaoDepth, linearDepthTexture); - batch.setResourceTexture(render_utils::slot::texture::SsaoNormal, nullptr); - batch.setUniformBuffer(render_utils::slot::buffer::DeferredFrameTransform, frameTransform->getFrameTransformBuffer()); - batch.setUniformBuffer(render_utils::slot::buffer::SsaoParams, _aoParametersBuffer); - batch.setFramebuffer(occlusionNormalFramebuffer); - batch.draw(gpu::TRIANGLE_STRIP, 4); - batch.popProfileRange(); + batch.setViewportTransform(occlusionViewport); + batch.setPipeline(buildNormalsPipeline); + batch.setResourceTexture(render_utils::slot::texture::SsaoDepth, linearDepthTexture); + batch.setResourceTexture(render_utils::slot::texture::SsaoNormal, nullptr); + batch.setUniformBuffer(render_utils::slot::buffer::DeferredFrameTransform, frameTransform->getFrameTransformBuffer()); + batch.setUniformBuffer(render_utils::slot::buffer::SsaoParams, _aoParametersBuffer); + batch.setFramebuffer(occlusionNormalFramebuffer); + batch.draw(gpu::TRIANGLE_STRIP, 4); + + _gpuTimer->end(batch); + }); #endif + gpu::doInBatch("AmbientOcclusionEffect::Occlusion", args->_context, [=](gpu::Batch& batch) { + PROFILE_RANGE_BATCH(batch, "AO::Occlusion"); + batch.enableStereo(false); + + _gpuTimer->begin(batch); + + batch.resetViewTransform(); + + Transform model; + batch.setProjectionTransform(glm::mat4()); + batch.setModelTransform(model); + // Occlusion pass batch.pushProfileRange("Occlusion"); diff --git a/libraries/render-utils/src/SurfaceGeometryPass.cpp b/libraries/render-utils/src/SurfaceGeometryPass.cpp index 8f337b66da..66ac3669a4 100644 --- a/libraries/render-utils/src/SurfaceGeometryPass.cpp +++ b/libraries/render-utils/src/SurfaceGeometryPass.cpp @@ -73,19 +73,20 @@ void LinearDepthFramebuffer::allocate() { auto height = _frameSize.y; // For Linear Depth: - const uint16_t LINEAR_DEPTH_MAX_MIP_LEVEL = 5; - // Point sampling of the depth is needed for the AmbientOcclusionEffect in HBAO, as well as the clamp to edge - const auto depthSampler = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_POINT, gpu::Sampler::WRAP_CLAMP); + const uint16_t LINEAR_DEPTH_MAX_MIP_LEVEL = 1; + const auto depthSamplerFull = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR_MIP_POINT, gpu::Sampler::WRAP_CLAMP); _linearDepthTexture = gpu::Texture::createRenderBuffer(gpu::Element(gpu::SCALAR, gpu::FLOAT, gpu::RED), width, height, LINEAR_DEPTH_MAX_MIP_LEVEL, - depthSampler); + depthSamplerFull); _linearDepthFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("linearDepth")); _linearDepthFramebuffer->setRenderBuffer(0, _linearDepthTexture); _linearDepthFramebuffer->setDepthStencilBuffer(_primaryDepthTexture, _primaryDepthTexture->getTexelFormat()); // For Downsampling: - const uint16_t HALF_LINEAR_DEPTH_MAX_MIP_LEVEL = LINEAR_DEPTH_MAX_MIP_LEVEL; + const uint16_t HALF_LINEAR_DEPTH_MAX_MIP_LEVEL = 5; + // Point sampling of the depth, as well as the clamp to edge, are needed for the AmbientOcclusionEffect with HBAO + const auto depthSamplerHalf = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_POINT, gpu::Sampler::WRAP_CLAMP); _halfLinearDepthTexture = gpu::Texture::createRenderBuffer(gpu::Element(gpu::SCALAR, gpu::FLOAT, gpu::RED), _halfFrameSize.x, _halfFrameSize.y, HALF_LINEAR_DEPTH_MAX_MIP_LEVEL, - depthSampler); + depthSamplerHalf); _halfNormalTexture = gpu::Texture::createRenderBuffer(gpu::Element::COLOR_RGBA_32, _halfFrameSize.x, _halfFrameSize.y, gpu::Texture::SINGLE_MIP, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR_MIP_POINT)); @@ -183,35 +184,47 @@ void LinearDepthPass::run(const render::RenderContextPointer& renderContext, con auto halfViewport = divideRoundUp(depthViewport, 2); float clearLinearDepth = args->getViewFrustum().getFarClip() * 2.0f; - gpu::doInBatch("LinearDepthPass::run", args->_context, [=](gpu::Batch& batch) { + gpu::doInBatch("LinearDepthPass::ZtoLinearDepth", args->_context, [=](gpu::Batch& batch) { + PROFILE_RANGE_BATCH(batch, "ZtoLinearDepth"); _gpuTimer->begin(batch); batch.enableStereo(false); - batch.setViewportTransform(depthViewport); batch.setProjectionTransform(glm::mat4()); batch.resetViewTransform(); - batch.setModelTransform(gpu::Framebuffer::evalSubregionTexcoordTransform(_linearDepthFramebuffer->getDepthFrameSize(), depthViewport)); batch.setUniformBuffer(ru::Buffer::DeferredFrameTransform, frameTransform->getFrameTransformBuffer()); // LinearDepth + batch.setViewportTransform(depthViewport); batch.setFramebuffer(linearDepthFBO); batch.clearColorFramebuffer(gpu::Framebuffer::BUFFER_COLOR0, glm::vec4(clearLinearDepth, 0.0f, 0.0f, 0.0f)); batch.setPipeline(linearDepthPipeline); + batch.setModelTransform(gpu::Framebuffer::evalSubregionTexcoordTransform(_linearDepthFramebuffer->getDepthFrameSize(), depthViewport)); batch.setResourceTexture(ru::Texture::SurfaceGeometryDepth, depthBuffer); batch.draw(gpu::TRIANGLE_STRIP, 4); + _gpuTimer->end(batch); + }); + + gpu::doInBatch("LinearDepthPass::halfDepth", args->_context, [=](gpu::Batch& batch) { + PROFILE_RANGE_BATCH(batch, "LinearDepthDownsample"); + _gpuTimer->begin(batch); + batch.enableStereo(false); + + batch.setProjectionTransform(glm::mat4()); + batch.resetViewTransform(); + + batch.setUniformBuffer(ru::Buffer::DeferredFrameTransform, frameTransform->getFrameTransformBuffer()); + // Downsample batch.setViewportTransform(halfViewport); - Transform model; - model.setScale( glm::vec3((depthViewport.z >> 1) / float(halfViewport.z), (depthViewport.w >> 1) / float(halfViewport.w), 1.0f) ); - batch.setModelTransform(model); batch.setFramebuffer(downsampleFBO); batch.setResourceTexture(ru::Texture::SurfaceGeometryDepth, linearDepthTexture); batch.setResourceTexture(ru::Texture::SurfaceGeometryNormal, normalTexture); batch.setPipeline(downsamplePipeline); + batch.setModelTransform(gpu::Framebuffer::evalSubregionTexcoordTransform(_linearDepthFramebuffer->getDepthFrameSize() >> 1, halfViewport)); batch.draw(gpu::TRIANGLE_STRIP, 4); - + _gpuTimer->end(batch); }); diff --git a/libraries/render-utils/src/ssao.slh b/libraries/render-utils/src/ssao.slh index 3f4d361531..f8bba54d6b 100644 --- a/libraries/render-utils/src/ssao.slh +++ b/libraries/render-utils/src/ssao.slh @@ -282,25 +282,26 @@ vec4 fetchTap(ivec4 side, vec2 tapSidePixelPos, float tapRadius, vec2 sideImageS return P; } -vec3 buildPosition(ivec4 side, vec2 fragUVPos, ivec2 depthTexFragPixelPos, ivec2 deltaDepthTexPixel, vec2 uvScale) { - depthTexFragPixelPos += deltaDepthTexPixel; - fragUVPos += deltaDepthTexPixel * uvScale; - float Zeye = getZEyeAtPixel(depthTexFragPixelPos, 0); +vec3 buildPosition(ivec4 side, vec2 fragUVPos) { + vec2 fetchUV = clamp(fragUVPos, vec2(0), vec2(1)); + fetchUV = mix(fetchUV, vec2((fetchUV.x + getStereoSide(side)) * 0.5, fetchUV.y), isStereo()); + float Zeye = getZEyeAtUV(fetchUV, 0); return evalEyePositionFromZeye(side.x, Zeye, fragUVPos); } vec3 getMinDelta(vec3 centralPoint, vec3 offsetPointPos, vec3 offsetPointNeg) { vec3 delta0 = offsetPointPos - centralPoint; vec3 delta1 = centralPoint - offsetPointNeg; - return dot(delta0, delta0) < dot(delta1, delta1) ? delta0 : delta1; + float sqrLength0 = dot(delta0, delta0); + float sqrLength1 = dot(delta1, delta1); + return sqrLength0 < sqrLength1 ? delta0 : delta1; } -vec3 buildNormal(ivec4 side, vec2 fragUVPos, ivec2 depthTexFragPixelPos, vec3 fragPosition, vec2 depthTextureSize) { - vec2 uvScale = vec2(1.0) / depthTextureSize; - vec3 fragPositionDxPos = buildPosition(side, fragUVPos, depthTexFragPixelPos, ivec2(1, 0), uvScale); - vec3 fragPositionDyPos = buildPosition(side, fragUVPos, depthTexFragPixelPos, ivec2(0, 1), uvScale); - vec3 fragPositionDxNeg = buildPosition(side, fragUVPos, depthTexFragPixelPos, ivec2(-1, 0), uvScale); - vec3 fragPositionDyNeg = buildPosition(side, fragUVPos, depthTexFragPixelPos, ivec2(0, -1), uvScale); +vec3 buildNormal(ivec4 side, vec2 fragUVPos, vec3 fragPosition, vec2 deltaDepthUV) { + vec3 fragPositionDxPos = buildPosition(side, fragUVPos + vec2(deltaDepthUV.x, 0)); + vec3 fragPositionDyPos = buildPosition(side, fragUVPos + vec2(0, deltaDepthUV.y)); + vec3 fragPositionDxNeg = buildPosition(side, fragUVPos - vec2(deltaDepthUV.x, 0)); + vec3 fragPositionDyNeg = buildPosition(side, fragUVPos - vec2(0, deltaDepthUV.y)); vec3 fragPositionDx = getMinDelta(fragPosition, fragPositionDxPos, fragPositionDxNeg); vec3 fragPositionDy = getMinDelta(fragPosition, fragPositionDyPos, fragPositionDyNeg); diff --git a/libraries/render-utils/src/ssao_buildNormals.slf b/libraries/render-utils/src/ssao_buildNormals.slf index 36066b87df..09502f2026 100644 --- a/libraries/render-utils/src/ssao_buildNormals.slf +++ b/libraries/render-utils/src/ssao_buildNormals.slf @@ -15,31 +15,28 @@ <$declareAmbientOcclusion()$> <$declareFetchDepthPyramidMap()$> +layout(location=0) in vec2 varTexCoord0; + layout(location=0) out vec4 outFragColor; void main(void) { // Pixel being shaded vec2 fragCoord = gl_FragCoord.xy; ivec2 fragPixelPos = ivec2(fragCoord.xy); - - // Fetch the z under the pixel (stereo or not) from full res depth - int depthTextureRatio = 1 << getNormalsResolutionLevel(); - ivec2 depthTexFragPixelPos = fragPixelPos * depthTextureRatio; - float Zeye = getZEyeAtPixel(depthTexFragPixelPos, 0); + vec2 fragUVPos = varTexCoord0; // Stereo side info based on the real viewport size of this pass ivec2 sideNormalsSize = ivec2( getNormalsSideSize() ); ivec4 side = getStereoSideInfoFromWidth(fragPixelPos.x, sideNormalsSize.x); - // From now on, fragPixelPos is the pixel pos in the side - vec2 depthSideSize = getDepthTextureSideSize(0); - vec2 sideImageSize = depthSideSize / float(depthTextureRatio); - fragPixelPos.x -= side.y; - vec2 fragUVPos = (vec2(fragPixelPos) + vec2(0.5)) / sideImageSize; + // From now on, fragUVPos is the UV pos in the side + fragUVPos.x = mix(fragUVPos.x, fragUVPos.x - getStereoSide(side) * 0.5, isStereo()); + + vec2 deltaDepthUV = vec2(1.0) / vec2(getDepthTextureSideSize(0)); // The position and normal of the pixel fragment in Eye space - vec3 fragPositionES = evalEyePositionFromZeye(side.x, Zeye, fragUVPos); - vec3 fragNormalES = buildNormal(side, fragUVPos, depthTexFragPixelPos, fragPositionES, depthSideSize); + vec3 fragPositionES = buildPosition(side, fragUVPos); + vec3 fragNormalES = buildNormal(side, fragUVPos, fragPositionES, deltaDepthUV); vec3 absFragNormalES = abs(fragNormalES); fragNormalES /= max(absFragNormalES.z, max(absFragNormalES.x, absFragNormalES.y)); diff --git a/libraries/render-utils/src/ssao_makeOcclusion.slf b/libraries/render-utils/src/ssao_makeOcclusion.slf index f4962d8286..510ca4ea7b 100644 --- a/libraries/render-utils/src/ssao_makeOcclusion.slf +++ b/libraries/render-utils/src/ssao_makeOcclusion.slf @@ -51,9 +51,10 @@ void main(void) { vec2 fragUVPos = (vec2(fragPixelPos) + vec2(0.5)) / sideImageSize; // The position and normal of the pixel fragment in Eye space - vec3 fragPositionES = evalEyePositionFromZeye(side.x, Zeye, fragUVPos); + vec3 fragPositionES = buildPosition(side, fragUVPos); #if !SSAO_USE_QUAD_SPLIT - vec3 fragNormalES = buildNormal(side, fragUVPos, depthTexFragPixelPos, fragPositionES, depthSideSize); + vec2 deltaDepthUV = vec2(1.0) / depthSideSize; + vec3 fragNormalES = buildNormal(side, fragUVPos, fragPositionES, deltaDepthUV); #endif // Choose the screen-space sample radius From 6704a27b122c383218d459cfceadcff89a0d2120 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Mon, 24 Sep 2018 14:59:57 +0200 Subject: [PATCH 039/286] Back to one batch --- .../src/AmbientOcclusionEffect.cpp | 96 +++++++------------ .../render-utils/src/SurfaceGeometryPass.cpp | 18 +--- 2 files changed, 39 insertions(+), 75 deletions(-) diff --git a/libraries/render-utils/src/AmbientOcclusionEffect.cpp b/libraries/render-utils/src/AmbientOcclusionEffect.cpp index 5e6bc39253..c906809b38 100644 --- a/libraries/render-utils/src/AmbientOcclusionEffect.cpp +++ b/libraries/render-utils/src/AmbientOcclusionEffect.cpp @@ -561,8 +561,8 @@ void AmbientOcclusionEffect::run(const render::RenderContextPointer& renderConte } // _frameId = (_frameId + 1) % SSAO_RANDOM_SAMPLE_COUNT; - gpu::doInBatch("AmbientOcclusionEffect::Depth mip creation", args->_context, [=](gpu::Batch& batch) { - PROFILE_RANGE_BATCH(batch, "AO::Depth mip creation"); + gpu::doInBatch("AmbientOcclusionEffect::run", args->_context, [=](gpu::Batch& batch) { + PROFILE_RANGE_BATCH(batch, "AmbientOcclusion"); batch.enableStereo(false); _gpuTimer->begin(batch); @@ -573,28 +573,18 @@ void AmbientOcclusionEffect::run(const render::RenderContextPointer& renderConte batch.setProjectionTransform(glm::mat4()); batch.setModelTransform(model); - // We need this with the mips levels + batch.pushProfileRange("Depth Mip Generation"); + // We need this with the mips levels #if SSAO_USE_HORIZON_BASED batch.setPipeline(mipCreationPipeline); batch.generateTextureMipsWithPipeline(occlusionDepthTexture); #else batch.generateTextureMips(occlusionDepthTexture); #endif - - _gpuTimer->end(batch); - }); + batch.popProfileRange(); #if SSAO_USE_QUAD_SPLIT - gpu::doInBatch("AmbientOcclusionEffect::Build Normals", args->_context, [=](gpu::Batch& batch) { - PROFILE_RANGE_BATCH(batch, "AO::Build Normals"); - batch.enableStereo(false); - - _gpuTimer->begin(batch); - - batch.resetViewTransform(); - - batch.setProjectionTransform(glm::mat4()); - Transform model; + batch.pushProfileRange("Normal Generation"); auto depthTextureSize = linearDepthTexture->getDimensions(); model.setScale( glm::vec3(occlusionViewport.z / (depthTextureSize.x * resolutionScale), occlusionViewport.w / (depthTextureSize.y * resolutionScale), 1.0f) ); batch.setModelTransform(model); @@ -608,63 +598,49 @@ void AmbientOcclusionEffect::run(const render::RenderContextPointer& renderConte batch.setUniformBuffer(render_utils::slot::buffer::SsaoParams, _aoParametersBuffer); batch.setFramebuffer(occlusionNormalFramebuffer); batch.draw(gpu::TRIANGLE_STRIP, 4); - - _gpuTimer->end(batch); - }); + batch.popProfileRange(); #endif - gpu::doInBatch("AmbientOcclusionEffect::Occlusion", args->_context, [=](gpu::Batch& batch) { - PROFILE_RANGE_BATCH(batch, "AO::Occlusion"); - batch.enableStereo(false); - - _gpuTimer->begin(batch); - - batch.resetViewTransform(); - - Transform model; - batch.setProjectionTransform(glm::mat4()); - batch.setModelTransform(model); - // Occlusion pass batch.pushProfileRange("Occlusion"); - batch.setUniformBuffer(render_utils::slot::buffer::DeferredFrameTransform, frameTransform->getFrameTransformBuffer()); - batch.setUniformBuffer(render_utils::slot::buffer::SsaoParams, _aoParametersBuffer); + batch.setUniformBuffer(render_utils::slot::buffer::DeferredFrameTransform, frameTransform->getFrameTransformBuffer()); + batch.setUniformBuffer(render_utils::slot::buffer::SsaoParams, _aoParametersBuffer); #if SSAO_USE_QUAD_SPLIT - batch.setFramebuffer(occlusionBlurredFBO); + batch.setFramebuffer(occlusionBlurredFBO); #else - batch.setFramebuffer(occlusionFBO); + batch.setFramebuffer(occlusionFBO); #endif - batch.setPipeline(occlusionPipeline); - batch.setResourceTexture(render_utils::slot::texture::SsaoDepth, occlusionDepthTexture); + batch.setPipeline(occlusionPipeline); + batch.setResourceTexture(render_utils::slot::texture::SsaoDepth, occlusionDepthTexture); #if SSAO_USE_QUAD_SPLIT - batch.setResourceTexture(render_utils::slot::texture::SsaoNormal, occlusionNormalTexture); - { - auto splitViewport = occlusionViewport >> SSAO_USE_QUAD_SPLIT; + batch.setResourceTexture(render_utils::slot::texture::SsaoNormal, occlusionNormalTexture); + { + auto splitViewport = occlusionViewport >> SSAO_USE_QUAD_SPLIT; - batch.setViewportTransform(splitViewport); - batch.setUniformBuffer(render_utils::slot::buffer::SsaoFrameParams, _aoFrameParametersBuffer[0]); - batch.draw(gpu::TRIANGLE_STRIP, 4); - - splitViewport.x += splitViewport.z; - batch.setViewportTransform(splitViewport); - batch.setUniformBuffer(render_utils::slot::buffer::SsaoFrameParams, _aoFrameParametersBuffer[1]); - batch.draw(gpu::TRIANGLE_STRIP, 4); - - splitViewport.y += splitViewport.w; - batch.setViewportTransform(splitViewport); - batch.setUniformBuffer(render_utils::slot::buffer::SsaoFrameParams, _aoFrameParametersBuffer[2]); - batch.draw(gpu::TRIANGLE_STRIP, 4); - - splitViewport.x = 0; - batch.setViewportTransform(splitViewport); - batch.setUniformBuffer(render_utils::slot::buffer::SsaoFrameParams, _aoFrameParametersBuffer[3]); - batch.draw(gpu::TRIANGLE_STRIP, 4); - } -#else + batch.setViewportTransform(splitViewport); batch.setUniformBuffer(render_utils::slot::buffer::SsaoFrameParams, _aoFrameParametersBuffer[0]); batch.draw(gpu::TRIANGLE_STRIP, 4); + + splitViewport.x += splitViewport.z; + batch.setViewportTransform(splitViewport); + batch.setUniformBuffer(render_utils::slot::buffer::SsaoFrameParams, _aoFrameParametersBuffer[1]); + batch.draw(gpu::TRIANGLE_STRIP, 4); + + splitViewport.y += splitViewport.w; + batch.setViewportTransform(splitViewport); + batch.setUniformBuffer(render_utils::slot::buffer::SsaoFrameParams, _aoFrameParametersBuffer[2]); + batch.draw(gpu::TRIANGLE_STRIP, 4); + + splitViewport.x = 0; + batch.setViewportTransform(splitViewport); + batch.setUniformBuffer(render_utils::slot::buffer::SsaoFrameParams, _aoFrameParametersBuffer[3]); + batch.draw(gpu::TRIANGLE_STRIP, 4); + } +#else + batch.setUniformBuffer(render_utils::slot::buffer::SsaoFrameParams, _aoFrameParametersBuffer[0]); + batch.draw(gpu::TRIANGLE_STRIP, 4); #endif batch.popProfileRange(); diff --git a/libraries/render-utils/src/SurfaceGeometryPass.cpp b/libraries/render-utils/src/SurfaceGeometryPass.cpp index 66ac3669a4..d37c468cbb 100644 --- a/libraries/render-utils/src/SurfaceGeometryPass.cpp +++ b/libraries/render-utils/src/SurfaceGeometryPass.cpp @@ -184,9 +184,10 @@ void LinearDepthPass::run(const render::RenderContextPointer& renderContext, con auto halfViewport = divideRoundUp(depthViewport, 2); float clearLinearDepth = args->getViewFrustum().getFarClip() * 2.0f; - gpu::doInBatch("LinearDepthPass::ZtoLinearDepth", args->_context, [=](gpu::Batch& batch) { - PROFILE_RANGE_BATCH(batch, "ZtoLinearDepth"); + gpu::doInBatch("LinearDepthPass::run", args->_context, [=](gpu::Batch& batch) { + PROFILE_RANGE_BATCH(batch, "LinearDepthPass"); _gpuTimer->begin(batch); + batch.enableStereo(false); batch.setProjectionTransform(glm::mat4()); @@ -203,19 +204,6 @@ void LinearDepthPass::run(const render::RenderContextPointer& renderContext, con batch.setResourceTexture(ru::Texture::SurfaceGeometryDepth, depthBuffer); batch.draw(gpu::TRIANGLE_STRIP, 4); - _gpuTimer->end(batch); - }); - - gpu::doInBatch("LinearDepthPass::halfDepth", args->_context, [=](gpu::Batch& batch) { - PROFILE_RANGE_BATCH(batch, "LinearDepthDownsample"); - _gpuTimer->begin(batch); - batch.enableStereo(false); - - batch.setProjectionTransform(glm::mat4()); - batch.resetViewTransform(); - - batch.setUniformBuffer(ru::Buffer::DeferredFrameTransform, frameTransform->getFrameTransformBuffer()); - // Downsample batch.setViewportTransform(halfViewport); batch.setFramebuffer(downsampleFBO); From 9cebdbd507eab997afd54a850988642dced1955a Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Mon, 24 Sep 2018 19:08:37 +0200 Subject: [PATCH 040/286] Separate frame buffers for each AO split --- .../src/AmbientOcclusionEffect.cpp | 118 ++++++++++++++---- .../render-utils/src/AmbientOcclusionEffect.h | 15 ++- libraries/render-utils/src/ssao.slh | 61 ++++----- libraries/render-utils/src/ssao_gather.slf | 11 +- .../render-utils/src/ssao_makeOcclusion.slf | 43 +++---- libraries/render-utils/src/ssao_shared.h | 1 - 6 files changed, 153 insertions(+), 96 deletions(-) diff --git a/libraries/render-utils/src/AmbientOcclusionEffect.cpp b/libraries/render-utils/src/AmbientOcclusionEffect.cpp index c906809b38..bfa5ae79e5 100644 --- a/libraries/render-utils/src/AmbientOcclusionEffect.cpp +++ b/libraries/render-utils/src/AmbientOcclusionEffect.cpp @@ -103,7 +103,7 @@ void AmbientOcclusionFramebuffer::allocate() { if (_isStereo) { sideSize.x >>= 1; } - sideSize = divideRoundUp(sideSize, 1 << _resolutionLevel); + sideSize = divideRoundUp(sideSize, 1 << std::min(1, _resolutionLevel)); if (_isStereo) { sideSize.x <<= 1; } @@ -114,8 +114,48 @@ void AmbientOcclusionFramebuffer::allocate() { _normalFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("ssaoNormals")); _normalFramebuffer->setRenderBuffer(0, _normalTexture); } + +#if SSAO_USE_QUAD_SPLIT + { + auto splitSize = glm::ivec2(_normalFramebuffer->getSize()); + if (_isStereo) { + splitSize.x >>= 1; + } + splitSize = divideRoundUp(splitSize, 2); + if (_isStereo) { + splitSize.x <<= 1; + } + auto width = splitSize.x; + auto height = splitSize.y; + auto format = gpu::Element::COLOR_R_8; + + _occlusionSplitTexture = gpu::Texture::createRenderBufferArray(format, width, height, SSAO_SPLIT_COUNT, gpu::Texture::SINGLE_MIP, + gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR, gpu::Sampler::WRAP_CLAMP)); + for (int i = 0; i < SSAO_SPLIT_COUNT; i++) { + _occlusionSplitFramebuffers[i] = gpu::FramebufferPointer(gpu::Framebuffer::create("occlusion")); + _occlusionSplitFramebuffers[i]->setRenderBuffer(0, _occlusionSplitTexture, i); + } + } +#endif } +#if SSAO_USE_QUAD_SPLIT +gpu::FramebufferPointer AmbientOcclusionFramebuffer::getOcclusionSplitFramebuffer(int index) { + assert(index < SSAO_SPLIT_COUNT); + if (!_occlusionSplitFramebuffers[index]) { + allocate(); + } + return _occlusionSplitFramebuffers[index]; +} + +gpu::TexturePointer AmbientOcclusionFramebuffer::getOcclusionSplitTexture() { + if (!_occlusionSplitTexture) { + allocate(); + } + return _occlusionSplitTexture; +} +#endif + gpu::FramebufferPointer AmbientOcclusionFramebuffer::getOcclusionFramebuffer() { if (!_occlusionFramebuffer) { allocate(); @@ -340,13 +380,6 @@ void AmbientOcclusionEffect::configure(const Config& config) { auto& current = _aoParametersBuffer.edit()._resolutionInfo; current.x = (float)config.resolutionLevel; shouldUpdateBlurs = true; - - _aoFrameParametersBuffer[0].edit()._pixelOffsets = { 0, 0, 0, 0 }; -#if SSAO_USE_QUAD_SPLIT - _aoFrameParametersBuffer[1].edit()._pixelOffsets = { 1, 0, 0, 0 }; - _aoFrameParametersBuffer[2].edit()._pixelOffsets = { 1, 1, 0, 0 }; - _aoFrameParametersBuffer[3].edit()._pixelOffsets = { 0, 1, 0, 0 }; -#endif } if (config.blurRadius != _aoParametersBuffer.get().getBlurRadius()) { @@ -395,13 +428,19 @@ void AmbientOcclusionEffect::updateFramebufferSizes() { const int widthScale = _framebuffer->isStereo() & 1; auto sourceFrameSize = _framebuffer->getSourceFrameSize(); const int resolutionLevel = _aoParametersBuffer.get().getResolutionLevel(); + const float resolutionScale = powf(0.5f, resolutionLevel); // Depth is at maximum half depth const int depthResolutionLevel = std::min(1, resolutionLevel); + const float depthResolutionScale = powf(2.0f, depthResolutionLevel); + auto normalTextureSize = _framebuffer->getNormalTexture()->getDimensions(); + auto occlusionDepthFrameSize = divideRoundUp(sourceFrameSize, depthResolutionLevel); sourceFrameSize.x >>= widthScale; + normalTextureSize.x >>= widthScale; + occlusionDepthFrameSize.x >>= widthScale; - params._sideSizes[0].x = _framebuffer->getNormalTexture()->getWidth() >> widthScale; - params._sideSizes[0].y = _framebuffer->getNormalTexture()->getHeight(); + params._sideSizes[0].x = normalTextureSize.x; + params._sideSizes[0].y = normalTextureSize.y; params._sideSizes[0].z = resolutionLevel; params._sideSizes[0].w = depthResolutionLevel; @@ -512,7 +551,11 @@ void AmbientOcclusionEffect::run(const render::RenderContextPointer& renderConte auto occlusionViewport = divideRoundUp(sourceViewport, 1 << resolutionLevel); auto firstBlurViewport = sourceViewport; #if SSAO_USE_QUAD_SPLIT - auto splitViewport = divideRoundUp(sourceViewport, 1 << (resolutionLevel + SSAO_USE_QUAD_SPLIT)); + auto splitViewport = glm::ivec4{ 0, 0, _framebuffer->getOcclusionSplitTexture()->getWidth(), _framebuffer->getOcclusionSplitTexture()->getHeight() }; + auto depthSideSize = glm::ivec2(linearDepthTexture->getDimensions()); + if (args->isStereo()) { + depthSideSize.x >>= 1; + } #endif firstBlurViewport.w = divideRoundUp(firstBlurViewport.w, 1 << resolutionLevel); @@ -551,7 +594,15 @@ void AmbientOcclusionEffect::run(const render::RenderContextPointer& renderConte auto buildNormalsPipeline = getBuildNormalsPipeline(); auto occlusionNormalFramebuffer = _framebuffer->getNormalFramebuffer(); auto occlusionNormalTexture = _framebuffer->getNormalTexture(); + auto normalViewport = glm::ivec4{ 0, 0, occlusionNormalFramebuffer->getWidth(), occlusionNormalFramebuffer->getHeight() }; #endif + auto occlusionDepthSideSize = glm::ivec2(occlusionDepthTexture->getDimensions()); + if (args->isStereo()) { + occlusionDepthSideSize.x >>= 1; + } + + const auto depthResolutionLevel = std::min(1, resolutionLevel); + const auto depthResolutionScale = powf(2.0f, depthResolutionLevel); // Update sample rotation const int SSAO_RANDOM_SAMPLE_COUNT = int(_randomSamples.size() / SSAO_SPLIT_COUNT); @@ -585,12 +636,11 @@ void AmbientOcclusionEffect::run(const render::RenderContextPointer& renderConte #if SSAO_USE_QUAD_SPLIT batch.pushProfileRange("Normal Generation"); - auto depthTextureSize = linearDepthTexture->getDimensions(); - model.setScale( glm::vec3(occlusionViewport.z / (depthTextureSize.x * resolutionScale), occlusionViewport.w / (depthTextureSize.y * resolutionScale), 1.0f) ); + model.setScale( glm::vec3((normalViewport.z * depthResolutionScale) / depthSideSize.x, (normalViewport.w * depthResolutionScale) / depthSideSize.y, 1.0f) ); batch.setModelTransform(model); // Build face normals pass - batch.setViewportTransform(occlusionViewport); + batch.setViewportTransform(normalViewport); batch.setPipeline(buildNormalsPipeline); batch.setResourceTexture(render_utils::slot::texture::SsaoDepth, linearDepthTexture); batch.setResourceTexture(render_utils::slot::texture::SsaoNormal, nullptr); @@ -617,28 +667,43 @@ void AmbientOcclusionEffect::run(const render::RenderContextPointer& renderConte #if SSAO_USE_QUAD_SPLIT batch.setResourceTexture(render_utils::slot::texture::SsaoNormal, occlusionNormalTexture); { - auto splitViewport = occlusionViewport >> SSAO_USE_QUAD_SPLIT; + const auto scale = glm::vec3( + (splitViewport.z * 2.0f) / occlusionDepthSideSize.x, + (splitViewport.w * 2.0f) / occlusionDepthSideSize.y, + 1.0f); + const auto pixelUV = glm::vec2(1.0f) / (glm::vec2(occlusionDepthSideSize) * glm::vec2(scale.x, scale.y)); batch.setViewportTransform(splitViewport); + + model.setScale(scale); + model.setTranslation(glm::vec3(0.0f, 0.0f, 0.0f)); + batch.setModelTransform(model); + batch.setFramebuffer(_framebuffer->getOcclusionSplitFramebuffer(0)); batch.setUniformBuffer(render_utils::slot::buffer::SsaoFrameParams, _aoFrameParametersBuffer[0]); batch.draw(gpu::TRIANGLE_STRIP, 4); - splitViewport.x += splitViewport.z; - batch.setViewportTransform(splitViewport); + model.setTranslation(glm::vec3(pixelUV.x, 0.0f, 0.0f)); + batch.setFramebuffer(_framebuffer->getOcclusionSplitFramebuffer(1)); batch.setUniformBuffer(render_utils::slot::buffer::SsaoFrameParams, _aoFrameParametersBuffer[1]); batch.draw(gpu::TRIANGLE_STRIP, 4); - splitViewport.y += splitViewport.w; - batch.setViewportTransform(splitViewport); + model.setTranslation(glm::vec3(pixelUV.x, pixelUV.y, 0.0f)); + batch.setFramebuffer(_framebuffer->getOcclusionSplitFramebuffer(3)); batch.setUniformBuffer(render_utils::slot::buffer::SsaoFrameParams, _aoFrameParametersBuffer[2]); batch.draw(gpu::TRIANGLE_STRIP, 4); - splitViewport.x = 0; - batch.setViewportTransform(splitViewport); + model.setTranslation(glm::vec3(0.0f, pixelUV.y, 0.0f)); + batch.setFramebuffer(_framebuffer->getOcclusionSplitFramebuffer(2)); batch.setUniformBuffer(render_utils::slot::buffer::SsaoFrameParams, _aoFrameParametersBuffer[3]); batch.draw(gpu::TRIANGLE_STRIP, 4); } #else + const auto scale = glm::vec3( + (occlusionViewport.z / (depthSideSize.x * resolutionScale)) * fullDepthToHalfDepthSizeRatio.x, + (occlusionViewport.w / (depthSideSize.y * resolutionScale)) * fullDepthToHalfDepthSizeRatio.y, + 1.0f); + model.setScale(scale); + batch.setViewportTransform(occlusionViewport); batch.setUniformBuffer(render_utils::slot::buffer::SsaoFrameParams, _aoFrameParametersBuffer[0]); batch.draw(gpu::TRIANGLE_STRIP, 4); #endif @@ -648,11 +713,12 @@ void AmbientOcclusionEffect::run(const render::RenderContextPointer& renderConte #if SSAO_USE_QUAD_SPLIT // Gather back the four separate renders into one interleaved one batch.pushProfileRange("Gather"); - batch.setViewportTransform(occlusionViewport); - batch.setFramebuffer(occlusionFBO); - batch.setPipeline(gatherPipeline); - batch.setResourceTexture(render_utils::slot::texture::SsaoOcclusion, occlusionBlurredFBO->getRenderBuffer(0)); - batch.draw(gpu::TRIANGLE_STRIP, 4); + batch.setViewportTransform(occlusionViewport); + batch.setModelTransform(Transform()); + batch.setFramebuffer(occlusionFBO); + batch.setPipeline(gatherPipeline); + batch.setResourceTexture(render_utils::slot::texture::SsaoOcclusion, _framebuffer->getOcclusionSplitTexture()); + batch.draw(gpu::TRIANGLE_STRIP, 4); batch.popProfileRange(); #endif diff --git a/libraries/render-utils/src/AmbientOcclusionEffect.h b/libraries/render-utils/src/AmbientOcclusionEffect.h index c9f5d42b14..d4f16f0c10 100644 --- a/libraries/render-utils/src/AmbientOcclusionEffect.h +++ b/libraries/render-utils/src/AmbientOcclusionEffect.h @@ -21,6 +21,8 @@ #include "DeferredFramebuffer.h" #include "SurfaceGeometryPass.h" +#include "ssao_shared.h" + class AmbientOcclusionFramebuffer { public: AmbientOcclusionFramebuffer(); @@ -34,6 +36,11 @@ public: gpu::FramebufferPointer getNormalFramebuffer(); gpu::TexturePointer getNormalTexture(); +#if SSAO_USE_QUAD_SPLIT + gpu::FramebufferPointer getOcclusionSplitFramebuffer(int index); + gpu::TexturePointer getOcclusionSplitTexture(); +#endif + // Update the source framebuffer size which will drive the allocation of all the other resources. bool update(const gpu::TexturePointer& linearDepthBuffer, int resolutionLevel, bool isStereo); gpu::TexturePointer getLinearDepthTexture(); @@ -56,7 +63,11 @@ protected: gpu::FramebufferPointer _normalFramebuffer; gpu::TexturePointer _normalTexture; - +#if SSAO_USE_QUAD_SPLIT + gpu::FramebufferPointer _occlusionSplitFramebuffers[SSAO_SPLIT_COUNT]; + gpu::TexturePointer _occlusionSplitTexture; +#endif + glm::ivec2 _frameSize; int _resolutionLevel{ 0 }; bool _isStereo{ false }; @@ -126,8 +137,6 @@ public: void configure(const Config& config); void run(const render::RenderContextPointer& renderContext, const Inputs& inputs, Outputs& outputs); -#include "ssao_shared.h" - // Class describing the uniform buffer with all the parameters common to the AO shaders class AOParameters : public AmbientOcclusionParams { public: diff --git a/libraries/render-utils/src/ssao.slh b/libraries/render-utils/src/ssao.slh index f8bba54d6b..5e250fe04b 100644 --- a/libraries/render-utils/src/ssao.slh +++ b/libraries/render-utils/src/ssao.slh @@ -148,10 +148,6 @@ float getAngleDithering(in ivec2 pixelPos) { #endif } -ivec2 getPixelOffset() { - return frameParams._pixelOffsets.xy; -} - float evalDiskRadius(float Zeye, vec2 sideImageSize) { // Choose the screen-space sample radius // proportional to the projected area of the sphere @@ -269,17 +265,16 @@ int evalMipFromRadius(float radius) { return clamp(findMSB(int(radius)) - LOG_MAX_OFFSET, 0, MAX_MIP_LEVEL); } -vec4 fetchTap(ivec4 side, vec2 tapSidePixelPos, float tapRadius, vec2 sideImageSize) { +vec2 fetchTap(ivec4 side, vec2 tapUV, float tapRadius) { int mipLevel = evalMipFromRadius(tapRadius * float(doFetchMips())); - vec2 tapUV = (tapSidePixelPos + vec2(0.5)) / sideImageSize; - vec2 fetchUV = mix(tapUV, vec2((tapUV.x + getStereoSide(side)) * 0.5, tapUV.y), isStereo()); + vec2 fetchUV = clamp(tapUV, vec2(0), vec2(1)); + fetchUV = mix(fetchUV, vec2((fetchUV.x + getStereoSide(side)) * 0.5, fetchUV.y), isStereo()); - vec4 P; - P.xy = tapUV; - P.w = float(mipLevel); - P.z = -textureLod(depthPyramidTex, fetchUV, P.w).x; - return P; + vec2 P; + P.x = float(mipLevel); + P.y = -textureLod(depthPyramidTex, fetchUV, P.x).x; + return P; } vec3 buildPosition(ivec4 side, vec2 fragUVPos) { @@ -348,13 +343,13 @@ float computeHorizonFromTap(vec3 tapPositionES, vec3 fragPositionES, vec3 fragNo } <@func computeHorizon()@> - vec2 tapSidePixelPos = tapPixelOffset + shadedPixelPos; - if (tapSidePixelPos.x<0 || tapSidePixelPos.y<0 || tapSidePixelPos.x>=sideImageSize.x || tapSidePixelPos.y>=sideImageSize.y) { + vec2 tapSideUVPos = tapUVOffset + fragUVPos; + if (tapSideUVPos.x<0 || tapSideUVPos.y<0 || tapSideUVPos.x>=1.0 || tapSideUVPos.y>=1.0) { // Early exit because we've hit the borders of the frame break; } - vec4 tapUVZ_mip = fetchTap(side, tapSidePixelPos, radius, sideImageSize); - vec3 tapPositionES = evalEyePositionFromZeye(side.x, tapUVZ_mip.z, tapUVZ_mip.xy); + vec2 tapMipZ = fetchTap(side, tapSideUVPos, radius); + vec3 tapPositionES = evalEyePositionFromZeye(side.x, tapMipZ.y, tapSideUVPos); float tapCosHorizonAngle = computeHorizonFromTap(tapPositionES, fragPositionES, fragNormalES); cosHorizonAngle = max(cosHorizonAngle, tapCosHorizonAngle); @@ -363,23 +358,22 @@ float computeHorizonFromTap(vec3 tapPositionES, vec3 fragPositionES, vec3 fragNo #define HBAO_HORIZON_SEARCH_CONSTANT_STEP 0 -float computeHorizon(ivec4 side, vec2 shadedPixelPos, vec2 sideImageSize, vec2 deltaTap, - vec3 fragPositionES, vec3 fragNormalES, vec2 searchVec, float searchRadius) { +float computeHorizon(ivec4 side, vec2 fragUVPos, vec3 fragPositionES, vec3 fragNormalES, vec2 searchVec, float searchRadius) { vec2 absSearchVec = abs(searchVec); - int stepCount = int(ceil(max(absSearchVec.x, absSearchVec.y))); + int stepCount = int(ceil(searchRadius)); float cosHorizonAngle = 0.0; if (stepCount>0) { - vec2 deltaPixelTap = searchVec / float(stepCount); + vec2 deltaTapUV = searchVec / float(stepCount); float deltaRadius = searchRadius / float(stepCount); - vec2 tapPixelOffset = vec2(0); + vec2 tapUVOffset = vec2(0); #if HBAO_HORIZON_SEARCH_CONSTANT_STEP float radius = 0.0; int stepIndex; for (stepIndex=0 ; stepIndex @@ -390,14 +384,14 @@ float computeHorizon(ivec4 side, vec2 shadedPixelPos, vec2 sideImageSize, vec2 d float mipLevel = evalMipFromRadius(radius * float(doFetchMips())); while (radius<=searchRadius) { - tapPixelOffset += deltaPixelTap; + tapUVOffset += deltaTapUV; <$computeHorizon()$> - if (tapUVZ_mip.w != mipLevel) { - mipLevel = tapUVZ_mip.w; + if (tapMipZ.x != mipLevel) { + mipLevel = tapMipZ.x; deltaRadius *= 2; - deltaPixelTap *= 2; + deltaTapUV *= 2; } radius += deltaRadius; } @@ -407,29 +401,22 @@ float computeHorizon(ivec4 side, vec2 shadedPixelPos, vec2 sideImageSize, vec2 d return cosHorizonAngle; } -float evalVisibilityHBAO(ivec4 side, vec2 shadedPixelPos, vec2 sideImageSize, vec2 deltaTap, float diskPixelRadius, +float evalVisibilityHBAO(ivec4 side, vec2 fragUVPos, vec2 invSideImageSize, vec2 deltaTap, float diskPixelRadius, vec3 fragPositionES, vec3 fragNormalES) { - vec2 searchVec = deltaTap * diskPixelRadius; + vec2 searchVec = (deltaTap * diskPixelRadius) * invSideImageSize; float obscurance = 0.0; // Forward search for h1 - obscurance = computeHorizon(side, shadedPixelPos, sideImageSize, deltaTap, fragPositionES, fragNormalES, searchVec, diskPixelRadius); + obscurance = computeHorizon(side, fragUVPos, fragPositionES, fragNormalES, searchVec, diskPixelRadius); // Backward search for h2 - obscurance += computeHorizon(side, shadedPixelPos, sideImageSize, deltaTap, fragPositionES, fragNormalES, -searchVec, diskPixelRadius); + obscurance += computeHorizon(side, fragUVPos, fragPositionES, fragNormalES, -searchVec, diskPixelRadius); return obscurance; } <@endfunc@> -<@func declareGatherPass()@> -<$declareAmbientOcclusion()$> - -// the source occlusion texture -layout(binding=RENDER_UTILS_TEXTURE_SSAO_OCCLUSION) uniform sampler2D occlusionMap; - -<@endfunc@> <@func declareBlurPass(axis)@> diff --git a/libraries/render-utils/src/ssao_gather.slf b/libraries/render-utils/src/ssao_gather.slf index 6814498b38..9a5eccb92b 100644 --- a/libraries/render-utils/src/ssao_gather.slf +++ b/libraries/render-utils/src/ssao_gather.slf @@ -15,7 +15,10 @@ // Hack comment -<$declareGatherPass()$> +<$declareAmbientOcclusion()$> + +// the source occlusion texture +layout(binding=RENDER_UTILS_TEXTURE_SSAO_OCCLUSION) uniform sampler2DArray occlusionMaps; layout(location=0) in vec4 varTexCoord0; @@ -27,8 +30,10 @@ void main(void) { ivec2 destPixelCoord = ivec2(gl_FragCoord.xy); ivec2 sourcePixelCoord = destPixelCoord / 2; ivec2 splitImageSize = getWidthHeightRoundUp(getResolutionLevel()+1); - + int occlusionMapIndex = (destPixelCoord.x & 1) + ((destPixelCoord.y) & 1)*2; + vec2 sourceUV = (sourcePixelCoord + vec2(0.5)) / splitImageSize; + sourcePixelCoord += (destPixelCoord & ivec2(1)) * splitImageSize; - outFragColor = texelFetch(occlusionMap, sourcePixelCoord, 0); + outFragColor = texture(occlusionMaps, vec3(sourceUV, occlusionMapIndex)); } diff --git a/libraries/render-utils/src/ssao_makeOcclusion.slf b/libraries/render-utils/src/ssao_makeOcclusion.slf index 510ca4ea7b..cc1f5d55ca 100644 --- a/libraries/render-utils/src/ssao_makeOcclusion.slf +++ b/libraries/render-utils/src/ssao_makeOcclusion.slf @@ -21,44 +21,35 @@ #define SSAO_HBAO_MAX_RADIUS 100.0 +layout(location=0) in vec2 varTexCoord0; + layout(location=0) out vec4 outFragColor; void main(void) { // Pixel being shaded vec2 fragCoord = gl_FragCoord.xy; ivec2 fragPixelPos = ivec2(fragCoord.xy); -#if SSAO_USE_QUAD_SPLIT - ivec2 splitImageSize = ivec2(getOcclusionSplitSideSize()); - fragPixelPos = ((fragPixelPos - getPixelOffset()*splitImageSize) * 2) + getPixelOffset(); -#endif - - // Fetch the z under the pixel (stereo or not) - int depthTextureRatio = 1 << (getResolutionLevel() - getDepthResolutionLevel()); - ivec2 depthTexFragPixelPos = fragPixelPos * depthTextureRatio; - float Zeye = getZEyeAtPixel(depthTexFragPixelPos, 0); -#if SSAO_USE_QUAD_SPLIT - vec3 fragNormalES = getNormalEyeAtPixel(fragPixelPos, 0); -#endif + vec2 fragUVPos = varTexCoord0; // Stereo side info based on the real viewport size of this pass + vec2 sideDepthSize = getDepthTextureSideSize(0); ivec2 sideOcclusionSize = ivec2( getOcclusionSideSize() ); ivec4 side = getStereoSideInfoFromWidth(fragPixelPos.x, sideOcclusionSize.x); - // From now on, fragPixelPos is the pixel pos in the side - vec2 depthSideSize = getDepthTextureSideSize(0); - vec2 sideImageSize = depthSideSize / float(depthTextureRatio); - fragPixelPos.x -= side.y; - vec2 fragUVPos = (vec2(fragPixelPos) + vec2(0.5)) / sideImageSize; + // From now on, fragUVPos is the UV pos in the side + fragUVPos.x = mix(fragUVPos.x, fragUVPos.x - getStereoSide(side) * 0.5, isStereo()); // The position and normal of the pixel fragment in Eye space + vec2 deltaDepthUV = vec2(1.0) / sideDepthSize; vec3 fragPositionES = buildPosition(side, fragUVPos); -#if !SSAO_USE_QUAD_SPLIT - vec2 deltaDepthUV = vec2(1.0) / depthSideSize; +#if SSAO_USE_QUAD_SPLIT + vec3 fragNormalES = getNormalEyeAtUV(fragUVPos, 0); +#else vec3 fragNormalES = buildNormal(side, fragUVPos, fragPositionES, deltaDepthUV); #endif // Choose the screen-space sample radius - float diskPixelRadius = evalDiskRadius(fragPositionES.z, sideImageSize); + float diskPixelRadius = evalDiskRadius(fragPositionES.z, sideDepthSize); #if SSAO_USE_HORIZON_BASED diskPixelRadius = min(diskPixelRadius, SSAO_HBAO_MAX_RADIUS); #endif @@ -72,16 +63,16 @@ void main(void) { float invNumSamples = getInvNumSamples(); // Steps are in the depth texture resolution - depthTexFragPixelPos = fragPixelPos * depthTextureRatio; + vec2 depthTexFragPixelPos = fragUVPos * sideDepthSize; for (int i = 0; i < numSamples; ++i) { #if SSAO_USE_HORIZON_BASED vec3 deltaTap = getUnitTapLocation(i, 1.0, randomPatternRotationAngle, PI); - obscuranceSum += evalVisibilityHBAO(side, vec2(depthTexFragPixelPos), depthSideSize, deltaTap.xy, diskPixelRadius, fragPositionES, fragNormalES); + obscuranceSum += evalVisibilityHBAO(side, fragUVPos, deltaDepthUV, deltaTap.xy, diskPixelRadius, fragPositionES, fragNormalES); #else - vec3 tap = getTapLocationClampedSSAO(i, randomPatternRotationAngle, diskPixelRadius, depthTexFragPixelPos, depthSideSize); - vec2 tapPixelPos = vec2(depthTexFragPixelPos) + tap.xy; - vec3 tapUVZ = fetchTap(side, tapPixelPos, tap.z, depthSideSize); - vec3 tapPositionES = evalEyePositionFromZeye(side.x, tapUVZ.z, tapUVZ.xy); + vec3 tap = getTapLocationClampedSSAO(i, randomPatternRotationAngle, diskPixelRadius, depthTexFragPixelPos, sideDepthSize); + vec2 tapUV = fragUVPos + tap.xy * deltaDepthUV; + vec2 tapMipZ = fetchTapWithUV(side, tapUV, tap.z); + vec3 tapPositionES = evalEyePositionFromZeye(side.x, tapMipZ.y, tapUV); obscuranceSum += float(tap.z > 0.0) * evalVisibilitySSAO(fragPositionES, fragNormalES, tapPositionES); #endif } diff --git a/libraries/render-utils/src/ssao_shared.h b/libraries/render-utils/src/ssao_shared.h index 109d471964..7410baf715 100644 --- a/libraries/render-utils/src/ssao_shared.h +++ b/libraries/render-utils/src/ssao_shared.h @@ -45,7 +45,6 @@ struct AmbientOcclusionParams { struct AmbientOcclusionFrameParams { SSAO_VEC4 _angleInfo; - SSAO_IVEC4 _pixelOffsets; }; #endif // RENDER_UTILS_SHADER_CONSTANTS_H From 190996e67033bea5f2fba193bb15329d43bb8b58 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Tue, 25 Sep 2018 10:01:30 +0200 Subject: [PATCH 041/286] Switched to full screen depth and normals --- .../src/AmbientOcclusionEffect.cpp | 60 +++++++++---------- .../render-utils/src/AmbientOcclusionEffect.h | 5 +- .../render-utils/src/SurfaceGeometryPass.cpp | 5 +- .../render-utils/src/ssao_buildNormals.slf | 6 +- .../render-utils/src/ssao_makeOcclusion.slf | 2 +- 5 files changed, 40 insertions(+), 38 deletions(-) diff --git a/libraries/render-utils/src/AmbientOcclusionEffect.cpp b/libraries/render-utils/src/AmbientOcclusionEffect.cpp index bfa5ae79e5..26d63f913c 100644 --- a/libraries/render-utils/src/AmbientOcclusionEffect.cpp +++ b/libraries/render-utils/src/AmbientOcclusionEffect.cpp @@ -41,15 +41,16 @@ gpu::PipelinePointer AmbientOcclusionEffect::_buildNormalsPipeline; AmbientOcclusionFramebuffer::AmbientOcclusionFramebuffer() { } -bool AmbientOcclusionFramebuffer::update(const gpu::TexturePointer& linearDepthBuffer, int resolutionLevel, bool isStereo) { +bool AmbientOcclusionFramebuffer::update(const gpu::TexturePointer& linearDepthBuffer, int resolutionLevel, int depthResolutionLevel, bool isStereo) { // If the depth buffer or size changed, we need to delete our FBOs bool reset = false; if (_linearDepthTexture != linearDepthBuffer) { _linearDepthTexture = linearDepthBuffer; reset = true; } - if (_resolutionLevel != resolutionLevel || isStereo != _isStereo) { + if (_resolutionLevel != resolutionLevel || isStereo != _isStereo || _depthResolutionLevel != depthResolutionLevel) { _resolutionLevel = resolutionLevel; + _depthResolutionLevel = depthResolutionLevel; _isStereo = isStereo; reset = true; } @@ -103,7 +104,7 @@ void AmbientOcclusionFramebuffer::allocate() { if (_isStereo) { sideSize.x >>= 1; } - sideSize = divideRoundUp(sideSize, 1 << std::min(1, _resolutionLevel)); + sideSize = divideRoundUp(sideSize, 1 << _depthResolutionLevel); if (_isStereo) { sideSize.x <<= 1; } @@ -117,11 +118,11 @@ void AmbientOcclusionFramebuffer::allocate() { #if SSAO_USE_QUAD_SPLIT { - auto splitSize = glm::ivec2(_normalFramebuffer->getSize()); + auto splitSize = _frameSize; if (_isStereo) { splitSize.x >>= 1; } - splitSize = divideRoundUp(splitSize, 2); + splitSize = divideRoundUp(splitSize, 2 << _resolutionLevel); if (_isStereo) { splitSize.x <<= 1; } @@ -531,6 +532,11 @@ void AmbientOcclusionEffect::updateGaussianDistribution() { GaussianDistribution::evalSampling(filterTaps, SSAO_BLUR_GAUSSIAN_COEFS_COUNT, blurRadius, _aoParametersBuffer->getBlurDeviation()); } +int AmbientOcclusionEffect::getDepthResolutionLevel() const { + // Having some problems making a nice AO with Half resolution depth, so stick to full res. + return 0; +} + void AmbientOcclusionEffect::run(const render::RenderContextPointer& renderContext, const Inputs& inputs, Outputs& outputs) { assert(renderContext->args); assert(renderContext->args->hasViewFrustum()); @@ -543,6 +549,9 @@ void AmbientOcclusionEffect::run(const render::RenderContextPointer& renderConte const int resolutionLevel = _aoParametersBuffer->getResolutionLevel(); const auto resolutionScale = powf(0.5f, resolutionLevel); + const auto depthResolutionLevel = getDepthResolutionLevel(); + const auto depthResolutionScale = powf(0.5f, depthResolutionLevel); + auto linearDepthTexture = linearDepthFramebuffer->getLinearDepthTexture(); auto occlusionDepthTexture = linearDepthTexture; auto sourceViewport = args->_viewport; @@ -550,13 +559,6 @@ void AmbientOcclusionEffect::run(const render::RenderContextPointer& renderConte // We need to take the rounded up resolution. auto occlusionViewport = divideRoundUp(sourceViewport, 1 << resolutionLevel); auto firstBlurViewport = sourceViewport; -#if SSAO_USE_QUAD_SPLIT - auto splitViewport = glm::ivec4{ 0, 0, _framebuffer->getOcclusionSplitTexture()->getWidth(), _framebuffer->getOcclusionSplitTexture()->getHeight() }; - auto depthSideSize = glm::ivec2(linearDepthTexture->getDimensions()); - if (args->isStereo()) { - depthSideSize.x >>= 1; - } -#endif firstBlurViewport.w = divideRoundUp(firstBlurViewport.w, 1 << resolutionLevel); if (!_gpuTimer) { @@ -567,11 +569,11 @@ void AmbientOcclusionEffect::run(const render::RenderContextPointer& renderConte _framebuffer = std::make_shared(); } - if (resolutionLevel > 0) { + if (depthResolutionLevel > 0) { occlusionDepthTexture = linearDepthFramebuffer->getHalfLinearDepthTexture(); } - if (_framebuffer->update(linearDepthTexture, resolutionLevel, args->isStereo())) { + if (_framebuffer->update(linearDepthTexture, resolutionLevel, depthResolutionLevel, args->isStereo())) { updateBlurParameters(); updateFramebufferSizes(); } @@ -595,14 +597,10 @@ void AmbientOcclusionEffect::run(const render::RenderContextPointer& renderConte auto occlusionNormalFramebuffer = _framebuffer->getNormalFramebuffer(); auto occlusionNormalTexture = _framebuffer->getNormalTexture(); auto normalViewport = glm::ivec4{ 0, 0, occlusionNormalFramebuffer->getWidth(), occlusionNormalFramebuffer->getHeight() }; + auto splitSize = glm::ivec2(_framebuffer->getOcclusionSplitTexture()->getDimensions()); + auto splitViewport = glm::ivec4{ 0, 0, splitSize.x, splitSize.y }; #endif - auto occlusionDepthSideSize = glm::ivec2(occlusionDepthTexture->getDimensions()); - if (args->isStereo()) { - occlusionDepthSideSize.x >>= 1; - } - - const auto depthResolutionLevel = std::min(1, resolutionLevel); - const auto depthResolutionScale = powf(2.0f, depthResolutionLevel); + auto occlusionDepthSize = glm::ivec2(occlusionDepthTexture->getDimensions()); // Update sample rotation const int SSAO_RANDOM_SAMPLE_COUNT = int(_randomSamples.size() / SSAO_SPLIT_COUNT); @@ -636,7 +634,7 @@ void AmbientOcclusionEffect::run(const render::RenderContextPointer& renderConte #if SSAO_USE_QUAD_SPLIT batch.pushProfileRange("Normal Generation"); - model.setScale( glm::vec3((normalViewport.z * depthResolutionScale) / depthSideSize.x, (normalViewport.w * depthResolutionScale) / depthSideSize.y, 1.0f) ); + model.setScale(glm::vec3(normalViewport.z / (sourceViewport.z * depthResolutionScale), normalViewport.w / (sourceViewport.w * depthResolutionScale), 1.0f)); batch.setModelTransform(model); // Build face normals pass @@ -668,39 +666,39 @@ void AmbientOcclusionEffect::run(const render::RenderContextPointer& renderConte batch.setResourceTexture(render_utils::slot::texture::SsaoNormal, occlusionNormalTexture); { const auto scale = glm::vec3( - (splitViewport.z * 2.0f) / occlusionDepthSideSize.x, - (splitViewport.w * 2.0f) / occlusionDepthSideSize.y, + (splitSize.x * 2.0f * depthResolutionScale) / (occlusionDepthSize.x * resolutionScale), + (splitSize.y * 2.0f * depthResolutionScale) / (occlusionDepthSize.y * resolutionScale), 1.0f); - const auto pixelUV = glm::vec2(1.0f) / (glm::vec2(occlusionDepthSideSize) * glm::vec2(scale.x, scale.y)); + const auto pixelOffset = glm::vec2(0.5f) / glm::vec2(occlusionDepthSize); batch.setViewportTransform(splitViewport); model.setScale(scale); - model.setTranslation(glm::vec3(0.0f, 0.0f, 0.0f)); + model.setTranslation(glm::vec3(-pixelOffset.x, -pixelOffset.y, 0.0f)); batch.setModelTransform(model); batch.setFramebuffer(_framebuffer->getOcclusionSplitFramebuffer(0)); batch.setUniformBuffer(render_utils::slot::buffer::SsaoFrameParams, _aoFrameParametersBuffer[0]); batch.draw(gpu::TRIANGLE_STRIP, 4); - model.setTranslation(glm::vec3(pixelUV.x, 0.0f, 0.0f)); + model.setTranslation(glm::vec3(pixelOffset.x, -pixelOffset.y, 0.0f)); batch.setFramebuffer(_framebuffer->getOcclusionSplitFramebuffer(1)); batch.setUniformBuffer(render_utils::slot::buffer::SsaoFrameParams, _aoFrameParametersBuffer[1]); batch.draw(gpu::TRIANGLE_STRIP, 4); - model.setTranslation(glm::vec3(pixelUV.x, pixelUV.y, 0.0f)); + model.setTranslation(glm::vec3(pixelOffset.x, pixelOffset.y, 0.0f)); batch.setFramebuffer(_framebuffer->getOcclusionSplitFramebuffer(3)); batch.setUniformBuffer(render_utils::slot::buffer::SsaoFrameParams, _aoFrameParametersBuffer[2]); batch.draw(gpu::TRIANGLE_STRIP, 4); - model.setTranslation(glm::vec3(0.0f, pixelUV.y, 0.0f)); + model.setTranslation(glm::vec3(-pixelOffset.x, pixelOffset.y, 0.0f)); batch.setFramebuffer(_framebuffer->getOcclusionSplitFramebuffer(2)); batch.setUniformBuffer(render_utils::slot::buffer::SsaoFrameParams, _aoFrameParametersBuffer[3]); batch.draw(gpu::TRIANGLE_STRIP, 4); } #else const auto scale = glm::vec3( - (occlusionViewport.z / (depthSideSize.x * resolutionScale)) * fullDepthToHalfDepthSizeRatio.x, - (occlusionViewport.w / (depthSideSize.y * resolutionScale)) * fullDepthToHalfDepthSizeRatio.y, + occlusionViewport.z / (framebufferSize.x * resolutionScale), + occlusionViewport.w / (framebufferSize.y * resolutionScale), 1.0f); model.setScale(scale); batch.setViewportTransform(occlusionViewport); diff --git a/libraries/render-utils/src/AmbientOcclusionEffect.h b/libraries/render-utils/src/AmbientOcclusionEffect.h index d4f16f0c10..a86cce4d23 100644 --- a/libraries/render-utils/src/AmbientOcclusionEffect.h +++ b/libraries/render-utils/src/AmbientOcclusionEffect.h @@ -42,7 +42,7 @@ public: #endif // Update the source framebuffer size which will drive the allocation of all the other resources. - bool update(const gpu::TexturePointer& linearDepthBuffer, int resolutionLevel, bool isStereo); + bool update(const gpu::TexturePointer& linearDepthBuffer, int resolutionLevel, int depthResolutionLevel, bool isStereo); gpu::TexturePointer getLinearDepthTexture(); const glm::ivec2& getSourceFrameSize() const { return _frameSize; } bool isStereo() const { return _isStereo; } @@ -70,6 +70,7 @@ protected: glm::ivec2 _frameSize; int _resolutionLevel{ 0 }; + int _depthResolutionLevel{ 0 }; bool _isStereo{ false }; }; @@ -177,6 +178,8 @@ private: void updateGaussianDistribution(); void updateBlurParameters(); void updateFramebufferSizes(); + + int getDepthResolutionLevel() const; AOParametersBuffer _aoParametersBuffer; FrameParametersBuffer _aoFrameParametersBuffer[SSAO_SPLIT_COUNT]; diff --git a/libraries/render-utils/src/SurfaceGeometryPass.cpp b/libraries/render-utils/src/SurfaceGeometryPass.cpp index d37c468cbb..d5e0249a21 100644 --- a/libraries/render-utils/src/SurfaceGeometryPass.cpp +++ b/libraries/render-utils/src/SurfaceGeometryPass.cpp @@ -73,8 +73,9 @@ void LinearDepthFramebuffer::allocate() { auto height = _frameSize.y; // For Linear Depth: - const uint16_t LINEAR_DEPTH_MAX_MIP_LEVEL = 1; - const auto depthSamplerFull = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR_MIP_POINT, gpu::Sampler::WRAP_CLAMP); + const uint16_t LINEAR_DEPTH_MAX_MIP_LEVEL = 5; + // Point sampling of the depth, as well as the clamp to edge, are needed for the AmbientOcclusionEffect with HBAO + const auto depthSamplerFull = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_POINT, gpu::Sampler::WRAP_CLAMP); _linearDepthTexture = gpu::Texture::createRenderBuffer(gpu::Element(gpu::SCALAR, gpu::FLOAT, gpu::RED), width, height, LINEAR_DEPTH_MAX_MIP_LEVEL, depthSamplerFull); _linearDepthFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("linearDepth")); diff --git a/libraries/render-utils/src/ssao_buildNormals.slf b/libraries/render-utils/src/ssao_buildNormals.slf index 09502f2026..9c183f640c 100644 --- a/libraries/render-utils/src/ssao_buildNormals.slf +++ b/libraries/render-utils/src/ssao_buildNormals.slf @@ -29,11 +29,11 @@ void main(void) { ivec2 sideNormalsSize = ivec2( getNormalsSideSize() ); ivec4 side = getStereoSideInfoFromWidth(fragPixelPos.x, sideNormalsSize.x); - // From now on, fragUVPos is the UV pos in the side - fragUVPos.x = mix(fragUVPos.x, fragUVPos.x - getStereoSide(side) * 0.5, isStereo()); - vec2 deltaDepthUV = vec2(1.0) / vec2(getDepthTextureSideSize(0)); + // From now on, fragUVPos is the UV pos in the side + fragUVPos.x = mix(fragUVPos.x, fragUVPos.x * 2.0 - getStereoSide(side), isStereo()); + // The position and normal of the pixel fragment in Eye space vec3 fragPositionES = buildPosition(side, fragUVPos); vec3 fragNormalES = buildNormal(side, fragUVPos, fragPositionES, deltaDepthUV); diff --git a/libraries/render-utils/src/ssao_makeOcclusion.slf b/libraries/render-utils/src/ssao_makeOcclusion.slf index cc1f5d55ca..f65a89ab09 100644 --- a/libraries/render-utils/src/ssao_makeOcclusion.slf +++ b/libraries/render-utils/src/ssao_makeOcclusion.slf @@ -37,7 +37,7 @@ void main(void) { ivec4 side = getStereoSideInfoFromWidth(fragPixelPos.x, sideOcclusionSize.x); // From now on, fragUVPos is the UV pos in the side - fragUVPos.x = mix(fragUVPos.x, fragUVPos.x - getStereoSide(side) * 0.5, isStereo()); + fragUVPos.x = mix(fragUVPos.x, fragUVPos.x * 2.0 - getStereoSide(side), isStereo()); // The position and normal of the pixel fragment in Eye space vec2 deltaDepthUV = vec2(1.0) / sideDepthSize; From 0c586edeeb20a8eaeba870ed0824b7291ab98ce6 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Tue, 25 Sep 2018 10:31:24 +0200 Subject: [PATCH 042/286] Fixed for stereo --- libraries/render-utils/src/AmbientOcclusionEffect.cpp | 4 ++-- libraries/render-utils/src/ssao_makeOcclusion.slf | 11 ++++++----- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/libraries/render-utils/src/AmbientOcclusionEffect.cpp b/libraries/render-utils/src/AmbientOcclusionEffect.cpp index 26d63f913c..776996536b 100644 --- a/libraries/render-utils/src/AmbientOcclusionEffect.cpp +++ b/libraries/render-utils/src/AmbientOcclusionEffect.cpp @@ -111,7 +111,7 @@ void AmbientOcclusionFramebuffer::allocate() { auto width = sideSize.x; auto height = sideSize.y; auto format = gpu::Element::COLOR_RGBA_32; - _normalTexture = gpu::Texture::createRenderBuffer(format, width, height, gpu::Texture::SINGLE_MIP, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_POINT, gpu::Sampler::WRAP_CLAMP)); + _normalTexture = gpu::Texture::createRenderBuffer(format, width, height, gpu::Texture::SINGLE_MIP, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR, gpu::Sampler::WRAP_CLAMP)); _normalFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("ssaoNormals")); _normalFramebuffer->setRenderBuffer(0, _normalTexture); } @@ -131,7 +131,7 @@ void AmbientOcclusionFramebuffer::allocate() { auto format = gpu::Element::COLOR_R_8; _occlusionSplitTexture = gpu::Texture::createRenderBufferArray(format, width, height, SSAO_SPLIT_COUNT, gpu::Texture::SINGLE_MIP, - gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR, gpu::Sampler::WRAP_CLAMP)); + gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_POINT, gpu::Sampler::WRAP_CLAMP)); for (int i = 0; i < SSAO_SPLIT_COUNT; i++) { _occlusionSplitFramebuffers[i] = gpu::FramebufferPointer(gpu::Framebuffer::create("occlusion")); _occlusionSplitFramebuffers[i]->setRenderBuffer(0, _occlusionSplitTexture, i); diff --git a/libraries/render-utils/src/ssao_makeOcclusion.slf b/libraries/render-utils/src/ssao_makeOcclusion.slf index f65a89ab09..044d11910a 100644 --- a/libraries/render-utils/src/ssao_makeOcclusion.slf +++ b/libraries/render-utils/src/ssao_makeOcclusion.slf @@ -31,20 +31,21 @@ void main(void) { ivec2 fragPixelPos = ivec2(fragCoord.xy); vec2 fragUVPos = varTexCoord0; +#if SSAO_USE_QUAD_SPLIT + vec3 fragNormalES = getNormalEyeAtUV(fragUVPos, 0); +#endif + // Stereo side info based on the real viewport size of this pass vec2 sideDepthSize = getDepthTextureSideSize(0); - ivec2 sideOcclusionSize = ivec2( getOcclusionSideSize() ); + ivec2 sideOcclusionSize = ivec2( getOcclusionSplitSideSize() ); ivec4 side = getStereoSideInfoFromWidth(fragPixelPos.x, sideOcclusionSize.x); - // From now on, fragUVPos is the UV pos in the side fragUVPos.x = mix(fragUVPos.x, fragUVPos.x * 2.0 - getStereoSide(side), isStereo()); // The position and normal of the pixel fragment in Eye space vec2 deltaDepthUV = vec2(1.0) / sideDepthSize; vec3 fragPositionES = buildPosition(side, fragUVPos); -#if SSAO_USE_QUAD_SPLIT - vec3 fragNormalES = getNormalEyeAtUV(fragUVPos, 0); -#else +#if !SSAO_USE_QUAD_SPLIT vec3 fragNormalES = buildNormal(side, fragUVPos, fragPositionES, deltaDepthUV); #endif From 60f59130023e6ce10e6ea457ed5dfb7bbf14d892 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Tue, 25 Sep 2018 11:06:05 +0200 Subject: [PATCH 043/286] Bilateral blur taps are evaluated in shader --- .../src/AmbientOcclusionEffect.cpp | 69 ------------------- .../render-utils/src/AmbientOcclusionEffect.h | 1 - libraries/render-utils/src/ssao.slh | 23 ++++--- libraries/render-utils/src/ssao_shared.h | 2 - 4 files changed, 13 insertions(+), 82 deletions(-) diff --git a/libraries/render-utils/src/AmbientOcclusionEffect.cpp b/libraries/render-utils/src/AmbientOcclusionEffect.cpp index 776996536b..5a1a168894 100644 --- a/libraries/render-utils/src/AmbientOcclusionEffect.cpp +++ b/libraries/render-utils/src/AmbientOcclusionEffect.cpp @@ -199,61 +199,6 @@ gpu::TexturePointer AmbientOcclusionFramebuffer::getNormalTexture() { return _normalTexture; } -class GaussianDistribution { -public: - - static double integral(float x, float deviation) { - return 0.5 * erf((double)x / ((double)deviation * sqrt(2.0))); - } - - static double rangeIntegral(float x0, float x1, float deviation) { - return integral(x1, deviation) - integral(x0, deviation); - } - - static std::vector evalSampling(int samplingRadius, float deviation) { - std::vector coefs(samplingRadius + 1, 0.0f); - - // corner case when radius is 0 or under - if (samplingRadius <= 0) { - coefs[0] = 1.0f; - return coefs; - } - - // Evaluate all the samples range integral of width 1 from center until the penultimate one - float halfWidth = 0.5f; - double sum = 0.0; - for (int i = 0; i < samplingRadius; i++) { - float x = (float) i; - double sample = rangeIntegral(x - halfWidth, x + halfWidth, deviation); - coefs[i] = sample; - sum += sample; - } - - // last sample goes to infinity - float lastSampleX0 = (float) samplingRadius - halfWidth; - float largeEnough = lastSampleX0 + 1000.0f * deviation; - double sample = rangeIntegral(lastSampleX0, largeEnough, deviation); - coefs[samplingRadius] = sample; - sum += sample; - - return coefs; - } - - static void evalSampling(float* coefs, unsigned int coefsLength, int samplingRadius, float deviation) { - auto coefsVector = evalSampling(samplingRadius, deviation); - if (coefsLength> coefsVector.size() + 1) { - unsigned int coefsNum = 0; - for (auto s : coefsVector) { - coefs[coefsNum] = s; - coefsNum++; - } - for (;coefsNum < coefsLength; coefsNum++) { - coefs[coefsNum] = 0.0f; - } - } - } -}; - AmbientOcclusionEffectConfig::AmbientOcclusionEffectConfig() : render::GPUJobConfig::Persistent(QStringList() << "Render" << "Engine" << "Ambient Occlusion", false), #if SSAO_USE_HORIZON_BASED @@ -298,7 +243,6 @@ AmbientOcclusionEffect::AmbientOcclusionEffect() { void AmbientOcclusionEffect::configure(const Config& config) { DependencyManager::get()->setAmbientOcclusionEnabled(config.enabled); - bool shouldUpdateGaussian = false; bool shouldUpdateBlurs = false; const double RADIUS_POWER = 6.0; @@ -332,7 +276,6 @@ void AmbientOcclusionEffect::configure(const Config& config) { if (config.blurDeviation != _aoParametersBuffer->getBlurDeviation()) { auto& current = _aoParametersBuffer.edit()._blurInfo; current.z = config.blurDeviation; - shouldUpdateGaussian = true; } if (config.numSpiralTurns != _aoParametersBuffer->getNumSpiralTurns()) { @@ -386,7 +329,6 @@ void AmbientOcclusionEffect::configure(const Config& config) { if (config.blurRadius != _aoParametersBuffer.get().getBlurRadius()) { auto& current = _aoParametersBuffer.edit()._blurInfo; current.y = (float)config.blurRadius; - shouldUpdateGaussian = true; } if (config.ditheringEnabled != _aoParametersBuffer->isDitheringEnabled()) { @@ -399,10 +341,6 @@ void AmbientOcclusionEffect::configure(const Config& config) { current.w = (float)config.borderingEnabled; } - if (shouldUpdateGaussian) { - updateGaussianDistribution(); - } - if (shouldUpdateBlurs) { updateBlurParameters(); } @@ -525,13 +463,6 @@ const gpu::PipelinePointer& AmbientOcclusionEffect::getBuildNormalsPipeline() { return _buildNormalsPipeline; } -void AmbientOcclusionEffect::updateGaussianDistribution() { - auto filterTaps = _aoParametersBuffer.edit()._blurFilterTaps; - auto blurRadius = _aoParametersBuffer.get().getBlurRadius(); - - GaussianDistribution::evalSampling(filterTaps, SSAO_BLUR_GAUSSIAN_COEFS_COUNT, blurRadius, _aoParametersBuffer->getBlurDeviation()); -} - int AmbientOcclusionEffect::getDepthResolutionLevel() const { // Having some problems making a nice AO with Half resolution depth, so stick to full res. return 0; diff --git a/libraries/render-utils/src/AmbientOcclusionEffect.h b/libraries/render-utils/src/AmbientOcclusionEffect.h index a86cce4d23..3ad5ee2138 100644 --- a/libraries/render-utils/src/AmbientOcclusionEffect.h +++ b/libraries/render-utils/src/AmbientOcclusionEffect.h @@ -175,7 +175,6 @@ private: using FrameParametersBuffer = gpu::StructBuffer< AmbientOcclusionFrameParams>; - void updateGaussianDistribution(); void updateBlurParameters(); void updateFramebufferSizes(); diff --git a/libraries/render-utils/src/ssao.slh b/libraries/render-utils/src/ssao.slh index 5e250fe04b..468e3fc7d1 100644 --- a/libraries/render-utils/src/ssao.slh +++ b/libraries/render-utils/src/ssao.slh @@ -123,10 +123,6 @@ int getBlurRadius() { return int(params._blurInfo.y); } -float getBlurCoef(int c) { - return params._blurFilterTaps[c]; -} - <@endfunc@> <@func declareSamplingDisk()@> @@ -454,7 +450,12 @@ float fetchOcclusion(vec2 coords) { const float BLUR_EDGE_DISTANCE_SCALE = 1000.0; -vec2 evalTapWeightedValue(ivec3 side, int r, ivec2 destPixelCoord, vec2 scaledTexCoord, vec2 fullTexCoord, float fragDepth) { +float evalBlurCoefficient(vec2 blurScales, float radialDistance, float zDistance) { + vec2 distances = vec2(radialDistance, zDistance); + return exp2(dot(blurScales, distances*distances)); +} + +vec2 evalTapWeightedValue(vec2 blurScales, ivec3 side, int r, ivec2 destPixelCoord, vec2 scaledTexCoord, vec2 fullTexCoord, float fragDepth) { ivec2 tapOffset = <$axis$> * r; ivec2 tapPixelCoord = destPixelCoord + ivec2(tapOffset); @@ -467,11 +468,10 @@ vec2 evalTapWeightedValue(ivec3 side, int r, ivec2 destPixelCoord, vec2 scaledTe tapTexCoord = fullTexCoord + tapOffset * getDepthBlurScale(); float tapDepth = getZEyeAtUV(tapTexCoord, 0); - float weight = getBlurCoef(abs(r)); // range domain (the "bilateral" weight). As depth difference increases, decrease weight. float zDistance = tapDepth - fragDepth; - weight *= exp(-getBlurEdgeSharpness() * (zDistance * zDistance * BLUR_EDGE_DISTANCE_SCALE)); + float weight = evalBlurCoefficient(blurScales, abs(r), zDistance); return vec2(tapOcclusion * weight, weight); } @@ -485,20 +485,23 @@ vec3 getBlurredOcclusion(ivec2 destPixelCoord, vec2 scaledTexCoord, vec2 fullTex // Accumulate weighted contributions along the bluring axis in the [-radius, radius] range int blurRadius = getBlurRadius(); + float blurRadialSigma = float(blurRadius) * 0.5; + float blurRadialScale = 1.0 / (2.0*blurRadialSigma*blurRadialSigma); + vec2 blurScales = -vec2(blurRadialScale, BLUR_EDGE_DISTANCE_SCALE) * getBlurEdgeSharpness(); // negative side first for (int r = -blurRadius; r <= -1; ++r) { - weightedSums += evalTapWeightedValue(side.xyz, r, destPixelCoord, scaledTexCoord, fullTexCoord, fragDepth); + weightedSums += evalTapWeightedValue(blurScales, side.xyz, r, destPixelCoord, scaledTexCoord, fullTexCoord, fragDepth); } // Central pixel contribution - float mainWeight = getBlurCoef(0); + float mainWeight = 1.0; float pixelOcclusion = fetchOcclusion(scaledTexCoord); weightedSums += vec2(pixelOcclusion * mainWeight, mainWeight); // then positive side for (int r = 1; r <= blurRadius; ++r) { - weightedSums += evalTapWeightedValue(side.xyz, r, destPixelCoord, scaledTexCoord, fullTexCoord, fragDepth); + weightedSums += evalTapWeightedValue(blurScales, side.xyz, r, destPixelCoord, scaledTexCoord, fullTexCoord, fragDepth); } // Final normalization diff --git a/libraries/render-utils/src/ssao_shared.h b/libraries/render-utils/src/ssao_shared.h index 7410baf715..11c7f894d5 100644 --- a/libraries/render-utils/src/ssao_shared.h +++ b/libraries/render-utils/src/ssao_shared.h @@ -16,7 +16,6 @@ #define SSAO_USE_HORIZON_BASED 1 #define SSAO_USE_QUAD_SPLIT 1 -#define SSAO_BLUR_GAUSSIAN_COEFS_COUNT 16 #if SSAO_USE_QUAD_SPLIT #define SSAO_SPLIT_COUNT 4 @@ -39,7 +38,6 @@ struct AmbientOcclusionParams { SSAO_VEC4 _ditheringInfo; SSAO_VEC4 _sampleInfo; SSAO_VEC4 _blurInfo; - float _blurFilterTaps[SSAO_BLUR_GAUSSIAN_COEFS_COUNT]; SSAO_VEC4 _sideSizes[2]; }; From 9899eb6d3ff5a066c1c55bc2c30ed7c27f8c2cb4 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Tue, 25 Sep 2018 15:46:07 +0200 Subject: [PATCH 044/286] Concentrating on resolution level 1 --- .../src/AmbientOcclusionEffect.cpp | 74 ++++++++++--------- libraries/render-utils/src/ssao.slh | 13 ++-- .../render-utils/src/ssao_makeOcclusion.slf | 2 +- 3 files changed, 48 insertions(+), 41 deletions(-) diff --git a/libraries/render-utils/src/AmbientOcclusionEffect.cpp b/libraries/render-utils/src/AmbientOcclusionEffect.cpp index 5a1a168894..1574a643db 100644 --- a/libraries/render-utils/src/AmbientOcclusionEffect.cpp +++ b/libraries/render-utils/src/AmbientOcclusionEffect.cpp @@ -89,6 +89,8 @@ void AmbientOcclusionFramebuffer::allocate() { auto height = _frameSize.y; auto format = gpu::Element::COLOR_R_8; + //TEMPO OP + format = gpu::Element::VEC4F_COLOR_RGBA; _occlusionTexture = gpu::Texture::createRenderBuffer(format, width, height, gpu::Texture::SINGLE_MIP, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR, gpu::Sampler::WRAP_CLAMP)); _occlusionFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("occlusion")); _occlusionFramebuffer->setRenderBuffer(0, _occlusionTexture); @@ -111,7 +113,8 @@ void AmbientOcclusionFramebuffer::allocate() { auto width = sideSize.x; auto height = sideSize.y; auto format = gpu::Element::COLOR_RGBA_32; - _normalTexture = gpu::Texture::createRenderBuffer(format, width, height, gpu::Texture::SINGLE_MIP, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR, gpu::Sampler::WRAP_CLAMP)); + _normalTexture = gpu::Texture::createRenderBuffer(format, width, height, gpu::Texture::SINGLE_MIP, + gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_POINT, gpu::Sampler::WRAP_CLAMP)); _normalFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("ssaoNormals")); _normalFramebuffer->setRenderBuffer(0, _normalTexture); } @@ -221,7 +224,7 @@ AmbientOcclusionEffectConfig::AmbientOcclusionEffectConfig() : #else numSamples{ 16 }, #endif - resolutionLevel{ 2 }, + resolutionLevel{ 1 }, blurRadius{ 4 }, ditheringEnabled{ true }, borderingEnabled{ true }, @@ -395,7 +398,7 @@ const gpu::PipelinePointer& AmbientOcclusionEffect::getOcclusionPipeline() { gpu::ShaderPointer program = gpu::Shader::createProgram(shader::render_utils::program::ssao_makeOcclusion); gpu::StatePointer state = gpu::StatePointer(new gpu::State()); - state->setColorWriteMask(true, true, true, false); + state->setColorWriteMask(true, true, true, true); // Good to go add the brand new pipeline _occlusionPipeline = gpu::Pipeline::create(program, state); @@ -585,18 +588,13 @@ void AmbientOcclusionEffect::run(const render::RenderContextPointer& renderConte batch.setUniformBuffer(render_utils::slot::buffer::DeferredFrameTransform, frameTransform->getFrameTransformBuffer()); batch.setUniformBuffer(render_utils::slot::buffer::SsaoParams, _aoParametersBuffer); -#if SSAO_USE_QUAD_SPLIT - batch.setFramebuffer(occlusionBlurredFBO); -#else - batch.setFramebuffer(occlusionFBO); -#endif batch.setPipeline(occlusionPipeline); batch.setResourceTexture(render_utils::slot::texture::SsaoDepth, occlusionDepthTexture); #if SSAO_USE_QUAD_SPLIT batch.setResourceTexture(render_utils::slot::texture::SsaoNormal, occlusionNormalTexture); { - const auto scale = glm::vec3( + const auto uvScale = glm::vec3( (splitSize.x * 2.0f * depthResolutionScale) / (occlusionDepthSize.x * resolutionScale), (splitSize.y * 2.0f * depthResolutionScale) / (occlusionDepthSize.y * resolutionScale), 1.0f); @@ -604,7 +602,7 @@ void AmbientOcclusionEffect::run(const render::RenderContextPointer& renderConte batch.setViewportTransform(splitViewport); - model.setScale(scale); + model.setScale(uvScale); model.setTranslation(glm::vec3(-pixelOffset.x, -pixelOffset.y, 0.0f)); batch.setModelTransform(model); batch.setFramebuffer(_framebuffer->getOcclusionSplitFramebuffer(0)); @@ -612,27 +610,34 @@ void AmbientOcclusionEffect::run(const render::RenderContextPointer& renderConte batch.draw(gpu::TRIANGLE_STRIP, 4); model.setTranslation(glm::vec3(pixelOffset.x, -pixelOffset.y, 0.0f)); + batch.setModelTransform(model); batch.setFramebuffer(_framebuffer->getOcclusionSplitFramebuffer(1)); batch.setUniformBuffer(render_utils::slot::buffer::SsaoFrameParams, _aoFrameParametersBuffer[1]); batch.draw(gpu::TRIANGLE_STRIP, 4); model.setTranslation(glm::vec3(pixelOffset.x, pixelOffset.y, 0.0f)); + batch.setModelTransform(model); batch.setFramebuffer(_framebuffer->getOcclusionSplitFramebuffer(3)); batch.setUniformBuffer(render_utils::slot::buffer::SsaoFrameParams, _aoFrameParametersBuffer[2]); batch.draw(gpu::TRIANGLE_STRIP, 4); model.setTranslation(glm::vec3(-pixelOffset.x, pixelOffset.y, 0.0f)); + batch.setModelTransform(model); batch.setFramebuffer(_framebuffer->getOcclusionSplitFramebuffer(2)); batch.setUniformBuffer(render_utils::slot::buffer::SsaoFrameParams, _aoFrameParametersBuffer[3]); batch.draw(gpu::TRIANGLE_STRIP, 4); } #else - const auto scale = glm::vec3( - occlusionViewport.z / (framebufferSize.x * resolutionScale), - occlusionViewport.w / (framebufferSize.y * resolutionScale), - 1.0f); - model.setScale(scale); batch.setViewportTransform(occlusionViewport); + { + const auto uvScale = glm::vec3( + (occlusionViewport.z * depthResolutionScale) / (occlusionDepthSize.x * resolutionScale), + (occlusionViewport.w * depthResolutionScale) / (occlusionDepthSize.y * resolutionScale), + 1.0f); + model.setScale(uvScale); + } + batch.setModelTransform(model); + batch.setFramebuffer(occlusionFBO); batch.setUniformBuffer(render_utils::slot::buffer::SsaoFrameParams, _aoFrameParametersBuffer[0]); batch.draw(gpu::TRIANGLE_STRIP, 4); #endif @@ -655,34 +660,35 @@ void AmbientOcclusionEffect::run(const render::RenderContextPointer& renderConte PROFILE_RANGE_BATCH(batch, "Bilateral Blur"); // Blur 1st pass batch.pushProfileRange("Horizontal"); - model.setScale(resolutionScale); - batch.setModelTransform(model); - batch.setViewportTransform(firstBlurViewport); - batch.setFramebuffer(occlusionBlurredFBO); - // Use full resolution depth and normal for bilateral upscaling and blur - batch.setResourceTexture(render_utils::slot::texture::SsaoDepth, linearDepthTexture); - batch.setUniformBuffer(render_utils::slot::buffer::SsaoBlurParams, _hblurParametersBuffer); - batch.setPipeline(firstHBlurPipeline); - batch.setResourceTexture(render_utils::slot::texture::SsaoOcclusion, occlusionFBO->getRenderBuffer(0)); - batch.draw(gpu::TRIANGLE_STRIP, 4); + model.setScale(resolutionScale); + batch.setModelTransform(model); + batch.setViewportTransform(firstBlurViewport); + batch.setFramebuffer(occlusionBlurredFBO); + // Use full resolution depth and normal for bilateral upscaling and blur + batch.setResourceTexture(render_utils::slot::texture::SsaoDepth, linearDepthTexture); + batch.setUniformBuffer(render_utils::slot::buffer::SsaoBlurParams, _hblurParametersBuffer); + batch.setPipeline(firstHBlurPipeline); + batch.setResourceTexture(render_utils::slot::texture::SsaoOcclusion, occlusionFBO->getRenderBuffer(0)); + batch.draw(gpu::TRIANGLE_STRIP, 4); batch.popProfileRange(); // Blur 2nd pass batch.pushProfileRange("Vertical"); - model.setScale(glm::vec3(1.0f, resolutionScale, 1.0f)); - batch.setModelTransform(model); - batch.setViewportTransform(sourceViewport); - batch.setFramebuffer(occlusionFBO); - batch.setUniformBuffer(render_utils::slot::buffer::SsaoBlurParams, _vblurParametersBuffer); - batch.setPipeline(lastVBlurPipeline); - batch.setResourceTexture(render_utils::slot::texture::SsaoOcclusion, occlusionBlurredFBO->getRenderBuffer(0)); - batch.draw(gpu::TRIANGLE_STRIP, 4); + model.setScale(glm::vec3(1.0f, resolutionScale, 1.0f)); + batch.setModelTransform(model); + batch.setViewportTransform(sourceViewport); + batch.setFramebuffer(occlusionFBO); + batch.setUniformBuffer(render_utils::slot::buffer::SsaoBlurParams, _vblurParametersBuffer); + batch.setPipeline(lastVBlurPipeline); + batch.setResourceTexture(render_utils::slot::texture::SsaoOcclusion, occlusionBlurredFBO->getRenderBuffer(0)); + batch.draw(gpu::TRIANGLE_STRIP, 4); batch.popProfileRange(); } batch.setResourceTexture(render_utils::slot::texture::SsaoDepth, nullptr); + batch.setResourceTexture(render_utils::slot::texture::SsaoNormal, nullptr); batch.setResourceTexture(render_utils::slot::texture::SsaoOcclusion, nullptr); - + _gpuTimer->end(batch); }); diff --git a/libraries/render-utils/src/ssao.slh b/libraries/render-utils/src/ssao.slh index 468e3fc7d1..345eeebafb 100644 --- a/libraries/render-utils/src/ssao.slh +++ b/libraries/render-utils/src/ssao.slh @@ -275,7 +275,7 @@ vec2 fetchTap(ivec4 side, vec2 tapUV, float tapRadius) { vec3 buildPosition(ivec4 side, vec2 fragUVPos) { vec2 fetchUV = clamp(fragUVPos, vec2(0), vec2(1)); - fetchUV = mix(fetchUV, vec2((fetchUV.x + getStereoSide(side)) * 0.5, fetchUV.y), isStereo()); + fetchUV.x = mix(fetchUV.x, (fetchUV.x + getStereoSide(side)) * 0.5, isStereo()); float Zeye = getZEyeAtUV(fetchUV, 0); return evalEyePositionFromZeye(side.x, Zeye, fragUVPos); } @@ -354,9 +354,9 @@ float computeHorizonFromTap(vec3 tapPositionES, vec3 fragPositionES, vec3 fragNo #define HBAO_HORIZON_SEARCH_CONSTANT_STEP 0 -float computeHorizon(ivec4 side, vec2 fragUVPos, vec3 fragPositionES, vec3 fragNormalES, vec2 searchVec, float searchRadius) { +float computeHorizon(ivec4 side, vec2 fragUVPos, vec3 fragPositionES, vec3 fragNormalES, vec2 searchVec, vec2 pixelSearchVec, float searchRadius) { vec2 absSearchVec = abs(searchVec); - int stepCount = int(ceil(searchRadius)); + int stepCount = int(ceil(max(pixelSearchVec.x, pixelSearchVec.y))); float cosHorizonAngle = 0.0; if (stepCount>0) { @@ -399,14 +399,15 @@ float computeHorizon(ivec4 side, vec2 fragUVPos, vec3 fragPositionES, vec3 fragN float evalVisibilityHBAO(ivec4 side, vec2 fragUVPos, vec2 invSideImageSize, vec2 deltaTap, float diskPixelRadius, vec3 fragPositionES, vec3 fragNormalES) { - vec2 searchVec = (deltaTap * diskPixelRadius) * invSideImageSize; + vec2 pixelSearchVec = deltaTap * diskPixelRadius; + vec2 searchVec = pixelSearchVec * invSideImageSize; float obscurance = 0.0; // Forward search for h1 - obscurance = computeHorizon(side, fragUVPos, fragPositionES, fragNormalES, searchVec, diskPixelRadius); + obscurance = computeHorizon(side, fragUVPos, fragPositionES, fragNormalES, searchVec, pixelSearchVec, diskPixelRadius); // Backward search for h2 - obscurance += computeHorizon(side, fragUVPos, fragPositionES, fragNormalES, -searchVec, diskPixelRadius); + obscurance += computeHorizon(side, fragUVPos, fragPositionES, fragNormalES, -searchVec, pixelSearchVec, diskPixelRadius); return obscurance; } diff --git a/libraries/render-utils/src/ssao_makeOcclusion.slf b/libraries/render-utils/src/ssao_makeOcclusion.slf index 044d11910a..0a34b0df03 100644 --- a/libraries/render-utils/src/ssao_makeOcclusion.slf +++ b/libraries/render-utils/src/ssao_makeOcclusion.slf @@ -43,7 +43,7 @@ void main(void) { fragUVPos.x = mix(fragUVPos.x, fragUVPos.x * 2.0 - getStereoSide(side), isStereo()); // The position and normal of the pixel fragment in Eye space - vec2 deltaDepthUV = vec2(1.0) / sideDepthSize; + vec2 deltaDepthUV = vec2(2.0) / sideDepthSize; vec3 fragPositionES = buildPosition(side, fragUVPos); #if !SSAO_USE_QUAD_SPLIT vec3 fragNormalES = buildNormal(side, fragUVPos, fragPositionES, deltaDepthUV); From dee0a6afa238ccebff133d4f755e6a77ea430f8c Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Wed, 26 Sep 2018 16:59:01 +0200 Subject: [PATCH 045/286] Found bug with low res depth fetch in occlusion --- .../src/AmbientOcclusionEffect.cpp | 86 ++++++++++--------- .../render-utils/src/AmbientOcclusionEffect.h | 6 +- libraries/render-utils/src/ssao.slh | 14 +-- libraries/render-utils/src/ssao_gather.slf | 9 +- libraries/render-utils/src/ssao_shared.h | 5 +- 5 files changed, 64 insertions(+), 56 deletions(-) diff --git a/libraries/render-utils/src/AmbientOcclusionEffect.cpp b/libraries/render-utils/src/AmbientOcclusionEffect.cpp index 1574a643db..4c1ff6fe09 100644 --- a/libraries/render-utils/src/AmbientOcclusionEffect.cpp +++ b/libraries/render-utils/src/AmbientOcclusionEffect.cpp @@ -89,8 +89,6 @@ void AmbientOcclusionFramebuffer::allocate() { auto height = _frameSize.y; auto format = gpu::Element::COLOR_R_8; - //TEMPO OP - format = gpu::Element::VEC4F_COLOR_RGBA; _occlusionTexture = gpu::Texture::createRenderBuffer(format, width, height, gpu::Texture::SINGLE_MIP, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR, gpu::Sampler::WRAP_CLAMP)); _occlusionFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("occlusion")); _occlusionFramebuffer->setRenderBuffer(0, _occlusionTexture); @@ -125,7 +123,7 @@ void AmbientOcclusionFramebuffer::allocate() { if (_isStereo) { splitSize.x >>= 1; } - splitSize = divideRoundUp(splitSize, 2 << _resolutionLevel); + splitSize = divideRoundUp(splitSize, SSAO_SPLIT_COUNT << _resolutionLevel); if (_isStereo) { splitSize.x <<= 1; } @@ -133,9 +131,9 @@ void AmbientOcclusionFramebuffer::allocate() { auto height = splitSize.y; auto format = gpu::Element::COLOR_R_8; - _occlusionSplitTexture = gpu::Texture::createRenderBufferArray(format, width, height, SSAO_SPLIT_COUNT, gpu::Texture::SINGLE_MIP, - gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_POINT, gpu::Sampler::WRAP_CLAMP)); - for (int i = 0; i < SSAO_SPLIT_COUNT; i++) { + _occlusionSplitTexture = gpu::Texture::createRenderBufferArray(format, width, height, SSAO_SPLIT_COUNT*SSAO_SPLIT_COUNT, gpu::Texture::SINGLE_MIP, + gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR, gpu::Sampler::WRAP_CLAMP)); + for (int i = 0; i < SSAO_SPLIT_COUNT*SSAO_SPLIT_COUNT; i++) { _occlusionSplitFramebuffers[i] = gpu::FramebufferPointer(gpu::Framebuffer::create("occlusion")); _occlusionSplitFramebuffers[i]->setRenderBuffer(0, _occlusionSplitTexture, i); } @@ -145,7 +143,7 @@ void AmbientOcclusionFramebuffer::allocate() { #if SSAO_USE_QUAD_SPLIT gpu::FramebufferPointer AmbientOcclusionFramebuffer::getOcclusionSplitFramebuffer(int index) { - assert(index < SSAO_SPLIT_COUNT); + assert(index < SSAO_SPLIT_COUNT*SSAO_SPLIT_COUNT); if (!_occlusionSplitFramebuffers[index]) { allocate(); } @@ -220,7 +218,7 @@ AmbientOcclusionEffectConfig::AmbientOcclusionEffectConfig() : blurDeviation{ 2.5f }, numSpiralTurns{ 7.0f }, #if SSAO_USE_HORIZON_BASED - numSamples{ 3 }, + numSamples{ 1 }, #else numSamples{ 16 }, #endif @@ -305,7 +303,7 @@ void AmbientOcclusionEffect::configure(const Config& config) { r = r + f * (float)(index % B); index = index / B; } - _randomSamples[i] = r * M_PI / config.numSamples; + _randomSamples[i] = r * 2.0f * M_PI / config.numSamples; } } @@ -445,7 +443,7 @@ const gpu::PipelinePointer& AmbientOcclusionEffect::getGatherPipeline() { gpu::ShaderPointer program = gpu::Shader::createProgram(shader::render_utils::program::ssao_gather); gpu::StatePointer state = gpu::StatePointer(new gpu::State()); - state->setColorWriteMask(true, true, true, false); + state->setColorWriteMask(true, true, true, true); // Good to go add the brand new pipeline _gatherPipeline = gpu::Pipeline::create(program, state); @@ -458,7 +456,7 @@ const gpu::PipelinePointer& AmbientOcclusionEffect::getBuildNormalsPipeline() { gpu::ShaderPointer program = gpu::Shader::createProgram(shader::render_utils::program::ssao_buildNormals); gpu::StatePointer state = gpu::StatePointer(new gpu::State()); - state->setColorWriteMask(true, true, true, false); + state->setColorWriteMask(true, true, true, true); // Good to go add the brand new pipeline _buildNormalsPipeline = gpu::Pipeline::create(program, state); @@ -537,8 +535,8 @@ void AmbientOcclusionEffect::run(const render::RenderContextPointer& renderConte auto occlusionDepthSize = glm::ivec2(occlusionDepthTexture->getDimensions()); // Update sample rotation - const int SSAO_RANDOM_SAMPLE_COUNT = int(_randomSamples.size() / SSAO_SPLIT_COUNT); - for (int splitId=0 ; splitId < SSAO_SPLIT_COUNT ; splitId++) { + const int SSAO_RANDOM_SAMPLE_COUNT = int(_randomSamples.size() / (SSAO_SPLIT_COUNT*SSAO_SPLIT_COUNT)); + for (int splitId=0 ; splitId < SSAO_SPLIT_COUNT*SSAO_SPLIT_COUNT ; splitId++) { auto& sample = _aoFrameParametersBuffer[splitId].edit(); sample._angleInfo.x = _randomSamples[splitId + SSAO_RANDOM_SAMPLE_COUNT * _frameId]; } @@ -568,7 +566,22 @@ void AmbientOcclusionEffect::run(const render::RenderContextPointer& renderConte #if SSAO_USE_QUAD_SPLIT batch.pushProfileRange("Normal Generation"); - model.setScale(glm::vec3(normalViewport.z / (sourceViewport.z * depthResolutionScale), normalViewport.w / (sourceViewport.w * depthResolutionScale), 1.0f)); + { + const auto uvScale = glm::vec3( + normalViewport.z / (sourceViewport.z * depthResolutionScale), + normalViewport.w / (sourceViewport.w * depthResolutionScale), + 1.0f); + const auto postPixelOffset = glm::vec2(0.5f) / glm::vec2(occlusionDepthSize); + const auto prePixelOffset = glm::vec2(0.5f * uvScale.x, 0.5f * uvScale.y) / glm::vec2(normalViewport.z, normalViewport.w); + const auto uvTranslate = glm::vec3( + postPixelOffset.x - prePixelOffset.x, + postPixelOffset.y - prePixelOffset.y, + 0.0f + ); + + model.setScale(uvScale); + model.setTranslation(uvTranslate); + } batch.setModelTransform(model); // Build face normals pass @@ -595,37 +608,30 @@ void AmbientOcclusionEffect::run(const render::RenderContextPointer& renderConte batch.setResourceTexture(render_utils::slot::texture::SsaoNormal, occlusionNormalTexture); { const auto uvScale = glm::vec3( - (splitSize.x * 2.0f * depthResolutionScale) / (occlusionDepthSize.x * resolutionScale), - (splitSize.y * 2.0f * depthResolutionScale) / (occlusionDepthSize.y * resolutionScale), + (splitSize.x * SSAO_SPLIT_COUNT * depthResolutionScale) / (occlusionDepthSize.x * resolutionScale), + (splitSize.y * SSAO_SPLIT_COUNT * depthResolutionScale) / (occlusionDepthSize.y * resolutionScale), 1.0f); - const auto pixelOffset = glm::vec2(0.5f) / glm::vec2(occlusionDepthSize); + const auto postPixelOffset = glm::vec2(0.5f) / glm::vec2(occlusionDepthSize); + const auto prePixelOffset = glm::vec2(0.5f * uvScale.x, 0.5f * uvScale.y) / glm::vec2(splitSize.x, splitSize.y); batch.setViewportTransform(splitViewport); model.setScale(uvScale); - model.setTranslation(glm::vec3(-pixelOffset.x, -pixelOffset.y, 0.0f)); - batch.setModelTransform(model); - batch.setFramebuffer(_framebuffer->getOcclusionSplitFramebuffer(0)); - batch.setUniformBuffer(render_utils::slot::buffer::SsaoFrameParams, _aoFrameParametersBuffer[0]); - batch.draw(gpu::TRIANGLE_STRIP, 4); - - model.setTranslation(glm::vec3(pixelOffset.x, -pixelOffset.y, 0.0f)); - batch.setModelTransform(model); - batch.setFramebuffer(_framebuffer->getOcclusionSplitFramebuffer(1)); - batch.setUniformBuffer(render_utils::slot::buffer::SsaoFrameParams, _aoFrameParametersBuffer[1]); - batch.draw(gpu::TRIANGLE_STRIP, 4); - - model.setTranslation(glm::vec3(pixelOffset.x, pixelOffset.y, 0.0f)); - batch.setModelTransform(model); - batch.setFramebuffer(_framebuffer->getOcclusionSplitFramebuffer(3)); - batch.setUniformBuffer(render_utils::slot::buffer::SsaoFrameParams, _aoFrameParametersBuffer[2]); - batch.draw(gpu::TRIANGLE_STRIP, 4); - - model.setTranslation(glm::vec3(-pixelOffset.x, pixelOffset.y, 0.0f)); - batch.setModelTransform(model); - batch.setFramebuffer(_framebuffer->getOcclusionSplitFramebuffer(2)); - batch.setUniformBuffer(render_utils::slot::buffer::SsaoFrameParams, _aoFrameParametersBuffer[3]); - batch.draw(gpu::TRIANGLE_STRIP, 4); + for (int y = 0; y < SSAO_SPLIT_COUNT; y++) { + for (int x = 0; x < SSAO_SPLIT_COUNT; x++) { + const int splitIndex = x + y * SSAO_SPLIT_COUNT; + const auto uvTranslate = glm::vec3( + postPixelOffset.x * (2 * x + 1) - prePixelOffset.x, + postPixelOffset.y * (2 * y + 1) - prePixelOffset.y, + 0.0f + ); + model.setTranslation(uvTranslate); + batch.setModelTransform(model); + batch.setFramebuffer(_framebuffer->getOcclusionSplitFramebuffer(splitIndex)); + batch.setUniformBuffer(render_utils::slot::buffer::SsaoFrameParams, _aoFrameParametersBuffer[splitIndex]); + batch.draw(gpu::TRIANGLE_STRIP, 4); + } + } } #else batch.setViewportTransform(occlusionViewport); diff --git a/libraries/render-utils/src/AmbientOcclusionEffect.h b/libraries/render-utils/src/AmbientOcclusionEffect.h index 3ad5ee2138..d84a8636ef 100644 --- a/libraries/render-utils/src/AmbientOcclusionEffect.h +++ b/libraries/render-utils/src/AmbientOcclusionEffect.h @@ -64,7 +64,7 @@ protected: gpu::TexturePointer _normalTexture; #if SSAO_USE_QUAD_SPLIT - gpu::FramebufferPointer _occlusionSplitFramebuffers[SSAO_SPLIT_COUNT]; + gpu::FramebufferPointer _occlusionSplitFramebuffers[SSAO_SPLIT_COUNT*SSAO_SPLIT_COUNT]; gpu::TexturePointer _occlusionSplitTexture; #endif @@ -181,7 +181,7 @@ private: int getDepthResolutionLevel() const; AOParametersBuffer _aoParametersBuffer; - FrameParametersBuffer _aoFrameParametersBuffer[SSAO_SPLIT_COUNT]; + FrameParametersBuffer _aoFrameParametersBuffer[SSAO_SPLIT_COUNT*SSAO_SPLIT_COUNT]; BlurParametersBuffer _vblurParametersBuffer; BlurParametersBuffer _hblurParametersBuffer; @@ -200,7 +200,7 @@ private: static gpu::PipelinePointer _buildNormalsPipeline; AmbientOcclusionFramebufferPointer _framebuffer; - std::array _randomSamples; + std::array _randomSamples; int _frameId{ 0 }; gpu::RangeTimerPointer _gpuTimer; diff --git a/libraries/render-utils/src/ssao.slh b/libraries/render-utils/src/ssao.slh index 345eeebafb..b3f66abb35 100644 --- a/libraries/render-utils/src/ssao.slh +++ b/libraries/render-utils/src/ssao.slh @@ -160,7 +160,7 @@ const float TWO_PI = 6.2831852; vec3 getUnitTapLocation(int sampleNumber, float spiralTurns, float spinAngle, float angleRange){ // Radius relative to ssR - float alpha = float(sampleNumber + 0.5) * getInvNumSamples(); + float alpha = float(sampleNumber) * getInvNumSamples(); float angle = alpha * (spiralTurns * angleRange) + spinAngle; return vec3(cos(angle), sin(angle), alpha); } @@ -244,11 +244,11 @@ float getZEyeAtPixel(ivec2 pixel, int level) { } float getZEyeAtUV(vec2 texCoord, int level) { - return -texture(depthPyramidTex, texCoord, level).x; + return -textureLod(depthPyramidTex, texCoord, level).x; } vec3 getNormalEyeAtUV(vec2 texCoord, int level) { - return normalize(texture(normalTex, texCoord, level).xyz*2.0 - vec3(1.0)); + return normalize(textureLod(normalTex, texCoord, level).xyz*2.0 - vec3(1.0)); } vec3 getNormalEyeAtPixel(ivec2 pixel, int level) { @@ -285,13 +285,14 @@ vec3 getMinDelta(vec3 centralPoint, vec3 offsetPointPos, vec3 offsetPointNeg) { vec3 delta1 = centralPoint - offsetPointNeg; float sqrLength0 = dot(delta0, delta0); float sqrLength1 = dot(delta1, delta1); - return sqrLength0 < sqrLength1 ? delta0 : delta1; + float epsilon = 1e-6; + return sqrLength0 < sqrLength1 && sqrLength0>epsilon ? delta0 : delta1; } vec3 buildNormal(ivec4 side, vec2 fragUVPos, vec3 fragPosition, vec2 deltaDepthUV) { vec3 fragPositionDxPos = buildPosition(side, fragUVPos + vec2(deltaDepthUV.x, 0)); - vec3 fragPositionDyPos = buildPosition(side, fragUVPos + vec2(0, deltaDepthUV.y)); vec3 fragPositionDxNeg = buildPosition(side, fragUVPos - vec2(deltaDepthUV.x, 0)); + vec3 fragPositionDyPos = buildPosition(side, fragUVPos + vec2(0, deltaDepthUV.y)); vec3 fragPositionDyNeg = buildPosition(side, fragUVPos - vec2(0, deltaDepthUV.y)); vec3 fragPositionDx = getMinDelta(fragPosition, fragPositionDxPos, fragPositionDxNeg); @@ -356,6 +357,7 @@ float computeHorizonFromTap(vec3 tapPositionES, vec3 fragPositionES, vec3 fragNo float computeHorizon(ivec4 side, vec2 fragUVPos, vec3 fragPositionES, vec3 fragNormalES, vec2 searchVec, vec2 pixelSearchVec, float searchRadius) { vec2 absSearchVec = abs(searchVec); + pixelSearchVec = abs(pixelSearchVec); int stepCount = int(ceil(max(pixelSearchVec.x, pixelSearchVec.y))); float cosHorizonAngle = 0.0; @@ -488,7 +490,7 @@ vec3 getBlurredOcclusion(ivec2 destPixelCoord, vec2 scaledTexCoord, vec2 fullTex int blurRadius = getBlurRadius(); float blurRadialSigma = float(blurRadius) * 0.5; float blurRadialScale = 1.0 / (2.0*blurRadialSigma*blurRadialSigma); - vec2 blurScales = -vec2(blurRadialScale, BLUR_EDGE_DISTANCE_SCALE) * getBlurEdgeSharpness(); + vec2 blurScales = -vec2(blurRadialScale, BLUR_EDGE_DISTANCE_SCALE * getBlurEdgeSharpness()); // negative side first for (int r = -blurRadius; r <= -1; ++r) { diff --git a/libraries/render-utils/src/ssao_gather.slf b/libraries/render-utils/src/ssao_gather.slf index 9a5eccb92b..64cbbdd0c7 100644 --- a/libraries/render-utils/src/ssao_gather.slf +++ b/libraries/render-utils/src/ssao_gather.slf @@ -28,12 +28,11 @@ void main(void) { // Gather the four splits of the occlusion result back into an interleaved full size // result (at the resolution level, of course) ivec2 destPixelCoord = ivec2(gl_FragCoord.xy); - ivec2 sourcePixelCoord = destPixelCoord / 2; - ivec2 splitImageSize = getWidthHeightRoundUp(getResolutionLevel()+1); - int occlusionMapIndex = (destPixelCoord.x & 1) + ((destPixelCoord.y) & 1)*2; + ivec2 sourcePixelCoord = destPixelCoord / SSAO_SPLIT_COUNT; + ivec2 splitImageSize = getWidthHeightRoundUp(getResolutionLevel()+SSAO_SPLIT_LOG2_COUNT); + ivec2 modPixelCoord = destPixelCoord % ivec2(SSAO_SPLIT_COUNT); + int occlusionMapIndex = modPixelCoord.x + modPixelCoord.y*SSAO_SPLIT_COUNT; vec2 sourceUV = (sourcePixelCoord + vec2(0.5)) / splitImageSize; - - sourcePixelCoord += (destPixelCoord & ivec2(1)) * splitImageSize; outFragColor = texture(occlusionMaps, vec3(sourceUV, occlusionMapIndex)); } diff --git a/libraries/render-utils/src/ssao_shared.h b/libraries/render-utils/src/ssao_shared.h index 11c7f894d5..7070d793ed 100644 --- a/libraries/render-utils/src/ssao_shared.h +++ b/libraries/render-utils/src/ssao_shared.h @@ -18,10 +18,11 @@ #define SSAO_USE_QUAD_SPLIT 1 #if SSAO_USE_QUAD_SPLIT -#define SSAO_SPLIT_COUNT 4 +#define SSAO_SPLIT_LOG2_COUNT 2 #else -#define SSAO_SPLIT_COUNT 1 +#define SSAO_SPLIT_LOG2_COUNT 0 #endif +#define SSAO_SPLIT_COUNT (1 << SSAO_SPLIT_LOG2_COUNT) // glsl / C++ compatible source as interface for ambient occlusion #ifdef __cplusplus From 94a162893aa154f82a29d0c808290314ae4c7aba Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Thu, 27 Sep 2018 10:44:06 +0200 Subject: [PATCH 046/286] Finally, a clean HBAO! --- .../src/AmbientOcclusionEffect.cpp | 124 +++++++++++------- .../render-utils/src/AmbientOcclusionEffect.h | 12 +- libraries/render-utils/src/ssao.slh | 74 ++++++----- libraries/render-utils/src/ssao_gather.slf | 10 +- .../src/ssao_makeHorizontalBlur.slf | 3 +- .../src/ssao_makeVerticalBlur.slf | 6 +- libraries/render-utils/src/ssao_shared.h | 10 +- 7 files changed, 134 insertions(+), 105 deletions(-) diff --git a/libraries/render-utils/src/AmbientOcclusionEffect.cpp b/libraries/render-utils/src/AmbientOcclusionEffect.cpp index 4c1ff6fe09..1dbabb7685 100644 --- a/libraries/render-utils/src/AmbientOcclusionEffect.cpp +++ b/libraries/render-utils/src/AmbientOcclusionEffect.cpp @@ -222,7 +222,7 @@ AmbientOcclusionEffectConfig::AmbientOcclusionEffectConfig() : #else numSamples{ 16 }, #endif - resolutionLevel{ 1 }, + resolutionLevel{ 2 }, blurRadius{ 4 }, ditheringEnabled{ true }, borderingEnabled{ true }, @@ -235,6 +235,9 @@ AmbientOcclusionEffect::AOParameters::AOParameters() { _radiusInfo = { 0.5f, 0.5f * 0.5f, 1.0f / (0.25f * 0.25f * 0.25f), 1.0f }; _ditheringInfo = { 0.0f, 0.0f, 0.01f, 1.0f }; _sampleInfo = { 11.0f, 1.0f / 11.0f, 7.0f, 1.0f }; +} + +AmbientOcclusionEffect::BlurParameters::BlurParameters() { _blurInfo = { 1.0f, 3.0f, 2.0f, 0.0f }; } @@ -269,14 +272,11 @@ void AmbientOcclusionEffect::configure(const Config& config) { current.y = 1.0f / (1.0f - config.falloffAngle); } - if (config.edgeSharpness != _aoParametersBuffer->getEdgeSharpness()) { - auto& current = _aoParametersBuffer.edit()._blurInfo; - current.x = config.edgeSharpness; - } - - if (config.blurDeviation != _aoParametersBuffer->getBlurDeviation()) { - auto& current = _aoParametersBuffer.edit()._blurInfo; - current.z = config.blurDeviation; + if (config.edgeSharpness != _hblurParametersBuffer.get().getEdgeSharpness()) { + auto& hblur = _hblurParametersBuffer.edit()._blurInfo; + auto& vblur = _vblurParametersBuffer.edit()._blurInfo; + hblur.x = config.edgeSharpness; + vblur.x = config.edgeSharpness; } if (config.numSpiralTurns != _aoParametersBuffer->getNumSpiralTurns()) { @@ -327,9 +327,11 @@ void AmbientOcclusionEffect::configure(const Config& config) { shouldUpdateBlurs = true; } - if (config.blurRadius != _aoParametersBuffer.get().getBlurRadius()) { - auto& current = _aoParametersBuffer.edit()._blurInfo; - current.y = (float)config.blurRadius; + if (config.blurRadius != _hblurParametersBuffer.get().getBlurRadius()) { + auto& hblur = _hblurParametersBuffer.edit()._blurInfo; + auto& vblur = _vblurParametersBuffer.edit()._blurInfo; + hblur.y = (float)config.blurRadius; + vblur.y = (float)config.blurRadius; } if (config.ditheringEnabled != _aoParametersBuffer->isDitheringEnabled()) { @@ -353,31 +355,47 @@ void AmbientOcclusionEffect::updateBlurParameters() { auto& vblur = _vblurParametersBuffer.edit(); auto& hblur = _hblurParametersBuffer.edit(); auto frameSize = _framebuffer->getSourceFrameSize(); + if (_framebuffer->isStereo()) { + frameSize.x >>= 1; + } + const auto occlusionSize = divideRoundUp(frameSize, resolutionScale); - hblur.scaleHeight.x = 1.0f / frameSize.x; - hblur.scaleHeight.y = float(resolutionScale) / frameSize.x; - hblur.scaleHeight.z = frameSize.y / resolutionScale; + // Occlusion UV limit + hblur._blurInfo.z = occlusionSize.x / float(frameSize.x); + hblur._blurInfo.w = occlusionSize.y / float(frameSize.y); - vblur.scaleHeight.x = 1.0f / frameSize.y; - vblur.scaleHeight.y = float(resolutionScale) / frameSize.y; - vblur.scaleHeight.z = frameSize.y; + vblur._blurInfo.z = 1.0f; + vblur._blurInfo.w = occlusionSize.y / float(frameSize.y); + + // Depth axis + hblur._blurAxis.x = 1.0f / occlusionSize.x; + hblur._blurAxis.y = 0.0f; + + vblur._blurAxis.x = 0.0f; + vblur._blurAxis.y = 1.0f / occlusionSize.y; + + // Occlusion axis + hblur._blurAxis.z = hblur._blurAxis.x * hblur._blurInfo.z; + hblur._blurAxis.w = 0.0f; + + vblur._blurAxis.z = 0.0f; + vblur._blurAxis.w = vblur._blurAxis.y * vblur._blurInfo.w; } void AmbientOcclusionEffect::updateFramebufferSizes() { auto& params = _aoParametersBuffer.edit(); const int widthScale = _framebuffer->isStereo() & 1; - auto sourceFrameSize = _framebuffer->getSourceFrameSize(); - const int resolutionLevel = _aoParametersBuffer.get().getResolutionLevel(); - const float resolutionScale = powf(0.5f, resolutionLevel); - // Depth is at maximum half depth - const int depthResolutionLevel = std::min(1, resolutionLevel); - const float depthResolutionScale = powf(2.0f, depthResolutionLevel); - auto normalTextureSize = _framebuffer->getNormalTexture()->getDimensions(); - auto occlusionDepthFrameSize = divideRoundUp(sourceFrameSize, depthResolutionLevel); + auto sourceFrameSideSize = _framebuffer->getSourceFrameSize(); + sourceFrameSideSize.x >>= widthScale; + + const int resolutionLevel = _aoParametersBuffer.get().getResolutionLevel(); + // Depth is at maximum half depth + const int depthResolutionLevel = getDepthResolutionLevel(); + const auto occlusionDepthFrameSize = divideRoundUp(sourceFrameSideSize, 1 << depthResolutionLevel); + const auto occlusionFrameSize = divideRoundUp(sourceFrameSideSize, 1 << resolutionLevel); + auto normalTextureSize = _framebuffer->getNormalTexture()->getDimensions(); - sourceFrameSize.x >>= widthScale; normalTextureSize.x >>= widthScale; - occlusionDepthFrameSize.x >>= widthScale; params._sideSizes[0].x = normalTextureSize.x; params._sideSizes[0].y = normalTextureSize.y; @@ -386,7 +404,7 @@ void AmbientOcclusionEffect::updateFramebufferSizes() { params._sideSizes[1].x = params._sideSizes[0].x; params._sideSizes[1].y = params._sideSizes[0].y; - auto occlusionSplitSize = divideRoundUp(sourceFrameSize, 1 << (resolutionLevel + SSAO_USE_QUAD_SPLIT)); + auto occlusionSplitSize = divideRoundUp(occlusionFrameSize, SSAO_SPLIT_COUNT); params._sideSizes[1].z = occlusionSplitSize.x; params._sideSizes[1].w = occlusionSplitSize.y; } @@ -491,7 +509,7 @@ void AmbientOcclusionEffect::run(const render::RenderContextPointer& renderConte // We need to take the rounded up resolution. auto occlusionViewport = divideRoundUp(sourceViewport, 1 << resolutionLevel); auto firstBlurViewport = sourceViewport; - firstBlurViewport.w = divideRoundUp(firstBlurViewport.w, 1 << resolutionLevel); + firstBlurViewport.w = occlusionViewport.w; if (!_gpuTimer) { _gpuTimer = std::make_shared < gpu::RangeTimer>(__FUNCTION__); @@ -550,9 +568,8 @@ void AmbientOcclusionEffect::run(const render::RenderContextPointer& renderConte batch.resetViewTransform(); - Transform model; batch.setProjectionTransform(glm::mat4()); - batch.setModelTransform(model); + batch.setModelTransform(Transform()); batch.pushProfileRange("Depth Mip Generation"); // We need this with the mips levels @@ -578,11 +595,12 @@ void AmbientOcclusionEffect::run(const render::RenderContextPointer& renderConte postPixelOffset.y - prePixelOffset.y, 0.0f ); + Transform model; model.setScale(uvScale); model.setTranslation(uvTranslate); + batch.setModelTransform(model); } - batch.setModelTransform(model); // Build face normals pass batch.setViewportTransform(normalViewport); @@ -608,11 +626,12 @@ void AmbientOcclusionEffect::run(const render::RenderContextPointer& renderConte batch.setResourceTexture(render_utils::slot::texture::SsaoNormal, occlusionNormalTexture); { const auto uvScale = glm::vec3( - (splitSize.x * SSAO_SPLIT_COUNT * depthResolutionScale) / (occlusionDepthSize.x * resolutionScale), - (splitSize.y * SSAO_SPLIT_COUNT * depthResolutionScale) / (occlusionDepthSize.y * resolutionScale), + (splitSize.x * SSAO_SPLIT_COUNT) / float(occlusionViewport.z), + (splitSize.y * SSAO_SPLIT_COUNT) / float(occlusionViewport.w), 1.0f); - const auto postPixelOffset = glm::vec2(0.5f) / glm::vec2(occlusionDepthSize); - const auto prePixelOffset = glm::vec2(0.5f * uvScale.x, 0.5f * uvScale.y) / glm::vec2(splitSize.x, splitSize.y); + const auto postPixelOffset = glm::vec2(0.5f) / glm::vec2(occlusionViewport.z, occlusionViewport.w); + const auto prePixelOffset = glm::vec2(0.5f * uvScale.x, 0.5f * uvScale.y) / glm::vec2(splitSize); + Transform model; batch.setViewportTransform(splitViewport); @@ -635,13 +654,7 @@ void AmbientOcclusionEffect::run(const render::RenderContextPointer& renderConte } #else batch.setViewportTransform(occlusionViewport); - { - const auto uvScale = glm::vec3( - (occlusionViewport.z * depthResolutionScale) / (occlusionDepthSize.x * resolutionScale), - (occlusionViewport.w * depthResolutionScale) / (occlusionDepthSize.y * resolutionScale), - 1.0f); - model.setScale(uvScale); - } + model.setIdentity(); batch.setModelTransform(model); batch.setFramebuffer(occlusionFBO); batch.setUniformBuffer(render_utils::slot::buffer::SsaoFrameParams, _aoFrameParametersBuffer[0]); @@ -666,8 +679,15 @@ void AmbientOcclusionEffect::run(const render::RenderContextPointer& renderConte PROFILE_RANGE_BATCH(batch, "Bilateral Blur"); // Blur 1st pass batch.pushProfileRange("Horizontal"); - model.setScale(resolutionScale); - batch.setModelTransform(model); + { + const auto uvScale = glm::vec3( + occlusionViewport.z / float(sourceViewport.z), + occlusionViewport.w / float(sourceViewport.w), + 1.0f); + Transform model; + model.setScale(uvScale); + batch.setModelTransform(model); + } batch.setViewportTransform(firstBlurViewport); batch.setFramebuffer(occlusionBlurredFBO); // Use full resolution depth and normal for bilateral upscaling and blur @@ -680,8 +700,16 @@ void AmbientOcclusionEffect::run(const render::RenderContextPointer& renderConte // Blur 2nd pass batch.pushProfileRange("Vertical"); - model.setScale(glm::vec3(1.0f, resolutionScale, 1.0f)); - batch.setModelTransform(model); + { + const auto uvScale = glm::vec3( + 1.0f, + occlusionViewport.w / float(sourceViewport.w), + 1.0f); + + Transform model; + model.setScale(uvScale); + batch.setModelTransform(model); + } batch.setViewportTransform(sourceViewport); batch.setFramebuffer(occlusionFBO); batch.setUniformBuffer(render_utils::slot::buffer::SsaoBlurParams, _vblurParametersBuffer); diff --git a/libraries/render-utils/src/AmbientOcclusionEffect.h b/libraries/render-utils/src/AmbientOcclusionEffect.h index d84a8636ef..63ba82cf83 100644 --- a/libraries/render-utils/src/AmbientOcclusionEffect.h +++ b/libraries/render-utils/src/AmbientOcclusionEffect.h @@ -149,14 +149,11 @@ public: float getPerspectiveScale() const { return _resolutionInfo.z; } float getObscuranceLevel() const { return _radiusInfo.w; } float getFalloffAngle() const { return (float)_ditheringInfo.z; } - float getEdgeSharpness() const { return (float)_blurInfo.x; } - float getBlurDeviation() const { return _blurInfo.z; } float getNumSpiralTurns() const { return _sampleInfo.z; } int getNumSamples() const { return (int)_sampleInfo.x; } bool isFetchMipsEnabled() const { return _sampleInfo.w; } - int getBlurRadius() const { return (int)_blurInfo.y; } bool isDitheringEnabled() const { return _ditheringInfo.x; } bool isBorderingEnabled() const { return _ditheringInfo.w; } }; @@ -165,11 +162,14 @@ public: private: // Class describing the uniform buffer with all the parameters common to the bilateral blur shaders - class BlurParameters { + class BlurParameters : public AmbientOcclusionBlurParams { public: - glm::vec4 scaleHeight{ 0.0f }; - BlurParameters() {} + BlurParameters(); + + float getEdgeSharpness() const { return (float)_blurInfo.x; } + int getBlurRadius() const { return (int)_blurInfo.y; } + }; using BlurParametersBuffer = gpu::StructBuffer; diff --git a/libraries/render-utils/src/ssao.slh b/libraries/render-utils/src/ssao.slh index b3f66abb35..ebe00ee740 100644 --- a/libraries/render-utils/src/ssao.slh +++ b/libraries/render-utils/src/ssao.slh @@ -115,14 +115,6 @@ int doFetchMips() { return int(params._sampleInfo.w); } -float getBlurEdgeSharpness() { - return params._blurInfo.x; -} - -int getBlurRadius() { - return int(params._blurInfo.y); -} - <@endfunc@> <@func declareSamplingDisk()@> @@ -247,6 +239,11 @@ float getZEyeAtUV(vec2 texCoord, int level) { return -textureLod(depthPyramidTex, texCoord, level).x; } +float getZEyeAtUV(ivec4 side, vec2 texCoord, int level) { + texCoord.x = mix(texCoord.x, (texCoord.x + getStereoSide(side)) * 0.5, isStereo()); + return getZEyeAtUV(texCoord, level); +} + vec3 getNormalEyeAtUV(vec2 texCoord, int level) { return normalize(textureLod(normalTex, texCoord, level).xyz*2.0 - vec3(1.0)); } @@ -417,37 +414,42 @@ float evalVisibilityHBAO(ivec4 side, vec2 fragUVPos, vec2 invSideImageSize, vec2 <@endfunc@> -<@func declareBlurPass(axis)@> +<@func declareBlurPass()@> -<$declarePackOcclusionDepth()$> <$declareAmbientOcclusion()$> <$declareFetchDepthPyramidMap()$> +<$declarePackOcclusionDepth()$> // the source occlusion texture layout(binding=RENDER_UTILS_TEXTURE_SSAO_OCCLUSION) uniform sampler2D occlusionMap; -struct BlurParams { - vec4 scaleHeight; -}; - layout(binding=RENDER_UTILS_BUFFER_SSAO_BLUR_PARAMS) uniform blurParamsBuffer { - BlurParams blurParams; + AmbientOcclusionBlurParams blurParams; }; -float getOcclusionBlurScale() { - return blurParams.scaleHeight.x; +vec2 getBlurOcclusionAxis() { + return blurParams._blurAxis.zw; } -float getDepthBlurScale() { - return blurParams.scaleHeight.y; +vec2 getBlurDepthAxis() { + return blurParams._blurAxis.xy; } -int getBlurImageHeight() { - return int(blurParams.scaleHeight.z); +vec2 getBlurOcclusionUVLimit() { + return blurParams._blurInfo.zw; } -float fetchOcclusion(vec2 coords) { - vec3 raw = texture(occlusionMap, coords, 0).xyz; +float getBlurEdgeSharpness() { + return blurParams._blurInfo.x; +} + +int getBlurRadius() { + return int(blurParams._blurInfo.y); +} + +float fetchOcclusion(ivec4 side, vec2 texCoord) { + texCoord.x = mix(texCoord.x, (texCoord.x + getStereoSide(side)) * 0.5, isStereo()); + vec3 raw = textureLod(occlusionMap, texCoord, 0).xyz; return raw.x; } @@ -458,19 +460,19 @@ float evalBlurCoefficient(vec2 blurScales, float radialDistance, float zDistance return exp2(dot(blurScales, distances*distances)); } -vec2 evalTapWeightedValue(vec2 blurScales, ivec3 side, int r, ivec2 destPixelCoord, vec2 scaledTexCoord, vec2 fullTexCoord, float fragDepth) { - ivec2 tapOffset = <$axis$> * r; - ivec2 tapPixelCoord = destPixelCoord + ivec2(tapOffset); +vec2 evalTapWeightedValue(vec2 blurScales, ivec4 side, int r, vec2 occlusionTexCoord, vec2 depthTexCoord, float fragDepth) { + vec2 tapOcclusionTexCoord = getBlurOcclusionAxis() * r + occlusionTexCoord; + vec2 occlusionTexCoordLimits = getBlurOcclusionUVLimit(); - if ((tapPixelCoord.x < side.y || tapPixelCoord.x >= side.z + side.y) || (tapPixelCoord.y < 0 || tapPixelCoord.y >= getBlurImageHeight())) { + if (tapOcclusionTexCoord.x < side.x || tapOcclusionTexCoord.x >= (side.x + occlusionTexCoordLimits.x) + || tapOcclusionTexCoord.y < 0 || tapOcclusionTexCoord.y >= occlusionTexCoordLimits.y) { return vec2(0.0); } - vec2 tapTexCoord = scaledTexCoord + tapOffset * getOcclusionBlurScale(); - float tapOcclusion = fetchOcclusion(tapTexCoord); + float tapOcclusion = fetchOcclusion(side, tapOcclusionTexCoord); - tapTexCoord = fullTexCoord + tapOffset * getDepthBlurScale(); - float tapDepth = getZEyeAtUV(tapTexCoord, 0); + vec2 tapDepthTexCoord = getBlurDepthAxis() * r + depthTexCoord; + float tapDepth = getZEyeAtUV(side, tapDepthTexCoord, 0); // range domain (the "bilateral" weight). As depth difference increases, decrease weight. float zDistance = tapDepth - fragDepth; @@ -479,11 +481,11 @@ vec2 evalTapWeightedValue(vec2 blurScales, ivec3 side, int r, ivec2 destPixelCoo return vec2(tapOcclusion * weight, weight); } -vec3 getBlurredOcclusion(ivec2 destPixelCoord, vec2 scaledTexCoord, vec2 fullTexCoord) { +vec3 getBlurredOcclusion(ivec2 destPixelCoord, vec2 occlusionTexCoord, vec2 depthTexCoord) { // Stereo side info ivec4 side = getStereoSideInfo(destPixelCoord.x, 0); - float fragDepth = getZEyeAtUV(fullTexCoord, 0); + float fragDepth = getZEyeAtUV(side, depthTexCoord, 0); vec2 weightedSums = vec2(0.0); // Accumulate weighted contributions along the bluring axis in the [-radius, radius] range @@ -494,17 +496,17 @@ vec3 getBlurredOcclusion(ivec2 destPixelCoord, vec2 scaledTexCoord, vec2 fullTex // negative side first for (int r = -blurRadius; r <= -1; ++r) { - weightedSums += evalTapWeightedValue(blurScales, side.xyz, r, destPixelCoord, scaledTexCoord, fullTexCoord, fragDepth); + weightedSums += evalTapWeightedValue(blurScales, side, r, occlusionTexCoord, depthTexCoord, fragDepth); } // Central pixel contribution float mainWeight = 1.0; - float pixelOcclusion = fetchOcclusion(scaledTexCoord); + float pixelOcclusion = fetchOcclusion(side, occlusionTexCoord); weightedSums += vec2(pixelOcclusion * mainWeight, mainWeight); // then positive side for (int r = 1; r <= blurRadius; ++r) { - weightedSums += evalTapWeightedValue(blurScales, side.xyz, r, destPixelCoord, scaledTexCoord, fullTexCoord, fragDepth); + weightedSums += evalTapWeightedValue(blurScales, side, r, occlusionTexCoord, depthTexCoord, fragDepth); } // Final normalization diff --git a/libraries/render-utils/src/ssao_gather.slf b/libraries/render-utils/src/ssao_gather.slf index 64cbbdd0c7..aaad3a5798 100644 --- a/libraries/render-utils/src/ssao_gather.slf +++ b/libraries/render-utils/src/ssao_gather.slf @@ -28,11 +28,9 @@ void main(void) { // Gather the four splits of the occlusion result back into an interleaved full size // result (at the resolution level, of course) ivec2 destPixelCoord = ivec2(gl_FragCoord.xy); - ivec2 sourcePixelCoord = destPixelCoord / SSAO_SPLIT_COUNT; - ivec2 splitImageSize = getWidthHeightRoundUp(getResolutionLevel()+SSAO_SPLIT_LOG2_COUNT); - ivec2 modPixelCoord = destPixelCoord % ivec2(SSAO_SPLIT_COUNT); - int occlusionMapIndex = modPixelCoord.x + modPixelCoord.y*SSAO_SPLIT_COUNT; - vec2 sourceUV = (sourcePixelCoord + vec2(0.5)) / splitImageSize; + ivec2 sourcePixelCoord = destPixelCoord >> SSAO_SPLIT_LOG2_COUNT; + ivec2 modPixelCoord = destPixelCoord & (SSAO_SPLIT_COUNT-1); + int occlusionMapIndex = modPixelCoord.x + (modPixelCoord.y << SSAO_SPLIT_LOG2_COUNT); - outFragColor = texture(occlusionMaps, vec3(sourceUV, occlusionMapIndex)); + outFragColor = texelFetch(occlusionMaps, ivec3(sourcePixelCoord, occlusionMapIndex), 0); } diff --git a/libraries/render-utils/src/ssao_makeHorizontalBlur.slf b/libraries/render-utils/src/ssao_makeHorizontalBlur.slf index fc90052eed..e535398241 100644 --- a/libraries/render-utils/src/ssao_makeHorizontalBlur.slf +++ b/libraries/render-utils/src/ssao_makeHorizontalBlur.slf @@ -15,8 +15,7 @@ // Hack comment -const ivec2 horizontal = ivec2(1,0); -<$declareBlurPass(horizontal)$> +<$declareBlurPass()$> layout(location=0) in vec4 varTexCoord0; diff --git a/libraries/render-utils/src/ssao_makeVerticalBlur.slf b/libraries/render-utils/src/ssao_makeVerticalBlur.slf index 69b92000de..15550a73cd 100644 --- a/libraries/render-utils/src/ssao_makeVerticalBlur.slf +++ b/libraries/render-utils/src/ssao_makeVerticalBlur.slf @@ -14,14 +14,12 @@ // Hack comment -const ivec2 vertical = ivec2(0,1); -<$declareBlurPass(vertical)$> +<$declareBlurPass()$> layout(location=0) in vec4 varTexCoord0; layout(location=0) out vec4 outFragColor; void main(void) { - float occlusion = getBlurredOcclusion(ivec2(gl_FragCoord.xy), varTexCoord0.xy, varTexCoord0.zw).x; - outFragColor = vec4(occlusion, 0.0, 0.0, occlusion); + outFragColor = vec4(getBlurredOcclusion(ivec2(gl_FragCoord.xy), varTexCoord0.xy, varTexCoord0.zw), 1.0); } diff --git a/libraries/render-utils/src/ssao_shared.h b/libraries/render-utils/src/ssao_shared.h index 7070d793ed..c331dabd7a 100644 --- a/libraries/render-utils/src/ssao_shared.h +++ b/libraries/render-utils/src/ssao_shared.h @@ -27,10 +27,10 @@ // glsl / C++ compatible source as interface for ambient occlusion #ifdef __cplusplus # define SSAO_VEC4 glm::vec4 -# define SSAO_IVEC4 glm::ivec4 +# define SSAO_MAT4 glm::mat4 #else # define SSAO_VEC4 vec4 -# define SSAO_IVEC4 ivec4 +# define SSAO_MAT4 mat4 #endif struct AmbientOcclusionParams { @@ -38,7 +38,6 @@ struct AmbientOcclusionParams { SSAO_VEC4 _radiusInfo; SSAO_VEC4 _ditheringInfo; SSAO_VEC4 _sampleInfo; - SSAO_VEC4 _blurInfo; SSAO_VEC4 _sideSizes[2]; }; @@ -46,6 +45,11 @@ struct AmbientOcclusionFrameParams { SSAO_VEC4 _angleInfo; }; +struct AmbientOcclusionBlurParams { + SSAO_VEC4 _blurInfo; + SSAO_VEC4 _blurAxis; +}; + #endif // RENDER_UTILS_SHADER_CONSTANTS_H // <@if 1@> From 349a8b39adaa786c98f3a6a6a1e667a3620350a8 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Thu, 27 Sep 2018 16:39:42 +0200 Subject: [PATCH 047/286] Added config parameter to switch between HBAO / SSAO --- .../src/AmbientOcclusionEffect.cpp | 50 ++++++++----------- .../render-utils/src/AmbientOcclusionEffect.h | 8 ++- libraries/render-utils/src/ssao.slh | 4 ++ .../render-utils/src/ssao_makeOcclusion.slf | 40 +++++++-------- libraries/render-utils/src/ssao_shared.h | 1 - .../utilities/render/ambientOcclusionPass.qml | 1 + 6 files changed, 51 insertions(+), 53 deletions(-) diff --git a/libraries/render-utils/src/AmbientOcclusionEffect.cpp b/libraries/render-utils/src/AmbientOcclusionEffect.cpp index 1dbabb7685..b56da99ee6 100644 --- a/libraries/render-utils/src/AmbientOcclusionEffect.cpp +++ b/libraries/render-utils/src/AmbientOcclusionEffect.cpp @@ -202,36 +202,24 @@ gpu::TexturePointer AmbientOcclusionFramebuffer::getNormalTexture() { AmbientOcclusionEffectConfig::AmbientOcclusionEffectConfig() : render::GPUJobConfig::Persistent(QStringList() << "Render" << "Engine" << "Ambient Occlusion", false), -#if SSAO_USE_HORIZON_BASED radius{ 0.3f }, -#else - radius{ 0.5f }, -#endif perspectiveScale{ 1.0f }, obscuranceLevel{ 0.5f }, -#if SSAO_USE_HORIZON_BASED falloffAngle{ 0.3f }, -#else - falloffAngle{ 0.01f }, -#endif edgeSharpness{ 1.0f }, blurDeviation{ 2.5f }, numSpiralTurns{ 7.0f }, -#if SSAO_USE_HORIZON_BASED numSamples{ 1 }, -#else - numSamples{ 16 }, -#endif resolutionLevel{ 2 }, blurRadius{ 4 }, ditheringEnabled{ true }, borderingEnabled{ true }, - fetchMipsEnabled{ true } { - + fetchMipsEnabled{ true }, + horizonBased{ true } { } AmbientOcclusionEffect::AOParameters::AOParameters() { - _resolutionInfo = { -1.0f, 0.0f, 1.0f, 0.0f }; + _resolutionInfo = { -1.0f, 1.0f, 1.0f, 0.0f }; _radiusInfo = { 0.5f, 0.5f * 0.5f, 1.0f / (0.25f * 0.25f * 0.25f), 1.0f }; _ditheringInfo = { 0.0f, 0.0f, 0.01f, 1.0f }; _sampleInfo = { 11.0f, 1.0f / 11.0f, 7.0f, 1.0f }; @@ -251,14 +239,19 @@ void AmbientOcclusionEffect::configure(const Config& config) { const double RADIUS_POWER = 6.0; const auto& radius = config.radius; - if (radius != _aoParametersBuffer->getRadius()) { + if (radius != _aoParametersBuffer->getRadius() || config.horizonBased != _aoParametersBuffer->isHorizonBased()) { auto& current = _aoParametersBuffer.edit()._radiusInfo; current.x = radius; current.y = radius * radius; current.z = 10.0f; -#if !SSAO_USE_HORIZON_BASED - current.z *= (float)(1.0 / pow((double)radius, RADIUS_POWER)); -#endif + if (!config.horizonBased) { + current.z *= (float)(1.0 / pow((double)radius, RADIUS_POWER)); + } + } + + if (config.horizonBased != _aoParametersBuffer->isHorizonBased()) { + auto& current = _aoParametersBuffer.edit()._resolutionInfo; + current.y = config.horizonBased & 1; } if (config.obscuranceLevel != _aoParametersBuffer->getObscuranceLevel()) { @@ -501,6 +494,7 @@ void AmbientOcclusionEffect::run(const render::RenderContextPointer& renderConte const auto depthResolutionLevel = getDepthResolutionLevel(); const auto depthResolutionScale = powf(0.5f, depthResolutionLevel); + const auto isHorizonBased = _aoParametersBuffer->isHorizonBased(); auto linearDepthTexture = linearDepthFramebuffer->getLinearDepthTexture(); auto occlusionDepthTexture = linearDepthTexture; @@ -538,9 +532,7 @@ void AmbientOcclusionEffect::run(const render::RenderContextPointer& renderConte auto occlusionPipeline = getOcclusionPipeline(); auto firstHBlurPipeline = getHBlurPipeline(); auto lastVBlurPipeline = getVBlurPipeline(); -#if SSAO_USE_HORIZON_BASED auto mipCreationPipeline = getMipCreationPipeline(); -#endif #if SSAO_USE_QUAD_SPLIT auto gatherPipeline = getGatherPipeline(); auto buildNormalsPipeline = getBuildNormalsPipeline(); @@ -571,14 +563,14 @@ void AmbientOcclusionEffect::run(const render::RenderContextPointer& renderConte batch.setProjectionTransform(glm::mat4()); batch.setModelTransform(Transform()); - batch.pushProfileRange("Depth Mip Generation"); // We need this with the mips levels -#if SSAO_USE_HORIZON_BASED - batch.setPipeline(mipCreationPipeline); - batch.generateTextureMipsWithPipeline(occlusionDepthTexture); -#else - batch.generateTextureMips(occlusionDepthTexture); -#endif + batch.pushProfileRange("Depth Mip Generation"); + if (isHorizonBased) { + batch.setPipeline(mipCreationPipeline); + batch.generateTextureMipsWithPipeline(occlusionDepthTexture); + } else { + batch.generateTextureMips(occlusionDepthTexture); + } batch.popProfileRange(); #if SSAO_USE_QUAD_SPLIT @@ -731,8 +723,6 @@ void AmbientOcclusionEffect::run(const render::RenderContextPointer& renderConte config->setGPUBatchRunTime(_gpuTimer->getGPUAverage(), _gpuTimer->getBatchAverage()); } - - DebugAmbientOcclusion::DebugAmbientOcclusion() { } diff --git a/libraries/render-utils/src/AmbientOcclusionEffect.h b/libraries/render-utils/src/AmbientOcclusionEffect.h index 63ba82cf83..4ace549e9e 100644 --- a/libraries/render-utils/src/AmbientOcclusionEffect.h +++ b/libraries/render-utils/src/AmbientOcclusionEffect.h @@ -79,6 +79,7 @@ using AmbientOcclusionFramebufferPointer = std::shared_ptr; diff --git a/libraries/render-utils/src/ssao.slh b/libraries/render-utils/src/ssao.slh index ebe00ee740..bc7473a3ba 100644 --- a/libraries/render-utils/src/ssao.slh +++ b/libraries/render-utils/src/ssao.slh @@ -51,6 +51,10 @@ int getResolutionLevel() { return int(params._resolutionInfo.x); } +bool isHorizonBased() { + return params._resolutionInfo.y!=0.0; +} + vec2 getNormalsSideSize() { return params._sideSizes[0].xy; } diff --git a/libraries/render-utils/src/ssao_makeOcclusion.slf b/libraries/render-utils/src/ssao_makeOcclusion.slf index 0a34b0df03..e2a94be134 100644 --- a/libraries/render-utils/src/ssao_makeOcclusion.slf +++ b/libraries/render-utils/src/ssao_makeOcclusion.slf @@ -51,9 +51,9 @@ void main(void) { // Choose the screen-space sample radius float diskPixelRadius = evalDiskRadius(fragPositionES.z, sideDepthSize); -#if SSAO_USE_HORIZON_BASED - diskPixelRadius = min(diskPixelRadius, SSAO_HBAO_MAX_RADIUS); -#endif + if (isHorizonBased()) { + diskPixelRadius = min(diskPixelRadius, SSAO_HBAO_MAX_RADIUS); + } // Let's make noise float randomPatternRotationAngle = getAngleDithering(fragPixelPos); @@ -63,24 +63,24 @@ void main(void) { int numSamples = int(getNumSamples()); float invNumSamples = getInvNumSamples(); - // Steps are in the depth texture resolution - vec2 depthTexFragPixelPos = fragUVPos * sideDepthSize; - for (int i = 0; i < numSamples; ++i) { -#if SSAO_USE_HORIZON_BASED - vec3 deltaTap = getUnitTapLocation(i, 1.0, randomPatternRotationAngle, PI); - obscuranceSum += evalVisibilityHBAO(side, fragUVPos, deltaDepthUV, deltaTap.xy, diskPixelRadius, fragPositionES, fragNormalES); -#else - vec3 tap = getTapLocationClampedSSAO(i, randomPatternRotationAngle, diskPixelRadius, depthTexFragPixelPos, sideDepthSize); - vec2 tapUV = fragUVPos + tap.xy * deltaDepthUV; - vec2 tapMipZ = fetchTapWithUV(side, tapUV, tap.z); - vec3 tapPositionES = evalEyePositionFromZeye(side.x, tapMipZ.y, tapUV); - obscuranceSum += float(tap.z > 0.0) * evalVisibilitySSAO(fragPositionES, fragNormalES, tapPositionES); -#endif - } + if (isHorizonBased()) { + for (int i = 0; i < numSamples; ++i) { + vec3 deltaTap = getUnitTapLocation(i, 1.0, randomPatternRotationAngle, PI); + obscuranceSum += evalVisibilityHBAO(side, fragUVPos, deltaDepthUV, deltaTap.xy, diskPixelRadius, fragPositionES, fragNormalES); + } + obscuranceSum *= 0.5 / PI; + } else { + // Steps are in the depth texture resolution + vec2 depthTexFragPixelPos = fragUVPos * sideDepthSize; -#if SSAO_USE_HORIZON_BASED - obscuranceSum *= 0.5 / PI; -#endif + for (int i = 0; i < numSamples; ++i) { + vec3 tap = getTapLocationClampedSSAO(i, randomPatternRotationAngle, diskPixelRadius, depthTexFragPixelPos, sideDepthSize); + vec2 tapUV = fragUVPos + tap.xy * deltaDepthUV; + vec2 tapMipZ = fetchTap(side, tapUV, tap.z); + vec3 tapPositionES = evalEyePositionFromZeye(side.x, tapMipZ.y, tapUV); + obscuranceSum += float(tap.z > 0.0) * evalVisibilitySSAO(fragPositionES, fragNormalES, tapPositionES); + } + } float occlusion = clamp(1.0 - obscuranceSum * getObscuranceScaling() * invNumSamples, 0.0, 1.0); diff --git a/libraries/render-utils/src/ssao_shared.h b/libraries/render-utils/src/ssao_shared.h index c331dabd7a..9c9e57337c 100644 --- a/libraries/render-utils/src/ssao_shared.h +++ b/libraries/render-utils/src/ssao_shared.h @@ -14,7 +14,6 @@ #ifndef RENDER_UTILS_SSAO_SHARED_H #define RENDER_UTILS_SSAO_SHARED_H -#define SSAO_USE_HORIZON_BASED 1 #define SSAO_USE_QUAD_SPLIT 1 #if SSAO_USE_QUAD_SPLIT diff --git a/scripts/developer/utilities/render/ambientOcclusionPass.qml b/scripts/developer/utilities/render/ambientOcclusionPass.qml index 141832e202..5197fc312d 100644 --- a/scripts/developer/utilities/render/ambientOcclusionPass.qml +++ b/scripts/developer/utilities/render/ambientOcclusionPass.qml @@ -58,6 +58,7 @@ Rectangle { Column { Repeater { model: [ + "horizonBased:horizonBased", "ditheringEnabled:ditheringEnabled", "fetchMipsEnabled:fetchMipsEnabled", "borderingEnabled:borderingEnabled" From 199809663050202472a26be68d46c1d9f7369609 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Thu, 27 Sep 2018 17:03:14 +0200 Subject: [PATCH 048/286] On single bilateral blur shader --- .../src/AmbientOcclusionEffect.cpp | 33 +++++-------------- .../render-utils/src/AmbientOcclusionEffect.h | 6 ++-- .../src/render-utils/ssao_bilateralBlur.slp | 1 + .../render-utils/ssao_makeHorizontalBlur.slp | 1 - .../render-utils/ssao_makeVerticalBlur.slp | 1 - ...erticalBlur.slf => ssao_bilateralBlur.slf} | 3 +- .../{ssao_blur.slv => ssao_bilateralBlur.slv} | 2 +- .../src/ssao_makeHorizontalBlur.slf | 26 --------------- 8 files changed, 14 insertions(+), 59 deletions(-) create mode 100644 libraries/render-utils/src/render-utils/ssao_bilateralBlur.slp delete mode 100644 libraries/render-utils/src/render-utils/ssao_makeHorizontalBlur.slp delete mode 100644 libraries/render-utils/src/render-utils/ssao_makeVerticalBlur.slp rename libraries/render-utils/src/{ssao_makeVerticalBlur.slf => ssao_bilateralBlur.slf} (94%) rename libraries/render-utils/src/{ssao_blur.slv => ssao_bilateralBlur.slv} (97%) delete mode 100644 libraries/render-utils/src/ssao_makeHorizontalBlur.slf diff --git a/libraries/render-utils/src/AmbientOcclusionEffect.cpp b/libraries/render-utils/src/AmbientOcclusionEffect.cpp index b56da99ee6..6084fc8f14 100644 --- a/libraries/render-utils/src/AmbientOcclusionEffect.cpp +++ b/libraries/render-utils/src/AmbientOcclusionEffect.cpp @@ -32,8 +32,7 @@ #include "ViewFrustum.h" gpu::PipelinePointer AmbientOcclusionEffect::_occlusionPipeline; -gpu::PipelinePointer AmbientOcclusionEffect::_hBlurPipeline; -gpu::PipelinePointer AmbientOcclusionEffect::_vBlurPipeline; +gpu::PipelinePointer AmbientOcclusionEffect::_bilateralBlurPipeline; gpu::PipelinePointer AmbientOcclusionEffect::_mipCreationPipeline; gpu::PipelinePointer AmbientOcclusionEffect::_gatherPipeline; gpu::PipelinePointer AmbientOcclusionEffect::_buildNormalsPipeline; @@ -415,31 +414,17 @@ const gpu::PipelinePointer& AmbientOcclusionEffect::getOcclusionPipeline() { return _occlusionPipeline; } -const gpu::PipelinePointer& AmbientOcclusionEffect::getHBlurPipeline() { - if (!_hBlurPipeline) { - gpu::ShaderPointer program = gpu::Shader::createProgram(shader::render_utils::program::ssao_makeHorizontalBlur); +const gpu::PipelinePointer& AmbientOcclusionEffect::getBilateralBlurPipeline() { + if (!_bilateralBlurPipeline) { + gpu::ShaderPointer program = gpu::Shader::createProgram(shader::render_utils::program::ssao_bilateralBlur); gpu::StatePointer state = gpu::StatePointer(new gpu::State()); state->setColorWriteMask(true, true, true, false); // Good to go add the brand new pipeline - _hBlurPipeline = gpu::Pipeline::create(program, state); + _bilateralBlurPipeline = gpu::Pipeline::create(program, state); } - return _hBlurPipeline; -} - -const gpu::PipelinePointer& AmbientOcclusionEffect::getVBlurPipeline() { - if (!_vBlurPipeline) { - gpu::ShaderPointer program = gpu::Shader::createProgram(shader::render_utils::program::ssao_makeVerticalBlur); - gpu::StatePointer state = gpu::StatePointer(new gpu::State()); - - // Vertical blur write just the final result Occlusion value in the alpha channel - state->setColorWriteMask(true, true, true, false); - - // Good to go add the brand new pipeline - _vBlurPipeline = gpu::Pipeline::create(program, state); - } - return _vBlurPipeline; + return _bilateralBlurPipeline; } const gpu::PipelinePointer& AmbientOcclusionEffect::getMipCreationPipeline() { @@ -530,8 +515,7 @@ void AmbientOcclusionEffect::run(const render::RenderContextPointer& renderConte auto framebufferSize = _framebuffer->getSourceFrameSize(); auto occlusionPipeline = getOcclusionPipeline(); - auto firstHBlurPipeline = getHBlurPipeline(); - auto lastVBlurPipeline = getVBlurPipeline(); + auto bilateralBlurPipeline = getBilateralBlurPipeline(); auto mipCreationPipeline = getMipCreationPipeline(); #if SSAO_USE_QUAD_SPLIT auto gatherPipeline = getGatherPipeline(); @@ -680,12 +664,12 @@ void AmbientOcclusionEffect::run(const render::RenderContextPointer& renderConte model.setScale(uvScale); batch.setModelTransform(model); } + batch.setPipeline(bilateralBlurPipeline); batch.setViewportTransform(firstBlurViewport); batch.setFramebuffer(occlusionBlurredFBO); // Use full resolution depth and normal for bilateral upscaling and blur batch.setResourceTexture(render_utils::slot::texture::SsaoDepth, linearDepthTexture); batch.setUniformBuffer(render_utils::slot::buffer::SsaoBlurParams, _hblurParametersBuffer); - batch.setPipeline(firstHBlurPipeline); batch.setResourceTexture(render_utils::slot::texture::SsaoOcclusion, occlusionFBO->getRenderBuffer(0)); batch.draw(gpu::TRIANGLE_STRIP, 4); batch.popProfileRange(); @@ -705,7 +689,6 @@ void AmbientOcclusionEffect::run(const render::RenderContextPointer& renderConte batch.setViewportTransform(sourceViewport); batch.setFramebuffer(occlusionFBO); batch.setUniformBuffer(render_utils::slot::buffer::SsaoBlurParams, _vblurParametersBuffer); - batch.setPipeline(lastVBlurPipeline); batch.setResourceTexture(render_utils::slot::texture::SsaoOcclusion, occlusionBlurredFBO->getRenderBuffer(0)); batch.draw(gpu::TRIANGLE_STRIP, 4); batch.popProfileRange(); diff --git a/libraries/render-utils/src/AmbientOcclusionEffect.h b/libraries/render-utils/src/AmbientOcclusionEffect.h index 4ace549e9e..dbbbee76d0 100644 --- a/libraries/render-utils/src/AmbientOcclusionEffect.h +++ b/libraries/render-utils/src/AmbientOcclusionEffect.h @@ -190,15 +190,13 @@ private: BlurParametersBuffer _hblurParametersBuffer; static const gpu::PipelinePointer& getOcclusionPipeline(); - static const gpu::PipelinePointer& getHBlurPipeline(); // first - static const gpu::PipelinePointer& getVBlurPipeline(); // second + static const gpu::PipelinePointer& getBilateralBlurPipeline(); static const gpu::PipelinePointer& getMipCreationPipeline(); static const gpu::PipelinePointer& getGatherPipeline(); static const gpu::PipelinePointer& getBuildNormalsPipeline(); static gpu::PipelinePointer _occlusionPipeline; - static gpu::PipelinePointer _hBlurPipeline; - static gpu::PipelinePointer _vBlurPipeline; + static gpu::PipelinePointer _bilateralBlurPipeline; static gpu::PipelinePointer _mipCreationPipeline; static gpu::PipelinePointer _gatherPipeline; static gpu::PipelinePointer _buildNormalsPipeline; diff --git a/libraries/render-utils/src/render-utils/ssao_bilateralBlur.slp b/libraries/render-utils/src/render-utils/ssao_bilateralBlur.slp new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/libraries/render-utils/src/render-utils/ssao_bilateralBlur.slp @@ -0,0 +1 @@ + diff --git a/libraries/render-utils/src/render-utils/ssao_makeHorizontalBlur.slp b/libraries/render-utils/src/render-utils/ssao_makeHorizontalBlur.slp deleted file mode 100644 index 49fd3dba93..0000000000 --- a/libraries/render-utils/src/render-utils/ssao_makeHorizontalBlur.slp +++ /dev/null @@ -1 +0,0 @@ -VERTEX ssao_blur diff --git a/libraries/render-utils/src/render-utils/ssao_makeVerticalBlur.slp b/libraries/render-utils/src/render-utils/ssao_makeVerticalBlur.slp deleted file mode 100644 index 49fd3dba93..0000000000 --- a/libraries/render-utils/src/render-utils/ssao_makeVerticalBlur.slp +++ /dev/null @@ -1 +0,0 @@ -VERTEX ssao_blur diff --git a/libraries/render-utils/src/ssao_makeVerticalBlur.slf b/libraries/render-utils/src/ssao_bilateralBlur.slf similarity index 94% rename from libraries/render-utils/src/ssao_makeVerticalBlur.slf rename to libraries/render-utils/src/ssao_bilateralBlur.slf index 15550a73cd..cadf00bf14 100644 --- a/libraries/render-utils/src/ssao_makeVerticalBlur.slf +++ b/libraries/render-utils/src/ssao_bilateralBlur.slf @@ -2,7 +2,7 @@ <$VERSION_HEADER$> // Generated on <$_SCRIBE_DATE$> // -// ssao_makeVerticalBlur.frag +// ssao_bilateralBlur.frag // // Created by Sam Gateau on 1/1/16. // Copyright 2016 High Fidelity, Inc. @@ -10,6 +10,7 @@ // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // + <@include ssao.slh@> // Hack comment diff --git a/libraries/render-utils/src/ssao_blur.slv b/libraries/render-utils/src/ssao_bilateralBlur.slv similarity index 97% rename from libraries/render-utils/src/ssao_blur.slv rename to libraries/render-utils/src/ssao_bilateralBlur.slv index aafd9adbf4..d45fdf8360 100644 --- a/libraries/render-utils/src/ssao_blur.slv +++ b/libraries/render-utils/src/ssao_bilateralBlur.slv @@ -2,7 +2,7 @@ <$VERSION_HEADER$> // Generated on <$_SCRIBE_DATE$> // -// ssao_blur.vert +// ssao_bilateralBlur.vert // // Draw the unit quad [-1,-1 -> 1,1] filling in // Simply draw a Triangle_strip of 2 triangles, no input buffers or index buffer needed diff --git a/libraries/render-utils/src/ssao_makeHorizontalBlur.slf b/libraries/render-utils/src/ssao_makeHorizontalBlur.slf deleted file mode 100644 index e535398241..0000000000 --- a/libraries/render-utils/src/ssao_makeHorizontalBlur.slf +++ /dev/null @@ -1,26 +0,0 @@ -<@include gpu/Config.slh@> -<$VERSION_HEADER$> -// Generated on <$_SCRIBE_DATE$> -// -// ssao_makeHorizontalBlur.frag -// -// Created by Sam Gateau on 1/1/16. -// Copyright 2016 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -<@include ssao.slh@> - -// Hack comment - -<$declareBlurPass()$> - -layout(location=0) in vec4 varTexCoord0; - -layout(location=0) out vec4 outFragColor; - -void main(void) { - outFragColor = vec4(getBlurredOcclusion(ivec2(gl_FragCoord.xy), varTexCoord0.xy, varTexCoord0.zw), 1.0); -} From 135e10eaa276b8472c361ff5b1ade79a0d3379a0 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Thu, 27 Sep 2018 19:51:01 +0200 Subject: [PATCH 049/286] Packed occlusion depth for faster bilateral filtering --- .../src/AmbientOcclusionEffect.cpp | 23 ++- libraries/render-utils/src/ssao.slh | 145 ++++-------------- .../render-utils/src/ssao_bilateralBlur.slf | 115 +++++++++++++- .../render-utils/src/ssao_debugOcclusion.slf | 2 +- .../render-utils/src/ssao_makeOcclusion.slf | 2 +- libraries/render-utils/src/ssao_shared.h | 1 + 6 files changed, 164 insertions(+), 124 deletions(-) diff --git a/libraries/render-utils/src/AmbientOcclusionEffect.cpp b/libraries/render-utils/src/AmbientOcclusionEffect.cpp index 6084fc8f14..6a702b1640 100644 --- a/libraries/render-utils/src/AmbientOcclusionEffect.cpp +++ b/libraries/render-utils/src/AmbientOcclusionEffect.cpp @@ -82,17 +82,22 @@ gpu::TexturePointer AmbientOcclusionFramebuffer::getLinearDepthTexture() { } void AmbientOcclusionFramebuffer::allocate() { +#if SSAO_BILATERAL_BLUR_USE_NORMAL + const auto occlusionformat = gpu::Element{ gpu::VEC4, gpu::HALF, gpu::RGBA }; +#else + const auto occlusionformat = gpu::Element{ gpu::VEC3, gpu::NUINT8, gpu::RGB }; +#endif + // Full frame { auto width = _frameSize.x; auto height = _frameSize.y; - auto format = gpu::Element::COLOR_R_8; - _occlusionTexture = gpu::Texture::createRenderBuffer(format, width, height, gpu::Texture::SINGLE_MIP, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR, gpu::Sampler::WRAP_CLAMP)); + _occlusionTexture = gpu::Texture::createRenderBuffer(occlusionformat, width, height, gpu::Texture::SINGLE_MIP, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR, gpu::Sampler::WRAP_CLAMP)); _occlusionFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("occlusion")); _occlusionFramebuffer->setRenderBuffer(0, _occlusionTexture); - _occlusionBlurredTexture = gpu::Texture::createRenderBuffer(format, width, height, gpu::Texture::SINGLE_MIP, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR, gpu::Sampler::WRAP_CLAMP)); + _occlusionBlurredTexture = gpu::Texture::createRenderBuffer(occlusionformat, width, height, gpu::Texture::SINGLE_MIP, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR, gpu::Sampler::WRAP_CLAMP)); _occlusionBlurredFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("occlusionBlurred")); _occlusionBlurredFramebuffer->setRenderBuffer(0, _occlusionBlurredTexture); } @@ -128,9 +133,8 @@ void AmbientOcclusionFramebuffer::allocate() { } auto width = splitSize.x; auto height = splitSize.y; - auto format = gpu::Element::COLOR_R_8; - _occlusionSplitTexture = gpu::Texture::createRenderBufferArray(format, width, height, SSAO_SPLIT_COUNT*SSAO_SPLIT_COUNT, gpu::Texture::SINGLE_MIP, + _occlusionSplitTexture = gpu::Texture::createRenderBufferArray(occlusionformat, width, height, SSAO_SPLIT_COUNT*SSAO_SPLIT_COUNT, gpu::Texture::SINGLE_MIP, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR, gpu::Sampler::WRAP_CLAMP)); for (int i = 0; i < SSAO_SPLIT_COUNT*SSAO_SPLIT_COUNT; i++) { _occlusionSplitFramebuffers[i] = gpu::FramebufferPointer(gpu::Framebuffer::create("occlusion")); @@ -665,10 +669,15 @@ void AmbientOcclusionEffect::run(const render::RenderContextPointer& renderConte batch.setModelTransform(model); } batch.setPipeline(bilateralBlurPipeline); - batch.setViewportTransform(firstBlurViewport); - batch.setFramebuffer(occlusionBlurredFBO); // Use full resolution depth and normal for bilateral upscaling and blur batch.setResourceTexture(render_utils::slot::texture::SsaoDepth, linearDepthTexture); +#if SSAO_USE_QUAD_SPLIT + batch.setResourceTexture(render_utils::slot::texture::SsaoNormal, occlusionNormalTexture); +#else + batch.setResourceTexture(render_utils::slot::texture::SsaoNormal, nullptr); +#endif + batch.setViewportTransform(firstBlurViewport); + batch.setFramebuffer(occlusionBlurredFBO); batch.setUniformBuffer(render_utils::slot::buffer::SsaoBlurParams, _hblurParametersBuffer); batch.setResourceTexture(render_utils::slot::texture::SsaoOcclusion, occlusionFBO->getRenderBuffer(0)); batch.draw(gpu::TRIANGLE_STRIP, 4); diff --git a/libraries/render-utils/src/ssao.slh b/libraries/render-utils/src/ssao.slh index bc7473a3ba..a02554a385 100644 --- a/libraries/render-utils/src/ssao.slh +++ b/libraries/render-utils/src/ssao.slh @@ -16,20 +16,39 @@ <@func declarePackOcclusionDepth()@> -const float FAR_PLANE_Z = -300.0; +#define SSAO_DEPTH_KEY_SCALE 300.0 float CSZToDepthKey(float z) { - return clamp(z * (1.0 / FAR_PLANE_Z), 0.0, 1.0); + return clamp(z * (-1.0 / SSAO_DEPTH_KEY_SCALE), 0.0, 1.0); } -vec3 packOcclusionDepth(float occlusion, float depth) { + +vec4 packOcclusionOutput(float occlusion, float depth, vec3 eyeNormal) { + depth = CSZToDepthKey(depth); +#if SSAO_BILATERAL_BLUR_USE_NORMAL + return vec4(occlusion, depth, eyeNormal.xy / eyeNormal.z); +#else // Round to the nearest 1/256.0 - float temp = floor(depth * 256.0); - return vec3(occlusion, temp * (1.0 / 256.0), depth * 256.0 - temp); + depth *= 256; + float temp = floor(depth); + return vec4(occlusion, temp * (1.0 / 256.0), depth - temp, 0.0); +#endif } -vec2 unpackOcclusionDepth(vec3 raw) { - float z = raw.y * (256.0 / 257.0) + raw.z * (1.0 / 257.0); - return vec2(raw.x, z); + +void unpackOcclusionOutput(vec4 raw, out float occlusion, out float depth, out vec3 eyeNormal) { + occlusion = raw.x; +#if SSAO_BILATERAL_BLUR_USE_NORMAL + depth = raw.y; + eyeNormal = normalize(vec3(raw.zw, 1.0)); +#else + depth = (raw.y + raw.z / 256.0); + eyeNormal = vec3(0,0,1); +#endif } + +float unpackOcclusion(vec4 raw) { + return raw.x; +} + <@endfunc@> <@func declareAmbientOcclusion()@> @@ -252,6 +271,11 @@ vec3 getNormalEyeAtUV(vec2 texCoord, int level) { return normalize(textureLod(normalTex, texCoord, level).xyz*2.0 - vec3(1.0)); } +vec3 getNormalEyeAtUV(ivec4 side, vec2 texCoord, int level) { + texCoord.x = mix(texCoord.x, (texCoord.x + getStereoSide(side)) * 0.5, isStereo()); + return getNormalEyeAtUV(texCoord, level); +} + vec3 getNormalEyeAtPixel(ivec2 pixel, int level) { return normalize(texelFetch(normalTex, pixel, level).xyz*2.0 - vec3(1.0)); } @@ -418,109 +442,4 @@ float evalVisibilityHBAO(ivec4 side, vec2 fragUVPos, vec2 invSideImageSize, vec2 <@endfunc@> -<@func declareBlurPass()@> - -<$declareAmbientOcclusion()$> -<$declareFetchDepthPyramidMap()$> -<$declarePackOcclusionDepth()$> - -// the source occlusion texture -layout(binding=RENDER_UTILS_TEXTURE_SSAO_OCCLUSION) uniform sampler2D occlusionMap; - -layout(binding=RENDER_UTILS_BUFFER_SSAO_BLUR_PARAMS) uniform blurParamsBuffer { - AmbientOcclusionBlurParams blurParams; -}; - -vec2 getBlurOcclusionAxis() { - return blurParams._blurAxis.zw; -} - -vec2 getBlurDepthAxis() { - return blurParams._blurAxis.xy; -} - -vec2 getBlurOcclusionUVLimit() { - return blurParams._blurInfo.zw; -} - -float getBlurEdgeSharpness() { - return blurParams._blurInfo.x; -} - -int getBlurRadius() { - return int(blurParams._blurInfo.y); -} - -float fetchOcclusion(ivec4 side, vec2 texCoord) { - texCoord.x = mix(texCoord.x, (texCoord.x + getStereoSide(side)) * 0.5, isStereo()); - vec3 raw = textureLod(occlusionMap, texCoord, 0).xyz; - return raw.x; -} - -const float BLUR_EDGE_DISTANCE_SCALE = 1000.0; - -float evalBlurCoefficient(vec2 blurScales, float radialDistance, float zDistance) { - vec2 distances = vec2(radialDistance, zDistance); - return exp2(dot(blurScales, distances*distances)); -} - -vec2 evalTapWeightedValue(vec2 blurScales, ivec4 side, int r, vec2 occlusionTexCoord, vec2 depthTexCoord, float fragDepth) { - vec2 tapOcclusionTexCoord = getBlurOcclusionAxis() * r + occlusionTexCoord; - vec2 occlusionTexCoordLimits = getBlurOcclusionUVLimit(); - - if (tapOcclusionTexCoord.x < side.x || tapOcclusionTexCoord.x >= (side.x + occlusionTexCoordLimits.x) - || tapOcclusionTexCoord.y < 0 || tapOcclusionTexCoord.y >= occlusionTexCoordLimits.y) { - return vec2(0.0); - } - - float tapOcclusion = fetchOcclusion(side, tapOcclusionTexCoord); - - vec2 tapDepthTexCoord = getBlurDepthAxis() * r + depthTexCoord; - float tapDepth = getZEyeAtUV(side, tapDepthTexCoord, 0); - - // range domain (the "bilateral" weight). As depth difference increases, decrease weight. - float zDistance = tapDepth - fragDepth; - float weight = evalBlurCoefficient(blurScales, abs(r), zDistance); - - return vec2(tapOcclusion * weight, weight); -} - -vec3 getBlurredOcclusion(ivec2 destPixelCoord, vec2 occlusionTexCoord, vec2 depthTexCoord) { - // Stereo side info - ivec4 side = getStereoSideInfo(destPixelCoord.x, 0); - - float fragDepth = getZEyeAtUV(side, depthTexCoord, 0); - vec2 weightedSums = vec2(0.0); - - // Accumulate weighted contributions along the bluring axis in the [-radius, radius] range - int blurRadius = getBlurRadius(); - float blurRadialSigma = float(blurRadius) * 0.5; - float blurRadialScale = 1.0 / (2.0*blurRadialSigma*blurRadialSigma); - vec2 blurScales = -vec2(blurRadialScale, BLUR_EDGE_DISTANCE_SCALE * getBlurEdgeSharpness()); - - // negative side first - for (int r = -blurRadius; r <= -1; ++r) { - weightedSums += evalTapWeightedValue(blurScales, side, r, occlusionTexCoord, depthTexCoord, fragDepth); - } - - // Central pixel contribution - float mainWeight = 1.0; - float pixelOcclusion = fetchOcclusion(side, occlusionTexCoord); - weightedSums += vec2(pixelOcclusion * mainWeight, mainWeight); - - // then positive side - for (int r = 1; r <= blurRadius; ++r) { - weightedSums += evalTapWeightedValue(blurScales, side, r, occlusionTexCoord, depthTexCoord, fragDepth); - } - - // Final normalization - const float epsilon = 0.0001; - float result = weightedSums.x / (weightedSums.y + epsilon); - - return vec3(result); -} - -<@endfunc@> - - <@endif@> diff --git a/libraries/render-utils/src/ssao_bilateralBlur.slf b/libraries/render-utils/src/ssao_bilateralBlur.slf index cadf00bf14..e85fc42b9e 100644 --- a/libraries/render-utils/src/ssao_bilateralBlur.slf +++ b/libraries/render-utils/src/ssao_bilateralBlur.slf @@ -15,12 +15,123 @@ // Hack comment -<$declareBlurPass()$> +<$declareAmbientOcclusion()$> +<$declareFetchDepthPyramidMap()$> +<$declarePackOcclusionDepth()$> + +// the source occlusion texture +layout(binding=RENDER_UTILS_TEXTURE_SSAO_OCCLUSION) uniform sampler2D occlusionMap; + +layout(binding=RENDER_UTILS_BUFFER_SSAO_BLUR_PARAMS) uniform blurParamsBuffer { + AmbientOcclusionBlurParams blurParams; +}; + +vec2 getBlurOcclusionAxis() { + return blurParams._blurAxis.zw; +} + +vec2 getBlurDepthAxis() { + return blurParams._blurAxis.xy; +} + +vec2 getBlurOcclusionUVLimit() { + return blurParams._blurInfo.zw; +} + +float getBlurEdgeSharpness() { + return blurParams._blurInfo.x; +} + +int getBlurRadius() { + return int(blurParams._blurInfo.y); +} + +vec4 fetchOcclusionPacked(ivec4 side, vec2 texCoord) { + texCoord.x = mix(texCoord.x, (texCoord.x + getStereoSide(side)) * 0.5, isStereo()); + return textureLod(occlusionMap, texCoord, 0); +} + +float evalBlurCoefficient(vec3 blurScales, float radialDistance, float zDistance, float normalDistance) { + vec3 distances = vec3(radialDistance, zDistance, normalDistance); + return exp2(dot(blurScales, distances*distances)); +} + +const float BLUR_EDGE_DISTANCE_SCALE = 1000 * SSAO_DEPTH_KEY_SCALE; +const float BLUR_EDGE_NORMAL_SCALE = 2.0; +const float BLUR_EDGE_NORMAL_LIMIT = 0.25; + +vec2 evalTapWeightedValue(vec3 blurScales, ivec4 side, int r, vec2 occlusionTexCoord, vec2 depthTexCoord, float fragDepth, vec3 fragNormal) { + vec2 tapOcclusionTexCoord = getBlurOcclusionAxis() * r + occlusionTexCoord; + vec2 occlusionTexCoordLimits = getBlurOcclusionUVLimit(); + + if (tapOcclusionTexCoord.x < side.x || tapOcclusionTexCoord.x >= (side.x + occlusionTexCoordLimits.x) + || tapOcclusionTexCoord.y < 0 || tapOcclusionTexCoord.y >= occlusionTexCoordLimits.y) { + return vec2(0.0); + } + + vec4 tapOcclusionPacked = fetchOcclusionPacked(side, tapOcclusionTexCoord); + float tapOcclusion; + float tapDepth; + vec3 tapNormal; + unpackOcclusionOutput(tapOcclusionPacked, tapOcclusion, tapDepth, tapNormal); + + // range domain (the "bilateral" weight). As depth difference increases, decrease weight. + float zDistance = tapDepth - fragDepth; +#if SSAO_BILATERAL_BLUR_USE_NORMAL + float normalDistance = BLUR_EDGE_NORMAL_LIMIT - min(BLUR_EDGE_NORMAL_LIMIT, dot(tapNormal, fragNormal)); +#else + float normalDistance = 0.0; +#endif + float weight = evalBlurCoefficient(blurScales, abs(r), zDistance, normalDistance); + + return vec2(tapOcclusion * weight, weight); +} + +vec4 getBlurredOcclusion(ivec2 destPixelCoord, vec2 occlusionTexCoord, vec2 depthTexCoord) { + // Stereo side info + ivec4 side = getStereoSideInfo(destPixelCoord.x, 0); + + float fragDepth = getZEyeAtUV(side, depthTexCoord, 0); + float fragDepthKey = CSZToDepthKey(fragDepth); +#if SSAO_BILATERAL_BLUR_USE_NORMAL + vec3 fragNormal = getNormalEyeAtUV(side, depthTexCoord, 0); +#else + vec3 fragNormal = vec3(0, 0, 1); +#endif + vec2 weightedSums = vec2(0.0); + + // Accumulate weighted contributions along the bluring axis in the [-radius, radius] range + int blurRadius = getBlurRadius(); + float blurRadialSigma = float(blurRadius) * 0.5; + float blurRadialScale = 1.0 / (2.0*blurRadialSigma*blurRadialSigma); + vec3 blurScales = -vec3(blurRadialScale, vec2(BLUR_EDGE_DISTANCE_SCALE, BLUR_EDGE_NORMAL_SCALE) * getBlurEdgeSharpness()); + + // negative side first + for (int r = -blurRadius; r <= -1; ++r) { + weightedSums += evalTapWeightedValue(blurScales, side, r, occlusionTexCoord, depthTexCoord, fragDepthKey, fragNormal); + } + + // Central pixel contribution + float mainWeight = 1.0; + float pixelOcclusion = unpackOcclusion(fetchOcclusionPacked(side, occlusionTexCoord)); + weightedSums += vec2(pixelOcclusion * mainWeight, mainWeight); + + // then positive side + for (int r = 1; r <= blurRadius; ++r) { + weightedSums += evalTapWeightedValue(blurScales, side, r, occlusionTexCoord, depthTexCoord, fragDepthKey, fragNormal); + } + + // Final normalization + const float epsilon = 0.0001; + float result = weightedSums.x / (weightedSums.y + epsilon); + + return packOcclusionOutput(result, fragDepth, fragNormal); +} layout(location=0) in vec4 varTexCoord0; layout(location=0) out vec4 outFragColor; void main(void) { - outFragColor = vec4(getBlurredOcclusion(ivec2(gl_FragCoord.xy), varTexCoord0.xy, varTexCoord0.zw), 1.0); + outFragColor = getBlurredOcclusion(ivec2(gl_FragCoord.xy), varTexCoord0.xy, varTexCoord0.zw); } diff --git a/libraries/render-utils/src/ssao_debugOcclusion.slf b/libraries/render-utils/src/ssao_debugOcclusion.slf index 2c1824dce7..efa6a02730 100644 --- a/libraries/render-utils/src/ssao_debugOcclusion.slf +++ b/libraries/render-utils/src/ssao_debugOcclusion.slf @@ -116,7 +116,7 @@ void main(void) { } !> - outFragColor = vec4(packOcclusionDepth(A, CSZToDepthKey(Cp.z)), 1.0); + outFragColor = packOcclusionOutput(A, Cp.z, vec3(0,0,1)); if ((dot(fragToCursor,fragToCursor) < (100.0 * keepTapRadius * keepTapRadius) )) { // outFragColor = vec4(vec3(A), 1.0); diff --git a/libraries/render-utils/src/ssao_makeOcclusion.slf b/libraries/render-utils/src/ssao_makeOcclusion.slf index e2a94be134..74617cc4bb 100644 --- a/libraries/render-utils/src/ssao_makeOcclusion.slf +++ b/libraries/render-utils/src/ssao_makeOcclusion.slf @@ -84,5 +84,5 @@ void main(void) { float occlusion = clamp(1.0 - obscuranceSum * getObscuranceScaling() * invNumSamples, 0.0, 1.0); - outFragColor = vec4(vec3(occlusion), 1.0); + outFragColor = packOcclusionOutput(occlusion * occlusion, fragPositionES.z, fragNormalES); } diff --git a/libraries/render-utils/src/ssao_shared.h b/libraries/render-utils/src/ssao_shared.h index 9c9e57337c..667dd702ef 100644 --- a/libraries/render-utils/src/ssao_shared.h +++ b/libraries/render-utils/src/ssao_shared.h @@ -15,6 +15,7 @@ #define RENDER_UTILS_SSAO_SHARED_H #define SSAO_USE_QUAD_SPLIT 1 +#define SSAO_BILATERAL_BLUR_USE_NORMAL 0 #if SSAO_USE_QUAD_SPLIT #define SSAO_SPLIT_LOG2_COUNT 2 From 8e914fa565bea92330c68fc8514f42c1e5329fd7 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Fri, 28 Sep 2018 10:25:57 +0200 Subject: [PATCH 050/286] Bilateral blur shader cleanup and quad split normal at occlusion resolution --- .../src/AmbientOcclusionEffect.cpp | 32 ++++++++----------- .../render-utils/src/ssao_bilateralBlur.slf | 6 +--- 2 files changed, 14 insertions(+), 24 deletions(-) diff --git a/libraries/render-utils/src/AmbientOcclusionEffect.cpp b/libraries/render-utils/src/AmbientOcclusionEffect.cpp index 6a702b1640..a89d17c5c4 100644 --- a/libraries/render-utils/src/AmbientOcclusionEffect.cpp +++ b/libraries/render-utils/src/AmbientOcclusionEffect.cpp @@ -108,7 +108,7 @@ void AmbientOcclusionFramebuffer::allocate() { if (_isStereo) { sideSize.x >>= 1; } - sideSize = divideRoundUp(sideSize, 1 << _depthResolutionLevel); + sideSize = divideRoundUp(sideSize, 1 << _resolutionLevel); if (_isStereo) { sideSize.x <<= 1; } @@ -208,7 +208,7 @@ AmbientOcclusionEffectConfig::AmbientOcclusionEffectConfig() : radius{ 0.3f }, perspectiveScale{ 1.0f }, obscuranceLevel{ 0.5f }, - falloffAngle{ 0.3f }, + falloffAngle{ 0.45f }, edgeSharpness{ 1.0f }, blurDeviation{ 2.5f }, numSpiralTurns{ 7.0f }, @@ -363,19 +363,12 @@ void AmbientOcclusionEffect::updateBlurParameters() { vblur._blurInfo.z = 1.0f; vblur._blurInfo.w = occlusionSize.y / float(frameSize.y); - // Depth axis - hblur._blurAxis.x = 1.0f / occlusionSize.x; + // Occlusion axis + hblur._blurAxis.x = hblur._blurInfo.z / occlusionSize.x; hblur._blurAxis.y = 0.0f; vblur._blurAxis.x = 0.0f; - vblur._blurAxis.y = 1.0f / occlusionSize.y; - - // Occlusion axis - hblur._blurAxis.z = hblur._blurAxis.x * hblur._blurInfo.z; - hblur._blurAxis.w = 0.0f; - - vblur._blurAxis.z = 0.0f; - vblur._blurAxis.w = vblur._blurAxis.y * vblur._blurInfo.w; + vblur._blurAxis.y = vblur._blurInfo.w / occlusionSize.y; } void AmbientOcclusionEffect::updateFramebufferSizes() { @@ -489,7 +482,7 @@ void AmbientOcclusionEffect::run(const render::RenderContextPointer& renderConte auto occlusionDepthTexture = linearDepthTexture; auto sourceViewport = args->_viewport; // divideRoundUp is used two compute the quarter or half resolution render sizes. - // We need to take the rounded up resolution. + // We choose to take the rounded up resolution. auto occlusionViewport = divideRoundUp(sourceViewport, 1 << resolutionLevel); auto firstBlurViewport = sourceViewport; firstBlurViewport.w = occlusionViewport.w; @@ -541,7 +534,7 @@ void AmbientOcclusionEffect::run(const render::RenderContextPointer& renderConte // _frameId = (_frameId + 1) % SSAO_RANDOM_SAMPLE_COUNT; gpu::doInBatch("AmbientOcclusionEffect::run", args->_context, [=](gpu::Batch& batch) { - PROFILE_RANGE_BATCH(batch, "AmbientOcclusion"); + PROFILE_RANGE_BATCH(batch, "SSAO"); batch.enableStereo(false); _gpuTimer->begin(batch); @@ -552,7 +545,7 @@ void AmbientOcclusionEffect::run(const render::RenderContextPointer& renderConte batch.setModelTransform(Transform()); // We need this with the mips levels - batch.pushProfileRange("Depth Mip Generation"); + batch.pushProfileRange("Depth Mips"); if (isHorizonBased) { batch.setPipeline(mipCreationPipeline); batch.generateTextureMipsWithPipeline(occlusionDepthTexture); @@ -562,7 +555,7 @@ void AmbientOcclusionEffect::run(const render::RenderContextPointer& renderConte batch.popProfileRange(); #if SSAO_USE_QUAD_SPLIT - batch.pushProfileRange("Normal Generation"); + batch.pushProfileRange("Normal Gen."); { const auto uvScale = glm::vec3( normalViewport.z / (sourceViewport.z * depthResolutionScale), @@ -579,7 +572,8 @@ void AmbientOcclusionEffect::run(const render::RenderContextPointer& renderConte model.setScale(uvScale); model.setTranslation(uvTranslate); - batch.setModelTransform(model); + // TEMPO OP batch.setModelTransform(model); + batch.setModelTransform(Transform()); } // Build face normals pass @@ -658,7 +652,7 @@ void AmbientOcclusionEffect::run(const render::RenderContextPointer& renderConte { PROFILE_RANGE_BATCH(batch, "Bilateral Blur"); // Blur 1st pass - batch.pushProfileRange("Horizontal"); + batch.pushProfileRange("Horiz."); { const auto uvScale = glm::vec3( occlusionViewport.z / float(sourceViewport.z), @@ -684,7 +678,7 @@ void AmbientOcclusionEffect::run(const render::RenderContextPointer& renderConte batch.popProfileRange(); // Blur 2nd pass - batch.pushProfileRange("Vertical"); + batch.pushProfileRange("Vert."); { const auto uvScale = glm::vec3( 1.0f, diff --git a/libraries/render-utils/src/ssao_bilateralBlur.slf b/libraries/render-utils/src/ssao_bilateralBlur.slf index e85fc42b9e..dc708b137b 100644 --- a/libraries/render-utils/src/ssao_bilateralBlur.slf +++ b/libraries/render-utils/src/ssao_bilateralBlur.slf @@ -27,10 +27,6 @@ layout(binding=RENDER_UTILS_BUFFER_SSAO_BLUR_PARAMS) uniform blurParamsBuffer { }; vec2 getBlurOcclusionAxis() { - return blurParams._blurAxis.zw; -} - -vec2 getBlurDepthAxis() { return blurParams._blurAxis.xy; } @@ -56,7 +52,7 @@ float evalBlurCoefficient(vec3 blurScales, float radialDistance, float zDistance return exp2(dot(blurScales, distances*distances)); } -const float BLUR_EDGE_DISTANCE_SCALE = 1000 * SSAO_DEPTH_KEY_SCALE; +const float BLUR_EDGE_DISTANCE_SCALE = 10000 * SSAO_DEPTH_KEY_SCALE; const float BLUR_EDGE_NORMAL_SCALE = 2.0; const float BLUR_EDGE_NORMAL_LIMIT = 0.25; From 6240454c551e2b6c3ac4558e92a371baacb4e2a6 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Fri, 28 Sep 2018 10:33:47 +0200 Subject: [PATCH 051/286] Switched back to half res depth when resolutionLevel>1 --- libraries/render-utils/src/AmbientOcclusionEffect.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/libraries/render-utils/src/AmbientOcclusionEffect.cpp b/libraries/render-utils/src/AmbientOcclusionEffect.cpp index a89d17c5c4..b0b371a715 100644 --- a/libraries/render-utils/src/AmbientOcclusionEffect.cpp +++ b/libraries/render-utils/src/AmbientOcclusionEffect.cpp @@ -458,8 +458,7 @@ const gpu::PipelinePointer& AmbientOcclusionEffect::getBuildNormalsPipeline() { } int AmbientOcclusionEffect::getDepthResolutionLevel() const { - // Having some problems making a nice AO with Half resolution depth, so stick to full res. - return 0; + return std::min(1, _aoParametersBuffer->getResolutionLevel()); } void AmbientOcclusionEffect::run(const render::RenderContextPointer& renderContext, const Inputs& inputs, Outputs& outputs) { From fbd158938ba578e7f5ae217476f62567ba24d8f4 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Fri, 28 Sep 2018 11:31:09 +0200 Subject: [PATCH 052/286] Put some bilateral blur computation on CPU --- .../src/AmbientOcclusionEffect.cpp | 42 +++++++++++-------- libraries/render-utils/src/ssao.slh | 2 - .../render-utils/src/ssao_bilateralBlur.slf | 14 +++---- libraries/render-utils/src/ssao_shared.h | 2 + 4 files changed, 32 insertions(+), 28 deletions(-) diff --git a/libraries/render-utils/src/AmbientOcclusionEffect.cpp b/libraries/render-utils/src/AmbientOcclusionEffect.cpp index b0b371a715..1df3da2b70 100644 --- a/libraries/render-utils/src/AmbientOcclusionEffect.cpp +++ b/libraries/render-utils/src/AmbientOcclusionEffect.cpp @@ -229,7 +229,7 @@ AmbientOcclusionEffect::AOParameters::AOParameters() { } AmbientOcclusionEffect::BlurParameters::BlurParameters() { - _blurInfo = { 1.0f, 3.0f, 2.0f, 0.0f }; + _blurInfo = { 1.0f, 2.0f, 0.0f, 3.0f }; } AmbientOcclusionEffect::AmbientOcclusionEffect() { @@ -268,11 +268,26 @@ void AmbientOcclusionEffect::configure(const Config& config) { current.y = 1.0f / (1.0f - config.falloffAngle); } - if (config.edgeSharpness != _hblurParametersBuffer.get().getEdgeSharpness()) { + // Update bilateral blur + { + const float BLUR_EDGE_DISTANCE_SCALE = float(10000 * SSAO_DEPTH_KEY_SCALE); + const float BLUR_EDGE_NORMAL_SCALE = 2.0f; + auto& hblur = _hblurParametersBuffer.edit()._blurInfo; auto& vblur = _vblurParametersBuffer.edit()._blurInfo; - hblur.x = config.edgeSharpness; - vblur.x = config.edgeSharpness; + float blurRadialSigma = float(config.blurRadius) * 0.5f; + float blurRadialScale = 1.0f / (2.0f*blurRadialSigma*blurRadialSigma); + glm::vec3 blurScales = -glm::vec3(blurRadialScale, glm::vec2(BLUR_EDGE_DISTANCE_SCALE, BLUR_EDGE_NORMAL_SCALE) * config.edgeSharpness); + + hblur.x = blurScales.x; + hblur.y = blurScales.y; + hblur.z = blurScales.z; + hblur.w = (float)config.blurRadius; + + vblur.x = blurScales.x; + vblur.y = blurScales.y; + vblur.z = blurScales.z; + vblur.w = (float)config.blurRadius; } if (config.numSpiralTurns != _aoParametersBuffer->getNumSpiralTurns()) { @@ -322,13 +337,6 @@ void AmbientOcclusionEffect::configure(const Config& config) { current.x = (float)config.resolutionLevel; shouldUpdateBlurs = true; } - - if (config.blurRadius != _hblurParametersBuffer.get().getBlurRadius()) { - auto& hblur = _hblurParametersBuffer.edit()._blurInfo; - auto& vblur = _vblurParametersBuffer.edit()._blurInfo; - hblur.y = (float)config.blurRadius; - vblur.y = (float)config.blurRadius; - } if (config.ditheringEnabled != _aoParametersBuffer->isDitheringEnabled()) { auto& current = _aoParametersBuffer.edit()._ditheringInfo; @@ -357,18 +365,18 @@ void AmbientOcclusionEffect::updateBlurParameters() { const auto occlusionSize = divideRoundUp(frameSize, resolutionScale); // Occlusion UV limit - hblur._blurInfo.z = occlusionSize.x / float(frameSize.x); - hblur._blurInfo.w = occlusionSize.y / float(frameSize.y); + hblur._blurAxis.z = occlusionSize.x / float(frameSize.x); + hblur._blurAxis.w = occlusionSize.y / float(frameSize.y); - vblur._blurInfo.z = 1.0f; - vblur._blurInfo.w = occlusionSize.y / float(frameSize.y); + vblur._blurAxis.z = 1.0f; + vblur._blurAxis.w = occlusionSize.y / float(frameSize.y); // Occlusion axis - hblur._blurAxis.x = hblur._blurInfo.z / occlusionSize.x; + hblur._blurAxis.x = hblur._blurAxis.z / occlusionSize.x; hblur._blurAxis.y = 0.0f; vblur._blurAxis.x = 0.0f; - vblur._blurAxis.y = vblur._blurInfo.w / occlusionSize.y; + vblur._blurAxis.y = vblur._blurAxis.w / occlusionSize.y; } void AmbientOcclusionEffect::updateFramebufferSizes() { diff --git a/libraries/render-utils/src/ssao.slh b/libraries/render-utils/src/ssao.slh index a02554a385..c72d1f4c0d 100644 --- a/libraries/render-utils/src/ssao.slh +++ b/libraries/render-utils/src/ssao.slh @@ -16,8 +16,6 @@ <@func declarePackOcclusionDepth()@> -#define SSAO_DEPTH_KEY_SCALE 300.0 - float CSZToDepthKey(float z) { return clamp(z * (-1.0 / SSAO_DEPTH_KEY_SCALE), 0.0, 1.0); } diff --git a/libraries/render-utils/src/ssao_bilateralBlur.slf b/libraries/render-utils/src/ssao_bilateralBlur.slf index dc708b137b..e89d696cbb 100644 --- a/libraries/render-utils/src/ssao_bilateralBlur.slf +++ b/libraries/render-utils/src/ssao_bilateralBlur.slf @@ -31,15 +31,15 @@ vec2 getBlurOcclusionAxis() { } vec2 getBlurOcclusionUVLimit() { - return blurParams._blurInfo.zw; + return blurParams._blurAxis.zw; } -float getBlurEdgeSharpness() { - return blurParams._blurInfo.x; +vec3 getBlurScales() { + return blurParams._blurInfo.xyz; } int getBlurRadius() { - return int(blurParams._blurInfo.y); + return int(blurParams._blurInfo.w); } vec4 fetchOcclusionPacked(ivec4 side, vec2 texCoord) { @@ -52,8 +52,6 @@ float evalBlurCoefficient(vec3 blurScales, float radialDistance, float zDistance return exp2(dot(blurScales, distances*distances)); } -const float BLUR_EDGE_DISTANCE_SCALE = 10000 * SSAO_DEPTH_KEY_SCALE; -const float BLUR_EDGE_NORMAL_SCALE = 2.0; const float BLUR_EDGE_NORMAL_LIMIT = 0.25; vec2 evalTapWeightedValue(vec3 blurScales, ivec4 side, int r, vec2 occlusionTexCoord, vec2 depthTexCoord, float fragDepth, vec3 fragNormal) { @@ -98,9 +96,7 @@ vec4 getBlurredOcclusion(ivec2 destPixelCoord, vec2 occlusionTexCoord, vec2 dept // Accumulate weighted contributions along the bluring axis in the [-radius, radius] range int blurRadius = getBlurRadius(); - float blurRadialSigma = float(blurRadius) * 0.5; - float blurRadialScale = 1.0 / (2.0*blurRadialSigma*blurRadialSigma); - vec3 blurScales = -vec3(blurRadialScale, vec2(BLUR_EDGE_DISTANCE_SCALE, BLUR_EDGE_NORMAL_SCALE) * getBlurEdgeSharpness()); + vec3 blurScales = getBlurScales(); // negative side first for (int r = -blurRadius; r <= -1; ++r) { diff --git a/libraries/render-utils/src/ssao_shared.h b/libraries/render-utils/src/ssao_shared.h index 667dd702ef..1552a38947 100644 --- a/libraries/render-utils/src/ssao_shared.h +++ b/libraries/render-utils/src/ssao_shared.h @@ -17,6 +17,8 @@ #define SSAO_USE_QUAD_SPLIT 1 #define SSAO_BILATERAL_BLUR_USE_NORMAL 0 +#define SSAO_DEPTH_KEY_SCALE 300.0 + #if SSAO_USE_QUAD_SPLIT #define SSAO_SPLIT_LOG2_COUNT 2 #else From 086ba998c89088dd62aff692798aa6702817734a Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Fri, 28 Sep 2018 16:41:18 +0200 Subject: [PATCH 053/286] Some small cleanups --- .../src/AmbientOcclusionEffect.cpp | 25 ++------------ libraries/render-utils/src/ssao.slh | 34 +++++++++++-------- .../render-utils/src/ssao_makeOcclusion.slf | 7 ++-- 3 files changed, 27 insertions(+), 39 deletions(-) diff --git a/libraries/render-utils/src/AmbientOcclusionEffect.cpp b/libraries/render-utils/src/AmbientOcclusionEffect.cpp index 1df3da2b70..5b27cfa41d 100644 --- a/libraries/render-utils/src/AmbientOcclusionEffect.cpp +++ b/libraries/render-utils/src/AmbientOcclusionEffect.cpp @@ -83,9 +83,9 @@ gpu::TexturePointer AmbientOcclusionFramebuffer::getLinearDepthTexture() { void AmbientOcclusionFramebuffer::allocate() { #if SSAO_BILATERAL_BLUR_USE_NORMAL - const auto occlusionformat = gpu::Element{ gpu::VEC4, gpu::HALF, gpu::RGBA }; + auto occlusionformat = gpu::Element{ gpu::VEC4, gpu::HALF, gpu::RGBA }; #else - const auto occlusionformat = gpu::Element{ gpu::VEC3, gpu::NUINT8, gpu::RGB }; + auto occlusionformat = gpu::Element{ gpu::VEC3, gpu::NUINT8, gpu::RGB }; #endif // Full frame @@ -563,27 +563,8 @@ void AmbientOcclusionEffect::run(const render::RenderContextPointer& renderConte #if SSAO_USE_QUAD_SPLIT batch.pushProfileRange("Normal Gen."); - { - const auto uvScale = glm::vec3( - normalViewport.z / (sourceViewport.z * depthResolutionScale), - normalViewport.w / (sourceViewport.w * depthResolutionScale), - 1.0f); - const auto postPixelOffset = glm::vec2(0.5f) / glm::vec2(occlusionDepthSize); - const auto prePixelOffset = glm::vec2(0.5f * uvScale.x, 0.5f * uvScale.y) / glm::vec2(normalViewport.z, normalViewport.w); - const auto uvTranslate = glm::vec3( - postPixelOffset.x - prePixelOffset.x, - postPixelOffset.y - prePixelOffset.y, - 0.0f - ); - Transform model; - - model.setScale(uvScale); - model.setTranslation(uvTranslate); - // TEMPO OP batch.setModelTransform(model); - batch.setModelTransform(Transform()); - } - // Build face normals pass + batch.setModelTransform(Transform()); batch.setViewportTransform(normalViewport); batch.setPipeline(buildNormalsPipeline); batch.setResourceTexture(render_utils::slot::texture::SsaoDepth, linearDepthTexture); diff --git a/libraries/render-utils/src/ssao.slh b/libraries/render-utils/src/ssao.slh index c72d1f4c0d..9590dd9277 100644 --- a/libraries/render-utils/src/ssao.slh +++ b/libraries/render-utils/src/ssao.slh @@ -297,9 +297,7 @@ vec2 fetchTap(ivec4 side, vec2 tapUV, float tapRadius) { } vec3 buildPosition(ivec4 side, vec2 fragUVPos) { - vec2 fetchUV = clamp(fragUVPos, vec2(0), vec2(1)); - fetchUV.x = mix(fetchUV.x, (fetchUV.x + getStereoSide(side)) * 0.5, isStereo()); - float Zeye = getZEyeAtUV(fetchUV, 0); + float Zeye = getZEyeAtUV(side, fragUVPos, 0); return evalEyePositionFromZeye(side.x, Zeye, fragUVPos); } @@ -348,12 +346,20 @@ float evalVisibilitySSAO(in vec3 centerPosition, in vec3 centerNormal, in vec3 t return f * f * f * max((vn - getFalloffAngle()) / (epsilon + vv), 0.0); } -float computeHorizonFromTap(vec3 tapPositionES, vec3 fragPositionES, vec3 fragNormalES) { +vec2 computeHorizonFromTap(vec3 tapPositionES, vec3 fragPositionES, vec3 fragNormalES) { const float epsilon = 0.001; vec3 deltaVec = tapPositionES - fragPositionES; float distance = length(deltaVec); float cosHorizonAngle = dot(deltaVec, fragNormalES) / (distance + epsilon); + + return vec2(cosHorizonAngle, distance); +} + +float computeWeightedHorizonFromTap(vec3 tapPositionES, vec3 fragPositionES, vec3 fragNormalES) { + vec2 rawHorizon = computeHorizonFromTap(tapPositionES, fragPositionES, fragNormalES); + float distance = rawHorizon.y; + float cosHorizonAngle = rawHorizon.x; float radiusFalloff = max(0.0, 1.0 - (distance*distance / getRadius2())); cosHorizonAngle = max(0.0, (cosHorizonAngle - getFalloffAngle()) * getFalloffAngleScale()); @@ -363,14 +369,13 @@ float computeHorizonFromTap(vec3 tapPositionES, vec3 fragPositionES, vec3 fragNo } <@func computeHorizon()@> - vec2 tapSideUVPos = tapUVOffset + fragUVPos; - if (tapSideUVPos.x<0 || tapSideUVPos.y<0 || tapSideUVPos.x>=1.0 || tapSideUVPos.y>=1.0) { + if (fragUVPos.x<0 || fragUVPos.y<0 || fragUVPos.x>=1.0 || fragUVPos.y>=1.0) { // Early exit because we've hit the borders of the frame break; } - vec2 tapMipZ = fetchTap(side, tapSideUVPos, radius); - vec3 tapPositionES = evalEyePositionFromZeye(side.x, tapMipZ.y, tapSideUVPos); - float tapCosHorizonAngle = computeHorizonFromTap(tapPositionES, fragPositionES, fragNormalES); + vec2 tapMipZ = fetchTap(side, fragUVPos, radius); + vec3 tapPositionES = evalEyePositionFromZeye(side.x, tapMipZ.y, fragUVPos); + float tapCosHorizonAngle = computeWeightedHorizonFromTap(tapPositionES, fragPositionES, fragNormalES); cosHorizonAngle = max(cosHorizonAngle, tapCosHorizonAngle); @@ -378,7 +383,8 @@ float computeHorizonFromTap(vec3 tapPositionES, vec3 fragPositionES, vec3 fragNo #define HBAO_HORIZON_SEARCH_CONSTANT_STEP 0 -float computeHorizon(ivec4 side, vec2 fragUVPos, vec3 fragPositionES, vec3 fragNormalES, vec2 searchVec, vec2 pixelSearchVec, float searchRadius) { +float computeHorizon(ivec4 side, vec2 fragUVPos, vec3 fragPositionES, vec3 fragNormalES, vec2 searchVec, vec2 pixelSearchVec, + float searchRadius) { vec2 absSearchVec = abs(searchVec); pixelSearchVec = abs(pixelSearchVec); int stepCount = int(ceil(max(pixelSearchVec.x, pixelSearchVec.y))); @@ -386,26 +392,26 @@ float computeHorizon(ivec4 side, vec2 fragUVPos, vec3 fragPositionES, vec3 fragN if (stepCount>0) { vec2 deltaTapUV = searchVec / float(stepCount); + float deltaRadius = searchRadius / float(stepCount); - vec2 tapUVOffset = vec2(0); #if HBAO_HORIZON_SEARCH_CONSTANT_STEP float radius = 0.0; int stepIndex; for (stepIndex=0 ; stepIndex } -// Step is adapted to Mip level #else + // Step is adapted to Mip level float radius = deltaRadius; float mipLevel = evalMipFromRadius(radius * float(doFetchMips())); while (radius<=searchRadius) { - tapUVOffset += deltaTapUV; + fragUVPos += deltaTapUV; <$computeHorizon()$> diff --git a/libraries/render-utils/src/ssao_makeOcclusion.slf b/libraries/render-utils/src/ssao_makeOcclusion.slf index 74617cc4bb..cfccf32938 100644 --- a/libraries/render-utils/src/ssao_makeOcclusion.slf +++ b/libraries/render-utils/src/ssao_makeOcclusion.slf @@ -19,7 +19,7 @@ <$declarePackOcclusionDepth()$> -#define SSAO_HBAO_MAX_RADIUS 100.0 +#define SSAO_HBAO_MAX_RADIUS 300.0 layout(location=0) in vec2 varTexCoord0; @@ -82,7 +82,8 @@ void main(void) { } } - float occlusion = clamp(1.0 - obscuranceSum * getObscuranceScaling() * invNumSamples, 0.0, 1.0); + obscuranceSum *= getObscuranceScaling() * invNumSamples; + float occlusion = clamp(1.0 - obscuranceSum * obscuranceSum, 0.0, 1.0); - outFragColor = packOcclusionOutput(occlusion * occlusion, fragPositionES.z, fragNormalES); + outFragColor = packOcclusionOutput(occlusion, fragPositionES.z, fragNormalES); } From 9f9fe909b0fc242b8cd960d9c19b1781fc21673a Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Fri, 28 Sep 2018 19:22:20 +0200 Subject: [PATCH 054/286] Fixed bugs in stereo --- .../src/AmbientOcclusionEffect.cpp | 49 +++++++++---------- .../render-utils/src/SurfaceGeometryPass.cpp | 4 +- libraries/render-utils/src/ssao.slh | 26 +++++++--- .../render-utils/src/ssao_bilateralBlur.slf | 19 ++++--- 4 files changed, 56 insertions(+), 42 deletions(-) diff --git a/libraries/render-utils/src/AmbientOcclusionEffect.cpp b/libraries/render-utils/src/AmbientOcclusionEffect.cpp index 5b27cfa41d..86de3cec04 100644 --- a/libraries/render-utils/src/AmbientOcclusionEffect.cpp +++ b/libraries/render-utils/src/AmbientOcclusionEffect.cpp @@ -92,12 +92,13 @@ void AmbientOcclusionFramebuffer::allocate() { { auto width = _frameSize.x; auto height = _frameSize.y; + auto sampler = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR, gpu::Sampler::WRAP_CLAMP); - _occlusionTexture = gpu::Texture::createRenderBuffer(occlusionformat, width, height, gpu::Texture::SINGLE_MIP, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR, gpu::Sampler::WRAP_CLAMP)); + _occlusionTexture = gpu::Texture::createRenderBuffer(occlusionformat, width, height, gpu::Texture::SINGLE_MIP, sampler); _occlusionFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("occlusion")); _occlusionFramebuffer->setRenderBuffer(0, _occlusionTexture); - _occlusionBlurredTexture = gpu::Texture::createRenderBuffer(occlusionformat, width, height, gpu::Texture::SINGLE_MIP, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR, gpu::Sampler::WRAP_CLAMP)); + _occlusionBlurredTexture = gpu::Texture::createRenderBuffer(occlusionformat, width, height, gpu::Texture::SINGLE_MIP, sampler); _occlusionBlurredFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("occlusionBlurred")); _occlusionBlurredFramebuffer->setRenderBuffer(0, _occlusionBlurredTexture); } @@ -108,7 +109,7 @@ void AmbientOcclusionFramebuffer::allocate() { if (_isStereo) { sideSize.x >>= 1; } - sideSize = divideRoundUp(sideSize, 1 << _resolutionLevel); + sideSize >>= _resolutionLevel; if (_isStereo) { sideSize.x <<= 1; } @@ -127,7 +128,7 @@ void AmbientOcclusionFramebuffer::allocate() { if (_isStereo) { splitSize.x >>= 1; } - splitSize = divideRoundUp(splitSize, SSAO_SPLIT_COUNT << _resolutionLevel); + splitSize = divideRoundUp(_frameSize >> _resolutionLevel, SSAO_SPLIT_COUNT); if (_isStereo) { splitSize.x <<= 1; } @@ -355,14 +356,13 @@ void AmbientOcclusionEffect::configure(const Config& config) { void AmbientOcclusionEffect::updateBlurParameters() { const auto resolutionLevel = _aoParametersBuffer->getResolutionLevel(); - const auto resolutionScale = 1 << resolutionLevel; auto& vblur = _vblurParametersBuffer.edit(); auto& hblur = _hblurParametersBuffer.edit(); auto frameSize = _framebuffer->getSourceFrameSize(); if (_framebuffer->isStereo()) { frameSize.x >>= 1; } - const auto occlusionSize = divideRoundUp(frameSize, resolutionScale); + const auto occlusionSize = frameSize >> resolutionLevel; // Occlusion UV limit hblur._blurAxis.z = occlusionSize.x / float(frameSize.x); @@ -381,18 +381,17 @@ void AmbientOcclusionEffect::updateBlurParameters() { void AmbientOcclusionEffect::updateFramebufferSizes() { auto& params = _aoParametersBuffer.edit(); - const int widthScale = _framebuffer->isStereo() & 1; + const int stereoDivide = _framebuffer->isStereo() & 1; auto sourceFrameSideSize = _framebuffer->getSourceFrameSize(); - sourceFrameSideSize.x >>= widthScale; + sourceFrameSideSize.x >>= stereoDivide; const int resolutionLevel = _aoParametersBuffer.get().getResolutionLevel(); - // Depth is at maximum half depth const int depthResolutionLevel = getDepthResolutionLevel(); - const auto occlusionDepthFrameSize = divideRoundUp(sourceFrameSideSize, 1 << depthResolutionLevel); - const auto occlusionFrameSize = divideRoundUp(sourceFrameSideSize, 1 << resolutionLevel); + const auto occlusionDepthFrameSize = sourceFrameSideSize >> depthResolutionLevel; + const auto occlusionFrameSize = sourceFrameSideSize >> resolutionLevel; auto normalTextureSize = _framebuffer->getNormalTexture()->getDimensions(); - normalTextureSize.x >>= widthScale; + normalTextureSize.x >>= stereoDivide; params._sideSizes[0].x = normalTextureSize.x; params._sideSizes[0].y = normalTextureSize.y; @@ -485,12 +484,10 @@ void AmbientOcclusionEffect::run(const render::RenderContextPointer& renderConte const auto depthResolutionScale = powf(0.5f, depthResolutionLevel); const auto isHorizonBased = _aoParametersBuffer->isHorizonBased(); - auto linearDepthTexture = linearDepthFramebuffer->getLinearDepthTexture(); - auto occlusionDepthTexture = linearDepthTexture; + auto fullResDepthTexture = linearDepthFramebuffer->getLinearDepthTexture(); + auto occlusionDepthTexture = fullResDepthTexture; auto sourceViewport = args->_viewport; - // divideRoundUp is used two compute the quarter or half resolution render sizes. - // We choose to take the rounded up resolution. - auto occlusionViewport = divideRoundUp(sourceViewport, 1 << resolutionLevel); + auto occlusionViewport = sourceViewport >> resolutionLevel; auto firstBlurViewport = sourceViewport; firstBlurViewport.w = occlusionViewport.w; @@ -506,7 +503,7 @@ void AmbientOcclusionEffect::run(const render::RenderContextPointer& renderConte occlusionDepthTexture = linearDepthFramebuffer->getHalfLinearDepthTexture(); } - if (_framebuffer->update(linearDepthTexture, resolutionLevel, depthResolutionLevel, args->isStereo())) { + if (_framebuffer->update(fullResDepthTexture, resolutionLevel, depthResolutionLevel, args->isStereo())) { updateBlurParameters(); updateFramebufferSizes(); } @@ -538,7 +535,7 @@ void AmbientOcclusionEffect::run(const render::RenderContextPointer& renderConte auto& sample = _aoFrameParametersBuffer[splitId].edit(); sample._angleInfo.x = _randomSamples[splitId + SSAO_RANDOM_SAMPLE_COUNT * _frameId]; } - // _frameId = (_frameId + 1) % SSAO_RANDOM_SAMPLE_COUNT; + _frameId = (_frameId + 1) % SSAO_RANDOM_SAMPLE_COUNT; gpu::doInBatch("AmbientOcclusionEffect::run", args->_context, [=](gpu::Batch& batch) { PROFILE_RANGE_BATCH(batch, "SSAO"); @@ -567,7 +564,7 @@ void AmbientOcclusionEffect::run(const render::RenderContextPointer& renderConte batch.setModelTransform(Transform()); batch.setViewportTransform(normalViewport); batch.setPipeline(buildNormalsPipeline); - batch.setResourceTexture(render_utils::slot::texture::SsaoDepth, linearDepthTexture); + batch.setResourceTexture(render_utils::slot::texture::SsaoDepth, fullResDepthTexture); batch.setResourceTexture(render_utils::slot::texture::SsaoNormal, nullptr); batch.setUniformBuffer(render_utils::slot::buffer::DeferredFrameTransform, frameTransform->getFrameTransformBuffer()); batch.setUniformBuffer(render_utils::slot::buffer::SsaoParams, _aoParametersBuffer); @@ -651,8 +648,8 @@ void AmbientOcclusionEffect::run(const render::RenderContextPointer& renderConte batch.setModelTransform(model); } batch.setPipeline(bilateralBlurPipeline); - // Use full resolution depth and normal for bilateral upscaling and blur - batch.setResourceTexture(render_utils::slot::texture::SsaoDepth, linearDepthTexture); + // Use full resolution depth for bilateral upscaling and blur + batch.setResourceTexture(render_utils::slot::texture::SsaoDepth, fullResDepthTexture); #if SSAO_USE_QUAD_SPLIT batch.setResourceTexture(render_utils::slot::texture::SsaoNormal, occlusionNormalTexture); #else @@ -742,18 +739,18 @@ void DebugAmbientOcclusion::run(const render::RenderContextPointer& renderContex return; } - auto linearDepthTexture = linearDepthFramebuffer->getLinearDepthTexture(); + auto fullResDepthTexture = linearDepthFramebuffer->getLinearDepthTexture(); auto sourceViewport = args->_viewport; auto occlusionViewport = sourceViewport; auto resolutionLevel = ambientOcclusionUniforms->getResolutionLevel(); if (resolutionLevel > 0) { - linearDepthTexture = linearDepthFramebuffer->getHalfLinearDepthTexture(); + fullResDepthTexture = linearDepthFramebuffer->getHalfLinearDepthTexture(); occlusionViewport = occlusionViewport >> ambientOcclusionUniforms->getResolutionLevel(); } - auto framebufferSize = glm::ivec2(linearDepthTexture->getDimensions()); + auto framebufferSize = glm::ivec2(fullResDepthTexture->getDimensions()); float sMin = occlusionViewport.x / (float)framebufferSize.x; float sWidth = occlusionViewport.z / (float)framebufferSize.x; @@ -779,7 +776,7 @@ void DebugAmbientOcclusion::run(const render::RenderContextPointer& renderContex batch.setUniformBuffer(render_utils::slot::buffer::SsaoDebugParams, _parametersBuffer); batch.setPipeline(debugPipeline); - batch.setResourceTexture(render_utils::slot::texture::SsaoDepth, linearDepthTexture); + batch.setResourceTexture(render_utils::slot::texture::SsaoDepth, fullResDepthTexture); batch.draw(gpu::TRIANGLE_STRIP, 4); diff --git a/libraries/render-utils/src/SurfaceGeometryPass.cpp b/libraries/render-utils/src/SurfaceGeometryPass.cpp index d5e0249a21..59d2198daa 100644 --- a/libraries/render-utils/src/SurfaceGeometryPass.cpp +++ b/libraries/render-utils/src/SurfaceGeometryPass.cpp @@ -45,7 +45,7 @@ void LinearDepthFramebuffer::update(const gpu::TexturePointer& depthBuffer, cons if (isStereo) { _halfFrameSize.x >>= 1; } - _halfFrameSize = divideRoundUp(_halfFrameSize, 2); + _halfFrameSize >>= 1; if (isStereo) { _halfFrameSize.x <<= 1; } @@ -182,7 +182,7 @@ void LinearDepthPass::run(const render::RenderContextPointer& renderContext, con auto downsamplePipeline = getDownsamplePipeline(renderContext); auto depthViewport = args->_viewport; - auto halfViewport = divideRoundUp(depthViewport, 2); + auto halfViewport = depthViewport >> 1; float clearLinearDepth = args->getViewFrustum().getFarClip() * 2.0f; gpu::doInBatch("LinearDepthPass::run", args->_context, [=](gpu::Batch& batch) { diff --git a/libraries/render-utils/src/ssao.slh b/libraries/render-utils/src/ssao.slh index 9590dd9277..a5bb689d06 100644 --- a/libraries/render-utils/src/ssao.slh +++ b/libraries/render-utils/src/ssao.slh @@ -260,11 +260,20 @@ float getZEyeAtUV(vec2 texCoord, int level) { return -textureLod(depthPyramidTex, texCoord, level).x; } +float getZEyeAtUV(vec2 texCoord, int level, ivec2 texelOffset) { + return -textureLodOffset(depthPyramidTex, texCoord, level, texelOffset).x; +} + float getZEyeAtUV(ivec4 side, vec2 texCoord, int level) { texCoord.x = mix(texCoord.x, (texCoord.x + getStereoSide(side)) * 0.5, isStereo()); return getZEyeAtUV(texCoord, level); } +float getZEyeAtUV(ivec4 side, vec2 texCoord, int level, ivec2 texelOffset) { + texCoord.x = mix(texCoord.x, (texCoord.x + getStereoSide(side)) * 0.5, isStereo()); + return getZEyeAtUV(texCoord, level, texelOffset); +} + vec3 getNormalEyeAtUV(vec2 texCoord, int level) { return normalize(textureLod(normalTex, texCoord, level).xyz*2.0 - vec3(1.0)); } @@ -301,20 +310,25 @@ vec3 buildPosition(ivec4 side, vec2 fragUVPos) { return evalEyePositionFromZeye(side.x, Zeye, fragUVPos); } +vec3 buildPosition(ivec4 side, vec2 fragUVPos, ivec2 texelOffset, vec2 deltaUV) { + float Zeye = getZEyeAtUV(side, fragUVPos, 0, texelOffset); + return evalEyePositionFromZeye(side.x, Zeye, fragUVPos + texelOffset*deltaUV); +} + vec3 getMinDelta(vec3 centralPoint, vec3 offsetPointPos, vec3 offsetPointNeg) { vec3 delta0 = offsetPointPos - centralPoint; vec3 delta1 = centralPoint - offsetPointNeg; float sqrLength0 = dot(delta0, delta0); float sqrLength1 = dot(delta1, delta1); - float epsilon = 1e-6; - return sqrLength0 < sqrLength1 && sqrLength0>epsilon ? delta0 : delta1; + + return sqrLength0 < sqrLength1 ? delta0 : delta1; } vec3 buildNormal(ivec4 side, vec2 fragUVPos, vec3 fragPosition, vec2 deltaDepthUV) { - vec3 fragPositionDxPos = buildPosition(side, fragUVPos + vec2(deltaDepthUV.x, 0)); - vec3 fragPositionDxNeg = buildPosition(side, fragUVPos - vec2(deltaDepthUV.x, 0)); - vec3 fragPositionDyPos = buildPosition(side, fragUVPos + vec2(0, deltaDepthUV.y)); - vec3 fragPositionDyNeg = buildPosition(side, fragUVPos - vec2(0, deltaDepthUV.y)); + vec3 fragPositionDxPos = buildPosition(side, fragUVPos, ivec2(1,0), deltaDepthUV); + vec3 fragPositionDxNeg = buildPosition(side, fragUVPos, ivec2(-1,0), deltaDepthUV); + vec3 fragPositionDyPos = buildPosition(side, fragUVPos, ivec2(0,1), deltaDepthUV); + vec3 fragPositionDyNeg = buildPosition(side, fragUVPos, ivec2(0,-1), deltaDepthUV); vec3 fragPositionDx = getMinDelta(fragPosition, fragPositionDxPos, fragPositionDxNeg); vec3 fragPositionDy = getMinDelta(fragPosition, fragPositionDyPos, fragPositionDyNeg); diff --git a/libraries/render-utils/src/ssao_bilateralBlur.slf b/libraries/render-utils/src/ssao_bilateralBlur.slf index e89d696cbb..bec6793307 100644 --- a/libraries/render-utils/src/ssao_bilateralBlur.slf +++ b/libraries/render-utils/src/ssao_bilateralBlur.slf @@ -43,7 +43,7 @@ int getBlurRadius() { } vec4 fetchOcclusionPacked(ivec4 side, vec2 texCoord) { - texCoord.x = mix(texCoord.x, (texCoord.x + getStereoSide(side)) * 0.5, isStereo()); + texCoord.x = mix(texCoord.x, (texCoord.x + getStereoSide(side) * getBlurOcclusionUVLimit().x) * 0.5, isStereo()); return textureLod(occlusionMap, texCoord, 0); } @@ -54,12 +54,11 @@ float evalBlurCoefficient(vec3 blurScales, float radialDistance, float zDistance const float BLUR_EDGE_NORMAL_LIMIT = 0.25; -vec2 evalTapWeightedValue(vec3 blurScales, ivec4 side, int r, vec2 occlusionTexCoord, vec2 depthTexCoord, float fragDepth, vec3 fragNormal) { +vec2 evalTapWeightedValue(vec3 blurScales, ivec4 side, int r, vec2 occlusionTexCoord, float fragDepth, vec3 fragNormal) { vec2 tapOcclusionTexCoord = getBlurOcclusionAxis() * r + occlusionTexCoord; vec2 occlusionTexCoordLimits = getBlurOcclusionUVLimit(); - if (tapOcclusionTexCoord.x < side.x || tapOcclusionTexCoord.x >= (side.x + occlusionTexCoordLimits.x) - || tapOcclusionTexCoord.y < 0 || tapOcclusionTexCoord.y >= occlusionTexCoordLimits.y) { + if (any(lessThan(tapOcclusionTexCoord, vec2(0.0))) || any(greaterThanEqual(tapOcclusionTexCoord, occlusionTexCoordLimits)) ) { return vec2(0.0); } @@ -85,10 +84,10 @@ vec4 getBlurredOcclusion(ivec2 destPixelCoord, vec2 occlusionTexCoord, vec2 dept // Stereo side info ivec4 side = getStereoSideInfo(destPixelCoord.x, 0); - float fragDepth = getZEyeAtUV(side, depthTexCoord, 0); + float fragDepth = getZEyeAtUV(depthTexCoord, 0); float fragDepthKey = CSZToDepthKey(fragDepth); #if SSAO_BILATERAL_BLUR_USE_NORMAL - vec3 fragNormal = getNormalEyeAtUV(side, depthTexCoord, 0); + vec3 fragNormal = getNormalEyeAtUV(depthTexCoord, 0); #else vec3 fragNormal = vec3(0, 0, 1); #endif @@ -98,9 +97,13 @@ vec4 getBlurredOcclusion(ivec2 destPixelCoord, vec2 occlusionTexCoord, vec2 dept int blurRadius = getBlurRadius(); vec3 blurScales = getBlurScales(); + // From now on, occlusionTexCoord is the UV pos in the side + float sideTexCoord = occlusionTexCoord.x * 2.0 - getStereoSide(side) * getBlurOcclusionUVLimit().x; + occlusionTexCoord.x = mix(occlusionTexCoord.x, sideTexCoord, isStereo()); + // negative side first for (int r = -blurRadius; r <= -1; ++r) { - weightedSums += evalTapWeightedValue(blurScales, side, r, occlusionTexCoord, depthTexCoord, fragDepthKey, fragNormal); + weightedSums += evalTapWeightedValue(blurScales, side, r, occlusionTexCoord, fragDepthKey, fragNormal); } // Central pixel contribution @@ -110,7 +113,7 @@ vec4 getBlurredOcclusion(ivec2 destPixelCoord, vec2 occlusionTexCoord, vec2 dept // then positive side for (int r = 1; r <= blurRadius; ++r) { - weightedSums += evalTapWeightedValue(blurScales, side, r, occlusionTexCoord, depthTexCoord, fragDepthKey, fragNormal); + weightedSums += evalTapWeightedValue(blurScales, side, r, occlusionTexCoord, fragDepthKey, fragNormal); } // Final normalization From abc415c5ad265f1ed7ea03247d8c27ffd9a3c9c5 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Fri, 28 Sep 2018 19:33:34 +0200 Subject: [PATCH 055/286] Put back temporal jitter of SSAO (but without filtering) --- libraries/render-utils/src/AmbientOcclusionEffect.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/render-utils/src/AmbientOcclusionEffect.h b/libraries/render-utils/src/AmbientOcclusionEffect.h index dbbbee76d0..50b35f7941 100644 --- a/libraries/render-utils/src/AmbientOcclusionEffect.h +++ b/libraries/render-utils/src/AmbientOcclusionEffect.h @@ -202,7 +202,7 @@ private: static gpu::PipelinePointer _buildNormalsPipeline; AmbientOcclusionFramebufferPointer _framebuffer; - std::array _randomSamples; + std::array _randomSamples; int _frameId{ 0 }; gpu::RangeTimerPointer _gpuTimer; From 7f6c9a6cc1fb1677a3e7be8d61ea492545e9467d Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Mon, 1 Oct 2018 19:22:54 +0200 Subject: [PATCH 056/286] Switched to sin based HBAO --- .../src/AmbientOcclusionEffect.cpp | 20 ++- .../render-utils/src/AmbientOcclusionEffect.h | 2 +- libraries/render-utils/src/ssao.slh | 137 +++++++++++++----- .../render-utils/src/ssao_makeOcclusion.slf | 25 +++- libraries/render-utils/src/ssao_shared.h | 1 + .../utilities/render/ambientOcclusionPass.qml | 2 +- 6 files changed, 135 insertions(+), 52 deletions(-) diff --git a/libraries/render-utils/src/AmbientOcclusionEffect.cpp b/libraries/render-utils/src/AmbientOcclusionEffect.cpp index 86de3cec04..54edfad3ec 100644 --- a/libraries/render-utils/src/AmbientOcclusionEffect.cpp +++ b/libraries/render-utils/src/AmbientOcclusionEffect.cpp @@ -206,10 +206,10 @@ gpu::TexturePointer AmbientOcclusionFramebuffer::getNormalTexture() { AmbientOcclusionEffectConfig::AmbientOcclusionEffectConfig() : render::GPUJobConfig::Persistent(QStringList() << "Render" << "Engine" << "Ambient Occlusion", false), - radius{ 0.3f }, + radius{ 0.5f }, perspectiveScale{ 1.0f }, - obscuranceLevel{ 0.5f }, - falloffAngle{ 0.45f }, + obscuranceLevel{ 0.25f }, + falloffAngle{ 0.7f }, edgeSharpness{ 1.0f }, blurDeviation{ 2.5f }, numSpiralTurns{ 7.0f }, @@ -227,6 +227,7 @@ AmbientOcclusionEffect::AOParameters::AOParameters() { _radiusInfo = { 0.5f, 0.5f * 0.5f, 1.0f / (0.25f * 0.25f * 0.25f), 1.0f }; _ditheringInfo = { 0.0f, 0.0f, 0.01f, 1.0f }; _sampleInfo = { 11.0f, 1.0f / 11.0f, 7.0f, 1.0f }; + _falloffInfo = { 0.5f, 2.0f, 0.866f, 1.1547f }; } AmbientOcclusionEffect::BlurParameters::BlurParameters() { @@ -263,10 +264,13 @@ void AmbientOcclusionEffect::configure(const Config& config) { current.w = config.obscuranceLevel; } - if (config.falloffAngle != _aoParametersBuffer->getFalloffAngle()) { - auto& current = _aoParametersBuffer.edit()._ditheringInfo; - current.z = config.falloffAngle; - current.y = 1.0f / (1.0f - config.falloffAngle); + if (config.falloffAngle != _aoParametersBuffer->getFalloffCosAngle()) { + auto& current = _aoParametersBuffer.edit()._falloffInfo; + current.x = config.falloffAngle; + current.y = 1.0f / (1.0f - current.x); + // Compute sin from cos + current.z = sqrtf(1.0f - config.falloffAngle * config.falloffAngle); + current.w = 1.0f / current.z; } // Update bilateral blur @@ -535,7 +539,7 @@ void AmbientOcclusionEffect::run(const render::RenderContextPointer& renderConte auto& sample = _aoFrameParametersBuffer[splitId].edit(); sample._angleInfo.x = _randomSamples[splitId + SSAO_RANDOM_SAMPLE_COUNT * _frameId]; } - _frameId = (_frameId + 1) % SSAO_RANDOM_SAMPLE_COUNT; + //_frameId = (_frameId + 1) % SSAO_RANDOM_SAMPLE_COUNT; gpu::doInBatch("AmbientOcclusionEffect::run", args->_context, [=](gpu::Batch& batch) { PROFILE_RANGE_BATCH(batch, "SSAO"); diff --git a/libraries/render-utils/src/AmbientOcclusionEffect.h b/libraries/render-utils/src/AmbientOcclusionEffect.h index 50b35f7941..8d5eef42a5 100644 --- a/libraries/render-utils/src/AmbientOcclusionEffect.h +++ b/libraries/render-utils/src/AmbientOcclusionEffect.h @@ -150,7 +150,7 @@ public: float getRadius() const { return _radiusInfo.x; } float getPerspectiveScale() const { return _resolutionInfo.z; } float getObscuranceLevel() const { return _radiusInfo.w; } - float getFalloffAngle() const { return (float)_ditheringInfo.z; } + float getFalloffCosAngle() const { return (float)_falloffInfo.x; } float getNumSpiralTurns() const { return _sampleInfo.z; } int getNumSamples() const { return (int)_sampleInfo.x; } diff --git a/libraries/render-utils/src/ssao.slh b/libraries/render-utils/src/ssao.slh index a5bb689d06..a289654486 100644 --- a/libraries/render-utils/src/ssao.slh +++ b/libraries/render-utils/src/ssao.slh @@ -115,11 +115,18 @@ float isBorderingEnabled() { return params._ditheringInfo.w; } -float getFalloffAngle() { - return params._ditheringInfo.z; +float getFalloffCosAngle() { + return params._falloffInfo.x; } -float getFalloffAngleScale() { - return params._ditheringInfo.y; +float getFalloffCosAngleScale() { + return params._falloffInfo.y; +} + +float getFalloffSinAngle() { + return params._falloffInfo.z; +} +float getFalloffSinAngleScale() { + return params._falloffInfo.w; } float getNumSamples() { @@ -330,10 +337,27 @@ vec3 buildNormal(ivec4 side, vec2 fragUVPos, vec3 fragPosition, vec2 deltaDepthU vec3 fragPositionDyPos = buildPosition(side, fragUVPos, ivec2(0,1), deltaDepthUV); vec3 fragPositionDyNeg = buildPosition(side, fragUVPos, ivec2(0,-1), deltaDepthUV); - vec3 fragPositionDx = getMinDelta(fragPosition, fragPositionDxPos, fragPositionDxNeg); - vec3 fragPositionDy = getMinDelta(fragPosition, fragPositionDyPos, fragPositionDyNeg); + vec3 fragDeltaDx = getMinDelta(fragPosition, fragPositionDxPos, fragPositionDxNeg); + vec3 fragDeltaDy = getMinDelta(fragPosition, fragPositionDyPos, fragPositionDyNeg); - return normalize( cross(fragPositionDx, fragPositionDy) ); + return normalize( cross(fragDeltaDx, fragDeltaDy) ); +} + +void buildTangentBinormal(ivec4 side, vec2 fragUVPos, vec3 fragPosition, vec3 fragNormal, vec2 deltaDepthUV, + out vec3 fragTangent, out vec3 fragBinormal) { + vec3 fragPositionDxPos = buildPosition(side, fragUVPos, ivec2(1,0), deltaDepthUV); + vec3 fragPositionDxNeg = buildPosition(side, fragUVPos, ivec2(-1,0), deltaDepthUV); + vec3 fragPositionDyPos = buildPosition(side, fragUVPos, ivec2(0,1), deltaDepthUV); + vec3 fragPositionDyNeg = buildPosition(side, fragUVPos, ivec2(0,-1), deltaDepthUV); + + vec3 fragDeltaDx = getMinDelta(fragPosition, fragPositionDxPos, fragPositionDxNeg); + vec3 fragDeltaDy = getMinDelta(fragPosition, fragPositionDyPos, fragPositionDyNeg); + + //fragTangent = normalize( cross(fragDeltaDy, fragNormal) ); + //fragBinormal = normalize( cross(fragNormal, fragDeltaDx) ); + + fragTangent = fragDeltaDx; + fragBinormal = fragDeltaDy; } <@endfunc@> @@ -341,6 +365,12 @@ vec3 buildNormal(ivec4 side, vec2 fragUVPos, vec3 fragPosition, vec2 deltaDepthU <@func declareEvalObscurance()@> +struct TBNFrame { + vec3 tangent; + vec3 binormal; + vec3 normal; +}; + vec3 fastAcos(vec3 x) { // [Eberly2014] GPGPU Programming for Games and Science vec3 absX = abs(x); @@ -357,29 +387,25 @@ float evalVisibilitySSAO(in vec3 centerPosition, in vec3 centerNormal, in vec3 t // Falloff function as recommended in SSAO paper const float epsilon = 0.01; float f = max(getRadius2() - vv, 0.0); - return f * f * f * max((vn - getFalloffAngle()) / (epsilon + vv), 0.0); + return f * f * f * max((vn - getFalloffCosAngle()) / (epsilon + vv), 0.0); } -vec2 computeHorizonFromTap(vec3 tapPositionES, vec3 fragPositionES, vec3 fragNormalES) { - const float epsilon = 0.001; +#define HBAO_USE_COS_ANGLE 0 - vec3 deltaVec = tapPositionES - fragPositionES; - float distance = length(deltaVec); - float cosHorizonAngle = dot(deltaVec, fragNormalES) / (distance + epsilon); +float computeWeightedHorizon(float horizonLimit, float distanceSquared) { + float radiusFalloff = distanceSquared / getRadius2(); - return vec2(cosHorizonAngle, distance); -} + radiusFalloff = max(0.0, 1.0 - radiusFalloff); -float computeWeightedHorizonFromTap(vec3 tapPositionES, vec3 fragPositionES, vec3 fragNormalES) { - vec2 rawHorizon = computeHorizonFromTap(tapPositionES, fragPositionES, fragNormalES); - float distance = rawHorizon.y; - float cosHorizonAngle = rawHorizon.x; - float radiusFalloff = max(0.0, 1.0 - (distance*distance / getRadius2())); +#if !HBAO_USE_COS_ANGLE + horizonLimit = getFalloffSinAngle() - horizonLimit; +#endif + horizonLimit *= radiusFalloff; +#if !HBAO_USE_COS_ANGLE + horizonLimit = getFalloffSinAngle() - horizonLimit; +#endif - cosHorizonAngle = max(0.0, (cosHorizonAngle - getFalloffAngle()) * getFalloffAngleScale()); - cosHorizonAngle *= radiusFalloff; - - return cosHorizonAngle; + return horizonLimit; } <@func computeHorizon()@> @@ -389,24 +415,46 @@ float computeWeightedHorizonFromTap(vec3 tapPositionES, vec3 fragPositionES, vec } vec2 tapMipZ = fetchTap(side, fragUVPos, radius); vec3 tapPositionES = evalEyePositionFromZeye(side.x, tapMipZ.y, fragUVPos); - float tapCosHorizonAngle = computeWeightedHorizonFromTap(tapPositionES, fragPositionES, fragNormalES); + vec3 deltaVec = tapPositionES - fragPositionES; + float distanceSquared = dot(deltaVec, deltaVec); + float distance = sqrt(distanceSquared); + float deltaDotNormal = dot(deltaVec, fragFrameES.normal); +#if HBAO_USE_COS_ANGLE + float tapHorizonLimit = deltaDotNormal; +#else + float tapHorizonLimit = dot(deltaVec, fragFrameES.tangent); +#endif + float epsilon = 0.0001; + tapHorizonLimit /= (distance + epsilon); - cosHorizonAngle = max(cosHorizonAngle, tapCosHorizonAngle); - + if (distanceSquared < getRadius2() && deltaDotNormal>0.0 && +#if HBAO_USE_COS_ANGLE + tapHorizonLimit > horizonLimit +#else + tapHorizonLimit < horizonLimit +#endif + ) { + tapHorizonLimit = computeWeightedHorizon(tapHorizonLimit, distanceSquared); +#if HBAO_USE_COS_ANGLE + horizonLimit = max(horizonLimit, tapHorizonLimit); +#else + horizonLimit = min(horizonLimit, tapHorizonLimit); +#endif + } <@endfunc@> #define HBAO_HORIZON_SEARCH_CONSTANT_STEP 0 -float computeHorizon(ivec4 side, vec2 fragUVPos, vec3 fragPositionES, vec3 fragNormalES, vec2 searchVec, vec2 pixelSearchVec, - float searchRadius) { +float computeHorizon(ivec4 side, vec2 fragUVPos, vec3 fragPositionES, TBNFrame fragFrameES, vec2 searchVec, float searchRadius, int stepCount) { vec2 absSearchVec = abs(searchVec); - pixelSearchVec = abs(pixelSearchVec); - int stepCount = int(ceil(max(pixelSearchVec.x, pixelSearchVec.y))); - float cosHorizonAngle = 0.0; +#if HBAO_USE_COS_ANGLE + float horizonLimit = getFalloffCosAngle(); +#else + float horizonLimit = getFalloffSinAngle(); +#endif if (stepCount>0) { vec2 deltaTapUV = searchVec / float(stepCount); - float deltaRadius = searchRadius / float(stepCount); #if HBAO_HORIZON_SEARCH_CONSTANT_STEP @@ -439,22 +487,33 @@ float computeHorizon(ivec4 side, vec2 fragUVPos, vec3 fragPositionES, vec3 fragN #endif } - return cosHorizonAngle; + return horizonLimit; } float evalVisibilityHBAO(ivec4 side, vec2 fragUVPos, vec2 invSideImageSize, vec2 deltaTap, float diskPixelRadius, - vec3 fragPositionES, vec3 fragNormalES) { + vec3 fragPositionES, TBNFrame fragFrameES) { vec2 pixelSearchVec = deltaTap * diskPixelRadius; vec2 searchVec = pixelSearchVec * invSideImageSize; - float obscurance = 0.0; + float obscuranceH1 = 0.0; + float obscuranceH2 = 0.0; + +#if !HBAO_USE_COS_ANGLE + fragFrameES.tangent = normalize(fragFrameES.tangent * deltaTap.x + fragFrameES.binormal * deltaTap.y); +#endif + + pixelSearchVec = abs(pixelSearchVec); + int stepCount = int(ceil(max(pixelSearchVec.x, pixelSearchVec.y))); // Forward search for h1 - obscurance = computeHorizon(side, fragUVPos, fragPositionES, fragNormalES, searchVec, pixelSearchVec, diskPixelRadius); + obscuranceH1 = computeHorizon(side, fragUVPos, fragPositionES, fragFrameES, searchVec, diskPixelRadius, stepCount); // Backward search for h2 - obscurance += computeHorizon(side, fragUVPos, fragPositionES, fragNormalES, -searchVec, pixelSearchVec, diskPixelRadius); +#if !HBAO_USE_COS_ANGLE + fragFrameES.tangent = -fragFrameES.tangent; +#endif + obscuranceH2 = computeHorizon(side, fragUVPos, fragPositionES, fragFrameES, -searchVec, diskPixelRadius, stepCount); - return obscurance; + return obscuranceH1 + obscuranceH2; } <@endfunc@> diff --git a/libraries/render-utils/src/ssao_makeOcclusion.slf b/libraries/render-utils/src/ssao_makeOcclusion.slf index cfccf32938..76357ffbb9 100644 --- a/libraries/render-utils/src/ssao_makeOcclusion.slf +++ b/libraries/render-utils/src/ssao_makeOcclusion.slf @@ -55,6 +55,15 @@ void main(void) { diskPixelRadius = min(diskPixelRadius, SSAO_HBAO_MAX_RADIUS); } + TBNFrame fragFrameES; + + fragFrameES.tangent = vec3(0.0); + fragFrameES.binormal = vec3(0.0); + fragFrameES.normal = fragNormalES; +#if !HBAO_USE_COS_ANGLE + buildTangentBinormal(side, fragUVPos, fragPositionES, fragNormalES, deltaDepthUV, fragFrameES.tangent, fragFrameES.binormal); +#endif + // Let's make noise float randomPatternRotationAngle = getAngleDithering(fragPixelPos); @@ -66,9 +75,18 @@ void main(void) { if (isHorizonBased()) { for (int i = 0; i < numSamples; ++i) { vec3 deltaTap = getUnitTapLocation(i, 1.0, randomPatternRotationAngle, PI); - obscuranceSum += evalVisibilityHBAO(side, fragUVPos, deltaDepthUV, deltaTap.xy, diskPixelRadius, fragPositionES, fragNormalES); + obscuranceSum += evalVisibilityHBAO(side, fragUVPos, deltaDepthUV, deltaTap.xy, diskPixelRadius, fragPositionES, fragFrameES); } + obscuranceSum *= invNumSamples; +#if HBAO_USE_COS_ANGLE obscuranceSum *= 0.5 / PI; + obscuranceSum = 1.0 - obscuranceSum * obscuranceSum * getObscuranceScaling(); +#else + obscuranceSum *= 0.5; + obscuranceSum += 1.0 - getFalloffSinAngle(); + + obscuranceSum = mix(1.0, obscuranceSum, getObscuranceScaling()); +#endif } else { // Steps are in the depth texture resolution vec2 depthTexFragPixelPos = fragUVPos * sideDepthSize; @@ -80,10 +98,11 @@ void main(void) { vec3 tapPositionES = evalEyePositionFromZeye(side.x, tapMipZ.y, tapUV); obscuranceSum += float(tap.z > 0.0) * evalVisibilitySSAO(fragPositionES, fragNormalES, tapPositionES); } + obscuranceSum *= invNumSamples; + obscuranceSum = 1.0 - obscuranceSum * obscuranceSum * getObscuranceScaling(); } - obscuranceSum *= getObscuranceScaling() * invNumSamples; - float occlusion = clamp(1.0 - obscuranceSum * obscuranceSum, 0.0, 1.0); + float occlusion = clamp(obscuranceSum, 0.0, 1.0); outFragColor = packOcclusionOutput(occlusion, fragPositionES.z, fragNormalES); } diff --git a/libraries/render-utils/src/ssao_shared.h b/libraries/render-utils/src/ssao_shared.h index 1552a38947..46e373c073 100644 --- a/libraries/render-utils/src/ssao_shared.h +++ b/libraries/render-utils/src/ssao_shared.h @@ -40,6 +40,7 @@ struct AmbientOcclusionParams { SSAO_VEC4 _radiusInfo; SSAO_VEC4 _ditheringInfo; SSAO_VEC4 _sampleInfo; + SSAO_VEC4 _falloffInfo; SSAO_VEC4 _sideSizes[2]; }; diff --git a/scripts/developer/utilities/render/ambientOcclusionPass.qml b/scripts/developer/utilities/render/ambientOcclusionPass.qml index 5197fc312d..2ea3a6c416 100644 --- a/scripts/developer/utilities/render/ambientOcclusionPass.qml +++ b/scripts/developer/utilities/render/ambientOcclusionPass.qml @@ -37,7 +37,7 @@ Rectangle { "Level:obscuranceLevel:1.0:false", "Num Taps:numSamples:16:true", "Taps Spiral:numSpiralTurns:10.0:false", - "Falloff Angle:falloffAngle:0.5:false", + "Falloff Angle:falloffAngle:1.0:false", "Blur Edge Sharpness:edgeSharpness:1.0:false", "Blur Radius:blurRadius:15.0:true", "Resolution Downscale:resolutionLevel:2:true", From 454531e3c32832ca417980fc834f5951f878effe Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Tue, 2 Oct 2018 10:26:55 +0200 Subject: [PATCH 057/286] Switched back to cos HBAO --- .../src/AmbientOcclusionEffect.cpp | 35 ++++++---- .../render-utils/src/AmbientOcclusionEffect.h | 4 ++ libraries/render-utils/src/ssao.slh | 67 ++++++++++--------- .../render-utils/src/ssao_makeOcclusion.slf | 8 +-- .../utilities/render/ambientOcclusionPass.qml | 1 + 5 files changed, 66 insertions(+), 49 deletions(-) diff --git a/libraries/render-utils/src/AmbientOcclusionEffect.cpp b/libraries/render-utils/src/AmbientOcclusionEffect.cpp index 54edfad3ec..8e82cf9a9c 100644 --- a/libraries/render-utils/src/AmbientOcclusionEffect.cpp +++ b/libraries/render-utils/src/AmbientOcclusionEffect.cpp @@ -209,7 +209,7 @@ AmbientOcclusionEffectConfig::AmbientOcclusionEffectConfig() : radius{ 0.5f }, perspectiveScale{ 1.0f }, obscuranceLevel{ 0.25f }, - falloffAngle{ 0.7f }, + falloffAngle{ 0.4f }, edgeSharpness{ 1.0f }, blurDeviation{ 2.5f }, numSpiralTurns{ 7.0f }, @@ -219,15 +219,16 @@ AmbientOcclusionEffectConfig::AmbientOcclusionEffectConfig() : ditheringEnabled{ true }, borderingEnabled{ true }, fetchMipsEnabled{ true }, - horizonBased{ true } { + horizonBased{ true }, + jitterEnabled{ true }{ } AmbientOcclusionEffect::AOParameters::AOParameters() { - _resolutionInfo = { -1.0f, 1.0f, 1.0f, 0.0f }; - _radiusInfo = { 0.5f, 0.5f * 0.5f, 1.0f / (0.25f * 0.25f * 0.25f), 1.0f }; - _ditheringInfo = { 0.0f, 0.0f, 0.01f, 1.0f }; - _sampleInfo = { 11.0f, 1.0f / 11.0f, 7.0f, 1.0f }; - _falloffInfo = { 0.5f, 2.0f, 0.866f, 1.1547f }; + _resolutionInfo = glm::vec4{ 0.0f }; + _radiusInfo = glm::vec4{ 0.0f }; + _ditheringInfo = glm::vec4{ 0.0f }; + _sampleInfo = glm::vec4{ 0.0f }; + _falloffInfo = glm::vec4{ 0.0f }; } AmbientOcclusionEffect::BlurParameters::BlurParameters() { @@ -242,6 +243,8 @@ void AmbientOcclusionEffect::configure(const Config& config) { bool shouldUpdateBlurs = false; + _isJitterEnabled = config.jitterEnabled; + const double RADIUS_POWER = 6.0; const auto& radius = config.radius; if (radius != _aoParametersBuffer->getRadius() || config.horizonBased != _aoParametersBuffer->isHorizonBased()) { @@ -321,6 +324,7 @@ void AmbientOcclusionEffect::configure(const Config& config) { } _randomSamples[i] = r * 2.0f * M_PI / config.numSamples; } + updateJitterSamples(); } if (config.fetchMipsEnabled != _aoParametersBuffer->isFetchMipsEnabled()) { @@ -472,6 +476,14 @@ int AmbientOcclusionEffect::getDepthResolutionLevel() const { return std::min(1, _aoParametersBuffer->getResolutionLevel()); } +void AmbientOcclusionEffect::updateJitterSamples() { + const int SSAO_RANDOM_SAMPLE_COUNT = int(_randomSamples.size() / (SSAO_SPLIT_COUNT*SSAO_SPLIT_COUNT)); + for (int splitId = 0; splitId < SSAO_SPLIT_COUNT*SSAO_SPLIT_COUNT; splitId++) { + auto& sample = _aoFrameParametersBuffer[splitId].edit(); + sample._angleInfo.x = _randomSamples[splitId + SSAO_RANDOM_SAMPLE_COUNT * _frameId]; + } +} + void AmbientOcclusionEffect::run(const render::RenderContextPointer& renderContext, const Inputs& inputs, Outputs& outputs) { assert(renderContext->args); assert(renderContext->args->hasViewFrustum()); @@ -534,12 +546,11 @@ void AmbientOcclusionEffect::run(const render::RenderContextPointer& renderConte auto occlusionDepthSize = glm::ivec2(occlusionDepthTexture->getDimensions()); // Update sample rotation - const int SSAO_RANDOM_SAMPLE_COUNT = int(_randomSamples.size() / (SSAO_SPLIT_COUNT*SSAO_SPLIT_COUNT)); - for (int splitId=0 ; splitId < SSAO_SPLIT_COUNT*SSAO_SPLIT_COUNT ; splitId++) { - auto& sample = _aoFrameParametersBuffer[splitId].edit(); - sample._angleInfo.x = _randomSamples[splitId + SSAO_RANDOM_SAMPLE_COUNT * _frameId]; + if (_isJitterEnabled) { + const int SSAO_RANDOM_SAMPLE_COUNT = int(_randomSamples.size() / (SSAO_SPLIT_COUNT*SSAO_SPLIT_COUNT)); + updateJitterSamples(); + _frameId = (_frameId + 1) % SSAO_RANDOM_SAMPLE_COUNT; } - //_frameId = (_frameId + 1) % SSAO_RANDOM_SAMPLE_COUNT; gpu::doInBatch("AmbientOcclusionEffect::run", args->_context, [=](gpu::Batch& batch) { PROFILE_RANGE_BATCH(batch, "SSAO"); diff --git a/libraries/render-utils/src/AmbientOcclusionEffect.h b/libraries/render-utils/src/AmbientOcclusionEffect.h index 8d5eef42a5..3fb58e3437 100644 --- a/libraries/render-utils/src/AmbientOcclusionEffect.h +++ b/libraries/render-utils/src/AmbientOcclusionEffect.h @@ -83,6 +83,7 @@ class AmbientOcclusionEffectConfig : public render::GPUJobConfig::Persistent { Q_PROPERTY(bool ditheringEnabled MEMBER ditheringEnabled NOTIFY dirty) Q_PROPERTY(bool borderingEnabled MEMBER borderingEnabled NOTIFY dirty) Q_PROPERTY(bool fetchMipsEnabled MEMBER fetchMipsEnabled NOTIFY dirty) + Q_PROPERTY(bool jitterEnabled MEMBER jitterEnabled NOTIFY dirty) Q_PROPERTY(float radius MEMBER radius WRITE setRadius) Q_PROPERTY(float obscuranceLevel MEMBER obscuranceLevel WRITE setObscuranceLevel) Q_PROPERTY(float falloffAngle MEMBER falloffAngle WRITE setFalloffAngle) @@ -123,6 +124,7 @@ public: bool ditheringEnabled; // randomize the distribution of taps per pixel, should always be true bool borderingEnabled; // avoid evaluating information from non existing pixels out of the frame, should always be true bool fetchMipsEnabled; // fetch taps in sub mips to otpimize cache, should always be true + bool jitterEnabled; // Add small jittering to AO samples at each frame signals: void dirty(); @@ -181,6 +183,7 @@ private: void updateBlurParameters(); void updateFramebufferSizes(); + void updateJitterSamples(); int getDepthResolutionLevel() const; @@ -204,6 +207,7 @@ private: AmbientOcclusionFramebufferPointer _framebuffer; std::array _randomSamples; int _frameId{ 0 }; + bool _isJitterEnabled{ true }; gpu::RangeTimerPointer _gpuTimer; diff --git a/libraries/render-utils/src/ssao.slh b/libraries/render-utils/src/ssao.slh index a289654486..93d0fb95e7 100644 --- a/libraries/render-utils/src/ssao.slh +++ b/libraries/render-utils/src/ssao.slh @@ -390,12 +390,14 @@ float evalVisibilitySSAO(in vec3 centerPosition, in vec3 centerNormal, in vec3 t return f * f * f * max((vn - getFalloffCosAngle()) / (epsilon + vv), 0.0); } -#define HBAO_USE_COS_ANGLE 0 +#define HBAO_USE_COS_ANGLE 1 + +float computeWeightForHorizon(float horizonLimit, float distanceSquared) { + return max(0.0, 1.0 - distanceSquared / getRadius2()); +} float computeWeightedHorizon(float horizonLimit, float distanceSquared) { - float radiusFalloff = distanceSquared / getRadius2(); - - radiusFalloff = max(0.0, 1.0 - radiusFalloff); + float radiusFalloff = computeWeightForHorizon(horizonLimit, distanceSquared); #if !HBAO_USE_COS_ANGLE horizonLimit = getFalloffSinAngle() - horizonLimit; @@ -417,44 +419,46 @@ float computeWeightedHorizon(float horizonLimit, float distanceSquared) { vec3 tapPositionES = evalEyePositionFromZeye(side.x, tapMipZ.y, fragUVPos); vec3 deltaVec = tapPositionES - fragPositionES; float distanceSquared = dot(deltaVec, deltaVec); - float distance = sqrt(distanceSquared); float deltaDotNormal = dot(deltaVec, fragFrameES.normal); #if HBAO_USE_COS_ANGLE float tapHorizonLimit = deltaDotNormal; #else float tapHorizonLimit = dot(deltaVec, fragFrameES.tangent); #endif - float epsilon = 0.0001; - tapHorizonLimit /= (distance + epsilon); + tapHorizonLimit *= inversesqrt(distanceSquared); - if (distanceSquared < getRadius2() && deltaDotNormal>0.0 && + if (distanceSquared < getRadius2() && deltaDotNormal>0.0) { #if HBAO_USE_COS_ANGLE - tapHorizonLimit > horizonLimit -#else - tapHorizonLimit < horizonLimit -#endif - ) { - tapHorizonLimit = computeWeightedHorizon(tapHorizonLimit, distanceSquared); -#if HBAO_USE_COS_ANGLE - horizonLimit = max(horizonLimit, tapHorizonLimit); + float weight = computeWeightForHorizon(tapHorizonLimit, distanceSquared); + if (tapHorizonLimit > horizonLimit) { + occlusion += weight * (tapHorizonLimit - horizonLimit); + horizonLimit = tapHorizonLimit; + } else if (dot(deltaVec, fragFrameES.tangent) < 0.0) { + // This is a hack to try to handle the case where the occlusion angle is + // greater than 90° + occlusion = mix(occlusion, (occlusion+1.0) * 0.5, weight); + } #else - horizonLimit = min(horizonLimit, tapHorizonLimit); + if (tapHorizonLimit < horizonLimit) { + tapHorizonLimit = computeWeightedHorizon(tapHorizonLimit, distanceSquared); + horizonLimit = min(horizonLimit, tapHorizonLimit); + } #endif } <@endfunc@> #define HBAO_HORIZON_SEARCH_CONSTANT_STEP 0 -float computeHorizon(ivec4 side, vec2 fragUVPos, vec3 fragPositionES, TBNFrame fragFrameES, vec2 searchVec, float searchRadius, int stepCount) { - vec2 absSearchVec = abs(searchVec); +float computeOcclusion(ivec4 side, vec2 fragUVPos, vec3 fragPositionES, TBNFrame fragFrameES, vec2 searchDir, float searchRadius, int stepCount) { #if HBAO_USE_COS_ANGLE float horizonLimit = getFalloffCosAngle(); + float occlusion = 0.0; #else float horizonLimit = getFalloffSinAngle(); #endif if (stepCount>0) { - vec2 deltaTapUV = searchVec / float(stepCount); + vec2 deltaTapUV = searchDir / float(stepCount); float deltaRadius = searchRadius / float(stepCount); #if HBAO_HORIZON_SEARCH_CONSTANT_STEP @@ -487,31 +491,32 @@ float computeHorizon(ivec4 side, vec2 fragUVPos, vec3 fragPositionES, TBNFrame f #endif } - return horizonLimit; +#if HBAO_USE_COS_ANGLE + occlusion = min(occlusion * getFalloffCosAngleScale(), 1.0); +#else + occlusion = horizonLimit > 0.0 ? horizonLimit * getFalloffSinAngleScale() : horizonLimit; +#endif + + return occlusion; } float evalVisibilityHBAO(ivec4 side, vec2 fragUVPos, vec2 invSideImageSize, vec2 deltaTap, float diskPixelRadius, vec3 fragPositionES, TBNFrame fragFrameES) { vec2 pixelSearchVec = deltaTap * diskPixelRadius; - vec2 searchVec = pixelSearchVec * invSideImageSize; + vec2 searchDir = pixelSearchVec * invSideImageSize; float obscuranceH1 = 0.0; float obscuranceH2 = 0.0; - -#if !HBAO_USE_COS_ANGLE - fragFrameES.tangent = normalize(fragFrameES.tangent * deltaTap.x + fragFrameES.binormal * deltaTap.y); -#endif - pixelSearchVec = abs(pixelSearchVec); int stepCount = int(ceil(max(pixelSearchVec.x, pixelSearchVec.y))); + fragFrameES.tangent = normalize(fragFrameES.tangent * deltaTap.x + fragFrameES.binormal * deltaTap.y); + // Forward search for h1 - obscuranceH1 = computeHorizon(side, fragUVPos, fragPositionES, fragFrameES, searchVec, diskPixelRadius, stepCount); + obscuranceH1 = computeOcclusion(side, fragUVPos, fragPositionES, fragFrameES, searchDir, diskPixelRadius, stepCount); // Backward search for h2 -#if !HBAO_USE_COS_ANGLE fragFrameES.tangent = -fragFrameES.tangent; -#endif - obscuranceH2 = computeHorizon(side, fragUVPos, fragPositionES, fragFrameES, -searchVec, diskPixelRadius, stepCount); + obscuranceH2 = computeOcclusion(side, fragUVPos, fragPositionES, fragFrameES, -searchDir, diskPixelRadius, stepCount); return obscuranceH1 + obscuranceH2; } diff --git a/libraries/render-utils/src/ssao_makeOcclusion.slf b/libraries/render-utils/src/ssao_makeOcclusion.slf index 76357ffbb9..f0c406f446 100644 --- a/libraries/render-utils/src/ssao_makeOcclusion.slf +++ b/libraries/render-utils/src/ssao_makeOcclusion.slf @@ -60,9 +60,7 @@ void main(void) { fragFrameES.tangent = vec3(0.0); fragFrameES.binormal = vec3(0.0); fragFrameES.normal = fragNormalES; -#if !HBAO_USE_COS_ANGLE buildTangentBinormal(side, fragUVPos, fragPositionES, fragNormalES, deltaDepthUV, fragFrameES.tangent, fragFrameES.binormal); -#endif // Let's make noise float randomPatternRotationAngle = getAngleDithering(fragPixelPos); @@ -79,12 +77,10 @@ void main(void) { } obscuranceSum *= invNumSamples; #if HBAO_USE_COS_ANGLE - obscuranceSum *= 0.5 / PI; - obscuranceSum = 1.0 - obscuranceSum * obscuranceSum * getObscuranceScaling(); + obscuranceSum *= 0.5; + obscuranceSum = 1.0 - obscuranceSum * getObscuranceScaling(); #else obscuranceSum *= 0.5; - obscuranceSum += 1.0 - getFalloffSinAngle(); - obscuranceSum = mix(1.0, obscuranceSum, getObscuranceScaling()); #endif } else { diff --git a/scripts/developer/utilities/render/ambientOcclusionPass.qml b/scripts/developer/utilities/render/ambientOcclusionPass.qml index 2ea3a6c416..91e385edb8 100644 --- a/scripts/developer/utilities/render/ambientOcclusionPass.qml +++ b/scripts/developer/utilities/render/ambientOcclusionPass.qml @@ -59,6 +59,7 @@ Rectangle { Repeater { model: [ "horizonBased:horizonBased", + "jitterEnabled:jitterEnabled", "ditheringEnabled:ditheringEnabled", "fetchMipsEnabled:fetchMipsEnabled", "borderingEnabled:borderingEnabled" From 6420d961498bc6700c61eee0cb3f3c719044e9a4 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Tue, 2 Oct 2018 11:16:23 +0200 Subject: [PATCH 058/286] Tried to limit banding effect --- libraries/render-utils/src/AmbientOcclusionEffect.cpp | 4 +--- libraries/render-utils/src/AmbientOcclusionEffect.h | 4 +++- libraries/render-utils/src/ssao.slh | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/libraries/render-utils/src/AmbientOcclusionEffect.cpp b/libraries/render-utils/src/AmbientOcclusionEffect.cpp index 8e82cf9a9c..eec8cc18a4 100644 --- a/libraries/render-utils/src/AmbientOcclusionEffect.cpp +++ b/libraries/render-utils/src/AmbientOcclusionEffect.cpp @@ -477,7 +477,6 @@ int AmbientOcclusionEffect::getDepthResolutionLevel() const { } void AmbientOcclusionEffect::updateJitterSamples() { - const int SSAO_RANDOM_SAMPLE_COUNT = int(_randomSamples.size() / (SSAO_SPLIT_COUNT*SSAO_SPLIT_COUNT)); for (int splitId = 0; splitId < SSAO_SPLIT_COUNT*SSAO_SPLIT_COUNT; splitId++) { auto& sample = _aoFrameParametersBuffer[splitId].edit(); sample._angleInfo.x = _randomSamples[splitId + SSAO_RANDOM_SAMPLE_COUNT * _frameId]; @@ -547,9 +546,8 @@ void AmbientOcclusionEffect::run(const render::RenderContextPointer& renderConte // Update sample rotation if (_isJitterEnabled) { - const int SSAO_RANDOM_SAMPLE_COUNT = int(_randomSamples.size() / (SSAO_SPLIT_COUNT*SSAO_SPLIT_COUNT)); updateJitterSamples(); - _frameId = (_frameId + 1) % SSAO_RANDOM_SAMPLE_COUNT; + _frameId = (_frameId + 1) % (SSAO_RANDOM_SAMPLE_COUNT); } gpu::doInBatch("AmbientOcclusionEffect::run", args->_context, [=](gpu::Batch& batch) { diff --git a/libraries/render-utils/src/AmbientOcclusionEffect.h b/libraries/render-utils/src/AmbientOcclusionEffect.h index 3fb58e3437..29dc665961 100644 --- a/libraries/render-utils/src/AmbientOcclusionEffect.h +++ b/libraries/render-utils/src/AmbientOcclusionEffect.h @@ -130,6 +130,8 @@ signals: void dirty(); }; +#define SSAO_RANDOM_SAMPLE_COUNT 16 + class AmbientOcclusionEffect { public: using Inputs = render::VaryingSet3; @@ -205,7 +207,7 @@ private: static gpu::PipelinePointer _buildNormalsPipeline; AmbientOcclusionFramebufferPointer _framebuffer; - std::array _randomSamples; + std::array _randomSamples; int _frameId{ 0 }; bool _isJitterEnabled{ true }; diff --git a/libraries/render-utils/src/ssao.slh b/libraries/render-utils/src/ssao.slh index 93d0fb95e7..72da94a4dd 100644 --- a/libraries/render-utils/src/ssao.slh +++ b/libraries/render-utils/src/ssao.slh @@ -295,7 +295,7 @@ vec3 getNormalEyeAtPixel(ivec2 pixel, int level) { } int evalMipFromRadius(float radius) { - const int LOG_MAX_OFFSET = 1; + const int LOG_MAX_OFFSET = 2; const int MAX_MIP_LEVEL = 5; return clamp(findMSB(int(radius)) - LOG_MAX_OFFSET, 0, MAX_MIP_LEVEL); } @@ -433,11 +433,11 @@ float computeWeightedHorizon(float horizonLimit, float distanceSquared) { if (tapHorizonLimit > horizonLimit) { occlusion += weight * (tapHorizonLimit - horizonLimit); horizonLimit = tapHorizonLimit; - } else if (dot(deltaVec, fragFrameES.tangent) < 0.0) { + } /*else if (dot(deltaVec, fragFrameES.tangent) < 0.0) { // This is a hack to try to handle the case where the occlusion angle is // greater than 90° occlusion = mix(occlusion, (occlusion+1.0) * 0.5, weight); - } + }*/ #else if (tapHorizonLimit < horizonLimit) { tapHorizonLimit = computeWeightedHorizon(tapHorizonLimit, distanceSquared); From 1ed0bd68b9f46b2315cbe3e7399341752e51614b Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Tue, 2 Oct 2018 12:25:00 +0200 Subject: [PATCH 059/286] Small bilateral blur optim --- .../src/AmbientOcclusionEffect.cpp | 2 +- libraries/render-utils/src/ssao.slh | 31 +++++++++++++------ .../render-utils/src/ssao_bilateralBlur.slf | 27 +++++++++------- .../render-utils/src/ssao_makeOcclusion.slf | 3 +- 4 files changed, 40 insertions(+), 23 deletions(-) diff --git a/libraries/render-utils/src/AmbientOcclusionEffect.cpp b/libraries/render-utils/src/AmbientOcclusionEffect.cpp index eec8cc18a4..8c56d17b71 100644 --- a/libraries/render-utils/src/AmbientOcclusionEffect.cpp +++ b/libraries/render-utils/src/AmbientOcclusionEffect.cpp @@ -220,7 +220,7 @@ AmbientOcclusionEffectConfig::AmbientOcclusionEffectConfig() : borderingEnabled{ true }, fetchMipsEnabled{ true }, horizonBased{ true }, - jitterEnabled{ true }{ + jitterEnabled{ false }{ } AmbientOcclusionEffect::AOParameters::AOParameters() { diff --git a/libraries/render-utils/src/ssao.slh b/libraries/render-utils/src/ssao.slh index 72da94a4dd..9ae32695c5 100644 --- a/libraries/render-utils/src/ssao.slh +++ b/libraries/render-utils/src/ssao.slh @@ -32,14 +32,20 @@ vec4 packOcclusionOutput(float occlusion, float depth, vec3 eyeNormal) { #endif } -void unpackOcclusionOutput(vec4 raw, out float occlusion, out float depth, out vec3 eyeNormal) { - occlusion = raw.x; +struct UnpackedOcclusion { + vec3 normal; + float depth; + float occlusion; +}; + +void unpackOcclusionOutput(vec4 raw, out UnpackedOcclusion result) { + result.occlusion = raw.x; #if SSAO_BILATERAL_BLUR_USE_NORMAL - depth = raw.y; - eyeNormal = normalize(vec3(raw.zw, 1.0)); + result.depth = raw.y; + result.normal = normalize(vec3(raw.zw, 1.0)); #else - depth = (raw.y + raw.z / 256.0); - eyeNormal = vec3(0,0,1); + result.depth = (raw.y + raw.z / 256.0); + result.normal = vec3(0,0,1); #endif } @@ -391,6 +397,7 @@ float evalVisibilitySSAO(in vec3 centerPosition, in vec3 centerNormal, in vec3 t } #define HBAO_USE_COS_ANGLE 1 +#define HBAO_USE_OVERHANG_HACK 0 float computeWeightForHorizon(float horizonLimit, float distanceSquared) { return max(0.0, 1.0 - distanceSquared / getRadius2()); @@ -433,11 +440,14 @@ float computeWeightedHorizon(float horizonLimit, float distanceSquared) { if (tapHorizonLimit > horizonLimit) { occlusion += weight * (tapHorizonLimit - horizonLimit); horizonLimit = tapHorizonLimit; - } /*else if (dot(deltaVec, fragFrameES.tangent) < 0.0) { + } +#if HBAO_USE_OVERHANG_HACK + else if (dot(deltaVec, fragFrameES.tangent) < 0.0) { // This is a hack to try to handle the case where the occlusion angle is // greater than 90° occlusion = mix(occlusion, (occlusion+1.0) * 0.5, weight); - }*/ + } +#endif #else if (tapHorizonLimit < horizonLimit) { tapHorizonLimit = computeWeightedHorizon(tapHorizonLimit, distanceSquared); @@ -509,13 +519,16 @@ float evalVisibilityHBAO(ivec4 side, vec2 fragUVPos, vec2 invSideImageSize, vec2 pixelSearchVec = abs(pixelSearchVec); int stepCount = int(ceil(max(pixelSearchVec.x, pixelSearchVec.y))); +#if HBAO_USE_OVERHANG_HACK || !HBAO_USE_COS_ANGLE fragFrameES.tangent = normalize(fragFrameES.tangent * deltaTap.x + fragFrameES.binormal * deltaTap.y); - +#endif // Forward search for h1 obscuranceH1 = computeOcclusion(side, fragUVPos, fragPositionES, fragFrameES, searchDir, diskPixelRadius, stepCount); // Backward search for h2 +#if HBAO_USE_OVERHANG_HACK || !HBAO_USE_COS_ANGLE fragFrameES.tangent = -fragFrameES.tangent; +#endif obscuranceH2 = computeOcclusion(side, fragUVPos, fragPositionES, fragFrameES, -searchDir, diskPixelRadius, stepCount); return obscuranceH1 + obscuranceH2; diff --git a/libraries/render-utils/src/ssao_bilateralBlur.slf b/libraries/render-utils/src/ssao_bilateralBlur.slf index bec6793307..73d1e794ed 100644 --- a/libraries/render-utils/src/ssao_bilateralBlur.slf +++ b/libraries/render-utils/src/ssao_bilateralBlur.slf @@ -55,29 +55,26 @@ float evalBlurCoefficient(vec3 blurScales, float radialDistance, float zDistance const float BLUR_EDGE_NORMAL_LIMIT = 0.25; vec2 evalTapWeightedValue(vec3 blurScales, ivec4 side, int r, vec2 occlusionTexCoord, float fragDepth, vec3 fragNormal) { - vec2 tapOcclusionTexCoord = getBlurOcclusionAxis() * r + occlusionTexCoord; vec2 occlusionTexCoordLimits = getBlurOcclusionUVLimit(); - if (any(lessThan(tapOcclusionTexCoord, vec2(0.0))) || any(greaterThanEqual(tapOcclusionTexCoord, occlusionTexCoordLimits)) ) { + if (any(lessThan(occlusionTexCoord, vec2(0.0))) || any(greaterThanEqual(occlusionTexCoord, occlusionTexCoordLimits)) ) { return vec2(0.0); } - vec4 tapOcclusionPacked = fetchOcclusionPacked(side, tapOcclusionTexCoord); - float tapOcclusion; - float tapDepth; - vec3 tapNormal; - unpackOcclusionOutput(tapOcclusionPacked, tapOcclusion, tapDepth, tapNormal); + vec4 tapOcclusionPacked = fetchOcclusionPacked(side, occlusionTexCoord); + UnpackedOcclusion tap; + unpackOcclusionOutput(tapOcclusionPacked, tap); // range domain (the "bilateral" weight). As depth difference increases, decrease weight. - float zDistance = tapDepth - fragDepth; + float zDistance = tap.depth - fragDepth; #if SSAO_BILATERAL_BLUR_USE_NORMAL - float normalDistance = BLUR_EDGE_NORMAL_LIMIT - min(BLUR_EDGE_NORMAL_LIMIT, dot(tapNormal, fragNormal)); + float normalDistance = BLUR_EDGE_NORMAL_LIMIT - min(BLUR_EDGE_NORMAL_LIMIT, dot(tap.normal, fragNormal)); #else float normalDistance = 0.0; #endif float weight = evalBlurCoefficient(blurScales, abs(r), zDistance, normalDistance); - return vec2(tapOcclusion * weight, weight); + return vec2(tap.occlusion * weight, weight); } vec4 getBlurredOcclusion(ivec2 destPixelCoord, vec2 occlusionTexCoord, vec2 depthTexCoord) { @@ -96,24 +93,30 @@ vec4 getBlurredOcclusion(ivec2 destPixelCoord, vec2 occlusionTexCoord, vec2 dept // Accumulate weighted contributions along the bluring axis in the [-radius, radius] range int blurRadius = getBlurRadius(); vec3 blurScales = getBlurScales(); + int r; // From now on, occlusionTexCoord is the UV pos in the side float sideTexCoord = occlusionTexCoord.x * 2.0 - getStereoSide(side) * getBlurOcclusionUVLimit().x; occlusionTexCoord.x = mix(occlusionTexCoord.x, sideTexCoord, isStereo()); + occlusionTexCoord -= getBlurOcclusionAxis() * blurRadius; + // negative side first - for (int r = -blurRadius; r <= -1; ++r) { + for (r = -blurRadius; r <= -1; r++) { weightedSums += evalTapWeightedValue(blurScales, side, r, occlusionTexCoord, fragDepthKey, fragNormal); + occlusionTexCoord += getBlurOcclusionAxis(); } // Central pixel contribution float mainWeight = 1.0; float pixelOcclusion = unpackOcclusion(fetchOcclusionPacked(side, occlusionTexCoord)); weightedSums += vec2(pixelOcclusion * mainWeight, mainWeight); + occlusionTexCoord += getBlurOcclusionAxis(); // then positive side - for (int r = 1; r <= blurRadius; ++r) { + for (r = 1; r <= blurRadius; ++r) { weightedSums += evalTapWeightedValue(blurScales, side, r, occlusionTexCoord, fragDepthKey, fragNormal); + occlusionTexCoord += getBlurOcclusionAxis(); } // Final normalization diff --git a/libraries/render-utils/src/ssao_makeOcclusion.slf b/libraries/render-utils/src/ssao_makeOcclusion.slf index f0c406f446..d456b7bb9a 100644 --- a/libraries/render-utils/src/ssao_makeOcclusion.slf +++ b/libraries/render-utils/src/ssao_makeOcclusion.slf @@ -60,8 +60,9 @@ void main(void) { fragFrameES.tangent = vec3(0.0); fragFrameES.binormal = vec3(0.0); fragFrameES.normal = fragNormalES; +#if HBAO_USE_OVERHANG_HACK || !HBAO_USE_COS_ANGLE buildTangentBinormal(side, fragUVPos, fragPositionES, fragNormalES, deltaDepthUV, fragFrameES.tangent, fragFrameES.binormal); - +#endif // Let's make noise float randomPatternRotationAngle = getAngleDithering(fragPixelPos); From 6fcd63ed64dffa6169be2db56fafbefe8b426993 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Tue, 2 Oct 2018 15:08:39 +0200 Subject: [PATCH 060/286] Improved acnee issues --- .../src/gpu/gl/GLTexelFormat.cpp | 9 +++- .../src/AmbientOcclusionEffect.cpp | 8 ++-- libraries/render-utils/src/ssao.slh | 45 +++++++++++++++---- .../render-utils/src/ssao_buildNormals.slf | 4 +- .../render-utils/src/ssao_makeOcclusion.slf | 12 ++--- 5 files changed, 52 insertions(+), 26 deletions(-) diff --git a/libraries/gpu-gl-common/src/gpu/gl/GLTexelFormat.cpp b/libraries/gpu-gl-common/src/gpu/gl/GLTexelFormat.cpp index 4d94f8d8e7..fef823718f 100644 --- a/libraries/gpu-gl-common/src/gpu/gl/GLTexelFormat.cpp +++ b/libraries/gpu-gl-common/src/gpu/gl/GLTexelFormat.cpp @@ -329,6 +329,8 @@ GLenum GLTexelFormat::evalGLTexelFormatInternal(const gpu::Element& dstFormat) { result = GL_RGBA8_SNORM; break; case gpu::NINT2_10_10_10: + result = GL_RGB10_A2; + break; case gpu::NUINT32: case gpu::NINT32: case gpu::COMPRESSED: @@ -729,9 +731,9 @@ GLTexelFormat GLTexelFormat::evalGLTexelFormat(const Element& dstFormat, const E texel.internalFormat = GL_DEPTH_COMPONENT24; break; } + case gpu::NINT2_10_10_10: case gpu::COMPRESSED: case gpu::NUINT2: - case gpu::NINT2_10_10_10: case gpu::NUM_TYPES: { // quiet compiler Q_UNREACHABLE(); } @@ -893,9 +895,12 @@ GLTexelFormat GLTexelFormat::evalGLTexelFormat(const Element& dstFormat, const E texel.format = GL_RGBA; texel.internalFormat = GL_RGBA2; break; + case gpu::NINT2_10_10_10: + texel.format = GL_RGBA; + texel.internalFormat = GL_RGB10_A2; + break; case gpu::NUINT32: case gpu::NINT32: - case gpu::NINT2_10_10_10: case gpu::COMPRESSED: case gpu::NUM_TYPES: // quiet compiler Q_UNREACHABLE(); diff --git a/libraries/render-utils/src/AmbientOcclusionEffect.cpp b/libraries/render-utils/src/AmbientOcclusionEffect.cpp index 8c56d17b71..9110285fc9 100644 --- a/libraries/render-utils/src/AmbientOcclusionEffect.cpp +++ b/libraries/render-utils/src/AmbientOcclusionEffect.cpp @@ -115,7 +115,7 @@ void AmbientOcclusionFramebuffer::allocate() { } auto width = sideSize.x; auto height = sideSize.y; - auto format = gpu::Element::COLOR_RGBA_32; + auto format = gpu::Element{ gpu::VEC4, gpu::NINT2_10_10_10, gpu::RGBA }; _normalTexture = gpu::Texture::createRenderBuffer(format, width, height, gpu::Texture::SINGLE_MIP, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_POINT, gpu::Sampler::WRAP_CLAMP)); _normalFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("ssaoNormals")); @@ -206,10 +206,10 @@ gpu::TexturePointer AmbientOcclusionFramebuffer::getNormalTexture() { AmbientOcclusionEffectConfig::AmbientOcclusionEffectConfig() : render::GPUJobConfig::Persistent(QStringList() << "Render" << "Engine" << "Ambient Occlusion", false), - radius{ 0.5f }, + radius{ 0.7f }, perspectiveScale{ 1.0f }, - obscuranceLevel{ 0.25f }, - falloffAngle{ 0.4f }, + obscuranceLevel{ 0.15f }, + falloffAngle{ 0.1f }, edgeSharpness{ 1.0f }, blurDeviation{ 2.5f }, numSpiralTurns{ 7.0f }, diff --git a/libraries/render-utils/src/ssao.slh b/libraries/render-utils/src/ssao.slh index 9ae32695c5..09c3cd3a66 100644 --- a/libraries/render-utils/src/ssao.slh +++ b/libraries/render-utils/src/ssao.slh @@ -287,8 +287,17 @@ float getZEyeAtUV(ivec4 side, vec2 texCoord, int level, ivec2 texelOffset) { return getZEyeAtUV(texCoord, level, texelOffset); } +vec3 packNormal(vec3 normal) { + vec3 absNormal = abs(normal); + return 0.5 + normal * 0.5 / max(absNormal.x, max(absNormal.y, absNormal.z)); +} + +vec3 unpackNormal(vec3 packedNormal) { + return normalize(packedNormal*2.0 - 1.0); +} + vec3 getNormalEyeAtUV(vec2 texCoord, int level) { - return normalize(textureLod(normalTex, texCoord, level).xyz*2.0 - vec3(1.0)); + return unpackNormal(textureLod(normalTex, texCoord, level).xyz); } vec3 getNormalEyeAtUV(ivec4 side, vec2 texCoord, int level) { @@ -297,7 +306,11 @@ vec3 getNormalEyeAtUV(ivec4 side, vec2 texCoord, int level) { } vec3 getNormalEyeAtPixel(ivec2 pixel, int level) { - return normalize(texelFetch(normalTex, pixel, level).xyz*2.0 - vec3(1.0)); + return unpackNormal(texelFetch(normalTex, pixel, level).xyz); +} + +vec2 snapToTexel(vec2 uv, vec2 pixelSize) { + return (floor(uv * pixelSize - 0.5) + 0.5) / pixelSize; } int evalMipFromRadius(float radius) { @@ -418,12 +431,12 @@ float computeWeightedHorizon(float horizonLimit, float distanceSquared) { } <@func computeHorizon()@> - if (fragUVPos.x<0 || fragUVPos.y<0 || fragUVPos.x>=1.0 || fragUVPos.y>=1.0) { + if (tapUVPos.x<0 || tapUVPos.y<0 || tapUVPos.x>=1.0 || tapUVPos.y>=1.0) { // Early exit because we've hit the borders of the frame break; } - vec2 tapMipZ = fetchTap(side, fragUVPos, radius); - vec3 tapPositionES = evalEyePositionFromZeye(side.x, tapMipZ.y, fragUVPos); + vec2 tapMipZ = fetchTap(side, tapUVPos, radius); + vec3 tapPositionES = evalEyePositionFromZeye(side.x, tapMipZ.y, tapUVPos); vec3 deltaVec = tapPositionES - fragPositionES; float distanceSquared = dot(deltaVec, deltaVec); float deltaDotNormal = dot(deltaVec, fragFrameES.normal); @@ -460,16 +473,18 @@ float computeWeightedHorizon(float horizonLimit, float distanceSquared) { #define HBAO_HORIZON_SEARCH_CONSTANT_STEP 0 float computeOcclusion(ivec4 side, vec2 fragUVPos, vec3 fragPositionES, TBNFrame fragFrameES, vec2 searchDir, float searchRadius, int stepCount) { + float occlusion = 0.0; #if HBAO_USE_COS_ANGLE float horizonLimit = getFalloffCosAngle(); - float occlusion = 0.0; #else float horizonLimit = getFalloffSinAngle(); #endif if (stepCount>0) { vec2 deltaTapUV = searchDir / float(stepCount); + vec2 tapUVPos; float deltaRadius = searchRadius / float(stepCount); + vec2 sideDepthSize = getDepthTextureSideSize(0); #if HBAO_HORIZON_SEARCH_CONSTANT_STEP float radius = 0.0; @@ -478,6 +493,7 @@ float computeOcclusion(ivec4 side, vec2 fragUVPos, vec3 fragPositionES, TBNFrame for (stepIndex=0 ; stepIndex } @@ -488,6 +504,7 @@ float computeOcclusion(ivec4 side, vec2 fragUVPos, vec3 fragPositionES, TBNFrame while (radius<=searchRadius) { fragUVPos += deltaTapUV; + tapUVPos = snapToTexel(fragUVPos, sideDepthSize); <$computeHorizon()$> @@ -495,6 +512,7 @@ float computeOcclusion(ivec4 side, vec2 fragUVPos, vec3 fragPositionES, TBNFrame mipLevel = tapMipZ.x; deltaRadius *= 2; deltaTapUV *= 2; + sideDepthSize = getDepthTextureSideSize(int(mipLevel)); } radius += deltaRadius; } @@ -511,16 +529,27 @@ float computeOcclusion(ivec4 side, vec2 fragUVPos, vec3 fragPositionES, TBNFrame } float evalVisibilityHBAO(ivec4 side, vec2 fragUVPos, vec2 invSideImageSize, vec2 deltaTap, float diskPixelRadius, - vec3 fragPositionES, TBNFrame fragFrameES) { + vec3 fragPositionES, vec3 fragNormalES) { vec2 pixelSearchVec = deltaTap * diskPixelRadius; vec2 searchDir = pixelSearchVec * invSideImageSize; + vec2 deltaTapUV = deltaTap * invSideImageSize; float obscuranceH1 = 0.0; float obscuranceH2 = 0.0; pixelSearchVec = abs(pixelSearchVec); int stepCount = int(ceil(max(pixelSearchVec.x, pixelSearchVec.y))); + TBNFrame fragFrameES; + + fragFrameES.tangent = vec3(0.0); + fragFrameES.binormal = vec3(0.0); + fragFrameES.normal = fragNormalES; #if HBAO_USE_OVERHANG_HACK || !HBAO_USE_COS_ANGLE - fragFrameES.tangent = normalize(fragFrameES.tangent * deltaTap.x + fragFrameES.binormal * deltaTap.y); + vec3 positionPos = buildPosition(side, fragUVPos + deltaTapUV); + vec3 positionNeg = buildPosition(side, fragUVPos - deltaTapUV); + + fragFrameES.tangent = getMinDelta(fragPositionES, positionPos, positionNeg); + fragFrameES.tangent -= dot(fragNormalES, fragFrameES.tangent) * fragNormalES; + fragFrameES.tangent = normalize(fragFrameES.tangent); #endif // Forward search for h1 obscuranceH1 = computeOcclusion(side, fragUVPos, fragPositionES, fragFrameES, searchDir, diskPixelRadius, stepCount); diff --git a/libraries/render-utils/src/ssao_buildNormals.slf b/libraries/render-utils/src/ssao_buildNormals.slf index 9c183f640c..0dea63ae14 100644 --- a/libraries/render-utils/src/ssao_buildNormals.slf +++ b/libraries/render-utils/src/ssao_buildNormals.slf @@ -37,8 +37,6 @@ void main(void) { // The position and normal of the pixel fragment in Eye space vec3 fragPositionES = buildPosition(side, fragUVPos); vec3 fragNormalES = buildNormal(side, fragUVPos, fragPositionES, deltaDepthUV); - vec3 absFragNormalES = abs(fragNormalES); - fragNormalES /= max(absFragNormalES.z, max(absFragNormalES.x, absFragNormalES.y)); - outFragColor = vec4(vec3(fragNormalES)*0.5 + vec3(0.5), 1.0); + outFragColor = vec4(packNormal(fragNormalES), 1.0); } diff --git a/libraries/render-utils/src/ssao_makeOcclusion.slf b/libraries/render-utils/src/ssao_makeOcclusion.slf index d456b7bb9a..aabc28a326 100644 --- a/libraries/render-utils/src/ssao_makeOcclusion.slf +++ b/libraries/render-utils/src/ssao_makeOcclusion.slf @@ -42,6 +42,8 @@ void main(void) { // From now on, fragUVPos is the UV pos in the side fragUVPos.x = mix(fragUVPos.x, fragUVPos.x * 2.0 - getStereoSide(side), isStereo()); + fragUVPos = snapToTexel(fragUVPos, sideDepthSize); + // The position and normal of the pixel fragment in Eye space vec2 deltaDepthUV = vec2(2.0) / sideDepthSize; vec3 fragPositionES = buildPosition(side, fragUVPos); @@ -55,14 +57,6 @@ void main(void) { diskPixelRadius = min(diskPixelRadius, SSAO_HBAO_MAX_RADIUS); } - TBNFrame fragFrameES; - - fragFrameES.tangent = vec3(0.0); - fragFrameES.binormal = vec3(0.0); - fragFrameES.normal = fragNormalES; -#if HBAO_USE_OVERHANG_HACK || !HBAO_USE_COS_ANGLE - buildTangentBinormal(side, fragUVPos, fragPositionES, fragNormalES, deltaDepthUV, fragFrameES.tangent, fragFrameES.binormal); -#endif // Let's make noise float randomPatternRotationAngle = getAngleDithering(fragPixelPos); @@ -74,7 +68,7 @@ void main(void) { if (isHorizonBased()) { for (int i = 0; i < numSamples; ++i) { vec3 deltaTap = getUnitTapLocation(i, 1.0, randomPatternRotationAngle, PI); - obscuranceSum += evalVisibilityHBAO(side, fragUVPos, deltaDepthUV, deltaTap.xy, diskPixelRadius, fragPositionES, fragFrameES); + obscuranceSum += evalVisibilityHBAO(side, fragUVPos, deltaDepthUV, deltaTap.xy, diskPixelRadius, fragPositionES, fragNormalES); } obscuranceSum *= invNumSamples; #if HBAO_USE_COS_ANGLE From dfe9deb1544a5908bd4133148d34bcabd7b669af Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Tue, 2 Oct 2018 17:47:33 +0200 Subject: [PATCH 061/286] Two group of config settings for SSAO and HBAO --- .../src/AmbientOcclusionEffect.cpp | 343 ++++++++++++------ .../render-utils/src/AmbientOcclusionEffect.h | 71 ++-- .../render-utils/src/SurfaceGeometryPass.cpp | 2 +- libraries/render-utils/src/ssao.slh | 20 +- .../render-utils/src/ssao_makeOcclusion.slf | 19 +- .../utilities/render/ambientOcclusionPass.qml | 81 ++++- 6 files changed, 384 insertions(+), 152 deletions(-) diff --git a/libraries/render-utils/src/AmbientOcclusionEffect.cpp b/libraries/render-utils/src/AmbientOcclusionEffect.cpp index 9110285fc9..9df3c86d41 100644 --- a/libraries/render-utils/src/AmbientOcclusionEffect.cpp +++ b/libraries/render-utils/src/AmbientOcclusionEffect.cpp @@ -206,23 +206,87 @@ gpu::TexturePointer AmbientOcclusionFramebuffer::getNormalTexture() { AmbientOcclusionEffectConfig::AmbientOcclusionEffectConfig() : render::GPUJobConfig::Persistent(QStringList() << "Render" << "Engine" << "Ambient Occlusion", false), - radius{ 0.7f }, perspectiveScale{ 1.0f }, - obscuranceLevel{ 0.15f }, - falloffAngle{ 0.1f }, edgeSharpness{ 1.0f }, - blurDeviation{ 2.5f }, - numSpiralTurns{ 7.0f }, - numSamples{ 1 }, resolutionLevel{ 2 }, blurRadius{ 4 }, + + ssaoRadius{ 1.0f }, + ssaoObscuranceLevel{ 0.4f }, + ssaoFalloffAngle{ 0.1f }, + ssaoNumSamples{ 32 }, + ssaoNumSpiralTurns{ 7.0f }, + + hbaoRadius{ 0.7f }, + hbaoObscuranceLevel{ 0.75f }, + hbaoFalloffAngle{ 0.3f }, + hbaoNumSamples{ 1 }, + ditheringEnabled{ true }, borderingEnabled{ true }, fetchMipsEnabled{ true }, - horizonBased{ true }, + horizonBased{ false }, jitterEnabled{ false }{ } +void AmbientOcclusionEffectConfig::setSSAORadius(float newRadius) { + ssaoRadius = std::max(0.01f, newRadius); emit dirty(); +} + +void AmbientOcclusionEffectConfig::setSSAOObscuranceLevel(float level) { + ssaoObscuranceLevel = std::max(0.01f, level); + emit dirty(); +} + +void AmbientOcclusionEffectConfig::setSSAOFalloffAngle(float bias) { + ssaoFalloffAngle = std::max(0.0f, std::min(bias, 1.0f)); + emit dirty(); +} + +void AmbientOcclusionEffectConfig::setSSAONumSpiralTurns(float turns) { + ssaoNumSpiralTurns = std::max(0.0f, (float)turns); + emit dirty(); +} + +void AmbientOcclusionEffectConfig::setSSAONumSamples(int samples) { + ssaoNumSamples = std::max(1.0f, (float)samples); + emit dirty(); +} + +void AmbientOcclusionEffectConfig::setHBAORadius(float newRadius) { + hbaoRadius = std::max(0.01f, newRadius); emit dirty(); +} + +void AmbientOcclusionEffectConfig::setHBAOObscuranceLevel(float level) { + hbaoObscuranceLevel = std::max(0.01f, level); + emit dirty(); +} + +void AmbientOcclusionEffectConfig::setHBAOFalloffAngle(float bias) { + hbaoFalloffAngle = std::max(0.0f, std::min(bias, 1.0f)); + emit dirty(); +} + +void AmbientOcclusionEffectConfig::setHBAONumSamples(int samples) { + hbaoNumSamples = std::max(1.0f, (float)samples); + emit dirty(); +} + +void AmbientOcclusionEffectConfig::setEdgeSharpness(float sharpness) { + edgeSharpness = std::max(0.0f, (float)sharpness); + emit dirty(); +} + +void AmbientOcclusionEffectConfig::setResolutionLevel(int level) { + resolutionLevel = std::max(0, std::min(level, MAX_RESOLUTION_LEVEL)); + emit dirty(); +} + +void AmbientOcclusionEffectConfig::setBlurRadius(int radius) { + blurRadius = std::max(0, std::min(MAX_BLUR_RADIUS, radius)); + emit dirty(); +} + AmbientOcclusionEffect::AOParameters::AOParameters() { _resolutionInfo = glm::vec4{ 0.0f }; _radiusInfo = glm::vec4{ 0.0f }; @@ -242,42 +306,17 @@ void AmbientOcclusionEffect::configure(const Config& config) { DependencyManager::get()->setAmbientOcclusionEnabled(config.enabled); bool shouldUpdateBlurs = false; + bool shouldUpdateTechnique = false; _isJitterEnabled = config.jitterEnabled; - const double RADIUS_POWER = 6.0; - const auto& radius = config.radius; - if (radius != _aoParametersBuffer->getRadius() || config.horizonBased != _aoParametersBuffer->isHorizonBased()) { - auto& current = _aoParametersBuffer.edit()._radiusInfo; - current.x = radius; - current.y = radius * radius; - current.z = 10.0f; - if (!config.horizonBased) { - current.z *= (float)(1.0 / pow((double)radius, RADIUS_POWER)); - } - } - - if (config.horizonBased != _aoParametersBuffer->isHorizonBased()) { - auto& current = _aoParametersBuffer.edit()._resolutionInfo; - current.y = config.horizonBased & 1; - } - - if (config.obscuranceLevel != _aoParametersBuffer->getObscuranceLevel()) { - auto& current = _aoParametersBuffer.edit()._radiusInfo; - current.w = config.obscuranceLevel; - } - - if (config.falloffAngle != _aoParametersBuffer->getFalloffCosAngle()) { - auto& current = _aoParametersBuffer.edit()._falloffInfo; - current.x = config.falloffAngle; - current.y = 1.0f / (1.0f - current.x); - // Compute sin from cos - current.z = sqrtf(1.0f - config.falloffAngle * config.falloffAngle); - current.w = 1.0f / current.z; + if (!_framebuffer) { + _framebuffer = std::make_shared(); + shouldUpdateBlurs = true; } // Update bilateral blur - { + if (config.blurRadius != _hblurParametersBuffer->getBlurRadius() || _blurEdgeSharpness != config.edgeSharpness) { const float BLUR_EDGE_DISTANCE_SCALE = float(10000 * SSAO_DEPTH_KEY_SCALE); const float BLUR_EDGE_NORMAL_SCALE = 2.0f; @@ -287,6 +326,8 @@ void AmbientOcclusionEffect::configure(const Config& config) { float blurRadialScale = 1.0f / (2.0f*blurRadialSigma*blurRadialSigma); glm::vec3 blurScales = -glm::vec3(blurRadialScale, glm::vec2(BLUR_EDGE_DISTANCE_SCALE, BLUR_EDGE_NORMAL_SCALE) * config.edgeSharpness); + _blurEdgeSharpness = config.edgeSharpness; + hblur.x = blurScales.x; hblur.y = blurScales.y; hblur.z = blurScales.z; @@ -298,33 +339,10 @@ void AmbientOcclusionEffect::configure(const Config& config) { vblur.w = (float)config.blurRadius; } - if (config.numSpiralTurns != _aoParametersBuffer->getNumSpiralTurns()) { - auto& current = _aoParametersBuffer.edit()._sampleInfo; - current.z = config.numSpiralTurns; - } - - if (config.numSamples != _aoParametersBuffer->getNumSamples()) { - auto& current = _aoParametersBuffer.edit()._sampleInfo; - current.x = config.numSamples; - current.y = 1.0f / config.numSamples; - - // Regenerate offsets - const int B = 3; - const float invB = 1.0f / (float)B; - - for (int i = 0; i < _randomSamples.size(); i++) { - int index = i+1; // Indices start at 1, not 0 - float f = 1.0f; - float r = 0.0f; - - while (index > 0) { - f = f * invB; - r = r + f * (float)(index % B); - index = index / B; - } - _randomSamples[i] = r * 2.0f * M_PI / config.numSamples; - } - updateJitterSamples(); + if (_aoParametersBuffer->isHorizonBased() != config.horizonBased) { + auto& current = _aoParametersBuffer.edit()._resolutionInfo; + current.y = config.horizonBased & 1; + shouldUpdateTechnique = true; } if (config.fetchMipsEnabled != _aoParametersBuffer->isFetchMipsEnabled()) { @@ -332,11 +350,6 @@ void AmbientOcclusionEffect::configure(const Config& config) { current.w = (float)config.fetchMipsEnabled; } - if (!_framebuffer) { - _framebuffer = std::make_shared(); - shouldUpdateBlurs = true; - } - if (config.perspectiveScale != _aoParametersBuffer->getPerspectiveScale()) { _aoParametersBuffer.edit()._resolutionInfo.z = config.perspectiveScale; } @@ -357,11 +370,102 @@ void AmbientOcclusionEffect::configure(const Config& config) { current.w = (float)config.borderingEnabled; } + if (config.horizonBased) { + // Configure for HBAO + const auto& radius = config.hbaoRadius; + if (shouldUpdateTechnique || radius != _aoParametersBuffer->getRadius()) { + auto& current = _aoParametersBuffer.edit()._radiusInfo; + current.x = radius; + current.y = radius * radius; + current.z = 1.0f / current.y; + } + + if (shouldUpdateTechnique || config.hbaoObscuranceLevel != _aoParametersBuffer->getObscuranceLevel()) { + auto& current = _aoParametersBuffer.edit()._radiusInfo; + current.w = config.hbaoObscuranceLevel; + } + + if (shouldUpdateTechnique || config.hbaoFalloffAngle != _aoParametersBuffer->getFalloffAngle()) { + auto& current = _aoParametersBuffer.edit()._falloffInfo; + current.x = config.hbaoFalloffAngle; + current.y = 1.0f / (1.0f - current.x); + // Compute sin from cos + current.z = sqrtf(1.0f - config.hbaoFalloffAngle * config.hbaoFalloffAngle); + current.w = 1.0f / current.z; + } + + if (shouldUpdateTechnique || config.hbaoNumSamples != _aoParametersBuffer->getNumSamples()) { + auto& current = _aoParametersBuffer.edit()._sampleInfo; + current.x = config.hbaoNumSamples; + current.y = 1.0f / config.hbaoNumSamples; + updateRandomSamples(); + updateJitterSamples(); + } + } else { + // Configure for SSAO + const double RADIUS_POWER = 6.0; + const auto& radius = config.ssaoRadius; + if (shouldUpdateTechnique || radius != _aoParametersBuffer->getRadius()) { + auto& current = _aoParametersBuffer.edit()._radiusInfo; + current.x = radius; + current.y = radius * radius; + current.z = (float)(10.0 / pow((double)radius, RADIUS_POWER)); + } + + if (shouldUpdateTechnique || config.ssaoObscuranceLevel != _aoParametersBuffer->getObscuranceLevel()) { + auto& current = _aoParametersBuffer.edit()._radiusInfo; + current.w = config.ssaoObscuranceLevel; + } + + if (shouldUpdateTechnique || config.ssaoFalloffAngle != _aoParametersBuffer->getFalloffAngle()) { + auto& current = _aoParametersBuffer.edit()._falloffInfo; + current.x = config.ssaoFalloffAngle; + } + + if (shouldUpdateTechnique || config.ssaoNumSpiralTurns != _aoParametersBuffer->getNumSpiralTurns()) { + auto& current = _aoParametersBuffer.edit()._sampleInfo; + current.z = config.ssaoNumSpiralTurns; + } + + if (shouldUpdateTechnique || config.ssaoNumSamples != _aoParametersBuffer->getNumSamples()) { + auto& current = _aoParametersBuffer.edit()._sampleInfo; + current.x = config.ssaoNumSamples; + current.y = 1.0f / config.ssaoNumSamples; + updateRandomSamples(); + updateJitterSamples(); + } + } + if (shouldUpdateBlurs) { updateBlurParameters(); } } +void AmbientOcclusionEffect::updateRandomSamples() { + // Regenerate offsets + if (_aoParametersBuffer->isHorizonBased()) { + const int B = 3; + const float invB = 1.0f / (float)B; + float sampleScale = 2.0f * M_PI / _aoParametersBuffer->getNumSamples(); + + for (int i = 0; i < _randomSamples.size(); i++) { + int index = i + 1; // Indices start at 1, not 0 + float f = 1.0f; + float r = 0.0f; + + while (index > 0) { + f = f * invB; + r = r + f * (float)(index % B); + index = index / B; + } + _randomSamples[i] = r * sampleScale; + } + } else { + for (int i = 0; i < _randomSamples.size(); i++) { + _randomSamples[i] = randFloat() * 2.0f * M_PI; + } + } +} void AmbientOcclusionEffect::updateBlurParameters() { const auto resolutionLevel = _aoParametersBuffer->getResolutionLevel(); auto& vblur = _vblurParametersBuffer.edit(); @@ -477,9 +581,14 @@ int AmbientOcclusionEffect::getDepthResolutionLevel() const { } void AmbientOcclusionEffect::updateJitterSamples() { - for (int splitId = 0; splitId < SSAO_SPLIT_COUNT*SSAO_SPLIT_COUNT; splitId++) { - auto& sample = _aoFrameParametersBuffer[splitId].edit(); - sample._angleInfo.x = _randomSamples[splitId + SSAO_RANDOM_SAMPLE_COUNT * _frameId]; + if (_aoParametersBuffer->isHorizonBased()) { + for (int splitId = 0; splitId < SSAO_SPLIT_COUNT*SSAO_SPLIT_COUNT; splitId++) { + auto& sample = _aoFrameParametersBuffer[splitId].edit(); + sample._angleInfo.x = _randomSamples[splitId + SSAO_RANDOM_SAMPLE_COUNT * _frameId]; + } + } else { + auto& sample = _aoFrameParametersBuffer[0].edit(); + sample._angleInfo.x = _randomSamples[_frameId]; } } @@ -594,57 +703,69 @@ void AmbientOcclusionEffect::run(const render::RenderContextPointer& renderConte batch.setPipeline(occlusionPipeline); batch.setResourceTexture(render_utils::slot::texture::SsaoDepth, occlusionDepthTexture); + if (_aoParametersBuffer->isHorizonBased()) { #if SSAO_USE_QUAD_SPLIT - batch.setResourceTexture(render_utils::slot::texture::SsaoNormal, occlusionNormalTexture); - { - const auto uvScale = glm::vec3( - (splitSize.x * SSAO_SPLIT_COUNT) / float(occlusionViewport.z), - (splitSize.y * SSAO_SPLIT_COUNT) / float(occlusionViewport.w), - 1.0f); - const auto postPixelOffset = glm::vec2(0.5f) / glm::vec2(occlusionViewport.z, occlusionViewport.w); - const auto prePixelOffset = glm::vec2(0.5f * uvScale.x, 0.5f * uvScale.y) / glm::vec2(splitSize); - Transform model; + batch.setResourceTexture(render_utils::slot::texture::SsaoNormal, occlusionNormalTexture); + { + const auto uvScale = glm::vec3( + (splitSize.x * SSAO_SPLIT_COUNT) / float(occlusionViewport.z), + (splitSize.y * SSAO_SPLIT_COUNT) / float(occlusionViewport.w), + 1.0f); + const auto postPixelOffset = glm::vec2(0.5f) / glm::vec2(occlusionViewport.z, occlusionViewport.w); + const auto prePixelOffset = glm::vec2(0.5f * uvScale.x, 0.5f * uvScale.y) / glm::vec2(splitSize); + Transform model; - batch.setViewportTransform(splitViewport); + batch.setViewportTransform(splitViewport); - model.setScale(uvScale); - for (int y = 0; y < SSAO_SPLIT_COUNT; y++) { - for (int x = 0; x < SSAO_SPLIT_COUNT; x++) { - const int splitIndex = x + y * SSAO_SPLIT_COUNT; - const auto uvTranslate = glm::vec3( - postPixelOffset.x * (2 * x + 1) - prePixelOffset.x, - postPixelOffset.y * (2 * y + 1) - prePixelOffset.y, - 0.0f + model.setScale(uvScale); + for (int y = 0; y < SSAO_SPLIT_COUNT; y++) { + for (int x = 0; x < SSAO_SPLIT_COUNT; x++) { + const int splitIndex = x + y * SSAO_SPLIT_COUNT; + const auto uvTranslate = glm::vec3( + postPixelOffset.x * (2 * x + 1) - prePixelOffset.x, + postPixelOffset.y * (2 * y + 1) - prePixelOffset.y, + 0.0f ); - model.setTranslation(uvTranslate); - batch.setModelTransform(model); - batch.setFramebuffer(_framebuffer->getOcclusionSplitFramebuffer(splitIndex)); - batch.setUniformBuffer(render_utils::slot::buffer::SsaoFrameParams, _aoFrameParametersBuffer[splitIndex]); - batch.draw(gpu::TRIANGLE_STRIP, 4); + model.setTranslation(uvTranslate); + batch.setModelTransform(model); + batch.setFramebuffer(_framebuffer->getOcclusionSplitFramebuffer(splitIndex)); + batch.setUniformBuffer(render_utils::slot::buffer::SsaoFrameParams, _aoFrameParametersBuffer[splitIndex]); + batch.draw(gpu::TRIANGLE_STRIP, 4); + } } } - } #else - batch.setViewportTransform(occlusionViewport); - model.setIdentity(); - batch.setModelTransform(model); - batch.setFramebuffer(occlusionFBO); - batch.setUniformBuffer(render_utils::slot::buffer::SsaoFrameParams, _aoFrameParametersBuffer[0]); - batch.draw(gpu::TRIANGLE_STRIP, 4); + batch.setViewportTransform(occlusionViewport); + batch.setModelTransform(Transform()); + batch.setFramebuffer(occlusionFBO); + batch.setUniformBuffer(render_utils::slot::buffer::SsaoFrameParams, _aoFrameParametersBuffer[0]); + batch.draw(gpu::TRIANGLE_STRIP, 4); #endif + } else { +#if SSAO_USE_QUAD_SPLIT + batch.setResourceTexture(render_utils::slot::texture::SsaoNormal, occlusionNormalTexture); +#endif + batch.setViewportTransform(occlusionViewport); + batch.setModelTransform(Transform()); + batch.setFramebuffer(occlusionFBO); + batch.setUniformBuffer(render_utils::slot::buffer::SsaoFrameParams, _aoFrameParametersBuffer[0]); + batch.draw(gpu::TRIANGLE_STRIP, 4); + } batch.popProfileRange(); #if SSAO_USE_QUAD_SPLIT - // Gather back the four separate renders into one interleaved one - batch.pushProfileRange("Gather"); - batch.setViewportTransform(occlusionViewport); - batch.setModelTransform(Transform()); - batch.setFramebuffer(occlusionFBO); - batch.setPipeline(gatherPipeline); - batch.setResourceTexture(render_utils::slot::texture::SsaoOcclusion, _framebuffer->getOcclusionSplitTexture()); - batch.draw(gpu::TRIANGLE_STRIP, 4); - batch.popProfileRange(); + if (_aoParametersBuffer->isHorizonBased()) { + // Gather back the four separate renders into one interleaved one + batch.pushProfileRange("Gather"); + batch.setViewportTransform(occlusionViewport); + batch.setModelTransform(Transform()); + batch.setFramebuffer(occlusionFBO); + batch.setPipeline(gatherPipeline); + batch.setResourceTexture(render_utils::slot::texture::SsaoOcclusion, _framebuffer->getOcclusionSplitTexture()); + batch.draw(gpu::TRIANGLE_STRIP, 4); + batch.popProfileRange(); + } #endif { diff --git a/libraries/render-utils/src/AmbientOcclusionEffect.h b/libraries/render-utils/src/AmbientOcclusionEffect.h index 29dc665961..a5b3ec1fdb 100644 --- a/libraries/render-utils/src/AmbientOcclusionEffect.h +++ b/libraries/render-utils/src/AmbientOcclusionEffect.h @@ -84,42 +84,61 @@ class AmbientOcclusionEffectConfig : public render::GPUJobConfig::Persistent { Q_PROPERTY(bool borderingEnabled MEMBER borderingEnabled NOTIFY dirty) Q_PROPERTY(bool fetchMipsEnabled MEMBER fetchMipsEnabled NOTIFY dirty) Q_PROPERTY(bool jitterEnabled MEMBER jitterEnabled NOTIFY dirty) - Q_PROPERTY(float radius MEMBER radius WRITE setRadius) - Q_PROPERTY(float obscuranceLevel MEMBER obscuranceLevel WRITE setObscuranceLevel) - Q_PROPERTY(float falloffAngle MEMBER falloffAngle WRITE setFalloffAngle) - Q_PROPERTY(float edgeSharpness MEMBER edgeSharpness WRITE setEdgeSharpness) - Q_PROPERTY(float blurDeviation MEMBER blurDeviation WRITE setBlurDeviation) - Q_PROPERTY(float numSpiralTurns MEMBER numSpiralTurns WRITE setNumSpiralTurns) - Q_PROPERTY(int numSamples MEMBER numSamples WRITE setNumSamples) + Q_PROPERTY(int resolutionLevel MEMBER resolutionLevel WRITE setResolutionLevel) + Q_PROPERTY(float edgeSharpness MEMBER edgeSharpness WRITE setEdgeSharpness) Q_PROPERTY(int blurRadius MEMBER blurRadius WRITE setBlurRadius) + // SSAO + Q_PROPERTY(float ssaoRadius MEMBER ssaoRadius WRITE setSSAORadius) + Q_PROPERTY(float ssaoObscuranceLevel MEMBER ssaoObscuranceLevel WRITE setSSAOObscuranceLevel) + Q_PROPERTY(float ssaoFalloffAngle MEMBER ssaoFalloffAngle WRITE setSSAOFalloffAngle) + Q_PROPERTY(float ssaoNumSpiralTurns MEMBER ssaoNumSpiralTurns WRITE setSSAONumSpiralTurns) + Q_PROPERTY(int ssaoNumSamples MEMBER ssaoNumSamples WRITE setSSAONumSamples) + + // HBAO + Q_PROPERTY(float hbaoRadius MEMBER hbaoRadius WRITE setHBAORadius) + Q_PROPERTY(float hbaoObscuranceLevel MEMBER hbaoObscuranceLevel WRITE setHBAOObscuranceLevel) + Q_PROPERTY(float hbaoFalloffAngle MEMBER hbaoFalloffAngle WRITE setHBAOFalloffAngle) + Q_PROPERTY(int hbaoNumSamples MEMBER hbaoNumSamples WRITE setHBAONumSamples) + public: AmbientOcclusionEffectConfig(); const int MAX_RESOLUTION_LEVEL = 4; const int MAX_BLUR_RADIUS = 15; - void setRadius(float newRadius) { radius = std::max(0.01f, newRadius); emit dirty(); } - void setObscuranceLevel(float level) { obscuranceLevel = std::max(0.01f, level); emit dirty(); } - void setFalloffAngle(float bias) { falloffAngle = std::max(0.0f, std::min(bias, 1.0f)); emit dirty(); } - void setEdgeSharpness(float sharpness) { edgeSharpness = std::max(0.0f, (float)sharpness); emit dirty(); } - void setBlurDeviation(float deviation) { blurDeviation = std::max(0.0f, deviation); emit dirty(); } - void setNumSpiralTurns(float turns) { numSpiralTurns = std::max(0.0f, (float)turns); emit dirty(); } - void setNumSamples(int samples) { numSamples = std::max(1.0f, (float)samples); emit dirty(); } - void setResolutionLevel(int level) { resolutionLevel = std::max(0, std::min(level, MAX_RESOLUTION_LEVEL)); emit dirty(); } - void setBlurRadius(int radius) { blurRadius = std::max(0, std::min(MAX_BLUR_RADIUS, radius)); emit dirty(); } + void setEdgeSharpness(float sharpness); + void setResolutionLevel(int level); + void setBlurRadius(int radius); + + void setSSAORadius(float newRadius); + void setSSAOObscuranceLevel(float level); + void setSSAOFalloffAngle(float bias); + void setSSAONumSpiralTurns(float turns); + void setSSAONumSamples(int samples); + + void setHBAORadius(float newRadius); + void setHBAOObscuranceLevel(float level); + void setHBAOFalloffAngle(float bias); + void setHBAONumSamples(int samples); - float radius; float perspectiveScale; - float obscuranceLevel; // intensify or dim down the obscurance effect - float falloffAngle; float edgeSharpness; - float blurDeviation; - float numSpiralTurns; // defining an angle span to distribute the samples ray directions - int numSamples; - int resolutionLevel; int blurRadius; // 0 means no blurring + int resolutionLevel; + + float ssaoRadius; + float ssaoObscuranceLevel; // intensify or dim down the obscurance effect + float ssaoFalloffAngle; + float ssaoNumSpiralTurns; // defining an angle span to distribute the samples ray directions + int ssaoNumSamples; + + float hbaoRadius; + float hbaoObscuranceLevel; // intensify or dim down the obscurance effect + float hbaoFalloffAngle; + int hbaoNumSamples; + bool horizonBased; // Use horizon based AO bool ditheringEnabled; // randomize the distribution of taps per pixel, should always be true bool borderingEnabled; // avoid evaluating information from non existing pixels out of the frame, should always be true @@ -154,7 +173,7 @@ public: float getRadius() const { return _radiusInfo.x; } float getPerspectiveScale() const { return _resolutionInfo.z; } float getObscuranceLevel() const { return _radiusInfo.w; } - float getFalloffCosAngle() const { return (float)_falloffInfo.x; } + float getFalloffAngle() const { return (float)_falloffInfo.x; } float getNumSpiralTurns() const { return _sampleInfo.z; } int getNumSamples() const { return (int)_sampleInfo.x; } @@ -176,7 +195,7 @@ private: BlurParameters(); float getEdgeSharpness() const { return (float)_blurInfo.x; } - int getBlurRadius() const { return (int)_blurInfo.y; } + int getBlurRadius() const { return (int)_blurInfo.w; } }; using BlurParametersBuffer = gpu::StructBuffer; @@ -185,6 +204,7 @@ private: void updateBlurParameters(); void updateFramebufferSizes(); + void updateRandomSamples(); void updateJitterSamples(); int getDepthResolutionLevel() const; @@ -193,6 +213,7 @@ private: FrameParametersBuffer _aoFrameParametersBuffer[SSAO_SPLIT_COUNT*SSAO_SPLIT_COUNT]; BlurParametersBuffer _vblurParametersBuffer; BlurParametersBuffer _hblurParametersBuffer; + float _blurEdgeSharpness{ 0.0f }; static const gpu::PipelinePointer& getOcclusionPipeline(); static const gpu::PipelinePointer& getBilateralBlurPipeline(); diff --git a/libraries/render-utils/src/SurfaceGeometryPass.cpp b/libraries/render-utils/src/SurfaceGeometryPass.cpp index 59d2198daa..4917cd31e1 100644 --- a/libraries/render-utils/src/SurfaceGeometryPass.cpp +++ b/libraries/render-utils/src/SurfaceGeometryPass.cpp @@ -86,7 +86,7 @@ void LinearDepthFramebuffer::allocate() { const uint16_t HALF_LINEAR_DEPTH_MAX_MIP_LEVEL = 5; // Point sampling of the depth, as well as the clamp to edge, are needed for the AmbientOcclusionEffect with HBAO const auto depthSamplerHalf = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_POINT, gpu::Sampler::WRAP_CLAMP); - _halfLinearDepthTexture = gpu::Texture::createRenderBuffer(gpu::Element(gpu::SCALAR, gpu::FLOAT, gpu::RED), _halfFrameSize.x, _halfFrameSize.y, HALF_LINEAR_DEPTH_MAX_MIP_LEVEL, + _halfLinearDepthTexture = gpu::Texture::createRenderBuffer(gpu::Element(gpu::SCALAR, gpu::HALF, gpu::RED), _halfFrameSize.x, _halfFrameSize.y, HALF_LINEAR_DEPTH_MAX_MIP_LEVEL, depthSamplerHalf); _halfNormalTexture = gpu::Texture::createRenderBuffer(gpu::Element::COLOR_RGBA_32, _halfFrameSize.x, _halfFrameSize.y, gpu::Texture::SINGLE_MIP, diff --git a/libraries/render-utils/src/ssao.slh b/libraries/render-utils/src/ssao.slh index 09c3cd3a66..2a80850eca 100644 --- a/libraries/render-utils/src/ssao.slh +++ b/libraries/render-utils/src/ssao.slh @@ -107,11 +107,14 @@ float getRadius2() { return params._radiusInfo.y; } float getInvRadius6() { + return mix(params._radiusInfo.z, 1.0, isHorizonBased()); +} +float getInvRadius2() { return params._radiusInfo.z; } float getObscuranceScaling() { - return params._radiusInfo.z * params._radiusInfo.w; + return getInvRadius6() * params._radiusInfo.w; } float isDitheringEnabled() { @@ -161,15 +164,24 @@ float getAngleDitheringWorldPos(in vec3 pixelWorldPos) { return isDitheringEnabled() * float(((3 * pixelPos.x ^ pixelPos.y + pixelPos.x * pixelPos.y) + (3 * pixelPos.y ^ pixelPos.z + pixelPos.x * pixelPos.z)) * 10); } +float getAngleDitheringSplit() { + return isDitheringEnabled() * frameParams._angleInfo.x; +} + float getAngleDithering(in ivec2 pixelPos) { #if SSAO_USE_QUAD_SPLIT - return isDitheringEnabled() * frameParams._angleInfo.x; + return getAngleDitheringSplit(); #else // Hash function used in the AlchemyAO paper - return isDitheringEnabled() * float((3 * pixelPos.x ^ pixelPos.y + pixelPos.x * pixelPos.y) * 10); + return getAngleDitheringPixelPos(pixelPos); #endif } +float getAngleDitheringPixelPos(in ivec2 pixelPos) { + // Hash function used in the AlchemyAO paper + return isDitheringEnabled() * float((3 * pixelPos.x ^ pixelPos.y + pixelPos.x * pixelPos.y) * 10); +} + float evalDiskRadius(float Zeye, vec2 sideImageSize) { // Choose the screen-space sample radius // proportional to the projected area of the sphere @@ -413,7 +425,7 @@ float evalVisibilitySSAO(in vec3 centerPosition, in vec3 centerNormal, in vec3 t #define HBAO_USE_OVERHANG_HACK 0 float computeWeightForHorizon(float horizonLimit, float distanceSquared) { - return max(0.0, 1.0 - distanceSquared / getRadius2()); + return max(0.0, 1.0 - distanceSquared * getInvRadius2()); } float computeWeightedHorizon(float horizonLimit, float distanceSquared) { diff --git a/libraries/render-utils/src/ssao_makeOcclusion.slf b/libraries/render-utils/src/ssao_makeOcclusion.slf index aabc28a326..ec747062e8 100644 --- a/libraries/render-utils/src/ssao_makeOcclusion.slf +++ b/libraries/render-utils/src/ssao_makeOcclusion.slf @@ -37,13 +37,18 @@ void main(void) { // Stereo side info based on the real viewport size of this pass vec2 sideDepthSize = getDepthTextureSideSize(0); - ivec2 sideOcclusionSize = ivec2( getOcclusionSplitSideSize() ); + ivec2 sideOcclusionSize; + if (isHorizonBased()) { + sideOcclusionSize = ivec2( getOcclusionSplitSideSize() ); + } else { + sideOcclusionSize = ivec2( getOcclusionSideSize() ); + } ivec4 side = getStereoSideInfoFromWidth(fragPixelPos.x, sideOcclusionSize.x); // From now on, fragUVPos is the UV pos in the side fragUVPos.x = mix(fragUVPos.x, fragUVPos.x * 2.0 - getStereoSide(side), isStereo()); fragUVPos = snapToTexel(fragUVPos, sideDepthSize); - + // The position and normal of the pixel fragment in Eye space vec2 deltaDepthUV = vec2(2.0) / sideDepthSize; vec3 fragPositionES = buildPosition(side, fragUVPos); @@ -58,7 +63,7 @@ void main(void) { } // Let's make noise - float randomPatternRotationAngle = getAngleDithering(fragPixelPos); + float randomPatternRotationAngle = 0.0; // Accumulate the obscurance for each samples float obscuranceSum = 0.0; @@ -66,22 +71,24 @@ void main(void) { float invNumSamples = getInvNumSamples(); if (isHorizonBased()) { + randomPatternRotationAngle = getAngleDithering(fragPixelPos); + for (int i = 0; i < numSamples; ++i) { vec3 deltaTap = getUnitTapLocation(i, 1.0, randomPatternRotationAngle, PI); obscuranceSum += evalVisibilityHBAO(side, fragUVPos, deltaDepthUV, deltaTap.xy, diskPixelRadius, fragPositionES, fragNormalES); } obscuranceSum *= invNumSamples; #if HBAO_USE_COS_ANGLE - obscuranceSum *= 0.5; obscuranceSum = 1.0 - obscuranceSum * getObscuranceScaling(); #else - obscuranceSum *= 0.5; obscuranceSum = mix(1.0, obscuranceSum, getObscuranceScaling()); #endif } else { // Steps are in the depth texture resolution vec2 depthTexFragPixelPos = fragUVPos * sideDepthSize; + randomPatternRotationAngle = getAngleDitheringPixelPos(fragPixelPos) + getAngleDitheringSplit(); + for (int i = 0; i < numSamples; ++i) { vec3 tap = getTapLocationClampedSSAO(i, randomPatternRotationAngle, diskPixelRadius, depthTexFragPixelPos, sideDepthSize); vec2 tapUV = fragUVPos + tap.xy * deltaDepthUV; @@ -90,7 +97,7 @@ void main(void) { obscuranceSum += float(tap.z > 0.0) * evalVisibilitySSAO(fragPositionES, fragNormalES, tapPositionES); } obscuranceSum *= invNumSamples; - obscuranceSum = 1.0 - obscuranceSum * obscuranceSum * getObscuranceScaling(); + obscuranceSum = 1.0 - obscuranceSum * getObscuranceScaling(); } float occlusion = clamp(obscuranceSum, 0.0, 1.0); diff --git a/scripts/developer/utilities/render/ambientOcclusionPass.qml b/scripts/developer/utilities/render/ambientOcclusionPass.qml index 91e385edb8..d6323c351a 100644 --- a/scripts/developer/utilities/render/ambientOcclusionPass.qml +++ b/scripts/developer/utilities/render/ambientOcclusionPass.qml @@ -33,11 +33,6 @@ Rectangle { Repeater { model: [ - "Radius:radius:2.0:false", - "Level:obscuranceLevel:1.0:false", - "Num Taps:numSamples:16:true", - "Taps Spiral:numSpiralTurns:10.0:false", - "Falloff Angle:falloffAngle:1.0:false", "Blur Edge Sharpness:edgeSharpness:1.0:false", "Blur Radius:blurRadius:15.0:true", "Resolution Downscale:resolutionLevel:2:true", @@ -102,5 +97,81 @@ Rectangle { } ] } + + + + TabView { + anchors.left: parent.left + anchors.right: parent.right + height: 400 + + Tab { + title: "SSAO" + + Rectangle { + color: hifi.colors.baseGray; + + Column { + spacing: 8 + anchors.left: parent.left + anchors.right: parent.right + anchors.margins: hifi.dimensions.contentMargin.x + + Repeater { + model: [ + "Radius:ssaoRadius:2.0:false", + "Level:ssaoObscuranceLevel:1.0:false", + "Num Taps:ssaoNumSamples:64:true", + "Taps Spiral:ssaoNumSpiralTurns:10.0:false", + "Falloff Angle:ssaoFalloffAngle:1.0:false", + ] + ConfigSlider { + label: qsTr(modelData.split(":")[0]) + integral: (modelData.split(":")[3] == 'true') + config: Render.getConfig("RenderMainView.AmbientOcclusion") + property: modelData.split(":")[1] + max: modelData.split(":")[2] + min: 0.0 + height:38 + } + } + } + } + } + + Tab { + title: "HBAO" + + Rectangle { + color: hifi.colors.baseGray; + + Column { + spacing: 8 + anchors.left: parent.left + anchors.right: parent.right + anchors.margins: hifi.dimensions.contentMargin.x + + Repeater { + model: [ + "Radius:hbaoRadius:2.0:false", + "Level:hbaoObscuranceLevel:1.0:false", + "Num Taps:hbaoNumSamples:6:true", + "Falloff Angle:hbaoFalloffAngle:1.0:false", + ] + ConfigSlider { + label: qsTr(modelData.split(":")[0]) + integral: (modelData.split(":")[3] == 'true') + config: Render.getConfig("RenderMainView.AmbientOcclusion") + property: modelData.split(":")[1] + max: modelData.split(":")[2] + min: 0.0 + height:38 + } + } + } + } + } + } + } } From 72d069325293549277197ae02e1303f8a557ca3e Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Tue, 2 Oct 2018 18:46:01 +0200 Subject: [PATCH 062/286] Clean up for stereo --- .../render-utils/src/ssao_makeOcclusion.slf | 77 ++++++++++--------- 1 file changed, 40 insertions(+), 37 deletions(-) diff --git a/libraries/render-utils/src/ssao_makeOcclusion.slf b/libraries/render-utils/src/ssao_makeOcclusion.slf index ec747062e8..8b44fd8301 100644 --- a/libraries/render-utils/src/ssao_makeOcclusion.slf +++ b/libraries/render-utils/src/ssao_makeOcclusion.slf @@ -56,51 +56,54 @@ void main(void) { vec3 fragNormalES = buildNormal(side, fragUVPos, fragPositionES, deltaDepthUV); #endif - // Choose the screen-space sample radius - float diskPixelRadius = evalDiskRadius(fragPositionES.z, sideDepthSize); - if (isHorizonBased()) { - diskPixelRadius = min(diskPixelRadius, SSAO_HBAO_MAX_RADIUS); - } + float occlusion = 1.0; - // Let's make noise - float randomPatternRotationAngle = 0.0; - - // Accumulate the obscurance for each samples - float obscuranceSum = 0.0; - int numSamples = int(getNumSamples()); - float invNumSamples = getInvNumSamples(); - - if (isHorizonBased()) { - randomPatternRotationAngle = getAngleDithering(fragPixelPos); - - for (int i = 0; i < numSamples; ++i) { - vec3 deltaTap = getUnitTapLocation(i, 1.0, randomPatternRotationAngle, PI); - obscuranceSum += evalVisibilityHBAO(side, fragUVPos, deltaDepthUV, deltaTap.xy, diskPixelRadius, fragPositionES, fragNormalES); + if (fragPositionES.z > (1.0-getPosLinearDepthFar())) { + // Choose the screen-space sample radius + float diskPixelRadius = evalDiskRadius(fragPositionES.z, sideDepthSize); + if (isHorizonBased()) { + diskPixelRadius = min(diskPixelRadius, SSAO_HBAO_MAX_RADIUS); } - obscuranceSum *= invNumSamples; + + // Let's make noise + float randomPatternRotationAngle = 0.0; + + // Accumulate the obscurance for each samples + float obscuranceSum = 0.0; + int numSamples = int(getNumSamples()); + float invNumSamples = getInvNumSamples(); + + if (isHorizonBased()) { + randomPatternRotationAngle = getAngleDithering(fragPixelPos); + + for (int i = 0; i < numSamples; ++i) { + vec3 deltaTap = getUnitTapLocation(i, 1.0, randomPatternRotationAngle, PI); + obscuranceSum += evalVisibilityHBAO(side, fragUVPos, deltaDepthUV, deltaTap.xy, diskPixelRadius, fragPositionES, fragNormalES); + } + obscuranceSum *= invNumSamples; #if HBAO_USE_COS_ANGLE - obscuranceSum = 1.0 - obscuranceSum * getObscuranceScaling(); + obscuranceSum = 1.0 - obscuranceSum * getObscuranceScaling(); #else - obscuranceSum = mix(1.0, obscuranceSum, getObscuranceScaling()); + obscuranceSum = mix(1.0, obscuranceSum, getObscuranceScaling()); #endif - } else { - // Steps are in the depth texture resolution - vec2 depthTexFragPixelPos = fragUVPos * sideDepthSize; + } else { + // Steps are in the depth texture resolution + vec2 depthTexFragPixelPos = fragUVPos * sideDepthSize; - randomPatternRotationAngle = getAngleDitheringPixelPos(fragPixelPos) + getAngleDitheringSplit(); + randomPatternRotationAngle = getAngleDitheringPixelPos(fragPixelPos) + getAngleDitheringSplit(); - for (int i = 0; i < numSamples; ++i) { - vec3 tap = getTapLocationClampedSSAO(i, randomPatternRotationAngle, diskPixelRadius, depthTexFragPixelPos, sideDepthSize); - vec2 tapUV = fragUVPos + tap.xy * deltaDepthUV; - vec2 tapMipZ = fetchTap(side, tapUV, tap.z); - vec3 tapPositionES = evalEyePositionFromZeye(side.x, tapMipZ.y, tapUV); - obscuranceSum += float(tap.z > 0.0) * evalVisibilitySSAO(fragPositionES, fragNormalES, tapPositionES); + for (int i = 0; i < numSamples; ++i) { + vec3 tap = getTapLocationClampedSSAO(i, randomPatternRotationAngle, diskPixelRadius, depthTexFragPixelPos, sideDepthSize); + vec2 tapUV = fragUVPos + tap.xy * deltaDepthUV; + vec2 tapMipZ = fetchTap(side, tapUV, tap.z); + vec3 tapPositionES = evalEyePositionFromZeye(side.x, tapMipZ.y, tapUV); + obscuranceSum += float(tap.z > 0.0) * evalVisibilitySSAO(fragPositionES, fragNormalES, tapPositionES); + } + obscuranceSum *= invNumSamples; + obscuranceSum = 1.0 - obscuranceSum * getObscuranceScaling(); } - obscuranceSum *= invNumSamples; - obscuranceSum = 1.0 - obscuranceSum * getObscuranceScaling(); - } - float occlusion = clamp(obscuranceSum, 0.0, 1.0); - + occlusion = clamp(obscuranceSum, 0.0, 1.0); + } outFragColor = packOcclusionOutput(occlusion, fragPositionES.z, fragNormalES); } From 9cf94c968aa7a5e02a0d44b50dbb8b068439a9c4 Mon Sep 17 00:00:00 2001 From: Alexander Ivash Date: Wed, 3 Oct 2018 02:55:33 +0300 Subject: [PATCH 063/286] FB18573 - Other avatars' avatar entities may appear to be successfully deleted until edited note: partially revert commit 888f2f64a5971b18c0dbcdac2b15d83c14deac45 / "Merge pull request #10981 from druiz17/bug/delete-avatar-entity --- libraries/entities/src/EntityScriptingInterface.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index 22eb57e051..7b9b6a8cba 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -712,11 +712,6 @@ void EntityScriptingInterface::deleteEntity(QUuid id) { const QUuid myNodeID = nodeList->getSessionUUID(); if (entity->getClientOnly() && entity->getOwningAvatarID() != myNodeID) { // don't delete other avatar's avatarEntities - // If you actually own the entity but the onwership property is not set because of a domain switch - // The lines below makes sure the entity is deleted once its properties are set. - auto avatarHashMap = DependencyManager::get(); - AvatarSharedPointer myAvatar = avatarHashMap->getAvatarBySessionID(myNodeID); - myAvatar->insertDetachedEntityID(id); shouldSendDeleteToServer = false; return; } From 8ad58754dac8a79b9fb1cd6e12795174265db03d Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Wed, 3 Oct 2018 08:41:03 +0200 Subject: [PATCH 064/286] Fixed compilation on Mac, Ubuntu and Android --- .../gpu-gles/src/gpu/gles/GLESBackend.cpp | 23 +++++++++++++++++++ libraries/gpu-gles/src/gpu/gles/GLESBackend.h | 3 +++ .../src/AmbientOcclusionEffect.cpp | 18 +++++---------- 3 files changed, 32 insertions(+), 12 deletions(-) diff --git a/libraries/gpu-gles/src/gpu/gles/GLESBackend.cpp b/libraries/gpu-gles/src/gpu/gles/GLESBackend.cpp index b277889771..cb40b4fc9b 100644 --- a/libraries/gpu-gles/src/gpu/gles/GLESBackend.cpp +++ b/libraries/gpu-gles/src/gpu/gles/GLESBackend.cpp @@ -20,6 +20,29 @@ using namespace gpu::gles; const std::string GLESBackend::GLES_VERSION { "GLES" }; +void GLESBackend::draw(GLenum mode, uint32 numVertices, uint32 startVertex) { + if (isStereo()) { +#ifdef GPU_STEREO_DRAWCALL_INSTANCED + glDrawArraysInstanced(mode, startVertex, numVertices, 2); +#else + setupStereoSide(0); + glDrawArrays(mode, startVertex, numVertices); + setupStereoSide(1); + glDrawArrays(mode, startVertex, numVertices); +#endif + _stats._DSNumTriangles += 2 * numVertices / 3; + _stats._DSNumDrawcalls += 2; + + } else { + glDrawArrays(mode, startVertex, numVertices); + _stats._DSNumTriangles += numVertices / 3; + _stats._DSNumDrawcalls++; + } + _stats._DSNumAPIDrawcalls++; + + (void)CHECK_GL_ERROR(); +} + void GLESBackend::do_draw(const Batch& batch, size_t paramOffset) { Primitive primitiveType = (Primitive)batch._params[paramOffset + 2]._uint; GLenum mode = gl::PRIMITIVE_TO_GL[primitiveType]; diff --git a/libraries/gpu-gles/src/gpu/gles/GLESBackend.h b/libraries/gpu-gles/src/gpu/gles/GLESBackend.h index 56ae41da31..7f6765c129 100644 --- a/libraries/gpu-gles/src/gpu/gles/GLESBackend.h +++ b/libraries/gpu-gles/src/gpu/gles/GLESBackend.h @@ -126,6 +126,9 @@ public: }; protected: + + void draw(GLenum mode, uint32 numVertices, uint32 startVertex) override; + GLuint getFramebufferID(const FramebufferPointer& framebuffer) override; GLFramebuffer* syncGPUObject(const Framebuffer& framebuffer) override; diff --git a/libraries/render-utils/src/AmbientOcclusionEffect.cpp b/libraries/render-utils/src/AmbientOcclusionEffect.cpp index 9df3c86d41..92654a66e6 100644 --- a/libraries/render-utils/src/AmbientOcclusionEffect.cpp +++ b/libraries/render-utils/src/AmbientOcclusionEffect.cpp @@ -208,24 +208,24 @@ AmbientOcclusionEffectConfig::AmbientOcclusionEffectConfig() : render::GPUJobConfig::Persistent(QStringList() << "Render" << "Engine" << "Ambient Occlusion", false), perspectiveScale{ 1.0f }, edgeSharpness{ 1.0f }, - resolutionLevel{ 2 }, blurRadius{ 4 }, + resolutionLevel{ 2 }, ssaoRadius{ 1.0f }, ssaoObscuranceLevel{ 0.4f }, ssaoFalloffAngle{ 0.1f }, - ssaoNumSamples{ 32 }, ssaoNumSpiralTurns{ 7.0f }, + ssaoNumSamples{ 32 }, hbaoRadius{ 0.7f }, hbaoObscuranceLevel{ 0.75f }, hbaoFalloffAngle{ 0.3f }, hbaoNumSamples{ 1 }, + horizonBased{ false }, ditheringEnabled{ true }, borderingEnabled{ true }, fetchMipsEnabled{ true }, - horizonBased{ false }, jitterEnabled{ false }{ } @@ -446,9 +446,9 @@ void AmbientOcclusionEffect::updateRandomSamples() { if (_aoParametersBuffer->isHorizonBased()) { const int B = 3; const float invB = 1.0f / (float)B; - float sampleScale = 2.0f * M_PI / _aoParametersBuffer->getNumSamples(); + float sampleScale = float(2.0f * M_PI / _aoParametersBuffer->getNumSamples()); - for (int i = 0; i < _randomSamples.size(); i++) { + for (auto i = 0; i < _randomSamples.size(); i++) { int index = i + 1; // Indices start at 1, not 0 float f = 1.0f; float r = 0.0f; @@ -461,7 +461,7 @@ void AmbientOcclusionEffect::updateRandomSamples() { _randomSamples[i] = r * sampleScale; } } else { - for (int i = 0; i < _randomSamples.size(); i++) { + for (auto i = 0; i < _randomSamples.size(); i++) { _randomSamples[i] = randFloat() * 2.0f * M_PI; } } @@ -499,7 +499,6 @@ void AmbientOcclusionEffect::updateFramebufferSizes() { const int resolutionLevel = _aoParametersBuffer.get().getResolutionLevel(); const int depthResolutionLevel = getDepthResolutionLevel(); - const auto occlusionDepthFrameSize = sourceFrameSideSize >> depthResolutionLevel; const auto occlusionFrameSize = sourceFrameSideSize >> resolutionLevel; auto normalTextureSize = _framebuffer->getNormalTexture()->getDimensions(); @@ -602,10 +601,7 @@ void AmbientOcclusionEffect::run(const render::RenderContextPointer& renderConte const auto& linearDepthFramebuffer = inputs.get2(); const int resolutionLevel = _aoParametersBuffer->getResolutionLevel(); - const auto resolutionScale = powf(0.5f, resolutionLevel); - const auto depthResolutionLevel = getDepthResolutionLevel(); - const auto depthResolutionScale = powf(0.5f, depthResolutionLevel); const auto isHorizonBased = _aoParametersBuffer->isHorizonBased(); auto fullResDepthTexture = linearDepthFramebuffer->getLinearDepthTexture(); @@ -638,7 +634,6 @@ void AmbientOcclusionEffect::run(const render::RenderContextPointer& renderConte outputs.edit0() = _framebuffer; outputs.edit1() = _aoParametersBuffer; - auto framebufferSize = _framebuffer->getSourceFrameSize(); auto occlusionPipeline = getOcclusionPipeline(); auto bilateralBlurPipeline = getBilateralBlurPipeline(); auto mipCreationPipeline = getMipCreationPipeline(); @@ -651,7 +646,6 @@ void AmbientOcclusionEffect::run(const render::RenderContextPointer& renderConte auto splitSize = glm::ivec2(_framebuffer->getOcclusionSplitTexture()->getDimensions()); auto splitViewport = glm::ivec4{ 0, 0, splitSize.x, splitSize.y }; #endif - auto occlusionDepthSize = glm::ivec2(occlusionDepthTexture->getDimensions()); // Update sample rotation if (_isJitterEnabled) { From b131de9e5104445be43e594b057c4f5f4fa35a1b Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Wed, 3 Oct 2018 08:43:51 +0200 Subject: [PATCH 065/286] Added some comments --- libraries/render-utils/src/SurfaceGeometryPass.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/libraries/render-utils/src/SurfaceGeometryPass.cpp b/libraries/render-utils/src/SurfaceGeometryPass.cpp index 4917cd31e1..83595b5a64 100644 --- a/libraries/render-utils/src/SurfaceGeometryPass.cpp +++ b/libraries/render-utils/src/SurfaceGeometryPass.cpp @@ -86,6 +86,7 @@ void LinearDepthFramebuffer::allocate() { const uint16_t HALF_LINEAR_DEPTH_MAX_MIP_LEVEL = 5; // Point sampling of the depth, as well as the clamp to edge, are needed for the AmbientOcclusionEffect with HBAO const auto depthSamplerHalf = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_POINT, gpu::Sampler::WRAP_CLAMP); + // The depth format here is half float as it increases performance in the AmbientOcclusion. But it might be needed elsewhere... _halfLinearDepthTexture = gpu::Texture::createRenderBuffer(gpu::Element(gpu::SCALAR, gpu::HALF, gpu::RED), _halfFrameSize.x, _halfFrameSize.y, HALF_LINEAR_DEPTH_MAX_MIP_LEVEL, depthSamplerHalf); From 7124474b0e931d38f341c9d79b0a74c8d2815bc1 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Wed, 3 Oct 2018 09:33:40 +0200 Subject: [PATCH 066/286] Fix ubuntu compilation --- libraries/render-utils/src/AmbientOcclusionEffect.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/libraries/render-utils/src/AmbientOcclusionEffect.cpp b/libraries/render-utils/src/AmbientOcclusionEffect.cpp index 92654a66e6..42f9673294 100644 --- a/libraries/render-utils/src/AmbientOcclusionEffect.cpp +++ b/libraries/render-utils/src/AmbientOcclusionEffect.cpp @@ -446,9 +446,9 @@ void AmbientOcclusionEffect::updateRandomSamples() { if (_aoParametersBuffer->isHorizonBased()) { const int B = 3; const float invB = 1.0f / (float)B; - float sampleScale = float(2.0f * M_PI / _aoParametersBuffer->getNumSamples()); + float sampleScale = float(2.0 * M_PI / double(_aoParametersBuffer->getNumSamples())); - for (auto i = 0; i < _randomSamples.size(); i++) { + for (size_t i = 0; i < _randomSamples.size(); i++) { int index = i + 1; // Indices start at 1, not 0 float f = 1.0f; float r = 0.0f; @@ -461,8 +461,8 @@ void AmbientOcclusionEffect::updateRandomSamples() { _randomSamples[i] = r * sampleScale; } } else { - for (auto i = 0; i < _randomSamples.size(); i++) { - _randomSamples[i] = randFloat() * 2.0f * M_PI; + for (size_t i = 0; i < _randomSamples.size(); i++) { + _randomSamples[i] = randFloat() * float(2.0 * M_PI); } } } From fe15063459f08706435797c0ae1c644023102568 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Wed, 3 Oct 2018 09:34:31 +0200 Subject: [PATCH 067/286] Another compilation fix --- libraries/render-utils/src/AmbientOcclusionEffect.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/render-utils/src/AmbientOcclusionEffect.cpp b/libraries/render-utils/src/AmbientOcclusionEffect.cpp index 42f9673294..d5c38ca47b 100644 --- a/libraries/render-utils/src/AmbientOcclusionEffect.cpp +++ b/libraries/render-utils/src/AmbientOcclusionEffect.cpp @@ -449,7 +449,7 @@ void AmbientOcclusionEffect::updateRandomSamples() { float sampleScale = float(2.0 * M_PI / double(_aoParametersBuffer->getNumSamples())); for (size_t i = 0; i < _randomSamples.size(); i++) { - int index = i + 1; // Indices start at 1, not 0 + auto index = i + 1; // Indices start at 1, not 0 float f = 1.0f; float r = 0.0f; From 1322302b6b995275f7bd4ffa36d1e0c29639cb8d Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Wed, 3 Oct 2018 14:17:24 +0200 Subject: [PATCH 068/286] Default parameter tweaking --- libraries/render-utils/src/AmbientOcclusionEffect.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/render-utils/src/AmbientOcclusionEffect.cpp b/libraries/render-utils/src/AmbientOcclusionEffect.cpp index d5c38ca47b..0e380b6c02 100644 --- a/libraries/render-utils/src/AmbientOcclusionEffect.cpp +++ b/libraries/render-utils/src/AmbientOcclusionEffect.cpp @@ -213,7 +213,7 @@ AmbientOcclusionEffectConfig::AmbientOcclusionEffectConfig() : ssaoRadius{ 1.0f }, ssaoObscuranceLevel{ 0.4f }, - ssaoFalloffAngle{ 0.1f }, + ssaoFalloffAngle{ 0.15f }, ssaoNumSpiralTurns{ 7.0f }, ssaoNumSamples{ 32 }, From c938c595c017147b9210b6a0b03d0e891cfb05bb Mon Sep 17 00:00:00 2001 From: Simon Walton Date: Wed, 10 Oct 2018 17:11:42 -0700 Subject: [PATCH 069/286] Detect failed conversion from QVariant to JSON better --- libraries/octree/src/Octree.cpp | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/libraries/octree/src/Octree.cpp b/libraries/octree/src/Octree.cpp index 3d387e0956..37dd4e410f 100644 --- a/libraries/octree/src/Octree.cpp +++ b/libraries/octree/src/Octree.cpp @@ -878,14 +878,24 @@ bool Octree::toJSONDocument(QJsonDocument* doc, const OctreeElementPointer& elem return false; } - *doc = QJsonDocument::fromVariant(entityDescription); + + bool noEntities = entityDescription["Entities"].toList().empty(); + QJsonDocument jsonDocTree = QJsonDocument::fromVariant(entityDescription); + QJsonValue entitiesJson = jsonDocTree["Entities"]; + if (entitiesJson.isNull() || (entitiesJson.toArray().empty() && !noEntities)) { + // Json version of entities too large. + return false; + } else { + *doc = jsonDocTree; + } + return true; } bool Octree::toJSON(QByteArray* data, const OctreeElementPointer& element, bool doGzip) { QJsonDocument doc; if (!toJSONDocument(&doc, element)) { - qCritical("Failed to convert Entities to QVariantMap while converting to json."); + qCritical("Failed to convert Entities to JSON document."); return false; } From 0eec0e376c8102ebc931f2cddbf9bdede0b082da Mon Sep 17 00:00:00 2001 From: Simon Walton Date: Thu, 11 Oct 2018 18:13:26 -0700 Subject: [PATCH 070/286] Convert EntityTree direct to JSON, not via QJsonDocument --- libraries/entities/src/EntityTree.cpp | 12 +++++ libraries/entities/src/EntityTree.h | 2 + .../src/RecurseOctreeToJSONOperator.cpp | 45 +++++++++++++++++ .../src/RecurseOctreeToJSONOperator.h | 31 ++++++++++++ libraries/octree/src/Octree.cpp | 50 ++++++++++++++++++- libraries/octree/src/Octree.h | 2 + 6 files changed, 141 insertions(+), 1 deletion(-) create mode 100644 libraries/entities/src/RecurseOctreeToJSONOperator.cpp create mode 100644 libraries/entities/src/RecurseOctreeToJSONOperator.h diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index a7c88ddc7d..aa2167029d 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -35,6 +35,7 @@ #include "QVariantGLM.h" #include "EntitiesLogging.h" #include "RecurseOctreeToMapOperator.h" +#include "RecurseOctreeToJsonOperator.h" #include "LogHandler.h" #include "EntityEditFilters.h" #include "EntityDynamicFactoryInterface.h" @@ -2658,6 +2659,17 @@ bool EntityTree::readFromMap(QVariantMap& map) { return success; } +bool EntityTree::writeToJSON(QString & jsonString, const OctreeElementPointer & element) { + QScriptEngine scriptEngine; + RecurseOctreeToJSONOperator theOperator(element, &scriptEngine, jsonString); + withReadLock([&] { + recurseTreeWithOperator(&theOperator); + }); + + jsonString = theOperator.getJson(); + return true; +} + void EntityTree::resetClientEditStats() { _treeResetTime = usecTimestampNow(); _maxEditDelta = 0; diff --git a/libraries/entities/src/EntityTree.h b/libraries/entities/src/EntityTree.h index 2f971b8566..b852a695d8 100644 --- a/libraries/entities/src/EntityTree.h +++ b/libraries/entities/src/EntityTree.h @@ -224,6 +224,8 @@ public: virtual bool writeToMap(QVariantMap& entityDescription, OctreeElementPointer element, bool skipDefaultValues, bool skipThoseWithBadParents) override; virtual bool readFromMap(QVariantMap& entityDescription) override; + virtual bool writeToJSON(QString& jsonString, const OctreeElementPointer& element) override; + glm::vec3 getContentsDimensions(); float getContentsLargestDimension(); diff --git a/libraries/entities/src/RecurseOctreeToJSONOperator.cpp b/libraries/entities/src/RecurseOctreeToJSONOperator.cpp new file mode 100644 index 0000000000..2db1a2b228 --- /dev/null +++ b/libraries/entities/src/RecurseOctreeToJSONOperator.cpp @@ -0,0 +1,45 @@ +// +// RecurseOctreeToJSONOperator.cpp +// libraries/entities/src +// +// Created by Simon Walton on Oct 11, 2018. +// Copyright 2018 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 "RecurseOctreeToJSONOperator.h" + +#include "EntityItemProperties.h" + +RecurseOctreeToJSONOperator::RecurseOctreeToJSONOperator(const OctreeElementPointer& top, QScriptEngine* engine, + QString jsonPrefix /* = QString() */) + : _top(top) + , _engine(engine) + , _json(jsonPrefix) +{ + _toStringMethod = _engine->evaluate("(function() { return JSON.stringify(this, null, ' ') })"); +} + +bool RecurseOctreeToJSONOperator::postRecursion(const OctreeElementPointer& element) { + EntityTreeElementPointer entityTreeElement = std::static_pointer_cast(element); + + entityTreeElement->forEachEntity([&](const EntityItemPointer& entity) { processEntity(entity); } ); + return true; +} + +void RecurseOctreeToJSONOperator::processEntity(const EntityItemPointer& entity) { + QScriptValue qScriptValues = EntityItemNonDefaultPropertiesToScriptValue(_engine, entity->getProperties()); + if (comma) { + _json += ','; + }; + comma = true; + _json += "\n "; + + // Override default toString(): + qScriptValues.setProperty("toString", _toStringMethod); + QString jsonResult = qScriptValues.toString(); + //auto exceptionString2 = _engine->uncaughtException().toString(); + _json += jsonResult; +} diff --git a/libraries/entities/src/RecurseOctreeToJSONOperator.h b/libraries/entities/src/RecurseOctreeToJSONOperator.h new file mode 100644 index 0000000000..92ce6bfdca --- /dev/null +++ b/libraries/entities/src/RecurseOctreeToJSONOperator.h @@ -0,0 +1,31 @@ +// +// RecurseOctreeToJSONOperator.h +// libraries/entities/src +// +// Created by Simon Walton on Oct 11, 2018. +// Copyright 2018 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 "EntityTree.h" + +class RecurseOctreeToJSONOperator : public RecurseOctreeOperator { +public: + RecurseOctreeToJSONOperator(const OctreeElementPointer& top, QScriptEngine* engine, QString jsonPrefix = QString()); + virtual bool preRecursion(const OctreeElementPointer& element) override { return true; }; + virtual bool postRecursion(const OctreeElementPointer& element) override; + + QString getJson() const { return _json; } + +private: + void processEntity(const EntityItemPointer& entity); + + const OctreeElementPointer& _top; + QScriptEngine* _engine; + QScriptValue _toStringMethod; + + QString _json; + bool comma { false }; +}; diff --git a/libraries/octree/src/Octree.cpp b/libraries/octree/src/Octree.cpp index 37dd4e410f..40d2449a38 100644 --- a/libraries/octree/src/Octree.cpp +++ b/libraries/octree/src/Octree.cpp @@ -51,7 +51,6 @@ #include "OctreeQueryNode.h" #include "OctreeUtils.h" - QVector PERSIST_EXTENSIONS = {"json", "json.gz"}; Octree::Octree(bool shouldReaverage) : @@ -892,13 +891,61 @@ bool Octree::toJSONDocument(QJsonDocument* doc, const OctreeElementPointer& elem return true; } +bool Octree::toJSONString(QString& jsonString, const OctreeElementPointer& element) { + OctreeElementPointer top; + if (element) { + top = element; + } else { + top = _rootElement; + } + + jsonString += QString(R"({ + "DataVersion": %1, + "Entities": [)").arg(_persistDataVersion); + + writeToJSON(jsonString, top); + + // include the "bitstream" version + PacketType expectedType = expectedDataPacketType(); + PacketVersion expectedVersion = versionForPacketType(expectedType); + + jsonString += QString(R"( + ], + "Id": "%1", + "Version": %2 +} +)").arg(_persistID.toString()).arg((int)expectedVersion); + + return true; +} + bool Octree::toJSON(QByteArray* data, const OctreeElementPointer& element, bool doGzip) { +#define HIFI_USE_DIRECT_TO_JSON +#ifdef HIFI_USE_DIRECT_TO_JSON + + QString jsonString; + toJSONString(jsonString); + + if (doGzip) { + if (!gzip(jsonString.toUtf8(), *data, -1)) { + qCritical("Unable to gzip data while saving to json."); + return false; + } + } else { + *data = jsonString.toUtf8(); + } + +#else + QJsonDocument doc; if (!toJSONDocument(&doc, element)) { qCritical("Failed to convert Entities to JSON document."); return false; } + QString jsonString; + toJSONString(jsonString); + if (doGzip) { QByteArray jsonData = doc.toJson(); @@ -909,6 +956,7 @@ bool Octree::toJSON(QByteArray* data, const OctreeElementPointer& element, bool } else { *data = doc.toJson(); } +#endif // HIFI_USE_DIRECT_TO_JSON return true; } diff --git a/libraries/octree/src/Octree.h b/libraries/octree/src/Octree.h index a2b2f227cb..678d8aa5de 100644 --- a/libraries/octree/src/Octree.h +++ b/libraries/octree/src/Octree.h @@ -202,11 +202,13 @@ public: // Octree exporters bool toJSONDocument(QJsonDocument* doc, const OctreeElementPointer& element = nullptr); + bool toJSONString(QString& jsonString, const OctreeElementPointer& element = nullptr); bool toJSON(QByteArray* data, const OctreeElementPointer& element = nullptr, bool doGzip = false); bool writeToFile(const char* filename, const OctreeElementPointer& element = nullptr, QString persistAsFileType = "json.gz"); bool writeToJSONFile(const char* filename, const OctreeElementPointer& element = nullptr, bool doGzip = false); virtual bool writeToMap(QVariantMap& entityDescription, OctreeElementPointer element, bool skipDefaultValues, bool skipThoseWithBadParents) = 0; + virtual bool writeToJSON(QString& jsonString, const OctreeElementPointer& element) = 0; // Octree importers bool readFromFile(const char* filename); From ad9f7f3a1d97d1a3fe766fcdb2b8190764d5e44b Mon Sep 17 00:00:00 2001 From: Simon Walton Date: Fri, 12 Oct 2018 11:18:38 -0700 Subject: [PATCH 071/286] Include the skipDefaults option in case we ever need it Also print errors from parsing entities file; other tweaks --- libraries/entities/src/EntityTree.cpp | 2 +- .../entities/src/RecurseOctreeToJSONOperator.cpp | 12 +++++++----- libraries/entities/src/RecurseOctreeToJSONOperator.h | 3 ++- libraries/octree/src/Octree.cpp | 2 +- libraries/octree/src/OctreeDataUtils.cpp | 6 +++++- 5 files changed, 16 insertions(+), 9 deletions(-) diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index aa2167029d..19182d8b22 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -2659,7 +2659,7 @@ bool EntityTree::readFromMap(QVariantMap& map) { return success; } -bool EntityTree::writeToJSON(QString & jsonString, const OctreeElementPointer & element) { +bool EntityTree::writeToJSON(QString& jsonString, const OctreeElementPointer& element) { QScriptEngine scriptEngine; RecurseOctreeToJSONOperator theOperator(element, &scriptEngine, jsonString); withReadLock([&] { diff --git a/libraries/entities/src/RecurseOctreeToJSONOperator.cpp b/libraries/entities/src/RecurseOctreeToJSONOperator.cpp index 2db1a2b228..d67e7176a3 100644 --- a/libraries/entities/src/RecurseOctreeToJSONOperator.cpp +++ b/libraries/entities/src/RecurseOctreeToJSONOperator.cpp @@ -10,14 +10,14 @@ // #include "RecurseOctreeToJSONOperator.h" - #include "EntityItemProperties.h" RecurseOctreeToJSONOperator::RecurseOctreeToJSONOperator(const OctreeElementPointer& top, QScriptEngine* engine, - QString jsonPrefix /* = QString() */) + QString jsonPrefix /* = QString() */, bool skipDefaults /* = true */) : _top(top) , _engine(engine) , _json(jsonPrefix) + , _skipDefaults(skipDefaults) { _toStringMethod = _engine->evaluate("(function() { return JSON.stringify(this, null, ' ') })"); } @@ -30,7 +30,10 @@ bool RecurseOctreeToJSONOperator::postRecursion(const OctreeElementPointer& elem } void RecurseOctreeToJSONOperator::processEntity(const EntityItemPointer& entity) { - QScriptValue qScriptValues = EntityItemNonDefaultPropertiesToScriptValue(_engine, entity->getProperties()); + QScriptValue qScriptValues = _skipDefaults + ? EntityItemNonDefaultPropertiesToScriptValue(_engine, entity->getProperties()) + : EntityItemPropertiesToScriptValue(_engine, entity->getProperties()); + if (comma) { _json += ','; }; @@ -39,7 +42,6 @@ void RecurseOctreeToJSONOperator::processEntity(const EntityItemPointer& entity) // Override default toString(): qScriptValues.setProperty("toString", _toStringMethod); - QString jsonResult = qScriptValues.toString(); + _json += qScriptValues.toString(); //auto exceptionString2 = _engine->uncaughtException().toString(); - _json += jsonResult; } diff --git a/libraries/entities/src/RecurseOctreeToJSONOperator.h b/libraries/entities/src/RecurseOctreeToJSONOperator.h index 92ce6bfdca..f661e08d5b 100644 --- a/libraries/entities/src/RecurseOctreeToJSONOperator.h +++ b/libraries/entities/src/RecurseOctreeToJSONOperator.h @@ -13,7 +13,7 @@ class RecurseOctreeToJSONOperator : public RecurseOctreeOperator { public: - RecurseOctreeToJSONOperator(const OctreeElementPointer& top, QScriptEngine* engine, QString jsonPrefix = QString()); + RecurseOctreeToJSONOperator(const OctreeElementPointer& top, QScriptEngine* engine, QString jsonPrefix = QString(), bool skipDefaults = true); virtual bool preRecursion(const OctreeElementPointer& element) override { return true; }; virtual bool postRecursion(const OctreeElementPointer& element) override; @@ -27,5 +27,6 @@ private: QScriptValue _toStringMethod; QString _json; + const bool _skipDefaults; bool comma { false }; }; diff --git a/libraries/octree/src/Octree.cpp b/libraries/octree/src/Octree.cpp index 40d2449a38..d583901b41 100644 --- a/libraries/octree/src/Octree.cpp +++ b/libraries/octree/src/Octree.cpp @@ -956,8 +956,8 @@ bool Octree::toJSON(QByteArray* data, const OctreeElementPointer& element, bool } else { *data = doc.toJson(); } -#endif // HIFI_USE_DIRECT_TO_JSON +#endif // HIFI_USE_DIRECT_TO_JSON return true; } diff --git a/libraries/octree/src/OctreeDataUtils.cpp b/libraries/octree/src/OctreeDataUtils.cpp index 44a56fe97a..4b1700d593 100644 --- a/libraries/octree/src/OctreeDataUtils.cpp +++ b/libraries/octree/src/OctreeDataUtils.cpp @@ -35,7 +35,11 @@ bool readOctreeFile(QString path, QJsonDocument* doc) { jsonData = data; } - *doc = QJsonDocument::fromJson(jsonData); + QJsonParseError parserError; + *doc = QJsonDocument::fromJson(jsonData, &parserError); + if (parserError.error != QJsonParseError::NoError) { + qWarning() << "Error reading JSON file" << path << "-" << parserError.errorString(); + } return !doc->isNull(); } From e86d1691ce00bc99cdfa556a15bbda51c59bfef9 Mon Sep 17 00:00:00 2001 From: Simon Walton Date: Fri, 12 Oct 2018 17:26:46 -0700 Subject: [PATCH 072/286] Cache the local Entities file to avoid reading 2 or 3 times --- libraries/octree/src/OctreeDataUtils.cpp | 42 ++++++++++---------- libraries/octree/src/OctreeDataUtils.h | 1 + libraries/octree/src/OctreePersistThread.cpp | 39 +++++++++++++----- libraries/octree/src/OctreePersistThread.h | 1 + 4 files changed, 54 insertions(+), 29 deletions(-) diff --git a/libraries/octree/src/OctreeDataUtils.cpp b/libraries/octree/src/OctreeDataUtils.cpp index 4b1700d593..a2b0d78209 100644 --- a/libraries/octree/src/OctreeDataUtils.cpp +++ b/libraries/octree/src/OctreeDataUtils.cpp @@ -18,30 +18,32 @@ #include #include -// Reads octree file and parses it into a QJsonDocument. Handles both gzipped and non-gzipped files. -// Returns true if the file was successfully opened and parsed, otherwise false. -// Example failures: file does not exist, gzipped file cannot be unzipped, invalid JSON. -bool readOctreeFile(QString path, QJsonDocument* doc) { - QFile file(path); - if (!file.open(QIODevice::ReadOnly)) { - qCritical() << "Cannot open json file for reading: " << path; - return false; - } +namespace { + // Reads octree file and parses it into a QJsonDocument. Handles both gzipped and non-gzipped files. + // Returns true if the file was successfully opened and parsed, otherwise false. + // Example failures: file does not exist, gzipped file cannot be unzipped, invalid JSON. + bool readOctreeFile(QString path, QJsonDocument* doc) { + QFile file(path); + if (!file.open(QIODevice::ReadOnly)) { + qCritical() << "Cannot open json file for reading: " << path; + return false; + } - QByteArray data = file.readAll(); - QByteArray jsonData; + QByteArray data = file.readAll(); + QByteArray jsonData; - if (!gunzip(data, jsonData)) { - jsonData = data; - } + if (!gunzip(data, jsonData)) { + jsonData = data; + } - QJsonParseError parserError; - *doc = QJsonDocument::fromJson(jsonData, &parserError); - if (parserError.error != QJsonParseError::NoError) { - qWarning() << "Error reading JSON file" << path << "-" << parserError.errorString(); + QJsonParseError parserError; + *doc = QJsonDocument::fromJson(jsonData, &parserError); + if (parserError.error != QJsonParseError::NoError) { + qWarning() << "Error reading JSON file" << path << "-" << parserError.errorString(); + } + return !doc->isNull(); } - return !doc->isNull(); -} +} // Anon namespace. bool OctreeUtils::RawOctreeData::readOctreeDataInfoFromJSON(QJsonObject root) { if (root.contains("Id") && root.contains("DataVersion") && root.contains("Version")) { diff --git a/libraries/octree/src/OctreeDataUtils.h b/libraries/octree/src/OctreeDataUtils.h index 9060e7b460..ef96415044 100644 --- a/libraries/octree/src/OctreeDataUtils.h +++ b/libraries/octree/src/OctreeDataUtils.h @@ -46,6 +46,7 @@ public: }; class RawEntityData : public RawOctreeData { +public: PacketType dataPacketType() const override; void readSubclassData(const QJsonObject& root) override; void writeSubclassData(QJsonObject& root) const override; diff --git a/libraries/octree/src/OctreePersistThread.cpp b/libraries/octree/src/OctreePersistThread.cpp index 8b1d766418..0c78a1a797 100644 --- a/libraries/octree/src/OctreePersistThread.cpp +++ b/libraries/octree/src/OctreePersistThread.cpp @@ -31,6 +31,7 @@ #include #include #include +#include #include "OctreeLogging.h" #include "OctreeUtils.h" @@ -72,14 +73,27 @@ void OctreePersistThread::start() { OctreeUtils::RawOctreeData data; qCDebug(octree) << "Reading octree data from" << _filename; - if (data.readOctreeDataInfoFromFile(_filename)) { - qCDebug(octree) << "Current octree data: ID(" << data.id << ") DataVersion(" << data.version << ")"; - packet->writePrimitive(true); - auto id = data.id.toRfc4122(); - packet->write(id); - packet->writePrimitive(data.version); + QFile file(_filename); + if (file.open(QIODevice::ReadOnly)) { + QByteArray jsonData(file.readAll()); + file.close(); + if (!gunzip(jsonData, _cachedJSONData)) { + _cachedJSONData = jsonData; + } + + if (data.readOctreeDataInfoFromData(_cachedJSONData)) { + qCDebug(octree) << "Current octree data: ID(" << data.id << ") DataVersion(" << data.version << ")"; + packet->writePrimitive(true); + auto id = data.id.toRfc4122(); + packet->write(id); + packet->writePrimitive(data.version); + } else { + _cachedJSONData.clear(); + qCWarning(octree) << "No octree data found"; + packet->writePrimitive(false); + } } else { - qCWarning(octree) << "No octree data found"; + qCWarning(octree) << "Couldn't access file" << _filename << file.errorString(); packet->writePrimitive(false); } @@ -99,6 +113,7 @@ void OctreePersistThread::handleOctreeDataFileReply(QSharedPointerreadAll(); replaceData(replacementData); hasValidOctreeData = data.readOctreeDataInfoFromFile(_filename); @@ -108,7 +123,7 @@ void OctreePersistThread::handleOctreeDataFileReply(QSharedPointerwithWriteLock([&] { PerformanceWarning warn(true, "Loading Octree File", true); - persistentFileRead = _tree->readFromFile(_filename.toLocal8Bit().constData()); + if (_cachedJSONData.isEmpty()) { + persistentFileRead = _tree->readFromFile(_filename.toLocal8Bit().constData()); + } else { + QDataStream jsonStream(_cachedJSONData); + persistentFileRead = _tree->readFromStream(-1, QDataStream(_cachedJSONData)); + } _tree->pruneTree(); }); + _cachedJSONData.clear(); quint64 loadDone = usecTimestampNow(); _loadTimeUSecs = loadDone - loadStarted; diff --git a/libraries/octree/src/OctreePersistThread.h b/libraries/octree/src/OctreePersistThread.h index 0044a8fa5a..6a38a21a84 100644 --- a/libraries/octree/src/OctreePersistThread.h +++ b/libraries/octree/src/OctreePersistThread.h @@ -78,6 +78,7 @@ private: quint64 _lastTimeDebug; QString _persistAsFileType; + QByteArray _cachedJSONData; }; #endif // hifi_OctreePersistThread_h From f5f34e8e7de96dc49766c98af48abd92a1435429 Mon Sep 17 00:00:00 2001 From: Simon Walton Date: Mon, 15 Oct 2018 18:14:40 -0700 Subject: [PATCH 073/286] Pasre Entities JSON with our own top-level parser Use QJsonDocument for each individual entity. --- libraries/octree/src/Octree.cpp | 8 + .../octree/src/OctreeEntitiesFileParser.cpp | 218 ++++++++++++++++++ .../octree/src/OctreeEntitiesFileParser.h | 38 +++ 3 files changed, 264 insertions(+) create mode 100644 libraries/octree/src/OctreeEntitiesFileParser.cpp create mode 100644 libraries/octree/src/OctreeEntitiesFileParser.h diff --git a/libraries/octree/src/Octree.cpp b/libraries/octree/src/Octree.cpp index d583901b41..30108d973e 100644 --- a/libraries/octree/src/Octree.cpp +++ b/libraries/octree/src/Octree.cpp @@ -50,6 +50,7 @@ #include "OctreeLogging.h" #include "OctreeQueryNode.h" #include "OctreeUtils.h" +#include "OctreeEntitiesFileParser.h" QVector PERSIST_EXTENSIONS = {"json", "json.gz"}; @@ -827,12 +828,19 @@ bool Octree::readJSONFromStream(uint64_t streamLength, QDataStream& inputStream, jsonBuffer += QByteArray(rawData, got); } + OctreeEntitiesFileParser octreeParser; + octreeParser.setEntitiesString(jsonBuffer); + QVariantMap asMap; + bool parseSuccess = octreeParser.parseEntities(asMap); + + /* QJsonDocument asDocument = QJsonDocument::fromJson(jsonBuffer); if (!marketplaceID.isEmpty()) { asDocument = addMarketplaceIDToDocumentEntities(asDocument, marketplaceID); } QVariant asVariant = asDocument.toVariant(); QVariantMap asMap = asVariant.toMap(); + */ bool success = readFromMap(asMap); delete[] rawData; return success; diff --git a/libraries/octree/src/OctreeEntitiesFileParser.cpp b/libraries/octree/src/OctreeEntitiesFileParser.cpp new file mode 100644 index 0000000000..14380de835 --- /dev/null +++ b/libraries/octree/src/OctreeEntitiesFileParser.cpp @@ -0,0 +1,218 @@ +// +// OctreeEntititesFileParser.cpp +// libraries/octree/src +// +// Created by Simon Walton on Oct 15, 2018. +// Copyright 2018 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 "OctreeEntitiesFileParser.h" + +using std::string; + +OctreeEntitiesFileParser::OctreeEntitiesFileParser() { +} + +std::string OctreeEntitiesFileParser::getErrorString() const { + std::ostringstream err; + if (_errorString.size() != 0) { + err << "Error: Line " << _line << ", byte position " << _position << ": " << _errorString; + }; + + return err.str(); +} + +void OctreeEntitiesFileParser::setEntitiesString(const QByteArray& entitiesContents) { + _entitiesContents = entitiesContents; + _entitiesLength = _entitiesContents.length(); + _position = 0; + _line = 1; +} + +bool OctreeEntitiesFileParser::parseEntities(QVariantMap& parsedEntities) { + if (nextToken() != '{') { + _errorString = "Text before start of object"; + return false; + } + + bool gotDataVersion = false; + bool gotEntities = false; + bool gotId = false; + bool gotVersion = false; + + while (!(gotDataVersion && gotEntities && gotId && gotVersion)) { + if (nextToken() != '"') { + _errorString = "Incorrect key string"; + return false; + } + + string key = readString(); + if (key.size() == 0) { + _errorString = "Missing object key"; + return false; + } + + if (nextToken() != ':') { + _errorString = "Ill-formed id/value entry"; + return false; + } + + if (key == "DataVersion") { + if (gotDataVersion) { + _errorString = "Duplicate DataVersion entries"; + return false; + } + + int dataVersionValue = readInteger(); + parsedEntities["DataVersion"] = dataVersionValue; + gotDataVersion = true; + } else if (key == "Entities") { + if (gotEntities) { + _errorString = "Duplicate Entities entries"; + return false; + } + + QVariantList entitiesValue; + if (!readEntitiesArray(entitiesValue)) { + return false; + } + + parsedEntities["Entities"] = entitiesValue; + gotEntities = true; + } else if (key == "Id") { + if (gotId) { + _errorString = "Duplicate Id entries"; + return false; + } + + if (nextToken() != '"') { + _errorString = "Invalid Id value"; + return false; + }; + string idString = readString(); + if (idString.size() == 0) { + _errorString = "Invalid Id string"; + return false; + } + QUuid idValue = QUuid::fromString(QLatin1String(idString.c_str()) ); + if (idValue.isNull()) { + _errorString = "Id value invalid UUID string: " + idString; + return false; + } + + parsedEntities["Id"] = idValue; + gotId = true; + } else if (key == "Version") { + if (gotVersion) { + _errorString = "Duplicate Version entries"; + return false; + } + + int versionValue = readInteger(); + parsedEntities["Version"] = versionValue; + gotVersion = true; + } else { + _errorString = "Unrecognized key name: " + key; + return false; + } + + if (gotDataVersion && gotEntities && gotId && gotVersion) { + break; + } else if (nextToken() != ',') { + _errorString = "Id/value incorrectly terminated"; + return false; + } + } + + if (nextToken() != '}' || nextToken() != -1) { + _errorString = "Ill-formed end of object"; + return false; + } + + return true; +} + +int OctreeEntitiesFileParser::nextToken() { + while (_position < _entitiesLength) { + char c = _entitiesContents[_position++]; + if (c != ' ' && c != '\t' && c != '\n' && c != '\r') { + return c; + } + if (c == '\n') { + ++_line; + } + } + + return -1; +} + +string OctreeEntitiesFileParser::readString() { + string returnString; + while (_position < _entitiesLength) { + char c = _entitiesContents[_position++]; + if (c == '"') { + break; + } else { + returnString.push_back(c); + } + } + + return returnString; +} + +int OctreeEntitiesFileParser::readInteger() { + const char* currentPosition = _entitiesContents.constData() + _position; + int i = atoi(currentPosition); + + int token; + do { + token = nextToken(); + } while (token == '-' || token == '+' || (token >= '0' && token <= '9')); + + --_position; + return i; +} + +bool OctreeEntitiesFileParser::readEntitiesArray(QVariantList& entitiesArray) { + if (nextToken() != '[') { + _errorString = "Entities entry is not an array"; + return false; + } + + while (true) { + QJsonParseError parseError; + QByteArray entitiesJson(_entitiesContents.right(_entitiesLength - _position)); + QJsonDocument entity = QJsonDocument::fromJson(entitiesJson, &parseError); + if (parseError.error != QJsonParseError::GarbageAtEnd) { + _errorString = "Ill-formed entity array"; + return false; + } + int entityLength = parseError.offset; + entitiesJson.truncate(entityLength); + _position += entityLength; + + entity = QJsonDocument::fromJson(entitiesJson, &parseError); + if (parseError.error != QJsonParseError::NoError) { + _errorString = "Entity item parse error"; + return false; + } + entitiesArray.append(entity.object()); + char c = nextToken(); + if (c == ']') { + return true; + } else if (c != ',') { + _errorString = "Entity array item incorrectly terminated"; + return false; + } + } + return true; +} diff --git a/libraries/octree/src/OctreeEntitiesFileParser.h b/libraries/octree/src/OctreeEntitiesFileParser.h new file mode 100644 index 0000000000..562a18e833 --- /dev/null +++ b/libraries/octree/src/OctreeEntitiesFileParser.h @@ -0,0 +1,38 @@ +// +// OctreeEntititesFileParser.h +// libraries/octree/src +// +// Created by Simon Walton on Oct 15, 2018. +// Copyright 2018 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_OctreeEntitiesFileParser_h +#define hifi_OctreeEntitiesFileParser_h + +#include +#include + +class OctreeEntitiesFileParser { +public: + OctreeEntitiesFileParser(); + void setEntitiesString(const QByteArray& entitiesContents); + bool parseEntities(QVariantMap& parsedEntities); + std::string getErrorString() const; + +private: + int nextToken(); + std::string readString(); + int readInteger(); + bool readEntitiesArray(QVariantList& entitiesArray); + + QByteArray _entitiesContents; + int _position { 0 }; + int _line { 1 }; + int _entitiesLength { 0 }; + std::string _errorString; +}; + +#endif // hifi_OctreeEntitiesFileParser_h From c031769c67d0c5391b7fd13cd937a8576e8d20e7 Mon Sep 17 00:00:00 2001 From: Simon Walton Date: Tue, 16 Oct 2018 18:08:07 -0700 Subject: [PATCH 074/286] Faster JSON entities parser --- libraries/octree/src/OctreeDataUtils.cpp | 20 +++++-- libraries/octree/src/OctreeDataUtils.h | 1 + .../octree/src/OctreeEntitiesFileParser.cpp | 58 +++++++++++++++---- .../octree/src/OctreeEntitiesFileParser.h | 3 + 4 files changed, 67 insertions(+), 15 deletions(-) diff --git a/libraries/octree/src/OctreeDataUtils.cpp b/libraries/octree/src/OctreeDataUtils.cpp index a2b0d78209..7bbad652f8 100644 --- a/libraries/octree/src/OctreeDataUtils.cpp +++ b/libraries/octree/src/OctreeDataUtils.cpp @@ -9,6 +9,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html #include "OctreeDataUtils.h" +#include "OctreeEntitiesFileParser.h" #include #include @@ -55,19 +56,30 @@ bool OctreeUtils::RawOctreeData::readOctreeDataInfoFromJSON(QJsonObject root) { return true; } +bool OctreeUtils::RawOctreeData::readOctreeDataInfoFromMap(QVariantMap map) { + if (map.contains("Id") && map.contains("DataVersion") && map.contains("Version")) { + id = map["Id"].toUuid(); + dataVersion = map["DataVersion"].toInt(); + version = map["Version"].toInt(); + } + return true; +} + bool OctreeUtils::RawOctreeData::readOctreeDataInfoFromData(QByteArray data) { QByteArray jsonData; if (gunzip(data, jsonData)) { data = jsonData; } - auto doc = QJsonDocument::fromJson(data); - if (doc.isNull()) { + OctreeEntitiesFileParser jsonParser; + jsonParser.setEntitiesString(data); + QVariantMap entitiesMap; + if (!jsonParser.parseEntities(entitiesMap)) { + qCritical() << "Can't parse Entities JSON: " << jsonParser.getErrorString().c_str(); return false; } - auto root = doc.object(); - return readOctreeDataInfoFromJSON(root); + return readOctreeDataInfoFromMap(entitiesMap); } // Reads octree file and parses it into a RawOctreeData object. diff --git a/libraries/octree/src/OctreeDataUtils.h b/libraries/octree/src/OctreeDataUtils.h index ef96415044..236d280bbf 100644 --- a/libraries/octree/src/OctreeDataUtils.h +++ b/libraries/octree/src/OctreeDataUtils.h @@ -43,6 +43,7 @@ public: bool readOctreeDataInfoFromData(QByteArray data); bool readOctreeDataInfoFromFile(QString path); bool readOctreeDataInfoFromJSON(QJsonObject root); + bool readOctreeDataInfoFromMap(QVariantMap map); }; class RawEntityData : public RawOctreeData { diff --git a/libraries/octree/src/OctreeEntitiesFileParser.cpp b/libraries/octree/src/OctreeEntitiesFileParser.cpp index 14380de835..a0e43d0810 100644 --- a/libraries/octree/src/OctreeEntitiesFileParser.cpp +++ b/libraries/octree/src/OctreeEntitiesFileParser.cpp @@ -189,23 +189,25 @@ bool OctreeEntitiesFileParser::readEntitiesArray(QVariantList& entitiesArray) { } while (true) { - QJsonParseError parseError; - QByteArray entitiesJson(_entitiesContents.right(_entitiesLength - _position)); - QJsonDocument entity = QJsonDocument::fromJson(entitiesJson, &parseError); - if (parseError.error != QJsonParseError::GarbageAtEnd) { - _errorString = "Ill-formed entity array"; + if (nextToken() != '{') { + _errorString = "Entity array item is not an object"; + return false; + } + int matchingBrace = findMatchingBrace(); + if (matchingBrace < 0) { + _errorString = "Unterminated entity object"; return false; } - int entityLength = parseError.offset; - entitiesJson.truncate(entityLength); - _position += entityLength; - entity = QJsonDocument::fromJson(entitiesJson, &parseError); - if (parseError.error != QJsonParseError::NoError) { - _errorString = "Entity item parse error"; + QByteArray jsonEntity = _entitiesContents.mid(_position - 1, matchingBrace - _position + 1); + QJsonDocument entity = QJsonDocument::fromJson(jsonEntity); + if (entity.isNull()) { + _errorString = "Ill-formed entity"; return false; } + entitiesArray.append(entity.object()); + _position = matchingBrace; char c = nextToken(); if (c == ']') { return true; @@ -216,3 +218,37 @@ bool OctreeEntitiesFileParser::readEntitiesArray(QVariantList& entitiesArray) { } return true; } + +int OctreeEntitiesFileParser::findMatchingBrace() const { + int index = _position; + int nestCount = 1; + while (index < _entitiesLength && nestCount != 0) { + switch (_entitiesContents[index++]) { + case '{': + ++nestCount; + break; + + case '}': + --nestCount; + break; + + case '"': + // Skip string + while (index < _entitiesLength) { + if (_entitiesContents[index] == '"') { + ++index; + break; + } else if (_entitiesContents[index] == '\\' && _entitiesContents[++index] == 'u') { + index += 4; + } + ++index; + } + break; + + default: + break; + } + } + + return nestCount == 0 ? index : -1; +} diff --git a/libraries/octree/src/OctreeEntitiesFileParser.h b/libraries/octree/src/OctreeEntitiesFileParser.h index 562a18e833..2daeb7e561 100644 --- a/libraries/octree/src/OctreeEntitiesFileParser.h +++ b/libraries/octree/src/OctreeEntitiesFileParser.h @@ -9,6 +9,8 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +// Parse the top-level of the Models object ourselves - use QJsonDocument for each Entity object. + #ifndef hifi_OctreeEntitiesFileParser_h #define hifi_OctreeEntitiesFileParser_h @@ -27,6 +29,7 @@ private: std::string readString(); int readInteger(); bool readEntitiesArray(QVariantList& entitiesArray); + int findMatchingBrace() const; QByteArray _entitiesContents; int _position { 0 }; From be279f08dc3d8819002125bb5e36eae9c121e0e8 Mon Sep 17 00:00:00 2001 From: Simon Walton Date: Wed, 17 Oct 2018 15:43:38 -0700 Subject: [PATCH 075/286] Restore the addMarketplaceIDToDocumentEntities() hack Fixes for Linux compiles; other improvements --- libraries/entities/src/EntityTree.cpp | 2 +- libraries/octree/src/Octree.cpp | 35 +++++++++---------- .../octree/src/OctreeEntitiesFileParser.cpp | 6 ++-- .../octree/src/OctreeEntitiesFileParser.h | 2 +- libraries/octree/src/OctreePersistThread.cpp | 2 +- 5 files changed, 22 insertions(+), 25 deletions(-) diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 19182d8b22..4cac2205a4 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -35,7 +35,7 @@ #include "QVariantGLM.h" #include "EntitiesLogging.h" #include "RecurseOctreeToMapOperator.h" -#include "RecurseOctreeToJsonOperator.h" +#include "RecurseOctreeToJSONOperator.h" #include "LogHandler.h" #include "EntityEditFilters.h" #include "EntityDynamicFactoryInterface.h" diff --git a/libraries/octree/src/Octree.cpp b/libraries/octree/src/Octree.cpp index 30108d973e..bbf1e44d08 100644 --- a/libraries/octree/src/Octree.cpp +++ b/libraries/octree/src/Octree.cpp @@ -785,28 +785,26 @@ bool Octree::readFromStream(uint64_t streamLength, QDataStream& inputStream, con } +namespace { // hack to get the marketplace id into the entities. We will create a way to get this from a hash of // the entity later, but this helps us move things along for now -QJsonDocument addMarketplaceIDToDocumentEntities(QJsonDocument& doc, const QString& marketplaceID) { +QVariantMap addMarketplaceIDToDocumentEntities(QVariantMap& doc, const QString& marketplaceID) { if (!marketplaceID.isEmpty()) { - QJsonDocument newDoc; - QJsonObject rootObj = doc.object(); - QJsonArray newEntitiesArray; + QVariantList newEntitiesArray; // build a new entities array - auto entitiesArray = rootObj["Entities"].toArray(); - for(auto it = entitiesArray.begin(); it != entitiesArray.end(); it++) { - auto entity = (*it).toObject(); + auto entitiesArray = doc["Entities"].toList(); + for (auto it = entitiesArray.begin(); it != entitiesArray.end(); it++) { + auto entity = (*it).toMap(); entity["marketplaceID"] = marketplaceID; newEntitiesArray.append(entity); } - rootObj["Entities"] = newEntitiesArray; - newDoc.setObject(rootObj); - return newDoc; + doc["Entities"] = newEntitiesArray; } return doc; } +} // Unnamed namepsace const int READ_JSON_BUFFER_SIZE = 2048; bool Octree::readJSONFromStream(uint64_t streamLength, QDataStream& inputStream, const QString& marketplaceID /*=""*/) { @@ -831,16 +829,15 @@ bool Octree::readJSONFromStream(uint64_t streamLength, QDataStream& inputStream, OctreeEntitiesFileParser octreeParser; octreeParser.setEntitiesString(jsonBuffer); QVariantMap asMap; - bool parseSuccess = octreeParser.parseEntities(asMap); - - /* - QJsonDocument asDocument = QJsonDocument::fromJson(jsonBuffer); - if (!marketplaceID.isEmpty()) { - asDocument = addMarketplaceIDToDocumentEntities(asDocument, marketplaceID); + if (!octreeParser.parseEntities(asMap)) { + qCritical() << "Couldn't parse Entities JSON:" << octreeParser.getErrorString().c_str(); + return false; } - QVariant asVariant = asDocument.toVariant(); - QVariantMap asMap = asVariant.toMap(); - */ + + if (!marketplaceID.isEmpty()) { + addMarketplaceIDToDocumentEntities(asMap, marketplaceID); + } + bool success = readFromMap(asMap); delete[] rawData; return success; diff --git a/libraries/octree/src/OctreeEntitiesFileParser.cpp b/libraries/octree/src/OctreeEntitiesFileParser.cpp index a0e43d0810..0b75991fd2 100644 --- a/libraries/octree/src/OctreeEntitiesFileParser.cpp +++ b/libraries/octree/src/OctreeEntitiesFileParser.cpp @@ -10,10 +10,10 @@ // #include +#include #include #include #include -#include #include "OctreeEntitiesFileParser.h" @@ -171,12 +171,12 @@ string OctreeEntitiesFileParser::readString() { int OctreeEntitiesFileParser::readInteger() { const char* currentPosition = _entitiesContents.constData() + _position; - int i = atoi(currentPosition); + int i = std::atoi(currentPosition); int token; do { token = nextToken(); - } while (token == '-' || token == '+' || (token >= '0' && token <= '9')); + } while (token == '-' || token == '+' || std::isdigit(token)); --_position; return i; diff --git a/libraries/octree/src/OctreeEntitiesFileParser.h b/libraries/octree/src/OctreeEntitiesFileParser.h index 2daeb7e561..e4e82f0e66 100644 --- a/libraries/octree/src/OctreeEntitiesFileParser.h +++ b/libraries/octree/src/OctreeEntitiesFileParser.h @@ -14,8 +14,8 @@ #ifndef hifi_OctreeEntitiesFileParser_h #define hifi_OctreeEntitiesFileParser_h -#include #include +#include class OctreeEntitiesFileParser { public: diff --git a/libraries/octree/src/OctreePersistThread.cpp b/libraries/octree/src/OctreePersistThread.cpp index 0c78a1a797..2717be593b 100644 --- a/libraries/octree/src/OctreePersistThread.cpp +++ b/libraries/octree/src/OctreePersistThread.cpp @@ -158,7 +158,7 @@ void OctreePersistThread::handleOctreeDataFileReply(QSharedPointerreadFromFile(_filename.toLocal8Bit().constData()); } else { QDataStream jsonStream(_cachedJSONData); - persistentFileRead = _tree->readFromStream(-1, QDataStream(_cachedJSONData)); + persistentFileRead = _tree->readFromStream(-1, jsonStream); } _tree->pruneTree(); }); From f7cfc61494a8ff4cdfccd2abcbd096556a28b040 Mon Sep 17 00:00:00 2001 From: humbletim Date: Mon, 22 Oct 2018 15:02:33 -0400 Subject: [PATCH 076/286] Package Model... -> Package Avatar... --- interface/src/Menu.cpp | 2 +- interface/src/Menu.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index eef14c873e..110ae3d892 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -141,7 +141,7 @@ Menu::Menu() { assetServerAction->setEnabled(nodeList->getThisNodeCanWriteAssets()); } - // Edit > Package Model as .fst... + // Edit > Package Avatar as .fst... addActionToQMenuAndActionHash(editMenu, MenuOption::PackageModel, 0, qApp, SLOT(packageModel())); diff --git a/interface/src/Menu.h b/interface/src/Menu.h index 031ee2561c..8d8c59d654 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -139,7 +139,7 @@ namespace MenuOption { const QString OpenVrThreadedSubmit = "OpenVR Threaded Submit"; const QString OutputMenu = "Display"; const QString Overlays = "Show Overlays"; - const QString PackageModel = "Package Model as .fst..."; + const QString PackageModel = "Package Avatar as .fst..."; const QString Pair = "Pair"; const QString PhysicsShowOwned = "Highlight Simulation Ownership"; const QString VerboseLogging = "Verbose Logging"; From b1d8a9a96bee5d172505f40c387150508475e845 Mon Sep 17 00:00:00 2001 From: humbletim Date: Mon, 22 Oct 2018 15:04:21 -0400 Subject: [PATCH 077/286] remove Qt "whatsthis" dialog icon --- interface/src/ModelSelector.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/ModelSelector.cpp b/interface/src/ModelSelector.cpp index 7d91359a11..d813e13f91 100644 --- a/interface/src/ModelSelector.cpp +++ b/interface/src/ModelSelector.cpp @@ -27,7 +27,7 @@ ModelSelector::ModelSelector() { setWindowTitle("Select Model"); setLayout(form); - + setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); _browseButton = new QPushButton("Browse", this); connect(_browseButton, &QPushButton::clicked, this, &ModelSelector::browse); form->addRow("Model File:", _browseButton); From 2cf633ca026e0d51e068384d3039229aedf03c7a Mon Sep 17 00:00:00 2001 From: humbletim Date: Mon, 22 Oct 2018 15:05:49 -0400 Subject: [PATCH 078/286] remove Model Type dropdown and related code paths --- interface/src/ModelPackager.cpp | 67 +++++------- interface/src/ModelPackager.h | 1 - interface/src/ModelPropertiesDialog.cpp | 140 ++++++++---------------- interface/src/ModelPropertiesDialog.h | 6 +- interface/src/ModelSelector.cpp | 20 ---- interface/src/ModelSelector.h | 2 - 6 files changed, 74 insertions(+), 162 deletions(-) diff --git a/interface/src/ModelPackager.cpp b/interface/src/ModelPackager.cpp index 3a5d92eb8c..9bc10c072c 100644 --- a/interface/src/ModelPackager.cpp +++ b/interface/src/ModelPackager.cpp @@ -68,7 +68,6 @@ bool ModelPackager::selectModel() { ModelSelector selector; if(selector.exec() == QDialog::Accepted) { _modelFile = selector.getFileInfo(); - _modelType = selector.getModelType(); return true; } return false; @@ -122,28 +121,26 @@ bool ModelPackager::loadModel() { bool ModelPackager::editProperties() { // open the dialog to configure the rest - ModelPropertiesDialog properties(_modelType, _mapping, _modelFile.path(), *_geometry); + ModelPropertiesDialog properties(_mapping, _modelFile.path(), *_geometry); if (properties.exec() == QDialog::Rejected) { return false; } _mapping = properties.getMapping(); - if (_modelType == FSTReader::BODY_ONLY_MODEL || _modelType == FSTReader::HEAD_AND_BODY_MODEL) { - // Make sure that a mapping for the root joint has been specified - QVariantHash joints = _mapping.value(JOINT_FIELD).toHash(); - if (!joints.contains("jointRoot")) { - qWarning() << QString("%1 root joint not configured for skeleton.").arg(_modelFile.fileName()); + // Make sure that a mapping for the root joint has been specified + QVariantHash joints = _mapping.value(JOINT_FIELD).toHash(); + if (!joints.contains("jointRoot")) { + qWarning() << QString("%1 root joint not configured for skeleton.").arg(_modelFile.fileName()); - QString message = "Your did not configure a root joint for your skeleton model.\n\nPackaging will be canceled."; - QMessageBox msgBox; - msgBox.setWindowTitle("Model Packager"); - msgBox.setText(message); - msgBox.setStandardButtons(QMessageBox::Ok); - msgBox.setIcon(QMessageBox::Warning); - msgBox.exec(); + QString message = "Your did not configure a root joint for your skeleton model.\n\nPackaging will be canceled."; + QMessageBox msgBox; + msgBox.setWindowTitle("Model Packager"); + msgBox.setText(message); + msgBox.setStandardButtons(QMessageBox::Ok); + msgBox.setIcon(QMessageBox::Warning); + msgBox.exec(); - return false; - } + return false; } return true; @@ -237,8 +234,6 @@ bool ModelPackager::zipModel() { void ModelPackager::populateBasicMapping(QVariantHash& mapping, QString filename, const FBXGeometry& geometry) { - bool isBodyType = _modelType == FSTReader::BODY_ONLY_MODEL || _modelType == FSTReader::HEAD_AND_BODY_MODEL; - // mixamo files - in the event that a mixamo file was edited by some other tool, it's likely the applicationName will // be rewritten, so we detect the existence of several different blendshapes which indicate we're likely a mixamo file bool likelyMixamoFile = geometry.applicationName == "mixamo.com" || @@ -279,19 +274,17 @@ void ModelPackager::populateBasicMapping(QVariantHash& mapping, QString filename joints.insert("jointNeck", geometry.jointIndices.contains("jointNeck") ? "jointNeck" : "Neck"); } - if (isBodyType) { - if (!joints.contains("jointRoot")) { - joints.insert("jointRoot", "Hips"); - } - if (!joints.contains("jointLean")) { - joints.insert("jointLean", "Spine"); - } - if (!joints.contains("jointLeftHand")) { - joints.insert("jointLeftHand", "LeftHand"); - } - if (!joints.contains("jointRightHand")) { - joints.insert("jointRightHand", "RightHand"); - } + if (!joints.contains("jointRoot")) { + joints.insert("jointRoot", "Hips"); + } + if (!joints.contains("jointLean")) { + joints.insert("jointLean", "Spine"); + } + if (!joints.contains("jointLeftHand")) { + joints.insert("jointLeftHand", "LeftHand"); + } + if (!joints.contains("jointRightHand")) { + joints.insert("jointRightHand", "RightHand"); } if (!joints.contains("jointHead")) { @@ -301,13 +294,11 @@ void ModelPackager::populateBasicMapping(QVariantHash& mapping, QString filename mapping.insert(JOINT_FIELD, joints); - if (isBodyType) { - if (!mapping.contains(FREE_JOINT_FIELD)) { - mapping.insertMulti(FREE_JOINT_FIELD, "LeftArm"); - mapping.insertMulti(FREE_JOINT_FIELD, "LeftForeArm"); - mapping.insertMulti(FREE_JOINT_FIELD, "RightArm"); - mapping.insertMulti(FREE_JOINT_FIELD, "RightForeArm"); - } + if (!mapping.contains(FREE_JOINT_FIELD)) { + mapping.insertMulti(FREE_JOINT_FIELD, "LeftArm"); + mapping.insertMulti(FREE_JOINT_FIELD, "LeftForeArm"); + mapping.insertMulti(FREE_JOINT_FIELD, "RightArm"); + mapping.insertMulti(FREE_JOINT_FIELD, "RightForeArm"); } // If there are no blendshape mappings, and we detect that this is likely a mixamo file, diff --git a/interface/src/ModelPackager.h b/interface/src/ModelPackager.h index 76295e5a85..8f9e437939 100644 --- a/interface/src/ModelPackager.h +++ b/interface/src/ModelPackager.h @@ -39,7 +39,6 @@ private: QFileInfo _modelFile; QFileInfo _fbxInfo; - FSTReader::ModelType _modelType; QString _texDir; QString _scriptDir; diff --git a/interface/src/ModelPropertiesDialog.cpp b/interface/src/ModelPropertiesDialog.cpp index 8984f89d07..066218205d 100644 --- a/interface/src/ModelPropertiesDialog.cpp +++ b/interface/src/ModelPropertiesDialog.cpp @@ -26,9 +26,8 @@ #include -ModelPropertiesDialog::ModelPropertiesDialog(FSTReader::ModelType modelType, const QVariantHash& originalMapping, +ModelPropertiesDialog::ModelPropertiesDialog(const QVariantHash& originalMapping, const QString& basePath, const FBXGeometry& geometry) : -_modelType(modelType), _originalMapping(originalMapping), _basePath(basePath), _geometry(geometry) @@ -50,36 +49,19 @@ _geometry(geometry) _scale->setMaximum(FLT_MAX); _scale->setSingleStep(0.01); - if (_modelType != FSTReader::ENTITY_MODEL) { - if (_modelType == FSTReader::ATTACHMENT_MODEL) { - QHBoxLayout* translation = new QHBoxLayout(); - form->addRow("Translation:", translation); - translation->addWidget(_translationX = createTranslationBox()); - translation->addWidget(_translationY = createTranslationBox()); - translation->addWidget(_translationZ = createTranslationBox()); - form->addRow("Pivot About Center:", _pivotAboutCenter = new QCheckBox()); - form->addRow("Pivot Joint:", _pivotJoint = createJointBox()); - connect(_pivotAboutCenter, SIGNAL(toggled(bool)), SLOT(updatePivotJoint())); - _pivotAboutCenter->setChecked(true); + form->addRow("Left Eye Joint:", _leftEyeJoint = createJointBox()); + form->addRow("Right Eye Joint:", _rightEyeJoint = createJointBox()); + form->addRow("Neck Joint:", _neckJoint = createJointBox()); + form->addRow("Root Joint:", _rootJoint = createJointBox()); + form->addRow("Lean Joint:", _leanJoint = createJointBox()); + form->addRow("Head Joint:", _headJoint = createJointBox()); + form->addRow("Left Hand Joint:", _leftHandJoint = createJointBox()); + form->addRow("Right Hand Joint:", _rightHandJoint = createJointBox()); - } else { - form->addRow("Left Eye Joint:", _leftEyeJoint = createJointBox()); - form->addRow("Right Eye Joint:", _rightEyeJoint = createJointBox()); - form->addRow("Neck Joint:", _neckJoint = createJointBox()); - } - if (_modelType == FSTReader::BODY_ONLY_MODEL || _modelType == FSTReader::HEAD_AND_BODY_MODEL) { - form->addRow("Root Joint:", _rootJoint = createJointBox()); - form->addRow("Lean Joint:", _leanJoint = createJointBox()); - form->addRow("Head Joint:", _headJoint = createJointBox()); - form->addRow("Left Hand Joint:", _leftHandJoint = createJointBox()); - form->addRow("Right Hand Joint:", _rightHandJoint = createJointBox()); - - form->addRow("Free Joints:", _freeJoints = new QVBoxLayout()); - QPushButton* newFreeJoint = new QPushButton("New Free Joint"); - _freeJoints->addWidget(newFreeJoint); - connect(newFreeJoint, SIGNAL(clicked(bool)), SLOT(createNewFreeJoint())); - } - } + form->addRow("Free Joints:", _freeJoints = new QVBoxLayout()); + QPushButton* newFreeJoint = new QPushButton("New Free Joint"); + _freeJoints->addWidget(newFreeJoint); + connect(newFreeJoint, SIGNAL(clicked(bool)), SLOT(createNewFreeJoint())); QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel | QDialogButtonBox::Reset); @@ -93,14 +75,9 @@ _geometry(geometry) reset(); } - -QString ModelPropertiesDialog::getType() const { - return FSTReader::getNameFromType(_modelType); -} - QVariantHash ModelPropertiesDialog::getMapping() const { QVariantHash mapping = _originalMapping; - mapping.insert(TYPE_FIELD, getType()); + mapping.insert(TYPE_FIELD, FSTReader::getNameFromType(FSTReader::HEAD_AND_BODY_MODEL)); mapping.insert(NAME_FIELD, _name->text()); mapping.insert(TEXDIR_FIELD, _textureDirectory->text()); mapping.insert(SCRIPT_FIELD, _scriptDirectory->text()); @@ -113,42 +90,24 @@ QVariantHash ModelPropertiesDialog::getMapping() const { } mapping.insert(JOINT_INDEX_FIELD, jointIndices); - if (_modelType != FSTReader::ENTITY_MODEL) { - QVariantHash joints = mapping.value(JOINT_FIELD).toHash(); - if (_modelType == FSTReader::ATTACHMENT_MODEL) { - glm::vec3 pivot; - if (_pivotAboutCenter->isChecked()) { - pivot = (_geometry.meshExtents.minimum + _geometry.meshExtents.maximum) * 0.5f; - - } else if (_pivotJoint->currentIndex() != 0) { - pivot = extractTranslation(_geometry.joints.at(_pivotJoint->currentIndex() - 1).transform); - } - mapping.insert(TRANSLATION_X_FIELD, -pivot.x * (float)_scale->value() + (float)_translationX->value()); - mapping.insert(TRANSLATION_Y_FIELD, -pivot.y * (float)_scale->value() + (float)_translationY->value()); - mapping.insert(TRANSLATION_Z_FIELD, -pivot.z * (float)_scale->value() + (float)_translationZ->value()); - - } else { - insertJointMapping(joints, "jointEyeLeft", _leftEyeJoint->currentText()); - insertJointMapping(joints, "jointEyeRight", _rightEyeJoint->currentText()); - insertJointMapping(joints, "jointNeck", _neckJoint->currentText()); - } + QVariantHash joints = mapping.value(JOINT_FIELD).toHash(); + insertJointMapping(joints, "jointEyeLeft", _leftEyeJoint->currentText()); + insertJointMapping(joints, "jointEyeRight", _rightEyeJoint->currentText()); + insertJointMapping(joints, "jointNeck", _neckJoint->currentText()); - if (_modelType == FSTReader::BODY_ONLY_MODEL || _modelType == FSTReader::HEAD_AND_BODY_MODEL) { - insertJointMapping(joints, "jointRoot", _rootJoint->currentText()); - insertJointMapping(joints, "jointLean", _leanJoint->currentText()); - insertJointMapping(joints, "jointHead", _headJoint->currentText()); - insertJointMapping(joints, "jointLeftHand", _leftHandJoint->currentText()); - insertJointMapping(joints, "jointRightHand", _rightHandJoint->currentText()); + insertJointMapping(joints, "jointRoot", _rootJoint->currentText()); + insertJointMapping(joints, "jointLean", _leanJoint->currentText()); + insertJointMapping(joints, "jointHead", _headJoint->currentText()); + insertJointMapping(joints, "jointLeftHand", _leftHandJoint->currentText()); + insertJointMapping(joints, "jointRightHand", _rightHandJoint->currentText()); - mapping.remove(FREE_JOINT_FIELD); - for (int i = 0; i < _freeJoints->count() - 1; i++) { - QComboBox* box = static_cast(_freeJoints->itemAt(i)->widget()->layout()->itemAt(0)->widget()); - mapping.insertMulti(FREE_JOINT_FIELD, box->currentText()); - } - } - mapping.insert(JOINT_FIELD, joints); + mapping.remove(FREE_JOINT_FIELD); + for (int i = 0; i < _freeJoints->count() - 1; i++) { + QComboBox* box = static_cast(_freeJoints->itemAt(i)->widget()->layout()->itemAt(0)->widget()); + mapping.insertMulti(FREE_JOINT_FIELD, box->currentText()); } + mapping.insert(JOINT_FIELD, joints); return mapping; } @@ -165,36 +124,23 @@ void ModelPropertiesDialog::reset() { QVariantHash jointHash = _originalMapping.value(JOINT_FIELD).toHash(); - if (_modelType != FSTReader::ENTITY_MODEL) { - if (_modelType == FSTReader::ATTACHMENT_MODEL) { - _translationX->setValue(_originalMapping.value(TRANSLATION_X_FIELD).toDouble()); - _translationY->setValue(_originalMapping.value(TRANSLATION_Y_FIELD).toDouble()); - _translationZ->setValue(_originalMapping.value(TRANSLATION_Z_FIELD).toDouble()); - _pivotAboutCenter->setChecked(true); - _pivotJoint->setCurrentIndex(0); + setJointText(_leftEyeJoint, jointHash.value("jointEyeLeft").toString()); + setJointText(_rightEyeJoint, jointHash.value("jointEyeRight").toString()); + setJointText(_neckJoint, jointHash.value("jointNeck").toString()); - } else { - setJointText(_leftEyeJoint, jointHash.value("jointEyeLeft").toString()); - setJointText(_rightEyeJoint, jointHash.value("jointEyeRight").toString()); - setJointText(_neckJoint, jointHash.value("jointNeck").toString()); - } + setJointText(_rootJoint, jointHash.value("jointRoot").toString()); + setJointText(_leanJoint, jointHash.value("jointLean").toString()); + setJointText(_headJoint, jointHash.value("jointHead").toString()); + setJointText(_leftHandJoint, jointHash.value("jointLeftHand").toString()); + setJointText(_rightHandJoint, jointHash.value("jointRightHand").toString()); - if (_modelType == FSTReader::BODY_ONLY_MODEL || _modelType == FSTReader::HEAD_AND_BODY_MODEL) { - setJointText(_rootJoint, jointHash.value("jointRoot").toString()); - setJointText(_leanJoint, jointHash.value("jointLean").toString()); - setJointText(_headJoint, jointHash.value("jointHead").toString()); - setJointText(_leftHandJoint, jointHash.value("jointLeftHand").toString()); - setJointText(_rightHandJoint, jointHash.value("jointRightHand").toString()); - - while (_freeJoints->count() > 1) { - delete _freeJoints->itemAt(0)->widget(); - } - foreach (const QVariant& joint, _originalMapping.values(FREE_JOINT_FIELD)) { - QString jointName = joint.toString(); - if (_geometry.jointIndices.contains(jointName)) { - createNewFreeJoint(jointName); - } - } + while (_freeJoints->count() > 1) { + delete _freeJoints->itemAt(0)->widget(); + } + foreach (const QVariant& joint, _originalMapping.values(FREE_JOINT_FIELD)) { + QString jointName = joint.toString(); + if (_geometry.jointIndices.contains(jointName)) { + createNewFreeJoint(jointName); } } } diff --git a/interface/src/ModelPropertiesDialog.h b/interface/src/ModelPropertiesDialog.h index e3c2d8ed6a..6e0f1b1a2e 100644 --- a/interface/src/ModelPropertiesDialog.h +++ b/interface/src/ModelPropertiesDialog.h @@ -29,7 +29,7 @@ class ModelPropertiesDialog : public QDialog { Q_OBJECT public: - ModelPropertiesDialog(FSTReader::ModelType modelType, const QVariantHash& originalMapping, + ModelPropertiesDialog(const QVariantHash& originalMapping, const QString& basePath, const FBXGeometry& geometry); QVariantHash getMapping() const; @@ -45,9 +45,7 @@ private: QComboBox* createJointBox(bool withNone = true) const; QDoubleSpinBox* createTranslationBox() const; void insertJointMapping(QVariantHash& joints, const QString& joint, const QString& name) const; - QString getType() const; - FSTReader::ModelType _modelType; QVariantHash _originalMapping; QString _basePath; FBXGeometry _geometry; @@ -71,4 +69,4 @@ private: QVBoxLayout* _freeJoints = nullptr; }; -#endif // hifi_ModelPropertiesDialog_h \ No newline at end of file +#endif // hifi_ModelPropertiesDialog_h diff --git a/interface/src/ModelSelector.cpp b/interface/src/ModelSelector.cpp index d813e13f91..7e428a294d 100644 --- a/interface/src/ModelSelector.cpp +++ b/interface/src/ModelSelector.cpp @@ -32,13 +32,6 @@ ModelSelector::ModelSelector() { connect(_browseButton, &QPushButton::clicked, this, &ModelSelector::browse); form->addRow("Model File:", _browseButton); - _modelType = new QComboBox(this); - - _modelType->addItem(AVATAR_HEAD_AND_BODY_STRING); - _modelType->addItem(AVATAR_ATTACHEMENT_STRING); - _modelType->addItem(ENTITY_MODEL_STRING); - form->addRow("Model Type:", _modelType); - QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this); connect(buttons, &QDialogButtonBox::accepted, this, &ModelSelector::accept); connect(buttons, &QDialogButtonBox::rejected, this, &QDialog::reject); @@ -49,19 +42,6 @@ QFileInfo ModelSelector::getFileInfo() const { return _modelFile; } -FSTReader::ModelType ModelSelector::getModelType() const { - QString text = _modelType->currentText(); - -if (text == AVATAR_HEAD_AND_BODY_STRING) { - return FSTReader::HEAD_AND_BODY_MODEL; - } else if (text == AVATAR_ATTACHEMENT_STRING) { - return FSTReader::ATTACHMENT_MODEL; - } else if (text == ENTITY_MODEL_STRING) { - return FSTReader::ENTITY_MODEL; - } - Q_UNREACHABLE(); -} - void ModelSelector::accept() { if (!_modelFile.isFile()) { return; diff --git a/interface/src/ModelSelector.h b/interface/src/ModelSelector.h index ee9e75c17a..5bbeb4665e 100644 --- a/interface/src/ModelSelector.h +++ b/interface/src/ModelSelector.h @@ -29,7 +29,6 @@ public: ModelSelector(); QFileInfo getFileInfo() const; - FSTReader::ModelType getModelType() const; public slots: virtual void accept() override; @@ -40,7 +39,6 @@ public: private: QFileInfo _modelFile; QPushButton* _browseButton; - QComboBox* _modelType; }; #endif // hifi_ModelSelector_h From d8217e9a7917cdf9ea5bab3978365ff332c9ee3b Mon Sep 17 00:00:00 2001 From: humbletim Date: Tue, 23 Oct 2018 22:42:06 -0400 Subject: [PATCH 079/286] synthesize key release events on Desktop focus change --- interface/src/Application.cpp | 5 +++++ interface/src/Application.h | 1 + 2 files changed, 6 insertions(+) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index e515a22403..c3196e69b1 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -4069,6 +4069,10 @@ void Application::focusOutEvent(QFocusEvent* event) { SpacemouseManager::getInstance().ManagerFocusOutEvent(); #endif + synthesizeKeyReleasEvents(); +} + +void Application::synthesizeKeyReleasEvents() { // synthesize events for keys currently pressed, since we may not get their release events // Because our key event handlers may manipulate _keysPressed, lets swap the keys pressed into a local copy, // clearing the existing list. @@ -4694,6 +4698,7 @@ void Application::idle() { if (_keyboardDeviceHasFocus && activeFocusItem != offscreenUi->getRootItem()) { _keyboardMouseDevice->pluginFocusOutEvent(); _keyboardDeviceHasFocus = false; + synthesizeKeyReleasEvents(); } else if (activeFocusItem == offscreenUi->getRootItem()) { _keyboardDeviceHasFocus = true; } diff --git a/interface/src/Application.h b/interface/src/Application.h index c3896a64e4..6a46032a73 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -541,6 +541,7 @@ private: void keyReleaseEvent(QKeyEvent* event); void focusOutEvent(QFocusEvent* event); + void synthesizeKeyReleasEvents(); void focusInEvent(QFocusEvent* event); void mouseMoveEvent(QMouseEvent* event); From aa64e51281ed5bc1c52195b03b9f35ac96b0c2d7 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Wed, 24 Oct 2018 11:04:32 +0200 Subject: [PATCH 080/286] Fixed shaders for GLSL 4.1 --- libraries/render-utils/src/ssao.slh | 60 ++++++++---- .../render-utils/src/ssao_debugOcclusion.slf | 93 ++----------------- libraries/render-utils/src/ssao_mip_depth.slf | 5 +- 3 files changed, 49 insertions(+), 109 deletions(-) diff --git a/libraries/render-utils/src/ssao.slh b/libraries/render-utils/src/ssao.slh index 7af0d0a7d6..4fd5e1fd5f 100644 --- a/libraries/render-utils/src/ssao.slh +++ b/libraries/render-utils/src/ssao.slh @@ -285,20 +285,15 @@ float getZEyeAtUV(vec2 texCoord, int level) { return -textureLod(depthPyramidTex, texCoord, level).x; } -float getZEyeAtUV(vec2 texCoord, int level, ivec2 texelOffset) { - return -textureLodOffset(depthPyramidTex, texCoord, level, texelOffset).x; -} +<@func getZEyeAtUVOffset(texCoord, level, texelOffset)@> +-textureLodOffset(depthPyramidTex, <$texCoord$>, <$level$>, <$texelOffset$>).x; +<@endfunc@> float getZEyeAtUV(ivec4 side, vec2 texCoord, int level) { texCoord.x = mix(texCoord.x, (texCoord.x + getStereoSide(side)) * 0.5, isStereo()); return getZEyeAtUV(texCoord, level); } -float getZEyeAtUV(ivec4 side, vec2 texCoord, int level, ivec2 texelOffset) { - texCoord.x = mix(texCoord.x, (texCoord.x + getStereoSide(side)) * 0.5, isStereo()); - return getZEyeAtUV(texCoord, level, texelOffset); -} - vec3 packNormal(vec3 normal) { vec3 absNormal = abs(normal); return 0.5 + normal * 0.5 / max(absNormal.x, max(absNormal.y, absNormal.z)); @@ -335,7 +330,7 @@ vec2 fetchTap(ivec4 side, vec2 tapUV, float tapRadius) { int mipLevel = evalMipFromRadius(tapRadius * float(doFetchMips())); vec2 fetchUV = clamp(tapUV, vec2(0), vec2(1)); - fetchUV = mix(fetchUV, vec2((fetchUV.x + getStereoSide(side)) * 0.5, fetchUV.y), isStereo()); + fetchUV = isStereo() ? vec2((fetchUV.x + getStereoSide(side)) * 0.5, fetchUV.y) : fetchUV; vec2 P; P.x = float(mipLevel); @@ -348,10 +343,12 @@ vec3 buildPosition(ivec4 side, vec2 fragUVPos) { return evalEyePositionFromZeye(side.x, Zeye, fragUVPos); } -vec3 buildPosition(ivec4 side, vec2 fragUVPos, ivec2 texelOffset, vec2 deltaUV) { - float Zeye = getZEyeAtUV(side, fragUVPos, 0, texelOffset); - return evalEyePositionFromZeye(side.x, Zeye, fragUVPos + texelOffset*deltaUV); +<@func buildPositionOffset(side, fragUVPos, sideFragUVPos, texelOffset, deltaUV, position)@> +{ + float Zeye = <$getZEyeAtUVOffset($sideFragUVPos$, 0, $texelOffset$)$> + <$position$> = evalEyePositionFromZeye(<$side$>.x, Zeye, <$fragUVPos$> + <$texelOffset$>*<$deltaUV$>); } +<@endfunc@> vec3 getMinDelta(vec3 centralPoint, vec3 offsetPointPos, vec3 offsetPointNeg) { vec3 delta0 = offsetPointPos - centralPoint; @@ -362,11 +359,25 @@ vec3 getMinDelta(vec3 centralPoint, vec3 offsetPointPos, vec3 offsetPointNeg) { return sqrLength0 < sqrLength1 ? delta0 : delta1; } +const ivec2 UV_RIGHT = ivec2(1,0); +const ivec2 UV_LEFT = ivec2(-1,0); +const ivec2 UV_TOP = ivec2(0,1); +const ivec2 UV_BOTTOM = ivec2(0,-1); + vec3 buildNormal(ivec4 side, vec2 fragUVPos, vec3 fragPosition, vec2 deltaDepthUV) { - vec3 fragPositionDxPos = buildPosition(side, fragUVPos, ivec2(1,0), deltaDepthUV); - vec3 fragPositionDxNeg = buildPosition(side, fragUVPos, ivec2(-1,0), deltaDepthUV); - vec3 fragPositionDyPos = buildPosition(side, fragUVPos, ivec2(0,1), deltaDepthUV); - vec3 fragPositionDyNeg = buildPosition(side, fragUVPos, ivec2(0,-1), deltaDepthUV); + vec2 sideUVPos = fragUVPos; + + sideUVPos.x = mix(sideUVPos.x, (sideUVPos.x + getStereoSide(side)) * 0.5, isStereo()); + + vec3 fragPositionDxPos; + vec3 fragPositionDxNeg; + vec3 fragPositionDyPos; + vec3 fragPositionDyNeg; + + <$buildPositionOffset(side, fragUVPos, sideUVPos, UV_RIGHT, deltaDepthUV, fragPositionDxPos)$> + <$buildPositionOffset(side, fragUVPos, sideUVPos, UV_LEFT, deltaDepthUV, fragPositionDxNeg)$> + <$buildPositionOffset(side, fragUVPos, sideUVPos, UV_TOP, deltaDepthUV, fragPositionDyPos)$> + <$buildPositionOffset(side, fragUVPos, sideUVPos, UV_BOTTOM, deltaDepthUV, fragPositionDyNeg)$> vec3 fragDeltaDx = getMinDelta(fragPosition, fragPositionDxPos, fragPositionDxNeg); vec3 fragDeltaDy = getMinDelta(fragPosition, fragPositionDyPos, fragPositionDyNeg); @@ -376,10 +387,19 @@ vec3 buildNormal(ivec4 side, vec2 fragUVPos, vec3 fragPosition, vec2 deltaDepthU void buildTangentBinormal(ivec4 side, vec2 fragUVPos, vec3 fragPosition, vec3 fragNormal, vec2 deltaDepthUV, out vec3 fragTangent, out vec3 fragBinormal) { - vec3 fragPositionDxPos = buildPosition(side, fragUVPos, ivec2(1,0), deltaDepthUV); - vec3 fragPositionDxNeg = buildPosition(side, fragUVPos, ivec2(-1,0), deltaDepthUV); - vec3 fragPositionDyPos = buildPosition(side, fragUVPos, ivec2(0,1), deltaDepthUV); - vec3 fragPositionDyNeg = buildPosition(side, fragUVPos, ivec2(0,-1), deltaDepthUV); + vec2 sideUVPos = fragUVPos; + + sideUVPos.x = mix(sideUVPos.x, (sideUVPos.x + getStereoSide(side)) * 0.5, isStereo()); + + vec3 fragPositionDxPos; + vec3 fragPositionDxNeg; + vec3 fragPositionDyPos; + vec3 fragPositionDyNeg; + + <$buildPositionOffset(side, fragUVPos, sideUVPos, UV_RIGHT, deltaDepthUV, fragPositionDxPos)$> + <$buildPositionOffset(side, fragUVPos, sideUVPos, UV_LEFT, deltaDepthUV, fragPositionDxNeg)$> + <$buildPositionOffset(side, fragUVPos, sideUVPos, UV_TOP, deltaDepthUV, fragPositionDyPos)$> + <$buildPositionOffset(side, fragUVPos, sideUVPos, UV_BOTTOM, deltaDepthUV, fragPositionDyNeg)$> vec3 fragDeltaDx = getMinDelta(fragPosition, fragPositionDxPos, fragPositionDxNeg); vec3 fragDeltaDy = getMinDelta(fragPosition, fragPositionDyPos, fragPositionDyNeg); diff --git a/libraries/render-utils/src/ssao_debugOcclusion.slf b/libraries/render-utils/src/ssao_debugOcclusion.slf index 52929d65cd..75e3ed5194 100644 --- a/libraries/render-utils/src/ssao_debugOcclusion.slf +++ b/libraries/render-utils/src/ssao_debugOcclusion.slf @@ -37,96 +37,15 @@ vec2 getDebugCursorTexcoord(){ layout(location=0) out vec4 outFragColor; void main(void) { - vec2 imageSize = getStereoSideSize(getResolutionLevel()); - - // In debug adjust the correct frag pixel based on base resolution - vec2 fragCoord = gl_FragCoord.xy; - if (getResolutionLevel() > 0) { - fragCoord /= float (1 << getResolutionLevel()); - } - + // Stereo side info based on the real viewport size of this pass + vec2 sideDepthSize = getDepthTextureSideSize(0); // Pixel Debugged vec2 cursorUV = getDebugCursorTexcoord(); - vec2 cursorPixelPos = cursorUV * imageSize; + vec2 cursorPixelPos = cursorUV * sideDepthSize; - ivec2 ssC = ivec2(cursorPixelPos); - - // Fetch the z under the pixel (stereo or not) - float Zeye = getZEyeAtPixel(ssC, 0); + ivec2 fragUVPos = ivec2(cursorPixelPos); - // Stereo side info - ivec4 side = getStereoSideInfo(ssC.x, getResolutionLevel()); + // TODO - // From now on, ssC is the pixel pos in the side - ssC.x -= side.y; - vec2 fragPos = (vec2(ssC) + vec2(0.5)) / imageSize; - - // The position and normal of the pixel fragment in Eye space - vec3 Cp = evalEyePositionFromZeye(side.x, Zeye, fragPos); - vec3 Cn = evalEyeNormal(Cp); - - // Choose the screen-space sample radius - float ssDiskRadius = evalDiskRadius(Cp.z, imageSize); - - vec2 fragToCursor = cursorPixelPos - fragCoord.xy; - if (dot(fragToCursor,fragToCursor) > ssDiskRadius * ssDiskRadius) { - discard; - } - - // Let's make noise - //float randomPatternRotationAngle = getAngleDithering(ssC); - vec3 wCp = (getViewInverse() * vec4(Cp, 1.0)).xyz; - float randomPatternRotationAngle = getAngleDitheringWorldPos(wCp); - - - // Accumulate the Obscurance for each samples - float sum = 0.0; - float keepTapRadius = 1.0; - int keptMip = -1; - bool keep = false; - int sampleCount = int(getNumSamples()); - for (int i = 0; i < sampleCount; ++i) { - vec3 tap = getTapLocationClampedSSAO(i, randomPatternRotationAngle, ssDiskRadius, cursorPixelPos, imageSize); - - // The occluding point in camera space - vec2 fragToTap = vec2(ssC) + tap.xy - fragCoord.xy; - if (dot(fragToTap,fragToTap) < keepTapRadius) { - keep = true; - keptMip = evalMipFromRadius(tap.z * float(doFetchMips())); - } - - vec3 tapUVZ = fetchTap(side, ssC, tap, imageSize); - - vec3 Q = evalEyePositionFromZeye(side.x, tapUVZ.z, tapUVZ.xy); - - sum += float(tap.z > 0.0) * evalVisibilitySSAO(Cp, Cn, Q); - } - - - float A = max(0.0, 1.0 - sum * getObscuranceScaling() * 5.0 * getInvNumSamples()); - - - - outFragColor = packOcclusionOutput(A, Cp.z, vec3(0,0,1)); - - if ((dot(fragToCursor,fragToCursor) < (100.0 * keepTapRadius * keepTapRadius) )) { - // outFragColor = vec4(vec3(A), 1.0); - outFragColor = vec4(vec3(A), 1.0); - return; - } - - if (!keep) { - outFragColor = vec4(0.1); - } else { - outFragColor.rgb = colorWheel(float(keptMip)/float(MAX_MIP_LEVEL)); - } + outFragColor = packOcclusionOutput(0.0, 0.0, vec3(0.0, 0.0, 1.0)); } diff --git a/libraries/render-utils/src/ssao_mip_depth.slf b/libraries/render-utils/src/ssao_mip_depth.slf index e83e017065..92b9012556 100644 --- a/libraries/render-utils/src/ssao_mip_depth.slf +++ b/libraries/render-utils/src/ssao_mip_depth.slf @@ -15,8 +15,9 @@ LAYOUT(binding=GPU_TEXTURE_MIP_CREATION_INPUT) uniform sampler2D depthTexture; -in vec2 varTexCoord0; -out vec4 outFragColor; +layout(location=0) in vec2 varTexCoord0; + +layout(location=0) out vec4 outFragColor; void main(void) { vec4 depths = textureGather(depthTexture, varTexCoord0); From 936c3beea0d8816c6cc7ae41df8935e55cc61fc6 Mon Sep 17 00:00:00 2001 From: NissimHadar Date: Wed, 24 Oct 2018 15:56:29 -0700 Subject: [PATCH 081/286] Exit if Python executable not found --- tools/auto-tester/src/PythonInterface.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tools/auto-tester/src/PythonInterface.cpp b/tools/auto-tester/src/PythonInterface.cpp index 4922b8a8df..c5878939cc 100644 --- a/tools/auto-tester/src/PythonInterface.cpp +++ b/tools/auto-tester/src/PythonInterface.cpp @@ -18,7 +18,9 @@ PythonInterface::PythonInterface() { QString _pythonPath = QProcessEnvironment::systemEnvironment().value("PYTHON_PATH"); if (!QFile::exists(_pythonPath + "/" + _pythonExe)) { QMessageBox::critical(0, _pythonExe, QString("Python executable not found in ") + _pythonPath); + exit(-1); } + _pythonCommand = _pythonPath + "/" + _pythonExe; } else { QMessageBox::critical(0, "PYTHON_PATH not defined", From 34a03bfc99ee02ec3652a3f913d99a3561af4da5 Mon Sep 17 00:00:00 2001 From: NissimHadar Date: Wed, 24 Oct 2018 15:57:06 -0700 Subject: [PATCH 082/286] Python executable for MAC is `python` --- tools/auto-tester/src/PythonInterface.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tools/auto-tester/src/PythonInterface.h b/tools/auto-tester/src/PythonInterface.h index f32a39a644..a218b68039 100644 --- a/tools/auto-tester/src/PythonInterface.h +++ b/tools/auto-tester/src/PythonInterface.h @@ -19,7 +19,13 @@ public: QString getPythonCommand(); private: +#ifdef Q_OS_WIN const QString _pythonExe{ "python.exe" }; +#endif +#ifdef Q_OS_MACOS + const QString _pythonExe{ "python" }; +#endif + QString _pythonCommand; }; From 14216bdeca89856d4d8e7588744e8d4a115b267f Mon Sep 17 00:00:00 2001 From: NissimHadar Date: Wed, 24 Oct 2018 16:22:12 -0700 Subject: [PATCH 083/286] Starting v7.0 Increased a button size for Mac. --- tools/auto-tester/src/ui/AutoTester.cpp | 2 +- tools/auto-tester/src/ui/AutoTester.ui | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/auto-tester/src/ui/AutoTester.cpp b/tools/auto-tester/src/ui/AutoTester.cpp index 32457c2224..d49f3aaa1c 100644 --- a/tools/auto-tester/src/ui/AutoTester.cpp +++ b/tools/auto-tester/src/ui/AutoTester.cpp @@ -36,7 +36,7 @@ AutoTester::AutoTester(QWidget* parent) : QMainWindow(parent) { _ui.statusLabel->setText(""); _ui.plainTextEdit->setReadOnly(true); - setWindowTitle("Auto Tester - v6.7"); + setWindowTitle("Auto Tester - v7.0"); // Coming soon to an auto-tester near you... //// _helpWindow.textBrowser->setText() diff --git a/tools/auto-tester/src/ui/AutoTester.ui b/tools/auto-tester/src/ui/AutoTester.ui index b277fbdb2a..cf2ffd35cd 100644 --- a/tools/auto-tester/src/ui/AutoTester.ui +++ b/tools/auto-tester/src/ui/AutoTester.ui @@ -43,7 +43,7 @@ - 0 + 3 @@ -572,7 +572,7 @@ 330 170 - 101 + 181 40 From 028d1cf237881c582df787bd859e38af653d75d0 Mon Sep 17 00:00:00 2001 From: NissimHadar Date: Thu, 25 Oct 2018 13:36:15 -0700 Subject: [PATCH 084/286] Minor cleanup. --- tools/auto-tester/src/Downloader.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/auto-tester/src/Downloader.h b/tools/auto-tester/src/Downloader.h index 6d1029698f..742a88b890 100644 --- a/tools/auto-tester/src/Downloader.h +++ b/tools/auto-tester/src/Downloader.h @@ -37,7 +37,7 @@ public: signals: void downloaded(); - private slots: +private slots: void fileDownloaded(QNetworkReply* pReply); private: From 47ce1573eb5494d5107d1967d899d1779c9b4009 Mon Sep 17 00:00:00 2001 From: NissimHadar Date: Thu, 25 Oct 2018 15:17:07 -0700 Subject: [PATCH 085/286] Corrected use of _snapshotFolder. --- tools/auto-tester/src/TestRunner.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/tools/auto-tester/src/TestRunner.cpp b/tools/auto-tester/src/TestRunner.cpp index 674cf6f8e8..94e65d0c8b 100644 --- a/tools/auto-tester/src/TestRunner.cpp +++ b/tools/auto-tester/src/TestRunner.cpp @@ -327,7 +327,6 @@ void TestRunner::startLocalServerProcesses() { void TestRunner::runInterfaceWithTestScript() { QString exeFile = QString("\"") + QDir::toNativeSeparators(_installationFolder) + "\\interface.exe\""; - QString snapshotFolder = QString("\"") + QDir::toNativeSeparators(_snapshotFolder) + "\""; QString url = QString("hifi://localhost"); if (_runServerless->isChecked()) { @@ -340,8 +339,11 @@ void TestRunner::runInterfaceWithTestScript() { QString testScript = QString("https://raw.githubusercontent.com/") + _user + "/hifi_tests/" + _branch + "/tests/testRecursive.js"; - QString commandLine = exeFile + " --url " + url + " --no-updater --no-login" + " --testScript " + testScript + - " quitWhenFinished --testResultsLocation " + snapshotFolder; + QString commandLine = exeFile + " --url " + url + " --no-updater" + " --testScript " + testScript + + " quitWhenFinished --testResultsLocation " + _snapshotFolder; + + // Helpful for debugging + appendLog(commandLine); interfaceWorker->setCommandLine(commandLine); emit startInterface(); From d2e31b3f6715092a48558f1ad3da54ce05670d4d Mon Sep 17 00:00:00 2001 From: NissimHadar Date: Thu, 25 Oct 2018 15:18:29 -0700 Subject: [PATCH 086/286] Use `fromPercentEncoding` to download URL (attempting to get download to run on Mac). --- tools/auto-tester/src/Downloader.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/auto-tester/src/Downloader.cpp b/tools/auto-tester/src/Downloader.cpp index cb9863f34d..3c07d8c3a3 100644 --- a/tools/auto-tester/src/Downloader.cpp +++ b/tools/auto-tester/src/Downloader.cpp @@ -17,7 +17,7 @@ Downloader::Downloader(QUrl fileURL, QObject *parent) : QObject(parent) { this, SLOT (fileDownloaded(QNetworkReply*)) ); - QNetworkRequest request(fileURL); + QNetworkRequest request(QUrl::fromPercentEncoding(fileURL.toString().toLatin1())); _networkAccessManager.get(request); } From 7bf6e9cda918a1c2f93fb74455e9f8b3e8c49694 Mon Sep 17 00:00:00 2001 From: NissimHadar Date: Thu, 25 Oct 2018 16:19:25 -0700 Subject: [PATCH 087/286] Simplified, and seems to work --- tools/auto-tester/src/Downloader.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tools/auto-tester/src/Downloader.cpp b/tools/auto-tester/src/Downloader.cpp index 3c07d8c3a3..de768398b0 100644 --- a/tools/auto-tester/src/Downloader.cpp +++ b/tools/auto-tester/src/Downloader.cpp @@ -17,8 +17,7 @@ Downloader::Downloader(QUrl fileURL, QObject *parent) : QObject(parent) { this, SLOT (fileDownloaded(QNetworkReply*)) ); - QNetworkRequest request(QUrl::fromPercentEncoding(fileURL.toString().toLatin1())); - _networkAccessManager.get(request); + _networkAccessManager.get(QNetworkRequest(fileURL)); } void Downloader::fileDownloaded(QNetworkReply* reply) { @@ -37,4 +36,4 @@ void Downloader::fileDownloaded(QNetworkReply* reply) { QByteArray Downloader::downloadedData() const { return _downloadedData; -} \ No newline at end of file +} From c3b1f8c332d48a75fa042046d760f237b5ff41f9 Mon Sep 17 00:00:00 2001 From: NissimHadar Date: Thu, 25 Oct 2018 17:22:45 -0700 Subject: [PATCH 088/286] Should be able to download Mac installer --- tools/auto-tester/src/TestRunner.cpp | 69 +++++++++++++++------------- tools/auto-tester/src/TestRunner.h | 6 +++ 2 files changed, 44 insertions(+), 31 deletions(-) diff --git a/tools/auto-tester/src/TestRunner.cpp b/tools/auto-tester/src/TestRunner.cpp index 94e65d0c8b..89d9867784 100644 --- a/tools/auto-tester/src/TestRunner.cpp +++ b/tools/auto-tester/src/TestRunner.cpp @@ -538,6 +538,7 @@ void TestRunner::parseBuildInformation() { #elif defined(Q_OS_MAC) platformOfInterest = "mac"; #endif + QDomElement element = domDocument.documentElement(); // Verify first element is "projects" @@ -554,42 +555,48 @@ void TestRunner::parseBuildInformation() { throw("File is not from 'interface' build"); } - // Now loop over the platforms + // Now loop over the platforms, looking for ours + bool platformFound{ false }; + element = element.firstChild().toElement(); while (!element.isNull()) { - element = element.firstChild().toElement(); - if (element.tagName() != "platform" || element.attribute("name") != platformOfInterest) { - continue; + if (element.attribute("name") == platformOfInterest) { + platformFound = true; + break; } - - // Next element should be the build - element = element.firstChild().toElement(); - if (element.tagName() != "build") { - throw("File seems to be in wrong format"); - } - - // Next element should be the version - element = element.firstChild().toElement(); - if (element.tagName() != "version") { - throw("File seems to be in wrong format"); - } - - // Add the build number to the end of the filename - _buildInformation.build = element.text(); - - // First sibling should be stable_version element = element.nextSibling().toElement(); - if (element.tagName() != "stable_version") { - throw("File seems to be in wrong format"); - } - - // Next sibling should be url - element = element.nextSibling().toElement(); - if (element.tagName() != "url") { - throw("File seems to be in wrong format"); - } - _buildInformation.url = element.text(); } + if (!platformFound) { + throw("File seems to be in wrong format - platform " + platformOfInterest + " not found"); + } + + element = element.firstChild().toElement(); + if (element.tagName() != "build") { + throw("File seems to be in wrong format"); + } + + // Next element should be the version + element = element.firstChild().toElement(); + if (element.tagName() != "version") { + throw("File seems to be in wrong format"); + } + + // Add the build number to the end of the filename + _buildInformation.build = element.text(); + + // First sibling should be stable_version + element = element.nextSibling().toElement(); + if (element.tagName() != "stable_version") { + throw("File seems to be in wrong format"); + } + + // Next sibling should be url + element = element.nextSibling().toElement(); + if (element.tagName() != "url") { + throw("File seems to be in wrong format"); + } + _buildInformation.url = element.text(); + } catch (QString errorMessage) { QMessageBox::critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), errorMessage); exit(-1); diff --git a/tools/auto-tester/src/TestRunner.h b/tools/auto-tester/src/TestRunner.h index e6cb7cd764..5b2d55959f 100644 --- a/tools/auto-tester/src/TestRunner.h +++ b/tools/auto-tester/src/TestRunner.h @@ -88,7 +88,13 @@ signals: private: bool _automatedTestIsRunning{ false }; +#ifndef Q_OS_WIN const QString INSTALLER_FILENAME_LATEST{ "HighFidelity-Beta-latest-dev.exe" }; +#elif defined(Q_OS_MAC) + const QString INSTALLER_FILENAME_LATEST{ "HighFidelity-Beta-latest-dev.dmg" }; +#else + const QString INSTALLER_FILENAME_LATEST{ "just to pass compilation" }; +#endif QString _installerURL; QString _installerFilename; From e9310415012a33f2869d341af59fa2ed06b0b289 Mon Sep 17 00:00:00 2001 From: NissimHadar Date: Thu, 25 Oct 2018 17:39:06 -0700 Subject: [PATCH 089/286] Corrected #ifdef --- tools/auto-tester/src/TestRunner.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tools/auto-tester/src/TestRunner.h b/tools/auto-tester/src/TestRunner.h index 5b2d55959f..340ede9bf7 100644 --- a/tools/auto-tester/src/TestRunner.h +++ b/tools/auto-tester/src/TestRunner.h @@ -88,7 +88,7 @@ signals: private: bool _automatedTestIsRunning{ false }; -#ifndef Q_OS_WIN +#ifdef Q_OS_WIN const QString INSTALLER_FILENAME_LATEST{ "HighFidelity-Beta-latest-dev.exe" }; #elif defined(Q_OS_MAC) const QString INSTALLER_FILENAME_LATEST{ "HighFidelity-Beta-latest-dev.dmg" }; @@ -154,4 +154,5 @@ signals: private: QString _commandLine; }; -#endif // hifi_testRunner_h \ No newline at end of file +#endif // hifi_testRunner_h + From 28338a83071d36f40ab7f0473eb262b449d1201c Mon Sep 17 00:00:00 2001 From: NissimHadar Date: Thu, 25 Oct 2018 17:40:39 -0700 Subject: [PATCH 090/286] Minor cleanup. --- tools/auto-tester/src/TestRunner.h | 1 - 1 file changed, 1 deletion(-) diff --git a/tools/auto-tester/src/TestRunner.h b/tools/auto-tester/src/TestRunner.h index 340ede9bf7..8267a042b7 100644 --- a/tools/auto-tester/src/TestRunner.h +++ b/tools/auto-tester/src/TestRunner.h @@ -155,4 +155,3 @@ private: QString _commandLine; }; #endif // hifi_testRunner_h - From 98441b2328f91c64f19fa283f256d62f00231ef2 Mon Sep 17 00:00:00 2001 From: NissimHadar Date: Mon, 29 Oct 2018 15:49:46 -0700 Subject: [PATCH 091/286] WIP - installing High Fidelity from command line. --- tools/auto-tester/src/TestRunner.cpp | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/tools/auto-tester/src/TestRunner.cpp b/tools/auto-tester/src/TestRunner.cpp index 89d9867784..2d5b013f31 100644 --- a/tools/auto-tester/src/TestRunner.cpp +++ b/tools/auto-tester/src/TestRunner.cpp @@ -176,10 +176,26 @@ void TestRunner::runInstaller() { QString installerFullPath = _workingFolder + "/" + _installerFilename; +#ifdef Q_OS_WIN QString commandLine = "\"" + QDir::toNativeSeparators(installerFullPath) + "\"" + " /S /D=" + QDir::toNativeSeparators(_installationFolder); installerWorker->setCommandLine(commandLine); +#elif defined Q_OS_MAC + QFile script; + script.setFileName(_workingFolder + "/install_app.sh"); + if (!script.open(QIODevice::Append | QIODevice::Text)) { + QMessageBox::critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), + "Could not open 'install_app.sh'"); + exit(-1); + } + + script.write("#/bin/sh\n\n"); + script.write("VOLUME=`hdiutil attach \"$1\" | grep Volumes | awk '{print $3}'`"); + script.write((QString("#cp -rf \"$VOLUME/") + _installerFilename + "High Fidelity/interface.app\"").toStdString().c_str()); + + QString commandLine = "yes | ../install_app.sh HighFidelity-Beta-latest-dev.dmg"; +#endif emit startInstaller(); } @@ -614,4 +630,4 @@ int Worker::runCommand() { int result = system(_commandLine.toStdString().c_str()); emit commandComplete(); return result; -} \ No newline at end of file +} From 1213f3f658b4e646863e06716e1c92630ba4a453 Mon Sep 17 00:00:00 2001 From: NissimHadar Date: Mon, 29 Oct 2018 17:51:41 -0700 Subject: [PATCH 092/286] Can run the installer --- tools/auto-tester/src/TestRunner.cpp | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/tools/auto-tester/src/TestRunner.cpp b/tools/auto-tester/src/TestRunner.cpp index 2d5b013f31..09de31120e 100644 --- a/tools/auto-tester/src/TestRunner.cpp +++ b/tools/auto-tester/src/TestRunner.cpp @@ -179,23 +179,31 @@ void TestRunner::runInstaller() { #ifdef Q_OS_WIN QString commandLine = "\"" + QDir::toNativeSeparators(installerFullPath) + "\"" + " /S /D=" + QDir::toNativeSeparators(_installationFolder); - - installerWorker->setCommandLine(commandLine); #elif defined Q_OS_MAC QFile script; script.setFileName(_workingFolder + "/install_app.sh"); - if (!script.open(QIODevice::Append | QIODevice::Text)) { + if (!script.open(QIODevice::WriteOnly | QIODevice::Text)) { QMessageBox::critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), "Could not open 'install_app.sh'"); exit(-1); } script.write("#/bin/sh\n\n"); - script.write("VOLUME=`hdiutil attach \"$1\" | grep Volumes | awk '{print $3}'`"); - script.write((QString("#cp -rf \"$VOLUME/") + _installerFilename + "High Fidelity/interface.app\"").toStdString().c_str()); + script.write("VOLUME=`hdiutil attach \"$1\" | grep Volumes | awk '{print $3}'`\n"); + script.write((QString("mkdir \"") + _workingFolder + "/High Fidelity\"\n").toStdString().c_str()); - QString commandLine = "yes | ../install_app.sh HighFidelity-Beta-latest-dev.dmg"; + QStringList urlParts = _buildInformation.url.split('/'); + QString installerFileName = urlParts[urlParts.length() - 1].split('.')[0]; + script.write((QString("cp -rf \"$VOLUME/") + "/High Fidelity/interface.app\" \"" + _workingFolder + "/High Fidelity/\"\n").toStdString().c_str()); + + script.write("hdiutil detach \"$VOLUME\"\n"); + script.close(); + script.setPermissions(QFile::ReadOwner | QFile::WriteOwner | QFile::ExeOwner); + + QString commandLine = "yes | " + _workingFolder + "/install_app.sh " + _workingFolder + "/HighFidelity-Beta-latest-dev.dmg"; #endif + + installerWorker->setCommandLine(commandLine); emit startInstaller(); } From 6f6c92e6475503ae6fd05fd87f440b04700e5321 Mon Sep 17 00:00:00 2001 From: Simon Walton Date: Tue, 30 Oct 2018 09:40:08 -0700 Subject: [PATCH 093/286] Remove unused variable --- libraries/entities/src/RecurseOctreeToJSONOperator.cpp | 5 ++--- libraries/entities/src/RecurseOctreeToJSONOperator.h | 3 +-- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/libraries/entities/src/RecurseOctreeToJSONOperator.cpp b/libraries/entities/src/RecurseOctreeToJSONOperator.cpp index d67e7176a3..a865009566 100644 --- a/libraries/entities/src/RecurseOctreeToJSONOperator.cpp +++ b/libraries/entities/src/RecurseOctreeToJSONOperator.cpp @@ -12,10 +12,9 @@ #include "RecurseOctreeToJSONOperator.h" #include "EntityItemProperties.h" -RecurseOctreeToJSONOperator::RecurseOctreeToJSONOperator(const OctreeElementPointer& top, QScriptEngine* engine, +RecurseOctreeToJSONOperator::RecurseOctreeToJSONOperator(const OctreeElementPointer&, QScriptEngine* engine, QString jsonPrefix /* = QString() */, bool skipDefaults /* = true */) - : _top(top) - , _engine(engine) + : _engine(engine) , _json(jsonPrefix) , _skipDefaults(skipDefaults) { diff --git a/libraries/entities/src/RecurseOctreeToJSONOperator.h b/libraries/entities/src/RecurseOctreeToJSONOperator.h index f661e08d5b..d0f03d02eb 100644 --- a/libraries/entities/src/RecurseOctreeToJSONOperator.h +++ b/libraries/entities/src/RecurseOctreeToJSONOperator.h @@ -13,7 +13,7 @@ class RecurseOctreeToJSONOperator : public RecurseOctreeOperator { public: - RecurseOctreeToJSONOperator(const OctreeElementPointer& top, QScriptEngine* engine, QString jsonPrefix = QString(), bool skipDefaults = true); + RecurseOctreeToJSONOperator(const OctreeElementPointer&, QScriptEngine* engine, QString jsonPrefix = QString(), bool skipDefaults = true); virtual bool preRecursion(const OctreeElementPointer& element) override { return true; }; virtual bool postRecursion(const OctreeElementPointer& element) override; @@ -22,7 +22,6 @@ public: private: void processEntity(const EntityItemPointer& entity); - const OctreeElementPointer& _top; QScriptEngine* _engine; QScriptValue _toStringMethod; From 418a578e288238a2ff3765b80fa7f6a30c4187eb Mon Sep 17 00:00:00 2001 From: NissimHadar Date: Tue, 30 Oct 2018 13:02:13 -0700 Subject: [PATCH 094/286] Can start Interface --- tools/auto-tester/src/TestRunner.cpp | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/tools/auto-tester/src/TestRunner.cpp b/tools/auto-tester/src/TestRunner.cpp index 09de31120e..9c38fee4c4 100644 --- a/tools/auto-tester/src/TestRunner.cpp +++ b/tools/auto-tester/src/TestRunner.cpp @@ -188,15 +188,21 @@ void TestRunner::runInstaller() { exit(-1); } + QString installFolder = QString("\"") + _workingFolder + "/High Fidelity\""; + if (!QDir().exists(installFolder)) { + QDir().mkdir(installFolder); + } + + // This script installs High Fidelity. It is run as "yes | install_app.sh... so "yes" is killed at the end script.write("#/bin/sh\n\n"); script.write("VOLUME=`hdiutil attach \"$1\" | grep Volumes | awk '{print $3}'`\n"); - script.write((QString("mkdir \"") + _workingFolder + "/High Fidelity\"\n").toStdString().c_str()); QStringList urlParts = _buildInformation.url.split('/'); QString installerFileName = urlParts[urlParts.length() - 1].split('.')[0]; script.write((QString("cp -rf \"$VOLUME/") + "/High Fidelity/interface.app\" \"" + _workingFolder + "/High Fidelity/\"\n").toStdString().c_str()); script.write("hdiutil detach \"$VOLUME\"\n"); + script.write("killall yes\n"); script.close(); script.setPermissions(QFile::ReadOwner | QFile::WriteOwner | QFile::ExeOwner); @@ -350,8 +356,6 @@ void TestRunner::startLocalServerProcesses() { } void TestRunner::runInterfaceWithTestScript() { - QString exeFile = QString("\"") + QDir::toNativeSeparators(_installationFolder) + "\\interface.exe\""; - QString url = QString("hifi://localhost"); if (_runServerless->isChecked()) { // Move to an empty area @@ -363,9 +367,14 @@ void TestRunner::runInterfaceWithTestScript() { QString testScript = QString("https://raw.githubusercontent.com/") + _user + "/hifi_tests/" + _branch + "/tests/testRecursive.js"; +#ifdef Q_OS_WIN + QString exeFile = QString("\"") + QDir::toNativeSeparators(_installationFolder) + "\\interface.exe\""; QString commandLine = exeFile + " --url " + url + " --no-updater" + " --testScript " + testScript + " quitWhenFinished --testResultsLocation " + _snapshotFolder; - +#elif defined Q_OS_MAC + QString commandLine = "open -a \"" +_installationFolder + "/interface.app\""; +#endif + // Helpful for debugging appendLog(commandLine); From 41b70ffab5681f14d27e756627f851bf79c28b33 Mon Sep 17 00:00:00 2001 From: NissimHadar Date: Tue, 30 Oct 2018 16:30:56 -0700 Subject: [PATCH 095/286] Installs both interface and Sandbox. Runs interface. --- tools/auto-tester/src/TestRunner.cpp | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/tools/auto-tester/src/TestRunner.cpp b/tools/auto-tester/src/TestRunner.cpp index 9c38fee4c4..d74073c164 100644 --- a/tools/auto-tester/src/TestRunner.cpp +++ b/tools/auto-tester/src/TestRunner.cpp @@ -84,7 +84,12 @@ void TestRunner::setWorkingFolder() { return; } +#ifdef Q_OS_WIN _installationFolder = _workingFolder + "/High Fidelity"; +#elif defined Q_OS_MAC + _installationFolder = _workingFolder + "/High_Fidelity"; +#endif + _logFile.setFileName(_workingFolder + "/log.txt"); autoTester->enableRunTabControls(); @@ -188,7 +193,7 @@ void TestRunner::runInstaller() { exit(-1); } - QString installFolder = QString("\"") + _workingFolder + "/High Fidelity\""; + QString installFolder = QString("\"") + _workingFolder + "/High_Fidelity\""; if (!QDir().exists(installFolder)) { QDir().mkdir(installFolder); } @@ -197,10 +202,9 @@ void TestRunner::runInstaller() { script.write("#/bin/sh\n\n"); script.write("VOLUME=`hdiutil attach \"$1\" | grep Volumes | awk '{print $3}'`\n"); - QStringList urlParts = _buildInformation.url.split('/'); - QString installerFileName = urlParts[urlParts.length() - 1].split('.')[0]; - script.write((QString("cp -rf \"$VOLUME/") + "/High Fidelity/interface.app\" \"" + _workingFolder + "/High Fidelity/\"\n").toStdString().c_str()); - + script.write((QString("cp -rf \"$VOLUME/") + "/High Fidelity/interface.app\" \"" + _workingFolder + "/High_Fidelity/\"\n").toStdString().c_str()); + script.write((QString("cp -rf \"$VOLUME/") + "/High Fidelity/Sandbox.app\" \"" + _workingFolder + "/High_Fidelity/\"\n").toStdString().c_str()); + script.write("hdiutil detach \"$VOLUME\"\n"); script.write("killall yes\n"); script.close(); From 339ecb1faa3897de1a9e0e5e1011fcb43ffdfdab Mon Sep 17 00:00:00 2001 From: amantley Date: Tue, 30 Oct 2018 16:54:34 -0700 Subject: [PATCH 096/286] adding the offsets to the AnimSkeleton.cpp --- libraries/animation/src/AnimSkeleton.cpp | 34 ++++++++++++++++++++++-- libraries/animation/src/AnimSkeleton.h | 1 + 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/libraries/animation/src/AnimSkeleton.cpp b/libraries/animation/src/AnimSkeleton.cpp index bed9c590be..0a4303baf3 100644 --- a/libraries/animation/src/AnimSkeleton.cpp +++ b/libraries/animation/src/AnimSkeleton.cpp @@ -20,11 +20,30 @@ AnimSkeleton::AnimSkeleton(const FBXGeometry& fbxGeometry) { // convert to std::vector of joints std::vector joints; joints.reserve(fbxGeometry.joints.size()); + AnimPose identity; for (auto& joint : fbxGeometry.joints) { joints.push_back(joint); + _avatarTPoseOffsets.push_back(identity); } buildSkeletonFromJoints(joints); -} + //add offsets for spine2 and the neck + _avatarTPoseOffsets[nameToJointIndex("Spine2")] = AnimPose(glm::quat(-0.707107f, 0.0f, 0.0f, 0.707107f), glm::vec3()); + _avatarTPoseOffsets[nameToJointIndex("Neck")] = AnimPose(glm::quat(0.0f, 0.707107f, 0.0f, 0.707107f), glm::vec3()); + + for (int i = 0; i < (int)fbxGeometry.meshes.size(); i++) { + const FBXMesh& mesh = fbxGeometry.meshes.at(i); + for (int j = 0; j < mesh.clusters.size(); j++) { + // cast into a non-const reference, so we can mutate the FBXCluster + FBXCluster& cluster = const_cast(mesh.clusters.at(j)); + // AJT: mutate bind pose! this allows us to oreint the skeleton back into the authored orientaiton before + // rendering, with no runtime overhead. + // this works if clusters match joints one for one. + //cluster.inverseBindMatrix = (glm::mat4)_avatarTPoseOffsets[cluster.jointIndex].inverse() * cluster.inverseBindMatrix; + cluster.inverseBindTransform.evalFromRawMatrix(cluster.inverseBindMatrix); + + } + } +} AnimSkeleton::AnimSkeleton(const std::vector& joints) { buildSkeletonFromJoints(joints); @@ -189,8 +208,19 @@ void AnimSkeleton::buildSkeletonFromJoints(const std::vector& joints) // build relative and absolute default poses glm::mat4 relDefaultMat = glm::translate(_joints[i].translation) * preRotationTransform * glm::mat4_cast(_joints[i].rotation) * postRotationTransform; AnimPose relDefaultPose(relDefaultMat); - _relativeDefaultPoses.push_back(relDefaultPose); + int parentIndex = getParentIndex(i); + + // putting the pipeline code is + // remember the inverse bind pose already has the offset added into it. the total effect is offset^-1 * relDefPose * offset. + // this gives us the correct transform for the joint that has been put in t-pose with an offset rotation. + //relDefaultPose = relDefaultPose * _avatarTPoseOffsets[i]; + //if (parentIndex >= 0) { + // relDefaultPose = _avatarTPoseOffsets[parentIndex].inverse() * AnimPose(glm::quat(), relDefaultPose.trans()) * _avatarTPoseOffsets[parentIndex] * AnimPose(relDefaultPose.rot(), glm::vec3()); + //} + + _relativeDefaultPoses.push_back(relDefaultPose); + if (parentIndex >= 0) { _absoluteDefaultPoses.push_back(_absoluteDefaultPoses[parentIndex] * relDefaultPose); } else { diff --git a/libraries/animation/src/AnimSkeleton.h b/libraries/animation/src/AnimSkeleton.h index 2ebf3f4f5d..ad1120fcbf 100644 --- a/libraries/animation/src/AnimSkeleton.h +++ b/libraries/animation/src/AnimSkeleton.h @@ -67,6 +67,7 @@ protected: void buildSkeletonFromJoints(const std::vector& joints); std::vector _joints; + AnimPoseVec _avatarTPoseOffsets; int _jointsSize { 0 }; AnimPoseVec _relativeDefaultPoses; AnimPoseVec _absoluteDefaultPoses; From e24f10a125e9a6ffba69b7c959b4ec665653e4c7 Mon Sep 17 00:00:00 2001 From: amantley Date: Tue, 30 Oct 2018 17:33:28 -0700 Subject: [PATCH 097/286] debugging offset vector --- libraries/animation/src/AnimSkeleton.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/libraries/animation/src/AnimSkeleton.cpp b/libraries/animation/src/AnimSkeleton.cpp index 0a4303baf3..bd8d62bdb4 100644 --- a/libraries/animation/src/AnimSkeleton.cpp +++ b/libraries/animation/src/AnimSkeleton.cpp @@ -20,12 +20,14 @@ AnimSkeleton::AnimSkeleton(const FBXGeometry& fbxGeometry) { // convert to std::vector of joints std::vector joints; joints.reserve(fbxGeometry.joints.size()); - AnimPose identity; + _avatarTPoseOffsets.reserve(fbxGeometry.joints.size()); + AnimPose identity(glm::quat(), glm::vec3()); for (auto& joint : fbxGeometry.joints) { joints.push_back(joint); _avatarTPoseOffsets.push_back(identity); } buildSkeletonFromJoints(joints); + /* //add offsets for spine2 and the neck _avatarTPoseOffsets[nameToJointIndex("Spine2")] = AnimPose(glm::quat(-0.707107f, 0.0f, 0.0f, 0.707107f), glm::vec3()); _avatarTPoseOffsets[nameToJointIndex("Neck")] = AnimPose(glm::quat(0.0f, 0.707107f, 0.0f, 0.707107f), glm::vec3()); @@ -43,6 +45,7 @@ AnimSkeleton::AnimSkeleton(const FBXGeometry& fbxGeometry) { } } + */ } AnimSkeleton::AnimSkeleton(const std::vector& joints) { From 263a831fc4dc5b2f3e3cc52aa5f20848d8d7e331 Mon Sep 17 00:00:00 2001 From: NissimHadar Date: Tue, 30 Oct 2018 18:41:10 -0700 Subject: [PATCH 098/286] Runs Interface with Sandbox --- tools/auto-tester/src/TestRunner.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/tools/auto-tester/src/TestRunner.cpp b/tools/auto-tester/src/TestRunner.cpp index d74073c164..315ac732e0 100644 --- a/tools/auto-tester/src/TestRunner.cpp +++ b/tools/auto-tester/src/TestRunner.cpp @@ -345,16 +345,21 @@ void TestRunner::killProcesses() { } void TestRunner::startLocalServerProcesses() { -#ifdef Q_OS_WIN QString commandLine; - + +#ifdef Q_OS_WIN commandLine = "start \"domain-server.exe\" \"" + QDir::toNativeSeparators(_installationFolder) + "\\domain-server.exe\""; system(commandLine.toStdString().c_str()); commandLine = "start \"assignment-client.exe\" \"" + QDir::toNativeSeparators(_installationFolder) + "\\assignment-client.exe\" -n 6"; system(commandLine.toStdString().c_str()); + +#elif defined Q_OS_MAC + commandLine = "open -a \"" +_installationFolder + "/Sandbox.app\""; + system(commandLine.toStdString().c_str()); #endif + // Give server processes time to stabilize QThread::sleep(20); } From 5e58da8ec10c18494dffb7a39e2a697a1bf59cee Mon Sep 17 00:00:00 2001 From: NissimHadar Date: Tue, 30 Oct 2018 22:29:50 -0700 Subject: [PATCH 099/286] Interface runs, but AutoTester doesn't wait for completion. --- tools/auto-tester/src/TestRunner.cpp | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/tools/auto-tester/src/TestRunner.cpp b/tools/auto-tester/src/TestRunner.cpp index 315ac732e0..2bf304943b 100644 --- a/tools/auto-tester/src/TestRunner.cpp +++ b/tools/auto-tester/src/TestRunner.cpp @@ -20,7 +20,7 @@ extern AutoTester* autoTester; #include #include #endif - +#include TestRunner::TestRunner(std::vector dayCheckboxes, std::vector timeEditCheckboxes, std::vector timeEdits, @@ -247,11 +247,10 @@ void TestRunner::verifyInstallationSucceeded() { } void TestRunner::saveExistingHighFidelityAppDataFolder() { +#ifdef Q_OS_WIN QString dataDirectory{ "NOT FOUND" }; -#ifdef Q_OS_WIN dataDirectory = qgetenv("USERPROFILE") + "\\AppData\\Roaming"; -#endif if (_runLatest->isChecked()) { _appDataFolder = dataDirectory + "\\High Fidelity"; @@ -272,6 +271,9 @@ void TestRunner::saveExistingHighFidelityAppDataFolder() { // Copy an "empty" AppData folder (i.e. no entities) copyFolder(QDir::currentPath() + "/AppDataHighFidelity", _appDataFolder.path()); +#elif defined Q_OS_MAC + // TODO: find Mac equivalent of AppData +#endif } void TestRunner::createSnapshotFolder() { @@ -341,6 +343,13 @@ void TestRunner::killProcesses() { QMessageBox::critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), "unknown error"); exit(-1); } +#elif defined Q_OS_MAC + // TODO: this doesn't allow interface to run + //QString commandLine = "killall interface\n"; + //system(commandLine.toStdString().c_str()); + + //commandLine = "killall Sandbox\n"; + //system(commandLine.toStdString().c_str()); #endif } @@ -370,7 +379,12 @@ void TestRunner::runInterfaceWithTestScript() { // Move to an empty area url = "file:///~serverless/tutorial.json"; } else { +#ifdef Q_OS_WIN url = "hifi://localhost"; +#elif defined Q_OS_MAC + // TODO: Find out Mac equivalent of AppData, then this win't be needed + url = "hifi://localhost/9999,9999,9999"; +#endif } QString testScript = @@ -381,7 +395,8 @@ void TestRunner::runInterfaceWithTestScript() { QString commandLine = exeFile + " --url " + url + " --no-updater" + " --testScript " + testScript + " quitWhenFinished --testResultsLocation " + _snapshotFolder; #elif defined Q_OS_MAC - QString commandLine = "open -a \"" +_installationFolder + "/interface.app\""; + QString commandLine = "open -a \"" +_installationFolder + "/interface.app\"" + " --args --url " + url + " --no-updater" + " --testScript " + testScript + + " quitWhenFinished --testResultsLocation " + _snapshotFolder; #endif // Helpful for debugging @@ -448,11 +463,15 @@ void TestRunner::addBuildNumberToResults(QString zippedFolderName) { } void TestRunner::restoreHighFidelityAppDataFolder() { +#ifdef Q_OS_WIN _appDataFolder.removeRecursively(); if (_savedAppDataFolder != QDir()) { _appDataFolder.rename(_savedAppDataFolder.path(), _appDataFolder.path()); } +#elif defined Q_OS_MAC + // TODO: find Mac equivalent of AppData +#endif } // Copies a folder recursively From 675334a105248363800fd3840dd95e1ccff49551 Mon Sep 17 00:00:00 2001 From: NissimHadar Date: Tue, 30 Oct 2018 22:47:11 -0700 Subject: [PATCH 100/286] Do not need `-a` when using open with full path to app --- tools/auto-tester/src/TestRunner.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/auto-tester/src/TestRunner.cpp b/tools/auto-tester/src/TestRunner.cpp index 2bf304943b..1beebe29b1 100644 --- a/tools/auto-tester/src/TestRunner.cpp +++ b/tools/auto-tester/src/TestRunner.cpp @@ -365,7 +365,7 @@ void TestRunner::startLocalServerProcesses() { system(commandLine.toStdString().c_str()); #elif defined Q_OS_MAC - commandLine = "open -a \"" +_installationFolder + "/Sandbox.app\""; + commandLine = "open \"" +_installationFolder + "/Sandbox.app\""; system(commandLine.toStdString().c_str()); #endif @@ -395,7 +395,7 @@ void TestRunner::runInterfaceWithTestScript() { QString commandLine = exeFile + " --url " + url + " --no-updater" + " --testScript " + testScript + " quitWhenFinished --testResultsLocation " + _snapshotFolder; #elif defined Q_OS_MAC - QString commandLine = "open -a \"" +_installationFolder + "/interface.app\"" + " --args --url " + url + " --no-updater" + " --testScript " + testScript + + QString commandLine = "open \"" +_installationFolder + "/interface.app\"" + " --args --url " + url + " --no-updater" + " --testScript " + testScript + " quitWhenFinished --testResultsLocation " + _snapshotFolder; #endif From c070bce2ec6cb257cc0f400fc7cea909b3663cd3 Mon Sep 17 00:00:00 2001 From: luiscuenca Date: Wed, 31 Oct 2018 08:39:35 -0700 Subject: [PATCH 101/286] Read joint rotation offsets from .fst --- libraries/animation/src/Rig.cpp | 13 ++++++++++ libraries/animation/src/Rig.h | 4 ++++ .../src/avatars-renderer/SkeletonModel.cpp | 3 ++- libraries/fbx/src/FSTReader.cpp | 24 +++++++++++++++++++ libraries/fbx/src/FSTReader.h | 3 +++ 5 files changed, 46 insertions(+), 1 deletion(-) diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 33b9569758..c58a96aa84 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -2062,3 +2062,16 @@ void Rig::computeAvatarBoundingCapsule( glm::vec3 capsuleCenter = transformPoint(_geometryToRigTransform, (0.5f * (totalExtents.maximum + totalExtents.minimum))); localOffsetOut = capsuleCenter - hipsPosition; } + +void Rig::setJointRotationOffsets(const QMap& offsets) { + _jointRotationOffsets.clear(); + for (auto itr = offsets.begin(); itr != offsets.end(); itr++) { + QString jointName = itr.key(); + glm::quat rotationOffset = itr.value(); + int jointIndex = indexOfJoint(jointName); + if (jointIndex != -1) { + _jointRotationOffsets.insert(jointIndex, rotationOffset); + } + qDebug() << "Joint Rotation Offset added to Rig._jointRotationOffsets : " << " jointName: " << jointName << " jointIndex: " << jointIndex << " rotation offset: " << rotationOffset; + } +} \ No newline at end of file diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index 7a090bd7bd..9928810026 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -231,6 +231,8 @@ public: const AnimContext::DebugAlphaMap& getDebugAlphaMap() const { return _lastContext.getDebugAlphaMap(); } const AnimVariantMap& getAnimVars() const { return _lastAnimVars; } const AnimContext::DebugStateMachineMap& getStateMachineMap() const { return _lastContext.getStateMachineMap(); } + + void setJointRotationOffsets(const QMap& offsets); signals: void onLoadComplete(); @@ -300,6 +302,8 @@ protected: int _rightElbowJointIndex { -1 }; int _rightShoulderJointIndex { -1 }; + QMap _jointRotationOffsets; + glm::vec3 _lastForward; glm::vec3 _lastPosition; glm::vec3 _lastVelocity; diff --git a/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp b/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp index 1ec58fd704..816e078886 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp @@ -18,6 +18,7 @@ #include #include #include +#include #include "Avatar.h" #include "Logging.h" @@ -82,7 +83,7 @@ void SkeletonModel::initJointStates() { // Skeleton may have already been scaled so unscale it _defaultEyeModelPosition = _defaultEyeModelPosition / _scale; - + _rig.setJointRotationOffsets(FSTReader::getJointRotationOffsets(getGeometry()->getMapping())); computeBoundingShape(); Extents meshExtents = getMeshExtents(); diff --git a/libraries/fbx/src/FSTReader.cpp b/libraries/fbx/src/FSTReader.cpp index 75596862d2..10ea9dc964 100644 --- a/libraries/fbx/src/FSTReader.cpp +++ b/libraries/fbx/src/FSTReader.cpp @@ -207,6 +207,30 @@ QVector FSTReader::getScripts(const QUrl& url, const QVariantHash& mapp return scriptPaths; } +QMap FSTReader::getJointRotationOffsets(const QVariantHash& mapping) { + QMap jointRotationOffsets; + if (!mapping.isEmpty() && mapping.contains(JOINT_ROTATION_OFFSET_FIELD) && mapping[JOINT_ROTATION_OFFSET_FIELD].type() == QVariant::Hash) { + auto offsets = mapping[JOINT_ROTATION_OFFSET_FIELD].toHash(); + for (auto itr = offsets.begin(); itr != offsets.end(); itr++) { + QString jointName = itr.key(); + QString line = itr.value().toString(); + auto eulerAngles = line.split(','); + if (eulerAngles.size() == 3) { + float eulerX = eulerAngles[0].mid(1).toFloat(); + float eulerY = eulerAngles[1].toFloat(); + float eulerZ = eulerAngles[2].mid(0, eulerAngles[2].size() - 1).toFloat(); + if (!isNaN(eulerX) && !isNaN(eulerY) && !isNaN(eulerZ)) { + glm::quat rotationOffset = (glm::angleAxis(eulerX * RADIANS_PER_DEGREE, Vectors::UNIT_Y) * + glm::angleAxis(eulerY * RADIANS_PER_DEGREE, Vectors::UNIT_X) * + glm::angleAxis(eulerZ * RADIANS_PER_DEGREE, Vectors::UNIT_Z)); + jointRotationOffsets.insert(jointName, rotationOffset); + } + } + } + } + return jointRotationOffsets; +} + QVariantHash FSTReader::downloadMapping(const QString& url) { QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); QNetworkRequest networkRequest = QNetworkRequest(url); diff --git a/libraries/fbx/src/FSTReader.h b/libraries/fbx/src/FSTReader.h index 4a8574f0cf..ef649eb4d8 100644 --- a/libraries/fbx/src/FSTReader.h +++ b/libraries/fbx/src/FSTReader.h @@ -14,6 +14,7 @@ #include #include +#include static const QString NAME_FIELD = "name"; static const QString TYPE_FIELD = "type"; @@ -29,6 +30,7 @@ static const QString JOINT_FIELD = "joint"; static const QString FREE_JOINT_FIELD = "freeJoint"; static const QString BLENDSHAPE_FIELD = "bs"; static const QString SCRIPT_FIELD = "script"; +static const QString JOINT_ROTATION_OFFSET_FIELD = "jointRotationOffset"; class FSTReader { public: @@ -51,6 +53,7 @@ public: static ModelType predictModelType(const QVariantHash& mapping); static QVector getScripts(const QUrl& fstUrl, const QVariantHash& mapping = QVariantHash()); + static QMap getJointRotationOffsets(const QVariantHash& mapping); static QString getNameFromType(ModelType modelType); static FSTReader::ModelType getTypeFromName(const QString& name); From 914491ae27e27ee0998a49967352bc3a776aa3c1 Mon Sep 17 00:00:00 2001 From: luiscuenca Date: Wed, 31 Oct 2018 11:48:55 -0700 Subject: [PATCH 102/286] fix YX coord swamp --- libraries/fbx/src/FSTReader.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/fbx/src/FSTReader.cpp b/libraries/fbx/src/FSTReader.cpp index 10ea9dc964..05690f5740 100644 --- a/libraries/fbx/src/FSTReader.cpp +++ b/libraries/fbx/src/FSTReader.cpp @@ -220,8 +220,8 @@ QMap FSTReader::getJointRotationOffsets(const QVariantHash& float eulerY = eulerAngles[1].toFloat(); float eulerZ = eulerAngles[2].mid(0, eulerAngles[2].size() - 1).toFloat(); if (!isNaN(eulerX) && !isNaN(eulerY) && !isNaN(eulerZ)) { - glm::quat rotationOffset = (glm::angleAxis(eulerX * RADIANS_PER_DEGREE, Vectors::UNIT_Y) * - glm::angleAxis(eulerY * RADIANS_PER_DEGREE, Vectors::UNIT_X) * + glm::quat rotationOffset = (glm::angleAxis(eulerX * RADIANS_PER_DEGREE, Vectors::UNIT_X) * + glm::angleAxis(eulerY * RADIANS_PER_DEGREE, Vectors::UNIT_Y) * glm::angleAxis(eulerZ * RADIANS_PER_DEGREE, Vectors::UNIT_Z)); jointRotationOffsets.insert(jointName, rotationOffset); } From 40a31b63a980eb4e34a54b87e4d0442e68dd245b Mon Sep 17 00:00:00 2001 From: NissimHadar Date: Wed, 31 Oct 2018 14:48:27 -0700 Subject: [PATCH 103/286] Waits for Interface to finish --- tools/auto-tester/src/PythonInterface.cpp | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/tools/auto-tester/src/PythonInterface.cpp b/tools/auto-tester/src/PythonInterface.cpp index c5878939cc..2866ca0b87 100644 --- a/tools/auto-tester/src/PythonInterface.cpp +++ b/tools/auto-tester/src/PythonInterface.cpp @@ -14,19 +14,6 @@ #include PythonInterface::PythonInterface() { - if (QProcessEnvironment::systemEnvironment().contains("PYTHON_PATH")) { - QString _pythonPath = QProcessEnvironment::systemEnvironment().value("PYTHON_PATH"); - if (!QFile::exists(_pythonPath + "/" + _pythonExe)) { - QMessageBox::critical(0, _pythonExe, QString("Python executable not found in ") + _pythonPath); - exit(-1); - } - - _pythonCommand = _pythonPath + "/" + _pythonExe; - } else { - QMessageBox::critical(0, "PYTHON_PATH not defined", - "Please set PYTHON_PATH to directory containing the Python executable"); - exit(-1); - } } QString PythonInterface::getPythonCommand() { From 1d67531a87b26832167b746a70e6e3fb3c0c9f90 Mon Sep 17 00:00:00 2001 From: NissimHadar Date: Wed, 31 Oct 2018 22:45:12 -0700 Subject: [PATCH 104/286] Improve UI --- tools/auto-tester/src/ui/AutoTester.ui | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/tools/auto-tester/src/ui/AutoTester.ui b/tools/auto-tester/src/ui/AutoTester.ui index cf2ffd35cd..08f4b46723 100644 --- a/tools/auto-tester/src/ui/AutoTester.ui +++ b/tools/auto-tester/src/ui/AutoTester.ui @@ -43,7 +43,7 @@ - 3 + 0 @@ -198,7 +198,7 @@ 10 160 161 - 28 + 51 @@ -525,7 +525,7 @@ 128 95 - 21 + 31 31 @@ -539,7 +539,7 @@ - 160 + 170 100 451 21 @@ -554,9 +554,9 @@ - 200 + 190 180 - 120 + 131 20 @@ -573,7 +573,7 @@ 330 170 181 - 40 + 51 @@ -684,10 +684,10 @@ - 240 + 270 30 160 - 40 + 51 @@ -699,7 +699,7 @@ 150 42 - 81 + 111 17 @@ -803,7 +803,7 @@ 0 0 720 - 21 + 22 From 716bd8ebd2ffaa7fb183f74526ca042cc5e10171 Mon Sep 17 00:00:00 2001 From: NissimHadar Date: Wed, 31 Oct 2018 22:45:52 -0700 Subject: [PATCH 105/286] WIP - starting on AWS Interface --- tools/auto-tester/src/TestRunner.cpp | 93 ++++++++++++++++++---------- 1 file changed, 60 insertions(+), 33 deletions(-) diff --git a/tools/auto-tester/src/TestRunner.cpp b/tools/auto-tester/src/TestRunner.cpp index 1beebe29b1..1f31004b2f 100644 --- a/tools/auto-tester/src/TestRunner.cpp +++ b/tools/auto-tester/src/TestRunner.cpp @@ -20,7 +20,10 @@ extern AutoTester* autoTester; #include #include #endif + +// TODO: for debug #include + TestRunner::TestRunner(std::vector dayCheckboxes, std::vector timeEditCheckboxes, std::vector timeEdits, @@ -98,6 +101,51 @@ void TestRunner::setWorkingFolder() { _timer = new QTimer(this); connect(_timer, SIGNAL(timeout()), this, SLOT(checkTime())); _timer->start(30 * 1000); //time specified in ms + +#ifdef Q_OS_MAC + // Create MAC shell scripts + QFile script; + script.setFileName(_workingFolder + "/install_app.sh"); + if (!script.open(QIODevice::WriteOnly | QIODevice::Text)) { + QMessageBox::critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), + "Could not open 'install_app.sh'"); + exit(-1); + } + + QString installFolder = QString("\"") + _workingFolder + "/High_Fidelity\""; + if (!QDir().exists(installFolder)) { + QDir().mkdir(installFolder); + } + + // This script installs High Fidelity. It is run as "yes | install_app.sh... so "yes" is killed at the end + script.write("#/bin/sh\n\n"); + script.write("VOLUME=`hdiutil attach \"$1\" | grep Volumes | awk '{print $3}'`\n"); + + script.write((QString("cp -rf \"$VOLUME/") + "/High Fidelity/interface.app\" \"" + _workingFolder + "/High_Fidelity/\"\n").toStdString().c_str()); + script.write((QString("cp -rf \"$VOLUME/") + "/High Fidelity/Sandbox.app\" \"" + _workingFolder + "/High_Fidelity/\"\n").toStdString().c_str()); + + script.write("hdiutil detach \"$VOLUME\"\n"); + script.write("killall yes\n"); + script.close(); + script.setPermissions(QFile::ReadOwner | QFile::WriteOwner | QFile::ExeOwner); + + // The Mac shell command returns immediately. This little script waits for a process to complete + script.setFileName(_workingFolder + "/waitForCompletion.sh"); + if (!script.open(QIODevice::WriteOnly | QIODevice::Text)) { + QMessageBox::critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), + "Could not open 'install_app.sh'"); + exit(-1); + } + + script.write("#/bin/sh\n\n"); + script.write("PROCESS=\"$1\"\n"); + script.write("while (pgrep $PROCESS)\n"); + script.write("do\n"); + script.write("\tsleep 2\n"); + script.write("done\n"); + script.close(); + script.setPermissions(QFile::ReadOwner | QFile::WriteOwner | QFile::ExeOwner); + #endif } void TestRunner::run() { @@ -185,31 +233,6 @@ void TestRunner::runInstaller() { QString commandLine = "\"" + QDir::toNativeSeparators(installerFullPath) + "\"" + " /S /D=" + QDir::toNativeSeparators(_installationFolder); #elif defined Q_OS_MAC - QFile script; - script.setFileName(_workingFolder + "/install_app.sh"); - if (!script.open(QIODevice::WriteOnly | QIODevice::Text)) { - QMessageBox::critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), - "Could not open 'install_app.sh'"); - exit(-1); - } - - QString installFolder = QString("\"") + _workingFolder + "/High_Fidelity\""; - if (!QDir().exists(installFolder)) { - QDir().mkdir(installFolder); - } - - // This script installs High Fidelity. It is run as "yes | install_app.sh... so "yes" is killed at the end - script.write("#/bin/sh\n\n"); - script.write("VOLUME=`hdiutil attach \"$1\" | grep Volumes | awk '{print $3}'`\n"); - - script.write((QString("cp -rf \"$VOLUME/") + "/High Fidelity/interface.app\" \"" + _workingFolder + "/High_Fidelity/\"\n").toStdString().c_str()); - script.write((QString("cp -rf \"$VOLUME/") + "/High Fidelity/Sandbox.app\" \"" + _workingFolder + "/High_Fidelity/\"\n").toStdString().c_str()); - - script.write("hdiutil detach \"$VOLUME\"\n"); - script.write("killall yes\n"); - script.close(); - script.setPermissions(QFile::ReadOwner | QFile::WriteOwner | QFile::ExeOwner); - QString commandLine = "yes | " + _workingFolder + "/install_app.sh " + _workingFolder + "/HighFidelity-Beta-latest-dev.dmg"; #endif @@ -344,12 +367,11 @@ void TestRunner::killProcesses() { exit(-1); } #elif defined Q_OS_MAC - // TODO: this doesn't allow interface to run - //QString commandLine = "killall interface\n"; - //system(commandLine.toStdString().c_str()); + QString commandLine = QString("killall interface") + "; " + _workingFolder +"/waitForCompletion.sh interface"; + system(commandLine.toStdString().c_str()); - //commandLine = "killall Sandbox\n"; - //system(commandLine.toStdString().c_str()); + commandLine = QString("killall Sandbox") + "; " + _workingFolder +"/waitForCompletion.sh interface"; + system(commandLine.toStdString().c_str()); #endif } @@ -357,7 +379,8 @@ void TestRunner::startLocalServerProcesses() { QString commandLine; #ifdef Q_OS_WIN - commandLine = "start \"domain-server.exe\" \"" + QDir::toNativeSeparators(_installationFolder) + "\\domain-server.exe\""; + commandLine = + "start \"domain-server.exe\" \"" + QDir::toNativeSeparators(_installationFolder) + "\\domain-server.exe\""; system(commandLine.toStdString().c_str()); commandLine = @@ -395,8 +418,12 @@ void TestRunner::runInterfaceWithTestScript() { QString commandLine = exeFile + " --url " + url + " --no-updater" + " --testScript " + testScript + " quitWhenFinished --testResultsLocation " + _snapshotFolder; #elif defined Q_OS_MAC - QString commandLine = "open \"" +_installationFolder + "/interface.app\"" + " --args --url " + url + " --no-updater" + " --testScript " + testScript + - " quitWhenFinished --testResultsLocation " + _snapshotFolder; + QString commandLine = "open \"" +_installationFolder + "/interface.app\" --args" + + " --url " + url + + " --no-updater" + + " --testScript " + testScript + " quitWhenFinished" + + " --testResultsLocation " + _snapshotFolder + + "; " + _workingFolder +"/waitForCompletion.sh interface"; #endif // Helpful for debugging From 1c7c0dd0a02680fb80215587aff0154470733fb2 Mon Sep 17 00:00:00 2001 From: NissimHadar Date: Wed, 31 Oct 2018 23:07:44 -0700 Subject: [PATCH 106/286] Booboo... --- tools/auto-tester/src/PythonInterface.cpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/tools/auto-tester/src/PythonInterface.cpp b/tools/auto-tester/src/PythonInterface.cpp index 2866ca0b87..c5878939cc 100644 --- a/tools/auto-tester/src/PythonInterface.cpp +++ b/tools/auto-tester/src/PythonInterface.cpp @@ -14,6 +14,19 @@ #include PythonInterface::PythonInterface() { + if (QProcessEnvironment::systemEnvironment().contains("PYTHON_PATH")) { + QString _pythonPath = QProcessEnvironment::systemEnvironment().value("PYTHON_PATH"); + if (!QFile::exists(_pythonPath + "/" + _pythonExe)) { + QMessageBox::critical(0, _pythonExe, QString("Python executable not found in ") + _pythonPath); + exit(-1); + } + + _pythonCommand = _pythonPath + "/" + _pythonExe; + } else { + QMessageBox::critical(0, "PYTHON_PATH not defined", + "Please set PYTHON_PATH to directory containing the Python executable"); + exit(-1); + } } QString PythonInterface::getPythonCommand() { From 6121d7022c302cc87223171e7c6de76629785a30 Mon Sep 17 00:00:00 2001 From: NissimHadar Date: Thu, 1 Nov 2018 07:49:36 -0700 Subject: [PATCH 107/286] Renamed folder. Removed useless 'mkdir'. --- tools/auto-tester/src/AWSInterface.cpp | 51 +++++++++++++++++--------- tools/auto-tester/src/AWSInterface.h | 4 +- tools/auto-tester/src/Test.cpp | 6 +-- 3 files changed, 39 insertions(+), 22 deletions(-) diff --git a/tools/auto-tester/src/AWSInterface.cpp b/tools/auto-tester/src/AWSInterface.cpp index 628db5329c..a5a72288de 100644 --- a/tools/auto-tester/src/AWSInterface.cpp +++ b/tools/auto-tester/src/AWSInterface.cpp @@ -22,11 +22,11 @@ AWSInterface::AWSInterface(QObject* parent) : QObject(parent) { } void AWSInterface::createWebPageFromResults(const QString& testResults, - const QString& workingDirectory, + const QString& snapshotDirectory, QCheckBox* updateAWSCheckBox, QLineEdit* urlLineEdit) { _testResults = testResults; - _workingDirectory = workingDirectory; + _snapshotDirectory = snapshotDirectory; _urlLineEdit = urlLineEdit; _urlLineEdit->setEnabled(false); @@ -44,14 +44,13 @@ void AWSInterface::extractTestFailuresFromZippedFolder() { // the folder will be called `TestResults--2018-10-02_16-54-11(9426)[DESKTOP-PMKNLSQ]` // and, this folder will be in the workign directory QStringList parts =_testResults.split('/'); - QString zipFolderName = _workingDirectory + "/" + parts[parts.length() - 1].split('.')[0]; + QString zipFolderName = _snapshotDirectory + "/" + parts[parts.length() - 1].split('.')[0]; if (QDir(zipFolderName).exists()) { QDir dir = zipFolderName; dir.removeRecursively(); } - QDir().mkdir(_workingDirectory); - JlCompress::extractDir(_testResults, _workingDirectory); + JlCompress::extractDir(_testResults, _snapshotDirectory); } void AWSInterface::createHTMLFile() { @@ -61,7 +60,7 @@ void AWSInterface::createHTMLFile() { QString filename = pathComponents[pathComponents.length() - 1]; _resultsFolder = filename.left(filename.length() - 4); - QString resultsPath = _workingDirectory + "/" + _resultsFolder + "/"; + QString resultsPath = _snapshotDirectory + "/" + _resultsFolder + "/"; QDir().mkdir(resultsPath); _htmlFilename = resultsPath + HTML_FILENAME; @@ -157,7 +156,7 @@ void AWSInterface::writeTable(QTextStream& stream) { // Note that failures are processed first, then successes QStringList originalNamesFailures; QStringList originalNamesSuccesses; - QDirIterator it1(_workingDirectory.toStdString().c_str()); + QDirIterator it1(_snapshotDirectory.toStdString().c_str()); while (it1.hasNext()) { QString nextDirectory = it1.next(); @@ -191,10 +190,10 @@ void AWSInterface::writeTable(QTextStream& stream) { newNamesSuccesses.append(originalNamesSuccesses[i].split("--tests.")[1]); } - _htmlFailuresFolder = _workingDirectory + "/" + _resultsFolder + "/" + FAILURES_FOLDER; + _htmlFailuresFolder = _snapshotDirectory + "/" + _resultsFolder + "/" + FAILURES_FOLDER; QDir().mkdir(_htmlFailuresFolder); - _htmlSuccessesFolder = _workingDirectory + "/" + _resultsFolder + "/" + SUCCESSES_FOLDER; + _htmlSuccessesFolder = _snapshotDirectory + "/" + _resultsFolder + "/" + SUCCESSES_FOLDER; QDir().mkdir(_htmlSuccessesFolder); for (int i = 0; i < newNamesFailures.length(); ++i) { @@ -321,7 +320,7 @@ void AWSInterface::createEntry(int index, const QString& testResult, QTextStream } void AWSInterface::updateAWS() { - QString filename = _workingDirectory + "/updateAWS.py"; + QString filename = _snapshotDirectory + "/updateAWS.py"; if (QFile::exists(filename)) { QFile::remove(filename); } @@ -352,14 +351,23 @@ void AWSInterface::updateAWS() { QStringList parts = nextDirectory.split('/'); QString filename = parts[parts.length() - 3] + "/" + parts[parts.length() - 2] + "/" + parts[parts.length() - 1]; - stream << "data = open('" << _workingDirectory << "/" << filename << "/" << "Actual Image.png" << "', 'rb')\n"; + stream << "data = open('" << _snapshotDirectory << "/" << filename << "/" + << "Actual Image.png" + << "', 'rb')\n"; + stream << "s3.Bucket('hifi-content').put_object(Bucket='" << AWS_BUCKET << "', Key='" << filename << "/" << "Actual Image.png" << "', Body=data)\n\n"; - stream << "data = open('" << _workingDirectory << "/" << filename << "/" << "Expected Image.png" << "', 'rb')\n"; + stream << "data = open('" << _snapshotDirectory << "/" << filename << "/" + << "Expected Image.png" + << "', 'rb')\n"; + stream << "s3.Bucket('hifi-content').put_object(Bucket='" << AWS_BUCKET << "', Key='" << filename << "/" << "Expected Image.png" << "', Body=data)\n\n"; if (QFile::exists(_htmlFailuresFolder + "/" + parts[parts.length() - 1] + "/Difference Image.png")) { - stream << "data = open('" << _workingDirectory << "/" << filename << "/" << "Difference Image.png" << "', 'rb')\n"; + stream << "data = open('" << _snapshotDirectory << "/" << filename << "/" + << "Difference Image.png" + << "', 'rb')\n"; + stream << "s3.Bucket('hifi-content').put_object(Bucket='" << AWS_BUCKET << "', Key='" << filename << "/" << "Difference Image.png" << "', Body=data)\n\n"; } } @@ -378,19 +386,28 @@ void AWSInterface::updateAWS() { QStringList parts = nextDirectory.split('/'); QString filename = parts[parts.length() - 3] + "/" + parts[parts.length() - 2] + "/" + parts[parts.length() - 1]; - stream << "data = open('" << _workingDirectory << "/" << filename << "/" << "Actual Image.png" << "', 'rb')\n"; + stream << "data = open('" << _snapshotDirectory << "/" << filename << "/" + << "Actual Image.png" + << "', 'rb')\n"; + stream << "s3.Bucket('hifi-content').put_object(Bucket='" << AWS_BUCKET << "', Key='" << filename << "/" << "Actual Image.png" << "', Body=data)\n\n"; - stream << "data = open('" << _workingDirectory << "/" << filename << "/" << "Expected Image.png" << "', 'rb')\n"; + stream << "data = open('" << _snapshotDirectory << "/" << filename << "/" + << "Expected Image.png" + << "', 'rb')\n"; + stream << "s3.Bucket('hifi-content').put_object(Bucket='" << AWS_BUCKET << "', Key='" << filename << "/" << "Expected Image.png" << "', Body=data)\n\n"; if (QFile::exists(_htmlSuccessesFolder + "/" + parts[parts.length() - 1] + "/Difference Image.png")) { - stream << "data = open('" << _workingDirectory << "/" << filename << "/" << "Difference Image.png" << "', 'rb')\n"; + stream << "data = open('" << _snapshotDirectory << "/" << filename << "/" + << "Difference Image.png" + << "', 'rb')\n"; + stream << "s3.Bucket('hifi-content').put_object(Bucket='" << AWS_BUCKET << "', Key='" << filename << "/" << "Difference Image.png" << "', Body=data)\n\n"; } } - stream << "data = open('" << _workingDirectory << "/" << _resultsFolder << "/" << HTML_FILENAME << "', 'rb')\n"; + stream << "data = open('" << _snapshotDirectory << "/" << _resultsFolder << "/" << HTML_FILENAME << "', 'rb')\n"; stream << "s3.Bucket('hifi-content').put_object(Bucket='" << AWS_BUCKET << "', Key='" << _resultsFolder << "/" << HTML_FILENAME << "', Body=data, ContentType='text/html')\n"; diff --git a/tools/auto-tester/src/AWSInterface.h b/tools/auto-tester/src/AWSInterface.h index c5be5f35bb..43299bd888 100644 --- a/tools/auto-tester/src/AWSInterface.h +++ b/tools/auto-tester/src/AWSInterface.h @@ -26,7 +26,7 @@ public: explicit AWSInterface(QObject* parent = 0); void createWebPageFromResults(const QString& testResults, - const QString& workingDirectory, + const QString& testDirectory, QCheckBox* updateAWSCheckBox, QLineEdit* urlLineEdit); @@ -49,7 +49,7 @@ public: private: QString _testResults; - QString _workingDirectory; + QString _snapshotDirectory; QString _resultsFolder; QString _htmlFailuresFolder; QString _htmlSuccessesFolder; diff --git a/tools/auto-tester/src/Test.cpp b/tools/auto-tester/src/Test.cpp index 582f6209af..2c262bec1e 100644 --- a/tools/auto-tester/src/Test.cpp +++ b/tools/auto-tester/src/Test.cpp @@ -1052,11 +1052,11 @@ void Test::createWebPage(QCheckBox* updateAWSCheckBox, QLineEdit* urlLineEdit) { return; } - QString tempDirectory = QFileDialog::getExistingDirectory(nullptr, "Please select a folder to store temporary files in", + QString snapshotDirectory = QFileDialog::getExistingDirectory(nullptr, "Please select a folder to store temporary files in", nullptr, QFileDialog::ShowDirsOnly); - if (tempDirectory.isNull()) { + if (snapshotDirectory.isNull()) { return; } - _awsInterface.createWebPageFromResults(testResults, tempDirectory, updateAWSCheckBox, urlLineEdit); + _awsInterface.createWebPageFromResults(testResults, snapshotDirectory, updateAWSCheckBox, urlLineEdit); } \ No newline at end of file From 8e3f2d360170a96c01e92c69fe8fb8b3fe420bef Mon Sep 17 00:00:00 2001 From: NissimHadar Date: Thu, 1 Nov 2018 08:17:35 -0700 Subject: [PATCH 108/286] Minor clean-up. --- tools/auto-tester/src/AWSInterface.cpp | 2 +- tools/auto-tester/src/AWSInterface.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/auto-tester/src/AWSInterface.cpp b/tools/auto-tester/src/AWSInterface.cpp index a5a72288de..b6eef39b1d 100644 --- a/tools/auto-tester/src/AWSInterface.cpp +++ b/tools/auto-tester/src/AWSInterface.cpp @@ -42,7 +42,7 @@ void AWSInterface::createWebPageFromResults(const QString& testResults, void AWSInterface::extractTestFailuresFromZippedFolder() { // For a test results zip file called `D:/tt/TestResults--2018-10-02_16-54-11(9426)[DESKTOP-PMKNLSQ].zip` // the folder will be called `TestResults--2018-10-02_16-54-11(9426)[DESKTOP-PMKNLSQ]` - // and, this folder will be in the workign directory + // and, this folder will be in the working directory QStringList parts =_testResults.split('/'); QString zipFolderName = _snapshotDirectory + "/" + parts[parts.length() - 1].split('.')[0]; if (QDir(zipFolderName).exists()) { diff --git a/tools/auto-tester/src/AWSInterface.h b/tools/auto-tester/src/AWSInterface.h index 43299bd888..f4084f1a14 100644 --- a/tools/auto-tester/src/AWSInterface.h +++ b/tools/auto-tester/src/AWSInterface.h @@ -26,7 +26,7 @@ public: explicit AWSInterface(QObject* parent = 0); void createWebPageFromResults(const QString& testResults, - const QString& testDirectory, + const QString& snapshotDirectory, QCheckBox* updateAWSCheckBox, QLineEdit* urlLineEdit); From 701f3274ce3ffe30efd0acc56f362c54cd593e09 Mon Sep 17 00:00:00 2001 From: NissimHadar Date: Thu, 1 Nov 2018 08:47:56 -0700 Subject: [PATCH 109/286] Updated IMPORTANT information for running Python3 on Mac. --- tools/auto-tester/README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tools/auto-tester/README.md b/tools/auto-tester/README.md index e029955edc..8851524ffd 100644 --- a/tools/auto-tester/README.md +++ b/tools/auto-tester/README.md @@ -14,7 +14,7 @@ Auto-tester has 5 functions, separated into 4 tabs: ## Installation ### Executable -1. Download the installer by browsing to [here](). +1. On Windows: download the installer by browsing to [here](). 2. Double click on the installer and install to a convenient location ![](./setup_7z.PNG) 3. To run the auto-tester, double click **auto-tester.exe**. @@ -26,7 +26,9 @@ Python 3 can be downloaded from: 2. Linux (source) (**Gzipped source tarball**) 3. Mac (**macOS 64-bit/32-bit installer** or **macOS 64-bit/32-bit installer**) -After installation - create an environment variable called PYTHON_PATH and set it to the folder containing the Python executable. +On Windows: after installation - create an environment variable called PYTHON_PATH and set it to the folder containing the Python executable. + +On Mac: after installation - run `open "/Applications/Python 3.6/Install Certificates.command"`. This is needed because the Mac Python supplied no longer links with the deprecated Apple-supplied system OpenSSL libraries but rather supplies a private copy of OpenSSL 1.0.2 which does not automatically access the system default root certificates. ### AWS interface #### Windows 1. Download the AWS CLI from `https://aws.amazon.com/cli/` From dcf20d2a15a674ec35b54e58e42eb1816b390689 Mon Sep 17 00:00:00 2001 From: Alexander Ivash Date: Fri, 26 Oct 2018 00:37:44 +0300 Subject: [PATCH 110/286] FB17381 - Reticle.setVisible isn't working --- scripts/system/controllers/controllerModules/mouseHMD.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/scripts/system/controllers/controllerModules/mouseHMD.js b/scripts/system/controllers/controllerModules/mouseHMD.js index 27fe82ca19..172923a8e2 100644 --- a/scripts/system/controllers/controllerModules/mouseHMD.js +++ b/scripts/system/controllers/controllerModules/mouseHMD.js @@ -29,6 +29,7 @@ function MouseHMD() { var _this = this; + this.hmdWasActive = HMD.active; this.mouseMoved = false; this.mouseActivity = new TimeLock(5000); this.handControllerActivity = new TimeLock(4000); @@ -102,6 +103,8 @@ this.isReady = function(controllerData, deltaTime) { var now = Date.now(); + var hmdChanged = this.hmdWasActive !== HMD.active; + this.hmdWasActive = HMD.active; this.triggersPressed(controllerData, now); if (HMD.active) { if (!this.mouseActivity.expired(now) && _this.handControllerActivity.expired()) { @@ -110,7 +113,7 @@ } else { Reticle.visible = false; } - } else if (!Reticle.visible) { + } else if (hmdChanged && !Reticle.visible) { Reticle.visible = true; } From 61ba685022e2d9764c6df00a4a5a41795f014330 Mon Sep 17 00:00:00 2001 From: NissimHadar Date: Thu, 1 Nov 2018 12:17:53 -0700 Subject: [PATCH 111/286] Set Python command to `usr/local/python3`. --- tools/auto-tester/src/PythonInterface.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/tools/auto-tester/src/PythonInterface.cpp b/tools/auto-tester/src/PythonInterface.cpp index c5878939cc..9832ac9f8d 100644 --- a/tools/auto-tester/src/PythonInterface.cpp +++ b/tools/auto-tester/src/PythonInterface.cpp @@ -14,6 +14,7 @@ #include PythonInterface::PythonInterface() { +#ifdef Q_OS_WIN if (QProcessEnvironment::systemEnvironment().contains("PYTHON_PATH")) { QString _pythonPath = QProcessEnvironment::systemEnvironment().value("PYTHON_PATH"); if (!QFile::exists(_pythonPath + "/" + _pythonExe)) { @@ -27,6 +28,14 @@ PythonInterface::PythonInterface() { "Please set PYTHON_PATH to directory containing the Python executable"); exit(-1); } +#elif defined Q_OS_MAC + _pythonCommand = "/usr/local/bin/python3"; + if (!QFile::exists(_pythonCommand)) { + QMessageBox::critical(0, "PYTHON_PATH not defined", + "python3 not found at " + _pythonCommand); + exit(-1); + } +#endif } QString PythonInterface::getPythonCommand() { From c1a01caeb8b97aef24ff1a4d3149ceff36395551 Mon Sep 17 00:00:00 2001 From: NissimHadar Date: Thu, 1 Nov 2018 12:18:55 -0700 Subject: [PATCH 112/286] Added missing `!` --- tools/auto-tester/src/TestRunner.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/auto-tester/src/TestRunner.cpp b/tools/auto-tester/src/TestRunner.cpp index 1f31004b2f..7d2d57b0bc 100644 --- a/tools/auto-tester/src/TestRunner.cpp +++ b/tools/auto-tester/src/TestRunner.cpp @@ -118,7 +118,7 @@ void TestRunner::setWorkingFolder() { } // This script installs High Fidelity. It is run as "yes | install_app.sh... so "yes" is killed at the end - script.write("#/bin/sh\n\n"); + script.write("#!/bin/sh\n\n"); script.write("VOLUME=`hdiutil attach \"$1\" | grep Volumes | awk '{print $3}'`\n"); script.write((QString("cp -rf \"$VOLUME/") + "/High Fidelity/interface.app\" \"" + _workingFolder + "/High_Fidelity/\"\n").toStdString().c_str()); @@ -137,7 +137,7 @@ void TestRunner::setWorkingFolder() { exit(-1); } - script.write("#/bin/sh\n\n"); + script.write("#!/bin/sh\n\n"); script.write("PROCESS=\"$1\"\n"); script.write("while (pgrep $PROCESS)\n"); script.write("do\n"); From 7d9b6a3f1052f523344527a37f50d2d56575bdf1 Mon Sep 17 00:00:00 2001 From: NissimHadar Date: Thu, 1 Nov 2018 12:19:25 -0700 Subject: [PATCH 113/286] Can read runs from TestRail. --- tools/auto-tester/src/TestRailInterface.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/tools/auto-tester/src/TestRailInterface.cpp b/tools/auto-tester/src/TestRailInterface.cpp index f943935539..912f72492c 100644 --- a/tools/auto-tester/src/TestRailInterface.cpp +++ b/tools/auto-tester/src/TestRailInterface.cpp @@ -1117,9 +1117,14 @@ void TestRailInterface::getRunsFromTestRail() { [=](int exitCode, QProcess::ExitStatus exitStatus) { updateRunsComboData(exitCode, exitStatus); }); connect(process, SIGNAL(finished(int)), process, SLOT(deleteLater())); +#ifdef Q_OS_WIN QStringList parameters = QStringList() << filename; - process->start(_pythonCommand, parameters); +#elif defined Q_OS_MAC + QStringList parameters = QStringList() << "-c" << _pythonCommand + " " + filename; + process->start("sh", parameters); + process->waitForFinished(); +#endif } void TestRailInterface::createTestRailRun(const QString& outputDirectory) { @@ -1164,4 +1169,4 @@ void TestRailInterface::extractTestFailuresFromZippedFolder(const QString& testR QDir dir = tempSubDirectory; dir.mkdir(tempSubDirectory); JlCompress::extractDir(testResults, tempSubDirectory); -} \ No newline at end of file +} From 6e8338fade6c7f131a23eed7c7d91a9daae9443f Mon Sep 17 00:00:00 2001 From: NissimHadar Date: Thu, 1 Nov 2018 12:55:28 -0700 Subject: [PATCH 114/286] Completed Python commands for Mac. --- tools/auto-tester/src/TestRailInterface.cpp | 26 ++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/tools/auto-tester/src/TestRailInterface.cpp b/tools/auto-tester/src/TestRailInterface.cpp index 912f72492c..a0c0d74526 100644 --- a/tools/auto-tester/src/TestRailInterface.cpp +++ b/tools/auto-tester/src/TestRailInterface.cpp @@ -357,8 +357,13 @@ void TestRailInterface::createAddTestCasesPythonScript(const QString& testDirect connect(process, static_cast(&QProcess::finished), this, [=](int exitCode, QProcess::ExitStatus exitStatus) { _busyWindow.hide(); }); +#ifdef Q_OS_WIN QStringList parameters = QStringList() << _outputDirectory + "/addTestCases.py"; process->start(_pythonCommand, parameters); +#elif defined Q_OS_MAC + QStringList parameters = QStringList() << "-c" << _pythonCommand + " " + _outputDirectory + "/addTestCases.py"; + process->start("sh", parameters); +#endif } } @@ -482,8 +487,13 @@ void TestRailInterface::addRun() { connect(process, static_cast(&QProcess::finished), this, [=](int exitCode, QProcess::ExitStatus exitStatus) { _busyWindow.hide(); }); +#ifdef Q_OS_WIN QStringList parameters = QStringList() << _outputDirectory + "/addRun.py"; process->start(_pythonCommand, parameters); +#elif defined Q_OS_MAC + QStringList parameters = QStringList() << "-c" << _pythonCommand + " " + _outputDirectory + "/addRun.py"; + process->start("sh", parameters); +#endif } } @@ -586,8 +596,13 @@ void TestRailInterface::updateRunWithResults() { connect(process, static_cast(&QProcess::finished), this, [=](int exitCode, QProcess::ExitStatus exitStatus) { _busyWindow.hide(); }); +#ifdef Q_OS_WIN QStringList parameters = QStringList() << _outputDirectory + "/updateRunWithResults.py"; process->start(_pythonCommand, parameters); +#elif defined Q_OS_MAC + QStringList parameters = QStringList() << "-c" << _pythonCommand + " " + _outputDirectory + "/updateRunWithResults.py"; + process->start("sh", parameters); +#endif } } @@ -759,8 +774,13 @@ void TestRailInterface::getReleasesFromTestRail() { [=](int exitCode, QProcess::ExitStatus exitStatus) { updateReleasesComboData(exitCode, exitStatus); }); connect(process, SIGNAL(finished(int)), process, SLOT(deleteLater())); +#ifdef Q_OS_WIN QStringList parameters = QStringList() << filename; process->start(_pythonCommand, parameters); +#elif defined Q_OS_MAC + QStringList parameters = QStringList() << "-c" << _pythonCommand + " " + filename; + process->start("sh", parameters); +#endif } void TestRailInterface::createTestSuitePython(const QString& testDirectory, @@ -1078,8 +1098,13 @@ void TestRailInterface::getTestSectionsFromTestRail() { [=](int exitCode, QProcess::ExitStatus exitStatus) { updateSectionsComboData(exitCode, exitStatus); }); connect(process, SIGNAL(finished(int)), process, SLOT(deleteLater())); +#ifdef Q_OS_WIN QStringList parameters = QStringList() << filename; process->start(_pythonCommand, parameters); +#elif defined Q_OS_MAC + QStringList parameters = QStringList() << "-c" << _pythonCommand + " " + filename; + process->start("sh", parameters); +#endif } void TestRailInterface::getRunsFromTestRail() { @@ -1123,7 +1148,6 @@ void TestRailInterface::getRunsFromTestRail() { #elif defined Q_OS_MAC QStringList parameters = QStringList() << "-c" << _pythonCommand + " " + filename; process->start("sh", parameters); - process->waitForFinished(); #endif } From 64d8fa6875ff402bc6a8a7bf8da62680e6d75b79 Mon Sep 17 00:00:00 2001 From: amantley Date: Thu, 1 Nov 2018 13:33:03 -0700 Subject: [PATCH 115/286] added the const argument to the AnimSkeleton constructor that allows the offset rotations to be passed in --- .../src/avatars/ScriptableAvatar.cpp | 3 +- libraries/animation/src/AnimClip.cpp | 3 +- libraries/animation/src/AnimSkeleton.cpp | 43 +++++++++++-------- libraries/animation/src/AnimSkeleton.h | 6 +-- libraries/animation/src/Rig.cpp | 4 +- tools/skeleton-dump/src/SkeletonDumpApp.cpp | 3 +- 6 files changed, 36 insertions(+), 26 deletions(-) diff --git a/assignment-client/src/avatars/ScriptableAvatar.cpp b/assignment-client/src/avatars/ScriptableAvatar.cpp index 7d2b267a05..da151c7a35 100644 --- a/assignment-client/src/avatars/ScriptableAvatar.cpp +++ b/assignment-client/src/avatars/ScriptableAvatar.cpp @@ -84,7 +84,8 @@ void ScriptableAvatar::update(float deltatime) { // Run animation if (_animation && _animation->isLoaded() && _animation->getFrames().size() > 0 && !_bind.isNull() && _bind->isLoaded()) { if (!_animSkeleton) { - _animSkeleton = std::make_shared(_bind->getGeometry()); + QMap jointRotationOffsets; + _animSkeleton = std::make_shared(_bind->getGeometry(), jointRotationOffsets); } float currentFrame = _animationDetails.currentFrame + deltatime * _animationDetails.fps; if (_animationDetails.loop || currentFrame < _animationDetails.lastFrame) { diff --git a/libraries/animation/src/AnimClip.cpp b/libraries/animation/src/AnimClip.cpp index f9195a608b..ffbbf1b0f4 100644 --- a/libraries/animation/src/AnimClip.cpp +++ b/libraries/animation/src/AnimClip.cpp @@ -102,7 +102,8 @@ void AnimClip::copyFromNetworkAnim() { // build a mapping from animation joint indices to skeleton joint indices. // by matching joints with the same name. const FBXGeometry& geom = _networkAnim->getGeometry(); - AnimSkeleton animSkeleton(geom); + QMap jointRotationOffsets; + AnimSkeleton animSkeleton(geom, jointRotationOffsets); const auto animJointCount = animSkeleton.getNumJoints(); const auto skeletonJointCount = _skeleton->getNumJoints(); std::vector jointMap; diff --git a/libraries/animation/src/AnimSkeleton.cpp b/libraries/animation/src/AnimSkeleton.cpp index bd8d62bdb4..23e4b7a9d6 100644 --- a/libraries/animation/src/AnimSkeleton.cpp +++ b/libraries/animation/src/AnimSkeleton.cpp @@ -16,21 +16,20 @@ #include "AnimationLogging.h" -AnimSkeleton::AnimSkeleton(const FBXGeometry& fbxGeometry) { +AnimSkeleton::AnimSkeleton(const FBXGeometry& fbxGeometry, const QMap jointOffsets) { // convert to std::vector of joints std::vector joints; joints.reserve(fbxGeometry.joints.size()); - _avatarTPoseOffsets.reserve(fbxGeometry.joints.size()); - AnimPose identity(glm::quat(), glm::vec3()); + //_avatarTPoseOffsets.reserve(_jointsSize); for (auto& joint : fbxGeometry.joints) { joints.push_back(joint); - _avatarTPoseOffsets.push_back(identity); + //_avatarTPoseOffsets.push_back(AnimPose(glm::quat(), glm::vec3())); } - buildSkeletonFromJoints(joints); - /* - //add offsets for spine2 and the neck - _avatarTPoseOffsets[nameToJointIndex("Spine2")] = AnimPose(glm::quat(-0.707107f, 0.0f, 0.0f, 0.707107f), glm::vec3()); - _avatarTPoseOffsets[nameToJointIndex("Neck")] = AnimPose(glm::quat(0.0f, 0.707107f, 0.0f, 0.707107f), glm::vec3()); + + + // add offsets for spine2 and the neck + // _avatarTPoseOffsets[nameToJointIndex("Spine2")] = AnimPose(glm::quat(-0.707107f, 0.0f, 0.0f, 0.707107f), glm::vec3()); + // _avatarTPoseOffsets[nameToJointIndex("Neck")] = AnimPose(glm::quat(0.0f, 0.707107f, 0.0f, 0.707107f), glm::vec3()); for (int i = 0; i < (int)fbxGeometry.meshes.size(); i++) { const FBXMesh& mesh = fbxGeometry.meshes.at(i); @@ -40,16 +39,19 @@ AnimSkeleton::AnimSkeleton(const FBXGeometry& fbxGeometry) { // AJT: mutate bind pose! this allows us to oreint the skeleton back into the authored orientaiton before // rendering, with no runtime overhead. // this works if clusters match joints one for one. - //cluster.inverseBindMatrix = (glm::mat4)_avatarTPoseOffsets[cluster.jointIndex].inverse() * cluster.inverseBindMatrix; - cluster.inverseBindTransform.evalFromRawMatrix(cluster.inverseBindMatrix); + if (jointOffsets.contains(j)) { + AnimPose localOffset(jointOffsets[j], glm::vec3()); + cluster.inverseBindMatrix = (glm::mat4)localOffset.inverse() * cluster.inverseBindMatrix; + cluster.inverseBindTransform.evalFromRawMatrix(cluster.inverseBindMatrix); + } } } - */ + buildSkeletonFromJoints(joints, jointOffsets); } -AnimSkeleton::AnimSkeleton(const std::vector& joints) { - buildSkeletonFromJoints(joints); +AnimSkeleton::AnimSkeleton(const std::vector& joints, const QMap jointOffsets) { + buildSkeletonFromJoints(joints, jointOffsets); } int AnimSkeleton::nameToJointIndex(const QString& jointName) const { @@ -188,7 +190,7 @@ void AnimSkeleton::mirrorAbsolutePoses(AnimPoseVec& poses) const { } } -void AnimSkeleton::buildSkeletonFromJoints(const std::vector& joints) { +void AnimSkeleton::buildSkeletonFromJoints(const std::vector& joints, const QMap jointOffsets) { _joints = joints; _jointsSize = (int)joints.size(); // build a cache of bind poses @@ -218,9 +220,14 @@ void AnimSkeleton::buildSkeletonFromJoints(const std::vector& joints) // remember the inverse bind pose already has the offset added into it. the total effect is offset^-1 * relDefPose * offset. // this gives us the correct transform for the joint that has been put in t-pose with an offset rotation. //relDefaultPose = relDefaultPose * _avatarTPoseOffsets[i]; - //if (parentIndex >= 0) { - // relDefaultPose = _avatarTPoseOffsets[parentIndex].inverse() * AnimPose(glm::quat(), relDefaultPose.trans()) * _avatarTPoseOffsets[parentIndex] * AnimPose(relDefaultPose.rot(), glm::vec3()); - //} + if (jointOffsets.contains(i)) { + AnimPose localOffset(jointOffsets[i], glm::vec3()); + relDefaultPose = relDefaultPose * localOffset; + if ((parentIndex >= 0) && jointOffsets.contains(parentIndex)) { + AnimPose localParentOffset(jointOffsets[parentIndex], glm::vec3()); + relDefaultPose = localParentOffset.inverse() * AnimPose(glm::quat(), relDefaultPose.trans()) * localParentOffset * AnimPose(relDefaultPose.rot(), glm::vec3()); + } + } _relativeDefaultPoses.push_back(relDefaultPose); diff --git a/libraries/animation/src/AnimSkeleton.h b/libraries/animation/src/AnimSkeleton.h index ad1120fcbf..0a5b177b63 100644 --- a/libraries/animation/src/AnimSkeleton.h +++ b/libraries/animation/src/AnimSkeleton.h @@ -23,8 +23,8 @@ public: using Pointer = std::shared_ptr; using ConstPointer = std::shared_ptr; - explicit AnimSkeleton(const FBXGeometry& fbxGeometry); - explicit AnimSkeleton(const std::vector& joints); + explicit AnimSkeleton(const FBXGeometry& fbxGeometry, const QMap jointOffsets); + explicit AnimSkeleton(const std::vector& joints, const QMap jointOffsets); int nameToJointIndex(const QString& jointName) const; const QString& getJointName(int jointIndex) const; int getNumJoints() const; @@ -64,7 +64,7 @@ public: std::vector lookUpJointIndices(const std::vector& jointNames) const; protected: - void buildSkeletonFromJoints(const std::vector& joints); + void buildSkeletonFromJoints(const std::vector& joints, const QMap jointOffsets); std::vector _joints; AnimPoseVec _avatarTPoseOffsets; diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index c58a96aa84..c7e5226c7b 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -267,7 +267,7 @@ void Rig::initJointStates(const FBXGeometry& geometry, const glm::mat4& modelOff _rigToGeometryTransform = glm::inverse(_geometryToRigTransform); setModelOffset(modelOffset); - _animSkeleton = std::make_shared(geometry); + _animSkeleton = std::make_shared(geometry,_jointRotationOffsets); _internalPoseSet._relativePoses.clear(); _internalPoseSet._relativePoses = _animSkeleton->getRelativeDefaultPoses(); @@ -310,7 +310,7 @@ void Rig::initJointStates(const FBXGeometry& geometry, const glm::mat4& modelOff void Rig::reset(const FBXGeometry& geometry) { _geometryOffset = AnimPose(geometry.offset); _invGeometryOffset = _geometryOffset.inverse(); - _animSkeleton = std::make_shared(geometry); + _animSkeleton = std::make_shared(geometry, _jointRotationOffsets); _internalPoseSet._relativePoses.clear(); _internalPoseSet._relativePoses = _animSkeleton->getRelativeDefaultPoses(); diff --git a/tools/skeleton-dump/src/SkeletonDumpApp.cpp b/tools/skeleton-dump/src/SkeletonDumpApp.cpp index e9d8243e38..cb623fd53e 100644 --- a/tools/skeleton-dump/src/SkeletonDumpApp.cpp +++ b/tools/skeleton-dump/src/SkeletonDumpApp.cpp @@ -55,7 +55,8 @@ SkeletonDumpApp::SkeletonDumpApp(int argc, char* argv[]) : QCoreApplication(argc } QByteArray blob = file.readAll(); std::unique_ptr fbxGeometry(readFBX(blob, QVariantHash())); - std::unique_ptr skeleton(new AnimSkeleton(*fbxGeometry)); + QMap jointRotationOffsets; + std::unique_ptr skeleton(new AnimSkeleton(*fbxGeometry, jointRotationOffsets)); skeleton->dump(verbose); } From 22ae1114b5cfc7f071bd9793e3783831618fa4cf Mon Sep 17 00:00:00 2001 From: NissimHadar Date: Thu, 1 Nov 2018 14:20:05 -0700 Subject: [PATCH 116/286] Updated Python command for Mac. --- tools/auto-tester/src/AWSInterface.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tools/auto-tester/src/AWSInterface.cpp b/tools/auto-tester/src/AWSInterface.cpp index b6eef39b1d..e43ef8dc75 100644 --- a/tools/auto-tester/src/AWSInterface.cpp +++ b/tools/auto-tester/src/AWSInterface.cpp @@ -425,6 +425,11 @@ void AWSInterface::updateAWS() { connect(process, static_cast(&QProcess::finished), this, [=](int exitCode, QProcess::ExitStatus exitStatus) { _busyWindow.hide(); }); +#ifdef Q_OS_WIN QStringList parameters = QStringList() << filename ; process->start(_pythonCommand, parameters); +#elif defined Q_OS_MAC + QStringList parameters = QStringList() << "-c" << _pythonCommand + " " + filename; + process->start("sh", parameters); +#endif } From ebc37610d3f450675bc7fc7eaf48ac1aed11d36e Mon Sep 17 00:00:00 2001 From: NissimHadar Date: Thu, 1 Nov 2018 15:07:19 -0700 Subject: [PATCH 117/286] Updated for MAC. --- tools/auto-tester/README.md | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/tools/auto-tester/README.md b/tools/auto-tester/README.md index 8851524ffd..0bec83d703 100644 --- a/tools/auto-tester/README.md +++ b/tools/auto-tester/README.md @@ -26,9 +26,13 @@ Python 3 can be downloaded from: 2. Linux (source) (**Gzipped source tarball**) 3. Mac (**macOS 64-bit/32-bit installer** or **macOS 64-bit/32-bit installer**) -On Windows: after installation - create an environment variable called PYTHON_PATH and set it to the folder containing the Python executable. +#### Windows +After installation - create an environment variable called PYTHON_PATH and set it to the folder containing the Python executable. + +#### Mac +After installation - run `open "/Applications/Python 3.6/Install Certificates.command"`. This is needed because the Mac Python supplied no longer links with the deprecated Apple-supplied system OpenSSL libraries but rather supplies a private copy of OpenSSL 1.0.2 which does not automatically access the system default root certificates. +Verify that `/usr/local/bin/python3` exists. -On Mac: after installation - run `open "/Applications/Python 3.6/Install Certificates.command"`. This is needed because the Mac Python supplied no longer links with the deprecated Apple-supplied system OpenSSL libraries but rather supplies a private copy of OpenSSL 1.0.2 which does not automatically access the system default root certificates. ### AWS interface #### Windows 1. Download the AWS CLI from `https://aws.amazon.com/cli/` @@ -38,8 +42,23 @@ On Mac: after installation - run `open "/Applications/Python 3.6/Install Certifi 1. Enter the secret key 1. Leave region name and ouput format as default [None] -1. Install the latest release of Boto3 via pip: ->pip install boto3 +1. Install the latest release of Boto3 via pip: +pip install boto3 +#### Mac +1. Install pip with the script provided by the Python Packaging Authority: +$ curl -O https://bootstrap.pypa.io/get-pip.py +$ python3 get-pip.py --user + +1. Use pip to install the AWS CLI. +$ pip3 install awscli --upgrade --user +This will install aws in your user. For user XXX, aws will be located in /Users/XXX/Library/Python/3.7/bin +1. Open a new command prompt and run `aws configure` +1. Enter the AWS account number +1. Enter the secret key +1. Leave region name and ouput format as default [None] +1. Install the latest release of Boto3 via pip: +pip3 install boto3 + # Create ![](./Create.PNG) From d851720cb01cc63566e1d741aaf52f49f0b8299c Mon Sep 17 00:00:00 2001 From: NissimHadar Date: Thu, 1 Nov 2018 16:57:28 -0700 Subject: [PATCH 118/286] Added method to detect OS type. Needed for testing. --- interface/src/scripting/TestScriptingInterface.cpp | 10 ++++++++++ interface/src/scripting/TestScriptingInterface.h | 6 ++++++ 2 files changed, 16 insertions(+) diff --git a/interface/src/scripting/TestScriptingInterface.cpp b/interface/src/scripting/TestScriptingInterface.cpp index 52f6a3ebc0..36846a1fef 100644 --- a/interface/src/scripting/TestScriptingInterface.cpp +++ b/interface/src/scripting/TestScriptingInterface.cpp @@ -198,4 +198,14 @@ void TestScriptingInterface::setOtherAvatarsReplicaCount(int count) { int TestScriptingInterface::getOtherAvatarsReplicaCount() { return qApp->getOtherAvatarsReplicaCount(); +} + +QString TestScriptingInterface::getOperatingSystemType() { +#ifdef Q_OS_WIN + return "WINDOWS"; +#elif #defined Q_MAC_OS + return "MACOS"; +#else + return "UNKNOWN"; +#endif } \ No newline at end of file diff --git a/interface/src/scripting/TestScriptingInterface.h b/interface/src/scripting/TestScriptingInterface.h index 4a1d1a3eeb..a40826bd25 100644 --- a/interface/src/scripting/TestScriptingInterface.h +++ b/interface/src/scripting/TestScriptingInterface.h @@ -163,6 +163,12 @@ public slots: */ Q_INVOKABLE int getOtherAvatarsReplicaCount(); + /**jsdoc + * Returns the Operating Sytem type + * @function Test.getOperatingSystemType + */ + QString getOperatingSystemType(); + private: bool waitForCondition(qint64 maxWaitMs, std::function condition); QString _testResultsLocation; From 470a4904948d16fba5a6944a0045aef7c252d722 Mon Sep 17 00:00:00 2001 From: NissimHadar Date: Thu, 1 Nov 2018 17:31:10 -0700 Subject: [PATCH 119/286] Typo. --- interface/src/scripting/TestScriptingInterface.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/src/scripting/TestScriptingInterface.cpp b/interface/src/scripting/TestScriptingInterface.cpp index 36846a1fef..a31e43d6c6 100644 --- a/interface/src/scripting/TestScriptingInterface.cpp +++ b/interface/src/scripting/TestScriptingInterface.cpp @@ -203,9 +203,9 @@ int TestScriptingInterface::getOtherAvatarsReplicaCount() { QString TestScriptingInterface::getOperatingSystemType() { #ifdef Q_OS_WIN return "WINDOWS"; -#elif #defined Q_MAC_OS +#elif defined Q_MAC_OS return "MACOS"; #else return "UNKNOWN"; #endif -} \ No newline at end of file +} From cd2194ad539467285e4b800c0f9863a6e75adbef Mon Sep 17 00:00:00 2001 From: NissimHadar Date: Thu, 1 Nov 2018 17:56:45 -0700 Subject: [PATCH 120/286] Correction for Linux --- tools/auto-tester/src/TestRunner.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/auto-tester/src/TestRunner.cpp b/tools/auto-tester/src/TestRunner.cpp index 7d2d57b0bc..906cb3ab06 100644 --- a/tools/auto-tester/src/TestRunner.cpp +++ b/tools/auto-tester/src/TestRunner.cpp @@ -229,11 +229,11 @@ void TestRunner::runInstaller() { QString installerFullPath = _workingFolder + "/" + _installerFilename; + QString commandLine; #ifdef Q_OS_WIN - QString commandLine = - "\"" + QDir::toNativeSeparators(installerFullPath) + "\"" + " /S /D=" + QDir::toNativeSeparators(_installationFolder); + commandLine = "\"" + QDir::toNativeSeparators(installerFullPath) + "\"" + " /S /D=" + QDir::toNativeSeparators(_installationFolder); #elif defined Q_OS_MAC - QString commandLine = "yes | " + _workingFolder + "/install_app.sh " + _workingFolder + "/HighFidelity-Beta-latest-dev.dmg"; + commandLine = "yes | " + _workingFolder + "/install_app.sh " + _workingFolder + "/HighFidelity-Beta-latest-dev.dmg"; #endif installerWorker->setCommandLine(commandLine); From 58bac436babc728a45b7a75a35c2c8143c4522fe Mon Sep 17 00:00:00 2001 From: NissimHadar Date: Thu, 1 Nov 2018 18:14:29 -0700 Subject: [PATCH 121/286] Fixed Linux error --- tools/auto-tester/src/TestRunner.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tools/auto-tester/src/TestRunner.cpp b/tools/auto-tester/src/TestRunner.cpp index 906cb3ab06..b2ef00647f 100644 --- a/tools/auto-tester/src/TestRunner.cpp +++ b/tools/auto-tester/src/TestRunner.cpp @@ -405,7 +405,7 @@ void TestRunner::runInterfaceWithTestScript() { #ifdef Q_OS_WIN url = "hifi://localhost"; #elif defined Q_OS_MAC - // TODO: Find out Mac equivalent of AppData, then this win't be needed + // TODO: Find out Mac equivalent of AppData, then this won't be needed url = "hifi://localhost/9999,9999,9999"; #endif } @@ -413,12 +413,13 @@ void TestRunner::runInterfaceWithTestScript() { QString testScript = QString("https://raw.githubusercontent.com/") + _user + "/hifi_tests/" + _branch + "/tests/testRecursive.js"; + QString commandLine; #ifdef Q_OS_WIN QString exeFile = QString("\"") + QDir::toNativeSeparators(_installationFolder) + "\\interface.exe\""; - QString commandLine = exeFile + " --url " + url + " --no-updater" + " --testScript " + testScript + + commandLine = exeFile + " --url " + url + " --no-updater" + " --testScript " + testScript + " quitWhenFinished --testResultsLocation " + _snapshotFolder; #elif defined Q_OS_MAC - QString commandLine = "open \"" +_installationFolder + "/interface.app\" --args" + + commandLine = "open \"" +_installationFolder + "/interface.app\" --args" + " --url " + url + " --no-updater" + " --testScript " + testScript + " quitWhenFinished" + From c7353e6c590c033e57c13ff8d49cbd992d9357f2 Mon Sep 17 00:00:00 2001 From: NissimHadar Date: Fri, 2 Nov 2018 09:57:58 -0700 Subject: [PATCH 122/286] Added `setWindowSize` - FOR TESTING ON MAC --- interface/src/scripting/TestScriptingInterface.cpp | 4 ++++ interface/src/scripting/TestScriptingInterface.h | 7 +++++++ 2 files changed, 11 insertions(+) diff --git a/interface/src/scripting/TestScriptingInterface.cpp b/interface/src/scripting/TestScriptingInterface.cpp index a31e43d6c6..968c5b8001 100644 --- a/interface/src/scripting/TestScriptingInterface.cpp +++ b/interface/src/scripting/TestScriptingInterface.cpp @@ -209,3 +209,7 @@ QString TestScriptingInterface::getOperatingSystemType() { return "UNKNOWN"; #endif } + +void TestScriptingInterface::setWindowSize(int width, int height) { + qApp->getWindow()->resize(width, height); +} \ No newline at end of file diff --git a/interface/src/scripting/TestScriptingInterface.h b/interface/src/scripting/TestScriptingInterface.h index a40826bd25..8dbfded39c 100644 --- a/interface/src/scripting/TestScriptingInterface.h +++ b/interface/src/scripting/TestScriptingInterface.h @@ -166,9 +166,16 @@ public slots: /**jsdoc * Returns the Operating Sytem type * @function Test.getOperatingSystemType + * @returns {string} "WINDOWS", "MACOS" or "UNKNOWN" */ QString getOperatingSystemType(); + /**jsdoc + * Sets the size of the Window on desktop + * @function Test.getOperatingSystemType + */ + void setWindowSize(int width, int height); + private: bool waitForCondition(qint64 maxWaitMs, std::function condition); QString _testResultsLocation; From 70c0addc3f7c306e5fbd36485348291ca173612b Mon Sep 17 00:00:00 2001 From: NissimHadar Date: Fri, 2 Nov 2018 10:47:17 -0700 Subject: [PATCH 123/286] Removed `setWindowSize` - crashes on both Windows and Mac. Fixed typo. --- interface/src/scripting/TestScriptingInterface.cpp | 6 +----- interface/src/scripting/TestScriptingInterface.h | 6 ------ 2 files changed, 1 insertion(+), 11 deletions(-) diff --git a/interface/src/scripting/TestScriptingInterface.cpp b/interface/src/scripting/TestScriptingInterface.cpp index 968c5b8001..a9ba165037 100644 --- a/interface/src/scripting/TestScriptingInterface.cpp +++ b/interface/src/scripting/TestScriptingInterface.cpp @@ -203,13 +203,9 @@ int TestScriptingInterface::getOtherAvatarsReplicaCount() { QString TestScriptingInterface::getOperatingSystemType() { #ifdef Q_OS_WIN return "WINDOWS"; -#elif defined Q_MAC_OS +#elif defined Q_OS_MAC return "MACOS"; #else return "UNKNOWN"; #endif } - -void TestScriptingInterface::setWindowSize(int width, int height) { - qApp->getWindow()->resize(width, height); -} \ No newline at end of file diff --git a/interface/src/scripting/TestScriptingInterface.h b/interface/src/scripting/TestScriptingInterface.h index 8dbfded39c..26e967c9b5 100644 --- a/interface/src/scripting/TestScriptingInterface.h +++ b/interface/src/scripting/TestScriptingInterface.h @@ -170,12 +170,6 @@ public slots: */ QString getOperatingSystemType(); - /**jsdoc - * Sets the size of the Window on desktop - * @function Test.getOperatingSystemType - */ - void setWindowSize(int width, int height); - private: bool waitForCondition(qint64 maxWaitMs, std::function condition); QString _testResultsLocation; From 51272d8fec68d8831f4c591ddee328fa24405ed7 Mon Sep 17 00:00:00 2001 From: NissimHadar Date: Fri, 2 Nov 2018 13:09:07 -0700 Subject: [PATCH 124/286] Added setGeometry script command. --- interface/src/Application.cpp | 12 ++++++++++++ interface/src/Application.h | 6 ++++++ interface/src/scripting/TestScriptingInterface.cpp | 4 ++++ interface/src/scripting/TestScriptingInterface.h | 10 ++++++++++ 4 files changed, 32 insertions(+) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 7cd91d76a0..30b5e3ed5f 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -3636,6 +3636,12 @@ void Application::onPresent(quint32 frameCount) { if (_renderEventHandler && !isAboutToQuit() && _pendingRenderEvent.compare_exchange_strong(expected, true)) { postEvent(_renderEventHandler, new QEvent((QEvent::Type)ApplicationEvent::Render)); } + + // This is done here so it won't be during a resize/move event + if (_setGeometryRequested) { + _setGeometryRequested = false; + _window->setGeometry(requestedGeometry); + } } static inline bool isKeyEvent(QEvent::Type type) { @@ -8578,6 +8584,12 @@ void Application::copyToClipboard(const QString& text) { QApplication::clipboard()->setText(text); } +void Application::setGeometry(int x, int y, int width, int height) { + // Note that calling setGeometry inside resizeEvent() or moveEvent() can cause infinite recursion + requestedGeometry = QRect(x, y, width, height); + _setGeometryRequested = true; +} + #if defined(Q_OS_ANDROID) void Application::beforeEnterBackground() { auto nodeList = DependencyManager::get(); diff --git a/interface/src/Application.h b/interface/src/Application.h index 14e30b8006..cd5d62c106 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -315,6 +315,8 @@ public: Q_INVOKABLE void copyToClipboard(const QString& text); + void setGeometry(int x, int y, int width, int height); + int getOtherAvatarsReplicaCount() { return DependencyManager::get()->getReplicaCount(); } void setOtherAvatarsReplicaCount(int count) { DependencyManager::get()->setReplicaCount(count); } @@ -784,5 +786,9 @@ private: bool _showTrackedObjects { false }; bool _prevShowTrackedObjects { false }; + + // Data for the setGeometry script command + bool _setGeometryRequested{ false }; + QRect requestedGeometry; }; #endif // hifi_Application_h diff --git a/interface/src/scripting/TestScriptingInterface.cpp b/interface/src/scripting/TestScriptingInterface.cpp index a9ba165037..74b03faf83 100644 --- a/interface/src/scripting/TestScriptingInterface.cpp +++ b/interface/src/scripting/TestScriptingInterface.cpp @@ -209,3 +209,7 @@ QString TestScriptingInterface::getOperatingSystemType() { return "UNKNOWN"; #endif } + +void TestScriptingInterface::setGeometry(int x, int y, int width, int height) { + qApp->setGeometry(x, y, width, height); +} \ No newline at end of file diff --git a/interface/src/scripting/TestScriptingInterface.h b/interface/src/scripting/TestScriptingInterface.h index 26e967c9b5..6c2c542f33 100644 --- a/interface/src/scripting/TestScriptingInterface.h +++ b/interface/src/scripting/TestScriptingInterface.h @@ -170,6 +170,16 @@ public slots: */ QString getOperatingSystemType(); + /**jsdoc + * Sets the position and size of the window on desktop. All units are pixels + * @function Test.setGeometry + * @param {int} x - Distance of window from left edge + * @param {int} y - Distance of window from top edge + * @param {int} width - Width of window + * @param {int} height - Height of window + */ + void setGeometry(int x, int y, int width, int height); + private: bool waitForCondition(qint64 maxWaitMs, std::function condition); QString _testResultsLocation; From 562c9b11964dccfb53712653591ef6a59a869630 Mon Sep 17 00:00:00 2001 From: NissimHadar Date: Fri, 2 Nov 2018 13:35:38 -0700 Subject: [PATCH 125/286] "works" on Mac --- interface/src/Application.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 30b5e3ed5f..d5d99a34b5 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -3636,12 +3636,6 @@ void Application::onPresent(quint32 frameCount) { if (_renderEventHandler && !isAboutToQuit() && _pendingRenderEvent.compare_exchange_strong(expected, true)) { postEvent(_renderEventHandler, new QEvent((QEvent::Type)ApplicationEvent::Render)); } - - // This is done here so it won't be during a resize/move event - if (_setGeometryRequested) { - _setGeometryRequested = false; - _window->setGeometry(requestedGeometry); - } } static inline bool isKeyEvent(QEvent::Type type) { @@ -4810,6 +4804,12 @@ void Application::idle() { } } + // This is done here so it won't be during a resize/move event + if (_setGeometryRequested) { + _setGeometryRequested = false; + _window->setGeometry(requestedGeometry); + } + _overlayConductor.update(secondsSinceLastUpdate); _gameLoopCounter.increment(); From 5392d21b1c9def68a0de2f52029276e8239b3b1b Mon Sep 17 00:00:00 2001 From: amantley Date: Fri, 2 Nov 2018 17:59:18 -0700 Subject: [PATCH 126/286] loading test file to check rotation offsets --- libraries/animation/src/AnimSkeleton.cpp | 7 +++++++ libraries/animation/src/Rig.cpp | 9 +++++++++ 2 files changed, 16 insertions(+) diff --git a/libraries/animation/src/AnimSkeleton.cpp b/libraries/animation/src/AnimSkeleton.cpp index 23e4b7a9d6..ab914b1da3 100644 --- a/libraries/animation/src/AnimSkeleton.cpp +++ b/libraries/animation/src/AnimSkeleton.cpp @@ -17,6 +17,8 @@ #include "AnimationLogging.h" AnimSkeleton::AnimSkeleton(const FBXGeometry& fbxGeometry, const QMap jointOffsets) { + + qCDebug(animation) << "in the animSkeleton"; // convert to std::vector of joints std::vector joints; joints.reserve(fbxGeometry.joints.size()); @@ -34,12 +36,17 @@ AnimSkeleton::AnimSkeleton(const FBXGeometry& fbxGeometry, const QMap(mesh.clusters.at(j)); + if ((cluster.jointIndex == nameToJointIndex("Neck")) || (cluster.jointIndex == nameToJointIndex("Spine2"))) { + qCDebug(animation) << "found a joint offset to add " << cluster.jointIndex << " " << jointOffsets[j]; + } // AJT: mutate bind pose! this allows us to oreint the skeleton back into the authored orientaiton before // rendering, with no runtime overhead. // this works if clusters match joints one for one. if (jointOffsets.contains(j)) { + qCDebug(animation) << "found a joint offset to add " << j << " " << jointOffsets[j]; AnimPose localOffset(jointOffsets[j], glm::vec3()); cluster.inverseBindMatrix = (glm::mat4)localOffset.inverse() * cluster.inverseBindMatrix; cluster.inverseBindTransform.evalFromRawMatrix(cluster.inverseBindMatrix); diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index c7e5226c7b..f1cb94a99c 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -2074,4 +2074,13 @@ void Rig::setJointRotationOffsets(const QMap& offsets) { } qDebug() << "Joint Rotation Offset added to Rig._jointRotationOffsets : " << " jointName: " << jointName << " jointIndex: " << jointIndex << " rotation offset: " << rotationOffset; } + int neckIndex = indexOfJoint("Neck"); + int spine2Index = indexOfJoint("Spine2"); + if (neckIndex != -1) { + _jointRotationOffsets.insert(neckIndex, glm::quat(0.7071f, 0.0f, 0.7071f, 0.0f)); + } + if (spine2Index != -1) { + _jointRotationOffsets.insert(spine2Index, glm::quat(0.7071f, -0.7071f, 0.0f, 0.0f)); + } + qCDebug(animation) << "set the neck and spine2 offsets"; } \ No newline at end of file From d231a35dfacfccbdcf25cb8050685c7df6064ee6 Mon Sep 17 00:00:00 2001 From: amantley Date: Mon, 5 Nov 2018 08:35:01 -0800 Subject: [PATCH 127/286] changes to get spine2 and neck to work almost there --- libraries/animation/src/AnimSkeleton.cpp | 38 +++++++++++++++---- libraries/animation/src/Rig.cpp | 14 +++---- .../src/avatars-renderer/SkeletonModel.cpp | 3 +- 3 files changed, 40 insertions(+), 15 deletions(-) diff --git a/libraries/animation/src/AnimSkeleton.cpp b/libraries/animation/src/AnimSkeleton.cpp index ab914b1da3..cc4a66027d 100644 --- a/libraries/animation/src/AnimSkeleton.cpp +++ b/libraries/animation/src/AnimSkeleton.cpp @@ -37,17 +37,30 @@ AnimSkeleton::AnimSkeleton(const FBXGeometry& fbxGeometry, const QMap(mesh.clusters.at(j)); - if ((cluster.jointIndex == nameToJointIndex("Neck")) || (cluster.jointIndex == nameToJointIndex("Spine2"))) { - qCDebug(animation) << "found a joint offset to add " << cluster.jointIndex << " " << jointOffsets[j]; + if (jointOffsets.contains(cluster.jointIndex)) { + qCDebug(animation) << "cluster joint equals index " << cluster.jointIndex; + } + + if ((cluster.jointIndex == 62) || (cluster.jointIndex == 13)) { + qCDebug(animation) << "cluster joint equals index " << cluster.jointIndex; } // AJT: mutate bind pose! this allows us to oreint the skeleton back into the authored orientaiton before // rendering, with no runtime overhead. // this works if clusters match joints one for one. - if (jointOffsets.contains(j)) { - qCDebug(animation) << "found a joint offset to add " << j << " " << jointOffsets[j]; - AnimPose localOffset(jointOffsets[j], glm::vec3()); + if (cluster.jointIndex == 62) { + qCDebug(animation) << "Neck"; + qCDebug(animation) << "found a joint offset to add " << cluster.jointIndex << " " << jointOffsets[cluster.jointIndex] << " cluster " << cluster.jointIndex; + AnimPose localOffset(jointOffsets[cluster.jointIndex], glm::vec3()); + cluster.inverseBindMatrix = (glm::mat4)localOffset.inverse() * cluster.inverseBindMatrix; + cluster.inverseBindTransform.evalFromRawMatrix(cluster.inverseBindMatrix); + } + if (cluster.jointIndex == 13) { + qCDebug(animation) << "Spine2"; + qCDebug(animation) << "found a joint offset to add " << cluster.jointIndex << " " << jointOffsets[cluster.jointIndex] << " cluster " << cluster.jointIndex; + AnimPose localOffset(jointOffsets[cluster.jointIndex], glm::vec3()); cluster.inverseBindMatrix = (glm::mat4)localOffset.inverse() * cluster.inverseBindMatrix; cluster.inverseBindTransform.evalFromRawMatrix(cluster.inverseBindMatrix); } @@ -227,14 +240,25 @@ void AnimSkeleton::buildSkeletonFromJoints(const std::vector& joints, // remember the inverse bind pose already has the offset added into it. the total effect is offset^-1 * relDefPose * offset. // this gives us the correct transform for the joint that has been put in t-pose with an offset rotation. //relDefaultPose = relDefaultPose * _avatarTPoseOffsets[i]; + + QString jointName = getJointName(i); if (jointOffsets.contains(i)) { + //QString parentIndex = getJointName(parentIndex); AnimPose localOffset(jointOffsets[i], glm::vec3()); relDefaultPose = relDefaultPose * localOffset; if ((parentIndex >= 0) && jointOffsets.contains(parentIndex)) { - AnimPose localParentOffset(jointOffsets[parentIndex], glm::vec3()); - relDefaultPose = localParentOffset.inverse() * AnimPose(glm::quat(), relDefaultPose.trans()) * localParentOffset * AnimPose(relDefaultPose.rot(), glm::vec3()); + // AnimPose localParentOffset(jointOffsets[parentIndex], glm::vec3()); + // relDefaultPose = localParentOffset.inverse() * AnimPose(glm::quat(), relDefaultPose.trans()) * localParentOffset * AnimPose(relDefaultPose.rot(), glm::vec3()); } } + if ((parentIndex == 13) && jointOffsets.contains(13)) { + AnimPose localParentOffset(jointOffsets[parentIndex], glm::vec3()); + relDefaultPose = localParentOffset.inverse() * AnimPose(glm::quat(), relDefaultPose.trans()) * localParentOffset * AnimPose(relDefaultPose.rot(), glm::vec3()); + } + if ((parentIndex == 62) && jointOffsets.contains(62)) { + AnimPose localParentOffset(glm::quat(0.7071f, 0.0f, -0.7071f, 0.0f), glm::vec3()); + relDefaultPose = localParentOffset.inverse() * AnimPose(glm::quat(), relDefaultPose.trans()) * localParentOffset * AnimPose(relDefaultPose.rot(), glm::vec3()); + } _relativeDefaultPoses.push_back(relDefaultPose); diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index f1cb94a99c..ff9d19c8a2 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -2074,13 +2074,13 @@ void Rig::setJointRotationOffsets(const QMap& offsets) { } qDebug() << "Joint Rotation Offset added to Rig._jointRotationOffsets : " << " jointName: " << jointName << " jointIndex: " << jointIndex << " rotation offset: " << rotationOffset; } - int neckIndex = indexOfJoint("Neck"); - int spine2Index = indexOfJoint("Spine2"); - if (neckIndex != -1) { - _jointRotationOffsets.insert(neckIndex, glm::quat(0.7071f, 0.0f, 0.7071f, 0.0f)); + int neckId = 62; + int spine2Id = 13; + if (true){ //neckIndex != -1) { + _jointRotationOffsets.insert(neckId, glm::quat(0.0f, 0.7071f, 0.7071f, 0.0f)); } - if (spine2Index != -1) { - _jointRotationOffsets.insert(spine2Index, glm::quat(0.7071f, -0.7071f, 0.0f, 0.0f)); + if (true){ //spine2Index != -1) { + _jointRotationOffsets.insert(spine2Id, glm::quat(0.5f, 0.5f, 0.5f, -0.5f)); } - qCDebug(animation) << "set the neck and spine2 offsets"; + qCDebug(animation) << "set the neck and spine2 offsets " << spine2Id << " " << neckId; } \ No newline at end of file diff --git a/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp b/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp index 816e078886..c6123ef003 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp @@ -57,6 +57,7 @@ void SkeletonModel::setTextures(const QVariantMap& textures) { void SkeletonModel::initJointStates() { const FBXGeometry& geometry = getFBXGeometry(); glm::mat4 modelOffset = glm::scale(_scale) * glm::translate(_offset); + _rig.setJointRotationOffsets(FSTReader::getJointRotationOffsets(getGeometry()->getMapping())); _rig.initJointStates(geometry, modelOffset); { @@ -83,7 +84,7 @@ void SkeletonModel::initJointStates() { // Skeleton may have already been scaled so unscale it _defaultEyeModelPosition = _defaultEyeModelPosition / _scale; - _rig.setJointRotationOffsets(FSTReader::getJointRotationOffsets(getGeometry()->getMapping())); + //_rig.setJointRotationOffsets(FSTReader::getJointRotationOffsets(getGeometry()->getMapping())); computeBoundingShape(); Extents meshExtents = getMeshExtents(); From bcde01621d13417baab6a2a173c8977a5b1aef6d Mon Sep 17 00:00:00 2001 From: luiscuenca Date: Mon, 5 Nov 2018 14:25:52 -0700 Subject: [PATCH 128/286] set offsets on FBXGeometry --- libraries/animation/src/Rig.cpp | 13 ------- libraries/animation/src/Rig.h | 4 -- .../src/avatars-renderer/SkeletonModel.cpp | 1 - libraries/fbx/src/FBX.h | 2 + libraries/fbx/src/FBXReader.cpp | 38 +++++++++++++++++++ libraries/fbx/src/FSTReader.cpp | 24 ------------ libraries/fbx/src/FSTReader.h | 2 - 7 files changed, 40 insertions(+), 44 deletions(-) diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index c58a96aa84..33b9569758 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -2062,16 +2062,3 @@ void Rig::computeAvatarBoundingCapsule( glm::vec3 capsuleCenter = transformPoint(_geometryToRigTransform, (0.5f * (totalExtents.maximum + totalExtents.minimum))); localOffsetOut = capsuleCenter - hipsPosition; } - -void Rig::setJointRotationOffsets(const QMap& offsets) { - _jointRotationOffsets.clear(); - for (auto itr = offsets.begin(); itr != offsets.end(); itr++) { - QString jointName = itr.key(); - glm::quat rotationOffset = itr.value(); - int jointIndex = indexOfJoint(jointName); - if (jointIndex != -1) { - _jointRotationOffsets.insert(jointIndex, rotationOffset); - } - qDebug() << "Joint Rotation Offset added to Rig._jointRotationOffsets : " << " jointName: " << jointName << " jointIndex: " << jointIndex << " rotation offset: " << rotationOffset; - } -} \ No newline at end of file diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index 9928810026..7a090bd7bd 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -231,8 +231,6 @@ public: const AnimContext::DebugAlphaMap& getDebugAlphaMap() const { return _lastContext.getDebugAlphaMap(); } const AnimVariantMap& getAnimVars() const { return _lastAnimVars; } const AnimContext::DebugStateMachineMap& getStateMachineMap() const { return _lastContext.getStateMachineMap(); } - - void setJointRotationOffsets(const QMap& offsets); signals: void onLoadComplete(); @@ -302,8 +300,6 @@ protected: int _rightElbowJointIndex { -1 }; int _rightShoulderJointIndex { -1 }; - QMap _jointRotationOffsets; - glm::vec3 _lastForward; glm::vec3 _lastPosition; glm::vec3 _lastVelocity; diff --git a/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp b/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp index 816e078886..bf6af763a3 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp @@ -83,7 +83,6 @@ void SkeletonModel::initJointStates() { // Skeleton may have already been scaled so unscale it _defaultEyeModelPosition = _defaultEyeModelPosition / _scale; - _rig.setJointRotationOffsets(FSTReader::getJointRotationOffsets(getGeometry()->getMapping())); computeBoundingShape(); Extents meshExtents = getMeshExtents(); diff --git a/libraries/fbx/src/FBX.h b/libraries/fbx/src/FBX.h index fdebb16bc8..ec3a29e7f5 100644 --- a/libraries/fbx/src/FBX.h +++ b/libraries/fbx/src/FBX.h @@ -366,6 +366,8 @@ public: QString getModelNameOfMesh(int meshIndex) const; QList blendshapeChannelNames; + + QMap jointRotationOffsets; }; Q_DECLARE_METATYPE(FBXGeometry) diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index dd766f002c..44976adfa8 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -615,6 +615,31 @@ QByteArray fileOnUrl(const QByteArray& filepath, const QString& url) { return filepath.mid(filepath.lastIndexOf('/') + 1); } +QMap getJointRotationOffsets(const QVariantHash& mapping) { + QMap jointRotationOffsets; + static const QString JOINT_ROTATION_OFFSET_FIELD = "jointRotationOffset"; + if (!mapping.isEmpty() && mapping.contains(JOINT_ROTATION_OFFSET_FIELD) && mapping[JOINT_ROTATION_OFFSET_FIELD].type() == QVariant::Hash) { + auto offsets = mapping[JOINT_ROTATION_OFFSET_FIELD].toHash(); + for (auto itr = offsets.begin(); itr != offsets.end(); itr++) { + QString jointName = itr.key(); + QString line = itr.value().toString(); + auto eulerAngles = line.split(','); + if (eulerAngles.size() == 3) { + float eulerX = eulerAngles[0].mid(1).toFloat(); + float eulerY = eulerAngles[1].toFloat(); + float eulerZ = eulerAngles[2].mid(0, eulerAngles[2].size() - 1).toFloat(); + if (!isNaN(eulerX) && !isNaN(eulerY) && !isNaN(eulerZ)) { + glm::quat rotationOffset = (glm::angleAxis(eulerX * RADIANS_PER_DEGREE, Vectors::UNIT_X) * + glm::angleAxis(eulerY * RADIANS_PER_DEGREE, Vectors::UNIT_Y) * + glm::angleAxis(eulerZ * RADIANS_PER_DEGREE, Vectors::UNIT_Z)); + jointRotationOffsets.insert(jointName, rotationOffset); + } + } + } + } + return jointRotationOffsets; +} + FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QString& url) { const FBXNode& node = _rootNode; QMap meshes; @@ -1991,6 +2016,19 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS } } } + + auto offsets = getJointRotationOffsets(mapping); + geometry.jointRotationOffsets.clear(); + for (auto itr = offsets.begin(); itr != offsets.end(); itr++) { + QString jointName = itr.key(); + glm::quat rotationOffset = itr.value(); + int jointIndex = geometry.getJointIndex(jointName); + if (jointIndex != -1) { + geometry.jointRotationOffsets.insert(jointIndex, rotationOffset); + } + qCDebug(modelformat) << "Joint Rotation Offset added to Rig._jointRotationOffsets : " << " jointName: " << jointName << " jointIndex: " << jointIndex << " rotation offset: " << rotationOffset; + } + return geometryPtr; } diff --git a/libraries/fbx/src/FSTReader.cpp b/libraries/fbx/src/FSTReader.cpp index 05690f5740..75596862d2 100644 --- a/libraries/fbx/src/FSTReader.cpp +++ b/libraries/fbx/src/FSTReader.cpp @@ -207,30 +207,6 @@ QVector FSTReader::getScripts(const QUrl& url, const QVariantHash& mapp return scriptPaths; } -QMap FSTReader::getJointRotationOffsets(const QVariantHash& mapping) { - QMap jointRotationOffsets; - if (!mapping.isEmpty() && mapping.contains(JOINT_ROTATION_OFFSET_FIELD) && mapping[JOINT_ROTATION_OFFSET_FIELD].type() == QVariant::Hash) { - auto offsets = mapping[JOINT_ROTATION_OFFSET_FIELD].toHash(); - for (auto itr = offsets.begin(); itr != offsets.end(); itr++) { - QString jointName = itr.key(); - QString line = itr.value().toString(); - auto eulerAngles = line.split(','); - if (eulerAngles.size() == 3) { - float eulerX = eulerAngles[0].mid(1).toFloat(); - float eulerY = eulerAngles[1].toFloat(); - float eulerZ = eulerAngles[2].mid(0, eulerAngles[2].size() - 1).toFloat(); - if (!isNaN(eulerX) && !isNaN(eulerY) && !isNaN(eulerZ)) { - glm::quat rotationOffset = (glm::angleAxis(eulerX * RADIANS_PER_DEGREE, Vectors::UNIT_X) * - glm::angleAxis(eulerY * RADIANS_PER_DEGREE, Vectors::UNIT_Y) * - glm::angleAxis(eulerZ * RADIANS_PER_DEGREE, Vectors::UNIT_Z)); - jointRotationOffsets.insert(jointName, rotationOffset); - } - } - } - } - return jointRotationOffsets; -} - QVariantHash FSTReader::downloadMapping(const QString& url) { QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); QNetworkRequest networkRequest = QNetworkRequest(url); diff --git a/libraries/fbx/src/FSTReader.h b/libraries/fbx/src/FSTReader.h index ef649eb4d8..3729cbaf16 100644 --- a/libraries/fbx/src/FSTReader.h +++ b/libraries/fbx/src/FSTReader.h @@ -30,7 +30,6 @@ static const QString JOINT_FIELD = "joint"; static const QString FREE_JOINT_FIELD = "freeJoint"; static const QString BLENDSHAPE_FIELD = "bs"; static const QString SCRIPT_FIELD = "script"; -static const QString JOINT_ROTATION_OFFSET_FIELD = "jointRotationOffset"; class FSTReader { public: @@ -53,7 +52,6 @@ public: static ModelType predictModelType(const QVariantHash& mapping); static QVector getScripts(const QUrl& fstUrl, const QVariantHash& mapping = QVariantHash()); - static QMap getJointRotationOffsets(const QVariantHash& mapping); static QString getNameFromType(ModelType modelType); static FSTReader::ModelType getTypeFromName(const QString& name); From 8310dd0cbac7203e15e39af8888b3dcc3f03fb0c Mon Sep 17 00:00:00 2001 From: luiscuenca Date: Mon, 5 Nov 2018 15:10:09 -0700 Subject: [PATCH 129/286] Delete leftovers --- .../avatars-renderer/src/avatars-renderer/SkeletonModel.cpp | 2 +- libraries/fbx/src/FSTReader.h | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp b/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp index bf6af763a3..fce33d38c1 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp @@ -18,7 +18,6 @@ #include #include #include -#include #include "Avatar.h" #include "Logging.h" @@ -83,6 +82,7 @@ void SkeletonModel::initJointStates() { // Skeleton may have already been scaled so unscale it _defaultEyeModelPosition = _defaultEyeModelPosition / _scale; + computeBoundingShape(); Extents meshExtents = getMeshExtents(); diff --git a/libraries/fbx/src/FSTReader.h b/libraries/fbx/src/FSTReader.h index 3729cbaf16..4a8574f0cf 100644 --- a/libraries/fbx/src/FSTReader.h +++ b/libraries/fbx/src/FSTReader.h @@ -14,7 +14,6 @@ #include #include -#include static const QString NAME_FIELD = "name"; static const QString TYPE_FIELD = "type"; From 484eb8ba09f2c42035623f012f5c1051f97a56d2 Mon Sep 17 00:00:00 2001 From: amantley Date: Mon, 5 Nov 2018 14:56:58 -0800 Subject: [PATCH 130/286] debugging unity offsets --- libraries/animation/src/AnimSkeleton.cpp | 32 ++++++++++++++++++------ 1 file changed, 25 insertions(+), 7 deletions(-) diff --git a/libraries/animation/src/AnimSkeleton.cpp b/libraries/animation/src/AnimSkeleton.cpp index cc4a66027d..39ab5a178e 100644 --- a/libraries/animation/src/AnimSkeleton.cpp +++ b/libraries/animation/src/AnimSkeleton.cpp @@ -28,11 +28,11 @@ AnimSkeleton::AnimSkeleton(const FBXGeometry& fbxGeometry, const QMap& joints, const QMap jointOffsets) { @@ -240,7 +248,7 @@ void AnimSkeleton::buildSkeletonFromJoints(const std::vector& joints, // remember the inverse bind pose already has the offset added into it. the total effect is offset^-1 * relDefPose * offset. // this gives us the correct transform for the joint that has been put in t-pose with an offset rotation. //relDefaultPose = relDefaultPose * _avatarTPoseOffsets[i]; - + /* QString jointName = getJointName(i); if (jointOffsets.contains(i)) { //QString parentIndex = getJointName(parentIndex); @@ -252,13 +260,23 @@ void AnimSkeleton::buildSkeletonFromJoints(const std::vector& joints, } } if ((parentIndex == 13) && jointOffsets.contains(13)) { + if (i == 62) { + qCDebug(animation) << "the neck translation is " << relDefaultPose.trans(); + } AnimPose localParentOffset(jointOffsets[parentIndex], glm::vec3()); - relDefaultPose = localParentOffset.inverse() * AnimPose(glm::quat(), relDefaultPose.trans()) * localParentOffset * AnimPose(relDefaultPose.rot(), glm::vec3()); + //relDefaultPose = localParentOffset.inverse() * AnimPose(glm::quat(), relDefaultPose.trans()) * localParentOffset * AnimPose(relDefaultPose.rot(), glm::vec3()); + if (i == 62) { + qCDebug(animation) << "the neck translation redo is " << relDefaultPose.trans(); + } } if ((parentIndex == 62) && jointOffsets.contains(62)) { - AnimPose localParentOffset(glm::quat(0.7071f, 0.0f, -0.7071f, 0.0f), glm::vec3()); - relDefaultPose = localParentOffset.inverse() * AnimPose(glm::quat(), relDefaultPose.trans()) * localParentOffset * AnimPose(relDefaultPose.rot(), glm::vec3()); + qCDebug(animation) << "the head translation is " << relDefaultPose.trans(); + //AnimPose localOffset(glm::quat(0.7071f, 0.0f, 0.7071f, 0.0f), glm::vec3()); + //relDefaultPose = relDefaultPose * localOffset; + //AnimPose localParentOffset(glm::quat(0.7071f, 0.0f, -0.7071f, 0.0f), glm::vec3()); + //relDefaultPose = localParentOffset.inverse() * AnimPose(glm::quat(), relDefaultPose.trans()) * localParentOffset * AnimPose(relDefaultPose.rot(), glm::vec3()); } + */ _relativeDefaultPoses.push_back(relDefaultPose); From f0c02bb49bbdb44b04f4c27226deb56147280829 Mon Sep 17 00:00:00 2001 From: amantley Date: Mon, 5 Nov 2018 17:36:54 -0800 Subject: [PATCH 131/286] more debug --- libraries/animation/src/AnimSkeleton.cpp | 25 ++++++++++++------------ libraries/animation/src/Rig.cpp | 2 +- 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/libraries/animation/src/AnimSkeleton.cpp b/libraries/animation/src/AnimSkeleton.cpp index 39ab5a178e..49ffe75ba0 100644 --- a/libraries/animation/src/AnimSkeleton.cpp +++ b/libraries/animation/src/AnimSkeleton.cpp @@ -32,7 +32,7 @@ AnimSkeleton::AnimSkeleton(const FBXGeometry& fbxGeometry, const QMap(mesh.clusters.at(j)); - if (jointOffsets.contains(cluster.jointIndex)) { - qCDebug(animation) << "cluster joint equals index " << cluster.jointIndex; - } if ((cluster.jointIndex == 62) || (cluster.jointIndex == 13)) { qCDebug(animation) << "cluster joint equals index " << cluster.jointIndex; @@ -51,8 +48,8 @@ AnimSkeleton::AnimSkeleton(const FBXGeometry& fbxGeometry, const QMap& joints, // build relative and absolute default poses glm::mat4 relDefaultMat = glm::translate(_joints[i].translation) * preRotationTransform * glm::mat4_cast(_joints[i].rotation) * postRotationTransform; AnimPose relDefaultPose(relDefaultMat); + qCDebug(animation) << "relative default pose for joint " << i << " " << relDefaultPose.trans() << " " << relDefaultPose.rot(); int parentIndex = getParentIndex(i); @@ -248,7 +246,7 @@ void AnimSkeleton::buildSkeletonFromJoints(const std::vector& joints, // remember the inverse bind pose already has the offset added into it. the total effect is offset^-1 * relDefPose * offset. // this gives us the correct transform for the joint that has been put in t-pose with an offset rotation. //relDefaultPose = relDefaultPose * _avatarTPoseOffsets[i]; - /* + QString jointName = getJointName(i); if (jointOffsets.contains(i)) { //QString parentIndex = getJointName(parentIndex); @@ -264,19 +262,20 @@ void AnimSkeleton::buildSkeletonFromJoints(const std::vector& joints, qCDebug(animation) << "the neck translation is " << relDefaultPose.trans(); } AnimPose localParentOffset(jointOffsets[parentIndex], glm::vec3()); - //relDefaultPose = localParentOffset.inverse() * AnimPose(glm::quat(), relDefaultPose.trans()) * localParentOffset * AnimPose(relDefaultPose.rot(), glm::vec3()); + relDefaultPose = localParentOffset.inverse() * AnimPose(glm::quat(), relDefaultPose.trans()) * localParentOffset * AnimPose(relDefaultPose.rot(), glm::vec3()); if (i == 62) { qCDebug(animation) << "the neck translation redo is " << relDefaultPose.trans(); } } if ((parentIndex == 62) && jointOffsets.contains(62)) { - qCDebug(animation) << "the head translation is " << relDefaultPose.trans(); + //AnimPose localOffset(glm::quat(0.7071f, 0.0f, 0.7071f, 0.0f), glm::vec3()); //relDefaultPose = relDefaultPose * localOffset; - //AnimPose localParentOffset(glm::quat(0.7071f, 0.0f, -0.7071f, 0.0f), glm::vec3()); - //relDefaultPose = localParentOffset.inverse() * AnimPose(glm::quat(), relDefaultPose.trans()) * localParentOffset * AnimPose(relDefaultPose.rot(), glm::vec3()); + AnimPose localParentOffset(glm::quat(0.7071f, 0.0f, -0.7071f, 0.0f), glm::vec3()); + relDefaultPose = localParentOffset.inverse() * AnimPose(glm::quat(), relDefaultPose.trans()) * localParentOffset * AnimPose(relDefaultPose.rot(), glm::vec3()); + qCDebug(animation) << "the head translation is " << relDefaultPose.trans(); } - */ + _relativeDefaultPoses.push_back(relDefaultPose); diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index ff9d19c8a2..3302760f6b 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -2077,7 +2077,7 @@ void Rig::setJointRotationOffsets(const QMap& offsets) { int neckId = 62; int spine2Id = 13; if (true){ //neckIndex != -1) { - _jointRotationOffsets.insert(neckId, glm::quat(0.0f, 0.7071f, 0.7071f, 0.0f)); + _jointRotationOffsets.insert(neckId, glm::quat(0.7071f, 0.0f, -0.7071f, 0.0f)); } if (true){ //spine2Index != -1) { _jointRotationOffsets.insert(spine2Id, glm::quat(0.5f, 0.5f, 0.5f, -0.5f)); From c0e7a4348bf1ce30a00a86ccc036fc7a8f75de12 Mon Sep 17 00:00:00 2001 From: NissimHadar Date: Mon, 5 Nov 2018 22:10:20 -0500 Subject: [PATCH 132/286] Reverted changes to Interface. --- interface/src/Application.cpp | 14 +------------- interface/src/Application.h | 6 ------ .../src/scripting/TestScriptingInterface.cpp | 14 -------------- .../src/scripting/TestScriptingInterface.h | 17 ----------------- 4 files changed, 1 insertion(+), 50 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index d5d99a34b5..9f69bab82c 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -4803,13 +4803,7 @@ void Application::idle() { _idleLoopStdev.reset(); } } - - // This is done here so it won't be during a resize/move event - if (_setGeometryRequested) { - _setGeometryRequested = false; - _window->setGeometry(requestedGeometry); - } - + _overlayConductor.update(secondsSinceLastUpdate); _gameLoopCounter.increment(); @@ -8584,12 +8578,6 @@ void Application::copyToClipboard(const QString& text) { QApplication::clipboard()->setText(text); } -void Application::setGeometry(int x, int y, int width, int height) { - // Note that calling setGeometry inside resizeEvent() or moveEvent() can cause infinite recursion - requestedGeometry = QRect(x, y, width, height); - _setGeometryRequested = true; -} - #if defined(Q_OS_ANDROID) void Application::beforeEnterBackground() { auto nodeList = DependencyManager::get(); diff --git a/interface/src/Application.h b/interface/src/Application.h index cd5d62c106..14e30b8006 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -315,8 +315,6 @@ public: Q_INVOKABLE void copyToClipboard(const QString& text); - void setGeometry(int x, int y, int width, int height); - int getOtherAvatarsReplicaCount() { return DependencyManager::get()->getReplicaCount(); } void setOtherAvatarsReplicaCount(int count) { DependencyManager::get()->setReplicaCount(count); } @@ -786,9 +784,5 @@ private: bool _showTrackedObjects { false }; bool _prevShowTrackedObjects { false }; - - // Data for the setGeometry script command - bool _setGeometryRequested{ false }; - QRect requestedGeometry; }; #endif // hifi_Application_h diff --git a/interface/src/scripting/TestScriptingInterface.cpp b/interface/src/scripting/TestScriptingInterface.cpp index 74b03faf83..c3aeb2643b 100644 --- a/interface/src/scripting/TestScriptingInterface.cpp +++ b/interface/src/scripting/TestScriptingInterface.cpp @@ -199,17 +199,3 @@ void TestScriptingInterface::setOtherAvatarsReplicaCount(int count) { int TestScriptingInterface::getOtherAvatarsReplicaCount() { return qApp->getOtherAvatarsReplicaCount(); } - -QString TestScriptingInterface::getOperatingSystemType() { -#ifdef Q_OS_WIN - return "WINDOWS"; -#elif defined Q_OS_MAC - return "MACOS"; -#else - return "UNKNOWN"; -#endif -} - -void TestScriptingInterface::setGeometry(int x, int y, int width, int height) { - qApp->setGeometry(x, y, width, height); -} \ No newline at end of file diff --git a/interface/src/scripting/TestScriptingInterface.h b/interface/src/scripting/TestScriptingInterface.h index 6c2c542f33..4a1d1a3eeb 100644 --- a/interface/src/scripting/TestScriptingInterface.h +++ b/interface/src/scripting/TestScriptingInterface.h @@ -163,23 +163,6 @@ public slots: */ Q_INVOKABLE int getOtherAvatarsReplicaCount(); - /**jsdoc - * Returns the Operating Sytem type - * @function Test.getOperatingSystemType - * @returns {string} "WINDOWS", "MACOS" or "UNKNOWN" - */ - QString getOperatingSystemType(); - - /**jsdoc - * Sets the position and size of the window on desktop. All units are pixels - * @function Test.setGeometry - * @param {int} x - Distance of window from left edge - * @param {int} y - Distance of window from top edge - * @param {int} width - Width of window - * @param {int} height - Height of window - */ - void setGeometry(int x, int y, int width, int height); - private: bool waitForCondition(qint64 maxWaitMs, std::function condition); QString _testResultsLocation; From e9a636d751f15c677a1baf4dfc33fd24190070e4 Mon Sep 17 00:00:00 2001 From: NissimHadar Date: Mon, 5 Nov 2018 22:13:02 -0500 Subject: [PATCH 133/286] Revert... --- interface/src/Application.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 9f69bab82c..69d114e51b 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -4803,7 +4803,6 @@ void Application::idle() { _idleLoopStdev.reset(); } } - _overlayConductor.update(secondsSinceLastUpdate); _gameLoopCounter.increment(); From ebdd443646cfc38849e285d8ab6efdd8f7bb71b5 Mon Sep 17 00:00:00 2001 From: NissimHadar Date: Tue, 6 Nov 2018 10:19:27 -0500 Subject: [PATCH 134/286] New resize method for Mac. --- tools/auto-tester/src/TestRunner.cpp | 62 ++++++++++++++++++++++++++-- 1 file changed, 58 insertions(+), 4 deletions(-) diff --git a/tools/auto-tester/src/TestRunner.cpp b/tools/auto-tester/src/TestRunner.cpp index b2ef00647f..3781024a63 100644 --- a/tools/auto-tester/src/TestRunner.cpp +++ b/tools/auto-tester/src/TestRunner.cpp @@ -129,11 +129,11 @@ void TestRunner::setWorkingFolder() { script.close(); script.setPermissions(QFile::ReadOwner | QFile::WriteOwner | QFile::ExeOwner); - // The Mac shell command returns immediately. This little script waits for a process to complete - script.setFileName(_workingFolder + "/waitForCompletion.sh"); + // This script waits for a process to start + script.setFileName(_workingFolder + "/waitForStart.sh"); if (!script.open(QIODevice::WriteOnly | QIODevice::Text)) { QMessageBox::critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), - "Could not open 'install_app.sh'"); + "Could not open 'waitForStart.sh'"); exit(-1); } @@ -145,7 +145,55 @@ void TestRunner::setWorkingFolder() { script.write("done\n"); script.close(); script.setPermissions(QFile::ReadOwner | QFile::WriteOwner | QFile::ExeOwner); - #endif + + // The Mac shell command returns immediately. This little script waits for a process to complete + script.setFileName(_workingFolder + "/waitForCompletion.sh"); + if (!script.open(QIODevice::WriteOnly | QIODevice::Text)) { + QMessageBox::critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), + "Could not open 'waitForCompletion.sh'"); + exit(-1); + } + + script.write("#!/bin/sh\n\n"); + script.write("PROCESS=\"$1\"\n"); + script.write("while (pgrep $PROCESS)\n"); + script.write("do\n"); + script.write("\tsleep 2\n"); + script.write("done\n"); + script.close(); + script.setPermissions(QFile::ReadOwner | QFile::WriteOwner | QFile::ExeOwner); + + // Create an AppleScript to resize Interface. This is needed so that snapshots taken + // with the primary camera will be the correct size. + // This will be run from a normal shell script + script.setFileName(_workingFolder + "/setInterfaceSizeAndPosition.scpt"); + if (!script.open(QIODevice::WriteOnly | QIODevice::Text)) { + QMessageBox::critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), + "Could not open 'setInterfaceSizeAndPosition.scpt'"); + exit(-1); + } + + script.write("set width to 960\n"); + script.write("set height to 540\n"); + script.write("set x to 100\n"); + script.write("set y to 100\n\n"); + script.write("tell application \"System Events\" to tell application process \"interface\" to tell window 1 to set {size, position} to {{width, height}, {x, y}})\n"); + + script.close(); + script.setPermissions(QFile::ReadOwner | QFile::WriteOwner | QFile::ExeOwner); + + script.setFileName(_workingFolder + "/setInterfaceSizeAndPosition.sh"); + if (!script.open(QIODevice::WriteOnly | QIODevice::Text)) { + QMessageBox::critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), + "Could not open 'setInterfaceSizeAndPosition.sh'"); + exit(-1); + } + + script.write("#!/bin/sh\n\n"); + script.write((_workingFolder + "/osascript " + _workingFolder + "/setInterfaceSizeAndPosition.scpt\n").toStdString().c_str()); + script.close(); + script.setPermissions(QFile::ReadOwner | QFile::WriteOwner | QFile::ExeOwner); +#endif } void TestRunner::run() { @@ -419,11 +467,17 @@ void TestRunner::runInterfaceWithTestScript() { commandLine = exeFile + " --url " + url + " --no-updater" + " --testScript " + testScript + " quitWhenFinished --testResultsLocation " + _snapshotFolder; #elif defined Q_OS_MAC + // On The Mac, we need to resize Interface. The Interface window opens a few seconds after the process has started, + // so we wait another 10 seconds + // The shell closes if we don't wait for completion, hence the final line commandLine = "open \"" +_installationFolder + "/interface.app\" --args" + " --url " + url + " --no-updater" + " --testScript " + testScript + " quitWhenFinished" + " --testResultsLocation " + _snapshotFolder + + "; " + _workingFolder +"/waitForStart.sh interface" + + "; " + "sleep 10" + + "; " + _workingFolder +"/setInterfaceSizeAndPosition.sh" + "; " + _workingFolder +"/waitForCompletion.sh interface"; #endif From 599b7761b2a7d44b848215a0bb001ac4d21e08bd Mon Sep 17 00:00:00 2001 From: luiscuenca Date: Tue, 6 Nov 2018 11:09:56 -0700 Subject: [PATCH 135/286] Read quats from fst --- libraries/fbx/src/FBXReader.cpp | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index 44976adfa8..c8dc78bd91 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -623,15 +623,14 @@ QMap getJointRotationOffsets(const QVariantHash& mapping) { for (auto itr = offsets.begin(); itr != offsets.end(); itr++) { QString jointName = itr.key(); QString line = itr.value().toString(); - auto eulerAngles = line.split(','); - if (eulerAngles.size() == 3) { - float eulerX = eulerAngles[0].mid(1).toFloat(); - float eulerY = eulerAngles[1].toFloat(); - float eulerZ = eulerAngles[2].mid(0, eulerAngles[2].size() - 1).toFloat(); - if (!isNaN(eulerX) && !isNaN(eulerY) && !isNaN(eulerZ)) { - glm::quat rotationOffset = (glm::angleAxis(eulerX * RADIANS_PER_DEGREE, Vectors::UNIT_X) * - glm::angleAxis(eulerY * RADIANS_PER_DEGREE, Vectors::UNIT_Y) * - glm::angleAxis(eulerZ * RADIANS_PER_DEGREE, Vectors::UNIT_Z)); + auto quatCoords = line.split(','); + if (quatCoords.size() == 4) { + float quatX = quatCoords[0].mid(1).toFloat(); + float quatY = quatCoords[1].toFloat(); + float quatZ = quatCoords[2].toFloat(); + float quatW = quatCoords[3].mid(0, quatCoords[3].size() - 1).toFloat(); + if (!isNaN(quatX) && !isNaN(quatY) && !isNaN(quatZ) && !isNaN(quatW)) { + glm::quat rotationOffset = glm::quat(quatW, quatX, quatY, quatZ); jointRotationOffsets.insert(jointName, rotationOffset); } } @@ -2026,7 +2025,7 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS if (jointIndex != -1) { geometry.jointRotationOffsets.insert(jointIndex, rotationOffset); } - qCDebug(modelformat) << "Joint Rotation Offset added to Rig._jointRotationOffsets : " << " jointName: " << jointName << " jointIndex: " << jointIndex << " rotation offset: " << rotationOffset; + qDebug() << "Joint Rotation Offset added to Rig._jointRotationOffsets : " << " jointName: " << jointName << " jointIndex: " << jointIndex << " rotation offset: " << rotationOffset; } return geometryPtr; From e0d1ef3be3128a9433787bb76bc27881c5f4d07f Mon Sep 17 00:00:00 2001 From: luiscuenca Date: Tue, 6 Nov 2018 11:11:16 -0700 Subject: [PATCH 136/286] Fix debug output --- libraries/fbx/src/FBXReader.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index c8dc78bd91..c357d0f430 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -2025,7 +2025,7 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS if (jointIndex != -1) { geometry.jointRotationOffsets.insert(jointIndex, rotationOffset); } - qDebug() << "Joint Rotation Offset added to Rig._jointRotationOffsets : " << " jointName: " << jointName << " jointIndex: " << jointIndex << " rotation offset: " << rotationOffset; + qCDebug(modelformat) << "Joint Rotation Offset added to Rig._jointRotationOffsets : " << " jointName: " << jointName << " jointIndex: " << jointIndex << " rotation offset: " << rotationOffset; } return geometryPtr; From e52f2ab7d6210ca8be1fdfe65dfb2aa74b5d9128 Mon Sep 17 00:00:00 2001 From: amantley Date: Tue, 6 Nov 2018 14:14:57 -0800 Subject: [PATCH 137/286] works for two joints now --- libraries/animation/src/AnimSkeleton.cpp | 21 +-------------------- libraries/animation/src/Rig.cpp | 3 ++- 2 files changed, 3 insertions(+), 21 deletions(-) diff --git a/libraries/animation/src/AnimSkeleton.cpp b/libraries/animation/src/AnimSkeleton.cpp index 49ffe75ba0..2dfcdee4b0 100644 --- a/libraries/animation/src/AnimSkeleton.cpp +++ b/libraries/animation/src/AnimSkeleton.cpp @@ -252,30 +252,11 @@ void AnimSkeleton::buildSkeletonFromJoints(const std::vector& joints, //QString parentIndex = getJointName(parentIndex); AnimPose localOffset(jointOffsets[i], glm::vec3()); relDefaultPose = relDefaultPose * localOffset; - if ((parentIndex >= 0) && jointOffsets.contains(parentIndex)) { - // AnimPose localParentOffset(jointOffsets[parentIndex], glm::vec3()); - // relDefaultPose = localParentOffset.inverse() * AnimPose(glm::quat(), relDefaultPose.trans()) * localParentOffset * AnimPose(relDefaultPose.rot(), glm::vec3()); - } } - if ((parentIndex == 13) && jointOffsets.contains(13)) { - if (i == 62) { - qCDebug(animation) << "the neck translation is " << relDefaultPose.trans(); - } + if ((parentIndex >= 0) && jointOffsets.contains(parentIndex)) { AnimPose localParentOffset(jointOffsets[parentIndex], glm::vec3()); relDefaultPose = localParentOffset.inverse() * AnimPose(glm::quat(), relDefaultPose.trans()) * localParentOffset * AnimPose(relDefaultPose.rot(), glm::vec3()); - if (i == 62) { - qCDebug(animation) << "the neck translation redo is " << relDefaultPose.trans(); - } } - if ((parentIndex == 62) && jointOffsets.contains(62)) { - - //AnimPose localOffset(glm::quat(0.7071f, 0.0f, 0.7071f, 0.0f), glm::vec3()); - //relDefaultPose = relDefaultPose * localOffset; - AnimPose localParentOffset(glm::quat(0.7071f, 0.0f, -0.7071f, 0.0f), glm::vec3()); - relDefaultPose = localParentOffset.inverse() * AnimPose(glm::quat(), relDefaultPose.trans()) * localParentOffset * AnimPose(relDefaultPose.rot(), glm::vec3()); - qCDebug(animation) << "the head translation is " << relDefaultPose.trans(); - } - _relativeDefaultPoses.push_back(relDefaultPose); diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 3302760f6b..02648d5771 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -2077,7 +2077,8 @@ void Rig::setJointRotationOffsets(const QMap& offsets) { int neckId = 62; int spine2Id = 13; if (true){ //neckIndex != -1) { - _jointRotationOffsets.insert(neckId, glm::quat(0.7071f, 0.0f, -0.7071f, 0.0f)); + _jointRotationOffsets.insert(neckId, glm::quat(0.0f, 0.7071f, 0.7071f, 0.0f) * glm::quat(0.5f, 0.5f, 0.5f, -0.5f) ); //glm::quat(0.7071f, 0.0f, -0.7071f, 0.0f) + qCDebug(animation) << "multiplied quats are " << glm::quat(0.0f, 0.7071f, 0.7071f, 0.0f) * glm::quat(0.5f, 0.5f, 0.5f, -0.5f); } if (true){ //spine2Index != -1) { _jointRotationOffsets.insert(spine2Id, glm::quat(0.5f, 0.5f, 0.5f, -0.5f)); From 1dbccc814b9471a554573d31ce85332b586039b3 Mon Sep 17 00:00:00 2001 From: amantley Date: Tue, 6 Nov 2018 17:35:53 -0800 Subject: [PATCH 138/286] working on the ik dislocation caused by the offsets, head is what I am working on --- interface/src/avatar/MyAvatar.cpp | 2 +- interface/src/avatar/MySkeletonModel.cpp | 8 ++++++-- libraries/animation/src/AnimSkeleton.cpp | 15 +++------------ libraries/animation/src/Rig.cpp | 6 +++++- libraries/animation/src/Rig.h | 2 ++ 5 files changed, 17 insertions(+), 16 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 3299bd10e7..0f9d3f2f81 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -431,7 +431,7 @@ void MyAvatar::reset(bool andRecenter, bool andReload, bool andHead) { _wasPushing = _isPushing = _isBraking = false; _follow.deactivate(); if (andReload) { - _skeletonModel->reset(); + //_skeletonModel->reset(); } if (andHead) { // which drives camera in desktop getHead()->reset(); diff --git a/interface/src/avatar/MySkeletonModel.cpp b/interface/src/avatar/MySkeletonModel.cpp index c6aae6124a..858cba69dd 100644 --- a/interface/src/avatar/MySkeletonModel.cpp +++ b/interface/src/avatar/MySkeletonModel.cpp @@ -91,6 +91,7 @@ static AnimPose computeHipsInSensorFrame(MyAvatar* myAvatar, bool isFlying) { // Called within Model::simulate call, below. void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { const FBXGeometry& geometry = getFBXGeometry(); + const QMap jointOffsetMap = _rig.getJointRotationOffsets(); Head* head = _owningAvatar->getHead(); @@ -118,8 +119,10 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { // input action is the highest priority source for head orientation. auto avatarHeadPose = myAvatar->getControllerPoseInAvatarFrame(controller::Action::HEAD); if (avatarHeadPose.isValid()) { + qCDebug(interfaceapp) << "neck joint offset " << jointOffsetMap[62]; + AnimPose jointOffset(jointOffsetMap[62], glm::vec3()); AnimPose pose(avatarHeadPose.getRotation(), avatarHeadPose.getTranslation()); - params.primaryControllerPoses[Rig::PrimaryControllerType_Head] = avatarToRigPose * pose; + params.primaryControllerPoses[Rig::PrimaryControllerType_Head] = jointOffset.inverse() * (avatarToRigPose * pose) * jointOffset; params.primaryControllerFlags[Rig::PrimaryControllerType_Head] = (uint8_t)Rig::ControllerFlags::Enabled; } else { // even though full head IK is disabled, the rig still needs the head orientation to rotate the head up and @@ -229,7 +232,8 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { params.primaryControllerFlags[Rig::PrimaryControllerType_Hips] = (uint8_t)Rig::ControllerFlags::Enabled | (uint8_t)Rig::ControllerFlags::Estimated; // set spine2 if we have hand controllers - if (myAvatar->getControllerPoseInAvatarFrame(controller::Action::RIGHT_HAND).isValid() && + qCDebug(interfaceapp) << "spine 2 joint offset " << jointOffsetMap[13]; + if (false && myAvatar->getControllerPoseInAvatarFrame(controller::Action::RIGHT_HAND).isValid() && myAvatar->getControllerPoseInAvatarFrame(controller::Action::LEFT_HAND).isValid() && !(params.primaryControllerFlags[Rig::PrimaryControllerType_Spine2] & (uint8_t)Rig::ControllerFlags::Enabled)) { diff --git a/libraries/animation/src/AnimSkeleton.cpp b/libraries/animation/src/AnimSkeleton.cpp index 2dfcdee4b0..05953e2803 100644 --- a/libraries/animation/src/AnimSkeleton.cpp +++ b/libraries/animation/src/AnimSkeleton.cpp @@ -41,19 +41,10 @@ AnimSkeleton::AnimSkeleton(const FBXGeometry& fbxGeometry, const QMap(mesh.clusters.at(j)); - if ((cluster.jointIndex == 62) || (cluster.jointIndex == 13)) { - qCDebug(animation) << "cluster joint equals index " << cluster.jointIndex; - } // AJT: mutate bind pose! this allows us to oreint the skeleton back into the authored orientaiton before // rendering, with no runtime overhead. // this works if clusters match joints one for one. - if (cluster.jointIndex == 63) { - //qCDebug(animation) << "Head"; - //qCDebug(animation) << "found a joint offset to add " << cluster.jointIndex << " " << jointOffsets[cluster.jointIndex] << " cluster " << cluster.jointIndex; - //AnimPose localOffset(glm::quat(.7071f, 0.0f, .7071f, 0.0f), glm::vec3()); - //cluster.inverseBindMatrix = (glm::mat4)localOffset.inverse() * cluster.inverseBindMatrix; - //cluster.inverseBindTransform.evalFromRawMatrix(cluster.inverseBindMatrix); - } + if (cluster.jointIndex == 62) { qCDebug(animation) << "Neck"; qCDebug(animation) << "found a joint offset to add " << cluster.jointIndex << " " << jointOffsets[cluster.jointIndex] << " cluster " << cluster.jointIndex; @@ -246,11 +237,11 @@ void AnimSkeleton::buildSkeletonFromJoints(const std::vector& joints, // remember the inverse bind pose already has the offset added into it. the total effect is offset^-1 * relDefPose * offset. // this gives us the correct transform for the joint that has been put in t-pose with an offset rotation. //relDefaultPose = relDefaultPose * _avatarTPoseOffsets[i]; - + QString jointName = getJointName(i); if (jointOffsets.contains(i)) { //QString parentIndex = getJointName(parentIndex); - AnimPose localOffset(jointOffsets[i], glm::vec3()); + AnimPose localOffset(jointOffsets[i], glm::vec3()); relDefaultPose = relDefaultPose * localOffset; } if ((parentIndex >= 0) && jointOffsets.contains(parentIndex)) { diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 02648d5771..1e20ec9a7d 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -267,7 +267,7 @@ void Rig::initJointStates(const FBXGeometry& geometry, const glm::mat4& modelOff _rigToGeometryTransform = glm::inverse(_geometryToRigTransform); setModelOffset(modelOffset); - _animSkeleton = std::make_shared(geometry,_jointRotationOffsets); + _animSkeleton = std::make_shared(geometry, _jointRotationOffsets); _internalPoseSet._relativePoses.clear(); _internalPoseSet._relativePoses = _animSkeleton->getRelativeDefaultPoses(); @@ -2084,4 +2084,8 @@ void Rig::setJointRotationOffsets(const QMap& offsets) { _jointRotationOffsets.insert(spine2Id, glm::quat(0.5f, 0.5f, 0.5f, -0.5f)); } qCDebug(animation) << "set the neck and spine2 offsets " << spine2Id << " " << neckId; +} + +const QMap& Rig::getJointRotationOffsets() const { + return _jointRotationOffsets; } \ No newline at end of file diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index 9928810026..dd7e766a49 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -128,6 +128,7 @@ public: int getJointStateCount() const; int indexOfJoint(const QString& jointName) const; QString nameOfJoint(int jointIndex) const; + const QMap& getJointRotationOffsets() const; void setModelOffset(const glm::mat4& modelOffsetMat); @@ -422,6 +423,7 @@ protected: SnapshotBlendPoseHelper _hipsBlendHelper; ControllerParameters _previousControllerParameters; + bool _alreadyInitialized { false }; }; #endif /* defined(__hifi__Rig__) */ From dfd0ce69cbade0f1ead879c597ce17f451452799 Mon Sep 17 00:00:00 2001 From: amantley Date: Tue, 6 Nov 2018 17:36:05 -0800 Subject: [PATCH 139/286] more head ik --- interface/src/avatar/MySkeletonModel.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/interface/src/avatar/MySkeletonModel.cpp b/interface/src/avatar/MySkeletonModel.cpp index 858cba69dd..6aa4d1402f 100644 --- a/interface/src/avatar/MySkeletonModel.cpp +++ b/interface/src/avatar/MySkeletonModel.cpp @@ -120,9 +120,10 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { auto avatarHeadPose = myAvatar->getControllerPoseInAvatarFrame(controller::Action::HEAD); if (avatarHeadPose.isValid()) { qCDebug(interfaceapp) << "neck joint offset " << jointOffsetMap[62]; - AnimPose jointOffset(jointOffsetMap[62], glm::vec3()); + AnimPose jointOffsetNeck(jointOffsetMap[62], glm::vec3()); + AnimPose jointOffsetSpine2(jointOffsetMap[13], glm::vec3()); AnimPose pose(avatarHeadPose.getRotation(), avatarHeadPose.getTranslation()); - params.primaryControllerPoses[Rig::PrimaryControllerType_Head] = jointOffset.inverse() * (avatarToRigPose * pose) * jointOffset; + params.primaryControllerPoses[Rig::PrimaryControllerType_Head] = jointOffsetSpine2.inverse() * jointOffsetNeck.inverse() * (avatarToRigPose * pose) * jointOffsetNeck * jointOffsetSpine2; params.primaryControllerFlags[Rig::PrimaryControllerType_Head] = (uint8_t)Rig::ControllerFlags::Enabled; } else { // even though full head IK is disabled, the rig still needs the head orientation to rotate the head up and From 189a799a3a1f8a672983b48b782a933683f00d36 Mon Sep 17 00:00:00 2001 From: NissimHadar Date: Wed, 7 Nov 2018 08:58:38 -0800 Subject: [PATCH 140/286] Added method to detect OS type. Needed for running automated tests. --- interface/src/scripting/TestScriptingInterface.cpp | 10 ++++++++++ interface/src/scripting/TestScriptingInterface.h | 7 +++++++ 2 files changed, 17 insertions(+) diff --git a/interface/src/scripting/TestScriptingInterface.cpp b/interface/src/scripting/TestScriptingInterface.cpp index c3aeb2643b..a9ba165037 100644 --- a/interface/src/scripting/TestScriptingInterface.cpp +++ b/interface/src/scripting/TestScriptingInterface.cpp @@ -199,3 +199,13 @@ void TestScriptingInterface::setOtherAvatarsReplicaCount(int count) { int TestScriptingInterface::getOtherAvatarsReplicaCount() { return qApp->getOtherAvatarsReplicaCount(); } + +QString TestScriptingInterface::getOperatingSystemType() { +#ifdef Q_OS_WIN + return "WINDOWS"; +#elif defined Q_OS_MAC + return "MACOS"; +#else + return "UNKNOWN"; +#endif +} diff --git a/interface/src/scripting/TestScriptingInterface.h b/interface/src/scripting/TestScriptingInterface.h index 4a1d1a3eeb..26e967c9b5 100644 --- a/interface/src/scripting/TestScriptingInterface.h +++ b/interface/src/scripting/TestScriptingInterface.h @@ -163,6 +163,13 @@ public slots: */ Q_INVOKABLE int getOtherAvatarsReplicaCount(); + /**jsdoc + * Returns the Operating Sytem type + * @function Test.getOperatingSystemType + * @returns {string} "WINDOWS", "MACOS" or "UNKNOWN" + */ + QString getOperatingSystemType(); + private: bool waitForCondition(qint64 maxWaitMs, std::function condition); QString _testResultsLocation; From f0dd07294e0132eb8ffb83f3f10ecd84da226d4b Mon Sep 17 00:00:00 2001 From: NissimHadar Date: Wed, 7 Nov 2018 15:10:01 -0800 Subject: [PATCH 141/286] Added the no-login option. --- interface/src/Application.cpp | 40 ++++++++++++++++++++--------------- 1 file changed, 23 insertions(+), 17 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 5caf58e992..155ef3549b 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2337,23 +2337,29 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo connect(&AndroidHelper::instance(), &AndroidHelper::enterForeground, this, &Application::enterForeground); AndroidHelper::instance().notifyLoadComplete(); #else - static int CHECK_LOGIN_TIMER = 3000; - QTimer* checkLoginTimer = new QTimer(this); - checkLoginTimer->setInterval(CHECK_LOGIN_TIMER); - checkLoginTimer->setSingleShot(true); - connect(checkLoginTimer, &QTimer::timeout, this, []() { - auto accountManager = DependencyManager::get(); - auto dialogsManager = DependencyManager::get(); - if (!accountManager->isLoggedIn()) { - Setting::Handle{"loginDialogPoppedUp", false}.set(true); - dialogsManager->showLoginDialog(); - QJsonObject loginData = {}; - loginData["action"] = "login dialog shown"; - UserActivityLogger::getInstance().logAction("encourageLoginDialog", loginData); - } - }); - Setting::Handle{"loginDialogPoppedUp", false}.set(false); - checkLoginTimer->start(); + // Do not show login dialog if requested not to on the command line + const QString HIFI_NO_LOGIN_COMMAND_LINE_KEY = "--no-login-suggestion"; + int index = arguments().indexOf(HIFI_NO_LOGIN_COMMAND_LINE_KEY); + if (index == -1) { + // request not found + static int CHECK_LOGIN_TIMER = 3000; + QTimer* checkLoginTimer = new QTimer(this); + checkLoginTimer->setInterval(CHECK_LOGIN_TIMER); + checkLoginTimer->setSingleShot(true); + connect(checkLoginTimer, &QTimer::timeout, this, []() { + auto accountManager = DependencyManager::get(); + auto dialogsManager = DependencyManager::get(); + if (!accountManager->isLoggedIn()) { + Setting::Handle{"loginDialogPoppedUp", false}.set(true); + dialogsManager->showLoginDialog(); + QJsonObject loginData = {}; + loginData["action"] = "login dialog shown"; + UserActivityLogger::getInstance().logAction("encourageLoginDialog", loginData); + } + }); + Setting::Handle{"loginDialogPoppedUp", false}.set(false); + checkLoginTimer->start(); + } #endif } From f0c2ec1fa435ffaecc5f75e04184d3f6c56d6465 Mon Sep 17 00:00:00 2001 From: David Back Date: Wed, 7 Nov 2018 16:02:15 -0800 Subject: [PATCH 142/286] entity list data-driven resizable addable/removable columns --- scripts/system/html/css/edit-style.css | 269 ++++-------- scripts/system/html/entityList.html | 67 +-- scripts/system/html/js/entityList.js | 580 ++++++++++++++++++------- scripts/system/html/js/listView.js | 17 +- 4 files changed, 530 insertions(+), 403 deletions(-) diff --git a/scripts/system/html/css/edit-style.css b/scripts/system/html/css/edit-style.css index cf7124d9eb..d0a83b9937 100644 --- a/scripts/system/html/css/edit-style.css +++ b/scripts/system/html/css/edit-style.css @@ -110,6 +110,7 @@ thead { tbody { width: 100%; + display: block; } tfoot { @@ -185,13 +186,18 @@ th { td { overflow: hidden; - text-overflow: ellipsis; + text-overflow: clip; white-space: nowrap; word-wrap: nowrap; padding-left: 12px; padding-right: 12px; } +td.hidden { + padding-left: 0px; + padding-right: 0px; +} + td.url { white-space: nowrap; overflow: hidden; @@ -777,20 +783,15 @@ hr { color: #252525; } -.multiselect { - position: relative; -} -.select-box { +.multiselect-box { position: absolute; } -.select-box select { +.multiselect-box select { font-family: FiraSans-SemiBold; font-size: 15px; color: #afafaf; background-color: #252525; border: none; - height: 28px; - width: 107px; text-align-last: center; } .over-select { @@ -800,6 +801,41 @@ hr { top: 0; bottom: 0; } +.multiselect-options { + position: absolute; + display: none; + border: none; +} +.multiselect-options span { + font-family: hifi-glyphs; + font-size: 13px; + color: #000000; +} +.multiselect-options label { + z-index: 2; + display: block; + font-family: FiraSans-SemiBold; + font-size: 11px; + color: #000000; + background-color: #afafaf; +} +.multiselect-options label:hover { + background-color: #1e90ff; +} +.multiselect-options input[type=checkbox] + label { + background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACwAAAAsCAYAAAAehFoBAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAC4jAAAuIwF4pT92AAAAB3RJTUUH4goSADUOYnF4LQAAABl0RVh0Q29tbWVudABDcmVhdGVkIHdpdGggR0lNUFeBDhcAAAIMSURBVFjD7ZmxkqowFIZ/7mwJPen1AezV3t6hFvrQhweAHvrQ8wL2xt4HwD7ppd+tvHOvu0gCYdEZTsmAfpNzzpcTcAB84o3iD94sZuCx4+Pxwvl8dl4JcL1ef84lMQPPwBZDSgkp5XsASylBKUUYhhBCvDbw7XYDpRRKKTRNA8YYOOevC5ymKZRS/13jnHdCTwLMOW8tAc45GGNomuY1gKuq6lxFIQQopdMDXy4X5HmudW8URdMCSynBGNOG3Ww20wHf9dVWl4+wbav7a8CMsW9G+Cm22+1T2F8BzvMc1+u18z5CCJIkseNhKSX2+z2qqjLWl84zhBAURQHXde0A31Oa57nWbqSrLwDwPA9FUcD3fTtb82NKu8QOAHVda+srSRJt2E7gtpQKIXA4HH6csmzpyxj4dDo9TalSCpRS1HX9TV86RujSlxGwlBJpmnY+rJRCGIZ/s2BTX9qnZgBwHAee52mJ/l7nx+PRqr6MVtj3fZRlaVRf/5aGDX0Z17DrusiyrHfqhuqrt9aiKEIcx4OBTfU1aOMIggBlWYIQ0utP+uhr8CyxXC5RFIUxdBAE1srKePgxbcbVamWlnAZNa7rNSAhBlmWv8yLlWTPa0Nco83BbM2ZZZsUIowzwj80YxzEWi8VoB4IPGz9yb0YhBHa73agnGGtHJNd1R4ed9FVV33Awf6ebgd8b+Av9A/rq6s3hjgAAAABJRU5ErkJggg=='); + background-size: 11px 11px; + background-position: top 5px left 14px; +} +.multiselect-options input[type=checkbox]:enabled + label:hover { + background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACwAAAAsCAYAAAAehFoBAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAC4jAAAuIwF4pT92AAAAB3RJTUUH4goSADUOYnF4LQAAABl0RVh0Q29tbWVudABDcmVhdGVkIHdpdGggR0lNUFeBDhcAAAIMSURBVFjD7ZmxkqowFIZ/7mwJPen1AezV3t6hFvrQhweAHvrQ8wL2xt4HwD7ppd+tvHOvu0gCYdEZTsmAfpNzzpcTcAB84o3iD94sZuCx4+Pxwvl8dl4JcL1ef84lMQPPwBZDSgkp5XsASylBKUUYhhBCvDbw7XYDpRRKKTRNA8YYOOevC5ymKZRS/13jnHdCTwLMOW8tAc45GGNomuY1gKuq6lxFIQQopdMDXy4X5HmudW8URdMCSynBGNOG3Ww20wHf9dVWl4+wbav7a8CMsW9G+Cm22+1T2F8BzvMc1+u18z5CCJIkseNhKSX2+z2qqjLWl84zhBAURQHXde0A31Oa57nWbqSrLwDwPA9FUcD3fTtb82NKu8QOAHVda+srSRJt2E7gtpQKIXA4HH6csmzpyxj4dDo9TalSCpRS1HX9TV86RujSlxGwlBJpmnY+rJRCGIZ/s2BTX9qnZgBwHAee52mJ/l7nx+PRqr6MVtj3fZRlaVRf/5aGDX0Z17DrusiyrHfqhuqrt9aiKEIcx4OBTfU1aOMIggBlWYIQ0utP+uhr8CyxXC5RFIUxdBAE1srKePgxbcbVamWlnAZNa7rNSAhBlmWv8yLlWTPa0Nco83BbM2ZZZsUIowzwj80YxzEWi8VoB4IPGz9yb0YhBHa73agnGGtHJNd1R4ed9FVV33Awf6ebgd8b+Av9A/rq6s3hjgAAAABJRU5ErkJggg=='); +} +.multiselect-options input[type=checkbox]:checked + label { + background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACwAAAAsCAYAAAAehFoBAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAC4jAAAuIwF4pT92AAAAB3RJTUUH4goSADMveELP9QAAABl0RVh0Q29tbWVudABDcmVhdGVkIHdpdGggR0lNUFeBDhcAAAIqSURBVFjD7ZmxkqowFIb/7GwJPfT6APZib+9QC33o4QGghz70vIC9sfcBsE966bPNWlxnlQTDRWc4JUT4hpPz5SQSAAofFF/4sJiBx47v+wun04m8E+B6vVbzlJiBZ2CLIYRQQgj1EcBCCEUpRRRF4Jyrtwa+Xq+glEJKia7rkKYpGGPqbYHzPFdSyn+uMcZ6oScBZowpzvmje0jTVHVd9x7ATdMoxtjTMZxzUErV5MDn81mVZak1No7jab+wEEKlaaoNGwQBmQz4pq9H8/IeNo5jMmnRpWmKeyP8FZvN5insfwEuy1JdLpfecb7vI8uy3tb2Szelu91ONU1jtP9jjKmmabRgq6qC4zh2VrpbSsuy1FqNdPUFAK7roqoqeJ6ntXH4Mk1pn9gBoG1bbX1lWaYN2wv8KKWcc+z3+z+7LFv6MgY+Ho9PUyqlBKUUbduqe33pGKFPX0bAQgiV53nvj6WUiKIIt2K0qS/tXTMAEELguq6W6H/nOQ6Hg1V9GX1hz/NIXdckCALtB7Vta1VfxnPYcRwURUEeNSGmYaqvwVqL45gkSfIysKm+Xlo4wjAkdV3D9/1BLxmir5d7ieVySaqqMoYOw3CwEV5ufkyLcbVaIUkSq2d1xt2abjH6vo+iKKwfLA5uL58Vow19jdIPPyrGoiisGGGUBv6+GJMkwWKxGO2M+dvGQ36LEZxztd1uRz0Qt7ZFchwHY8NOelQ1NAjm/+lm4M8G/gH2zx33BSr7jAAAAABJRU5ErkJggg=='); +} +.multiselect-options input[type=checkbox]:checked + label:hover { + background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACwAAAAsCAYAAAAehFoBAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAC4jAAAuIwF4pT92AAAAB3RJTUUH4goSADMveELP9QAAABl0RVh0Q29tbWVudABDcmVhdGVkIHdpdGggR0lNUFeBDhcAAAIqSURBVFjD7ZmxkqowFIb/7GwJPfT6APZib+9QC33o4QGghz70vIC9sfcBsE966bPNWlxnlQTDRWc4JUT4hpPz5SQSAAofFF/4sJiBx47v+wun04m8E+B6vVbzlJiBZ2CLIYRQQgj1EcBCCEUpRRRF4Jyrtwa+Xq+glEJKia7rkKYpGGPqbYHzPFdSyn+uMcZ6oScBZowpzvmje0jTVHVd9x7ATdMoxtjTMZxzUErV5MDn81mVZak1No7jab+wEEKlaaoNGwQBmQz4pq9H8/IeNo5jMmnRpWmKeyP8FZvN5insfwEuy1JdLpfecb7vI8uy3tb2Szelu91ONU1jtP9jjKmmabRgq6qC4zh2VrpbSsuy1FqNdPUFAK7roqoqeJ6ntXH4Mk1pn9gBoG1bbX1lWaYN2wv8KKWcc+z3+z+7LFv6MgY+Ho9PUyqlBKUUbduqe33pGKFPX0bAQgiV53nvj6WUiKIIt2K0qS/tXTMAEELguq6W6H/nOQ6Hg1V9GX1hz/NIXdckCALtB7Vta1VfxnPYcRwURUEeNSGmYaqvwVqL45gkSfIysKm+Xlo4wjAkdV3D9/1BLxmir5d7ieVySaqqMoYOw3CwEV5ufkyLcbVaIUkSq2d1xt2abjH6vo+iKKwfLA5uL58Vow19jdIPPyrGoiisGGGUBv6+GJMkwWKxGO2M+dvGQ36LEZxztd1uRz0Qt7ZFchwHY8NOelQ1NAjm/+lm4M8G/gH2zx33BSr7jAAAAABJRU5ErkJggg=='); +} div.refresh { box-sizing: border-box; @@ -1112,57 +1148,32 @@ body#entity-list-body { padding-bottom: 24px; } -#filter-type-select-box select { +#filter-type-multiselect-box select { border-radius: 14.5px; + width: 107px; + height: 28px; } -#filter-type-checkboxes { - position: absolute; - z-index: 2; +#filter-type-options { + position: absolute; top: 48px; - display: none; - border: none; } -#filter-type-checkboxes div { - position: relative; - height: 22px; +#filter-type-options div { + position: relative; + height: 22px; } -#filter-type-checkboxes span { +#filter-type-options span { position: relative; top: 3px; - font-family: hifi-glyphs; - font-size: 13px; - color: #000000; padding-left: 6px; padding-right: 4px; } -#filter-type-checkboxes label { - position: absolute; - top: -20px; +#filter-type-options label { + position: absolute; + top: -20px; z-index: 2; - display: block; - font-family: FiraSans-SemiBold; - font-size: 11px; - color: #000000; - background-color: #afafaf; - width: 200px; - height: 22px; - padding-top: 1px; -} -#filter-type-checkboxes label:hover { - background-color: #1e90ff; -} -#filter-type-checkboxes input[type=checkbox] + label { - background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACwAAAAsCAYAAAAehFoBAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAC4jAAAuIwF4pT92AAAAB3RJTUUH4goSADUOYnF4LQAAABl0RVh0Q29tbWVudABDcmVhdGVkIHdpdGggR0lNUFeBDhcAAAIMSURBVFjD7ZmxkqowFIZ/7mwJPen1AezV3t6hFvrQhweAHvrQ8wL2xt4HwD7ppd+tvHOvu0gCYdEZTsmAfpNzzpcTcAB84o3iD94sZuCx4+Pxwvl8dl4JcL1ef84lMQPPwBZDSgkp5XsASylBKUUYhhBCvDbw7XYDpRRKKTRNA8YYOOevC5ymKZRS/13jnHdCTwLMOW8tAc45GGNomuY1gKuq6lxFIQQopdMDXy4X5HmudW8URdMCSynBGNOG3Ww20wHf9dVWl4+wbav7a8CMsW9G+Cm22+1T2F8BzvMc1+u18z5CCJIkseNhKSX2+z2qqjLWl84zhBAURQHXde0A31Oa57nWbqSrLwDwPA9FUcD3fTtb82NKu8QOAHVda+srSRJt2E7gtpQKIXA4HH6csmzpyxj4dDo9TalSCpRS1HX9TV86RujSlxGwlBJpmnY+rJRCGIZ/s2BTX9qnZgBwHAee52mJ/l7nx+PRqr6MVtj3fZRlaVRf/5aGDX0Z17DrusiyrHfqhuqrt9aiKEIcx4OBTfU1aOMIggBlWYIQ0utP+uhr8CyxXC5RFIUxdBAE1srKePgxbcbVamWlnAZNa7rNSAhBlmWv8yLlWTPa0Nco83BbM2ZZZsUIowzwj80YxzEWi8VoB4IPGz9yb0YhBHa73agnGGtHJNd1R4ed9FVV33Awf6ebgd8b+Av9A/rq6s3hjgAAAABJRU5ErkJggg=='); - background-size: 11px 11px; - background-position: top 5px left 14px; -} -#filter-type-checkboxes input[type=checkbox]:checked + label { - background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACwAAAAsCAYAAAAehFoBAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAC4jAAAuIwF4pT92AAAAB3RJTUUH4goSADMveELP9QAAABl0RVh0Q29tbWVudABDcmVhdGVkIHdpdGggR0lNUFeBDhcAAAIqSURBVFjD7ZmxkqowFIb/7GwJPfT6APZib+9QC33o4QGghz70vIC9sfcBsE966bPNWlxnlQTDRWc4JUT4hpPz5SQSAAofFF/4sJiBx47v+wun04m8E+B6vVbzlJiBZ2CLIYRQQgj1EcBCCEUpRRRF4Jyrtwa+Xq+glEJKia7rkKYpGGPqbYHzPFdSyn+uMcZ6oScBZowpzvmje0jTVHVd9x7ATdMoxtjTMZxzUErV5MDn81mVZak1No7jab+wEEKlaaoNGwQBmQz4pq9H8/IeNo5jMmnRpWmKeyP8FZvN5insfwEuy1JdLpfecb7vI8uy3tb2Szelu91ONU1jtP9jjKmmabRgq6qC4zh2VrpbSsuy1FqNdPUFAK7roqoqeJ6ntXH4Mk1pn9gBoG1bbX1lWaYN2wv8KKWcc+z3+z+7LFv6MgY+Ho9PUyqlBKUUbduqe33pGKFPX0bAQgiV53nvj6WUiKIIt2K0qS/tXTMAEELguq6W6H/nOQ6Hg1V9GX1hz/NIXdckCALtB7Vta1VfxnPYcRwURUEeNSGmYaqvwVqL45gkSfIysKm+Xlo4wjAkdV3D9/1BLxmir5d7ieVySaqqMoYOw3CwEV5ufkyLcbVaIUkSq2d1xt2abjH6vo+iKKwfLA5uL58Vow19jdIPPyrGoiisGGGUBv6+GJMkwWKxGO2M+dvGQ36LEZxztd1uRz0Qt7ZFchwHY8NOelQ1NAjm/+lm4M8G/gH2zx33BSr7jAAAAABJRU5ErkJggg=='); - background-size: 11px 11px; - background-position: top 5px left 14px; -} -#filter-type-checkboxes input[type=checkbox]:hover + label { - background-color: #1e90ff; + height: 22px; + width: 200px; + padding-top: 1px; } #filter-search-and-icon { @@ -1228,6 +1239,37 @@ input[type=button]#export { color: #afafaf; } +#entity-table-columns-multiselect { + position: absolute; + top: 51px; + right: 22px; +} +#entity-table-columns-multiselect-box select { + height: 28px; + width: 20px; + background-color: #1c1c1c; + border-top-right-radius: 7px; +} +#entity-table-columns-options { + position: absolute; + top: 50px; + right: 110px; +} +#entity-table-columns-options div { + position: relative; + height: 22px; +} +#entity-table-columns-options label { + position: absolute; + top: -22px; + height: 22px; + width: 100px; + padding-top: 4px; +} +#entity-table-columns-options input[type=checkbox] + label { + padding-left: 30px; +} + #entity-table-scroll { /* Height is set by JavaScript. */ width: 100%; @@ -1281,10 +1323,6 @@ input[type=button]#export { overflow: hidden; } -.verticesCount, .texturesCount, .texturesSize, .drawCalls { - text-align: right; -} - #entity-table th { display: inline-block; box-sizing: border-box; @@ -1307,14 +1345,6 @@ input[type=button]#export { left: 4px; } -#entity-table th#entity-hasScript { - overflow: visible; -} - -#entity-table th#entity-hasScript .glyph { - text-transform: none; -} - #entity-table thead .sort-order { display: inline-block; width: 8px; @@ -1322,128 +1352,21 @@ input[type=button]#export { vertical-align: middle; } -#entity-table th #info-toggle { - display: inline-block; - position: absolute; - left: initial; - right: 0; - width: 11px; - background-color: #1c1c1c; - z-index: 100; -} -#entity-table th #info-toggle span { - position: relative; - left: -2px; -} - -th#entity-hasTransparent .glyph { - font-weight: normal; - font-size: 24px !important; - margin: -6px; - position: relative; - top: -6px; -} -th#entity-hasTransparent .sort-order { - position: relative; - top: -4px; +#entity-table thead .resizer { + position: absolute; + top: 1px; + height: 26px; + width: 10px; + cursor: col-resize; } #entity-table td { box-sizing: border-box; } - #entity-table td.glyph { text-align: center; padding: 0; } -#entity-table td.hasTransparent.glyph { - font-size: 22px; - position: relative; - top: -1px; -} - -#entity-table td.isBaked.glyph { - font-size: 22px; - position: relative; - top: -1px; -} - -#col-type { - width: 16%; -} -#col-name { - width: 34%; -} -#col-url { - width: 34%; -} -#col-locked, #col-visible { - width: 9%; -} -#col-verticesCount, #col-texturesCount, #col-texturesSize, #col-hasTransparent, #col-isBaked, #col-drawCalls, #col-hasScript { - width: 0; -} - -.showExtraInfo #col-type { - width: 10%; -} -.showExtraInfo #col-name { - width: 19%; -} -.showExtraInfo #col-url { - width: 19%; -} -.showExtraInfo #col-locked, .showExtraInfo #col-visible { - width: 4%; -} -.showExtraInfo #col-verticesCount { - width: 8%; -} -.showExtraInfo #col-texturesCount { - width: 8%; -} -.showExtraInfo #col-texturesSize { - width: 10%; -} -.showExtraInfo #col-hasTransparent { - width: 4%; -} -.showExtraInfo #col-isBaked { - width: 8%; -} -.showExtraInfo #col-drawCalls { - width: 8%; -} -.showExtraInfo #col-hasScript { - width: 6%; -} - -th#entity-verticesCount, th#entity-texturesCount, th#entity-texturesSize, th#entity-hasTransparent, th#entity-isBaked, th#entity-drawCalls, -th#entity-hasScript { - display: none; -} - -.verticesCount, .texturesCount, .texturesSize, .hasTransparent, .isBaked, .drawCalls, .hasScript { - display: none; -} - -#entity-visible { - border: none; -} - -.showExtraInfo #entity-verticesCount, .showExtraInfo #entity-texturesCount, .showExtraInfo #entity-texturesSize, -.showExtraInfo #entity-hasTransparent, .showExtraInfo #entity-isBaked, .showExtraInfo #entity-drawCalls, .showExtraInfo #entity-hasScript { - display: inline-block; -} - -.showExtraInfo .verticesCount, .showExtraInfo .texturesCount, .showExtraInfo .texturesSize, .showExtraInfo .hasTransparent, -.showExtraInfo .isBaked, .showExtraInfo .drawCalls, .showExtraInfo .hasScript { - display: table-cell; -} - -.showExtraInfo #entity-visible { - border-right: 1px solid #575757; -} #properties-base { border-top: none !important; diff --git a/scripts/system/html/entityList.html b/scripts/system/html/entityList.html index 2e7ac58ac1..84397fdb5a 100644 --- a/scripts/system/html/entityList.html +++ b/scripts/system/html/entityList.html @@ -31,14 +31,14 @@
-
-
+
+
-
+
@@ -53,54 +53,21 @@
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + +
TypeDNameFileVertsTextsText MBBakedDrawsk
+
+
+ +
+
+
+ +
+
There are no entities to display. Please check your filters or create an entity to begin.
diff --git a/scripts/system/html/js/entityList.js b/scripts/system/html/js/entityList.js index 41c957c4fa..0d75f6c1d1 100644 --- a/scripts/system/html/js/entityList.js +++ b/scripts/system/html/js/entityList.js @@ -10,36 +10,103 @@ const ASCENDING_SORT = 1; const DESCENDING_SORT = -1; const ASCENDING_STRING = '▴'; const DESCENDING_STRING = '▾'; -const LOCKED_GLYPH = ""; -const VISIBLE_GLYPH = ""; -const TRANSPARENCY_GLYPH = ""; -const BAKED_GLYPH = ""; -const SCRIPT_GLYPH = "k"; const BYTES_PER_MEGABYTE = 1024 * 1024; const IMAGE_MODEL_NAME = 'default-image-model.fbx'; const COLLAPSE_EXTRA_INFO = "E"; const EXPAND_EXTRA_INFO = "D"; const FILTER_IN_VIEW_ATTRIBUTE = "pressed"; const WINDOW_NONVARIABLE_HEIGHT = 227; -const NUM_COLUMNS = 12; const EMPTY_ENTITY_ID = "0"; const MAX_LENGTH_RADIUS = 9; +const MINIMUM_COLUMN_WIDTH = 24; +const SCROLLBAR_WIDTH = 20; +const RESIZER_WIDTH = 10; const DELETE = 46; // Key code for the delete key. const KEY_P = 80; // Key code for letter p used for Parenting hotkey. -const COLUMN_INDEX = { - TYPE: 0, - NAME: 1, - URL: 2, - LOCKED: 3, - VISIBLE: 4, - VERTICLES_COUNT: 5, - TEXTURES_COUNT: 6, - TEXTURES_SIZE: 7, - HAS_TRANSPARENT: 8, - IS_BAKED: 9, - DRAW_CALLS: 10, - HAS_SCRIPT: 11 +const COLUMNS = { + type: { + columnHeader: "Type", + propertyID: "type", + initialWidth: 0.16, + initiallyShown: true, + alwaysShown: true, + }, + name: { + columnHeader: "Name", + propertyID: "name", + initialWidth: 0.34, + initiallyShown: true, + alwaysShown: true, + }, + url: { + columnHeader: "File", + dropdownLabel: "File", + propertyID: "url", + initialWidth: 0.34, + initiallyShown: true, + }, + locked: { + columnHeader: "", + glyph: true, + propertyID: "locked", + initialWidth: 0.08, + initiallyShown: true, + alwaysShown: true, + }, + visible: { + columnHeader: "", + glyph: true, + propertyID: "visible", + initialWidth: 0.08, + initiallyShown: true, + alwaysShown: true, + }, + verticesCount: { + columnHeader: "Verts", + dropdownLabel: "Vertices", + propertyID: "verticesCount", + initialWidth: 0.08, + }, + texturesCount: { + columnHeader: "Texts", + dropdownLabel: "Textures", + propertyID: "texturesCount", + initialWidth: 0.08, + }, + texturesSize: { + columnHeader: "Text MB", + dropdownLabel: "Texture Size", + propertyID: "texturesSize", + initialWidth: 0.10, + }, + hasTransparent: { + columnHeader: "", + glyph: true, + dropdownLabel: "Transparency", + propertyID: "hasTransparent", + initialWidth: 0.04, + }, + isBaked: { + columnHeader: "", + glyph: true, + dropdownLabel: "Baked", + propertyID: "isBaked", + initialWidth: 0.08, + }, + drawCalls: { + columnHeader: "Draws", + dropdownLabel: "Draws", + propertyID: "drawCalls", + initialWidth: 0.08, + }, + hasScript: { + columnHeader: "k", + glyph: true, + dropdownLabel: "Script", + propertyID: "hasScript", + initialWidth: 0.06, + }, }; const COMPARE_ASCENDING = function(a, b) { @@ -100,11 +167,18 @@ var entityList = null; // The ListView */ var entityListContextMenu = null; -var currentSortColumn = 'type'; +var currentSortColumn = null; var currentSortOrder = ASCENDING_SORT; +var elSortOrders = {}; var typeFilters = []; var isFilterInView = false; -var showExtraInfo = false; + +var columns = []; +var columnsByID = {}; +var currentResizeEl = null; +var startResizeEvent = null; +var resizeColumnIndex = 0; +var startThClick = null; const ENABLE_PROFILING = false; var profileIndent = ''; @@ -129,67 +203,28 @@ debugPrint = function (message) { function loaded() { openEventBridge(function() { elEntityTable = document.getElementById("entity-table"); + elEntityTableHeader = document.getElementById("entity-table-header"); elEntityTableBody = document.getElementById("entity-table-body"); elEntityTableScroll = document.getElementById("entity-table-scroll"); - elEntityTableHeaderRow = document.querySelectorAll("#entity-table thead th"); elRefresh = document.getElementById("refresh"); elToggleLocked = document.getElementById("locked"); elToggleVisible = document.getElementById("visible"); elDelete = document.getElementById("delete"); - elFilterTypeSelectBox = document.getElementById("filter-type-select-box"); + elFilterTypeMultiselectBox = document.getElementById("filter-type-multiselect-box"); elFilterTypeText = document.getElementById("filter-type-text"); - elFilterTypeCheckboxes = document.getElementById("filter-type-checkboxes"); + elFilterTypeOptions = document.getElementById("filter-type-options"); elFilterSearch = document.getElementById("filter-search"); elFilterInView = document.getElementById("filter-in-view") elFilterRadius = document.getElementById("filter-radius"); elExport = document.getElementById("export"); elPal = document.getElementById("pal"); - elInfoToggle = document.getElementById("info-toggle"); - elInfoToggleGlyph = elInfoToggle.firstChild; elSelectedEntitiesCount = document.getElementById("selected-entities-count"); elVisibleEntitiesCount = document.getElementById("visible-entities-count"); elNoEntitiesMessage = document.getElementById("no-entities"); + elColumnsMultiselectBox = document.getElementById("entity-table-columns-multiselect-box"); + elColumnsOptions = document.getElementById("entity-table-columns-options"); document.body.onclick = onBodyClick; - document.getElementById("entity-name").onclick = function() { - setSortColumn('name'); - }; - document.getElementById("entity-type").onclick = function() { - setSortColumn('type'); - }; - document.getElementById("entity-url").onclick = function() { - setSortColumn('url'); - }; - document.getElementById("entity-locked").onclick = function() { - setSortColumn('locked'); - }; - document.getElementById("entity-visible").onclick = function() { - setSortColumn('visible'); - }; - document.getElementById("entity-verticesCount").onclick = function() { - setSortColumn('verticesCount'); - }; - document.getElementById("entity-texturesCount").onclick = function() { - setSortColumn('texturesCount'); - }; - document.getElementById("entity-texturesSize").onclick = function() { - setSortColumn('texturesSize'); - }; - document.getElementById("entity-hasTransparent").onclick = function() { - setSortColumn('hasTransparent'); - }; - document.getElementById("entity-isBaked").onclick = function() { - setSortColumn('isBaked'); - }; - document.getElementById("entity-drawCalls").onclick = function() { - setSortColumn('drawCalls'); - }; - document.getElementById("entity-hasScript").onclick = function() { - setSortColumn('hasScript'); - }; - elRefresh.onclick = function() { - refreshEntities(); - }; elToggleLocked.onclick = function() { EventBridge.emitWebEvent(JSON.stringify({ type: 'toggleLocked' })); }; @@ -205,52 +240,139 @@ function loaded() { elDelete.onclick = function() { EventBridge.emitWebEvent(JSON.stringify({ type: 'delete' })); }; - elFilterTypeSelectBox.onclick = onToggleTypeDropdown; + elRefresh.onclick = refreshEntities; + elFilterTypeMultiselectBox.onclick = onToggleTypeDropdown; elFilterSearch.onkeyup = refreshEntityList; elFilterSearch.onsearch = refreshEntityList; - elFilterInView.onclick = toggleFilterInView; + elFilterInView.onclick = onToggleFilterInView; elFilterRadius.onkeyup = onRadiusChange; elFilterRadius.onchange = onRadiusChange; - elFilterRadius.onclick = onRadiusChange; - elInfoToggle.onclick = toggleInfo; + elColumnsMultiselectBox.onclick = onToggleColumnsDropdown; // create filter type dropdown checkboxes with label and icon for each type for (let i = 0; i < FILTER_TYPES.length; ++i) { let type = FILTER_TYPES[i]; let typeFilterID = "filter-type-" + type; + let elDiv = document.createElement('div'); - let elLabel = document.createElement('label'); - elLabel.setAttribute("for", typeFilterID); - elLabel.innerText = type; - let elSpan = document.createElement('span'); - elSpan.setAttribute("class", "typeIcon"); - elSpan.innerHTML = ICON_FOR_TYPE[type]; + elDiv.onclick = onToggleTypeFilter; + elFilterTypeOptions.appendChild(elDiv); + let elInput = document.createElement('input'); elInput.setAttribute("type", "checkbox"); elInput.setAttribute("id", typeFilterID); elInput.setAttribute("filterType", type); elInput.checked = true; // all types are checked initially - toggleTypeFilter(elInput, false); // add all types to the initial types filter elDiv.appendChild(elInput); - elLabel.insertBefore(elSpan, elLabel.childNodes[0]); + + let elLabel = document.createElement('label'); + elLabel.setAttribute("for", typeFilterID); + elLabel.innerText = type; elDiv.appendChild(elLabel); - elFilterTypeCheckboxes.appendChild(elDiv); - elDiv.onclick = onToggleTypeFilter; + + let elSpan = document.createElement('span'); + elSpan.setAttribute("class", "typeIcon"); + elSpan.innerHTML = ICON_FOR_TYPE[type]; + + elLabel.insertBefore(elSpan, elLabel.childNodes[0]); + + toggleTypeFilter(elInput, false); // add all types to the initial types filter } + // create columns + elHeaderTr = document.createElement("tr"); + elEntityTableHeader.appendChild(elHeaderTr); + let columnIndex = 0; + for (let columnID in COLUMNS) { + let columnData = COLUMNS[columnID]; + + let thID = "entity-" + columnID; + let elTh = document.createElement("th"); + elTh.setAttribute("id", thID); + elTh.setAttribute("data-resizable-column-id", thID); + if (columnData.glyph) { + let elGlyph = document.createElement("span"); + elGlyph.className = "glyph"; + elGlyph.innerHTML = columnData.columnHeader; + elTh.appendChild(elGlyph); + } else { + elTh.innerText = columnData.columnHeader; + } + elTh.onmousedown = function() { + startThClick = this; + }; + elTh.onmouseup = function() { + if (startThClick === this) { + setSortColumn(columnID); + } + startThClick = null; + }; + + let elResizer = document.createElement("span"); + elResizer.className = "resizer"; + elResizer.innerHTML = " "; + elResizer.setAttribute("columnIndex", columnIndex); + elResizer.onmousedown = onStartResize; + elTh.appendChild(elResizer); + + let elSortOrder = document.createElement("span"); + elSortOrder.className = "sort-order"; + elTh.appendChild(elSortOrder); + elHeaderTr.appendChild(elTh); + + elSortOrders[columnID] = document.querySelector('#' + thID + ' .sort-order'); + if (currentSortColumn === null) { + currentSortColumn = columnID; + } + + // add column to columns dropdown if it is not set to be always shown + if (columnData.alwaysShown !== true) { + let columnDropdownID = "entity-table-column-" + columnID; + + let elDiv = document.createElement('div'); + elDiv.onclick = onToggleColumn; + elColumnsOptions.appendChild(elDiv); + + let elInput = document.createElement('input'); + elInput.setAttribute("type", "checkbox"); + elInput.setAttribute("id", columnDropdownID); + elInput.setAttribute("columnID", columnID); + elInput.checked = columnData.initiallyShown === true; + elDiv.appendChild(elInput); + + let elLabel = document.createElement('label'); + elLabel.setAttribute("for", columnDropdownID); + elLabel.innerText = columnData.dropdownLabel; + elDiv.appendChild(elLabel); + } + + let initialWidth = columnData.initiallyShown === true ? columnData.initialWidth : 0; + columns.push({ + columnID: columnID, + elTh: elTh, + elResizer: elResizer, + width: initialWidth, + data: columnData + }); + columnsByID[columnID] = columns[columnIndex]; + + ++columnIndex; + } + + elEntityTableHeaderRow = document.querySelectorAll("#entity-table thead th"); + entityList = new ListView(elEntityTableBody, elEntityTableScroll, elEntityTableHeaderRow, createRow, updateRow, clearRow, WINDOW_NONVARIABLE_HEIGHT); entityListContextMenu = new EntityListContextMenu(); - function startRenamingEntity(entityID) { let entity = entitiesByID[entityID]; if (!entity || entity.locked || !entity.elRow) { return; } - let elCell = entity.elRow.childNodes[COLUMN_INDEX.NAME]; + let elCell = entity.elRow.childNodes[getColumnIndex("name")]; let elRenameInput = document.createElement("input"); elRenameInput.setAttribute('class', 'rename-entity'); elRenameInput.value = entity.name; @@ -467,6 +589,7 @@ function loaded() { PROFILE("update-dom", function() { entityList.itemData = visibleEntities; entityList.refresh(); + updateColumnWidths(); }); refreshFooter(); @@ -549,26 +672,12 @@ function loaded() { refreshNoEntitiesMessage(); } - var elSortOrder = { - name: document.querySelector('#entity-name .sort-order'), - type: document.querySelector('#entity-type .sort-order'), - url: document.querySelector('#entity-url .sort-order'), - locked: document.querySelector('#entity-locked .sort-order'), - visible: document.querySelector('#entity-visible .sort-order'), - verticesCount: document.querySelector('#entity-verticesCount .sort-order'), - texturesCount: document.querySelector('#entity-texturesCount .sort-order'), - texturesSize: document.querySelector('#entity-texturesSize .sort-order'), - hasTransparent: document.querySelector('#entity-hasTransparent .sort-order'), - isBaked: document.querySelector('#entity-isBaked .sort-order'), - drawCalls: document.querySelector('#entity-drawCalls .sort-order'), - hasScript: document.querySelector('#entity-hasScript .sort-order'), - }; function setSortColumn(column) { PROFILE("set-sort-column", function() { if (currentSortColumn === column) { currentSortOrder *= -1; } else { - elSortOrder[currentSortColumn].innerHTML = ""; + elSortOrders[currentSortColumn].innerHTML = ""; currentSortColumn = column; currentSortOrder = ASCENDING_SORT; } @@ -576,8 +685,9 @@ function loaded() { refreshEntityList(); }); } + function refreshSortOrder() { - elSortOrder[currentSortColumn].innerHTML = currentSortOrder === ASCENDING_SORT ? ASCENDING_STRING : DESCENDING_STRING; + elSortOrders[currentSortColumn].innerHTML = currentSortOrder === ASCENDING_SORT ? ASCENDING_STRING : DESCENDING_STRING; } function refreshEntities() { @@ -651,54 +761,33 @@ function loaded() { return notFound; } - function isGlyphColumn(columnIndex) { - return columnIndex === COLUMN_INDEX.LOCKED || columnIndex === COLUMN_INDEX.VISIBLE || - columnIndex === COLUMN_INDEX.HAS_TRANSPARENT || columnIndex === COLUMN_INDEX.IS_BAKED || - columnIndex === COLUMN_INDEX.HAS_SCRIPT; - } - function createRow() { - let row = document.createElement("tr"); - for (let i = 0; i < NUM_COLUMNS; i++) { - let column = document.createElement("td"); - if (isGlyphColumn(i)) { - column.className = 'glyph'; - } - row.appendChild(column); - } - row.oncontextmenu = onRowContextMenu; - row.onclick = onRowClicked; - row.ondblclick = onRowDoubleClicked; - return row; + let elRow = document.createElement("tr"); + columns.forEach(function(column) { + let elRowColumn = document.createElement("td"); + elRowColumn.className = getColumnClassName(column.columnID); + elRow.appendChild(elRowColumn); + }); + elRow.oncontextmenu = onRowContextMenu; + elRow.onclick = onRowClicked; + elRow.ondblclick = onRowDoubleClicked; + return elRow; } function updateRow(elRow, itemData) { // update all column texts and glyphs to this entity's data - let typeCell = elRow.childNodes[COLUMN_INDEX.TYPE]; - typeCell.innerText = itemData.type; - let nameCell = elRow.childNodes[COLUMN_INDEX.NAME]; - nameCell.innerText = itemData.name; - let urlCell = elRow.childNodes[COLUMN_INDEX.URL]; - urlCell.innerText = itemData.url; - let lockedCell = elRow.childNodes[COLUMN_INDEX.LOCKED]; - lockedCell.innerHTML = itemData.locked ? LOCKED_GLYPH : null; - let visibleCell = elRow.childNodes[COLUMN_INDEX.VISIBLE]; - visibleCell.innerHTML = itemData.visible ? VISIBLE_GLYPH : null; - let verticesCountCell = elRow.childNodes[COLUMN_INDEX.VERTICLES_COUNT]; - verticesCountCell.innerText = itemData.verticesCount; - let texturesCountCell = elRow.childNodes[COLUMN_INDEX.TEXTURES_COUNT]; - texturesCountCell.innerText = itemData.texturesCount; - let texturesSizeCell = elRow.childNodes[COLUMN_INDEX.TEXTURES_SIZE]; - texturesSizeCell.innerText = itemData.texturesSize; - let hasTransparentCell = elRow.childNodes[COLUMN_INDEX.HAS_TRANSPARENT]; - hasTransparentCell.innerHTML = itemData.hasTransparent ? TRANSPARENCY_GLYPH : null; - let isBakedCell = elRow.childNodes[COLUMN_INDEX.IS_BAKED]; - isBakedCell.innerHTML = itemData.isBaked ? BAKED_GLYPH : null; - let drawCallsCell = elRow.childNodes[COLUMN_INDEX.DRAW_CALLS]; - drawCallsCell.innerText = itemData.drawCalls; - let hasScriptCell = elRow.childNodes[COLUMN_INDEX.HAS_SCRIPT]; - hasScriptCell.innerHTML = itemData.hasScript ? SCRIPT_GLYPH : null; - + for (let i = 0; i < columns.length; ++i) { + let column = columns[i]; + let elCell = elRow.childNodes[i]; + if (column.data.glyph) { + elCell.innerHTML = itemData[column.data.propertyID] ? column.data.columnHeader : null; + } else { + elCell.innerText = itemData[column.data.propertyID]; + } + elCell.style = "min-width:" + column.widthPx + "px;" + "max-width:" + column.widthPx + "px;"; + elCell.className = getColumnClassName(column.columnID); + } + // if this entity was previously selected flag it's row as selected if (itemData.selected) { elRow.className = 'selected'; @@ -723,16 +812,16 @@ function loaded() { } function clearRow(elRow) { - // reset all texts and glyphs for each of the row's column - for (let i = 0; i < NUM_COLUMNS; i++) { + // reset all texts and glyphs for each of the row's columns + for (let i = 0; i < columns.length; ++i) { let cell = elRow.childNodes[i]; - if (isGlyphColumn(i)) { + if (columns[i].data.glyph) { cell.innerHTML = ""; } else { cell.innerText = ""; } - } - + } + // clear the row from any associated entity let entityID = elRow.dataset.entityID; if (entityID && entitiesByID[entityID]) { @@ -744,7 +833,7 @@ function loaded() { elRow.dataset.entityID = EMPTY_ENTITY_ID; } - function toggleFilterInView() { + function onToggleFilterInView() { isFilterInView = !isFilterInView; if (isFilterInView) { elFilterInView.setAttribute(FILTER_IN_VIEW_ATTRIBUTE, FILTER_IN_VIEW_ATTRIBUTE); @@ -761,17 +850,110 @@ function loaded() { EventBridge.emitWebEvent(JSON.stringify({ type: 'radius', radius: elFilterRadius.value })); refreshEntities(); } + + function getColumnIndex(columnID) { + for (let i = 0; i < columns.length; ++i) { + if (columns[i].columnID === columnID) { + return i; + } + } + return -1; + } - function isTypeDropdownVisible() { - return elFilterTypeCheckboxes.style.display === "block"; + function getColumnClassName(columnID) { + let column = columnsByID[columnID]; + let visible = column.elTh.style.visibility !== "hidden"; + let className = column.data.glyph ? "glyph" : ""; + className += visible ? "" : " hidden"; + return className; + } + + function isColumnsDropdownVisible() { + return elColumnsOptions.style.display === "block"; + } + + function toggleColumnsDropdown() { + elColumnsOptions.style.display = isColumnsDropdownVisible() ? "none" : "block"; + } + + function onToggleColumnsDropdown(event) { + toggleColumnsDropdown(); + if (isTypeDropdownVisible()) { + toggleTypeDropdown(); + } + event.stopPropagation(); + } + + function toggleColumn(elInput, refresh) { + let columnID = elInput.getAttribute("columnID"); + let columnChecked = elInput.checked; + + if (columnChecked) { + let widthNeeded = columnsByID[columnID].data.initialWidth; + + let numberVisibleColumns = 0; + for (let i = 0; i < columns.length; ++i) { + let column = columns[i]; + if (column.columnID === columnID) { + column.width = widthNeeded; + } else if (column.width > 0) { + ++numberVisibleColumns; + } + } + + for (let i = 0; i < columns.length; ++i) { + let column = columns[i]; + if (column.columnID !== columnID && column.width > 0) { + column.width -= column.width * widthNeeded; + } + } + } else { + let widthLoss = 0; + + let numberVisibleColumns = 0; + for (let i = 0; i < columns.length; ++i) { + let column = columns[i]; + if (column.columnID === columnID) { + widthLoss = column.width; + column.width = 0; + } else if (column.width > 0) { + ++numberVisibleColumns; + } + } + + for (let i = 0; i < columns.length; ++i) { + let column = columns[i]; + if (column.columnID !== columnID && column.width > 0) { + let newTotalWidth = (1 - widthLoss); + column.width += (column.width / newTotalWidth) * widthLoss; + } + } + } + + updateColumnWidths(); + } + + function onToggleColumn(event) { + let elTarget = event.target; + if (elTarget instanceof HTMLInputElement) { + toggleColumn(elTarget, true); + } + event.stopPropagation(); + } + + function isTypeDropdownVisible() { + return elFilterTypeOptions.style.display === "block"; } function toggleTypeDropdown() { - elFilterTypeCheckboxes.style.display = isTypeDropdownVisible() ? "none" : "block"; + elFilterTypeOptions.style.display = isTypeDropdownVisible() ? "none" : "block"; } function onToggleTypeDropdown(event) { toggleTypeDropdown(); + if (isColumnsDropdownVisible()) { + toggleColumnsDropdown(); + } event.stopPropagation(); } @@ -808,26 +990,94 @@ function loaded() { } function onBodyClick(event) { - // if clicking anywhere outside of the type filter dropdown (since click event bubbled up to onBodyClick and - // propagation wasn't stopped by onToggleTypeFilter or onToggleTypeDropdown) and the dropdown is open then close it + // if clicking anywhere outside of the multiselect dropdowns (since click event bubbled up to onBodyClick and + // propagation wasn't stopped in the toggle type/column callbacks) and the dropdown is open then close it if (isTypeDropdownVisible()) { toggleTypeDropdown(); } - } - - function toggleInfo(event) { - showExtraInfo = !showExtraInfo; - if (showExtraInfo) { - elEntityTable.className = "showExtraInfo"; - elInfoToggleGlyph.innerHTML = COLLAPSE_EXTRA_INFO; - } else { - elEntityTable.className = ""; - elInfoToggleGlyph.innerHTML = EXPAND_EXTRA_INFO; + if (isColumnsDropdownVisible()) { + toggleColumnsDropdown(); } - entityList.resize(); - event.stopPropagation(); } - + + function onStartResize(event) { + startResizeEvent = event; + resizeColumnIndex = parseInt(this.getAttribute("columnIndex")); + event.stopPropagation(); + } + + function updateColumnWidths() { + let fullWidth = elEntityTableBody.offsetWidth; + let remainingWidth = fullWidth; + let scrollbarVisible = elEntityTableScroll.scrollHeight > elEntityTableScroll.clientHeight; + let resizerRight = scrollbarVisible ? SCROLLBAR_WIDTH - RESIZER_WIDTH/2 : -RESIZER_WIDTH/2; + let visibleColumns = 0; + + for (let i = columns.length - 1; i > 0; --i) { + let column = columns[i]; + column.widthPx = Math.ceil(column.width * fullWidth); + column.elTh.style = "min-width:" + column.widthPx + "px;" + "max-width:" + column.widthPx + "px;"; + let columnVisible = column.width > 0; + column.elTh.style.visibility = columnVisible ? "visible" : "hidden"; + if (column.elResizer) { + column.elResizer.style = "right:" + resizerRight + "px;"; + column.elResizer.style.visibility = columnVisible && visibleColumns > 0 ? "visible" : "hidden"; + } + resizerRight += column.widthPx; + remainingWidth -= column.widthPx; + if (columnVisible) { + ++visibleColumns; + } + } + + // assign all remaining space to the first column + let column = columns[0]; + column.widthPx = remainingWidth; + column.width = remainingWidth / fullWidth; + column.elTh.style = "min-width:" + column.widthPx + "px;" + "max-width:" + column.widthPx + "px;"; + let columnVisible = column.width > 0; + column.elTh.style.visibility = columnVisible ? "visible" : "hidden"; + if (column.elResizer) { + column.elResizer.style = "right:" + resizerRight + "px;"; + column.elResizer.style.visibility = columnVisible && visibleColumns > 0 ? "visible" : "hidden"; + } + + entityList.refresh(); + } + + document.onmousemove = function(ev) { + if (startResizeEvent) { + startTh = null; + + let column = columns[resizeColumnIndex]; + + let nextColumnIndex = resizeColumnIndex + 1; + let nextColumn = columns[nextColumnIndex]; + while (nextColumn.width === 0) { + nextColumn = columns[++nextColumnIndex]; + } + + let fullWidth = elEntityTableBody.offsetWidth; + let dx = ev.clientX - startResizeEvent.clientX; + let dPct = dx / fullWidth; + + let newColWidth = column.width + dPct; + let newNextColWidth = nextColumn.width - dPct; + + if (newColWidth * fullWidth >= MINIMUM_COLUMN_WIDTH && newNextColWidth * fullWidth >= MINIMUM_COLUMN_WIDTH) { + column.width += dPct; + nextColumn.width -= dPct; + updateColumnWidths(); + startResizeEvent = ev; + } + } + } + + document.onmouseup = function(ev) { + startResizeEvent = null; + ev.stopPropagation(); + } + document.addEventListener("keydown", function (keyDownEvent) { if (keyDownEvent.target.nodeName === "INPUT") { return; @@ -878,6 +1128,8 @@ function loaded() { refreshSortOrder(); refreshEntities(); + + window.onresize = updateColumnWidths; }); augmentSpinButtons(); diff --git a/scripts/system/html/js/listView.js b/scripts/system/html/js/listView.js index 62163ffe16..07aba53f1c 100644 --- a/scripts/system/html/js/listView.js +++ b/scripts/system/html/js/listView.js @@ -7,7 +7,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html const SCROLL_ROWS = 2; // number of rows used as scrolling buffer, each time we pass this number of rows we scroll -const FIRST_ROW_INDEX = 3; // the first elRow element's index in the child nodes of the table body +const FIRST_ROW_INDEX = 2; // the first elRow element's index in the child nodes of the table body debugPrint = function (message) { console.log(message); @@ -284,18 +284,6 @@ ListView.prototype = { this.elTableBody.insertBefore(scrollRow, this.elBottomBuffer); this.elRows.push(scrollRow); } - - let ths = this.elTableHeaderRow; - let tds = this.getNumRows() > 0 ? this.elRows[0].childNodes : []; - if (!ths) { - debugPrint("ListView.resize - no valid table header row"); - } else if (tds.length !== ths.length) { - debugPrint("ListView.resize - td list size " + tds.length + " does not match th list size " + ths.length); - } - // update the widths of the header cells to match the body cells (using first body row) - for (let i = 0; i < ths.length; i++) { - ths[i].width = tds[i].offsetWidth; - } // restore the scroll point to the same scroll point from before above changes this.elTableScroll.scrollTop = prevScrollTop; @@ -309,9 +297,6 @@ ListView.prototype = { return; } - // delete initial blank row - this.elTableBody.deleteRow(0); - this.elTopBuffer = document.createElement("tr"); this.elTableBody.appendChild(this.elTopBuffer); this.elTopBuffer.setAttribute("height", 0); From 0597d15771eaff90f3838ba6ca51baf197e41522 Mon Sep 17 00:00:00 2001 From: David Back Date: Wed, 7 Nov 2018 16:06:18 -0800 Subject: [PATCH 143/286] tabs --- scripts/system/html/css/edit-style.css | 66 +-- scripts/system/html/entityList.html | 22 +- scripts/system/html/js/entityList.js | 534 ++++++++++++------------- 3 files changed, 311 insertions(+), 311 deletions(-) diff --git a/scripts/system/html/css/edit-style.css b/scripts/system/html/css/edit-style.css index d0a83b9937..cc045157fd 100644 --- a/scripts/system/html/css/edit-style.css +++ b/scripts/system/html/css/edit-style.css @@ -110,7 +110,7 @@ thead { tbody { width: 100%; - display: block; + display: block; } tfoot { @@ -194,7 +194,7 @@ td { } td.hidden { - padding-left: 0px; + padding-left: 0px; padding-right: 0px; } @@ -1150,16 +1150,16 @@ body#entity-list-body { #filter-type-multiselect-box select { border-radius: 14.5px; - width: 107px; - height: 28px; + width: 107px; + height: 28px; } #filter-type-options { - position: absolute; + position: absolute; top: 48px; } #filter-type-options div { - position: relative; - height: 22px; + position: relative; + height: 22px; } #filter-type-options span { position: relative; @@ -1168,12 +1168,12 @@ body#entity-list-body { padding-right: 4px; } #filter-type-options label { - position: absolute; - top: -20px; + position: absolute; + top: -20px; z-index: 2; - height: 22px; - width: 200px; - padding-top: 1px; + height: 22px; + width: 200px; + padding-top: 1px; } #filter-search-and-icon { @@ -1240,34 +1240,34 @@ input[type=button]#export { } #entity-table-columns-multiselect { - position: absolute; - top: 51px; - right: 22px; + position: absolute; + top: 51px; + right: 22px; } #entity-table-columns-multiselect-box select { - height: 28px; - width: 20px; - background-color: #1c1c1c; - border-top-right-radius: 7px; + height: 28px; + width: 20px; + background-color: #1c1c1c; + border-top-right-radius: 7px; } #entity-table-columns-options { - position: absolute; + position: absolute; top: 50px; - right: 110px; + right: 110px; } #entity-table-columns-options div { - position: relative; - height: 22px; + position: relative; + height: 22px; } #entity-table-columns-options label { - position: absolute; + position: absolute; top: -22px; - height: 22px; - width: 100px; - padding-top: 4px; + height: 22px; + width: 100px; + padding-top: 4px; } #entity-table-columns-options input[type=checkbox] + label { - padding-left: 30px; + padding-left: 30px; } #entity-table-scroll { @@ -1353,11 +1353,11 @@ input[type=button]#export { } #entity-table thead .resizer { - position: absolute; - top: 1px; - height: 26px; - width: 10px; - cursor: col-resize; + position: absolute; + top: 1px; + height: 26px; + width: 10px; + cursor: col-resize; } #entity-table td { diff --git a/scripts/system/html/entityList.html b/scripts/system/html/entityList.html index 84397fdb5a..300b6f0b12 100644 --- a/scripts/system/html/entityList.html +++ b/scripts/system/html/entityList.html @@ -57,17 +57,17 @@ -
-
- -
-
-
- -
-
+
+
+ +
+
+
+ +
+
There are no entities to display. Please check your filters or create an entity to begin.
diff --git a/scripts/system/html/js/entityList.js b/scripts/system/html/js/entityList.js index 0d75f6c1d1..091994dc1f 100644 --- a/scripts/system/html/js/entityList.js +++ b/scripts/system/html/js/entityList.js @@ -27,84 +27,84 @@ const KEY_P = 80; // Key code for letter p used for Parenting hotkey. const COLUMNS = { type: { columnHeader: "Type", - propertyID: "type", + propertyID: "type", initialWidth: 0.16, - initiallyShown: true, - alwaysShown: true, + initiallyShown: true, + alwaysShown: true, }, - name: { + name: { columnHeader: "Name", - propertyID: "name", + propertyID: "name", initialWidth: 0.34, - initiallyShown: true, - alwaysShown: true, + initiallyShown: true, + alwaysShown: true, }, url: { columnHeader: "File", - dropdownLabel: "File", - propertyID: "url", + dropdownLabel: "File", + propertyID: "url", initialWidth: 0.34, - initiallyShown: true, + initiallyShown: true, }, locked: { columnHeader: "", glyph: true, - propertyID: "locked", + propertyID: "locked", initialWidth: 0.08, - initiallyShown: true, - alwaysShown: true, + initiallyShown: true, + alwaysShown: true, }, visible: { columnHeader: "", glyph: true, - propertyID: "visible", + propertyID: "visible", initialWidth: 0.08, - initiallyShown: true, - alwaysShown: true, + initiallyShown: true, + alwaysShown: true, }, - verticesCount: { + verticesCount: { columnHeader: "Verts", - dropdownLabel: "Vertices", - propertyID: "verticesCount", + dropdownLabel: "Vertices", + propertyID: "verticesCount", initialWidth: 0.08, }, - texturesCount: { + texturesCount: { columnHeader: "Texts", - dropdownLabel: "Textures", - propertyID: "texturesCount", + dropdownLabel: "Textures", + propertyID: "texturesCount", initialWidth: 0.08, }, - texturesSize: { + texturesSize: { columnHeader: "Text MB", - dropdownLabel: "Texture Size", - propertyID: "texturesSize", + dropdownLabel: "Texture Size", + propertyID: "texturesSize", initialWidth: 0.10, }, - hasTransparent: { + hasTransparent: { columnHeader: "", glyph: true, - dropdownLabel: "Transparency", - propertyID: "hasTransparent", + dropdownLabel: "Transparency", + propertyID: "hasTransparent", initialWidth: 0.04, }, - isBaked: { + isBaked: { columnHeader: "", - glyph: true, - dropdownLabel: "Baked", - propertyID: "isBaked", + glyph: true, + dropdownLabel: "Baked", + propertyID: "isBaked", initialWidth: 0.08, }, - drawCalls: { + drawCalls: { columnHeader: "Draws", - dropdownLabel: "Draws", - propertyID: "drawCalls", + dropdownLabel: "Draws", + propertyID: "drawCalls", initialWidth: 0.08, }, - hasScript: { + hasScript: { columnHeader: "k", - glyph: true, - dropdownLabel: "Script", - propertyID: "hasScript", + glyph: true, + dropdownLabel: "Script", + propertyID: "hasScript", initialWidth: 0.06, }, }; @@ -221,8 +221,8 @@ function loaded() { elSelectedEntitiesCount = document.getElementById("selected-entities-count"); elVisibleEntitiesCount = document.getElementById("visible-entities-count"); elNoEntitiesMessage = document.getElementById("no-entities"); - elColumnsMultiselectBox = document.getElementById("entity-table-columns-multiselect-box"); - elColumnsOptions = document.getElementById("entity-table-columns-options"); + elColumnsMultiselectBox = document.getElementById("entity-table-columns-multiselect-box"); + elColumnsOptions = document.getElementById("entity-table-columns-options"); document.body.onclick = onBodyClick; elToggleLocked.onclick = function() { @@ -240,14 +240,14 @@ function loaded() { elDelete.onclick = function() { EventBridge.emitWebEvent(JSON.stringify({ type: 'delete' })); }; - elRefresh.onclick = refreshEntities; + elRefresh.onclick = refreshEntities; elFilterTypeMultiselectBox.onclick = onToggleTypeDropdown; elFilterSearch.onkeyup = refreshEntityList; elFilterSearch.onsearch = refreshEntityList; elFilterInView.onclick = onToggleFilterInView; elFilterRadius.onkeyup = onRadiusChange; elFilterRadius.onchange = onRadiusChange; - elColumnsMultiselectBox.onclick = onToggleColumnsDropdown; + elColumnsMultiselectBox.onclick = onToggleColumnsDropdown; // create filter type dropdown checkboxes with label and icon for each type for (let i = 0; i < FILTER_TYPES.length; ++i) { @@ -282,14 +282,14 @@ function loaded() { // create columns elHeaderTr = document.createElement("tr"); elEntityTableHeader.appendChild(elHeaderTr); - let columnIndex = 0; - for (let columnID in COLUMNS) { + let columnIndex = 0; + for (let columnID in COLUMNS) { let columnData = COLUMNS[columnID]; let thID = "entity-" + columnID; let elTh = document.createElement("th"); elTh.setAttribute("id", thID); - elTh.setAttribute("data-resizable-column-id", thID); + elTh.setAttribute("data-resizable-column-id", thID); if (columnData.glyph) { let elGlyph = document.createElement("span"); elGlyph.className = "glyph"; @@ -298,68 +298,68 @@ function loaded() { } else { elTh.innerText = columnData.columnHeader; } - elTh.onmousedown = function() { - startThClick = this; + elTh.onmousedown = function() { + startThClick = this; + }; + elTh.onmouseup = function() { + if (startThClick === this) { + setSortColumn(columnID); + } + startThClick = null; }; - elTh.onmouseup = function() { - if (startThClick === this) { - setSortColumn(columnID); - } - startThClick = null; - }; - let elResizer = document.createElement("span"); - elResizer.className = "resizer"; - elResizer.innerHTML = " "; - elResizer.setAttribute("columnIndex", columnIndex); - elResizer.onmousedown = onStartResize; - elTh.appendChild(elResizer); + let elResizer = document.createElement("span"); + elResizer.className = "resizer"; + elResizer.innerHTML = " "; + elResizer.setAttribute("columnIndex", columnIndex); + elResizer.onmousedown = onStartResize; + elTh.appendChild(elResizer); let elSortOrder = document.createElement("span"); elSortOrder.className = "sort-order"; elTh.appendChild(elSortOrder); elHeaderTr.appendChild(elTh); - - elSortOrders[columnID] = document.querySelector('#' + thID + ' .sort-order'); - if (currentSortColumn === null) { - currentSortColumn = columnID; - } - - // add column to columns dropdown if it is not set to be always shown - if (columnData.alwaysShown !== true) { - let columnDropdownID = "entity-table-column-" + columnID; - - let elDiv = document.createElement('div'); - elDiv.onclick = onToggleColumn; - elColumnsOptions.appendChild(elDiv); - - let elInput = document.createElement('input'); - elInput.setAttribute("type", "checkbox"); - elInput.setAttribute("id", columnDropdownID); - elInput.setAttribute("columnID", columnID); - elInput.checked = columnData.initiallyShown === true; - elDiv.appendChild(elInput); - - let elLabel = document.createElement('label'); - elLabel.setAttribute("for", columnDropdownID); - elLabel.innerText = columnData.dropdownLabel; - elDiv.appendChild(elLabel); - } - - let initialWidth = columnData.initiallyShown === true ? columnData.initialWidth : 0; - columns.push({ - columnID: columnID, - elTh: elTh, - elResizer: elResizer, - width: initialWidth, - data: columnData - }); - columnsByID[columnID] = columns[columnIndex]; - - ++columnIndex; + + elSortOrders[columnID] = document.querySelector('#' + thID + ' .sort-order'); + if (currentSortColumn === null) { + currentSortColumn = columnID; + } + + // add column to columns dropdown if it is not set to be always shown + if (columnData.alwaysShown !== true) { + let columnDropdownID = "entity-table-column-" + columnID; + + let elDiv = document.createElement('div'); + elDiv.onclick = onToggleColumn; + elColumnsOptions.appendChild(elDiv); + + let elInput = document.createElement('input'); + elInput.setAttribute("type", "checkbox"); + elInput.setAttribute("id", columnDropdownID); + elInput.setAttribute("columnID", columnID); + elInput.checked = columnData.initiallyShown === true; + elDiv.appendChild(elInput); + + let elLabel = document.createElement('label'); + elLabel.setAttribute("for", columnDropdownID); + elLabel.innerText = columnData.dropdownLabel; + elDiv.appendChild(elLabel); + } + + let initialWidth = columnData.initiallyShown === true ? columnData.initialWidth : 0; + columns.push({ + columnID: columnID, + elTh: elTh, + elResizer: elResizer, + width: initialWidth, + data: columnData + }); + columnsByID[columnID] = columns[columnIndex]; + + ++columnIndex; } - - elEntityTableHeaderRow = document.querySelectorAll("#entity-table thead th"); + + elEntityTableHeaderRow = document.querySelectorAll("#entity-table thead th"); entityList = new ListView(elEntityTableBody, elEntityTableScroll, elEntityTableHeaderRow, createRow, updateRow, clearRow, WINDOW_NONVARIABLE_HEIGHT); @@ -589,7 +589,7 @@ function loaded() { PROFILE("update-dom", function() { entityList.itemData = visibleEntities; entityList.refresh(); - updateColumnWidths(); + updateColumnWidths(); }); refreshFooter(); @@ -685,7 +685,7 @@ function loaded() { refreshEntityList(); }); } - + function refreshSortOrder() { elSortOrders[currentSortColumn].innerHTML = currentSortOrder === ASCENDING_SORT ? ASCENDING_STRING : DESCENDING_STRING; } @@ -763,11 +763,11 @@ function loaded() { function createRow() { let elRow = document.createElement("tr"); - columns.forEach(function(column) { - let elRowColumn = document.createElement("td"); + columns.forEach(function(column) { + let elRowColumn = document.createElement("td"); elRowColumn.className = getColumnClassName(column.columnID); elRow.appendChild(elRowColumn); - }); + }); elRow.oncontextmenu = onRowContextMenu; elRow.onclick = onRowClicked; elRow.ondblclick = onRowDoubleClicked; @@ -776,17 +776,17 @@ function loaded() { function updateRow(elRow, itemData) { // update all column texts and glyphs to this entity's data - for (let i = 0; i < columns.length; ++i) { - let column = columns[i]; - let elCell = elRow.childNodes[i]; - if (column.data.glyph) { - elCell.innerHTML = itemData[column.data.propertyID] ? column.data.columnHeader : null; - } else { - elCell.innerText = itemData[column.data.propertyID]; - } - elCell.style = "min-width:" + column.widthPx + "px;" + "max-width:" + column.widthPx + "px;"; - elCell.className = getColumnClassName(column.columnID); - } + for (let i = 0; i < columns.length; ++i) { + let column = columns[i]; + let elCell = elRow.childNodes[i]; + if (column.data.glyph) { + elCell.innerHTML = itemData[column.data.propertyID] ? column.data.columnHeader : null; + } else { + elCell.innerText = itemData[column.data.propertyID]; + } + elCell.style = "min-width:" + column.widthPx + "px;" + "max-width:" + column.widthPx + "px;"; + elCell.className = getColumnClassName(column.columnID); + } // if this entity was previously selected flag it's row as selected if (itemData.selected) { @@ -813,14 +813,14 @@ function loaded() { function clearRow(elRow) { // reset all texts and glyphs for each of the row's columns - for (let i = 0; i < columns.length; ++i) { + for (let i = 0; i < columns.length; ++i) { let cell = elRow.childNodes[i]; - if (columns[i].data.glyph) { + if (columns[i].data.glyph) { cell.innerHTML = ""; } else { cell.innerText = ""; } - } + } // clear the row from any associated entity let entityID = elRow.dataset.entityID; @@ -850,87 +850,87 @@ function loaded() { EventBridge.emitWebEvent(JSON.stringify({ type: 'radius', radius: elFilterRadius.value })); refreshEntities(); } - - function getColumnIndex(columnID) { - for (let i = 0; i < columns.length; ++i) { - if (columns[i].columnID === columnID) { - return i; - } - } - return -1; - } - function getColumnClassName(columnID) { - let column = columnsByID[columnID]; - let visible = column.elTh.style.visibility !== "hidden"; - let className = column.data.glyph ? "glyph" : ""; - className += visible ? "" : " hidden"; - return className; - } - - function isColumnsDropdownVisible() { + function getColumnIndex(columnID) { + for (let i = 0; i < columns.length; ++i) { + if (columns[i].columnID === columnID) { + return i; + } + } + return -1; + } + + function getColumnClassName(columnID) { + let column = columnsByID[columnID]; + let visible = column.elTh.style.visibility !== "hidden"; + let className = column.data.glyph ? "glyph" : ""; + className += visible ? "" : " hidden"; + return className; + } + + function isColumnsDropdownVisible() { return elColumnsOptions.style.display === "block"; } - - function toggleColumnsDropdown() { + + function toggleColumnsDropdown() { elColumnsOptions.style.display = isColumnsDropdownVisible() ? "none" : "block"; } function onToggleColumnsDropdown(event) { toggleColumnsDropdown(); - if (isTypeDropdownVisible()) { + if (isTypeDropdownVisible()) { toggleTypeDropdown(); } event.stopPropagation(); } - - function toggleColumn(elInput, refresh) { + + function toggleColumn(elInput, refresh) { let columnID = elInput.getAttribute("columnID"); let columnChecked = elInput.checked; if (columnChecked) { - let widthNeeded = columnsByID[columnID].data.initialWidth; - - let numberVisibleColumns = 0; - for (let i = 0; i < columns.length; ++i) { - let column = columns[i]; - if (column.columnID === columnID) { - column.width = widthNeeded; - } else if (column.width > 0) { - ++numberVisibleColumns; - } - } - - for (let i = 0; i < columns.length; ++i) { - let column = columns[i]; - if (column.columnID !== columnID && column.width > 0) { - column.width -= column.width * widthNeeded; - } - } - } else { - let widthLoss = 0; - - let numberVisibleColumns = 0; - for (let i = 0; i < columns.length; ++i) { - let column = columns[i]; - if (column.columnID === columnID) { - widthLoss = column.width; - column.width = 0; - } else if (column.width > 0) { - ++numberVisibleColumns; - } - } - - for (let i = 0; i < columns.length; ++i) { - let column = columns[i]; - if (column.columnID !== columnID && column.width > 0) { - let newTotalWidth = (1 - widthLoss); - column.width += (column.width / newTotalWidth) * widthLoss; - } - } - } - - updateColumnWidths(); + let widthNeeded = columnsByID[columnID].data.initialWidth; + + let numberVisibleColumns = 0; + for (let i = 0; i < columns.length; ++i) { + let column = columns[i]; + if (column.columnID === columnID) { + column.width = widthNeeded; + } else if (column.width > 0) { + ++numberVisibleColumns; + } + } + + for (let i = 0; i < columns.length; ++i) { + let column = columns[i]; + if (column.columnID !== columnID && column.width > 0) { + column.width -= column.width * widthNeeded; + } + } + } else { + let widthLoss = 0; + + let numberVisibleColumns = 0; + for (let i = 0; i < columns.length; ++i) { + let column = columns[i]; + if (column.columnID === columnID) { + widthLoss = column.width; + column.width = 0; + } else if (column.width > 0) { + ++numberVisibleColumns; + } + } + + for (let i = 0; i < columns.length; ++i) { + let column = columns[i]; + if (column.columnID !== columnID && column.width > 0) { + let newTotalWidth = (1 - widthLoss); + column.width += (column.width / newTotalWidth) * widthLoss; + } + } + } + + updateColumnWidths(); } function onToggleColumn(event) { @@ -940,8 +940,8 @@ function loaded() { } event.stopPropagation(); } - - function isTypeDropdownVisible() { + + function isTypeDropdownVisible() { return elFilterTypeOptions.style.display === "block"; } @@ -951,7 +951,7 @@ function loaded() { function onToggleTypeDropdown(event) { toggleTypeDropdown(); - if (isColumnsDropdownVisible()) { + if (isColumnsDropdownVisible()) { toggleColumnsDropdown(); } event.stopPropagation(); @@ -995,88 +995,88 @@ function loaded() { if (isTypeDropdownVisible()) { toggleTypeDropdown(); } - if (isColumnsDropdownVisible()) { + if (isColumnsDropdownVisible()) { toggleColumnsDropdown(); } } - - function onStartResize(event) { - startResizeEvent = event; - resizeColumnIndex = parseInt(this.getAttribute("columnIndex")); - event.stopPropagation(); - } - - function updateColumnWidths() { - let fullWidth = elEntityTableBody.offsetWidth; - let remainingWidth = fullWidth; - let scrollbarVisible = elEntityTableScroll.scrollHeight > elEntityTableScroll.clientHeight; - let resizerRight = scrollbarVisible ? SCROLLBAR_WIDTH - RESIZER_WIDTH/2 : -RESIZER_WIDTH/2; - let visibleColumns = 0; - - for (let i = columns.length - 1; i > 0; --i) { - let column = columns[i]; - column.widthPx = Math.ceil(column.width * fullWidth); - column.elTh.style = "min-width:" + column.widthPx + "px;" + "max-width:" + column.widthPx + "px;"; - let columnVisible = column.width > 0; - column.elTh.style.visibility = columnVisible ? "visible" : "hidden"; - if (column.elResizer) { - column.elResizer.style = "right:" + resizerRight + "px;"; - column.elResizer.style.visibility = columnVisible && visibleColumns > 0 ? "visible" : "hidden"; - } - resizerRight += column.widthPx; - remainingWidth -= column.widthPx; - if (columnVisible) { - ++visibleColumns; - } - } - - // assign all remaining space to the first column - let column = columns[0]; - column.widthPx = remainingWidth; - column.width = remainingWidth / fullWidth; - column.elTh.style = "min-width:" + column.widthPx + "px;" + "max-width:" + column.widthPx + "px;"; - let columnVisible = column.width > 0; - column.elTh.style.visibility = columnVisible ? "visible" : "hidden"; - if (column.elResizer) { - column.elResizer.style = "right:" + resizerRight + "px;"; - column.elResizer.style.visibility = columnVisible && visibleColumns > 0 ? "visible" : "hidden"; - } - - entityList.refresh(); - } - - document.onmousemove = function(ev) { - if (startResizeEvent) { - startTh = null; - - let column = columns[resizeColumnIndex]; - - let nextColumnIndex = resizeColumnIndex + 1; - let nextColumn = columns[nextColumnIndex]; - while (nextColumn.width === 0) { - nextColumn = columns[++nextColumnIndex]; - } + + function onStartResize(event) { + startResizeEvent = event; + resizeColumnIndex = parseInt(this.getAttribute("columnIndex")); + event.stopPropagation(); + } + + function updateColumnWidths() { + let fullWidth = elEntityTableBody.offsetWidth; + let remainingWidth = fullWidth; + let scrollbarVisible = elEntityTableScroll.scrollHeight > elEntityTableScroll.clientHeight; + let resizerRight = scrollbarVisible ? SCROLLBAR_WIDTH - RESIZER_WIDTH/2 : -RESIZER_WIDTH/2; + let visibleColumns = 0; + + for (let i = columns.length - 1; i > 0; --i) { + let column = columns[i]; + column.widthPx = Math.ceil(column.width * fullWidth); + column.elTh.style = "min-width:" + column.widthPx + "px;" + "max-width:" + column.widthPx + "px;"; + let columnVisible = column.width > 0; + column.elTh.style.visibility = columnVisible ? "visible" : "hidden"; + if (column.elResizer) { + column.elResizer.style = "right:" + resizerRight + "px;"; + column.elResizer.style.visibility = columnVisible && visibleColumns > 0 ? "visible" : "hidden"; + } + resizerRight += column.widthPx; + remainingWidth -= column.widthPx; + if (columnVisible) { + ++visibleColumns; + } + } + + // assign all remaining space to the first column + let column = columns[0]; + column.widthPx = remainingWidth; + column.width = remainingWidth / fullWidth; + column.elTh.style = "min-width:" + column.widthPx + "px;" + "max-width:" + column.widthPx + "px;"; + let columnVisible = column.width > 0; + column.elTh.style.visibility = columnVisible ? "visible" : "hidden"; + if (column.elResizer) { + column.elResizer.style = "right:" + resizerRight + "px;"; + column.elResizer.style.visibility = columnVisible && visibleColumns > 0 ? "visible" : "hidden"; + } + + entityList.refresh(); + } + + document.onmousemove = function(ev) { + if (startResizeEvent) { + startTh = null; + + let column = columns[resizeColumnIndex]; + + let nextColumnIndex = resizeColumnIndex + 1; + let nextColumn = columns[nextColumnIndex]; + while (nextColumn.width === 0) { + nextColumn = columns[++nextColumnIndex]; + } - let fullWidth = elEntityTableBody.offsetWidth; - let dx = ev.clientX - startResizeEvent.clientX; - let dPct = dx / fullWidth; - - let newColWidth = column.width + dPct; - let newNextColWidth = nextColumn.width - dPct; - - if (newColWidth * fullWidth >= MINIMUM_COLUMN_WIDTH && newNextColWidth * fullWidth >= MINIMUM_COLUMN_WIDTH) { - column.width += dPct; - nextColumn.width -= dPct; - updateColumnWidths(); - startResizeEvent = ev; - } - } - } - - document.onmouseup = function(ev) { - startResizeEvent = null; - ev.stopPropagation(); - } + let fullWidth = elEntityTableBody.offsetWidth; + let dx = ev.clientX - startResizeEvent.clientX; + let dPct = dx / fullWidth; + + let newColWidth = column.width + dPct; + let newNextColWidth = nextColumn.width - dPct; + + if (newColWidth * fullWidth >= MINIMUM_COLUMN_WIDTH && newNextColWidth * fullWidth >= MINIMUM_COLUMN_WIDTH) { + column.width += dPct; + nextColumn.width -= dPct; + updateColumnWidths(); + startResizeEvent = ev; + } + } + } + + document.onmouseup = function(ev) { + startResizeEvent = null; + ev.stopPropagation(); + } document.addEventListener("keydown", function (keyDownEvent) { if (keyDownEvent.target.nodeName === "INPUT") { @@ -1128,8 +1128,8 @@ function loaded() { refreshSortOrder(); refreshEntities(); - - window.onresize = updateColumnWidths; + + window.onresize = updateColumnWidths; }); augmentSpinButtons(); From 6b4620b4a14d5254925bd5e54ada3caf61d54edb Mon Sep 17 00:00:00 2001 From: amantley Date: Wed, 7 Nov 2018 17:32:23 -0800 Subject: [PATCH 144/286] working on the input mapper problem for the offset rotations --- interface/src/avatar/MyAvatar.cpp | 5 ++++- interface/src/avatar/MySkeletonModel.cpp | 15 +++++++++++++-- libraries/animation/src/Rig.cpp | 2 +- 3 files changed, 18 insertions(+), 4 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 0f9d3f2f81..39e6a56b02 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -431,7 +431,7 @@ void MyAvatar::reset(bool andRecenter, bool andReload, bool andHead) { _wasPushing = _isPushing = _isBraking = false; _follow.deactivate(); if (andReload) { - //_skeletonModel->reset(); + _skeletonModel->reset(); } if (andHead) { // which drives camera in desktop getHead()->reset(); @@ -2037,7 +2037,9 @@ controller::Pose MyAvatar::getControllerPoseInSensorFrame(controller::Action act controller::Pose MyAvatar::getControllerPoseInWorldFrame(controller::Action action) const { auto pose = getControllerPoseInSensorFrame(action); + qCDebug(interfaceapp) << "avatar sensor orientation " << pose.getRotation(); if (pose.valid) { + qCDebug(interfaceapp) << "sensor to world matrix orientation " << extractRotation(getSensorToWorldMatrix()); return pose.transform(getSensorToWorldMatrix()); } else { return controller::Pose(); // invalid pose @@ -2047,6 +2049,7 @@ controller::Pose MyAvatar::getControllerPoseInWorldFrame(controller::Action acti controller::Pose MyAvatar::getControllerPoseInAvatarFrame(controller::Action action) const { auto pose = getControllerPoseInWorldFrame(action); if (pose.valid) { + qCDebug(interfaceapp) << "avatar world orientation " << getWorldOrientation(); glm::mat4 invAvatarMatrix = glm::inverse(createMatFromQuatAndPos(getWorldOrientation(), getWorldPosition())); return pose.transform(invAvatarMatrix); } else { diff --git a/interface/src/avatar/MySkeletonModel.cpp b/interface/src/avatar/MySkeletonModel.cpp index 6aa4d1402f..d389d99e26 100644 --- a/interface/src/avatar/MySkeletonModel.cpp +++ b/interface/src/avatar/MySkeletonModel.cpp @@ -119,11 +119,22 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { // input action is the highest priority source for head orientation. auto avatarHeadPose = myAvatar->getControllerPoseInAvatarFrame(controller::Action::HEAD); if (avatarHeadPose.isValid()) { + AnimPose previousHeadPose; + bool headUnfuckedWith = _rig.getAbsoluteJointPoseInRigFrame(_rig.indexOfJoint("Head"), previousHeadPose); + if (headUnfuckedWith) { + qCDebug(interfaceapp) << "unset head position " << previousHeadPose.trans(); + qCDebug(interfaceapp) << "unset head rotation " << previousHeadPose.rot(); + } qCDebug(interfaceapp) << "neck joint offset " << jointOffsetMap[62]; + qCDebug(interfaceapp) << "head joint avatar frame " << avatarHeadPose.getRotation(); AnimPose jointOffsetNeck(jointOffsetMap[62], glm::vec3()); AnimPose jointOffsetSpine2(jointOffsetMap[13], glm::vec3()); + AnimPose testPose(glm::quat(0.7071f, 0.0f, 0.0f, 0.7071f), glm::vec3()); AnimPose pose(avatarHeadPose.getRotation(), avatarHeadPose.getTranslation()); - params.primaryControllerPoses[Rig::PrimaryControllerType_Head] = jointOffsetSpine2.inverse() * jointOffsetNeck.inverse() * (avatarToRigPose * pose) * jointOffsetNeck * jointOffsetSpine2; + AnimPose newHeadRot = (avatarToRigPose * pose) * testPose; + AnimPose newHeadRot2(newHeadRot.rot(), avatarHeadPose.getTranslation()); + AnimPose identityPose(glm::quat(1.0f,0.0f,0.0f,0.0f), glm::vec3(0.0f,0.57f,0.0f)); + params.primaryControllerPoses[Rig::PrimaryControllerType_Head] = avatarToRigPose * pose; params.primaryControllerFlags[Rig::PrimaryControllerType_Head] = (uint8_t)Rig::ControllerFlags::Enabled; } else { // even though full head IK is disabled, the rig still needs the head orientation to rotate the head up and @@ -234,7 +245,7 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { // set spine2 if we have hand controllers qCDebug(interfaceapp) << "spine 2 joint offset " << jointOffsetMap[13]; - if (false && myAvatar->getControllerPoseInAvatarFrame(controller::Action::RIGHT_HAND).isValid() && + if (myAvatar->getControllerPoseInAvatarFrame(controller::Action::RIGHT_HAND).isValid() && myAvatar->getControllerPoseInAvatarFrame(controller::Action::LEFT_HAND).isValid() && !(params.primaryControllerFlags[Rig::PrimaryControllerType_Spine2] & (uint8_t)Rig::ControllerFlags::Enabled)) { diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 1e20ec9a7d..a73c945124 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -2078,7 +2078,7 @@ void Rig::setJointRotationOffsets(const QMap& offsets) { int spine2Id = 13; if (true){ //neckIndex != -1) { _jointRotationOffsets.insert(neckId, glm::quat(0.0f, 0.7071f, 0.7071f, 0.0f) * glm::quat(0.5f, 0.5f, 0.5f, -0.5f) ); //glm::quat(0.7071f, 0.0f, -0.7071f, 0.0f) - qCDebug(animation) << "multiplied quats are " << glm::quat(0.0f, 0.7071f, 0.7071f, 0.0f) * glm::quat(0.5f, 0.5f, 0.5f, -0.5f); + qCDebug(animation) << "multiplied quats are " << glm::quat(0.7071f, 0.0f, -0.7071f, 0.0f) * glm::quat(0.5f, 0.5f, 0.5f, -0.5f); } if (true){ //spine2Index != -1) { _jointRotationOffsets.insert(spine2Id, glm::quat(0.5f, 0.5f, 0.5f, -0.5f)); From 7e93d91bfa841cb4b35cba92eb58aef8f9bc7fb6 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Thu, 8 Nov 2018 09:29:02 +0100 Subject: [PATCH 145/286] Fixed android shader compilation --- libraries/render-utils/src/ssao.slh | 18 +++++------------- .../render-utils/src/ssao_bilateralBlur.slf | 6 +++--- .../render-utils/src/ssao_makeOcclusion.slf | 2 +- 3 files changed, 9 insertions(+), 17 deletions(-) diff --git a/libraries/render-utils/src/ssao.slh b/libraries/render-utils/src/ssao.slh index 4fd5e1fd5f..bd2e29d04d 100644 --- a/libraries/render-utils/src/ssao.slh +++ b/libraries/render-utils/src/ssao.slh @@ -277,11 +277,7 @@ vec2 getStereoSideSizeRoundUp(int resolutionLevel) { return vec2((fullRes + resolutionDivisor - 1) / resolutionDivisor); } -float getZEyeAtPixel(ivec2 pixel, int level) { - return -texelFetch(depthPyramidTex, pixel, level).x; -} - -float getZEyeAtUV(vec2 texCoord, int level) { +float getZEyeAtUV(vec2 texCoord, float level) { return -textureLod(depthPyramidTex, texCoord, level).x; } @@ -289,7 +285,7 @@ float getZEyeAtUV(vec2 texCoord, int level) { -textureLodOffset(depthPyramidTex, <$texCoord$>, <$level$>, <$texelOffset$>).x; <@endfunc@> -float getZEyeAtUV(ivec4 side, vec2 texCoord, int level) { +float getZEyeAtUV(ivec4 side, vec2 texCoord, float level) { texCoord.x = mix(texCoord.x, (texCoord.x + getStereoSide(side)) * 0.5, isStereo()); return getZEyeAtUV(texCoord, level); } @@ -303,19 +299,15 @@ vec3 unpackNormal(vec3 packedNormal) { return normalize(packedNormal*2.0 - 1.0); } -vec3 getNormalEyeAtUV(vec2 texCoord, int level) { +vec3 getNormalEyeAtUV(vec2 texCoord, float level) { return unpackNormal(textureLod(normalTex, texCoord, level).xyz); } -vec3 getNormalEyeAtUV(ivec4 side, vec2 texCoord, int level) { +vec3 getNormalEyeAtUV(ivec4 side, vec2 texCoord, float level) { texCoord.x = mix(texCoord.x, (texCoord.x + getStereoSide(side)) * 0.5, isStereo()); return getNormalEyeAtUV(texCoord, level); } -vec3 getNormalEyeAtPixel(ivec2 pixel, int level) { - return unpackNormal(texelFetch(normalTex, pixel, level).xyz); -} - vec2 snapToTexel(vec2 uv, vec2 pixelSize) { return (floor(uv * pixelSize - 0.5) + 0.5) / pixelSize; } @@ -339,7 +331,7 @@ vec2 fetchTap(ivec4 side, vec2 tapUV, float tapRadius) { } vec3 buildPosition(ivec4 side, vec2 fragUVPos) { - float Zeye = getZEyeAtUV(side, fragUVPos, 0); + float Zeye = getZEyeAtUV(side, fragUVPos, 0.0); return evalEyePositionFromZeye(side.x, Zeye, fragUVPos); } diff --git a/libraries/render-utils/src/ssao_bilateralBlur.slf b/libraries/render-utils/src/ssao_bilateralBlur.slf index a68c22b5ef..cf13065555 100644 --- a/libraries/render-utils/src/ssao_bilateralBlur.slf +++ b/libraries/render-utils/src/ssao_bilateralBlur.slf @@ -44,7 +44,7 @@ int getBlurRadius() { vec4 fetchOcclusionPacked(ivec4 side, vec2 texCoord) { texCoord.x = mix(texCoord.x, (texCoord.x + getStereoSide(side) * getBlurOcclusionUVLimit().x) * 0.5, isStereo()); - return textureLod(occlusionMap, texCoord, 0); + return textureLod(occlusionMap, texCoord, 0.0); } float evalBlurCoefficient(vec3 blurScales, float radialDistance, float zDistance, float normalDistance) { @@ -81,10 +81,10 @@ vec4 getBlurredOcclusion(ivec2 destPixelCoord, vec2 occlusionTexCoord, vec2 dept // Stereo side info ivec4 side = getStereoSideInfo(destPixelCoord.x, 0); - float fragDepth = getZEyeAtUV(depthTexCoord, 0); + float fragDepth = getZEyeAtUV(depthTexCoord, 0.0); float fragDepthKey = CSZToDepthKey(fragDepth); #if SSAO_BILATERAL_BLUR_USE_NORMAL - vec3 fragNormal = getNormalEyeAtUV(depthTexCoord, 0); + vec3 fragNormal = getNormalEyeAtUV(depthTexCoord, 0.0); #else vec3 fragNormal = vec3(0, 0, 1); #endif diff --git a/libraries/render-utils/src/ssao_makeOcclusion.slf b/libraries/render-utils/src/ssao_makeOcclusion.slf index 8b44fd8301..cc161d7323 100644 --- a/libraries/render-utils/src/ssao_makeOcclusion.slf +++ b/libraries/render-utils/src/ssao_makeOcclusion.slf @@ -32,7 +32,7 @@ void main(void) { vec2 fragUVPos = varTexCoord0; #if SSAO_USE_QUAD_SPLIT - vec3 fragNormalES = getNormalEyeAtUV(fragUVPos, 0); + vec3 fragNormalES = getNormalEyeAtUV(fragUVPos, 0.0); #endif // Stereo side info based on the real viewport size of this pass From c79c403a52ac6e86b19b0210042d1edf715717bd Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Thu, 8 Nov 2018 10:47:24 +0100 Subject: [PATCH 146/286] More android shader compilation fixes --- libraries/render-utils/src/ssao.slh | 38 ++++++++++--------- .../render-utils/src/ssao_bilateralBlur.slf | 4 +- .../render-utils/src/ssao_buildNormals.slf | 2 +- .../render-utils/src/ssao_makeOcclusion.slf | 3 +- 4 files changed, 25 insertions(+), 22 deletions(-) diff --git a/libraries/render-utils/src/ssao.slh b/libraries/render-utils/src/ssao.slh index bd2e29d04d..248a9f930e 100644 --- a/libraries/render-utils/src/ssao.slh +++ b/libraries/render-utils/src/ssao.slh @@ -253,6 +253,14 @@ vec3 getTapLocationClampedSSAO(int sampleNumber, float spinAngle, float outerRad LAYOUT(binding=RENDER_UTILS_TEXTURE_SSAO_DEPTH) uniform sampler2D depthPyramidTex; LAYOUT(binding=RENDER_UTILS_TEXTURE_SSAO_NORMAL) uniform sampler2D normalTex; +vec2 getFramebufferUVFromSideUV(ivec4 side, vec2 uv) { + return isStereo() ? vec2((uv.x + float(getStereoSide(side))) * 0.5, uv.y) : uv; +} + +vec2 getSideUVFromFramebufferUV(ivec4 side, vec2 uv) { + return isStereo() ? vec2(uv.x * 2.0 - float(getStereoSide(side)), uv.y) : uv; +} + ivec2 getDepthTextureSize(int level) { return textureSize(depthPyramidTex, level); } @@ -286,7 +294,7 @@ float getZEyeAtUV(vec2 texCoord, float level) { <@endfunc@> float getZEyeAtUV(ivec4 side, vec2 texCoord, float level) { - texCoord.x = mix(texCoord.x, (texCoord.x + getStereoSide(side)) * 0.5, isStereo()); + texCoord = getFramebufferUVFromSideUV(side, texCoord); return getZEyeAtUV(texCoord, level); } @@ -304,7 +312,7 @@ vec3 getNormalEyeAtUV(vec2 texCoord, float level) { } vec3 getNormalEyeAtUV(ivec4 side, vec2 texCoord, float level) { - texCoord.x = mix(texCoord.x, (texCoord.x + getStereoSide(side)) * 0.5, isStereo()); + texCoord = getFramebufferUVFromSideUV(side, texCoord); return getNormalEyeAtUV(texCoord, level); } @@ -322,7 +330,7 @@ vec2 fetchTap(ivec4 side, vec2 tapUV, float tapRadius) { int mipLevel = evalMipFromRadius(tapRadius * float(doFetchMips())); vec2 fetchUV = clamp(tapUV, vec2(0), vec2(1)); - fetchUV = isStereo() ? vec2((fetchUV.x + getStereoSide(side)) * 0.5, fetchUV.y) : fetchUV; + fetchUV = getFramebufferUVFromSideUV(side, fetchUV); vec2 P; P.x = float(mipLevel); @@ -357,19 +365,17 @@ const ivec2 UV_TOP = ivec2(0,1); const ivec2 UV_BOTTOM = ivec2(0,-1); vec3 buildNormal(ivec4 side, vec2 fragUVPos, vec3 fragPosition, vec2 deltaDepthUV) { - vec2 sideUVPos = fragUVPos; - - sideUVPos.x = mix(sideUVPos.x, (sideUVPos.x + getStereoSide(side)) * 0.5, isStereo()); + vec2 fullUVPos = getFramebufferUVFromSideUV(side, fragUVPos); vec3 fragPositionDxPos; vec3 fragPositionDxNeg; vec3 fragPositionDyPos; vec3 fragPositionDyNeg; - <$buildPositionOffset(side, fragUVPos, sideUVPos, UV_RIGHT, deltaDepthUV, fragPositionDxPos)$> - <$buildPositionOffset(side, fragUVPos, sideUVPos, UV_LEFT, deltaDepthUV, fragPositionDxNeg)$> - <$buildPositionOffset(side, fragUVPos, sideUVPos, UV_TOP, deltaDepthUV, fragPositionDyPos)$> - <$buildPositionOffset(side, fragUVPos, sideUVPos, UV_BOTTOM, deltaDepthUV, fragPositionDyNeg)$> + <$buildPositionOffset(side, fragUVPos, fullUVPos, UV_RIGHT, deltaDepthUV, fragPositionDxPos)$> + <$buildPositionOffset(side, fragUVPos, fullUVPos, UV_LEFT, deltaDepthUV, fragPositionDxNeg)$> + <$buildPositionOffset(side, fragUVPos, fullUVPos, UV_TOP, deltaDepthUV, fragPositionDyPos)$> + <$buildPositionOffset(side, fragUVPos, fullUVPos, UV_BOTTOM, deltaDepthUV, fragPositionDyNeg)$> vec3 fragDeltaDx = getMinDelta(fragPosition, fragPositionDxPos, fragPositionDxNeg); vec3 fragDeltaDy = getMinDelta(fragPosition, fragPositionDyPos, fragPositionDyNeg); @@ -379,19 +385,17 @@ vec3 buildNormal(ivec4 side, vec2 fragUVPos, vec3 fragPosition, vec2 deltaDepthU void buildTangentBinormal(ivec4 side, vec2 fragUVPos, vec3 fragPosition, vec3 fragNormal, vec2 deltaDepthUV, out vec3 fragTangent, out vec3 fragBinormal) { - vec2 sideUVPos = fragUVPos; - - sideUVPos.x = mix(sideUVPos.x, (sideUVPos.x + getStereoSide(side)) * 0.5, isStereo()); + vec2 fullUVPos = getFramebufferUVFromSideUV(side, fragUVPos); vec3 fragPositionDxPos; vec3 fragPositionDxNeg; vec3 fragPositionDyPos; vec3 fragPositionDyNeg; - <$buildPositionOffset(side, fragUVPos, sideUVPos, UV_RIGHT, deltaDepthUV, fragPositionDxPos)$> - <$buildPositionOffset(side, fragUVPos, sideUVPos, UV_LEFT, deltaDepthUV, fragPositionDxNeg)$> - <$buildPositionOffset(side, fragUVPos, sideUVPos, UV_TOP, deltaDepthUV, fragPositionDyPos)$> - <$buildPositionOffset(side, fragUVPos, sideUVPos, UV_BOTTOM, deltaDepthUV, fragPositionDyNeg)$> + <$buildPositionOffset(side, fragUVPos, fullUVPos, UV_RIGHT, deltaDepthUV, fragPositionDxPos)$> + <$buildPositionOffset(side, fragUVPos, fullUVPos, UV_LEFT, deltaDepthUV, fragPositionDxNeg)$> + <$buildPositionOffset(side, fragUVPos, fullUVPos, UV_TOP, deltaDepthUV, fragPositionDyPos)$> + <$buildPositionOffset(side, fragUVPos, fullUVPos, UV_BOTTOM, deltaDepthUV, fragPositionDyNeg)$> vec3 fragDeltaDx = getMinDelta(fragPosition, fragPositionDxPos, fragPositionDxNeg); vec3 fragDeltaDy = getMinDelta(fragPosition, fragPositionDyPos, fragPositionDyNeg); diff --git a/libraries/render-utils/src/ssao_bilateralBlur.slf b/libraries/render-utils/src/ssao_bilateralBlur.slf index cf13065555..c392f79322 100644 --- a/libraries/render-utils/src/ssao_bilateralBlur.slf +++ b/libraries/render-utils/src/ssao_bilateralBlur.slf @@ -43,7 +43,7 @@ int getBlurRadius() { } vec4 fetchOcclusionPacked(ivec4 side, vec2 texCoord) { - texCoord.x = mix(texCoord.x, (texCoord.x + getStereoSide(side) * getBlurOcclusionUVLimit().x) * 0.5, isStereo()); + texCoord.x = isStereo() ? (texCoord.x + float(getStereoSide(side)) * getBlurOcclusionUVLimit().x) * 0.5 : texCoord.x; return textureLod(occlusionMap, texCoord, 0.0); } @@ -96,7 +96,7 @@ vec4 getBlurredOcclusion(ivec2 destPixelCoord, vec2 occlusionTexCoord, vec2 dept int r; // From now on, occlusionTexCoord is the UV pos in the side - float sideTexCoord = occlusionTexCoord.x * 2.0 - getStereoSide(side) * getBlurOcclusionUVLimit().x; + float sideTexCoord = occlusionTexCoord.x * 2.0 - float(getStereoSide(side)) * getBlurOcclusionUVLimit().x; occlusionTexCoord.x = mix(occlusionTexCoord.x, sideTexCoord, isStereo()); occlusionTexCoord -= getBlurOcclusionAxis() * blurRadius; diff --git a/libraries/render-utils/src/ssao_buildNormals.slf b/libraries/render-utils/src/ssao_buildNormals.slf index 0dea63ae14..2f205119d9 100644 --- a/libraries/render-utils/src/ssao_buildNormals.slf +++ b/libraries/render-utils/src/ssao_buildNormals.slf @@ -32,7 +32,7 @@ void main(void) { vec2 deltaDepthUV = vec2(1.0) / vec2(getDepthTextureSideSize(0)); // From now on, fragUVPos is the UV pos in the side - fragUVPos.x = mix(fragUVPos.x, fragUVPos.x * 2.0 - getStereoSide(side), isStereo()); + fragUVPos = getSideUVFromFramebufferUV(side, fragUVPos); // The position and normal of the pixel fragment in Eye space vec3 fragPositionES = buildPosition(side, fragUVPos); diff --git a/libraries/render-utils/src/ssao_makeOcclusion.slf b/libraries/render-utils/src/ssao_makeOcclusion.slf index cc161d7323..5dfa879c69 100644 --- a/libraries/render-utils/src/ssao_makeOcclusion.slf +++ b/libraries/render-utils/src/ssao_makeOcclusion.slf @@ -45,8 +45,7 @@ void main(void) { } ivec4 side = getStereoSideInfoFromWidth(fragPixelPos.x, sideOcclusionSize.x); // From now on, fragUVPos is the UV pos in the side - fragUVPos.x = mix(fragUVPos.x, fragUVPos.x * 2.0 - getStereoSide(side), isStereo()); - + fragUVPos = getSideUVFromFramebufferUV(side, fragUVPos); fragUVPos = snapToTexel(fragUVPos, sideDepthSize); // The position and normal of the pixel fragment in Eye space From 851cfff96006174b2efb49191eefb82eb0d57cf6 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Thu, 8 Nov 2018 11:06:28 +0100 Subject: [PATCH 147/286] Again, android shader compilatio fix --- libraries/render-utils/src/ssao.slh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/render-utils/src/ssao.slh b/libraries/render-utils/src/ssao.slh index 248a9f930e..e942c15002 100644 --- a/libraries/render-utils/src/ssao.slh +++ b/libraries/render-utils/src/ssao.slh @@ -345,7 +345,7 @@ vec3 buildPosition(ivec4 side, vec2 fragUVPos) { <@func buildPositionOffset(side, fragUVPos, sideFragUVPos, texelOffset, deltaUV, position)@> { - float Zeye = <$getZEyeAtUVOffset($sideFragUVPos$, 0, $texelOffset$)$> + float Zeye = <$getZEyeAtUVOffset($sideFragUVPos$, 0.0, $texelOffset$)$> <$position$> = evalEyePositionFromZeye(<$side$>.x, Zeye, <$fragUVPos$> + <$texelOffset$>*<$deltaUV$>); } <@endfunc@> From 05ac2fccb6714557a869cab9d697b4ce5dca3d24 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Thu, 8 Nov 2018 11:24:46 +0100 Subject: [PATCH 148/286] Still android fixes --- libraries/render-utils/src/ssao.slh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/render-utils/src/ssao.slh b/libraries/render-utils/src/ssao.slh index e942c15002..1b73c71ed5 100644 --- a/libraries/render-utils/src/ssao.slh +++ b/libraries/render-utils/src/ssao.slh @@ -346,7 +346,7 @@ vec3 buildPosition(ivec4 side, vec2 fragUVPos) { <@func buildPositionOffset(side, fragUVPos, sideFragUVPos, texelOffset, deltaUV, position)@> { float Zeye = <$getZEyeAtUVOffset($sideFragUVPos$, 0.0, $texelOffset$)$> - <$position$> = evalEyePositionFromZeye(<$side$>.x, Zeye, <$fragUVPos$> + <$texelOffset$>*<$deltaUV$>); + <$position$> = evalEyePositionFromZeye(<$side$>.x, Zeye, <$fragUVPos$> + vec2(<$texelOffset$>)*<$deltaUV$>); } <@endfunc@> From de2f1c52ccb2f2f145085e38e3d5ff04eb6bad8e Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Thu, 8 Nov 2018 11:44:47 +0100 Subject: [PATCH 149/286] Another android shader compilation fix --- libraries/render-utils/src/ssao.slh | 4 ++-- libraries/render-utils/src/ssao_bilateralBlur.slf | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/libraries/render-utils/src/ssao.slh b/libraries/render-utils/src/ssao.slh index 1b73c71ed5..64a279c578 100644 --- a/libraries/render-utils/src/ssao.slh +++ b/libraries/render-utils/src/ssao.slh @@ -26,7 +26,7 @@ vec4 packOcclusionOutput(float occlusion, float depth, vec3 eyeNormal) { return vec4(occlusion, depth, eyeNormal.xy / eyeNormal.z); #else // Round to the nearest 1/256.0 - depth *= 256; + depth *= 256.0; float temp = floor(depth); return vec4(occlusion, temp * (1.0 / 256.0), depth - temp, 0.0); #endif @@ -45,7 +45,7 @@ void unpackOcclusionOutput(vec4 raw, out UnpackedOcclusion result) { result.normal = normalize(vec3(raw.zw, 1.0)); #else result.depth = (raw.y + raw.z / 256.0); - result.normal = vec3(0,0,1); + result.normal = vec3(0.0, 0.0, 1.0); #endif } diff --git a/libraries/render-utils/src/ssao_bilateralBlur.slf b/libraries/render-utils/src/ssao_bilateralBlur.slf index c392f79322..3d8fe531bf 100644 --- a/libraries/render-utils/src/ssao_bilateralBlur.slf +++ b/libraries/render-utils/src/ssao_bilateralBlur.slf @@ -86,7 +86,7 @@ vec4 getBlurredOcclusion(ivec2 destPixelCoord, vec2 occlusionTexCoord, vec2 dept #if SSAO_BILATERAL_BLUR_USE_NORMAL vec3 fragNormal = getNormalEyeAtUV(depthTexCoord, 0.0); #else - vec3 fragNormal = vec3(0, 0, 1); + vec3 fragNormal = vec3(0.0, 0.0, 1.0); #endif vec2 weightedSums = vec2(0.0); From cd89ce538833ef167d5d735539112d1695951023 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Thu, 8 Nov 2018 12:07:17 +0100 Subject: [PATCH 150/286] Another stupid android shader compilation error fix --- libraries/render-utils/src/ssao_bilateralBlur.slf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/render-utils/src/ssao_bilateralBlur.slf b/libraries/render-utils/src/ssao_bilateralBlur.slf index 3d8fe531bf..f9fb7288a5 100644 --- a/libraries/render-utils/src/ssao_bilateralBlur.slf +++ b/libraries/render-utils/src/ssao_bilateralBlur.slf @@ -72,7 +72,7 @@ vec2 evalTapWeightedValue(vec3 blurScales, ivec4 side, int r, vec2 occlusionTexC #else float normalDistance = 0.0; #endif - float weight = evalBlurCoefficient(blurScales, abs(r), zDistance, normalDistance); + float weight = evalBlurCoefficient(blurScales, float(abs(r)), zDistance, normalDistance); return vec2(tap.occlusion * weight, weight); } From 40b7b42f5bc4657c33e6d9854a06c350ff150b36 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Thu, 8 Nov 2018 12:29:01 +0100 Subject: [PATCH 151/286] Bis --- libraries/render-utils/src/ssao_bilateralBlur.slf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/render-utils/src/ssao_bilateralBlur.slf b/libraries/render-utils/src/ssao_bilateralBlur.slf index f9fb7288a5..52e7356e85 100644 --- a/libraries/render-utils/src/ssao_bilateralBlur.slf +++ b/libraries/render-utils/src/ssao_bilateralBlur.slf @@ -99,7 +99,7 @@ vec4 getBlurredOcclusion(ivec2 destPixelCoord, vec2 occlusionTexCoord, vec2 dept float sideTexCoord = occlusionTexCoord.x * 2.0 - float(getStereoSide(side)) * getBlurOcclusionUVLimit().x; occlusionTexCoord.x = mix(occlusionTexCoord.x, sideTexCoord, isStereo()); - occlusionTexCoord -= getBlurOcclusionAxis() * blurRadius; + occlusionTexCoord -= getBlurOcclusionAxis() * float(blurRadius); // negative side first for (r = -blurRadius; r <= -1; r++) { From 09b26db6507dbbb6a5862f4a901db03b7e68fb97 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Thu, 8 Nov 2018 13:19:42 +0100 Subject: [PATCH 152/286] Shader fix for android... --- libraries/render-utils/src/ssao.slh | 18 +++++------------- .../render-utils/src/ssao_buildNormals.slf | 2 +- 2 files changed, 6 insertions(+), 14 deletions(-) diff --git a/libraries/render-utils/src/ssao.slh b/libraries/render-utils/src/ssao.slh index 64a279c578..af7f8029b8 100644 --- a/libraries/render-utils/src/ssao.slh +++ b/libraries/render-utils/src/ssao.slh @@ -261,22 +261,14 @@ vec2 getSideUVFromFramebufferUV(ivec4 side, vec2 uv) { return isStereo() ? vec2(uv.x * 2.0 - float(getStereoSide(side)), uv.y) : uv; } -ivec2 getDepthTextureSize(int level) { - return textureSize(depthPyramidTex, level); -} -ivec2 getDepthTextureSideSize(int level) { - ivec2 size = getDepthTextureSize(level); - size.x >>= int(isStereo()) & 1; - return size; +vec2 getDepthTextureSize(int level) { + return vec2(textureSize(depthPyramidTex, level)); } -ivec2 getNormalTextureSize(int level) { - return textureSize(normalTex, level); -} -ivec2 getNormalTextureSideSize(int level) { - ivec2 size = getNormalTextureSize(level); +vec2 getDepthTextureSideSize(int level) { + ivec2 size = textureSize(depthPyramidTex, level); size.x >>= int(isStereo()) & 1; - return size; + return vec2(size); } vec2 getStereoSideSizeRoundUp(int resolutionLevel) { diff --git a/libraries/render-utils/src/ssao_buildNormals.slf b/libraries/render-utils/src/ssao_buildNormals.slf index 2f205119d9..0a733ff451 100644 --- a/libraries/render-utils/src/ssao_buildNormals.slf +++ b/libraries/render-utils/src/ssao_buildNormals.slf @@ -29,7 +29,7 @@ void main(void) { ivec2 sideNormalsSize = ivec2( getNormalsSideSize() ); ivec4 side = getStereoSideInfoFromWidth(fragPixelPos.x, sideNormalsSize.x); - vec2 deltaDepthUV = vec2(1.0) / vec2(getDepthTextureSideSize(0)); + vec2 deltaDepthUV = vec2(1.0) / getDepthTextureSideSize(0); // From now on, fragUVPos is the UV pos in the side fragUVPos = getSideUVFromFramebufferUV(side, fragUVPos); From 829e5ff44f4ccffeb8458b5ec20753e8d6e09f4c Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Thu, 8 Nov 2018 13:37:22 +0100 Subject: [PATCH 153/286] Same android problem fix, but elsewhere --- libraries/render-utils/src/ssao.slh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libraries/render-utils/src/ssao.slh b/libraries/render-utils/src/ssao.slh index af7f8029b8..942578fc44 100644 --- a/libraries/render-utils/src/ssao.slh +++ b/libraries/render-utils/src/ssao.slh @@ -520,7 +520,7 @@ float computeOcclusion(ivec4 side, vec2 fragUVPos, vec3 fragPositionES, TBNFrame #else // Step is adapted to Mip level float radius = deltaRadius; - float mipLevel = evalMipFromRadius(radius * float(doFetchMips())); + float mipLevel = float(evalMipFromRadius(radius * float(doFetchMips()))); while (radius<=searchRadius) { fragUVPos += deltaTapUV; @@ -530,8 +530,8 @@ float computeOcclusion(ivec4 side, vec2 fragUVPos, vec3 fragPositionES, TBNFrame if (tapMipZ.x != mipLevel) { mipLevel = tapMipZ.x; - deltaRadius *= 2; - deltaTapUV *= 2; + deltaRadius *= 2.0; + deltaTapUV *= 2.0; sideDepthSize = getDepthTextureSideSize(int(mipLevel)); } radius += deltaRadius; From d64127aa9c9defb13e5e10aba50c12b1e947af51 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Thu, 8 Nov 2018 13:48:19 +0100 Subject: [PATCH 154/286] Android shader compilation fix --- libraries/render-utils/src/ssao.slh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/render-utils/src/ssao.slh b/libraries/render-utils/src/ssao.slh index 942578fc44..ee31f2b701 100644 --- a/libraries/render-utils/src/ssao.slh +++ b/libraries/render-utils/src/ssao.slh @@ -451,7 +451,7 @@ float computeWeightedHorizon(float horizonLimit, float distanceSquared) { } <@func computeHorizon()@> - if (tapUVPos.x<0 || tapUVPos.y<0 || tapUVPos.x>=1.0 || tapUVPos.y>=1.0) { + if (tapUVPos.x<0.0 || tapUVPos.y<0.0 || tapUVPos.x>=1.0 || tapUVPos.y>=1.0) { // Early exit because we've hit the borders of the frame break; } From 87e6bffc461c0577c58cd3c97d9e3cc5fcb244b9 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Thu, 8 Nov 2018 14:28:00 +0100 Subject: [PATCH 155/286] Added sampler2Darray precision for GLES --- libraries/shaders/headers/310es/header.glsl | 1 + 1 file changed, 1 insertion(+) diff --git a/libraries/shaders/headers/310es/header.glsl b/libraries/shaders/headers/310es/header.glsl index ac48d5c94c..9a0af85281 100644 --- a/libraries/shaders/headers/310es/header.glsl +++ b/libraries/shaders/headers/310es/header.glsl @@ -13,3 +13,4 @@ precision highp float; precision highp samplerBuffer; precision highp sampler2DShadow; precision highp sampler2DArrayShadow; +precision lowp sampler2DArray; From be7806644a4deae2899b1b65b32ee7f3415d3788 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Thu, 8 Nov 2018 17:29:00 +0100 Subject: [PATCH 156/286] Removed ternary operators with mix (try to limit branching) --- libraries/render-utils/src/ssao.slh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/libraries/render-utils/src/ssao.slh b/libraries/render-utils/src/ssao.slh index ee31f2b701..b683dc38f1 100644 --- a/libraries/render-utils/src/ssao.slh +++ b/libraries/render-utils/src/ssao.slh @@ -254,11 +254,11 @@ LAYOUT(binding=RENDER_UTILS_TEXTURE_SSAO_DEPTH) uniform sampler2D depthPyramidTe LAYOUT(binding=RENDER_UTILS_TEXTURE_SSAO_NORMAL) uniform sampler2D normalTex; vec2 getFramebufferUVFromSideUV(ivec4 side, vec2 uv) { - return isStereo() ? vec2((uv.x + float(getStereoSide(side))) * 0.5, uv.y) : uv; + return mix(uv, vec2((uv.x + float(getStereoSide(side))) * 0.5, uv.y), float(isStereo())); } vec2 getSideUVFromFramebufferUV(ivec4 side, vec2 uv) { - return isStereo() ? vec2(uv.x * 2.0 - float(getStereoSide(side)), uv.y) : uv; + return mix(uv, vec2(uv.x * 2.0 - float(getStereoSide(side)), uv.y), float(isStereo())); } vec2 getDepthTextureSize(int level) { @@ -348,7 +348,7 @@ vec3 getMinDelta(vec3 centralPoint, vec3 offsetPointPos, vec3 offsetPointNeg) { float sqrLength0 = dot(delta0, delta0); float sqrLength1 = dot(delta1, delta1); - return sqrLength0 < sqrLength1 ? delta0 : delta1; + return mix(delta1, delta0, float(sqrLength0 < sqrLength1)); } const ivec2 UV_RIGHT = ivec2(1,0); @@ -542,7 +542,7 @@ float computeOcclusion(ivec4 side, vec2 fragUVPos, vec3 fragPositionES, TBNFrame #if HBAO_USE_COS_ANGLE occlusion = min(occlusion * getFalloffCosAngleScale(), 1.0); #else - occlusion = horizonLimit > 0.0 ? horizonLimit * getFalloffSinAngleScale() : horizonLimit; + occlusion = horizonLimit * mix(1.0, getFalloffSinAngleScale(), horizonLimit > 0.0); #endif return occlusion; From dd7ce2cfffbdc5693ab7c4e256d92a00a464306e Mon Sep 17 00:00:00 2001 From: NissimHadar Date: Thu, 8 Nov 2018 08:37:44 -0800 Subject: [PATCH 157/286] Enable GitHub fields correctly on MAC --- tools/auto-tester/src/ui/AutoTester.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tools/auto-tester/src/ui/AutoTester.cpp b/tools/auto-tester/src/ui/AutoTester.cpp index d49f3aaa1c..9eac1a35e5 100644 --- a/tools/auto-tester/src/ui/AutoTester.cpp +++ b/tools/auto-tester/src/ui/AutoTester.cpp @@ -97,7 +97,12 @@ void AutoTester::startTestsEvaluation(const bool isRunningFromCommandLine, } void AutoTester::on_tabWidget_currentChanged(int index) { +// Enable the GitHub edit boxes as required +#ifdef Q_OS_WIN if (index == 0 || index == 2 || index == 3) { +#elif defined Q_OS_MAC + if (index == 0 || index == 1 || index == 2) { +#endif _ui.userLineEdit->setDisabled(false); _ui.branchLineEdit->setDisabled(false); } else { @@ -307,4 +312,4 @@ void AutoTester::updateStatusLabel(const QString& status) { void AutoTester::appendLogWindow(const QString& message) { _ui.plainTextEdit->appendPlainText(message); -} \ No newline at end of file +} From 8b2287126af908a12609e21e559bb2334e4c54d1 Mon Sep 17 00:00:00 2001 From: NissimHadar Date: Thu, 8 Nov 2018 08:38:44 -0800 Subject: [PATCH 158/286] WIP - working on MAC --- tools/auto-tester/src/TestRunner.cpp | 180 +++++++++++++++++---------- tools/auto-tester/src/TestRunner.h | 14 ++- 2 files changed, 123 insertions(+), 71 deletions(-) diff --git a/tools/auto-tester/src/TestRunner.cpp b/tools/auto-tester/src/TestRunner.cpp index 3781024a63..b947f2544a 100644 --- a/tools/auto-tester/src/TestRunner.cpp +++ b/tools/auto-tester/src/TestRunner.cpp @@ -43,27 +43,29 @@ TestRunner::TestRunner(std::vector dayCheckboxes, _url = url; _runNow = runNow; - installerThread = new QThread(); - installerWorker = new Worker(); - installerWorker->moveToThread(installerThread); - installerThread->start(); - connect(this, SIGNAL(startInstaller()), installerWorker, SLOT(runCommand())); - connect(installerWorker, SIGNAL(commandComplete()), this, SLOT(installationComplete())); + _installerThread = new QThread(); + _installerWorker = new Worker(); + + _installerWorker->moveToThread(_installerThread); + _installerThread->start(); + connect(this, SIGNAL(startInstaller()), _installerWorker, SLOT(runCommand())); + connect(_installerWorker, SIGNAL(commandComplete()), this, SLOT(installationComplete())); - interfaceThread = new QThread(); - interfaceWorker = new Worker(); - interfaceThread->start(); - interfaceWorker->moveToThread(interfaceThread); - connect(this, SIGNAL(startInterface()), interfaceWorker, SLOT(runCommand())); - connect(interfaceWorker, SIGNAL(commandComplete()), this, SLOT(interfaceExecutionComplete())); + _interfaceThread = new QThread(); + _interfaceWorker = new Worker(); + + _interfaceThread->start(); + _interfaceWorker->moveToThread(_interfaceThread); + connect(this, SIGNAL(startInterface()), _interfaceWorker, SLOT(runCommand())); + connect(_interfaceWorker, SIGNAL(commandComplete()), this, SLOT(interfaceExecutionComplete())); } TestRunner::~TestRunner() { - delete installerThread; - delete interfaceThread; + delete _installerThread; + delete _installerWorker; - delete interfaceThread; - delete interfaceWorker; + delete _interfaceThread; + delete _interfaceWorker; if (_timer) { delete _timer; @@ -105,30 +107,7 @@ void TestRunner::setWorkingFolder() { #ifdef Q_OS_MAC // Create MAC shell scripts QFile script; - script.setFileName(_workingFolder + "/install_app.sh"); - if (!script.open(QIODevice::WriteOnly | QIODevice::Text)) { - QMessageBox::critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), - "Could not open 'install_app.sh'"); - exit(-1); - } - QString installFolder = QString("\"") + _workingFolder + "/High_Fidelity\""; - if (!QDir().exists(installFolder)) { - QDir().mkdir(installFolder); - } - - // This script installs High Fidelity. It is run as "yes | install_app.sh... so "yes" is killed at the end - script.write("#!/bin/sh\n\n"); - script.write("VOLUME=`hdiutil attach \"$1\" | grep Volumes | awk '{print $3}'`\n"); - - script.write((QString("cp -rf \"$VOLUME/") + "/High Fidelity/interface.app\" \"" + _workingFolder + "/High_Fidelity/\"\n").toStdString().c_str()); - script.write((QString("cp -rf \"$VOLUME/") + "/High Fidelity/Sandbox.app\" \"" + _workingFolder + "/High_Fidelity/\"\n").toStdString().c_str()); - - script.write("hdiutil detach \"$VOLUME\"\n"); - script.write("killall yes\n"); - script.close(); - script.setPermissions(QFile::ReadOwner | QFile::WriteOwner | QFile::ExeOwner); - // This script waits for a process to start script.setFileName(_workingFolder + "/waitForStart.sh"); if (!script.open(QIODevice::WriteOnly | QIODevice::Text)) { @@ -139,27 +118,31 @@ void TestRunner::setWorkingFolder() { script.write("#!/bin/sh\n\n"); script.write("PROCESS=\"$1\"\n"); - script.write("while (pgrep $PROCESS)\n"); + script.write("until (pgrep $PROCESS >nul)\n"); script.write("do\n"); + script.write("\techo waiting for \"$1\" to start\n"); script.write("\tsleep 2\n"); script.write("done\n"); + script.write("echo \"$1\" \"started\"\n"); script.close(); script.setPermissions(QFile::ReadOwner | QFile::WriteOwner | QFile::ExeOwner); - // The Mac shell command returns immediately. This little script waits for a process to complete - script.setFileName(_workingFolder + "/waitForCompletion.sh"); + // The Mac shell command returns immediately. This little script waits for a process to finish + script.setFileName(_workingFolder + "/waitForFinish.sh"); if (!script.open(QIODevice::WriteOnly | QIODevice::Text)) { QMessageBox::critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), - "Could not open 'waitForCompletion.sh'"); + "Could not open 'waitForFinish.sh'"); exit(-1); } script.write("#!/bin/sh\n\n"); script.write("PROCESS=\"$1\"\n"); - script.write("while (pgrep $PROCESS)\n"); + script.write("while (pgrep $PROCESS >nul)\n"); script.write("do\n"); + script.write("\techo waiting for \"$1\" to finish\n"); script.write("\tsleep 2\n"); script.write("done\n"); + script.write("echo \"$1\" \"finished\"\n"); script.close(); script.setPermissions(QFile::ReadOwner | QFile::WriteOwner | QFile::ExeOwner); @@ -177,7 +160,7 @@ void TestRunner::setWorkingFolder() { script.write("set height to 540\n"); script.write("set x to 100\n"); script.write("set y to 100\n\n"); - script.write("tell application \"System Events\" to tell application process \"interface\" to tell window 1 to set {size, position} to {{width, height}, {x, y}})\n"); + script.write("tell application \"System Events\" to tell application process \"interface\" to tell window 1 to set {size, position} to {{width, height}, {x, y}}\n"); script.close(); script.setPermissions(QFile::ReadOwner | QFile::WriteOwner | QFile::ExeOwner); @@ -190,7 +173,7 @@ void TestRunner::setWorkingFolder() { } script.write("#!/bin/sh\n\n"); - script.write((_workingFolder + "/osascript " + _workingFolder + "/setInterfaceSizeAndPosition.scpt\n").toStdString().c_str()); + script.write(("osascript " + _workingFolder + "/setInterfaceSizeAndPosition.scpt\n").toStdString().c_str()); script.close(); script.setPermissions(QFile::ReadOwner | QFile::WriteOwner | QFile::ExeOwner); #endif @@ -281,10 +264,39 @@ void TestRunner::runInstaller() { #ifdef Q_OS_WIN commandLine = "\"" + QDir::toNativeSeparators(installerFullPath) + "\"" + " /S /D=" + QDir::toNativeSeparators(_installationFolder); #elif defined Q_OS_MAC - commandLine = "yes | " + _workingFolder + "/install_app.sh " + _workingFolder + "/HighFidelity-Beta-latest-dev.dmg"; -#endif + // Create installation shell script + QFile script; + script.setFileName(_workingFolder + "/install_app.sh"); + if (!script.open(QIODevice::WriteOnly | QIODevice::Text)) { + QMessageBox::critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), + "Could not open 'install_app.sh'"); + exit(-1); + } + + if (!QDir().exists(_installationFolder)) { + QDir().mkdir(_installationFolder); + } + + // This script installs High Fidelity. It is run as "yes | install_app.sh... so "yes" is killed at the end + script.write("#!/bin/sh\n\n"); + script.write("VOLUME=`hdiutil attach \"$1\" | grep Volumes | awk '{print $3}'`\n"); + + QString folderName {"High Fidelity"}; + if (!_runLatest->isChecked()) { + folderName += QString(" - ") + getPRNumberFromURL(_url->text()); + } - installerWorker->setCommandLine(commandLine); + script.write((QString("cp -rf \"$VOLUME/") + folderName + "/interface.app\" \"" + _workingFolder + "/High_Fidelity/\"\n").toStdString().c_str()); + script.write((QString("cp -rf \"$VOLUME/") + folderName + "/Sandbox.app\" \"" + _workingFolder + "/High_Fidelity/\"\n").toStdString().c_str()); + + script.write("hdiutil detach \"$VOLUME\"\n"); + script.write("killall yes\n"); + script.close(); + script.setPermissions(QFile::ReadOwner | QFile::WriteOwner | QFile::ExeOwner); + commandLine = "yes | " + _workingFolder + "/install_app.sh " + installerFullPath; +#endif + appendLog(commandLine); + _installerWorker->setCommandLine(commandLine); emit startInstaller(); } @@ -415,10 +427,15 @@ void TestRunner::killProcesses() { exit(-1); } #elif defined Q_OS_MAC - QString commandLine = QString("killall interface") + "; " + _workingFolder +"/waitForCompletion.sh interface"; + QString commandLine; + + commandLine = QString("killall interface") + "; " + _workingFolder +"/waitForFinish.sh interface"; system(commandLine.toStdString().c_str()); - - commandLine = QString("killall Sandbox") + "; " + _workingFolder +"/waitForCompletion.sh interface"; + + commandLine = QString("killall Sandbox") + "; " + _workingFolder +"/waitForFinish.sh Sandbox"; + system(commandLine.toStdString().c_str()); + + commandLine = QString("killall Console") + "; " + _workingFolder +"/waitForFinish.sh Console"; system(commandLine.toStdString().c_str()); #endif } @@ -464,28 +481,56 @@ void TestRunner::runInterfaceWithTestScript() { QString commandLine; #ifdef Q_OS_WIN QString exeFile = QString("\"") + QDir::toNativeSeparators(_installationFolder) + "\\interface.exe\""; - commandLine = exeFile + " --url " + url + " --no-updater" + " --testScript " + testScript + - " quitWhenFinished --testResultsLocation " + _snapshotFolder; + commandLine = exeFile + + " --url " + url + + " --no-updater" + + " --no-login-suggestion" + " --testScript " + testScript + " quitWhenFinished" + + " --testResultsLocation " + _snapshotFolder; + + interfaceWorker->setCommandLine(commandLine); + emit startInterface(); #elif defined Q_OS_MAC - // On The Mac, we need to resize Interface. The Interface window opens a few seconds after the process has started, - // so we wait another 10 seconds - // The shell closes if we don't wait for completion, hence the final line - commandLine = "open \"" +_installationFolder + "/interface.app\" --args" + + // On The Mac, we need to resize Interface. The Interface window opens a few seconds after the process + // has started. + // Before starting interface, start a process that will resize interface 10s after it opens + // This is performed by creating a bash script that runs to processes + QFile script; + script.setFileName(_workingFolder + "/runInterfaceTests.sh"); + if (!script.open(QIODevice::WriteOnly | QIODevice::Text)) { + QMessageBox::critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), + "Could not open 'runInterfaceTests.sh'"); + exit(-1); + } + + script.write("#!/bin/sh\n\n"); + + commandLine = _workingFolder +"/waitForStart.sh interface && sleep 10 && " + _workingFolder +"/setInterfaceSizeAndPosition.sh &\n"; + script.write(commandLine.toStdString().c_str()); + + commandLine = + "open \"" +_installationFolder + "/interface.app\" --args" + " --url " + url + " --no-updater" + + " --no-login-suggestion" " --testScript " + testScript + " quitWhenFinished" + " --testResultsLocation " + _snapshotFolder + - "; " + _workingFolder +"/waitForStart.sh interface" + - "; " + "sleep 10" + - "; " + _workingFolder +"/setInterfaceSizeAndPosition.sh" + - "; " + _workingFolder +"/waitForCompletion.sh interface"; + " && " + _workingFolder +"/waitForStart.sh interface" + + " && " + _workingFolder +"/waitForFinish.sh interface"; + + script.write(commandLine.toStdString().c_str()); + + script.close(); + script.setPermissions(QFile::ReadOwner | QFile::WriteOwner | QFile::ExeOwner); + + commandLine = _workingFolder + "/runInterfaceTests.sh"; + _interfaceWorker->setCommandLine(commandLine); + + emit startInterface(); #endif // Helpful for debugging appendLog(commandLine); - - interfaceWorker->setCommandLine(commandLine); - emit startInterface(); } void TestRunner::interfaceExecutionComplete() { @@ -637,6 +682,7 @@ void TestRunner::appendLog(const QString& message) { QString TestRunner::getInstallerNameFromURL(const QString& url) { // An example URL: https://deployment.highfidelity.com/jobs/pr-build/label%3Dwindows/13023/HighFidelity-Beta-Interface-PR14006-be76c43.exe + // On Mac, replace `exe` with `dmg` try { QStringList urlParts = url.split("/"); return urlParts[urlParts.size() - 1]; @@ -654,7 +700,11 @@ QString TestRunner::getPRNumberFromURL(const QString& url) { QStringList urlParts = url.split("/"); QStringList filenameParts = urlParts[urlParts.size() - 1].split("-"); if (filenameParts.size() <= 3) { +#ifdef Q_OS_WIN throw "URL not in expected format, should look like `https://deployment.highfidelity.com/jobs/pr-build/label%3Dwindows/13023/HighFidelity-Beta-Interface-PR14006-be76c43.exe`"; +#elif defined Q_OS_MAC + throw "URL not in expected format, should look like `https://deployment.highfidelity.com/jobs/pr-build/label%3Dwindows/13023/HighFidelity-Beta-Interface-PR14006-be76c43.dmg`"; +#endif } return filenameParts[filenameParts.size() - 2]; } catch (QString errorMessage) { diff --git a/tools/auto-tester/src/TestRunner.h b/tools/auto-tester/src/TestRunner.h index 8267a042b7..11129d2f36 100644 --- a/tools/auto-tester/src/TestRunner.h +++ b/tools/auto-tester/src/TestRunner.h @@ -84,7 +84,8 @@ private slots: signals: void startInstaller(); void startInterface(); - + void startResize(); + private: bool _automatedTestIsRunning{ false }; @@ -130,11 +131,12 @@ private: QDateTime _testStartDateTime; - QThread* installerThread; - QThread* interfaceThread; - Worker* installerWorker; - Worker* interfaceWorker; + QThread* _installerThread; + QThread* _interfaceThread; + Worker* _installerWorker; + Worker* _interfaceWorker; + BuildInformation _buildInformation; }; @@ -150,7 +152,7 @@ signals: void commandComplete(); void startInstaller(); void startInterface(); - + private: QString _commandLine; }; From 05939f8a7c81fef1dc86fdd165d8bb1b4197be25 Mon Sep 17 00:00:00 2001 From: r3tk0n Date: Thu, 8 Nov 2018 11:09:19 -0800 Subject: [PATCH 159/286] Fix settings to only show relevant HMD controls. --- interface/resources/html/tabletHelp.html | 24 ++++++++---------------- 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/interface/resources/html/tabletHelp.html b/interface/resources/html/tabletHelp.html index 279213bbcb..68a34245c7 100644 --- a/interface/resources/html/tabletHelp.html +++ b/interface/resources/html/tabletHelp.html @@ -66,7 +66,7 @@ From 1bd978e4c3a0695532a00d0573f0ef0f0d488ef4 Mon Sep 17 00:00:00 2001 From: r3tk0n Date: Thu, 8 Nov 2018 11:22:43 -0800 Subject: [PATCH 160/286] Clean up code in tabletHelp.html --- interface/resources/html/tabletHelp.html | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/interface/resources/html/tabletHelp.html b/interface/resources/html/tabletHelp.html index 68a34245c7..a1739b3d67 100644 --- a/interface/resources/html/tabletHelp.html +++ b/interface/resources/html/tabletHelp.html @@ -137,29 +137,24 @@ switch (params.handControllerName) { case "oculus": handControllerImageURL = "img/tablet-help-oculus.jpg"; - index = 0; break; case "windowsMR": handControllerImageURL = "img/tablet-help-windowsMR.jpg"; - index = 0; break; case "vive": handControllerImageURL = "img/tablet-help-vive.jpg"; - index = 0; default: - } switch (params.defaultTab) { + case "handControllers": + showHandControllers(); + index = 0; + break; case "gamepad": showGamepad(); index = 1; break; - - case "handControllers": - showHandControllers(); - break; - case "kbm": default: showKbm(); From a6494bf379eaaf20ace3b81c677b4d2a57af70c4 Mon Sep 17 00:00:00 2001 From: r3tk0n Date: Thu, 8 Nov 2018 11:25:55 -0800 Subject: [PATCH 161/286] Fix edge case with Oculus. --- interface/resources/html/tabletHelp.html | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/interface/resources/html/tabletHelp.html b/interface/resources/html/tabletHelp.html index a1739b3d67..f3e60ff16f 100644 --- a/interface/resources/html/tabletHelp.html +++ b/interface/resources/html/tabletHelp.html @@ -135,15 +135,15 @@ } switch (params.handControllerName) { - case "oculus": - handControllerImageURL = "img/tablet-help-oculus.jpg"; - break; case "windowsMR": handControllerImageURL = "img/tablet-help-windowsMR.jpg"; break; case "vive": handControllerImageURL = "img/tablet-help-vive.jpg"; + break; + case "oculus": default: + handControllerImageURL = "img/tablet-help-oculus.jpg"; } switch (params.defaultTab) { From 298c5efe69a1d347b84481f86d83dbf7509f8682 Mon Sep 17 00:00:00 2001 From: Simon Walton Date: Thu, 8 Nov 2018 12:13:56 -0800 Subject: [PATCH 162/286] Simple demo of uploading file in chunks --- .../resources/web/content/js/content.js | 80 +++++++++++++++++++ 1 file changed, 80 insertions(+) diff --git a/domain-server/resources/web/content/js/content.js b/domain-server/resources/web/content/js/content.js index 346e846748..61fee425ff 100644 --- a/domain-server/resources/web/content/js/content.js +++ b/domain-server/resources/web/content/js/content.js @@ -14,6 +14,81 @@ $(document).ready(function(){ return html; } + function uploadInChunks(file) { + var fileSize = file.size; + var filename = file.name; + var CHUNK_SIZE = 16384; + + for(p = 0; p <= fileSize; p += CHUNK_SIZE) { + var chunk = file.slice(p, p + CHUNK_SIZE, file.type); + var chunkFormData = new FormData(); + chunkFormData.append('restore-file-chunk', chunk, filename); + var ajaxParams = { + url: '/content/upload', + type: 'POST', + async: false, + timeout: 60, + processData: false, + contentType: false, + data: chunkFormData + }; + var ajaxObject = $.ajax(ajaxParams); + + } + + + } + + function uploadNextChunk(file, offset) + { + var fileSize = file.size; + var filename = file.name; + + var CHUNK_SIZE = 16384; + if (offset == undefined) { + offset = 0; + } + var isFinal = fileSize - offset > CHUNK_SIZE ? false : true; + var nextChunkSize = Math.min(fileSize - offset, CHUNK_SIZE); + var chunk = file.slice(offset, offset + CHUNK_SIZE, file.type); + var chunkFormData = new FormData(); + + var formItemName = isFinal ? 'restore-file-chunk-final' : 'restore-file-chunk'; + chunkFormData.append(formItemName, chunk, filename); + var ajaxParams = { + url: '/content/upload', + type: 'POST', + timeout: 30000, + cache: false, + processData: false, + data: chunkFormData + }; + + var ajaxObject = $.ajax(ajaxParams); + ajaxObject.fail(function(jqXHR, textStatus, errorThrown) { + showErrorMessage( + "Error", + "There was a problem restoring domain content.\n" + + "Please ensure that the content archive or entity file is valid and try again." + ); + }); + + if (!isFinal) { + ajaxObject.done(function (data, textStatus, jqXHR) + {uploadNextChunk(file, offset + CHUNK_SIZE);}); + } else { + ajaxObject.done(function(data, textStatus, jqXHR) { + isRestoring = true; + + // immediately reload backup information since one should be restoring now + reloadBackupInformation(); + + swal.close(); + }); + } + + } + function setupBackupUpload() { // construct the HTML needed for the settings backup panel var html = "
"; @@ -56,6 +131,11 @@ $(document).ready(function(){ showSpinnerAlert("Uploading content to restore"); + + uploadNextChunk(files[0]); + return; + + // Previous one-upload method. $.ajax({ url: '/content/upload', type: 'POST', From b4be7544b9fedd28b95334ed1f811b2a4c054407 Mon Sep 17 00:00:00 2001 From: Jason Najera <39922250+r3tk0n@users.noreply.github.com> Date: Thu, 8 Nov 2018 13:15:19 -0800 Subject: [PATCH 163/286] Attempt to fix spacing Weird thing is it still looked fine in GitHub's editor... --- interface/resources/html/tabletHelp.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/resources/html/tabletHelp.html b/interface/resources/html/tabletHelp.html index f3e60ff16f..a48ee9d6d1 100644 --- a/interface/resources/html/tabletHelp.html +++ b/interface/resources/html/tabletHelp.html @@ -147,7 +147,7 @@ } switch (params.defaultTab) { - case "handControllers": + case "handControllers": showHandControllers(); index = 0; break; From 59bba5b6e69c9487f4a01b58f97b43d7e3f10129 Mon Sep 17 00:00:00 2001 From: Jason Najera <39922250+r3tk0n@users.noreply.github.com> Date: Thu, 8 Nov 2018 13:16:24 -0800 Subject: [PATCH 164/286] More attempt to fix indentation. Still looked fine in github's editor and N++. --- interface/resources/html/tabletHelp.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/resources/html/tabletHelp.html b/interface/resources/html/tabletHelp.html index a48ee9d6d1..1399c98f9f 100644 --- a/interface/resources/html/tabletHelp.html +++ b/interface/resources/html/tabletHelp.html @@ -149,7 +149,7 @@ switch (params.defaultTab) { case "handControllers": showHandControllers(); - index = 0; + index = 0; break; case "gamepad": showGamepad(); From 84209c0e5319a1b7cfbb23c65b2f30a0834a50fd Mon Sep 17 00:00:00 2001 From: r3tk0n Date: Thu, 8 Nov 2018 13:18:25 -0800 Subject: [PATCH 165/286] Finally fix all indentation. --- interface/resources/html/tabletHelp.html | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/interface/resources/html/tabletHelp.html b/interface/resources/html/tabletHelp.html index 1399c98f9f..cc931580c3 100644 --- a/interface/resources/html/tabletHelp.html +++ b/interface/resources/html/tabletHelp.html @@ -139,11 +139,11 @@ handControllerImageURL = "img/tablet-help-windowsMR.jpg"; break; case "vive": - handControllerImageURL = "img/tablet-help-vive.jpg"; - break; - case "oculus": + handControllerImageURL = "img/tablet-help-vive.jpg"; + break; + case "oculus": default: - handControllerImageURL = "img/tablet-help-oculus.jpg"; + handControllerImageURL = "img/tablet-help-oculus.jpg"; } switch (params.defaultTab) { From bb60324335d4c733e3d8cd26bd0519b458b45731 Mon Sep 17 00:00:00 2001 From: Simon Walton Date: Thu, 8 Nov 2018 14:01:27 -0800 Subject: [PATCH 166/286] Chunked content upload - working proof-of-concept --- .../resources/web/content/js/content.js | 5 +- domain-server/src/DomainServer.cpp | 76 ++++++++++++------- domain-server/src/DomainServer.h | 2 + 3 files changed, 54 insertions(+), 29 deletions(-) diff --git a/domain-server/resources/web/content/js/content.js b/domain-server/resources/web/content/js/content.js index 61fee425ff..d581ddaf85 100644 --- a/domain-server/resources/web/content/js/content.js +++ b/domain-server/resources/web/content/js/content.js @@ -44,7 +44,7 @@ $(document).ready(function(){ var fileSize = file.size; var filename = file.name; - var CHUNK_SIZE = 16384; + var CHUNK_SIZE = 65536; if (offset == undefined) { offset = 0; } @@ -58,9 +58,10 @@ $(document).ready(function(){ var ajaxParams = { url: '/content/upload', type: 'POST', - timeout: 30000, + timeout: 30000, // 30 s cache: false, processData: false, + contentType: false, data: chunkFormData }; diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index b540592d7e..fd378cb39b 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -2258,41 +2258,63 @@ bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url // check the file extension to see what kind of file this is // to make sure we handle this filetype for a content restore auto dispositionValue = QString(firstFormData.first.value("Content-Disposition")); - auto formDataFilenameRegex = QRegExp("filename=\"(.+)\""); - auto matchIndex = formDataFilenameRegex.indexIn(dispositionValue); + QRegExp formDataFieldsRegex(R":(name="(restore-file.*)".*filename="(.+)"):"); + auto matchIndex = formDataFieldsRegex.indexIn(dispositionValue); + QString formItemName = ""; QString uploadedFilename = ""; if (matchIndex != -1) { - uploadedFilename = formDataFilenameRegex.cap(1); + formItemName = formDataFieldsRegex.cap(1); + uploadedFilename = formDataFieldsRegex.cap(2); } - if (uploadedFilename.endsWith(".json", Qt::CaseInsensitive) - || uploadedFilename.endsWith(".json.gz", Qt::CaseInsensitive)) { - // invoke our method to hand the new octree file off to the octree server - QMetaObject::invokeMethod(this, "handleOctreeFileReplacement", - Qt::QueuedConnection, Q_ARG(QByteArray, firstFormData.second)); - - // respond with a 200 for success + _pendingUploadedContent += firstFormData.second; + if (formItemName == "restore-file-chunk") { + // Received another chunk connection->respond(HTTPConnection::StatusCode200); - } else if (uploadedFilename.endsWith(".zip", Qt::CaseInsensitive)) { - auto deferred = makePromise("recoverFromUploadedBackup"); + } else if (formItemName == "restore-file-chunk-final") { + if (uploadedFilename.endsWith(".json", Qt::CaseInsensitive) + || uploadedFilename.endsWith(".json.gz", Qt::CaseInsensitive)) { + // invoke our method to hand the new octree file off to the octree server + QMetaObject::invokeMethod(this, "handleOctreeFileReplacement", + Qt::QueuedConnection, Q_ARG(QByteArray, _pendingUploadedContent)); + _pendingUploadedContent.clear(); + // respond with a 200 for success + connection->respond(HTTPConnection::StatusCode200); + } else if (formItemName == "restore-file") { + if (uploadedFilename.endsWith(".json", Qt::CaseInsensitive) + || uploadedFilename.endsWith(".json.gz", Qt::CaseInsensitive)) { + // invoke our method to hand the new octree file off to the octree server + QMetaObject::invokeMethod(this, "handleOctreeFileReplacement", + Qt::QueuedConnection, Q_ARG(QByteArray, firstFormData.second)); - deferred->then([connectionPtr, JSON_MIME_TYPE](QString error, QVariantMap result) { - if (!connectionPtr) { - return; + _pendingUploadedContent.clear(); + // respond with a 200 for success + connection->respond(HTTPConnection::StatusCode200); + } else if (uploadedFilename.endsWith(".zip", Qt::CaseInsensitive)) { + auto deferred = makePromise("recoverFromUploadedBackup"); + + deferred->then([connectionPtr, JSON_MIME_TYPE](QString error, QVariantMap result) { + if (!connectionPtr) { + return; + } + + QJsonObject rootJSON; + auto success = result["success"].toBool(); + rootJSON["success"] = success; + QJsonDocument docJSON(rootJSON); + connectionPtr->respond(success ? HTTPConnection::StatusCode200 : HTTPConnection::StatusCode400, docJSON.toJson(), + JSON_MIME_TYPE.toUtf8()); + }); + + _contentManager->recoverFromUploadedBackup(deferred, firstFormData.second); + _pendingUploadedContent.clear(); + + return true; } - - QJsonObject rootJSON; - auto success = result["success"].toBool(); - rootJSON["success"] = success; - QJsonDocument docJSON(rootJSON); - connectionPtr->respond(success ? HTTPConnection::StatusCode200 : HTTPConnection::StatusCode400, docJSON.toJson(), - JSON_MIME_TYPE.toUtf8()); - }); - - _contentManager->recoverFromUploadedBackup(deferred, firstFormData.second); - - return true; + } else { + connection->respond(HTTPConnection::StatusCode400); + } } else { // we don't have handling for this filetype, send back a 400 for failure connection->respond(HTTPConnection::StatusCode400); diff --git a/domain-server/src/DomainServer.h b/domain-server/src/DomainServer.h index e2bddc1aa5..db50af546e 100644 --- a/domain-server/src/DomainServer.h +++ b/domain-server/src/DomainServer.h @@ -281,6 +281,8 @@ private: QHash> _pendingOAuthConnections; + QByteArray _pendingUploadedContent; + QThread _assetClientThread; }; From 8aea237ad755ebee312e80d06505769019fd5bd6 Mon Sep 17 00:00:00 2001 From: NissimHadar Date: Thu, 8 Nov 2018 14:26:27 -0800 Subject: [PATCH 167/286] Fixed endless wait --- tools/auto-tester/src/TestRunner.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tools/auto-tester/src/TestRunner.cpp b/tools/auto-tester/src/TestRunner.cpp index b947f2544a..740b3f4087 100644 --- a/tools/auto-tester/src/TestRunner.cpp +++ b/tools/auto-tester/src/TestRunner.cpp @@ -118,7 +118,7 @@ void TestRunner::setWorkingFolder() { script.write("#!/bin/sh\n\n"); script.write("PROCESS=\"$1\"\n"); - script.write("until (pgrep $PROCESS >nul)\n"); + script.write("until (pgrep -x $PROCESS >nul)\n"); script.write("do\n"); script.write("\techo waiting for \"$1\" to start\n"); script.write("\tsleep 2\n"); @@ -137,7 +137,7 @@ void TestRunner::setWorkingFolder() { script.write("#!/bin/sh\n\n"); script.write("PROCESS=\"$1\"\n"); - script.write("while (pgrep $PROCESS >nul)\n"); + script.write("while (pgrep -x $PROCESS >nul)\n"); script.write("do\n"); script.write("\techo waiting for \"$1\" to finish\n"); script.write("\tsleep 2\n"); @@ -515,7 +515,6 @@ void TestRunner::runInterfaceWithTestScript() { " --no-login-suggestion" " --testScript " + testScript + " quitWhenFinished" + " --testResultsLocation " + _snapshotFolder + - " && " + _workingFolder +"/waitForStart.sh interface" + " && " + _workingFolder +"/waitForFinish.sh interface"; script.write(commandLine.toStdString().c_str()); From 9b60103f47c1d38b177817e7600b57feb998a08a Mon Sep 17 00:00:00 2001 From: NissimHadar Date: Thu, 8 Nov 2018 15:07:32 -0800 Subject: [PATCH 168/286] Renaming auto-tester ==> nitpick --- tools/CMakeLists.txt | 4 +- .../AppDataHighFidelity/Interface.json | 0 .../Interface/AccountInfo.bin | Bin .../Interface/avatarbookmarks.json | 0 .../assignment-client/entities/models.json.gz | Bin .../domain-server/AccountInfo.bin | Bin .../domain-server/config.json | 0 .../domain-server/entities/models.json.gz | Bin tools/{auto-tester => nitpick}/CMakeLists.txt | 4 +- tools/{auto-tester => nitpick}/Create.PNG | Bin tools/{auto-tester => nitpick}/Evaluate.PNG | Bin tools/{auto-tester => nitpick}/README.md | 29 +++--- tools/{auto-tester => nitpick}/Run.PNG | Bin .../TestRailSelector.PNG | Bin .../Web Interface.PNG | Bin .../{auto-tester => nitpick}/WebInterface.PNG | Bin tools/{auto-tester => nitpick}/Windows.PNG | Bin .../nitpickMismatchExample.PNG} | Bin tools/{auto-tester => nitpick}/setup_7z.PNG | Bin .../src/AWSInterface.cpp | 0 .../src/AWSInterface.h | 0 .../src/Downloader.cpp | 0 .../{auto-tester => nitpick}/src/Downloader.h | 0 .../src/ImageComparer.cpp | 0 .../src/ImageComparer.h | 0 .../src/PythonInterface.cpp | 0 .../src/PythonInterface.h | 0 tools/{auto-tester => nitpick}/src/Test.cpp | 64 ++++++------ tools/{auto-tester => nitpick}/src/Test.h | 2 +- .../src/TestRailInterface.cpp | 0 .../src/TestRailInterface.h | 0 .../src/TestRunner.cpp | 20 ++-- .../{auto-tester => nitpick}/src/TestRunner.h | 0 tools/{auto-tester => nitpick}/src/common.h | 0 tools/{auto-tester => nitpick}/src/main.cpp | 12 +-- .../src/ui/BusyWindow.cpp | 0 .../src/ui/BusyWindow.h | 0 .../src/ui/BusyWindow.ui | 0 .../src/ui/HelpWindow.cpp | 0 .../src/ui/HelpWindow.h | 0 .../src/ui/HelpWindow.ui | 2 +- .../src/ui/MismatchWindow.cpp | 0 .../src/ui/MismatchWindow.h | 0 .../src/ui/MismatchWindow.ui | 0 .../src/ui/Nitpick.cpp} | 92 +++++++++--------- .../AutoTester.h => nitpick/src/ui/Nitpick.h} | 18 ++-- .../src/ui/Nitpick.ui} | 6 +- .../src/ui/TestRailResultsSelectorWindow.cpp | 0 .../src/ui/TestRailResultsSelectorWindow.h | 0 .../src/ui/TestRailResultsSelectorWindow.ui | 0 .../src/ui/TestRailRunSelectorWindow.cpp | 0 .../src/ui/TestRailRunSelectorWindow.h | 0 .../src/ui/TestRailRunSelectorWindow.ui | 0 .../ui/TestRailTestCasesSelectorWindow.cpp | 0 .../src/ui/TestRailTestCasesSelectorWindow.h | 0 .../src/ui/TestRailTestCasesSelectorWindow.ui | 0 56 files changed, 126 insertions(+), 127 deletions(-) rename tools/{auto-tester => nitpick}/AppDataHighFidelity/Interface.json (100%) rename tools/{auto-tester => nitpick}/AppDataHighFidelity/Interface/AccountInfo.bin (100%) rename tools/{auto-tester => nitpick}/AppDataHighFidelity/Interface/avatarbookmarks.json (100%) rename tools/{auto-tester => nitpick}/AppDataHighFidelity/assignment-client/entities/models.json.gz (100%) rename tools/{auto-tester => nitpick}/AppDataHighFidelity/domain-server/AccountInfo.bin (100%) rename tools/{auto-tester => nitpick}/AppDataHighFidelity/domain-server/config.json (100%) rename tools/{auto-tester => nitpick}/AppDataHighFidelity/domain-server/entities/models.json.gz (100%) rename tools/{auto-tester => nitpick}/CMakeLists.txt (95%) rename tools/{auto-tester => nitpick}/Create.PNG (100%) rename tools/{auto-tester => nitpick}/Evaluate.PNG (100%) rename tools/{auto-tester => nitpick}/README.md (93%) rename tools/{auto-tester => nitpick}/Run.PNG (100%) rename tools/{auto-tester => nitpick}/TestRailSelector.PNG (100%) rename tools/{auto-tester => nitpick}/Web Interface.PNG (100%) rename tools/{auto-tester => nitpick}/WebInterface.PNG (100%) rename tools/{auto-tester => nitpick}/Windows.PNG (100%) rename tools/{auto-tester/autoTesterMismatchExample.PNG => nitpick/nitpickMismatchExample.PNG} (100%) rename tools/{auto-tester => nitpick}/setup_7z.PNG (100%) rename tools/{auto-tester => nitpick}/src/AWSInterface.cpp (100%) rename tools/{auto-tester => nitpick}/src/AWSInterface.h (100%) rename tools/{auto-tester => nitpick}/src/Downloader.cpp (100%) rename tools/{auto-tester => nitpick}/src/Downloader.h (100%) rename tools/{auto-tester => nitpick}/src/ImageComparer.cpp (100%) rename tools/{auto-tester => nitpick}/src/ImageComparer.h (100%) rename tools/{auto-tester => nitpick}/src/PythonInterface.cpp (100%) rename tools/{auto-tester => nitpick}/src/PythonInterface.h (100%) rename tools/{auto-tester => nitpick}/src/Test.cpp (94%) rename tools/{auto-tester => nitpick}/src/Test.h (98%) rename tools/{auto-tester => nitpick}/src/TestRailInterface.cpp (100%) rename tools/{auto-tester => nitpick}/src/TestRailInterface.h (100%) rename tools/{auto-tester => nitpick}/src/TestRunner.cpp (98%) rename tools/{auto-tester => nitpick}/src/TestRunner.h (100%) rename tools/{auto-tester => nitpick}/src/common.h (100%) rename tools/{auto-tester => nitpick}/src/main.cpp (89%) rename tools/{auto-tester => nitpick}/src/ui/BusyWindow.cpp (100%) rename tools/{auto-tester => nitpick}/src/ui/BusyWindow.h (100%) rename tools/{auto-tester => nitpick}/src/ui/BusyWindow.ui (100%) rename tools/{auto-tester => nitpick}/src/ui/HelpWindow.cpp (100%) rename tools/{auto-tester => nitpick}/src/ui/HelpWindow.h (100%) rename tools/{auto-tester => nitpick}/src/ui/HelpWindow.ui (96%) rename tools/{auto-tester => nitpick}/src/ui/MismatchWindow.cpp (100%) rename tools/{auto-tester => nitpick}/src/ui/MismatchWindow.h (100%) rename tools/{auto-tester => nitpick}/src/ui/MismatchWindow.ui (100%) rename tools/{auto-tester/src/ui/AutoTester.cpp => nitpick/src/ui/Nitpick.cpp} (72%) rename tools/{auto-tester/src/ui/AutoTester.h => nitpick/src/ui/Nitpick.h} (92%) rename tools/{auto-tester/src/ui/AutoTester.ui => nitpick/src/ui/Nitpick.ui} (99%) rename tools/{auto-tester => nitpick}/src/ui/TestRailResultsSelectorWindow.cpp (100%) rename tools/{auto-tester => nitpick}/src/ui/TestRailResultsSelectorWindow.h (100%) rename tools/{auto-tester => nitpick}/src/ui/TestRailResultsSelectorWindow.ui (100%) rename tools/{auto-tester => nitpick}/src/ui/TestRailRunSelectorWindow.cpp (100%) rename tools/{auto-tester => nitpick}/src/ui/TestRailRunSelectorWindow.h (100%) rename tools/{auto-tester => nitpick}/src/ui/TestRailRunSelectorWindow.ui (100%) rename tools/{auto-tester => nitpick}/src/ui/TestRailTestCasesSelectorWindow.cpp (100%) rename tools/{auto-tester => nitpick}/src/ui/TestRailTestCasesSelectorWindow.h (100%) rename tools/{auto-tester => nitpick}/src/ui/TestRailTestCasesSelectorWindow.ui (100%) diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt index 1c36306410..8ab84372cb 100644 --- a/tools/CMakeLists.txt +++ b/tools/CMakeLists.txt @@ -33,6 +33,6 @@ if (BUILD_TOOLS) add_subdirectory(oven) set_target_properties(oven PROPERTIES FOLDER "Tools") - add_subdirectory(auto-tester) - set_target_properties(auto-tester PROPERTIES FOLDER "Tools") + add_subdirectory(nitpick) + set_target_properties(nitpick PROPERTIES FOLDER "Tools") endif() diff --git a/tools/auto-tester/AppDataHighFidelity/Interface.json b/tools/nitpick/AppDataHighFidelity/Interface.json similarity index 100% rename from tools/auto-tester/AppDataHighFidelity/Interface.json rename to tools/nitpick/AppDataHighFidelity/Interface.json diff --git a/tools/auto-tester/AppDataHighFidelity/Interface/AccountInfo.bin b/tools/nitpick/AppDataHighFidelity/Interface/AccountInfo.bin similarity index 100% rename from tools/auto-tester/AppDataHighFidelity/Interface/AccountInfo.bin rename to tools/nitpick/AppDataHighFidelity/Interface/AccountInfo.bin diff --git a/tools/auto-tester/AppDataHighFidelity/Interface/avatarbookmarks.json b/tools/nitpick/AppDataHighFidelity/Interface/avatarbookmarks.json similarity index 100% rename from tools/auto-tester/AppDataHighFidelity/Interface/avatarbookmarks.json rename to tools/nitpick/AppDataHighFidelity/Interface/avatarbookmarks.json diff --git a/tools/auto-tester/AppDataHighFidelity/assignment-client/entities/models.json.gz b/tools/nitpick/AppDataHighFidelity/assignment-client/entities/models.json.gz similarity index 100% rename from tools/auto-tester/AppDataHighFidelity/assignment-client/entities/models.json.gz rename to tools/nitpick/AppDataHighFidelity/assignment-client/entities/models.json.gz diff --git a/tools/auto-tester/AppDataHighFidelity/domain-server/AccountInfo.bin b/tools/nitpick/AppDataHighFidelity/domain-server/AccountInfo.bin similarity index 100% rename from tools/auto-tester/AppDataHighFidelity/domain-server/AccountInfo.bin rename to tools/nitpick/AppDataHighFidelity/domain-server/AccountInfo.bin diff --git a/tools/auto-tester/AppDataHighFidelity/domain-server/config.json b/tools/nitpick/AppDataHighFidelity/domain-server/config.json similarity index 100% rename from tools/auto-tester/AppDataHighFidelity/domain-server/config.json rename to tools/nitpick/AppDataHighFidelity/domain-server/config.json diff --git a/tools/auto-tester/AppDataHighFidelity/domain-server/entities/models.json.gz b/tools/nitpick/AppDataHighFidelity/domain-server/entities/models.json.gz similarity index 100% rename from tools/auto-tester/AppDataHighFidelity/domain-server/entities/models.json.gz rename to tools/nitpick/AppDataHighFidelity/domain-server/entities/models.json.gz diff --git a/tools/auto-tester/CMakeLists.txt b/tools/nitpick/CMakeLists.txt similarity index 95% rename from tools/auto-tester/CMakeLists.txt rename to tools/nitpick/CMakeLists.txt index c8c0a336d2..543b9c9b47 100644 --- a/tools/auto-tester/CMakeLists.txt +++ b/tools/nitpick/CMakeLists.txt @@ -1,4 +1,4 @@ -set (TARGET_NAME auto-tester) +set (TARGET_NAME nitpick) project(${TARGET_NAME}) # Automatically run UIC and MOC. This replaces the older WRAP macros @@ -22,7 +22,7 @@ set (QT_LIBRARIES Qt5::Core Qt5::Widgets QT::Gui Qt5::Xml) if (WIN32) # Do not show Console - set_property (TARGET auto-tester PROPERTY WIN32_EXECUTABLE true) + set_property (TARGET nitpick PROPERTY WIN32_EXECUTABLE true) endif() target_zlib() diff --git a/tools/auto-tester/Create.PNG b/tools/nitpick/Create.PNG similarity index 100% rename from tools/auto-tester/Create.PNG rename to tools/nitpick/Create.PNG diff --git a/tools/auto-tester/Evaluate.PNG b/tools/nitpick/Evaluate.PNG similarity index 100% rename from tools/auto-tester/Evaluate.PNG rename to tools/nitpick/Evaluate.PNG diff --git a/tools/auto-tester/README.md b/tools/nitpick/README.md similarity index 93% rename from tools/auto-tester/README.md rename to tools/nitpick/README.md index 0bec83d703..d21614f5f5 100644 --- a/tools/auto-tester/README.md +++ b/tools/nitpick/README.md @@ -1,11 +1,11 @@ -# Auto Tester +# nitpick -The auto-tester is a stand alone application that provides a mechanism for regression testing. The general idea is simple: +Nitpick is a stand alone application that provides a mechanism for regression testing. The general idea is simple: * Each test folder has a script that produces a set of snapshots. * The snapshots are compared to a 'canonical' set of images that have been produced beforehand. * The result, if any test failed, is a zipped folder describing the failure. -Auto-tester has 5 functions, separated into 4 tabs: +Nitpick has 5 functions, separated into 4 tabs: 1. Creating tests, MD files and recursive scripts 1. Windows task bar utility (Windows only) 1. Running tests @@ -14,12 +14,12 @@ Auto-tester has 5 functions, separated into 4 tabs: ## Installation ### Executable -1. On Windows: download the installer by browsing to [here](). +1. On Windows: download the installer by browsing to [here](). 2. Double click on the installer and install to a convenient location ![](./setup_7z.PNG) -3. To run the auto-tester, double click **auto-tester.exe**. +3. To run nitpick, double click **nitpick.exe**. ### Python -The TestRail interface requires Python 3 to be installed. Auto-Tester has been tested with Python 3.7.0 but should work with newer versions. +The TestRail interface requires Python 3 to be installed. Nitpick has been tested with Python 3.7.0 but should work with newer versions. Python 3 can be downloaded from: 1. Windows installer @@ -41,7 +41,6 @@ Verify that `/usr/local/bin/python3` exists. 1. Enter the AWS account number 1. Enter the secret key 1. Leave region name and ouput format as default [None] - 1. Install the latest release of Boto3 via pip: pip install boto3 #### Mac @@ -78,7 +77,7 @@ This function creates an MD file in the (user-selected) tests root folder. The This function creates a file named `test.md` from a `test.js` script. The user will be asked for the folder containing the test script: ### Details The process to produce the MD file is a simplistic parse of the test script. -- The string in the `autoTester.perform(...)` function call will be the title of the file +- The string in the `nitpick.perform(...)` function call will be the title of the file - Instructions to run the script are then provided: @@ -110,26 +109,26 @@ The various scripts are called in alphabetical order. An example of a recursive script is as follows: ``` -// This is an automatically generated file, created by auto-tester on Jul 5 2018, 10:19 +// This is an automatically generated file, created by nitpick on Jul 5 2018, 10:19 PATH_TO_THE_REPO_PATH_UTILS_FILE = "https://raw.githubusercontent.com/highfidelity/hifi_tests/master/tests/utils/branchUtils.js"; Script.include(PATH_TO_THE_REPO_PATH_UTILS_FILE); -var autoTester = createAutoTester(Script.resolvePath(".")); +var nitpick = createNitpick(Script.resolvePath(".")); -var testsRootPath = autoTester.getTestsRootPath(); +var testsRootPath = nitpick.getTestsRootPath(); if (typeof Test !== 'undefined') { Test.wait(10000); }; -autoTester.enableRecursive(); -autoTester.enableAuto(); +nitpick.enableRecursive(); +nitpick.enableAuto(); Script.include(testsRootPath + "content/overlay/layer/drawInFront/shape/test.js"); Script.include(testsRootPath + "content/overlay/layer/drawInFront/model/test.js"); Script.include(testsRootPath + "content/overlay/layer/drawHUDLayer/test.js"); -autoTester.runRecursive(); +nitpick.runRecursive(); ``` ## Create all Recursive Scripts ### Usage @@ -186,7 +185,7 @@ Evaluation proceeds in a number of steps: 1. The images are then pair-wise compared, using the SSIM algorithm. A fixed threshold is used to define a mismatch. 1. In interactive mode - a window is opened showing the expected image, actual image, difference image and error: -![](./autoTesterMismatchExample.PNG) +![](./nitpickMismatchExample.PNG) 1. If not in interactive mode, or the user has defined the results as an error, an error is written into the error folder. The error itself is a folder with the 3 images and a small text file containing details. diff --git a/tools/auto-tester/Run.PNG b/tools/nitpick/Run.PNG similarity index 100% rename from tools/auto-tester/Run.PNG rename to tools/nitpick/Run.PNG diff --git a/tools/auto-tester/TestRailSelector.PNG b/tools/nitpick/TestRailSelector.PNG similarity index 100% rename from tools/auto-tester/TestRailSelector.PNG rename to tools/nitpick/TestRailSelector.PNG diff --git a/tools/auto-tester/Web Interface.PNG b/tools/nitpick/Web Interface.PNG similarity index 100% rename from tools/auto-tester/Web Interface.PNG rename to tools/nitpick/Web Interface.PNG diff --git a/tools/auto-tester/WebInterface.PNG b/tools/nitpick/WebInterface.PNG similarity index 100% rename from tools/auto-tester/WebInterface.PNG rename to tools/nitpick/WebInterface.PNG diff --git a/tools/auto-tester/Windows.PNG b/tools/nitpick/Windows.PNG similarity index 100% rename from tools/auto-tester/Windows.PNG rename to tools/nitpick/Windows.PNG diff --git a/tools/auto-tester/autoTesterMismatchExample.PNG b/tools/nitpick/nitpickMismatchExample.PNG similarity index 100% rename from tools/auto-tester/autoTesterMismatchExample.PNG rename to tools/nitpick/nitpickMismatchExample.PNG diff --git a/tools/auto-tester/setup_7z.PNG b/tools/nitpick/setup_7z.PNG similarity index 100% rename from tools/auto-tester/setup_7z.PNG rename to tools/nitpick/setup_7z.PNG diff --git a/tools/auto-tester/src/AWSInterface.cpp b/tools/nitpick/src/AWSInterface.cpp similarity index 100% rename from tools/auto-tester/src/AWSInterface.cpp rename to tools/nitpick/src/AWSInterface.cpp diff --git a/tools/auto-tester/src/AWSInterface.h b/tools/nitpick/src/AWSInterface.h similarity index 100% rename from tools/auto-tester/src/AWSInterface.h rename to tools/nitpick/src/AWSInterface.h diff --git a/tools/auto-tester/src/Downloader.cpp b/tools/nitpick/src/Downloader.cpp similarity index 100% rename from tools/auto-tester/src/Downloader.cpp rename to tools/nitpick/src/Downloader.cpp diff --git a/tools/auto-tester/src/Downloader.h b/tools/nitpick/src/Downloader.h similarity index 100% rename from tools/auto-tester/src/Downloader.h rename to tools/nitpick/src/Downloader.h diff --git a/tools/auto-tester/src/ImageComparer.cpp b/tools/nitpick/src/ImageComparer.cpp similarity index 100% rename from tools/auto-tester/src/ImageComparer.cpp rename to tools/nitpick/src/ImageComparer.cpp diff --git a/tools/auto-tester/src/ImageComparer.h b/tools/nitpick/src/ImageComparer.h similarity index 100% rename from tools/auto-tester/src/ImageComparer.h rename to tools/nitpick/src/ImageComparer.h diff --git a/tools/auto-tester/src/PythonInterface.cpp b/tools/nitpick/src/PythonInterface.cpp similarity index 100% rename from tools/auto-tester/src/PythonInterface.cpp rename to tools/nitpick/src/PythonInterface.cpp diff --git a/tools/auto-tester/src/PythonInterface.h b/tools/nitpick/src/PythonInterface.h similarity index 100% rename from tools/auto-tester/src/PythonInterface.h rename to tools/nitpick/src/PythonInterface.h diff --git a/tools/auto-tester/src/Test.cpp b/tools/nitpick/src/Test.cpp similarity index 94% rename from tools/auto-tester/src/Test.cpp rename to tools/nitpick/src/Test.cpp index 2c262bec1e..13a03c32e8 100644 --- a/tools/auto-tester/src/Test.cpp +++ b/tools/nitpick/src/Test.cpp @@ -19,8 +19,8 @@ #include #include -#include "ui/AutoTester.h" -extern AutoTester* autoTester; +#include "ui/Nitpick.h" +extern Nitpick* nitpick; #include @@ -30,9 +30,9 @@ Test::Test(QProgressBar* progressBar, QCheckBox* checkBoxInteractiveMode) { _mismatchWindow.setModal(true); - if (autoTester) { - autoTester->setUserText(GIT_HUB_DEFAULT_USER); - autoTester->setBranchText(GIT_HUB_DEFAULT_BRANCH); + if (nitpick) { + nitpick->setUserText(GIT_HUB_DEFAULT_USER); + nitpick->setBranchText(GIT_HUB_DEFAULT_BRANCH); } } @@ -240,8 +240,8 @@ void Test::startTestsEvaluation(const bool isRunningFromCommandLine, _expectedImagesFilenames.clear(); _expectedImagesFullFilenames.clear(); - QString branch = (branchFromCommandLine.isNull()) ? autoTester->getSelectedBranch() : branchFromCommandLine; - QString user = (userFromCommandLine.isNull()) ? autoTester->getSelectedUser() : userFromCommandLine; + QString branch = (branchFromCommandLine.isNull()) ? nitpick->getSelectedBranch() : branchFromCommandLine; + QString user = (userFromCommandLine.isNull()) ? nitpick->getSelectedUser() : userFromCommandLine; foreach(QString currentFilename, sortedTestResultsFilenames) { QString fullCurrentFilename = _snapshotDirectory + "/" + currentFilename; @@ -268,7 +268,7 @@ void Test::startTestsEvaluation(const bool isRunningFromCommandLine, } } - autoTester->downloadFiles(expectedImagesURLs, _snapshotDirectory, _expectedImagesFilenames, (void *)this); + nitpick->downloadFiles(expectedImagesURLs, _snapshotDirectory, _expectedImagesFilenames, (void *)this); } void Test::finishTestsEvaluation() { int numberOfFailures = compareImageLists(); @@ -288,7 +288,7 @@ void Test::finishTestsEvaluation() { } if (_isRunningInAutomaticTestRun) { - autoTester->automaticTestRunEvaluationComplete(zippedFolderName, numberOfFailures); + nitpick->automaticTestRunEvaluationComplete(zippedFolderName, numberOfFailures); } } @@ -429,22 +429,22 @@ ExtractedText Test::getTestScriptLines(QString testFileName) { QString line = stream.readLine(); // Name of test is the string in the following line: - // autoTester.perform("Apply Material Entities to Avatars", Script.resolvePath("."), function(testType) {... + // nitpick.perform("Apply Material Entities to Avatars", Script.resolvePath("."), function(testType) {... const QString ws("\\h*"); //white-space character - const QString functionPerformName(ws + "autoTester" + ws + "\\." + ws + "perform"); + const QString functionPerformName(ws + "nitpick" + ws + "\\." + ws + "perform"); const QString quotedString("\\\".+\\\""); QString regexTestTitle(ws + functionPerformName + "\\(" + quotedString); QRegularExpression lineContainingTitle = QRegularExpression(regexTestTitle); // Each step is either of the following forms: - // autoTester.addStepSnapshot("Take snapshot"... - // autoTester.addStep("Clean up after test"... - const QString functionAddStepSnapshotName(ws + "autoTester" + ws + "\\." + ws + "addStepSnapshot"); + // nitpick.addStepSnapshot("Take snapshot"... + // nitpick.addStep("Clean up after test"... + const QString functionAddStepSnapshotName(ws + "nitpick" + ws + "\\." + ws + "addStepSnapshot"); const QString regexStepSnapshot(ws + functionAddStepSnapshotName + ws + "\\(" + ws + quotedString + ".*"); const QRegularExpression lineStepSnapshot = QRegularExpression(regexStepSnapshot); - const QString functionAddStepName(ws + "autoTester" + ws + "\\." + ws + "addStep"); + const QString functionAddStepName(ws + "nitpick" + ws + "\\." + ws + "addStep"); const QString regexStep(ws + functionAddStepName + ws + "\\(" + ws + quotedString + ".*"); const QRegularExpression lineStep = QRegularExpression(regexStep); @@ -620,7 +620,7 @@ void Test::createTestAutoScript() { } if (createTestAutoScript(_testDirectory)) { - QMessageBox::information(0, "Success", "'autoTester.js` script has been created"); + QMessageBox::information(0, "Success", "'nitpick.js` script has been created"); } } @@ -653,7 +653,7 @@ void Test::createAllTestAutoScripts() { } } - QMessageBox::information(0, "Success", "'autoTester.js' scripts have been created"); + QMessageBox::information(0, "Success", "'nitpick.js' scripts have been created"); } bool Test::createTestAutoScript(const QString& directory) { @@ -677,8 +677,8 @@ bool Test::createTestAutoScript(const QString& directory) { stream << "if (typeof PATH_TO_THE_REPO_PATH_UTILS_FILE === 'undefined') PATH_TO_THE_REPO_PATH_UTILS_FILE = 'https://raw.githubusercontent.com/highfidelity/hifi_tests/master/tests/utils/branchUtils.js';\n"; stream << "Script.include(PATH_TO_THE_REPO_PATH_UTILS_FILE);\n"; - stream << "var autoTester = createAutoTester(Script.resolvePath('.'));\n\n"; - stream << "autoTester.enableAuto();\n\n"; + stream << "var nitpick = createAutoTester(Script.resolvePath('.'));\n\n"; + stream << "nitpick.enableAuto();\n\n"; stream << "Script.include('./test.js?raw=true');\n"; testAutoScriptFile.close(); @@ -751,29 +751,29 @@ void Test::createRecursiveScript(const QString& topLevelDirectory, bool interact textStream << "// This is an automatically generated file, created by auto-tester" << endl; // Include 'autoTest.js' - QString branch = autoTester->getSelectedBranch(); - QString user = autoTester->getSelectedUser(); + QString branch = nitpick->getSelectedBranch(); + QString user = nitpick->getSelectedUser(); textStream << "PATH_TO_THE_REPO_PATH_UTILS_FILE = \"https://raw.githubusercontent.com/" + user + "/hifi_tests/" + branch + "/tests/utils/branchUtils.js\";" << endl; textStream << "Script.include(PATH_TO_THE_REPO_PATH_UTILS_FILE);" << endl; - textStream << "var autoTester = createAutoTester(Script.resolvePath(\".\"));" << endl << endl; + textStream << "var nitpick = createAutoTester(Script.resolvePath(\".\"));" << endl << endl; - textStream << "var testsRootPath = autoTester.getTestsRootPath();" << endl << endl; + textStream << "var testsRootPath = nitpick.getTestsRootPath();" << endl << endl; // Wait 10 seconds before starting textStream << "if (typeof Test !== 'undefined') {" << endl; textStream << " Test.wait(10000);" << endl; textStream << "};" << endl << endl; - textStream << "autoTester.enableRecursive();" << endl; - textStream << "autoTester.enableAuto();" << endl << endl; + textStream << "nitpick.enableRecursive();" << endl; + textStream << "nitpick.enableAuto();" << endl << endl; // This is used to verify that the recursive test contains at least one test bool testFound{ false }; - // Directories are included in reverse order. The autoTester scripts use a stack mechanism, + // Directories are included in reverse order. The nitpick scripts use a stack mechanism, // so this ensures that the tests run in alphabetical order (a convenience when debugging) QStringList directories; @@ -819,7 +819,7 @@ void Test::createRecursiveScript(const QString& topLevelDirectory, bool interact } textStream << endl; - textStream << "autoTester.runRecursive();" << endl; + textStream << "nitpick.runRecursive();" << endl; allTestsFilename.close(); } @@ -881,7 +881,7 @@ void Test::createTestsOutline() { // The directory name appears after the last slash (we are assured there is at least 1). QString directoryName = directory.right(directory.length() - directory.lastIndexOf("/") - 1); - // autoTester is run on a clone of the repository. We use relative paths, so we can use both local disk and GitHub + // nitpick is run on a clone of the repository. We use relative paths, so we can use both local disk and GitHub // For a test in "D:/GitHub/hifi_tests/tests/content/entity/zone/ambientLightInheritance" the // GitHub URL is "./content/entity/zone/ambientLightInheritance?raw=true" QString partialPath = directory.right(directory.length() - (directory.lastIndexOf("/tests/") + QString("/tests").length() + 1)); @@ -937,11 +937,11 @@ void Test::createTestRailTestCases() { } if (_testRailCreateMode == PYTHON) { - _testRailInterface.createTestSuitePython(_testDirectory, outputDirectory, autoTester->getSelectedUser(), - autoTester->getSelectedBranch()); + _testRailInterface.createTestSuitePython(_testDirectory, outputDirectory, nitpick->getSelectedUser(), + nitpick->getSelectedBranch()); } else { - _testRailInterface.createTestSuiteXML(_testDirectory, outputDirectory, autoTester->getSelectedUser(), - autoTester->getSelectedBranch()); + _testRailInterface.createTestSuiteXML(_testDirectory, outputDirectory, nitpick->getSelectedUser(), + nitpick->getSelectedBranch()); } } diff --git a/tools/auto-tester/src/Test.h b/tools/nitpick/src/Test.h similarity index 98% rename from tools/auto-tester/src/Test.h rename to tools/nitpick/src/Test.h index f653a91782..a79252b92a 100644 --- a/tools/auto-tester/src/Test.h +++ b/tools/nitpick/src/Test.h @@ -146,7 +146,7 @@ private: const QString DATETIME_FORMAT{ "yyyy-MM-dd_hh-mm-ss" }; - // NOTE: these need to match the appropriate var's in autoTester.js + // NOTE: these need to match the appropriate var's in nitpick.js // var advanceKey = "n"; // var pathSeparator = "."; const QString ADVANCE_KEY{ "n" }; diff --git a/tools/auto-tester/src/TestRailInterface.cpp b/tools/nitpick/src/TestRailInterface.cpp similarity index 100% rename from tools/auto-tester/src/TestRailInterface.cpp rename to tools/nitpick/src/TestRailInterface.cpp diff --git a/tools/auto-tester/src/TestRailInterface.h b/tools/nitpick/src/TestRailInterface.h similarity index 100% rename from tools/auto-tester/src/TestRailInterface.h rename to tools/nitpick/src/TestRailInterface.h diff --git a/tools/auto-tester/src/TestRunner.cpp b/tools/nitpick/src/TestRunner.cpp similarity index 98% rename from tools/auto-tester/src/TestRunner.cpp rename to tools/nitpick/src/TestRunner.cpp index 3781024a63..1387635b8f 100644 --- a/tools/auto-tester/src/TestRunner.cpp +++ b/tools/nitpick/src/TestRunner.cpp @@ -13,8 +13,8 @@ #include #include -#include "ui/AutoTester.h" -extern AutoTester* autoTester; +#include "ui/Nitpick.h" +extern Nitpick* nitpick; #ifdef Q_OS_WIN #include @@ -95,7 +95,7 @@ void TestRunner::setWorkingFolder() { _logFile.setFileName(_workingFolder + "/log.txt"); - autoTester->enableRunTabControls(); + nitpick->enableRunTabControls(); _workingFolderLabel->setText(QDir::toNativeSeparators(_workingFolder)); _timer = new QTimer(this); @@ -203,8 +203,8 @@ void TestRunner::run() { _automatedTestIsRunning = true; // Initial setup - _branch = autoTester->getSelectedBranch(); - _user = autoTester->getSelectedUser(); + _branch = nitpick->getSelectedBranch(); + _user = nitpick->getSelectedUser(); // This will be restored at the end of the tests saveExistingHighFidelityAppDataFolder(); @@ -221,7 +221,7 @@ void TestRunner::run() { updateStatusLabel("Downloading Build XML"); buildXMLDownloaded = false; - autoTester->downloadFiles(urls, _workingFolder, filenames, (void*)this); + nitpick->downloadFiles(urls, _workingFolder, filenames, (void*)this); // `downloadComplete` will run after download has completed } @@ -250,7 +250,7 @@ void TestRunner::downloadComplete() { updateStatusLabel("Downloading installer"); - autoTester->downloadFiles(urls, _workingFolder, filenames, (void*)this); + nitpick->downloadFiles(urls, _workingFolder, filenames, (void*)this); // `downloadComplete` will run again after download has completed @@ -505,7 +505,7 @@ void TestRunner::interfaceExecutionComplete() { void TestRunner::evaluateResults() { updateStatusLabel("Evaluating results"); - autoTester->startTestsEvaluation(false, true, _snapshotFolder, _branch, _user); + nitpick->startTestsEvaluation(false, true, _snapshotFolder, _branch, _user); } void TestRunner::automaticTestRunEvaluationComplete(QString zippedFolder, int numberOfFailures) { @@ -618,7 +618,7 @@ void TestRunner::checkTime() { } void TestRunner::updateStatusLabel(const QString& message) { - autoTester->updateStatusLabel(message); + nitpick->updateStatusLabel(message); } void TestRunner::appendLog(const QString& message) { @@ -632,7 +632,7 @@ void TestRunner::appendLog(const QString& message) { _logFile.write("\n"); _logFile.close(); - autoTester->appendLogWindow(message); + nitpick->appendLogWindow(message); } QString TestRunner::getInstallerNameFromURL(const QString& url) { diff --git a/tools/auto-tester/src/TestRunner.h b/tools/nitpick/src/TestRunner.h similarity index 100% rename from tools/auto-tester/src/TestRunner.h rename to tools/nitpick/src/TestRunner.h diff --git a/tools/auto-tester/src/common.h b/tools/nitpick/src/common.h similarity index 100% rename from tools/auto-tester/src/common.h rename to tools/nitpick/src/common.h diff --git a/tools/auto-tester/src/main.cpp b/tools/nitpick/src/main.cpp similarity index 89% rename from tools/auto-tester/src/main.cpp rename to tools/nitpick/src/main.cpp index ac4b4593c5..089a72e6ce 100644 --- a/tools/auto-tester/src/main.cpp +++ b/tools/nitpick/src/main.cpp @@ -8,11 +8,11 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // #include -#include "ui/AutoTester.h" +#include "ui/Nitpick.h" #include -AutoTester* autoTester; +Nitpick* nitpick; int main(int argc, char *argv[]) { // If no parameters then run in interactive mode @@ -62,13 +62,13 @@ int main(int argc, char *argv[]) { QApplication application(argc, argv); - autoTester = new AutoTester(); - autoTester->setup(); + nitpick = new Nitpick(); + nitpick->setup(); if (!testFolder.isNull()) { - autoTester->startTestsEvaluation(true ,false, testFolder, branch, user); + nitpick->startTestsEvaluation(true ,false, testFolder, branch, user); } else { - autoTester->show(); + nitpick->show(); } return application.exec(); diff --git a/tools/auto-tester/src/ui/BusyWindow.cpp b/tools/nitpick/src/ui/BusyWindow.cpp similarity index 100% rename from tools/auto-tester/src/ui/BusyWindow.cpp rename to tools/nitpick/src/ui/BusyWindow.cpp diff --git a/tools/auto-tester/src/ui/BusyWindow.h b/tools/nitpick/src/ui/BusyWindow.h similarity index 100% rename from tools/auto-tester/src/ui/BusyWindow.h rename to tools/nitpick/src/ui/BusyWindow.h diff --git a/tools/auto-tester/src/ui/BusyWindow.ui b/tools/nitpick/src/ui/BusyWindow.ui similarity index 100% rename from tools/auto-tester/src/ui/BusyWindow.ui rename to tools/nitpick/src/ui/BusyWindow.ui diff --git a/tools/auto-tester/src/ui/HelpWindow.cpp b/tools/nitpick/src/ui/HelpWindow.cpp similarity index 100% rename from tools/auto-tester/src/ui/HelpWindow.cpp rename to tools/nitpick/src/ui/HelpWindow.cpp diff --git a/tools/auto-tester/src/ui/HelpWindow.h b/tools/nitpick/src/ui/HelpWindow.h similarity index 100% rename from tools/auto-tester/src/ui/HelpWindow.h rename to tools/nitpick/src/ui/HelpWindow.h diff --git a/tools/auto-tester/src/ui/HelpWindow.ui b/tools/nitpick/src/ui/HelpWindow.ui similarity index 96% rename from tools/auto-tester/src/ui/HelpWindow.ui rename to tools/nitpick/src/ui/HelpWindow.ui index d2aa0da0d4..1ce6e8c321 100644 --- a/tools/auto-tester/src/ui/HelpWindow.ui +++ b/tools/nitpick/src/ui/HelpWindow.ui @@ -14,7 +14,7 @@ - AutoTester Help + Nitpick Help diff --git a/tools/auto-tester/src/ui/MismatchWindow.cpp b/tools/nitpick/src/ui/MismatchWindow.cpp similarity index 100% rename from tools/auto-tester/src/ui/MismatchWindow.cpp rename to tools/nitpick/src/ui/MismatchWindow.cpp diff --git a/tools/auto-tester/src/ui/MismatchWindow.h b/tools/nitpick/src/ui/MismatchWindow.h similarity index 100% rename from tools/auto-tester/src/ui/MismatchWindow.h rename to tools/nitpick/src/ui/MismatchWindow.h diff --git a/tools/auto-tester/src/ui/MismatchWindow.ui b/tools/nitpick/src/ui/MismatchWindow.ui similarity index 100% rename from tools/auto-tester/src/ui/MismatchWindow.ui rename to tools/nitpick/src/ui/MismatchWindow.ui diff --git a/tools/auto-tester/src/ui/AutoTester.cpp b/tools/nitpick/src/ui/Nitpick.cpp similarity index 72% rename from tools/auto-tester/src/ui/AutoTester.cpp rename to tools/nitpick/src/ui/Nitpick.cpp index d49f3aaa1c..185599538e 100644 --- a/tools/auto-tester/src/ui/AutoTester.cpp +++ b/tools/nitpick/src/ui/Nitpick.cpp @@ -1,5 +1,5 @@ // -// AutoTester.cpp +// Nitpick.cpp // zone/ambientLightInheritence // // Created by Nissim Hadar on 2 Nov 2017. @@ -8,14 +8,14 @@ // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#include "AutoTester.h" +#include "Nitpick.h" #ifdef Q_OS_WIN #include #include #endif -AutoTester::AutoTester(QWidget* parent) : QMainWindow(parent) { +Nitpick::Nitpick(QWidget* parent) : QMainWindow(parent) { _ui.setupUi(this); _ui.checkBoxInteractiveMode->setChecked(true); @@ -24,9 +24,9 @@ AutoTester::AutoTester(QWidget* parent) : QMainWindow(parent) { _signalMapper = new QSignalMapper(); - connect(_ui.actionClose, &QAction::triggered, this, &AutoTester::on_closeButton_clicked); - connect(_ui.actionAbout, &QAction::triggered, this, &AutoTester::about); - connect(_ui.actionContent, &QAction::triggered, this, &AutoTester::content); + connect(_ui.actionClose, &QAction::triggered, this, &Nitpick::on_closeButton_clicked); + connect(_ui.actionAbout, &QAction::triggered, this, &Nitpick::about); + connect(_ui.actionContent, &QAction::triggered, this, &Nitpick::content); // The second tab hides and shows the Windows task bar #ifndef Q_OS_WIN @@ -36,13 +36,13 @@ AutoTester::AutoTester(QWidget* parent) : QMainWindow(parent) { _ui.statusLabel->setText(""); _ui.plainTextEdit->setReadOnly(true); - setWindowTitle("Auto Tester - v7.0"); + setWindowTitle("Nitpick - v1.0"); - // Coming soon to an auto-tester near you... + // Coming soon to a nitpick near you... //// _helpWindow.textBrowser->setText() } -AutoTester::~AutoTester() { +Nitpick::~Nitpick() { delete _signalMapper; if (_test) { @@ -54,7 +54,7 @@ AutoTester::~AutoTester() { } } -void AutoTester::setup() { +void Nitpick::setup() { if (_test) { delete _test; } @@ -87,7 +87,7 @@ void AutoTester::setup() { _testRunner = new TestRunner(dayCheckboxes, timeEditCheckboxes, timeEdits, _ui.workingFolderLabel, _ui.checkBoxServerless, _ui.checkBoxRunLatest, _ui.urlLineEdit, _ui.runNowButton); } -void AutoTester::startTestsEvaluation(const bool isRunningFromCommandLine, +void Nitpick::startTestsEvaluation(const bool isRunningFromCommandLine, const bool isRunningInAutomaticTestRun, const QString& snapshotDirectory, const QString& branch, @@ -96,7 +96,7 @@ void AutoTester::startTestsEvaluation(const bool isRunningFromCommandLine, _test->startTestsEvaluation(isRunningFromCommandLine, isRunningInAutomaticTestRun, snapshotDirectory, branch, user); } -void AutoTester::on_tabWidget_currentChanged(int index) { +void Nitpick::on_tabWidget_currentChanged(int index) { if (index == 0 || index == 2 || index == 3) { _ui.userLineEdit->setDisabled(false); _ui.branchLineEdit->setDisabled(false); @@ -106,73 +106,73 @@ void AutoTester::on_tabWidget_currentChanged(int index) { } } -void AutoTester::on_evaluateTestsButton_clicked() { +void Nitpick::on_evaluateTestsButton_clicked() { _test->startTestsEvaluation(false, false); } -void AutoTester::on_createRecursiveScriptButton_clicked() { +void Nitpick::on_createRecursiveScriptButton_clicked() { _test->createRecursiveScript(); } -void AutoTester::on_createAllRecursiveScriptsButton_clicked() { +void Nitpick::on_createAllRecursiveScriptsButton_clicked() { _test->createAllRecursiveScripts(); } -void AutoTester::on_createTestsButton_clicked() { +void Nitpick::on_createTestsButton_clicked() { _test->createTests(); } -void AutoTester::on_createMDFileButton_clicked() { +void Nitpick::on_createMDFileButton_clicked() { _test->createMDFile(); } -void AutoTester::on_createAllMDFilesButton_clicked() { +void Nitpick::on_createAllMDFilesButton_clicked() { _test->createAllMDFiles(); } -void AutoTester::on_createTestAutoScriptButton_clicked() { +void Nitpick::on_createTestAutoScriptButton_clicked() { _test->createTestAutoScript(); } -void AutoTester::on_createAllTestAutoScriptsButton_clicked() { +void Nitpick::on_createAllTestAutoScriptsButton_clicked() { _test->createAllTestAutoScripts(); } -void AutoTester::on_createTestsOutlineButton_clicked() { +void Nitpick::on_createTestsOutlineButton_clicked() { _test->createTestsOutline(); } -void AutoTester::on_createTestRailTestCasesButton_clicked() { +void Nitpick::on_createTestRailTestCasesButton_clicked() { _test->createTestRailTestCases(); } -void AutoTester::on_createTestRailRunButton_clicked() { +void Nitpick::on_createTestRailRunButton_clicked() { _test->createTestRailRun(); } -void AutoTester::on_setWorkingFolderButton_clicked() { +void Nitpick::on_setWorkingFolderButton_clicked() { _testRunner->setWorkingFolder(); } -void AutoTester::enableRunTabControls() { +void Nitpick::enableRunTabControls() { _ui.runNowButton->setEnabled(true); _ui.daysGroupBox->setEnabled(true); _ui.timesGroupBox->setEnabled(true); } -void AutoTester::on_runNowButton_clicked() { +void Nitpick::on_runNowButton_clicked() { _testRunner->run(); } -void AutoTester::on_checkBoxRunLatest_clicked() { +void Nitpick::on_checkBoxRunLatest_clicked() { _ui.urlLineEdit->setEnabled(!_ui.checkBoxRunLatest->isChecked()); } -void AutoTester::automaticTestRunEvaluationComplete(QString zippedFolderName, int numberOfFailures) { +void Nitpick::automaticTestRunEvaluationComplete(QString zippedFolderName, int numberOfFailures) { _testRunner->automaticTestRunEvaluationComplete(zippedFolderName, numberOfFailures); } -void AutoTester::on_updateTestRailRunResultsButton_clicked() { +void Nitpick::on_updateTestRailRunResultsButton_clicked() { _test->updateTestRailRunResult(); } @@ -180,7 +180,7 @@ void AutoTester::on_updateTestRailRunResultsButton_clicked() { // if (uState & ABS_AUTOHIDE) on_showTaskbarButton_clicked(); // else on_hideTaskbarButton_clicked(); // -void AutoTester::on_hideTaskbarButton_clicked() { +void Nitpick::on_hideTaskbarButton_clicked() { #ifdef Q_OS_WIN APPBARDATA abd = { sizeof abd }; UINT uState = (UINT)SHAppBarMessage(ABM_GETSTATE, &abd); @@ -190,7 +190,7 @@ void AutoTester::on_hideTaskbarButton_clicked() { #endif } -void AutoTester::on_showTaskbarButton_clicked() { +void Nitpick::on_showTaskbarButton_clicked() { #ifdef Q_OS_WIN APPBARDATA abd = { sizeof abd }; UINT uState = (UINT)SHAppBarMessage(ABM_GETSTATE, &abd); @@ -200,23 +200,23 @@ void AutoTester::on_showTaskbarButton_clicked() { #endif } -void AutoTester::on_closeButton_clicked() { +void Nitpick::on_closeButton_clicked() { exit(0); } -void AutoTester::on_createPythonScriptRadioButton_clicked() { +void Nitpick::on_createPythonScriptRadioButton_clicked() { _test->setTestRailCreateMode(PYTHON); } -void AutoTester::on_createXMLScriptRadioButton_clicked() { +void Nitpick::on_createXMLScriptRadioButton_clicked() { _test->setTestRailCreateMode(XML); } -void AutoTester::on_createWebPagePushButton_clicked() { +void Nitpick::on_createWebPagePushButton_clicked() { _test->createWebPage(_ui.updateAWSCheckBox, _ui.awsURLLineEdit); } -void AutoTester::downloadFile(const QUrl& url) { +void Nitpick::downloadFile(const QUrl& url) { _downloaders.emplace_back(new Downloader(url, this)); connect(_downloaders[_index], SIGNAL(downloaded()), _signalMapper, SLOT(map())); @@ -225,7 +225,7 @@ void AutoTester::downloadFile(const QUrl& url) { ++_index; } -void AutoTester::downloadFiles(const QStringList& URLs, const QString& directoryName, const QStringList& filenames, void *caller) { +void Nitpick::downloadFiles(const QStringList& URLs, const QString& directoryName, const QStringList& filenames, void *caller) { connect(_signalMapper, SIGNAL(mapped(int)), this, SLOT(saveFile(int))); _directoryName = directoryName; @@ -251,7 +251,7 @@ void AutoTester::downloadFiles(const QStringList& URLs, const QString& directory } } -void AutoTester::saveFile(int index) { +void Nitpick::saveFile(int index) { try { QFile file(_directoryName + "/" + _filenames[index]); file.open(QIODevice::WriteOnly); @@ -277,34 +277,34 @@ void AutoTester::saveFile(int index) { } } -void AutoTester::about() { +void Nitpick::about() { QMessageBox::information(0, "About", QString("Built ") + __DATE__ + ", " + __TIME__); } -void AutoTester::content() { +void Nitpick::content() { _helpWindow.show(); } -void AutoTester::setUserText(const QString& user) { +void Nitpick::setUserText(const QString& user) { _ui.userLineEdit->setText(user); } -QString AutoTester::getSelectedUser() { +QString Nitpick::getSelectedUser() { return _ui.userLineEdit->text(); } -void AutoTester::setBranchText(const QString& branch) { +void Nitpick::setBranchText(const QString& branch) { _ui.branchLineEdit->setText(branch); } -QString AutoTester::getSelectedBranch() { +QString Nitpick::getSelectedBranch() { return _ui.branchLineEdit->text(); } -void AutoTester::updateStatusLabel(const QString& status) { +void Nitpick::updateStatusLabel(const QString& status) { _ui.statusLabel->setText(status); } -void AutoTester::appendLogWindow(const QString& message) { +void Nitpick::appendLogWindow(const QString& message) { _ui.plainTextEdit->appendPlainText(message); } \ No newline at end of file diff --git a/tools/auto-tester/src/ui/AutoTester.h b/tools/nitpick/src/ui/Nitpick.h similarity index 92% rename from tools/auto-tester/src/ui/AutoTester.h rename to tools/nitpick/src/ui/Nitpick.h index 429a8b60e1..21b917654b 100644 --- a/tools/auto-tester/src/ui/AutoTester.h +++ b/tools/nitpick/src/ui/Nitpick.h @@ -1,5 +1,5 @@ // -// AutoTester.h +// Nitpick.h // // Created by Nissim Hadar on 2 Nov 2017. // Copyright 2013 High Fidelity, Inc. @@ -7,13 +7,13 @@ // 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_AutoTester_h -#define hifi_AutoTester_h +#ifndef hifi_Nitpick_h +#define hifi_Nitpick_h #include #include #include -#include "ui_AutoTester.h" +#include "ui_Nitpick.h" #include "../Downloader.h" #include "../Test.h" @@ -22,12 +22,12 @@ #include "../TestRunner.h" #include "../AWSInterface.h" -class AutoTester : public QMainWindow { +class Nitpick : public QMainWindow { Q_OBJECT public: - AutoTester(QWidget* parent = Q_NULLPTR); - ~AutoTester(); + Nitpick(QWidget* parent = Q_NULLPTR); + ~Nitpick(); void setup(); @@ -95,7 +95,7 @@ private slots: void content(); private: - Ui::AutoTesterClass _ui; + Ui::NitpickClass _ui; Test* _test{ nullptr }; TestRunner* _testRunner{ nullptr }; @@ -121,4 +121,4 @@ private: void* _caller; }; -#endif // hifi_AutoTester_h \ No newline at end of file +#endif // hifi_Nitpick_h \ No newline at end of file diff --git a/tools/auto-tester/src/ui/AutoTester.ui b/tools/nitpick/src/ui/Nitpick.ui similarity index 99% rename from tools/auto-tester/src/ui/AutoTester.ui rename to tools/nitpick/src/ui/Nitpick.ui index 08f4b46723..5e20e75553 100644 --- a/tools/auto-tester/src/ui/AutoTester.ui +++ b/tools/nitpick/src/ui/Nitpick.ui @@ -1,7 +1,7 @@ - AutoTesterClass - + NitpickClass + 0 @@ -17,7 +17,7 @@ - AutoTester + Nitpick diff --git a/tools/auto-tester/src/ui/TestRailResultsSelectorWindow.cpp b/tools/nitpick/src/ui/TestRailResultsSelectorWindow.cpp similarity index 100% rename from tools/auto-tester/src/ui/TestRailResultsSelectorWindow.cpp rename to tools/nitpick/src/ui/TestRailResultsSelectorWindow.cpp diff --git a/tools/auto-tester/src/ui/TestRailResultsSelectorWindow.h b/tools/nitpick/src/ui/TestRailResultsSelectorWindow.h similarity index 100% rename from tools/auto-tester/src/ui/TestRailResultsSelectorWindow.h rename to tools/nitpick/src/ui/TestRailResultsSelectorWindow.h diff --git a/tools/auto-tester/src/ui/TestRailResultsSelectorWindow.ui b/tools/nitpick/src/ui/TestRailResultsSelectorWindow.ui similarity index 100% rename from tools/auto-tester/src/ui/TestRailResultsSelectorWindow.ui rename to tools/nitpick/src/ui/TestRailResultsSelectorWindow.ui diff --git a/tools/auto-tester/src/ui/TestRailRunSelectorWindow.cpp b/tools/nitpick/src/ui/TestRailRunSelectorWindow.cpp similarity index 100% rename from tools/auto-tester/src/ui/TestRailRunSelectorWindow.cpp rename to tools/nitpick/src/ui/TestRailRunSelectorWindow.cpp diff --git a/tools/auto-tester/src/ui/TestRailRunSelectorWindow.h b/tools/nitpick/src/ui/TestRailRunSelectorWindow.h similarity index 100% rename from tools/auto-tester/src/ui/TestRailRunSelectorWindow.h rename to tools/nitpick/src/ui/TestRailRunSelectorWindow.h diff --git a/tools/auto-tester/src/ui/TestRailRunSelectorWindow.ui b/tools/nitpick/src/ui/TestRailRunSelectorWindow.ui similarity index 100% rename from tools/auto-tester/src/ui/TestRailRunSelectorWindow.ui rename to tools/nitpick/src/ui/TestRailRunSelectorWindow.ui diff --git a/tools/auto-tester/src/ui/TestRailTestCasesSelectorWindow.cpp b/tools/nitpick/src/ui/TestRailTestCasesSelectorWindow.cpp similarity index 100% rename from tools/auto-tester/src/ui/TestRailTestCasesSelectorWindow.cpp rename to tools/nitpick/src/ui/TestRailTestCasesSelectorWindow.cpp diff --git a/tools/auto-tester/src/ui/TestRailTestCasesSelectorWindow.h b/tools/nitpick/src/ui/TestRailTestCasesSelectorWindow.h similarity index 100% rename from tools/auto-tester/src/ui/TestRailTestCasesSelectorWindow.h rename to tools/nitpick/src/ui/TestRailTestCasesSelectorWindow.h diff --git a/tools/auto-tester/src/ui/TestRailTestCasesSelectorWindow.ui b/tools/nitpick/src/ui/TestRailTestCasesSelectorWindow.ui similarity index 100% rename from tools/auto-tester/src/ui/TestRailTestCasesSelectorWindow.ui rename to tools/nitpick/src/ui/TestRailTestCasesSelectorWindow.ui From a21d10ad1b086ae932f1db2a3b05f85306b3daa7 Mon Sep 17 00:00:00 2001 From: Simon Walton Date: Thu, 8 Nov 2018 16:34:02 -0800 Subject: [PATCH 169/286] Restore content archives (zip) correctly; other tweaks --- .../resources/web/content/js/content.js | 63 ++------------- domain-server/src/DomainServer.cpp | 78 +++++++++---------- domain-server/src/DomainServer.h | 2 + 3 files changed, 43 insertions(+), 100 deletions(-) diff --git a/domain-server/resources/web/content/js/content.js b/domain-server/resources/web/content/js/content.js index d581ddaf85..38f93a57be 100644 --- a/domain-server/resources/web/content/js/content.js +++ b/domain-server/resources/web/content/js/content.js @@ -14,41 +14,18 @@ $(document).ready(function(){ return html; } - function uploadInChunks(file) { - var fileSize = file.size; - var filename = file.name; - var CHUNK_SIZE = 16384; - - for(p = 0; p <= fileSize; p += CHUNK_SIZE) { - var chunk = file.slice(p, p + CHUNK_SIZE, file.type); - var chunkFormData = new FormData(); - chunkFormData.append('restore-file-chunk', chunk, filename); - var ajaxParams = { - url: '/content/upload', - type: 'POST', - async: false, - timeout: 60, - processData: false, - contentType: false, - data: chunkFormData - }; - var ajaxObject = $.ajax(ajaxParams); - - } - - - } - function uploadNextChunk(file, offset) { + if (offset == undefined) { + offset = 0; + } + var fileSize = file.size; var filename = file.name; var CHUNK_SIZE = 65536; - if (offset == undefined) { - offset = 0; - } - var isFinal = fileSize - offset > CHUNK_SIZE ? false : true; + + var isFinal = Boolean(fileSize - offset <= CHUNK_SIZE); var nextChunkSize = Math.min(fileSize - offset, CHUNK_SIZE); var chunk = file.slice(offset, offset + CHUNK_SIZE, file.type); var chunkFormData = new FormData(); @@ -127,38 +104,10 @@ $(document).ready(function(){ function() { var files = $('#' + RESTORE_SETTINGS_FILE_ID).prop('files'); - var fileFormData = new FormData(); - fileFormData.append('restore-file', files[0]); - showSpinnerAlert("Uploading content to restore"); uploadNextChunk(files[0]); - return; - - // Previous one-upload method. - $.ajax({ - url: '/content/upload', - type: 'POST', - timeout: 3600000, // Set timeout to 1h - cache: false, - processData: false, - contentType: false, - data: fileFormData - }).done(function(data, textStatus, jqXHR) { - isRestoring = true; - - // immediately reload backup information since one should be restoring now - reloadBackupInformation(); - - swal.close(); - }).fail(function(jqXHR, textStatus, errorThrown) { - showErrorMessage( - "Error", - "There was a problem restoring domain content.\n" - + "Please ensure that the content archive or entity file is valid and try again." - ); - }); } ); }); diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index fd378cb39b..117313462b 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -2273,53 +2273,12 @@ bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url // Received another chunk connection->respond(HTTPConnection::StatusCode200); } else if (formItemName == "restore-file-chunk-final") { - if (uploadedFilename.endsWith(".json", Qt::CaseInsensitive) - || uploadedFilename.endsWith(".json.gz", Qt::CaseInsensitive)) { - // invoke our method to hand the new octree file off to the octree server - QMetaObject::invokeMethod(this, "handleOctreeFileReplacement", - Qt::QueuedConnection, Q_ARG(QByteArray, _pendingUploadedContent)); - _pendingUploadedContent.clear(); - // respond with a 200 for success - connection->respond(HTTPConnection::StatusCode200); + readPendingContent(connection, uploadedFilename); } else if (formItemName == "restore-file") { - if (uploadedFilename.endsWith(".json", Qt::CaseInsensitive) - || uploadedFilename.endsWith(".json.gz", Qt::CaseInsensitive)) { - // invoke our method to hand the new octree file off to the octree server - QMetaObject::invokeMethod(this, "handleOctreeFileReplacement", - Qt::QueuedConnection, Q_ARG(QByteArray, firstFormData.second)); - - _pendingUploadedContent.clear(); - // respond with a 200 for success - connection->respond(HTTPConnection::StatusCode200); - } else if (uploadedFilename.endsWith(".zip", Qt::CaseInsensitive)) { - auto deferred = makePromise("recoverFromUploadedBackup"); - - deferred->then([connectionPtr, JSON_MIME_TYPE](QString error, QVariantMap result) { - if (!connectionPtr) { - return; - } - - QJsonObject rootJSON; - auto success = result["success"].toBool(); - rootJSON["success"] = success; - QJsonDocument docJSON(rootJSON); - connectionPtr->respond(success ? HTTPConnection::StatusCode200 : HTTPConnection::StatusCode400, docJSON.toJson(), - JSON_MIME_TYPE.toUtf8()); - }); - - _contentManager->recoverFromUploadedBackup(deferred, firstFormData.second); - _pendingUploadedContent.clear(); - - return true; - } - } else { - connection->respond(HTTPConnection::StatusCode400); - } + readPendingContent(connection, uploadedFilename); } else { - // we don't have handling for this filetype, send back a 400 for failure connection->respond(HTTPConnection::StatusCode400); } - } else { // respond with a 400 for failure connection->respond(HTTPConnection::StatusCode400); @@ -2568,6 +2527,39 @@ bool DomainServer::handleHTTPSRequest(HTTPSConnection* connection, const QUrl &u } } +void DomainServer::readPendingContent(HTTPConnection* connection, QString filename) { + if (filename.endsWith(".json", Qt::CaseInsensitive) + || filename.endsWith(".json.gz", Qt::CaseInsensitive)) { + // invoke our method to hand the new octree file off to the octree server + QMetaObject::invokeMethod(this, "handleOctreeFileReplacement", + Qt::QueuedConnection, Q_ARG(QByteArray, _pendingUploadedContent)); + + // respond with a 200 for success + connection->respond(HTTPConnection::StatusCode200); + _pendingUploadedContent.clear(); + } else if (filename.endsWith(".zip", Qt::CaseInsensitive)) { + auto deferred = makePromise("recoverFromUploadedBackup"); + + QPointer connectionPtr(connection); + const QString JSON_MIME_TYPE = "application/json"; + deferred->then([connectionPtr, JSON_MIME_TYPE, this](QString error, QVariantMap result) { + if (!connectionPtr) { + return; + } + + QJsonObject rootJSON; + auto success = result["success"].toBool(); + rootJSON["success"] = success; + QJsonDocument docJSON(rootJSON); + connectionPtr->respond(success ? HTTPConnection::StatusCode200 : HTTPConnection::StatusCode400, docJSON.toJson(), + JSON_MIME_TYPE.toUtf8()); + _pendingUploadedContent.clear(); + }); + + _contentManager->recoverFromUploadedBackup(deferred, _pendingUploadedContent); + } +} + HTTPSConnection* DomainServer::connectionFromReplyWithState(QNetworkReply* reply) { // grab the UUID state property from the reply QUuid stateUUID = reply->property(STATE_QUERY_KEY.toLocal8Bit()).toUuid(); diff --git a/domain-server/src/DomainServer.h b/domain-server/src/DomainServer.h index db50af546e..b2ef933bc3 100644 --- a/domain-server/src/DomainServer.h +++ b/domain-server/src/DomainServer.h @@ -209,6 +209,8 @@ private: HTTPSConnection* connectionFromReplyWithState(QNetworkReply* reply); + void readPendingContent(HTTPConnection* connection, QString filename); + bool forwardMetaverseAPIRequest(HTTPConnection* connection, const QString& metaversePath, const QString& requestSubobject, From 44d0a7254cc00fd5f450bb695cc1eabad331b07e Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 8 Nov 2018 14:53:33 -0800 Subject: [PATCH 170/286] Update the styling for the Entity Properties window --- scripts/system/html/css/edit-style.css | 404 +++++----- scripts/system/html/css/jsoneditor.css | 10 +- scripts/system/html/entityProperties.html | 13 + scripts/system/html/js/entityProperties.js | 863 ++++++++++++--------- 4 files changed, 714 insertions(+), 576 deletions(-) diff --git a/scripts/system/html/css/edit-style.css b/scripts/system/html/css/edit-style.css index 7d2350e1c8..8137d6e659 100644 --- a/scripts/system/html/css/edit-style.css +++ b/scripts/system/html/css/edit-style.css @@ -70,12 +70,11 @@ } body { - padding: 21px 21px 21px 21px; color: #afafaf; background-color: #404040; font-family: Raleway-Regular; - font-size: 15px; + font-size: 12px; -webkit-touch-callout: none; -webkit-user-select: none; @@ -425,9 +424,9 @@ input[type=checkbox] { } input[type=checkbox] + label { padding-left: 24px; - background-position-y: 6px; background-repeat: no-repeat; background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAACpSURBVDhPY2xoaGD68+dPMSMjY9L////VgTQjAw4AlH8PxLOPHj1azWxjY1MBVNsBFBfBpwkEgNKcQGwtJyfHyATkF0KEiQdAzYlMQEIUyicFyDD9+/ePgRxMvsb///4zkIOZ/v0HmkAGHginYjGNGAzS+BpdkAj8mun/3//92DyPD//993cG88nTJ4+Zm5p/BSZeJYb/DEJADEzNOPF7hn8Mk69cvVIPAHN5pyfo70F5AAAAAElFTkSuQmCC); + cursor: pointer; } input[type=checkbox]:enabled + label:hover { background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAClSURBVDhPY2hoaGD6//9/6b9//64D8T8gGycASr/7+/dv5/79+1kYgIxKqDjRAKiniRFIv2JgYBAFYlLAE0aQ66AckgDjjx8/yNP44cMH8jS+fPmSPI0PHz4kT+PNmzfJ03jp0iXyNJ46dYo8jYcPHyYnAbxm+vnzZz8wLhlIwd+/f5/BrKSkdExCQuLrnz9/lIBpUAiIQekXF34PTGmTT548WQ8AokXg+rhVtPYAAAAASUVORK5CYII=); @@ -455,6 +454,15 @@ input[type=checkbox]:checked + label:hover { color: #ffffff; } +.icon { + font-family: HiFi-Glyphs; + color: white; +} + +#property-type-icon { + font-size: 50px; +} + .selectable { -webkit-touch-callout: text; -webkit-user-select: text; @@ -483,9 +491,11 @@ input[type=checkbox]:checked + label:hover { #properties-list { display: flex; flex-direction: column; + + margin-top: 16px; } -#properties-list fieldset { +#properties-list .fieldset { position: relative; /* 0.1px on the top is to prevent margin collapsing between this and it's first child */ margin: 0 -21px 21px -21px; @@ -495,71 +505,101 @@ input[type=checkbox]:checked + label:hover { box-shadow: 0 -1px 0 rgb(37,37,37); } -#properties-list fieldset.fstuple, #properties-list fieldset.fsrow { +#properties-list .fieldset.fstuple, #properties-list .fieldset.fsrow { margin-top: 21px; border: none; box-shadow: none; } -#properties-list > fieldset[data-collapsed="true"] + fieldset { +#properties-list > .fieldset[data-collapsed="true"] + .fieldset { margin-top: 0; } -#properties-list > fieldset[data-collapsed="true"] > *:not(legend) { +#properties-list > .fieldset[data-collapsed="true"] > *:not(div.legend) { display: none !important; } -#properties-list legend + fieldset { - margin-top: 0; - border: none; - box-shadow: none; -} - -#properties-list > fieldset > legend { - position: relative; - display: table; - width: 100%; - margin: 21px -21px 0 -21px; - padding: 14px 21px 0 21px; - font-family: Raleway-Regular; - font-size: 12px; - color: #afafaf; - height: 28px; - text-transform: uppercase; - outline: none; - background-color: #404040; - border: none; +.section-header { + padding: 0 16px; border-top: 1px rgb(90,90,90) solid; - box-shadow: 0 -1px 0 rgb(37,37,37), 0 4px 4px 0 rgba(0,0,0,0.75); + box-shadow: 1px -1px 0 rgb(37,37,37); + border-bottom: 1px solid rgb(37, 37, 37); } div.section-header, hr { - display: table; - width: 100%; - margin: 21px -21px 0 -21px; - padding: 14px 21px 0 21px; + display: flex; + flex-flow: row nowrap; + + padding: 10px 16px; font-family: Raleway-Regular; font-size: 12px; color: #afafaf; height: 28px; text-transform: uppercase; outline: none; + margin-bottom: 10px; + align-items: center; } -div.section-header:first-child { - margin-top: -2px; - padding-top: 0; - background: none; - height: auto; +.section.minor { + margin: 0 21px; + box-shadow: 1px -1px 0 rgb(37,37,37); + border-left: 1px solid #575757; } -#properties-list > fieldset > legend span, .section-header span { - font-family: HiFi-Glyphs; +.container.property { + padding: 0 16px; +} + +.stretch { + width: 100%; +} + +div.section-header .label { + width: 100%; +} + +.section.minor div.section-header { + border-right: 0; +} + +div.section[collapsed="true"] > .container { + display: none; +} + +div.section[collapsed="true"], div.section[collapsed="true"] > .section-header { + margin-bottom: 0; +} + +.section.major { + margin-bottom: 20px; +} + +.section.minor.last { + margin-bottom: 20px; + border-bottom: 1px solid rgb(37,37,37); +} + +.section-header { + background-color: #373737; +} + +.section-header { + cursor: pointer; +} + +.section-header span { font-size: 30px; - float: right; - position: absolute; - top: 4px; - right: 13px; + font-family: HiFi-Glyphs; +} + +.triple-label { + text-transform: uppercase; + padding: 6px 0; +} + +.triple-item { + margin-right: 10px; } .section-header[collapsed="true"] { @@ -582,9 +622,6 @@ hr { } .property { - display: table; - width: 100%; - margin-top: 21px; min-height: 28px; } @@ -592,6 +629,10 @@ hr { width: auto; } +span.indented { + padding-left: 16px; +} + .property label, .number label { display: table-cell; vertical-align: middle; @@ -604,13 +645,13 @@ hr { font-size: 13px; } -.property legend, .number legend { +.property div.legend, .number div.legend { display: table-cell; vertical-align: middle; font-family: Raleway-SemiBold; font-size: 14px; } -.property legend .unit, .number legend .unit { +.property div.legend .unit, .number div.legend .unit { margin-left: 8px; font-family: Raleway-Light; font-size: 13px; @@ -623,16 +664,26 @@ hr { .value label { display: inline-block; vertical-align: top; - width: 48px; } -.value legend { +.value div.legend { display: inline-block; vertical-align: top; width: 48px; } .value span { - font-family: FiraSans-SemiBold; font-size: 15px; + margin-right: 4px; +} + +#placeholder-property-type { + display: flex; + align-items: center; + width: auto; + margin-right: 20px; +} + +#placeholder-property-locked { + margin-left: 6px; } .checkbox + .checkbox { @@ -659,33 +710,6 @@ hr { height: 1.8rem; } -.text label, .url label, .number label, .textarea label, .xy label, .wh label, .rgb label, .xyz label, .pyr label, .dropdown label, .gen label { - float: left; - margin-left: 1px; - margin-bottom: 3px; - margin-top: -2px; -} - -.text legend, .url legend, .number legend, .textarea legend, .xy legend, .wh legend, .rgb legend, .xyz legend, .pyr legend, .dropdown legend, .gen legend { - float: left; - margin-left: 1px; - margin-bottom: 3px; - margin-top: -2px; -} - -.xy > div, .wh > div, .xyz > div, .pyr > div, .gen > div { - clear: both; -} - -.number > input { - clear: both; - float: left; -} -.number > span { - clear: both; - float: left; -} - .dropdown { position: relative; margin-bottom: -17px; @@ -697,6 +721,7 @@ hr { .dropdown dl { clear: both; + cursor: pointer; } .dropdown dl { font-family: FiraSans-SemiBold; @@ -705,7 +730,7 @@ hr { height: 28px; padding: 0 28px 0 12px; color: #afafaf; - background: linear-gradient(#7d7d7d 20%, #686a68 100%); + background: #575757; position: relative; } .dropdown dl[dropped="true"] { @@ -812,27 +837,20 @@ div.refresh input[type="button"] { .color-picker { box-sizing: border-box; - float: left; - margin-bottom: 21px; - width: 36px; - height: 36px; - border: 4px solid #afafaf; - border-radius: 4px; - background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAABTSURBVChTjcxBDoAwCETRwTs33sFjwB6uaCE1Ggvav5qQF7CSqu40dllHjYiOT3gh3yV8Ii+Fb+RNMEP9hm3sKENmBhG5P1aImWMH/EMerSAAOAFgTC/R8ZXSXAAAAABJRU5ErkJggg==); - background-position: bottom right; - background-repeat: no-repeat; + width: 26px; + height: 26px; + border: 3px solid #2B2B2B; + cursor: pointer; } .color-picker:focus { outline: none; } .color-picker[active="true"] { border-color: #000; - background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAABDSURBVChTjcoLCkAhCETRNq0tf97Y5xGZ1gVJ45TH6njThIO+xk2UwhWFcEdH6JCqOuiQiMDi/hcii3crRRb/7ggAPvIMVihQwvSXAAAAAElFTkSuQmCC); } .color-picker[disabled="disabled"] { border-color: #afafaf; - background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAABTSURBVChTjcxBDoAwCETRwTs33sFjwB6uaCE1Ggvav5qQF7CSqu40dllHjYiOT3gh3yV8Ii+Fb+RNMEP9hm3sKENmBhG5P1aImWMH/EMerSAAOAFgTC/R8ZXSXAAAAABJRU5ErkJggg==); } .colpick[disabled="disabled"] { @@ -848,89 +866,15 @@ div.refresh input[type="button"] { clear: both; } -.rgb legend { +.rgb div.legend { float: left; margin-top: 10px; margin-left: 21px; } -.rgb legend + * { +.rgb div.legend + * { clear: both; } -.tuple div { - display: inline-block; - position: relative; - margin-right: 6px; -} -.tuple div:last-child { - margin-right: 0; -} - -.tuple label { - margin-right: -6px; -} - -.xy .tuple input { - padding-left: 25px; -} -.wh .tuple input { - padding-left: 45px; -} -.rgb .tuple input { - padding-left: 65px; -} -.xyz .tuple input { - padding-left: 25px; -} -.pyr .tuple input { - padding-left: 40px; -} - -.tuple div > label:first-child { - float: left; -} -.tuple div > label + input { - clear: both; - float: left; -} -.tuple div input + label { - display: inline !important; - float: none !important; - position: absolute; - margin-top: 8px; - margin-left: 6px; - left: 0; - font-family: FiraSans-SemiBold; - font-size: 12px; -} -.tuple .red + label, .tuple .x + label, .tuple .pitch + label, .tuple .width + label { - color: #e2334d; -} -.tuple .green + label, .tuple .y + label, .tuple .yaw + label, .tuple .height + label { - color: #1ac567; -} -.tuple .blue + label, .tuple .z + label, .tuple .roll + label { - color: #1080b8; -} - -.tuple .red:focus, .tuple .x:focus, .tuple .pitch:focus, .tuple .width:focus { - outline-color: #e2334d; -} -.tuple .green:focus, .tuple .y:focus, .tuple .yaw:focus, .tuple .height:focus { - outline-color: #1ac567; -} -.tuple .blue:focus, .tuple .z:focus, .tuple .roll:focus { - outline-color: #1080b8; -} - -.xyz .buttons input { - margin-top: 14px; -} -.xyz .buttons span { - word-wrap: nowrap; - white-space: nowrap; -} - .row .property { width: auto; display: inline-block; @@ -984,12 +928,12 @@ div.refresh input[type="button"] { width: 50%; } -#properties-list fieldset .two-column { +#properties-list .fieldset .two-column { padding-top: 10px; display: flex; } -#properties-list .two-column fieldset { +#properties-list .two-column .fieldset { width: 50%; margin: 0; padding: 0; @@ -1002,7 +946,7 @@ div.refresh input[type="button"] { top: -10px; } -#properties-list .two-column fieldset legend { +#properties-list .two-column .fieldset div.legend { width: 100%; margin: 21px -21px 0 -21px; padding: 16px 0 0 21px; @@ -1018,11 +962,11 @@ div.refresh input[type="button"] { margin-top: 6px; } -fieldset .checkbox-sub-props { +.fieldset .checkbox-sub-props { margin-top: 0; } -fieldset .checkbox-sub-props .property:first-child { +.fieldset .checkbox-sub-props .property:first-child { margin-top: 0; } @@ -1069,6 +1013,7 @@ textarea:enabled[scrolling="true"]::-webkit-resizer { body#entity-list-body { padding-bottom: 0; + margin: 16px; } #entity-list-header { @@ -1449,7 +1394,6 @@ th#entity-hasScript { border-top: none !important; box-shadow: none !important; margin-bottom: 5px !important; - top: -15px; } #properties-base #property-type-icon { @@ -1468,6 +1412,7 @@ th#entity-hasScript { display: inline-block; } +/* #properties-base #div-property-locked { position: absolute; top: 6px; @@ -1488,6 +1433,7 @@ th#entity-hasScript { #properties-base #div-property-visible label { background-position-y: 1px; } +*/ #properties-base .checkbox label span { font-family: HiFi-Glyphs; @@ -1514,16 +1460,17 @@ th#entity-hasScript { } input#property-scale-button-rescale { + margin-top: 6px; min-width: 50px; - margin-left: 6px; } input#property-scale-button-reset { + margin-top: 6px; margin-right: 0; } -#property-userData-button-clear, +#property-userData-button-edit, #property-materialData-button-clear { - margin-bottom: 10px; + margin: 6px 0 6px 0; } #property-userData-static, @@ -1545,15 +1492,6 @@ input#property-scale-button-reset { display: none; } -#div-property-serverScripts-status label { - position: relative; - top: 8px; -} -#property-serverScripts-status { - position: relative; - top: 5px; - right: -20px; -} #div-property-collisionSoundURL[style*="display: none"] + .property { margin-top: 0; @@ -1620,3 +1558,101 @@ input.rename-entity { bottom: 0; margin-top: 5px; } + +.container { + display: flex; + flex-flow: row nowrap; + justify-content: space-around; + margin-bottom: 8px; + min-height: 28px; +} + +.container > label { + margin-top: 6px; + width: 200px; +} + +.container > div.checkbox { + padding-top: 6px; +} + +.container > .value { + width: 100%; +} + +.container .row { + display: flex; + flex-flow: row nowrap; +} + +.container.shrink { + width: min-content; +} + +.fstuple { + display: flex; + flex-flow: row; +} +.fstuple input { + margin-left: 4px; + margin-right: 10px; +} +.fstuple label.red, .fstuple label.x { + color: #C62147; +} +.fstuple label.green, .fstuple label.y { + color: #359D85; +} +.fstuple label.blue, .fstuple label.z { + color: #0093C5; +} + +.xyz.fstuple, .pyr.fstuple { + position: relative; + left: -12px; +} + +.rgb.fstuple .tuple { + display: none; +} + +input.number-slider { + background: #575757; + border-radius: 4px; + color: white; +} + +.fstuple > div { + display: flex; + align-items: center; + justify-content: left; +} + +.flex-row { + display: flex; + flex-flow: row; +} + +.flex-column { + display: flex; + flex-flow: column; +} + +.flex-center { + align-items: center; +} + +.flex-evenly-spaced { + flex: 1; +} + +#property-serverScripts-status { + font-family: Raleway-Light; + font-size: 14px; + margin: 6px 0; +} + +#property-name, #property-id { + display: flex; + width: 100%; +} diff --git a/scripts/system/html/css/jsoneditor.css b/scripts/system/html/css/jsoneditor.css index ce83b45ff3..eedef60a7f 100644 --- a/scripts/system/html/css/jsoneditor.css +++ b/scripts/system/html/css/jsoneditor.css @@ -223,8 +223,6 @@ div.jsoneditor-tree table.jsoneditor-tree { div.jsoneditor-outer { width: 100%; - height: 100%; - min-height: 150px; margin: -35px 0 0 0; padding: 0 0 0 0; -moz-box-sizing: border-box; @@ -233,20 +231,18 @@ div.jsoneditor-outer { overflow-y: auto; } -textarea.jsoneditor-text, .ace-jsoneditor { - min-height: 150px; + min-height: 150px; + height: auto !important; } div.jsoneditor-tree { width: 100%; - height: 100%; position: relative; } textarea.jsoneditor-text { width: 100%; - height: 100%; margin: 0; -moz-box-sizing: border-box; -webkit-box-sizing: border-box; @@ -931,4 +927,4 @@ table.jsoneditor-search button.jsoneditor-previous { table.jsoneditor-search button.jsoneditor-previous:hover { background-position: -148px -49px; -} \ No newline at end of file +} diff --git a/scripts/system/html/entityProperties.html b/scripts/system/html/entityProperties.html index b56ad346e2..6af8ee992a 100644 --- a/scripts/system/html/entityProperties.html +++ b/scripts/system/html/entityProperties.html @@ -28,6 +28,19 @@
+
+ +
+
+
+
+
+
+
+
+
+
+
diff --git a/scripts/system/html/js/entityProperties.js b/scripts/system/html/js/entityProperties.js index b66d7e19c6..e5e7426dd2 100644 --- a/scripts/system/html/js/entityProperties.js +++ b/scripts/system/html/js/entityProperties.js @@ -29,7 +29,7 @@ const ICON_FOR_TYPE = { const DEGREES_TO_RADIANS = Math.PI / 180.0; -const NO_SELECTION = "No selection"; +const NO_SELECTION = "w"; const GROUPS = [ { @@ -40,17 +40,22 @@ const GROUPS = [ type: "icon", icons: ICON_FOR_TYPE, propertyID: "type", + replaceID: "placeholder-property-type", }, { label: "Name", type: "string", propertyID: "name", + placeholder: "Name", + replaceID: "placeholder-property-name", }, { label: "ID", type: "string", propertyID: "id", + placeholder: "ID", readOnly: true, + replaceID: "placeholder-property-id", }, { label: "Description", @@ -68,16 +73,18 @@ const GROUPS = [ propertyID: "parentJointIndex", }, { - label: "Locked", + label: "", glyph: "", type: "bool", propertyID: "locked", + replaceID: "placeholder-property-locked", }, { - label: "Visible", + label: "", glyph: "", type: "bool", propertyID: "visible", + replaceID: "placeholder-property-visible", }, ] }, @@ -457,6 +464,7 @@ const GROUPS = [ { label: "Image", type: "string", + placeholder: "URL", propertyID: "image", }, ] @@ -634,6 +642,7 @@ const GROUPS = [ { id: "particles_emit", label: "EMIT", + isMinor: true, properties: [ { label: "Emit Rate", @@ -686,7 +695,7 @@ const GROUPS = [ vec3Type: "pyr", step: 0.01, round: 100, - subLabels: [ "pitch", "yaw", "roll" ], + subLabels: [ "x", "y", "z" ], unit: "deg", propertyID: "emitOrientation", }, @@ -700,35 +709,42 @@ const GROUPS = [ { id: "particles_size", label: "SIZE", + isMinor: true, properties: [ { + type: "triple", label: "Size", - type: "slider", - min: 0, - max: 4, - step: 0.01, - decimals: 2, - propertyID: "particleRadius", - }, - { - label: "Size Start", - type: "slider", - min: 0, - max: 4, - step: 0.01, - decimals: 2, - propertyID: "radiusStart", - fallbackProperty: "particleRadius", - }, - { - label: "Size Finish", - type: "slider", - min: 0, - max: 4, - step: 0.01, - decimals: 2, - propertyID: "radiusFinish", - fallbackProperty: "particleRadius", + properties: [ + { + label: "Start", + type: "slider", + min: 0, + max: 4, + step: 0.01, + decimals: 2, + propertyID: "radiusStart", + fallbackProperty: "particleRadius", + }, + { + label: "Middle", + type: "slider", + min: 0, + max: 4, + step: 0.01, + decimals: 2, + propertyID: "particleRadius", + }, + { + label: "Finish", + type: "slider", + min: 0, + max: 4, + step: 0.01, + decimals: 2, + propertyID: "radiusFinish", + fallbackProperty: "particleRadius", + }, + ] }, { label: "Size Spread", @@ -744,24 +760,31 @@ const GROUPS = [ { id: "particles_color", label: "COLOR", + isMinor: true, properties: [ { + type: "triple", label: "Color", - type: "color", - propertyID: "particleColor", - propertyName: "color", // actual entity property name - }, - { - label: "Color Start", - type: "color", - propertyID: "colorStart", - fallbackProperty: "color", - }, - { - label: "Color Finish", - type: "color", - propertyID: "colorFinish", - fallbackProperty: "color", + properties: [ + { + label: "Start", + type: "color", + propertyID: "colorStart", + fallbackProperty: "color", + }, + { + label: "Middle", + type: "color", + propertyID: "particleColor", + propertyName: "color", // actual entity property name + }, + { + label: "Finish", + type: "color", + propertyID: "colorFinish", + fallbackProperty: "color", + }, + ] }, { label: "Color Spread", @@ -773,35 +796,42 @@ const GROUPS = [ { id: "particles_alpha", label: "ALPHA", + isMinor: true, properties: [ { + type: "triple", label: "Alpha", - type: "slider", - min: 0, - max: 1, - step: 0.01, - decimals: 2, - propertyID: "alpha", - }, - { - label: "Alpha Start", - type: "slider", - min: 0, - max: 1, - step: 0.01, - decimals: 2, - propertyID: "alphaStart", - fallbackProperty: "alpha", - }, - { - label: "Alpha Finish", - type: "slider", - min: 0, - max: 1, - step: 0.01, - decimals: 2, - propertyID: "alphaFinish", - fallbackProperty: "alpha", + properties: [ + { + label: "Start", + type: "slider", + min: 0, + max: 1, + step: 0.01, + decimals: 2, + propertyID: "alphaStart", + fallbackProperty: "alpha", + }, + { + label: "Middle", + type: "slider", + min: 0, + max: 1, + step: 0.01, + decimals: 2, + propertyID: "alpha", + }, + { + label: "Finish", + type: "slider", + min: 0, + max: 1, + step: 0.01, + decimals: 2, + propertyID: "alphaFinish", + fallbackProperty: "alpha", + }, + ] }, { label: "Alpha Spread", @@ -817,6 +847,7 @@ const GROUPS = [ { id: "particles_acceleration", label: "ACCELERATION", + isMinor: true, properties: [ { label: "Emit Acceleration", @@ -841,41 +872,48 @@ const GROUPS = [ { id: "particles_spin", label: "SPIN", + isMinor: true, properties: [ { - label: "Spin", - type: "slider", - min: -360, - max: 360, - step: 1, - decimals: 0, - multiplier: DEGREES_TO_RADIANS, - unit: "deg", - propertyID: "particleSpin", - }, - { - label: "Spin Start", - type: "slider", - min: -360, - max: 360, - step: 1, - decimals: 0, - multiplier: DEGREES_TO_RADIANS, - unit: "deg", - propertyID: "spinStart", - fallbackProperty: "particleSpin", - }, - { - label: "Spin Finish", - type: "slider", - min: -360, - max: 360, - step: 1, - decimals: 0, - multiplier: DEGREES_TO_RADIANS, - unit: "deg", - propertyID: "spinFinish", - fallbackProperty: "particleSpin", + type: "triple", + label: "Alpha", + properties: [ + { + label: "Start", + type: "slider", + min: -360, + max: 360, + step: 1, + decimals: 0, + multiplier: DEGREES_TO_RADIANS, + unit: "deg", + propertyID: "spinStart", + fallbackProperty: "particleSpin", + }, + { + label: "Middle", + type: "slider", + min: -360, + max: 360, + step: 1, + decimals: 0, + multiplier: DEGREES_TO_RADIANS, + unit: "deg", + propertyID: "particleSpin", + }, + { + label: "Finish", + type: "slider", + min: -360, + max: 360, + step: 1, + decimals: 0, + multiplier: DEGREES_TO_RADIANS, + unit: "deg", + propertyID: "spinFinish", + fallbackProperty: "particleSpin", + }, + ] }, { label: "Spin Spread", @@ -898,51 +936,64 @@ const GROUPS = [ { id: "particles_constraints", label: "CONSTRAINTS", + isMinor: true, properties: [ { - label: "Horizontal Angle Start", - type: "slider", - min: 0, - max: 180, - step: 1, - decimals: 0, - multiplier: DEGREES_TO_RADIANS, - unit: "deg", - propertyID: "polarStart", + type: "triple", + label: "Horizontal Angle", + properties: [ + { + label: "Start", + type: "slider", + min: -180, + max: 0, + step: 1, + decimals: 0, + multiplier: DEGREES_TO_RADIANS, + unit: "deg", + propertyID: "polarStart", + }, + { + label: "Finish", + type: "slider", + min: 0, + max: 180, + step: 1, + decimals: 0, + multiplier: DEGREES_TO_RADIANS, + unit: "deg", + propertyID: "polarFinish", + }, + ], }, { - label: "Horizontal Angle Finish", - type: "slider", - min: 0, - max: 180, - step: 1, - decimals: 0, - multiplier: DEGREES_TO_RADIANS, - unit: "deg", - propertyID: "polarFinish", - }, - { - label: "Vertical Angle Start", - type: "slider", - min: -180, - max: 0, - step: 1, - decimals: 0, - multiplier: DEGREES_TO_RADIANS, - unit: "deg", - propertyID: "azimuthStart", - }, - { - label: "Vertical Angle Finish", - type: "slider", - min: 0, - max: 180, - step: 1, - decimals: 0, - multiplier: DEGREES_TO_RADIANS, - unit: "deg", - propertyID: "azimuthFinish", - }, + type: "triple", + label: "Vertical Angle", + properties: [ + { + label: "Start", + type: "slider", + min: 0, + max: 180, + step: 1, + decimals: 0, + multiplier: DEGREES_TO_RADIANS, + unit: "deg", + propertyID: "azimuthStart", + }, + { + label: "Finish", + type: "slider", + min: 0, + max: 180, + step: 1, + decimals: 0, + multiplier: DEGREES_TO_RADIANS, + unit: "deg", + propertyID: "azimuthFinish", + }, + ] + } ] }, { @@ -964,7 +1015,7 @@ const GROUPS = [ vec3Type: "pyr", step: 0.1, decimals: 4, - subLabels: [ "pitch", "yaw", "roll" ], + subLabels: [ "x", "y", "z" ], unit: "deg", propertyID: "rotation", }, @@ -1023,6 +1074,7 @@ const GROUPS = [ }, { label: "Clone Lifetime", + indentedLabel: true, type: "number", unit: "s", propertyID: "cloneLifetime", @@ -1030,18 +1082,21 @@ const GROUPS = [ }, { label: "Clone Limit", + indentedLabel: true, type: "number", propertyID: "cloneLimit", showPropertyRule: { "cloneable": "true" }, }, { label: "Clone Dynamic", + indentedLabel: true, type: "bool", propertyID: "cloneDynamic", showPropertyRule: { "cloneable": "true" }, }, { label: "Clone Avatar Entity", + indentedLabel: true, type: "bool", propertyID: "cloneAvatarEntity", showPropertyRule: { "cloneable": "true" }, @@ -1078,6 +1133,12 @@ const GROUPS = [ buttons: [ { id: "reload", label: "F", className: "glyph", onClick: reloadServerScripts } ], propertyID: "serverScripts", }, + { + label: "Server Script Status", + type: "placeholder", + indentedLabel: true, + propertyID: "serverScriptStatus", + }, { label: "Lifetime", type: "number", @@ -1104,12 +1165,6 @@ const GROUPS = [ inverse: true, propertyID: "collisionless", }, - { - label: "Collides With", - type: "sub-header", - propertyID: "collidesWithHeader", // not actually a property but used for naming/storing this element - showPropertyRule: { "collisionless": "false" }, - }, { label: "Static Entities", type: "bool", @@ -1188,7 +1243,7 @@ const GROUPS = [ vec3Type: "pyr", multiplier: DEGREES_TO_RADIANS, decimals: 4, - subLabels: [ "pitch", "yaw", "roll" ], + subLabels: [ "x", "y", "z" ], unit: "deg/s", propertyID: "angularVelocity", }, @@ -1327,6 +1382,12 @@ function debugPrint(message) { ); } +function createElementFromHTML(htmlString) { + var elTemplate = document.createElement('template'); + elTemplate.innerHTML = htmlString.trim(); + return elTemplate.content.firstChild; +} + /** * GENERAL PROPERTY/GROUP FUNCTIONS @@ -1399,8 +1460,8 @@ function disableProperties() { } function showPropertyElement(propertyID, show) { - let elProperty = properties[propertyID].elProperty; - elProperty.style.display = show ? "table" : "none"; + let elProperty = properties[propertyID].elContainer; + elProperty.style.display = show ? "flex" : "none"; } function resetProperties() { @@ -1457,7 +1518,6 @@ function resetProperties() { } case 'icon': { property.elSpan.style.display = "none"; - property.elLabel.innerHTML = propertyData.label; break; } case 'texture': { @@ -1658,23 +1718,23 @@ function createImageURLUpdateFunction(propertyName, isParticleProperty) { * PROPERTY ELEMENT CREATION FUNCTIONS */ -function createStringProperty(property, elProperty, elLabel) { +function createStringProperty(property, elProperty) { let propertyName = property.name; let elementID = property.elementID; let propertyData = property.data; - elProperty.className = "property text"; + elProperty.className = "text"; - let elInput = document.createElement('input'); - elInput.setAttribute("id", elementID); - elInput.setAttribute("type", "text"); - if (propertyData.readOnly) { - elInput.readOnly = true; - } + let elInput = createElementFromHTML(` + + `) + elInput.addEventListener('change', createEmitTextPropertyUpdateFunction(propertyName, property.isParticleProperty)); - elProperty.appendChild(elLabel); elProperty.appendChild(elInput); if (propertyData.buttons !== undefined) { @@ -1684,18 +1744,18 @@ function createStringProperty(property, elProperty, elLabel) { return elInput; } -function createBoolProperty(property, elProperty, elLabel) { +function createBoolProperty(property, elProperty) { let propertyName = property.name; let elementID = property.elementID; let propertyData = property.data; - elProperty.className = "property checkbox"; + elProperty.className = "checkbox"; if (propertyData.glyph !== undefined) { - elLabel.innerText = " " + propertyData.label; let elSpan = document.createElement('span'); elSpan.innerHTML = propertyData.glyph; - elLabel.insertBefore(elSpan, elLabel.firstChild); + elSpan.className = 'icon'; + elProperty.appendChild(elSpan); } let elInput = document.createElement('input'); @@ -1703,7 +1763,7 @@ function createBoolProperty(property, elProperty, elLabel) { elInput.setAttribute("type", "checkbox"); elProperty.appendChild(elInput); - elProperty.appendChild(elLabel); + elProperty.appendChild(createElementFromHTML(``)); let subPropertyOf = propertyData.subPropertyOf; if (subPropertyOf !== undefined) { @@ -1717,14 +1777,12 @@ function createBoolProperty(property, elProperty, elLabel) { return elInput; } -function createNumberProperty(property, elProperty, elLabel) { +function createNumberProperty(property, elProperty) { let propertyName = property.name; let elementID = property.elementID; let propertyData = property.data; - elProperty.className = "property number"; - - addUnit(propertyData.unit, elLabel); + elProperty.className = "number"; let elInput = document.createElement('input'); elInput.setAttribute("id", elementID); @@ -1746,7 +1804,6 @@ function createNumberProperty(property, elProperty, elLabel) { elInput.addEventListener('change', createEmitNumberPropertyUpdateFunction(propertyName, propertyData.multiplier, propertyData.decimals, property.isParticleProperty)); - elProperty.appendChild(elLabel); elProperty.appendChild(elInput); if (propertyData.buttons !== undefined) { @@ -1756,10 +1813,10 @@ function createNumberProperty(property, elProperty, elLabel) { return elInput; } -function createSliderProperty(property, elProperty, elLabel) { +function createSliderProperty(property, elProperty) { let propertyData = property.data; - elProperty.className = "property range"; + elProperty.className = "range"; let elDiv = document.createElement("div"); elDiv.className = "slider-wrapper"; @@ -1809,7 +1866,6 @@ function createSliderProperty(property, elProperty, elLabel) { updateProperty(property.name, sliderValue, property.isParticleProperty); }; - elDiv.appendChild(elLabel); elDiv.appendChild(elSlider); elDiv.appendChild(elInput); elProperty.appendChild(elDiv); @@ -1820,26 +1876,23 @@ function createSliderProperty(property, elProperty, elLabel) { return elResult; } -function createVec3Property(property, elProperty, elLabel) { +function createVec3Property(property, elProperty) { let propertyName = property.name; let elementID = property.elementID; let propertyData = property.data; - elProperty.className = "property " + propertyData.vec3Type + " fstuple"; + elProperty.className = propertyData.vec3Type + " fstuple"; - let elTuple = document.createElement('div'); - elTuple.className = "tuple"; + //let elTuple = document.createElement('div'); + //elTuple.className = "tuple"; - addUnit(propertyData.unit, elLabel); + //elProperty.appendChild(elTuple); - elProperty.appendChild(elLabel); - elProperty.appendChild(elTuple); - - let elInputX = createTupleNumberInput(elTuple, elementID, propertyData.subLabels[VECTOR_ELEMENTS.X_INPUT], + let elInputX = createTupleNumberInput(elProperty, elementID, propertyData.subLabels[VECTOR_ELEMENTS.X_INPUT], propertyData.min, propertyData.max, propertyData.step); - let elInputY = createTupleNumberInput(elTuple, elementID, propertyData.subLabels[VECTOR_ELEMENTS.Y_INPUT], + let elInputY = createTupleNumberInput(elProperty, elementID, propertyData.subLabels[VECTOR_ELEMENTS.Y_INPUT], propertyData.min, propertyData.max, propertyData.step); - let elInputZ = createTupleNumberInput(elTuple, elementID, propertyData.subLabels[VECTOR_ELEMENTS.Z_INPUT], + let elInputZ = createTupleNumberInput(elProperty, elementID, propertyData.subLabels[VECTOR_ELEMENTS.Z_INPUT], propertyData.min, propertyData.max, propertyData.step); let inputChangeFunction = createEmitVec3PropertyUpdateFunction(propertyName, elInputX, elInputY, elInputZ, @@ -1855,19 +1908,16 @@ function createVec3Property(property, elProperty, elLabel) { return elResult; } -function createVec2Property(property, elProperty, elLabel) { +function createVec2Property(property, elProperty) { let propertyName = property.name; let elementID = property.elementID; let propertyData = property.data; - elProperty.className = "property " + propertyData.vec2Type + " fstuple"; + elProperty.className = propertyData.vec2Type + " fstuple"; let elTuple = document.createElement('div'); elTuple.className = "tuple"; - addUnit(propertyData.unit, elLabel); - - elProperty.appendChild(elLabel); elProperty.appendChild(elTuple); let elInputX = createTupleNumberInput(elTuple, elementID, propertyData.subLabels[VECTOR_ELEMENTS.X_INPUT], @@ -1886,11 +1936,11 @@ function createVec2Property(property, elProperty, elLabel) { return elResult; } -function createColorProperty(property, elProperty, elLabel) { +function createColorProperty(property, elProperty) { let propertyName = property.name; let elementID = property.elementID; - elProperty.className = "property rgb fstuple"; + elProperty.className = "rgb fstuple"; let elColorPicker = document.createElement('div'); elColorPicker.className = "color-picker"; @@ -1900,7 +1950,6 @@ function createColorProperty(property, elProperty, elLabel) { elTuple.className = "tuple"; elProperty.appendChild(elColorPicker); - elProperty.appendChild(elLabel); elProperty.appendChild(elTuple); let elInputR = createTupleNumberInput(elTuple, elementID, "red", COLOR_MIN, COLOR_MAX, COLOR_STEP); @@ -1916,7 +1965,7 @@ function createColorProperty(property, elProperty, elLabel) { let colorPickerID = "#" + elementID; colorPickers[colorPickerID] = $(colorPickerID).colpick({ colorScheme: 'dark', - layout: 'hex', + layout: 'rgbhex', color: '000000', submit: false, // We don't want to have a submission button onShow: function(colpick) { @@ -1946,12 +1995,12 @@ function createColorProperty(property, elProperty, elLabel) { return elResult; } -function createDropdownProperty(property, propertyID, elProperty, elLabel) { +function createDropdownProperty(property, propertyID, elProperty) { let propertyName = property.name; let elementID = property.elementID; let propertyData = property.data; - elProperty.className = "property dropdown"; + elProperty.className = "dropdown"; let elInput = document.createElement('select'); elInput.setAttribute("id", elementID); @@ -1966,24 +2015,17 @@ function createDropdownProperty(property, propertyID, elProperty, elLabel) { elInput.addEventListener('change', createEmitTextPropertyUpdateFunction(propertyName, property.isParticleProperty)); - elProperty.appendChild(elLabel); elProperty.appendChild(elInput); return elInput; } -function createTextareaProperty(property, elProperty, elLabel) { +function createTextareaProperty(property, elProperty) { let propertyName = property.name; let elementID = property.elementID; let propertyData = property.data; - elProperty.className = "property textarea"; - - elProperty.appendChild(elLabel); - - if (propertyData.buttons !== undefined) { - addButtons(elProperty, elementID, propertyData.buttons, true); - } + elProperty.className = "textarea"; let elInput = document.createElement('textarea'); elInput.setAttribute("id", elementID); @@ -1994,35 +2036,35 @@ function createTextareaProperty(property, elProperty, elLabel) { elInput.addEventListener('change', createEmitTextPropertyUpdateFunction(propertyName, property.isParticleProperty)); elProperty.appendChild(elInput); + + if (propertyData.buttons !== undefined) { + addButtons(elProperty, elementID, propertyData.buttons, true); + } return elInput; } -function createIconProperty(property, elProperty, elLabel) { +function createIconProperty(property, elProperty) { let elementID = property.elementID; let propertyData = property.data; - elProperty.className = "property value"; - - elLabel.setAttribute("id", elementID); - elLabel.innerHTML = " " + propertyData.label; + elProperty.className = "value"; let elSpan = document.createElement('span'); elSpan.setAttribute("id", elementID + "-icon"); + elSpan.className = 'icon'; elProperty.appendChild(elSpan); - elProperty.appendChild(elLabel); let elResult = []; elResult[ICON_ELEMENTS.ICON] = elSpan; - elResult[ICON_ELEMENTS.LABEL] = elLabel; return elResult; } -function createTextureProperty(property, elProperty, elLabel) { +function createTextureProperty(property, elProperty) { let elementID = property.elementID; - elProperty.className = "property texture"; + elProperty.className = "texture"; let elDiv = document.createElement("div"); let elImage = document.createElement("img"); @@ -2063,9 +2105,8 @@ function createTextureProperty(property, elProperty, elLabel) { }; elInput.onchange = elInput.oninput; - elProperty.appendChild(elLabel); - elProperty.appendChild(elDiv); elProperty.appendChild(elInput); + elProperty.appendChild(elDiv); let elResult = []; elResult[TEXTURE_ELEMENTS.IMAGE] = elImage; @@ -2077,11 +2118,10 @@ function createButtonsProperty(property, elProperty, elLabel) { let elementID = property.elementID; let propertyData = property.data; - elProperty.className = "property text"; + elProperty.className = "text"; let hasLabel = propertyData.label !== undefined; if (hasLabel) { - elProperty.appendChild(elLabel); } if (propertyData.buttons !== undefined) { @@ -2096,33 +2136,25 @@ function createTupleNumberInput(elTuple, propertyElementID, subLabel, min, max, let elDiv = document.createElement('div'); let elLabel = document.createElement('label'); - elLabel.innerText = subLabel[0].toUpperCase() + subLabel.slice(1) + ":"; + elLabel.className = subLabel; + elLabel.innerText = subLabel[0].toUpperCase() + subLabel.slice(1); elLabel.setAttribute("for", elementID); let elInput = document.createElement('input'); - elInput.className = subLabel; + elInput.className = subLabel + " number-slider"; elInput.setAttribute("id", elementID); elInput.setAttribute("type", "number"); elInput.setAttribute("min", min); elInput.setAttribute("max", max); elInput.setAttribute("step", step); - elDiv.appendChild(elInput); elDiv.appendChild(elLabel); + elDiv.appendChild(elInput); elTuple.appendChild(elDiv); return elInput; } -function addUnit(unit, elLabel) { - if (unit !== undefined) { - let elSpan = document.createElement('span'); - elSpan.className = "unit"; - elSpan.innerHTML = unit; - elLabel.appendChild(elSpan); - } -} - function addButtons(elProperty, propertyID, buttons, newRow) { let elDiv = document.createElement('div'); elDiv.className = "row"; @@ -2666,29 +2698,120 @@ function showParentMaterialNameBox(number, elNumber, elString) { } } +function createProperty(propertyData, propertyElementID, propertyName, propertyID, elProperty) { + let property = { + data: propertyData, + elementID: propertyElementID, + name: propertyName, + elProperty: elProperty, + }; + let propertyType = propertyData.type; + + switch (propertyType) { + case 'string': { + property.elInput = createStringProperty(property, elProperty); + break; + } + case 'bool': { + property.elInput = createBoolProperty(property, elProperty); + break; + } + case 'number': { + property.elInput = createNumberProperty(property, elProperty); + break; + } + case 'slider': { + let elSlider = createSliderProperty(property, elProperty); + property.elSlider = elSlider[SLIDER_ELEMENTS.SLIDER]; + property.elInput = elSlider[SLIDER_ELEMENTS.NUMBER_INPUT]; + break; + } + case 'vec3': { + let elVec3 = createVec3Property(property, elProperty); + property.elInputX = elVec3[VECTOR_ELEMENTS.X_INPUT]; + property.elInputY = elVec3[VECTOR_ELEMENTS.Y_INPUT]; + property.elInputZ = elVec3[VECTOR_ELEMENTS.Z_INPUT]; + break; + } + case 'vec2': { + let elVec2 = createVec2Property(property, elProperty); + property.elInputX = elVec2[VECTOR_ELEMENTS.X_INPUT]; + property.elInputY = elVec2[VECTOR_ELEMENTS.Y_INPUT]; + break; + } + case 'color': { + let elColor = createColorProperty(property, elProperty); + property.elColorPicker = elColor[COLOR_ELEMENTS.COLOR_PICKER]; + property.elInputR = elColor[COLOR_ELEMENTS.RED_INPUT]; + property.elInputG = elColor[COLOR_ELEMENTS.GREEN_INPUT]; + property.elInputB = elColor[COLOR_ELEMENTS.BLUE_INPUT]; + break; + } + case 'dropdown': { + property.elInput = createDropdownProperty(property, propertyID, elProperty); + break; + } + case 'textarea': { + property.elInput = createTextareaProperty(property, elProperty); + break; + } + case 'icon': { + let elIcon = createIconProperty(property, elProperty); + property.elSpan = elIcon[ICON_ELEMENTS.ICON]; + property.elLabel = elIcon[ICON_ELEMENTS.LABEL]; + break; + } + case 'texture': { + let elTexture = createTextureProperty(property, elProperty); + property.elImage = elTexture[TEXTURE_ELEMENTS.IMAGE]; + property.elInput = elTexture[TEXTURE_ELEMENTS.TEXT_INPUT]; + break; + } + case 'buttons': { + property.elProperty = createButtonsProperty(property, elProperty); + break; + } + case 'placeholder': + case 'sub-header': { + break; + } + default: { + console.log("EntityProperties - Unknown property type " + + propertyType + " set to property " + propertyID); + break; + } + } + + return property; +} + function loaded() { openEventBridge(function() { let elPropertiesList = document.getElementById("properties-list"); + + let templatePropertyRow = document.getElementById('property-row'); - GROUPS.forEach(function(group) { + GROUPS.forEach(function(group) { let elGroup; if (group.addToGroup !== undefined) { let fieldset = document.getElementById("properties-" + group.addToGroup); elGroup = document.createElement('div'); fieldset.appendChild(elGroup); } else { - elGroup = document.createElement('fieldset'); - elGroup.className = "major"; + elGroup = document.createElement('div'); + elGroup.className = 'section ' + (group.isMinor ? "minor" : "major"); elGroup.setAttribute("id", "properties-" + group.id); elPropertiesList.appendChild(elGroup); } if (group.label !== undefined) { - let elLegend = document.createElement('legend'); + let elLegend = document.createElement('div'); elLegend.className = "section-header"; - elLegend.innerText = group.label; + + elLegend.appendChild(createElementFromHTML(`
${group.label}
`)); + let elSpan = document.createElement('span'); elSpan.className = ".collapse-icon"; elSpan.innerText = "M"; @@ -2703,143 +2826,117 @@ function loaded() { let propertyElementID = "property-" + propertyID; propertyElementID = propertyElementID.replace('.', '-'); - let elProperty; - if (propertyType === "sub-header") { - elProperty = document.createElement('legend'); - elProperty.className = "sub-section-header"; - elProperty.innerText = propertyData.label; - } else { - elProperty = document.createElement('div'); - elProperty.setAttribute("id", "div-" + propertyElementID); - } + let elContainer, elLabel; - if (group.twoColumn && propertyData.column !== undefined && propertyData.column !== -1) { - let columnName = group.id + "column" + propertyData.column; - let elColumn = document.getElementById(columnName); - if (!elColumn) { - let columnDivName = group.id + "columnDiv"; - let elColumnDiv = document.getElementById(columnDivName); - if (!elColumnDiv) { - elColumnDiv = document.createElement('div'); - elColumnDiv.className = "two-column"; - elColumnDiv.setAttribute("id", group.id + "columnDiv"); - elGroup.appendChild(elColumnDiv); - } - elColumn = document.createElement('fieldset'); - elColumn.className = "column"; - elColumn.setAttribute("id", columnName); - elColumnDiv.appendChild(elColumn); + if (propertyData.replaceID === undefined) { + // Create subheader, or create new property and append it. + if (propertyType === "sub-header") { + elContainer = createElementFromHTML( + `
${propertyData.label}
`); + } else { + elContainer = document.createElement('div'); + elContainer.setAttribute("id", "div-" + propertyElementID); + elContainer.className = 'property container'; } - elColumn.appendChild(elProperty); - } else { - elGroup.appendChild(elProperty); - } - - let elLabel = document.createElement('label'); - elLabel.innerText = propertyData.label; - elLabel.setAttribute("for", propertyElementID); - createAppTooltip.registerTooltipElement(elLabel, propertyID); - - let property = { - data: propertyData, - elementID: propertyElementID, - name: propertyName, - isParticleProperty: group.id.includes("particles"), - elProperty: elProperty - }; - properties[propertyID] = property; - - switch (propertyType) { - case 'string': { - properties[propertyID].elInput = createStringProperty(property, elProperty, elLabel); - break; + if (group.twoColumn && propertyData.column !== undefined && propertyData.column !== -1) { + let columnName = group.id + "column" + propertyData.column; + let elColumn = document.getElementById(columnName); + if (!elColumn) { + let columnDivName = group.id + "columnDiv"; + let elColumnDiv = document.getElementById(columnDivName); + if (!elColumnDiv) { + elColumnDiv = document.createElement('div'); + elColumnDiv.className = "two-column"; + elColumnDiv.setAttribute("id", group.id + "columnDiv"); + elGroup.appendChild(elColumnDiv); + } + elColumn = document.createElement('fieldset'); + elColumn.className = "column"; + elColumn.setAttribute("id", columnName); + elColumnDiv.appendChild(elColumn); + } + elColumn.appendChild(elContainer); + } else { + elGroup.appendChild(elContainer); } - case 'bool': { - properties[propertyID].elInput = createBoolProperty(property, elProperty, elLabel); - break; - } - case 'number': { - properties[propertyID].elInput = createNumberProperty(property, elProperty, elLabel); - break; - } - case 'slider': { - let elSlider = createSliderProperty(property, elProperty, elLabel); - properties[propertyID].elSlider = elSlider[SLIDER_ELEMENTS.SLIDER]; - properties[propertyID].elInput = elSlider[SLIDER_ELEMENTS.NUMBER_INPUT]; - break; - } - case 'vec3': { - let elVec3 = createVec3Property(property, elProperty, elLabel); - properties[propertyID].elInputX = elVec3[VECTOR_ELEMENTS.X_INPUT]; - properties[propertyID].elInputY = elVec3[VECTOR_ELEMENTS.Y_INPUT]; - properties[propertyID].elInputZ = elVec3[VECTOR_ELEMENTS.Z_INPUT]; - break; - } - case 'vec2': { - let elVec2 = createVec2Property(property, elProperty, elLabel); - properties[propertyID].elInputX = elVec2[VECTOR_ELEMENTS.X_INPUT]; - properties[propertyID].elInputY = elVec2[VECTOR_ELEMENTS.Y_INPUT]; - break; - } - case 'color': { - let elColor = createColorProperty(property, elProperty, elLabel); - properties[propertyID].elColorPicker = elColor[COLOR_ELEMENTS.COLOR_PICKER]; - properties[propertyID].elInputR = elColor[COLOR_ELEMENTS.RED_INPUT]; - properties[propertyID].elInputG = elColor[COLOR_ELEMENTS.GREEN_INPUT]; - properties[propertyID].elInputB = elColor[COLOR_ELEMENTS.BLUE_INPUT]; - break; - } - case 'dropdown': { - properties[propertyID].elInput = createDropdownProperty(property, propertyID, elProperty, elLabel); - break; - } - case 'textarea': { - properties[propertyID].elInput = createTextareaProperty(property, elProperty, elLabel); - break; - } - case 'icon': { - let elIcon = createIconProperty(property, elProperty, elLabel); - properties[propertyID].elSpan = elIcon[ICON_ELEMENTS.ICON]; - properties[propertyID].elLabel = elIcon[ICON_ELEMENTS.LABEL]; - break; - } - case 'texture': { - let elTexture = createTextureProperty(property, elProperty, elLabel); - properties[propertyID].elImage = elTexture[TEXTURE_ELEMENTS.IMAGE]; - properties[propertyID].elInput = elTexture[TEXTURE_ELEMENTS.TEXT_INPUT]; - break; - } - case 'buttons': { - properties[propertyID].elProperty = createButtonsProperty(property, elProperty, elLabel); - break; - } - case 'sub-header': { - break; - } - default: { - console.log("EntityProperties - Unknown property type " + - propertyType + " set to property " + propertyID); - break; + + elLabel = document.createElement('label'); + elLabel.setAttribute("for", propertyElementID); + if (propertyData.indentedLabel || propertyData.showPropertyRule !== undefined) { + let elSpan = document.createElement('span'); + elSpan.className = 'indented'; + elSpan.innerText = propertyData.label; + elLabel.appendChild(elSpan); + } else { + elLabel.innerText = propertyData.label; } + elContainer.appendChild(elLabel); + } else { + elContainer = document.getElementById(propertyData.replaceID); } - - let showPropertyRule = propertyData.showPropertyRule; - if (showPropertyRule !== undefined) { - let dependentProperty = Object.keys(showPropertyRule)[0]; - let dependentPropertyValue = showPropertyRule[dependentProperty]; - if (properties[dependentProperty] === undefined) { - properties[dependentProperty] = {}; + + if (elLabel) { + createAppTooltip.registerTooltipElement(elLabel, propertyID); + } + + let elProperty = createElementFromHTML('
'); + elContainer.appendChild(elProperty); + + if (propertyType == 'triple') { + elProperty.className = 'flex-row'; + for (var i = 0; i < propertyData.properties.length; ++i) { + let innerPropertyData = propertyData.properties[i]; + + let elWrapper = createElementFromHTML('
'); + + let propertyID = innerPropertyData.propertyID; + let propertyName = innerPropertyData.propertyName !== undefined ? innerPropertyData.propertyName : propertyID; + let propertyElementID = "property-" + propertyID; + propertyElementID = propertyElementID.replace('.', '-'); + + elWrapper.appendChild(createElementFromHTML(`
${innerPropertyData.label}
`)); + elProperty.appendChild(elWrapper); + + let property = createProperty(innerPropertyData, propertyElementID, propertyName, propertyID, elWrapper.childNodes[0]); + property.isParticleProperty = group.id.includes("particles"); + property.elContainer = elContainer; + + if (property.type !== 'placeholder') { + properties[propertyID] = property; + } } - if (properties[dependentProperty].showPropertyRules === undefined) { - properties[dependentProperty].showPropertyRules = {}; + } else { + let property = createProperty(propertyData, propertyElementID, propertyName, propertyID, elProperty); + property.elementID = propertyElementID; + property.name = propertyName; + property.isParticleProperty = group.id.includes("particles"); + property.elContainer = elContainer; + + if (property.type !== 'placeholder') { + properties[propertyID] = property; + } + + let showPropertyRule = propertyData.showPropertyRule; + if (showPropertyRule !== undefined) { + let dependentProperty = Object.keys(showPropertyRule)[0]; + let dependentPropertyValue = showPropertyRule[dependentProperty]; + if (properties[dependentProperty] === undefined) { + properties[dependentProperty] = {}; + } + if (properties[dependentProperty].showPropertyRules === undefined) { + properties[dependentProperty].showPropertyRules = {}; + } + properties[dependentProperty].showPropertyRules[propertyID] = dependentPropertyValue; } - properties[dependentProperty].showPropertyRules[propertyID] = dependentPropertyValue; } }); elGroups[group.id] = elGroup; }); + + let minorSections = document.querySelectorAll(".section.minor"); + minorSections[minorSections.length - 1].className += " last"; if (window.EventBridge !== undefined) { EventBridge.scriptEventReceived.connect(function(data) { @@ -2874,6 +2971,10 @@ function loaded() { resetProperties(); showGroupsForType("None"); + + let elIcon = properties.type.elSpan; + elIcon.innerText = NO_SELECTION; + elIcon.style.display = 'inline-block'; deleteJSONEditor(); getPropertyInputElement("userData").value = ""; @@ -3063,7 +3164,6 @@ function loaded() { case 'icon': { property.elSpan.innerHTML = propertyData.icons[propertyValue]; property.elSpan.style.display = "inline-block"; - property.elLabel.innerHTML = propertyValue; break; } case 'texture': { @@ -3175,34 +3275,27 @@ function loaded() { } // Server Script Status - let serverScriptProperty = properties["serverScripts"]; - let elServerScript = serverScriptProperty.elInput; - let serverScriptElementID = serverScriptProperty.elementID; - let serverScriptStatusElementID = serverScriptElementID + "-status"; - let elDiv = document.createElement('div'); - elDiv.className = "property"; - elDiv.setAttribute("id", "div-" + serverScriptStatusElementID); - let elLabel = document.createElement('label'); - elLabel.setAttribute("for", serverScriptStatusElementID); - elLabel.innerText = "Server Script Status"; - createAppTooltip.registerTooltipElement(elLabel, "serverScriptsStatus"); - let elServerScriptStatus = document.createElement('span'); + let elServerScriptStatusOuter = document.getElementById('div-property-serverScriptStatus'); + let elServerScriptStatusContainer = document.getElementById('div-property-serverScriptStatus').childNodes[1]; + let serverScriptStatusElementID = 'property-serverScripts-status'; + createAppTooltip.registerTooltipElement(elServerScriptStatusOuter.childNodes[0], "serverScriptsStatus"); + let elServerScriptStatus = document.createElement('div'); elServerScriptStatus.setAttribute("id", serverScriptStatusElementID); - elDiv.appendChild(elLabel); - elDiv.appendChild(elServerScriptStatus); - elServerScript.parentNode.appendChild(elDiv); + elServerScriptStatusContainer.appendChild(elServerScriptStatus); // Server Script Error + let elServerScripts = getPropertyInputElement("serverScripts"); elDiv = document.createElement('div'); elDiv.className = "property"; let elServerScriptError = document.createElement('textarea'); - elServerScriptError.setAttribute("id", serverScriptElementID + "-error"); + let serverScriptErrorElementID = 'property-serverScripts-error'; + elServerScriptError.setAttribute("id", serverScriptErrorElementID); elDiv.appendChild(elServerScriptError); - elServerScript.parentNode.appendChild(elDiv); + elServerScriptStatusContainer.appendChild(elDiv); let elScript = getPropertyInputElement("script"); - elScript.parentNode.className = "property url refresh"; - elServerScript.parentNode.className = "property url refresh"; + elScript.parentNode.className = "url refresh"; + elServerScripts.parentNode.className = "url refresh"; // User Data let userDataProperty = properties["userData"]; @@ -3262,7 +3355,7 @@ function loaded() { let elCollapsible = document.getElementsByClassName("section-header"); let toggleCollapsedEvent = function(event) { - let element = event.target.parentNode.parentNode; + let element = this.parentNode; let isCollapsed = element.dataset.collapsed !== "true"; element.dataset.collapsed = isCollapsed ? "true" : false; element.setAttribute("collapsed", isCollapsed ? "true" : "false"); @@ -3271,7 +3364,7 @@ function loaded() { for (let collapseIndex = 0, numCollapsibles = elCollapsible.length; collapseIndex < numCollapsibles; ++collapseIndex) { let curCollapsibleElement = elCollapsible[collapseIndex]; - curCollapsibleElement.getElementsByTagName('span')[0].addEventListener("click", toggleCollapsedEvent, true); + curCollapsibleElement.addEventListener("click", toggleCollapsedEvent, true); } // Textarea scrollbars From 3b57e45fdd2b4225e91fe091a2afa81429929e74 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 8 Nov 2018 16:33:25 -0800 Subject: [PATCH 171/286] Remove redundant dimensions in default entity properties --- scripts/system/edit.js | 1 - 1 file changed, 1 deletion(-) diff --git a/scripts/system/edit.js b/scripts/system/edit.js index c8811bd603..705e0fc8c4 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -466,7 +466,6 @@ const DEFAULT_ENTITY_PROPERTIES = { isSpotlight: false, exponent: 1.0, cutoff: 75.0, - dimensions: { x: 20, y: 20, z: 20 }, }, }; From d2a1b35f969027806483a8651a54af740232eb51 Mon Sep 17 00:00:00 2001 From: r3tk0n Date: Thu, 8 Nov 2018 16:50:11 -0800 Subject: [PATCH 172/286] Fix desktop case showing Vive controls. --- interface/resources/html/tabletHelp.html | 20 +++++++++++--------- interface/src/Application.cpp | 2 +- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/interface/resources/html/tabletHelp.html b/interface/resources/html/tabletHelp.html index cc931580c3..8d4214d5af 100644 --- a/interface/resources/html/tabletHelp.html +++ b/interface/resources/html/tabletHelp.html @@ -94,15 +94,14 @@ switch (index) { case 0: - showHandControllers(); - break; - case 1: showGamepad(); break; - case 2: + case 1: showKbm(); break; - + case 2: + showHandControllers(); + break; default: } } @@ -142,23 +141,26 @@ handControllerImageURL = "img/tablet-help-vive.jpg"; break; case "oculus": + handControllerImageURL = "img/tablet-help-oculus.jpg"; + break; default: - handControllerImageURL = "img/tablet-help-oculus.jpg"; + handControllerImageURL = null; + count = 2; } switch (params.defaultTab) { case "handControllers": showHandControllers(); - index = 0; + index = 2; break; case "gamepad": showGamepad(); - index = 1; + index = 0; break; case "kbm": default: showKbm(); - index = 2; + index = 1; } } diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 245e6c0017..ecb738c7c4 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -3366,7 +3366,7 @@ void Application::showHelp() { static const QString TAB_GAMEPAD = "gamepad"; static const QString TAB_HAND_CONTROLLERS = "handControllers"; - QString handControllerName = HAND_CONTROLLER_NAME_VIVE; + QString handControllerName = ""; QString defaultTab = TAB_KEYBOARD_MOUSE; if (PluginUtils::isViveControllerAvailable()) { From 8735b409ab110e4cc7b9d67b18932a6f37711782 Mon Sep 17 00:00:00 2001 From: amantley Date: Thu, 8 Nov 2018 17:12:10 -0800 Subject: [PATCH 173/286] in the process of changing the rotation application to being absolute joints not local --- interface/src/avatar/MyAvatar.cpp | 3 - interface/src/avatar/MySkeletonModel.cpp | 17 +---- libraries/animation/src/AnimSkeleton.cpp | 82 +++++++++++++----------- libraries/animation/src/Rig.cpp | 2 + 4 files changed, 48 insertions(+), 56 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 39e6a56b02..3299bd10e7 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -2037,9 +2037,7 @@ controller::Pose MyAvatar::getControllerPoseInSensorFrame(controller::Action act controller::Pose MyAvatar::getControllerPoseInWorldFrame(controller::Action action) const { auto pose = getControllerPoseInSensorFrame(action); - qCDebug(interfaceapp) << "avatar sensor orientation " << pose.getRotation(); if (pose.valid) { - qCDebug(interfaceapp) << "sensor to world matrix orientation " << extractRotation(getSensorToWorldMatrix()); return pose.transform(getSensorToWorldMatrix()); } else { return controller::Pose(); // invalid pose @@ -2049,7 +2047,6 @@ controller::Pose MyAvatar::getControllerPoseInWorldFrame(controller::Action acti controller::Pose MyAvatar::getControllerPoseInAvatarFrame(controller::Action action) const { auto pose = getControllerPoseInWorldFrame(action); if (pose.valid) { - qCDebug(interfaceapp) << "avatar world orientation " << getWorldOrientation(); glm::mat4 invAvatarMatrix = glm::inverse(createMatFromQuatAndPos(getWorldOrientation(), getWorldPosition())); return pose.transform(invAvatarMatrix); } else { diff --git a/interface/src/avatar/MySkeletonModel.cpp b/interface/src/avatar/MySkeletonModel.cpp index d389d99e26..6026794cda 100644 --- a/interface/src/avatar/MySkeletonModel.cpp +++ b/interface/src/avatar/MySkeletonModel.cpp @@ -119,21 +119,7 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { // input action is the highest priority source for head orientation. auto avatarHeadPose = myAvatar->getControllerPoseInAvatarFrame(controller::Action::HEAD); if (avatarHeadPose.isValid()) { - AnimPose previousHeadPose; - bool headUnfuckedWith = _rig.getAbsoluteJointPoseInRigFrame(_rig.indexOfJoint("Head"), previousHeadPose); - if (headUnfuckedWith) { - qCDebug(interfaceapp) << "unset head position " << previousHeadPose.trans(); - qCDebug(interfaceapp) << "unset head rotation " << previousHeadPose.rot(); - } - qCDebug(interfaceapp) << "neck joint offset " << jointOffsetMap[62]; - qCDebug(interfaceapp) << "head joint avatar frame " << avatarHeadPose.getRotation(); - AnimPose jointOffsetNeck(jointOffsetMap[62], glm::vec3()); - AnimPose jointOffsetSpine2(jointOffsetMap[13], glm::vec3()); - AnimPose testPose(glm::quat(0.7071f, 0.0f, 0.0f, 0.7071f), glm::vec3()); AnimPose pose(avatarHeadPose.getRotation(), avatarHeadPose.getTranslation()); - AnimPose newHeadRot = (avatarToRigPose * pose) * testPose; - AnimPose newHeadRot2(newHeadRot.rot(), avatarHeadPose.getTranslation()); - AnimPose identityPose(glm::quat(1.0f,0.0f,0.0f,0.0f), glm::vec3(0.0f,0.57f,0.0f)); params.primaryControllerPoses[Rig::PrimaryControllerType_Head] = avatarToRigPose * pose; params.primaryControllerFlags[Rig::PrimaryControllerType_Head] = (uint8_t)Rig::ControllerFlags::Enabled; } else { @@ -244,7 +230,7 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { params.primaryControllerFlags[Rig::PrimaryControllerType_Hips] = (uint8_t)Rig::ControllerFlags::Enabled | (uint8_t)Rig::ControllerFlags::Estimated; // set spine2 if we have hand controllers - qCDebug(interfaceapp) << "spine 2 joint offset " << jointOffsetMap[13]; + //qCDebug(interfaceapp) << "spine 2 joint offset " << jointOffsetMap[13]; if (myAvatar->getControllerPoseInAvatarFrame(controller::Action::RIGHT_HAND).isValid() && myAvatar->getControllerPoseInAvatarFrame(controller::Action::LEFT_HAND).isValid() && !(params.primaryControllerFlags[Rig::PrimaryControllerType_Spine2] & (uint8_t)Rig::ControllerFlags::Enabled)) { @@ -255,6 +241,7 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { bool spine2Exists = _rig.getAbsoluteJointPoseInRigFrame(_rig.indexOfJoint("Spine2"), currentSpine2Pose); bool headExists = _rig.getAbsoluteJointPoseInRigFrame(_rig.indexOfJoint("Head"), currentHeadPose); bool hipsExists = _rig.getAbsoluteJointPoseInRigFrame(_rig.indexOfJoint("Hips"), currentHipsPose); + if (spine2Exists && headExists && hipsExists) { AnimPose rigSpaceYaw(myAvatar->getSpine2RotationRigSpace()); diff --git a/libraries/animation/src/AnimSkeleton.cpp b/libraries/animation/src/AnimSkeleton.cpp index 05953e2803..d2bed40a06 100644 --- a/libraries/animation/src/AnimSkeleton.cpp +++ b/libraries/animation/src/AnimSkeleton.cpp @@ -15,6 +15,7 @@ #include #include "AnimationLogging.h" +static bool notBound = true; AnimSkeleton::AnimSkeleton(const FBXGeometry& fbxGeometry, const QMap jointOffsets) { @@ -22,48 +23,46 @@ AnimSkeleton::AnimSkeleton(const FBXGeometry& fbxGeometry, const QMap joints; joints.reserve(fbxGeometry.joints.size()); - //_avatarTPoseOffsets.reserve(_jointsSize); for (auto& joint : fbxGeometry.joints) { joints.push_back(joint); - //_avatarTPoseOffsets.push_back(AnimPose(glm::quat(), glm::vec3())); } buildSkeletonFromJoints(joints, jointOffsets); // add offsets for spine2 and the neck - // _avatarTPoseOffsets[nameToJointIndex("Spine2")] = AnimPose(glm::quat(-0.707107f, 0.0f, 0.0f, 0.707107f), glm::vec3()); - // _avatarTPoseOffsets[nameToJointIndex("Neck")] = AnimPose(glm::quat(0.0f, 0.707107f, 0.0f, 0.707107f), glm::vec3()); - - for (int i = 0; i < (int)fbxGeometry.meshes.size(); i++) { - const FBXMesh& mesh = fbxGeometry.meshes.at(i); - for (int j = 0; j < mesh.clusters.size(); j++) { - - - // cast into a non-const reference, so we can mutate the FBXCluster - FBXCluster& cluster = const_cast(mesh.clusters.at(j)); + if (notBound) { + notBound = false; + for (int i = 0; i < (int)fbxGeometry.meshes.size(); i++) { + const FBXMesh& mesh = fbxGeometry.meshes.at(i); + for (int j = 0; j < mesh.clusters.size(); j++) { - // AJT: mutate bind pose! this allows us to oreint the skeleton back into the authored orientaiton before - // rendering, with no runtime overhead. - // this works if clusters match joints one for one. - - if (cluster.jointIndex == 62) { - qCDebug(animation) << "Neck"; - qCDebug(animation) << "found a joint offset to add " << cluster.jointIndex << " " << jointOffsets[cluster.jointIndex] << " cluster " << cluster.jointIndex; - AnimPose localOffset(jointOffsets[cluster.jointIndex], glm::vec3()); - cluster.inverseBindMatrix = (glm::mat4)localOffset.inverse() * cluster.inverseBindMatrix; - cluster.inverseBindTransform.evalFromRawMatrix(cluster.inverseBindMatrix); - } - if (cluster.jointIndex == 13) { - qCDebug(animation) << "Spine2"; - qCDebug(animation) << "found a joint offset to add " << cluster.jointIndex << " " << jointOffsets[cluster.jointIndex] << " cluster " << cluster.jointIndex; - AnimPose localOffset(jointOffsets[cluster.jointIndex], glm::vec3()); - cluster.inverseBindMatrix = (glm::mat4)localOffset.inverse() * cluster.inverseBindMatrix; - cluster.inverseBindTransform.evalFromRawMatrix(cluster.inverseBindMatrix); - } + // cast into a non-const reference, so we can mutate the FBXCluster + FBXCluster& cluster = const_cast(mesh.clusters.at(j)); + + // AJT: mutate bind pose! this allows us to oreint the skeleton back into the authored orientaiton before + // rendering, with no runtime overhead. + // this works if clusters match joints one for one. + + if (cluster.jointIndex == 62) { + qCDebug(animation) << "Neck"; + qCDebug(animation) << "found a joint offset to add " << cluster.jointIndex << " " << jointOffsets[cluster.jointIndex] << " cluster " << cluster.jointIndex; + AnimPose localOffset(jointOffsets[cluster.jointIndex], glm::vec3()); + cluster.inverseBindMatrix = (glm::mat4)localOffset.inverse() * cluster.inverseBindMatrix; + cluster.inverseBindTransform.evalFromRawMatrix(cluster.inverseBindMatrix); + } + if (cluster.jointIndex == 13) { + qCDebug(animation) << "Spine2"; + qCDebug(animation) << "found a joint offset to add " << cluster.jointIndex << " " << jointOffsets[cluster.jointIndex] << " cluster " << cluster.jointIndex; + AnimPose localOffset(jointOffsets[cluster.jointIndex], glm::vec3()); + cluster.inverseBindMatrix = (glm::mat4)localOffset.inverse() * cluster.inverseBindMatrix; + cluster.inverseBindTransform.evalFromRawMatrix(cluster.inverseBindMatrix); + } + + } } + } - } AnimSkeleton::AnimSkeleton(const std::vector& joints, const QMap jointOffsets) { @@ -232,6 +231,12 @@ void AnimSkeleton::buildSkeletonFromJoints(const std::vector& joints, qCDebug(animation) << "relative default pose for joint " << i << " " << relDefaultPose.trans() << " " << relDefaultPose.rot(); int parentIndex = getParentIndex(i); + AnimPose newAbsPose; + if (parentIndex >= 0) { + newAbsPose = _absoluteDefaultPoses[parentIndex] * AnimPose(relDefaultPose.rot(),glm::vec3()); + } else { + newAbsPose = relDefaultPose; + } // putting the pipeline code is // remember the inverse bind pose already has the offset added into it. the total effect is offset^-1 * relDefPose * offset. @@ -242,20 +247,21 @@ void AnimSkeleton::buildSkeletonFromJoints(const std::vector& joints, if (jointOffsets.contains(i)) { //QString parentIndex = getJointName(parentIndex); AnimPose localOffset(jointOffsets[i], glm::vec3()); - relDefaultPose = relDefaultPose * localOffset; + newAbsPose = newAbsPose * localOffset; } if ((parentIndex >= 0) && jointOffsets.contains(parentIndex)) { AnimPose localParentOffset(jointOffsets[parentIndex], glm::vec3()); - relDefaultPose = localParentOffset.inverse() * AnimPose(glm::quat(), relDefaultPose.trans()) * localParentOffset * AnimPose(relDefaultPose.rot(), glm::vec3()); + newAbsPose = localParentOffset.inverse() * AnimPose(glm::quat(), relDefaultPose.trans()) * localParentOffset * AnimPose(newAbsPose.rot(), glm::vec3()); } - _relativeDefaultPoses.push_back(relDefaultPose); - if (parentIndex >= 0) { - _absoluteDefaultPoses.push_back(_absoluteDefaultPoses[parentIndex] * relDefaultPose); - } else { - _absoluteDefaultPoses.push_back(relDefaultPose); + relDefaultPose = _absoluteDefaultPoses[parentIndex].inverse() * newAbsPose; } + _relativeDefaultPoses.push_back(relDefaultPose); + + + _absoluteDefaultPoses.push_back(newAbsPose); + } for (int i = 0; i < _jointsSize; i++) { diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index a73c945124..bdbbd02706 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -2084,6 +2084,8 @@ void Rig::setJointRotationOffsets(const QMap& offsets) { _jointRotationOffsets.insert(spine2Id, glm::quat(0.5f, 0.5f, 0.5f, -0.5f)); } qCDebug(animation) << "set the neck and spine2 offsets " << spine2Id << " " << neckId; + glm::quat testRot(glm::vec3(0.000018095f, -4.74360667058f, -89.9994155926f)); + qCDebug(animation) << "test rot from euler" << testRot; } const QMap& Rig::getJointRotationOffsets() const { From 8d4d0beebd9138f8256fd63903d4290341c6f1ee Mon Sep 17 00:00:00 2001 From: NissimHadar Date: Thu, 8 Nov 2018 17:52:07 -0800 Subject: [PATCH 174/286] Cleanup --- interface/src/Application.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 5844462497..368f4d4d9a 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -4834,6 +4834,7 @@ void Application::idle() { _idleLoopStdev.reset(); } } + _overlayConductor.update(secondsSinceLastUpdate); _gameLoopCounter.increment(); From d183968175a98782385a511ebd0a17791943a6c4 Mon Sep 17 00:00:00 2001 From: Simon Walton Date: Thu, 8 Nov 2018 18:20:20 -0800 Subject: [PATCH 175/286] Add skipThoseWithBadParents functionality to octree visitor; reviewer suggestions --- .../src/RecurseOctreeToJSONOperator.cpp | 18 ++++++++----- .../src/RecurseOctreeToJSONOperator.h | 6 +++-- libraries/octree/src/Octree.cpp | 26 ------------------- libraries/octree/src/OctreeDataUtils.cpp | 2 +- libraries/octree/src/OctreeDataUtils.h | 2 +- .../octree/src/OctreeEntitiesFileParser.cpp | 5 +--- .../octree/src/OctreeEntitiesFileParser.h | 1 - 7 files changed, 18 insertions(+), 42 deletions(-) diff --git a/libraries/entities/src/RecurseOctreeToJSONOperator.cpp b/libraries/entities/src/RecurseOctreeToJSONOperator.cpp index a865009566..b64a700abc 100644 --- a/libraries/entities/src/RecurseOctreeToJSONOperator.cpp +++ b/libraries/entities/src/RecurseOctreeToJSONOperator.cpp @@ -13,10 +13,11 @@ #include "EntityItemProperties.h" RecurseOctreeToJSONOperator::RecurseOctreeToJSONOperator(const OctreeElementPointer&, QScriptEngine* engine, - QString jsonPrefix /* = QString() */, bool skipDefaults /* = true */) - : _engine(engine) - , _json(jsonPrefix) - , _skipDefaults(skipDefaults) + QString jsonPrefix, bool skipDefaults, bool skipThoseWithBadParents): + _engine(engine), + _json(jsonPrefix), + _skipDefaults(skipDefaults), + _skipThoseWithBadParents(skipThoseWithBadParents) { _toStringMethod = _engine->evaluate("(function() { return JSON.stringify(this, null, ' ') })"); } @@ -29,18 +30,21 @@ bool RecurseOctreeToJSONOperator::postRecursion(const OctreeElementPointer& elem } void RecurseOctreeToJSONOperator::processEntity(const EntityItemPointer& entity) { + if (_skipThoseWithBadParents && !entity->isParentIDValid()) { + return; // we weren't able to resolve a parent from _parentID, so don't save this entity. + } + QScriptValue qScriptValues = _skipDefaults ? EntityItemNonDefaultPropertiesToScriptValue(_engine, entity->getProperties()) : EntityItemPropertiesToScriptValue(_engine, entity->getProperties()); - if (comma) { + if (_comma) { _json += ','; }; - comma = true; + _comma = true; _json += "\n "; // Override default toString(): qScriptValues.setProperty("toString", _toStringMethod); _json += qScriptValues.toString(); - //auto exceptionString2 = _engine->uncaughtException().toString(); } diff --git a/libraries/entities/src/RecurseOctreeToJSONOperator.h b/libraries/entities/src/RecurseOctreeToJSONOperator.h index d0f03d02eb..a1d388ed22 100644 --- a/libraries/entities/src/RecurseOctreeToJSONOperator.h +++ b/libraries/entities/src/RecurseOctreeToJSONOperator.h @@ -13,7 +13,8 @@ class RecurseOctreeToJSONOperator : public RecurseOctreeOperator { public: - RecurseOctreeToJSONOperator(const OctreeElementPointer&, QScriptEngine* engine, QString jsonPrefix = QString(), bool skipDefaults = true); + RecurseOctreeToJSONOperator(const OctreeElementPointer&, QScriptEngine* engine, QString jsonPrefix = QString(), bool skipDefaults = true, + bool skipThoseWithBadParents = false); virtual bool preRecursion(const OctreeElementPointer& element) override { return true; }; virtual bool postRecursion(const OctreeElementPointer& element) override; @@ -27,5 +28,6 @@ private: QString _json; const bool _skipDefaults; - bool comma { false }; + bool _skipThoseWithBadParents; + bool _comma { false }; }; diff --git a/libraries/octree/src/Octree.cpp b/libraries/octree/src/Octree.cpp index bbf1e44d08..cfa207dbf8 100644 --- a/libraries/octree/src/Octree.cpp +++ b/libraries/octree/src/Octree.cpp @@ -925,9 +925,6 @@ bool Octree::toJSONString(QString& jsonString, const OctreeElementPointer& eleme } bool Octree::toJSON(QByteArray* data, const OctreeElementPointer& element, bool doGzip) { -#define HIFI_USE_DIRECT_TO_JSON -#ifdef HIFI_USE_DIRECT_TO_JSON - QString jsonString; toJSONString(jsonString); @@ -940,29 +937,6 @@ bool Octree::toJSON(QByteArray* data, const OctreeElementPointer& element, bool *data = jsonString.toUtf8(); } -#else - - QJsonDocument doc; - if (!toJSONDocument(&doc, element)) { - qCritical("Failed to convert Entities to JSON document."); - return false; - } - - QString jsonString; - toJSONString(jsonString); - - if (doGzip) { - QByteArray jsonData = doc.toJson(); - - if (!gzip(jsonData, *data, -1)) { - qCritical("Unable to gzip data while saving to json."); - return false; - } - } else { - *data = doc.toJson(); - } - -#endif // HIFI_USE_DIRECT_TO_JSON return true; } diff --git a/libraries/octree/src/OctreeDataUtils.cpp b/libraries/octree/src/OctreeDataUtils.cpp index 7bbad652f8..a94fa31800 100644 --- a/libraries/octree/src/OctreeDataUtils.cpp +++ b/libraries/octree/src/OctreeDataUtils.cpp @@ -56,7 +56,7 @@ bool OctreeUtils::RawOctreeData::readOctreeDataInfoFromJSON(QJsonObject root) { return true; } -bool OctreeUtils::RawOctreeData::readOctreeDataInfoFromMap(QVariantMap map) { +bool OctreeUtils::RawOctreeData::readOctreeDataInfoFromMap(const QVariantMap& map) { if (map.contains("Id") && map.contains("DataVersion") && map.contains("Version")) { id = map["Id"].toUuid(); dataVersion = map["DataVersion"].toInt(); diff --git a/libraries/octree/src/OctreeDataUtils.h b/libraries/octree/src/OctreeDataUtils.h index 236d280bbf..0dfa99a9a0 100644 --- a/libraries/octree/src/OctreeDataUtils.h +++ b/libraries/octree/src/OctreeDataUtils.h @@ -43,7 +43,7 @@ public: bool readOctreeDataInfoFromData(QByteArray data); bool readOctreeDataInfoFromFile(QString path); bool readOctreeDataInfoFromJSON(QJsonObject root); - bool readOctreeDataInfoFromMap(QVariantMap map); + bool readOctreeDataInfoFromMap(const QVariantMap& map); }; class RawEntityData : public RawOctreeData { diff --git a/libraries/octree/src/OctreeEntitiesFileParser.cpp b/libraries/octree/src/OctreeEntitiesFileParser.cpp index 0b75991fd2..72853947c6 100644 --- a/libraries/octree/src/OctreeEntitiesFileParser.cpp +++ b/libraries/octree/src/OctreeEntitiesFileParser.cpp @@ -19,9 +19,6 @@ using std::string; -OctreeEntitiesFileParser::OctreeEntitiesFileParser() { -} - std::string OctreeEntitiesFileParser::getErrorString() const { std::ostringstream err; if (_errorString.size() != 0) { @@ -86,7 +83,7 @@ bool OctreeEntitiesFileParser::parseEntities(QVariantMap& parsedEntities) { return false; } - parsedEntities["Entities"] = entitiesValue; + parsedEntities["Entities"] = std::move(entitiesValue); gotEntities = true; } else if (key == "Id") { if (gotId) { diff --git a/libraries/octree/src/OctreeEntitiesFileParser.h b/libraries/octree/src/OctreeEntitiesFileParser.h index e4e82f0e66..bc51896b18 100644 --- a/libraries/octree/src/OctreeEntitiesFileParser.h +++ b/libraries/octree/src/OctreeEntitiesFileParser.h @@ -19,7 +19,6 @@ class OctreeEntitiesFileParser { public: - OctreeEntitiesFileParser(); void setEntitiesString(const QByteArray& entitiesContents); bool parseEntities(QVariantMap& parsedEntities); std::string getErrorString() const; From 244b768b9ae279e978e14768bb8963e60dc1513d Mon Sep 17 00:00:00 2001 From: Angus Antley Date: Thu, 8 Nov 2018 23:42:50 -0800 Subject: [PATCH 176/286] fixed the absolute default joint poses so they work for the 2 joint avatar --- interface/src/avatar/MyAvatar.cpp | 2 +- libraries/animation/src/AnimSkeleton.cpp | 38 ++++++++-- libraries/animation/src/Rig.cpp | 79 ++++++++++++++++++--- tools/skeleton-dump/src/SkeletonDumpApp.cpp | 11 ++- 4 files changed, 109 insertions(+), 21 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 8d47591d40..502be9d57c 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -431,7 +431,7 @@ void MyAvatar::reset(bool andRecenter, bool andReload, bool andHead) { _wasPushing = _isPushing = _isBraking = false; _follow.deactivate(); if (andReload) { - _skeletonModel->reset(); + //_skeletonModel->reset(); } if (andHead) { // which drives camera in desktop getHead()->reset(); diff --git a/libraries/animation/src/AnimSkeleton.cpp b/libraries/animation/src/AnimSkeleton.cpp index 03eac7de49..af0ec0d5b0 100644 --- a/libraries/animation/src/AnimSkeleton.cpp +++ b/libraries/animation/src/AnimSkeleton.cpp @@ -28,8 +28,8 @@ AnimSkeleton::AnimSkeleton(const HFMModel& hfmModel, const QMap buildSkeletonFromJoints(joints, jointOffsets); // add offsets for spine2 and the neck - if (notBound) { - notBound = false; + + for (int i = 0; i < (int)hfmModel.meshes.size(); i++) { const HFMMesh& mesh = hfmModel.meshes.at(i); for (int j = 0; j < mesh.clusters.size(); j++) { @@ -59,7 +59,7 @@ AnimSkeleton::AnimSkeleton(const HFMModel& hfmModel, const QMap } } - } + } @@ -230,6 +230,34 @@ void AnimSkeleton::mirrorAbsolutePoses(AnimPoseVec& poses) const { qCDebug(animation) << "relative default pose for joint " << i << " " << relDefaultPose.trans() << " " << relDefaultPose.rot(); int parentIndex = getParentIndex(i); + + AnimPose newAbsPose; + if (parentIndex >= 0) { + newAbsPose = _absoluteDefaultPoses[parentIndex] * relDefaultPose; + + _absoluteDefaultPoses.push_back(newAbsPose); + } else { + + _absoluteDefaultPoses.push_back(relDefaultPose); + } + + } + for (int k = 0; k < _jointsSize; k++) { + int parentIndex2 = getParentIndex(k); + if (jointOffsets.contains(k)) { + AnimPose localOffset(jointOffsets[k], glm::vec3()); + _absoluteDefaultPoses[k] = _absoluteDefaultPoses[k] * localOffset; + } + if (parentIndex2 >= 0) { + + _relativeDefaultPoses.push_back(_absoluteDefaultPoses[parentIndex2].inverse() * _absoluteDefaultPoses[k]); + } else { + + _relativeDefaultPoses.push_back(_absoluteDefaultPoses[k]); + } + } + + /* AnimPose newAbsPose; if (parentIndex >= 0) { newAbsPose = _absoluteDefaultPoses[parentIndex] * AnimPose(relDefaultPose.rot(),glm::vec3()); @@ -260,9 +288,9 @@ void AnimSkeleton::mirrorAbsolutePoses(AnimPoseVec& poses) const { _absoluteDefaultPoses.push_back(newAbsPose); - + } - + */ for (int i = 0; i < _jointsSize; i++) { _jointIndicesByName[_joints[i].name] = i; } diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 6f1e37d9ff..28ef0aef5c 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -310,11 +310,9 @@ void Rig::initJointStates(const HFMModel& hfmModel, const glm::mat4& modelOffset void Rig::reset(const HFMModel& hfmModel) { _geometryOffset = AnimPose(hfmModel.offset); _invGeometryOffset = _geometryOffset.inverse(); -<<<<<<< HEAD - _animSkeleton = std::make_shared(geometry, _jointRotationOffsets); -======= - _animSkeleton = std::make_shared(hfmModel); ->>>>>>> upstream/master + + _animSkeleton = std::make_shared(hfmModel, _jointRotationOffsets); + _internalPoseSet._relativePoses.clear(); _internalPoseSet._relativePoses = _animSkeleton->getRelativeDefaultPoses(); @@ -2087,9 +2085,74 @@ void Rig::setJointRotationOffsets(const QMap& offsets) { if (true){ //spine2Index != -1) { _jointRotationOffsets.insert(spine2Id, glm::quat(0.5f, 0.5f, 0.5f, -0.5f)); } - qCDebug(animation) << "set the neck and spine2 offsets " << spine2Id << " " << neckId; - glm::quat testRot(glm::vec3(0.000018095f, -4.74360667058f, -89.9994155926f)); - qCDebug(animation) << "test rot from euler" << testRot; + + std::vector eulersFromEngineer; + eulersFromEngineer.push_back(glm::vec3(-81.1050620752f, -20.1443891394f, 100.309499473f)); + eulersFromEngineer.push_back(glm::vec3(260.889773298f, -20.143735467f, 79.7184972987f)); + eulersFromEngineer.push_back(glm::vec3(-196.885439853f, -15.4618394328f, 107.273377257f)); + eulersFromEngineer.push_back(glm::vec3(185.169288353f, -14.3865564204f, 73.5514142013f)); + eulersFromEngineer.push_back(glm::vec3(85.0436206363f, -0.405424354654f, 279.126747746f)); + eulersFromEngineer.push_back(glm::vec3(265.040827938f, -24.8809133357f, 80.8874530433f)); + eulersFromEngineer.push_back(glm::vec3(0.0000012746f, -9.5408567955f, 89.9999827841f)); + eulersFromEngineer.push_back(glm::vec3(-304.676178613f, -1.77806457464f, 94.6267844984f)); + eulersFromEngineer.push_back(glm::vec3(299.239708621f, -1.75410019523f, 85.3712190608f)); + eulersFromEngineer.push_back(glm::vec3(-70.5442845855f, 180.00795643f, -73.899282669f)); + eulersFromEngineer.push_back(glm::vec3(-5.65981473995f, -0.554837824572f, 90.3818083467f)); + eulersFromEngineer.push_back(glm::vec3(282.516400893f, -2.86457680169f, 106.966109268f)); + eulersFromEngineer.push_back(glm::vec3(-5.74446362761f, -3.69252247483f, 89.4088276443f)); + eulersFromEngineer.push_back(glm::vec3(175.098015269f, 11.7273076323f, 282.707060357f)); + eulersFromEngineer.push_back(glm::vec3(-5.86440719705f, -11.5976507143f, 93.7305032918f)); + eulersFromEngineer.push_back(glm::vec3(212.162310762f, 11.1889030401f, 90.489887929f)); + eulersFromEngineer.push_back(glm::vec3(-183.528298852f, 11.1630787238f, 89.5119846423f)); + eulersFromEngineer.push_back(glm::vec3(-21.5907187136f, -1.16096242449f, 93.4017341288f)); + eulersFromEngineer.push_back(glm::vec3(-230.174545226f, -1.16165879865f, 86.5984269351f)); + eulersFromEngineer.push_back(glm::vec3(-71.3250132734f, 179.345867731f, -73.9245897857f)); + eulersFromEngineer.push_back(glm::vec3(4.4183033408f, 0.0214780500113f, 89.6156394563f)); + eulersFromEngineer.push_back(glm::vec3(-71.3557521294f, -1.96233777224f, 101.311991763f)); + eulersFromEngineer.push_back(glm::vec3(4.52300317986f, -3.2744480538f, 93.9297689938f)); + eulersFromEngineer.push_back(glm::vec3(183.372659045f, 18.4240672177f, 289.089833888f)); + eulersFromEngineer.push_back(glm::vec3(4.69575782766f, -18.4271517191f, 85.8795582505f)); + eulersFromEngineer.push_back(glm::vec3(180.000000381f, 184.227605816f, -90.0000102753f)); + eulersFromEngineer.push_back(glm::vec3(180.000018095f, -4.74360667058f, -89.9994155926f)); + eulersFromEngineer.push_back(glm::vec3(-6.65059725988f, 179.738939306f, -74.3826161322f)); + eulersFromEngineer.push_back(glm::vec3(1.39276173327f, 0.376166992987f, 92.9332563713f)); + eulersFromEngineer.push_back(glm::vec3(290.266370725f, -2.98535083963f, 105.944099833f)); + eulersFromEngineer.push_back(glm::vec3(1.43200712942f, -2.97286285904f, 93.1492284976f)); + eulersFromEngineer.push_back(glm::vec3(194.564263389f, 20.3832505773f, 294.645037769f)); + eulersFromEngineer.push_back(glm::vec3(1.63629620002f, -20.9266621269f, 88.1506158063f)); + eulersFromEngineer.push_back(glm::vec3(-0.0431167069757f, 179.97446471f, -73.261703364f)); + eulersFromEngineer.push_back(glm::vec3(7.60338271732f, -0.342204658844f, 91.3116554499f)); + eulersFromEngineer.push_back(glm::vec3(279.206234833f, -3.4007859075f, 106.246009192f)); + eulersFromEngineer.push_back(glm::vec3(7.72548545025f, -3.33436960581f, 92.0998444782f)); + eulersFromEngineer.push_back(glm::vec3(197.45405684f, 22.6995116972f, 295.695981195f)); + eulersFromEngineer.push_back(glm::vec3(8.21527813645f, -19.794697098f, 86.2545417524f)); + eulersFromEngineer.push_back(glm::vec3(180.000036925f, 168.400465599f, -90.0003196145f)); + eulersFromEngineer.push_back(glm::vec3(7.86635710548e-05f, -2.45210839758f, 89.9992192762f)); + eulersFromEngineer.push_back(glm::vec3(179.999920323f, 4.95709895474f, 269.999215822f)); + eulersFromEngineer.push_back(glm::vec3(241.077352225f, -184.455008741f, -426.541304554f)); + eulersFromEngineer.push_back(glm::vec3(-70.6642196779f, 6.88023375774f, 85.5052784744f)); + eulersFromEngineer.push_back(glm::vec3(51.5850104459f, 4.10357301154f, 96.1370118091f)); + eulersFromEngineer.push_back(glm::vec3(-70.3665845407f, 5.50499763056f, 102.700969646f)); + eulersFromEngineer.push_back(glm::vec3(94.1058470618f, -4.50566341059f, -63.663409373f)); + eulersFromEngineer.push_back(glm::vec3(-70.8795528982f, 7.23642133863f, 82.4215992116f)); + eulersFromEngineer.push_back(glm::vec3(354.986036508f, -0.167802950666f, -73.0700479208f)); + eulersFromEngineer.push_back(glm::vec3(174.972844269f, 0.163203627576f, 73.0320316709f)); + eulersFromEngineer.push_back(glm::vec3(-156.40949683f, -2.25584901219f, 89.478726037f)); + eulersFromEngineer.push_back(glm::vec3(-147.791270237f, -2.25609369727f, 90.5216948763f)); + + qCDebug(animation) << "list of joint quats for engineer"; + for (glm::vec3 bone : eulersFromEngineer) { + glm::quat boneQuat(glm::vec3((bone.x / 180.0f)*PI, (bone.y / 180.0f)*PI, (bone.z / 180.0f)*PI)); + qCDebug(animation) << boneQuat; + } + + + + //qCDebug(animation) << "set the neck and spine2 offsets " << spine2Id << " " << neckId; + //glm::quat leftShoulder(glm::vec3((-81.1050620752f / 180.0f)*PI, (-20.1443891394f / 180.0f)*PI, (100.309499473f / 180.0f)*PI)); + //qCDebug(animation) << "jointRotationOffset = LeftShoulder = (" << leftShoulder << ")"; + //glm::quat testRot2(glm::vec3(PI / 2.0f, 0.0f, PI / 2.0f)); + //qCDebug(animation) << "test rot2 from euler" << testRot2; } const QMap& Rig::getJointRotationOffsets() const { diff --git a/tools/skeleton-dump/src/SkeletonDumpApp.cpp b/tools/skeleton-dump/src/SkeletonDumpApp.cpp index b818ae5da1..d1520d81f9 100644 --- a/tools/skeleton-dump/src/SkeletonDumpApp.cpp +++ b/tools/skeleton-dump/src/SkeletonDumpApp.cpp @@ -54,14 +54,11 @@ SkeletonDumpApp::SkeletonDumpApp(int argc, char* argv[]) : QCoreApplication(argc return; } QByteArray blob = file.readAll(); -<<<<<<< HEAD - std::unique_ptr fbxGeometry(readFBX(blob, QVariantHash())); - QMap jointRotationOffsets; - std::unique_ptr skeleton(new AnimSkeleton(*fbxGeometry, jointRotationOffsets)); -======= + std::unique_ptr geometry(readFBX(blob, QVariantHash())); - std::unique_ptr skeleton(new AnimSkeleton(*geometry)); ->>>>>>> upstream/master + QMap jointRotationOffsets; + std::unique_ptr skeleton(new AnimSkeleton(*geometry, jointRotationOffsets)); + skeleton->dump(verbose); } From e36ab9efd682bc2baf3557ebfb24f0303b09fd4d Mon Sep 17 00:00:00 2001 From: Angus Antley Date: Fri, 9 Nov 2018 00:44:52 -0800 Subject: [PATCH 177/286] cleaned up for merge with luis --- .../src/avatars/ScriptableAvatar.cpp | 3 +- interface/src/avatar/MySkeletonModel.cpp | 1 - libraries/animation/src/AnimClip.cpp | 3 +- libraries/animation/src/AnimSkeleton.cpp | 6 +- libraries/animation/src/Rig.cpp | 98 +------------------ libraries/animation/src/Rig.h | 6 -- .../src/avatars-renderer/SkeletonModel.cpp | 3 - libraries/fbx/src/FSTReader.cpp | 24 ----- libraries/fbx/src/FSTReader.h | 3 - tools/skeleton-dump/src/SkeletonDumpApp.cpp | 3 +- 10 files changed, 8 insertions(+), 142 deletions(-) diff --git a/assignment-client/src/avatars/ScriptableAvatar.cpp b/assignment-client/src/avatars/ScriptableAvatar.cpp index a211fe1bbe..51038a782f 100644 --- a/assignment-client/src/avatars/ScriptableAvatar.cpp +++ b/assignment-client/src/avatars/ScriptableAvatar.cpp @@ -84,8 +84,7 @@ void ScriptableAvatar::update(float deltatime) { // Run animation if (_animation && _animation->isLoaded() && _animation->getFrames().size() > 0 && !_bind.isNull() && _bind->isLoaded()) { if (!_animSkeleton) { - QMap jointRotationOffsets; - _animSkeleton = std::make_shared(_bind->getHFMModel(), jointRotationOffsets); + _animSkeleton = std::make_shared(_bind->getHFMModel()); } float currentFrame = _animationDetails.currentFrame + deltatime * _animationDetails.fps; if (_animationDetails.loop || currentFrame < _animationDetails.lastFrame) { diff --git a/interface/src/avatar/MySkeletonModel.cpp b/interface/src/avatar/MySkeletonModel.cpp index a5089dc0ea..720bbd1066 100644 --- a/interface/src/avatar/MySkeletonModel.cpp +++ b/interface/src/avatar/MySkeletonModel.cpp @@ -92,7 +92,6 @@ static AnimPose computeHipsInSensorFrame(MyAvatar* myAvatar, bool isFlying) { void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { const HFMModel& hfmModel = getHFMModel(); - const QMap jointOffsetMap = _rig.getJointRotationOffsets(); Head* head = _owningAvatar->getHead(); diff --git a/libraries/animation/src/AnimClip.cpp b/libraries/animation/src/AnimClip.cpp index 4900f414b7..a3d55726d6 100644 --- a/libraries/animation/src/AnimClip.cpp +++ b/libraries/animation/src/AnimClip.cpp @@ -103,8 +103,7 @@ void AnimClip::copyFromNetworkAnim() { // by matching joints with the same name. const HFMModel& hfmModel = _networkAnim->getHFMModel(); - QMap jointRotationOffsets; - AnimSkeleton animSkeleton(hfmModel, jointRotationOffsets); + AnimSkeleton animSkeleton(hfmModel); const auto animJointCount = animSkeleton.getNumJoints(); const auto skeletonJointCount = _skeleton->getNumJoints(); diff --git a/libraries/animation/src/AnimSkeleton.cpp b/libraries/animation/src/AnimSkeleton.cpp index af0ec0d5b0..f0219eb946 100644 --- a/libraries/animation/src/AnimSkeleton.cpp +++ b/libraries/animation/src/AnimSkeleton.cpp @@ -17,7 +17,7 @@ #include "AnimationLogging.h" static bool notBound = true; -AnimSkeleton::AnimSkeleton(const HFMModel& hfmModel, const QMap jointOffsets) { +AnimSkeleton::AnimSkeleton(const HFMModel& hfmModel) { qCDebug(animation) << "in the animSkeleton"; // convert to std::vector of joints std::vector joints; @@ -26,7 +26,7 @@ AnimSkeleton::AnimSkeleton(const HFMModel& hfmModel, const QMap joints.push_back(joint); } - buildSkeletonFromJoints(joints, jointOffsets); + buildSkeletonFromJoints(joints, hfmModel.jointOffsets); // add offsets for spine2 and the neck @@ -203,7 +203,7 @@ void AnimSkeleton::mirrorAbsolutePoses(AnimPoseVec& poses) const { } } - void AnimSkeleton::buildSkeletonFromJoints(const std::vector& joints, const QMap jointOffsets) { +void AnimSkeleton::buildSkeletonFromJoints(const std::vector& joints, const QMap jointOffsets) { _joints = joints; _jointsSize = (int)joints.size(); diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 28ef0aef5c..85a19f6cfe 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -267,7 +267,7 @@ void Rig::initJointStates(const HFMModel& hfmModel, const glm::mat4& modelOffset _rigToGeometryTransform = glm::inverse(_geometryToRigTransform); setModelOffset(modelOffset); - _animSkeleton = std::make_shared(hfmModel, _jointRotationOffsets); + _animSkeleton = std::make_shared(hfmModel); _internalPoseSet._relativePoses.clear(); _internalPoseSet._relativePoses = _animSkeleton->getRelativeDefaultPoses(); @@ -311,7 +311,7 @@ void Rig::reset(const HFMModel& hfmModel) { _geometryOffset = AnimPose(hfmModel.offset); _invGeometryOffset = _geometryOffset.inverse(); - _animSkeleton = std::make_shared(hfmModel, _jointRotationOffsets); + _animSkeleton = std::make_shared(hfmModel); _internalPoseSet._relativePoses.clear(); @@ -2064,97 +2064,3 @@ void Rig::computeAvatarBoundingCapsule( glm::vec3 capsuleCenter = transformPoint(_geometryToRigTransform, (0.5f * (totalExtents.maximum + totalExtents.minimum))); localOffsetOut = capsuleCenter - hipsPosition; } - -void Rig::setJointRotationOffsets(const QMap& offsets) { - _jointRotationOffsets.clear(); - for (auto itr = offsets.begin(); itr != offsets.end(); itr++) { - QString jointName = itr.key(); - glm::quat rotationOffset = itr.value(); - int jointIndex = indexOfJoint(jointName); - if (jointIndex != -1) { - _jointRotationOffsets.insert(jointIndex, rotationOffset); - } - qDebug() << "Joint Rotation Offset added to Rig._jointRotationOffsets : " << " jointName: " << jointName << " jointIndex: " << jointIndex << " rotation offset: " << rotationOffset; - } - int neckId = 62; - int spine2Id = 13; - if (true){ //neckIndex != -1) { - _jointRotationOffsets.insert(neckId, glm::quat(0.0f, 0.7071f, 0.7071f, 0.0f) * glm::quat(0.5f, 0.5f, 0.5f, -0.5f) ); //glm::quat(0.7071f, 0.0f, -0.7071f, 0.0f) - qCDebug(animation) << "multiplied quats are " << glm::quat(0.7071f, 0.0f, -0.7071f, 0.0f) * glm::quat(0.5f, 0.5f, 0.5f, -0.5f); - } - if (true){ //spine2Index != -1) { - _jointRotationOffsets.insert(spine2Id, glm::quat(0.5f, 0.5f, 0.5f, -0.5f)); - } - - std::vector eulersFromEngineer; - eulersFromEngineer.push_back(glm::vec3(-81.1050620752f, -20.1443891394f, 100.309499473f)); - eulersFromEngineer.push_back(glm::vec3(260.889773298f, -20.143735467f, 79.7184972987f)); - eulersFromEngineer.push_back(glm::vec3(-196.885439853f, -15.4618394328f, 107.273377257f)); - eulersFromEngineer.push_back(glm::vec3(185.169288353f, -14.3865564204f, 73.5514142013f)); - eulersFromEngineer.push_back(glm::vec3(85.0436206363f, -0.405424354654f, 279.126747746f)); - eulersFromEngineer.push_back(glm::vec3(265.040827938f, -24.8809133357f, 80.8874530433f)); - eulersFromEngineer.push_back(glm::vec3(0.0000012746f, -9.5408567955f, 89.9999827841f)); - eulersFromEngineer.push_back(glm::vec3(-304.676178613f, -1.77806457464f, 94.6267844984f)); - eulersFromEngineer.push_back(glm::vec3(299.239708621f, -1.75410019523f, 85.3712190608f)); - eulersFromEngineer.push_back(glm::vec3(-70.5442845855f, 180.00795643f, -73.899282669f)); - eulersFromEngineer.push_back(glm::vec3(-5.65981473995f, -0.554837824572f, 90.3818083467f)); - eulersFromEngineer.push_back(glm::vec3(282.516400893f, -2.86457680169f, 106.966109268f)); - eulersFromEngineer.push_back(glm::vec3(-5.74446362761f, -3.69252247483f, 89.4088276443f)); - eulersFromEngineer.push_back(glm::vec3(175.098015269f, 11.7273076323f, 282.707060357f)); - eulersFromEngineer.push_back(glm::vec3(-5.86440719705f, -11.5976507143f, 93.7305032918f)); - eulersFromEngineer.push_back(glm::vec3(212.162310762f, 11.1889030401f, 90.489887929f)); - eulersFromEngineer.push_back(glm::vec3(-183.528298852f, 11.1630787238f, 89.5119846423f)); - eulersFromEngineer.push_back(glm::vec3(-21.5907187136f, -1.16096242449f, 93.4017341288f)); - eulersFromEngineer.push_back(glm::vec3(-230.174545226f, -1.16165879865f, 86.5984269351f)); - eulersFromEngineer.push_back(glm::vec3(-71.3250132734f, 179.345867731f, -73.9245897857f)); - eulersFromEngineer.push_back(glm::vec3(4.4183033408f, 0.0214780500113f, 89.6156394563f)); - eulersFromEngineer.push_back(glm::vec3(-71.3557521294f, -1.96233777224f, 101.311991763f)); - eulersFromEngineer.push_back(glm::vec3(4.52300317986f, -3.2744480538f, 93.9297689938f)); - eulersFromEngineer.push_back(glm::vec3(183.372659045f, 18.4240672177f, 289.089833888f)); - eulersFromEngineer.push_back(glm::vec3(4.69575782766f, -18.4271517191f, 85.8795582505f)); - eulersFromEngineer.push_back(glm::vec3(180.000000381f, 184.227605816f, -90.0000102753f)); - eulersFromEngineer.push_back(glm::vec3(180.000018095f, -4.74360667058f, -89.9994155926f)); - eulersFromEngineer.push_back(glm::vec3(-6.65059725988f, 179.738939306f, -74.3826161322f)); - eulersFromEngineer.push_back(glm::vec3(1.39276173327f, 0.376166992987f, 92.9332563713f)); - eulersFromEngineer.push_back(glm::vec3(290.266370725f, -2.98535083963f, 105.944099833f)); - eulersFromEngineer.push_back(glm::vec3(1.43200712942f, -2.97286285904f, 93.1492284976f)); - eulersFromEngineer.push_back(glm::vec3(194.564263389f, 20.3832505773f, 294.645037769f)); - eulersFromEngineer.push_back(glm::vec3(1.63629620002f, -20.9266621269f, 88.1506158063f)); - eulersFromEngineer.push_back(glm::vec3(-0.0431167069757f, 179.97446471f, -73.261703364f)); - eulersFromEngineer.push_back(glm::vec3(7.60338271732f, -0.342204658844f, 91.3116554499f)); - eulersFromEngineer.push_back(glm::vec3(279.206234833f, -3.4007859075f, 106.246009192f)); - eulersFromEngineer.push_back(glm::vec3(7.72548545025f, -3.33436960581f, 92.0998444782f)); - eulersFromEngineer.push_back(glm::vec3(197.45405684f, 22.6995116972f, 295.695981195f)); - eulersFromEngineer.push_back(glm::vec3(8.21527813645f, -19.794697098f, 86.2545417524f)); - eulersFromEngineer.push_back(glm::vec3(180.000036925f, 168.400465599f, -90.0003196145f)); - eulersFromEngineer.push_back(glm::vec3(7.86635710548e-05f, -2.45210839758f, 89.9992192762f)); - eulersFromEngineer.push_back(glm::vec3(179.999920323f, 4.95709895474f, 269.999215822f)); - eulersFromEngineer.push_back(glm::vec3(241.077352225f, -184.455008741f, -426.541304554f)); - eulersFromEngineer.push_back(glm::vec3(-70.6642196779f, 6.88023375774f, 85.5052784744f)); - eulersFromEngineer.push_back(glm::vec3(51.5850104459f, 4.10357301154f, 96.1370118091f)); - eulersFromEngineer.push_back(glm::vec3(-70.3665845407f, 5.50499763056f, 102.700969646f)); - eulersFromEngineer.push_back(glm::vec3(94.1058470618f, -4.50566341059f, -63.663409373f)); - eulersFromEngineer.push_back(glm::vec3(-70.8795528982f, 7.23642133863f, 82.4215992116f)); - eulersFromEngineer.push_back(glm::vec3(354.986036508f, -0.167802950666f, -73.0700479208f)); - eulersFromEngineer.push_back(glm::vec3(174.972844269f, 0.163203627576f, 73.0320316709f)); - eulersFromEngineer.push_back(glm::vec3(-156.40949683f, -2.25584901219f, 89.478726037f)); - eulersFromEngineer.push_back(glm::vec3(-147.791270237f, -2.25609369727f, 90.5216948763f)); - - qCDebug(animation) << "list of joint quats for engineer"; - for (glm::vec3 bone : eulersFromEngineer) { - glm::quat boneQuat(glm::vec3((bone.x / 180.0f)*PI, (bone.y / 180.0f)*PI, (bone.z / 180.0f)*PI)); - qCDebug(animation) << boneQuat; - } - - - - //qCDebug(animation) << "set the neck and spine2 offsets " << spine2Id << " " << neckId; - //glm::quat leftShoulder(glm::vec3((-81.1050620752f / 180.0f)*PI, (-20.1443891394f / 180.0f)*PI, (100.309499473f / 180.0f)*PI)); - //qCDebug(animation) << "jointRotationOffset = LeftShoulder = (" << leftShoulder << ")"; - //glm::quat testRot2(glm::vec3(PI / 2.0f, 0.0f, PI / 2.0f)); - //qCDebug(animation) << "test rot2 from euler" << testRot2; -} - -const QMap& Rig::getJointRotationOffsets() const { - return _jointRotationOffsets; -} \ No newline at end of file diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index 1b5826a757..345f335f88 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -128,7 +128,6 @@ public: int getJointStateCount() const; int indexOfJoint(const QString& jointName) const; QString nameOfJoint(int jointIndex) const; - const QMap& getJointRotationOffsets() const; void setModelOffset(const glm::mat4& modelOffsetMat); @@ -232,8 +231,6 @@ public: const AnimContext::DebugAlphaMap& getDebugAlphaMap() const { return _lastContext.getDebugAlphaMap(); } const AnimVariantMap& getAnimVars() const { return _lastAnimVars; } const AnimContext::DebugStateMachineMap& getStateMachineMap() const { return _lastContext.getStateMachineMap(); } - - void setJointRotationOffsets(const QMap& offsets); signals: void onLoadComplete(); @@ -303,8 +300,6 @@ protected: int _rightElbowJointIndex { -1 }; int _rightShoulderJointIndex { -1 }; - QMap _jointRotationOffsets; - glm::vec3 _lastForward; glm::vec3 _lastPosition; glm::vec3 _lastVelocity; @@ -423,7 +418,6 @@ protected: SnapshotBlendPoseHelper _hipsBlendHelper; ControllerParameters _previousControllerParameters; - bool _alreadyInitialized { false }; }; #endif /* defined(__hifi__Rig__) */ diff --git a/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp b/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp index 0f18d254c3..3b9e874fba 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp @@ -18,7 +18,6 @@ #include #include #include -#include #include "Avatar.h" #include "Logging.h" @@ -58,7 +57,6 @@ void SkeletonModel::initJointStates() { const HFMModel& hfmModel = getHFMModel(); glm::mat4 modelOffset = glm::scale(_scale) * glm::translate(_offset); - _rig.setJointRotationOffsets(FSTReader::getJointRotationOffsets(getGeometry()->getMapping())); _rig.initJointStates(hfmModel, modelOffset); { @@ -85,7 +83,6 @@ void SkeletonModel::initJointStates() { // Skeleton may have already been scaled so unscale it _defaultEyeModelPosition = _defaultEyeModelPosition / _scale; - //_rig.setJointRotationOffsets(FSTReader::getJointRotationOffsets(getGeometry()->getMapping())); computeBoundingShape(); Extents meshExtents = getMeshExtents(); diff --git a/libraries/fbx/src/FSTReader.cpp b/libraries/fbx/src/FSTReader.cpp index 10ea9dc964..75596862d2 100644 --- a/libraries/fbx/src/FSTReader.cpp +++ b/libraries/fbx/src/FSTReader.cpp @@ -207,30 +207,6 @@ QVector FSTReader::getScripts(const QUrl& url, const QVariantHash& mapp return scriptPaths; } -QMap FSTReader::getJointRotationOffsets(const QVariantHash& mapping) { - QMap jointRotationOffsets; - if (!mapping.isEmpty() && mapping.contains(JOINT_ROTATION_OFFSET_FIELD) && mapping[JOINT_ROTATION_OFFSET_FIELD].type() == QVariant::Hash) { - auto offsets = mapping[JOINT_ROTATION_OFFSET_FIELD].toHash(); - for (auto itr = offsets.begin(); itr != offsets.end(); itr++) { - QString jointName = itr.key(); - QString line = itr.value().toString(); - auto eulerAngles = line.split(','); - if (eulerAngles.size() == 3) { - float eulerX = eulerAngles[0].mid(1).toFloat(); - float eulerY = eulerAngles[1].toFloat(); - float eulerZ = eulerAngles[2].mid(0, eulerAngles[2].size() - 1).toFloat(); - if (!isNaN(eulerX) && !isNaN(eulerY) && !isNaN(eulerZ)) { - glm::quat rotationOffset = (glm::angleAxis(eulerX * RADIANS_PER_DEGREE, Vectors::UNIT_Y) * - glm::angleAxis(eulerY * RADIANS_PER_DEGREE, Vectors::UNIT_X) * - glm::angleAxis(eulerZ * RADIANS_PER_DEGREE, Vectors::UNIT_Z)); - jointRotationOffsets.insert(jointName, rotationOffset); - } - } - } - } - return jointRotationOffsets; -} - QVariantHash FSTReader::downloadMapping(const QString& url) { QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); QNetworkRequest networkRequest = QNetworkRequest(url); diff --git a/libraries/fbx/src/FSTReader.h b/libraries/fbx/src/FSTReader.h index ef649eb4d8..4a8574f0cf 100644 --- a/libraries/fbx/src/FSTReader.h +++ b/libraries/fbx/src/FSTReader.h @@ -14,7 +14,6 @@ #include #include -#include static const QString NAME_FIELD = "name"; static const QString TYPE_FIELD = "type"; @@ -30,7 +29,6 @@ static const QString JOINT_FIELD = "joint"; static const QString FREE_JOINT_FIELD = "freeJoint"; static const QString BLENDSHAPE_FIELD = "bs"; static const QString SCRIPT_FIELD = "script"; -static const QString JOINT_ROTATION_OFFSET_FIELD = "jointRotationOffset"; class FSTReader { public: @@ -53,7 +51,6 @@ public: static ModelType predictModelType(const QVariantHash& mapping); static QVector getScripts(const QUrl& fstUrl, const QVariantHash& mapping = QVariantHash()); - static QMap getJointRotationOffsets(const QVariantHash& mapping); static QString getNameFromType(ModelType modelType); static FSTReader::ModelType getTypeFromName(const QString& name); diff --git a/tools/skeleton-dump/src/SkeletonDumpApp.cpp b/tools/skeleton-dump/src/SkeletonDumpApp.cpp index d1520d81f9..2bb5c758ce 100644 --- a/tools/skeleton-dump/src/SkeletonDumpApp.cpp +++ b/tools/skeleton-dump/src/SkeletonDumpApp.cpp @@ -56,8 +56,7 @@ SkeletonDumpApp::SkeletonDumpApp(int argc, char* argv[]) : QCoreApplication(argc QByteArray blob = file.readAll(); std::unique_ptr geometry(readFBX(blob, QVariantHash())); - QMap jointRotationOffsets; - std::unique_ptr skeleton(new AnimSkeleton(*geometry, jointRotationOffsets)); + std::unique_ptr skeleton(new AnimSkeleton(*geometry)); skeleton->dump(verbose); } From 4e89e9ed2a3a91d84c7327bfdc2d67d48ff6e3c3 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Fri, 9 Nov 2018 09:58:17 +0100 Subject: [PATCH 178/286] Fixed draw / do_draw in GLBackends --- .../src/gpu/gl/GLBackendTexture.cpp | 12 ++++---- libraries/gpu-gl/src/gpu/gl41/GL41Backend.cpp | 28 ------------------ libraries/gpu-gl/src/gpu/gl41/GL41Backend.h | 1 - libraries/gpu-gl/src/gpu/gl45/GL45Backend.cpp | 29 ------------------- libraries/gpu-gl/src/gpu/gl45/GL45Backend.h | 1 - 5 files changed, 6 insertions(+), 65 deletions(-) diff --git a/libraries/gpu-gl-common/src/gpu/gl/GLBackendTexture.cpp b/libraries/gpu-gl-common/src/gpu/gl/GLBackendTexture.cpp index d4009302e2..b74ff079d7 100644 --- a/libraries/gpu-gl-common/src/gpu/gl/GLBackendTexture.cpp +++ b/libraries/gpu-gl-common/src/gpu/gl/GLBackendTexture.cpp @@ -71,13 +71,13 @@ void GLBackend::do_generateTextureMips(const Batch& batch, size_t paramOffset) { return; } - // DO not transfer the texture, this call is expected for rendering texture - GLTexture* object = syncGPUObject(resourceTexture); - if (!object) { - return; - } + // DO not transfer the texture, this call is expected for rendering texture + GLTexture* object = syncGPUObject(resourceTexture); + if (!object) { + return; + } - object->generateMips(); + object->generateMips(); } void GLBackend::do_generateTextureMipsWithPipeline(const Batch& batch, size_t paramOffset) { diff --git a/libraries/gpu-gl/src/gpu/gl41/GL41Backend.cpp b/libraries/gpu-gl/src/gpu/gl41/GL41Backend.cpp index 43ae4691b9..ea884fe125 100644 --- a/libraries/gpu-gl/src/gpu/gl41/GL41Backend.cpp +++ b/libraries/gpu-gl/src/gpu/gl41/GL41Backend.cpp @@ -43,34 +43,6 @@ void GL41Backend::draw(GLenum mode, uint32 numVertices, uint32 startVertex) { (void)CHECK_GL_ERROR(); } -void GL41Backend::do_draw(const Batch& batch, size_t paramOffset) { - Primitive primitiveType = (Primitive)batch._params[paramOffset + 2]._uint; - GLenum mode = gl::PRIMITIVE_TO_GL[primitiveType]; - uint32 numVertices = batch._params[paramOffset + 1]._uint; - uint32 startVertex = batch._params[paramOffset + 0]._uint; - - if (isStereo()) { -#ifdef GPU_STEREO_DRAWCALL_INSTANCED - glDrawArraysInstanced(mode, startVertex, numVertices, 2); -#else - setupStereoSide(0); - glDrawArrays(mode, startVertex, numVertices); - setupStereoSide(1); - glDrawArrays(mode, startVertex, numVertices); -#endif - _stats._DSNumTriangles += 2 * numVertices / 3; - _stats._DSNumDrawcalls += 2; - - } else { - glDrawArrays(mode, startVertex, numVertices); - _stats._DSNumTriangles += numVertices / 3; - _stats._DSNumDrawcalls++; - } - _stats._DSNumAPIDrawcalls++; - - (void) CHECK_GL_ERROR(); -} - void GL41Backend::do_drawIndexed(const Batch& batch, size_t paramOffset) { Primitive primitiveType = (Primitive)batch._params[paramOffset + 2]._uint; GLenum mode = gl::PRIMITIVE_TO_GL[primitiveType]; diff --git a/libraries/gpu-gl/src/gpu/gl41/GL41Backend.h b/libraries/gpu-gl/src/gpu/gl41/GL41Backend.h index 881487c9db..23e49773ae 100644 --- a/libraries/gpu-gl/src/gpu/gl41/GL41Backend.h +++ b/libraries/gpu-gl/src/gpu/gl41/GL41Backend.h @@ -147,7 +147,6 @@ protected: GLQuery* syncGPUObject(const Query& query) override; // Draw Stage - void do_draw(const Batch& batch, size_t paramOffset) override; void do_drawIndexed(const Batch& batch, size_t paramOffset) override; void do_drawInstanced(const Batch& batch, size_t paramOffset) override; void do_drawIndexedInstanced(const Batch& batch, size_t paramOffset) override; diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.cpp index c4de7359d2..29c2a230a0 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.cpp @@ -66,35 +66,6 @@ void GL45Backend::draw(GLenum mode, uint32 numVertices, uint32 startVertex) { (void)CHECK_GL_ERROR(); } -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]; - uint32 numVertices = batch._params[paramOffset + 1]._uint; - uint32 startVertex = batch._params[paramOffset + 0]._uint; - - if (isStereo()) { -#ifdef GPU_STEREO_DRAWCALL_INSTANCED - glDrawArraysInstanced(mode, startVertex, numVertices, 2); -#else - setupStereoSide(0); - glDrawArrays(mode, startVertex, numVertices); - setupStereoSide(1); - glDrawArrays(mode, startVertex, numVertices); -#endif - - _stats._DSNumTriangles += 2 * numVertices / 3; - _stats._DSNumDrawcalls += 2; - - } else { - glDrawArrays(mode, startVertex, numVertices); - _stats._DSNumTriangles += numVertices / 3; - _stats._DSNumDrawcalls++; - } - _stats._DSNumAPIDrawcalls++; - - (void)CHECK_GL_ERROR(); -} - void GL45Backend::do_drawIndexed(const Batch& batch, size_t paramOffset) { Primitive primitiveType = (Primitive)batch._params[paramOffset + 2]._uint; GLenum mode = gl::PRIMITIVE_TO_GL[primitiveType]; diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h index c1ce074188..391fec45ce 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h +++ b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h @@ -248,7 +248,6 @@ protected: GLQuery* syncGPUObject(const Query& query) override; // Draw Stage - void do_draw(const Batch& batch, size_t paramOffset) override; void do_drawIndexed(const Batch& batch, size_t paramOffset) override; void do_drawInstanced(const Batch& batch, size_t paramOffset) override; void do_drawIndexedInstanced(const Batch& batch, size_t paramOffset) override; From dce040978cda9ef13794d48b8053fc1f1e3a8f4d Mon Sep 17 00:00:00 2001 From: Angus Antley Date: Fri, 9 Nov 2018 01:31:27 -0800 Subject: [PATCH 179/286] works two joint avatar with luis's new code. remains to do the engineer --- libraries/animation/src/AnimSkeleton.cpp | 50 ++++++------------------ libraries/animation/src/AnimSkeleton.h | 2 +- libraries/fbx/src/FBXReader.cpp | 2 + 3 files changed, 14 insertions(+), 40 deletions(-) diff --git a/libraries/animation/src/AnimSkeleton.cpp b/libraries/animation/src/AnimSkeleton.cpp index f0219eb946..a285c100fe 100644 --- a/libraries/animation/src/AnimSkeleton.cpp +++ b/libraries/animation/src/AnimSkeleton.cpp @@ -26,7 +26,10 @@ AnimSkeleton::AnimSkeleton(const HFMModel& hfmModel) { joints.push_back(joint); } - buildSkeletonFromJoints(joints, hfmModel.jointOffsets); + glm::quat offset1(0.5f, 0.5f, 0.5f, -0.5f); + glm::quat offset2(0.7071f, 0.0f, 0.7071f, 0.0f); + + buildSkeletonFromJoints(joints, hfmModel.jointRotationOffsets); // add offsets for spine2 and the neck @@ -44,15 +47,17 @@ AnimSkeleton::AnimSkeleton(const HFMModel& hfmModel) { if (cluster.jointIndex == 62) { qCDebug(animation) << "Neck"; - qCDebug(animation) << "found a joint offset to add " << cluster.jointIndex << " " << jointOffsets[cluster.jointIndex] << " cluster " << cluster.jointIndex; - AnimPose localOffset(jointOffsets[cluster.jointIndex], glm::vec3()); + qCDebug(animation) << "found a joint offset to add " << cluster.jointIndex << " " << offset2 << " cluster " << cluster.jointIndex; + AnimPose localOffset(hfmModel.jointRotationOffsets[cluster.jointIndex], glm::vec3()); + //AnimPose localOffset(offset2, glm::vec3()); cluster.inverseBindMatrix = (glm::mat4)localOffset.inverse() * cluster.inverseBindMatrix; cluster.inverseBindTransform.evalFromRawMatrix(cluster.inverseBindMatrix); } if (cluster.jointIndex == 13) { qCDebug(animation) << "Spine2"; - qCDebug(animation) << "found a joint offset to add " << cluster.jointIndex << " " << jointOffsets[cluster.jointIndex] << " cluster " << cluster.jointIndex; - AnimPose localOffset(jointOffsets[cluster.jointIndex], glm::vec3()); + qCDebug(animation) << "found a joint offset to add " << cluster.jointIndex << " " << offset1<< " cluster " << cluster.jointIndex; + AnimPose localOffset(hfmModel.jointRotationOffsets[cluster.jointIndex], glm::vec3()); + //AnimPose localOffset(offset1, glm::vec3()); cluster.inverseBindMatrix = (glm::mat4)localOffset.inverse() * cluster.inverseBindMatrix; cluster.inverseBindTransform.evalFromRawMatrix(cluster.inverseBindMatrix); } @@ -257,40 +262,7 @@ void AnimSkeleton::buildSkeletonFromJoints(const std::vector& joints, } } - /* - AnimPose newAbsPose; - if (parentIndex >= 0) { - newAbsPose = _absoluteDefaultPoses[parentIndex] * AnimPose(relDefaultPose.rot(),glm::vec3()); - } else { - newAbsPose = relDefaultPose; - } - - // putting the pipeline code is - // remember the inverse bind pose already has the offset added into it. the total effect is offset^-1 * relDefPose * offset. - // this gives us the correct transform for the joint that has been put in t-pose with an offset rotation. - //relDefaultPose = relDefaultPose * _avatarTPoseOffsets[i]; - - QString jointName = getJointName(i); - if (jointOffsets.contains(i)) { - //QString parentIndex = getJointName(parentIndex); - AnimPose localOffset(jointOffsets[i], glm::vec3()); - newAbsPose = newAbsPose * localOffset; - } - if ((parentIndex >= 0) && jointOffsets.contains(parentIndex)) { - AnimPose localParentOffset(jointOffsets[parentIndex], glm::vec3()); - newAbsPose = localParentOffset.inverse() * AnimPose(glm::quat(), relDefaultPose.trans()) * localParentOffset * AnimPose(newAbsPose.rot(), glm::vec3()); - } - - if (parentIndex >= 0) { - relDefaultPose = _absoluteDefaultPoses[parentIndex].inverse() * newAbsPose; - } - _relativeDefaultPoses.push_back(relDefaultPose); - - - _absoluteDefaultPoses.push_back(newAbsPose); - - } - */ + for (int i = 0; i < _jointsSize; i++) { _jointIndicesByName[_joints[i].name] = i; } diff --git a/libraries/animation/src/AnimSkeleton.h b/libraries/animation/src/AnimSkeleton.h index a08276d2cb..f06813e51f 100644 --- a/libraries/animation/src/AnimSkeleton.h +++ b/libraries/animation/src/AnimSkeleton.h @@ -23,7 +23,7 @@ public: using Pointer = std::shared_ptr; using ConstPointer = std::shared_ptr; - explicit AnimSkeleton(const HFMModel& hfmModel, const QMap jointOffsets); + explicit AnimSkeleton(const HFMModel& hfmModel); explicit AnimSkeleton(const std::vector& joints, const QMap jointOffsets); int nameToJointIndex(const QString& jointName) const; diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index 6ec7e0e73c..281759a9e9 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -2027,6 +2027,8 @@ HFMModel* FBXReader::extractHFMModel(const QVariantHash& mapping, const QString& } qCDebug(modelformat) << "Joint Rotation Offset added to Rig._jointRotationOffsets : " << " jointName: " << jointName << " jointIndex: " << jointIndex << " rotation offset: " << rotationOffset; } + hfmModel.jointRotationOffsets.insert(13, glm::quat(0.5f, 0.5f, 0.5f, -0.5f)); + hfmModel.jointRotationOffsets.insert(62, glm::quat(0.7071f, 0.0f, -0.7071f, 0.0f)); return hfmModelPtr; } From fd365b5509e3e3b6cfa37bf4bffba95411dd8e5a Mon Sep 17 00:00:00 2001 From: Angus Antley Date: Fri, 9 Nov 2018 07:22:36 -0800 Subject: [PATCH 180/286] added debug print to fbx reader to see if the fst is being read correctly --- libraries/animation/src/AnimSkeleton.cpp | 2 +- libraries/fbx/src/FBXReader.cpp | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/libraries/animation/src/AnimSkeleton.cpp b/libraries/animation/src/AnimSkeleton.cpp index a285c100fe..1d4d662928 100644 --- a/libraries/animation/src/AnimSkeleton.cpp +++ b/libraries/animation/src/AnimSkeleton.cpp @@ -27,7 +27,7 @@ AnimSkeleton::AnimSkeleton(const HFMModel& hfmModel) { } glm::quat offset1(0.5f, 0.5f, 0.5f, -0.5f); - glm::quat offset2(0.7071f, 0.0f, 0.7071f, 0.0f); + glm::quat offset2(0.7071f, 0.0f, -0.7071f, 0.0f); buildSkeletonFromJoints(joints, hfmModel.jointRotationOffsets); // add offsets for spine2 and the neck diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index 281759a9e9..6ae2b69e60 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -632,10 +632,14 @@ QMap getJointRotationOffsets(const QVariantHash& mapping) { if (!isNaN(quatX) && !isNaN(quatY) && !isNaN(quatZ) && !isNaN(quatW)) { glm::quat rotationOffset = glm::quat(quatW, quatX, quatY, quatZ); jointRotationOffsets.insert(jointName, rotationOffset); + } } + qCDebug(modelformat) << "found an offset in fst"; } + qCDebug(modelformat) << "found an offset in fst 2"; } + qCDebug(modelformat) << "found an offset in fst 3"; return jointRotationOffsets; } From 615ffebb2cb4850a1c2793bce55909f31d7293d4 Mon Sep 17 00:00:00 2001 From: David Back Date: Fri, 9 Nov 2018 13:14:05 -0800 Subject: [PATCH 181/286] new draggable numbers WIP --- scripts/system/html/css/edit-style.css | 38 ++- scripts/system/html/entityProperties.html | 1 + scripts/system/html/js/draggableNumber.js | 92 ++++++++ scripts/system/html/js/entityProperties.js | 258 +++++++-------------- 4 files changed, 204 insertions(+), 185 deletions(-) create mode 100644 scripts/system/html/js/draggableNumber.js diff --git a/scripts/system/html/css/edit-style.css b/scripts/system/html/css/edit-style.css index 8137d6e659..5107ad8a3d 100644 --- a/scripts/system/html/css/edit-style.css +++ b/scripts/system/html/css/edit-style.css @@ -309,6 +309,34 @@ input[type=number].hover-down::-webkit-inner-spin-button:after { color: #ffffff; } +.draggable-number { + position: relative; + right: 10px; +} +.draggable-number span { + position: relative; + top: -2px; + display: inline-block; + font-family: hifi-glyphs; + font-size: 20px; + z-index: 2; +} +.draggable-number.left-arrow { + left: 18px; + transform: rotate(180deg); +} +.draggable-number.right-arrow { + right: 18px; +} +.draggable-number input { +} +.draggable-number input::selection { +} +.draggable-number input::-webkit-inner-spin-button { + -webkit-appearance: none; + visibility: hidden; +} + input[type=range] { -webkit-appearance: none; background: #2e2e2e; @@ -1460,11 +1488,9 @@ th#entity-hasScript { } input#property-scale-button-rescale { - margin-top: 6px; min-width: 50px; } input#property-scale-button-reset { - margin-top: 6px; margin-right: 0; } @@ -1593,9 +1619,9 @@ input.rename-entity { display: flex; flex-flow: row; } -.fstuple input { - margin-left: 4px; - margin-right: 10px; +.fstuple label { + position: relative; + left: 10px; } .fstuple label.red, .fstuple label.x { color: #C62147; @@ -1609,7 +1635,7 @@ input.rename-entity { .xyz.fstuple, .pyr.fstuple { position: relative; - left: -12px; + left: -19px; } .rgb.fstuple .tuple { diff --git a/scripts/system/html/entityProperties.html b/scripts/system/html/entityProperties.html index 6af8ee992a..01395368b0 100644 --- a/scripts/system/html/entityProperties.html +++ b/scripts/system/html/entityProperties.html @@ -22,6 +22,7 @@ + diff --git a/scripts/system/html/js/draggableNumber.js b/scripts/system/html/js/draggableNumber.js new file mode 100644 index 0000000000..62fa02c6f9 --- /dev/null +++ b/scripts/system/html/js/draggableNumber.js @@ -0,0 +1,92 @@ +// draggableNumber.js +// +// Created by David Back on 7 Nov 2018 +// Copyright 2018 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 + +debugPrint = function (message) { + console.log(message); +}; + +function DraggableNumber(min, max, step) { + this.min = min; + this.max = max; + this.step = step !== undefined ? step : 1; + this.startEvent = null; + this.initialize(); +} + +DraggableNumber.prototype = { + onMouseDown: function(event) { + let that = this.draggableNumber; + that.mouseDown(event); + }, + + mouseDown: function(event) { + this.startEvent = event; + document.addEventListener("mousemove", this.onMouseMove); + document.addEventListener("mouseup", this.onMouseUp); + }, + + mouseMove: function(event, that) { + if (this.startEvent) { + let dx = event.clientX - this.startEvent.clientX; + let valueChange = dx * this.step; + let newValue = parseFloat(this.elInput.value) + valueChange; + if (this.min !== undefined && newValue < this.min) { + newValue = this.min; + } else if (this.max !== undefined && newValue > this.max) { + newValue = this.max; + } + this.elInput.value = newValue; + this.inputChangeFunction(); + this.startEvent = event; + } + }, + + mouseUp: function(event) { + this.startEvent = null; + document.removeEventListener("mousemove", this.onMouseMove); + document.removeEventListener("mouseup", this.onMouseUp); + }, + + setInputChangeFunction: function(inputChangeFunction) { + this.inputChangeFunction = inputChangeFunction.bind(this.elInput); + this.elInput.addEventListener('change', this.inputChangeFunction); + }, + + initialize: function() { + this.onMouseMove = this.mouseMove.bind(this); + this.onMouseUp = this.mouseUp.bind(this); + + this.elDiv = document.createElement('div'); + this.elDiv.className = "draggable-number"; + + this.elInput = document.createElement('input'); + this.elInput.setAttribute("type", "number"); + if (this.min !== undefined) { + this.elInput.setAttribute("min", this.min); + } + if (this.max !== undefined) { + this.elInput.setAttribute("max", this.max); + } + if (this.step !== undefined) { + this.elInput.setAttribute("step", this.step); + } + this.elInput.draggableNumber = this; + this.elInput.addEventListener("mousedown", this.onMouseDown); + + this.elLeftArrow = document.createElement('span'); + this.elRightArrow = document.createElement('span'); + this.elLeftArrow.className = 'draggable-number left-arrow'; + this.elLeftArrow.innerHTML = 'D'; + this.elRightArrow.className = 'draggable-number right-arrow'; + this.elRightArrow.innerHTML = 'D'; + + this.elDiv.appendChild(this.elLeftArrow); + this.elDiv.appendChild(this.elInput); + this.elDiv.appendChild(this.elRightArrow); + } +}; diff --git a/scripts/system/html/js/entityProperties.js b/scripts/system/html/js/entityProperties.js index e5e7426dd2..5ea810a069 100644 --- a/scripts/system/html/js/entityProperties.js +++ b/scripts/system/html/js/entityProperties.js @@ -309,7 +309,7 @@ const GROUPS = [ }, { label: "Background Blend", - type: "slider", + type: "number", min: 0, max: 1, step: 0.01, @@ -331,7 +331,7 @@ const GROUPS = [ }, { label: "Glare Angle", - type: "slider", + type: "number", min: 0, max: 180, step: 1, @@ -347,7 +347,7 @@ const GROUPS = [ }, { label: "Bloom Intensity", - type: "slider", + type: "number", min: 0, max: 1, step: 0.01, @@ -357,7 +357,7 @@ const GROUPS = [ }, { label: "Bloom Threshold", - type: "slider", + type: "number", min: 0, max: 1, step: 0.01, @@ -367,7 +367,7 @@ const GROUPS = [ }, { label: "Bloom Size", - type: "slider", + type: "number", min: 0, max: 2, step: 0.01, @@ -615,7 +615,7 @@ const GROUPS = [ }, { label: "Lifespan", - type: "slider", + type: "number", unit: "s", min: 0.01, max: 10, @@ -625,7 +625,7 @@ const GROUPS = [ }, { label: "Max Particles", - type: "slider", + type: "number", min: 1, max: 10000, step: 1, @@ -646,7 +646,7 @@ const GROUPS = [ properties: [ { label: "Emit Rate", - type: "slider", + type: "number", min: 1, max: 1000, step: 1, @@ -654,7 +654,7 @@ const GROUPS = [ }, { label: "Emit Speed", - type: "slider", + type: "number", min: 0, max: 5, step: 0.01, @@ -663,7 +663,7 @@ const GROUPS = [ }, { label: "Speed Spread", - type: "slider", + type: "number", min: 0, max: 5, step: 0.01, @@ -682,7 +682,7 @@ const GROUPS = [ }, { label: "Emit Radius Start", - type: "slider", + type: "number", min: 0, max: 1, step: 0.01, @@ -717,7 +717,7 @@ const GROUPS = [ properties: [ { label: "Start", - type: "slider", + type: "number", min: 0, max: 4, step: 0.01, @@ -727,7 +727,7 @@ const GROUPS = [ }, { label: "Middle", - type: "slider", + type: "number", min: 0, max: 4, step: 0.01, @@ -736,7 +736,7 @@ const GROUPS = [ }, { label: "Finish", - type: "slider", + type: "number", min: 0, max: 4, step: 0.01, @@ -748,7 +748,7 @@ const GROUPS = [ }, { label: "Size Spread", - type: "slider", + type: "number", min: 0, max: 4, step: 0.01, @@ -804,7 +804,7 @@ const GROUPS = [ properties: [ { label: "Start", - type: "slider", + type: "number", min: 0, max: 1, step: 0.01, @@ -814,7 +814,7 @@ const GROUPS = [ }, { label: "Middle", - type: "slider", + type: "number", min: 0, max: 1, step: 0.01, @@ -823,7 +823,7 @@ const GROUPS = [ }, { label: "Finish", - type: "slider", + type: "number", min: 0, max: 1, step: 0.01, @@ -835,7 +835,7 @@ const GROUPS = [ }, { label: "Alpha Spread", - type: "slider", + type: "number", min: 0, max: 1, step: 0.01, @@ -880,7 +880,7 @@ const GROUPS = [ properties: [ { label: "Start", - type: "slider", + type: "number", min: -360, max: 360, step: 1, @@ -892,7 +892,7 @@ const GROUPS = [ }, { label: "Middle", - type: "slider", + type: "number", min: -360, max: 360, step: 1, @@ -903,7 +903,7 @@ const GROUPS = [ }, { label: "Finish", - type: "slider", + type: "number", min: -360, max: 360, step: 1, @@ -917,7 +917,7 @@ const GROUPS = [ }, { label: "Spin Spread", - type: "slider", + type: "number", min: 0, max: 360, step: 1, @@ -944,7 +944,7 @@ const GROUPS = [ properties: [ { label: "Start", - type: "slider", + type: "number", min: -180, max: 0, step: 1, @@ -955,7 +955,7 @@ const GROUPS = [ }, { label: "Finish", - type: "slider", + type: "number", min: 0, max: 180, step: 1, @@ -972,7 +972,7 @@ const GROUPS = [ properties: [ { label: "Start", - type: "slider", + type: "number", min: 0, max: 180, step: 1, @@ -983,7 +983,7 @@ const GROUPS = [ }, { label: "Finish", - type: "slider", + type: "number", min: 0, max: 180, step: 1, @@ -1348,11 +1348,6 @@ const COLOR_ELEMENTS = { BLUE_INPUT: 3, }; -const SLIDER_ELEMENTS = { - SLIDER: 0, - NUMBER_INPUT: 1, -}; - const ICON_ELEMENTS = { ICON: 0, LABEL: 1, @@ -1399,7 +1394,6 @@ function getPropertyInputElement(propertyID) { case 'string': case 'bool': case 'number': - case 'slider': case 'dropdown': case 'textarea': case 'texture': @@ -1478,16 +1472,12 @@ function resetProperties() { property.elInput.checked = false; break; } - case 'number': - case 'slider': { + case 'number': { if (propertyData.defaultValue !== undefined) { property.elInput.value = propertyData.defaultValue; } else { property.elInput.value = ""; } - if (property.elSlider !== undefined) { - property.elSlider.value = property.elInput.value; - } break; } case 'vec3': @@ -1782,98 +1772,21 @@ function createNumberProperty(property, elProperty) { let elementID = property.elementID; let propertyData = property.data; - elProperty.className = "number"; + elProperty.className = "property draggable-number"; - let elInput = document.createElement('input'); - elInput.setAttribute("id", elementID); - elInput.setAttribute("type", "number"); - if (propertyData.min !== undefined) { - elInput.setAttribute("min", propertyData.min); - } - if (propertyData.max !== undefined) { - elInput.setAttribute("max", propertyData.max); - } - if (propertyData.step !== undefined) { - elInput.setAttribute("step", propertyData.step); - } - - let defaultValue = propertyData.defaultValue; - if (defaultValue !== undefined) { - elInput.value = defaultValue; - } - - elInput.addEventListener('change', createEmitNumberPropertyUpdateFunction(propertyName, propertyData.multiplier, propertyData.decimals, property.isParticleProperty)); - - elProperty.appendChild(elInput); + let elDraggableNumber = new DraggableNumber(propertyData.min, propertyData.max, propertyData.step); + + let inputChangeFunction = createEmitNumberPropertyUpdateFunction(propertyName, propertyData.multiplier, propertyData.decimals, property.isParticleProperty); + elDraggableNumber.setInputChangeFunction(inputChangeFunction); + elDraggableNumber.elInput.setAttribute("id", elementID); + elProperty.appendChild(elDraggableNumber.elDiv); + if (propertyData.buttons !== undefined) { - addButtons(elProperty, elementID, propertyData.buttons, true); + addButtons(elDraggableNumber.elDiv, elementID, propertyData.buttons, false); } - return elInput; -} - -function createSliderProperty(property, elProperty) { - let propertyData = property.data; - - elProperty.className = "range"; - - let elDiv = document.createElement("div"); - elDiv.className = "slider-wrapper"; - - let elSlider = document.createElement("input"); - elSlider.setAttribute("type", "range"); - - let elInput = document.createElement("input"); - elInput.setAttribute("type", "number"); - - if (propertyData.min !== undefined) { - elInput.setAttribute("min", propertyData.min); - elSlider.setAttribute("min", propertyData.min); - } - if (propertyData.max !== undefined) { - elInput.setAttribute("max", propertyData.max); - elSlider.setAttribute("max", propertyData.max); - elSlider.setAttribute("data-max", propertyData.max); - } - if (propertyData.step !== undefined) { - elInput.setAttribute("step", propertyData.step); - elSlider.setAttribute("step", propertyData.step); - } - - elInput.onchange = function (event) { - let inputValue = event.target.value; - elSlider.value = inputValue; - if (propertyData.multiplier !== undefined) { - inputValue *= propertyData.multiplier; - } - updateProperty(property.name, inputValue, property.isParticleProperty); - }; - elSlider.oninput = function (event) { - let sliderValue = event.target.value; - if (propertyData.step === 1) { - if (sliderValue > 0) { - elInput.value = Math.floor(sliderValue); - } else { - elInput.value = Math.ceil(sliderValue); - } - } else { - elInput.value = sliderValue; - } - if (propertyData.multiplier !== undefined) { - sliderValue *= propertyData.multiplier; - } - updateProperty(property.name, sliderValue, property.isParticleProperty); - }; - - elDiv.appendChild(elSlider); - elDiv.appendChild(elInput); - elProperty.appendChild(elDiv); - - let elResult = []; - elResult[SLIDER_ELEMENTS.SLIDER] = elSlider; - elResult[SLIDER_ELEMENTS.NUMBER_INPUT] = elInput; - return elResult; + return elDraggableNumber.elInput; } function createVec3Property(property, elProperty) { @@ -1888,23 +1801,24 @@ function createVec3Property(property, elProperty) { //elProperty.appendChild(elTuple); - let elInputX = createTupleNumberInput(elProperty, elementID, propertyData.subLabels[VECTOR_ELEMENTS.X_INPUT], - propertyData.min, propertyData.max, propertyData.step); - let elInputY = createTupleNumberInput(elProperty, elementID, propertyData.subLabels[VECTOR_ELEMENTS.Y_INPUT], - propertyData.min, propertyData.max, propertyData.step); - let elInputZ = createTupleNumberInput(elProperty, elementID, propertyData.subLabels[VECTOR_ELEMENTS.Z_INPUT], - propertyData.min, propertyData.max, propertyData.step); + let elNumberX = createTupleNumberInput(elProperty, elementID, propertyData.subLabels[VECTOR_ELEMENTS.X_INPUT], + propertyData.min, propertyData.max, propertyData.step); + let elNumberY = createTupleNumberInput(elProperty, elementID, propertyData.subLabels[VECTOR_ELEMENTS.Y_INPUT], + propertyData.min, propertyData.max, propertyData.step); + let elNumberZ = createTupleNumberInput(elProperty, elementID, propertyData.subLabels[VECTOR_ELEMENTS.Z_INPUT], + propertyData.min, propertyData.max, propertyData.step); - let inputChangeFunction = createEmitVec3PropertyUpdateFunction(propertyName, elInputX, elInputY, elInputZ, - propertyData.multiplier, property.isParticleProperty); - elInputX.addEventListener('change', inputChangeFunction); - elInputY.addEventListener('change', inputChangeFunction); - elInputZ.addEventListener('change', inputChangeFunction); + let inputChangeFunction = createEmitVec3PropertyUpdateFunction(propertyName, elNumberX.elInput, elNumberY.elInput, + elNumberZ.elInput, propertyData.multiplier, + property.isParticleProperty); + elNumberX.setInputChangeFunction(inputChangeFunction); + elNumberY.setInputChangeFunction(inputChangeFunction); + elNumberZ.setInputChangeFunction(inputChangeFunction); let elResult = []; - elResult[VECTOR_ELEMENTS.X_INPUT] = elInputX; - elResult[VECTOR_ELEMENTS.Y_INPUT] = elInputY; - elResult[VECTOR_ELEMENTS.Z_INPUT] = elInputZ; + elResult[VECTOR_ELEMENTS.X_INPUT] = elNumberX.elInput; + elResult[VECTOR_ELEMENTS.Y_INPUT] = elNumberY.elInput; + elResult[VECTOR_ELEMENTS.Z_INPUT] = elNumberZ.elInput; return elResult; } @@ -1920,19 +1834,19 @@ function createVec2Property(property, elProperty) { elProperty.appendChild(elTuple); - let elInputX = createTupleNumberInput(elTuple, elementID, propertyData.subLabels[VECTOR_ELEMENTS.X_INPUT], - propertyData.min, propertyData.max, propertyData.step); - let elInputY = createTupleNumberInput(elTuple, elementID, propertyData.subLabels[VECTOR_ELEMENTS.Y_INPUT], - propertyData.min, propertyData.max, propertyData.step); + let elNumberX = createTupleNumberInput(elProperty, elementID, propertyData.subLabels[VECTOR_ELEMENTS.X_INPUT], + propertyData.min, propertyData.max, propertyData.step); + let elNumberY = createTupleNumberInput(elProperty, elementID, propertyData.subLabels[VECTOR_ELEMENTS.Y_INPUT], + propertyData.min, propertyData.max, propertyData.step); - let inputChangeFunction = createEmitVec2PropertyUpdateFunction(propertyName, elInputX, elInputY, + let inputChangeFunction = createEmitVec3PropertyUpdateFunction(propertyName, elNumberX.elInput, elNumberY.elInput, propertyData.multiplier, property.isParticleProperty); - elInputX.addEventListener('change', inputChangeFunction); - elInputY.addEventListener('change', inputChangeFunction); + elNumberX.setInputChangeFunction(inputChangeFunction); + elNumberY.setInputChangeFunction(inputChangeFunction); let elResult = []; - elResult[VECTOR_ELEMENTS.X_INPUT] = elInputX; - elResult[VECTOR_ELEMENTS.Y_INPUT] = elInputY; + elResult[VECTOR_ELEMENTS.X_INPUT] = elNumberX.elInput; + elResult[VECTOR_ELEMENTS.Y_INPUT] = elNumberY.elInput; return elResult; } @@ -1952,15 +1866,15 @@ function createColorProperty(property, elProperty) { elProperty.appendChild(elColorPicker); elProperty.appendChild(elTuple); - let elInputR = createTupleNumberInput(elTuple, elementID, "red", COLOR_MIN, COLOR_MAX, COLOR_STEP); - let elInputG = createTupleNumberInput(elTuple, elementID, "green", COLOR_MIN, COLOR_MAX, COLOR_STEP); - let elInputB = createTupleNumberInput(elTuple, elementID, "blue", COLOR_MIN, COLOR_MAX, COLOR_STEP); + let elNumberR = createTupleNumberInput(elTuple, elementID, "red", COLOR_MIN, COLOR_MAX, COLOR_STEP); + let elNumberG = createTupleNumberInput(elTuple, elementID, "green", COLOR_MIN, COLOR_MAX, COLOR_STEP); + let elNumberB = createTupleNumberInput(elTuple, elementID, "blue", COLOR_MIN, COLOR_MAX, COLOR_STEP); - let inputChangeFunction = createEmitColorPropertyUpdateFunction(propertyName, elInputR, elInputG, elInputB, - property.isParticleProperty); - elInputR.addEventListener('change', inputChangeFunction); - elInputG.addEventListener('change', inputChangeFunction); - elInputB.addEventListener('change', inputChangeFunction); + let inputChangeFunction = createEmitColorPropertyUpdateFunction(propertyName, elNumberR.elInput, elNumberG.elInput, + elNumberB.elInput, property.isParticleProperty); + elNumberR.setInputChangeFunction(inputChangeFunction); + elNumberG.setInputChangeFunction(inputChangeFunction); + elNumberB.setInputChangeFunction(inputChangeFunction); let colorPickerID = "#" + elementID; colorPickers[colorPickerID] = $(colorPickerID).colpick({ @@ -1989,9 +1903,9 @@ function createColorProperty(property, elProperty) { let elResult = []; elResult[COLOR_ELEMENTS.COLOR_PICKER] = elColorPicker; - elResult[COLOR_ELEMENTS.RED_INPUT] = elInputR; - elResult[COLOR_ELEMENTS.GREEN_INPUT] = elInputG; - elResult[COLOR_ELEMENTS.BLUE_INPUT] = elInputB; + elResult[COLOR_ELEMENTS.RED_INPUT] = elNumberR.elInput; + elResult[COLOR_ELEMENTS.GREEN_INPUT] = elNumberG.elInput; + elResult[COLOR_ELEMENTS.BLUE_INPUT] = elNumberB.elInput; return elResult; } @@ -2125,7 +2039,7 @@ function createButtonsProperty(property, elProperty, elLabel) { } if (propertyData.buttons !== undefined) { - addButtons(elProperty, elementID, propertyData.buttons, hasLabel); + addButtons(elProperty, elementID, propertyData.buttons, false); } return elProperty; @@ -2134,25 +2048,17 @@ function createButtonsProperty(property, elProperty, elLabel) { function createTupleNumberInput(elTuple, propertyElementID, subLabel, min, max, step) { let elementID = propertyElementID + "-" + subLabel.toLowerCase(); - let elDiv = document.createElement('div'); let elLabel = document.createElement('label'); elLabel.className = subLabel; elLabel.innerText = subLabel[0].toUpperCase() + subLabel.slice(1); elLabel.setAttribute("for", elementID); - let elInput = document.createElement('input'); - elInput.className = subLabel + " number-slider"; - elInput.setAttribute("id", elementID); - elInput.setAttribute("type", "number"); - elInput.setAttribute("min", min); - elInput.setAttribute("max", max); - elInput.setAttribute("step", step); + let elDraggableNumber = new DraggableNumber(min, max, step); + elDraggableNumber.elInput.setAttribute("id", elementID); + elDraggableNumber.elDiv.insertBefore(elLabel, elDraggableNumber.elLeftArrow); + elTuple.appendChild(elDraggableNumber.elDiv); - elDiv.appendChild(elLabel); - elDiv.appendChild(elInput); - elTuple.appendChild(elDiv); - - return elInput; + return elDraggableNumber; } function addButtons(elProperty, propertyID, buttons, newRow) { @@ -2720,12 +2626,6 @@ function createProperty(propertyData, propertyElementID, propertyName, propertyI property.elInput = createNumberProperty(property, elProperty); break; } - case 'slider': { - let elSlider = createSliderProperty(property, elProperty); - property.elSlider = elSlider[SLIDER_ELEMENTS.SLIDER]; - property.elInput = elSlider[SLIDER_ELEMENTS.NUMBER_INPUT]; - break; - } case 'vec3': { let elVec3 = createVec3Property(property, elProperty); property.elInputX = elVec3[VECTOR_ELEMENTS.X_INPUT]; From 5bb2378cd90a44e921e4b24cde6613a6e9510d77 Mon Sep 17 00:00:00 2001 From: amantley Date: Fri, 9 Nov 2018 15:10:14 -0800 Subject: [PATCH 182/286] changes to get the engineer working --- .../qml/hifi/tablet/OpenVrConfiguration.qml | 4 +-- interface/src/avatar/MyAvatar.cpp | 2 +- libraries/animation/src/AnimSkeleton.cpp | 25 ++++++++++++++++--- libraries/fbx/src/FBXReader.cpp | 4 +-- 4 files changed, 27 insertions(+), 8 deletions(-) diff --git a/interface/resources/qml/hifi/tablet/OpenVrConfiguration.qml b/interface/resources/qml/hifi/tablet/OpenVrConfiguration.qml index 2fc5cc4196..f91642105f 100644 --- a/interface/resources/qml/hifi/tablet/OpenVrConfiguration.qml +++ b/interface/resources/qml/hifi/tablet/OpenVrConfiguration.qml @@ -9,9 +9,9 @@ import QtQuick 2.5 import QtGraphicalEffects 1.0 -import stylesUit 1.0 +import "../../styles-uit" import "../../controls" -import controlsUit 1.0 as HifiControls +import "../../controls-uit" as HifiControls import "." diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 502be9d57c..8d47591d40 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -431,7 +431,7 @@ void MyAvatar::reset(bool andRecenter, bool andReload, bool andHead) { _wasPushing = _isPushing = _isBraking = false; _follow.deactivate(); if (andReload) { - //_skeletonModel->reset(); + _skeletonModel->reset(); } if (andHead) { // which drives camera in desktop getHead()->reset(); diff --git a/libraries/animation/src/AnimSkeleton.cpp b/libraries/animation/src/AnimSkeleton.cpp index 1d4d662928..aec969af69 100644 --- a/libraries/animation/src/AnimSkeleton.cpp +++ b/libraries/animation/src/AnimSkeleton.cpp @@ -31,8 +31,11 @@ AnimSkeleton::AnimSkeleton(const HFMModel& hfmModel) { buildSkeletonFromJoints(joints, hfmModel.jointRotationOffsets); // add offsets for spine2 and the neck - - + static bool once = true; + qCDebug(animation) << "the hfm model path is " << hfmModel.originalURL; + if (once && hfmModel.originalURL == "/angus/avatars/engineer_hifinames/engineer_hifinames/engineer_hifinames.fbx") { + //if (once && hfmModel.originalURL == "/angus/avatars/pei_z_neckNexX_spine2NegY_fwd/pei_z_neckNexX_spine2NegY_fwd/pei_z_neckNexX_spine2NegY_fwd.fbx") { + once = false; for (int i = 0; i < (int)hfmModel.meshes.size(); i++) { const HFMMesh& mesh = hfmModel.meshes.at(i); for (int j = 0; j < mesh.clusters.size(); j++) { @@ -44,6 +47,15 @@ AnimSkeleton::AnimSkeleton(const HFMModel& hfmModel) { // AJT: mutate bind pose! this allows us to oreint the skeleton back into the authored orientaiton before // rendering, with no runtime overhead. // this works if clusters match joints one for one. + if (hfmModel.jointRotationOffsets.contains(cluster.jointIndex)) { + qCDebug(animation) << "found a joint offset to add " << cluster.jointIndex << " " << offset2 << " cluster " << cluster.jointIndex; + AnimPose localOffset(hfmModel.jointRotationOffsets[cluster.jointIndex], glm::vec3()); + //AnimPose localOffset(offset2, glm::vec3()); + cluster.inverseBindMatrix = (glm::mat4)localOffset.inverse() * cluster.inverseBindMatrix; + cluster.inverseBindTransform.evalFromRawMatrix(cluster.inverseBindMatrix); + } + + /* if (cluster.jointIndex == 62) { qCDebug(animation) << "Neck"; @@ -55,14 +67,16 @@ AnimSkeleton::AnimSkeleton(const HFMModel& hfmModel) { } if (cluster.jointIndex == 13) { qCDebug(animation) << "Spine2"; - qCDebug(animation) << "found a joint offset to add " << cluster.jointIndex << " " << offset1<< " cluster " << cluster.jointIndex; + qCDebug(animation) << "found a joint offset to add " << cluster.jointIndex << " " << offset1 << " cluster " << cluster.jointIndex; AnimPose localOffset(hfmModel.jointRotationOffsets[cluster.jointIndex], glm::vec3()); //AnimPose localOffset(offset1, glm::vec3()); cluster.inverseBindMatrix = (glm::mat4)localOffset.inverse() * cluster.inverseBindMatrix; cluster.inverseBindTransform.evalFromRawMatrix(cluster.inverseBindMatrix); } + */ } } + } @@ -247,6 +261,7 @@ void AnimSkeleton::buildSkeletonFromJoints(const std::vector& joints, } } + for (int k = 0; k < _jointsSize; k++) { int parentIndex2 = getParentIndex(k); if (jointOffsets.contains(k)) { @@ -261,6 +276,10 @@ void AnimSkeleton::buildSkeletonFromJoints(const std::vector& joints, _relativeDefaultPoses.push_back(_absoluteDefaultPoses[k]); } } + + // re-compute relative poses + //_relativeDefaultPoses = _absoluteDefaultPoses; + //convertAbsolutePosesToRelative(_relativeDefaultPoses); for (int i = 0; i < _jointsSize; i++) { diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index 6ae2b69e60..6213cc458b 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -2031,8 +2031,8 @@ HFMModel* FBXReader::extractHFMModel(const QVariantHash& mapping, const QString& } qCDebug(modelformat) << "Joint Rotation Offset added to Rig._jointRotationOffsets : " << " jointName: " << jointName << " jointIndex: " << jointIndex << " rotation offset: " << rotationOffset; } - hfmModel.jointRotationOffsets.insert(13, glm::quat(0.5f, 0.5f, 0.5f, -0.5f)); - hfmModel.jointRotationOffsets.insert(62, glm::quat(0.7071f, 0.0f, -0.7071f, 0.0f)); + //hfmModel.jointRotationOffsets.insert(13, glm::quat(0.5f, 0.5f, 0.5f, -0.5f)); + //hfmModel.jointRotationOffsets.insert(62, glm::quat(0.7071f, 0.0f, -0.7071f, 0.0f)); return hfmModelPtr; } From 08d9eda7d7115a785fb8f72fd63039df91a5378d Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 9 Nov 2018 17:08:10 -0800 Subject: [PATCH 183/286] fix property bugs / style issues --- scripts/system/html/css/edit-style.css | 55 +++++++++++----------- scripts/system/html/js/draggableNumber.js | 8 +++- scripts/system/html/js/entityProperties.js | 18 ++++--- 3 files changed, 46 insertions(+), 35 deletions(-) diff --git a/scripts/system/html/css/edit-style.css b/scripts/system/html/css/edit-style.css index 5107ad8a3d..cb8dd7bcbc 100644 --- a/scripts/system/html/css/edit-style.css +++ b/scripts/system/html/css/edit-style.css @@ -309,34 +309,6 @@ input[type=number].hover-down::-webkit-inner-spin-button:after { color: #ffffff; } -.draggable-number { - position: relative; - right: 10px; -} -.draggable-number span { - position: relative; - top: -2px; - display: inline-block; - font-family: hifi-glyphs; - font-size: 20px; - z-index: 2; -} -.draggable-number.left-arrow { - left: 18px; - transform: rotate(180deg); -} -.draggable-number.right-arrow { - right: 18px; -} -.draggable-number input { -} -.draggable-number input::selection { -} -.draggable-number input::-webkit-inner-spin-button { - -webkit-appearance: none; - visibility: hidden; -} - input[type=range] { -webkit-appearance: none; background: #2e2e2e; @@ -903,6 +875,33 @@ div.refresh input[type="button"] { clear: both; } +.draggable-number { + position: relative; + right: 10px; +} +.draggable-number span { + position: relative; + top: -2px; + display: inline-block; + font-family: hifi-glyphs; + font-size: 20px; + z-index: 2; +} +.draggable-number.left-arrow { + left: 17px; + transform: rotate(180deg); +} +.draggable-number.right-arrow { + right: 17px; +} +.draggable-number.fstuple span { + top: 0; +} +.draggable-number input::-webkit-inner-spin-button { + -webkit-appearance: none; + visibility: hidden; +} + .row .property { width: auto; display: inline-block; diff --git a/scripts/system/html/js/draggableNumber.js b/scripts/system/html/js/draggableNumber.js index 62fa02c6f9..30ad6f1b5c 100644 --- a/scripts/system/html/js/draggableNumber.js +++ b/scripts/system/html/js/draggableNumber.js @@ -15,6 +15,7 @@ function DraggableNumber(min, max, step) { this.max = max; this.step = step !== undefined ? step : 1; this.startEvent = null; + this.inputChangeFunction = null; this.initialize(); } @@ -41,7 +42,9 @@ DraggableNumber.prototype = { newValue = this.max; } this.elInput.value = newValue; - this.inputChangeFunction(); + if (this.inputChangeFunction) { + this.inputChangeFunction(); + } this.startEvent = event; } }, @@ -53,6 +56,9 @@ DraggableNumber.prototype = { }, setInputChangeFunction: function(inputChangeFunction) { + if (this.inputChangeFunction) { + this.elInput.removeEventListener('change', this.inputChangeFunction); + } this.inputChangeFunction = inputChangeFunction.bind(this.elInput); this.elInput.addEventListener('change', this.inputChangeFunction); }, diff --git a/scripts/system/html/js/entityProperties.js b/scripts/system/html/js/entityProperties.js index 5ea810a069..204b97918e 100644 --- a/scripts/system/html/js/entityProperties.js +++ b/scripts/system/html/js/entityProperties.js @@ -254,7 +254,7 @@ const GROUPS = [ buttons: [ { id: "copy", label: "Copy from Skybox", className: "black", onClick: copySkyboxURLToAmbientURL } ], propertyID: "copyURLToAmbient", - showPropertyRule: { "skyboxMode": "enabled" }, + showPropertyRule: { "ambientLightMode": "enabled" }, }, { label: "Haze", @@ -1772,9 +1772,14 @@ function createNumberProperty(property, elProperty) { let elementID = property.elementID; let propertyData = property.data; - elProperty.className = "property draggable-number"; + elProperty.className = "draggable-number"; let elDraggableNumber = new DraggableNumber(propertyData.min, propertyData.max, propertyData.step); + + let defaultValue = propertyData.defaultValue; + if (defaultValue !== undefined) { + elDraggableNumber.elInput.value = defaultValue; + } let inputChangeFunction = createEmitNumberPropertyUpdateFunction(propertyName, propertyData.multiplier, propertyData.decimals, property.isParticleProperty); elDraggableNumber.setInputChangeFunction(inputChangeFunction); @@ -1839,7 +1844,7 @@ function createVec2Property(property, elProperty) { let elNumberY = createTupleNumberInput(elProperty, elementID, propertyData.subLabels[VECTOR_ELEMENTS.Y_INPUT], propertyData.min, propertyData.max, propertyData.step); - let inputChangeFunction = createEmitVec3PropertyUpdateFunction(propertyName, elNumberX.elInput, elNumberY.elInput, + let inputChangeFunction = createEmitVec2PropertyUpdateFunction(propertyName, elNumberX.elInput, elNumberY.elInput, propertyData.multiplier, property.isParticleProperty); elNumberX.setInputChangeFunction(inputChangeFunction); elNumberY.setInputChangeFunction(inputChangeFunction); @@ -1871,7 +1876,7 @@ function createColorProperty(property, elProperty) { let elNumberB = createTupleNumberInput(elTuple, elementID, "blue", COLOR_MIN, COLOR_MAX, COLOR_STEP); let inputChangeFunction = createEmitColorPropertyUpdateFunction(propertyName, elNumberR.elInput, elNumberG.elInput, - elNumberB.elInput, property.isParticleProperty); + elNumberB.elInput, property.isParticleProperty); elNumberR.setInputChangeFunction(inputChangeFunction); elNumberG.setInputChangeFunction(inputChangeFunction); elNumberB.setInputChangeFunction(inputChangeFunction); @@ -2055,6 +2060,7 @@ function createTupleNumberInput(elTuple, propertyElementID, subLabel, min, max, let elDraggableNumber = new DraggableNumber(min, max, step); elDraggableNumber.elInput.setAttribute("id", elementID); + elDraggableNumber.elDiv.className += " fstuple"; elDraggableNumber.elDiv.insertBefore(elLabel, elDraggableNumber.elLeftArrow); elTuple.appendChild(elDraggableNumber.elDiv); @@ -2766,10 +2772,10 @@ function loaded() { if (propertyData.indentedLabel || propertyData.showPropertyRule !== undefined) { let elSpan = document.createElement('span'); elSpan.className = 'indented'; - elSpan.innerText = propertyData.label; + elSpan.innerText = propertyData.label !== undefined ? propertyData.label : ""; elLabel.appendChild(elSpan); } else { - elLabel.innerText = propertyData.label; + elLabel.innerText = propertyData.label !== undefined ? propertyData.label : ""; } elContainer.appendChild(elLabel); } else { From 0160ab23ab2efd745238c582ddcf6b864b08a4cc Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 9 Nov 2018 17:23:51 -0800 Subject: [PATCH 184/286] fix color pickers --- scripts/system/html/css/edit-style.css | 3 +++ scripts/system/html/js/entityProperties.js | 6 +++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/scripts/system/html/css/edit-style.css b/scripts/system/html/css/edit-style.css index cb8dd7bcbc..7aa08b35cd 100644 --- a/scripts/system/html/css/edit-style.css +++ b/scripts/system/html/css/edit-style.css @@ -853,6 +853,9 @@ div.refresh input[type="button"] { border-color: #afafaf; } +.colpick { + z-index: 3; +} .colpick[disabled="disabled"] { display: none !important; } diff --git a/scripts/system/html/js/entityProperties.js b/scripts/system/html/js/entityProperties.js index 204b97918e..c26d581ec3 100644 --- a/scripts/system/html/js/entityProperties.js +++ b/scripts/system/html/js/entityProperties.js @@ -1892,9 +1892,9 @@ function createColorProperty(property, elProperty) { // The original color preview within the picker needs to be updated on show because // prior to the picker being shown we don't have access to the selections' starting color. colorPickers[colorPickerID].colpickSetColor({ - "r": elInputR.value, - "g": elInputG.value, - "b": elInputB.value + "r": elNumberR.elInput.value, + "g": elNumberG.elInput.value, + "b": elNumberB.elInput.value }); }, onHide: function(colpick) { From 4eab623fd8a22484c0d833bc1272aca79a6b990e Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 9 Nov 2018 17:25:48 -0800 Subject: [PATCH 185/286] remove debugPrint function --- scripts/system/html/js/draggableNumber.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/scripts/system/html/js/draggableNumber.js b/scripts/system/html/js/draggableNumber.js index 30ad6f1b5c..e961bbb4a7 100644 --- a/scripts/system/html/js/draggableNumber.js +++ b/scripts/system/html/js/draggableNumber.js @@ -6,10 +6,6 @@ // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -debugPrint = function (message) { - console.log(message); -}; - function DraggableNumber(min, max, step) { this.min = min; this.max = max; From a535519fbcd8441076c87c03500a7bf7ff9472cd Mon Sep 17 00:00:00 2001 From: NissimHadar Date: Mon, 12 Nov 2018 11:00:24 -0800 Subject: [PATCH 186/286] Typo. --- tools/nitpick/src/TestRunner.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/nitpick/src/TestRunner.cpp b/tools/nitpick/src/TestRunner.cpp index 0e6acf073e..20f419a356 100644 --- a/tools/nitpick/src/TestRunner.cpp +++ b/tools/nitpick/src/TestRunner.cpp @@ -488,7 +488,7 @@ void TestRunner::runInterfaceWithTestScript() { " --testScript " + testScript + " quitWhenFinished" + " --testResultsLocation " + _snapshotFolder; - interfaceWorker->setCommandLine(commandLine); + _interfaceWorker->setCommandLine(commandLine); emit startInterface(); #elif defined Q_OS_MAC // On The Mac, we need to resize Interface. The Interface window opens a few seconds after the process From 4a158ea789d3380f92cf2c0716e297185c372942 Mon Sep 17 00:00:00 2001 From: NissimHadar Date: Mon, 12 Nov 2018 11:07:18 -0800 Subject: [PATCH 187/286] Fixed Linux compilation error. --- tools/nitpick/src/ui/Nitpick.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/nitpick/src/ui/Nitpick.cpp b/tools/nitpick/src/ui/Nitpick.cpp index 9ae21d0233..a4aef8fad5 100644 --- a/tools/nitpick/src/ui/Nitpick.cpp +++ b/tools/nitpick/src/ui/Nitpick.cpp @@ -100,7 +100,7 @@ void Nitpick::on_tabWidget_currentChanged(int index) { // Enable the GitHub edit boxes as required #ifdef Q_OS_WIN if (index == 0 || index == 2 || index == 3) { -#elif defined Q_OS_MAC +#else if (index == 0 || index == 1 || index == 2) { #endif _ui.userLineEdit->setDisabled(false); From 92befbc5bf7371ac775f468e6969af1fd8d6745f Mon Sep 17 00:00:00 2001 From: amantley Date: Mon, 12 Nov 2018 11:12:56 -0800 Subject: [PATCH 188/286] implementing backup of cluster matrices --- libraries/fbx/src/FBX.h | 1 + libraries/fbx/src/FBXReader.cpp | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/libraries/fbx/src/FBX.h b/libraries/fbx/src/FBX.h index d4072f03d7..943db4b7fe 100644 --- a/libraries/fbx/src/FBX.h +++ b/libraries/fbx/src/FBX.h @@ -349,6 +349,7 @@ public: Extents meshExtents; QVector animationFrames; + std::vector> clusterBindMatrixOriginalValues; int getJointIndex(const QString& name) const { return jointIndices.value(name) - 1; } QStringList getJointNames() const; diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index 6213cc458b..a8cbcdcf25 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -2034,6 +2034,14 @@ HFMModel* FBXReader::extractHFMModel(const QVariantHash& mapping, const QString& //hfmModel.jointRotationOffsets.insert(13, glm::quat(0.5f, 0.5f, 0.5f, -0.5f)); //hfmModel.jointRotationOffsets.insert(62, glm::quat(0.7071f, 0.0f, -0.7071f, 0.0f)); + for (int i = 0; i < (int)hfmModel.meshes.size(); i++) { + const HFMMesh& mesh = hfmModel.meshes.at(i); + for (int j = 0; j < mesh.clusters.size(); j++) { + const HFMCluster& cluster = mesh.clusters.at(j); + hfmModel.clusterBindMatrixOriginalValues[i][cluster.jointIndex] = cluster.inverseBindMatrix; + } + } + return hfmModelPtr; } From 05033b7da1c5343d35aff316444c861e56edecb8 Mon Sep 17 00:00:00 2001 From: NissimHadar Date: Mon, 12 Nov 2018 12:19:32 -0800 Subject: [PATCH 189/286] Echo status to screen --- tools/nitpick/src/TestRunner.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tools/nitpick/src/TestRunner.cpp b/tools/nitpick/src/TestRunner.cpp index 0e6acf073e..70321e088e 100644 --- a/tools/nitpick/src/TestRunner.cpp +++ b/tools/nitpick/src/TestRunner.cpp @@ -173,7 +173,9 @@ void TestRunner::setWorkingFolder() { } script.write("#!/bin/sh\n\n"); + script.write("echo resizing interface"); script.write(("osascript " + _workingFolder + "/setInterfaceSizeAndPosition.scpt\n").toStdString().c_str()); + script.write("echo resize complete"); script.close(); script.setPermissions(QFile::ReadOwner | QFile::WriteOwner | QFile::ExeOwner); #endif From 8c347fae7082a746d594f3a3b5fb743618addcc8 Mon Sep 17 00:00:00 2001 From: Simon Walton Date: Mon, 12 Nov 2018 12:45:01 -0800 Subject: [PATCH 190/286] Fixes for domain server use of changed OctreeDataUtils --- libraries/octree/src/OctreeDataUtils.cpp | 33 +++++++++++++++++++----- libraries/octree/src/OctreeDataUtils.h | 3 +++ 2 files changed, 29 insertions(+), 7 deletions(-) diff --git a/libraries/octree/src/OctreeDataUtils.cpp b/libraries/octree/src/OctreeDataUtils.cpp index a94fa31800..255933a783 100644 --- a/libraries/octree/src/OctreeDataUtils.cpp +++ b/libraries/octree/src/OctreeDataUtils.cpp @@ -44,6 +44,7 @@ namespace { } return !doc->isNull(); } + } // Anon namespace. bool OctreeUtils::RawOctreeData::readOctreeDataInfoFromJSON(QJsonObject root) { @@ -62,6 +63,7 @@ bool OctreeUtils::RawOctreeData::readOctreeDataInfoFromMap(const QVariantMap& ma dataVersion = map["DataVersion"].toInt(); version = map["Version"].toInt(); } + readSubclassData(map); return true; } @@ -85,13 +87,20 @@ bool OctreeUtils::RawOctreeData::readOctreeDataInfoFromData(QByteArray data) { // Reads octree file and parses it into a RawOctreeData object. // Returns false if readOctreeFile fails. bool OctreeUtils::RawOctreeData::readOctreeDataInfoFromFile(QString path) { - QJsonDocument doc; - if (!readOctreeFile(path, &doc)) { + QFile file(path); + if (!file.open(QIODevice::ReadOnly)) { + qCritical() << "Cannot open json file for reading: " << path; return false; } - auto root = doc.object(); - return readOctreeDataInfoFromJSON(root); + QByteArray data = file.readAll(); + QByteArray jsonData; + + if (!gunzip(data, jsonData)) { + jsonData = data; + } + + return readOctreeDataInfoFromData(jsonData); } QByteArray OctreeUtils::RawOctreeData::toByteArray() { @@ -139,8 +148,18 @@ void OctreeUtils::RawEntityData::readSubclassData(const QJsonObject& root) { } } -void OctreeUtils::RawEntityData::writeSubclassData(QJsonObject& root) const { - root["Entities"] = entityData; +void OctreeUtils::RawEntityData::readSubclassData(const QVariantMap& root) { + variantEntityData = root["Entities"].toList(); } -PacketType OctreeUtils::RawEntityData::dataPacketType() const { return PacketType::EntityData; } \ No newline at end of file +void OctreeUtils::RawEntityData::writeSubclassData(QJsonObject& root) const { + //root["Entities"] = entityData; + QJsonArray entitiesJsonArray; + for (auto entityIter = variantEntityData.begin(); entityIter != variantEntityData.end(); ++entityIter) { + entitiesJsonArray += entityIter->toJsonObject(); + } + + root["Entities"] = entitiesJsonArray; +} + +PacketType OctreeUtils::RawEntityData::dataPacketType() const { return PacketType::EntityData; } diff --git a/libraries/octree/src/OctreeDataUtils.h b/libraries/octree/src/OctreeDataUtils.h index 0dfa99a9a0..20092cd451 100644 --- a/libraries/octree/src/OctreeDataUtils.h +++ b/libraries/octree/src/OctreeDataUtils.h @@ -34,6 +34,7 @@ public: virtual PacketType dataPacketType() const; virtual void readSubclassData(const QJsonObject& root) { } + virtual void readSubclassData(const QVariantMap& root) { } virtual void writeSubclassData(QJsonObject& root) const { } void resetIdAndVersion(); @@ -50,9 +51,11 @@ class RawEntityData : public RawOctreeData { public: PacketType dataPacketType() const override; void readSubclassData(const QJsonObject& root) override; + void readSubclassData(const QVariantMap& root) override; void writeSubclassData(QJsonObject& root) const override; QJsonArray entityData; + QVariantList variantEntityData; }; } From bcd651a65dc5cbf690b1f12e4ded566fc53988a7 Mon Sep 17 00:00:00 2001 From: amantley Date: Mon, 12 Nov 2018 14:39:48 -0800 Subject: [PATCH 191/286] adding the member variable to keep track of the orig bind matrices to fbx.h --- libraries/animation/src/AnimSkeleton.cpp | 4 ++++ libraries/fbx/src/FBX.h | 2 +- libraries/fbx/src/FBXReader.cpp | 11 ++++++++--- 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/libraries/animation/src/AnimSkeleton.cpp b/libraries/animation/src/AnimSkeleton.cpp index aec969af69..aec4ae6dda 100644 --- a/libraries/animation/src/AnimSkeleton.cpp +++ b/libraries/animation/src/AnimSkeleton.cpp @@ -52,6 +52,10 @@ AnimSkeleton::AnimSkeleton(const HFMModel& hfmModel) { AnimPose localOffset(hfmModel.jointRotationOffsets[cluster.jointIndex], glm::vec3()); //AnimPose localOffset(offset2, glm::vec3()); cluster.inverseBindMatrix = (glm::mat4)localOffset.inverse() * cluster.inverseBindMatrix; + qCDebug(animation) << "the new bind matrix num: " << cluster.jointIndex << cluster.inverseBindMatrix; + //if ((hfmModel.clusterBindMatrixOriginalValues.size() > i) && (hfmModel.clusterBindMatrixOriginalValues[i].contains(cluster.jointIndex))) { + // qCDebug(animation) << "the saved orig matrix num: " << cluster.jointIndex << hfmModel.clusterBindMatrixOriginalValues[i][cluster.jointIndex]; + //} cluster.inverseBindTransform.evalFromRawMatrix(cluster.inverseBindMatrix); } diff --git a/libraries/fbx/src/FBX.h b/libraries/fbx/src/FBX.h index 943db4b7fe..342f3c456a 100644 --- a/libraries/fbx/src/FBX.h +++ b/libraries/fbx/src/FBX.h @@ -349,7 +349,7 @@ public: Extents meshExtents; QVector animationFrames; - std::vector> clusterBindMatrixOriginalValues; + std::vector> clusterBindMatrixOriginalValues; int getJointIndex(const QString& name) const { return jointIndices.value(name) - 1; } QStringList getJointNames() const; diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index a8cbcdcf25..ed5e349957 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -2033,15 +2033,20 @@ HFMModel* FBXReader::extractHFMModel(const QVariantHash& mapping, const QString& } //hfmModel.jointRotationOffsets.insert(13, glm::quat(0.5f, 0.5f, 0.5f, -0.5f)); //hfmModel.jointRotationOffsets.insert(62, glm::quat(0.7071f, 0.0f, -0.7071f, 0.0f)); - + /* for (int i = 0; i < (int)hfmModel.meshes.size(); i++) { const HFMMesh& mesh = hfmModel.meshes.at(i); + QMap tempBindMat; for (int j = 0; j < mesh.clusters.size(); j++) { const HFMCluster& cluster = mesh.clusters.at(j); - hfmModel.clusterBindMatrixOriginalValues[i][cluster.jointIndex] = cluster.inverseBindMatrix; + tempBindMat.insert(cluster.jointIndex, cluster.inverseBindMatrix); + //if(hfmModel.clusterBindMatrixOriginalValues[i]) + //hfmModel.clusterBindMatrixOriginalValues[i].insert(cluster.jointIndex,Matrices::IDENTITY);// cluster.inverseBindMatrix; + //glm::mat4 testMat = cluster.inverseBindMatrix; } + hfmModel.clusterBindMatrixOriginalValues.push_back(tempBindMat); } - + */ return hfmModelPtr; } From 6ee837d47c2f198e2284828baefff5687cd86534 Mon Sep 17 00:00:00 2001 From: Simon Walton Date: Mon, 12 Nov 2018 15:43:09 -0800 Subject: [PATCH 192/286] OctreeDataUtils - use new json from DS also; allow upload of large domains --- domain-server/src/DomainServer.cpp | 13 +----- libraries/octree/src/OctreeDataUtils.cpp | 58 +++++++++--------------- libraries/octree/src/OctreeDataUtils.h | 8 +--- 3 files changed, 25 insertions(+), 54 deletions(-) diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index 8136bf3b53..f90155c786 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -3411,20 +3411,11 @@ void DomainServer::maybeHandleReplacementEntityFile() { } void DomainServer::handleOctreeFileReplacement(QByteArray octreeFile) { - //Assume we have compressed data - auto compressedOctree = octreeFile; - QByteArray jsonOctree; - - bool wasCompressed = gunzip(compressedOctree, jsonOctree); - if (!wasCompressed) { - // the source was not compressed, assume we were sent regular JSON data - jsonOctree = compressedOctree; - } - OctreeUtils::RawEntityData data; - if (data.readOctreeDataInfoFromData(jsonOctree)) { + if (data.readOctreeDataInfoFromData(octreeFile)) { data.resetIdAndVersion(); + QByteArray compressedOctree; gzip(data.toByteArray(), compressedOctree); // write the compressed octree data to a special file diff --git a/libraries/octree/src/OctreeDataUtils.cpp b/libraries/octree/src/OctreeDataUtils.cpp index 255933a783..8c6e45ae66 100644 --- a/libraries/octree/src/OctreeDataUtils.cpp +++ b/libraries/octree/src/OctreeDataUtils.cpp @@ -47,16 +47,6 @@ namespace { } // Anon namespace. -bool OctreeUtils::RawOctreeData::readOctreeDataInfoFromJSON(QJsonObject root) { - if (root.contains("Id") && root.contains("DataVersion") && root.contains("Version")) { - id = root["Id"].toVariant().toUuid(); - dataVersion = root["DataVersion"].toInt(); - version = root["Version"].toInt(); - } - readSubclassData(root); - return true; -} - bool OctreeUtils::RawOctreeData::readOctreeDataInfoFromMap(const QVariantMap& map) { if (map.contains("Id") && map.contains("DataVersion") && map.contains("Version")) { id = map["Id"].toUuid(); @@ -94,28 +84,25 @@ bool OctreeUtils::RawOctreeData::readOctreeDataInfoFromFile(QString path) { } QByteArray data = file.readAll(); - QByteArray jsonData; - if (!gunzip(data, jsonData)) { - jsonData = data; - } - - return readOctreeDataInfoFromData(jsonData); + return readOctreeDataInfoFromData(data); } QByteArray OctreeUtils::RawOctreeData::toByteArray() { - QJsonObject obj { - { "DataVersion", QJsonValue((qint64)dataVersion) }, - { "Id", QJsonValue(id.toString()) }, - { "Version", QJsonValue((qint64)version) }, - }; + QByteArray jsonString; - writeSubclassData(obj); + jsonString += QString(R"({ + "DataVersion": %1, +)").arg(dataVersion); - QJsonDocument doc; - doc.setObject(obj); + writeSubclassData(jsonString); - return doc.toJson(); + jsonString += QString(R"(, + "Id": "%1", + "Version": %2 +})").arg(id.toString()).arg(version); + + return jsonString; } QByteArray OctreeUtils::RawOctreeData::toGzippedByteArray() { @@ -142,24 +129,21 @@ void OctreeUtils::RawOctreeData::resetIdAndVersion() { qDebug() << "Reset octree data to: " << id << dataVersion; } -void OctreeUtils::RawEntityData::readSubclassData(const QJsonObject& root) { - if (root.contains("Entities")) { - entityData = root["Entities"].toArray(); - } -} - void OctreeUtils::RawEntityData::readSubclassData(const QVariantMap& root) { variantEntityData = root["Entities"].toList(); } -void OctreeUtils::RawEntityData::writeSubclassData(QJsonObject& root) const { - //root["Entities"] = entityData; - QJsonArray entitiesJsonArray; +void OctreeUtils::RawEntityData::writeSubclassData(QByteArray& root) const { + root += " \"Entities\": ["; for (auto entityIter = variantEntityData.begin(); entityIter != variantEntityData.end(); ++entityIter) { - entitiesJsonArray += entityIter->toJsonObject(); + if (entityIter != variantEntityData.begin()) { + root += ","; + } + root += "\n "; + // Convert to string and remove trailing LF. + root += QJsonDocument(entityIter->toJsonObject()).toJson().chopped(1); } - - root["Entities"] = entitiesJsonArray; + root += "]"; } PacketType OctreeUtils::RawEntityData::dataPacketType() const { return PacketType::EntityData; } diff --git a/libraries/octree/src/OctreeDataUtils.h b/libraries/octree/src/OctreeDataUtils.h index 20092cd451..642c00a675 100644 --- a/libraries/octree/src/OctreeDataUtils.h +++ b/libraries/octree/src/OctreeDataUtils.h @@ -33,9 +33,8 @@ public: virtual PacketType dataPacketType() const; - virtual void readSubclassData(const QJsonObject& root) { } virtual void readSubclassData(const QVariantMap& root) { } - virtual void writeSubclassData(QJsonObject& root) const { } + virtual void writeSubclassData(QByteArray& root) const { } void resetIdAndVersion(); QByteArray toByteArray(); @@ -43,18 +42,15 @@ public: bool readOctreeDataInfoFromData(QByteArray data); bool readOctreeDataInfoFromFile(QString path); - bool readOctreeDataInfoFromJSON(QJsonObject root); bool readOctreeDataInfoFromMap(const QVariantMap& map); }; class RawEntityData : public RawOctreeData { public: PacketType dataPacketType() const override; - void readSubclassData(const QJsonObject& root) override; void readSubclassData(const QVariantMap& root) override; - void writeSubclassData(QJsonObject& root) const override; + void writeSubclassData(QByteArray& root) const override; - QJsonArray entityData; QVariantList variantEntityData; }; From 25df605459c45669f822e3e95d0b7e5b09ace4bb Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 8 Nov 2018 14:53:33 -0800 Subject: [PATCH 193/286] Update the styling for the Entity Properties window --- scripts/system/html/css/edit-style.css | 403 +++++----- scripts/system/html/css/jsoneditor.css | 10 +- scripts/system/html/entityProperties.html | 13 + scripts/system/html/js/entityProperties.js | 869 ++++++++++++--------- 4 files changed, 717 insertions(+), 578 deletions(-) diff --git a/scripts/system/html/css/edit-style.css b/scripts/system/html/css/edit-style.css index e4b33414ab..c09655b451 100644 --- a/scripts/system/html/css/edit-style.css +++ b/scripts/system/html/css/edit-style.css @@ -70,12 +70,11 @@ } body { - padding: 21px 21px 21px 21px; color: #afafaf; background-color: #404040; font-family: Raleway-Regular; - font-size: 15px; + font-size: 12px; -webkit-touch-callout: none; -webkit-user-select: none; @@ -425,9 +424,9 @@ input[type=checkbox] { } input[type=checkbox] + label { padding-left: 24px; - background-position-y: 6px; background-repeat: no-repeat; background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAACpSURBVDhPY2xoaGD68+dPMSMjY9L////VgTQjAw4AlH8PxLOPHj1azWxjY1MBVNsBFBfBpwkEgNKcQGwtJyfHyATkF0KEiQdAzYlMQEIUyicFyDD9+/ePgRxMvsb///4zkIOZ/v0HmkAGHginYjGNGAzS+BpdkAj8mun/3//92DyPD//993cG88nTJ4+Zm5p/BSZeJYb/DEJADEzNOPF7hn8Mk69cvVIPAHN5pyfo70F5AAAAAElFTkSuQmCC); + cursor: pointer; } input[type=checkbox]:enabled + label:hover { background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAClSURBVDhPY2hoaGD6//9/6b9//64D8T8gGycASr/7+/dv5/79+1kYgIxKqDjRAKiniRFIv2JgYBAFYlLAE0aQ66AckgDjjx8/yNP44cMH8jS+fPmSPI0PHz4kT+PNmzfJ03jp0iXyNJ46dYo8jYcPHyYnAbxm+vnzZz8wLhlIwd+/f5/BrKSkdExCQuLrnz9/lIBpUAiIQekXF34PTGmTT548WQ8AokXg+rhVtPYAAAAASUVORK5CYII=); @@ -455,6 +454,15 @@ input[type=checkbox]:checked + label:hover { color: #ffffff; } +.icon { + font-family: HiFi-Glyphs; + color: white; +} + +#property-type-icon { + font-size: 50px; +} + .selectable { -webkit-touch-callout: text; -webkit-user-select: text; @@ -483,9 +491,11 @@ input[type=checkbox]:checked + label:hover { #properties-list { display: flex; flex-direction: column; + + margin-top: 16px; } -#properties-list fieldset { +#properties-list .fieldset { position: relative; /* 0.1px on the top is to prevent margin collapsing between this and it's first child */ margin: 0 -21px 21px -21px; @@ -495,71 +505,101 @@ input[type=checkbox]:checked + label:hover { box-shadow: 0 -1px 0 rgb(37,37,37); } -#properties-list fieldset.fstuple, #properties-list fieldset.fsrow { +#properties-list .fieldset.fstuple, #properties-list .fieldset.fsrow { margin-top: 21px; border: none; box-shadow: none; } -#properties-list > fieldset[data-collapsed="true"] + fieldset { +#properties-list > .fieldset[data-collapsed="true"] + .fieldset { margin-top: 0; } -#properties-list > fieldset[data-collapsed="true"] > *:not(legend) { +#properties-list > .fieldset[data-collapsed="true"] > *:not(div.legend) { display: none !important; } -#properties-list legend + fieldset { - margin-top: 0; - border: none; - box-shadow: none; -} - -#properties-list > fieldset > legend { - position: relative; - display: table; - width: 100%; - margin: 21px -21px 0 -21px; - padding: 14px 21px 0 21px; - font-family: Raleway-Regular; - font-size: 12px; - color: #afafaf; - height: 28px; - text-transform: uppercase; - outline: none; - background-color: #404040; - border: none; +.section-header { + padding: 0 16px; border-top: 1px rgb(90,90,90) solid; - box-shadow: 0 -1px 0 rgb(37,37,37), 0 4px 4px 0 rgba(0,0,0,0.75); + box-shadow: 1px -1px 0 rgb(37,37,37); + border-bottom: 1px solid rgb(37, 37, 37); } div.section-header, hr { - display: table; - width: 100%; - margin: 21px -21px 0 -21px; - padding: 14px 21px 0 21px; + display: flex; + flex-flow: row nowrap; + + padding: 10px 16px; font-family: Raleway-Regular; font-size: 12px; color: #afafaf; height: 28px; text-transform: uppercase; outline: none; + margin-bottom: 10px; + align-items: center; } -div.section-header:first-child { - margin-top: -2px; - padding-top: 0; - background: none; - height: auto; +.section.minor { + margin: 0 21px; + box-shadow: 1px -1px 0 rgb(37,37,37); + border-left: 1px solid #575757; } -#properties-list > fieldset > legend span, .section-header span { - font-family: HiFi-Glyphs; +.container.property { + padding: 0 16px; +} + +.stretch { + width: 100%; +} + +div.section-header .label { + width: 100%; +} + +.section.minor div.section-header { + border-right: 0; +} + +div.section[collapsed="true"] > .container { + display: none; +} + +div.section[collapsed="true"], div.section[collapsed="true"] > .section-header { + margin-bottom: 0; +} + +.section.major { + margin-bottom: 20px; +} + +.section.minor.last { + margin-bottom: 20px; + border-bottom: 1px solid rgb(37,37,37); +} + +.section-header { + background-color: #373737; +} + +.section-header { + cursor: pointer; +} + +.section-header span { font-size: 30px; - float: right; - position: absolute; - top: 4px; - right: 13px; + font-family: HiFi-Glyphs; +} + +.triple-label { + text-transform: uppercase; + padding: 6px 0; +} + +.triple-item { + margin-right: 10px; } .section-header[collapsed="true"] { @@ -582,9 +622,6 @@ hr { } .property { - display: table; - width: 100%; - margin-top: 21px; min-height: 28px; } @@ -592,6 +629,10 @@ hr { width: auto; } +span.indented { + padding-left: 16px; +} + .property label, .number label { display: table-cell; vertical-align: middle; @@ -604,13 +645,13 @@ hr { font-size: 13px; } -.property legend, .number legend { +.property div.legend, .number div.legend { display: table-cell; vertical-align: middle; font-family: Raleway-SemiBold; font-size: 14px; } -.property legend .unit, .number legend .unit { +.property div.legend .unit, .number div.legend .unit { margin-left: 8px; font-family: Raleway-Light; font-size: 13px; @@ -623,16 +664,26 @@ hr { .value label { display: inline-block; vertical-align: top; - width: 48px; } -.value legend { +.value div.legend { display: inline-block; vertical-align: top; width: 48px; } .value span { - font-family: FiraSans-SemiBold; font-size: 15px; + margin-right: 4px; +} + +#placeholder-property-type { + display: flex; + align-items: center; + width: auto; + margin-right: 20px; +} + +#placeholder-property-locked { + margin-left: 6px; } .checkbox + .checkbox { @@ -659,33 +710,6 @@ hr { height: 1.8rem; } -.text label, .url label, .number label, .textarea label, .xy label, .wh label, .rgb label, .xyz label, .pyr label, .dropdown label, .gen label { - float: left; - margin-left: 1px; - margin-bottom: 3px; - margin-top: -2px; -} - -.text legend, .url legend, .number legend, .textarea legend, .xy legend, .wh legend, .rgb legend, .xyz legend, .pyr legend, .dropdown legend, .gen legend { - float: left; - margin-left: 1px; - margin-bottom: 3px; - margin-top: -2px; -} - -.xy > div, .wh > div, .xyz > div, .pyr > div, .gen > div { - clear: both; -} - -.number > input { - clear: both; - float: left; -} -.number > span { - clear: both; - float: left; -} - .dropdown { position: relative; margin-bottom: -17px; @@ -697,6 +721,7 @@ hr { .dropdown dl { clear: both; + cursor: pointer; } .dropdown dl { font-family: FiraSans-SemiBold; @@ -705,7 +730,7 @@ hr { height: 28px; padding: 0 28px 0 12px; color: #afafaf; - background: linear-gradient(#7d7d7d 20%, #686a68 100%); + background: #575757; position: relative; } .dropdown dl[dropped="true"] { @@ -812,27 +837,20 @@ div.refresh input[type="button"] { .color-picker { box-sizing: border-box; - float: left; - margin-bottom: 21px; - width: 36px; - height: 36px; - border: 4px solid #afafaf; - border-radius: 4px; - background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAABTSURBVChTjcxBDoAwCETRwTs33sFjwB6uaCE1Ggvav5qQF7CSqu40dllHjYiOT3gh3yV8Ii+Fb+RNMEP9hm3sKENmBhG5P1aImWMH/EMerSAAOAFgTC/R8ZXSXAAAAABJRU5ErkJggg==); - background-position: bottom right; - background-repeat: no-repeat; + width: 26px; + height: 26px; + border: 3px solid #2B2B2B; + cursor: pointer; } .color-picker:focus { outline: none; } .color-picker[active="true"] { border-color: #000; - background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAABDSURBVChTjcoLCkAhCETRNq0tf97Y5xGZ1gVJ45TH6njThIO+xk2UwhWFcEdH6JCqOuiQiMDi/hcii3crRRb/7ggAPvIMVihQwvSXAAAAAElFTkSuQmCC); } .color-picker[disabled="disabled"] { border-color: #afafaf; - background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAABTSURBVChTjcxBDoAwCETRwTs33sFjwB6uaCE1Ggvav5qQF7CSqu40dllHjYiOT3gh3yV8Ii+Fb+RNMEP9hm3sKENmBhG5P1aImWMH/EMerSAAOAFgTC/R8ZXSXAAAAABJRU5ErkJggg==); } .colpick[disabled="disabled"] { @@ -848,89 +866,15 @@ div.refresh input[type="button"] { clear: both; } -.rgb legend { +.rgb div.legend { float: left; margin-top: 10px; margin-left: 21px; } -.rgb legend + * { +.rgb div.legend + * { clear: both; } -.tuple div { - display: inline-block; - position: relative; - margin-right: 6px; -} -.tuple div:last-child { - margin-right: 0; -} - -.tuple label { - margin-right: -6px; -} - -.xy .tuple input { - padding-left: 25px; -} -.wh .tuple input { - padding-left: 45px; -} -.rgb .tuple input { - padding-left: 65px; -} -.xyz .tuple input { - padding-left: 25px; -} -.pyr .tuple input { - padding-left: 40px; -} - -.tuple div > label:first-child { - float: left; -} -.tuple div > label + input { - clear: both; - float: left; -} -.tuple div input + label { - display: inline !important; - float: none !important; - position: absolute; - margin-top: 8px; - margin-left: 6px; - left: 0; - font-family: FiraSans-SemiBold; - font-size: 12px; -} -.tuple .red + label, .tuple .x + label, .tuple .pitch + label, .tuple .width + label { - color: #e2334d; -} -.tuple .green + label, .tuple .y + label, .tuple .yaw + label, .tuple .height + label { - color: #1ac567; -} -.tuple .blue + label, .tuple .z + label, .tuple .roll + label { - color: #1080b8; -} - -.tuple .red:focus, .tuple .x:focus, .tuple .pitch:focus, .tuple .width:focus { - outline-color: #e2334d; -} -.tuple .green:focus, .tuple .y:focus, .tuple .yaw:focus, .tuple .height:focus { - outline-color: #1ac567; -} -.tuple .blue:focus, .tuple .z:focus, .tuple .roll:focus { - outline-color: #1080b8; -} - -.xyz .buttons input { - margin-top: 14px; -} -.xyz .buttons span { - word-wrap: nowrap; - white-space: nowrap; -} - .row .property { width: auto; display: inline-block; @@ -984,12 +928,12 @@ div.refresh input[type="button"] { width: 50%; } -#properties-list fieldset .two-column { +#properties-list .fieldset .two-column { padding-top: 10px; display: flex; } -#properties-list .two-column fieldset { +#properties-list .two-column .fieldset { width: 50%; margin: 0; padding: 0; @@ -1002,7 +946,7 @@ div.refresh input[type="button"] { top: -10px; } -#properties-list .two-column fieldset legend { +#properties-list .two-column .fieldset div.legend { width: 100%; margin: 21px -21px 0 -21px; padding: 16px 0 0 21px; @@ -1018,11 +962,11 @@ div.refresh input[type="button"] { margin-top: 6px; } -fieldset .checkbox-sub-props { +.fieldset .checkbox-sub-props { margin-top: 0; } -fieldset .checkbox-sub-props .property:first-child { +.fieldset .checkbox-sub-props .property:first-child { margin-top: 0; } @@ -1069,6 +1013,7 @@ textarea:enabled[scrolling="true"]::-webkit-resizer { body#entity-list-body { padding-bottom: 0; + margin: 16px; } #entity-list-header { @@ -1448,7 +1393,6 @@ th#entity-hasScript { border-top: none !important; box-shadow: none !important; margin-bottom: 5px !important; - top: -15px; } #properties-base #property-type-icon { @@ -1467,6 +1411,7 @@ th#entity-hasScript { display: inline-block; } +/* #properties-base #div-property-locked { position: absolute; top: 6px; @@ -1487,6 +1432,7 @@ th#entity-hasScript { #properties-base #div-property-visible label { background-position-y: 1px; } +*/ #properties-base .checkbox label span { font-family: HiFi-Glyphs; @@ -1513,16 +1459,17 @@ th#entity-hasScript { } input#property-scale-button-rescale { + margin-top: 6px; min-width: 50px; - margin-left: 6px; } input#property-scale-button-reset { + margin-top: 6px; margin-right: 0; } -#property-userData-button-clear, +#property-userData-button-edit, #property-materialData-button-clear { - margin-bottom: 10px; + margin: 6px 0 6px 0; } #property-userData-static, @@ -1544,15 +1491,6 @@ input#property-scale-button-reset { display: none; } -#div-property-serverScripts-status label { - position: relative; - top: 8px; -} -#property-serverScripts-status { - position: relative; - top: 5px; - right: -20px; -} #div-property-collisionSoundURL[style*="display: none"] + .property { margin-top: 0; @@ -1637,3 +1575,100 @@ input.rename-entity { content: "\e02c"; } +.container { + display: flex; + flex-flow: row nowrap; + justify-content: space-around; + margin-bottom: 8px; + min-height: 28px; +} + +.container > label { + margin-top: 6px; + width: 200px; +} + +.container > div.checkbox { + padding-top: 6px; +} + +.container > .value { + width: 100%; +} + +.container .row { + display: flex; + flex-flow: row nowrap; +} + +.container.shrink { + width: min-content; +} + +.fstuple { + display: flex; + flex-flow: row; +} +.fstuple input { + margin-left: 4px; + margin-right: 10px; +} +.fstuple label.red, .fstuple label.x { + color: #C62147; +} +.fstuple label.green, .fstuple label.y { + color: #359D85; +} +.fstuple label.blue, .fstuple label.z { + color: #0093C5; +} + +.xyz.fstuple, .pyr.fstuple { + position: relative; + left: -12px; +} + +.rgb.fstuple .tuple { + display: none; +} + +input.number-slider { + background: #575757; + border-radius: 4px; + color: white; +} + +.fstuple > div { + display: flex; + align-items: center; + justify-content: left; +} + +.flex-row { + display: flex; + flex-flow: row; +} + +.flex-column { + display: flex; + flex-flow: column; +} + +.flex-center { + align-items: center; +} + +.flex-evenly-spaced { + flex: 1; +} + +#property-serverScripts-status { + font-family: Raleway-Light; + font-size: 14px; + margin: 6px 0; +} + +#property-name, #property-id { + display: flex; + width: 100%; +} diff --git a/scripts/system/html/css/jsoneditor.css b/scripts/system/html/css/jsoneditor.css index ce83b45ff3..eedef60a7f 100644 --- a/scripts/system/html/css/jsoneditor.css +++ b/scripts/system/html/css/jsoneditor.css @@ -223,8 +223,6 @@ div.jsoneditor-tree table.jsoneditor-tree { div.jsoneditor-outer { width: 100%; - height: 100%; - min-height: 150px; margin: -35px 0 0 0; padding: 0 0 0 0; -moz-box-sizing: border-box; @@ -233,20 +231,18 @@ div.jsoneditor-outer { overflow-y: auto; } -textarea.jsoneditor-text, .ace-jsoneditor { - min-height: 150px; + min-height: 150px; + height: auto !important; } div.jsoneditor-tree { width: 100%; - height: 100%; position: relative; } textarea.jsoneditor-text { width: 100%; - height: 100%; margin: 0; -moz-box-sizing: border-box; -webkit-box-sizing: border-box; @@ -931,4 +927,4 @@ table.jsoneditor-search button.jsoneditor-previous { table.jsoneditor-search button.jsoneditor-previous:hover { background-position: -148px -49px; -} \ No newline at end of file +} diff --git a/scripts/system/html/entityProperties.html b/scripts/system/html/entityProperties.html index b56ad346e2..6af8ee992a 100644 --- a/scripts/system/html/entityProperties.html +++ b/scripts/system/html/entityProperties.html @@ -28,6 +28,19 @@
+
+ +
+
+
+
+
+
+
+
+
+
+
diff --git a/scripts/system/html/js/entityProperties.js b/scripts/system/html/js/entityProperties.js index f2c84d2f36..5d77c56b4a 100644 --- a/scripts/system/html/js/entityProperties.js +++ b/scripts/system/html/js/entityProperties.js @@ -29,7 +29,7 @@ const ICON_FOR_TYPE = { const DEGREES_TO_RADIANS = Math.PI / 180.0; -const NO_SELECTION = "No selection"; +const NO_SELECTION = "w"; const PROPERTY_SPACE_MODE = { ALL: 0, @@ -46,17 +46,22 @@ const GROUPS = [ type: "icon", icons: ICON_FOR_TYPE, propertyID: "type", + replaceID: "placeholder-property-type", }, { label: "Name", type: "string", propertyID: "name", + placeholder: "Name", + replaceID: "placeholder-property-name", }, { label: "ID", type: "string", propertyID: "id", + placeholder: "ID", readOnly: true, + replaceID: "placeholder-property-id", }, { label: "Description", @@ -74,16 +79,18 @@ const GROUPS = [ propertyID: "parentJointIndex", }, { - label: "Locked", + label: "", glyph: "", type: "bool", propertyID: "locked", + replaceID: "placeholder-property-locked", }, { - label: "Visible", + label: "", glyph: "", type: "bool", propertyID: "visible", + replaceID: "placeholder-property-visible", }, ] }, @@ -463,6 +470,7 @@ const GROUPS = [ { label: "Image", type: "string", + placeholder: "URL", propertyID: "image", }, ] @@ -640,6 +648,7 @@ const GROUPS = [ { id: "particles_emit", label: "EMIT", + isMinor: true, properties: [ { label: "Emit Rate", @@ -692,7 +701,7 @@ const GROUPS = [ vec3Type: "pyr", step: 0.01, round: 100, - subLabels: [ "pitch", "yaw", "roll" ], + subLabels: [ "x", "y", "z" ], unit: "deg", propertyID: "emitOrientation", }, @@ -706,35 +715,42 @@ const GROUPS = [ { id: "particles_size", label: "SIZE", + isMinor: true, properties: [ { + type: "triple", label: "Size", - type: "slider", - min: 0, - max: 4, - step: 0.01, - decimals: 2, - propertyID: "particleRadius", - }, - { - label: "Size Start", - type: "slider", - min: 0, - max: 4, - step: 0.01, - decimals: 2, - propertyID: "radiusStart", - fallbackProperty: "particleRadius", - }, - { - label: "Size Finish", - type: "slider", - min: 0, - max: 4, - step: 0.01, - decimals: 2, - propertyID: "radiusFinish", - fallbackProperty: "particleRadius", + properties: [ + { + label: "Start", + type: "slider", + min: 0, + max: 4, + step: 0.01, + decimals: 2, + propertyID: "radiusStart", + fallbackProperty: "particleRadius", + }, + { + label: "Middle", + type: "slider", + min: 0, + max: 4, + step: 0.01, + decimals: 2, + propertyID: "particleRadius", + }, + { + label: "Finish", + type: "slider", + min: 0, + max: 4, + step: 0.01, + decimals: 2, + propertyID: "radiusFinish", + fallbackProperty: "particleRadius", + }, + ] }, { label: "Size Spread", @@ -750,24 +766,31 @@ const GROUPS = [ { id: "particles_color", label: "COLOR", + isMinor: true, properties: [ { + type: "triple", label: "Color", - type: "color", - propertyID: "particleColor", - propertyName: "color", // actual entity property name - }, - { - label: "Color Start", - type: "color", - propertyID: "colorStart", - fallbackProperty: "color", - }, - { - label: "Color Finish", - type: "color", - propertyID: "colorFinish", - fallbackProperty: "color", + properties: [ + { + label: "Start", + type: "color", + propertyID: "colorStart", + fallbackProperty: "color", + }, + { + label: "Middle", + type: "color", + propertyID: "particleColor", + propertyName: "color", // actual entity property name + }, + { + label: "Finish", + type: "color", + propertyID: "colorFinish", + fallbackProperty: "color", + }, + ] }, { label: "Color Spread", @@ -779,35 +802,42 @@ const GROUPS = [ { id: "particles_alpha", label: "ALPHA", + isMinor: true, properties: [ { + type: "triple", label: "Alpha", - type: "slider", - min: 0, - max: 1, - step: 0.01, - decimals: 2, - propertyID: "alpha", - }, - { - label: "Alpha Start", - type: "slider", - min: 0, - max: 1, - step: 0.01, - decimals: 2, - propertyID: "alphaStart", - fallbackProperty: "alpha", - }, - { - label: "Alpha Finish", - type: "slider", - min: 0, - max: 1, - step: 0.01, - decimals: 2, - propertyID: "alphaFinish", - fallbackProperty: "alpha", + properties: [ + { + label: "Start", + type: "slider", + min: 0, + max: 1, + step: 0.01, + decimals: 2, + propertyID: "alphaStart", + fallbackProperty: "alpha", + }, + { + label: "Middle", + type: "slider", + min: 0, + max: 1, + step: 0.01, + decimals: 2, + propertyID: "alpha", + }, + { + label: "Finish", + type: "slider", + min: 0, + max: 1, + step: 0.01, + decimals: 2, + propertyID: "alphaFinish", + fallbackProperty: "alpha", + }, + ] }, { label: "Alpha Spread", @@ -823,6 +853,7 @@ const GROUPS = [ { id: "particles_acceleration", label: "ACCELERATION", + isMinor: true, properties: [ { label: "Emit Acceleration", @@ -847,41 +878,48 @@ const GROUPS = [ { id: "particles_spin", label: "SPIN", + isMinor: true, properties: [ { - label: "Spin", - type: "slider", - min: -360, - max: 360, - step: 1, - decimals: 0, - multiplier: DEGREES_TO_RADIANS, - unit: "deg", - propertyID: "particleSpin", - }, - { - label: "Spin Start", - type: "slider", - min: -360, - max: 360, - step: 1, - decimals: 0, - multiplier: DEGREES_TO_RADIANS, - unit: "deg", - propertyID: "spinStart", - fallbackProperty: "particleSpin", - }, - { - label: "Spin Finish", - type: "slider", - min: -360, - max: 360, - step: 1, - decimals: 0, - multiplier: DEGREES_TO_RADIANS, - unit: "deg", - propertyID: "spinFinish", - fallbackProperty: "particleSpin", + type: "triple", + label: "Alpha", + properties: [ + { + label: "Start", + type: "slider", + min: -360, + max: 360, + step: 1, + decimals: 0, + multiplier: DEGREES_TO_RADIANS, + unit: "deg", + propertyID: "spinStart", + fallbackProperty: "particleSpin", + }, + { + label: "Middle", + type: "slider", + min: -360, + max: 360, + step: 1, + decimals: 0, + multiplier: DEGREES_TO_RADIANS, + unit: "deg", + propertyID: "particleSpin", + }, + { + label: "Finish", + type: "slider", + min: -360, + max: 360, + step: 1, + decimals: 0, + multiplier: DEGREES_TO_RADIANS, + unit: "deg", + propertyID: "spinFinish", + fallbackProperty: "particleSpin", + }, + ] }, { label: "Spin Spread", @@ -904,51 +942,64 @@ const GROUPS = [ { id: "particles_constraints", label: "CONSTRAINTS", + isMinor: true, properties: [ { - label: "Horizontal Angle Start", - type: "slider", - min: 0, - max: 180, - step: 1, - decimals: 0, - multiplier: DEGREES_TO_RADIANS, - unit: "deg", - propertyID: "polarStart", + type: "triple", + label: "Horizontal Angle", + properties: [ + { + label: "Start", + type: "slider", + min: -180, + max: 0, + step: 1, + decimals: 0, + multiplier: DEGREES_TO_RADIANS, + unit: "deg", + propertyID: "polarStart", + }, + { + label: "Finish", + type: "slider", + min: 0, + max: 180, + step: 1, + decimals: 0, + multiplier: DEGREES_TO_RADIANS, + unit: "deg", + propertyID: "polarFinish", + }, + ], }, { - label: "Horizontal Angle Finish", - type: "slider", - min: 0, - max: 180, - step: 1, - decimals: 0, - multiplier: DEGREES_TO_RADIANS, - unit: "deg", - propertyID: "polarFinish", - }, - { - label: "Vertical Angle Start", - type: "slider", - min: -180, - max: 0, - step: 1, - decimals: 0, - multiplier: DEGREES_TO_RADIANS, - unit: "deg", - propertyID: "azimuthStart", - }, - { - label: "Vertical Angle Finish", - type: "slider", - min: 0, - max: 180, - step: 1, - decimals: 0, - multiplier: DEGREES_TO_RADIANS, - unit: "deg", - propertyID: "azimuthFinish", - }, + type: "triple", + label: "Vertical Angle", + properties: [ + { + label: "Start", + type: "slider", + min: 0, + max: 180, + step: 1, + decimals: 0, + multiplier: DEGREES_TO_RADIANS, + unit: "deg", + propertyID: "azimuthStart", + }, + { + label: "Finish", + type: "slider", + min: 0, + max: 180, + step: 1, + decimals: 0, + multiplier: DEGREES_TO_RADIANS, + unit: "deg", + propertyID: "azimuthFinish", + }, + ] + } ] }, { @@ -981,7 +1032,7 @@ const GROUPS = [ vec3Type: "pyr", step: 0.1, decimals: 4, - subLabels: [ "pitch", "yaw", "roll" ], + subLabels: [ "x", "y", "z" ], unit: "deg", propertyID: "rotation", spaceMode: PROPERTY_SPACE_MODE.WORLD, @@ -992,7 +1043,7 @@ const GROUPS = [ vec3Type: "pyr", step: 0.1, decimals: 4, - subLabels: [ "pitch", "yaw", "roll" ], + subLabels: [ "x", "y", "z" ], unit: "deg", propertyID: "localRotation", spaceMode: PROPERTY_SPACE_MODE.LOCAL, @@ -1065,6 +1116,7 @@ const GROUPS = [ }, { label: "Clone Lifetime", + indentedLabel: true, type: "number", unit: "s", propertyID: "cloneLifetime", @@ -1072,18 +1124,21 @@ const GROUPS = [ }, { label: "Clone Limit", + indentedLabel: true, type: "number", propertyID: "cloneLimit", showPropertyRule: { "cloneable": "true" }, }, { label: "Clone Dynamic", + indentedLabel: true, type: "bool", propertyID: "cloneDynamic", showPropertyRule: { "cloneable": "true" }, }, { label: "Clone Avatar Entity", + indentedLabel: true, type: "bool", propertyID: "cloneAvatarEntity", showPropertyRule: { "cloneable": "true" }, @@ -1120,6 +1175,12 @@ const GROUPS = [ buttons: [ { id: "reload", label: "F", className: "glyph", onClick: reloadServerScripts } ], propertyID: "serverScripts", }, + { + label: "Server Script Status", + type: "placeholder", + indentedLabel: true, + propertyID: "serverScriptStatus", + }, { label: "Lifetime", type: "number", @@ -1146,12 +1207,6 @@ const GROUPS = [ inverse: true, propertyID: "collisionless", }, - { - label: "Collides With", - type: "sub-header", - propertyID: "collidesWithHeader", // not actually a property but used for naming/storing this element - showPropertyRule: { "collisionless": "false" }, - }, { label: "Static Entities", type: "bool", @@ -1230,7 +1285,7 @@ const GROUPS = [ vec3Type: "pyr", multiplier: DEGREES_TO_RADIANS, decimals: 4, - subLabels: [ "pitch", "yaw", "roll" ], + subLabels: [ "x", "y", "z" ], unit: "deg/s", propertyID: "localAngularVelocity", }, @@ -1370,6 +1425,12 @@ function debugPrint(message) { ); } +function createElementFromHTML(htmlString) { + var elTemplate = document.createElement('template'); + elTemplate.innerHTML = htmlString.trim(); + return elTemplate.content.firstChild; +} + /** * GENERAL PROPERTY/GROUP FUNCTIONS @@ -1442,8 +1503,8 @@ function disableProperties() { } function showPropertyElement(propertyID, show) { - let elProperty = properties[propertyID].elProperty; - elProperty.style.display = show ? "table" : "none"; + let elProperty = properties[propertyID].elContainer; + elProperty.style.display = show ? "flex" : "none"; } function resetProperties() { @@ -1500,7 +1561,6 @@ function resetProperties() { } case 'icon': { property.elSpan.style.display = "none"; - property.elLabel.innerHTML = propertyData.label; break; } case 'texture': { @@ -1701,23 +1761,23 @@ function createImageURLUpdateFunction(propertyName, isParticleProperty) { * PROPERTY ELEMENT CREATION FUNCTIONS */ -function createStringProperty(property, elProperty, elLabel) { +function createStringProperty(property, elProperty) { let propertyName = property.name; let elementID = property.elementID; let propertyData = property.data; - elProperty.className = "property text"; + elProperty.className = "text"; - let elInput = document.createElement('input'); - elInput.setAttribute("id", elementID); - elInput.setAttribute("type", "text"); - if (propertyData.readOnly) { - elInput.readOnly = true; - } + let elInput = createElementFromHTML(` + + `) + elInput.addEventListener('change', createEmitTextPropertyUpdateFunction(propertyName, property.isParticleProperty)); - elProperty.appendChild(elLabel); elProperty.appendChild(elInput); if (propertyData.buttons !== undefined) { @@ -1727,18 +1787,18 @@ function createStringProperty(property, elProperty, elLabel) { return elInput; } -function createBoolProperty(property, elProperty, elLabel) { +function createBoolProperty(property, elProperty) { let propertyName = property.name; let elementID = property.elementID; let propertyData = property.data; - elProperty.className = "property checkbox"; + elProperty.className = "checkbox"; if (propertyData.glyph !== undefined) { - elLabel.innerText = " " + propertyData.label; let elSpan = document.createElement('span'); elSpan.innerHTML = propertyData.glyph; - elLabel.insertBefore(elSpan, elLabel.firstChild); + elSpan.className = 'icon'; + elProperty.appendChild(elSpan); } let elInput = document.createElement('input'); @@ -1746,7 +1806,7 @@ function createBoolProperty(property, elProperty, elLabel) { elInput.setAttribute("type", "checkbox"); elProperty.appendChild(elInput); - elProperty.appendChild(elLabel); + elProperty.appendChild(createElementFromHTML(``)); let subPropertyOf = propertyData.subPropertyOf; if (subPropertyOf !== undefined) { @@ -1760,14 +1820,12 @@ function createBoolProperty(property, elProperty, elLabel) { return elInput; } -function createNumberProperty(property, elProperty, elLabel) { +function createNumberProperty(property, elProperty) { let propertyName = property.name; let elementID = property.elementID; let propertyData = property.data; - elProperty.className = "property number"; - - addUnit(propertyData.unit, elLabel); + elProperty.className = "number"; let elInput = document.createElement('input'); elInput.setAttribute("id", elementID); @@ -1789,7 +1847,6 @@ function createNumberProperty(property, elProperty, elLabel) { elInput.addEventListener('change', createEmitNumberPropertyUpdateFunction(propertyName, propertyData.multiplier, propertyData.decimals, property.isParticleProperty)); - elProperty.appendChild(elLabel); elProperty.appendChild(elInput); if (propertyData.buttons !== undefined) { @@ -1799,10 +1856,10 @@ function createNumberProperty(property, elProperty, elLabel) { return elInput; } -function createSliderProperty(property, elProperty, elLabel) { +function createSliderProperty(property, elProperty) { let propertyData = property.data; - elProperty.className = "property range"; + elProperty.className = "range"; let elDiv = document.createElement("div"); elDiv.className = "slider-wrapper"; @@ -1852,7 +1909,6 @@ function createSliderProperty(property, elProperty, elLabel) { updateProperty(property.name, sliderValue, property.isParticleProperty); }; - elDiv.appendChild(elLabel); elDiv.appendChild(elSlider); elDiv.appendChild(elInput); elProperty.appendChild(elDiv); @@ -1863,26 +1919,23 @@ function createSliderProperty(property, elProperty, elLabel) { return elResult; } -function createVec3Property(property, elProperty, elLabel) { +function createVec3Property(property, elProperty) { let propertyName = property.name; let elementID = property.elementID; let propertyData = property.data; - elProperty.className = "property " + propertyData.vec3Type + " fstuple"; + elProperty.className = propertyData.vec3Type + " fstuple"; - let elTuple = document.createElement('div'); - elTuple.className = "tuple"; + //let elTuple = document.createElement('div'); + //elTuple.className = "tuple"; - addUnit(propertyData.unit, elLabel); + //elProperty.appendChild(elTuple); - elProperty.appendChild(elLabel); - elProperty.appendChild(elTuple); - - let elInputX = createTupleNumberInput(elTuple, elementID, propertyData.subLabels[VECTOR_ELEMENTS.X_INPUT], + let elInputX = createTupleNumberInput(elProperty, elementID, propertyData.subLabels[VECTOR_ELEMENTS.X_INPUT], propertyData.min, propertyData.max, propertyData.step); - let elInputY = createTupleNumberInput(elTuple, elementID, propertyData.subLabels[VECTOR_ELEMENTS.Y_INPUT], + let elInputY = createTupleNumberInput(elProperty, elementID, propertyData.subLabels[VECTOR_ELEMENTS.Y_INPUT], propertyData.min, propertyData.max, propertyData.step); - let elInputZ = createTupleNumberInput(elTuple, elementID, propertyData.subLabels[VECTOR_ELEMENTS.Z_INPUT], + let elInputZ = createTupleNumberInput(elProperty, elementID, propertyData.subLabels[VECTOR_ELEMENTS.Z_INPUT], propertyData.min, propertyData.max, propertyData.step); let inputChangeFunction = createEmitVec3PropertyUpdateFunction(propertyName, elInputX, elInputY, elInputZ, @@ -1898,19 +1951,16 @@ function createVec3Property(property, elProperty, elLabel) { return elResult; } -function createVec2Property(property, elProperty, elLabel) { +function createVec2Property(property, elProperty) { let propertyName = property.name; let elementID = property.elementID; let propertyData = property.data; - elProperty.className = "property " + propertyData.vec2Type + " fstuple"; + elProperty.className = propertyData.vec2Type + " fstuple"; let elTuple = document.createElement('div'); elTuple.className = "tuple"; - addUnit(propertyData.unit, elLabel); - - elProperty.appendChild(elLabel); elProperty.appendChild(elTuple); let elInputX = createTupleNumberInput(elTuple, elementID, propertyData.subLabels[VECTOR_ELEMENTS.X_INPUT], @@ -1929,11 +1979,11 @@ function createVec2Property(property, elProperty, elLabel) { return elResult; } -function createColorProperty(property, elProperty, elLabel) { +function createColorProperty(property, elProperty) { let propertyName = property.name; let elementID = property.elementID; - elProperty.className = "property rgb fstuple"; + elProperty.className = "rgb fstuple"; let elColorPicker = document.createElement('div'); elColorPicker.className = "color-picker"; @@ -1943,7 +1993,6 @@ function createColorProperty(property, elProperty, elLabel) { elTuple.className = "tuple"; elProperty.appendChild(elColorPicker); - elProperty.appendChild(elLabel); elProperty.appendChild(elTuple); let elInputR = createTupleNumberInput(elTuple, elementID, "red", COLOR_MIN, COLOR_MAX, COLOR_STEP); @@ -1959,7 +2008,7 @@ function createColorProperty(property, elProperty, elLabel) { let colorPickerID = "#" + elementID; colorPickers[colorPickerID] = $(colorPickerID).colpick({ colorScheme: 'dark', - layout: 'hex', + layout: 'rgbhex', color: '000000', submit: false, // We don't want to have a submission button onShow: function(colpick) { @@ -1989,12 +2038,12 @@ function createColorProperty(property, elProperty, elLabel) { return elResult; } -function createDropdownProperty(property, propertyID, elProperty, elLabel) { +function createDropdownProperty(property, propertyID, elProperty) { let propertyName = property.name; let elementID = property.elementID; let propertyData = property.data; - elProperty.className = "property dropdown"; + elProperty.className = "dropdown"; let elInput = document.createElement('select'); elInput.setAttribute("id", elementID); @@ -2009,24 +2058,17 @@ function createDropdownProperty(property, propertyID, elProperty, elLabel) { elInput.addEventListener('change', createEmitTextPropertyUpdateFunction(propertyName, property.isParticleProperty)); - elProperty.appendChild(elLabel); elProperty.appendChild(elInput); return elInput; } -function createTextareaProperty(property, elProperty, elLabel) { +function createTextareaProperty(property, elProperty) { let propertyName = property.name; let elementID = property.elementID; let propertyData = property.data; - elProperty.className = "property textarea"; - - elProperty.appendChild(elLabel); - - if (propertyData.buttons !== undefined) { - addButtons(elProperty, elementID, propertyData.buttons, true); - } + elProperty.className = "textarea"; let elInput = document.createElement('textarea'); elInput.setAttribute("id", elementID); @@ -2037,35 +2079,35 @@ function createTextareaProperty(property, elProperty, elLabel) { elInput.addEventListener('change', createEmitTextPropertyUpdateFunction(propertyName, property.isParticleProperty)); elProperty.appendChild(elInput); + + if (propertyData.buttons !== undefined) { + addButtons(elProperty, elementID, propertyData.buttons, true); + } return elInput; } -function createIconProperty(property, elProperty, elLabel) { +function createIconProperty(property, elProperty) { let elementID = property.elementID; let propertyData = property.data; - elProperty.className = "property value"; - - elLabel.setAttribute("id", elementID); - elLabel.innerHTML = " " + propertyData.label; + elProperty.className = "value"; let elSpan = document.createElement('span'); elSpan.setAttribute("id", elementID + "-icon"); + elSpan.className = 'icon'; elProperty.appendChild(elSpan); - elProperty.appendChild(elLabel); let elResult = []; elResult[ICON_ELEMENTS.ICON] = elSpan; - elResult[ICON_ELEMENTS.LABEL] = elLabel; return elResult; } -function createTextureProperty(property, elProperty, elLabel) { +function createTextureProperty(property, elProperty) { let elementID = property.elementID; - elProperty.className = "property texture"; + elProperty.className = "texture"; let elDiv = document.createElement("div"); let elImage = document.createElement("img"); @@ -2106,9 +2148,8 @@ function createTextureProperty(property, elProperty, elLabel) { }; elInput.onchange = elInput.oninput; - elProperty.appendChild(elLabel); - elProperty.appendChild(elDiv); elProperty.appendChild(elInput); + elProperty.appendChild(elDiv); let elResult = []; elResult[TEXTURE_ELEMENTS.IMAGE] = elImage; @@ -2120,11 +2161,10 @@ function createButtonsProperty(property, elProperty, elLabel) { let elementID = property.elementID; let propertyData = property.data; - elProperty.className = "property text"; + elProperty.className = "text"; let hasLabel = propertyData.label !== undefined; if (hasLabel) { - elProperty.appendChild(elLabel); } if (propertyData.buttons !== undefined) { @@ -2139,33 +2179,25 @@ function createTupleNumberInput(elTuple, propertyElementID, subLabel, min, max, let elDiv = document.createElement('div'); let elLabel = document.createElement('label'); - elLabel.innerText = subLabel[0].toUpperCase() + subLabel.slice(1) + ":"; + elLabel.className = subLabel; + elLabel.innerText = subLabel[0].toUpperCase() + subLabel.slice(1); elLabel.setAttribute("for", elementID); let elInput = document.createElement('input'); - elInput.className = subLabel; + elInput.className = subLabel + " number-slider"; elInput.setAttribute("id", elementID); elInput.setAttribute("type", "number"); elInput.setAttribute("min", min); elInput.setAttribute("max", max); elInput.setAttribute("step", step); - elDiv.appendChild(elInput); elDiv.appendChild(elLabel); + elDiv.appendChild(elInput); elTuple.appendChild(elDiv); return elInput; } -function addUnit(unit, elLabel) { - if (unit !== undefined) { - let elSpan = document.createElement('span'); - elSpan.className = "unit"; - elSpan.innerHTML = unit; - elLabel.appendChild(elSpan); - } -} - function addButtons(elProperty, propertyID, buttons, newRow) { let elDiv = document.createElement('div'); elDiv.className = "row"; @@ -2716,32 +2748,125 @@ function updateVisibleSpaceModeProperties() { let propertySpaceMode = property.spaceMode; if (propertySpaceMode !== PROPERTY_SPACE_MODE.ALL) { showPropertyElement(propertyID, propertySpaceMode === currentSpaceMode); + } else { + showPropertyElement(propertyID, true); } } } } +function createProperty(propertyData, propertyElementID, propertyName, propertyID, elProperty) { + let property = { + data: propertyData, + elementID: propertyElementID, + name: propertyName, + elProperty: elProperty, + }; + let propertyType = propertyData.type; + + switch (propertyType) { + case 'string': { + property.elInput = createStringProperty(property, elProperty); + break; + } + case 'bool': { + property.elInput = createBoolProperty(property, elProperty); + break; + } + case 'number': { + property.elInput = createNumberProperty(property, elProperty); + break; + } + case 'slider': { + let elSlider = createSliderProperty(property, elProperty); + property.elSlider = elSlider[SLIDER_ELEMENTS.SLIDER]; + property.elInput = elSlider[SLIDER_ELEMENTS.NUMBER_INPUT]; + break; + } + case 'vec3': { + let elVec3 = createVec3Property(property, elProperty); + property.elInputX = elVec3[VECTOR_ELEMENTS.X_INPUT]; + property.elInputY = elVec3[VECTOR_ELEMENTS.Y_INPUT]; + property.elInputZ = elVec3[VECTOR_ELEMENTS.Z_INPUT]; + break; + } + case 'vec2': { + let elVec2 = createVec2Property(property, elProperty); + property.elInputX = elVec2[VECTOR_ELEMENTS.X_INPUT]; + property.elInputY = elVec2[VECTOR_ELEMENTS.Y_INPUT]; + break; + } + case 'color': { + let elColor = createColorProperty(property, elProperty); + property.elColorPicker = elColor[COLOR_ELEMENTS.COLOR_PICKER]; + property.elInputR = elColor[COLOR_ELEMENTS.RED_INPUT]; + property.elInputG = elColor[COLOR_ELEMENTS.GREEN_INPUT]; + property.elInputB = elColor[COLOR_ELEMENTS.BLUE_INPUT]; + break; + } + case 'dropdown': { + property.elInput = createDropdownProperty(property, propertyID, elProperty); + break; + } + case 'textarea': { + property.elInput = createTextareaProperty(property, elProperty); + break; + } + case 'icon': { + let elIcon = createIconProperty(property, elProperty); + property.elSpan = elIcon[ICON_ELEMENTS.ICON]; + property.elLabel = elIcon[ICON_ELEMENTS.LABEL]; + break; + } + case 'texture': { + let elTexture = createTextureProperty(property, elProperty); + property.elImage = elTexture[TEXTURE_ELEMENTS.IMAGE]; + property.elInput = elTexture[TEXTURE_ELEMENTS.TEXT_INPUT]; + break; + } + case 'buttons': { + property.elProperty = createButtonsProperty(property, elProperty); + break; + } + case 'placeholder': + case 'sub-header': { + break; + } + default: { + console.log("EntityProperties - Unknown property type " + + propertyType + " set to property " + propertyID); + break; + } + } + + return property; +} + function loaded() { openEventBridge(function() { let elPropertiesList = document.getElementById("properties-list"); + + let templatePropertyRow = document.getElementById('property-row'); - GROUPS.forEach(function(group) { + GROUPS.forEach(function(group) { let elGroup; if (group.addToGroup !== undefined) { let fieldset = document.getElementById("properties-" + group.addToGroup); elGroup = document.createElement('div'); fieldset.appendChild(elGroup); } else { - elGroup = document.createElement('fieldset'); - elGroup.className = "major"; + elGroup = document.createElement('div'); + elGroup.className = 'section ' + (group.isMinor ? "minor" : "major"); elGroup.setAttribute("id", "properties-" + group.id); elPropertiesList.appendChild(elGroup); } if (group.label !== undefined) { - let elLegend = document.createElement('legend'); + let elLegend = document.createElement('div'); elLegend.className = "section-header"; - elLegend.innerText = group.label; + + elLegend.appendChild(createElementFromHTML(`
${group.label}
`)); + let elSpan = document.createElement('span'); elSpan.className = ".collapse-icon"; elSpan.innerText = "M"; @@ -2757,145 +2882,119 @@ function loaded() { let propertyElementID = "property-" + propertyID; propertyElementID = propertyElementID.replace('.', '-'); - let elProperty; - if (propertyType === "sub-header") { - elProperty = document.createElement('legend'); - elProperty.className = "sub-section-header"; - elProperty.innerText = propertyData.label; - } else { - elProperty = document.createElement('div'); - elProperty.setAttribute("id", "div-" + propertyElementID); - } + let elContainer, elLabel; - if (group.twoColumn && propertyData.column !== undefined && propertyData.column !== -1) { - let columnName = group.id + "column" + propertyData.column; - let elColumn = document.getElementById(columnName); - if (!elColumn) { - let columnDivName = group.id + "columnDiv"; - let elColumnDiv = document.getElementById(columnDivName); - if (!elColumnDiv) { - elColumnDiv = document.createElement('div'); - elColumnDiv.className = "two-column"; - elColumnDiv.setAttribute("id", group.id + "columnDiv"); - elGroup.appendChild(elColumnDiv); - } - elColumn = document.createElement('fieldset'); - elColumn.className = "column"; - elColumn.setAttribute("id", columnName); - elColumnDiv.appendChild(elColumn); + if (propertyData.replaceID === undefined) { + // Create subheader, or create new property and append it. + if (propertyType === "sub-header") { + elContainer = createElementFromHTML( + `
${propertyData.label}
`); + } else { + elContainer = document.createElement('div'); + elContainer.setAttribute("id", "div-" + propertyElementID); + elContainer.className = 'property container'; } - elColumn.appendChild(elProperty); - } else { - elGroup.appendChild(elProperty); - } - - let elLabel = document.createElement('label'); - elLabel.innerText = propertyData.label; - elLabel.setAttribute("for", propertyElementID); - createAppTooltip.registerTooltipElement(elLabel, propertyID); - - let property = { - data: propertyData, - elementID: propertyElementID, - name: propertyName, - isParticleProperty: group.id.includes("particles"), - elProperty, - spaceMode: propertySpaceMode, - }; - properties[propertyID] = property; - - switch (propertyType) { - case 'string': { - properties[propertyID].elInput = createStringProperty(property, elProperty, elLabel); - break; + if (group.twoColumn && propertyData.column !== undefined && propertyData.column !== -1) { + let columnName = group.id + "column" + propertyData.column; + let elColumn = document.getElementById(columnName); + if (!elColumn) { + let columnDivName = group.id + "columnDiv"; + let elColumnDiv = document.getElementById(columnDivName); + if (!elColumnDiv) { + elColumnDiv = document.createElement('div'); + elColumnDiv.className = "two-column"; + elColumnDiv.setAttribute("id", group.id + "columnDiv"); + elGroup.appendChild(elColumnDiv); + } + elColumn = document.createElement('fieldset'); + elColumn.className = "column"; + elColumn.setAttribute("id", columnName); + elColumnDiv.appendChild(elColumn); + } + elColumn.appendChild(elContainer); + } else { + elGroup.appendChild(elContainer); } - case 'bool': { - properties[propertyID].elInput = createBoolProperty(property, elProperty, elLabel); - break; - } - case 'number': { - properties[propertyID].elInput = createNumberProperty(property, elProperty, elLabel); - break; - } - case 'slider': { - let elSlider = createSliderProperty(property, elProperty, elLabel); - properties[propertyID].elSlider = elSlider[SLIDER_ELEMENTS.SLIDER]; - properties[propertyID].elInput = elSlider[SLIDER_ELEMENTS.NUMBER_INPUT]; - break; - } - case 'vec3': { - let elVec3 = createVec3Property(property, elProperty, elLabel); - properties[propertyID].elInputX = elVec3[VECTOR_ELEMENTS.X_INPUT]; - properties[propertyID].elInputY = elVec3[VECTOR_ELEMENTS.Y_INPUT]; - properties[propertyID].elInputZ = elVec3[VECTOR_ELEMENTS.Z_INPUT]; - break; - } - case 'vec2': { - let elVec2 = createVec2Property(property, elProperty, elLabel); - properties[propertyID].elInputX = elVec2[VECTOR_ELEMENTS.X_INPUT]; - properties[propertyID].elInputY = elVec2[VECTOR_ELEMENTS.Y_INPUT]; - break; - } - case 'color': { - let elColor = createColorProperty(property, elProperty, elLabel); - properties[propertyID].elColorPicker = elColor[COLOR_ELEMENTS.COLOR_PICKER]; - properties[propertyID].elInputR = elColor[COLOR_ELEMENTS.RED_INPUT]; - properties[propertyID].elInputG = elColor[COLOR_ELEMENTS.GREEN_INPUT]; - properties[propertyID].elInputB = elColor[COLOR_ELEMENTS.BLUE_INPUT]; - break; - } - case 'dropdown': { - properties[propertyID].elInput = createDropdownProperty(property, propertyID, elProperty, elLabel); - break; - } - case 'textarea': { - properties[propertyID].elInput = createTextareaProperty(property, elProperty, elLabel); - break; - } - case 'icon': { - let elIcon = createIconProperty(property, elProperty, elLabel); - properties[propertyID].elSpan = elIcon[ICON_ELEMENTS.ICON]; - properties[propertyID].elLabel = elIcon[ICON_ELEMENTS.LABEL]; - break; - } - case 'texture': { - let elTexture = createTextureProperty(property, elProperty, elLabel); - properties[propertyID].elImage = elTexture[TEXTURE_ELEMENTS.IMAGE]; - properties[propertyID].elInput = elTexture[TEXTURE_ELEMENTS.TEXT_INPUT]; - break; - } - case 'buttons': { - properties[propertyID].elProperty = createButtonsProperty(property, elProperty, elLabel); - break; - } - case 'sub-header': { - break; - } - default: { - console.log("EntityProperties - Unknown property type " + - propertyType + " set to property " + propertyID); - break; + + elLabel = document.createElement('label'); + elLabel.setAttribute("for", propertyElementID); + if (propertyData.indentedLabel || propertyData.showPropertyRule !== undefined) { + let elSpan = document.createElement('span'); + elSpan.className = 'indented'; + elSpan.innerText = propertyData.label; + elLabel.appendChild(elSpan); + } else { + elLabel.innerText = propertyData.label; } + elContainer.appendChild(elLabel); + } else { + elContainer = document.getElementById(propertyData.replaceID); } - - let showPropertyRule = propertyData.showPropertyRule; - if (showPropertyRule !== undefined) { - let dependentProperty = Object.keys(showPropertyRule)[0]; - let dependentPropertyValue = showPropertyRule[dependentProperty]; - if (properties[dependentProperty] === undefined) { - properties[dependentProperty] = {}; + + + if (elLabel) { + createAppTooltip.registerTooltipElement(elLabel, propertyID); + } + + let elProperty = createElementFromHTML('
'); + elContainer.appendChild(elProperty); + + if (propertyType == 'triple') { + elProperty.className = 'flex-row'; + for (var i = 0; i < propertyData.properties.length; ++i) { + let innerPropertyData = propertyData.properties[i]; + + let elWrapper = createElementFromHTML('
'); + + let propertyID = innerPropertyData.propertyID; + let propertyName = innerPropertyData.propertyName !== undefined ? innerPropertyData.propertyName : propertyID; + let propertyElementID = "property-" + propertyID; + propertyElementID = propertyElementID.replace('.', '-'); + + elWrapper.appendChild(createElementFromHTML(`
${innerPropertyData.label}
`)); + elProperty.appendChild(elWrapper); + + let property = createProperty(innerPropertyData, propertyElementID, propertyName, propertyID, elWrapper.childNodes[0]); + property.isParticleProperty = group.id.includes("particles"); + property.elContainer = elContainer; + property.spaceMode = propertySpaceMode; + + if (property.type !== 'placeholder') { + properties[propertyID] = property; + } } - if (properties[dependentProperty].showPropertyRules === undefined) { - properties[dependentProperty].showPropertyRules = {}; + } else { + let property = createProperty(propertyData, propertyElementID, propertyName, propertyID, elProperty); + property.isParticleProperty = group.id.includes("particles"); + property.elContainer = elContainer; + property.spaceMode = propertySpaceMode; + + if (property.type !== 'placeholder') { + properties[propertyID] = property; + } + + let showPropertyRule = propertyData.showPropertyRule; + if (showPropertyRule !== undefined) { + let dependentProperty = Object.keys(showPropertyRule)[0]; + let dependentPropertyValue = showPropertyRule[dependentProperty]; + if (properties[dependentProperty] === undefined) { + properties[dependentProperty] = {}; + } + if (properties[dependentProperty].showPropertyRules === undefined) { + properties[dependentProperty].showPropertyRules = {}; + } + properties[dependentProperty].showPropertyRules[propertyID] = dependentPropertyValue; } - properties[dependentProperty].showPropertyRules[propertyID] = dependentPropertyValue; } }); elGroups[group.id] = elGroup; }); + let minorSections = document.querySelectorAll(".section.minor"); + minorSections[minorSections.length - 1].className += " last"; + updateVisibleSpaceModeProperties(); if (window.EventBridge !== undefined) { @@ -2934,6 +3033,10 @@ function loaded() { resetProperties(); showGroupsForType("None"); + + let elIcon = properties.type.elSpan; + elIcon.innerText = NO_SELECTION; + elIcon.style.display = 'inline-block'; deleteJSONEditor(); getPropertyInputElement("userData").value = ""; @@ -3123,7 +3226,6 @@ function loaded() { case 'icon': { property.elSpan.innerHTML = propertyData.icons[propertyValue]; property.elSpan.style.display = "inline-block"; - property.elLabel.innerHTML = propertyValue; break; } case 'texture': { @@ -3240,34 +3342,27 @@ function loaded() { } // Server Script Status - let serverScriptProperty = properties["serverScripts"]; - let elServerScript = serverScriptProperty.elInput; - let serverScriptElementID = serverScriptProperty.elementID; - let serverScriptStatusElementID = serverScriptElementID + "-status"; - let elDiv = document.createElement('div'); - elDiv.className = "property"; - elDiv.setAttribute("id", "div-" + serverScriptStatusElementID); - let elLabel = document.createElement('label'); - elLabel.setAttribute("for", serverScriptStatusElementID); - elLabel.innerText = "Server Script Status"; - createAppTooltip.registerTooltipElement(elLabel, "serverScriptsStatus"); - let elServerScriptStatus = document.createElement('span'); + let elServerScriptStatusOuter = document.getElementById('div-property-serverScriptStatus'); + let elServerScriptStatusContainer = document.getElementById('div-property-serverScriptStatus').childNodes[1]; + let serverScriptStatusElementID = 'property-serverScripts-status'; + createAppTooltip.registerTooltipElement(elServerScriptStatusOuter.childNodes[0], "serverScriptsStatus"); + let elServerScriptStatus = document.createElement('div'); elServerScriptStatus.setAttribute("id", serverScriptStatusElementID); - elDiv.appendChild(elLabel); - elDiv.appendChild(elServerScriptStatus); - elServerScript.parentNode.appendChild(elDiv); + elServerScriptStatusContainer.appendChild(elServerScriptStatus); // Server Script Error + let elServerScripts = getPropertyInputElement("serverScripts"); elDiv = document.createElement('div'); elDiv.className = "property"; let elServerScriptError = document.createElement('textarea'); - elServerScriptError.setAttribute("id", serverScriptElementID + "-error"); + let serverScriptErrorElementID = 'property-serverScripts-error'; + elServerScriptError.setAttribute("id", serverScriptErrorElementID); elDiv.appendChild(elServerScriptError); - elServerScript.parentNode.appendChild(elDiv); + elServerScriptStatusContainer.appendChild(elDiv); let elScript = getPropertyInputElement("script"); - elScript.parentNode.className = "property url refresh"; - elServerScript.parentNode.className = "property url refresh"; + elScript.parentNode.className = "url refresh"; + elServerScripts.parentNode.className = "url refresh"; // User Data let userDataProperty = properties["userData"]; @@ -3327,7 +3422,7 @@ function loaded() { let elCollapsible = document.getElementsByClassName("section-header"); let toggleCollapsedEvent = function(event) { - let element = event.target.parentNode.parentNode; + let element = this.parentNode; let isCollapsed = element.dataset.collapsed !== "true"; element.dataset.collapsed = isCollapsed ? "true" : false; element.setAttribute("collapsed", isCollapsed ? "true" : "false"); @@ -3336,7 +3431,7 @@ function loaded() { for (let collapseIndex = 0, numCollapsibles = elCollapsible.length; collapseIndex < numCollapsibles; ++collapseIndex) { let curCollapsibleElement = elCollapsible[collapseIndex]; - curCollapsibleElement.getElementsByTagName('span')[0].addEventListener("click", toggleCollapsedEvent, true); + curCollapsibleElement.addEventListener("click", toggleCollapsedEvent, true); } // Textarea scrollbars From a020a09e8c35b7359fb7842ecf5dc8f14c5d630b Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 8 Nov 2018 16:33:25 -0800 Subject: [PATCH 194/286] Remove redundant dimensions in default entity properties --- scripts/system/edit.js | 1 - 1 file changed, 1 deletion(-) diff --git a/scripts/system/edit.js b/scripts/system/edit.js index d3e9a475ac..35aeac7ab5 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -466,7 +466,6 @@ const DEFAULT_ENTITY_PROPERTIES = { isSpotlight: false, exponent: 1.0, cutoff: 75.0, - dimensions: { x: 20, y: 20, z: 20 }, }, }; From a787f55506f73c2617ce12e5fd6b2d129c9bb03e Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Mon, 12 Nov 2018 12:28:11 -0800 Subject: [PATCH 195/286] Remove dead code and cleanup JS styling for entity properties redesign --- scripts/system/html/css/edit-style.css | 23 ------------------ scripts/system/html/js/entityProperties.js | 28 +++++----------------- 2 files changed, 6 insertions(+), 45 deletions(-) diff --git a/scripts/system/html/css/edit-style.css b/scripts/system/html/css/edit-style.css index c09655b451..aa7ed821c1 100644 --- a/scripts/system/html/css/edit-style.css +++ b/scripts/system/html/css/edit-style.css @@ -1411,29 +1411,6 @@ th#entity-hasScript { display: inline-block; } -/* -#properties-base #div-property-locked { - position: absolute; - top: 6px; - right: 140px; - display: table-cell; - vertical-align: middle; -} - -#properties-base #div-property-visible { - position: absolute; - top: 26px; - right: 20px; - display: table-cell; - vertical-align: middle; -} - -#properties-base #div-property-locked label, -#properties-base #div-property-visible label { - background-position-y: 1px; -} -*/ - #properties-base .checkbox label span { font-family: HiFi-Glyphs; font-size: 20px; diff --git a/scripts/system/html/js/entityProperties.js b/scripts/system/html/js/entityProperties.js index 5d77c56b4a..a236e406ce 100644 --- a/scripts/system/html/js/entityProperties.js +++ b/scripts/system/html/js/entityProperties.js @@ -1116,7 +1116,6 @@ const GROUPS = [ }, { label: "Clone Lifetime", - indentedLabel: true, type: "number", unit: "s", propertyID: "cloneLifetime", @@ -1124,21 +1123,18 @@ const GROUPS = [ }, { label: "Clone Limit", - indentedLabel: true, type: "number", propertyID: "cloneLimit", showPropertyRule: { "cloneable": "true" }, }, { label: "Clone Dynamic", - indentedLabel: true, type: "bool", propertyID: "cloneDynamic", showPropertyRule: { "cloneable": "true" }, }, { label: "Clone Avatar Entity", - indentedLabel: true, type: "bool", propertyID: "cloneAvatarEntity", showPropertyRule: { "cloneable": "true" }, @@ -1426,7 +1422,7 @@ function debugPrint(message) { } function createElementFromHTML(htmlString) { - var elTemplate = document.createElement('template'); + let elTemplate = document.createElement('template'); elTemplate.innerHTML = htmlString.trim(); return elTemplate.content.firstChild; } @@ -1926,11 +1922,6 @@ function createVec3Property(property, elProperty) { elProperty.className = propertyData.vec3Type + " fstuple"; - //let elTuple = document.createElement('div'); - //elTuple.className = "tuple"; - - //elProperty.appendChild(elTuple); - let elInputX = createTupleNumberInput(elProperty, elementID, propertyData.subLabels[VECTOR_ELEMENTS.X_INPUT], propertyData.min, propertyData.max, propertyData.step); let elInputY = createTupleNumberInput(elProperty, elementID, propertyData.subLabels[VECTOR_ELEMENTS.Y_INPUT], @@ -2099,9 +2090,7 @@ function createIconProperty(property, elProperty) { elProperty.appendChild(elSpan); - let elResult = []; - elResult[ICON_ELEMENTS.ICON] = elSpan; - return elResult; + return elSpan; } function createTextureProperty(property, elProperty) { @@ -2164,9 +2153,6 @@ function createButtonsProperty(property, elProperty, elLabel) { elProperty.className = "text"; let hasLabel = propertyData.label !== undefined; - if (hasLabel) { - } - if (propertyData.buttons !== undefined) { addButtons(elProperty, elementID, propertyData.buttons, hasLabel); } @@ -2813,9 +2799,7 @@ function createProperty(propertyData, propertyElementID, propertyName, propertyI break; } case 'icon': { - let elIcon = createIconProperty(property, elProperty); - property.elSpan = elIcon[ICON_ELEMENTS.ICON]; - property.elLabel = elIcon[ICON_ELEMENTS.LABEL]; + property.elSpan = createIconProperty(property, elProperty); break; } case 'texture': { @@ -2834,7 +2818,7 @@ function createProperty(propertyData, propertyElementID, propertyName, propertyI } default: { console.log("EntityProperties - Unknown property type " + - propertyType + " set to property " + propertyID); + propertyType + " set to property " + propertyID); break; } } @@ -2940,9 +2924,9 @@ function loaded() { let elProperty = createElementFromHTML('
'); elContainer.appendChild(elProperty); - if (propertyType == 'triple') { + if (propertyType === 'triple') { elProperty.className = 'flex-row'; - for (var i = 0; i < propertyData.properties.length; ++i) { + for (let i = 0; i < propertyData.properties.length; ++i) { let innerPropertyData = propertyData.properties[i]; let elWrapper = createElementFromHTML('
'); From b3ccce9a3f23a4c8038176b5a3a8097547b0e5f6 Mon Sep 17 00:00:00 2001 From: David Back Date: Mon, 12 Nov 2018 17:30:21 -0800 Subject: [PATCH 196/286] merge follow-up --- scripts/system/html/css/edit-style.css | 32 ---------------------- scripts/system/html/js/entityProperties.js | 22 ++++----------- 2 files changed, 5 insertions(+), 49 deletions(-) diff --git a/scripts/system/html/css/edit-style.css b/scripts/system/html/css/edit-style.css index 1a1d4adaaf..7b62cdc9aa 100644 --- a/scripts/system/html/css/edit-style.css +++ b/scripts/system/html/css/edit-style.css @@ -876,7 +876,6 @@ div.refresh input[type="button"] { } .rgb div.legend + * { clear: both; -<<<<<<< HEAD } .draggable-number { @@ -904,8 +903,6 @@ div.refresh input[type="button"] { .draggable-number input::-webkit-inner-spin-button { -webkit-appearance: none; visibility: hidden; -======= ->>>>>>> a787f55506f73c2617ce12e5fd6b2d129c9bb03e } .row .property { @@ -1444,32 +1441,6 @@ th#entity-hasScript { display: inline-block; } -<<<<<<< HEAD -/* -#properties-base #div-property-locked { - position: absolute; - top: 6px; - right: 140px; - display: table-cell; - vertical-align: middle; -} - -#properties-base #div-property-visible { - position: absolute; - top: 26px; - right: 20px; - display: table-cell; - vertical-align: middle; -} - -#properties-base #div-property-locked label, -#properties-base #div-property-visible label { - background-position-y: 1px; -} -*/ - -======= ->>>>>>> a787f55506f73c2617ce12e5fd6b2d129c9bb03e #properties-base .checkbox label span { font-family: HiFi-Glyphs; font-size: 20px; @@ -1708,8 +1679,6 @@ input.number-slider { #toggle-space-mode.space-mode-world::before { content: "\e02c"; } -<<<<<<< HEAD -======= .container { display: flex; @@ -1808,4 +1777,3 @@ input.number-slider { display: flex; width: 100%; } ->>>>>>> a787f55506f73c2617ce12e5fd6b2d129c9bb03e diff --git a/scripts/system/html/js/entityProperties.js b/scripts/system/html/js/entityProperties.js index 5a3f783c51..7c34e1841b 100644 --- a/scripts/system/html/js/entityProperties.js +++ b/scripts/system/html/js/entityProperties.js @@ -1390,11 +1390,6 @@ const COLOR_ELEMENTS = { BLUE_INPUT: 3, }; -const ICON_ELEMENTS = { - ICON: 0, - LABEL: 1, -}; - const TEXTURE_ELEMENTS = { IMAGE: 0, TEXT_INPUT: 1, @@ -1622,7 +1617,9 @@ function updateVisibleSpaceModeProperties() { let propertySpaceMode = property.spaceMode; if (propertySpaceMode !== PROPERTY_SPACE_MODE.ALL) { showPropertyElement(propertyID, propertySpaceMode === currentSpaceMode); - } + } else { + showPropertyElement(propertyID, true); + } } } } @@ -2189,9 +2186,7 @@ function createProperty(propertyData, propertyElementID, propertyName, propertyI break; } case 'icon': { - let elIcon = createIconProperty(property, elProperty); - property.elSpan = elIcon[ICON_ELEMENTS.ICON]; - property.elLabel = elIcon[ICON_ELEMENTS.LABEL]; + property.elSpan = createIconProperty(property, elProperty); break; } case 'texture': { @@ -2853,7 +2848,6 @@ function loaded() { let property = createProperty(innerPropertyData, propertyElementID, propertyName, propertyID, elWrapper.childNodes[0]); property.isParticleProperty = group.id.includes("particles"); property.elContainer = elContainer; - property.spaceMode = propertySpaceMode; if (property.type !== 'placeholder') { @@ -2978,7 +2972,6 @@ function loaded() { let typeProperty = properties["type"]; typeProperty.elSpan.innerHTML = typeProperty.data.icons[type]; typeProperty.elSpan.style.display = "inline-block"; - typeProperty.elLabel.innerHTML = type + " (" + data.selections.length + ")"; disableProperties(); } else { @@ -3025,7 +3018,6 @@ function loaded() { let isPropertyNotNumber = false; switch (propertyData.type) { case 'number': - case 'slider': isPropertyNotNumber = isNaN(propertyValue) || propertyValue === null; break; case 'vec3': @@ -3057,8 +3049,7 @@ function loaded() { } break; } - case 'number': - case 'slider': { + case 'number': { let multiplier = propertyData.multiplier !== undefined ? propertyData.multiplier : 1; let value = propertyValue / multiplier; if (propertyData.round !== undefined) { @@ -3069,9 +3060,6 @@ function loaded() { } else { property.elInput.value = value; } - if (property.elSlider !== undefined) { - property.elSlider.value = property.elInput.value; - } break; } case 'vec3': From a9ff1f4ecd6356f77f794fc1d6cc9d49f36e2ae2 Mon Sep 17 00:00:00 2001 From: Alexander Ivash Date: Fri, 7 Sep 2018 17:53:45 +0300 Subject: [PATCH 197/286] add onscreen keyboard into 'Calibration' tab of Settings=>Controls dialog --- .../qml/hifi/tablet/ControllerSettings.qml | 31 +++++++++++++++++++ .../qml/hifi/tablet/OpenVrConfiguration.qml | 14 +++++++++ 2 files changed, 45 insertions(+) diff --git a/interface/resources/qml/hifi/tablet/ControllerSettings.qml b/interface/resources/qml/hifi/tablet/ControllerSettings.qml index b8bbd71f33..6727047eb0 100644 --- a/interface/resources/qml/hifi/tablet/ControllerSettings.qml +++ b/interface/resources/qml/hifi/tablet/ControllerSettings.qml @@ -9,6 +9,7 @@ import QtQuick 2.7 import QtQuick.Controls 2.2 import QtQuick.Layouts 1.3 +import QtQuick.Window 2.2 import QtGraphicalEffects 1.0 import Qt.labs.settings 1.0 import stylesUit 1.0 @@ -72,6 +73,11 @@ Item { initialItem: inputConfiguration property alias messageVisible: imageMessageBox.visible property string selectedPlugin: "" + + property bool keyboardEnabled: false + property bool keyboardRaised: false + property bool punctuationMode: false + Rectangle { id: inputConfiguration anchors { @@ -227,6 +233,8 @@ Item { anchors.right: parent.right anchors.top: inputConfiguration.bottom anchors.bottom: parent.bottom + anchors.bottomMargin: keyboard.height + Loader { id: loader asynchronous: false @@ -248,6 +256,29 @@ Item { } } + HifiControls.Keyboard { + id: keyboard + raised: parent.keyboardEnabled && parent.keyboardRaised + onRaisedChanged: { + if (raised) { + // delayed execution to allow loader and its content to adjust size + Qt.callLater(function() { + loader.item.bringToView(Window.activeFocusItem); + }) + } + } + + numeric: parent.punctuationMode + anchors { + bottom: parent.bottom + left: parent.left + right: parent.right + } + + Component.onCompleted: { + parent.keyboardEnabled = HMD.active; + } + } function inputPlugins() { if (checkBox.checked) { diff --git a/interface/resources/qml/hifi/tablet/OpenVrConfiguration.qml b/interface/resources/qml/hifi/tablet/OpenVrConfiguration.qml index 2fc5cc4196..1c629349d8 100644 --- a/interface/resources/qml/hifi/tablet/OpenVrConfiguration.qml +++ b/interface/resources/qml/hifi/tablet/OpenVrConfiguration.qml @@ -32,6 +32,18 @@ Flickable { } } + function bringToView(item) { + var yTop = item.mapToItem(contentItem, 0, 0).y; + var yBottom = yTop + item.height; + + var surfaceTop = contentY; + var surfaceBottom = contentY + height; + + if(yTop < surfaceTop || yBottom > surfaceBottom) { + contentY = yTop - height / 2 + item.height + } + } + Component.onCompleted: { page = config.createObject(flick.contentItem); } @@ -39,6 +51,8 @@ Flickable { id: config Rectangle { id: openVrConfiguration + anchors.fill: parent + property int leftMargin: 75 property int countDown: 0 property string pluginName: "" From 37bb3586eaee91eb0b1e6f202888e0ed196e0e75 Mon Sep 17 00:00:00 2001 From: Alexander Ivash Date: Tue, 13 Nov 2018 19:05:16 +0530 Subject: [PATCH 198/286] show spinbox suffix in separate control to: disallow editing it & allow to pass validator --- .../resources/qml/controlsUit/SpinBox.qml | 27 ++++++++++++++++--- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/interface/resources/qml/controlsUit/SpinBox.qml b/interface/resources/qml/controlsUit/SpinBox.qml index d24c7c5e8c..34794d80c7 100644 --- a/interface/resources/qml/controlsUit/SpinBox.qml +++ b/interface/resources/qml/controlsUit/SpinBox.qml @@ -83,8 +83,10 @@ SpinBox { } validator: DoubleValidator { - bottom: Math.min(spinBox.from, spinBox.to) - top: Math.max(spinBox.from, spinBox.to) + decimals: spinBox.decimals + bottom: Math.min(spinBox.realFrom, spinBox.realTo) + top: Math.max(spinBox.realFrom, spinBox.realTo) + notation: DoubleValidator.StandardNotation } textFromValue: function(value, locale) { @@ -97,20 +99,37 @@ SpinBox { contentItem: TextInput { + id: spinboxText z: 2 color: isLightColorScheme ? (spinBox.activeFocus ? hifi.colors.black : hifi.colors.lightGray) : (spinBox.activeFocus ? hifi.colors.white : hifi.colors.lightGrayText) selectedTextColor: hifi.colors.black selectionColor: hifi.colors.primaryHighlight - text: spinBox.textFromValue(spinBox.value, spinBox.locale) + suffix + text: spinBox.textFromValue(spinBox.value, spinBox.locale) inputMethodHints: spinBox.inputMethodHints validator: spinBox.validator verticalAlignment: Qt.AlignVCenter leftPadding: spinBoxLabelInside.visible ? 30 : hifi.dimensions.textPadding - //rightPadding: hifi.dimensions.spinnerSize width: spinBox.width - hifi.dimensions.spinnerSize onEditingFinished: spinBox.editingFinished() + + Text { + id: suffixText + x: metrics.advanceWidth(spinboxText.text + '*') + height: spinboxText.height + + FontMetrics { + id: metrics + font: spinboxText.font + } + + color: isLightColorScheme + ? (spinBox.activeFocus ? hifi.colors.black : hifi.colors.lightGray) + : (spinBox.activeFocus ? hifi.colors.white : hifi.colors.lightGrayText) + text: suffix + verticalAlignment: Qt.AlignVCenter + } } up.indicator: Item { From 95c51c2ffac2fcdd5e8ca58d58ced4220019b033 Mon Sep 17 00:00:00 2001 From: Alexander Ivash Date: Fri, 7 Sep 2018 20:46:27 +0300 Subject: [PATCH 199/286] unfocus spinboxes on finishing editing --- .../resources/qml/hifi/tablet/OpenVrConfiguration.qml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/interface/resources/qml/hifi/tablet/OpenVrConfiguration.qml b/interface/resources/qml/hifi/tablet/OpenVrConfiguration.qml index 1c629349d8..eccf456b5e 100644 --- a/interface/resources/qml/hifi/tablet/OpenVrConfiguration.qml +++ b/interface/resources/qml/hifi/tablet/OpenVrConfiguration.qml @@ -214,6 +214,7 @@ Flickable { onEditingFinished: { sendConfigurationSettings(); + openVrConfiguration.forceActiveFocus(); } } @@ -231,6 +232,7 @@ Flickable { onEditingFinished: { sendConfigurationSettings(); + openVrConfiguration.forceActiveFocus(); } } } @@ -323,6 +325,7 @@ Flickable { onEditingFinished: { sendConfigurationSettings(); + openVrConfiguration.forceActiveFocus(); } } @@ -339,6 +342,7 @@ Flickable { onEditingFinished: { sendConfigurationSettings(); + openVrConfiguration.forceActiveFocus(); } } } @@ -571,6 +575,7 @@ Flickable { onEditingFinished: { sendConfigurationSettings(); + openVrConfiguration.forceActiveFocus(); } } @@ -587,6 +592,7 @@ Flickable { onEditingFinished: { sendConfigurationSettings(); + openVrConfiguration.forceActiveFocus(); } } } @@ -765,6 +771,7 @@ Flickable { calibrationTimer.interval = realValue * 1000; openVrConfiguration.countDown = realValue; numberAnimation.duration = calibrationTimer.interval; + openVrConfiguration.forceActiveFocus(); } } From 3a62184afd227f41f6fe584a5653a63137f660b0 Mon Sep 17 00:00:00 2001 From: Alexander Ivash Date: Wed, 12 Sep 2018 02:12:05 +0300 Subject: [PATCH 200/286] specify min/max/stepsize --- .../resources/qml/hifi/tablet/OpenVrConfiguration.qml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/interface/resources/qml/hifi/tablet/OpenVrConfiguration.qml b/interface/resources/qml/hifi/tablet/OpenVrConfiguration.qml index eccf456b5e..b68a094846 100644 --- a/interface/resources/qml/hifi/tablet/OpenVrConfiguration.qml +++ b/interface/resources/qml/hifi/tablet/OpenVrConfiguration.qml @@ -568,7 +568,8 @@ Flickable { width: 160 suffix: " cm" label: "Arm Circumference" - minimumValue: 0 + minimumValue: 10.0 + maximumValue: 50.0 realStepSize: 1.0 colorScheme: hifi.colorSchemes.dark realValue: 33.0 @@ -585,6 +586,7 @@ Flickable { label: "Shoulder Width" suffix: " cm" minimumValue: 0 + maximumValue: 50.0 realStepSize: 1.0 decimals: 1 colorScheme: hifi.colorSchemes.dark @@ -763,8 +765,10 @@ Flickable { anchors.left: parent.left anchors.leftMargin: leftMargin - minimumValue: 5 + minimumValue: 0 + maximumValue: 5 realValue: 5 + realStepSize: 1.0 colorScheme: hifi.colorSchemes.dark onEditingFinished: { From b16efb2afbda71e9f54cae7971ee30f24d8b467f Mon Sep 17 00:00:00 2001 From: Alexander Ivash Date: Wed, 12 Sep 2018 02:45:30 +0300 Subject: [PATCH 201/286] set minimum shoulder width to 25 cm (based on discussion with Tony) --- interface/resources/qml/hifi/tablet/OpenVrConfiguration.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/resources/qml/hifi/tablet/OpenVrConfiguration.qml b/interface/resources/qml/hifi/tablet/OpenVrConfiguration.qml index b68a094846..e18fdea444 100644 --- a/interface/resources/qml/hifi/tablet/OpenVrConfiguration.qml +++ b/interface/resources/qml/hifi/tablet/OpenVrConfiguration.qml @@ -585,7 +585,7 @@ Flickable { width: 160 label: "Shoulder Width" suffix: " cm" - minimumValue: 0 + minimumValue: 25.0 maximumValue: 50.0 realStepSize: 1.0 decimals: 1 From e378a1b79cbea8c811af2472422c1d581545ba33 Mon Sep 17 00:00:00 2001 From: NissimHadar Date: Tue, 13 Nov 2018 08:39:52 -0800 Subject: [PATCH 202/286] Added missing CR --- tools/nitpick/src/TestRunner.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/nitpick/src/TestRunner.cpp b/tools/nitpick/src/TestRunner.cpp index 868e608982..db6ce6cece 100644 --- a/tools/nitpick/src/TestRunner.cpp +++ b/tools/nitpick/src/TestRunner.cpp @@ -173,9 +173,9 @@ void TestRunner::setWorkingFolder() { } script.write("#!/bin/sh\n\n"); - script.write("echo resizing interface"); + script.write("echo resizing interface\n"); script.write(("osascript " + _workingFolder + "/setInterfaceSizeAndPosition.scpt\n").toStdString().c_str()); - script.write("echo resize complete"); + script.write("echo resize complete\n"); script.close(); script.setPermissions(QFile::ReadOwner | QFile::WriteOwner | QFile::ExeOwner); #endif From 8af3bf3748c104c283d39db527f16444c0e8fb29 Mon Sep 17 00:00:00 2001 From: Simon Walton Date: Tue, 13 Nov 2018 09:45:18 -0800 Subject: [PATCH 203/286] Add new dependency class (ResourceRequestObserver) to Oven --- tools/oven/src/Oven.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tools/oven/src/Oven.cpp b/tools/oven/src/Oven.cpp index 52b6db1aa5..af98376034 100644 --- a/tools/oven/src/Oven.cpp +++ b/tools/oven/src/Oven.cpp @@ -19,6 +19,7 @@ #include #include #include +#include Oven* Oven::_staticInstance { nullptr }; @@ -31,6 +32,7 @@ Oven::Oven() { // Initialize dependencies for OBJ Baker DependencyManager::set(); DependencyManager::set(false); + DependencyManager::set(); } Oven::~Oven() { From c7e0cb1757b1d906a72b9624c799abb86b868f15 Mon Sep 17 00:00:00 2001 From: NissimHadar Date: Tue, 13 Nov 2018 10:11:22 -0800 Subject: [PATCH 204/286] Moved downloader deletion to after completion of download. --- tools/nitpick/src/ui/Nitpick.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tools/nitpick/src/ui/Nitpick.cpp b/tools/nitpick/src/ui/Nitpick.cpp index a4aef8fad5..8893173284 100644 --- a/tools/nitpick/src/ui/Nitpick.cpp +++ b/tools/nitpick/src/ui/Nitpick.cpp @@ -246,10 +246,6 @@ void Nitpick::downloadFiles(const QStringList& URLs, const QString& directoryNam _ui.progressBar->setValue(0); _ui.progressBar->setVisible(true); - foreach (auto downloader, _downloaders) { - delete downloader; - } - _downloaders.clear(); for (int i = 0; i < _numberOfFilesToDownload; ++i) { downloadFile(URLs[i]); @@ -277,6 +273,10 @@ void Nitpick::saveFile(int index) { } else if (_caller == _testRunner) { _testRunner->downloadComplete(); } + + foreach (auto downloader, _downloaders) { + delete downloader; + } } else { _ui.progressBar->setValue(_numberOfFilesDownloaded); } From 234a46bd4316f641d1d6c3356808a0acecf68cd0 Mon Sep 17 00:00:00 2001 From: NissimHadar Date: Tue, 13 Nov 2018 10:12:06 -0800 Subject: [PATCH 205/286] Evaluate results even if Interface crashed midway. --- tools/nitpick/src/TestRunner.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tools/nitpick/src/TestRunner.cpp b/tools/nitpick/src/TestRunner.cpp index 868e608982..8667852032 100644 --- a/tools/nitpick/src/TestRunner.cpp +++ b/tools/nitpick/src/TestRunner.cpp @@ -539,9 +539,7 @@ void TestRunner::interfaceExecutionComplete() { QFileInfo testCompleted(QDir::toNativeSeparators(_snapshotFolder) +"/tests_completed.txt"); if (!testCompleted.exists()) { - QMessageBox::critical(0, "Tests not completed", "Interface seems to have crashed before completion of the test scripts"); - _runNow->setEnabled(true); - return; + QMessageBox::critical(0, "Tests not completed", "Interface seems to have crashed before completion of the test scripts\nExisting images will be evaluated"); } evaluateResults(); From df2bc6ba5cbf1236f6ba89a2085eadca35df88de Mon Sep 17 00:00:00 2001 From: Simon Walton Date: Tue, 13 Nov 2018 10:18:30 -0800 Subject: [PATCH 206/286] Remove unused function --- libraries/octree/src/OctreeDataUtils.cpp | 28 ------------------------ 1 file changed, 28 deletions(-) diff --git a/libraries/octree/src/OctreeDataUtils.cpp b/libraries/octree/src/OctreeDataUtils.cpp index 8c6e45ae66..f3eed084e0 100644 --- a/libraries/octree/src/OctreeDataUtils.cpp +++ b/libraries/octree/src/OctreeDataUtils.cpp @@ -19,34 +19,6 @@ #include #include -namespace { - // Reads octree file and parses it into a QJsonDocument. Handles both gzipped and non-gzipped files. - // Returns true if the file was successfully opened and parsed, otherwise false. - // Example failures: file does not exist, gzipped file cannot be unzipped, invalid JSON. - bool readOctreeFile(QString path, QJsonDocument* doc) { - QFile file(path); - if (!file.open(QIODevice::ReadOnly)) { - qCritical() << "Cannot open json file for reading: " << path; - return false; - } - - QByteArray data = file.readAll(); - QByteArray jsonData; - - if (!gunzip(data, jsonData)) { - jsonData = data; - } - - QJsonParseError parserError; - *doc = QJsonDocument::fromJson(jsonData, &parserError); - if (parserError.error != QJsonParseError::NoError) { - qWarning() << "Error reading JSON file" << path << "-" << parserError.errorString(); - } - return !doc->isNull(); - } - -} // Anon namespace. - bool OctreeUtils::RawOctreeData::readOctreeDataInfoFromMap(const QVariantMap& map) { if (map.contains("Id") && map.contains("DataVersion") && map.contains("Version")) { id = map["Id"].toUuid(); From 10d045b11b0dfe78eda1dcb82260dfe3dcf0503f Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Thu, 8 Nov 2018 17:16:34 -0800 Subject: [PATCH 207/286] fix infinate loading if entity server is not running --- interface/src/Application.cpp | 1 + interface/src/octree/SafeLanding.cpp | 15 ++++++++++++++- interface/src/octree/SafeLanding.h | 1 + scripts/system/interstitialPage.js | 2 +- 4 files changed, 17 insertions(+), 2 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 75a9f598f7..a9d75e745c 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -5705,6 +5705,7 @@ void Application::update(float deltaTime) { quint64 now = usecTimestampNow(); if (isServerlessMode() || _octreeProcessor.isLoadSequenceComplete()) { bool enableInterstitial = DependencyManager::get()->getDomainHandler().getInterstitialModeEnabled(); + if (gpuTextureMemSizeStable() || !enableInterstitial) { // we've received a new full-scene octree stats packet, or it's been long enough to try again anyway _lastPhysicsCheckTime = now; diff --git a/interface/src/octree/SafeLanding.cpp b/interface/src/octree/SafeLanding.cpp index db381d5350..224ef064b6 100644 --- a/interface/src/octree/SafeLanding.cpp +++ b/interface/src/octree/SafeLanding.cpp @@ -11,6 +11,7 @@ #include "SafeLanding.h" +#include #include #include "EntityTreeRenderer.h" @@ -19,6 +20,8 @@ #include "Application.h" const int SafeLanding::SEQUENCE_MODULO = std::numeric_limits::max() + 1; +const quint64 MAX_ELAPSED_TIME = 1000; // msec +const quint64 MICRO_TO_MILI_SECONDS = 1000; namespace { template bool lessThanWraparound(int a, int b) { @@ -107,7 +110,9 @@ void SafeLanding::noteReceivedsequenceNumber(int sequenceNumber) { } bool SafeLanding::isLoadSequenceComplete() { - if (isEntityLoadingComplete() && isSequenceNumbersComplete()) { + quint64 elapsedTime = (usecTimestampNow() - _startTime) / MICRO_TO_MILI_SECONDS; + if ((isEntityLoadingComplete() && isSequenceNumbersComplete()) || + (elapsedTime >= MAX_ELAPSED_TIME && isEntityServerNotRunning() && _sequenceNumbers.empty())) { Locker lock(_lock); _initialStart = INVALID_SEQUENCE; _initialEnd = INVALID_SEQUENCE; @@ -119,6 +124,14 @@ bool SafeLanding::isLoadSequenceComplete() { return !_trackingEntities; } +bool SafeLanding::isEntityServerNotRunning() { + auto nodeList = DependencyManager::get(); + const auto& domainHandler = nodeList->getDomainHandler(); + auto entityServer = nodeList->soloNodeOfType(NodeType::EntityServer); + + return (domainHandler.isConnected() && !entityServer); +} + float SafeLanding::loadingProgressPercentage() { Locker lock(_lock); static const int MINIMUM_TRACKED_ENTITY_STABILITY_COUNT = 15; diff --git a/interface/src/octree/SafeLanding.h b/interface/src/octree/SafeLanding.h index 51357b60ff..157fda53aa 100644 --- a/interface/src/octree/SafeLanding.h +++ b/interface/src/octree/SafeLanding.h @@ -40,6 +40,7 @@ private: bool isSequenceNumbersComplete(); void debugDumpSequenceIDs() const; bool isEntityLoadingComplete(); + bool isEntityServerNotRunning(); std::mutex _lock; using Locker = std::lock_guard; diff --git a/scripts/system/interstitialPage.js b/scripts/system/interstitialPage.js index b7c5809b3a..9a67964801 100644 --- a/scripts/system/interstitialPage.js +++ b/scripts/system/interstitialPage.js @@ -15,7 +15,7 @@ (function() { Script.include("/~/system/libraries/Xform.js"); Script.include("/~/system/libraries/globals.js"); - var DEBUG = false; + var DEBUG = true; var TOTAL_LOADING_PROGRESS = 3.7; var EPSILON = 0.05; var TEXTURE_EPSILON = 0.01; From 1a95ee2b31fcb3b0dcb77d758ce17d9d4d9fb14a Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Fri, 9 Nov 2018 09:27:55 -0800 Subject: [PATCH 208/286] making requested changes --- interface/src/octree/SafeLanding.cpp | 4 ++-- scripts/system/interstitialPage.js | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/interface/src/octree/SafeLanding.cpp b/interface/src/octree/SafeLanding.cpp index 224ef064b6..ff32e03006 100644 --- a/interface/src/octree/SafeLanding.cpp +++ b/interface/src/octree/SafeLanding.cpp @@ -13,6 +13,7 @@ #include #include +#include #include "EntityTreeRenderer.h" #include "RenderableModelEntityItem.h" @@ -21,7 +22,6 @@ const int SafeLanding::SEQUENCE_MODULO = std::numeric_limits::max() + 1; const quint64 MAX_ELAPSED_TIME = 1000; // msec -const quint64 MICRO_TO_MILI_SECONDS = 1000; namespace { template bool lessThanWraparound(int a, int b) { @@ -110,7 +110,7 @@ void SafeLanding::noteReceivedsequenceNumber(int sequenceNumber) { } bool SafeLanding::isLoadSequenceComplete() { - quint64 elapsedTime = (usecTimestampNow() - _startTime) / MICRO_TO_MILI_SECONDS; + quint64 elapsedTime = (usecTimestampNow() - _startTime) / USECS_PER_MSEC; if ((isEntityLoadingComplete() && isSequenceNumbersComplete()) || (elapsedTime >= MAX_ELAPSED_TIME && isEntityServerNotRunning() && _sequenceNumbers.empty())) { Locker lock(_lock); diff --git a/scripts/system/interstitialPage.js b/scripts/system/interstitialPage.js index 9a67964801..cd8869d805 100644 --- a/scripts/system/interstitialPage.js +++ b/scripts/system/interstitialPage.js @@ -15,7 +15,7 @@ (function() { Script.include("/~/system/libraries/Xform.js"); Script.include("/~/system/libraries/globals.js"); - var DEBUG = true; + var DEBUG = false; var TOTAL_LOADING_PROGRESS = 3.7; var EPSILON = 0.05; var TEXTURE_EPSILON = 0.01; @@ -197,7 +197,7 @@ var loadingBarProgress = Overlays.addOverlay("image3d", { name: "Loading-Bar-Progress", - localPosition: { x: 0.0, y: -0.86, z: 0.0 }, + localPosition: { x: 0.0, y: -0.91, z: 0.0 }, url: LOADING_BAR_PROGRESS, alpha: 1, dimensions: { x: TOTAL_LOADING_PROGRESS, y: 0.3}, @@ -482,7 +482,7 @@ var end = 0; var xLocalPosition = (progressPercentage * (end - start)) + start; var properties = { - localPosition: { x: xLocalPosition, y: -0.93, z: 0.0 }, + localPosition: { x: xLocalPosition, y: -0.91, z: 0.0 }, dimensions: { x: progress, y: 0.3 From 812373cdabe1f8aa81fd3f427bbc58b6326c84c9 Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Fri, 9 Nov 2018 10:37:21 -0800 Subject: [PATCH 209/286] fixing loading bar visual --- scripts/system/interstitialPage.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/system/interstitialPage.js b/scripts/system/interstitialPage.js index cd8869d805..e4dc778985 100644 --- a/scripts/system/interstitialPage.js +++ b/scripts/system/interstitialPage.js @@ -274,6 +274,7 @@ previousCameraMode = Camera.mode; Camera.mode = "first person"; updateProgressBar(0.0); + scaleInterstitialPage(MyAvatar.sensorToWorldScale); timer = Script.setTimeout(update, 2000); } } @@ -482,7 +483,7 @@ var end = 0; var xLocalPosition = (progressPercentage * (end - start)) + start; var properties = { - localPosition: { x: xLocalPosition, y: -0.91, z: 0.0 }, + localPosition: { x: xLocalPosition, y: (HMD.active ? -0.93 : -0.91), z: 0.0 }, dimensions: { x: progress, y: 0.3 From 0b0614c84a47cd501137e0a9b05928b397b68e9e Mon Sep 17 00:00:00 2001 From: David Back Date: Tue, 13 Nov 2018 11:13:00 -0800 Subject: [PATCH 210/286] CR fixes --- scripts/system/html/js/entityList.js | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/scripts/system/html/js/entityList.js b/scripts/system/html/js/entityList.js index 0cc4e5a269..82e64a6fbc 100644 --- a/scripts/system/html/js/entityList.js +++ b/scripts/system/html/js/entityList.js @@ -167,7 +167,7 @@ let entityList = null; // The ListView */ let entityListContextMenu = null; -let currentSortColumn = null; +let currentSortColumn = 'type'; let currentSortOrder = ASCENDING_SORT; let elSortOrders = {}; let typeFilters = []; @@ -181,6 +181,7 @@ let resizeColumnIndex = 0; let startThClick = null; let elEntityTable, + elEntityTableHeader, elEntityTableBody, elEntityTableScroll, elEntityTableHeaderRow, @@ -188,19 +189,19 @@ let elEntityTable, elToggleLocked, elToggleVisible, elDelete, - elFilterTypeSelectBox, + elFilterTypeMultiselectBox, elFilterTypeText, - elFilterTypeCheckboxes, + elFilterTypeOptions, elFilterSearch, elFilterInView, elFilterRadius, elExport, elPal, - elInfoToggle, - elInfoToggleGlyph, elSelectedEntitiesCount, elVisibleEntitiesCount, elNoEntitiesMessage, + elColumnsMultiselectBox, + elColumnsOptions, elToggleSpaceMode; const ENABLE_PROFILING = false; @@ -269,7 +270,6 @@ function loaded() { }; elRefresh.onclick = refreshEntities; elFilterTypeMultiselectBox.onclick = onToggleTypeDropdown; - elFilterTypeSelectBox.onclick = onToggleTypeDropdown; elFilterSearch.onkeyup = refreshEntityList; elFilterSearch.onsearch = refreshEntityList; elFilterInView.onclick = onToggleFilterInView; @@ -348,10 +348,7 @@ function loaded() { elTh.appendChild(elSortOrder); elHeaderTr.appendChild(elTh); - elSortOrders[columnID] = document.querySelector('#' + thID + ' .sort-order'); - if (currentSortColumn === null) { - currentSortColumn = columnID; - } + elSortOrders[columnID] = elSortOrder; // add column to columns dropdown if it is not set to be always shown if (columnData.alwaysShown !== true) { @@ -795,7 +792,7 @@ function loaded() { let elRow = document.createElement("tr"); columns.forEach(function(column) { let elRowColumn = document.createElement("td"); - elRowColumn.className = getColumnClassName(column.columnID); + elRowColumn.className = createColumnClassName(column.columnID); elRow.appendChild(elRowColumn); }); elRow.oncontextmenu = onRowContextMenu; @@ -815,7 +812,7 @@ function loaded() { elCell.innerText = itemData[column.data.propertyID]; } elCell.style = "min-width:" + column.widthPx + "px;" + "max-width:" + column.widthPx + "px;"; - elCell.className = getColumnClassName(column.columnID); + elCell.className = createColumnClassName(column.columnID); } // if this entity was previously selected flag it's row as selected @@ -890,7 +887,7 @@ function loaded() { return -1; } - function getColumnClassName(columnID) { + function createColumnClassName(columnID) { let column = columnsByID[columnID]; let visible = column.elTh.style.visibility !== "hidden"; let className = column.data.glyph ? "glyph" : ""; From f83edf4b7ffa373c8bac88214eb1b0771ca26194 Mon Sep 17 00:00:00 2001 From: amantley Date: Tue, 13 Nov 2018 11:29:01 -0800 Subject: [PATCH 211/286] joint offsets working. reset working. to do: handling extra joints --- libraries/animation/src/AnimSkeleton.cpp | 12 ++++++------ libraries/fbx/src/FBX.h | 2 +- libraries/fbx/src/FBXReader.cpp | 7 +++---- 3 files changed, 10 insertions(+), 11 deletions(-) diff --git a/libraries/animation/src/AnimSkeleton.cpp b/libraries/animation/src/AnimSkeleton.cpp index aec4ae6dda..790c898674 100644 --- a/libraries/animation/src/AnimSkeleton.cpp +++ b/libraries/animation/src/AnimSkeleton.cpp @@ -33,7 +33,7 @@ AnimSkeleton::AnimSkeleton(const HFMModel& hfmModel) { // add offsets for spine2 and the neck static bool once = true; qCDebug(animation) << "the hfm model path is " << hfmModel.originalURL; - if (once && hfmModel.originalURL == "/angus/avatars/engineer_hifinames/engineer_hifinames/engineer_hifinames.fbx") { + //if (once && hfmModel.originalURL == "/angus/avatars/engineer_hifinames/engineer_hifinames/engineer_hifinames.fbx") { //if (once && hfmModel.originalURL == "/angus/avatars/pei_z_neckNexX_spine2NegY_fwd/pei_z_neckNexX_spine2NegY_fwd/pei_z_neckNexX_spine2NegY_fwd.fbx") { once = false; for (int i = 0; i < (int)hfmModel.meshes.size(); i++) { @@ -51,11 +51,11 @@ AnimSkeleton::AnimSkeleton(const HFMModel& hfmModel) { qCDebug(animation) << "found a joint offset to add " << cluster.jointIndex << " " << offset2 << " cluster " << cluster.jointIndex; AnimPose localOffset(hfmModel.jointRotationOffsets[cluster.jointIndex], glm::vec3()); //AnimPose localOffset(offset2, glm::vec3()); - cluster.inverseBindMatrix = (glm::mat4)localOffset.inverse() * cluster.inverseBindMatrix; + cluster.inverseBindMatrix = (glm::mat4)localOffset.inverse() * hfmModel.clusterBindMatrixOriginalValues[i][j]; qCDebug(animation) << "the new bind matrix num: " << cluster.jointIndex << cluster.inverseBindMatrix; - //if ((hfmModel.clusterBindMatrixOriginalValues.size() > i) && (hfmModel.clusterBindMatrixOriginalValues[i].contains(cluster.jointIndex))) { - // qCDebug(animation) << "the saved orig matrix num: " << cluster.jointIndex << hfmModel.clusterBindMatrixOriginalValues[i][cluster.jointIndex]; - //} + if ((hfmModel.clusterBindMatrixOriginalValues.size() > i) && (hfmModel.clusterBindMatrixOriginalValues[i].size() > cluster.jointIndex)) { + qCDebug(animation) << "the saved orig matrix num: " << cluster.jointIndex << hfmModel.clusterBindMatrixOriginalValues[i][j]; + } cluster.inverseBindTransform.evalFromRawMatrix(cluster.inverseBindMatrix); } @@ -80,7 +80,7 @@ AnimSkeleton::AnimSkeleton(const HFMModel& hfmModel) { */ } } - } + //} diff --git a/libraries/fbx/src/FBX.h b/libraries/fbx/src/FBX.h index 342f3c456a..943db4b7fe 100644 --- a/libraries/fbx/src/FBX.h +++ b/libraries/fbx/src/FBX.h @@ -349,7 +349,7 @@ public: Extents meshExtents; QVector animationFrames; - std::vector> clusterBindMatrixOriginalValues; + std::vector> clusterBindMatrixOriginalValues; int getJointIndex(const QString& name) const { return jointIndices.value(name) - 1; } QStringList getJointNames() const; diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index ed5e349957..7a49874abb 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -2033,20 +2033,19 @@ HFMModel* FBXReader::extractHFMModel(const QVariantHash& mapping, const QString& } //hfmModel.jointRotationOffsets.insert(13, glm::quat(0.5f, 0.5f, 0.5f, -0.5f)); //hfmModel.jointRotationOffsets.insert(62, glm::quat(0.7071f, 0.0f, -0.7071f, 0.0f)); - /* + for (int i = 0; i < (int)hfmModel.meshes.size(); i++) { const HFMMesh& mesh = hfmModel.meshes.at(i); - QMap tempBindMat; + vector tempBindMat; for (int j = 0; j < mesh.clusters.size(); j++) { const HFMCluster& cluster = mesh.clusters.at(j); - tempBindMat.insert(cluster.jointIndex, cluster.inverseBindMatrix); + tempBindMat.push_back(cluster.inverseBindMatrix); //if(hfmModel.clusterBindMatrixOriginalValues[i]) //hfmModel.clusterBindMatrixOriginalValues[i].insert(cluster.jointIndex,Matrices::IDENTITY);// cluster.inverseBindMatrix; //glm::mat4 testMat = cluster.inverseBindMatrix; } hfmModel.clusterBindMatrixOriginalValues.push_back(tempBindMat); } - */ return hfmModelPtr; } From c7b3b9c0d2313b416406212fcd976b4f66403c1d Mon Sep 17 00:00:00 2001 From: NissimHadar Date: Tue, 13 Nov 2018 11:49:59 -0800 Subject: [PATCH 212/286] Reverted previous commit. --- tools/nitpick/src/ui/Nitpick.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tools/nitpick/src/ui/Nitpick.cpp b/tools/nitpick/src/ui/Nitpick.cpp index 8893173284..a4aef8fad5 100644 --- a/tools/nitpick/src/ui/Nitpick.cpp +++ b/tools/nitpick/src/ui/Nitpick.cpp @@ -246,6 +246,10 @@ void Nitpick::downloadFiles(const QStringList& URLs, const QString& directoryNam _ui.progressBar->setValue(0); _ui.progressBar->setVisible(true); + foreach (auto downloader, _downloaders) { + delete downloader; + } + _downloaders.clear(); for (int i = 0; i < _numberOfFilesToDownload; ++i) { downloadFile(URLs[i]); @@ -273,10 +277,6 @@ void Nitpick::saveFile(int index) { } else if (_caller == _testRunner) { _testRunner->downloadComplete(); } - - foreach (auto downloader, _downloaders) { - delete downloader; - } } else { _ui.progressBar->setValue(_numberOfFilesDownloaded); } From c80ade98ecfcea0d1a8eeb712647a033e78cd660 Mon Sep 17 00:00:00 2001 From: amantley Date: Tue, 13 Nov 2018 12:10:10 -0800 Subject: [PATCH 213/286] cleaned up white space --- .../qml/hifi/tablet/OpenVrConfiguration.qml | 4 +- interface/src/avatar/MySkeletonModel.cpp | 3 - libraries/animation/src/AnimClip.cpp | 2 - libraries/animation/src/AnimSkeleton.cpp | 84 +++++-------------- libraries/fbx/src/FBXReader.cpp | 13 +-- tools/skeleton-dump/src/SkeletonDumpApp.cpp | 2 - 6 files changed, 24 insertions(+), 84 deletions(-) diff --git a/interface/resources/qml/hifi/tablet/OpenVrConfiguration.qml b/interface/resources/qml/hifi/tablet/OpenVrConfiguration.qml index f91642105f..2fc5cc4196 100644 --- a/interface/resources/qml/hifi/tablet/OpenVrConfiguration.qml +++ b/interface/resources/qml/hifi/tablet/OpenVrConfiguration.qml @@ -9,9 +9,9 @@ import QtQuick 2.5 import QtGraphicalEffects 1.0 -import "../../styles-uit" +import stylesUit 1.0 import "../../controls" -import "../../controls-uit" as HifiControls +import controlsUit 1.0 as HifiControls import "." diff --git a/interface/src/avatar/MySkeletonModel.cpp b/interface/src/avatar/MySkeletonModel.cpp index 720bbd1066..2a21f78b21 100644 --- a/interface/src/avatar/MySkeletonModel.cpp +++ b/interface/src/avatar/MySkeletonModel.cpp @@ -90,7 +90,6 @@ static AnimPose computeHipsInSensorFrame(MyAvatar* myAvatar, bool isFlying) { // Called within Model::simulate call, below. void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { - const HFMModel& hfmModel = getHFMModel(); Head* head = _owningAvatar->getHead(); @@ -230,7 +229,6 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { params.primaryControllerFlags[Rig::PrimaryControllerType_Hips] = (uint8_t)Rig::ControllerFlags::Enabled | (uint8_t)Rig::ControllerFlags::Estimated; // set spine2 if we have hand controllers - //qCDebug(interfaceapp) << "spine 2 joint offset " << jointOffsetMap[13]; if (myAvatar->getControllerPoseInAvatarFrame(controller::Action::RIGHT_HAND).isValid() && myAvatar->getControllerPoseInAvatarFrame(controller::Action::LEFT_HAND).isValid() && !(params.primaryControllerFlags[Rig::PrimaryControllerType_Spine2] & (uint8_t)Rig::ControllerFlags::Enabled)) { @@ -241,7 +239,6 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { bool spine2Exists = _rig.getAbsoluteJointPoseInRigFrame(_rig.indexOfJoint("Spine2"), currentSpine2Pose); bool headExists = _rig.getAbsoluteJointPoseInRigFrame(_rig.indexOfJoint("Head"), currentHeadPose); bool hipsExists = _rig.getAbsoluteJointPoseInRigFrame(_rig.indexOfJoint("Hips"), currentHipsPose); - if (spine2Exists && headExists && hipsExists) { AnimPose rigSpaceYaw(myAvatar->getSpine2RotationRigSpace()); diff --git a/libraries/animation/src/AnimClip.cpp b/libraries/animation/src/AnimClip.cpp index a3d55726d6..eeac8fadce 100644 --- a/libraries/animation/src/AnimClip.cpp +++ b/libraries/animation/src/AnimClip.cpp @@ -101,10 +101,8 @@ void AnimClip::copyFromNetworkAnim() { // build a mapping from animation joint indices to skeleton joint indices. // by matching joints with the same name. - const HFMModel& hfmModel = _networkAnim->getHFMModel(); AnimSkeleton animSkeleton(hfmModel); - const auto animJointCount = animSkeleton.getNumJoints(); const auto skeletonJointCount = _skeleton->getNumJoints(); std::vector jointMap; diff --git a/libraries/animation/src/AnimSkeleton.cpp b/libraries/animation/src/AnimSkeleton.cpp index 790c898674..85c9999788 100644 --- a/libraries/animation/src/AnimSkeleton.cpp +++ b/libraries/animation/src/AnimSkeleton.cpp @@ -15,76 +15,38 @@ #include #include "AnimationLogging.h" -static bool notBound = true; AnimSkeleton::AnimSkeleton(const HFMModel& hfmModel) { - qCDebug(animation) << "in the animSkeleton"; // convert to std::vector of joints std::vector joints; joints.reserve(hfmModel.joints.size()); for (auto& joint : hfmModel.joints) { joints.push_back(joint); } - - glm::quat offset1(0.5f, 0.5f, 0.5f, -0.5f); - glm::quat offset2(0.7071f, 0.0f, -0.7071f, 0.0f); - buildSkeletonFromJoints(joints, hfmModel.jointRotationOffsets); - // add offsets for spine2 and the neck - static bool once = true; - qCDebug(animation) << "the hfm model path is " << hfmModel.originalURL; - //if (once && hfmModel.originalURL == "/angus/avatars/engineer_hifinames/engineer_hifinames/engineer_hifinames.fbx") { - //if (once && hfmModel.originalURL == "/angus/avatars/pei_z_neckNexX_spine2NegY_fwd/pei_z_neckNexX_spine2NegY_fwd/pei_z_neckNexX_spine2NegY_fwd.fbx") { - once = false; - for (int i = 0; i < (int)hfmModel.meshes.size(); i++) { - const HFMMesh& mesh = hfmModel.meshes.at(i); - for (int j = 0; j < mesh.clusters.size(); j++) { + for (int i = 0; i < (int)hfmModel.meshes.size(); i++) { + const HFMMesh& mesh = hfmModel.meshes.at(i); + for (int j = 0; j < mesh.clusters.size(); j++) { - // cast into a non-const reference, so we can mutate the FBXCluster - HFMCluster& cluster = const_cast(mesh.clusters.at(j)); + // cast into a non-const reference, so we can mutate the FBXCluster + HFMCluster& cluster = const_cast(mesh.clusters.at(j)); - // AJT: mutate bind pose! this allows us to oreint the skeleton back into the authored orientaiton before - // rendering, with no runtime overhead. - // this works if clusters match joints one for one. - if (hfmModel.jointRotationOffsets.contains(cluster.jointIndex)) { - qCDebug(animation) << "found a joint offset to add " << cluster.jointIndex << " " << offset2 << " cluster " << cluster.jointIndex; - AnimPose localOffset(hfmModel.jointRotationOffsets[cluster.jointIndex], glm::vec3()); - //AnimPose localOffset(offset2, glm::vec3()); - cluster.inverseBindMatrix = (glm::mat4)localOffset.inverse() * hfmModel.clusterBindMatrixOriginalValues[i][j]; - qCDebug(animation) << "the new bind matrix num: " << cluster.jointIndex << cluster.inverseBindMatrix; - if ((hfmModel.clusterBindMatrixOriginalValues.size() > i) && (hfmModel.clusterBindMatrixOriginalValues[i].size() > cluster.jointIndex)) { - qCDebug(animation) << "the saved orig matrix num: " << cluster.jointIndex << hfmModel.clusterBindMatrixOriginalValues[i][j]; - } - cluster.inverseBindTransform.evalFromRawMatrix(cluster.inverseBindMatrix); + // AJT: mutate bind pose! this allows us to oreint the skeleton back into the authored orientaiton before + // rendering, with no runtime overhead. + if (hfmModel.jointRotationOffsets.contains(cluster.jointIndex)) { + qCDebug(animation) << "found a cluster " << cluster.jointIndex; + AnimPose localOffset(hfmModel.jointRotationOffsets[cluster.jointIndex], glm::vec3()); + cluster.inverseBindMatrix = (glm::mat4)localOffset.inverse() * hfmModel.clusterBindMatrixOriginalValues[i][j]; + qCDebug(animation) << "the new bind matrix num: " << cluster.jointIndex << cluster.inverseBindMatrix; + if ((hfmModel.clusterBindMatrixOriginalValues.size() > i) && (hfmModel.clusterBindMatrixOriginalValues[i].size() > cluster.jointIndex)) { + qCDebug(animation) << "the saved orig matrix num: " << cluster.jointIndex << hfmModel.clusterBindMatrixOriginalValues[i][j]; } - - /* - - if (cluster.jointIndex == 62) { - qCDebug(animation) << "Neck"; - qCDebug(animation) << "found a joint offset to add " << cluster.jointIndex << " " << offset2 << " cluster " << cluster.jointIndex; - AnimPose localOffset(hfmModel.jointRotationOffsets[cluster.jointIndex], glm::vec3()); - //AnimPose localOffset(offset2, glm::vec3()); - cluster.inverseBindMatrix = (glm::mat4)localOffset.inverse() * cluster.inverseBindMatrix; - cluster.inverseBindTransform.evalFromRawMatrix(cluster.inverseBindMatrix); - } - if (cluster.jointIndex == 13) { - qCDebug(animation) << "Spine2"; - qCDebug(animation) << "found a joint offset to add " << cluster.jointIndex << " " << offset1 << " cluster " << cluster.jointIndex; - AnimPose localOffset(hfmModel.jointRotationOffsets[cluster.jointIndex], glm::vec3()); - //AnimPose localOffset(offset1, glm::vec3()); - cluster.inverseBindMatrix = (glm::mat4)localOffset.inverse() * cluster.inverseBindMatrix; - cluster.inverseBindTransform.evalFromRawMatrix(cluster.inverseBindMatrix); - } - */ + cluster.inverseBindTransform.evalFromRawMatrix(cluster.inverseBindMatrix); } } - //} - - - -} + } +} AnimSkeleton::AnimSkeleton(const std::vector& joints, const QMap jointOffsets) { buildSkeletonFromJoints(joints, jointOffsets); @@ -253,19 +215,15 @@ void AnimSkeleton::buildSkeletonFromJoints(const std::vector& joints, qCDebug(animation) << "relative default pose for joint " << i << " " << relDefaultPose.trans() << " " << relDefaultPose.rot(); int parentIndex = getParentIndex(i); - AnimPose newAbsPose; if (parentIndex >= 0) { newAbsPose = _absoluteDefaultPoses[parentIndex] * relDefaultPose; - _absoluteDefaultPoses.push_back(newAbsPose); } else { - _absoluteDefaultPoses.push_back(relDefaultPose); } - } - + for (int k = 0; k < _jointsSize; k++) { int parentIndex2 = getParentIndex(k); if (jointOffsets.contains(k)) { @@ -273,19 +231,15 @@ void AnimSkeleton::buildSkeletonFromJoints(const std::vector& joints, _absoluteDefaultPoses[k] = _absoluteDefaultPoses[k] * localOffset; } if (parentIndex2 >= 0) { - _relativeDefaultPoses.push_back(_absoluteDefaultPoses[parentIndex2].inverse() * _absoluteDefaultPoses[k]); } else { - _relativeDefaultPoses.push_back(_absoluteDefaultPoses[k]); } } - - // re-compute relative poses + // re-compute relative poses //_relativeDefaultPoses = _absoluteDefaultPoses; //convertAbsolutePosesToRelative(_relativeDefaultPoses); - for (int i = 0; i < _jointsSize; i++) { _jointIndicesByName[_joints[i].name] = i; } diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index 7a49874abb..da0fe4fe01 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -632,14 +632,10 @@ QMap getJointRotationOffsets(const QVariantHash& mapping) { if (!isNaN(quatX) && !isNaN(quatY) && !isNaN(quatZ) && !isNaN(quatW)) { glm::quat rotationOffset = glm::quat(quatW, quatX, quatY, quatZ); jointRotationOffsets.insert(jointName, rotationOffset); - } } - qCDebug(modelformat) << "found an offset in fst"; } - qCDebug(modelformat) << "found an offset in fst 2"; } - qCDebug(modelformat) << "found an offset in fst 3"; return jointRotationOffsets; } @@ -2031,18 +2027,15 @@ HFMModel* FBXReader::extractHFMModel(const QVariantHash& mapping, const QString& } qCDebug(modelformat) << "Joint Rotation Offset added to Rig._jointRotationOffsets : " << " jointName: " << jointName << " jointIndex: " << jointIndex << " rotation offset: " << rotationOffset; } - //hfmModel.jointRotationOffsets.insert(13, glm::quat(0.5f, 0.5f, 0.5f, -0.5f)); - //hfmModel.jointRotationOffsets.insert(62, glm::quat(0.7071f, 0.0f, -0.7071f, 0.0f)); - + + // create a backup copy of the bindposes, + // these are needed when we recompute the bindpose offsets on reset. for (int i = 0; i < (int)hfmModel.meshes.size(); i++) { const HFMMesh& mesh = hfmModel.meshes.at(i); vector tempBindMat; for (int j = 0; j < mesh.clusters.size(); j++) { const HFMCluster& cluster = mesh.clusters.at(j); tempBindMat.push_back(cluster.inverseBindMatrix); - //if(hfmModel.clusterBindMatrixOriginalValues[i]) - //hfmModel.clusterBindMatrixOriginalValues[i].insert(cluster.jointIndex,Matrices::IDENTITY);// cluster.inverseBindMatrix; - //glm::mat4 testMat = cluster.inverseBindMatrix; } hfmModel.clusterBindMatrixOriginalValues.push_back(tempBindMat); } diff --git a/tools/skeleton-dump/src/SkeletonDumpApp.cpp b/tools/skeleton-dump/src/SkeletonDumpApp.cpp index 2bb5c758ce..10b13aef36 100644 --- a/tools/skeleton-dump/src/SkeletonDumpApp.cpp +++ b/tools/skeleton-dump/src/SkeletonDumpApp.cpp @@ -54,10 +54,8 @@ SkeletonDumpApp::SkeletonDumpApp(int argc, char* argv[]) : QCoreApplication(argc return; } QByteArray blob = file.readAll(); - std::unique_ptr geometry(readFBX(blob, QVariantHash())); std::unique_ptr skeleton(new AnimSkeleton(*geometry)); - skeleton->dump(verbose); } From dab025304d23c6af5e5bfaaa165841cb79148c52 Mon Sep 17 00:00:00 2001 From: Simon Walton Date: Tue, 13 Nov 2018 12:22:30 -0800 Subject: [PATCH 214/286] Set chunk size to 1 MB --- domain-server/resources/web/content/js/content.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/domain-server/resources/web/content/js/content.js b/domain-server/resources/web/content/js/content.js index 38f93a57be..3f36a1b447 100644 --- a/domain-server/resources/web/content/js/content.js +++ b/domain-server/resources/web/content/js/content.js @@ -23,7 +23,7 @@ $(document).ready(function(){ var fileSize = file.size; var filename = file.name; - var CHUNK_SIZE = 65536; + var CHUNK_SIZE = 1048576; // 1 MiB var isFinal = Boolean(fileSize - offset <= CHUNK_SIZE); var nextChunkSize = Math.min(fileSize - offset, CHUNK_SIZE); From a65466f1546ff42a35d11e8fcc0188dfe1ebb169 Mon Sep 17 00:00:00 2001 From: Simon Walton Date: Tue, 13 Nov 2018 13:33:36 -0800 Subject: [PATCH 215/286] Use traditional C strings rather than raw strings --- libraries/octree/src/Octree.cpp | 11 ++--------- libraries/octree/src/OctreeDataUtils.cpp | 9 ++------- 2 files changed, 4 insertions(+), 16 deletions(-) diff --git a/libraries/octree/src/Octree.cpp b/libraries/octree/src/Octree.cpp index cfa207dbf8..c184fe122d 100644 --- a/libraries/octree/src/Octree.cpp +++ b/libraries/octree/src/Octree.cpp @@ -904,9 +904,7 @@ bool Octree::toJSONString(QString& jsonString, const OctreeElementPointer& eleme top = _rootElement; } - jsonString += QString(R"({ - "DataVersion": %1, - "Entities": [)").arg(_persistDataVersion); + jsonString += QString("{\n \"DataVersion\": %1,\n \"Entities\": [").arg(_persistDataVersion); writeToJSON(jsonString, top); @@ -914,12 +912,7 @@ bool Octree::toJSONString(QString& jsonString, const OctreeElementPointer& eleme PacketType expectedType = expectedDataPacketType(); PacketVersion expectedVersion = versionForPacketType(expectedType); - jsonString += QString(R"( - ], - "Id": "%1", - "Version": %2 -} -)").arg(_persistID.toString()).arg((int)expectedVersion); + jsonString += QString("\n ],\n \"Id\": \"%1\",\n \"Version\": %2\n}\n").arg(_persistID.toString()).arg((int)expectedVersion); return true; } diff --git a/libraries/octree/src/OctreeDataUtils.cpp b/libraries/octree/src/OctreeDataUtils.cpp index f3eed084e0..b861904255 100644 --- a/libraries/octree/src/OctreeDataUtils.cpp +++ b/libraries/octree/src/OctreeDataUtils.cpp @@ -63,16 +63,11 @@ bool OctreeUtils::RawOctreeData::readOctreeDataInfoFromFile(QString path) { QByteArray OctreeUtils::RawOctreeData::toByteArray() { QByteArray jsonString; - jsonString += QString(R"({ - "DataVersion": %1, -)").arg(dataVersion); + jsonString += QString("{\n \"DataVersion\": %1,\n").arg(dataVersion); writeSubclassData(jsonString); - jsonString += QString(R"(, - "Id": "%1", - "Version": %2 -})").arg(id.toString()).arg(version); + jsonString += QString(",\n \"Id\": \"%1\",\n \"Version\": %2\n}").arg(id.toString()).arg(version); return jsonString; } From 620c9e2f93fd62fdfab33a6e73bb6c8035cebd90 Mon Sep 17 00:00:00 2001 From: NissimHadar Date: Tue, 13 Nov 2018 13:38:44 -0800 Subject: [PATCH 216/286] All results now logged, so don't mark as failed. --- tools/nitpick/src/Test.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/nitpick/src/Test.cpp b/tools/nitpick/src/Test.cpp index 13a03c32e8..47458d00ee 100644 --- a/tools/nitpick/src/Test.cpp +++ b/tools/nitpick/src/Test.cpp @@ -167,7 +167,7 @@ void Test::appendTestResultsToFile(const QString& _testResultsFolderPath, TestRe // Create text file describing the failure QTextStream stream(&descriptionFile); - stream << "Test failed in folder " << testResult._pathname.left(testResult._pathname.length() - 1) << endl; // remove trailing '/' + stream << "Test in folder " << testResult._pathname.left(testResult._pathname.length() - 1) << endl; // remove trailing '/' stream << "Expected image was " << testResult._expectedImageFilename << endl; stream << "Actual image was " << testResult._actualImageFilename << endl; stream << "Similarity index was " << testResult._error << endl; From a8d7b0503d10f6d0e7b2104f1f7e921e2558a8f1 Mon Sep 17 00:00:00 2001 From: amantley Date: Tue, 13 Nov 2018 14:24:18 -0800 Subject: [PATCH 217/286] removed more cruft --- libraries/animation/src/AnimSkeleton.cpp | 21 +++++---------------- libraries/fbx/src/FBXReader.cpp | 6 +++--- 2 files changed, 8 insertions(+), 19 deletions(-) diff --git a/libraries/animation/src/AnimSkeleton.cpp b/libraries/animation/src/AnimSkeleton.cpp index 85c9999788..a9dab701ed 100644 --- a/libraries/animation/src/AnimSkeleton.cpp +++ b/libraries/animation/src/AnimSkeleton.cpp @@ -35,12 +35,9 @@ AnimSkeleton::AnimSkeleton(const HFMModel& hfmModel) { // AJT: mutate bind pose! this allows us to oreint the skeleton back into the authored orientaiton before // rendering, with no runtime overhead. if (hfmModel.jointRotationOffsets.contains(cluster.jointIndex)) { - qCDebug(animation) << "found a cluster " << cluster.jointIndex; AnimPose localOffset(hfmModel.jointRotationOffsets[cluster.jointIndex], glm::vec3()); - cluster.inverseBindMatrix = (glm::mat4)localOffset.inverse() * hfmModel.clusterBindMatrixOriginalValues[i][j]; - qCDebug(animation) << "the new bind matrix num: " << cluster.jointIndex << cluster.inverseBindMatrix; - if ((hfmModel.clusterBindMatrixOriginalValues.size() > i) && (hfmModel.clusterBindMatrixOriginalValues[i].size() > cluster.jointIndex)) { - qCDebug(animation) << "the saved orig matrix num: " << cluster.jointIndex << hfmModel.clusterBindMatrixOriginalValues[i][j]; + if ((hfmModel.clusterBindMatrixOriginalValues.size() > i) && (hfmModel.clusterBindMatrixOriginalValues[i].size() > j)) { + cluster.inverseBindMatrix = (glm::mat4)localOffset.inverse() * hfmModel.clusterBindMatrixOriginalValues[i][j]; } cluster.inverseBindTransform.evalFromRawMatrix(cluster.inverseBindMatrix); } @@ -215,30 +212,22 @@ void AnimSkeleton::buildSkeletonFromJoints(const std::vector& joints, qCDebug(animation) << "relative default pose for joint " << i << " " << relDefaultPose.trans() << " " << relDefaultPose.rot(); int parentIndex = getParentIndex(i); - AnimPose newAbsPose; if (parentIndex >= 0) { - newAbsPose = _absoluteDefaultPoses[parentIndex] * relDefaultPose; - _absoluteDefaultPoses.push_back(newAbsPose); + _absoluteDefaultPoses.push_back(_absoluteDefaultPoses[parentIndex] * relDefaultPose); } else { _absoluteDefaultPoses.push_back(relDefaultPose); } } for (int k = 0; k < _jointsSize; k++) { - int parentIndex2 = getParentIndex(k); if (jointOffsets.contains(k)) { AnimPose localOffset(jointOffsets[k], glm::vec3()); _absoluteDefaultPoses[k] = _absoluteDefaultPoses[k] * localOffset; } - if (parentIndex2 >= 0) { - _relativeDefaultPoses.push_back(_absoluteDefaultPoses[parentIndex2].inverse() * _absoluteDefaultPoses[k]); - } else { - _relativeDefaultPoses.push_back(_absoluteDefaultPoses[k]); - } } // re-compute relative poses - //_relativeDefaultPoses = _absoluteDefaultPoses; - //convertAbsolutePosesToRelative(_relativeDefaultPoses); + _relativeDefaultPoses = _absoluteDefaultPoses; + convertAbsolutePosesToRelative(_relativeDefaultPoses); for (int i = 0; i < _jointsSize; i++) { _jointIndicesByName[_joints[i].name] = i; diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index da0fe4fe01..2e4f98ee4c 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -2032,12 +2032,12 @@ HFMModel* FBXReader::extractHFMModel(const QVariantHash& mapping, const QString& // these are needed when we recompute the bindpose offsets on reset. for (int i = 0; i < (int)hfmModel.meshes.size(); i++) { const HFMMesh& mesh = hfmModel.meshes.at(i); - vector tempBindMat; + vector meshBindMatrices; for (int j = 0; j < mesh.clusters.size(); j++) { const HFMCluster& cluster = mesh.clusters.at(j); - tempBindMat.push_back(cluster.inverseBindMatrix); + meshBindMatrices.push_back(cluster.inverseBindMatrix); } - hfmModel.clusterBindMatrixOriginalValues.push_back(tempBindMat); + hfmModel.clusterBindMatrixOriginalValues.push_back(meshBindMatrices); } return hfmModelPtr; } From 91db81cd9410673d859e0e9d58c634546eed766e Mon Sep 17 00:00:00 2001 From: David Back Date: Tue, 13 Nov 2018 15:10:53 -0800 Subject: [PATCH 218/286] merge fix --- interface/src/avatar/AvatarManager.cpp | 2 +- interface/src/avatar/AvatarManager.h | 8 +- scripts/system/html/css/edit-style.css | 98 ---------------------- scripts/system/html/js/entityProperties.js | 6 +- 4 files changed, 6 insertions(+), 108 deletions(-) diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index 5bed6a36d9..76ad49b1f4 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -837,7 +837,7 @@ void AvatarManager::setAvatarSortCoefficient(const QString& name, const QScriptV } } -QVariantMap AvatarManager::getPalData(const QList specificAvatarIdentifiers) { +QVariantMap AvatarManager::getPalData(const QStringList& specificAvatarIdentifiers) { QJsonArray palData; auto avatarMap = getHashCopy(); diff --git a/interface/src/avatar/AvatarManager.h b/interface/src/avatar/AvatarManager.h index c8901065af..75dbbc7abb 100644 --- a/interface/src/avatar/AvatarManager.h +++ b/interface/src/avatar/AvatarManager.h @@ -184,11 +184,11 @@ public: * than iterating over each avatar and obtaining data about them in JavaScript, as that method * locks and unlocks each avatar's data structure potentially hundreds of times per update tick. * @function AvatarManager.getPalData - * @param {string[]} [specificAvatarIdentifiers] - A list of specific Avatar Identifiers about - * which you want to get PAL data - * @returns {object} + * @param {string[]} [specificAvatarIdentifiers=[]] - The list of IDs of the avatars you want the PAL data for. + * If an empty list, the PAL data for all nearby avatars is returned. + * @returns {object[]} An array of objects, each object being the PAL data for an avatar. */ - Q_INVOKABLE QVariantMap getPalData(const QList specificAvatarIdentifiers = QList()); + Q_INVOKABLE QVariantMap getPalData(const QStringList& specificAvatarIdentifiers = QStringList()); float getMyAvatarSendRate() const { return _myAvatarSendRate.rate(); } int getIdentityRequestsSent() const { return _identityRequestsSent; } diff --git a/scripts/system/html/css/edit-style.css b/scripts/system/html/css/edit-style.css index 7b62cdc9aa..28d9115c54 100644 --- a/scripts/system/html/css/edit-style.css +++ b/scripts/system/html/css/edit-style.css @@ -1565,104 +1565,6 @@ input.rename-entity { margin-top: 5px; } -.container { - display: flex; - flex-flow: row nowrap; - justify-content: space-around; - margin-bottom: 8px; - min-height: 28px; -} - -.container > label { - margin-top: 6px; - width: 200px; -} - -.container > div.checkbox { - padding-top: 6px; -} - -.container > .value { - width: 100%; -} - -.container .row { - display: flex; - flex-flow: row nowrap; -} - -.container.shrink { - width: min-content; -} - -.fstuple { - display: flex; - flex-flow: row; -} -.fstuple label { - position: relative; - left: 10px; -} -.fstuple label.red, .fstuple label.x { - color: #C62147; -} -.fstuple label.green, .fstuple label.y { - color: #359D85; -} -.fstuple label.blue, .fstuple label.z { - color: #0093C5; -} - -.xyz.fstuple, .pyr.fstuple { - position: relative; - left: -19px; -} - -.rgb.fstuple .tuple { - display: none; -} - -input.number-slider { - background: #575757; - border-radius: 4px; - color: white; -} - -.fstuple > div { - display: flex; - align-items: center; - justify-content: left; -} - -.flex-row { - display: flex; - flex-flow: row; -} - -.flex-column { - display: flex; - flex-flow: column; -} - -.flex-center { - align-items: center; -} - -.flex-evenly-spaced { - flex: 1; -} - -#property-serverScripts-status { - font-family: Raleway-Light; - font-size: 14px; - margin: 6px 0; -} - -#property-name, #property-id { - display: flex; - width: 100%; -} - #toggle-space-mode::before { font-family: HiFi-Glyphs; font-size: 20px; diff --git a/scripts/system/html/js/entityProperties.js b/scripts/system/html/js/entityProperties.js index 81876cb6df..d4ea980292 100644 --- a/scripts/system/html/js/entityProperties.js +++ b/scripts/system/html/js/entityProperties.js @@ -1116,7 +1116,6 @@ const GROUPS = [ }, { label: "Clone Lifetime", - indentedLabel: true, type: "number", unit: "s", propertyID: "cloneLifetime", @@ -1124,21 +1123,18 @@ const GROUPS = [ }, { label: "Clone Limit", - indentedLabel: true, type: "number", propertyID: "cloneLimit", showPropertyRule: { "cloneable": "true" }, }, { label: "Clone Dynamic", - indentedLabel: true, type: "bool", propertyID: "cloneDynamic", showPropertyRule: { "cloneable": "true" }, }, { label: "Clone Avatar Entity", - indentedLabel: true, type: "bool", propertyID: "cloneAvatarEntity", showPropertyRule: { "cloneable": "true" }, @@ -2193,7 +2189,7 @@ function createProperty(propertyData, propertyElementID, propertyName, propertyI } default: { console.log("EntityProperties - Unknown property type " + - propertyType + " set to property " + propertyID); + propertyType + " set to property " + propertyID); break; } } From ecee31960299f71859d621568ccdfa029c778d8a Mon Sep 17 00:00:00 2001 From: r3tk0n Date: Tue, 13 Nov 2018 15:20:46 -0800 Subject: [PATCH 219/286] Get logic working correctly. --- interface/resources/html/tabletHelp.html | 2 +- interface/src/Application.cpp | 46 +++++++++++++++++++----- 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/interface/resources/html/tabletHelp.html b/interface/resources/html/tabletHelp.html index 8d4214d5af..dd7931d0ed 100644 --- a/interface/resources/html/tabletHelp.html +++ b/interface/resources/html/tabletHelp.html @@ -144,7 +144,7 @@ handControllerImageURL = "img/tablet-help-oculus.jpg"; break; default: - handControllerImageURL = null; + handControllerImageURL = ""; count = 2; } diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index e537d618e9..9430d15794 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -3400,21 +3400,51 @@ void Application::showHelp() { static const QString TAB_GAMEPAD = "gamepad"; static const QString TAB_HAND_CONTROLLERS = "handControllers"; - QString handControllerName = ""; + QString handControllerName; QString defaultTab = TAB_KEYBOARD_MOUSE; - if (PluginUtils::isViveControllerAvailable()) { - defaultTab = TAB_HAND_CONTROLLERS; - handControllerName = HAND_CONTROLLER_NAME_VIVE; - } else if (PluginUtils::isOculusTouchControllerAvailable()) { - defaultTab = TAB_HAND_CONTROLLERS; - handControllerName = HAND_CONTROLLER_NAME_OCULUS_TOUCH; - } else if (qApp->getActiveDisplayPlugin()->getName() == "WindowMS") { + qDebug() << "Printing names..."; + for (auto& inputPlugin : PluginManager::getInstance()->getInputPlugins()) { + qDebug() << inputPlugin->getName(); + auto subdeviceNames = inputPlugin->getSubdeviceNames(); + for (auto& subdeviceName : subdeviceNames) { + qDebug() << " -|" << subdeviceName; + } + } + + qDebug() << "----------------------"; + + auto displayPlugins = PluginManager::getInstance()->getDisplayPlugins(); + for (auto& displayPlugin : displayPlugins) { + qDebug() << displayPlugin->getName(); + } + + if (PluginUtils::isHMDAvailable("WindowsMR")) { defaultTab = TAB_HAND_CONTROLLERS; handControllerName = HAND_CONTROLLER_NAME_WINDOWS_MR; + //} else if (PluginUtils::isViveControllerAvailable()) { + } else if (PluginUtils::isHMDAvailable("HTC Vive")) { + defaultTab = TAB_HAND_CONTROLLERS; + handControllerName = HAND_CONTROLLER_NAME_VIVE; + } else if (PluginUtils::isHMDAvailable("Oculus Rift")) { + defaultTab = TAB_HAND_CONTROLLERS; + handControllerName = HAND_CONTROLLER_NAME_OCULUS_TOUCH; } else if (PluginUtils::isXboxControllerAvailable()) { defaultTab = TAB_GAMEPAD; } + //if (QString::compare(qApp->getActiveDisplayPlugin()->getName(), "WindowsMR") == 0) { + // defaultTab = TAB_HAND_CONTROLLERS; + // handControllerName = HAND_CONTROLLER_NAME_WINDOWS_MR; + ////} else if (PluginUtils::isViveControllerAvailable()) { + //} else if (QString::compare(qApp->getActiveDisplayPlugin()->getName(), "HTC Vive") == 0) { + // defaultTab = TAB_HAND_CONTROLLERS; + // handControllerName = HAND_CONTROLLER_NAME_VIVE; + //} else if (PluginUtils::isOculusTouchControllerAvailable()) { + // defaultTab = TAB_HAND_CONTROLLERS; + // handControllerName = HAND_CONTROLLER_NAME_OCULUS_TOUCH; + //} else if (PluginUtils::isXboxControllerAvailable()) { + // defaultTab = TAB_GAMEPAD; + //} // TODO need some way to detect windowsMR to load controls reference default tab in Help > Controls Reference menu. QUrlQuery queryString; From dd8bbeb0e5378c49d22db7b740a28cfe8e98abea Mon Sep 17 00:00:00 2001 From: r3tk0n Date: Tue, 13 Nov 2018 15:36:25 -0800 Subject: [PATCH 220/286] Clean up code and remove comments. --- interface/src/Application.cpp | 41 ++++++----------------------------- 1 file changed, 7 insertions(+), 34 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 9430d15794..aec580ff9d 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -3396,6 +3396,10 @@ void Application::showHelp() { static const QString HAND_CONTROLLER_NAME_OCULUS_TOUCH = "oculus"; static const QString HAND_CONTROLLER_NAME_WINDOWS_MR = "windowsMR"; + static const QString VIVE_PLUGIN_NAME = "HTC Vive"; + static const QString OCULUS_RIFT_PLUGIN_NAME = "Oculus Rift"; + static const QString WINDOWS_MR_PLUGIN_NAME = "WindowsMR"; + static const QString TAB_KEYBOARD_MOUSE = "kbm"; static const QString TAB_GAMEPAD = "gamepad"; static const QString TAB_HAND_CONTROLLERS = "handControllers"; @@ -3403,49 +3407,18 @@ void Application::showHelp() { QString handControllerName; QString defaultTab = TAB_KEYBOARD_MOUSE; - qDebug() << "Printing names..."; - for (auto& inputPlugin : PluginManager::getInstance()->getInputPlugins()) { - qDebug() << inputPlugin->getName(); - auto subdeviceNames = inputPlugin->getSubdeviceNames(); - for (auto& subdeviceName : subdeviceNames) { - qDebug() << " -|" << subdeviceName; - } - } - - qDebug() << "----------------------"; - - auto displayPlugins = PluginManager::getInstance()->getDisplayPlugins(); - for (auto& displayPlugin : displayPlugins) { - qDebug() << displayPlugin->getName(); - } - - if (PluginUtils::isHMDAvailable("WindowsMR")) { + if (PluginUtils::isHMDAvailable(WINDOWS_MR_PLUGIN_NAME)) { defaultTab = TAB_HAND_CONTROLLERS; handControllerName = HAND_CONTROLLER_NAME_WINDOWS_MR; - //} else if (PluginUtils::isViveControllerAvailable()) { - } else if (PluginUtils::isHMDAvailable("HTC Vive")) { + } else if (PluginUtils::isHMDAvailable(VIVE_PLUGIN_NAME)) { defaultTab = TAB_HAND_CONTROLLERS; handControllerName = HAND_CONTROLLER_NAME_VIVE; - } else if (PluginUtils::isHMDAvailable("Oculus Rift")) { + } else if (PluginUtils::isHMDAvailable(OCULUS_RIFT_PLUGIN_NAME)) { defaultTab = TAB_HAND_CONTROLLERS; handControllerName = HAND_CONTROLLER_NAME_OCULUS_TOUCH; } else if (PluginUtils::isXboxControllerAvailable()) { defaultTab = TAB_GAMEPAD; } - //if (QString::compare(qApp->getActiveDisplayPlugin()->getName(), "WindowsMR") == 0) { - // defaultTab = TAB_HAND_CONTROLLERS; - // handControllerName = HAND_CONTROLLER_NAME_WINDOWS_MR; - ////} else if (PluginUtils::isViveControllerAvailable()) { - //} else if (QString::compare(qApp->getActiveDisplayPlugin()->getName(), "HTC Vive") == 0) { - // defaultTab = TAB_HAND_CONTROLLERS; - // handControllerName = HAND_CONTROLLER_NAME_VIVE; - //} else if (PluginUtils::isOculusTouchControllerAvailable()) { - // defaultTab = TAB_HAND_CONTROLLERS; - // handControllerName = HAND_CONTROLLER_NAME_OCULUS_TOUCH; - //} else if (PluginUtils::isXboxControllerAvailable()) { - // defaultTab = TAB_GAMEPAD; - //} - // TODO need some way to detect windowsMR to load controls reference default tab in Help > Controls Reference menu. QUrlQuery queryString; queryString.addQueryItem("handControllerName", handControllerName); From 8db81d9ce6451739739d94493a41ed7153b84e26 Mon Sep 17 00:00:00 2001 From: David Back Date: Tue, 13 Nov 2018 16:04:08 -0800 Subject: [PATCH 221/286] restructure draggable numbers to be more like blender --- scripts/system/html/css/edit-style.css | 72 ++++++-- scripts/system/html/js/draggableNumber.js | 145 ++++++++++----- scripts/system/html/js/entityProperties.js | 204 ++++++++++++++------- 3 files changed, 302 insertions(+), 119 deletions(-) diff --git a/scripts/system/html/css/edit-style.css b/scripts/system/html/css/edit-style.css index 28d9115c54..883e0ab9d7 100644 --- a/scripts/system/html/css/edit-style.css +++ b/scripts/system/html/css/edit-style.css @@ -880,30 +880,76 @@ div.refresh input[type="button"] { .draggable-number { position: relative; - right: 10px; +} +.draggable-number div { + height: 28px; + width: 92px; +} +.draggable-number.text { + display: inline-block; + color: #afafaf; + background-color: #252525; + font-family: FiraSans-SemiBold; + font-size: 15px; + margin: 0; + padding: 0 16px; + height: 28px; + width: 100%; + line-height: 2; +} +.draggable-number.text:hover { + cursor: ew-resize; } .draggable-number span { - position: relative; - top: -2px; + position: absolute; display: inline-block; - font-family: hifi-glyphs; + font-family: HiFi-Glyphs; font-size: 20px; z-index: 2; } +.draggable-number span:hover { + cursor: default; +} .draggable-number.left-arrow { - left: 17px; + top: -5px; + right: 106px; transform: rotate(180deg); } .draggable-number.right-arrow { - right: 17px; + top: -5px; + left: 106px; } -.draggable-number.fstuple span { +.draggable-number input[type=number] { + position: absolute; + right: 0; + width: 100%; +} +.draggable-number input[type=button] { + position: absolute; top: 0; } .draggable-number input::-webkit-inner-spin-button { -webkit-appearance: none; visibility: hidden; } +.draggable-number.fstuple { + height: 28px; + width: 124px; + left: 12px; +} +.draggable-number.fstuple + .draggable-number.fstuple { + padding-left: 28px; +} +.draggable-number.fstuple input { + right: -10px; +} +.draggable-number.fstuple .sublabel { + position: absolute; + top: 0; + left: -16px; + font-family: FiraSans-SemiBold; + font-size: 15px; +} .row .property { width: auto; @@ -921,10 +967,10 @@ div.refresh input[type="button"] { .property.texture { display: block; } -.property.texture input{ +.property.texture input { margin: 0.4rem 0; } -.texture-image img{ +.texture-image img { padding: 0; margin: 0; width: 100%; @@ -1466,12 +1512,12 @@ th#entity-hasScript { } input#property-scale-button-rescale { - margin-top: 6px; min-width: 50px; + left: 152px; } input#property-scale-button-reset { - margin-top: 6px; margin-right: 0; + left: 250px; } #property-userData-button-edit, @@ -1661,6 +1707,10 @@ input.number-slider { flex-flow: column; } +.flex-column + .flex-column { + padding-left: 50px; +} + .flex-center { align-items: center; } diff --git a/scripts/system/html/js/draggableNumber.js b/scripts/system/html/js/draggableNumber.js index e961bbb4a7..326854bc92 100644 --- a/scripts/system/html/js/draggableNumber.js +++ b/scripts/system/html/js/draggableNumber.js @@ -6,67 +6,125 @@ // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +const DELTA_X_FOCUS_THRESHOLD = 2; + function DraggableNumber(min, max, step) { this.min = min; this.max = max; this.step = step !== undefined ? step : 1; - this.startEvent = null; - this.inputChangeFunction = null; + this.initialMouseEvent = null; + this.lastMouseEvent = null; + this.valueChangeFunction = null; this.initialize(); } DraggableNumber.prototype = { - onMouseDown: function(event) { - let that = this.draggableNumber; - that.mouseDown(event); - }, - mouseDown: function(event) { - this.startEvent = event; - document.addEventListener("mousemove", this.onMouseMove); - document.addEventListener("mouseup", this.onMouseUp); + if (event.target === this.elText) { + this.initialMouseEvent = event; + this.lastMouseEvent = event; + document.addEventListener("mousemove", this.onMouseMove); + document.addEventListener("mouseup", this.onMouseUp); + } }, - mouseMove: function(event, that) { - if (this.startEvent) { - let dx = event.clientX - this.startEvent.clientX; - let valueChange = dx * this.step; - let newValue = parseFloat(this.elInput.value) + valueChange; - if (this.min !== undefined && newValue < this.min) { - newValue = this.min; - } else if (this.max !== undefined && newValue > this.max) { - newValue = this.max; - } - this.elInput.value = newValue; - if (this.inputChangeFunction) { - this.inputChangeFunction(); - } - this.startEvent = event; + mouseMove: function(event) { + if (this.lastMouseEvent) { + let dx = event.clientX - this.lastMouseEvent.clientX; + let inputChanged = dx !== 0; + if (inputChanged) { + while (dx !== 0) { + if (dx > 0) { + this.stepUp(); + --dx; + } else { + this.stepDown(); + ++dx; + } + } + if (this.valueChangeFunction) { + this.valueChangeFunction(); + } + } + this.lastMouseEvent = event; } }, mouseUp: function(event) { - this.startEvent = null; - document.removeEventListener("mousemove", this.onMouseMove); - document.removeEventListener("mouseup", this.onMouseUp); + if (this.initialMouseEvent) { + let dx = event.clientX - this.initialMouseEvent.clientX; + if (dx <= DELTA_X_FOCUS_THRESHOLD) { + this.elInput.style.visibility = "visible"; + this.elText.style.visibility = "hidden"; + } + this.initialMouseEvent = null; + this.lastMouseEvent = null; + document.removeEventListener("mousemove", this.onMouseMove); + document.removeEventListener("mouseup", this.onMouseUp); + } }, - - setInputChangeFunction: function(inputChangeFunction) { - if (this.inputChangeFunction) { - this.elInput.removeEventListener('change', this.inputChangeFunction); + + stepUp: function() { + this.elInput.stepUp(); + this.inputChange(); + }, + + stepDown: function() { + this.elInput.stepDown(); + this.inputChange(); + }, + + setValue: function(newValue) { + this.elInput.value = newValue; + this.elText.firstChild.data = newValue; + }, + + setValueChangeFunction: function(valueChangeFunction) { + if (this.valueChangeFunction) { + this.elInput.removeEventListener("change", this.valueChangeFunction); } - this.inputChangeFunction = inputChangeFunction.bind(this.elInput); - this.elInput.addEventListener('change', this.inputChangeFunction); + this.valueChangeFunction = valueChangeFunction.bind(this.elInput); + this.elInput.addEventListener("change", this.valueChangeFunction); }, + + inputChange: function() { + this.setValue(this.elInput.value); + }, + + inputBlur: function() { + this.elInput.style.visibility = "hidden"; + this.elText.style.visibility = "visible"; + }, initialize: function() { + this.onMouseDown = this.mouseDown.bind(this); this.onMouseMove = this.mouseMove.bind(this); this.onMouseUp = this.mouseUp.bind(this); + this.onStepUp = this.stepUp.bind(this); + this.onStepDown = this.stepDown.bind(this); + this.onInputChange = this.inputChange.bind(this); + this.onInputBlur = this.inputBlur.bind(this); this.elDiv = document.createElement('div'); this.elDiv.className = "draggable-number"; + + this.elText = document.createElement('label'); + this.elText.className = "draggable-number text"; + this.elText.innerText = " "; + this.elText.style.visibility = "visible"; + this.elText.addEventListener("mousedown", this.onMouseDown); + + this.elLeftArrow = document.createElement('span'); + this.elRightArrow = document.createElement('span'); + this.elLeftArrow.className = 'draggable-number left-arrow'; + this.elLeftArrow.innerHTML = 'D'; + this.elLeftArrow.addEventListener("click", this.onStepDown); + this.elRightArrow.className = 'draggable-number right-arrow'; + this.elRightArrow.innerHTML = 'D'; + this.elRightArrow.addEventListener("click", this.onStepUp); this.elInput = document.createElement('input'); + this.elInput.className = "draggable-number input"; this.elInput.setAttribute("type", "number"); if (this.min !== undefined) { this.elInput.setAttribute("min", this.min); @@ -77,18 +135,13 @@ DraggableNumber.prototype = { if (this.step !== undefined) { this.elInput.setAttribute("step", this.step); } - this.elInput.draggableNumber = this; - this.elInput.addEventListener("mousedown", this.onMouseDown); - - this.elLeftArrow = document.createElement('span'); - this.elRightArrow = document.createElement('span'); - this.elLeftArrow.className = 'draggable-number left-arrow'; - this.elLeftArrow.innerHTML = 'D'; - this.elRightArrow.className = 'draggable-number right-arrow'; - this.elRightArrow.innerHTML = 'D'; + this.elInput.style.visibility = "hidden"; + this.elInput.addEventListener("change", this.onInputChange); + this.elInput.addEventListener("blur", this.onInputBlur); - this.elDiv.appendChild(this.elLeftArrow); - this.elDiv.appendChild(this.elInput); - this.elDiv.appendChild(this.elRightArrow); + this.elText.appendChild(this.elLeftArrow); + this.elText.appendChild(this.elInput); + this.elText.appendChild(this.elRightArrow); + this.elDiv.appendChild(this.elText); } }; diff --git a/scripts/system/html/js/entityProperties.js b/scripts/system/html/js/entityProperties.js index d4ea980292..cf7360b927 100644 --- a/scripts/system/html/js/entityProperties.js +++ b/scripts/system/html/js/entityProperties.js @@ -1374,16 +1374,16 @@ const PROPERTY_NAME_DIVISION = { }; const VECTOR_ELEMENTS = { - X_INPUT: 0, - Y_INPUT: 1, - Z_INPUT: 2, + X_NUMBER: 0, + Y_NUMBER: 1, + Z_NUMBER: 2, }; const COLOR_ELEMENTS = { COLOR_PICKER: 0, - RED_INPUT: 1, - GREEN_INPUT: 2, - BLUE_INPUT: 3, + RED_NUMBER: 1, + GREEN_NUMBER: 2, + BLUE_NUMBER: 3, }; const TEXTURE_ELEMENTS = { @@ -1417,16 +1417,17 @@ function getPropertyInputElement(propertyID) { switch (property.data.type) { case 'string': case 'bool': - case 'number': case 'dropdown': case 'textarea': case 'texture': return property.elInput; + case 'number': + return property.elNumber.elInput; case 'vec3': case 'vec2': - return { x: property.elInputX, y: property.elInputY, z: property.elInputZ }; + return { x: property.elNumberX.elInput, y: property.elNumberY.elInput, z: property.elNumberZ.elInput }; case 'color': - return { red: property.elInputR, green: property.elInputG, blue: property.elInputB }; + return { red: property.elNumberR.elInput, green: property.elNumberG.elInput, blue: property.elNumberB.elInput }; case 'icon': return property.elLabel; default: @@ -1498,26 +1499,26 @@ function resetProperties() { } case 'number': { if (propertyData.defaultValue !== undefined) { - property.elInput.value = propertyData.defaultValue; + property.elNumber.setValue(propertyData.defaultValue); } else { - property.elInput.value = ""; + property.elNumber.setValue(""); } break; } case 'vec3': case 'vec2': { - property.elInputX.value = ""; - property.elInputY.value = ""; - if (property.elInputZ !== undefined) { - property.elInputZ.value = ""; + property.elNumberX.setValue(""); + property.elNumberY.setValue(""); + if (property.elNumberZ !== undefined) { + property.elNumberZ.setValue(""); } break; } case 'color': { property.elColorPicker.style.backgroundColor = "rgb(" + 0 + "," + 0 + "," + 0 + ")"; - property.elInputR.value = ""; - property.elInputG.value = ""; - property.elInputB.value = ""; + property.elNumberR.setValue(""); + property.elNumberG.setValue(""); + property.elNumberB.setValue(""); break; } case 'dropdown': { @@ -1819,17 +1820,17 @@ function createNumberProperty(property, elProperty) { elDraggableNumber.elInput.value = defaultValue; } - let inputChangeFunction = createEmitNumberPropertyUpdateFunction(propertyName, propertyData.multiplier, propertyData.decimals, property.isParticleProperty); - elDraggableNumber.setInputChangeFunction(inputChangeFunction); + let valueChangeFunction = createEmitNumberPropertyUpdateFunction(propertyName, propertyData.multiplier, property.isParticleProperty); + elDraggableNumber.setValueChangeFunction(valueChangeFunction); elDraggableNumber.elInput.setAttribute("id", elementID); elProperty.appendChild(elDraggableNumber.elDiv); if (propertyData.buttons !== undefined) { - addButtons(elDraggableNumber.elDiv, elementID, propertyData.buttons, false); + addButtons(elDraggableNumber.elText, elementID, propertyData.buttons, false); } - return elDraggableNumber.elInput; + return elDraggableNumber; } function createVec3Property(property, elProperty) { @@ -1839,24 +1840,24 @@ function createVec3Property(property, elProperty) { elProperty.className = propertyData.vec3Type + " fstuple"; - let elNumberX = createTupleNumberInput(elProperty, elementID, propertyData.subLabels[VECTOR_ELEMENTS.X_INPUT], + let elNumberX = createTupleNumberInput(elProperty, elementID, propertyData.subLabels[VECTOR_ELEMENTS.X_NUMBER], propertyData.min, propertyData.max, propertyData.step); - let elNumberY = createTupleNumberInput(elProperty, elementID, propertyData.subLabels[VECTOR_ELEMENTS.Y_INPUT], + let elNumberY = createTupleNumberInput(elProperty, elementID, propertyData.subLabels[VECTOR_ELEMENTS.Y_NUMBER], propertyData.min, propertyData.max, propertyData.step); - let elNumberZ = createTupleNumberInput(elProperty, elementID, propertyData.subLabels[VECTOR_ELEMENTS.Z_INPUT], + let elNumberZ = createTupleNumberInput(elProperty, elementID, propertyData.subLabels[VECTOR_ELEMENTS.Z_NUMBER], propertyData.min, propertyData.max, propertyData.step); - let inputChangeFunction = createEmitVec3PropertyUpdateFunction(propertyName, elNumberX.elInput, elNumberY.elInput, + let valueChangeFunction = createEmitVec3PropertyUpdateFunction(propertyName, elNumberX.elInput, elNumberY.elInput, elNumberZ.elInput, propertyData.multiplier, property.isParticleProperty); - elNumberX.setInputChangeFunction(inputChangeFunction); - elNumberY.setInputChangeFunction(inputChangeFunction); - elNumberZ.setInputChangeFunction(inputChangeFunction); + elNumberX.setValueChangeFunction(valueChangeFunction); + elNumberY.setValueChangeFunction(valueChangeFunction); + elNumberZ.setValueChangeFunction(valueChangeFunction); let elResult = []; - elResult[VECTOR_ELEMENTS.X_INPUT] = elNumberX.elInput; - elResult[VECTOR_ELEMENTS.Y_INPUT] = elNumberY.elInput; - elResult[VECTOR_ELEMENTS.Z_INPUT] = elNumberZ.elInput; + elResult[VECTOR_ELEMENTS.X_NUMBER] = elNumberX; + elResult[VECTOR_ELEMENTS.Y_NUMBER] = elNumberY; + elResult[VECTOR_ELEMENTS.Z_NUMBER] = elNumberZ; return elResult; } @@ -1872,19 +1873,19 @@ function createVec2Property(property, elProperty) { elProperty.appendChild(elTuple); - let elNumberX = createTupleNumberInput(elProperty, elementID, propertyData.subLabels[VECTOR_ELEMENTS.X_INPUT], + let elNumberX = createTupleNumberInput(elProperty, elementID, propertyData.subLabels[VECTOR_ELEMENTS.X_NUMBER], propertyData.min, propertyData.max, propertyData.step); - let elNumberY = createTupleNumberInput(elProperty, elementID, propertyData.subLabels[VECTOR_ELEMENTS.Y_INPUT], + let elNumberY = createTupleNumberInput(elProperty, elementID, propertyData.subLabels[VECTOR_ELEMENTS.Y_NUMBER], propertyData.min, propertyData.max, propertyData.step); - let inputChangeFunction = createEmitVec2PropertyUpdateFunction(propertyName, elNumberX.elInput, elNumberY.elInput, + let valueChangeFunction = createEmitVec2PropertyUpdateFunction(propertyName, elNumberX.elInput, elNumberY.elInput, propertyData.multiplier, property.isParticleProperty); - elNumberX.setInputChangeFunction(inputChangeFunction); - elNumberY.setInputChangeFunction(inputChangeFunction); + elNumberX.setValueChangeFunction(valueChangeFunction); + elNumberY.setValueChangeFunction(valueChangeFunction); let elResult = []; - elResult[VECTOR_ELEMENTS.X_INPUT] = elNumberX.elInput; - elResult[VECTOR_ELEMENTS.Y_INPUT] = elNumberY.elInput; + elResult[VECTOR_ELEMENTS.X_NUMBER] = elNumberX; + elResult[VECTOR_ELEMENTS.Y_NUMBER] = elNumberY; return elResult; } @@ -1908,11 +1909,11 @@ function createColorProperty(property, elProperty) { let elNumberG = createTupleNumberInput(elTuple, elementID, "green", COLOR_MIN, COLOR_MAX, COLOR_STEP); let elNumberB = createTupleNumberInput(elTuple, elementID, "blue", COLOR_MIN, COLOR_MAX, COLOR_STEP); - let inputChangeFunction = createEmitColorPropertyUpdateFunction(propertyName, elNumberR.elInput, elNumberG.elInput, + let valueChangeFunction = createEmitColorPropertyUpdateFunction(propertyName, elNumberR.elInput, elNumberG.elInput, elNumberB.elInput, property.isParticleProperty); - elNumberR.setInputChangeFunction(inputChangeFunction); - elNumberG.setInputChangeFunction(inputChangeFunction); - elNumberB.setInputChangeFunction(inputChangeFunction); + elNumberR.setValueChangeFunction(valueChangeFunction); + elNumberG.setValueChangeFunction(valueChangeFunction); + elNumberB.setValueChangeFunction(valueChangeFunction); let colorPickerID = "#" + elementID; colorPickers[colorPickerID] = $(colorPickerID).colpick({ @@ -1941,9 +1942,9 @@ function createColorProperty(property, elProperty) { let elResult = []; elResult[COLOR_ELEMENTS.COLOR_PICKER] = elColorPicker; - elResult[COLOR_ELEMENTS.RED_INPUT] = elNumberR.elInput; - elResult[COLOR_ELEMENTS.GREEN_INPUT] = elNumberG.elInput; - elResult[COLOR_ELEMENTS.BLUE_INPUT] = elNumberB.elInput; + elResult[COLOR_ELEMENTS.RED_NUMBER] = elNumberR; + elResult[COLOR_ELEMENTS.GREEN_NUMBER] = elNumberG; + elResult[COLOR_ELEMENTS.BLUE_NUMBER] = elNumberB; return elResult; } @@ -2081,14 +2082,14 @@ function createTupleNumberInput(elTuple, propertyElementID, subLabel, min, max, let elementID = propertyElementID + "-" + subLabel.toLowerCase(); let elLabel = document.createElement('label'); - elLabel.className = subLabel; + elLabel.className = "sublabel " + subLabel; elLabel.innerText = subLabel[0].toUpperCase() + subLabel.slice(1); elLabel.setAttribute("for", elementID); let elDraggableNumber = new DraggableNumber(min, max, step); elDraggableNumber.elInput.setAttribute("id", elementID); elDraggableNumber.elDiv.className += " fstuple"; - elDraggableNumber.elDiv.insertBefore(elLabel, elDraggableNumber.elLeftArrow); + elDraggableNumber.elText.insertBefore(elLabel, elDraggableNumber.elLeftArrow); elTuple.appendChild(elDraggableNumber.elDiv); return elDraggableNumber; @@ -2189,7 +2190,7 @@ function createProperty(propertyData, propertyElementID, propertyName, propertyI } default: { console.log("EntityProperties - Unknown property type " + - propertyType + " set to property " + propertyID); + propertyType + " set to property " + propertyID); break; } } @@ -2716,6 +2717,85 @@ function showParentMaterialNameBox(number, elNumber, elString) { } } +function createProperty(propertyData, propertyElementID, propertyName, propertyID, elProperty) { + let property = { + data: propertyData, + elementID: propertyElementID, + name: propertyName, + elProperty: elProperty, + }; + let propertyType = propertyData.type; + + switch (propertyType) { + case 'string': { + property.elInput = createStringProperty(property, elProperty); + break; + } + case 'bool': { + property.elInput = createBoolProperty(property, elProperty); + break; + } + case 'number': { + property.elNumber = createNumberProperty(property, elProperty); + break; + } + case 'vec3': { + let elVec3 = createVec3Property(property, elProperty); + property.elNumberX = elVec3[VECTOR_ELEMENTS.X_NUMBER]; + property.elNumberY = elVec3[VECTOR_ELEMENTS.Y_NUMBER]; + property.elNumberZ = elVec3[VECTOR_ELEMENTS.Z_NUMBER]; + break; + } + case 'vec2': { + let elVec2 = createVec2Property(property, elProperty); + property.elNumberX = elVec2[VECTOR_ELEMENTS.X_NUMBER]; + property.elNumberY = elVec2[VECTOR_ELEMENTS.Y_NUMBER]; + break; + } + case 'color': { + let elColor = createColorProperty(property, elProperty); + property.elColorPicker = elColor[COLOR_ELEMENTS.COLOR_PICKER]; + property.elNumberR = elColor[COLOR_ELEMENTS.RED_NUMBER]; + property.elNumberG = elColor[COLOR_ELEMENTS.GREEN_NUMBER]; + property.elNumberB = elColor[COLOR_ELEMENTS.BLUE_NUMBER]; + break; + } + case 'dropdown': { + property.elInput = createDropdownProperty(property, propertyID, elProperty); + break; + } + case 'textarea': { + property.elInput = createTextareaProperty(property, elProperty); + break; + } + case 'icon': { + property.elSpan = createIconProperty(property, elProperty); + break; + } + case 'texture': { + let elTexture = createTextureProperty(property, elProperty); + property.elImage = elTexture[TEXTURE_ELEMENTS.IMAGE]; + property.elInput = elTexture[TEXTURE_ELEMENTS.TEXT_INPUT]; + break; + } + case 'buttons': { + property.elProperty = createButtonsProperty(property, elProperty); + break; + } + case 'placeholder': + case 'sub-header': { + break; + } + default: { + console.log("EntityProperties - Unknown property type " + + propertyType + " set to property " + propertyID); + break; + } + } + + return property; +} + function loaded() { openEventBridge(function() { @@ -3040,9 +3120,9 @@ function loaded() { value = Math.round(value.round) / propertyData.round; } if (propertyData.decimals !== undefined) { - property.elInput.value = value.toFixed(propertyData.decimals); + property.elNumber.setValue(value.toFixed(propertyData.decimals)); } else { - property.elInput.value = value; + property.elNumber.setValue(value); } break; } @@ -3058,16 +3138,16 @@ function loaded() { valueZ = Math.round(valueZ * propertyData.round) / propertyData.round; } if (propertyData.decimals !== undefined) { - property.elInputX.value = valueX.toFixed(propertyData.decimals); - property.elInputY.value = valueY.toFixed(propertyData.decimals); - if (property.elInputZ !== undefined) { - property.elInputZ.value = valueZ.toFixed(propertyData.decimals); + property.elNumberX.setValue(valueX.toFixed(propertyData.decimals)); + property.elNumberY.setValue(valueY.toFixed(propertyData.decimals)); + if (property.elNumberZ !== undefined) { + property.elNumberZ.setValue(valueZ.toFixed(propertyData.decimals)); } } else { - property.elInputX.value = valueX; - property.elInputY.value = valueY; - if (property.elInputZ !== undefined) { - property.elInputZ.value = valueZ; + property.elNumberX.setValue(valueX); + property.elNumberY.setValue(valueY); + if (property.elNumberZ !== undefined) { + property.elNumberZ.setValue(valueZ); } } break; @@ -3076,9 +3156,9 @@ function loaded() { property.elColorPicker.style.backgroundColor = "rgb(" + propertyValue.red + "," + propertyValue.green + "," + propertyValue.blue + ")"; - property.elInputR.value = propertyValue.red; - property.elInputG.value = propertyValue.green; - property.elInputB.value = propertyValue.blue; + property.elNumberR.setValue(propertyValue.red); + property.elNumberG.setValue(propertyValue.green); + property.elNumberB.setValue(propertyValue.blue); break; } case 'dropdown': { From e890228fc7d9c383e3943953242da5675b4a2372 Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Sat, 3 Nov 2018 00:25:43 +0100 Subject: [PATCH 222/286] Make shortcuts work in edit windows --- scripts/system/edit.js | 82 ++++++++++++++-------- scripts/system/html/js/entityList.js | 49 +++++++++---- scripts/system/html/js/entityProperties.js | 27 ++++--- scripts/system/html/js/gridControls.js | 31 ++++---- scripts/system/libraries/entityList.js | 5 +- scripts/system/libraries/gridTool.js | 16 +++-- 6 files changed, 137 insertions(+), 73 deletions(-) diff --git a/scripts/system/edit.js b/scripts/system/edit.js index 711c459e5f..46520dbf60 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -113,7 +113,6 @@ selectionManager.addEventListener(function () { entityIconOverlayManager.updatePositions(); }); -var KEY_P = 80; //Key code for letter p used for Parenting hotkey. var DEGREES_TO_RADIANS = Math.PI / 180.0; var RADIANS_TO_DEGREES = 180.0 / Math.PI; @@ -1964,14 +1963,6 @@ var keyReleaseEvent = function (event) { if (isActive) { cameraManager.keyReleaseEvent(event); } - // since sometimes our menu shortcut keys don't work, trap our menu items here also and fire the appropriate menu items - if (event.key === KEY_P && event.isControl && !event.isAutoRepeat) { - if (event.isShifted) { - unparentSelectedEntities(); - } else { - parentSelectedEntities(); - } - } }; Controller.keyReleaseEvent.connect(keyReleaseEvent); Controller.keyPressEvent.connect(keyPressEvent); @@ -2365,10 +2356,6 @@ var PropertiesTool = function (opts) { } pushCommandForSelections(); selectionManager._update(false, this); - } else if (data.type === 'parent') { - parentSelectedEntities(); - } else if (data.type === 'unparent') { - unparentSelectedEntities(); } else if (data.type === 'saveUserData' || data.type === 'saveMaterialData') { //the event bridge and json parsing handle our avatar id string differently. var actualID = data.id.split('"')[1]; @@ -2681,9 +2668,14 @@ function whenReleased(fn) { }; } +var isOnMacPlatform = Controller.getValue(Controller.Hardware.Application.PlatformMac); + var mapping = Controller.newMapping(CONTROLLER_MAPPING_NAME); -mapping.from([Controller.Hardware.Keyboard.Delete]).when([!Controller.Hardware.Application.PlatformMac]).to(deleteKey); -mapping.from([Controller.Hardware.Keyboard.Backspace]).when([Controller.Hardware.Application.PlatformMac]).to(deleteKey); +if (isOnMacPlatform) { + mapping.from([Controller.Hardware.Keyboard.Backspace]).to(deleteKey); +} else { + mapping.from([Controller.Hardware.Keyboard.Delete]).to(deleteKey); +} mapping.from([Controller.Hardware.Keyboard.T]).to(toggleKey); mapping.from([Controller.Hardware.Keyboard.F]).to(focusKey); mapping.from([Controller.Hardware.Keyboard.G]).to(gridKey); @@ -2706,6 +2698,51 @@ mapping.from([Controller.Hardware.Keyboard.Z]) .to(whenPressed(function() { undoHistory.redo() })); +mapping.from([Controller.Hardware.Keyboard.P]) + .when([Controller.Hardware.Keyboard.Control, Controller.Hardware.Keyboard.Shift]) + .to(whenReleased(function() { unparentSelectedEntities(); })); + +mapping.from([Controller.Hardware.Keyboard.P]) + .when([Controller.Hardware.Keyboard.Control, !Controller.Hardware.Keyboard.Shift]) + .to(whenReleased(function() { parentSelectedEntities(); })); + +function keyUpEventFromUIWindow(keyUpEvent) { + var WANT_DEBUG_MISSING_SHORTCUTS = false; + + var pressedValue = 0.0; + // Note: For some reason the keyboardEvent for delete does not show a code, using key instead: + if ((!isOnMacPlatform && keyUpEvent.key === "Delete") || (isOnMacPlatform && keyUpEvent.code === "Backspace")) { + deleteKey(pressedValue); + } else if (keyUpEvent.code === "KeyT") { + toggleKey(pressedValue); + } else if (keyUpEvent.code === "KeyF") { + focusKey(pressedValue); + } else if (keyUpEvent.code === "KeyG") { + gridKey(pressedValue); + } else if (keyUpEvent.ctrlKey && keyUpEvent.code === "KeyX") { + selectionManager.cutSelectedEntities(); + } else if (keyUpEvent.ctrlKey && keyUpEvent.code === "KeyC") { + selectionManager.copySelectedEntities(); + } else if (keyUpEvent.ctrlKey && keyUpEvent.code === "KeyV") { + selectionManager.pasteEntities(); + } else if (keyUpEvent.ctrlKey && keyUpEvent.code === "KeyD") { + selectionManager.duplicateSelection(); + } else if (keyUpEvent.ctrlKey && !keyUpEvent.shiftKey && keyUpEvent.code === "KeyZ") { + undoHistory.undo(); + } else if (keyUpEvent.ctrlKey && !keyUpEvent.shiftKey && keyUpEvent.code === "KeyP") { + parentSelectedEntities(); + } else if (keyUpEvent.ctrlKey && keyUpEvent.shiftKey && keyUpEvent.code === "KeyP") { + unparentSelectedEntities(); + } else if ( + (keyUpEvent.ctrlKey && keyUpEvent.shiftKey && keyUpEvent.code === "KeyZ") || + (keyUpEvent.ctrlKey && keyUpEvent.code === "KeyY")) { + + undoHistory.redo(); + } else if (WANT_DEBUG_MISSING_SHORTCUTS) { + console.warn("unhandled key event: " + JSON.stringify(keyUpEvent)) + } +} + var propertyMenu = new PopupMenu(); propertyMenu.onSelectMenuItem = function (name) { @@ -2719,21 +2756,6 @@ var showMenuItem = propertyMenu.addMenuItem("Show in Marketplace"); var propertiesTool = new PropertiesTool(); -entityListTool.webView.webEventReceived.connect(function(data) { - try { - data = JSON.parse(data); - } catch(e) { - print("edit.js: Error parsing JSON"); - return; - } - - if (data.type === 'parent') { - parentSelectedEntities(); - } else if (data.type === 'unparent') { - unparentSelectedEntities(); - } -}); - selectionDisplay.onSpaceModeChange = function(spaceMode) { entityListTool.setSpaceMode(spaceMode); diff --git a/scripts/system/html/js/entityList.js b/scripts/system/html/js/entityList.js index 82e64a6fbc..e4220ddd53 100644 --- a/scripts/system/html/js/entityList.js +++ b/scripts/system/html/js/entityList.js @@ -21,8 +21,6 @@ const MAX_LENGTH_RADIUS = 9; const MINIMUM_COLUMN_WIDTH = 24; const SCROLLBAR_WIDTH = 20; const RESIZER_WIDTH = 10; -const DELETE = 46; // Key code for the delete key. -const KEY_P = 80; // Key code for letter p used for Parenting hotkey. const COLUMNS = { type: { @@ -1115,21 +1113,46 @@ function loaded() { } } - document.addEventListener("keydown", function (keyDownEvent) { - if (keyDownEvent.target.nodeName === "INPUT") { + document.addEventListener("keyup", function (keyUpEvent) { + if (keyUpEvent.target.nodeName === "INPUT") { return; } - let keyCode = keyDownEvent.keyCode; - if (keyCode === DELETE) { - EventBridge.emitWebEvent(JSON.stringify({ type: 'delete' })); - } - if (keyDownEvent.keyCode === KEY_P && keyDownEvent.ctrlKey) { - if (keyDownEvent.shiftKey) { - EventBridge.emitWebEvent(JSON.stringify({ type: 'unparent' })); - } else { - EventBridge.emitWebEvent(JSON.stringify({ type: 'parent' })); + + if (keyUpEvent.ctrlKey && keyUpEvent.code === "KeyA") { + let visibleEntityIDs = visibleEntities.map(visibleEntity => visibleEntity.id); + let selectionIncludesAllVisibleEntityIDs = visibleEntityIDs.every(visibleEntityID => { + return selectedEntities.includes(visibleEntityID); + }); + + let selection = []; + + if (!selectionIncludesAllVisibleEntityIDs) { + selection = visibleEntityIDs; } + + updateSelectedEntities(selection); + + EventBridge.emitWebEvent(JSON.stringify({ + type: "selectionUpdate", + focus: false, + entityIds: selection, + })); + + return; } + + + let {code, key, altKey, ctrlKey, shiftKey} = keyUpEvent; + EventBridge.emitWebEvent(JSON.stringify({ + type: 'keyUpEvent', + keyUpEvent: { + code, + key, + altKey, + ctrlKey, + shiftKey + } + })); }, false); if (window.EventBridge !== undefined) { diff --git a/scripts/system/html/js/entityProperties.js b/scripts/system/html/js/entityProperties.js index e05ca3ee60..29dfb6a840 100644 --- a/scripts/system/html/js/entityProperties.js +++ b/scripts/system/html/js/entityProperties.js @@ -1352,8 +1352,6 @@ const COLOR_MIN = 0; const COLOR_MAX = 255; const COLOR_STEP = 1; -const KEY_P = 80; // Key code for letter p used for Parenting hotkey. - const MATERIAL_PREFIX_STRING = "mat::"; const PENDING_SCRIPT_STATUS = "[ Fetching status ]"; @@ -3494,16 +3492,23 @@ function loaded() { el.parentNode.removeChild(el); elDropdowns = document.getElementsByTagName("select"); } - - document.addEventListener("keydown", function (keyDown) { - if (keyDown.keyCode === KEY_P && keyDown.ctrlKey) { - if (keyDown.shiftKey) { - EventBridge.emitWebEvent(JSON.stringify({ type: 'unparent' })); - } else { - EventBridge.emitWebEvent(JSON.stringify({ type: 'parent' })); - } + + document.addEventListener("keyup", function (keyUpEvent) { + if (keyUpEvent.target.nodeName === "INPUT") { + return; } - }); + let {code, key, altKey, ctrlKey, shiftKey} = keyUpEvent; + EventBridge.emitWebEvent(JSON.stringify({ + type: 'keyUpEvent', + keyUpEvent: { + code, + key, + altKey, + ctrlKey, + shiftKey + } + })); + }, false); window.onblur = function() { // Fake a change event diff --git a/scripts/system/html/js/gridControls.js b/scripts/system/html/js/gridControls.js index 79a169400a..fa3d86a293 100644 --- a/scripts/system/html/js/gridControls.js +++ b/scripts/system/html/js/gridControls.js @@ -6,8 +6,6 @@ // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -const KEY_P = 80; //Key code for letter p used for Parenting hotkey. - function loaded() { openEventBridge(function() { elPosY = document.getElementById("horiz-y"); @@ -105,7 +103,7 @@ function loaded() { elColorBlue.value = blue; gridColor = { red: red, green: green, blue: blue }; emitUpdate(); - } + }; elColorRed.addEventListener('change', colorChangeFunction); elColorGreen.addEventListener('change', colorChangeFunction); @@ -131,15 +129,24 @@ function loaded() { EventBridge.emitWebEvent(JSON.stringify({ type: 'init' })); }); - document.addEventListener("keydown", function (keyDown) { - if (keyDown.keyCode === KEY_P && keyDown.ctrlKey) { - if (keyDown.shiftKey) { - EventBridge.emitWebEvent(JSON.stringify({ type: 'unparent' })); - } else { - EventBridge.emitWebEvent(JSON.stringify({ type: 'parent' })); - } - } - }) + + document.addEventListener("keyup", function (keyUpEvent) { + if (keyUpEvent.target.nodeName === "INPUT") { + return; + } + let {code, key, altKey, ctrlKey, shiftKey} = keyUpEvent; + EventBridge.emitWebEvent(JSON.stringify({ + type: 'keyUpEvent', + keyUpEvent: { + code, + key, + altKey, + ctrlKey, + shiftKey + } + })); + }, false); + // Disable right-click context menu which is not visible in the HMD and makes it seem like the app has locked document.addEventListener("contextmenu", function (event) { event.preventDefault(); diff --git a/scripts/system/libraries/entityList.js b/scripts/system/libraries/entityList.js index 4ba1012a58..8942c84f33 100644 --- a/scripts/system/libraries/entityList.js +++ b/scripts/system/libraries/entityList.js @@ -9,7 +9,8 @@ // /* global EntityListTool, Tablet, selectionManager, Entities, Camera, MyAvatar, Vec3, Menu, Messages, - cameraManager, MENU_EASE_ON_FOCUS, deleteSelectedEntities, toggleSelectedEntitiesLocked, toggleSelectedEntitiesVisible */ + cameraManager, MENU_EASE_ON_FOCUS, deleteSelectedEntities, toggleSelectedEntitiesLocked, toggleSelectedEntitiesVisible, + keyUpEventFromUIWindow */ var PROFILING_ENABLED = false; var profileIndent = ''; @@ -298,6 +299,8 @@ EntityListTool = function(shouldUseEditTabletApp) { SelectionManager._update(); } else if (data.type === "toggleSpaceMode") { SelectionDisplay.toggleSpaceMode(); + } else if (data.type === 'keyUpEvent') { + keyUpEventFromUIWindow(data.keyUpEvent); } }; diff --git a/scripts/system/libraries/gridTool.js b/scripts/system/libraries/gridTool.js index 6f62742e8f..1fd3cbfbaa 100644 --- a/scripts/system/libraries/gridTool.js +++ b/scripts/system/libraries/gridTool.js @@ -1,3 +1,5 @@ +/* global keyUpEventFromUIWindow */ + var GRID_CONTROLS_HTML_URL = Script.resolvePath('../html/gridControls.html'); Grid = function() { @@ -270,24 +272,26 @@ GridTool = function(opts) { return; } - if (data.type == "init") { + if (data.type === "init") { horizontalGrid.emitUpdate(); - } else if (data.type == "update") { + } else if (data.type === "update") { horizontalGrid.update(data); for (var i = 0; i < listeners.length; i++) { listeners[i] && listeners[i](data); } - } else if (data.type == "action") { + } else if (data.type === "action") { var action = data.action; - if (action == "moveToAvatar") { + if (action === "moveToAvatar") { var position = MyAvatar.getJointPosition("LeftFoot"); - if (position.x == 0 && position.y == 0 && position.z == 0) { + if (position.x === 0 && position.y === 0 && position.z === 0) { position = MyAvatar.position; } horizontalGrid.setPosition(position); - } else if (action == "moveToSelection") { + } else if (action === "moveToSelection") { horizontalGrid.moveToSelection(); } + } else if (data.type === 'keyUpEvent') { + keyUpEventFromUIWindow(data.keyUpEvent); } }; From afd69d04dd8e05876d3721a3f1f9de0a396f1ad0 Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Sat, 3 Nov 2018 00:27:03 +0100 Subject: [PATCH 223/286] Make ctrl+shift shortcuts work for create app --- interface/resources/qml/controls/FlickableWebViewCore.qml | 6 ++++-- interface/resources/qml/controls/WebView.qml | 1 + interface/resources/qml/hifi/tablet/EditEntityList.qml | 3 ++- interface/resources/qml/hifi/tablet/EditTabView.qml | 3 +++ interface/resources/qml/hifi/tablet/EditToolsTabView.qml | 2 ++ interface/resources/qml/hifi/tablet/EntityList.qml | 1 + scripts/system/libraries/EditEntityList.qml | 1 + 7 files changed, 14 insertions(+), 3 deletions(-) diff --git a/interface/resources/qml/controls/FlickableWebViewCore.qml b/interface/resources/qml/controls/FlickableWebViewCore.qml index a89a0cf37b..823d0107a2 100644 --- a/interface/resources/qml/controls/FlickableWebViewCore.qml +++ b/interface/resources/qml/controls/FlickableWebViewCore.qml @@ -26,6 +26,8 @@ Item { property bool interactive: false + property bool blurOnCtrlShift: true + StylesUIt.HifiConstants { id: hifi } @@ -180,8 +182,8 @@ Item { } Keys.onPressed: { - if ((event.modifiers & Qt.ShiftModifier) && (event.modifiers & Qt.ControlModifier)) { - webViewCore.focus = false; + if (blurOnCtrlShift && (event.modifiers & Qt.ShiftModifier) && (event.modifiers & Qt.ControlModifier)) { + webViewCore.focus = false; } } } diff --git a/interface/resources/qml/controls/WebView.qml b/interface/resources/qml/controls/WebView.qml index 6d72c529f6..24ea11a906 100644 --- a/interface/resources/qml/controls/WebView.qml +++ b/interface/resources/qml/controls/WebView.qml @@ -22,6 +22,7 @@ Item { property bool punctuationMode: false property bool passwordField: false property alias flickable: webroot.interactive + property alias blurOnCtrlShift: webroot.blurOnCtrlShift function stop() { webroot.stop(); diff --git a/interface/resources/qml/hifi/tablet/EditEntityList.qml b/interface/resources/qml/hifi/tablet/EditEntityList.qml index d2fb99ea0a..b6305db388 100644 --- a/interface/resources/qml/hifi/tablet/EditEntityList.qml +++ b/interface/resources/qml/hifi/tablet/EditEntityList.qml @@ -10,6 +10,7 @@ import stylesUit 1.0 WebView { id: entityListToolWebView - url: Paths.defaultScripts + "/system/html/entityList.html" + url: Paths.defaultScripts + "/system/html/entityListf.html" enabled: true + blurOnCtrlShift: false } diff --git a/interface/resources/qml/hifi/tablet/EditTabView.qml b/interface/resources/qml/hifi/tablet/EditTabView.qml index ff1c5a7c47..5959725a6a 100644 --- a/interface/resources/qml/hifi/tablet/EditTabView.qml +++ b/interface/resources/qml/hifi/tablet/EditTabView.qml @@ -245,6 +245,7 @@ TabBar { id: entityListToolWebView url: Paths.defaultScripts + "/system/html/entityList.html" enabled: true + blurOnCtrlShift: false } } } @@ -260,6 +261,7 @@ TabBar { id: entityPropertiesWebView url: Paths.defaultScripts + "/system/html/entityProperties.html" enabled: true + blurOnCtrlShift: false } } } @@ -275,6 +277,7 @@ TabBar { id: gridControlsWebView url: Paths.defaultScripts + "/system/html/gridControls.html" enabled: true + blurOnCtrlShift: false } } } diff --git a/interface/resources/qml/hifi/tablet/EditToolsTabView.qml b/interface/resources/qml/hifi/tablet/EditToolsTabView.qml index 2b78576526..6b64520feb 100644 --- a/interface/resources/qml/hifi/tablet/EditToolsTabView.qml +++ b/interface/resources/qml/hifi/tablet/EditToolsTabView.qml @@ -251,6 +251,7 @@ TabBar { id: entityPropertiesWebView url: Paths.defaultScripts + "/system/html/entityProperties.html" enabled: true + blurOnCtrlShift: false } } } @@ -266,6 +267,7 @@ TabBar { id: gridControlsWebView url: Paths.defaultScripts + "/system/html/gridControls.html" enabled: true + blurOnCtrlShift: false } } } diff --git a/interface/resources/qml/hifi/tablet/EntityList.qml b/interface/resources/qml/hifi/tablet/EntityList.qml index f4b47c19bb..2f8a8863be 100644 --- a/interface/resources/qml/hifi/tablet/EntityList.qml +++ b/interface/resources/qml/hifi/tablet/EntityList.qml @@ -2,4 +2,5 @@ WebView { id: entityListToolWebView url: Paths.defaultScripts + "/system/html/entityList.html" enabled: true + blurOnCtrlShift: false } diff --git a/scripts/system/libraries/EditEntityList.qml b/scripts/system/libraries/EditEntityList.qml index d8099cb670..4fc5ff19ef 100644 --- a/scripts/system/libraries/EditEntityList.qml +++ b/scripts/system/libraries/EditEntityList.qml @@ -8,4 +8,5 @@ HifiControls.WebView { id: entityListToolWebView url: Qt.resolvedUrl("../html/entityList.html") enabled: true + blurOnCtrlShift: false } From cc207537a32901197034038d91a7703980af0b9f Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Sat, 3 Nov 2018 00:27:40 +0100 Subject: [PATCH 224/286] Fix for unintended snapshots while parenting/un-parenting --- interface/src/Application.cpp | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index b085f0b4de..5256ef2c6a 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -4039,21 +4039,23 @@ void Application::keyPressEvent(QKeyEvent* event) { break; case Qt::Key_P: { - AudioInjectorOptions options; - options.localOnly = true; - options.stereo = true; - Setting::Handle notificationSounds{ MenuOption::NotificationSounds, true}; - Setting::Handle notificationSoundSnapshot{ MenuOption::NotificationSoundsSnapshot, true}; - if (notificationSounds.get() && notificationSoundSnapshot.get()) { - if (_snapshotSoundInjector) { - _snapshotSoundInjector->setOptions(options); - _snapshotSoundInjector->restart(); - } else { - QByteArray samples = _snapshotSound->getByteArray(); - _snapshotSoundInjector = AudioInjector::playSound(samples, options); + if (!isShifted && !isMeta && !isOption && !event->isAutoRepeat()) { + AudioInjectorOptions options; + options.localOnly = true; + options.stereo = true; + Setting::Handle notificationSounds{ MenuOption::NotificationSounds, true }; + Setting::Handle notificationSoundSnapshot{ MenuOption::NotificationSoundsSnapshot, true }; + if (notificationSounds.get() && notificationSoundSnapshot.get()) { + if (_snapshotSoundInjector) { + _snapshotSoundInjector->setOptions(options); + _snapshotSoundInjector->restart(); + } else { + QByteArray samples = _snapshotSound->getByteArray(); + _snapshotSoundInjector = AudioInjector::playSound(samples, options); + } } + takeSnapshot(true); } - takeSnapshot(true); break; } From 0248323beedb340773c16d422e02858a7b26284e Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Sat, 3 Nov 2018 00:34:00 +0100 Subject: [PATCH 225/286] fix test path --- interface/resources/qml/hifi/tablet/EditEntityList.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/resources/qml/hifi/tablet/EditEntityList.qml b/interface/resources/qml/hifi/tablet/EditEntityList.qml index b6305db388..2afaa8cd82 100644 --- a/interface/resources/qml/hifi/tablet/EditEntityList.qml +++ b/interface/resources/qml/hifi/tablet/EditEntityList.qml @@ -10,7 +10,7 @@ import stylesUit 1.0 WebView { id: entityListToolWebView - url: Paths.defaultScripts + "/system/html/entityListf.html" + url: Paths.defaultScripts + "/system/html/entityList.html" enabled: true blurOnCtrlShift: false } From 2c715bf59259a3724ce54be38fc8c800370a5c19 Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Wed, 14 Nov 2018 00:48:16 +0100 Subject: [PATCH 226/286] make window shortcuts work on OSX --- scripts/system/edit.js | 38 ++++++++++++---------- scripts/system/html/js/entityList.js | 25 ++++++++++++-- scripts/system/html/js/entityProperties.js | 23 ++++++++++++- scripts/system/html/js/gridControls.js | 23 ++++++++++++- 4 files changed, 87 insertions(+), 22 deletions(-) diff --git a/scripts/system/edit.js b/scripts/system/edit.js index 46520dbf60..3876b1b074 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -12,7 +12,8 @@ /* global Script, SelectionDisplay, LightOverlayManager, CameraManager, Grid, GridTool, EntityListTool, Vec3, SelectionManager, Overlays, OverlayWebWindow, UserActivityLogger, Settings, Entities, Tablet, Toolbars, Messages, Menu, Camera, - progressDialog, tooltip, MyAvatar, Quat, Controller, Clipboard, HMD, UndoStack, OverlaySystemWindow */ + progressDialog, tooltip, MyAvatar, Quat, Controller, Clipboard, HMD, UndoStack, OverlaySystemWindow, + keyUpEventFromUIWindow:true */ (function() { // BEGIN LOCAL_SCOPE @@ -2706,42 +2707,44 @@ mapping.from([Controller.Hardware.Keyboard.P]) .when([Controller.Hardware.Keyboard.Control, !Controller.Hardware.Keyboard.Shift]) .to(whenReleased(function() { parentSelectedEntities(); })); -function keyUpEventFromUIWindow(keyUpEvent) { +keyUpEventFromUIWindow = function(keyUpEvent) { var WANT_DEBUG_MISSING_SHORTCUTS = false; var pressedValue = 0.0; - // Note: For some reason the keyboardEvent for delete does not show a code, using key instead: - if ((!isOnMacPlatform && keyUpEvent.key === "Delete") || (isOnMacPlatform && keyUpEvent.code === "Backspace")) { + + if ((!isOnMacPlatform && keyUpEvent.keyCodeString === "Delete") + || (isOnMacPlatform && keyUpEvent.keyCodeString === "Backspace")) { + deleteKey(pressedValue); - } else if (keyUpEvent.code === "KeyT") { + } else if (keyUpEvent.keyCodeString === "T") { toggleKey(pressedValue); - } else if (keyUpEvent.code === "KeyF") { + } else if (keyUpEvent.keyCodeString === "F") { focusKey(pressedValue); - } else if (keyUpEvent.code === "KeyG") { + } else if (keyUpEvent.keyCodeString === "G") { gridKey(pressedValue); - } else if (keyUpEvent.ctrlKey && keyUpEvent.code === "KeyX") { + } else if (keyUpEvent.ctrlKey && keyUpEvent.keyCodeString === "X") { selectionManager.cutSelectedEntities(); - } else if (keyUpEvent.ctrlKey && keyUpEvent.code === "KeyC") { + } else if (keyUpEvent.ctrlKey && keyUpEvent.keyCodeString === "C") { selectionManager.copySelectedEntities(); - } else if (keyUpEvent.ctrlKey && keyUpEvent.code === "KeyV") { + } else if (keyUpEvent.ctrlKey && keyUpEvent.keyCodeString === "V") { selectionManager.pasteEntities(); - } else if (keyUpEvent.ctrlKey && keyUpEvent.code === "KeyD") { + } else if (keyUpEvent.ctrlKey && keyUpEvent.keyCodeString === "D") { selectionManager.duplicateSelection(); - } else if (keyUpEvent.ctrlKey && !keyUpEvent.shiftKey && keyUpEvent.code === "KeyZ") { + } else if (keyUpEvent.ctrlKey && !keyUpEvent.shiftKey && keyUpEvent.keyCodeString === "Z") { undoHistory.undo(); - } else if (keyUpEvent.ctrlKey && !keyUpEvent.shiftKey && keyUpEvent.code === "KeyP") { + } else if (keyUpEvent.ctrlKey && !keyUpEvent.shiftKey && keyUpEvent.keyCodeString === "P") { parentSelectedEntities(); - } else if (keyUpEvent.ctrlKey && keyUpEvent.shiftKey && keyUpEvent.code === "KeyP") { + } else if (keyUpEvent.ctrlKey && keyUpEvent.shiftKey && keyUpEvent.keyCodeString === "P") { unparentSelectedEntities(); } else if ( - (keyUpEvent.ctrlKey && keyUpEvent.shiftKey && keyUpEvent.code === "KeyZ") || - (keyUpEvent.ctrlKey && keyUpEvent.code === "KeyY")) { + (keyUpEvent.ctrlKey && keyUpEvent.shiftKey && keyUpEvent.keyCodeString === "Z") || + (keyUpEvent.ctrlKey && keyUpEvent.keyCodeString === "Y")) { undoHistory.redo(); } else if (WANT_DEBUG_MISSING_SHORTCUTS) { console.warn("unhandled key event: " + JSON.stringify(keyUpEvent)) } -} +}; var propertyMenu = new PopupMenu(); @@ -2756,7 +2759,6 @@ var showMenuItem = propertyMenu.addMenuItem("Show in Marketplace"); var propertiesTool = new PropertiesTool(); - selectionDisplay.onSpaceModeChange = function(spaceMode) { entityListTool.setSpaceMode(spaceMode); propertiesTool.setSpaceMode(spaceMode); diff --git a/scripts/system/html/js/entityList.js b/scripts/system/html/js/entityList.js index e4220ddd53..ea096be5e2 100644 --- a/scripts/system/html/js/entityList.js +++ b/scripts/system/html/js/entityList.js @@ -1112,13 +1112,33 @@ function loaded() { elToggleSpaceMode.innerText = "World"; } } + + const KEY_CODES = { + BACKSPACE: 8, + DELETE: 46 + }; document.addEventListener("keyup", function (keyUpEvent) { if (keyUpEvent.target.nodeName === "INPUT") { return; } - if (keyUpEvent.ctrlKey && keyUpEvent.code === "KeyA") { + let {code, key, keyCode, altKey, ctrlKey, shiftKey} = keyUpEvent; + + let keyCodeString; + switch (keyCode) { + case KEY_CODES.DELETE: + keyCodeString = "Delete"; + break; + case KEY_CODES.BACKSPACE: + keyCodeString = "Backspace"; + break; + default: + keyCodeString = String.fromCharCode(keyUpEvent.keyCode); + break; + } + + if (ctrlKey && keyCodeString === "A") { let visibleEntityIDs = visibleEntities.map(visibleEntity => visibleEntity.id); let selectionIncludesAllVisibleEntityIDs = visibleEntityIDs.every(visibleEntityID => { return selectedEntities.includes(visibleEntityID); @@ -1142,12 +1162,13 @@ function loaded() { } - let {code, key, altKey, ctrlKey, shiftKey} = keyUpEvent; EventBridge.emitWebEvent(JSON.stringify({ type: 'keyUpEvent', keyUpEvent: { code, key, + keyCode, + keyCodeString, altKey, ctrlKey, shiftKey diff --git a/scripts/system/html/js/entityProperties.js b/scripts/system/html/js/entityProperties.js index 29dfb6a840..9bb8a2c5a4 100644 --- a/scripts/system/html/js/entityProperties.js +++ b/scripts/system/html/js/entityProperties.js @@ -3493,16 +3493,37 @@ function loaded() { elDropdowns = document.getElementsByTagName("select"); } + const KEY_CODES = { + BACKSPACE: 8, + DELETE: 46 + }; + document.addEventListener("keyup", function (keyUpEvent) { if (keyUpEvent.target.nodeName === "INPUT") { return; } - let {code, key, altKey, ctrlKey, shiftKey} = keyUpEvent; + let {code, key, keyCode, altKey, ctrlKey, shiftKey} = keyUpEvent; + + let keyCodeString; + switch (keyCode) { + case KEY_CODES.DELETE: + keyCodeString = "Delete"; + break; + case KEY_CODES.BACKSPACE: + keyCodeString = "Backspace"; + break; + default: + keyCodeString = String.fromCharCode(keyUpEvent.keyCode); + break; + } + EventBridge.emitWebEvent(JSON.stringify({ type: 'keyUpEvent', keyUpEvent: { code, key, + keyCode, + keyCodeString, altKey, ctrlKey, shiftKey diff --git a/scripts/system/html/js/gridControls.js b/scripts/system/html/js/gridControls.js index fa3d86a293..6f7024ccaa 100644 --- a/scripts/system/html/js/gridControls.js +++ b/scripts/system/html/js/gridControls.js @@ -130,16 +130,37 @@ function loaded() { EventBridge.emitWebEvent(JSON.stringify({ type: 'init' })); }); + const KEY_CODES = { + BACKSPACE: 8, + DELETE: 46 + }; + document.addEventListener("keyup", function (keyUpEvent) { if (keyUpEvent.target.nodeName === "INPUT") { return; } - let {code, key, altKey, ctrlKey, shiftKey} = keyUpEvent; + let {code, key, keyCode, altKey, ctrlKey, shiftKey} = keyUpEvent; + + let keyCodeString; + switch (keyCode) { + case KEY_CODES.DELETE: + keyCodeString = "Delete"; + break; + case KEY_CODES.BACKSPACE: + keyCodeString = "Backspace"; + break; + default: + keyCodeString = String.fromCharCode(keyUpEvent.keyCode); + break; + } + EventBridge.emitWebEvent(JSON.stringify({ type: 'keyUpEvent', keyUpEvent: { code, key, + keyCode, + keyCodeString, altKey, ctrlKey, shiftKey From 1aa048f4d61d1c8f506349eb446daa05f8f88312 Mon Sep 17 00:00:00 2001 From: David Back Date: Tue, 13 Nov 2018 17:56:31 -0800 Subject: [PATCH 227/286] style and decimal fixes --- scripts/system/html/css/edit-style.css | 7 +- scripts/system/html/js/draggableNumber.js | 163 +++++++++++---------- scripts/system/html/js/entityProperties.js | 43 +++--- 3 files changed, 108 insertions(+), 105 deletions(-) diff --git a/scripts/system/html/css/edit-style.css b/scripts/system/html/css/edit-style.css index 84f01e6205..3a291bf27b 100644 --- a/scripts/system/html/css/edit-style.css +++ b/scripts/system/html/css/edit-style.css @@ -252,7 +252,7 @@ input.search:focus { box-shadow: 0 0 0 1px #00b4ef; } -input:disabled, textarea:disabled { +input:disabled, textarea:disabled, .draggable-number.text[disabled="disabled"] { background-color: #383838; color: #afafaf; } @@ -1446,11 +1446,6 @@ input#property-scale-button-reset { left: 250px; } -#property-userData-button-edit, -#property-materialData-button-clear { - margin: 6px 0 6px 0; -} - #property-userData-static, #property-materialData-static { display: none; diff --git a/scripts/system/html/js/draggableNumber.js b/scripts/system/html/js/draggableNumber.js index 326854bc92..7e5d9d524a 100644 --- a/scripts/system/html/js/draggableNumber.js +++ b/scripts/system/html/js/draggableNumber.js @@ -6,13 +6,14 @@ // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -const DELTA_X_FOCUS_THRESHOLD = 2; +const DELTA_X_FOCUS_THRESHOLD = 1; -function DraggableNumber(min, max, step) { +function DraggableNumber(min, max, step, decimals) { this.min = min; this.max = max; this.step = step !== undefined ? step : 1; - this.initialMouseEvent = null; + this.decimals = decimals; + this.initialMouseEvent = null; this.lastMouseEvent = null; this.valueChangeFunction = null; this.initialize(); @@ -20,64 +21,68 @@ function DraggableNumber(min, max, step) { DraggableNumber.prototype = { mouseDown: function(event) { - if (event.target === this.elText) { - this.initialMouseEvent = event; - this.lastMouseEvent = event; - document.addEventListener("mousemove", this.onMouseMove); - document.addEventListener("mouseup", this.onMouseUp); - } + if (event.target === this.elText) { + this.initialMouseEvent = event; + this.lastMouseEvent = event; + document.addEventListener("mousemove", this.onMouseMove); + document.addEventListener("mouseup", this.onMouseUp); + } }, mouseMove: function(event) { if (this.lastMouseEvent) { let dx = event.clientX - this.lastMouseEvent.clientX; - let inputChanged = dx !== 0; - if (inputChanged) { - while (dx !== 0) { - if (dx > 0) { - this.stepUp(); - --dx; - } else { - this.stepDown(); - ++dx; - } - } - if (this.valueChangeFunction) { - this.valueChangeFunction(); - } - } + let inputChanged = dx !== 0; + if (inputChanged) { + while (dx !== 0) { + if (dx > 0) { + this.stepUp(); + --dx; + } else { + this.stepDown(); + ++dx; + } + } + if (this.valueChangeFunction) { + this.valueChangeFunction(); + } + } this.lastMouseEvent = event; } }, mouseUp: function(event) { - if (this.initialMouseEvent) { - let dx = event.clientX - this.initialMouseEvent.clientX; - if (dx <= DELTA_X_FOCUS_THRESHOLD) { - this.elInput.style.visibility = "visible"; - this.elText.style.visibility = "hidden"; - } - this.initialMouseEvent = null; - this.lastMouseEvent = null; - document.removeEventListener("mousemove", this.onMouseMove); - document.removeEventListener("mouseup", this.onMouseUp); - } + if (this.initialMouseEvent) { + let dx = event.clientX - this.initialMouseEvent.clientX; + if (dx <= DELTA_X_FOCUS_THRESHOLD) { + this.elInput.style.visibility = "visible"; + this.elText.style.visibility = "hidden"; + } + this.initialMouseEvent = null; + this.lastMouseEvent = null; + document.removeEventListener("mousemove", this.onMouseMove); + document.removeEventListener("mouseup", this.onMouseUp); + } + }, + + stepUp: function() { + this.elInput.stepUp(); + this.inputChange(); + }, + + stepDown: function() { + this.elInput.stepDown(); + this.inputChange(); + }, + + setValue: function(newValue) { + if (newValue !== "" && this.decimals !== undefined) { + this.elInput.value = parseFloat(newValue).toFixed(this.decimals); + } else { + this.elInput.value = newValue; + } + this.elText.firstChild.data = this.elInput.value; }, - - stepUp: function() { - this.elInput.stepUp(); - this.inputChange(); - }, - - stepDown: function() { - this.elInput.stepDown(); - this.inputChange(); - }, - - setValue: function(newValue) { - this.elInput.value = newValue; - this.elText.firstChild.data = newValue; - }, setValueChangeFunction: function(valueChangeFunction) { if (this.valueChangeFunction) { @@ -86,45 +91,45 @@ DraggableNumber.prototype = { this.valueChangeFunction = valueChangeFunction.bind(this.elInput); this.elInput.addEventListener("change", this.valueChangeFunction); }, - - inputChange: function() { - this.setValue(this.elInput.value); - }, - - inputBlur: function() { - this.elInput.style.visibility = "hidden"; - this.elText.style.visibility = "visible"; - }, + + inputChange: function() { + this.setValue(this.elInput.value); + }, + + inputBlur: function() { + this.elInput.style.visibility = "hidden"; + this.elText.style.visibility = "visible"; + }, initialize: function() { - this.onMouseDown = this.mouseDown.bind(this); + this.onMouseDown = this.mouseDown.bind(this); this.onMouseMove = this.mouseMove.bind(this); this.onMouseUp = this.mouseUp.bind(this); - this.onStepUp = this.stepUp.bind(this); - this.onStepDown = this.stepDown.bind(this); - this.onInputChange = this.inputChange.bind(this); - this.onInputBlur = this.inputBlur.bind(this); + this.onStepUp = this.stepUp.bind(this); + this.onStepDown = this.stepDown.bind(this); + this.onInputChange = this.inputChange.bind(this); + this.onInputBlur = this.inputBlur.bind(this); this.elDiv = document.createElement('div'); this.elDiv.className = "draggable-number"; - - this.elText = document.createElement('label'); - this.elText.className = "draggable-number text"; - this.elText.innerText = " "; - this.elText.style.visibility = "visible"; - this.elText.addEventListener("mousedown", this.onMouseDown); - - this.elLeftArrow = document.createElement('span'); + + this.elText = document.createElement('label'); + this.elText.className = "draggable-number text"; + this.elText.innerText = " "; + this.elText.style.visibility = "visible"; + this.elText.addEventListener("mousedown", this.onMouseDown); + + this.elLeftArrow = document.createElement('span'); this.elRightArrow = document.createElement('span'); this.elLeftArrow.className = 'draggable-number left-arrow'; this.elLeftArrow.innerHTML = 'D'; - this.elLeftArrow.addEventListener("click", this.onStepDown); + this.elLeftArrow.addEventListener("click", this.onStepDown); this.elRightArrow.className = 'draggable-number right-arrow'; this.elRightArrow.innerHTML = 'D'; - this.elRightArrow.addEventListener("click", this.onStepUp); + this.elRightArrow.addEventListener("click", this.onStepUp); this.elInput = document.createElement('input'); - this.elInput.className = "draggable-number input"; + this.elInput.className = "draggable-number input"; this.elInput.setAttribute("type", "number"); if (this.min !== undefined) { this.elInput.setAttribute("min", this.min); @@ -135,13 +140,13 @@ DraggableNumber.prototype = { if (this.step !== undefined) { this.elInput.setAttribute("step", this.step); } - this.elInput.style.visibility = "hidden"; - this.elInput.addEventListener("change", this.onInputChange); - this.elInput.addEventListener("blur", this.onInputBlur); + this.elInput.style.visibility = "hidden"; + this.elInput.addEventListener("change", this.onInputChange); + this.elInput.addEventListener("blur", this.onInputBlur); this.elText.appendChild(this.elLeftArrow); this.elText.appendChild(this.elInput); this.elText.appendChild(this.elRightArrow); - this.elDiv.appendChild(this.elText); + this.elDiv.appendChild(this.elText); } }; diff --git a/scripts/system/html/js/entityProperties.js b/scripts/system/html/js/entityProperties.js index cf7360b927..a9233f0dce 100644 --- a/scripts/system/html/js/entityProperties.js +++ b/scripts/system/html/js/entityProperties.js @@ -1450,10 +1450,11 @@ function disableChildren(el, selector) { } function enableProperties() { - enableChildren(document.getElementById("properties-list"), "input, textarea, checkbox, .dropdown dl, .color-picker"); + enableChildren(document.getElementById("properties-list"), + "input, textarea, checkbox, .dropdown dl, .color-picker , .draggable-number.text"); enableChildren(document, ".colpick"); + let elLocked = getPropertyInputElement("locked"); - if (elLocked.checked === false) { removeStaticUserData(); removeStaticMaterialData(); @@ -1461,13 +1462,14 @@ function enableProperties() { } function disableProperties() { - disableChildren(document.getElementById("properties-list"), "input, textarea, checkbox, .dropdown dl, .color-picker"); + disableChildren(document.getElementById("properties-list"), + "input, textarea, checkbox, .dropdown dl, .color-picker, .draggable-number.text"); disableChildren(document, ".colpick"); for (let pickKey in colorPickers) { colorPickers[pickKey].colpickHide(); } + let elLocked = getPropertyInputElement("locked"); - if (elLocked.checked === true) { if ($('#property-userData-editor').css('display') === "block") { showStaticUserData(); @@ -1797,10 +1799,12 @@ function createBoolProperty(property, elProperty) { let subPropertyOf = propertyData.subPropertyOf; if (subPropertyOf !== undefined) { elInput.addEventListener('change', function() { - updateCheckedSubProperty(subPropertyOf, selectedEntityProperties[subPropertyOf], elInput, propertyName, property.isParticleProperty); + updateCheckedSubProperty(subPropertyOf, selectedEntityProperties[subPropertyOf], + elInput, propertyName, property.isParticleProperty); }); } else { - elInput.addEventListener('change', createEmitCheckedPropertyUpdateFunction(propertyName, propertyData.inverse, property.isParticleProperty)); + elInput.addEventListener('change', createEmitCheckedPropertyUpdateFunction(propertyName, propertyData.inverse, + property.isParticleProperty)); } return elInput; @@ -1813,14 +1817,16 @@ function createNumberProperty(property, elProperty) { elProperty.className = "draggable-number"; - let elDraggableNumber = new DraggableNumber(propertyData.min, propertyData.max, propertyData.step); + let elDraggableNumber = new DraggableNumber(propertyData.min, propertyData.max, + propertyData.step, propertyData.decimals); let defaultValue = propertyData.defaultValue; if (defaultValue !== undefined) { elDraggableNumber.elInput.value = defaultValue; } - let valueChangeFunction = createEmitNumberPropertyUpdateFunction(propertyName, propertyData.multiplier, property.isParticleProperty); + let valueChangeFunction = createEmitNumberPropertyUpdateFunction(propertyName, propertyData.multiplier, + property.isParticleProperty); elDraggableNumber.setValueChangeFunction(valueChangeFunction); elDraggableNumber.elInput.setAttribute("id", elementID); @@ -1841,11 +1847,11 @@ function createVec3Property(property, elProperty) { elProperty.className = propertyData.vec3Type + " fstuple"; let elNumberX = createTupleNumberInput(elProperty, elementID, propertyData.subLabels[VECTOR_ELEMENTS.X_NUMBER], - propertyData.min, propertyData.max, propertyData.step); + propertyData.min, propertyData.max, propertyData.step, propertyData.decimals); let elNumberY = createTupleNumberInput(elProperty, elementID, propertyData.subLabels[VECTOR_ELEMENTS.Y_NUMBER], - propertyData.min, propertyData.max, propertyData.step); + propertyData.min, propertyData.max, propertyData.step, propertyData.decimals); let elNumberZ = createTupleNumberInput(elProperty, elementID, propertyData.subLabels[VECTOR_ELEMENTS.Z_NUMBER], - propertyData.min, propertyData.max, propertyData.step); + propertyData.min, propertyData.max, propertyData.step, propertyData.decimals); let valueChangeFunction = createEmitVec3PropertyUpdateFunction(propertyName, elNumberX.elInput, elNumberY.elInput, elNumberZ.elInput, propertyData.multiplier, @@ -1874,9 +1880,9 @@ function createVec2Property(property, elProperty) { elProperty.appendChild(elTuple); let elNumberX = createTupleNumberInput(elProperty, elementID, propertyData.subLabels[VECTOR_ELEMENTS.X_NUMBER], - propertyData.min, propertyData.max, propertyData.step); + propertyData.min, propertyData.max, propertyData.step, propertyData.decimals); let elNumberY = createTupleNumberInput(elProperty, elementID, propertyData.subLabels[VECTOR_ELEMENTS.Y_NUMBER], - propertyData.min, propertyData.max, propertyData.step); + propertyData.min, propertyData.max, propertyData.step, propertyData.decimals); let valueChangeFunction = createEmitVec2PropertyUpdateFunction(propertyName, elNumberX.elInput, elNumberY.elInput, propertyData.multiplier, property.isParticleProperty); @@ -2078,15 +2084,16 @@ function createButtonsProperty(property, elProperty, elLabel) { return elProperty; } -function createTupleNumberInput(elTuple, propertyElementID, subLabel, min, max, step) { +function createTupleNumberInput(elTuple, propertyElementID, subLabel, min, max, step, decimals) { let elementID = propertyElementID + "-" + subLabel.toLowerCase(); let elLabel = document.createElement('label'); elLabel.className = "sublabel " + subLabel; elLabel.innerText = subLabel[0].toUpperCase() + subLabel.slice(1); elLabel.setAttribute("for", elementID); + elLabel.style.visibility = "visible"; - let elDraggableNumber = new DraggableNumber(min, max, step); + let elDraggableNumber = new DraggableNumber(min, max, step, decimals); elDraggableNumber.elInput.setAttribute("id", elementID); elDraggableNumber.elDiv.className += " fstuple"; elDraggableNumber.elText.insertBefore(elLabel, elDraggableNumber.elLeftArrow); @@ -3119,11 +3126,7 @@ function loaded() { if (propertyData.round !== undefined) { value = Math.round(value.round) / propertyData.round; } - if (propertyData.decimals !== undefined) { - property.elNumber.setValue(value.toFixed(propertyData.decimals)); - } else { - property.elNumber.setValue(value); - } + property.elNumber.setValue(value); break; } case 'vec3': From 452c212c4f43fd912606b6da6c144308bcb52633 Mon Sep 17 00:00:00 2001 From: Simon Walton Date: Tue, 13 Nov 2018 17:58:12 -0800 Subject: [PATCH 228/286] Add Paths entry parsing to json parser --- .../octree/src/OctreeEntitiesFileParser.cpp | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/libraries/octree/src/OctreeEntitiesFileParser.cpp b/libraries/octree/src/OctreeEntitiesFileParser.cpp index 72853947c6..873eaff0e1 100644 --- a/libraries/octree/src/OctreeEntitiesFileParser.cpp +++ b/libraries/octree/src/OctreeEntitiesFileParser.cpp @@ -117,6 +117,28 @@ bool OctreeEntitiesFileParser::parseEntities(QVariantMap& parsedEntities) { int versionValue = readInteger(); parsedEntities["Version"] = versionValue; gotVersion = true; + } else if (key == "Paths") { + // Serverless JSON has optional Paths entry. + if (nextToken() != '{') { + _errorString = "Paths item is not an object"; + return false; + } + + int matchingBrace = findMatchingBrace(); + if (matchingBrace < 0) { + _errorString = "Unterminated entity object"; + return false; + } + + QByteArray jsonObject = _entitiesContents.mid(_position - 1, matchingBrace - _position + 1); + QJsonDocument pathsObject = QJsonDocument::fromJson(jsonObject); + if (pathsObject.isNull()) { + _errorString = "Ill-formed paths entry"; + return false; + } + + parsedEntities["Paths"] = pathsObject.object(); + _position = matchingBrace; } else { _errorString = "Unrecognized key name: " + key; return false; From 010f7121a377c9d7cb7a505de5eabc116ae79915 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Wed, 14 Nov 2018 15:27:18 +1300 Subject: [PATCH 229/286] Fix Goto app clicks not working Includes better card highlighting fix. --- interface/resources/qml/hifi/Card.qml | 23 +++++++++++------------ interface/resources/qml/hifi/Feed.qml | 1 - 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/interface/resources/qml/hifi/Card.qml b/interface/resources/qml/hifi/Card.qml index f7147e1264..d7fafcc165 100644 --- a/interface/resources/qml/hifi/Card.qml +++ b/interface/resources/qml/hifi/Card.qml @@ -49,7 +49,6 @@ Item { property string defaultThumbnail: Qt.resolvedUrl("../../images/default-domain.gif"); property int shadowHeight: 10; property bool hovered: false - property bool scrolling: false HifiConstants { id: hifi } @@ -238,7 +237,7 @@ Item { property var unhoverThunk: function () { }; Rectangle { anchors.fill: parent - visible: root.hovered && !root.scrolling + visible: root.hovered color: "transparent" border.width: 4 border.color: hifiStyleConstants.colors.primaryHighlight @@ -252,17 +251,17 @@ Item { goFunction("hifi://" + hifiUrl); } hoverEnabled: true; - onEntered: { - Tablet.playSound(TabletEnums.ButtonHover); - hoverThunk(); + onContainsMouseChanged: { + // Use onContainsMouseChanged rather than onEntered and onExited because the latter aren't always + // triggered correctly - e.g., if drag rightwards from right hand side of a card to the next card + // onExited doesn't fire, in which case can end up with two cards highlighted. + if (containsMouse) { + Tablet.playSound(TabletEnums.ButtonHover); + hoverThunk(); + } else { + unhoverThunk(); + } } - onExited: unhoverThunk(); - onCanceled: unhoverThunk(); - } - MouseArea { - // This second mouse area causes onEntered to fire on the first if you scroll just a little and the cursor stays on - // the original card. I.e., the original card is re-highlighted if the cursor is on it after scrolling finishes. - anchors.fill: parent } StateImage { id: actionIcon; diff --git a/interface/resources/qml/hifi/Feed.qml b/interface/resources/qml/hifi/Feed.qml index 6928fc6f02..1e89971938 100644 --- a/interface/resources/qml/hifi/Feed.qml +++ b/interface/resources/qml/hifi/Feed.qml @@ -141,7 +141,6 @@ Column { textSizeSmall: root.textSizeSmall; stackShadowNarrowing: root.stackShadowNarrowing; shadowHeight: root.stackedCardShadowHeight; - scrolling: scroll.moving hoverThunk: function () { hovered = true; From 85a3938cbee6755eb0f56ab596efb31b285b9dfc Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Wed, 14 Nov 2018 11:55:11 +0100 Subject: [PATCH 230/286] Switch to app for ambient occlusion debug script --- .../render-utils/src/AmbientOcclusionEffect.h | 8 +- .../render/debugAmbientOcclusionPass.js | 104 +++++------------- 2 files changed, 33 insertions(+), 79 deletions(-) diff --git a/libraries/render-utils/src/AmbientOcclusionEffect.h b/libraries/render-utils/src/AmbientOcclusionEffect.h index a5b3ec1fdb..864aef09e4 100644 --- a/libraries/render-utils/src/AmbientOcclusionEffect.h +++ b/libraries/render-utils/src/AmbientOcclusionEffect.h @@ -208,7 +208,7 @@ private: void updateJitterSamples(); int getDepthResolutionLevel() const; - + AOParametersBuffer _aoParametersBuffer; FrameParametersBuffer _aoFrameParametersBuffer[SSAO_SPLIT_COUNT*SSAO_SPLIT_COUNT]; BlurParametersBuffer _vblurParametersBuffer; @@ -216,13 +216,13 @@ private: float _blurEdgeSharpness{ 0.0f }; static const gpu::PipelinePointer& getOcclusionPipeline(); - static const gpu::PipelinePointer& getBilateralBlurPipeline(); - static const gpu::PipelinePointer& getMipCreationPipeline(); + static const gpu::PipelinePointer& getBilateralBlurPipeline(); + static const gpu::PipelinePointer& getMipCreationPipeline(); static const gpu::PipelinePointer& getGatherPipeline(); static const gpu::PipelinePointer& getBuildNormalsPipeline(); static gpu::PipelinePointer _occlusionPipeline; - static gpu::PipelinePointer _bilateralBlurPipeline; + static gpu::PipelinePointer _bilateralBlurPipeline; static gpu::PipelinePointer _mipCreationPipeline; static gpu::PipelinePointer _gatherPipeline; static gpu::PipelinePointer _buildNormalsPipeline; diff --git a/scripts/developer/utilities/render/debugAmbientOcclusionPass.js b/scripts/developer/utilities/render/debugAmbientOcclusionPass.js index 409473511e..19c29c59a3 100644 --- a/scripts/developer/utilities/render/debugAmbientOcclusionPass.js +++ b/scripts/developer/utilities/render/debugAmbientOcclusionPass.js @@ -12,88 +12,42 @@ // (function() { - var TABLET_BUTTON_NAME = "AO"; - var QMLAPP_URL = Script.resolvePath("./ambientOcclusionPass.qml"); + var AppUi = Script.require('appUi'); - - var onLuciScreen = false; + var onMousePressEvent = function (e) { + }; + Controller.mousePressEvent.connect(onMousePressEvent); - function onClicked() { - if (onLuciScreen) { - tablet.gotoHomeScreen(); - } else { - tablet.loadQMLSource(QMLAPP_URL); - } - } + var onMouseReleaseEvent = function () { + }; + Controller.mouseReleaseEvent.connect(onMouseReleaseEvent); - var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); - var button = tablet.addButton({ - text: TABLET_BUTTON_NAME, - sortOrder: 1 - }); - - var hasEventBridge = false; - - function wireEventBridge(on) { - if (!tablet) { - print("Warning in wireEventBridge(): 'tablet' undefined!"); - return; - } - if (on) { - if (!hasEventBridge) { - tablet.fromQml.connect(fromQml); - hasEventBridge = true; - } - } else { - if (hasEventBridge) { - tablet.fromQml.disconnect(fromQml); - hasEventBridge = false; - } - } - } - - function onScreenChanged(type, url) { - if (url === QMLAPP_URL) { - onLuciScreen = true; - } else { - onLuciScreen = false; - } - - button.editProperties({isActive: onLuciScreen}); - wireEventBridge(onLuciScreen); - } + var onMouseMoveEvent = function (e) { + }; + Controller.mouseMoveEvent.connect(onMouseMoveEvent); function fromQml(message) { + } - - button.clicked.connect(onClicked); - tablet.screenChanged.connect(onScreenChanged); - - var moveDebugCursor = false; - Controller.mousePressEvent.connect(function (e) { - if (e.isMiddleButton) { - moveDebugCursor = true; - setDebugCursor(e.x, e.y); - } - }); - Controller.mouseReleaseEvent.connect(function() { moveDebugCursor = false; }); - Controller.mouseMoveEvent.connect(function (e) { if (moveDebugCursor) setDebugCursor(e.x, e.y); }); - + var ui; + function startup() { + ui = new AppUi({ + buttonName: "AO", + home: Script.resolvePath("ambientOcclusionPass.qml"), + onMessage: fromQml, + //normalButton: Script.resolvePath("../../../system/assets/images/ao-i.svg"), + //activeButton: Script.resolvePath("../../../system/assets/images/ao-a.svg") + }); + } + startup(); Script.scriptEnding.connect(function () { - if (onLuciScreen) { - tablet.gotoHomeScreen(); - } - button.clicked.disconnect(onClicked); - tablet.screenChanged.disconnect(onScreenChanged); - tablet.removeButton(button); + Controller.mousePressEvent.disconnect(onMousePressEvent); + Controller.mouseReleaseEvent.disconnect(onMouseReleaseEvent); + Controller.mouseMoveEvent.disconnect(onMouseMoveEvent); + pages.clear(); + // killEngineInspectorView(); + // killCullInspectorView(); + // killEngineLODWindow(); }); - - function setDebugCursor(x, y) { - nx = ((x + 0.5) / Window.innerWidth); - ny = 1.0 - ((y + 0.5) / (Window.innerHeight)); - - Render.getConfig("RenderMainView").getConfig("DebugAmbientOcclusion").debugCursorTexcoord = { x: nx, y: ny }; - } - }()); From f5e14565b804f92c9a86a55edf8a23a015b92bfa Mon Sep 17 00:00:00 2001 From: Simon Walton Date: Wed, 14 Nov 2018 10:17:48 -0800 Subject: [PATCH 231/286] Clean-up javascript formatting --- .../resources/web/content/js/content.js | 46 +++++++++---------- 1 file changed, 22 insertions(+), 24 deletions(-) diff --git a/domain-server/resources/web/content/js/content.js b/domain-server/resources/web/content/js/content.js index 3f36a1b447..93bde3122b 100644 --- a/domain-server/resources/web/content/js/content.js +++ b/domain-server/resources/web/content/js/content.js @@ -14,17 +14,16 @@ $(document).ready(function(){ return html; } - function uploadNextChunk(file, offset) - { + function uploadNextChunk(file, offset) { if (offset == undefined) { offset = 0; } var fileSize = file.size; var filename = file.name; - + var CHUNK_SIZE = 1048576; // 1 MiB - + var isFinal = Boolean(fileSize - offset <= CHUNK_SIZE); var nextChunkSize = Math.min(fileSize - offset, CHUNK_SIZE); var chunk = file.slice(offset, offset + CHUNK_SIZE, file.type); @@ -33,29 +32,29 @@ $(document).ready(function(){ var formItemName = isFinal ? 'restore-file-chunk-final' : 'restore-file-chunk'; chunkFormData.append(formItemName, chunk, filename); var ajaxParams = { - url: '/content/upload', - type: 'POST', - timeout: 30000, // 30 s - cache: false, - processData: false, - contentType: false, - data: chunkFormData - }; - + url: '/content/upload', + type: 'POST', + timeout: 30000, // 30 s + cache: false, + processData: false, + contentType: false, + data: chunkFormData + }; + var ajaxObject = $.ajax(ajaxParams); ajaxObject.fail(function(jqXHR, textStatus, errorThrown) { - showErrorMessage( - "Error", - "There was a problem restoring domain content.\n" - + "Please ensure that the content archive or entity file is valid and try again." - ); - }); + showErrorMessage( + "Error", + "There was a problem restoring domain content.\n" + + "Please ensure that the content archive or entity file is valid and try again." + ); + }); if (!isFinal) { - ajaxObject.done(function (data, textStatus, jqXHR) - {uploadNextChunk(file, offset + CHUNK_SIZE);}); + ajaxObject.done(function (data, textStatus, jqXHR) + { uploadNextChunk(file, offset + CHUNK_SIZE); }); } else { - ajaxObject.done(function(data, textStatus, jqXHR) { + ajaxObject.done(function(data, textStatus, jqXHR) { isRestoring = true; // immediately reload backup information since one should be restoring now @@ -65,7 +64,7 @@ $(document).ready(function(){ }); } - } + } function setupBackupUpload() { // construct the HTML needed for the settings backup panel @@ -106,7 +105,6 @@ $(document).ready(function(){ showSpinnerAlert("Uploading content to restore"); - uploadNextChunk(files[0]); } ); From cd12dd8f6ddf2beda7e687597afbd9511ac116c7 Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Wed, 14 Nov 2018 19:28:57 +0100 Subject: [PATCH 232/286] fix OSX shortcut inconsistencies --- scripts/system/edit.js | 18 +++++++++--------- scripts/system/html/js/entityList.js | 10 ++++++---- scripts/system/html/js/entityProperties.js | 8 +++++--- scripts/system/html/js/gridControls.js | 8 +++++--- 4 files changed, 25 insertions(+), 19 deletions(-) diff --git a/scripts/system/edit.js b/scripts/system/edit.js index 3876b1b074..48fdb8e565 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -2722,23 +2722,23 @@ keyUpEventFromUIWindow = function(keyUpEvent) { focusKey(pressedValue); } else if (keyUpEvent.keyCodeString === "G") { gridKey(pressedValue); - } else if (keyUpEvent.ctrlKey && keyUpEvent.keyCodeString === "X") { + } else if (keyUpEvent.controlKey && keyUpEvent.keyCodeString === "X") { selectionManager.cutSelectedEntities(); - } else if (keyUpEvent.ctrlKey && keyUpEvent.keyCodeString === "C") { + } else if (keyUpEvent.controlKey && keyUpEvent.keyCodeString === "C") { selectionManager.copySelectedEntities(); - } else if (keyUpEvent.ctrlKey && keyUpEvent.keyCodeString === "V") { + } else if (keyUpEvent.controlKey && keyUpEvent.keyCodeString === "V") { selectionManager.pasteEntities(); - } else if (keyUpEvent.ctrlKey && keyUpEvent.keyCodeString === "D") { + } else if (keyUpEvent.controlKey && keyUpEvent.keyCodeString === "D") { selectionManager.duplicateSelection(); - } else if (keyUpEvent.ctrlKey && !keyUpEvent.shiftKey && keyUpEvent.keyCodeString === "Z") { + } else if (keyUpEvent.controlKey && !keyUpEvent.shiftKey && keyUpEvent.keyCodeString === "Z") { undoHistory.undo(); - } else if (keyUpEvent.ctrlKey && !keyUpEvent.shiftKey && keyUpEvent.keyCodeString === "P") { + } else if (keyUpEvent.controlKey && !keyUpEvent.shiftKey && keyUpEvent.keyCodeString === "P") { parentSelectedEntities(); - } else if (keyUpEvent.ctrlKey && keyUpEvent.shiftKey && keyUpEvent.keyCodeString === "P") { + } else if (keyUpEvent.controlKey && keyUpEvent.shiftKey && keyUpEvent.keyCodeString === "P") { unparentSelectedEntities(); } else if ( - (keyUpEvent.ctrlKey && keyUpEvent.shiftKey && keyUpEvent.keyCodeString === "Z") || - (keyUpEvent.ctrlKey && keyUpEvent.keyCodeString === "Y")) { + (keyUpEvent.controlKey && keyUpEvent.shiftKey && keyUpEvent.keyCodeString === "Z") || + (keyUpEvent.controlKey && keyUpEvent.keyCodeString === "Y")) { undoHistory.redo(); } else if (WANT_DEBUG_MISSING_SHORTCUTS) { diff --git a/scripts/system/html/js/entityList.js b/scripts/system/html/js/entityList.js index ea096be5e2..141055f2b7 100644 --- a/scripts/system/html/js/entityList.js +++ b/scripts/system/html/js/entityList.js @@ -1123,7 +1123,9 @@ function loaded() { return; } - let {code, key, keyCode, altKey, ctrlKey, shiftKey} = keyUpEvent; + let {code, key, keyCode, altKey, ctrlKey, metaKey, shiftKey} = keyUpEvent; + + let controlKey = window.navigator.platform.startsWith("Mac") ? metaKey : ctrlKey; let keyCodeString; switch (keyCode) { @@ -1138,7 +1140,7 @@ function loaded() { break; } - if (ctrlKey && keyCodeString === "A") { + if (controlKey && keyCodeString === "A") { let visibleEntityIDs = visibleEntities.map(visibleEntity => visibleEntity.id); let selectionIncludesAllVisibleEntityIDs = visibleEntityIDs.every(visibleEntityID => { return selectedEntities.includes(visibleEntityID); @@ -1170,8 +1172,8 @@ function loaded() { keyCode, keyCodeString, altKey, - ctrlKey, - shiftKey + controlKey, + shiftKey, } })); }, false); diff --git a/scripts/system/html/js/entityProperties.js b/scripts/system/html/js/entityProperties.js index 9bb8a2c5a4..847d583ca3 100644 --- a/scripts/system/html/js/entityProperties.js +++ b/scripts/system/html/js/entityProperties.js @@ -3502,7 +3502,9 @@ function loaded() { if (keyUpEvent.target.nodeName === "INPUT") { return; } - let {code, key, keyCode, altKey, ctrlKey, shiftKey} = keyUpEvent; + let {code, key, keyCode, altKey, ctrlKey, metaKey, shiftKey} = keyUpEvent; + + let controlKey = window.navigator.platform.startsWith("Mac") ? metaKey : ctrlKey; let keyCodeString; switch (keyCode) { @@ -3525,8 +3527,8 @@ function loaded() { keyCode, keyCodeString, altKey, - ctrlKey, - shiftKey + controlKey, + shiftKey, } })); }, false); diff --git a/scripts/system/html/js/gridControls.js b/scripts/system/html/js/gridControls.js index 6f7024ccaa..70e91071fb 100644 --- a/scripts/system/html/js/gridControls.js +++ b/scripts/system/html/js/gridControls.js @@ -139,7 +139,9 @@ function loaded() { if (keyUpEvent.target.nodeName === "INPUT") { return; } - let {code, key, keyCode, altKey, ctrlKey, shiftKey} = keyUpEvent; + let {code, key, keyCode, altKey, ctrlKey, metaKey, shiftKey} = keyUpEvent; + + let controlKey = window.navigator.platform.startsWith("Mac") ? metaKey : ctrlKey; let keyCodeString; switch (keyCode) { @@ -162,8 +164,8 @@ function loaded() { keyCode, keyCodeString, altKey, - ctrlKey, - shiftKey + controlKey, + shiftKey, } })); }, false); From 70493e3f29f37c57fa8422487032825a0c6d5c87 Mon Sep 17 00:00:00 2001 From: David Back Date: Wed, 14 Nov 2018 12:23:16 -0800 Subject: [PATCH 233/286] add polyline/polyvox to entity list/properties types --- scripts/system/html/js/entityList.js | 4 ++++ scripts/system/html/js/entityProperties.js | 2 ++ 2 files changed, 6 insertions(+) diff --git a/scripts/system/html/js/entityList.js b/scripts/system/html/js/entityList.js index 82e64a6fbc..0319012cf6 100644 --- a/scripts/system/html/js/entityList.js +++ b/scripts/system/html/js/entityList.js @@ -136,6 +136,8 @@ const FILTER_TYPES = [ "Web", "Material", "ParticleEffect", + "PolyLine", + "PolyVox", "Text", ]; @@ -148,6 +150,8 @@ const ICON_FOR_TYPE = { Web: "q", Material: "", ParticleEffect: "", + PolyLine: "", + PolyVox: "", Text: "l", }; diff --git a/scripts/system/html/js/entityProperties.js b/scripts/system/html/js/entityProperties.js index e05ca3ee60..96f2323e7d 100644 --- a/scripts/system/html/js/entityProperties.js +++ b/scripts/system/html/js/entityProperties.js @@ -1342,6 +1342,8 @@ const GROUPS_PER_TYPE = { Material: [ 'base', 'material', 'spatial', 'behavior' ], ParticleEffect: [ 'base', 'particles', 'particles_emit', 'particles_size', 'particles_color', 'particles_alpha', 'particles_acceleration', 'particles_spin', 'particles_constraints', 'spatial', 'behavior', 'physics' ], + PolyLine: [ 'base', 'spatial', 'behavior', 'collision', 'physics' ], + PolyVox: [ 'base', 'spatial', 'behavior', 'collision', 'physics' ], Multiple: [ 'base', 'spatial', 'behavior', 'collision', 'physics' ], }; From 1593c1185aa0701d6bdf2754570cfbeebc62a45b Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Wed, 14 Nov 2018 12:28:01 -0800 Subject: [PATCH 234/286] revert PR-14238 "TypeError: Cannot read property 'buttons'" until we can adjust it. --- interface/resources/qml/hifi/Desktop.qml | 4 ++-- interface/resources/qml/hifi/tablet/TabletHome.qml | 4 ++-- libraries/qml/src/qml/OffscreenSurface.cpp | 8 ++------ libraries/qml/src/qml/impl/SharedObject.cpp | 12 +----------- libraries/qml/src/qml/impl/SharedObject.h | 4 +--- 5 files changed, 8 insertions(+), 24 deletions(-) diff --git a/interface/resources/qml/hifi/Desktop.qml b/interface/resources/qml/hifi/Desktop.qml index 354c8095a0..f57c612b98 100644 --- a/interface/resources/qml/hifi/Desktop.qml +++ b/interface/resources/qml/hifi/Desktop.qml @@ -70,8 +70,8 @@ OriginalDesktop.Desktop { anchors.horizontalCenter: settings.constrainToolbarToCenterX ? desktop.horizontalCenter : undefined; // Literal 50 is overwritten by settings from previous session, and sysToolbar.x comes from settings when not constrained. x: sysToolbar.x - buttonModel: tablet ? tablet.buttons : null; - shown: tablet ? tablet.toolbarMode : false; + buttonModel: tablet.buttons; + shown: tablet.toolbarMode; } Settings { diff --git a/interface/resources/qml/hifi/tablet/TabletHome.qml b/interface/resources/qml/hifi/tablet/TabletHome.qml index cbfb3c1337..f1f54e8419 100644 --- a/interface/resources/qml/hifi/tablet/TabletHome.qml +++ b/interface/resources/qml/hifi/tablet/TabletHome.qml @@ -115,9 +115,9 @@ Item { property int previousIndex: -1 Repeater { id: pageRepeater - model: tabletProxy != null ? Math.ceil(tabletProxy.buttons.rowCount() / TabletEnums.ButtonsOnPage) : 0 + model: Math.ceil(tabletProxy.buttons.rowCount() / TabletEnums.ButtonsOnPage) onItemAdded: { - item.proxyModel.sourceModel = tabletProxy != null ? tabletProxy.buttons : null; + item.proxyModel.sourceModel = tabletProxy.buttons; item.proxyModel.pageIndex = index; } diff --git a/libraries/qml/src/qml/OffscreenSurface.cpp b/libraries/qml/src/qml/OffscreenSurface.cpp index 7edd4e481c..91532534e3 100644 --- a/libraries/qml/src/qml/OffscreenSurface.cpp +++ b/libraries/qml/src/qml/OffscreenSurface.cpp @@ -387,12 +387,8 @@ void OffscreenSurface::finishQmlLoad(QQmlComponent* qmlComponent, if (!parent) { parent = getRootItem(); } - // manually control children items lifetime - QQmlEngine::setObjectOwnership(newObject, QQmlEngine::CppOwnership); - - // add object to the manual deletion list - _sharedObject->addToDeletionList(newObject); - + // Allow child windows to be destroyed from JS + QQmlEngine::setObjectOwnership(newObject, QQmlEngine::JavaScriptOwnership); newObject->setParent(parent); newItem->setParentItem(parent); } else { diff --git a/libraries/qml/src/qml/impl/SharedObject.cpp b/libraries/qml/src/qml/impl/SharedObject.cpp index 1f41fefb02..259defdb48 100644 --- a/libraries/qml/src/qml/impl/SharedObject.cpp +++ b/libraries/qml/src/qml/impl/SharedObject.cpp @@ -15,7 +15,6 @@ #include #include -#include #include #include @@ -82,6 +81,7 @@ SharedObject::SharedObject() { SharedObject::~SharedObject() { // After destroy returns, the rendering thread should be gone destroy(); + // _renderTimer is created with `this` as the parent, so need no explicit destruction #ifndef DISABLE_QML // Destroy the event hand @@ -96,11 +96,6 @@ SharedObject::~SharedObject() { } #endif - // already deleted objects will be reset to null by QPointer so it should be safe just iterate here - for (auto qmlObject : _deletionList) { - delete qmlObject; - } - if (_rootItem) { delete _rootItem; _rootItem = nullptr; @@ -417,11 +412,6 @@ bool SharedObject::fetchTexture(TextureAndFence& textureAndFence) { return true; } -void hifi::qml::impl::SharedObject::addToDeletionList(QObject * object) -{ - _deletionList.append(QPointer(object)); -} - void SharedObject::setProxyWindow(QWindow* window) { #ifndef DISABLE_QML _proxyWindow = window; diff --git a/libraries/qml/src/qml/impl/SharedObject.h b/libraries/qml/src/qml/impl/SharedObject.h index ce9fcd46d2..002679c44d 100644 --- a/libraries/qml/src/qml/impl/SharedObject.h +++ b/libraries/qml/src/qml/impl/SharedObject.h @@ -66,7 +66,7 @@ public: void resume(); bool isPaused() const; bool fetchTexture(TextureAndFence& textureAndFence); - void addToDeletionList(QObject* object); + private: bool event(QEvent* e) override; @@ -91,8 +91,6 @@ private: void onAboutToQuit(); void updateTextureAndFence(const TextureAndFence& newTextureAndFence); - QList> _deletionList; - // Texture management TextureAndFence _latestTextureAndFence{ 0, 0 }; QQuickItem* _item{ nullptr }; From 9c5d31834143fa5e10e4ea2c3c78a0ea63c3bbda Mon Sep 17 00:00:00 2001 From: David Rowe Date: Thu, 15 Nov 2018 10:15:30 +1300 Subject: [PATCH 235/286] Fix highlighting --- interface/resources/qml/hifi/Card.qml | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/interface/resources/qml/hifi/Card.qml b/interface/resources/qml/hifi/Card.qml index d7fafcc165..5c89a07b08 100644 --- a/interface/resources/qml/hifi/Card.qml +++ b/interface/resources/qml/hifi/Card.qml @@ -244,13 +244,9 @@ Item { z: 1 } MouseArea { - anchors.fill: parent; - acceptedButtons: Qt.LeftButton; - onClicked: { - Tablet.playSound(TabletEnums.ButtonClick); - goFunction("hifi://" + hifiUrl); - } - hoverEnabled: true; + anchors.fill: parent + acceptedButtons: Qt.LeftButton + hoverEnabled: true onContainsMouseChanged: { // Use onContainsMouseChanged rather than onEntered and onExited because the latter aren't always // triggered correctly - e.g., if drag rightwards from right hand side of a card to the next card @@ -263,6 +259,17 @@ Item { } } } + MouseArea { + // Separate MouseArea for click handling so that it doesn't interfere with hovering and interaction + // with containing ListView. + anchors.fill: parent + acceptedButtons: Qt.LeftButton + hoverEnabled: false + onClicked: { + Tablet.playSound(TabletEnums.ButtonClick); + goFunction("hifi://" + hifiUrl); + } + } StateImage { id: actionIcon; visible: !isAnnouncement; From d8f5e0f7088e6b5122a116fb50b4fec81f3a615e Mon Sep 17 00:00:00 2001 From: David Back Date: Wed, 14 Nov 2018 14:32:17 -0800 Subject: [PATCH 236/286] add min/max/step for some properties, remove duplicate createProperty, improve switch between states --- scripts/system/html/js/draggableNumber.js | 42 ++++---- scripts/system/html/js/entityProperties.js | 110 +++++---------------- 2 files changed, 46 insertions(+), 106 deletions(-) diff --git a/scripts/system/html/js/draggableNumber.js b/scripts/system/html/js/draggableNumber.js index 7e5d9d524a..1f4bc21441 100644 --- a/scripts/system/html/js/draggableNumber.js +++ b/scripts/system/html/js/draggableNumber.js @@ -24,16 +24,28 @@ DraggableNumber.prototype = { if (event.target === this.elText) { this.initialMouseEvent = event; this.lastMouseEvent = event; - document.addEventListener("mousemove", this.onMouseMove); - document.addEventListener("mouseup", this.onMouseUp); + document.addEventListener("mousemove", this.onDocumentMouseMove); + document.addEventListener("mouseup", this.onDocumentMouseUp); } }, - mouseMove: function(event) { + mouseUp: function(event) { + if (event.target === this.elText && this.initialMouseEvent) { + let dx = event.clientX - this.initialMouseEvent.clientX; + if (dx <= DELTA_X_FOCUS_THRESHOLD) { + this.elInput.style.visibility = "visible"; + this.elText.style.visibility = "hidden"; + } + this.initialMouseEvent = null; + } + }, + + documentMouseMove: function(event) { if (this.lastMouseEvent) { + let initialValue = this.elInput.value; let dx = event.clientX - this.lastMouseEvent.clientX; - let inputChanged = dx !== 0; - if (inputChanged) { + let changeValue = dx !== 0; + if (changeValue) { while (dx !== 0) { if (dx > 0) { this.stepUp(); @@ -51,18 +63,10 @@ DraggableNumber.prototype = { } }, - mouseUp: function(event) { - if (this.initialMouseEvent) { - let dx = event.clientX - this.initialMouseEvent.clientX; - if (dx <= DELTA_X_FOCUS_THRESHOLD) { - this.elInput.style.visibility = "visible"; - this.elText.style.visibility = "hidden"; - } - this.initialMouseEvent = null; - this.lastMouseEvent = null; - document.removeEventListener("mousemove", this.onMouseMove); - document.removeEventListener("mouseup", this.onMouseUp); - } + documentMouseUp: function(event) { + this.lastMouseEvent = null; + document.removeEventListener("mousemove", this.onDocumentMouseMove); + document.removeEventListener("mouseup", this.onDocumentMouseUp); }, stepUp: function() { @@ -103,8 +107,9 @@ DraggableNumber.prototype = { initialize: function() { this.onMouseDown = this.mouseDown.bind(this); - this.onMouseMove = this.mouseMove.bind(this); this.onMouseUp = this.mouseUp.bind(this); + this.onDocumentMouseMove = this.documentMouseMove.bind(this); + this.onDocumentMouseUp = this.documentMouseUp.bind(this); this.onStepUp = this.stepUp.bind(this); this.onStepDown = this.stepDown.bind(this); this.onInputChange = this.inputChange.bind(this); @@ -118,6 +123,7 @@ DraggableNumber.prototype = { this.elText.innerText = " "; this.elText.style.visibility = "visible"; this.elText.addEventListener("mousedown", this.onMouseDown); + this.elText.addEventListener("mouseup", this.onMouseUp); this.elLeftArrow = document.createElement('span'); this.elRightArrow = document.createElement('span'); diff --git a/scripts/system/html/js/entityProperties.js b/scripts/system/html/js/entityProperties.js index a9233f0dce..ae5748cbc8 100644 --- a/scripts/system/html/js/entityProperties.js +++ b/scripts/system/html/js/entityProperties.js @@ -1288,24 +1288,36 @@ const GROUPS = [ { label: "Angular Damping", type: "number", + min: 0, + max: 1, + step: 0.01, decimals: 4, propertyID: "angularDamping", }, { label: "Bounciness", type: "number", + min: 0, + max: 1, + step: 0.01, decimals: 4, propertyID: "restitution", }, { label: "Friction", type: "number", + min: 0, + max: 10, + step: 0.1, decimals: 4, propertyID: "friction", }, { label: "Density", type: "number", + min: 100, + max: 10000, + step: 1, decimals: 4, propertyID: "density", }, @@ -1314,6 +1326,7 @@ const GROUPS = [ type: "vec3", vec3Type: "xyz", subLabels: [ "x", "y", "z" ], + decimals: 4, unit: "m/s2", propertyID: "gravity", }, @@ -2145,28 +2158,28 @@ function createProperty(propertyData, propertyElementID, propertyName, propertyI break; } case 'number': { - property.elInput = createNumberProperty(property, elProperty); + property.elNumber = createNumberProperty(property, elProperty); break; } case 'vec3': { let elVec3 = createVec3Property(property, elProperty); - property.elInputX = elVec3[VECTOR_ELEMENTS.X_INPUT]; - property.elInputY = elVec3[VECTOR_ELEMENTS.Y_INPUT]; - property.elInputZ = elVec3[VECTOR_ELEMENTS.Z_INPUT]; + property.elNumberX = elVec3[VECTOR_ELEMENTS.X_NUMBER]; + property.elNumberY = elVec3[VECTOR_ELEMENTS.Y_NUMBER]; + property.elNumberZ = elVec3[VECTOR_ELEMENTS.Z_NUMBER]; break; } case 'vec2': { let elVec2 = createVec2Property(property, elProperty); - property.elInputX = elVec2[VECTOR_ELEMENTS.X_INPUT]; - property.elInputY = elVec2[VECTOR_ELEMENTS.Y_INPUT]; + property.elNumberX = elVec2[VECTOR_ELEMENTS.X_NUMBER]; + property.elNumberY = elVec2[VECTOR_ELEMENTS.Y_NUMBER]; break; } case 'color': { let elColor = createColorProperty(property, elProperty); property.elColorPicker = elColor[COLOR_ELEMENTS.COLOR_PICKER]; - property.elInputR = elColor[COLOR_ELEMENTS.RED_INPUT]; - property.elInputG = elColor[COLOR_ELEMENTS.GREEN_INPUT]; - property.elInputB = elColor[COLOR_ELEMENTS.BLUE_INPUT]; + property.elNumberR = elColor[COLOR_ELEMENTS.RED_NUMBER]; + property.elNumberG = elColor[COLOR_ELEMENTS.GREEN_NUMBER]; + property.elNumberB = elColor[COLOR_ELEMENTS.BLUE_NUMBER]; break; } case 'dropdown': { @@ -2724,85 +2737,6 @@ function showParentMaterialNameBox(number, elNumber, elString) { } } -function createProperty(propertyData, propertyElementID, propertyName, propertyID, elProperty) { - let property = { - data: propertyData, - elementID: propertyElementID, - name: propertyName, - elProperty: elProperty, - }; - let propertyType = propertyData.type; - - switch (propertyType) { - case 'string': { - property.elInput = createStringProperty(property, elProperty); - break; - } - case 'bool': { - property.elInput = createBoolProperty(property, elProperty); - break; - } - case 'number': { - property.elNumber = createNumberProperty(property, elProperty); - break; - } - case 'vec3': { - let elVec3 = createVec3Property(property, elProperty); - property.elNumberX = elVec3[VECTOR_ELEMENTS.X_NUMBER]; - property.elNumberY = elVec3[VECTOR_ELEMENTS.Y_NUMBER]; - property.elNumberZ = elVec3[VECTOR_ELEMENTS.Z_NUMBER]; - break; - } - case 'vec2': { - let elVec2 = createVec2Property(property, elProperty); - property.elNumberX = elVec2[VECTOR_ELEMENTS.X_NUMBER]; - property.elNumberY = elVec2[VECTOR_ELEMENTS.Y_NUMBER]; - break; - } - case 'color': { - let elColor = createColorProperty(property, elProperty); - property.elColorPicker = elColor[COLOR_ELEMENTS.COLOR_PICKER]; - property.elNumberR = elColor[COLOR_ELEMENTS.RED_NUMBER]; - property.elNumberG = elColor[COLOR_ELEMENTS.GREEN_NUMBER]; - property.elNumberB = elColor[COLOR_ELEMENTS.BLUE_NUMBER]; - break; - } - case 'dropdown': { - property.elInput = createDropdownProperty(property, propertyID, elProperty); - break; - } - case 'textarea': { - property.elInput = createTextareaProperty(property, elProperty); - break; - } - case 'icon': { - property.elSpan = createIconProperty(property, elProperty); - break; - } - case 'texture': { - let elTexture = createTextureProperty(property, elProperty); - property.elImage = elTexture[TEXTURE_ELEMENTS.IMAGE]; - property.elInput = elTexture[TEXTURE_ELEMENTS.TEXT_INPUT]; - break; - } - case 'buttons': { - property.elProperty = createButtonsProperty(property, elProperty); - break; - } - case 'placeholder': - case 'sub-header': { - break; - } - default: { - console.log("EntityProperties - Unknown property type " + - propertyType + " set to property " + propertyID); - break; - } - } - - return property; -} - function loaded() { openEventBridge(function() { From 7192aed131440b3eb9860f8ee95cbd2e43973081 Mon Sep 17 00:00:00 2001 From: Clement Date: Wed, 24 Oct 2018 15:46:54 -0700 Subject: [PATCH 237/286] Rework audio data memory ownership model --- assignment-client/src/Agent.cpp | 10 +- interface/src/Application.cpp | 3 +- interface/src/avatar/AvatarManager.cpp | 9 +- libraries/audio/src/AudioConstants.h | 2 +- libraries/audio/src/AudioInjector.cpp | 149 +++++---- libraries/audio/src/AudioInjector.h | 14 +- .../audio/src/AudioInjectorLocalBuffer.cpp | 20 +- .../audio/src/AudioInjectorLocalBuffer.h | 13 +- libraries/audio/src/Sound.cpp | 286 ++++++++++-------- libraries/audio/src/Sound.h | 93 ++++-- .../src/EntityTreeRenderer.cpp | 9 +- .../src/AudioScriptingInterface.cpp | 6 +- libraries/script-engine/src/ScriptEngine.h | 4 +- .../ui/src/ui/TabletScriptingInterface.cpp | 2 +- libraries/ui/src/ui/types/SoundEffect.cpp | 3 +- 15 files changed, 355 insertions(+), 268 deletions(-) diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index d9a399c162..88897a0fed 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -754,13 +754,13 @@ void Agent::processAgentAvatarAudio() { const int16_t* nextSoundOutput = NULL; if (_avatarSound) { - const QByteArray& soundByteArray = _avatarSound->getByteArray(); - nextSoundOutput = reinterpret_cast(soundByteArray.data() + auto audioData = _avatarSound->getAudioData(); + nextSoundOutput = reinterpret_cast(audioData->rawData() + _numAvatarSoundSentBytes); - int numAvailableBytes = (soundByteArray.size() - _numAvatarSoundSentBytes) > AudioConstants::NETWORK_FRAME_BYTES_PER_CHANNEL + int numAvailableBytes = (audioData->getNumBytes() - _numAvatarSoundSentBytes) > AudioConstants::NETWORK_FRAME_BYTES_PER_CHANNEL ? AudioConstants::NETWORK_FRAME_BYTES_PER_CHANNEL - : soundByteArray.size() - _numAvatarSoundSentBytes; + : audioData->getNumBytes() - _numAvatarSoundSentBytes; numAvailableSamples = (int16_t)numAvailableBytes / sizeof(int16_t); @@ -773,7 +773,7 @@ void Agent::processAgentAvatarAudio() { } _numAvatarSoundSentBytes += numAvailableBytes; - if (_numAvatarSoundSentBytes == soundByteArray.size()) { + if (_numAvatarSoundSentBytes == (int)audioData->getNumBytes()) { // we're done with this sound object - so set our pointer back to NULL // and our sent bytes back to zero _avatarSound.clear(); diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 5256ef2c6a..4a1a18c470 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -4050,8 +4050,7 @@ void Application::keyPressEvent(QKeyEvent* event) { _snapshotSoundInjector->setOptions(options); _snapshotSoundInjector->restart(); } else { - QByteArray samples = _snapshotSound->getByteArray(); - _snapshotSoundInjector = AudioInjector::playSound(samples, options); + _snapshotSoundInjector = AudioInjector::playSound(_snapshotSound, options); } } takeSnapshot(true); diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index 76ad49b1f4..7ca18ca258 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -562,8 +562,13 @@ void AvatarManager::handleCollisionEvents(const CollisionEvents& collisionEvents static const int MAX_INJECTOR_COUNT = 3; if (_collisionInjectors.size() < MAX_INJECTOR_COUNT) { - auto injector = AudioInjector::playSound(collisionSound, energyFactorOfFull, AVATAR_STRETCH_FACTOR, - myAvatar->getWorldPosition()); + AudioInjectorOptions options; + options.stereo = collisionSound->isStereo(); + options.position = myAvatar->getWorldPosition(); + options.volume = energyFactorOfFull; + options.pitch = 1.0f / AVATAR_STRETCH_FACTOR; + + auto injector = AudioInjector::playSoundAndDelete(collisionSound, options); _collisionInjectors.emplace_back(injector); } myAvatar->collisionWithEntity(collision); diff --git a/libraries/audio/src/AudioConstants.h b/libraries/audio/src/AudioConstants.h index 6d9678c368..f4235f42c5 100644 --- a/libraries/audio/src/AudioConstants.h +++ b/libraries/audio/src/AudioConstants.h @@ -22,7 +22,7 @@ namespace AudioConstants { const int STEREO = 2; const int AMBISONIC = 4; - typedef int16_t AudioSample; + using AudioSample = int16_t; const int SAMPLE_SIZE = sizeof(AudioSample); inline const char* getAudioFrameName() { return "com.highfidelity.recording.Audio"; } diff --git a/libraries/audio/src/AudioInjector.cpp b/libraries/audio/src/AudioInjector.cpp index d1b919292a..efd823c5b1 100644 --- a/libraries/audio/src/AudioInjector.cpp +++ b/libraries/audio/src/AudioInjector.cpp @@ -38,12 +38,14 @@ AudioInjectorState& operator|= (AudioInjectorState& lhs, AudioInjectorState rhs) return lhs; }; -AudioInjector::AudioInjector(const Sound& sound, const AudioInjectorOptions& injectorOptions) : - AudioInjector(sound.getByteArray(), injectorOptions) +AudioInjector::AudioInjector(SharedSoundPointer sound, const AudioInjectorOptions& injectorOptions) : + _sound(sound), + _audioData(sound->getAudioData()), + _options(injectorOptions) { } -AudioInjector::AudioInjector(const QByteArray& audioData, const AudioInjectorOptions& injectorOptions) : +AudioInjector::AudioInjector(AudioDataPointer audioData, const AudioInjectorOptions& injectorOptions) : _audioData(audioData), _options(injectorOptions) { @@ -154,7 +156,7 @@ bool AudioInjector::inject(bool(AudioInjectorManager::*injection)(const AudioInj bool AudioInjector::injectLocally() { bool success = false; if (_localAudioInterface) { - if (_audioData.size() > 0) { + if (_audioData->getNumBytes() > 0) { _localBuffer = new AudioInjectorLocalBuffer(_audioData); @@ -220,22 +222,12 @@ int64_t AudioInjector::injectNextFrame() { if (!_currentPacket) { if (_currentSendOffset < 0 || - _currentSendOffset >= _audioData.size()) { + _currentSendOffset >= (int)_audioData->getNumBytes()) { _currentSendOffset = 0; } // make sure we actually have samples downloaded to inject - if (_audioData.size()) { - - int sampleSize = (_options.stereo ? 2 : 1) * sizeof(AudioConstants::AudioSample); - auto numSamples = static_cast(_audioData.size() / sampleSize); - auto targetSize = numSamples * sampleSize; - if (targetSize != _audioData.size()) { - qCDebug(audio) << "Resizing audio that doesn't end at multiple of sample size, resizing from " - << _audioData.size() << " to " << targetSize; - _audioData.resize(targetSize); - } - + if (_audioData && _audioData->getNumSamples() > 0) { _outgoingSequenceNumber = 0; _nextFrame = 0; @@ -307,19 +299,10 @@ int64_t AudioInjector::injectNextFrame() { _frameTimer->restart(); } - int totalBytesLeftToCopy = (_options.stereo ? 2 : 1) * AudioConstants::NETWORK_FRAME_BYTES_PER_CHANNEL; - if (!_options.loop) { - // If we aren't looping, let's make sure we don't read past the end - totalBytesLeftToCopy = std::min(totalBytesLeftToCopy, _audioData.size() - _currentSendOffset); - } - - // Measure the loudness of this frame - _loudness = 0.0f; - for (int i = 0; i < totalBytesLeftToCopy; i += sizeof(int16_t)) { - _loudness += abs(*reinterpret_cast(_audioData.data() + ((_currentSendOffset + i) % _audioData.size()))) / - (AudioConstants::MAX_SAMPLE_VALUE / 2.0f); - } - _loudness /= (float)(totalBytesLeftToCopy/ sizeof(int16_t)); + assert(loopbackOptionOffset != -1); + assert(positionOptionOffset != -1); + assert(volumeOptionOffset != -1); + assert(audioDataOffset != -1); _currentPacket->seek(0); @@ -339,19 +322,37 @@ int64_t AudioInjector::injectNextFrame() { _currentPacket->seek(audioDataOffset); - // This code is copying bytes from the _audioData directly into the packet, handling looping appropriately. + // This code is copying bytes from the _sound directly into the packet, handling looping appropriately. // Might be a reasonable place to do the encode step here. QByteArray decodedAudio; - while (totalBytesLeftToCopy > 0) { - int bytesToCopy = std::min(totalBytesLeftToCopy, _audioData.size() - _currentSendOffset); - decodedAudio.append(_audioData.data() + _currentSendOffset, bytesToCopy); - _currentSendOffset += bytesToCopy; - totalBytesLeftToCopy -= bytesToCopy; - if (_options.loop && _currentSendOffset >= _audioData.size()) { - _currentSendOffset = 0; - } + int totalBytesLeftToCopy = (_options.stereo ? 2 : 1) * AudioConstants::NETWORK_FRAME_BYTES_PER_CHANNEL; + if (!_options.loop) { + // If we aren't looping, let's make sure we don't read past the end + int bytesLeftToRead = _audioData->getNumBytes() - _currentSendOffset; + totalBytesLeftToCopy = std::min(totalBytesLeftToCopy, bytesLeftToRead); } + + auto samples = _audioData->data(); + auto currentSample = _currentSendOffset / AudioConstants::SAMPLE_SIZE; + auto samplesLeftToCopy = totalBytesLeftToCopy / AudioConstants::SAMPLE_SIZE; + + using AudioConstants::AudioSample; + decodedAudio.resize(totalBytesLeftToCopy); + auto samplesOut = reinterpret_cast(decodedAudio.data()); + + // Copy and Measure the loudness of this frame + _loudness = 0.0f; + for (int i = 0; i < samplesLeftToCopy; ++i) { + auto index = (currentSample + i) % _audioData->getNumSamples(); + auto sample = samples[index]; + samplesOut[i] = sample; + _loudness += abs(sample) / (AudioConstants::MAX_SAMPLE_VALUE / 2.0f); + } + _loudness /= (float)samplesLeftToCopy; + _currentSendOffset = (_currentSendOffset + totalBytesLeftToCopy) % + _audioData->getNumBytes(); + // FIXME -- good place to call codec encode here. We need to figure out how to tell the AudioInjector which // codec to use... possible through AbstractAudioInterface. QByteArray encodedAudio = decodedAudio; @@ -370,7 +371,7 @@ int64_t AudioInjector::injectNextFrame() { _outgoingSequenceNumber++; } - if (_currentSendOffset >= _audioData.size() && !_options.loop) { + if (_currentSendOffset == 0 && !_options.loop) { finishNetworkInjection(); return NEXT_FRAME_DELTA_ERROR_OR_FINISHED; } @@ -390,7 +391,7 @@ int64_t AudioInjector::injectNextFrame() { // If we are falling behind by more frames than our threshold, let's skip the frames ahead qCDebug(audio) << this << "injectNextFrame() skipping ahead, fell behind by " << (currentFrameBasedOnElapsedTime - _nextFrame) << " frames"; _nextFrame = currentFrameBasedOnElapsedTime; - _currentSendOffset = _nextFrame * AudioConstants::NETWORK_FRAME_BYTES_PER_CHANNEL * (_options.stereo ? 2 : 1) % _audioData.size(); + _currentSendOffset = _nextFrame * AudioConstants::NETWORK_FRAME_BYTES_PER_CHANNEL * (_options.stereo ? 2 : 1) % _audioData->getNumBytes(); } int64_t playNextFrameAt = ++_nextFrame * AudioConstants::NETWORK_FRAME_USECS; @@ -417,38 +418,25 @@ void AudioInjector::triggerDeleteAfterFinish() { } } -AudioInjectorPointer AudioInjector::playSound(SharedSoundPointer sound, const float volume, - const float stretchFactor, const glm::vec3 position) { +AudioInjectorPointer AudioInjector::playSoundAndDelete(SharedSoundPointer sound, const AudioInjectorOptions& options) { + AudioInjectorPointer injector = playSound(sound, options); + + if (injector) { + injector->_state |= AudioInjectorState::PendingDelete; + } + + return injector; +} + + +AudioInjectorPointer AudioInjector::playSound(SharedSoundPointer sound, const AudioInjectorOptions& options) { if (!sound || !sound->isReady()) { return AudioInjectorPointer(); } - AudioInjectorOptions options; - options.stereo = sound->isStereo(); - options.position = position; - options.volume = volume; - options.pitch = 1.0f / stretchFactor; - - QByteArray samples = sound->getByteArray(); - - return playSoundAndDelete(samples, options); -} - -AudioInjectorPointer AudioInjector::playSoundAndDelete(const QByteArray& buffer, const AudioInjectorOptions options) { - AudioInjectorPointer sound = playSound(buffer, options); - - if (sound) { - sound->_state |= AudioInjectorState::PendingDelete; - } - - return sound; -} - -AudioInjectorPointer AudioInjector::playSound(const QByteArray& buffer, const AudioInjectorOptions options) { - if (options.pitch == 1.0f) { - AudioInjectorPointer injector = AudioInjectorPointer::create(buffer, options); + AudioInjectorPointer injector = AudioInjectorPointer::create(sound, options); if (!injector->inject(&AudioInjectorManager::threadInjector)) { qWarning() << "AudioInjector::playSound failed to thread injector"; @@ -456,24 +444,31 @@ AudioInjectorPointer AudioInjector::playSound(const QByteArray& buffer, const Au return injector; } else { + using AudioConstants::AudioSample; + using AudioConstants::SAMPLE_RATE; + const int standardRate = SAMPLE_RATE; + // limit to 4 octaves + const int pitch = glm::clamp(options.pitch, 1 / 16.0f, 16.0f); + const int resampledRate = SAMPLE_RATE / pitch; - const int standardRate = AudioConstants::SAMPLE_RATE; - const int resampledRate = AudioConstants::SAMPLE_RATE / glm::clamp(options.pitch, 1/16.0f, 16.0f); // limit to 4 octaves - const int numChannels = options.ambisonic ? AudioConstants::AMBISONIC : - (options.stereo ? AudioConstants::STEREO : AudioConstants::MONO); + auto audioData = sound->getAudioData(); + auto numChannels = audioData->getNumChannels(); + auto numFrames = audioData->getNumFrames(); AudioSRC resampler(standardRate, resampledRate, numChannels); // create a resampled buffer that is guaranteed to be large enough - const int nInputFrames = buffer.size() / (numChannels * sizeof(int16_t)); - const int maxOutputFrames = resampler.getMaxOutput(nInputFrames); - QByteArray resampledBuffer(maxOutputFrames * numChannels * sizeof(int16_t), '\0'); + const int maxOutputFrames = resampler.getMaxOutput(numFrames); + const int maxOutputSize = maxOutputFrames * numChannels * sizeof(AudioSample); + QByteArray resampledBuffer(maxOutputSize, '\0'); + auto bufferPtr = reinterpret_cast(resampledBuffer.data()); - resampler.render(reinterpret_cast(buffer.data()), - reinterpret_cast(resampledBuffer.data()), - nInputFrames); + resampler.render(audioData->data(), bufferPtr, numFrames); - AudioInjectorPointer injector = AudioInjectorPointer::create(resampledBuffer, options); + int numSamples = maxOutputFrames * numChannels; + auto newAudioData = AudioData::make(numSamples, numChannels, bufferPtr); + + AudioInjectorPointer injector = AudioInjectorPointer::create(newAudioData, options); if (!injector->inject(&AudioInjectorManager::threadInjector)) { qWarning() << "AudioInjector::playSound failed to thread pitch-shifted injector"; diff --git a/libraries/audio/src/AudioInjector.h b/libraries/audio/src/AudioInjector.h index fc197f7ba0..37e4cbd66c 100644 --- a/libraries/audio/src/AudioInjector.h +++ b/libraries/audio/src/AudioInjector.h @@ -52,8 +52,8 @@ AudioInjectorState& operator|= (AudioInjectorState& lhs, AudioInjectorState rhs) class AudioInjector : public QObject, public QEnableSharedFromThis { Q_OBJECT public: - AudioInjector(const Sound& sound, const AudioInjectorOptions& injectorOptions); - AudioInjector(const QByteArray& audioData, const AudioInjectorOptions& injectorOptions); + AudioInjector(SharedSoundPointer sound, const AudioInjectorOptions& injectorOptions); + AudioInjector(AudioDataPointer audioData, const AudioInjectorOptions& injectorOptions); ~AudioInjector(); bool isFinished() const { return (stateHas(AudioInjectorState::Finished)); } @@ -74,10 +74,9 @@ public: bool stateHas(AudioInjectorState state) const ; static void setLocalAudioInterface(AbstractAudioInterface* audioInterface) { _localAudioInterface = audioInterface; } - static AudioInjectorPointer playSoundAndDelete(const QByteArray& buffer, const AudioInjectorOptions options); - static AudioInjectorPointer playSound(const QByteArray& buffer, const AudioInjectorOptions options); - static AudioInjectorPointer playSound(SharedSoundPointer sound, const float volume, - const float stretchFactor, const glm::vec3 position); + + static AudioInjectorPointer playSoundAndDelete(SharedSoundPointer sound, const AudioInjectorOptions& options); + static AudioInjectorPointer playSound(SharedSoundPointer sound, const AudioInjectorOptions& options); public slots: void restart(); @@ -106,7 +105,8 @@ private: static AbstractAudioInterface* _localAudioInterface; - QByteArray _audioData; + const SharedSoundPointer _sound; + AudioDataPointer _audioData; AudioInjectorOptions _options; AudioInjectorState _state { AudioInjectorState::NotFinished }; bool _hasSentFirstFrame { false }; diff --git a/libraries/audio/src/AudioInjectorLocalBuffer.cpp b/libraries/audio/src/AudioInjectorLocalBuffer.cpp index 7834644baf..015d87e03b 100644 --- a/libraries/audio/src/AudioInjectorLocalBuffer.cpp +++ b/libraries/audio/src/AudioInjectorLocalBuffer.cpp @@ -11,13 +11,9 @@ #include "AudioInjectorLocalBuffer.h" -AudioInjectorLocalBuffer::AudioInjectorLocalBuffer(const QByteArray& rawAudioArray) : - _rawAudioArray(rawAudioArray), - _shouldLoop(false), - _isStopped(false), - _currentOffset(0) +AudioInjectorLocalBuffer::AudioInjectorLocalBuffer(AudioDataPointer audioData) : + _audioData(audioData) { - } void AudioInjectorLocalBuffer::stop() { @@ -39,7 +35,7 @@ qint64 AudioInjectorLocalBuffer::readData(char* data, qint64 maxSize) { if (!_isStopped) { // first copy to the end of the raw audio - int bytesToEnd = _rawAudioArray.size() - _currentOffset; + int bytesToEnd = (int)_audioData->getNumBytes() - _currentOffset; int bytesRead = maxSize; @@ -47,7 +43,7 @@ qint64 AudioInjectorLocalBuffer::readData(char* data, qint64 maxSize) { bytesRead = bytesToEnd; } - memcpy(data, _rawAudioArray.data() + _currentOffset, bytesRead); + memcpy(data, _audioData->rawData() + _currentOffset, bytesRead); // now check if we are supposed to loop and if we can copy more from the beginning if (_shouldLoop && maxSize != bytesRead) { @@ -56,7 +52,7 @@ qint64 AudioInjectorLocalBuffer::readData(char* data, qint64 maxSize) { _currentOffset += bytesRead; } - if (_shouldLoop && _currentOffset == _rawAudioArray.size()) { + if (_shouldLoop && _currentOffset == (int)_audioData->getNumBytes()) { _currentOffset = 0; } @@ -70,12 +66,12 @@ qint64 AudioInjectorLocalBuffer::recursiveReadFromFront(char* data, qint64 maxSi // see how much we can get in this pass int bytesRead = maxSize; - if (bytesRead > _rawAudioArray.size()) { - bytesRead = _rawAudioArray.size(); + if (bytesRead > (int)_audioData->getNumBytes()) { + bytesRead = _audioData->getNumBytes(); } // copy that amount - memcpy(data, _rawAudioArray.data(), bytesRead); + memcpy(data, _audioData->rawData(), bytesRead); // check if we need to call ourselves again and pull from the front again if (bytesRead < maxSize) { diff --git a/libraries/audio/src/AudioInjectorLocalBuffer.h b/libraries/audio/src/AudioInjectorLocalBuffer.h index 3065de5199..e0f8847883 100644 --- a/libraries/audio/src/AudioInjectorLocalBuffer.h +++ b/libraries/audio/src/AudioInjectorLocalBuffer.h @@ -16,10 +16,12 @@ #include +#include "Sound.h" + class AudioInjectorLocalBuffer : public QIODevice { Q_OBJECT public: - AudioInjectorLocalBuffer(const QByteArray& rawAudioArray); + AudioInjectorLocalBuffer(AudioDataPointer audioData); void stop(); @@ -34,11 +36,10 @@ public: private: qint64 recursiveReadFromFront(char* data, qint64 maxSize); - QByteArray _rawAudioArray; - bool _shouldLoop; - bool _isStopped; - - int _currentOffset; + AudioDataPointer _audioData; + bool _shouldLoop { false }; + bool _isStopped { false }; + int _currentOffset { 0 }; }; #endif // hifi_AudioInjectorLocalBuffer_h diff --git a/libraries/audio/src/Sound.cpp b/libraries/audio/src/Sound.cpp index 3807882b00..3cc32333f6 100644 --- a/libraries/audio/src/Sound.cpp +++ b/libraries/audio/src/Sound.cpp @@ -33,48 +33,59 @@ #include "flump3dec.h" -QScriptValue soundSharedPointerToScriptValue(QScriptEngine* engine, const SharedSoundPointer& in) { - return engine->newQObject(new SoundScriptingInterface(in), QScriptEngine::ScriptOwnership); +int audioDataPointerMetaTypeID = qRegisterMetaType("AudioDataPointer"); + +using AudioConstants::AudioSample; + +AudioDataPointer AudioData::make(uint32_t numSamples, uint32_t numChannels, + const AudioSample* samples) { + // Compute the amount of memory required for the audio data object + const size_t bufferSize = numSamples * sizeof(AudioSample); + const size_t memorySize = sizeof(AudioData) + bufferSize; + + // Allocate the memory for the audio data object and the buffer + void* memory = ::malloc(memorySize); + auto audioData = reinterpret_cast(memory); + auto buffer = reinterpret_cast(audioData + 1); + assert(((char*)buffer - (char*)audioData) == sizeof(AudioData)); + + // Use placement new to construct the audio data object at the memory allocated + ::new(audioData) AudioData(numSamples, numChannels, buffer); + + // Copy the samples to the buffer + memcpy(buffer, samples, bufferSize); + + // Return shared_ptr that properly destruct the object and release the memory + return AudioDataPointer(audioData, [](AudioData* ptr) { + ptr->~AudioData(); + ::free(ptr); + }); } -void soundSharedPointerFromScriptValue(const QScriptValue& object, SharedSoundPointer& out) { - if (auto soundInterface = qobject_cast(object.toQObject())) { - out = soundInterface->getSound(); - } -} -SoundScriptingInterface::SoundScriptingInterface(const SharedSoundPointer& sound) : _sound(sound) { - // During shutdown we can sometimes get an empty sound pointer back - if (_sound) { - QObject::connect(_sound.data(), &Sound::ready, this, &SoundScriptingInterface::ready); - } -} - -Sound::Sound(const QUrl& url, bool isStereo, bool isAmbisonic) : - Resource(url), - _isStereo(isStereo), - _isAmbisonic(isAmbisonic), - _isReady(false) -{ -} +AudioData::AudioData(uint32_t numSamples, uint32_t numChannels, const AudioSample* samples) + : _numSamples(numSamples), + _numChannels(numChannels), + _data(samples) +{} void Sound::downloadFinished(const QByteArray& data) { + if (!_self) { + soundProcessError(301, "Sound object has gone out of scope"); + return; + } + // this is a QRunnable, will delete itself after it has finished running - SoundProcessor* soundProcessor = new SoundProcessor(_url, data, _isStereo, _isAmbisonic); + auto soundProcessor = new SoundProcessor(_self, data); connect(soundProcessor, &SoundProcessor::onSuccess, this, &Sound::soundProcessSuccess); connect(soundProcessor, &SoundProcessor::onError, this, &Sound::soundProcessError); QThreadPool::globalInstance()->start(soundProcessor); } -void Sound::soundProcessSuccess(QByteArray data, bool stereo, bool ambisonic, float duration) { +void Sound::soundProcessSuccess(AudioDataPointer audioData) { + qCDebug(audio) << "Setting ready state for sound file" << _url.fileName(); - qCDebug(audio) << "Setting ready state for sound file"; - - _byteArray = data; - _isStereo = stereo; - _isAmbisonic = ambisonic; - _duration = duration; - _isReady = true; + _audioData = std::move(audioData); finishedLoading(true); emit ready(); @@ -86,91 +97,102 @@ void Sound::soundProcessError(int error, QString str) { finishedLoading(false); } -void SoundProcessor::run() { - qCDebug(audio) << "Processing sound file"; +SoundProcessor::SoundProcessor(QWeakPointer sound, QByteArray data) : + _sound(sound), + _data(data) +{ +} + +void SoundProcessor::run() { + auto sound = qSharedPointerCast(_sound.lock()); + if (!sound) { + emit onError(301, "Sound object has gone out of scope"); + return; + } + + QString fileName = sound->getURL().fileName().toLower(); + qCDebug(audio) << "Processing sound file" << fileName; // replace our byte array with the downloaded data - QByteArray rawAudioByteArray = QByteArray(_data); - QString fileName = _url.fileName().toLower(); static const QString WAV_EXTENSION = ".wav"; static const QString MP3_EXTENSION = ".mp3"; static const QString RAW_EXTENSION = ".raw"; + static const QString STEREO_RAW_EXTENSION = ".stereo.raw"; + QString fileType; + + QByteArray outputAudioByteArray; + AudioProperties properties; if (fileName.endsWith(WAV_EXTENSION)) { - - QByteArray outputAudioByteArray; - - int sampleRate = interpretAsWav(rawAudioByteArray, outputAudioByteArray); - if (sampleRate == 0) { - qCWarning(audio) << "Unsupported WAV file type"; - emit onError(300, "Failed to load sound file, reason: unsupported WAV file type"); - return; - } - - downSample(outputAudioByteArray, sampleRate); - + fileType = "WAV"; + properties = interpretAsWav(_data, outputAudioByteArray); } else if (fileName.endsWith(MP3_EXTENSION)) { - - QByteArray outputAudioByteArray; - - int sampleRate = interpretAsMP3(rawAudioByteArray, outputAudioByteArray); - if (sampleRate == 0) { - qCWarning(audio) << "Unsupported MP3 file type"; - emit onError(300, "Failed to load sound file, reason: unsupported MP3 file type"); - return; - } - - downSample(outputAudioByteArray, sampleRate); - - } else if (fileName.endsWith(RAW_EXTENSION)) { + fileType = "MP3"; + properties = interpretAsMP3(_data, outputAudioByteArray); + } else if (fileName.endsWith(STEREO_RAW_EXTENSION)) { // check if this was a stereo raw file // since it's raw the only way for us to know that is if the file was called .stereo.raw - if (fileName.toLower().endsWith("stereo.raw")) { - _isStereo = true; - qCDebug(audio) << "Processing sound of" << rawAudioByteArray.size() << "bytes as stereo audio file."; - } - + qCDebug(audio) << "Processing sound of" << _data.size() << "bytes from" << fileName << "as stereo audio file."; // Process as 48khz RAW file - downSample(rawAudioByteArray, 48000); - + properties.numChannels = 2; + properties.sampleRate = 48000; + outputAudioByteArray = _data; + } else if (fileName.endsWith(RAW_EXTENSION)) { + // Process as 48khz RAW file + properties.numChannels = 1; + properties.sampleRate = 48000; + outputAudioByteArray = _data; } else { qCWarning(audio) << "Unknown sound file type"; emit onError(300, "Failed to load sound file, reason: unknown sound file type"); return; } - emit onSuccess(_data, _isStereo, _isAmbisonic, _duration); + if (properties.sampleRate == 0) { + qCWarning(audio) << "Unsupported" << fileType << "file type"; + emit onError(300, "Failed to load sound file, reason: unsupported " + fileType + " file type"); + return; + } + + auto data = downSample(outputAudioByteArray, properties); + + int numSamples = data.size() / AudioConstants::SAMPLE_SIZE; + auto audioData = AudioData::make(numSamples, properties.numChannels, + (const AudioSample*)data.constData()); + emit onSuccess(audioData); } -void SoundProcessor::downSample(const QByteArray& rawAudioByteArray, int sampleRate) { +QByteArray SoundProcessor::downSample(const QByteArray& rawAudioByteArray, + AudioProperties properties) { // we want to convert it to the format that the audio-mixer wants // which is signed, 16-bit, 24Khz - if (sampleRate == AudioConstants::SAMPLE_RATE) { + if (properties.sampleRate == AudioConstants::SAMPLE_RATE) { // no resampling needed - _data = rawAudioByteArray; - } else { - - int numChannels = _isAmbisonic ? AudioConstants::AMBISONIC : (_isStereo ? AudioConstants::STEREO : AudioConstants::MONO); - AudioSRC resampler(sampleRate, AudioConstants::SAMPLE_RATE, numChannels); - - // resize to max possible output - int numSourceFrames = rawAudioByteArray.size() / (numChannels * sizeof(AudioConstants::AudioSample)); - int maxDestinationFrames = resampler.getMaxOutput(numSourceFrames); - int maxDestinationBytes = maxDestinationFrames * numChannels * sizeof(AudioConstants::AudioSample); - _data.resize(maxDestinationBytes); - - int numDestinationFrames = resampler.render((int16_t*)rawAudioByteArray.data(), - (int16_t*)_data.data(), - numSourceFrames); - - // truncate to actual output - int numDestinationBytes = numDestinationFrames * numChannels * sizeof(AudioConstants::AudioSample); - _data.resize(numDestinationBytes); + return rawAudioByteArray; } + + AudioSRC resampler(properties.sampleRate, AudioConstants::SAMPLE_RATE, + properties.numChannels); + + // resize to max possible output + int numSourceFrames = rawAudioByteArray.size() / (properties.numChannels * AudioConstants::SAMPLE_SIZE); + int maxDestinationFrames = resampler.getMaxOutput(numSourceFrames); + int maxDestinationBytes = maxDestinationFrames * properties.numChannels * AudioConstants::SAMPLE_SIZE; + QByteArray data(maxDestinationBytes, Qt::Uninitialized); + + int numDestinationFrames = resampler.render((int16_t*)rawAudioByteArray.data(), + (int16_t*)data.data(), + numSourceFrames); + + // truncate to actual output + int numDestinationBytes = numDestinationFrames * properties.numChannels * sizeof(AudioSample); + data.resize(numDestinationBytes); + + return data; } // @@ -218,7 +240,9 @@ struct WAVEFormat { }; // returns wavfile sample rate, used for resampling -int SoundProcessor::interpretAsWav(const QByteArray& inputAudioByteArray, QByteArray& outputAudioByteArray) { +SoundProcessor::AudioProperties SoundProcessor::interpretAsWav(const QByteArray& inputAudioByteArray, + QByteArray& outputAudioByteArray) { + AudioProperties properties; // Create a data stream to analyze the data QDataStream waveStream(const_cast(&inputAudioByteArray), QIODevice::ReadOnly); @@ -227,7 +251,7 @@ int SoundProcessor::interpretAsWav(const QByteArray& inputAudioByteArray, QByteA RIFFHeader riff; if (waveStream.readRawData((char*)&riff, sizeof(RIFFHeader)) != sizeof(RIFFHeader)) { qCWarning(audio) << "Not a valid WAVE file."; - return 0; + return AudioProperties(); } // Parse the "RIFF" chunk @@ -235,11 +259,11 @@ int SoundProcessor::interpretAsWav(const QByteArray& inputAudioByteArray, QByteA waveStream.setByteOrder(QDataStream::LittleEndian); } else { qCWarning(audio) << "Currently not supporting big-endian audio files."; - return 0; + return AudioProperties(); } if (strncmp(riff.type, "WAVE", 4) != 0) { qCWarning(audio) << "Not a valid WAVE file."; - return 0; + return AudioProperties(); } // Read chunks until the "fmt " chunk is found @@ -247,7 +271,7 @@ int SoundProcessor::interpretAsWav(const QByteArray& inputAudioByteArray, QByteA while (true) { if (waveStream.readRawData((char*)&fmt, sizeof(chunk)) != sizeof(chunk)) { qCWarning(audio) << "Not a valid WAVE file."; - return 0; + return AudioProperties(); } if (strncmp(fmt.id, "fmt ", 4) == 0) { break; @@ -259,26 +283,26 @@ int SoundProcessor::interpretAsWav(const QByteArray& inputAudioByteArray, QByteA WAVEFormat wave; if (waveStream.readRawData((char*)&wave, sizeof(WAVEFormat)) != sizeof(WAVEFormat)) { qCWarning(audio) << "Not a valid WAVE file."; - return 0; + return AudioProperties(); } // Parse the "fmt " chunk if (qFromLittleEndian(wave.audioFormat) != WAVEFORMAT_PCM && qFromLittleEndian(wave.audioFormat) != WAVEFORMAT_EXTENSIBLE) { qCWarning(audio) << "Currently not supporting non PCM audio files."; - return 0; + return AudioProperties(); } - if (qFromLittleEndian(wave.numChannels) == 2) { - _isStereo = true; - } else if (qFromLittleEndian(wave.numChannels) == 4) { - _isAmbisonic = true; - } else if (qFromLittleEndian(wave.numChannels) != 1) { + + properties.numChannels = qFromLittleEndian(wave.numChannels); + if (properties.numChannels != 1 && + properties.numChannels != 2 && + properties.numChannels != 4) { qCWarning(audio) << "Currently not supporting audio files with other than 1/2/4 channels."; - return 0; + return AudioProperties(); } if (qFromLittleEndian(wave.bitsPerSample) != 16) { qCWarning(audio) << "Currently not supporting non 16bit audio files."; - return 0; + return AudioProperties(); } // Skip any extra data in the "fmt " chunk @@ -289,7 +313,7 @@ int SoundProcessor::interpretAsWav(const QByteArray& inputAudioByteArray, QByteA while (true) { if (waveStream.readRawData((char*)&data, sizeof(chunk)) != sizeof(chunk)) { qCWarning(audio) << "Not a valid WAVE file."; - return 0; + return AudioProperties(); } if (strncmp(data.id, "data", 4) == 0) { break; @@ -300,17 +324,21 @@ int SoundProcessor::interpretAsWav(const QByteArray& inputAudioByteArray, QByteA // Read the "data" chunk quint32 outputAudioByteArraySize = qFromLittleEndian(data.size); outputAudioByteArray.resize(outputAudioByteArraySize); - if (waveStream.readRawData(outputAudioByteArray.data(), outputAudioByteArraySize) != (int)outputAudioByteArraySize) { + auto bytesRead = waveStream.readRawData(outputAudioByteArray.data(), outputAudioByteArraySize); + if (bytesRead != (int)outputAudioByteArraySize) { qCWarning(audio) << "Error reading WAV file"; - return 0; + return AudioProperties(); } - _duration = (float)(outputAudioByteArraySize / (wave.sampleRate * wave.numChannels * wave.bitsPerSample / 8.0f)); - return wave.sampleRate; + properties.sampleRate = wave.sampleRate; + return properties; } // returns MP3 sample rate, used for resampling -int SoundProcessor::interpretAsMP3(const QByteArray& inputAudioByteArray, QByteArray& outputAudioByteArray) { +SoundProcessor::AudioProperties SoundProcessor::interpretAsMP3(const QByteArray& inputAudioByteArray, + QByteArray& outputAudioByteArray) { + AudioProperties properties; + using namespace flump3dec; static const int MP3_SAMPLES_MAX = 1152; @@ -321,21 +349,19 @@ int SoundProcessor::interpretAsMP3(const QByteArray& inputAudioByteArray, QByteA // create bitstream Bit_stream_struc *bitstream = bs_new(); if (bitstream == nullptr) { - return 0; + return AudioProperties(); } // create decoder mp3tl *decoder = mp3tl_new(bitstream, MP3TL_MODE_16BIT); if (decoder == nullptr) { bs_free(bitstream); - return 0; + return AudioProperties(); } // initialize bs_set_data(bitstream, (uint8_t*)inputAudioByteArray.data(), inputAudioByteArray.size()); int frameCount = 0; - int sampleRate = 0; - int numChannels = 0; // skip ID3 tag, if present Mp3TlRetcode result = mp3tl_skip_id3(decoder); @@ -357,8 +383,8 @@ int SoundProcessor::interpretAsMP3(const QByteArray& inputAudioByteArray, QByteA << "channels =" << header->channels; // save header info - sampleRate = header->sample_rate; - numChannels = header->channels; + properties.sampleRate = header->sample_rate; + properties.numChannels = header->channels; // skip Xing header, if present result = mp3tl_skip_xing(decoder, header); @@ -388,14 +414,32 @@ int SoundProcessor::interpretAsMP3(const QByteArray& inputAudioByteArray, QByteA // free bitstream bs_free(bitstream); - int outputAudioByteArraySize = outputAudioByteArray.size(); - if (outputAudioByteArraySize == 0) { + if (outputAudioByteArray.isEmpty()) { qCWarning(audio) << "Error decoding MP3 file"; - return 0; + return AudioProperties(); } - _isStereo = (numChannels == 2); - _isAmbisonic = false; - _duration = (float)outputAudioByteArraySize / (sampleRate * numChannels * sizeof(int16_t)); - return sampleRate; + return properties; +} + + +QScriptValue soundSharedPointerToScriptValue(QScriptEngine* engine, const SharedSoundPointer& in) { + return engine->newQObject(new SoundScriptingInterface(in), QScriptEngine::ScriptOwnership); +} + +void soundSharedPointerFromScriptValue(const QScriptValue& object, SharedSoundPointer& out) { + if (auto soundInterface = qobject_cast(object.toQObject())) { + out = soundInterface->getSound(); + } +} + +SoundScriptingInterface::SoundScriptingInterface(const SharedSoundPointer& sound) : _sound(sound) { + // During shutdown we can sometimes get an empty sound pointer back + if (_sound) { + QObject::connect(_sound.data(), &Sound::ready, this, &SoundScriptingInterface::ready); + } +} + +Sound::Sound(const QUrl& url, bool isStereo, bool isAmbisonic) : Resource(url) { + _numChannels = isAmbisonic ? 4 : (isStereo ? 2 : 1); } diff --git a/libraries/audio/src/Sound.h b/libraries/audio/src/Sound.h index a0544870d0..36bc9c72ac 100644 --- a/libraries/audio/src/Sound.h +++ b/libraries/audio/src/Sound.h @@ -19,61 +19,102 @@ #include +#include "AudioConstants.h" + +class AudioData; +using AudioDataPointer = std::shared_ptr; + +Q_DECLARE_METATYPE(AudioDataPointer); + +// AudioData is designed to be immutable +// All of its members and methods are const +// This makes it perfectly safe to access from multiple threads at once +class AudioData { +public: + using AudioSample = AudioConstants::AudioSample; + + // Allocates the buffer memory contiguous with the object + static AudioDataPointer make(uint32_t numSamples, uint32_t numChannels, + const AudioSample* samples); + + AudioData(uint32_t numSamples, uint32_t numChannels, const AudioSample* samples); + + uint32_t getNumSamples() const { return _numSamples; } + uint32_t getNumChannels() const { return _numChannels; } + const AudioSample* data() const { return _data; } + const char* rawData() const { return reinterpret_cast(_data); } + + float isStereo() const { return _numChannels == 2; } + float isAmbisonic() const { return _numChannels == 4; } + float getDuration() const { return (float)_numSamples / (_numChannels * AudioConstants::SAMPLE_RATE); } + uint32_t getNumFrames() const { return _numSamples / _numChannels; } + uint32_t getNumBytes() const { return _numSamples * sizeof(AudioSample); } + +private: + const uint32_t _numSamples { 0 }; + const uint32_t _numChannels { 0 }; + const AudioSample* const _data { nullptr }; +}; + class Sound : public Resource { Q_OBJECT public: Sound(const QUrl& url, bool isStereo = false, bool isAmbisonic = false); - bool isStereo() const { return _isStereo; } - bool isAmbisonic() const { return _isAmbisonic; } - bool isReady() const { return _isReady; } - float getDuration() const { return _duration; } - - const QByteArray& getByteArray() const { return _byteArray; } + bool isReady() const { return (bool)_audioData; } + + bool isStereo() const { return _audioData ? _audioData->isStereo() : false; } + bool isAmbisonic() const { return _audioData ? _audioData->isAmbisonic() : false; } + float getDuration() const { return _audioData ? _audioData->getDuration() : 0.0f; } + + AudioDataPointer getAudioData() const { return _audioData; } + + int getNumChannels() const { return _numChannels; } signals: void ready(); protected slots: - void soundProcessSuccess(QByteArray data, bool stereo, bool ambisonic, float duration); + void soundProcessSuccess(AudioDataPointer audioData); void soundProcessError(int error, QString str); private: - QByteArray _byteArray; - bool _isStereo; - bool _isAmbisonic; - bool _isReady; - float _duration; // In seconds - virtual void downloadFinished(const QByteArray& data) override; + + AudioDataPointer _audioData; + + // Only used for caching until the download has finished + int _numChannels { 0 }; }; class SoundProcessor : public QObject, public QRunnable { Q_OBJECT public: - SoundProcessor(const QUrl& url, const QByteArray& data, bool stereo, bool ambisonic) - : _url(url), _data(data), _isStereo(stereo), _isAmbisonic(ambisonic) - { - } + struct AudioProperties { + uint8_t numChannels { 0 }; + uint32_t sampleRate { 0 }; + }; + + SoundProcessor(QWeakPointer sound, QByteArray data); virtual void run() override; - void downSample(const QByteArray& rawAudioByteArray, int sampleRate); - int interpretAsWav(const QByteArray& inputAudioByteArray, QByteArray& outputAudioByteArray); - int interpretAsMP3(const QByteArray& inputAudioByteArray, QByteArray& outputAudioByteArray); + QByteArray downSample(const QByteArray& rawAudioByteArray, + AudioProperties properties); + AudioProperties interpretAsWav(const QByteArray& inputAudioByteArray, + QByteArray& outputAudioByteArray); + AudioProperties interpretAsMP3(const QByteArray& inputAudioByteArray, + QByteArray& outputAudioByteArray); signals: - void onSuccess(QByteArray data, bool stereo, bool ambisonic, float duration); + void onSuccess(AudioDataPointer audioData); void onError(int error, QString str); private: - QUrl _url; - QByteArray _data; - bool _isStereo; - bool _isAmbisonic; - float _duration; + const QWeakPointer _sound; + const QByteArray _data; }; typedef QSharedPointer SharedSoundPointer; diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index 6e82d26f29..980ff8834c 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -1031,7 +1031,14 @@ void EntityTreeRenderer::playEntityCollisionSound(const EntityItemPointer& entit // Shift the pitch down by ln(1 + (size / COLLISION_SIZE_FOR_STANDARD_PITCH)) / ln(2) const float COLLISION_SIZE_FOR_STANDARD_PITCH = 0.2f; const float stretchFactor = logf(1.0f + (minAACube.getLargestDimension() / COLLISION_SIZE_FOR_STANDARD_PITCH)) / logf(2.0f); - AudioInjector::playSound(collisionSound, volume, stretchFactor, collision.contactPoint); + + AudioInjectorOptions options; + options.stereo = collisionSound->isStereo(); + options.position = collision.contactPoint; + options.volume = volume; + options.pitch = 1.0f / stretchFactor; + + AudioInjector::playSoundAndDelete(collisionSound, options); } void EntityTreeRenderer::entityCollisionWithEntity(const EntityItemID& idA, const EntityItemID& idB, diff --git a/libraries/script-engine/src/AudioScriptingInterface.cpp b/libraries/script-engine/src/AudioScriptingInterface.cpp index 72918e33f6..0c4a593487 100644 --- a/libraries/script-engine/src/AudioScriptingInterface.cpp +++ b/libraries/script-engine/src/AudioScriptingInterface.cpp @@ -63,15 +63,15 @@ ScriptAudioInjector* AudioScriptingInterface::playSound(SharedSoundPointer sound optionsCopy.ambisonic = sound->isAmbisonic(); optionsCopy.localOnly = optionsCopy.localOnly || sound->isAmbisonic(); // force localOnly when Ambisonic - auto injector = AudioInjector::playSound(sound->getByteArray(), optionsCopy); + auto injector = AudioInjector::playSound(sound, optionsCopy); if (!injector) { - return NULL; + return nullptr; } return new ScriptAudioInjector(injector); } else { qCDebug(scriptengine) << "AudioScriptingInterface::playSound called with null Sound object."; - return NULL; + return nullptr; } } diff --git a/libraries/script-engine/src/ScriptEngine.h b/libraries/script-engine/src/ScriptEngine.h index 17afd3dbbd..fe8396bc50 100644 --- a/libraries/script-engine/src/ScriptEngine.h +++ b/libraries/script-engine/src/ScriptEngine.h @@ -766,8 +766,8 @@ protected: */ Q_INVOKABLE void entityScriptContentAvailable(const EntityItemID& entityID, const QString& scriptOrURL, const QString& contents, bool isURL, bool success, const QString& status); - EntityItemID currentEntityIdentifier {}; // Contains the defining entity script entity id during execution, if any. Empty for interface script execution. - QUrl currentSandboxURL {}; // The toplevel url string for the entity script that loaded the code being executed, else empty. + EntityItemID currentEntityIdentifier; // Contains the defining entity script entity id during execution, if any. Empty for interface script execution. + QUrl currentSandboxURL; // The toplevel url string for the entity script that loaded the code being executed, else empty. void doWithEnvironment(const EntityItemID& entityID, const QUrl& sandboxURL, std::function operation); void callWithEnvironment(const EntityItemID& entityID, const QUrl& sandboxURL, QScriptValue function, QScriptValue thisObject, QScriptValueList args); diff --git a/libraries/ui/src/ui/TabletScriptingInterface.cpp b/libraries/ui/src/ui/TabletScriptingInterface.cpp index 52d359ad0d..8fc2d069ec 100644 --- a/libraries/ui/src/ui/TabletScriptingInterface.cpp +++ b/libraries/ui/src/ui/TabletScriptingInterface.cpp @@ -211,7 +211,7 @@ void TabletScriptingInterface::playSound(TabletAudioEvents aEvent) { options.ambisonic = sound->isAmbisonic(); options.localOnly = true; - AudioInjectorPointer injector = AudioInjector::playSoundAndDelete(sound->getByteArray(), options); + AudioInjectorPointer injector = AudioInjector::playSoundAndDelete(sound, options); } } diff --git a/libraries/ui/src/ui/types/SoundEffect.cpp b/libraries/ui/src/ui/types/SoundEffect.cpp index e35d009ef6..dc2328b33e 100644 --- a/libraries/ui/src/ui/types/SoundEffect.cpp +++ b/libraries/ui/src/ui/types/SoundEffect.cpp @@ -37,7 +37,6 @@ void SoundEffect::play(QVariant position) { _injector->setOptions(options); _injector->restart(); } else { - QByteArray samples = _sound->getByteArray(); - _injector = AudioInjector::playSound(samples, options); + _injector = AudioInjector::playSound(_sound, options); } } From d2a1bb4806a26f9cb3d3e5249814a2633ba4b272 Mon Sep 17 00:00:00 2001 From: Clement Date: Thu, 8 Nov 2018 11:29:20 -0800 Subject: [PATCH 238/286] CR --- libraries/audio/src/Sound.cpp | 7 +++---- libraries/audio/src/Sound.h | 4 ++-- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/libraries/audio/src/Sound.cpp b/libraries/audio/src/Sound.cpp index 3cc32333f6..7c5dd3813b 100644 --- a/libraries/audio/src/Sound.cpp +++ b/libraries/audio/src/Sound.cpp @@ -111,11 +111,10 @@ void SoundProcessor::run() { return; } - QString fileName = sound->getURL().fileName().toLower(); + auto url = sound->getURL(); + QString fileName = url.fileName().toLower(); qCDebug(audio) << "Processing sound file" << fileName; - // replace our byte array with the downloaded data - static const QString WAV_EXTENSION = ".wav"; static const QString MP3_EXTENSION = ".mp3"; static const QString RAW_EXTENSION = ".raw"; @@ -165,7 +164,7 @@ void SoundProcessor::run() { } QByteArray SoundProcessor::downSample(const QByteArray& rawAudioByteArray, - AudioProperties properties) { + AudioProperties properties) { // we want to convert it to the format that the audio-mixer wants // which is signed, 16-bit, 24Khz diff --git a/libraries/audio/src/Sound.h b/libraries/audio/src/Sound.h index 36bc9c72ac..836e28d582 100644 --- a/libraries/audio/src/Sound.h +++ b/libraries/audio/src/Sound.h @@ -37,8 +37,6 @@ public: static AudioDataPointer make(uint32_t numSamples, uint32_t numChannels, const AudioSample* samples); - AudioData(uint32_t numSamples, uint32_t numChannels, const AudioSample* samples); - uint32_t getNumSamples() const { return _numSamples; } uint32_t getNumChannels() const { return _numChannels; } const AudioSample* data() const { return _data; } @@ -51,6 +49,8 @@ public: uint32_t getNumBytes() const { return _numSamples * sizeof(AudioSample); } private: + AudioData(uint32_t numSamples, uint32_t numChannels, const AudioSample* samples); + const uint32_t _numSamples { 0 }; const uint32_t _numChannels { 0 }; const AudioSample* const _data { nullptr }; From 7f910a38e3810608ae7976e657b57e38c8382bf1 Mon Sep 17 00:00:00 2001 From: Clement Date: Mon, 12 Nov 2018 16:06:22 -0800 Subject: [PATCH 239/286] Update keyboard injector to new interface --- interface/src/ui/Keyboard.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/ui/Keyboard.cpp b/interface/src/ui/Keyboard.cpp index 773253f85c..5f83c095c8 100644 --- a/interface/src/ui/Keyboard.cpp +++ b/interface/src/ui/Keyboard.cpp @@ -485,7 +485,7 @@ void Keyboard::handleTriggerBegin(const OverlayID& overlayID, const PointerEvent audioOptions.position = keyWorldPosition; audioOptions.volume = 0.1f; - AudioInjector::playSound(_keySound->getByteArray(), audioOptions); + AudioInjector::playSoundAndDelete(_keySound, audioOptions); int scanCode = key.getScanCode(_capsEnabled); QString keyString = key.getKeyString(_capsEnabled); From f17723c216520c481b30db69676fe57c35b71d54 Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Wed, 14 Nov 2018 14:50:38 -0800 Subject: [PATCH 240/286] disable interstitial for master --- libraries/networking/src/DomainHandler.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/networking/src/DomainHandler.h b/libraries/networking/src/DomainHandler.h index c0c5a4d059..620ffb9641 100644 --- a/libraries/networking/src/DomainHandler.h +++ b/libraries/networking/src/DomainHandler.h @@ -234,7 +234,7 @@ private: #ifdef Q_OS_ANDROID Setting::Handle _enableInterstitialMode{ "enableInterstitialMode", false }; #else - Setting::Handle _enableInterstitialMode { "enableInterstitialMode", true }; + Setting::Handle _enableInterstitialMode { "enableInterstitialMode", false }; #endif QSet _domainConnectionRefusals; From b7c567af08c88e16acfb7797502d9dafb2ef4162 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Wed, 14 Nov 2018 15:28:22 -0800 Subject: [PATCH 241/286] Fix procedurals using the iGlobalTime input --- libraries/procedural/src/procedural/ProceduralCommon.slh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/libraries/procedural/src/procedural/ProceduralCommon.slh b/libraries/procedural/src/procedural/ProceduralCommon.slh index 64a4fdd1f9..d515a79e22 100644 --- a/libraries/procedural/src/procedural/ProceduralCommon.slh +++ b/libraries/procedural/src/procedural/ProceduralCommon.slh @@ -32,8 +32,10 @@ LAYOUT_STD140(binding=0) uniform standardInputsBuffer { vec4 date; // Offset 16, acts as vec4 for alignment purposes vec3 worldPosition; - // Offset 32, acts as vec4 for alignment purposes + // Offset 32, acts as vec4 for alignment purposes (but not packing purposes) vec3 worldScale; + // We need this float here to keep globalTime from getting pulled to offset 44 + float _spare0; // Offset 48 float globalTime; // Offset 52 From 31033ad68589658ead1e75f3293b589755ff744f Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Tue, 13 Nov 2018 12:12:48 -0800 Subject: [PATCH 242/286] Update TTS audio API use --- .../src/scripting/TTSScriptingInterface.cpp | 6 ++- libraries/audio/src/AudioInjector.cpp | 46 +++++++++++++++++++ libraries/audio/src/AudioInjector.h | 2 + 3 files changed, 53 insertions(+), 1 deletion(-) diff --git a/interface/src/scripting/TTSScriptingInterface.cpp b/interface/src/scripting/TTSScriptingInterface.cpp index 6b1677aecb..6589769ece 100644 --- a/interface/src/scripting/TTSScriptingInterface.cpp +++ b/interface/src/scripting/TTSScriptingInterface.cpp @@ -147,7 +147,11 @@ void TTSScriptingInterface::speakText(const QString& textToSpeak) { _lastSoundAudioInjectorUpdateTimer.stop(); } - _lastSoundAudioInjector = AudioInjector::playSoundAndDelete(_lastSoundByteArray, options); + uint32_t numChannels = 1; + uint32_t numSamples = (uint32_t)_lastSoundByteArray.size() / sizeof(AudioData::AudioSample); + auto samples = reinterpret_cast(_lastSoundByteArray.data()); + auto newAudioData = AudioData::make(numSamples, numChannels, samples); + _lastSoundAudioInjector = AudioInjector::playSoundAndDelete(newAudioData, options); _lastSoundAudioInjectorUpdateTimer.start(INJECTOR_INTERVAL_MS); #else diff --git a/libraries/audio/src/AudioInjector.cpp b/libraries/audio/src/AudioInjector.cpp index efd823c5b1..4af6e79caf 100644 --- a/libraries/audio/src/AudioInjector.cpp +++ b/libraries/audio/src/AudioInjector.cpp @@ -476,3 +476,49 @@ AudioInjectorPointer AudioInjector::playSound(SharedSoundPointer sound, const A return injector; } } + +AudioInjectorPointer AudioInjector::playSoundAndDelete(AudioDataPointer audioData, const AudioInjectorOptions& options) { + AudioInjectorPointer injector = playSound(audioData, options); + + if (injector) { + injector->_state |= AudioInjectorState::PendingDelete; + } + + return injector; +} + +AudioInjectorPointer AudioInjector::playSound(AudioDataPointer audioData, const AudioInjectorOptions& options) { + if (options.pitch == 1.0f) { + AudioInjectorPointer injector = AudioInjectorPointer::create(audioData, options); + + if (!injector->inject(&AudioInjectorManager::threadInjector)) { + qWarning() << "AudioInjector::playSound failed to thread pitch-shifted injector"; + } + return injector; + } else { + using AudioConstants::AudioSample; + using AudioConstants::SAMPLE_RATE; + const int standardRate = SAMPLE_RATE; + // limit to 4 octaves + const int pitch = glm::clamp(options.pitch, 1 / 16.0f, 16.0f); + const int resampledRate = SAMPLE_RATE / pitch; + + auto numChannels = audioData->getNumChannels(); + auto numFrames = audioData->getNumFrames(); + + AudioSRC resampler(standardRate, resampledRate, numChannels); + + // create a resampled buffer that is guaranteed to be large enough + const int maxOutputFrames = resampler.getMaxOutput(numFrames); + const int maxOutputSize = maxOutputFrames * numChannels * sizeof(AudioSample); + QByteArray resampledBuffer(maxOutputSize, '\0'); + auto bufferPtr = reinterpret_cast(resampledBuffer.data()); + + resampler.render(audioData->data(), bufferPtr, numFrames); + + int numSamples = maxOutputFrames * numChannels; + auto newAudioData = AudioData::make(numSamples, numChannels, bufferPtr); + + return AudioInjector::playSound(newAudioData, options); + } +} \ No newline at end of file diff --git a/libraries/audio/src/AudioInjector.h b/libraries/audio/src/AudioInjector.h index 37e4cbd66c..49faa61b91 100644 --- a/libraries/audio/src/AudioInjector.h +++ b/libraries/audio/src/AudioInjector.h @@ -77,6 +77,8 @@ public: static AudioInjectorPointer playSoundAndDelete(SharedSoundPointer sound, const AudioInjectorOptions& options); static AudioInjectorPointer playSound(SharedSoundPointer sound, const AudioInjectorOptions& options); + static AudioInjectorPointer playSoundAndDelete(AudioDataPointer audioData, const AudioInjectorOptions& options); + static AudioInjectorPointer playSound(AudioDataPointer audioData, const AudioInjectorOptions& options); public slots: void restart(); From dfed7976826d5faf700c18ed50aaab32474fa5ad Mon Sep 17 00:00:00 2001 From: David Back Date: Wed, 14 Nov 2018 15:44:30 -0800 Subject: [PATCH 243/286] set more min/max/steps --- scripts/system/html/js/entityProperties.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/scripts/system/html/js/entityProperties.js b/scripts/system/html/js/entityProperties.js index ae5748cbc8..5bb130b124 100644 --- a/scripts/system/html/js/entityProperties.js +++ b/scripts/system/html/js/entityProperties.js @@ -1264,6 +1264,7 @@ const GROUPS = [ label: "Linear Velocity", type: "vec3", vec3Type: "xyz", + step: 0.1, decimals: 4, subLabels: [ "x", "y", "z" ], unit: "m/s", @@ -1272,6 +1273,9 @@ const GROUPS = [ { label: "Linear Damping", type: "number", + min: 0, + max: 1, + step: 0.01, decimals: 2, propertyID: "damping", }, @@ -1326,6 +1330,7 @@ const GROUPS = [ type: "vec3", vec3Type: "xyz", subLabels: [ "x", "y", "z" ], + step: 0.1, decimals: 4, unit: "m/s2", propertyID: "gravity", From 8256c652fc7dd2d919f3e957659970fce41df23e Mon Sep 17 00:00:00 2001 From: amantley Date: Wed, 14 Nov 2018 17:02:25 -0800 Subject: [PATCH 244/286] added the hook to put the copy of the mutated bind poses into tthe skeleton --- libraries/animation/src/AnimSkeleton.cpp | 19 +++++++++++++---- libraries/animation/src/AnimSkeleton.h | 2 ++ libraries/fbx/src/FBXReader.cpp | 2 +- .../render-utils/src/CauterizedModel.cpp | 21 +++++++++++++++---- libraries/render-utils/src/Model.cpp | 10 +++++++-- .../render-utils/src/SoftAttachmentModel.cpp | 7 ++++--- 6 files changed, 47 insertions(+), 14 deletions(-) diff --git a/libraries/animation/src/AnimSkeleton.cpp b/libraries/animation/src/AnimSkeleton.cpp index a9dab701ed..d9c90a58cc 100644 --- a/libraries/animation/src/AnimSkeleton.cpp +++ b/libraries/animation/src/AnimSkeleton.cpp @@ -27,21 +27,32 @@ AnimSkeleton::AnimSkeleton(const HFMModel& hfmModel) { for (int i = 0; i < (int)hfmModel.meshes.size(); i++) { const HFMMesh& mesh = hfmModel.meshes.at(i); - for (int j = 0; j < mesh.clusters.size(); j++) { + std::vector dummyClustersList; + for (int j = 0; j < mesh.clusters.size(); j++) { + std::vector bindMatrices; // cast into a non-const reference, so we can mutate the FBXCluster HFMCluster& cluster = const_cast(mesh.clusters.at(j)); - + + HFMCluster localCluster; + localCluster.jointIndex = cluster.jointIndex; + localCluster.inverseBindMatrix = cluster.inverseBindMatrix; + localCluster.inverseBindTransform.evalFromRawMatrix(localCluster.inverseBindMatrix); + //dummyClustersList.push_back(localCluster); // AJT: mutate bind pose! this allows us to oreint the skeleton back into the authored orientaiton before // rendering, with no runtime overhead. if (hfmModel.jointRotationOffsets.contains(cluster.jointIndex)) { AnimPose localOffset(hfmModel.jointRotationOffsets[cluster.jointIndex], glm::vec3()); if ((hfmModel.clusterBindMatrixOriginalValues.size() > i) && (hfmModel.clusterBindMatrixOriginalValues[i].size() > j)) { - cluster.inverseBindMatrix = (glm::mat4)localOffset.inverse() * hfmModel.clusterBindMatrixOriginalValues[i][j]; + localCluster.inverseBindMatrix = (glm::mat4)localOffset.inverse() * hfmModel.clusterBindMatrixOriginalValues[i][j]; + //cluster.inverseBindMatrix = (glm::mat4)localOffset.inverse() * hfmModel.clusterBindMatrixOriginalValues[i][j]; } - cluster.inverseBindTransform.evalFromRawMatrix(cluster.inverseBindMatrix); + //cluster.inverseBindTransform.evalFromRawMatrix(cluster.inverseBindMatrix); + localCluster.inverseBindTransform.evalFromRawMatrix(localCluster.inverseBindMatrix); } + dummyClustersList.push_back(localCluster); } + _clusterBindMatrixOriginalValues.push_back(dummyClustersList); } } diff --git a/libraries/animation/src/AnimSkeleton.h b/libraries/animation/src/AnimSkeleton.h index f06813e51f..552b40b135 100644 --- a/libraries/animation/src/AnimSkeleton.h +++ b/libraries/animation/src/AnimSkeleton.h @@ -63,6 +63,7 @@ public: void dump(const AnimPoseVec& poses) const; std::vector lookUpJointIndices(const std::vector& jointNames) const; + std::vector> _clusterBindMatrixOriginalValues; protected: void buildSkeletonFromJoints(const std::vector& joints, const QMap jointOffsets); @@ -77,6 +78,7 @@ protected: std::vector _nonMirroredIndices; std::vector _mirrorMap; QHash _jointIndicesByName; + // std::vector> _clusterBindMatrixOriginalValues; // no copies AnimSkeleton(const AnimSkeleton&) = delete; diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index 2e4f98ee4c..5ac87e3004 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -2029,7 +2029,7 @@ HFMModel* FBXReader::extractHFMModel(const QVariantHash& mapping, const QString& } // create a backup copy of the bindposes, - // these are needed when we recompute the bindpose offsets on reset. + // these are needed when we recompute the bindpose offsets in the AnimSkeleton constructor on mySkeleton->reset() for (int i = 0; i < (int)hfmModel.meshes.size(); i++) { const HFMMesh& mesh = hfmModel.meshes.at(i); vector meshBindMatrices; diff --git a/libraries/render-utils/src/CauterizedModel.cpp b/libraries/render-utils/src/CauterizedModel.cpp index c31345bc55..eed5c16445 100644 --- a/libraries/render-utils/src/CauterizedModel.cpp +++ b/libraries/render-utils/src/CauterizedModel.cpp @@ -112,6 +112,7 @@ void CauterizedModel::updateClusterMatrices() { const HFMModel& hfmModel = getHFMModel(); for (int i = 0; i < (int)_meshStates.size(); i++) { + qCDebug(renderutils) << "mesh states size " << _meshStates.size() << " mesh size " << hfmModel.meshes.size(); Model::MeshState& state = _meshStates[i]; const HFMMesh& mesh = hfmModel.meshes.at(i); for (int j = 0; j < mesh.clusters.size(); j++) { @@ -120,12 +121,20 @@ void CauterizedModel::updateClusterMatrices() { auto jointPose = _rig.getJointPose(cluster.jointIndex); Transform jointTransform(jointPose.rot(), jointPose.scale(), jointPose.trans()); Transform clusterTransform; - Transform::mult(clusterTransform, jointTransform, cluster.inverseBindTransform); + Transform clusterTransform2; + //Transform::mult(clusterTransform, jointTransform, cluster.inverseBindTransform); + Transform::mult(clusterTransform, jointTransform, _rig.getAnimSkeleton()->_clusterBindMatrixOriginalValues[i][j].inverseBindTransform); + Transform::mult(clusterTransform2, jointTransform, cluster.inverseBindTransform); + //qCDebug(renderutils) << "the animskel matrix i " << i << " j " << j << _rig.getAnimSkeleton()->_clusterBindMatrixOriginalValues[i][j].inverseBindTransform; + qCDebug(renderutils) << "the mesh state cluster matrix " << state.clusterMatrices[j]; + //qCDebug(renderutils) << "cluster transform old way " << clusterTransform2; + //qCDebug(renderutils) << "cluster transform new way " << clusterTransform; state.clusterDualQuaternions[j] = Model::TransformDualQuaternion(clusterTransform); state.clusterDualQuaternions[j].setCauterizationParameters(0.0f, jointPose.trans()); } else { auto jointMatrix = _rig.getJointTransform(cluster.jointIndex); - glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, state.clusterMatrices[j]); + //glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, state.clusterMatrices[j]); + glm_mat4u_mul(jointMatrix, _rig.getAnimSkeleton()->_clusterBindMatrixOriginalValues[i][j].inverseBindMatrix, state.clusterMatrices[j]); } } } @@ -157,7 +166,8 @@ void CauterizedModel::updateClusterMatrices() { } else { Transform jointTransform(cauterizePose.rot(), cauterizePose.scale(), cauterizePose.trans()); Transform clusterTransform; - Transform::mult(clusterTransform, jointTransform, cluster.inverseBindTransform); + //Transform::mult(clusterTransform, jointTransform, cluster.inverseBindTransform); + Transform::mult(clusterTransform, jointTransform, _rig.getAnimSkeleton()->_clusterBindMatrixOriginalValues[i][j].inverseBindTransform); state.clusterDualQuaternions[j] = Model::TransformDualQuaternion(clusterTransform); state.clusterDualQuaternions[j].setCauterizationParameters(1.0f, cauterizePose.trans()); } @@ -166,7 +176,8 @@ void CauterizedModel::updateClusterMatrices() { // not cauterized so just copy the value from the non-cauterized version. state.clusterMatrices[j] = _meshStates[i].clusterMatrices[j]; } else { - glm_mat4u_mul(cauterizeMatrix, cluster.inverseBindMatrix, state.clusterMatrices[j]); + // glm_mat4u_mul(cauterizeMatrix, cluster.inverseBindMatrix, state.clusterMatrices[j]); + glm_mat4u_mul(cauterizeMatrix, _rig.getAnimSkeleton()->_clusterBindMatrixOriginalValues[i][j].inverseBindMatrix, state.clusterMatrices[j]); } } } @@ -231,6 +242,8 @@ void CauterizedModel::updateRenderItems() { if (useDualQuaternionSkinning) { data.updateClusterBuffer(meshState.clusterDualQuaternions, cauterizedMeshState.clusterDualQuaternions); + //qCDebug(renderutils) << "mesh cluster transform " << meshState.clusterDualQuaternions[0]; + //qCDebug(renderutils) << "cauterized mesh cluster transform " << cauterizedMeshState.clusterDualQuaternions[0]; } else { data.updateClusterBuffer(meshState.clusterMatrices, cauterizedMeshState.clusterMatrices); diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 7da7a45e83..25dba9c1de 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -237,6 +237,10 @@ void Model::updateRenderItems() { bool invalidatePayloadShapeKey = self->shouldInvalidatePayloadShapeKey(meshIndex); bool useDualQuaternionSkinning = self->getUseDualQuaternionSkinning(); + if (useDualQuaternionSkinning) { + qCDebug(renderutils) << "use dual quats value " << useDualQuaternionSkinning; + } + transaction.updateItem(itemID, [modelTransform, meshState, useDualQuaternionSkinning, invalidatePayloadShapeKey, isWireframe, renderItemKeyGlobalFlags](ModelMeshPartPayload& data) { if (useDualQuaternionSkinning) { @@ -1425,11 +1429,13 @@ void Model::updateClusterMatrices() { auto jointPose = _rig.getJointPose(cluster.jointIndex); Transform jointTransform(jointPose.rot(), jointPose.scale(), jointPose.trans()); Transform clusterTransform; - Transform::mult(clusterTransform, jointTransform, cluster.inverseBindTransform); + Transform::mult(clusterTransform, jointTransform, _rig.getAnimSkeleton()->_clusterBindMatrixOriginalValues[i][j].inverseBindTransform); + //Transform::mult(clusterTransform, jointTransform, cluster.inverseBindTransform); state.clusterDualQuaternions[j] = Model::TransformDualQuaternion(clusterTransform); } else { auto jointMatrix = _rig.getJointTransform(cluster.jointIndex); - glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, state.clusterMatrices[j]); + glm_mat4u_mul(jointMatrix, _rig.getAnimSkeleton()->_clusterBindMatrixOriginalValues[i][j].inverseBindMatrix, state.clusterMatrices[j]); + // glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, state.clusterMatrices[j]); } } } diff --git a/libraries/render-utils/src/SoftAttachmentModel.cpp b/libraries/render-utils/src/SoftAttachmentModel.cpp index f26bad86b0..58b0989403 100644 --- a/libraries/render-utils/src/SoftAttachmentModel.cpp +++ b/libraries/render-utils/src/SoftAttachmentModel.cpp @@ -61,7 +61,8 @@ void SoftAttachmentModel::updateClusterMatrices() { } glm::mat4 m; - glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, m); + //glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, m); + glm_mat4u_mul(jointMatrix, _rig.getAnimSkeleton()->_clusterBindMatrixOriginalValues[i][j].inverseBindMatrix, m); state.clusterDualQuaternions[j] = Model::TransformDualQuaternion(m); } else { glm::mat4 jointMatrix; @@ -70,8 +71,8 @@ void SoftAttachmentModel::updateClusterMatrices() { } else { jointMatrix = _rig.getJointTransform(cluster.jointIndex); } - - glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, state.clusterMatrices[j]); + //glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, state.clusterMatrices[j]); + glm_mat4u_mul(jointMatrix, _rig.getAnimSkeleton()->_clusterBindMatrixOriginalValues[i][j].inverseBindMatrix, state.clusterMatrices[j]); } } } From 80ebde10369f576359834c073355c5de13524fcb Mon Sep 17 00:00:00 2001 From: amantley Date: Wed, 14 Nov 2018 17:27:45 -0800 Subject: [PATCH 245/286] started cleanup of pr --- libraries/animation/src/AnimSkeleton.cpp | 10 +++------- libraries/animation/src/AnimSkeleton.h | 1 - .../src/avatars-renderer/SkeletonModel.cpp | 1 - libraries/fbx/src/FBXReader.cpp | 11 ----------- libraries/render-utils/src/CauterizedModel.cpp | 13 ------------- libraries/render-utils/src/Model.cpp | 4 ---- libraries/render-utils/src/SoftAttachmentModel.cpp | 2 -- 7 files changed, 3 insertions(+), 39 deletions(-) diff --git a/libraries/animation/src/AnimSkeleton.cpp b/libraries/animation/src/AnimSkeleton.cpp index d9c90a58cc..435eaf9184 100644 --- a/libraries/animation/src/AnimSkeleton.cpp +++ b/libraries/animation/src/AnimSkeleton.cpp @@ -33,21 +33,18 @@ AnimSkeleton::AnimSkeleton(const HFMModel& hfmModel) { std::vector bindMatrices; // cast into a non-const reference, so we can mutate the FBXCluster HFMCluster& cluster = const_cast(mesh.clusters.at(j)); - + HFMCluster localCluster; localCluster.jointIndex = cluster.jointIndex; localCluster.inverseBindMatrix = cluster.inverseBindMatrix; localCluster.inverseBindTransform.evalFromRawMatrix(localCluster.inverseBindMatrix); - //dummyClustersList.push_back(localCluster); - // AJT: mutate bind pose! this allows us to oreint the skeleton back into the authored orientaiton before - // rendering, with no runtime overhead. + + // we make a copy of the inversebindMatrices in order to prevent mutating the model bind pose if (hfmModel.jointRotationOffsets.contains(cluster.jointIndex)) { AnimPose localOffset(hfmModel.jointRotationOffsets[cluster.jointIndex], glm::vec3()); if ((hfmModel.clusterBindMatrixOriginalValues.size() > i) && (hfmModel.clusterBindMatrixOriginalValues[i].size() > j)) { localCluster.inverseBindMatrix = (glm::mat4)localOffset.inverse() * hfmModel.clusterBindMatrixOriginalValues[i][j]; - //cluster.inverseBindMatrix = (glm::mat4)localOffset.inverse() * hfmModel.clusterBindMatrixOriginalValues[i][j]; } - //cluster.inverseBindTransform.evalFromRawMatrix(cluster.inverseBindMatrix); localCluster.inverseBindTransform.evalFromRawMatrix(localCluster.inverseBindMatrix); } dummyClustersList.push_back(localCluster); @@ -220,7 +217,6 @@ void AnimSkeleton::buildSkeletonFromJoints(const std::vector& joints, // build relative and absolute default poses glm::mat4 relDefaultMat = glm::translate(_joints[i].translation) * preRotationTransform * glm::mat4_cast(_joints[i].rotation) * postRotationTransform; AnimPose relDefaultPose(relDefaultMat); - qCDebug(animation) << "relative default pose for joint " << i << " " << relDefaultPose.trans() << " " << relDefaultPose.rot(); int parentIndex = getParentIndex(i); if (parentIndex >= 0) { diff --git a/libraries/animation/src/AnimSkeleton.h b/libraries/animation/src/AnimSkeleton.h index 552b40b135..7436609ee1 100644 --- a/libraries/animation/src/AnimSkeleton.h +++ b/libraries/animation/src/AnimSkeleton.h @@ -78,7 +78,6 @@ protected: std::vector _nonMirroredIndices; std::vector _mirrorMap; QHash _jointIndicesByName; - // std::vector> _clusterBindMatrixOriginalValues; // no copies AnimSkeleton(const AnimSkeleton&) = delete; diff --git a/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp b/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp index 32c95b8c4c..36e37dd3d4 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp @@ -56,7 +56,6 @@ void SkeletonModel::setTextures(const QVariantMap& textures) { void SkeletonModel::initJointStates() { const HFMModel& hfmModel = getHFMModel(); glm::mat4 modelOffset = glm::scale(_scale) * glm::translate(_offset); - _rig.initJointStates(hfmModel, modelOffset); { diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index 5ac87e3004..6ec7e0e73c 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -2028,17 +2028,6 @@ HFMModel* FBXReader::extractHFMModel(const QVariantHash& mapping, const QString& qCDebug(modelformat) << "Joint Rotation Offset added to Rig._jointRotationOffsets : " << " jointName: " << jointName << " jointIndex: " << jointIndex << " rotation offset: " << rotationOffset; } - // create a backup copy of the bindposes, - // these are needed when we recompute the bindpose offsets in the AnimSkeleton constructor on mySkeleton->reset() - for (int i = 0; i < (int)hfmModel.meshes.size(); i++) { - const HFMMesh& mesh = hfmModel.meshes.at(i); - vector meshBindMatrices; - for (int j = 0; j < mesh.clusters.size(); j++) { - const HFMCluster& cluster = mesh.clusters.at(j); - meshBindMatrices.push_back(cluster.inverseBindMatrix); - } - hfmModel.clusterBindMatrixOriginalValues.push_back(meshBindMatrices); - } return hfmModelPtr; } diff --git a/libraries/render-utils/src/CauterizedModel.cpp b/libraries/render-utils/src/CauterizedModel.cpp index eed5c16445..e00a82d71f 100644 --- a/libraries/render-utils/src/CauterizedModel.cpp +++ b/libraries/render-utils/src/CauterizedModel.cpp @@ -112,7 +112,6 @@ void CauterizedModel::updateClusterMatrices() { const HFMModel& hfmModel = getHFMModel(); for (int i = 0; i < (int)_meshStates.size(); i++) { - qCDebug(renderutils) << "mesh states size " << _meshStates.size() << " mesh size " << hfmModel.meshes.size(); Model::MeshState& state = _meshStates[i]; const HFMMesh& mesh = hfmModel.meshes.at(i); for (int j = 0; j < mesh.clusters.size(); j++) { @@ -121,19 +120,11 @@ void CauterizedModel::updateClusterMatrices() { auto jointPose = _rig.getJointPose(cluster.jointIndex); Transform jointTransform(jointPose.rot(), jointPose.scale(), jointPose.trans()); Transform clusterTransform; - Transform clusterTransform2; - //Transform::mult(clusterTransform, jointTransform, cluster.inverseBindTransform); Transform::mult(clusterTransform, jointTransform, _rig.getAnimSkeleton()->_clusterBindMatrixOriginalValues[i][j].inverseBindTransform); - Transform::mult(clusterTransform2, jointTransform, cluster.inverseBindTransform); - //qCDebug(renderutils) << "the animskel matrix i " << i << " j " << j << _rig.getAnimSkeleton()->_clusterBindMatrixOriginalValues[i][j].inverseBindTransform; - qCDebug(renderutils) << "the mesh state cluster matrix " << state.clusterMatrices[j]; - //qCDebug(renderutils) << "cluster transform old way " << clusterTransform2; - //qCDebug(renderutils) << "cluster transform new way " << clusterTransform; state.clusterDualQuaternions[j] = Model::TransformDualQuaternion(clusterTransform); state.clusterDualQuaternions[j].setCauterizationParameters(0.0f, jointPose.trans()); } else { auto jointMatrix = _rig.getJointTransform(cluster.jointIndex); - //glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, state.clusterMatrices[j]); glm_mat4u_mul(jointMatrix, _rig.getAnimSkeleton()->_clusterBindMatrixOriginalValues[i][j].inverseBindMatrix, state.clusterMatrices[j]); } } @@ -166,7 +157,6 @@ void CauterizedModel::updateClusterMatrices() { } else { Transform jointTransform(cauterizePose.rot(), cauterizePose.scale(), cauterizePose.trans()); Transform clusterTransform; - //Transform::mult(clusterTransform, jointTransform, cluster.inverseBindTransform); Transform::mult(clusterTransform, jointTransform, _rig.getAnimSkeleton()->_clusterBindMatrixOriginalValues[i][j].inverseBindTransform); state.clusterDualQuaternions[j] = Model::TransformDualQuaternion(clusterTransform); state.clusterDualQuaternions[j].setCauterizationParameters(1.0f, cauterizePose.trans()); @@ -176,7 +166,6 @@ void CauterizedModel::updateClusterMatrices() { // not cauterized so just copy the value from the non-cauterized version. state.clusterMatrices[j] = _meshStates[i].clusterMatrices[j]; } else { - // glm_mat4u_mul(cauterizeMatrix, cluster.inverseBindMatrix, state.clusterMatrices[j]); glm_mat4u_mul(cauterizeMatrix, _rig.getAnimSkeleton()->_clusterBindMatrixOriginalValues[i][j].inverseBindMatrix, state.clusterMatrices[j]); } } @@ -242,8 +231,6 @@ void CauterizedModel::updateRenderItems() { if (useDualQuaternionSkinning) { data.updateClusterBuffer(meshState.clusterDualQuaternions, cauterizedMeshState.clusterDualQuaternions); - //qCDebug(renderutils) << "mesh cluster transform " << meshState.clusterDualQuaternions[0]; - //qCDebug(renderutils) << "cauterized mesh cluster transform " << cauterizedMeshState.clusterDualQuaternions[0]; } else { data.updateClusterBuffer(meshState.clusterMatrices, cauterizedMeshState.clusterMatrices); diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 25dba9c1de..a5565d0646 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -237,10 +237,6 @@ void Model::updateRenderItems() { bool invalidatePayloadShapeKey = self->shouldInvalidatePayloadShapeKey(meshIndex); bool useDualQuaternionSkinning = self->getUseDualQuaternionSkinning(); - if (useDualQuaternionSkinning) { - qCDebug(renderutils) << "use dual quats value " << useDualQuaternionSkinning; - } - transaction.updateItem(itemID, [modelTransform, meshState, useDualQuaternionSkinning, invalidatePayloadShapeKey, isWireframe, renderItemKeyGlobalFlags](ModelMeshPartPayload& data) { if (useDualQuaternionSkinning) { diff --git a/libraries/render-utils/src/SoftAttachmentModel.cpp b/libraries/render-utils/src/SoftAttachmentModel.cpp index 58b0989403..e45141a838 100644 --- a/libraries/render-utils/src/SoftAttachmentModel.cpp +++ b/libraries/render-utils/src/SoftAttachmentModel.cpp @@ -61,7 +61,6 @@ void SoftAttachmentModel::updateClusterMatrices() { } glm::mat4 m; - //glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, m); glm_mat4u_mul(jointMatrix, _rig.getAnimSkeleton()->_clusterBindMatrixOriginalValues[i][j].inverseBindMatrix, m); state.clusterDualQuaternions[j] = Model::TransformDualQuaternion(m); } else { @@ -71,7 +70,6 @@ void SoftAttachmentModel::updateClusterMatrices() { } else { jointMatrix = _rig.getJointTransform(cluster.jointIndex); } - //glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, state.clusterMatrices[j]); glm_mat4u_mul(jointMatrix, _rig.getAnimSkeleton()->_clusterBindMatrixOriginalValues[i][j].inverseBindMatrix, state.clusterMatrices[j]); } } From d0e4c6297874d9fc726656e66bc45f2b17bc7a89 Mon Sep 17 00:00:00 2001 From: humbletim Date: Wed, 14 Nov 2018 23:50:02 -0500 Subject: [PATCH 246/286] fix merge conflict --- interface/src/ModelPropertiesDialog.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/ModelPropertiesDialog.cpp b/interface/src/ModelPropertiesDialog.cpp index a32d8adefb..1bdb170b60 100644 --- a/interface/src/ModelPropertiesDialog.cpp +++ b/interface/src/ModelPropertiesDialog.cpp @@ -26,7 +26,7 @@ #include -ModelPropertiesDialog::ModelPropertiesDialog(FSTReader::ModelType modelType, const QVariantHash& originalMapping, +ModelPropertiesDialog::ModelPropertiesDialog(const QVariantHash& originalMapping, const QString& basePath, const HFMModel& hfmModel) : _originalMapping(originalMapping), _basePath(basePath), From 1e17b878e70d3e7dd90a10c0c0a0708ab6a72ed8 Mon Sep 17 00:00:00 2001 From: NissimHadar Date: Thu, 15 Nov 2018 07:32:26 -0800 Subject: [PATCH 247/286] CR updates. --- tools/nitpick/src/PythonInterface.h | 3 +-- tools/nitpick/src/TestRunner.h | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/tools/nitpick/src/PythonInterface.h b/tools/nitpick/src/PythonInterface.h index a218b68039..a6e954f356 100644 --- a/tools/nitpick/src/PythonInterface.h +++ b/tools/nitpick/src/PythonInterface.h @@ -21,8 +21,7 @@ public: private: #ifdef Q_OS_WIN const QString _pythonExe{ "python.exe" }; -#endif -#ifdef Q_OS_MACOS +#elif const QString _pythonExe{ "python" }; #endif diff --git a/tools/nitpick/src/TestRunner.h b/tools/nitpick/src/TestRunner.h index 11129d2f36..00f0f66ecf 100644 --- a/tools/nitpick/src/TestRunner.h +++ b/tools/nitpick/src/TestRunner.h @@ -94,7 +94,7 @@ private: #elif defined(Q_OS_MAC) const QString INSTALLER_FILENAME_LATEST{ "HighFidelity-Beta-latest-dev.dmg" }; #else - const QString INSTALLER_FILENAME_LATEST{ "just to pass compilation" }; + const QString INSTALLER_FILENAME_LATEST{ "" }; #endif QString _installerURL; From fb0a84e80047c05bf788a31253c4f9026546797f Mon Sep 17 00:00:00 2001 From: NissimHadar Date: Thu, 15 Nov 2018 07:54:56 -0800 Subject: [PATCH 248/286] CR updates. --- tools/nitpick/src/PythonInterface.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/nitpick/src/PythonInterface.h b/tools/nitpick/src/PythonInterface.h index a6e954f356..947b359037 100644 --- a/tools/nitpick/src/PythonInterface.h +++ b/tools/nitpick/src/PythonInterface.h @@ -21,7 +21,8 @@ public: private: #ifdef Q_OS_WIN const QString _pythonExe{ "python.exe" }; -#elif +#else + // Both Mac and Linux use "python" const QString _pythonExe{ "python" }; #endif From 0401858129b2c1d14ad735853c80630cf41fcaa4 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Thu, 15 Nov 2018 18:34:41 +0100 Subject: [PATCH 249/286] Cleaned up do_draw in GLESBackend --- .../gpu-gl-common/src/gpu/gl/GLBackend.cpp | 9 -------- .../gpu-gl-common/src/gpu/gl/GLBackend.h | 2 +- libraries/gpu-gl/src/gpu/gl41/GL41Backend.cpp | 9 ++++++++ libraries/gpu-gl/src/gpu/gl41/GL41Backend.h | 1 + libraries/gpu-gl/src/gpu/gl45/GL45Backend.cpp | 9 ++++++++ libraries/gpu-gl/src/gpu/gl45/GL45Backend.h | 1 + .../gpu-gles/src/gpu/gles/GLESBackend.cpp | 23 +------------------ 7 files changed, 22 insertions(+), 32 deletions(-) diff --git a/libraries/gpu-gl-common/src/gpu/gl/GLBackend.cpp b/libraries/gpu-gl-common/src/gpu/gl/GLBackend.cpp index d5124e82a4..c1ce05c18b 100644 --- a/libraries/gpu-gl-common/src/gpu/gl/GLBackend.cpp +++ b/libraries/gpu-gl-common/src/gpu/gl/GLBackend.cpp @@ -177,15 +177,6 @@ void GLBackend::shutdown() { killShaderBinaryCache(); } -void GLBackend::do_draw(const Batch& batch, size_t paramOffset) { - Primitive primitiveType = (Primitive)batch._params[paramOffset + 2]._uint; - GLenum mode = gl::PRIMITIVE_TO_GL[primitiveType]; - uint32 numVertices = batch._params[paramOffset + 1]._uint; - uint32 startVertex = batch._params[paramOffset + 0]._uint; - - draw(mode, numVertices, startVertex); -} - void GLBackend::renderPassTransfer(const Batch& batch) { const size_t numCommands = batch.getCommands().size(); const Batch::Commands::value_type* command = batch.getCommands().data(); diff --git a/libraries/gpu-gl-common/src/gpu/gl/GLBackend.h b/libraries/gpu-gl-common/src/gpu/gl/GLBackend.h index ae3a7ce5fd..37dde5b08e 100644 --- a/libraries/gpu-gl-common/src/gpu/gl/GLBackend.h +++ b/libraries/gpu-gl-common/src/gpu/gl/GLBackend.h @@ -275,7 +275,7 @@ public: size_t getMaxNumResourceTextureTables() const { return MAX_NUM_RESOURCE_TABLE_TEXTURES; } // Draw Stage - virtual void do_draw(const Batch& batch, size_t paramOffset); + virtual void do_draw(const Batch& batch, size_t paramOffset) = 0; virtual void do_drawIndexed(const Batch& batch, size_t paramOffset) = 0; virtual void do_drawInstanced(const Batch& batch, size_t paramOffset) = 0; virtual void do_drawIndexedInstanced(const Batch& batch, size_t paramOffset) = 0; diff --git a/libraries/gpu-gl/src/gpu/gl41/GL41Backend.cpp b/libraries/gpu-gl/src/gpu/gl41/GL41Backend.cpp index ea884fe125..cb0950c06d 100644 --- a/libraries/gpu-gl/src/gpu/gl41/GL41Backend.cpp +++ b/libraries/gpu-gl/src/gpu/gl41/GL41Backend.cpp @@ -43,6 +43,15 @@ void GL41Backend::draw(GLenum mode, uint32 numVertices, uint32 startVertex) { (void)CHECK_GL_ERROR(); } +void GL41Backend::do_draw(const Batch& batch, size_t paramOffset) { + Primitive primitiveType = (Primitive)batch._params[paramOffset + 2]._uint; + GLenum mode = gl::PRIMITIVE_TO_GL[primitiveType]; + uint32 numVertices = batch._params[paramOffset + 1]._uint; + uint32 startVertex = batch._params[paramOffset + 0]._uint; + + draw(mode, numVertices, startVertex); +} + void GL41Backend::do_drawIndexed(const Batch& batch, size_t paramOffset) { Primitive primitiveType = (Primitive)batch._params[paramOffset + 2]._uint; GLenum mode = gl::PRIMITIVE_TO_GL[primitiveType]; diff --git a/libraries/gpu-gl/src/gpu/gl41/GL41Backend.h b/libraries/gpu-gl/src/gpu/gl41/GL41Backend.h index 23e49773ae..881487c9db 100644 --- a/libraries/gpu-gl/src/gpu/gl41/GL41Backend.h +++ b/libraries/gpu-gl/src/gpu/gl41/GL41Backend.h @@ -147,6 +147,7 @@ protected: GLQuery* syncGPUObject(const Query& query) override; // Draw Stage + void do_draw(const Batch& batch, size_t paramOffset) override; void do_drawIndexed(const Batch& batch, size_t paramOffset) override; void do_drawInstanced(const Batch& batch, size_t paramOffset) override; void do_drawIndexedInstanced(const Batch& batch, size_t paramOffset) override; diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.cpp index 29c2a230a0..385ddca065 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.cpp @@ -66,6 +66,15 @@ void GL45Backend::draw(GLenum mode, uint32 numVertices, uint32 startVertex) { (void)CHECK_GL_ERROR(); } +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]; + uint32 numVertices = batch._params[paramOffset + 1]._uint; + uint32 startVertex = batch._params[paramOffset + 0]._uint; + + draw(mode, numVertices, startVertex); +} + void GL45Backend::do_drawIndexed(const Batch& batch, size_t paramOffset) { Primitive primitiveType = (Primitive)batch._params[paramOffset + 2]._uint; GLenum mode = gl::PRIMITIVE_TO_GL[primitiveType]; diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h index 391fec45ce..c1ce074188 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h +++ b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h @@ -248,6 +248,7 @@ protected: GLQuery* syncGPUObject(const Query& query) override; // Draw Stage + void do_draw(const Batch& batch, size_t paramOffset) override; void do_drawIndexed(const Batch& batch, size_t paramOffset) override; void do_drawInstanced(const Batch& batch, size_t paramOffset) override; void do_drawIndexedInstanced(const Batch& batch, size_t paramOffset) override; diff --git a/libraries/gpu-gles/src/gpu/gles/GLESBackend.cpp b/libraries/gpu-gles/src/gpu/gles/GLESBackend.cpp index cb40b4fc9b..12c1346c7b 100644 --- a/libraries/gpu-gles/src/gpu/gles/GLESBackend.cpp +++ b/libraries/gpu-gles/src/gpu/gles/GLESBackend.cpp @@ -49,28 +49,7 @@ void GLESBackend::do_draw(const Batch& batch, size_t paramOffset) { uint32 numVertices = batch._params[paramOffset + 1]._uint; uint32 startVertex = batch._params[paramOffset + 0]._uint; - if (isStereo()) { -#ifdef GPU_STEREO_DRAWCALL_INSTANCED - glDrawArraysInstanced(mode, startVertex, numVertices, 2); -#else - - setupStereoSide(0); - glDrawArrays(mode, startVertex, numVertices); - setupStereoSide(1); - glDrawArrays(mode, startVertex, numVertices); - -#endif - _stats._DSNumTriangles += 2 * numVertices / 3; - _stats._DSNumDrawcalls += 2; - - } else { - glDrawArrays(mode, startVertex, numVertices); - _stats._DSNumTriangles += numVertices / 3; - _stats._DSNumDrawcalls++; - } - _stats._DSNumAPIDrawcalls++; - - (void) CHECK_GL_ERROR(); + draw(mode, numVertices, startVertex); } void GLESBackend::do_drawIndexed(const Batch& batch, size_t paramOffset) { From cd00abd2163583b68296443e30f1c113218bff15 Mon Sep 17 00:00:00 2001 From: Simon Walton Date: Thu, 15 Nov 2018 09:47:43 -0800 Subject: [PATCH 250/286] Add upload progress bar --- .../resources/web/content/js/content.js | 32 +++++++++++++------ 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/domain-server/resources/web/content/js/content.js b/domain-server/resources/web/content/js/content.js index 93bde3122b..78e78266d3 100644 --- a/domain-server/resources/web/content/js/content.js +++ b/domain-server/resources/web/content/js/content.js @@ -10,10 +10,20 @@ $(document).ready(function(){ function progressBarHTML(extraClass, label) { var html = "
"; html += "
"; - html += label + "
"; + html += label + "
"; return html; } + function showUploadProgress(title) { + swal({ + title: title, + text: progressBarHTML('upload-content-progress', 'Upload'), + html: true, + showConfirmButton: false, + allowEscapeKey: false + }); + } + function uploadNextChunk(file, offset) { if (offset == undefined) { offset = 0; @@ -26,7 +36,7 @@ $(document).ready(function(){ var isFinal = Boolean(fileSize - offset <= CHUNK_SIZE); var nextChunkSize = Math.min(fileSize - offset, CHUNK_SIZE); - var chunk = file.slice(offset, offset + CHUNK_SIZE, file.type); + var chunk = file.slice(offset, offset + nextChunkSize, file.type); var chunkFormData = new FormData(); var formItemName = isFinal ? 'restore-file-chunk-final' : 'restore-file-chunk'; @@ -50,6 +60,8 @@ $(document).ready(function(){ ); }); + updateProgressBars($('.upload-content-progress'), Math.round((offset + nextChunkSize) * 100 / fileSize)); + if (!isFinal) { ajaxObject.done(function (data, textStatus, jqXHR) { uploadNextChunk(file, offset + CHUNK_SIZE); }); @@ -102,10 +114,10 @@ $(document).ready(function(){ "Restore content", function() { var files = $('#' + RESTORE_SETTINGS_FILE_ID).prop('files'); + var file = files[0]; - showSpinnerAlert("Uploading content to restore"); - - uploadNextChunk(files[0]); + showUploadProgress("Uploading " + file.name); + uploadNextChunk(file); } ); }); @@ -196,6 +208,11 @@ $(document).ready(function(){ checkBackupStatus(); }); + function updateProgressBars($progressBar, value) { + $progressBar.attr('aria-valuenow', value).attr('style', 'width: ' + value + '%'); + $progressBar.find('.ongoing-msg').html(" " + value + "% Complete"); + } + function reloadBackupInformation() { // make a GET request to get backup information to populate the table $.ajax({ @@ -232,11 +249,6 @@ $(document).ready(function(){ + "
  • Delete
  • "; } - function updateProgressBars($progressBar, value) { - $progressBar.attr('aria-valuenow', value).attr('style', 'width: ' + value + '%'); - $progressBar.find('.sr-only').html(value + "% Complete"); - } - // before we add any new rows and update existing ones // remove our flag for active rows $('.' + ACTIVE_BACKUP_ROW_CLASS).removeClass(ACTIVE_BACKUP_ROW_CLASS); From c8cd65c3bd46d17668f4c94042fed45b2b0fa199 Mon Sep 17 00:00:00 2001 From: amantley Date: Thu, 15 Nov 2018 09:57:09 -0800 Subject: [PATCH 251/286] added getter for the orginal cluster bind matrix values in AnimSkeleton --- libraries/animation/src/AnimSkeleton.cpp | 9 ++++--- libraries/animation/src/AnimSkeleton.h | 3 ++- libraries/fbx/src/FBX.h | 1 - .../render-utils/src/CauterizedModel.cpp | 14 +++++++--- libraries/render-utils/src/Model.cpp | 9 ++++--- .../render-utils/src/SoftAttachmentModel.cpp | 26 +++++++------------ 6 files changed, 32 insertions(+), 30 deletions(-) diff --git a/libraries/animation/src/AnimSkeleton.cpp b/libraries/animation/src/AnimSkeleton.cpp index 435eaf9184..91ca2359b4 100644 --- a/libraries/animation/src/AnimSkeleton.cpp +++ b/libraries/animation/src/AnimSkeleton.cpp @@ -25,6 +25,8 @@ AnimSkeleton::AnimSkeleton(const HFMModel& hfmModel) { } buildSkeletonFromJoints(joints, hfmModel.jointRotationOffsets); + // we make a copy of the inverseBindMatrices in order to prevent mutating the model bind pose + // when we are dealing with a joint offset in the model for (int i = 0; i < (int)hfmModel.meshes.size(); i++) { const HFMMesh& mesh = hfmModel.meshes.at(i); std::vector dummyClustersList; @@ -39,12 +41,11 @@ AnimSkeleton::AnimSkeleton(const HFMModel& hfmModel) { localCluster.inverseBindMatrix = cluster.inverseBindMatrix; localCluster.inverseBindTransform.evalFromRawMatrix(localCluster.inverseBindMatrix); - // we make a copy of the inversebindMatrices in order to prevent mutating the model bind pose + // if we have a joint offset in the fst file then multiply its inverse by the + // model cluster inverse bind matrix if (hfmModel.jointRotationOffsets.contains(cluster.jointIndex)) { AnimPose localOffset(hfmModel.jointRotationOffsets[cluster.jointIndex], glm::vec3()); - if ((hfmModel.clusterBindMatrixOriginalValues.size() > i) && (hfmModel.clusterBindMatrixOriginalValues[i].size() > j)) { - localCluster.inverseBindMatrix = (glm::mat4)localOffset.inverse() * hfmModel.clusterBindMatrixOriginalValues[i][j]; - } + localCluster.inverseBindMatrix = (glm::mat4)localOffset.inverse() * cluster.inverseBindMatrix; localCluster.inverseBindTransform.evalFromRawMatrix(localCluster.inverseBindMatrix); } dummyClustersList.push_back(localCluster); diff --git a/libraries/animation/src/AnimSkeleton.h b/libraries/animation/src/AnimSkeleton.h index 7436609ee1..ab89eb643d 100644 --- a/libraries/animation/src/AnimSkeleton.h +++ b/libraries/animation/src/AnimSkeleton.h @@ -63,7 +63,7 @@ public: void dump(const AnimPoseVec& poses) const; std::vector lookUpJointIndices(const std::vector& jointNames) const; - std::vector> _clusterBindMatrixOriginalValues; + const HFMCluster getClusterBindMatricesOriginalValues(const int meshIndex, const int clusterIndex) const { return _clusterBindMatrixOriginalValues[meshIndex][clusterIndex]; } protected: void buildSkeletonFromJoints(const std::vector& joints, const QMap jointOffsets); @@ -78,6 +78,7 @@ protected: std::vector _nonMirroredIndices; std::vector _mirrorMap; QHash _jointIndicesByName; + std::vector> _clusterBindMatrixOriginalValues; // no copies AnimSkeleton(const AnimSkeleton&) = delete; diff --git a/libraries/fbx/src/FBX.h b/libraries/fbx/src/FBX.h index 943db4b7fe..d4072f03d7 100644 --- a/libraries/fbx/src/FBX.h +++ b/libraries/fbx/src/FBX.h @@ -349,7 +349,6 @@ public: Extents meshExtents; QVector animationFrames; - std::vector> clusterBindMatrixOriginalValues; int getJointIndex(const QString& name) const { return jointIndices.value(name) - 1; } QStringList getJointNames() const; diff --git a/libraries/render-utils/src/CauterizedModel.cpp b/libraries/render-utils/src/CauterizedModel.cpp index e00a82d71f..86d4793aa5 100644 --- a/libraries/render-utils/src/CauterizedModel.cpp +++ b/libraries/render-utils/src/CauterizedModel.cpp @@ -114,18 +114,22 @@ void CauterizedModel::updateClusterMatrices() { for (int i = 0; i < (int)_meshStates.size(); i++) { Model::MeshState& state = _meshStates[i]; const HFMMesh& mesh = hfmModel.meshes.at(i); + int meshIndex = i; + for (int j = 0; j < mesh.clusters.size(); j++) { const HFMCluster& cluster = mesh.clusters.at(j); + int clusterIndex = j; + if (_useDualQuaternionSkinning) { auto jointPose = _rig.getJointPose(cluster.jointIndex); Transform jointTransform(jointPose.rot(), jointPose.scale(), jointPose.trans()); Transform clusterTransform; - Transform::mult(clusterTransform, jointTransform, _rig.getAnimSkeleton()->_clusterBindMatrixOriginalValues[i][j].inverseBindTransform); + Transform::mult(clusterTransform, jointTransform, _rig.getAnimSkeleton()->getClusterBindMatricesOriginalValues(meshIndex, clusterIndex).inverseBindTransform); state.clusterDualQuaternions[j] = Model::TransformDualQuaternion(clusterTransform); state.clusterDualQuaternions[j].setCauterizationParameters(0.0f, jointPose.trans()); } else { auto jointMatrix = _rig.getJointTransform(cluster.jointIndex); - glm_mat4u_mul(jointMatrix, _rig.getAnimSkeleton()->_clusterBindMatrixOriginalValues[i][j].inverseBindMatrix, state.clusterMatrices[j]); + glm_mat4u_mul(jointMatrix, _rig.getAnimSkeleton()->getClusterBindMatricesOriginalValues(meshIndex, clusterIndex).inverseBindMatrix, state.clusterMatrices[j]); } } } @@ -146,9 +150,11 @@ void CauterizedModel::updateClusterMatrices() { for (int i = 0; i < _cauterizeMeshStates.size(); i++) { Model::MeshState& state = _cauterizeMeshStates[i]; const HFMMesh& mesh = hfmModel.meshes.at(i); + int meshIndex = i; for (int j = 0; j < mesh.clusters.size(); j++) { const HFMCluster& cluster = mesh.clusters.at(j); + int clusterIndex = j; if (_useDualQuaternionSkinning) { if (_cauterizeBoneSet.find(cluster.jointIndex) == _cauterizeBoneSet.end()) { @@ -157,7 +163,7 @@ void CauterizedModel::updateClusterMatrices() { } else { Transform jointTransform(cauterizePose.rot(), cauterizePose.scale(), cauterizePose.trans()); Transform clusterTransform; - Transform::mult(clusterTransform, jointTransform, _rig.getAnimSkeleton()->_clusterBindMatrixOriginalValues[i][j].inverseBindTransform); + Transform::mult(clusterTransform, jointTransform, _rig.getAnimSkeleton()->getClusterBindMatricesOriginalValues(meshIndex, clusterIndex).inverseBindTransform); state.clusterDualQuaternions[j] = Model::TransformDualQuaternion(clusterTransform); state.clusterDualQuaternions[j].setCauterizationParameters(1.0f, cauterizePose.trans()); } @@ -166,7 +172,7 @@ void CauterizedModel::updateClusterMatrices() { // not cauterized so just copy the value from the non-cauterized version. state.clusterMatrices[j] = _meshStates[i].clusterMatrices[j]; } else { - glm_mat4u_mul(cauterizeMatrix, _rig.getAnimSkeleton()->_clusterBindMatrixOriginalValues[i][j].inverseBindMatrix, state.clusterMatrices[j]); + glm_mat4u_mul(cauterizeMatrix, _rig.getAnimSkeleton()->getClusterBindMatricesOriginalValues(meshIndex, clusterIndex).inverseBindMatrix, state.clusterMatrices[j]); } } } diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index a5565d0646..9cefbf65a8 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -1418,20 +1418,21 @@ void Model::updateClusterMatrices() { const HFMModel& hfmModel = getHFMModel(); for (int i = 0; i < (int) _meshStates.size(); i++) { MeshState& state = _meshStates[i]; + int meshIndex = i; const HFMMesh& mesh = hfmModel.meshes.at(i); for (int j = 0; j < mesh.clusters.size(); j++) { const HFMCluster& cluster = mesh.clusters.at(j); + int clusterIndex = j; + if (_useDualQuaternionSkinning) { auto jointPose = _rig.getJointPose(cluster.jointIndex); Transform jointTransform(jointPose.rot(), jointPose.scale(), jointPose.trans()); Transform clusterTransform; - Transform::mult(clusterTransform, jointTransform, _rig.getAnimSkeleton()->_clusterBindMatrixOriginalValues[i][j].inverseBindTransform); - //Transform::mult(clusterTransform, jointTransform, cluster.inverseBindTransform); + Transform::mult(clusterTransform, jointTransform, _rig.getAnimSkeleton()->getClusterBindMatricesOriginalValues(meshIndex, clusterIndex).inverseBindTransform); state.clusterDualQuaternions[j] = Model::TransformDualQuaternion(clusterTransform); } else { auto jointMatrix = _rig.getJointTransform(cluster.jointIndex); - glm_mat4u_mul(jointMatrix, _rig.getAnimSkeleton()->_clusterBindMatrixOriginalValues[i][j].inverseBindMatrix, state.clusterMatrices[j]); - // glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, state.clusterMatrices[j]); + glm_mat4u_mul(jointMatrix, _rig.getAnimSkeleton()->getClusterBindMatricesOriginalValues(meshIndex, clusterIndex).inverseBindMatrix, state.clusterMatrices[j]); } } } diff --git a/libraries/render-utils/src/SoftAttachmentModel.cpp b/libraries/render-utils/src/SoftAttachmentModel.cpp index e45141a838..a6f82e1417 100644 --- a/libraries/render-utils/src/SoftAttachmentModel.cpp +++ b/libraries/render-utils/src/SoftAttachmentModel.cpp @@ -46,31 +46,25 @@ void SoftAttachmentModel::updateClusterMatrices() { for (int i = 0; i < (int) _meshStates.size(); i++) { MeshState& state = _meshStates[i]; const HFMMesh& mesh = hfmModel.meshes.at(i); - + int meshIndex = i; for (int j = 0; j < mesh.clusters.size(); j++) { const HFMCluster& cluster = mesh.clusters.at(j); + int clusterIndex = j; // TODO: cache these look-ups as an optimization int jointIndexOverride = getJointIndexOverride(cluster.jointIndex); + glm::mat4 jointMatrix; + if (jointIndexOverride >= 0 && jointIndexOverride < _rigOverride.getJointStateCount()) { + jointMatrix = _rigOverride.getJointTransform(jointIndexOverride); + } else { + jointMatrix = _rig.getJointTransform(cluster.jointIndex); + } if (_useDualQuaternionSkinning) { - glm::mat4 jointMatrix; - if (jointIndexOverride >= 0 && jointIndexOverride < _rigOverride.getJointStateCount()) { - jointMatrix = _rigOverride.getJointTransform(jointIndexOverride); - } else { - jointMatrix = _rig.getJointTransform(cluster.jointIndex); - } - glm::mat4 m; - glm_mat4u_mul(jointMatrix, _rig.getAnimSkeleton()->_clusterBindMatrixOriginalValues[i][j].inverseBindMatrix, m); + glm_mat4u_mul(jointMatrix, _rig.getAnimSkeleton()->getClusterBindMatricesOriginalValues(meshIndex, clusterIndex).inverseBindMatrix, m); state.clusterDualQuaternions[j] = Model::TransformDualQuaternion(m); } else { - glm::mat4 jointMatrix; - if (jointIndexOverride >= 0 && jointIndexOverride < _rigOverride.getJointStateCount()) { - jointMatrix = _rigOverride.getJointTransform(jointIndexOverride); - } else { - jointMatrix = _rig.getJointTransform(cluster.jointIndex); - } - glm_mat4u_mul(jointMatrix, _rig.getAnimSkeleton()->_clusterBindMatrixOriginalValues[i][j].inverseBindMatrix, state.clusterMatrices[j]); + glm_mat4u_mul(jointMatrix, _rig.getAnimSkeleton()->getClusterBindMatricesOriginalValues(meshIndex, clusterIndex).inverseBindMatrix, state.clusterMatrices[j]); } } } From 597ff773e1ff065792d5b6beb168a1ea24a25528 Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Wed, 14 Nov 2018 18:00:56 -0800 Subject: [PATCH 252/286] fix for avatar flashing --- libraries/render/src/render/Item.h | 4 ++-- libraries/render/src/render/Scene.cpp | 7 ++++++- libraries/render/src/render/Scene.h | 2 +- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/libraries/render/src/render/Item.h b/libraries/render/src/render/Item.h index a87e2031f9..79557e3e44 100644 --- a/libraries/render/src/render/Item.h +++ b/libraries/render/src/render/Item.h @@ -480,8 +480,8 @@ public: protected: PayloadPointer _payload; ItemKey _key; - ItemCell _cell{ INVALID_CELL }; - Index _transitionId{ INVALID_INDEX }; + ItemCell _cell { INVALID_CELL }; + Index _transitionId { INVALID_INDEX }; friend class Scene; }; diff --git a/libraries/render/src/render/Scene.cpp b/libraries/render/src/render/Scene.cpp index 8d12cfae25..1850261c99 100644 --- a/libraries/render/src/render/Scene.cpp +++ b/libraries/render/src/render/Scene.cpp @@ -391,7 +391,12 @@ void Scene::transitionItems(const Transaction::TransitionAdds& transactions) { // Remove pre-existing transition, if need be if (!TransitionStage::isIndexInvalid(transitionId)) { - resetItemTransition(itemId); + // Only remove if: + // transitioning to something other than none or we're transitioning to none from ELEMENT_LEAVE_DOMAIN or USER_LEAVE_DOMAIN + const auto& oldTransitionType = transitionStage->getTransition(transitionId).eventType; + if (transitionType != Transition::NONE || !(oldTransitionType == Transition::ELEMENT_LEAVE_DOMAIN || oldTransitionType == Transition::USER_LEAVE_DOMAIN)) { + resetItemTransition(itemId); + } } // Add a new one. diff --git a/libraries/render/src/render/Scene.h b/libraries/render/src/render/Scene.h index 68acfe8d0f..e05cb04532 100644 --- a/libraries/render/src/render/Scene.h +++ b/libraries/render/src/render/Scene.h @@ -157,7 +157,7 @@ public: // This next call are NOT threadsafe, you have to call them from the correct thread to avoid any potential issues - // Access a particular item form its ID + // Access a particular item from its ID // WARNING, There is No check on the validity of the ID, so this could return a bad Item const Item& getItem(const ItemID& id) const { return _items[id]; } From 20ad05726efd0d86dac24f924f36d741b76cd014 Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Tue, 13 Nov 2018 15:13:30 -0800 Subject: [PATCH 253/286] entity server timout --- .../qml/hifi/tablet/TabletAddressDialog.qml | 2 +- interface/src/Application.cpp | 26 ++++++++++++++++++- interface/src/Application.h | 4 +++ interface/src/octree/SafeLanding.cpp | 15 +---------- interface/src/octree/SafeLanding.h | 1 - 5 files changed, 31 insertions(+), 17 deletions(-) diff --git a/interface/resources/qml/hifi/tablet/TabletAddressDialog.qml b/interface/resources/qml/hifi/tablet/TabletAddressDialog.qml index b8972378ad..ab0a98a8c5 100644 --- a/interface/resources/qml/hifi/tablet/TabletAddressDialog.qml +++ b/interface/resources/qml/hifi/tablet/TabletAddressDialog.qml @@ -56,7 +56,7 @@ StackView { Qt.callLater(function() { addressBarDialog.keyboardEnabled = HMD.active; addressLine.forceActiveFocus(); - addressBarDialog.raised = true; + addressBarDialog.keyboardRaised = true; }) } diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index a9d75e745c..47a73cee57 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -371,6 +371,8 @@ static const QString INFO_HELP_PATH = "html/tabletHelp.html"; static const unsigned int THROTTLED_SIM_FRAMERATE = 15; static const int THROTTLED_SIM_FRAME_PERIOD_MS = MSECS_PER_SECOND / THROTTLED_SIM_FRAMERATE; +static const int ENTITY_SERVER_ADDED_TIMEOUT = 5000; +static const int ENTITY_SERVER_CONNECTION_TIMEOUT = 5000; static const uint32_t INVALID_FRAME = UINT32_MAX; @@ -1224,6 +1226,18 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo getOverlays().deleteOverlay(getTabletScreenID()); getOverlays().deleteOverlay(getTabletHomeButtonID()); getOverlays().deleteOverlay(getTabletFrameID()); + _failedToConnectToEntityServer = false; + }); + + _entityServerConnectionTimer.setSingleShot(true); + connect(&_entityServerConnectionTimer, &QTimer::timeout, this, &Application::setFailedToConnectToEntityServer); + + connect(&domainHandler, &DomainHandler::connectedToDomain, this, [this]() { + if (!isServerlessMode()) { + _entityServerConnectionTimer.setInterval(ENTITY_SERVER_ADDED_TIMEOUT); + _entityServerConnectionTimer.start(); + _failedToConnectToEntityServer = false; + } }); connect(&domainHandler, &DomainHandler::domainConnectionRefused, this, &Application::domainConnectionRefused); @@ -6630,7 +6644,13 @@ void Application::resettingDomain() { } void Application::nodeAdded(SharedNodePointer node) const { - // nothing to do here + if (node->getType() == NodeType::EntityServer) { + if (!_failedToConnectToEntityServer) { + _entityServerConnectionTimer.stop(); + _entityServerConnectionTimer.setInterval(ENTITY_SERVER_CONNECTION_TIMEOUT); + _entityServerConnectionTimer.start(); + } + } } void Application::nodeActivated(SharedNodePointer node) { @@ -6658,6 +6678,10 @@ void Application::nodeActivated(SharedNodePointer node) { if (node->getType() == NodeType::EntityServer) { _queryExpiry = SteadyClock::now(); _octreeQuery.incrementConnectionID(); + + if (!_failedToConnectToEntityServer) { + _entityServerConnectionTimer.stop(); + } } if (node->getType() == NodeType::AudioMixer && !isInterstitialMode()) { diff --git a/interface/src/Application.h b/interface/src/Application.h index 14e30b8006..053cb158e0 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -307,6 +307,7 @@ public: bool isServerlessMode() const; bool isInterstitialMode() const { return _interstitialMode; } + bool failedToConnectToEntityServer() const { return _failedToConnectToEntityServer; } void replaceDomainContent(const QString& url); @@ -460,6 +461,7 @@ private slots: void loadSettings(); void saveSettings() const; + void setFailedToConnectToEntityServer() { _failedToConnectToEntityServer = true; } bool acceptSnapshot(const QString& urlString); bool askToSetAvatarUrl(const QString& url); @@ -710,6 +712,7 @@ private: bool _isForeground = true; // starts out assumed to be in foreground bool _isGLInitialized { false }; bool _physicsEnabled { false }; + bool _failedToConnectToEntityServer { false }; bool _reticleClickPressed { false }; @@ -756,6 +759,7 @@ private: QStringList _addAssetToWorldInfoMessages; // Info message QTimer _addAssetToWorldInfoTimer; QTimer _addAssetToWorldErrorTimer; + mutable QTimer _entityServerConnectionTimer; FileScriptingInterface* _fileDownload; AudioInjectorPointer _snapshotSoundInjector; diff --git a/interface/src/octree/SafeLanding.cpp b/interface/src/octree/SafeLanding.cpp index ff32e03006..9efad22d09 100644 --- a/interface/src/octree/SafeLanding.cpp +++ b/interface/src/octree/SafeLanding.cpp @@ -11,9 +11,7 @@ #include "SafeLanding.h" -#include #include -#include #include "EntityTreeRenderer.h" #include "RenderableModelEntityItem.h" @@ -21,7 +19,6 @@ #include "Application.h" const int SafeLanding::SEQUENCE_MODULO = std::numeric_limits::max() + 1; -const quint64 MAX_ELAPSED_TIME = 1000; // msec namespace { template bool lessThanWraparound(int a, int b) { @@ -110,9 +107,7 @@ void SafeLanding::noteReceivedsequenceNumber(int sequenceNumber) { } bool SafeLanding::isLoadSequenceComplete() { - quint64 elapsedTime = (usecTimestampNow() - _startTime) / USECS_PER_MSEC; - if ((isEntityLoadingComplete() && isSequenceNumbersComplete()) || - (elapsedTime >= MAX_ELAPSED_TIME && isEntityServerNotRunning() && _sequenceNumbers.empty())) { + if ((isEntityLoadingComplete() && isSequenceNumbersComplete()) || qApp->failedToConnectToEntityServer()) { Locker lock(_lock); _initialStart = INVALID_SEQUENCE; _initialEnd = INVALID_SEQUENCE; @@ -124,14 +119,6 @@ bool SafeLanding::isLoadSequenceComplete() { return !_trackingEntities; } -bool SafeLanding::isEntityServerNotRunning() { - auto nodeList = DependencyManager::get(); - const auto& domainHandler = nodeList->getDomainHandler(); - auto entityServer = nodeList->soloNodeOfType(NodeType::EntityServer); - - return (domainHandler.isConnected() && !entityServer); -} - float SafeLanding::loadingProgressPercentage() { Locker lock(_lock); static const int MINIMUM_TRACKED_ENTITY_STABILITY_COUNT = 15; diff --git a/interface/src/octree/SafeLanding.h b/interface/src/octree/SafeLanding.h index 157fda53aa..51357b60ff 100644 --- a/interface/src/octree/SafeLanding.h +++ b/interface/src/octree/SafeLanding.h @@ -40,7 +40,6 @@ private: bool isSequenceNumbersComplete(); void debugDumpSequenceIDs() const; bool isEntityLoadingComplete(); - bool isEntityServerNotRunning(); std::mutex _lock; using Locker = std::lock_guard; From 3bf4a7844d6d17b93e54f12f072b55d7796c6380 Mon Sep 17 00:00:00 2001 From: amantley Date: Thu, 15 Nov 2018 10:56:17 -0800 Subject: [PATCH 254/286] moved the jointrotationoffsets to hfmModel.h from fbx.h --- libraries/hfm/src/hfm/HFM.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libraries/hfm/src/hfm/HFM.h b/libraries/hfm/src/hfm/HFM.h index 7d4479e681..05e48b6534 100644 --- a/libraries/hfm/src/hfm/HFM.h +++ b/libraries/hfm/src/hfm/HFM.h @@ -311,6 +311,8 @@ public: QString getModelNameOfMesh(int meshIndex) const; QList blendshapeChannelNames; + + QMap jointRotationOffsets; }; }; From 34224c4f16382ae0a3faf311fca388302bc7067d Mon Sep 17 00:00:00 2001 From: r3tk0n Date: Thu, 15 Nov 2018 14:08:21 -0800 Subject: [PATCH 255/286] Fix XBox One controller detection. --- libraries/plugins/src/plugins/PluginUtils.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/plugins/src/plugins/PluginUtils.cpp b/libraries/plugins/src/plugins/PluginUtils.cpp index ce67f7c585..f6419ccba0 100644 --- a/libraries/plugins/src/plugins/PluginUtils.cpp +++ b/libraries/plugins/src/plugins/PluginUtils.cpp @@ -65,6 +65,6 @@ bool PluginUtils::isOculusTouchControllerAvailable() { }; bool PluginUtils::isXboxControllerAvailable() { - return isSubdeviceContainingNameAvailable("X360 Controller"); + return isSubdeviceContainingNameAvailable("X360 Controller") || isSubdeviceContainingNameAvailable("XInput Controller"); }; From 60832b071cb8088ec432131b464807e9d3c56b33 Mon Sep 17 00:00:00 2001 From: r3tk0n Date: Thu, 15 Nov 2018 14:08:38 -0800 Subject: [PATCH 256/286] Fix logic for cases with rift headset but no touch controllers. --- interface/src/Application.cpp | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index aec580ff9d..a4277d6132 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -3414,10 +3414,18 @@ void Application::showHelp() { defaultTab = TAB_HAND_CONTROLLERS; handControllerName = HAND_CONTROLLER_NAME_VIVE; } else if (PluginUtils::isHMDAvailable(OCULUS_RIFT_PLUGIN_NAME)) { - defaultTab = TAB_HAND_CONTROLLERS; - handControllerName = HAND_CONTROLLER_NAME_OCULUS_TOUCH; + if (PluginUtils::isOculusTouchControllerAvailable()) { + defaultTab = TAB_HAND_CONTROLLERS; + handControllerName = HAND_CONTROLLER_NAME_OCULUS_TOUCH; + } else if (PluginUtils::isXboxControllerAvailable()) { + defaultTab = TAB_GAMEPAD; + } else { + defaultTab = TAB_KEYBOARD_MOUSE; + } } else if (PluginUtils::isXboxControllerAvailable()) { defaultTab = TAB_GAMEPAD; + } else { + defaultTab = TAB_KEYBOARD_MOUSE; } QUrlQuery queryString; From de7f4a4a4d8d4578d38416d2308a91d80a545a25 Mon Sep 17 00:00:00 2001 From: David Back Date: Thu, 15 Nov 2018 15:23:18 -0800 Subject: [PATCH 257/286] fix entity list resizing --- scripts/system/html/js/entityList.js | 2 +- scripts/system/html/js/listView.js | 20 +++++--------------- 2 files changed, 6 insertions(+), 16 deletions(-) diff --git a/scripts/system/html/js/entityList.js b/scripts/system/html/js/entityList.js index 00c50169a6..39673b8f16 100644 --- a/scripts/system/html/js/entityList.js +++ b/scripts/system/html/js/entityList.js @@ -1219,7 +1219,7 @@ function loaded() { refreshSortOrder(); refreshEntities(); - window.onresize = updateColumnWidths; + window.addEventListener("resize", updateColumnWidths); }); augmentSpinButtons(); diff --git a/scripts/system/html/js/listView.js b/scripts/system/html/js/listView.js index 07aba53f1c..f775a4cb24 100644 --- a/scripts/system/html/js/listView.js +++ b/scripts/system/html/js/listView.js @@ -72,11 +72,6 @@ ListView.prototype = { } }, - onScroll: function() { - var that = this.listView; - that.scroll(); - }, - scroll: function() { let scrollTop = this.elTableScroll.scrollTop; let scrollHeight = this.getScrollHeight(); @@ -244,18 +239,13 @@ ListView.prototype = { } }, - onResize: function() { - var that = this.listView; - that.resize(); - }, - resize: function() { if (!this.elTableBody || !this.elTableScroll) { debugPrint("ListView.resize - no valid table body or table scroll element"); return; } - let prevScrollTop = this.elTableScroll.scrollTop; + let prevScrollTop = this.elTableScroll.scrollTop; // take up available window space this.elTableScroll.style.height = window.innerHeight - WINDOW_NONVARIABLE_HEIGHT; @@ -305,10 +295,10 @@ ListView.prototype = { this.elTableBody.appendChild(this.elBottomBuffer); this.elBottomBuffer.setAttribute("height", 0); - this.elTableScroll.listView = this; - this.elTableScroll.onscroll = this.onScroll; - window.listView = this; - window.onresize = this.onResize; + this.onScroll = this.scroll.bind(this); + this.elTableScroll.addEventListener("scroll", this.onScroll); + this.onResize = this.resize.bind(this); + window.addEventListener("resize", this.onResize); // initialize all row elements this.resize(); From 556aec94121babbc47a03e1d38ff052bffd29a1a Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 15 Nov 2018 15:27:30 -0800 Subject: [PATCH 258/286] Fix hiding of properties in Create --- scripts/system/html/css/edit-style.css | 12 +++++++++++- scripts/system/html/js/entityProperties.js | 14 ++++++++------ 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/scripts/system/html/css/edit-style.css b/scripts/system/html/css/edit-style.css index 3a291bf27b..bec926ad03 100644 --- a/scripts/system/html/css/edit-style.css +++ b/scripts/system/html/css/edit-style.css @@ -1559,7 +1559,9 @@ input.rename-entity { .container > label { margin-top: 6px; - width: 200px; + width: 160px; + min-width: 160px; + max-width: 160px; } .container > div.checkbox { @@ -1650,3 +1652,11 @@ input.number-slider { display: flex; width: 100%; } + +.spacemode-hidden { + display: none; +} + +#placeholder-property-type { + min-width: 0px; +} diff --git a/scripts/system/html/js/entityProperties.js b/scripts/system/html/js/entityProperties.js index c49e0d88f6..a4cde98abe 100644 --- a/scripts/system/html/js/entityProperties.js +++ b/scripts/system/html/js/entityProperties.js @@ -29,7 +29,7 @@ const ICON_FOR_TYPE = { const DEGREES_TO_RADIANS = Math.PI / 180.0; -const NO_SELECTION = "w"; +const NO_SELECTION = ","; const PROPERTY_SPACE_MODE = { ALL: 0, @@ -1244,8 +1244,9 @@ const GROUPS = [ showPropertyRule: { "collisionless": "false" }, }, { - label: "Collision sound URL", + label: "Collision Sound", type: "string", + placeholder: "URL", propertyID: "collisionSoundURL", showPropertyRule: { "collisionless": "false" }, }, @@ -1500,7 +1501,7 @@ function disableProperties() { function showPropertyElement(propertyID, show) { let elProperty = properties[propertyID].elContainer; - elProperty.style.display = show ? "flex" : "none"; + elProperty.style.display = show ? "" : "none"; } function resetProperties() { @@ -1622,10 +1623,11 @@ function updateVisibleSpaceModeProperties() { if (properties.hasOwnProperty(propertyID)) { let property = properties[propertyID]; let propertySpaceMode = property.spaceMode; - if (propertySpaceMode !== PROPERTY_SPACE_MODE.ALL) { - showPropertyElement(propertyID, propertySpaceMode === currentSpaceMode); + let elProperty = properties[propertyID].elContainer; + if (propertySpaceMode !== PROPERTY_SPACE_MODE.ALL && propertySpaceMode !== currentSpaceMode) { + elProperty.classList.add('spacemode-hidden'); } else { - showPropertyElement(propertyID, true); + elProperty.classList.remove('spacemode-hidden'); } } } From 8e1785bd909f4f2173b35d418d201c37be5a3966 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Thu, 15 Nov 2018 15:59:52 -0800 Subject: [PATCH 259/286] security settings post-oculus-store wording --- interface/resources/qml/hifi/dialogs/security/Security.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/resources/qml/hifi/dialogs/security/Security.qml b/interface/resources/qml/hifi/dialogs/security/Security.qml index b754cb06ab..5c52af1c05 100644 --- a/interface/resources/qml/hifi/dialogs/security/Security.qml +++ b/interface/resources/qml/hifi/dialogs/security/Security.qml @@ -256,7 +256,7 @@ Rectangle { color: hifi.colors.baseGrayHighlight; HifiStylesUit.RalewaySemiBold { - text: "Wallet"; + text: "Secure Transactions"; anchors.fill: parent; anchors.leftMargin: 20; color: hifi.colors.white; @@ -287,7 +287,7 @@ Rectangle { HifiStylesUit.RalewaySemiBold { id: securityPictureText; - text: "Wallet Security Picture"; + text: "Security Picture"; // Anchors anchors.top: parent.top; anchors.bottom: parent.bottom; From 22c3f5239adbc48fbd3ed05b4617a8f09cd1a0b1 Mon Sep 17 00:00:00 2001 From: Simon Walton Date: Thu, 15 Nov 2018 16:59:52 -0800 Subject: [PATCH 260/286] Relay upoaded content.zip chunks to temp file Entities uploads still build in-memory. Move out chunk handling to new routine. --- .../resources/web/content/js/content.js | 4 +- .../src/DomainContentBackupManager.cpp | 21 +++++ .../src/DomainContentBackupManager.h | 1 + domain-server/src/DomainServer.cpp | 77 ++++++++++--------- domain-server/src/DomainServer.h | 3 +- 5 files changed, 67 insertions(+), 39 deletions(-) diff --git a/domain-server/resources/web/content/js/content.js b/domain-server/resources/web/content/js/content.js index 78e78266d3..365c5e8403 100644 --- a/domain-server/resources/web/content/js/content.js +++ b/domain-server/resources/web/content/js/content.js @@ -60,7 +60,7 @@ $(document).ready(function(){ ); }); - updateProgressBars($('.upload-content-progress'), Math.round((offset + nextChunkSize) * 100 / fileSize)); + updateProgressBars($('.upload-content-progress'), (offset + nextChunkSize) * 100 / fileSize); if (!isFinal) { ajaxObject.done(function (data, textStatus, jqXHR) @@ -210,7 +210,7 @@ $(document).ready(function(){ function updateProgressBars($progressBar, value) { $progressBar.attr('aria-valuenow', value).attr('style', 'width: ' + value + '%'); - $progressBar.find('.ongoing-msg').html(" " + value + "% Complete"); + $progressBar.find('.ongoing-msg').html(" " + Math.round(value) + "% Complete"); } function reloadBackupInformation() { diff --git a/domain-server/src/DomainContentBackupManager.cpp b/domain-server/src/DomainContentBackupManager.cpp index 518ed73f9e..3b8180e49e 100644 --- a/domain-server/src/DomainContentBackupManager.cpp +++ b/domain-server/src/DomainContentBackupManager.cpp @@ -348,6 +348,27 @@ void DomainContentBackupManager::recoverFromUploadedBackup(MiniPromise::Promise }); } +void DomainContentBackupManager::recoverFromUploadedFile(MiniPromise::Promise promise, QString uploadedFilename) { + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(this, "recoverFromUploadedFile", Q_ARG(MiniPromise::Promise, promise), + Q_ARG(QString, uploadedFilename)); + return; + } + + qDebug() << "Recovering from uploaded file -" << uploadedFilename; + + QFile uploadedFile(uploadedFilename); + QuaZip uploadedZip { &uploadedFile }; + + QString backupName = MANUAL_BACKUP_PREFIX + "uploaded.zip"; + + bool success = recoverFromBackupZip(backupName, uploadedZip); + + promise->resolve({ + { "success", success } + }); +} + std::vector DomainContentBackupManager::getAllBackups() { QDir backupDir { _backupDirectory }; diff --git a/domain-server/src/DomainContentBackupManager.h b/domain-server/src/DomainContentBackupManager.h index 2b07afe0b3..4af3ae5bfd 100644 --- a/domain-server/src/DomainContentBackupManager.h +++ b/domain-server/src/DomainContentBackupManager.h @@ -86,6 +86,7 @@ public slots: void createManualBackup(MiniPromise::Promise promise, const QString& name); void recoverFromBackup(MiniPromise::Promise promise, const QString& backupName); void recoverFromUploadedBackup(MiniPromise::Promise promise, QByteArray uploadedBackup); + void recoverFromUploadedFile(MiniPromise::Promise promise, QString uploadedFilename); void deleteBackup(MiniPromise::Promise promise, const QString& backupName); signals: diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index 117313462b..3e68cd0fc4 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -2268,17 +2268,8 @@ bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url uploadedFilename = formDataFieldsRegex.cap(2); } - _pendingUploadedContent += firstFormData.second; - if (formItemName == "restore-file-chunk") { - // Received another chunk - connection->respond(HTTPConnection::StatusCode200); - } else if (formItemName == "restore-file-chunk-final") { - readPendingContent(connection, uploadedFilename); - } else if (formItemName == "restore-file") { - readPendingContent(connection, uploadedFilename); - } else { - connection->respond(HTTPConnection::StatusCode400); - } + // Received a chunk + processPendingContent(connection, formItemName, uploadedFilename, firstFormData.second); } else { // respond with a 400 for failure connection->respond(HTTPConnection::StatusCode400); @@ -2527,37 +2518,51 @@ bool DomainServer::handleHTTPSRequest(HTTPSConnection* connection, const QUrl &u } } -void DomainServer::readPendingContent(HTTPConnection* connection, QString filename) { - if (filename.endsWith(".json", Qt::CaseInsensitive) - || filename.endsWith(".json.gz", Qt::CaseInsensitive)) { - // invoke our method to hand the new octree file off to the octree server - QMetaObject::invokeMethod(this, "handleOctreeFileReplacement", - Qt::QueuedConnection, Q_ARG(QByteArray, _pendingUploadedContent)); +bool DomainServer::processPendingContent(HTTPConnection* connection, QString itemName, QString filename, QByteArray dataChunk) { + if (filename.endsWith(".zip", Qt::CaseInsensitive)) { + static const QString TEMPORARY_CONTENT_FILEPATH { QDir::tempPath() + "/hifiUploadContent_XXXXXX.zip" }; - // respond with a 200 for success + if (!_pendingFileContent) { + _pendingFileContent = std::make_unique(TEMPORARY_CONTENT_FILEPATH); + } + if (!_pendingFileContent->open()) { + _pendingFileContent = nullptr; + connection->respond(HTTPConnection::StatusCode400); + return false; + } + _pendingFileContent->seek(_pendingFileContent->size()); + _pendingFileContent->write(dataChunk); + _pendingFileContent->close(); + + // Respond immediately - will timeout if we wait for restore. connection->respond(HTTPConnection::StatusCode200); - _pendingUploadedContent.clear(); - } else if (filename.endsWith(".zip", Qt::CaseInsensitive)) { - auto deferred = makePromise("recoverFromUploadedBackup"); + if (itemName == "restore-file-chunk-final" || itemName == "restore-file") { + auto deferred = makePromise("recoverFromUploadedBackup"); - QPointer connectionPtr(connection); - const QString JSON_MIME_TYPE = "application/json"; - deferred->then([connectionPtr, JSON_MIME_TYPE, this](QString error, QVariantMap result) { - if (!connectionPtr) { - return; - } + deferred->then([this](QString error, QVariantMap result) { + _pendingFileContent = nullptr; + }); - QJsonObject rootJSON; - auto success = result["success"].toBool(); - rootJSON["success"] = success; - QJsonDocument docJSON(rootJSON); - connectionPtr->respond(success ? HTTPConnection::StatusCode200 : HTTPConnection::StatusCode400, docJSON.toJson(), - JSON_MIME_TYPE.toUtf8()); + _contentManager->recoverFromUploadedFile(deferred, _pendingFileContent->fileName()); + } else { + } + } else if (filename.endsWith(".json", Qt::CaseInsensitive) + || filename.endsWith(".json.gz", Qt::CaseInsensitive)) { + _pendingUploadedContent += dataChunk; + connection->respond(HTTPConnection::StatusCode200); + + if (itemName == "restore-file-chunk-final" || itemName == "restore-file") { + // invoke our method to hand the new octree file off to the octree server + QMetaObject::invokeMethod(this, "handleOctreeFileReplacement", + Qt::QueuedConnection, Q_ARG(QByteArray, _pendingUploadedContent)); _pendingUploadedContent.clear(); - }); - - _contentManager->recoverFromUploadedBackup(deferred, _pendingUploadedContent); + } + } else { + connection->respond(HTTPConnection::StatusCode400); + return false; } + + return true; } HTTPSConnection* DomainServer::connectionFromReplyWithState(QNetworkReply* reply) { diff --git a/domain-server/src/DomainServer.h b/domain-server/src/DomainServer.h index b2ef933bc3..73e23bb3fe 100644 --- a/domain-server/src/DomainServer.h +++ b/domain-server/src/DomainServer.h @@ -209,7 +209,7 @@ private: HTTPSConnection* connectionFromReplyWithState(QNetworkReply* reply); - void readPendingContent(HTTPConnection* connection, QString filename); + bool processPendingContent(HTTPConnection* connection, QString itemName, QString filename, QByteArray dataChunk); bool forwardMetaverseAPIRequest(HTTPConnection* connection, const QString& metaversePath, @@ -284,6 +284,7 @@ private: QHash> _pendingOAuthConnections; QByteArray _pendingUploadedContent; + std::unique_ptr _pendingFileContent; QThread _assetClientThread; }; From bb0f046a728bd30d4f157adb80bf791c5483d93e Mon Sep 17 00:00:00 2001 From: birarda Date: Thu, 15 Nov 2018 17:28:34 -0800 Subject: [PATCH 261/286] add throttle start and backoff to DS settings --- assignment-client/src/audio/AudioMixer.cpp | 15 ++++++++++----- assignment-client/src/audio/AudioMixer.h | 4 +++- domain-server/resources/describe-settings.json | 18 ++++++++++++++++++ 3 files changed, 31 insertions(+), 6 deletions(-) diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp index d6f893c42e..5d203595c2 100644 --- a/assignment-client/src/audio/AudioMixer.cpp +++ b/assignment-client/src/audio/AudioMixer.cpp @@ -488,11 +488,8 @@ void AudioMixer::throttle(chrono::microseconds duration, int frame) { // target different mix and backoff ratios (they also have different backoff rates) // this is to prevent oscillation, and encourage throttling to find a steady state - const float TARGET = 0.9f; - // on a "regular" machine with 100 avatars, this is the largest value where - // - overthrottling can be recovered - // - oscillations will not occur after the recovery - const float BACKOFF_TARGET = 0.44f; + const float TARGET = _throttleStartTarget; + const float BACKOFF_TARGET = _throttleBackoffTarget; // the mixer is known to struggle at about 80 on a "regular" machine // so throttle 2/80 the streams to ensure smooth audio (throttling is linear) @@ -551,6 +548,14 @@ void AudioMixer::parseSettingsObject(const QJsonObject& settingsObject) { _slavePool.setNumThreads(numThreads); } } + + const QString THROTTLE_START_KEY = "throttle_start"; + const QString THROTTLE_BACKOFF_KEY = "throttle_backoff"; + + _throttleStartTarget = audioThreadingGroupObject[THROTTLE_START_KEY].toDouble(_throttleStartTarget); + _throttleBackoffTarget = audioThreadingGroupObject[THROTTLE_BACKOFF_KEY].toDouble(_throttleBackoffTarget); + + qCDebug(audio) << "Throttle Start:" << _throttleStartTarget << "Throttle Backoff:" << _throttleBackoffTarget; } if (settingsObject.contains(AUDIO_BUFFER_GROUP_KEY)) { diff --git a/assignment-client/src/audio/AudioMixer.h b/assignment-client/src/audio/AudioMixer.h index b8ea0d5c58..c7726d2fea 100644 --- a/assignment-client/src/audio/AudioMixer.h +++ b/assignment-client/src/audio/AudioMixer.h @@ -144,11 +144,13 @@ private: static std::map _availableCodecs; static QStringList _codecPreferenceOrder; - static std::vector _audioZones; static std::vector _zoneSettings; static std::vector _zoneReverbSettings; + float _throttleStartTarget = 0.9f; + float _throttleBackoffTarget = 0.44f; + AudioMixerSlave::SharedData _workerSharedData; }; diff --git a/domain-server/resources/describe-settings.json b/domain-server/resources/describe-settings.json index 5d1f725ab4..49023c9af8 100644 --- a/domain-server/resources/describe-settings.json +++ b/domain-server/resources/describe-settings.json @@ -1012,6 +1012,24 @@ "placeholder": "1", "default": "1", "advanced": true + }, + { + "name": "throttle_start", + "type": "double", + "label": "Throttle Start Target", + "help": "Target percentage of frame time to start throttling", + "placeholder": "0.9", + "default": 0.9, + "advanced": true + }, + { + "name": "throttle_backoff", + "type": "double", + "label": "Throttle Backoff Target", + "help": "Target percentage of frame time to backoff throttling", + "placeholder": "0.44", + "default": 0.44, + "advanced": true } ] }, From 7fba4c467322a937762db20e905ce62fba315a8f Mon Sep 17 00:00:00 2001 From: NissimHadar Date: Thu, 15 Nov 2018 17:41:01 -0800 Subject: [PATCH 262/286] Deleted unneeded file. --- .../AppDataHighFidelity/Interface/AccountInfo.bin | Bin 489 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 tools/nitpick/AppDataHighFidelity/Interface/AccountInfo.bin diff --git a/tools/nitpick/AppDataHighFidelity/Interface/AccountInfo.bin b/tools/nitpick/AppDataHighFidelity/Interface/AccountInfo.bin deleted file mode 100644 index 65c971ea793926e8f7518d36c6079a59b4cc08e8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 489 zcmZwDIZgvX6a>(RL`XzHz#(8ZGh;F&5@N{FEZx`=U}O(SWQ3fJvv3Dcw;T{q(O;|T ze;nZ2%53&x=hC@zPl8$RO|Z2SoLO0WuoA6Jf=6q!cV|W$PaH}(x$fuwHf-O*_G%nA zug~+%bKIoPhf&apCcROkQCf8hNg_vhkUa5_0l~HGDQko1ZiyTL-sL>kK znJMirv{|oHyD*6y#LuteZyXr}y=^`)c}q-5WyrjPP9q9hg-($@q>Wmg)jJm&efAoZ zd-b5o4i}jz`AWUYq}a!$LZQw-XjJ){#)wl_Ud=Y{-3fN)?CxIqvq%2$AEff1^k1Zz EU+;ia(EtDd From 6440c13bfe9a6ad70549d53246dff5a0a92c9d40 Mon Sep 17 00:00:00 2001 From: NissimHadar Date: Thu, 15 Nov 2018 18:50:56 -0800 Subject: [PATCH 263/286] Updated manual. --- tools/nitpick/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/nitpick/README.md b/tools/nitpick/README.md index d21614f5f5..e8129ae29e 100644 --- a/tools/nitpick/README.md +++ b/tools/nitpick/README.md @@ -14,7 +14,7 @@ Nitpick has 5 functions, separated into 4 tabs: ## Installation ### Executable -1. On Windows: download the installer by browsing to [here](). +1. On Windows: download the installer by browsing to [here](). 2. Double click on the installer and install to a convenient location ![](./setup_7z.PNG) 3. To run nitpick, double click **nitpick.exe**. From 5f51ed0210b24393098f9cd46620db07bb1eda8c Mon Sep 17 00:00:00 2001 From: Simon Walton Date: Fri, 16 Nov 2018 09:44:21 -0800 Subject: [PATCH 264/286] Keep pending content per remote address --- domain-server/src/DomainServer.cpp | 28 ++++++++++++++++------------ domain-server/src/DomainServer.h | 5 +++-- 2 files changed, 19 insertions(+), 14 deletions(-) diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index 3e68cd0fc4..670e179f81 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -2521,33 +2521,37 @@ bool DomainServer::handleHTTPSRequest(HTTPSConnection* connection, const QUrl &u bool DomainServer::processPendingContent(HTTPConnection* connection, QString itemName, QString filename, QByteArray dataChunk) { if (filename.endsWith(".zip", Qt::CaseInsensitive)) { static const QString TEMPORARY_CONTENT_FILEPATH { QDir::tempPath() + "/hifiUploadContent_XXXXXX.zip" }; + const auto peerAddressHash = qHash(connection->socket()->peerAddress()); - if (!_pendingFileContent) { - _pendingFileContent = std::make_unique(TEMPORARY_CONTENT_FILEPATH); + if (_pendingContentFiles.find(peerAddressHash) == _pendingContentFiles.end()) { + _pendingContentFiles.emplace(peerAddressHash, TEMPORARY_CONTENT_FILEPATH); } - if (!_pendingFileContent->open()) { - _pendingFileContent = nullptr; + + QTemporaryFile& _pendingFileContent = _pendingContentFiles[peerAddressHash]; + if (!_pendingFileContent.open()) { + _pendingContentFiles.erase(peerAddressHash); connection->respond(HTTPConnection::StatusCode400); return false; } - _pendingFileContent->seek(_pendingFileContent->size()); - _pendingFileContent->write(dataChunk); - _pendingFileContent->close(); + _pendingFileContent.seek(_pendingFileContent.size()); + _pendingFileContent.write(dataChunk); + _pendingFileContent.close(); // Respond immediately - will timeout if we wait for restore. connection->respond(HTTPConnection::StatusCode200); if (itemName == "restore-file-chunk-final" || itemName == "restore-file") { auto deferred = makePromise("recoverFromUploadedBackup"); - deferred->then([this](QString error, QVariantMap result) { - _pendingFileContent = nullptr; + deferred->then([this, peerAddressHash](QString error, QVariantMap result) { + _pendingContentFiles.erase(peerAddressHash); }); - _contentManager->recoverFromUploadedFile(deferred, _pendingFileContent->fileName()); - } else { + _contentManager->recoverFromUploadedFile(deferred, _pendingFileContent.fileName()); } } else if (filename.endsWith(".json", Qt::CaseInsensitive) || filename.endsWith(".json.gz", Qt::CaseInsensitive)) { + auto peerAddressHash = qHash(connection->socket()->peerAddress()); + QByteArray& _pendingUploadedContent = _pendingUploadedContents[peerAddressHash]; _pendingUploadedContent += dataChunk; connection->respond(HTTPConnection::StatusCode200); @@ -2555,7 +2559,7 @@ bool DomainServer::processPendingContent(HTTPConnection* connection, QString ite // invoke our method to hand the new octree file off to the octree server QMetaObject::invokeMethod(this, "handleOctreeFileReplacement", Qt::QueuedConnection, Q_ARG(QByteArray, _pendingUploadedContent)); - _pendingUploadedContent.clear(); + _pendingUploadedContents.erase(peerAddressHash); } } else { connection->respond(HTTPConnection::StatusCode400); diff --git a/domain-server/src/DomainServer.h b/domain-server/src/DomainServer.h index 73e23bb3fe..2d5729be59 100644 --- a/domain-server/src/DomainServer.h +++ b/domain-server/src/DomainServer.h @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -283,8 +284,8 @@ private: QHash> _pendingOAuthConnections; - QByteArray _pendingUploadedContent; - std::unique_ptr _pendingFileContent; + std::unordered_map _pendingUploadedContents; + std::unordered_map _pendingContentFiles; QThread _assetClientThread; }; From 6daac008ff660ce8a6db9651d6257441802b7812 Mon Sep 17 00:00:00 2001 From: birarda Date: Fri, 16 Nov 2018 11:50:13 -0800 Subject: [PATCH 265/286] sanitize throttle start and backoff values from DS settings --- assignment-client/src/audio/AudioMixer.cpp | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp index 5d203595c2..e8568a7ff3 100644 --- a/assignment-client/src/audio/AudioMixer.cpp +++ b/assignment-client/src/audio/AudioMixer.cpp @@ -552,8 +552,18 @@ void AudioMixer::parseSettingsObject(const QJsonObject& settingsObject) { const QString THROTTLE_START_KEY = "throttle_start"; const QString THROTTLE_BACKOFF_KEY = "throttle_backoff"; - _throttleStartTarget = audioThreadingGroupObject[THROTTLE_START_KEY].toDouble(_throttleStartTarget); - _throttleBackoffTarget = audioThreadingGroupObject[THROTTLE_BACKOFF_KEY].toDouble(_throttleBackoffTarget); + float settingsThrottleStart = audioThreadingGroupObject[THROTTLE_START_KEY].toDouble(_throttleStartTarget); + float settingsThrottleBackoff = audioThreadingGroupObject[THROTTLE_BACKOFF_KEY].toDouble(_throttleBackoffTarget); + + if (settingsThrottleBackoff > settingsThrottleStart) { + qCWarning(audio) << "Throttle backoff target cannot be higher than throttle start target. Using default values."; + } else if (settingsThrottleBackoff < 0.0f || settingsThrottleStart > 1.0f) { + qCWarning(audio) << "Throttle start and backoff targets must be greater than or equal to 0.0" + << "and lesser than or equal to 1.0. Using default values."; + } else { + _throttleStartTarget = settingsThrottleStart; + _throttleBackoffTarget = settingsThrottleBackoff; + } qCDebug(audio) << "Throttle Start:" << _throttleStartTarget << "Throttle Backoff:" << _throttleBackoffTarget; } From 31d37e7e75569d1c5c33ef97085539ea1b4afa0a Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Thu, 15 Nov 2018 02:38:39 +0100 Subject: [PATCH 266/286] Fix entity list double click rename / highlight issue --- scripts/system/html/js/entityList.js | 32 +++++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/scripts/system/html/js/entityList.js b/scripts/system/html/js/entityList.js index 39673b8f16..ac2a4132ae 100644 --- a/scripts/system/html/js/entityList.js +++ b/scripts/system/html/js/entityList.js @@ -153,6 +153,9 @@ const ICON_FOR_TYPE = { Text: "l", }; +const DOUBLE_CLICK_TIMEOUT = 300; // ms +const RENAME_COOLDOWN = 400; // ms + // List of all entities let entities = []; // List of all entities, indexed by Entity ID @@ -181,6 +184,9 @@ let currentResizeEl = null; let startResizeEvent = null; let resizeColumnIndex = 0; let startThClick = null; +let renameTimeout = null; +let renameLastBlur = null; +let renameLastEntityID = null; let elEntityTable, elEntityTableHeader, @@ -394,6 +400,7 @@ function loaded() { entityListContextMenu = new EntityListContextMenu(); function startRenamingEntity(entityID) { + renameLastEntityID = entityID; let entity = entitiesByID[entityID]; if (!entity || entity.locked || !entity.elRow) { return; @@ -423,6 +430,8 @@ function loaded() { })); entity.name = value; elCell.innerText = value; + + renameLastBlur = Date.now(); }; elCell.innerHTML = ""; @@ -478,6 +487,13 @@ function loaded() { entityListContextMenu.open(clickEvent, entityID, enabledContextMenuItems); } + let clearRenameTimeout = () => { + if (renameTimeout !== null) { + window.clearTimeout(renameTimeout); + renameTimeout = null; + } + }; + function onRowClicked(clickEvent) { let entityID = this.dataset.entityID; let selection = [entityID]; @@ -516,7 +532,15 @@ function loaded() { } else if (!clickEvent.ctrlKey && !clickEvent.shiftKey && selectedEntities.length === 1) { // if reselecting the same entity then start renaming it if (selectedEntities[0] === entityID) { - startRenamingEntity(entityID); + if (renameLastBlur && renameLastEntityID === entityID && (Date.now() - renameLastBlur) < RENAME_COOLDOWN) { + + return; + } + clearRenameTimeout(); + renameTimeout = window.setTimeout(() => { + renameTimeout = null; + startRenamingEntity(entityID); + }, DOUBLE_CLICK_TIMEOUT); } } @@ -530,6 +554,8 @@ function loaded() { } function onRowDoubleClicked() { + clearRenameTimeout(); + let selection = [this.dataset.entityID]; updateSelectedEntities(selection, false); @@ -1100,12 +1126,12 @@ function loaded() { startResizeEvent = ev; } } - } + }; document.onmouseup = function(ev) { startResizeEvent = null; ev.stopPropagation(); - } + }; function setSpaceMode(spaceMode) { if (spaceMode === "local") { From e89346c146386a17cc08143fcd113a569152690d Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Fri, 16 Nov 2018 20:02:16 +0100 Subject: [PATCH 267/286] making rename functionality list refresh proof --- scripts/system/html/js/entityList.js | 45 +++++++++++++++++++++++--- scripts/system/html/js/listView.js | 8 +++-- scripts/system/libraries/entityList.js | 2 +- 3 files changed, 48 insertions(+), 7 deletions(-) diff --git a/scripts/system/html/js/entityList.js b/scripts/system/html/js/entityList.js index ac2a4132ae..328c8c76e1 100644 --- a/scripts/system/html/js/entityList.js +++ b/scripts/system/html/js/entityList.js @@ -187,6 +187,7 @@ let startThClick = null; let renameTimeout = null; let renameLastBlur = null; let renameLastEntityID = null; +let isRenameFieldIsBeingMoved = false; let elEntityTable, elEntityTableHeader, @@ -210,7 +211,8 @@ let elEntityTable, elNoEntitiesMessage, elColumnsMultiselectBox, elColumnsOptions, - elToggleSpaceMode; + elToggleSpaceMode, + elRenameInput; const ENABLE_PROFILING = false; let profileIndent = ''; @@ -395,7 +397,7 @@ function loaded() { elEntityTableHeaderRow = document.querySelectorAll("#entity-table thead th"); entityList = new ListView(elEntityTableBody, elEntityTableScroll, elEntityTableHeaderRow, - createRow, updateRow, clearRow, WINDOW_NONVARIABLE_HEIGHT); + createRow, updateRow, clearRow, beforeUpdate, afterUpdate, WINDOW_NONVARIABLE_HEIGHT); entityListContextMenu = new EntityListContextMenu(); @@ -407,7 +409,7 @@ function loaded() { } let elCell = entity.elRow.childNodes[getColumnIndex("name")]; - let elRenameInput = document.createElement("input"); + elRenameInput = document.createElement("input"); elRenameInput.setAttribute('class', 'rename-entity'); elRenameInput.value = entity.name; let ignoreClicks = function(event) { @@ -422,6 +424,9 @@ function loaded() { }; elRenameInput.onblur = function(event) { + if (isRenameFieldIsBeingMoved) { + return; + } let value = elRenameInput.value; EventBridge.emitWebEvent(JSON.stringify({ type: 'rename', @@ -429,9 +434,10 @@ function loaded() { name: value })); entity.name = value; - elCell.innerText = value; + elRenameInput.parentElement.innerText = value; renameLastBlur = Date.now(); + elRenameInput = null; }; elCell.innerHTML = ""; @@ -440,6 +446,32 @@ function loaded() { elRenameInput.select(); } + function beforeUpdate() { + // move the rename input to the body + if (elRenameInput) { + isRenameFieldIsBeingMoved = true; + document.body.appendChild(elRenameInput); + // keep the focus + elRenameInput.select(); + } + } + + function afterUpdate() { + if (!elRenameInput || !isRenameFieldIsBeingMoved) { + return; + } + let entity = entitiesByID[renameLastEntityID]; + if (!entity || entity.locked || !entity.elRow) { + return; + } + let elCell = entity.elRow.childNodes[getColumnIndex("name")]; + elCell.innerHTML = ""; + elCell.appendChild(elRenameInput); + // keep the focus + elRenameInput.select(); + isRenameFieldIsBeingMoved = false; + } + entityListContextMenu.setOnSelectedCallback(function(optionName, selectedEntityID) { switch (optionName) { case "Cut": @@ -464,6 +496,11 @@ function loaded() { }); function onRowContextMenu(clickEvent) { + if (elRenameInput) { + // disallow the context menu from popping up while renaming + return; + } + let entityID = this.dataset.entityID; if (!selectedEntities.includes(entityID)) { diff --git a/scripts/system/html/js/listView.js b/scripts/system/html/js/listView.js index f775a4cb24..4336698688 100644 --- a/scripts/system/html/js/listView.js +++ b/scripts/system/html/js/listView.js @@ -14,7 +14,7 @@ debugPrint = function (message) { }; function ListView(elTableBody, elTableScroll, elTableHeaderRow, createRowFunction, - updateRowFunction, clearRowFunction, WINDOW_NONVARIABLE_HEIGHT) { + updateRowFunction, clearRowFunction, beforeRefreshFunction, afterRefreshFunction, WINDOW_NONVARIABLE_HEIGHT) { this.elTableBody = elTableBody; this.elTableScroll = elTableScroll; this.elTableHeaderRow = elTableHeaderRow; @@ -25,6 +25,8 @@ function ListView(elTableBody, elTableScroll, elTableHeaderRow, createRowFunctio this.createRowFunction = createRowFunction; this.updateRowFunction = updateRowFunction; this.clearRowFunction = clearRowFunction; + this.beforeRefreshFunction = beforeRefreshFunction; + this.afterRefreshFunction = afterRefreshFunction; // the list of row elements created in the table up to max viewable height plus SCROLL_ROWS rows for scrolling buffer this.elRows = []; @@ -173,6 +175,7 @@ ListView.prototype = { }, refresh: function() { + this.beforeRefreshFunction(); // block refreshing before rows are initialized let numRows = this.getNumRows(); if (numRows === 0) { @@ -211,6 +214,7 @@ ListView.prototype = { this.lastRowShiftScrollTop = 0; } } + this.afterRefreshFunction(); }, refreshBuffers: function() { @@ -230,7 +234,7 @@ ListView.prototype = { refreshRowOffset: function() { // make sure the row offset isn't causing visible rows to pass the end of the item list and is clamped to 0 - var numRows = this.getNumRows(); + let numRows = this.getNumRows(); if (this.rowOffset + numRows > this.itemData.length) { this.rowOffset = this.itemData.length - numRows; } diff --git a/scripts/system/libraries/entityList.js b/scripts/system/libraries/entityList.js index 8942c84f33..eeb16fd60d 100644 --- a/scripts/system/libraries/entityList.js +++ b/scripts/system/libraries/entityList.js @@ -17,7 +17,7 @@ var profileIndent = ''; const PROFILE_NOOP = function(_name, fn, args) { fn.apply(this, args); }; -PROFILE = !PROFILING_ENABLED ? PROFILE_NOOP : function(name, fn, args) { +const PROFILE = !PROFILING_ENABLED ? PROFILE_NOOP : function(name, fn, args) { console.log("PROFILE-Script " + profileIndent + "(" + name + ") Begin"); var previousIndent = profileIndent; profileIndent += ' '; From 7b3d2bed15a61caf4a2fef24612e6ca32ba01efb Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Fri, 16 Nov 2018 20:49:25 +0100 Subject: [PATCH 268/286] CR fixes --- scripts/system/html/js/entityList.js | 16 ++++++++-------- scripts/system/html/js/listView.js | 10 +++++----- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/scripts/system/html/js/entityList.js b/scripts/system/html/js/entityList.js index 328c8c76e1..fe657cd8b5 100644 --- a/scripts/system/html/js/entityList.js +++ b/scripts/system/html/js/entityList.js @@ -187,7 +187,7 @@ let startThClick = null; let renameTimeout = null; let renameLastBlur = null; let renameLastEntityID = null; -let isRenameFieldIsBeingMoved = false; +let isRenameFieldBeingMoved = false; let elEntityTable, elEntityTableHeader, @@ -397,7 +397,7 @@ function loaded() { elEntityTableHeaderRow = document.querySelectorAll("#entity-table thead th"); entityList = new ListView(elEntityTableBody, elEntityTableScroll, elEntityTableHeaderRow, - createRow, updateRow, clearRow, beforeUpdate, afterUpdate, WINDOW_NONVARIABLE_HEIGHT); + createRow, updateRow, clearRow, preRefresh, postRefresh, WINDOW_NONVARIABLE_HEIGHT); entityListContextMenu = new EntityListContextMenu(); @@ -424,7 +424,7 @@ function loaded() { }; elRenameInput.onblur = function(event) { - if (isRenameFieldIsBeingMoved) { + if (isRenameFieldBeingMoved) { return; } let value = elRenameInput.value; @@ -446,18 +446,18 @@ function loaded() { elRenameInput.select(); } - function beforeUpdate() { + function preRefresh() { // move the rename input to the body if (elRenameInput) { - isRenameFieldIsBeingMoved = true; + isRenameFieldBeingMoved = true; document.body.appendChild(elRenameInput); // keep the focus elRenameInput.select(); } } - function afterUpdate() { - if (!elRenameInput || !isRenameFieldIsBeingMoved) { + function postRefresh() { + if (!elRenameInput || !isRenameFieldBeingMoved) { return; } let entity = entitiesByID[renameLastEntityID]; @@ -469,7 +469,7 @@ function loaded() { elCell.appendChild(elRenameInput); // keep the focus elRenameInput.select(); - isRenameFieldIsBeingMoved = false; + isRenameFieldBeingMoved = false; } entityListContextMenu.setOnSelectedCallback(function(optionName, selectedEntityID) { diff --git a/scripts/system/html/js/listView.js b/scripts/system/html/js/listView.js index 4336698688..482576e2fe 100644 --- a/scripts/system/html/js/listView.js +++ b/scripts/system/html/js/listView.js @@ -14,7 +14,7 @@ debugPrint = function (message) { }; function ListView(elTableBody, elTableScroll, elTableHeaderRow, createRowFunction, - updateRowFunction, clearRowFunction, beforeRefreshFunction, afterRefreshFunction, WINDOW_NONVARIABLE_HEIGHT) { + updateRowFunction, clearRowFunction, preRefreshFunction, postRefreshFunction, WINDOW_NONVARIABLE_HEIGHT) { this.elTableBody = elTableBody; this.elTableScroll = elTableScroll; this.elTableHeaderRow = elTableHeaderRow; @@ -25,8 +25,8 @@ function ListView(elTableBody, elTableScroll, elTableHeaderRow, createRowFunctio this.createRowFunction = createRowFunction; this.updateRowFunction = updateRowFunction; this.clearRowFunction = clearRowFunction; - this.beforeRefreshFunction = beforeRefreshFunction; - this.afterRefreshFunction = afterRefreshFunction; + this.preRefreshFunction = preRefreshFunction; + this.postRefreshFunction = postRefreshFunction; // the list of row elements created in the table up to max viewable height plus SCROLL_ROWS rows for scrolling buffer this.elRows = []; @@ -175,7 +175,7 @@ ListView.prototype = { }, refresh: function() { - this.beforeRefreshFunction(); + this.preRefreshFunction(); // block refreshing before rows are initialized let numRows = this.getNumRows(); if (numRows === 0) { @@ -214,7 +214,7 @@ ListView.prototype = { this.lastRowShiftScrollTop = 0; } } - this.afterRefreshFunction(); + this.postRefreshFunction(); }, refreshBuffers: function() { From a77121a5882cd6145772df5a22c0ce64c70e4c0b Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Wed, 14 Nov 2018 16:12:34 -0800 Subject: [PATCH 269/286] keyboard touch ups --- interface/resources/config/keyboard.json | 36 +++++++++---------- interface/resources/meshes/drumstick.fbx | Bin 63952 -> 40496 bytes interface/resources/sounds/keyboardPress.mp3 | Bin 0 -> 7313 bytes interface/resources/sounds/keyboard_key.mp3 | Bin 9089 -> 0 bytes interface/src/ui/Keyboard.cpp | 19 +++++----- interface/src/ui/overlays/Overlays.cpp | 5 +-- 6 files changed, 32 insertions(+), 28 deletions(-) create mode 100644 interface/resources/sounds/keyboardPress.mp3 delete mode 100644 interface/resources/sounds/keyboard_key.mp3 diff --git a/interface/resources/config/keyboard.json b/interface/resources/config/keyboard.json index 186a9c1084..b3688ef06e 100644 --- a/interface/resources/config/keyboard.json +++ b/interface/resources/config/keyboard.json @@ -701,9 +701,9 @@ "y": 0.04787999764084816 }, "position": { - "x": -0.53203323516845703, + "x": -0.59333323516845703, "y": 0.019300000742077827, - "z": -0.07286686894893646 + "z": 0.037454843521118164 }, "modelURL": "meshes/keyboard/SM_key.fbx", "texture": { @@ -752,7 +752,7 @@ "y": 0.04787999764084816 }, "position": { - "x": -0.59333323516845703, + "x": -0.65333323516845703, "y": 0.019300000742077827, "z": 0.037454843521118164 }, @@ -777,9 +777,9 @@ "y": 0.04787999764084816 }, "position": { - "x": -0.5103323516845703, - "y": 0.019300000742077827, - "z": -0.127054843521118164 + "x": -0.5503323516845703, + "y": 0.019300000742077827, + "z": -0.07282185554504395 }, "modelURL": "meshes/keyboard/SM_enter.fbx", "texture": { @@ -1479,9 +1479,9 @@ "y": 0.04787999764084816 }, "position": { - "x": -0.53203323516845703, + "x": -0.59333323516845703, "y": 0.019300000742077827, - "z": -0.07286686894893646 + "z": 0.037454843521118164 }, "modelURL": "meshes/keyboard/SM_key.fbx", "texture": { @@ -1530,7 +1530,7 @@ "y": 0.04787999764084816 }, "position": { - "x": -0.59333323516845703, + "x": -0.65333323516845703, "y": 0.019300000742077827, "z": 0.037454843521118164 }, @@ -1555,9 +1555,9 @@ "y": 0.04787999764084816 }, "position": { - "x": -0.5103323516845703, - "y": 0.019300000742077827, - "z": -0.127054843521118164 + "x": -0.5503323516845703, + "y": 0.019300000742077827, + "z": -0.07282185554504395 }, "modelURL": "meshes/keyboard/SM_enter.fbx", "texture": { @@ -2305,9 +2305,9 @@ "y": 0.04787999764084816 }, "position": { - "x": -0.53203323516845703, + "x": -0.59333323516845703, "y": 0.019300000742077827, - "z": -0.07286686894893646 + "z": 0.037454843521118164 }, "modelURL": "meshes/keyboard/SM_key.fbx", "texture": { @@ -2356,7 +2356,7 @@ "y": 0.04787999764084816 }, "position": { - "x": -0.59333323516845703, + "x": -0.65333323516845703, "y": 0.019300000742077827, "z": 0.037454843521118164 }, @@ -2381,9 +2381,9 @@ "y": 0.04787999764084816 }, "position": { - "x": -0.5103323516845703, - "y": 0.019300000742077827, - "z": -0.127054843521118164 + "x": -0.5503323516845703, + "y": 0.019300000742077827, + "z": -0.07282185554504395 }, "modelURL": "meshes/keyboard/SM_enter.fbx", "texture": { diff --git a/interface/resources/meshes/drumstick.fbx b/interface/resources/meshes/drumstick.fbx index 0243d9fd1b89f0dfe6572a7ec293a38ec571ce53..b384e6a30ae85addae191f9f9a9345e24230b08d 100644 GIT binary patch delta 17681 zcmbWf2UJsA7bhOBA}T&mP>~uD0UIFFTcV;Oq9CFoVu%PBkrJx(+-G};N|CD~H5N1$ zii*-9iWCtcib6z6AanwR5JE`*uN&U?y*F!S{%dBk0@>N8+6dN7G&A+MUR&pgaPBSR;c#$RH31Sp)*HWS@78qap%<(Ek^6=|OyCnB!^$ z0)hS)ci9pD;K&F+$2AB9!u{WTH$NYIu&<*v0)dG57hnE}e_)`0_+q0Nz90gzQTku{ zsQ$aFJ_3P|vk&w0j=+bxZGwuHZ<4hzG#-_MKFHX}s6av)O<*Sg$*fRT_l3tT`DcdZ zNBknZ_jyNndr8}6;r2+#8(6QF0{pW=Ik(7j7yW!9+~9%FpgVwy*@u5MFL&~biNc5Z zhD+Oj|GVXw-zEQWe|)gCg$?xry1;Kah-Im=;>MCi?Ri6+ml*9fK_C$Otj`?s5Ar{A z&>weU;{ktPzd-+pm@`Mi@FC%6e8VDx!Xx~B0{)>4&!3Ct|2>H>bbZM#%m2qoc0dD5 zjAh~><)tn%jgZe$#}&%5i?X4i_e*V8EJYv?yLK8|1Vh55_8X1Ojf{5Hd&I zG$7|?8=xOcwf-xW2lRW{zW<$6t#XT_&qFA=y<2tvPn8U@*6455SiT5=bqEAv*#ZAR zKS$qVFrE!+^+qcVn_Az0D&JnTd3M1S@?E}Zi?OkRvDrpr^F@tVn;GxS{(HRnzrkq? zgVV^u(0JpyNdG`zyInht&5d{Nv_L5QgGmL4K>RuHKsUdL2>)PQIAu8`vqA-6{{h2B z1_|w2VJ>qT@?T*sa~H~AVFk3WfM_dhWmZ8NE3IS>K_^$*$s|E{SE7KgE1{n&_sA$g z>*e>$oQ2NFACW1AUdo@4kxgIaEMp0|tlA}mhpwzLmw5qItg-?oRzY*C&@$T~(?8HM zAyC90`(^T>%0Enikv|~jAE#B!U?2bcv0@+ICo;${I3nBtI;*hWXp7>aTWvrf5X=A5 zc6cUUwVf)1KuFQ=2FD6FAHQHfk%Dw7B&bHg1ej5PW)+Uhs6!5l7BU_XsHmf+zwbF?~!20yN zjs>DLpe;K#sVb}dPl^E$2*gDV$Y+N!fYE@b1sa!vxUX0O=Yy1AZHz zNCOn`ZUgjs!UP&KPy^T-prwX(z+P2IGGPK;GE@UDt3u8UOGt66EbvVg`fhjx*sKO^ zG&%bwF7gdZK0yxNAe*YI}i)+Rz*IP0${7U0{5X zys54W?AipqQFj1BHbH!KTcB|hWT|l+(A0raG)@2~b)chdCeTBzr9goWq@{Tr05(Hf znrcvrrZr%-8IsY`kct)plV$*Ek#k&273hOWH7H3-9ayI;B|m7X1BVw$ShOp;koW7I z(2V&~pi&n)gfs^f^`HToDwK`127F->4TWr43uNg*Z5vgg{TtPRZ;NEyMs;ABzLa}R zOC7Lz+N!|wMe>BUDj>H73em;@K3gD)_GzGY3$%Tc2LNn^6m&G9C!2JE z1B>M7CSBkrOzs8RwnD#kG$Fi>E}*ncO2UmNVRA2!yA9IXydM~XUp7Lgb#;O5+oj~! z&ALF?cIdM1Mo3v#7pPby;SPu$&}H5I!2TW3A9`BQh^{Vh6DI8e$|5I3PZy9ifHL$< z0XqYzRnHskg5JYV8H-#{jz(RL?V%FHH1Fs8v&;cA(p-oP-+NGe{zH- zC)NO~j3C06mZ3tOY15XC=s7H9txm*iZe z6+yUj+G60-h6A62A3n%0A}r?cMuglmstly%pthpUZqXrap*;@kK%+kph-jIhtbhW{ z{&h`qL|Nl{#nnTP9M-fiKd!V|cX`x9!ozdVwI8T?+3h~M>foQAA2#XPM{m7x978d5-hq(6VQgY!lqBC}v<~F{Qg?r1tJ)VpDBs z98pk2ND_=kw8Gxv!47OlA}B@{n!!W9u;POkY}#=fn!dTPJ9_ z!?f*Su`nSGT@D^)-EcugWeBg#Hq_d^xQ+W}N3p_=41C@t-oq7gc{4Y6VUtG`(Vu7; zydX2$%=x$%iD21JIaH441odTJNEC-zdv79W>JvB+i)!1-DVWEP$5?w(GG>v^_ePC= z3-w#rYOT?qs%PT`I&;+~iW7+!Mj-JDjAVXP|0V75gkv6y{1oj&CF{(O-Xl1RxUqTY5HX#o5^+;`B;8)i?F2H^YM)BiAf;^aTj{%LsR8XQd zRU94c;c|5%Np!ZffIQF=cjhOPHqW`m=68QJ5g7{3Cwd@<$jMI_j>l2rFW))(TO?;Y zUk&7UGKy1*@P#9|@~{Xy4HRZ1d}d~hKYrg|EU~EU<98i10zZ!NH8_$bA{(}H&#jo6 z;1e$8aryN+s2D&ikG88(ff59uKnHUohMPLg= ztV)J+^o~}iU!T5G>UJ@`ktW1hq~3v1^fuyivJP^V`>6g!4y(_mM7sg+A_*$O3;XB- zwvbE~CZVFbacoeqJ37vsOm!DvT8N+Loz~N6Ws2Odfd_kD)c#6Tj=IZEc5>1v|Kt_g zzen5~n6!i|*~Y7{a?*${&tFKsxulq5HZT5?9(7z}QWbYR=L>B+yUzCG;Fn#g!F$3Z zUmEoYGoD#{{6tKB97}aj1-kwKLH_US$f@4{2D=?+NW+ z7*)~wOP6>cwBL)T7;G5Yf1COnD&rt2MuQQ0NK(v-n^z?dVd(9I~+B zk1^pYUl6%g4$TVi>$^(Wp3!@%#+11y^kog-B0g4hI0a;sp270pVahMIEC*vwSQ$n8 zoj`HfZ&OKyUq;v3VnetuIK%M{B6%j?nwY&tE~Um@W8tWncRjICmlzU~w1iY#L;vui z_`N=!fxZ+o#M-VvSeB*2SIe7qng5iF8{(e?Ys2)2PTxrPT2R8!2-GV{aF2+mPrM5f zA#k#>do;3|*ux2H3t+dMq0O5#Ww5jC_1}t)_DC*xqR_Ktfi0sSW}-juD{{9ST8MX+ zR}?T~V_vEoiR7aaTR~km<-TRe3y7R4whK8OB6eHf`|AXSgzJs3_uz$rbVDfX+f z*iD=ysBpn#@>QlQZIzId=Lt%RXmd8NVi(hk+>tuVy17Ijq<>Jwmj^Vw+UhKvC-7{S zYOahk`pP?5gnC>&Aoh_D*ZlrRbai;cE4N(ignX3Tw21WeDQRY-;6P~qLTcPS%MgC+ zIJ18&()MBfWs^SRxbZXKPVu-7y>2d!S@8A(LQU~0F~Tl1TOdEbHPzJUjXEAA9P7)B8q^%f;+hsGIHR1wL~z!H z{ksfTeKpmV7*jr8aE9iLWY=cY{^c;fH5OCB*S~;NK*lfd&NxiSGx=!n&tmRcqoQ4WZmrYJf(UiB|ygo<#yi^f2}2lHvrC<;=Hai`HUvuwRi;^;BcF zrJ(OiRkcY*UfnB@vsEJ@>gEz0QtO6KBjco3-k@Szts#|KQ(;XeUt^+XPTy2a1y9|N zG!Mqyk5rvDitT^@qODDo5J^z{GcnGP5}98+Q|=3XXEdX>C}_mdKjaD%nRhK`9>!PG zKLm(xE+N(W%zx?TbJa-h8p3?mvly11#5==*K6TY8JmHPYVCy`no{OC94IVM+B88H$ z!ZbxtE_GIYEh%h@n0#(FQNK*GkU!iB-Xz`iDiXD~(alMbgfjN1RXT>FA_LVpg4ygo(LB!1PjW- zI7WPi#q=*}vOAe#c&Mp=Y^z9~1b)L>&W{<{_G95}RoF9Tl*4~4O;=~f0K=pak;qt;z z4B%bw7Mb)+69;};OnLsWR>VpCMJCRT$u+&`Pg7#mU@m3mu7g+Ed$6cyk~lClxM0(9 zOW1|>p+tA-mw|RgKi!#;)smCJec(VfaaJs!I$<#(@8H6Sc}s66VL-ei8%(tm$4IU# z`i-uY3#Nesx1+xHRZGrM1bU(UoSM7ri^I$++haA+k_IiiVY_uH2|oq+;SrIFGpbrr z$5MYv4QW)5APg5{?lM{`ZvWt|%VBS7EAGz`gb-w1My*~r|5OR46!-rMC&)IT$sx0$ zHH{GI80GgncaY+P3f6D%C1gv{l#aSUuiGC2EU zl=2qW*OllVmt5B8Fd66LL7WoZS@4`!zJ&x@BA@ej1+_F-Hn0;a*MmIP+ALS=*^WX4@VUD=x%N9 zvHES(PaY^ukQ3Ru1|&1$J}M$A|J2aM&~cNr^m za%A_dZcg5@`R4w8%Ad}iOBy^+cRafyY4e-AzQ;}-*yi7J$Xd1MfS!jh^a&GxD}H5p z2J=|mMXmllUtaXDA?#k)VcVAaoOD^Ar`j4g;Gg#>`BL5>HKM)Ymhe94#gEQJ@A{1L z8a7i1NweUo`)CYf#2+n53u$2J{qXoY(i!?L>`IlcZ^x}&%1i~P{-7=jt?D*7ST%+^hopx(hi0wNv-;%G7Mdr2IP{LwnUR%0&iIX(T}np)E6y2y zyteuab-7N?@=YEG*M6^G?WDm1H>Vv`YR*qPq&DvRI_L1)&w7bxW>0-QG`sa-+NP7P zhgUqk`{pk>4=1a2$KR&FqF%iC{Ao(^&mX&Wjs%`FX$^dI{bcFeWb@zSXKieLIqG8~ zeta^F9e+YZ@BE-Ozw0yVuMOv~i8T5*KGC4>bl5PsrOC2wh4ZeqtVi1Oj(kFKq8UV3N8=9?GJ-Ox>mRyw#j$@kbn-Q2^pp;MD(#pmH|jL$Z})&k`|{&%g{6U-hhFoT@Jp zRw_XuB+XSJ^nuRp(7Qewqf7NwN7s@v&!(H?4BM(apu0&0_0D?rb(QIorK=CF4$pMi zseMi9faRKfZ_~CsPSU<_Nq5|MXLGuv_C3?~=8e#fw-r}4!kT?#w>Oji+QsaQp%{PGj-48trM--n6`0b7xC=g<+`^+ zHr?>mcHO6Jzaz9B+O5yWx8%feH+zFf*!1Y9Nk;;oWg5Ku_T^;h_X9SXfjBvv@%&rCvA-IEVe8Ax zzs}e*Z(P4qXAz~a>4Q?jZV%@4ZywB{n<^g;`>P~p&{hR}K;M*6{&n56iZ5TS_{zIC z?)mD6`gm>ip$!B3Ry(eReeF&C8W$jG^PR&h|4#({zjP;u|N0Z5ufJGhWI1yt$~h#R z%lp%@ZN^yQee+g)o2ff*c8%%3V!+0m7uK`?P&(+lwQL47xxA12!4>*=0SVYdLi7up z0Ff*FRfmQj0)bFIwwTJ`uTPdbdk6VRzfC!G4BDPz2E-nN0{sm^!^p)x4>utYa?bd$ zAn(9%-{)L?mRmF#%UfKQppe}cysJ( z_$~TVk$$Sf8Ti@(uYn(y<8y|8ML?RB~6zT)X4L0@c4G&TmYr~(p~0D7S5*df(8XOoQ2jOmJLflb~eqN(6fiTBpEVnO_gI1g5t}LaZOUP zC1pG}7DC^Q{2u&f_pnJK6ru!eR>gw&L&t z@-Q0Yu16=kfwq<)G|oitYXRj3?XAX@f6&m5rGyJP5&ncqJYRrr>q~76y(wpz%M056 zW+51h-Jp-555?AQt)9<#nffJM4f!S1hl%`aUTkz5G7N3M^^7dZL_HV5IL)G+zH*M^ z+U)XJic=F3S)#$<70>wRMEKWM(`-*;X<%RKU#v$tmh-hWW!xwJu)5ylm8CQy`|_Lc zmd-2$$%OoV)*351H3!R7`)aiC2`i&&QgmSh^2^qdG6Qj7!Zp}E@uIh^7OV&QI_pu+ zNST|^;HTn+p38=97UYZZB8Jnlp=1F+*VasNuzEGaCI7Zw`#vo~%q7ZjiD=p`jp%l(Q2Zlf7@3Lx^iMfl8XjbL{EC2kq!4*t5{<=q==#T06_`I zY~LHY+%U>$RS-fLhj-bAtgU%(MRl~A*kuZ=B6jyyGAOSb~@e${yC*`ZQ_*`Vf9d@(qNq9toK*UQAswq9RhM0KJ5MP$t06rx?o4Hwoo#wPJmg;{U#UYE;PRAI~m zRy>%tV2@Q{C5HVqk~s2X!Hs2G(S$y(q6WV7a`CD;fL8S9++nX(RqQ?zXw8>&NB6~K zv@|A6uto$yiJ=z=w~Fn8x8#`@+m-UBV&Vb^=PG|!&;J@J8=ZQSjP}rI?)H^`h6>)I zl|^e+ZG6Pjc{C@fcocmBm5la?A=J^kE`jtnne%IE4If}@XKh*cSi(;iJX`m5FPb^ zjC+7Bg~jvnqdin?7n**P^RGpIe#EQG5L3nG@A5*$ICD?qdZS&f$a2E2uZl|;&TJ6_ zUvuFg@(E9tz7Kt!SLi$yYR|dI{_T@Z%PVU#cV@kdnR?TTr*!+MOK5G5OQ?#iE+oZn z6;pCtZ=uB0SF=4HJrnsI6rNd}spJcJ3_;`7lqVNz z)jvPtO>y2eVdo+%YKY+Pw80ZO#Iowh&#dsjcpQ#`HQ^c0uk$rqK8rTntpyI)TKs8; z>dG;V`^DPU%Dja9&yE%c6F_Ig$Q-sL%A{Pw6DOuv@YZq5~y zV(&C1j(l6t2#SPwyF9~C`Yj`>bB|=y9Nl%Fz)9@>10))0}kHX+jTJSW4KPVOd+`IUr~xJF$lg^P1^V`T?~E zGc0+@CtU`slKV>D|9sz#VRX$7qTVvw5^DBjST@sBlgS@C19}~1D{~rXn8%EJ5lMro zuZ7+GRV{(!4$*dD+x&|v4A)bxvVO%$?n!LfZo&03>rxSeAH4njl=@UJI;7UfH2=Fr#v`Ta$C-WTqHV0Po@S)<{U1H9WyMS77_EpGdQDe$0^{i z34f6l#-AYzSw3Ra9Z(D2NLEx0&m58_hrNF(hTWmlM z9gBIZa4^HNA)+Y(icgF1NAYsWVKlL|@;FpgkXaBqYF{+QJ@IQeyo|%)%WFnaiIK@d zke)`1l*4{!QZK}RS5_ETN{sJJ7%vFe+;%p!ai*gCnoDK9HknNYZKGUX;7S)-(0rym zGV)__Gp;4Wtk?kkVx)$mIHn|*Fn_cV$9F39sXWe>j%g{(E38d?;U$Bir&0cL4?z9q zPa0C=?-+)DW`4PMnsSy!+8;N$g`YI1~28ph!ulS^>FF|4bE@}hwUq1y zYr1>~W9IL*_~yhzLPK=??8DV~w}pHsjoGT{r&-iM9xK~l_*|ZV`M|u{VRBk?kYp;!cLFtLZE)44&RJe@ z8dO~cUT*i#34fMKF>e!wo<_Up^DfUoT+U~bzi|V;=_>FrrJPfzg+}e1LzVS=$R)xu z4pM^Qs!v?O#?5q{W$2eJ6k?;L9eYkQy1K*$*%xTqn4vV^5nmn;_9^lTMf ziD)ivX7-qarzz{n0zXD}zI<)wXIy|-x33djGSuZCa0pO>UTA&*)0n%@eP0MZ?W)o_00E z5aV0rXa7{-^Atf7WL*`Fsy$)}+JPAS&IUYMDz5LtM zYv_AK^$2paIM*@5ax5wmxkCRG;qjDbJFNKFTSb!f;M>|Cmkq-{Fx&g&w0;Vhi3Q}s zn(z+WW|c9${xsUXO7Oah0UE=|e(035oIv%kd%{VaK0if;QDDP8GUx%aogzh2;2i69 zNGG<>gH)}~;yv*4Urg%^lySOhxYcEjHL$y64_(c7V*8N~<_w~;#R>AQlnVI{6>LIL zEt&s)_>~~iZ*X8*rO(nsVi&5hfa#g)5sS3I+SwmhJ?9c+A}`w$sD*@qWF?$Afj2YT zp`;H70rN?LqG*)-v_VqR3jP|E0VD1#L3wT?jlQ?lPbNk*TQoCgU%kuI^-Q2jgqU6j z_JnP_Zpx(1fc_wk(eh}TM(Ck)t0o9KuyLG`c`^B_he5FnSvMh65EaVeQ~V z;q6UNk*U)(l;ok4##~igfg}efn$r1gAq^xI9VH!9ek6<$iry=nIl*0UeJC;Fjy)=X z6VaI3Y2law`)h}&oBCs6;4yR3kaGz}aBY`mp~Mu~lQvChl_Mq_5hKB}dhB%7L9|89 zXyYyqqQD0m!yJo_mNbY)>xQz2qK2o{1p>r1xw}ZLF(Y^Yk_uPfz>m4ka!dAHbnl3d zUZPQlUVAk%c-B>7WhKR_){B_dMi*rJf>dDt_Z>fG-7eM$gjeB6ccr7Gka!kWC^%bn zmh-g~^~p8M2*z11c$A?!UB=-R_>JC>q{E$WHqcbj8js)%XHRh7A!(jq1d^rUHf*># zmUSQGdUhJY5H7*AHf^CHNpKFcCyYi34}+M;rR2T6WcCC@Tk21hsnQ-mqSr~(ce2slieuvL#Lb(w?PzR2eN|YS-V^63z9?C%?r~*ZsJNV_S=4i z9DHf4Gsl9xN3PP8C^W1ds}tK=U}wLJ36;aD=xU$0wRy8`O>>b6u1%r?+k@|i($~JC zmQ$X6frNLBwloDrdV_?Ul^?Xp;C9NX(2|+nhA+&EJyM7Yi&{LjbVTK6&I>hDzcZ5{ z!v!;5Aa_UWD+@wSF$=Z;vq$eVLJ{tVuTv&1cRtVta5kBWN{RP=O#B zR-X#-1%p%~$}YsNmB!PqQ;aUY{aZLW?ICBEFxDJ%p-p%)3}EnHCEyY^imr0XqWo#M zA3ABwcD=pRSzqGmap3a*LR5_WU{gpwC1P)a; zuW&d~t7vYFa!ZnK7`8(kN;D|d`td+9w*5M9C@N}5;g%i%1kryR)QaBZW-TDH-xs0p#m=}9yt)+GzPAJnd z5LjsD3m0ZGK@qd^AZ%ggw`%Jam&!eZ`cj~zde{}#_RSKdq47--vbn5G*fe+|H6m&7 zD=ACSoEXIWe4iK?`ou6q3g=(esoez&`>^;|pUR~O4?X88VMoH+{mhJSA$M3%g?QDg zJR|!*$`Wk%AnU=BI->8#M|9F|j>ml4udon^<9sbdQ$^cj^POrP3HpNPskBZP%g`Vu z8IHjob9=r$<*CMZb|nl|UM>B-Ck0eLQ-$W2aK!h=`wOoWV9QQ3!6c2Du5(;RMjL9R zwQlQFZEwE3#;lD*r^d+pH}9JJN^);ThIAn+H1VEhCtisgtrz_};ptHoVV1Dy71ubg zKp64Y2&q9@FFBobA-BBZjOVr?JyNS;);LwwCTwj6^`u_5ksn%!4fzv7wAbV(mQbv;;xHyhtX8V0}O zswz`Ai_fv*s*H5TWBb!6dR5qv+TjuO-RgZ+%$;!DzjRUabf4~Y%n501aG&oJ@9Qcf zG%169+ft`u4WwmC1bLoVJ^72ff?Oh4D>d9^#RQaA4{O6fnNaF?zeQVG+VrbWE_~#h zNu%5;RQ#~f3*m7lxT82R;?5F+#R&?oDE?kO)3Ch1T^#wRAwbyPs^DZ?M(n{omqDb56pizIg91KJCj#ja-FGroE`4Zpc7K2VM$z6Mck`1j_3eeR7+#3{ z#gB-Td?Dyz=!XltjfqneyeD(&cn0YgPiLZZu)`QG4m#TV+#j>5I-B;WSoN0-U@nOR z7$zN@Z9Hwv?2*~3Gc>Z2S1_-Ped4DqS&z)_l;mkgHlWr+6@Og&OV%|j=`5dt+qGDo{Zm^3^_cte?5%t)Ab1mfmXdASLaOletNdEz7` z(M)`f2I^tx?+7;2B_E@k7v78{yoHUd!#E?xz%^3Iu#P7v?)9D*;ox}#OJl7E1*z5j zJ6YJD?+1(5i#Tc5_@f&&*aG2gyb} zN-3eo&Rqgo1`s< zPR_UaY4KfdPsT{o8(RzFktyrV4$~zsDLXH4dZjw+h+_fB8v@;>iP12$X-l)4Dj z^5a4;lEDENy>?af)}~2jM8%5{cw6{Pt2ou|-viq7<92sKv^JowXo%eO^i3j7(^qy#_Y@T)I;CR!cN~|>e(F%kuyG;IyjH?y8?BY-V(4l3O9tXjbgLAtlxI3~ zd}1&7^cJ6aqTFVVPw90T;Y4PtfmvRpdzNES_o&6hhA*jdW1K|kc92rSDE4|JocmVY zhg~W4xA;qCn*|owXTs^4*qZ+3@IvLu=VQzW9wZh7?-DC*-sc&J!(NXd;oX?~1V6KY zw-x(SxNxtOup3SkkBUJ10h}k1pxml5ZccV8MUsSS@#Y$5Ii`fJz!r?JB;AmA{zp(k z0WYx3YcR?{Onc3sU4fPJ=}{@suv3Vu5}UyL+oE5bej``HsfUZ3DQZv=C!&-<_xT;A zB$YLnJomW|OvWnwWXMWWxi8WYbUKLzo8u%J(!|3+O~iB}h1&PQc3JZM1pS;+6e<2v zUG#dgiJjCyrpOhIsg%RrCN78yyE4LWJ3l0zYTx)z&QyaB0!Mx`c zZ)^v8U{j0Am^oQ}G>yiq1O=i%9?J;(<}h5dlEza6dc2L}?eViVk{i9FAg_}ririQC zA{HAdv4{ozB^XU^cyYP!+gd~VL&~L+jDv)_sV||V@q~J5B8e&N8||V!6HX7tVg>u) zFcalZdha?hZh#)%QE_nq%qs$COTv z4r7x#8uL6h{}!k*Qx#G<@=2A(d|!@DlwGVJ65r+3Hb@+*B!|f09u=#h5}%y%QM!{a zI?a%$$19k8@?9t(Q$_0?VRtbs<*@C#vF;bh8)2(OG9pF#gQQRIq@ScPdt?&Z?!>NV z`0!k7Qf(%#U?JMD8u_QmZ+ac$GPIN_~P72~|You=8(gcgBi z_d-VoC*kW9dt6+p-B7gTE;h8qPYLw0dBtrFBZcZk5S59bW$68pDih~ZWwfxQ#D)5; zB%=feWJUw?Qb(<0JiT6pK~RhuW6XB@gZik@$}A$PxN*F#6aJd5w(}Jwaacb%kgy_t z=qG<=lSLbbZ=K|lBRtR=@G7Hw%+AxP&u%6O3M9OX?g21z_4+C3x>=t{#g+(+64CPG zY$k~gzNuXjPmd@i9Z{KnpJ$$2|}q>7vHICLc22!XUJF)RLP8LFI|xKN+Oc*f*L zr;U&hM9Zfm;a(UcmLWr(v?XJqe!Ku%(OFzdr8>sb^DFao^V%nBnZzUopygQHaEmA3WIxd-k~Sx}_gKwTm ziG zX$Lp>mjI6VgZ3-BrLjyE7ATk|t{jv{umkx!F9|EZ9&_}BBlH>ezi1pb}BjWy|RPa&#<^R#( z;Sj!hCRGN~oX`SJL$D5@qZ3O35Q0ul=m6Oe6gQy;;OS$Y@JHLy_KXw=(--Ut6$ z#Q#9J-GyFH904BRg(MS4fPuS^Gs6zhy$2;T_5#uOpzjRp|J}$h_n^&_M*)rd(51jHD$uW z0dCpQ#woRBM9Tvi1FrB|;F5Hcc z2z8K!!C;ubi~zrV2A&n@8au&4Lael00x7F|1Do;pMPMWf7oo{7N`^k z(~$ftIY@t9x)}z8N!x|`dWT~}T{eK}QtPFW2F3=)21`Jal(p14kSC=E-wFqNla!?3 zpfOw#er)zbB#PQR-zUJ}GmU(|o9^@}DmO2jVFLaiA4#q8X zSjt~GOJWOB7TU-xhQVN4jf{~-Mu$MfMRpoSriMnAhER^iR%0uptyV@R21q0G@

    > zIs`^8+NwM6yN@jb`xY5Xp+UvP`=t^<-^F{Db}pW+X**cD81=W?n}MRmJAQThOzGJ# z9RZI^Z`W1&wPuP-W-knwm02CUzHps%xc^z-UH*aKjYVsX^?&_ccSqmo<5=&|6Mqx_ z%yIDL8lBBPOJ+S`H4Fw@yvsk(*Wtu(D`7C$rWc#PSLxQgt7>|o^+=CDbsJP$vVN11 zrM{7=hOz0ad{#(fqk+G^kNmqsnm`V@TH=rz$0Pg$PoTCM8Cw{c8JRhNSxc1RYnOt( zNeYX9EP%mmWFaS#It30dF_*dzYAm&q>IU7GTEbP9foV%^q<#n6mRd@I;EH7^saDW& z85+J?2E4Y+R>}=*Uba)}38)~mPf84W%lt0o1eVA+Nu385FW)Lv3R*5Vmzo5Fms`S3 zR)EizW2C}B`f`j^8K}Err_>l2vcd#zC<{JXaa0NqO3AKB=8epxU31>QVx6~dr;~GD3Z01 zx(AxeX(_iF|LnOkJA6<22lNK*L}#hd5{ZWBCOQUqTm53ZHhk#-69_6b-hFTvr0Q7e&fi&Y>h!xV~K zk|P{sthA9T2K83iN^!uTReta;MUW|{wRo?0v~Q?as{9(M7EnoEAO1#BQb`Z!DQ^K^ zp#Y}K>%(mnz&d$-sQ_?F-T+>%0IpiSPih-Z; ztX1+b7;KrdB(xli4)%3f0fWJuuqS+N!^1=Uk4J?2s;vHp`KmA&%xDexQgy?kh`>Ot zH4>EP;L6obO0?Cp{tvmi)P5Hz`nveX_=2{|>({w%TeD`iRP$%)UMw~Mys=zSN(y8e zZh?Q_3`VOW)HaLyW#rHY2{R{~Ac4#OWSfuxk} z;7VmhIBYFApmqp;cr9q7{yQ983xeua@X@s(Mg1^*=Q@z0t_JQzSiymH;CTcRUIzU^ zfL{^o;H=rCqQ*M7zA|_op$6{NP=Wi+Ca-9y!2g_0zSdBIcPfM38s=~{6>zoYcKAsZ zkgmBNjMdbJ6K9jPn%eLgC}|HjR|QSi+rY1@f}!gV!uwUhck6$LtEzzrEem+48kn!8 z42Ei1!QVm2gK)7L_k3?}CzMaCw9z z$3%M_d^ZAg(l&*kLx7jG&EcOB;Hr)5z|Y!N@GTnPs*Nu2V;Ue@XB`M^RDq|>CW|(z zz~9d%=^Itxa7_@cqXKH_tb-$=q$xaTHYZtU9lTi+e5&IC-@G1Ny6GtV(t6N$lPmn) zdhqq8?eOJV;Ok9lpyp<6`2N|X?`CcIO)c=lW;O81W^MQvD7hW3xW^JK+HIM+3a6s|~N4O+tk-8^P;(>Y#?6HhlkV(pOI# zehW(4!J9UMAN5S(3v@tfeRKF$9T20h50BCTgYbAvVu~^K|0eSj{aE(o% zu7M#Ow+VDNI0|pw1eO{cgm2so${PL#Ke-t^Zit4LZ3gcdM!`4gfnV1sgZYX};3`|7 zE)QR)2fkF*TD;FU?9_5SiBmq`0`)t%XbX63^?vw%UGUiIHQ>9|R`5h!aN!zj_*-4j zc8xh)tP4i3@q)iK2EVRZ5BJuCYFk=rJloaQfC%~3ps~_w_%mZkrQ>>_w-N@fr4Qay zGJ%KbgB?mv@Irl1XDu2IHvs+C?t|MJfW>Q3@Du}(y>=g*VgRnXjRcRaT?AJ*1TU`J z2M;v_Ti5*tFEj)1+&xNkct% zwwo$}t5jEmDk?Vc9Aj{SYB=~^dc~p9@&Cju7)*LLeuU+2osG&HVKCT={}Dv?PK=+Q z4b74$DfK@JB~ijDA}}y^D`c%w(AHl?iM7Dbo5EnQjIFbJLE&>L6uou@di#aVRrf!x zJ!SGsw%6VBV6!oSssleSQBX28nJ1r{$^7{?oBon-KV=?#!k>H-^W{@Doj>2mula&| z=D|lYozw57={)u8{xA5Rnf+2f*oS%W0cLZ2p=R^s%Q2h3eu3t{B^XJpI_$A-D&-2hv%3_XhYQB6L7W3yD|25x)+=c)D*i!wIugGGa`t|-7d_?3g z^)uqkL%)mA)OSqL> z2Vb)7oPHN<=c!-5?fmt_ZT}_TO2v8b?cYAfXSaQxeAw;t=R?{3k`E?55584)b9@W! z=E-MdH-A3SulY3A%!9A>C*Rxo@-hDlKJt!V>UWnnkN6DknA7jdj(O@=uw(xEo!j|K zzVGwt9|w2N@!9X3Ctujk`SaQC`XwJseje?+de42iu&yds$KDSl&0z`?nv*OEOS&h^(10K zn!b?6Tb%osS!5MOzwg4l$8YFlpnThIALbX^oU9CR>P8N+1uhoK3~}M$OJl@zRzX7) zP7Wr2tQX_8`gQRNX*S5AvFjIeOw~y-wwR&M+Q}Qy?!|&0V{HDB z($AxGa(t0bPjgpB)R#hLk};wM)wj72J2ZOKY$)P6ff?fnbcZnasMWmK#O!_H?p*r0 zYm|RD4ApxG+h0@lGb~$f@QV!nn&b7qvv z6ikhjc(PAq3FFH!{4ZpMX=<}DP-s2GCVdmPlYKme>xoJiM-s&2y_KY?kV+pu?_H(3 zE8Yj~p3y9kW-X#dbS6;n(zc4J4)NxK}%$an+W z{#~A>kYU4Y|pJz@wk2n%>R+DXU zUFx?OG+Wkrg13JT@${PqJ)h+UxM!5C;y%To9;0W5lUmqx(GB`ixq2$@;b#?LUb1EX z?XT@-x#VEt&-1di@N(=tPT*G;Nw)_K8-AnDp4eU|pw|=VeIv=LjO591cy6U_UZovB z(P=MHy%E5+aai|4YnQ2*P{+UBY?{6+_UeyBXllR;#Os;mBXe#A^EjkEs`XG!p^ z-BTsx6Jx&pap|-kQdN|A%H5TSn>y7-9~zy`d1WXVC>rLjQb!+Uo@2(4>4+DkOB_O} zIm3kp6mhOcAE^YsGS1fLe0mgDB`Bd|qpAhVueu2I8mG-dUZjhi4v$vIv7#2^b@&?O z#6Cs7NxS=*{)y?tx-sG5#<2ZaeYDp$hS>;l@}$l5vmn7Rl8Ucc;rt-l(k~Qm)mh>l zigx1?!?lBs^{^|d%|us`8Szfsvy|s}&9{Kp3)JVxsNqXfSs%J>{P?#y9ocsXZbXbp z_Tf(j#6!)UIU_~=mtLvY)U-YGGV&bJ<%`@*Jh!ULDNb^wLrQ#O`@ZLjeWwwM(mkhb zs_@cSQ_-%F?*sI)niiXJ+mIKCLjyL}Z_gjjNcRl+9{0Xu=oz7i_w=CG;O88(e(ioB zF_YjlUDT(DF{Yd#>Q;gtHH_QN+!a?{Mt`T`S>@?%rH4qj=+^zO>T?5mN}g3=?=#&z zCY%h(SMw+d#2?ai+dlxkM8L-Jwo*q(WzrG+MSg5A>Y3!NcR}N6E*#g=Xv?t zT)i0gkSYE4ybHCZQ?Af7sP7%{&_)_~`^NcxwEt>pPk(hf7X9+uhzzz58R!WQ8Nj?F zC9iNuhi z>Tg>_j&+^M;-lUTgw)2HO%iqS7e=oUz{i6|yKBFcT9soU`i~eG!3(d)dq(nslHF z%Zj&edc;d`Fg`G@De4)3yxc?*+a@hwT9)KyA6^^6G2#!DjxN)D?c zxmQdKaP{~OQt_tGN~k>SX;#$RVHch6Ox)3a~7)g?-o zzlSbW>2*gKS-vJ8pU<|A(os`8JAl0Gm|PjRu7pK>3jCn=>3r?P{n;_GF{vghwz<Hbwn0efF{vf1jmn&|$NEh`E6 zLiL!!44KRFE817{ZO_`0FIp%A&yc~Bz=|-7p&1#1GuxNy=q1;eH?^wj3TG;Nx_4r{ zH1SE&lwoCA05xZGlM6o}N;fvVBADlL7-@BMICA0;fB1{iX4x!URor*(8*T~iOisc@ z1TQHLS((&t#1~Dh9_A{l^MjH@nk=|&xUa|w%+L(6KdMeO&X5ceWXS=cs(nBjPL6Ey zmeGTp3Md*3`*B4nx38#TxakSCm)!@lNSi$M4JxmmJ&BO*n&`V7NvJSsHe~Bhdwmn) z3&hX+HW^Gy`uIt)d#~`GO-=@K(#2mOR0>rcZRqHV3{f>7_Y8Saf^$J%T~HFT7JFOg ztr@wlMwj5k)}Myd0X#vBA2#~BrO5j2o8;zeovWlh0|eQ9uNxx2i>$t99rTJ#%D9S; z8Q!JcbBe0M;3!J>>~XH?Ve$ke$geTu^nlf>Se{@8K!}p7N z*@0gj#CHImd6k{cN*oPDWQe@JF$he%xT-2D#)rX?Tsuvf*u_6OO$jCgSGthDRTUXt zam%R-=-aPU0vJpOiI?km@xo^s8=jC37#4Tom;#yPxb&7MQKHLynpadV={_lV>LzmH z&@>ZIp?NDO1ERO2ND4VvEO0B0g)RcTqUbq5uTA=-Kqi+FCAxgS!;JEzQHH-wHAE1m zk3Vs^kXWZqjuE}h;ia*jVfFU0&x9Mev=D1^v%TI3xwmFsv2>aKw8OywUOR|}JWDPr zm+c#LPhf3=a%T?2HT}G1rlagPOY#LiOScpw`NQ-FXovgST8I<<6k_cYLrP&Y2kXdY z(ZVMnqtHNycdgx;O@MdU5+{C-EEYY7;9%f{5`a&w8j*>x8^XRnV2KIt`(UwXi4&<3bK$Qvbg7W9RSEzLp^HVr zRo}>x!iiL33-)OdWq?=CAy2e+#E4}Kt)6mHCSHqfvmCe_2JkYOa^4@aM>UGkBd#7s zOuq~k10p3SQcn+A{5FUExXo&TJuzA=!zfA~6dG23W^HBwy`!eXF8SRL-#YF5<5!72_9-ld0$U42$n5s`n56iYPj1_M?cu0?C~eF8&6P zr8rdVYio{hab+YGtJo5`nulL7zUi;8iTH`t-5izRNnhb8)-An<5sP-`#XODe6E8X+ z+PZnhyRa6ewE`fYnte_yg5GJ{dBV>(%s*@4Y}Vt&(4*Vx4)6Udr`9bFeiPnz^THyR zJx;ri1XnLzDq~P~_j|Rr%o;t{Q16Bdj_IbQ!3~RL(gQ=i8y2S<_y@m9U9m>5bD%o) zp<{ZQ!IAP;;crqOE^;*N#>dTci@Rbum62i^FrIDfQ+GY!c8yJbf6SU-t!u8;&3eA; zn#yi0gc*o=SEX46EBN}_RB7*5KQX}+Gl zl*&eW<(tgPx7L+Sv6Yl~Tx~tB+7H*F^nOVS!Sa2-Rlc8;MZsmI{AM6AKXhT>$b01wR3sMhLj!4{GV%pKwzlrPW0vHCs#atem{#H_~5Ft zxr_Y*x6WNA)~9@RDu(Y~Lrt|(Jg1G{OUZ`bzOU+c6U-XY>(8RWUEKSYNl8sA)Z(@; z@zK2Y$NDQs`Q7U;n{I7bpII@pCVC*ukoxepna%5qHxIOE3C`_#8kL1O;Hc(2X`Av@9Ag*5IZ=Q~64 zUK1Gjg0W;v+^u8VOLK?{Z`Q8~Q+QeX^5%)uBP|vVev3-Nq%8+REvS#qZ6EKCy}7^9 z4o6tl&f1iGVBZSo37--F@0;;X3e7f6hW+o=xe z_Y5t(Us;c_tZ_y6QTK+Pr_30pmsO`D!ZsEzJ+9s%zh|vX>G`*My(iNOt`B}|JoBw3 z``g>@Z#7ll>V-D1?QGs_?ND&EN!S4Hv08WHM$MkdO?Uq|zUPSJy48VqQ%{>8a74MC zdXO;o>O-BuS77)-raEu!u@uc|lWNVR;Oh$4L#~=+`CM=?*xKxyFCUedz4iaKhh%AE z!j^8l8NB|F4H>|ZK8Fo=4z@OETvI)iYA>}@(WUxmxf~$gmy0TvA z5eM}}iU;;C4cL7D!is~xHE%rq#7`#C^kw#%uU~drXNR}hAr>s(T8rO>tX-89a>3Xs zXvP7*qHwNfXlnn>JM&4+$_vVJb0ky&h7`81m2usnXk2?^oBZYnFE8vZXc)AH zYHi=&Xw~RS+4c0Xw@HL4@Z#1P)542~JdatZg};pVe{~D8VQQ6r5E=WH2ALODi&mz-7H87Z7;mB<@A@m zYAXT<*JX6t>RNa1llB<>GaWx&knAlRoKmAYh`f(`?ccJsCECE~t#|SklDDY9z;^KnDlV?9PI-PB^Rf_%bkWjU}kg%ZgjhAKhfu3H*^^jB! z_gvU@ma({Zy$i18_xy>$5H#4i&^S3@srL0n-?nWypx3KX3%=eO)GNNfc?akFDe%t& z2zYxUSbbnUT+JVP2N?{GSh&{2e|BVp-U(gkwl1cH*(hY^Ll6Xh4g7j{S;*uBuP!#xaWnL=698Y~%)dzxO*2w-7B3oA3WrK}yx;KWgS_O(4A=Et7G zn@qBQ$fWJNiD2obIVm#$;T?;hGdOpr%291_rn`Q;Z0C%r6Z5;k^%~wZ#(nIfF6sCF zcxH&@*nwPe@J$2{Bx&K4DR&)Nnd#Y;-Z!c5v)QQLwwcl_09T)hP#uhv_Uw`&)Hx{8 zfT-pL_XV%o@;L1?-Z*!sB*qa;qa4<~thi3Df5mXM0R1rHz5wrkR0pS=8+8hgV>K4t z7nD-1+hquK^@CG}WOk2(Ks!MFF|!6aQZ2xJC=zbK59{={A>Z1uGNTJg7L61`RP@CO zf1JA$84x{ST%T-Pl`GyqW9n3Hib0q6x!o58#qY$(qZ5;x8h{9j{(Zp&H{E6fPFcqz zg8ool-XMk`$Q5qFc5WX~oy!cdY!J3D z4E-RCuREZG94}D^uza1w`}y4Il)_G|5OY=hSx_M-S9d^Zh-UGGYV|0aDB9VtDAz1U zdqTA$leYnnm?4%SUWpytUO9HusjP5+ydY($i`yKC62A$#3dAF+zmX&GxXc{#PGtnx zRz}U(c34oFZ@5;+V?gx@{d2!uKm;n%RJPm{bBAh026j-yka9l!!IPpp#e{0)uzbC+ zo!ia&BzFqup2WY!YCFm{fu!;({fy1VOvW`bLoDA2+RJ*KP=vc7SL28LIBp7mkRQ?P zOwoXW&P}?PtF!19`cGPzX;S%X|7L7975jm80ICAfjLjxAu^EEN1&PT`!|$=#RK}x@ z1(KG+#ZHRWT}|F2Fh!fwE;aChWihBVr?UFaa}&8^Ljpxi+KfMBZlvghIC0QV;%+2& zu~XDUwZ2G4(wGc9K$q{UFvASh)G$LVna9{}vTDQ@F$(iuV*=iedcTFMCmqJ;*tr9+*d7qC*S02Me8?g)4e1)@Kpn|0fP zUSj`IwwE#@y)BcvXU~A6z-5{pBpNacTV2W<*&q%@y?6nY! zBW!jO+h0zx?Cm=)Cnu{oS9J9OF1dXx36lyM>U~-?URaN*ax#rDW0yqJTt&Wcy< zc!-nbwuKyTpy0J$49_S!lj)zbGzoo7=8gR$7*viXmF8mOKmJK*GN4dj-?Aq ztGB96dPKhKwDA=5DIFl5>}XK!!>S`@3X;#)PegY9&OSO-W3~8-2@iftvo_W{lJ5sJdK!S2lL})2=on`o_kp#aBu75&u?f@N;u7|UbJm}yxMd^qfV(Uc=**gF ze9|#o%G_}@`85LIl%|LsztXBm-sugb(fBMnXi>Am3L!!JYR3cc7G_`1o zQu>%bCYd+O3)K%qT%fo0Cj#qZ~6{mEs)P?1^2Q=x2ovxe~pR zl&p}OiQ-mv&=d&BRmfUIsEyoX8Nr>%OCfSiHv9E_ET=!;SfN1*rTQzBW$>P?VlliGc)@+N^dnu z$(U~XV2CWsqda4}FB>+`wesh-MR<8`)-p>K>v0Gv)`J zGGf~Mj)iX=A&C7|l@fG~pz2|-E}kfTlu30Lf6a_1#=f&s4$X3AWtLqdsG_h_+;{8V zbC8Z+XKpWzAL2)GMobKpJtOxdrQd%g^c> zDaS}*U(2pBG8}A7srCJpCTL@)oOo8Y%k=q|BTg(w(G@SUJb#xS-FG8)Xy=#LASX)RH+D1Oas6 z0+x=}h_f3lkSh}*N~>wBEgUoy>Wh2n^*$V{f$OsRmSxeLbe?%GnE#SbZv-YN>Qf-i^91G&-#xz<;dQ?m6ENTG^nb*Vcx4rR})=lPK3*eC|tqrG~EBcdbigsxUueXMR2u9hNE4-8K7i1IoaLT{ptgX;J1Q32c z+{it9#Pk3IXzh%OY#SYn5InHx@#OPVD%$z3m%2P>BGyqX)S}|A*NdO^bFbFqKVzK- z)ibLA_aRQeK?}p2(gQ=0C6yEOtvT1J*%NhLy3*kZpr2RR!{7_`UP&&n*9$PvxY`jGuBX>>g4#IM_C^j;tO((rL;eV7Z*``G7Lh#rhB>2S28{dE_KTh znelHkI!2>OuKhY5eFLdL!ZQmzX&Z|`dcp>#1Ous&ckIh+Z-{PC9S>2@c3J*T49j^- z{q`j0u}s!PX}bwhjBY+W5o0AbZB`nV&jqfvxAAp?SUuSf^M#_L&J^dFFMq~1A&2F4 z+ytHdUiXAw(<2VD&M-NLkCHDKgjByw1L%~f!u%4WcR;0Ehsv;gZjp#NQzPGPcmZHL z)A)9-WR^DOu47gU4?}$>q`tSR9Cu-!%Jbtpms4upBB+`keV6K~<>XI!MS@5fgFLZM zE8WRzQ_bb-c=Vb2V$%A@$4$HdWcQS)dK&1@4fvh7z@snxq4+XG*f!?P-9j&!zQz$N zy%K8;!*y=4b+hU?G&`Ws)9XP&(ur>!SXSzN!!-1%ZRyArGaI=Y!>u@1Lo>XW{ZL`= zS37###&XJdq4&K@*-5E4}rj=TF0X=g&JXDBYu69BKD7-37x?wBEl2w@Ez{C(cM|%)X3iZ1~X>7 zZsVhro<7Q9f008vvilf0g3x@3a*Cb!NNHO-av*YyZiZ->5t?Nfep*D{NH{~f#}2F# zr>np4>#nzjE&w`5hPe(8ds>MP993N-ix@b91>qK3xBnv#gTT^Pgx^?zR!MFbHMC>K zK9_r?7;raoJ;bZ?{OE$JaH^)qz&Xm!gwPWWSu#B#{Wfv|kBTmB z?5&vT^wWaqhDx(p;Rg?&_5=rYM(;8~gOGQOm}h)5Xj%dPd^^tO~ zBZyrr|H;lHyca`VGrhilp|e)y`EA9Qh!`T<=>UoBV|j=wOpVi5NaM+hmPsm)Crrq* z_@FJz1v~=SQ*qlS4d{uI)*XA!9*q;C-8gEkJ7s4Ad&C~`j;J=@a(tIuirZO2O?9Y6D+z!DdMlw0Z@G&vE`Gg9E#Rcdk7Vb*ua$TuU85n?N+k< zURaQH8g)ik_gPHeN5bc}s&!es0i+$2;uHi1RFOk7Woi8L%#?_TCLW`ab8>ti$3ZY$ zW7VTRm4kiAu3!K_NK0snWvv-x( z-Vh!_+7ZT@tWufneT+b|hDTotd5-7%eoGh&7Nkdk4-HO;y z|Jg0BU#i&aq5;>8>v6c9IB}oJ;v3*|KV^m|*pVF2yUR_z+OO5FTa4}!p0IDG?DTtV zND|%S@lLatx9Gn3fPSV(iE4fT4{UUTk?GSFyS)&lJGjI3)+40F5+_rSVxE!r!wM!| z-lA(eK^&uh0$x<{8H1u3LI!(5FK7_@+V8SfWzXcdv&w)>`9S86Nd75Q&1GePRXRin zPf&Q{$GYFxRL7tm6D}yPy+O*0V8wl*aBL81grJLT&0%=FzyPr6FDuH)5l_?>I1b`; z&q40alhR3#F!h?b3q=HQvY+qcYQHrHh2f9HXhC7A$kV=`Mi{|s^6PGvfA1KGOl0V` zW3n06cOA221qNs${~(H>>`G1rByKV(q#f_VG>NoulFqQt)42(F5eGtm3@hN7-z1OZ z#E*xhrK4Ndl0Aq*=kkHffygJ%wHhLWMT68%a(fr=l#Itjz~sJUvKfj0J-obzJ5bpD z%#Z5DXZAMH`ANbBC2WPksPbe{&&8tTz)Jr2)oh{8z&Xk^hkA&xU?t)Jo}balIgB2L z+Y7e$t>XnA!R?GA-vdlYwfZ;-CuvF}PW(eh8Q{EY<)JNy{r9MX}AjKd2E`{T#XY;zkpqQg6A!TW2Qi8$*LX>wAPxB3L^!v` zNk_aPHZb~f{5tsZT;Ay>-YtlufkQY?xl)jHiALAuk(NRgvf}hyMcNF=*)_>BzG_Q} zYOZ86OuN0EM_;M)3ZYZ`jR|EsC6K!wVrj6F#}@!IHU7Ec#2Le3`TB^GiG9ReT0Jf$ z-1@dNx{EYg5JO5M3Zmk2f>_TH-%K>?SvflNkRq>F%rl3GsUdw)geXKYiGpECb9Uo- zfKiK6`e(X}L{0Jg&(AJV-TxH0_vMF3#Au{)vNrKUfFdTx<|W=KD?H&Mnh&Tp2N>Zi zwm~zK6slR5KdLHe_+f^4IV5cJLtq27itX$+X@CI@LQYh@W2}fPX2vLEP;RW9kdfD7 z{ZZEakzMHifYZ#21|c}A3eSU_*lwbN zxg~VzlMoj@1WuFmF?npc;q~=6zaNDKNtY1$t%3(*ufz_A+llWHq3;+MKATiH3k8=9 z{}87dgjlt)S;6FoWL+8JiNd?ZS?brE?ZnT`pjEiJ%T zXqk$pb7`mzkbm>KE6j**Cc&W3kn;>gHtQ7SG_ZUCuJc`JP|BZjZmz$PL1^K{hbL&! zcOu6xju+-62XY(MSd2h{;wUPX&N`d^nQ$Wpgltgb1qQH3^+ZFczlOCR+!EeUs+g;d zmyBF`^vCTT{KBc^321=9700Dd0|f$FDzQ=8V`|?-#rU^)@!pY6fK25TeySLX4?k~* z?;v$g6D~JWim|Cs@45H6u!Mapki1di8g=wDKv$&~XfRXs+^d29!LKT(p%2^0yA=&} z7He1g>ZWlQ0{%JAa9Cc)RbWS8$H>O5jCf-a+r;|dcOPY+ZyA~!9on;UjQe9OjLl8s zzRSKh1z_EY6!M0aq4G|v?$N^OtXH@<3DO-A$0%cx+g?VDge|S8au%U_`C-wqv+|~O z3%wg03n$)Vo>7~q-p)4>2b2UE0)uqgS{b7CVOIa_w883`-xnG?{*xe`_9a1mczr#G zd!BN(j;@sLDt6MnS=rGq)eXoZ8*rz86u!yfXI08Ig;BRiW_gEyJ3wETCua5NP||E} z7=#?1=nPs(=ByZ&*NN#_jP6Qj>vB&e6|e&+z8fSX-QiOxmfADXk;&H1uz09}3-`H_ zixBz@d5++oog~`G_Vl+E1oh|WknKq6#Q4jS$qYbOZx`xx3qHy%!SKVDL36@Fj|OsW zmoSQ=aG81(8m1G+#C8NWZTEC6K~R^W(`Dh1j_hp7yn@cuL!9X~Oc&gwX*V%bpe9@P zw7n2$|Jvao=xuc*rZS+WahoTj7$c|&jWy}ovh@i@V1&H%QJb+9jm zI+)zWEoJ(A%-rXN&_$<1?zME(|dv7tG<)D(*z=H-1Y^q$U zL9-6IX;8SWAySVhZsN4}7*2Ia9gp_#sI!ai#^1x)%g#KnRM#d`k@Y=>8=%hBSzL+O zBp_9iHK7V3xy+8dfRPK`TZfl zjW{Ps-|eLRu<9>iO|dfWgufwvWQ?*XZ)SuEJWF+nJh6RG`vXw=a>L%j%D@HX@1)30 zqG&Oly9n-Uz&2Sj~~*xQcC}V)pi9 zlRlz%cuWLLxjyqV!V7>e*OFF=wLJ!Ik3$hHo9RZ+Twb0NuaEp**w6tElJyA~B0q)w zalgc+j-W0w78D0LC|M2^PFo6wmUaP7FXE&nLTB8f>7}x}Tm>Et6$3hwAvZ(r2aQmL zxj&XEnNU0up!Otk{4xlY6>%cf@B}+_7x0BfsM+Y|o49bE-mJD2z6|WA6a}0ALuaGE4UShmS-06oo zew|Sj?V@pcoPw}LgwM0K=Qe}9$cv9<@cZyeql|_ECM)-FJN@OF@B}*muOx12b$*9e zrF6{H5b4xw_n^nxY#}OuCIF#_M_H~1mOYw&G|s|RBza_gIYqfNiluEK3**KRt=djo z$rEw!X114_srg}#^+ob)wJ7Zpe~2!YRd zY!P~KNf^Oy*5`9jya;o(uLd|m9rwxXYi|LENO$TE#0HOveUlX}B7+LdcuGX5YI7tT zZy_9yp{7npsQ`uDxr+tL!tt|Jggt(OX~Gy9E*ZT~a1>6XABS8)O`&C1{E&|qawvZ%McC&nY)QxKafl$JVH~&&1!^#2gF8c9d6X@w zqBR~`RQPh5v|mY(fh*oj-#ydG&22(3sVA893z35W5<7+qWBzT)>*I0jwuF@n9X|Is)h#2V^93 zT>Gw=@@gKH0Zoc+$@HQe;jLmhy^q2R5|^a3##}MnLXQ27lPxQOFmMfVXM6Qq*#8h- zIG`j@7Un;{UoMUn-^AY{NCNB`SNm#uAh5*VUt|qPW;7t%jpKKXOzs$m?s0>(y5)nP zuUbP?fMC4ruT_mL^iwwp;vXK;yQ>`1ktnX-jE3Yny;zIllG6WUv}}15O1$Y7?dc~b zU?sk?lnZ>nOYLMOACE@=5qtp!NJlgf-}nimx3*yM9XaU>B#(IA_JD!SZzG+ehjoPf z541t3*ZIZ9iVnEsVApdUV!K$$&@xuxq>aoIH_`RaOz{s^sN`7*j}iCGgd~hed@Uwc z_6AJ{u;r&ftz^w}U)(?jpc23PsQAa6h=Vy=haz_6^cy*(;}me_g8L}Sh@zWU?iW@l zuq_>@AROOKM_j{rVJS^g(27=FKv7O2lW*~Z6&mvaTD`hfkKmlPBrIzo_dw{iwq#w^ zCtjxb7m~SjVZ*d>G(P3Ws}6sW0@N7{x>(6E{WQ*B_>uQ1E-5p76*I3{A0H@NUQOvz z4?XuOGaN{;BPF1hl$#O+`PLmdI?-+UG33PfQEr~xk1&-?;&2q_N)0hHg?G^_mQX5J zPN^f%SsOBmx;k<0g!@+{OF&~>o=rntAH)0y>yTvX*W5{oshGjOu&M25N57QZ^hjTN z7x za2pWlshtIQNzCi)ORi&xck*wAHxL{ASii*@{K@tsw_PK7ax^8YP<|1kHsYPcN!xVf zk3ywWsMvlB4xJ>KhdPog2I79BG&2of(cWGFpi#Z=@*(GBv27wDyul()tjY_-ZRb!1 z1YxG)$Ix0~M;5knbuoteEJk8V%j7CO(RgSV(IIjm`f{t^c*l=IUtyySt^P2PF$75jWY^x1gz00HTBzNG-bCW4|H$3FY@>Ab?UY%1^2<^C}PBo&?AUl!XQqSA&q^ zNL`(*hm=+J&^klK)NM4)@`!-z!gJ^EkWAj9iS=KPQmpkw_H4^>3CN2B1QUe5Xxu~@ zBZ)R1`Rpk{I41y_GyjNu2s|)M9n7uR%Z7rJzyR;W`WPjWJ9>!!LK<>1bIiw>vvmLk zC=g?zK7|8u#cH)w_0rKju5wWQoks8MW~|+?k_bO4WW|k%hY)ATFlZqyvU?+4%;-di zT_3leWtsTl@i3h`YW(>^_Bkgv=SMI)Ht84^$7RmEZYb2J zsyipx^=YEtwq;fIb;}<%clrVx=IrZJ$c-z@WOA8t{U4~60hom3nHNs};tlAxc6>|EOS6t z@7~d&whC!V$Tv&@7IfG~sYx+E;L1JkMxDabX&}DJr$ejuPj0x#Sv3oTbe26EXRBIn zaF-R`2VJEh+nh^>_k9qIvX!&M3h_g2T=S4q=$ylLm^PdZuvoSwo)u=5zl6oKQSJMa z8`a~eip1oy+^h_;^Fx&KdyuhXwxhh%;^z`icpu51HUIJLT@gqZcdtg z0SVftYWB?~XkWZD-oeJllAyPjq|!mFBy;$s%tBEFeL39rQ6c@pyJag-xj$ccQUq@# zD@r>+)h9ms&nr=g`;UHJ@$;3a*1vL>gR7EMRzOU)fq^dGXMC6Dg4Ri98b*KBy9(Mi zYoB+xZ>Ya_V3Ty~PiA+dgu!5}r;?q~E9bimpbcWBT~2wQ@DK8{bJcqWcBUHGY|ooDXA`tr;jB5|L;f-c5;;87J1FcV zHuTKa|83CqXLAM#o=Xhsc{WF<_V1{?Z#Lx-B{tQ({D)239{8C3*+HoYNb8`EP8+FCjLo;aC4~?fozR>DvGB1jT|mviq-Q zUAsV{b`)s`tWQRPu5k|9VgNeMk-im3)c&`k)rE8RI2O*?bGLBLo}ogCJ=(7p30+ry*gmv{#_4ArfP?b1-|!7KLp){*n0ECtxsG*{gq(;@c}o3iQg7St+*s z>=s6ZzobxrdO0>Q+9e|Nq_@vMdpf#kPL9T6i5w|KbB5Iw%^9XrJZG49vBa>dV#u(i z9x30)zP7L2wgdbqy?X8U;(y~@P$F@sb!Go>r(@;+=1yk+yZic-%u%+LN+^p;<|yeU zbClak=P0j~N+|0}=iF(y^xxQ)mq|24R{TT5;F^Ee@E+Ab0H-s7s{W-#D^v^8s&RQwyJZiPfb+p2#^cz55wNoe}NCA?NK z2g$FLKz3KmK`KV4{x{prsyViW zWC`0))f}5DX^t&~G{;s#lCW_|b8O1we`7{Z(iQ~&UxqEd(KHXccYO7`2|1g(cHf@wZ{v2e-+vyxX*F z*#3TXcv-(mMr^htQ)$^4oi(pl$exvE}80t(OUGt4&}_G!dKC1U9!k8QYjV3Wy5@x`0Ts$^%08 zQhk;Oy2t{edy>aeVuO8*2Nzoor!1NmvT=ofYn_@y9PH}qI*_t7p9T5NdFIET=fk#J z`LNBa0Jg0zP;GNfz@rOf)+H8@b-Rmn))j7&Tc_K@4TZ2Su!yW%Q3$@6Lhw}=f^T$@ zo^NrHjBj(1$~Whrne(aVL1+a+a71y^QF;D+PU7 zDd;DZfqq9B(O)TpZ!KjqIA@s(7c{7n!~L%^^gp4&xm1R;dOr8nl0lQI!53K#z8lrxGg!&pYpfK=N2?C}>@}vJ$hQoJWGe--X+y;MZND|~ zKsH-t_>Zj=$OCJ2f$SKd;kzypx-I*y8qjCji2lbK(AU&}ewq#R`)x#j!-im~w#ncO zwdC7r^*Y}gyyd>tr910tL1(Wcx?gKS*H#O=5%&JUa;cpEkB}=v0>IChl8D7)^l z{qbQ+#yw_=4^w(~FuxP47RK5+nR)`kp!wT3Z;Mb*lA&W?LjB`f`cj8eU0lN>wnX{| z22M7(saMej1o(-aIIeZDIj4e@2OZ4eF_m39<*rP(vx%NFnd6kZvfIw0JZGpXo0Z@* zsz&?H0GIEimTjm<-l4wp74~7mY-PHWT~2sG8GoNS5(Ymn+O44?^@d*19mFF-4SEpQxd8?PytiJvUYnVZzVR1a7kyXgmsQ={ zOFQBLec5t;xj0$@J<%?%i*_RPv40SzIO+iX#wA=AM+~6Xw~Ond-vvEEyN}kxfu72B z(U^eV%5`x>02*i>e-e-AQ$e4>buqFZjjXj?7o+mgL^{lM(X4@fk?Ue~J-Rt&u8WcJ z=%hT=bZt~S+884r5T_{Mp@(o?6s6FkRNdQ4E1Mvn;FpU@>u3nv;<~82p*L_{Og=|p zKHwqYh>7DUil=a0l#l4xf28W(URt^h z+4m7)ic}H$LavK})hKe_=enpVpvQ4t43I|Aah>a8a5M7#8m^1{8G7Hx#3KeNBf*}? zbuqvgxotSt#o%D%mC;-mSsC>2wd=Kv0(v&r#U=^eq1{It{EIYi$P?leX%qBdu8Tpu z$g3i`E(YKtr8&xVkvTyBh3jGFoZq zkGfy%+~i~zPLEIqb~4jxU*)2cwVfWLlsK8knX!tYlg&Ky=I{&s&Bm&TYtjE6d(koB XqwX81=M(y}%V(mPt=|CVpXl))T_y31 diff --git a/interface/resources/sounds/keyboardPress.mp3 b/interface/resources/sounds/keyboardPress.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..e4cd21b9715c64fbfa5153b89666c530305c0333 GIT binary patch literal 7313 zcmeI%)nC((qsQ^jVDx}7Vxy#C^hQXkbjN_vIZ9F*1mg?RDcvD8Y9QU+`30mDFenj0 z9ViN-avtA{bMw79k8^p>hQ^=02-@fliaqTK*9*n`n~Bl1ulNj`C^JNIv&N|6_es>G08TZft3Cg|d#8 zl@6S-j#W2yOJ{sX2GUt&(jS$n_C+;^Y^%h@jS7CRD4rN+912y1Ka=ofOIgH`i=*W+ za}yMXe_}X{Y!591kYpmMogMF@$&a~DCWb6E!@uqN$30S`>tU!?NdW7r!b>h*jwD9@L?YGW;dI~Aa*>CYjqNPwbSC}?+7=^00PPH)8f{(dY7=4BKOp%gK zqYCId$um!r2RItp65#i097kDi5ziB0l#7*kN$kMH8>D%+#7eq^s8DtY*vh_VmbZ0u zJm!I-A*WhM@*+0vQ%3u~D@Bn*3nz|^*N_ao@V&9aw#?mIuQSAe9(HmxQ8PxF2}*M5RXkfP5Bi zo1Wky=0h^&`h^sN3qE)&SrOFj-^sns3ID_FTxcxTr4H6{n#y5%Z?hk$B=$sDY(=X& z1>IzQ*RLwK}B>E~>SkhUPWDR8`k}0EQ=ih35 zJkWQM#M0BQ_jwn-w&?yGn)|qnHA$zx)mopX%|R0J*Wm$_uZw;EF3g0B2c|4N0Lfp;NEm`MW!bc$~4@>JZAOqu>+{o4N|(4X|b?C246{ za}s(5r_QND?d$bfs5oJSV8|d*k#LLWovsULk*GbTwTQ+j1%{H0J)74qLzNsMywEq# z&C2zdcrs5o2TD>MVWd998*YOS4f9_V?QvQiSp~{(!dm%UZnzA+7M8wT^IJ(q*td16 z+)W&67zfo0Ja)eT@u7%_o-& zGwY)Luj&171(ZU8epPxJ5Tzj$CZ=L}h4X*+cJwN?Ja#d@2{eva+FG?1gYJ>|EGKdO zl6>Y&Z1G1!A;VCo_fU{OsL?5)(393B=EzeTx2>oHza}EO5m53&Aei(-NX;JvfK=!( z%8{=Apme%Jx|da{x^27{J93I%rb2o#%3rRpua`_vM)23*m7;+YN5kuV!X-$efW_2M zNGgI{AFR@BY9>||y>Y=oM?sqMSDW(LRZ@wJDF+eU(l-`aEpwoG}Y9>fA3brej$6b&9B*(Kz0Fja7 zB(y}k=zM9wRXZv}#8Jna38IgUX8;LYUq=QI(c{201f&{Ooy?s;>H(mD!KkO86ftrq zC@W?scKn;BZfmBi#w!-VvKl%&Z8yC8pF?3G)F_fDl%Hw-p9&hhMCoA z=@gbmasyD!@aHnQ=|FwrG^x)D(oBCIsZiur)4x*;==Cz_cGbq=&HD* zUBZc&m5FFyATzmy*kzHV*ZGN-`vK$Y2R7$T9oN^g&lkLZH+snaS%1WOuBvJd@B@H( zDW6(9l?=*e{646aQq@@2gy1*ErQO=Rtue@@ZOZ8;rQt^zc-FpbF_MA0t)nY${Sb#d z#Ex-ScsV?LS~4+bvRZo2q=6%SnTI7G{Nas0vRFroty_G`Vp%xO^|YcfIbq)Hz~*;u zZEmt30v9vJP*Cd!+pANYHmZp^Bw=Kj4K74_#kuq4zfd=;o=~q>aAEFzGw$x}PGI+& za=T;RfGU$k7A>tAWzDSRy6x^SbS>c;u!6*MxDZnQUP*8~GR+Nk{{?0(!{IT9VZ13P zbDcT47@~Wint(vN;Laa;ryk2UhFo9&+OYcG@$8tTU1;5zz&27Z07Pc`W1ms>B6vK)H7(t#4ACj|3Ud$^8J^v>P zT#zXPZ)0|YzfiBmJ~HB-`t!(EBYp3%gK1>=7y92_kUI46WOd|XKC8^v;fJ~uWaS|9 zHqX4x{1ZI?VBlvIR#;6GFP6J0IUM=1y3j>F`+H1fX}R#%jMeO)FN>&*skT5F@&V$? z!TH6rqPO@DO9I!k(ZWg;Ym!{1GE=Y2MZ@vdMYf!|>oEzPH5}>(rq4Z6;{*}t{H5GZ z3a?k9Y$uB8(a5D~Sg&(wsV?2P7Wf2crZiT|KK{KGZcZl3kh}^(Vud4>-m7 z=q4U3>Sd_J#gz+x75Da~7yRZc2KLY=9xBB~QH|zl08zpvVc%T!EVOs>ZvK(Wck{B8 zrYimr9rIe>CShah=d(zjrF@gSf#LY4m_l`<0sZcdo#6$K(%U!2)v;CUkab^+Ci^w+ z>2uqn)+Ubn{@B*mwK(<5ADJZcl-;kn`JC_Elv5&A&+y1Dc{tn>_VQKKfg&>fO7{4? z9M4R*z_aGC*~Z&duboB~QI3hoOrCZLRNzo6G^ZPUkylF)n|YJkV_qkKIcLG5R#SKK z?BC^twD`TE9weGdJVqt~z~pD@eAaqgW?Iw%=Z>4 zIXP@Kn*mID(!z*7aY*_QF>Vo1Bvn>sCSu+#ihRK(5A{!UGH@+sXn7l`Yf%R4KA{n5 zkVslKUz2vMiR8%pPXCFf;1+v+Fi+j3(OZw)FoFvrenafo>>>WZawA31>-A4tSmE=} zU4c!OE~~zq_O|TUFLQdO)pdLgtZmXYUJ>WsCZ(p^DLV#dJDgsHZ4xDTeDmbn&GtzG zFLsV8QNi@GHfx7&HmjqfxQ_SfSz&3elA{k>UteFA<;mS{sePHYZO>r(R0)i^%y|7D zbi`?VhwKd6cH^h%Cu)EM0I*v`s$pW?ECn0NL!D@)0`Oboo!6Z-E$qF#9P%``oFs~= zXI%Y`LvqXpKlk=z$_i`D%CsDEqF=JL&E`^BbilKF^x(oAAkS~+e&z>W*4hCzf#?*? zf1)5k8L#XyXXZ2Y?9&3Pw&%r5Ub15jBTu<`-gcLTg?(tP+slLSAK2!H`r^M9Xqo$G zbZUPyrtikjseFl0^yI%Z6R4h>qj~8TFgZ9AQr@$QhbGyKik6vYFI7u=CYhV7K~_~{ ze9*Nw`kS~Sv284vur^QY%&oMwqQbPd?*o)r#MN(pS1>I9YfdoX=5>@m~R%<=F*V!P|KLCxX5woG!( zR*!UDY6Nse+B*uEg-?(4`RfwqMRRRFhTYobEiSkAOsEj*UU0aqBq$lH#50scMPY*x z-z>_Tw0e6QAqr($nXOY!5tlis-#)d5F{$S64=NORxy=Rk-v3B$*iVrsISpEiBUYzO zJ6>M=er0qMj|%nq)j9urGRWObN#ySZ*`7zd#=V^Fd#)px;_{1Cbrz#{P2neinHjprT?`eY* z1cJJ6-Ae%iRI;J@SQcsu{^m%3@|RE^+2!;`DdRkjDRqbd14b2dv!Zoc%F)m0>f&k! z47jD>Ek7SB$Z?wA{dps18QYcv%)cc@>~_iaePn+}_HY=MBXX{e?1W6FdV zX2Y2Z4whVfoT+zpA6pBTr_3R1_sr`zEAzIi!}=H=)w%Cs*5%x51!VEsWJBm9&YLF( zsmFairD1+YA+U5}8ISV)n|^)$Pr22;EvHtpx7I9=TKp}ODlaWO(-UeI-*X-QOX^pv z0+(mC0 zWo){$Q!8kh^o)kmt-HSv7h)6mAQke3tU@+YVszt?Zo4t|3RU8{T6e$y;IzP}_4h5v zYqG7RExq}GpY-V}XXY&4riY!OZ?G*zjA2z5FQ!_PuChcUwi@evI+LCT&h&jfP14mo z?)sWLdE~n|$5U6BnfQqCT({Qub)CLMw(-J7<#w*`WMTbSfeep2zy9QPjjrar~y&G6Y2~S}B%G_-QorWgzPyd+sc{=Nj(d zC!xu-aMviUyA?Uqoy9qaTjwcMLlF_r2QJ#SZJX-w0M{?DsmX1Rb|El%xZdzVbO1&Q zKrFaw@cK~f2-ldp}^=wZ^6MG!a zdI93@V=<91TZ8gitqw4nVX;hEND|zr`D|S(w<&v4`1ma15}($(KmT3XO`nlO&yqs# zhlvM%_0_EDH(Tw7G8!DbD(GjUtgW8V?6mlzRp~nCzOUI}a%@CzglO&&otx{;yObiv zoI!kBy`?mhhm`q~kGvT-*Kv#A^A1hi7vxgESYDlW<>zpFhU0}ZUc0?j;kZqf;MuEE$g z>r6X2W<>>|RfrEbN@z&(pD6^I9Z+F?sN$nk(^(=lcynW)jHoqgPKCeH!H^+HqTS_bC^OSy z7)Uw}*65W?Ir<(ct`6n#d zpRC86bL34q+S|`9^0L65Iu^TRio;DOpqjr=a&}w_5>UZs0~E%*$PPxxZT7z8WAASv z4xPz`Rwr)$X0&^0R7{{H(&*3ft6$sW8HPD}qFQwep5PiI5%21U&>)7TT^t`kYNY{E z@DT7k$T87a54QF``9NT7ku-K&x3gRh;v-@>{i=^VG@Pby#E2Vo;mP^sp)z8T7UxmQ zpF+*08UBd^ivCCKL!~TFxeRiDZHpktJpcez0RX@a05XvP08TI)FtQ1*Z@V|{xwBp8 zjL{Fa93EyNFG1(k*-OFE&lN=rypUR9`+RoD-NVj*xA?SiQ*TDlM*#Ns1};I^`9pui77V$`le% z(~1^Ln{OM{8t-spXRcu}#R*wFAuwySZ`W*S90MvCz`M7+TMtjP%y-ieXOYg>ABh3~y0G`J~!6v`hl?^haY$s(Mio@)A| z9U-MbudGw|&6yGoQX}_Nuo6S;8O-^-c%f@cDyPYG&&hXxtP-MG&Yw>D4klEnHF`Fk zZ7*Lle_oy#0{3-a?@2UQ$$pgtt0n}Q6nFO?d)n-Nuo$j=52w%UUH-bQJ?(Z9EwSBJ=X4-WVglq9`Q-b?s5QC-^0oi|_K>vsQ U_tV6Ubao5I3I88j{{PN@0jrj2?*IS* literal 0 HcmV?d00001 diff --git a/interface/resources/sounds/keyboard_key.mp3 b/interface/resources/sounds/keyboard_key.mp3 deleted file mode 100644 index e2cec81032389f719f90dd60b0ae4ca10719e725..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9089 zcmd6t_dnO)|NkG);b}eXE%Y?9vt{MwNyyAhR@r2Rgpxe%71=A}X>Zwyva?r6q>O|@ zh$Q3rJl{+2&-?xU;rsdi0pHs%=hy2w=XyE!b3c~@&Ki->-?)>t!fF#{G4iyzy1VXtHFt-;3w$q1D|uw^C;HAwwxVmgb_*NC^jZ%1>zH ziPcqXg4^50Rdq}(P35y{>Z=6975P4xk9rw zdBRbLxNZ)#A`K-ACjcPa<*k^PAS}zCT59cecP;>c5Tk?_lvGXpDM+iScuR)aeDmK^ zOqRq_CRKRj=wM3e-`VxYsK^R8Wu)a~F^+m+oN};m!(kjm8;EeE55v)NAYm)sXlfWv zqaUIp$K4gBnz+Jz*M7gL#}V(zMS-LmnR>rMWon@r+DIvp0~y*fdN}pjqC2tb=Oz>) zoUkzR70DhlBS=%`(G>wLdu1E^fHZHJ8!tllG>a?Dmm!EPE!B!AK zhLaRUs3$)R+#Bo?_=P^WHpGe9TDcyT-z0RtxpPWTsT;kv>=L}Yd{~ZNqk z{nnHpDHlqXRH=j*QRZVZSSEc%mysR6?kP_$p-cu<)MwtE!}XgBuBlHOJ1rPHFm`$? zrOY03848YTLH0#+l^He{lrAsO4;q|lU%NNg_@^c3f34R(72mg_Tt00a1|}G;4^*|>rFmjA zDN3jLU?VfKFu#>6TPbtnnoCX{$l&e!9vkz zkCa^n$GR#PMNtct@B6OYpqNSlJZrHwYX|>pUx`9k2(pDB5=bE<#H5-=&;h1b8!6!{ z5R$hiD=S()@ri57#dlPwg0QsRUMqa`<$g;3&j{YE*hI1d$@2%D+cf77qYlAx2mlv9 zA(-x0ir)K&__9XjXRit7{*JM+-O2427i#U-FAb7Mc-e#)S2TwQ26VmBg-O7A!dROY zt{K$1%dasMn;PRFs2hSPp!=f4ax*T&0eH3*h3(fHKxxU%&B7qBO&LG2o?&8i_U6L_ z%bVN%o0pW=$gWE=aM1nZ@Mk-C?&6ds(aDEuo9135y6!Y++55`Z#z47zibiK(cV@odA>L(F4X!T zP?Da6@qvry4B4c`2w`y#f!APj>!#OjDdNQXSf`t;-i)kUi@EScxMBrq z^eNeW<_CaCpI(LhcV9Yj(!$WGYFAVPDr+~F!GyHFdwX92APjO|pU-0ff?sNdUKrf; z{dRJ)>NRn78@eZbmMS?Pwj27gAOswJ@;O%kr%PqU-n2#6Vb5L+WV`x5h3k~Fg?41_ zfBXN1gINNC7%_eAl?j*4#FZr;og{)SuWJQ z#LzqrmZS;#OT5w#Ua$SqP56tlxn%OPq@mB(@GjR$zDh^LYK?uqCr_YX3vx=Ph|v*keEfNStV<8~bbmz^$($TK zpF>a2l-5$_-8Pg{24+3Hhz%ng*es0HD$WgKZeBVpt;QwgAZE}703hPnAPA=8v9O`A z5G2%m?J$sH!{8(|sxsZ~p@zh|Fcoyo0)zP%bHCDDBIgrmy^djQPtser(}!`<3D zxP^Hnuc+FI#4h3%ZSJ5=dvD>#%?QaSDaJ)8=Mli)*f zxI!oH2(@J_l7d1<^(#x|=Y~ZaP#OP+czjCOOwW7#Nsc}~X$L1tn2nrMzt?QCC9y&f&Mf}ooXi}?($zaw5Dm2%Yus6%TA}%?oBIbGGNPe@xny0wAn)G=^Qv$=#NTl(5jqF(_-wb;dG45KNZ+GhsGe@lmF@>Q$a@D;KXu zd1E&a$9XsHI$02w0z$w;!^@deomOn4Tq#K@KGdvNTW#(gg7d}?A;$(EElHj9Oj|D* z7_+&L9+Vl5Qn@yW0_Kso-!Dy3WYyo&Dfs>_Pw?yKLH0KQRD#!ydn!k=PCdbtrVjKC z)#&yxLl@o17;+p0TtDGBz#Hf!K%k+MU5X^3N&ayU4V3MG^M;S-Q zQwFPD^)W3C_#D-WpB4=B6^Z}kw^|laRy{(~lpk_7npfHc0H|FL$dZtWe|U_%68q$N z%x~OQRcdZm0a1GURBA}IiRSwiDyEiafM{CM!_ol&IJ5Cv@F}H5WlA#gQkgbKy`U9v z_WWbwPyhhfE$KR%2mzRZJJj`p6bB_#2|;N|-(q-%Zul?BChXb`mq>A2QMf1nNw~WH zspe)$`r?KP0IzN+0C3=ZD5;qeE95Z0r}aI^z=ZyO<{=me4$-tAyJAP+?}vu&c^QdjAEvGu}nv@wc}n%w`f_O@gniqdjvZusf!q4KI9C zIO&iU@ERxth%mWc@;`Y7z^+MR2sqDvJ!Vu=q+|YDXEN_ReNq`}C${7u)uD9o;}U7J`&TG%~l|m?IK}^&_fD zQCACGy#9$;$~@(%&q!ux^OMwaOZH=XjFt#D%7Q(th*gTznDQsQzW9qb#ord%KB#?9DOVq%cm+7$sdr-M1YMqOZ3wCnIi z-PY;5%W-BC8hK$`mxcZ+7YiY8Xz1}i==~BqR2kvbW^Fi3CrV*L8OK`W6slwAb9tqv zKrflL*ZMX@zyz96TSz#ExlIY-rg;5{xR_e$YtrkzD+8oO2srM;4Qh|}ajz$5k)DUg zpkS-NZt{Kl{)Cvr;DL+91+&2RT08cPf=p)r=vN`|DOCDbLR zrXAPD*xOZVI?PIUG5}zj#J_syRs4CDT^M&% zRH^`%?fZ8Gr;jb-c3t4Jh2D3u|L4CcZ=y$~ZNilH?IPpVt%iTo!)g zWb%f7P@tgt&6KxI!|fT;J&xbyj$*3);)}1c#>g7d!BI*zwRIW|qahhKA5Q_OezW}Y zVoqU$Ti8Vu`m66o)vp1TZ|UX75w51H!#wbAIp~nw?8AGnv8%0kXI4ulG7fBG`!X=Q zsh@d?@f?8B%f4lg2`+57Y?HQ+;v`&|`w$j_IF+9sXOCSQ70WIprJ%;qkdVbSnzufb zGad*O&EqFFFnJ~WxrEc9s^A#x;Dhv*p;m=^2cu^;HsC0rtb#% z^z5F*T80E_rmJ%#uZSYNX(ZA;gUxpOM%_?$k%fk#{gfiL;K<2z!Pm%O@^9f5cA1>> z$s$izN+{Yl`W^z&7(J}nR-$f@P>PE#NgvqHkD=ix>b$VLMM**>{`aBmM?}CrZn<;e zLCVqHTs;&@!==scb=`U<*phU3u%lVo^mRr-_O&AIP$8^AMFwdq8_uWqlkA1-eM-m_SHc5HHEQ;AyIx$%7b z4m(%Tp~aj(r@d_e6OFb=!m2VYmx9J)xvp7e3U-@G0Kg*I-YTpsxN-Z^);swuG&*k5 zyYJ!cQE@P9Kd?QYVB#`d=Ib8l$DOQ(aDH-^tim0omHkCA*DT+4pKnbaJANZ;r!k2l ztuc-^;u@x1@5^2HR*q!zjYnmPqaKG{WBW@@gBN$p@+SN_oX4F`a$d{ms;z3?uar8D zg{2{z@XV@xf0fI@A)l#hg+J{5lKd@}_MW`82?-4swTnocE%#c_*t*Y<{o8Ajm7X=S ziH77%3~x38APh)Pjzn5nFL02MmnzQcdzus0#$6^SyAF*!ylxhpE2#2N3VYz6S9GGi zEtvedk#Esm_lmaYVklgdLL(y#u%#$@w>u|K;a_lTC;<&l}} z%{$8>=7;FBKkk=hN*|aho+)N6(q1a`E>@V$4(i8^z%8aIF{@uC?F+MJTj$sZ1(9*F zONRgu*Vs1K)oSZJ65$ckd2alzHBWwSG;MA!pH#HU2~QF^@W~|UR}A%MZBX)}aUHZ_ z_E9!3-#sAfCy~kP?(G%88;5hqia#De$O#D+K2PTN;7irQ*+J<^>wL1z>#k#mFYa~y z`MM)Ki;2%WUB{;*ZieUA?)`{$ymDIoG6IPig$$l~T-Omx{W^UofZL9)hGHRe_$4f* zUU2h?`UE$56u!*o`bGYPfpq73(afB^05`F{Uf(@HY>B+od7o<2w|bkZuGMzbMYCv- zeaWB4YvjfirYUuWTxtrK;OZ}~k;p@Jex58uU$R@5mWqh1n|&-Z+6zT*g+E^(57|C^ z86-|1Hs++M&@P_YTTpdY{aA&ONp&-CC<_(#vyTe?(wEjRuDcd`c~9C@*bveEt~Uv7g%kK&_qjz1)q%S2vHMK3GXOHI;uw&HC>BRW1uc zR;dplnn%8*3ubluXImTnpkbw&6p2&jUW2___Ys<_r0w$|5lttD}gSsQ)W;_dAs)0C7s-dh96nwWl1ue#83|! zbibR^h8l5d_8~Px6D)7|-s#${Izd4`jVNrai44XN+oP)1k+CxuJKNw~dgTP1_vHWp zMx-fT$>|<>{5r>dOuW6W_@0N=Gi&PEZ1Y6U|m<>JxSf%E`8~Zs}-JT4)(B7sZ508eCe+mGHgX+v0V`_cA!>3 zLEPr}=HqZkegT0|ugswp#Mt5sZ;$%Bt8bY%89#pvh<4*40Iah`T0bv+Z>3Cj+WW;X za3p_fC9aPK3Z=gPoB=W}dcv!{cI#7A_>dczVHw_**N8HY&!mHCi~j(CsJsWj`Ixi{ zWBDj0+bFAqEF!A78Jnr!G*t(`C-Ch8#d|UDs!gK-MV`KLZug~=!9VIkB`5DAJ#HeLcoIV87B0*WO)PbYCL7sx1Z?>g`w$iE)s~_#aRpr zRQFK)%i|ZeFB>wKRW4Ao3TKf-@Ka$$bd^E?xb+4Akk%n3PhTLc%l4LOim##iuIF1s zTMm6;_Y!}o3U<=8nVrn^q*>5GHug(Cm-#Sa3MsOg10QMz<6g&IgA#oaM*Z+a!uX4{JWU%_%@YInnnL}B4 z-$&NQ59#Qs4Mf5W3%{riuX}SC%rn3YQFn7i(i!wM=mME*Ubhot>Z<3mv-MJ;>%%q2 zM+Cy5H~{D0x#MY%13oqc=!-Any08CJ#u%;B{Uz+^t8{pJ0I*yhTVRAE3 zNsBI;!J5%W#a535%U0xFXR0sDsIg+%c(8rS)BeKJ*-1(dc9Fkn8SL$4hXAl?&zc+E z(fK~J-Sx>Q6kOp*f=|(ml(B3^qW`9F?@Fe}&j>cj0pn8hy0MwIu^z!}&w_7BTRx=l z0f#FO>l~zMG@aT20DEm~0KEPelhS`)s86v9_y-M^W!0iLitMb6^l59zmURrJX)V^g zpG!zT66i*&=cr6EZO-}~@M*Q)WiY9Be*~6p1F*zdeFsr}9~gb5M8Zkw=(+5EvHJI4 z`zUhaWj9NYv_;}E<5y+2(qCP4?Ubo4qK&|7Kh07vTptUhHN5hCf~erqda7{__Q0MP z0K}$2@0*g~^wwU$Q3yB#%iuJTmFZEfS+!PO9Aek4D)JL*UNaO&ZGNtw#sA9yXT`L* zh;=A1UZCf*!tzg!zxY5rTLy&Hk1cCQULjjoi=5iXC`37W<|W+8LR(qwKVM%(;BeFU zVF~GPtn@iE!O}1~oc4h|KlCkgUsCEv`|G{6jG%`lHh&w#(JlaLetvrNT!la^I8yxzV3<1XwOcJfZ{ z9zz7P@%Sxp4h|_(-2u-ss%o9J<77k%!1-X-F#yCm6EWA3+oo1n0bq~aig9K0>`~{28vuX>9PjL;k}Q-nPLz8mf%@bA4C!L)xQ`}xTW4c7kHX@{`sivvIepz9p` zP}5#h*`D1E0x!K@921sirA0sq=g}pY>U6c_mG+^sRP}z_@DC{v&`^&}jUJ;N@Rm?2 zU#L!=c>f*0jSl}Eaty$qH84pS*>=gWoXEd$J)DkLUa~2fvD;(cQPRST1tY_dSi^qT zPK_avi7X-MPTg5!6SYYRj=s2%&5@4rZTZO`JJznJWtji~6@n9(B+l|>IUCWAAe@I}`*acAZOA(=Zk*KygiHd$ zn(pV#jG$Lk(u|K}lD3efk-aQxWTvfF`kZvgVd*3jSfAw6z}Rr6YMmjRC={BQG4)bY zwKeJ3cckUYWZ29t#*wJpcSGP0lR`I)%XhY0ZG1_)owbI4~Q$KYEz6n)5QuKi27k;!^O-nLl4nuHWE`0Db+>?8E zJ>AW7^S7^Sb)3^RBl}Hdd3f};W$RzE=dV3pDv1;xPY`&1vV9htY(P&bhApOxS=Vwp z)=zIpX7xUbs!`_ecx_LrSJdvOpd|J8=fQ)$n)9ut4--ERi+`a^@q&r=5#IfsH5>dG zd&pEFOs3eUzx{R+R)|_^$|~}$#Xk-%d%N{!DLePl;1-}u@igQ<&+&5N-rmo_h8HTV zKf(EN`z})_(K*267hzY_+^VB>^yEC~n8yaKFBrMj77dd>5$>8x&cztl!) zv|j}FGiGa)=jJbg&CT=e<;~5-y@yN6et9UjZne|_^2@Qo7GvJwfh0wa93w9|T=duL zd14WBV<#5PUxgLbhqc0I< zF%qr@*7Vp(I2HGH+}-RISc$UF$opmG`-&%%gL8Yqd-4Nvaf@u_Nvm98{%wXD zb2U$%XM-;zA# z#wf<()>NpKiJv;?4W;6te(~KU){$uF4naA1=L7VI+`%!Z)voH6ef|qQ@s_EwKWqx@ qLhX6c+uN6%&O*C19ZT>}OLzl#Hv?uQC5F?R-gb5OTEhgmq5lKCskIIO diff --git a/interface/src/ui/Keyboard.cpp b/interface/src/ui/Keyboard.cpp index 5f83c095c8..6852691634 100644 --- a/interface/src/ui/Keyboard.cpp +++ b/interface/src/ui/Keyboard.cpp @@ -45,6 +45,7 @@ #include "scripting/HMDScriptingInterface.h" #include "scripting/WindowScriptingInterface.h" #include "scripting/SelectionScriptingInterface.h" +#include "scripting/HMDScriptingInterface.h" #include "DependencyManager.h" #include "raypick/StylusPointer.h" @@ -54,9 +55,9 @@ static const int LEFT_HAND_CONTROLLER_INDEX = 0; static const int RIGHT_HAND_CONTROLLER_INDEX = 1; -static const float MALLET_LENGTH = 0.2f; -static const float MALLET_TOUCH_Y_OFFSET = 0.052f; -static const float MALLET_Y_OFFSET = 0.180f; +static const float MALLET_LENGTH = 0.18f; +static const float MALLET_TOUCH_Y_OFFSET = 0.050f; +static const float MALLET_Y_OFFSET = 0.160f; static const glm::quat MALLET_ROTATION_OFFSET{0.70710678f, 0.0f, -0.70710678f, 0.0f}; static const glm::vec3 MALLET_MODEL_DIMENSIONS{0.03f, MALLET_LENGTH, 0.03f}; @@ -65,14 +66,14 @@ static const glm::vec3 MALLET_TIP_OFFSET{0.0f, MALLET_LENGTH - MALLET_TOUCH_Y_OF static const glm::vec3 Z_AXIS {0.0f, 0.0f, 1.0f}; -static const glm::vec3 KEYBOARD_TABLET_OFFSET{0.28f, -0.3f, -0.05f}; +static const glm::vec3 KEYBOARD_TABLET_OFFSET{0.30f, -0.38f, -0.04f}; static const glm::vec3 KEYBOARD_TABLET_DEGREES_OFFSET{-45.0f, 0.0f, 0.0f}; static const glm::vec3 KEYBOARD_TABLET_LANDSCAPE_OFFSET{-0.2f, -0.27f, -0.05f}; static const glm::vec3 KEYBOARD_TABLET_LANDSCAPE_DEGREES_OFFSET{-45.0f, 0.0f, -90.0f}; static const glm::vec3 KEYBOARD_AVATAR_OFFSET{-0.6f, 0.3f, -0.7f}; static const glm::vec3 KEYBOARD_AVATAR_DEGREES_OFFSET{0.0f, 180.0f, 0.0f}; -static const QString SOUND_FILE = PathUtils::resourcesUrl() + "sounds/keyboard_key.mp3"; +static const QString SOUND_FILE = PathUtils::resourcesUrl() + "sounds/keyboardPress.mp3"; static const QString MALLET_MODEL_URL = PathUtils::resourcesUrl() + "meshes/drumstick.fbx"; static const float PULSE_STRENGTH = 0.6f; @@ -221,6 +222,7 @@ Keyboard::Keyboard() { auto pointerManager = DependencyManager::get(); auto windowScriptingInterface = DependencyManager::get(); auto myAvatar = DependencyManager::get()->getMyAvatar(); + auto hmdScriptingInterface = DependencyManager::get(); connect(pointerManager.data(), &PointerManager::triggerBeginOverlay, this, &Keyboard::handleTriggerBegin, Qt::QueuedConnection); connect(pointerManager.data(), &PointerManager::triggerContinueOverlay, this, &Keyboard::handleTriggerContinue, Qt::QueuedConnection); connect(pointerManager.data(), &PointerManager::triggerEndOverlay, this, &Keyboard::handleTriggerEnd, Qt::QueuedConnection); @@ -228,6 +230,7 @@ Keyboard::Keyboard() { connect(pointerManager.data(), &PointerManager::hoverEndOverlay, this, &Keyboard::handleHoverEnd, Qt::QueuedConnection); connect(myAvatar.get(), &MyAvatar::sensorToWorldScaleChanged, this, &Keyboard::scaleKeyboard, Qt::QueuedConnection); connect(windowScriptingInterface.data(), &WindowScriptingInterface::domainChanged, [&]() { setRaised(false); }); + connect(hmdScriptingInterface.data(), &HMDScriptingInterface::displayModeChanged, [&]() { setRaised(false); }); } void Keyboard::registerKeyboardHighlighting() { @@ -483,7 +486,7 @@ void Keyboard::handleTriggerBegin(const OverlayID& overlayID, const PointerEvent AudioInjectorOptions audioOptions; audioOptions.localOnly = true; audioOptions.position = keyWorldPosition; - audioOptions.volume = 0.1f; + audioOptions.volume = 0.05f; AudioInjector::playSoundAndDelete(_keySound, audioOptions); @@ -835,8 +838,8 @@ void Keyboard::loadKeyboardFile(const QString& keyboardFile) { _textDisplay = textDisplay; _ignoreItemsLock.withWriteLock([&] { - _itemsToIgnore.push_back(_textDisplay.overlayID); - _itemsToIgnore.push_back(_anchor.overlayID); + _itemsToIgnore.append(_textDisplay.overlayID); + _itemsToIgnore.append(_anchor.overlayID); }); _layerIndex = 0; auto pointerManager = DependencyManager::get(); diff --git a/interface/src/ui/overlays/Overlays.cpp b/interface/src/ui/overlays/Overlays.cpp index 7593e12e07..22b123c85d 100644 --- a/interface/src/ui/overlays/Overlays.cpp +++ b/interface/src/ui/overlays/Overlays.cpp @@ -535,7 +535,7 @@ RayToOverlayIntersectionResult Overlays::findRayIntersectionVector(const PickRay bool bestIsFront = false; bool bestIsTablet = false; auto tabletIDs = qApp->getTabletIDs(); - + const QVector keyboardKeysToDiscard = DependencyManager::get()->getKeysID(); QMutexLocker locker(&_mutex); RayToOverlayIntersectionResult result; QMapIterator i(_overlaysWorld); @@ -545,7 +545,8 @@ RayToOverlayIntersectionResult Overlays::findRayIntersectionVector(const PickRay auto thisOverlay = std::dynamic_pointer_cast(i.value()); if ((overlaysToDiscard.size() > 0 && overlaysToDiscard.contains(thisID)) || - (overlaysToInclude.size() > 0 && !overlaysToInclude.contains(thisID))) { + (overlaysToInclude.size() > 0 && !overlaysToInclude.contains(thisID)) || + (keyboardKeysToDiscard.size() > 0 && keyboardKeysToDiscard.contains(thisID))) { continue; } From 588303599185bb1adbd43da95b7a41d3db42fd15 Mon Sep 17 00:00:00 2001 From: Simon Walton Date: Fri, 16 Nov 2018 14:50:03 -0800 Subject: [PATCH 270/286] Gcc doesn't like QTemporaryFile in a map so store unique ptrs --- domain-server/src/DomainServer.cpp | 5 +++-- domain-server/src/DomainServer.h | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index 670e179f81..20ca5c94d4 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -2524,10 +2524,11 @@ bool DomainServer::processPendingContent(HTTPConnection* connection, QString ite const auto peerAddressHash = qHash(connection->socket()->peerAddress()); if (_pendingContentFiles.find(peerAddressHash) == _pendingContentFiles.end()) { - _pendingContentFiles.emplace(peerAddressHash, TEMPORARY_CONTENT_FILEPATH); + std::unique_ptr newTemp(new QTemporaryFile(TEMPORARY_CONTENT_FILEPATH)); + _pendingContentFiles[peerAddressHash] = std::move(newTemp); } - QTemporaryFile& _pendingFileContent = _pendingContentFiles[peerAddressHash]; + QTemporaryFile& _pendingFileContent = *_pendingContentFiles[peerAddressHash]; if (!_pendingFileContent.open()) { _pendingContentFiles.erase(peerAddressHash); connection->respond(HTTPConnection::StatusCode400); diff --git a/domain-server/src/DomainServer.h b/domain-server/src/DomainServer.h index 2d5729be59..5d6cd4e5f9 100644 --- a/domain-server/src/DomainServer.h +++ b/domain-server/src/DomainServer.h @@ -285,7 +285,7 @@ private: QHash> _pendingOAuthConnections; std::unordered_map _pendingUploadedContents; - std::unordered_map _pendingContentFiles; + std::unordered_map> _pendingContentFiles; QThread _assetClientThread; }; From 2d2cc0eaca55a14af050e622d63168872d6f6431 Mon Sep 17 00:00:00 2001 From: Simon Walton Date: Fri, 16 Nov 2018 15:56:39 -0800 Subject: [PATCH 271/286] Use a random session id for a sequence of chunks --- .../resources/web/content/js/content.js | 13 +++++++---- domain-server/src/DomainServer.cpp | 22 ++++++++++--------- domain-server/src/DomainServer.h | 4 ++-- 3 files changed, 23 insertions(+), 16 deletions(-) diff --git a/domain-server/resources/web/content/js/content.js b/domain-server/resources/web/content/js/content.js index 365c5e8403..afee2cc8a9 100644 --- a/domain-server/resources/web/content/js/content.js +++ b/domain-server/resources/web/content/js/content.js @@ -10,7 +10,7 @@ $(document).ready(function(){ function progressBarHTML(extraClass, label) { var html = "

    "; html += "
    "; - html += label + "
    "; + html += "
    "; return html; } @@ -24,10 +24,14 @@ $(document).ready(function(){ }); } - function uploadNextChunk(file, offset) { + function uploadNextChunk(file, offset, id) { if (offset == undefined) { offset = 0; } + if (id == undefined) { + // Identify this upload session + id = Math.round(Math.random() * 2147483647); + } var fileSize = file.size; var filename = file.name; @@ -45,6 +49,7 @@ $(document).ready(function(){ url: '/content/upload', type: 'POST', timeout: 30000, // 30 s + headers: {"X-Session-Id": id}, cache: false, processData: false, contentType: false, @@ -64,7 +69,7 @@ $(document).ready(function(){ if (!isFinal) { ajaxObject.done(function (data, textStatus, jqXHR) - { uploadNextChunk(file, offset + CHUNK_SIZE); }); + { uploadNextChunk(file, offset + CHUNK_SIZE, id); }); } else { ajaxObject.done(function(data, textStatus, jqXHR) { isRestoring = true; @@ -210,7 +215,7 @@ $(document).ready(function(){ function updateProgressBars($progressBar, value) { $progressBar.attr('aria-valuenow', value).attr('style', 'width: ' + value + '%'); - $progressBar.find('.ongoing-msg').html(" " + Math.round(value) + "% Complete"); + $progressBar.find('.ongoing-msg').html(" " + Math.round(value) + "%"); } function reloadBackupInformation() { diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index 20ca5c94d4..2a5ada729c 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -2519,18 +2519,21 @@ bool DomainServer::handleHTTPSRequest(HTTPSConnection* connection, const QUrl &u } bool DomainServer::processPendingContent(HTTPConnection* connection, QString itemName, QString filename, QByteArray dataChunk) { + static const QString UPLOAD_SESSION_KEY { "X-Session-Id" }; + QByteArray sessionIdBytes = connection->requestHeader(UPLOAD_SESSION_KEY); + int sessionId = sessionIdBytes.toInt(); + if (filename.endsWith(".zip", Qt::CaseInsensitive)) { static const QString TEMPORARY_CONTENT_FILEPATH { QDir::tempPath() + "/hifiUploadContent_XXXXXX.zip" }; - const auto peerAddressHash = qHash(connection->socket()->peerAddress()); - if (_pendingContentFiles.find(peerAddressHash) == _pendingContentFiles.end()) { + if (_pendingContentFiles.find(sessionId) == _pendingContentFiles.end()) { std::unique_ptr newTemp(new QTemporaryFile(TEMPORARY_CONTENT_FILEPATH)); - _pendingContentFiles[peerAddressHash] = std::move(newTemp); + _pendingContentFiles[sessionId] = std::move(newTemp); } - QTemporaryFile& _pendingFileContent = *_pendingContentFiles[peerAddressHash]; + QTemporaryFile& _pendingFileContent = *_pendingContentFiles[sessionId]; if (!_pendingFileContent.open()) { - _pendingContentFiles.erase(peerAddressHash); + _pendingContentFiles.erase(sessionId); connection->respond(HTTPConnection::StatusCode400); return false; } @@ -2543,16 +2546,15 @@ bool DomainServer::processPendingContent(HTTPConnection* connection, QString ite if (itemName == "restore-file-chunk-final" || itemName == "restore-file") { auto deferred = makePromise("recoverFromUploadedBackup"); - deferred->then([this, peerAddressHash](QString error, QVariantMap result) { - _pendingContentFiles.erase(peerAddressHash); + deferred->then([this, sessionId](QString error, QVariantMap result) { + _pendingContentFiles.erase(sessionId); }); _contentManager->recoverFromUploadedFile(deferred, _pendingFileContent.fileName()); } } else if (filename.endsWith(".json", Qt::CaseInsensitive) || filename.endsWith(".json.gz", Qt::CaseInsensitive)) { - auto peerAddressHash = qHash(connection->socket()->peerAddress()); - QByteArray& _pendingUploadedContent = _pendingUploadedContents[peerAddressHash]; + QByteArray& _pendingUploadedContent = _pendingUploadedContents[sessionId]; _pendingUploadedContent += dataChunk; connection->respond(HTTPConnection::StatusCode200); @@ -2560,7 +2562,7 @@ bool DomainServer::processPendingContent(HTTPConnection* connection, QString ite // invoke our method to hand the new octree file off to the octree server QMetaObject::invokeMethod(this, "handleOctreeFileReplacement", Qt::QueuedConnection, Q_ARG(QByteArray, _pendingUploadedContent)); - _pendingUploadedContents.erase(peerAddressHash); + _pendingUploadedContents.erase(sessionId); } } else { connection->respond(HTTPConnection::StatusCode400); diff --git a/domain-server/src/DomainServer.h b/domain-server/src/DomainServer.h index 5d6cd4e5f9..f0c20241a2 100644 --- a/domain-server/src/DomainServer.h +++ b/domain-server/src/DomainServer.h @@ -284,8 +284,8 @@ private: QHash> _pendingOAuthConnections; - std::unordered_map _pendingUploadedContents; - std::unordered_map> _pendingContentFiles; + std::unordered_map _pendingUploadedContents; + std::unordered_map> _pendingContentFiles; QThread _assetClientThread; }; From 4388763c34f69dd31491fa114e78778effa8d9e0 Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Fri, 16 Nov 2018 16:04:40 -0800 Subject: [PATCH 272/286] fixing keyboard not being dismissing when backout of the login screen --- interface/resources/qml/dialogs/TabletLoginDialog.qml | 1 + 1 file changed, 1 insertion(+) diff --git a/interface/resources/qml/dialogs/TabletLoginDialog.qml b/interface/resources/qml/dialogs/TabletLoginDialog.qml index dad2bb91aa..0b19e34b0a 100644 --- a/interface/resources/qml/dialogs/TabletLoginDialog.qml +++ b/interface/resources/qml/dialogs/TabletLoginDialog.qml @@ -141,6 +141,7 @@ TabletModalWindow { Component.onDestruction: { loginKeyboard.raised = false; + KeyboardScriptingInterface.raised = false; } Component.onCompleted: { From ba790ae47013470e9bce78a87e07c060e1a1c5b8 Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Sat, 17 Nov 2018 01:20:36 +0100 Subject: [PATCH 273/286] fix behavior after merge. --- scripts/system/html/js/entityList.js | 6 +++--- scripts/system/html/js/listView.js | 7 ++++--- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/scripts/system/html/js/entityList.js b/scripts/system/html/js/entityList.js index fe657cd8b5..84ad59df36 100644 --- a/scripts/system/html/js/entityList.js +++ b/scripts/system/html/js/entityList.js @@ -396,8 +396,8 @@ function loaded() { elEntityTableHeaderRow = document.querySelectorAll("#entity-table thead th"); - entityList = new ListView(elEntityTableBody, elEntityTableScroll, elEntityTableHeaderRow, - createRow, updateRow, clearRow, preRefresh, postRefresh, WINDOW_NONVARIABLE_HEIGHT); + entityList = new ListView(elEntityTableBody, elEntityTableScroll, elEntityTableHeaderRow, createRow, updateRow, + clearRow, preRefresh, postRefresh, preRefresh, WINDOW_NONVARIABLE_HEIGHT); entityListContextMenu = new EntityListContextMenu(); @@ -448,7 +448,7 @@ function loaded() { function preRefresh() { // move the rename input to the body - if (elRenameInput) { + if (!isRenameFieldBeingMoved && elRenameInput) { isRenameFieldBeingMoved = true; document.body.appendChild(elRenameInput); // keep the focus diff --git a/scripts/system/html/js/listView.js b/scripts/system/html/js/listView.js index 482576e2fe..49a91388a5 100644 --- a/scripts/system/html/js/listView.js +++ b/scripts/system/html/js/listView.js @@ -13,8 +13,8 @@ debugPrint = function (message) { console.log(message); }; -function ListView(elTableBody, elTableScroll, elTableHeaderRow, createRowFunction, - updateRowFunction, clearRowFunction, preRefreshFunction, postRefreshFunction, WINDOW_NONVARIABLE_HEIGHT) { +function ListView(elTableBody, elTableScroll, elTableHeaderRow, createRowFunction, updateRowFunction, clearRowFunction, + preRefreshFunction, postRefreshFunction, preResizeFunction, WINDOW_NONVARIABLE_HEIGHT) { this.elTableBody = elTableBody; this.elTableScroll = elTableScroll; this.elTableHeaderRow = elTableHeaderRow; @@ -27,6 +27,7 @@ function ListView(elTableBody, elTableScroll, elTableHeaderRow, createRowFunctio this.clearRowFunction = clearRowFunction; this.preRefreshFunction = preRefreshFunction; this.postRefreshFunction = postRefreshFunction; + this.preResizeFunction = preResizeFunction; // the list of row elements created in the table up to max viewable height plus SCROLL_ROWS rows for scrolling buffer this.elRows = []; @@ -248,7 +249,7 @@ ListView.prototype = { debugPrint("ListView.resize - no valid table body or table scroll element"); return; } - + this.preResizeFunction(); let prevScrollTop = this.elTableScroll.scrollTop; // take up available window space From 3baebf55c5d263ddcaff9edd0f43dafb4eaf3a05 Mon Sep 17 00:00:00 2001 From: Simon Walton Date: Fri, 16 Nov 2018 17:33:13 -0800 Subject: [PATCH 274/286] Don't enforce key presence in models.json --- .../octree/src/OctreeEntitiesFileParser.cpp | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/libraries/octree/src/OctreeEntitiesFileParser.cpp b/libraries/octree/src/OctreeEntitiesFileParser.cpp index 873eaff0e1..962f744c34 100644 --- a/libraries/octree/src/OctreeEntitiesFileParser.cpp +++ b/libraries/octree/src/OctreeEntitiesFileParser.cpp @@ -46,8 +46,13 @@ bool OctreeEntitiesFileParser::parseEntities(QVariantMap& parsedEntities) { bool gotId = false; bool gotVersion = false; - while (!(gotDataVersion && gotEntities && gotId && gotVersion)) { - if (nextToken() != '"') { + int token = nextToken(); + + while (true) { + if (token == '}') { + break; + } + else if (token != '"') { _errorString = "Incorrect key string"; return false; } @@ -144,15 +149,13 @@ bool OctreeEntitiesFileParser::parseEntities(QVariantMap& parsedEntities) { return false; } - if (gotDataVersion && gotEntities && gotId && gotVersion) { - break; - } else if (nextToken() != ',') { - _errorString = "Id/value incorrectly terminated"; - return false; + token = nextToken(); + if (token == ',') { + token = nextToken(); } } - if (nextToken() != '}' || nextToken() != -1) { + if (nextToken() != -1) { _errorString = "Ill-formed end of object"; return false; } From a0a87e71704515cd82689e7aea7d0bb83e1aa233 Mon Sep 17 00:00:00 2001 From: birarda Date: Mon, 19 Nov 2018 10:54:25 -0800 Subject: [PATCH 275/286] consider avatar traits bytes in budget and omit if over --- assignment-client/src/avatars/AvatarMixerSlave.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/assignment-client/src/avatars/AvatarMixerSlave.cpp b/assignment-client/src/avatars/AvatarMixerSlave.cpp index 7e0b6a00ad..a037f24345 100644 --- a/assignment-client/src/avatars/AvatarMixerSlave.cpp +++ b/assignment-client/src/avatars/AvatarMixerSlave.cpp @@ -416,7 +416,8 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node) // NOTE: Here's where we determine if we are over budget and drop remaining avatars, // or send minimal avatar data in uncommon case of PALIsOpen. int minimRemainingAvatarBytes = minimumBytesPerAvatar * remainingAvatars; - bool overBudget = (identityBytesSent + numAvatarDataBytes + minimRemainingAvatarBytes) > maxAvatarBytesPerFrame; + auto frameByteEstimate = identityBytesSent + traitBytesSent + numAvatarDataBytes + minimRemainingAvatarBytes; + bool overBudget = frameByteEstimate > maxAvatarBytesPerFrame; if (overBudget) { if (PALIsOpen) { _stats.overBudgetAvatars++; @@ -497,8 +498,11 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node) _stats.avatarDataPackingElapsedTime += (quint64) chrono::duration_cast(endAvatarDataPacking - startAvatarDataPacking).count(); - // use helper to add any changed traits to our packet list - traitBytesSent += addChangedTraitsToBulkPacket(nodeData, otherNodeData, *traitsPacketList); + if (!overBudget) { + // use helper to add any changed traits to our packet list + traitBytesSent += addChangedTraitsToBulkPacket(nodeData, otherNodeData, *traitsPacketList); + } + remainingAvatars--; } From 84e8e21d8512b80a80d8765601200936de7da058 Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Mon, 19 Nov 2018 21:08:21 +0100 Subject: [PATCH 276/286] don't trigger virtual keyboard scroll on non input focus. --- interface/resources/html/raiseAndLowerKeyboard.js | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/interface/resources/html/raiseAndLowerKeyboard.js b/interface/resources/html/raiseAndLowerKeyboard.js index 8cdb3c2327..db8ff24fcb 100644 --- a/interface/resources/html/raiseAndLowerKeyboard.js +++ b/interface/resources/html/raiseAndLowerKeyboard.js @@ -18,7 +18,7 @@ window.isKeyboardRaised = false; window.isNumericKeyboard = false; window.isPasswordField = false; - window.lastActiveElement = null; + window.lastActiveInputElement = null; function getActiveElement() { return document.activeElement; @@ -70,11 +70,15 @@ var keyboardRaised = shouldRaiseKeyboard(); var numericKeyboard = shouldSetNumeric(); var passwordField = shouldSetPasswordField(); - var activeElement = getActiveElement(); + var activeInputElement = null; + // Only set the active input element when there is an input element focussed, otherwise it will scroll on body focus as well. + if (keyboardRaised) { + activeInputElement = getActiveElement(); + } if (isWindowFocused && (keyboardRaised !== window.isKeyboardRaised || numericKeyboard !== window.isNumericKeyboard - || passwordField !== window.isPasswordField || activeElement !== window.lastActiveElement)) { + || passwordField !== window.isPasswordField || activeInputElement !== window.lastActiveInputElement)) { if (typeof EventBridge !== "undefined" && EventBridge !== null) { EventBridge.emitWebEvent( @@ -96,7 +100,7 @@ window.isKeyboardRaised = keyboardRaised; window.isNumericKeyboard = numericKeyboard; window.isPasswordField = passwordField; - window.lastActiveElement = activeElement; + window.lastActiveInputElement = activeInputElement; } }, POLL_FREQUENCY); From 359a841f693c4609d28b32c303418562708640cf Mon Sep 17 00:00:00 2001 From: Clement Date: Mon, 19 Nov 2018 12:20:31 -0800 Subject: [PATCH 277/286] Turn off throttling behavior when not throttling --- assignment-client/src/audio/AudioMixer.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp index e8568a7ff3..77f416f31e 100644 --- a/assignment-client/src/audio/AudioMixer.cpp +++ b/assignment-client/src/audio/AudioMixer.cpp @@ -435,7 +435,11 @@ void AudioMixer::start() { QCoreApplication::processEvents(); } - int numToRetain = nodeList->size() * (1 - _throttlingRatio); + int numToRetain = -1; + assert(_throttlingRatio >= 0.0f && _throttlingRatio <= 1.0f); + if (_throttlingRatio > EPSILON) { + numToRetain = nodeList->size() * (1.0f - _throttlingRatio); + } nodeList->nestedEach([&](NodeList::const_iterator cbegin, NodeList::const_iterator cend) { // mix across slave threads auto mixTimer = _mixTiming.timer(); From 193b9752b80981a5a87dfda0ac05dbf4ee2ad533 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Mon, 19 Nov 2018 13:50:56 -0800 Subject: [PATCH 278/286] Fix styling of properties window when narrow --- scripts/system/html/css/edit-style.css | 16 +++++++++------- scripts/system/html/js/entityProperties.js | 11 +++++++---- 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/scripts/system/html/css/edit-style.css b/scripts/system/html/css/edit-style.css index bec926ad03..5b5c9e057c 100644 --- a/scripts/system/html/css/edit-style.css +++ b/scripts/system/html/css/edit-style.css @@ -590,9 +590,6 @@ div.section[collapsed="true"], div.section[collapsed="true"] > .section-header { background-color: #373737; } -.section-header { - cursor: pointer; -} .section-header span { font-size: 30px; @@ -948,12 +945,12 @@ div.refresh input[type="button"] { } .draggable-number.left-arrow { top: -5px; - right: 106px; + left: 0px; transform: rotate(180deg); } .draggable-number.right-arrow { top: -5px; - left: 106px; + right: 0px; } .draggable-number input[type=number] { position: absolute; @@ -1123,7 +1120,7 @@ textarea:enabled[scrolling="true"]::-webkit-resizer { } -body#entity-list-body { +div#grid-section, body#entity-list-body { padding-bottom: 0; margin: 16px; } @@ -1552,7 +1549,6 @@ input.rename-entity { .container { display: flex; flex-flow: row nowrap; - justify-content: space-around; margin-bottom: 8px; min-height: 28px; } @@ -1602,6 +1598,8 @@ input.rename-entity { .xyz.fstuple, .pyr.fstuple { position: relative; left: -12px; + min-width: 50px; + width: 100px; } .rgb.fstuple .tuple { @@ -1660,3 +1658,7 @@ input.number-slider { #placeholder-property-type { min-width: 0px; } + +.collapse-icon { + cursor: pointer; +} diff --git a/scripts/system/html/js/entityProperties.js b/scripts/system/html/js/entityProperties.js index a4cde98abe..d3e0751732 100644 --- a/scripts/system/html/js/entityProperties.js +++ b/scripts/system/html/js/entityProperties.js @@ -1158,18 +1158,21 @@ const GROUPS = [ label: "Link", type: "string", propertyID: "href", + placeholder: "URL", }, { label: "Script", type: "string", buttons: [ { id: "reload", label: "F", className: "glyph", onClick: reloadScripts } ], propertyID: "script", + placeholder: "URL", }, { label: "Server Script", type: "string", buttons: [ { id: "reload", label: "F", className: "glyph", onClick: reloadServerScripts } ], propertyID: "serverScripts", + placeholder: "URL", }, { label: "Server Script Status", @@ -2771,7 +2774,7 @@ function loaded() { elLegend.appendChild(createElementFromHTML(`
    ${group.label}
    `)); let elSpan = document.createElement('span'); - elSpan.className = ".collapse-icon"; + elSpan.className = "collapse-icon"; elSpan.innerText = "M"; elLegend.appendChild(elSpan); elGroup.appendChild(elLegend); @@ -3311,14 +3314,14 @@ function loaded() { getPropertyInputElement("image").addEventListener('change', createImageURLUpdateFunction('textures', false)); // Collapsible sections - let elCollapsible = document.getElementsByClassName("section-header"); + let elCollapsible = document.getElementsByClassName("collapse-icon"); let toggleCollapsedEvent = function(event) { - let element = this.parentNode; + let element = this.parentNode.parentNode; let isCollapsed = element.dataset.collapsed !== "true"; element.dataset.collapsed = isCollapsed ? "true" : false; element.setAttribute("collapsed", isCollapsed ? "true" : "false"); - element.getElementsByClassName(".collapse-icon")[0].textContent = isCollapsed ? "L" : "M"; + this.textContent = isCollapsed ? "L" : "M"; }; for (let collapseIndex = 0, numCollapsibles = elCollapsible.length; collapseIndex < numCollapsibles; ++collapseIndex) { From 7694aa8e6637cb13fc2bf1fec0c7ae00a2e506cc Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Mon, 19 Nov 2018 13:51:25 -0800 Subject: [PATCH 279/286] Fix draggable number going into edit too often --- scripts/system/html/js/draggableNumber.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/system/html/js/draggableNumber.js b/scripts/system/html/js/draggableNumber.js index 1f4bc21441..4d1d008f7f 100644 --- a/scripts/system/html/js/draggableNumber.js +++ b/scripts/system/html/js/draggableNumber.js @@ -32,7 +32,7 @@ DraggableNumber.prototype = { mouseUp: function(event) { if (event.target === this.elText && this.initialMouseEvent) { let dx = event.clientX - this.initialMouseEvent.clientX; - if (dx <= DELTA_X_FOCUS_THRESHOLD) { + if (Math.abs(dx) <= DELTA_X_FOCUS_THRESHOLD) { this.elInput.style.visibility = "visible"; this.elText.style.visibility = "hidden"; } From 007396346c7382783446e26b1ba12031a2e917d7 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Mon, 19 Nov 2018 14:07:24 -0800 Subject: [PATCH 280/286] Fix grid tool styling --- scripts/system/html/gridControls.html | 55 ++++++++++++++------------ scripts/system/html/js/gridControls.js | 24 ++--------- 2 files changed, 33 insertions(+), 46 deletions(-) diff --git a/scripts/system/html/gridControls.html b/scripts/system/html/gridControls.html index cd646fed51..4be002619a 100644 --- a/scripts/system/html/gridControls.html +++ b/scripts/system/html/gridControls.html @@ -19,47 +19,52 @@ -
    -
    - -
    -
    - +
    +
    +
    + + +
    -
    - +
    +
    + + +
    -
    +
    +
    -
    +
    +
    +
    -
    -
    +
    - -
    -
    -
    - -
    -
    -
    -
    +
    +
    -
    - +
    + +
    +
    +
    +
    +
    + +
    - +
    - \ No newline at end of file + diff --git a/scripts/system/html/js/gridControls.js b/scripts/system/html/js/gridControls.js index 70e91071fb..b2d5988938 100644 --- a/scripts/system/html/js/gridControls.js +++ b/scripts/system/html/js/gridControls.js @@ -83,44 +83,26 @@ function loaded() { var gridColor = { red: 255, green: 255, blue: 255 }; var elColor = document.getElementById("grid-color"); - var elColorRed = document.getElementById("grid-color-red"); - var elColorGreen = document.getElementById("grid-color-green"); - var elColorBlue = document.getElementById("grid-color-blue"); elColor.style.backgroundColor = "rgb(" + gridColor.red + "," + gridColor.green + "," + gridColor.blue + ")"; - elColorRed.value = gridColor.red; - elColorGreen.value = gridColor.green; - elColorBlue.value = gridColor.blue; - - var colorChangeFunction = function () { - gridColor = { red: elColorRed.value, green: elColorGreen.value, blue: elColorBlue.value }; - elColor.style.backgroundColor = "rgb(" + gridColor.red + "," + gridColor.green + "," + gridColor.blue + ")"; - emitUpdate(); - }; var colorPickFunction = function (red, green, blue) { - elColorRed.value = red; - elColorGreen.value = green; - elColorBlue.value = blue; gridColor = { red: red, green: green, blue: blue }; emitUpdate(); }; - elColorRed.addEventListener('change', colorChangeFunction); - elColorGreen.addEventListener('change', colorChangeFunction); - elColorBlue.addEventListener('change', colorChangeFunction); $('#grid-color').colpick({ colorScheme: 'dark', - layout: 'hex', + layout: 'rgbhex', color: { r: gridColor.red, g: gridColor.green, b: gridColor.blue }, + submit: false, onShow: function (colpick) { $('#grid-color').attr('active', 'true'); }, onHide: function (colpick) { $('#grid-color').attr('active', 'false'); }, - onSubmit: function (hsb, hex, rgb, el) { + onChange: function (hsb, hex, rgb, el) { $(el).css('background-color', '#' + hex); - $(el).colpickHide(); colorPickFunction(rgb.r, rgb.g, rgb.b); } }); From cd177a445703698596fdfb974d1fabd2dbf5261c Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 19 Nov 2018 15:25:14 -0800 Subject: [PATCH 281/286] catch QString exceptions that ModelCache can throw --- .../model-networking/src/model-networking/ModelCache.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/libraries/model-networking/src/model-networking/ModelCache.cpp b/libraries/model-networking/src/model-networking/ModelCache.cpp index e3edf7bc53..adb23ecd7d 100644 --- a/libraries/model-networking/src/model-networking/ModelCache.cpp +++ b/libraries/model-networking/src/model-networking/ModelCache.cpp @@ -243,6 +243,13 @@ void GeometryReader::run() { QMetaObject::invokeMethod(resource.data(), "finishedLoading", Q_ARG(bool, false)); } + } catch (QString& e) { + qCWarning(modelnetworking) << "Exception while loading" << _url << "--" << e; + auto resource = _resource.toStrongRef(); + if (resource) { + QMetaObject::invokeMethod(resource.data(), "finishedLoading", + Q_ARG(bool, false)); + } } } From 2a7e22bf317698f2d5d94ce011b8b07c11ef6b43 Mon Sep 17 00:00:00 2001 From: Simon Walton Date: Mon, 19 Nov 2018 15:26:08 -0800 Subject: [PATCH 282/286] Identify initial chunk of an upload so as to be more resilient --- .../resources/web/content/js/content.js | 10 ++++++++-- domain-server/src/DomainServer.cpp | 16 ++++++++++++++-- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/domain-server/resources/web/content/js/content.js b/domain-server/resources/web/content/js/content.js index afee2cc8a9..85bd9e68b3 100644 --- a/domain-server/resources/web/content/js/content.js +++ b/domain-server/resources/web/content/js/content.js @@ -43,7 +43,13 @@ $(document).ready(function(){ var chunk = file.slice(offset, offset + nextChunkSize, file.type); var chunkFormData = new FormData(); - var formItemName = isFinal ? 'restore-file-chunk-final' : 'restore-file-chunk'; + var formItemName = 'restore-file-chunk'; + if (offset == 0) { + formItemName = isFinal ? 'restore-file-chunk-only' : 'restore-file-chunk-initial'; + } else if (isFinal) { + formItemName = 'restore-file-chunk-final'; + } + chunkFormData.append(formItemName, chunk, filename); var ajaxParams = { url: '/content/upload', @@ -57,7 +63,7 @@ $(document).ready(function(){ }; var ajaxObject = $.ajax(ajaxParams); - ajaxObject.fail(function(jqXHR, textStatus, errorThrown) { + ajaxObject.fail(function (jqXHR, textStatus, errorThrown) { showErrorMessage( "Error", "There was a problem restoring domain content.\n" diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index 2a5ada729c..2d1abc8c71 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -2523,12 +2523,20 @@ bool DomainServer::processPendingContent(HTTPConnection* connection, QString ite QByteArray sessionIdBytes = connection->requestHeader(UPLOAD_SESSION_KEY); int sessionId = sessionIdBytes.toInt(); + bool newUpload = itemName == "restore-file" || itemName == "restore-file-chunk-initial" || itemName == "restore-file-chunk-only"; + if (filename.endsWith(".zip", Qt::CaseInsensitive)) { static const QString TEMPORARY_CONTENT_FILEPATH { QDir::tempPath() + "/hifiUploadContent_XXXXXX.zip" }; if (_pendingContentFiles.find(sessionId) == _pendingContentFiles.end()) { + if (!newUpload) { + return false; + } std::unique_ptr newTemp(new QTemporaryFile(TEMPORARY_CONTENT_FILEPATH)); _pendingContentFiles[sessionId] = std::move(newTemp); + } else if (newUpload) { + qCDebug(domain_server) << "New upload received using existing session ID"; + _pendingContentFiles[sessionId]->resize(0); } QTemporaryFile& _pendingFileContent = *_pendingContentFiles[sessionId]; @@ -2543,7 +2551,7 @@ bool DomainServer::processPendingContent(HTTPConnection* connection, QString ite // Respond immediately - will timeout if we wait for restore. connection->respond(HTTPConnection::StatusCode200); - if (itemName == "restore-file-chunk-final" || itemName == "restore-file") { + if (itemName == "restore-file" || itemName == "restore-file-chunk-final" || itemName == "restore-file-chunk-only") { auto deferred = makePromise("recoverFromUploadedBackup"); deferred->then([this, sessionId](QString error, QVariantMap result) { @@ -2554,11 +2562,15 @@ bool DomainServer::processPendingContent(HTTPConnection* connection, QString ite } } else if (filename.endsWith(".json", Qt::CaseInsensitive) || filename.endsWith(".json.gz", Qt::CaseInsensitive)) { + if (_pendingUploadedContents.find(sessionId) == _pendingUploadedContents.end() && !newUpload) { + qCDebug(domain_server) << "Json upload with invalid session ID received"; + return false; + } QByteArray& _pendingUploadedContent = _pendingUploadedContents[sessionId]; _pendingUploadedContent += dataChunk; connection->respond(HTTPConnection::StatusCode200); - if (itemName == "restore-file-chunk-final" || itemName == "restore-file") { + if (itemName == "restore-file" || itemName == "restore-file-chunk-final" || itemName == "restore-file-chunk-only") { // invoke our method to hand the new octree file off to the octree server QMetaObject::invokeMethod(this, "handleOctreeFileReplacement", Qt::QueuedConnection, Q_ARG(QByteArray, _pendingUploadedContent)); From 26a50e182045115417e3921937822cde3c0e7a67 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 19 Nov 2018 15:38:12 -0800 Subject: [PATCH 283/286] don't log urls! --- libraries/model-networking/src/model-networking/ModelCache.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/model-networking/src/model-networking/ModelCache.cpp b/libraries/model-networking/src/model-networking/ModelCache.cpp index adb23ecd7d..d3f24073c4 100644 --- a/libraries/model-networking/src/model-networking/ModelCache.cpp +++ b/libraries/model-networking/src/model-networking/ModelCache.cpp @@ -244,7 +244,7 @@ void GeometryReader::run() { Q_ARG(bool, false)); } } catch (QString& e) { - qCWarning(modelnetworking) << "Exception while loading" << _url << "--" << e; + qCWarning(modelnetworking) << "Exception while loading model --" << e; auto resource = _resource.toStrongRef(); if (resource) { QMetaObject::invokeMethod(resource.data(), "finishedLoading", From ff6d43d0e4890d8612bb0d177064ad2e3dfdb5e2 Mon Sep 17 00:00:00 2001 From: David Back Date: Mon, 19 Nov 2018 16:15:11 -0800 Subject: [PATCH 284/286] block updates during dragging --- scripts/system/edit.js | 9 ++ scripts/system/html/js/draggableNumber.js | 57 +++++-- scripts/system/html/js/entityProperties.js | 166 +++++++++++++-------- 3 files changed, 150 insertions(+), 82 deletions(-) diff --git a/scripts/system/edit.js b/scripts/system/edit.js index 48fdb8e565..85fd8c536d 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -2223,6 +2223,7 @@ var PropertiesTool = function (opts) { // are selected or if no entity is selected this will be `null`. var currentSelectedEntityID = null; var statusMonitor = null; + var nextPropertyUpdateDisabled = false; that.setVisible = function (newVisible) { visible = newVisible; @@ -2260,6 +2261,11 @@ var PropertiesTool = function (opts) { }; function updateSelections(selectionUpdated) { + if (nextPropertyUpdateDisabled) { + nextPropertyUpdateDisabled = false; + return; + } + var data = { type: 'update', spaceMode: selectionDisplay.getSpaceMode() @@ -2356,6 +2362,7 @@ var PropertiesTool = function (opts) { } } pushCommandForSelections(); + nextPropertyUpdateDisabled = data.blockUpdateCallback === true; selectionManager._update(false, this); } else if (data.type === 'saveUserData' || data.type === 'saveMaterialData') { //the event bridge and json parsing handle our avatar id string differently. @@ -2466,6 +2473,8 @@ var PropertiesTool = function (opts) { tooltips: Script.require('./assets/data/createAppTooltips.json'), hmdActive: HMD.active, }); + } else if (data.type === "updateProperties") { + updateSelections(true); } }; diff --git a/scripts/system/html/js/draggableNumber.js b/scripts/system/html/js/draggableNumber.js index 1f4bc21441..c08cac2ce4 100644 --- a/scripts/system/html/js/draggableNumber.js +++ b/scripts/system/html/js/draggableNumber.js @@ -8,11 +8,14 @@ const DELTA_X_FOCUS_THRESHOLD = 1; -function DraggableNumber(min, max, step, decimals) { +function DraggableNumber(min, max, step, decimals, dragStart, dragEnd) { this.min = min; this.max = max; this.step = step !== undefined ? step : 1; this.decimals = decimals; + this.dragStartFunction = dragStart; + this.dragEndFunction = dragEnd; + this.dragging = false; this.initialMouseEvent = null; this.lastMouseEvent = null; this.valueChangeFunction = null; @@ -32,7 +35,7 @@ DraggableNumber.prototype = { mouseUp: function(event) { if (event.target === this.elText && this.initialMouseEvent) { let dx = event.clientX - this.initialMouseEvent.clientX; - if (dx <= DELTA_X_FOCUS_THRESHOLD) { + if (Math.abs(dx) <= DELTA_X_FOCUS_THRESHOLD) { this.elInput.style.visibility = "visible"; this.elText.style.visibility = "hidden"; } @@ -41,22 +44,32 @@ DraggableNumber.prototype = { }, documentMouseMove: function(event) { - if (this.lastMouseEvent) { - let initialValue = this.elInput.value; - let dx = event.clientX - this.lastMouseEvent.clientX; - let changeValue = dx !== 0; - if (changeValue) { - while (dx !== 0) { - if (dx > 0) { - this.stepUp(); - --dx; - } else { - this.stepDown(); - ++dx; + if (this.initialMouseEvent) { + let dxFromInitial = event.clientX - this.initialMouseEvent.clientX; + if (Math.abs(dxFromInitial) > DELTA_X_FOCUS_THRESHOLD && this.lastMouseEvent) { + let initialValue = this.elInput.value; + let dx = event.clientX - this.lastMouseEvent.clientX; + let changeValue = dx !== 0; + if (changeValue) { + while (dx !== 0) { + if (dx > 0) { + this.elInput.stepUp(); + --dx; + } else { + this.elInput.stepDown(); + ++dx; + } + } + this.inputChange(); + if (this.valueChangeFunction) { + this.valueChangeFunction(); } } - if (this.valueChangeFunction) { - this.valueChangeFunction(); + if (!this.dragging) { + if (this.dragStartFunction) { + this.dragStartFunction(); + } + this.dragging = true; } } this.lastMouseEvent = event; @@ -64,6 +77,12 @@ DraggableNumber.prototype = { }, documentMouseUp: function(event) { + if (this.dragging) { + if (this.dragEndFunction) { + this.dragEndFunction(); + } + this.dragging = false; + } this.lastMouseEvent = null; document.removeEventListener("mousemove", this.onDocumentMouseMove); document.removeEventListener("mouseup", this.onDocumentMouseUp); @@ -72,11 +91,17 @@ DraggableNumber.prototype = { stepUp: function() { this.elInput.stepUp(); this.inputChange(); + if (this.valueChangeFunction) { + this.valueChangeFunction(); + } }, stepDown: function() { this.elInput.stepDown(); this.inputChange(); + if (this.valueChangeFunction) { + this.valueChangeFunction(); + } }, setValue: function(newValue) { diff --git a/scripts/system/html/js/entityProperties.js b/scripts/system/html/js/entityProperties.js index c49e0d88f6..850c012be0 100644 --- a/scripts/system/html/js/entityProperties.js +++ b/scripts/system/html/js/entityProperties.js @@ -1636,7 +1636,7 @@ function updateVisibleSpaceModeProperties() { * PROPERTY UPDATE FUNCTIONS */ -function updateProperty(originalPropertyName, propertyValue, isParticleProperty) { +function updateProperty(originalPropertyName, propertyValue, isParticleProperty, blockUpdateCallback) { let propertyUpdate = {}; // if this is a compound property name (i.e. animation.running) then split it by . up to 3 times let splitPropertyName = originalPropertyName.split('.'); @@ -1662,7 +1662,7 @@ function updateProperty(originalPropertyName, propertyValue, isParticleProperty) }); particleSyncDebounce(); } else { - updateProperties(propertyUpdate); + updateProperties(propertyUpdate, blockUpdateCallback); } } @@ -1671,66 +1671,89 @@ var particleSyncDebounce = _.debounce(function () { particlePropertyUpdates = {}; }, DEBOUNCE_TIMEOUT); -function updateProperties(propertiesToUpdate) { +function updateProperties(propertiesToUpdate, blockUpdateCallback) { + if (blockUpdateCallback === undefined) { + blockUpdateCallback = false; + } EventBridge.emitWebEvent(JSON.stringify({ id: lastEntityID, type: "update", - properties: propertiesToUpdate + properties: propertiesToUpdate, + blockUpdateCallback: blockUpdateCallback })); } -function createEmitTextPropertyUpdateFunction(propertyName, isParticleProperty) { +function createEmitTextPropertyUpdateFunction(property) { return function() { - updateProperty(propertyName, this.value, isParticleProperty); + updateProperty(property.name, this.value, property.isParticleProperty); }; } -function createEmitCheckedPropertyUpdateFunction(propertyName, inverse, isParticleProperty) { +function createEmitCheckedPropertyUpdateFunction(property) { return function() { - updateProperty(propertyName, inverse ? !this.checked : this.checked, isParticleProperty); + updateProperty(property.name, property.data.inverse ? !this.checked : this.checked, property.isParticleProperty); }; } -function createEmitNumberPropertyUpdateFunction(propertyName, multiplier, isParticleProperty) { +function createDragStartFunction(property) { return function() { + property.dragging = true; + }; +} + +function createDragEndFunction(property) { + return function() { + property.dragging = false; + EventBridge.emitWebEvent(JSON.stringify({ + type: "updateProperties" + })); + }; +} + +function createEmitNumberPropertyUpdateFunction(property) { + return function() { + let multiplier = property.data.multiplier; if (multiplier === undefined) { multiplier = 1; } let value = parseFloat(this.value) * multiplier; - updateProperty(propertyName, value, isParticleProperty); + updateProperty(property.name, value, property.isParticleProperty, property.dragging); }; } -function createEmitVec2PropertyUpdateFunction(propertyName, elX, elY, multiplier, isParticleProperty) { +function createEmitVec2PropertyUpdateFunction(property) { return function () { + let multiplier = property.data.multiplier; if (multiplier === undefined) { multiplier = 1; } let newValue = { - x: elX.value * multiplier, - y: elY.value * multiplier + x: property.elNumberX.elInput.value * multiplier, + y: property.elNumberY.elInput.value * multiplier }; - updateProperty(propertyName, newValue, isParticleProperty); + updateProperty(property.name, newValue, property.isParticleProperty, property.dragging); }; } -function createEmitVec3PropertyUpdateFunction(propertyName, elX, elY, elZ, multiplier, isParticleProperty) { +function createEmitVec3PropertyUpdateFunction(property) { return function() { + let multiplier = property.data.multiplier; if (multiplier === undefined) { multiplier = 1; } let newValue = { - x: elX.value * multiplier, - y: elY.value * multiplier, - z: elZ.value * multiplier + x: property.elNumberX.elInput.value * multiplier, + y: property.elNumberY.elInput.value * multiplier, + z: property.elNumberZ.elInput.value * multiplier }; - updateProperty(propertyName, newValue, isParticleProperty); + updateProperty(property.name, newValue, property.isParticleProperty, property.dragging); }; } -function createEmitColorPropertyUpdateFunction(propertyName, elRed, elGreen, elBlue, isParticleProperty) { +function createEmitColorPropertyUpdateFunction(property) { return function() { - emitColorPropertyUpdate(propertyName, elRed.value, elGreen.value, elBlue.value, isParticleProperty); + emitColorPropertyUpdate(property.name, property.elNumberR.elInput.value, property.elNumberG.elInput.value, + property.elNumberB.elInput.value, property.isParticleProperty); }; } @@ -1755,10 +1778,10 @@ function updateCheckedSubProperty(propertyName, propertyValue, subPropertyElemen updateProperty(propertyName, propertyValue, isParticleProperty); } -function createImageURLUpdateFunction(propertyName, isParticleProperty) { +function createImageURLUpdateFunction(property) { return function () { let newTextures = JSON.stringify({ "tex.picture": this.value }); - updateProperty(propertyName, newTextures, isParticleProperty); + updateProperty(property.name, newTextures, property.isParticleProperty); }; } @@ -1768,7 +1791,6 @@ function createImageURLUpdateFunction(propertyName, isParticleProperty) { */ function createStringProperty(property, elProperty) { - let propertyName = property.name; let elementID = property.elementID; let propertyData = property.data; @@ -1782,7 +1804,7 @@ function createStringProperty(property, elProperty) { `) - elInput.addEventListener('change', createEmitTextPropertyUpdateFunction(propertyName, property.isParticleProperty)); + elInput.addEventListener('change', createEmitTextPropertyUpdateFunction(property)); elProperty.appendChild(elInput); @@ -1821,30 +1843,30 @@ function createBoolProperty(property, elProperty) { elInput, propertyName, property.isParticleProperty); }); } else { - elInput.addEventListener('change', createEmitCheckedPropertyUpdateFunction(propertyName, propertyData.inverse, - property.isParticleProperty)); + elInput.addEventListener('change', createEmitCheckedPropertyUpdateFunction(property)); } return elInput; } function createNumberProperty(property, elProperty) { - let propertyName = property.name; let elementID = property.elementID; let propertyData = property.data; elProperty.className = "draggable-number"; - let elDraggableNumber = new DraggableNumber(propertyData.min, propertyData.max, - propertyData.step, propertyData.decimals); + let dragStartFunction = createDragStartFunction(property); + let dragEndFunction = createDragEndFunction(property); + + let elDraggableNumber = new DraggableNumber(propertyData.min, propertyData.max, propertyData.step, + propertyData.decimals, dragStartFunction, dragEndFunction); let defaultValue = propertyData.defaultValue; if (defaultValue !== undefined) { elDraggableNumber.elInput.value = defaultValue; } - let valueChangeFunction = createEmitNumberPropertyUpdateFunction(propertyName, propertyData.multiplier, - property.isParticleProperty); + let valueChangeFunction = createEmitNumberPropertyUpdateFunction(property); elDraggableNumber.setValueChangeFunction(valueChangeFunction); elDraggableNumber.elInput.setAttribute("id", elementID); @@ -1858,22 +1880,18 @@ function createNumberProperty(property, elProperty) { } function createVec3Property(property, elProperty) { - let propertyName = property.name; - let elementID = property.elementID; let propertyData = property.data; elProperty.className = propertyData.vec3Type + " fstuple"; - let elNumberX = createTupleNumberInput(elProperty, elementID, propertyData.subLabels[VECTOR_ELEMENTS.X_NUMBER], - propertyData.min, propertyData.max, propertyData.step, propertyData.decimals); - let elNumberY = createTupleNumberInput(elProperty, elementID, propertyData.subLabels[VECTOR_ELEMENTS.Y_NUMBER], - propertyData.min, propertyData.max, propertyData.step, propertyData.decimals); - let elNumberZ = createTupleNumberInput(elProperty, elementID, propertyData.subLabels[VECTOR_ELEMENTS.Z_NUMBER], - propertyData.min, propertyData.max, propertyData.step, propertyData.decimals); + let elNumberX = createTupleNumberInput(property, propertyData.subLabels[VECTOR_ELEMENTS.X_NUMBER]); + let elNumberY = createTupleNumberInput(property, propertyData.subLabels[VECTOR_ELEMENTS.Y_NUMBER]); + let elNumberZ = createTupleNumberInput(property, propertyData.subLabels[VECTOR_ELEMENTS.Z_NUMBER]); + elProperty.appendChild(elNumberX.elDiv); + elProperty.appendChild(elNumberY.elDiv); + elProperty.appendChild(elNumberZ.elDiv); - let valueChangeFunction = createEmitVec3PropertyUpdateFunction(propertyName, elNumberX.elInput, elNumberY.elInput, - elNumberZ.elInput, propertyData.multiplier, - property.isParticleProperty); + let valueChangeFunction = createEmitVec3PropertyUpdateFunction(property); elNumberX.setValueChangeFunction(valueChangeFunction); elNumberY.setValueChangeFunction(valueChangeFunction); elNumberZ.setValueChangeFunction(valueChangeFunction); @@ -1886,8 +1904,6 @@ function createVec3Property(property, elProperty) { } function createVec2Property(property, elProperty) { - let propertyName = property.name; - let elementID = property.elementID; let propertyData = property.data; elProperty.className = propertyData.vec2Type + " fstuple"; @@ -1897,13 +1913,15 @@ function createVec2Property(property, elProperty) { elProperty.appendChild(elTuple); - let elNumberX = createTupleNumberInput(elProperty, elementID, propertyData.subLabels[VECTOR_ELEMENTS.X_NUMBER], - propertyData.min, propertyData.max, propertyData.step, propertyData.decimals); - let elNumberY = createTupleNumberInput(elProperty, elementID, propertyData.subLabels[VECTOR_ELEMENTS.Y_NUMBER], - propertyData.min, propertyData.max, propertyData.step, propertyData.decimals); + let dragStartFunction = createDragStartFunction(property); + let dragEndFunction = createDragEndFunction(property); - let valueChangeFunction = createEmitVec2PropertyUpdateFunction(propertyName, elNumberX.elInput, elNumberY.elInput, - propertyData.multiplier, property.isParticleProperty); + let elNumberX = createTupleNumberInput(property, propertyData.subLabels[VECTOR_ELEMENTS.X_NUMBER]); + let elNumberY = createTupleNumberInput(property, propertyData.subLabels[VECTOR_ELEMENTS.Y_NUMBER]); + elProperty.appendChild(elNumberX.elDiv); + elProperty.appendChild(elNumberY.elDiv); + + let valueChangeFunction = createEmitVec2PropertyUpdateFunction(property); elNumberX.setValueChangeFunction(valueChangeFunction); elNumberY.setValueChangeFunction(valueChangeFunction); @@ -1916,6 +1934,7 @@ function createVec2Property(property, elProperty) { function createColorProperty(property, elProperty) { let propertyName = property.name; let elementID = property.elementID; + let propertyData = property.data; elProperty.className = "rgb fstuple"; @@ -1929,12 +1948,24 @@ function createColorProperty(property, elProperty) { elProperty.appendChild(elColorPicker); elProperty.appendChild(elTuple); - let elNumberR = createTupleNumberInput(elTuple, elementID, "red", COLOR_MIN, COLOR_MAX, COLOR_STEP); - let elNumberG = createTupleNumberInput(elTuple, elementID, "green", COLOR_MIN, COLOR_MAX, COLOR_STEP); - let elNumberB = createTupleNumberInput(elTuple, elementID, "blue", COLOR_MIN, COLOR_MAX, COLOR_STEP); + if (propertyData.min === undefined) { + propertyData.min = COLOR_MIN; + } + if (propertyData.max === undefined) { + propertyData.max = COLOR_MAX; + } + if (propertyData.step === undefined) { + propertyData.step = COLOR_STEP; + } - let valueChangeFunction = createEmitColorPropertyUpdateFunction(propertyName, elNumberR.elInput, elNumberG.elInput, - elNumberB.elInput, property.isParticleProperty); + let elNumberR = createTupleNumberInput(property, "red"); + let elNumberG = createTupleNumberInput(property, "green"); + let elNumberB = createTupleNumberInput(property, "blue"); + elTuple.appendChild(elNumberR.elDiv); + elTuple.appendChild(elNumberG.elDiv); + elTuple.appendChild(elNumberB.elDiv); + + let valueChangeFunction = createEmitColorPropertyUpdateFunction(property); elNumberR.setValueChangeFunction(valueChangeFunction); elNumberG.setValueChangeFunction(valueChangeFunction); elNumberB.setValueChangeFunction(valueChangeFunction); @@ -1973,7 +2004,6 @@ function createColorProperty(property, elProperty) { } function createDropdownProperty(property, propertyID, elProperty) { - let propertyName = property.name; let elementID = property.elementID; let propertyData = property.data; @@ -1990,7 +2020,7 @@ function createDropdownProperty(property, propertyID, elProperty) { elInput.add(option); } - elInput.addEventListener('change', createEmitTextPropertyUpdateFunction(propertyName, property.isParticleProperty)); + elInput.addEventListener('change', createEmitTextPropertyUpdateFunction(property)); elProperty.appendChild(elInput); @@ -1998,7 +2028,6 @@ function createDropdownProperty(property, propertyID, elProperty) { } function createTextareaProperty(property, elProperty) { - let propertyName = property.name; let elementID = property.elementID; let propertyData = property.data; @@ -2010,7 +2039,7 @@ function createTextareaProperty(property, elProperty) { elInput.readOnly = true; } - elInput.addEventListener('change', createEmitTextPropertyUpdateFunction(propertyName, property.isParticleProperty)); + elInput.addEventListener('change', createEmitTextPropertyUpdateFunction(property)); elProperty.appendChild(elInput); @@ -2102,7 +2131,10 @@ function createButtonsProperty(property, elProperty, elLabel) { return elProperty; } -function createTupleNumberInput(elTuple, propertyElementID, subLabel, min, max, step, decimals) { +function createTupleNumberInput(property, subLabel) { + let propertyElementID = property.elementID; + let propertyData = property.data; + let elementID = propertyElementID + "-" + subLabel.toLowerCase(); let elLabel = document.createElement('label'); @@ -2111,11 +2143,13 @@ function createTupleNumberInput(elTuple, propertyElementID, subLabel, min, max, elLabel.setAttribute("for", elementID); elLabel.style.visibility = "visible"; - let elDraggableNumber = new DraggableNumber(min, max, step, decimals); + let dragStartFunction = createDragStartFunction(property); + let dragEndFunction = createDragEndFunction(property); + let elDraggableNumber = new DraggableNumber(propertyData.min, propertyData.max, propertyData.step, + propertyData.decimals, dragStartFunction, dragEndFunction); elDraggableNumber.elInput.setAttribute("id", elementID); elDraggableNumber.elDiv.className += " fstuple"; elDraggableNumber.elText.insertBefore(elLabel, elDraggableNumber.elLeftArrow); - elTuple.appendChild(elDraggableNumber.elDiv); return elDraggableNumber; } @@ -2388,7 +2422,7 @@ function multiDataUpdater(groupName, updateKeyPair, userDataElement, defaults, r userDataElement.value = propertyUpdate.userData; - updateProperties(propertyUpdate); + updateProperties(propertyUpdate, false); } var editor = null; @@ -3306,7 +3340,7 @@ function loaded() { } }); - getPropertyInputElement("image").addEventListener('change', createImageURLUpdateFunction('textures', false)); + getPropertyInputElement("image").addEventListener('change', createImageURLUpdateFunction(properties['textures'])); // Collapsible sections let elCollapsible = document.getElementsByClassName("section-header"); @@ -3401,7 +3435,7 @@ function loaded() { let propertyID = elDropdown.getAttribute("propertyID"); let property = properties[propertyID]; property.elInput = dt; - dt.addEventListener('change', createEmitTextPropertyUpdateFunction(property.name, property.isParticleProperty)); + dt.addEventListener('change', createEmitTextPropertyUpdateFunction(property)); } elDropdowns = document.getElementsByTagName("select"); From 249ebb238923d174481ac11b3c2e139da84152c8 Mon Sep 17 00:00:00 2001 From: David Back Date: Mon, 19 Nov 2018 16:21:52 -0800 Subject: [PATCH 285/286] remove unneeded --- scripts/system/html/js/entityProperties.js | 5 ----- 1 file changed, 5 deletions(-) diff --git a/scripts/system/html/js/entityProperties.js b/scripts/system/html/js/entityProperties.js index 850c012be0..eca38f099a 100644 --- a/scripts/system/html/js/entityProperties.js +++ b/scripts/system/html/js/entityProperties.js @@ -1857,7 +1857,6 @@ function createNumberProperty(property, elProperty) { let dragStartFunction = createDragStartFunction(property); let dragEndFunction = createDragEndFunction(property); - let elDraggableNumber = new DraggableNumber(propertyData.min, propertyData.max, propertyData.step, propertyData.decimals, dragStartFunction, dragEndFunction); @@ -1913,9 +1912,6 @@ function createVec2Property(property, elProperty) { elProperty.appendChild(elTuple); - let dragStartFunction = createDragStartFunction(property); - let dragEndFunction = createDragEndFunction(property); - let elNumberX = createTupleNumberInput(property, propertyData.subLabels[VECTOR_ELEMENTS.X_NUMBER]); let elNumberY = createTupleNumberInput(property, propertyData.subLabels[VECTOR_ELEMENTS.Y_NUMBER]); elProperty.appendChild(elNumberX.elDiv); @@ -2134,7 +2130,6 @@ function createButtonsProperty(property, elProperty, elLabel) { function createTupleNumberInput(property, subLabel) { let propertyElementID = property.elementID; let propertyData = property.data; - let elementID = propertyElementID + "-" + subLabel.toLowerCase(); let elLabel = document.createElement('label'); From dff4e34e7bc96eb622c0d2b4733e0e85b7cb3878 Mon Sep 17 00:00:00 2001 From: David Back Date: Mon, 19 Nov 2018 16:59:33 -0800 Subject: [PATCH 286/286] CR changes --- scripts/system/edit.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/system/edit.js b/scripts/system/edit.js index 85fd8c536d..077d50ddde 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -2223,7 +2223,7 @@ var PropertiesTool = function (opts) { // are selected or if no entity is selected this will be `null`. var currentSelectedEntityID = null; var statusMonitor = null; - var nextPropertyUpdateDisabled = false; + var blockPropertyUpdates = false; that.setVisible = function (newVisible) { visible = newVisible; @@ -2261,8 +2261,7 @@ var PropertiesTool = function (opts) { }; function updateSelections(selectionUpdated) { - if (nextPropertyUpdateDisabled) { - nextPropertyUpdateDisabled = false; + if (blockPropertyUpdates) { return; } @@ -2362,8 +2361,9 @@ var PropertiesTool = function (opts) { } } pushCommandForSelections(); - nextPropertyUpdateDisabled = data.blockUpdateCallback === true; + blockPropertyUpdates = data.blockUpdateCallback === true; selectionManager._update(false, this); + blockPropertyUpdates = false; } else if (data.type === 'saveUserData' || data.type === 'saveMaterialData') { //the event bridge and json parsing handle our avatar id string differently. var actualID = data.id.split('"')[1];