Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[3.x] Implement LOD range in GeometryInstance #53778

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 6 additions & 8 deletions doc/classes/GeometryInstance.xml
Original file line number Diff line number Diff line change
Expand Up @@ -46,20 +46,18 @@
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.
The GeometryInstance's maximum level of detail (LOD) distance. The [GeometryInstance] will not be drawn if the camera is further away than [member lod_max_distance] units from the [GeometryInstance]'s origin. Use this to improve performance by hiding [GeometryInstance]s when they don't contribute much to the scene's visual output. See also [member lod_max_hysteresis].
[b]Note:[/b] LOD is currently not applied to shadow rendering.
</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.
The [GeometryInstance]'s maximum level of detail (LOD) margin. Margins are symmetrical around [member lod_max_distance]. This can be set to a value greater than [code]0.0[/code] to prevent LOD levels from "flip-flopping" when the camera moves back and forth (at the cost of less effective LOD levels being chosen at a given distance). See also [member lod_max_distance].
</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.
The GeometryInstance's minimum level of detail (LOD) distance. The [GeometryInstance] will not be drawn if the camera is closer than [member lod_max_distance] units from the [GeometryInstance]'s origin. Use this to improve performance by hiding [GeometryInstance]s when they don't contribute much to the scene's visual output.
[b]Note:[/b] LOD is currently not applied to shadow rendering. See also [member lod_min_hysteresis].
</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.
The [GeometryInstance]'s minimum level of detail (LOD) margin. Margins are symmetrical around [member lod_max_distance]. This can be set to a value greater than [code]0.0[/code] to prevent LOD levels from "flip-flopping" when the camera moves back and forth (at the cost of less effective LOD levels being chosen at a given distance). See also [member lod_min_distance].
</member>
<member name="material_overlay" type="Material" setter="set_material_overlay" getter="get_material_overlay">
The material overlay for the whole geometry.
Expand Down
2 changes: 1 addition & 1 deletion doc/classes/VisualServer.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1364,7 +1364,7 @@
<argument index="3" name="min_margin" type="float" />
<argument index="4" name="max_margin" type="float" />
<description>
Not implemented in Godot 3.x.
Sets the level of detail (LOD) thresholds on the specified [code]instance[/code]. See also [member GeometryInstance.lod_min_distance], [member GeometryInstance.lod_max_distance], [member GeometryInstance.lod_min_hysteresis] and [member GeometryInstance.lod_max_hysteresis].
</description>
</method>
<method name="instance_geometry_set_flag">
Expand Down
62 changes: 57 additions & 5 deletions servers/visual/visual_server_scene.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1978,6 +1978,13 @@ void VisualServerScene::instance_geometry_set_material_overlay(RID p_instance, R
}

void VisualServerScene::instance_geometry_set_draw_range(RID p_instance, float p_min, float p_max, float p_min_margin, float p_max_margin) {
Calinou marked this conversation as resolved.
Show resolved Hide resolved
Instance *instance = instance_owner.get(p_instance);
ERR_FAIL_COND(!instance);

instance->lod_begin = p_min;
instance->lod_end = p_max;
instance->lod_begin_hysteresis = p_min_margin;
instance->lod_end_hysteresis = p_max_margin;
}
void VisualServerScene::instance_geometry_set_as_instance_lod(RID p_instance, RID p_as_lod_of_instance) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This one should be removed, nothing ever calls it.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That method is exposed to the scripting API – wouldn't removing it technically break compatibility?

}
Expand Down Expand Up @@ -2847,7 +2854,7 @@ void VisualServerScene::render_camera(RID p_camera, RID p_scenario, Size2 p_view

Transform camera_transform = _interpolation_data.interpolation_enabled ? camera->get_transform_interpolated() : camera->transform;

_prepare_scene(camera_transform, camera_matrix, ortho, camera->env, camera->visible_layers, p_scenario, p_shadow_atlas, RID(), camera->previous_room_id_hint);
_prepare_scene(camera_transform, camera_matrix, ortho, camera->env, camera->visible_layers, p_scenario, p_shadow_atlas, RID(), camera->previous_room_id_hint, &camera->lod_hysteresis_visible_state);
_render_scene(camera_transform, camera_matrix, 0, ortho, camera->env, p_scenario, p_shadow_atlas, RID(), -1);
#endif
}
Expand Down Expand Up @@ -2926,17 +2933,17 @@ void VisualServerScene::render_camera(Ref<ARVRInterface> &p_interface, ARVRInter
mono_transform *= apply_z_shift;

// now prepare our scene with our adjusted transform projection matrix
_prepare_scene(mono_transform, combined_matrix, false, camera->env, camera->visible_layers, p_scenario, p_shadow_atlas, RID(), camera->previous_room_id_hint);
_prepare_scene(mono_transform, combined_matrix, false, camera->env, camera->visible_layers, p_scenario, p_shadow_atlas, RID(), camera->previous_room_id_hint, &camera->lod_hysteresis_visible_state);
} else if (p_eye == ARVRInterface::EYE_MONO) {
// For mono render, prepare as per usual
_prepare_scene(cam_transform, camera_matrix, false, camera->env, camera->visible_layers, p_scenario, p_shadow_atlas, RID(), camera->previous_room_id_hint);
_prepare_scene(cam_transform, camera_matrix, false, camera->env, camera->visible_layers, p_scenario, p_shadow_atlas, RID(), camera->previous_room_id_hint, &camera->lod_hysteresis_visible_state);
}

// And render our scene...
_render_scene(cam_transform, camera_matrix, p_eye, false, camera->env, p_scenario, p_shadow_atlas, RID(), -1);
};

void VisualServerScene::_prepare_scene(const Transform p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_orthogonal, RID p_force_environment, uint32_t p_visible_layers, RID p_scenario, RID p_shadow_atlas, RID p_reflection_probe, int32_t &r_previous_room_id_hint) {
void VisualServerScene::_prepare_scene(const Transform p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_orthogonal, RID p_force_environment, uint32_t p_visible_layers, RID p_scenario, RID p_shadow_atlas, RID p_reflection_probe, int32_t &r_previous_room_id_hint, OAHashMap<uint32_t, bool> *lod_visible_state) {
// Note, in stereo rendering:
// - p_cam_transform will be a transform in the middle of our two eyes
// - p_cam_projection is a wider frustrum that encompasses both eyes
Expand Down Expand Up @@ -3035,6 +3042,50 @@ void VisualServerScene::_prepare_scene(const Transform p_cam_transform, const Ca

InstanceGeometryData *geom = static_cast<InstanceGeometryData *>(ins->base_data);

// Calculate instance->depth from the camera.
const Vector3 aabb_center = ins->transformed_aabb.position + (ins->transformed_aabb.size * 0.5);
if (p_cam_orthogonal) {
ins->depth = near_plane.distance_to(aabb_center);
} else {
ins->depth = p_cam_transform.origin.distance_to(aabb_center);
}

Calinou marked this conversation as resolved.
Show resolved Hide resolved
// If LOD is active, and the instance is not within its LOD range, don't render it.
if (ins->lod_begin > CMP_EPSILON || ins->lod_end > CMP_EPSILON) { // LOD valid
bool prev_lod_state = false;
if (lod_visible_state != nullptr) {
lod_visible_state->lookup(ins->self.get_id(), prev_lod_state);
}

float lod_begin_with_hys = ins->lod_begin;
float lod_end_with_hys = ins->lod_end;
if (prev_lod_state) {
lod_begin_with_hys -= ins->lod_begin_hysteresis / 2.f;
lod_end_with_hys += ins->lod_end_hysteresis / 2.f;
} else {
lod_begin_with_hys += ins->lod_begin_hysteresis / 2.f;
lod_end_with_hys -= ins->lod_end_hysteresis / 2.f;
}

if (ins->lod_begin < CMP_EPSILON) {
lod_begin_with_hys = -Math_INF;
}
if (ins->lod_end < CMP_EPSILON) {
lod_end_with_hys = +Math_INF;
}

if (lod_begin_with_hys <= ins->depth && ins->depth < lod_end_with_hys) {
if (lod_visible_state != nullptr) {
lod_visible_state->set(ins->self.get_id(), true);
}
} else {
if (lod_visible_state != nullptr) {
lod_visible_state->set(ins->self.get_id(), false);
}
keep = false;
}
}

if (ins->redraw_if_visible) {
VisualServerRaster::redraw_request(false);
}
Expand Down Expand Up @@ -3349,7 +3400,8 @@ bool VisualServerScene::_render_reflection_probe_step(Instance *p_instance, int
shadow_atlas = scenario->reflection_probe_shadow_atlas;
}

_prepare_scene(xform, cm, false, RID(), VSG::storage->reflection_probe_get_cull_mask(p_instance->base), p_instance->scenario->self, shadow_atlas, reflection_probe->instance, reflection_probe->previous_room_id_hint);
// No LOD hysteresis handling for ReflectionProbes.
_prepare_scene(xform, cm, false, RID(), VSG::storage->reflection_probe_get_cull_mask(p_instance->base), p_instance->scenario->self, shadow_atlas, reflection_probe->instance, reflection_probe->previous_room_id_hint, nullptr);

bool async_forbidden_backup = VSG::storage->is_shader_async_hidden_forbidden();
VSG::storage->set_shader_async_hidden_forbidden(true);
Expand Down
4 changes: 3 additions & 1 deletion servers/visual/visual_server_scene.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
#include "core/math/bvh.h"
#include "core/math/geometry.h"
#include "core/math/octree.h"
#include "core/oa_hash_map.h"
#include "core/os/semaphore.h"
#include "core/os/thread.h"
#include "core/safe_refcount.h"
Expand Down Expand Up @@ -89,6 +90,7 @@ class VisualServerScene {
bool vaspect : 1;
TransformInterpolator::Method interpolation_method : 3;

OAHashMap<uint32_t, bool> lod_hysteresis_visible_state;
int32_t previous_room_id_hint;

Transform get_transform_interpolated() const;
Expand Down Expand Up @@ -835,7 +837,7 @@ class VisualServerScene {

_FORCE_INLINE_ bool _light_instance_update_shadow(Instance *p_instance, const Transform p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_orthogonal, RID p_shadow_atlas, Scenario *p_scenario);

void _prepare_scene(const Transform p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_orthogonal, RID p_force_environment, uint32_t p_visible_layers, RID p_scenario, RID p_shadow_atlas, RID p_reflection_probe, int32_t &r_previous_room_id_hint);
void _prepare_scene(const Transform p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_orthogonal, RID p_force_environment, uint32_t p_visible_layers, RID p_scenario, RID p_shadow_atlas, RID p_reflection_probe, int32_t &r_previous_room_id_hint, OAHashMap<uint32_t, bool> *lod_visible_state);
void _render_scene(const Transform p_cam_transform, const CameraMatrix &p_cam_projection, const int p_eye, bool p_cam_orthogonal, RID p_force_environment, RID p_scenario, RID p_shadow_atlas, RID p_reflection_probe, int p_reflection_probe_pass);
void render_empty_scene(RID p_scenario, RID p_shadow_atlas);

Expand Down