1b5fa74e39
Add scene side discrete level of detail. New node `LOD` for UI, and `LODManager` within `World` for automatically updating child visibilities based on distance from cameras.
182 lines
5.8 KiB
C++
182 lines
5.8 KiB
C++
/**************************************************************************/
|
|
/* lod_manager.cpp */
|
|
/**************************************************************************/
|
|
/* This file is part of: */
|
|
/* GODOT ENGINE */
|
|
/* https://godotengine.org */
|
|
/**************************************************************************/
|
|
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
|
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
|
/* */
|
|
/* Permission is hereby granted, free of charge, to any person obtaining */
|
|
/* a copy of this software and associated documentation files (the */
|
|
/* "Software"), to deal in the Software without restriction, including */
|
|
/* without limitation the rights to use, copy, modify, merge, publish, */
|
|
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
|
/* permit persons to whom the Software is furnished to do so, subject to */
|
|
/* the following conditions: */
|
|
/* */
|
|
/* The above copyright notice and this permission notice shall be */
|
|
/* included in all copies or substantial portions of the Software. */
|
|
/* */
|
|
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
|
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
|
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
|
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
|
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
|
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
|
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
|
/**************************************************************************/
|
|
|
|
#include "lod_manager.h"
|
|
|
|
#include "scene/3d/camera.h"
|
|
#include "scene/3d/lod.h"
|
|
|
|
bool LODManager::_enabled = true;
|
|
|
|
void LODManager::register_camera(Camera *p_camera) {
|
|
DEV_ASSERT(p_camera);
|
|
data.cameras.push_back(p_camera);
|
|
}
|
|
|
|
void LODManager::remove_camera(Camera *p_camera) {
|
|
data.cameras.erase(p_camera);
|
|
}
|
|
|
|
void LODManager::register_lod(LOD *p_lod, uint32_t p_queue_id) {
|
|
ERR_FAIL_UNSIGNED_INDEX(p_queue_id, NUM_LOD_QUEUES);
|
|
data.queues[p_queue_id].lods.push_back(p_lod);
|
|
}
|
|
|
|
void LODManager::unregister_lod(LOD *p_lod, uint32_t p_queue_id) {
|
|
ERR_FAIL_UNSIGNED_INDEX(p_queue_id, NUM_LOD_QUEUES);
|
|
data.queues[p_queue_id].lods.erase(p_lod);
|
|
}
|
|
|
|
void LODManager::_update_queue(uint32_t p_queue_id, const Vector3 *p_camera_positions, uint32_t p_num_cameras) {
|
|
// Some local aliases.
|
|
Queue &queue = data.queues[p_queue_id];
|
|
LocalVector<LOD *> &lods = queue.lods;
|
|
|
|
uint32_t total_lods = lods.size();
|
|
|
|
if (!total_lods) {
|
|
return;
|
|
}
|
|
|
|
// Wraparound.
|
|
queue.lod_iterator %= total_lods;
|
|
|
|
uint32_t first_lod = queue.lod_iterator;
|
|
|
|
uint32_t num_lods_to_check = 1;
|
|
uint32_t num_lods_to_change = 1;
|
|
|
|
switch (p_queue_id) {
|
|
case 4: {
|
|
num_lods_to_check = 3125;
|
|
num_lods_to_change = 32;
|
|
} break;
|
|
case 3: {
|
|
num_lods_to_check = 256;
|
|
num_lods_to_change = 8;
|
|
} break;
|
|
case 2: {
|
|
num_lods_to_check = 27;
|
|
num_lods_to_change = 4;
|
|
} break;
|
|
case 1: {
|
|
num_lods_to_check = 4;
|
|
num_lods_to_change = 1;
|
|
} break;
|
|
default: {
|
|
} break;
|
|
}
|
|
|
|
// No point updating more lods than the total.
|
|
num_lods_to_check = MIN(num_lods_to_check, total_lods);
|
|
num_lods_to_change = MIN(num_lods_to_change, total_lods);
|
|
|
|
// Find minimum distances to cameras...
|
|
uint32_t changed = 0;
|
|
|
|
for (uint32_t l = 0; l < num_lods_to_check; l++) {
|
|
uint32_t lod_id = (first_lod + l) % total_lods;
|
|
LOD *lod = lods[lod_id];
|
|
Vector3 lod_pos = lod->get_global_translation();
|
|
|
|
float min_dist = FLT_MAX;
|
|
for (uint32_t c = 0; c < p_num_cameras; c++) {
|
|
float dist = (lod_pos - p_camera_positions[c]).length_squared();
|
|
min_dist = MIN(min_dist, dist);
|
|
}
|
|
|
|
if (lod->_lod_update(min_dist)) {
|
|
changed++;
|
|
if (changed >= num_lods_to_change) {
|
|
// Only update the iterator to where we got to.
|
|
num_lods_to_check = l + 1;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
queue.lod_iterator += num_lods_to_check;
|
|
}
|
|
|
|
void LODManager::notify_saving(bool p_active) {
|
|
// When saving in the editor, to prevent file delta due to
|
|
// different visibilities from LOD childs, we standardize
|
|
// to showing the first LOD.
|
|
MutexLock lock(data.mutex);
|
|
data.saving = p_active;
|
|
|
|
if (p_active) {
|
|
for (uint32_t n = 0; n < NUM_LOD_QUEUES; n++) {
|
|
Queue &queue = data.queues[n];
|
|
LocalVector<LOD *> &lods = queue.lods;
|
|
|
|
for (uint32_t l = 0; l < lods.size(); l++) {
|
|
lods[l]->_lod_pre_save();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void LODManager::update() {
|
|
if (!_enabled) {
|
|
return;
|
|
}
|
|
|
|
MutexLock lock(data.mutex);
|
|
|
|
// We don't want to change the visibilities while saving from the editor.
|
|
if (data.saving) {
|
|
return;
|
|
}
|
|
|
|
// Get all camera positions
|
|
// Reserve enough for all cameras, some may not be used.
|
|
Vector3 *camera_positions = (Vector3 *)alloca(data.cameras.size() * sizeof(Vector3));
|
|
|
|
uint32_t num_cameras = 0;
|
|
for (uint32_t c = 0; c < data.cameras.size(); c++) {
|
|
const Camera *camera = data.cameras[c];
|
|
|
|
// Ignore ortho cameras for LOD.
|
|
if (!camera->get_affect_lod() || !camera->is_current() || (camera->get_projection() == Camera::PROJECTION_ORTHOGONAL)) {
|
|
continue;
|
|
}
|
|
|
|
camera_positions[num_cameras++] = camera->get_global_transform().origin;
|
|
}
|
|
|
|
if (!num_cameras) {
|
|
return;
|
|
}
|
|
|
|
for (uint32_t n = 0; n < NUM_LOD_QUEUES; n++) {
|
|
_update_queue(n, camera_positions, num_cameras);
|
|
}
|
|
}
|