From 3804917cf46abb3f9919e252d68ad41a0aa5b6c3 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Fri, 2 Feb 2018 09:40:57 +0100 Subject: [PATCH] Orthographic octree selection seems to be working --- libraries/gpu/src/gpu/Framebuffer.cpp | 2 +- libraries/octree/src/OctreeUtils.cpp | 8 +- libraries/octree/src/OctreeUtils.h | 3 +- .../render-utils/src/RenderShadowTask.cpp | 50 +++++++++---- libraries/render-utils/src/RenderShadowTask.h | 10 +-- libraries/render-utils/src/Shadow.slh | 5 -- libraries/render-utils/src/ShadowCore.slh | 4 +- libraries/render/src/render/CullTask.cpp | 30 +++++--- libraries/render/src/render/CullTask.h | 3 +- .../render/src/render/DrawSceneOctree.cpp | 2 +- .../src/render/RenderFetchCullSortTask.cpp | 2 +- libraries/render/src/render/SpatialTree.cpp | 74 +++++++++++++------ libraries/render/src/render/SpatialTree.h | 31 ++++---- libraries/shared/src/ViewFrustum.cpp | 4 + libraries/shared/src/ViewFrustum.h | 1 + 15 files changed, 152 insertions(+), 77 deletions(-) diff --git a/libraries/gpu/src/gpu/Framebuffer.cpp b/libraries/gpu/src/gpu/Framebuffer.cpp index 4fcc9ab0f9..8bb9be4a76 100755 --- a/libraries/gpu/src/gpu/Framebuffer.cpp +++ b/libraries/gpu/src/gpu/Framebuffer.cpp @@ -62,7 +62,7 @@ Framebuffer* Framebuffer::createShadowmap(uint16 width) { samplerDesc._wrapModeU = Sampler::WRAP_BORDER; samplerDesc._wrapModeV = Sampler::WRAP_BORDER; samplerDesc._filter = Sampler::FILTER_MIN_MAG_LINEAR; - samplerDesc._comparisonFunc = LESS_EQUAL; + samplerDesc._comparisonFunc = LESS; depthTexture->setSampler(Sampler(samplerDesc)); framebuffer->setDepthStencilBuffer(depthTexture, depthFormat); diff --git a/libraries/octree/src/OctreeUtils.cpp b/libraries/octree/src/OctreeUtils.cpp index c55016d8e2..ca15324d4e 100644 --- a/libraries/octree/src/OctreeUtils.cpp +++ b/libraries/octree/src/OctreeUtils.cpp @@ -64,8 +64,14 @@ float boundaryDistanceForRenderLevel(unsigned int renderLevel, float voxelSizeSc return voxelSizeScale / powf(2.0f, renderLevel); } -float getAccuracyAngle(float octreeSizeScale, int boundaryLevelAdjust) { +float getPerspectiveAccuracyAngle(float octreeSizeScale, int boundaryLevelAdjust) { const float maxScale = (float)TREE_SCALE; float visibleDistanceAtMaxScale = boundaryDistanceForRenderLevel(boundaryLevelAdjust, octreeSizeScale) / OCTREE_TO_MESH_RATIO; return atan(maxScale / visibleDistanceAtMaxScale); } + +float getOrthographicAccuracySize(float octreeSizeScale, int boundaryLevelAdjust) { + // Smallest visible element is 1cm + const float smallestSize = 0.01f; + return (smallestSize * MAX_VISIBILITY_DISTANCE_FOR_UNIT_ELEMENT) / boundaryDistanceForRenderLevel(boundaryLevelAdjust, octreeSizeScale); +} diff --git a/libraries/octree/src/OctreeUtils.h b/libraries/octree/src/OctreeUtils.h index c257bcd5f1..0f87bb6f68 100644 --- a/libraries/octree/src/OctreeUtils.h +++ b/libraries/octree/src/OctreeUtils.h @@ -25,7 +25,8 @@ float calculateRenderAccuracy(const glm::vec3& position, float boundaryDistanceForRenderLevel(unsigned int renderLevel, float voxelSizeScale); -float getAccuracyAngle(float octreeSizeScale, int boundaryLevelAdjust); +float getPerspectiveAccuracyAngle(float octreeSizeScale, int boundaryLevelAdjust); +float getOrthographicAccuracySize(float octreeSizeScale, int boundaryLevelAdjust); // MIN_ELEMENT_ANGULAR_DIAMETER = angular diameter of 1x1x1m cube at 400m = sqrt(3) / 400 = 0.0043301 radians ~= 0.25 degrees const float MIN_ELEMENT_ANGULAR_DIAMETER = 0.0043301f; // radians diff --git a/libraries/render-utils/src/RenderShadowTask.cpp b/libraries/render-utils/src/RenderShadowTask.cpp index c9df67dad1..b576bf774c 100644 --- a/libraries/render-utils/src/RenderShadowTask.cpp +++ b/libraries/render-utils/src/RenderShadowTask.cpp @@ -214,9 +214,10 @@ void RenderShadowTask::build(JobModel& task, const render::Varying& input, rende } const auto setupOutput = task.addJob("ShadowSetup"); + const auto queryResolution = setupOutput.getN(2); // Fetch and cull the items from the scene static const auto shadowCasterFilter = ItemFilter::Builder::visibleWorldItems().withTypeShape().withOpaque().withoutLayered(); - const auto fetchInput = render::Varying(shadowCasterFilter); + const auto fetchInput = FetchSpatialTree::Inputs(shadowCasterFilter, queryResolution).asVarying(); const auto shadowSelection = task.addJob("FetchShadowTree", fetchInput); const auto selectionInputs = FetchSpatialSelection::Inputs(shadowSelection, shadowCasterFilter).asVarying(); const auto shadowItems = task.addJob("FetchShadowSelection", selectionInputs); @@ -281,14 +282,15 @@ void RenderShadowSetup::run(const render::RenderContextPointer& renderContext, O output.edit0() = args->_renderMode; output.edit1() = args->_sizeScale; + output.edit2() = glm::ivec2(0, 0); const auto globalShadow = lightStage->getCurrentKeyShadow(); if (globalShadow) { globalShadow->setKeylightFrustum(args->getViewFrustum(), SHADOW_FRUSTUM_NEAR, SHADOW_FRUSTUM_FAR); - auto& firstCascadeFrustum = globalShadow->getCascade(0).getFrustum(); + + auto& firstCascade = globalShadow->getCascade(0); + auto& firstCascadeFrustum = firstCascade.getFrustum(); unsigned int cascadeIndex; - _coarseShadowFrustum->setPosition(firstCascadeFrustum->getPosition()); - _coarseShadowFrustum->setOrientation(firstCascadeFrustum->getOrientation()); // Adjust each cascade frustum for (cascadeIndex = 0; cascadeIndex < globalShadow->getCascadeCount(); ++cascadeIndex) { @@ -297,19 +299,29 @@ void RenderShadowSetup::run(const render::RenderContextPointer& renderContext, O SHADOW_FRUSTUM_NEAR, SHADOW_FRUSTUM_FAR, bias._constant, bias._slope); } + // Now adjust coarse frustum bounds - auto left = glm::dot(firstCascadeFrustum->getFarTopLeft(), firstCascadeFrustum->getRight()); - auto right = glm::dot(firstCascadeFrustum->getFarTopRight(), firstCascadeFrustum->getRight()); - auto top = glm::dot(firstCascadeFrustum->getFarTopLeft(), firstCascadeFrustum->getUp()); - auto bottom = glm::dot(firstCascadeFrustum->getFarBottomRight(), firstCascadeFrustum->getUp()); + auto frustumPosition = firstCascadeFrustum->getPosition(); + auto farTopLeft = firstCascadeFrustum->getFarTopLeft() - frustumPosition; + auto farBottomRight = firstCascadeFrustum->getFarBottomRight() - frustumPosition; + + auto left = glm::dot(farTopLeft, firstCascadeFrustum->getRight()); + auto right = glm::dot(farBottomRight, firstCascadeFrustum->getRight()); + auto top = glm::dot(farTopLeft, firstCascadeFrustum->getUp()); + auto bottom = glm::dot(farBottomRight, firstCascadeFrustum->getUp()); auto near = firstCascadeFrustum->getNearClip(); auto far = firstCascadeFrustum->getFarClip(); + for (cascadeIndex = 1; cascadeIndex < globalShadow->getCascadeCount(); ++cascadeIndex) { auto& cascadeFrustum = globalShadow->getCascade(cascadeIndex).getFrustum(); - auto cascadeLeft = glm::dot(cascadeFrustum->getFarTopLeft(), cascadeFrustum->getRight()); - auto cascadeRight = glm::dot(cascadeFrustum->getFarTopRight(), cascadeFrustum->getRight()); - auto cascadeTop = glm::dot(cascadeFrustum->getFarTopLeft(), cascadeFrustum->getUp()); - auto cascadeBottom = glm::dot(cascadeFrustum->getFarBottomRight(), cascadeFrustum->getUp()); + + farTopLeft = cascadeFrustum->getFarTopLeft() - frustumPosition; + farBottomRight = cascadeFrustum->getFarBottomRight() - frustumPosition; + + auto cascadeLeft = glm::dot(farTopLeft, cascadeFrustum->getRight()); + auto cascadeRight = glm::dot(farBottomRight, cascadeFrustum->getRight()); + auto cascadeTop = glm::dot(farTopLeft, cascadeFrustum->getUp()); + auto cascadeBottom = glm::dot(farBottomRight, cascadeFrustum->getUp()); auto cascadeNear = cascadeFrustum->getNearClip(); auto cascadeFar = cascadeFrustum->getFarClip(); left = glm::min(left, cascadeLeft); @@ -319,6 +331,9 @@ void RenderShadowSetup::run(const render::RenderContextPointer& renderContext, O near = glm::min(near, cascadeNear); far = glm::max(far, cascadeFar); } + + _coarseShadowFrustum->setPosition(firstCascadeFrustum->getPosition()); + _coarseShadowFrustum->setOrientation(firstCascadeFrustum->getOrientation()); _coarseShadowFrustum->setProjection(glm::ortho(left, right, bottom, top, near, far)); _coarseShadowFrustum->calculate(); @@ -326,10 +341,13 @@ void RenderShadowSetup::run(const render::RenderContextPointer& renderContext, O args->pushViewFrustum(*_coarseShadowFrustum); args->_renderMode = RenderArgs::SHADOW_RENDER_MODE; - if (lightStage->getCurrentKeyLight()->getType() == graphics::Light::SUN) { - // Set to ridiculously high amount to prevent solid angle culling in octree selection - args->_sizeScale = 1e16f; - } + + // We want for the octree query enough resolution to catch the details in the lowest cascade. So compute + // the desired resolution for the first cascade frustum and extrapolate it to the coarse frustum. + glm::ivec2 queryResolution = firstCascade.framebuffer->getSize(); + queryResolution.x = int(queryResolution.x * _coarseShadowFrustum->getWidth() / firstCascadeFrustum->getWidth()); + queryResolution.y = int(queryResolution.y * _coarseShadowFrustum->getHeight() / firstCascadeFrustum->getHeight()); + output.edit2() = queryResolution; } } diff --git a/libraries/render-utils/src/RenderShadowTask.h b/libraries/render-utils/src/RenderShadowTask.h index 1736d07fd5..ce4c3047d8 100644 --- a/libraries/render-utils/src/RenderShadowTask.h +++ b/libraries/render-utils/src/RenderShadowTask.h @@ -71,10 +71,10 @@ public: float constantBias1{ 0.15f }; float constantBias2{ 0.15f }; float constantBias3{ 0.15f }; - float slopeBias0{ 0.55f }; - float slopeBias1{ 0.55f }; - float slopeBias2{ 0.55f }; - float slopeBias3{ 0.55f }; + float slopeBias0{ 0.6f }; + float slopeBias1{ 0.6f }; + float slopeBias2{ 0.6f }; + float slopeBias3{ 0.6f }; signals: void dirty(); @@ -82,7 +82,7 @@ signals: class RenderShadowSetup { public: - using Outputs = render::VaryingSet2; + using Outputs = render::VaryingSet3; using Config = RenderShadowSetupConfig; using JobModel = render::Job::ModelO; diff --git a/libraries/render-utils/src/Shadow.slh b/libraries/render-utils/src/Shadow.slh index 6575e68090..e87519b5f4 100644 --- a/libraries/render-utils/src/Shadow.slh +++ b/libraries/render-utils/src/Shadow.slh @@ -79,15 +79,10 @@ float evalShadowAttenuationPCF(int cascadeIndex, ShadowSampleOffsets offsets, ve fetchShadow(cascadeIndex, shadowTexcoord.xyz + offsets.points[2]) + fetchShadow(cascadeIndex, shadowTexcoord.xyz + offsets.points[3]) ); - return shadowAttenuation; } float evalShadowCascadeAttenuation(int cascadeIndex, ShadowSampleOffsets offsets, vec4 shadowTexcoord, float oneMinusNdotL) { - if (!isShadowCascadeProjectedOnPixel(shadowTexcoord)) { - // If a point is not in the map, do not attenuate - return 1.0; - } float bias = getShadowFixedBias(cascadeIndex) + getShadowSlopeBias(cascadeIndex) * oneMinusNdotL; return evalShadowAttenuationPCF(cascadeIndex, offsets, shadowTexcoord, bias); } diff --git a/libraries/render-utils/src/ShadowCore.slh b/libraries/render-utils/src/ShadowCore.slh index 782e2bc2b8..e49e8d638f 100644 --- a/libraries/render-utils/src/ShadowCore.slh +++ b/libraries/render-utils/src/ShadowCore.slh @@ -53,8 +53,8 @@ vec4 evalShadowTexcoord(int cascadeIndex, vec4 position) { } bool isShadowCascadeProjectedOnPixel(vec4 cascadeTexCoords) { - bvec2 greaterThanZero = greaterThanEqual(cascadeTexCoords.xy, vec2(0)); - bvec2 lessThanOne = lessThanEqual(cascadeTexCoords.xy, vec2(1)); + bvec2 greaterThanZero = greaterThan(cascadeTexCoords.xy, vec2(0)); + bvec2 lessThanOne = lessThan(cascadeTexCoords.xy, vec2(1)); return all(greaterThanZero) && all(lessThanOne); } diff --git a/libraries/render/src/render/CullTask.cpp b/libraries/render/src/render/CullTask.cpp index c6ff224560..b3efc4f1a8 100644 --- a/libraries/render/src/render/CullTask.cpp +++ b/libraries/render/src/render/CullTask.cpp @@ -14,8 +14,8 @@ #include #include -#include #include +#include using namespace render; @@ -33,7 +33,7 @@ struct Test { _renderDetails(renderDetails) { // FIXME: Keep this code here even though we don't use it yet /*_eyePos = _args->getViewFrustum().getPosition(); - float a = glm::degrees(Octree::getAccuracyAngle(_args->_sizeScale, _args->_boundaryLevelAdjust)); + float a = glm::degrees(Octree::getPerspectiveAccuracyAngle(_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); @@ -130,7 +130,8 @@ void FetchSpatialTree::run(const RenderContextPointer& renderContext, const Inpu // start fresh outSelection.clear(); - auto& filter = inputs; + auto& filter = inputs.get0(); + auto frustumResolution = inputs.get1(); if (!filter.selectsNothing()) { assert(renderContext->args); @@ -149,8 +150,19 @@ void FetchSpatialTree::run(const RenderContextPointer& renderContext, const Inpu } // Octree selection! - float angle = glm::degrees(getAccuracyAngle(args->_sizeScale, args->_boundaryLevelAdjust)); - scene->getSpatialTree().selectCellItems(outSelection, filter, queryFrustum, angle); + float threshold = 0.0f; + if (queryFrustum.isPerspective()) { + threshold = getPerspectiveAccuracyAngle(args->_sizeScale, args->_boundaryLevelAdjust); + if (frustumResolution.y > 0) { + threshold = glm::max(queryFrustum.getFieldOfView() / frustumResolution.y, threshold); + } + } else { + threshold = getOrthographicAccuracySize(args->_sizeScale, args->_boundaryLevelAdjust); + glm::vec2 frustumSize = glm::vec2(queryFrustum.getWidth(), queryFrustum.getHeight()); + const auto pixelResolution = frustumResolution.x > 0 ? frustumResolution : glm::ivec2(2048, 2048); + threshold = glm::max(threshold, glm::min(frustumSize.x / pixelResolution.x, frustumSize.y / pixelResolution.y)); + } + scene->getSpatialTree().selectCellItems(outSelection, filter, queryFrustum, threshold); } } @@ -371,7 +383,7 @@ void FetchSpatialSelection::run(const RenderContextPointer& renderContext, // Now get the bound, and // filter individually against the _filter - // inside & fit items: filter only, culling is disabled + // inside & fit items: filter only { PerformanceTimer perfTimer("insideFitItems"); for (auto id : inSelection.insideItems) { @@ -383,7 +395,7 @@ void FetchSpatialSelection::run(const RenderContextPointer& renderContext, } } - // inside & subcell items: filter only, culling is disabled + // inside & subcell items: filter only { PerformanceTimer perfTimer("insideSmallItems"); for (auto id : inSelection.insideSubcellItems) { @@ -395,7 +407,7 @@ void FetchSpatialSelection::run(const RenderContextPointer& renderContext, } } - // partial & fit items: filter only, culling is disabled + // partial & fit items: filter only { PerformanceTimer perfTimer("partialFitItems"); for (auto id : inSelection.partialItems) { @@ -407,7 +419,7 @@ void FetchSpatialSelection::run(const RenderContextPointer& renderContext, } } - // partial & subcell items: filter only, culling is disabled + // partial & subcell items: filter only { PerformanceTimer perfTimer("partialSmallItems"); for (auto id : inSelection.partialSubcellItems) { diff --git a/libraries/render/src/render/CullTask.h b/libraries/render/src/render/CullTask.h index a140a86aee..53d46d11b4 100644 --- a/libraries/render/src/render/CullTask.h +++ b/libraries/render/src/render/CullTask.h @@ -53,9 +53,10 @@ namespace render { bool _justFrozeFrustum{ false }; ViewFrustum _frozenFrustum; float _lodAngle; + public: using Config = FetchSpatialTreeConfig; - using Inputs = ItemFilter; + using Inputs = render::VaryingSet2; using JobModel = Job::ModelIO; FetchSpatialTree() {} diff --git a/libraries/render/src/render/DrawSceneOctree.cpp b/libraries/render/src/render/DrawSceneOctree.cpp index 36663a454a..08d6340e43 100644 --- a/libraries/render/src/render/DrawSceneOctree.cpp +++ b/libraries/render/src/render/DrawSceneOctree.cpp @@ -148,7 +148,7 @@ void DrawSceneOctree::run(const RenderContextPointer& renderContext, const ItemS } // Draw the LOD Reticle { - float angle = glm::degrees(getAccuracyAngle(args->_sizeScale, args->_boundaryLevelAdjust)); + float angle = glm::degrees(getPerspectiveAccuracyAngle(args->_sizeScale, args->_boundaryLevelAdjust)); Transform crosshairModel; crosshairModel.setTranslation(glm::vec3(0.0, 0.0, -1000.0)); crosshairModel.setScale(1000.0f * tanf(glm::radians(angle))); // Scaling at the actual tan of the lod angle => Multiplied by TWO diff --git a/libraries/render/src/render/RenderFetchCullSortTask.cpp b/libraries/render/src/render/RenderFetchCullSortTask.cpp index a1b4f079e7..23935851b3 100644 --- a/libraries/render/src/render/RenderFetchCullSortTask.cpp +++ b/libraries/render/src/render/RenderFetchCullSortTask.cpp @@ -23,7 +23,7 @@ void RenderFetchCullSortTask::build(JobModel& task, const Varying& input, Varyin // CPU jobs: // Fetch and cull the items from the scene const ItemFilter filter = ItemFilter::Builder::visibleWorldItems().withoutLayered(); - const auto fetchInput = render::Varying(filter); + const auto fetchInput = FetchSpatialTree::Inputs(filter, glm::ivec2(0,0)).asVarying(); const auto spatialSelection = task.addJob("FetchSceneSelection", fetchInput); const auto cullInputs = CullSpatialSelection::Inputs(spatialSelection, render::Varying(filter)).asVarying(); const auto culledSpatialSelection = task.addJob("CullSceneSelection", cullInputs, cullFunctor, RenderDetails::ITEM); diff --git a/libraries/render/src/render/SpatialTree.cpp b/libraries/render/src/render/SpatialTree.cpp index 1bb3538521..c9f810ebc5 100644 --- a/libraries/render/src/render/SpatialTree.cpp +++ b/libraries/render/src/render/SpatialTree.cpp @@ -12,9 +12,26 @@ #include - using namespace render; +void Octree::PerspectiveSelector::setAngle(float a) { + const float MAX_LOD_ANGLE = glm::radians(45.0f); + const float MIN_LOD_ANGLE = glm::radians(1.0f / 60.0f); + + angle = std::max(MIN_LOD_ANGLE, std::min(MAX_LOD_ANGLE, a)); + auto tanAlpha = tan(angle); + squareTanAlpha = (float)(tanAlpha * tanAlpha); +} + +float Octree::PerspectiveSelector::testThreshold(const Coord3f& point, float size) const { + auto eyeToPoint = point - eyePos; + return (size * size / glm::dot(eyeToPoint, eyeToPoint)) - squareTanAlpha; +} + +float Octree::OrthographicSelector::testThreshold(const Coord3f& point, float size) const { + return (size * size) - squareMinSize; +} + const float Octree::INV_DEPTH_DIM[] = { 1.0f, @@ -520,10 +537,9 @@ 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.0f) { + float test = selector.testThreshold(cellLocation.getCenter(), Octree::getCoordSubcellWidth(cellLocation.depth)); + if (test < 0.0f) { return 0; - break; } // Select this cell partially in frustum @@ -543,13 +559,13 @@ int Octree::selectTraverse(Index cellID, CellSelection& selection, const Frustum } -int Octree::selectBranch(Index cellID, CellSelection& selection, const FrustumSelector& selector) const { +int Octree::selectBranch(Index cellID, CellSelection& selection, const FrustumSelector& selector) const { int numSelectedsIn = (int) selection.size(); auto cell = getConcreteCell(cellID); auto cellLocation = cell.getlocation(); - float lod = selector.testSolidAngle(cellLocation.getCenter(), Octree::getCoordSubcellWidth(cellLocation.depth)); - if (lod < 0.0f) { + float test = selector.testThreshold(cellLocation.getCenter(), Octree::getCoordSubcellWidth(cellLocation.depth)); + if (test < 0.0f) { return 0; } @@ -580,24 +596,40 @@ int Octree::selectCellBrick(Index cellID, CellSelection& selection, bool inside) return (int) selection.size() - numSelectedsIn; } - -int ItemSpatialTree::selectCells(CellSelection& selection, const ViewFrustum& frustum, float lodAngle) const { +int ItemSpatialTree::selectCells(CellSelection& selection, const ViewFrustum& frustum, float threshold) const { auto worldPlanes = frustum.getPlanes(); - FrustumSelector selector; - for (int i = 0; i < ViewFrustum::NUM_PLANES; i++) { - ::Plane octPlane; - octPlane.setNormalAndPoint(worldPlanes[i].getNormal(), evalCoordf(worldPlanes[i].getPoint(), ROOT_DEPTH)); - selector.frustum[i] = Coord4f(octPlane.getNormal(), octPlane.getDCoefficient()); + if (frustum.isPerspective()) { + PerspectiveSelector selector; + for (int i = 0; i < ViewFrustum::NUM_PLANES; i++) { + ::Plane octPlane; + octPlane.setNormalAndPoint(worldPlanes[i].getNormal(), evalCoordf(worldPlanes[i].getPoint(), ROOT_DEPTH)); + selector.frustum[i] = Coord4f(octPlane.getNormal(), octPlane.getDCoefficient()); + } + + selector.eyePos = evalCoordf(frustum.getPosition(), ROOT_DEPTH); + selector.setAngle(threshold); + + return Octree::select(selection, selector); + } else { + OrthographicSelector selector; + for (int i = 0; i < ViewFrustum::NUM_PLANES; i++) { + ::Plane octPlane; + octPlane.setNormalAndPoint(worldPlanes[i].getNormal(), evalCoordf(worldPlanes[i].getPoint(), ROOT_DEPTH)); + selector.frustum[i] = Coord4f(octPlane.getNormal(), octPlane.getDCoefficient()); + } + + // Divide the threshold (which is in world distance units) by the dimension of the octree + // as all further computations will be done in normalized octree units + threshold *= getInvCellWidth(ROOT_DEPTH); + selector.setSize(threshold); + + return Octree::select(selection, selector); } - - selector.eyePos = evalCoordf(frustum.getPosition(), ROOT_DEPTH); - selector.setAngle(glm::radians(lodAngle)); - - return Octree::select(selection, selector); } -int ItemSpatialTree::selectCellItems(ItemSelection& selection, const ItemFilter& filter, const ViewFrustum& frustum, float lodAngle) const { - selectCells(selection.cellSelection, frustum, lodAngle); +int ItemSpatialTree::selectCellItems(ItemSelection& selection, const ItemFilter& filter, const ViewFrustum& frustum, + float threshold) const { + selectCells(selection.cellSelection, frustum, threshold); // Just grab the items in every selected bricks for (auto brickId : selection.cellSelection.insideBricks) { diff --git a/libraries/render/src/render/SpatialTree.h b/libraries/render/src/render/SpatialTree.h index a89b9847e6..b06053344d 100644 --- a/libraries/render/src/render/SpatialTree.h +++ b/libraries/render/src/render/SpatialTree.h @@ -312,23 +312,27 @@ namespace render { class FrustumSelector { public: Coord4f frustum[6]; + + virtual ~FrustumSelector() {} + virtual float testThreshold(const Coord3f& point, float size) const = 0; + }; + + class PerspectiveSelector : public FrustumSelector { + public: Coord3f eyePos; 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); + float testThreshold(const Coord3f& point, float size) const override; + }; - void setAngle(float a) { - angle = std::max(MIN_LOD_ANGLE, std::min(MAX_LOD_ANGLE, a)); - auto tanAlpha = tan(angle); - squareTanAlpha = (float)(tanAlpha * tanAlpha); - } + class OrthographicSelector : public FrustumSelector { + public: + float squareMinSize; - float testSolidAngle(const Coord3f& point, float size) const { - auto eyeToPoint = point - eyePos; - return (size * size / glm::dot(eyeToPoint, eyeToPoint)) - squareTanAlpha; - } + void setSize(float a) { squareMinSize = a * a; } + float testThreshold(const Coord3f& point, float size) const override; }; int select(CellSelection& selection, const FrustumSelector& selector) const; @@ -443,7 +447,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, float lodAngle) const; + int selectCells(CellSelection& selection, const ViewFrustum& frustum, float threshold) const; class ItemSelection { public: @@ -469,7 +473,8 @@ namespace render { } }; - int selectCellItems(ItemSelection& selection, const ItemFilter& filter, const ViewFrustum& frustum, float lodAngle) const; + int selectCellItems(ItemSelection& selection, const ItemFilter& filter, const ViewFrustum& frustum, + float threshold) const; }; } diff --git a/libraries/shared/src/ViewFrustum.cpp b/libraries/shared/src/ViewFrustum.cpp index 5b016d4e91..0f98e8020c 100644 --- a/libraries/shared/src/ViewFrustum.cpp +++ b/libraries/shared/src/ViewFrustum.cpp @@ -847,3 +847,7 @@ void ViewFrustum::tesselateSides(const glm::vec3 points[8], Triangle triangles[8 triangle.v2 = points[vertexIndices[2]]; } } + +bool ViewFrustum::isPerspective() const { + return _projection[3][2] != 0.0f && _projection[2][3] != 0.0f && _projection[3][3] == 0.0f; +} diff --git a/libraries/shared/src/ViewFrustum.h b/libraries/shared/src/ViewFrustum.h index b55fe8b327..859b5c49c5 100644 --- a/libraries/shared/src/ViewFrustum.h +++ b/libraries/shared/src/ViewFrustum.h @@ -49,6 +49,7 @@ public: // setters for lens attributes void setProjection(const glm::mat4 & projection); void setFocalLength(float focalLength) { _focalLength = focalLength; } + bool isPerspective() const; // getters for lens attributes const glm::mat4& getProjection() const { return _projection; }