From b415558f307fddc422902f5023ceb9e5190fbc9d Mon Sep 17 00:00:00 2001 From: samcake Date: Fri, 22 Jan 2016 03:23:43 -0800 Subject: [PATCH 01/49] FIrst draft of an octree for rendering --- libraries/render/src/render/Octree.h | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 libraries/render/src/render/Octree.h diff --git a/libraries/render/src/render/Octree.h b/libraries/render/src/render/Octree.h new file mode 100644 index 0000000000..e69de29bb2 From 3ebcb705ab231fec0484d6ac61e03434f725b573 Mon Sep 17 00:00:00 2001 From: samcake Date: Mon, 25 Jan 2016 09:44:01 -0800 Subject: [PATCH 02/49] defining the octree --- libraries/render/src/render/Octree.h | 139 +++++++++++++++++++++++++++ 1 file changed, 139 insertions(+) diff --git a/libraries/render/src/render/Octree.h b/libraries/render/src/render/Octree.h index e69de29bb2..6dc9e05bfb 100644 --- a/libraries/render/src/render/Octree.h +++ b/libraries/render/src/render/Octree.h @@ -0,0 +1,139 @@ + + +namespace render { + + class Octree { + public: + enum Octant { + L_B_N = 0, + R_B_N = 1, + L_T_N = 2, + R_T_N = 3, + L_B_F = 4, + R_B_F = 5, + L_T_F = 6, + R_T_F = 7, + + NUM_OCTANTS, + }; + + enum OctantBits { + XAxis = 0x01, + YAxis = 0x02, + ZAXis = 0x04, + + }; + + // Depth, Width, Volume + // {0, 1, 1} + // {1, 2, 8} + // {2, 4, 64} + // {3, 8, 512} + // {4, 16, 4096} + // {5, 32, 32768} + // {6, 64, 262144} + // {7, 128, 2097152} + // {8, 256, 16777216} + // {9, 512, 134217728} + // {10, 1024, 1073741824} + // {11, 2048, 8589934592} + // {12, 4096, 68719476736} + // {13, 8192, 549755813888} + // {14, 16384, 4398046511104} + // {15, 32768, 35184372088832} + // {16, 65536, 281474976710656} + + using Depth = int8_t; // Max depth is 15 => 32Km root down to 1m cells + using Coord = int16_t// Need 16bits integer coordinates on each axes: 32768 cell positions + using Pos = glm::vec3; + + class CellCoord { + public: + CellCoord() {} + CellCoord(const Pos& xyz, Depth d) : pos(xyz), depth(d) {} + CellCoord(Depth d) : pos(0), depth(d) {} + + union { + glm::vec4 raw; + struct { + Pos pos { 0 }; + int8_t spare { 0 }; + Depth depth { 0 }; + }; + }; + + CellCoord parent() const { return CellCoord{ pos >> 1, (d <= 0? 0 : d-1) }; } + CellCoord child(Octant octant) const { return CellCoord{ Pos((pos.x << 1) | (octant & XAxis), + (pos.y << 1) | (octant & YAxis), + (pos.z << 1) | (octant & ZAxis)), d+1 }; } + }; + + class Range { + public: + Pos _min; + Pos _max; + } + + + // the cell description + class Cell { + public: + using Index = int32_t; + static const Index INVALID = -1; + + + Index children[NUM_OCTANTS] = { INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID }; + Index parent{ INVALID }; + + Index brick{ INVALID }; + + CellPos cellpos; + }; + using Cells = std::vector< Cell >; + + class CellPath { + public: + using PosArray = std::vector< CellPos >; + PosArray cellCoords; + + }; + + class Brick { + public: + + } + using Bricks = std::vector< Block >; + + + + // Octree members + Cells _cells; + Bricks _bricks; + + + CellPath rootTo(const CellPos& dest) { + CellPos current{ dest }; + CellPath path(dest.depth + 1) + path[dest.depth] = dest; + while (current.depth > 0) { + current = current.parent(); + path[current.depth] = current); + } + return path; + } + + CellPath rootTo(const CellPos& dest) { + CellPos current{ dest }; + CellPath path(dest.depth + 1) + path[dest.depth] = dest; + while (current.depth > 0) { + current = current.parent(); + path[current.depth] = current); + } + return path; + } + }; + + + +} \ No newline at end of file From b96bbab22f037df8f9bf761693ecbfddf4dc2a99 Mon Sep 17 00:00:00 2001 From: samcake Date: Mon, 25 Jan 2016 18:14:05 -0800 Subject: [PATCH 03/49] Drafting the Octree and a debugging job --- .../render-utils/src/RenderDeferredTask.cpp | 8 + libraries/render/src/render/Octree.h | 202 +++++++++++------- libraries/render/src/render/Scene.cpp | 6 + libraries/render/src/render/Scene.h | 4 + 4 files changed, 138 insertions(+), 82 deletions(-) diff --git a/libraries/render-utils/src/RenderDeferredTask.cpp b/libraries/render-utils/src/RenderDeferredTask.cpp index 7afab44c6c..659a0de40a 100755 --- a/libraries/render-utils/src/RenderDeferredTask.cpp +++ b/libraries/render-utils/src/RenderDeferredTask.cpp @@ -25,6 +25,7 @@ #include "render/DrawTask.h" #include "render/DrawStatus.h" +#include "render/DrawSceneOctree.h" #include "AmbientOcclusionEffect.h" #include "AntialiasingEffect.h" @@ -135,6 +136,13 @@ RenderDeferredTask::RenderDeferredTask(CullFunctor cullFunctor) : Task() { _drawDebugDeferredBufferIndex = (int)_jobs.size() - 1; enableJob(_drawDebugDeferredBufferIndex, false); + // Scene Octree Debuging job + { + addJob("DrawSceneOctree"); + // _drawStatusJobIndex = (int)_jobs.size() - 1; + // enableJob(_drawStatusJobIndex, false); + } + // Status icon rendering job { // Grab a texture map representing the different status icons and assign that to the drawStatsuJob diff --git a/libraries/render/src/render/Octree.h b/libraries/render/src/render/Octree.h index 6dc9e05bfb..eee93a37d9 100644 --- a/libraries/render/src/render/Octree.h +++ b/libraries/render/src/render/Octree.h @@ -1,29 +1,57 @@ +// +// Octree.h +// render/src/render +// +// Created by Sam Gateau on 1/25/16. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +#ifndef hifi_render_Octree_h +#define hifi_render_Octree_h + +#include +#include +#include +#include +#include +#include namespace render { class Octree { public: - enum Octant { - L_B_N = 0, - R_B_N = 1, - L_T_N = 2, - R_T_N = 3, - L_B_F = 4, - R_B_F = 5, - L_T_F = 6, - R_T_F = 7, - + + using LinkStorage = int8_t; + enum Link : LinkStorage { + + Octant_L_B_N = 0, + Octant_R_B_N = 1, + Octant_L_T_N = 2, + Octant_R_T_N = 3, + Octant_L_B_F = 4, + Octant_R_B_F = 5, + Octant_L_T_F = 6, + Octant_R_T_F = 7, + NUM_OCTANTS, - }; - - enum OctantBits { + + Parent = NUM_OCTANTS, + + NUM_LINKS = NUM_OCTANTS + 1, + XAxis = 0x01, YAxis = 0x02, - ZAXis = 0x04, - + ZAxis = 0x04, + + NoLink = 0x0F, }; - + + using Octant = Link; + + // Depth, Width, Volume // {0, 1, 1} // {1, 2, 8} @@ -44,96 +72,106 @@ namespace render { // {16, 65536, 281474976710656} using Depth = int8_t; // Max depth is 15 => 32Km root down to 1m cells - using Coord = int16_t// Need 16bits integer coordinates on each axes: 32768 cell positions - using Pos = glm::vec3; + using Coord = int16_t;// Need 16bits integer coordinates on each axes: 32768 cell positions + using Coord3 = glm::i16vec3; + using Coord4 = glm::i16vec4; - class CellCoord { + class CellPoint { + void assertValid() { + assert((pos.x >= 0) && (pos.y >= 0) && (pos.z >= 0)); + assert((pos.x < (2 << depth)) && (pos.y < (2 << depth)) && (pos.z < (2 << depth))); + } public: - CellCoord() {} - CellCoord(const Pos& xyz, Depth d) : pos(xyz), depth(d) {} - CellCoord(Depth d) : pos(0), depth(d) {} - - union { - glm::vec4 raw; - struct { - Pos pos { 0 }; - int8_t spare { 0 }; - Depth depth { 0 }; - }; - }; - - CellCoord parent() const { return CellCoord{ pos >> 1, (d <= 0? 0 : d-1) }; } - CellCoord child(Octant octant) const { return CellCoord{ Pos((pos.x << 1) | (octant & XAxis), - (pos.y << 1) | (octant & YAxis), - (pos.z << 1) | (octant & ZAxis)), d+1 }; } + CellPoint() {} + CellPoint(const Coord3& xyz, Depth d) : pos(xyz), depth(d) { assertValid(); } + CellPoint(Depth d) : pos(0), depth(d) { assertValid(); } + + Coord3 pos{ 0 }; + uint8_t spare{ 0 }; + Depth depth{ 0 }; + + + // Eval the octant of this cell relative to its parent + Octant octant() const { return Octant((pos.x & XAxis) | (pos.y & YAxis) | (pos.z & ZAxis)); } + + // Get the Parent cell pos of this cell + CellPoint parent() const { + return CellPoint{ pos >> Coord3(1), Depth(depth <= 0 ? 0 : depth - 1) }; + } + CellPoint child(Link octant) const { + return CellPoint{ pos << Coord3(1) | Coord3((Coord)bool(octant & XAxis), (Coord)bool(octant & YAxis), (Coord)bool(octant & ZAxis)), Depth(depth + 1) }; + } + + using vector = std::vector< CellPoint >; + static vector rootTo(const CellPoint& dest) { + CellPoint current{ dest }; + vector path(dest.depth + 1); + path[dest.depth] = dest; + while (current.depth > 0) { + current = current.parent(); + path[current.depth] = current; + } + return path; + } }; + using CellPath = CellPoint::vector; + class Range { public: - Pos _min; - Pos _max; - } - - + Coord3 _min; + Coord3 _max; + }; + + // Cell or Brick Indexing + using Index = int32_t; + static const Index INVALID = -1; + using Indices = std::vector; + // the cell description class Cell { public: - using Index = int32_t; - static const Index INVALID = -1; - - Index children[NUM_OCTANTS] = { INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID }; - Index parent{ INVALID }; - + CellPoint cellpos; + + std::array links; + + Index parent() const { return links[Parent]; } + bool asParent() const { return parent() != INVALID; } + + Index child(Link octant) const { return links[octant]; } + bool asChild(Link octant) const { return child(octant) != INVALID; } + Index brick{ INVALID }; - - CellPos cellpos; + + Cell() : + links({ { INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID } }) + {} }; using Cells = std::vector< Cell >; - class CellPath { - public: - using PosArray = std::vector< CellPos >; - PosArray cellCoords; - - }; - + class Brick { public: - } - using Bricks = std::vector< Block >; + }; + using Bricks = std::vector< Brick >; // Octree members - Cells _cells; + Cells _cells = Cells(1, Cell()); // start with only the Cell root Bricks _bricks; - - CellPath rootTo(const CellPos& dest) { - CellPos current{ dest }; - CellPath path(dest.depth + 1) - path[dest.depth] = dest; - while (current.depth > 0) { - current = current.parent(); - path[current.depth] = current); - } - return path; - } - - CellPath rootTo(const CellPos& dest) { - CellPos current{ dest }; - CellPath path(dest.depth + 1) - path[dest.depth] = dest; - while (current.depth > 0) { - current = current.parent(); - path[current.depth] = current); - } - return path; - } + Octree() {}; + + // allocatePath + Indices allocateCellPath(const CellPath& path); + }; -} \ No newline at end of file +} + +#endif // hifi_render_Octree_h diff --git a/libraries/render/src/render/Scene.cpp b/libraries/render/src/render/Scene.cpp index 18cf1d8335..39f7eb63c7 100644 --- a/libraries/render/src/render/Scene.cpp +++ b/libraries/render/src/render/Scene.cpp @@ -139,6 +139,12 @@ void PendingChanges::merge(PendingChanges& changes) { Scene::Scene() { _items.push_back(Item()); // add the itemID #0 to nothing _masterBucketMap.allocateStandardOpaqueTranparentBuckets(); + + + _spatialTree; + Octree::CellPoint point{ Octree::Coord3{ 2, 4, 3 }, 3 }; + auto path = Octree::CellPoint::rootTo(point); + } ItemID Scene::allocateID() { diff --git a/libraries/render/src/render/Scene.h b/libraries/render/src/render/Scene.h index e28225a899..8cbb5a43dd 100644 --- a/libraries/render/src/render/Scene.h +++ b/libraries/render/src/render/Scene.h @@ -27,6 +27,8 @@ #include "model/Material.h" #include "ShapePipeline.h" +#include "Octree.h" + namespace render { class Context; @@ -527,6 +529,8 @@ protected: void removeItems(const ItemIDs& ids); void updateItems(const ItemIDs& ids, UpdateFunctors& functors); + Octree _spatialTree; + friend class Engine; }; From a847a1678802e8fdba99ea99482547c3582faa79 Mon Sep 17 00:00:00 2001 From: samcake Date: Mon, 25 Jan 2016 18:15:07 -0800 Subject: [PATCH 04/49] Drafting the Octree and a debugging job --- .../render/src/render/DrawSceneOctree.cpp | 150 ++++++++++++++++++ libraries/render/src/render/DrawSceneOctree.h | 37 +++++ libraries/render/src/render/Octree.cpp | 43 +++++ .../render/src/render/drawCellBounds.slf | 20 +++ .../render/src/render/drawCellBounds.slv | 57 +++++++ 5 files changed, 307 insertions(+) create mode 100644 libraries/render/src/render/DrawSceneOctree.cpp create mode 100644 libraries/render/src/render/DrawSceneOctree.h create mode 100644 libraries/render/src/render/Octree.cpp create mode 100644 libraries/render/src/render/drawCellBounds.slf create mode 100644 libraries/render/src/render/drawCellBounds.slv diff --git a/libraries/render/src/render/DrawSceneOctree.cpp b/libraries/render/src/render/DrawSceneOctree.cpp new file mode 100644 index 0000000000..0364de8e49 --- /dev/null +++ b/libraries/render/src/render/DrawSceneOctree.cpp @@ -0,0 +1,150 @@ +// +// DrawSceneOctree.cpp +// render/src/render +// +// Created by Sam Gateau on 1/25/16. +// 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 "DrawSceneOctree.h" + +#include +#include + +#include +#include +#include + +#include + +#include "drawCellBounds_vert.h" +#include "drawCellBounds_frag.h" + +using namespace render; + +const int DrawSceneOctree_CellsSlot = 0; +const int DrawSceneOctree_OctreeInfoSlot = 1; + +const gpu::PipelinePointer DrawSceneOctree::getDrawCellBoundsPipeline() { + if (!_drawCellBoundsPipeline) { + auto vs = gpu::Shader::createVertex(std::string(drawCellBounds_vert)); + auto ps = gpu::Shader::createPixel(std::string(drawCellBounds_frag)); + gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps); + + gpu::Shader::BindingSet slotBindings; + slotBindings.insert(gpu::Shader::Binding(std::string("cellsBuffer"), DrawSceneOctree_CellsSlot)); + slotBindings.insert(gpu::Shader::Binding(std::string("octreeInfoBuffer"), DrawSceneOctree_OctreeInfoSlot)); + gpu::Shader::makeProgram(*program, slotBindings); + + auto state = std::make_shared(); + + state->setDepthTest(true, false, gpu::LESS_EQUAL); + + // Blend on transparent + 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); + + // Good to go add the brand new pipeline + _drawCellBoundsPipeline = gpu::Pipeline::create(program, state); + } + return _drawCellBoundsPipeline; +} + +void DrawSceneOctree::run(const SceneContextPointer& sceneContext, + const RenderContextPointer& renderContext) { + assert(renderContext->getArgs()); + assert(renderContext->getArgs()->_viewFrustum); + RenderArgs* args = renderContext->getArgs(); + auto& scene = sceneContext->_scene; + const int NUM_STATUS_VEC4_PER_ITEM = 2; + const int VEC4_LENGTH = 4; + + // FIrst thing, we update the local buffers with the values coming from Scene octree + int nbCells = 0; + { + if (!_cells) { + _cells = std::make_shared(); + } + if (!_octreeInfo) { + _octreeInfo = std::make_shared();; + } + /* + _cells->resize((inItems.size() * sizeof(AABox))); + _itemStatus->resize((inItems.size() * NUM_STATUS_VEC4_PER_ITEM * sizeof(glm::vec4))); + AABox* itemAABox = reinterpret_cast (_itemBounds->editData()); + glm::ivec4* itemStatus = reinterpret_cast (_itemStatus->editData()); + for (auto& item : inItems) { + if (!item.bounds.isInvalid()) { + if (!item.bounds.isNull()) { + (*itemAABox) = item.bounds; + } else { + (*itemAABox).setBox(item.bounds.getCorner(), 0.1f); + } + auto& itemScene = scene->getItem(item.id); + + auto itemStatusPointer = itemScene.getStatus(); + if (itemStatusPointer) { + // Query the current status values, this is where the statusGetter lambda get called + auto&& currentStatusValues = itemStatusPointer->getCurrentValues(); + int valueNum = 0; + for (int vec4Num = 0; vec4Num < NUM_STATUS_VEC4_PER_ITEM; vec4Num++) { + (*itemStatus) = glm::ivec4(Item::Status::Value::INVALID.getPackedData()); + for (int component = 0; component < VEC4_LENGTH; component++) { + valueNum = vec4Num * VEC4_LENGTH + component; + if (valueNum < (int)currentStatusValues.size()) { + (*itemStatus)[component] = currentStatusValues[valueNum].getPackedData(); + } + } + itemStatus++; + } + } else { + (*itemStatus) = glm::ivec4(Item::Status::Value::INVALID.getPackedData()); + itemStatus++; + (*itemStatus) = glm::ivec4(Item::Status::Value::INVALID.getPackedData()); + itemStatus++; + } + + nbItems++; + itemAABox++; + } + }*/ + } + + if (nbCells == 0) { + return; + } + + // Allright, something to render let's do it + /* gpu::doInBatch(args->_context, [&](gpu::Batch& batch) { + glm::mat4 projMat; + Transform viewMat; + args->_viewFrustum->evalProjectionMatrix(projMat); + args->_viewFrustum->evalViewTransform(viewMat); + batch.setViewportTransform(args->_viewport); + + batch.setProjectionTransform(projMat); + batch.setViewTransform(viewMat); + batch.setModelTransform(Transform()); + + // bind the one gpu::Pipeline we need + batch.setPipeline(getDrawCellBoundsPipeline()); + + AABox* itemAABox = reinterpret_cast (_itemBounds->editData()); + glm::ivec4* itemStatus = reinterpret_cast (_itemStatus->editData()); + + const unsigned int VEC3_ADRESS_OFFSET = 3; + + if ((renderContext->getDrawStatus() & showDisplayStatusFlag) > 0) { + for (int i = 0; i < nbCells; i++) { + batch._glUniform3fv(_drawItemBoundPosLoc, 1, (const float*) (itemAABox + i)); + batch._glUniform3fv(_drawItemBoundDimLoc, 1, ((const float*) (itemAABox + i)) + VEC3_ADRESS_OFFSET); + + batch.draw(gpu::LINES, 24, 0); + } + } + });*/ +} diff --git a/libraries/render/src/render/DrawSceneOctree.h b/libraries/render/src/render/DrawSceneOctree.h new file mode 100644 index 0000000000..c5b04b73b3 --- /dev/null +++ b/libraries/render/src/render/DrawSceneOctree.h @@ -0,0 +1,37 @@ +// +// DrawSceneOctree.h +// render/src/render +// +// Created by Sam Gateau on 1/25/16. +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_render_DrawSceneOctree_h +#define hifi_render_DrawSceneOctree_h + +#include "DrawTask.h" +#include "gpu/Batch.h" + +namespace render { + class DrawSceneOctree { + + gpu::PipelinePointer _drawCellBoundsPipeline; + gpu::BufferPointer _cells; + gpu::BufferPointer _octreeInfo; + + public: + + DrawSceneOctree() {} + + void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext); + + using JobModel = Task::Job::Model; + + const gpu::PipelinePointer getDrawCellBoundsPipeline(); + }; +} + +#endif // hifi_render_DrawStatus_h diff --git a/libraries/render/src/render/Octree.cpp b/libraries/render/src/render/Octree.cpp new file mode 100644 index 0000000000..bfe6940a4c --- /dev/null +++ b/libraries/render/src/render/Octree.cpp @@ -0,0 +1,43 @@ +// +// Octree.h +// render/src/render +// +// Created by Sam Gateau on 1/25/16. +// 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 "Octree.h" + + +using namespace render; + +Octree::Indices Octree::allocateCellPath(const CellPath& path) { + + + CellPoint point{ Coord3{ 2, 4, 3 }, 3 }; + + auto ppath = CellPoint::rootTo(point); + + Indices cellPath; + + Index currentIndex = 0; + Cell* currentCell = _cells.data(); + int d = 0; + cellPath.push_back(currentIndex); + + for (; d < path.back().depth; d++) { + auto& cellPoint = path[d]; + + auto currentIndex = currentCell->child(cellPoint.octant()); + if (currentIndex == INVALID) { + break; + } + + cellPath.push_back(currentIndex); + currentCell = _cells.data() + currentIndex; + } + + return cellPath; +} \ No newline at end of file diff --git a/libraries/render/src/render/drawCellBounds.slf b/libraries/render/src/render/drawCellBounds.slf new file mode 100644 index 0000000000..e82622085c --- /dev/null +++ b/libraries/render/src/render/drawCellBounds.slf @@ -0,0 +1,20 @@ +<@include gpu/Config.slh@> +<$VERSION_HEADER$> +// Generated on <$_SCRIBE_DATE$> +// drawCellBounds.slf +// fragment shader +// +// Created by Sam Gateau on 1/25/2016. +// 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 +// + +in vec4 varColor; +out vec4 outFragColor; + + +void main(void) { + outFragColor = vec4(1.0, 1.0, 1.0, 1.0); +} diff --git a/libraries/render/src/render/drawCellBounds.slv b/libraries/render/src/render/drawCellBounds.slv new file mode 100644 index 0000000000..634c694d4e --- /dev/null +++ b/libraries/render/src/render/drawCellBounds.slv @@ -0,0 +1,57 @@ +<@include gpu/Config.slh@> +<$VERSION_HEADER$> +// Generated on <$_SCRIBE_DATE$> +// +// drawCellBounds.slv +// Vertex shader +// +// Created by Sam Gateau on 1/25/2016 +// 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 gpu/Transform.slh@> + +<$declareStandardTransform()$> + +uniform vec3 inBoundPos; +uniform vec3 inBoundDim; + +void main(void) { + const vec4 UNIT_BOX[8] = vec4[8]( + vec4(0.0, 0.0, 0.0, 1.0), + vec4(1.0, 0.0, 0.0, 1.0), + vec4(0.0, 1.0, 0.0, 1.0), + vec4(1.0, 1.0, 0.0, 1.0), + vec4(0.0, 0.0, 1.0, 1.0), + vec4(1.0, 0.0, 1.0, 1.0), + vec4(0.0, 1.0, 1.0, 1.0), + vec4(1.0, 1.0, 1.0, 1.0) + ); + const int UNIT_BOX_LINE_INDICES[24] = int[24]( + 0, 1, + 1, 3, + 3, 2, + 2, 0, + 4, 5, + 5, 7, + 7, 6, + 6, 4, + 2, 6, + 3, 7, + 0, 4, + 1, 5 + ); + vec4 pos = UNIT_BOX[UNIT_BOX_LINE_INDICES[gl_VertexID]]; + + pos.xyz = inBoundPos + inBoundDim * pos.xyz; + + // standard transform + TransformCamera cam = getTransformCamera(); + TransformObject obj = getTransformObject(); + <$transformModelToClipPos(cam, obj, pos, gl_Position)$> + + // varTexcoord = (pos.xy + 1) * 0.5; +} \ No newline at end of file From 903c7dde5a504a67b53e5e874e61de27c50a7a0d Mon Sep 17 00:00:00 2001 From: samcake Date: Tue, 26 Jan 2016 01:59:00 -0800 Subject: [PATCH 05/49] Drafting the octree, allocating the first cells --- .../render/src/render/DrawSceneOctree.cpp | 40 ++++++++++--------- libraries/render/src/render/DrawSceneOctree.h | 3 +- libraries/render/src/render/Octree.cpp | 13 ++---- libraries/render/src/render/Octree.h | 17 ++++++++ libraries/render/src/render/Scene.cpp | 4 +- libraries/render/src/render/Scene.h | 5 ++- 6 files changed, 50 insertions(+), 32 deletions(-) diff --git a/libraries/render/src/render/DrawSceneOctree.cpp b/libraries/render/src/render/DrawSceneOctree.cpp index 0364de8e49..c47560bd87 100644 --- a/libraries/render/src/render/DrawSceneOctree.cpp +++ b/libraries/render/src/render/DrawSceneOctree.cpp @@ -25,8 +25,6 @@ using namespace render; -const int DrawSceneOctree_CellsSlot = 0; -const int DrawSceneOctree_OctreeInfoSlot = 1; const gpu::PipelinePointer DrawSceneOctree::getDrawCellBoundsPipeline() { if (!_drawCellBoundsPipeline) { @@ -35,10 +33,11 @@ const gpu::PipelinePointer DrawSceneOctree::getDrawCellBoundsPipeline() { gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps); gpu::Shader::BindingSet slotBindings; - slotBindings.insert(gpu::Shader::Binding(std::string("cellsBuffer"), DrawSceneOctree_CellsSlot)); - slotBindings.insert(gpu::Shader::Binding(std::string("octreeInfoBuffer"), DrawSceneOctree_OctreeInfoSlot)); gpu::Shader::makeProgram(*program, slotBindings); + _drawBoundPosLoc = program->getUniforms().findLocation("inBoundPos"); + _drawBoundDimLoc = program->getUniforms().findLocation("inBoundDim"); + auto state = std::make_shared(); state->setDepthTest(true, false, gpu::LESS_EQUAL); @@ -69,9 +68,19 @@ void DrawSceneOctree::run(const SceneContextPointer& sceneContext, if (!_cells) { _cells = std::make_shared(); } - if (!_octreeInfo) { + /* if (!_octreeInfo) { _octreeInfo = std::make_shared();; + }*/ + + const auto& inCells = scene->_spatialTree._cells; + _cells->resize(inCells.size() * sizeof(AABox)); + AABox* cellAABox = reinterpret_cast (_cells->editData()); + for (const auto& cell : inCells) { + (*cellAABox) = scene->_spatialTree.evalBound(cell.cellpos); + nbCells++; + cellAABox++; } + /* _cells->resize((inItems.size() * sizeof(AABox))); _itemStatus->resize((inItems.size() * NUM_STATUS_VEC4_PER_ITEM * sizeof(glm::vec4))); @@ -108,9 +117,7 @@ void DrawSceneOctree::run(const SceneContextPointer& sceneContext, itemStatus++; } - nbItems++; - itemAABox++; - } + } }*/ } @@ -119,7 +126,7 @@ void DrawSceneOctree::run(const SceneContextPointer& sceneContext, } // Allright, something to render let's do it - /* gpu::doInBatch(args->_context, [&](gpu::Batch& batch) { + gpu::doInBatch(args->_context, [&](gpu::Batch& batch) { glm::mat4 projMat; Transform viewMat; args->_viewFrustum->evalProjectionMatrix(projMat); @@ -133,18 +140,15 @@ void DrawSceneOctree::run(const SceneContextPointer& sceneContext, // bind the one gpu::Pipeline we need batch.setPipeline(getDrawCellBoundsPipeline()); - AABox* itemAABox = reinterpret_cast (_itemBounds->editData()); - glm::ivec4* itemStatus = reinterpret_cast (_itemStatus->editData()); + AABox* cellAABox = reinterpret_cast (_cells->editData()); const unsigned int VEC3_ADRESS_OFFSET = 3; - if ((renderContext->getDrawStatus() & showDisplayStatusFlag) > 0) { - for (int i = 0; i < nbCells; i++) { - batch._glUniform3fv(_drawItemBoundPosLoc, 1, (const float*) (itemAABox + i)); - batch._glUniform3fv(_drawItemBoundDimLoc, 1, ((const float*) (itemAABox + i)) + VEC3_ADRESS_OFFSET); + for (int i = 0; i < nbCells; i++) { + batch._glUniform3fv(_drawBoundPosLoc, 1, (const float*) (cellAABox + i)); + batch._glUniform3fv(_drawBoundDimLoc, 1, ((const float*) (cellAABox + i)) + VEC3_ADRESS_OFFSET); - batch.draw(gpu::LINES, 24, 0); - } + batch.draw(gpu::LINES, 24, 0); } - });*/ + }); } diff --git a/libraries/render/src/render/DrawSceneOctree.h b/libraries/render/src/render/DrawSceneOctree.h index c5b04b73b3..c0b943d20a 100644 --- a/libraries/render/src/render/DrawSceneOctree.h +++ b/libraries/render/src/render/DrawSceneOctree.h @@ -18,9 +18,10 @@ namespace render { class DrawSceneOctree { + int _drawBoundPosLoc; + int _drawBoundDimLoc; gpu::PipelinePointer _drawCellBoundsPipeline; gpu::BufferPointer _cells; - gpu::BufferPointer _octreeInfo; public: diff --git a/libraries/render/src/render/Octree.cpp b/libraries/render/src/render/Octree.cpp index bfe6940a4c..c693e3e7a3 100644 --- a/libraries/render/src/render/Octree.cpp +++ b/libraries/render/src/render/Octree.cpp @@ -14,12 +14,6 @@ using namespace render; Octree::Indices Octree::allocateCellPath(const CellPath& path) { - - - CellPoint point{ Coord3{ 2, 4, 3 }, 3 }; - - auto ppath = CellPoint::rootTo(point); - Indices cellPath; Index currentIndex = 0; @@ -27,14 +21,15 @@ Octree::Indices Octree::allocateCellPath(const CellPath& path) { int d = 0; cellPath.push_back(currentIndex); - for (; d < path.back().depth; d++) { + for (; d <= path.back().depth; d++) { auto& cellPoint = path[d]; auto currentIndex = currentCell->child(cellPoint.octant()); if (currentIndex == INVALID) { - break; + currentIndex = _cells.size(); + currentCell->links[cellPoint.octant()] = currentIndex; + _cells.push_back(Cell(cellPath.back(), cellPoint)); } - cellPath.push_back(currentIndex); currentCell = _cells.data() + currentIndex; } diff --git a/libraries/render/src/render/Octree.h b/libraries/render/src/render/Octree.h index eee93a37d9..674ef8080d 100644 --- a/libraries/render/src/render/Octree.h +++ b/libraries/render/src/render/Octree.h @@ -19,6 +19,9 @@ #include #include + +#include + namespace render { class Octree { @@ -147,6 +150,11 @@ namespace render { Cell() : links({ { INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID } }) {} + + Cell(Index parent, CellPoint pos) : + cellpos(pos), + links({ { INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, parent } }) + {} }; using Cells = std::vector< Cell >; @@ -163,11 +171,20 @@ namespace render { Cells _cells = Cells(1, Cell()); // start with only the Cell root Bricks _bricks; + float _size = 320.0f; + Octree() {}; // allocatePath Indices allocateCellPath(const CellPath& path); + AABox evalBound(const CellPoint& point) const { + + float width = (float) (_size / double(1 << point.depth)); + glm::vec3 corner = glm::vec3(-_size * 0.5f) + glm::vec3(point.pos) * width; + return AABox(corner, width); + + } }; diff --git a/libraries/render/src/render/Scene.cpp b/libraries/render/src/render/Scene.cpp index 39f7eb63c7..9c6b9da241 100644 --- a/libraries/render/src/render/Scene.cpp +++ b/libraries/render/src/render/Scene.cpp @@ -142,9 +142,9 @@ Scene::Scene() { _spatialTree; - Octree::CellPoint point{ Octree::Coord3{ 2, 4, 3 }, 3 }; + Octree::CellPoint point{ Octree::Coord3{ 2, 4, 3 }, 15}; auto path = Octree::CellPoint::rootTo(point); - + auto indices = _spatialTree.allocateCellPath(path); } ItemID Scene::allocateID() { diff --git a/libraries/render/src/render/Scene.h b/libraries/render/src/render/Scene.h index 8cbb5a43dd..2264ce860f 100644 --- a/libraries/render/src/render/Scene.h +++ b/libraries/render/src/render/Scene.h @@ -513,6 +513,9 @@ public: void processPendingChangesQueue(); + + Octree _spatialTree; + protected: // Thread safe elements that can be accessed from anywhere std::atomic _IDAllocator{ 1 }; // first valid itemID will be One @@ -529,8 +532,6 @@ protected: void removeItems(const ItemIDs& ids); void updateItems(const ItemIDs& ids, UpdateFunctors& functors); - Octree _spatialTree; - friend class Engine; }; From 1b1ffa494b2711e58b32c6db3221b805ebbe553b Mon Sep 17 00:00:00 2001 From: samcake Date: Tue, 26 Jan 2016 18:46:57 -0800 Subject: [PATCH 06/49] Making the octree more useful and cleaning up the interaction with the Scene --- .../render-utils/src/RenderDeferredTask.cpp | 8 +- .../render-utils/src/RenderDeferredTask.h | 8 +- .../render/src/render/DrawSceneOctree.cpp | 43 +- libraries/render/src/render/DrawStatus.cpp | 10 +- libraries/render/src/render/DrawStatus.h | 4 +- libraries/render/src/render/DrawTask.cpp | 58 +-- libraries/render/src/render/DrawTask.h | 24 +- libraries/render/src/render/Item.cpp | 73 +++ libraries/render/src/render/Item.h | 444 ++++++++++++++++++ libraries/render/src/render/Octree.cpp | 40 +- libraries/render/src/render/Octree.h | 160 ++++--- libraries/render/src/render/Scene.cpp | 66 +-- libraries/render/src/render/Scene.h | 444 +----------------- 13 files changed, 728 insertions(+), 654 deletions(-) create mode 100644 libraries/render/src/render/Item.cpp create mode 100644 libraries/render/src/render/Item.h diff --git a/libraries/render-utils/src/RenderDeferredTask.cpp b/libraries/render-utils/src/RenderDeferredTask.cpp index 659a0de40a..3ceef40d14 100755 --- a/libraries/render-utils/src/RenderDeferredTask.cpp +++ b/libraries/render-utils/src/RenderDeferredTask.cpp @@ -215,7 +215,7 @@ void RenderDeferredTask::run(const SceneContextPointer& sceneContext, const Rend } }; -void DrawOpaqueDeferred::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemIDsBounds& inItems) { +void DrawOpaqueDeferred::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemBounds& inItems) { assert(renderContext->getArgs()); assert(renderContext->getArgs()->_viewFrustum); @@ -241,7 +241,7 @@ void DrawOpaqueDeferred::run(const SceneContextPointer& sceneContext, const Rend }); } -void DrawTransparentDeferred::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemIDsBounds& inItems) { +void DrawTransparentDeferred::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemBounds& inItems) { assert(renderContext->getArgs()); assert(renderContext->getArgs()->_viewFrustum); @@ -294,7 +294,7 @@ void DrawOverlay3D::run(const SceneContextPointer& sceneContext, const RenderCon auto& items = scene->getMasterBucket().at(ItemFilter::Builder::opaqueShape().withLayered()); - ItemIDsBounds inItems; + ItemBounds inItems; inItems.reserve(items.size()); for (auto id : items) { auto& item = scene->getItem(id); @@ -399,7 +399,7 @@ void DrawBackgroundDeferred::run(const SceneContextPointer& sceneContext, const auto& items = scene->getMasterBucket().at(ItemFilter::Builder::background()); - ItemIDsBounds inItems; + ItemBounds inItems; inItems.reserve(items.size()); for (auto id : items) { inItems.emplace_back(id); diff --git a/libraries/render-utils/src/RenderDeferredTask.h b/libraries/render-utils/src/RenderDeferredTask.h index 78327006cd..43110d2720 100755 --- a/libraries/render-utils/src/RenderDeferredTask.h +++ b/libraries/render-utils/src/RenderDeferredTask.h @@ -52,9 +52,9 @@ public: class DrawOpaqueDeferred { public: DrawOpaqueDeferred(render::ShapePlumberPointer shapePlumber) : _shapePlumber{ shapePlumber } {} - void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext, const render::ItemIDsBounds& inItems); + void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext, const render::ItemBounds& inItems); - using JobModel = render::Task::Job::ModelI; + using JobModel = render::Task::Job::ModelI; protected: render::ShapePlumberPointer _shapePlumber; @@ -63,9 +63,9 @@ protected: class DrawTransparentDeferred { public: DrawTransparentDeferred(render::ShapePlumberPointer shapePlumber) : _shapePlumber{ shapePlumber } {} - void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext, const render::ItemIDsBounds& inItems); + void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext, const render::ItemBounds& inItems); - using JobModel = render::Task::Job::ModelI; + using JobModel = render::Task::Job::ModelI; protected: render::ShapePlumberPointer _shapePlumber; diff --git a/libraries/render/src/render/DrawSceneOctree.cpp b/libraries/render/src/render/DrawSceneOctree.cpp index c47560bd87..cff0218861 100644 --- a/libraries/render/src/render/DrawSceneOctree.cpp +++ b/libraries/render/src/render/DrawSceneOctree.cpp @@ -72,53 +72,14 @@ void DrawSceneOctree::run(const SceneContextPointer& sceneContext, _octreeInfo = std::make_shared();; }*/ - const auto& inCells = scene->_spatialTree._cells; + const auto& inCells = scene->getSpatialTree()._cells; _cells->resize(inCells.size() * sizeof(AABox)); AABox* cellAABox = reinterpret_cast (_cells->editData()); for (const auto& cell : inCells) { - (*cellAABox) = scene->_spatialTree.evalBound(cell.cellpos); + (*cellAABox) = scene->getSpatialTree().evalBound(cell.getlocation()); nbCells++; cellAABox++; } - - /* - _cells->resize((inItems.size() * sizeof(AABox))); - _itemStatus->resize((inItems.size() * NUM_STATUS_VEC4_PER_ITEM * sizeof(glm::vec4))); - AABox* itemAABox = reinterpret_cast (_itemBounds->editData()); - glm::ivec4* itemStatus = reinterpret_cast (_itemStatus->editData()); - for (auto& item : inItems) { - if (!item.bounds.isInvalid()) { - if (!item.bounds.isNull()) { - (*itemAABox) = item.bounds; - } else { - (*itemAABox).setBox(item.bounds.getCorner(), 0.1f); - } - auto& itemScene = scene->getItem(item.id); - - auto itemStatusPointer = itemScene.getStatus(); - if (itemStatusPointer) { - // Query the current status values, this is where the statusGetter lambda get called - auto&& currentStatusValues = itemStatusPointer->getCurrentValues(); - int valueNum = 0; - for (int vec4Num = 0; vec4Num < NUM_STATUS_VEC4_PER_ITEM; vec4Num++) { - (*itemStatus) = glm::ivec4(Item::Status::Value::INVALID.getPackedData()); - for (int component = 0; component < VEC4_LENGTH; component++) { - valueNum = vec4Num * VEC4_LENGTH + component; - if (valueNum < (int)currentStatusValues.size()) { - (*itemStatus)[component] = currentStatusValues[valueNum].getPackedData(); - } - } - itemStatus++; - } - } else { - (*itemStatus) = glm::ivec4(Item::Status::Value::INVALID.getPackedData()); - itemStatus++; - (*itemStatus) = glm::ivec4(Item::Status::Value::INVALID.getPackedData()); - itemStatus++; - } - - } - }*/ } if (nbCells == 0) { diff --git a/libraries/render/src/render/DrawStatus.cpp b/libraries/render/src/render/DrawStatus.cpp index 3f919b3d8c..a32cd39612 100644 --- a/libraries/render/src/render/DrawStatus.cpp +++ b/libraries/render/src/render/DrawStatus.cpp @@ -96,7 +96,7 @@ const gpu::TexturePointer DrawStatus::getStatusIconMap() const { void DrawStatus::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, - const ItemIDsBounds& inItems) { + const ItemBounds& inItems) { assert(renderContext->getArgs()); assert(renderContext->getArgs()->_viewFrustum); RenderArgs* args = renderContext->getArgs(); @@ -119,11 +119,11 @@ void DrawStatus::run(const SceneContextPointer& sceneContext, AABox* itemAABox = reinterpret_cast (_itemBounds->editData()); glm::ivec4* itemStatus = reinterpret_cast (_itemStatus->editData()); for (auto& item : inItems) { - if (!item.bounds.isInvalid()) { - if (!item.bounds.isNull()) { - (*itemAABox) = item.bounds; + if (!item.bound.isInvalid()) { + if (!item.bound.isNull()) { + (*itemAABox) = item.bound; } else { - (*itemAABox).setBox(item.bounds.getCorner(), 0.1f); + (*itemAABox).setBox(item.bound.getCorner(), 0.1f); } auto& itemScene = scene->getItem(item.id); diff --git a/libraries/render/src/render/DrawStatus.h b/libraries/render/src/render/DrawStatus.h index c9cf16c0cb..ecbdb37dbd 100644 --- a/libraries/render/src/render/DrawStatus.h +++ b/libraries/render/src/render/DrawStatus.h @@ -36,9 +36,9 @@ namespace render { DrawStatus() {} DrawStatus(const gpu::TexturePointer statusIconMap) { setStatusIconMap(statusIconMap); } - void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemIDsBounds& inItems); + void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemBounds& inItems); - using JobModel = Task::Job::ModelI; + using JobModel = Task::Job::ModelI; const gpu::PipelinePointer getDrawItemBoundsPipeline(); const gpu::PipelinePointer getDrawItemStatusPipeline(); diff --git a/libraries/render/src/render/DrawTask.cpp b/libraries/render/src/render/DrawTask.cpp index ed35519b9d..60cd9936cd 100755 --- a/libraries/render/src/render/DrawTask.cpp +++ b/libraries/render/src/render/DrawTask.cpp @@ -21,7 +21,7 @@ using namespace render; void render::cullItems(const RenderContextPointer& renderContext, const CullFunctor& cullFunctor, RenderDetails::Item& details, - const ItemIDsBounds& inItems, ItemIDsBounds& outItems) { + const ItemBounds& inItems, ItemBounds& outItems) { assert(renderContext->getArgs()); assert(renderContext->getArgs()->_viewFrustum); @@ -32,7 +32,7 @@ void render::cullItems(const RenderContextPointer& renderContext, const CullFunc // Culling / LOD for (auto item : inItems) { - if (item.bounds.isNull()) { + if (item.bound.isNull()) { outItems.emplace_back(item); // One more Item to render continue; } @@ -42,13 +42,13 @@ void render::cullItems(const RenderContextPointer& renderContext, const CullFunc bool outOfView; { PerformanceTimer perfTimer("boxInFrustum"); - outOfView = frustum->boxInFrustum(item.bounds) == ViewFrustum::OUTSIDE; + outOfView = frustum->boxInFrustum(item.bound) == ViewFrustum::OUTSIDE; } if (!outOfView) { bool bigEnoughToRender; { PerformanceTimer perfTimer("shouldRender"); - bigEnoughToRender = cullFunctor(args, item.bounds); + bigEnoughToRender = cullFunctor(args, item.bound); } if (bigEnoughToRender) { outItems.emplace_back(item); // One more Item to render @@ -62,30 +62,30 @@ void render::cullItems(const RenderContextPointer& renderContext, const CullFunc details._rendered += outItems.size(); } -struct ItemBound { +struct ItemBoundSort { float _centerDepth = 0.0f; float _nearDepth = 0.0f; float _farDepth = 0.0f; ItemID _id = 0; AABox _bounds; - ItemBound() {} - ItemBound(float centerDepth, float nearDepth, float farDepth, ItemID id, const AABox& bounds) : _centerDepth(centerDepth), _nearDepth(nearDepth), _farDepth(farDepth), _id(id), _bounds(bounds) {} + ItemBoundSort() {} + ItemBoundSort(float centerDepth, float nearDepth, float farDepth, ItemID id, const AABox& bounds) : _centerDepth(centerDepth), _nearDepth(nearDepth), _farDepth(farDepth), _id(id), _bounds(bounds) {} }; struct FrontToBackSort { - bool operator() (const ItemBound& left, const ItemBound& right) { + bool operator() (const ItemBoundSort& left, const ItemBoundSort& right) { return (left._centerDepth < right._centerDepth); } }; struct BackToFrontSort { - bool operator() (const ItemBound& left, const ItemBound& right) { + bool operator() (const ItemBoundSort& left, const ItemBoundSort& right) { return (left._centerDepth > right._centerDepth); } }; -void render::depthSortItems(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, bool frontToBack, const ItemIDsBounds& inItems, ItemIDsBounds& outItems) { +void render::depthSortItems(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, bool frontToBack, const ItemBounds& inItems, ItemBounds& outItems) { assert(renderContext->getArgs()); assert(renderContext->getArgs()->_viewFrustum); @@ -99,33 +99,33 @@ void render::depthSortItems(const SceneContextPointer& sceneContext, const Rende // Make a local dataset of the center distance and closest point distance - std::vector itemBounds; - itemBounds.reserve(outItems.size()); + std::vector itemBoundSorts; + itemBoundSorts.reserve(outItems.size()); for (auto itemDetails : inItems) { auto item = scene->getItem(itemDetails.id); - auto bound = itemDetails.bounds; // item.getBound(); + auto bound = itemDetails.bound; // item.getBound(); float distance = args->_viewFrustum->distanceToCamera(bound.calcCenter()); - itemBounds.emplace_back(ItemBound(distance, distance, distance, itemDetails.id, bound)); + itemBoundSorts.emplace_back(ItemBoundSort(distance, distance, distance, itemDetails.id, bound)); } // sort against Z if (frontToBack) { FrontToBackSort frontToBackSort; - std::sort (itemBounds.begin(), itemBounds.end(), frontToBackSort); + std::sort(itemBoundSorts.begin(), itemBoundSorts.end(), frontToBackSort); } else { BackToFrontSort backToFrontSort; - std::sort (itemBounds.begin(), itemBounds.end(), backToFrontSort); + std::sort(itemBoundSorts.begin(), itemBoundSorts.end(), backToFrontSort); } // FInally once sorted result to a list of itemID - for (auto& itemBound : itemBounds) { - outItems.emplace_back(ItemIDAndBounds(itemBound._id, itemBound._bounds)); + for (auto& item : itemBoundSorts) { + outItems.emplace_back(ItemBound(item._id, item._bounds)); } } -void render::renderItems(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemIDsBounds& inItems) { +void render::renderItems(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemBounds& inItems) { auto& scene = sceneContext->_scene; RenderArgs* args = renderContext->getArgs(); @@ -151,7 +151,7 @@ void renderShape(RenderArgs* args, const ShapePlumberPointer& shapeContext, cons } void render::renderShapes(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, - const ShapePlumberPointer& shapeContext, const ItemIDsBounds& inItems, int maxDrawnItems) { + const ShapePlumberPointer& shapeContext, const ItemBounds& inItems, int maxDrawnItems) { auto& scene = sceneContext->_scene; RenderArgs* args = renderContext->getArgs(); @@ -162,7 +162,7 @@ void render::renderShapes(const SceneContextPointer& sceneContext, const RenderC } } -void FetchItems::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, ItemIDsBounds& outItems) { +void FetchItems::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, ItemBounds& outItems) { auto& scene = sceneContext->_scene; outItems.clear(); @@ -173,7 +173,7 @@ void FetchItems::run(const SceneContextPointer& sceneContext, const RenderContex outItems.reserve(items->second.size()); for (auto& id : items->second) { auto& item = scene->getItem(id); - outItems.emplace_back(ItemIDAndBounds(id, item.getBound())); + outItems.emplace_back(ItemBound(id, item.getBound())); } } @@ -182,7 +182,7 @@ void FetchItems::run(const SceneContextPointer& sceneContext, const RenderContex } } -void DepthSortItems::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemIDsBounds& inItems, ItemIDsBounds& outItems) { +void DepthSortItems::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemBounds& inItems, ItemBounds& outItems) { depthSortItems(sceneContext, renderContext, _frontToBack, inItems, outItems); } @@ -194,17 +194,17 @@ void DrawLight::run(const SceneContextPointer& sceneContext, const RenderContext auto& scene = sceneContext->_scene; auto& items = scene->getMasterBucket().at(ItemFilter::Builder::light()); - ItemIDsBounds inItems; + ItemBounds inItems; inItems.reserve(items.size()); for (auto id : items) { auto item = scene->getItem(id); - inItems.emplace_back(ItemIDAndBounds(id, item.getBound())); + inItems.emplace_back(ItemBound(id, item.getBound())); } RenderArgs* args = renderContext->getArgs(); auto& details = args->_details.edit(RenderDetails::OTHER_ITEM); - ItemIDsBounds culledItems; + ItemBounds culledItems; culledItems.reserve(inItems.size()); cullItems(renderContext, _cullFunctor, details, inItems, culledItems); @@ -215,7 +215,7 @@ void DrawLight::run(const SceneContextPointer& sceneContext, const RenderContext }); } -void PipelineSortShapes::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemIDsBounds& inItems, ShapesIDsBounds& outShapes) { +void PipelineSortShapes::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemBounds& inItems, ShapesIDsBounds& outShapes) { auto& scene = sceneContext->_scene; outShapes.clear(); @@ -223,7 +223,7 @@ void PipelineSortShapes::run(const SceneContextPointer& sceneContext, const Rend auto key = scene->getItem(item.id).getShapeKey(); auto outItems = outShapes.find(key); if (outItems == outShapes.end()) { - outItems = outShapes.insert(std::make_pair(key, ItemIDsBounds{})).first; + outItems = outShapes.insert(std::make_pair(key, ItemBounds{})).first; outItems->second.reserve(inItems.size()); } @@ -243,7 +243,7 @@ void DepthSortShapes::run(const SceneContextPointer& sceneContext, const RenderC auto& inItems = pipeline.second; auto outItems = outShapes.find(pipeline.first); if (outItems == outShapes.end()) { - outItems = outShapes.insert(std::make_pair(pipeline.first, ItemIDsBounds{})).first; + outItems = outShapes.insert(std::make_pair(pipeline.first, ItemBounds{})).first; } depthSortItems(sceneContext, renderContext, _frontToBack, inItems, outItems->second); diff --git a/libraries/render/src/render/DrawTask.h b/libraries/render/src/render/DrawTask.h index 684d8bf4ea..574a1a112e 100755 --- a/libraries/render/src/render/DrawTask.h +++ b/libraries/render/src/render/DrawTask.h @@ -22,10 +22,10 @@ namespace render { using CullFunctor = std::function; void cullItems(const RenderContextPointer& renderContext, const CullFunctor& cullFunctor, RenderDetails::Item& details, - const ItemIDsBounds& inItems, ItemIDsBounds& outItems); -void depthSortItems(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, bool frontToBack, const ItemIDsBounds& inItems, ItemIDsBounds& outItems); -void renderItems(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemIDsBounds& inItems); -void renderShapes(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ShapePlumberPointer& shapeContext, const ItemIDsBounds& inItems, int maxDrawnItems = -1); + const ItemBounds& inItems, ItemBounds& outItems); +void depthSortItems(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, bool frontToBack, const ItemBounds& inItems, ItemBounds& outItems); +void renderItems(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemBounds& inItems); +void renderShapes(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ShapePlumberPointer& shapeContext, const ItemBounds& inItems, int maxDrawnItems = -1); class FetchItems { public: @@ -37,8 +37,8 @@ public: ItemFilter _filter = ItemFilter::Builder::opaqueShape().withoutLayered(); ProbeNumItems _probeNumItems; - void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, ItemIDsBounds& outItems); - using JobModel = Task::Job::ModelO; + void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, ItemBounds& outItems); + using JobModel = Task::Job::ModelO; }; template @@ -46,7 +46,7 @@ class CullItems { public: CullItems(CullFunctor cullFunctor) : _cullFunctor{ cullFunctor } {} - void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemIDsBounds& inItems, ItemIDsBounds& outItems) { + void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemBounds& inItems, ItemBounds& outItems) { const auto& args = renderContext->getArgs(); auto& details = args->_details.edit(T); outItems.clear(); @@ -54,7 +54,7 @@ public: render::cullItems(renderContext, _cullFunctor, details, inItems, outItems); } - using JobModel = Task::Job::ModelIO, ItemIDsBounds, ItemIDsBounds>; + using JobModel = Task::Job::ModelIO, ItemBounds, ItemBounds>; protected: CullFunctor _cullFunctor; @@ -65,8 +65,8 @@ public: bool _frontToBack; DepthSortItems(bool frontToBack = true) : _frontToBack(frontToBack) {} - void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemIDsBounds& inItems, ItemIDsBounds& outItems); - using JobModel = Task::Job::ModelIO; + void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemBounds& inItems, ItemBounds& outItems); + using JobModel = Task::Job::ModelIO; }; class DrawLight { @@ -81,8 +81,8 @@ protected: class PipelineSortShapes { public: - void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemIDsBounds& inItems, ShapesIDsBounds& outShapes); - using JobModel = Task::Job::ModelIO; + void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemBounds& inItems, ShapesIDsBounds& outShapes); + using JobModel = Task::Job::ModelIO; }; class DepthSortShapes { diff --git a/libraries/render/src/render/Item.cpp b/libraries/render/src/render/Item.cpp new file mode 100644 index 0000000000..e5356c2b0c --- /dev/null +++ b/libraries/render/src/render/Item.cpp @@ -0,0 +1,73 @@ +// +// Item.cpp +// render/src/render +// +// Created by Sam Gateau on 1/26/16. +// 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 "Item.h" + +#include +#include "gpu/Batch.h" + +using namespace render; + +const Item::Status::Value Item::Status::Value::INVALID = Item::Status::Value(); + +const float Item::Status::Value::RED = 0.0f; +const float Item::Status::Value::YELLOW = 60.0f; +const float Item::Status::Value::GREEN = 120.0f; +const float Item::Status::Value::CYAN = 180.0f; +const float Item::Status::Value::BLUE = 240.0f; +const float Item::Status::Value::MAGENTA = 300.0f; + +void Item::Status::Value::setScale(float scale) { + _scale = (std::numeric_limits::max() -1) * 0.5f * (1.0f + std::max(std::min(scale, 1.0f), 0.0f)); + } + +void Item::Status::Value::setColor(float hue) { + // Convert the HUe from range [0, 360] to signed normalized value + const float HUE_MAX = 360.0f; + _color = (std::numeric_limits::max()) * (std::max(std::min(hue, HUE_MAX), 0.0f) / HUE_MAX); +} +void Item::Status::Value::setIcon(unsigned char icon) { + _icon = icon; +} + +Item::Status::Values Item::Status::getCurrentValues() const { + Values currentValues(_values.size()); + auto currentValue = currentValues.begin(); + for (auto& getter : _values) { + (*currentValue) = getter(); + currentValue++; + } + return currentValues; +} + +void Item::PayloadInterface::addStatusGetter(const Status::Getter& getter) { + if (!_status) { + _status = std::make_shared(); + } + _status->addGetter(getter); +} + +void Item::PayloadInterface::addStatusGetters(const Status::Getters& getters) { + if (!_status) { + _status = std::make_shared(); + } + for (auto& g : getters) { + _status->addGetter(g); + } +} + +void Item::resetPayload(const PayloadPointer& payload) { + if (!payload) { + kill(); + } else { + _payload = payload; + _key = _payload->getKey(); + } +} diff --git a/libraries/render/src/render/Item.h b/libraries/render/src/render/Item.h new file mode 100644 index 0000000000..568b052623 --- /dev/null +++ b/libraries/render/src/render/Item.h @@ -0,0 +1,444 @@ +// +// Item.h +// render/src/render +// +// Created by Sam Gateau on 1/26/16. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_render_Item_h +#define hifi_render_Item_h + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "model/Material.h" +#include "ShapePipeline.h" + + +namespace render { + +class Context; + +// Key is the KEY to filter Items and create specialized lists +class ItemKey { +public: + enum FlagBit { + TYPE_SHAPE = 0, // Item is a Shape + TYPE_LIGHT, // Item is a Light + 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 + DEFORMED, // Deformed within bound, not solid + INVISIBLE, // Visible or not? could be just here to cast shadow + SHADOW_CASTER, // Item cast shadows + PICKABLE, // Item can be picked/selected + LAYERED, // Item belongs to one of the layers different from the default layer + + NUM_FLAGS, // Not a valid flag + }; + typedef std::bitset Flags; + + // The key is the Flags + Flags _flags; + + ItemKey() : _flags(0) {} + ItemKey(const Flags& flags) : _flags(flags) {} + + class Builder { + friend class ItemKey; + Flags _flags{ 0 }; + public: + Builder() {} + + ItemKey build() const { return ItemKey(_flags); } + + Builder& withTypeShape() { _flags.set(TYPE_SHAPE); return (*this); } + Builder& withTypeLight() { _flags.set(TYPE_LIGHT); return (*this); } + Builder& withTransparent() { _flags.set(TRANSLUCENT); return (*this); } + Builder& withViewSpace() { _flags.set(VIEW_SPACE); return (*this); } + Builder& withDynamic() { _flags.set(DYNAMIC); return (*this); } + Builder& withDeformed() { _flags.set(DEFORMED); return (*this); } + Builder& withInvisible() { _flags.set(INVISIBLE); return (*this); } + Builder& withShadowCaster() { _flags.set(SHADOW_CASTER); return (*this); } + Builder& withPickable() { _flags.set(PICKABLE); return (*this); } + Builder& withLayered() { _flags.set(LAYERED); return (*this); } + + // Convenient standard keys that we will keep on using all over the place + static Builder opaqueShape() { return Builder().withTypeShape(); } + static Builder transparentShape() { return Builder().withTypeShape().withTransparent(); } + static Builder light() { return Builder().withTypeLight(); } + static Builder background() { return Builder().withViewSpace().withLayered(); } + }; + ItemKey(const Builder& builder) : ItemKey(builder._flags) {} + + bool isShape() const { return _flags[TYPE_SHAPE]; } + bool isLight() const { return _flags[TYPE_LIGHT]; } + + bool isOpaque() const { return !_flags[TRANSLUCENT]; } + bool isTransparent() const { return _flags[TRANSLUCENT]; } + + bool isWorldSpace() const { return !_flags[VIEW_SPACE]; } + bool isViewSpace() const { return _flags[VIEW_SPACE]; } + + bool isStatic() const { return !_flags[DYNAMIC]; } + bool isDynamic() const { return _flags[DYNAMIC]; } + + bool isRigid() const { return !_flags[DEFORMED]; } + bool isDeformed() const { return _flags[DEFORMED]; } + + bool isVisible() const { return !_flags[INVISIBLE]; } + bool isInvisible() const { return _flags[INVISIBLE]; } + + bool isShadowCaster() const { return _flags[SHADOW_CASTER]; } + + bool isPickable() const { return _flags[PICKABLE]; } + + bool isLayered() const { return _flags[LAYERED]; } +}; + +inline QDebug operator<<(QDebug debug, const ItemKey& itemKey) { + debug << "[ItemKey: isOpaque:" << itemKey.isOpaque() + << ", isStatic:" << itemKey.isStatic() + << ", isWorldSpace:" << itemKey.isWorldSpace() + << "]"; + return debug; +} + +class ItemFilter { +public: + ItemKey::Flags _value{ 0 }; + ItemKey::Flags _mask{ 0 }; + + + ItemFilter(const ItemKey::Flags& value = ItemKey::Flags(0), const ItemKey::Flags& mask = ItemKey::Flags(0)) : _value(value), _mask(mask) {} + + class Builder { + friend class ItemFilter; + ItemKey::Flags _value{ 0 }; + ItemKey::Flags _mask{ 0 }; + public: + Builder() {} + + ItemFilter build() const { return ItemFilter(_value, _mask); } + + 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& withOpaque() { _value.reset(ItemKey::TRANSLUCENT); _mask.set(ItemKey::TRANSLUCENT); return (*this); } + Builder& withTransparent() { _value.set(ItemKey::TRANSLUCENT); _mask.set(ItemKey::TRANSLUCENT); return (*this); } + + Builder& withWorldSpace() { _value.reset(ItemKey::VIEW_SPACE); _mask.set(ItemKey::VIEW_SPACE); return (*this); } + Builder& withViewSpace() { _value.set(ItemKey::VIEW_SPACE); _mask.set(ItemKey::VIEW_SPACE); return (*this); } + + Builder& withStatic() { _value.reset(ItemKey::DYNAMIC); _mask.set(ItemKey::DYNAMIC); return (*this); } + Builder& withDynamic() { _value.set(ItemKey::DYNAMIC); _mask.set(ItemKey::DYNAMIC); return (*this); } + + Builder& withRigid() { _value.reset(ItemKey::DEFORMED); _mask.set(ItemKey::DEFORMED); return (*this); } + Builder& withDeformed() { _value.set(ItemKey::DEFORMED); _mask.set(ItemKey::DEFORMED); return (*this); } + + Builder& withVisible() { _value.reset(ItemKey::INVISIBLE); _mask.set(ItemKey::INVISIBLE); return (*this); } + Builder& withInvisible() { _value.set(ItemKey::INVISIBLE); _mask.set(ItemKey::INVISIBLE); return (*this); } + + Builder& withNoShadowCaster() { _value.reset(ItemKey::SHADOW_CASTER); _mask.set(ItemKey::SHADOW_CASTER); return (*this); } + Builder& withShadowCaster() { _value.set(ItemKey::SHADOW_CASTER); _mask.set(ItemKey::SHADOW_CASTER); return (*this); } + + Builder& withPickable() { _value.set(ItemKey::PICKABLE); _mask.set(ItemKey::PICKABLE); return (*this); } + + Builder& withoutLayered() { _value.reset(ItemKey::LAYERED); _mask.set(ItemKey::LAYERED); return (*this); } + Builder& withLayered() { _value.set(ItemKey::LAYERED); _mask.set(ItemKey::LAYERED); return (*this); } + + // Convenient standard keys that we will keep on using all over the place + static Builder opaqueShape() { return Builder().withTypeShape().withOpaque().withWorldSpace(); } + static Builder transparentShape() { return Builder().withTypeShape().withTransparent().withWorldSpace(); } + static Builder light() { return Builder().withTypeLight(); } + static Builder background() { return Builder().withViewSpace().withLayered(); } + }; + + ItemFilter(const Builder& builder) : ItemFilter(builder._value, builder._mask) {} + + // Item Filter operator testing if a key pass the filter + bool test(const ItemKey& key) const { return (key._flags & _mask) == (_value & _mask); } + + class Less { + public: + bool operator() (const ItemFilter& left, const ItemFilter& right) const { + if (left._value.to_ulong() == right._value.to_ulong()) { + return left._mask.to_ulong() < right._mask.to_ulong(); + } else { + return left._value.to_ulong() < right._value.to_ulong(); + } + } + }; +}; + +inline QDebug operator<<(QDebug debug, const ItemFilter& me) { + debug << "[ItemFilter: opaqueShape:" << me.test(ItemKey::Builder::opaqueShape().build()) + << "]"; + return debug; +} + + +class Item { +public: + typedef std::vector Vector; + typedef unsigned int ID; + + static const ID INVALID_ITEM_ID = 0; + + // Bound is the AABBox fully containing this item + typedef AABox Bound; + + // Status records the life history and performances of this item while performing at rendering and updating. + // This is Used for monitoring and dynamically adjust the quality + class Status { + public: + + // Status::Value class is the data used to represent the transient information of a status as a square icon + // The "icon" is a square displayed in the 3D scene over the render::Item AABB center. + // It can be scaled in the range [0, 1] and the color hue in the range [0, 360] representing the color wheel hue + class Value { + unsigned short _scale = 0xFFFF; + unsigned char _color = 0xFF; + unsigned char _icon = 0xFF; + public: + const static Value INVALID; // Invalid value meanss the status won't show + + Value() {} + Value(float scale, float hue, unsigned char icon = 0xFF) { setScale(scale); setColor(hue); setIcon(icon); } + + // It can be scaled in the range [0, 1] + void setScale(float scale); + // the color hue in the range [0, 360] representing the color wheel hue + void setColor(float hue); + // the icon to display in the range [0, 255], where 0 means no icon, just filled quad and anything else would + // hopefully have an icon available to display (see DrawStatusJob) + void setIcon(unsigned char icon); + + // Standard color Hue + static const float RED; // 0.0f; + static const float YELLOW; // 60.0f; + static const float GREEN; // 120.0f; + static const float CYAN; // 180.0f; + static const float BLUE; // 240.0f; + static const float MAGENTA; // 300.0f; + + // Retreive the Value data tightely packed as an int + int getPackedData() const { return *((const int*) this); } + }; + + typedef std::function Getter; + typedef std::vector Getters; + + Getters _values; + + void addGetter(const Getter& getter) { _values.push_back(getter); } + + size_t getNumValues() const { return _values.size(); } + + using Values = std::vector ; + Values getCurrentValues() const; + }; + typedef std::shared_ptr StatusPointer; + + // Update Functor + class UpdateFunctorInterface { + public: + virtual ~UpdateFunctorInterface() {} + }; + typedef std::shared_ptr UpdateFunctorPointer; + + // Payload is whatever is in this Item and implement the Payload Interface + class PayloadInterface { + public: + virtual const ItemKey getKey() const = 0; + virtual const Bound getBound() const = 0; + virtual int getLayer() const = 0; + + virtual void render(RenderArgs* args) = 0; + + virtual const ShapeKey getShapeKey() const = 0; + + ~PayloadInterface() {} + + // Status interface is local to the base class + const StatusPointer& getStatus() const { return _status; } + void addStatusGetter(const Status::Getter& getter); + void addStatusGetters(const Status::Getters& getters); + + protected: + StatusPointer _status; + + friend class Item; + virtual void update(const UpdateFunctorPointer& functor) = 0; + }; + typedef std::shared_ptr PayloadPointer; + + Item() {} + ~Item() {} + + // Main scene / item managment interface Reset/Update/Kill + void resetPayload(const PayloadPointer& payload); + void update(const UpdateFunctorPointer& updateFunctor) { _payload->update(updateFunctor); } // Communicate update to the payload + void kill() { _payload.reset(); _key._flags.reset(); } // Kill means forget the payload and key + + // Check heuristic key + const ItemKey& getKey() const { return _key; } + + // Payload Interface + + // Get the bound of the item expressed in world space (or eye space depending on the key.isWorldSpace()) + const Bound getBound() const { return _payload->getBound(); } + + // Get the layer where the item belongs. 0 by default meaning NOT LAYERED + int getLayer() const { return _payload->getLayer(); } + + // Render call for the item + void render(RenderArgs* args) const { _payload->render(args); } + + // Shape Type Interface + const ShapeKey getShapeKey() const { return _payload->getShapeKey(); } + + // Access the status + const StatusPointer& getStatus() const { return _payload->getStatus(); } + +protected: + PayloadPointer _payload; + ItemKey _key; + + friend class Scene; +}; + + +typedef Item::UpdateFunctorInterface UpdateFunctorInterface; +typedef Item::UpdateFunctorPointer UpdateFunctorPointer; +typedef std::vector UpdateFunctors; + +template class UpdateFunctor : public Item::UpdateFunctorInterface { +public: + typedef std::function Func; + Func _func; + + UpdateFunctor(Func func): _func(func) {} + ~UpdateFunctor() {} +}; + + +inline QDebug operator<<(QDebug debug, const Item& item) { + debug << "[Item: _key:" << item.getKey() << ", bounds:" << item.getBound() << "]"; + 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 +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; } +template void payloadRender(const std::shared_ptr& payloadData, RenderArgs* args) { } + +// Shape type interface +// This allows shapes to characterize their pipeline via a ShapeKey, to be picked with a subclass of Shape. +// When creating a new shape payload you need to create a specialized version, or the ShapeKey will be ownPipeline, +// 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(); } + +template class Payload : public Item::PayloadInterface { +public: + typedef std::shared_ptr DataPointer; + typedef UpdateFunctor Updater; + + Payload(const DataPointer& data) : _data(data) {} + + // Payload general interface + virtual const ItemKey getKey() const { return payloadGetKey(_data); } + virtual const Item::Bound getBound() const { return payloadGetBound(_data); } + virtual int getLayer() const { return payloadGetLayer(_data); } + + + virtual void render(RenderArgs* args) { payloadRender(_data, args); } + + // Shape Type interface + virtual const ShapeKey getShapeKey() const { return shapeGetShapeKey(_data); } + +protected: + DataPointer _data; + + // Update mechanics + virtual void update(const UpdateFunctorPointer& functor) { std::static_pointer_cast(functor)->_func((*_data)); } + friend class Item; +}; + +// Let's show how to make a simple FooPayload example: +/* +class Foo { +public: + mutable ItemKey _myownKey; + void makeMywnKey() const { + _myownKey = ItemKey::Builder().withTypeShape().build(); + } + + const Item::Bound evaluateMyBound() { + // Do stuff here to get your final Bound + return Item::Bound(); + } +}; + +typedef Payload FooPayload; +typedef std::shared_ptr FooPointer; + +// In a Source file, not a header, implement the Payload interface function specialized for Foo: +template <> const ItemKey payloadGetKey(const FooPointer& foo) { + // Foo's way of provinding its Key + foo->makeMyKey(); + return foo->_myownKey; +} +template <> const Item::Bound payloadGetBound(const FooPointer& foo) { + // evaluate Foo's own bound + return foo->evaluateMyBound(); +} + +// In this example, do not specialize the payloadRender call which means the compiler will use the default version which does nothing + +*/ +// End of the example + +typedef Item::PayloadPointer PayloadPointer; +typedef std::vector< PayloadPointer > Payloads; + +// A few typedefs for standard containers of ItemIDs +using ItemID = Item::ID; +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 ShapesIDsBounds = std::unordered_map; + +} + +#endif // hifi_render_Item_h diff --git a/libraries/render/src/render/Octree.cpp b/libraries/render/src/render/Octree.cpp index c693e3e7a3..c530def0ba 100644 --- a/libraries/render/src/render/Octree.cpp +++ b/libraries/render/src/render/Octree.cpp @@ -13,26 +13,54 @@ using namespace render; + +const double Octree::INV_DEPTH_DIM[] = { + 1.0, + 1.0 / 2.0, + 1.0 / 4.0, + 1.0 / 8.0, + 1.0 / 16.0, + 1.0 / 32.0, + 1.0 / 64.0, + 1.0 / 128.0, + 1.0 / 256.0, + 1.0 / 512.0, + 1.0 / 1024.0, + 1.0 / 2048.0, + 1.0 / 4096.0, + 1.0 / 8192.0, + 1.0 / 16384.0, + 1.0 / 32768.0 }; + Octree::Indices Octree::allocateCellPath(const CellPath& path) { Indices cellPath; Index currentIndex = 0; Cell* currentCell = _cells.data(); - int d = 0; cellPath.push_back(currentIndex); - for (; d <= path.back().depth; d++) { - auto& cellPoint = path[d]; + for (int d = 0; d <= path.back().depth; d++) { + auto& location = path[d]; - auto currentIndex = currentCell->child(cellPoint.octant()); + auto currentIndex = currentCell->child(location.octant()); if (currentIndex == INVALID) { currentIndex = _cells.size(); - currentCell->links[cellPoint.octant()] = currentIndex; - _cells.push_back(Cell(cellPath.back(), cellPoint)); + currentCell->setChild(location.octant(), currentIndex); + _cells.push_back(Cell(cellPath.back(), location)); } cellPath.push_back(currentIndex); currentCell = _cells.data() + currentIndex; } return cellPath; +} + + +void ItemSpatialTree::insert(const ItemBounds& items) { + + +} + +void ItemSpatialTree::erase(const ItemBounds& items) { + } \ No newline at end of file diff --git a/libraries/render/src/render/Octree.h b/libraries/render/src/render/Octree.h index 674ef8080d..627228ae8a 100644 --- a/libraries/render/src/render/Octree.h +++ b/libraries/render/src/render/Octree.h @@ -18,12 +18,10 @@ #include #include #include - - #include namespace render { - + class Octree { public: @@ -55,7 +53,7 @@ namespace render { using Octant = Link; - // Depth, Width, Volume + // Depth, Dim, Volume // {0, 1, 1} // {1, 2, 8} // {2, 4, 64} @@ -73,21 +71,30 @@ namespace render { // {14, 16384, 4398046511104} // {15, 32768, 35184372088832} // {16, 65536, 281474976710656} - - using Depth = int8_t; // Max depth is 15 => 32Km root down to 1m cells - using Coord = int16_t;// Need 16bits integer coordinates on each axes: 32768 cell positions + + // Max depth is 15 => 32Km root down to 1m cells + using Depth = int8_t; + static const Depth MAX_DEPTH { 15 }; + static const double INV_DEPTH_DIM[Octree::MAX_DEPTH + 1]; + + static int getDepthDimension(Depth depth) { return 2 << depth; } + static double getInvDepthDimension(Depth depth) { return INV_DEPTH_DIM[depth]; } + + + // Need 16bits integer coordinates on each axes: 32768 cell positions + using Coord = int16_t; using Coord3 = glm::i16vec3; using Coord4 = glm::i16vec4; - - class CellPoint { + + class Location { void assertValid() { assert((pos.x >= 0) && (pos.y >= 0) && (pos.z >= 0)); assert((pos.x < (2 << depth)) && (pos.y < (2 << depth)) && (pos.z < (2 << depth))); } public: - CellPoint() {} - CellPoint(const Coord3& xyz, Depth d) : pos(xyz), depth(d) { assertValid(); } - CellPoint(Depth d) : pos(0), depth(d) { assertValid(); } + Location() {} + Location(const Coord3& xyz, Depth d) : pos(xyz), depth(d) { assertValid(); } + Location(Depth d) : pos(0), depth(d) { assertValid(); } Coord3 pos{ 0 }; uint8_t spare{ 0 }; @@ -97,17 +104,17 @@ namespace render { // Eval the octant of this cell relative to its parent Octant octant() const { return Octant((pos.x & XAxis) | (pos.y & YAxis) | (pos.z & ZAxis)); } - // Get the Parent cell pos of this cell - CellPoint parent() const { - return CellPoint{ pos >> Coord3(1), Depth(depth <= 0 ? 0 : depth - 1) }; + // Get the Parent cell Location of this cell + Location parent() const { + return Location{ pos >> Coord3(1), Depth(depth <= 0 ? 0 : depth - 1) }; } - CellPoint child(Link octant) const { - return CellPoint{ pos << Coord3(1) | Coord3((Coord)bool(octant & XAxis), (Coord)bool(octant & YAxis), (Coord)bool(octant & ZAxis)), Depth(depth + 1) }; + Location child(Link octant) const { + return Location{ pos << Coord3(1) | Coord3((Coord)bool(octant & XAxis), (Coord)bool(octant & YAxis), (Coord)bool(octant & ZAxis)), Depth(depth + 1) }; } - using vector = std::vector< CellPoint >; - static vector rootTo(const CellPoint& dest) { - CellPoint current{ dest }; + using vector = std::vector< Location >; + static vector rootTo(const Location& dest) { + Location current{ dest }; vector path(dest.depth + 1); path[dest.depth] = dest; while (current.depth > 0) { @@ -117,9 +124,9 @@ namespace render { return path; } }; - using CellPath = CellPoint::vector; + using CellPath = Location::vector; + - class Range { public: Coord3 _min; @@ -129,66 +136,113 @@ namespace render { // Cell or Brick Indexing using Index = int32_t; static const Index INVALID = -1; + static const Index ROOT = 0; using Indices = std::vector; // the cell description class Cell { public: + const Location& getlocation() const { return _location; } - CellPoint cellpos; - - std::array links; - - Index parent() const { return links[Parent]; } + Index parent() const { return _links[Parent]; } bool asParent() const { return parent() != INVALID; } - Index child(Link octant) const { return links[octant]; } + Index child(Link octant) const { return _links[octant]; } bool asChild(Link octant) const { return child(octant) != INVALID; } + void setChild(Link octant, Index child) { _links[octant] = child; } + + Cell() : + _links({ { INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID } }) + {} + + Cell(Index parent, Location loc) : + _location(loc), + _links({ { INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, parent } }) + {} Index brick{ INVALID }; - Cell() : - links({ { INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID } }) - {} - - Cell(Index parent, CellPoint pos) : - cellpos(pos), - links({ { INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, parent } }) - {} + private: + Location _location; + std::array _links; }; using Cells = std::vector< Cell >; - + class Brick { public: - + }; using Bricks = std::vector< Brick >; - - - + + + // Octree members Cells _cells = Cells(1, Cell()); // start with only the Cell root Bricks _bricks; - - float _size = 320.0f; - + Octree() {}; // allocatePath Indices allocateCellPath(const CellPath& path); - AABox evalBound(const CellPoint& point) const { - - float width = (float) (_size / double(1 << point.depth)); - glm::vec3 corner = glm::vec3(-_size * 0.5f) + glm::vec3(point.pos) * width; - return AABox(corner, width); - + // reach to Cell and allocate the cell path to it if needed + Index editCell(const Location& loc) { + auto cells = allocateCellPath(Location::rootTo(loc)); + return cells.back(); } }; - - - +} +// CLose the namespace here before including the Item in the picture, maybe Octre could stay pure of it +#include "Item.h" + +namespace render { + + // An octree of Items organizing them efficiently for culling + // The octree only cares about the bound & the key of an item to store it a the right cell location + class ItemSpatialTree : public Octree { + float _size{ 32000.0f }; + double _invSize{ 1.0 / _size }; + glm::vec3 _origin{ -_size }; + public: + + + float getSize() const { _size; } + glm::vec3 getOrigin() const { _origin; } + + float getCellWidth(Depth depth) const { return (float) _size * getInvDepthDimension(depth); } + float getInvCellWidth(Depth depth) const { return (float) getDepthDimension(depth) * _invSize; } + + glm::vec3 evalPos(const Coord3& coord, Depth depth = Octree::MAX_DEPTH) const { + return getOrigin() + glm::vec3(coord) * getCellWidth(depth); + } + glm::vec3 evalPos(const Coord3& coord, float cellWidth) const { + return getOrigin() + glm::vec3(coord) * cellWidth; + } + + Coord3 evalCoord(const glm::vec3& pos, Depth depth = Octree::MAX_DEPTH) const { + return Coord3((pos - getOrigin()) * getInvCellWidth(depth)); + } + + AABox evalBound(const Location& loc) const { + float cellWidth = getCellWidth(loc.depth); + return AABox(evalPos(loc.pos, cellWidth), cellWidth); + } + + + Location evalLocation(const AABox& bound) const { + auto minPos = evalCoord(bound.getMinimumPoint()); + auto maxPos = evalCoord(bound.getMaximumPoint()); + auto range = maxPos - minPos; + //range + return Location(minPos, 4); + } + + ItemSpatialTree() {} + + void insert(const ItemBounds& items); + void erase(const ItemBounds& items); + }; } #endif // hifi_render_Octree_h diff --git a/libraries/render/src/render/Scene.cpp b/libraries/render/src/render/Scene.cpp index 9c6b9da241..eb8dd532e0 100644 --- a/libraries/render/src/render/Scene.cpp +++ b/libraries/render/src/render/Scene.cpp @@ -56,62 +56,6 @@ void ItemBucketMap::allocateStandardOpaqueTranparentBuckets() { (*this)[ItemFilter::Builder::transparentShape().withLayered()]; } -const Item::Status::Value Item::Status::Value::INVALID = Item::Status::Value(); - -const float Item::Status::Value::RED = 0.0f; -const float Item::Status::Value::YELLOW = 60.0f; -const float Item::Status::Value::GREEN = 120.0f; -const float Item::Status::Value::CYAN = 180.0f; -const float Item::Status::Value::BLUE = 240.0f; -const float Item::Status::Value::MAGENTA = 300.0f; - -void Item::Status::Value::setScale(float scale) { - _scale = (std::numeric_limits::max() -1) * 0.5f * (1.0f + std::max(std::min(scale, 1.0f), 0.0f)); - } - -void Item::Status::Value::setColor(float hue) { - // Convert the HUe from range [0, 360] to signed normalized value - const float HUE_MAX = 360.0f; - _color = (std::numeric_limits::max()) * (std::max(std::min(hue, HUE_MAX), 0.0f) / HUE_MAX); -} -void Item::Status::Value::setIcon(unsigned char icon) { - _icon = icon; -} - -Item::Status::Values Item::Status::getCurrentValues() const { - Values currentValues(_values.size()); - auto currentValue = currentValues.begin(); - for (auto& getter : _values) { - (*currentValue) = getter(); - currentValue++; - } - return currentValues; -} - -void Item::PayloadInterface::addStatusGetter(const Status::Getter& getter) { - if (!_status) { - _status = std::make_shared(); - } - _status->addGetter(getter); -} - -void Item::PayloadInterface::addStatusGetters(const Status::Getters& getters) { - if (!_status) { - _status = std::make_shared(); - } - for (auto& g : getters) { - _status->addGetter(g); - } -} - -void Item::resetPayload(const PayloadPointer& payload) { - if (!payload) { - kill(); - } else { - _payload = payload; - _key = _payload->getKey(); - } -} void PendingChanges::resetItem(ItemID id, const PayloadPointer& payload) { _resetItems.push_back(id); @@ -127,7 +71,6 @@ void PendingChanges::updateItem(ItemID id, const UpdateFunctorPointer& functor) _updateFunctors.push_back(functor); } - void PendingChanges::merge(PendingChanges& changes) { _resetItems.insert(_resetItems.end(), changes._resetItems.begin(), changes._resetItems.end()); _resetPayloads.insert(_resetPayloads.end(), changes._resetPayloads.begin(), changes._resetPayloads.end()); @@ -139,12 +82,6 @@ void PendingChanges::merge(PendingChanges& changes) { Scene::Scene() { _items.push_back(Item()); // add the itemID #0 to nothing _masterBucketMap.allocateStandardOpaqueTranparentBuckets(); - - - _spatialTree; - Octree::CellPoint point{ Octree::Coord3{ 2, 4, 3 }, 15}; - auto path = Octree::CellPoint::rootTo(point); - auto indices = _spatialTree.allocateCellPath(path); } ItemID Scene::allocateID() { @@ -200,8 +137,9 @@ void Scene::resetItems(const ItemIDs& ids, Payloads& payloads) { item.resetPayload(*resetPayload); _masterBucketMap.reset((*resetID), oldKey, item.getKey()); - } + + } } void Scene::removeItems(const ItemIDs& ids) { diff --git a/libraries/render/src/render/Scene.h b/libraries/render/src/render/Scene.h index 2264ce860f..d3d5a6865c 100644 --- a/libraries/render/src/render/Scene.h +++ b/libraries/render/src/render/Scene.h @@ -12,436 +12,12 @@ #ifndef hifi_render_Scene_h #define hifi_render_Scene_h -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include "model/Material.h" -#include "ShapePipeline.h" - +#include "Item.h" #include "Octree.h" namespace render { -class Context; - -// Key is the KEY to filter Items and create specialized lists -class ItemKey { -public: - enum FlagBit { - TYPE_SHAPE = 0, // Item is a Shape - TYPE_LIGHT, // Item is a Light - 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 - DEFORMED, // Deformed within bound, not solid - INVISIBLE, // Visible or not? could be just here to cast shadow - SHADOW_CASTER, // Item cast shadows - PICKABLE, // Item can be picked/selected - LAYERED, // Item belongs to one of the layers different from the default layer - - NUM_FLAGS, // Not a valid flag - }; - typedef std::bitset Flags; - - // The key is the Flags - Flags _flags; - - ItemKey() : _flags(0) {} - ItemKey(const Flags& flags) : _flags(flags) {} - - class Builder { - friend class ItemKey; - Flags _flags{ 0 }; - public: - Builder() {} - - ItemKey build() const { return ItemKey(_flags); } - - Builder& withTypeShape() { _flags.set(TYPE_SHAPE); return (*this); } - Builder& withTypeLight() { _flags.set(TYPE_LIGHT); return (*this); } - Builder& withTransparent() { _flags.set(TRANSLUCENT); return (*this); } - Builder& withViewSpace() { _flags.set(VIEW_SPACE); return (*this); } - Builder& withDynamic() { _flags.set(DYNAMIC); return (*this); } - Builder& withDeformed() { _flags.set(DEFORMED); return (*this); } - Builder& withInvisible() { _flags.set(INVISIBLE); return (*this); } - Builder& withShadowCaster() { _flags.set(SHADOW_CASTER); return (*this); } - Builder& withPickable() { _flags.set(PICKABLE); return (*this); } - Builder& withLayered() { _flags.set(LAYERED); return (*this); } - - // Convenient standard keys that we will keep on using all over the place - static Builder opaqueShape() { return Builder().withTypeShape(); } - static Builder transparentShape() { return Builder().withTypeShape().withTransparent(); } - static Builder light() { return Builder().withTypeLight(); } - static Builder background() { return Builder().withViewSpace().withLayered(); } - }; - ItemKey(const Builder& builder) : ItemKey(builder._flags) {} - - bool isShape() const { return _flags[TYPE_SHAPE]; } - bool isLight() const { return _flags[TYPE_LIGHT]; } - - bool isOpaque() const { return !_flags[TRANSLUCENT]; } - bool isTransparent() const { return _flags[TRANSLUCENT]; } - - bool isWorldSpace() const { return !_flags[VIEW_SPACE]; } - bool isViewSpace() const { return _flags[VIEW_SPACE]; } - - bool isStatic() const { return !_flags[DYNAMIC]; } - bool isDynamic() const { return _flags[DYNAMIC]; } - - bool isRigid() const { return !_flags[DEFORMED]; } - bool isDeformed() const { return _flags[DEFORMED]; } - - bool isVisible() const { return !_flags[INVISIBLE]; } - bool isInvisible() const { return _flags[INVISIBLE]; } - - bool isShadowCaster() const { return _flags[SHADOW_CASTER]; } - - bool isPickable() const { return _flags[PICKABLE]; } - - bool isLayered() const { return _flags[LAYERED]; } -}; - -inline QDebug operator<<(QDebug debug, const ItemKey& itemKey) { - debug << "[ItemKey: isOpaque:" << itemKey.isOpaque() - << ", isStatic:" << itemKey.isStatic() - << ", isWorldSpace:" << itemKey.isWorldSpace() - << "]"; - return debug; -} - -class ItemFilter { -public: - ItemKey::Flags _value{ 0 }; - ItemKey::Flags _mask{ 0 }; - - - ItemFilter(const ItemKey::Flags& value = ItemKey::Flags(0), const ItemKey::Flags& mask = ItemKey::Flags(0)) : _value(value), _mask(mask) {} - - class Builder { - friend class ItemFilter; - ItemKey::Flags _value{ 0 }; - ItemKey::Flags _mask{ 0 }; - public: - Builder() {} - - ItemFilter build() const { return ItemFilter(_value, _mask); } - - 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& withOpaque() { _value.reset(ItemKey::TRANSLUCENT); _mask.set(ItemKey::TRANSLUCENT); return (*this); } - Builder& withTransparent() { _value.set(ItemKey::TRANSLUCENT); _mask.set(ItemKey::TRANSLUCENT); return (*this); } - - Builder& withWorldSpace() { _value.reset(ItemKey::VIEW_SPACE); _mask.set(ItemKey::VIEW_SPACE); return (*this); } - Builder& withViewSpace() { _value.set(ItemKey::VIEW_SPACE); _mask.set(ItemKey::VIEW_SPACE); return (*this); } - - Builder& withStatic() { _value.reset(ItemKey::DYNAMIC); _mask.set(ItemKey::DYNAMIC); return (*this); } - Builder& withDynamic() { _value.set(ItemKey::DYNAMIC); _mask.set(ItemKey::DYNAMIC); return (*this); } - - Builder& withRigid() { _value.reset(ItemKey::DEFORMED); _mask.set(ItemKey::DEFORMED); return (*this); } - Builder& withDeformed() { _value.set(ItemKey::DEFORMED); _mask.set(ItemKey::DEFORMED); return (*this); } - - Builder& withVisible() { _value.reset(ItemKey::INVISIBLE); _mask.set(ItemKey::INVISIBLE); return (*this); } - Builder& withInvisible() { _value.set(ItemKey::INVISIBLE); _mask.set(ItemKey::INVISIBLE); return (*this); } - - Builder& withNoShadowCaster() { _value.reset(ItemKey::SHADOW_CASTER); _mask.set(ItemKey::SHADOW_CASTER); return (*this); } - Builder& withShadowCaster() { _value.set(ItemKey::SHADOW_CASTER); _mask.set(ItemKey::SHADOW_CASTER); return (*this); } - - Builder& withPickable() { _value.set(ItemKey::PICKABLE); _mask.set(ItemKey::PICKABLE); return (*this); } - - Builder& withoutLayered() { _value.reset(ItemKey::LAYERED); _mask.set(ItemKey::LAYERED); return (*this); } - Builder& withLayered() { _value.set(ItemKey::LAYERED); _mask.set(ItemKey::LAYERED); return (*this); } - - // Convenient standard keys that we will keep on using all over the place - static Builder opaqueShape() { return Builder().withTypeShape().withOpaque().withWorldSpace(); } - static Builder transparentShape() { return Builder().withTypeShape().withTransparent().withWorldSpace(); } - static Builder light() { return Builder().withTypeLight(); } - static Builder background() { return Builder().withViewSpace().withLayered(); } - }; - - ItemFilter(const Builder& builder) : ItemFilter(builder._value, builder._mask) {} - - // Item Filter operator testing if a key pass the filter - bool test(const ItemKey& key) const { return (key._flags & _mask) == (_value & _mask); } - - class Less { - public: - bool operator() (const ItemFilter& left, const ItemFilter& right) const { - if (left._value.to_ulong() == right._value.to_ulong()) { - return left._mask.to_ulong() < right._mask.to_ulong(); - } else { - return left._value.to_ulong() < right._value.to_ulong(); - } - } - }; -}; - -inline QDebug operator<<(QDebug debug, const ItemFilter& me) { - debug << "[ItemFilter: opaqueShape:" << me.test(ItemKey::Builder::opaqueShape().build()) - << "]"; - return debug; -} - - -class Item { -public: - typedef std::vector Vector; - typedef unsigned int ID; - - static const ID INVALID_ITEM_ID = 0; - - // Bound is the AABBox fully containing this item - typedef AABox Bound; - - // Status records the life history and performances of this item while performing at rendering and updating. - // This is Used for monitoring and dynamically adjust the quality - class Status { - public: - - // Status::Value class is the data used to represent the transient information of a status as a square icon - // The "icon" is a square displayed in the 3D scene over the render::Item AABB center. - // It can be scaled in the range [0, 1] and the color hue in the range [0, 360] representing the color wheel hue - class Value { - unsigned short _scale = 0xFFFF; - unsigned char _color = 0xFF; - unsigned char _icon = 0xFF; - public: - const static Value INVALID; // Invalid value meanss the status won't show - - Value() {} - Value(float scale, float hue, unsigned char icon = 0xFF) { setScale(scale); setColor(hue); setIcon(icon); } - - // It can be scaled in the range [0, 1] - void setScale(float scale); - // the color hue in the range [0, 360] representing the color wheel hue - void setColor(float hue); - // the icon to display in the range [0, 255], where 0 means no icon, just filled quad and anything else would - // hopefully have an icon available to display (see DrawStatusJob) - void setIcon(unsigned char icon); - - // Standard color Hue - static const float RED; // 0.0f; - static const float YELLOW; // 60.0f; - static const float GREEN; // 120.0f; - static const float CYAN; // 180.0f; - static const float BLUE; // 240.0f; - static const float MAGENTA; // 300.0f; - - // Retreive the Value data tightely packed as an int - int getPackedData() const { return *((const int*) this); } - }; - - typedef std::function Getter; - typedef std::vector Getters; - - Getters _values; - - void addGetter(const Getter& getter) { _values.push_back(getter); } - - size_t getNumValues() const { return _values.size(); } - - using Values = std::vector ; - Values getCurrentValues() const; - }; - typedef std::shared_ptr StatusPointer; - - // Update Functor - class UpdateFunctorInterface { - public: - virtual ~UpdateFunctorInterface() {} - }; - typedef std::shared_ptr UpdateFunctorPointer; - - // Payload is whatever is in this Item and implement the Payload Interface - class PayloadInterface { - public: - virtual const ItemKey getKey() const = 0; - virtual const Bound getBound() const = 0; - virtual int getLayer() const = 0; - - virtual void render(RenderArgs* args) = 0; - - virtual const ShapeKey getShapeKey() const = 0; - - ~PayloadInterface() {} - - // Status interface is local to the base class - const StatusPointer& getStatus() const { return _status; } - void addStatusGetter(const Status::Getter& getter); - void addStatusGetters(const Status::Getters& getters); - - protected: - StatusPointer _status; - - friend class Item; - virtual void update(const UpdateFunctorPointer& functor) = 0; - }; - typedef std::shared_ptr PayloadPointer; - - Item() {} - ~Item() {} - - // Main scene / item managment interface Reset/Update/Kill - void resetPayload(const PayloadPointer& payload); - void update(const UpdateFunctorPointer& updateFunctor) { _payload->update(updateFunctor); } // Communicate update to the payload - void kill() { _payload.reset(); _key._flags.reset(); } // Kill means forget the payload and key - - // Check heuristic key - const ItemKey& getKey() const { return _key; } - - // Payload Interface - - // Get the bound of the item expressed in world space (or eye space depending on the key.isWorldSpace()) - const Bound getBound() const { return _payload->getBound(); } - - // Get the layer where the item belongs. 0 by default meaning NOT LAYERED - int getLayer() const { return _payload->getLayer(); } - - // Render call for the item - void render(RenderArgs* args) const { _payload->render(args); } - - // Shape Type Interface - const ShapeKey getShapeKey() const { return _payload->getShapeKey(); } - - // Access the status - const StatusPointer& getStatus() const { return _payload->getStatus(); } - -protected: - PayloadPointer _payload; - ItemKey _key; - - friend class Scene; -}; - - -typedef Item::UpdateFunctorInterface UpdateFunctorInterface; -typedef Item::UpdateFunctorPointer UpdateFunctorPointer; -typedef std::vector UpdateFunctors; - -template class UpdateFunctor : public Item::UpdateFunctorInterface { -public: - typedef std::function Func; - Func _func; - - UpdateFunctor(Func func): _func(func) {} - ~UpdateFunctor() {} -}; - - -inline QDebug operator<<(QDebug debug, const Item& item) { - debug << "[Item: _key:" << item.getKey() << ", bounds:" << item.getBound() << "]"; - 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 -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; } -template void payloadRender(const std::shared_ptr& payloadData, RenderArgs* args) { } - -// Shape type interface -// This allows shapes to characterize their pipeline via a ShapeKey, to be picked with a subclass of Shape. -// When creating a new shape payload you need to create a specialized version, or the ShapeKey will be ownPipeline, -// 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(); } - -template class Payload : public Item::PayloadInterface { -public: - typedef std::shared_ptr DataPointer; - typedef UpdateFunctor Updater; - - Payload(const DataPointer& data) : _data(data) {} - - // Payload general interface - virtual const ItemKey getKey() const { return payloadGetKey(_data); } - virtual const Item::Bound getBound() const { return payloadGetBound(_data); } - virtual int getLayer() const { return payloadGetLayer(_data); } - - - virtual void render(RenderArgs* args) { payloadRender(_data, args); } - - // Shape Type interface - virtual const ShapeKey getShapeKey() const { return shapeGetShapeKey(_data); } - -protected: - DataPointer _data; - - // Update mechanics - virtual void update(const UpdateFunctorPointer& functor) { std::static_pointer_cast(functor)->_func((*_data)); } - friend class Item; -}; - -// Let's show how to make a simple FooPayload example: -/* -class Foo { -public: - mutable ItemKey _myownKey; - void makeMywnKey() const { - _myownKey = ItemKey::Builder().withTypeShape().build(); - } - - const Item::Bound evaluateMyBound() { - // Do stuff here to get your final Bound - return Item::Bound(); - } -}; - -typedef Payload FooPayload; -typedef std::shared_ptr FooPointer; - -// In a Source file, not a header, implement the Payload interface function specialized for Foo: -template <> const ItemKey payloadGetKey(const FooPointer& foo) { - // Foo's way of provinding its Key - foo->makeMyKey(); - return foo->_myownKey; -} -template <> const Item::Bound payloadGetBound(const FooPointer& foo) { - // evaluate Foo's own bound - return foo->evaluateMyBound(); -} - -// In this example, do not specialize the payloadRender call which means the compiler will use the default version which does nothing - -*/ -// End of the example - -typedef Item::PayloadPointer PayloadPointer; -typedef std::vector< PayloadPointer > Payloads; - -// A few typedefs for standard containers of ItemIDs -typedef Item::ID ItemID; -typedef std::vector ItemIDs; -typedef std::set ItemIDSet; - -class ItemIDAndBounds { -public: - ItemIDAndBounds(ItemID id) : id(id) { } - ItemIDAndBounds(ItemID id, const AABox& bounds) : id(id), bounds(bounds) { } - - ItemID id; - AABox bounds; -}; - -// A list of items to be passed between rendering jobs -using ItemIDsBounds = std::vector; - -// A map of items by ShapeKey to optimize rendering pipeline assignments -using ShapesIDsBounds = std::unordered_map; - - -// A map of ItemIDSets allowing to create bucket lists of items which are filtering correctly +// A map of ItemIDSets allowing to create bucket lists of items which are filtered according to their key class ItemBucketMap : public std::map { public: @@ -501,8 +77,8 @@ public: /// Enqueue change batch to the scene void enqueuePendingChanges(const PendingChanges& pendingChanges); - /// Access the main bucketmap of items - const ItemBucketMap& getMasterBucket() const { return _masterBucketMap; } + // Process the penging changes equeued + void processPendingChangesQueue(); /// Access a particular item form its ID /// WARNING, There is No check on the validity of the ID, so this could return a bad Item @@ -510,11 +86,11 @@ public: size_t getNumItems() const { return _items.size(); } + /// Access the main bucketmap of items + const ItemBucketMap& getMasterBucket() const { return _masterBucketMap; } - void processPendingChangesQueue(); - - - Octree _spatialTree; + /// Access the item spatial tree + const ItemSpatialTree& getSpatialTree() const { return _masterSpatialTree; } protected: // Thread safe elements that can be accessed from anywhere @@ -527,6 +103,8 @@ protected: std::mutex _itemsMutex; Item::Vector _items; ItemBucketMap _masterBucketMap; + ItemSpatialTree _masterSpatialTree; + void resetItems(const ItemIDs& ids, Payloads& payloads); void removeItems(const ItemIDs& ids); @@ -535,8 +113,6 @@ protected: friend class Engine; }; - - typedef std::shared_ptr ScenePointer; typedef std::vector Scenes; From b53f6b2e035c6a5f7c33bab98715d7c79ec880c3 Mon Sep 17 00:00:00 2001 From: samcake Date: Wed, 27 Jan 2016 10:03:31 -0800 Subject: [PATCH 07/49] late night octree traversal --- libraries/render/src/render/Octree.cpp | 7 +++-- libraries/render/src/render/Octree.h | 40 ++++++++++++++++++++------ libraries/render/src/render/Scene.cpp | 7 ++++- 3 files changed, 42 insertions(+), 12 deletions(-) diff --git a/libraries/render/src/render/Octree.cpp b/libraries/render/src/render/Octree.cpp index c530def0ba..bc141fb797 100644 --- a/libraries/render/src/render/Octree.cpp +++ b/libraries/render/src/render/Octree.cpp @@ -57,8 +57,11 @@ Octree::Indices Octree::allocateCellPath(const CellPath& path) { void ItemSpatialTree::insert(const ItemBounds& items) { - - + for (auto& item : items) { + if (!item.bound.isNull()) { + editCell(evalLocation(item.bound)); + } + } } void ItemSpatialTree::erase(const ItemBounds& items) { diff --git a/libraries/render/src/render/Octree.h b/libraries/render/src/render/Octree.h index 627228ae8a..b16b0b1ded 100644 --- a/libraries/render/src/render/Octree.h +++ b/libraries/render/src/render/Octree.h @@ -203,12 +203,19 @@ namespace render { class ItemSpatialTree : public Octree { float _size{ 32000.0f }; double _invSize{ 1.0 / _size }; - glm::vec3 _origin{ -_size }; + glm::vec3 _origin{ -0.5f * _size }; public: + Depth coordToDepth(Coord length) const { + Depth d = MAX_DEPTH; + while(length) { + length >>= 1; + d--; + } + return d; + } - - float getSize() const { _size; } - glm::vec3 getOrigin() const { _origin; } + float getSize() const { return _size; } + const glm::vec3& getOrigin() const { return _origin; } float getCellWidth(Depth depth) const { return (float) _size * getInvDepthDimension(depth); } float getInvCellWidth(Depth depth) const { return (float) getDepthDimension(depth) * _invSize; } @@ -224,6 +231,7 @@ namespace render { return Coord3((pos - getOrigin()) * getInvCellWidth(depth)); } + AABox evalBound(const Location& loc) const { float cellWidth = getCellWidth(loc.depth); return AABox(evalPos(loc.pos, cellWidth), cellWidth); @@ -231,11 +239,25 @@ namespace render { Location evalLocation(const AABox& bound) const { - auto minPos = evalCoord(bound.getMinimumPoint()); - auto maxPos = evalCoord(bound.getMaximumPoint()); - auto range = maxPos - minPos; - //range - return Location(minPos, 4); + auto minVec = bound.getMinimumPoint(); + auto maxVec = bound.getMaximumPoint(); + + Depth depth = MAX_DEPTH; + auto minPos = evalCoord(minVec); + auto maxPos = evalCoord(maxVec); + + while ((depth > 0) && + !((maxPos.x == minPos.x) && (maxPos.y == minPos.y) && (maxPos.z == minPos.z))) { + depth--; + minPos >>= 1; + maxPos >>= 1; + } + + if (depth == 0) { + return Location(); + } else { + return Location(minPos, depth); + } } ItemSpatialTree() {} diff --git a/libraries/render/src/render/Scene.cpp b/libraries/render/src/render/Scene.cpp index eb8dd532e0..1458e6fcf8 100644 --- a/libraries/render/src/render/Scene.cpp +++ b/libraries/render/src/render/Scene.cpp @@ -131,6 +131,9 @@ void Scene::processPendingChangesQueue() { void Scene::resetItems(const ItemIDs& ids, Payloads& payloads) { auto resetID = ids.begin(); auto resetPayload = payloads.begin(); + ItemBounds itemBounds; + itemBounds.reserve(ids.size()); + for (;resetID != ids.end(); resetID++, resetPayload++) { auto& item = _items[(*resetID)]; auto oldKey = item.getKey(); @@ -138,8 +141,10 @@ void Scene::resetItems(const ItemIDs& ids, Payloads& payloads) { _masterBucketMap.reset((*resetID), oldKey, item.getKey()); - + itemBounds.emplace_back(ItemBound((*resetID), item.getBound())); } + + _masterSpatialTree.insert(itemBounds); } void Scene::removeItems(const ItemIDs& ids) { From ce5c1157872d1cc44fd79d06533e4bc1ec3ae9e3 Mon Sep 17 00:00:00 2001 From: samcake Date: Wed, 27 Jan 2016 14:26:17 -0800 Subject: [PATCH 08/49] Debugging some of the computations in the OCtree::Location --- libraries/render/src/render/Octree.cpp | 37 +++++++++++++++- libraries/render/src/render/Octree.h | 59 +++++++------------------- 2 files changed, 52 insertions(+), 44 deletions(-) diff --git a/libraries/render/src/render/Octree.cpp b/libraries/render/src/render/Octree.cpp index bc141fb797..ce0d052c09 100644 --- a/libraries/render/src/render/Octree.cpp +++ b/libraries/render/src/render/Octree.cpp @@ -32,7 +32,42 @@ const double Octree::INV_DEPTH_DIM[] = { 1.0 / 16384.0, 1.0 / 32768.0 }; -Octree::Indices Octree::allocateCellPath(const CellPath& path) { + +Octree::Location::vector Octree::Location::rootTo(const Location& dest) { + Location current{ dest }; + vector path(dest.depth + 1); + path[dest.depth] = dest; + while (current.depth > 0) { + current = current.parent(); + path[current.depth] = current; + } + return path; +} + +Octree::Location Octree::Location::evalFromRange(const Coord3& minCoord, const Coord3& maxCoord, Depth rangeDepth) { + Depth depthOffset = MAX_DEPTH - rangeDepth; + Depth depth = depthOffset; + Coord3 mask(depthBitmask(depth)); + + while (depth < rangeDepth) { + Coord3 nextMask = mask | depthBitmask(depth + 1); + if ((minCoord & nextMask) != (maxCoord & nextMask)) { + break; + } + mask = nextMask; + depth++; + } + + if (depth == 0) { + return Location(); + } else { + // Location depth and coordinate are found, need to bring the coordinate from sourceDepth to destinationDepth + auto sourceCoord = (minCoord & mask); + return Location(sourceCoord >> Coord3(rangeDepth - depth), depth); + } +} + +Octree::Indices Octree::allocateCellPath(const Locations& path) { Indices cellPath; Index currentIndex = 0; diff --git a/libraries/render/src/render/Octree.h b/libraries/render/src/render/Octree.h index b16b0b1ded..afc9648d34 100644 --- a/libraries/render/src/render/Octree.h +++ b/libraries/render/src/render/Octree.h @@ -77,19 +77,21 @@ namespace render { static const Depth MAX_DEPTH { 15 }; static const double INV_DEPTH_DIM[Octree::MAX_DEPTH + 1]; - static int getDepthDimension(Depth depth) { return 2 << depth; } + static int getDepthDimension(Depth depth) { return 1 << depth; } static double getInvDepthDimension(Depth depth) { return INV_DEPTH_DIM[depth]; } - // Need 16bits integer coordinates on each axes: 32768 cell positions using Coord = int16_t; using Coord3 = glm::i16vec3; using Coord4 = glm::i16vec4; + static Coord depthBitmask(Depth depth) { return Coord(1 << (MAX_DEPTH - depth)); } + + class Location { void assertValid() { assert((pos.x >= 0) && (pos.y >= 0) && (pos.z >= 0)); - assert((pos.x < (2 << depth)) && (pos.y < (2 << depth)) && (pos.z < (2 << depth))); + assert((pos.x < (1 << depth)) && (pos.y < (1 << depth)) && (pos.z < (1 << depth))); } public: Location() {} @@ -103,35 +105,17 @@ namespace render { // Eval the octant of this cell relative to its parent Octant octant() const { return Octant((pos.x & XAxis) | (pos.y & YAxis) | (pos.z & ZAxis)); } + Coord3 octantAxes(Link octant) const { Coord3((Coord)bool(octant & XAxis), (Coord)bool(octant & YAxis), (Coord)bool(octant & ZAxis)); } // Get the Parent cell Location of this cell - Location parent() const { - return Location{ pos >> Coord3(1), Depth(depth <= 0 ? 0 : depth - 1) }; - } - Location child(Link octant) const { - return Location{ pos << Coord3(1) | Coord3((Coord)bool(octant & XAxis), (Coord)bool(octant & YAxis), (Coord)bool(octant & ZAxis)), Depth(depth + 1) }; - } + Location parent() const { return Location{ (pos >> Coord3(1)), Depth(depth <= 0 ? 0 : depth - 1) }; } + Location child(Link octant) const { return Location{ (pos << Coord3(1)) | octantAxes(octant), Depth(depth + 1) }; } using vector = std::vector< Location >; - static vector rootTo(const Location& dest) { - Location current{ dest }; - vector path(dest.depth + 1); - path[dest.depth] = dest; - while (current.depth > 0) { - current = current.parent(); - path[current.depth] = current; - } - return path; - } - }; - using CellPath = Location::vector; - - - class Range { - public: - Coord3 _min; - Coord3 _max; + static vector rootTo(const Location& dest); + static Location evalFromRange(const Coord3& minCoord, const Coord3& maxCoord, Depth rangeDepth = MAX_DEPTH); }; + using Locations = Location::vector; // Cell or Brick Indexing using Index = int32_t; @@ -184,7 +168,7 @@ namespace render { Octree() {}; // allocatePath - Indices allocateCellPath(const CellPath& path); + Indices allocateCellPath(const Locations& path); // reach to Cell and allocate the cell path to it if needed Index editCell(const Location& loc) { @@ -228,7 +212,8 @@ namespace render { } Coord3 evalCoord(const glm::vec3& pos, Depth depth = Octree::MAX_DEPTH) const { - return Coord3((pos - getOrigin()) * getInvCellWidth(depth)); + auto npos = (pos - getOrigin()); + return Coord3(npos * getInvCellWidth(depth)); } @@ -242,22 +227,10 @@ namespace render { auto minVec = bound.getMinimumPoint(); auto maxVec = bound.getMaximumPoint(); - Depth depth = MAX_DEPTH; auto minPos = evalCoord(minVec); auto maxPos = evalCoord(maxVec); - - while ((depth > 0) && - !((maxPos.x == minPos.x) && (maxPos.y == minPos.y) && (maxPos.z == minPos.z))) { - depth--; - minPos >>= 1; - maxPos >>= 1; - } - - if (depth == 0) { - return Location(); - } else { - return Location(minPos, depth); - } + + return Location::evalFromRange(minPos, maxPos); } ItemSpatialTree() {} From 6d8c33b996eb8cf7886f4689647afd84730dc4ca Mon Sep 17 00:00:00 2001 From: samcake Date: Wed, 27 Jan 2016 18:59:45 -0800 Subject: [PATCH 09/49] Merging with upstream and still debugging the cell navigation math --- libraries/render/src/render/Octree.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/render/src/render/Octree.h b/libraries/render/src/render/Octree.h index f8afae3898..e78a876bcd 100644 --- a/libraries/render/src/render/Octree.h +++ b/libraries/render/src/render/Octree.h @@ -108,7 +108,7 @@ namespace render { // Eval the octant of this cell relative to its parent Octant octant() const { return Octant((pos.x & 1) | (pos.y & 1) | (pos.z & 1)); } - Coord3 octantAxes(Link octant) const { Coord3((Coord)bool(octant & XAxis), (Coord)bool(octant & YAxis), (Coord)bool(octant & ZAxis)); } + Coord3 octantAxes(Link octant) const { return Coord3((Coord)bool(octant & XAxis), (Coord)bool(octant & YAxis), (Coord)bool(octant & ZAxis)); } // Get the Parent cell Location of this cell Location parent() const { return Location{ (pos >> Coord3(1)), Depth(depth <= 0 ? 0 : depth - 1) }; } From 23f2c98834e6c45e893ee12f42c32e9bc4ee39fd Mon Sep 17 00:00:00 2001 From: samcake Date: Thu, 28 Jan 2016 09:23:45 -0800 Subject: [PATCH 10/49] fixing the addressing issue --- libraries/render/src/render/Octree.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/render/src/render/Octree.h b/libraries/render/src/render/Octree.h index e78a876bcd..6b55e38d8f 100644 --- a/libraries/render/src/render/Octree.h +++ b/libraries/render/src/render/Octree.h @@ -107,7 +107,7 @@ namespace render { bool operator== (const Location& right) const { return pos == right.pos && depth == right.depth; } // Eval the octant of this cell relative to its parent - Octant octant() const { return Octant((pos.x & 1) | (pos.y & 1) | (pos.z & 1)); } + Octant octant() const { return Octant((pos.x & 1) | ((pos.y & 1) << 1) | ((pos.z & 1) << 2)); } Coord3 octantAxes(Link octant) const { return Coord3((Coord)bool(octant & XAxis), (Coord)bool(octant & YAxis), (Coord)bool(octant & ZAxis)); } // Get the Parent cell Location of this cell From a2a9227d28a72883ec91e1c2b26385fdc640cb74 Mon Sep 17 00:00:00 2001 From: samcake Date: Thu, 28 Jan 2016 11:37:24 -0800 Subject: [PATCH 11/49] Merging with master --- libraries/render/src/render/Octree.h | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/libraries/render/src/render/Octree.h b/libraries/render/src/render/Octree.h index 6b55e38d8f..6362f66e2d 100644 --- a/libraries/render/src/render/Octree.h +++ b/libraries/render/src/render/Octree.h @@ -87,6 +87,15 @@ namespace render { static Coord depthBitmask(Depth depth) { return Coord(1 << (MAX_DEPTH - depth)); } + static Depth coordToDepth(Coord length) { + Depth d = MAX_DEPTH; + while (length) { + length >>= 1; + d--; + } + return d; + } + class Location { void assertValid() { @@ -203,14 +212,6 @@ namespace render { double _invSize{ 1.0 / _size }; glm::vec3 _origin{ -16384.0f }; public: - Depth coordToDepth(Coord length) const { - Depth d = MAX_DEPTH; - while(length) { - length >>= 1; - d--; - } - return d; - } float getSize() const { return _size; } const glm::vec3& getOrigin() const { return _origin; } From 972d4cda774a52c032b7e91a7494e3cc2cc5ddf5 Mon Sep 17 00:00:00 2001 From: samcake Date: Thu, 28 Jan 2016 15:31:24 -0800 Subject: [PATCH 12/49] Adding better octree visualization --- libraries/gpu/src/gpu/Color.slh | 52 ++++++++--------- .../render/src/render/DrawSceneOctree.cpp | 9 ++- libraries/render/src/render/DrawSceneOctree.h | 1 + .../render/src/render/drawCellBounds.slf | 2 +- .../render/src/render/drawCellBounds.slv | 57 ++++++++++++++++++- 5 files changed, 89 insertions(+), 32 deletions(-) diff --git a/libraries/gpu/src/gpu/Color.slh b/libraries/gpu/src/gpu/Color.slh index f633e6e2d4..d4d9ba7b81 100644 --- a/libraries/gpu/src/gpu/Color.slh +++ b/libraries/gpu/src/gpu/Color.slh @@ -10,35 +10,6 @@ !> <@if not GPU_COLOR_SLH@> <@def GPU_COLOR_SLH@> - 0.04045 - // constants: - // T = 0.04045 - // A = 1 / 1.055 = 0.94786729857 - // B = 0.055 * A = 0.05213270142 - // C = 1 / 12.92 = 0.0773993808 - // G = 2.4 - const float T = 0.04045; - const float A = 0.947867; - const float B = 0.052132; - const float C = 0.077399; - const float G = 2.4; - - if (cs > T) { - return pow((cs * A + B), G); - } else { - return cs * C; - } -} - -vec3 colorToLinear(vec3 srgb) { - return vec3(colorComponentToLinear(srgb.x), colorComponentToLinear(srgb.y), colorComponentToLinear(srgb.z)); -} -!> vec3 colorToLinearRGB(vec3 srgb) { const float GAMMA_22 = 2.2; @@ -49,4 +20,27 @@ vec4 colorToLinearRGBA(vec4 srgba) { return vec4(colorToLinearRGB(srgba.xyz), srgba.w); } +<@func declareColorWheel()@> +vec3 colorWheel(float normalizedHue) { + float v = normalizedHue * 6.f; + if (v < 0.f) { + return vec3(1.f, 0.f, 0.f); + } else if (v < 1.f) { + return vec3(1.f, v, 0.f); + } else if (v < 2.f) { + return vec3(1.f - (v-1.f), 1.f, 0.f); + } else if (v < 3.f) { + return vec3(0.f, 1.f, (v-2.f)); + } else if (v < 4.f) { + return vec3(0.f, 1.f - (v-3.f), 1.f ); + } else if (v < 5.f) { + return vec3((v-4.f), 0.f, 1.f ); + } else if (v < 6.f) { + return vec3(1.f, 0.f, 1.f - (v-5.f)); + } else { + return vec3(1.f, 0.f, 0.f); + } +} +<@endfunc@> + <@endif@> \ No newline at end of file diff --git a/libraries/render/src/render/DrawSceneOctree.cpp b/libraries/render/src/render/DrawSceneOctree.cpp index fbbb919bc8..2cfa0e190a 100644 --- a/libraries/render/src/render/DrawSceneOctree.cpp +++ b/libraries/render/src/render/DrawSceneOctree.cpp @@ -37,6 +37,7 @@ const gpu::PipelinePointer DrawSceneOctree::getDrawCellBoundsPipeline() { _drawBoundPosLoc = program->getUniforms().findLocation("inBoundPos"); _drawBoundDimLoc = program->getUniforms().findLocation("inBoundDim"); + _drawCellLocationLoc = program->getUniforms().findLocation("inCellLocation"); auto state = std::make_shared(); @@ -104,10 +105,16 @@ void DrawSceneOctree::run(const SceneContextPointer& sceneContext, AABox* cellAABox = reinterpret_cast (_cells->editData()); const unsigned int VEC3_ADRESS_OFFSET = 3; + const auto& inCells = scene->getSpatialTree()._cells; for (int i = 0; i < nbCells; i++) { batch._glUniform3fv(_drawBoundPosLoc, 1, (const float*) (cellAABox + i)); - batch._glUniform3fv(_drawBoundDimLoc, 1, ((const float*) (cellAABox + i)) + VEC3_ADRESS_OFFSET); + batch._glUniform3fv(_drawBoundDimLoc, 1, ((const float*)(cellAABox + i)) + VEC3_ADRESS_OFFSET); + + auto& cellLoc = inCells[i].getlocation(); + glm::ivec4 cellLocation(cellLoc.pos.x, cellLoc.pos.y, cellLoc.pos.z, cellLoc.depth); + + batch._glUniform4iv(_drawCellLocationLoc, 1, ((const int*)(&cellLocation))); batch.draw(gpu::LINES, 24, 0); } diff --git a/libraries/render/src/render/DrawSceneOctree.h b/libraries/render/src/render/DrawSceneOctree.h index d7ded1aa2e..409dfae42c 100644 --- a/libraries/render/src/render/DrawSceneOctree.h +++ b/libraries/render/src/render/DrawSceneOctree.h @@ -20,6 +20,7 @@ namespace render { int _drawBoundPosLoc; int _drawBoundDimLoc; + int _drawCellLocationLoc; gpu::PipelinePointer _drawCellBoundsPipeline; gpu::BufferPointer _cells; diff --git a/libraries/render/src/render/drawCellBounds.slf b/libraries/render/src/render/drawCellBounds.slf index e82622085c..6b9f5b96bc 100644 --- a/libraries/render/src/render/drawCellBounds.slf +++ b/libraries/render/src/render/drawCellBounds.slf @@ -16,5 +16,5 @@ out vec4 outFragColor; void main(void) { - outFragColor = vec4(1.0, 1.0, 1.0, 1.0); + outFragColor = varColor; } diff --git a/libraries/render/src/render/drawCellBounds.slv b/libraries/render/src/render/drawCellBounds.slv index 634c694d4e..e5ff6c6939 100644 --- a/libraries/render/src/render/drawCellBounds.slv +++ b/libraries/render/src/render/drawCellBounds.slv @@ -16,8 +16,63 @@ <$declareStandardTransform()$> +<@include gpu/Color.slh@> +<$declareColorWheel()$> + + + uniform vec3 inBoundPos; uniform vec3 inBoundDim; +uniform ivec4 inCellLocation; + +out vec4 varColor; + void main(void) { const vec4 UNIT_BOX[8] = vec4[8]( @@ -53,5 +108,5 @@ void main(void) { TransformObject obj = getTransformObject(); <$transformModelToClipPos(cam, obj, pos, gl_Position)$> - // varTexcoord = (pos.xy + 1) * 0.5; + varColor = vec4(colorWheel(fract(float(inCellLocation.w) / 5.0)), 1.0); } \ No newline at end of file From 71e6c6e443aea5d85ce825f0ad88e4d1ed3a4d8c Mon Sep 17 00:00:00 2001 From: samcake Date: Thu, 28 Jan 2016 17:57:21 -0800 Subject: [PATCH 13/49] Starting to add the Items on the Octree --- libraries/render/src/render/Item.h | 4 +- libraries/render/src/render/Octree.cpp | 25 +++++++++++ libraries/render/src/render/Octree.h | 61 +++++++++++++++++--------- 3 files changed, 67 insertions(+), 23 deletions(-) diff --git a/libraries/render/src/render/Item.h b/libraries/render/src/render/Item.h index 568b052623..46793d1ec3 100644 --- a/libraries/render/src/render/Item.h +++ b/libraries/render/src/render/Item.h @@ -190,11 +190,12 @@ inline QDebug operator<<(QDebug debug, const ItemFilter& me) { return debug; } +using ItemID = uint32_t; class Item { public: typedef std::vector Vector; - typedef unsigned int ID; + typedef ItemID ID; static const ID INVALID_ITEM_ID = 0; @@ -420,7 +421,6 @@ typedef Item::PayloadPointer PayloadPointer; typedef std::vector< PayloadPointer > Payloads; // A few typedefs for standard containers of ItemIDs -using ItemID = Item::ID; using ItemIDs = std::vector; using ItemIDSet = std::set; diff --git a/libraries/render/src/render/Octree.cpp b/libraries/render/src/render/Octree.cpp index 9aa4541d65..a162ee3a3f 100644 --- a/libraries/render/src/render/Octree.cpp +++ b/libraries/render/src/render/Octree.cpp @@ -125,11 +125,36 @@ Octree::Indices Octree::indexCellPath(const Locations& path) { } +Octree::Index Octree::allocateBrick() { + Index brickIdx = _bricks.size(); + _bricks.push_back(Brick()); + return brickIdx; +} + +Octree::Index Octree::accessCellBrick(const Location& loc, const CellBrickAccessor& accessor) { + auto cellId = indexCell(loc); + auto cell = editCell(cellId); + if (!cell.asBrick()) { + cell.setBrick(allocateBrick()); + } + + // access the brick + auto& brick = _bricks[cell.brick()]; + + // execute the accessor + accessor(brick, cell.brick()); + + return cell.brick(); +} + void ItemSpatialTree::insert(const ItemBounds& items) { for (auto& item : items) { if (!item.bound.isNull()) { auto cellIdx = indexCell(evalLocation(item.bound)); + accessCellBrick(evalLocation(item.bound), [&] (Brick& brick, Octree::Index cellID) { + brick.items.push_back(item.id); + }); } } } diff --git a/libraries/render/src/render/Octree.h b/libraries/render/src/render/Octree.h index 6362f66e2d..7ee4910358 100644 --- a/libraries/render/src/render/Octree.h +++ b/libraries/render/src/render/Octree.h @@ -16,12 +16,21 @@ #include #include #include +#include #include #include #include +// maybe we could avoid the Item inclusion here for the OCtree class? +#include "Item.h" + namespace render { + class Brick { + public: + std::vector items; + }; + class Octree { public: @@ -40,8 +49,10 @@ namespace render { NUM_OCTANTS, Parent = NUM_OCTANTS, + BrickLink = Parent + 1, + + NUM_LINKS = BrickLink + 1, - NUM_LINKS = NUM_OCTANTS + 1, XAxis = 0x01, YAxis = 0x02, @@ -150,27 +161,25 @@ namespace render { bool asChild(Link octant) const { return child(octant) != INVALID; } void setChild(Link octant, Index child) { _links[octant] = child; } + Index brick() const { return _links[BrickLink]; } + bool asBrick() const { return _links[BrickLink] != INVALID; } + void setBrick(Index brick) { _links[BrickLink] = brick; } + Cell() : - _links({ { INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID } }) + _links({ { INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID } }) {} Cell(Index parent, Location loc) : _location(loc), - _links({ { INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, parent } }) + _links({ { INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, parent, INVALID } }) {} - Index brick{ INVALID }; - private: - Location _location; std::array _links; + Location _location; }; using Cells = std::vector< Cell >; - class Brick { - public: - - }; using Bricks = std::vector< Brick >; @@ -194,15 +203,32 @@ namespace render { assert(index < _cells.size()); return _cells[index]; } + + + + // Let s talk about the Cell Bricks now + using CellBrickAccessor = std::function; + + Index accessCellBrick(const Location& loc, const CellBrickAccessor& accessor); + + const Brick& getBrick(Index index) const { + assert(index < _bricks.size()); + return _bricks[index]; + } + + protected: Index allocateCell(Index parent, const Location& location); + Index allocateBrick(); - + Cell& editCell(Index index) { + assert(index < _cells.size()); + return _cells[index]; + } }; } -// CLose the namespace here before including the Item in the picture, maybe Octre could stay pure of it -#include "Item.h" +// CLose the namespace here before including the Item in the picture, maybe Octre could stay pure of it namespace render { // An octree of Items organizing them efficiently for culling @@ -237,15 +263,8 @@ namespace render { return AABox(evalPos(loc.pos, cellWidth), cellWidth); } - Location evalLocation(const AABox& bound) const { - auto minVec = bound.getMinimumPoint(); - auto maxVec = bound.getMaximumPoint(); - - auto minPos = evalCoord(minVec); - auto maxPos = evalCoord(maxVec); - - return Location::evalFromRange(minPos, maxPos); + return Location::evalFromRange(evalCoord(bound.getMinimumPoint()), evalCoord(bound.getMaximumPoint())); } ItemSpatialTree() {} From 7b50a4d05f5d75768de9f4e113f0f3c0199a3a5a Mon Sep 17 00:00:00 2001 From: samcake Date: Fri, 29 Jan 2016 14:30:53 -0800 Subject: [PATCH 14/49] clening up the BRick insert item interface and the shaders --- .../render/src/render/DrawSceneOctree.cpp | 15 +--- libraries/render/src/render/DrawSceneOctree.h | 2 - libraries/render/src/render/Item.h | 9 +- libraries/render/src/render/Octree.cpp | 90 +++++++++++++++---- libraries/render/src/render/Octree.h | 22 +++-- libraries/render/src/render/Scene.cpp | 28 +++--- libraries/render/src/render/SceneOctree.slh | 76 ++++++++++++++++ .../render/src/render/drawCellBounds.slv | 54 +---------- 8 files changed, 192 insertions(+), 104 deletions(-) create mode 100644 libraries/render/src/render/SceneOctree.slh diff --git a/libraries/render/src/render/DrawSceneOctree.cpp b/libraries/render/src/render/DrawSceneOctree.cpp index 2cfa0e190a..1a89aa3275 100644 --- a/libraries/render/src/render/DrawSceneOctree.cpp +++ b/libraries/render/src/render/DrawSceneOctree.cpp @@ -34,9 +34,7 @@ const gpu::PipelinePointer DrawSceneOctree::getDrawCellBoundsPipeline() { gpu::Shader::BindingSet slotBindings; gpu::Shader::makeProgram(*program, slotBindings); - - _drawBoundPosLoc = program->getUniforms().findLocation("inBoundPos"); - _drawBoundDimLoc = program->getUniforms().findLocation("inBoundDim"); + _drawCellLocationLoc = program->getUniforms().findLocation("inCellLocation"); auto state = std::make_shared(); @@ -69,10 +67,7 @@ void DrawSceneOctree::run(const SceneContextPointer& sceneContext, if (!_cells) { _cells = std::make_shared(); } - /* if (!_octreeInfo) { - _octreeInfo = std::make_shared();; - }*/ - + const auto& inCells = scene->getSpatialTree()._cells; _cells->resize(inCells.size() * sizeof(AABox)); AABox* cellAABox = reinterpret_cast (_cells->editData()); @@ -102,15 +97,9 @@ void DrawSceneOctree::run(const SceneContextPointer& sceneContext, // bind the one gpu::Pipeline we need batch.setPipeline(getDrawCellBoundsPipeline()); - AABox* cellAABox = reinterpret_cast (_cells->editData()); - - const unsigned int VEC3_ADRESS_OFFSET = 3; const auto& inCells = scene->getSpatialTree()._cells; for (int i = 0; i < nbCells; i++) { - batch._glUniform3fv(_drawBoundPosLoc, 1, (const float*) (cellAABox + i)); - batch._glUniform3fv(_drawBoundDimLoc, 1, ((const float*)(cellAABox + i)) + VEC3_ADRESS_OFFSET); - auto& cellLoc = inCells[i].getlocation(); glm::ivec4 cellLocation(cellLoc.pos.x, cellLoc.pos.y, cellLoc.pos.z, cellLoc.depth); diff --git a/libraries/render/src/render/DrawSceneOctree.h b/libraries/render/src/render/DrawSceneOctree.h index 409dfae42c..1b9bef12de 100644 --- a/libraries/render/src/render/DrawSceneOctree.h +++ b/libraries/render/src/render/DrawSceneOctree.h @@ -18,8 +18,6 @@ namespace render { class DrawSceneOctree { - int _drawBoundPosLoc; - int _drawBoundDimLoc; int _drawCellLocationLoc; gpu::PipelinePointer _drawCellBoundsPipeline; gpu::BufferPointer _cells; diff --git a/libraries/render/src/render/Item.h b/libraries/render/src/render/Item.h index 46793d1ec3..7b164a91eb 100644 --- a/libraries/render/src/render/Item.h +++ b/libraries/render/src/render/Item.h @@ -191,6 +191,7 @@ inline QDebug operator<<(QDebug debug, const ItemFilter& me) { } using ItemID = uint32_t; +using ItemCell = int32_t; class Item { public: @@ -198,6 +199,7 @@ public: typedef ItemID ID; static const ID INVALID_ITEM_ID = 0; + static const ItemCell INVALID_CELL = -1; // Bound is the AABBox fully containing this item typedef AABox Bound; @@ -292,12 +294,16 @@ public: // Main scene / item managment interface Reset/Update/Kill void resetPayload(const PayloadPointer& payload); + void resetCell(ItemCell cell) { _cell = cell; } void update(const UpdateFunctorPointer& updateFunctor) { _payload->update(updateFunctor); } // Communicate update to the payload - void kill() { _payload.reset(); _key._flags.reset(); } // Kill means forget the payload and key + void kill() { _payload.reset(); _key._flags.reset(); _cell = INVALID_CELL; } // Kill means forget the payload and key and cell // Check heuristic key const ItemKey& getKey() const { return _key; } + // Check spatial cell + const ItemCell& getCell() const { return _cell; } + // Payload Interface // Get the bound of the item expressed in world space (or eye space depending on the key.isWorldSpace()) @@ -318,6 +324,7 @@ public: protected: PayloadPointer _payload; ItemKey _key; + ItemCell _cell{ INVALID_CELL }; friend class Scene; }; diff --git a/libraries/render/src/render/Octree.cpp b/libraries/render/src/render/Octree.cpp index a162ee3a3f..0b638db3ae 100644 --- a/libraries/render/src/render/Octree.cpp +++ b/libraries/render/src/render/Octree.cpp @@ -67,7 +67,7 @@ Octree::Location Octree::Location::evalFromRange(const Coord3& minCoord, const C } } -Octree::Indices Octree::indexAllocatedCellPath(const Locations& path) const { +Octree::Indices Octree::indexConcreteCellPath(const Locations& path) const { Index currentIndex = ROOT; Indices cellPath(1, currentIndex); @@ -105,7 +105,7 @@ Octree::Index Octree::allocateCell(Index parent, const Location& location) { Octree::Indices Octree::indexCellPath(const Locations& path) { // First through the aallocated cells - Indices cellPath = indexAllocatedCellPath(path); + Indices cellPath = indexConcreteCellPath(path); // Catch up from the last allocated cell on the path auto currentIndex = cellPath.back(); @@ -131,34 +131,86 @@ Octree::Index Octree::allocateBrick() { return brickIdx; } -Octree::Index Octree::accessCellBrick(const Location& loc, const CellBrickAccessor& accessor) { - auto cellId = indexCell(loc); - auto cell = editCell(cellId); +Octree::Index Octree::accessCellBrick(Index cellID, const CellBrickAccessor& accessor, bool createBrick) { + assert(cellID != INVALID); + auto cell = editCell(cellID); if (!cell.asBrick()) { + if (!createBrick) { + return INVALID; + } cell.setBrick(allocateBrick()); } // access the brick - auto& brick = _bricks[cell.brick()]; + auto brickID = cell.brick(); + auto& brick = _bricks[brickID]; // execute the accessor - accessor(brick, cell.brick()); + accessor(brick, brickID); - return cell.brick(); + return brickID; } -void ItemSpatialTree::insert(const ItemBounds& items) { - for (auto& item : items) { - if (!item.bound.isNull()) { - auto cellIdx = indexCell(evalLocation(item.bound)); - - accessCellBrick(evalLocation(item.bound), [&] (Brick& brick, Octree::Index cellID) { - brick.items.push_back(item.id); - }); +Octree::Locations ItemSpatialTree::evalLocations(const ItemBounds& bounds) const { + Locations locations; + locations.reserve(bounds.size()); + for (auto& bound : bounds) { + if (!bound.bound.isNull()) { + locations.emplace_back(evalLocation(bound.bound)); + } else { + locations.emplace_back(Location()); } } + return locations; +} + +ItemSpatialTree::Index ItemSpatialTree::insertItem(const Location& location, const ItemID& item) { + // Go to the cell + auto cellIdx = indexCell(location); + + // Add the item to the brick (and a brick if needed) + accessCellBrick(cellIdx, [&](Brick& brick, Octree::Index cellID) { + brick.items.push_back(item); + }, true); + + return cellIdx; +} + +bool ItemSpatialTree::removeItem(Index cellIdx, const ItemID& item) { + auto success = false; + + // Access the brick at the cell (without createing new ones) + auto brickIdx = accessCellBrick(cellIdx, [&](Brick& brick, Octree::Index brickID) { + // remove the item from the list + brick.items.erase(std::find(brick.items.begin(), brick.items.end(), item)); + success = true; + }, false); // do not create brick! + + return success; +} + +ItemSpatialTree::Index ItemSpatialTree::resetItem(Index oldCell, const Location& location, const ItemID& item) { + // do we know about this item ? + if (oldCell == Item::INVALID_CELL) { + auto newCell = insertItem(location, item); + return newCell; + } else { + auto newCell = indexCell(location); + + accessCellBrick(newCell, [&](Brick& brick, Octree::Index brickID) { + // insert the item only if the new cell is different from the previous one + if (newCell != oldCell) { + brick.items.push_back(item); + } + }, true); + + // now we know where the cell has been added and where it was, + // if different then go clean the previous cell + if (newCell != oldCell) { + removeItem(oldCell, item); + } + + return newCell; + } } -void ItemSpatialTree::erase(const ItemBounds& items) { - -} \ No newline at end of file diff --git a/libraries/render/src/render/Octree.h b/libraries/render/src/render/Octree.h index 7ee4910358..7d3cce9267 100644 --- a/libraries/render/src/render/Octree.h +++ b/libraries/render/src/render/Octree.h @@ -144,7 +144,7 @@ namespace render { using Locations = Location::vector; // Cell or Brick Indexing - using Index = int32_t; + using Index = ItemCell; // int32_t static const Index INVALID = -1; static const Index ROOT = 0; using Indices = std::vector; @@ -194,9 +194,9 @@ namespace render { Indices indexCellPath(const Locations& path); Index indexCell(const Location& loc) { return indexCellPath(Location::pathTo(loc)).back(); } - // Same as indexCellPath except that NO cells are allocated, + // Same as indexCellPath except that NO cells are allocated, only the COncrete cells previously allocated // the returned indices stops at the last existing cell on the requested path. - Indices indexAllocatedCellPath(const Locations& path) const; + Indices indexConcreteCellPath(const Locations& path) const; // Reach a concrete cell const Cell& getCell(Index index) const { @@ -209,7 +209,10 @@ namespace render { // Let s talk about the Cell Bricks now using CellBrickAccessor = std::function; - Index accessCellBrick(const Location& loc, const CellBrickAccessor& accessor); + // acces a cell (must be concrete), then call the brick accessor if the brick exists ( or is just created if authorized to) + // This returns the Brick index + Index accessCellBrick(Index cellID, const CellBrickAccessor& accessor, bool createBrick = true); + const Brick& getBrick(Index index) const { assert(index < _bricks.size()); @@ -258,19 +261,22 @@ namespace render { } + // Bound to Location AABox evalBound(const Location& loc) const { float cellWidth = getCellWidth(loc.depth); return AABox(evalPos(loc.pos, cellWidth), cellWidth); } - Location evalLocation(const AABox& bound) const { return Location::evalFromRange(evalCoord(bound.getMinimumPoint()), evalCoord(bound.getMaximumPoint())); } + Locations evalLocations(const ItemBounds& bounds) const; + + // Managing itemsInserting items in cells + Index insertItem(const Location& location, const ItemID& item); + bool removeItem(Index cellIdx, const ItemID& item); + Index resetItem(Index oldCell, const Location& location, const ItemID& item); ItemSpatialTree() {} - - void insert(const ItemBounds& items); - void erase(const ItemBounds& items); }; } diff --git a/libraries/render/src/render/Scene.cpp b/libraries/render/src/render/Scene.cpp index 1458e6fcf8..241f23e290 100644 --- a/libraries/render/src/render/Scene.cpp +++ b/libraries/render/src/render/Scene.cpp @@ -129,22 +129,28 @@ void Scene::processPendingChangesQueue() { } void Scene::resetItems(const ItemIDs& ids, Payloads& payloads) { - auto resetID = ids.begin(); - auto resetPayload = payloads.begin(); - ItemBounds itemBounds; - itemBounds.reserve(ids.size()); - - for (;resetID != ids.end(); resetID++, resetPayload++) { - auto& item = _items[(*resetID)]; + + auto& resetPayload = payloads.begin(); + for (auto resetID : ids) { + // Access the true item + auto& item = _items[resetID]; auto oldKey = item.getKey(); + auto oldCell = item.getCell(); + + // Reset the item with a new payload item.resetPayload(*resetPayload); - _masterBucketMap.reset((*resetID), oldKey, item.getKey()); + // Reset the item in the Bucket map + _masterBucketMap.reset(resetID, oldKey, item.getKey()); - itemBounds.emplace_back(ItemBound((*resetID), item.getBound())); + // Reset the item in the spatial tree + auto newCellLocation = _masterSpatialTree.evalLocation(item.getBound()); + auto newCell = _masterSpatialTree.resetItem(oldCell, newCellLocation, resetID); + item.resetCell(newCell); + + // next loop + resetPayload++; } - - _masterSpatialTree.insert(itemBounds); } void Scene::removeItems(const ItemIDs& ids) { diff --git a/libraries/render/src/render/SceneOctree.slh b/libraries/render/src/render/SceneOctree.slh new file mode 100644 index 0000000000..d3fa36cf0f --- /dev/null +++ b/libraries/render/src/render/SceneOctree.slh @@ -0,0 +1,76 @@ + +<@if not RENDER_OCTREE_SLH@> +<@def RENDER_OCTREE_SLH@> + +const float _size = 32768.0; +const float _invSize = 1.0 / _size; +const vec3 _origin = vec3(-16384.0); + +float getSize() { return _size; } +vec3 getOrigin() { return _origin; } + +const int MAX_DEPTH = 15; +const float DEPTH_DIM[16] = float[16]( + 1.0, + 2.0, + 4.0, + 8.0, + 16.0, + 32.0, + 64.0, + 128.0, + 256.0, + 512.0, + 1024.0, + 2048.0, + 4096.0, + 8192.0, + 16384.0, + 32768.0 ); +const float INV_DEPTH_DIM[16] = float[16]( + 1.0, + 1.0 / 2.0, + 1.0 / 4.0, + 1.0 / 8.0, + 1.0 / 16.0, + 1.0 / 32.0, + 1.0 / 64.0, + 1.0 / 128.0, + 1.0 / 256.0, + 1.0 / 512.0, + 1.0 / 1024.0, + 1.0 / 2048.0, + 1.0 / 4096.0, + 1.0 / 8192.0, + 1.0 / 16384.0, + 1.0 / 32768.0 ); + +int getDepthDimension(int depth) { return 1 << depth; } +float getDepthDimensionf(int depth) { return DEPTH_DIM[depth]; } +float getInvDepthDimension(int depth) { return INV_DEPTH_DIM[depth]; } + +float getCellWidth(int depth) { return _size * getInvDepthDimension(depth); } +float getInvCellWidth(int depth) { return getDepthDimensionf(depth) * _invSize; } + +vec3 evalPos(ivec3 coord, int depth = MAX_DEPTH) { + return getOrigin() + vec3(coord) * getCellWidth(depth); +} +vec3 evalPosDepthWidth(ivec3 coord, float cellWidth) { + return getOrigin() + vec3(coord) * cellWidth; +} + +vec4 evalBound(ivec4 loc) { + float cellWidth = getCellWidth(loc.w); + return vec4(evalPosDepthWidth(loc.xyz, cellWidth), cellWidth); +} + + +<@endif@> diff --git a/libraries/render/src/render/drawCellBounds.slv b/libraries/render/src/render/drawCellBounds.slv index e5ff6c6939..13b0c465d5 100644 --- a/libraries/render/src/render/drawCellBounds.slv +++ b/libraries/render/src/render/drawCellBounds.slv @@ -19,56 +19,8 @@ <@include gpu/Color.slh@> <$declareColorWheel()$> - -const float _size = 32768.0; -const float _invSize = 1.0 / _size; -const vec3 _origin = vec3(-16384.0); - -float getSize() { return _size; } -vec3 getOrigin() { return _origin; } - -const int MAX_DEPTH = 15; -const float INV_DEPTH_DIM[16] = float[16]( - 1.0, - 1.0 / 2.0, - 1.0 / 4.0, - 1.0 / 8.0, - 1.0 / 16.0, - 1.0 / 32.0, - 1.0 / 64.0, - 1.0 / 128.0, - 1.0 / 256.0, - 1.0 / 512.0, - 1.0 / 1024.0, - 1.0 / 2048.0, - 1.0 / 4096.0, - 1.0 / 8192.0, - 1.0 / 16384.0, - 1.0 / 32768.0 ); - -int getDepthDimension(int depth) { return 1 << depth; } -float getInvDepthDimension(int depth) { return INV_DEPTH_DIM[depth]; } - -float getCellWidth(int depth) { return _size * getInvDepthDimension(depth); } -float getInvCellWidth(int depth) { return float(getDepthDimension(depth)) * _invSize; } - -vec3 evalPos(ivec3 coord, int depth = MAX_DEPTH) { - return getOrigin() + vec3(coord) * getCellWidth(depth); -} -vec3 evalPos(ivec3 coord, float cellWidth) { - return getOrigin() + vec3(coord) * cellWidth; -} - -vec4 evalBound(ivec4 loc) { - float cellWidth = getCellWidth(loc.w); - return vec4(evalPos(loc.xyz, cellWidth), cellWidth); -} -!> - -uniform vec3 inBoundPos; -uniform vec3 inBoundDim; uniform ivec4 inCellLocation; out vec4 varColor; @@ -101,7 +53,9 @@ void main(void) { ); vec4 pos = UNIT_BOX[UNIT_BOX_LINE_INDICES[gl_VertexID]]; - pos.xyz = inBoundPos + inBoundDim * pos.xyz; + vec4 cellBound = evalBound(inCellLocation); + + pos.xyz = cellBound.xyz + vec3(cellBound.w) * pos.xyz; // standard transform TransformCamera cam = getTransformCamera(); From 59434e1ea9f53a62d9b3ad49ec41e2b7aba59878 Mon Sep 17 00:00:00 2001 From: samcake Date: Fri, 29 Jan 2016 18:16:45 -0800 Subject: [PATCH 15/49] DDisplay of the octree cells is working, added the Brick empty / full per cell --- .../render/src/render/DrawSceneOctree.cpp | 12 ++-- libraries/render/src/render/DrawStatus.cpp | 20 +++++++ libraries/render/src/render/DrawStatus.h | 6 +- libraries/render/src/render/Octree.cpp | 55 ++++++++++--------- libraries/render/src/render/Octree.h | 23 ++++++-- libraries/render/src/render/Scene.cpp | 35 ++++++++++-- .../render/src/render/drawCellBounds.slv | 7 ++- .../render/src/render/drawItemBounds.slf | 5 +- .../render/src/render/drawItemBounds.slv | 26 ++++++--- 9 files changed, 132 insertions(+), 57 deletions(-) diff --git a/libraries/render/src/render/DrawSceneOctree.cpp b/libraries/render/src/render/DrawSceneOctree.cpp index 1a89aa3275..55e0506a1d 100644 --- a/libraries/render/src/render/DrawSceneOctree.cpp +++ b/libraries/render/src/render/DrawSceneOctree.cpp @@ -42,9 +42,7 @@ const gpu::PipelinePointer DrawSceneOctree::getDrawCellBoundsPipeline() { state->setDepthTest(true, false, gpu::LESS_EQUAL); // Blend on transparent - 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); + state->setBlendFunction(true, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA); // Good to go add the brand new pipeline _drawCellBoundsPipeline = gpu::Pipeline::create(program, state); @@ -99,9 +97,13 @@ void DrawSceneOctree::run(const SceneContextPointer& sceneContext, const auto& inCells = scene->getSpatialTree()._cells; - for (int i = 0; i < nbCells; i++) { - auto& cellLoc = inCells[i].getlocation(); + for (const auto& cell: inCells ) { + auto cellLoc = cell.getlocation(); + glm::ivec4 cellLocation(cellLoc.pos.x, cellLoc.pos.y, cellLoc.pos.z, cellLoc.depth); + if (cell.isBrickEmpty() || !cell.hasBrick()) { + cellLocation.w *= -1; + } batch._glUniform4iv(_drawCellLocationLoc, 1, ((const int*)(&cellLocation))); diff --git a/libraries/render/src/render/DrawStatus.cpp b/libraries/render/src/render/DrawStatus.cpp index 9ffb444e8e..584f41499a 100644 --- a/libraries/render/src/render/DrawStatus.cpp +++ b/libraries/render/src/render/DrawStatus.cpp @@ -43,6 +43,7 @@ const gpu::PipelinePointer DrawStatus::getDrawItemBoundsPipeline() { _drawItemBoundPosLoc = program->getUniforms().findLocation("inBoundPos"); _drawItemBoundDimLoc = program->getUniforms().findLocation("inBoundDim"); + _drawItemCellLocLoc = program->getUniforms().findLocation("inCellLocation"); auto state = std::make_shared(); @@ -121,11 +122,17 @@ void DrawStatus::run(const SceneContextPointer& sceneContext, if (!_itemStatus) { _itemStatus = std::make_shared();; } + if (!_itemCells) { + _itemCells = std::make_shared();; + } _itemBounds->resize((inItems.size() * sizeof(AABox))); _itemStatus->resize((inItems.size() * NUM_STATUS_VEC4_PER_ITEM * sizeof(glm::vec4))); + _itemCells->resize((inItems.size() * sizeof(Octree::Location))); + AABox* itemAABox = reinterpret_cast (_itemBounds->editData()); glm::ivec4* itemStatus = reinterpret_cast (_itemStatus->editData()); + Octree::Location* itemCell = reinterpret_cast (_itemCells->editData()); for (auto& item : inItems) { if (!item.bound.isInvalid()) { if (!item.bound.isNull()) { @@ -133,8 +140,12 @@ void DrawStatus::run(const SceneContextPointer& sceneContext, } else { (*itemAABox).setBox(item.bound.getCorner(), 0.1f); } + + auto& itemScene = scene->getItem(item.id); + (*itemCell) = scene->getSpatialTree().getCellLocation(itemScene.getCell()); + auto itemStatusPointer = itemScene.getStatus(); if (itemStatusPointer) { // Query the current status values, this is where the statusGetter lambda get called @@ -159,6 +170,7 @@ void DrawStatus::run(const SceneContextPointer& sceneContext, nbItems++; itemAABox++; + itemCell++; } } } @@ -184,6 +196,7 @@ void DrawStatus::run(const SceneContextPointer& sceneContext, AABox* itemAABox = reinterpret_cast (_itemBounds->editData()); glm::ivec4* itemStatus = reinterpret_cast (_itemStatus->editData()); + Octree::Location* itemCell = reinterpret_cast (_itemCells->editData()); const unsigned int VEC3_ADRESS_OFFSET = 3; @@ -191,8 +204,15 @@ void DrawStatus::run(const SceneContextPointer& sceneContext, for (int i = 0; i < nbItems; i++) { batch._glUniform3fv(_drawItemBoundPosLoc, 1, (const float*) (itemAABox + i)); batch._glUniform3fv(_drawItemBoundDimLoc, 1, ((const float*) (itemAABox + i)) + VEC3_ADRESS_OFFSET); + + + glm::ivec4 cellLocation(itemCell->pos.x, itemCell->pos.y, itemCell->pos.z, itemCell->depth); + + batch._glUniform4iv(_drawItemCellLocLoc, 1, ((const int*)(&cellLocation))); + batch.draw(gpu::LINES, 24, 0); + itemCell++; } } diff --git a/libraries/render/src/render/DrawStatus.h b/libraries/render/src/render/DrawStatus.h index f33193c808..263ebc2548 100644 --- a/libraries/render/src/render/DrawStatus.h +++ b/libraries/render/src/render/DrawStatus.h @@ -21,11 +21,11 @@ namespace render { Q_PROPERTY(bool showDisplay MEMBER showDisplay WRITE setShowDisplay) Q_PROPERTY(bool showNetwork MEMBER showNetwork WRITE setShowNetwork) public: - DrawStatusConfig() : Job::Config(false) {} + DrawStatusConfig() : Job::Config(true) {} // FIXME FOR debug void dirtyHelper(); - bool showDisplay{ false }; + bool showDisplay{ true }; // FIXME FOR debug bool showNetwork{ false }; public slots: @@ -59,6 +59,7 @@ namespace render { int _drawItemBoundPosLoc = -1; int _drawItemBoundDimLoc = -1; + int _drawItemCellLocLoc = -1; int _drawItemStatusPosLoc = -1; int _drawItemStatusDimLoc = -1; int _drawItemStatusValue0Loc = -1; @@ -68,6 +69,7 @@ namespace render { gpu::PipelinePointer _drawItemBoundsPipeline; gpu::PipelinePointer _drawItemStatusPipeline; gpu::BufferPointer _itemBounds; + gpu::BufferPointer _itemCells; gpu::BufferPointer _itemStatus; gpu::TexturePointer _statusIconMap; }; diff --git a/libraries/render/src/render/Octree.cpp b/libraries/render/src/render/Octree.cpp index 0b638db3ae..65187f1e4a 100644 --- a/libraries/render/src/render/Octree.cpp +++ b/libraries/render/src/render/Octree.cpp @@ -73,7 +73,7 @@ Octree::Indices Octree::indexConcreteCellPath(const Locations& path) const { for (int l = 1; l < path.size(); l++) { auto& location = path[l]; - auto nextIndex = getCell(currentIndex).child(location.octant()); + auto nextIndex = getConcreteCell(currentIndex).child(location.octant()); if (nextIndex == INVALID) { break; } @@ -133,8 +133,8 @@ Octree::Index Octree::allocateBrick() { Octree::Index Octree::accessCellBrick(Index cellID, const CellBrickAccessor& accessor, bool createBrick) { assert(cellID != INVALID); - auto cell = editCell(cellID); - if (!cell.asBrick()) { + auto& cell = editCell(cellID); + if (!cell.hasBrick()) { if (!createBrick) { return INVALID; } @@ -146,7 +146,7 @@ Octree::Index Octree::accessCellBrick(Index cellID, const CellBrickAccessor& acc auto& brick = _bricks[brickID]; // execute the accessor - accessor(brick, brickID); + accessor(cell, brick, brickID); return brickID; } @@ -164,13 +164,11 @@ Octree::Locations ItemSpatialTree::evalLocations(const ItemBounds& bounds) const return locations; } -ItemSpatialTree::Index ItemSpatialTree::insertItem(const Location& location, const ItemID& item) { - // Go to the cell - auto cellIdx = indexCell(location); - +ItemSpatialTree::Index ItemSpatialTree::insertItem(Index cellIdx, const ItemID& item) { // Add the item to the brick (and a brick if needed) - accessCellBrick(cellIdx, [&](Brick& brick, Octree::Index cellID) { + auto brickID = accessCellBrick(cellIdx, [&](Cell& cell, Brick& brick, Octree::Index cellID) { brick.items.push_back(item); + cell.signalBrickFilled(); }, true); return cellIdx; @@ -180,9 +178,12 @@ bool ItemSpatialTree::removeItem(Index cellIdx, const ItemID& item) { auto success = false; // Access the brick at the cell (without createing new ones) - auto brickIdx = accessCellBrick(cellIdx, [&](Brick& brick, Octree::Index brickID) { + accessCellBrick(cellIdx, [&](Cell& cell, Brick& brick, Octree::Index brickID) { // remove the item from the list brick.items.erase(std::find(brick.items.begin(), brick.items.end(), item)); + if (brick.items.empty()) { + cell.signalBrickEmpty(); + } success = true; }, false); // do not create brick! @@ -190,25 +191,27 @@ bool ItemSpatialTree::removeItem(Index cellIdx, const ItemID& item) { } ItemSpatialTree::Index ItemSpatialTree::resetItem(Index oldCell, const Location& location, const ItemID& item) { - // do we know about this item ? - if (oldCell == Item::INVALID_CELL) { - auto newCell = insertItem(location, item); - return newCell; - } else { - auto newCell = indexCell(location); + auto newCell = indexCell(location); - accessCellBrick(newCell, [&](Brick& brick, Octree::Index brickID) { - // insert the item only if the new cell is different from the previous one - if (newCell != oldCell) { - brick.items.push_back(item); - } + // Early exit if nothing changed + if (newCell == oldCell) { + return newCell; + } + // do we know about this item ? + else if (oldCell == Item::INVALID_CELL) { + insertItem(newCell, item); + return newCell; + } + // A true update of cell is required + else { + // Add the item to the brick (and a brick if needed) + accessCellBrick(newCell, [&](Cell& cell, Brick& brick, Octree::Index cellID) { + brick.items.push_back(item); + cell.signalBrickFilled(); }, true); - // now we know where the cell has been added and where it was, - // if different then go clean the previous cell - if (newCell != oldCell) { - removeItem(oldCell, item); - } + // And remove it from the previous one + removeItem(oldCell, item); return newCell; } diff --git a/libraries/render/src/render/Octree.h b/libraries/render/src/render/Octree.h index 7d3cce9267..bd54a297b0 100644 --- a/libraries/render/src/render/Octree.h +++ b/libraries/render/src/render/Octree.h @@ -162,8 +162,11 @@ namespace render { void setChild(Link octant, Index child) { _links[octant] = child; } Index brick() const { return _links[BrickLink]; } - bool asBrick() const { return _links[BrickLink] != INVALID; } + bool hasBrick() const { return _links[BrickLink] != INVALID; } void setBrick(Index brick) { _links[BrickLink] = brick; } + void signalBrickFilled() { _location.spare = 1; } + void signalBrickEmpty() { _location.spare = 0; } + bool isBrickEmpty() const { return _location.spare == 0; } Cell() : _links({ { INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID } }) @@ -198,8 +201,16 @@ namespace render { // the returned indices stops at the last existing cell on the requested path. Indices indexConcreteCellPath(const Locations& path) const; + // Get the cell location from the CellID + Location getCellLocation(Index cellID) const { + if ((cellID >= 0) && (cellID < _cells.size())) { + return getConcreteCell(cellID).getlocation(); + } + return Location(); + } + // Reach a concrete cell - const Cell& getCell(Index index) const { + const Cell& getConcreteCell(Index index) const { assert(index < _cells.size()); return _cells[index]; } @@ -207,14 +218,14 @@ namespace render { // Let s talk about the Cell Bricks now - using CellBrickAccessor = std::function; + using CellBrickAccessor = std::function; // acces a cell (must be concrete), then call the brick accessor if the brick exists ( or is just created if authorized to) // This returns the Brick index Index accessCellBrick(Index cellID, const CellBrickAccessor& accessor, bool createBrick = true); - const Brick& getBrick(Index index) const { + const Brick& getConcreteBrick(Index index) const { assert(index < _bricks.size()); return _bricks[index]; } @@ -272,8 +283,10 @@ namespace render { Locations evalLocations(const ItemBounds& bounds) const; // Managing itemsInserting items in cells - Index insertItem(const Location& location, const ItemID& item); + // Cells need to have been allocated first calling indexCell + Index insertItem(Index cellIdx, const ItemID& item); bool removeItem(Index cellIdx, const ItemID& item); + Index resetItem(Index oldCell, const Location& location, const ItemID& item); ItemSpatialTree() {} diff --git a/libraries/render/src/render/Scene.cpp b/libraries/render/src/render/Scene.cpp index 241f23e290..78defa4c81 100644 --- a/libraries/render/src/render/Scene.cpp +++ b/libraries/render/src/render/Scene.cpp @@ -155,15 +155,38 @@ void Scene::resetItems(const ItemIDs& ids, Payloads& payloads) { void Scene::removeItems(const ItemIDs& ids) { for (auto removedID :ids) { - _masterBucketMap.erase(removedID, _items[removedID].getKey()); - _items[removedID].kill(); + // Access the true item + auto& item = _items[removedID]; + + // Remove from Bucket map + _masterBucketMap.erase(removedID, item.getKey()); + + // Remove from spatial tree + _masterSpatialTree.removeItem(item.getCell(), removedID); + + // Kill it + item.kill(); } } void Scene::updateItems(const ItemIDs& ids, UpdateFunctors& functors) { - auto updateID = ids.begin(); - auto updateFunctor = functors.begin(); - for (;updateID != ids.end(); updateID++, updateFunctor++) { - _items[(*updateID)].update((*updateFunctor)); + + auto& updateFunctor = functors.begin(); + for (auto updateID : ids) { + // Access the true item + auto& item = _items[updateID]; + auto oldCell = item.getCell(); + + // Update it + _items[updateID].update((*updateFunctor)); + + // Update the citem in the spatial tree if needed + // THis could be avoided if we + auto newCellLocation = _masterSpatialTree.evalLocation(item.getBound()); + auto newCell = _masterSpatialTree.resetItem(oldCell, newCellLocation, updateID); + item.resetCell(newCell); + + // next loop + updateFunctor++; } } diff --git a/libraries/render/src/render/drawCellBounds.slv b/libraries/render/src/render/drawCellBounds.slv index 13b0c465d5..628d44c0b2 100644 --- a/libraries/render/src/render/drawCellBounds.slv +++ b/libraries/render/src/render/drawCellBounds.slv @@ -18,7 +18,6 @@ <@include gpu/Color.slh@> <$declareColorWheel()$> - <@include SceneOctree.slh@> uniform ivec4 inCellLocation; @@ -53,7 +52,9 @@ void main(void) { ); vec4 pos = UNIT_BOX[UNIT_BOX_LINE_INDICES[gl_VertexID]]; - vec4 cellBound = evalBound(inCellLocation); + int cellIsEmpty = sign(inCellLocation.w); + ivec4 cellLocation = ivec4(inCellLocation.xyz, (inCellLocation.w < 0 ? -inCellLocation.w : inCellLocation.w)); + vec4 cellBound = evalBound(cellLocation); pos.xyz = cellBound.xyz + vec3(cellBound.w) * pos.xyz; @@ -62,5 +63,5 @@ void main(void) { TransformObject obj = getTransformObject(); <$transformModelToClipPos(cam, obj, pos, gl_Position)$> - varColor = vec4(colorWheel(fract(float(inCellLocation.w) / 5.0)), 1.0); + varColor = vec4(colorWheel(fract(float(inCellLocation.w) / 5.0)), 0.5 + 0.4 * cellIsEmpty); } \ No newline at end of file diff --git a/libraries/render/src/render/drawItemBounds.slf b/libraries/render/src/render/drawItemBounds.slf index 16a8fd6165..6f5db0a6c2 100644 --- a/libraries/render/src/render/drawItemBounds.slf +++ b/libraries/render/src/render/drawItemBounds.slf @@ -12,9 +12,12 @@ // in vec4 varColor; +in vec2 varTexcoord; out vec4 outFragColor; void main(void) { - outFragColor = vec4(1.0, 0.5, 0.0, 1.0); + float var = step(fract(varTexcoord.x * varTexcoord.y * 1.0), 0.5); + + outFragColor = vec4(mix(vec3(1.0), varColor.xyz, var), varColor.a); } diff --git a/libraries/render/src/render/drawItemBounds.slv b/libraries/render/src/render/drawItemBounds.slv index 7b69370f13..08c5fc7ed4 100644 --- a/libraries/render/src/render/drawItemBounds.slv +++ b/libraries/render/src/render/drawItemBounds.slv @@ -13,22 +13,28 @@ // <@include gpu/Transform.slh@> - <$declareStandardTransform()$> +<@include gpu/Color.slh@> +<$declareColorWheel()$> + uniform vec3 inBoundPos; uniform vec3 inBoundDim; +uniform ivec4 inCellLocation; + +out vec4 varColor; +out vec2 varTexcoord; void main(void) { const vec4 UNIT_BOX[8] = vec4[8]( - vec4(0.0, 0.0, 0.0, 1.0), + vec4(0.0, 0.0, 0.0, 0.0), vec4(1.0, 0.0, 0.0, 1.0), vec4(0.0, 1.0, 0.0, 1.0), - vec4(1.0, 1.0, 0.0, 1.0), + vec4(1.0, 1.0, 0.0, 2.0), vec4(0.0, 0.0, 1.0, 1.0), - vec4(1.0, 0.0, 1.0, 1.0), - vec4(0.0, 1.0, 1.0, 1.0), - vec4(1.0, 1.0, 1.0, 1.0) + vec4(1.0, 0.0, 1.0, 2.0), + vec4(0.0, 1.0, 1.0, 2.0), + vec4(1.0, 1.0, 1.0, 3.0) ); const int UNIT_BOX_LINE_INDICES[24] = int[24]( 0, 1, @@ -44,14 +50,16 @@ void main(void) { 0, 4, 1, 5 ); - vec4 pos = UNIT_BOX[UNIT_BOX_LINE_INDICES[gl_VertexID]]; + vec4 cubeVec = UNIT_BOX[UNIT_BOX_LINE_INDICES[gl_VertexID]]; - pos.xyz = inBoundPos + inBoundDim * pos.xyz; + vec4 pos = vec4(inBoundPos + inBoundDim * cubeVec.xyz, 1.0); // standard transform TransformCamera cam = getTransformCamera(); TransformObject obj = getTransformObject(); <$transformModelToClipPos(cam, obj, pos, gl_Position)$> - // varTexcoord = (pos.xy + 1) * 0.5; + varColor = vec4(colorWheel(fract(float(inCellLocation.w) / 5.0)), 1.0); + varTexcoord = vec2(cubeVec.w, length(inBoundDim)); + } \ No newline at end of file From f551ea9f76006489419fa4fb76fc84fc4aea1a9f Mon Sep 17 00:00:00 2001 From: samcake Date: Sun, 31 Jan 2016 17:19:47 -0800 Subject: [PATCH 16/49] fixing compilation on mac and a glsl error on mac --- libraries/render/src/render/Scene.cpp | 4 ++-- libraries/render/src/render/SceneOctree.slh | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/libraries/render/src/render/Scene.cpp b/libraries/render/src/render/Scene.cpp index 78defa4c81..b05f145a49 100644 --- a/libraries/render/src/render/Scene.cpp +++ b/libraries/render/src/render/Scene.cpp @@ -130,7 +130,7 @@ void Scene::processPendingChangesQueue() { void Scene::resetItems(const ItemIDs& ids, Payloads& payloads) { - auto& resetPayload = payloads.begin(); + auto resetPayload = payloads.begin(); for (auto resetID : ids) { // Access the true item auto& item = _items[resetID]; @@ -171,7 +171,7 @@ void Scene::removeItems(const ItemIDs& ids) { void Scene::updateItems(const ItemIDs& ids, UpdateFunctors& functors) { - auto& updateFunctor = functors.begin(); + auto updateFunctor = functors.begin(); for (auto updateID : ids) { // Access the true item auto& item = _items[updateID]; diff --git a/libraries/render/src/render/SceneOctree.slh b/libraries/render/src/render/SceneOctree.slh index d3fa36cf0f..b8a179b64d 100644 --- a/libraries/render/src/render/SceneOctree.slh +++ b/libraries/render/src/render/SceneOctree.slh @@ -60,7 +60,7 @@ float getInvDepthDimension(int depth) { return INV_DEPTH_DIM[depth]; } float getCellWidth(int depth) { return _size * getInvDepthDimension(depth); } float getInvCellWidth(int depth) { return getDepthDimensionf(depth) * _invSize; } -vec3 evalPos(ivec3 coord, int depth = MAX_DEPTH) { +vec3 evalPos(ivec3 coord, int depth) { return getOrigin() + vec3(coord) * getCellWidth(depth); } vec3 evalPosDepthWidth(ivec3 coord, float cellWidth) { From 18d8a2fb42e6f8303f83a726f2665f88ccc2c7f6 Mon Sep 17 00:00:00 2001 From: samcake Date: Mon, 1 Feb 2016 13:47:38 -0800 Subject: [PATCH 17/49] Implementing the octree selection from frustum --- libraries/octree/src/ViewFrustum.h | 5 +- .../render/src/render/DrawSceneOctree.cpp | 5 + libraries/render/src/render/Octree.cpp | 115 +++++++++++++++++- libraries/render/src/render/Octree.h | 31 ++++- 4 files changed, 150 insertions(+), 6 deletions(-) diff --git a/libraries/octree/src/ViewFrustum.h b/libraries/octree/src/ViewFrustum.h index 98e782b267..548bd6a940 100644 --- a/libraries/octree/src/ViewFrustum.h +++ b/libraries/octree/src/ViewFrustum.h @@ -125,6 +125,9 @@ public: float calculateRenderAccuracy(const AABox& bounds, float octreeSizeScale = DEFAULT_OCTREE_SIZE_SCALE, int boundaryLevelAdjust = 0) const; + enum PlaneIndex { TOP_PLANE = 0, BOTTOM_PLANE, LEFT_PLANE, RIGHT_PLANE, NEAR_PLANE, FAR_PLANE, NUM_PLANES }; + + const ::Plane* getPlanes() const { return _planes; } private: // Used for keyhole calculations ViewFrustum::location pointInKeyhole(const glm::vec3& point) const; @@ -160,7 +163,7 @@ private: float _fieldOfView = DEFAULT_FIELD_OF_VIEW_DEGREES; glm::vec4 _corners[8]; glm::vec3 _cornersWorld[8]; - enum { TOP_PLANE = 0, BOTTOM_PLANE, LEFT_PLANE, RIGHT_PLANE, NEAR_PLANE, FAR_PLANE }; + // enum { TOP_PLANE = 0, BOTTOM_PLANE, LEFT_PLANE, RIGHT_PLANE, NEAR_PLANE, FAR_PLANE }; ::Plane _planes[6]; // How will this be used? const char* debugPlaneName (int plane) const; diff --git a/libraries/render/src/render/DrawSceneOctree.cpp b/libraries/render/src/render/DrawSceneOctree.cpp index 55e0506a1d..53b0e2b87a 100644 --- a/libraries/render/src/render/DrawSceneOctree.cpp +++ b/libraries/render/src/render/DrawSceneOctree.cpp @@ -80,6 +80,11 @@ void DrawSceneOctree::run(const SceneContextPointer& sceneContext, return; } + // Try that: + render::ItemIDs items; + scene->getSpatialTree().select(items, *args->_viewFrustum); + + // Allright, something to render let's do it gpu::doInBatch(args->_context, [&](gpu::Batch& batch) { glm::mat4 projMat; diff --git a/libraries/render/src/render/Octree.cpp b/libraries/render/src/render/Octree.cpp index 65187f1e4a..536e876e12 100644 --- a/libraries/render/src/render/Octree.cpp +++ b/libraries/render/src/render/Octree.cpp @@ -10,6 +10,8 @@ // #include "Octree.h" +#include + using namespace render; @@ -68,7 +70,7 @@ Octree::Location Octree::Location::evalFromRange(const Coord3& minCoord, const C } Octree::Indices Octree::indexConcreteCellPath(const Locations& path) const { - Index currentIndex = ROOT; + Index currentIndex = ROOT_CELL; Indices cellPath(1, currentIndex); for (int l = 1; l < path.size(); l++) { @@ -217,3 +219,114 @@ ItemSpatialTree::Index ItemSpatialTree::resetItem(Index oldCell, const Location& } } +int ItemSpatialTree::select(ItemIDs& selection, const ViewFrustum& frustum) const { + auto worldPlanes = frustum.getPlanes(); + Coord4f planes[6]; + for (int i = 0; i < ViewFrustum::NUM_PLANES; i++) { + ::Plane octPlane; + octPlane.setNormalAndPoint(worldPlanes[i].getNormal(), evalCoordf(worldPlanes[i].getPoint(), ROOT_DEPTH)); + planes[i] = Coord4f(octPlane.getNormal(), octPlane.getDCoefficient()); + } + return Octree::select(selection, planes); +} + +int Octree::select(ItemIDs& selection, const glm::vec4 frustum[6]) const { + + Index cellID = ROOT_CELL; + selectTraverse(cellID, selection, frustum); + return selection.size(); +} + +int Octree::selectTraverse(Index cellID, ItemIDs& selection, const Coord4f frustum[6]) const { + int numItemsIn = selection.size(); + auto cell = getConcreteCell(cellID); + + auto intersection = Octree::Location::intersectCell(cell.getlocation(), frustum); + switch (intersection) { + case Octree::Location::Outside: + // cell is outside, stop traversing this branch + return 0; + break; + case Octree::Location::Inside: { + // traverse all the Cell Branch and collect items in the selection + selection.push_back(cellID); + break; + } + case Octree::Location::Intersect: + default: { + // Cell is partially in + selection.push_back(cellID); + + // Collect the items of this cell + + // then traverse further + for (int i = 0; i < NUM_OCTANTS; i++) { + Index subCellID = cell.child((Link)i); + if (subCellID != INVALID) { + selectTraverse(subCellID, selection, frustum); + } + } + } + } + + return selection.size() - numItemsIn; +} + + +Octree::Location::Intersection Octree::Location::intersectCell(const Location& cell, const Coord4f frustum[6]) { + const Coord3f CornerOffsets[8] = { + { 0.0, 0.0, 0.0 }, + { 1.0, 0.0, 0.0 }, + { 0.0, 1.0, 0.0 }, + { 1.0, 1.0, 0.0 }, + { 0.0, 0.0, 1.0 }, + { 1.0, 0.0, 1.0 }, + { 0.0, 1.0, 1.0 }, + { 1.0, 1.0, 1.0 }, + }; + + struct Tool { + static int normalToIndex(const Coord3f& n) { + int index = 0; + if (n.x >= 0.0) index |= 1; + if (n.y >= 0.0) index |= 2; + if (n.z >= 0.0) index |= 4; + return index; + } + + static bool halfPlaneTest(const Coord4f& plane, const Coord3f& pos) { + return (glm::dot(plane, Coord4f(pos, 1.0f)) >= 0.0f); + } + }; + + Coord3f cellSize = Coord3f(Octree::getInvDepthDimension(cell.depth)); + Coord3f cellPos = Coord3f(cell.pos) * cellSize; + + bool partialFlag = false; + for (int p = 0; p < ViewFrustum::NUM_PLANES; p++) { + Coord4f plane = frustum[p]; + Coord3f planeNormal(plane); + + int index = Tool::normalToIndex(planeNormal); + + auto negTestPoint = cellPos + cellSize * CornerOffsets[index]; + + if (!Tool::halfPlaneTest(plane, negTestPoint)) { + return Outside; + } + + index = Tool::normalToIndex(-planeNormal); + + auto posTestPoint = cellPos + cellSize * CornerOffsets[index]; + + if (!Tool::halfPlaneTest(plane, posTestPoint)) { + partialFlag = true; + } + } + + if (partialFlag) { + return Intersect; + } + + return Inside; +} \ No newline at end of file diff --git a/libraries/render/src/render/Octree.h b/libraries/render/src/render/Octree.h index bd54a297b0..4f148636b5 100644 --- a/libraries/render/src/render/Octree.h +++ b/libraries/render/src/render/Octree.h @@ -85,6 +85,7 @@ namespace render { // Max depth is 15 => 32Km root down to 1m cells using Depth = int8_t; + static const Depth ROOT_DEPTH{ 0 }; static const Depth MAX_DEPTH { 15 }; static const double INV_DEPTH_DIM[Octree::MAX_DEPTH + 1]; @@ -95,6 +96,8 @@ namespace render { using Coord = int16_t; using Coord3 = glm::i16vec3; using Coord4 = glm::i16vec4; + using Coord3f = glm::vec3; + using Coord4f = glm::vec4; static Coord depthBitmask(Depth depth) { return Coord(1 << (MAX_DEPTH - depth)); } @@ -122,7 +125,7 @@ namespace render { Coord3 pos{ 0 }; uint8_t spare{ 0 }; - Depth depth{ 0 }; + Depth depth{ ROOT_DEPTH }; bool operator== (const Location& right) const { return pos == right.pos && depth == right.depth; } @@ -140,13 +143,22 @@ namespace render { // Eval the location best fitting the specified range static Location evalFromRange(const Coord3& minCoord, const Coord3& maxCoord, Depth rangeDepth = MAX_DEPTH); + + // Eval the intersection test against a frustum + enum Intersection { + Outside = 0, + Intersect, + Inside, + }; + static Intersection intersectCell(const Location& cell, const Coord4f frustum[6]); + }; using Locations = Location::vector; // Cell or Brick Indexing using Index = ItemCell; // int32_t static const Index INVALID = -1; - static const Index ROOT = 0; + static const Index ROOT_CELL = 0; using Indices = std::vector; // the cell description @@ -230,6 +242,10 @@ namespace render { return _bricks[index]; } + // Selection and traverse + int select(ItemIDs& selection, const Coord4f frustum[6]) const; + int selectTraverse(Index cellID, ItemIDs& selection, const Coord4f frustum[6]) const; + protected: Index allocateCell(Index parent, const Location& location); Index allocateBrick(); @@ -252,6 +268,7 @@ namespace render { double _invSize{ 1.0 / _size }; glm::vec3 _origin{ -16384.0f }; public: + ItemSpatialTree() {} float getSize() const { return _size; } const glm::vec3& getOrigin() const { return _origin; } @@ -270,8 +287,11 @@ namespace render { auto npos = (pos - getOrigin()); return Coord3(npos * getInvCellWidth(depth)); } + Coord3f evalCoordf(const glm::vec3& pos, Depth depth = Octree::MAX_DEPTH) const { + auto npos = (pos - getOrigin()); + return Coord3f(npos * getInvCellWidth(depth)); + } - // Bound to Location AABox evalBound(const Location& loc) const { float cellWidth = getCellWidth(loc.depth); @@ -289,7 +309,10 @@ namespace render { Index resetItem(Index oldCell, const Location& location, const ItemID& item); - ItemSpatialTree() {} + // Selection and traverse + int select(ItemIDs& selection, const ViewFrustum& frustum) const; + + }; } From 6d4744ed478e9adefd783f0527208dbe655acbea Mon Sep 17 00:00:00 2001 From: samcake Date: Mon, 1 Feb 2016 15:17:32 -0800 Subject: [PATCH 18/49] Trying to see what s going on --- .../render/src/render/DrawSceneOctree.cpp | 32 ++++++++++++++++--- libraries/render/src/render/DrawSceneOctree.h | 29 ++++++++++++++++- libraries/render/src/render/DrawStatus.h | 2 +- 3 files changed, 56 insertions(+), 7 deletions(-) diff --git a/libraries/render/src/render/DrawSceneOctree.cpp b/libraries/render/src/render/DrawSceneOctree.cpp index 53b0e2b87a..e9908b98be 100644 --- a/libraries/render/src/render/DrawSceneOctree.cpp +++ b/libraries/render/src/render/DrawSceneOctree.cpp @@ -15,7 +15,6 @@ #include #include -#include #include #include @@ -50,6 +49,15 @@ const gpu::PipelinePointer DrawSceneOctree::getDrawCellBoundsPipeline() { return _drawCellBoundsPipeline; } + +void DrawSceneOctree::configure(const Config& config) { + _showVisibleCells = config.showVisibleCells; + + _justFrozeFrustum = (config.freezeFrustum && !_freezeFrustum); + _freezeFrustum = config.freezeFrustum; +} + + void DrawSceneOctree::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) { assert(renderContext->args); @@ -80,9 +88,18 @@ void DrawSceneOctree::run(const SceneContextPointer& sceneContext, return; } + auto queryFrustum = *args->_viewFrustum; + if (_freezeFrustum) { + if (_justFrozeFrustum) { + _justFrozeFrustum = false; + _frozenFrutstum = *args->_viewFrustum; + } + queryFrustum = _frozenFrutstum; + } + // Try that: - render::ItemIDs items; - scene->getSpatialTree().select(items, *args->_viewFrustum); + render::ItemIDs itemsCell; + scene->getSpatialTree().select(itemsCell, queryFrustum); // Allright, something to render let's do it @@ -100,14 +117,19 @@ void DrawSceneOctree::run(const SceneContextPointer& sceneContext, // bind the one gpu::Pipeline we need batch.setPipeline(getDrawCellBoundsPipeline()); - const auto& inCells = scene->getSpatialTree()._cells; + /* const auto& inCells = scene->getSpatialTree()._cells; for (const auto& cell: inCells ) { + */ + + for (const auto& cellID : itemsCell) { + auto cell = scene->getSpatialTree().getConcreteCell(cellID); + auto cellLoc = cell.getlocation(); glm::ivec4 cellLocation(cellLoc.pos.x, cellLoc.pos.y, cellLoc.pos.z, cellLoc.depth); if (cell.isBrickEmpty() || !cell.hasBrick()) { - cellLocation.w *= -1; + // cellLocation.w *= -1; } batch._glUniform4iv(_drawCellLocationLoc, 1, ((const int*)(&cellLocation))); diff --git a/libraries/render/src/render/DrawSceneOctree.h b/libraries/render/src/render/DrawSceneOctree.h index 1b9bef12de..7c86e06450 100644 --- a/libraries/render/src/render/DrawSceneOctree.h +++ b/libraries/render/src/render/DrawSceneOctree.h @@ -14,19 +14,46 @@ #include "DrawTask.h" #include "gpu/Batch.h" +#include namespace render { + class DrawSceneOctreeConfig : public Job::Config { + Q_OBJECT + Q_PROPERTY(bool showVisibleCells MEMBER showVisibleCells WRITE setShowVisibleCells) + Q_PROPERTY(bool freezeFrustum MEMBER freezeFrustum WRITE setFreezeFrustum) + public: + DrawSceneOctreeConfig() : Job::Config(true) {} // FIXME FOR debug + + bool showVisibleCells{ true }; // FIXME FOR debug + bool freezeFrustum{ false }; + + public slots: + void setShowVisibleCells(bool enabled) { showVisibleCells = enabled; dirty(); } + void setFreezeFrustum(bool enabled) { freezeFrustum = enabled; dirty(); } + + signals: + void dirty(); + }; + class DrawSceneOctree { int _drawCellLocationLoc; gpu::PipelinePointer _drawCellBoundsPipeline; gpu::BufferPointer _cells; + + bool _showVisibleCells; // initialized by Config + bool _freezeFrustum{ false }; // initialized by Config + bool _justFrozeFrustum{ false }; + ViewFrustum _frozenFrutstum; + public: - using JobModel = Job::Model; + using Config = DrawSceneOctreeConfig; + using JobModel = Job::Model; DrawSceneOctree() {} + void configure(const Config& config); void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext); const gpu::PipelinePointer getDrawCellBoundsPipeline(); diff --git a/libraries/render/src/render/DrawStatus.h b/libraries/render/src/render/DrawStatus.h index 263ebc2548..117e351cbf 100644 --- a/libraries/render/src/render/DrawStatus.h +++ b/libraries/render/src/render/DrawStatus.h @@ -21,7 +21,7 @@ namespace render { Q_PROPERTY(bool showDisplay MEMBER showDisplay WRITE setShowDisplay) Q_PROPERTY(bool showNetwork MEMBER showNetwork WRITE setShowNetwork) public: - DrawStatusConfig() : Job::Config(true) {} // FIXME FOR debug + DrawStatusConfig() : Job::Config(false) {} // FIXME FOR debug void dirtyHelper(); From ee7ca35c0c3e3e6fe9e6533591e6c008c2a0f87b Mon Sep 17 00:00:00 2001 From: samcake Date: Mon, 1 Feb 2016 16:48:23 -0800 Subject: [PATCH 19/49] Adding the selectBranch --- libraries/render/src/render/Octree.cpp | 24 +++++++++++++++++++++--- libraries/render/src/render/Octree.h | 1 + 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/libraries/render/src/render/Octree.cpp b/libraries/render/src/render/Octree.cpp index 536e876e12..cd269c9a50 100644 --- a/libraries/render/src/render/Octree.cpp +++ b/libraries/render/src/render/Octree.cpp @@ -238,7 +238,7 @@ int Octree::select(ItemIDs& selection, const glm::vec4 frustum[6]) const { } int Octree::selectTraverse(Index cellID, ItemIDs& selection, const Coord4f frustum[6]) const { - int numItemsIn = selection.size(); + int numSelectedsIn = selection.size(); auto cell = getConcreteCell(cellID); auto intersection = Octree::Location::intersectCell(cell.getlocation(), frustum); @@ -249,7 +249,7 @@ int Octree::selectTraverse(Index cellID, ItemIDs& selection, const Coord4f frust break; case Octree::Location::Inside: { // traverse all the Cell Branch and collect items in the selection - selection.push_back(cellID); + selectBranch(cellID, selection, frustum); break; } case Octree::Location::Intersect: @@ -269,10 +269,28 @@ int Octree::selectTraverse(Index cellID, ItemIDs& selection, const Coord4f frust } } - return selection.size() - numItemsIn; + return selection.size() - numSelectedsIn; } +int Octree::selectBranch(Index cellID, ItemIDs& selection, const Coord4f frustum[6]) const { + int numSelectedsIn = selection.size(); + auto cell = getConcreteCell(cellID); + + // Collect the items of this cell + selection.push_back(cellID); + + // then traverse further + for (int i = 0; i < NUM_OCTANTS; i++) { + Index subCellID = cell.child((Link)i); + if (subCellID != INVALID) { + selectBranch(subCellID, selection, frustum); + } + } + + return selection.size() - numSelectedsIn; +} + Octree::Location::Intersection Octree::Location::intersectCell(const Location& cell, const Coord4f frustum[6]) { const Coord3f CornerOffsets[8] = { { 0.0, 0.0, 0.0 }, diff --git a/libraries/render/src/render/Octree.h b/libraries/render/src/render/Octree.h index 4f148636b5..ad19cb30a3 100644 --- a/libraries/render/src/render/Octree.h +++ b/libraries/render/src/render/Octree.h @@ -245,6 +245,7 @@ namespace render { // Selection and traverse int select(ItemIDs& selection, const Coord4f frustum[6]) const; int selectTraverse(Index cellID, ItemIDs& selection, const Coord4f frustum[6]) const; + int selectBranch(Index cellID, ItemIDs& selection, const Coord4f frustum[6]) const; protected: Index allocateCell(Index parent, const Location& location); From a29831bdf4c63e9593cafac7782111cd09251aef Mon Sep 17 00:00:00 2001 From: samcake Date: Mon, 1 Feb 2016 18:16:23 -0800 Subject: [PATCH 20/49] Fancy selection of the brick and cells --- .../render/src/render/DrawSceneOctree.cpp | 7 +- libraries/render/src/render/DrawSceneOctree.h | 9 +- libraries/render/src/render/Octree.cpp | 132 ++++++++++-------- libraries/render/src/render/Octree.h | 9 +- 4 files changed, 87 insertions(+), 70 deletions(-) diff --git a/libraries/render/src/render/DrawSceneOctree.cpp b/libraries/render/src/render/DrawSceneOctree.cpp index e9908b98be..7fcfb5766e 100644 --- a/libraries/render/src/render/DrawSceneOctree.cpp +++ b/libraries/render/src/render/DrawSceneOctree.cpp @@ -98,8 +98,9 @@ void DrawSceneOctree::run(const SceneContextPointer& sceneContext, } // Try that: - render::ItemIDs itemsCell; - scene->getSpatialTree().select(itemsCell, queryFrustum); + render::Octree::Indices itemsBrick; + render::Octree::Indices itemsCell; + scene->getSpatialTree().select(itemsBrick, itemsCell, queryFrustum); // Allright, something to render let's do it @@ -129,7 +130,7 @@ void DrawSceneOctree::run(const SceneContextPointer& sceneContext, glm::ivec4 cellLocation(cellLoc.pos.x, cellLoc.pos.y, cellLoc.pos.z, cellLoc.depth); if (cell.isBrickEmpty() || !cell.hasBrick()) { - // cellLocation.w *= -1; + cellLocation.w *= -1; } batch._glUniform4iv(_drawCellLocationLoc, 1, ((const int*)(&cellLocation))); diff --git a/libraries/render/src/render/DrawSceneOctree.h b/libraries/render/src/render/DrawSceneOctree.h index 7c86e06450..f7ceb9fb91 100644 --- a/libraries/render/src/render/DrawSceneOctree.h +++ b/libraries/render/src/render/DrawSceneOctree.h @@ -19,17 +19,18 @@ namespace render { class DrawSceneOctreeConfig : public Job::Config { Q_OBJECT - Q_PROPERTY(bool showVisibleCells MEMBER showVisibleCells WRITE setShowVisibleCells) - Q_PROPERTY(bool freezeFrustum MEMBER freezeFrustum WRITE setFreezeFrustum) public: + Q_PROPERTY(bool showVisibleCells MEMBER showVisibleCells WRITE setShowVisibleCells) + Q_PROPERTY(bool freezeFrustum MEMBER freezeFrustum WRITE setFreezeFrustum) + DrawSceneOctreeConfig() : Job::Config(true) {} // FIXME FOR debug bool showVisibleCells{ true }; // FIXME FOR debug bool freezeFrustum{ false }; public slots: - void setShowVisibleCells(bool enabled) { showVisibleCells = enabled; dirty(); } - void setFreezeFrustum(bool enabled) { freezeFrustum = enabled; dirty(); } + void setShowVisibleCells(bool enabled) { showVisibleCells = enabled; emit dirty(); } + void setFreezeFrustum(bool enabled) { freezeFrustum = enabled; emit dirty(); } signals: void dirty(); diff --git a/libraries/render/src/render/Octree.cpp b/libraries/render/src/render/Octree.cpp index cd269c9a50..f66454c624 100644 --- a/libraries/render/src/render/Octree.cpp +++ b/libraries/render/src/render/Octree.cpp @@ -219,7 +219,7 @@ ItemSpatialTree::Index ItemSpatialTree::resetItem(Index oldCell, const Location& } } -int ItemSpatialTree::select(ItemIDs& selection, const ViewFrustum& frustum) const { +int ItemSpatialTree::select(Indices& selectedBricks, Indices& selectedCells, const ViewFrustum& frustum) const { auto worldPlanes = frustum.getPlanes(); Coord4f planes[6]; for (int i = 0; i < ViewFrustum::NUM_PLANES; i++) { @@ -227,69 +227,16 @@ int ItemSpatialTree::select(ItemIDs& selection, const ViewFrustum& frustum) cons octPlane.setNormalAndPoint(worldPlanes[i].getNormal(), evalCoordf(worldPlanes[i].getPoint(), ROOT_DEPTH)); planes[i] = Coord4f(octPlane.getNormal(), octPlane.getDCoefficient()); } - return Octree::select(selection, planes); + return Octree::select(selectedBricks, selectedCells, planes); } -int Octree::select(ItemIDs& selection, const glm::vec4 frustum[6]) const { +int Octree::select(Indices& selectedBricks, Indices& selectedCells, const glm::vec4 frustum[6]) const { Index cellID = ROOT_CELL; - selectTraverse(cellID, selection, frustum); - return selection.size(); + selectTraverse(cellID, selectedBricks, selectedCells, frustum); + return selectedCells.size(); } -int Octree::selectTraverse(Index cellID, ItemIDs& selection, const Coord4f frustum[6]) const { - int numSelectedsIn = selection.size(); - auto cell = getConcreteCell(cellID); - - auto intersection = Octree::Location::intersectCell(cell.getlocation(), frustum); - switch (intersection) { - case Octree::Location::Outside: - // cell is outside, stop traversing this branch - return 0; - break; - case Octree::Location::Inside: { - // traverse all the Cell Branch and collect items in the selection - selectBranch(cellID, selection, frustum); - break; - } - case Octree::Location::Intersect: - default: { - // Cell is partially in - selection.push_back(cellID); - - // Collect the items of this cell - - // then traverse further - for (int i = 0; i < NUM_OCTANTS; i++) { - Index subCellID = cell.child((Link)i); - if (subCellID != INVALID) { - selectTraverse(subCellID, selection, frustum); - } - } - } - } - - return selection.size() - numSelectedsIn; -} - - -int Octree::selectBranch(Index cellID, ItemIDs& selection, const Coord4f frustum[6]) const { - int numSelectedsIn = selection.size(); - auto cell = getConcreteCell(cellID); - - // Collect the items of this cell - selection.push_back(cellID); - - // then traverse further - for (int i = 0; i < NUM_OCTANTS; i++) { - Index subCellID = cell.child((Link)i); - if (subCellID != INVALID) { - selectBranch(subCellID, selection, frustum); - } - } - - return selection.size() - numSelectedsIn; -} Octree::Location::Intersection Octree::Location::intersectCell(const Location& cell, const Coord4f frustum[6]) { const Coord3f CornerOffsets[8] = { @@ -347,4 +294,71 @@ Octree::Location::Intersection Octree::Location::intersectCell(const Location& c } return Inside; -} \ No newline at end of file +} + +int Octree::selectTraverse(Index cellID, Indices& selectedBricks, Indices& selectedCells, const Coord4f frustum[6]) const { + int numSelectedsIn = selectedCells.size(); + auto cell = getConcreteCell(cellID); + + auto intersection = Octree::Location::intersectCell(cell.getlocation(), frustum); + switch (intersection) { + case Octree::Location::Outside: + // cell is outside, stop traversing this branch + return 0; + break; + case Octree::Location::Inside: { + // traverse all the Cell Branch and collect items in the selection + selectBranch(cellID, selectedBricks, selectedCells, frustum); + break; + } + case Octree::Location::Intersect: + default: { + // Cell is partially in + + // Select within this cell + selectInCell(cellID, selectedBricks, selectedCells, frustum); + + // then traverse deeper + for (int i = 0; i < NUM_OCTANTS; i++) { + Index subCellID = cell.child((Link)i); + if (subCellID != INVALID) { + selectTraverse(subCellID, selectedBricks, selectedCells, frustum); + } + } + } + } + + return selectedCells.size() - numSelectedsIn; +} + + +int Octree::selectBranch(Index cellID, Indices& selectedBricks, Indices& selectedCells, const Coord4f frustum[6]) const { + int numSelectedsIn = selectedCells.size(); + auto cell = getConcreteCell(cellID); + + // Select within this cell + selectInCell(cellID, selectedBricks, selectedCells, frustum); + + // then traverse deeper + for (int i = 0; i < NUM_OCTANTS; i++) { + Index subCellID = cell.child((Link)i); + if (subCellID != INVALID) { + selectBranch(subCellID, selectedBricks, selectedCells, frustum); + } + } + + return selectedCells.size() - numSelectedsIn; +} + +int Octree::selectInCell(Index cellID, Indices& selectedBricks, Indices& selectedCells, const Coord4f frustum[6]) const { + int numSelectedsIn = selectedCells.size(); + auto cell = getConcreteCell(cellID); + selectedCells.push_back(cellID); + + if (!cell.isBrickEmpty()) { + // Collect the items of this cell + selectedBricks.push_back(cell.brick()); + } + + return selectedCells.size() - numSelectedsIn; +} diff --git a/libraries/render/src/render/Octree.h b/libraries/render/src/render/Octree.h index ad19cb30a3..7fc36b9844 100644 --- a/libraries/render/src/render/Octree.h +++ b/libraries/render/src/render/Octree.h @@ -243,9 +243,10 @@ namespace render { } // Selection and traverse - int select(ItemIDs& selection, const Coord4f frustum[6]) const; - int selectTraverse(Index cellID, ItemIDs& selection, const Coord4f frustum[6]) const; - int selectBranch(Index cellID, ItemIDs& selection, const Coord4f frustum[6]) const; + int select(Indices& selectedBricks, Indices& selectedCells, const Coord4f frustum[6]) const; + int selectTraverse(Index cellID, Indices& selectedBricks, Indices& selectedCells, const Coord4f frustum[6]) const; + int selectBranch(Index cellID, Indices& selectedBricks, Indices& selectedCells, const Coord4f frustum[6]) const; + int selectInCell(Index cellID, Indices& selectedBricks, Indices& selectedCells, const Coord4f frustum[6]) const; protected: Index allocateCell(Index parent, const Location& location); @@ -311,7 +312,7 @@ namespace render { Index resetItem(Index oldCell, const Location& location, const ItemID& item); // Selection and traverse - int select(ItemIDs& selection, const ViewFrustum& frustum) const; + int select(Indices& selectedBricks, Indices& selectedCells, const ViewFrustum& frustum) const; }; From cb59cccc0cafdcfb503d1995c66df4cd0d2b343b Mon Sep 17 00:00:00 2001 From: samcake Date: Tue, 2 Feb 2016 18:10:56 -0800 Subject: [PATCH 21/49] Adding 2 lists per brick now, the subcelItems and the regular items, trying to use it, probably buggy --- .../tests/performance/renderableMatrix.js | 2 +- .../render-utils/src/DebugDeferredBuffer.h | 2 +- libraries/render/src/render/CullTask.cpp | 180 ++++++++++++++++++ libraries/render/src/render/CullTask.h | 93 +++++++++ libraries/render/src/render/DrawSceneOctree.h | 2 +- libraries/render/src/render/DrawTask.cpp | 127 ------------ libraries/render/src/render/DrawTask.h | 58 +----- libraries/render/src/render/Item.h | 9 +- libraries/render/src/render/Octree.cpp | 104 ++++++++-- libraries/render/src/render/Octree.h | 13 +- libraries/render/src/render/Scene.cpp | 22 ++- 11 files changed, 394 insertions(+), 218 deletions(-) create mode 100644 libraries/render/src/render/CullTask.cpp create mode 100644 libraries/render/src/render/CullTask.h diff --git a/examples/tests/performance/renderableMatrix.js b/examples/tests/performance/renderableMatrix.js index 46a197a04a..dd0fd6e54d 100644 --- a/examples/tests/performance/renderableMatrix.js +++ b/examples/tests/performance/renderableMatrix.js @@ -14,7 +14,7 @@ var Entities, Script, print, Vec3, MyAvatar, Camera, Quat; // NOTE: to test the full rendering of the specified number of objects (as opposed to // testing LOD filtering), you may want to set LOD to manual maximum visibility. -var LIFETIME = 20; +var LIFETIME = 120; var ROWS_X = 17; var ROWS_Y = 10; var ROWS_Z = 10; diff --git a/libraries/render-utils/src/DebugDeferredBuffer.h b/libraries/render-utils/src/DebugDeferredBuffer.h index ecc2012466..b31985d6a3 100644 --- a/libraries/render-utils/src/DebugDeferredBuffer.h +++ b/libraries/render-utils/src/DebugDeferredBuffer.h @@ -27,7 +27,7 @@ public: void setMode(int newMode); int mode{ 0 }; - glm::vec4 size{ 0.0f, 0.0f, 0.0f, 0.0f }; + glm::vec4 size{ 0.0f, -1.0f, 1.0f, 1.0f }; signals: void dirty(); }; diff --git a/libraries/render/src/render/CullTask.cpp b/libraries/render/src/render/CullTask.cpp new file mode 100644 index 0000000000..9069a453e2 --- /dev/null +++ b/libraries/render/src/render/CullTask.cpp @@ -0,0 +1,180 @@ +// +// CullTask.cpp +// render/src/render +// +// Created by Sam Gateau on 2/2/16. +// Copyright 2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "CullTask.h" + +#include +#include + +#include +#include +#include + +using namespace render; + +void render::cullItems(const RenderContextPointer& renderContext, const CullFunctor& cullFunctor, RenderDetails::Item& details, + const ItemBounds& inItems, ItemBounds& outItems) { + assert(renderContext->args); + assert(renderContext->args->_viewFrustum); + + RenderArgs* args = renderContext->args; + ViewFrustum* frustum = args->_viewFrustum; + + details._considered += inItems.size(); + + // Culling / LOD + for (auto item : inItems) { + if (item.bound.isNull()) { + outItems.emplace_back(item); // One more Item to render + continue; + } + + // TODO: some entity types (like lights) might want to be rendered even + // when they are outside of the view frustum... + bool outOfView; + { + PerformanceTimer perfTimer("boxInFrustum"); + outOfView = frustum->boxInFrustum(item.bound) == ViewFrustum::OUTSIDE; + } + if (!outOfView) { + bool bigEnoughToRender; + { + PerformanceTimer perfTimer("shouldRender"); + bigEnoughToRender = cullFunctor(args, item.bound); + } + if (bigEnoughToRender) { + outItems.emplace_back(item); // One more Item to render + } else { + details._tooSmall++; + } + } else { + details._outOfView++; + } + } + details._rendered += outItems.size(); +} + +struct ItemBoundSort { + float _centerDepth = 0.0f; + float _nearDepth = 0.0f; + float _farDepth = 0.0f; + ItemID _id = 0; + AABox _bounds; + + ItemBoundSort() {} + ItemBoundSort(float centerDepth, float nearDepth, float farDepth, ItemID id, const AABox& bounds) : _centerDepth(centerDepth), _nearDepth(nearDepth), _farDepth(farDepth), _id(id), _bounds(bounds) {} +}; + +struct FrontToBackSort { + bool operator() (const ItemBoundSort& left, const ItemBoundSort& right) { + return (left._centerDepth < right._centerDepth); + } +}; + +struct BackToFrontSort { + bool operator() (const ItemBoundSort& left, const ItemBoundSort& right) { + return (left._centerDepth > right._centerDepth); + } +}; + +void render::depthSortItems(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, bool frontToBack, const ItemBounds& inItems, ItemBounds& outItems) { + assert(renderContext->args); + assert(renderContext->args->_viewFrustum); + + auto& scene = sceneContext->_scene; + RenderArgs* args = renderContext->args; + + + // Allocate and simply copy + outItems.clear(); + outItems.reserve(inItems.size()); + + + // Make a local dataset of the center distance and closest point distance + std::vector itemBoundSorts; + itemBoundSorts.reserve(outItems.size()); + + for (auto itemDetails : inItems) { + auto item = scene->getItem(itemDetails.id); + auto bound = itemDetails.bound; // item.getBound(); + float distance = args->_viewFrustum->distanceToCamera(bound.calcCenter()); + + itemBoundSorts.emplace_back(ItemBoundSort(distance, distance, distance, itemDetails.id, bound)); + } + + // sort against Z + if (frontToBack) { + FrontToBackSort frontToBackSort; + std::sort(itemBoundSorts.begin(), itemBoundSorts.end(), frontToBackSort); + } else { + BackToFrontSort backToFrontSort; + std::sort(itemBoundSorts.begin(), itemBoundSorts.end(), backToFrontSort); + } + + // FInally once sorted result to a list of itemID + for (auto& item : itemBoundSorts) { + outItems.emplace_back(ItemBound(item._id, item._bounds)); + } +} + +void DepthSortItems::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemBounds& inItems, ItemBounds& outItems) { + depthSortItems(sceneContext, renderContext, _frontToBack, inItems, outItems); +} + + +void FetchItems::configure(const Config& config) { + _justFrozeFrustum = (config.freezeFrustum && !_freezeFrustum); + _freezeFrustum = config.freezeFrustum; +} + +void FetchItems::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, ItemBounds& outItems) { + assert(renderContext->args); + assert(renderContext->args->_viewFrustum); + RenderArgs* args = renderContext->args; + auto& scene = sceneContext->_scene; + + outItems.clear(); + + /* const auto& bucket = scene->getMasterBucket(); + const auto& items = bucket.find(_filter); + if (items != bucket.end()) { + outItems.reserve(items->second.size()); + for (auto& id : items->second) { + auto& item = scene->getItem(id); + outItems.emplace_back(ItemBound(id, item.getBound())); + } + } + */ + + auto queryFrustum = *args->_viewFrustum; + if (_freezeFrustum) { + if (_justFrozeFrustum) { + _justFrozeFrustum = false; + _frozenFrutstum = *args->_viewFrustum; + } + queryFrustum = _frozenFrutstum; + } + + // Try that: + ItemIDs fetchedItems; + scene->getSpatialTree().fetch(fetchedItems, _filter, queryFrustum); + + // Now get the bound, and filter individually against the _filter + outItems.reserve(fetchedItems.size()); + for (auto id : fetchedItems) { + auto& item = scene->getItem(id); + if (_filter.test(item.getKey())) { + outItems.emplace_back(ItemBound(id, item.getBound())); + } + } + + std::static_pointer_cast(renderContext->jobConfig)->numItems = (int)outItems.size(); +} diff --git a/libraries/render/src/render/CullTask.h b/libraries/render/src/render/CullTask.h new file mode 100644 index 0000000000..e1f5d3f0a3 --- /dev/null +++ b/libraries/render/src/render/CullTask.h @@ -0,0 +1,93 @@ +// +// CullTask.h +// render/src/render +// +// Created by Sam Gateau on 2/2/16. +// Copyright 2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_render_CullTask_h +#define hifi_render_CullTask_h + +#include "Engine.h" +#include "ViewFrustum.h" + +namespace render { + + using CullFunctor = std::function; + + void cullItems(const RenderContextPointer& renderContext, const CullFunctor& cullFunctor, RenderDetails::Item& details, + const ItemBounds& inItems, ItemBounds& outItems); + void depthSortItems(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, bool frontToBack, const ItemBounds& inItems, ItemBounds& outItems); + + + class FetchItemsConfig : public Job::Config { + Q_OBJECT + Q_PROPERTY(int numItems READ getNumItems) + Q_PROPERTY(bool freezeFrustum MEMBER freezeFrustum WRITE setFreezeFrustum) + public: + int getNumItems() { return numItems; } + + int numItems{ 0 }; + + bool freezeFrustum{ false }; + + public slots: + void setFreezeFrustum(bool enabled) { freezeFrustum = enabled; emit dirty(); } + + signals: + void dirty(); + }; + + class FetchItems { + bool _freezeFrustum{ false }; // initialized by Config + bool _justFrozeFrustum{ false }; + ViewFrustum _frozenFrutstum; + public: + using Config = FetchItemsConfig; + using JobModel = Job::ModelO; + + FetchItems() {} + FetchItems(const ItemFilter& filter) : _filter(filter) {} + + ItemFilter _filter{ ItemFilter::Builder::opaqueShape().withoutLayered() }; + + void configure(const Config& config); + void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, ItemBounds& outItems); + }; + + + template + class CullItems { + public: + using JobModel = Job::ModelIO, ItemBounds, ItemBounds>; + + CullItems(CullFunctor cullFunctor) : _cullFunctor{ cullFunctor } {} + + void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemBounds& inItems, ItemBounds& outItems) { + const auto& args = renderContext->args; + auto& details = args->_details.edit(T); + outItems.clear(); + outItems.reserve(inItems.size()); + render::cullItems(renderContext, _cullFunctor, details, inItems, outItems); + } + + protected: + CullFunctor _cullFunctor; + }; + + class DepthSortItems { + public: + using JobModel = Job::ModelIO; + + bool _frontToBack; + DepthSortItems(bool frontToBack = true) : _frontToBack(frontToBack) {} + + void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemBounds& inItems, ItemBounds& outItems); + }; +} + +#endif // hifi_render_CullTask_h \ No newline at end of file diff --git a/libraries/render/src/render/DrawSceneOctree.h b/libraries/render/src/render/DrawSceneOctree.h index f7ceb9fb91..2137401fa5 100644 --- a/libraries/render/src/render/DrawSceneOctree.h +++ b/libraries/render/src/render/DrawSceneOctree.h @@ -19,9 +19,9 @@ namespace render { class DrawSceneOctreeConfig : public Job::Config { Q_OBJECT - public: Q_PROPERTY(bool showVisibleCells MEMBER showVisibleCells WRITE setShowVisibleCells) Q_PROPERTY(bool freezeFrustum MEMBER freezeFrustum WRITE setFreezeFrustum) + public: DrawSceneOctreeConfig() : Job::Config(true) {} // FIXME FOR debug diff --git a/libraries/render/src/render/DrawTask.cpp b/libraries/render/src/render/DrawTask.cpp index 5cdd661614..f122b36dec 100755 --- a/libraries/render/src/render/DrawTask.cpp +++ b/libraries/render/src/render/DrawTask.cpp @@ -20,111 +20,6 @@ using namespace render; -void render::cullItems(const RenderContextPointer& renderContext, const CullFunctor& cullFunctor, RenderDetails::Item& details, - const ItemBounds& inItems, ItemBounds& outItems) { - assert(renderContext->args); - assert(renderContext->args->_viewFrustum); - - RenderArgs* args = renderContext->args; - ViewFrustum* frustum = args->_viewFrustum; - - details._considered += inItems.size(); - - // Culling / LOD - for (auto item : inItems) { - if (item.bound.isNull()) { - outItems.emplace_back(item); // One more Item to render - continue; - } - - // TODO: some entity types (like lights) might want to be rendered even - // when they are outside of the view frustum... - bool outOfView; - { - PerformanceTimer perfTimer("boxInFrustum"); - outOfView = frustum->boxInFrustum(item.bound) == ViewFrustum::OUTSIDE; - } - if (!outOfView) { - bool bigEnoughToRender; - { - PerformanceTimer perfTimer("shouldRender"); - bigEnoughToRender = cullFunctor(args, item.bound); - } - if (bigEnoughToRender) { - outItems.emplace_back(item); // One more Item to render - } else { - details._tooSmall++; - } - } else { - details._outOfView++; - } - } - details._rendered += outItems.size(); -} - -struct ItemBoundSort { - float _centerDepth = 0.0f; - float _nearDepth = 0.0f; - float _farDepth = 0.0f; - ItemID _id = 0; - AABox _bounds; - - ItemBoundSort() {} - ItemBoundSort(float centerDepth, float nearDepth, float farDepth, ItemID id, const AABox& bounds) : _centerDepth(centerDepth), _nearDepth(nearDepth), _farDepth(farDepth), _id(id), _bounds(bounds) {} -}; - -struct FrontToBackSort { - bool operator() (const ItemBoundSort& left, const ItemBoundSort& right) { - return (left._centerDepth < right._centerDepth); - } -}; - -struct BackToFrontSort { - bool operator() (const ItemBoundSort& left, const ItemBoundSort& right) { - return (left._centerDepth > right._centerDepth); - } -}; - -void render::depthSortItems(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, bool frontToBack, const ItemBounds& inItems, ItemBounds& outItems) { - assert(renderContext->args); - assert(renderContext->args->_viewFrustum); - - auto& scene = sceneContext->_scene; - RenderArgs* args = renderContext->args; - - - // Allocate and simply copy - outItems.clear(); - outItems.reserve(inItems.size()); - - - // Make a local dataset of the center distance and closest point distance - std::vector itemBoundSorts; - itemBoundSorts.reserve(outItems.size()); - - for (auto itemDetails : inItems) { - auto item = scene->getItem(itemDetails.id); - auto bound = itemDetails.bound; // item.getBound(); - float distance = args->_viewFrustum->distanceToCamera(bound.calcCenter()); - - itemBoundSorts.emplace_back(ItemBoundSort(distance, distance, distance, itemDetails.id, bound)); - } - - // sort against Z - if (frontToBack) { - FrontToBackSort frontToBackSort; - std::sort(itemBoundSorts.begin(), itemBoundSorts.end(), frontToBackSort); - } else { - BackToFrontSort backToFrontSort; - std::sort(itemBoundSorts.begin(), itemBoundSorts.end(), backToFrontSort); - } - - // FInally once sorted result to a list of itemID - for (auto& item : itemBoundSorts) { - outItems.emplace_back(ItemBound(item._id, item._bounds)); - } -} - void render::renderItems(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemBounds& inItems) { auto& scene = sceneContext->_scene; RenderArgs* args = renderContext->args; @@ -165,28 +60,6 @@ void render::renderShapes(const SceneContextPointer& sceneContext, const RenderC } } -void FetchItems::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, ItemBounds& outItems) { - auto& scene = sceneContext->_scene; - - outItems.clear(); - - const auto& bucket = scene->getMasterBucket(); - const auto& items = bucket.find(_filter); - if (items != bucket.end()) { - outItems.reserve(items->second.size()); - for (auto& id : items->second) { - auto& item = scene->getItem(id); - outItems.emplace_back(ItemBound(id, item.getBound())); - } - } - - std::static_pointer_cast(renderContext->jobConfig)->numItems = (int)outItems.size(); -} - -void DepthSortItems::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemBounds& inItems, ItemBounds& outItems) { - depthSortItems(sceneContext, renderContext, _frontToBack, inItems, outItems); -} - void DrawLight::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) { assert(renderContext->args); assert(renderContext->args->_viewFrustum); diff --git a/libraries/render/src/render/DrawTask.h b/libraries/render/src/render/DrawTask.h index 42efe45329..b647d77211 100755 --- a/libraries/render/src/render/DrawTask.h +++ b/libraries/render/src/render/DrawTask.h @@ -13,72 +13,16 @@ #define hifi_render_DrawTask_h #include "Engine.h" +#include "CullTask.h" #include "ShapePipeline.h" #include "gpu/Batch.h" namespace render { -using CullFunctor = std::function; - -void cullItems(const RenderContextPointer& renderContext, const CullFunctor& cullFunctor, RenderDetails::Item& details, - const ItemBounds& inItems, ItemBounds& outItems); -void depthSortItems(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, bool frontToBack, const ItemBounds& inItems, ItemBounds& outItems); void renderItems(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemBounds& inItems); void renderShapes(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ShapePlumberPointer& shapeContext, const ItemBounds& inItems, int maxDrawnItems = -1); -class FetchItemsConfig : public Job::Config { - Q_OBJECT - Q_PROPERTY(int numItems READ getNumItems) -public: - int getNumItems() { return numItems; } - - int numItems{ 0 }; -}; - -class FetchItems { -public: - using Config = FetchItemsConfig; - using JobModel = Job::ModelO; - - FetchItems() {} - FetchItems(const ItemFilter& filter) : _filter(filter) {} - - ItemFilter _filter{ ItemFilter::Builder::opaqueShape().withoutLayered() }; - - void configure(const Config& config) {} - void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, ItemBounds& outItems); -}; - -template -class CullItems { -public: - using JobModel = Job::ModelIO, ItemBounds, ItemBounds>; - - CullItems(CullFunctor cullFunctor) : _cullFunctor{ cullFunctor } {} - - void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemBounds& inItems, ItemBounds& outItems) { - const auto& args = renderContext->args; - auto& details = args->_details.edit(T); - outItems.clear(); - outItems.reserve(inItems.size()); - render::cullItems(renderContext, _cullFunctor, details, inItems, outItems); - } - -protected: - CullFunctor _cullFunctor; -}; - -class DepthSortItems { -public: - using JobModel = Job::ModelIO; - - bool _frontToBack; - DepthSortItems(bool frontToBack = true) : _frontToBack(frontToBack) {} - - void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemBounds& inItems, ItemBounds& outItems); -}; - class DrawLight { public: using JobModel = Job::Model; diff --git a/libraries/render/src/render/Item.h b/libraries/render/src/render/Item.h index 7b164a91eb..75b41a3262 100644 --- a/libraries/render/src/render/Item.h +++ b/libraries/render/src/render/Item.h @@ -47,6 +47,8 @@ public: PICKABLE, // Item can be picked/selected LAYERED, // Item belongs to one of the layers different from the default layer + SMALLER, + NUM_FLAGS, // Not a valid flag }; typedef std::bitset Flags; @@ -107,6 +109,11 @@ public: bool isPickable() const { return _flags[PICKABLE]; } bool isLayered() const { return _flags[LAYERED]; } + + // Probably not public, flags used by the scene + bool isSmall() const { return _flags[SMALLER]; } + void setSmaller(bool smaller) { (smaller ? _flags.set(SMALLER) : _flags.reset(SMALLER)); } + }; inline QDebug operator<<(QDebug debug, const ItemKey& itemKey) { @@ -294,7 +301,7 @@ public: // Main scene / item managment interface Reset/Update/Kill void resetPayload(const PayloadPointer& payload); - void resetCell(ItemCell cell) { _cell = cell; } + void resetCell(ItemCell cell, bool _small) { _cell = cell; _key.setSmaller(_small); } void update(const UpdateFunctorPointer& updateFunctor) { _payload->update(updateFunctor); } // Communicate update to the payload void kill() { _payload.reset(); _key._flags.reset(); _cell = INVALID_CELL; } // Kill means forget the payload and key and cell diff --git a/libraries/render/src/render/Octree.cpp b/libraries/render/src/render/Octree.cpp index f66454c624..c8e4301e2e 100644 --- a/libraries/render/src/render/Octree.cpp +++ b/libraries/render/src/render/Octree.cpp @@ -166,24 +166,44 @@ Octree::Locations ItemSpatialTree::evalLocations(const ItemBounds& bounds) const return locations; } -ItemSpatialTree::Index ItemSpatialTree::insertItem(Index cellIdx, const ItemID& item) { +ItemSpatialTree::Index ItemSpatialTree::insertItem(Index cellIdx, const ItemKey& key, const ItemID& item) { // Add the item to the brick (and a brick if needed) auto brickID = accessCellBrick(cellIdx, [&](Cell& cell, Brick& brick, Octree::Index cellID) { - brick.items.push_back(item); + if (key.isSmall()) { + brick.items.push_back(item); + } else { + brick.subcellItems.push_back(item); + } cell.signalBrickFilled(); }, true); return cellIdx; } -bool ItemSpatialTree::removeItem(Index cellIdx, const ItemID& item) { +bool ItemSpatialTree::updateItem(Index cellIdx, const ItemKey& oldKey, const ItemKey& key, const ItemID& item) { + auto success = false; + + auto brickID = accessCellBrick(cellIdx, [&](Cell& cell, Brick& brick, Octree::Index cellID) { + auto& itemIn = (key.isSmall() ? brick.subcellItems : brick.items); + auto& itemOut = (oldKey.isSmall() ? brick.subcellItems : brick.items); + + itemIn.push_back(item); + itemOut.erase(std::find(itemOut.begin(), itemOut.end(), item)); + }, false); // do not create brick! + + return success; +} + +bool ItemSpatialTree::removeItem(Index cellIdx, const ItemKey& key, const ItemID& item) { auto success = false; // Access the brick at the cell (without createing new ones) accessCellBrick(cellIdx, [&](Cell& cell, Brick& brick, Octree::Index brickID) { // remove the item from the list - brick.items.erase(std::find(brick.items.begin(), brick.items.end(), item)); - if (brick.items.empty()) { + auto& itemList = (key.isSmall() ? brick.subcellItems : brick.items); + + itemList.erase(std::find(itemList.begin(), itemList.end(), item)); + if (brick.items.empty() && brick.subcellItems.empty()) { cell.signalBrickEmpty(); } success = true; @@ -191,7 +211,7 @@ bool ItemSpatialTree::removeItem(Index cellIdx, const ItemID& item) { return success; } - +/* ItemSpatialTree::Index ItemSpatialTree::resetItem(Index oldCell, const Location& location, const ItemID& item) { auto newCell = indexCell(location); @@ -217,17 +237,46 @@ ItemSpatialTree::Index ItemSpatialTree::resetItem(Index oldCell, const Location& return newCell; } -} +}*/ -int ItemSpatialTree::select(Indices& selectedBricks, Indices& selectedCells, const ViewFrustum& frustum) const { - auto worldPlanes = frustum.getPlanes(); - Coord4f planes[6]; - for (int i = 0; i < ViewFrustum::NUM_PLANES; i++) { - ::Plane octPlane; - octPlane.setNormalAndPoint(worldPlanes[i].getNormal(), evalCoordf(worldPlanes[i].getPoint(), ROOT_DEPTH)); - planes[i] = Coord4f(octPlane.getNormal(), octPlane.getDCoefficient()); +ItemSpatialTree::Index ItemSpatialTree::resetItem(Index oldCell, const ItemKey& oldKey, const AABox& bound, const ItemID& item, ItemKey& newKey) { + auto minCoordf = evalCoordf(bound.getMinimumPoint()); + auto maxCoordf = evalCoordf(bound.getMaximumPoint()); + Coord3 minCoord(minCoordf); + Coord3 maxCoord(maxCoordf); + auto location = Location::evalFromRange(minCoord, maxCoord); + + // Compare range size vs cell location size and tag itemKey accordingly + auto rangeSizef = maxCoordf - minCoordf; + const float SQRT_OF_3 = 1.73205; + float cellDiagonalSquare = 3 * (float)getDepthDimension(location.depth); + bool subcellItem = glm::dot(rangeSizef, rangeSizef) < cellDiagonalSquare; + newKey.setSmaller(subcellItem); + + auto newCell = indexCell(location); + + // Early exit if nothing changed + if (newCell == oldCell) { + if (newKey._flags != oldKey._flags) { + updateItem(newCell, oldKey, newKey, item); + } + return newCell; + } + // do we know about this item ? + else if (oldCell == Item::INVALID_CELL) { + insertItem(newCell, newKey, item); + return newCell; + } + // A true update of cell is required + else { + // Add the item to the brick (and a brick if needed) + insertItem(newCell, newKey, item); + + // And remove it from the previous one + removeItem(oldCell, oldKey, item); + + return newCell; } - return Octree::select(selectedBricks, selectedCells, planes); } int Octree::select(Indices& selectedBricks, Indices& selectedCells, const glm::vec4 frustum[6]) const { @@ -362,3 +411,28 @@ int Octree::selectInCell(Index cellID, Indices& selectedBricks, Indices& selecte return selectedCells.size() - numSelectedsIn; } + + +int ItemSpatialTree::select(Indices& selectedBricks, Indices& selectedCells, const ViewFrustum& frustum) const { + auto worldPlanes = frustum.getPlanes(); + Coord4f planes[6]; + for (int i = 0; i < ViewFrustum::NUM_PLANES; i++) { + ::Plane octPlane; + octPlane.setNormalAndPoint(worldPlanes[i].getNormal(), evalCoordf(worldPlanes[i].getPoint(), ROOT_DEPTH)); + planes[i] = Coord4f(octPlane.getNormal(), octPlane.getDCoefficient()); + } + return Octree::select(selectedBricks, selectedCells, planes); +} + +int ItemSpatialTree::fetch(ItemIDs& fetchedItems, const ItemFilter& filter, const ViewFrustum& frustum) const { + Indices bricks; + Indices cells; + select(bricks, cells, frustum); + + for (auto brickId : bricks) { + auto& brickItems = getConcreteBrick(brickId).items; + fetchedItems.insert(fetchedItems.end(), brickItems.begin(), brickItems.end()); + } + + return bricks.size(); +} diff --git a/libraries/render/src/render/Octree.h b/libraries/render/src/render/Octree.h index 7fc36b9844..dd663bb232 100644 --- a/libraries/render/src/render/Octree.h +++ b/libraries/render/src/render/Octree.h @@ -29,6 +29,7 @@ namespace render { class Brick { public: std::vector items; + std::vector subcellItems; }; class Octree { @@ -287,7 +288,7 @@ namespace render { Coord3 evalCoord(const glm::vec3& pos, Depth depth = Octree::MAX_DEPTH) const { auto npos = (pos - getOrigin()); - return Coord3(npos * getInvCellWidth(depth)); + return Coord3(npos * getInvCellWidth(depth)); // Truncate fractional part } Coord3f evalCoordf(const glm::vec3& pos, Depth depth = Octree::MAX_DEPTH) const { auto npos = (pos - getOrigin()); @@ -306,15 +307,15 @@ namespace render { // Managing itemsInserting items in cells // Cells need to have been allocated first calling indexCell - Index insertItem(Index cellIdx, const ItemID& item); - bool removeItem(Index cellIdx, const ItemID& item); + Index insertItem(Index cellIdx, const ItemKey& key, const ItemID& item); + bool updateItem(Index cellIdx, const ItemKey& oldKey, const ItemKey& key, const ItemID& item); + bool removeItem(Index cellIdx, const ItemKey& key, const ItemID& item); - Index resetItem(Index oldCell, const Location& location, const ItemID& item); + Index resetItem(Index oldCell, const ItemKey& oldKey, const AABox& bound, const ItemID& item, ItemKey& newKey); // Selection and traverse int select(Indices& selectedBricks, Indices& selectedCells, const ViewFrustum& frustum) const; - - + int fetch(ItemIDs& fetchedItems, const ItemFilter& filter, const ViewFrustum& frustum) const; }; } diff --git a/libraries/render/src/render/Scene.cpp b/libraries/render/src/render/Scene.cpp index b05f145a49..43187feb66 100644 --- a/libraries/render/src/render/Scene.cpp +++ b/libraries/render/src/render/Scene.cpp @@ -139,14 +139,15 @@ void Scene::resetItems(const ItemIDs& ids, Payloads& payloads) { // Reset the item with a new payload item.resetPayload(*resetPayload); + auto newKey = item.getKey(); + // Reset the item in the Bucket map - _masterBucketMap.reset(resetID, oldKey, item.getKey()); + _masterBucketMap.reset(resetID, oldKey, newKey); // Reset the item in the spatial tree - auto newCellLocation = _masterSpatialTree.evalLocation(item.getBound()); - auto newCell = _masterSpatialTree.resetItem(oldCell, newCellLocation, resetID); - item.resetCell(newCell); + auto newCell = _masterSpatialTree.resetItem(oldCell, oldKey, item.getBound(), resetID, newKey); + item.resetCell(newCell, newKey.isSmall()); // next loop resetPayload++; @@ -157,12 +158,14 @@ void Scene::removeItems(const ItemIDs& ids) { for (auto removedID :ids) { // Access the true item auto& item = _items[removedID]; + auto oldCell = item.getCell(); + auto oldKey = item.getKey(); // Remove from Bucket map _masterBucketMap.erase(removedID, item.getKey()); // Remove from spatial tree - _masterSpatialTree.removeItem(item.getCell(), removedID); + _masterSpatialTree.removeItem(oldCell, oldKey, removedID); // Kill it item.kill(); @@ -176,15 +179,16 @@ void Scene::updateItems(const ItemIDs& ids, UpdateFunctors& functors) { // Access the true item auto& item = _items[updateID]; auto oldCell = item.getCell(); + auto oldKey = item.getKey(); // Update it _items[updateID].update((*updateFunctor)); + auto newKey = item.getKey(); + // Update the citem in the spatial tree if needed - // THis could be avoided if we - auto newCellLocation = _masterSpatialTree.evalLocation(item.getBound()); - auto newCell = _masterSpatialTree.resetItem(oldCell, newCellLocation, updateID); - item.resetCell(newCell); + auto newCell = _masterSpatialTree.resetItem(oldCell, oldKey, item.getBound(), updateID, newKey); + item.resetCell(newCell, newKey.isSmall()); // next loop updateFunctor++; From 0478450205853183afdee39c766d43c9e67c4cb2 Mon Sep 17 00:00:00 2001 From: samcake Date: Wed, 3 Feb 2016 18:38:57 -0800 Subject: [PATCH 22/49] Refining the culling test and defining the 2 new jobs FetchSPatialTree and CullSpatialSelection --- .../render-utils/src/RenderDeferredTask.cpp | 15 +- libraries/render/src/render/CullTask.cpp | 131 ++++++++++++-- libraries/render/src/render/CullTask.h | 78 +++++++-- .../render/src/render/DrawSceneOctree.cpp | 24 ++- libraries/render/src/render/Item.h | 3 + libraries/render/src/render/Octree.cpp | 160 ++++++++++-------- libraries/render/src/render/Octree.h | 87 +++++++++- 7 files changed, 388 insertions(+), 110 deletions(-) diff --git a/libraries/render-utils/src/RenderDeferredTask.cpp b/libraries/render-utils/src/RenderDeferredTask.cpp index e877991a9a..0578fc78b5 100755 --- a/libraries/render-utils/src/RenderDeferredTask.cpp +++ b/libraries/render-utils/src/RenderDeferredTask.cpp @@ -52,7 +52,8 @@ RenderDeferredTask::RenderDeferredTask(CullFunctor cullFunctor) { ShapePlumberPointer shapePlumber = std::make_shared(); initDeferredPipelines(*shapePlumber); - // CPU: Fetch the renderOpaques + + /* // CPU: Fetch the renderOpaques const auto fetchedOpaques = addJob("FetchOpaque"); const auto culledOpaques = addJob>("CullOpaque", fetchedOpaques, cullFunctor); const auto opaques = addJob("DepthSortOpaque", culledOpaques); @@ -63,6 +64,18 @@ RenderDeferredTask::RenderDeferredTask(CullFunctor cullFunctor) { const auto culledTransparents = addJob>("CullTransparent", fetchedTransparents, cullFunctor); const auto transparents = addJob("DepthSortTransparent", culledTransparents, DepthSortItems(false)); + */ + // CPU: Fetch the renderOpaques + const auto opaqueSelection = addJob("FetchOpaque"); + const auto culledOpaques = addJob("CullOpaque", opaqueSelection, cullFunctor); + const auto opaques = addJob("DepthSortOpaque", culledOpaques); + + // CPU only, create the list of renderedTransparents items + const auto fetchedTransparents = addJob("FetchTransparent", FetchItems( + ItemFilter::Builder::transparentShape().withoutLayered())); + const auto culledTransparents = + addJob>("CullTransparent", fetchedTransparents, cullFunctor); + const auto transparents = addJob("DepthSortTransparent", culledTransparents, DepthSortItems(false)); // GPU Jobs: Start preparing the deferred and lighting buffer addJob("PrepareDeferred"); diff --git a/libraries/render/src/render/CullTask.cpp b/libraries/render/src/render/CullTask.cpp index 9069a453e2..332af9b321 100644 --- a/libraries/render/src/render/CullTask.cpp +++ b/libraries/render/src/render/CullTask.cpp @@ -131,8 +131,6 @@ void DepthSortItems::run(const SceneContextPointer& sceneContext, const RenderCo void FetchItems::configure(const Config& config) { - _justFrozeFrustum = (config.freezeFrustum && !_freezeFrustum); - _freezeFrustum = config.freezeFrustum; } void FetchItems::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, ItemBounds& outItems) { @@ -143,7 +141,7 @@ void FetchItems::run(const SceneContextPointer& sceneContext, const RenderContex outItems.clear(); - /* const auto& bucket = scene->getMasterBucket(); + const auto& bucket = scene->getMasterBucket(); const auto& items = bucket.find(_filter); if (items != bucket.end()) { outItems.reserve(items->second.size()); @@ -152,8 +150,27 @@ void FetchItems::run(const SceneContextPointer& sceneContext, const RenderContex outItems.emplace_back(ItemBound(id, item.getBound())); } } - */ + std::static_pointer_cast(renderContext->jobConfig)->numItems = (int)outItems.size(); +} + + + +void FetchSpatialTree::configure(const Config& config) { + _justFrozeFrustum = (config.freezeFrustum && !_freezeFrustum); + _freezeFrustum = config.freezeFrustum; +} + +void FetchSpatialTree::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, ItemSpatialTree::ItemSelection& outSelection) { + assert(renderContext->args); + assert(renderContext->args->_viewFrustum); + RenderArgs* args = renderContext->args; + auto& scene = sceneContext->_scene; + + // start fresh + outSelection.clear(); + + // Eventually use a frozen frustum auto queryFrustum = *args->_viewFrustum; if (_freezeFrustum) { if (_justFrozeFrustum) { @@ -163,18 +180,110 @@ void FetchItems::run(const SceneContextPointer& sceneContext, const RenderContex queryFrustum = _frozenFrutstum; } - // Try that: - ItemIDs fetchedItems; - scene->getSpatialTree().fetch(fetchedItems, _filter, queryFrustum); + // Octree selection! + scene->getSpatialTree().selectCellItems(outSelection, _filter, queryFrustum); +} - // Now get the bound, and filter individually against the _filter - outItems.reserve(fetchedItems.size()); - for (auto id : fetchedItems) { +void CullSpatialSelection::configure(const Config& config) { +} + +void CullSpatialSelection::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemSpatialTree::ItemSelection& inSelection, ItemBounds& outItems) { + assert(renderContext->args); + assert(renderContext->args->_viewFrustum); + RenderArgs* args = renderContext->args; + auto& scene = sceneContext->_scene; + ViewFrustum* frustum = args->_viewFrustum; + + auto& details = args->_details.edit(_detailType); + details._considered += inSelection.numItems(); + + // Culling Frustum / solidAngle test helper class + struct Test { + CullFunctor _functor; + RenderArgs* _args; + RenderDetails::Item& _renderDetails; + + Test(CullFunctor& functor, RenderArgs* pargs, RenderDetails::Item& renderDetails) : + _functor(functor), + _args(pargs), + _renderDetails(renderDetails) + {} + + bool frustumTest(const AABox& bound) { + PerformanceTimer perfTimer("frustumItemTest"); + if (_args->_viewFrustum->boxInFrustum(bound) == ViewFrustum::OUTSIDE) { + _renderDetails._outOfView++; + return false; + } + return true; + } + + bool solidAngleTest(const AABox& bound) { + PerformanceTimer perfTimer("solidAngleItemTest"); + if (!_functor(_args, bound)) { + _renderDetails._tooSmall++; + return false; + } + return true; + } + }; + Test test(_cullFunctor, args, details); + + // Now we have a selection of items to render + + outItems.clear(); + outItems.reserve(inSelection.numItems()); + + // Now get the bound, and + // filter individually against the _filter + // visibility cull if partially selected ( octree cell contianing it was partial) + // distance cull if was a subcell item ( octree cell is way bigger than the item bound itself, so now need to test per item) + + // inside & fit items: easy, just filter + for (auto id : inSelection.insideItems) { auto& item = scene->getItem(id); if (_filter.test(item.getKey())) { - outItems.emplace_back(ItemBound(id, item.getBound())); + ItemBound itemBound(id, item.getBound()); + outItems.emplace_back(itemBound); } } + // inside & subcell items: filter & distance cull + for (auto id : inSelection.insideSubcellItems) { + auto& item = scene->getItem(id); + if (_filter.test(item.getKey())) { + ItemBound itemBound(id, item.getBound()); + if (test.solidAngleTest(itemBound.bound)) { + outItems.emplace_back(itemBound); + } + } + } + + // partial & fit items: filter & frustum cull + for (auto id : inSelection.partialItems) { + auto& item = scene->getItem(id); + if (_filter.test(item.getKey())) { + ItemBound itemBound(id, item.getBound()); + if (test.frustumTest(itemBound.bound)) { + outItems.emplace_back(itemBound); + } + } + } + + // partial & subcell items:: filter & frutum cull & solidangle cull + for (auto id : inSelection.partialSubcellItems) { + auto& item = scene->getItem(id); + if (_filter.test(item.getKey())) { + ItemBound itemBound(id, item.getBound()); + if (test.frustumTest(itemBound.bound)) { + if (test.solidAngleTest(itemBound.bound)) { + outItems.emplace_back(itemBound); + } + } + } + } + + details._rendered += outItems.size(); + std::static_pointer_cast(renderContext->jobConfig)->numItems = (int)outItems.size(); } diff --git a/libraries/render/src/render/CullTask.h b/libraries/render/src/render/CullTask.h index e1f5d3f0a3..daa6832e0a 100644 --- a/libraries/render/src/render/CullTask.h +++ b/libraries/render/src/render/CullTask.h @@ -23,29 +23,16 @@ namespace render { const ItemBounds& inItems, ItemBounds& outItems); void depthSortItems(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, bool frontToBack, const ItemBounds& inItems, ItemBounds& outItems); - class FetchItemsConfig : public Job::Config { Q_OBJECT Q_PROPERTY(int numItems READ getNumItems) - Q_PROPERTY(bool freezeFrustum MEMBER freezeFrustum WRITE setFreezeFrustum) public: int getNumItems() { return numItems; } int numItems{ 0 }; - - bool freezeFrustum{ false }; - - public slots: - void setFreezeFrustum(bool enabled) { freezeFrustum = enabled; emit dirty(); } - - signals: - void dirty(); }; class FetchItems { - bool _freezeFrustum{ false }; // initialized by Config - bool _justFrozeFrustum{ false }; - ViewFrustum _frozenFrutstum; public: using Config = FetchItemsConfig; using JobModel = Job::ModelO; @@ -79,6 +66,69 @@ namespace render { CullFunctor _cullFunctor; }; + + class FetchSpatialTreeConfig : public Job::Config { + Q_OBJECT + Q_PROPERTY(int numItems READ getNumItems) + Q_PROPERTY(bool freezeFrustum MEMBER freezeFrustum WRITE setFreezeFrustum) + public: + int numItems{ 0 }; + int getNumItems() { return numItems; } + + bool freezeFrustum{ false }; + public slots: + void setFreezeFrustum(bool enabled) { freezeFrustum = enabled; emit dirty(); } + + signals: + void dirty(); + }; + + class FetchSpatialTree { + bool _freezeFrustum{ false }; // initialized by Config + bool _justFrozeFrustum{ false }; + ViewFrustum _frozenFrutstum; + public: + using Config = FetchSpatialTreeConfig; + using JobModel = Job::ModelO; + + FetchSpatialTree() {} + FetchSpatialTree(const ItemFilter& filter) : _filter(filter) {} + + ItemFilter _filter{ ItemFilter::Builder::opaqueShape().withoutLayered() }; + + void configure(const Config& config); + void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, ItemSpatialTree::ItemSelection& outSelection); + }; + + class CullSpatialSelectionConfig : public Job::Config { + Q_OBJECT + Q_PROPERTY(int numItems READ getNumItems) + public: + int numItems{ 0 }; + int getNumItems() { return numItems; } + }; + + class CullSpatialSelection { + public: + using Config = CullSpatialSelectionConfig; + using JobModel = Job::ModelIO; + + CullSpatialSelection(CullFunctor cullFunctor, RenderDetails::Type type, const ItemFilter& filter) : + _cullFunctor{ cullFunctor }, + _detailType(type), + _filter(filter) {} + + CullSpatialSelection(CullFunctor cullFunctor) : + _cullFunctor{ cullFunctor } {} + + CullFunctor _cullFunctor; + RenderDetails::Type _detailType{ RenderDetails::OPAQUE_ITEM }; + ItemFilter _filter{ ItemFilter::Builder::opaqueShape().withoutLayered() }; + + void configure(const Config& config); + void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemSpatialTree::ItemSelection& outSelection, ItemBounds& outItems); + }; + class DepthSortItems { public: using JobModel = Job::ModelIO; @@ -90,4 +140,4 @@ namespace render { }; } -#endif // hifi_render_CullTask_h \ No newline at end of file +#endif // hifi_render_CullTask_h; \ No newline at end of file diff --git a/libraries/render/src/render/DrawSceneOctree.cpp b/libraries/render/src/render/DrawSceneOctree.cpp index 7fcfb5766e..bfe052c539 100644 --- a/libraries/render/src/render/DrawSceneOctree.cpp +++ b/libraries/render/src/render/DrawSceneOctree.cpp @@ -98,9 +98,8 @@ void DrawSceneOctree::run(const SceneContextPointer& sceneContext, } // Try that: - render::Octree::Indices itemsBrick; - render::Octree::Indices itemsCell; - scene->getSpatialTree().select(itemsBrick, itemsCell, queryFrustum); + Octree::CellSelection selection; + scene->getSpatialTree().selectCells(selection, queryFrustum); // Allright, something to render let's do it @@ -118,12 +117,21 @@ void DrawSceneOctree::run(const SceneContextPointer& sceneContext, // bind the one gpu::Pipeline we need batch.setPipeline(getDrawCellBoundsPipeline()); - /* const auto& inCells = scene->getSpatialTree()._cells; + for (const auto& cellID : selection.insideCells) { + auto cell = scene->getSpatialTree().getConcreteCell(cellID); - for (const auto& cell: inCells ) { - */ - - for (const auto& cellID : itemsCell) { + auto cellLoc = cell.getlocation(); + + glm::ivec4 cellLocation(cellLoc.pos.x, cellLoc.pos.y, cellLoc.pos.z, cellLoc.depth); + if (cell.isBrickEmpty() || !cell.hasBrick()) { + cellLocation.w *= -1; + } + + batch._glUniform4iv(_drawCellLocationLoc, 1, ((const int*)(&cellLocation))); + + batch.draw(gpu::LINES, 24, 0); + } + for (const auto& cellID : selection.partialCells) { auto cell = scene->getSpatialTree().getConcreteCell(cellID); auto cellLoc = cell.getlocation(); diff --git a/libraries/render/src/render/Item.h b/libraries/render/src/render/Item.h index 75b41a3262..0503d6845c 100644 --- a/libraries/render/src/render/Item.h +++ b/libraries/render/src/render/Item.h @@ -59,6 +59,9 @@ public: ItemKey() : _flags(0) {} ItemKey(const Flags& flags) : _flags(flags) {} + bool operator== (const ItemKey& rhs) const { return _flags == rhs._flags; } + bool operator!= (const ItemKey& rhs) const { return _flags != rhs._flags; } + class Builder { friend class ItemKey; Flags _flags{ 0 }; diff --git a/libraries/render/src/render/Octree.cpp b/libraries/render/src/render/Octree.cpp index c8e4301e2e..e7e7c1155a 100644 --- a/libraries/render/src/render/Octree.cpp +++ b/libraries/render/src/render/Octree.cpp @@ -34,6 +34,24 @@ const double Octree::INV_DEPTH_DIM[] = { 1.0 / 16384.0, 1.0 / 32768.0 }; +const float Octree::COORD_SUBCELL_WIDTH[] = { // 2 ^ MAX_DEPTH / 2 ^ (depth + 1) + 16384.0f, + 8192.0f, + 4096.0f, + 2048.0f, + 1024.0f, + 512.0f, + 256.0f, + 128.0f, + 64.0f, + 32.0f, + 16.0f, + 8.0f, + 4.0f, + 2.0f, + 1.0f, + 0.5f }; + Octree::Location::vector Octree::Location::pathTo(const Location& dest) { Location current{ dest }; @@ -169,11 +187,10 @@ Octree::Locations ItemSpatialTree::evalLocations(const ItemBounds& bounds) const ItemSpatialTree::Index ItemSpatialTree::insertItem(Index cellIdx, const ItemKey& key, const ItemID& item) { // Add the item to the brick (and a brick if needed) auto brickID = accessCellBrick(cellIdx, [&](Cell& cell, Brick& brick, Octree::Index cellID) { - if (key.isSmall()) { - brick.items.push_back(item); - } else { - brick.subcellItems.push_back(item); - } + auto& itemIn = (key.isSmall() ? brick.subcellItems : brick.items); + + itemIn.push_back(item); + cell.signalBrickFilled(); }, true); @@ -183,6 +200,10 @@ ItemSpatialTree::Index ItemSpatialTree::insertItem(Index cellIdx, const ItemKey& bool ItemSpatialTree::updateItem(Index cellIdx, const ItemKey& oldKey, const ItemKey& key, const ItemID& item) { auto success = false; + // only if key changed + assert(oldKey != key); + + // Get to the brick where the item is and update where it s stored auto brickID = accessCellBrick(cellIdx, [&](Cell& cell, Brick& brick, Octree::Index cellID) { auto& itemIn = (key.isSmall() ? brick.subcellItems : brick.items); auto& itemOut = (oldKey.isSmall() ? brick.subcellItems : brick.items); @@ -197,12 +218,12 @@ bool ItemSpatialTree::updateItem(Index cellIdx, const ItemKey& oldKey, const Ite bool ItemSpatialTree::removeItem(Index cellIdx, const ItemKey& key, const ItemID& item) { auto success = false; - // Access the brick at the cell (without createing new ones) + // Remove the item from the brick accessCellBrick(cellIdx, [&](Cell& cell, Brick& brick, Octree::Index brickID) { - // remove the item from the list auto& itemList = (key.isSmall() ? brick.subcellItems : brick.items); itemList.erase(std::find(itemList.begin(), itemList.end(), item)); + if (brick.items.empty() && brick.subcellItems.empty()) { cell.signalBrickEmpty(); } @@ -211,33 +232,6 @@ bool ItemSpatialTree::removeItem(Index cellIdx, const ItemKey& key, const ItemID return success; } -/* -ItemSpatialTree::Index ItemSpatialTree::resetItem(Index oldCell, const Location& location, const ItemID& item) { - auto newCell = indexCell(location); - - // Early exit if nothing changed - if (newCell == oldCell) { - return newCell; - } - // do we know about this item ? - else if (oldCell == Item::INVALID_CELL) { - insertItem(newCell, item); - return newCell; - } - // A true update of cell is required - else { - // Add the item to the brick (and a brick if needed) - accessCellBrick(newCell, [&](Cell& cell, Brick& brick, Octree::Index cellID) { - brick.items.push_back(item); - cell.signalBrickFilled(); - }, true); - - // And remove it from the previous one - removeItem(oldCell, item); - - return newCell; - } -}*/ ItemSpatialTree::Index ItemSpatialTree::resetItem(Index oldCell, const ItemKey& oldKey, const AABox& bound, const ItemID& item, ItemKey& newKey) { auto minCoordf = evalCoordf(bound.getMinimumPoint()); @@ -255,8 +249,9 @@ ItemSpatialTree::Index ItemSpatialTree::resetItem(Index oldCell, const ItemKey& auto newCell = indexCell(location); - // Early exit if nothing changed + // Staying in the same cell if (newCell == oldCell) { + // Did the key changed, if yes update if (newKey._flags != oldKey._flags) { updateItem(newCell, oldKey, newKey, item); } @@ -279,11 +274,10 @@ ItemSpatialTree::Index ItemSpatialTree::resetItem(Index oldCell, const ItemKey& } } -int Octree::select(Indices& selectedBricks, Indices& selectedCells, const glm::vec4 frustum[6]) const { +int Octree::select(CellSelection& selection, const FrustumSelector& selector) const { Index cellID = ROOT_CELL; - selectTraverse(cellID, selectedBricks, selectedCells, frustum); - return selectedCells.size(); + return selectTraverse(cellID, selection, selector); } @@ -345,11 +339,13 @@ Octree::Location::Intersection Octree::Location::intersectCell(const Location& c return Inside; } -int Octree::selectTraverse(Index cellID, Indices& selectedBricks, Indices& selectedCells, const Coord4f frustum[6]) const { - int numSelectedsIn = selectedCells.size(); +int Octree::selectTraverse(Index cellID, CellSelection& selection, const FrustumSelector& selector) const { + int numSelectedsIn = selection.size(); auto cell = getConcreteCell(cellID); - auto intersection = Octree::Location::intersectCell(cell.getlocation(), frustum); + auto cellLocation = cell.getlocation(); + + auto intersection = Octree::Location::intersectCell(cellLocation, selector.frustum); switch (intersection) { case Octree::Location::Outside: // cell is outside, stop traversing this branch @@ -357,82 +353,110 @@ int Octree::selectTraverse(Index cellID, Indices& selectedBricks, Indices& selec break; case Octree::Location::Inside: { // traverse all the Cell Branch and collect items in the selection - selectBranch(cellID, selectedBricks, selectedCells, frustum); + selectBranch(cellID, selection, selector); break; } case Octree::Location::Intersect: default: { // Cell is partially in - // Select within this cell - selectInCell(cellID, selectedBricks, selectedCells, frustum); + // Test for lod + auto cellLocation = cell.getlocation(); + float lod = selector.testSolidAngle(cellLocation.getCenter(), Octree::getCoordSubcellWidth(cellLocation.depth)); + if (lod < 0.0) { + return 0; + break; + } + + // Select this cell partially in frustum + selectCellBrick(cellID, selection, false); // then traverse deeper for (int i = 0; i < NUM_OCTANTS; i++) { Index subCellID = cell.child((Link)i); if (subCellID != INVALID) { - selectTraverse(subCellID, selectedBricks, selectedCells, frustum); + selectTraverse(subCellID, selection, selector); } } } } - return selectedCells.size() - numSelectedsIn; + return selection.size() - numSelectedsIn; } -int Octree::selectBranch(Index cellID, Indices& selectedBricks, Indices& selectedCells, const Coord4f frustum[6]) const { - int numSelectedsIn = selectedCells.size(); +int Octree::selectBranch(Index cellID, CellSelection& selection, const FrustumSelector& selector) const { + int numSelectedsIn = selection.size(); auto cell = getConcreteCell(cellID); - // Select within this cell - selectInCell(cellID, selectedBricks, selectedCells, frustum); + auto cellLocation = cell.getlocation(); + float lod = selector.testSolidAngle(cellLocation.getCenter(), Octree::getCoordSubcellWidth(cellLocation.depth)); + if (lod < 0.0) { + return 0; + } + + // Select this cell fully inside the frustum + selectCellBrick(cellID, selection, true); // then traverse deeper for (int i = 0; i < NUM_OCTANTS; i++) { Index subCellID = cell.child((Link)i); if (subCellID != INVALID) { - selectBranch(subCellID, selectedBricks, selectedCells, frustum); + selectBranch(subCellID, selection, selector); } } - return selectedCells.size() - numSelectedsIn; + return selection.size() - numSelectedsIn; } -int Octree::selectInCell(Index cellID, Indices& selectedBricks, Indices& selectedCells, const Coord4f frustum[6]) const { - int numSelectedsIn = selectedCells.size(); +int Octree::selectCellBrick(Index cellID, CellSelection& selection, bool inside) const { + int numSelectedsIn = selection.size(); auto cell = getConcreteCell(cellID); - selectedCells.push_back(cellID); + selection.cells(inside).push_back(cellID); if (!cell.isBrickEmpty()) { // Collect the items of this cell - selectedBricks.push_back(cell.brick()); + selection.bricks(inside).push_back(cell.brick()); } - return selectedCells.size() - numSelectedsIn; + return selection.size() - numSelectedsIn; } -int ItemSpatialTree::select(Indices& selectedBricks, Indices& selectedCells, const ViewFrustum& frustum) const { +int ItemSpatialTree::selectCells(CellSelection& selection, const ViewFrustum& frustum) const { auto worldPlanes = frustum.getPlanes(); - Coord4f planes[6]; + FrustumSelector selector; for (int i = 0; i < ViewFrustum::NUM_PLANES; i++) { ::Plane octPlane; octPlane.setNormalAndPoint(worldPlanes[i].getNormal(), evalCoordf(worldPlanes[i].getPoint(), ROOT_DEPTH)); - planes[i] = Coord4f(octPlane.getNormal(), octPlane.getDCoefficient()); + selector.frustum[i] = Coord4f(octPlane.getNormal(), octPlane.getDCoefficient()); } - return Octree::select(selectedBricks, selectedCells, planes); + + selector.eyePos = evalCoordf(frustum.getPosition(), ROOT_DEPTH); + selector.setAngle(glm::radians(2.0f)); + + return Octree::select(selection, selector); } -int ItemSpatialTree::fetch(ItemIDs& fetchedItems, const ItemFilter& filter, const ViewFrustum& frustum) const { - Indices bricks; - Indices cells; - select(bricks, cells, frustum); +int ItemSpatialTree::selectCellItems(ItemSelection& selection, const ItemFilter& filter, const ViewFrustum& frustum) const { + selectCells(selection.cellSelection, frustum); - for (auto brickId : bricks) { + // Just grab the items in every selected bricks + for (auto brickId : selection.cellSelection.insideBricks) { auto& brickItems = getConcreteBrick(brickId).items; - fetchedItems.insert(fetchedItems.end(), brickItems.begin(), brickItems.end()); + selection.insideItems.insert(selection.insideItems.end(), brickItems.begin(), brickItems.end()); + + auto& brickSubcellItems = getConcreteBrick(brickId).subcellItems; + selection.insideSubcellItems.insert(selection.insideSubcellItems.end(), brickSubcellItems.begin(), brickSubcellItems.end()); } - return bricks.size(); + for (auto brickId : selection.cellSelection.partialBricks) { + auto& brickItems = getConcreteBrick(brickId).items; + selection.partialItems.insert(selection.partialItems.end(), brickItems.begin(), brickItems.end()); + + auto& brickSubcellItems = getConcreteBrick(brickId).subcellItems; + selection.partialSubcellItems.insert(selection.partialSubcellItems.end(), brickSubcellItems.begin(), brickSubcellItems.end()); + } + + return selection.numItems(); } diff --git a/libraries/render/src/render/Octree.h b/libraries/render/src/render/Octree.h index dd663bb232..4d842a3924 100644 --- a/libraries/render/src/render/Octree.h +++ b/libraries/render/src/render/Octree.h @@ -92,6 +92,7 @@ namespace render { static int getDepthDimension(Depth depth) { return 1 << depth; } static double getInvDepthDimension(Depth depth) { return INV_DEPTH_DIM[depth]; } + static float getCoordSubcellWidth(Depth depth) { return (float) (1.7320 * getInvDepthDimension(depth) * 0.5); } // Need 16bits integer coordinates on each axes: 32768 cell positions using Coord = int16_t; @@ -128,6 +129,11 @@ namespace render { uint8_t spare{ 0 }; Depth depth{ ROOT_DEPTH }; + Coord3f getCenter() const { + Coord3f center = (Coord3f(pos) + Coord3f(0.5f)) * Coord3f(Octree::getInvDepthDimension(depth)); + return center; + } + bool operator== (const Location& right) const { return pos == right.pos && depth == right.depth; } // Eval the octant of this cell relative to its parent @@ -138,6 +144,7 @@ namespace render { Location parent() const { return Location{ (pos >> Coord3(1)), Depth(depth <= 0 ? 0 : depth - 1) }; } Location child(Link octant) const { return Location{ (pos << Coord3(1)) | octantAxes(octant), Depth(depth + 1) }; } + // Eval the list of locations (the path) from root to the destination. // the root cell is included static vector pathTo(const Location& destination); @@ -152,7 +159,6 @@ namespace render { Inside, }; static Intersection intersectCell(const Location& cell, const Coord4f frustum[6]); - }; using Locations = Location::vector; @@ -243,11 +249,51 @@ namespace render { return _bricks[index]; } - // Selection and traverse - int select(Indices& selectedBricks, Indices& selectedCells, const Coord4f frustum[6]) const; - int selectTraverse(Index cellID, Indices& selectedBricks, Indices& selectedCells, const Coord4f frustum[6]) const; - int selectBranch(Index cellID, Indices& selectedBricks, Indices& selectedCells, const Coord4f frustum[6]) const; - int selectInCell(Index cellID, Indices& selectedBricks, Indices& selectedCells, const Coord4f frustum[6]) const; + // Cell Selection and traversal + + class CellSelection { + public: + Indices insideCells; + Indices insideBricks; + Indices partialCells; + Indices partialBricks; + + Indices& cells(bool inside) { return (inside ? insideCells : partialCells); } + Indices& bricks(bool inside) { return (inside ? insideBricks : partialBricks); } + + int size() const { return insideBricks.size() + partialBricks.size(); } + + void clear() { + insideCells.clear(); + insideBricks.clear(); + partialCells.clear(); + partialBricks.clear(); + } + }; + + class FrustumSelector { + public: + Coord4f frustum[6]; + Coord3f eyePos; + float angle; + float squareTanAlpha; + + void setAngle(float a) { + angle = std::max(glm::radians(1.0f/60.0f), a); // no better than 1 minute of degree + auto tanAlpha = tan(angle); + squareTanAlpha = (float)(tanAlpha * tanAlpha); + } + + float testSolidAngle(const Coord3f& point, float size) const { + auto eyeToPoint = point - eyePos; + return (size * size / glm::dot(eyeToPoint, eyeToPoint)) - squareTanAlpha; + } + }; + + int select(CellSelection& selection, const FrustumSelector& selector) const; + int selectTraverse(Index cellID, CellSelection& selection, const FrustumSelector& selector) const; + int selectBranch(Index cellID, CellSelection& selection, const FrustumSelector& selector) const; + int selectCellBrick(Index cellID, CellSelection& selection, bool inside) const; protected: Index allocateCell(Index parent, const Location& location); @@ -314,8 +360,33 @@ namespace render { Index resetItem(Index oldCell, const ItemKey& oldKey, const AABox& bound, const ItemID& item, ItemKey& newKey); // Selection and traverse - int select(Indices& selectedBricks, Indices& selectedCells, const ViewFrustum& frustum) const; - int fetch(ItemIDs& fetchedItems, const ItemFilter& filter, const ViewFrustum& frustum) const; + int selectCells(CellSelection& selection, const ViewFrustum& frustum) const; + + class ItemSelection { + public: + CellSelection cellSelection; + ItemIDs insideItems; + ItemIDs insideSubcellItems; + ItemIDs partialItems; + ItemIDs partialSubcellItems; + + ItemIDs& items(bool inside) { return (inside ? insideItems : partialItems); } + ItemIDs& subcellItems(bool inside) { return (inside ? insideSubcellItems : partialSubcellItems); } + + int insideNumItems() const { return insideItems.size() + insideSubcellItems.size(); } + int partialNumItems() const { return partialItems.size() + partialSubcellItems.size(); } + int numItems() const { return insideNumItems() + partialNumItems(); } + + void clear() { + cellSelection.clear(); + insideItems.clear(); + insideSubcellItems.clear(); + partialItems.clear(); + partialSubcellItems.clear(); + } + }; + + int selectCellItems(ItemSelection& selection, const ItemFilter& filter, const ViewFrustum& frustum) const; }; } From 78b21c3f4d235517f9fc3f4264b81944ae973062 Mon Sep 17 00:00:00 2001 From: samcake Date: Wed, 3 Feb 2016 18:39:40 -0800 Subject: [PATCH 23/49] Refining the culling test and defining the 2 new jobs FetchSPatialTree and CullSpatialSelection --- libraries/render/src/render/Octree.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libraries/render/src/render/Octree.cpp b/libraries/render/src/render/Octree.cpp index e7e7c1155a..47e17b1d09 100644 --- a/libraries/render/src/render/Octree.cpp +++ b/libraries/render/src/render/Octree.cpp @@ -34,6 +34,7 @@ const double Octree::INV_DEPTH_DIM[] = { 1.0 / 16384.0, 1.0 / 32768.0 }; +/* const float Octree::COORD_SUBCELL_WIDTH[] = { // 2 ^ MAX_DEPTH / 2 ^ (depth + 1) 16384.0f, 8192.0f, @@ -51,7 +52,7 @@ const float Octree::COORD_SUBCELL_WIDTH[] = { // 2 ^ MAX_DEPTH / 2 ^ (depth + 1) 2.0f, 1.0f, 0.5f }; - +*/ Octree::Location::vector Octree::Location::pathTo(const Location& dest) { Location current{ dest }; From 513561ba2db36b90e6f2c5fc15db44a217609685 Mon Sep 17 00:00:00 2001 From: samcake Date: Thu, 4 Feb 2016 09:31:21 -0800 Subject: [PATCH 24/49] Using an angle for the LOD test --- libraries/render-utils/src/RenderDeferredTask.cpp | 2 +- libraries/render/src/render/CullTask.cpp | 3 ++- libraries/render/src/render/CullTask.h | 5 +++++ libraries/render/src/render/DrawSceneOctree.cpp | 14 +++++++------- libraries/render/src/render/DrawSceneOctree.h | 4 ++-- libraries/render/src/render/Octree.cpp | 8 ++++---- libraries/render/src/render/Octree.h | 4 ++-- 7 files changed, 23 insertions(+), 17 deletions(-) diff --git a/libraries/render-utils/src/RenderDeferredTask.cpp b/libraries/render-utils/src/RenderDeferredTask.cpp index 0578fc78b5..c99b35352e 100755 --- a/libraries/render-utils/src/RenderDeferredTask.cpp +++ b/libraries/render-utils/src/RenderDeferredTask.cpp @@ -112,7 +112,7 @@ RenderDeferredTask::RenderDeferredTask(CullFunctor cullFunctor) { // Scene Octree Debuging job { - addJob("DrawSceneOctree"); + addJob("DrawSceneOctree", opaqueSelection); // _drawStatusJobIndex = (int)_jobs.size() - 1; // enableJob(_drawStatusJobIndex, false); } diff --git a/libraries/render/src/render/CullTask.cpp b/libraries/render/src/render/CullTask.cpp index 332af9b321..53bed259d2 100644 --- a/libraries/render/src/render/CullTask.cpp +++ b/libraries/render/src/render/CullTask.cpp @@ -159,6 +159,7 @@ void FetchItems::run(const SceneContextPointer& sceneContext, const RenderContex void FetchSpatialTree::configure(const Config& config) { _justFrozeFrustum = (config.freezeFrustum && !_freezeFrustum); _freezeFrustum = config.freezeFrustum; + _lodAngle = config.lodAngle; } void FetchSpatialTree::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, ItemSpatialTree::ItemSelection& outSelection) { @@ -181,7 +182,7 @@ void FetchSpatialTree::run(const SceneContextPointer& sceneContext, const Render } // Octree selection! - scene->getSpatialTree().selectCellItems(outSelection, _filter, queryFrustum); + scene->getSpatialTree().selectCellItems(outSelection, _filter, queryFrustum, _lodAngle); } void CullSpatialSelection::configure(const Config& config) { diff --git a/libraries/render/src/render/CullTask.h b/libraries/render/src/render/CullTask.h index daa6832e0a..d17e95b5a8 100644 --- a/libraries/render/src/render/CullTask.h +++ b/libraries/render/src/render/CullTask.h @@ -71,11 +71,15 @@ namespace render { Q_OBJECT Q_PROPERTY(int numItems READ getNumItems) Q_PROPERTY(bool freezeFrustum MEMBER freezeFrustum WRITE setFreezeFrustum) + Q_PROPERTY(float LODAngle MEMBER lodAngle NOTIFY dirty) + public: int numItems{ 0 }; int getNumItems() { return numItems; } bool freezeFrustum{ false }; + + float lodAngle{ 2.0 }; public slots: void setFreezeFrustum(bool enabled) { freezeFrustum = enabled; emit dirty(); } @@ -87,6 +91,7 @@ namespace render { bool _freezeFrustum{ false }; // initialized by Config bool _justFrozeFrustum{ false }; ViewFrustum _frozenFrutstum; + float _lodAngle; public: using Config = FetchSpatialTreeConfig; using JobModel = Job::ModelO; diff --git a/libraries/render/src/render/DrawSceneOctree.cpp b/libraries/render/src/render/DrawSceneOctree.cpp index bfe052c539..e41c94ced8 100644 --- a/libraries/render/src/render/DrawSceneOctree.cpp +++ b/libraries/render/src/render/DrawSceneOctree.cpp @@ -59,12 +59,12 @@ void DrawSceneOctree::configure(const Config& config) { void DrawSceneOctree::run(const SceneContextPointer& sceneContext, - const RenderContextPointer& renderContext) { + const RenderContextPointer& renderContext, const ItemSpatialTree::ItemSelection& inSelection) { assert(renderContext->args); assert(renderContext->args->_viewFrustum); RenderArgs* args = renderContext->args; auto& scene = sceneContext->_scene; - const int NUM_STATUS_VEC4_PER_ITEM = 2; + /*const int NUM_STATUS_VEC4_PER_ITEM = 2; const int VEC4_LENGTH = 4; // FIrst thing, we update the local buffers with the values coming from Scene octree @@ -96,10 +96,10 @@ void DrawSceneOctree::run(const SceneContextPointer& sceneContext, } queryFrustum = _frozenFrutstum; } - +*/ // Try that: - Octree::CellSelection selection; - scene->getSpatialTree().selectCells(selection, queryFrustum); + // Octree::CellSelection selection; + // scene->getSpatialTree().selectCells(selection, queryFrustum); // Allright, something to render let's do it @@ -117,7 +117,7 @@ void DrawSceneOctree::run(const SceneContextPointer& sceneContext, // bind the one gpu::Pipeline we need batch.setPipeline(getDrawCellBoundsPipeline()); - for (const auto& cellID : selection.insideCells) { + for (const auto& cellID : inSelection.cellSelection.insideCells) { auto cell = scene->getSpatialTree().getConcreteCell(cellID); auto cellLoc = cell.getlocation(); @@ -131,7 +131,7 @@ void DrawSceneOctree::run(const SceneContextPointer& sceneContext, batch.draw(gpu::LINES, 24, 0); } - for (const auto& cellID : selection.partialCells) { + for (const auto& cellID : inSelection.cellSelection.partialCells) { auto cell = scene->getSpatialTree().getConcreteCell(cellID); auto cellLoc = cell.getlocation(); diff --git a/libraries/render/src/render/DrawSceneOctree.h b/libraries/render/src/render/DrawSceneOctree.h index 2137401fa5..2137bd5c4d 100644 --- a/libraries/render/src/render/DrawSceneOctree.h +++ b/libraries/render/src/render/DrawSceneOctree.h @@ -50,12 +50,12 @@ namespace render { public: using Config = DrawSceneOctreeConfig; - using JobModel = Job::Model; + using JobModel = Job::ModelI; DrawSceneOctree() {} void configure(const Config& config); - void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext); + void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemSpatialTree::ItemSelection& selection); const gpu::PipelinePointer getDrawCellBoundsPipeline(); }; diff --git a/libraries/render/src/render/Octree.cpp b/libraries/render/src/render/Octree.cpp index 47e17b1d09..3ccad4d7b3 100644 --- a/libraries/render/src/render/Octree.cpp +++ b/libraries/render/src/render/Octree.cpp @@ -424,7 +424,7 @@ int Octree::selectCellBrick(Index cellID, CellSelection& selection, bool inside) } -int ItemSpatialTree::selectCells(CellSelection& selection, const ViewFrustum& frustum) const { +int ItemSpatialTree::selectCells(CellSelection& selection, const ViewFrustum& frustum, float lodAngle) const { auto worldPlanes = frustum.getPlanes(); FrustumSelector selector; for (int i = 0; i < ViewFrustum::NUM_PLANES; i++) { @@ -434,13 +434,13 @@ int ItemSpatialTree::selectCells(CellSelection& selection, const ViewFrustum& fr } selector.eyePos = evalCoordf(frustum.getPosition(), ROOT_DEPTH); - selector.setAngle(glm::radians(2.0f)); + selector.setAngle(glm::radians(lodAngle)); return Octree::select(selection, selector); } -int ItemSpatialTree::selectCellItems(ItemSelection& selection, const ItemFilter& filter, const ViewFrustum& frustum) const { - selectCells(selection.cellSelection, frustum); +int ItemSpatialTree::selectCellItems(ItemSelection& selection, const ItemFilter& filter, const ViewFrustum& frustum, float lodAngle) const { + selectCells(selection.cellSelection, frustum, lodAngle); // Just grab the items in every selected bricks for (auto brickId : selection.cellSelection.insideBricks) { diff --git a/libraries/render/src/render/Octree.h b/libraries/render/src/render/Octree.h index 4d842a3924..48118ea4a9 100644 --- a/libraries/render/src/render/Octree.h +++ b/libraries/render/src/render/Octree.h @@ -360,7 +360,7 @@ namespace render { Index resetItem(Index oldCell, const ItemKey& oldKey, const AABox& bound, const ItemID& item, ItemKey& newKey); // Selection and traverse - int selectCells(CellSelection& selection, const ViewFrustum& frustum) const; + int selectCells(CellSelection& selection, const ViewFrustum& frustum, float lodAngle) const; class ItemSelection { public: @@ -386,7 +386,7 @@ namespace render { } }; - int selectCellItems(ItemSelection& selection, const ItemFilter& filter, const ViewFrustum& frustum) const; + int selectCellItems(ItemSelection& selection, const ItemFilter& filter, const ViewFrustum& frustum, float lodAngle) const; }; } From a999e046b5d5196959f05c6f653eb85412e0cf7a Mon Sep 17 00:00:00 2001 From: samcake Date: Thu, 4 Feb 2016 17:59:35 -0800 Subject: [PATCH 25/49] Showing the LOD angle --- libraries/octree/src/ViewFrustum.cpp | 6 ++++ libraries/octree/src/ViewFrustum.h | 2 ++ libraries/render/src/render/CullTask.cpp | 1 + .../render/src/render/DrawSceneOctree.cpp | 36 +++++++++++++++++++ libraries/render/src/render/DrawSceneOctree.h | 3 ++ libraries/render/src/render/Octree.h | 10 +++--- .../render/src/render/drawLODReticle.slf | 26 ++++++++++++++ 7 files changed, 80 insertions(+), 4 deletions(-) create mode 100644 libraries/render/src/render/drawLODReticle.slf diff --git a/libraries/octree/src/ViewFrustum.cpp b/libraries/octree/src/ViewFrustum.cpp index dd4ddc28e7..4c07a0c784 100644 --- a/libraries/octree/src/ViewFrustum.cpp +++ b/libraries/octree/src/ViewFrustum.cpp @@ -810,3 +810,9 @@ float ViewFrustum::calculateRenderAccuracy(const AABox& bounds, float octreeSize float boundaryDistanceForRenderLevel(unsigned int renderLevel, float voxelSizeScale) { return voxelSizeScale / powf(2, renderLevel); } + +float ViewFrustum::getAccuracyAngle(float octreeSizeScale, int boundaryLevelAdjust) const { + const float maxScale = (float)TREE_SCALE; + float visibleDistanceAtMaxScale = boundaryDistanceForRenderLevel(boundaryLevelAdjust, octreeSizeScale) / OCTREE_TO_MESH_RATIO; + return atan(maxScale / visibleDistanceAtMaxScale); +} diff --git a/libraries/octree/src/ViewFrustum.h b/libraries/octree/src/ViewFrustum.h index 548bd6a940..b6096114a4 100644 --- a/libraries/octree/src/ViewFrustum.h +++ b/libraries/octree/src/ViewFrustum.h @@ -125,6 +125,8 @@ public: float calculateRenderAccuracy(const AABox& bounds, float octreeSizeScale = DEFAULT_OCTREE_SIZE_SCALE, int boundaryLevelAdjust = 0) const; + float getAccuracyAngle(float octreeSizeScale = DEFAULT_OCTREE_SIZE_SCALE, int boundaryLevelAdjust = 0) const; + enum PlaneIndex { TOP_PLANE = 0, BOTTOM_PLANE, LEFT_PLANE, RIGHT_PLANE, NEAR_PLANE, FAR_PLANE, NUM_PLANES }; const ::Plane* getPlanes() const { return _planes; } diff --git a/libraries/render/src/render/CullTask.cpp b/libraries/render/src/render/CullTask.cpp index 53bed259d2..70cf420e3b 100644 --- a/libraries/render/src/render/CullTask.cpp +++ b/libraries/render/src/render/CullTask.cpp @@ -183,6 +183,7 @@ void FetchSpatialTree::run(const SceneContextPointer& sceneContext, const Render // Octree selection! scene->getSpatialTree().selectCellItems(outSelection, _filter, queryFrustum, _lodAngle); + } void CullSpatialSelection::configure(const Config& config) { diff --git a/libraries/render/src/render/DrawSceneOctree.cpp b/libraries/render/src/render/DrawSceneOctree.cpp index e41c94ced8..d664af3c9b 100644 --- a/libraries/render/src/render/DrawSceneOctree.cpp +++ b/libraries/render/src/render/DrawSceneOctree.cpp @@ -18,9 +18,12 @@ #include #include +#include + #include "drawCellBounds_vert.h" #include "drawCellBounds_frag.h" +#include "drawLODReticle_frag.h" using namespace render; @@ -49,6 +52,27 @@ const gpu::PipelinePointer DrawSceneOctree::getDrawCellBoundsPipeline() { return _drawCellBoundsPipeline; } +const gpu::PipelinePointer DrawSceneOctree::getDrawLODReticlePipeline() { + if (!_drawLODReticlePipeline) { + auto vs = gpu::StandardShaderLib::getDrawTransformUnitQuadVS(); + auto ps = gpu::Shader::createPixel(std::string(drawLODReticle_frag)); + gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps); + + gpu::Shader::BindingSet slotBindings; + gpu::Shader::makeProgram(*program, slotBindings); + + // _drawCellLocationLoc = program->getUniforms().findLocation("inCellLocation"); + + auto state = std::make_shared(); + + // Blend on transparent + state->setBlendFunction(true, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA); + + // Good to go add the brand new pipeline + _drawLODReticlePipeline = gpu::Pipeline::create(program, state); + } + return _drawLODReticlePipeline; +} void DrawSceneOctree::configure(const Config& config) { _showVisibleCells = config.showVisibleCells; @@ -145,5 +169,17 @@ void DrawSceneOctree::run(const SceneContextPointer& sceneContext, batch.draw(gpu::LINES, 24, 0); } + + // Draw the LOD Reticle + { + float angle = glm::degrees(args->_viewFrustum->getAccuracyAngle(args->_sizeScale, args->_boundaryLevelAdjust)); + Transform crosshairModel; + crosshairModel.setTranslation(glm::vec3(0.0, 0.0, -1.0)); + crosshairModel.setScale(tan(glm::radians(angle))); // Scaling at the actual tan of the lod angle => Multiplied by TWO + batch.setViewTransform(Transform()); + batch.setModelTransform(crosshairModel); + batch.setPipeline(getDrawLODReticlePipeline()); + batch.draw(gpu::TRIANGLE_STRIP, 4, 0); + } }); } diff --git a/libraries/render/src/render/DrawSceneOctree.h b/libraries/render/src/render/DrawSceneOctree.h index 2137bd5c4d..ddf22f4e9b 100644 --- a/libraries/render/src/render/DrawSceneOctree.h +++ b/libraries/render/src/render/DrawSceneOctree.h @@ -42,6 +42,8 @@ namespace render { gpu::PipelinePointer _drawCellBoundsPipeline; gpu::BufferPointer _cells; + gpu::PipelinePointer _drawLODReticlePipeline; + bool _showVisibleCells; // initialized by Config bool _freezeFrustum{ false }; // initialized by Config @@ -58,6 +60,7 @@ namespace render { void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemSpatialTree::ItemSelection& selection); const gpu::PipelinePointer getDrawCellBoundsPipeline(); + const gpu::PipelinePointer getDrawLODReticlePipeline(); }; } diff --git a/libraries/render/src/render/Octree.h b/libraries/render/src/render/Octree.h index 48118ea4a9..6309ea8d6b 100644 --- a/libraries/render/src/render/Octree.h +++ b/libraries/render/src/render/Octree.h @@ -87,7 +87,8 @@ namespace render { // Max depth is 15 => 32Km root down to 1m cells using Depth = int8_t; static const Depth ROOT_DEPTH{ 0 }; - static const Depth MAX_DEPTH { 15 }; + static const Depth MAX_DEPTH{ 15 }; + static const Depth METRIC_COORD_DEPTH{ 15 }; static const double INV_DEPTH_DIM[Octree::MAX_DEPTH + 1]; static int getDepthDimension(Depth depth) { return 1 << depth; } @@ -279,6 +280,7 @@ namespace render { float squareTanAlpha; void setAngle(float a) { + angle = std::min(glm::radians(45.0f), a); // no worse than 45 degrees angle = std::max(glm::radians(1.0f/60.0f), a); // no better than 1 minute of degree auto tanAlpha = tan(angle); squareTanAlpha = (float)(tanAlpha * tanAlpha); @@ -325,18 +327,18 @@ namespace render { float getCellWidth(Depth depth) const { return (float) _size * getInvDepthDimension(depth); } float getInvCellWidth(Depth depth) const { return (float) getDepthDimension(depth) * _invSize; } - glm::vec3 evalPos(const Coord3& coord, Depth depth = Octree::MAX_DEPTH) const { + glm::vec3 evalPos(const Coord3& coord, Depth depth = Octree::METRIC_COORD_DEPTH) const { return getOrigin() + glm::vec3(coord) * getCellWidth(depth); } glm::vec3 evalPos(const Coord3& coord, float cellWidth) const { return getOrigin() + glm::vec3(coord) * cellWidth; } - Coord3 evalCoord(const glm::vec3& pos, Depth depth = Octree::MAX_DEPTH) const { + Coord3 evalCoord(const glm::vec3& pos, Depth depth = Octree::METRIC_COORD_DEPTH) const { auto npos = (pos - getOrigin()); return Coord3(npos * getInvCellWidth(depth)); // Truncate fractional part } - Coord3f evalCoordf(const glm::vec3& pos, Depth depth = Octree::MAX_DEPTH) const { + Coord3f evalCoordf(const glm::vec3& pos, Depth depth = Octree::METRIC_COORD_DEPTH) const { auto npos = (pos - getOrigin()); return Coord3f(npos * getInvCellWidth(depth)); } diff --git a/libraries/render/src/render/drawLODReticle.slf b/libraries/render/src/render/drawLODReticle.slf new file mode 100644 index 0000000000..68eb27b775 --- /dev/null +++ b/libraries/render/src/render/drawLODReticle.slf @@ -0,0 +1,26 @@ +<@include gpu/Config.slh@> +<$VERSION_HEADER$> +// Generated on <$_SCRIBE_DATE$> +// +// Draw the LOD reticle used to visualize the current LOD angle +// +// Created by Sam Gateau on 2/4/16 +// 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 +// + +in vec2 varTexCoord0; +out vec4 outFragColor; + +void main(void) { + vec2 circlePos = 2.0 * ( varTexCoord0.xy * 2.0 - vec2(1.0) ); + float radius = length(circlePos); + + float lodEdge = step(abs(1.0 - radius), 0.05); + + float cellEdge = step(abs(2.0 - radius), 0.05); + + outFragColor = vec4(lodEdge * vec3(1.0, 1.0, 0.0) + cellEdge * vec3(0.0, 1.0, 1.0), lodEdge + 0.5 * cellEdge); +} \ No newline at end of file From e5f3805d0e3558ab0ecbb4d5ee453fd18d7c9c80 Mon Sep 17 00:00:00 2001 From: samcake Date: Fri, 5 Feb 2016 16:49:43 -0800 Subject: [PATCH 26/49] Fixing the item insert in cell criteria for subcell and adding debgging tool --- .../utilities/tools/debugRenderCulling.js | 81 +++++++ .../render-utils/src/RenderDeferredTask.cpp | 3 +- libraries/render/src/render/CullTask.cpp | 6 +- .../render/src/render/DrawSceneOctree.cpp | 203 ++++++++++++------ libraries/render/src/render/DrawSceneOctree.h | 67 +++++- libraries/render/src/render/Octree.cpp | 6 +- libraries/render/src/render/Octree.h | 5 + 7 files changed, 299 insertions(+), 72 deletions(-) create mode 100644 examples/utilities/tools/debugRenderCulling.js diff --git a/examples/utilities/tools/debugRenderCulling.js b/examples/utilities/tools/debugRenderCulling.js new file mode 100644 index 0000000000..4579f434d0 --- /dev/null +++ b/examples/utilities/tools/debugRenderCulling.js @@ -0,0 +1,81 @@ +// +// debugRenderOctree.js +// examples/utilities/tools +// +// Sam Gateau +// Copyright 2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +Script.include("cookies.js"); + +var panel = new Panel(10, 300); +var drawOctree = Render.RenderDeferredTask.DrawSceneOctree; +Render.RenderDeferredTask.DrawSceneOctree.enabled = true; +Render.RenderDeferredTask.DrawItemSelection.enabled = true; + +panel.newCheckbox("Show Octree Cells", + function(value) { Render.RenderDeferredTask.DrawSceneOctree.showVisibleCells = value; }, + function() { return (Render.RenderDeferredTask.DrawSceneOctree.showVisibleCells); }, + function(value) { return (value); } +); +panel.newCheckbox("Show Empty Cells", + function(value) { Render.RenderDeferredTask.DrawSceneOctree.showEmptyCells = value; }, + function() { return (Render.RenderDeferredTask.DrawSceneOctree.showEmptyCells); }, + function(value) { return (value); } +); +panel.newCheckbox("Freeze Frustum", + function(value) { Render.RenderDeferredTask.DrawSceneOctree.freezeFrustum = value; }, + function() { return (Render.RenderDeferredTask.DrawSceneOctree.freezeFrustum); }, + function(value) { return (value); } +); +panel.newCheckbox("Show Inside Items", + function(value) { Render.RenderDeferredTask.DrawItemSelection.showInsideItems = value; }, + function() { return (Render.RenderDeferredTask.DrawItemSelection.showInsideItems); }, + function(value) { return (value); } +); + +panel.newCheckbox("Show Inside Subcell Items", + function(value) { Render.RenderDeferredTask.DrawItemSelection.showInsideSubcellItems = value; }, + function() { return (Render.RenderDeferredTask.DrawItemSelection.showInsideSubcellItems); }, + function(value) { return (value); } +); + +panel.newCheckbox("Show Partial Items", + function(value) { Render.RenderDeferredTask.DrawItemSelection.showPartialItems = value; }, + function() { return (Render.RenderDeferredTask.DrawItemSelection.showPartialItems); }, + function(value) { return (value); } +); + +panel.newCheckbox("Show Partial Subcell Items", + function(value) { Render.RenderDeferredTask.DrawItemSelection.showPartialSubcellItems = value; }, + function() { return (Render.RenderDeferredTask.DrawItemSelection.showPartialSubcellItems); }, + function(value) { return (value); } +); + +function mouseMoveEvent(event) { + panel.mouseMoveEvent(event); +} + +function mousePressEvent(event) { + panel.mousePressEvent(event); +} + +function mouseReleaseEvent(event) { + panel.mouseReleaseEvent(event); +} + +Controller.mouseMoveEvent.connect(mouseMoveEvent); +Controller.mousePressEvent.connect(mousePressEvent); +Controller.mouseReleaseEvent.connect(mouseReleaseEvent); + +function scriptEnding() { + panel.destroy(); + Render.RenderDeferredTask.DrawSceneOctree.enabled = false; + Render.RenderDeferredTask.DrawItemSelection.enabled = false; +} +Script.scriptEnding.connect(scriptEnding); + + diff --git a/libraries/render-utils/src/RenderDeferredTask.cpp b/libraries/render-utils/src/RenderDeferredTask.cpp index c99b35352e..654e797e1b 100755 --- a/libraries/render-utils/src/RenderDeferredTask.cpp +++ b/libraries/render-utils/src/RenderDeferredTask.cpp @@ -113,8 +113,7 @@ RenderDeferredTask::RenderDeferredTask(CullFunctor cullFunctor) { // Scene Octree Debuging job { addJob("DrawSceneOctree", opaqueSelection); - // _drawStatusJobIndex = (int)_jobs.size() - 1; - // enableJob(_drawStatusJobIndex, false); + addJob("DrawItemSelection", opaqueSelection); } // Status icon rendering job diff --git a/libraries/render/src/render/CullTask.cpp b/libraries/render/src/render/CullTask.cpp index 70cf420e3b..580084cb86 100644 --- a/libraries/render/src/render/CullTask.cpp +++ b/libraries/render/src/render/CullTask.cpp @@ -182,7 +182,11 @@ void FetchSpatialTree::run(const SceneContextPointer& sceneContext, const Render } // Octree selection! - scene->getSpatialTree().selectCellItems(outSelection, _filter, queryFrustum, _lodAngle); + + float angle = glm::degrees(args->_viewFrustum->getAccuracyAngle(args->_sizeScale, args->_boundaryLevelAdjust)); + + + scene->getSpatialTree().selectCellItems(outSelection, _filter, queryFrustum, angle); } diff --git a/libraries/render/src/render/DrawSceneOctree.cpp b/libraries/render/src/render/DrawSceneOctree.cpp index d664af3c9b..f60b20a69f 100644 --- a/libraries/render/src/render/DrawSceneOctree.cpp +++ b/libraries/render/src/render/DrawSceneOctree.cpp @@ -25,6 +25,9 @@ #include "drawCellBounds_frag.h" #include "drawLODReticle_frag.h" +#include "drawItemBounds_vert.h" +#include "drawItemBounds_frag.h" + using namespace render; @@ -76,6 +79,7 @@ const gpu::PipelinePointer DrawSceneOctree::getDrawLODReticlePipeline() { void DrawSceneOctree::configure(const Config& config) { _showVisibleCells = config.showVisibleCells; + _showEmptyCells = config.showEmptyCells; _justFrozeFrustum = (config.freezeFrustum && !_freezeFrustum); _freezeFrustum = config.freezeFrustum; @@ -88,45 +92,7 @@ void DrawSceneOctree::run(const SceneContextPointer& sceneContext, assert(renderContext->args->_viewFrustum); RenderArgs* args = renderContext->args; auto& scene = sceneContext->_scene; - /*const int NUM_STATUS_VEC4_PER_ITEM = 2; - const int VEC4_LENGTH = 4; - // FIrst thing, we update the local buffers with the values coming from Scene octree - int nbCells = 0; - { - if (!_cells) { - _cells = std::make_shared(); - } - - const auto& inCells = scene->getSpatialTree()._cells; - _cells->resize(inCells.size() * sizeof(AABox)); - AABox* cellAABox = reinterpret_cast (_cells->editData()); - for (const auto& cell : inCells) { - (*cellAABox) = scene->getSpatialTree().evalBound(cell.getlocation()); - nbCells++; - cellAABox++; - } - } - - if (nbCells == 0) { - return; - } - - auto queryFrustum = *args->_viewFrustum; - if (_freezeFrustum) { - if (_justFrozeFrustum) { - _justFrozeFrustum = false; - _frozenFrutstum = *args->_viewFrustum; - } - queryFrustum = _frozenFrutstum; - } -*/ - // Try that: - // Octree::CellSelection selection; - // scene->getSpatialTree().selectCells(selection, queryFrustum); - - - // Allright, something to render let's do it gpu::doInBatch(args->_context, [&](gpu::Batch& batch) { glm::mat4 projMat; Transform viewMat; @@ -141,41 +107,50 @@ void DrawSceneOctree::run(const SceneContextPointer& sceneContext, // bind the one gpu::Pipeline we need batch.setPipeline(getDrawCellBoundsPipeline()); - for (const auto& cellID : inSelection.cellSelection.insideCells) { - auto cell = scene->getSpatialTree().getConcreteCell(cellID); + if (_showVisibleCells) { - auto cellLoc = cell.getlocation(); + for (const auto& cellID : inSelection.cellSelection.insideCells) { + auto cell = scene->getSpatialTree().getConcreteCell(cellID); + auto cellLoc = cell.getlocation(); + glm::ivec4 cellLocation(cellLoc.pos.x, cellLoc.pos.y, cellLoc.pos.z, cellLoc.depth); - glm::ivec4 cellLocation(cellLoc.pos.x, cellLoc.pos.y, cellLoc.pos.z, cellLoc.depth); - if (cell.isBrickEmpty() || !cell.hasBrick()) { - cellLocation.w *= -1; + bool doDraw = true; + if (cell.isBrickEmpty() || !cell.hasBrick()) { + if (!_showEmptyCells) { + doDraw = false; + } + cellLocation.w *= -1; + } + if (doDraw) { + batch._glUniform4iv(_drawCellLocationLoc, 1, ((const int*)(&cellLocation))); + batch.draw(gpu::LINES, 24, 0); + } } - batch._glUniform4iv(_drawCellLocationLoc, 1, ((const int*)(&cellLocation))); + for (const auto& cellID : inSelection.cellSelection.partialCells) { + auto cell = scene->getSpatialTree().getConcreteCell(cellID); + auto cellLoc = cell.getlocation(); + glm::ivec4 cellLocation(cellLoc.pos.x, cellLoc.pos.y, cellLoc.pos.z, cellLoc.depth); - batch.draw(gpu::LINES, 24, 0); - } - for (const auto& cellID : inSelection.cellSelection.partialCells) { - auto cell = scene->getSpatialTree().getConcreteCell(cellID); - - auto cellLoc = cell.getlocation(); - - glm::ivec4 cellLocation(cellLoc.pos.x, cellLoc.pos.y, cellLoc.pos.z, cellLoc.depth); - if (cell.isBrickEmpty() || !cell.hasBrick()) { - cellLocation.w *= -1; + bool doDraw = true; + if (cell.isBrickEmpty() || !cell.hasBrick()) { + if (!_showEmptyCells) { + doDraw = false; + } + cellLocation.w *= -1; + } + if (doDraw) { + batch._glUniform4iv(_drawCellLocationLoc, 1, ((const int*)(&cellLocation))); + batch.draw(gpu::LINES, 24, 0); + } } - - batch._glUniform4iv(_drawCellLocationLoc, 1, ((const int*)(&cellLocation))); - - batch.draw(gpu::LINES, 24, 0); } - // Draw the LOD Reticle { float angle = glm::degrees(args->_viewFrustum->getAccuracyAngle(args->_sizeScale, args->_boundaryLevelAdjust)); Transform crosshairModel; - crosshairModel.setTranslation(glm::vec3(0.0, 0.0, -1.0)); - crosshairModel.setScale(tan(glm::radians(angle))); // Scaling at the actual tan of the lod angle => Multiplied by TWO + crosshairModel.setTranslation(glm::vec3(0.0, 0.0, -1000.0)); + crosshairModel.setScale(1000.0 * tan(glm::radians(angle))); // Scaling at the actual tan of the lod angle => Multiplied by TWO batch.setViewTransform(Transform()); batch.setModelTransform(crosshairModel); batch.setPipeline(getDrawLODReticlePipeline()); @@ -183,3 +158,107 @@ void DrawSceneOctree::run(const SceneContextPointer& sceneContext, } }); } + +const gpu::PipelinePointer DrawItemSelection::getDrawItemBoundPipeline() { + if (!_drawItemBoundPipeline) { + 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); + + _drawItemBoundPosLoc = program->getUniforms().findLocation("inBoundPos"); + _drawItemBoundDimLoc = program->getUniforms().findLocation("inBoundDim"); + + auto state = std::make_shared(); + + state->setDepthTest(true, false, gpu::LESS_EQUAL); + + // Blend on transparent + state->setBlendFunction(true, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA); + + // Good to go add the brand new pipeline + _drawItemBoundPipeline = gpu::Pipeline::create(program, state); + } + return _drawItemBoundPipeline; +} + +void DrawItemSelection::configure(const Config& config) { + _showInsideItems = config.showInsideItems; + _showInsideSubcellItems = config.showInsideSubcellItems; + _showPartialItems = config.showPartialItems; + _showPartialSubcellItems = config.showPartialSubcellItems; +} + + +void DrawItemSelection::run(const SceneContextPointer& sceneContext, + const RenderContextPointer& renderContext, const ItemSpatialTree::ItemSelection& inSelection) { + assert(renderContext->args); + assert(renderContext->args->_viewFrustum); + RenderArgs* args = renderContext->args; + auto& scene = sceneContext->_scene; + + gpu::doInBatch(args->_context, [&](gpu::Batch& batch) { + glm::mat4 projMat; + Transform viewMat; + args->_viewFrustum->evalProjectionMatrix(projMat); + args->_viewFrustum->evalViewTransform(viewMat); + batch.setViewportTransform(args->_viewport); + + batch.setProjectionTransform(projMat); + batch.setViewTransform(viewMat); + batch.setModelTransform(Transform()); + + // bind the one gpu::Pipeline we need + batch.setPipeline(getDrawItemBoundPipeline()); + + if (_showInsideItems) { + for (const auto& itemID : inSelection.insideItems) { + auto& item = scene->getItem(itemID); + auto itemBound = item.getBound(); + + batch._glUniform3fv(_drawItemBoundPosLoc, 1, (const float*)(&itemBound.getCorner())); + batch._glUniform3fv(_drawItemBoundDimLoc, 1, (const float*)(&itemBound.getScale())); + + batch.draw(gpu::LINES, 24, 0); + } + } + + if (_showInsideSubcellItems) { + for (const auto& itemID : inSelection.insideSubcellItems) { + auto& item = scene->getItem(itemID); + auto itemBound = item.getBound(); + + batch._glUniform3fv(_drawItemBoundPosLoc, 1, (const float*)(&itemBound.getCorner())); + batch._glUniform3fv(_drawItemBoundDimLoc, 1, (const float*)(&itemBound.getScale())); + + batch.draw(gpu::LINES, 24, 0); + } + } + + if (_showPartialItems) { + for (const auto& itemID : inSelection.partialItems) { + auto& item = scene->getItem(itemID); + auto itemBound = item.getBound(); + + batch._glUniform3fv(_drawItemBoundPosLoc, 1, (const float*)(&itemBound.getCorner())); + batch._glUniform3fv(_drawItemBoundDimLoc, 1, (const float*)(&itemBound.getScale())); + + batch.draw(gpu::LINES, 24, 0); + } + } + + if (_showPartialSubcellItems) { + for (const auto& itemID : inSelection.partialSubcellItems) { + auto& item = scene->getItem(itemID); + auto itemBound = item.getBound(); + + batch._glUniform3fv(_drawItemBoundPosLoc, 1, (const float*)(&itemBound.getCorner())); + batch._glUniform3fv(_drawItemBoundDimLoc, 1, (const float*)(&itemBound.getScale())); + + batch.draw(gpu::LINES, 24, 0); + } + } + }); +} diff --git a/libraries/render/src/render/DrawSceneOctree.h b/libraries/render/src/render/DrawSceneOctree.h index ddf22f4e9b..7301a776f3 100644 --- a/libraries/render/src/render/DrawSceneOctree.h +++ b/libraries/render/src/render/DrawSceneOctree.h @@ -19,18 +19,22 @@ namespace render { class DrawSceneOctreeConfig : public Job::Config { Q_OBJECT + Q_PROPERTY(bool enabled MEMBER enabled NOTIFY dirty()) Q_PROPERTY(bool showVisibleCells MEMBER showVisibleCells WRITE setShowVisibleCells) + Q_PROPERTY(bool showEmptyCells MEMBER showEmptyCells WRITE setShowEmptyCells) Q_PROPERTY(bool freezeFrustum MEMBER freezeFrustum WRITE setFreezeFrustum) public: - DrawSceneOctreeConfig() : Job::Config(true) {} // FIXME FOR debug + DrawSceneOctreeConfig() : Job::Config(false) {} - bool showVisibleCells{ true }; // FIXME FOR debug + bool showVisibleCells{ true }; + bool showEmptyCells{ false }; bool freezeFrustum{ false }; public slots: - void setShowVisibleCells(bool enabled) { showVisibleCells = enabled; emit dirty(); } - void setFreezeFrustum(bool enabled) { freezeFrustum = enabled; emit dirty(); } + void setShowVisibleCells(bool show) { showVisibleCells = show; emit dirty(); } + void setShowEmptyCells(bool show) { showEmptyCells = show; emit dirty(); } + void setFreezeFrustum(bool freeze) { freezeFrustum = freeze; emit dirty(); } signals: void dirty(); @@ -44,8 +48,12 @@ namespace render { gpu::PipelinePointer _drawLODReticlePipeline; + int _drawItemBoundPosLoc = -1; + int _drawItemBoundDimLoc = -1; + gpu::PipelinePointer _drawItemBoundPipeline; bool _showVisibleCells; // initialized by Config + bool _showEmptyCells; // initialized by Config bool _freezeFrustum{ false }; // initialized by Config bool _justFrozeFrustum{ false }; ViewFrustum _frozenFrutstum; @@ -61,6 +69,57 @@ namespace render { const gpu::PipelinePointer getDrawCellBoundsPipeline(); const gpu::PipelinePointer getDrawLODReticlePipeline(); + const gpu::PipelinePointer getDrawItemBoundPipeline(); + }; + + + class DrawItemSelectionConfig : public Job::Config { + Q_OBJECT + Q_PROPERTY(bool enabled MEMBER enabled NOTIFY dirty()) + Q_PROPERTY(bool showInsideItems MEMBER showInsideItems WRITE setShowInsideItems) + Q_PROPERTY(bool showInsideSubcellItems MEMBER showInsideSubcellItems WRITE setShowInsideSubcellItems) + Q_PROPERTY(bool showPartialItems MEMBER showPartialItems WRITE setShowPartialItems) + Q_PROPERTY(bool showPartialSubcellItems MEMBER showPartialSubcellItems WRITE setShowPartialSubcellItems) + public: + + DrawItemSelectionConfig() : Job::Config(false) {} + + bool showInsideItems{ true }; + bool showInsideSubcellItems{ true }; + bool showPartialItems{ true }; + bool showPartialSubcellItems{ true }; + + public slots: + void setShowInsideItems(bool show) { showInsideItems = show; emit dirty(); } + void setShowInsideSubcellItems(bool show) { showInsideSubcellItems = show; emit dirty(); } + void setShowPartialItems(bool show) { showPartialItems = show; emit dirty(); } + void setShowPartialSubcellItems(bool show) { showPartialSubcellItems = show; emit dirty(); } + + signals: + void dirty(); + }; + + class DrawItemSelection { + + int _drawItemBoundPosLoc = -1; + int _drawItemBoundDimLoc = -1; + gpu::PipelinePointer _drawItemBoundPipeline; + + bool _showInsideItems; // initialized by Config + bool _showInsideSubcellItems; // initialized by Config + bool _showPartialItems; // initialized by Config + bool _showPartialSubcellItems; // initialized by Config + + public: + using Config = DrawItemSelectionConfig; + using JobModel = Job::ModelI; + + DrawItemSelection() {} + + void configure(const Config& config); + void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemSpatialTree::ItemSelection& selection); + + const gpu::PipelinePointer getDrawItemBoundPipeline(); }; } diff --git a/libraries/render/src/render/Octree.cpp b/libraries/render/src/render/Octree.cpp index 3ccad4d7b3..c78013c19d 100644 --- a/libraries/render/src/render/Octree.cpp +++ b/libraries/render/src/render/Octree.cpp @@ -243,9 +243,8 @@ ItemSpatialTree::Index ItemSpatialTree::resetItem(Index oldCell, const ItemKey& // Compare range size vs cell location size and tag itemKey accordingly auto rangeSizef = maxCoordf - minCoordf; - const float SQRT_OF_3 = 1.73205; - float cellDiagonalSquare = 3 * (float)getDepthDimension(location.depth); - bool subcellItem = glm::dot(rangeSizef, rangeSizef) < cellDiagonalSquare; + float cellFitSize = getCellHalfDiagonalSquare(location.depth); + bool subcellItem = glm::dot(rangeSizef, rangeSizef) < cellFitSize; newKey.setSmaller(subcellItem); auto newCell = indexCell(location); @@ -255,6 +254,7 @@ ItemSpatialTree::Index ItemSpatialTree::resetItem(Index oldCell, const ItemKey& // Did the key changed, if yes update if (newKey._flags != oldKey._flags) { updateItem(newCell, oldKey, newKey, item); + return newCell; } return newCell; } diff --git a/libraries/render/src/render/Octree.h b/libraries/render/src/render/Octree.h index 6309ea8d6b..356da469ff 100644 --- a/libraries/render/src/render/Octree.h +++ b/libraries/render/src/render/Octree.h @@ -327,6 +327,11 @@ namespace render { float getCellWidth(Depth depth) const { return (float) _size * getInvDepthDimension(depth); } float getInvCellWidth(Depth depth) const { return (float) getDepthDimension(depth) * _invSize; } + float getCellHalfDiagonalSquare(Depth depth) const { + float cellHalfWidth = 0.5f * getCellWidth(depth); + return 3.0f * cellHalfWidth * cellHalfWidth; + } + glm::vec3 evalPos(const Coord3& coord, Depth depth = Octree::METRIC_COORD_DEPTH) const { return getOrigin() + glm::vec3(coord) * getCellWidth(depth); } From 91a28c597a07806f2b19604c593f6714e9b866bf Mon Sep 17 00:00:00 2001 From: samcake Date: Fri, 5 Feb 2016 17:18:14 -0800 Subject: [PATCH 27/49] Quiet warnings --- libraries/render/src/render/Octree.cpp | 22 +++++++++++----------- libraries/render/src/render/Octree.h | 8 ++++---- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/libraries/render/src/render/Octree.cpp b/libraries/render/src/render/Octree.cpp index c78013c19d..f96f691f38 100644 --- a/libraries/render/src/render/Octree.cpp +++ b/libraries/render/src/render/Octree.cpp @@ -116,7 +116,7 @@ Octree::Index Octree::allocateCell(Index parent, const Location& location) { assert(_cells[parent].getlocation().child(location.octant()) == location); - auto newIndex = _cells.size(); + auto newIndex = (Index) _cells.size(); _cells.push_back(Cell(parent, location)); _cells[parent].setChild(location.octant(), newIndex); @@ -125,13 +125,13 @@ Octree::Index Octree::allocateCell(Index parent, const Location& location) { Octree::Indices Octree::indexCellPath(const Locations& path) { - // First through the aallocated cells + // First through the allocated cells Indices cellPath = indexConcreteCellPath(path); // Catch up from the last allocated cell on the path auto currentIndex = cellPath.back(); - for (int l = cellPath.size(); l < path.size(); l++) { + for (int l = (Index) cellPath.size(); l < (Index) path.size(); l++) { auto& location = path[l]; // Allocate the new index & connect it to the parent @@ -147,7 +147,7 @@ Octree::Indices Octree::indexCellPath(const Locations& path) { Octree::Index Octree::allocateBrick() { - Index brickIdx = _bricks.size(); + Index brickIdx = (int) _bricks.size(); _bricks.push_back(Brick()); return brickIdx; } @@ -341,7 +341,7 @@ Octree::Location::Intersection Octree::Location::intersectCell(const Location& c } int Octree::selectTraverse(Index cellID, CellSelection& selection, const FrustumSelector& selector) const { - int numSelectedsIn = selection.size(); + int numSelectedsIn = (int) selection.size(); auto cell = getConcreteCell(cellID); auto cellLocation = cell.getlocation(); @@ -382,12 +382,12 @@ int Octree::selectTraverse(Index cellID, CellSelection& selection, const Frustum } } - return selection.size() - numSelectedsIn; + return (int) selection.size() - numSelectedsIn; } int Octree::selectBranch(Index cellID, CellSelection& selection, const FrustumSelector& selector) const { - int numSelectedsIn = selection.size(); + int numSelectedsIn = (int) selection.size(); auto cell = getConcreteCell(cellID); auto cellLocation = cell.getlocation(); @@ -407,11 +407,11 @@ int Octree::selectBranch(Index cellID, CellSelection& selection, const FrustumS } } - return selection.size() - numSelectedsIn; + return (int) selection.size() - numSelectedsIn; } int Octree::selectCellBrick(Index cellID, CellSelection& selection, bool inside) const { - int numSelectedsIn = selection.size(); + int numSelectedsIn = (int) selection.size(); auto cell = getConcreteCell(cellID); selection.cells(inside).push_back(cellID); @@ -420,7 +420,7 @@ int Octree::selectCellBrick(Index cellID, CellSelection& selection, bool inside) selection.bricks(inside).push_back(cell.brick()); } - return selection.size() - numSelectedsIn; + return (int) selection.size() - numSelectedsIn; } @@ -459,5 +459,5 @@ int ItemSpatialTree::selectCellItems(ItemSelection& selection, const ItemFilter& selection.partialSubcellItems.insert(selection.partialSubcellItems.end(), brickSubcellItems.begin(), brickSubcellItems.end()); } - return selection.numItems(); + return (int) selection.numItems(); } diff --git a/libraries/render/src/render/Octree.h b/libraries/render/src/render/Octree.h index 356da469ff..c22313afe3 100644 --- a/libraries/render/src/render/Octree.h +++ b/libraries/render/src/render/Octree.h @@ -262,7 +262,7 @@ namespace render { Indices& cells(bool inside) { return (inside ? insideCells : partialCells); } Indices& bricks(bool inside) { return (inside ? insideBricks : partialBricks); } - int size() const { return insideBricks.size() + partialBricks.size(); } + size_t size() const { return insideBricks.size() + partialBricks.size(); } void clear() { insideCells.clear(); @@ -380,9 +380,9 @@ namespace render { ItemIDs& items(bool inside) { return (inside ? insideItems : partialItems); } ItemIDs& subcellItems(bool inside) { return (inside ? insideSubcellItems : partialSubcellItems); } - int insideNumItems() const { return insideItems.size() + insideSubcellItems.size(); } - int partialNumItems() const { return partialItems.size() + partialSubcellItems.size(); } - int numItems() const { return insideNumItems() + partialNumItems(); } + size_t insideNumItems() const { return insideItems.size() + insideSubcellItems.size(); } + size_t partialNumItems() const { return partialItems.size() + partialSubcellItems.size(); } + size_t numItems() const { return insideNumItems() + partialNumItems(); } void clear() { cellSelection.clear(); From 76d8135fdc452e02f3178e7e3f469ecefa874e1c Mon Sep 17 00:00:00 2001 From: samcake Date: Fri, 5 Feb 2016 18:21:21 -0800 Subject: [PATCH 28/49] Trying to introduce the Filtering step after the full scene culling --- .../render-utils/src/RenderDeferredTask.cpp | 11 +++++--- libraries/render/src/render/CullTask.cpp | 25 +++++++++++++++++-- libraries/render/src/render/CullTask.h | 25 ++++++++++++++++++- 3 files changed, 54 insertions(+), 7 deletions(-) diff --git a/libraries/render-utils/src/RenderDeferredTask.cpp b/libraries/render-utils/src/RenderDeferredTask.cpp index 654e797e1b..86b433e191 100755 --- a/libraries/render-utils/src/RenderDeferredTask.cpp +++ b/libraries/render-utils/src/RenderDeferredTask.cpp @@ -66,8 +66,11 @@ RenderDeferredTask::RenderDeferredTask(CullFunctor cullFunctor) { const auto transparents = addJob("DepthSortTransparent", culledTransparents, DepthSortItems(false)); */ // CPU: Fetch the renderOpaques - const auto opaqueSelection = addJob("FetchOpaque"); - const auto culledOpaques = addJob("CullOpaque", opaqueSelection, cullFunctor); + auto sceneFilter = ItemFilter::Builder::opaqueShape().withTransparent().withTypeLight(); + const auto sceneSelection = addJob("FetchSceneSelection", sceneFilter); + const auto culledSceneSelection = addJob("CullSceneSelection", sceneSelection, cullFunctor, RenderDetails::OPAQUE_ITEM, ItemFilter::Builder::opaqueShape().withoutLayered()); + + const auto culledOpaques = addJob("FilterOpaque", culledSceneSelection); const auto opaques = addJob("DepthSortOpaque", culledOpaques); // CPU only, create the list of renderedTransparents items @@ -112,8 +115,8 @@ RenderDeferredTask::RenderDeferredTask(CullFunctor cullFunctor) { // Scene Octree Debuging job { - addJob("DrawSceneOctree", opaqueSelection); - addJob("DrawItemSelection", opaqueSelection); + addJob("DrawSceneOctree", sceneSelection); + addJob("DrawItemSelection", sceneSelection); } // Status icon rendering job diff --git a/libraries/render/src/render/CullTask.cpp b/libraries/render/src/render/CullTask.cpp index 580084cb86..a1343318ca 100644 --- a/libraries/render/src/render/CullTask.cpp +++ b/libraries/render/src/render/CullTask.cpp @@ -193,7 +193,8 @@ void FetchSpatialTree::run(const SceneContextPointer& sceneContext, const Render void CullSpatialSelection::configure(const Config& config) { } -void CullSpatialSelection::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemSpatialTree::ItemSelection& inSelection, ItemBounds& outItems) { +void CullSpatialSelection::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, + const ItemSpatialTree::ItemSelection& inSelection, ItemBounds& outItems) { assert(renderContext->args); assert(renderContext->args->_viewFrustum); RenderArgs* args = renderContext->args; @@ -236,7 +237,6 @@ void CullSpatialSelection::run(const SceneContextPointer& sceneContext, const Re Test test(_cullFunctor, args, details); // Now we have a selection of items to render - outItems.clear(); outItems.reserve(inSelection.numItems()); @@ -293,3 +293,24 @@ void CullSpatialSelection::run(const SceneContextPointer& sceneContext, const Re std::static_pointer_cast(renderContext->jobConfig)->numItems = (int)outItems.size(); } + +void FilterItemSelection::configure(const Config& config) { +} + +void FilterItemSelection::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemBounds& inItems, ItemBounds& outItems) { + assert(renderContext->args); + assert(renderContext->args->_viewFrustum); + RenderArgs* args = renderContext->args; + auto& scene = sceneContext->_scene; + + // Now we have a selection of items to render + outItems.clear(); + outItems.reserve(inItems.size()); + + for (auto itemBound : inItems) { + auto& item = scene->getItem(itemBound.id); + if (_filter.test(item.getKey())) { + outItems.emplace_back(itemBound); + } + } +} diff --git a/libraries/render/src/render/CullTask.h b/libraries/render/src/render/CullTask.h index d17e95b5a8..795dd103e9 100644 --- a/libraries/render/src/render/CullTask.h +++ b/libraries/render/src/render/CullTask.h @@ -131,7 +131,30 @@ namespace render { ItemFilter _filter{ ItemFilter::Builder::opaqueShape().withoutLayered() }; void configure(const Config& config); - void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemSpatialTree::ItemSelection& outSelection, ItemBounds& outItems); + void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemSpatialTree::ItemSelection& inSelection, ItemBounds& outItems); + }; + + class FilterItemSelectionConfig : public Job::Config { + Q_OBJECT + Q_PROPERTY(int numItems READ getNumItems) + public: + int numItems{ 0 }; + int getNumItems() { return numItems; } + }; + + class FilterItemSelection { + public: + using Config = FilterItemSelectionConfig; + using JobModel = Job::ModelIO; + + FilterItemSelection() {} + FilterItemSelection(const ItemFilter& filter) : + _filter(filter) {} + + ItemFilter _filter{ ItemFilter::Builder::opaqueShape().withoutLayered() }; + + void configure(const Config& config); + void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemBounds& inItems, ItemBounds& outItems); }; class DepthSortItems { From 40e49b2376a90cf7184588cb7e09fb98d071e103 Mon Sep 17 00:00:00 2001 From: samcake Date: Mon, 8 Feb 2016 18:23:29 -0800 Subject: [PATCH 29/49] Starting to remove empty bricks and empty cells --- .../utilities/tools/debugRenderCulling.js | 18 ++++ .../render/src/render/DrawSceneOctree.cpp | 19 +++- libraries/render/src/render/DrawSceneOctree.h | 10 ++ libraries/render/src/render/Octree.cpp | 95 +++++++++++++++++-- libraries/render/src/render/Octree.h | 50 +++++++--- .../render/src/render/drawItemBounds.slf | 8 +- .../render/src/render/drawItemBounds.slv | 4 +- 7 files changed, 179 insertions(+), 25 deletions(-) diff --git a/examples/utilities/tools/debugRenderCulling.js b/examples/utilities/tools/debugRenderCulling.js index 4579f434d0..3d260fc95c 100644 --- a/examples/utilities/tools/debugRenderCulling.js +++ b/examples/utilities/tools/debugRenderCulling.js @@ -55,6 +55,24 @@ panel.newCheckbox("Show Partial Subcell Items", function(value) { return (value); } ); +/* +panel.newSlider('Cells Free / Allocated', -1, 1, + function(value) { value; }, // setter + function() { return Render.RenderDeferredTask.DrawSceneOctree.numFreeCells; }, // getter + function(value) { return value; }); + +this.update = function () { + var numFree = Render.RenderDeferredTask.DrawSceneOctree.numFreeCells; + var numAlloc = Render.RenderDeferredTask.DrawSceneOctree.numAllocatedCells; + var title = [ + ' ' + name, + numFree + ' / ' + numAlloc + ].join('\t'); + + widget.editTitle({ text: title }); + slider.setMaxValue(numAlloc); +}; +*/ function mouseMoveEvent(event) { panel.mouseMoveEvent(event); } diff --git a/libraries/render/src/render/DrawSceneOctree.cpp b/libraries/render/src/render/DrawSceneOctree.cpp index f60b20a69f..c62a77288e 100644 --- a/libraries/render/src/render/DrawSceneOctree.cpp +++ b/libraries/render/src/render/DrawSceneOctree.cpp @@ -93,6 +93,10 @@ void DrawSceneOctree::run(const SceneContextPointer& sceneContext, RenderArgs* args = renderContext->args; auto& scene = sceneContext->_scene; + std::static_pointer_cast(renderContext->jobConfig)->numAllocatedCells = (int)scene->getSpatialTree().getNumAllocatedCells(); + std::static_pointer_cast(renderContext->jobConfig)->numFreeCells = (int)scene->getSpatialTree().getNumFreeCells(); + + gpu::doInBatch(args->_context, [&](gpu::Batch& batch) { glm::mat4 projMat; Transform viewMat; @@ -171,6 +175,8 @@ const gpu::PipelinePointer DrawItemSelection::getDrawItemBoundPipeline() { _drawItemBoundPosLoc = program->getUniforms().findLocation("inBoundPos"); _drawItemBoundDimLoc = program->getUniforms().findLocation("inBoundDim"); + _drawCellLocationLoc = program->getUniforms().findLocation("inCellLocation"); + auto state = std::make_shared(); state->setDepthTest(true, false, gpu::LESS_EQUAL); @@ -217,7 +223,10 @@ void DrawItemSelection::run(const SceneContextPointer& sceneContext, for (const auto& itemID : inSelection.insideItems) { auto& item = scene->getItem(itemID); auto itemBound = item.getBound(); + auto itemCell = scene->getSpatialTree().getCellLocation(item.getCell()); + glm::ivec4 cellLocation(0, 0, 0, itemCell.depth); + batch._glUniform4iv(_drawCellLocationLoc, 1, ((const int*)(&cellLocation))); batch._glUniform3fv(_drawItemBoundPosLoc, 1, (const float*)(&itemBound.getCorner())); batch._glUniform3fv(_drawItemBoundDimLoc, 1, (const float*)(&itemBound.getScale())); @@ -229,7 +238,10 @@ void DrawItemSelection::run(const SceneContextPointer& sceneContext, for (const auto& itemID : inSelection.insideSubcellItems) { auto& item = scene->getItem(itemID); auto itemBound = item.getBound(); + auto itemCell = scene->getSpatialTree().getCellLocation(item.getCell()); + glm::ivec4 cellLocation(0, 0, 1, itemCell.depth); + batch._glUniform4iv(_drawCellLocationLoc, 1, ((const int*)(&cellLocation))); batch._glUniform3fv(_drawItemBoundPosLoc, 1, (const float*)(&itemBound.getCorner())); batch._glUniform3fv(_drawItemBoundDimLoc, 1, (const float*)(&itemBound.getScale())); @@ -241,7 +253,10 @@ void DrawItemSelection::run(const SceneContextPointer& sceneContext, for (const auto& itemID : inSelection.partialItems) { auto& item = scene->getItem(itemID); auto itemBound = item.getBound(); + auto itemCell = scene->getSpatialTree().getCellLocation(item.getCell()); + glm::ivec4 cellLocation(0, 0, 0, itemCell.depth); + batch._glUniform4iv(_drawCellLocationLoc, 1, ((const int*)(&cellLocation))); batch._glUniform3fv(_drawItemBoundPosLoc, 1, (const float*)(&itemBound.getCorner())); batch._glUniform3fv(_drawItemBoundDimLoc, 1, (const float*)(&itemBound.getScale())); @@ -253,7 +268,9 @@ void DrawItemSelection::run(const SceneContextPointer& sceneContext, for (const auto& itemID : inSelection.partialSubcellItems) { auto& item = scene->getItem(itemID); auto itemBound = item.getBound(); - + auto itemCell = scene->getSpatialTree().getCellLocation(item.getCell()); + glm::ivec4 cellLocation(0, 0, 1, itemCell.depth); + batch._glUniform4iv(_drawCellLocationLoc, 1, ((const int*)(&cellLocation))); batch._glUniform3fv(_drawItemBoundPosLoc, 1, (const float*)(&itemBound.getCorner())); batch._glUniform3fv(_drawItemBoundDimLoc, 1, (const float*)(&itemBound.getScale())); diff --git a/libraries/render/src/render/DrawSceneOctree.h b/libraries/render/src/render/DrawSceneOctree.h index 7301a776f3..63e7203b38 100644 --- a/libraries/render/src/render/DrawSceneOctree.h +++ b/libraries/render/src/render/DrawSceneOctree.h @@ -23,6 +23,9 @@ namespace render { Q_PROPERTY(bool showVisibleCells MEMBER showVisibleCells WRITE setShowVisibleCells) Q_PROPERTY(bool showEmptyCells MEMBER showEmptyCells WRITE setShowEmptyCells) Q_PROPERTY(bool freezeFrustum MEMBER freezeFrustum WRITE setFreezeFrustum) + Q_PROPERTY(int numAllocatedCells READ getNumAllocatedCells) + Q_PROPERTY(int numFreeCells READ getNumFreeCells) + public: DrawSceneOctreeConfig() : Job::Config(false) {} @@ -31,6 +34,12 @@ namespace render { bool showEmptyCells{ false }; bool freezeFrustum{ false }; + int numAllocatedCells{ 0 }; + int numFreeCells{ 0 }; + + int getNumAllocatedCells() const { return numAllocatedCells; } + int getNumFreeCells() const { return numFreeCells; } + public slots: void setShowVisibleCells(bool show) { showVisibleCells = show; emit dirty(); } void setShowEmptyCells(bool show) { showEmptyCells = show; emit dirty(); } @@ -103,6 +112,7 @@ namespace render { int _drawItemBoundPosLoc = -1; int _drawItemBoundDimLoc = -1; + int _drawCellLocationLoc = -1; gpu::PipelinePointer _drawItemBoundPipeline; bool _showInsideItems; // initialized by Config diff --git a/libraries/render/src/render/Octree.cpp b/libraries/render/src/render/Octree.cpp index f96f691f38..ef2f10e983 100644 --- a/libraries/render/src/render/Octree.cpp +++ b/libraries/render/src/render/Octree.cpp @@ -109,21 +109,70 @@ Octree::Indices Octree::indexConcreteCellPath(const Locations& path) const { Octree::Index Octree::allocateCell(Index parent, const Location& location) { - if (_cells[parent].asChild(location.octant())) { + if (_cells[parent].hasChild(location.octant())) { assert(_cells[parent].child(location.octant()) == INVALID); return _cells[parent].child(location.octant()); } assert(_cells[parent].getlocation().child(location.octant()) == location); - auto newIndex = (Index) _cells.size(); - _cells.push_back(Cell(parent, location)); - _cells[parent].setChild(location.octant(), newIndex); + Index newIndex; + if (_freeCells.empty()) { + newIndex = (Index)_cells.size(); + _cells.push_back(Cell(parent, location)); + } else { + newIndex = _freeCells.back(); + _freeCells.pop_back(); + _cells[newIndex] = Cell(parent, location); + } + _cells[parent].setChild(location.octant(), newIndex); return newIndex; } +void Octree::freeCell(Index index) { + if (checkCellIndex(index)) { + auto & cell = _cells[index]; + cell.free(); + _freeCells.push_back(index); + } +} + +void Octree::clearCell(Index index) { + auto& cell = editCell(index); + + // Free the brick + if (cell.hasBrick()) { + freeBrick(cell.brick()); + cell.setBrick(INVALID); + } + + // Free the cell ? + Index parentIdx = cell.parent(); + if (!cell.hasParent()) { + if (index == ROOT_CELL) { + // Stop here, this is the root cell! + return; + } else { + // THis is not expected + assert(false); + return; + } + } + + bool traverseParent = false; + if (!cell.hasChildren()) { + editCell(parentIdx).setChild(cell.getlocation().octant(), INVALID); + freeCell(index); + traverseParent = true; + } + + if (traverseParent) { + clearCell(parentIdx); + } +} + Octree::Indices Octree::indexCellPath(const Locations& path) { // First through the allocated cells Indices cellPath = indexConcreteCellPath(path); @@ -147,13 +196,27 @@ Octree::Indices Octree::indexCellPath(const Locations& path) { Octree::Index Octree::allocateBrick() { - Index brickIdx = (int) _bricks.size(); - _bricks.push_back(Brick()); - return brickIdx; + if (_freeBricks.empty()) { + Index brickIdx = (int)_bricks.size(); + _bricks.push_back(Brick()); + return brickIdx; + } else { + Index brickIdx = _freeBricks.back(); + _freeBricks.pop_back(); + return brickIdx; + } +} + +void Octree::freeBrick(Index index) { + if (checkCellIndex(index)) { + auto & brick = _bricks[index]; + // brick.free(); + _freeBricks.push_back(index); + } } Octree::Index Octree::accessCellBrick(Index cellID, const CellBrickAccessor& accessor, bool createBrick) { - assert(cellID != INVALID); + assert(checkCellIndex(cellID)); auto& cell = editCell(cellID); if (!cell.hasBrick()) { if (!createBrick) { @@ -220,6 +283,7 @@ bool ItemSpatialTree::removeItem(Index cellIdx, const ItemKey& key, const ItemID auto success = false; // Remove the item from the brick + bool emptyCell = false; accessCellBrick(cellIdx, [&](Cell& cell, Brick& brick, Octree::Index brickID) { auto& itemList = (key.isSmall() ? brick.subcellItems : brick.items); @@ -227,10 +291,16 @@ bool ItemSpatialTree::removeItem(Index cellIdx, const ItemKey& key, const ItemID if (brick.items.empty() && brick.subcellItems.empty()) { cell.signalBrickEmpty(); + emptyCell = true; } success = true; }, false); // do not create brick! + + if (emptyCell) { + clearCell(cellIdx); + } + return success; } @@ -245,7 +315,11 @@ ItemSpatialTree::Index ItemSpatialTree::resetItem(Index oldCell, const ItemKey& auto rangeSizef = maxCoordf - minCoordf; float cellFitSize = getCellHalfDiagonalSquare(location.depth); bool subcellItem = glm::dot(rangeSizef, rangeSizef) < cellFitSize; - newKey.setSmaller(subcellItem); + if (subcellItem) { + newKey.setSmaller(subcellItem); + } else { + newKey.setSmaller(false); + } auto newCell = indexCell(location); @@ -461,3 +535,6 @@ int ItemSpatialTree::selectCellItems(ItemSelection& selection, const ItemFilter& return (int) selection.numItems(); } + + + diff --git a/libraries/render/src/render/Octree.h b/libraries/render/src/render/Octree.h index c22313afe3..7602706ff3 100644 --- a/libraries/render/src/render/Octree.h +++ b/libraries/render/src/render/Octree.h @@ -172,13 +172,23 @@ namespace render { // the cell description class Cell { public: + void free() { _location = Location(); for (auto& link : _links) { link = INVALID; } } + const Location& getlocation() const { return _location; } Index parent() const { return _links[Parent]; } - bool asParent() const { return parent() != INVALID; } + bool hasParent() const { return parent() != INVALID; } Index child(Link octant) const { return _links[octant]; } - bool asChild(Link octant) const { return child(octant) != INVALID; } + bool hasChild(Link octant) const { return child(octant) != INVALID; } + bool hasChildren() const { + for (LinkStorage octant = Octant_L_B_N; octant < NUM_OCTANTS; octant++) { + if (hasChild((Link)octant)) { + return true; + } + } + return false; + } void setChild(Link octant, Index child) { _links[octant] = child; } Index brick() const { return _links[BrickLink]; } @@ -205,13 +215,17 @@ namespace render { using Bricks = std::vector< Brick >; - - // Octree members - Cells _cells = Cells(1, Cell()); // start with only the Cell root - Bricks _bricks; + bool checkCellIndex(Index index) const { return (index >= 0) && (index < _cells.size()); } + bool checkBrickIndex(Index index) const { return (index >= 0) && (index < _bricks.size()); } Octree() {}; + // Clear a cell: + // Check that the cell brick is empty, if so free it + // CHeck that the cell has no children, if so free itself + // Apply the same logic to the parent cell + void clearCell(Index index); + // Indexing/Allocating the cells as the tree gets populated // Return the cell Index/Indices at the specified location/path, allocate all the cells on the path from the root if needed Indices indexCellPath(const Locations& path); @@ -223,7 +237,7 @@ namespace render { // Get the cell location from the CellID Location getCellLocation(Index cellID) const { - if ((cellID >= 0) && (cellID < _cells.size())) { + if (checkCellIndex(cellID)) { return getConcreteCell(cellID).getlocation(); } return Location(); @@ -231,12 +245,10 @@ namespace render { // Reach a concrete cell const Cell& getConcreteCell(Index index) const { - assert(index < _cells.size()); + assert(checkCellIndex(index)); return _cells[index]; } - - // Let s talk about the Cell Bricks now using CellBrickAccessor = std::function; @@ -244,9 +256,8 @@ namespace render { // This returns the Brick index Index accessCellBrick(Index cellID, const CellBrickAccessor& accessor, bool createBrick = true); - const Brick& getConcreteBrick(Index index) const { - assert(index < _bricks.size()); + assert(checkBrickIndex(index)); return _bricks[index]; } @@ -297,15 +308,28 @@ namespace render { int selectBranch(Index cellID, CellSelection& selection, const FrustumSelector& selector) const; int selectCellBrick(Index cellID, CellSelection& selection, bool inside) const; + + int getNumAllocatedCells() const { return (int)_cells.size(); } + int getNumFreeCells() const { return (int)_freeCells.size(); } + protected: Index allocateCell(Index parent, const Location& location); + void freeCell(Index index); + Index allocateBrick(); + void freeBrick(Index index); Cell& editCell(Index index) { - assert(index < _cells.size()); + assert(checkCellIndex(index)); return _cells[index]; } + + // Octree members + Cells _cells = Cells(1, Cell()); // start with only the Cell root + Bricks _bricks; + Indices _freeCells; // stack of free cells to be reused for allocation + Indices _freeBricks; // stack of free bricks to be reused for allocation }; } diff --git a/libraries/render/src/render/drawItemBounds.slf b/libraries/render/src/render/drawItemBounds.slf index 6f5db0a6c2..4fb23df8f6 100644 --- a/libraries/render/src/render/drawItemBounds.slf +++ b/libraries/render/src/render/drawItemBounds.slf @@ -19,5 +19,11 @@ out vec4 outFragColor; void main(void) { float var = step(fract(varTexcoord.x * varTexcoord.y * 1.0), 0.5); - outFragColor = vec4(mix(vec3(1.0), varColor.xyz, var), varColor.a); + if (varColor.a == 0) { + outFragColor = vec4(mix(vec3(0.0), varColor.xyz, var), mix(0.0, 1.0, var)); + + } else { + outFragColor = vec4(mix(vec3(1.0), varColor.xyz, var), varColor.a); + } + } diff --git a/libraries/render/src/render/drawItemBounds.slv b/libraries/render/src/render/drawItemBounds.slv index 08c5fc7ed4..cf1eab7163 100644 --- a/libraries/render/src/render/drawItemBounds.slv +++ b/libraries/render/src/render/drawItemBounds.slv @@ -59,7 +59,9 @@ void main(void) { TransformObject obj = getTransformObject(); <$transformModelToClipPos(cam, obj, pos, gl_Position)$> - varColor = vec4(colorWheel(fract(float(inCellLocation.w) / 5.0)), 1.0); + bool subcell = bool((inCellLocation.z)); + float cellDepth = float(inCellLocation.w); + varColor = vec4(colorWheel(fract(cellDepth / 5.0)), 1.0 - float(subcell)); varTexcoord = vec2(cubeVec.w, length(inBoundDim)); } \ No newline at end of file From c96dd7f131c02154966694bde16cc1a5c5705ae9 Mon Sep 17 00:00:00 2001 From: samcake Date: Tue, 9 Feb 2016 15:51:38 -0800 Subject: [PATCH 30/49] Cleaning up of the octree as items are removed --- libraries/render/src/render/Octree.cpp | 92 ++++++++++++++----- libraries/render/src/render/Octree.h | 85 ++++++++++------- .../render/src/render/drawCellBounds.slv | 2 +- 3 files changed, 124 insertions(+), 55 deletions(-) diff --git a/libraries/render/src/render/Octree.cpp b/libraries/render/src/render/Octree.cpp index ef2f10e983..2885fc662f 100644 --- a/libraries/render/src/render/Octree.cpp +++ b/libraries/render/src/render/Octree.cpp @@ -95,7 +95,7 @@ Octree::Indices Octree::indexConcreteCellPath(const Locations& path) const { for (int l = 1; l < path.size(); l++) { auto& location = path[l]; auto nextIndex = getConcreteCell(currentIndex).child(location.octant()); - if (nextIndex == INVALID) { + if (nextIndex == INVALID_CELL) { break; } @@ -110,7 +110,7 @@ Octree::Indices Octree::indexConcreteCellPath(const Locations& path) const { Octree::Index Octree::allocateCell(Index parent, const Location& location) { if (_cells[parent].hasChild(location.octant())) { - assert(_cells[parent].child(location.octant()) == INVALID); + assert(_cells[parent].child(location.octant()) == INVALID_CELL); return _cells[parent].child(location.octant()); } @@ -119,6 +119,10 @@ Octree::Index Octree::allocateCell(Index parent, const Location& location) { Index newIndex; if (_freeCells.empty()) { newIndex = (Index)_cells.size(); + if (newIndex >= MAXIMUM_INDEX) { + // abort! we are trying to go overboard with the total number of allocated bricks + return INVALID_CELL; + } _cells.push_back(Cell(parent, location)); } else { newIndex = _freeCells.back(); @@ -139,15 +143,21 @@ void Octree::freeCell(Index index) { } } -void Octree::clearCell(Index index) { +void Octree::cleanCellBranch(Index index) { auto& cell = editCell(index); // Free the brick - if (cell.hasBrick()) { - freeBrick(cell.brick()); - cell.setBrick(INVALID); + if (cell.isBrickEmpty()) { + if (cell.hasBrick()) { + freeBrick(cell.brick()); + cell.setBrick(INVALID_CELL); + } + } else { + // If the brick is still filled, stop clearing + return; } + // Free the cell ? Index parentIdx = cell.parent(); if (!cell.hasParent()) { @@ -155,7 +165,7 @@ void Octree::clearCell(Index index) { // Stop here, this is the root cell! return; } else { - // THis is not expected + // This is not expected assert(false); return; } @@ -163,13 +173,13 @@ void Octree::clearCell(Index index) { bool traverseParent = false; if (!cell.hasChildren()) { - editCell(parentIdx).setChild(cell.getlocation().octant(), INVALID); + editCell(parentIdx).setChild(cell.getlocation().octant(), INVALID_CELL); freeCell(index); traverseParent = true; } if (traverseParent) { - clearCell(parentIdx); + cleanCellBranch(parentIdx); } } @@ -189,15 +199,28 @@ Octree::Indices Octree::indexCellPath(const Locations& path) { // One more cell index on the path, moving on currentIndex = newIndex; cellPath.push_back(currentIndex); + + // Except !!! if we actually couldn't allocate anymore + if (newIndex == INVALID_CELL) { + // no more cellID available, stop allocating + // THe last index added is INVALID_CELL so the caller will know we failed allocating everything + break; + } } return cellPath; } - Octree::Index Octree::allocateBrick() { if (_freeBricks.empty()) { Index brickIdx = (int)_bricks.size(); + if (brickIdx >= MAXIMUM_INDEX) { + // abort! we are trying to go overboard with the total number of allocated bricks + assert(false); + // This should never happen because Bricks are allocated along with the cells and there + // is already a cap on the cells allocation + return INVALID_CELL; + } _bricks.push_back(Brick()); return brickIdx; } else { @@ -208,7 +231,7 @@ Octree::Index Octree::allocateBrick() { } void Octree::freeBrick(Index index) { - if (checkCellIndex(index)) { + if (checkBrickIndex(index)) { auto & brick = _bricks[index]; // brick.free(); _freeBricks.push_back(index); @@ -218,18 +241,25 @@ void Octree::freeBrick(Index index) { Octree::Index Octree::accessCellBrick(Index cellID, const CellBrickAccessor& accessor, bool createBrick) { assert(checkCellIndex(cellID)); auto& cell = editCell(cellID); + + // Allocate a brick if needed if (!cell.hasBrick()) { if (!createBrick) { - return INVALID; + return INVALID_CELL; } - cell.setBrick(allocateBrick()); + auto newBrick = allocateBrick(); + if (newBrick == INVALID_CELL) { + // This should never happen but just in case... + return INVALID_CELL; + } + cell.setBrick(newBrick); } - // access the brick + // Access the brick auto brickID = cell.brick(); auto& brick = _bricks[brickID]; - // execute the accessor + // Execute the accessor accessor(cell, brick, brickID); return brickID; @@ -255,13 +285,17 @@ ItemSpatialTree::Index ItemSpatialTree::insertItem(Index cellIdx, const ItemKey& itemIn.push_back(item); - cell.signalBrickFilled(); + cell.setBrickFilled(); }, true); return cellIdx; } bool ItemSpatialTree::updateItem(Index cellIdx, const ItemKey& oldKey, const ItemKey& key, const ItemID& item) { + // In case we missed that one, nothing to do + if (cellIdx == INVALID_CELL) { + return true; + } auto success = false; // only if key changed @@ -280,6 +314,10 @@ bool ItemSpatialTree::updateItem(Index cellIdx, const ItemKey& oldKey, const Ite } bool ItemSpatialTree::removeItem(Index cellIdx, const ItemKey& key, const ItemID& item) { + // In case we missed that one, nothing to do + if (cellIdx == INVALID_CELL) { + return true; + } auto success = false; // Remove the item from the brick @@ -290,15 +328,15 @@ bool ItemSpatialTree::removeItem(Index cellIdx, const ItemKey& key, const ItemID itemList.erase(std::find(itemList.begin(), itemList.end(), item)); if (brick.items.empty() && brick.subcellItems.empty()) { - cell.signalBrickEmpty(); + cell.setBrickEmpty(); emptyCell = true; } success = true; }, false); // do not create brick! - + // Because we know the cell is now empty, lets try to clean the octree here if (emptyCell) { - clearCell(cellIdx); + cleanCellBranch(cellIdx); } return success; @@ -323,8 +361,16 @@ ItemSpatialTree::Index ItemSpatialTree::resetItem(Index oldCell, const ItemKey& auto newCell = indexCell(location); + // Did we fail finding a cell for the item? + if (newCell == INVALID_CELL) { + // Remove the item where it was + if (oldCell != INVALID_CELL) { + removeItem(oldCell, oldKey, item); + } + return newCell; + } // Staying in the same cell - if (newCell == oldCell) { + else if (newCell == oldCell) { // Did the key changed, if yes update if (newKey._flags != oldKey._flags) { updateItem(newCell, oldKey, newKey, item); @@ -333,7 +379,7 @@ ItemSpatialTree::Index ItemSpatialTree::resetItem(Index oldCell, const ItemKey& return newCell; } // do we know about this item ? - else if (oldCell == Item::INVALID_CELL) { + else if (oldCell == INVALID_CELL) { insertItem(newCell, newKey, item); return newCell; } @@ -449,7 +495,7 @@ int Octree::selectTraverse(Index cellID, CellSelection& selection, const Frustum // then traverse deeper for (int i = 0; i < NUM_OCTANTS; i++) { Index subCellID = cell.child((Link)i); - if (subCellID != INVALID) { + if (subCellID != INVALID_CELL) { selectTraverse(subCellID, selection, selector); } } @@ -476,7 +522,7 @@ int Octree::selectBranch(Index cellID, CellSelection& selection, const FrustumS // then traverse deeper for (int i = 0; i < NUM_OCTANTS; i++) { Index subCellID = cell.child((Link)i); - if (subCellID != INVALID) { + if (subCellID != INVALID_CELL) { selectBranch(subCellID, selection, selector); } } diff --git a/libraries/render/src/render/Octree.h b/libraries/render/src/render/Octree.h index 7602706ff3..63b5721d52 100644 --- a/libraries/render/src/render/Octree.h +++ b/libraries/render/src/render/Octree.h @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -165,23 +166,66 @@ namespace render { // Cell or Brick Indexing using Index = ItemCell; // int32_t - static const Index INVALID = -1; + static const Index INVALID_CELL = -1; static const Index ROOT_CELL = 0; + + // With a maximum of INT_MAX(2 ^ 31) cells in our octree + static const Index MAXIMUM_INDEX = INT_MAX; // For fun, test setting this to 100 and this should still works with only the allocated maximum number of cells + using Indices = std::vector; // the cell description class Cell { public: - void free() { _location = Location(); for (auto& link : _links) { link = INVALID; } } + + enum BitFlags : uint8_t { + HasChildren = 0x01, + BrickFilled = 0x02, + }; + + void free() { _location = Location(); for (auto& link : _links) { link = INVALID_CELL; } } const Location& getlocation() const { return _location; } Index parent() const { return _links[Parent]; } - bool hasParent() const { return parent() != INVALID; } + bool hasParent() const { return parent() != INVALID_CELL; } Index child(Link octant) const { return _links[octant]; } - bool hasChild(Link octant) const { return child(octant) != INVALID; } - bool hasChildren() const { + bool hasChild(Link octant) const { return child(octant) != INVALID_CELL; } + bool hasChildren() const { return (_location.spare & HasChildren); } + void setChild(Link octant, Index child) { + _links[octant] = child; + if (child != INVALID_CELL) { + _location.spare |= HasChildren; + } else { + if (!checkHasChildren()) { + _location.spare &= ~HasChildren; + } + } + } + + Index brick() const { return _links[BrickLink]; } + bool hasBrick() const { return _links[BrickLink] != INVALID_CELL; } + void setBrick(Index brick) { _links[BrickLink] = brick; } + + void setBrickFilled() { _location.spare |= BrickFilled; } + void setBrickEmpty() { _location.spare &= ~BrickFilled; } + bool isBrickEmpty() const { return !(_location.spare & BrickFilled); } + + Cell() : + _links({ { INVALID_CELL, INVALID_CELL, INVALID_CELL, INVALID_CELL, INVALID_CELL, INVALID_CELL, INVALID_CELL, INVALID_CELL, INVALID_CELL, INVALID_CELL } }) + {} + + Cell(Index parent, Location loc) : + _location(loc), + _links({ { INVALID_CELL, INVALID_CELL, INVALID_CELL, INVALID_CELL, INVALID_CELL, INVALID_CELL, INVALID_CELL, INVALID_CELL, parent, INVALID_CELL } }) + {} + + private: + std::array _links; + Location _location; + + bool checkHasChildren() const { for (LinkStorage octant = Octant_L_B_N; octant < NUM_OCTANTS; octant++) { if (hasChild((Link)octant)) { return true; @@ -189,42 +233,21 @@ namespace render { } return false; } - void setChild(Link octant, Index child) { _links[octant] = child; } - - Index brick() const { return _links[BrickLink]; } - bool hasBrick() const { return _links[BrickLink] != INVALID; } - void setBrick(Index brick) { _links[BrickLink] = brick; } - void signalBrickFilled() { _location.spare = 1; } - void signalBrickEmpty() { _location.spare = 0; } - bool isBrickEmpty() const { return _location.spare == 0; } - - Cell() : - _links({ { INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID } }) - {} - - Cell(Index parent, Location loc) : - _location(loc), - _links({ { INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, parent, INVALID } }) - {} - - private: - std::array _links; - Location _location; }; using Cells = std::vector< Cell >; using Bricks = std::vector< Brick >; bool checkCellIndex(Index index) const { return (index >= 0) && (index < _cells.size()); } - bool checkBrickIndex(Index index) const { return (index >= 0) && (index < _bricks.size()); } + bool checkBrickIndex(Index index) const { return ((index >= 0) && (index < _bricks.size())); } Octree() {}; - // Clear a cell: - // Check that the cell brick is empty, if so free it - // CHeck that the cell has no children, if so free itself + // Clean a cell branch starting from the leave: + // Check that the cell brick is empty, if so free it else stop + // Check that the cell has no children, if so free itself else stop // Apply the same logic to the parent cell - void clearCell(Index index); + void cleanCellBranch(Index index); // Indexing/Allocating the cells as the tree gets populated // Return the cell Index/Indices at the specified location/path, allocate all the cells on the path from the root if needed diff --git a/libraries/render/src/render/drawCellBounds.slv b/libraries/render/src/render/drawCellBounds.slv index 628d44c0b2..f50331f15c 100644 --- a/libraries/render/src/render/drawCellBounds.slv +++ b/libraries/render/src/render/drawCellBounds.slv @@ -63,5 +63,5 @@ void main(void) { TransformObject obj = getTransformObject(); <$transformModelToClipPos(cam, obj, pos, gl_Position)$> - varColor = vec4(colorWheel(fract(float(inCellLocation.w) / 5.0)), 0.5 + 0.4 * cellIsEmpty); + varColor = vec4(colorWheel(fract(float(inCellLocation.w) / 5.0)), 0.8 + 0.2 * cellIsEmpty); } \ No newline at end of file From 8a4e2cb0c5c4a077c7fc5e629a3c8593b2ae6be8 Mon Sep 17 00:00:00 2001 From: samcake Date: Tue, 9 Feb 2016 16:27:40 -0800 Subject: [PATCH 31/49] cleaning the warnings coming from the os side --- libraries/render/src/render/Octree.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/libraries/render/src/render/Octree.h b/libraries/render/src/render/Octree.h index 63b5721d52..c07224833b 100644 --- a/libraries/render/src/render/Octree.h +++ b/libraries/render/src/render/Octree.h @@ -217,8 +217,8 @@ namespace render { {} Cell(Index parent, Location loc) : - _location(loc), - _links({ { INVALID_CELL, INVALID_CELL, INVALID_CELL, INVALID_CELL, INVALID_CELL, INVALID_CELL, INVALID_CELL, INVALID_CELL, parent, INVALID_CELL } }) + _links({ { INVALID_CELL, INVALID_CELL, INVALID_CELL, INVALID_CELL, INVALID_CELL, INVALID_CELL, INVALID_CELL, INVALID_CELL, parent, INVALID_CELL } }), + _location(loc) {} private: @@ -238,8 +238,8 @@ namespace render { using Bricks = std::vector< Brick >; - bool checkCellIndex(Index index) const { return (index >= 0) && (index < _cells.size()); } - bool checkBrickIndex(Index index) const { return ((index >= 0) && (index < _bricks.size())); } + bool checkCellIndex(Index index) const { return (index >= 0) && (index < (Index) _cells.size()); } + bool checkBrickIndex(Index index) const { return ((index >= 0) && (index < (Index) _bricks.size())); } Octree() {}; From 5ef7a19d67be3f0e49b4f77a6d56dfd0ff77c4ab Mon Sep 17 00:00:00 2001 From: samcake Date: Tue, 9 Feb 2016 18:29:22 -0800 Subject: [PATCH 32/49] Drafting a solution to update the render items when ENtity is moving --- .../src/RenderableEntityItem.h | 22 ++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/libraries/entities-renderer/src/RenderableEntityItem.h b/libraries/entities-renderer/src/RenderableEntityItem.h index d7d8d65e3a..fc4d00f9a5 100644 --- a/libraries/entities-renderer/src/RenderableEntityItem.h +++ b/libraries/entities-renderer/src/RenderableEntityItem.h @@ -14,6 +14,8 @@ #include #include +#include "AbstractViewStateInterface.h" + // These or the icon "name" used by the render item status value, they correspond to the atlas texture used by the DrawItemStatus // job in the current rendering pipeline defined as of now (11/2015) in render-utils/RenderDeferredTask.cpp. @@ -35,6 +37,8 @@ public: typedef render::Payload Payload; typedef Payload::DataPointer Pointer; + int touch = 0; + EntityItemPointer entity; }; @@ -66,8 +70,23 @@ public: pendingChanges.removeItem(_myItem); } + void notifyChanged() { + if (_myItem == render::Item::INVALID_ITEM_ID) { + return; + } + + render::PendingChanges pendingChanges; + render::ScenePointer scene = AbstractViewStateInterface::instance()->getMain3DScene(); + + pendingChanges.updateItem(_myItem, [](RenderableEntityItemProxy& data) { + data.touch++; + }); + + scene->enqueuePendingChanges(pendingChanges); + } + private: - render::ItemID _myItem; + render::ItemID _myItem = render::Item::INVALID_ITEM_ID; }; @@ -75,6 +94,7 @@ private: public: \ virtual bool addToScene(EntityItemPointer self, std::shared_ptr scene, render::PendingChanges& pendingChanges) override { return _renderHelper.addToScene(self, scene, pendingChanges); } \ virtual void removeFromScene(EntityItemPointer self, std::shared_ptr scene, render::PendingChanges& pendingChanges) override { _renderHelper.removeFromScene(self, scene, pendingChanges); } \ + virtual void locationChanged() override { EntityItem::locationChanged(); _renderHelper.notifyChanged(); } \ private: \ SimpleRenderableEntityItem _renderHelper; From 51bacdebddf74724425746a9538b3a645dd15921 Mon Sep 17 00:00:00 2001 From: samcake Date: Wed, 10 Feb 2016 11:52:37 -0800 Subject: [PATCH 33/49] Trying to notify the entity changes to the render item correctly, need more work --- libraries/entities-renderer/src/RenderableEntityItem.h | 1 + libraries/entities/src/EntityItem.cpp | 6 +++++- libraries/entities/src/EntityItem.h | 7 +++++-- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableEntityItem.h b/libraries/entities-renderer/src/RenderableEntityItem.h index fc4d00f9a5..aaeecbdc0e 100644 --- a/libraries/entities-renderer/src/RenderableEntityItem.h +++ b/libraries/entities-renderer/src/RenderableEntityItem.h @@ -95,6 +95,7 @@ public: \ virtual bool addToScene(EntityItemPointer self, std::shared_ptr scene, render::PendingChanges& pendingChanges) override { return _renderHelper.addToScene(self, scene, pendingChanges); } \ virtual void removeFromScene(EntityItemPointer self, std::shared_ptr scene, render::PendingChanges& pendingChanges) override { _renderHelper.removeFromScene(self, scene, pendingChanges); } \ virtual void locationChanged() override { EntityItem::locationChanged(); _renderHelper.notifyChanged(); } \ + virtual void dimensionsChanged() override { EntityItem::dimensionsChanged(); _renderHelper.notifyChanged(); } \ private: \ SimpleRenderableEntityItem _renderHelper; diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index e56f2c267a..2793b68eaf 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -1192,7 +1192,6 @@ void EntityItem::setDimensions(const glm::vec3& value) { return; } setScale(value); - requiresRecalcBoxes(); } /// The maximum bounding cube for the entity, independent of it's rotation. @@ -1916,3 +1915,8 @@ void EntityItem::locationChanged() { requiresRecalcBoxes(); SpatiallyNestable::locationChanged(); // tell all the children, also } + +void EntityItem::dimensionsChanged() { + requiresRecalcBoxes(); + SpatiallyNestable::dimensionsChanged(); // Do what you have to do +} diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index c8b54bca87..6a02816681 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -250,8 +250,9 @@ public: const glm::vec3& getRegistrationPoint() const { return _registrationPoint; } /// registration point as ratio of entity /// registration point as ratio of entity - void setRegistrationPoint(const glm::vec3& value) - { _registrationPoint = glm::clamp(value, 0.0f, 1.0f); requiresRecalcBoxes(); } + void setRegistrationPoint(const glm::vec3& value) { + _registrationPoint = glm::clamp(value, 0.0f, 1.0f); dimensionsChanged(); // Registration Point affects the bounding box + } bool hasAngularVelocity() const { return getAngularVelocity() != ENTITY_ITEM_ZERO_VEC3; } @@ -405,6 +406,8 @@ protected: void setActionDataInternal(QByteArray actionData); virtual void locationChanged() override; + virtual void dimensionsChanged() override; + EntityTypes::EntityType _type; quint64 _lastSimulated; // last time this entity called simulate(), this includes velocity, angular velocity, // and physics changes From b295a3732e42a0897f42c4a15a9eab5376fe4544 Mon Sep 17 00:00:00 2001 From: samcake Date: Wed, 10 Feb 2016 12:18:46 -0800 Subject: [PATCH 34/49] Fix a bunch of warnings --- libraries/render/src/render/CullTask.cpp | 3 -- libraries/render/src/render/Octree.cpp | 53 ++++++++++++------------ libraries/render/src/render/Octree.h | 15 ++++--- 3 files changed, 35 insertions(+), 36 deletions(-) diff --git a/libraries/render/src/render/CullTask.cpp b/libraries/render/src/render/CullTask.cpp index a1343318ca..3f80a4c033 100644 --- a/libraries/render/src/render/CullTask.cpp +++ b/libraries/render/src/render/CullTask.cpp @@ -136,7 +136,6 @@ void FetchItems::configure(const Config& config) { void FetchItems::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, ItemBounds& outItems) { assert(renderContext->args); assert(renderContext->args->_viewFrustum); - RenderArgs* args = renderContext->args; auto& scene = sceneContext->_scene; outItems.clear(); @@ -199,7 +198,6 @@ void CullSpatialSelection::run(const SceneContextPointer& sceneContext, const Re assert(renderContext->args->_viewFrustum); RenderArgs* args = renderContext->args; auto& scene = sceneContext->_scene; - ViewFrustum* frustum = args->_viewFrustum; auto& details = args->_details.edit(_detailType); details._considered += inSelection.numItems(); @@ -300,7 +298,6 @@ void FilterItemSelection::configure(const Config& config) { void FilterItemSelection::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemBounds& inItems, ItemBounds& outItems) { assert(renderContext->args); assert(renderContext->args->_viewFrustum); - RenderArgs* args = renderContext->args; auto& scene = sceneContext->_scene; // Now we have a selection of items to render diff --git a/libraries/render/src/render/Octree.cpp b/libraries/render/src/render/Octree.cpp index 2885fc662f..5a9fe28b4a 100644 --- a/libraries/render/src/render/Octree.cpp +++ b/libraries/render/src/render/Octree.cpp @@ -16,23 +16,23 @@ using namespace render; -const double Octree::INV_DEPTH_DIM[] = { - 1.0, - 1.0 / 2.0, - 1.0 / 4.0, - 1.0 / 8.0, - 1.0 / 16.0, - 1.0 / 32.0, - 1.0 / 64.0, - 1.0 / 128.0, - 1.0 / 256.0, - 1.0 / 512.0, - 1.0 / 1024.0, - 1.0 / 2048.0, - 1.0 / 4096.0, - 1.0 / 8192.0, - 1.0 / 16384.0, - 1.0 / 32768.0 }; +const float Octree::INV_DEPTH_DIM[] = { + 1.0f, + 1.0f / 2.0f, + 1.0f / 4.0f, + 1.0f / 8.0f, + 1.0f / 16.0f, + 1.0f / 32.0f, + 1.0f / 64.0f, + 1.0f / 128.0f, + 1.0f / 256.0f, + 1.0f / 512.0f, + 1.0f / 1024.0f, + 1.0f / 2048.0f, + 1.0f / 4096.0f, + 1.0f / 8192.0f, + 1.0f / 16384.0f, + 1.0f / 32768.0f }; /* const float Octree::COORD_SUBCELL_WIDTH[] = { // 2 ^ MAX_DEPTH / 2 ^ (depth + 1) @@ -92,8 +92,7 @@ Octree::Indices Octree::indexConcreteCellPath(const Locations& path) const { Index currentIndex = ROOT_CELL; Indices cellPath(1, currentIndex); - for (int l = 1; l < path.size(); l++) { - auto& location = path[l]; + for (auto& location : path) { auto nextIndex = getConcreteCell(currentIndex).child(location.octant()); if (nextIndex == INVALID_CELL) { break; @@ -233,7 +232,7 @@ Octree::Index Octree::allocateBrick() { void Octree::freeBrick(Index index) { if (checkBrickIndex(index)) { auto & brick = _bricks[index]; - // brick.free(); + brick.free(); _freeBricks.push_back(index); } } @@ -280,7 +279,7 @@ Octree::Locations ItemSpatialTree::evalLocations(const ItemBounds& bounds) const ItemSpatialTree::Index ItemSpatialTree::insertItem(Index cellIdx, const ItemKey& key, const ItemID& item) { // Add the item to the brick (and a brick if needed) - auto brickID = accessCellBrick(cellIdx, [&](Cell& cell, Brick& brick, Octree::Index cellID) { + accessCellBrick(cellIdx, [&](Cell& cell, Brick& brick, Octree::Index cellID) { auto& itemIn = (key.isSmall() ? brick.subcellItems : brick.items); itemIn.push_back(item); @@ -302,7 +301,7 @@ bool ItemSpatialTree::updateItem(Index cellIdx, const ItemKey& oldKey, const Ite assert(oldKey != key); // Get to the brick where the item is and update where it s stored - auto brickID = accessCellBrick(cellIdx, [&](Cell& cell, Brick& brick, Octree::Index cellID) { + accessCellBrick(cellIdx, [&](Cell& cell, Brick& brick, Octree::Index cellID) { auto& itemIn = (key.isSmall() ? brick.subcellItems : brick.items); auto& itemOut = (oldKey.isSmall() ? brick.subcellItems : brick.items); @@ -417,9 +416,9 @@ Octree::Location::Intersection Octree::Location::intersectCell(const Location& c struct Tool { static int normalToIndex(const Coord3f& n) { int index = 0; - if (n.x >= 0.0) index |= 1; - if (n.y >= 0.0) index |= 2; - if (n.z >= 0.0) index |= 4; + if (n.x >= 0.0f) index |= 1; + if (n.y >= 0.0f) index |= 2; + if (n.z >= 0.0f) index |= 4; return index; } @@ -484,7 +483,7 @@ int Octree::selectTraverse(Index cellID, CellSelection& selection, const Frustum // Test for lod auto cellLocation = cell.getlocation(); float lod = selector.testSolidAngle(cellLocation.getCenter(), Octree::getCoordSubcellWidth(cellLocation.depth)); - if (lod < 0.0) { + if (lod < 0.0f) { return 0; break; } @@ -512,7 +511,7 @@ int Octree::selectBranch(Index cellID, CellSelection& selection, const FrustumS auto cellLocation = cell.getlocation(); float lod = selector.testSolidAngle(cellLocation.getCenter(), Octree::getCoordSubcellWidth(cellLocation.depth)); - if (lod < 0.0) { + if (lod < 0.0f) { return 0; } diff --git a/libraries/render/src/render/Octree.h b/libraries/render/src/render/Octree.h index c07224833b..f283f5b3e2 100644 --- a/libraries/render/src/render/Octree.h +++ b/libraries/render/src/render/Octree.h @@ -31,6 +31,8 @@ namespace render { public: std::vector items; std::vector subcellItems; + + void free() {}; }; class Octree { @@ -90,11 +92,12 @@ namespace render { static const Depth ROOT_DEPTH{ 0 }; static const Depth MAX_DEPTH{ 15 }; static const Depth METRIC_COORD_DEPTH{ 15 }; - static const double INV_DEPTH_DIM[Octree::MAX_DEPTH + 1]; + static const float INV_DEPTH_DIM[Octree::MAX_DEPTH + 1]; static int getDepthDimension(Depth depth) { return 1 << depth; } - static double getInvDepthDimension(Depth depth) { return INV_DEPTH_DIM[depth]; } - static float getCoordSubcellWidth(Depth depth) { return (float) (1.7320 * getInvDepthDimension(depth) * 0.5); } + static float getDepthDimensionf(Depth depth) { return (float) getDepthDimension(depth); } + static float getInvDepthDimension(Depth depth) { return INV_DEPTH_DIM[depth]; } + static float getCoordSubcellWidth(Depth depth) { return (1.7320f * getInvDepthDimension(depth) * 0.5f); } // Need 16bits integer coordinates on each axes: 32768 cell positions using Coord = int16_t; @@ -363,7 +366,7 @@ namespace render { // The octree only cares about the bound & the key of an item to store it a the right cell location class ItemSpatialTree : public Octree { float _size{ 32768.0f }; - double _invSize{ 1.0 / _size }; + float _invSize{ 1.0f / _size }; glm::vec3 _origin{ -16384.0f }; public: ItemSpatialTree() {} @@ -371,8 +374,8 @@ namespace render { float getSize() const { return _size; } const glm::vec3& getOrigin() const { return _origin; } - float getCellWidth(Depth depth) const { return (float) _size * getInvDepthDimension(depth); } - float getInvCellWidth(Depth depth) const { return (float) getDepthDimension(depth) * _invSize; } + float getCellWidth(Depth depth) const { return _size * getInvDepthDimension(depth); } + float getInvCellWidth(Depth depth) const { return getDepthDimensionf(depth) * _invSize; } float getCellHalfDiagonalSquare(Depth depth) const { float cellHalfWidth = 0.5f * getCellWidth(depth); From 6922c9adf0b6211f0c7aadb2d20c284e0af35a8f Mon Sep 17 00:00:00 2001 From: samcake Date: Wed, 10 Feb 2016 16:41:09 -0800 Subject: [PATCH 35/49] updating all the entity types when their bound is changing --- .../entities-renderer/src/RenderableEntityItem.h | 3 --- .../src/RenderableParticleEffectEntityItem.cpp | 11 +++++++++++ .../src/RenderableParticleEffectEntityItem.h | 5 +++++ .../src/RenderableZoneEntityItem.cpp | 14 ++++++++++++++ .../src/RenderableZoneEntityItem.h | 4 ++++ 5 files changed, 34 insertions(+), 3 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableEntityItem.h b/libraries/entities-renderer/src/RenderableEntityItem.h index aaeecbdc0e..faa719b102 100644 --- a/libraries/entities-renderer/src/RenderableEntityItem.h +++ b/libraries/entities-renderer/src/RenderableEntityItem.h @@ -37,8 +37,6 @@ public: typedef render::Payload Payload; typedef Payload::DataPointer Pointer; - int touch = 0; - EntityItemPointer entity; }; @@ -79,7 +77,6 @@ public: render::ScenePointer scene = AbstractViewStateInterface::instance()->getMain3DScene(); pendingChanges.updateItem(_myItem, [](RenderableEntityItemProxy& data) { - data.touch++; }); scene->enqueuePendingChanges(pendingChanges); diff --git a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp index e757f5c68a..e62dc82988 100644 --- a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp @@ -310,3 +310,14 @@ void RenderableParticleEffectEntityItem::createPipelines() { _texturedPipeline = gpu::Pipeline::create(program, state); } } + +void RenderableParticleEffectEntityItem::notifyBoundChanged() { + if (_renderItemId == render::Item::INVALID_ITEM_ID) { + return; + } + render::PendingChanges pendingChanges; + pendingChanges.updateItem(_renderItemId, [](ParticlePayloadData& payload) { + }); + + _scene->enqueuePendingChanges(pendingChanges); +} \ No newline at end of file diff --git a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.h b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.h index 0c1cc50a98..f2640bdf10 100644 --- a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.h +++ b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.h @@ -29,6 +29,11 @@ public: virtual void removeFromScene(EntityItemPointer self, render::ScenePointer scene, render::PendingChanges& pendingChanges) override; protected: + virtual void locationChanged() override { EntityItem::locationChanged(); notifyBoundChanged(); } + virtual void dimensionsChanged() override { EntityItem::dimensionsChanged(); notifyBoundChanged(); } + + void notifyBoundChanged(); + void createPipelines(); render::ScenePointer _scene; diff --git a/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp b/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp index 530c4f5063..56771296db 100644 --- a/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp @@ -234,3 +234,17 @@ void RenderableZoneEntityItem::removeFromScene(EntityItemPointer self, std::shar _model->removeFromScene(scene, pendingChanges); } } + + +void RenderableZoneEntityItem::notifyBoundChanged() { + if (_myMetaItem == render::Item::INVALID_ITEM_ID) { + return; + } + render::PendingChanges pendingChanges; + render::ScenePointer scene = AbstractViewStateInterface::instance()->getMain3DScene(); + + pendingChanges.updateItem(_myMetaItem, [](RenderableZoneEntityItemMeta& data) { + }); + + scene->enqueuePendingChanges(pendingChanges); +} diff --git a/libraries/entities-renderer/src/RenderableZoneEntityItem.h b/libraries/entities-renderer/src/RenderableZoneEntityItem.h index 36555dbc45..f669960a94 100644 --- a/libraries/entities-renderer/src/RenderableZoneEntityItem.h +++ b/libraries/entities-renderer/src/RenderableZoneEntityItem.h @@ -40,6 +40,10 @@ public: virtual void removeFromScene(EntityItemPointer self, std::shared_ptr scene, render::PendingChanges& pendingChanges); private: + virtual void locationChanged() override { EntityItem::locationChanged(); notifyBoundChanged(); } + virtual void dimensionsChanged() override { EntityItem::dimensionsChanged(); notifyBoundChanged(); } + void notifyBoundChanged(); + Model* getModel(); void initialSimulation(); void updateGeometry(); From 355b8ee40df53d32a5b63c9d16f7bd87bf35e17f Mon Sep 17 00:00:00 2001 From: samcake Date: Thu, 11 Feb 2016 10:05:39 -0800 Subject: [PATCH 36/49] THursday morning the frustum culling seems broken in one particular angle --- interface/src/Application.cpp | 2 +- interface/src/avatar/Avatar.h | 2 +- interface/src/ui/overlays/Overlay.h | 2 +- libraries/entities-renderer/src/RenderableModelEntityItem.h | 2 +- .../src/RenderableParticleEffectEntityItem.h | 2 +- .../entities-renderer/src/RenderablePolyVoxEntityItem.h | 2 +- libraries/entities-renderer/src/RenderableZoneEntityItem.h | 2 +- libraries/render-utils/src/AnimDebugDraw.h | 2 +- libraries/render/src/render/Octree.cpp | 6 ++++-- 9 files changed, 12 insertions(+), 10 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index efc482a3b4..9af0f7c00f 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -3600,7 +3600,7 @@ public: static render::ItemID _item; // unique WorldBoxRenderData }; -render::ItemID WorldBoxRenderData::_item = 0; +render::ItemID WorldBoxRenderData::_item { render::Item::INVALID_ITEM_ID }; namespace render { template <> const ItemKey payloadGetKey(const WorldBoxRenderData::Pointer& stuff) { return ItemKey::Builder::opaqueShape(); } diff --git a/interface/src/avatar/Avatar.h b/interface/src/avatar/Avatar.h index 0f84f35a25..dfca9dca6d 100644 --- a/interface/src/avatar/Avatar.h +++ b/interface/src/avatar/Avatar.h @@ -239,7 +239,7 @@ protected: virtual void updatePalms(); - render::ItemID _renderItemID; + render::ItemID _renderItemID{ render::Item::INVALID_ITEM_ID }; ThreadSafeValueCache _leftPalmPositionCache { glm::vec3() }; ThreadSafeValueCache _leftPalmRotationCache { glm::quat() }; diff --git a/interface/src/ui/overlays/Overlay.h b/interface/src/ui/overlays/Overlay.h index 2343e434ca..84e9c1bb59 100644 --- a/interface/src/ui/overlays/Overlay.h +++ b/interface/src/ui/overlays/Overlay.h @@ -92,7 +92,7 @@ public: protected: float updatePulse(); - render::ItemID _renderItemID; + render::ItemID _renderItemID{ render::Item::INVALID_ITEM_ID }; bool _isLoaded; float _alpha; diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.h b/libraries/entities-renderer/src/RenderableModelEntityItem.h index 20afb3c157..cbd4146772 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.h +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.h @@ -96,7 +96,7 @@ private: QVector> _points; bool _dimensionsInitialized = true; - render::ItemID _myMetaItem; + render::ItemID _myMetaItem{ render::Item::INVALID_ITEM_ID }; bool _showCollisionHull = false; diff --git a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.h b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.h index f2640bdf10..1f066a81fd 100644 --- a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.h +++ b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.h @@ -37,7 +37,7 @@ protected: void createPipelines(); render::ScenePointer _scene; - render::ItemID _renderItemId; + render::ItemID _renderItemId{ render::Item::INVALID_ITEM_ID }; NetworkTexturePointer _texture; gpu::PipelinePointer _untexturedPipeline; diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h index efd9b4afda..fdbaefb0c3 100644 --- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h +++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h @@ -129,7 +129,7 @@ private: NetworkTexturePointer _zTexture; const int MATERIAL_GPU_SLOT = 3; - render::ItemID _myItem; + render::ItemID _myItem{ render::Item::INVALID_ITEM_ID }; static gpu::PipelinePointer _pipeline; ShapeInfo _shapeInfo; diff --git a/libraries/entities-renderer/src/RenderableZoneEntityItem.h b/libraries/entities-renderer/src/RenderableZoneEntityItem.h index f669960a94..6eb829a48f 100644 --- a/libraries/entities-renderer/src/RenderableZoneEntityItem.h +++ b/libraries/entities-renderer/src/RenderableZoneEntityItem.h @@ -54,7 +54,7 @@ private: Model* _model; bool _needsInitialSimulation; - render::ItemID _myMetaItem; + render::ItemID _myMetaItem{ render::Item::INVALID_ITEM_ID }; }; #endif // hifi_RenderableZoneEntityItem_h diff --git a/libraries/render-utils/src/AnimDebugDraw.h b/libraries/render-utils/src/AnimDebugDraw.h index eb4621a880..20336ed6dd 100644 --- a/libraries/render-utils/src/AnimDebugDraw.h +++ b/libraries/render-utils/src/AnimDebugDraw.h @@ -37,7 +37,7 @@ public: protected: std::shared_ptr _animDebugDrawData; std::shared_ptr _animDebugDrawPayload; - render::ItemID _itemID; + render::ItemID _itemID{ render::Item::INVALID_ITEM_ID }; static gpu::PipelinePointer _pipeline; diff --git a/libraries/render/src/render/Octree.cpp b/libraries/render/src/render/Octree.cpp index 5a9fe28b4a..5b419f2918 100644 --- a/libraries/render/src/render/Octree.cpp +++ b/libraries/render/src/render/Octree.cpp @@ -109,11 +109,13 @@ Octree::Indices Octree::indexConcreteCellPath(const Locations& path) const { Octree::Index Octree::allocateCell(Index parent, const Location& location) { if (_cells[parent].hasChild(location.octant())) { - assert(_cells[parent].child(location.octant()) == INVALID_CELL); return _cells[parent].child(location.octant()); } - assert(_cells[parent].getlocation().child(location.octant()) == location); + if (!(_cells[parent].getlocation().child(location.octant()) == location)) { + auto parentLoc = _cells[parent].getlocation(); + assert(false); + } Index newIndex; if (_freeCells.empty()) { From dedfee3acc1f6012811b6a707f82afabc8236dda Mon Sep 17 00:00:00 2001 From: samcake Date: Thu, 11 Feb 2016 13:43:43 -0800 Subject: [PATCH 37/49] Fixing a terrible bug! --- libraries/render/src/render/Octree.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/libraries/render/src/render/Octree.cpp b/libraries/render/src/render/Octree.cpp index 5b419f2918..82c8ca604b 100644 --- a/libraries/render/src/render/Octree.cpp +++ b/libraries/render/src/render/Octree.cpp @@ -92,7 +92,9 @@ Octree::Indices Octree::indexConcreteCellPath(const Locations& path) const { Index currentIndex = ROOT_CELL; Indices cellPath(1, currentIndex); - for (auto& location : path) { + // Start the path after the root cell so at #1 + for (size_t p = 1; p < path.size(); p++) { + auto& location = path[p]; auto nextIndex = getConcreteCell(currentIndex).child(location.octant()); if (nextIndex == INVALID_CELL) { break; From 898cfbc043ce0586ae32301eeaadb223fae06a05 Mon Sep 17 00:00:00 2001 From: samcake Date: Thu, 11 Feb 2016 14:41:37 -0800 Subject: [PATCH 38/49] cleaning a if into an assert --- libraries/render/src/render/Octree.cpp | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/libraries/render/src/render/Octree.cpp b/libraries/render/src/render/Octree.cpp index 82c8ca604b..6a61c16567 100644 --- a/libraries/render/src/render/Octree.cpp +++ b/libraries/render/src/render/Octree.cpp @@ -113,11 +113,7 @@ Octree::Index Octree::allocateCell(Index parent, const Location& location) { if (_cells[parent].hasChild(location.octant())) { return _cells[parent].child(location.octant()); } - - if (!(_cells[parent].getlocation().child(location.octant()) == location)) { - auto parentLoc = _cells[parent].getlocation(); - assert(false); - } + assert(_cells[parent].getlocation().child(location.octant()) == location); Index newIndex; if (_freeCells.empty()) { From b94c2c9b7f9b05dcb65597b4263b2e8c84253620 Mon Sep 17 00:00:00 2001 From: samcake Date: Thu, 11 Feb 2016 18:40:36 -0800 Subject: [PATCH 39/49] Introducing a cleaner filtering pipeline, normally it s good to go --- .../render-utils/src/RenderDeferredTask.cpp | 110 +++++++++--------- .../render-utils/src/RenderDeferredTask.h | 8 +- libraries/render/src/render/CullTask.cpp | 20 ---- libraries/render/src/render/CullTask.h | 54 +++++++++ libraries/render/src/render/DrawTask.cpp | 20 +--- libraries/render/src/render/DrawTask.h | 6 +- libraries/render/src/render/Item.h | 3 + libraries/render/src/render/Octree.cpp | 34 +++--- libraries/render/src/render/Task.h | 2 +- 9 files changed, 144 insertions(+), 113 deletions(-) diff --git a/libraries/render-utils/src/RenderDeferredTask.cpp b/libraries/render-utils/src/RenderDeferredTask.cpp index 31d9bce77d..189ae4d590 100755 --- a/libraries/render-utils/src/RenderDeferredTask.cpp +++ b/libraries/render-utils/src/RenderDeferredTask.cpp @@ -51,34 +51,35 @@ RenderDeferredTask::RenderDeferredTask(CullFunctor cullFunctor) { // Prepare the ShapePipelines ShapePlumberPointer shapePlumber = std::make_shared(); initDeferredPipelines(*shapePlumber); - - /* // CPU: Fetch the renderOpaques - const auto fetchedOpaques = addJob("FetchOpaque"); - const auto culledOpaques = addJob>("CullOpaque", fetchedOpaques, cullFunctor); - const auto opaques = addJob("DepthSortOpaque", culledOpaques); - - // CPU only, create the list of renderedTransparents items - const auto fetchedTransparents = addJob("FetchTransparent", FetchItems( - ItemFilter::Builder::transparentShape().withoutLayered())); - const auto culledTransparents = - addJob>("CullTransparent", fetchedTransparents, cullFunctor); - const auto transparents = addJob("DepthSortTransparent", culledTransparents, DepthSortItems(false)); - */ - // CPU: Fetch the renderOpaques - auto sceneFilter = ItemFilter::Builder::opaqueShape().withTransparent().withTypeLight(); + // CPU jobs: + // Fetch and cull the items from the scene + auto sceneFilter = ItemFilter::Builder::visibleWorldItems().withoutLayered(); const auto sceneSelection = addJob("FetchSceneSelection", sceneFilter); - const auto culledSceneSelection = addJob("CullSceneSelection", sceneSelection, cullFunctor, RenderDetails::OPAQUE_ITEM, ItemFilter::Builder::opaqueShape().withoutLayered()); + const auto culledSceneSelection = addJob("CullSceneSelection", sceneSelection, cullFunctor, RenderDetails::OPAQUE_ITEM, sceneFilter); - const auto culledOpaques = addJob("FilterOpaque", culledSceneSelection); - const auto opaques = addJob("DepthSortOpaque", culledOpaques); + // Multi filter visible items into different buckets + const int NUM_FILTERS = 3; + const int OPAQUE_SHAPE_BUCKET = 0; + const int TRANSPARENT_SHAPE_BUCKET = 1; + const int LIGHT_BUCKET = 2; + MultiFilterItem::ItemFilterArray triageFilters = { + ItemFilter::Builder::opaqueShape(), + ItemFilter::Builder::transparentShape(), + ItemFilter::Builder::light() + }; + const auto filteredItemsBuckets = addJob>("FilterSceneSelection", culledSceneSelection, triageFilters).get::ItemBoundsArray>(); - // CPU only, create the list of renderedTransparents items - const auto fetchedTransparents = addJob("FetchTransparent", FetchItems( - ItemFilter::Builder::transparentShape().withoutLayered())); - const auto culledTransparents = - addJob>("CullTransparent", fetchedTransparents, cullFunctor); - const auto transparents = addJob("DepthSortTransparent", culledTransparents, DepthSortItems(false)); + // Extract / Sort opaques / Transparents / Lights / Overlays + const auto opaques = addJob("DepthSortOpaque", filteredItemsBuckets[OPAQUE_SHAPE_BUCKET]); + const auto transparents = addJob("DepthSortTransparent", filteredItemsBuckets[TRANSPARENT_SHAPE_BUCKET], DepthSortItems(false)); + const auto lights = filteredItemsBuckets[LIGHT_BUCKET]; + + // Overlays are not culled because we want to make sure they are seen + // Could be considered a bug in the current cullfunctor + const auto overlayOpaques = addJob("FetchOverlayOpaque", ItemFilter::Builder::opaqueShapeLayered()); + const auto fetchedOverlayOpaques = addJob("FetchOverlayTransparents", ItemFilter::Builder::transparentShapeLayered()); + const auto overlayTransparents = addJob("DepthSortTransparentOverlay", fetchedOverlayOpaques, DepthSortItems(false)); // GPU Jobs: Start preparing the deferred and lighting buffer addJob("PrepareDeferred"); @@ -96,7 +97,7 @@ RenderDeferredTask::RenderDeferredTask(CullFunctor cullFunctor) { addJob("AmbientOcclusion"); // Draw Lights just add the lights to the current list of lights to deal with. NOt really gpu job for now. - addJob("DrawLight", cullFunctor); + addJob("DrawLight", lights); // DeferredBuffer is complete, now let's shade it into the LightingBuffer addJob("RenderDeferred"); @@ -110,28 +111,35 @@ RenderDeferredTask::RenderDeferredTask(CullFunctor cullFunctor) { // Lighting Buffer ready for tone mapping addJob("ToneMapping"); - // Debugging Deferred buffer job - addJob("DebugDeferredBuffer"); + // Overlays + addJob("DrawOverlay3DOpaque", overlayOpaques, true); + addJob("DrawOverlay3DTransparent", overlayTransparents, false); - // Scene Octree Debuging job + + // Debugging stages { - addJob("DrawSceneOctree", sceneSelection); - addJob("DrawItemSelection", sceneSelection); + // Debugging Deferred buffer job + addJob("DebugDeferredBuffer"); + + // Scene Octree Debuging job + { + addJob("DrawSceneOctree", sceneSelection); + addJob("DrawItemSelection", sceneSelection); + } + + // Status icon rendering job + { + // Grab a texture map representing the different status icons and assign that to the drawStatsuJob + auto iconMapPath = PathUtils::resourcesPath() + "icons/statusIconAtlas.svg"; + auto statusIconMap = DependencyManager::get()->getImageTexture(iconMapPath); + addJob("DrawStatus", opaques, DrawStatus(statusIconMap)); + } } - // Status icon rendering job - { - // Grab a texture map representing the different status icons and assign that to the drawStatsuJob - auto iconMapPath = PathUtils::resourcesPath() + "icons/statusIconAtlas.svg"; - auto statusIconMap = DependencyManager::get()->getImageTexture(iconMapPath); - addJob("DrawStatus", opaques, DrawStatus(statusIconMap)); - } - - addJob("DrawOverlay3DOpaque", ItemFilter::Builder::opaqueShape().withLayered()); - addJob("DrawOverlay3DTransparent", ItemFilter::Builder::transparentShape().withLayered()); - - addJob("HitEffect"); + // FIXME: Hit effect is never used, let's hide it for now, probably a more generic way to add custom post process effects + // addJob("HitEffect"); + // Blit! addJob("Blit"); } @@ -182,28 +190,22 @@ void DrawDeferred::run(const SceneContextPointer& sceneContext, const RenderCont }); } -DrawOverlay3D::DrawOverlay3D(ItemFilter filter) : _filter{ filter }, _shapePlumber{ std::make_shared() } { +DrawOverlay3D::DrawOverlay3D(bool opaque) : + _shapePlumber(std::make_shared()), + _opaquePass(opaque) { initOverlay3DPipelines(*_shapePlumber); } -void DrawOverlay3D::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) { +void DrawOverlay3D::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const render::ItemBounds& inItems) { assert(renderContext->args); assert(renderContext->args->_viewFrustum); // render backgrounds auto& scene = sceneContext->_scene; - auto& items = scene->getMasterBucket().at(_filter); auto config = std::static_pointer_cast(renderContext->jobConfig); - ItemBounds inItems; - inItems.reserve(items.size()); - for (auto id : items) { - auto& item = scene->getItem(id); - if (item.getKey().isVisible() && (item.getLayer() == 1)) { - inItems.emplace_back(id); - } - } + config->setNumDrawn((int)inItems.size()); emit config->numDrawnChanged(); @@ -213,7 +215,7 @@ void DrawOverlay3D::run(const SceneContextPointer& sceneContext, const RenderCon // Clear the framebuffer without stereo // Needs to be distinct from the other batch because using the clear call // while stereo is enabled triggers a warning - { + if (_opaquePass) { gpu::Batch batch; batch.enableStereo(false); batch.clearFramebuffer(gpu::Framebuffer::BUFFER_DEPTH, glm::vec4(), 1.f, 0, true); diff --git a/libraries/render-utils/src/RenderDeferredTask.h b/libraries/render-utils/src/RenderDeferredTask.h index 7ea0e62fbc..5591edce7c 100755 --- a/libraries/render-utils/src/RenderDeferredTask.h +++ b/libraries/render-utils/src/RenderDeferredTask.h @@ -111,17 +111,17 @@ protected: class DrawOverlay3D { public: using Config = DrawOverlay3DConfig; - using JobModel = render::Job::Model; + using JobModel = render::Job::ModelI; - DrawOverlay3D(render::ItemFilter filter); + DrawOverlay3D(bool opaque); void configure(const Config& config) { _maxDrawn = config.maxDrawn; } - void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext); + void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext, const render::ItemBounds& inItems); protected: - render::ItemFilter _filter; render::ShapePlumberPointer _shapePlumber; int _maxDrawn; // initialized by Config + bool _opaquePass{ true }; }; class Blit { diff --git a/libraries/render/src/render/CullTask.cpp b/libraries/render/src/render/CullTask.cpp index 3f80a4c033..bf7b22b689 100644 --- a/libraries/render/src/render/CullTask.cpp +++ b/libraries/render/src/render/CullTask.cpp @@ -291,23 +291,3 @@ void CullSpatialSelection::run(const SceneContextPointer& sceneContext, const Re std::static_pointer_cast(renderContext->jobConfig)->numItems = (int)outItems.size(); } - -void FilterItemSelection::configure(const Config& config) { -} - -void FilterItemSelection::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemBounds& inItems, ItemBounds& outItems) { - assert(renderContext->args); - assert(renderContext->args->_viewFrustum); - auto& scene = sceneContext->_scene; - - // Now we have a selection of items to render - outItems.clear(); - outItems.reserve(inItems.size()); - - for (auto itemBound : inItems) { - auto& item = scene->getItem(itemBound.id); - if (_filter.test(item.getKey())) { - outItems.emplace_back(itemBound); - } - } -} diff --git a/libraries/render/src/render/CullTask.h b/libraries/render/src/render/CullTask.h index 795dd103e9..5a089967c0 100644 --- a/libraries/render/src/render/CullTask.h +++ b/libraries/render/src/render/CullTask.h @@ -157,6 +157,60 @@ namespace render { void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemBounds& inItems, ItemBounds& outItems); }; + class MultiFilterItemConfig : public Job::Config { + Q_OBJECT + Q_PROPERTY(int numItems READ getNumItems) + public: + int numItems{ 0 }; + int getNumItems() { return numItems; } + }; + + template < class T, int NUM > + class VaryingArray : public std::array { + public: + VaryingArray() { + for (size_t i = 0; i < NUM; i++) { + (*this)[i] = Varying(T()); + } + } + }; + + template + class MultiFilterItem { + public: + using ItemFilterArray = std::array; + using ItemBoundsArray = VaryingArray; + using Config = MultiFilterItemConfig; + using JobModel = Job::ModelIO; + + MultiFilterItem() {} + MultiFilterItem(const ItemFilterArray& filters) : + _filters(filters) {} + + ItemFilterArray _filters; + + void configure(const Config& config) {} + void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemBounds& inItems, ItemBoundsArray& outItems) { + auto& scene = sceneContext->_scene; + + // Clear previous values + for (size_t i = 0; i < NUM_FILTERS; i++) { + outItems[i].edit().clear(); + } + + // For each item, filter it into the buckets + for (auto itemBound : inItems) { + auto& item = scene->getItem(itemBound.id); + auto itemKey = item.getKey(); + for (size_t i = 0; i < NUM_FILTERS; i++) { + if (_filters[i].test(itemKey)) { + outItems[i].edit().emplace_back(itemBound); + } + } + } + } + }; + class DepthSortItems { public: using JobModel = Job::ModelIO; diff --git a/libraries/render/src/render/DrawTask.cpp b/libraries/render/src/render/DrawTask.cpp index fcc9041934..6774eadc1d 100755 --- a/libraries/render/src/render/DrawTask.cpp +++ b/libraries/render/src/render/DrawTask.cpp @@ -61,31 +61,19 @@ void render::renderShapes(const SceneContextPointer& sceneContext, const RenderC } } -void DrawLight::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) { +void DrawLight::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemBounds& inLights) { assert(renderContext->args); assert(renderContext->args->_viewFrustum); - - // render lights auto& scene = sceneContext->_scene; - auto& items = scene->getMasterBucket().at(ItemFilter::Builder::light()); - - ItemBounds inItems; - inItems.reserve(items.size()); - for (auto id : items) { - auto item = scene->getItem(id); - inItems.emplace_back(ItemBound(id, item.getBound())); - } - RenderArgs* args = renderContext->args; + // render lights + auto& details = args->_details.edit(RenderDetails::OTHER_ITEM); - ItemBounds culledItems; - culledItems.reserve(inItems.size()); - cullItems(renderContext, _cullFunctor, details, inItems, culledItems); gpu::doInBatch(args->_context, [&](gpu::Batch& batch) { args->_batch = &batch; - renderItems(sceneContext, renderContext, culledItems); + renderItems(sceneContext, renderContext, inLights); args->_batch = nullptr; }); } diff --git a/libraries/render/src/render/DrawTask.h b/libraries/render/src/render/DrawTask.h index b647d77211..a52bd3192f 100755 --- a/libraries/render/src/render/DrawTask.h +++ b/libraries/render/src/render/DrawTask.h @@ -25,12 +25,10 @@ void renderShapes(const SceneContextPointer& sceneContext, const RenderContextPo class DrawLight { public: - using JobModel = Job::Model; + using JobModel = Job::ModelI; - DrawLight(CullFunctor cullFunctor) : _cullFunctor{ cullFunctor } {} - void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext); + void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemBounds& inLights); protected: - CullFunctor _cullFunctor; }; class PipelineSortShapes { diff --git a/libraries/render/src/render/Item.h b/libraries/render/src/render/Item.h index 0503d6845c..1c4dbe1c29 100644 --- a/libraries/render/src/render/Item.h +++ b/libraries/render/src/render/Item.h @@ -171,10 +171,13 @@ public: Builder& withLayered() { _value.set(ItemKey::LAYERED); _mask.set(ItemKey::LAYERED); return (*this); } // Convenient standard keys that we will keep on using all over the place + static Builder visibleWorldItems() { return Builder().withVisible().withWorldSpace(); } static Builder opaqueShape() { return Builder().withTypeShape().withOpaque().withWorldSpace(); } static Builder transparentShape() { return Builder().withTypeShape().withTransparent().withWorldSpace(); } static Builder light() { return Builder().withTypeLight(); } 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(); } }; ItemFilter(const Builder& builder) : ItemFilter(builder._value, builder._mask) {} diff --git a/libraries/render/src/render/Octree.cpp b/libraries/render/src/render/Octree.cpp index 6a61c16567..9e655b6719 100644 --- a/libraries/render/src/render/Octree.cpp +++ b/libraries/render/src/render/Octree.cpp @@ -342,24 +342,30 @@ bool ItemSpatialTree::removeItem(Index cellIdx, const ItemKey& key, const ItemID } ItemSpatialTree::Index ItemSpatialTree::resetItem(Index oldCell, const ItemKey& oldKey, const AABox& bound, const ItemID& item, ItemKey& newKey) { - auto minCoordf = evalCoordf(bound.getMinimumPoint()); - auto maxCoordf = evalCoordf(bound.getMaximumPoint()); - Coord3 minCoord(minCoordf); - Coord3 maxCoord(maxCoordf); - auto location = Location::evalFromRange(minCoord, maxCoord); + auto newCell = INVALID_CELL; + if (!newKey.isViewSpace()) { + auto minCoordf = evalCoordf(bound.getMinimumPoint()); + auto maxCoordf = evalCoordf(bound.getMaximumPoint()); + Coord3 minCoord(minCoordf); + Coord3 maxCoord(maxCoordf); + auto location = Location::evalFromRange(minCoord, maxCoord); - // Compare range size vs cell location size and tag itemKey accordingly - auto rangeSizef = maxCoordf - minCoordf; - float cellFitSize = getCellHalfDiagonalSquare(location.depth); - bool subcellItem = glm::dot(rangeSizef, rangeSizef) < cellFitSize; - if (subcellItem) { - newKey.setSmaller(subcellItem); + // Compare range size vs cell location size and tag itemKey accordingly + // If Item bound fits in sub cell then tag as small + auto rangeSizef = maxCoordf - minCoordf; + float cellHalfSize = 0.5f * getCellWidth(location.depth); + bool subcellItem = std::max(std::max(rangeSizef.x, rangeSizef.y), rangeSizef.z) < cellHalfSize; + if (subcellItem) { + newKey.setSmaller(subcellItem); + } else { + newKey.setSmaller(false); + } + + newCell = indexCell(location); } else { - newKey.setSmaller(false); + // A very rare case, if we were adding items with boundary semantic expressed in view space } - auto newCell = indexCell(location); - // Did we fail finding a cell for the item? if (newCell == INVALID_CELL) { // Remove the item where it was diff --git a/libraries/render/src/render/Task.h b/libraries/render/src/render/Task.h index a47f8e39fe..2ae2742fa1 100644 --- a/libraries/render/src/render/Task.h +++ b/libraries/render/src/render/Task.h @@ -35,7 +35,7 @@ public: template Varying(const T& data) : _concept(std::make_shared>(data)) {} template T& edit() { return std::static_pointer_cast>(_concept)->_data; } - template const T& get() { return std::static_pointer_cast>(_concept)->_data; } + template const T& get() const { return std::static_pointer_cast>(_concept)->_data; } protected: class Concept { From 171acb883f9ea40cbd26efd35bd1afeeb7ab2b38 Mon Sep 17 00:00:00 2001 From: samcake Date: Fri, 12 Feb 2016 09:59:33 -0800 Subject: [PATCH 40/49] Fixing compilation issue on mac and linux --- libraries/render/src/render/CullTask.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/render/src/render/CullTask.h b/libraries/render/src/render/CullTask.h index 5a089967c0..0eaa4559a0 100644 --- a/libraries/render/src/render/CullTask.h +++ b/libraries/render/src/render/CullTask.h @@ -195,7 +195,7 @@ namespace render { // Clear previous values for (size_t i = 0; i < NUM_FILTERS; i++) { - outItems[i].edit().clear(); + outItems[i].template edit().clear(); } // For each item, filter it into the buckets @@ -204,7 +204,7 @@ namespace render { auto itemKey = item.getKey(); for (size_t i = 0; i < NUM_FILTERS; i++) { if (_filters[i].test(itemKey)) { - outItems[i].edit().emplace_back(itemBound); + outItems[i].template edit().emplace_back(itemBound); } } } From 40455af7fc2feb1c77457b2d6dd9bfe4f6bdf5b5 Mon Sep 17 00:00:00 2001 From: samcake Date: Fri, 12 Feb 2016 10:16:20 -0800 Subject: [PATCH 41/49] LEss warning --- libraries/render-utils/src/RenderDeferredTask.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libraries/render-utils/src/RenderDeferredTask.cpp b/libraries/render-utils/src/RenderDeferredTask.cpp index 189ae4d590..5981e90a11 100755 --- a/libraries/render-utils/src/RenderDeferredTask.cpp +++ b/libraries/render-utils/src/RenderDeferredTask.cpp @@ -64,9 +64,9 @@ RenderDeferredTask::RenderDeferredTask(CullFunctor cullFunctor) { const int TRANSPARENT_SHAPE_BUCKET = 1; const int LIGHT_BUCKET = 2; MultiFilterItem::ItemFilterArray triageFilters = { - ItemFilter::Builder::opaqueShape(), - ItemFilter::Builder::transparentShape(), - ItemFilter::Builder::light() + { ItemFilter::Builder::opaqueShape() }, + { ItemFilter::Builder::transparentShape() }, + { ItemFilter::Builder::light() } }; const auto filteredItemsBuckets = addJob>("FilterSceneSelection", culledSceneSelection, triageFilters).get::ItemBoundsArray>(); From 40c283cba202394af55cb025142d45ddf5e6614e Mon Sep 17 00:00:00 2001 From: samcake Date: Fri, 12 Feb 2016 10:21:28 -0800 Subject: [PATCH 42/49] Fixing some warnings --- libraries/render-utils/src/RenderDeferredTask.cpp | 9 +++------ libraries/render/src/render/DrawTask.cpp | 4 ---- 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/libraries/render-utils/src/RenderDeferredTask.cpp b/libraries/render-utils/src/RenderDeferredTask.cpp index 5981e90a11..95e6eb4e11 100755 --- a/libraries/render-utils/src/RenderDeferredTask.cpp +++ b/libraries/render-utils/src/RenderDeferredTask.cpp @@ -64,9 +64,9 @@ RenderDeferredTask::RenderDeferredTask(CullFunctor cullFunctor) { const int TRANSPARENT_SHAPE_BUCKET = 1; const int LIGHT_BUCKET = 2; MultiFilterItem::ItemFilterArray triageFilters = { - { ItemFilter::Builder::opaqueShape() }, - { ItemFilter::Builder::transparentShape() }, - { ItemFilter::Builder::light() } + ItemFilter::Builder::opaqueShape(), + ItemFilter::Builder::transparentShape(), + ItemFilter::Builder::light() }; const auto filteredItemsBuckets = addJob>("FilterSceneSelection", culledSceneSelection, triageFilters).get::ItemBoundsArray>(); @@ -200,9 +200,6 @@ void DrawOverlay3D::run(const SceneContextPointer& sceneContext, const RenderCon assert(renderContext->args); assert(renderContext->args->_viewFrustum); - // render backgrounds - auto& scene = sceneContext->_scene; - auto config = std::static_pointer_cast(renderContext->jobConfig); diff --git a/libraries/render/src/render/DrawTask.cpp b/libraries/render/src/render/DrawTask.cpp index 6774eadc1d..5ad6ca0547 100755 --- a/libraries/render/src/render/DrawTask.cpp +++ b/libraries/render/src/render/DrawTask.cpp @@ -64,13 +64,9 @@ void render::renderShapes(const SceneContextPointer& sceneContext, const RenderC void DrawLight::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemBounds& inLights) { assert(renderContext->args); assert(renderContext->args->_viewFrustum); - auto& scene = sceneContext->_scene; RenderArgs* args = renderContext->args; // render lights - - auto& details = args->_details.edit(RenderDetails::OTHER_ITEM); - gpu::doInBatch(args->_context, [&](gpu::Batch& batch) { args->_batch = &batch; renderItems(sceneContext, renderContext, inLights); From c248732bc701f61ee815c82f06726d8899197c91 Mon Sep 17 00:00:00 2001 From: samcake Date: Fri, 12 Feb 2016 10:27:08 -0800 Subject: [PATCH 43/49] Getting the array initialization right ? --- libraries/render-utils/src/RenderDeferredTask.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/libraries/render-utils/src/RenderDeferredTask.cpp b/libraries/render-utils/src/RenderDeferredTask.cpp index 95e6eb4e11..13a24fc16d 100755 --- a/libraries/render-utils/src/RenderDeferredTask.cpp +++ b/libraries/render-utils/src/RenderDeferredTask.cpp @@ -63,11 +63,11 @@ RenderDeferredTask::RenderDeferredTask(CullFunctor cullFunctor) { const int OPAQUE_SHAPE_BUCKET = 0; const int TRANSPARENT_SHAPE_BUCKET = 1; const int LIGHT_BUCKET = 2; - MultiFilterItem::ItemFilterArray triageFilters = { - ItemFilter::Builder::opaqueShape(), - ItemFilter::Builder::transparentShape(), - ItemFilter::Builder::light() - }; + MultiFilterItem::ItemFilterArray triageFilters = { { + ItemFilter::Builder::opaqueShape(), + ItemFilter::Builder::transparentShape(), + ItemFilter::Builder::light() + } }; const auto filteredItemsBuckets = addJob>("FilterSceneSelection", culledSceneSelection, triageFilters).get::ItemBoundsArray>(); // Extract / Sort opaques / Transparents / Lights / Overlays From 2d33bb83e12d19ad36da15684b2121db31f64129 Mon Sep 17 00:00:00 2001 From: samcake Date: Fri, 12 Feb 2016 10:30:44 -0800 Subject: [PATCH 44/49] Remove a dead comment --- libraries/octree/src/ViewFrustum.h | 1 - 1 file changed, 1 deletion(-) diff --git a/libraries/octree/src/ViewFrustum.h b/libraries/octree/src/ViewFrustum.h index b6096114a4..89c632df8c 100644 --- a/libraries/octree/src/ViewFrustum.h +++ b/libraries/octree/src/ViewFrustum.h @@ -165,7 +165,6 @@ private: float _fieldOfView = DEFAULT_FIELD_OF_VIEW_DEGREES; glm::vec4 _corners[8]; glm::vec3 _cornersWorld[8]; - // enum { TOP_PLANE = 0, BOTTOM_PLANE, LEFT_PLANE, RIGHT_PLANE, NEAR_PLANE, FAR_PLANE }; ::Plane _planes[6]; // How will this be used? const char* debugPlaneName (int plane) const; From c4faa091a154ca8543063a37bd5a00ca1439dd71 Mon Sep 17 00:00:00 2001 From: samcake Date: Fri, 12 Feb 2016 11:31:21 -0800 Subject: [PATCH 45/49] Fixing initialization syntax --- libraries/entities-renderer/src/RenderableEntityItem.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/entities-renderer/src/RenderableEntityItem.h b/libraries/entities-renderer/src/RenderableEntityItem.h index faa719b102..7b3ea8edaa 100644 --- a/libraries/entities-renderer/src/RenderableEntityItem.h +++ b/libraries/entities-renderer/src/RenderableEntityItem.h @@ -83,7 +83,7 @@ public: } private: - render::ItemID _myItem = render::Item::INVALID_ITEM_ID; + render::ItemID _myItem { render::Item::INVALID_ITEM_ID }; }; From e3307d91ad48195a7d3d3f2b7e852acb6b96aca5 Mon Sep 17 00:00:00 2001 From: samcake Date: Fri, 12 Feb 2016 14:59:43 -0800 Subject: [PATCH 46/49] Fix the debugging tool to be able to freeze the frustum for scene fetch and culling --- .../utilities/tools/debugRenderCulling.js | 4 ++-- libraries/render/src/render/CullTask.cpp | 23 +++++++++++++++++-- libraries/render/src/render/CullTask.h | 11 +++++++++ .../render/src/render/DrawSceneOctree.cpp | 3 --- libraries/render/src/render/DrawSceneOctree.h | 6 ----- 5 files changed, 34 insertions(+), 13 deletions(-) diff --git a/examples/utilities/tools/debugRenderCulling.js b/examples/utilities/tools/debugRenderCulling.js index 3d260fc95c..dbc5f07e0d 100644 --- a/examples/utilities/tools/debugRenderCulling.js +++ b/examples/utilities/tools/debugRenderCulling.js @@ -27,8 +27,8 @@ panel.newCheckbox("Show Empty Cells", function(value) { return (value); } ); panel.newCheckbox("Freeze Frustum", - function(value) { Render.RenderDeferredTask.DrawSceneOctree.freezeFrustum = value; }, - function() { return (Render.RenderDeferredTask.DrawSceneOctree.freezeFrustum); }, + function(value) { Render.RenderDeferredTask.FetchSceneSelection.freezeFrustum = value; Render.RenderDeferredTask.CullSceneSelection.freezeFrustum = value; }, + function() { return (Render.RenderDeferredTask.FetchSceneSelection.freezeFrustum); }, function(value) { return (value); } ); panel.newCheckbox("Show Inside Items", diff --git a/libraries/render/src/render/CullTask.cpp b/libraries/render/src/render/CullTask.cpp index bf7b22b689..adc4114d6c 100644 --- a/libraries/render/src/render/CullTask.cpp +++ b/libraries/render/src/render/CullTask.cpp @@ -156,7 +156,7 @@ void FetchItems::run(const SceneContextPointer& sceneContext, const RenderContex void FetchSpatialTree::configure(const Config& config) { - _justFrozeFrustum = (config.freezeFrustum && !_freezeFrustum); + _justFrozeFrustum = _justFrozeFrustum || (config.freezeFrustum && !_freezeFrustum); _freezeFrustum = config.freezeFrustum; _lodAngle = config.lodAngle; } @@ -182,7 +182,7 @@ void FetchSpatialTree::run(const SceneContextPointer& sceneContext, const Render // Octree selection! - float angle = glm::degrees(args->_viewFrustum->getAccuracyAngle(args->_sizeScale, args->_boundaryLevelAdjust)); + float angle = glm::degrees(queryFrustum.getAccuracyAngle(args->_sizeScale, args->_boundaryLevelAdjust)); scene->getSpatialTree().selectCellItems(outSelection, _filter, queryFrustum, angle); @@ -190,6 +190,8 @@ void FetchSpatialTree::run(const SceneContextPointer& sceneContext, const Render } void CullSpatialSelection::configure(const Config& config) { + _justFrozeFrustum = _justFrozeFrustum || (config.freezeFrustum && !_freezeFrustum); + _freezeFrustum = config.freezeFrustum; } void CullSpatialSelection::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, @@ -202,6 +204,17 @@ void CullSpatialSelection::run(const SceneContextPointer& sceneContext, const Re auto& details = args->_details.edit(_detailType); details._considered += inSelection.numItems(); + // Eventually use a frozen frustum + auto queryFrustum = args->_viewFrustum; + auto argFrustum = args->_viewFrustum; + if (_freezeFrustum) { + if (_justFrozeFrustum) { + _justFrozeFrustum = false; + _frozenFrutstum = *args->_viewFrustum; + } + args->_viewFrustum = &_frozenFrutstum; // replace the true view frustum by the frozen one + } + // Culling Frustum / solidAngle test helper class struct Test { CullFunctor _functor; @@ -289,5 +302,11 @@ void CullSpatialSelection::run(const SceneContextPointer& sceneContext, const Re details._rendered += outItems.size(); + + // Restore frustum if using the frozen one: + if (_freezeFrustum) { + args->_viewFrustum = argFrustum; + } + std::static_pointer_cast(renderContext->jobConfig)->numItems = (int)outItems.size(); } diff --git a/libraries/render/src/render/CullTask.h b/libraries/render/src/render/CullTask.h index 0eaa4559a0..9a52bb06ff 100644 --- a/libraries/render/src/render/CullTask.h +++ b/libraries/render/src/render/CullTask.h @@ -108,12 +108,23 @@ namespace render { class CullSpatialSelectionConfig : public Job::Config { Q_OBJECT Q_PROPERTY(int numItems READ getNumItems) + Q_PROPERTY(bool freezeFrustum MEMBER freezeFrustum WRITE setFreezeFrustum) public: int numItems{ 0 }; int getNumItems() { return numItems; } + + bool freezeFrustum{ false }; + public slots: + void setFreezeFrustum(bool enabled) { freezeFrustum = enabled; emit dirty(); } + + signals: + void dirty(); }; class CullSpatialSelection { + bool _freezeFrustum{ false }; // initialized by Config + bool _justFrozeFrustum{ false }; + ViewFrustum _frozenFrutstum; public: using Config = CullSpatialSelectionConfig; using JobModel = Job::ModelIO; diff --git a/libraries/render/src/render/DrawSceneOctree.cpp b/libraries/render/src/render/DrawSceneOctree.cpp index c62a77288e..9b22649a8d 100644 --- a/libraries/render/src/render/DrawSceneOctree.cpp +++ b/libraries/render/src/render/DrawSceneOctree.cpp @@ -80,9 +80,6 @@ const gpu::PipelinePointer DrawSceneOctree::getDrawLODReticlePipeline() { void DrawSceneOctree::configure(const Config& config) { _showVisibleCells = config.showVisibleCells; _showEmptyCells = config.showEmptyCells; - - _justFrozeFrustum = (config.freezeFrustum && !_freezeFrustum); - _freezeFrustum = config.freezeFrustum; } diff --git a/libraries/render/src/render/DrawSceneOctree.h b/libraries/render/src/render/DrawSceneOctree.h index 63e7203b38..60fc0bb2c4 100644 --- a/libraries/render/src/render/DrawSceneOctree.h +++ b/libraries/render/src/render/DrawSceneOctree.h @@ -22,7 +22,6 @@ namespace render { Q_PROPERTY(bool enabled MEMBER enabled NOTIFY dirty()) Q_PROPERTY(bool showVisibleCells MEMBER showVisibleCells WRITE setShowVisibleCells) Q_PROPERTY(bool showEmptyCells MEMBER showEmptyCells WRITE setShowEmptyCells) - Q_PROPERTY(bool freezeFrustum MEMBER freezeFrustum WRITE setFreezeFrustum) Q_PROPERTY(int numAllocatedCells READ getNumAllocatedCells) Q_PROPERTY(int numFreeCells READ getNumFreeCells) @@ -32,7 +31,6 @@ namespace render { bool showVisibleCells{ true }; bool showEmptyCells{ false }; - bool freezeFrustum{ false }; int numAllocatedCells{ 0 }; int numFreeCells{ 0 }; @@ -43,7 +41,6 @@ namespace render { public slots: void setShowVisibleCells(bool show) { showVisibleCells = show; emit dirty(); } void setShowEmptyCells(bool show) { showEmptyCells = show; emit dirty(); } - void setFreezeFrustum(bool freeze) { freezeFrustum = freeze; emit dirty(); } signals: void dirty(); @@ -63,9 +60,6 @@ namespace render { bool _showVisibleCells; // initialized by Config bool _showEmptyCells; // initialized by Config - bool _freezeFrustum{ false }; // initialized by Config - bool _justFrozeFrustum{ false }; - ViewFrustum _frozenFrutstum; public: using Config = DrawSceneOctreeConfig; From bcd7876f6cb8b890b883c98a8ca6df91b7827d0a Mon Sep 17 00:00:00 2001 From: samcake Date: Fri, 12 Feb 2016 16:53:24 -0800 Subject: [PATCH 47/49] adding better performance stats --- libraries/render/src/render/CullTask.cpp | 80 ++++++++++++++++-------- 1 file changed, 53 insertions(+), 27 deletions(-) diff --git a/libraries/render/src/render/CullTask.cpp b/libraries/render/src/render/CullTask.cpp index adc4114d6c..cb37b566ed 100644 --- a/libraries/render/src/render/CullTask.cpp +++ b/libraries/render/src/render/CullTask.cpp @@ -205,7 +205,6 @@ void CullSpatialSelection::run(const SceneContextPointer& sceneContext, const Re details._considered += inSelection.numItems(); // Eventually use a frozen frustum - auto queryFrustum = args->_viewFrustum; auto argFrustum = args->_viewFrustum; if (_freezeFrustum) { if (_justFrozeFrustum) { @@ -220,12 +219,22 @@ void CullSpatialSelection::run(const SceneContextPointer& sceneContext, const Re CullFunctor _functor; RenderArgs* _args; RenderDetails::Item& _renderDetails; + glm::vec3 _eyePos; + float _squareTanAlpha; Test(CullFunctor& functor, RenderArgs* pargs, RenderDetails::Item& renderDetails) : _functor(functor), _args(pargs), _renderDetails(renderDetails) - {} + { + _eyePos = _args->_viewFrustum->getPosition(); + + float a = glm::degrees(_args->_viewFrustum->getAccuracyAngle(_args->_sizeScale, _args->_boundaryLevelAdjust)); + auto angle = std::min(glm::radians(45.0f), a); // no worse than 45 degrees + angle = std::max(glm::radians(1.0f / 60.0f), a); // no better than 1 minute of degree + auto tanAlpha = tan(angle); + _squareTanAlpha = (float)(tanAlpha * tanAlpha); + } bool frustumTest(const AABox& bound) { PerformanceTimer perfTimer("frustumItemTest"); @@ -238,6 +247,11 @@ void CullSpatialSelection::run(const SceneContextPointer& sceneContext, const Re bool solidAngleTest(const AABox& bound) { PerformanceTimer perfTimer("solidAngleItemTest"); + // FIXME: Keep this code here even though we don't use it yet + //auto eyeToPoint = bound.calcCenter() - _eyePos; + //auto boundSize = bound.getDimensions(); + //float test = (glm::dot(boundSize, boundSize) / glm::dot(eyeToPoint, eyeToPoint)) - squareTanAlpha; + //if (test < 0.0f) { if (!_functor(_args, bound)) { _renderDetails._tooSmall++; return false; @@ -255,46 +269,58 @@ void CullSpatialSelection::run(const SceneContextPointer& sceneContext, const Re // filter individually against the _filter // visibility cull if partially selected ( octree cell contianing it was partial) // distance cull if was a subcell item ( octree cell is way bigger than the item bound itself, so now need to test per item) - + // inside & fit items: easy, just filter - for (auto id : inSelection.insideItems) { - auto& item = scene->getItem(id); - if (_filter.test(item.getKey())) { - ItemBound itemBound(id, item.getBound()); - outItems.emplace_back(itemBound); + { + PerformanceTimer perfTimer("insideFitItems"); + for (auto id : inSelection.insideItems) { + auto& item = scene->getItem(id); + if (_filter.test(item.getKey())) { + ItemBound itemBound(id, item.getBound()); + outItems.emplace_back(itemBound); + } } } // inside & subcell items: filter & distance cull - for (auto id : inSelection.insideSubcellItems) { - auto& item = scene->getItem(id); - if (_filter.test(item.getKey())) { - ItemBound itemBound(id, item.getBound()); - if (test.solidAngleTest(itemBound.bound)) { - outItems.emplace_back(itemBound); + { + PerformanceTimer perfTimer("insideSmallItems"); + for (auto id : inSelection.insideSubcellItems) { + auto& item = scene->getItem(id); + if (_filter.test(item.getKey())) { + ItemBound itemBound(id, item.getBound()); + if (test.solidAngleTest(itemBound.bound)) { + outItems.emplace_back(itemBound); + } } } } // partial & fit items: filter & frustum cull - for (auto id : inSelection.partialItems) { - auto& item = scene->getItem(id); - if (_filter.test(item.getKey())) { - ItemBound itemBound(id, item.getBound()); - if (test.frustumTest(itemBound.bound)) { - outItems.emplace_back(itemBound); + { + PerformanceTimer perfTimer("partialFitItems"); + for (auto id : inSelection.partialItems) { + auto& item = scene->getItem(id); + if (_filter.test(item.getKey())) { + ItemBound itemBound(id, item.getBound()); + if (test.frustumTest(itemBound.bound)) { + outItems.emplace_back(itemBound); + } } } } // partial & subcell items:: filter & frutum cull & solidangle cull - for (auto id : inSelection.partialSubcellItems) { - auto& item = scene->getItem(id); - if (_filter.test(item.getKey())) { - ItemBound itemBound(id, item.getBound()); - if (test.frustumTest(itemBound.bound)) { - if (test.solidAngleTest(itemBound.bound)) { - outItems.emplace_back(itemBound); + { + PerformanceTimer perfTimer("partialSmallItems"); + for (auto id : inSelection.partialSubcellItems) { + auto& item = scene->getItem(id); + if (_filter.test(item.getKey())) { + ItemBound itemBound(id, item.getBound()); + if (test.frustumTest(itemBound.bound)) { + if (test.solidAngleTest(itemBound.bound)) { + outItems.emplace_back(itemBound); + } } } } From 21c9b74a6867a01ad6388a2bfaa178350f32b212 Mon Sep 17 00:00:00 2001 From: samcake Date: Fri, 12 Feb 2016 17:07:53 -0800 Subject: [PATCH 48/49] Cleaning up performance timers which are taking too much time... --- libraries/render/src/render/CullTask.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/libraries/render/src/render/CullTask.cpp b/libraries/render/src/render/CullTask.cpp index cb37b566ed..cf4e825ea3 100644 --- a/libraries/render/src/render/CullTask.cpp +++ b/libraries/render/src/render/CullTask.cpp @@ -227,17 +227,17 @@ void CullSpatialSelection::run(const SceneContextPointer& sceneContext, const Re _args(pargs), _renderDetails(renderDetails) { - _eyePos = _args->_viewFrustum->getPosition(); - + // FIXME: Keep this code here even though we don't use it yet + /*_eyePos = _args->_viewFrustum->getPosition(); float a = glm::degrees(_args->_viewFrustum->getAccuracyAngle(_args->_sizeScale, _args->_boundaryLevelAdjust)); auto angle = std::min(glm::radians(45.0f), a); // no worse than 45 degrees angle = std::max(glm::radians(1.0f / 60.0f), a); // no better than 1 minute of degree auto tanAlpha = tan(angle); _squareTanAlpha = (float)(tanAlpha * tanAlpha); + */ } bool frustumTest(const AABox& bound) { - PerformanceTimer perfTimer("frustumItemTest"); if (_args->_viewFrustum->boxInFrustum(bound) == ViewFrustum::OUTSIDE) { _renderDetails._outOfView++; return false; @@ -246,7 +246,6 @@ void CullSpatialSelection::run(const SceneContextPointer& sceneContext, const Re } bool solidAngleTest(const AABox& bound) { - PerformanceTimer perfTimer("solidAngleItemTest"); // FIXME: Keep this code here even though we don't use it yet //auto eyeToPoint = bound.calcCenter() - _eyePos; //auto boundSize = bound.getDimensions(); From 0c0089f736edd925c8e58c065092683d88c78304 Mon Sep 17 00:00:00 2001 From: samcake Date: Mon, 15 Feb 2016 11:08:12 -0800 Subject: [PATCH 49/49] REnamed Octree.h/cpp to SpatialTree and fixing some coding guidelines --- interface/src/Application.h | 2 +- libraries/render/src/render/Scene.cpp | 4 +- libraries/render/src/render/Scene.h | 4 +- .../render/{Octree.cpp => SpatialTree.cpp} | 4 +- .../src/render/{Octree.h => SpatialTree.h} | 43 +++++++++++-------- 5 files changed, 34 insertions(+), 23 deletions(-) rename libraries/render/src/render/{Octree.cpp => SpatialTree.cpp} (99%) rename libraries/render/src/render/{Octree.h => SpatialTree.h} (93%) diff --git a/interface/src/Application.h b/interface/src/Application.h index 3880263aee..d5b677302a 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -479,7 +479,7 @@ private: quint64 _lastFaceTrackerUpdate; - render::ScenePointer _main3DScene{ new render::Scene() }; + render::ScenePointer _main3DScene{ new render::Scene(glm::vec3(-0.5f * (float)TREE_SCALE), (float)TREE_SCALE) }; render::EnginePointer _renderEngine{ new render::Engine() }; gpu::ContextPointer _gpuContext; // initialized during window creation diff --git a/libraries/render/src/render/Scene.cpp b/libraries/render/src/render/Scene.cpp index 43187feb66..c9e83f07aa 100644 --- a/libraries/render/src/render/Scene.cpp +++ b/libraries/render/src/render/Scene.cpp @@ -79,7 +79,9 @@ void PendingChanges::merge(PendingChanges& changes) { _updateFunctors.insert(_updateFunctors.end(), changes._updateFunctors.begin(), changes._updateFunctors.end()); } -Scene::Scene() { +Scene::Scene(glm::vec3 origin, float size) : + _masterSpatialTree(origin, size) +{ _items.push_back(Item()); // add the itemID #0 to nothing _masterBucketMap.allocateStandardOpaqueTranparentBuckets(); } diff --git a/libraries/render/src/render/Scene.h b/libraries/render/src/render/Scene.h index d3d5a6865c..7972507979 100644 --- a/libraries/render/src/render/Scene.h +++ b/libraries/render/src/render/Scene.h @@ -13,7 +13,7 @@ #define hifi_render_Scene_h #include "Item.h" -#include "Octree.h" +#include "SpatialTree.h" namespace render { @@ -68,7 +68,7 @@ typedef std::queue PendingChangesQueue; // Items are notified accordingly on any update message happening class Scene { public: - Scene(); + Scene(glm::vec3 origin, float size); ~Scene() {} /// This call is thread safe, can be called from anywhere to allocate a new ID diff --git a/libraries/render/src/render/Octree.cpp b/libraries/render/src/render/SpatialTree.cpp similarity index 99% rename from libraries/render/src/render/Octree.cpp rename to libraries/render/src/render/SpatialTree.cpp index 9e655b6719..b09b6f4778 100644 --- a/libraries/render/src/render/Octree.cpp +++ b/libraries/render/src/render/SpatialTree.cpp @@ -1,5 +1,5 @@ // -// Octree.h +// SpatialTree.h // render/src/render // // Created by Sam Gateau on 1/25/16. @@ -8,7 +8,7 @@ // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#include "Octree.h" +#include "SpatialTree.h" #include diff --git a/libraries/render/src/render/Octree.h b/libraries/render/src/render/SpatialTree.h similarity index 93% rename from libraries/render/src/render/Octree.h rename to libraries/render/src/render/SpatialTree.h index f283f5b3e2..a5dbb29544 100644 --- a/libraries/render/src/render/Octree.h +++ b/libraries/render/src/render/SpatialTree.h @@ -1,5 +1,5 @@ // -// Octree.h +// SpatialTree.h // render/src/render // // Created by Sam Gateau on 1/25/16. @@ -89,9 +89,9 @@ namespace render { // Max depth is 15 => 32Km root down to 1m cells using Depth = int8_t; - static const Depth ROOT_DEPTH{ 0 }; - static const Depth MAX_DEPTH{ 15 }; - static const Depth METRIC_COORD_DEPTH{ 15 }; + static const Depth ROOT_DEPTH { 0 }; + static const Depth MAX_DEPTH { 15 }; + static const Depth METRIC_COORD_DEPTH { 15 }; static const float INV_DEPTH_DIM[Octree::MAX_DEPTH + 1]; static int getDepthDimension(Depth depth) { return 1 << depth; } @@ -109,12 +109,12 @@ namespace render { static Coord depthBitmask(Depth depth) { return Coord(1 << (MAX_DEPTH - depth)); } static Depth coordToDepth(Coord length) { - Depth d = MAX_DEPTH; + Depth depth = MAX_DEPTH; while (length) { length >>= 1; - d--; + depth--; } - return d; + return depth; } @@ -130,16 +130,16 @@ namespace render { Location(const Coord3& xyz, Depth d) : pos(xyz), depth(d) { assertValid(); } Location(Depth d) : pos(0), depth(d) { assertValid(); } - Coord3 pos{ 0 }; - uint8_t spare{ 0 }; - Depth depth{ ROOT_DEPTH }; + Coord3 pos { 0 }; + uint8_t spare { 0 }; + Depth depth { ROOT_DEPTH }; Coord3f getCenter() const { Coord3f center = (Coord3f(pos) + Coord3f(0.5f)) * Coord3f(Octree::getInvDepthDimension(depth)); return center; } - bool operator== (const Location& right) const { return pos == right.pos && depth == right.depth; } + bool operator== (const Location& other) const { return pos == other.pos && depth == other.depth; } // Eval the octant of this cell relative to its parent Octant octant() const { return Octant((pos.x & 1) | ((pos.y & 1) << 1) | ((pos.z & 1) << 2)); } @@ -316,9 +316,11 @@ namespace render { float angle; float squareTanAlpha; + const float MAX_LOD_ANGLE = glm::radians(45.0f); + const float MIN_LOD_ANGLE = glm::radians(1.0f / 60.0f); + void setAngle(float a) { - angle = std::min(glm::radians(45.0f), a); // no worse than 45 degrees - angle = std::max(glm::radians(1.0f/60.0f), a); // no better than 1 minute of degree + angle = std::max(MIN_LOD_ANGLE, std::min(MAX_LOD_ANGLE, a)); auto tanAlpha = tan(angle); squareTanAlpha = (float)(tanAlpha * tanAlpha); } @@ -365,11 +367,18 @@ namespace render { // An octree of Items organizing them efficiently for culling // The octree only cares about the bound & the key of an item to store it a the right cell location class ItemSpatialTree : public Octree { - float _size{ 32768.0f }; - float _invSize{ 1.0f / _size }; - glm::vec3 _origin{ -16384.0f }; + float _size { 32768.0f }; + float _invSize { 1.0f / _size }; + glm::vec3 _origin { -16384.0f }; + + void init(glm::vec3 origin, float size) { + _size = size; + _invSize = 1.0f / _size; + _origin = origin; + } public: - ItemSpatialTree() {} + // THe overall size and origin of the tree are defined at creation + ItemSpatialTree(glm::vec3 origin, float size) { init(origin, size); } float getSize() const { return _size; } const glm::vec3& getOrigin() const { return _origin; }