diff --git a/doc/classes/GeometryInstance.xml b/doc/classes/GeometryInstance.xml index c7656d806..f4422ea22 100644 --- a/doc/classes/GeometryInstance.xml +++ b/doc/classes/GeometryInstance.xml @@ -64,10 +64,13 @@ Will only show the shadows casted from this object. In other words, the actual mesh will not be visible, only the shadows casted from the mesh will be. - + + Will allow the GeometryInstance to be used when baking lights using a [GIProbe] or [BakedLightmap]. + + Unused in this class, exposed for consistency with [enum RenderingServer.InstanceFlags]. - + Represents the size of the [enum Flags] enum. diff --git a/doc/classes/RenderingServer.xml b/doc/classes/RenderingServer.xml index 140d05c8f..e364f2b5c 100644 --- a/doc/classes/RenderingServer.xml +++ b/doc/classes/RenderingServer.xml @@ -3292,10 +3292,13 @@ A combination of the flags of geometry instances (mesh, multimesh, immediate and particles). - + + Allows the instance to be used in baked lighting. + + When set, manually requests to draw geometry on next frame. - + Represents the size of the [enum InstanceFlags] enum. diff --git a/drivers/dummy/rasterizer_dummy.h b/drivers/dummy/rasterizer_dummy.h index f96029c5a..10b06bd50 100644 --- a/drivers/dummy/rasterizer_dummy.h +++ b/drivers/dummy/rasterizer_dummy.h @@ -551,6 +551,71 @@ public: void instance_add_dependency(RID p_base, RasterizerScene::InstanceBase *p_instance) {} void instance_remove_dependency(RID p_base, RasterizerScene::InstanceBase *p_instance) {} + /* LIGHTMAP CAPTURE */ + struct Instantiable : public RID_Data { + SelfList::List instance_list; + + _FORCE_INLINE_ void instance_change_notify(bool p_aabb = true, bool p_materials = true) { + SelfList *instances = instance_list.first(); + while (instances) { + instances->self()->base_changed(p_aabb, p_materials); + instances = instances->next(); + } + } + + _FORCE_INLINE_ void instance_remove_deps() { + SelfList *instances = instance_list.first(); + while (instances) { + SelfList *next = instances->next(); + instances->self()->base_removed(); + instances = next; + } + } + + Instantiable() {} + virtual ~Instantiable() { + } + }; + + struct LightmapCapture : public Instantiable { + PoolVector octree; + AABB bounds; + Transform cell_xform; + int cell_subdiv; + float energy; + LightmapCapture() { + energy = 1.0; + cell_subdiv = 1; + } + }; + + mutable RID_Owner lightmap_capture_data_owner; + void lightmap_capture_set_bounds(RID p_capture, const AABB &p_bounds) {} + AABB lightmap_capture_get_bounds(RID p_capture) const { return AABB(); } + void lightmap_capture_set_octree(RID p_capture, const PoolVector &p_octree) {} + RID lightmap_capture_create() { + LightmapCapture *capture = memnew(LightmapCapture); + return lightmap_capture_data_owner.make_rid(capture); + } + PoolVector lightmap_capture_get_octree(RID p_capture) const { + const LightmapCapture *capture = lightmap_capture_data_owner.getornull(p_capture); + ERR_FAIL_COND_V(!capture, PoolVector()); + return PoolVector(); + } + void lightmap_capture_set_octree_cell_transform(RID p_capture, const Transform &p_xform) {} + Transform lightmap_capture_get_octree_cell_transform(RID p_capture) const { return Transform(); } + void lightmap_capture_set_octree_cell_subdiv(RID p_capture, int p_subdiv) {} + int lightmap_capture_get_octree_cell_subdiv(RID p_capture) const { return 0; } + void lightmap_capture_set_energy(RID p_capture, float p_energy) {} + float lightmap_capture_get_energy(RID p_capture) const { return 0.0; } + void lightmap_capture_set_interior(RID p_capture, bool p_interior) {} + bool lightmap_capture_is_interior(RID p_capture) const { return false; } + const PoolVector *lightmap_capture_get_octree_ptr(RID p_capture) const { + const LightmapCapture *capture = lightmap_capture_data_owner.getornull(p_capture); + ERR_FAIL_COND_V(!capture, NULL); + return &capture->octree; + } + /* RENDER TARGET */ RID render_target_create() { return RID(); } @@ -579,6 +644,8 @@ public: RS::InstanceType get_base_type(RID p_rid) const { if (mesh_owner.owns(p_rid)) { return RS::INSTANCE_MESH; + } else if (lightmap_capture_data_owner.owns(p_rid)) { + return RS::INSTANCE_LIGHTMAP_CAPTURE; } return RS::INSTANCE_NONE; @@ -595,6 +662,11 @@ public: DummyMesh *mesh = mesh_owner.getornull(p_rid); mesh_owner.free(p_rid); memdelete(mesh); + } else if (lightmap_capture_data_owner.owns(p_rid)) { + // delete the lightmap + LightmapCapture *lightmap_capture = lightmap_capture_data_owner.getornull(p_rid); + lightmap_capture_data_owner.free(p_rid); + memdelete(lightmap_capture); } else { return false; } diff --git a/drivers/gles2/rasterizer_scene_gles2.cpp b/drivers/gles2/rasterizer_scene_gles2.cpp index 3fbc9f33a..3ccc16f35 100644 --- a/drivers/gles2/rasterizer_scene_gles2.cpp +++ b/drivers/gles2/rasterizer_scene_gles2.cpp @@ -1209,7 +1209,13 @@ void RasterizerSceneGLES2::_add_geometry_with_material(RasterizerStorageGLES2::G copy = true; } - e->light_mode = LIGHTMODE_NORMAL; + if (e->instance->lightmap.is_valid()) { + e->light_mode = LIGHTMODE_LIGHTMAP; + } else if (!e->instance->lightmap_capture_data.empty()) { + e->light_mode = LIGHTMODE_LIGHTMAP_CAPTURE; + } else { + e->light_mode = LIGHTMODE_NORMAL; + } } } @@ -2294,6 +2300,10 @@ void RasterizerSceneGLES2::_render_render_list(RenderList::Element **p_elements, using_fog = true; } + RasterizerStorageGLES2::Texture *prev_lightmap = nullptr; + float lightmap_energy = 1.0; + bool prev_use_lightmap_capture = false; + storage->info.render.draw_call_count += p_element_count; for (int i = 0; i < p_element_count; i++) { @@ -2307,8 +2317,11 @@ void RasterizerSceneGLES2::_render_render_list(RenderList::Element **p_elements, LightInstance *light = nullptr; ReflectionProbeInstance *refprobe_1 = nullptr; ReflectionProbeInstance *refprobe_2 = nullptr; + RasterizerStorageGLES2::Texture *lightmap = nullptr; + bool use_lightmap_capture = false; bool rebind_light = false; bool rebind_reflection = false; + bool rebind_lightmap = false; if (!p_shadow && material->shader) { bool unshaded = material->shader->spatial.unshaded; @@ -2432,6 +2445,34 @@ void RasterizerSceneGLES2::_render_render_list(RenderList::Element **p_elements, rebind = true; rebind_reflection = true; } + + use_lightmap_capture = !unshaded && !accum_pass && !e->instance->lightmap_capture_data.empty(); + + if (use_lightmap_capture != prev_use_lightmap_capture) { + state.scene_shader.set_conditional(SceneShaderGLES2::USE_LIGHTMAP_CAPTURE, use_lightmap_capture); + rebind = true; + } + + if (!unshaded && !accum_pass && e->instance->lightmap.is_valid()) { + lightmap = storage->texture_owner.getornull(e->instance->lightmap); + lightmap_energy = 1.0; + if (lightmap) { + RasterizerStorageGLES2::LightmapCapture *capture = storage->lightmap_capture_data_owner.getornull(e->instance->lightmap_capture->base); + if (capture) { + lightmap_energy = capture->energy; + } + } + } + + if (lightmap != prev_lightmap) { + state.scene_shader.set_conditional(SceneShaderGLES2::USE_LIGHTMAP, lightmap != nullptr); + if (lightmap != nullptr) { + WRAPPED_GL_ACTIVE_TEXTURE(GL_TEXTURE0 + storage->config.max_texture_image_units - 4); + glBindTexture(GL_TEXTURE_2D, lightmap->tex_id); + } + rebind = true; + rebind_lightmap = true; + } } bool depth_prepass = false; @@ -2533,6 +2574,7 @@ void RasterizerSceneGLES2::_render_render_list(RenderList::Element **p_elements, //rebind all these rebind_light = true; rebind_reflection = true; + rebind_lightmap = true; if (using_fog) { state.scene_shader.set_uniform(SceneShaderGLES2::FOG_COLOR_BASE, p_env->fog_color); @@ -2579,8 +2621,19 @@ void RasterizerSceneGLES2::_render_render_list(RenderList::Element **p_elements, _setup_refprobes(refprobe_1, refprobe_2, p_view_transform, p_env); } + if (rebind_lightmap && lightmap) { + state.scene_shader.set_uniform(SceneShaderGLES2::LIGHTMAP_ENERGY, lightmap_energy); + if (storage->config.use_lightmap_filter_bicubic) { + state.scene_shader.set_uniform(SceneShaderGLES2::LIGHTMAP_TEXTURE_SIZE, Vector2(lightmap->width, lightmap->height)); + } + } + state.scene_shader.set_uniform(SceneShaderGLES2::WORLD_TRANSFORM, e->instance->transform); + if (use_lightmap_capture) { //this is per instance, must be set always if present + glUniform4fv(state.scene_shader.get_uniform_location(SceneShaderGLES2::LIGHTMAP_CAPTURES), 12, (const GLfloat *)e->instance->lightmap_capture_data.ptr()); + } + _render_geometry(e); prev_geometry = e->geometry; @@ -2592,6 +2645,8 @@ void RasterizerSceneGLES2::_render_render_list(RenderList::Element **p_elements, prev_light = light; prev_refprobe_1 = refprobe_1; prev_refprobe_2 = refprobe_2; + prev_lightmap = lightmap; + prev_use_lightmap_capture = use_lightmap_capture; } _setup_light_type(nullptr, nullptr); //clear light stuff @@ -2608,6 +2663,8 @@ void RasterizerSceneGLES2::_render_render_list(RenderList::Element **p_elements, state.scene_shader.set_conditional(SceneShaderGLES2::USE_VERTEX_LIGHTING, false); state.scene_shader.set_conditional(SceneShaderGLES2::USE_REFLECTION_PROBE1, false); state.scene_shader.set_conditional(SceneShaderGLES2::USE_REFLECTION_PROBE2, false); + state.scene_shader.set_conditional(SceneShaderGLES2::USE_LIGHTMAP, false); + state.scene_shader.set_conditional(SceneShaderGLES2::USE_LIGHTMAP_CAPTURE, false); state.scene_shader.set_conditional(SceneShaderGLES2::FOG_DEPTH_ENABLED, false); state.scene_shader.set_conditional(SceneShaderGLES2::FOG_HEIGHT_ENABLED, false); state.scene_shader.set_conditional(SceneShaderGLES2::USE_DEPTH_PREPASS, false); @@ -3967,6 +4024,10 @@ void RasterizerSceneGLES2::initialize() { directional_shadow_create(); + if (storage->config.use_lightmap_filter_bicubic) { + state.scene_shader.add_custom_define("#define USE_LIGHTMAP_FILTER_BICUBIC\n"); + } + shadow_filter_mode = SHADOW_FILTER_NEAREST; glFrontFace(GL_CW); diff --git a/drivers/gles2/rasterizer_scene_gles2.h b/drivers/gles2/rasterizer_scene_gles2.h index fb31e20de..750f3a1bb 100644 --- a/drivers/gles2/rasterizer_scene_gles2.h +++ b/drivers/gles2/rasterizer_scene_gles2.h @@ -558,6 +558,8 @@ public: enum LightMode { LIGHTMODE_NORMAL, LIGHTMODE_UNSHADED, + LIGHTMODE_LIGHTMAP, + LIGHTMODE_LIGHTMAP_CAPTURE, }; struct RenderList { diff --git a/drivers/gles2/rasterizer_storage_gles2.cpp b/drivers/gles2/rasterizer_storage_gles2.cpp index 4c75aac9e..5c4cf7f85 100644 --- a/drivers/gles2/rasterizer_storage_gles2.cpp +++ b/drivers/gles2/rasterizer_storage_gles2.cpp @@ -4649,6 +4649,127 @@ int RasterizerStorageGLES2::reflection_probe_get_resolution(RID p_probe) const { return reflection_probe->resolution; } +/////// + +RID RasterizerStorageGLES2::lightmap_capture_create() { + LightmapCapture *capture = memnew(LightmapCapture); + return lightmap_capture_data_owner.make_rid(capture); +} + +void RasterizerStorageGLES2::lightmap_capture_set_bounds(RID p_capture, const AABB &p_bounds) { + LightmapCapture *capture = lightmap_capture_data_owner.getornull(p_capture); + ERR_FAIL_COND(!capture); + capture->bounds = p_bounds; + capture->instance_change_notify(true, false); +} +AABB RasterizerStorageGLES2::lightmap_capture_get_bounds(RID p_capture) const { + const LightmapCapture *capture = lightmap_capture_data_owner.getornull(p_capture); + ERR_FAIL_COND_V(!capture, AABB()); + return capture->bounds; +} +void RasterizerStorageGLES2::lightmap_capture_set_octree(RID p_capture, const PoolVector &p_octree) { + LightmapCapture *capture = lightmap_capture_data_owner.getornull(p_capture); + ERR_FAIL_COND(!capture); + + ERR_FAIL_COND(p_octree.size() == 0 || (p_octree.size() % sizeof(LightmapCaptureOctree)) != 0); + + capture->octree.resize(p_octree.size() / sizeof(LightmapCaptureOctree)); + if (p_octree.size()) { + PoolVector::Write w = capture->octree.write(); + PoolVector::Read r = p_octree.read(); + memcpy(w.ptr(), r.ptr(), p_octree.size()); + } + capture->instance_change_notify(true, false); +} +PoolVector RasterizerStorageGLES2::lightmap_capture_get_octree(RID p_capture) const { + const LightmapCapture *capture = lightmap_capture_data_owner.getornull(p_capture); + ERR_FAIL_COND_V(!capture, PoolVector()); + + if (capture->octree.size() == 0) { + return PoolVector(); + } + + PoolVector ret; + ret.resize(capture->octree.size() * sizeof(LightmapCaptureOctree)); + { + PoolVector::Read r = capture->octree.read(); + PoolVector::Write w = ret.write(); + memcpy(w.ptr(), r.ptr(), ret.size()); + } + + return ret; +} + +void RasterizerStorageGLES2::lightmap_capture_set_octree_cell_transform(RID p_capture, const Transform &p_xform) { + LightmapCapture *capture = lightmap_capture_data_owner.getornull(p_capture); + ERR_FAIL_COND(!capture); + capture->cell_xform = p_xform; +} + +Transform RasterizerStorageGLES2::lightmap_capture_get_octree_cell_transform(RID p_capture) const { + const LightmapCapture *capture = lightmap_capture_data_owner.getornull(p_capture); + ERR_FAIL_COND_V(!capture, Transform()); + return capture->cell_xform; +} + +void RasterizerStorageGLES2::lightmap_capture_set_octree_cell_subdiv(RID p_capture, int p_subdiv) { + LightmapCapture *capture = lightmap_capture_data_owner.getornull(p_capture); + ERR_FAIL_COND(!capture); + capture->cell_subdiv = p_subdiv; +} + +int RasterizerStorageGLES2::lightmap_capture_get_octree_cell_subdiv(RID p_capture) const { + const LightmapCapture *capture = lightmap_capture_data_owner.getornull(p_capture); + ERR_FAIL_COND_V(!capture, 0); + return capture->cell_subdiv; +} + +void RasterizerStorageGLES2::lightmap_capture_set_energy(RID p_capture, float p_energy) { + LightmapCapture *capture = lightmap_capture_data_owner.getornull(p_capture); + ERR_FAIL_COND(!capture); + capture->energy = p_energy; + + if (!capture->update_list.in_list()) { + capture_update_list.add(&capture->update_list); + } +} + +float RasterizerStorageGLES2::lightmap_capture_get_energy(RID p_capture) const { + const LightmapCapture *capture = lightmap_capture_data_owner.getornull(p_capture); + ERR_FAIL_COND_V(!capture, 0); + return capture->energy; +} + +void RasterizerStorageGLES2::lightmap_capture_set_interior(RID p_capture, bool p_interior) { + LightmapCapture *capture = lightmap_capture_data_owner.getornull(p_capture); + ERR_FAIL_COND(!capture); + capture->interior = p_interior; + + if (!capture->update_list.in_list()) { + capture_update_list.add(&capture->update_list); + } +} + +bool RasterizerStorageGLES2::lightmap_capture_is_interior(RID p_capture) const { + const LightmapCapture *capture = lightmap_capture_data_owner.getornull(p_capture); + ERR_FAIL_COND_V(!capture, false); + return capture->interior; +} + +void RasterizerStorageGLES2::update_dirty_captures() { + while (capture_update_list.first()) { + LightmapCapture *capture = capture_update_list.first()->self(); + capture->instance_change_notify(false, true); + capture_update_list.remove(capture_update_list.first()); + } +} + +const PoolVector *RasterizerStorageGLES2::lightmap_capture_get_octree_ptr(RID p_capture) const { + const LightmapCapture *capture = lightmap_capture_data_owner.getornull(p_capture); + ERR_FAIL_COND_V(!capture, nullptr); + return &capture->octree; +} + //////// void RasterizerStorageGLES2::instance_add_skeleton(RID p_skeleton, RasterizerScene::InstanceBase *p_instance) { @@ -4688,6 +4809,10 @@ void RasterizerStorageGLES2::instance_add_dependency(RID p_base, RasterizerScene inst = light_owner.getornull(p_base); ERR_FAIL_COND(!inst); } break; + case RS::INSTANCE_LIGHTMAP_CAPTURE: { + inst = lightmap_capture_data_owner.getornull(p_base); + ERR_FAIL_COND(!inst); + } break; default: { ERR_FAIL(); } @@ -4720,6 +4845,10 @@ void RasterizerStorageGLES2::instance_remove_dependency(RID p_base, RasterizerSc inst = light_owner.getornull(p_base); ERR_FAIL_COND(!inst); } break; + case RS::INSTANCE_LIGHTMAP_CAPTURE: { + inst = lightmap_capture_data_owner.getornull(p_base); + ERR_FAIL_COND(!inst); + } break; default: { ERR_FAIL(); } @@ -5626,6 +5755,8 @@ RS::InstanceType RasterizerStorageGLES2::get_base_type(RID p_rid) const { return RS::INSTANCE_IMMEDIATE; } else if (reflection_probe_owner.owns(p_rid)) { return RS::INSTANCE_REFLECTION_PROBE; + } else if (lightmap_capture_data_owner.owns(p_rid)) { + return RS::INSTANCE_LIGHTMAP_CAPTURE; } else { return RS::INSTANCE_NONE; } @@ -5816,6 +5947,15 @@ bool RasterizerStorageGLES2::free(RID p_rid) { memdelete(reflection_probe); return true; + } else if (lightmap_capture_data_owner.owns(p_rid)) { + // delete the texture + LightmapCapture *lightmap_capture = lightmap_capture_data_owner.get(p_rid); + lightmap_capture->instance_remove_deps(); + + lightmap_capture_data_owner.free(p_rid); + memdelete(lightmap_capture); + return true; + } else if (canvas_occluder_owner.owns(p_rid)) { CanvasOccluder *co = canvas_occluder_owner.get(p_rid); if (co->index_id) { @@ -6341,6 +6481,9 @@ void RasterizerStorageGLES2::initialize() { config.force_vertex_shading = GLOBAL_GET("rendering/quality/shading/force_vertex_shading"); config.use_fast_texture_filter = GLOBAL_GET("rendering/quality/filters/use_nearest_mipmap_filter"); + GLOBAL_DEF_RST("rendering/quality/lightmapping/use_bicubic_sampling", true); + GLOBAL_DEF_RST("rendering/quality/lightmapping/use_bicubic_sampling.mobile", false); + config.use_lightmap_filter_bicubic = GLOBAL_GET("rendering/quality/lightmapping/use_bicubic_sampling"); config.use_physical_light_attenuation = GLOBAL_GET("rendering/quality/shading/use_physical_light_attenuation"); @@ -6372,6 +6515,7 @@ void RasterizerStorageGLES2::update_dirty_resources() { update_dirty_blend_shapes(); update_dirty_skeletons(); update_dirty_multimeshes(); + update_dirty_captures(); } RasterizerStorageGLES2::RasterizerStorageGLES2() { diff --git a/drivers/gles2/rasterizer_storage_gles2.h b/drivers/gles2/rasterizer_storage_gles2.h index dc47cb988..21158889c 100644 --- a/drivers/gles2/rasterizer_storage_gles2.h +++ b/drivers/gles2/rasterizer_storage_gles2.h @@ -60,6 +60,7 @@ public: bool use_fast_texture_filter; bool use_anisotropic_filter; bool use_skeleton_software; + bool use_lightmap_filter_bicubic; bool use_physical_light_attenuation; int max_vertex_texture_image_units; @@ -1048,6 +1049,48 @@ public: virtual float reflection_probe_get_origin_max_distance(RID p_probe) const; virtual bool reflection_probe_renders_shadows(RID p_probe) const; + /* LIGHTMAP */ + + struct LightmapCapture : public Instantiable { + PoolVector octree; + AABB bounds; + Transform cell_xform; + int cell_subdiv; + float energy; + bool interior; + + SelfList update_list; + + LightmapCapture() : + update_list(this) { + energy = 1.0; + cell_subdiv = 1; + interior = false; + } + }; + + SelfList::List capture_update_list; + + void update_dirty_captures(); + + mutable RID_Owner lightmap_capture_data_owner; + + virtual RID lightmap_capture_create(); + virtual void lightmap_capture_set_bounds(RID p_capture, const AABB &p_bounds); + virtual AABB lightmap_capture_get_bounds(RID p_capture) const; + virtual void lightmap_capture_set_octree(RID p_capture, const PoolVector &p_octree); + virtual PoolVector lightmap_capture_get_octree(RID p_capture) const; + virtual void lightmap_capture_set_octree_cell_transform(RID p_capture, const Transform &p_xform); + virtual Transform lightmap_capture_get_octree_cell_transform(RID p_capture) const; + virtual void lightmap_capture_set_octree_cell_subdiv(RID p_capture, int p_subdiv); + virtual int lightmap_capture_get_octree_cell_subdiv(RID p_capture) const; + virtual void lightmap_capture_set_energy(RID p_capture, float p_energy); + virtual float lightmap_capture_get_energy(RID p_capture) const; + virtual void lightmap_capture_set_interior(RID p_capture, bool p_interior); + virtual bool lightmap_capture_is_interior(RID p_capture) const; + + virtual const PoolVector *lightmap_capture_get_octree_ptr(RID p_capture) const; + /* INSTANCE */ virtual void instance_add_skeleton(RID p_skeleton, RasterizerScene::InstanceBase *p_instance); diff --git a/drivers/gles2/shaders/scene.glsl b/drivers/gles2/shaders/scene.glsl index 1e07d55b3..2858f79d8 100644 --- a/drivers/gles2/shaders/scene.glsl +++ b/drivers/gles2/shaders/scene.glsl @@ -53,7 +53,7 @@ attribute vec4 color_attrib; // attrib:3 attribute vec2 uv_attrib; // attrib:4 #endif -#if defined(ENABLE_UV2_INTERP) +#if defined(ENABLE_UV2_INTERP) || defined(USE_LIGHTMAP) attribute vec2 uv2_attrib; // attrib:5 #endif @@ -143,7 +143,7 @@ varying vec4 color_interp; varying vec2 uv_interp; #endif -#if defined(ENABLE_UV2_INTERP) +#if defined(ENABLE_UV2_INTERP) || defined(USE_LIGHTMAP) varying vec2 uv2_interp; #endif @@ -305,7 +305,9 @@ uniform highp mat4 refprobe1_local_matrix; varying mediump vec4 refprobe1_reflection_normal_blend; uniform highp vec3 refprobe1_box_extents; +#ifndef USE_LIGHTMAP varying mediump vec3 refprobe1_ambient_normal; +#endif #endif //reflection probe1 @@ -315,7 +317,9 @@ uniform highp mat4 refprobe2_local_matrix; varying mediump vec4 refprobe2_reflection_normal_blend; uniform highp vec3 refprobe2_box_extents; +#ifndef USE_LIGHTMAP varying mediump vec3 refprobe2_ambient_normal; +#endif #endif //reflection probe2 @@ -392,7 +396,7 @@ void main() { uv_interp = uv_attrib; #endif -#if defined(ENABLE_UV2_INTERP) +#if defined(ENABLE_UV2_INTERP) || defined(USE_LIGHTMAP) uv2_interp = uv2_attrib; #endif @@ -678,7 +682,9 @@ VERTEX_SHADER_CODE refprobe1_reflection_normal_blend.a = blend; } +#ifndef USE_LIGHTMAP refprobe1_ambient_normal = (refprobe1_local_matrix * vec4(normal_interp, 0.0)).xyz; +#endif } #endif //USE_REFLECTION_PROBE1 @@ -696,7 +702,9 @@ VERTEX_SHADER_CODE refprobe2_reflection_normal_blend.a = blend; } +#ifndef USE_LIGHTMAP refprobe2_ambient_normal = (refprobe2_local_matrix * vec4(normal_interp, 0.0)).xyz; +#endif } #endif //USE_REFLECTION_PROBE2 @@ -819,8 +827,9 @@ uniform highp sampler2D depth_texture; //texunit:-4 #ifdef USE_VERTEX_LIGHTING varying mediump vec4 refprobe1_reflection_normal_blend; - +#ifndef USE_LIGHTMAP varying mediump vec3 refprobe1_ambient_normal; +#endif #else @@ -845,8 +854,9 @@ uniform vec4 refprobe1_ambient; #ifdef USE_VERTEX_LIGHTING varying mediump vec4 refprobe2_reflection_normal_blend; - +#ifndef USE_LIGHTMAP varying mediump vec3 refprobe2_ambient_normal; +#endif #else @@ -873,7 +883,9 @@ uniform vec4 refprobe2_ambient; void reflection_process(samplerCube reflection_map, #ifdef USE_VERTEX_LIGHTING vec3 ref_normal, +#ifndef USE_LIGHTMAP vec3 amb_normal, +#endif float ref_blend, #else //no vertex lighting @@ -936,6 +948,8 @@ void reflection_process(samplerCube reflection_map, reflection_accum += reflection; +#ifndef USE_LIGHTMAP + vec4 ambient_out; #ifndef USE_VERTEX_LIGHTING @@ -951,10 +965,84 @@ void reflection_process(samplerCube reflection_map, ambient_out.a = blend; ambient_out.rgb *= blend; ambient_accum += ambient_out; +#endif } #endif //use refprobe 1 or 2 +#ifdef USE_LIGHTMAP +uniform mediump sampler2D lightmap; //texunit:-4 +uniform mediump float lightmap_energy; + +#if defined(USE_LIGHTMAP_FILTER_BICUBIC) +uniform mediump vec2 lightmap_texture_size; + +// w0, w1, w2, and w3 are the four cubic B-spline basis functions +float w0(float a) { + return (1.0 / 6.0) * (a * (a * (-a + 3.0) - 3.0) + 1.0); +} + +float w1(float a) { + return (1.0 / 6.0) * (a * a * (3.0 * a - 6.0) + 4.0); +} + +float w2(float a) { + return (1.0 / 6.0) * (a * (a * (-3.0 * a + 3.0) + 3.0) + 1.0); +} + +float w3(float a) { + return (1.0 / 6.0) * (a * a * a); +} + +// g0 and g1 are the two amplitude functions +float g0(float a) { + return w0(a) + w1(a); +} + +float g1(float a) { + return w2(a) + w3(a); +} + +// h0 and h1 are the two offset functions +float h0(float a) { + return -1.0 + w1(a) / (w0(a) + w1(a)); +} + +float h1(float a) { + return 1.0 + w3(a) / (w2(a) + w3(a)); +} + +vec4 texture2D_bicubic(sampler2D tex, vec2 uv) { + vec2 texel_size = vec2(1.0) / lightmap_texture_size; + + uv = uv * lightmap_texture_size + vec2(0.5); + + vec2 iuv = floor(uv); + vec2 fuv = fract(uv); + + float g0x = g0(fuv.x); + float g1x = g1(fuv.x); + float h0x = h0(fuv.x); + float h1x = h1(fuv.x); + float h0y = h0(fuv.y); + float h1y = h1(fuv.y); + + vec2 p0 = (vec2(iuv.x + h0x, iuv.y + h0y) - vec2(0.5)) * texel_size; + vec2 p1 = (vec2(iuv.x + h1x, iuv.y + h0y) - vec2(0.5)) * texel_size; + vec2 p2 = (vec2(iuv.x + h0x, iuv.y + h1y) - vec2(0.5)) * texel_size; + vec2 p3 = (vec2(iuv.x + h1x, iuv.y + h1y) - vec2(0.5)) * texel_size; + + return (g0(fuv.y) * (g0x * texture2D(tex, p0) + g1x * texture2D(tex, p1))) + + (g1(fuv.y) * (g0x * texture2D(tex, p2) + g1x * texture2D(tex, p3))); +} +#endif //USE_LIGHTMAP_FILTER_BICUBIC +#endif + +#ifdef USE_LIGHTMAP_CAPTURE +uniform mediump vec4 lightmap_captures[12]; +#endif + + #ifdef USE_RADIANCE_MAP uniform samplerCube radiance_map; // texunit:-2 @@ -1064,7 +1152,7 @@ varying vec4 color_interp; varying vec2 uv_interp; #endif -#if defined(ENABLE_UV2_INTERP) +#if defined(ENABLE_UV2_INTERP) || defined(USE_LIGHTMAP) varying vec2 uv2_interp; #endif @@ -1666,6 +1754,7 @@ FRAGMENT_SHADER_CODE specular_light = textureCubeLod(radiance_map, ref_vec, roughness * RADIANCE_MAX_LOD).xyz * bg_energy; specular_light *= horizon * horizon; +#ifndef USE_LIGHTMAP { vec3 ambient_dir = normalize((radiance_inverse_xform * vec4(normal, 0.0)).xyz); vec3 env_ambient = textureCubeLod(radiance_map, ambient_dir, 4.0).xyz * bg_energy; @@ -1673,6 +1762,7 @@ FRAGMENT_SHADER_CODE ambient_light = mix(ambient_color.rgb, env_ambient, ambient_sky_contribution); } +#endif #else @@ -1693,7 +1783,9 @@ FRAGMENT_SHADER_CODE reflection_process(reflection_probe1, #ifdef USE_VERTEX_LIGHTING refprobe1_reflection_normal_blend.rgb, +#ifndef USE_LIGHTMAP refprobe1_ambient_normal, +#endif refprobe1_reflection_normal_blend.a, #else normal, vertex_interp, refprobe1_local_matrix, @@ -1709,7 +1801,9 @@ FRAGMENT_SHADER_CODE reflection_process(reflection_probe2, #ifdef USE_VERTEX_LIGHTING refprobe2_reflection_normal_blend.rgb, +#ifndef USE_LIGHTMAP refprobe2_ambient_normal, +#endif refprobe2_reflection_normal_blend.a, #else normal, vertex_interp, refprobe2_local_matrix, @@ -1724,9 +1818,11 @@ FRAGMENT_SHADER_CODE specular_light = reflection_accum.rgb / reflection_accum.a; } +#ifndef USE_LIGHTMAP if (ambient_accum.a > 0.0) { ambient_light = ambient_accum.rgb / ambient_accum.a; } +#endif #endif // defined(USE_REFLECTION_PROBE1) || defined(USE_REFLECTION_PROBE2) @@ -1750,6 +1846,51 @@ FRAGMENT_SHADER_CODE #endif } +#ifdef USE_LIGHTMAP +//ambient light will come entirely from lightmap is lightmap is used +#if defined(USE_LIGHTMAP_FILTER_BICUBIC) + ambient_light = texture2D_bicubic(lightmap, uv2_interp).rgb * lightmap_energy; +#else + ambient_light = texture2D(lightmap, uv2_interp).rgb * lightmap_energy; +#endif +#endif + +#ifdef USE_LIGHTMAP_CAPTURE + { + vec3 cone_dirs[12]; + cone_dirs[0] = vec3(0.0, 0.0, 1.0); + cone_dirs[1] = vec3(0.866025, 0.0, 0.5); + cone_dirs[2] = vec3(0.267617, 0.823639, 0.5); + cone_dirs[3] = vec3(-0.700629, 0.509037, 0.5); + cone_dirs[4] = vec3(-0.700629, -0.509037, 0.5); + cone_dirs[5] = vec3(0.267617, -0.823639, 0.5); + cone_dirs[6] = vec3(0.0, 0.0, -1.0); + cone_dirs[7] = vec3(0.866025, 0.0, -0.5); + cone_dirs[8] = vec3(0.267617, 0.823639, -0.5); + cone_dirs[9] = vec3(-0.700629, 0.509037, -0.5); + cone_dirs[10] = vec3(-0.700629, -0.509037, -0.5); + cone_dirs[11] = vec3(0.267617, -0.823639, -0.5); + + vec3 local_normal = normalize(camera_matrix * vec4(normal, 0.0)).xyz; + vec4 captured = vec4(0.0); + float sum = 0.0; + for (int i = 0; i < 12; i++) { + float amount = max(0.0, dot(local_normal, cone_dirs[i])); //not correct, but creates a nice wrap around effect + captured += lightmap_captures[i] * amount; + sum += amount; + } + + captured /= sum; + + // Alpha channel is used to indicate if dynamic objects keep the environment lighting + if (lightmap_captures[0].a > 0.5) { + ambient_light += captured.rgb; + } else { + ambient_light = captured.rgb; + } + } +#endif + #endif //BASE PASS // diff --git a/editor/editor_settings.cpp b/editor/editor_settings.cpp index 822415be9..97711f017 100644 --- a/editor/editor_settings.cpp +++ b/editor/editor_settings.cpp @@ -572,6 +572,9 @@ void EditorSettings::_load_defaults(Ref p_extra_config) { _initial_set("editors/3d/default_z_near", 0.05); _initial_set("editors/3d/default_z_far", 500.0); + _initial_set("editors/3d/lightmap_baking_number_of_cpu_threads", 0); + hints["editors/3d/lightmap_baking_number_of_cpu_threads"] = PropertyInfo(Variant::INT, "editors/3d/lightmap_baking_number_of_cpu_threads", PROPERTY_HINT_RANGE, "-2,128,1", PROPERTY_USAGE_DEFAULT); + // 3D: Navigation _initial_set("editors/3d/navigation/navigation_scheme", 0); _initial_set("editors/3d/navigation/invert_y_axis", false); diff --git a/editor/import/resource_importer_scene.cpp b/editor/import/resource_importer_scene.cpp index 1e9838cbc..1b47f0695 100644 --- a/editor/import/resource_importer_scene.cpp +++ b/editor/import/resource_importer_scene.cpp @@ -204,6 +204,10 @@ bool ResourceImporterScene::get_option_visibility(const String &p_option, const return false; } + if (p_option == "meshes/lightmap_texel_size" && int(p_options["meshes/light_baking"]) < 2) { + return false; + } + return true; } diff --git a/editor/plugins/mesh_instance_editor_plugin.cpp b/editor/plugins/mesh_instance_editor_plugin.cpp index a2faa5e97..63bf333fb 100644 --- a/editor/plugins/mesh_instance_editor_plugin.cpp +++ b/editor/plugins/mesh_instance_editor_plugin.cpp @@ -285,6 +285,23 @@ void MeshInstanceEditor::_menu_option(int p_option) { case MENU_OPTION_CREATE_OUTLINE_MESH: { outline_dialog->popup_centered(Vector2(200, 90)); } break; + case MENU_OPTION_CREATE_UV2: { + Ref mesh2 = node->get_mesh(); + if (!mesh2.is_valid()) { + err_dialog->set_text(TTR("Contained Mesh is not of type ArrayMesh.")); + err_dialog->popup_centered_minsize(); + return; + } + /* + Error err = mesh2->lightmap_unwrap(node->get_global_transform()); + if (err != OK) { + err_dialog->set_text(TTR("UV Unwrap failed, mesh may not be manifold?")); + err_dialog->popup_centered_minsize(); + return; + } + */ + + } break; case MENU_OPTION_DEBUG_UV1: { Ref mesh2 = node->get_mesh(); if (!mesh2.is_valid()) { @@ -481,6 +498,7 @@ MeshInstanceEditor::MeshInstanceEditor() { options->get_popup()->add_separator(); options->get_popup()->add_item(TTR("View UV1"), MENU_OPTION_DEBUG_UV1); options->get_popup()->add_item(TTR("View UV2"), MENU_OPTION_DEBUG_UV2); + options->get_popup()->add_item(TTR("Unwrap UV2 for Lightmap/AO"), MENU_OPTION_CREATE_UV2); options->get_popup()->connect("id_pressed", this, "_menu_option"); diff --git a/editor/plugins/mesh_instance_editor_plugin.h b/editor/plugins/mesh_instance_editor_plugin.h index 28ea41630..826ed1a17 100644 --- a/editor/plugins/mesh_instance_editor_plugin.h +++ b/editor/plugins/mesh_instance_editor_plugin.h @@ -61,6 +61,7 @@ class MeshInstanceEditor : public Control { MENU_OPTION_CREATE_MULTIPLE_CONVEX_COLLISION_SHAPES, MENU_OPTION_CREATE_NAVMESH, MENU_OPTION_CREATE_OUTLINE_MESH, + MENU_OPTION_CREATE_UV2, MENU_OPTION_DEBUG_UV1, MENU_OPTION_DEBUG_UV2, }; diff --git a/scene/3d/visual_instance.cpp b/scene/3d/visual_instance.cpp index 787f28eba..0fc25ecbf 100644 --- a/scene/3d/visual_instance.cpp +++ b/scene/3d/visual_instance.cpp @@ -257,6 +257,23 @@ Ref GeometryInstance::get_material_overlay() const { return material_overlay; } +void GeometryInstance::set_generate_lightmap(bool p_enabled) { + generate_lightmap = p_enabled; +} + +bool GeometryInstance::get_generate_lightmap() { + return generate_lightmap; +} + +void GeometryInstance::set_lightmap_scale(LightmapScale p_scale) { + ERR_FAIL_INDEX(p_scale, LIGHTMAP_SCALE_MAX); + lightmap_scale = p_scale; +} + +GeometryInstance::LightmapScale GeometryInstance::get_lightmap_scale() const { + return lightmap_scale; +} + void GeometryInstance::_notification(int p_what) { } @@ -317,6 +334,12 @@ void GeometryInstance::_bind_methods() { ClassDB::bind_method(D_METHOD("set_cast_shadows_setting", "shadow_casting_setting"), &GeometryInstance::set_cast_shadows_setting); ClassDB::bind_method(D_METHOD("get_cast_shadows_setting"), &GeometryInstance::get_cast_shadows_setting); + ClassDB::bind_method(D_METHOD("set_generate_lightmap", "enabled"), &GeometryInstance::set_generate_lightmap); + ClassDB::bind_method(D_METHOD("get_generate_lightmap"), &GeometryInstance::get_generate_lightmap); + + ClassDB::bind_method(D_METHOD("set_lightmap_scale", "scale"), &GeometryInstance::set_lightmap_scale); + ClassDB::bind_method(D_METHOD("get_lightmap_scale"), &GeometryInstance::get_lightmap_scale); + ClassDB::bind_method(D_METHOD("set_extra_cull_margin", "margin"), &GeometryInstance::set_extra_cull_margin); ClassDB::bind_method(D_METHOD("get_extra_cull_margin"), &GeometryInstance::get_extra_cull_margin); @@ -330,13 +353,25 @@ void GeometryInstance::_bind_methods() { 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::REAL, "extra_cull_margin", PROPERTY_HINT_RANGE, "0,16384,0.01"), "set_extra_cull_margin", "get_extra_cull_margin"); + ADD_GROUP("Baked Light", ""); + ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "use_in_baked_light"), "set_flag", "get_flag", FLAG_USE_BAKED_LIGHT); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "generate_lightmap"), "set_generate_lightmap", "get_generate_lightmap"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "lightmap_scale", PROPERTY_HINT_ENUM, "1x,2x,4x,8x"), "set_lightmap_scale", "get_lightmap_scale"); + //ADD_SIGNAL( MethodInfo("visibility_changed")); + BIND_ENUM_CONSTANT(LIGHTMAP_SCALE_1X); + BIND_ENUM_CONSTANT(LIGHTMAP_SCALE_2X); + BIND_ENUM_CONSTANT(LIGHTMAP_SCALE_4X); + BIND_ENUM_CONSTANT(LIGHTMAP_SCALE_8X); + BIND_ENUM_CONSTANT(LIGHTMAP_SCALE_MAX); + BIND_ENUM_CONSTANT(SHADOW_CASTING_SETTING_OFF); BIND_ENUM_CONSTANT(SHADOW_CASTING_SETTING_ON); BIND_ENUM_CONSTANT(SHADOW_CASTING_SETTING_DOUBLE_SIDED); BIND_ENUM_CONSTANT(SHADOW_CASTING_SETTING_SHADOWS_ONLY); + BIND_ENUM_CONSTANT(FLAG_USE_BAKED_LIGHT); BIND_ENUM_CONSTANT(FLAG_DRAW_NEXT_FRAME_IF_VISIBLE); BIND_ENUM_CONSTANT(FLAG_MAX); } @@ -348,4 +383,6 @@ GeometryInstance::GeometryInstance() { shadow_casting_setting = SHADOW_CASTING_SETTING_ON; extra_cull_margin = 0; + generate_lightmap = true; + lightmap_scale = LightmapScale::LIGHTMAP_SCALE_1X; } diff --git a/scene/3d/visual_instance.h b/scene/3d/visual_instance.h index 765d293bb..8a6bff297 100644 --- a/scene/3d/visual_instance.h +++ b/scene/3d/visual_instance.h @@ -98,10 +98,19 @@ class GeometryInstance : public VisualInstance { public: enum Flags { + FLAG_USE_BAKED_LIGHT = RS::INSTANCE_FLAG_USE_BAKED_LIGHT, FLAG_DRAW_NEXT_FRAME_IF_VISIBLE = RS::INSTANCE_FLAG_DRAW_NEXT_FRAME_IF_VISIBLE, FLAG_MAX = RS::INSTANCE_FLAG_MAX, }; + enum LightmapScale { + LIGHTMAP_SCALE_1X, + LIGHTMAP_SCALE_2X, + LIGHTMAP_SCALE_4X, + LIGHTMAP_SCALE_8X, + LIGHTMAP_SCALE_MAX, + }; + enum ShadowCastingSetting { SHADOW_CASTING_SETTING_OFF = RS::SHADOW_CASTING_SETTING_OFF, SHADOW_CASTING_SETTING_ON = RS::SHADOW_CASTING_SETTING_ON, @@ -111,6 +120,8 @@ public: private: bool flags[FLAG_MAX]; + bool generate_lightmap; + LightmapScale lightmap_scale; ShadowCastingSetting shadow_casting_setting; Ref material_override; Ref material_overlay; @@ -128,6 +139,12 @@ public: void set_cast_shadows_setting(ShadowCastingSetting p_shadow_casting_setting); ShadowCastingSetting get_cast_shadows_setting() const; + void set_generate_lightmap(bool p_enabled); + bool get_generate_lightmap(); + + void set_lightmap_scale(LightmapScale p_scale); + LightmapScale get_lightmap_scale() const; + virtual void set_material_override(const Ref &p_material); Ref get_material_override() const; @@ -143,6 +160,7 @@ public: }; VARIANT_ENUM_CAST(GeometryInstance::Flags); +VARIANT_ENUM_CAST(GeometryInstance::LightmapScale); VARIANT_ENUM_CAST(GeometryInstance::ShadowCastingSetting); #endif diff --git a/scene/resources/mesh/mesh.cpp b/scene/resources/mesh/mesh.cpp index d925340dd..e81146de3 100644 --- a/scene/resources/mesh/mesh.cpp +++ b/scene/resources/mesh/mesh.cpp @@ -594,9 +594,21 @@ Ref Mesh::create_outline(float p_margin) const { return newmesh; } +void Mesh::set_lightmap_size_hint(const Vector2i &p_size) { + lightmap_size_hint = p_size; +} + +Size2i Mesh::get_lightmap_size_hint() const { + return lightmap_size_hint; +} + void Mesh::_bind_methods() { ClassDB::bind_method(D_METHOD("get_aabb"), &Mesh::get_aabb); + ClassDB::bind_method(D_METHOD("set_lightmap_size_hint", "size"), &Mesh::set_lightmap_size_hint); + ClassDB::bind_method(D_METHOD("get_lightmap_size_hint"), &Mesh::get_lightmap_size_hint); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "lightmap_size_hint"), "set_lightmap_size_hint", "get_lightmap_size_hint"); + ClassDB::bind_method(D_METHOD("get_surface_count"), &Mesh::get_surface_count); ClassDB::bind_method(D_METHOD("surface_get_arrays", "surf_idx"), &Mesh::surface_get_arrays); ClassDB::bind_method(D_METHOD("surface_get_blend_shape_arrays", "surf_idx"), &Mesh::surface_get_blend_shape_arrays); diff --git a/scene/resources/mesh/mesh.h b/scene/resources/mesh/mesh.h index 93a6069f9..75ac0f489 100644 --- a/scene/resources/mesh/mesh.h +++ b/scene/resources/mesh/mesh.h @@ -46,6 +46,7 @@ class Mesh : public Resource { mutable Ref triangle_mesh; //cached mutable Vector debug_lines; + Size2i lightmap_size_hint; protected: static void _bind_methods(); @@ -169,6 +170,9 @@ public: virtual AABB get_aabb() const = 0; virtual void set_storage_mode(StorageMode p_storage_mode); + void set_lightmap_size_hint(const Vector2i &p_size); + Size2i get_lightmap_size_hint() const; + void clear_cache() const; typedef Vector> (*ConvexDecompositionFunc)(const real_t *p_vertices, int p_vertex_count, const uint32_t *p_triangles, int p_triangle_count, int p_max_convex_hulls, Vector> *r_convex_indices); diff --git a/servers/rendering/rasterizer.h b/servers/rendering/rasterizer.h index 0c6aee1f3..20f432969 100644 --- a/servers/rendering/rasterizer.h +++ b/servers/rendering/rasterizer.h @@ -118,6 +118,7 @@ public: bool mirror : 1; bool receive_shadows : 1; bool visible : 1; + bool baked_light : 1; //this flag is only to know if it actually did use baked light bool redraw_if_visible : 1; bool on_interpolate_list : 1; @@ -134,6 +135,12 @@ public: SelfList dependency_item; + InstanceBase *lightmap_capture; + RID lightmap; + Vector lightmap_capture_data; //in a array (12 values) to avoid wasting space if unused. Alpha is unused, but needed to send to shader + int lightmap_slice; + Rect2 lightmap_uv_rect; + virtual void base_removed() = 0; virtual void base_changed(bool p_aabb, bool p_materials) = 0; InstanceBase() : @@ -144,7 +151,11 @@ public: visible = true; depth_layer = 0; layer_mask = 1; + baked_light = false; redraw_if_visible = false; + lightmap_capture = nullptr; + lightmap_slice = -1; + lightmap_uv_rect = Rect2(0, 0, 1, 1); on_interpolate_list = false; on_interpolate_transform_list = false; interpolated = true; @@ -504,6 +515,33 @@ public: virtual void instance_add_dependency(RID p_base, RasterizerScene::InstanceBase *p_instance) = 0; virtual void instance_remove_dependency(RID p_base, RasterizerScene::InstanceBase *p_instance) = 0; + /* LIGHTMAP CAPTURE */ + + struct LightmapCaptureOctree { + enum { + CHILD_EMPTY = 0xFFFFFFFF + }; + + uint16_t light[6][3]; //anisotropic light + float alpha; + uint32_t children[8]; + }; + + virtual RID lightmap_capture_create() = 0; + virtual void lightmap_capture_set_bounds(RID p_capture, const AABB &p_bounds) = 0; + virtual AABB lightmap_capture_get_bounds(RID p_capture) const = 0; + virtual void lightmap_capture_set_octree(RID p_capture, const PoolVector &p_octree) = 0; + virtual PoolVector lightmap_capture_get_octree(RID p_capture) const = 0; + virtual void lightmap_capture_set_octree_cell_transform(RID p_capture, const Transform &p_xform) = 0; + virtual Transform lightmap_capture_get_octree_cell_transform(RID p_capture) const = 0; + virtual void lightmap_capture_set_octree_cell_subdiv(RID p_capture, int p_subdiv) = 0; + virtual int lightmap_capture_get_octree_cell_subdiv(RID p_capture) const = 0; + virtual void lightmap_capture_set_energy(RID p_capture, float p_energy) = 0; + virtual float lightmap_capture_get_energy(RID p_capture) const = 0; + virtual void lightmap_capture_set_interior(RID p_capture, bool p_interior) = 0; + virtual bool lightmap_capture_is_interior(RID p_capture) const = 0; + virtual const PoolVector *lightmap_capture_get_octree_ptr(RID p_capture) const = 0; + /* RENDER TARGET */ enum RenderTargetFlags { diff --git a/servers/rendering/rendering_server_raster.h b/servers/rendering/rendering_server_raster.h index 763dac8c5..ca2f31123 100644 --- a/servers/rendering/rendering_server_raster.h +++ b/servers/rendering/rendering_server_raster.h @@ -359,6 +359,27 @@ public: BIND2(reflection_probe_set_cull_mask, RID, uint32_t) BIND2(reflection_probe_set_resolution, RID, int) + /* LIGHTMAP CAPTURE */ + + BIND0R(RID, lightmap_capture_create) + + BIND2(lightmap_capture_set_bounds, RID, const AABB &) + BIND1RC(AABB, lightmap_capture_get_bounds, RID) + + BIND2(lightmap_capture_set_octree, RID, const PoolVector &) + BIND1RC(PoolVector, lightmap_capture_get_octree, RID) + + BIND2(lightmap_capture_set_octree_cell_transform, RID, const Transform &) + BIND1RC(Transform, lightmap_capture_get_octree_cell_transform, RID) + BIND2(lightmap_capture_set_octree_cell_subdiv, RID, int) + BIND1RC(int, lightmap_capture_get_octree_cell_subdiv, RID) + + BIND2(lightmap_capture_set_energy, RID, float) + BIND1RC(float, lightmap_capture_get_energy, RID) + + BIND2(lightmap_capture_set_interior, RID, bool) + BIND1RC(bool, lightmap_capture_is_interior, RID) + #undef BINDBASE //from now on, calls forwarded to this singleton #define BINDBASE RSG::scene @@ -485,6 +506,7 @@ public: BIND3(instance_set_blend_shape_weight, RID, int, float) BIND3(instance_set_surface_material, RID, int, RID) BIND2(instance_set_visible, RID, bool) + BIND5(instance_set_use_lightmap, RID, RID, RID, int, const Rect2 &) BIND2(instance_set_custom_aabb, RID, AABB) diff --git a/servers/rendering/rendering_server_scene.cpp b/servers/rendering/rendering_server_scene.cpp index deff2c6a7..e2006cc54 100644 --- a/servers/rendering/rendering_server_scene.cpp +++ b/servers/rendering/rendering_server_scene.cpp @@ -286,6 +286,18 @@ void *RenderingServerScene::_instance_pair(void *p_self, SpatialPartitionID, Ins } geom->lighting_dirty = true; + return E; //this element should make freeing faster + } else if (B->base_type == RS::INSTANCE_LIGHTMAP_CAPTURE && ((1 << A->base_type) & RS::INSTANCE_GEOMETRY_MASK)) { + InstanceLightmapCaptureData *lightmap_capture = static_cast(B->base_data); + InstanceGeometryData *geom = static_cast(A->base_data); + + InstanceLightmapCaptureData::PairInfo pinfo; + pinfo.geometry = A; + pinfo.L = geom->lightmap_captures.push_back(B); + + List::Element *E = lightmap_capture->geometries.push_back(pinfo); + ((RenderingServerScene *)p_self)->_instance_queue_update(A, false, false); //need to update capture + return E; //this element should make freeing faster } else if (B->base_type == RS::INSTANCE_REFLECTION_PROBE && ((1 << A->base_type) & RS::INSTANCE_GEOMETRY_MASK)) { InstanceReflectionProbeData *reflection_probe = static_cast(B->base_data); @@ -339,6 +351,16 @@ void RenderingServerScene::_instance_unpair(void *p_self, SpatialPartitionID, In reflection_probe->geometries.erase(E); geom->reflection_dirty = true; + } else if (B->base_type == RS::INSTANCE_LIGHTMAP_CAPTURE && ((1 << A->base_type) & RS::INSTANCE_GEOMETRY_MASK)) { + InstanceLightmapCaptureData *lightmap_capture = static_cast(B->base_data); + InstanceGeometryData *geom = static_cast(A->base_data); + + List::Element *E = reinterpret_cast::Element *>(udata); + + geom->lightmap_captures.erase(E->get().L); + lightmap_capture->geometries.erase(E); + ((RenderingServerScene *)p_self)->_instance_queue_update(A, false, false); //need to update capture + } } @@ -473,6 +495,13 @@ void RenderingServerScene::instance_set_base(RID p_instance, RID p_base) { reflection_probe_render_list.remove(&reflection_probe->update_list); } } break; + case RS::INSTANCE_LIGHTMAP_CAPTURE: { + InstanceLightmapCaptureData *lightmap_capture = static_cast(instance->base_data); + //erase dependencies, since no longer a lightmap + while (lightmap_capture->users.front()) { + instance_set_use_lightmap(lightmap_capture->users.front()->get()->self, RID(), RID(), -1, Rect2(0, 0, 1, 1)); + } + } break; default: { } } @@ -527,6 +556,11 @@ void RenderingServerScene::instance_set_base(RID p_instance, RID p_base) { reflection_probe->instance = RSG::scene_render->reflection_probe_instance_create(p_base); } break; + case RS::INSTANCE_LIGHTMAP_CAPTURE: { + InstanceLightmapCaptureData *lightmap_capture = memnew(InstanceLightmapCaptureData); + instance->base_data = lightmap_capture; + //lightmap_capture->instance = RSG::scene_render->lightmap_capture_instance_create(p_base); + } break; default: { } @@ -961,6 +995,12 @@ void RenderingServerScene::instance_set_visible(RID p_instance, bool p_visible) instance->scenario->sps->set_pairable(instance, p_visible, 1 << RS::INSTANCE_REFLECTION_PROBE, p_visible ? RS::INSTANCE_GEOMETRY_MASK : 0); } + } break; + case RS::INSTANCE_LIGHTMAP_CAPTURE: { + if (instance->spatial_partition_id && instance->scenario) { + instance->scenario->sps->set_pairable(instance, p_visible, 1 << RS::INSTANCE_LIGHTMAP_CAPTURE, p_visible ? RS::INSTANCE_GEOMETRY_MASK : 0); + } + } break; default: { // if we haven't called set_pairable, we STILL need to do a collision check @@ -975,6 +1015,36 @@ inline bool is_geometry_instance(RenderingServer::InstanceType p_type) { return p_type == RS::INSTANCE_MESH || p_type == RS::INSTANCE_MULTIMESH || p_type == RS::INSTANCE_IMMEDIATE; } +void RenderingServerScene::instance_set_use_lightmap(RID p_instance, RID p_lightmap_instance, RID p_lightmap, int p_lightmap_slice, const Rect2 &p_lightmap_uv_rect) { + Instance *instance = instance_owner.get(p_instance); + ERR_FAIL_COND(!instance); + + instance->lightmap = RID(); + instance->lightmap_slice = -1; + instance->lightmap_uv_rect = Rect2(0, 0, 1, 1); + instance->baked_light = false; + + if (instance->lightmap_capture) { + InstanceLightmapCaptureData *lightmap_capture = static_cast(((Instance *)instance->lightmap_capture)->base_data); + lightmap_capture->users.erase(instance); + instance->lightmap_capture = nullptr; + } + + if (p_lightmap_instance.is_valid()) { + Instance *lightmap_instance = instance_owner.get(p_lightmap_instance); + ERR_FAIL_COND(!lightmap_instance); + ERR_FAIL_COND(lightmap_instance->base_type != RS::INSTANCE_LIGHTMAP_CAPTURE); + instance->lightmap_capture = lightmap_instance; + + InstanceLightmapCaptureData *lightmap_capture = static_cast(((Instance *)instance->lightmap_capture)->base_data); + lightmap_capture->users.insert(instance); + instance->lightmap = p_lightmap; + instance->lightmap_slice = p_lightmap_slice; + instance->lightmap_uv_rect = p_lightmap_uv_rect; + instance->baked_light = true; + } +} + void RenderingServerScene::instance_set_custom_aabb(RID p_instance, AABB p_aabb) { Instance *instance = instance_owner.get(p_instance); ERR_FAIL_COND(!instance); @@ -1729,6 +1799,13 @@ void RenderingServerScene::_update_instance(Instance *p_instance) { reflection_probe->reflection_dirty = true; } + if (p_instance->base_type == RS::INSTANCE_LIGHTMAP_CAPTURE) { + InstanceLightmapCaptureData *capture = static_cast(p_instance->base_data); + for (List::Element *E = capture->geometries.front(); E; E = E->next()) { + _instance_queue_update(E->get().geometry, false, true); + } + } + if (p_instance->aabb.has_no_surface()) { return; } @@ -1743,6 +1820,15 @@ void RenderingServerScene::_update_instance(Instance *p_instance) { light->make_shadow_dirty(); } } + + if (!p_instance->lightmap_capture && geom->lightmap_captures.size()) { + //affected by lightmap captures, must update capture info! + _update_instance_lightmap_captures(p_instance); + } else { + if (!p_instance->lightmap_capture_data.empty()) { + p_instance->lightmap_capture_data.resize(0); //not in use, clear capture data + } + } } p_instance->mirror = instance_xform->basis.determinant() < 0.0; @@ -1762,7 +1848,7 @@ void RenderingServerScene::_update_instance(Instance *p_instance) { uint32_t pairable_mask = 0; bool pairable = false; - if (p_instance->base_type == RS::INSTANCE_LIGHT || p_instance->base_type == RS::INSTANCE_REFLECTION_PROBE) { + if (p_instance->base_type == RS::INSTANCE_LIGHT || p_instance->base_type == RS::INSTANCE_REFLECTION_PROBE || p_instance->base_type == RS::INSTANCE_LIGHTMAP_CAPTURE) { pairable_mask = p_instance->visible ? RS::INSTANCE_GEOMETRY_MASK : 0; pairable = true; } @@ -1824,6 +1910,10 @@ void RenderingServerScene::_update_instance_aabb(Instance *p_instance) { case RenderingServer::INSTANCE_REFLECTION_PROBE: { new_aabb = RSG::storage->reflection_probe_get_aabb(p_instance->base); + } break; + case RenderingServer::INSTANCE_LIGHTMAP_CAPTURE: { + new_aabb = RSG::storage->lightmap_capture_get_bounds(p_instance->base); + } break; default: { } @@ -1966,6 +2056,254 @@ void RenderingServerScene::_update_dirty_instance(Instance *p_instance) { p_instance->update_materials = false; } +_FORCE_INLINE_ static void _light_capture_sample_octree(const RasterizerStorage::LightmapCaptureOctree *p_octree, int p_cell_subdiv, const Vector3 &p_pos, const Vector3 &p_dir, float p_level, Vector3 &r_color, float &r_alpha) { + static const Vector3 aniso_normal[6] = { + Vector3(-1, 0, 0), + Vector3(1, 0, 0), + Vector3(0, -1, 0), + Vector3(0, 1, 0), + Vector3(0, 0, -1), + Vector3(0, 0, 1) + }; + + int size = 1 << (p_cell_subdiv - 1); + + int clamp_v = size - 1; + //first of all, clamp + Vector3 pos; + pos.x = CLAMP(p_pos.x, 0, clamp_v); + pos.y = CLAMP(p_pos.y, 0, clamp_v); + pos.z = CLAMP(p_pos.z, 0, clamp_v); + + float level = (p_cell_subdiv - 1) - p_level; + + int target_level; + float level_filter; + if (level <= 0.0) { + level_filter = 0; + target_level = 0; + } else { + target_level = Math::ceil(level); + level_filter = target_level - level; + } + + Vector3 color[2][8]; + float alpha[2][8]; + memset(alpha, 0, sizeof(float) * 2 * 8); + + //find cell at given level first + + for (int c = 0; c < 2; c++) { + int current_level = MAX(0, target_level - c); + int level_cell_size = (1 << (p_cell_subdiv - 1)) >> current_level; + + for (int n = 0; n < 8; n++) { + int x = int(pos.x); + int y = int(pos.y); + int z = int(pos.z); + + if (n & 1) { + x += level_cell_size; + } + if (n & 2) { + y += level_cell_size; + } + if (n & 4) { + z += level_cell_size; + } + + int ofs_x = 0; + int ofs_y = 0; + int ofs_z = 0; + + x = CLAMP(x, 0, clamp_v); + y = CLAMP(y, 0, clamp_v); + z = CLAMP(z, 0, clamp_v); + + int half = size / 2; + uint32_t cell = 0; + for (int i = 0; i < current_level; i++) { + const RasterizerStorage::LightmapCaptureOctree *bc = &p_octree[cell]; + + int child = 0; + if (x >= ofs_x + half) { + child |= 1; + ofs_x += half; + } + if (y >= ofs_y + half) { + child |= 2; + ofs_y += half; + } + if (z >= ofs_z + half) { + child |= 4; + ofs_z += half; + } + + cell = bc->children[child]; + if (cell == RasterizerStorage::LightmapCaptureOctree::CHILD_EMPTY) { + break; + } + + half >>= 1; + } + + if (cell != RasterizerStorage::LightmapCaptureOctree::CHILD_EMPTY) { + alpha[c][n] = p_octree[cell].alpha; + + for (int i = 0; i < 6; i++) { + //anisotropic read light + float amount = p_dir.dot(aniso_normal[i]); + + if (amount > 0) { + constexpr float ONE_1024TH = 1.0 / 1024.0; + color[c][n].x += p_octree[cell].light[i][0] * ONE_1024TH * amount; + color[c][n].y += p_octree[cell].light[i][1] * ONE_1024TH * amount; + color[c][n].z += p_octree[cell].light[i][2] * ONE_1024TH * amount; + } + } + } + + //print_line("\tlev " + itos(c) + " - " + itos(n) + " alpha: " + rtos(cells[test_cell].alpha) + " col: " + color[c][n]); + } + } + + float target_level_size = size >> target_level; + Vector3 pos_fract[2]; + + float target_level_size_inv = 1.0f / target_level_size; + real_t res; + res = pos.x * target_level_size_inv; + pos_fract[0].x = res - (int)res; + res = pos.y * target_level_size_inv; + pos_fract[0].y = res - (int)res; + res = pos.z * target_level_size_inv; + pos_fract[0].z = res - (int)res; + + target_level_size = size >> MAX(0, target_level - 1); + + target_level_size_inv = 1.0f / target_level_size; + res = pos.x * target_level_size_inv; + pos_fract[1].x = res - (int)res; + res = pos.y * target_level_size_inv; + pos_fract[1].y = res - (int)res; + res = pos.z * target_level_size_inv; + pos_fract[1].z = res - (int)res; + + float alpha_interp[2]; + Vector3 color_interp[2]; + + for (int i = 0; i < 2; i++) { + Vector3 color_x00 = color[i][0].linear_interpolate(color[i][1], pos_fract[i].x); + Vector3 color_xy0 = color[i][2].linear_interpolate(color[i][3], pos_fract[i].x); + Vector3 blend_z0 = color_x00.linear_interpolate(color_xy0, pos_fract[i].y); + + Vector3 color_x0z = color[i][4].linear_interpolate(color[i][5], pos_fract[i].x); + Vector3 color_xyz = color[i][6].linear_interpolate(color[i][7], pos_fract[i].x); + Vector3 blend_z1 = color_x0z.linear_interpolate(color_xyz, pos_fract[i].y); + + color_interp[i] = blend_z0.linear_interpolate(blend_z1, pos_fract[i].z); + + float alpha_x00 = Math::lerp(alpha[i][0], alpha[i][1], pos_fract[i].x); + float alpha_xy0 = Math::lerp(alpha[i][2], alpha[i][3], pos_fract[i].x); + float alpha_z0 = Math::lerp(alpha_x00, alpha_xy0, pos_fract[i].y); + + float alpha_x0z = Math::lerp(alpha[i][4], alpha[i][5], pos_fract[i].x); + float alpha_xyz = Math::lerp(alpha[i][6], alpha[i][7], pos_fract[i].x); + float alpha_z1 = Math::lerp(alpha_x0z, alpha_xyz, pos_fract[i].y); + + alpha_interp[i] = Math::lerp(alpha_z0, alpha_z1, pos_fract[i].z); + } + + r_color = color_interp[0].linear_interpolate(color_interp[1], level_filter); + r_alpha = Math::lerp(alpha_interp[0], alpha_interp[1], level_filter); + + //print_line("pos: " + p_posf + " level " + rtos(p_level) + " down to " + itos(target_level) + "." + rtos(level_filter) + " color " + r_color + " alpha " + rtos(r_alpha)); +} + +_FORCE_INLINE_ static Color _light_capture_voxel_cone_trace(const RasterizerStorage::LightmapCaptureOctree *p_octree, const Vector3 &p_pos, const Vector3 &p_dir, float p_aperture, int p_cell_subdiv) { + float bias = 0.0; //no need for bias here + float max_distance = (Vector3(1, 1, 1) * (1 << (p_cell_subdiv - 1))).length(); + + float dist = bias; + float alpha = 0.0; + Vector3 color; + + Vector3 scolor; + float salpha; + + while (dist < max_distance && alpha < 0.95) { + float diameter = MAX(1.0, 2.0 * p_aperture * dist); + _light_capture_sample_octree(p_octree, p_cell_subdiv, p_pos + dist * p_dir, p_dir, log2(diameter), scolor, salpha); + float a = (1.0 - alpha); + color += scolor * a; + alpha += a * salpha; + dist += diameter * 0.5; + } + + return Color(color.x, color.y, color.z, alpha); +} + +void RenderingServerScene::_update_instance_lightmap_captures(Instance *p_instance) { + InstanceGeometryData *geom = static_cast(p_instance->base_data); + + static const Vector3 cone_traces[12] = { + Vector3(0, 0, 1), + Vector3(0.866025, 0, 0.5), + Vector3(0.267617, 0.823639, 0.5), + Vector3(-0.700629, 0.509037, 0.5), + Vector3(-0.700629, -0.509037, 0.5), + Vector3(0.267617, -0.823639, 0.5), + Vector3(0, 0, -1), + Vector3(0.866025, 0, -0.5), + Vector3(0.267617, 0.823639, -0.5), + Vector3(-0.700629, 0.509037, -0.5), + Vector3(-0.700629, -0.509037, -0.5), + Vector3(0.267617, -0.823639, -0.5) + }; + + float cone_aperture = 0.577; // tan(angle) 60 degrees + + if (p_instance->lightmap_capture_data.empty()) { + p_instance->lightmap_capture_data.resize(12); + } + + //print_line("update captures for pos: " + p_instance->transform.origin); + + for (int i = 0; i < 12; i++) { + new (&p_instance->lightmap_capture_data.ptrw()[i]) Color; + } + + bool interior = true; + //this could use some sort of blending.. + for (List::Element *E = geom->lightmap_captures.front(); E; E = E->next()) { + const PoolVector *octree = RSG::storage->lightmap_capture_get_octree_ptr(E->get()->base); + //print_line("octree size: " + itos(octree->size())); + if (octree->size() == 0) { + continue; + } + Transform to_cell_xform = RSG::storage->lightmap_capture_get_octree_cell_transform(E->get()->base); + int cell_subdiv = RSG::storage->lightmap_capture_get_octree_cell_subdiv(E->get()->base); + to_cell_xform = to_cell_xform * E->get()->transform.affine_inverse(); + + PoolVector::Read octree_r = octree->read(); + + Vector3 pos = to_cell_xform.xform(p_instance->transform.origin); + + const float capture_energy = RSG::storage->lightmap_capture_get_energy(E->get()->base); + interior = interior && RSG::storage->lightmap_capture_is_interior(E->get()->base); + + for (int i = 0; i < 12; i++) { + Vector3 dir = to_cell_xform.basis.xform(cone_traces[i]).normalized(); + Color capture = _light_capture_voxel_cone_trace(octree_r.ptr(), pos, dir, cone_aperture, cell_subdiv); + capture.r *= capture_energy; + capture.g *= capture_energy; + capture.b *= capture_energy; + p_instance->lightmap_capture_data.write[i] += capture; + } + } + p_instance->lightmap_capture_data.write[0].a = interior ? 0.0f : 1.0f; +} + bool RenderingServerScene::_light_instance_update_shadow(Instance *p_instance, const Transform p_cam_transform, const Projection &p_cam_projection, bool p_cam_orthogonal, RID p_shadow_atlas, Scenario *p_scenario, uint32_t p_visible_layers) { InstanceLightData *light = static_cast(p_instance->base_data); @@ -2846,6 +3184,7 @@ bool RenderingServerScene::free(RID p_rid) { _interpolation_data.notify_free_instance(p_rid, *instance); + instance_set_use_lightmap(p_rid, RID(), RID(), -1, Rect2(0, 0, 1, 1)); instance_set_scenario(p_rid, RID()); instance_set_base(p_rid, RID()); instance_geometry_set_material_override(p_rid, RID()); diff --git a/servers/rendering/rendering_server_scene.h b/servers/rendering/rendering_server_scene.h index 352875e6f..b395a5777 100644 --- a/servers/rendering/rendering_server_scene.h +++ b/servers/rendering/rendering_server_scene.h @@ -418,6 +418,8 @@ public: List reflection_probes; bool reflection_dirty; + List lightmap_captures; + InstanceGeometryData() { lighting_dirty = true; reflection_dirty = true; @@ -521,6 +523,19 @@ public: } }; + struct InstanceLightmapCaptureData : public InstanceBaseData { + struct PairInfo { + List::Element *L; //iterator in geometry + Instance *geometry; + }; + List geometries; + + RBSet users; + + InstanceLightmapCaptureData() { + } + }; + int instance_cull_count; Instance *instance_cull_result[MAX_INSTANCE_CULL]; Instance *instance_shadow_cull_result[MAX_INSTANCE_CULL]; //used for generating shadowmaps @@ -547,6 +562,7 @@ public: virtual void instance_set_blend_shape_weight(RID p_instance, int p_shape, float p_weight); virtual void instance_set_surface_material(RID p_instance, int p_surface, RID p_material); virtual void instance_set_visible(RID p_instance, bool p_visible); + virtual void instance_set_use_lightmap(RID p_instance, RID p_lightmap_instance, RID p_lightmap, int p_lightmap_slice, const Rect2 &p_lightmap_uv_rect); virtual void instance_set_custom_aabb(RID p_instance, AABB p_aabb); @@ -753,6 +769,7 @@ public: _FORCE_INLINE_ void _update_instance(Instance *p_instance); _FORCE_INLINE_ void _update_instance_aabb(Instance *p_instance); _FORCE_INLINE_ void _update_dirty_instance(Instance *p_instance); + _FORCE_INLINE_ void _update_instance_lightmap_captures(Instance *p_instance); _FORCE_INLINE_ bool _light_instance_update_shadow(Instance *p_instance, const Transform p_cam_transform, const Projection &p_cam_projection, bool p_cam_orthogonal, RID p_shadow_atlas, Scenario *p_scenario, uint32_t p_visible_layers = 0xFFFFFF); diff --git a/servers/rendering/rendering_server_wrap_mt.cpp b/servers/rendering/rendering_server_wrap_mt.cpp index c03b3ac4e..7f7f3d5f1 100644 --- a/servers/rendering/rendering_server_wrap_mt.cpp +++ b/servers/rendering/rendering_server_wrap_mt.cpp @@ -153,6 +153,7 @@ void RenderingServerWrapMT::finish() { omni_light_free_cached_ids(); spot_light_free_cached_ids(); reflection_probe_free_cached_ids(); + lightmap_capture_free_cached_ids(); camera_free_cached_ids(); viewport_free_cached_ids(); environment_free_cached_ids(); diff --git a/servers/rendering/rendering_server_wrap_mt.h b/servers/rendering/rendering_server_wrap_mt.h index 9484f91b8..83a2ba676 100644 --- a/servers/rendering/rendering_server_wrap_mt.h +++ b/servers/rendering/rendering_server_wrap_mt.h @@ -283,6 +283,24 @@ public: FUNC2(reflection_probe_set_cull_mask, RID, uint32_t) FUNC2(reflection_probe_set_resolution, RID, int) + /* LIGHTMAP CAPTURE */ + + FUNCRID(lightmap_capture) + + FUNC2(lightmap_capture_set_bounds, RID, const AABB &) + FUNC1RC(AABB, lightmap_capture_get_bounds, RID) + + FUNC2(lightmap_capture_set_octree, RID, const PoolVector &) + FUNC1RC(PoolVector, lightmap_capture_get_octree, RID) + FUNC2(lightmap_capture_set_octree_cell_transform, RID, const Transform &) + FUNC1RC(Transform, lightmap_capture_get_octree_cell_transform, RID) + FUNC2(lightmap_capture_set_octree_cell_subdiv, RID, int) + FUNC1RC(int, lightmap_capture_get_octree_cell_subdiv, RID) + FUNC2(lightmap_capture_set_energy, RID, float) + FUNC1RC(float, lightmap_capture_get_energy, RID) + FUNC2(lightmap_capture_set_interior, RID, bool) + FUNC1RC(bool, lightmap_capture_is_interior, RID) + /* CAMERA API */ FUNCRID(camera) @@ -397,6 +415,7 @@ public: FUNC3(instance_set_blend_shape_weight, RID, int, float) FUNC3(instance_set_surface_material, RID, int, RID) FUNC2(instance_set_visible, RID, bool) + FUNC5(instance_set_use_lightmap, RID, RID, RID, int, const Rect2 &) FUNC2(instance_set_custom_aabb, RID, AABB) diff --git a/servers/rendering_server.cpp b/servers/rendering_server.cpp index ee61877e1..63f41b2d4 100644 --- a/servers/rendering_server.cpp +++ b/servers/rendering_server.cpp @@ -2023,6 +2023,19 @@ void RenderingServer::_bind_methods() { ClassDB::bind_method(D_METHOD("reflection_probe_set_enable_shadows", "probe", "enable"), &RenderingServer::reflection_probe_set_enable_shadows); ClassDB::bind_method(D_METHOD("reflection_probe_set_cull_mask", "probe", "layers"), &RenderingServer::reflection_probe_set_cull_mask); + ClassDB::bind_method(D_METHOD("lightmap_capture_create"), &RenderingServer::lightmap_capture_create); + ClassDB::bind_method(D_METHOD("lightmap_capture_set_bounds", "capture", "bounds"), &RenderingServer::lightmap_capture_set_bounds); + ClassDB::bind_method(D_METHOD("lightmap_capture_get_bounds", "capture"), &RenderingServer::lightmap_capture_get_bounds); + ClassDB::bind_method(D_METHOD("lightmap_capture_set_octree", "capture", "octree"), &RenderingServer::lightmap_capture_set_octree); + ClassDB::bind_method(D_METHOD("lightmap_capture_set_octree_cell_transform", "capture", "xform"), &RenderingServer::lightmap_capture_set_octree_cell_transform); + ClassDB::bind_method(D_METHOD("lightmap_capture_get_octree_cell_transform", "capture"), &RenderingServer::lightmap_capture_get_octree_cell_transform); + ClassDB::bind_method(D_METHOD("lightmap_capture_set_octree_cell_subdiv", "capture", "subdiv"), &RenderingServer::lightmap_capture_set_octree_cell_subdiv); + ClassDB::bind_method(D_METHOD("lightmap_capture_get_octree_cell_subdiv", "capture"), &RenderingServer::lightmap_capture_get_octree_cell_subdiv); + ClassDB::bind_method(D_METHOD("lightmap_capture_get_octree", "capture"), &RenderingServer::lightmap_capture_get_octree); + ClassDB::bind_method(D_METHOD("lightmap_capture_set_energy", "capture", "energy"), &RenderingServer::lightmap_capture_set_energy); + ClassDB::bind_method(D_METHOD("lightmap_capture_get_energy", "capture"), &RenderingServer::lightmap_capture_get_energy); + ClassDB::bind_method(D_METHOD("lightmap_capture_set_interior", "capture", "interior"), &RenderingServer::lightmap_capture_set_interior); + ClassDB::bind_method(D_METHOD("lightmap_capture_is_interior", "capture"), &RenderingServer::lightmap_capture_is_interior); #endif ClassDB::bind_method(D_METHOD("camera_create"), &RenderingServer::camera_create); @@ -2111,6 +2124,7 @@ void RenderingServer::_bind_methods() { ClassDB::bind_method(D_METHOD("instance_set_blend_shape_weight", "instance", "shape", "weight"), &RenderingServer::instance_set_blend_shape_weight); ClassDB::bind_method(D_METHOD("instance_set_surface_material", "instance", "surface", "material"), &RenderingServer::instance_set_surface_material); ClassDB::bind_method(D_METHOD("instance_set_visible", "instance", "visible"), &RenderingServer::instance_set_visible); + ClassDB::bind_method(D_METHOD("instance_set_use_lightmap", "instance", "lightmap_instance", "lightmap", "lightmap_slice", "lightmap_uv_rect"), &RenderingServer::instance_set_use_lightmap, DEFVAL(-1), DEFVAL(Rect2(0, 0, 1, 1))); ClassDB::bind_method(D_METHOD("instance_set_custom_aabb", "instance", "aabb"), &RenderingServer::instance_set_custom_aabb); ClassDB::bind_method(D_METHOD("instance_attach_skeleton", "instance", "skeleton"), &RenderingServer::instance_attach_skeleton); ClassDB::bind_method(D_METHOD("instance_set_exterior", "instance", "enabled"), &RenderingServer::instance_set_exterior); @@ -2403,9 +2417,11 @@ void RenderingServer::_bind_methods() { BIND_ENUM_CONSTANT(INSTANCE_IMMEDIATE); BIND_ENUM_CONSTANT(INSTANCE_LIGHT); BIND_ENUM_CONSTANT(INSTANCE_REFLECTION_PROBE); + BIND_ENUM_CONSTANT(INSTANCE_LIGHTMAP_CAPTURE); BIND_ENUM_CONSTANT(INSTANCE_MAX); BIND_ENUM_CONSTANT(INSTANCE_GEOMETRY_MASK); + BIND_ENUM_CONSTANT(INSTANCE_FLAG_USE_BAKED_LIGHT); BIND_ENUM_CONSTANT(INSTANCE_FLAG_DRAW_NEXT_FRAME_IF_VISIBLE); BIND_ENUM_CONSTANT(INSTANCE_FLAG_MAX); diff --git a/servers/rendering_server.h b/servers/rendering_server.h index a90e042ca..65ef0b492 100644 --- a/servers/rendering_server.h +++ b/servers/rendering_server.h @@ -526,6 +526,22 @@ public: virtual void reflection_probe_set_cull_mask(RID p_probe, uint32_t p_layers) = 0; virtual void reflection_probe_set_resolution(RID p_probe, int p_resolution) = 0; + /* LIGHTMAP CAPTURE */ + + virtual RID lightmap_capture_create() = 0; + virtual void lightmap_capture_set_bounds(RID p_capture, const AABB &p_bounds) = 0; + virtual AABB lightmap_capture_get_bounds(RID p_capture) const = 0; + virtual void lightmap_capture_set_octree(RID p_capture, const PoolVector &p_octree) = 0; + virtual void lightmap_capture_set_octree_cell_transform(RID p_capture, const Transform &p_xform) = 0; + virtual Transform lightmap_capture_get_octree_cell_transform(RID p_capture) const = 0; + virtual void lightmap_capture_set_octree_cell_subdiv(RID p_capture, int p_subdiv) = 0; + virtual int lightmap_capture_get_octree_cell_subdiv(RID p_capture) const = 0; + virtual PoolVector lightmap_capture_get_octree(RID p_capture) const = 0; + virtual void lightmap_capture_set_energy(RID p_capture, float p_energy) = 0; + virtual float lightmap_capture_get_energy(RID p_capture) const = 0; + virtual void lightmap_capture_set_interior(RID p_capture, bool p_interior) = 0; + virtual bool lightmap_capture_is_interior(RID p_capture) const = 0; + /* CAMERA API */ virtual RID camera_create() = 0; @@ -757,6 +773,7 @@ public: INSTANCE_IMMEDIATE, INSTANCE_LIGHT, INSTANCE_REFLECTION_PROBE, + INSTANCE_LIGHTMAP_CAPTURE, INSTANCE_MAX, INSTANCE_GEOMETRY_MASK = (1 << INSTANCE_MESH) | (1 << INSTANCE_MULTIMESH) | (1 << INSTANCE_IMMEDIATE) @@ -778,6 +795,8 @@ public: virtual void instance_set_surface_material(RID p_instance, int p_surface, RID p_material) = 0; virtual void instance_set_visible(RID p_instance, bool p_visible) = 0; + virtual void instance_set_use_lightmap(RID p_instance, RID p_lightmap_instance, RID p_lightmap, int p_lightmap_slice, const Rect2 &p_lightmap_uv_rect) = 0; + virtual void instance_set_custom_aabb(RID p_instance, AABB aabb) = 0; virtual void instance_attach_skeleton(RID p_instance, RID p_skeleton) = 0; @@ -874,7 +893,8 @@ public: Array _instances_cull_convex_bind(const Array &p_convex, RID p_scenario = RID()) const; enum InstanceFlags { - INSTANCE_FLAG_DRAW_NEXT_FRAME_IF_VISIBLE, + INSTANCE_FLAG_USE_BAKED_LIGHT, + INSTANCE_FLAG_DRAW_NEXT_FRAME_IF_VISIBLE, INSTANCE_FLAG_MAX };