diff --git a/interface/resources/qml/hifi/dialogs/TabletLODTools.qml b/interface/resources/qml/hifi/dialogs/TabletLODTools.qml index bb3d668850..ed0545e6ad 100644 --- a/interface/resources/qml/hifi/dialogs/TabletLODTools.qml +++ b/interface/resources/qml/hifi/dialogs/TabletLODTools.qml @@ -27,11 +27,15 @@ Rectangle { HifiConstants { id: hifi } - readonly property real treeScale: 32768; // ~20 miles.. This is the number of meters of the 0.0 to 1.0 voxel universe - readonly property real halfTreeScale: treeScale / 2; - - // This controls the LOD. Larger number will make smaller voxels visible at greater distance. - readonly property real defaultOctreeSizeScale: treeScale * 400.0 + // This controls the LOD. Larger number will make smaller objects visible at greater distance. + readonly property real defaultMaxVisibilityDistance: 400.0 + readonly property real unitElementMaxExtent: Math.sqrt(3.0) * 0.5 + + function visibilityDistanceToLODAngleDeg(visibilityDistance) { + var lodHalfAngle = Math.atan(unitElementMaxExtent / visibilityDistance); + var lodAngle = lodHalfAngle * 2.0; + return lodAngle * 180.0 / Math.PI; + } Column { anchors.margins: 10 @@ -71,7 +75,7 @@ Rectangle { id: adjustCheckbox boxSize: 20 anchors.verticalCenter: parent.verticalCenter - onCheckedChanged: LODManager.setAutomaticLODAdjust(!checked); + onCheckedChanged: LODManager.setAutomaticLODAdjust(!adjustCheckbox.checked); } } @@ -89,10 +93,10 @@ Rectangle { anchors.right: parent.right minimumValue: 5 maximumValue: 2000 - value: LODManager.getOctreeSizeScale() / treeScale + value: defaultMaxVisibilityDistance tickmarksEnabled: false onValueChanged: { - LODManager.setOctreeSizeScale(value * treeScale); + LODManager.lodAngleDeg = visibilityDistanceToLODAngleDeg(slider.value); whatYouCanSeeLabel.text = LODManager.getLODFeedbackText() } } @@ -106,7 +110,7 @@ Rectangle { colorScheme: root.colorScheme height: 30 onClicked: { - slider.value = defaultOctreeSizeScale/treeScale + slider.value = defaultMaxVisibilityDistance adjustCheckbox.checked = false LODManager.setAutomaticLODAdjust(adjustCheckbox.checked); } diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index caac8805b1..481975caac 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -6817,8 +6817,8 @@ void Application::updateRenderArgs(float deltaTime) { _viewFrustum.setProjection(adjustedProjection); _viewFrustum.calculate(); } - appRenderArgs._renderArgs = RenderArgs(_graphicsEngine.getGPUContext(), lodManager->getOctreeSizeScale(), - lodManager->getBoundaryLevelAdjust(), lodManager->getLODAngleHalfTan(), RenderArgs::DEFAULT_RENDER_MODE, + appRenderArgs._renderArgs = RenderArgs(_graphicsEngine.getGPUContext(), lodManager->getVisibilityDistance(), + lodManager->getBoundaryLevelAdjust(), lodManager->getLODHalfAngleTan(), RenderArgs::DEFAULT_RENDER_MODE, RenderArgs::MONO, RenderArgs::DEFERRED, RenderArgs::RENDER_DEBUG_NONE); appRenderArgs._renderArgs._scene = getMain3DScene(); diff --git a/interface/src/LODManager.cpp b/interface/src/LODManager.cpp index 0cf795e35b..3e47d88f3c 100644 --- a/interface/src/LODManager.cpp +++ b/interface/src/LODManager.cpp @@ -12,7 +12,6 @@ #include "LODManager.h" #include -#include #include #include @@ -93,8 +92,7 @@ void LODManager::autoAdjustLOD(float realTimeDelta) { return; } - // Previous values for output - float oldOctreeSizeScale = getOctreeSizeScale(); + // Previous value for output float oldLODAngle = getLODAngleDeg(); // Target fps is slightly overshooted by 5hz @@ -165,7 +163,7 @@ void LODManager::autoAdjustLOD(float realTimeDelta) { // And now add the output of the controller to the LODAngle where we will guarantee it is in the proper range setLODAngleDeg(oldLODAngle + output); - if (oldOctreeSizeScale != _octreeSizeScale) { + if (oldLODAngle != getLODAngleDeg()) { auto lodToolsDialog = DependencyManager::get()->getLodToolsDialog(); if (lodToolsDialog) { lodToolsDialog->reloadSliders(); @@ -173,21 +171,32 @@ void LODManager::autoAdjustLOD(float realTimeDelta) { } } -float LODManager::getLODAngleHalfTan() const { - return getPerspectiveAccuracyAngleTan(_octreeSizeScale, _boundaryLevelAdjust); +float LODManager::getLODHalfAngleTan() const { + return tan(_lodHalfAngle); } float LODManager::getLODAngle() const { - return 2.0f * atanf(getLODAngleHalfTan()); + return 2.0f * _lodHalfAngle; } float LODManager::getLODAngleDeg() const { return glm::degrees(getLODAngle()); } +float LODManager::getVisibilityDistance() const { + float systemDistance = getVisibilityDistanceFromHalfAngle(_lodHalfAngle); + // Maintain behavior with deprecated _boundaryLevelAdjust property + return systemDistance * powf(2.0f, _boundaryLevelAdjust); +} + +void LODManager::setVisibilityDistance(float distance) { + // Maintain behavior with deprecated _boundaryLevelAdjust property + float userDistance = distance / powf(2.0f, _boundaryLevelAdjust); + _lodHalfAngle = getHalfAngleFromVisibilityDistance(userDistance); +} + void LODManager::setLODAngleDeg(float lodAngle) { - auto newSolidAngle = std::max(0.5f, std::min(lodAngle, 90.f)); - auto halTan = glm::tan(glm::radians(newSolidAngle * 0.5f)); - auto octreeSizeScale = TREE_SCALE * OCTREE_TO_MESH_RATIO / halTan; - setOctreeSizeScale(octreeSizeScale); + auto newLODAngleDeg = std::max(0.001f, std::min(lodAngle, 90.f)); + auto newLODHalfAngle = glm::radians(newLODAngleDeg * 0.5f); + _lodHalfAngle = newLODHalfAngle; } void LODManager::setSmoothScale(float t) { @@ -267,7 +276,11 @@ bool LODManager::shouldRender(const RenderArgs* args, const AABox& bounds) { }; void LODManager::setOctreeSizeScale(float sizeScale) { - _octreeSizeScale = sizeScale; + setVisibilityDistance(sizeScale / TREE_SCALE); +} + +float LODManager::getOctreeSizeScale() const { + return getVisibilityDistance() * TREE_SCALE; } void LODManager::setBoundaryLevelAdjust(int boundaryLevelAdjust) { @@ -293,12 +306,14 @@ QString LODManager::getLODFeedbackText() { } break; } // distance feedback - float octreeSizeScale = getOctreeSizeScale(); - float relativeToDefault = octreeSizeScale / DEFAULT_OCTREE_SIZE_SCALE; + float visibilityDistance = getVisibilityDistance(); + float relativeToDefault = visibilityDistance / DEFAULT_VISIBILITY_DISTANCE_FOR_UNIT_ELEMENT; int relativeToTwentyTwenty = 20 / relativeToDefault; QString result; - if (relativeToDefault > 1.01f) { + if (relativeToTwentyTwenty < 1) { + result = QString("%2 times further than average vision%3").arg(relativeToDefault, 0, 'f', 3).arg(granularityFeedback); + } else if (relativeToDefault > 1.01f) { result = QString("20:%1 or %2 times further than average vision%3").arg(relativeToTwentyTwenty).arg(relativeToDefault, 0, 'f', 2).arg(granularityFeedback); } else if (relativeToDefault > 0.99f) { result = QString("20:20 or the default distance for average vision%1").arg(granularityFeedback); diff --git a/interface/src/LODManager.h b/interface/src/LODManager.h index 649e0e8e34..3dafa8c800 100644 --- a/interface/src/LODManager.h +++ b/interface/src/LODManager.h @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -138,24 +139,28 @@ public: /**jsdoc * @function LODManager.setOctreeSizeScale * @param {number} sizeScale + * @deprecated This function is deprecated and will be removed. Use the {@link LODManager.lodAngleDeg} property instead. */ Q_INVOKABLE void setOctreeSizeScale(float sizeScale); /**jsdoc * @function LODManager.getOctreeSizeScale * @returns {number} + * @deprecated This function is deprecated and will be removed. Use the {@link LODManager.lodAngleDeg} property instead. */ - Q_INVOKABLE float getOctreeSizeScale() const { return _octreeSizeScale; } + Q_INVOKABLE float getOctreeSizeScale() const; /**jsdoc * @function LODManager.setBoundaryLevelAdjust * @param {number} boundaryLevelAdjust + * @deprecated This function is deprecated and will be removed. */ Q_INVOKABLE void setBoundaryLevelAdjust(int boundaryLevelAdjust); /**jsdoc * @function LODManager.getBoundaryLevelAdjust * @returns {number} + * @deprecated This function is deprecated and will be removed. */ Q_INVOKABLE int getBoundaryLevelAdjust() const { return _boundaryLevelAdjust; } @@ -196,8 +201,10 @@ public: float getLODAngleDeg() const; void setLODAngleDeg(float lodAngle); - float getLODAngleHalfTan() const; + float getLODHalfAngleTan() const; float getLODAngle() const; + float getVisibilityDistance() const; + void setVisibilityDistance(float distance); float getPidKp() const; float getPidKi() const; @@ -254,7 +261,7 @@ private: float _desktopTargetFPS { LOD_OFFSET_FPS + LOD_DEFAULT_QUALITY_LEVEL * LOD_MAX_LIKELY_DESKTOP_FPS }; float _hmdTargetFPS { LOD_OFFSET_FPS + LOD_DEFAULT_QUALITY_LEVEL * LOD_MAX_LIKELY_HMD_FPS }; - float _octreeSizeScale = DEFAULT_OCTREE_SIZE_SCALE; + float _lodHalfAngle = getHalfAngleFromVisibilityDistance(DEFAULT_VISIBILITY_DISTANCE_FOR_UNIT_ELEMENT); int _boundaryLevelAdjust = 0; glm::vec4 _pidCoefs{ 1.0f, 0.0f, 0.0f, 1.0f }; // Kp, Ki, Kd, Kv diff --git a/interface/src/ui/LodToolsDialog.cpp b/interface/src/ui/LodToolsDialog.cpp index e2f2d9e011..b25b9bc5a6 100644 --- a/interface/src/ui/LodToolsDialog.cpp +++ b/interface/src/ui/LodToolsDialog.cpp @@ -64,7 +64,7 @@ LodToolsDialog::LodToolsDialog(QWidget* parent) : _lodSize->setTickPosition(QSlider::TicksBelow); _lodSize->setFixedWidth(SLIDER_WIDTH); _lodSize->setPageStep(PAGE_STEP_LOD_SIZE); - int sliderValue = lodManager->getOctreeSizeScale() / TREE_SCALE; + int sliderValue = lodManager->getVisibilityDistance(); _lodSize->setValue(sliderValue); form->addRow("Level of Detail:", _lodSize); connect(_lodSize,SIGNAL(valueChanged(int)),this,SLOT(sizeScaleValueChanged(int))); @@ -81,7 +81,7 @@ LodToolsDialog::LodToolsDialog(QWidget* parent) : void LodToolsDialog::reloadSliders() { auto lodManager = DependencyManager::get(); - _lodSize->setValue(lodManager->getOctreeSizeScale() / TREE_SCALE); + _lodSize->setValue(lodManager->getVisibilityDistance()); _feedback->setText(lodManager->getLODFeedbackText()); } @@ -93,15 +93,14 @@ void LodToolsDialog::updateAutomaticLODAdjust() { void LodToolsDialog::sizeScaleValueChanged(int value) { auto lodManager = DependencyManager::get(); - float realValue = value * TREE_SCALE; - lodManager->setOctreeSizeScale(realValue); + lodManager->setVisibilityDistance(value); _feedback->setText(lodManager->getLODFeedbackText()); } void LodToolsDialog::resetClicked(bool checked) { - int sliderValue = DEFAULT_OCTREE_SIZE_SCALE / TREE_SCALE; + int sliderValue = DEFAULT_VISIBILITY_DISTANCE_FOR_UNIT_ELEMENT; _lodSize->setValue(sliderValue); _manualLODAdjust->setChecked(false); @@ -124,8 +123,8 @@ void LodToolsDialog::closeEvent(QCloseEvent* event) { lodManager->setAutomaticLODAdjust(true); // if the user adjusted the LOD above "normal" then always revert back to default - if (lodManager->getOctreeSizeScale() > DEFAULT_OCTREE_SIZE_SCALE) { - lodManager->setOctreeSizeScale(DEFAULT_OCTREE_SIZE_SCALE); + if (lodManager->getVisibilityDistance() > DEFAULT_VISIBILITY_DISTANCE_FOR_UNIT_ELEMENT) { + lodManager->setVisibilityDistance(DEFAULT_VISIBILITY_DISTANCE_FOR_UNIT_ELEMENT); } #endif } diff --git a/libraries/octree/src/OctreeConstants.h b/libraries/octree/src/OctreeConstants.h index 062d4e1ef2..de681402c4 100644 --- a/libraries/octree/src/OctreeConstants.h +++ b/libraries/octree/src/OctreeConstants.h @@ -21,8 +21,9 @@ const int TREE_SCALE = 32768; // ~20 miles.. This is the number of meters of the const int HALF_TREE_SCALE = TREE_SCALE / 2; // This controls the LOD. Larger number will make smaller voxels visible at greater distance. -const float MAX_VISIBILITY_DISTANCE_FOR_UNIT_ELEMENT = 400.0f; // max distance where a 1x1x1 cube is visible for 20:20 vision -const float DEFAULT_OCTREE_SIZE_SCALE = TREE_SCALE * MAX_VISIBILITY_DISTANCE_FOR_UNIT_ELEMENT; +const float DEFAULT_VISIBILITY_DISTANCE_FOR_UNIT_ELEMENT = 400.0f; // max distance where a 1x1x1 cube is visible for 20:20 vision +const float UNIT_ELEMENT_MAX_EXTENT = sqrtf(3.0f) / 2.0f; // A unit cube tilted on its edge will have its edge jutting out sqrt(3)/2 units from the center +const float DEFAULT_OCTREE_SIZE_SCALE = TREE_SCALE * DEFAULT_VISIBILITY_DISTANCE_FOR_UNIT_ELEMENT; // Since entities like models live inside of octree cells, and they themselves can have very small mesh parts, // we want to have some constant that controls have big a mesh part must be to render even if the octree cell itself diff --git a/libraries/octree/src/OctreeUtils.cpp b/libraries/octree/src/OctreeUtils.cpp index 7ed9c2ed3c..2aae73c566 100644 --- a/libraries/octree/src/OctreeUtils.cpp +++ b/libraries/octree/src/OctreeUtils.cpp @@ -18,64 +18,31 @@ #include #include -float calculateRenderAccuracy(const glm::vec3& position, - const AABox& bounds, - float octreeSizeScale, - int boundaryLevelAdjust) { - float largestDimension = bounds.getLargestDimension(); - - const float maxScale = (float)TREE_SCALE; - float visibleDistanceAtMaxScale = boundaryDistanceForRenderLevel(boundaryLevelAdjust, octreeSizeScale) / OCTREE_TO_MESH_RATIO; - - static std::once_flag once; - static QMap shouldRenderTable; - std::call_once(once, [&] { - float SMALLEST_SCALE_IN_TABLE = 0.001f; // 1mm is plenty small - float scale = maxScale; - float factor = 1.0f; - - while (scale > SMALLEST_SCALE_IN_TABLE) { - scale /= 2.0f; - factor /= 2.0f; - shouldRenderTable[scale] = factor; - } - }); - - float closestScale = maxScale; - float visibleDistanceAtClosestScale = visibleDistanceAtMaxScale; - QMap::const_iterator lowerBound = shouldRenderTable.lowerBound(largestDimension); - if (lowerBound != shouldRenderTable.constEnd()) { - closestScale = lowerBound.key(); - visibleDistanceAtClosestScale = visibleDistanceAtMaxScale * lowerBound.value(); - } - - if (closestScale < largestDimension) { - visibleDistanceAtClosestScale *= 2.0f; - } - - // FIXME - for now, it's either visible or not visible. We want to adjust this to eventually return - // a floating point for objects that have small angular size to indicate that they may be rendered - // with lower preciscion - float distanceToCamera = glm::length(bounds.calcCenter() - position); - return (distanceToCamera <= visibleDistanceAtClosestScale) ? 1.0f : 0.0f; +float boundaryDistanceForRenderLevel(unsigned int renderLevel, float visibilityDistance) { + return visibilityDistance / powf(2.0f, renderLevel); } -float boundaryDistanceForRenderLevel(unsigned int renderLevel, float voxelSizeScale) { - return voxelSizeScale / powf(2.0f, renderLevel); +float getPerspectiveAccuracyHalfAngleTan(float visibilityDistance, int boundaryLevelAdjust) { + float visibleDistanceAtMaxScale = boundaryDistanceForRenderLevel(boundaryLevelAdjust, visibilityDistance); + return UNIT_ELEMENT_MAX_EXTENT / visibleDistanceAtMaxScale; } -float getPerspectiveAccuracyAngleTan(float octreeSizeScale, int boundaryLevelAdjust) { - const float maxScale = (float)TREE_SCALE; - float visibleDistanceAtMaxScale = boundaryDistanceForRenderLevel(boundaryLevelAdjust, octreeSizeScale) / OCTREE_TO_MESH_RATIO; - return (maxScale / visibleDistanceAtMaxScale); +float getPerspectiveAccuracyHalfAngle(float visibilityDistance, int boundaryLevelAdjust) { + return atan(getPerspectiveAccuracyHalfAngleTan(visibilityDistance, boundaryLevelAdjust)); } -float getPerspectiveAccuracyAngle(float octreeSizeScale, int boundaryLevelAdjust) { - return atan(getPerspectiveAccuracyAngleTan(octreeSizeScale, boundaryLevelAdjust)); +float getVisibilityDistanceFromHalfAngle(float halfAngle) { + float halfAngleTan = tan(halfAngle); + return UNIT_ELEMENT_MAX_EXTENT / halfAngleTan; } -float getOrthographicAccuracySize(float octreeSizeScale, int boundaryLevelAdjust) { +float getHalfAngleFromVisibilityDistance(float visibilityDistance) { + float halfAngleTan = UNIT_ELEMENT_MAX_EXTENT / visibilityDistance; + return atan(halfAngleTan); +} + +float getOrthographicAccuracySize(float visibilityDistance, int boundaryLevelAdjust) { // Smallest visible element is 1cm const float smallestSize = 0.01f; - return (smallestSize * MAX_VISIBILITY_DISTANCE_FOR_UNIT_ELEMENT) / boundaryDistanceForRenderLevel(boundaryLevelAdjust, octreeSizeScale); + return (smallestSize * DEFAULT_VISIBILITY_DISTANCE_FOR_UNIT_ELEMENT) / boundaryDistanceForRenderLevel(boundaryLevelAdjust, visibilityDistance); } diff --git a/libraries/octree/src/OctreeUtils.h b/libraries/octree/src/OctreeUtils.h index eedbfe8bda..73d7e12a39 100644 --- a/libraries/octree/src/OctreeUtils.h +++ b/libraries/octree/src/OctreeUtils.h @@ -20,18 +20,13 @@ class AABox; class AACube; class QJsonDocument; -/// renderAccuracy represents a floating point "visibility" of an object based on it's view from the camera. At a simple -/// level it returns 0.0f for things that are so small for the current settings that they could not be visible. -float calculateRenderAccuracy(const glm::vec3& position, - const AABox& bounds, - float octreeSizeScale = DEFAULT_OCTREE_SIZE_SCALE, - int boundaryLevelAdjust = 0); +float boundaryDistanceForRenderLevel(unsigned int renderLevel, float visibilityDistance); -float boundaryDistanceForRenderLevel(unsigned int renderLevel, float voxelSizeScale); - -float getPerspectiveAccuracyAngleTan(float octreeSizeScale, int boundaryLevelAdjust); -float getPerspectiveAccuracyAngle(float octreeSizeScale, int boundaryLevelAdjust); -float getOrthographicAccuracySize(float octreeSizeScale, int boundaryLevelAdjust); +float getPerspectiveAccuracyHalfAngleTan(float visibilityDistance, int boundaryLevelAdjust); +float getPerspectiveAccuracyHalfAngle(float visibilityDistance, int boundaryLevelAdjust); +float getVisibilityDistanceFromHalfAngle(float halfAngle); +float getHalfAngleFromVisibilityDistance(float visibilityDistance); +float getOrthographicAccuracySize(float visibilityDistance, 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/src/render/DrawSceneOctree.cpp b/libraries/render/src/render/DrawSceneOctree.cpp index b10c3fef8d..493ed01c3f 100644 --- a/libraries/render/src/render/DrawSceneOctree.cpp +++ b/libraries/render/src/render/DrawSceneOctree.cpp @@ -120,7 +120,7 @@ void DrawSceneOctree::run(const RenderContextPointer& renderContext, const ItemS // Draw the LOD Reticle { - float angle = glm::degrees(getPerspectiveAccuracyAngle(args->_sizeScale, args->_boundaryLevelAdjust)); + float angle = glm::degrees(getPerspectiveAccuracyHalfAngle(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/scripts/developer/utilities/render/lod.qml b/scripts/developer/utilities/render/lod.qml index 892b43d8be..6497fb967e 100644 --- a/scripts/developer/utilities/render/lod.qml +++ b/scripts/developer/utilities/render/lod.qml @@ -80,7 +80,7 @@ Item { valueVar: LODManager["lodAngleDeg"] valueVarSetter: (function (v) { LODManager["lodAngleDeg"] = v }) max: 90.0 - min: 0.5 + min: 0.01 integral: false anchors.left: parent.left @@ -239,6 +239,7 @@ Item { object: LODManager valueScale: 1.0 valueUnit: "deg" + valueNumDigits: 2 plots: [ { prop: "lodAngleDeg", diff --git a/scripts/simplifiedUI/ui/simplifiedUI.js b/scripts/simplifiedUI/ui/simplifiedUI.js index f4f2627c66..46e10847f9 100644 --- a/scripts/simplifiedUI/ui/simplifiedUI.js +++ b/scripts/simplifiedUI/ui/simplifiedUI.js @@ -533,7 +533,7 @@ function maybeUpdateOutputDeviceMutedOverlay() { var oldAutomaticLODAdjust; var oldLODAngleDeg; var SIMPLIFIED_UI_AUTO_LOD_ADJUST = false; -var SIMPLIFIED_UI_LOD_ANGLE_DEG = 0.5; +var SIMPLIFIED_UI_LOD_ANGLE_DEG = 0.248; function modifyLODSettings() { oldAutomaticLODAdjust = LODManager.automaticLODAdjust; oldLODAngleDeg = LODManager.lodAngleDeg;