From 0716965df7239684de1eedb0ac1b4b8f18747bee Mon Sep 17 00:00:00 2001 From: Alayan <25536748+Alayan-stk-2@users.noreply.github.com> Date: Mon, 29 Apr 2024 21:47:17 +0200 Subject: [PATCH] Improve Auto-LoD - Increase the Auto-LoD distance on tracks with a low scene complexity (fix #5065) - Increase the Auto-LoD distance when the old formula gives a too low base distance (small objects). This helps with some of the worst popping issues for comparatively little performance cost. - Make the LoD distance auto-compute code clearer by moving the settings-related multiplier after the squaring step - Also fix a couple of unrelated warnings about comparison between signed and unsigned integers --- src/graphics/draw_calls.cpp | 2 +- src/graphics/irr_driver.cpp | 1 + src/graphics/irr_driver.hpp | 8 ++++++ src/graphics/lod_node.cpp | 49 +++++++++++++++++++++++++------------ src/graphics/lod_node.hpp | 1 + src/tracks/track.cpp | 23 ++++++++++++++++- 6 files changed, 67 insertions(+), 17 deletions(-) diff --git a/src/graphics/draw_calls.cpp b/src/graphics/draw_calls.cpp index d89a715f80c..f05c9ec3d57 100644 --- a/src/graphics/draw_calls.cpp +++ b/src/graphics/draw_calls.cpp @@ -156,7 +156,7 @@ void DrawCalls::parseSceneManager(core::array &List, core::array child; if (node->getLevel() >= 0) child.push_back(node->getAllNodes()[node->getLevel()]); - for (int i = 0; i < node->getChildren().size(); i++) + for (unsigned int i = 0; i < node->getChildren().size(); i++) { if (node->getNodesSet().find(node->getChildren()[i]) == node->getNodesSet().end()) child.push_back(node->getChildren()[i]); diff --git a/src/graphics/irr_driver.cpp b/src/graphics/irr_driver.cpp index 71d1ac1daa2..0c29bbc2336 100644 --- a/src/graphics/irr_driver.cpp +++ b/src/graphics/irr_driver.cpp @@ -210,6 +210,7 @@ IrrDriver::IrrDriver() m_recording = false; m_sun_interposer = NULL; m_scene_complexity = 0; + m_lod_multiplier = 1.0f; #ifndef SERVER_ONLY for (unsigned i = 0; i < Q_LAST; i++) diff --git a/src/graphics/irr_driver.hpp b/src/graphics/irr_driver.hpp index 7f0e20421d7..6a183d012e7 100644 --- a/src/graphics/irr_driver.hpp +++ b/src/graphics/irr_driver.hpp @@ -155,6 +155,8 @@ class IrrDriver : public IEventReceiver, public NoCopy /** Store if the scene is complex (based on polycount, etc) */ int m_scene_complexity; + /** Used for auto-LoD adjustment in low-complexity scenes */ + float m_lod_multiplier; /** Internal method that applies the resolution in user settings. */ void applyResolutionSettings(bool recreate_device); @@ -368,12 +370,18 @@ class IrrDriver : public IEventReceiver, public NoCopy bool getBoundingBoxesViz() { return m_boundingboxesviz; } // ------------------------------------------------------------------------ int getSceneComplexity() { return m_scene_complexity; } + // ------------------------------------------------------------------------ void resetSceneComplexity() { m_scene_complexity = 0; } + // ------------------------------------------------------------------------ void addSceneComplexity(int complexity) { if (complexity > 1) m_scene_complexity += (complexity - 1); } // ------------------------------------------------------------------------ + float getLODMultiplier() { return m_lod_multiplier; } + // ------------------------------------------------------------------------ + void setLODMultiplier(float multiplier) { m_lod_multiplier = multiplier; } + // ------------------------------------------------------------------------ bool isRecording() const { return m_recording; } // ------------------------------------------------------------------------ void setRecording(bool val); diff --git a/src/graphics/lod_node.cpp b/src/graphics/lod_node.cpp index c99ec8a8713..79208d2a0a7 100644 --- a/src/graphics/lod_node.cpp +++ b/src/graphics/lod_node.cpp @@ -51,6 +51,7 @@ LODNode::LODNode(std::string group_name, scene::ISceneNode* parent, m_area = 0; m_current_level = -1; m_current_level_dirty = true; + m_lod_distances_updated = false; } LODNode::~LODNode() @@ -86,6 +87,15 @@ int LODNode::getLevel() const int dist = (int)((m_nodes[0]->getAbsolutePosition()).getDistanceFromSQ(pos.toIrrVector() )); + if (!m_lod_distances_updated) + { + for (unsigned int n=0; ngetLODMultiplier()); + } + m_lod_distances_updated = true; + } + for (unsigned int n=0; n 0 } void LODNode::updateVisibility() @@ -173,12 +182,12 @@ void LODNode::OnRegisterSceneNode() { m_nodes[level]->OnRegisterSceneNode(); } - for (int i = 0; i < Children.size(); i++) + for (unsigned i = 0; i < Children.size(); i++) { if (m_nodes_set.find(Children[i]) == m_nodes_set.end()) Children[i]->OnRegisterSceneNode(); } - } + } // if isVisible() && m_nodes.size() > 0 } /* Each model with LoD has specific distances beyond which it is rendered at a lower @@ -189,18 +198,14 @@ void LODNode::autoComputeLevel(float scale) { m_area *= scale; - // Amount of details based on user's input - float agressivity = 1.0; - if( UserConfigParams::m_geometry_level == 0) agressivity = 1.3; - else if(UserConfigParams::m_geometry_level == 1) agressivity = 0.8; - else if(UserConfigParams::m_geometry_level == 2) agressivity = 0.8; // Also removes many objects - else if(UserConfigParams::m_geometry_level == 3) agressivity = 1.8; - else if(UserConfigParams::m_geometry_level == 4) agressivity = 2.4; - else if(UserConfigParams::m_geometry_level == 5) agressivity = 3.2; - // First we try to estimate how far away we need to draw // This first formula is equivalent to the one used up to STK 1.4 float max_draw = 10*(sqrtf(m_area + 20) - 1); + // At really short distances, popping is more annoying even if + // the object is small, so we limit how small the distance can be + if (max_draw < 80) + max_draw = 30 + (max_draw * 0.625); + // If the draw distance is too big we artificially reduce it // The formulas are still experimental and improvable. if(max_draw > 250) @@ -209,9 +214,23 @@ void LODNode::autoComputeLevel(float scale) if (max_draw > 500) max_draw = 200 + (max_draw * 0.6); - max_draw *= agressivity; + // As it is faster to compute the squared distance than distance, at runtime + // we compare the distance saved in the LoD node with the square of the distance + // between the camera and the object. Therefore, we apply squaring here. + max_draw *= max_draw; + + // Amount of details based on the user's input + float aggressivity = 1.0; + if( UserConfigParams::m_geometry_level == 0) aggressivity = 1.5; + else if(UserConfigParams::m_geometry_level == 1) aggressivity = 0.65; + else if(UserConfigParams::m_geometry_level == 2) aggressivity = 0.65; // Also removes many objects + else if(UserConfigParams::m_geometry_level == 3) aggressivity = 4.5; + else if(UserConfigParams::m_geometry_level == 4) aggressivity = 5.75; + else if(UserConfigParams::m_geometry_level == 5) aggressivity = 15.0; + + max_draw *= aggressivity; - int step = (int) (max_draw * max_draw) / m_detail.size(); + int step = (int) (max_draw) / m_detail.size(); // Then we recompute the level of detail culling distance int biais = m_detail.size(); diff --git a/src/graphics/lod_node.hpp b/src/graphics/lod_node.hpp index fe52253de79..31daa1e6f16 100644 --- a/src/graphics/lod_node.hpp +++ b/src/graphics/lod_node.hpp @@ -70,6 +70,7 @@ class LODNode : public scene::ISceneNode float m_area; bool m_update_box_every_frame; + bool m_lod_distances_updated; public: LODNode(std::string group_name, scene::ISceneNode* parent, scene::ISceneManager* mgr, s32 id=-1); diff --git a/src/tracks/track.cpp b/src/tracks/track.cpp index 93945edbbe1..d6b0f3d3582 100644 --- a/src/tracks/track.cpp +++ b/src/tracks/track.cpp @@ -2090,7 +2090,28 @@ void Track::loadTrackModel(bool reverse_track, unsigned int mode_id) loadObjects(root, path, model_def_loader, true, NULL, NULL); main_loop->renderGUI(5000); - Log::info("Track", "Overall scene complexity estimated at %d", irr_driver->getSceneComplexity()); + // If the track is low complexity, increase distances for LoD nodes + // Scene complexity is not computed before LoD nodes are loaded, so + // instead we set a variable that will be used to update the distances + // later on. + float squared_multiplier = 1.0f; + if (irr_driver->getSceneComplexity() < 1500) + { + float ratio = 1.0f; + // Cap the potential effect + if (irr_driver->getSceneComplexity() < 100) + ratio = 15.0f; + else + ratio = 1500.0f / (float)(irr_driver->getSceneComplexity()); + + squared_multiplier = 0.3f + 0.7f * ratio; + } + irr_driver->setLODMultiplier(squared_multiplier); + // The LoD distances are stored squared in the node, therefore the real multiplier + // is the square root of the one that gets applied + Log::info("Track", "Overall scene complexity estimated at %d, Auto-LoD multiplier is %f", + irr_driver->getSceneComplexity(), sqrtf(squared_multiplier)); + // Correct the parenting of meta library for (auto& p : m_meta_library) {