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

Implement automatic LOD (Level of Detail) #44468

Merged
merged 1 commit into from
Dec 18, 2020
Merged
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
6 changes: 3 additions & 3 deletions core/math/aabb.h
Original file line number Diff line number Diff line change
Expand Up @@ -190,9 +190,9 @@ Vector3 AABB::get_support(const Vector3 &p_normal) const {
Vector3 ofs = position + half_extents;

return Vector3(
(p_normal.x > 0) ? -half_extents.x : half_extents.x,
(p_normal.y > 0) ? -half_extents.y : half_extents.y,
(p_normal.z > 0) ? -half_extents.z : half_extents.z) +
(p_normal.x > 0) ? half_extents.x : -half_extents.x,
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this a bugfix? is this worth backporting?

Copy link
Member Author

Choose a reason for hiding this comment

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

Ah I am not sure if this function exists in 3.x, (I have the feeling I added it recently but I may be wrong) but if it does probably should be.

Copy link
Member

Choose a reason for hiding this comment

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

This does exist in 3.2, but backporting this would break compatibility.

(p_normal.y > 0) ? half_extents.y : -half_extents.y,
(p_normal.z > 0) ? half_extents.z : -half_extents.z) +
ofs;
}

Expand Down
11 changes: 11 additions & 0 deletions core/math/camera_matrix.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -655,6 +655,17 @@ real_t CameraMatrix::get_fov() const {
}
}

float CameraMatrix::get_lod_multiplier() const {
if (is_orthogonal()) {
return get_viewport_half_extents().x;
} else {
float zn = get_z_near();
float width = get_viewport_half_extents().x * 2.0;
return 1.0 / (zn / width);
}

//usage is lod_size / (lod_distance * multiplier) < threshold
}
void CameraMatrix::make_scale(const Vector3 &p_scale) {
set_identity();
matrix[0][0] = p_scale.x;
Expand Down
2 changes: 2 additions & 0 deletions core/math/camera_matrix.h
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,8 @@ struct CameraMatrix {
return !(*this == p_cam);
}

float get_lod_multiplier() const;

CameraMatrix();
CameraMatrix(const Transform &p_transform);
~CameraMatrix();
Expand Down
3 changes: 3 additions & 0 deletions editor/editor_node.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -525,6 +525,9 @@ void EditorNode::_notification(int p_what) {
scene_root->set_sdf_oversize(sdf_oversize);
Viewport::SDFScale sdf_scale = Viewport::SDFScale(int(GLOBAL_GET("rendering/quality/2d_sdf/scale")));
scene_root->set_sdf_scale(sdf_scale);

float lod_threshold = GLOBAL_GET("rendering/quality/mesh_lod/threshold_pixels");
scene_root->set_lod_threshold(lod_threshold);
}

ResourceImporterTexture::get_singleton()->update_imports();
Expand Down
71 changes: 63 additions & 8 deletions editor/import/resource_importer_scene.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
#include "scene/resources/ray_shape_3d.h"
#include "scene/resources/resource_format_text.h"
#include "scene/resources/sphere_shape_3d.h"
#include "scene/resources/surface_tool.h"
#include "scene/resources/world_margin_shape_3d.h"

uint32_t EditorSceneImporter::get_import_flags() const {
Expand Down Expand Up @@ -217,6 +218,59 @@ Ref<Material> EditorSceneImporterMesh::get_surface_material(int p_surface) const
return surfaces[p_surface].material;
}

void EditorSceneImporterMesh::generate_lods() {
if (!SurfaceTool::simplify_func) {
return;
}

for (int i = 0; i < surfaces.size(); i++) {
if (surfaces[i].primitive != Mesh::PRIMITIVE_TRIANGLES) {
continue;
}

surfaces.write[i].lods.clear();
Vector<Vector3> vertices = surfaces[i].arrays[RS::ARRAY_VERTEX];
Vector<int> indices = surfaces[i].arrays[RS::ARRAY_INDEX];
if (indices.size() == 0) {
continue; //no lods if no indices
}
uint32_t vertex_count = vertices.size();
const Vector3 *vertices_ptr = vertices.ptr();
AABB aabb;
{
for (uint32_t j = 0; j < vertex_count; j++) {
if (j == 0) {
aabb.position = vertices_ptr[j];
} else {
aabb.expand_to(vertices_ptr[j]);
}
}
}

float longest_axis_size = aabb.get_longest_axis_size();

int min_indices = 10;
int index_target = indices.size() / 2;
print_line("total: " + itos(indices.size()));
while (index_target > min_indices) {
float error;
Vector<int> new_indices;
new_indices.resize(indices.size());
size_t new_len = SurfaceTool::simplify_func((unsigned int *)new_indices.ptrw(), (const unsigned int *)indices.ptr(), indices.size(), (const float *)vertices_ptr, vertex_count, sizeof(Vector3), index_target, 1e20, &error);
print_line("shoot for " + itos(index_target) + ", got " + itos(new_len) + " distance " + rtos(error));
if ((int)new_len > (index_target * 120 / 100)) {
break; // 20 percent tolerance
}
new_indices.resize(new_len);
Surface::LOD lod;
lod.distance = error * longest_axis_size;
lod.indices = new_indices;
surfaces.write[i].lods.push_back(lod);
index_target /= 2;
}
}
}

bool EditorSceneImporterMesh::has_mesh() const {
return mesh.is_valid();
}
Expand Down Expand Up @@ -1422,9 +1476,9 @@ void ResourceImporterScene::get_import_options(List<ImportOption> *r_options, in
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "materials/location", PROPERTY_HINT_ENUM, "Node,Mesh"), (meshes_out || materials_out) ? 1 : 0));
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "materials/storage", PROPERTY_HINT_ENUM, "Built-In,Files (.material),Files (.tres)", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), materials_out ? 1 : 0));
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "materials/keep_on_reimport"), materials_out));
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "meshes/compress"), true));
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "meshes/ensure_tangents"), true));
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "meshes/storage", PROPERTY_HINT_ENUM, "Built-In,Files (.mesh),Files (.tres)"), meshes_out ? 1 : 0));
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "meshes/generate_lods"), true));
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "meshes/light_baking", PROPERTY_HINT_ENUM, "Disabled,Enable,Gen Lightmaps", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), 0));
r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "meshes/lightmap_texel_size", PROPERTY_HINT_RANGE, "0.001,100,0.001"), 0.1));
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "skins/use_named_skins"), true));
Expand Down Expand Up @@ -1517,7 +1571,7 @@ Ref<Animation> ResourceImporterScene::import_animation_from_other_importer(Edito
return importer->import_animation(p_path, p_flags, p_bake_fps);
}

void ResourceImporterScene::_generate_meshes(Node *p_node) {
void ResourceImporterScene::_generate_meshes(Node *p_node, bool p_generate_lods) {
EditorSceneImporterMeshNode *src_mesh = Object::cast_to<EditorSceneImporterMeshNode>(p_node);
if (src_mesh != nullptr) {
//is mesh
Expand All @@ -1528,6 +1582,9 @@ void ResourceImporterScene::_generate_meshes(Node *p_node) {

Ref<ArrayMesh> mesh;
if (!src_mesh->get_mesh()->has_mesh()) {
if (p_generate_lods) {
src_mesh->get_mesh()->generate_lods();
}
//do mesh processing
}
mesh = src_mesh->get_mesh()->get_mesh();
Expand All @@ -1542,7 +1599,7 @@ void ResourceImporterScene::_generate_meshes(Node *p_node) {
}

for (int i = 0; i < p_node->get_child_count(); i++) {
_generate_meshes(p_node->get_child(i));
_generate_meshes(p_node->get_child(i), p_generate_lods);
}
}
Error ResourceImporterScene::import(const String &p_source_file, const String &p_save_path, const Map<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) {
Expand Down Expand Up @@ -1583,10 +1640,6 @@ Error ResourceImporterScene::import(const String &p_source_file, const String &p
import_flags |= EditorSceneImporter::IMPORT_ANIMATION;
}

if (int(p_options["meshes/compress"])) {
import_flags |= EditorSceneImporter::IMPORT_USE_COMPRESSION;
}

if (bool(p_options["meshes/ensure_tangents"])) {
import_flags |= EditorSceneImporter::IMPORT_GENERATE_TANGENT_ARRAYS;
}
Expand Down Expand Up @@ -1641,7 +1694,9 @@ Error ResourceImporterScene::import(const String &p_source_file, const String &p
scene->set_name(p_save_path.get_file().get_basename());
}

_generate_meshes(scene);
bool gen_lods = bool(p_options["meshes/generate_lods"]);

_generate_meshes(scene, gen_lods);

err = OK;

Expand Down
4 changes: 3 additions & 1 deletion editor/import/resource_importer_scene.h
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,8 @@ class EditorSceneImporterMesh : public Resource {
float get_surface_lod_size(int p_surface, int p_lod) const;
Ref<Material> get_surface_material(int p_surface) const;

void generate_lods();

bool has_mesh() const;
Ref<ArrayMesh> get_mesh();
void clear();
Expand Down Expand Up @@ -205,7 +207,7 @@ class ResourceImporterScene : public ResourceImporter {
};

void _replace_owner(Node *p_node, Node *p_scene, Node *p_new_owner);
void _generate_meshes(Node *p_node);
void _generate_meshes(Node *p_node, bool p_generate_lods);

public:
static ResourceImporterScene *get_singleton() { return singleton; }
Expand Down
7 changes: 6 additions & 1 deletion editor/plugins/node_3d_editor_plugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3017,7 +3017,8 @@ void Node3DEditorViewport::_menu_option(int p_option) {
case VIEW_DISPLAY_DEBUG_DECAL_ATLAS:
case VIEW_DISPLAY_DEBUG_SDFGI:
case VIEW_DISPLAY_DEBUG_SDFGI_PROBES:
case VIEW_DISPLAY_DEBUG_GI_BUFFER: {
case VIEW_DISPLAY_DEBUG_GI_BUFFER:
case VIEW_DISPLAY_DEBUG_DISABLE_LOD: {
static const int display_options[] = {
VIEW_DISPLAY_NORMAL,
VIEW_DISPLAY_WIREFRAME,
Expand All @@ -3034,6 +3035,7 @@ void Node3DEditorViewport::_menu_option(int p_option) {
VIEW_DISPLAY_DEBUG_SCENE_LUMINANCE,
VIEW_DISPLAY_DEBUG_SSAO,
VIEW_DISPLAY_DEBUG_GI_BUFFER,
VIEW_DISPLAY_DEBUG_DISABLE_LOD,
VIEW_DISPLAY_DEBUG_PSSM_SPLITS,
VIEW_DISPLAY_DEBUG_DECAL_ATLAS,
VIEW_DISPLAY_DEBUG_SDFGI,
Expand All @@ -3056,6 +3058,7 @@ void Node3DEditorViewport::_menu_option(int p_option) {
Viewport::DEBUG_DRAW_SCENE_LUMINANCE,
Viewport::DEBUG_DRAW_SSAO,
Viewport::DEBUG_DRAW_GI_BUFFER,
Viewport::DEBUG_DRAW_DISABLE_LOD,
Viewport::DEBUG_DRAW_PSSM_SPLITS,
Viewport::DEBUG_DRAW_DECAL_ATLAS,
Viewport::DEBUG_DRAW_SDFGI,
Expand Down Expand Up @@ -3959,6 +3962,8 @@ Node3DEditorViewport::Node3DEditorViewport(Node3DEditor *p_spatial_editor, Edito
display_submenu->add_radio_check_item(TTR("SSAO"), VIEW_DISPLAY_DEBUG_SSAO);
display_submenu->add_separator();
display_submenu->add_radio_check_item(TTR("GI Buffer"), VIEW_DISPLAY_DEBUG_GI_BUFFER);
display_submenu->add_separator();
display_submenu->add_radio_check_item(TTR("Disable LOD"), VIEW_DISPLAY_DEBUG_DISABLE_LOD);
display_submenu->set_name("display_advanced");
view_menu->get_popup()->add_submenu_item(TTR("Display Advanced..."), "display_advanced", VIEW_DISPLAY_ADVANCED);
view_menu->get_popup()->add_separator();
Expand Down
1 change: 1 addition & 0 deletions editor/plugins/node_3d_editor_plugin.h
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,7 @@ class Node3DEditorViewport : public Control {
VIEW_DISPLAY_DEBUG_SDFGI,
VIEW_DISPLAY_DEBUG_SDFGI_PROBES,
VIEW_DISPLAY_DEBUG_GI_BUFFER,
VIEW_DISPLAY_DEBUG_DISABLE_LOD,
VIEW_LOCK_ROTATION,
VIEW_CINEMATIC_PREVIEW,
VIEW_AUTO_ORTHOGONAL,
Expand Down
14 changes: 14 additions & 0 deletions scene/3d/reflection_probe.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,15 @@ float ReflectionProbe::get_max_distance() const {
return max_distance;
}

void ReflectionProbe::set_lod_threshold(float p_pixels) {
lod_threshold = p_pixels;
RS::get_singleton()->reflection_probe_set_lod_threshold(probe, p_pixels);
}

float ReflectionProbe::get_lod_threshold() const {
return lod_threshold;
}

void ReflectionProbe::set_extents(const Vector3 &p_extents) {
extents = p_extents;

Expand Down Expand Up @@ -199,6 +208,9 @@ void ReflectionProbe::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_max_distance", "max_distance"), &ReflectionProbe::set_max_distance);
ClassDB::bind_method(D_METHOD("get_max_distance"), &ReflectionProbe::get_max_distance);

ClassDB::bind_method(D_METHOD("set_lod_threshold", "ratio"), &ReflectionProbe::set_lod_threshold);
ClassDB::bind_method(D_METHOD("get_lod_threshold"), &ReflectionProbe::get_lod_threshold);

ClassDB::bind_method(D_METHOD("set_extents", "extents"), &ReflectionProbe::set_extents);
ClassDB::bind_method(D_METHOD("get_extents"), &ReflectionProbe::get_extents);

Expand Down Expand Up @@ -229,6 +241,7 @@ void ReflectionProbe::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "interior"), "set_as_interior", "is_set_as_interior");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "enable_shadows"), "set_enable_shadows", "are_shadows_enabled");
ADD_PROPERTY(PropertyInfo(Variant::INT, "cull_mask", PROPERTY_HINT_LAYERS_3D_RENDER), "set_cull_mask", "get_cull_mask");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "lod_threshold", PROPERTY_HINT_RANGE, "0,1024,0.1"), "set_lod_threshold", "get_lod_threshold");

ADD_GROUP("Ambient", "ambient_");
ADD_PROPERTY(PropertyInfo(Variant::INT, "ambient_mode", PROPERTY_HINT_ENUM, "Disabled,Environment,ConstantColor"), "set_ambient_mode", "get_ambient_mode");
Expand Down Expand Up @@ -256,6 +269,7 @@ ReflectionProbe::ReflectionProbe() {
enable_shadows = false;
cull_mask = (1 << 20) - 1;
update_mode = UPDATE_ONCE;
lod_threshold = 1.0;

probe = RenderingServer::get_singleton()->reflection_probe_create();
RS::get_singleton()->instance_set_base(get_instance(), probe);
Expand Down
4 changes: 4 additions & 0 deletions scene/3d/reflection_probe.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ class ReflectionProbe : public VisualInstance3D {
AmbientMode ambient_mode;
Color ambient_color;
float ambient_color_energy;
float lod_threshold;

uint32_t cull_mask;
UpdateMode update_mode;
Expand Down Expand Up @@ -90,6 +91,9 @@ class ReflectionProbe : public VisualInstance3D {
void set_max_distance(float p_distance);
float get_max_distance() const;

void set_lod_threshold(float p_pixels);
float get_lod_threshold() const;

void set_extents(const Vector3 &p_extents);
Vector3 get_extents() const;

Expand Down
16 changes: 16 additions & 0 deletions scene/3d/visual_instance_3d.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,16 @@ float GeometryInstance3D::get_extra_cull_margin() const {
return extra_cull_margin;
}

void GeometryInstance3D::set_lod_bias(float p_bias) {
ERR_FAIL_COND(p_bias < 0.0);
lod_bias = p_bias;
RS::get_singleton()->instance_geometry_set_lod_bias(get_instance(), lod_bias);
}

float GeometryInstance3D::get_lod_bias() const {
return lod_bias;
}

void GeometryInstance3D::set_shader_instance_uniform(const StringName &p_uniform, const Variant &p_value) {
if (p_value.get_type() == Variant::NIL) {
Variant def_value = RS::get_singleton()->instance_geometry_get_shader_parameter_default_value(get_instance(), p_uniform);
Expand Down Expand Up @@ -361,6 +371,9 @@ void GeometryInstance3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_gi_mode", "mode"), &GeometryInstance3D::set_gi_mode);
ClassDB::bind_method(D_METHOD("get_gi_mode"), &GeometryInstance3D::get_gi_mode);

ClassDB::bind_method(D_METHOD("set_lod_bias", "p_bias"), &GeometryInstance3D::set_lod_bias);
ClassDB::bind_method(D_METHOD("get_lod_bias"), &GeometryInstance3D::get_lod_bias);

ClassDB::bind_method(D_METHOD("set_custom_aabb", "aabb"), &GeometryInstance3D::set_custom_aabb);

ClassDB::bind_method(D_METHOD("get_aabb"), &GeometryInstance3D::get_aabb);
Expand All @@ -369,6 +382,7 @@ void GeometryInstance3D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "material_override", PROPERTY_HINT_RESOURCE_TYPE, "ShaderMaterial,StandardMaterial3D", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_DEFERRED_SET_RESOURCE), "set_material_override", "get_material_override");
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::FLOAT, "extra_cull_margin", PROPERTY_HINT_RANGE, "0,16384,0.01"), "set_extra_cull_margin", "get_extra_cull_margin");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "lod_bias", PROPERTY_HINT_RANGE, "0.001,128,0.001"), "set_lod_bias", "get_lod_bias");
ADD_GROUP("Global Illumination", "gi_");
ADD_PROPERTY(PropertyInfo(Variant::INT, "gi_mode", PROPERTY_HINT_ENUM, "Disabled,Baked,Dynamic"), "set_gi_mode", "get_gi_mode");
ADD_PROPERTY(PropertyInfo(Variant::INT, "gi_lightmap_scale", PROPERTY_HINT_ENUM, "1x,2x,4x,8x"), "set_lightmap_scale", "get_lightmap_scale");
Expand Down Expand Up @@ -403,6 +417,8 @@ GeometryInstance3D::GeometryInstance3D() {
lod_min_hysteresis = 0;
lod_max_hysteresis = 0;

lod_bias = 1.0;

gi_mode = GI_MODE_DISABLED;
lightmap_scale = LIGHTMAP_SCALE_1X;

Expand Down
5 changes: 5 additions & 0 deletions scene/3d/visual_instance_3d.h
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,8 @@ class GeometryInstance3D : public VisualInstance3D {
float lod_min_hysteresis;
float lod_max_hysteresis;

float lod_bias;

mutable HashMap<StringName, Variant> instance_uniforms;
mutable HashMap<StringName, StringName> instance_uniform_property_remap;

Expand Down Expand Up @@ -151,6 +153,9 @@ class GeometryInstance3D : public VisualInstance3D {
void set_extra_cull_margin(float p_margin);
float get_extra_cull_margin() const;

void set_lod_bias(float p_bias);
float get_lod_bias() const;

void set_gi_mode(GIMode p_mode);
GIMode get_gi_mode() const;

Expand Down
4 changes: 4 additions & 0 deletions scene/main/scene_tree.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1391,6 +1391,10 @@ SceneTree::SceneTree() {
const bool use_debanding = GLOBAL_DEF("rendering/quality/screen_filters/use_debanding", false);
root->set_use_debanding(use_debanding);

float lod_threshold = GLOBAL_DEF("rendering/quality/mesh_lod/threshold_pixels", 1.0);
ProjectSettings::get_singleton()->set_custom_property_info("rendering/quality/mesh_lod/threshold_pixels", PropertyInfo(Variant::FLOAT, "rendering/quality/mesh_lod/threshold_pixels", PROPERTY_HINT_RANGE, "0,1024,0.1"));
root->set_lod_threshold(lod_threshold);

bool snap_2d_transforms = GLOBAL_DEF("rendering/quality/2d/snap_2d_transforms_to_pixel", false);
root->set_snap_2d_transforms_to_pixel(snap_2d_transforms);

Expand Down
Loading