From e406e3d47e129acf1c35c294b3ce4b20406a518b Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Tue, 3 Jan 2017 14:46:51 -0500 Subject: [PATCH 001/109] Make Forward DrawBounds enableable --- .../render-utils/src/RenderForwardTask.h | 26 ++++++++++++------- libraries/render/src/render/Task.h | 3 ++- 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/libraries/render-utils/src/RenderForwardTask.h b/libraries/render-utils/src/RenderForwardTask.h index 000210c761..9d93c7d5c3 100755 --- a/libraries/render-utils/src/RenderForwardTask.h +++ b/libraries/render-utils/src/RenderForwardTask.h @@ -33,11 +33,25 @@ private: gpu::FramebufferPointer _framebuffer; }; -class DrawBounds { +class DrawBackground { public: using Inputs = render::ItemBounds; - using JobModel = render::Job::ModelI; + using JobModel = render::Job::ModelI; + void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext, const Inputs& background); +}; + +class DrawBounds { +public: + class Config : public render::JobConfig { + public: + Config() : JobConfig(false) {} + }; + + using Inputs = render::ItemBounds; + using JobModel = render::Job::ModelI; + + void configure(const Config& configuration) {} void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext, const Inputs& items); private: @@ -47,12 +61,4 @@ private: int _scaleLocation { -1 }; }; -class DrawBackground { -public: - using Inputs = render::ItemBounds; - using JobModel = render::Job::ModelI; - - void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext, const Inputs& background); -}; - #endif // hifi_RenderForwardTask_h diff --git a/libraries/render/src/render/Task.h b/libraries/render/src/render/Task.h index 49273d79a5..4e3aa79c8d 100644 --- a/libraries/render/src/render/Task.h +++ b/libraries/render/src/render/Task.h @@ -371,6 +371,7 @@ protected: class JobConfig : public QObject { Q_OBJECT Q_PROPERTY(double cpuRunTime READ getCPURunTime NOTIFY newStats()) //ms + Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled) double _msCPURunTime{ 0.0 }; public: @@ -380,7 +381,7 @@ public: JobConfig(bool enabled) : alwaysEnabled{ false }, enabled{ enabled } {} bool isEnabled() { return alwaysEnabled || enabled; } - void setEnabled(bool enable) { enabled = enable; } + void setEnabled(bool enable) { enabled = alwaysEnabled || enable; } bool alwaysEnabled{ true }; bool enabled{ true }; From 01cf48807c7af9f37e834908ad0a2bc9392bb1f2 Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Tue, 3 Jan 2017 14:47:48 -0500 Subject: [PATCH 002/109] change Render.RenderForwardTask to Render.Forward --- interface/src/Application.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index b58bedb363..8372e7f609 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1824,7 +1824,7 @@ void Application::initializeGL() { assert(items.canCast()); static const QString RENDER_FORWARD = "HIFI_RENDER_FORWARD"; if (QProcessEnvironment::systemEnvironment().contains(RENDER_FORWARD)) { - _renderEngine->addJob("RenderForwardTask", items.get()); + _renderEngine->addJob("Forward", items.get()); } else { _renderEngine->addJob("RenderDeferredTask", items.get()); } From 2e274b026da979abcb5c6d64865b028260f5e4e2 Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Tue, 3 Jan 2017 14:54:44 -0500 Subject: [PATCH 003/109] fix line lengths --- libraries/render-utils/src/RenderForwardTask.cpp | 11 +++++++---- libraries/render-utils/src/RenderForwardTask.h | 12 ++++++++---- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/libraries/render-utils/src/RenderForwardTask.cpp b/libraries/render-utils/src/RenderForwardTask.cpp index e78d457666..be47abeeab 100755 --- a/libraries/render-utils/src/RenderForwardTask.cpp +++ b/libraries/render-utils/src/RenderForwardTask.cpp @@ -49,7 +49,8 @@ RenderForwardTask::RenderForwardTask(RenderFetchCullSortTask::Output items) { addJob("Blit", framebuffer); } -void PrepareFramebuffer::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, gpu::FramebufferPointer& framebuffer) { +void PrepareFramebuffer::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, + gpu::FramebufferPointer& framebuffer) { auto framebufferCache = DependencyManager::get(); auto framebufferSize = framebufferCache->getFrameBufferSize(); glm::uvec2 frameSize(framebufferSize.width(), framebufferSize.height()); @@ -112,7 +113,8 @@ const gpu::PipelinePointer DrawBounds::getPipeline() { return _boundsPipeline; } -void DrawBounds::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const Inputs& items) { +void DrawBounds::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, + const Inputs& items) { RenderArgs* args = renderContext->args; gpu::doInBatch(args->_context, [&](gpu::Batch& batch) { @@ -143,7 +145,8 @@ void DrawBounds::run(const SceneContextPointer& sceneContext, const RenderContex }); } -void DrawBackground::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const Inputs& items) { +void DrawBackground::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, + const Inputs& background) { RenderArgs* args = renderContext->args; gpu::doInBatch(args->_context, [&](gpu::Batch& batch) { @@ -161,7 +164,7 @@ void DrawBackground::run(const SceneContextPointer& sceneContext, const RenderCo batch.setProjectionTransform(projMat); batch.setViewTransform(viewMat); - renderItems(sceneContext, renderContext, items); + renderItems(sceneContext, renderContext, background); }); args->_batch = nullptr; } diff --git a/libraries/render-utils/src/RenderForwardTask.h b/libraries/render-utils/src/RenderForwardTask.h index 9d93c7d5c3..21fe43af64 100755 --- a/libraries/render-utils/src/RenderForwardTask.h +++ b/libraries/render-utils/src/RenderForwardTask.h @@ -25,9 +25,11 @@ public: class PrepareFramebuffer { public: - using JobModel = render::Job::ModelO; + using Inputs = gpu::FramebufferPointer; + using JobModel = render::Job::ModelO; - void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext, gpu::FramebufferPointer& framebuffer); + void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext, + gpu::FramebufferPointer& framebuffer); private: gpu::FramebufferPointer _framebuffer; @@ -38,7 +40,8 @@ public: using Inputs = render::ItemBounds; using JobModel = render::Job::ModelI; - void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext, const Inputs& background); + void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext, + const Inputs& background); }; class DrawBounds { @@ -52,7 +55,8 @@ public: using JobModel = render::Job::ModelI; void configure(const Config& configuration) {} - void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext, const Inputs& items); + void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext, + const Inputs& items); private: const gpu::PipelinePointer getPipeline(); From f8e8065e93a39170824eae2f30d85952f1e9a892 Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Tue, 3 Jan 2017 14:56:47 -0500 Subject: [PATCH 004/109] mv DrawBounds to end of ForwardTask --- .../render-utils/src/RenderForwardTask.cpp | 48 +++++++++---------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/libraries/render-utils/src/RenderForwardTask.cpp b/libraries/render-utils/src/RenderForwardTask.cpp index be47abeeab..51dc2dcd47 100755 --- a/libraries/render-utils/src/RenderForwardTask.cpp +++ b/libraries/render-utils/src/RenderForwardTask.cpp @@ -90,6 +90,30 @@ void PrepareFramebuffer::run(const SceneContextPointer& sceneContext, const Rend framebuffer = _framebuffer; } +void DrawBackground::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, + const Inputs& background) { + RenderArgs* args = renderContext->args; + + gpu::doInBatch(args->_context, [&](gpu::Batch& batch) { + args->_batch = &batch; + + batch.enableSkybox(true); + batch.setViewportTransform(args->_viewport); + batch.setStateScissorRect(args->_viewport); + + // Setup projection + glm::mat4 projMat; + Transform viewMat; + args->getViewFrustum().evalProjectionMatrix(projMat); + args->getViewFrustum().evalViewTransform(viewMat); + batch.setProjectionTransform(projMat); + batch.setViewTransform(viewMat); + + renderItems(sceneContext, renderContext, background); + }); + args->_batch = nullptr; +} + const gpu::PipelinePointer DrawBounds::getPipeline() { if (!_boundsPipeline) { auto vs = gpu::Shader::createVertex(std::string(drawItemBounds_vert)); @@ -144,27 +168,3 @@ void DrawBounds::run(const SceneContextPointer& sceneContext, const RenderContex } }); } - -void DrawBackground::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, - const Inputs& background) { - RenderArgs* args = renderContext->args; - - gpu::doInBatch(args->_context, [&](gpu::Batch& batch) { - args->_batch = &batch; - - batch.enableSkybox(true); - batch.setViewportTransform(args->_viewport); - batch.setStateScissorRect(args->_viewport); - - // Setup projection - glm::mat4 projMat; - Transform viewMat; - args->getViewFrustum().evalProjectionMatrix(projMat); - args->getViewFrustum().evalViewTransform(viewMat); - batch.setProjectionTransform(projMat); - batch.setViewTransform(viewMat); - - renderItems(sceneContext, renderContext, background); - }); - args->_batch = nullptr; -} From 89917b41fd08d9936c133a8e71cdfd596249f0d0 Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Tue, 3 Jan 2017 15:18:22 -0500 Subject: [PATCH 005/109] add Forward Draw for opaques (no pipelines) --- .../render-utils/src/RenderForwardTask.cpp | 26 +++++++++++++++++++ .../render-utils/src/RenderForwardTask.h | 13 ++++++++++ 2 files changed, 39 insertions(+) diff --git a/libraries/render-utils/src/RenderForwardTask.cpp b/libraries/render-utils/src/RenderForwardTask.cpp index 51dc2dcd47..ebc9ac5b94 100755 --- a/libraries/render-utils/src/RenderForwardTask.cpp +++ b/libraries/render-utils/src/RenderForwardTask.cpp @@ -30,6 +30,9 @@ using namespace render; RenderForwardTask::RenderForwardTask(RenderFetchCullSortTask::Output items) { + // Prepare the ShapePipelines + ShapePlumberPointer shapePlumber = std::make_shared(); + // Extract opaques / transparents / lights / overlays const auto opaques = items[0]; const auto transparents = items[1]; @@ -40,6 +43,7 @@ RenderForwardTask::RenderForwardTask(RenderFetchCullSortTask::Output items) { const auto framebuffer = addJob("PrepareFramebuffer"); + addJob("DrawOpaques", opaques, shapePlumber); addJob("DrawBackground", background); // bounds do not draw on stencil buffer, so they must come last @@ -90,6 +94,28 @@ void PrepareFramebuffer::run(const SceneContextPointer& sceneContext, const Rend framebuffer = _framebuffer; } +void Draw::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, + const Inputs& items) { + RenderArgs* args = renderContext->args; + + gpu::doInBatch(args->_context, [&](gpu::Batch& batch) { + args->_batch = &batch; + + // Setup projection + glm::mat4 projMat; + Transform viewMat; + args->getViewFrustum().evalProjectionMatrix(projMat); + args->getViewFrustum().evalViewTransform(viewMat); + batch.setProjectionTransform(projMat); + batch.setViewTransform(viewMat); + batch.setModelTransform(Transform()); + + // Render items + renderStateSortShapes(sceneContext, renderContext, _shapePlumber, items, -1); + }); + args->_batch = nullptr; +} + void DrawBackground::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const Inputs& background) { RenderArgs* args = renderContext->args; diff --git a/libraries/render-utils/src/RenderForwardTask.h b/libraries/render-utils/src/RenderForwardTask.h index 21fe43af64..5f9386ceba 100755 --- a/libraries/render-utils/src/RenderForwardTask.h +++ b/libraries/render-utils/src/RenderForwardTask.h @@ -35,6 +35,19 @@ private: gpu::FramebufferPointer _framebuffer; }; +class Draw { +public: + using Inputs = render::ItemBounds; + using JobModel = render::Job::ModelI; + + Draw(const render::ShapePlumberPointer& shapePlumber) : _shapePlumber(shapePlumber) {} + void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext, + const Inputs& items); + +private: + render::ShapePlumberPointer _shapePlumber; +}; + class DrawBackground { public: using Inputs = render::ItemBounds; From 62eeb0c8072e1f64580a81523bca251f66c43a9b Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Tue, 3 Jan 2017 18:33:14 -0500 Subject: [PATCH 006/109] clean RenderPipelines with forward decls --- .../render-utils/src/RenderPipelines.cpp | 76 +++++++++++-------- 1 file changed, 43 insertions(+), 33 deletions(-) diff --git a/libraries/render-utils/src/RenderPipelines.cpp b/libraries/render-utils/src/RenderPipelines.cpp index 1f4b7ca445..506a6b87f0 100644 --- a/libraries/render-utils/src/RenderPipelines.cpp +++ b/libraries/render-utils/src/RenderPipelines.cpp @@ -48,40 +48,11 @@ using namespace render; -gpu::BufferView getDefaultMaterialBuffer() { - model::Material::Schema schema; - schema._albedo = vec3(1.0f); - schema._opacity = 1.0f; - schema._metallic = 0.1f; - schema._roughness = 0.9f; - return gpu::BufferView(std::make_shared(sizeof(model::Material::Schema), (const gpu::Byte*) &schema)); -} +void initOverlay3DPipelines(ShapePlumber& plumber); +void initDeferredPipelines(render::ShapePlumber& plumber); -void batchSetter(const ShapePipeline& pipeline, gpu::Batch& batch) { - // Set a default albedo map - batch.setResourceTexture(render::ShapePipeline::Slot::MAP::ALBEDO, - DependencyManager::get()->getWhiteTexture()); - // Set a default normal map - batch.setResourceTexture(render::ShapePipeline::Slot::MAP::NORMAL_FITTING, - DependencyManager::get()->getNormalFittingTexture()); - - // Set a default material - if (pipeline.locations->materialBufferUnit >= 0) { - static const gpu::BufferView OPAQUE_SCHEMA_BUFFER = getDefaultMaterialBuffer(); - batch.setUniformBuffer(ShapePipeline::Slot::BUFFER::MATERIAL, OPAQUE_SCHEMA_BUFFER); - } -} - -void lightBatchSetter(const ShapePipeline& pipeline, gpu::Batch& batch) { - batchSetter(pipeline, batch); - // Set the light - if (pipeline.locations->lightBufferUnit >= 0) { - DependencyManager::get()->setupKeyLightBatch(batch, - pipeline.locations->lightBufferUnit, - pipeline.locations->lightAmbientBufferUnit, - pipeline.locations->lightAmbientMapUnit); - } -} +void batchSetter(const ShapePipeline& pipeline, gpu::Batch& batch); +void lightBatchSetter(const ShapePipeline& pipeline, gpu::Batch& batch); void initOverlay3DPipelines(ShapePlumber& plumber) { auto vertex = gpu::Shader::createVertex(std::string(overlay3D_vert)); @@ -283,3 +254,42 @@ void initDeferredPipelines(render::ShapePlumber& plumber) { skinModelShadowVertex, modelShadowPixel); } + +void batchSetter(const ShapePipeline& pipeline, gpu::Batch& batch) { + // Set a default albedo map + batch.setResourceTexture(render::ShapePipeline::Slot::MAP::ALBEDO, + DependencyManager::get()->getWhiteTexture()); + // Set a default normal map + batch.setResourceTexture(render::ShapePipeline::Slot::MAP::NORMAL_FITTING, + DependencyManager::get()->getNormalFittingTexture()); + + // Set a default material + if (pipeline.locations->materialBufferUnit >= 0) { + // Create a default schema + static bool isMaterialSet = false; + static model::Material material; + if (!isMaterialSet) { + material.setAlbedo(vec3(1.0f)); + material.setOpacity(1.0f); + material.setMetallic(0.1f); + material.setRoughness(0.9f); + isMaterialSet = true; + } + + // Set a default schema + batch.setUniformBuffer(ShapePipeline::Slot::BUFFER::MATERIAL, material.getSchemaBuffer()); + } +} + +void lightBatchSetter(const ShapePipeline& pipeline, gpu::Batch& batch) { + // Set the batch + batchSetter(pipeline, batch); + + // Set the light + if (pipeline.locations->lightBufferUnit >= 0) { + DependencyManager::get()->setupKeyLightBatch(batch, + pipeline.locations->lightBufferUnit, + pipeline.locations->lightAmbientBufferUnit, + pipeline.locations->lightAmbientMapUnit); + } +} From a414ba4cb50286261e0afb6c0b4d4e99985685e8 Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Tue, 3 Jan 2017 18:54:34 -0500 Subject: [PATCH 007/109] take addPipeline lambda out of initDeferredPipelines --- .../render-utils/src/RenderPipelines.cpp | 95 ++++++++++--------- 1 file changed, 50 insertions(+), 45 deletions(-) diff --git a/libraries/render-utils/src/RenderPipelines.cpp b/libraries/render-utils/src/RenderPipelines.cpp index 506a6b87f0..d28e1aa0a2 100644 --- a/libraries/render-utils/src/RenderPipelines.cpp +++ b/libraries/render-utils/src/RenderPipelines.cpp @@ -10,6 +10,8 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include + #include #include @@ -47,9 +49,13 @@ using namespace render; +using namespace std::placeholders; void initOverlay3DPipelines(ShapePlumber& plumber); -void initDeferredPipelines(render::ShapePlumber& plumber); +void initDeferredPipelines(ShapePlumber& plumber); + +void addPlumberPipeline(ShapePlumber& plumber, + const ShapeKey& key, const gpu::ShaderPointer& vertex, const gpu::ShaderPointer& pixel); void batchSetter(const ShapePipeline& pipeline, gpu::Batch& batch); void lightBatchSetter(const ShapePipeline& pipeline, gpu::Batch& batch); @@ -101,50 +107,6 @@ void initOverlay3DPipelines(ShapePlumber& plumber) { } void initDeferredPipelines(render::ShapePlumber& plumber) { - using Key = render::ShapeKey; - using ShaderPointer = gpu::ShaderPointer; - - auto addPipeline = [&plumber](const Key& key, const ShaderPointer& vertexShader, const ShaderPointer& pixelShader) { - // These keyvalues' pipelines will be added by this lamdba in addition to the key passed - assert(!key.isWireFrame()); - assert(!key.isDepthBiased()); - assert(key.isCullFace()); - - ShaderPointer program = gpu::Shader::createProgram(vertexShader, pixelShader); - - for (int i = 0; i < 8; i++) { - bool isCulled = (i & 1); - bool isBiased = (i & 2); - bool isWireframed = (i & 4); - - ShapeKey::Builder builder(key); - auto state = std::make_shared(); - - // Depth test depends on transparency - state->setDepthTest(true, !key.isTranslucent(), gpu::LESS_EQUAL); - state->setBlendFunction(key.isTranslucent(), - gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA, - gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE); - - if (!isCulled) { - builder.withoutCullFace(); - } - state->setCullMode(isCulled ? gpu::State::CULL_BACK : gpu::State::CULL_NONE); - if (isWireframed) { - builder.withWireframe(); - state->setFillMode(gpu::State::FILL_LINE); - } - if (isBiased) { - builder.withDepthBias(); - state->setDepthBias(1.0f); - state->setDepthBiasSlopeScale(1.0f); - } - - plumber.addPipeline(builder.build(), program, state, - key.isTranslucent() ? &lightBatchSetter : &batchSetter); - } - }; - // Vertex shaders auto modelVertex = gpu::Shader::createVertex(std::string(model_vert)); auto modelNormalMapVertex = gpu::Shader::createVertex(std::string(model_normal_map_vert)); @@ -169,6 +131,8 @@ void initDeferredPipelines(render::ShapePlumber& plumber) { auto modelLightmapSpecularMapPixel = gpu::Shader::createPixel(std::string(model_lightmap_specular_map_frag)); auto modelLightmapNormalSpecularMapPixel = gpu::Shader::createPixel(std::string(model_lightmap_normal_specular_map_frag)); + using Key = render::ShapeKey; + auto addPipeline = std::bind(&addPlumberPipeline, std::ref(plumber), _1, _2, _3); // TODO: Refactor this to use a filter // Opaques addPipeline( @@ -252,7 +216,48 @@ void initDeferredPipelines(render::ShapePlumber& plumber) { addPipeline( Key::Builder().withSkinned().withDepthOnly(), skinModelShadowVertex, modelShadowPixel); +} +void addPlumberPipeline(ShapePlumber& plumber, + const ShapeKey& key, const gpu::ShaderPointer& vertex, const gpu::ShaderPointer& pixel) { + // These key-values' pipelines are added by this functor in addition to the key passed + assert(!key.isWireFrame()); + assert(!key.isDepthBiased()); + assert(key.isCullFace()); + + gpu::ShaderPointer program = gpu::Shader::createProgram(vertex, pixel); + + for (int i = 0; i < 8; i++) { + bool isCulled = (i & 1); + bool isBiased = (i & 2); + bool isWireframed = (i & 4); + + auto state = std::make_shared(); + + // Depth test depends on transparency + state->setDepthTest(true, !key.isTranslucent(), gpu::LESS_EQUAL); + state->setBlendFunction(key.isTranslucent(), + gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA, + gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE); + + ShapeKey::Builder builder(key); + if (!isCulled) { + builder.withoutCullFace(); + } + state->setCullMode(isCulled ? gpu::State::CULL_BACK : gpu::State::CULL_NONE); + if (isWireframed) { + builder.withWireframe(); + state->setFillMode(gpu::State::FILL_LINE); + } + if (isBiased) { + builder.withDepthBias(); + state->setDepthBias(1.0f); + state->setDepthBiasSlopeScale(1.0f); + } + + plumber.addPipeline(builder.build(), program, state, + key.isTranslucent() ? &lightBatchSetter : &batchSetter); + } } void batchSetter(const ShapePipeline& pipeline, gpu::Batch& batch) { From f7cb2ec85baaf41cffa3ff4e050680e5059d443c Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Tue, 3 Jan 2017 20:41:57 -0500 Subject: [PATCH 008/109] add plain forward model shaders --- .../render-utils/src/RenderForwardTask.cpp | 42 ++++++++++++++++++- .../render-utils/src/RenderForwardTask.h | 11 +++++ .../render-utils/src/RenderPipelines.cpp | 16 +++++++ libraries/render-utils/src/nop.slf | 16 +++++++ 4 files changed, 84 insertions(+), 1 deletion(-) create mode 100644 libraries/render-utils/src/nop.slf diff --git a/libraries/render-utils/src/RenderForwardTask.cpp b/libraries/render-utils/src/RenderForwardTask.cpp index ebc9ac5b94..c7a3433c96 100755 --- a/libraries/render-utils/src/RenderForwardTask.cpp +++ b/libraries/render-utils/src/RenderForwardTask.cpp @@ -26,12 +26,15 @@ #include #include +#include "nop_frag.h" using namespace render; +extern void initForwardPipelines(ShapePlumber& plumber); RenderForwardTask::RenderForwardTask(RenderFetchCullSortTask::Output items) { // Prepare the ShapePipelines ShapePlumberPointer shapePlumber = std::make_shared(); + initForwardPipelines(*shapePlumber); // Extract opaques / transparents / lights / overlays const auto opaques = items[0]; @@ -44,9 +47,10 @@ RenderForwardTask::RenderForwardTask(RenderFetchCullSortTask::Output items) { const auto framebuffer = addJob("PrepareFramebuffer"); addJob("DrawOpaques", opaques, shapePlumber); + addJob("Stencil"); addJob("DrawBackground", background); - // bounds do not draw on stencil buffer, so they must come last + // Bounds do not draw on stencil buffer, so they must come last addJob("DrawBounds", opaques); // Blit! @@ -116,6 +120,42 @@ void Draw::run(const SceneContextPointer& sceneContext, const RenderContextPoint args->_batch = nullptr; } +const gpu::PipelinePointer Stencil::getPipeline() { + if (!_stencilPipeline) { + auto vs = gpu::StandardShaderLib::getDrawUnitQuadTexcoordVS(); + auto ps = gpu::Shader::createPixel(std::string(nop_frag)); + gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps); + gpu::Shader::makeProgram(*program); + + auto state = std::make_shared(); + state->setDepthTest(true, false, gpu::LESS_EQUAL); + const gpu::int8 STENCIL_OPAQUE = 1; + state->setStencilTest(true, 0xFF, gpu::State::StencilTest(STENCIL_OPAQUE, 0xFF, gpu::ALWAYS, + gpu::State::STENCIL_OP_REPLACE, + gpu::State::STENCIL_OP_REPLACE, + gpu::State::STENCIL_OP_KEEP)); + + _stencilPipeline = gpu::Pipeline::create(program, state); + } + return _stencilPipeline; +} + +void Stencil::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) { + RenderArgs* args = renderContext->args; + + gpu::doInBatch(args->_context, [&](gpu::Batch& batch) { + args->_batch = &batch; + + batch.enableStereo(false); + batch.setViewportTransform(args->_viewport); + batch.setStateScissorRect(args->_viewport); + + batch.setPipeline(getPipeline()); + batch.draw(gpu::TRIANGLE_STRIP, 4); + }); + args->_batch = nullptr; +} + void DrawBackground::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const Inputs& background) { RenderArgs* args = renderContext->args; diff --git a/libraries/render-utils/src/RenderForwardTask.h b/libraries/render-utils/src/RenderForwardTask.h index 5f9386ceba..a957f7493e 100755 --- a/libraries/render-utils/src/RenderForwardTask.h +++ b/libraries/render-utils/src/RenderForwardTask.h @@ -48,6 +48,17 @@ private: render::ShapePlumberPointer _shapePlumber; }; +class Stencil { +public: + using JobModel = render::Job::Model; + + void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext); + +private: + const gpu::PipelinePointer getPipeline(); + gpu::PipelinePointer _stencilPipeline; +}; + class DrawBackground { public: using Inputs = render::ItemBounds; diff --git a/libraries/render-utils/src/RenderPipelines.cpp b/libraries/render-utils/src/RenderPipelines.cpp index d28e1aa0a2..2f04d778e2 100644 --- a/libraries/render-utils/src/RenderPipelines.cpp +++ b/libraries/render-utils/src/RenderPipelines.cpp @@ -53,6 +53,7 @@ using namespace std::placeholders; void initOverlay3DPipelines(ShapePlumber& plumber); void initDeferredPipelines(ShapePlumber& plumber); +void initForwardPipelines(ShapePlumber& plumber); void addPlumberPipeline(ShapePlumber& plumber, const ShapeKey& key, const gpu::ShaderPointer& vertex, const gpu::ShaderPointer& pixel); @@ -218,6 +219,21 @@ void initDeferredPipelines(render::ShapePlumber& plumber) { skinModelShadowVertex, modelShadowPixel); } +void initForwardPipelines(render::ShapePlumber& plumber) { + // Vertex shaders + auto modelVertex = gpu::Shader::createVertex(std::string(model_vert)); + + // Pixel shaders + auto modelPixel = gpu::Shader::createPixel(std::string(model_frag)); + + using Key = render::ShapeKey; + auto addPipeline = std::bind(&addPlumberPipeline, std::ref(plumber), _1, _2, _3); + // Opaques + addPipeline( + Key::Builder(), + modelVertex, modelPixel); + } + void addPlumberPipeline(ShapePlumber& plumber, const ShapeKey& key, const gpu::ShaderPointer& vertex, const gpu::ShaderPointer& pixel) { // These key-values' pipelines are added by this functor in addition to the key passed diff --git a/libraries/render-utils/src/nop.slf b/libraries/render-utils/src/nop.slf new file mode 100644 index 0000000000..f87db4e138 --- /dev/null +++ b/libraries/render-utils/src/nop.slf @@ -0,0 +1,16 @@ +<@include gpu/Config.slh@> +<$VERSION_HEADER$> +// Generated on <$_SCRIBE_DATE$> +// +// nop.frag +// fragment shader +// +// Created by Zach Pomerantz on 1/3/2017. +// Copyright 2017 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +void main(void) { +} From 6b984d38d3415a8f86dcd8261c72381fa0a1b701 Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Wed, 4 Jan 2017 15:37:17 -0500 Subject: [PATCH 009/109] add opaque forward model shaders --- .../render-utils/src/RenderPipelines.cpp | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/libraries/render-utils/src/RenderPipelines.cpp b/libraries/render-utils/src/RenderPipelines.cpp index 2f04d778e2..d65db9b818 100644 --- a/libraries/render-utils/src/RenderPipelines.cpp +++ b/libraries/render-utils/src/RenderPipelines.cpp @@ -222,9 +222,14 @@ void initDeferredPipelines(render::ShapePlumber& plumber) { void initForwardPipelines(render::ShapePlumber& plumber) { // Vertex shaders auto modelVertex = gpu::Shader::createVertex(std::string(model_vert)); + auto modelNormalMapVertex = gpu::Shader::createVertex(std::string(model_normal_map_vert)); // Pixel shaders auto modelPixel = gpu::Shader::createPixel(std::string(model_frag)); + auto modelUnlitPixel = gpu::Shader::createPixel(std::string(model_unlit_frag)); + auto modelNormalMapPixel = gpu::Shader::createPixel(std::string(model_normal_map_frag)); + auto modelSpecularMapPixel = gpu::Shader::createPixel(std::string(model_specular_map_frag)); + auto modelNormalSpecularMapPixel = gpu::Shader::createPixel(std::string(model_normal_specular_map_frag)); using Key = render::ShapeKey; auto addPipeline = std::bind(&addPlumberPipeline, std::ref(plumber), _1, _2, _3); @@ -232,7 +237,20 @@ void initForwardPipelines(render::ShapePlumber& plumber) { addPipeline( Key::Builder(), modelVertex, modelPixel); - } + addPipeline( + Key::Builder().withUnlit(), + modelVertex, modelUnlitPixel); + addPipeline( + Key::Builder().withTangents(), + modelNormalMapVertex, modelNormalMapPixel); + addPipeline( + Key::Builder().withSpecular(), + modelVertex, modelSpecularMapPixel); + addPipeline( + Key::Builder().withTangents().withSpecular(), + modelNormalMapVertex, modelNormalSpecularMapPixel); + +} void addPlumberPipeline(ShapePlumber& plumber, const ShapeKey& key, const gpu::ShaderPointer& vertex, const gpu::ShaderPointer& pixel) { From 1b948c7a05393016f19e58892b846a102e559182 Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Wed, 4 Jan 2017 15:49:29 -0500 Subject: [PATCH 010/109] add skinned forward model shaders --- libraries/render-utils/src/RenderPipelines.cpp | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/libraries/render-utils/src/RenderPipelines.cpp b/libraries/render-utils/src/RenderPipelines.cpp index d65db9b818..b0d676ef78 100644 --- a/libraries/render-utils/src/RenderPipelines.cpp +++ b/libraries/render-utils/src/RenderPipelines.cpp @@ -223,6 +223,8 @@ void initForwardPipelines(render::ShapePlumber& plumber) { // Vertex shaders auto modelVertex = gpu::Shader::createVertex(std::string(model_vert)); auto modelNormalMapVertex = gpu::Shader::createVertex(std::string(model_normal_map_vert)); + auto skinModelVertex = gpu::Shader::createVertex(std::string(skin_model_vert)); + auto skinModelNormalMapVertex = gpu::Shader::createVertex(std::string(skin_model_normal_map_vert)); // Pixel shaders auto modelPixel = gpu::Shader::createPixel(std::string(model_frag)); @@ -249,7 +251,19 @@ void initForwardPipelines(render::ShapePlumber& plumber) { addPipeline( Key::Builder().withTangents().withSpecular(), modelNormalMapVertex, modelNormalSpecularMapPixel); - + // Skinned + addPipeline( + Key::Builder().withSkinned(), + skinModelVertex, modelPixel); + addPipeline( + Key::Builder().withSkinned().withTangents(), + skinModelNormalMapVertex, modelNormalMapPixel); + addPipeline( + Key::Builder().withSkinned().withSpecular(), + skinModelVertex, modelSpecularMapPixel); + addPipeline( + Key::Builder().withSkinned().withTangents().withSpecular(), + skinModelNormalMapVertex, modelNormalSpecularMapPixel); } void addPlumberPipeline(ShapePlumber& plumber, From 4278d7b20dd903422734b316049d1a0dee7c7b23 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 16 Dec 2016 11:05:27 -0800 Subject: [PATCH 011/109] download and link oculus platform for oculus plugin --- cmake/externals/LibOVRPlatform/CMakeLists.txt | 32 +++++++++++++++ cmake/modules/FindLibOVRPlatform.cmake | 41 +++++++++++++++++++ plugins/oculus/CMakeLists.txt | 36 +++++++++------- 3 files changed, 94 insertions(+), 15 deletions(-) create mode 100644 cmake/externals/LibOVRPlatform/CMakeLists.txt create mode 100644 cmake/modules/FindLibOVRPlatform.cmake diff --git a/cmake/externals/LibOVRPlatform/CMakeLists.txt b/cmake/externals/LibOVRPlatform/CMakeLists.txt new file mode 100644 index 0000000000..75338ce041 --- /dev/null +++ b/cmake/externals/LibOVRPlatform/CMakeLists.txt @@ -0,0 +1,32 @@ +include(ExternalProject) +include(SelectLibraryConfigurations) + +set(EXTERNAL_NAME LibOVRPlatform) + +string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER) + +if (WIN32) + + ExternalProject_Add( + ${EXTERNAL_NAME} + URL http://hifi-public.s3.amazonaws.com/dependencies/OVRPlatformSDK_v1.10.0.zip + URL_MD5 e6c8264af16d904e6506acd5172fa0a9 + CONFIGURE_COMMAND "" + BUILD_COMMAND "" + INSTALL_COMMAND "" + LOG_DOWNLOAD 1 + ) + + ExternalProject_Get_Property(${EXTERNAL_NAME} SOURCE_DIR) + + if ("${CMAKE_SIZEOF_VOID_P}" EQUAL "8") + set(${EXTERNAL_NAME_UPPER}_LIB ${SOURCE_DIR}/Windows/LibOVRPlatform64_1.lib CACHE TYPE INTERNAL) + else() + set(${EXTERNAL_NAME_UPPER}_LIB ${SOURCE_DIR}/Windows/LibOVRPlatform32_1.lib CACHE TYPE INTERNAL) + endif() + + set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${SOURCE_DIR}/Include CACHE TYPE INTERNAL) +endif () + +# Hide this external target (for ide users) +set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals") diff --git a/cmake/modules/FindLibOVRPlatform.cmake b/cmake/modules/FindLibOVRPlatform.cmake new file mode 100644 index 0000000000..e09fa21895 --- /dev/null +++ b/cmake/modules/FindLibOVRPlatform.cmake @@ -0,0 +1,41 @@ +# +# FindLibOVRPlatform.cmake +# +# Try to find the LibOVRPlatform library to use the Oculus Platform SDK +# +# You must provide a LIBOVRPLATFORM_ROOT_DIR which contains Windows and Include directories +# +# Once done this will define +# +# LIBOVRPLATFORM_FOUND - system found Oculus Platform SDK +# LIBOVRPLATFORM_INCLUDE_DIRS - the Oculus Platform include directory +# LIBOVRPLATFORM_LIBRARIES - Link this to use Oculus Platform +# +# Created on December 16, 2016 by Stephen Birarda +# 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 +# + + +if (WIN32) + # setup hints for LIBOVRPLATFORM search + include("${MACRO_DIR}/HifiLibrarySearchHints.cmake") + hifi_library_search_hints("LibOVRPlatform") + + find_path(LIBOVRPLATFORM_INCLUDE_DIRS OVR_Platform.h PATH_SUFFIXES Include HINTS ${LIBOVRPLATFORM_SEARCH_DIRS}) + + if ("${CMAKE_SIZEOF_VOID_P}" EQUAL "8") + set(_LIB_NAME LibOVRPlatform64_1.lib) + else() + set(_LIB_NAME LibOVRPlatform32_1.lib) + endif() + + find_library(LIBOVRPLATFORM_LIBRARY_RELEASE NAMES ${_LIB_NAME} PATH_SUFFIXES Windows HINTS ${LIBOVRPLATFORM_SEARCH_DIRS}) + + include(FindPackageHandleStandardArgs) + find_package_handle_standard_args(LIBOVRPLATFORM DEFAULT_MSG LIBOVRPLATFORM_INCLUDE_DIRS LIBOVRPLATFORM_LIBRARIES) + + mark_as_advanced(LIBOVRPLATFORM_INCLUDE_DIRS LIBOVRPLATFORM_LIBRARIES LIBOVRPLATFORM_SEARCH_DIRS) +endif () diff --git a/plugins/oculus/CMakeLists.txt b/plugins/oculus/CMakeLists.txt index a768af932e..7ee0228616 100644 --- a/plugins/oculus/CMakeLists.txt +++ b/plugins/oculus/CMakeLists.txt @@ -8,21 +8,27 @@ if (WIN32) - # we're using static GLEW, so define GLEW_STATIC - add_definitions(-DGLEW_STATIC) + # we're using static GLEW, so define GLEW_STATIC + add_definitions(-DGLEW_STATIC) - set(TARGET_NAME oculus) - setup_hifi_plugin(Multimedia) - link_hifi_libraries(shared gl gpu gpu-gl controllers ui - plugins ui-plugins display-plugins input-plugins - audio-client networking render-utils) - - include_hifi_library_headers(octree) - - add_dependency_external_projects(LibOVR) - find_package(LibOVR REQUIRED) - target_include_directories(${TARGET_NAME} PRIVATE ${LIBOVR_INCLUDE_DIRS}) - target_link_libraries(${TARGET_NAME} ${LIBOVR_LIBRARIES}) - target_link_libraries(${TARGET_NAME} Winmm.lib) + set(TARGET_NAME oculus) + setup_hifi_plugin(Multimedia) + link_hifi_libraries( + shared gl gpu gpu-gl controllers ui + plugins ui-plugins display-plugins input-plugins + audio-client networking render-utils + ) + include_hifi_library_headers(octree) + + add_dependency_external_projects(LibOVR) + find_package(LibOVR REQUIRED) + target_include_directories(${TARGET_NAME} PRIVATE ${LIBOVR_INCLUDE_DIRS}) + target_link_libraries(${TARGET_NAME} ${LIBOVR_LIBRARIES}) + target_link_libraries(${TARGET_NAME} Winmm.lib) + + add_dependency_external_projects(LibOVRPlatform) + find_package(LibOVRPlatform REQUIRED) + target_include_directories(${TARGET_NAME} PRIVATE ${LIBOVRPLATFORM_INCLUDE_DIRS}) + target_link_libraries(${TARGET_NAME} ${LIBOVRPLATFORM_LIBRARIES}) endif() From ac7637f8ff897e9d9133a327b9a16e485b8dbd88 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 16 Dec 2016 13:10:49 -0800 Subject: [PATCH 012/109] use _LIBRARY_RELEASE for LibOVRPlatform external --- cmake/externals/LibOVRPlatform/CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmake/externals/LibOVRPlatform/CMakeLists.txt b/cmake/externals/LibOVRPlatform/CMakeLists.txt index 75338ce041..3622972a13 100644 --- a/cmake/externals/LibOVRPlatform/CMakeLists.txt +++ b/cmake/externals/LibOVRPlatform/CMakeLists.txt @@ -20,9 +20,9 @@ if (WIN32) ExternalProject_Get_Property(${EXTERNAL_NAME} SOURCE_DIR) if ("${CMAKE_SIZEOF_VOID_P}" EQUAL "8") - set(${EXTERNAL_NAME_UPPER}_LIB ${SOURCE_DIR}/Windows/LibOVRPlatform64_1.lib CACHE TYPE INTERNAL) + set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE ${SOURCE_DIR}/Windows/LibOVRPlatform64_1.lib CACHE TYPE INTERNAL) else() - set(${EXTERNAL_NAME_UPPER}_LIB ${SOURCE_DIR}/Windows/LibOVRPlatform32_1.lib CACHE TYPE INTERNAL) + set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE ${SOURCE_DIR}/Windows/LibOVRPlatform32_1.lib CACHE TYPE INTERNAL) endif() set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${SOURCE_DIR}/Include CACHE TYPE INTERNAL) From 1a50a1174125b0ed2257d1c0563e510435e47a2c Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 16 Dec 2016 13:15:14 -0800 Subject: [PATCH 013/109] have FindLibOVRPlatform produce LIBOVRPLATFORM_LIBRARY --- cmake/modules/FindLibOVRPlatform.cmake | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cmake/modules/FindLibOVRPlatform.cmake b/cmake/modules/FindLibOVRPlatform.cmake index e09fa21895..fd07ff6f06 100644 --- a/cmake/modules/FindLibOVRPlatform.cmake +++ b/cmake/modules/FindLibOVRPlatform.cmake @@ -9,7 +9,7 @@ # # LIBOVRPLATFORM_FOUND - system found Oculus Platform SDK # LIBOVRPLATFORM_INCLUDE_DIRS - the Oculus Platform include directory -# LIBOVRPLATFORM_LIBRARIES - Link this to use Oculus Platform +# LIBOVRPLATFORM_LIBRARY - Link this to use Oculus Platform # # Created on December 16, 2016 by Stephen Birarda # Copyright 2016 High Fidelity, Inc. @@ -35,7 +35,7 @@ if (WIN32) find_library(LIBOVRPLATFORM_LIBRARY_RELEASE NAMES ${_LIB_NAME} PATH_SUFFIXES Windows HINTS ${LIBOVRPLATFORM_SEARCH_DIRS}) include(FindPackageHandleStandardArgs) - find_package_handle_standard_args(LIBOVRPLATFORM DEFAULT_MSG LIBOVRPLATFORM_INCLUDE_DIRS LIBOVRPLATFORM_LIBRARIES) + find_package_handle_standard_args(LIBOVRPLATFORM DEFAULT_MSG LIBOVRPLATFORM_INCLUDE_DIRS LIBOVRPLATFORM_LIBRARY) - mark_as_advanced(LIBOVRPLATFORM_INCLUDE_DIRS LIBOVRPLATFORM_LIBRARIES LIBOVRPLATFORM_SEARCH_DIRS) + mark_as_advanced(LIBOVRPLATFORM_INCLUDE_DIRS LIBOVRPLATFORM_LIBRARY LIBOVRPLATFORM_SEARCH_DIRS) endif () From d0236af33468d251317d54443ec0c4c76db3b380 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 16 Dec 2016 13:25:35 -0800 Subject: [PATCH 014/109] use select_library_configurations for LibOVRPlatform --- cmake/modules/FindLibOVRPlatform.cmake | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/cmake/modules/FindLibOVRPlatform.cmake b/cmake/modules/FindLibOVRPlatform.cmake index fd07ff6f06..e9caa2cb98 100644 --- a/cmake/modules/FindLibOVRPlatform.cmake +++ b/cmake/modules/FindLibOVRPlatform.cmake @@ -9,7 +9,7 @@ # # LIBOVRPLATFORM_FOUND - system found Oculus Platform SDK # LIBOVRPLATFORM_INCLUDE_DIRS - the Oculus Platform include directory -# LIBOVRPLATFORM_LIBRARY - Link this to use Oculus Platform +# LIBOVRPLATFORM_LIBRARIES - Link this to use Oculus Platform # # Created on December 16, 2016 by Stephen Birarda # Copyright 2016 High Fidelity, Inc. @@ -34,8 +34,11 @@ if (WIN32) find_library(LIBOVRPLATFORM_LIBRARY_RELEASE NAMES ${_LIB_NAME} PATH_SUFFIXES Windows HINTS ${LIBOVRPLATFORM_SEARCH_DIRS}) - include(FindPackageHandleStandardArgs) - find_package_handle_standard_args(LIBOVRPLATFORM DEFAULT_MSG LIBOVRPLATFORM_INCLUDE_DIRS LIBOVRPLATFORM_LIBRARY) + include(SelectLibraryConfigurations) + select_library_configurations(LIBOVRPLATFORM) - mark_as_advanced(LIBOVRPLATFORM_INCLUDE_DIRS LIBOVRPLATFORM_LIBRARY LIBOVRPLATFORM_SEARCH_DIRS) + include(FindPackageHandleStandardArgs) + find_package_handle_standard_args(LIBOVRPLATFORM DEFAULT_MSG LIBOVRPLATFORM_INCLUDE_DIRS LIBOVRPLATFORM_LIBRARIES) + + mark_as_advanced(LIBOVRPLATFORM_INCLUDE_DIRS LIBOVRPLATFORM_LIBRARIES LIBOVRPLATFORM_SEARCH_DIRS) endif () From ea3e5f702a26e80c3653d70a39a7c7853a8a3ef2 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 16 Dec 2016 13:44:06 -0800 Subject: [PATCH 015/109] add popping of messages for entitlement check --- plugins/oculus/src/OculusHelpers.cpp | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/plugins/oculus/src/OculusHelpers.cpp b/plugins/oculus/src/OculusHelpers.cpp index 8a160b67ff..7cdf5cfeef 100644 --- a/plugins/oculus/src/OculusHelpers.cpp +++ b/plugins/oculus/src/OculusHelpers.cpp @@ -15,6 +15,9 @@ #include #include +#define OVRPL_DISABLED +#include + #include #include #include @@ -27,6 +30,7 @@ static ovrSession session { nullptr }; static bool _quitRequested { false }; static bool _reorientRequested { false }; +static bool _entitlementCheckFailed { false }; inline ovrErrorInfo getError() { ovrErrorInfo error; @@ -125,6 +129,20 @@ void handleOVREvents() { return; } + // pop messages to see if we got a return for an entitlement check + while ((message = ovr_PopMessage()) != nullptr) { + switch (ovr_Message_GetType(message)) { + case ovrMessage_Entitlement_GetIsViewerEntitled: { + if (!ovr_Message_IsError(message)) { + // this viewer is entitled, no need to flag anything + } else { + // we failed the entitlement check, set our flag so the app can stop + _entitlementCheckFailed = true; + } + } + } + } + _quitRequested = status.ShouldQuit; _reorientRequested = status.ShouldRecenter; } @@ -217,4 +235,4 @@ controller::Pose ovrControllerPoseToHandPose( pose.velocity = toGlm(handPose.LinearVelocity); pose.valid = true; return pose; -} \ No newline at end of file +} From b87a1b2b2bb3afe0090e01adfc48849e8011a35f Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 16 Dec 2016 14:53:48 -0800 Subject: [PATCH 016/109] only fire the entitlement check if OCULUS_APP_ID is set --- plugins/oculus/CMakeLists.txt | 6 ++++- plugins/oculus/src/OculusHelpers.cpp | 35 +++++++++++++++++++++++----- 2 files changed, 34 insertions(+), 7 deletions(-) diff --git a/plugins/oculus/CMakeLists.txt b/plugins/oculus/CMakeLists.txt index 7ee0228616..6b8d1a2ff9 100644 --- a/plugins/oculus/CMakeLists.txt +++ b/plugins/oculus/CMakeLists.txt @@ -11,6 +11,11 @@ if (WIN32) # we're using static GLEW, so define GLEW_STATIC add_definitions(-DGLEW_STATIC) + # if we were passed an Oculus App ID for entitlement checks, send that along + if (DEFINED ENV{OCULUS_APP_ID}) + add_definitions(-DOCULUS_APP_ID="\\"$ENV{OCULUS_APP_ID}\\"") + endif () + set(TARGET_NAME oculus) setup_hifi_plugin(Multimedia) link_hifi_libraries( @@ -18,7 +23,6 @@ if (WIN32) plugins ui-plugins display-plugins input-plugins audio-client networking render-utils ) - include_hifi_library_headers(octree) add_dependency_external_projects(LibOVR) diff --git a/plugins/oculus/src/OculusHelpers.cpp b/plugins/oculus/src/OculusHelpers.cpp index 7cdf5cfeef..ef9b667342 100644 --- a/plugins/oculus/src/OculusHelpers.cpp +++ b/plugins/oculus/src/OculusHelpers.cpp @@ -30,7 +30,6 @@ static ovrSession session { nullptr }; static bool _quitRequested { false }; static bool _reorientRequested { false }; -static bool _entitlementCheckFailed { false }; inline ovrErrorInfo getError() { ovrErrorInfo error; @@ -92,6 +91,18 @@ ovrSession acquireOculusSession() { logWarning("Failed to initialize Oculus SDK"); return session; } + +#ifdef OCULUS_APP_ID + if (true) { + if (ovr_PlatformInitializeWindows(OCULUS_APP_ID) != ovrPlatformInitialize_Success) { + // we were unable to initialize the platform for entitlement check - fail the check + _quitRequested = true; + } else { + qCDebug(oculus) << "Performing Oculus Platform entitlement check"; + ovr_Entitlement_GetIsViewerEntitled(); + } + } +#endif Q_ASSERT(0 == refCount); ovrGraphicsLuid luid; @@ -129,22 +140,34 @@ void handleOVREvents() { return; } + _quitRequested = status.ShouldQuit; + _reorientRequested = status.ShouldRecenter; + + #ifdef OCULUS_APP_ID // pop messages to see if we got a return for an entitlement check - while ((message = ovr_PopMessage()) != nullptr) { + ovrMessageHandle message = ovr_PopMessage(); + + while (message) { switch (ovr_Message_GetType(message)) { case ovrMessage_Entitlement_GetIsViewerEntitled: { if (!ovr_Message_IsError(message)) { // this viewer is entitled, no need to flag anything + qCDebug(oculus) << "Oculus Platform entitlement check succeeded, proceeding normally"; } else { // we failed the entitlement check, set our flag so the app can stop - _entitlementCheckFailed = true; + qCDebug(oculus) << "Oculus Platform entitlement check failed, app will now quit" << OCULUS_APP_ID; + _quitRequested = true; } } } - } - _quitRequested = status.ShouldQuit; - _reorientRequested = status.ShouldRecenter; + // free the message handle to cleanup and not leak + ovr_FreeMessage(message); + + // pop the next message to check, if there is one + message = ovr_PopMessage(); + } +#endif } bool quitRequested() { From f592d8d894fa7abe6c3072397061d857b7070481 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 16 Dec 2016 15:19:26 -0800 Subject: [PATCH 017/109] force entitlement check for all builds with OCULUS_APP_ID --- plugins/oculus/src/OculusHelpers.cpp | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/plugins/oculus/src/OculusHelpers.cpp b/plugins/oculus/src/OculusHelpers.cpp index ef9b667342..92cfbcc5bd 100644 --- a/plugins/oculus/src/OculusHelpers.cpp +++ b/plugins/oculus/src/OculusHelpers.cpp @@ -93,14 +93,13 @@ ovrSession acquireOculusSession() { } #ifdef OCULUS_APP_ID - if (true) { - if (ovr_PlatformInitializeWindows(OCULUS_APP_ID) != ovrPlatformInitialize_Success) { - // we were unable to initialize the platform for entitlement check - fail the check - _quitRequested = true; - } else { - qCDebug(oculus) << "Performing Oculus Platform entitlement check"; - ovr_Entitlement_GetIsViewerEntitled(); - } + if (ovr_PlatformInitializeWindows(OCULUS_APP_ID) != ovrPlatformInitialize_Success) { + // we were unable to initialize the platform for entitlement check - fail the check + _quitRequested = true; + } + else { + qCDebug(oculus) << "Performing Oculus Platform entitlement check"; + ovr_Entitlement_GetIsViewerEntitled(); } #endif From 2ec9b81aa6880a13d83269df2c8fa0b37960365e Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 16 Dec 2016 15:48:16 -0800 Subject: [PATCH 018/109] fix passing of OCULUS_APP_ID to platform SDK initialization --- plugins/oculus/CMakeLists.txt | 2 +- plugins/oculus/src/OculusHelpers.cpp | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/plugins/oculus/CMakeLists.txt b/plugins/oculus/CMakeLists.txt index 6b8d1a2ff9..55eb9c7b85 100644 --- a/plugins/oculus/CMakeLists.txt +++ b/plugins/oculus/CMakeLists.txt @@ -13,7 +13,7 @@ if (WIN32) # if we were passed an Oculus App ID for entitlement checks, send that along if (DEFINED ENV{OCULUS_APP_ID}) - add_definitions(-DOCULUS_APP_ID="\\"$ENV{OCULUS_APP_ID}\\"") + add_definitions(-DOCULUS_APP_ID="$ENV{OCULUS_APP_ID}") endif () set(TARGET_NAME oculus) diff --git a/plugins/oculus/src/OculusHelpers.cpp b/plugins/oculus/src/OculusHelpers.cpp index 92cfbcc5bd..7cafe9a1d4 100644 --- a/plugins/oculus/src/OculusHelpers.cpp +++ b/plugins/oculus/src/OculusHelpers.cpp @@ -96,8 +96,7 @@ ovrSession acquireOculusSession() { if (ovr_PlatformInitializeWindows(OCULUS_APP_ID) != ovrPlatformInitialize_Success) { // we were unable to initialize the platform for entitlement check - fail the check _quitRequested = true; - } - else { + } else { qCDebug(oculus) << "Performing Oculus Platform entitlement check"; ovr_Entitlement_GetIsViewerEntitled(); } From 46086cdcceb4b25a1e8cbf73e03a787f2a56fc39 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 5 Jan 2017 13:50:45 -0800 Subject: [PATCH 019/109] set the OCULUS_STORE property with oculus store arg --- interface/src/Application.cpp | 41 ++++++++++--------- .../shared/src/shared/GlobalAppProperties.cpp | 1 + .../shared/src/shared/GlobalAppProperties.h | 1 + plugins/oculus/src/OculusHelpers.cpp | 17 ++++---- 4 files changed, 34 insertions(+), 26 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 832419a75c..75290f77d5 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -250,7 +250,7 @@ public: static const unsigned long MAX_HEARTBEAT_AGE_USECS = 30 * USECS_PER_SECOND; static const int WARNING_ELAPSED_HEARTBEAT = 500 * USECS_PER_MSEC; // warn if elapsed heartbeat average is large static const int HEARTBEAT_SAMPLES = 100000; // ~5 seconds worth of samples - + // Set the heartbeat on launch DeadlockWatchdogThread() { setObjectName("Deadlock Watchdog"); @@ -613,7 +613,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo _window->setWindowTitle("Interface"); Model::setAbstractViewStateInterface(this); // The model class will sometimes need to know view state details from us - + auto nodeList = DependencyManager::get(); // Set up a watchdog thread to intentionally crash the application on deadlocks @@ -634,6 +634,9 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo qCDebug(interfaceapp) << "[VERSION] We will use DEVELOPMENT global services."; #endif + // set the OCULUS_STORE property so the oculus plugin can know if we ran from the Oculus Store + static const QString OCULUS_STORE_ARG = "--oculus-store"; + setProperty(hifi::properties::OCULUS_STORE, arguments().indexOf(OCULUS_STORE_ARG) != -1); static const QString NO_UPDATER_ARG = "--no-updater"; static const bool noUpdater = arguments().indexOf(NO_UPDATER_ARG) != -1; @@ -694,7 +697,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo audioIO->setPositionGetter([]{ auto avatarManager = DependencyManager::get(); auto myAvatar = avatarManager ? avatarManager->getMyAvatar() : nullptr; - + return myAvatar ? myAvatar->getPositionForAudio() : Vectors::ZERO; }); audioIO->setOrientationGetter([]{ @@ -877,7 +880,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo #ifdef Q_OS_MAC auto cursorTarget = _window; // OSX doesn't seem to provide for hiding the cursor only on the GL widget #else - // On windows and linux, hiding the top level cursor also means it's invisible when hovering over the + // On windows and linux, hiding the top level cursor also means it's invisible when hovering over the // window menu, which is a pain, so only hide it for the GL surface auto cursorTarget = _glWidget; #endif @@ -1122,7 +1125,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo loadSettings(); // Now that we've loaded the menu and thus switched to the previous display plugin - // we can unlock the desktop repositioning code, since all the positions will be + // we can unlock the desktop repositioning code, since all the positions will be // relative to the desktop size for this plugin auto offscreenUi = DependencyManager::get(); offscreenUi->getDesktop()->setProperty("repositionLocked", false); @@ -1595,7 +1598,7 @@ void Application::checkChangeCursor() { #ifdef Q_OS_MAC auto cursorTarget = _window; // OSX doesn't seem to provide for hiding the cursor only on the GL widget #else - // On windows and linux, hiding the top level cursor also means it's invisible when hovering over the + // On windows and linux, hiding the top level cursor also means it's invisible when hovering over the // window menu, which is a pain, so only hide it for the GL surface auto cursorTarget = _glWidget; #endif @@ -1782,7 +1785,7 @@ Application::~Application() { #endif // The window takes ownership of the menu, so this has the side effect of destroying it. _window->setMenuBar(nullptr); - + _window->deleteLater(); // Can't log to file passed this point, FileLogger about to be deleted @@ -1808,10 +1811,10 @@ void Application::initializeGL() { _glWidget->makeCurrent(); gpu::Context::init(); - qApp->setProperty(hifi::properties::gl::MAKE_PROGRAM_CALLBACK, + qApp->setProperty(hifi::properties::gl::MAKE_PROGRAM_CALLBACK, QVariant::fromValue((void*)(&gpu::gl::GLBackend::makeProgram))); _gpuContext = std::make_shared(); - // The gpu context can make child contexts for transfers, so + // The gpu context can make child contexts for transfers, so // we need to restore primary rendering context _glWidget->makeCurrent(); @@ -2027,7 +2030,7 @@ void Application::paintGL() { // FIXME not needed anymore? _offscreenContext->makeCurrent(); - // If a display plugin loses it's underlying support, it + // If a display plugin loses it's underlying support, it // needs to be able to signal us to not use it if (!displayPlugin->beginFrameRender(_frameCount)) { _inPaint = false; @@ -2839,7 +2842,7 @@ void Application::keyPressEvent(QKeyEvent* event) { if (isMirrorChecked) { // if we got here without coming in from a non-Full Screen mirror case, then our - // _returnFromFullScreenMirrorTo is unknown. In that case we'll go to the old + // _returnFromFullScreenMirrorTo is unknown. In that case we'll go to the old // behavior of returning to ThirdPerson if (_returnFromFullScreenMirrorTo.isEmpty()) { _returnFromFullScreenMirrorTo = MenuOption::ThirdPerson; @@ -3013,7 +3016,7 @@ void Application::mouseMoveEvent(QMouseEvent* event) { maybeToggleMenuVisible(event); auto& compositor = getApplicationCompositor(); - // if this is a real mouse event, and we're in HMD mode, then we should use it to move the + // if this is a real mouse event, and we're in HMD mode, then we should use it to move the // compositor reticle // handleRealMouseMoveEvent() will return true, if we shouldn't process the event further if (!compositor.fakeEventActive() && compositor.handleRealMouseMoveEvent()) { @@ -4027,7 +4030,7 @@ void Application::setKeyboardFocusEntity(EntityItemID entityItemID) { } _lastAcceptedKeyPress = usecTimestampNow(); - setKeyboardFocusHighlight(entity->getPosition(), entity->getRotation(), + setKeyboardFocusHighlight(entity->getPosition(), entity->getRotation(), entity->getDimensions() * FOCUS_HIGHLIGHT_EXPANSION_FACTOR); } } @@ -4618,7 +4621,7 @@ void Application::queryOctree(NodeType_t serverType, PacketType packetType, Node _octreeQuery.setMaxQueryPacketsPerSecond(0); } - // if asked to forceResend, then set the query's position/orientation to be degenerate in a manner + // if asked to forceResend, then set the query's position/orientation to be degenerate in a manner // that will cause our next query to be guarenteed to be different and the server will resend to us if (forceResend) { _octreeQuery.setCameraPosition(glm::vec3(-0.1, -0.1, -0.1)); @@ -5751,7 +5754,7 @@ void Application::addAssetToWorldWithNewMapping(QString filePath, QString mappin mapping = mapping.insert(mapping.lastIndexOf("."), "-" + QString::number(copy)); addAssetToWorldWithNewMapping(filePath, mapping, copy); } else { - QString errorInfo = "Too many copies of asset name: " + QString errorInfo = "Too many copies of asset name: " + mapping.left(mapping.length() - QString::number(copy).length() - 1); qWarning(interfaceapp) << "Error downloading asset: " + errorInfo; addAssetToWorldError(filenameFromPath(filePath), errorInfo); @@ -5818,7 +5821,7 @@ void Application::addAssetToWorldAddEntity(QString filePath, QString mapping) { // Note: Model dimensions are not available here; model is scaled per FBX mesh in RenderableModelEntityItem::update() later // on. But FBX dimensions may be in cm, so we monitor for the dimension change and rescale again if warranted. - + if (entityID == QUuid()) { QString errorInfo = "Could not add asset " + mapping + " to world."; qWarning(interfaceapp) << "Could not add asset to world: " + errorInfo; @@ -6283,7 +6286,7 @@ glm::uvec2 Application::getCanvasSize() const { } QRect Application::getRenderingGeometry() const { - auto geometry = _glWidget->geometry(); + auto geometry = _glWidget->geometry(); auto topLeft = geometry.topLeft(); auto topLeftScreen = _glWidget->mapToGlobal(topLeft); geometry.moveTopLeft(topLeftScreen); @@ -6646,8 +6649,8 @@ bool Application::makeRenderingContextCurrent() { return _offscreenContext->makeCurrent(); } -bool Application::isForeground() const { - return _isForeground && !_window->isMinimized(); +bool Application::isForeground() const { + return _isForeground && !_window->isMinimized(); } void Application::sendMousePressOnEntity(QUuid id, PointerEvent event) { diff --git a/libraries/shared/src/shared/GlobalAppProperties.cpp b/libraries/shared/src/shared/GlobalAppProperties.cpp index a8a38457f2..f2d8990708 100644 --- a/libraries/shared/src/shared/GlobalAppProperties.cpp +++ b/libraries/shared/src/shared/GlobalAppProperties.cpp @@ -13,6 +13,7 @@ namespace hifi { namespace properties { const char* CRASHED = "com.highfidelity.crashed"; const char* STEAM = "com.highfidelity.launchedFromSteam"; const char* LOGGER = "com.highfidelity.logger"; + const char* OCULUS_STORE = "com.highfidelity.oculusStore"; const char* TEST = "com.highfidelity.test"; const char* TRACING = "com.highfidelity.tracing"; diff --git a/libraries/shared/src/shared/GlobalAppProperties.h b/libraries/shared/src/shared/GlobalAppProperties.h index 92d8388f09..609f2afd94 100644 --- a/libraries/shared/src/shared/GlobalAppProperties.h +++ b/libraries/shared/src/shared/GlobalAppProperties.h @@ -15,6 +15,7 @@ namespace hifi { namespace properties { extern const char* CRASHED; extern const char* STEAM; extern const char* LOGGER; + extern const char* OCULUS_STORE; extern const char* TEST; extern const char* TRACING; diff --git a/plugins/oculus/src/OculusHelpers.cpp b/plugins/oculus/src/OculusHelpers.cpp index 7cafe9a1d4..be4866a71e 100644 --- a/plugins/oculus/src/OculusHelpers.cpp +++ b/plugins/oculus/src/OculusHelpers.cpp @@ -20,6 +20,7 @@ #include #include +#include #include Q_LOGGING_CATEGORY(displayplugins, "hifi.plugins.display") @@ -91,14 +92,16 @@ ovrSession acquireOculusSession() { logWarning("Failed to initialize Oculus SDK"); return session; } - + #ifdef OCULUS_APP_ID - if (ovr_PlatformInitializeWindows(OCULUS_APP_ID) != ovrPlatformInitialize_Success) { - // we were unable to initialize the platform for entitlement check - fail the check - _quitRequested = true; - } else { - qCDebug(oculus) << "Performing Oculus Platform entitlement check"; - ovr_Entitlement_GetIsViewerEntitled(); + if (qApp->property(hifi::properties::OCULUS_STORE).toBool()) { + if (ovr_PlatformInitializeWindows(OCULUS_APP_ID) != ovrPlatformInitialize_Success) { + // we were unable to initialize the platform for entitlement check - fail the check + _quitRequested = true; + } else { + qCDebug(oculus) << "Performing Oculus Platform entitlement check"; + ovr_Entitlement_GetIsViewerEntitled(); + } } #endif From 8f279ce1f7db4367bfce1d6362cad4768d8f0439 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 5 Jan 2017 14:21:35 -0800 Subject: [PATCH 020/109] only pop to check entitlements if oculus store build --- plugins/oculus/src/OculusHelpers.cpp | 39 +++++++++++++++------------- 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/plugins/oculus/src/OculusHelpers.cpp b/plugins/oculus/src/OculusHelpers.cpp index be4866a71e..340b804404 100644 --- a/plugins/oculus/src/OculusHelpers.cpp +++ b/plugins/oculus/src/OculusHelpers.cpp @@ -145,28 +145,31 @@ void handleOVREvents() { _reorientRequested = status.ShouldRecenter; #ifdef OCULUS_APP_ID - // pop messages to see if we got a return for an entitlement check - ovrMessageHandle message = ovr_PopMessage(); - while (message) { - switch (ovr_Message_GetType(message)) { - case ovrMessage_Entitlement_GetIsViewerEntitled: { - if (!ovr_Message_IsError(message)) { - // this viewer is entitled, no need to flag anything - qCDebug(oculus) << "Oculus Platform entitlement check succeeded, proceeding normally"; - } else { - // we failed the entitlement check, set our flag so the app can stop - qCDebug(oculus) << "Oculus Platform entitlement check failed, app will now quit" << OCULUS_APP_ID; - _quitRequested = true; + if (qApp->property(hifi::properties::OCULUS_STORE).toBool()) { + // pop messages to see if we got a return for an entitlement check + ovrMessageHandle message = ovr_PopMessage(); + + while (message) { + switch (ovr_Message_GetType(message)) { + case ovrMessage_Entitlement_GetIsViewerEntitled: { + if (!ovr_Message_IsError(message)) { + // this viewer is entitled, no need to flag anything + qCDebug(oculus) << "Oculus Platform entitlement check succeeded, proceeding normally"; + } else { + // we failed the entitlement check, set our flag so the app can stop + qCDebug(oculus) << "Oculus Platform entitlement check failed, app will now quit" << OCULUS_APP_ID; + _quitRequested = true; + } } } + + // free the message handle to cleanup and not leak + ovr_FreeMessage(message); + + // pop the next message to check, if there is one + message = ovr_PopMessage(); } - - // free the message handle to cleanup and not leak - ovr_FreeMessage(message); - - // pop the next message to check, if there is one - message = ovr_PopMessage(); } #endif } From 8bb200902f3b1c496afadfba0cc9ecb249f70857 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Tue, 10 Jan 2017 16:40:48 -0800 Subject: [PATCH 021/109] First pass --- assignment-client/src/audio/AudioMixer.cpp | 18 ++++++++-- assignment-client/src/audio/AudioMixer.h | 1 + .../src/audio/AudioMixerClientData.h | 6 ++++ .../src/audio/AudioMixerSlave.cpp | 7 ++-- assignment-client/src/audio/AudioMixerSlave.h | 2 +- interface/resources/qml/hifi/NameCard.qml | 33 ++++++++++++++++++- interface/resources/qml/hifi/Pal.qml | 2 ++ libraries/networking/src/NodeList.cpp | 23 +++++++++++++ libraries/networking/src/NodeList.h | 1 + libraries/networking/src/udt/PacketHeaders.h | 3 +- .../src/UsersScriptingInterface.cpp | 5 +++ .../src/UsersScriptingInterface.h | 8 +++++ scripts/system/pal.js | 4 +++ 13 files changed, 105 insertions(+), 8 deletions(-) diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp index 19ebd4ea87..fddd2d641e 100644 --- a/assignment-client/src/audio/AudioMixer.cpp +++ b/assignment-client/src/audio/AudioMixer.cpp @@ -68,7 +68,8 @@ AudioMixer::AudioMixer(ReceivedMessage& message) : packetReceiver.registerListener(PacketType::KillAvatar, this, "handleKillAvatarPacket"); packetReceiver.registerListener(PacketType::NodeMuteRequest, this, "handleNodeMuteRequestPacket"); packetReceiver.registerListener(PacketType::RadiusIgnoreRequest, this, "handleRadiusIgnoreRequestPacket"); - packetReceiver.registerListener(PacketType::RequestsDomainListData, this, "handleRequestsDomainListDataPacket"); + packetReceiver.registerListener(PacketType::RequestsDomainListData, this, "handleRequestsDomainListDataPacket"); + packetReceiver.registerListener(PacketType::PerAvatarGainSet, this, "handlePerAvatarGainSetDataPacket"); connect(nodeList.data(), &NodeList::nodeKilled, this, &AudioMixer::handleNodeKilled); } @@ -186,7 +187,9 @@ void AudioMixer::handleNodeKilled(SharedNodePointer killedNode) { nodeList->eachNode([&killedNode](const SharedNodePointer& node) { auto clientData = dynamic_cast(node->getLinkedData()); if (clientData) { - clientData->removeHRTFsForNode(killedNode->getUUID()); + QUuid killedUUID = killedNode->getUUID(); + clientData->removePerAvatarGain(killedUUID); + clientData->removeHRTFsForNode(killedUUID); } }); } @@ -240,6 +243,17 @@ void AudioMixer::handleNodeIgnoreRequestPacket(QSharedPointer p sendingNode->parseIgnoreRequestMessage(packet); } +void AudioMixer::handlePerAvatarGainSetDataPacket(QSharedPointer packet, SharedNodePointer sendingNode) { + auto clientData = dynamic_cast(sendingNode->getLinkedData()); + if (clientData) { + // parse the UUID from the packet + QUuid ignoredUUID = QUuid::fromRfc4122(packet->readWithoutCopy(NUM_BYTES_RFC4122_UUID)); + float gain; + packet->readPrimitive(&gain); + clientData->setPerAvatarGain(ignoredUUID, gain); + } +} + void AudioMixer::handleRadiusIgnoreRequestPacket(QSharedPointer packet, SharedNodePointer sendingNode) { sendingNode->parseIgnoreRadiusRequestMessage(packet); } diff --git a/assignment-client/src/audio/AudioMixer.h b/assignment-client/src/audio/AudioMixer.h index d9759653fb..d88bc3b917 100644 --- a/assignment-client/src/audio/AudioMixer.h +++ b/assignment-client/src/audio/AudioMixer.h @@ -66,6 +66,7 @@ private slots: void handleRadiusIgnoreRequestPacket(QSharedPointer packet, SharedNodePointer sendingNode); void handleKillAvatarPacket(QSharedPointer packet, SharedNodePointer sendingNode); void handleNodeMuteRequestPacket(QSharedPointer packet, SharedNodePointer sendingNode); + void handlePerAvatarGainSetDataPacket(QSharedPointer packet, SharedNodePointer sendingNode); void start(); void removeHRTFsForFinishedInjector(const QUuid& streamID); diff --git a/assignment-client/src/audio/AudioMixerClientData.h b/assignment-client/src/audio/AudioMixerClientData.h index e637fd0409..d7fbfe5112 100644 --- a/assignment-client/src/audio/AudioMixerClientData.h +++ b/assignment-client/src/audio/AudioMixerClientData.h @@ -95,6 +95,10 @@ public: bool getRequestsDomainListData() { return _requestsDomainListData; } void setRequestsDomainListData(bool requesting) { _requestsDomainListData = requesting; } + float getPerAvatarGain(const QUuid& avatarID) { return (_perAvatarGain.count(avatarID) ? _perAvatarGain.at(avatarID) : 1.0f); } + void setPerAvatarGain(const QUuid& avatarID, float gain) { _perAvatarGain[avatarID] = gain; } + void removePerAvatarGain(const QUuid& avatarID) { _perAvatarGain.erase(avatarID); } + signals: void injectorStreamFinished(const QUuid& streamIdentifier); @@ -125,6 +129,8 @@ private: bool _shouldMuteClient { false }; bool _requestsDomainListData { false }; + + std::unordered_map _perAvatarGain; }; #endif // hifi_AudioMixerClientData_h diff --git a/assignment-client/src/audio/AudioMixerSlave.cpp b/assignment-client/src/audio/AudioMixerSlave.cpp index 28d3358eb5..90037b7187 100644 --- a/assignment-client/src/audio/AudioMixerSlave.cpp +++ b/assignment-client/src/audio/AudioMixerSlave.cpp @@ -252,12 +252,13 @@ bool AudioMixerSlave::prepareMix(const SharedNodePointer& node) { // Enumerate the audio streams attached to the otherNode auto streamsCopy = otherData->getAudioStreams(); + float thisAvatarGain = nodeData->getPerAvatarGain(otherNode->getUUID()); for (auto& streamPair : streamsCopy) { auto otherNodeStream = streamPair.second; bool isSelfWithEcho = ((*otherNode == *node) && (otherNodeStream->shouldLoopbackForNode())); // Add all audio streams that should be added to the mix if (isSelfWithEcho || (!isSelfWithEcho && !insideIgnoreRadius)) { - addStreamToMix(*nodeData, otherNode->getUUID(), *nodeAudioStream, *otherNodeStream); + addStreamToMix(*nodeData, otherNode->getUUID(), *nodeAudioStream, *otherNodeStream, thisAvatarGain); } } } @@ -278,7 +279,7 @@ bool AudioMixerSlave::prepareMix(const SharedNodePointer& node) { } void AudioMixerSlave::addStreamToMix(AudioMixerClientData& listenerNodeData, const QUuid& sourceNodeID, - const AvatarAudioStream& listeningNodeStream, const PositionalAudioStream& streamToAdd) { + const AvatarAudioStream& listeningNodeStream, const PositionalAudioStream& streamToAdd, float perAvatarGain) { // to reduce artifacts we calculate the gain and azimuth for every source for this listener // even if we are not going to end up mixing in this source @@ -295,7 +296,7 @@ void AudioMixerSlave::addStreamToMix(AudioMixerClientData& listenerNodeData, con float distance = glm::max(glm::length(relativePosition), EPSILON); // figure out the gain for this source at the listener - float gain = gainForSource(listeningNodeStream, streamToAdd, relativePosition, isEcho); + float gain = gainForSource(listeningNodeStream, streamToAdd, relativePosition, isEcho) + (perAvatarGain - 1.0f); // figure out the azimuth to this source at the listener float azimuth = isEcho ? 0.0f : azimuthForSource(listeningNodeStream, listeningNodeStream, relativePosition); diff --git a/assignment-client/src/audio/AudioMixerSlave.h b/assignment-client/src/audio/AudioMixerSlave.h index c4aabfbb4a..89aa70f99a 100644 --- a/assignment-client/src/audio/AudioMixerSlave.h +++ b/assignment-client/src/audio/AudioMixerSlave.h @@ -43,7 +43,7 @@ private: bool prepareMix(const SharedNodePointer& node); // add a stream to the mix void addStreamToMix(AudioMixerClientData& listenerData, const QUuid& streamerID, - const AvatarAudioStream& listenerStream, const PositionalAudioStream& streamer); + const AvatarAudioStream& listenerStream, const PositionalAudioStream& streamer, float perAvatarGain); float gainForSource(const AvatarAudioStream& listener, const PositionalAudioStream& streamer, const glm::vec3& relativePosition, bool isEcho); diff --git a/interface/resources/qml/hifi/NameCard.qml b/interface/resources/qml/hifi/NameCard.qml index a0dde8bacc..0a0b662193 100644 --- a/interface/resources/qml/hifi/NameCard.qml +++ b/interface/resources/qml/hifi/NameCard.qml @@ -10,6 +10,7 @@ // import QtQuick 2.5 +import QtQuick.Controls 1.4 import QtGraphicalEffects 1.0 import "../styles-uit" @@ -27,12 +28,14 @@ Row { } // Properties - property int contentHeight: 50 + property int contentHeight: isMyCard ? 50 : 70 + property string uuid: "" property string displayName: "" property string userName: "" property int displayTextHeight: 18 property int usernameTextHeight: 12 property real audioLevel: 0.0 + property bool isMyCard: false /* User image commented out for now - will probably be re-introduced later. Column { @@ -138,5 +141,33 @@ Row { } } } + + // Per-Avatar Gain Slider Spacer + Item { + width: parent.width + height: 4 + visible: !isMyCard + } + // Per-Avatar Gain Slider + Slider { + id: gainSlider + visible: !isMyCard + width: parent.width + height: 16 + value: 1.0 + minimumValue: 0.0 + maximumValue: 1.5 + stepSize: 0.1 + updateValueWhileDragging: false + onValueChanged: updateGainFromQML(uuid, value) + } + } + + function updateGainFromQML(avatarUuid, gainValue) { + var data = { + sessionId: avatarUuid, + gain: (Math.exp(gainValue) - 1) / (Math.E - 1) + }; + pal.sendToScript({method: 'updateGain', params: data}); } } diff --git a/interface/resources/qml/hifi/Pal.qml b/interface/resources/qml/hifi/Pal.qml index 66dce622ff..4f65497da5 100644 --- a/interface/resources/qml/hifi/Pal.qml +++ b/interface/resources/qml/hifi/Pal.qml @@ -65,6 +65,7 @@ Rectangle { displayName: myData.displayName userName: myData.userName audioLevel: myData.audioLevel + isMyCard: true // Size width: nameCardWidth height: parent.height @@ -206,6 +207,7 @@ Rectangle { userName: model && model.userName audioLevel: model && model.audioLevel visible: !isCheckBox && !isButton + uuid: model && model.sessionId // Size width: nameCardWidth height: parent.height diff --git a/libraries/networking/src/NodeList.cpp b/libraries/networking/src/NodeList.cpp index d890431a45..00f13dff3d 100644 --- a/libraries/networking/src/NodeList.cpp +++ b/libraries/networking/src/NodeList.cpp @@ -951,6 +951,29 @@ void NodeList::maybeSendIgnoreSetToNode(SharedNodePointer newNode) { } } +void NodeList::setAvatarGain(const QUuid& nodeID, float gain) { + // cannot set gain of yourself or nobody + if (!nodeID.isNull() && _sessionUUID != nodeID) { + auto audioMixer = soloNodeOfType(NodeType::AudioMixer); + if (audioMixer) { + // setup the packet + auto setAvatarGainPacket = NLPacket::create(PacketType::PerAvatarGainSet, NUM_BYTES_RFC4122_UUID + sizeof(float), true); + + // write the node ID to the packet + setAvatarGainPacket->write(nodeID.toRfc4122()); + setAvatarGainPacket->writePrimitive((gain < 5.0f ? gain : 5.0f)); + + qCDebug(networking) << "Sending Set Avatar Gain packet UUID: " << uuidStringWithoutCurlyBraces(nodeID) << "Gain:" << gain; + + sendPacket(std::move(setAvatarGainPacket), *audioMixer); + } else { + qWarning() << "Couldn't find audio mixer to send set gain request"; + } + } else { + qWarning() << "NodeList::setAvatarGain called with an invalid ID or an ID which matches the current session ID:" << nodeID; + } +} + void NodeList::kickNodeBySessionID(const QUuid& nodeID) { // send a request to domain-server to kick the node with the given session ID // the domain-server will handle the persistence of the kick (via username or IP) diff --git a/libraries/networking/src/NodeList.h b/libraries/networking/src/NodeList.h index 8e285629dc..5c477303e2 100644 --- a/libraries/networking/src/NodeList.h +++ b/libraries/networking/src/NodeList.h @@ -82,6 +82,7 @@ public: bool isIgnoringNode(const QUuid& nodeID) const; void personalMuteNodeBySessionID(const QUuid& nodeID, bool muteEnabled); bool isPersonalMutingNode(const QUuid& nodeID) const; + void setAvatarGain(const QUuid& nodeID, float gain); void kickNodeBySessionID(const QUuid& nodeID); void muteNodeBySessionID(const QUuid& nodeID); diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index e4c4937622..0328037eb3 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -106,7 +106,8 @@ public: ViewFrustum, RequestsDomainListData, ExitingSpaceBubble, - LAST_PACKET_TYPE = ExitingSpaceBubble + PerAvatarGainSet, + LAST_PACKET_TYPE = ExitingSpaceBubble // FIXME!!! }; }; diff --git a/libraries/script-engine/src/UsersScriptingInterface.cpp b/libraries/script-engine/src/UsersScriptingInterface.cpp index d0ad699846..3a3225ec75 100644 --- a/libraries/script-engine/src/UsersScriptingInterface.cpp +++ b/libraries/script-engine/src/UsersScriptingInterface.cpp @@ -42,6 +42,11 @@ bool UsersScriptingInterface::getPersonalMuteStatus(const QUuid& nodeID) { return DependencyManager::get()->isPersonalMutingNode(nodeID); } +void UsersScriptingInterface::setAvatarGain(const QUuid& nodeID, float gain) { + // ask the NodeList to set the gain of the specified avatar + DependencyManager::get()->setAvatarGain(nodeID, gain); +} + void UsersScriptingInterface::kick(const QUuid& nodeID) { // ask the NodeList to kick the user with the given session ID DependencyManager::get()->kickNodeBySessionID(nodeID); diff --git a/libraries/script-engine/src/UsersScriptingInterface.h b/libraries/script-engine/src/UsersScriptingInterface.h index 341285a742..d2d77a6796 100644 --- a/libraries/script-engine/src/UsersScriptingInterface.h +++ b/libraries/script-engine/src/UsersScriptingInterface.h @@ -61,6 +61,14 @@ public slots: */ bool getPersonalMuteStatus(const QUuid& nodeID); + /**jsdoc + * Sets an avatar's gain for you and you only. + * @function Users.setAvatarGain + * @param {nodeID} nodeID The node or session ID of the user whose gain you want to modify. + * @param {float} gain The gain of the avatar you'd like to set. + */ + void setAvatarGain(const QUuid& nodeID, float gain); + /**jsdoc * Kick another user. * @function Users.kick diff --git a/scripts/system/pal.js b/scripts/system/pal.js index 48f44570fd..12f0793b97 100644 --- a/scripts/system/pal.js +++ b/scripts/system/pal.js @@ -233,6 +233,10 @@ pal.fromQml.connect(function (message) { // messages are {method, params}, like removeOverlays(); populateUserList(); break; + case 'updateGain': + data = message.params; + Users.setAvatarGain(data['sessionId'], data['gain']); + break; default: print('Unrecognized message from Pal.qml:', JSON.stringify(message)); } From dcff873cd46d4747c53539a728c3717c774453fa Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Tue, 10 Jan 2017 16:50:38 -0800 Subject: [PATCH 022/109] Modify slider function --- assignment-client/src/audio/AudioMixerSlave.cpp | 2 +- interface/resources/qml/hifi/NameCard.qml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/assignment-client/src/audio/AudioMixerSlave.cpp b/assignment-client/src/audio/AudioMixerSlave.cpp index 90037b7187..d502239341 100644 --- a/assignment-client/src/audio/AudioMixerSlave.cpp +++ b/assignment-client/src/audio/AudioMixerSlave.cpp @@ -296,7 +296,7 @@ void AudioMixerSlave::addStreamToMix(AudioMixerClientData& listenerNodeData, con float distance = glm::max(glm::length(relativePosition), EPSILON); // figure out the gain for this source at the listener - float gain = gainForSource(listeningNodeStream, streamToAdd, relativePosition, isEcho) + (perAvatarGain - 1.0f); + float gain = gainForSource(listeningNodeStream, streamToAdd, relativePosition, isEcho) * perAvatarGain; // figure out the azimuth to this source at the listener float azimuth = isEcho ? 0.0f : azimuthForSource(listeningNodeStream, listeningNodeStream, relativePosition); diff --git a/interface/resources/qml/hifi/NameCard.qml b/interface/resources/qml/hifi/NameCard.qml index 0a0b662193..18dbaf1fe1 100644 --- a/interface/resources/qml/hifi/NameCard.qml +++ b/interface/resources/qml/hifi/NameCard.qml @@ -166,7 +166,7 @@ Row { function updateGainFromQML(avatarUuid, gainValue) { var data = { sessionId: avatarUuid, - gain: (Math.exp(gainValue) - 1) / (Math.E - 1) + gain: (Math.pow(20, gainValue) - 1) / (20 - 1) }; pal.sendToScript({method: 'updateGain', params: data}); } From 503468e49b865f341ffdcfd7a965b12b1a959f39 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Tue, 10 Jan 2017 17:20:25 -0800 Subject: [PATCH 023/109] Style changes --- interface/resources/qml/hifi/NameCard.qml | 19 +++++++++++++++++-- interface/resources/qml/hifi/Pal.qml | 6 +++--- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/interface/resources/qml/hifi/NameCard.qml b/interface/resources/qml/hifi/NameCard.qml index 18dbaf1fe1..1f4ef07131 100644 --- a/interface/resources/qml/hifi/NameCard.qml +++ b/interface/resources/qml/hifi/NameCard.qml @@ -11,6 +11,7 @@ import QtQuick 2.5 import QtQuick.Controls 1.4 +import QtQuick.Controls.Styles 1.4 import QtGraphicalEffects 1.0 import "../styles-uit" @@ -28,7 +29,7 @@ Row { } // Properties - property int contentHeight: isMyCard ? 50 : 70 + property int contentHeight: 70 property string uuid: "" property string displayName: "" property string userName: "" @@ -153,13 +154,27 @@ Row { id: gainSlider visible: !isMyCard width: parent.width - height: 16 + height: 18 value: 1.0 minimumValue: 0.0 maximumValue: 1.5 stepSize: 0.1 updateValueWhileDragging: false onValueChanged: updateGainFromQML(uuid, value) + style: SliderStyle { + groove: Rectangle { + color: "#dbdbdb" + implicitWidth: gainSlider.width + implicitHeight: 4 + radius: 2 + } + handle: Rectangle { + anchors.centerIn: parent + color: (control.pressed || control.hovered) ? "#00b4ef" : "#8F8F8F" + implicitWidth: 10 + implicitHeight: 18 + } + } } } diff --git a/interface/resources/qml/hifi/Pal.qml b/interface/resources/qml/hifi/Pal.qml index 4f65497da5..2cc07bf90d 100644 --- a/interface/resources/qml/hifi/Pal.qml +++ b/interface/resources/qml/hifi/Pal.qml @@ -24,8 +24,8 @@ Rectangle { // Style color: "#E3E3E3" // Properties - property int myCardHeight: 70 - property int rowHeight: 70 + property int myCardHeight: 90 + property int rowHeight: 90 property int actionButtonWidth: 75 property int nameCardWidth: palContainer.width - actionButtonWidth*(iAmAdmin ? 4 : 2) - 4 - hifi.dimensions.scrollbarBackgroundWidth property var myData: ({displayName: "", userName: "", audioLevel: 0.0}) // valid dummy until set @@ -51,7 +51,7 @@ Rectangle { id: myInfo // Size width: palContainer.width - height: myCardHeight + 20 + height: myCardHeight // Style color: pal.color // Anchors From 8c3766dfe81d26192b0f1447f55d511041e73f58 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Tue, 10 Jan 2017 17:21:51 -0800 Subject: [PATCH 024/109] Fixme --- libraries/networking/src/udt/PacketHeaders.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index 0328037eb3..c012ed8f67 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -107,7 +107,7 @@ public: RequestsDomainListData, ExitingSpaceBubble, PerAvatarGainSet, - LAST_PACKET_TYPE = ExitingSpaceBubble // FIXME!!! + LAST_PACKET_TYPE = PerAvatarGainSet }; }; From b9cddc31a941f940067692537b2117449244ee52 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Wed, 11 Jan 2017 11:56:35 -0800 Subject: [PATCH 025/109] Checkpoint before implementing Ken's suggestions --- interface/resources/qml/hifi/NameCard.qml | 3 ++- interface/resources/qml/hifi/Pal.qml | 4 +++- scripts/system/pal.js | 12 ++++++------ 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/interface/resources/qml/hifi/NameCard.qml b/interface/resources/qml/hifi/NameCard.qml index 1f4ef07131..4b8ee6cbc3 100644 --- a/interface/resources/qml/hifi/NameCard.qml +++ b/interface/resources/qml/hifi/NameCard.qml @@ -155,7 +155,7 @@ Row { visible: !isMyCard width: parent.width height: 18 - value: 1.0 + value: pal.gain[uuid] ? pal.gain[uuid] : 1.0 minimumValue: 0.0 maximumValue: 1.5 stepSize: 0.1 @@ -179,6 +179,7 @@ Row { } function updateGainFromQML(avatarUuid, gainValue) { + pal.gain[avatarUuid] = gainValue; var data = { sessionId: avatarUuid, gain: (Math.pow(20, gainValue) - 1) / (20 - 1) diff --git a/interface/resources/qml/hifi/Pal.qml b/interface/resources/qml/hifi/Pal.qml index 2cc07bf90d..039ab78baf 100644 --- a/interface/resources/qml/hifi/Pal.qml +++ b/interface/resources/qml/hifi/Pal.qml @@ -32,6 +32,7 @@ Rectangle { property var ignored: ({}); // Keep a local list of ignored avatars & their data. Necessary because HashMap is slow to respond after ignoring. property var userModelData: [] // This simple list is essentially a mirror of the userModel listModel without all the extra complexities. property bool iAmAdmin: false + property var gain: ({}); // Keep a local list of per-avatar gain. Far faster than keeping this data on the server. // This is the container for the PAL Rectangle { @@ -494,8 +495,9 @@ Rectangle { } } break; - case 'clearIgnored': + case 'clearLocalQMLData': ignored = {}; + gain = {}; break; default: console.log('Unrecognized message:', JSON.stringify(message)); diff --git a/scripts/system/pal.js b/scripts/system/pal.js index 12f0793b97..6b3fd00c3b 100644 --- a/scripts/system/pal.js +++ b/scripts/system/pal.js @@ -586,14 +586,14 @@ button.clicked.connect(onClicked); pal.visibleChanged.connect(onVisibleChanged); pal.closed.connect(off); Users.usernameFromIDReply.connect(usernameFromIDReply); -function clearIgnoredInQMLAndClosePAL() { - pal.sendToQml({ method: 'clearIgnored' }); +function clearLocalQMLDataAndClosePAL() { + pal.sendToQml({ method: 'clearLocalQMLData' }); if (pal.visible) { onClicked(); // Close the PAL } } -Window.domainChanged.connect(clearIgnoredInQMLAndClosePAL); -Window.domainConnectionRefused.connect(clearIgnoredInQMLAndClosePAL); +Window.domainChanged.connect(clearLocalQMLDataAndClosePAL); +Window.domainConnectionRefused.connect(clearLocalQMLDataAndClosePAL); // // Cleanup. @@ -604,8 +604,8 @@ Script.scriptEnding.connect(function () { pal.visibleChanged.disconnect(onVisibleChanged); pal.closed.disconnect(off); Users.usernameFromIDReply.disconnect(usernameFromIDReply); - Window.domainChanged.disconnect(clearIgnoredInQMLAndClosePAL); - Window.domainConnectionRefused.disconnect(clearIgnoredInQMLAndClosePAL); + Window.domainChanged.disconnect(clearLocalQMLDataAndClosePAL); + Window.domainConnectionRefused.disconnect(clearLocalQMLDataAndClosePAL); Messages.unsubscribe(CHANNEL); Messages.messageReceived.disconnect(receiveMessage); off(); From b0b6aeac6c0eba9331eaa4f837803032acb93944 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Wed, 11 Jan 2017 16:16:35 -0800 Subject: [PATCH 026/109] Ken's feedback --- assignment-client/src/audio/AudioMixer.cpp | 13 ++++---- .../src/audio/AudioMixerClientData.h | 6 ---- .../src/audio/AudioMixerSlave.cpp | 7 ++--- assignment-client/src/audio/AudioMixerSlave.h | 2 +- interface/resources/qml/hifi/NameCard.qml | 30 ++++++++++++------- interface/resources/qml/hifi/Pal.qml | 6 ++-- libraries/audio/src/AudioHRTF.h | 2 +- libraries/networking/src/NodeList.cpp | 4 ++- .../src/UsersScriptingInterface.h | 3 +- 9 files changed, 41 insertions(+), 32 deletions(-) diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp index fddd2d641e..07639867eb 100644 --- a/assignment-client/src/audio/AudioMixer.cpp +++ b/assignment-client/src/audio/AudioMixer.cpp @@ -28,6 +28,7 @@ #include #include +#include "AudioHelpers.h" #include "AudioRingBuffer.h" #include "AudioMixerClientData.h" #include "AvatarAudioStream.h" @@ -188,7 +189,6 @@ void AudioMixer::handleNodeKilled(SharedNodePointer killedNode) { auto clientData = dynamic_cast(node->getLinkedData()); if (clientData) { QUuid killedUUID = killedNode->getUUID(); - clientData->removePerAvatarGain(killedUUID); clientData->removeHRTFsForNode(killedUUID); } }); @@ -246,11 +246,14 @@ void AudioMixer::handleNodeIgnoreRequestPacket(QSharedPointer p void AudioMixer::handlePerAvatarGainSetDataPacket(QSharedPointer packet, SharedNodePointer sendingNode) { auto clientData = dynamic_cast(sendingNode->getLinkedData()); if (clientData) { + QUuid listeningNodeUUID = sendingNode->getUUID(); // parse the UUID from the packet - QUuid ignoredUUID = QUuid::fromRfc4122(packet->readWithoutCopy(NUM_BYTES_RFC4122_UUID)); - float gain; - packet->readPrimitive(&gain); - clientData->setPerAvatarGain(ignoredUUID, gain); + QUuid audioSourceUUID = QUuid::fromRfc4122(packet->readWithoutCopy(NUM_BYTES_RFC4122_UUID)); + uint8_t packedGain; + packet->readPrimitive(&packedGain); + float gain = unpackFloatGainFromByte(packedGain); + clientData->hrtfForStream(audioSourceUUID, QUuid()).setGainAdjustment(gain); + qDebug() << "Setting gain adjustment for hrtf[" << listeningNodeUUID << "][" << audioSourceUUID << "] to " << gain; } } diff --git a/assignment-client/src/audio/AudioMixerClientData.h b/assignment-client/src/audio/AudioMixerClientData.h index d7fbfe5112..e637fd0409 100644 --- a/assignment-client/src/audio/AudioMixerClientData.h +++ b/assignment-client/src/audio/AudioMixerClientData.h @@ -95,10 +95,6 @@ public: bool getRequestsDomainListData() { return _requestsDomainListData; } void setRequestsDomainListData(bool requesting) { _requestsDomainListData = requesting; } - float getPerAvatarGain(const QUuid& avatarID) { return (_perAvatarGain.count(avatarID) ? _perAvatarGain.at(avatarID) : 1.0f); } - void setPerAvatarGain(const QUuid& avatarID, float gain) { _perAvatarGain[avatarID] = gain; } - void removePerAvatarGain(const QUuid& avatarID) { _perAvatarGain.erase(avatarID); } - signals: void injectorStreamFinished(const QUuid& streamIdentifier); @@ -129,8 +125,6 @@ private: bool _shouldMuteClient { false }; bool _requestsDomainListData { false }; - - std::unordered_map _perAvatarGain; }; #endif // hifi_AudioMixerClientData_h diff --git a/assignment-client/src/audio/AudioMixerSlave.cpp b/assignment-client/src/audio/AudioMixerSlave.cpp index d502239341..28d3358eb5 100644 --- a/assignment-client/src/audio/AudioMixerSlave.cpp +++ b/assignment-client/src/audio/AudioMixerSlave.cpp @@ -252,13 +252,12 @@ bool AudioMixerSlave::prepareMix(const SharedNodePointer& node) { // Enumerate the audio streams attached to the otherNode auto streamsCopy = otherData->getAudioStreams(); - float thisAvatarGain = nodeData->getPerAvatarGain(otherNode->getUUID()); for (auto& streamPair : streamsCopy) { auto otherNodeStream = streamPair.second; bool isSelfWithEcho = ((*otherNode == *node) && (otherNodeStream->shouldLoopbackForNode())); // Add all audio streams that should be added to the mix if (isSelfWithEcho || (!isSelfWithEcho && !insideIgnoreRadius)) { - addStreamToMix(*nodeData, otherNode->getUUID(), *nodeAudioStream, *otherNodeStream, thisAvatarGain); + addStreamToMix(*nodeData, otherNode->getUUID(), *nodeAudioStream, *otherNodeStream); } } } @@ -279,7 +278,7 @@ bool AudioMixerSlave::prepareMix(const SharedNodePointer& node) { } void AudioMixerSlave::addStreamToMix(AudioMixerClientData& listenerNodeData, const QUuid& sourceNodeID, - const AvatarAudioStream& listeningNodeStream, const PositionalAudioStream& streamToAdd, float perAvatarGain) { + const AvatarAudioStream& listeningNodeStream, const PositionalAudioStream& streamToAdd) { // to reduce artifacts we calculate the gain and azimuth for every source for this listener // even if we are not going to end up mixing in this source @@ -296,7 +295,7 @@ void AudioMixerSlave::addStreamToMix(AudioMixerClientData& listenerNodeData, con float distance = glm::max(glm::length(relativePosition), EPSILON); // figure out the gain for this source at the listener - float gain = gainForSource(listeningNodeStream, streamToAdd, relativePosition, isEcho) * perAvatarGain; + float gain = gainForSource(listeningNodeStream, streamToAdd, relativePosition, isEcho); // figure out the azimuth to this source at the listener float azimuth = isEcho ? 0.0f : azimuthForSource(listeningNodeStream, listeningNodeStream, relativePosition); diff --git a/assignment-client/src/audio/AudioMixerSlave.h b/assignment-client/src/audio/AudioMixerSlave.h index 89aa70f99a..c4aabfbb4a 100644 --- a/assignment-client/src/audio/AudioMixerSlave.h +++ b/assignment-client/src/audio/AudioMixerSlave.h @@ -43,7 +43,7 @@ private: bool prepareMix(const SharedNodePointer& node); // add a stream to the mix void addStreamToMix(AudioMixerClientData& listenerData, const QUuid& streamerID, - const AvatarAudioStream& listenerStream, const PositionalAudioStream& streamer, float perAvatarGain); + const AvatarAudioStream& listenerStream, const PositionalAudioStream& streamer); float gainForSource(const AvatarAudioStream& listener, const PositionalAudioStream& streamer, const glm::vec3& relativePosition, bool isEcho); diff --git a/interface/resources/qml/hifi/NameCard.qml b/interface/resources/qml/hifi/NameCard.qml index 4b8ee6cbc3..3e8ee355c8 100644 --- a/interface/resources/qml/hifi/NameCard.qml +++ b/interface/resources/qml/hifi/NameCard.qml @@ -155,10 +155,10 @@ Row { visible: !isMyCard width: parent.width height: 18 - value: pal.gain[uuid] ? pal.gain[uuid] : 1.0 - minimumValue: 0.0 - maximumValue: 1.5 - stepSize: 0.1 + value: pal.gainSliderValueDB[uuid] ? pal.gainSliderValueDB[uuid] : 0.0 + minimumValue: -60.0 + maximumValue: 20.0 + stepSize: 2 updateValueWhileDragging: false onValueChanged: updateGainFromQML(uuid, value) style: SliderStyle { @@ -167,6 +167,12 @@ Row { implicitWidth: gainSlider.width implicitHeight: 4 radius: 2 + MouseArea { + anchors.fill: parent + onDoubleClicked: { + gainSlider.value = 0.0 + } + } } handle: Rectangle { anchors.centerIn: parent @@ -178,12 +184,14 @@ Row { } } - function updateGainFromQML(avatarUuid, gainValue) { - pal.gain[avatarUuid] = gainValue; - var data = { - sessionId: avatarUuid, - gain: (Math.pow(20, gainValue) - 1) / (20 - 1) - }; - pal.sendToScript({method: 'updateGain', params: data}); + function updateGainFromQML(avatarUuid, sliderValue) { + if (pal.gainSliderValueDB[avatarUuid] !== sliderValue) { + pal.gainSliderValueDB[avatarUuid] = sliderValue; + var data = { + sessionId: avatarUuid, + gain: sliderValue + }; + pal.sendToScript({method: 'updateGain', params: data}); + } } } diff --git a/interface/resources/qml/hifi/Pal.qml b/interface/resources/qml/hifi/Pal.qml index 039ab78baf..c74d8757f7 100644 --- a/interface/resources/qml/hifi/Pal.qml +++ b/interface/resources/qml/hifi/Pal.qml @@ -32,7 +32,9 @@ Rectangle { property var ignored: ({}); // Keep a local list of ignored avatars & their data. Necessary because HashMap is slow to respond after ignoring. property var userModelData: [] // This simple list is essentially a mirror of the userModel listModel without all the extra complexities. property bool iAmAdmin: false - property var gain: ({}); // Keep a local list of per-avatar gain. Far faster than keeping this data on the server. + // Keep a local list of per-avatar gainSliderValueDBs. Far faster than fetching this data from the server. + // NOTE: if another script modifies the per-avatar gain, this value won't be accurate! + property var gainSliderValueDB: ({}); // This is the container for the PAL Rectangle { @@ -497,7 +499,7 @@ Rectangle { break; case 'clearLocalQMLData': ignored = {}; - gain = {}; + gainSliderValueDB = {}; break; default: console.log('Unrecognized message:', JSON.stringify(message)); diff --git a/libraries/audio/src/AudioHRTF.h b/libraries/audio/src/AudioHRTF.h index c9d053bec4..6a17a2d3cc 100644 --- a/libraries/audio/src/AudioHRTF.h +++ b/libraries/audio/src/AudioHRTF.h @@ -45,7 +45,7 @@ public: void renderSilent(int16_t* input, float* output, int index, float azimuth, float distance, float gain, int numFrames); // - // HRTF local gain adjustment + // HRTF local gain adjustment in amplitude (1.0 == unity) // void setGainAdjustment(float gain) { _gainAdjust = HRTF_GAIN * gain; }; diff --git a/libraries/networking/src/NodeList.cpp b/libraries/networking/src/NodeList.cpp index 00f13dff3d..98a563c4e5 100644 --- a/libraries/networking/src/NodeList.cpp +++ b/libraries/networking/src/NodeList.cpp @@ -26,6 +26,7 @@ #include "AccountManager.h" #include "AddressManager.h" #include "Assignment.h" +#include "AudioHelpers.h" #include "HifiSockAddr.h" #include "FingerprintUtils.h" @@ -961,7 +962,8 @@ void NodeList::setAvatarGain(const QUuid& nodeID, float gain) { // write the node ID to the packet setAvatarGainPacket->write(nodeID.toRfc4122()); - setAvatarGainPacket->writePrimitive((gain < 5.0f ? gain : 5.0f)); + // We need to convert the gain in dB (from the script) to an amplitude before packing it. + setAvatarGainPacket->writePrimitive(packFloatGainToByte(fastExp2f(gain / 6.0206f))); qCDebug(networking) << "Sending Set Avatar Gain packet UUID: " << uuidStringWithoutCurlyBraces(nodeID) << "Gain:" << gain; diff --git a/libraries/script-engine/src/UsersScriptingInterface.h b/libraries/script-engine/src/UsersScriptingInterface.h index d2d77a6796..0b6b7855b5 100644 --- a/libraries/script-engine/src/UsersScriptingInterface.h +++ b/libraries/script-engine/src/UsersScriptingInterface.h @@ -63,9 +63,10 @@ public slots: /**jsdoc * Sets an avatar's gain for you and you only. + * Units are Decibels (dB) * @function Users.setAvatarGain * @param {nodeID} nodeID The node or session ID of the user whose gain you want to modify. - * @param {float} gain The gain of the avatar you'd like to set. + * @param {float} gain The gain of the avatar you'd like to set. Units are dB. */ void setAvatarGain(const QUuid& nodeID, float gain); From a9226e303c666e72bdb3b1b67b7b601cbf4b4025 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Thu, 12 Jan 2017 11:18:43 -0800 Subject: [PATCH 027/109] Pull out visual changess & move them to other PR --- interface/resources/qml/controls-uit/CheckBox.qml | 1 + interface/resources/qml/hifi/LetterboxMessage.qml | 6 +++--- interface/resources/qml/hifi/NameCard.qml | 8 ++++---- interface/resources/qml/hifi/Pal.qml | 1 - 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/interface/resources/qml/controls-uit/CheckBox.qml b/interface/resources/qml/controls-uit/CheckBox.qml index 09a0e04148..d3cbb87e5b 100644 --- a/interface/resources/qml/controls-uit/CheckBox.qml +++ b/interface/resources/qml/controls-uit/CheckBox.qml @@ -16,6 +16,7 @@ import "../styles-uit" Original.CheckBox { id: checkBox + HifiConstants { id: hifi } property int colorScheme: hifi.colorSchemes.light readonly property bool isLightColorScheme: colorScheme == hifi.colorSchemes.light diff --git a/interface/resources/qml/hifi/LetterboxMessage.qml b/interface/resources/qml/hifi/LetterboxMessage.qml index c8adcdcb74..290cff6634 100644 --- a/interface/resources/qml/hifi/LetterboxMessage.qml +++ b/interface/resources/qml/hifi/LetterboxMessage.qml @@ -15,7 +15,7 @@ import "../styles-uit" Item { property alias text: popupText.text - property real popupRadius: hifi.dimensions.borderRadius + property real radius: hifi.dimensions.borderRadius visible: false id: letterbox anchors.fill: parent @@ -23,13 +23,13 @@ Item { anchors.fill: parent color: "black" opacity: 0.5 - radius: popupRadius + radius: radius } Rectangle { width: Math.max(parent.width * 0.75, 400) height: popupText.contentHeight*1.5 anchors.centerIn: parent - radius: popupRadius + radius: radius color: "white" FiraSansSemiBold { id: popupText diff --git a/interface/resources/qml/hifi/NameCard.qml b/interface/resources/qml/hifi/NameCard.qml index 3e8ee355c8..7892e0755e 100644 --- a/interface/resources/qml/hifi/NameCard.qml +++ b/interface/resources/qml/hifi/NameCard.qml @@ -9,6 +9,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +import Hifi 1.0 as Hifi import QtQuick 2.5 import QtQuick.Controls 1.4 import QtQuick.Controls.Styles 1.4 @@ -135,10 +136,9 @@ Row { start: Qt.point(0, 0) end: Qt.point(parent.width, 0) gradient: Gradient { - GradientStop { position: 0.0; color: "#2c8e72" } - GradientStop { position: 0.9; color: "#1fc6a6" } - GradientStop { position: 0.91; color: "#ea4c5f" } - GradientStop { position: 1.0; color: "#ea4c5f" } + GradientStop { position: 0.05; color: "#00CFEF" } + GradientStop { position: 0.5; color: "#9450A5" } + GradientStop { position: 0.95; color: "#EA4C5F" } } } } diff --git a/interface/resources/qml/hifi/Pal.qml b/interface/resources/qml/hifi/Pal.qml index c74d8757f7..fe0c25d803 100644 --- a/interface/resources/qml/hifi/Pal.qml +++ b/interface/resources/qml/hifi/Pal.qml @@ -451,7 +451,6 @@ Rectangle { if (selected) { table.selection.clear(); // for now, no multi-select table.selection.select(userIndex); - table.positionViewAtRow(userIndex, ListView.Visible); } else { table.selection.deselect(userIndex); } From a69a10b8e4dd687c107d7732661ba3d0734b865b Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Fri, 13 Jan 2017 13:15:01 -0800 Subject: [PATCH 028/109] Cleanup after rebase --- interface/resources/qml/controls-uit/CheckBox.qml | 1 - interface/resources/qml/hifi/LetterboxMessage.qml | 6 +++--- interface/resources/qml/hifi/NameCard.qml | 8 ++++---- interface/resources/qml/hifi/Pal.qml | 1 + 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/interface/resources/qml/controls-uit/CheckBox.qml b/interface/resources/qml/controls-uit/CheckBox.qml index d3cbb87e5b..09a0e04148 100644 --- a/interface/resources/qml/controls-uit/CheckBox.qml +++ b/interface/resources/qml/controls-uit/CheckBox.qml @@ -16,7 +16,6 @@ import "../styles-uit" Original.CheckBox { id: checkBox - HifiConstants { id: hifi } property int colorScheme: hifi.colorSchemes.light readonly property bool isLightColorScheme: colorScheme == hifi.colorSchemes.light diff --git a/interface/resources/qml/hifi/LetterboxMessage.qml b/interface/resources/qml/hifi/LetterboxMessage.qml index 290cff6634..c8adcdcb74 100644 --- a/interface/resources/qml/hifi/LetterboxMessage.qml +++ b/interface/resources/qml/hifi/LetterboxMessage.qml @@ -15,7 +15,7 @@ import "../styles-uit" Item { property alias text: popupText.text - property real radius: hifi.dimensions.borderRadius + property real popupRadius: hifi.dimensions.borderRadius visible: false id: letterbox anchors.fill: parent @@ -23,13 +23,13 @@ Item { anchors.fill: parent color: "black" opacity: 0.5 - radius: radius + radius: popupRadius } Rectangle { width: Math.max(parent.width * 0.75, 400) height: popupText.contentHeight*1.5 anchors.centerIn: parent - radius: radius + radius: popupRadius color: "white" FiraSansSemiBold { id: popupText diff --git a/interface/resources/qml/hifi/NameCard.qml b/interface/resources/qml/hifi/NameCard.qml index 7892e0755e..3e8ee355c8 100644 --- a/interface/resources/qml/hifi/NameCard.qml +++ b/interface/resources/qml/hifi/NameCard.qml @@ -9,7 +9,6 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -import Hifi 1.0 as Hifi import QtQuick 2.5 import QtQuick.Controls 1.4 import QtQuick.Controls.Styles 1.4 @@ -136,9 +135,10 @@ Row { start: Qt.point(0, 0) end: Qt.point(parent.width, 0) gradient: Gradient { - GradientStop { position: 0.05; color: "#00CFEF" } - GradientStop { position: 0.5; color: "#9450A5" } - GradientStop { position: 0.95; color: "#EA4C5F" } + GradientStop { position: 0.0; color: "#2c8e72" } + GradientStop { position: 0.9; color: "#1fc6a6" } + GradientStop { position: 0.91; color: "#ea4c5f" } + GradientStop { position: 1.0; color: "#ea4c5f" } } } } diff --git a/interface/resources/qml/hifi/Pal.qml b/interface/resources/qml/hifi/Pal.qml index fe0c25d803..c74d8757f7 100644 --- a/interface/resources/qml/hifi/Pal.qml +++ b/interface/resources/qml/hifi/Pal.qml @@ -451,6 +451,7 @@ Rectangle { if (selected) { table.selection.clear(); // for now, no multi-select table.selection.select(userIndex); + table.positionViewAtRow(userIndex, ListView.Visible); } else { table.selection.deselect(userIndex); } From 8343769352f8a94d941ce15535aab88caa914257 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Fri, 13 Jan 2017 15:25:07 -0800 Subject: [PATCH 029/109] NameCard improvements & user feedback --- interface/resources/qml/hifi/NameCard.qml | 41 +++++++++++++---------- interface/resources/qml/hifi/Pal.qml | 2 +- 2 files changed, 25 insertions(+), 18 deletions(-) diff --git a/interface/resources/qml/hifi/NameCard.qml b/interface/resources/qml/hifi/NameCard.qml index 3e8ee355c8..b5bd0a88f8 100644 --- a/interface/resources/qml/hifi/NameCard.qml +++ b/interface/resources/qml/hifi/NameCard.qml @@ -20,16 +20,13 @@ Row { // Spacing spacing: 10 // Anchors - anchors.top: parent.top anchors { - topMargin: (parent.height - contentHeight)/2 - bottomMargin: (parent.height - contentHeight)/2 + verticalCenter: parent.verticalCenter leftMargin: 10 rightMargin: 10 } // Properties - property int contentHeight: 70 property string uuid: "" property string displayName: "" property string userName: "" @@ -42,7 +39,7 @@ Row { Column { id: avatarImage // Size - height: contentHeight + height: parent.height width: height Image { id: userImage @@ -56,9 +53,8 @@ Row { Column { id: textContainer // Size - width: parent.width - /*avatarImage.width - */parent.anchors.leftMargin - parent.anchors.rightMargin - parent.spacing - height: contentHeight - + width: parent.width - /*avatarImage.width - parent.spacing - */parent.anchors.leftMargin - parent.anchors.rightMargin + anchors.verticalCenter: parent.verticalCenter // DisplayName Text FiraSansSemiBold { id: displayNameText @@ -94,7 +90,7 @@ Row { // Spacer Item { - height: 4 + height: 3 width: parent.width } @@ -146,7 +142,7 @@ Row { // Per-Avatar Gain Slider Spacer Item { width: parent.width - height: 4 + height: 3 visible: !isMyCard } // Per-Avatar Gain Slider @@ -159,20 +155,31 @@ Row { minimumValue: -60.0 maximumValue: 20.0 stepSize: 2 - updateValueWhileDragging: false + updateValueWhileDragging: true onValueChanged: updateGainFromQML(uuid, value) + MouseArea { + anchors.fill: parent + onWheel: { + // Do nothing. + } + onDoubleClicked: { + gainSlider.value = 0.0 + } + onPressed: { + // Pass through to Slider + mouse.accepted = false + } + onReleased: { + // Pass through to Slider + mouse.accepted = false + } + } style: SliderStyle { groove: Rectangle { color: "#dbdbdb" implicitWidth: gainSlider.width implicitHeight: 4 radius: 2 - MouseArea { - anchors.fill: parent - onDoubleClicked: { - gainSlider.value = 0.0 - } - } } handle: Rectangle { anchors.centerIn: parent diff --git a/interface/resources/qml/hifi/Pal.qml b/interface/resources/qml/hifi/Pal.qml index c74d8757f7..0b093ed430 100644 --- a/interface/resources/qml/hifi/Pal.qml +++ b/interface/resources/qml/hifi/Pal.qml @@ -25,7 +25,7 @@ Rectangle { color: "#E3E3E3" // Properties property int myCardHeight: 90 - property int rowHeight: 90 + property int rowHeight: 80 property int actionButtonWidth: 75 property int nameCardWidth: palContainer.width - actionButtonWidth*(iAmAdmin ? 4 : 2) - 4 - hifi.dimensions.scrollbarBackgroundWidth property var myData: ({displayName: "", userName: "", audioLevel: 0.0}) // valid dummy until set From a89868dcad7cf23c27396d861e6bdcedf3d9678f Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Fri, 13 Jan 2017 15:41:17 -0800 Subject: [PATCH 030/109] Make sliders appear only when row is selected --- interface/resources/qml/hifi/NameCard.qml | 5 +++-- interface/resources/qml/hifi/Pal.qml | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/interface/resources/qml/hifi/NameCard.qml b/interface/resources/qml/hifi/NameCard.qml index b5bd0a88f8..10afee2e6a 100644 --- a/interface/resources/qml/hifi/NameCard.qml +++ b/interface/resources/qml/hifi/NameCard.qml @@ -34,6 +34,7 @@ Row { property int usernameTextHeight: 12 property real audioLevel: 0.0 property bool isMyCard: false + property bool selected: false /* User image commented out for now - will probably be re-introduced later. Column { @@ -143,12 +144,12 @@ Row { Item { width: parent.width height: 3 - visible: !isMyCard + visible: !isMyCard && selected } // Per-Avatar Gain Slider Slider { id: gainSlider - visible: !isMyCard + visible: !isMyCard && selected width: parent.width height: 18 value: pal.gainSliderValueDB[uuid] ? pal.gainSliderValueDB[uuid] : 0.0 diff --git a/interface/resources/qml/hifi/Pal.qml b/interface/resources/qml/hifi/Pal.qml index 0b093ed430..052c953316 100644 --- a/interface/resources/qml/hifi/Pal.qml +++ b/interface/resources/qml/hifi/Pal.qml @@ -25,7 +25,7 @@ Rectangle { color: "#E3E3E3" // Properties property int myCardHeight: 90 - property int rowHeight: 80 + property int rowHeight: 70 property int actionButtonWidth: 75 property int nameCardWidth: palContainer.width - actionButtonWidth*(iAmAdmin ? 4 : 2) - 4 - hifi.dimensions.scrollbarBackgroundWidth property var myData: ({displayName: "", userName: "", audioLevel: 0.0}) // valid dummy until set @@ -190,7 +190,7 @@ Rectangle { // This Rectangle refers to each Row in the table. rowDelegate: Rectangle { // The only way I know to specify a row height. // Size - height: rowHeight + height: rowHeight + (styleData.selected ? 20 : 0) color: styleData.selected ? hifi.colors.orangeHighlight : styleData.alternate ? hifi.colors.tableRowLightEven : hifi.colors.tableRowLightOdd @@ -211,6 +211,7 @@ Rectangle { audioLevel: model && model.audioLevel visible: !isCheckBox && !isButton uuid: model && model.sessionId + selected: styleData.selected // Size width: nameCardWidth height: parent.height From a5343d9aa9104e1c9117ef26d1cc1e9224e24c4d Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Fri, 13 Jan 2017 17:20:29 -0800 Subject: [PATCH 031/109] Final tweaks before PR! --- interface/resources/qml/hifi/NameCard.qml | 73 ++++++++++++++++------- interface/resources/qml/hifi/Pal.qml | 2 +- 2 files changed, 54 insertions(+), 21 deletions(-) diff --git a/interface/resources/qml/hifi/NameCard.qml b/interface/resources/qml/hifi/NameCard.qml index 10afee2e6a..c6daf53b9a 100644 --- a/interface/resources/qml/hifi/NameCard.qml +++ b/interface/resources/qml/hifi/NameCard.qml @@ -15,10 +15,8 @@ import QtQuick.Controls.Styles 1.4 import QtGraphicalEffects 1.0 import "../styles-uit" -Row { +Item { id: thisNameCard - // Spacing - spacing: 10 // Anchors anchors { verticalCenter: parent.verticalCenter @@ -51,10 +49,11 @@ Row { } } */ - Column { + Item { id: textContainer // Size width: parent.width - /*avatarImage.width - parent.spacing - */parent.anchors.leftMargin - parent.anchors.rightMargin + height: childrenRect.height anchors.verticalCenter: parent.verticalCenter // DisplayName Text FiraSansSemiBold { @@ -64,6 +63,8 @@ Row { elide: Text.ElideRight // Size width: parent.width + // Anchors + anchors.top: parent.top // Text Size size: thisNameCard.displayTextHeight // Text Positioning @@ -81,6 +82,8 @@ Row { visible: thisNameCard.displayName // Size width: parent.width + // Anchors + anchors.top: displayNameText.bottom // Text Size size: thisNameCard.usernameTextHeight // Text Positioning @@ -91,25 +94,56 @@ Row { // Spacer Item { - height: 3 + id: spacer + height: 4 width: parent.width + // Anchors + anchors.top: userNameText.bottom } // VU Meter - Rectangle { // CHANGEME to the appropriate type! + Rectangle { id: nameCardVUMeter // Size - width: parent.width + width: ((gainSlider.value - gainSlider.minimumValue)/(gainSlider.maximumValue - gainSlider.minimumValue)) * parent.width height: 8 + // Anchors + anchors.top: spacer.bottom // Style radius: 4 + color: "#c5c5c5" + // Rectangle for the zero-gain point on the VU meter + Rectangle { + id: vuMeterZeroGain + visible: gainSlider.visible + // Size + width: 4 + height: 18 + // Style + color: hifi.colors.darkGray + // Anchors + anchors.verticalCenter: parent.verticalCenter + anchors.left: parent.left + anchors.leftMargin: (-gainSlider.minimumValue)/(gainSlider.maximumValue - gainSlider.minimumValue) * gainSlider.width - 4 + } + // Rectangle for the VU meter line + Rectangle { + id: vuMeterLine + width: gainSlider.width + visible: gainSlider.visible + // Style + color: vuMeterBase.color + radius: nameCardVUMeter.radius + height: nameCardVUMeter.height / 2 + anchors.verticalCenter: nameCardVUMeter.verticalCenter + } // Rectangle for the VU meter base Rectangle { id: vuMeterBase // Anchors anchors.fill: parent // Style - color: "#c5c5c5" + color: parent.color radius: parent.radius } // Rectangle for the VU meter audio level @@ -118,7 +152,7 @@ Row { // Size width: (thisNameCard.audioLevel) * parent.width // Style - color: "#c5c5c5" + color: parent.color radius: parent.radius // Anchors anchors.bottom: parent.bottom @@ -140,22 +174,20 @@ Row { } } - // Per-Avatar Gain Slider Spacer - Item { - width: parent.width - height: 3 - visible: !isMyCard && selected - } // Per-Avatar Gain Slider Slider { id: gainSlider - visible: !isMyCard && selected + // Size width: parent.width - height: 18 + height: 14 + // Anchors + anchors.verticalCenter: nameCardVUMeter.verticalCenter + // Properties + visible: !isMyCard && selected value: pal.gainSliderValueDB[uuid] ? pal.gainSliderValueDB[uuid] : 0.0 minimumValue: -60.0 maximumValue: 20.0 - stepSize: 2 + stepSize: 5 updateValueWhileDragging: true onValueChanged: updateGainFromQML(uuid, value) MouseArea { @@ -177,16 +209,17 @@ Row { } style: SliderStyle { groove: Rectangle { - color: "#dbdbdb" + color: "#c5c5c5" implicitWidth: gainSlider.width implicitHeight: 4 radius: 2 + opacity: 0 } handle: Rectangle { anchors.centerIn: parent color: (control.pressed || control.hovered) ? "#00b4ef" : "#8F8F8F" implicitWidth: 10 - implicitHeight: 18 + implicitHeight: 16 } } } diff --git a/interface/resources/qml/hifi/Pal.qml b/interface/resources/qml/hifi/Pal.qml index 052c953316..3438baa217 100644 --- a/interface/resources/qml/hifi/Pal.qml +++ b/interface/resources/qml/hifi/Pal.qml @@ -190,7 +190,7 @@ Rectangle { // This Rectangle refers to each Row in the table. rowDelegate: Rectangle { // The only way I know to specify a row height. // Size - height: rowHeight + (styleData.selected ? 20 : 0) + height: rowHeight color: styleData.selected ? hifi.colors.orangeHighlight : styleData.alternate ? hifi.colors.tableRowLightEven : hifi.colors.tableRowLightOdd From baed3bc3b3493ad9c75a3b808378bd2437751ad2 Mon Sep 17 00:00:00 2001 From: David Kelly Date: Sat, 14 Jan 2017 12:57:00 -0700 Subject: [PATCH 032/109] Manage audio interval timer in PAL Now, it is only running when PAL is up. Was running (but doing nothing before. Somewhat more efficient this way. --- scripts/system/pal.js | 121 ++++++++++++++++++++++-------------------- 1 file changed, 64 insertions(+), 57 deletions(-) diff --git a/scripts/system/pal.js b/scripts/system/pal.js index 48f44570fd..f65fa10d88 100644 --- a/scripts/system/pal.js +++ b/scripts/system/pal.js @@ -448,50 +448,6 @@ triggerMapping.from(Controller.Standard.RTClick).peek().to(makeClickHandler(Cont triggerMapping.from(Controller.Standard.LTClick).peek().to(makeClickHandler(Controller.Standard.LeftHand)); triggerPressMapping.from(Controller.Standard.RT).peek().to(makePressHandler(Controller.Standard.RightHand)); triggerPressMapping.from(Controller.Standard.LT).peek().to(makePressHandler(Controller.Standard.LeftHand)); -// -// Manage the connection between the button and the window. -// -var toolBar = Toolbars.getToolbar("com.highfidelity.interface.toolbar.system"); -var buttonName = "pal"; -var button = toolBar.addButton({ - objectName: buttonName, - imageURL: Script.resolvePath("assets/images/tools/people.svg"), - visible: true, - hoverState: 2, - defaultState: 1, - buttonState: 1, - alpha: 0.9 -}); -var isWired = false; -function off() { - if (isWired) { // It is not ok to disconnect these twice, hence guard. - Script.update.disconnect(updateOverlays); - Controller.mousePressEvent.disconnect(handleMouseEvent); - Controller.mouseMoveEvent.disconnect(handleMouseMoveEvent); - isWired = false; - } - triggerMapping.disable(); // It's ok if we disable twice. - triggerPressMapping.disable(); // see above - removeOverlays(); - Users.requestsDomainListData = false; -} -function onClicked() { - if (!pal.visible) { - Users.requestsDomainListData = true; - populateUserList(); - pal.raise(); - isWired = true; - Script.update.connect(updateOverlays); - Controller.mousePressEvent.connect(handleMouseEvent); - Controller.mouseMoveEvent.connect(handleMouseMoveEvent); - triggerMapping.enable(); - triggerPressMapping.enable(); - } else { - off(); - } - pal.setVisible(!pal.visible); -} - // // Message from other scripts, such as edit.js // @@ -523,6 +479,7 @@ var LOUDNESS_SCALE = 2.8 / 5.0; var LOG2 = Math.log(2.0); var AUDIO_LEVEL_UPDATE_INTERVAL_MS = 100; // 10hz for now (change this and change the AVERAGING_RATIO too) var myData = {}; // we're not includied in ExtendedOverlay.get. +var audioInterval; function getAudioLevel(id) { // the VU meter should work similarly to the one in AvatarInputs: log scale, exponentially averaged @@ -555,21 +512,71 @@ function getAudioLevel(id) { return audioLevel; } +function createAudioInterval() { + // we will update the audioLevels periodically + // TODO: tune for efficiency - expecially with large numbers of avatars + return Script.setInterval(function () { + if (pal.visible) { + var param = {}; + AvatarList.getAvatarIdentifiers().forEach(function (id) { + var level = getAudioLevel(id); + // qml didn't like an object with null/empty string for a key, so... + var userId = id || 0; + param[userId] = level; + }); + pal.sendToQml({method: 'updateAudioLevel', params: param}); + } + }, AUDIO_LEVEL_UPDATE_INTERVAL_MS); +} -// we will update the audioLevels periodically -// TODO: tune for efficiency - expecially with large numbers of avatars -Script.setInterval(function () { - if (pal.visible) { - var param = {}; - AvatarList.getAvatarIdentifiers().forEach(function (id) { - var level = getAudioLevel(id); - // qml didn't like an object with null/empty string for a key, so... - var userId = id || 0; - param[userId] = level; - }); - pal.sendToQml({method: 'updateAudioLevel', params: param}); +// +// Manage the connection between the button and the window. +// +var toolBar = Toolbars.getToolbar("com.highfidelity.interface.toolbar.system"); +var buttonName = "pal"; +var button = toolBar.addButton({ + objectName: buttonName, + imageURL: Script.resolvePath("assets/images/tools/people.svg"), + visible: true, + hoverState: 2, + defaultState: 1, + buttonState: 1, + alpha: 0.9 +}); +var isWired = false; +function off() { + if (isWired) { // It is not ok to disconnect these twice, hence guard. + Script.update.disconnect(updateOverlays); + Controller.mousePressEvent.disconnect(handleMouseEvent); + Controller.mouseMoveEvent.disconnect(handleMouseMoveEvent); + isWired = false; } -}, AUDIO_LEVEL_UPDATE_INTERVAL_MS); + triggerMapping.disable(); // It's ok if we disable twice. + triggerPressMapping.disable(); // see above + removeOverlays(); + Users.requestsDomainListData = false; + if (audioInterval) { + Script.clearInterval(audioInterval); + } +} +function onClicked() { + if (!pal.visible) { + Users.requestsDomainListData = true; + populateUserList(); + pal.raise(); + isWired = true; + Script.update.connect(updateOverlays); + Controller.mousePressEvent.connect(handleMouseEvent); + Controller.mouseMoveEvent.connect(handleMouseMoveEvent); + triggerMapping.enable(); + triggerPressMapping.enable(); + createAudioInterval(); + } else { + off(); + } + pal.setVisible(!pal.visible); +} + // // Button state. // From 050a7e87d24acd499601abee1b98b9220f9dd7b0 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Sun, 15 Jan 2017 17:04:51 -0800 Subject: [PATCH 033/109] load entity models that don't yet need to be rendered but are close enough to keep physics from being enabled. --- .../entities-renderer/src/RenderableModelEntityItem.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index b901db38e7..55c766c267 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -646,6 +646,12 @@ bool RenderableModelEntityItem::isReadyToComputeShape() { // the model is still being downloaded. return false; } else if (type >= SHAPE_TYPE_SIMPLE_HULL && type <= SHAPE_TYPE_STATIC_MESH) { + if (!_model) { + EntityTreePointer tree = getTree(); + if (tree) { + QMetaObject::invokeMethod(tree.get(), "callLoader", Qt::QueuedConnection, Q_ARG(EntityItemID, getID())); + } + } return (_model && _model->isLoaded()); } return true; From bd67715cd2ff44c8f16c6d7966e37bb637559222 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 16 Jan 2017 08:54:56 -0800 Subject: [PATCH 034/109] don't early-exit from the loop that checks for loaded collision hulls because the check is what causes the hulls to be constructed --- interface/src/Application.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index b056a8e378..28f4d54594 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -5294,15 +5294,17 @@ bool Application::nearbyEntitiesAreReadyForPhysics() { if (_nearbyEntitiesStabilityCount >= MINIMUM_NEARBY_ENTITIES_STABILITY_COUNT) { // We've seen the same number of nearby entities for several stats packets in a row. assume we've got all // the local entities. + bool result = true; foreach (EntityItemPointer entity, entities) { if (entity->shouldBePhysical() && !entity->isReadyToComputeShape()) { static QString repeatedMessage = LogHandler::getInstance().addRepeatedMessageRegex("Physics disabled until entity loads: .*"); qCDebug(interfaceapp) << "Physics disabled until entity loads: " << entity->getID() << entity->getName(); - return false; + // don't break here because we want all the relevant entities to start their downloads + result = false; } } - return true; + return result; } return false; } From 67ce5040ec66a03d3db0041bd9a811048f0582cb Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Mon, 16 Jan 2017 10:09:47 -0800 Subject: [PATCH 035/109] Fix bug in FBX parse changes --- 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 5272969e6b..42922ce226 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -972,7 +972,7 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS static const QVariant EMISSIVE = QByteArray("Emissive"); static const QVariant AMBIENT_FACTOR = QByteArray("AmbientFactor"); static const QVariant SHININESS = QByteArray("Shininess"); - static const QVariant OPACITY = QByteArray("Shininess"); + static const QVariant OPACITY = QByteArray("Opacity"); static const QVariant MAYA_USE_NORMAL_MAP = QByteArray("Maya|use_normal_map"); static const QVariant MAYA_BASE_COLOR = QByteArray("Maya|base_color"); static const QVariant MAYA_USE_COLOR_MAP = QByteArray("Maya|use_color_map"); From 117e302b2af7400b5694bb3fd7898ad2d08d1ab6 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Thu, 12 Jan 2017 15:40:37 -0800 Subject: [PATCH 036/109] First pass --- interface/resources/qml/hifi/NameCard.qml | 72 ++++++++++++++++++++++- 1 file changed, 71 insertions(+), 1 deletion(-) diff --git a/interface/resources/qml/hifi/NameCard.qml b/interface/resources/qml/hifi/NameCard.qml index c6daf53b9a..c1b93b92ca 100644 --- a/interface/resources/qml/hifi/NameCard.qml +++ b/interface/resources/qml/hifi/NameCard.qml @@ -55,12 +55,82 @@ Item { width: parent.width - /*avatarImage.width - parent.spacing - */parent.anchors.leftMargin - parent.anchors.rightMargin height: childrenRect.height anchors.verticalCenter: parent.verticalCenter - // DisplayName Text + + // DisplayName field for my card + Rectangle { + id: myDisplayName + visible: isMyCard + color: "#C5C5C5" + anchors.left: parent.left + anchors.leftMargin: -10 + width: parent.width + 70 + height: 35 + TextInput { + id: myDisplayNameText + // Properties + text: thisNameCard.displayName + autoScroll: false + maximumLength: 64 + width: parent.width + height: parent.height + wrapMode: TextInput.Wrap + // Anchors + anchors.fill: parent + anchors.leftMargin: 10 + // Text Positioning + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignLeft + // Style + color: hifi.colors.darkGray + FontLoader { id: firaSansSemiBold; source: "../../fonts/FiraSans-SemiBold.ttf"; } + font.family: firaSansSemiBold.name + font.pointSize: thisNameCard.displayTextHeight + // Signals + onEditingFinished: { + console.log("New displayName: ", text) + } + } + MouseArea { + anchors.fill: parent + acceptedButtons: Qt.LeftButton + hoverEnabled: true + propagateComposedEvents: true + onClicked: { + myDisplayNameText.selectAll(); + myDisplayNameText.focus = true; + } + onEntered: myDisplayName.color = hifi.colors.lightGrayText + onExited: myDisplayName.color = "#C5C5C5" + } + // Edit pencil glyph + HiFiGlyphs { + text: hifi.glyphs.edit + // Size + size: thisNameCard.displayTextHeight + // Anchors + anchors.right: parent.right + anchors.rightMargin: size/2 + anchors.verticalCenter: parent.verticalCenter + // Style + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + color: hifi.colors.baseGray + } + } + // Spacer for DisplayName for my card + Rectangle { + width: myDisplayName.width + height: 5 + visible: isMyCard + opacity: 0 + } + // DisplayName Text for others' cards FiraSansSemiBold { id: displayNameText // Properties text: thisNameCard.displayName elide: Text.ElideRight + visible: !isMyCard // Size width: parent.width // Anchors From 6e4a4da92ab2ec2920ac135493d935b73d62f5f3 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Thu, 12 Jan 2017 16:40:47 -0800 Subject: [PATCH 037/109] Checkpoint --- interface/resources/qml/hifi/NameCard.qml | 18 ++++++++++-------- scripts/system/pal.js | 6 ++++++ 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/interface/resources/qml/hifi/NameCard.qml b/interface/resources/qml/hifi/NameCard.qml index c1b93b92ca..906e81a08d 100644 --- a/interface/resources/qml/hifi/NameCard.qml +++ b/interface/resources/qml/hifi/NameCard.qml @@ -69,17 +69,15 @@ Item { id: myDisplayNameText // Properties text: thisNameCard.displayName - autoScroll: false maximumLength: 64 - width: parent.width - height: parent.height - wrapMode: TextInput.Wrap + clip: true // Anchors anchors.fill: parent anchors.leftMargin: 10 + anchors.rightMargin: editGlyph.implicitWidth + editGlyph.anchors.rightMargin // Text Positioning - verticalAlignment: Text.AlignVCenter - horizontalAlignment: Text.AlignLeft + verticalAlignment: TextInput.AlignVCenter + horizontalAlignment: TextInput.AlignLeft // Style color: hifi.colors.darkGray FontLoader { id: firaSansSemiBold; source: "../../fonts/FiraSans-SemiBold.ttf"; } @@ -87,15 +85,18 @@ Item { font.pointSize: thisNameCard.displayTextHeight // Signals onEditingFinished: { - console.log("New displayName: ", text) + pal.sendToScript({method: 'displayNameUpdate', params: text}) } } MouseArea { anchors.fill: parent acceptedButtons: Qt.LeftButton hoverEnabled: true - propagateComposedEvents: true onClicked: { + myDisplayNameText.focus ? myDisplayNameText.cursorPosition = myDisplayNameText.positionAt(mouseX, mouseY, TextInput.CursorOnCharacter) : myDisplayNameText.selectAll(); + myDisplayNameText.focus = true; + } + onDoubleClicked: { myDisplayNameText.selectAll(); myDisplayNameText.focus = true; } @@ -104,6 +105,7 @@ Item { } // Edit pencil glyph HiFiGlyphs { + id: editGlyph text: hifi.glyphs.edit // Size size: thisNameCard.displayTextHeight diff --git a/scripts/system/pal.js b/scripts/system/pal.js index 6b3fd00c3b..c6ac1056de 100644 --- a/scripts/system/pal.js +++ b/scripts/system/pal.js @@ -237,6 +237,12 @@ pal.fromQml.connect(function (message) { // messages are {method, params}, like data = message.params; Users.setAvatarGain(data['sessionId'], data['gain']); break; + case 'displayNameUpdate': + if (MyAvatar.displayName != message.params) { + MyAvatar.displayName = message.params; + } + break; + default: default: print('Unrecognized message from Pal.qml:', JSON.stringify(message)); } From 2f176f50fae4ff06d9ab2e92b92acdf81b63e38c Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Fri, 13 Jan 2017 12:56:59 -0800 Subject: [PATCH 038/109] Another checkpoint --- interface/resources/fonts/hifi-glyphs.ttf | Bin 24580 -> 24772 bytes interface/resources/qml/hifi/NameCard.qml | 13 +++++++++---- interface/resources/qml/hifi/Pal.qml | 13 +++++++++++-- .../qml/styles-uit/HifiConstants.qml | 3 ++- libraries/avatars/src/AvatarData.cpp | 1 + 5 files changed, 23 insertions(+), 7 deletions(-) diff --git a/interface/resources/fonts/hifi-glyphs.ttf b/interface/resources/fonts/hifi-glyphs.ttf index 09aefffdfef007aa94f3cbdfd02a61612c0ee9e7..c139a196d0f70536b980d7cbbad81c416d237e0b 100644 GIT binary patch delta 684 zcmX|;O-Phs5Xb-XywCf*>%Q(6$S&oZx@9eHv}!B62_*z(6xCL4;;xpy=;jLh1+Jh( z>Y;-|hw`~Z*sVi>V9P>>2uv~|=n@!tkdO}2rArZH1jc^Qz|8y(GsFC6?!BS)HJSwh zuwf1guJ-gqFHcTK2Z7;d0F3k9=X$UN0SwOrXo~cQqI-(GC(KMSZ&foL?DFh{Mi9}57s0fdst#H`8NXa}G=0L{sC ze(Jz(-5NkI0PLo+Bj%l^hur{Q2e6Yir*il}w*Wd-S}0?t6W_wG`heJPfQq^7MBZ}$ z>nbp?4DgR-aRiK-7nX+{r+-0bDB0$dt&K`%TN{gXhO)AYZjwzd(Oz({T%rJFH_9_W z%}~l@fRm*p4i;2~&Ft9ZI{QhP;!DLr@-6?&{QlySTIiisFo+nf&y? zVy4k%5F@MqxvQY7cFh^-AZZBA>D4M7hyAs@)MEA3Qw?Wa+-3QK`kR zDeVZ#SUkYmgK|DTDgVa3l$8ww+l*4hKLhU+yPg;w&!5O8G9%+DK&}ooP)2?ks@MPj HXlvsiT0xGP delta 494 zcmX|+Pe>GT6vsbrW_Gp4>>>-+5?hq(sSUw@UQl~C_V^eb z5-UJuYW%_I)Aqmn;9G#$Qab63obA}G1$Kd^bUK-Hj2p5CQUhE~yVJ9^&udmemVlq* z6T{BKQ0f90%fNTnna%Q520>c9hcb?v91A~s5B*Uulbx8F{?oob3GpK^J{IK!=6Zj0 zCeF7VU}|`^t*m@$YF7S*+?0&EB?D5W7UVF3Cs6EQF^^n9FH#Ug{GJ$D=U6E9HrGeVw}Msz}qtW z2(VRVKLP%$+x9h8i1$dNs<5w1qiVKi)Ehe_8MSHe7GB@~tgGD6C2`f_&_(mq(#oFz Dg()->killNodeWithUUID(getSessionUUID()); qCDebug(avatars) << "Changing display name for avatar to" << displayName; } From 32e330320e0e50d84d8d7bb1a03d0831732fcb17 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Mon, 16 Jan 2017 11:26:57 -0800 Subject: [PATCH 039/109] Cleanup after rebase --- interface/resources/qml/hifi/NameCard.qml | 17 ++++++++++++----- interface/resources/qml/hifi/Pal.qml | 8 -------- libraries/avatars/src/AvatarData.cpp | 2 +- scripts/system/pal.js | 1 - 4 files changed, 13 insertions(+), 15 deletions(-) diff --git a/interface/resources/qml/hifi/NameCard.qml b/interface/resources/qml/hifi/NameCard.qml index 250dad7f7f..b389891255 100644 --- a/interface/resources/qml/hifi/NameCard.qml +++ b/interface/resources/qml/hifi/NameCard.qml @@ -60,11 +60,15 @@ Item { Rectangle { id: myDisplayName visible: isMyCard - color: "#C5C5C5" - anchors.left: parent.left - anchors.leftMargin: -10 + // Size width: parent.width + 70 height: 35 + // Anchors + anchors.top: parent.top + anchors.left: parent.left + anchors.leftMargin: -10 + // Style + color: "#C5C5C5" border.color: hifi.colors.blueHighlight border.width: 0 TextInput { @@ -126,7 +130,10 @@ Item { } // Spacer for DisplayName for my card Rectangle { + id: myDisplayNameSpacer width: myDisplayName.width + // Anchors + anchors.top: myDisplayName.bottom height: 5 visible: isMyCard opacity: 0 @@ -160,7 +167,7 @@ Item { // Size width: parent.width // Anchors - anchors.top: displayNameText.bottom + anchors.top: isMyCard ? myDisplayNameSpacer.bottom : displayNameText.bottom // Text Size size: thisNameCard.usernameTextHeight // Text Positioning @@ -182,7 +189,7 @@ Item { Rectangle { id: nameCardVUMeter // Size - width: ((gainSlider.value - gainSlider.minimumValue)/(gainSlider.maximumValue - gainSlider.minimumValue)) * parent.width + width: isMyCard ? gainSlider.width : ((gainSlider.value - gainSlider.minimumValue)/(gainSlider.maximumValue - gainSlider.minimumValue)) * parent.width height: 8 // Anchors anchors.top: spacer.bottom diff --git a/interface/resources/qml/hifi/Pal.qml b/interface/resources/qml/hifi/Pal.qml index 8fa8fe8514..68ad3f811c 100644 --- a/interface/resources/qml/hifi/Pal.qml +++ b/interface/resources/qml/hifi/Pal.qml @@ -346,11 +346,6 @@ Rectangle { visible: iAmAdmin color: hifi.colors.lightGrayText } - function letterbox(message) { - letterboxMessage.text = message; - letterboxMessage.visible = true - - } // This Rectangle refers to the [?] popup button next to "NAMES" Rectangle { color: hifi.colors.tableBackgroundLight @@ -411,9 +406,6 @@ Rectangle { onExited: adminHelpText.color = hifi.colors.redHighlight } } - LetterboxMessage { - id: letterboxMessage - } } function findSessionIndex(sessionId, optionalData) { // no findIndex in .qml diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 0381363ef3..0350e73d2e 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -1094,7 +1094,7 @@ void AvatarData::setSkeletonModelURL(const QUrl& skeletonModelURL) { void AvatarData::setDisplayName(const QString& displayName) { _displayName = displayName; - DependencyManager::get()->killNodeWithUUID(getSessionUUID()); + DependencyManager::get()->getDomainHandler().softReset(); qCDebug(avatars) << "Changing display name for avatar to" << displayName; } diff --git a/scripts/system/pal.js b/scripts/system/pal.js index c6ac1056de..6fd386b00f 100644 --- a/scripts/system/pal.js +++ b/scripts/system/pal.js @@ -242,7 +242,6 @@ pal.fromQml.connect(function (message) { // messages are {method, params}, like MyAvatar.displayName = message.params; } break; - default: default: print('Unrecognized message from Pal.qml:', JSON.stringify(message)); } From e22c1ac30e35b376bfe28b0309536f9e1c5de911 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Mon, 16 Jan 2017 11:48:51 -0800 Subject: [PATCH 040/109] Fix the bug --- .../src/DomainServerSettingsManager.cpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/domain-server/src/DomainServerSettingsManager.cpp b/domain-server/src/DomainServerSettingsManager.cpp index c8895839ff..c6bcc5af34 100644 --- a/domain-server/src/DomainServerSettingsManager.cpp +++ b/domain-server/src/DomainServerSettingsManager.cpp @@ -780,12 +780,12 @@ void DomainServerSettingsManager::processNodeKickRequestPacket(QSharedPointer message, SharedNodePointer sendingNode) { - // Before we do any processing on this packet, make sure it comes from a node that is allowed to kick (is an admin) - if (sendingNode->getCanKick()) { - // From the packet, pull the UUID we're identifying - QUuid nodeUUID = QUuid::fromRfc4122(message->readWithoutCopy(NUM_BYTES_RFC4122_UUID)); - - if (!nodeUUID.isNull()) { + // From the packet, pull the UUID we're identifying + QUuid nodeUUID = QUuid::fromRfc4122(message->readWithoutCopy(NUM_BYTES_RFC4122_UUID)); + if (!nodeUUID.isNull()) { + // Before we do any processing on this packet, make sure it comes from a node that is allowed to kick (is an admin) + // OR from a node whose UUID matches the one in the packet + if (sendingNode->getCanKick() || nodeUUID == sendingNode->getUUID()) { // First, make sure we actually have a node with this UUID auto limitedNodeList = DependencyManager::get(); auto matchingNode = limitedNodeList->nodeWithUUID(nodeUUID); @@ -813,12 +813,12 @@ void DomainServerSettingsManager::processUsernameFromIDRequestPacket(QSharedPoin qWarning() << "Node username request received for unknown node. Refusing to process."; } } else { - qWarning() << "Node username request received for invalid node ID. Refusing to process."; + qWarning() << "Refusing to process a username request packet from node" << uuidStringWithoutCurlyBraces(sendingNode->getUUID()) + << ". Either node doesn't have kick permissions or is requesting a username not from their UUID."; } } else { - qWarning() << "Refusing to process a username request packet from node" << uuidStringWithoutCurlyBraces(sendingNode->getUUID()) - << "that does not have kick permissions."; + qWarning() << "Node username request received for invalid node ID. Refusing to process."; } } From 3b56f46206a3267c8016c9959c90958f343f5fb3 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Mon, 16 Jan 2017 13:18:24 -0800 Subject: [PATCH 041/109] Style changes --- interface/resources/qml/hifi/NameCard.qml | 34 ++++++++++++++--------- libraries/avatars/src/AvatarData.cpp | 3 +- 2 files changed, 23 insertions(+), 14 deletions(-) diff --git a/interface/resources/qml/hifi/NameCard.qml b/interface/resources/qml/hifi/NameCard.qml index b389891255..367ef3b25b 100644 --- a/interface/resources/qml/hifi/NameCard.qml +++ b/interface/resources/qml/hifi/NameCard.qml @@ -28,7 +28,7 @@ Item { property string uuid: "" property string displayName: "" property string userName: "" - property int displayTextHeight: 18 + property real displayNameTextPixelSize: 18 property int usernameTextHeight: 12 property real audioLevel: 0.0 property bool isMyCard: false @@ -68,7 +68,7 @@ Item { anchors.left: parent.left anchors.leftMargin: -10 // Style - color: "#C5C5C5" + color: hifi.colors.textFieldLightBackground border.color: hifi.colors.blueHighlight border.width: 0 TextInput { @@ -77,23 +77,30 @@ Item { text: thisNameCard.displayName maximumLength: 64 clip: true + // Size + width: parent.width + height: parent.height // Anchors - anchors.fill: parent + anchors.verticalCenter: parent.verticalCenter + anchors.left: parent.left anchors.leftMargin: 10 anchors.rightMargin: editGlyph.implicitWidth + editGlyph.anchors.rightMargin - // Text Positioning - verticalAlignment: TextInput.AlignVCenter - horizontalAlignment: TextInput.AlignLeft // Style color: hifi.colors.darkGray FontLoader { id: firaSansSemiBold; source: "../../fonts/FiraSans-SemiBold.ttf"; } font.family: firaSansSemiBold.name - font.pointSize: thisNameCard.displayTextHeight + font.pixelSize: displayNameTextPixelSize + selectionColor: hifi.colors.blueHighlight + selectedTextColor: "black" + // Text Positioning + verticalAlignment: TextInput.AlignVCenter + horizontalAlignment: TextInput.AlignLeft // Signals onEditingFinished: { pal.sendToScript({method: 'displayNameUpdate', params: text}) focus = false myDisplayName.border.width = 0 + color = hifi.colors.darkGray } } MouseArea { @@ -101,23 +108,24 @@ Item { acceptedButtons: Qt.LeftButton hoverEnabled: true onClicked: { - myDisplayName.border.width = 3 + myDisplayName.border.width = 1 myDisplayNameText.focus ? myDisplayNameText.cursorPosition = myDisplayNameText.positionAt(mouseX, mouseY, TextInput.CursorOnCharacter) : myDisplayNameText.selectAll(); myDisplayNameText.focus = true + myDisplayNameText.color = "black" } onDoubleClicked: { myDisplayNameText.selectAll(); myDisplayNameText.focus = true; } onEntered: myDisplayName.color = hifi.colors.lightGrayText - onExited: myDisplayName.color = "#C5C5C5" + onExited: myDisplayName.color = hifi.colors.textFieldLightBackground } // Edit pencil glyph HiFiGlyphs { id: editGlyph text: hifi.glyphs.editPencil - // Size - size: thisNameCard.displayTextHeight*1.5 + // Text Size + size: displayNameTextPixelSize*1.5 // Anchors anchors.right: parent.right anchors.rightMargin: 5 @@ -150,7 +158,7 @@ Item { // Anchors anchors.top: parent.top // Text Size - size: thisNameCard.displayTextHeight + size: displayNameTextPixelSize // Text Positioning verticalAlignment: Text.AlignVCenter // Style @@ -189,7 +197,7 @@ Item { Rectangle { id: nameCardVUMeter // Size - width: isMyCard ? gainSlider.width : ((gainSlider.value - gainSlider.minimumValue)/(gainSlider.maximumValue - gainSlider.minimumValue)) * parent.width + width: isMyCard ? myDisplayName.width - 20 : ((gainSlider.value - gainSlider.minimumValue)/(gainSlider.maximumValue - gainSlider.minimumValue)) * parent.width height: 8 // Anchors anchors.top: spacer.bottom diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 0350e73d2e..3d09be0679 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -1047,7 +1047,7 @@ bool AvatarData::processAvatarIdentity(const Identity& identity) { } if (identity.displayName != _displayName) { - setDisplayName(identity.displayName); + _displayName = identity.displayName; hasIdentityChanged = true; } maybeUpdateSessionDisplayNameFromTransport(identity.sessionDisplayName); @@ -1094,6 +1094,7 @@ void AvatarData::setSkeletonModelURL(const QUrl& skeletonModelURL) { void AvatarData::setDisplayName(const QString& displayName) { _displayName = displayName; + DependencyManager::get()->getDomainHandler().softReset(); qCDebug(avatars) << "Changing display name for avatar to" << displayName; From e98a40c30e287897fddef8be2fe20f256f7a8485 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Mon, 16 Jan 2017 14:43:30 -0800 Subject: [PATCH 042/109] Does this work? --- assignment-client/src/avatars/AvatarMixer.cpp | 12 +++++++++--- .../src/avatars/AvatarMixerClientData.h | 6 +++--- libraries/avatars/src/AvatarData.cpp | 3 ++- 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/assignment-client/src/avatars/AvatarMixer.cpp b/assignment-client/src/avatars/AvatarMixer.cpp index 11cbd73970..8ac2f3922b 100644 --- a/assignment-client/src/avatars/AvatarMixer.cpp +++ b/assignment-client/src/avatars/AvatarMixer.cpp @@ -262,8 +262,13 @@ void AvatarMixer::broadcastAvatarData() { // setup a PacketList for the avatarPackets auto avatarPacketList = NLPacketList::create(PacketType::BulkAvatarData); - if (avatar.getSessionDisplayName().isEmpty() && // We haven't set it yet... - nodeData->getReceivedIdentity()) { // ... but we have processed identity (with possible displayName). + if (nodeData->getAvatarSessionDisplayNameMustChange()) { // ... but we have processed identity (with possible displayName). + const QString& existingBaseDisplayName = nodeData->getBaseDisplayName(); + // No sense guarding against very rare case of a node with no entry, as this will work without the guard and do one less lookup in the common case. + if (--_sessionDisplayNames[existingBaseDisplayName].second <= 0) { + _sessionDisplayNames.remove(existingBaseDisplayName); + } + QString baseName = avatar.getDisplayName().trimmed(); const QRegularExpression curses{ "fuck|shit|damn|cock|cunt" }; // POC. We may eventually want something much more elaborate (subscription?). baseName = baseName.replace(curses, "*"); // Replace rather than remove, so that people have a clue that the person's a jerk. @@ -280,6 +285,7 @@ void AvatarMixer::broadcastAvatarData() { highWater++; soFar.second++; // refcount nodeData->flagIdentityChange(); + nodeData->setAvatarSessionDisplayNameMustChange(false); sendIdentityPacket(nodeData, node); // Tell new node about its sessionUUID. Others will find out below. } @@ -584,7 +590,7 @@ void AvatarMixer::handleAvatarIdentityPacket(QSharedPointer mes if (avatar.processAvatarIdentity(identity)) { QMutexLocker nodeDataLocker(&nodeData->getMutex()); nodeData->flagIdentityChange(); - nodeData->setReceivedIdentity(); + nodeData->setAvatarSessionDisplayNameMustChange(); } } } diff --git a/assignment-client/src/avatars/AvatarMixerClientData.h b/assignment-client/src/avatars/AvatarMixerClientData.h index f18cfdde1b..38db2e74d2 100644 --- a/assignment-client/src/avatars/AvatarMixerClientData.h +++ b/assignment-client/src/avatars/AvatarMixerClientData.h @@ -53,8 +53,8 @@ public: HRCTime getIdentityChangeTimestamp() const { return _identityChangeTimestamp; } void flagIdentityChange() { _identityChangeTimestamp = p_high_resolution_clock::now(); } - bool getReceivedIdentity() const { return _gotIdentity; } - void setReceivedIdentity() { _gotIdentity = true; } + bool getAvatarSessionDisplayNameMustChange() const { return _avatarSessionDisplayNameMustChange; } + void setAvatarSessionDisplayNameMustChange(bool set = true) { _avatarSessionDisplayNameMustChange = set; } void setFullRateDistance(float fullRateDistance) { _fullRateDistance = fullRateDistance; } float getFullRateDistance() const { return _fullRateDistance; } @@ -112,7 +112,7 @@ private: std::unordered_set _hasReceivedFirstPacketsFrom; HRCTime _identityChangeTimestamp; - bool _gotIdentity { false }; + bool _avatarSessionDisplayNameMustChange{ false }; float _fullRateDistance = FLT_MAX; float _maxAvatarDistance = FLT_MAX; diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 3d09be0679..1fb68fce14 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -1094,8 +1094,9 @@ void AvatarData::setSkeletonModelURL(const QUrl& skeletonModelURL) { void AvatarData::setDisplayName(const QString& displayName) { _displayName = displayName; + _sessionDisplayName = ""; - DependencyManager::get()->getDomainHandler().softReset(); + sendIdentityPacket(); qCDebug(avatars) << "Changing display name for avatar to" << displayName; } From 0a3236dcc91d7ec0c0a392ed10e788094ed93e27 Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Mon, 16 Jan 2017 14:54:32 -0800 Subject: [PATCH 043/109] handControllerGrab update loop change --- scripts/system/controllers/handControllerGrab.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index 2ed09232e6..57698bd0dc 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -3082,7 +3082,7 @@ var handleHandMessages = function(channel, message, sender) { Messages.messageReceived.connect(handleHandMessages); -var TARGET_UPDATE_HZ = 50; // 50hz good enough (no change in logic) +var TARGET_UPDATE_HZ = 60; // 50hz good enough, but we're using update var BASIC_TIMER_INTERVAL_MS = 1000 / TARGET_UPDATE_HZ; var lastInterval = Date.now(); @@ -3095,7 +3095,7 @@ var updateTotalWork = 0; var UPDATE_PERFORMANCE_DEBUGGING = false; -var updateIntervalTimer = Script.setInterval(function(){ +function updateWrapper(){ intervalCount++; var thisInterval = Date.now(); @@ -3141,11 +3141,12 @@ var updateIntervalTimer = Script.setInterval(function(){ updateTotalWork = 0; } -}, BASIC_TIMER_INTERVAL_MS); +} +Script.update.connect(updateWrapper); function cleanup() { Menu.removeMenuItem("Developer", "Show Grab Sphere"); - Script.clearInterval(updateIntervalTimer); + Script.update.disconnect(updateWrapper); rightController.cleanup(); leftController.cleanup(); Controller.disableMapping(MAPPING_NAME); From fd284578073e665df4d9f1b9fd5fd0da4b772c59 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Mon, 16 Jan 2017 15:13:28 -0800 Subject: [PATCH 044/109] fix cauterization hack --- .../render-utils/src/MeshPartPayload.cpp | 19 ++++++++++++++++--- libraries/render-utils/src/MeshPartPayload.h | 5 ++++- libraries/render-utils/src/Model.cpp | 2 +- 3 files changed, 21 insertions(+), 5 deletions(-) diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp index 5c4c0890a7..e3b2527e67 100644 --- a/libraries/render-utils/src/MeshPartPayload.cpp +++ b/libraries/render-utils/src/MeshPartPayload.cpp @@ -359,8 +359,11 @@ void ModelMeshPartPayload::notifyLocationChanged() { } -void ModelMeshPartPayload::updateTransformForSkinnedMesh(const Transform& transform, const QVector& clusterMatrices) { +void ModelMeshPartPayload::updateTransformForSkinnedMesh(const Transform& transform, + const QVector& clusterMatrices, + const QVector& cauterizedClusterMatrices) { _transform = transform; + _cauterizedTransform = transform; if (clusterMatrices.size() > 0) { _worldBound = AABox(); @@ -373,6 +376,11 @@ void ModelMeshPartPayload::updateTransformForSkinnedMesh(const Transform& transf _worldBound.transform(transform); if (clusterMatrices.size() == 1) { _transform = _transform.worldTransform(Transform(clusterMatrices[0])); + if (cauterizedClusterMatrices.size() != 0) { + _cauterizedTransform = _cauterizedTransform.worldTransform(Transform(cauterizedClusterMatrices[0])); + } else { + _cauterizedTransform = _transform; + } } } } @@ -527,9 +535,14 @@ void ModelMeshPartPayload::bindTransform(gpu::Batch& batch, const ShapePipeline: } else { batch.setUniformBuffer(ShapePipeline::Slot::BUFFER::SKINNING, state.clusterBuffer); } + batch.setModelTransform(_transform); + } else { + if (canCauterize && _model->getCauterizeBones()) { + batch.setModelTransform(_cauterizedTransform); + } else { + batch.setModelTransform(_transform); + } } - - batch.setModelTransform(_transform); } void ModelMeshPartPayload::startFade() { diff --git a/libraries/render-utils/src/MeshPartPayload.h b/libraries/render-utils/src/MeshPartPayload.h index b048dc903f..b7a8cf63f0 100644 --- a/libraries/render-utils/src/MeshPartPayload.h +++ b/libraries/render-utils/src/MeshPartPayload.h @@ -85,7 +85,9 @@ public: typedef Payload::DataPointer Pointer; void notifyLocationChanged() override; - void updateTransformForSkinnedMesh(const Transform& transform, const QVector& clusterMatrices); + void updateTransformForSkinnedMesh(const Transform& transform, + const QVector& clusterMatrices, + const QVector& cauterizedClusterMatrices); // Entity fade in void startFade(); @@ -106,6 +108,7 @@ public: Model* _model; + Transform _cauterizedTransform; int _meshIndex; int _shapeID; diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 7c1a6e14d0..3a3095458c 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -238,7 +238,7 @@ void Model::updateRenderItems() { // update the model transform and bounding box for this render item. const Model::MeshState& state = data._model->_meshStates.at(data._meshIndex); - data.updateTransformForSkinnedMesh(modelTransform, state.clusterMatrices); + data.updateTransformForSkinnedMesh(modelTransform, state.clusterMatrices, state.cauterizedClusterMatrices); } } }); From e72c25736e804bbd625c5855b6d85e7b15086eff Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Mon, 16 Jan 2017 15:21:18 -0800 Subject: [PATCH 045/109] Cleanup --- assignment-client/src/avatars/AvatarMixer.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/assignment-client/src/avatars/AvatarMixer.cpp b/assignment-client/src/avatars/AvatarMixer.cpp index 8ac2f3922b..4127719b7b 100644 --- a/assignment-client/src/avatars/AvatarMixer.cpp +++ b/assignment-client/src/avatars/AvatarMixer.cpp @@ -262,9 +262,8 @@ void AvatarMixer::broadcastAvatarData() { // setup a PacketList for the avatarPackets auto avatarPacketList = NLPacketList::create(PacketType::BulkAvatarData); - if (nodeData->getAvatarSessionDisplayNameMustChange()) { // ... but we have processed identity (with possible displayName). + if (nodeData->getAvatarSessionDisplayNameMustChange()) { const QString& existingBaseDisplayName = nodeData->getBaseDisplayName(); - // No sense guarding against very rare case of a node with no entry, as this will work without the guard and do one less lookup in the common case. if (--_sessionDisplayNames[existingBaseDisplayName].second <= 0) { _sessionDisplayNames.remove(existingBaseDisplayName); } @@ -286,7 +285,7 @@ void AvatarMixer::broadcastAvatarData() { soFar.second++; // refcount nodeData->flagIdentityChange(); nodeData->setAvatarSessionDisplayNameMustChange(false); - sendIdentityPacket(nodeData, node); // Tell new node about its sessionUUID. Others will find out below. + sendIdentityPacket(nodeData, node); // Tell node whose name changed about its new session display name. Others will find out below. } // this is an AGENT we have received head data from @@ -590,7 +589,7 @@ void AvatarMixer::handleAvatarIdentityPacket(QSharedPointer mes if (avatar.processAvatarIdentity(identity)) { QMutexLocker nodeDataLocker(&nodeData->getMutex()); nodeData->flagIdentityChange(); - nodeData->setAvatarSessionDisplayNameMustChange(); + nodeData->setAvatarSessionDisplayNameMustChange(true); } } } From 1e8effdcc42d85b6326b0c4d240088e23b36cee1 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Mon, 16 Jan 2017 15:28:53 -0800 Subject: [PATCH 046/109] Packet verison bump --- libraries/networking/src/udt/PacketHeaders.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index c012ed8f67..9bbd139138 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -211,7 +211,8 @@ enum class AvatarMixerPacketVersion : PacketVersion { HandControllerJoints, HasKillAvatarReason, SessionDisplayName, - Unignore + Unignore, + ImmediateSessionDisplayNameUpdates }; enum class DomainConnectRequestVersion : PacketVersion { From a1fe908380642b73c1601f764943e5a235376def Mon Sep 17 00:00:00 2001 From: David Rowe Date: Tue, 17 Jan 2017 12:56:56 +1300 Subject: [PATCH 047/109] Remove a lingering line of old code --- scripts/system/html/js/marketplacesInject.js | 1 - 1 file changed, 1 deletion(-) diff --git a/scripts/system/html/js/marketplacesInject.js b/scripts/system/html/js/marketplacesInject.js index 0944c4113e..d5f0f4cb06 100644 --- a/scripts/system/html/js/marketplacesInject.js +++ b/scripts/system/html/js/marketplacesInject.js @@ -148,7 +148,6 @@ if (!canWriteAssets) { console.log("ERROR: Clara.io FBX: File download cancelled because no permissions to write to Asset Server"); EventBridge.emitWebEvent(WARN_USER_NO_PERMISSIONS); - event.stopPropagation(); return; } From c9fd650e70939bfe62fea6323f16d986519d999c Mon Sep 17 00:00:00 2001 From: humbletim Date: Mon, 16 Jan 2017 19:37:31 -0500 Subject: [PATCH 048/109] update to support click/far-click toggling and userData.disabled setting --- .../tutorials/entity_scripts/ambientSound.js | 330 +++++++++++++++--- 1 file changed, 282 insertions(+), 48 deletions(-) diff --git a/scripts/tutorials/entity_scripts/ambientSound.js b/scripts/tutorials/entity_scripts/ambientSound.js index 620b371400..64adcf379a 100644 --- a/scripts/tutorials/entity_scripts/ambientSound.js +++ b/scripts/tutorials/entity_scripts/ambientSound.js @@ -1,55 +1,70 @@ // ambientSound.js // // This entity script will allow you to create an ambient sound that loops when a person is within a given -// range of this entity. Great way to add one or more ambisonic soundfields to your environment. +// range of this entity. Great way to add one or more ambisonic soundfields to your environment. // -// In the userData section for the entity, add/edit three values: -// userData.soundURL should be a string giving the URL to the sound file. Defaults to 100 meters if not set. +// In the userData section for the entity, add/edit three values: +// userData.soundURL should be a string giving the URL to the sound file. Defaults to 100 meters if not set. // userData.range should be an integer for the max distance away from the entity where the sound will be audible. -// userData.volume is the max volume at which the clip should play. Defaults to 1.0 full volume) +// userData.maxVolume is the max volume at which the clip should play. Defaults to 1.0 full volume. +// userData.disabled is an optionanl boolean flag which can be used to disable the ambient sound. Defaults to false. +// +// The rotation of the entity is copied to the ambisonic field, so by rotating the entity you will rotate the +// direction in-which a certain sound comes from. // -// The rotation of the entity is copied to the ambisonic field, so by rotating the entity you will rotate the -// direction in-which a certain sound comes from. -// // Remember that the entity has to be visible to the user for the sound to play at all, so make sure the entity is -// large enough to be loaded at the range you set, particularly for large ranges. -// +// large enough to be loaded at the range you set, particularly for large ranges. +// // Copyright 2016 High Fidelity, Inc. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -(function(){ +(function(){ + var VERSION = "0.0.1"; // This sample clip and range will be used if you don't add userData to the entity (see above) var DEFAULT_RANGE = 100; var DEFAULT_URL = "http://hifi-content.s3.amazonaws.com/ken/samples/forest_ambiX.wav"; var DEFAULT_VOLUME = 1.0; var soundURL = ""; + var soundName = ""; + var startTime; + var soundOptions = { + loop: true, + localOnly: true, + //ignorePenumbra: true, + }; var range = DEFAULT_RANGE; var maxVolume = DEFAULT_VOLUME; + var disabled = false; var UPDATE_INTERVAL_MSECS = 100; var rotation; var entity; var ambientSound; var center; - var soundPlaying = false; + var soundPlaying; var checkTimer = false; var _this; - var WANT_COLOR_CHANGE = false; var COLOR_OFF = { red: 128, green: 128, blue: 128 }; var COLOR_ON = { red: 255, green: 0, blue: 0 }; - var WANT_DEBUG = false; + var WANT_DEBUG = true; function debugPrint(string) { if (WANT_DEBUG) { - print(string); + print("ambientSound | " + string); } } + var WANT_DEBUG_BROADCASTS = "ambientSound.js"; + var WANT_DEBUG_OVERLAY = false; + var LINEHEIGHT = 0.1; + // Optionally enable debug overlays using a Settings value + WANT_DEBUG_OVERLAY = WANT_DEBUG_OVERLAY || /ambientSound/.test(Settings.getValue("WANT_DEBUG_OVERLAY")); + this.updateSettings = function() { // Check user data on the entity for any changes var oldSoundURL = soundURL; @@ -58,81 +73,300 @@ var data = JSON.parse(props.userData); if (data.soundURL && !(soundURL === data.soundURL)) { soundURL = data.soundURL; - debugPrint("Read ambient sound URL: " + soundURL); - } else if (!data.soundURL) { - soundURL = DEFAULT_URL; + soundName = (soundURL||"").split("/").pop(); // just filename part + debugPrint("Read ambient sound URL: " + soundURL); } if (data.range && !(range === data.range)) { range = data.range; debugPrint("Read ambient sound range: " + range); } - if (data.volume && !(maxVolume === data.volume)) { - maxVolume = data.volume; + // Check known aliases for the "volume" setting (which allows for inplace upgrade of existing marketplace entities) + data.maxVolume = data.maxVolume || data.soundVolume || data.volume; + if (data.maxVolume && !(maxVolume === data.maxVolume)) { + maxVolume = data.maxVolume; debugPrint("Read ambient sound volume: " + maxVolume); } + if ("disabled" in data && !(disabled === data.disabled)) { + disabled = data.disabled; + debugPrint("Read ambient disabled state: " + disabled); + if (disabled) { + this.cleanup(); + debugState("disabled"); + return; + } + } + /*if ("loop" in data && !(soundOptions.loop === data.loop)) { + soundOptions.loop = data.loop; + debugPrint("Read ambient loop state: " + soundOptions.loop); + }*/ } if (!(soundURL === oldSoundURL) || (soundURL === "")) { - debugPrint("Loading ambient sound into cache"); - ambientSound = SoundCache.getSound(soundURL); + if (soundURL) { + debugState("downloading", "Loading ambient sound into cache"); + // Use prefetch to detect URL loading errors + var resource = SoundCache.prefetch(soundURL); + function onStateChanged() { + if (resource.state === Resource.State.FINISHED) { + resource.stateChanged.disconnect(onStateChanged); + ambientSound = SoundCache.getSound(soundURL); + debugState("idle"); + } else if (resource.state === Resource.State.FAILED) { + resource.stateChanged.disconnect(onStateChanged); + debugPrint("Failed to download ambient sound: " + soundURL); + debugState("error"); + } + debugPrint("onStateChanged: " + JSON.stringify({ + sound: soundName, + state: resource.state, + stateName: Object.keys(Resource.State).filter(function(key) { + return Resource.State[key] === resource.state; + }) + })); + } + resource.stateChanged.connect(onStateChanged); + onStateChanged(resource.state); + } if (soundPlaying && soundPlaying.playing) { + debugPrint("URL changed, stopping current ambient sound"); soundPlaying.stop(); soundPlaying = false; - if (WANT_COLOR_CHANGE) { - Entities.editEntity(entity, { color: COLOR_OFF }); - } - debugPrint("Restarting ambient sound"); } - } + } } - this.preload = function(entityID) { + this.clickDownOnEntity = function(entityID, mouseEvent) { + print("click"); + if (mouseEvent.isPrimaryButton) { + this._toggle("primary click"); + } + }; + + this.startFarTrigger = function() { + this._toggle("far click"); + }; + + this._toggle = function(hint) { + // Toggle between ON/OFF state, but only if not in edit mode + if (Settings.getValue("io.highfidelity.isEditting")) { + return; + } + var props = Entities.getEntityProperties(entity, [ "userData", "age", "scriptTimestamp" ]); + var data = JSON.parse(props.userData); + data.disabled = !data.disabled; + + debugPrint(hint + " -- triggering ambient sound " + (data.disabled ? "OFF" : "ON") + " (" + soundName + ")"); + var oldState = _debugState; + + if (WANT_DEBUG_BROADCASTS) { + Messages.sendMessage(WANT_DEBUG_BROADCASTS /*entity*/, JSON.stringify({ palName: MyAvatar.sessionDisplayName, soundName: soundName, hint: hint, scriptTimestamp: props.scriptTimestamp, oldState: oldState, newState: _debugState, age: props.age })); + } + + this.cleanup(); + + // Save the userData and bump scriptTimestamp, which causes all nearby listeners to apply the state change + Entities.editEntity(entity, { + userData: JSON.stringify(data), + scriptTimestamp: Math.round(props.age * 1000) + }); + //this._updateColor(data.disabled); + }; + + this._updateColor = function(disabled) { + // Update Shape or Text Entity color based on ON/OFF status + var props = Entities.getEntityProperties(entity, [ "color", "textColor" ]); + var targetColor = disabled ? COLOR_OFF : COLOR_ON; + var currentColor = props.textColor || props.color; + var newProps = props.textColor ? { textColor: targetColor } : { color: targetColor }; + + if (currentColor.red !== targetColor.red || + currentColor.green !== targetColor.green || + currentColor.blue !== targetColor.blue) { + Entities.editEntity(entity, newProps); + } + }; + + this.preload = function(entityID) { // Load the sound and range from the entity userData fields, and note the position of the entity. - debugPrint("Ambient sound preload"); + debugPrint("Ambient sound preload " + VERSION); entity = entityID; _this = this; - checkTimer = Script.setInterval(this.maybeUpdate, UPDATE_INTERVAL_MSECS); - }; + + if (WANT_DEBUG_OVERLAY) { + _createDebugOverlays(); + } + + var props = Entities.getEntityProperties(entity, [ "userData" ]); + if (props.userData) { + var data = JSON.parse(props.userData); + this._updateColor(data.disabled); + if (data.disabled) { + _this.maybeUpdate(); + return; + } + } + + checkTimer = Script.setInterval(_this.maybeUpdate, UPDATE_INTERVAL_MSECS); + }; this.maybeUpdate = function() { // Every UPDATE_INTERVAL_MSECS, update the volume of the ambient sound based on distance from my avatar _this.updateSettings(); var HYSTERESIS_FRACTION = 0.1; var props = Entities.getEntityProperties(entity, [ "position", "rotation" ]); + if (!props.position) { + // FIXME: this mysterious state has been happening while testing + // and might indicate a bug where an Entity can become unreachable without `unload` having been called.. + print("FIXME: ambientSound.js -- expected Entity unavailable!") + if (WANT_DEBUG_BROADCASTS) { + Messages.sendMessage(WANT_DEBUG_BROADCASTS /*entity*/, JSON.stringify({ palName: MyAvatar.sessionDisplayName, soundName: soundName, hint: "FIXME: maybeUpdate", oldState: _debugState })); + } + return _this.cleanup(); + } center = props.position; rotation = props.rotation; var distance = Vec3.length(Vec3.subtract(MyAvatar.position, center)); if (distance <= range) { var volume = (1.0 - distance / range) * maxVolume; - if (!soundPlaying && ambientSound.downloaded) { - soundPlaying = Audio.playSound(ambientSound, { loop: true, - localOnly: true, - orientation: rotation, - volume: volume }); - debugPrint("Starting ambient sound, volume: " + volume); - if (WANT_COLOR_CHANGE) { - Entities.editEntity(entity, { color: COLOR_ON }); - } - + soundOptions.orientation = Quat.rotation; + soundOptions.volume = volume; + if (!soundPlaying && ambientSound && ambientSound.downloaded) { + debugState("playing", "Starting ambient sound: " + soundName + " (duration: " + ambientSound.duration + ")"); + soundPlaying = Audio.playSound(ambientSound, soundOptions); } else if (soundPlaying && soundPlaying.playing) { - soundPlaying.setOptions( { volume: volume, orientation: rotation } ); + soundPlaying.setOptions(soundOptions); } - } else if (soundPlaying && soundPlaying.playing && (distance > range * HYSTERESIS_FRACTION)) { soundPlaying.stop(); soundPlaying = false; - Entities.editEntity(entity, { color: { red: 128, green: 128, blue: 128 }}); - debugPrint("Out of range, stopping ambient sound"); + debugState("idle", "Out of range, stopping ambient sound: " + soundName); + } + if (WANT_DEBUG_OVERLAY) { + updateDebugOverlay(distance); } } - this.unload = function(entityID) { - debugPrint("Ambient sound unload"); + this.unload = function(entityID) { + debugPrint("Ambient sound unload "); + if (WANT_DEBUG_BROADCASTS) { + var offset = ambientSound && (new Date - startTime)/1000 % ambientSound.duration; + Messages.sendMessage(WANT_DEBUG_BROADCASTS /*entity*/, JSON.stringify({ palName: MyAvatar.sessionDisplayName, soundName: soundName, hint: "unload", oldState: _debugState, offset: offset })); + } + if (WANT_DEBUG_OVERLAY) { + _removeDebugOverlays(); + } + this.cleanup(); + }; + + this.cleanup = function() { if (checkTimer) { Script.clearInterval(checkTimer); + checkTimer = false; } if (soundPlaying && soundPlaying.playing) { soundPlaying.stop(); + soundPlaying = false; } - }; + }; -}) + // Visual debugging overlay (to see set WANT_DEBUG_OVERLAY = true) + + var DEBUG_COLORS = { + //preload: { red: 0, green: 80, blue: 80 }, + disabled: { red: 0, green: 0, blue: 0, alpha: 0.0 }, + downloading: { red: 255, green: 255, blue: 0 }, + error: { red: 255, green: 0, blue: 0 }, + playing: { red: 0, green: 200, blue: 0 }, + idle: { red: 0, green: 100, blue: 0 } + }; + var _debugOverlay; + var _debugState = ""; + function debugState(state, message) { + if (state === "playing") { + startTime = new Date; + } + _debugState = state; + if (message) { + debugPrint(message); + } + updateDebugOverlay(); + if (WANT_DEBUG_BROADCASTS) { + // Broadcast state changes to an implicit entity channel, making multi-user scenarios easier to verify from a single console + Messages.sendMessage(WANT_DEBUG_BROADCASTS /*entity*/, JSON.stringify({ palName: MyAvatar.sessionDisplayName, soundName: soundName, state: state })); + } + } + + function updateDebugOverlay(distance) { + var props = Entities.getEntityProperties(entity, [ "name", "dimensions" ]); + if (!props.dimensions) { + return print("ambientSound.js: updateDebugOverlay -- entity no longer available " + entity); + } + var options = soundPlaying && soundPlaying.options; + if (options) { + var offset = soundPlaying.playing && ambientSound && (new Date - startTime)/1000 % ambientSound.duration; + var deg = Quat.safeEulerAngles(options.orientation); + var orientation = [ deg.x, deg.y, deg.z].map(Math.round).join(", "); + var volume = options.volume; + } + var info = { + //loudness: soundPlaying.loudness && soundPlaying.loudness.toFixed(4) || undefined, + offset: offset && ("00"+offset.toFixed(1)).substr(-4)+"s" || undefined, + orientation: orientation, + injector: soundPlaying && soundPlaying.playing && "playing", + resource: ambientSound && ambientSound.downloaded && "ready (" + ambientSound.duration.toFixed(1) + "s)", + name: props.name || undefined, + uuid: entity.split(/\W/)[1], // extracts just the first part of the UUID + sound: soundName, + volume: Math.max(0,volume||0).toFixed(2) + " / " + maxVolume.toFixed(2), + distance: (distance||0).toFixed(1) + "m / " + range.toFixed(1) + "m", + state: _debugState.toUpperCase(), + }; + + // Pretty print key/value pairs, excluding any undefined values + var outputText = Object.keys(info).filter(function(key) { + return info[key] !== undefined; + }).map(function(key) { + return key + ": " + info[key]; + }).join("\n"); + + // Calculate a local position for displaying info just above the Entity + var textSize = Overlays.textSize(_debugOverlay, outputText); + var size = { + x: textSize.width + LINEHEIGHT, + y: textSize.height + LINEHEIGHT + }; + var pos = { x: 0, y: props.dimensions.y + size.y/2, z: 0 }; + + var backgroundColor = DEBUG_COLORS[_debugState]; + var backgroundAlpha = backgroundColor ? backgroundColor.alpha : 0.6; + Overlays.editOverlay(_debugOverlay, { + visible: true, + backgroundColor: backgroundColor, + backgroundAlpha: backgroundAlpha, + text: outputText, + localPosition: pos, + size: size, + }); + } + + function _removeDebugOverlays() { + if (_debugOverlay) { + Overlays.deleteOverlay(_debugOverlay); + _debugOverlay = 0; + } + } + + function _createDebugOverlays() { + _debugOverlay = Overlays.addOverlay("text3d", { + visible: true, + lineHeight: LINEHEIGHT, + leftMargin: LINEHEIGHT/2, + topMargin: LINEHEIGHT/2, + localPosition: Vec3.ZERO, + parentID: entity, + ignoreRayIntersection: true, + isFacingAvatar: true, + textAlpha: 0.6, + //drawInFront: true, + }); + } +}) From 827aa68d3f609146a59fd7edf20c6d4a906e6e6f Mon Sep 17 00:00:00 2001 From: humbletim Date: Mon, 16 Jan 2017 19:50:25 -0500 Subject: [PATCH 049/109] cleanup pass --- .../tutorials/entity_scripts/ambientSound.js | 23 ++++++++----------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/scripts/tutorials/entity_scripts/ambientSound.js b/scripts/tutorials/entity_scripts/ambientSound.js index 64adcf379a..3b75ed6720 100644 --- a/scripts/tutorials/entity_scripts/ambientSound.js +++ b/scripts/tutorials/entity_scripts/ambientSound.js @@ -45,25 +45,25 @@ var entity; var ambientSound; var center; - var soundPlaying; + var soundPlaying = false; var checkTimer = false; var _this; var COLOR_OFF = { red: 128, green: 128, blue: 128 }; var COLOR_ON = { red: 255, green: 0, blue: 0 }; - var WANT_DEBUG = true; + var WANT_DEBUG = false; function debugPrint(string) { if (WANT_DEBUG) { print("ambientSound | " + string); } } - var WANT_DEBUG_BROADCASTS = "ambientSound.js"; var WANT_DEBUG_OVERLAY = false; var LINEHEIGHT = 0.1; // Optionally enable debug overlays using a Settings value WANT_DEBUG_OVERLAY = WANT_DEBUG_OVERLAY || /ambientSound/.test(Settings.getValue("WANT_DEBUG_OVERLAY")); + var WANT_DEBUG_BROADCASTS = WANT_DEBUG_OVERLAY && "ambientSound.js"; this.updateSettings = function() { // Check user data on the entity for any changes @@ -95,10 +95,6 @@ return; } } - /*if ("loop" in data && !(soundOptions.loop === data.loop)) { - soundOptions.loop = data.loop; - debugPrint("Read ambient loop state: " + soundOptions.loop); - }*/ } if (!(soundURL === oldSoundURL) || (soundURL === "")) { if (soundURL) { @@ -135,7 +131,6 @@ } this.clickDownOnEntity = function(entityID, mouseEvent) { - print("click"); if (mouseEvent.isPrimaryButton) { this._toggle("primary click"); } @@ -158,7 +153,7 @@ var oldState = _debugState; if (WANT_DEBUG_BROADCASTS) { - Messages.sendMessage(WANT_DEBUG_BROADCASTS /*entity*/, JSON.stringify({ palName: MyAvatar.sessionDisplayName, soundName: soundName, hint: hint, scriptTimestamp: props.scriptTimestamp, oldState: oldState, newState: _debugState, age: props.age })); + Messages.sendMessage(WANT_DEBUG_BROADCASTS, JSON.stringify({ palName: MyAvatar.sessionDisplayName, soundName: soundName, hint: hint, scriptTimestamp: props.scriptTimestamp, oldState: oldState, newState: _debugState, age: props.age })); } this.cleanup(); @@ -218,7 +213,7 @@ // and might indicate a bug where an Entity can become unreachable without `unload` having been called.. print("FIXME: ambientSound.js -- expected Entity unavailable!") if (WANT_DEBUG_BROADCASTS) { - Messages.sendMessage(WANT_DEBUG_BROADCASTS /*entity*/, JSON.stringify({ palName: MyAvatar.sessionDisplayName, soundName: soundName, hint: "FIXME: maybeUpdate", oldState: _debugState })); + Messages.sendMessage(WANT_DEBUG_BROADCASTS, JSON.stringify({ palName: MyAvatar.sessionDisplayName, soundName: soundName, hint: "FIXME: maybeUpdate", oldState: _debugState })); } return _this.cleanup(); } @@ -227,7 +222,7 @@ var distance = Vec3.length(Vec3.subtract(MyAvatar.position, center)); if (distance <= range) { var volume = (1.0 - distance / range) * maxVolume; - soundOptions.orientation = Quat.rotation; + soundOptions.orientation = rotation; soundOptions.volume = volume; if (!soundPlaying && ambientSound && ambientSound.downloaded) { debugState("playing", "Starting ambient sound: " + soundName + " (duration: " + ambientSound.duration + ")"); @@ -249,7 +244,7 @@ debugPrint("Ambient sound unload "); if (WANT_DEBUG_BROADCASTS) { var offset = ambientSound && (new Date - startTime)/1000 % ambientSound.duration; - Messages.sendMessage(WANT_DEBUG_BROADCASTS /*entity*/, JSON.stringify({ palName: MyAvatar.sessionDisplayName, soundName: soundName, hint: "unload", oldState: _debugState, offset: offset })); + Messages.sendMessage(WANT_DEBUG_BROADCASTS, JSON.stringify({ palName: MyAvatar.sessionDisplayName, soundName: soundName, hint: "unload", oldState: _debugState, offset: offset })); } if (WANT_DEBUG_OVERLAY) { _removeDebugOverlays(); @@ -290,8 +285,8 @@ } updateDebugOverlay(); if (WANT_DEBUG_BROADCASTS) { - // Broadcast state changes to an implicit entity channel, making multi-user scenarios easier to verify from a single console - Messages.sendMessage(WANT_DEBUG_BROADCASTS /*entity*/, JSON.stringify({ palName: MyAvatar.sessionDisplayName, soundName: soundName, state: state })); + // Broadcast state changes to make multi-user scenarios easier to verify from a single console + Messages.sendMessage(WANT_DEBUG_BROADCASTS, JSON.stringify({ palName: MyAvatar.sessionDisplayName, soundName: soundName, state: state })); } } From 83d1c3ad01d630e764ddcd6b31b31d3aaf884ec3 Mon Sep 17 00:00:00 2001 From: samcake Date: Mon, 16 Jan 2017 17:47:58 -0800 Subject: [PATCH 050/109] Adding support for the meta rendering --- interface/src/avatar/Avatar.cpp | 11 +++- interface/src/avatar/Avatar.h | 1 + .../src/RenderableEntityItem.cpp | 28 +++++--- .../src/RenderableEntityItem.h | 8 ++- .../src/RenderableModelEntityItem.cpp | 15 ++++- .../src/RenderableModelEntityItem.h | 1 + libraries/render-utils/src/Model.cpp | 7 ++ libraries/render-utils/src/Model.h | 3 + .../render-utils/src/RenderDeferredTask.cpp | 17 ++--- .../render-utils/src/RenderForwardTask.cpp | 16 +++-- libraries/render/src/render/Item.h | 64 +++++++++++++------ .../src/render/RenderFetchCullSortTask.cpp | 22 +++++-- .../src/render/RenderFetchCullSortTask.h | 16 ++++- libraries/render/src/render/Scene.cpp | 2 +- libraries/render/src/render/Scene.h | 5 +- 15 files changed, 156 insertions(+), 60 deletions(-) diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index 81eee6b3a7..d86f82a455 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -62,7 +62,7 @@ const glm::vec3 HAND_TO_PALM_OFFSET(0.0f, 0.12f, 0.08f); namespace render { template <> const ItemKey payloadGetKey(const AvatarSharedPointer& avatar) { - return ItemKey::Builder::opaqueShape(); + return ItemKey::Builder::opaqueShape().withTypeMeta(); } template <> const Item::Bound payloadGetBound(const AvatarSharedPointer& avatar) { return static_pointer_cast(avatar)->getBounds(); @@ -74,6 +74,15 @@ namespace render { avatarPtr->render(args, qApp->getCamera()->getPosition()); } } + template <> uint32_t metaFetchMetaSubItems(const AvatarSharedPointer& avatar, ItemIDs& subItems) { + auto avatarPtr = static_pointer_cast(avatar); + if (avatarPtr->getSkeletonModel()) { + auto metaSubItems = avatarPtr->getSkeletonModel()->fetchRenderItemIDs(); + subItems.insert(subItems.end(), metaSubItems.begin(), metaSubItems.end()); + return metaSubItems.size(); + } + return 0; + } } static uint64_t timeProcessingJoints = 0; diff --git a/interface/src/avatar/Avatar.h b/interface/src/avatar/Avatar.h index d03a8e9a54..d370402865 100644 --- a/interface/src/avatar/Avatar.h +++ b/interface/src/avatar/Avatar.h @@ -32,6 +32,7 @@ namespace render { template <> const ItemKey payloadGetKey(const AvatarSharedPointer& avatar); template <> const Item::Bound payloadGetBound(const AvatarSharedPointer& avatar); template <> void payloadRender(const AvatarSharedPointer& avatar, RenderArgs* args); + template <> uint32_t metaFetchMetaSubItems(const AvatarSharedPointer& avatar, ItemIDs& subItems); } static const float SCALING_RATIO = .05f; diff --git a/libraries/entities-renderer/src/RenderableEntityItem.cpp b/libraries/entities-renderer/src/RenderableEntityItem.cpp index 359b050803..be9ef08c0b 100644 --- a/libraries/entities-renderer/src/RenderableEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableEntityItem.cpp @@ -15,21 +15,21 @@ namespace render { template <> const ItemKey payloadGetKey(const RenderableEntityItemProxy::Pointer& payload) { - if (payload && payload->entity) { - if (payload->entity->getType() == EntityTypes::Light) { - return ItemKey::Builder::light(); + if (payload && payload->_entity) { + if (payload->_entity->getType() == EntityTypes::Light) { + return ItemKey::Builder::light().withTypeMeta(); } - if (payload && payload->entity->isTransparent()) { - return ItemKey::Builder::transparentShape(); + if (payload && payload->_entity->isTransparent()) { + return ItemKey::Builder::transparentShape().withTypeMeta(); } } - return ItemKey::Builder::opaqueShape(); + return ItemKey::Builder::opaqueShape().withTypeMeta(); } template <> const Item::Bound payloadGetBound(const RenderableEntityItemProxy::Pointer& payload) { - if (payload && payload->entity) { + if (payload && payload->_entity) { bool success; - auto result = payload->entity->getAABox(success); + auto result = payload->_entity->getAABox(success); if (!success) { return render::Item::Bound(); } @@ -39,11 +39,19 @@ namespace render { } template <> void payloadRender(const RenderableEntityItemProxy::Pointer& payload, RenderArgs* args) { if (args) { - if (payload && payload->entity && payload->entity->getVisible()) { - payload->entity->render(args); + if (payload && payload->_entity && payload->_entity->getVisible()) { + payload->_entity->render(args); } } } + template <> uint32_t metaFetchMetaSubItems(const RenderableEntityItemProxy::Pointer& payload, ItemIDs& subItems) { + auto metaID = payload->_metaID; + if (Item::isValidID(metaID)) { + subItems.emplace_back(metaID); + return 1; + } + return 0; + } } void makeEntityItemStatusGetters(EntityItemPointer entity, render::Item::Status::Getters& statusGetters) { diff --git a/libraries/entities-renderer/src/RenderableEntityItem.h b/libraries/entities-renderer/src/RenderableEntityItem.h index ec65bab1c8..98271ddbbb 100644 --- a/libraries/entities-renderer/src/RenderableEntityItem.h +++ b/libraries/entities-renderer/src/RenderableEntityItem.h @@ -36,17 +36,19 @@ void makeEntityItemStatusGetters(EntityItemPointer entity, render::Item::Status: class RenderableEntityItemProxy { public: - RenderableEntityItemProxy(EntityItemPointer entity) : entity(entity) { } + RenderableEntityItemProxy(EntityItemPointer entity, render::ItemID metaID) : _entity(entity), _metaID(metaID) { } typedef render::Payload Payload; typedef Payload::DataPointer Pointer; - EntityItemPointer entity; + EntityItemPointer _entity; + render::ItemID _metaID; }; namespace render { template <> const ItemKey payloadGetKey(const RenderableEntityItemProxy::Pointer& payload); template <> const Item::Bound payloadGetBound(const RenderableEntityItemProxy::Pointer& payload); template <> void payloadRender(const RenderableEntityItemProxy::Pointer& payload, RenderArgs* args); + template <> uint32_t metaFetchMetaSubItems(const RenderableEntityItemProxy::Pointer& payload, ItemIDs& subItems); } // Mixin class for implementing basic single item rendering @@ -55,7 +57,7 @@ public: bool addToScene(EntityItemPointer self, std::shared_ptr scene, render::PendingChanges& pendingChanges) { _myItem = scene->allocateID(); - auto renderData = std::make_shared(self); + auto renderData = std::make_shared(self, _myItem); auto renderPayload = std::make_shared(renderData); render::Item::Status::Getters statusGetters; diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index b901db38e7..cf1bcafe7c 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -194,7 +194,7 @@ public: namespace render { template <> const ItemKey payloadGetKey(const RenderableModelEntityItemMeta::Pointer& payload) { - return ItemKey::Builder::opaqueShape(); + return ItemKey::Builder::opaqueShape().withTypeMeta(); } template <> const Item::Bound payloadGetBound(const RenderableModelEntityItemMeta::Pointer& payload) { @@ -216,6 +216,15 @@ namespace render { } } } + template <> uint32_t metaFetchMetaSubItems(const RenderableModelEntityItemMeta::Pointer& payload, ItemIDs& subItems) { + auto modelEntity = std::static_pointer_cast(payload->entity); + if (modelEntity->hasModel()) { + auto metaSubItems = modelEntity->getModelNotSafe()->fetchRenderItemIDs(); + subItems.insert(subItems.end(), metaSubItems.begin(), metaSubItems.end()); + return (uint32_t) metaSubItems.size(); + } + return 0; + } } bool RenderableModelEntityItem::addToScene(EntityItemPointer self, std::shared_ptr scene, @@ -473,6 +482,10 @@ void RenderableModelEntityItem::render(RenderArgs* args) { } } +ModelPointer RenderableModelEntityItem::getModelNotSafe() { + return _model; +} + ModelPointer RenderableModelEntityItem::getModel(QSharedPointer renderer) { if (!renderer) { return nullptr; diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.h b/libraries/entities-renderer/src/RenderableModelEntityItem.h index 93d48c6085..bac2118326 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.h +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.h @@ -52,6 +52,7 @@ public: BoxFace& face, glm::vec3& surfaceNormal, void** intersectedObject, bool precisionPicking) const override; ModelPointer getModel(QSharedPointer renderer); + ModelPointer getModelNotSafe(); virtual bool needsToCallUpdate() const override; virtual void update(const quint64& now) override; diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 7c1a6e14d0..7c615c5414 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -673,6 +673,7 @@ bool Model::addToScene(std::shared_ptr scene, hasTransparent = hasTransparent || renderItem.get()->getShapeKey().isTranslucent(); verticesCount += renderItem.get()->getVerticesCount(); _modelMeshRenderItems.insert(item, renderPayload); + _modelMeshRenderItemIDs.emplace_back(item); } somethingAdded = !_modelMeshRenderItems.empty(); @@ -695,6 +696,7 @@ void Model::removeFromScene(std::shared_ptr scene, render::Pendin foreach (auto item, _modelMeshRenderItems.keys()) { pendingChanges.removeItem(item); } + _modelMeshRenderItemIDs.clear(); _modelMeshRenderItems.clear(); _modelMeshRenderItemsSet.clear(); @@ -1301,6 +1303,10 @@ AABox Model::getRenderableMeshBound() const { } } +const render::ItemIDs& Model::fetchRenderItemIDs() const { + return _modelMeshRenderItemIDs; +} + void Model::createRenderItemSet() { if (_collisionGeometry) { if (_collisionRenderItemsSet.empty()) { @@ -1479,6 +1485,7 @@ void ModelBlender::noteRequiresBlend(ModelPointer model) { { Lock lock(_mutex); + _modelsRequiringBlends.insert(model); } } diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index 55806b27e1..a11d6d511e 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -105,6 +105,7 @@ public: void setRenderItemsNeedUpdate() { _renderItemsNeedUpdate = true; } bool getRenderItemsNeedUpdate() { return _renderItemsNeedUpdate; } AABox getRenderableMeshBound() const; + const render::ItemIDs& fetchRenderItemIDs() const; bool maybeStartBlender(); @@ -389,6 +390,8 @@ protected: QSet> _modelMeshRenderItemsSet; QMap _modelMeshRenderItems; + render::ItemIDs _modelMeshRenderItemIDs; + bool _addedToScene { false }; // has been added to scene bool _needsFixupInScene { true }; // needs to be removed/re-added to scene bool _needsReload { true }; diff --git a/libraries/render-utils/src/RenderDeferredTask.cpp b/libraries/render-utils/src/RenderDeferredTask.cpp index f8cd112007..8547c5adb0 100644 --- a/libraries/render-utils/src/RenderDeferredTask.cpp +++ b/libraries/render-utils/src/RenderDeferredTask.cpp @@ -53,14 +53,15 @@ RenderDeferredTask::RenderDeferredTask(RenderFetchCullSortTask::Output items) { ShapePlumberPointer shapePlumber = std::make_shared(); initDeferredPipelines(*shapePlumber); - // Extract opaques / transparents / lights / overlays - const auto opaques = items[0]; - const auto transparents = items[1]; - const auto lights = items[2]; - const auto overlayOpaques = items[3]; - const auto overlayTransparents = items[4]; - const auto background = items[5]; - const auto spatialSelection = items[6]; + // Extract opaques / transparents / lights / metas / overlays / background + const auto opaques = items[RenderFetchCullSortTask::OPAQUE_SHAPE]; + const auto transparents = items[RenderFetchCullSortTask::TRANSPARENT_SHAPE]; + const auto lights = items[RenderFetchCullSortTask::LIGHT]; + const auto metas = items[RenderFetchCullSortTask::META]; + const auto overlayOpaques = items[RenderFetchCullSortTask::OVERLAY_OPAQUE_SHAPE]; + const auto overlayTransparents = items[RenderFetchCullSortTask::OVERLAY_TRANSPARENT_SHAPE]; + const auto background = items[RenderFetchCullSortTask::BACKGROUND]; + const auto spatialSelection = items[RenderFetchCullSortTask::SPATIAL_SELECTION]; // Prepare deferred, generate the shared Deferred Frame Transform const auto deferredFrameTransform = addJob("DeferredFrameTransform"); diff --git a/libraries/render-utils/src/RenderForwardTask.cpp b/libraries/render-utils/src/RenderForwardTask.cpp index c7a3433c96..7550d2326b 100755 --- a/libraries/render-utils/src/RenderForwardTask.cpp +++ b/libraries/render-utils/src/RenderForwardTask.cpp @@ -36,13 +36,15 @@ RenderForwardTask::RenderForwardTask(RenderFetchCullSortTask::Output items) { ShapePlumberPointer shapePlumber = std::make_shared(); initForwardPipelines(*shapePlumber); - // Extract opaques / transparents / lights / overlays - const auto opaques = items[0]; - const auto transparents = items[1]; - const auto lights = items[2]; - const auto overlayOpaques = items[3]; - const auto overlayTransparents = items[4]; - const auto background = items[5]; + // Extract opaques / transparents / lights / metas / overlays / background + const auto opaques = items[RenderFetchCullSortTask::OPAQUE_SHAPE]; + const auto transparents = items[RenderFetchCullSortTask::TRANSPARENT_SHAPE]; + const auto lights = items[RenderFetchCullSortTask::LIGHT]; + const auto metas = items[RenderFetchCullSortTask::META]; + const auto overlayOpaques = items[RenderFetchCullSortTask::OVERLAY_OPAQUE_SHAPE]; + const auto overlayTransparents = items[RenderFetchCullSortTask::OVERLAY_TRANSPARENT_SHAPE]; + const auto background = items[RenderFetchCullSortTask::BACKGROUND]; + const auto spatialSelection = items[RenderFetchCullSortTask::SPATIAL_SELECTION]; const auto framebuffer = addJob("PrepareFramebuffer"); diff --git a/libraries/render/src/render/Item.h b/libraries/render/src/render/Item.h index 9a65b48021..27bc2b790a 100644 --- a/libraries/render/src/render/Item.h +++ b/libraries/render/src/render/Item.h @@ -38,6 +38,7 @@ public: enum FlagBit { TYPE_SHAPE = 0, // Item is a Shape TYPE_LIGHT, // Item is a Light + TYPE_META, // Item is a Meta: meanning it s used to represent a higher level object, potentially represented by other render items TRANSLUCENT, // Transparent and not opaque, for some odd reason TRANSPARENCY doesn't work... VIEW_SPACE, // Transformed in view space, and not in world space DYNAMIC, // Dynamic and bound will change unlike static item @@ -72,6 +73,7 @@ public: Builder& withTypeShape() { _flags.set(TYPE_SHAPE); return (*this); } Builder& withTypeLight() { _flags.set(TYPE_LIGHT); return (*this); } + Builder& withTypeMeta() { _flags.set(TYPE_META); return (*this); } Builder& withTransparent() { _flags.set(TRANSLUCENT); return (*this); } Builder& withViewSpace() { _flags.set(VIEW_SPACE); return (*this); } Builder& withDynamic() { _flags.set(DYNAMIC); return (*this); } @@ -91,6 +93,7 @@ public: bool isShape() const { return _flags[TYPE_SHAPE]; } bool isLight() const { return _flags[TYPE_LIGHT]; } + bool isMeta() const { return _flags[TYPE_META]; } bool isOpaque() const { return !_flags[TRANSLUCENT]; } bool isTransparent() const { return _flags[TRANSLUCENT]; } @@ -150,6 +153,7 @@ public: Builder& withTypeShape() { _value.set(ItemKey::TYPE_SHAPE); _mask.set(ItemKey::TYPE_SHAPE); return (*this); } Builder& withTypeLight() { _value.set(ItemKey::TYPE_LIGHT); _mask.set(ItemKey::TYPE_LIGHT); return (*this); } + Builder& withTypeMeta() { _value.set(ItemKey::TYPE_META); _mask.set(ItemKey::TYPE_META); return (*this); } Builder& withOpaque() { _value.reset(ItemKey::TRANSLUCENT); _mask.set(ItemKey::TRANSLUCENT); return (*this); } Builder& withTransparent() { _value.set(ItemKey::TRANSLUCENT); _mask.set(ItemKey::TRANSLUCENT); return (*this); } @@ -179,6 +183,7 @@ public: static Builder opaqueShape() { return Builder().withTypeShape().withOpaque().withWorldSpace(); } static Builder transparentShape() { return Builder().withTypeShape().withTransparent().withWorldSpace(); } static Builder light() { return Builder().withTypeLight(); } + static Builder meta() { return Builder().withTypeMeta(); } static Builder background() { return Builder().withViewSpace().withLayered(); } static Builder opaqueShapeLayered() { return Builder().withTypeShape().withOpaque().withWorldSpace().withLayered(); } static Builder transparentShapeLayered() { return Builder().withTypeShape().withTransparent().withWorldSpace().withLayered(); } @@ -210,6 +215,25 @@ inline QDebug operator<<(QDebug debug, const ItemFilter& me) { using ItemID = uint32_t; using ItemCell = int32_t; +// A few typedefs for standard containers of ItemIDs +using ItemIDs = std::vector; +using ItemIDSet = std::set; + +// Handy type to just pass the ID and the bound of an item +class ItemBound { + public: + ItemBound(ItemID id) : id(id) { } + ItemBound(ItemID id, const AABox& bound) : id(id), bound(bound) { } + + ItemID id; + AABox bound; +}; + +// many Item Bounds in a vector +using ItemBounds = std::vector; + +// Item is the proxy to a bounded "object" in the scene +// An item is described by its Key class Item { public: typedef std::vector Vector; @@ -295,6 +319,8 @@ public: virtual const ShapeKey getShapeKey() const = 0; + virtual uint32_t fetchMetaSubItems(ItemIDs& subItems) const = 0; + ~PayloadInterface() {} // Status interface is local to the base class @@ -313,6 +339,9 @@ public: Item() {} ~Item() {} + // Item exists if it has a valid payload + bool exist() const { return (bool)(_payload); } + // Main scene / item managment interface reset/update/kill void resetPayload(const PayloadPointer& payload); void resetCell(ItemCell cell = INVALID_CELL, bool _small = false) { _cell = cell; _key.setSmaller(_small); } @@ -339,6 +368,9 @@ public: // Shape Type Interface const ShapeKey getShapeKey() const { return _payload->getShapeKey(); } + // Meta Type Interface + uint32_t fetchMetaSubItems(ItemIDs& subItems) const { return _payload->fetchMetaSubItems(subItems); } + // Access the status const StatusPointer& getStatus() const { return _payload->getStatus(); } @@ -370,10 +402,7 @@ inline QDebug operator<<(QDebug debug, const Item& item) { return debug; } -// THe Payload class is the real Payload to be used -// THis allow anything to be turned into a Payload as long as the required interface functions are available -// When creating a new kind of payload from a new "stuff" class then you need to create specialized version for "stuff" -// of the Payload interface +// Item shared interface supported by the payload template const ItemKey payloadGetKey(const std::shared_ptr& payloadData) { return ItemKey(); } template const Item::Bound payloadGetBound(const std::shared_ptr& payloadData) { return Item::Bound(); } template int payloadGetLayer(const std::shared_ptr& payloadData) { return 0; } @@ -385,6 +414,14 @@ template void payloadRender(const std::shared_ptr& payloadData, Ren // implying that the shape will setup its own pipeline without the use of the ShapeKey. template const ShapeKey shapeGetShapeKey(const std::shared_ptr& payloadData) { return ShapeKey::Builder::ownPipeline(); } +// Meta Type Interface +// Meta items act as the grouping object for several sub items (typically shapes). +template uint32_t metaFetchMetaSubItems(const std::shared_ptr& payloadData, ItemIDs& subItems) { return 0; } + +// THe Payload class is the real Payload to be used +// THis allow anything to be turned into a Payload as long as the required interface functions are available +// When creating a new kind of payload from a new "stuff" class then you need to create specialized version for "stuff" +// of the Payload interface template class Payload : public Item::PayloadInterface { public: typedef std::shared_ptr DataPointer; @@ -403,6 +440,9 @@ public: // Shape Type interface virtual const ShapeKey getShapeKey() const override { return shapeGetShapeKey(_data); } + // Meta Type Interface + virtual uint32_t fetchMetaSubItems(ItemIDs& subItems) const { return metaFetchMetaSubItems(_data, subItems); } + protected: DataPointer _data; @@ -450,22 +490,6 @@ template <> const Item::Bound payloadGetBound(const FooPointer& foo) { typedef Item::PayloadPointer PayloadPointer; typedef std::vector< PayloadPointer > Payloads; -// A few typedefs for standard containers of ItemIDs -using ItemIDs = std::vector; -using ItemIDSet = std::set; - -// Handy type to just pass the ID and the bound of an item -class ItemBound { -public: - ItemBound(ItemID id) : id(id) { } - ItemBound(ItemID id, const AABox& bound) : id(id), bound(bound) { } - - ItemID id; - AABox bound; -}; -// many Item Bounds in a vector -using ItemBounds = std::vector; - // A map of items by ShapeKey to optimize rendering pipeline assignments using ShapeBounds = std::unordered_map; diff --git a/libraries/render/src/render/RenderFetchCullSortTask.cpp b/libraries/render/src/render/RenderFetchCullSortTask.cpp index 3195d8c5f8..69c415dffd 100644 --- a/libraries/render/src/render/RenderFetchCullSortTask.cpp +++ b/libraries/render/src/render/RenderFetchCullSortTask.cpp @@ -29,33 +29,41 @@ RenderFetchCullSortTask::RenderFetchCullSortTask(CullFunctor cullFunctor) { const auto nonspatialSelection = addJob("FetchOverlaySelection"); // Multi filter visible items into different buckets - const int NUM_FILTERS = 3; + const int NUM_SPATIAL_FILTERS = 4; + const int NUM_NON_SPATIAL_FILTERS = 3; const int OPAQUE_SHAPE_BUCKET = 0; const int TRANSPARENT_SHAPE_BUCKET = 1; const int LIGHT_BUCKET = 2; + const int META_BUCKET = 3; const int BACKGROUND_BUCKET = 2; - MultiFilterItem::ItemFilterArray spatialFilters = { { + MultiFilterItem::ItemFilterArray spatialFilters = { { ItemFilter::Builder::opaqueShape(), ItemFilter::Builder::transparentShape(), - ItemFilter::Builder::light() + ItemFilter::Builder::light(), + ItemFilter::Builder::meta() } }; - MultiFilterItem::ItemFilterArray nonspatialFilters = { { + MultiFilterItem::ItemFilterArray nonspatialFilters = { { ItemFilter::Builder::opaqueShape(), ItemFilter::Builder::transparentShape(), ItemFilter::Builder::background() } }; - const auto filteredSpatialBuckets = addJob>("FilterSceneSelection", culledSpatialSelection, spatialFilters).get::ItemBoundsArray>(); - const auto filteredNonspatialBuckets = addJob>("FilterOverlaySelection", nonspatialSelection, nonspatialFilters).get::ItemBoundsArray>(); + const auto filteredSpatialBuckets = + addJob>("FilterSceneSelection", culledSpatialSelection, spatialFilters) + .get::ItemBoundsArray>(); + const auto filteredNonspatialBuckets = + addJob>("FilterOverlaySelection", nonspatialSelection, nonspatialFilters) + .get::ItemBoundsArray>(); // Extract opaques / transparents / lights / overlays const auto opaques = addJob("DepthSortOpaque", filteredSpatialBuckets[OPAQUE_SHAPE_BUCKET]); const auto transparents = addJob("DepthSortTransparent", filteredSpatialBuckets[TRANSPARENT_SHAPE_BUCKET], DepthSortItems(false)); const auto lights = filteredSpatialBuckets[LIGHT_BUCKET]; + const auto metas = filteredSpatialBuckets[META_BUCKET]; const auto overlayOpaques = addJob("DepthSortOverlayOpaque", filteredNonspatialBuckets[OPAQUE_SHAPE_BUCKET]); const auto overlayTransparents = addJob("DepthSortOverlayTransparent", filteredNonspatialBuckets[TRANSPARENT_SHAPE_BUCKET], DepthSortItems(false)); const auto background = filteredNonspatialBuckets[BACKGROUND_BUCKET]; setOutput(Output{{ - opaques, transparents, lights, overlayOpaques, overlayTransparents, background, spatialSelection }}); + opaques, transparents, lights, metas, overlayOpaques, overlayTransparents, background, spatialSelection }}); } diff --git a/libraries/render/src/render/RenderFetchCullSortTask.h b/libraries/render/src/render/RenderFetchCullSortTask.h index bea45247b0..1af74939c9 100644 --- a/libraries/render/src/render/RenderFetchCullSortTask.h +++ b/libraries/render/src/render/RenderFetchCullSortTask.h @@ -19,7 +19,21 @@ class RenderFetchCullSortTask : public render::Task { public: - using Output = std::array; + + enum Buckets { + OPAQUE_SHAPE = 0, + TRANSPARENT_SHAPE, + LIGHT, + META, + OVERLAY_OPAQUE_SHAPE, + OVERLAY_TRANSPARENT_SHAPE, + BACKGROUND, + SPATIAL_SELECTION, + + NUM_BUCKETS + }; + + using Output = std::array; using JobModel = ModelO; RenderFetchCullSortTask(render::CullFunctor cullFunctor); diff --git a/libraries/render/src/render/Scene.cpp b/libraries/render/src/render/Scene.cpp index 95fef3e9f0..076879ae25 100644 --- a/libraries/render/src/render/Scene.cpp +++ b/libraries/render/src/render/Scene.cpp @@ -58,7 +58,7 @@ ItemID Scene::allocateID() { return _IDAllocator.fetch_add(1); } -bool Scene::isAllocatedID(const ItemID& id) { +bool Scene::isAllocatedID(const ItemID& id) const { return Item::isValidID(id) && (id < _numAllocatedItems.load()); } diff --git a/libraries/render/src/render/Scene.h b/libraries/render/src/render/Scene.h index 13475d0556..f2f8403dc9 100644 --- a/libraries/render/src/render/Scene.h +++ b/libraries/render/src/render/Scene.h @@ -61,7 +61,7 @@ public: ItemID allocateID(); // Check that the ID is valid and allocated for this scene, this a threadsafe call - bool isAllocatedID(const ItemID& id); + bool isAllocatedID(const ItemID& id) const; // THis is the total number of allocated items, this a threadsafe call size_t getNumItems() const { return _numAllocatedItems.load(); } @@ -78,6 +78,9 @@ public: // 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]; } + // Same as getItem, checking if the id is valid + const Item getItemSafe(const ItemID& id) const { if (isAllocatedID(id)) { return _items[id]; } else { return Item(); } } + // Access the spatialized items const ItemSpatialTree& getSpatialTree() const { return _masterSpatialTree; } From 6c701bb0f0124002fb8e57c6a1cec8cfec1394ab Mon Sep 17 00:00:00 2001 From: David Kelly Date: Mon, 16 Jan 2017 18:22:53 -0800 Subject: [PATCH 051/109] remove ignored avatars from PAL when they disconnect --- interface/resources/qml/hifi/Pal.qml | 4 ++++ interface/src/avatar/AvatarManager.cpp | 5 +++++ libraries/networking/src/NodeList.cpp | 12 ++++++++++++ libraries/networking/src/NodeList.h | 2 ++ .../script-engine/src/UsersScriptingInterface.h | 6 ++++++ scripts/system/pal.js | 8 +++++++- 6 files changed, 36 insertions(+), 1 deletion(-) diff --git a/interface/resources/qml/hifi/Pal.qml b/interface/resources/qml/hifi/Pal.qml index 3438baa217..a57e76f864 100644 --- a/interface/resources/qml/hifi/Pal.qml +++ b/interface/resources/qml/hifi/Pal.qml @@ -502,6 +502,10 @@ Rectangle { ignored = {}; gainSliderValueDB = {}; break; + case 'avatarDisconnected': + var sessionID = message.params[0]; + delete ignored[sessionID]; + break; default: console.log('Unrecognized message:', JSON.stringify(message)); } diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index 1f5726acba..848218a27e 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -261,6 +261,11 @@ void AvatarManager::handleRemovedAvatar(const AvatarSharedPointer& removedAvatar if (removalReason == KillAvatarReason::TheirAvatarEnteredYourBubble || removalReason == YourAvatarEnteredTheirBubble) { DependencyManager::get()->radiusIgnoreNodeBySessionID(avatar->getSessionUUID(), true); } + if (removalReason == KillAvatarReason::AvatarDisconnected) { + // remove from node sets, if present + DependencyManager::get()->maintainIgnoreMuteSets(avatar->getSessionUUID()); + DependencyManager::get()->avatarDisconnected(avatar->getSessionUUID()); + } _avatarFades.push_back(removedAvatar); } diff --git a/libraries/networking/src/NodeList.cpp b/libraries/networking/src/NodeList.cpp index 98a563c4e5..0ad31b70fe 100644 --- a/libraries/networking/src/NodeList.cpp +++ b/libraries/networking/src/NodeList.cpp @@ -847,6 +847,18 @@ void NodeList::ignoreNodeBySessionID(const QUuid& nodeID, bool ignoreEnabled) { } } +// removes this UUID from ignore and mute lists. +void NodeList::maintainIgnoreMuteSets(const QUuid& nodeID) { + // don't remove yourself, or nobody + if (!nodeID.isNull() && _sessionUUID != nodeID) { + QWriteLocker ignoredSetLocker{ &_ignoredSetLock }; + QWriteLocker personalMutedSetLocker{ &_personalMutedSetLock }; + _ignoredNodeIDs.unsafe_erase(nodeID); + _personalMutedNodeIDs.unsafe_erase(nodeID); + qCDebug(networking) << "removed" << nodeID.toString() << "from ignore/mute sets (if present)"; + } +} + bool NodeList::isIgnoringNode(const QUuid& nodeID) const { QReadLocker ignoredSetLocker{ &_ignoredSetLock }; return _ignoredNodeIDs.find(nodeID) != _ignoredNodeIDs.cend(); diff --git a/libraries/networking/src/NodeList.h b/libraries/networking/src/NodeList.h index 5c477303e2..4cbe3b1c3b 100644 --- a/libraries/networking/src/NodeList.h +++ b/libraries/networking/src/NodeList.h @@ -90,6 +90,8 @@ public: bool getRequestsDomainListData() { return _requestsDomainListData; } void setRequestsDomainListData(bool isRequesting); + void maintainIgnoreMuteSets(const QUuid& nodeID); + public slots: void reset(); void sendDomainServerCheckIn(); diff --git a/libraries/script-engine/src/UsersScriptingInterface.h b/libraries/script-engine/src/UsersScriptingInterface.h index 0b6b7855b5..2dcff02c77 100644 --- a/libraries/script-engine/src/UsersScriptingInterface.h +++ b/libraries/script-engine/src/UsersScriptingInterface.h @@ -139,6 +139,12 @@ signals: */ void usernameFromIDReply(const QString& nodeID, const QString& username, const QString& machineFingerprint); + /**jsdoc + * Notifies scripts that a user has disconnected from the domain + * @function Users.avatar.avatarDisconnected + */ + void avatarDisconnected(const QUuid& nodeID); + private: bool getRequestsDomainListData(); void setRequestsDomainListData(bool requests); diff --git a/scripts/system/pal.js b/scripts/system/pal.js index 5c5b84e3e8..ad7d2bd7b5 100644 --- a/scripts/system/pal.js +++ b/scripts/system/pal.js @@ -580,7 +580,11 @@ function onClicked() { } pal.setVisible(!pal.visible); } - +function avatarDisconnected(nodeID) { + // remove from the pal list + print("got avatarDisconnected for " + nodeID); + pal.sendToQml({method: 'avatarDisconnected', params: [nodeID]}); +} // // Button state. // @@ -593,6 +597,8 @@ button.clicked.connect(onClicked); pal.visibleChanged.connect(onVisibleChanged); pal.closed.connect(off); Users.usernameFromIDReply.connect(usernameFromIDReply); +Users.avatarDisconnected.connect(avatarDisconnected); + function clearLocalQMLDataAndClosePAL() { pal.sendToQml({ method: 'clearLocalQMLData' }); if (pal.visible) { From 960f4f0eadf8f813b4fd55f219244c64e8308bab Mon Sep 17 00:00:00 2001 From: samcake Date: Mon, 16 Jan 2017 18:33:42 -0800 Subject: [PATCH 052/109] Move the drawBOunds job to DrawTasks --- .../render-utils/src/RenderDeferredTask.cpp | 5 ++ .../render-utils/src/RenderForwardTask.cpp | 55 ----------------- .../render-utils/src/RenderForwardTask.h | 21 ------- libraries/render/src/render/DrawTask.cpp | 60 +++++++++++++++++++ libraries/render/src/render/DrawTask.h | 21 +++++++ 5 files changed, 86 insertions(+), 76 deletions(-) diff --git a/libraries/render-utils/src/RenderDeferredTask.cpp b/libraries/render-utils/src/RenderDeferredTask.cpp index 8547c5adb0..e0192b5f85 100644 --- a/libraries/render-utils/src/RenderDeferredTask.cpp +++ b/libraries/render-utils/src/RenderDeferredTask.cpp @@ -159,6 +159,11 @@ RenderDeferredTask::RenderDeferredTask(RenderFetchCullSortTask::Output items) { // Debugging stages { + + + // Bounds do not draw on stencil buffer, so they must come last + addJob("DrawMetaBounds", metas); + // Debugging Deferred buffer job const auto debugFramebuffers = render::Varying(DebugDeferredBuffer::Inputs(deferredFramebuffer, linearDepthTarget, surfaceGeometryFramebuffer, ambientOcclusionFramebuffer)); addJob("DebugDeferredBuffer", debugFramebuffers); diff --git a/libraries/render-utils/src/RenderForwardTask.cpp b/libraries/render-utils/src/RenderForwardTask.cpp index 7550d2326b..45a32c1aaf 100755 --- a/libraries/render-utils/src/RenderForwardTask.cpp +++ b/libraries/render-utils/src/RenderForwardTask.cpp @@ -24,8 +24,6 @@ #include -#include -#include #include "nop_frag.h" using namespace render; @@ -182,57 +180,4 @@ void DrawBackground::run(const SceneContextPointer& sceneContext, const RenderCo args->_batch = nullptr; } -const gpu::PipelinePointer DrawBounds::getPipeline() { - if (!_boundsPipeline) { - auto vs = gpu::Shader::createVertex(std::string(drawItemBounds_vert)); - auto ps = gpu::Shader::createPixel(std::string(drawItemBounds_frag)); - gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps); - gpu::Shader::BindingSet slotBindings; - gpu::Shader::makeProgram(*program, slotBindings); - - _cornerLocation = program->getUniforms().findLocation("inBoundPos"); - _scaleLocation = program->getUniforms().findLocation("inBoundDim"); - - auto state = std::make_shared(); - state->setDepthTest(true, false, gpu::LESS_EQUAL); - state->setBlendFunction(true, - gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA, - gpu::State::DEST_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ZERO); - - _boundsPipeline = gpu::Pipeline::create(program, state); - } - return _boundsPipeline; -} - -void DrawBounds::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, - const Inputs& items) { - RenderArgs* args = renderContext->args; - - gpu::doInBatch(args->_context, [&](gpu::Batch& batch) { - args->_batch = &batch; - - // Setup projection - glm::mat4 projMat; - Transform viewMat; - args->getViewFrustum().evalProjectionMatrix(projMat); - args->getViewFrustum().evalViewTransform(viewMat); - batch.setProjectionTransform(projMat); - batch.setViewTransform(viewMat); - batch.setModelTransform(Transform()); - - // Bind program - batch.setPipeline(getPipeline()); - assert(_cornerLocation >= 0); - assert(_scaleLocation >= 0); - - // Render bounds - for (const auto& item : items) { - batch._glUniform3fv(_cornerLocation, 1, (const float*)(&item.bound.getCorner())); - batch._glUniform3fv(_scaleLocation, 1, (const float*)(&item.bound.getScale())); - - static const int NUM_VERTICES_PER_CUBE = 24; - batch.draw(gpu::LINES, NUM_VERTICES_PER_CUBE, 0); - } - }); -} diff --git a/libraries/render-utils/src/RenderForwardTask.h b/libraries/render-utils/src/RenderForwardTask.h index a957f7493e..62cbca4382 100755 --- a/libraries/render-utils/src/RenderForwardTask.h +++ b/libraries/render-utils/src/RenderForwardTask.h @@ -68,25 +68,4 @@ public: const Inputs& background); }; -class DrawBounds { -public: - class Config : public render::JobConfig { - public: - Config() : JobConfig(false) {} - }; - - using Inputs = render::ItemBounds; - using JobModel = render::Job::ModelI; - - void configure(const Config& configuration) {} - void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext, - const Inputs& items); - -private: - const gpu::PipelinePointer getPipeline(); - gpu::PipelinePointer _boundsPipeline; - int _cornerLocation { -1 }; - int _scaleLocation { -1 }; -}; - #endif // hifi_RenderForwardTask_h diff --git a/libraries/render/src/render/DrawTask.cpp b/libraries/render/src/render/DrawTask.cpp index 1c9a92c511..2829c6f8e7 100755 --- a/libraries/render/src/render/DrawTask.cpp +++ b/libraries/render/src/render/DrawTask.cpp @@ -19,6 +19,10 @@ #include #include + +#include +#include + using namespace render; void render::renderItems(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemBounds& inItems, int maxDrawnItems) { @@ -134,3 +138,59 @@ void DrawLight::run(const SceneContextPointer& sceneContext, const RenderContext auto config = std::static_pointer_cast(renderContext->jobConfig); config->setNumDrawn((int)inLights.size()); } + +const gpu::PipelinePointer DrawBounds::getPipeline() { + if (!_boundsPipeline) { + auto vs = gpu::Shader::createVertex(std::string(drawItemBounds_vert)); + auto ps = gpu::Shader::createPixel(std::string(drawItemBounds_frag)); + gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps); + + gpu::Shader::BindingSet slotBindings; + gpu::Shader::makeProgram(*program, slotBindings); + + _cornerLocation = program->getUniforms().findLocation("inBoundPos"); + _scaleLocation = program->getUniforms().findLocation("inBoundDim"); + + auto state = std::make_shared(); + state->setDepthTest(true, false, gpu::LESS_EQUAL); + state->setBlendFunction(true, + gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA, + gpu::State::DEST_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ZERO); + + _boundsPipeline = gpu::Pipeline::create(program, state); + } + return _boundsPipeline; +} + +void DrawBounds::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, + const Inputs& items) { + RenderArgs* args = renderContext->args; + + gpu::doInBatch(args->_context, [&](gpu::Batch& batch) { + args->_batch = &batch; + + // Setup projection + glm::mat4 projMat; + Transform viewMat; + args->getViewFrustum().evalProjectionMatrix(projMat); + args->getViewFrustum().evalViewTransform(viewMat); + batch.setProjectionTransform(projMat); + batch.setViewTransform(viewMat); + batch.setModelTransform(Transform()); + + // Bind program + batch.setPipeline(getPipeline()); + assert(_cornerLocation >= 0); + assert(_scaleLocation >= 0); + + // Render bounds + for (const auto& item : items) { + batch._glUniform3fv(_cornerLocation, 1, (const float*)(&item.bound.getCorner())); + batch._glUniform3fv(_scaleLocation, 1, (const float*)(&item.bound.getScale())); + + static const int NUM_VERTICES_PER_CUBE = 24; + batch.draw(gpu::LINES, NUM_VERTICES_PER_CUBE, 0); + } + }); +} + diff --git a/libraries/render/src/render/DrawTask.h b/libraries/render/src/render/DrawTask.h index aa564980c4..27f07921c3 100755 --- a/libraries/render/src/render/DrawTask.h +++ b/libraries/render/src/render/DrawTask.h @@ -50,6 +50,27 @@ protected: int _maxDrawn; // initialized by Config }; +class DrawBounds { +public: + class Config : public render::JobConfig { + public: + Config() : JobConfig(false) {} + }; + + using Inputs = render::ItemBounds; + using JobModel = render::Job::ModelI; + + void configure(const Config& configuration) {} + void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext, + const Inputs& items); + +private: + const gpu::PipelinePointer getPipeline(); + gpu::PipelinePointer _boundsPipeline; + int _cornerLocation { -1 }; + int _scaleLocation { -1 }; +}; + } #endif // hifi_render_DrawTask_h From 6c07a9aece6920794cc0d69a5f60d585911877ae Mon Sep 17 00:00:00 2001 From: David Kelly Date: Mon, 16 Jan 2017 19:06:36 -0800 Subject: [PATCH 053/109] remove debuggng logspam --- libraries/networking/src/NodeList.cpp | 1 - scripts/system/pal.js | 1 - 2 files changed, 2 deletions(-) diff --git a/libraries/networking/src/NodeList.cpp b/libraries/networking/src/NodeList.cpp index 0ad31b70fe..8451b2c4a0 100644 --- a/libraries/networking/src/NodeList.cpp +++ b/libraries/networking/src/NodeList.cpp @@ -855,7 +855,6 @@ void NodeList::maintainIgnoreMuteSets(const QUuid& nodeID) { QWriteLocker personalMutedSetLocker{ &_personalMutedSetLock }; _ignoredNodeIDs.unsafe_erase(nodeID); _personalMutedNodeIDs.unsafe_erase(nodeID); - qCDebug(networking) << "removed" << nodeID.toString() << "from ignore/mute sets (if present)"; } } diff --git a/scripts/system/pal.js b/scripts/system/pal.js index ad7d2bd7b5..7ecce80480 100644 --- a/scripts/system/pal.js +++ b/scripts/system/pal.js @@ -582,7 +582,6 @@ function onClicked() { } function avatarDisconnected(nodeID) { // remove from the pal list - print("got avatarDisconnected for " + nodeID); pal.sendToQml({method: 'avatarDisconnected', params: [nodeID]}); } // From 5e49e8025ab84d11a8363e058ee5b837961e5b68 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Mon, 16 Jan 2017 21:06:38 -0600 Subject: [PATCH 054/109] improve comment in domain settings --- domain-server/resources/describe-settings.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/domain-server/resources/describe-settings.json b/domain-server/resources/describe-settings.json index dd0e4ad4a1..c813ffc54c 100644 --- a/domain-server/resources/describe-settings.json +++ b/domain-server/resources/describe-settings.json @@ -40,7 +40,7 @@ { "name": "local_port", "label": "Local UDP Port", - "help": "This is the local port your domain-server binds to for UDP connections.
Depending on your router, this may need to be changed to run multiple full automatic networking domain-servers in the same network.", + "help": "This is the local port your domain-server binds to for UDP connections.
Depending on your router, this may need to be changed to unique values for each domain-server in order to run multiple full automatic networking domain-servers in the same network. You can use the value 0 to have the domain-server select a random port, which will help in preventing port collisions.", "default": "40102", "type": "int", "advanced": true From c7a561aff9cbcee7a01fb5ef81aa401f6920a31b Mon Sep 17 00:00:00 2001 From: humbletim Date: Tue, 17 Jan 2017 01:23:03 -0500 Subject: [PATCH 055/109] trim extra debugging code --- .../tutorials/entity_scripts/ambientSound.js | 150 +----------------- 1 file changed, 4 insertions(+), 146 deletions(-) diff --git a/scripts/tutorials/entity_scripts/ambientSound.js b/scripts/tutorials/entity_scripts/ambientSound.js index 3b75ed6720..700db4753c 100644 --- a/scripts/tutorials/entity_scripts/ambientSound.js +++ b/scripts/tutorials/entity_scripts/ambientSound.js @@ -22,19 +22,15 @@ // (function(){ - var VERSION = "0.0.1"; // This sample clip and range will be used if you don't add userData to the entity (see above) var DEFAULT_RANGE = 100; var DEFAULT_URL = "http://hifi-content.s3.amazonaws.com/ken/samples/forest_ambiX.wav"; var DEFAULT_VOLUME = 1.0; var soundURL = ""; - var soundName = ""; - var startTime; var soundOptions = { loop: true, localOnly: true, - //ignorePenumbra: true, }; var range = DEFAULT_RANGE; var maxVolume = DEFAULT_VOLUME; @@ -59,12 +55,6 @@ } } - var WANT_DEBUG_OVERLAY = false; - var LINEHEIGHT = 0.1; - // Optionally enable debug overlays using a Settings value - WANT_DEBUG_OVERLAY = WANT_DEBUG_OVERLAY || /ambientSound/.test(Settings.getValue("WANT_DEBUG_OVERLAY")); - var WANT_DEBUG_BROADCASTS = WANT_DEBUG_OVERLAY && "ambientSound.js"; - this.updateSettings = function() { // Check user data on the entity for any changes var oldSoundURL = soundURL; @@ -73,7 +63,6 @@ var data = JSON.parse(props.userData); if (data.soundURL && !(soundURL === data.soundURL)) { soundURL = data.soundURL; - soundName = (soundURL||"").split("/").pop(); // just filename part debugPrint("Read ambient sound URL: " + soundURL); } if (data.range && !(range === data.range)) { @@ -111,13 +100,6 @@ debugPrint("Failed to download ambient sound: " + soundURL); debugState("error"); } - debugPrint("onStateChanged: " + JSON.stringify({ - sound: soundName, - state: resource.state, - stateName: Object.keys(Resource.State).filter(function(key) { - return Resource.State[key] === resource.state; - }) - })); } resource.stateChanged.connect(onStateChanged); onStateChanged(resource.state); @@ -149,13 +131,9 @@ var data = JSON.parse(props.userData); data.disabled = !data.disabled; - debugPrint(hint + " -- triggering ambient sound " + (data.disabled ? "OFF" : "ON") + " (" + soundName + ")"); + debugPrint(hint + " -- triggering ambient sound " + (data.disabled ? "OFF" : "ON") + " (" + soundURL + ")"); var oldState = _debugState; - if (WANT_DEBUG_BROADCASTS) { - Messages.sendMessage(WANT_DEBUG_BROADCASTS, JSON.stringify({ palName: MyAvatar.sessionDisplayName, soundName: soundName, hint: hint, scriptTimestamp: props.scriptTimestamp, oldState: oldState, newState: _debugState, age: props.age })); - } - this.cleanup(); // Save the userData and bump scriptTimestamp, which causes all nearby listeners to apply the state change @@ -182,14 +160,10 @@ this.preload = function(entityID) { // Load the sound and range from the entity userData fields, and note the position of the entity. - debugPrint("Ambient sound preload " + VERSION); + debugPrint("Ambient sound preload"); entity = entityID; _this = this; - if (WANT_DEBUG_OVERLAY) { - _createDebugOverlays(); - } - var props = Entities.getEntityProperties(entity, [ "userData" ]); if (props.userData) { var data = JSON.parse(props.userData); @@ -209,12 +183,7 @@ var HYSTERESIS_FRACTION = 0.1; var props = Entities.getEntityProperties(entity, [ "position", "rotation" ]); if (!props.position) { - // FIXME: this mysterious state has been happening while testing - // and might indicate a bug where an Entity can become unreachable without `unload` having been called.. print("FIXME: ambientSound.js -- expected Entity unavailable!") - if (WANT_DEBUG_BROADCASTS) { - Messages.sendMessage(WANT_DEBUG_BROADCASTS, JSON.stringify({ palName: MyAvatar.sessionDisplayName, soundName: soundName, hint: "FIXME: maybeUpdate", oldState: _debugState })); - } return _this.cleanup(); } center = props.position; @@ -225,7 +194,7 @@ soundOptions.orientation = rotation; soundOptions.volume = volume; if (!soundPlaying && ambientSound && ambientSound.downloaded) { - debugState("playing", "Starting ambient sound: " + soundName + " (duration: " + ambientSound.duration + ")"); + debugState("playing", "Starting ambient sound: " + soundURL + " (duration: " + ambientSound.duration + ")"); soundPlaying = Audio.playSound(ambientSound, soundOptions); } else if (soundPlaying && soundPlaying.playing) { soundPlaying.setOptions(soundOptions); @@ -233,22 +202,12 @@ } else if (soundPlaying && soundPlaying.playing && (distance > range * HYSTERESIS_FRACTION)) { soundPlaying.stop(); soundPlaying = false; - debugState("idle", "Out of range, stopping ambient sound: " + soundName); - } - if (WANT_DEBUG_OVERLAY) { - updateDebugOverlay(distance); + debugState("idle", "Out of range, stopping ambient sound: " + soundURL); } } this.unload = function(entityID) { debugPrint("Ambient sound unload "); - if (WANT_DEBUG_BROADCASTS) { - var offset = ambientSound && (new Date - startTime)/1000 % ambientSound.duration; - Messages.sendMessage(WANT_DEBUG_BROADCASTS, JSON.stringify({ palName: MyAvatar.sessionDisplayName, soundName: soundName, hint: "unload", oldState: _debugState, offset: offset })); - } - if (WANT_DEBUG_OVERLAY) { - _removeDebugOverlays(); - } this.cleanup(); }; @@ -263,105 +222,4 @@ } }; - // Visual debugging overlay (to see set WANT_DEBUG_OVERLAY = true) - - var DEBUG_COLORS = { - //preload: { red: 0, green: 80, blue: 80 }, - disabled: { red: 0, green: 0, blue: 0, alpha: 0.0 }, - downloading: { red: 255, green: 255, blue: 0 }, - error: { red: 255, green: 0, blue: 0 }, - playing: { red: 0, green: 200, blue: 0 }, - idle: { red: 0, green: 100, blue: 0 } - }; - var _debugOverlay; - var _debugState = ""; - function debugState(state, message) { - if (state === "playing") { - startTime = new Date; - } - _debugState = state; - if (message) { - debugPrint(message); - } - updateDebugOverlay(); - if (WANT_DEBUG_BROADCASTS) { - // Broadcast state changes to make multi-user scenarios easier to verify from a single console - Messages.sendMessage(WANT_DEBUG_BROADCASTS, JSON.stringify({ palName: MyAvatar.sessionDisplayName, soundName: soundName, state: state })); - } - } - - function updateDebugOverlay(distance) { - var props = Entities.getEntityProperties(entity, [ "name", "dimensions" ]); - if (!props.dimensions) { - return print("ambientSound.js: updateDebugOverlay -- entity no longer available " + entity); - } - var options = soundPlaying && soundPlaying.options; - if (options) { - var offset = soundPlaying.playing && ambientSound && (new Date - startTime)/1000 % ambientSound.duration; - var deg = Quat.safeEulerAngles(options.orientation); - var orientation = [ deg.x, deg.y, deg.z].map(Math.round).join(", "); - var volume = options.volume; - } - var info = { - //loudness: soundPlaying.loudness && soundPlaying.loudness.toFixed(4) || undefined, - offset: offset && ("00"+offset.toFixed(1)).substr(-4)+"s" || undefined, - orientation: orientation, - injector: soundPlaying && soundPlaying.playing && "playing", - resource: ambientSound && ambientSound.downloaded && "ready (" + ambientSound.duration.toFixed(1) + "s)", - name: props.name || undefined, - uuid: entity.split(/\W/)[1], // extracts just the first part of the UUID - sound: soundName, - volume: Math.max(0,volume||0).toFixed(2) + " / " + maxVolume.toFixed(2), - distance: (distance||0).toFixed(1) + "m / " + range.toFixed(1) + "m", - state: _debugState.toUpperCase(), - }; - - // Pretty print key/value pairs, excluding any undefined values - var outputText = Object.keys(info).filter(function(key) { - return info[key] !== undefined; - }).map(function(key) { - return key + ": " + info[key]; - }).join("\n"); - - // Calculate a local position for displaying info just above the Entity - var textSize = Overlays.textSize(_debugOverlay, outputText); - var size = { - x: textSize.width + LINEHEIGHT, - y: textSize.height + LINEHEIGHT - }; - var pos = { x: 0, y: props.dimensions.y + size.y/2, z: 0 }; - - var backgroundColor = DEBUG_COLORS[_debugState]; - var backgroundAlpha = backgroundColor ? backgroundColor.alpha : 0.6; - Overlays.editOverlay(_debugOverlay, { - visible: true, - backgroundColor: backgroundColor, - backgroundAlpha: backgroundAlpha, - text: outputText, - localPosition: pos, - size: size, - }); - } - - function _removeDebugOverlays() { - if (_debugOverlay) { - Overlays.deleteOverlay(_debugOverlay); - _debugOverlay = 0; - } - } - - function _createDebugOverlays() { - _debugOverlay = Overlays.addOverlay("text3d", { - visible: true, - lineHeight: LINEHEIGHT, - leftMargin: LINEHEIGHT/2, - topMargin: LINEHEIGHT/2, - localPosition: Vec3.ZERO, - parentID: entity, - ignoreRayIntersection: true, - isFacingAvatar: true, - textAlpha: 0.6, - //drawInFront: true, - }); - } }) From 27a23676345b744465d31195c210a1e2cc8ea886 Mon Sep 17 00:00:00 2001 From: humbletim Date: Tue, 17 Jan 2017 01:27:48 -0500 Subject: [PATCH 056/109] update debugPrints --- scripts/tutorials/entity_scripts/ambientSound.js | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/scripts/tutorials/entity_scripts/ambientSound.js b/scripts/tutorials/entity_scripts/ambientSound.js index 700db4753c..523b96c076 100644 --- a/scripts/tutorials/entity_scripts/ambientSound.js +++ b/scripts/tutorials/entity_scripts/ambientSound.js @@ -80,25 +80,22 @@ debugPrint("Read ambient disabled state: " + disabled); if (disabled) { this.cleanup(); - debugState("disabled"); return; } } } if (!(soundURL === oldSoundURL) || (soundURL === "")) { if (soundURL) { - debugState("downloading", "Loading ambient sound into cache"); + debugPrint("Loading ambient sound into cache"); // Use prefetch to detect URL loading errors var resource = SoundCache.prefetch(soundURL); function onStateChanged() { if (resource.state === Resource.State.FINISHED) { resource.stateChanged.disconnect(onStateChanged); ambientSound = SoundCache.getSound(soundURL); - debugState("idle"); } else if (resource.state === Resource.State.FAILED) { resource.stateChanged.disconnect(onStateChanged); debugPrint("Failed to download ambient sound: " + soundURL); - debugState("error"); } } resource.stateChanged.connect(onStateChanged); @@ -132,7 +129,6 @@ data.disabled = !data.disabled; debugPrint(hint + " -- triggering ambient sound " + (data.disabled ? "OFF" : "ON") + " (" + soundURL + ")"); - var oldState = _debugState; this.cleanup(); @@ -141,7 +137,6 @@ userData: JSON.stringify(data), scriptTimestamp: Math.round(props.age * 1000) }); - //this._updateColor(data.disabled); }; this._updateColor = function(disabled) { @@ -194,7 +189,7 @@ soundOptions.orientation = rotation; soundOptions.volume = volume; if (!soundPlaying && ambientSound && ambientSound.downloaded) { - debugState("playing", "Starting ambient sound: " + soundURL + " (duration: " + ambientSound.duration + ")"); + debugPrint("Starting ambient sound: " + soundURL + " (duration: " + ambientSound.duration + ")"); soundPlaying = Audio.playSound(ambientSound, soundOptions); } else if (soundPlaying && soundPlaying.playing) { soundPlaying.setOptions(soundOptions); @@ -202,7 +197,7 @@ } else if (soundPlaying && soundPlaying.playing && (distance > range * HYSTERESIS_FRACTION)) { soundPlaying.stop(); soundPlaying = false; - debugState("idle", "Out of range, stopping ambient sound: " + soundURL); + debugPrint("Out of range, stopping ambient sound: " + soundURL); } } From 88b7f9ec9d6edbda0659b48aef1594680627f6f3 Mon Sep 17 00:00:00 2001 From: humbletim Date: Tue, 17 Jan 2017 01:44:03 -0500 Subject: [PATCH 057/109] add ambient sound test --- scripts/developer/tests/ambientSoundTest.js | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 scripts/developer/tests/ambientSoundTest.js diff --git a/scripts/developer/tests/ambientSoundTest.js b/scripts/developer/tests/ambientSoundTest.js new file mode 100644 index 0000000000..2d5fd832c6 --- /dev/null +++ b/scripts/developer/tests/ambientSoundTest.js @@ -0,0 +1,18 @@ +var WAVE = 'http://cdn.rawgit.com/ambisonictoolkit/atk-sounds/aa31005c/stereo/Aurora_Surgit-Lux_Aeterna.wav'; +var uuid = Entities.addEntity({ + type: "Shape", + shape: "Icosahedron", + dimensions: Vec3.HALF, + script: Script.resolvePath('../../tutorials/entity_scripts/ambientSound.js'), + position: Vec3.sum(Vec3.multiply(5, Quat.getFront(MyAvatar.orientation)), MyAvatar.position), + userData: JSON.stringify({ + soundURL: WAVE, + maxVolume: 0.1, + range: 25, + disabled: true, + }), + lifetime: 600, +}); +Script.scriptEnding.connect(function() { + Entities.deleteEntity(uuid); +}); From 7025ed59090ac719412606975b56000e3edb40a9 Mon Sep 17 00:00:00 2001 From: humbletim Date: Tue, 17 Jan 2017 03:18:56 -0500 Subject: [PATCH 058/109] update test script to create a triggerable entity --- scripts/developer/tests/ambientSoundTest.js | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/developer/tests/ambientSoundTest.js b/scripts/developer/tests/ambientSoundTest.js index 2d5fd832c6..5b373715c0 100644 --- a/scripts/developer/tests/ambientSoundTest.js +++ b/scripts/developer/tests/ambientSoundTest.js @@ -10,6 +10,7 @@ var uuid = Entities.addEntity({ maxVolume: 0.1, range: 25, disabled: true, + grabbableKey: { wantsTrigger: true }, }), lifetime: 600, }); From 2460e89a574613ab615c05c0d2ba283a6d2a5d71 Mon Sep 17 00:00:00 2001 From: David Kelly Date: Tue, 17 Jan 2017 09:53:04 -0800 Subject: [PATCH 059/109] CR feedback --- interface/src/avatar/AvatarManager.cpp | 2 +- libraries/networking/src/NodeList.cpp | 3 +-- libraries/networking/src/NodeList.h | 2 +- scripts/system/pal.js | 1 + 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index 848218a27e..ef08a463f4 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -263,7 +263,7 @@ void AvatarManager::handleRemovedAvatar(const AvatarSharedPointer& removedAvatar } if (removalReason == KillAvatarReason::AvatarDisconnected) { // remove from node sets, if present - DependencyManager::get()->maintainIgnoreMuteSets(avatar->getSessionUUID()); + DependencyManager::get()->removeFromIgnoreMuteSets(avatar->getSessionUUID()); DependencyManager::get()->avatarDisconnected(avatar->getSessionUUID()); } _avatarFades.push_back(removedAvatar); diff --git a/libraries/networking/src/NodeList.cpp b/libraries/networking/src/NodeList.cpp index 8451b2c4a0..0fcd207b94 100644 --- a/libraries/networking/src/NodeList.cpp +++ b/libraries/networking/src/NodeList.cpp @@ -847,8 +847,7 @@ void NodeList::ignoreNodeBySessionID(const QUuid& nodeID, bool ignoreEnabled) { } } -// removes this UUID from ignore and mute lists. -void NodeList::maintainIgnoreMuteSets(const QUuid& nodeID) { +void NodeList::removeFromIgnoreMuteSets(const QUuid& nodeID) { // don't remove yourself, or nobody if (!nodeID.isNull() && _sessionUUID != nodeID) { QWriteLocker ignoredSetLocker{ &_ignoredSetLock }; diff --git a/libraries/networking/src/NodeList.h b/libraries/networking/src/NodeList.h index 4cbe3b1c3b..c4564c0889 100644 --- a/libraries/networking/src/NodeList.h +++ b/libraries/networking/src/NodeList.h @@ -90,7 +90,7 @@ public: bool getRequestsDomainListData() { return _requestsDomainListData; } void setRequestsDomainListData(bool isRequesting); - void maintainIgnoreMuteSets(const QUuid& nodeID); + void removeFromIgnoreMuteSets(const QUuid& nodeID); public slots: void reset(); diff --git a/scripts/system/pal.js b/scripts/system/pal.js index 7ecce80480..fb0d2b310b 100644 --- a/scripts/system/pal.js +++ b/scripts/system/pal.js @@ -620,6 +620,7 @@ Script.scriptEnding.connect(function () { Window.domainConnectionRefused.disconnect(clearLocalQMLDataAndClosePAL); Messages.unsubscribe(CHANNEL); Messages.messageReceived.disconnect(receiveMessage); + Users.avatarDisconnected.disconnect(avatarDisconnected); off(); }); From 305e3085751b90a045929f7d233a74fc6dd5adf1 Mon Sep 17 00:00:00 2001 From: samcake Date: Tue, 17 Jan 2017 10:54:45 -0800 Subject: [PATCH 060/109] avoiding the exclusive filtering behavior --- libraries/render/src/render/CullTask.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/render/src/render/CullTask.h b/libraries/render/src/render/CullTask.h index 56729083dd..3397f9e41c 100644 --- a/libraries/render/src/render/CullTask.h +++ b/libraries/render/src/render/CullTask.h @@ -170,7 +170,7 @@ namespace render { for (size_t i = 0; i < NUM_FILTERS; i++) { if (_filters[i].test(itemKey)) { outItems[i].template edit().emplace_back(itemBound); - break; + // break; } } } From 1e04aebb8d33e9e9bb4a4e072690dc7d5b7f381f Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 17 Jan 2017 11:25:40 -0800 Subject: [PATCH 061/109] add --fast-hearbeat command line switch for 1s stats --- interface/src/Application.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index ceda6c6598..4f35b10a1b 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1238,8 +1238,11 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo // Add periodic checks to send user activity data static int CHECK_NEARBY_AVATARS_INTERVAL_MS = 10000; - static int SEND_STATS_INTERVAL_MS = 10000; static int NEARBY_AVATAR_RADIUS_METERS = 10; + + // setup the stats interval depending on if the 1s faster hearbeat was requested + static const QString FAST_STATS_ARG = "--fast-heartbeat"; + static int SEND_STATS_INTERVAL_MS = arguments().indexOf(FAST_STATS_ARG) != -1 ? 1000 : 10000; static glm::vec3 lastAvatarPosition = myAvatar->getPosition(); static glm::mat4 lastHMDHeadPose = getHMDSensorPose(); From 246f5fb7f5da02172c45e8c8032bb8274ffd8ef0 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Tue, 17 Jan 2017 11:51:09 -0800 Subject: [PATCH 062/109] Add log; fix whitespace --- assignment-client/src/avatars/AvatarMixer.cpp | 4 +++- libraries/networking/src/udt/PacketHeaders.h | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/assignment-client/src/avatars/AvatarMixer.cpp b/assignment-client/src/avatars/AvatarMixer.cpp index 4127719b7b..cd866cecb2 100644 --- a/assignment-client/src/avatars/AvatarMixer.cpp +++ b/assignment-client/src/avatars/AvatarMixer.cpp @@ -280,12 +280,14 @@ void AvatarMixer::broadcastAvatarData() { QPair& soFar = _sessionDisplayNames[baseName]; // Inserts and answers 0, 0 if not already present, which is what we want. int& highWater = soFar.first; nodeData->setBaseDisplayName(baseName); - avatar.setSessionDisplayName((highWater > 0) ? baseName + "_" + QString::number(highWater) : baseName); + QString sessionDisplayName = (highWater > 0) ? baseName + "_" + QString::number(highWater) : baseName; + avatar.setSessionDisplayName(sessionDisplayName); highWater++; soFar.second++; // refcount nodeData->flagIdentityChange(); nodeData->setAvatarSessionDisplayNameMustChange(false); sendIdentityPacket(nodeData, node); // Tell node whose name changed about its new session display name. Others will find out below. + qDebug() << "Giving session display name" << sessionDisplayName << "to node with ID" << node->getUUID(); } // this is an AGENT we have received head data from diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index 9bbd139138..5cef6013d9 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -212,7 +212,7 @@ enum class AvatarMixerPacketVersion : PacketVersion { HasKillAvatarReason, SessionDisplayName, Unignore, - ImmediateSessionDisplayNameUpdates + ImmediateSessionDisplayNameUpdates }; enum class DomainConnectRequestVersion : PacketVersion { From 9b1aaf3bfeb9a136a79396f75565eb02abd110a0 Mon Sep 17 00:00:00 2001 From: David Kelly Date: Tue, 17 Jan 2017 12:09:47 -0800 Subject: [PATCH 063/109] CR feedback --- interface/src/avatar/AvatarManager.cpp | 3 +-- libraries/script-engine/src/UsersScriptingInterface.h | 1 + 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index ef08a463f4..b0dc9922ff 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -260,8 +260,7 @@ void AvatarManager::handleRemovedAvatar(const AvatarSharedPointer& removedAvatar } if (removalReason == KillAvatarReason::TheirAvatarEnteredYourBubble || removalReason == YourAvatarEnteredTheirBubble) { DependencyManager::get()->radiusIgnoreNodeBySessionID(avatar->getSessionUUID(), true); - } - if (removalReason == KillAvatarReason::AvatarDisconnected) { + } else if (removalReason == KillAvatarReason::AvatarDisconnected) { // remove from node sets, if present DependencyManager::get()->removeFromIgnoreMuteSets(avatar->getSessionUUID()); DependencyManager::get()->avatarDisconnected(avatar->getSessionUUID()); diff --git a/libraries/script-engine/src/UsersScriptingInterface.h b/libraries/script-engine/src/UsersScriptingInterface.h index 2dcff02c77..758868ac63 100644 --- a/libraries/script-engine/src/UsersScriptingInterface.h +++ b/libraries/script-engine/src/UsersScriptingInterface.h @@ -142,6 +142,7 @@ signals: /**jsdoc * Notifies scripts that a user has disconnected from the domain * @function Users.avatar.avatarDisconnected + * @param {nodeID} NodeID The session ID of the avatar that has disconnected */ void avatarDisconnected(const QUuid& nodeID); From 5ed3b48f9d6333a42d8a9c13c90668bef4b94dc1 Mon Sep 17 00:00:00 2001 From: samcake Date: Tue, 17 Jan 2017 12:15:09 -0800 Subject: [PATCH 064/109] adding the checkbox to show the meta bounds from the debugDeferredLighting.js --- .../utilities/render/debugDeferredLighting.js | 2 +- .../utilities/render/deferredLighting.qml | 14 ++++++++++++-- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/scripts/developer/utilities/render/debugDeferredLighting.js b/scripts/developer/utilities/render/debugDeferredLighting.js index b32a924c10..625f71f8a4 100644 --- a/scripts/developer/utilities/render/debugDeferredLighting.js +++ b/scripts/developer/utilities/render/debugDeferredLighting.js @@ -13,7 +13,7 @@ var qml = Script.resolvePath('deferredLighting.qml'); var window = new OverlayWindow({ title: 'Lighting', source: qml, - width: 400, height:220, + width: 400, height:280, }); window.setPosition(Window.innerWidth - 420, 50); window.closed.connect(function() { Script.stop(); }); diff --git a/scripts/developer/utilities/render/deferredLighting.qml b/scripts/developer/utilities/render/deferredLighting.qml index 4901be8e03..26dbc1f2bc 100644 --- a/scripts/developer/utilities/render/deferredLighting.qml +++ b/scripts/developer/utilities/render/deferredLighting.qml @@ -74,7 +74,7 @@ Column { Column { spacing: 10 Repeater { - model: [ "Tone Mapping exposure:ToneMapping:exposure:5.0:-5.0" + model: [ "Tone Mapping Exposure:ToneMapping:exposure:5.0:-5.0" ] ConfigSlider { label: qsTr(modelData.split(":")[0]) @@ -88,7 +88,7 @@ Column { Row { Label { - text: "Debug Framebuffer" + text: "Tone Mapping Curve" anchors.left: root.left } @@ -109,6 +109,7 @@ Column { } Row { id: framebuffer + spacing: 10 Label { text: "Debug Framebuffer" @@ -156,5 +157,14 @@ Column { onCurrentIndexChanged: { framebuffer.setDebugMode(currentIndex) } } } + + Row { + id: metas + CheckBox { + text: "Draw Meta Bounds" + checked: Render.getConfig("DrawMetaBounds")["enabled"] + onCheckedChanged: { Render.getConfig("DrawMetaBounds")["enabled"] = checked } + } + } } From 59ff970d5b400867506418da8761442fdca6dc3d Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Tue, 17 Jan 2017 15:01:02 -0800 Subject: [PATCH 065/109] CR feedback --- interface/resources/qml/hifi/NameCard.qml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/interface/resources/qml/hifi/NameCard.qml b/interface/resources/qml/hifi/NameCard.qml index 367ef3b25b..2725ea1617 100644 --- a/interface/resources/qml/hifi/NameCard.qml +++ b/interface/resources/qml/hifi/NameCard.qml @@ -75,7 +75,7 @@ Item { id: myDisplayNameText // Properties text: thisNameCard.displayName - maximumLength: 64 + maximumLength: 256 clip: true // Size width: parent.width @@ -84,7 +84,8 @@ Item { anchors.verticalCenter: parent.verticalCenter anchors.left: parent.left anchors.leftMargin: 10 - anchors.rightMargin: editGlyph.implicitWidth + editGlyph.anchors.rightMargin + anchors.right: parent.right + anchors.rightMargin: editGlyph.width + editGlyph.anchors.rightMargin // Style color: hifi.colors.darkGray FontLoader { id: firaSansSemiBold; source: "../../fonts/FiraSans-SemiBold.ttf"; } @@ -98,6 +99,7 @@ Item { // Signals onEditingFinished: { pal.sendToScript({method: 'displayNameUpdate', params: text}) + cursorPosition = 0 focus = false myDisplayName.border.width = 0 color = hifi.colors.darkGray From 246dcbd0c0b343531cd3b489abdd7756af91a7fc Mon Sep 17 00:00:00 2001 From: samcake Date: Tue, 17 Jan 2017 15:01:27 -0800 Subject: [PATCH 066/109] Removing the warning --- interface/src/avatar/Avatar.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index d86f82a455..ab4ae02a8e 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -79,7 +79,7 @@ namespace render { if (avatarPtr->getSkeletonModel()) { auto metaSubItems = avatarPtr->getSkeletonModel()->fetchRenderItemIDs(); subItems.insert(subItems.end(), metaSubItems.begin(), metaSubItems.end()); - return metaSubItems.size(); + return (uint32_t) metaSubItems.size(); } return 0; } From f5300d3862b8ce9dea56201674aa687965487c0d Mon Sep 17 00:00:00 2001 From: David Kelly Date: Tue, 17 Jan 2017 17:06:18 -0800 Subject: [PATCH 067/109] Removing some PAL-related interface log spam Probably more, but these _seem_ to be the worst --- interface/resources/qml/hifi/Pal.qml | 4 ---- scripts/system/pal.js | 1 - 2 files changed, 5 deletions(-) diff --git a/interface/resources/qml/hifi/Pal.qml b/interface/resources/qml/hifi/Pal.qml index 549598dd2e..7260cd1c14 100644 --- a/interface/resources/qml/hifi/Pal.qml +++ b/interface/resources/qml/hifi/Pal.qml @@ -476,8 +476,6 @@ Rectangle { // Set the userName appropriately userModel.setProperty(userIndex, "userName", userName); userModelData[userIndex].userName = userName; // Defensive programming - } else { - console.log("updateUsername() called with unknown UUID: ", userId); } } break; @@ -493,8 +491,6 @@ Rectangle { if (userIndex != -1) { userModel.setProperty(userIndex, "audioLevel", audioLevel); userModelData[userIndex].audioLevel = audioLevel; // Defensive programming - } else { - console.log("updateUsername() called with unknown UUID: ", userId); } } } diff --git a/scripts/system/pal.js b/scripts/system/pal.js index 2430ce6e87..c61dbba872 100644 --- a/scripts/system/pal.js +++ b/scripts/system/pal.js @@ -498,7 +498,6 @@ function getAudioLevel(id) { var audioLevel = 0.0; var data = id ? ExtendedOverlay.get(id) : myData; if (!data) { - print('no data for', id); return audioLevel; } From cbc89e77ab11a9d1a0d0657b4eb9405e6b641618 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Tue, 17 Jan 2017 09:32:58 -0800 Subject: [PATCH 068/109] Consume fewer resources in AssetServer when interface is running locally --- assignment-client/src/assets/AssetServer.cpp | 48 +++++++++++++++++ interface/src/main.cpp | 2 +- libraries/shared/src/SharedUtil.cpp | 54 +++++++++++++++++++- libraries/shared/src/SharedUtil.h | 4 ++ 4 files changed, 106 insertions(+), 2 deletions(-) diff --git a/assignment-client/src/assets/AssetServer.cpp b/assignment-client/src/assets/AssetServer.cpp index 2fbe2f6dfe..69f3d83583 100644 --- a/assignment-client/src/assets/AssetServer.cpp +++ b/assignment-client/src/assets/AssetServer.cpp @@ -12,6 +12,8 @@ #include "AssetServer.h" +#include + #include #include #include @@ -21,6 +23,7 @@ #include #include +#include #include #include "NetworkLogging.h" @@ -30,6 +33,37 @@ const QString ASSET_SERVER_LOGGING_TARGET_NAME = "asset-server"; +bool interfaceRunning() { + bool result = false; + +#ifdef Q_OS_WIN + QSharedMemory sharedMemory { getInterfaceSharedMemoryName() }; + result = sharedMemory.attach(QSharedMemory::ReadOnly); + if (result) { + sharedMemory.detach(); + } +#endif + return result; +} + +void updateConsumedCores() { + static bool wasInterfaceRunning = false; + bool isInterfaceRunning = interfaceRunning(); + // If state is unchanged, return early + if (isInterfaceRunning == wasInterfaceRunning) { + return; + } + + wasInterfaceRunning = isInterfaceRunning; + auto coreCount = std::thread::hardware_concurrency(); + if (isInterfaceRunning) { + coreCount = coreCount > 4 ? 2 : 1; + } + qDebug() << "Setting max consumed cores to " << coreCount; + setMaxCores(coreCount); +} + + AssetServer::AssetServer(ReceivedMessage& message) : ThreadedAssignment(message), _taskPool(this) @@ -45,6 +79,20 @@ AssetServer::AssetServer(ReceivedMessage& message) : packetReceiver.registerListener(PacketType::AssetGetInfo, this, "handleAssetGetInfo"); packetReceiver.registerListener(PacketType::AssetUpload, this, "handleAssetUpload"); packetReceiver.registerListener(PacketType::AssetMappingOperation, this, "handleAssetMappingOperation"); + +#ifdef Q_OS_WIN + updateConsumedCores(); + QTimer* timer = new QTimer(this); + auto timerConnection = connect(timer, &QTimer::timeout, [] { + updateConsumedCores(); + }); + connect(qApp, &QCoreApplication::aboutToQuit, [this, timerConnection] { + disconnect(timerConnection); + }); + timer->setInterval(1000); + timer->setTimerType(Qt::CoarseTimer); + timer->start(); +#endif } void AssetServer::run() { diff --git a/interface/src/main.cpp b/interface/src/main.cpp index c83f7a989c..d33dba535e 100644 --- a/interface/src/main.cpp +++ b/interface/src/main.cpp @@ -56,7 +56,7 @@ int main(int argc, const char* argv[]) { QCoreApplication::setOrganizationDomain(BuildInfo::ORGANIZATION_DOMAIN); QCoreApplication::setApplicationVersion(BuildInfo::VERSION); - QString applicationName = "High Fidelity Interface - " + qgetenv("USERNAME"); + const QString& applicationName = getInterfaceSharedMemoryName(); bool instanceMightBeRunning = true; diff --git a/libraries/shared/src/SharedUtil.cpp b/libraries/shared/src/SharedUtil.cpp index 89116aa924..4b8699befd 100644 --- a/libraries/shared/src/SharedUtil.cpp +++ b/libraries/shared/src/SharedUtil.cpp @@ -18,6 +18,8 @@ #include #include #include +#include +#include #include @@ -1022,4 +1024,54 @@ bool getProcessorInfo(ProcessorInfo& info) { #endif return false; -} \ No newline at end of file +} + + +const QString& getInterfaceSharedMemoryName() { + static const QString applicationName = "High Fidelity Interface - " + qgetenv("USERNAME"); + return applicationName; +} + +const std::vector& getAvailableCores() { + static std::vector availableCores; +#ifdef Q_OS_WIN + static std::once_flag once; + std::call_once(once, [&] { + DWORD_PTR defaultProcessAffinity = 0, defaultSystemAffinity = 0; + HANDLE process = GetCurrentProcess(); + GetProcessAffinityMask(process, &defaultProcessAffinity, &defaultSystemAffinity); + for (uint64_t i = 0; i < sizeof(DWORD_PTR) * 8; ++i) { + DWORD_PTR coreMask = 1; + coreMask <<= i; + if (0 != (defaultSystemAffinity & coreMask)) { + availableCores.push_back(i); + } + } + }); +#endif + return availableCores; +} + +void setMaxCores(uint8_t maxCores) { +#ifdef Q_OS_WIN + HANDLE process = GetCurrentProcess(); + auto availableCores = getAvailableCores(); + if (availableCores.size() <= maxCores) { + DWORD_PTR currentProcessAffinity = 0, currentSystemAffinity = 0; + GetProcessAffinityMask(process, ¤tProcessAffinity, ¤tSystemAffinity); + SetProcessAffinityMask(GetCurrentProcess(), currentSystemAffinity); + return; + } + + DWORD_PTR newProcessAffinity = 0; + while (maxCores) { + int index = randIntInRange(0, (int)availableCores.size() - 1); + DWORD_PTR coreMask = 1; + coreMask <<= availableCores[index]; + newProcessAffinity |= coreMask; + availableCores.erase(availableCores.begin() + index); + maxCores--; + } + SetProcessAffinityMask(process, newProcessAffinity); +#endif +} diff --git a/libraries/shared/src/SharedUtil.h b/libraries/shared/src/SharedUtil.h index 77204c9608..0f30842f47 100644 --- a/libraries/shared/src/SharedUtil.h +++ b/libraries/shared/src/SharedUtil.h @@ -231,4 +231,8 @@ struct ProcessorInfo { bool getProcessorInfo(ProcessorInfo& info); +const QString& getInterfaceSharedMemoryName(); + +void setMaxCores(uint8_t maxCores); + #endif // hifi_SharedUtil_h From a593a2116a069bc8625e8ad2ca57f9d629f56000 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Tue, 17 Jan 2017 13:40:19 -0800 Subject: [PATCH 069/109] Removing magic numbers --- assignment-client/src/assets/AssetServer.cpp | 9 +++++++-- libraries/shared/src/SharedUtil.cpp | 2 +- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/assignment-client/src/assets/AssetServer.cpp b/assignment-client/src/assets/AssetServer.cpp index 69f3d83583..2498af8010 100644 --- a/assignment-client/src/assets/AssetServer.cpp +++ b/assignment-client/src/assets/AssetServer.cpp @@ -31,6 +31,11 @@ #include "SendAssetTask.h" #include "UploadAssetTask.h" +static const uint8_t MIN_CORES_FOR_MULTICORE = 4; +static const uint8_t CPU_AFFINITY_COUNT_HIGH = 2; +static const uint8_t CPU_AFFINITY_COUNT_LOW = 1; +static const int INTERFACE_RUNNING_CHECK_FREQUENCY_MS = 1000; + const QString ASSET_SERVER_LOGGING_TARGET_NAME = "asset-server"; bool interfaceRunning() { @@ -57,7 +62,7 @@ void updateConsumedCores() { wasInterfaceRunning = isInterfaceRunning; auto coreCount = std::thread::hardware_concurrency(); if (isInterfaceRunning) { - coreCount = coreCount > 4 ? 2 : 1; + coreCount = coreCount > MIN_CORES_FOR_MULTICORE ? CPU_AFFINITY_COUNT_HIGH : CPU_AFFINITY_COUNT_LOW; } qDebug() << "Setting max consumed cores to " << coreCount; setMaxCores(coreCount); @@ -89,7 +94,7 @@ AssetServer::AssetServer(ReceivedMessage& message) : connect(qApp, &QCoreApplication::aboutToQuit, [this, timerConnection] { disconnect(timerConnection); }); - timer->setInterval(1000); + timer->setInterval(INTERFACE_RUNNING_CHECK_FREQUENCY_MS); timer->setTimerType(Qt::CoarseTimer); timer->start(); #endif diff --git a/libraries/shared/src/SharedUtil.cpp b/libraries/shared/src/SharedUtil.cpp index 4b8699befd..a7e251f0e0 100644 --- a/libraries/shared/src/SharedUtil.cpp +++ b/libraries/shared/src/SharedUtil.cpp @@ -1040,7 +1040,7 @@ const std::vector& getAvailableCores() { DWORD_PTR defaultProcessAffinity = 0, defaultSystemAffinity = 0; HANDLE process = GetCurrentProcess(); GetProcessAffinityMask(process, &defaultProcessAffinity, &defaultSystemAffinity); - for (uint64_t i = 0; i < sizeof(DWORD_PTR) * 8; ++i) { + for (uint64_t i = 0; i < sizeof(DWORD_PTR) * BITS_IN_BYTE; ++i) { DWORD_PTR coreMask = 1; coreMask <<= i; if (0 != (defaultSystemAffinity & coreMask)) { From 1048c2a3bf2c099d8912b55096eb48f41bc6c5ce Mon Sep 17 00:00:00 2001 From: humbletim Date: Wed, 18 Jan 2017 01:39:55 -0500 Subject: [PATCH 070/109] apply default userData values; add error checking; throttle toggle clicks --- .../tutorials/entity_scripts/ambientSound.js | 67 ++++++++++++++----- 1 file changed, 52 insertions(+), 15 deletions(-) diff --git a/scripts/tutorials/entity_scripts/ambientSound.js b/scripts/tutorials/entity_scripts/ambientSound.js index 523b96c076..35c9753221 100644 --- a/scripts/tutorials/entity_scripts/ambientSound.js +++ b/scripts/tutorials/entity_scripts/ambientSound.js @@ -27,6 +27,14 @@ var DEFAULT_URL = "http://hifi-content.s3.amazonaws.com/ken/samples/forest_ambiX.wav"; var DEFAULT_VOLUME = 1.0; + var DEFAULT_USERDATA = { + soundURL: DEFAULT_URL, + range: DEFAULT_RANGE, + maxVolume: DEFAULT_VOLUME, + disabled: true, + grabbableKey: { wantsTrigger: true }, + }; + var soundURL = ""; var soundOptions = { loop: true, @@ -60,7 +68,13 @@ var oldSoundURL = soundURL; var props = Entities.getEntityProperties(entity, [ "userData" ]); if (props.userData) { - var data = JSON.parse(props.userData); + try { + var data = JSON.parse(props.userData); + } catch(e) { + debugPrint("unable to parse userData JSON string: " + props.userData); + this.cleanup(); + return; + } if (data.soundURL && !(soundURL === data.soundURL)) { soundURL = data.soundURL; debugPrint("Read ambient sound URL: " + soundURL); @@ -125,18 +139,27 @@ return; } var props = Entities.getEntityProperties(entity, [ "userData", "age", "scriptTimestamp" ]); + if (!props.userData) { + debugPrint("userData is empty; ignoring " + hint); + this.cleanup(); + return; + } var data = JSON.parse(props.userData); data.disabled = !data.disabled; debugPrint(hint + " -- triggering ambient sound " + (data.disabled ? "OFF" : "ON") + " (" + soundURL + ")"); this.cleanup(); + // Prevent rapid-fire toggling (which seems to sometimes lead to sound injectors becoming unstoppable) + this._toggle = this.clickDownOnEntity = function(hint) { debugPrint("ignoring additonal clicks" + hint); }; - // Save the userData and bump scriptTimestamp, which causes all nearby listeners to apply the state change - Entities.editEntity(entity, { - userData: JSON.stringify(data), - scriptTimestamp: Math.round(props.age * 1000) - }); + Script.setTimeout(function() { + // Save the userData and bump scriptTimestamp, which causes all nearby listeners to apply the state change + Entities.editEntity(entity, { + userData: JSON.stringify(data), + scriptTimestamp: props.scriptTimestamp + 1 + }); + }, 250); }; this._updateColor = function(disabled) { @@ -155,21 +178,35 @@ this.preload = function(entityID) { // Load the sound and range from the entity userData fields, and note the position of the entity. - debugPrint("Ambient sound preload"); + debugPrint("Ambient sound preload " + entityID); entity = entityID; _this = this; - var props = Entities.getEntityProperties(entity, [ "userData" ]); + var props = Entities.getEntityProperties(entity, [ "name", "userData" ]); + var data = {}; if (props.userData) { - var data = JSON.parse(props.userData); - this._updateColor(data.disabled); - if (data.disabled) { - _this.maybeUpdate(); - return; + data = JSON.parse(props.userData); + } + var changed = false; + for(var p in DEFAULT_USERDATA) { + if (!(p in data)) { + data[p] = DEFAULT_USERDATA[p]; + changed = true; } } - - checkTimer = Script.setInterval(_this.maybeUpdate, UPDATE_INTERVAL_MSECS); + if (!data.grabbableKey.wantsTrigger) { + data.grabbableKey.wantsTrigger = true; + changed = true; + } + if (changed) { + debugPrint("applying default values to userData"); + Entities.editEntity(entity, { userData: JSON.stringify(data) }); + } + this._updateColor(data.disabled); + this.updateSettings(); + if (!data.disabled) { + checkTimer = Script.setInterval(_this.maybeUpdate, UPDATE_INTERVAL_MSECS); + } }; this.maybeUpdate = function() { From f88b39918f8281dd314a188fc41560016727f53f Mon Sep 17 00:00:00 2001 From: humbletim Date: Wed, 18 Jan 2017 02:54:42 -0500 Subject: [PATCH 071/109] switch to messaging for toggle notifications; cleanup --- .../tutorials/entity_scripts/ambientSound.js | 57 +++++++++++-------- 1 file changed, 34 insertions(+), 23 deletions(-) diff --git a/scripts/tutorials/entity_scripts/ambientSound.js b/scripts/tutorials/entity_scripts/ambientSound.js index 35c9753221..810c9769b8 100644 --- a/scripts/tutorials/entity_scripts/ambientSound.js +++ b/scripts/tutorials/entity_scripts/ambientSound.js @@ -92,12 +92,16 @@ if ("disabled" in data && !(disabled === data.disabled)) { disabled = data.disabled; debugPrint("Read ambient disabled state: " + disabled); - if (disabled) { - this.cleanup(); - return; - } + this._updateColor(disabled); } } + if (disabled) { + this.cleanup(); + soundURL = ""; + return; + } else if (!checkTimer) { + checkTimer = Script.setInterval(_this.maybeUpdate, UPDATE_INTERVAL_MSECS); + } if (!(soundURL === oldSoundURL) || (soundURL === "")) { if (soundURL) { debugPrint("Loading ambient sound into cache"); @@ -138,7 +142,7 @@ if (Settings.getValue("io.highfidelity.isEditting")) { return; } - var props = Entities.getEntityProperties(entity, [ "userData", "age", "scriptTimestamp" ]); + var props = Entities.getEntityProperties(entity, [ "userData" ]); if (!props.userData) { debugPrint("userData is empty; ignoring " + hint); this.cleanup(); @@ -147,19 +151,15 @@ var data = JSON.parse(props.userData); data.disabled = !data.disabled; - debugPrint(hint + " -- triggering ambient sound " + (data.disabled ? "OFF" : "ON") + " (" + soundURL + ")"); + debugPrint(hint + " -- triggering ambient sound " + (data.disabled ? "OFF" : "ON") + " (" + data.soundURL + ")"); this.cleanup(); - // Prevent rapid-fire toggling (which seems to sometimes lead to sound injectors becoming unstoppable) - this._toggle = this.clickDownOnEntity = function(hint) { debugPrint("ignoring additonal clicks" + hint); }; - Script.setTimeout(function() { - // Save the userData and bump scriptTimestamp, which causes all nearby listeners to apply the state change - Entities.editEntity(entity, { - userData: JSON.stringify(data), - scriptTimestamp: props.scriptTimestamp + 1 - }); - }, 250); + // Save the userData and notify nearby listeners of the change + Entities.editEntity(entity, { + userData: JSON.stringify(data) + }); + Messages.sendMessage(entity, "toggled"); }; this._updateColor = function(disabled) { @@ -182,7 +182,7 @@ entity = entityID; _this = this; - var props = Entities.getEntityProperties(entity, [ "name", "userData" ]); + var props = Entities.getEntityProperties(entity, [ "userData" ]); var data = {}; if (props.userData) { data = JSON.parse(props.userData); @@ -204,8 +204,17 @@ } this._updateColor(data.disabled); this.updateSettings(); - if (!data.disabled) { - checkTimer = Script.setInterval(_this.maybeUpdate, UPDATE_INTERVAL_MSECS); + + // Subscribe to toggle notifications using entity ID as a channel name + Messages.subscribe(entity); + Messages.messageReceived.connect(this, "_onMessageReceived"); + }; + + this._onMessageReceived = function(channel, message, sender, local) { + // Handle incoming toggle notifications + if (channel === entity && message === "toggled"/*&& sender !== MyAvatar.sessionUUID*/) { + debugPrint("received " + message + " from " + sender); + this.updateSettings(); } }; @@ -214,9 +223,9 @@ _this.updateSettings(); var HYSTERESIS_FRACTION = 0.1; var props = Entities.getEntityProperties(entity, [ "position", "rotation" ]); - if (!props.position) { - print("FIXME: ambientSound.js -- expected Entity unavailable!") - return _this.cleanup(); + if (disabled || !props.position) { + _this.cleanup(); + return; } center = props.position; rotation = props.rotation; @@ -236,11 +245,13 @@ soundPlaying = false; debugPrint("Out of range, stopping ambient sound: " + soundURL); } - } + }; this.unload = function(entityID) { - debugPrint("Ambient sound unload "); + debugPrint("Ambient sound unload"); this.cleanup(); + Messages.unsubscribe(entity); + Messages.messageReceived.disconnect(this, "_onMessageReceived"); }; this.cleanup = function() { From 6320d872b40a8f6332e82097fbddccae98c62bec Mon Sep 17 00:00:00 2001 From: humbletim Date: Wed, 18 Jan 2017 02:57:13 -0500 Subject: [PATCH 072/109] remove commented code --- scripts/tutorials/entity_scripts/ambientSound.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/tutorials/entity_scripts/ambientSound.js b/scripts/tutorials/entity_scripts/ambientSound.js index 810c9769b8..e0a7d0a3cf 100644 --- a/scripts/tutorials/entity_scripts/ambientSound.js +++ b/scripts/tutorials/entity_scripts/ambientSound.js @@ -212,7 +212,7 @@ this._onMessageReceived = function(channel, message, sender, local) { // Handle incoming toggle notifications - if (channel === entity && message === "toggled"/*&& sender !== MyAvatar.sessionUUID*/) { + if (channel === entity && message === "toggled") { debugPrint("received " + message + " from " + sender); this.updateSettings(); } From 346cfbfe028d4b465ba6ea471b1549e0b3eca420 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Tue, 17 Jan 2017 14:16:31 -0800 Subject: [PATCH 073/109] First checkpoint --- .../src/DomainServerSettingsManager.cpp | 44 +++++---- interface/resources/qml/hifi/NameCard.qml | 93 +++++++++++++++---- interface/resources/qml/hifi/Pal.qml | 5 + libraries/networking/src/NodeList.cpp | 4 +- libraries/networking/src/NodeList.h | 2 +- .../src/UsersScriptingInterface.h | 3 +- scripts/system/pal.js | 12 +-- 7 files changed, 113 insertions(+), 50 deletions(-) diff --git a/domain-server/src/DomainServerSettingsManager.cpp b/domain-server/src/DomainServerSettingsManager.cpp index c6bcc5af34..f4d7d324a6 100644 --- a/domain-server/src/DomainServerSettingsManager.cpp +++ b/domain-server/src/DomainServerSettingsManager.cpp @@ -782,21 +782,24 @@ void DomainServerSettingsManager::processNodeKickRequestPacket(QSharedPointer message, SharedNodePointer sendingNode) { // From the packet, pull the UUID we're identifying QUuid nodeUUID = QUuid::fromRfc4122(message->readWithoutCopy(NUM_BYTES_RFC4122_UUID)); - if (!nodeUUID.isNull()) { - // Before we do any processing on this packet, make sure it comes from a node that is allowed to kick (is an admin) - // OR from a node whose UUID matches the one in the packet - if (sendingNode->getCanKick() || nodeUUID == sendingNode->getUUID()) { - // First, make sure we actually have a node with this UUID - auto limitedNodeList = DependencyManager::get(); - auto matchingNode = limitedNodeList->nodeWithUUID(nodeUUID); - // If we do have a matching node... - if (matchingNode) { + if (!nodeUUID.isNull()) { + // First, make sure we actually have a node with this UUID + auto limitedNodeList = DependencyManager::get(); + auto matchingNode = limitedNodeList->nodeWithUUID(nodeUUID); + + // If we do have a matching node... + if (matchingNode) { + // Setup the packet + auto usernameFromIDReplyPacket = NLPacket::create(PacketType::UsernameFromIDReply); + + bool isAdmin = matchingNode->getCanKick(); + + // Check if the sending node has permission to kick (is an admin) + if (sendingNode->getCanKick()) { // It's time to figure out the username QString verifiedUsername = matchingNode->getPermissions().getVerifiedUserName(); - // Setup the packet - auto usernameFromIDReplyPacket = NLPacket::create(PacketType::UsernameFromIDReply); usernameFromIDReplyPacket->write(nodeUUID.toRfc4122()); usernameFromIDReplyPacket->writeString(verifiedUsername); @@ -804,19 +807,20 @@ void DomainServerSettingsManager::processUsernameFromIDRequestPacket(QSharedPoin DomainServerNodeData* nodeData = reinterpret_cast(matchingNode->getLinkedData()); QUuid machineFingerprint = nodeData ? nodeData->getMachineFingerprint() : QUuid(); usernameFromIDReplyPacket->write(machineFingerprint.toRfc4122()); - - qDebug() << "Sending username" << verifiedUsername << "and machine fingerprint" << machineFingerprint << "associated with node" << nodeUUID; - - // Ship it! - limitedNodeList->sendPacket(std::move(usernameFromIDReplyPacket), *sendingNode); + qDebug() << "Sending username" << verifiedUsername << "and machine fingerprint" << machineFingerprint << "associated with node" << nodeUUID << ". Node admin status: " << isAdmin; } else { - qWarning() << "Node username request received for unknown node. Refusing to process."; + usernameFromIDReplyPacket->write(nodeUUID.toRfc4122()); + usernameFromIDReplyPacket->writeString(""); + usernameFromIDReplyPacket->writeString(""); } + // Write whether or not the user is an admin + usernameFromIDReplyPacket->writePrimitive(isAdmin); + + // Ship it! + limitedNodeList->sendPacket(std::move(usernameFromIDReplyPacket), *sendingNode); } else { - qWarning() << "Refusing to process a username request packet from node" << uuidStringWithoutCurlyBraces(sendingNode->getUUID()) - << ". Either node doesn't have kick permissions or is requesting a username not from their UUID."; + qWarning() << "Node username request received for unknown node. Refusing to process."; } - } else { qWarning() << "Node username request received for invalid node ID. Refusing to process."; } diff --git a/interface/resources/qml/hifi/NameCard.qml b/interface/resources/qml/hifi/NameCard.qml index 2725ea1617..1ad3fae7c1 100644 --- a/interface/resources/qml/hifi/NameCard.qml +++ b/interface/resources/qml/hifi/NameCard.qml @@ -33,6 +33,7 @@ Item { property real audioLevel: 0.0 property bool isMyCard: false property bool selected: false + property bool isAdmin: false /* User image commented out for now - will probably be re-introduced later. Column { @@ -139,32 +140,84 @@ Item { } } // Spacer for DisplayName for my card - Rectangle { + Item { id: myDisplayNameSpacer - width: myDisplayName.width + width: 1 + height: 4 // Anchors anchors.top: myDisplayName.bottom - height: 5 - visible: isMyCard - opacity: 0 } - // DisplayName Text for others' cards - FiraSansSemiBold { - id: displayNameText - // Properties - text: thisNameCard.displayName - elide: Text.ElideRight + // DisplayName container for others' cards + Item { + id: displayNameContainer visible: !isMyCard // Size width: parent.width + height: displayNameTextPixelSize + 4 // Anchors anchors.top: parent.top - // Text Size - size: displayNameTextPixelSize - // Text Positioning - verticalAlignment: Text.AlignVCenter - // Style - color: hifi.colors.darkGray + anchors.left: parent.left + // DisplayName Text for others' cards + FiraSansSemiBold { + id: displayNameText + // Properties + text: thisNameCard.displayName + elide: Text.ElideRight + // Anchors + anchors.top: parent.top + anchors.left: parent.left + // Text Size + size: displayNameTextPixelSize + // Text Positioning + verticalAlignment: Text.AlignVCenter + // Style + color: hifi.colors.darkGray + } + // "ADMIN" label for other users' cards + RalewaySemiBold { + id: adminLabelText + text: "ADMIN" + // Text size + size: displayNameText.size - 4 + // Anchors + anchors.verticalCenter: parent.verticalCenter + anchors.left: displayNameText.right + anchors.leftMargin: 8 + // Style + font.capitalization: Font.AllUppercase + color: hifi.colors.redHighlight + // Alignment + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignTop + } + // This Rectangle refers to the [?] popup button next to "ADMIN" + Item { + // Size + width: 20 + height: displayNameText.height + // Anchors + anchors.verticalCenter: parent.verticalCenter + anchors.left: adminLabelText.right + RalewayRegular { + id: adminLabelQuestionMark + text: "[?]" + size: adminLabelText.size + font.capitalization: Font.AllUppercase + color: hifi.colors.redHighlight + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + anchors.fill: parent + } + MouseArea { + anchors.fill: parent + acceptedButtons: Qt.LeftButton + hoverEnabled: true + onClicked: letterbox('Silencing a user mutes their microphone. Silenced users can unmute themselves by clicking the "UNMUTE" button on their HUD.\n\n' + + "Banning a user will remove them from this domain and prevent them from returning. You can un-ban users from your domain's settings page.)") + onEntered: adminLabelQuestionMark.color = "#94132e" + onExited: adminLabelQuestionMark.color = hifi.colors.redHighlight + } + } } // UserName Text @@ -177,7 +230,7 @@ Item { // Size width: parent.width // Anchors - anchors.top: isMyCard ? myDisplayNameSpacer.bottom : displayNameText.bottom + anchors.top: isMyCard ? myDisplayNameSpacer.bottom : displayNameContainer.bottom // Text Size size: thisNameCard.usernameTextHeight // Text Positioning @@ -188,7 +241,7 @@ Item { // Spacer Item { - id: spacer + id: userNameSpacer height: 4 width: parent.width // Anchors @@ -202,7 +255,7 @@ Item { width: isMyCard ? myDisplayName.width - 20 : ((gainSlider.value - gainSlider.minimumValue)/(gainSlider.maximumValue - gainSlider.minimumValue)) * parent.width height: 8 // Anchors - anchors.top: spacer.bottom + anchors.top: userNameSpacer.bottom // Style radius: 4 color: "#c5c5c5" diff --git a/interface/resources/qml/hifi/Pal.qml b/interface/resources/qml/hifi/Pal.qml index 549598dd2e..ef1203ffa4 100644 --- a/interface/resources/qml/hifi/Pal.qml +++ b/interface/resources/qml/hifi/Pal.qml @@ -221,6 +221,7 @@ Rectangle { visible: !isCheckBox && !isButton uuid: model && model.sessionId selected: styleData.selected + isAdmin: model && model.isAdmin // Size width: nameCardWidth height: parent.height @@ -465,6 +466,7 @@ Rectangle { var userId = message.params[0]; // The text that goes in the userName field is the second parameter in the message. var userName = message.params[1]; + var isAdmin = message.params[2]; // If the userId is empty, we're updating "myData". if (!userId) { myData.userName = userName; @@ -476,6 +478,9 @@ Rectangle { // Set the userName appropriately userModel.setProperty(userIndex, "userName", userName); userModelData[userIndex].userName = userName; // Defensive programming + // Set the admin status appropriately + userModel.setProperty(userIndex, "isAdmin", isAdmin); + userModelData[userIndex].isAdmin = isAdmin; // Defensive programming } else { console.log("updateUsername() called with unknown UUID: ", userId); } diff --git a/libraries/networking/src/NodeList.cpp b/libraries/networking/src/NodeList.cpp index 0fcd207b94..7c5e7fc5b7 100644 --- a/libraries/networking/src/NodeList.cpp +++ b/libraries/networking/src/NodeList.cpp @@ -1068,10 +1068,12 @@ void NodeList::processUsernameFromIDReply(QSharedPointer messag QString username = message->readString(); // read the machine fingerprint from the packet QString machineFingerprintString = (QUuid::fromRfc4122(message->readWithoutCopy(NUM_BYTES_RFC4122_UUID))).toString(); + bool isAdmin; + message->readPrimitive(&isAdmin); qCDebug(networking) << "Got username" << username << "and machine fingerprint" << machineFingerprintString << "for node" << nodeUUIDString; - emit usernameFromIDReply(nodeUUIDString, username, machineFingerprintString); + emit usernameFromIDReply(nodeUUIDString, username, machineFingerprintString, isAdmin); } void NodeList::setRequestsDomainListData(bool isRequesting) { diff --git a/libraries/networking/src/NodeList.h b/libraries/networking/src/NodeList.h index c4564c0889..0e0a2fd6c8 100644 --- a/libraries/networking/src/NodeList.h +++ b/libraries/networking/src/NodeList.h @@ -120,7 +120,7 @@ signals: void receivedDomainServerList(); void ignoredNode(const QUuid& nodeID, bool enabled); void ignoreRadiusEnabledChanged(bool isIgnored); - void usernameFromIDReply(const QString& nodeID, const QString& username, const QString& machineFingerprint); + void usernameFromIDReply(const QString& nodeID, const QString& username, const QString& machineFingerprint, bool isAdmin); private slots: void stopKeepalivePingTimer(); diff --git a/libraries/script-engine/src/UsersScriptingInterface.h b/libraries/script-engine/src/UsersScriptingInterface.h index 758868ac63..5ae1d5a75b 100644 --- a/libraries/script-engine/src/UsersScriptingInterface.h +++ b/libraries/script-engine/src/UsersScriptingInterface.h @@ -135,9 +135,10 @@ signals: /**jsdoc * Notifies scripts of the username and machine fingerprint associated with a UUID. + * Username and machineFingerprint will be empty strings if the requesting user isn't an admin. * @function Users.usernameFromIDReply */ - void usernameFromIDReply(const QString& nodeID, const QString& username, const QString& machineFingerprint); + void usernameFromIDReply(const QString& nodeID, const QString& username, const QString& machineFingerprint, bool isAdmin); /**jsdoc * Notifies scripts that a user has disconnected from the domain diff --git a/scripts/system/pal.js b/scripts/system/pal.js index 2430ce6e87..b95e9bdb87 100644 --- a/scripts/system/pal.js +++ b/scripts/system/pal.js @@ -269,12 +269,9 @@ function populateUserList() { sessionId: id || '', audioLevel: 0.0 }; - // If the current user is an admin OR - // they're requesting their own username ("id" is blank)... - if (Users.canKick || !id) { - // Request the username from the given UUID - Users.requestUsernameFromID(id); - } + // Request the username, fingerprint, and admin status from the given UUID + // Username and fingerprint returns "" if the requesting user isn't an admin + Users.requestUsernameFromID(id); // Request personal mute status and ignore status // from NodeList (as long as we're not requesting it for our own ID) if (id) { @@ -289,7 +286,7 @@ function populateUserList() { } // The function that handles the reply from the server -function usernameFromIDReply(id, username, machineFingerprint) { +function usernameFromIDReply(id, username, machineFingerprint, isAdmin) { var data; // If the ID we've received is our ID... if (MyAvatar.sessionUUID === id) { @@ -300,6 +297,7 @@ function usernameFromIDReply(id, username, machineFingerprint) { // or fingerprint (if we don't have a username) string. data = [id, username || machineFingerprint]; } + data.push(isAdmin); print('Username Data:', JSON.stringify(data)); // Ship the data off to QML pal.sendToQml({ method: 'updateUsername', params: data }); From 202d4e810733b2dfe36bbc65d8b77526f11cbb7e Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Tue, 17 Jan 2017 16:30:15 -0800 Subject: [PATCH 074/109] Checkpoint --- .../resources/qml/hifi/LetterboxMessage.qml | 79 ++++++++++++++++--- interface/resources/qml/hifi/NameCard.qml | 17 ++-- interface/resources/qml/hifi/Pal.qml | 14 ++-- 3 files changed, 89 insertions(+), 21 deletions(-) diff --git a/interface/resources/qml/hifi/LetterboxMessage.qml b/interface/resources/qml/hifi/LetterboxMessage.qml index c8adcdcb74..4172939a8a 100644 --- a/interface/resources/qml/hifi/LetterboxMessage.qml +++ b/interface/resources/qml/hifi/LetterboxMessage.qml @@ -15,7 +15,12 @@ import "../styles-uit" Item { property alias text: popupText.text + property alias headerGlyph: headerGlyph.text + property alias headerText: headerText.text property real popupRadius: hifi.dimensions.borderRadius + property real headerTextPixelSize: 22 + FontLoader { id: ralewayRegular; source: "../../fonts/Raleway-Regular.ttf"; } + FontLoader { id: ralewaySemiBold; source: "../../fonts/Raleway-SemiBold.ttf"; } visible: false id: letterbox anchors.fill: parent @@ -27,19 +32,73 @@ Item { } Rectangle { width: Math.max(parent.width * 0.75, 400) - height: popupText.contentHeight*1.5 + height: contentContainer.height*1.5 anchors.centerIn: parent radius: popupRadius color: "white" - FiraSansSemiBold { - id: popupText - size: hifi.fontSizes.textFieldInput - color: hifi.colors.darkGray - horizontalAlignment: Text.AlignHCenter - anchors.fill: parent - anchors.leftMargin: 15 - anchors.rightMargin: 15 - wrapMode: Text.WordWrap + Item { + id: contentContainer + anchors.centerIn: parent + anchors.margins: 20 + height: childrenRect.height + Item { + id: popupHeaderContainer + visible: headerText.text !== "" || glyphText.text !== "" + // Size + width: parent.width + height: childrenRect.height + // Anchors + anchors.top: parent.top + anchors.left: parent.left + // Header Glyph + HiFiGlyphs { + id: headerGlyph + visible: headerText.text !== "" + // Text Size + size: headerTextPixelSize * 2.5 + // Anchors + anchors.top: parent.top + anchors.left: parent.left + anchors.rightMargin: 5 + // Style + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + color: hifi.colors.darkGray + } + Text { + id: headerText + visible: headerGlyph.text !== "" + // Text Size + font.pixelSize: headerTextPixelSize + // Anchors + anchors.top: parent.top + anchors.left: headerGlyph.right + // Style + font.family: ralewaySemiBold.name + color: hifi.colors.darkGray + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignHCenter + wrapMode: Text.WordWrap + textFormat: Text.StyledText + } + } + Text { + id: popupText + // Anchors + anchors.top: popupHeaderContainer.visible ? popupHeaderContainer.anchors.bottom : parent.top + anchors.left: parent.left + anchors.right: parent.right + anchors.bottom: parent.bottom + // Text alignment + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignHCenter + // Style + font.pixelSize: hifi.fontSizes.textFieldInput + font.family: ralewayRegular.name + color: hifi.colors.darkGray + wrapMode: Text.WordWrap + textFormat: Text.StyledText + } } } MouseArea { diff --git a/interface/resources/qml/hifi/NameCard.qml b/interface/resources/qml/hifi/NameCard.qml index 1ad3fae7c1..83cdcda504 100644 --- a/interface/resources/qml/hifi/NameCard.qml +++ b/interface/resources/qml/hifi/NameCard.qml @@ -163,6 +163,8 @@ Item { // Properties text: thisNameCard.displayName elide: Text.ElideRight + // Size + width: Math.min(displayNameTextMetrics.tightBoundingRect.width, parent.width - adminLabelText.width - adminLabelQuestionMark.width) // Anchors anchors.top: parent.top anchors.left: parent.left @@ -173,6 +175,11 @@ Item { // Style color: hifi.colors.darkGray } + TextMetrics { + id: displayNameTextMetrics + font: displayNameText.font + text: displayNameText.text + } // "ADMIN" label for other users' cards RalewaySemiBold { id: adminLabelText @@ -192,6 +199,7 @@ Item { } // This Rectangle refers to the [?] popup button next to "ADMIN" Item { + id: adminLabelQuestionMark // Size width: 20 height: displayNameText.height @@ -199,7 +207,7 @@ Item { anchors.verticalCenter: parent.verticalCenter anchors.left: adminLabelText.right RalewayRegular { - id: adminLabelQuestionMark + id: adminLabelQuestionMarkText text: "[?]" size: adminLabelText.size font.capitalization: Font.AllUppercase @@ -212,10 +220,9 @@ Item { anchors.fill: parent acceptedButtons: Qt.LeftButton hoverEnabled: true - onClicked: letterbox('Silencing a user mutes their microphone. Silenced users can unmute themselves by clicking the "UNMUTE" button on their HUD.\n\n' + - "Banning a user will remove them from this domain and prevent them from returning. You can un-ban users from your domain's settings page.)") - onEntered: adminLabelQuestionMark.color = "#94132e" - onExited: adminLabelQuestionMark.color = hifi.colors.redHighlight + onClicked: letterbox("This user is an admin on this domain. Admins can Silence and Ban other users at their discretion - so be extra nice!", hifi.glyphs.question, "Domain Admin") + onEntered: adminLabelQuestionMarkText.color = "#94132e" + onExited: adminLabelQuestionMarkText.color = hifi.colors.redHighlight } } } diff --git a/interface/resources/qml/hifi/Pal.qml b/interface/resources/qml/hifi/Pal.qml index ef1203ffa4..5277d97e2c 100644 --- a/interface/resources/qml/hifi/Pal.qml +++ b/interface/resources/qml/hifi/Pal.qml @@ -41,8 +41,10 @@ Rectangle { id: letterboxMessage z: 999 // Force the popup on top of everything else } - function letterbox(message) { + function letterbox(message, headerGlyph, headerText) { letterboxMessage.text = message + letterboxMessage.headerGlyph = headerGlyph + letterboxMessage.headerText = headerText letterboxMessage.visible = true letterboxMessage.popupRadius = 0 } @@ -372,7 +374,7 @@ Rectangle { hoverEnabled: true onClicked: letterbox("Bold names in the list are Avatar Display Names.\n" + "If a Display Name isn't set, a unique Session Display Name is assigned." + - "\n\nAdministrators of this domain can also see the Username or Machine ID associated with each avatar present.") + "\n\nAdministrators of this domain can also see the Username or Machine ID associated with each avatar present.", "", "") onEntered: helpText.color = hifi.colors.baseGrayHighlight onExited: helpText.color = hifi.colors.darkGray } @@ -402,7 +404,7 @@ Rectangle { acceptedButtons: Qt.LeftButton hoverEnabled: true onClicked: letterbox('Silencing a user mutes their microphone. Silenced users can unmute themselves by clicking the "UNMUTE" button on their HUD.\n\n' + - "Banning a user will remove them from this domain and prevent them from returning. You can un-ban users from your domain's settings page.)") + "Banning a user will remove them from this domain and prevent them from returning. You can un-ban users from your domain's settings page.)", "", "") onEntered: adminHelpText.color = "#94132e" onExited: adminHelpText.color = hifi.colors.redHighlight } @@ -447,9 +449,9 @@ Rectangle { var selected = message.params[1]; var userIndex = findSessionIndex(sessionIds[0]); if (sessionIds.length > 1) { - letterbox('Only one user can be selected at a time.'); + letterbox('Only one user can be selected at a time.', "", ""); } else if (userIndex < 0) { - letterbox('The last editor is not among this list of users.'); + letterbox('The last editor is not among this list of users.', "", ""); } else { if (selected) { table.selection.clear(); // for now, no multi-select @@ -499,7 +501,7 @@ Rectangle { userModel.setProperty(userIndex, "audioLevel", audioLevel); userModelData[userIndex].audioLevel = audioLevel; // Defensive programming } else { - console.log("updateUsername() called with unknown UUID: ", userId); + console.log("updateAudioLevel() called with unknown UUID: ", userId); } } } From 9d19aa3c796eab7810e2129b9073fd38f071bcbe Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Tue, 17 Jan 2017 17:39:13 -0800 Subject: [PATCH 075/109] Checkpoint --- .../resources/qml/hifi/LetterboxMessage.qml | 112 +++++++++--------- interface/resources/qml/hifi/NameCard.qml | 4 +- interface/resources/qml/hifi/Pal.qml | 6 +- 3 files changed, 65 insertions(+), 57 deletions(-) diff --git a/interface/resources/qml/hifi/LetterboxMessage.qml b/interface/resources/qml/hifi/LetterboxMessage.qml index 4172939a8a..c1063ea61c 100644 --- a/interface/resources/qml/hifi/LetterboxMessage.qml +++ b/interface/resources/qml/hifi/LetterboxMessage.qml @@ -19,6 +19,7 @@ Item { property alias headerText: headerText.text property real popupRadius: hifi.dimensions.borderRadius property real headerTextPixelSize: 22 + property real popupTextPixelSize: 16 FontLoader { id: ralewayRegular; source: "../../fonts/Raleway-Regular.ttf"; } FontLoader { id: ralewaySemiBold; source: "../../fonts/Raleway-SemiBold.ttf"; } visible: false @@ -32,73 +33,76 @@ Item { } Rectangle { width: Math.max(parent.width * 0.75, 400) - height: contentContainer.height*1.5 + height: childrenRect.height + 50 anchors.centerIn: parent radius: popupRadius color: "white" - Item { + Column { id: contentContainer + width: parent.width - 60 anchors.centerIn: parent - anchors.margins: 20 - height: childrenRect.height - Item { + spacing: 20 + Row { id: popupHeaderContainer - visible: headerText.text !== "" || glyphText.text !== "" - // Size - width: parent.width - height: childrenRect.height - // Anchors - anchors.top: parent.top - anchors.left: parent.left - // Header Glyph - HiFiGlyphs { - id: headerGlyph - visible: headerText.text !== "" - // Text Size - size: headerTextPixelSize * 2.5 - // Anchors - anchors.top: parent.top - anchors.left: parent.left - anchors.rightMargin: 5 - // Style - horizontalAlignment: Text.AlignHCenter - verticalAlignment: Text.AlignVCenter - color: hifi.colors.darkGray + visible: headerText.text !== "" || headerGlyph.text !== "" + height: 30 + Column { + // Header Glyph + HiFiGlyphs { + id: headerGlyph + visible: headerGlyph.text !== "" + // Size + height: parent.parent.height + // Anchors + anchors.left: parent.left + anchors.leftMargin: -15 + // Text Size + size: headerTextPixelSize*2.5 + // Style + horizontalAlignment: Text.AlignHLeft + verticalAlignment: Text.AlignVCenter + color: hifi.colors.darkGray + } } + Column { + // Header Text + Text { + id: headerText + visible: headerText.text !== "" + // Size + height: parent.parent.height + // Anchors + anchors.left: parent.left + anchors.leftMargin: -15 + // Text Size + font.pixelSize: headerTextPixelSize + // Style + font.family: ralewaySemiBold.name + color: hifi.colors.darkGray + horizontalAlignment: Text.AlignHLeft + verticalAlignment: Text.AlignVCenter + wrapMode: Text.WordWrap + textFormat: Text.StyledText + } + } + } + Row { + // Popup Text Text { - id: headerText - visible: headerGlyph.text !== "" - // Text Size - font.pixelSize: headerTextPixelSize - // Anchors - anchors.top: parent.top - anchors.left: headerGlyph.right - // Style - font.family: ralewaySemiBold.name - color: hifi.colors.darkGray + id: popupText + // Size + width: parent.parent.width + // Text alignment verticalAlignment: Text.AlignVCenter - horizontalAlignment: Text.AlignHCenter + horizontalAlignment: Text.AlignHLeft + // Style + font.pixelSize: popupTextPixelSize + font.family: ralewayRegular.name + color: hifi.colors.darkGray wrapMode: Text.WordWrap textFormat: Text.StyledText } } - Text { - id: popupText - // Anchors - anchors.top: popupHeaderContainer.visible ? popupHeaderContainer.anchors.bottom : parent.top - anchors.left: parent.left - anchors.right: parent.right - anchors.bottom: parent.bottom - // Text alignment - verticalAlignment: Text.AlignVCenter - horizontalAlignment: Text.AlignHCenter - // Style - font.pixelSize: hifi.fontSizes.textFieldInput - font.family: ralewayRegular.name - color: hifi.colors.darkGray - wrapMode: Text.WordWrap - textFormat: Text.StyledText - } } } MouseArea { diff --git a/interface/resources/qml/hifi/NameCard.qml b/interface/resources/qml/hifi/NameCard.qml index 83cdcda504..c0b8d1c614 100644 --- a/interface/resources/qml/hifi/NameCard.qml +++ b/interface/resources/qml/hifi/NameCard.qml @@ -220,7 +220,9 @@ Item { anchors.fill: parent acceptedButtons: Qt.LeftButton hoverEnabled: true - onClicked: letterbox("This user is an admin on this domain. Admins can Silence and Ban other users at their discretion - so be extra nice!", hifi.glyphs.question, "Domain Admin") + onClicked: letterbox("This user is an admin on this domain. Admins can Silence and Ban other users at their discretion - so be extra nice!", + hifi.glyphs.question, + "Domain Admin") onEntered: adminLabelQuestionMarkText.color = "#94132e" onExited: adminLabelQuestionMarkText.color = hifi.colors.redHighlight } diff --git a/interface/resources/qml/hifi/Pal.qml b/interface/resources/qml/hifi/Pal.qml index 5277d97e2c..273fc0b73c 100644 --- a/interface/resources/qml/hifi/Pal.qml +++ b/interface/resources/qml/hifi/Pal.qml @@ -403,8 +403,10 @@ Rectangle { anchors.fill: parent acceptedButtons: Qt.LeftButton hoverEnabled: true - onClicked: letterbox('Silencing a user mutes their microphone. Silenced users can unmute themselves by clicking the "UNMUTE" button on their HUD.\n\n' + - "Banning a user will remove them from this domain and prevent them from returning. You can un-ban users from your domain's settings page.)", "", "") + onClicked: letterbox("Silence mutes a user's microphone. Silenced users can unmute themselves by clicking "UNMUTE" on their toolbar.

" + + "Ban removes a user from this domain and prevents them from returning. Admins can un-ban users from the Sandbox Domain Settings Page.)", + hifi.glyphs.question, + "Admin Actions") onEntered: adminHelpText.color = "#94132e" onExited: adminHelpText.color = hifi.colors.redHighlight } From d067ba7dcc7bc39ed116da4007626f3074c833ad Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Tue, 17 Jan 2017 17:58:01 -0800 Subject: [PATCH 076/109] Alan's feedback --- interface/resources/qml/hifi/LetterboxMessage.qml | 2 +- interface/resources/qml/hifi/NameCard.qml | 2 +- interface/resources/qml/hifi/Pal.qml | 10 ++++++---- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/interface/resources/qml/hifi/LetterboxMessage.qml b/interface/resources/qml/hifi/LetterboxMessage.qml index c1063ea61c..66699eb753 100644 --- a/interface/resources/qml/hifi/LetterboxMessage.qml +++ b/interface/resources/qml/hifi/LetterboxMessage.qml @@ -33,7 +33,7 @@ Item { } Rectangle { width: Math.max(parent.width * 0.75, 400) - height: childrenRect.height + 50 + height: contentContainer.height + 50 anchors.centerIn: parent radius: popupRadius color: "white" diff --git a/interface/resources/qml/hifi/NameCard.qml b/interface/resources/qml/hifi/NameCard.qml index c0b8d1c614..08fa14ff97 100644 --- a/interface/resources/qml/hifi/NameCard.qml +++ b/interface/resources/qml/hifi/NameCard.qml @@ -261,7 +261,7 @@ Item { Rectangle { id: nameCardVUMeter // Size - width: isMyCard ? myDisplayName.width - 20 : ((gainSlider.value - gainSlider.minimumValue)/(gainSlider.maximumValue - gainSlider.minimumValue)) * parent.width + width: isMyCard ? myDisplayName.width - 70 : ((gainSlider.value - gainSlider.minimumValue)/(gainSlider.maximumValue - gainSlider.minimumValue)) * parent.width height: 8 // Anchors anchors.top: userNameSpacer.bottom diff --git a/interface/resources/qml/hifi/Pal.qml b/interface/resources/qml/hifi/Pal.qml index 273fc0b73c..dc6f698947 100644 --- a/interface/resources/qml/hifi/Pal.qml +++ b/interface/resources/qml/hifi/Pal.qml @@ -372,9 +372,11 @@ Rectangle { anchors.fill: parent acceptedButtons: Qt.LeftButton hoverEnabled: true - onClicked: letterbox("Bold names in the list are Avatar Display Names.\n" + - "If a Display Name isn't set, a unique Session Display Name is assigned." + - "\n\nAdministrators of this domain can also see the Username or Machine ID associated with each avatar present.", "", "") + onClicked: letterbox("Bold names in the list are avatar display names.
" + + "If a display name isn't set, a unique session display name is assigned." + + "

Administrators of this domain can also see the username or machine ID associated with each avatar present.", + hifi.glyphs.question, + "Display Names") onEntered: helpText.color = hifi.colors.baseGrayHighlight onExited: helpText.color = hifi.colors.darkGray } @@ -404,7 +406,7 @@ Rectangle { acceptedButtons: Qt.LeftButton hoverEnabled: true onClicked: letterbox("Silence mutes a user's microphone. Silenced users can unmute themselves by clicking "UNMUTE" on their toolbar.

" + - "Ban removes a user from this domain and prevents them from returning. Admins can un-ban users from the Sandbox Domain Settings Page.)", + "Ban removes a user from this domain and prevents them from returning. Admins can un-ban users from the Sandbox Domain Settings page.", hifi.glyphs.question, "Admin Actions") onEntered: adminHelpText.color = "#94132e" From 1175752e1dd411587e8157fee68d1c69918e9958 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Tue, 17 Jan 2017 18:05:03 -0800 Subject: [PATCH 077/109] Cleanup after rebase --- domain-server/src/DomainServerSettingsManager.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/domain-server/src/DomainServerSettingsManager.cpp b/domain-server/src/DomainServerSettingsManager.cpp index f4d7d324a6..8731167428 100644 --- a/domain-server/src/DomainServerSettingsManager.cpp +++ b/domain-server/src/DomainServerSettingsManager.cpp @@ -796,7 +796,8 @@ void DomainServerSettingsManager::processUsernameFromIDRequestPacket(QSharedPoin bool isAdmin = matchingNode->getCanKick(); // Check if the sending node has permission to kick (is an admin) - if (sendingNode->getCanKick()) { + // OR if the message is from a node whose UUID matches the one in the packet + if (sendingNode->getCanKick() || nodeUUID == sendingNode->getUUID()) { // It's time to figure out the username QString verifiedUsername = matchingNode->getPermissions().getVerifiedUserName(); From e9d3c36873ea57f42c48f1a824c859734228fc42 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Wed, 18 Jan 2017 12:47:45 -0800 Subject: [PATCH 078/109] Final commit? --- .../resources/qml/hifi/LetterboxMessage.qml | 109 +++++++++--------- interface/resources/qml/hifi/NameCard.qml | 5 +- interface/resources/qml/hifi/Pal.qml | 10 +- scripts/system/pal.js | 3 +- 4 files changed, 66 insertions(+), 61 deletions(-) diff --git a/interface/resources/qml/hifi/LetterboxMessage.qml b/interface/resources/qml/hifi/LetterboxMessage.qml index 66699eb753..e50d1de547 100644 --- a/interface/resources/qml/hifi/LetterboxMessage.qml +++ b/interface/resources/qml/hifi/LetterboxMessage.qml @@ -37,72 +37,75 @@ Item { anchors.centerIn: parent radius: popupRadius color: "white" - Column { + Item { id: contentContainer width: parent.width - 60 + height: childrenRect.height anchors.centerIn: parent - spacing: 20 - Row { + Item { id: popupHeaderContainer visible: headerText.text !== "" || headerGlyph.text !== "" height: 30 - Column { - // Header Glyph - HiFiGlyphs { - id: headerGlyph - visible: headerGlyph.text !== "" - // Size - height: parent.parent.height - // Anchors - anchors.left: parent.left - anchors.leftMargin: -15 - // Text Size - size: headerTextPixelSize*2.5 - // Style - horizontalAlignment: Text.AlignHLeft - verticalAlignment: Text.AlignVCenter - color: hifi.colors.darkGray - } - } - Column { - // Header Text - Text { - id: headerText - visible: headerText.text !== "" - // Size - height: parent.parent.height - // Anchors - anchors.left: parent.left - anchors.leftMargin: -15 - // Text Size - font.pixelSize: headerTextPixelSize - // Style - font.family: ralewaySemiBold.name - color: hifi.colors.darkGray - horizontalAlignment: Text.AlignHLeft - verticalAlignment: Text.AlignVCenter - wrapMode: Text.WordWrap - textFormat: Text.StyledText - } - } - } - Row { - // Popup Text - Text { - id: popupText + // Anchors + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + // Header Glyph + HiFiGlyphs { + id: headerGlyph + visible: headerGlyph.text !== "" // Size - width: parent.parent.width - // Text alignment - verticalAlignment: Text.AlignVCenter - horizontalAlignment: Text.AlignHLeft + height: parent.height + // Anchors + anchors.left: parent.left + anchors.leftMargin: -15 + // Text Size + size: headerTextPixelSize*2.5 // Style - font.pixelSize: popupTextPixelSize - font.family: ralewayRegular.name + horizontalAlignment: Text.AlignHLeft + verticalAlignment: Text.AlignVCenter color: hifi.colors.darkGray + } + // Header Text + Text { + id: headerText + visible: headerText.text !== "" + // Size + height: parent.height + // Anchors + anchors.left: headerGlyph.right + anchors.leftMargin: -5 + // Text Size + font.pixelSize: headerTextPixelSize + // Style + font.family: ralewaySemiBold.name + color: hifi.colors.darkGray + horizontalAlignment: Text.AlignHLeft + verticalAlignment: Text.AlignVCenter wrapMode: Text.WordWrap textFormat: Text.StyledText } } + // Popup Text + Text { + id: popupText + // Size + width: parent.width + // Anchors + anchors.top: popupHeaderContainer.visible ? popupHeaderContainer.bottom : parent.top + anchors.topMargin: popupHeaderContainer.visible ? 15 : 0 + anchors.left: parent.left + anchors.right: parent.right + // Text alignment + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignHLeft + // Style + font.pixelSize: popupTextPixelSize + font.family: ralewayRegular.name + color: hifi.colors.darkGray + wrapMode: Text.WordWrap + textFormat: Text.StyledText + } } } MouseArea { diff --git a/interface/resources/qml/hifi/NameCard.qml b/interface/resources/qml/hifi/NameCard.qml index 08fa14ff97..745c100202 100644 --- a/interface/resources/qml/hifi/NameCard.qml +++ b/interface/resources/qml/hifi/NameCard.qml @@ -164,7 +164,7 @@ Item { text: thisNameCard.displayName elide: Text.ElideRight // Size - width: Math.min(displayNameTextMetrics.tightBoundingRect.width, parent.width - adminLabelText.width - adminLabelQuestionMark.width) + width: isAdmin ? Math.min(displayNameTextMetrics.tightBoundingRect.width + 8, parent.width - adminLabelText.width - adminLabelQuestionMark.width + 8) : parent.width // Anchors anchors.top: parent.top anchors.left: parent.left @@ -183,13 +183,13 @@ Item { // "ADMIN" label for other users' cards RalewaySemiBold { id: adminLabelText + visible: isAdmin text: "ADMIN" // Text size size: displayNameText.size - 4 // Anchors anchors.verticalCenter: parent.verticalCenter anchors.left: displayNameText.right - anchors.leftMargin: 8 // Style font.capitalization: Font.AllUppercase color: hifi.colors.redHighlight @@ -200,6 +200,7 @@ Item { // This Rectangle refers to the [?] popup button next to "ADMIN" Item { id: adminLabelQuestionMark + visible: isAdmin // Size width: 20 height: displayNameText.height diff --git a/interface/resources/qml/hifi/Pal.qml b/interface/resources/qml/hifi/Pal.qml index dc6f698947..fe14016931 100644 --- a/interface/resources/qml/hifi/Pal.qml +++ b/interface/resources/qml/hifi/Pal.qml @@ -28,7 +28,7 @@ Rectangle { property int rowHeight: 70 property int actionButtonWidth: 75 property int nameCardWidth: palContainer.width - actionButtonWidth*(iAmAdmin ? 4 : 2) - 4 - hifi.dimensions.scrollbarBackgroundWidth - property var myData: ({displayName: "", userName: "", audioLevel: 0.0}) // valid dummy until set + property var myData: ({displayName: "", userName: "", audioLevel: 0.0, admin: true}) // valid dummy until set property var ignored: ({}); // Keep a local list of ignored avatars & their data. Necessary because HashMap is slow to respond after ignoring. property var userModelData: [] // This simple list is essentially a mirror of the userModel listModel without all the extra complexities. property bool iAmAdmin: false @@ -223,7 +223,7 @@ Rectangle { visible: !isCheckBox && !isButton uuid: model && model.sessionId selected: styleData.selected - isAdmin: model && model.isAdmin + isAdmin: model && model.admin // Size width: nameCardWidth height: parent.height @@ -472,7 +472,7 @@ Rectangle { var userId = message.params[0]; // The text that goes in the userName field is the second parameter in the message. var userName = message.params[1]; - var isAdmin = message.params[2]; + var admin = message.params[2]; // If the userId is empty, we're updating "myData". if (!userId) { myData.userName = userName; @@ -485,8 +485,8 @@ Rectangle { userModel.setProperty(userIndex, "userName", userName); userModelData[userIndex].userName = userName; // Defensive programming // Set the admin status appropriately - userModel.setProperty(userIndex, "isAdmin", isAdmin); - userModelData[userIndex].isAdmin = isAdmin; // Defensive programming + userModel.setProperty(userIndex, "admin", admin); + userModelData[userIndex].admin = admin; // Defensive programming } else { console.log("updateUsername() called with unknown UUID: ", userId); } diff --git a/scripts/system/pal.js b/scripts/system/pal.js index b95e9bdb87..7c6b8662c7 100644 --- a/scripts/system/pal.js +++ b/scripts/system/pal.js @@ -267,7 +267,8 @@ function populateUserList() { displayName: avatar.sessionDisplayName, userName: '', sessionId: id || '', - audioLevel: 0.0 + audioLevel: 0.0, + admin: false }; // Request the username, fingerprint, and admin status from the given UUID // Username and fingerprint returns "" if the requesting user isn't an admin From f8dd7f54330f18483b8688eefc2a4d3e7632336c Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Wed, 18 Jan 2017 12:54:48 -0800 Subject: [PATCH 079/109] Cleanup --- interface/resources/qml/hifi/NameCard.qml | 6 +++--- interface/resources/qml/hifi/Pal.qml | 24 +++++++++++------------ 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/interface/resources/qml/hifi/NameCard.qml b/interface/resources/qml/hifi/NameCard.qml index 745c100202..6fa3a46dca 100644 --- a/interface/resources/qml/hifi/NameCard.qml +++ b/interface/resources/qml/hifi/NameCard.qml @@ -221,9 +221,9 @@ Item { anchors.fill: parent acceptedButtons: Qt.LeftButton hoverEnabled: true - onClicked: letterbox("This user is an admin on this domain. Admins can Silence and Ban other users at their discretion - so be extra nice!", - hifi.glyphs.question, - "Domain Admin") + onClicked: letterbox(hifi.glyphs.question, + "Domain Admin", + "This user is an admin on this domain. Admins can Silence and Ban other users at their discretion - so be extra nice!") onEntered: adminLabelQuestionMarkText.color = "#94132e" onExited: adminLabelQuestionMarkText.color = hifi.colors.redHighlight } diff --git a/interface/resources/qml/hifi/Pal.qml b/interface/resources/qml/hifi/Pal.qml index fe14016931..0c4b26a1fe 100644 --- a/interface/resources/qml/hifi/Pal.qml +++ b/interface/resources/qml/hifi/Pal.qml @@ -41,10 +41,10 @@ Rectangle { id: letterboxMessage z: 999 // Force the popup on top of everything else } - function letterbox(message, headerGlyph, headerText) { - letterboxMessage.text = message + function letterbox(headerGlyph, headerText, message) { letterboxMessage.headerGlyph = headerGlyph letterboxMessage.headerText = headerText + letterboxMessage.text = message letterboxMessage.visible = true letterboxMessage.popupRadius = 0 } @@ -372,11 +372,11 @@ Rectangle { anchors.fill: parent acceptedButtons: Qt.LeftButton hoverEnabled: true - onClicked: letterbox("Bold names in the list are avatar display names.
" + + onClicked: letterbox(hifi.glyphs.question, + "Display Names", + "Bold names in the list are avatar display names.
" + "If a display name isn't set, a unique session display name is assigned." + - "

Administrators of this domain can also see the username or machine ID associated with each avatar present.", - hifi.glyphs.question, - "Display Names") + "

Administrators of this domain can also see the username or machine ID associated with each avatar present.") onEntered: helpText.color = hifi.colors.baseGrayHighlight onExited: helpText.color = hifi.colors.darkGray } @@ -405,10 +405,10 @@ Rectangle { anchors.fill: parent acceptedButtons: Qt.LeftButton hoverEnabled: true - onClicked: letterbox("Silence mutes a user's microphone. Silenced users can unmute themselves by clicking "UNMUTE" on their toolbar.

" + - "Ban removes a user from this domain and prevents them from returning. Admins can un-ban users from the Sandbox Domain Settings page.", - hifi.glyphs.question, - "Admin Actions") + onClicked: letterbox(hifi.glyphs.question, + "Admin Actions", + "Silence mutes a user's microphone. Silenced users can unmute themselves by clicking "UNMUTE" on their toolbar.

" + + "Ban removes a user from this domain and prevents them from returning. Admins can un-ban users from the Sandbox Domain Settings page.") onEntered: adminHelpText.color = "#94132e" onExited: adminHelpText.color = hifi.colors.redHighlight } @@ -453,9 +453,9 @@ Rectangle { var selected = message.params[1]; var userIndex = findSessionIndex(sessionIds[0]); if (sessionIds.length > 1) { - letterbox('Only one user can be selected at a time.', "", ""); + letterbox("", "", 'Only one user can be selected at a time.'); } else if (userIndex < 0) { - letterbox('The last editor is not among this list of users.', "", ""); + letterbox("", "", 'The last editor is not among this list of users.'); } else { if (selected) { table.selection.clear(); // for now, no multi-select From 7f57251a0e9dfec38ec62301cacfd8495e12d408 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Wed, 18 Jan 2017 13:38:19 -0800 Subject: [PATCH 080/109] Remove logs also removed in Master --- interface/resources/qml/hifi/Pal.qml | 4 ---- scripts/system/pal.js | 1 - 2 files changed, 5 deletions(-) diff --git a/interface/resources/qml/hifi/Pal.qml b/interface/resources/qml/hifi/Pal.qml index 0c4b26a1fe..09ae5223e7 100644 --- a/interface/resources/qml/hifi/Pal.qml +++ b/interface/resources/qml/hifi/Pal.qml @@ -487,8 +487,6 @@ Rectangle { // Set the admin status appropriately userModel.setProperty(userIndex, "admin", admin); userModelData[userIndex].admin = admin; // Defensive programming - } else { - console.log("updateUsername() called with unknown UUID: ", userId); } } break; @@ -504,8 +502,6 @@ Rectangle { if (userIndex != -1) { userModel.setProperty(userIndex, "audioLevel", audioLevel); userModelData[userIndex].audioLevel = audioLevel; // Defensive programming - } else { - console.log("updateAudioLevel() called with unknown UUID: ", userId); } } } diff --git a/scripts/system/pal.js b/scripts/system/pal.js index 7c6b8662c7..6e8d3bc163 100644 --- a/scripts/system/pal.js +++ b/scripts/system/pal.js @@ -497,7 +497,6 @@ function getAudioLevel(id) { var audioLevel = 0.0; var data = id ? ExtendedOverlay.get(id) : myData; if (!data) { - print('no data for', id); return audioLevel; } From 560247998841e390b2194646991532f93199de5f Mon Sep 17 00:00:00 2001 From: Gabriel Calero Date: Wed, 18 Jan 2017 19:18:48 -0300 Subject: [PATCH 081/109] Create render forward version of pixel shaders --- .../render-utils/src/RenderPipelines.cpp | 17 +++-- libraries/render-utils/src/forward_model.slf | 59 +++++++++++++++++ .../src/forward_model_normal_map.slf | 64 ++++++++++++++++++ .../src/forward_model_normal_specular_map.slf | 66 +++++++++++++++++++ .../src/forward_model_specular_map.slf | 63 ++++++++++++++++++ .../render-utils/src/forward_model_unlit.slf | 45 +++++++++++++ 6 files changed, 309 insertions(+), 5 deletions(-) create mode 100644 libraries/render-utils/src/forward_model.slf create mode 100644 libraries/render-utils/src/forward_model_normal_map.slf create mode 100644 libraries/render-utils/src/forward_model_normal_specular_map.slf create mode 100644 libraries/render-utils/src/forward_model_specular_map.slf create mode 100644 libraries/render-utils/src/forward_model_unlit.slf diff --git a/libraries/render-utils/src/RenderPipelines.cpp b/libraries/render-utils/src/RenderPipelines.cpp index b0d676ef78..c5a6c4b6ca 100644 --- a/libraries/render-utils/src/RenderPipelines.cpp +++ b/libraries/render-utils/src/RenderPipelines.cpp @@ -34,6 +34,13 @@ #include "model_normal_map_frag.h" #include "model_normal_specular_map_frag.h" #include "model_specular_map_frag.h" + +#include "forward_model_frag.h" +#include "forward_model_unlit_frag.h" +#include "forward_model_normal_map_frag.h" +#include "forward_model_normal_specular_map_frag.h" +#include "forward_model_specular_map_frag.h" + #include "model_lightmap_frag.h" #include "model_lightmap_normal_map_frag.h" #include "model_lightmap_normal_specular_map_frag.h" @@ -227,11 +234,11 @@ void initForwardPipelines(render::ShapePlumber& plumber) { auto skinModelNormalMapVertex = gpu::Shader::createVertex(std::string(skin_model_normal_map_vert)); // Pixel shaders - auto modelPixel = gpu::Shader::createPixel(std::string(model_frag)); - auto modelUnlitPixel = gpu::Shader::createPixel(std::string(model_unlit_frag)); - auto modelNormalMapPixel = gpu::Shader::createPixel(std::string(model_normal_map_frag)); - auto modelSpecularMapPixel = gpu::Shader::createPixel(std::string(model_specular_map_frag)); - auto modelNormalSpecularMapPixel = gpu::Shader::createPixel(std::string(model_normal_specular_map_frag)); + auto modelPixel = gpu::Shader::createPixel(std::string(forward_model_frag)); + auto modelUnlitPixel = gpu::Shader::createPixel(std::string(forward_model_unlit_frag)); + auto modelNormalMapPixel = gpu::Shader::createPixel(std::string(forward_model_normal_map_frag)); + auto modelSpecularMapPixel = gpu::Shader::createPixel(std::string(forward_model_specular_map_frag)); + auto modelNormalSpecularMapPixel = gpu::Shader::createPixel(std::string(forward_model_normal_specular_map_frag)); using Key = render::ShapeKey; auto addPipeline = std::bind(&addPlumberPipeline, std::ref(plumber), _1, _2, _3); diff --git a/libraries/render-utils/src/forward_model.slf b/libraries/render-utils/src/forward_model.slf new file mode 100644 index 0000000000..daeead65ec --- /dev/null +++ b/libraries/render-utils/src/forward_model.slf @@ -0,0 +1,59 @@ +<@include gpu/Config.slh@> +<$VERSION_HEADER$> +// Generated on <$_SCRIBE_DATE$> +// model.frag +// fragment shader +// +// Created by Andrzej Kapolka on 10/14/13. +// Copyright 2013 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +<@include DeferredBufferWrite.slh@> + +<@include model/Material.slh@> + +<@include MaterialTextures.slh@> +<$declareMaterialTextures(ALBEDO, ROUGHNESS, _SCRIBE_NULL, _SCRIBE_NULL, EMISSIVE, OCCLUSION)$> + +in vec4 _position; +in vec3 _normal; +in vec3 _color; +in vec2 _texCoord0; +in vec2 _texCoord1; + + +void main(void) { + Material mat = getMaterial(); + int matKey = getMaterialKey(mat); + <$fetchMaterialTexturesCoord0(matKey, _texCoord0, albedoTex, roughnessTex, _SCRIBE_NULL, _SCRIBE_NULL, emissiveTex)$> + <$fetchMaterialTexturesCoord1(matKey, _texCoord1, occlusionTex)$> + + float opacity = 1.0; + <$evalMaterialOpacity(albedoTex.a, opacity, matKey, opacity)$>; + <$discardTransparent(opacity)$>; + + vec3 albedo = getMaterialAlbedo(mat); + <$evalMaterialAlbedo(albedoTex, albedo, matKey, albedo)$>; + albedo *= _color; + + float roughness = getMaterialRoughness(mat); + <$evalMaterialRoughness(roughnessTex, roughness, matKey, roughness)$>; + + vec3 emissive = getMaterialEmissive(mat); + <$evalMaterialEmissive(emissiveTex, emissive, matKey, emissive)$>; + + float scattering = getMaterialScattering(mat); + + packDeferredFragment( + normalize(_normal.xyz), + opacity, + albedo, + roughness, + getMaterialMetallic(mat), + emissive, + occlusionTex, + scattering); +} diff --git a/libraries/render-utils/src/forward_model_normal_map.slf b/libraries/render-utils/src/forward_model_normal_map.slf new file mode 100644 index 0000000000..3acdedab2a --- /dev/null +++ b/libraries/render-utils/src/forward_model_normal_map.slf @@ -0,0 +1,64 @@ +<@include gpu/Config.slh@> +<$VERSION_HEADER$> +// Generated on <$_SCRIBE_DATE$> +// +// model_normal_map.frag +// fragment shader +// +// Created by Andrzej Kapolka on 10/29/13. +// Copyright 2013 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +<@include DeferredBufferWrite.slh@> + +<@include model/Material.slh@> + +<@include MaterialTextures.slh@> +<$declareMaterialTextures(ALBEDO, ROUGHNESS, NORMAL, _SCRIBE_NULL, EMISSIVE, OCCLUSION, SCATTERING)$> + +in vec4 _position; +in vec2 _texCoord0; +in vec2 _texCoord1; +in vec3 _normal; +in vec3 _tangent; +in vec3 _color; + +void main(void) { + Material mat = getMaterial(); + int matKey = getMaterialKey(mat); + <$fetchMaterialTexturesCoord0(matKey, _texCoord0, albedoTex, roughnessTex, normalTex, _SCRIBE_NULL, emissiveTex, scatteringTex)$> + <$fetchMaterialTexturesCoord1(matKey, _texCoord1, occlusionTex)$> + + float opacity = 1.0; + <$evalMaterialOpacity(albedoTex.a, opacity, matKey, opacity)$>; + <$discardTransparent(opacity)$>; + + vec3 albedo = getMaterialAlbedo(mat); + <$evalMaterialAlbedo(albedoTex, albedo, matKey, albedo)$>; + albedo *= _color; + + float roughness = getMaterialRoughness(mat); + <$evalMaterialRoughness(roughnessTex, roughness, matKey, roughness)$>; + + vec3 emissive = getMaterialEmissive(mat); + <$evalMaterialEmissive(emissiveTex, emissive, matKey, emissive)$>; + + vec3 viewNormal; + <$tangentToViewSpace(normalTex, _normal, _tangent, viewNormal)$> + + float scattering = getMaterialScattering(mat); + <$evalMaterialScattering(scatteringTex, scattering, matKey, scattering)$>; + + packDeferredFragment( + viewNormal, + opacity, + albedo, + roughness, + getMaterialMetallic(mat), + emissive, + occlusionTex, + scattering); +} diff --git a/libraries/render-utils/src/forward_model_normal_specular_map.slf b/libraries/render-utils/src/forward_model_normal_specular_map.slf new file mode 100644 index 0000000000..d5dd607b8f --- /dev/null +++ b/libraries/render-utils/src/forward_model_normal_specular_map.slf @@ -0,0 +1,66 @@ +<@include gpu/Config.slh@> +<$VERSION_HEADER$> +// Generated on <$_SCRIBE_DATE$> +// +// model_normal_specular_map.frag +// fragment shader +// +// Created by Andrzej Kapolka on 5/6/14. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +<@include DeferredBufferWrite.slh@> + +<@include model/Material.slh@> + +<@include MaterialTextures.slh@> +<$declareMaterialTextures(ALBEDO, ROUGHNESS, NORMAL, METALLIC, EMISSIVE, OCCLUSION)$> + +in vec4 _position; +in vec2 _texCoord0; +in vec2 _texCoord1; +in vec3 _normal; +in vec3 _tangent; +in vec3 _color; + +void main(void) { + Material mat = getMaterial(); + int matKey = getMaterialKey(mat); + <$fetchMaterialTexturesCoord0(matKey, _texCoord0, albedoTex, roughnessTex, normalTex, metallicTex, emissiveTex)$> + <$fetchMaterialTexturesCoord1(matKey, _texCoord1, occlusionTex)$> + + float opacity = 1.0; + <$evalMaterialOpacity(albedoTex.a, opacity, matKey, opacity)&>; + <$discardTransparent(opacity)$>; + + vec3 albedo = getMaterialAlbedo(mat); + <$evalMaterialAlbedo(albedoTex, albedo, matKey, albedo)$>; + albedo *= _color; + + float roughness = getMaterialRoughness(mat); + <$evalMaterialRoughness(roughnessTex, roughness, matKey, roughness)$>; + + vec3 emissive = getMaterialEmissive(mat); + <$evalMaterialEmissive(emissiveTex, emissive, matKey, emissive)$>; + + vec3 viewNormal; + <$tangentToViewSpace(normalTex, _normal, _tangent, viewNormal)$> + + float metallic = getMaterialMetallic(mat); + <$evalMaterialMetallic(metallicTex, metallic, matKey, metallic)$>; + + float scattering = getMaterialScattering(mat); + + packDeferredFragment( + normalize(viewNormal.xyz), + opacity, + albedo, + roughness, + metallic, + emissive, + occlusionTex, + scattering); +} diff --git a/libraries/render-utils/src/forward_model_specular_map.slf b/libraries/render-utils/src/forward_model_specular_map.slf new file mode 100644 index 0000000000..47b5e3389d --- /dev/null +++ b/libraries/render-utils/src/forward_model_specular_map.slf @@ -0,0 +1,63 @@ +<@include gpu/Config.slh@> +<$VERSION_HEADER$> +// Generated on <$_SCRIBE_DATE$> +// +// model_specular_map.frag +// fragment shader +// +// Created by Andrzej Kapolka on 5/6/14. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +<@include DeferredBufferWrite.slh@> + +<@include model/Material.slh@> + +<@include MaterialTextures.slh@> +<$declareMaterialTextures(ALBEDO, ROUGHNESS, _SCRIBE_NULL, METALLIC, EMISSIVE, OCCLUSION)$> + +in vec4 _position; +in vec2 _texCoord0; +in vec2 _texCoord1; +in vec3 _normal; +in vec3 _color; + + +void main(void) { + Material mat = getMaterial(); + int matKey = getMaterialKey(mat); + <$fetchMaterialTexturesCoord0(matKey, _texCoord0, albedoTex, roughnessTex, _SCRIBE_NULL, metallicTex, emissiveTex)$> + <$fetchMaterialTexturesCoord1(matKey, _texCoord1, occlusionTex)$> + + float opacity = 1.0; + <$evalMaterialOpacity(albedoTex.a, opacity, matKey, opacity)$>; + <$discardTransparent(opacity)$>; + + vec3 albedo = getMaterialAlbedo(mat); + <$evalMaterialAlbedo(albedoTex, albedo, matKey, albedo)$>; + albedo *= _color; + + float roughness = getMaterialRoughness(mat); + <$evalMaterialRoughness(roughnessTex, roughness, matKey, roughness)$>; + + vec3 emissive = getMaterialEmissive(mat); + <$evalMaterialEmissive(emissiveTex, emissive, matKey, emissive)$>; + + float metallic = getMaterialMetallic(mat); + <$evalMaterialMetallic(metallicTex, metallic, matKey, metallic)$>; + + float scattering = getMaterialScattering(mat); + + packDeferredFragment( + normalize(_normal), + opacity, + albedo, + roughness, + metallic, + emissive, + occlusionTex, + scattering); +} diff --git a/libraries/render-utils/src/forward_model_unlit.slf b/libraries/render-utils/src/forward_model_unlit.slf new file mode 100644 index 0000000000..750b51fe8c --- /dev/null +++ b/libraries/render-utils/src/forward_model_unlit.slf @@ -0,0 +1,45 @@ +<@include gpu/Config.slh@> +<$VERSION_HEADER$> +// Generated on <$_SCRIBE_DATE$> +// +// material_opaque_unlit.frag +// fragment shader +// +// Created by Sam Gateau on 5/5/2016. +// Copyright 2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +<@include DeferredBufferWrite.slh@> +<@include LightingModel.slh@> +<@include model/Material.slh@> + +<@include MaterialTextures.slh@> +<$declareMaterialTextures(ALBEDO)$> + +in vec2 _texCoord0; +in vec3 _normal; +in vec3 _color; +in float _alpha; + +void main(void) { + + Material mat = getMaterial(); + int matKey = getMaterialKey(mat); + <$fetchMaterialTexturesCoord0(matKey, _texCoord0, albedoTex)$> + + float opacity = 1.0; + <$evalMaterialOpacity(albedoTex.a, opacity, matKey, opacity)$>; + <$discardTransparent(opacity)$>; + + vec3 albedo = getMaterialAlbedo(mat); + <$evalMaterialAlbedo(albedoTex, albedo, matKey, albedo)$>; + albedo *= _color; + + packDeferredFragmentUnlit( + normalize(_normal), + opacity, + albedo * isUnlitEnabled()); +} From 0ec01c0dc573c80279aaf8a516feb3a4bc75286a Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Wed, 18 Jan 2017 14:55:53 -0800 Subject: [PATCH 082/109] CR feedback --- libraries/networking/src/NodeList.cpp | 30 ++++++++++++--------------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/libraries/networking/src/NodeList.cpp b/libraries/networking/src/NodeList.cpp index 7c5e7fc5b7..7bfe1d1845 100644 --- a/libraries/networking/src/NodeList.cpp +++ b/libraries/networking/src/NodeList.cpp @@ -1040,25 +1040,20 @@ void NodeList::muteNodeBySessionID(const QUuid& nodeID) { } void NodeList::requestUsernameFromSessionID(const QUuid& nodeID) { - // send a request to domain-server to get the username associated with the given session ID - if (getThisNodeCanKick() || nodeID.isNull()) { - // setup the packet - auto usernameFromIDRequestPacket = NLPacket::create(PacketType::UsernameFromIDRequest, NUM_BYTES_RFC4122_UUID, true); + // send a request to domain-server to get the username/machine fingerprint/admin status associated with the given session ID + // If the requesting user isn't an admin, the username and machine fingerprint will return "". + auto usernameFromIDRequestPacket = NLPacket::create(PacketType::UsernameFromIDRequest, NUM_BYTES_RFC4122_UUID, true); - // write the node ID to the packet - if (nodeID.isNull()) { - usernameFromIDRequestPacket->write(getSessionUUID().toRfc4122()); - } else { - usernameFromIDRequestPacket->write(nodeID.toRfc4122()); - } - - qCDebug(networking) << "Sending packet to get username of node" << uuidStringWithoutCurlyBraces(nodeID); - - sendPacket(std::move(usernameFromIDRequestPacket), _domainHandler.getSockAddr()); + // write the node ID to the packet + if (nodeID.isNull()) { + usernameFromIDRequestPacket->write(getSessionUUID().toRfc4122()); } else { - qWarning() << "You do not have permissions to kick in this domain." - << "Request to get the username of node" << uuidStringWithoutCurlyBraces(nodeID) << "will not be sent"; + usernameFromIDRequestPacket->write(nodeID.toRfc4122()); } + + qCDebug(networking) << "Sending packet to get username/fingerprint/admin status of node" << uuidStringWithoutCurlyBraces(nodeID); + + sendPacket(std::move(usernameFromIDRequestPacket), _domainHandler.getSockAddr()); } void NodeList::processUsernameFromIDReply(QSharedPointer message) { @@ -1071,7 +1066,8 @@ void NodeList::processUsernameFromIDReply(QSharedPointer messag bool isAdmin; message->readPrimitive(&isAdmin); - qCDebug(networking) << "Got username" << username << "and machine fingerprint" << machineFingerprintString << "for node" << nodeUUIDString; + qCDebug(networking) << "Got username" << username << "and machine fingerprint" + << machineFingerprintString << "for node" << nodeUUIDString << ". isAdmin:" << isAdmin; emit usernameFromIDReply(nodeUUIDString, username, machineFingerprintString, isAdmin); } From e3da6aa9f1812da18121074929a65da0e38185aa Mon Sep 17 00:00:00 2001 From: samcake Date: Wed, 18 Jan 2017 15:03:31 -0800 Subject: [PATCH 083/109] Removing the comment --- libraries/render/src/render/CullTask.h | 1 - 1 file changed, 1 deletion(-) diff --git a/libraries/render/src/render/CullTask.h b/libraries/render/src/render/CullTask.h index 3397f9e41c..1a709ed102 100644 --- a/libraries/render/src/render/CullTask.h +++ b/libraries/render/src/render/CullTask.h @@ -170,7 +170,6 @@ namespace render { for (size_t i = 0; i < NUM_FILTERS; i++) { if (_filters[i].test(itemKey)) { outItems[i].template edit().emplace_back(itemBound); - // break; } } } From 1ef4ac9a6c6b62190a2e6946bfcb5c8a82e28a07 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Wed, 18 Jan 2017 11:06:57 -0800 Subject: [PATCH 084/109] Additional trace details for GPU timing, frame data updates --- interface/src/Application.cpp | 14 +++++++++++--- .../src/display-plugins/OpenGLDisplayPlugin.cpp | 17 +++++++++++------ 2 files changed, 22 insertions(+), 9 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 4f35b10a1b..8abf63f9f2 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -3398,6 +3398,8 @@ void Application::idle(float nsecsElapsed) { PROFILE_COUNTER(app, "cpuSystem", { { "system", kernelUserAndSystem.z } }); #endif + + auto displayPlugin = getActiveDisplayPlugin(); if (displayPlugin) { PROFILE_COUNTER_IF_CHANGED(app, "present", float, displayPlugin->presentRate()); @@ -3407,9 +3409,15 @@ void Application::idle(float nsecsElapsed) { PROFILE_COUNTER_IF_CHANGED(app, "pendingDownloads", int, ResourceCache::getPendingRequestCount()); PROFILE_COUNTER_IF_CHANGED(app, "currentProcessing", int, DependencyManager::get()->getStat("Processing").toInt()); PROFILE_COUNTER_IF_CHANGED(app, "pendingProcessing", int, DependencyManager::get()->getStat("PendingProcessing").toInt()); - - - + auto renderConfig = _renderEngine->getConfiguration(); + PROFILE_COUNTER_IF_CHANGED(render, "gpuTime", float, (float)_gpuContext->getFrameTimerGPUAverage()); + PROFILE_COUNTER(render_detail, "gpuTimes", { + { "OpaqueRangeTimer", renderConfig->getConfig("OpaqueRangeTimer")->property("gpuRunTime") }, + { "LinearDepth", renderConfig->getConfig("LinearDepth")->property("gpuRunTime") }, + { "SurfaceGeometry", renderConfig->getConfig("SurfaceGeometry")->property("gpuRunTime") }, + { "RenderDeferred", renderConfig->getConfig("RenderDeferred")->property("gpuRunTime") }, + { "ToneAndPostRangeTimer", renderConfig->getConfig("ToneAndPostRangeTimer")->property("gpuRunTime") } + }); PROFILE_RANGE(app, __FUNCTION__); diff --git a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp index e57c2b1d52..574c574f66 100644 --- a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp @@ -482,6 +482,7 @@ void OpenGLDisplayPlugin::submitFrame(const gpu::FramePointer& newFrame) { } void OpenGLDisplayPlugin::updateFrameData() { + PROFILE_RANGE(render, __FUNCTION__) if (_lockCurrentTexture) { return; } @@ -596,12 +597,16 @@ void OpenGLDisplayPlugin::internalPresent() { } void OpenGLDisplayPlugin::present() { - PROFILE_RANGE_EX(render, __FUNCTION__, 0xffffff00, (uint64_t)presentCount()) - updateFrameData(); + auto frameId = (uint64_t)presentCount(); + PROFILE_RANGE_EX(render, __FUNCTION__, 0xffffff00, frameId) + { + PROFILE_RANGE_EX(render, "updateFrameData", 0xff00ff00, frameId) + updateFrameData(); + } incrementPresentCount(); { - PROFILE_RANGE_EX(render, "recycle", 0xff00ff00, (uint64_t)presentCount()) + PROFILE_RANGE_EX(render, "recycle", 0xff00ff00, frameId) _gpuContext->recycle(); } @@ -615,19 +620,19 @@ void OpenGLDisplayPlugin::present() { _lastFrame = _currentFrame.get(); }); // Execute the frame rendering commands - PROFILE_RANGE_EX(render, "execute", 0xff00ff00, (uint64_t)presentCount()) + PROFILE_RANGE_EX(render, "execute", 0xff00ff00, frameId) _gpuContext->executeFrame(_currentFrame); } // Write all layers to a local framebuffer { - PROFILE_RANGE_EX(render, "composite", 0xff00ffff, (uint64_t)presentCount()) + PROFILE_RANGE_EX(render, "composite", 0xff00ffff, frameId) compositeLayers(); } // Take the composite framebuffer and send it to the output device { - PROFILE_RANGE_EX(render, "internalPresent", 0xff00ffff, (uint64_t)presentCount()) + PROFILE_RANGE_EX(render, "internalPresent", 0xff00ffff, frameId) internalPresent(); } From 027bd30cbc9e80495d9c691efab388c92ff39cbb Mon Sep 17 00:00:00 2001 From: samcake Date: Wed, 18 Jan 2017 16:59:50 -0800 Subject: [PATCH 085/109] Removing macOS warning --- libraries/render/src/render/Item.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/render/src/render/Item.h b/libraries/render/src/render/Item.h index 27bc2b790a..551b48fd7d 100644 --- a/libraries/render/src/render/Item.h +++ b/libraries/render/src/render/Item.h @@ -441,7 +441,7 @@ public: virtual const ShapeKey getShapeKey() const override { return shapeGetShapeKey(_data); } // Meta Type Interface - virtual uint32_t fetchMetaSubItems(ItemIDs& subItems) const { return metaFetchMetaSubItems(_data, subItems); } + virtual uint32_t fetchMetaSubItems(ItemIDs& subItems) const override { return metaFetchMetaSubItems(_data, subItems); } protected: DataPointer _data; From 534fcd93990cde5e4cddebf5ed60d86182f3d08a Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Thu, 19 Jan 2017 10:20:36 -0800 Subject: [PATCH 086/109] Potential fix --- .../src/DomainServerSettingsManager.cpp | 22 ++++++++++--------- .../src/UsersScriptingInterface.h | 2 +- scripts/system/pal.js | 12 +++++----- 3 files changed, 20 insertions(+), 16 deletions(-) diff --git a/domain-server/src/DomainServerSettingsManager.cpp b/domain-server/src/DomainServerSettingsManager.cpp index 8731167428..f7859ac1e0 100644 --- a/domain-server/src/DomainServerSettingsManager.cpp +++ b/domain-server/src/DomainServerSettingsManager.cpp @@ -793,30 +793,32 @@ void DomainServerSettingsManager::processUsernameFromIDRequestPacket(QSharedPoin // Setup the packet auto usernameFromIDReplyPacket = NLPacket::create(PacketType::UsernameFromIDReply); - bool isAdmin = matchingNode->getCanKick(); + QString verifiedUsername; + QUuid machineFingerprint; + + // Write the UUID to the packet + usernameFromIDReplyPacket->write(nodeUUID.toRfc4122()); // Check if the sending node has permission to kick (is an admin) // OR if the message is from a node whose UUID matches the one in the packet if (sendingNode->getCanKick() || nodeUUID == sendingNode->getUUID()) { // It's time to figure out the username - QString verifiedUsername = matchingNode->getPermissions().getVerifiedUserName(); - - usernameFromIDReplyPacket->write(nodeUUID.toRfc4122()); + verifiedUsername = matchingNode->getPermissions().getVerifiedUserName(); usernameFromIDReplyPacket->writeString(verifiedUsername); // now put in the machine fingerprint DomainServerNodeData* nodeData = reinterpret_cast(matchingNode->getLinkedData()); - QUuid machineFingerprint = nodeData ? nodeData->getMachineFingerprint() : QUuid(); + machineFingerprint = nodeData ? nodeData->getMachineFingerprint() : QUuid(); usernameFromIDReplyPacket->write(machineFingerprint.toRfc4122()); - qDebug() << "Sending username" << verifiedUsername << "and machine fingerprint" << machineFingerprint << "associated with node" << nodeUUID << ". Node admin status: " << isAdmin; } else { - usernameFromIDReplyPacket->write(nodeUUID.toRfc4122()); - usernameFromIDReplyPacket->writeString(""); - usernameFromIDReplyPacket->writeString(""); + usernameFromIDReplyPacket->writeString(verifiedUsername); + usernameFromIDReplyPacket->writeString(machineFingerprint.toRfc4122()); } // Write whether or not the user is an admin + bool isAdmin = matchingNode->getCanKick(); usernameFromIDReplyPacket->writePrimitive(isAdmin); - + + qDebug() << "Sending username" << verifiedUsername << "and machine fingerprint" << machineFingerprint << "associated with node" << nodeUUID << ". Node admin status: " << isAdmin; // Ship it! limitedNodeList->sendPacket(std::move(usernameFromIDReplyPacket), *sendingNode); } else { diff --git a/libraries/script-engine/src/UsersScriptingInterface.h b/libraries/script-engine/src/UsersScriptingInterface.h index 5ae1d5a75b..76b98c6217 100644 --- a/libraries/script-engine/src/UsersScriptingInterface.h +++ b/libraries/script-engine/src/UsersScriptingInterface.h @@ -135,7 +135,7 @@ signals: /**jsdoc * Notifies scripts of the username and machine fingerprint associated with a UUID. - * Username and machineFingerprint will be empty strings if the requesting user isn't an admin. + * Username and machineFingerprint will be their default constructor output if the requesting user isn't an admin. * @function Users.usernameFromIDReply */ void usernameFromIDReply(const QString& nodeID, const QString& username, const QString& machineFingerprint, bool isAdmin); diff --git a/scripts/system/pal.js b/scripts/system/pal.js index 6e8d3bc163..cbfac76545 100644 --- a/scripts/system/pal.js +++ b/scripts/system/pal.js @@ -271,7 +271,7 @@ function populateUserList() { admin: false }; // Request the username, fingerprint, and admin status from the given UUID - // Username and fingerprint returns "" if the requesting user isn't an admin + // Username and fingerprint returns default constructor output if the requesting user isn't an admin Users.requestUsernameFromID(id); // Request personal mute status and ignore status // from NodeList (as long as we're not requesting it for our own ID) @@ -292,13 +292,15 @@ function usernameFromIDReply(id, username, machineFingerprint, isAdmin) { // If the ID we've received is our ID... if (MyAvatar.sessionUUID === id) { // Set the data to contain specific strings. - data = ['', username]; - } else { + data = ['', username, isAdmin]; + } else if (Users.canKick) { // Set the data to contain the ID and the username (if we have one) // or fingerprint (if we don't have a username) string. - data = [id, username || machineFingerprint]; + data = [id, username || machineFingerprint, isAdmin]; + } else { + // Set the data to contain specific strings. + data = [id, '', isAdmin]; } - data.push(isAdmin); print('Username Data:', JSON.stringify(data)); // Ship the data off to QML pal.sendToQml({ method: 'updateUsername', params: data }); From 44eaa64a1d6f681cf9543ff52c9481ea27535896 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Thu, 19 Jan 2017 11:25:17 -0800 Subject: [PATCH 087/109] It was writeString() all along! --- domain-server/src/DomainServerSettingsManager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/domain-server/src/DomainServerSettingsManager.cpp b/domain-server/src/DomainServerSettingsManager.cpp index f7859ac1e0..5e1d747604 100644 --- a/domain-server/src/DomainServerSettingsManager.cpp +++ b/domain-server/src/DomainServerSettingsManager.cpp @@ -812,7 +812,7 @@ void DomainServerSettingsManager::processUsernameFromIDRequestPacket(QSharedPoin usernameFromIDReplyPacket->write(machineFingerprint.toRfc4122()); } else { usernameFromIDReplyPacket->writeString(verifiedUsername); - usernameFromIDReplyPacket->writeString(machineFingerprint.toRfc4122()); + usernameFromIDReplyPacket->write(machineFingerprint.toRfc4122()); } // Write whether or not the user is an admin bool isAdmin = matchingNode->getCanKick(); From d9fbf832e248bb96fc06a6b22c62bf3f93d1468e Mon Sep 17 00:00:00 2001 From: David Kelly Date: Thu, 19 Jan 2017 11:35:17 -0800 Subject: [PATCH 088/109] Some initial analytics for PAL usage --- interface/resources/qml/hifi/Pal.qml | 2 ++ interface/src/Application.cpp | 2 ++ .../src/UserActivityLoggerScriptingInterface.cpp | 15 +++++++++++++++ .../src/UserActivityLoggerScriptingInterface.h | 3 ++- scripts/system/pal.js | 13 +++++++++++++ 5 files changed, 34 insertions(+), 1 deletion(-) diff --git a/interface/resources/qml/hifi/Pal.qml b/interface/resources/qml/hifi/Pal.qml index 7260cd1c14..1ce6ab5e7d 100644 --- a/interface/resources/qml/hifi/Pal.qml +++ b/interface/resources/qml/hifi/Pal.qml @@ -247,6 +247,7 @@ Rectangle { userModel.setProperty(model.userIndex, styleData.role, newValue) userModelData[model.userIndex][styleData.role] = newValue // Defensive programming Users[styleData.role](model.sessionId, newValue) + UserActivityLogger["palAction"](newValue ? "un-" + styleData.role : styleData.role, model.sessionId) if (styleData.role === "ignore") { userModel.setProperty(model.userIndex, "personalMute", newValue) userModelData[model.userIndex]["personalMute"] = newValue // Defensive programming @@ -273,6 +274,7 @@ Rectangle { height: 24 onClicked: { Users[styleData.role](model.sessionId) + UserActivityLogger["palAction"](styleData.role, model.sessionId) if (styleData.role === "kick") { // Just for now, while we cannot undo "Ban": userModel.remove(model.userIndex) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 8abf63f9f2..3d109a6549 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1937,6 +1937,8 @@ void Application::initializeUi() { rootContext->setContextProperty("AvatarList", DependencyManager::get().data()); rootContext->setContextProperty("Users", DependencyManager::get().data()); + rootContext->setContextProperty("UserActivityLogger", DependencyManager::get().data()); + rootContext->setContextProperty("Camera", &_myCamera); #if defined(Q_OS_MAC) || defined(Q_OS_WIN) diff --git a/libraries/networking/src/UserActivityLoggerScriptingInterface.cpp b/libraries/networking/src/UserActivityLoggerScriptingInterface.cpp index fa8cd9abd9..02d1711230 100644 --- a/libraries/networking/src/UserActivityLoggerScriptingInterface.cpp +++ b/libraries/networking/src/UserActivityLoggerScriptingInterface.cpp @@ -38,6 +38,21 @@ void UserActivityLoggerScriptingInterface::tutorialProgress( QString stepName, i } +void UserActivityLoggerScriptingInterface::palAction(QString action, QString target) { + QJsonObject payload; + payload["action"] = action; + if (target.length() > 0) { + payload["target"] = target; + } + logAction("pal_activity", payload); +} + +void UserActivityLoggerScriptingInterface::palOpened(float secondsOpened) { + logAction("pal_opened", { + { "seconds_opened", secondsOpened } + }); +} + void UserActivityLoggerScriptingInterface::logAction(QString action, QJsonObject details) { QMetaObject::invokeMethod(&UserActivityLogger::getInstance(), "logAction", Q_ARG(QString, action), diff --git a/libraries/networking/src/UserActivityLoggerScriptingInterface.h b/libraries/networking/src/UserActivityLoggerScriptingInterface.h index 07459967bc..a202858a1c 100644 --- a/libraries/networking/src/UserActivityLoggerScriptingInterface.h +++ b/libraries/networking/src/UserActivityLoggerScriptingInterface.h @@ -25,7 +25,8 @@ public: Q_INVOKABLE void toggledAway(bool isAway); Q_INVOKABLE void tutorialProgress(QString stepName, int stepNumber, float secondsToComplete, float tutorialElapsedTime, QString tutorialRunID = "", int tutorialVersion = 0, QString controllerType = ""); - + Q_INVOKABLE void palAction(QString action, QString target); + Q_INVOKABLE void palOpened(float secondsOpen); private: void logAction(QString action, QJsonObject details = {}); }; diff --git a/scripts/system/pal.js b/scripts/system/pal.js index c61dbba872..3d91bd27b2 100644 --- a/scripts/system/pal.js +++ b/scripts/system/pal.js @@ -208,6 +208,7 @@ pal.fromQml.connect(function (message) { // messages are {method, params}, like var id = overlay.key; var selected = ExtendedOverlay.isSelected(id); overlay.select(selected); + UserActivityLogger.palAction("avatar_selected", id); }); HighlightedEntity.clearOverlays(); @@ -232,14 +233,17 @@ pal.fromQml.connect(function (message) { // messages are {method, params}, like case 'refresh': removeOverlays(); populateUserList(); + UserActivityLogger.palAction("refresh"); break; case 'updateGain': data = message.params; Users.setAvatarGain(data['sessionId'], data['gain']); + UserActivityLogger.palAction("avatar_gain_changed", data['sessionId']); break; case 'displayNameUpdate': if (MyAvatar.displayName != message.params) { MyAvatar.displayName = message.params; + UserActivityLogger.palAction("display_name_change"); } break; default: @@ -551,7 +555,10 @@ var button = toolBar.addButton({ buttonState: 1, alpha: 0.9 }); + var isWired = false; +var palOpenedAt; + function off() { if (isWired) { // It is not ok to disconnect these twice, hence guard. Script.update.disconnect(updateOverlays); @@ -563,6 +570,11 @@ function off() { triggerPressMapping.disable(); // see above removeOverlays(); Users.requestsDomainListData = false; + if (palOpenedAt) { + var duration = new Date().getTime() - palOpenedAt; + UserActivityLogger.palOpened(duration / 1000.0); + palOpenedAt = 0; // just a falsy number is good enough. + } if (audioInterval) { Script.clearInterval(audioInterval); } @@ -579,6 +591,7 @@ function onClicked() { triggerMapping.enable(); triggerPressMapping.enable(); createAudioInterval(); + palOpenedAt = new Date().getTime(); } else { off(); } From 3dd8fd838b7a81b2b6ff0b0c892006dde802c176 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 18 Jan 2017 10:34:41 -0800 Subject: [PATCH 089/109] move cauterization out of Model class --- .../src/avatar/CauterizedMeshPartPayload.cpp | 72 ++++++ .../src/avatar/CauterizedMeshPartPayload.h | 29 +++ interface/src/avatar/CauterizedModel.cpp | 236 ++++++++++++++++++ interface/src/avatar/CauterizedModel.h | 49 ++++ interface/src/avatar/SkeletonModel.cpp | 8 +- interface/src/avatar/SkeletonModel.h | 13 +- interface/src/avatar/SoftAttachmentModel.cpp | 3 +- interface/src/avatar/SoftAttachmentModel.h | 9 +- .../render-utils/src/MeshPartPayload.cpp | 41 +-- libraries/render-utils/src/MeshPartPayload.h | 8 +- libraries/render-utils/src/Model.cpp | 37 +-- libraries/render-utils/src/Model.h | 40 ++- 12 files changed, 430 insertions(+), 115 deletions(-) create mode 100644 interface/src/avatar/CauterizedMeshPartPayload.cpp create mode 100644 interface/src/avatar/CauterizedMeshPartPayload.h create mode 100644 interface/src/avatar/CauterizedModel.cpp create mode 100644 interface/src/avatar/CauterizedModel.h diff --git a/interface/src/avatar/CauterizedMeshPartPayload.cpp b/interface/src/avatar/CauterizedMeshPartPayload.cpp new file mode 100644 index 0000000000..e073b2cedf --- /dev/null +++ b/interface/src/avatar/CauterizedMeshPartPayload.cpp @@ -0,0 +1,72 @@ +// +// MeshPartPayload.cpp +// interface/src/renderer +// +// Created by Sam Gateau on 10/3/15. +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "CauterizedMeshPartPayload.h" + +#include + +#include "SkeletonModel.h" +//#include "EntityItem.h" + +using namespace render; + +CauterizedMeshPartPayload::CauterizedMeshPartPayload(Model* model, int meshIndex, int partIndex, int shapeIndex, const Transform& transform, const Transform& offsetTransform) + : ModelMeshPartPayload(model, meshIndex, partIndex, shapeIndex, transform, offsetTransform) {} + +void CauterizedMeshPartPayload::updateTransformForSkinnedCauterizedMesh(const Transform& transform, + const QVector& clusterMatrices, + const QVector& cauterizedClusterMatrices) { + _transform = transform; + _cauterizedTransform = transform; + + if (clusterMatrices.size() > 0) { + _worldBound = AABox(); + for (auto& clusterMatrix : clusterMatrices) { + AABox clusterBound = _localBound; + clusterBound.transform(clusterMatrix); + _worldBound += clusterBound; + } + + _worldBound.transform(transform); + if (clusterMatrices.size() == 1) { + _transform = _transform.worldTransform(Transform(clusterMatrices[0])); + if (cauterizedClusterMatrices.size() != 0) { + _cauterizedTransform = _cauterizedTransform.worldTransform(Transform(cauterizedClusterMatrices[0])); + } else { + _cauterizedTransform = _transform; + } + } + } +} + +void CauterizedMeshPartPayload::bindTransform(gpu::Batch& batch, const render::ShapePipeline::LocationsPointer locations, RenderArgs::RenderMode renderMode) const { + // Still relying on the raw data from the model + const Model::MeshState& state = _model->getMeshState(_meshIndex); + SkeletonModel* skeleton = static_cast(_model); + bool canCauterize = (renderMode == RenderArgs::RenderMode::SHADOW_RENDER_MODE); + + if (state.clusterBuffer) { + if (canCauterize && skeleton->getCauterizeBones()) { + const Model::MeshState& cState = skeleton->getCauterizeMeshState(_meshIndex); + batch.setUniformBuffer(ShapePipeline::Slot::BUFFER::SKINNING, cState.clusterBuffer); + } else { + batch.setUniformBuffer(ShapePipeline::Slot::BUFFER::SKINNING, state.clusterBuffer); + } + batch.setModelTransform(_transform); + } else { + if (canCauterize && skeleton->getCauterizeBones()) { + batch.setModelTransform(_cauterizedTransform); + } else { + batch.setModelTransform(_transform); + } + } +} + diff --git a/interface/src/avatar/CauterizedMeshPartPayload.h b/interface/src/avatar/CauterizedMeshPartPayload.h new file mode 100644 index 0000000000..f4319ead6f --- /dev/null +++ b/interface/src/avatar/CauterizedMeshPartPayload.h @@ -0,0 +1,29 @@ +// +// CauterizedModelMeshPartPayload.h +// interface/src/avatar +// +// Created by AndrewMeadows 2017.01.17 +// Copyright 2017 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_CauterizedMeshPartPayload_h +#define hifi_CauterizedMeshPartPayload_h + +#include + +class CauterizedMeshPartPayload : public ModelMeshPartPayload { +public: + CauterizedMeshPartPayload(Model* model, int meshIndex, int partIndex, int shapeIndex, const Transform& transform, const Transform& offsetTransform); + void updateTransformForSkinnedCauterizedMesh(const Transform& transform, + const QVector& clusterMatrices, + const QVector& cauterizedClusterMatrices); + + void bindTransform(gpu::Batch& batch, const render::ShapePipeline::LocationsPointer locations, RenderArgs::RenderMode renderMode) const override; +private: + Transform _cauterizedTransform; +}; + +#endif // hifi_CauterizedMeshPartPayload_h diff --git a/interface/src/avatar/CauterizedModel.cpp b/interface/src/avatar/CauterizedModel.cpp new file mode 100644 index 0000000000..d58d392bb0 --- /dev/null +++ b/interface/src/avatar/CauterizedModel.cpp @@ -0,0 +1,236 @@ +// +// CauterizedModel.cpp +// interface/src/avatar +// +// Created by Andrew Meadows 2017.01.17 +// Copyright 2017 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +//#include +//#include + +#include "CauterizedModel.h" + +#include +#include +#include + +#include "CauterizedMeshPartPayload.h" + + +CauterizedModel::CauterizedModel(RigPointer rig, QObject* parent) : Model(rig, parent) { +} + +CauterizedModel::~CauterizedModel() { +} + +void CauterizedModel::deleteGeometry() { + Model::deleteGeometry(); + _cauterizeMeshStates.clear(); +} + +// Called within Model::simulate call, below. +void CauterizedModel::updateRig(float deltaTime, glm::mat4 parentTransform) { + Model::updateRig(deltaTime, parentTransform); + _needsUpdateClusterMatrices = true; +} + +void CauterizedModel::createVisibleRenderItemSet() { + // Temporary HACK: use base class method for now + Model::createVisibleRenderItemSet(); +} + +void CauterizedModel::createCollisionRenderItemSet() { + // Temporary HACK: use base class method for now + Model::createCollisionRenderItemSet(); +} + +bool CauterizedModel::updateGeometry() { + bool returnValue = Model::updateGeometry(); + if (_rig->jointStatesEmpty() && getFBXGeometry().joints.size() > 0) { + const FBXGeometry& fbxGeometry = getFBXGeometry(); + foreach (const FBXMesh& mesh, fbxGeometry.meshes) { + Model::MeshState state; + state.clusterMatrices.resize(mesh.clusters.size()); + _cauterizeMeshStates.append(state); + } + } + return returnValue; +} + +void CauterizedModel::updateClusterMatrices() { + PerformanceTimer perfTimer("CauterizedModel::updateClusterMatrices"); + + if (!_needsUpdateClusterMatrices || !isLoaded()) { + return; + } + _needsUpdateClusterMatrices = false; + const FBXGeometry& geometry = getFBXGeometry(); + + for (int i = 0; i < _meshStates.size(); i++) { + Model::MeshState& state = _meshStates[i]; + const FBXMesh& mesh = geometry.meshes.at(i); + for (int j = 0; j < mesh.clusters.size(); j++) { + const FBXCluster& cluster = mesh.clusters.at(j); + auto jointMatrix = _rig->getJointTransform(cluster.jointIndex); +#if GLM_ARCH & GLM_ARCH_SSE2 + glm::mat4 out, inverseBindMatrix = cluster.inverseBindMatrix; + glm_mat4_mul((glm_vec4*)&jointMatrix, (glm_vec4*)&inverseBindMatrix, (glm_vec4*)&out); + state.clusterMatrices[j] = out; +#else + state.clusterMatrices[j] = jointMatrix * cluster.inverseBindMatrix; +#endif + } + + // Once computed the cluster matrices, update the buffer(s) + if (mesh.clusters.size() > 1) { + if (!state.clusterBuffer) { + state.clusterBuffer = std::make_shared(state.clusterMatrices.size() * sizeof(glm::mat4), + (const gpu::Byte*) state.clusterMatrices.constData()); + } else { + state.clusterBuffer->setSubData(0, state.clusterMatrices.size() * sizeof(glm::mat4), + (const gpu::Byte*) state.clusterMatrices.constData()); + } + } + } + + // as an optimization, don't build cautrizedClusterMatrices if the boneSet is empty. + if (!_cauterizeBoneSet.empty()) { + static const glm::mat4 zeroScale( + glm::vec4(0.0f, 0.0f, 0.0f, 0.0f), + glm::vec4(0.0f, 0.0f, 0.0f, 0.0f), + glm::vec4(0.0f, 0.0f, 0.0f, 0.0f), + glm::vec4(0.0f, 0.0f, 0.0f, 1.0f)); + auto cauterizeMatrix = _rig->getJointTransform(geometry.neckJointIndex) * zeroScale; + + for (int i = 0; i < _cauterizeMeshStates.size(); i++) { + Model::MeshState& state = _cauterizeMeshStates[i]; + const FBXMesh& mesh = geometry.meshes.at(i); + for (int j = 0; j < mesh.clusters.size(); j++) { + const FBXCluster& cluster = mesh.clusters.at(j); + auto jointMatrix = _rig->getJointTransform(cluster.jointIndex); + if (_cauterizeBoneSet.find(cluster.jointIndex) != _cauterizeBoneSet.end()) { + jointMatrix = cauterizeMatrix; + } +#if GLM_ARCH & GLM_ARCH_SSE2 + glm::mat4 out, inverseBindMatrix = cluster.inverseBindMatrix; + glm_mat4_mul((glm_vec4*)&jointMatrix, (glm_vec4*)&inverseBindMatrix, (glm_vec4*)&out); + state.clusterMatrices[j] = out; +#else + state.clusterMatrices[j] = jointMatrix * cluster.inverseBindMatrix; +#endif + } + + if (!_cauterizeBoneSet.empty() && (state.clusterMatrices.size() > 1)) { + if (!state.clusterBuffer) { + state.clusterBuffer = + std::make_shared(state.clusterMatrices.size() * sizeof(glm::mat4), + (const gpu::Byte*) state.clusterMatrices.constData()); + } else { + state.clusterBuffer->setSubData(0, state.clusterMatrices.size() * sizeof(glm::mat4), + (const gpu::Byte*) state.clusterMatrices.constData()); + } + } + } + } + + // post the blender if we're not currently waiting for one to finish + if (geometry.hasBlendedMeshes() && _blendshapeCoefficients != _blendedBlendshapeCoefficients) { + _blendedBlendshapeCoefficients = _blendshapeCoefficients; + DependencyManager::get()->noteRequiresBlend(getThisPointer()); + } +} + +void CauterizedModel::updateRenderItems() { + // Temporary HACK: use base class method for now + Model::updateRenderItems(); +} + +#ifdef FOO +// TODO: finish implementing this +void CauterizedModel::updateRenderItems() { + if (!_addedToScene) { + return; + } + + glm::vec3 scale = getScale(); + if (_collisionGeometry) { + // _collisionGeometry is already scaled + scale = glm::vec3(1.0f); + } + _needsUpdateClusterMatrices = true; + _renderItemsNeedUpdate = false; + + // queue up this work for later processing, at the end of update and just before rendering. + // the application will ensure only the last lambda is actually invoked. + void* key = (void*)this; + std::weak_ptr weakSelf = shared_from_this(); + AbstractViewStateInterface::instance()->pushPostUpdateLambda(key, [weakSelf, scale]() { + + // do nothing, if the model has already been destroyed. + auto self = weakSelf.lock(); + if (!self) { + return; + } + + render::ScenePointer scene = AbstractViewStateInterface::instance()->getMain3DScene(); + + Transform modelTransform; + modelTransform.setTranslation(self->getTranslation()); + modelTransform.setRotation(self->getRotation()); + + Transform scaledModelTransform(modelTransform); + scaledModelTransform.setScale(scale); + + uint32_t deleteGeometryCounter = self->getGeometryCounter(); + + // TODO: handle two cases: + // (a) our payloads are of type ModelMeshPartPayload + // (b) our payloads are of type ModelMeshPartPayload + render::PendingChanges pendingChanges; + QList keys = self->getRenderItems().keys(); + foreach (auto itemID, keys) { + pendingChanges.updateItem(itemID, [modelTransform, deleteGeometryCounter](CauterizedMeshPartPayload& data) { + if (data._model && data._model->isLoaded()) { + if (!data.hasStartedFade() && data._model->getGeometry()->areTexturesLoaded()) { + data.startFade(); + } + // Ensure the model geometry was not reset between frames + if (deleteGeometryCounter == data._model->getGeometryCounter()) { + // lazy update of cluster matrices used for rendering. We need to update them here, so we can correctly update the bounding box. + data._model->updateClusterMatrices(); + + // update the model transform and bounding box for this render item. + const Model::MeshState& state = data._model->getMeshState(data._meshIndex); + CauterizedModel* cModel = static_cast(data._model); + const Model::MeshState& cState = cModel->_cauterizeMeshStates.at(data._meshIndex); + data.updateTransformForSkinnedCauterizedMesh(modelTransform, state.clusterMatrices, cState.clusterMatrices); + } + } + }); + } + + /* + // collision mesh does not share the same unit scale as the FBX file's mesh: only apply offset + Transform collisionMeshOffset; + collisionMeshOffset.setIdentity(); + foreach (auto itemID, self->_collisionRenderItems.keys()) { + pendingChanges.updateItem(itemID, [scaledModelTransform, collisionMeshOffset](MeshPartPayload& data) { + // update the model transform for this render item. + data.updateTransform(scaledModelTransform, collisionMeshOffset); + }); + } + */ + + scene->enqueuePendingChanges(pendingChanges); + }); +} +#endif // FOO + +const Model::MeshState& CauterizedModel::getCauterizeMeshState(int index) const { + assert(index < _meshStates.size()); + return _cauterizeMeshStates.at(index); +} diff --git a/interface/src/avatar/CauterizedModel.h b/interface/src/avatar/CauterizedModel.h new file mode 100644 index 0000000000..12a9723dd4 --- /dev/null +++ b/interface/src/avatar/CauterizedModel.h @@ -0,0 +1,49 @@ +// +// CauterizeableModel.h +// interface/src/avatar +// +// Created by Andrew Meadows 2016.01.17 +// Copyright 2017 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_CauterizedModel_h +#define hifi_CauterizedModel_h + + +#include + +class CauterizedModel : public Model { + Q_OBJECT + +public: + CauterizedModel(RigPointer rig, QObject* parent); + virtual ~CauterizedModel(); + + void deleteGeometry() override; + virtual void updateRig(float deltaTime, glm::mat4 parentTransform) override; + + void setCauterizeBones(bool flag) { _cauterizeBones = flag; } + bool getCauterizeBones() const { return _cauterizeBones; } + + const std::unordered_set& getCauterizeBoneSet() const { return _cauterizeBoneSet; } + void setCauterizeBoneSet(const std::unordered_set& boneSet) { _cauterizeBoneSet = boneSet; } + + void createVisibleRenderItemSet() override; + void createCollisionRenderItemSet() override; + + bool updateGeometry() override; + virtual void updateClusterMatrices() override; + void updateRenderItems() override; + + const Model::MeshState& getCauterizeMeshState(int index) const; + +protected: + std::unordered_set _cauterizeBoneSet; + QVector _cauterizeMeshStates; + bool _cauterizeBones { false }; +}; + +#endif // hifi_CauterizedModel_h diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index 889f0ef36b..54f6682191 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -24,7 +24,7 @@ #include "AnimDebugDraw.h" SkeletonModel::SkeletonModel(Avatar* owningAvatar, QObject* parent, RigPointer rig) : - Model(rig, parent), + CauterizedModel(rig, parent), _owningAvatar(owningAvatar), _boundingCapsuleLocalOffset(0.0f), _boundingCapsuleRadius(0.0f), @@ -166,7 +166,7 @@ void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { _rig->computeMotionAnimationState(deltaTime, position, velocity, orientation, ccState); // evaluate AnimGraph animation and update jointStates. - Model::updateRig(deltaTime, parentTransform); + CauterizedModel::updateRig(deltaTime, parentTransform); Rig::EyeParameters eyeParams; eyeParams.worldHeadOrientation = headParams.worldHeadOrientation; @@ -178,10 +178,8 @@ void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { eyeParams.rightEyeJointIndex = geometry.rightEyeJointIndex; _rig->updateFromEyeParameters(eyeParams); - } else { - - Model::updateRig(deltaTime, parentTransform); + CauterizedModel::updateRig(deltaTime, parentTransform); // This is a little more work than we really want. // diff --git a/interface/src/avatar/SkeletonModel.h b/interface/src/avatar/SkeletonModel.h index 8e61e6f3ca..7a6081a010 100644 --- a/interface/src/avatar/SkeletonModel.h +++ b/interface/src/avatar/SkeletonModel.h @@ -12,8 +12,7 @@ #ifndef hifi_SkeletonModel_h #define hifi_SkeletonModel_h - -#include +#include "CauterizedModel.h" class Avatar; class MuscleConstraint; @@ -23,7 +22,7 @@ using SkeletonModelPointer = std::shared_ptr; using SkeletonModelWeakPointer = std::weak_ptr; /// A skeleton loaded from a model. -class SkeletonModel : public Model { +class SkeletonModel : public CauterizedModel { Q_OBJECT public: @@ -31,10 +30,10 @@ public: SkeletonModel(Avatar* owningAvatar, QObject* parent = nullptr, RigPointer rig = nullptr); ~SkeletonModel(); - virtual void initJointStates() override; + void initJointStates() override; - virtual void simulate(float deltaTime, bool fullUpdate = true) override; - virtual void updateRig(float deltaTime, glm::mat4 parentTransform) override; + void simulate(float deltaTime, bool fullUpdate = true) override; + void updateRig(float deltaTime, glm::mat4 parentTransform) override; void updateAttitude(); /// Returns the index of the left hand joint, or -1 if not found. @@ -105,7 +104,7 @@ public: float getHeadClipDistance() const { return _headClipDistance; } - virtual void onInvalidate() override; + void onInvalidate() override; signals: diff --git a/interface/src/avatar/SoftAttachmentModel.cpp b/interface/src/avatar/SoftAttachmentModel.cpp index 6351495598..530801007e 100644 --- a/interface/src/avatar/SoftAttachmentModel.cpp +++ b/interface/src/avatar/SoftAttachmentModel.cpp @@ -13,7 +13,7 @@ #include "InterfaceLogging.h" SoftAttachmentModel::SoftAttachmentModel(RigPointer rig, QObject* parent, RigPointer rigOverride) : - Model(rig, parent), + CauterizedModel(rig, parent), _rigOverride(rigOverride) { assert(_rig); assert(_rigOverride); @@ -38,6 +38,7 @@ int SoftAttachmentModel::getJointIndexOverride(int i) const { // virtual // use the _rigOverride matrices instead of the Model::_rig void SoftAttachmentModel::updateClusterMatrices() { + // adebug TODO: this needs work? if (!_needsUpdateClusterMatrices) { return; } diff --git a/interface/src/avatar/SoftAttachmentModel.h b/interface/src/avatar/SoftAttachmentModel.h index cdf957514c..fea679839a 100644 --- a/interface/src/avatar/SoftAttachmentModel.h +++ b/interface/src/avatar/SoftAttachmentModel.h @@ -12,7 +12,7 @@ #ifndef hifi_SoftAttachmentModel_h #define hifi_SoftAttachmentModel_h -#include +#include "CauterizedModel.h" // A model that allows the creator to specify a secondary rig instance. // When the cluster matrices are created for rendering, the @@ -22,16 +22,15 @@ // This is used by Avatar instances to wear clothing that follows the same // animated pose as the SkeletonModel. -class SoftAttachmentModel : public Model { +class SoftAttachmentModel : public CauterizedModel { Q_OBJECT public: - SoftAttachmentModel(RigPointer rig, QObject* parent, RigPointer rigOverride); ~SoftAttachmentModel(); - virtual void updateRig(float deltaTime, glm::mat4 parentTransform) override; - virtual void updateClusterMatrices() override; + void updateRig(float deltaTime, glm::mat4 parentTransform) override; + void updateClusterMatrices() override; protected: int getJointIndexOverride(int i) const; diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp index e3b2527e67..52eb006b9f 100644 --- a/libraries/render-utils/src/MeshPartPayload.cpp +++ b/libraries/render-utils/src/MeshPartPayload.cpp @@ -251,7 +251,7 @@ void MeshPartPayload::bindMaterial(gpu::Batch& batch, const ShapePipeline::Locat } } -void MeshPartPayload::bindTransform(gpu::Batch& batch, const ShapePipeline::LocationsPointer locations, bool canCauterize) const { +void MeshPartPayload::bindTransform(gpu::Batch& batch, const ShapePipeline::LocationsPointer locations, RenderArgs::RenderMode renderMode) const { batch.setModelTransform(_drawTransform); } @@ -265,7 +265,7 @@ void MeshPartPayload::render(RenderArgs* args) const { assert(locations); // Bind the model transform and the skinCLusterMatrices if needed - bindTransform(batch, locations); + bindTransform(batch, locations, args->_renderMode); //Bind the index buffer and vertex buffer and Blend shapes if needed bindMesh(batch); @@ -359,11 +359,8 @@ void ModelMeshPartPayload::notifyLocationChanged() { } -void ModelMeshPartPayload::updateTransformForSkinnedMesh(const Transform& transform, - const QVector& clusterMatrices, - const QVector& cauterizedClusterMatrices) { +void ModelMeshPartPayload::updateTransformForSkinnedMesh(const Transform& transform, const QVector& clusterMatrices) { _transform = transform; - _cauterizedTransform = transform; if (clusterMatrices.size() > 0) { _worldBound = AABox(); @@ -372,16 +369,7 @@ void ModelMeshPartPayload::updateTransformForSkinnedMesh(const Transform& transf clusterBound.transform(clusterMatrix); _worldBound += clusterBound; } - _worldBound.transform(transform); - if (clusterMatrices.size() == 1) { - _transform = _transform.worldTransform(Transform(clusterMatrices[0])); - if (cauterizedClusterMatrices.size() != 0) { - _cauterizedTransform = _cauterizedTransform.worldTransform(Transform(cauterizedClusterMatrices[0])); - } else { - _cauterizedTransform = _transform; - } - } } } @@ -525,24 +513,13 @@ void ModelMeshPartPayload::bindMesh(gpu::Batch& batch) const { } } -void ModelMeshPartPayload::bindTransform(gpu::Batch& batch, const ShapePipeline::LocationsPointer locations, bool canCauterize) const { +void ModelMeshPartPayload::bindTransform(gpu::Batch& batch, const ShapePipeline::LocationsPointer locations, RenderArgs::RenderMode renderMode) const { // Still relying on the raw data from the model - const Model::MeshState& state = _model->_meshStates.at(_meshIndex); - + const Model::MeshState& state = _model->getMeshState(_meshIndex); if (state.clusterBuffer) { - if (canCauterize && _model->getCauterizeBones()) { - batch.setUniformBuffer(ShapePipeline::Slot::BUFFER::SKINNING, state.cauterizedClusterBuffer); - } else { - batch.setUniformBuffer(ShapePipeline::Slot::BUFFER::SKINNING, state.clusterBuffer); - } - batch.setModelTransform(_transform); - } else { - if (canCauterize && _model->getCauterizeBones()) { - batch.setModelTransform(_cauterizedTransform); - } else { - batch.setModelTransform(_transform); - } + batch.setUniformBuffer(ShapePipeline::Slot::BUFFER::SKINNING, state.clusterBuffer); } + batch.setModelTransform(_transform); } void ModelMeshPartPayload::startFade() { @@ -596,9 +573,8 @@ void ModelMeshPartPayload::render(RenderArgs* args) const { assert(locations); // Bind the model transform and the skinCLusterMatrices if needed - bool canCauterize = args->_renderMode != RenderArgs::SHADOW_RENDER_MODE; _model->updateClusterMatrices(); - bindTransform(batch, locations, canCauterize); + bindTransform(batch, locations, args->_renderMode); //Bind the index buffer and vertex buffer and Blend shapes if needed bindMesh(batch); @@ -621,4 +597,3 @@ void ModelMeshPartPayload::render(RenderArgs* args) const { args->_details._trianglesRendered += _drawPart._numIndices / INDICES_PER_TRIANGLE; } } - diff --git a/libraries/render-utils/src/MeshPartPayload.h b/libraries/render-utils/src/MeshPartPayload.h index b7a8cf63f0..53160db91e 100644 --- a/libraries/render-utils/src/MeshPartPayload.h +++ b/libraries/render-utils/src/MeshPartPayload.h @@ -48,7 +48,7 @@ public: void drawCall(gpu::Batch& batch) const; virtual void bindMesh(gpu::Batch& batch) const; virtual void bindMaterial(gpu::Batch& batch, const render::ShapePipeline::LocationsPointer locations) const; - virtual void bindTransform(gpu::Batch& batch, const render::ShapePipeline::LocationsPointer locations, bool canCauterize = true) const; + virtual void bindTransform(gpu::Batch& batch, const render::ShapePipeline::LocationsPointer locations, RenderArgs::RenderMode renderMode) const; // Payload resource cached values std::shared_ptr _drawMesh; @@ -86,8 +86,7 @@ public: void notifyLocationChanged() override; void updateTransformForSkinnedMesh(const Transform& transform, - const QVector& clusterMatrices, - const QVector& cauterizedClusterMatrices); + const QVector& clusterMatrices); // Entity fade in void startFade(); @@ -102,13 +101,12 @@ public: // ModelMeshPartPayload functions to perform render void bindMesh(gpu::Batch& batch) const override; - void bindTransform(gpu::Batch& batch, const render::ShapePipeline::LocationsPointer locations, bool canCauterize = true) const override; + void bindTransform(gpu::Batch& batch, const render::ShapePipeline::LocationsPointer locations, RenderArgs::RenderMode renderMode) const override; void initCache(); Model* _model; - Transform _cauterizedTransform; int _meshIndex; int _shapeID; diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 14391b9f16..ef613e8c14 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -91,7 +91,6 @@ Model::Model(RigPointer rig, QObject* parent) : _scaledToFit(false), _snapModelToRegistrationPoint(false), _snappedToRegistrationPoint(false), - _cauterizeBones(false), _url(HTTP_INVALID_COM), _isVisible(true), _blendNumber(0), @@ -238,7 +237,7 @@ void Model::updateRenderItems() { // update the model transform and bounding box for this render item. const Model::MeshState& state = data._model->_meshStates.at(data._meshIndex); - data.updateTransformForSkinnedMesh(modelTransform, state.clusterMatrices, state.cauterizedClusterMatrices); + data.updateTransformForSkinnedMesh(modelTransform, state.clusterMatrices); } } }); @@ -294,8 +293,6 @@ bool Model::updateGeometry() { foreach (const FBXMesh& mesh, fbxGeometry.meshes) { MeshState state; state.clusterMatrices.resize(mesh.clusters.size()); - state.cauterizedClusterMatrices.resize(mesh.clusters.size()); - _meshStates.append(state); // Note: we add empty buffers for meshes that lack blendshapes so we can access the buffers by index @@ -1159,13 +1156,6 @@ void Model::updateClusterMatrices() { } _needsUpdateClusterMatrices = false; const FBXGeometry& geometry = getFBXGeometry(); - static const glm::mat4 zeroScale( - glm::vec4(0.0f, 0.0f, 0.0f, 0.0f), - glm::vec4(0.0f, 0.0f, 0.0f, 0.0f), - glm::vec4(0.0f, 0.0f, 0.0f, 0.0f), - glm::vec4(0.0f, 0.0f, 0.0f, 1.0f)); - auto cauterizeMatrix = _rig->getJointTransform(geometry.neckJointIndex) * zeroScale; - for (int i = 0; i < _meshStates.size(); i++) { MeshState& state = _meshStates[i]; const FBXMesh& mesh = geometry.meshes.at(i); @@ -1179,20 +1169,6 @@ void Model::updateClusterMatrices() { #else state.clusterMatrices[j] = jointMatrix * cluster.inverseBindMatrix; #endif - - // as an optimization, don't build cautrizedClusterMatrices if the boneSet is empty. - if (!_cauterizeBoneSet.empty()) { - if (_cauterizeBoneSet.find(cluster.jointIndex) != _cauterizeBoneSet.end()) { - jointMatrix = cauterizeMatrix; - } -#if GLM_ARCH & GLM_ARCH_SSE2 - glm::mat4 out, inverseBindMatrix = cluster.inverseBindMatrix; - glm_mat4_mul((glm_vec4*)&jointMatrix, (glm_vec4*)&inverseBindMatrix, (glm_vec4*)&out); - state.cauterizedClusterMatrices[j] = out; -#else - state.cauterizedClusterMatrices[j] = jointMatrix * cluster.inverseBindMatrix; -#endif - } } // Once computed the cluster matrices, update the buffer(s) @@ -1204,17 +1180,6 @@ void Model::updateClusterMatrices() { state.clusterBuffer->setSubData(0, state.clusterMatrices.size() * sizeof(glm::mat4), (const gpu::Byte*) state.clusterMatrices.constData()); } - - if (!_cauterizeBoneSet.empty() && (state.cauterizedClusterMatrices.size() > 1)) { - if (!state.cauterizedClusterBuffer) { - state.cauterizedClusterBuffer = - std::make_shared(state.cauterizedClusterMatrices.size() * sizeof(glm::mat4), - (const gpu::Byte*) state.cauterizedClusterMatrices.constData()); - } else { - state.cauterizedClusterBuffer->setSubData(0, state.cauterizedClusterMatrices.size() * sizeof(glm::mat4), - (const gpu::Byte*) state.cauterizedClusterMatrices.constData()); - } - } } } diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index a11d6d511e..246d67180e 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -101,7 +101,7 @@ public: bool isLayeredInFront() const { return _isLayeredInFront; } - void updateRenderItems(); + virtual void updateRenderItems(); void setRenderItemsNeedUpdate() { _renderItemsNeedUpdate = true; } bool getRenderItemsNeedUpdate() { return _renderItemsNeedUpdate; } AABox getRenderableMeshBound() const; @@ -215,12 +215,6 @@ public: bool getIsScaledToFit() const { return _scaledToFit; } /// is model scaled to fit glm::vec3 getScaleToFitDimensions() const; /// the dimensions model is scaled to, including inferred y/z - void setCauterizeBones(bool flag) { _cauterizeBones = flag; } - bool getCauterizeBones() const { return _cauterizeBones; } - - const std::unordered_set& getCauterizeBoneSet() const { return _cauterizeBoneSet; } - void setCauterizeBoneSet(const std::unordered_set& boneSet) { _cauterizeBoneSet = boneSet; } - int getBlendshapeCoefficientsNum() const { return _blendshapeCoefficients.size(); } float getBlendshapeCoefficient(int index) const { return ((index < 0) && (index >= _blendshapeCoefficients.size())) ? 0.0f : _blendshapeCoefficients.at(index); @@ -231,7 +225,7 @@ public: const glm::vec3& getRegistrationPoint() const { return _registrationPoint; } // returns 'true' if needs fullUpdate after geometry change - bool updateGeometry(); + virtual bool updateGeometry(); void setCollisionMesh(model::MeshPointer mesh); void setLoadingPriority(float priority) { _loadingPriority = priority; } @@ -242,6 +236,18 @@ public: int getRenderInfoDrawCalls() const { return _renderInfoDrawCalls; } bool getRenderInfoHasTransparent() const { return _renderInfoHasTransparent; } + class MeshState { + public: + QVector clusterMatrices; + gpu::BufferPointer clusterBuffer; + + }; + + const MeshState& getMeshState(int index) { return _meshStates.at(index); } + + uint32_t getGeometryCounter() const { return _deleteGeometryCounter; } + const QMap& getRenderItems() const { return _modelMeshRenderItems; } + public slots: void loadURLFinished(bool success); @@ -298,18 +304,7 @@ protected: bool _snappedToRegistrationPoint; /// are we currently snapped to a registration point glm::vec3 _registrationPoint = glm::vec3(0.5f); /// the point in model space our center is snapped to - class MeshState { - public: - QVector clusterMatrices; - QVector cauterizedClusterMatrices; - gpu::BufferPointer clusterBuffer; - gpu::BufferPointer cauterizedClusterBuffer; - - }; - QVector _meshStates; - std::unordered_set _cauterizeBoneSet; - bool _cauterizeBones; virtual void initJointStates(); @@ -342,7 +337,7 @@ protected: protected: - void deleteGeometry(); + virtual void deleteGeometry(); void initJointTransforms(); QVector _blendshapeCoefficients; @@ -371,12 +366,11 @@ protected: void recalculateMeshBoxes(bool pickAgainstTriangles = false); void createRenderItemSet(); - void createVisibleRenderItemSet(); - void createCollisionRenderItemSet(); + virtual void createVisibleRenderItemSet(); + virtual void createCollisionRenderItemSet(); bool _isWireframe; - // debug rendering support void renderDebugMeshBoxes(gpu::Batch& batch); int _debugMeshBoxesID = GeometryCache::UNKNOWN_ID; From 189da81bb1ac00eb2164a9887b41f10d1a71021b Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 18 Jan 2017 10:44:54 -0800 Subject: [PATCH 090/109] fix for crash on unloaded model --- interface/src/avatar/CauterizedModel.cpp | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/interface/src/avatar/CauterizedModel.cpp b/interface/src/avatar/CauterizedModel.cpp index d58d392bb0..5c06412d9c 100644 --- a/interface/src/avatar/CauterizedModel.cpp +++ b/interface/src/avatar/CauterizedModel.cpp @@ -49,16 +49,18 @@ void CauterizedModel::createCollisionRenderItemSet() { } bool CauterizedModel::updateGeometry() { - bool returnValue = Model::updateGeometry(); - if (_rig->jointStatesEmpty() && getFBXGeometry().joints.size() > 0) { - const FBXGeometry& fbxGeometry = getFBXGeometry(); - foreach (const FBXMesh& mesh, fbxGeometry.meshes) { - Model::MeshState state; - state.clusterMatrices.resize(mesh.clusters.size()); - _cauterizeMeshStates.append(state); + bool needsFullUpdate = Model::updateGeometry(); + if (needsFullUpdate) { + if (_rig->jointStatesEmpty() && getFBXGeometry().joints.size() > 0) { + const FBXGeometry& fbxGeometry = getFBXGeometry(); + foreach (const FBXMesh& mesh, fbxGeometry.meshes) { + Model::MeshState state; + state.clusterMatrices.resize(mesh.clusters.size()); + _cauterizeMeshStates.append(state); + } } } - return returnValue; + return needsFullUpdate; } void CauterizedModel::updateClusterMatrices() { From 1a3fa849859a9112028df0e9f5c2a6cb8f52b552 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 18 Jan 2017 14:54:48 -0800 Subject: [PATCH 091/109] add newline at EOF --- interface/src/avatar/Avatar.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index ab4ae02a8e..4adad0d938 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -1363,4 +1363,4 @@ void Avatar::ensureInScene(AvatarSharedPointer self) { if (!_inScene) { addToScene(self); } -} \ No newline at end of file +} From bfbef91f531613c2c3cad9ad2b8136e83ec6a29f Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 18 Jan 2017 18:35:48 -0800 Subject: [PATCH 092/109] re-enable cauterization of meshes for MyAvatar --- interface/src/avatar/Avatar.cpp | 1 + .../src/avatar/CauterizedMeshPartPayload.cpp | 9 +- interface/src/avatar/CauterizedModel.cpp | 219 ++++++++++-------- interface/src/avatar/CauterizedModel.h | 16 +- interface/src/avatar/MyAvatar.cpp | 4 +- interface/src/avatar/SoftAttachmentModel.cpp | 1 - .../render-utils/src/MeshPartPayload.cpp | 3 + 7 files changed, 141 insertions(+), 112 deletions(-) diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index 4adad0d938..ee5e397592 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -1047,6 +1047,7 @@ void Avatar::setModelURLFinished(bool success) { static std::shared_ptr allocateAttachmentModel(bool isSoft, RigPointer rigOverride) { if (isSoft) { // cast to std::shared_ptr + // TODO: re-enable cauterization for the SoftAttachmentModel when this is MyAvatar return std::dynamic_pointer_cast(std::make_shared(std::make_shared(), nullptr, rigOverride)); } else { return std::make_shared(std::make_shared()); diff --git a/interface/src/avatar/CauterizedMeshPartPayload.cpp b/interface/src/avatar/CauterizedMeshPartPayload.cpp index e073b2cedf..a7db9608fe 100644 --- a/interface/src/avatar/CauterizedMeshPartPayload.cpp +++ b/interface/src/avatar/CauterizedMeshPartPayload.cpp @@ -44,6 +44,9 @@ void CauterizedMeshPartPayload::updateTransformForSkinnedCauterizedMesh(const Tr _cauterizedTransform = _transform; } } + } else { + _worldBound = _localBound; + _worldBound.transform(_drawTransform); } } @@ -51,10 +54,10 @@ void CauterizedMeshPartPayload::bindTransform(gpu::Batch& batch, const render::S // Still relying on the raw data from the model const Model::MeshState& state = _model->getMeshState(_meshIndex); SkeletonModel* skeleton = static_cast(_model); - bool canCauterize = (renderMode == RenderArgs::RenderMode::SHADOW_RENDER_MODE); + bool useCauterizedMesh = (renderMode != RenderArgs::RenderMode::SHADOW_RENDER_MODE) && skeleton->getEnableCauterization(); if (state.clusterBuffer) { - if (canCauterize && skeleton->getCauterizeBones()) { + if (useCauterizedMesh) { const Model::MeshState& cState = skeleton->getCauterizeMeshState(_meshIndex); batch.setUniformBuffer(ShapePipeline::Slot::BUFFER::SKINNING, cState.clusterBuffer); } else { @@ -62,7 +65,7 @@ void CauterizedMeshPartPayload::bindTransform(gpu::Batch& batch, const render::S } batch.setModelTransform(_transform); } else { - if (canCauterize && skeleton->getCauterizeBones()) { + if (useCauterizedMesh) { batch.setModelTransform(_cauterizedTransform); } else { batch.setModelTransform(_transform); diff --git a/interface/src/avatar/CauterizedModel.cpp b/interface/src/avatar/CauterizedModel.cpp index 5c06412d9c..30566e357d 100644 --- a/interface/src/avatar/CauterizedModel.cpp +++ b/interface/src/avatar/CauterizedModel.cpp @@ -9,9 +9,6 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -//#include -//#include - #include "CauterizedModel.h" #include @@ -21,7 +18,8 @@ #include "CauterizedMeshPartPayload.h" -CauterizedModel::CauterizedModel(RigPointer rig, QObject* parent) : Model(rig, parent) { +CauterizedModel::CauterizedModel(RigPointer rig, QObject* parent) : + Model(rig, parent) { } CauterizedModel::~CauterizedModel() { @@ -32,15 +30,64 @@ void CauterizedModel::deleteGeometry() { _cauterizeMeshStates.clear(); } -// Called within Model::simulate call, below. -void CauterizedModel::updateRig(float deltaTime, glm::mat4 parentTransform) { - Model::updateRig(deltaTime, parentTransform); - _needsUpdateClusterMatrices = true; +bool CauterizedModel::updateGeometry() { + bool needsFullUpdate = Model::updateGeometry(); + if (_isCauterized && needsFullUpdate) { + assert(_cauterizeMeshStates.empty()); + const FBXGeometry& fbxGeometry = getFBXGeometry(); + foreach (const FBXMesh& mesh, fbxGeometry.meshes) { + Model::MeshState state; + state.clusterMatrices.resize(mesh.clusters.size()); + _cauterizeMeshStates.append(state); + } + } + return needsFullUpdate; } void CauterizedModel::createVisibleRenderItemSet() { - // Temporary HACK: use base class method for now - Model::createVisibleRenderItemSet(); + if (_isCauterized) { + assert(isLoaded()); + const auto& meshes = _renderGeometry->getMeshes(); + + // all of our mesh vectors must match in size + if ((int)meshes.size() != _meshStates.size()) { + qCDebug(renderlogging) << "WARNING!!!! Mesh Sizes don't match! We will not segregate mesh groups yet."; + return; + } + + // We should not have any existing renderItems if we enter this section of code + Q_ASSERT(_modelMeshRenderItemsSet.isEmpty()); + + _modelMeshRenderItemsSet.clear(); + + Transform transform; + transform.setTranslation(_translation); + transform.setRotation(_rotation); + + Transform offset; + offset.setScale(_scale); + offset.postTranslate(_offset); + + // Run through all of the meshes, and place them into their segregated, but unsorted buckets + int shapeID = 0; + uint32_t numMeshes = (uint32_t)meshes.size(); + for (uint32_t i = 0; i < numMeshes; i++) { + const auto& mesh = meshes.at(i); + if (!mesh) { + continue; + } + + // Create the render payloads + int numParts = (int)mesh->getNumParts(); + for (int partIndex = 0; partIndex < numParts; partIndex++) { + auto ptr = std::make_shared(this, i, partIndex, shapeID, transform, offset); + _modelMeshRenderItemsSet << std::static_pointer_cast(ptr); + shapeID++; + } + } + } else { + Model::createVisibleRenderItemSet(); + } } void CauterizedModel::createCollisionRenderItemSet() { @@ -48,19 +95,10 @@ void CauterizedModel::createCollisionRenderItemSet() { Model::createCollisionRenderItemSet(); } -bool CauterizedModel::updateGeometry() { - bool needsFullUpdate = Model::updateGeometry(); - if (needsFullUpdate) { - if (_rig->jointStatesEmpty() && getFBXGeometry().joints.size() > 0) { - const FBXGeometry& fbxGeometry = getFBXGeometry(); - foreach (const FBXMesh& mesh, fbxGeometry.meshes) { - Model::MeshState state; - state.clusterMatrices.resize(mesh.clusters.size()); - _cauterizeMeshStates.append(state); - } - } - } - return needsFullUpdate; +// Called within Model::simulate call, below. +void CauterizedModel::updateRig(float deltaTime, glm::mat4 parentTransform) { + Model::updateRig(deltaTime, parentTransform); + _needsUpdateClusterMatrices = true; } void CauterizedModel::updateClusterMatrices() { @@ -147,90 +185,71 @@ void CauterizedModel::updateClusterMatrices() { } void CauterizedModel::updateRenderItems() { - // Temporary HACK: use base class method for now - Model::updateRenderItems(); -} - -#ifdef FOO -// TODO: finish implementing this -void CauterizedModel::updateRenderItems() { - if (!_addedToScene) { - return; - } - - glm::vec3 scale = getScale(); - if (_collisionGeometry) { - // _collisionGeometry is already scaled - scale = glm::vec3(1.0f); - } - _needsUpdateClusterMatrices = true; - _renderItemsNeedUpdate = false; - - // queue up this work for later processing, at the end of update and just before rendering. - // the application will ensure only the last lambda is actually invoked. - void* key = (void*)this; - std::weak_ptr weakSelf = shared_from_this(); - AbstractViewStateInterface::instance()->pushPostUpdateLambda(key, [weakSelf, scale]() { - - // do nothing, if the model has already been destroyed. - auto self = weakSelf.lock(); - if (!self) { + if (_isCauterized) { + if (!_addedToScene) { return; } - render::ScenePointer scene = AbstractViewStateInterface::instance()->getMain3DScene(); - - Transform modelTransform; - modelTransform.setTranslation(self->getTranslation()); - modelTransform.setRotation(self->getRotation()); - - Transform scaledModelTransform(modelTransform); - scaledModelTransform.setScale(scale); - - uint32_t deleteGeometryCounter = self->getGeometryCounter(); - - // TODO: handle two cases: - // (a) our payloads are of type ModelMeshPartPayload - // (b) our payloads are of type ModelMeshPartPayload - render::PendingChanges pendingChanges; - QList keys = self->getRenderItems().keys(); - foreach (auto itemID, keys) { - pendingChanges.updateItem(itemID, [modelTransform, deleteGeometryCounter](CauterizedMeshPartPayload& data) { - if (data._model && data._model->isLoaded()) { - if (!data.hasStartedFade() && data._model->getGeometry()->areTexturesLoaded()) { - data.startFade(); - } - // Ensure the model geometry was not reset between frames - if (deleteGeometryCounter == data._model->getGeometryCounter()) { - // lazy update of cluster matrices used for rendering. We need to update them here, so we can correctly update the bounding box. - data._model->updateClusterMatrices(); - - // update the model transform and bounding box for this render item. - const Model::MeshState& state = data._model->getMeshState(data._meshIndex); - CauterizedModel* cModel = static_cast(data._model); - const Model::MeshState& cState = cModel->_cauterizeMeshStates.at(data._meshIndex); - data.updateTransformForSkinnedCauterizedMesh(modelTransform, state.clusterMatrices, cState.clusterMatrices); - } - } - }); + glm::vec3 scale = getScale(); + if (_collisionGeometry) { + // _collisionGeometry is already scaled + scale = glm::vec3(1.0f); } + _needsUpdateClusterMatrices = true; + _renderItemsNeedUpdate = false; - /* - // collision mesh does not share the same unit scale as the FBX file's mesh: only apply offset - Transform collisionMeshOffset; - collisionMeshOffset.setIdentity(); - foreach (auto itemID, self->_collisionRenderItems.keys()) { - pendingChanges.updateItem(itemID, [scaledModelTransform, collisionMeshOffset](MeshPartPayload& data) { - // update the model transform for this render item. - data.updateTransform(scaledModelTransform, collisionMeshOffset); - }); - } - */ + // queue up this work for later processing, at the end of update and just before rendering. + // the application will ensure only the last lambda is actually invoked. + void* key = (void*)this; + std::weak_ptr weakSelf = shared_from_this(); + AbstractViewStateInterface::instance()->pushPostUpdateLambda(key, [weakSelf, scale]() { + // do nothing, if the model has already been destroyed. + auto self = weakSelf.lock(); + if (!self) { + return; + } - scene->enqueuePendingChanges(pendingChanges); - }); + render::ScenePointer scene = AbstractViewStateInterface::instance()->getMain3DScene(); + + Transform modelTransform; + modelTransform.setTranslation(self->getTranslation()); + modelTransform.setRotation(self->getRotation()); + + Transform scaledModelTransform(modelTransform); + scaledModelTransform.setScale(scale); + + uint32_t deleteGeometryCounter = self->getGeometryCounter(); + + render::PendingChanges pendingChanges; + QList keys = self->getRenderItems().keys(); + foreach (auto itemID, keys) { + pendingChanges.updateItem(itemID, [modelTransform, deleteGeometryCounter](CauterizedMeshPartPayload& data) { + if (data._model && data._model->isLoaded()) { + if (!data.hasStartedFade() && data._model->getGeometry()->areTexturesLoaded()) { + data.startFade(); + } + // Ensure the model geometry was not reset between frames + if (deleteGeometryCounter == data._model->getGeometryCounter()) { + // lazy update of cluster matrices used for rendering. We need to update them here, so we can correctly update the bounding box. + data._model->updateClusterMatrices(); + + // update the model transform and bounding box for this render item. + const Model::MeshState& state = data._model->getMeshState(data._meshIndex); + CauterizedModel* cModel = static_cast(data._model); + assert(data._meshIndex < cModel->_cauterizeMeshStates.size()); + const Model::MeshState& cState = cModel->_cauterizeMeshStates.at(data._meshIndex); + data.updateTransformForSkinnedCauterizedMesh(modelTransform, state.clusterMatrices, cState.clusterMatrices); + } + } + }); + } + + scene->enqueuePendingChanges(pendingChanges); + }); + } else { + Model::updateRenderItems(); + } } -#endif // FOO const Model::MeshState& CauterizedModel::getCauterizeMeshState(int index) const { assert(index < _meshStates.size()); diff --git a/interface/src/avatar/CauterizedModel.h b/interface/src/avatar/CauterizedModel.h index 12a9723dd4..01e0b13650 100644 --- a/interface/src/avatar/CauterizedModel.h +++ b/interface/src/avatar/CauterizedModel.h @@ -22,19 +22,22 @@ public: CauterizedModel(RigPointer rig, QObject* parent); virtual ~CauterizedModel(); - void deleteGeometry() override; - virtual void updateRig(float deltaTime, glm::mat4 parentTransform) override; + void flagAsCauterized() { _isCauterized = true; } + bool getIsCauterized() const { return _isCauterized; } - void setCauterizeBones(bool flag) { _cauterizeBones = flag; } - bool getCauterizeBones() const { return _cauterizeBones; } + void setEnableCauterization(bool flag) { _enableCauterization = flag; } + bool getEnableCauterization() const { return _enableCauterization; } const std::unordered_set& getCauterizeBoneSet() const { return _cauterizeBoneSet; } void setCauterizeBoneSet(const std::unordered_set& boneSet) { _cauterizeBoneSet = boneSet; } + void deleteGeometry() override; + bool updateGeometry() override; + void createVisibleRenderItemSet() override; void createCollisionRenderItemSet() override; - bool updateGeometry() override; + virtual void updateRig(float deltaTime, glm::mat4 parentTransform) override; virtual void updateClusterMatrices() override; void updateRenderItems() override; @@ -43,7 +46,8 @@ public: protected: std::unordered_set _cauterizeBoneSet; QVector _cauterizeMeshStates; - bool _cauterizeBones { false }; + bool _isCauterized { false }; + bool _enableCauterization { false }; }; #endif // hifi_CauterizedModel_h diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 488752b309..dd95c5963d 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -116,12 +116,12 @@ MyAvatar::MyAvatar(RigPointer rig) : _hmdAtRestDetector(glm::vec3(0), glm::quat()) { using namespace recording; + _skeletonModel->flagAsCauterized(); for (int i = 0; i < MAX_DRIVE_KEYS; i++) { _driveKeys[i] = 0.0f; } - // Necessary to select the correct slot using SlotType = void(MyAvatar::*)(const glm::vec3&, bool, const glm::quat&, bool); @@ -1592,7 +1592,7 @@ void MyAvatar::preDisplaySide(RenderArgs* renderArgs) { // toggle using the cauterizedBones depending on where the camera is and the rendering pass type. const bool shouldDrawHead = shouldRenderHead(renderArgs); if (shouldDrawHead != _prevShouldDrawHead) { - _skeletonModel->setCauterizeBones(!shouldDrawHead); + _skeletonModel->setEnableCauterization(!shouldDrawHead); } _prevShouldDrawHead = shouldDrawHead; } diff --git a/interface/src/avatar/SoftAttachmentModel.cpp b/interface/src/avatar/SoftAttachmentModel.cpp index 530801007e..da7ca0b87d 100644 --- a/interface/src/avatar/SoftAttachmentModel.cpp +++ b/interface/src/avatar/SoftAttachmentModel.cpp @@ -38,7 +38,6 @@ int SoftAttachmentModel::getJointIndexOverride(int i) const { // virtual // use the _rigOverride matrices instead of the Model::_rig void SoftAttachmentModel::updateClusterMatrices() { - // adebug TODO: this needs work? if (!_needsUpdateClusterMatrices) { return; } diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp index 52eb006b9f..57498abff9 100644 --- a/libraries/render-utils/src/MeshPartPayload.cpp +++ b/libraries/render-utils/src/MeshPartPayload.cpp @@ -370,6 +370,9 @@ void ModelMeshPartPayload::updateTransformForSkinnedMesh(const Transform& transf _worldBound += clusterBound; } _worldBound.transform(transform); + } else { + _worldBound = _localBound; + _worldBound.transform(_drawTransform); } } From 5a2e33775825ff3e142a7abec9572dc1a404fe6d Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 19 Jan 2017 09:22:18 -0800 Subject: [PATCH 093/109] fix comments --- interface/src/avatar/CauterizedMeshPartPayload.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/interface/src/avatar/CauterizedMeshPartPayload.cpp b/interface/src/avatar/CauterizedMeshPartPayload.cpp index a7db9608fe..c8ec90dcee 100644 --- a/interface/src/avatar/CauterizedMeshPartPayload.cpp +++ b/interface/src/avatar/CauterizedMeshPartPayload.cpp @@ -1,9 +1,9 @@ // -// MeshPartPayload.cpp +// CauterizedMeshPartPayload.cpp // interface/src/renderer // -// Created by Sam Gateau on 10/3/15. -// Copyright 2015 High Fidelity, Inc. +// Created by Andrew Meadows 2017.01.17 +// Copyright 2017 High Fidelity, Inc. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html @@ -14,7 +14,6 @@ #include #include "SkeletonModel.h" -//#include "EntityItem.h" using namespace render; From 9631864ae261d60910c0a5be3ccc7c6c1dc603d3 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 19 Jan 2017 09:46:29 -0800 Subject: [PATCH 094/109] make soft attachments cauterized for MyAvatar --- interface/src/avatar/Avatar.cpp | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index ee5e397592..6061812d2a 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -1044,11 +1044,14 @@ void Avatar::setModelURLFinished(bool success) { // create new model, can return an instance of a SoftAttachmentModel rather then Model -static std::shared_ptr allocateAttachmentModel(bool isSoft, RigPointer rigOverride) { +static std::shared_ptr allocateAttachmentModel(bool isSoft, RigPointer rigOverride, bool isCauterized) { if (isSoft) { // cast to std::shared_ptr - // TODO: re-enable cauterization for the SoftAttachmentModel when this is MyAvatar - return std::dynamic_pointer_cast(std::make_shared(std::make_shared(), nullptr, rigOverride)); + std::shared_ptr softModel = std::make_shared(std::make_shared(), nullptr, rigOverride); + if (isCauterized) { + softModel->flagAsCauterized(); + } + return std::dynamic_pointer_cast(softModel); } else { return std::make_shared(std::make_shared()); } @@ -1074,12 +1077,12 @@ void Avatar::setAttachmentData(const QVector& attachmentData) { for (int i = 0; i < attachmentData.size(); i++) { if (i == (int)_attachmentModels.size()) { // if number of attachments has been increased, we need to allocate a new model - _attachmentModels.push_back(allocateAttachmentModel(attachmentData[i].isSoft, _skeletonModel->getRig())); + _attachmentModels.push_back(allocateAttachmentModel(attachmentData[i].isSoft, _skeletonModel->getRig(), isMyAvatar())); } else if (i < oldAttachmentData.size() && oldAttachmentData[i].isSoft != attachmentData[i].isSoft) { // if the attachment has changed type, we need to re-allocate a new one. _attachmentsToRemove.push_back(_attachmentModels[i]); - _attachmentModels[i] = allocateAttachmentModel(attachmentData[i].isSoft, _skeletonModel->getRig()); + _attachmentModels[i] = allocateAttachmentModel(attachmentData[i].isSoft, _skeletonModel->getRig(), isMyAvatar()); } _attachmentModels[i]->setURL(attachmentData[i].modelURL); } From 1d6f47e3d82b7a20a9b8c4c37b482e55d962044d Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 19 Jan 2017 10:46:10 -0800 Subject: [PATCH 095/109] repack to reduce MeshPartPayload footprint 8 bytes --- libraries/render-utils/src/MeshPartPayload.h | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/libraries/render-utils/src/MeshPartPayload.h b/libraries/render-utils/src/MeshPartPayload.h index 53160db91e..a2c932b589 100644 --- a/libraries/render-utils/src/MeshPartPayload.h +++ b/libraries/render-utils/src/MeshPartPayload.h @@ -51,18 +51,17 @@ public: virtual void bindTransform(gpu::Batch& batch, const render::ShapePipeline::LocationsPointer locations, RenderArgs::RenderMode renderMode) const; // Payload resource cached values - std::shared_ptr _drawMesh; - int _partIndex = 0; - model::Mesh::Part _drawPart; - - std::shared_ptr _drawMaterial; - - model::Box _localBound; Transform _drawTransform; Transform _transform; - mutable model::Box _worldBound; + int _partIndex = 0; + bool _hasColorAttrib { false }; - bool _hasColorAttrib = false; + model::Box _localBound; + mutable model::Box _worldBound; + std::shared_ptr _drawMesh; + + std::shared_ptr _drawMaterial; + model::Mesh::Part _drawPart; size_t getVerticesCount() const { return _drawMesh ? _drawMesh->getNumVertices() : 0; } size_t getMaterialTextureSize() { return _drawMaterial ? _drawMaterial->getTextureSize() : 0; } From d3af420516601584b9a3d02d59dcb948b0684b1c Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 19 Jan 2017 13:07:50 -0800 Subject: [PATCH 096/109] fix broken model transforms --- libraries/render-utils/src/MeshPartPayload.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp index 57498abff9..10162ee437 100644 --- a/libraries/render-utils/src/MeshPartPayload.cpp +++ b/libraries/render-utils/src/MeshPartPayload.cpp @@ -370,6 +370,9 @@ void ModelMeshPartPayload::updateTransformForSkinnedMesh(const Transform& transf _worldBound += clusterBound; } _worldBound.transform(transform); + if (clusterMatrices.size() == 1) { + _transform = _transform.worldTransform(Transform(clusterMatrices[0])); + } } else { _worldBound = _localBound; _worldBound.transform(_drawTransform); From b7c1bfaf5803a4599e4efdd39732a165485e8563 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 19 Jan 2017 14:01:40 -0800 Subject: [PATCH 097/109] another attempt to fix model transform --- libraries/render-utils/src/MeshPartPayload.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp index 10162ee437..57346ceb53 100644 --- a/libraries/render-utils/src/MeshPartPayload.cpp +++ b/libraries/render-utils/src/MeshPartPayload.cpp @@ -369,13 +369,13 @@ void ModelMeshPartPayload::updateTransformForSkinnedMesh(const Transform& transf clusterBound.transform(clusterMatrix); _worldBound += clusterBound; } - _worldBound.transform(transform); + _worldBound.transform(_transform); if (clusterMatrices.size() == 1) { _transform = _transform.worldTransform(Transform(clusterMatrices[0])); } } else { _worldBound = _localBound; - _worldBound.transform(_drawTransform); + _worldBound.transform(_transform); } } From 10cdea9cd8356b82d051b43f4d1f3179399ea899 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 19 Jan 2017 14:17:50 -0800 Subject: [PATCH 098/109] cleanup model fade logic --- interface/src/avatar/CauterizedModel.cpp | 3 - .../render-utils/src/MeshPartPayload.cpp | 80 +++++++++---------- libraries/render-utils/src/MeshPartPayload.h | 15 ++-- libraries/render-utils/src/Model.cpp | 4 - libraries/shared/src/Interpolate.cpp | 9 ++- libraries/shared/src/Interpolate.h | 4 + 6 files changed, 58 insertions(+), 57 deletions(-) diff --git a/interface/src/avatar/CauterizedModel.cpp b/interface/src/avatar/CauterizedModel.cpp index 30566e357d..02107e9d24 100644 --- a/interface/src/avatar/CauterizedModel.cpp +++ b/interface/src/avatar/CauterizedModel.cpp @@ -225,9 +225,6 @@ void CauterizedModel::updateRenderItems() { foreach (auto itemID, keys) { pendingChanges.updateItem(itemID, [modelTransform, deleteGeometryCounter](CauterizedMeshPartPayload& data) { if (data._model && data._model->isLoaded()) { - if (!data.hasStartedFade() && data._model->getGeometry()->areTexturesLoaded()) { - data.startFade(); - } // Ensure the model geometry was not reset between frames if (deleteGeometryCounter == data._model->getGeometryCounter()) { // lazy update of cluster matrices used for rendering. We need to update them here, so we can correctly update the bounding box. diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp index 57346ceb53..fa180a654a 100644 --- a/libraries/render-utils/src/MeshPartPayload.cpp +++ b/libraries/render-utils/src/MeshPartPayload.cpp @@ -402,7 +402,7 @@ ItemKey ModelMeshPartPayload::getKey() const { } } - if (!_hasFinishedFade) { + if (_fadeState != FADE_COMPLETE) { builder.withTransparent(); } @@ -472,7 +472,7 @@ ShapeKey ModelMeshPartPayload::getShapeKey() const { } ShapeKey::Builder builder; - if (isTranslucent || !_hasFinishedFade) { + if (isTranslucent || _fadeState != FADE_COMPLETE) { builder.withTranslucent(); } if (hasTangents) { @@ -513,9 +513,10 @@ void ModelMeshPartPayload::bindMesh(gpu::Batch& batch) const { batch.setInputStream(2, _drawMesh->getVertexStream().makeRangedStream(2)); } - float fadeRatio = _isFading ? Interpolate::calculateFadeRatio(_fadeStartTime) : 1.0f; - if (!_hasColorAttrib || fadeRatio < 1.0f) { - batch._glColor4f(1.0f, 1.0f, 1.0f, fadeRatio); + if (_fadeState != FADE_COMPLETE) { + batch._glColor4f(1.0f, 1.0f, 1.0f, computeFadeAlpha()); + } else if (!_hasColorAttrib) { + batch._glColor4f(1.0f, 1.0f, 1.0f, 1.0f); } } @@ -528,17 +529,23 @@ void ModelMeshPartPayload::bindTransform(gpu::Batch& batch, const ShapePipeline: batch.setModelTransform(_transform); } -void ModelMeshPartPayload::startFade() { - bool shouldFade = EntityItem::getEntitiesShouldFadeFunction()(); - if (shouldFade) { - _fadeStartTime = usecTimestampNow(); - _hasStartedFade = true; - _hasFinishedFade = false; - } else { - _isFading = true; - _hasStartedFade = true; - _hasFinishedFade = true; +float ModelMeshPartPayload::computeFadeAlpha() const { + if (_fadeState == FADE_WAITING_TO_START) { + return 0.0f; } + float fadeAlpha = 1.0f; + const float INV_FADE_PERIOD = 1.0f / (float)(1 * USECS_PER_SECOND); + float fraction = (float)(usecTimestampNow() - _fadeStartTime) * INV_FADE_PERIOD; + if (fraction < 1.0f) { + fadeAlpha = Interpolate::simpleNonLinearBlend(fraction); + } + if (fadeAlpha >= 1.0f) { + _fadeState = FADE_COMPLETE; + // when fade-in completes we flag model for one last "render item update" + _model->setRenderItemsNeedUpdate(); + return 1.0f; + } + return Interpolate::simpleNonLinearBlend(fadeAlpha); } void ModelMeshPartPayload::render(RenderArgs* args) const { @@ -548,33 +555,28 @@ void ModelMeshPartPayload::render(RenderArgs* args) const { return; // bail asap } - // If we didn't start the fade in, check if we are ready to now.... - if (!_hasStartedFade && _model->isLoaded() && _model->getGeometry()->areTexturesLoaded()) { - const_cast(*this).startFade(); + if (_fadeState == FADE_WAITING_TO_START) { + if (_model->isLoaded() && _model->getGeometry()->areTexturesLoaded()) { + if (EntityItem::getEntitiesShouldFadeFunction()()) { + _fadeStartTime = usecTimestampNow(); + _fadeState = FADE_IN_PROGRESS; + } else { + _fadeState = FADE_COMPLETE; + } + _model->setRenderItemsNeedUpdate(); + } else { + return; + } } - // If we still didn't start the fade in, bail - if (!_hasStartedFade) { + if (!args) { return; } - - // When an individual mesh parts like this finishes its fade, we will mark the Model as - // having render items that need updating - bool nextIsFading = _isFading ? isStillFading() : false; - bool startFading = !_isFading && !_hasFinishedFade && _hasStartedFade; - bool endFading = _isFading && !nextIsFading; - if (startFading || endFading) { - _isFading = startFading; - _hasFinishedFade = endFading; - _model->setRenderItemsNeedUpdate(); - } - - gpu::Batch& batch = *(args->_batch); - if (!getShapeKey().isValid()) { return; } + gpu::Batch& batch = *(args->_batch); auto locations = args->_pipeline->locations; assert(locations); @@ -588,9 +590,7 @@ void ModelMeshPartPayload::render(RenderArgs* args) const { // apply material properties bindMaterial(batch, locations); - if (args) { - args->_details._materialSwitches++; - } + args->_details._materialSwitches++; // Draw! { @@ -598,8 +598,6 @@ void ModelMeshPartPayload::render(RenderArgs* args) const { drawCall(batch); } - if (args) { - const int INDICES_PER_TRIANGLE = 3; - args->_details._trianglesRendered += _drawPart._numIndices / INDICES_PER_TRIANGLE; - } + const int INDICES_PER_TRIANGLE = 3; + args->_details._trianglesRendered += _drawPart._numIndices / INDICES_PER_TRIANGLE; } diff --git a/libraries/render-utils/src/MeshPartPayload.h b/libraries/render-utils/src/MeshPartPayload.h index a2c932b589..7d0aeab2bd 100644 --- a/libraries/render-utils/src/MeshPartPayload.h +++ b/libraries/render-utils/src/MeshPartPayload.h @@ -21,6 +21,10 @@ #include +const uint8_t FADE_WAITING_TO_START = 0; +const uint8_t FADE_IN_PROGRESS = 1; +const uint8_t FADE_COMPLETE = 2; + class Model; class MeshPartPayload { @@ -87,10 +91,7 @@ public: void updateTransformForSkinnedMesh(const Transform& transform, const QVector& clusterMatrices); - // Entity fade in - void startFade(); - bool hasStartedFade() { return _hasStartedFade; } - bool isStillFading() const { return Interpolate::calculateFadeRatio(_fadeStartTime) < 1.0f; } + float computeFadeAlpha() const; // Render Item interface render::ItemKey getKey() const override; @@ -113,10 +114,8 @@ public: bool _isBlendShaped{ false }; private: - quint64 _fadeStartTime { 0 }; - bool _hasStartedFade { false }; - mutable bool _hasFinishedFade { false }; - mutable bool _isFading { false }; + mutable quint64 _fadeStartTime { 0 }; + mutable uint8_t _fadeState { FADE_WAITING_TO_START }; }; namespace render { diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index ef613e8c14..b79e69a9b7 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -227,9 +227,6 @@ void Model::updateRenderItems() { foreach (auto itemID, self->_modelMeshRenderItems.keys()) { pendingChanges.updateItem(itemID, [modelTransform, deleteGeometryCounter](ModelMeshPartPayload& data) { if (data._model && data._model->isLoaded()) { - if (!data.hasStartedFade() && data._model->getGeometry()->areTexturesLoaded()) { - data.startFade(); - } // Ensure the model geometry was not reset between frames if (deleteGeometryCounter == data._model->_deleteGeometryCounter) { // lazy update of cluster matrices used for rendering. We need to update them here, so we can correctly update the bounding box. @@ -1450,7 +1447,6 @@ void ModelBlender::noteRequiresBlend(ModelPointer model) { { Lock lock(_mutex); - _modelsRequiringBlends.insert(model); } } diff --git a/libraries/shared/src/Interpolate.cpp b/libraries/shared/src/Interpolate.cpp index b20c7b8fac..ba93a21a8e 100644 --- a/libraries/shared/src/Interpolate.cpp +++ b/libraries/shared/src/Interpolate.cpp @@ -61,6 +61,13 @@ float Interpolate::interpolate3Points(float y1, float y2, float y3, float u) { } } +float Interpolate::simpleNonLinearBlend(float fraction) { + // uses arctan() to map a linear distribution in domain [0,1] to a non-linear blend (slow out, slow in) in range [0,1] + const float WIDTH = 20.0f; + const float INV_ARCTAN_WIDTH = 0.339875327433f; // 1 / (2 * atan(WIDTH/2)) + return 0.5f + atanf(WIDTH * (fraction - 0.5f)) * INV_ARCTAN_WIDTH; +} + float Interpolate::calculateFadeRatio(quint64 start) { const float FADE_TIME = 1.0f; float t = 2.0f * std::min(((float)(usecTimestampNow() - start)) / ((float)(FADE_TIME * USECS_PER_SECOND)), 1.0f); @@ -69,4 +76,4 @@ float Interpolate::calculateFadeRatio(quint64 start) { // The easing function isn't exactly 1 at t = 2, so we need to scale the whole function up slightly const float EASING_SCALE = 1.001f; return std::min(EASING_SCALE * fadeRatio, 1.0f); -} \ No newline at end of file +} diff --git a/libraries/shared/src/Interpolate.h b/libraries/shared/src/Interpolate.h index a9fa5baad2..79ebd2f7fc 100644 --- a/libraries/shared/src/Interpolate.h +++ b/libraries/shared/src/Interpolate.h @@ -25,6 +25,10 @@ public: // pass through all three y values. Return value lies wholly within the range of y values passed in. static float interpolate3Points(float y1, float y2, float y3, float u); + // returns smooth in and out blend between 0 and 1 + // DANGER: assumes fraction is properly inside range [0, 1] + static float simpleNonLinearBlend(float fraction); + static float calculateFadeRatio(quint64 start); }; From 5adc2cc593c61b3b01baf80ab13abe2c346072ed Mon Sep 17 00:00:00 2001 From: David Kelly Date: Thu, 19 Jan 2017 17:22:39 -0800 Subject: [PATCH 099/109] Couple improvements Only send one message when gain is changed. Eliminated the bool and now just sending un-personalMute, etc... --- interface/resources/qml/hifi/NameCard.qml | 17 ++++++++++++----- interface/resources/qml/hifi/Pal.qml | 2 +- scripts/system/pal.js | 18 +++++++++++++----- 3 files changed, 26 insertions(+), 11 deletions(-) diff --git a/interface/resources/qml/hifi/NameCard.qml b/interface/resources/qml/hifi/NameCard.qml index 2725ea1617..bffac0b2b4 100644 --- a/interface/resources/qml/hifi/NameCard.qml +++ b/interface/resources/qml/hifi/NameCard.qml @@ -283,7 +283,12 @@ Item { maximumValue: 20.0 stepSize: 5 updateValueWhileDragging: true - onValueChanged: updateGainFromQML(uuid, value) + onValueChanged: updateGainFromQML(uuid, value, false) + onPressedChanged: { + if (!pressed) { + updateGainFromQML(uuid, value, true) + } + } MouseArea { anchors.fill: parent onWheel: { @@ -297,7 +302,8 @@ Item { mouse.accepted = false } onReleased: { - // Pass through to Slider + // the above mouse.accepted seems to make this + // never get called, nonetheless... mouse.accepted = false } } @@ -319,12 +325,13 @@ Item { } } - function updateGainFromQML(avatarUuid, sliderValue) { - if (pal.gainSliderValueDB[avatarUuid] !== sliderValue) { + function updateGainFromQML(avatarUuid, sliderValue, isReleased) { + if (isReleased || pal.gainSliderValueDB[avatarUuid] !== sliderValue) { pal.gainSliderValueDB[avatarUuid] = sliderValue; var data = { sessionId: avatarUuid, - gain: sliderValue + gain: sliderValue, + isReleased: isReleased }; pal.sendToScript({method: 'updateGain', params: data}); } diff --git a/interface/resources/qml/hifi/Pal.qml b/interface/resources/qml/hifi/Pal.qml index 1ce6ab5e7d..ba1ffe37b6 100644 --- a/interface/resources/qml/hifi/Pal.qml +++ b/interface/resources/qml/hifi/Pal.qml @@ -247,7 +247,7 @@ Rectangle { userModel.setProperty(model.userIndex, styleData.role, newValue) userModelData[model.userIndex][styleData.role] = newValue // Defensive programming Users[styleData.role](model.sessionId, newValue) - UserActivityLogger["palAction"](newValue ? "un-" + styleData.role : styleData.role, model.sessionId) + UserActivityLogger["palAction"](newValue ? styleData.role : "un-" + styleData.role, model.sessionId) if (styleData.role === "ignore") { userModel.setProperty(model.userIndex, "personalMute", newValue) userModelData[model.userIndex]["personalMute"] = newValue // Defensive programming diff --git a/scripts/system/pal.js b/scripts/system/pal.js index 3d91bd27b2..1fbc7db9a0 100644 --- a/scripts/system/pal.js +++ b/scripts/system/pal.js @@ -103,6 +103,8 @@ ExtendedOverlay.prototype.select = function (selected) { return; } + UserActivityLogger.palAction(selected ? "avatar_selected" : "avatar_deselected", this.key); + this.editOverlay({color: color(selected, this.hovering, this.audioLevel)}); if (this.model) { this.model.editOverlay({textures: textures(selected)}); @@ -208,7 +210,6 @@ pal.fromQml.connect(function (message) { // messages are {method, params}, like var id = overlay.key; var selected = ExtendedOverlay.isSelected(id); overlay.select(selected); - UserActivityLogger.palAction("avatar_selected", id); }); HighlightedEntity.clearOverlays(); @@ -233,17 +234,24 @@ pal.fromQml.connect(function (message) { // messages are {method, params}, like case 'refresh': removeOverlays(); populateUserList(); - UserActivityLogger.palAction("refresh"); + UserActivityLogger.palAction("refresh", ""); break; case 'updateGain': data = message.params; - Users.setAvatarGain(data['sessionId'], data['gain']); - UserActivityLogger.palAction("avatar_gain_changed", data['sessionId']); + if (data['isReleased']) { + // isReleased=true happens once at the end of a cycle of dragging + // the slider about, but with same gain as last isReleased=false so + // we don't set the gain in that case, and only here do we want to + // send an analytic event. + UserActivityLogger.palAction("avatar_gain_changed", data['sessionId']); + } else { + Users.setAvatarGain(data['sessionId'], data['gain']); + } break; case 'displayNameUpdate': if (MyAvatar.displayName != message.params) { MyAvatar.displayName = message.params; - UserActivityLogger.palAction("display_name_change"); + UserActivityLogger.palAction("display_name_change", ""); } break; default: From eba96c45d7d1a8d5319814dad0f18069118463b8 Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Sun, 8 Jan 2017 18:30:40 -0500 Subject: [PATCH 100/109] fix warnings for signed comp in AudioMixerSlavePool --- assignment-client/src/audio/AudioMixerSlavePool.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/assignment-client/src/audio/AudioMixerSlavePool.cpp b/assignment-client/src/audio/AudioMixerSlavePool.cpp index 6446092448..1b884fa089 100644 --- a/assignment-client/src/audio/AudioMixerSlavePool.cpp +++ b/assignment-client/src/audio/AudioMixerSlavePool.cpp @@ -131,7 +131,7 @@ void AudioMixerSlavePool::setNumThreads(int numThreads) { } void AudioMixerSlavePool::resize(int numThreads) { - assert(_numThreads == _slaves.size()); + assert(_numThreads == (int)_slaves.size()); #ifdef AUDIO_SINGLE_THREADED qDebug("%s: running single threaded", __FUNCTION__, numThreads); @@ -182,6 +182,6 @@ void AudioMixerSlavePool::resize(int numThreads) { } _numThreads = _numStarted = _numFinished = numThreads; - assert(_numThreads == _slaves.size()); + assert(_numThreads == (int)_slaves.size()); #endif } From c83cd4a94a238ef418e474f7b57a197bf0f58da1 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Thu, 19 Jan 2017 16:03:46 -0800 Subject: [PATCH 101/109] Complex checkpoint --- interface/resources/qml/hifi/Pal.qml | 34 ++++++++++++++++++++------ scripts/system/libraries/entityList.js | 2 +- scripts/system/pal.js | 12 ++++++--- 3 files changed, 36 insertions(+), 12 deletions(-) diff --git a/interface/resources/qml/hifi/Pal.qml b/interface/resources/qml/hifi/Pal.qml index 989b560e4e..374f9618bb 100644 --- a/interface/resources/qml/hifi/Pal.qml +++ b/interface/resources/qml/hifi/Pal.qml @@ -416,6 +416,20 @@ Rectangle { } } } + Timer { + property var selected + property int userIndex + id: selectionTimer + onTriggered: { + if (selected) { + table.selection.clear(); // for now, no multi-select + table.selection.select(userIndex); + table.positionViewAtRow(userIndex, ListView.Visible); + } else { + table.selection.deselect(userIndex); + } + } + } function findSessionIndex(sessionId, optionalData) { // no findIndex in .qml var data = optionalData || userModelData, length = data.length; @@ -453,19 +467,25 @@ Rectangle { case 'select': var sessionIds = message.params[0]; var selected = message.params[1]; + var alreadyRefreshed = message.params[2]; var userIndex = findSessionIndex(sessionIds[0]); if (sessionIds.length > 1) { letterbox("", "", 'Only one user can be selected at a time.'); } else if (userIndex < 0) { - letterbox("", "", 'The last editor is not among this list of users.'); - } else { - if (selected) { - table.selection.clear(); // for now, no multi-select - table.selection.select(userIndex); - table.positionViewAtRow(userIndex, ListView.Visible); + if (alreadyRefreshed === true) { + letterbox('', '', 'The last editor of this object is either you or not among this list of users.'); } else { - table.selection.deselect(userIndex); + pal.sendToScript({method: 'refresh', params: message.params}); } + } else { + if (alreadyRefreshed === true) { + selectionTimer.interval = 250; + } else { + selectionTimer.interval = 0; + } + selectionTimer.selected = selected; + selectionTimer.userIndex = userIndex; + selectionTimer.start(); } break; // Received an "updateUsername()" request from the JS diff --git a/scripts/system/libraries/entityList.js b/scripts/system/libraries/entityList.js index 085d4f5e27..6dc2486ffb 100644 --- a/scripts/system/libraries/entityList.js +++ b/scripts/system/libraries/entityList.js @@ -134,7 +134,7 @@ EntityListTool = function(opts) { Window.alert('There were no recent users of the ' + selectionManager.selections.length + ' selected objects.'); } else { // No need to subscribe if we're just sending. - Messages.sendMessage('com.highfidelity.pal', JSON.stringify({method: 'select', params: [dedupped, true]}), 'local'); + Messages.sendMessage('com.highfidelity.pal', JSON.stringify({method: 'select', params: [dedupped, true, false]}), 'local'); } } else if (data.type == "delete") { deleteSelectedEntities(); diff --git a/scripts/system/pal.js b/scripts/system/pal.js index 3b34eed268..f148ad5fdb 100644 --- a/scripts/system/pal.js +++ b/scripts/system/pal.js @@ -233,7 +233,7 @@ pal.fromQml.connect(function (message) { // messages are {method, params}, like break; case 'refresh': removeOverlays(); - populateUserList(); + populateUserList(message.params); UserActivityLogger.palAction("refresh", ""); break; case 'updateGain': @@ -271,7 +271,7 @@ function addAvatarNode(id) { color: color(selected, false, 0.0), ignoreRayIntersection: false}, selected, true); } -function populateUserList() { +function populateUserList(selectData) { var data = []; AvatarList.getAvatarIdentifiers().sort().forEach(function (id) { // sorting the identifiers is just an aid for debugging var avatar = AvatarList.getAvatar(id); @@ -295,7 +295,11 @@ function populateUserList() { data.push(avatarPalDatum); print('PAL data:', JSON.stringify(avatarPalDatum)); }); - pal.sendToQml({method: 'users', params: data}); + pal.sendToQml({ method: 'users', params: data }); + if (selectData) { + selectData[2] = true; + pal.sendToQml({ method: 'select', params: selectData }); + } } // The function that handles the reply from the server @@ -388,7 +392,7 @@ function removeOverlays() { function handleClick(pickRay) { ExtendedOverlay.applyPickRay(pickRay, function (overlay) { // Don't select directly. Tell qml, who will give us back a list of ids. - var message = {method: 'select', params: [[overlay.key], !overlay.selected]}; + var message = {method: 'select', params: [[overlay.key], !overlay.selected, false]}; pal.sendToQml(message); return true; }); From 9c9206acc0001de5ed0148a0da977e244fd2bcef Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Thu, 19 Jan 2017 17:37:09 -0800 Subject: [PATCH 102/109] Prevent logspam --- interface/resources/qml/hifi/Pal.qml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/interface/resources/qml/hifi/Pal.qml b/interface/resources/qml/hifi/Pal.qml index 374f9618bb..7b747f8fb2 100644 --- a/interface/resources/qml/hifi/Pal.qml +++ b/interface/resources/qml/hifi/Pal.qml @@ -218,10 +218,10 @@ Rectangle { id: nameCard // Properties displayName: styleData.value - userName: model && model.userName - audioLevel: model && model.audioLevel + userName: model ? model.userName : "" + audioLevel: model ? model.audioLevel : 0.0 visible: !isCheckBox && !isButton - uuid: model && model.sessionId + uuid: model ? model.sessionId : "" selected: styleData.selected isAdmin: model && model.admin // Size @@ -241,9 +241,9 @@ Rectangle { id: actionCheckBox visible: isCheckBox anchors.centerIn: parent - checked: model[styleData.role] + checked: model ? model[styleData.role] : false // If this is a "Personal Mute" checkbox, disable the checkbox if the "Ignore" checkbox is checked. - enabled: !(styleData.role === "personalMute" && model["ignore"]) + enabled: !(styleData.role === "personalMute" && (model ? model["ignore"] : true)) boxSize: 24 onClicked: { var newValue = !model[styleData.role] From 8f3c4794c6c5a5497b210ff1b74eb76353509a8b Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Fri, 20 Jan 2017 10:21:18 -0800 Subject: [PATCH 103/109] Comments --- interface/resources/qml/hifi/Pal.qml | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/interface/resources/qml/hifi/Pal.qml b/interface/resources/qml/hifi/Pal.qml index 7b747f8fb2..51ef6e17e3 100644 --- a/interface/resources/qml/hifi/Pal.qml +++ b/interface/resources/qml/hifi/Pal.qml @@ -416,9 +416,11 @@ Rectangle { } } } + // Timer used when selecting table rows that aren't yet present in the model + // (i.e. when selecting avatars using edit.js or sphere overlays) Timer { - property var selected - property int userIndex + property bool selected // Selected or deselected? + property int userIndex // The userIndex of the avatar we want to select id: selectionTimer onTriggered: { if (selected) { @@ -472,15 +474,20 @@ Rectangle { if (sessionIds.length > 1) { letterbox("", "", 'Only one user can be selected at a time.'); } else if (userIndex < 0) { + // If we've already refreshed the PAL and the avatar still isn't present in the model... if (alreadyRefreshed === true) { letterbox('', '', 'The last editor of this object is either you or not among this list of users.'); } else { pal.sendToScript({method: 'refresh', params: message.params}); } } else { + // If we've already refreshed the PAL and found the avatar in the model if (alreadyRefreshed === true) { + // Wait a little bit before trying to actually select the avatar in the table selectionTimer.interval = 250; } else { + // If we've found the avatar in the model and didn't need to refresh, + // select the avatar in the table immediately selectionTimer.interval = 0; } selectionTimer.selected = selected; From 11a57efafb2798078f8227445557f6384c077bbd Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Fri, 20 Jan 2017 13:44:29 -0800 Subject: [PATCH 104/109] Fix it! --- interface/resources/qml/hifi/Pal.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/resources/qml/hifi/Pal.qml b/interface/resources/qml/hifi/Pal.qml index 51ef6e17e3..50f1808f94 100644 --- a/interface/resources/qml/hifi/Pal.qml +++ b/interface/resources/qml/hifi/Pal.qml @@ -426,7 +426,7 @@ Rectangle { if (selected) { table.selection.clear(); // for now, no multi-select table.selection.select(userIndex); - table.positionViewAtRow(userIndex, ListView.Visible); + table.positionViewAtRow(userIndex, ListView.Beginning); } else { table.selection.deselect(userIndex); } From ab7ce1921159a21efac98932071b6ad0e97fbbb1 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 23 Jan 2017 09:49:46 -0800 Subject: [PATCH 105/109] use html_id for settings anchor if no name --- domain-server/resources/web/settings/index.shtml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/domain-server/resources/web/settings/index.shtml b/domain-server/resources/web/settings/index.shtml index 7af21fe84b..3eb7a53726 100644 --- a/domain-server/resources/web/settings/index.shtml +++ b/domain-server/resources/web/settings/index.shtml @@ -12,7 +12,7 @@