Skip to content

Commit

Permalink
Merge pull request #85437 from lawnjelly/lod_scene_side
Browse files Browse the repository at this point in the history
[3.x] Discrete Level of Detail
  • Loading branch information
akien-mga committed Mar 8, 2024
2 parents 6c35b48 + 1b5fa74 commit ae7dfd1
Show file tree
Hide file tree
Showing 30 changed files with 713 additions and 134 deletions.
5 changes: 5 additions & 0 deletions doc/classes/Camera.xml
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,11 @@
</method>
</methods>
<members>
<member name="affect_lod" type="bool" setter="set_affect_lod" getter="get_affect_lod" default="true">
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].
</member>
<member name="cull_mask" type="int" setter="set_cull_mask" getter="get_cull_mask" default="1048575">
The culling mask that describes which 3D render layers are rendered by this camera.
</member>
Expand Down
16 changes: 0 additions & 16 deletions doc/classes/GeometryInstance.xml
Original file line number Diff line number Diff line change
Expand Up @@ -45,22 +45,6 @@
<member name="lightmap_scale" type="int" setter="set_lightmap_scale" getter="get_lightmap_scale" enum="GeometryInstance.LightmapScale" default="0">
Scale factor for the generated baked lightmap. Useful for adding detail to certain mesh instances.
</member>
<member name="lod_max_distance" type="float" setter="set_lod_max_distance" getter="get_lod_max_distance" default="0.0">
The GeometryInstance's max LOD distance.
[b]Note:[/b] This property currently has no effect.
</member>
<member name="lod_max_hysteresis" type="float" setter="set_lod_max_hysteresis" getter="get_lod_max_hysteresis" default="0.0">
The GeometryInstance's max LOD margin.
[b]Note:[/b] This property currently has no effect.
</member>
<member name="lod_min_distance" type="float" setter="set_lod_min_distance" getter="get_lod_min_distance" default="0.0">
The GeometryInstance's min LOD distance.
[b]Note:[/b] This property currently has no effect.
</member>
<member name="lod_min_hysteresis" type="float" setter="set_lod_min_hysteresis" getter="get_lod_min_hysteresis" default="0.0">
The GeometryInstance's min LOD margin.
[b]Note:[/b] This property currently has no effect.
</member>
<member name="material_overlay" type="Material" setter="set_material_overlay" getter="get_material_overlay">
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.
Expand Down
27 changes: 27 additions & 0 deletions doc/classes/LOD.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="LOD" inherits="Spatial" version="3.6" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
Provides discrete level of detail.
</brief_description>
<description>
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.
</description>
<tutorials>
</tutorials>
<methods>
</methods>
<members>
<member name="hysteresis" type="float" setter="set_hysteresis" getter="get_hysteresis" default="1.0">
To prevent flickering at borders, a hysteresis distance will be added to threshold distances when the object is moving away from the [Camera].
</member>
<member name="lod_priority" type="int" setter="set_lod_priority" getter="get_lod_priority" default="0">
Determines the rate at which level of detail will be updated.
Higher priorities will update faster, but use more CPU.
</member>
</members>
<constants>
</constants>
</class>
4 changes: 4 additions & 0 deletions doc/classes/Spatial.xml
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,10 @@
<member name="global_translation" type="Vector3" setter="set_global_translation" getter="get_global_translation">
Global position of this node. This is equivalent to [code]global_transform.origin[/code].
</member>
<member name="lod_range" type="float" setter="set_lod_range" getter="get_lod_range" default="10.0">
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.
</member>
<member name="merging_mode" type="int" setter="set_merging_mode" getter="get_merging_mode" enum="Spatial.MergingMode" default="0">
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].
Expand Down
19 changes: 0 additions & 19 deletions doc/classes/VisualServer.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1462,14 +1462,6 @@
Once finished with your RID, you will want to free the RID using the VisualServer's [method free_rid] static method.
</description>
</method>
<method name="instance_geometry_set_as_instance_lod">
<return type="void" />
<argument index="0" name="instance" type="RID" />
<argument index="1" name="as_lod_of_instance" type="RID" />
<description>
Not implemented in Godot 3.x.
</description>
</method>
<method name="instance_geometry_set_cast_shadows_setting">
<return type="void" />
<argument index="0" name="instance" type="RID" />
Expand All @@ -1478,17 +1470,6 @@
Sets the shadow casting setting to one of [enum ShadowCastingSetting]. Equivalent to [member GeometryInstance.cast_shadow].
</description>
</method>
<method name="instance_geometry_set_draw_range">
<return type="void" />
<argument index="0" name="instance" type="RID" />
<argument index="1" name="min" type="float" />
<argument index="2" name="max" type="float" />
<argument index="3" name="min_margin" type="float" />
<argument index="4" name="max_margin" type="float" />
<description>
Not implemented in Godot 3.x.
</description>
</method>
<method name="instance_geometry_set_flag">
<return type="void" />
<argument index="0" name="instance" type="RID" />
Expand Down
14 changes: 14 additions & 0 deletions editor/editor_node.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1347,7 +1347,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<World> 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;
Expand Down Expand Up @@ -1428,6 +1438,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);
}
Expand Down
1 change: 1 addition & 0 deletions editor/icons/icon_l_o_d.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
16 changes: 16 additions & 0 deletions editor/plugins/spatial_editor_plugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,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"
Expand Down Expand Up @@ -2735,7 +2736,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) {
Expand Down Expand Up @@ -5503,6 +5509,14 @@ void SpatialEditor::_menu_item_pressed(int p_option) {
VisualServer::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));
Expand Down Expand Up @@ -7076,13 +7090,15 @@ 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);

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");

Expand Down
1 change: 1 addition & 0 deletions editor/plugins/spatial_editor_plugin.h
Original file line number Diff line number Diff line change
Expand Up @@ -673,6 +673,7 @@ class SpatialEditor : public VBoxContainer {
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_LOCK_SELECTED,
Expand Down
13 changes: 9 additions & 4 deletions editor/scene_tree_editor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
#include "editor/node_dock.h"
#include "editor/plugins/animation_player_editor_plugin.h"
#include "editor/plugins/canvas_item_editor_plugin.h"
#include "scene/3d/lod_manager.h"
#include "scene/gui/label.h"
#include "scene/main/viewport.h"
#include "scene/resources/packed_scene.h"
Expand Down Expand Up @@ -162,7 +163,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;
}
Expand Down Expand Up @@ -405,9 +406,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_icon("GuiVisibilityVisible", "EditorIcons"), BUTTON_VISIBILITY, false, TTR("Toggle Visibility"));
item->add_button(0, get_icon("GuiVisibilityVisible", "EditorIcons"), BUTTON_VISIBILITY, p_disable_visibility, TTR("Toggle Visibility"));
} else {
item->add_button(0, get_icon("GuiVisibilityHidden", "EditorIcons"), BUTTON_VISIBILITY, false, TTR("Toggle Visibility"));
item->add_button(0, get_icon("GuiVisibilityHidden", "EditorIcons"), BUTTON_VISIBILITY, p_disable_visibility, TTR("Toggle Visibility"));
}

if (!p_node->is_connected("visibility_changed", this, "_node_visibility_changed")) {
Expand Down Expand Up @@ -437,8 +438,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()) {
Expand Down
2 changes: 1 addition & 1 deletion editor/scene_tree_editor.h
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,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);
Expand Down
3 changes: 3 additions & 0 deletions scene/3d/camera.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -504,6 +504,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);
Expand All @@ -523,6 +525,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);
Expand Down
4 changes: 4 additions & 0 deletions scene/3d/camera.h
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ class Camera : public Spatial {

DopplerTracking doppler_tracking;
Ref<SpatialVelocityTracker> velocity_tracker;
bool affect_lod = true;

protected:
void _update_camera();
Expand Down Expand Up @@ -170,6 +171,9 @@ class Camera : public Spatial {

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();
};
Expand Down
Loading

0 comments on commit ae7dfd1

Please sign in to comment.