From 909acac329b603b29e7328895933520adca225d6 Mon Sep 17 00:00:00 2001 From: lawnjelly Date: Mon, 27 Nov 2023 07:07:01 +0000 Subject: [PATCH] Discrete Level of Detail 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. --- doc/classes/Camera.xml | 5 + doc/classes/GeometryInstance.xml | 16 -- doc/classes/LOD.xml | 27 ++ doc/classes/RenderingServer.xml | 19 -- doc/classes/Spatial.xml | 4 + editor/editor_node.cpp | 14 ++ editor/icons/icon_l_o_d.svg | 1 + editor/plugins/spatial_editor_plugin.cpp | 16 ++ editor/plugins/spatial_editor_plugin.h | 1 + editor/scene_tree_editor.cpp | 13 +- editor/scene_tree_editor.h | 2 +- scene/3d/camera.cpp | 3 + scene/3d/camera.h | 4 + scene/3d/lod.cpp | 250 +++++++++++++++++++ scene/3d/lod.h | 73 ++++++ scene/3d/lod_manager.cpp | 182 ++++++++++++++ scene/3d/lod_manager.h | 74 ++++++ scene/3d/visual_instance.cpp | 59 ----- scene/3d/visual_instance.h | 16 -- scene/main/spatial.cpp | 8 + scene/main/spatial.h | 4 + scene/register_scene_types.cpp | 2 + scene/resources/world_3d.cpp | 27 ++ scene/resources/world_3d.h | 9 + servers/rendering/rendering_server_raster.h | 3 - servers/rendering/rendering_server_scene.h | 3 - servers/rendering/rendering_server_wrap_mt.h | 3 - servers/rendering_server.cpp | 2 - servers/rendering_server.h | 3 - 29 files changed, 714 insertions(+), 129 deletions(-) create mode 100644 doc/classes/LOD.xml create mode 100644 editor/icons/icon_l_o_d.svg create mode 100644 scene/3d/lod.cpp create mode 100644 scene/3d/lod.h create mode 100644 scene/3d/lod_manager.cpp create mode 100644 scene/3d/lod_manager.h diff --git a/doc/classes/Camera.xml b/doc/classes/Camera.xml index 2e0e36823..a3945409b 100644 --- a/doc/classes/Camera.xml +++ b/doc/classes/Camera.xml @@ -137,6 +137,11 @@ + + If [code]true[/code], the camera will be used to calculate the level of detail in [LOD] nodes. + Objects further from the camera will select lower levels of detail than those closer to the camera. + [b]Note:[/b] This property has no effect if [member projection] is set to [constant PROJECTION_ORTHOGONAL]. + The culling mask that describes which 3D render layers are rendered by this camera. diff --git a/doc/classes/GeometryInstance.xml b/doc/classes/GeometryInstance.xml index 95233c55e..6dcfd3c11 100644 --- a/doc/classes/GeometryInstance.xml +++ b/doc/classes/GeometryInstance.xml @@ -39,22 +39,6 @@ The extra distance added to the GeometryInstance's bounding box ([AABB]) to increase its cull box. - - The GeometryInstance's max LOD distance. - [b]Note:[/b] This property currently has no effect. - - - The GeometryInstance's max LOD margin. - [b]Note:[/b] This property currently has no effect. - - - The GeometryInstance's min LOD distance. - [b]Note:[/b] This property currently has no effect. - - - The GeometryInstance's min LOD margin. - [b]Note:[/b] This property currently has no effect. - The material overlay for the whole geometry. If a material is assigned to this property, it will be rendered on top of any other active material for all the surfaces. diff --git a/doc/classes/LOD.xml b/doc/classes/LOD.xml new file mode 100644 index 000000000..b7cd39a95 --- /dev/null +++ b/doc/classes/LOD.xml @@ -0,0 +1,27 @@ + + + + Provides discrete level of detail. + + + Automatically hides and shows [Spatial] children depending on the distance from the closest [Camera] to the [LOD] node. Child nodes that don't inherit from [Spatial] are ignored by the LOD system. + Children are shown in order with the first children shown when closest to the [Camera], and the latter children shown when further away. + The threshold distances for changes are determined by [member Spatial.lod_range]. + For example, a first child with a range of [code]2[/code] will be shown from distance 0 to 2. A second child with a range of [code]5[/code] will be shown from distance 2 to 7, etc. + + + + + + + + To prevent flickering at borders, a hysteresis distance will be added to threshold distances when the object is moving away from the [Camera]. + + + Determines the rate at which level of detail will be updated. + Higher priorities will update faster, but use more CPU. + + + + + diff --git a/doc/classes/RenderingServer.xml b/doc/classes/RenderingServer.xml index 73d862b5b..f81700025 100644 --- a/doc/classes/RenderingServer.xml +++ b/doc/classes/RenderingServer.xml @@ -1293,14 +1293,6 @@ Once finished with your RID, you will want to free the RID using the RenderingServer's [method free_rid] static method. - - - - - - Not implemented in Godot 3.x. - - @@ -1309,17 +1301,6 @@ Sets the shadow casting setting to one of [enum ShadowCastingSetting]. Equivalent to [member GeometryInstance.cast_shadow]. - - - - - - - - - Not implemented in Godot 3.x. - - diff --git a/doc/classes/Spatial.xml b/doc/classes/Spatial.xml index 12255cc5c..261a05863 100644 --- a/doc/classes/Spatial.xml +++ b/doc/classes/Spatial.xml @@ -294,6 +294,10 @@ Global position of this node. This is equivalent to [code]global_transform.origin[/code]. + + Determines the threshold distance at which this node will be shown or hidden when this node is parented by a [LOD] node. + For example, a first child with a range of [code]2[/code] will be shown from distance 0 to 2. A second child with a range of [code]5[/code] will be shown from distance 2 to 7, etc. + The merging mode determines whether merging features of the engine ([MergeGroup] and [RoomManager]) will attempt to operate on branches of the scene tree. The default mode inherited from the scene tree root is [constant MERGING_MODE_ON]. diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index 35c047fe3..a7e86ee8e 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -1438,7 +1438,17 @@ void EditorNode::_find_node_types(Node *p_node, int &count_2d, int &count_3d) { void EditorNode::_save_scene_with_preview(String p_file, int p_idx) { EditorProgress save("save", TTR("Saving Scene"), 4); + Ref edited_world; + if (editor_data.get_edited_scene_root() != nullptr) { + // Allow a generic mechanism for the engine to make changes prior, and after saving. + if (editor_data.get_edited_scene_root()->get_tree() && editor_data.get_edited_scene_root()->get_tree()->get_root()) { + edited_world = editor_data.get_edited_scene_root()->get_tree()->get_root()->get_world(); + if (edited_world.is_valid()) { + edited_world->notify_saving(true); + } + } + save.step(TTR("Analyzing"), 0); int c2d = 0; @@ -1516,6 +1526,10 @@ void EditorNode::_save_scene_with_preview(String p_file, int p_idx) { save.step(TTR("Saving Scene"), 4); _save_scene(p_file, p_idx); + if (edited_world.is_valid()) { + edited_world->notify_saving(false); + } + if (!singleton->cmdline_export_mode) { EditorResourcePreview::get_singleton()->check_for_invalidation(p_file); } diff --git a/editor/icons/icon_l_o_d.svg b/editor/icons/icon_l_o_d.svg new file mode 100644 index 000000000..e75ebb047 --- /dev/null +++ b/editor/icons/icon_l_o_d.svg @@ -0,0 +1 @@ + diff --git a/editor/plugins/spatial_editor_plugin.cpp b/editor/plugins/spatial_editor_plugin.cpp index 4c0df8b62..56bab7b65 100644 --- a/editor/plugins/spatial_editor_plugin.cpp +++ b/editor/plugins/spatial_editor_plugin.cpp @@ -69,6 +69,7 @@ #include "editor/spatial_editor_gizmos.h" #include "scene/3d/camera.h" #include "scene/3d/collision_shape.h" +#include "scene/3d/lod_manager.h" #include "scene/3d/mesh_instance.h" #include "scene/3d/physics_body.h" #include "scene/3d/room_manager.h" @@ -2836,7 +2837,12 @@ void SpatialEditorViewport::_notification(int p_what) { } else { set_freelook_active(false); } + call_deferred("update_transform_gizmo_view"); + + if (camera) { + camera->set_affect_lod(visible); + } } if (p_what == NOTIFICATION_RESIZED) { @@ -5690,6 +5696,14 @@ void SpatialEditor::_menu_item_pressed(int p_option) { RenderingServer::get_singleton()->set_use_occlusion_culling(!is_checked); view_menu->get_popup()->set_item_checked(checkbox_id, !is_checked); + } break; + case MENU_VIEW_LEVEL_OF_DETAIL: { + int checkbox_id = view_menu->get_popup()->get_item_index(p_option); + bool is_checked = view_menu->get_popup()->is_item_checked(checkbox_id); + LODManager::set_enabled(!is_checked); + view_menu->get_popup()->set_item_checked(checkbox_id, !is_checked); + EditorNode::get_singleton()->get_scene_tree_dock()->get_tree_editor()->update_tree(); + } break; case MENU_VIEW_CAMERA_SETTINGS: { settings_dialog->popup_centered(settings_vbc->get_combined_minimum_size() + Size2(50, 50)); @@ -7336,6 +7350,7 @@ SpatialEditor::SpatialEditor(EditorNode *p_editor) { p->add_check_shortcut(ED_SHORTCUT("spatial_editor/view_grid", TTR("View Grid"), KEY_NUMBERSIGN), MENU_VIEW_GRID); p->add_check_shortcut(ED_SHORTCUT("spatial_editor/view_portal_culling", TTR("View Portal Culling"), KEY_MASK_ALT | KEY_P), MENU_VIEW_PORTAL_CULLING); p->add_check_shortcut(ED_SHORTCUT("spatial_editor/view_occlusion_culling", TTR("View Occlusion Culling")), MENU_VIEW_OCCLUSION_CULLING); + p->add_check_shortcut(ED_SHORTCUT("spatial_editor/view_level_of_detail", TTR("View Level of Detail")), MENU_VIEW_LEVEL_OF_DETAIL); p->add_separator(); p->add_shortcut(ED_SHORTCUT("spatial_editor/settings", TTR("Settings...")), MENU_VIEW_CAMERA_SETTINGS); @@ -7343,6 +7358,7 @@ SpatialEditor::SpatialEditor(EditorNode *p_editor) { p->set_item_checked(p->get_item_index(MENU_VIEW_ORIGIN), true); p->set_item_checked(p->get_item_index(MENU_VIEW_GRID), true); p->set_item_checked(p->get_item_index(MENU_VIEW_OCCLUSION_CULLING), true); + p->set_item_checked(p->get_item_index(MENU_VIEW_LEVEL_OF_DETAIL), true); p->connect("id_pressed", this, "_menu_item_pressed"); diff --git a/editor/plugins/spatial_editor_plugin.h b/editor/plugins/spatial_editor_plugin.h index 9cbef9b76..f88e1be3a 100644 --- a/editor/plugins/spatial_editor_plugin.h +++ b/editor/plugins/spatial_editor_plugin.h @@ -658,6 +658,7 @@ private: MENU_VIEW_GRID, MENU_VIEW_PORTAL_CULLING, MENU_VIEW_OCCLUSION_CULLING, + MENU_VIEW_LEVEL_OF_DETAIL, MENU_VIEW_GIZMOS_3D_ICONS, MENU_VIEW_CAMERA_SETTINGS, MENU_SNAP_TO_FLOOR, diff --git a/editor/scene_tree_editor.cpp b/editor/scene_tree_editor.cpp index 0b649321c..001c997df 100644 --- a/editor/scene_tree_editor.cpp +++ b/editor/scene_tree_editor.cpp @@ -54,6 +54,7 @@ #include "editor/plugins/canvas_item_editor_plugin.h" #include "scene/animation/animation_player.h" #include "scene/gui/box_container.h" +#include "scene/3d/lod_manager.h" #include "scene/gui/label.h" #include "scene/gui/line_edit.h" #include "scene/gui/texture_rect.h" @@ -194,7 +195,7 @@ void SceneTreeEditor::_toggle_visible(Node *p_node) { } } -void SceneTreeEditor::_add_nodes(Node *p_node, TreeItem *p_parent) { +void SceneTreeEditor::_add_nodes(Node *p_node, TreeItem *p_parent, bool p_disable_visibility) { if (!p_node) { return; } @@ -443,9 +444,9 @@ void SceneTreeEditor::_add_nodes(Node *p_node, TreeItem *p_parent) { bool v = p_node->call("is_visible"); if (v) { - item->add_button(0, get_theme_icon("GuiVisibilityVisible", "EditorIcons"), BUTTON_VISIBILITY, false, TTR("Toggle Visibility")); + item->add_button(0, get_theme_icon("GuiVisibilityVisible", "EditorIcons"), BUTTON_VISIBILITY, p_disable_visibility, TTR("Toggle Visibility")); } else { - item->add_button(0, get_theme_icon("GuiVisibilityHidden", "EditorIcons"), BUTTON_VISIBILITY, false, TTR("Toggle Visibility")); + item->add_button(0, get_theme_icon("GuiVisibilityHidden", "EditorIcons"), BUTTON_VISIBILITY, p_disable_visibility, TTR("Toggle Visibility")); } if (!p_node->is_connected("visibility_changed", this, "_node_visibility_changed")) { @@ -475,8 +476,12 @@ void SceneTreeEditor::_add_nodes(Node *p_node, TreeItem *p_parent) { item->set_as_cursor(0); } + // In some cases we want to disable visibility control by the user + // for automatically visibility-controlled children. + bool disable_visibility = p_node->is_class("LOD") && LODManager::is_enabled(); + for (int i = 0; i < p_node->get_child_count(); i++) { - _add_nodes(p_node->get_child(i), item); + _add_nodes(p_node->get_child(i), item, disable_visibility); } if (valid_types.size()) { diff --git a/editor/scene_tree_editor.h b/editor/scene_tree_editor.h index 7da8666ad..dfe670b65 100644 --- a/editor/scene_tree_editor.h +++ b/editor/scene_tree_editor.h @@ -88,7 +88,7 @@ class SceneTreeEditor : public Control { void _compute_hash(Node *p_node, uint64_t &hash); - void _add_nodes(Node *p_node, TreeItem *p_parent); + void _add_nodes(Node *p_node, TreeItem *p_parent, bool p_disable_visibility = false); void _test_update_tree(); void _update_tree(bool p_scroll_to_selected = false); bool _update_filter(TreeItem *p_parent = nullptr, bool p_scroll_to_selected = false); diff --git a/scene/3d/camera.cpp b/scene/3d/camera.cpp index 4d9794aa1..5744218b6 100644 --- a/scene/3d/camera.cpp +++ b/scene/3d/camera.cpp @@ -510,6 +510,8 @@ void Camera::_bind_methods() { ClassDB::bind_method(D_METHOD("get_doppler_tracking"), &Camera::get_doppler_tracking); ClassDB::bind_method(D_METHOD("get_frustum"), &Camera::get_frustum); ClassDB::bind_method(D_METHOD("get_camera_rid"), &Camera::get_camera); + ClassDB::bind_method(D_METHOD("set_affect_lod", "enable"), &Camera::set_affect_lod); + ClassDB::bind_method(D_METHOD("get_affect_lod"), &Camera::get_affect_lod); ClassDB::bind_method(D_METHOD("set_cull_mask_bit", "layer", "enable"), &Camera::set_cull_mask_bit); ClassDB::bind_method(D_METHOD("get_cull_mask_bit", "layer"), &Camera::get_cull_mask_bit); @@ -529,6 +531,7 @@ void Camera::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "frustum_offset"), "set_frustum_offset", "get_frustum_offset"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "near", PROPERTY_HINT_EXP_RANGE, "0.01,8192,0.01,or_greater"), "set_znear", "get_znear"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "far", PROPERTY_HINT_EXP_RANGE, "0.1,8192,0.1,or_greater"), "set_zfar", "get_zfar"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "affect_lod"), "set_affect_lod", "get_affect_lod"); BIND_ENUM_CONSTANT(PROJECTION_PERSPECTIVE); BIND_ENUM_CONSTANT(PROJECTION_ORTHOGONAL); diff --git a/scene/3d/camera.h b/scene/3d/camera.h index 45683c54a..2cadb81bc 100644 --- a/scene/3d/camera.h +++ b/scene/3d/camera.h @@ -92,6 +92,7 @@ private: DopplerTracking doppler_tracking; Ref velocity_tracker; + bool affect_lod = true; protected: void _update_camera(); @@ -174,6 +175,9 @@ public: Vector3 get_doppler_tracked_velocity() const; + void set_affect_lod(bool p_enable) { affect_lod = p_enable; } + bool get_affect_lod() const { return affect_lod; } + Camera(); ~Camera(); }; diff --git a/scene/3d/lod.cpp b/scene/3d/lod.cpp new file mode 100644 index 000000000..09c2e8849 --- /dev/null +++ b/scene/3d/lod.cpp @@ -0,0 +1,250 @@ +/**************************************************************************/ +/* lod.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.h" + +#include "core/config/engine.h" +#include "scene/3d/visual_instance.h" + +void LOD::_lod_register() { + if (!data.registered) { + Ref world = get_world(); + ERR_FAIL_COND(!world.is_valid()); + world->_register_lod(this, data.queue_id); + data.registered = true; + } +} + +void LOD::_lod_unregister() { + if (data.registered) { + Ref world = get_world(); + ERR_FAIL_COND(!world.is_valid()); + world->_unregister_lod(this, data.queue_id); + data.registered = false; + } +} + +void LOD::_notification(int p_what) { + switch (p_what) { + case NOTIFICATION_ENTER_TREE: { + if (is_visible_in_tree()) { + _lod_register(); + } + } break; + case NOTIFICATION_EXIT_TREE: { + if (is_visible_in_tree()) { + _lod_unregister(); + } + } break; + case NOTIFICATION_VISIBILITY_CHANGED: { + if (is_inside_tree()) { + if (is_visible_in_tree()) { + _lod_register(); + } else { + _lod_unregister(); + } + } + } break; + default: + break; + } +} + +void LOD::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_hysteresis", "distance"), &LOD::set_hysteresis); + ClassDB::bind_method(D_METHOD("get_hysteresis"), &LOD::get_hysteresis); + + ADD_PROPERTY(PropertyInfo(Variant::REAL, "hysteresis", PROPERTY_HINT_RANGE, "0,1024,0.01,or_greater"), "set_hysteresis", "get_hysteresis"); + + ClassDB::bind_method(D_METHOD("set_lod_priority", "priority"), &LOD::set_lod_priority); + ClassDB::bind_method(D_METHOD("get_lod_priority"), &LOD::get_lod_priority); + ADD_PROPERTY(PropertyInfo(Variant::INT, "lod_priority", PROPERTY_HINT_RANGE, "0,4"), "set_lod_priority", "get_lod_priority"); +} + +void LOD::set_hysteresis(real_t p_distance) { + data.hysteresis = CLAMP((float)p_distance, 0.0f, 100000.0f); +} + +void LOD::set_lod_priority(int p_priority) { + // We are just using priority as a user facing + // description. Internally we use queues. + int queue_id = CLAMP(p_priority, 0, 4); + + if (queue_id == data.queue_id) { + return; + } + + if (is_inside_tree()) { + // If already in the world, we must remove the LOD + // and re-add in a different queue. + Ref world = get_world(); + ERR_FAIL_COND(!world.is_valid()); + world->_unregister_lod(this, data.queue_id); + + data.queue_id = queue_id; + + world->_register_lod(this, data.queue_id); + } else { + data.queue_id = queue_id; + } +} + +void LOD::_lod_pre_save() { + // The pre-save is primarily for the editor, + // to ensure that saved scenes do not have unnecessary + // diffs because of changes to which LOD child is active. + // We standardize on just showing the first child in saved scenes. + _update_child_distances(); + + int32_t num_lods = data.lod_children.size(); + + // Make first visible, and all others invisible. + data.current_lod_child = 0; + + for (int32_t n = 0; n < num_lods; n++) { + uint32_t child_id = data.lod_children[n].child_id; + Spatial *child = Object::cast_to(get_child(child_id)); + + if (child) { + child->set_visible(n == data.current_lod_child); + } + } +} + +// Returns whether a visibility change was triggered. +bool LOD::_lod_update(float p_camera_dist_squared) { + // This should later be done as a one-off, as + // this is expensive. + _update_child_distances(); + + int32_t num_lods = data.lod_children.size(); + + // LOD node has no valid children to update. + if (!num_lods) { + return false; + } + + data.current_lod_child = MIN(data.current_lod_child, num_lods - 1); + int32_t curr = data.current_lod_child; + + float dist = Math::sqrt(p_camera_dist_squared); + + bool changed = true; + + while (changed) { + changed = false; + if ((curr < num_lods - 1) && (dist >= (data.lod_children[curr + 1].distance) + data.hysteresis)) { + // Lower detail. + curr += 1; + changed = true; + } + + if (curr && (dist < data.lod_children[curr].distance)) { + // Increase detail. + curr -= 1; + changed = true; + } + } + + // No change? + if ((curr == data.current_lod_child) && (data.current_lod_node == get_child(data.lod_children[curr].child_id))) { + return false; + } + + data.current_lod_child = curr; + + // Make current visible, and all others invisible. + for (int32_t n = 0; n < num_lods; n++) { + uint32_t child_id = data.lod_children[n].child_id; + + Spatial *child = Object::cast_to(get_child(child_id)); + + if (child) { + child->set_visible(n == curr); + if (n == curr) { + data.current_lod_node = child; + } + } + } + + return true; +} + +void LOD::_update_child_distances() { + // Reserve enough space for all children, assuming they are all valid. + LODChild *lod_children = (LODChild *)alloca(sizeof(LODChild) * get_child_count()); + + // Reset prior to loop. + float total_dist = 0.0f; + uint32_t valid_count = 0; + +#ifdef TOOLS_ENABLED + bool is_editor = Engine::get_singleton()->is_editor_hint(); + uint32_t visible_count = 0; +#endif + + // Check every possible node child, not all will be valid lod children. + for (int32_t n = 0; n < get_child_count(); n++) { + // Destination for a valid child. + LODChild &lod_child = lod_children[valid_count]; + + const Spatial *child = Object::cast_to(get_child(n)); + if (child) { + // Fill the data. + lod_child.distance = total_dist; + lod_child.child_id = n; + + // Keep running total of the distance range used by each lod child. + total_dist += child->get_lod_range(); + +#ifdef TOOLS_ENABLED + if (is_editor && child->is_visible()) { + visible_count++; + } +#endif + valid_count++; + } + } + + // Size the actual vector, and copy data across. + data.lod_children.resize(valid_count); + if (valid_count) { + memcpy(&data.lod_children[0], lod_children, valid_count * sizeof(LODChild)); + } + +#ifdef TOOLS_ENABLED + // Something external has changed the visibilities of the children, + // such as the editor. + if (is_editor && visible_count != 1) { + // Force the current child to reset. + data.current_lod_child = -1; + } +#endif +} diff --git a/scene/3d/lod.h b/scene/3d/lod.h new file mode 100644 index 000000000..3a3cb9da6 --- /dev/null +++ b/scene/3d/lod.h @@ -0,0 +1,73 @@ +/**************************************************************************/ +/* lod.h */ +/**************************************************************************/ +/* 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. */ +/**************************************************************************/ + +#ifndef LOD_H +#define LOD_H + +#include "scene/main/spatial.h" + +class LOD : public Spatial { + GDCLASS(LOD, Spatial); + + struct LODChild { + float distance; + int32_t child_id; + }; + + struct Data { + LocalVector lod_children; + int32_t current_lod_child = 0; + float hysteresis = 1.0f; + int32_t queue_id = 0; + const Spatial *current_lod_node = nullptr; + bool registered = false; + } data; + + friend class LODManager; + bool _lod_update(float p_camera_dist_squared); + void _lod_pre_save(); + void _update_child_distances(); + + void _lod_register(); + void _lod_unregister(); + +protected: + void _notification(int p_what); + static void _bind_methods(); + +public: + void set_hysteresis(real_t p_distance); + real_t get_hysteresis() const { return data.hysteresis; } + + void set_lod_priority(int p_priority); + int get_lod_priority() const { return data.queue_id; } +}; + +#endif // LOD_H diff --git a/scene/3d/lod_manager.cpp b/scene/3d/lod_manager.cpp new file mode 100644 index 000000000..ec3281625 --- /dev/null +++ b/scene/3d/lod_manager.cpp @@ -0,0 +1,182 @@ +/**************************************************************************/ +/* 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 &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 &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); + } +} diff --git a/scene/3d/lod_manager.h b/scene/3d/lod_manager.h new file mode 100644 index 000000000..0a0333870 --- /dev/null +++ b/scene/3d/lod_manager.h @@ -0,0 +1,74 @@ +/**************************************************************************/ +/* lod_manager.h */ +/**************************************************************************/ +/* 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. */ +/**************************************************************************/ + +#ifndef LOD_MANAGER_H +#define LOD_MANAGER_H + +#include "core/containers/local_vector.h" +#include "core/os/mutex.h" + +class Camera; +class LOD; +struct Vector3; + +class LODManager { +public: + enum { NUM_LOD_QUEUES = 5 }; + + void register_camera(Camera *p_camera); + void remove_camera(Camera *p_camera); + void register_lod(LOD *p_lod, uint32_t p_queue_id); + void unregister_lod(LOD *p_lod, uint32_t p_queue_id); + void update(); + + void notify_saving(bool p_active); + + static void set_enabled(bool p_enabled) { _enabled = p_enabled; } + static bool is_enabled() { return _enabled; } + +private: + void _update_queue(uint32_t p_queue_id, const Vector3 *p_camera_positions, uint32_t p_num_cameras); + + struct Queue { + LocalVector lods; + uint32_t lod_iterator = 0; + }; + + struct Data { + LocalVector cameras; + Queue queues[NUM_LOD_QUEUES]; + BinaryMutex mutex; + bool saving = false; + } data; + + static bool _enabled; +}; + +#endif // LOD_MANAGER_H diff --git a/scene/3d/visual_instance.cpp b/scene/3d/visual_instance.cpp index f13bc14ca..da663cd8a 100644 --- a/scene/3d/visual_instance.cpp +++ b/scene/3d/visual_instance.cpp @@ -251,42 +251,6 @@ Ref GeometryInstance::get_material_overlay() const { return material_overlay; } -void GeometryInstance::set_lod_min_distance(float p_dist) { - lod_min_distance = p_dist; - RS::get_singleton()->instance_geometry_set_draw_range(get_instance(), lod_min_distance, lod_max_distance, lod_min_hysteresis, lod_max_hysteresis); -} - -float GeometryInstance::get_lod_min_distance() const { - return lod_min_distance; -} - -void GeometryInstance::set_lod_max_distance(float p_dist) { - lod_max_distance = p_dist; - RS::get_singleton()->instance_geometry_set_draw_range(get_instance(), lod_min_distance, lod_max_distance, lod_min_hysteresis, lod_max_hysteresis); -} - -float GeometryInstance::get_lod_max_distance() const { - return lod_max_distance; -} - -void GeometryInstance::set_lod_min_hysteresis(float p_dist) { - lod_min_hysteresis = p_dist; - RS::get_singleton()->instance_geometry_set_draw_range(get_instance(), lod_min_distance, lod_max_distance, lod_min_hysteresis, lod_max_hysteresis); -} - -float GeometryInstance::get_lod_min_hysteresis() const { - return lod_min_hysteresis; -} - -void GeometryInstance::set_lod_max_hysteresis(float p_dist) { - lod_max_hysteresis = p_dist; - RS::get_singleton()->instance_geometry_set_draw_range(get_instance(), lod_min_distance, lod_max_distance, lod_min_hysteresis, lod_max_hysteresis); -} - -float GeometryInstance::get_lod_max_hysteresis() const { - return lod_max_hysteresis; -} - void GeometryInstance::_notification(int p_what) { } @@ -347,18 +311,6 @@ void GeometryInstance::_bind_methods() { ClassDB::bind_method(D_METHOD("set_cast_shadows_setting", "shadow_casting_setting"), &GeometryInstance::set_cast_shadows_setting); ClassDB::bind_method(D_METHOD("get_cast_shadows_setting"), &GeometryInstance::get_cast_shadows_setting); - ClassDB::bind_method(D_METHOD("set_lod_max_hysteresis", "mode"), &GeometryInstance::set_lod_max_hysteresis); - ClassDB::bind_method(D_METHOD("get_lod_max_hysteresis"), &GeometryInstance::get_lod_max_hysteresis); - - ClassDB::bind_method(D_METHOD("set_lod_max_distance", "mode"), &GeometryInstance::set_lod_max_distance); - ClassDB::bind_method(D_METHOD("get_lod_max_distance"), &GeometryInstance::get_lod_max_distance); - - ClassDB::bind_method(D_METHOD("set_lod_min_hysteresis", "mode"), &GeometryInstance::set_lod_min_hysteresis); - ClassDB::bind_method(D_METHOD("get_lod_min_hysteresis"), &GeometryInstance::get_lod_min_hysteresis); - - ClassDB::bind_method(D_METHOD("set_lod_min_distance", "mode"), &GeometryInstance::set_lod_min_distance); - ClassDB::bind_method(D_METHOD("get_lod_min_distance"), &GeometryInstance::get_lod_min_distance); - ClassDB::bind_method(D_METHOD("set_extra_cull_margin", "margin"), &GeometryInstance::set_extra_cull_margin); ClassDB::bind_method(D_METHOD("get_extra_cull_margin"), &GeometryInstance::get_extra_cull_margin); @@ -372,12 +324,6 @@ void GeometryInstance::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "cast_shadow", PROPERTY_HINT_ENUM, "Off,On,Double-Sided,Shadows Only"), "set_cast_shadows_setting", "get_cast_shadows_setting"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "extra_cull_margin", PROPERTY_HINT_RANGE, "0,16384,0.01"), "set_extra_cull_margin", "get_extra_cull_margin"); - ADD_GROUP("LOD", "lod_"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "lod_min_distance", PROPERTY_HINT_RANGE, "0,32768,0.01"), "set_lod_min_distance", "get_lod_min_distance"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "lod_min_hysteresis", PROPERTY_HINT_RANGE, "0,32768,0.01"), "set_lod_min_hysteresis", "get_lod_min_hysteresis"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "lod_max_distance", PROPERTY_HINT_RANGE, "0,32768,0.01"), "set_lod_max_distance", "get_lod_max_distance"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "lod_max_hysteresis", PROPERTY_HINT_RANGE, "0,32768,0.01"), "set_lod_max_hysteresis", "get_lod_max_hysteresis"); - //ADD_SIGNAL( MethodInfo("visibility_changed")); BIND_ENUM_CONSTANT(SHADOW_CASTING_SETTING_OFF); @@ -390,11 +336,6 @@ void GeometryInstance::_bind_methods() { } GeometryInstance::GeometryInstance() { - lod_min_distance = 0; - lod_max_distance = 0; - lod_min_hysteresis = 0; - lod_max_hysteresis = 0; - for (int i = 0; i < FLAG_MAX; i++) { flags[i] = false; } diff --git a/scene/3d/visual_instance.h b/scene/3d/visual_instance.h index e604b8e72..765d293bb 100644 --- a/scene/3d/visual_instance.h +++ b/scene/3d/visual_instance.h @@ -114,10 +114,6 @@ private: ShadowCastingSetting shadow_casting_setting; Ref material_override; Ref material_overlay; - float lod_min_distance; - float lod_max_distance; - float lod_min_hysteresis; - float lod_max_hysteresis; float extra_cull_margin; @@ -132,18 +128,6 @@ public: void set_cast_shadows_setting(ShadowCastingSetting p_shadow_casting_setting); ShadowCastingSetting get_cast_shadows_setting() const; - void set_lod_min_distance(float p_dist); - float get_lod_min_distance() const; - - void set_lod_max_distance(float p_dist); - float get_lod_max_distance() const; - - void set_lod_min_hysteresis(float p_dist); - float get_lod_min_hysteresis() const; - - void set_lod_max_hysteresis(float p_dist); - float get_lod_max_hysteresis() const; - virtual void set_material_override(const Ref &p_material); Ref get_material_override() const; diff --git a/scene/main/spatial.cpp b/scene/main/spatial.cpp index 1549882a7..da097297d 100644 --- a/scene/main/spatial.cpp +++ b/scene/main/spatial.cpp @@ -962,6 +962,10 @@ void Spatial::set_merging_mode(MergingMode p_mode) { _propagate_merging_allowed(merging_allowed); } +void Spatial::set_lod_range(float p_range) { + data.lod_range = p_range; +} + void Spatial::force_update_transform() { ERR_FAIL_COND(!is_inside_tree()); if (!xform_change.in_list()) { @@ -1043,6 +1047,9 @@ void Spatial::_bind_methods() { ClassDB::bind_method(D_METHOD("set_merging_mode", "mode"), &Spatial::set_merging_mode); ClassDB::bind_method(D_METHOD("get_merging_mode"), &Spatial::get_merging_mode); + ClassDB::bind_method(D_METHOD("set_lod_range", "range"), &Spatial::set_lod_range); + ClassDB::bind_method(D_METHOD("get_lod_range"), &Spatial::get_lod_range); + ClassDB::bind_method(D_METHOD("to_local", "global_point"), &Spatial::to_local); ClassDB::bind_method(D_METHOD("to_global", "local_point"), &Spatial::to_global); @@ -1072,6 +1079,7 @@ void Spatial::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "visible"), "set_visible", "is_visible"); ADD_GROUP("Misc", ""); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "lod_range", PROPERTY_HINT_RANGE, "0,1024,0.01,or_greater"), "set_lod_range", "get_lod_range"); ADD_PROPERTY(PropertyInfo(Variant::INT, "merging_mode", PROPERTY_HINT_ENUM, "Inherit,Off,On"), "set_merging_mode", "get_merging_mode"); ADD_SIGNAL(MethodInfo("visibility_changed")); diff --git a/scene/main/spatial.h b/scene/main/spatial.h index 9ac0d5bcd..579ebe5be 100644 --- a/scene/main/spatial.h +++ b/scene/main/spatial.h @@ -116,6 +116,7 @@ private: List children; List::Element *C; + float lod_range = 10.0f; ClientPhysicsInterpolationData *client_physics_interpolation_data; #ifdef TOOLS_ENABLED @@ -263,6 +264,9 @@ public: void force_update_transform(); + void set_lod_range(float p_range); + float get_lod_range() const { return data.lod_range; } + Spatial(); ~Spatial(); }; diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp index 897b49eee..2c323ef58 100644 --- a/scene/register_scene_types.cpp +++ b/scene/register_scene_types.cpp @@ -200,6 +200,7 @@ #include "scene/3d/interpolated_camera.h" #include "scene/3d/light.h" #include "scene/3d/listener.h" +#include "scene/3d/lod.h" #include "scene/3d/merge_group.h" #include "scene/3d/mesh_instance.h" #include "scene/3d/multimesh_instance.h" @@ -447,6 +448,7 @@ void register_scene_types() { ClassDB::register_class(); ClassDB::register_class(); ClassDB::register_class(); + ClassDB::register_class(); ClassDB::register_class(); ClassDB::register_virtual_class(); ClassDB::register_class(); diff --git a/scene/resources/world_3d.cpp b/scene/resources/world_3d.cpp index e5ab16163..196b5701f 100644 --- a/scene/resources/world_3d.cpp +++ b/scene/resources/world_3d.cpp @@ -35,6 +35,7 @@ #include "core/math/octree.h" #include "core/math/projection.h" #include "scene/3d/camera.h" +#include "scene/3d/lod_manager.h" #include "scene/3d/visibility_notifier.h" #include "scene/main/scene_string_names.h" #include "servers/navigation_server.h" @@ -224,6 +225,7 @@ struct SpatialIndexer { void World3D::_register_camera(Camera *p_camera) { #ifndef _3D_DISABLED indexer->_add_camera(p_camera); + lod_manager->register_camera(p_camera); #endif } @@ -235,6 +237,19 @@ void World3D::_update_camera(Camera *p_camera) { void World3D::_remove_camera(Camera *p_camera) { #ifndef _3D_DISABLED indexer->_remove_camera(p_camera); + lod_manager->remove_camera(p_camera); +#endif +} + +void World::_register_lod(LOD *p_lod, uint32_t p_queue_id) { +#ifndef _3D_DISABLED + lod_manager->register_lod(p_lod, p_queue_id); +#endif +} + +void World::_unregister_lod(LOD *p_lod, uint32_t p_queue_id) { +#ifndef _3D_DISABLED + lod_manager->unregister_lod(p_lod, p_queue_id); #endif } @@ -259,6 +274,7 @@ void World3D::_remove_notifier(VisibilityNotifier *p_notifier) { void World3D::_update(uint64_t p_frame) { #ifndef _3D_DISABLED indexer->_update(p_frame); + lod_manager->update(); #endif } @@ -339,7 +355,15 @@ void World3D::move_cameras_into(Ref target) { } } +void World::notify_saving(bool p_active) { + if (lod_manager) { + lod_manager->notify_saving(p_active); + } +} + void World3D::_bind_methods() { + + ClassDB::bind_method(D_METHOD("get_space"), &World3D::get_space); ClassDB::bind_method(D_METHOD("get_scenario"), &World3D::get_scenario); ClassDB::bind_method(D_METHOD("get_navigation_map"), &World3D::get_navigation_map); @@ -390,6 +414,7 @@ World3D::World3D() { indexer = NULL; #else indexer = memnew(SpatialIndexer); + lod_manager = memnew(LODManager); #endif } @@ -404,5 +429,7 @@ World3D::~World3D() { #ifndef _3D_DISABLED memdelete(indexer); + memdelete(lod_manager); + lod_manager = nullptr; #endif } diff --git a/scene/resources/world_3d.h b/scene/resources/world_3d.h index 4ecbcd92e..b70a68124 100644 --- a/scene/resources/world_3d.h +++ b/scene/resources/world_3d.h @@ -39,7 +39,9 @@ class Camera; class VisibilityNotifier; +class LOD; struct SpatialIndexer; +class LODManager; class World3D : public Resource { GDCLASS(World3D, Resource); @@ -51,6 +53,7 @@ private: RID navigation_map; RID vertex_lights_3d_map; SpatialIndexer *indexer; + LODManager *lod_manager = nullptr; Ref environment; Ref fallback_environment; @@ -59,6 +62,7 @@ protected: friend class Camera; friend class VisibilityNotifier; + friend class LOD; void _register_camera(Camera *p_camera); void _update_camera(Camera *p_camera); @@ -67,6 +71,10 @@ protected: void _register_notifier(VisibilityNotifier *p_notifier, const AABB &p_rect); void _update_notifier(VisibilityNotifier *p_notifier, const AABB &p_rect); void _remove_notifier(VisibilityNotifier *p_notifier); + + void _register_lod(LOD *p_lod, uint32_t p_queue_id); + void _unregister_lod(LOD *p_lod, uint32_t p_queue_id); + friend class Viewport; friend class World; void _update(uint64_t p_frame); @@ -90,6 +98,7 @@ public: PhysicsDirectSpaceState *get_direct_space_state(); void move_cameras_into(Ref target); + void notify_saving(bool p_active); World3D(); ~World3D(); diff --git a/servers/rendering/rendering_server_raster.h b/servers/rendering/rendering_server_raster.h index 4ab4b2ede..338875155 100644 --- a/servers/rendering/rendering_server_raster.h +++ b/servers/rendering/rendering_server_raster.h @@ -564,9 +564,6 @@ public: BIND2(instance_geometry_set_material_override, RID, RID) BIND2(instance_geometry_set_material_overlay, RID, RID) - BIND5(instance_geometry_set_draw_range, RID, float, float, float, float) - BIND2(instance_geometry_set_as_instance_lod, RID, RID) - #undef BINDBASE //from now on, calls forwarded to this singleton #define BINDBASE RSG::canvas diff --git a/servers/rendering/rendering_server_scene.h b/servers/rendering/rendering_server_scene.h index 50e74a1dd..325755b13 100644 --- a/servers/rendering/rendering_server_scene.h +++ b/servers/rendering/rendering_server_scene.h @@ -770,9 +770,6 @@ public: virtual void instance_geometry_set_material_override(RID p_instance, RID p_material); virtual void instance_geometry_set_material_overlay(RID p_instance, RID p_material); - virtual void instance_geometry_set_draw_range(RID p_instance, float p_min, float p_max, float p_min_margin, float p_max_margin); - virtual void instance_geometry_set_as_instance_lod(RID p_instance, RID p_as_lod_of_instance); - _FORCE_INLINE_ void _update_instance(Instance *p_instance); _FORCE_INLINE_ void _update_instance_aabb(Instance *p_instance); _FORCE_INLINE_ void _update_dirty_instance(Instance *p_instance); diff --git a/servers/rendering/rendering_server_wrap_mt.h b/servers/rendering/rendering_server_wrap_mt.h index 76f625681..51605eb78 100644 --- a/servers/rendering/rendering_server_wrap_mt.h +++ b/servers/rendering/rendering_server_wrap_mt.h @@ -476,9 +476,6 @@ public: FUNC2(instance_geometry_set_material_override, RID, RID) FUNC2(instance_geometry_set_material_overlay, RID, RID) - FUNC5(instance_geometry_set_draw_range, RID, float, float, float, float) - FUNC2(instance_geometry_set_as_instance_lod, RID, RID) - /* CANVAS (2D) */ FUNCRID(canvas) diff --git a/servers/rendering_server.cpp b/servers/rendering_server.cpp index 336f432eb..fb8375363 100644 --- a/servers/rendering_server.cpp +++ b/servers/rendering_server.cpp @@ -2121,8 +2121,6 @@ void RenderingServer::_bind_methods() { ClassDB::bind_method(D_METHOD("instance_geometry_set_cast_shadows_setting", "instance", "shadow_casting_setting"), &RenderingServer::instance_geometry_set_cast_shadows_setting); ClassDB::bind_method(D_METHOD("instance_geometry_set_material_override", "instance", "material"), &RenderingServer::instance_geometry_set_material_override); ClassDB::bind_method(D_METHOD("instance_geometry_set_material_overlay", "instance", "material"), &RenderingServer::instance_geometry_set_material_overlay); - ClassDB::bind_method(D_METHOD("instance_geometry_set_draw_range", "instance", "min", "max", "min_margin", "max_margin"), &RenderingServer::instance_geometry_set_draw_range); - ClassDB::bind_method(D_METHOD("instance_geometry_set_as_instance_lod", "instance", "as_lod_of_instance"), &RenderingServer::instance_geometry_set_as_instance_lod); ClassDB::bind_method(D_METHOD("instances_cull_aabb", "aabb", "scenario"), &RenderingServer::_instances_cull_aabb_bind, DEFVAL(RID())); ClassDB::bind_method(D_METHOD("instances_cull_ray", "from", "to", "scenario"), &RenderingServer::_instances_cull_ray_bind, DEFVAL(RID())); diff --git a/servers/rendering_server.h b/servers/rendering_server.h index 6f29b7826..f42e6e633 100644 --- a/servers/rendering_server.h +++ b/servers/rendering_server.h @@ -892,9 +892,6 @@ public: virtual void instance_geometry_set_material_override(RID p_instance, RID p_material) = 0; virtual void instance_geometry_set_material_overlay(RID p_instance, RID p_material) = 0; - virtual void instance_geometry_set_draw_range(RID p_instance, float p_min, float p_max, float p_min_margin, float p_max_margin) = 0; - virtual void instance_geometry_set_as_instance_lod(RID p_instance, RID p_as_lod_of_instance) = 0; - /* CANVAS (2D) */ virtual RID canvas_create() = 0;