Skip to content

Commit

Permalink
Merge pull request #44468 from reduz/implement-lod
Browse files Browse the repository at this point in the history
Implement automatic LOD (Level of Detail)
  • Loading branch information
akien-mga authored Dec 18, 2020
2 parents 36b4e03 + d2302f5 commit 7ad29ed
Show file tree
Hide file tree
Showing 38 changed files with 513 additions and 109 deletions.
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,
(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

0 comments on commit 7ad29ed

Please sign in to comment.