mirror of
https://github.com/overte-org/overte.git
synced 2025-05-11 20:09:12 +02:00
210 lines
8.4 KiB
C++
210 lines
8.4 KiB
C++
//
|
|
// LODManager.cpp
|
|
//
|
|
//
|
|
// Created by Clement on 1/16/15.
|
|
// Copyright 2015 High Fidelity, Inc.
|
|
//
|
|
// Distributed under the Apache License, Version 2.0.
|
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
|
//
|
|
|
|
#include <SettingHandle.h>
|
|
#include <Util.h>
|
|
|
|
#include "Application.h"
|
|
#include "ui/DialogsManager.h"
|
|
|
|
#include "LODManager.h"
|
|
|
|
Setting::Handle<bool> automaticAvatarLOD("automaticAvatarLOD", true);
|
|
Setting::Handle<float> avatarLODDecreaseFPS("avatarLODDecreaseFPS", DEFAULT_ADJUST_AVATAR_LOD_DOWN_FPS);
|
|
Setting::Handle<float> avatarLODIncreaseFPS("avatarLODIncreaseFPS", ADJUST_LOD_UP_FPS);
|
|
Setting::Handle<float> avatarLODDistanceMultiplier("avatarLODDistanceMultiplier",
|
|
DEFAULT_AVATAR_LOD_DISTANCE_MULTIPLIER);
|
|
Setting::Handle<int> boundaryLevelAdjust("boundaryLevelAdjust", 0);
|
|
Setting::Handle<float> octreeSizeScale("octreeSizeScale", DEFAULT_OCTREE_SIZE_SCALE);
|
|
|
|
|
|
void LODManager::autoAdjustLOD(float currentFPS) {
|
|
// NOTE: our first ~100 samples at app startup are completely all over the place, and we don't
|
|
// really want to count them in our average, so we will ignore the real frame rates and stuff
|
|
// our moving average with simulated good data
|
|
const int IGNORE_THESE_SAMPLES = 100;
|
|
const float ASSUMED_FPS = 60.0f;
|
|
if (_fpsAverage.getSampleCount() < IGNORE_THESE_SAMPLES) {
|
|
currentFPS = ASSUMED_FPS;
|
|
}
|
|
_fpsAverage.updateAverage(currentFPS);
|
|
_fastFPSAverage.updateAverage(currentFPS);
|
|
|
|
quint64 now = usecTimestampNow();
|
|
|
|
const quint64 ADJUST_AVATAR_LOD_DOWN_DELAY = 1000 * 1000;
|
|
if (_automaticAvatarLOD) {
|
|
if (_fastFPSAverage.getAverage() < _avatarLODDecreaseFPS) {
|
|
if (now - _lastAvatarDetailDrop > ADJUST_AVATAR_LOD_DOWN_DELAY) {
|
|
// attempt to lower the detail in proportion to the fps difference
|
|
float targetFps = (_avatarLODDecreaseFPS + _avatarLODIncreaseFPS) * 0.5f;
|
|
float averageFps = _fastFPSAverage.getAverage();
|
|
const float MAXIMUM_MULTIPLIER_SCALE = 2.0f;
|
|
_avatarLODDistanceMultiplier = qMin(MAXIMUM_AVATAR_LOD_DISTANCE_MULTIPLIER, _avatarLODDistanceMultiplier *
|
|
(averageFps < EPSILON ? MAXIMUM_MULTIPLIER_SCALE :
|
|
qMin(MAXIMUM_MULTIPLIER_SCALE, targetFps / averageFps)));
|
|
_lastAvatarDetailDrop = now;
|
|
}
|
|
} else if (_fastFPSAverage.getAverage() > _avatarLODIncreaseFPS) {
|
|
// let the detail level creep slowly upwards
|
|
const float DISTANCE_DECREASE_RATE = 0.05f;
|
|
_avatarLODDistanceMultiplier = qMax(MINIMUM_AVATAR_LOD_DISTANCE_MULTIPLIER,
|
|
_avatarLODDistanceMultiplier - DISTANCE_DECREASE_RATE);
|
|
}
|
|
}
|
|
|
|
bool changed = false;
|
|
quint64 elapsed = now - _lastAdjust;
|
|
|
|
if (elapsed > ADJUST_LOD_DOWN_DELAY && _fpsAverage.getAverage() < ADJUST_LOD_DOWN_FPS
|
|
&& _octreeSizeScale > ADJUST_LOD_MIN_SIZE_SCALE) {
|
|
|
|
_octreeSizeScale *= ADJUST_LOD_DOWN_BY;
|
|
|
|
if (_octreeSizeScale < ADJUST_LOD_MIN_SIZE_SCALE) {
|
|
_octreeSizeScale = ADJUST_LOD_MIN_SIZE_SCALE;
|
|
}
|
|
changed = true;
|
|
_lastAdjust = now;
|
|
qDebug() << "adjusting LOD down... average fps for last approximately 5 seconds=" << _fpsAverage.getAverage()
|
|
<< "_octreeSizeScale=" << _octreeSizeScale;
|
|
}
|
|
|
|
if (elapsed > ADJUST_LOD_UP_DELAY && _fpsAverage.getAverage() > ADJUST_LOD_UP_FPS
|
|
&& _octreeSizeScale < ADJUST_LOD_MAX_SIZE_SCALE) {
|
|
_octreeSizeScale *= ADJUST_LOD_UP_BY;
|
|
if (_octreeSizeScale > ADJUST_LOD_MAX_SIZE_SCALE) {
|
|
_octreeSizeScale = ADJUST_LOD_MAX_SIZE_SCALE;
|
|
}
|
|
changed = true;
|
|
_lastAdjust = now;
|
|
qDebug() << "adjusting LOD up... average fps for last approximately 5 seconds=" << _fpsAverage.getAverage()
|
|
<< "_octreeSizeScale=" << _octreeSizeScale;
|
|
}
|
|
|
|
if (changed) {
|
|
_shouldRenderTableNeedsRebuilding = true;
|
|
auto lodToolsDialog = DependencyManager::get<DialogsManager>()->getLodToolsDialog();
|
|
if (lodToolsDialog) {
|
|
lodToolsDialog->reloadSliders();
|
|
}
|
|
}
|
|
}
|
|
|
|
void LODManager::resetLODAdjust() {
|
|
_fpsAverage.reset();
|
|
_fastFPSAverage.reset();
|
|
_lastAvatarDetailDrop = _lastAdjust = usecTimestampNow();
|
|
}
|
|
|
|
QString LODManager::getLODFeedbackText() {
|
|
// determine granularity feedback
|
|
int boundaryLevelAdjust = getBoundaryLevelAdjust();
|
|
QString granularityFeedback;
|
|
|
|
switch (boundaryLevelAdjust) {
|
|
case 0: {
|
|
granularityFeedback = QString("at standard granularity.");
|
|
} break;
|
|
case 1: {
|
|
granularityFeedback = QString("at half of standard granularity.");
|
|
} break;
|
|
case 2: {
|
|
granularityFeedback = QString("at a third of standard granularity.");
|
|
} break;
|
|
default: {
|
|
granularityFeedback = QString("at 1/%1th of standard granularity.").arg(boundaryLevelAdjust + 1);
|
|
} break;
|
|
}
|
|
|
|
// distance feedback
|
|
float octreeSizeScale = getOctreeSizeScale();
|
|
float relativeToDefault = octreeSizeScale / DEFAULT_OCTREE_SIZE_SCALE;
|
|
QString result;
|
|
if (relativeToDefault > 1.01) {
|
|
result = QString("%1 further %2").arg(relativeToDefault,8,'f',2).arg(granularityFeedback);
|
|
} else if (relativeToDefault > 0.99) {
|
|
result = QString("the default distance %1").arg(granularityFeedback);
|
|
} else {
|
|
result = QString("%1 of default %2").arg(relativeToDefault,8,'f',3).arg(granularityFeedback);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
// TODO: This is essentially the same logic used to render octree cells, but since models are more detailed then octree cells
|
|
// I've added a voxelToModelRatio that adjusts how much closer to a model you have to be to see it.
|
|
bool LODManager::shouldRenderMesh(float largestDimension, float distanceToCamera) {
|
|
const float octreeToMeshRatio = 4.0f; // must be this many times closer to a mesh than a voxel to see it.
|
|
float octreeSizeScale = getOctreeSizeScale();
|
|
int boundaryLevelAdjust = getBoundaryLevelAdjust();
|
|
float maxScale = (float)TREE_SCALE;
|
|
float visibleDistanceAtMaxScale = boundaryDistanceForRenderLevel(boundaryLevelAdjust, octreeSizeScale) / octreeToMeshRatio;
|
|
|
|
if (_shouldRenderTableNeedsRebuilding) {
|
|
_shouldRenderTable.clear();
|
|
|
|
float SMALLEST_SCALE_IN_TABLE = 0.001f; // 1mm is plenty small
|
|
float scale = maxScale;
|
|
float visibleDistanceAtScale = visibleDistanceAtMaxScale;
|
|
|
|
while (scale > SMALLEST_SCALE_IN_TABLE) {
|
|
scale /= 2.0f;
|
|
visibleDistanceAtScale /= 2.0f;
|
|
_shouldRenderTable[scale] = visibleDistanceAtScale;
|
|
}
|
|
_shouldRenderTableNeedsRebuilding = false;
|
|
}
|
|
|
|
float closestScale = maxScale;
|
|
float visibleDistanceAtClosestScale = visibleDistanceAtMaxScale;
|
|
QMap<float, float>::const_iterator lowerBound = _shouldRenderTable.lowerBound(largestDimension);
|
|
if (lowerBound != _shouldRenderTable.constEnd()) {
|
|
closestScale = lowerBound.key();
|
|
visibleDistanceAtClosestScale = lowerBound.value();
|
|
}
|
|
|
|
if (closestScale < largestDimension) {
|
|
visibleDistanceAtClosestScale *= 2.0f;
|
|
}
|
|
|
|
return (distanceToCamera <= visibleDistanceAtClosestScale);
|
|
}
|
|
|
|
void LODManager::setOctreeSizeScale(float sizeScale) {
|
|
_octreeSizeScale = sizeScale;
|
|
_shouldRenderTableNeedsRebuilding = true;
|
|
}
|
|
|
|
void LODManager::setBoundaryLevelAdjust(int boundaryLevelAdjust) {
|
|
_boundaryLevelAdjust = boundaryLevelAdjust;
|
|
_shouldRenderTableNeedsRebuilding = true;
|
|
}
|
|
|
|
|
|
void LODManager::loadSettings() {
|
|
setAutomaticAvatarLOD(automaticAvatarLOD.get());
|
|
setAvatarLODDecreaseFPS(avatarLODDecreaseFPS.get());
|
|
setAvatarLODIncreaseFPS(avatarLODIncreaseFPS.get());
|
|
setAvatarLODDistanceMultiplier(avatarLODDistanceMultiplier.get());
|
|
setBoundaryLevelAdjust(boundaryLevelAdjust.get());
|
|
setOctreeSizeScale(octreeSizeScale.get());
|
|
}
|
|
|
|
void LODManager::saveSettings() {
|
|
automaticAvatarLOD.set(getAutomaticAvatarLOD());
|
|
avatarLODDecreaseFPS.set(getAvatarLODDecreaseFPS());
|
|
avatarLODIncreaseFPS.set(getAvatarLODIncreaseFPS());
|
|
avatarLODDistanceMultiplier.set(getAvatarLODDistanceMultiplier());
|
|
boundaryLevelAdjust.set(getBoundaryLevelAdjust());
|
|
octreeSizeScale.set(getOctreeSizeScale());
|
|
}
|
|
|
|
|