diff --git a/src/graphics/draw_calls.cpp b/src/graphics/draw_calls.cpp index 57df7c140..a686f6220 100644 --- a/src/graphics/draw_calls.cpp +++ b/src/graphics/draw_calls.cpp @@ -202,11 +202,17 @@ void DrawCalls::prepareDrawCalls(scene::ICameraSceneNode *camnode) { CPUParticleManager::getInstance()->reset(); TextBillboardDrawer::reset(); - PROFILER_PUSH_CPU_MARKER("- culling", 0xFF, 0xFF, 0x0); + PROFILER_PUSH_CPU_MARKER("- prepare draw call", 0xFF, 0xFF, 0x0); SP::prepareDrawCalls(); + PROFILER_POP_CPU_MARKER(); + + PROFILER_PUSH_CPU_MARKER("-- parse scene manager", 0x00, 0xFF, 0x0); parseSceneManager( irr_driver->getSceneManager()->getRootSceneNode()->getChildren(), camnode); + PROFILER_POP_CPU_MARKER(); + + PROFILER_PUSH_CPU_MARKER("-- handle dynamic draw", 0x00, 0xFF, 0x0); SP::handleDynamicDrawCall(); SP::updateModelMatrix(); PROFILER_POP_CPU_MARKER(); @@ -214,7 +220,7 @@ void DrawCalls::prepareDrawCalls(scene::ICameraSceneNode *camnode) PROFILER_PUSH_CPU_MARKER("- cpu particle generation", 0x2F, 0x1F, 0x11); CPUParticleManager::getInstance()->generateAll(); PROFILER_POP_CPU_MARKER(); - + // Add a 1 s timeout if (m_sync != 0) { @@ -233,22 +239,7 @@ void DrawCalls::prepareDrawCalls(scene::ICameraSceneNode *camnode) PROFILER_POP_CPU_MARKER(); } - /* switch (reason) - { - case GL_ALREADY_SIGNALED: - printf("Already Signaled\n"); - break; - case GL_TIMEOUT_EXPIRED: - printf("Timeout Expired\n"); - break; - case GL_CONDITION_SATISFIED: - printf("Condition Satisfied\n"); - break; - case GL_WAIT_FAILED: - printf("Wait Failed\n"); - break; - }*/ - + PROFILER_PUSH_CPU_MARKER("- particle and text billboard upload", 0x3F, 0x03, 0x61); CPUParticleManager::getInstance()->uploadAll(); diff --git a/src/graphics/irr_driver.cpp b/src/graphics/irr_driver.cpp index 4a4ede5a1..cb0c21391 100644 --- a/src/graphics/irr_driver.cpp +++ b/src/graphics/irr_driver.cpp @@ -172,6 +172,7 @@ IrrDriver::IrrDriver() m_skinning_joint = 0; m_recording = false; m_sun_interposer = NULL; + m_scene_complexity = 0; #ifndef SERVER_ONLY for (unsigned i = 0; i < Q_LAST; i++) @@ -1718,7 +1719,7 @@ void IrrDriver::displayFPS() core::rect position; if (UserConfigParams::m_artist_debug_mode) - position = core::rect(75, 0, 1100, 40); + position = core::rect(51, 0, 1100, 80); else position = core::rect(75, 0, 900, 40); GL32_draw2DRectangle(video::SColor(150, 96, 74, 196), position, NULL); @@ -1777,10 +1778,10 @@ void IrrDriver::displayFPS() { fps_string = StringUtils::insertValues (L"FPS: %d/%d/%d - PolyCount: %d Solid, " - "%d Shadows - LightDist : %d, Total skinning joints: %d, " + "%d Shadows - LightDist : %d\nComplexity %d, Total skinning joints: %d, " "Ping: %dms", min, fps, max, SP::sp_solid_poly_count, - SP::sp_shadow_poly_count, m_last_light_bucket_distance, + SP::sp_shadow_poly_count, m_last_light_bucket_distance, irr_driver->getSceneComplexity(), m_skinning_joint, ping); } else diff --git a/src/graphics/irr_driver.hpp b/src/graphics/irr_driver.hpp index 33604edb6..b6456682d 100644 --- a/src/graphics/irr_driver.hpp +++ b/src/graphics/irr_driver.hpp @@ -150,6 +150,9 @@ private: /** Whether the mouse cursor is currently shown */ bool m_pointer_shown; + /** Store if the scene is complex (based on polycount, etc) */ + int m_scene_complexity; + /** Internal method that applies the resolution in user settings. */ void applyResolutionSettings(); void createListOfVideoModes(); @@ -369,6 +372,13 @@ public: // ------------------------------------------------------------------------ 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.0); + } + // ------------------------------------------------------------------------ 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 2fc971525..087aea136 100644 --- a/src/graphics/lod_node.cpp +++ b/src/graphics/lod_node.cpp @@ -40,8 +40,6 @@ LODNode::LODNode(std::string group_name, scene::ISceneNode* parent, m_group_name = group_name; - m_previous_visibility = FIRST_PASS; - // At this stage refcount is two: one because of the object being // created, and once because it is a child of the parent. Drop once, // so that only the reference from the parent is active, causing this @@ -50,6 +48,17 @@ LODNode::LODNode(std::string group_name, scene::ISceneNode* parent, m_forced_lod = -1; m_last_tick = 0; + m_area = 0; + + m_previous_level = 0; + m_current_level = 0; + + m_timer = 0; + + is_in_transition = false; + + //m_node_to_fade_out = 0; + //m_node_to_fade_in = 0; } LODNode::~LODNode() @@ -78,13 +87,33 @@ int LODNode::getLevel() return (int)m_detail.size() - 1; const Vec3 &pos = camera->getCameraSceneNode()->getAbsolutePosition(); - const int dist = + int dist = (int)((m_nodes[0]->getAbsolutePosition()).getDistanceFromSQ(pos.toIrrVector() )); + + // Based on the complexity of the track we are more or less aggressive with culling + int complexity = irr_driver->getSceneComplexity(); + // The track has high complexity so we decrease the draw distance by 10% + if (complexity > 3000 ) + { + dist += (dist/10); + } + // The track has medium complexity, we can increase slightly the draw distance + else if(complexity > 1500 ) + { + dist -= (dist/100); + } + // The track has low complexity we can increase a lot the draw distance + else + { + dist -= (dist/10); + } for (unsigned int n=0; nsetVisible(i == level); - if (i == level && shown != NULL) - *shown = (i > 0); + m_current_level = getLevel(); } + + if (m_previous_level != m_current_level && !is_in_transition) + { + is_in_transition = true; + m_timer = 0; + } + + if (is_in_transition) + { + // Initially we display the previous one along the new one + for (size_t i = 0; i < m_nodes.size(); i++) + { + if (m_current_level == i || m_previous_level == i) + m_nodes[i]->setVisible(true); + } + + // We reset counting + if (m_timer > 20) + { + is_in_transition = false; + m_previous_level = m_current_level; + } + m_timer ++; + } + else + { + for (size_t i = 0; i < m_nodes.size(); i++) + { + m_nodes[i]->setVisible(i == m_current_level); + } + } + + //const u32 now = irr_driver->getDevice()->getTimer()->getTime(); } void LODNode::OnRegisterSceneNode() { - bool shown = false; - updateVisibility(&shown); #ifndef SERVER_ONLY if (CVS->isGLSL()) @@ -173,69 +231,7 @@ void LODNode::OnRegisterSceneNode() const u32 now = irr_driver->getDevice()->getTimer()->getTime(); // support an optional, mostly hard-coded fade-in/out effect for objects with a single level - if (m_nodes.size() == 1 && (m_nodes[0]->getType() == scene::ESNT_MESH || - m_nodes[0]->getType() == scene::ESNT_ANIMATED_MESH) && - now > m_last_tick) - { - if (m_previous_visibility == WAS_HIDDEN && shown) - { - scene::IMesh* mesh; - - if (m_nodes[0]->getType() == scene::ESNT_MESH) - { - scene::IMeshSceneNode* node = (scene::IMeshSceneNode*)(m_nodes[0]); - mesh = node->getMesh(); - } - else - { - assert(m_nodes[0]->getType() == scene::ESNT_ANIMATED_MESH); - scene::IAnimatedMeshSceneNode* node = - (scene::IAnimatedMeshSceneNode*)(m_nodes[0]); - assert(node != NULL); - mesh = node->getMesh(); - } - } - else if (m_previous_visibility == WAS_SHOWN && !shown) - { - scene::IMesh* mesh; - - if (m_nodes[0]->getType() == scene::ESNT_MESH) - { - scene::IMeshSceneNode* node = (scene::IMeshSceneNode*)(m_nodes[0]); - mesh = node->getMesh(); - } - else - { - assert(m_nodes[0]->getType() == scene::ESNT_ANIMATED_MESH); - scene::IAnimatedMeshSceneNode* node = - (scene::IAnimatedMeshSceneNode*)(m_nodes[0]); - assert(node != NULL); - mesh = node->getMesh(); - } - - } - else if (m_previous_visibility == FIRST_PASS && !shown) - { - scene::IMesh* mesh; - - if (m_nodes[0]->getType() == scene::ESNT_MESH) - { - scene::IMeshSceneNode* node = (scene::IMeshSceneNode*)(m_nodes[0]); - mesh = node->getMesh(); - } - else - { - assert(m_nodes[0]->getType() == scene::ESNT_ANIMATED_MESH); - scene::IAnimatedMeshSceneNode* node = - (scene::IAnimatedMeshSceneNode*)(m_nodes[0]); - assert(node != NULL); - mesh = node->getMesh(); - } - } - } - - m_previous_visibility = (shown ? WAS_SHOWN : WAS_HIDDEN); - m_last_tick = now; + #ifndef SERVER_ONLY if (!CVS->isGLSL()) { @@ -249,8 +245,43 @@ void LODNode::OnRegisterSceneNode() scene::ISceneNode::OnRegisterSceneNode(); } +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.25; + if(UserConfigParams::m_geometry_level == 1) agressivity = 1.0; + if(UserConfigParams::m_geometry_level == 2) agressivity = 0.75; + + // First we try to estimate how far away we need to draw + float max_draw = 0.0; + max_draw = sqrtf((0.5 * m_area + 10) * 200) - 10; + // If the draw distance is too big we artificially reduce it + if(max_draw > 250) + { + max_draw = 250 + (max_draw * 0.06); + } + + max_draw *= agressivity; + + int step = (int) (max_draw * max_draw) / m_detail.size(); + + // Then we recompute the level of detail culling distance + int biais = m_detail.size(); + for(int i = 0; i < m_detail.size(); i++) + { + m_detail[i] = ((step / biais) * (i + 1)); + biais--; + } +} + void LODNode::add(int level, scene::ISceneNode* node, bool reparent) { + Box = node->getBoundingBox(); + m_area = Box.getArea(); + // samuncle suggested to put a slight randomisation in LOD // I'm not convinced (Auria) but he's the artist pro, so I listen ;P // The last level should not be randomized because after that the object disappears, @@ -266,6 +297,7 @@ void LODNode::add(int level, scene::ISceneNode* node, bool reparent) node->grab(); node->remove(); node->setPosition(core::vector3df(0,0,0)); + node->setVisible(false); m_detail.push_back(level*level); m_nodes.push_back(node); m_nodes_set.insert(node); diff --git a/src/graphics/lod_node.hpp b/src/graphics/lod_node.hpp index a9297fc4f..9f323b713 100644 --- a/src/graphics/lod_node.hpp +++ b/src/graphics/lod_node.hpp @@ -62,14 +62,18 @@ private: * m_forced_lod is >=0, only this level is be used. */ int m_forced_lod; - enum PreviousVisibility - { - FIRST_PASS, - WAS_SHOWN, - WAS_HIDDEN - }; + // Area of the bounding box (for autoLOD computation) + float m_area; + + // Previous level for the smooth transitions + int m_previous_level; + int m_current_level; + + int m_timer; + + bool is_in_transition; + - PreviousVisibility m_previous_visibility; u32 m_last_tick; @@ -83,7 +87,7 @@ public: int getLevel(); - void updateVisibility(bool* shown = NULL); + void updateVisibility(); /* //! Returns a reference to the current relative transformation matrix. @@ -104,6 +108,11 @@ public: */ void add(int level, scene::ISceneNode* node, bool reparent); + /** + * This method can be used to automatically compute LoD level + */ + void autoComputeLevel(float scale); + void forceLevelOfDetail(int n); /** Get the highest level of detail node */ diff --git a/src/graphics/shader_based_renderer.cpp b/src/graphics/shader_based_renderer.cpp index 8035b904a..229b6c502 100644 --- a/src/graphics/shader_based_renderer.cpp +++ b/src/graphics/shader_based_renderer.cpp @@ -235,7 +235,7 @@ void ShaderBasedRenderer::renderSceneDeferred(scene::ICameraSceneNode * const ca } irr_driver->getSceneManager()->setActiveCamera(camnode); - PROFILER_PUSH_CPU_MARKER("- Draw Call Generation", 0xFF, 0xFF, 0xFF); + PROFILER_PUSH_CPU_MARKER("- Draw Call Generation xxx", 0xFF, 0xFF, 0xFF); m_draw_calls.prepareDrawCalls(camnode); PROFILER_POP_CPU_MARKER(); PROFILER_PUSH_CPU_MARKER("Update Light Info", 0xFF, 0x0, 0x0); diff --git a/src/tracks/model_definition_loader.cpp b/src/tracks/model_definition_loader.cpp index d5e976502..17f9b6c59 100644 --- a/src/tracks/model_definition_loader.cpp +++ b/src/tracks/model_definition_loader.cpp @@ -142,6 +142,9 @@ LODNode* ModelDefinitionLoader::instanciateAsLOD(const XMLNode* node, scene::ISc lod_node->add(group[m].m_distance, scene_node, true); } } + vector3df scale = vector3df(1.f, 1.f, 1.f); + node->get("scale", &scale); + lod_node->autoComputeLevel(scale.getLength()); #ifdef DEBUG std::string debug_name = groupname+" (LOD track-object)"; diff --git a/src/tracks/track.cpp b/src/tracks/track.cpp index 8f81e68e6..41bd29b35 100644 --- a/src/tracks/track.cpp +++ b/src/tracks/track.cpp @@ -302,6 +302,7 @@ void Track::reset() */ void Track::cleanup() { + irr_driver->resetSceneComplexity(); m_physical_object_uid = 0; #ifdef USE_RESIZE_CACHE if (!UserConfigParams::m_high_definition_textures) @@ -2018,6 +2019,7 @@ 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()); // Correct the parenting of meta library for (auto& p : m_meta_library) { @@ -2258,6 +2260,8 @@ void Track::loadObjects(const XMLNode* root, const std::string& path, const bool is_mode_ctf = m_is_ctf && race_manager->getMinorMode() == RaceManager::MINOR_MODE_CAPTURE_THE_FLAG; + // We keep track of the complexity of the scene (amount of objects loaded, etc) + irr_driver->addSceneComplexity(node_count); for (unsigned int i = 0; i < node_count; i++) { main_loop->renderGUI(4950, i, node_count);