/**************************************************************************/ /* light_storage.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. */ /**************************************************************************/ #ifdef GLES3_ENABLED #include "light_storage.h" #include "../rasterizer_gles3.h" #include "../rasterizer_scene_gles3.h" #include "config.h" #include "texture_storage.h" using namespace GLES3; LightStorage *LightStorage::singleton = nullptr; LightStorage *LightStorage::get_singleton() { return singleton; } LightStorage::LightStorage() { singleton = this; } LightStorage::~LightStorage() { singleton = nullptr; } /* Light API */ void LightStorage::_light_initialize(RID p_light, RS::LightType p_type) { Light light; light.type = p_type; light.param[RS::LIGHT_PARAM_ENERGY] = 1.0; light.param[RS::LIGHT_PARAM_INDIRECT_ENERGY] = 1.0; light.param[RS::LIGHT_PARAM_VOLUMETRIC_FOG_ENERGY] = 1.0; light.param[RS::LIGHT_PARAM_SPECULAR] = 0.5; light.param[RS::LIGHT_PARAM_RANGE] = 1.0; light.param[RS::LIGHT_PARAM_SIZE] = 0.0; light.param[RS::LIGHT_PARAM_ATTENUATION] = 1.0; light.param[RS::LIGHT_PARAM_SPOT_ANGLE] = 45; light.param[RS::LIGHT_PARAM_SPOT_ATTENUATION] = 1.0; light.param[RS::LIGHT_PARAM_SHADOW_MAX_DISTANCE] = 0; light.param[RS::LIGHT_PARAM_SHADOW_SPLIT_1_OFFSET] = 0.1; light.param[RS::LIGHT_PARAM_SHADOW_SPLIT_2_OFFSET] = 0.3; light.param[RS::LIGHT_PARAM_SHADOW_SPLIT_3_OFFSET] = 0.6; light.param[RS::LIGHT_PARAM_SHADOW_FADE_START] = 0.8; light.param[RS::LIGHT_PARAM_SHADOW_NORMAL_BIAS] = 1.0; light.param[RS::LIGHT_PARAM_SHADOW_OPACITY] = 1.0; light.param[RS::LIGHT_PARAM_SHADOW_BIAS] = 0.02; light.param[RS::LIGHT_PARAM_SHADOW_BLUR] = 0; light.param[RS::LIGHT_PARAM_SHADOW_PANCAKE_SIZE] = 20.0; light.param[RS::LIGHT_PARAM_TRANSMITTANCE_BIAS] = 0.05; light.param[RS::LIGHT_PARAM_INTENSITY] = p_type == RS::LIGHT_DIRECTIONAL ? 100000.0 : 1000.0; light_owner.initialize_rid(p_light, light); } RID LightStorage::directional_light_allocate() { return light_owner.allocate_rid(); } void LightStorage::directional_light_initialize(RID p_rid) { _light_initialize(p_rid, RS::LIGHT_DIRECTIONAL); } RID LightStorage::omni_light_allocate() { return light_owner.allocate_rid(); } void LightStorage::omni_light_initialize(RID p_rid) { _light_initialize(p_rid, RS::LIGHT_OMNI); } RID LightStorage::spot_light_allocate() { return light_owner.allocate_rid(); } void LightStorage::spot_light_initialize(RID p_rid) { _light_initialize(p_rid, RS::LIGHT_SPOT); } void LightStorage::light_free(RID p_rid) { light_set_projector(p_rid, RID()); //clear projector // delete the texture Light *light = light_owner.get_or_null(p_rid); light->dependency.deleted_notify(p_rid); light_owner.free(p_rid); } void LightStorage::light_set_color(RID p_light, const Color &p_color) { Light *light = light_owner.get_or_null(p_light); ERR_FAIL_NULL(light); light->color = p_color; } void LightStorage::light_set_param(RID p_light, RS::LightParam p_param, float p_value) { Light *light = light_owner.get_or_null(p_light); ERR_FAIL_NULL(light); ERR_FAIL_INDEX(p_param, RS::LIGHT_PARAM_MAX); if (light->param[p_param] == p_value) { return; } switch (p_param) { case RS::LIGHT_PARAM_RANGE: case RS::LIGHT_PARAM_SPOT_ANGLE: case RS::LIGHT_PARAM_SHADOW_MAX_DISTANCE: case RS::LIGHT_PARAM_SHADOW_SPLIT_1_OFFSET: case RS::LIGHT_PARAM_SHADOW_SPLIT_2_OFFSET: case RS::LIGHT_PARAM_SHADOW_SPLIT_3_OFFSET: case RS::LIGHT_PARAM_SHADOW_NORMAL_BIAS: case RS::LIGHT_PARAM_SHADOW_PANCAKE_SIZE: case RS::LIGHT_PARAM_SHADOW_BIAS: { light->version++; light->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_LIGHT); } break; case RS::LIGHT_PARAM_SIZE: { if ((light->param[p_param] > CMP_EPSILON) != (p_value > CMP_EPSILON)) { //changing from no size to size and the opposite light->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_LIGHT_SOFT_SHADOW_AND_PROJECTOR); } } break; default: { } } light->param[p_param] = p_value; } void LightStorage::light_set_shadow(RID p_light, bool p_enabled) { Light *light = light_owner.get_or_null(p_light); ERR_FAIL_NULL(light); light->shadow = p_enabled; light->version++; light->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_LIGHT); } void LightStorage::light_set_projector(RID p_light, RID p_texture) { GLES3::TextureStorage *texture_storage = GLES3::TextureStorage::get_singleton(); Light *light = light_owner.get_or_null(p_light); ERR_FAIL_NULL(light); if (light->projector == p_texture) { return; } if (light->type != RS::LIGHT_DIRECTIONAL && light->projector.is_valid()) { texture_storage->texture_remove_from_decal_atlas(light->projector, light->type == RS::LIGHT_OMNI); } light->projector = p_texture; if (light->type != RS::LIGHT_DIRECTIONAL) { if (light->projector.is_valid()) { texture_storage->texture_add_to_decal_atlas(light->projector, light->type == RS::LIGHT_OMNI); } light->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_LIGHT_SOFT_SHADOW_AND_PROJECTOR); } } void LightStorage::light_set_negative(RID p_light, bool p_enable) { Light *light = light_owner.get_or_null(p_light); ERR_FAIL_NULL(light); light->negative = p_enable; } void LightStorage::light_set_cull_mask(RID p_light, uint32_t p_mask) { Light *light = light_owner.get_or_null(p_light); ERR_FAIL_NULL(light); light->cull_mask = p_mask; light->version++; light->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_LIGHT); } void LightStorage::light_set_distance_fade(RID p_light, bool p_enabled, float p_begin, float p_shadow, float p_length) { Light *light = light_owner.get_or_null(p_light); ERR_FAIL_NULL(light); light->distance_fade = p_enabled; light->distance_fade_begin = p_begin; light->distance_fade_shadow = p_shadow; light->distance_fade_length = p_length; } void LightStorage::light_set_reverse_cull_face_mode(RID p_light, bool p_enabled) { Light *light = light_owner.get_or_null(p_light); ERR_FAIL_NULL(light); light->reverse_cull = p_enabled; light->version++; light->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_LIGHT); } void LightStorage::light_set_bake_mode(RID p_light, RS::LightBakeMode p_bake_mode) { Light *light = light_owner.get_or_null(p_light); ERR_FAIL_NULL(light); light->bake_mode = p_bake_mode; light->version++; light->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_LIGHT); } void LightStorage::light_omni_set_shadow_mode(RID p_light, RS::LightOmniShadowMode p_mode) { Light *light = light_owner.get_or_null(p_light); ERR_FAIL_NULL(light); light->omni_shadow_mode = p_mode; light->version++; light->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_LIGHT); } RS::LightOmniShadowMode LightStorage::light_omni_get_shadow_mode(RID p_light) { const Light *light = light_owner.get_or_null(p_light); ERR_FAIL_NULL_V(light, RS::LIGHT_OMNI_SHADOW_CUBE); return light->omni_shadow_mode; } void LightStorage::light_directional_set_shadow_mode(RID p_light, RS::LightDirectionalShadowMode p_mode) { Light *light = light_owner.get_or_null(p_light); ERR_FAIL_NULL(light); light->directional_shadow_mode = p_mode; light->version++; light->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_LIGHT); } void LightStorage::light_directional_set_blend_splits(RID p_light, bool p_enable) { Light *light = light_owner.get_or_null(p_light); ERR_FAIL_NULL(light); light->directional_blend_splits = p_enable; light->version++; light->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_LIGHT); } bool LightStorage::light_directional_get_blend_splits(RID p_light) const { const Light *light = light_owner.get_or_null(p_light); ERR_FAIL_NULL_V(light, false); return light->directional_blend_splits; } void LightStorage::light_directional_set_sky_mode(RID p_light, RS::LightDirectionalSkyMode p_mode) { Light *light = light_owner.get_or_null(p_light); ERR_FAIL_NULL(light); light->directional_sky_mode = p_mode; } RS::LightDirectionalSkyMode LightStorage::light_directional_get_sky_mode(RID p_light) const { const Light *light = light_owner.get_or_null(p_light); ERR_FAIL_NULL_V(light, RS::LIGHT_DIRECTIONAL_SKY_MODE_LIGHT_AND_SKY); return light->directional_sky_mode; } RS::LightDirectionalShadowMode LightStorage::light_directional_get_shadow_mode(RID p_light) { const Light *light = light_owner.get_or_null(p_light); ERR_FAIL_NULL_V(light, RS::LIGHT_DIRECTIONAL_SHADOW_ORTHOGONAL); return light->directional_shadow_mode; } RS::LightBakeMode LightStorage::light_get_bake_mode(RID p_light) { const Light *light = light_owner.get_or_null(p_light); ERR_FAIL_NULL_V(light, RS::LIGHT_BAKE_DISABLED); return light->bake_mode; } uint64_t LightStorage::light_get_version(RID p_light) const { const Light *light = light_owner.get_or_null(p_light); ERR_FAIL_NULL_V(light, 0); return light->version; } uint32_t LightStorage::light_get_cull_mask(RID p_light) const { const Light *light = light_owner.get_or_null(p_light); ERR_FAIL_NULL_V(light, 0); return light->cull_mask; } AABB LightStorage::light_get_aabb(RID p_light) const { const Light *light = light_owner.get_or_null(p_light); ERR_FAIL_NULL_V(light, AABB()); switch (light->type) { case RS::LIGHT_SPOT: { float len = light->param[RS::LIGHT_PARAM_RANGE]; float size = Math::tan(Math::deg_to_rad(light->param[RS::LIGHT_PARAM_SPOT_ANGLE])) * len; return AABB(Vector3(-size, -size, -len), Vector3(size * 2, size * 2, len)); }; case RS::LIGHT_OMNI: { float r = light->param[RS::LIGHT_PARAM_RANGE]; return AABB(-Vector3(r, r, r), Vector3(r, r, r) * 2); }; case RS::LIGHT_DIRECTIONAL: { return AABB(); }; } ERR_FAIL_V(AABB()); } /* LIGHT INSTANCE API */ RID LightStorage::light_instance_create(RID p_light) { RID li = light_instance_owner.make_rid(LightInstance()); LightInstance *light_instance = light_instance_owner.get_or_null(li); light_instance->self = li; light_instance->light = p_light; light_instance->light_type = light_get_type(p_light); return li; } void LightStorage::light_instance_free(RID p_light_instance) { LightInstance *light_instance = light_instance_owner.get_or_null(p_light_instance); ERR_FAIL_NULL(light_instance); // Remove from shadow atlases. for (const RID &E : light_instance->shadow_atlases) { ShadowAtlas *shadow_atlas = shadow_atlas_owner.get_or_null(E); ERR_CONTINUE(!shadow_atlas->shadow_owners.has(p_light_instance)); uint32_t key = shadow_atlas->shadow_owners[p_light_instance]; uint32_t q = (key >> QUADRANT_SHIFT) & 0x3; uint32_t s = key & SHADOW_INDEX_MASK; shadow_atlas->quadrants[q].shadows.write[s].owner = RID(); shadow_atlas->shadow_owners.erase(p_light_instance); } light_instance_owner.free(p_light_instance); } void LightStorage::light_instance_set_transform(RID p_light_instance, const Transform3D &p_transform) { LightInstance *light_instance = light_instance_owner.get_or_null(p_light_instance); ERR_FAIL_NULL(light_instance); light_instance->transform = p_transform; } void LightStorage::light_instance_set_aabb(RID p_light_instance, const AABB &p_aabb) { LightInstance *light_instance = light_instance_owner.get_or_null(p_light_instance); ERR_FAIL_NULL(light_instance); light_instance->aabb = p_aabb; } void LightStorage::light_instance_set_shadow_transform(RID p_light_instance, const Projection &p_projection, const Transform3D &p_transform, float p_far, float p_split, int p_pass, float p_shadow_texel_size, float p_bias_scale, float p_range_begin, const Vector2 &p_uv_scale) { LightInstance *light_instance = light_instance_owner.get_or_null(p_light_instance); ERR_FAIL_NULL(light_instance); ERR_FAIL_INDEX(p_pass, 6); light_instance->shadow_transform[p_pass].camera = p_projection; light_instance->shadow_transform[p_pass].transform = p_transform; light_instance->shadow_transform[p_pass].farplane = p_far; light_instance->shadow_transform[p_pass].split = p_split; light_instance->shadow_transform[p_pass].bias_scale = p_bias_scale; light_instance->shadow_transform[p_pass].range_begin = p_range_begin; light_instance->shadow_transform[p_pass].shadow_texel_size = p_shadow_texel_size; light_instance->shadow_transform[p_pass].uv_scale = p_uv_scale; } void LightStorage::light_instance_mark_visible(RID p_light_instance) { LightInstance *light_instance = light_instance_owner.get_or_null(p_light_instance); ERR_FAIL_NULL(light_instance); light_instance->last_scene_pass = RasterizerSceneGLES3::get_singleton()->get_scene_pass(); } /* PROBE API */ RID LightStorage::reflection_probe_allocate() { return RID(); } void LightStorage::reflection_probe_initialize(RID p_rid) { } void LightStorage::reflection_probe_free(RID p_rid) { } void LightStorage::reflection_probe_set_update_mode(RID p_probe, RS::ReflectionProbeUpdateMode p_mode) { } void LightStorage::reflection_probe_set_intensity(RID p_probe, float p_intensity) { } void LightStorage::reflection_probe_set_ambient_mode(RID p_probe, RS::ReflectionProbeAmbientMode p_mode) { } void LightStorage::reflection_probe_set_ambient_color(RID p_probe, const Color &p_color) { } void LightStorage::reflection_probe_set_ambient_energy(RID p_probe, float p_energy) { } void LightStorage::reflection_probe_set_max_distance(RID p_probe, float p_distance) { } void LightStorage::reflection_probe_set_size(RID p_probe, const Vector3 &p_size) { } void LightStorage::reflection_probe_set_origin_offset(RID p_probe, const Vector3 &p_offset) { } void LightStorage::reflection_probe_set_as_interior(RID p_probe, bool p_enable) { } void LightStorage::reflection_probe_set_enable_box_projection(RID p_probe, bool p_enable) { } void LightStorage::reflection_probe_set_enable_shadows(RID p_probe, bool p_enable) { } void LightStorage::reflection_probe_set_cull_mask(RID p_probe, uint32_t p_layers) { } void LightStorage::reflection_probe_set_resolution(RID p_probe, int p_resolution) { } AABB LightStorage::reflection_probe_get_aabb(RID p_probe) const { return AABB(); } RS::ReflectionProbeUpdateMode LightStorage::reflection_probe_get_update_mode(RID p_probe) const { return RenderingServer::REFLECTION_PROBE_UPDATE_ONCE; } uint32_t LightStorage::reflection_probe_get_cull_mask(RID p_probe) const { return 0; } Vector3 LightStorage::reflection_probe_get_size(RID p_probe) const { return Vector3(); } Vector3 LightStorage::reflection_probe_get_origin_offset(RID p_probe) const { return Vector3(); } float LightStorage::reflection_probe_get_origin_max_distance(RID p_probe) const { return 0.0; } bool LightStorage::reflection_probe_renders_shadows(RID p_probe) const { return false; } void LightStorage::reflection_probe_set_mesh_lod_threshold(RID p_probe, float p_ratio) { } float LightStorage::reflection_probe_get_mesh_lod_threshold(RID p_probe) const { return 0.0; } /* REFLECTION ATLAS */ RID LightStorage::reflection_atlas_create() { return RID(); } void LightStorage::reflection_atlas_free(RID p_ref_atlas) { } int LightStorage::reflection_atlas_get_size(RID p_ref_atlas) const { return 0; } void LightStorage::reflection_atlas_set_size(RID p_ref_atlas, int p_reflection_size, int p_reflection_count) { } /* REFLECTION PROBE INSTANCE */ RID LightStorage::reflection_probe_instance_create(RID p_probe) { return RID(); } void LightStorage::reflection_probe_instance_free(RID p_instance) { } void LightStorage::reflection_probe_instance_set_transform(RID p_instance, const Transform3D &p_transform) { } void LightStorage::reflection_probe_release_atlas_index(RID p_instance) { } bool LightStorage::reflection_probe_instance_needs_redraw(RID p_instance) { return false; } bool LightStorage::reflection_probe_instance_has_reflection(RID p_instance) { return false; } bool LightStorage::reflection_probe_instance_begin_render(RID p_instance, RID p_reflection_atlas) { return false; } Ref LightStorage::reflection_probe_atlas_get_render_buffers(RID p_reflection_atlas) { return Ref(); } bool LightStorage::reflection_probe_instance_postprocess_step(RID p_instance) { return true; } /* LIGHTMAP CAPTURE */ RID LightStorage::lightmap_allocate() { return lightmap_owner.allocate_rid(); } void LightStorage::lightmap_initialize(RID p_rid) { lightmap_owner.initialize_rid(p_rid, Lightmap()); } void LightStorage::lightmap_free(RID p_rid) { Lightmap *lightmap = lightmap_owner.get_or_null(p_rid); lightmap->dependency.deleted_notify(p_rid); lightmap_owner.free(p_rid); } void LightStorage::lightmap_set_textures(RID p_lightmap, RID p_light, bool p_uses_spherical_haromics) { } void LightStorage::lightmap_set_probe_bounds(RID p_lightmap, const AABB &p_bounds) { } void LightStorage::lightmap_set_probe_interior(RID p_lightmap, bool p_interior) { } void LightStorage::lightmap_set_probe_capture_data(RID p_lightmap, const PackedVector3Array &p_points, const PackedColorArray &p_point_sh, const PackedInt32Array &p_tetrahedra, const PackedInt32Array &p_bsp_tree) { } void LightStorage::lightmap_set_baked_exposure_normalization(RID p_lightmap, float p_exposure) { } PackedVector3Array LightStorage::lightmap_get_probe_capture_points(RID p_lightmap) const { return PackedVector3Array(); } PackedColorArray LightStorage::lightmap_get_probe_capture_sh(RID p_lightmap) const { return PackedColorArray(); } PackedInt32Array LightStorage::lightmap_get_probe_capture_tetrahedra(RID p_lightmap) const { return PackedInt32Array(); } PackedInt32Array LightStorage::lightmap_get_probe_capture_bsp_tree(RID p_lightmap) const { return PackedInt32Array(); } AABB LightStorage::lightmap_get_aabb(RID p_lightmap) const { return AABB(); } void LightStorage::lightmap_tap_sh_light(RID p_lightmap, const Vector3 &p_point, Color *r_sh) { } bool LightStorage::lightmap_is_interior(RID p_lightmap) const { return false; } void LightStorage::lightmap_set_probe_capture_update_speed(float p_speed) { } float LightStorage::lightmap_get_probe_capture_update_speed() const { return 0; } /* LIGHTMAP INSTANCE */ RID LightStorage::lightmap_instance_create(RID p_lightmap) { return RID(); } void LightStorage::lightmap_instance_free(RID p_lightmap) { } void LightStorage::lightmap_instance_set_transform(RID p_lightmap, const Transform3D &p_transform) { } /* SHADOW ATLAS API */ RID LightStorage::shadow_atlas_create() { return shadow_atlas_owner.make_rid(ShadowAtlas()); } void LightStorage::shadow_atlas_free(RID p_atlas) { shadow_atlas_set_size(p_atlas, 0); shadow_atlas_owner.free(p_atlas); } void LightStorage::shadow_atlas_set_size(RID p_atlas, int p_size, bool p_16_bits) { ShadowAtlas *shadow_atlas = shadow_atlas_owner.get_or_null(p_atlas); ERR_FAIL_NULL(shadow_atlas); ERR_FAIL_COND(p_size < 0); p_size = next_power_of_2(p_size); if (p_size == shadow_atlas->size && p_16_bits == shadow_atlas->use_16_bits) { return; } for (uint32_t i = 0; i < 4; i++) { // Clear all subdivisions and free shadows. for (uint32_t j = 0; j < shadow_atlas->quadrants[i].textures.size(); j++) { glDeleteTextures(1, &shadow_atlas->quadrants[i].textures[j]); glDeleteFramebuffers(1, &shadow_atlas->quadrants[i].fbos[j]); } shadow_atlas->quadrants[i].textures.clear(); shadow_atlas->quadrants[i].fbos.clear(); shadow_atlas->quadrants[i].shadows.clear(); shadow_atlas->quadrants[i].shadows.resize(shadow_atlas->quadrants[i].subdivision * shadow_atlas->quadrants[i].subdivision); } // Erase shadow atlas reference from lights. for (const KeyValue &E : shadow_atlas->shadow_owners) { LightInstance *li = light_instance_owner.get_or_null(E.key); ERR_CONTINUE(!li); li->shadow_atlases.erase(p_atlas); } if (shadow_atlas->debug_texture != 0) { glDeleteTextures(1, &shadow_atlas->debug_texture); } if (shadow_atlas->debug_fbo != 0) { glDeleteFramebuffers(1, &shadow_atlas->debug_fbo); } // Clear owners. shadow_atlas->shadow_owners.clear(); shadow_atlas->size = p_size; shadow_atlas->use_16_bits = p_16_bits; } void LightStorage::shadow_atlas_set_quadrant_subdivision(RID p_atlas, int p_quadrant, int p_subdivision) { ShadowAtlas *shadow_atlas = shadow_atlas_owner.get_or_null(p_atlas); ERR_FAIL_NULL(shadow_atlas); ERR_FAIL_INDEX(p_quadrant, 4); ERR_FAIL_INDEX(p_subdivision, 16384); uint32_t subdiv = next_power_of_2(p_subdivision); if (subdiv & 0xaaaaaaaa) { // sqrt(subdiv) must be integer. subdiv <<= 1; } subdiv = int(Math::sqrt((float)subdiv)); if (shadow_atlas->quadrants[p_quadrant].subdivision == subdiv) { return; } // Erase all data from quadrant. for (int i = 0; i < shadow_atlas->quadrants[p_quadrant].shadows.size(); i++) { if (shadow_atlas->quadrants[p_quadrant].shadows[i].owner.is_valid()) { shadow_atlas->shadow_owners.erase(shadow_atlas->quadrants[p_quadrant].shadows[i].owner); LightInstance *li = light_instance_owner.get_or_null(shadow_atlas->quadrants[p_quadrant].shadows[i].owner); ERR_CONTINUE(!li); li->shadow_atlases.erase(p_atlas); } } for (uint32_t j = 0; j < shadow_atlas->quadrants[p_quadrant].textures.size(); j++) { glDeleteTextures(1, &shadow_atlas->quadrants[p_quadrant].textures[j]); glDeleteFramebuffers(1, &shadow_atlas->quadrants[p_quadrant].fbos[j]); } shadow_atlas->quadrants[p_quadrant].textures.clear(); shadow_atlas->quadrants[p_quadrant].fbos.clear(); shadow_atlas->quadrants[p_quadrant].shadows.clear(); shadow_atlas->quadrants[p_quadrant].shadows.resize(subdiv * subdiv); shadow_atlas->quadrants[p_quadrant].subdivision = subdiv; // Cache the smallest subdiv (for faster allocation in light update). shadow_atlas->smallest_subdiv = 1 << 30; for (int i = 0; i < 4; i++) { if (shadow_atlas->quadrants[i].subdivision) { shadow_atlas->smallest_subdiv = MIN(shadow_atlas->smallest_subdiv, shadow_atlas->quadrants[i].subdivision); } } if (shadow_atlas->smallest_subdiv == 1 << 30) { shadow_atlas->smallest_subdiv = 0; } // Re-sort the size orders, simple bubblesort for 4 elements. int swaps = 0; do { swaps = 0; for (int i = 0; i < 3; i++) { if (shadow_atlas->quadrants[shadow_atlas->size_order[i]].subdivision < shadow_atlas->quadrants[shadow_atlas->size_order[i + 1]].subdivision) { SWAP(shadow_atlas->size_order[i], shadow_atlas->size_order[i + 1]); swaps++; } } } while (swaps > 0); } bool LightStorage::shadow_atlas_update_light(RID p_atlas, RID p_light_instance, float p_coverage, uint64_t p_light_version) { ShadowAtlas *shadow_atlas = shadow_atlas_owner.get_or_null(p_atlas); ERR_FAIL_NULL_V(shadow_atlas, false); LightInstance *li = light_instance_owner.get_or_null(p_light_instance); ERR_FAIL_NULL_V(li, false); if (shadow_atlas->size == 0 || shadow_atlas->smallest_subdiv == 0) { return false; } uint32_t quad_size = shadow_atlas->size >> 1; int desired_fit = MIN(quad_size / shadow_atlas->smallest_subdiv, next_power_of_2(quad_size * p_coverage)); int valid_quadrants[4]; int valid_quadrant_count = 0; int best_size = -1; // Best size found. int best_subdiv = -1; // Subdiv for the best size. // Find the quadrants this fits into, and the best possible size it can fit into. for (int i = 0; i < 4; i++) { int q = shadow_atlas->size_order[i]; int sd = shadow_atlas->quadrants[q].subdivision; if (sd == 0) { continue; // Unused. } int max_fit = quad_size / sd; if (best_size != -1 && max_fit > best_size) { break; // Too large. } valid_quadrants[valid_quadrant_count++] = q; best_subdiv = sd; if (max_fit >= desired_fit) { best_size = max_fit; } } ERR_FAIL_COND_V(valid_quadrant_count == 0, false); uint64_t tick = OS::get_singleton()->get_ticks_msec(); uint32_t old_key = SHADOW_INVALID; uint32_t old_quadrant = SHADOW_INVALID; uint32_t old_shadow = SHADOW_INVALID; int old_subdivision = -1; bool should_realloc = false; bool should_redraw = false; if (shadow_atlas->shadow_owners.has(p_light_instance)) { old_key = shadow_atlas->shadow_owners[p_light_instance]; old_quadrant = (old_key >> QUADRANT_SHIFT) & 0x3; old_shadow = old_key & SHADOW_INDEX_MASK; // Only re-allocate if a better option is available, and enough time has passed. should_realloc = shadow_atlas->quadrants[old_quadrant].subdivision != (uint32_t)best_subdiv && (shadow_atlas->quadrants[old_quadrant].shadows[old_shadow].alloc_tick - tick > shadow_atlas_realloc_tolerance_msec); should_redraw = shadow_atlas->quadrants[old_quadrant].shadows[old_shadow].version != p_light_version; if (!should_realloc) { shadow_atlas->quadrants[old_quadrant].shadows.write[old_shadow].version = p_light_version; // Already existing, see if it should redraw or it's just OK. return should_redraw; } old_subdivision = shadow_atlas->quadrants[old_quadrant].subdivision; } bool is_omni = li->light_type == RS::LIGHT_OMNI; bool found_shadow = false; int new_quadrant = -1; int new_shadow = -1; found_shadow = _shadow_atlas_find_shadow(shadow_atlas, valid_quadrants, valid_quadrant_count, old_subdivision, tick, is_omni, new_quadrant, new_shadow); // For new shadows if we found an atlas. // Or for existing shadows that found a better atlas. if (found_shadow) { if (old_quadrant != SHADOW_INVALID) { shadow_atlas->quadrants[old_quadrant].shadows.write[old_shadow].version = 0; shadow_atlas->quadrants[old_quadrant].shadows.write[old_shadow].owner = RID(); } uint32_t new_key = new_quadrant << QUADRANT_SHIFT; new_key |= new_shadow; ShadowAtlas::Quadrant::Shadow *sh = &shadow_atlas->quadrants[new_quadrant].shadows.write[new_shadow]; _shadow_atlas_invalidate_shadow(sh, p_atlas, shadow_atlas, new_quadrant, new_shadow); sh->owner = p_light_instance; sh->owner_is_omni = is_omni; sh->alloc_tick = tick; sh->version = p_light_version; li->shadow_atlases.insert(p_atlas); // Update it in map. shadow_atlas->shadow_owners[p_light_instance] = new_key; // Make it dirty, as it should redraw anyway. return true; } return should_redraw; } bool LightStorage::_shadow_atlas_find_shadow(ShadowAtlas *shadow_atlas, int *p_in_quadrants, int p_quadrant_count, int p_current_subdiv, uint64_t p_tick, bool is_omni, int &r_quadrant, int &r_shadow) { for (int i = p_quadrant_count - 1; i >= 0; i--) { int qidx = p_in_quadrants[i]; if (shadow_atlas->quadrants[qidx].subdivision == (uint32_t)p_current_subdiv) { return false; } // Look for an empty space. int sc = shadow_atlas->quadrants[qidx].shadows.size(); const ShadowAtlas::Quadrant::Shadow *sarr = shadow_atlas->quadrants[qidx].shadows.ptr(); // We have a free space in this quadrant, allocate a texture and use it. if (sc > (int)shadow_atlas->quadrants[qidx].textures.size()) { GLuint fbo_id = 0; glGenFramebuffers(1, &fbo_id); glBindFramebuffer(GL_FRAMEBUFFER, fbo_id); GLuint texture_id = 0; glGenTextures(1, &texture_id); glActiveTexture(GL_TEXTURE0); int size = (shadow_atlas->size >> 1) / shadow_atlas->quadrants[qidx].subdivision; GLenum format = shadow_atlas->use_16_bits ? GL_DEPTH_COMPONENT16 : GL_DEPTH_COMPONENT24; GLenum type = shadow_atlas->use_16_bits ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT; if (is_omni) { glBindTexture(GL_TEXTURE_CUBE_MAP, texture_id); for (int id = 0; id < 6; id++) { glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + id, 0, format, size / 2, size / 2, 0, GL_DEPTH_COMPONENT, type, nullptr); } glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE); glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_COMPARE_FUNC, GL_LESS); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_CUBE_MAP_POSITIVE_X, texture_id, 0); #ifdef DEBUG_ENABLED GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); if (status != GL_FRAMEBUFFER_COMPLETE) { ERR_PRINT("Could not create omni light shadow framebuffer, status: " + GLES3::TextureStorage::get_singleton()->get_framebuffer_error(status)); } #endif glBindTexture(GL_TEXTURE_CUBE_MAP, 0); } else { glBindTexture(GL_TEXTURE_2D, texture_id); glTexImage2D(GL_TEXTURE_2D, 0, format, size, size, 0, GL_DEPTH_COMPONENT, type, nullptr); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_LESS); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, texture_id, 0); glBindTexture(GL_TEXTURE_2D, 0); } glBindFramebuffer(GL_FRAMEBUFFER, 0); r_quadrant = qidx; r_shadow = shadow_atlas->quadrants[qidx].textures.size(); shadow_atlas->quadrants[qidx].textures.push_back(texture_id); shadow_atlas->quadrants[qidx].fbos.push_back(fbo_id); return true; } int found_used_idx = -1; // Found existing one, must steal it. uint64_t min_pass = 0; // Pass of the existing one, try to use the least recently used one (LRU fashion). for (int j = 0; j < sc; j++) { LightInstance *sli = light_instance_owner.get_or_null(sarr[j].owner); ERR_CONTINUE(!sli); if (sli->last_scene_pass != RasterizerSceneGLES3::get_singleton()->get_scene_pass()) { // Was just allocated, don't kill it so soon, wait a bit. if (p_tick - sarr[j].alloc_tick < shadow_atlas_realloc_tolerance_msec) { continue; } if (found_used_idx == -1 || sli->last_scene_pass < min_pass) { found_used_idx = j; min_pass = sli->last_scene_pass; } } } if (found_used_idx != -1) { r_quadrant = qidx; r_shadow = found_used_idx; return true; } } return false; } void LightStorage::_shadow_atlas_invalidate_shadow(ShadowAtlas::Quadrant::Shadow *p_shadow, RID p_atlas, ShadowAtlas *p_shadow_atlas, uint32_t p_quadrant, uint32_t p_shadow_idx) { if (p_shadow->owner.is_valid()) { LightInstance *sli = light_instance_owner.get_or_null(p_shadow->owner); p_shadow_atlas->shadow_owners.erase(p_shadow->owner); p_shadow->version = 0; p_shadow->owner = RID(); sli->shadow_atlases.erase(p_atlas); } } void LightStorage::shadow_atlas_update(RID p_atlas) { // Do nothing as there is no shadow atlas texture. } /* DIRECTIONAL SHADOW */ // Create if necessary and clear. void LightStorage::update_directional_shadow_atlas() { if (directional_shadow.depth == 0 && directional_shadow.size > 0) { glGenFramebuffers(1, &directional_shadow.fbo); glBindFramebuffer(GL_FRAMEBUFFER, directional_shadow.fbo); glGenTextures(1, &directional_shadow.depth); glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, directional_shadow.depth); GLenum format = directional_shadow.use_16_bits ? GL_DEPTH_COMPONENT16 : GL_DEPTH_COMPONENT24; GLenum type = directional_shadow.use_16_bits ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT; glTexImage2D(GL_TEXTURE_2D, 0, format, directional_shadow.size, directional_shadow.size, 0, GL_DEPTH_COMPONENT, type, nullptr); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_LESS); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, directional_shadow.depth, 0); } glDepthMask(GL_TRUE); glBindFramebuffer(GL_FRAMEBUFFER, directional_shadow.fbo); RasterizerGLES3::clear_depth(1.0); glClear(GL_DEPTH_BUFFER_BIT); glBindTexture(GL_TEXTURE_2D, 0); glBindFramebuffer(GL_FRAMEBUFFER, 0); } void LightStorage::directional_shadow_atlas_set_size(int p_size, bool p_16_bits) { p_size = nearest_power_of_2_templated(p_size); if (directional_shadow.size == p_size && directional_shadow.use_16_bits == p_16_bits) { return; } directional_shadow.size = p_size; directional_shadow.use_16_bits = p_16_bits; if (directional_shadow.depth != 0) { glDeleteTextures(1, &directional_shadow.depth); directional_shadow.depth = 0; glDeleteFramebuffers(1, &directional_shadow.fbo); directional_shadow.fbo = 0; } } void LightStorage::set_directional_shadow_count(int p_count) { directional_shadow.light_count = p_count; directional_shadow.current_light = 0; } static Rect2i _get_directional_shadow_rect(int p_size, int p_shadow_count, int p_shadow_index) { int split_h = 1; int split_v = 1; while (split_h * split_v < p_shadow_count) { if (split_h == split_v) { split_h <<= 1; } else { split_v <<= 1; } } Rect2i rect(0, 0, p_size, p_size); rect.size.width /= split_h; rect.size.height /= split_v; rect.position.x = rect.size.width * (p_shadow_index % split_h); rect.position.y = rect.size.height * (p_shadow_index / split_h); return rect; } Rect2i LightStorage::get_directional_shadow_rect() { return _get_directional_shadow_rect(directional_shadow.size, directional_shadow.light_count, directional_shadow.current_light); } int LightStorage::get_directional_light_shadow_size(RID p_light_instance) { ERR_FAIL_COND_V(directional_shadow.light_count == 0, 0); Rect2i r = _get_directional_shadow_rect(directional_shadow.size, directional_shadow.light_count, 0); LightInstance *light_instance = light_instance_owner.get_or_null(p_light_instance); ERR_FAIL_NULL_V(light_instance, 0); switch (light_directional_get_shadow_mode(light_instance->light)) { case RS::LIGHT_DIRECTIONAL_SHADOW_ORTHOGONAL: break; //none case RS::LIGHT_DIRECTIONAL_SHADOW_PARALLEL_2_SPLITS: r.size.height /= 2; break; case RS::LIGHT_DIRECTIONAL_SHADOW_PARALLEL_4_SPLITS: r.size /= 2; break; } return MAX(r.size.width, r.size.height); } #endif // !GLES3_ENABLED