summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJuan Linietsky <reduzio@gmail.com>2017-12-14 08:59:46 -0300
committerJuan Linietsky <reduzio@gmail.com>2017-12-14 09:01:27 -0300
commitf3ad14224e27f2a25196575e3c43ebc792c90894 (patch)
tree84e60bc2fafda809f51bd0892e39205da9423068
parentaa6772d7abb3ff1ff77b1b658617904bb0af1fbb (diff)
-Add lightmapper
-Fixes to unwrapper (remove degenerates), makes Thekla not crash -Added optional cancel button in EditorProgress -Added function to force processing of events (needed for cancel button)
-rw-r--r--SConstruct1
-rw-r--r--core/os/os.h1
-rw-r--r--drivers/gles3/rasterizer_scene_gles3.cpp28
-rw-r--r--drivers/gles3/rasterizer_scene_gles3.h28
-rw-r--r--drivers/gles3/rasterizer_storage_gles3.cpp117
-rw-r--r--drivers/gles3/rasterizer_storage_gles3.h32
-rw-r--r--drivers/gles3/shaders/scene.glsl76
-rw-r--r--editor/editor_node.cpp10
-rw-r--r--editor/editor_node.h10
-rw-r--r--editor/import/resource_importer_scene.cpp4
-rw-r--r--editor/plugins/baked_lightmap_editor_plugin.cpp95
-rw-r--r--editor/plugins/baked_lightmap_editor_plugin.h39
-rw-r--r--editor/progress_dialog.cpp36
-rw-r--r--editor/progress_dialog.h11
-rw-r--r--editor/spatial_editor_gizmos.cpp121
-rw-r--r--editor/spatial_editor_gizmos.h17
-rw-r--r--modules/thekla_unwrap/register_types.cpp7
-rw-r--r--platform/osx/os_osx.h2
-rw-r--r--platform/osx/os_osx.mm7
-rw-r--r--platform/windows/detect.py7
-rw-r--r--platform/windows/os_windows.cpp4
-rw-r--r--platform/windows/os_windows.h2
-rw-r--r--platform/x11/detect.py6
-rw-r--r--platform/x11/os_x11.cpp7
-rw-r--r--platform/x11/os_x11.h1
-rw-r--r--scene/3d/baked_lightmap.cpp717
-rw-r--r--scene/3d/baked_lightmap.h189
-rw-r--r--scene/3d/gi_probe.cpp1028
-rw-r--r--scene/3d/gi_probe.h80
-rw-r--r--scene/3d/light.cpp17
-rw-r--r--scene/3d/light.h11
-rw-r--r--scene/3d/voxel_light_baker.cpp2373
-rw-r--r--scene/3d/voxel_light_baker.h148
-rw-r--r--scene/main/node.cpp2
-rw-r--r--scene/register_scene_types.cpp3
-rw-r--r--scene/resources/material.cpp19
-rw-r--r--scene/resources/material.h3
-rw-r--r--scene/resources/mesh.cpp28
-rw-r--r--servers/visual/rasterizer.h31
-rw-r--r--servers/visual/visual_server_raster.h19
-rw-r--r--servers/visual/visual_server_scene.cpp332
-rw-r--r--servers/visual/visual_server_scene.h18
-rw-r--r--servers/visual/visual_server_wrap_mt.h18
-rw-r--r--servers/visual_server.h17
44 files changed, 4584 insertions, 1138 deletions
diff --git a/SConstruct b/SConstruct
index b3e0672c94..cf18bfe18a 100644
--- a/SConstruct
+++ b/SConstruct
@@ -168,6 +168,7 @@ opts.Add(BoolVariable('vsproj', "Generate Visual Studio Project.", False))
opts.Add(EnumVariable('warnings', "Set the level of warnings emitted during compilation", 'no', ('extra', 'all', 'moderate', 'no')))
opts.Add(BoolVariable('progress', "Show a progress indicator during build", True))
opts.Add(BoolVariable('dev', "If yes, alias for verbose=yes warnings=all", False))
+opts.Add(BoolVariable('openmp', "If yes, enable OpenMP", True))
# Thirdparty libraries
opts.Add(BoolVariable('builtin_enet', "Use the builtin enet library", True))
diff --git a/core/os/os.h b/core/os/os.h
index 979ad7e92a..24871c5995 100644
--- a/core/os/os.h
+++ b/core/os/os.h
@@ -442,6 +442,7 @@ public:
virtual int get_power_seconds_left();
virtual int get_power_percent_left();
+ virtual void force_process_input(){};
bool has_feature(const String &p_feature);
/**
diff --git a/drivers/gles3/rasterizer_scene_gles3.cpp b/drivers/gles3/rasterizer_scene_gles3.cpp
index d38ec2a1f9..13fbb5c293 100644
--- a/drivers/gles3/rasterizer_scene_gles3.cpp
+++ b/drivers/gles3/rasterizer_scene_gles3.cpp
@@ -1849,6 +1849,20 @@ void RasterizerSceneGLES3::_setup_light(RenderList::Element *e, const Transform
state.scene_shader.set_uniform(SceneShaderGLES3::GI_PROBE2_ENABLED, false);
}
+ } else if (!e->instance->lightmap_capture_data.empty()) {
+
+ glUniform4fv(state.scene_shader.get_uniform_location(SceneShaderGLES3::LIGHTMAP_CAPTURES), 12, (const GLfloat *)e->instance->lightmap_capture_data.ptr());
+ state.scene_shader.set_uniform(SceneShaderGLES3::LIGHTMAP_CAPTURE_SKY, false);
+
+ } else if (e->instance->lightmap.is_valid()) {
+ RasterizerStorageGLES3::Texture *lightmap = storage->texture_owner.getornull(e->instance->lightmap);
+ RasterizerStorageGLES3::LightmapCapture *capture = storage->lightmap_capture_data_owner.getornull(e->instance->lightmap_capture->base);
+
+ if (lightmap && capture) {
+ glActiveTexture(GL_TEXTURE0 + storage->config.max_texture_image_units - 9);
+ glBindTexture(GL_TEXTURE_2D, lightmap->tex_id);
+ state.scene_shader.set_uniform(SceneShaderGLES3::LIGHTMAP_ENERGY, capture->energy);
+ }
}
}
@@ -1971,6 +1985,8 @@ void RasterizerSceneGLES3::_render_list(RenderList::Element **p_elements, int p_
state.scene_shader.set_conditional(SceneShaderGLES3::SHADOW_MODE_PCF_5, false);
state.scene_shader.set_conditional(SceneShaderGLES3::SHADOW_MODE_PCF_13, false);
state.scene_shader.set_conditional(SceneShaderGLES3::USE_GI_PROBES, false);
+ state.scene_shader.set_conditional(SceneShaderGLES3::USE_LIGHTMAP_CAPTURE, false);
+ state.scene_shader.set_conditional(SceneShaderGLES3::USE_LIGHTMAP, false);
state.scene_shader.set_conditional(SceneShaderGLES3::USE_RADIANCE_MAP, false);
state.scene_shader.set_conditional(SceneShaderGLES3::USE_CONTACT_SHADOWS, false);
@@ -1978,6 +1994,8 @@ void RasterizerSceneGLES3::_render_list(RenderList::Element **p_elements, int p_
} else {
state.scene_shader.set_conditional(SceneShaderGLES3::USE_GI_PROBES, e->instance->gi_probe_instances.size() > 0);
+ state.scene_shader.set_conditional(SceneShaderGLES3::USE_LIGHTMAP, e->instance->lightmap.is_valid() && e->instance->gi_probe_instances.size() == 0);
+ state.scene_shader.set_conditional(SceneShaderGLES3::USE_LIGHTMAP_CAPTURE, !e->instance->lightmap_capture_data.empty() && !e->instance->lightmap.is_valid() && e->instance->gi_probe_instances.size() == 0);
state.scene_shader.set_conditional(SceneShaderGLES3::SHADELESS, false);
@@ -2148,6 +2166,8 @@ void RasterizerSceneGLES3::_render_list(RenderList::Element **p_elements, int p_
state.scene_shader.set_conditional(SceneShaderGLES3::SHADOW_MODE_PCF_5, false);
state.scene_shader.set_conditional(SceneShaderGLES3::SHADOW_MODE_PCF_13, false);
state.scene_shader.set_conditional(SceneShaderGLES3::USE_GI_PROBES, false);
+ state.scene_shader.set_conditional(SceneShaderGLES3::USE_LIGHTMAP, false);
+ state.scene_shader.set_conditional(SceneShaderGLES3::USE_LIGHTMAP_CAPTURE, false);
state.scene_shader.set_conditional(SceneShaderGLES3::USE_CONTACT_SHADOWS, false);
state.scene_shader.set_conditional(SceneShaderGLES3::USE_VERTEX_LIGHTING, false);
state.scene_shader.set_conditional(SceneShaderGLES3::USE_OPAQUE_PREPASS, false);
@@ -2274,6 +2294,14 @@ void RasterizerSceneGLES3::_add_geometry_with_material(RasterizerStorageGLES3::G
e->sort_key |= SORT_KEY_GI_PROBES_FLAG;
}
+ if (e->instance->lightmap.is_valid()) {
+ e->sort_key |= SORT_KEY_LIGHTMAP_FLAG;
+ }
+
+ if (!e->instance->lightmap_capture_data.empty()) {
+ e->sort_key |= SORT_KEY_LIGHTMAP_CAPTURE_FLAG;
+ }
+
e->sort_key |= uint64_t(p_material->render_priority + 128) << RenderList::SORT_KEY_PRIORITY_SHIFT;
} else {
e->sort_key |= uint64_t(e->instance->depth_layer) << RenderList::SORT_KEY_OPAQUE_DEPTH_LAYER_SHIFT;
diff --git a/drivers/gles3/rasterizer_scene_gles3.h b/drivers/gles3/rasterizer_scene_gles3.h
index ffbe10fb60..6df223c961 100644
--- a/drivers/gles3/rasterizer_scene_gles3.h
+++ b/drivers/gles3/rasterizer_scene_gles3.h
@@ -662,19 +662,21 @@ public:
SORT_KEY_OPAQUE_DEPTH_LAYER_SHIFT = 52,
SORT_KEY_OPAQUE_DEPTH_LAYER_MASK = 0xF,
//64 bits unsupported in MSVC
-#define SORT_KEY_UNSHADED_FLAG (uint64_t(1) << 51)
-#define SORT_KEY_NO_DIRECTIONAL_FLAG (uint64_t(1) << 50)
-#define SORT_KEY_GI_PROBES_FLAG (uint64_t(1) << 49)
-#define SORT_KEY_VERTEX_LIT_FLAG (uint64_t(1) << 48)
- SORT_KEY_SHADING_SHIFT = 48,
- SORT_KEY_SHADING_MASK = 15,
- //48-32 material index
- SORT_KEY_MATERIAL_INDEX_SHIFT = 32,
- //32-12 geometry index
- SORT_KEY_GEOMETRY_INDEX_SHIFT = 12,
- //bits 12-8 geometry type
- SORT_KEY_GEOMETRY_TYPE_SHIFT = 8,
- //bits 0-7 for flags
+#define SORT_KEY_UNSHADED_FLAG (uint64_t(1) << 49)
+#define SORT_KEY_NO_DIRECTIONAL_FLAG (uint64_t(1) << 48)
+#define SORT_KEY_LIGHTMAP_CAPTURE_FLAG (uint64_t(1) << 47)
+#define SORT_KEY_LIGHTMAP_FLAG (uint64_t(1) << 46)
+#define SORT_KEY_GI_PROBES_FLAG (uint64_t(1) << 45)
+#define SORT_KEY_VERTEX_LIT_FLAG (uint64_t(1) << 44)
+ SORT_KEY_SHADING_SHIFT = 44,
+ SORT_KEY_SHADING_MASK = 63,
+ //44-28 material index
+ SORT_KEY_MATERIAL_INDEX_SHIFT = 28,
+ //28-8 geometry index
+ SORT_KEY_GEOMETRY_INDEX_SHIFT = 8,
+ //bits 5-7 geometry type
+ SORT_KEY_GEOMETRY_TYPE_SHIFT = 5,
+ //bits 0-5 for flags
SORT_KEY_OPAQUE_PRE_PASS = 8,
SORT_KEY_CULL_DISABLED_FLAG = 4,
SORT_KEY_SKELETON_FLAG = 2,
diff --git a/drivers/gles3/rasterizer_storage_gles3.cpp b/drivers/gles3/rasterizer_storage_gles3.cpp
index cba9f08537..ee6c738a05 100644
--- a/drivers/gles3/rasterizer_storage_gles3.cpp
+++ b/drivers/gles3/rasterizer_storage_gles3.cpp
@@ -5216,6 +5216,104 @@ void RasterizerStorageGLES3::gi_probe_dynamic_data_update(RID p_gi_probe_data, i
//glTexImage3D(GL_TEXTURE_3D,p_mipmap,GL_RGBA8,gipd->width>>p_mipmap,gipd->height>>p_mipmap,gipd->depth>>p_mipmap,0,GL_RGBA,GL_UNSIGNED_BYTE,p_data);
//glTexImage3D(GL_TEXTURE_3D,p_mipmap,GL_RGBA8,gipd->width>>p_mipmap,gipd->height>>p_mipmap,gipd->depth>>p_mipmap,0,GL_RGBA,GL_UNSIGNED_BYTE,data.ptr());
}
+/////////////////////////////
+
+RID RasterizerStorageGLES3::lightmap_capture_create() {
+
+ LightmapCapture *capture = memnew(LightmapCapture);
+ return lightmap_capture_data_owner.make_rid(capture);
+}
+
+void RasterizerStorageGLES3::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();
+}
+AABB RasterizerStorageGLES3::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 RasterizerStorageGLES3::lightmap_capture_set_octree(RID p_capture, const PoolVector<uint8_t> &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<LightmapCaptureOctree>::Write w = capture->octree.write();
+ PoolVector<uint8_t>::Read r = p_octree.read();
+ copymem(w.ptr(), r.ptr(), p_octree.size());
+ }
+ capture->instance_change_notify();
+}
+PoolVector<uint8_t> RasterizerStorageGLES3::lightmap_capture_get_octree(RID p_capture) const {
+
+ const LightmapCapture *capture = lightmap_capture_data_owner.getornull(p_capture);
+ ERR_FAIL_COND_V(!capture, PoolVector<uint8_t>());
+
+ if (capture->octree.size() == 0)
+ return PoolVector<uint8_t>();
+
+ PoolVector<uint8_t> ret;
+ ret.resize(capture->octree.size() * sizeof(LightmapCaptureOctree));
+ {
+ PoolVector<LightmapCaptureOctree>::Read r = capture->octree.read();
+ PoolVector<uint8_t>::Write w = ret.write();
+ copymem(w.ptr(), r.ptr(), ret.size());
+ }
+
+ return ret;
+}
+
+void RasterizerStorageGLES3::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 RasterizerStorageGLES3::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 RasterizerStorageGLES3::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 RasterizerStorageGLES3::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 RasterizerStorageGLES3::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;
+}
+
+float RasterizerStorageGLES3::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;
+}
+
+const PoolVector<RasterizerStorage::LightmapCaptureOctree> *RasterizerStorageGLES3::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;
+}
///////
@@ -5817,6 +5915,10 @@ void RasterizerStorageGLES3::instance_add_dependency(RID p_base, RasterizerScene
inst = gi_probe_owner.getornull(p_base);
ERR_FAIL_COND(!inst);
} break;
+ case VS::INSTANCE_LIGHTMAP_CAPTURE: {
+ inst = lightmap_capture_data_owner.getornull(p_base);
+ ERR_FAIL_COND(!inst);
+ } break;
default: {
if (!inst) {
ERR_FAIL();
@@ -5860,6 +5962,10 @@ void RasterizerStorageGLES3::instance_remove_dependency(RID p_base, RasterizerSc
inst = gi_probe_owner.getornull(p_base);
ERR_FAIL_COND(!inst);
} break;
+ case VS::INSTANCE_LIGHTMAP_CAPTURE: {
+ inst = lightmap_capture_data_owner.getornull(p_base);
+ ERR_FAIL_COND(!inst);
+ } break;
default: {
if (!inst) {
@@ -6609,6 +6715,10 @@ VS::InstanceType RasterizerStorageGLES3::get_base_type(RID p_rid) const {
return VS::INSTANCE_GI_PROBE;
}
+ if (lightmap_capture_data_owner.owns(p_rid)) {
+ return VS::INSTANCE_LIGHTMAP_CAPTURE;
+ }
+
return VS::INSTANCE_NONE;
}
@@ -6795,6 +6905,13 @@ bool RasterizerStorageGLES3::free(RID p_rid) {
glDeleteTextures(1, &gi_probe_data->tex_id);
gi_probe_owner.free(p_rid);
memdelete(gi_probe_data);
+ } else if (lightmap_capture_data_owner.owns(p_rid)) {
+
+ // delete the texture
+ LightmapCapture *lightmap_capture = lightmap_capture_data_owner.get(p_rid);
+
+ gi_probe_owner.free(p_rid);
+ memdelete(lightmap_capture);
} else if (canvas_occluder_owner.owns(p_rid)) {
diff --git a/drivers/gles3/rasterizer_storage_gles3.h b/drivers/gles3/rasterizer_storage_gles3.h
index 25327af0a5..6647372688 100644
--- a/drivers/gles3/rasterizer_storage_gles3.h
+++ b/drivers/gles3/rasterizer_storage_gles3.h
@@ -1069,6 +1069,38 @@ public:
virtual RID gi_probe_dynamic_data_create(int p_width, int p_height, int p_depth, GIProbeCompression p_compression);
virtual void gi_probe_dynamic_data_update(RID p_gi_probe_data, int p_depth_slice, int p_slice_count, int p_mipmap, const void *p_data);
+ /* LIGHTMAP CAPTURE */
+
+ 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<uint8_t> &p_octree);
+ virtual PoolVector<uint8_t> 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 const PoolVector<LightmapCaptureOctree> *lightmap_capture_get_octree_ptr(RID p_capture) const;
+
+ struct LightmapCapture : public Instantiable {
+
+ PoolVector<LightmapCaptureOctree> octree;
+ AABB bounds;
+ Transform cell_xform;
+ int cell_subdiv;
+ float energy;
+ LightmapCapture() {
+ energy = 1.0;
+ cell_subdiv = 1;
+ }
+ };
+
+ mutable RID_Owner<LightmapCapture> lightmap_capture_data_owner;
+
/* PARTICLES */
struct Particles : public GeometryOwner {
diff --git a/drivers/gles3/shaders/scene.glsl b/drivers/gles3/shaders/scene.glsl
index fb0f06de01..9bc2bc079d 100644
--- a/drivers/gles3/shaders/scene.glsl
+++ b/drivers/gles3/shaders/scene.glsl
@@ -35,7 +35,7 @@ layout(location=3) in vec4 color_attrib;
layout(location=4) in vec2 uv_attrib;
#endif
-#if defined(ENABLE_UV2_INTERP)
+#if defined(ENABLE_UV2_INTERP) || defined(USE_LIGHTMAP)
layout(location=5) in vec2 uv2_attrib;
#endif
@@ -223,7 +223,7 @@ out vec4 color_interp;
out vec2 uv_interp;
#endif
-#if defined(ENABLE_UV2_INTERP)
+#if defined(ENABLE_UV2_INTERP) || defined (USE_LIGHTMAP)
out vec2 uv2_interp;
#endif
@@ -234,9 +234,6 @@ out vec3 binormal_interp;
#endif
-
-
-
#if defined(USE_MATERIAL)
layout(std140) uniform UniformData { //ubo:1
@@ -356,7 +353,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
@@ -549,7 +546,7 @@ in vec4 color_interp;
in vec2 uv_interp;
#endif
-#if defined(ENABLE_UV2_INTERP)
+#if defined(ENABLE_UV2_INTERP) || defined(USE_LIGHTMAP)
in vec2 uv2_interp;
#endif
@@ -1357,7 +1354,7 @@ void reflection_process(int idx, vec3 vertex, vec3 normal,vec3 binormal, vec3 ta
reflection_accum+=reflection;
}
-
+#ifndef USE_LIGHTMAP
if (reflections[idx].ambient.a>0.0) { //compute ambient using skybox
@@ -1403,8 +1400,20 @@ void reflection_process(int idx, vec3 vertex, vec3 normal,vec3 binormal, vec3 ta
ambient_accum+=ambient_out;
}
+#endif
}
+#ifdef USE_LIGHTMAP
+uniform mediump sampler2D lightmap; //texunit:-9
+uniform mediump float lightmap_energy;
+#endif
+
+#ifdef USE_LIGHTMAP_CAPTURE
+uniform mediump vec4[12] lightmap_captures;
+uniform bool lightmap_capture_sky;
+
+#endif
+
#ifdef USE_GI_PROBES
uniform mediump sampler3D gi_probe1; //texunit:-9
@@ -1632,7 +1641,7 @@ void main() {
vec2 uv = uv_interp;
#endif
-#if defined(ENABLE_UV2_INTERP)
+#if defined(ENABLE_UV2_INTERP) || defined (USE_LIGHTMAP)
vec2 uv2 = uv2_interp;
#endif
@@ -1745,7 +1754,7 @@ FRAGMENT_SHADER_CODE
//vec3 radiance = textureLod(radiance_cube, r, lod).xyz * ( brdf.x + brdf.y);
}
-
+#ifndef USE_LIGHTMAP
{
vec3 ambient_dir=normalize((radiance_inverse_xform * vec4(normal,0.0)).xyz);
@@ -1754,6 +1763,7 @@ FRAGMENT_SHADER_CODE
ambient_light=mix(ambient_light_color.rgb,env_ambient,radiance_ambient_contribution);
//ambient_light=vec3(0.0,0.0,0.0);
}
+#endif
}
#else
@@ -1938,6 +1948,48 @@ FRAGMENT_SHADER_CODE
#endif
+#ifdef USE_LIGHTMAP
+ ambient_light = texture(lightmap,uv2).rgb * lightmap_energy;
+#endif
+
+#ifdef USE_LIGHTMAP_CAPTURE
+ {
+ vec3 cone_dirs[12] = vec3[] (
+ vec3(0, 0, 1),
+ vec3(0.866025, 0, 0.5),
+ vec3(0.267617, 0.823639, 0.5),
+ vec3(-0.700629, 0.509037, 0.5),
+ vec3(-0.700629, -0.509037, 0.5),
+ vec3(0.267617, -0.823639, 0.5),
+ vec3(0, 0, -1),
+ vec3(0.866025, 0, -0.5),
+ vec3(0.267617, 0.823639, -0.5),
+ vec3(-0.700629, 0.509037, -0.5),
+ vec3(-0.700629, -0.509037, -0.5),
+ 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;
+
+ if (lightmap_capture_sky) {
+ ambient_light = mix( ambient_light, captured.rgb, captured.a);
+ } else {
+ ambient_light = captured.rgb;
+ }
+
+ }
+#endif
+
#ifdef USE_FORWARD_LIGHTING
@@ -1952,11 +2004,11 @@ FRAGMENT_SHADER_CODE
} else {
specular_light+=env_reflection_light;
}
-
+#ifndef USE_LIGHTMAP
if (ambient_accum.a>0.0) {
ambient_light+=ambient_accum.rgb/ambient_accum.a;
}
-
+#endif
#ifdef USE_VERTEX_LIGHTING
diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp
index 27ed53bb42..d0d13fdbcf 100644
--- a/editor/editor_node.cpp
+++ b/editor/editor_node.cpp
@@ -67,6 +67,7 @@
#include "editor/plugins/animation_player_editor_plugin.h"
#include "editor/plugins/animation_tree_editor_plugin.h"
#include "editor/plugins/asset_library_editor_plugin.h"
+#include "editor/plugins/baked_lightmap_editor_plugin.h"
#include "editor/plugins/camera_editor_plugin.h"
#include "editor/plugins/canvas_item_editor_plugin.h"
#include "editor/plugins/collision_polygon_2d_editor_plugin.h"
@@ -3384,14 +3385,14 @@ void EditorNode::stop_child_process() {
_menu_option_confirm(RUN_STOP, false);
}
-void EditorNode::progress_add_task(const String &p_task, const String &p_label, int p_steps) {
+void EditorNode::progress_add_task(const String &p_task, const String &p_label, int p_steps, bool p_can_cancel) {
- singleton->progress_dialog->add_task(p_task, p_label, p_steps);
+ singleton->progress_dialog->add_task(p_task, p_label, p_steps, p_can_cancel);
}
-void EditorNode::progress_task_step(const String &p_task, const String &p_state, int p_step, bool p_force_refresh) {
+bool EditorNode::progress_task_step(const String &p_task, const String &p_state, int p_step, bool p_force_refresh) {
- singleton->progress_dialog->task_step(p_task, p_state, p_step, p_force_refresh);
+ return singleton->progress_dialog->task_step(p_task, p_state, p_step, p_force_refresh);
}
void EditorNode::progress_end_task(const String &p_task) {
@@ -5659,6 +5660,7 @@ EditorNode::EditorNode() {
add_editor_plugin(memnew(TextureRegionEditorPlugin(this)));
add_editor_plugin(memnew(Particles2DEditorPlugin(this)));
add_editor_plugin(memnew(GIProbeEditorPlugin(this)));
+ add_editor_plugin(memnew(BakedLightmapEditorPlugin(this)));
add_editor_plugin(memnew(Path2DEditorPlugin(this)));
add_editor_plugin(memnew(PathEditorPlugin(this)));
add_editor_plugin(memnew(Line2DEditorPlugin(this)));
diff --git a/editor/editor_node.h b/editor/editor_node.h
index 658d5dc0ae..e7ef9eefb5 100644
--- a/editor/editor_node.h
+++ b/editor/editor_node.h
@@ -745,8 +745,8 @@ public:
static void add_io_error(const String &p_error);
- static void progress_add_task(const String &p_task, const String &p_label, int p_steps);
- static void progress_task_step(const String &p_task, const String &p_state, int p_step = -1, bool p_force_refresh = true);
+ static void progress_add_task(const String &p_task, const String &p_label, int p_steps, bool p_can_cancel = false);
+ static bool progress_task_step(const String &p_task, const String &p_state, int p_step = -1, bool p_force_refresh = true);
static void progress_end_task(const String &p_task);
static void progress_add_task_bg(const String &p_task, const String &p_label, int p_steps);
@@ -807,9 +807,9 @@ public:
struct EditorProgress {
String task;
- void step(const String &p_state, int p_step = -1, bool p_force_refresh = true) { EditorNode::progress_task_step(task, p_state, p_step, p_force_refresh); }
- EditorProgress(const String &p_task, const String &p_label, int p_amount) {
- EditorNode::progress_add_task(p_task, p_label, p_amount);
+ bool step(const String &p_state, int p_step = -1, bool p_force_refresh = true) { return EditorNode::progress_task_step(task, p_state, p_step, p_force_refresh); }
+ EditorProgress(const String &p_task, const String &p_label, int p_amount, bool p_can_cancel = false) {
+ EditorNode::progress_add_task(p_task, p_label, p_amount, p_can_cancel);
task = p_task;
}
~EditorProgress() { EditorNode::progress_end_task(task); }
diff --git a/editor/import/resource_importer_scene.cpp b/editor/import/resource_importer_scene.cpp
index 08d2897250..ed7c6dba79 100644
--- a/editor/import/resource_importer_scene.cpp
+++ b/editor/import/resource_importer_scene.cpp
@@ -1165,8 +1165,8 @@ void ResourceImporterScene::get_import_options(List<ImportOption> *r_options, in
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "meshes/compress"), true));
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "meshes/ensure_tangents"), true));
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "meshes/storage", PROPERTY_HINT_ENUM, "Built-In,Files"), meshes_out ? 1 : 0));
- r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "meshes/light_baking", PROPERTY_HINT_ENUM, "Disabled,Enable,Gen Lightmaps"), 0));
- r_options->push_back(ImportOption(PropertyInfo(Variant::REAL, "meshes/lightmap_texel_size", PROPERTY_HINT_RANGE, "0.001,100,0.001"), 0.05));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "meshes/light_baking", PROPERTY_HINT_ENUM, "Disabled,Enable,Gen Lightmaps", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), 0));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::REAL, "meshes/lightmap_texel_size", PROPERTY_HINT_RANGE, "0.001,100,0.001"), 0.1));
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "external_files/store_in_subdir"), false));
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "animation/import", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), true));
r_options->push_back(ImportOption(PropertyInfo(Variant::REAL, "animation/fps", PROPERTY_HINT_RANGE, "1,120,1"), 15));
diff --git a/editor/plugins/baked_lightmap_editor_plugin.cpp b/editor/plugins/baked_lightmap_editor_plugin.cpp
new file mode 100644
index 0000000000..9ae3a4c771
--- /dev/null
+++ b/editor/plugins/baked_lightmap_editor_plugin.cpp
@@ -0,0 +1,95 @@
+#include "baked_lightmap_editor_plugin.h"
+
+void BakedLightmapEditorPlugin::_bake() {
+
+ if (lightmap) {
+ BakedLightmap::BakeError err;
+ if (get_tree()->get_edited_scene_root() && get_tree()->get_edited_scene_root() == lightmap) {
+ err = lightmap->bake(lightmap);
+ } else {
+ err = lightmap->bake(lightmap->get_parent());
+ }
+
+ switch (err) {
+ case BakedLightmap::BAKE_ERROR_NO_SAVE_PATH:
+ EditorNode::get_singleton()->show_warning(TTR("Can't determine a save path for lightmap images.\nSave your scene (for images to be saved in the same dir), or pick a save path from the BakedLightmap properties."));
+ break;
+ case BakedLightmap::BAKE_ERROR_NO_MESHES:
+ EditorNode::get_singleton()->show_warning(TTR("No meshes to bake. Make sure they contain an UV2 channel and that the 'Bake Light' flag is on."));
+ break;
+ case BakedLightmap::BAKE_ERROR_CANT_CREATE_IMAGE:
+ EditorNode::get_singleton()->show_warning(TTR("Failed creating lightmap images, make sure path is writable."));
+ break;
+ defaut : {}
+ }
+ }
+}
+
+void BakedLightmapEditorPlugin::edit(Object *p_object) {
+
+ BakedLightmap *s = Object::cast_to<BakedLightmap>(p_object);
+ if (!s)
+ return;
+
+ lightmap = s;
+}
+
+bool BakedLightmapEditorPlugin::handles(Object *p_object) const {
+
+ return p_object->is_class("BakedLightmap");
+}
+
+void BakedLightmapEditorPlugin::make_visible(bool p_visible) {
+
+ if (p_visible) {
+ bake->show();
+ } else {
+
+ bake->hide();
+ }
+}
+
+EditorProgress *BakedLightmapEditorPlugin::tmp_progress = NULL;
+
+void BakedLightmapEditorPlugin::bake_func_begin(int p_steps) {
+
+ ERR_FAIL_COND(tmp_progress != NULL);
+
+ tmp_progress = memnew(EditorProgress("bake_lightmaps", TTR("Bake Lightmaps"), p_steps, true));
+}
+
+bool BakedLightmapEditorPlugin::bake_func_step(int p_step, const String &p_description) {
+
+ ERR_FAIL_COND_V(tmp_progress == NULL, false);
+ return tmp_progress->step(p_description, p_step);
+}
+
+void BakedLightmapEditorPlugin::bake_func_end() {
+ ERR_FAIL_COND(tmp_progress == NULL);
+ memdelete(tmp_progress);
+ tmp_progress = NULL;
+}
+
+void BakedLightmapEditorPlugin::_bind_methods() {
+
+ ClassDB::bind_method("_bake", &BakedLightmapEditorPlugin::_bake);
+}
+
+BakedLightmapEditorPlugin::BakedLightmapEditorPlugin(EditorNode *p_node) {
+
+ editor = p_node;
+ bake = memnew(Button);
+ bake->set_icon(editor->get_gui_base()->get_icon("BakedLight", "EditorIcons"));
+ bake->set_text(TTR("Bake Lightmaps"));
+ bake->hide();
+ bake->connect("pressed", this, "_bake");
+ add_control_to_container(CONTAINER_SPATIAL_EDITOR_MENU, bake);
+ lightmap = NULL;
+
+ BakedLightmap::bake_begin_function = bake_func_begin;
+ BakedLightmap::bake_step_function = bake_func_step;
+ BakedLightmap::bake_end_function = bake_func_end;
+}
+
+BakedLightmapEditorPlugin::~BakedLightmapEditorPlugin() {
+}
diff --git a/editor/plugins/baked_lightmap_editor_plugin.h b/editor/plugins/baked_lightmap_editor_plugin.h
new file mode 100644
index 0000000000..d64c33884a
--- /dev/null
+++ b/editor/plugins/baked_lightmap_editor_plugin.h
@@ -0,0 +1,39 @@
+#ifndef BAKED_LIGHTMAP_EDITOR_PLUGIN_H
+#define BAKED_LIGHTMAP_EDITOR_PLUGIN_H
+
+#include "editor/editor_node.h"
+#include "editor/editor_plugin.h"
+#include "scene/3d/baked_lightmap.h"
+#include "scene/resources/material.h"
+
+class BakedLightmapEditorPlugin : public EditorPlugin {
+
+ GDCLASS(BakedLightmapEditorPlugin, EditorPlugin);
+
+ BakedLightmap *lightmap;
+
+ Button *bake;
+ EditorNode *editor;
+
+ static EditorProgress *tmp_progress;
+ static void bake_func_begin(int p_steps);
+ static bool bake_func_step(int p_step, const String &p_description);
+ static void bake_func_end();
+
+ void _bake();
+
+protected:
+ static void _bind_methods();
+
+public:
+ virtual String get_name() const { return "BakedLightmap"; }
+ bool has_main_screen() const { return false; }
+ virtual void edit(Object *p_object);
+ virtual bool handles(Object *p_object) const;
+ virtual void make_visible(bool p_visible);
+
+ BakedLightmapEditorPlugin(EditorNode *p_node);
+ ~BakedLightmapEditorPlugin();
+};
+
+#endif // BAKED_LIGHTMAP_EDITOR_PLUGIN_H
diff --git a/editor/progress_dialog.cpp b/editor/progress_dialog.cpp
index 09f5375bb4..2c2e5a7c9b 100644
--- a/editor/progress_dialog.cpp
+++ b/editor/progress_dialog.cpp
@@ -163,7 +163,7 @@ void ProgressDialog::_popup() {
popup_centered(ms);
}
-void ProgressDialog::add_task(const String &p_task, const String &p_label, int p_steps) {
+void ProgressDialog::add_task(const String &p_task, const String &p_label, int p_steps, bool p_can_cancel) {
ERR_FAIL_COND(tasks.has(p_task));
Task t;
@@ -180,17 +180,24 @@ void ProgressDialog::add_task(const String &p_task, const String &p_label, int p
main->add_child(t.vb);
tasks[p_task] = t;
+ if (p_can_cancel) {
+ cancel_hb->show();
+ } else {
+ cancel_hb->hide();
+ }
+ cancel_hb->raise();
+ cancelled = false;
_popup();
}
-void ProgressDialog::task_step(const String &p_task, const String &p_state, int p_step, bool p_force_redraw) {
+bool ProgressDialog::task_step(const String &p_task, const String &p_state, int p_step, bool p_force_redraw) {
- ERR_FAIL_COND(!tasks.has(p_task));
+ ERR_FAIL_COND_V(!tasks.has(p_task), cancelled);
if (!p_force_redraw) {
uint64_t tus = OS::get_singleton()->get_ticks_usec();
if (tus - last_progress_tick < 50000) //50ms
- return;
+ return cancelled;
}
Task &t = tasks[p_task];
@@ -201,7 +208,11 @@ void ProgressDialog::task_step(const String &p_task, const String &p_state, int
t.state->set_text(p_state);
last_progress_tick = OS::get_singleton()->get_ticks_usec();
+ if (cancel_hb->is_visible()) {
+ OS::get_singleton()->force_process_input();
+ }
Main::iteration(); // this will not work on a lot of platforms, so it's only meant for the editor
+ return cancelled;
}
void ProgressDialog::end_task(const String &p_task) {
@@ -218,6 +229,14 @@ void ProgressDialog::end_task(const String &p_task) {
_popup();
}
+void ProgressDialog::_cancel_pressed() {
+ cancelled = true;
+}
+
+void ProgressDialog::_bind_methods() {
+ ClassDB::bind_method("_cancel_pressed", &ProgressDialog::_cancel_pressed);
+}
+
ProgressDialog::ProgressDialog() {
main = memnew(VBoxContainer);
@@ -226,4 +245,13 @@ ProgressDialog::ProgressDialog() {
set_exclusive(true);
last_progress_tick = 0;
singleton = this;
+ cancel_hb = memnew(HBoxContainer);
+ main->add_child(cancel_hb);
+ cancel_hb->hide();
+ cancel = memnew(Button);
+ cancel_hb->add_spacer();
+ cancel_hb->add_child(cancel);
+ cancel->set_text(TTR("Cancel"));
+ cancel_hb->add_spacer();
+ cancel->connect("pressed", this, "_cancel_pressed");
}
diff --git a/editor/progress_dialog.h b/editor/progress_dialog.h
index 8ac0907145..b13ea606bc 100644
--- a/editor/progress_dialog.h
+++ b/editor/progress_dialog.h
@@ -31,6 +31,7 @@
#define PROGRESS_DIALOG_H
#include "scene/gui/box_container.h"
+#include "scene/gui/button.h"
#include "scene/gui/label.h"
#include "scene/gui/popup.h"
#include "scene/gui/progress_bar.h"
@@ -76,6 +77,8 @@ class ProgressDialog : public Popup {
ProgressBar *progress;
Label *state;
};
+ HBoxContainer *cancel_hb;
+ Button *cancel;
Map<String, Task> tasks;
VBoxContainer *main;
@@ -84,13 +87,17 @@ class ProgressDialog : public Popup {
static ProgressDialog *singleton;
void _popup();
+ void _cancel_pressed();
+ bool cancelled;
+
protected:
void _notification(int p_what);
+ static void _bind_methods();
public:
static ProgressDialog *get_singleton() { return singleton; }
- void add_task(const String &p_task, const String &p_label, int p_steps);
- void task_step(const String &p_task, const String &p_state, int p_step = -1, bool p_force_redraw = true);
+ void add_task(const String &p_task, const String &p_label, int p_steps, bool p_can_cancel = false);
+ bool task_step(const String &p_task, const String &p_state, int p_step = -1, bool p_force_redraw = true);
void end_task(const String &p_task);
ProgressDialog();
diff --git a/editor/spatial_editor_gizmos.cpp b/editor/spatial_editor_gizmos.cpp
index f785b3e198..8fddd2170f 100644
--- a/editor/spatial_editor_gizmos.cpp
+++ b/editor/spatial_editor_gizmos.cpp
@@ -2753,7 +2753,122 @@ GIProbeGizmo::GIProbeGizmo(GIProbe *p_probe) {
}
////////
+////////
+
+///
+
+String BakedIndirectLightGizmo::get_handle_name(int p_idx) const {
+
+ switch (p_idx) {
+ case 0: return "Extents X";
+ case 1: return "Extents Y";
+ case 2: return "Extents Z";
+ }
+
+ return "";
+}
+Variant BakedIndirectLightGizmo::get_handle_value(int p_idx) const {
+
+ return baker->get_extents();
+}
+void BakedIndirectLightGizmo::set_handle(int p_idx, Camera *p_camera, const Point2 &p_point) {
+
+ Transform gt = baker->get_global_transform();
+ //gt.orthonormalize();
+ Transform gi = gt.affine_inverse();
+
+ Vector3 extents = baker->get_extents();
+
+ Vector3 ray_from = p_camera->project_ray_origin(p_point);
+ Vector3 ray_dir = p_camera->project_ray_normal(p_point);
+
+ Vector3 sg[2] = { gi.xform(ray_from), gi.xform(ray_from + ray_dir * 16384) };
+
+ Vector3 axis;
+ axis[p_idx] = 1.0;
+
+ Vector3 ra, rb;
+ Geometry::get_closest_points_between_segments(Vector3(), axis * 16384, sg[0], sg[1], ra, rb);
+ float d = ra[p_idx];
+ if (d < 0.001)
+ d = 0.001;
+
+ extents[p_idx] = d;
+ baker->set_extents(extents);
+}
+
+void BakedIndirectLightGizmo::commit_handle(int p_idx, const Variant &p_restore, bool p_cancel) {
+
+ Vector3 restore = p_restore;
+
+ if (p_cancel) {
+ baker->set_extents(restore);
+ return;
+ }
+
+ UndoRedo *ur = SpatialEditor::get_singleton()->get_undo_redo();
+ ur->create_action(TTR("Change Probe Extents"));
+ ur->add_do_method(baker, "set_extents", baker->get_extents());
+ ur->add_undo_method(baker, "set_extents", restore);
+ ur->commit_action();
+}
+void BakedIndirectLightGizmo::redraw() {
+
+ Color gizmo_color = EDITOR_GET("editors/3d_gizmos/gizmo_colors/baked_indirect_light");
+ Ref<Material> material = create_material("baked_indirect_light_material", gizmo_color);
+ Ref<Material> icon = create_icon_material("baked_indirect_light_icon", SpatialEditor::get_singleton()->get_icon("GizmoGIProbe", "EditorIcons"));
+ Color gizmo_color_internal = gizmo_color;
+ gizmo_color_internal.a = 0.1;
+ Ref<Material> material_internal = create_material("baked_indirect_light_internal_material", gizmo_color_internal);
+
+ clear();
+
+ Vector<Vector3> lines;
+ Vector3 extents = baker->get_extents();
+
+ static const int subdivs[BakedLightmap::SUBDIV_MAX] = { 64, 128, 256, 512 };
+
+ AABB aabb = AABB(-extents, extents * 2);
+ int subdiv = subdivs[baker->get_bake_subdiv()];
+ float cell_size = aabb.get_longest_axis_size() / subdiv;
+
+ for (int i = 0; i < 12; i++) {
+ Vector3 a, b;
+ aabb.get_edge(i, a, b);
+ lines.push_back(a);
+ lines.push_back(b);
+ }
+
+ add_lines(lines, material);
+ add_collision_segments(lines);
+
+ Vector<Vector3> handles;
+
+ for (int i = 0; i < 3; i++) {
+
+ Vector3 ax;
+ ax[i] = aabb.position[i] + aabb.size[i];
+ handles.push_back(ax);
+ }
+
+ if (is_selected()) {
+
+ gizmo_color.a = 0.1;
+ Ref<Material> solid_material = create_material("baked_indirect_light_solid_material", gizmo_color);
+ add_solid_box(solid_material, aabb.get_size());
+ }
+
+ add_unscaled_billboard(icon, 0.05);
+ add_handles(handles);
+}
+BakedIndirectLightGizmo::BakedIndirectLightGizmo(BakedLightmap *p_baker) {
+
+ baker = p_baker;
+ set_spatial_node(p_baker);
+}
+
+////////
void NavigationMeshSpatialGizmo::redraw() {
Ref<Material> edge_material = create_material("navigation_material", EDITOR_GET("editors/3d_gizmos/gizmo_colors/navigation_edge"));
@@ -3409,6 +3524,11 @@ Ref<SpatialEditorGizmo> SpatialEditorGizmos::get_gizmo(Spatial *p_spatial) {
Ref<GIProbeGizmo> misg = memnew(GIProbeGizmo(Object::cast_to<GIProbe>(p_spatial)));
return misg;
}
+ if (Object::cast_to<BakedLightmap>(p_spatial)) {
+
+ Ref<BakedIndirectLightGizmo> misg = memnew(BakedIndirectLightGizmo(Object::cast_to<BakedLightmap>(p_spatial)));
+ return misg;
+ }
if (Object::cast_to<VehicleWheel>(p_spatial)) {
@@ -3495,6 +3615,7 @@ SpatialEditorGizmos::SpatialEditorGizmos() {
EDITOR_DEF("editors/3d_gizmos/gizmo_colors/particles", Color(0.8, 0.7, 0.4));
EDITOR_DEF("editors/3d_gizmos/gizmo_colors/reflection_probe", Color(0.6, 1, 0.5));
EDITOR_DEF("editors/3d_gizmos/gizmo_colors/gi_probe", Color(0.5, 1, 0.6));
+ EDITOR_DEF("editors/3d_gizmos/gizmo_colors/baked_indirect_light", Color(0.5, 0.6, 1));
EDITOR_DEF("editors/3d_gizmos/gizmo_colors/shape", Color(0.5, 0.7, 1));
EDITOR_DEF("editors/3d_gizmos/gizmo_colors/joint", Color(0.5, 0.8, 1));
EDITOR_DEF("editors/3d_gizmos/gizmo_colors/navigation_edge", Color(0.5, 1, 1));
diff --git a/editor/spatial_editor_gizmos.h b/editor/spatial_editor_gizmos.h
index 751bad2b13..ea8a33d2c6 100644
--- a/editor/spatial_editor_gizmos.h
+++ b/editor/spatial_editor_gizmos.h
@@ -32,6 +32,7 @@
#include "editor/plugins/spatial_editor_plugin.h"
#include "scene/3d/audio_stream_player_3d.h"
+#include "scene/3d/baked_lightmap.h"
#include "scene/3d/camera.h"
#include "scene/3d/collision_polygon.h"
#include "scene/3d/collision_shape.h"
@@ -288,6 +289,22 @@ public:
GIProbeGizmo(GIProbe *p_probe = NULL);
};
+class BakedIndirectLightGizmo : public EditorSpatialGizmo {
+
+ GDCLASS(BakedIndirectLightGizmo, EditorSpatialGizmo);
+
+ BakedLightmap *baker;
+
+public:
+ virtual String get_handle_name(int p_idx) const;
+ virtual Variant get_handle_value(int p_idx) const;
+ virtual void set_handle(int p_idx, Camera *p_camera, const Point2 &p_point);
+ virtual void commit_handle(int p_idx, const Variant &p_restore, bool p_cancel = false);
+
+ void redraw();
+ BakedIndirectLightGizmo(BakedLightmap *p_baker = NULL);
+};
+
class CollisionShapeSpatialGizmo : public EditorSpatialGizmo {
GDCLASS(CollisionShapeSpatialGizmo, EditorSpatialGizmo);
diff --git a/modules/thekla_unwrap/register_types.cpp b/modules/thekla_unwrap/register_types.cpp
index 01b834f8cb..ab3203068f 100644
--- a/modules/thekla_unwrap/register_types.cpp
+++ b/modules/thekla_unwrap/register_types.cpp
@@ -42,7 +42,7 @@ bool thekla_mesh_lightmap_unwrap_callback(float p_texel_size, const float *p_ver
input_mesh.face_array[i].vertex_index[0] = p_indices[i * 3 + 0];
input_mesh.face_array[i].vertex_index[1] = p_indices[i * 3 + 1];
input_mesh.face_array[i].vertex_index[2] = p_indices[i * 3 + 2];
- printf("face %i - %i, %i, %i - mat %i\n", i, input_mesh.face_array[i].vertex_index[0], input_mesh.face_array[i].vertex_index[1], input_mesh.face_array[i].vertex_index[2], p_face_materials[i]);
+ //printf("face %i - %i, %i, %i - mat %i\n", i, input_mesh.face_array[i].vertex_index[0], input_mesh.face_array[i].vertex_index[1], input_mesh.face_array[i].vertex_index[2], p_face_materials[i]);
input_mesh.face_array[i].material_index = p_face_materials[i];
}
input_mesh.vertex_array = new Thekla::Atlas_Input_Vertex[p_vertex_count];
@@ -54,8 +54,8 @@ bool thekla_mesh_lightmap_unwrap_callback(float p_texel_size, const float *p_ver
}
input_mesh.vertex_array[i].uv[0] = 0;
input_mesh.vertex_array[i].uv[1] = 0;
- printf("vertex %i - %f, %f, %f\n", i, input_mesh.vertex_array[i].position[0], input_mesh.vertex_array[i].position[1], input_mesh.vertex_array[i].position[2]);
- printf("normal %i - %f, %f, %f\n", i, input_mesh.vertex_array[i].normal[0], input_mesh.vertex_array[i].normal[1], input_mesh.vertex_array[i].normal[2]);
+ //printf("vertex %i - %f, %f, %f\n", i, input_mesh.vertex_array[i].position[0], input_mesh.vertex_array[i].position[1], input_mesh.vertex_array[i].position[2]);
+ //printf("normal %i - %f, %f, %f\n", i, input_mesh.vertex_array[i].normal[0], input_mesh.vertex_array[i].normal[1], input_mesh.vertex_array[i].normal[2]);
}
input_mesh.face_count = p_index_count / 3;
input_mesh.vertex_count = p_vertex_count;
@@ -65,6 +65,7 @@ bool thekla_mesh_lightmap_unwrap_callback(float p_texel_size, const float *p_ver
Thekla::atlas_set_default_options(&options);
options.packer_options.witness.packing_quality = 1;
options.packer_options.witness.texel_area = 1.0 / p_texel_size;
+ options.packer_options.witness.conservative = true;
//generate
Thekla::Atlas_Error err;
diff --git a/platform/osx/os_osx.h b/platform/osx/os_osx.h
index 6543ca7dd2..33127a8c2a 100644
--- a/platform/osx/os_osx.h
+++ b/platform/osx/os_osx.h
@@ -228,6 +228,8 @@ public:
virtual Error move_to_trash(const String &p_path);
+ void force_process_input();
+
OS_OSX();
private:
diff --git a/platform/osx/os_osx.mm b/platform/osx/os_osx.mm
index 75d0bd1648..3d4883e269 100644
--- a/platform/osx/os_osx.mm
+++ b/platform/osx/os_osx.mm
@@ -1971,6 +1971,13 @@ void OS_OSX::push_input(const Ref<InputEvent> &p_event) {
input->parse_input_event(ev);
}
+void OS_OSX::force_process_input() {
+
+ process_events(); // get rid of pending events
+ joypad_osx->process_joypads();
+
+}
+
void OS_OSX::run() {
force_quit = false;
diff --git a/platform/windows/detect.py b/platform/windows/detect.py
index d85e1b061c..3f66207457 100644
--- a/platform/windows/detect.py
+++ b/platform/windows/detect.py
@@ -188,6 +188,9 @@ def configure(env):
else:
VC_PATH = ""
+ if (env["openmp"]):
+ env.Append(CPPFLAGS=['/openmp'])
+
env.Append(CCFLAGS=["/I" + p for p in os.getenv("INCLUDE").split(";")])
env.Append(LIBPATH=[p for p in os.getenv("LIB").split(";")])
@@ -264,6 +267,10 @@ def configure(env):
env.Append(CCFLAGS=['-flto'])
env.Append(LINKFLAGS=['-flto=' + str(env.GetOption("num_jobs"))])
+ if (env["openmp"]):
+ env.Append(CPPFLAGS=['-fopenmp'])
+ env.Append(LIBS=['gomp'])
+
## Compile flags
env.Append(CCFLAGS=['-DWINDOWS_ENABLED', '-mwindows'])
diff --git a/platform/windows/os_windows.cpp b/platform/windows/os_windows.cpp
index 41730d33af..bbfeff74e8 100644
--- a/platform/windows/os_windows.cpp
+++ b/platform/windows/os_windows.cpp
@@ -2156,6 +2156,10 @@ void OS_Windows::swap_buffers() {
gl_context->swap_buffers();
}
+void OS_Windows::force_process_input() {
+ process_events(); // get rid of pending events
+}
+
void OS_Windows::run() {
if (!main_loop)
diff --git a/platform/windows/os_windows.h b/platform/windows/os_windows.h
index af1ccd4446..0b0269a372 100644
--- a/platform/windows/os_windows.h
+++ b/platform/windows/os_windows.h
@@ -287,6 +287,8 @@ public:
void disable_crash_handler();
bool is_disable_crash_handler() const;
+ void force_process_input();
+
virtual Error move_to_trash(const String &p_path);
OS_Windows(HINSTANCE _hInstance);
diff --git a/platform/x11/detect.py b/platform/x11/detect.py
index d7dbe71da4..fb3b7588d2 100644
--- a/platform/x11/detect.py
+++ b/platform/x11/detect.py
@@ -158,6 +158,7 @@ def configure(env):
if not env['builtin_libwebp']:
env.ParseConfig('pkg-config libwebp --cflags --libs')
+
# freetype depends on libpng and zlib, so bundling one of them while keeping others
# as shared libraries leads to weird issues
if env['builtin_freetype'] or env['builtin_libpng'] or env['builtin_zlib']:
@@ -263,5 +264,10 @@ def configure(env):
env.Append(CPPFLAGS=['-m64'])
env.Append(LINKFLAGS=['-m64', '-L/usr/lib/i686-linux-gnu'])
+
+ if (env["openmp"]):
+ env.Append(CPPFLAGS=['-fopenmp'])
+ env.Append(LIBS=['gomp'])
+
if env['use_static_cpp']:
env.Append(LINKFLAGS=['-static-libstdc++'])
diff --git a/platform/x11/os_x11.cpp b/platform/x11/os_x11.cpp
index b59fab7088..b2e023d991 100644
--- a/platform/x11/os_x11.cpp
+++ b/platform/x11/os_x11.cpp
@@ -2264,6 +2264,13 @@ void OS_X11::set_icon(const Ref<Image> &p_icon) {
XFlush(x11_display);
}
+void OS_X11::force_process_input() {
+ process_xevents(); // get rid of pending events
+#ifdef JOYDEV_ENABLED
+ joypad->process_joypads();
+#endif
+}
+
void OS_X11::run() {
force_quit = false;
diff --git a/platform/x11/os_x11.h b/platform/x11/os_x11.h
index 244c69ee2b..ec8d12da27 100644
--- a/platform/x11/os_x11.h
+++ b/platform/x11/os_x11.h
@@ -279,6 +279,7 @@ public:
virtual bool _check_internal_feature_support(const String &p_feature);
+ virtual void force_process_input();
void run();
void disable_crash_handler();
diff --git a/scene/3d/baked_lightmap.cpp b/scene/3d/baked_lightmap.cpp
new file mode 100644
index 0000000000..6fdfec38a8
--- /dev/null
+++ b/scene/3d/baked_lightmap.cpp
@@ -0,0 +1,717 @@
+#include "baked_lightmap.h"
+#include "io/resource_saver.h"
+#include "os/dir_access.h"
+#include "os/os.h"
+#include "voxel_light_baker.h"
+
+void BakedLightmapData::set_bounds(const AABB &p_bounds) {
+
+ bounds = p_bounds;
+ VS::get_singleton()->lightmap_capture_set_bounds(baked_light, p_bounds);
+}
+
+AABB BakedLightmapData::get_bounds() const {
+
+ return bounds;
+}
+
+void BakedLightmapData::set_octree(const PoolVector<uint8_t> &p_octree) {
+
+ VS::get_singleton()->lightmap_capture_set_octree(baked_light, p_octree);
+}
+
+PoolVector<uint8_t> BakedLightmapData::get_octree() const {
+
+ return VS::get_singleton()->lightmap_capture_get_octree(baked_light);
+}
+
+void BakedLightmapData::set_cell_space_transform(const Transform &p_xform) {
+
+ cell_space_xform = p_xform;
+ VS::get_singleton()->lightmap_capture_set_octree_cell_transform(baked_light, p_xform);
+}
+
+Transform BakedLightmapData::get_cell_space_transform() const {
+ return cell_space_xform;
+}
+
+void BakedLightmapData::set_cell_subdiv(int p_cell_subdiv) {
+ cell_subdiv = p_cell_subdiv;
+ VS::get_singleton()->lightmap_capture_set_octree_cell_subdiv(baked_light, p_cell_subdiv);
+}
+
+int BakedLightmapData::get_cell_subdiv() const {
+ return cell_subdiv;
+}
+
+void BakedLightmapData::set_energy(float p_energy) {
+
+ energy = p_energy;
+ VS::get_singleton()->lightmap_capture_set_energy(baked_light, energy);
+}
+
+float BakedLightmapData::get_energy() const {
+
+ return energy;
+}
+
+void BakedLightmapData::add_user(const NodePath &p_path, const Ref<Texture> &p_lightmap) {
+
+ ERR_FAIL_COND(p_lightmap.is_null());
+ User user;
+ user.path = p_path;
+ user.lightmap = p_lightmap;
+ users.push_back(user);
+}
+
+int BakedLightmapData::get_user_count() const {
+
+ return users.size();
+}
+NodePath BakedLightmapData::get_user_path(int p_user) const {
+
+ ERR_FAIL_INDEX_V(p_user, users.size(), NodePath());
+ return users[p_user].path;
+}
+Ref<Texture> BakedLightmapData::get_user_lightmap(int p_user) const {
+
+ ERR_FAIL_INDEX_V(p_user, users.size(), Ref<Texture>());
+ return users[p_user].lightmap;
+}
+
+void BakedLightmapData::clear_users() {
+ users.clear();
+}
+
+void BakedLightmapData::_set_user_data(const Array &p_data) {
+
+ ERR_FAIL_COND(p_data.size() & 1);
+
+ for (int i = 0; i < p_data.size(); i += 2) {
+ add_user(p_data[i], p_data[i + 1]);
+ }
+}
+
+Array BakedLightmapData::_get_user_data() const {
+
+ Array ret;
+ for (int i = 0; i < users.size(); i++) {
+ ret.push_back(users[i].path);
+ ret.push_back(users[i].lightmap);
+ }
+ return ret;
+}
+
+RID BakedLightmapData::get_rid() const {
+ return baked_light;
+}
+void BakedLightmapData::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("_set_user_data", "data"), &BakedLightmapData::_set_user_data);
+ ClassDB::bind_method(D_METHOD("_get_user_data"), &BakedLightmapData::_get_user_data);
+
+ ClassDB::bind_method(D_METHOD("set_bounds", "bounds"), &BakedLightmapData::set_bounds);
+ ClassDB::bind_method(D_METHOD("get_bounds"), &BakedLightmapData::get_bounds);
+
+ ClassDB::bind_method(D_METHOD("set_cell_space_transform", "xform"), &BakedLightmapData::set_cell_space_transform);
+ ClassDB::bind_method(D_METHOD("get_cell_space_transform"), &BakedLightmapData::get_cell_space_transform);
+
+ ClassDB::bind_method(D_METHOD("set_cell_subdiv", "cell_subdiv"), &BakedLightmapData::set_cell_subdiv);
+ ClassDB::bind_method(D_METHOD("get_cell_subdiv"), &BakedLightmapData::get_cell_subdiv);
+
+ ClassDB::bind_method(D_METHOD("set_octree", "octree"), &BakedLightmapData::set_octree);
+ ClassDB::bind_method(D_METHOD("get_octree"), &BakedLightmapData::get_octree);
+
+ ClassDB::bind_method(D_METHOD("set_energy", "energy"), &BakedLightmapData::set_energy);
+ ClassDB::bind_method(D_METHOD("get_energy"), &BakedLightmapData::get_energy);
+
+ ClassDB::bind_method(D_METHOD("add_user", "path", "lightmap"), &BakedLightmapData::add_user);
+ ClassDB::bind_method(D_METHOD("get_user_count"), &BakedLightmapData::get_user_count);
+ ClassDB::bind_method(D_METHOD("get_user_path", "user_idx"), &BakedLightmapData::get_user_path);
+ ClassDB::bind_method(D_METHOD("get_user_lightmap", "user_idx"), &BakedLightmapData::get_user_lightmap);
+ ClassDB::bind_method(D_METHOD("clear_users"), &BakedLightmapData::clear_users);
+
+ ADD_PROPERTY(PropertyInfo(Variant::AABB, "bounds", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_bounds", "get_bounds");
+ ADD_PROPERTY(PropertyInfo(Variant::POOL_BYTE_ARRAY, "octree", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_octree", "get_octree");
+ ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM, "cell_space_transform", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_cell_space_transform", "get_cell_space_transform");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "cell_subdiv", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_cell_subdiv", "get_cell_subdiv");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "energy", PROPERTY_HINT_RANGE, "0,16,0.01"), "set_energy", "get_energy");
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "user_data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "_set_user_data", "_get_user_data");
+}
+
+BakedLightmapData::BakedLightmapData() {
+
+ baked_light = VS::get_singleton()->lightmap_capture_create();
+ energy = 1;
+ cell_subdiv = 1;
+}
+
+BakedLightmapData::~BakedLightmapData() {
+
+ VS::get_singleton()->free(baked_light);
+}
+
+///////////////////////////
+
+BakedLightmap::BakeBeginFunc BakedLightmap::bake_begin_function = NULL;
+BakedLightmap::BakeStepFunc BakedLightmap::bake_step_function = NULL;
+BakedLightmap::BakeEndFunc BakedLightmap::bake_end_function = NULL;
+
+void BakedLightmap::set_bake_subdiv(Subdiv p_subdiv) {
+ bake_subdiv = p_subdiv;
+}
+
+BakedLightmap::Subdiv BakedLightmap::get_bake_subdiv() const {
+ return bake_subdiv;
+}
+
+void BakedLightmap::set_capture_subdiv(Subdiv p_subdiv) {
+ capture_subdiv = p_subdiv;
+}
+
+BakedLightmap::Subdiv BakedLightmap::get_capture_subdiv() const {
+ return capture_subdiv;
+}
+
+void BakedLightmap::set_extents(const Vector3 &p_extents) {
+ extents = p_extents;
+}
+
+Vector3 BakedLightmap::get_extents() const {
+ return extents;
+}
+
+void BakedLightmap::_find_meshes_and_lights(Node *p_at_node, List<PlotMesh> &plot_meshes, List<PlotLight> &plot_lights) {
+
+ MeshInstance *mi = Object::cast_to<MeshInstance>(p_at_node);
+ if (mi && mi->get_flag(GeometryInstance::FLAG_USE_BAKED_LIGHT) && mi->is_visible_in_tree()) {
+ Ref<Mesh> mesh = mi->get_mesh();
+ if (mesh.is_valid()) {
+
+ bool all_have_uv2 = true;
+ for (int i = 0; i < mesh->get_surface_count(); i++) {
+ if (!(mesh->surface_get_format(i) & Mesh::ARRAY_FORMAT_TEX_UV2)) {
+ all_have_uv2 = false;
+ break;
+ }
+ }
+
+ if (all_have_uv2 && mesh->get_lightmap_size_hint() != Size2()) {
+ //READY TO BAKE! size hint could be computed if not found, actually..
+
+ AABB aabb = mesh->get_aabb();
+
+ Transform xf = get_global_transform().affine_inverse() * mi->get_global_transform();
+
+ if (AABB(-extents, extents * 2).intersects(xf.xform(aabb))) {
+ PlotMesh pm;
+ pm.local_xform = xf;
+ pm.mesh = mesh;
+ pm.path = get_path_to(mi);
+ for (int i = 0; i < mesh->get_surface_count(); i++) {
+ pm.instance_materials.push_back(mi->get_surface_material(i));
+ }
+ pm.override_material = mi->get_material_override();
+ plot_meshes.push_back(pm);
+ }
+ }
+ }
+ }
+
+ Light *light = Object::cast_to<Light>(p_at_node);
+
+ if (light && light->get_bake_mode() != Light::BAKE_DISABLED) {
+ PlotLight pl;
+ Transform xf = get_global_transform().affine_inverse() * light->get_global_transform();
+
+ pl.local_xform = xf;
+ pl.light = light;
+ plot_lights.push_back(pl);
+ }
+ for (int i = 0; i < p_at_node->get_child_count(); i++) {
+
+ Node *child = p_at_node->get_child(i);
+ if (!child->get_owner())
+ continue; //maybe a helper
+
+ _find_meshes_and_lights(child, plot_meshes, plot_lights);
+ }
+}
+
+void BakedLightmap::set_hdr(bool p_enable) {
+ hdr = p_enable;
+}
+
+bool BakedLightmap::is_hdr() const {
+ return hdr;
+}
+
+bool BakedLightmap::_bake_time(void *ud, float p_secs, float p_progress) {
+
+ uint64_t time = OS::get_singleton()->get_ticks_usec();
+ BakeTimeData *btd = (BakeTimeData *)ud;
+
+ if (time - btd->last_step > 1000000) {
+
+ int mins_left = p_secs / 60;
+ int secs_left = Math::fmod(p_secs, 60.0);
+ int percent = p_progress * 100;
+ bool abort = bake_step_function(btd->pass + percent, btd->text + " " + itos(percent) + "% (Time Left: " + itos(mins_left) + ":" + itos(secs_left) + "s)");
+ btd->last_step = time;
+ if (abort)
+ return true;
+ }
+
+ return false;
+}
+
+BakedLightmap::BakeError BakedLightmap::bake(Node *p_from_node, bool p_create_visual_debug) {
+
+ String save_path;
+
+ if (image_path.begins_with("res://")) {
+ save_path = image_path;
+ } else {
+ if (get_filename() != "") {
+ save_path = get_filename().get_base_dir();
+ } else if (get_owner() && get_owner()->get_filename() != "") {
+ save_path = get_owner()->get_filename().get_base_dir();
+ }
+
+ if (save_path == "") {
+ return BAKE_ERROR_NO_SAVE_PATH;
+ }
+ if (image_path != "") {
+ save_path.plus_file(image_path);
+ }
+ }
+ {
+ //check for valid save path
+ DirAccessRef d = DirAccess::open(save_path);
+ if (!d) {
+ ERR_PRINTS("Invalid Save Path: " + save_path);
+ return BAKE_ERROR_NO_SAVE_PATH;
+ }
+ }
+
+ Ref<BakedLightmapData> new_light_data;
+ new_light_data.instance();
+
+ static const int subdiv_value[SUBDIV_MAX] = { 8, 9, 10, 11, 12, 13 };
+
+ VoxelLightBaker baker;
+
+ baker.begin_bake(subdiv_value[bake_subdiv], AABB(-extents, extents * 2.0));
+
+ List<PlotMesh> mesh_list;
+ List<PlotLight> light_list;
+
+ _find_meshes_and_lights(p_from_node ? p_from_node : get_parent(), mesh_list, light_list);
+
+ if (bake_begin_function) {
+ bake_begin_function(mesh_list.size() + light_list.size() + 1 + mesh_list.size() * 100);
+ }
+
+ int step = 0;
+
+ int pmc = 0;
+
+ for (List<PlotMesh>::Element *E = mesh_list.front(); E; E = E->next()) {
+
+ if (bake_step_function) {
+ bake_step_function(step++, RTR("Plotting Meshes: ") + " (" + itos(pmc + 1) + "/" + itos(mesh_list.size()) + ")");
+ }
+
+ pmc++;
+ baker.plot_mesh(E->get().local_xform, E->get().mesh, E->get().instance_materials, E->get().override_material);
+ }
+
+ pmc = 0;
+ baker.begin_bake_light(VoxelLightBaker::BakeQuality(bake_quality), VoxelLightBaker::BakeMode(bake_mode), propagation, energy);
+
+ for (List<PlotLight>::Element *E = light_list.front(); E; E = E->next()) {
+
+ if (bake_step_function) {
+ bake_step_function(step++, RTR("Plotting Lights:") + " (" + itos(pmc + 1) + "/" + itos(light_list.size()) + ")");
+ }
+
+ pmc++;
+ PlotLight pl = E->get();
+ switch (pl.light->get_light_type()) {
+ case VS::LIGHT_DIRECTIONAL: {
+ baker.plot_light_directional(-pl.local_xform.basis.get_axis(2), pl.light->get_color(), pl.light->get_param(Light::PARAM_ENERGY), pl.light->get_param(Light::PARAM_INDIRECT_ENERGY), pl.light->get_bake_mode() == Light::BAKE_ALL);
+ } break;
+ case VS::LIGHT_OMNI: {
+ baker.plot_light_omni(pl.local_xform.origin, pl.light->get_color(), pl.light->get_param(Light::PARAM_ENERGY), pl.light->get_param(Light::PARAM_INDIRECT_ENERGY), pl.light->get_param(Light::PARAM_RANGE), pl.light->get_param(Light::PARAM_ATTENUATION), pl.light->get_bake_mode() == Light::BAKE_ALL);
+ } break;
+ case VS::LIGHT_SPOT: {
+ baker.plot_light_spot(pl.local_xform.origin, pl.local_xform.basis.get_axis(2), pl.light->get_color(), pl.light->get_param(Light::PARAM_ENERGY), pl.light->get_param(Light::PARAM_INDIRECT_ENERGY), pl.light->get_param(Light::PARAM_RANGE), pl.light->get_param(Light::PARAM_ATTENUATION), pl.light->get_param(Light::PARAM_SPOT_ANGLE), pl.light->get_param(Light::PARAM_SPOT_ATTENUATION), pl.light->get_bake_mode() == Light::BAKE_ALL);
+
+ } break;
+ }
+ }
+ /*if (bake_step_function) {
+ bake_step_function(pmc++, RTR("Finishing Plot"));
+ }*/
+
+ baker.end_bake();
+
+ Set<String> used_mesh_names;
+
+ pmc = 0;
+ for (List<PlotMesh>::Element *E = mesh_list.front(); E; E = E->next()) {
+
+ String mesh_name = E->get().mesh->get_name();
+ if (mesh_name == "" || mesh_name.find(":") != -1 || mesh_name.find("/") != -1) {
+ mesh_name = "LightMap";
+ }
+
+ if (used_mesh_names.has(mesh_name)) {
+ int idx = 2;
+ String base = mesh_name;
+ while (true) {
+ mesh_name = base + itos(idx);
+ if (!used_mesh_names.has(mesh_name))
+ break;
+ idx++;
+ }
+ }
+ used_mesh_names.insert(mesh_name);
+
+ pmc++;
+ VoxelLightBaker::LightMapData lm;
+
+ Error err;
+ if (bake_step_function) {
+ BakeTimeData btd;
+ btd.text = RTR("Lighting Meshes: ") + mesh_name + " (" + itos(pmc) + "/" + itos(mesh_list.size()) + ")";
+ btd.pass = step;
+ btd.last_step = 0;
+ err = baker.make_lightmap(E->get().local_xform, E->get().mesh, lm, _bake_time, &btd);
+ if (err != OK) {
+ bake_end_function();
+ if (err == ERR_SKIP)
+ return BAKE_ERROR_USER_ABORTED;
+ return BAKE_ERROR_CANT_CREATE_IMAGE;
+ }
+ step += 100;
+ } else {
+
+ err = baker.make_lightmap(E->get().local_xform, E->get().mesh, lm);
+ }
+
+ if (err == OK) {
+
+ Ref<Image> image;
+ image.instance();
+
+ uint32_t tex_flags = Texture::FLAGS_DEFAULT;
+ if (hdr) {
+
+ //just save a regular image
+ PoolVector<uint8_t> data;
+ int s = lm.light.size();
+ data.resize(lm.light.size() * 2);
+ {
+
+ PoolVector<uint8_t>::Write w = data.write();
+ PoolVector<float>::Read r = lm.light.read();
+ uint16_t *hfw = (uint16_t *)w.ptr();
+ for (int i = 0; i < s; i++) {
+ hfw[i] = Math::make_half_float(r[i]);
+ }
+ }
+
+ image->create(lm.width, lm.height, false, Image::FORMAT_RGBH, data);
+
+ } else {
+
+ //just save a regular image
+ PoolVector<uint8_t> data;
+ int s = lm.light.size();
+ data.resize(lm.light.size());
+ {
+
+ PoolVector<uint8_t>::Write w = data.write();
+ PoolVector<float>::Read r = lm.light.read();
+ for (int i = 0; i < s; i += 3) {
+ Color c(r[i + 0], r[i + 1], r[i + 2]);
+ c = c.to_srgb();
+ w[i + 0] = CLAMP(c.r * 255, 0, 255);
+ w[i + 1] = CLAMP(c.g * 255, 0, 255);
+ w[i + 2] = CLAMP(c.b * 255, 0, 255);
+ }
+ }
+
+ image->create(lm.width, lm.height, false, Image::FORMAT_RGB8, data);
+
+ //This texture is saved to SRGB for two reasons:
+ // 1) first is so it looks better when doing the LINEAR->SRGB conversion (more accurate)
+ // 2) So it can be used in the GLES2 backend, which does not support linkear workflow
+ tex_flags |= Texture::FLAG_CONVERT_TO_LINEAR;
+ }
+
+ Ref<ImageTexture> tex;
+ String image_path = save_path.plus_file(mesh_name + ".tex");
+ bool set_path = true;
+ if (ResourceCache::has(image_path)) {
+ tex = Ref<Resource>((Resource *)ResourceCache::get(image_path));
+ set_path = false;
+ }
+
+ if (!tex.is_valid()) {
+ tex.instance();
+ }
+
+ tex->create_from_image(image, tex_flags);
+
+ err = ResourceSaver::save(image_path, tex, ResourceSaver::FLAG_CHANGE_PATH);
+ if (err != OK) {
+ if (bake_end_function) {
+ bake_end_function();
+ }
+ ERR_FAIL_COND_V(err != OK, BAKE_ERROR_CANT_CREATE_IMAGE);
+ }
+
+ if (set_path) {
+ tex->set_path(image_path);
+ }
+ new_light_data->add_user(E->get().path, tex);
+ }
+ }
+
+ int csubdiv = subdiv_value[capture_subdiv];
+ AABB bounds = AABB(-extents, extents * 2);
+ new_light_data->set_cell_subdiv(csubdiv);
+ new_light_data->set_bounds(bounds);
+ new_light_data->set_octree(baker.create_capture_octree(csubdiv));
+ {
+
+ Transform to_bounds;
+ to_bounds.basis.scale(Vector3(bounds.get_longest_axis_size(), bounds.get_longest_axis_size(), bounds.get_longest_axis_size()));
+ to_bounds.origin = bounds.position;
+
+ Transform to_grid;
+ to_grid.basis.scale(Vector3(1 << (csubdiv - 1), 1 << (csubdiv - 1), 1 << (csubdiv - 1)));
+
+ Transform to_cell_space = to_grid * to_bounds.affine_inverse();
+ new_light_data->set_cell_space_transform(to_cell_space);
+ }
+
+ if (bake_end_function) {
+ bake_end_function();
+ }
+
+ //create the data for visual server
+
+ if (p_create_visual_debug) {
+ MultiMeshInstance *mmi = memnew(MultiMeshInstance);
+ mmi->set_multimesh(baker.create_debug_multimesh(VoxelLightBaker::DEBUG_LIGHT));
+ add_child(mmi);
+#ifdef TOOLS_ENABLED
+ if (get_tree()->get_edited_scene_root() == this) {
+ mmi->set_owner(this);
+ } else {
+ mmi->set_owner(get_owner());
+ }
+#else
+ mmi->set_owner(get_owner());
+#endif
+ }
+
+ set_light_data(new_light_data);
+
+ return BAKE_ERROR_OK;
+}
+
+void BakedLightmap::_notification(int p_what) {
+ if (p_what == NOTIFICATION_READY) {
+
+ if (light_data.is_valid()) {
+ _assign_lightmaps();
+ }
+ request_ready(); //will need ready again if re-enters tree
+ }
+
+ if (p_what == NOTIFICATION_EXIT_TREE) {
+
+ if (light_data.is_valid()) {
+ _clear_lightmaps();
+ }
+ }
+}
+
+void BakedLightmap::_assign_lightmaps() {
+
+ ERR_FAIL_COND(!light_data.is_valid());
+
+ for (int i = 0; i < light_data->get_user_count(); i++) {
+ Node *node = get_node(light_data->get_user_path(i));
+ VisualInstance *vi = Object::cast_to<VisualInstance>(node);
+ ERR_CONTINUE(!vi);
+ Ref<Texture> lightmap = light_data->get_user_lightmap(i);
+ ERR_CONTINUE(!lightmap.is_valid());
+ VS::get_singleton()->instance_set_use_lightmap(vi->get_instance(), get_instance(), lightmap->get_rid());
+ }
+}
+
+void BakedLightmap::_clear_lightmaps() {
+ ERR_FAIL_COND(!light_data.is_valid());
+ for (int i = 0; i < light_data->get_user_count(); i++) {
+ Node *node = get_node(light_data->get_user_path(i));
+ VisualInstance *vi = Object::cast_to<VisualInstance>(node);
+ ERR_CONTINUE(!vi);
+ VS::get_singleton()->instance_set_use_lightmap(vi->get_instance(), RID(), RID());
+ }
+}
+
+void BakedLightmap::set_light_data(const Ref<BakedLightmapData> &p_data) {
+
+ if (light_data.is_valid()) {
+ if (is_inside_tree()) {
+ _clear_lightmaps();
+ }
+ set_base(RID());
+ }
+ light_data = p_data;
+
+ if (light_data.is_valid()) {
+ set_base(light_data->get_rid());
+ if (is_inside_tree()) {
+ _assign_lightmaps();
+ }
+ }
+}
+
+Ref<BakedLightmapData> BakedLightmap::get_light_data() const {
+
+ return light_data;
+}
+
+void BakedLightmap::_debug_bake() {
+ bake(get_parent(), true);
+}
+
+void BakedLightmap::set_propagation(float p_propagation) {
+ propagation = p_propagation;
+}
+
+float BakedLightmap::get_propagation() const {
+
+ return propagation;
+}
+
+void BakedLightmap::set_energy(float p_energy) {
+ energy = p_energy;
+}
+
+float BakedLightmap::get_energy() const {
+
+ return energy;
+}
+
+void BakedLightmap::set_bake_quality(BakeQuality p_quality) {
+ bake_quality = p_quality;
+}
+
+BakedLightmap::BakeQuality BakedLightmap::get_bake_quality() const {
+ return bake_quality;
+}
+
+void BakedLightmap::set_bake_mode(BakeMode p_mode) {
+ bake_mode = p_mode;
+}
+
+BakedLightmap::BakeMode BakedLightmap::get_bake_mode() const {
+ return bake_mode;
+}
+
+void BakedLightmap::set_image_path(const String &p_path) {
+ image_path = p_path;
+}
+
+String BakedLightmap::get_image_path() const {
+ return image_path;
+}
+
+AABB BakedLightmap::get_aabb() const {
+ return AABB(-extents, extents * 2);
+}
+PoolVector<Face3> BakedLightmap::get_faces(uint32_t p_usage_flags) const {
+ return PoolVector<Face3>();
+}
+
+void BakedLightmap::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("set_light_data", "data"), &BakedLightmap::set_light_data);
+ ClassDB::bind_method(D_METHOD("get_light_data"), &BakedLightmap::get_light_data);
+
+ ClassDB::bind_method(D_METHOD("set_bake_subdiv", "bake_subdiv"), &BakedLightmap::set_bake_subdiv);
+ ClassDB::bind_method(D_METHOD("get_bake_subdiv"), &BakedLightmap::get_bake_subdiv);
+
+ ClassDB::bind_method(D_METHOD("set_capture_subdiv", "capture_subdiv"), &BakedLightmap::set_capture_subdiv);
+ ClassDB::bind_method(D_METHOD("get_capture_subdiv"), &BakedLightmap::get_capture_subdiv);
+
+ ClassDB::bind_method(D_METHOD("set_bake_quality", "bake_quality"), &BakedLightmap::set_bake_quality);
+ ClassDB::bind_method(D_METHOD("get_bake_quality"), &BakedLightmap::get_bake_quality);
+
+ ClassDB::bind_method(D_METHOD("set_bake_mode", "bake_mode"), &BakedLightmap::set_bake_mode);
+ ClassDB::bind_method(D_METHOD("get_bake_mode"), &BakedLightmap::get_bake_mode);
+
+ ClassDB::bind_method(D_METHOD("set_extents", "extents"), &BakedLightmap::set_extents);
+ ClassDB::bind_method(D_METHOD("get_extents"), &BakedLightmap::get_extents);
+
+ ClassDB::bind_method(D_METHOD("set_propagation", "propagation"), &BakedLightmap::set_propagation);
+ ClassDB::bind_method(D_METHOD("get_propagation"), &BakedLightmap::get_propagation);
+
+ ClassDB::bind_method(D_METHOD("set_energy", "energy"), &BakedLightmap::set_energy);
+ ClassDB::bind_method(D_METHOD("get_energy"), &BakedLightmap::get_energy);
+
+ ClassDB::bind_method(D_METHOD("set_hdr", "hdr"), &BakedLightmap::set_hdr);
+ ClassDB::bind_method(D_METHOD("is_hdr"), &BakedLightmap::is_hdr);
+
+ ClassDB::bind_method(D_METHOD("set_image_path", "image_path"), &BakedLightmap::set_image_path);
+ ClassDB::bind_method(D_METHOD("get_image_path"), &BakedLightmap::get_image_path);
+
+ ClassDB::bind_method(D_METHOD("bake", "from_node", "create_visual_debug"), &BakedLightmap::bake, DEFVAL(Variant()), DEFVAL(false));
+ ClassDB::bind_method(D_METHOD("debug_bake"), &BakedLightmap::_debug_bake);
+ ClassDB::set_method_flags(get_class_static(), _scs_create("debug_bake"), METHOD_FLAGS_DEFAULT | METHOD_FLAG_EDITOR);
+
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "bake_subdiv", PROPERTY_HINT_ENUM, "128,256,512,1024,2048,4096"), "set_bake_subdiv", "get_bake_subdiv");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "capture_subdiv", PROPERTY_HINT_ENUM, "128,256,512"), "set_capture_subdiv", "get_capture_subdiv");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "bake_quality", PROPERTY_HINT_ENUM, "Low,Medium,High"), "set_bake_quality", "get_bake_quality");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "bake_mode", PROPERTY_HINT_ENUM, "ConeTrace,RayTrace"), "set_bake_mode", "get_bake_mode");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "propagation", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_propagation", "get_propagation");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "energy", PROPERTY_HINT_RANGE, "0,32,0.01"), "set_energy", "get_energy");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "hdr"), "set_hdr", "is_hdr");
+ ADD_PROPERTY(PropertyInfo(Variant::STRING, "image_path", PROPERTY_HINT_DIR), "set_image_path", "get_image_path");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "extents"), "set_extents", "get_extents");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "light_data", PROPERTY_HINT_RESOURCE_TYPE, "BakedIndirectLightData"), "set_light_data", "get_light_data");
+
+ BIND_ENUM_CONSTANT(SUBDIV_128);
+ BIND_ENUM_CONSTANT(SUBDIV_256);
+ BIND_ENUM_CONSTANT(SUBDIV_512);
+ BIND_ENUM_CONSTANT(SUBDIV_1024);
+ BIND_ENUM_CONSTANT(SUBDIV_2048);
+ BIND_ENUM_CONSTANT(SUBDIV_4096);
+ BIND_ENUM_CONSTANT(SUBDIV_MAX);
+
+ BIND_ENUM_CONSTANT(BAKE_QUALITY_LOW);
+ BIND_ENUM_CONSTANT(BAKE_QUALITY_MEDIUM);
+ BIND_ENUM_CONSTANT(BAKE_QUALITY_HIGH);
+ BIND_ENUM_CONSTANT(BAKE_MODE_CONE_TRACE);
+ BIND_ENUM_CONSTANT(BAKE_MODE_RAY_TRACE);
+}
+
+BakedLightmap::BakedLightmap() {
+
+ extents = Vector3(10, 10, 10);
+ bake_subdiv = SUBDIV_256;
+ capture_subdiv = SUBDIV_128;
+ bake_quality = BAKE_QUALITY_MEDIUM;
+ bake_mode = BAKE_MODE_CONE_TRACE;
+ energy = 1;
+ propagation = 1;
+ hdr = false;
+ image_path = ".";
+}
diff --git a/scene/3d/baked_lightmap.h b/scene/3d/baked_lightmap.h
new file mode 100644
index 0000000000..5595ec1e61
--- /dev/null
+++ b/scene/3d/baked_lightmap.h
@@ -0,0 +1,189 @@
+#ifndef BAKED_INDIRECT_LIGHT_H
+#define BAKED_INDIRECT_LIGHT_H
+
+#include "multimesh_instance.h"
+#include "scene/3d/light.h"
+#include "scene/3d/visual_instance.h"
+
+class BakedLightmapData : public Resource {
+ GDCLASS(BakedLightmapData, Resource);
+
+ RID baked_light;
+ AABB bounds;
+ float energy;
+ int cell_subdiv;
+ Transform cell_space_xform;
+
+ struct User {
+
+ NodePath path;
+ Ref<Texture> lightmap;
+ };
+
+ Vector<User> users;
+
+ void _set_user_data(const Array &p_data);
+ Array _get_user_data() const;
+
+protected:
+ static void _bind_methods();
+
+public:
+ void set_bounds(const AABB &p_bounds);
+ AABB get_bounds() const;
+
+ void set_octree(const PoolVector<uint8_t> &p_octree);
+ PoolVector<uint8_t> get_octree() const;
+
+ void set_cell_space_transform(const Transform &p_xform);
+ Transform get_cell_space_transform() const;
+
+ void set_cell_subdiv(int p_cell_subdiv);
+ int get_cell_subdiv() const;
+
+ void set_energy(float p_energy);
+ float get_energy() const;
+
+ void add_user(const NodePath &p_path, const Ref<Texture> &p_lightmap);
+ int get_user_count() const;
+ NodePath get_user_path(int p_user) const;
+ Ref<Texture> get_user_lightmap(int p_user) const;
+ void clear_users();
+
+ virtual RID get_rid() const;
+ BakedLightmapData();
+ ~BakedLightmapData();
+};
+
+class BakedLightmap : public VisualInstance {
+ GDCLASS(BakedLightmap, VisualInstance);
+
+public:
+ enum Subdiv {
+ SUBDIV_128,
+ SUBDIV_256,
+ SUBDIV_512,
+ SUBDIV_1024,
+ SUBDIV_2048,
+ SUBDIV_4096,
+ SUBDIV_MAX
+
+ };
+
+ enum BakeQuality {
+ BAKE_QUALITY_LOW,
+ BAKE_QUALITY_MEDIUM,
+ BAKE_QUALITY_HIGH
+ };
+
+ enum BakeMode {
+ BAKE_MODE_CONE_TRACE,
+ BAKE_MODE_RAY_TRACE,
+ };
+
+ enum BakeError {
+ BAKE_ERROR_OK,
+ BAKE_ERROR_NO_SAVE_PATH,
+ BAKE_ERROR_NO_MESHES,
+ BAKE_ERROR_CANT_CREATE_IMAGE,
+ BAKE_ERROR_USER_ABORTED
+
+ };
+
+ typedef void (*BakeBeginFunc)(int);
+ typedef bool (*BakeStepFunc)(int, const String &);
+ typedef void (*BakeEndFunc)();
+
+private:
+ Subdiv bake_subdiv;
+ Subdiv capture_subdiv;
+ Vector3 extents;
+ float propagation;
+ float energy;
+ BakeQuality bake_quality;
+ BakeMode bake_mode;
+ bool hdr;
+ String image_path;
+
+ Ref<BakedLightmapData> light_data;
+
+ struct PlotMesh {
+ Ref<Material> override_material;
+ Vector<Ref<Material> > instance_materials;
+ Ref<Mesh> mesh;
+ Transform local_xform;
+ NodePath path;
+ };
+
+ struct PlotLight {
+ Light *light;
+ Transform local_xform;
+ };
+
+ void _find_meshes_and_lights(Node *p_at_node, List<PlotMesh> &plot_meshes, List<PlotLight> &plot_lights);
+
+ void _debug_bake();
+
+ void _assign_lightmaps();
+ void _clear_lightmaps();
+
+ static bool _bake_time(void *ud, float p_secs, float p_progress);
+
+ struct BakeTimeData {
+ String text;
+ int pass;
+ uint64_t last_step;
+ };
+
+protected:
+ static void _bind_methods();
+ void _notification(int p_what);
+
+public:
+ static BakeBeginFunc bake_begin_function;
+ static BakeStepFunc bake_step_function;
+ static BakeEndFunc bake_end_function;
+
+ void set_light_data(const Ref<BakedLightmapData> &p_data);
+ Ref<BakedLightmapData> get_light_data() const;
+
+ void set_bake_subdiv(Subdiv p_subdiv);
+ Subdiv get_bake_subdiv() const;
+
+ void set_capture_subdiv(Subdiv p_subdiv);
+ Subdiv get_capture_subdiv() const;
+
+ void set_extents(const Vector3 &p_extents);
+ Vector3 get_extents() const;
+
+ void set_propagation(float p_propagation);
+ float get_propagation() const;
+
+ void set_energy(float p_energy);
+ float get_energy() const;
+
+ void set_bake_quality(BakeQuality p_quality);
+ BakeQuality get_bake_quality() const;
+
+ void set_bake_mode(BakeMode p_mode);
+ BakeMode get_bake_mode() const;
+
+ void set_hdr(bool p_enable);
+ bool is_hdr() const;
+
+ void set_image_path(const String &p_path);
+ String get_image_path() const;
+
+ AABB get_aabb() const;
+ PoolVector<Face3> get_faces(uint32_t p_usage_flags) const;
+
+ BakeError bake(Node *p_from_node, bool p_create_visual_debug = false);
+ BakedLightmap();
+};
+
+VARIANT_ENUM_CAST(BakedLightmap::Subdiv);
+VARIANT_ENUM_CAST(BakedLightmap::BakeQuality);
+VARIANT_ENUM_CAST(BakedLightmap::BakeMode);
+VARIANT_ENUM_CAST(BakedLightmap::BakeError);
+
+#endif // BAKED_INDIRECT_LIGHT_H
diff --git a/scene/3d/gi_probe.cpp b/scene/3d/gi_probe.cpp
index 1f2b43165e..9c811a74bf 100644
--- a/scene/3d/gi_probe.cpp
+++ b/scene/3d/gi_probe.cpp
@@ -30,6 +30,7 @@
#include "gi_probe.h"
#include "mesh_instance.h"
+#include "voxel_light_baker.h"
void GIProbeData::set_bounds(const AABB &p_bounds) {
@@ -329,754 +330,7 @@ bool GIProbe::is_compressed() const {
return compress;
}
-#include "math.h"
-
-#define FINDMINMAX(x0, x1, x2, min, max) \
- min = max = x0; \
- if (x1 < min) min = x1; \
- if (x1 > max) max = x1; \
- if (x2 < min) min = x2; \
- if (x2 > max) max = x2;
-
-static bool planeBoxOverlap(Vector3 normal, float d, Vector3 maxbox) {
- int q;
- Vector3 vmin, vmax;
- for (q = 0; q <= 2; q++) {
- if (normal[q] > 0.0f) {
- vmin[q] = -maxbox[q];
- vmax[q] = maxbox[q];
- } else {
- vmin[q] = maxbox[q];
- vmax[q] = -maxbox[q];
- }
- }
- if (normal.dot(vmin) + d > 0.0f) return false;
- if (normal.dot(vmax) + d >= 0.0f) return true;
-
- return false;
-}
-
-/*======================== X-tests ========================*/
-#define AXISTEST_X01(a, b, fa, fb) \
- p0 = a * v0.y - b * v0.z; \
- p2 = a * v2.y - b * v2.z; \
- if (p0 < p2) { \
- min = p0; \
- max = p2; \
- } else { \
- min = p2; \
- max = p0; \
- } \
- rad = fa * boxhalfsize.y + fb * boxhalfsize.z; \
- if (min > rad || max < -rad) return false;
-
-#define AXISTEST_X2(a, b, fa, fb) \
- p0 = a * v0.y - b * v0.z; \
- p1 = a * v1.y - b * v1.z; \
- if (p0 < p1) { \
- min = p0; \
- max = p1; \
- } else { \
- min = p1; \
- max = p0; \
- } \
- rad = fa * boxhalfsize.y + fb * boxhalfsize.z; \
- if (min > rad || max < -rad) return false;
-
-/*======================== Y-tests ========================*/
-#define AXISTEST_Y02(a, b, fa, fb) \
- p0 = -a * v0.x + b * v0.z; \
- p2 = -a * v2.x + b * v2.z; \
- if (p0 < p2) { \
- min = p0; \
- max = p2; \
- } else { \
- min = p2; \
- max = p0; \
- } \
- rad = fa * boxhalfsize.x + fb * boxhalfsize.z; \
- if (min > rad || max < -rad) return false;
-
-#define AXISTEST_Y1(a, b, fa, fb) \
- p0 = -a * v0.x + b * v0.z; \
- p1 = -a * v1.x + b * v1.z; \
- if (p0 < p1) { \
- min = p0; \
- max = p1; \
- } else { \
- min = p1; \
- max = p0; \
- } \
- rad = fa * boxhalfsize.x + fb * boxhalfsize.z; \
- if (min > rad || max < -rad) return false;
-
- /*======================== Z-tests ========================*/
-
-#define AXISTEST_Z12(a, b, fa, fb) \
- p1 = a * v1.x - b * v1.y; \
- p2 = a * v2.x - b * v2.y; \
- if (p2 < p1) { \
- min = p2; \
- max = p1; \
- } else { \
- min = p1; \
- max = p2; \
- } \
- rad = fa * boxhalfsize.x + fb * boxhalfsize.y; \
- if (min > rad || max < -rad) return false;
-
-#define AXISTEST_Z0(a, b, fa, fb) \
- p0 = a * v0.x - b * v0.y; \
- p1 = a * v1.x - b * v1.y; \
- if (p0 < p1) { \
- min = p0; \
- max = p1; \
- } else { \
- min = p1; \
- max = p0; \
- } \
- rad = fa * boxhalfsize.x + fb * boxhalfsize.y; \
- if (min > rad || max < -rad) return false;
-
-static bool fast_tri_box_overlap(const Vector3 &boxcenter, const Vector3 boxhalfsize, const Vector3 *triverts) {
-
- /* use separating axis theorem to test overlap between triangle and box */
- /* need to test for overlap in these directions: */
- /* 1) the {x,y,z}-directions (actually, since we use the AABB of the triangle */
- /* we do not even need to test these) */
- /* 2) normal of the triangle */
- /* 3) crossproduct(edge from tri, {x,y,z}-directin) */
- /* this gives 3x3=9 more tests */
- Vector3 v0, v1, v2;
- float min, max, d, p0, p1, p2, rad, fex, fey, fez;
- Vector3 normal, e0, e1, e2;
-
- /* This is the fastest branch on Sun */
- /* move everything so that the boxcenter is in (0,0,0) */
-
- v0 = triverts[0] - boxcenter;
- v1 = triverts[1] - boxcenter;
- v2 = triverts[2] - boxcenter;
-
- /* compute triangle edges */
- e0 = v1 - v0; /* tri edge 0 */
- e1 = v2 - v1; /* tri edge 1 */
- e2 = v0 - v2; /* tri edge 2 */
-
- /* Bullet 3: */
- /* test the 9 tests first (this was faster) */
- fex = Math::abs(e0.x);
- fey = Math::abs(e0.y);
- fez = Math::abs(e0.z);
- AXISTEST_X01(e0.z, e0.y, fez, fey);
- AXISTEST_Y02(e0.z, e0.x, fez, fex);
- AXISTEST_Z12(e0.y, e0.x, fey, fex);
-
- fex = Math::abs(e1.x);
- fey = Math::abs(e1.y);
- fez = Math::abs(e1.z);
- AXISTEST_X01(e1.z, e1.y, fez, fey);
- AXISTEST_Y02(e1.z, e1.x, fez, fex);
- AXISTEST_Z0(e1.y, e1.x, fey, fex);
-
- fex = Math::abs(e2.x);
- fey = Math::abs(e2.y);
- fez = Math::abs(e2.z);
- AXISTEST_X2(e2.z, e2.y, fez, fey);
- AXISTEST_Y1(e2.z, e2.x, fez, fex);
- AXISTEST_Z12(e2.y, e2.x, fey, fex);
-
- /* Bullet 1: */
- /* first test overlap in the {x,y,z}-directions */
- /* find min, max of the triangle each direction, and test for overlap in */
- /* that direction -- this is equivalent to testing a minimal AABB around */
- /* the triangle against the AABB */
-
- /* test in X-direction */
- FINDMINMAX(v0.x, v1.x, v2.x, min, max);
- if (min > boxhalfsize.x || max < -boxhalfsize.x) return false;
-
- /* test in Y-direction */
- FINDMINMAX(v0.y, v1.y, v2.y, min, max);
- if (min > boxhalfsize.y || max < -boxhalfsize.y) return false;
-
- /* test in Z-direction */
- FINDMINMAX(v0.z, v1.z, v2.z, min, max);
- if (min > boxhalfsize.z || max < -boxhalfsize.z) return false;
-
- /* Bullet 2: */
- /* test if the box intersects the plane of the triangle */
- /* compute plane equation of triangle: normal*x+d=0 */
- normal = e0.cross(e1);
- d = -normal.dot(v0); /* plane eq: normal.x+d=0 */
- if (!planeBoxOverlap(normal, d, boxhalfsize)) return false;
-
- return true; /* box and triangle overlaps */
-}
-
-static _FORCE_INLINE_ Vector2 get_uv(const Vector3 &p_pos, const Vector3 *p_vtx, const Vector2 *p_uv) {
-
- if (p_pos.distance_squared_to(p_vtx[0]) < CMP_EPSILON2)
- return p_uv[0];
- if (p_pos.distance_squared_to(p_vtx[1]) < CMP_EPSILON2)
- return p_uv[1];
- if (p_pos.distance_squared_to(p_vtx[2]) < CMP_EPSILON2)
- return p_uv[2];
-
- Vector3 v0 = p_vtx[1] - p_vtx[0];
- Vector3 v1 = p_vtx[2] - p_vtx[0];
- Vector3 v2 = p_pos - p_vtx[0];
-
- float d00 = v0.dot(v0);
- float d01 = v0.dot(v1);
- float d11 = v1.dot(v1);
- float d20 = v2.dot(v0);
- float d21 = v2.dot(v1);
- float denom = (d00 * d11 - d01 * d01);
- if (denom == 0)
- return p_uv[0];
- float v = (d11 * d20 - d01 * d21) / denom;
- float w = (d00 * d21 - d01 * d20) / denom;
- float u = 1.0f - v - w;
-
- return p_uv[0] * u + p_uv[1] * v + p_uv[2] * w;
-}
-
-void GIProbe::_plot_face(int p_idx, int p_level, int p_x, int p_y, int p_z, const Vector3 *p_vtx, const Vector2 *p_uv, const Baker::MaterialCache &p_material, const AABB &p_aabb, Baker *p_baker) {
-
- if (p_level == p_baker->cell_subdiv - 1) {
- //plot the face by guessing it's albedo and emission value
-
- //find best axis to map to, for scanning values
- int closest_axis = 0;
- float closest_dot = 0;
-
- Plane plane = Plane(p_vtx[0], p_vtx[1], p_vtx[2]);
- Vector3 normal = plane.normal;
-
- for (int i = 0; i < 3; i++) {
-
- Vector3 axis;
- axis[i] = 1.0;
- float dot = ABS(normal.dot(axis));
- if (i == 0 || dot > closest_dot) {
- closest_axis = i;
- closest_dot = dot;
- }
- }
-
- Vector3 axis;
- axis[closest_axis] = 1.0;
- Vector3 t1;
- t1[(closest_axis + 1) % 3] = 1.0;
- Vector3 t2;
- t2[(closest_axis + 2) % 3] = 1.0;
-
- t1 *= p_aabb.size[(closest_axis + 1) % 3] / float(color_scan_cell_width);
- t2 *= p_aabb.size[(closest_axis + 2) % 3] / float(color_scan_cell_width);
-
- Color albedo_accum;
- Color emission_accum;
- Vector3 normal_accum;
-
- float alpha = 0.0;
-
- //map to a grid average in the best axis for this face
- for (int i = 0; i < color_scan_cell_width; i++) {
-
- Vector3 ofs_i = float(i) * t1;
-
- for (int j = 0; j < color_scan_cell_width; j++) {
-
- Vector3 ofs_j = float(j) * t2;
-
- Vector3 from = p_aabb.position + ofs_i + ofs_j;
- Vector3 to = from + t1 + t2 + axis * p_aabb.size[closest_axis];
- Vector3 half = (to - from) * 0.5;
-
- //is in this cell?
- if (!fast_tri_box_overlap(from + half, half, p_vtx)) {
- continue; //face does not span this cell
- }
-
- //go from -size to +size*2 to avoid skipping collisions
- Vector3 ray_from = from + (t1 + t2) * 0.5 - axis * p_aabb.size[closest_axis];
- Vector3 ray_to = ray_from + axis * p_aabb.size[closest_axis] * 2;
-
- if (normal.dot(ray_from - ray_to) < 0) {
- SWAP(ray_from, ray_to);
- }
-
- Vector3 intersection;
-
- if (!plane.intersects_segment(ray_from, ray_to, &intersection)) {
- if (ABS(plane.distance_to(ray_from)) < ABS(plane.distance_to(ray_to))) {
- intersection = plane.project(ray_from);
- } else {
-
- intersection = plane.project(ray_to);
- }
- }
-
- intersection = Face3(p_vtx[0], p_vtx[1], p_vtx[2]).get_closest_point_to(intersection);
-
- Vector2 uv = get_uv(intersection, p_vtx, p_uv);
-
- int uv_x = CLAMP(Math::fposmod(uv.x, 1.0f) * bake_texture_size, 0, bake_texture_size - 1);
- int uv_y = CLAMP(Math::fposmod(uv.y, 1.0f) * bake_texture_size, 0, bake_texture_size - 1);
-
- int ofs = uv_y * bake_texture_size + uv_x;
- albedo_accum.r += p_material.albedo[ofs].r;
- albedo_accum.g += p_material.albedo[ofs].g;
- albedo_accum.b += p_material.albedo[ofs].b;
- albedo_accum.a += p_material.albedo[ofs].a;
-
- emission_accum.r += p_material.emission[ofs].r;
- emission_accum.g += p_material.emission[ofs].g;
- emission_accum.b += p_material.emission[ofs].b;
-
- normal_accum += normal;
-
- alpha += 1.0;
- }
- }
-
- if (alpha == 0) {
- //could not in any way get texture information.. so use closest point to center
-
- Face3 f(p_vtx[0], p_vtx[1], p_vtx[2]);
- Vector3 inters = f.get_closest_point_to(p_aabb.position + p_aabb.size * 0.5);
-
- Vector2 uv = get_uv(inters, p_vtx, p_uv);
-
- int uv_x = CLAMP(Math::fposmod(uv.x, 1.0f) * bake_texture_size, 0, bake_texture_size - 1);
- int uv_y = CLAMP(Math::fposmod(uv.y, 1.0f) * bake_texture_size, 0, bake_texture_size - 1);
-
- int ofs = uv_y * bake_texture_size + uv_x;
-
- alpha = 1.0 / (color_scan_cell_width * color_scan_cell_width);
-
- albedo_accum.r = p_material.albedo[ofs].r * alpha;
- albedo_accum.g = p_material.albedo[ofs].g * alpha;
- albedo_accum.b = p_material.albedo[ofs].b * alpha;
- albedo_accum.a = p_material.albedo[ofs].a * alpha;
-
- emission_accum.r = p_material.emission[ofs].r * alpha;
- emission_accum.g = p_material.emission[ofs].g * alpha;
- emission_accum.b = p_material.emission[ofs].b * alpha;
-
- normal_accum *= alpha;
-
- } else {
-
- float accdiv = 1.0 / (color_scan_cell_width * color_scan_cell_width);
- alpha *= accdiv;
-
- albedo_accum.r *= accdiv;
- albedo_accum.g *= accdiv;
- albedo_accum.b *= accdiv;
- albedo_accum.a *= accdiv;
-
- emission_accum.r *= accdiv;
- emission_accum.g *= accdiv;
- emission_accum.b *= accdiv;
-
- normal_accum *= accdiv;
- }
-
- //put this temporarily here, corrected in a later step
- p_baker->bake_cells[p_idx].albedo[0] += albedo_accum.r;
- p_baker->bake_cells[p_idx].albedo[1] += albedo_accum.g;
- p_baker->bake_cells[p_idx].albedo[2] += albedo_accum.b;
- p_baker->bake_cells[p_idx].emission[0] += emission_accum.r;
- p_baker->bake_cells[p_idx].emission[1] += emission_accum.g;
- p_baker->bake_cells[p_idx].emission[2] += emission_accum.b;
- p_baker->bake_cells[p_idx].normal[0] += normal_accum.x;
- p_baker->bake_cells[p_idx].normal[1] += normal_accum.y;
- p_baker->bake_cells[p_idx].normal[2] += normal_accum.z;
- p_baker->bake_cells[p_idx].alpha += alpha;
-
- } else {
- //go down
-
- int half = (1 << (p_baker->cell_subdiv - 1)) >> (p_level + 1);
- for (int i = 0; i < 8; i++) {
-
- AABB aabb = p_aabb;
- aabb.size *= 0.5;
-
- int nx = p_x;
- int ny = p_y;
- int nz = p_z;
-
- if (i & 1) {
- aabb.position.x += aabb.size.x;
- nx += half;
- }
- if (i & 2) {
- aabb.position.y += aabb.size.y;
- ny += half;
- }
- if (i & 4) {
- aabb.position.z += aabb.size.z;
- nz += half;
- }
- //make sure to not plot beyond limits
- if (nx < 0 || nx >= p_baker->axis_cell_size[0] || ny < 0 || ny >= p_baker->axis_cell_size[1] || nz < 0 || nz >= p_baker->axis_cell_size[2])
- continue;
-
- {
- AABB test_aabb = aabb;
- //test_aabb.grow_by(test_aabb.get_longest_axis_size()*0.05); //grow a bit to avoid numerical error in real-time
- Vector3 qsize = test_aabb.size * 0.5; //quarter size, for fast aabb test
-
- if (!fast_tri_box_overlap(test_aabb.position + qsize, qsize, p_vtx)) {
- //if (!Face3(p_vtx[0],p_vtx[1],p_vtx[2]).intersects_aabb2(aabb)) {
- //does not fit in child, go on
- continue;
- }
- }
-
- if (p_baker->bake_cells[p_idx].childs[i] == Baker::CHILD_EMPTY) {
- //sub cell must be created
-
- uint32_t child_idx = p_baker->bake_cells.size();
- p_baker->bake_cells[p_idx].childs[i] = child_idx;
- p_baker->bake_cells.resize(p_baker->bake_cells.size() + 1);
- p_baker->bake_cells[child_idx].level = p_level + 1;
- }
-
- _plot_face(p_baker->bake_cells[p_idx].childs[i], p_level + 1, nx, ny, nz, p_vtx, p_uv, p_material, aabb, p_baker);
- }
- }
-}
-
-void GIProbe::_fixup_plot(int p_idx, int p_level, int p_x, int p_y, int p_z, Baker *p_baker) {
-
- if (p_level == p_baker->cell_subdiv - 1) {
-
- p_baker->leaf_voxel_count++;
- float alpha = p_baker->bake_cells[p_idx].alpha;
-
- p_baker->bake_cells[p_idx].albedo[0] /= alpha;
- p_baker->bake_cells[p_idx].albedo[1] /= alpha;
- p_baker->bake_cells[p_idx].albedo[2] /= alpha;
-
- //transfer emission to light
- p_baker->bake_cells[p_idx].emission[0] /= alpha;
- p_baker->bake_cells[p_idx].emission[1] /= alpha;
- p_baker->bake_cells[p_idx].emission[2] /= alpha;
-
- p_baker->bake_cells[p_idx].normal[0] /= alpha;
- p_baker->bake_cells[p_idx].normal[1] /= alpha;
- p_baker->bake_cells[p_idx].normal[2] /= alpha;
-
- Vector3 n(p_baker->bake_cells[p_idx].normal[0], p_baker->bake_cells[p_idx].normal[1], p_baker->bake_cells[p_idx].normal[2]);
- if (n.length() < 0.01) {
- //too much fight over normal, zero it
- p_baker->bake_cells[p_idx].normal[0] = 0;
- p_baker->bake_cells[p_idx].normal[1] = 0;
- p_baker->bake_cells[p_idx].normal[2] = 0;
- } else {
- n.normalize();
- p_baker->bake_cells[p_idx].normal[0] = n.x;
- p_baker->bake_cells[p_idx].normal[1] = n.y;
- p_baker->bake_cells[p_idx].normal[2] = n.z;
- }
-
- p_baker->bake_cells[p_idx].alpha = 1.0;
-
- /*
- //remove neighbours from used sides
-
- for(int n=0;n<6;n++) {
-
- int ofs[3]={0,0,0};
-
- ofs[n/2]=(n&1)?1:-1;
-
- //convert to x,y,z on this level
- int x=p_x;
- int y=p_y;
- int z=p_z;
-
- x+=ofs[0];
- y+=ofs[1];
- z+=ofs[2];
-
- int ofs_x=0;
- int ofs_y=0;
- int ofs_z=0;
- int size = 1<<p_level;
- int half=size/2;
-
-
- if (x<0 || x>=size || y<0 || y>=size || z<0 || z>=size) {
- //neighbour is out, can't use it
- p_baker->bake_cells[p_idx].used_sides&=~(1<<uint32_t(n));
- continue;
- }
-
- uint32_t neighbour=0;
-
- for(int i=0;i<p_baker->cell_subdiv-1;i++) {
-
- Baker::Cell *bc = &p_baker->bake_cells[neighbour];
-
- 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;
- }
-
- neighbour = bc->childs[child];
- if (neighbour==Baker::CHILD_EMPTY) {
- break;
- }
-
- half>>=1;
- }
-
- if (neighbour!=Baker::CHILD_EMPTY) {
- p_baker->bake_cells[p_idx].used_sides&=~(1<<uint32_t(n));
- }
- }
- */
- } else {
-
- //go down
-
- float alpha_average = 0;
- int half = (1 << (p_baker->cell_subdiv - 1)) >> (p_level + 1);
- for (int i = 0; i < 8; i++) {
-
- uint32_t child = p_baker->bake_cells[p_idx].childs[i];
-
- if (child == Baker::CHILD_EMPTY)
- continue;
-
- int nx = p_x;
- int ny = p_y;
- int nz = p_z;
-
- if (i & 1)
- nx += half;
- if (i & 2)
- ny += half;
- if (i & 4)
- nz += half;
-
- _fixup_plot(child, p_level + 1, nx, ny, nz, p_baker);
- alpha_average += p_baker->bake_cells[child].alpha;
- }
-
- p_baker->bake_cells[p_idx].alpha = alpha_average / 8.0;
- p_baker->bake_cells[p_idx].emission[0] = 0;
- p_baker->bake_cells[p_idx].emission[1] = 0;
- p_baker->bake_cells[p_idx].emission[2] = 0;
- p_baker->bake_cells[p_idx].normal[0] = 0;
- p_baker->bake_cells[p_idx].normal[1] = 0;
- p_baker->bake_cells[p_idx].normal[2] = 0;
- p_baker->bake_cells[p_idx].albedo[0] = 0;
- p_baker->bake_cells[p_idx].albedo[1] = 0;
- p_baker->bake_cells[p_idx].albedo[2] = 0;
- }
-}
-
-Vector<Color> GIProbe::_get_bake_texture(Ref<Image> p_image, const Color &p_color_mul, const Color &p_color_add) {
-
- Vector<Color> ret;
-
- if (p_image.is_null() || p_image->empty()) {
-
- ret.resize(bake_texture_size * bake_texture_size);
- for (int i = 0; i < bake_texture_size * bake_texture_size; i++) {
- ret[i] = p_color_add;
- }
-
- return ret;
- }
- p_image = p_image->duplicate();
-
- if (p_image->is_compressed()) {
- print_line("DECOMPRESSING!!!!");
-
- p_image->decompress();
- }
- p_image->convert(Image::FORMAT_RGBA8);
- p_image->resize(bake_texture_size, bake_texture_size, Image::INTERPOLATE_CUBIC);
-
- PoolVector<uint8_t>::Read r = p_image->get_data().read();
- ret.resize(bake_texture_size * bake_texture_size);
-
- for (int i = 0; i < bake_texture_size * bake_texture_size; i++) {
- Color c;
- c.r = (r[i * 4 + 0] / 255.0) * p_color_mul.r + p_color_add.r;
- c.g = (r[i * 4 + 1] / 255.0) * p_color_mul.g + p_color_add.g;
- c.b = (r[i * 4 + 2] / 255.0) * p_color_mul.b + p_color_add.b;
-
- c.a = r[i * 4 + 3] / 255.0;
-
- ret[i] = c;
- }
-
- return ret;
-}
-
-GIProbe::Baker::MaterialCache GIProbe::_get_material_cache(Ref<Material> p_material, Baker *p_baker) {
-
- //this way of obtaining materials is inaccurate and also does not support some compressed formats very well
- Ref<SpatialMaterial> mat = p_material;
-
- Ref<Material> material = mat; //hack for now
-
- if (p_baker->material_cache.has(material)) {
- return p_baker->material_cache[material];
- }
-
- Baker::MaterialCache mc;
-
- if (mat.is_valid()) {
-
- Ref<Texture> albedo_tex = mat->get_texture(SpatialMaterial::TEXTURE_ALBEDO);
-
- Ref<Image> img_albedo;
- if (albedo_tex.is_valid()) {
-
- img_albedo = albedo_tex->get_data();
- mc.albedo = _get_bake_texture(img_albedo, mat->get_albedo(), Color(0, 0, 0)); // albedo texture, color is multiplicative
- } else {
- mc.albedo = _get_bake_texture(img_albedo, Color(1, 1, 1), mat->get_albedo()); // no albedo texture, color is additive
- }
-
- Ref<Texture> emission_tex = mat->get_texture(SpatialMaterial::TEXTURE_EMISSION);
-
- Color emission_col = mat->get_emission();
- float emission_energy = mat->get_emission_energy();
-
- Ref<Image> img_emission;
-
- if (emission_tex.is_valid()) {
-
- img_emission = emission_tex->get_data();
- }
-
- if (mat->get_emission_operator() == SpatialMaterial::EMISSION_OP_ADD) {
- mc.emission = _get_bake_texture(img_emission, Color(1, 1, 1) * emission_energy, emission_col * emission_energy);
- } else {
- mc.emission = _get_bake_texture(img_emission, emission_col * emission_energy, Color(0, 0, 0));
- }
-
- } else {
- Ref<Image> empty;
-
- mc.albedo = _get_bake_texture(empty, Color(0, 0, 0), Color(1, 1, 1));
- mc.emission = _get_bake_texture(empty, Color(0, 0, 0), Color(0, 0, 0));
- }
-
- p_baker->material_cache[p_material] = mc;
- return mc;
-}
-
-void GIProbe::_plot_mesh(const Transform &p_xform, Ref<Mesh> &p_mesh, Baker *p_baker, const Vector<Ref<Material> > &p_materials, const Ref<Material> &p_override_material) {
-
- for (int i = 0; i < p_mesh->get_surface_count(); i++) {
-
- if (p_mesh->surface_get_primitive_type(i) != Mesh::PRIMITIVE_TRIANGLES)
- continue; //only triangles
-
- Ref<Material> src_material;
-
- if (p_override_material.is_valid()) {
- src_material = p_override_material;
- } else if (i < p_materials.size() && p_materials[i].is_valid()) {
- src_material = p_materials[i];
- } else {
- src_material = p_mesh->surface_get_material(i);
- }
- Baker::MaterialCache material = _get_material_cache(src_material, p_baker);
-
- Array a = p_mesh->surface_get_arrays(i);
-
- PoolVector<Vector3> vertices = a[Mesh::ARRAY_VERTEX];
- PoolVector<Vector3>::Read vr = vertices.read();
- PoolVector<Vector2> uv = a[Mesh::ARRAY_TEX_UV];
- PoolVector<Vector2>::Read uvr;
- PoolVector<int> index = a[Mesh::ARRAY_INDEX];
-
- bool read_uv = false;
-
- if (uv.size()) {
-
- uvr = uv.read();
- read_uv = true;
- }
-
- if (index.size()) {
-
- int facecount = index.size() / 3;
- PoolVector<int>::Read ir = index.read();
-
- for (int j = 0; j < facecount; j++) {
-
- Vector3 vtxs[3];
- Vector2 uvs[3];
-
- for (int k = 0; k < 3; k++) {
- vtxs[k] = p_xform.xform(vr[ir[j * 3 + k]]);
- }
-
- if (read_uv) {
- for (int k = 0; k < 3; k++) {
- uvs[k] = uvr[ir[j * 3 + k]];
- }
- }
-
- //test against original bounds
- if (!fast_tri_box_overlap(-extents, extents * 2, vtxs))
- continue;
- //plot
- _plot_face(0, 0, 0, 0, 0, vtxs, uvs, material, p_baker->po2_bounds, p_baker);
- }
-
- } else {
-
- int facecount = vertices.size() / 3;
-
- for (int j = 0; j < facecount; j++) {
-
- Vector3 vtxs[3];
- Vector2 uvs[3];
-
- for (int k = 0; k < 3; k++) {
- vtxs[k] = p_xform.xform(vr[j * 3 + k]);
- }
-
- if (read_uv) {
- for (int k = 0; k < 3; k++) {
- uvs[k] = uvr[j * 3 + k];
- }
- }
-
- //test against original bounds
- if (!fast_tri_box_overlap(-extents, extents * 2, vtxs))
- continue;
- //plot face
- _plot_face(0, 0, 0, 0, 0, vtxs, uvs, material, p_baker->po2_bounds, p_baker);
- }
- }
- }
-}
-
-void GIProbe::_find_meshes(Node *p_at_node, Baker *p_baker) {
+void GIProbe::_find_meshes(Node *p_at_node, List<PlotMesh> &plot_meshes) {
MeshInstance *mi = Object::cast_to<MeshInstance>(p_at_node);
if (mi && mi->get_flag(GeometryInstance::FLAG_USE_BAKED_LIGHT) && mi->is_visible_in_tree()) {
@@ -1088,14 +342,14 @@ void GIProbe::_find_meshes(Node *p_at_node, Baker *p_baker) {
Transform xf = get_global_transform().affine_inverse() * mi->get_global_transform();
if (AABB(-extents, extents * 2).intersects(xf.xform(aabb))) {
- Baker::PlotMesh pm;
+ PlotMesh pm;
pm.local_xform = xf;
pm.mesh = mesh;
for (int i = 0; i < mesh->get_surface_count(); i++) {
pm.instance_materials.push_back(mi->get_surface_material(i));
}
pm.override_material = mi->get_material_override();
- p_baker->mesh_list.push_back(pm);
+ plot_meshes.push_back(pm);
}
}
}
@@ -1118,10 +372,10 @@ void GIProbe::_find_meshes(Node *p_at_node, Baker *p_baker) {
Transform xf = get_global_transform().affine_inverse() * (s->get_global_transform() * mxf);
if (AABB(-extents, extents * 2).intersects(xf.xform(aabb))) {
- Baker::PlotMesh pm;
+ PlotMesh pm;
pm.local_xform = xf;
pm.mesh = mesh;
- p_baker->mesh_list.push_back(pm);
+ plot_meshes.push_back(pm);
}
}
}
@@ -1133,7 +387,7 @@ void GIProbe::_find_meshes(Node *p_at_node, Baker *p_baker) {
if (!child->get_owner())
continue; //maybe a helper
- _find_meshes(child, p_baker);
+ _find_meshes(child, plot_meshes);
}
}
@@ -1143,145 +397,56 @@ GIProbe::BakeEndFunc GIProbe::bake_end_function = NULL;
void GIProbe::bake(Node *p_from_node, bool p_create_visual_debug) {
- Baker baker;
-
static const int subdiv_value[SUBDIV_MAX] = { 7, 8, 9, 10 };
- baker.cell_subdiv = subdiv_value[subdiv];
- baker.bake_cells.resize(1);
-
- //find out the actual real bounds, power of 2, which gets the highest subdivision
- baker.po2_bounds = AABB(-extents, extents * 2.0);
- int longest_axis = baker.po2_bounds.get_longest_axis_index();
- baker.axis_cell_size[longest_axis] = (1 << (baker.cell_subdiv - 1));
- baker.leaf_voxel_count = 0;
-
- for (int i = 0; i < 3; i++) {
-
- if (i == longest_axis)
- continue;
+ VoxelLightBaker baker;
- baker.axis_cell_size[i] = baker.axis_cell_size[longest_axis];
- float axis_size = baker.po2_bounds.size[longest_axis];
+ baker.begin_bake(subdiv_value[subdiv], AABB(-extents, extents * 2.0));
- //shrink until fit subdiv
- while (axis_size / 2.0 >= baker.po2_bounds.size[i]) {
- axis_size /= 2.0;
- baker.axis_cell_size[i] >>= 1;
- }
-
- baker.po2_bounds.size[i] = baker.po2_bounds.size[longest_axis];
- }
-
- Transform to_bounds;
- to_bounds.basis.scale(Vector3(baker.po2_bounds.size[longest_axis], baker.po2_bounds.size[longest_axis], baker.po2_bounds.size[longest_axis]));
- to_bounds.origin = baker.po2_bounds.position;
-
- Transform to_grid;
- to_grid.basis.scale(Vector3(baker.axis_cell_size[longest_axis], baker.axis_cell_size[longest_axis], baker.axis_cell_size[longest_axis]));
+ List<PlotMesh> mesh_list;
- baker.to_cell_space = to_grid * to_bounds.affine_inverse();
-
- _find_meshes(p_from_node ? p_from_node : get_parent(), &baker);
+ _find_meshes(p_from_node ? p_from_node : get_parent(), mesh_list);
if (bake_begin_function) {
- bake_begin_function(baker.mesh_list.size() + 1);
+ bake_begin_function(mesh_list.size() + 1);
}
int pmc = 0;
- for (List<Baker::PlotMesh>::Element *E = baker.mesh_list.front(); E; E = E->next()) {
+ for (List<PlotMesh>::Element *E = mesh_list.front(); E; E = E->next()) {
if (bake_step_function) {
- bake_step_function(pmc, RTR("Plotting Meshes") + " " + itos(pmc) + "/" + itos(baker.mesh_list.size()));
+ bake_step_function(pmc, RTR("Plotting Meshes") + " " + itos(pmc) + "/" + itos(mesh_list.size()));
}
pmc++;
- _plot_mesh(E->get().local_xform, E->get().mesh, &baker, E->get().instance_materials, E->get().override_material);
+ baker.plot_mesh(E->get().local_xform, E->get().mesh, E->get().instance_materials, E->get().override_material);
}
if (bake_step_function) {
bake_step_function(pmc++, RTR("Finishing Plot"));
}
- _fixup_plot(0, 0, 0, 0, 0, &baker);
+ baker.end_bake();
//create the data for visual server
- PoolVector<int> data;
-
- data.resize(16 + (8 + 1 + 1 + 1 + 1) * baker.bake_cells.size()); //4 for header, rest for rest.
-
- {
- PoolVector<int>::Write w = data.write();
-
- uint32_t *w32 = (uint32_t *)w.ptr();
+ PoolVector<int> data = baker.create_gi_probe_data();
- w32[0] = 0; //version
- w32[1] = baker.cell_subdiv; //subdiv
- w32[2] = baker.axis_cell_size[0];
- w32[3] = baker.axis_cell_size[1];
- w32[4] = baker.axis_cell_size[2];
- w32[5] = baker.bake_cells.size();
- w32[6] = baker.leaf_voxel_count;
-
- int ofs = 16;
-
- for (int i = 0; i < baker.bake_cells.size(); i++) {
-
- for (int j = 0; j < 8; j++) {
- w32[ofs++] = baker.bake_cells[i].childs[j];
- }
-
- { //albedo
- uint32_t rgba = uint32_t(CLAMP(baker.bake_cells[i].albedo[0] * 255.0, 0, 255)) << 16;
- rgba |= uint32_t(CLAMP(baker.bake_cells[i].albedo[1] * 255.0, 0, 255)) << 8;
- rgba |= uint32_t(CLAMP(baker.bake_cells[i].albedo[2] * 255.0, 0, 255)) << 0;
-
- w32[ofs++] = rgba;
- }
- { //emission
-
- Vector3 e(baker.bake_cells[i].emission[0], baker.bake_cells[i].emission[1], baker.bake_cells[i].emission[2]);
- float l = e.length();
- if (l > 0) {
- e.normalize();
- l = CLAMP(l / 8.0, 0, 1.0);
- }
-
- uint32_t em = uint32_t(CLAMP(e[0] * 255, 0, 255)) << 24;
- em |= uint32_t(CLAMP(e[1] * 255, 0, 255)) << 16;
- em |= uint32_t(CLAMP(e[2] * 255, 0, 255)) << 8;
- em |= uint32_t(CLAMP(l * 255, 0, 255));
-
- w32[ofs++] = em;
- }
-
- //w32[ofs++]=baker.bake_cells[i].used_sides;
- { //normal
-
- Vector3 n(baker.bake_cells[i].normal[0], baker.bake_cells[i].normal[1], baker.bake_cells[i].normal[2]);
- n = n * Vector3(0.5, 0.5, 0.5) + Vector3(0.5, 0.5, 0.5);
- uint32_t norm = 0;
-
- norm |= uint32_t(CLAMP(n.x * 255.0, 0, 255)) << 16;
- norm |= uint32_t(CLAMP(n.y * 255.0, 0, 255)) << 8;
- norm |= uint32_t(CLAMP(n.z * 255.0, 0, 255)) << 0;
-
- w32[ofs++] = norm;
- }
-
- {
- uint16_t alpha = CLAMP(uint32_t(baker.bake_cells[i].alpha * 65535.0), 0, 65535);
- uint16_t level = baker.bake_cells[i].level;
-
- w32[ofs++] = (uint32_t(level) << 16) | uint32_t(alpha);
- }
+ if (p_create_visual_debug) {
+ MultiMeshInstance *mmi = memnew(MultiMeshInstance);
+ mmi->set_multimesh(baker.create_debug_multimesh());
+ add_child(mmi);
+#ifdef TOOLS_ENABLED
+ if (get_tree()->get_edited_scene_root() == this) {
+ mmi->set_owner(this);
+ } else {
+ mmi->set_owner(get_owner());
}
- }
+#else
+ mmi->set_owner(get_owner());
+#endif
- if (p_create_visual_debug) {
- _create_debug_mesh(&baker);
} else {
Ref<GIProbeData> probe_data = get_probe_data();
@@ -1290,7 +455,7 @@ void GIProbe::bake(Node *p_from_node, bool p_create_visual_debug) {
probe_data.instance();
probe_data->set_bounds(AABB(-extents, extents * 2.0));
- probe_data->set_cell_size(baker.po2_bounds.size[longest_axis] / baker.axis_cell_size[longest_axis]);
+ probe_data->set_cell_size(baker.get_cell_size());
probe_data->set_dynamic_data(data);
probe_data->set_dynamic_range(dynamic_range);
probe_data->set_energy(energy);
@@ -1299,7 +464,7 @@ void GIProbe::bake(Node *p_from_node, bool p_create_visual_debug) {
probe_data->set_propagation(propagation);
probe_data->set_interior(interior);
probe_data->set_compress(compress);
- probe_data->set_to_cell_xform(baker.to_cell_space);
+ probe_data->set_to_cell_xform(baker.get_to_cell_space_xform());
set_probe_data(probe_data);
}
@@ -1309,135 +474,6 @@ void GIProbe::bake(Node *p_from_node, bool p_create_visual_debug) {
}
}
-void GIProbe::_debug_mesh(int p_idx, int p_level, const AABB &p_aabb, Ref<MultiMesh> &p_multimesh, int &idx, Baker *p_baker) {
-
- if (p_level == p_baker->cell_subdiv - 1) {
-
- Vector3 center = p_aabb.position + p_aabb.size * 0.5;
- Transform xform;
- xform.origin = center;
- xform.basis.scale(p_aabb.size * 0.5);
- p_multimesh->set_instance_transform(idx, xform);
- Color col = Color(p_baker->bake_cells[p_idx].albedo[0], p_baker->bake_cells[p_idx].albedo[1], p_baker->bake_cells[p_idx].albedo[2]);
- //Color col = Color(p_baker->bake_cells[p_idx].emission[0], p_baker->bake_cells[p_idx].emission[1], p_baker->bake_cells[p_idx].emission[2]);
- p_multimesh->set_instance_color(idx, col);
-
- idx++;
-
- } else {
-
- for (int i = 0; i < 8; i++) {
-
- if (p_baker->bake_cells[p_idx].childs[i] == Baker::CHILD_EMPTY)
- continue;
-
- AABB aabb = p_aabb;
- aabb.size *= 0.5;
-
- if (i & 1)
- aabb.position.x += aabb.size.x;
- if (i & 2)
- aabb.position.y += aabb.size.y;
- if (i & 4)
- aabb.position.z += aabb.size.z;
-
- _debug_mesh(p_baker->bake_cells[p_idx].childs[i], p_level + 1, aabb, p_multimesh, idx, p_baker);
- }
- }
-}
-
-void GIProbe::_create_debug_mesh(Baker *p_baker) {
-
- Ref<MultiMesh> mm;
- mm.instance();
-
- mm->set_transform_format(MultiMesh::TRANSFORM_3D);
- mm->set_color_format(MultiMesh::COLOR_8BIT);
- print_line("leaf voxels: " + itos(p_baker->leaf_voxel_count));
- mm->set_instance_count(p_baker->leaf_voxel_count);
-
- Ref<ArrayMesh> mesh;
- mesh.instance();
-
- {
- Array arr;
- arr.resize(Mesh::ARRAY_MAX);
-
- PoolVector<Vector3> vertices;
- PoolVector<Color> colors;
-
- int vtx_idx = 0;
-#define ADD_VTX(m_idx) \
- ; \
- vertices.push_back(face_points[m_idx]); \
- colors.push_back(Color(1, 1, 1, 1)); \
- vtx_idx++;
-
- for (int i = 0; i < 6; i++) {
-
- Vector3 face_points[4];
-
- for (int j = 0; j < 4; j++) {
-
- float v[3];
- v[0] = 1.0;
- v[1] = 1 - 2 * ((j >> 1) & 1);
- v[2] = v[1] * (1 - 2 * (j & 1));
-
- for (int k = 0; k < 3; k++) {
-
- if (i < 3)
- face_points[j][(i + k) % 3] = v[k] * (i >= 3 ? -1 : 1);
- else
- face_points[3 - j][(i + k) % 3] = v[k] * (i >= 3 ? -1 : 1);
- }
- }
-
- //tri 1
- ADD_VTX(0);
- ADD_VTX(1);
- ADD_VTX(2);
- //tri 2
- ADD_VTX(2);
- ADD_VTX(3);
- ADD_VTX(0);
- }
-
- arr[Mesh::ARRAY_VERTEX] = vertices;
- arr[Mesh::ARRAY_COLOR] = colors;
- mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, arr);
- }
-
- {
- Ref<SpatialMaterial> fsm;
- fsm.instance();
- fsm->set_flag(SpatialMaterial::FLAG_SRGB_VERTEX_COLOR, true);
- fsm->set_flag(SpatialMaterial::FLAG_ALBEDO_FROM_VERTEX_COLOR, true);
- fsm->set_flag(SpatialMaterial::FLAG_UNSHADED, true);
- fsm->set_albedo(Color(1, 1, 1, 1));
-
- mesh->surface_set_material(0, fsm);
- }
-
- mm->set_mesh(mesh);
-
- int idx = 0;
- _debug_mesh(0, 0, p_baker->po2_bounds, mm, idx, p_baker);
-
- MultiMeshInstance *mmi = memnew(MultiMeshInstance);
- mmi->set_multimesh(mm);
- add_child(mmi);
-#ifdef TOOLS_ENABLED
- if (get_tree()->get_edited_scene_root() == this) {
- mmi->set_owner(this);
- } else {
- mmi->set_owner(get_owner());
- }
-#else
- mmi->set_owner(get_owner());
-#endif
-}
-
void GIProbe::_debug_bake() {
bake(NULL, true);
@@ -1516,8 +552,6 @@ GIProbe::GIProbe() {
normal_bias = 0.0;
propagation = 0.7;
extents = Vector3(10, 10, 10);
- color_scan_cell_width = 4;
- bake_texture_size = 128;
interior = false;
compress = false;
diff --git a/scene/3d/gi_probe.h b/scene/3d/gi_probe.h
index 324ff8e917..0858af0001 100644
--- a/scene/3d/gi_probe.h
+++ b/scene/3d/gi_probe.h
@@ -100,67 +100,6 @@ public:
typedef void (*BakeEndFunc)();
private:
- //stuff used for bake
- struct Baker {
-
- enum {
- CHILD_EMPTY = 0xFFFFFFFF
- };
- struct Cell {
-
- uint32_t childs[8];
- float albedo[3]; //albedo in RGB24
- float emission[3]; //accumulated light in 16:16 fixed point (needs to be integer for moving lights fast)
- float normal[3];
- uint32_t used_sides;
- float alpha; //used for upsampling
- int level;
-
- Cell() {
- for (int i = 0; i < 8; i++) {
- childs[i] = CHILD_EMPTY;
- }
-
- for (int i = 0; i < 3; i++) {
- emission[i] = 0;
- albedo[i] = 0;
- normal[i] = 0;
- }
- alpha = 0;
- used_sides = 0;
- level = 0;
- }
- };
-
- Vector<Cell> bake_cells;
- int cell_subdiv;
-
- struct MaterialCache {
- //128x128 textures
- Vector<Color> albedo;
- Vector<Color> emission;
- };
-
- Vector<Color> _get_bake_texture(Ref<Image> p_image, const Color &p_color);
- Map<Ref<Material>, MaterialCache> material_cache;
- MaterialCache _get_material_cache(Ref<Material> p_material);
- int leaf_voxel_count;
-
- AABB po2_bounds;
- int axis_cell_size[3];
-
- struct PlotMesh {
- Ref<Material> override_material;
- Vector<Ref<Material> > instance_materials;
- Ref<Mesh> mesh;
- Transform local_xform;
- };
-
- Transform to_cell_space;
-
- List<PlotMesh> mesh_list;
- };
-
Ref<GIProbeData> probe_data;
RID gi_probe;
@@ -175,19 +114,14 @@ private:
bool interior;
bool compress;
- int color_scan_cell_width;
- int bake_texture_size;
-
- Vector<Color> _get_bake_texture(Ref<Image> p_image, const Color &p_color_mul, const Color &p_color_add);
- Baker::MaterialCache _get_material_cache(Ref<Material> p_material, Baker *p_baker);
- void _plot_face(int p_idx, int p_level, int p_x, int p_y, int p_z, const Vector3 *p_vtx, const Vector2 *p_uv, const Baker::MaterialCache &p_material, const AABB &p_aabb, Baker *p_baker);
- void _plot_mesh(const Transform &p_xform, Ref<Mesh> &p_mesh, Baker *p_baker, const Vector<Ref<Material> > &p_materials, const Ref<Material> &p_override_material);
- void _find_meshes(Node *p_at_node, Baker *p_baker);
- void _fixup_plot(int p_idx, int p_level, int p_x, int p_y, int p_z, Baker *p_baker);
-
- void _debug_mesh(int p_idx, int p_level, const AABB &p_aabb, Ref<MultiMesh> &p_multimesh, int &idx, Baker *p_baker);
- void _create_debug_mesh(Baker *p_baker);
+ struct PlotMesh {
+ Ref<Material> override_material;
+ Vector<Ref<Material> > instance_materials;
+ Ref<Mesh> mesh;
+ Transform local_xform;
+ };
+ void _find_meshes(Node *p_at_node, List<PlotMesh> &plot_meshes);
void _debug_bake();
protected:
diff --git a/scene/3d/light.cpp b/scene/3d/light.cpp
index 1fc4e932e8..6eb2028d8e 100644
--- a/scene/3d/light.cpp
+++ b/scene/3d/light.cpp
@@ -142,6 +142,14 @@ PoolVector<Face3> Light::get_faces(uint32_t p_usage_flags) const {
return PoolVector<Face3>();
}
+void Light::set_bake_mode(BakeMode p_mode) {
+ bake_mode = p_mode;
+}
+
+Light::BakeMode Light::get_bake_mode() const {
+ return bake_mode;
+}
+
void Light::_update_visibility() {
if (!is_inside_tree())
@@ -219,12 +227,16 @@ void Light::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_shadow_color", "shadow_color"), &Light::set_shadow_color);
ClassDB::bind_method(D_METHOD("get_shadow_color"), &Light::get_shadow_color);
+ ClassDB::bind_method(D_METHOD("set_bake_mode", "bake_mode"), &Light::set_bake_mode);
+ ClassDB::bind_method(D_METHOD("get_bake_mode"), &Light::get_bake_mode);
+
ADD_GROUP("Light", "light_");
ADD_PROPERTY(PropertyInfo(Variant::COLOR, "light_color", PROPERTY_HINT_COLOR_NO_ALPHA), "set_color", "get_color");
ADD_PROPERTYI(PropertyInfo(Variant::REAL, "light_energy", PROPERTY_HINT_RANGE, "0,16,0.01"), "set_param", "get_param", PARAM_ENERGY);
ADD_PROPERTYI(PropertyInfo(Variant::REAL, "light_indirect_energy", PROPERTY_HINT_RANGE, "0,16,0.01"), "set_param", "get_param", PARAM_INDIRECT_ENERGY);
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "light_negative"), "set_negative", "is_negative");
ADD_PROPERTYI(PropertyInfo(Variant::REAL, "light_specular", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param", "get_param", PARAM_SPECULAR);
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "light_bake_mode", PROPERTY_HINT_ENUM, "Disable,Indirect,All"), "set_bake_mode", "get_bake_mode");
ADD_PROPERTY(PropertyInfo(Variant::INT, "light_cull_mask", PROPERTY_HINT_LAYERS_3D_RENDER), "set_cull_mask", "get_cull_mask");
ADD_GROUP("Shadow", "shadow_");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "shadow_enabled"), "set_shadow", "has_shadow");
@@ -252,6 +264,10 @@ void Light::_bind_methods() {
BIND_ENUM_CONSTANT(PARAM_SHADOW_BIAS);
BIND_ENUM_CONSTANT(PARAM_SHADOW_BIAS_SPLIT_SCALE);
BIND_ENUM_CONSTANT(PARAM_MAX);
+
+ BIND_ENUM_CONSTANT(BAKE_DISABLED);
+ BIND_ENUM_CONSTANT(BAKE_INDIRECT);
+ BIND_ENUM_CONSTANT(BAKE_ALL);
}
Light::Light(VisualServer::LightType p_type) {
@@ -267,6 +283,7 @@ Light::Light(VisualServer::LightType p_type) {
VS::get_singleton()->instance_set_base(get_instance(), light);
reverse_cull = false;
+ bake_mode = BAKE_INDIRECT;
editor_only = false;
set_color(Color(1, 1, 1, 1));
diff --git a/scene/3d/light.h b/scene/3d/light.h
index 33e62214b1..7ba25731d9 100644
--- a/scene/3d/light.h
+++ b/scene/3d/light.h
@@ -63,6 +63,12 @@ public:
PARAM_MAX = VS::LIGHT_PARAM_MAX
};
+ enum BakeMode {
+ BAKE_DISABLED,
+ BAKE_INDIRECT,
+ BAKE_ALL
+ };
+
private:
Color color;
float param[PARAM_MAX];
@@ -74,6 +80,7 @@ private:
VS::LightType type;
bool editor_only;
void _update_visibility();
+ BakeMode bake_mode;
// bind helpers
@@ -114,6 +121,9 @@ public:
void set_shadow_reverse_cull_face(bool p_enable);
bool get_shadow_reverse_cull_face() const;
+ void set_bake_mode(BakeMode p_mode);
+ BakeMode get_bake_mode() const;
+
virtual AABB get_aabb() const;
virtual PoolVector<Face3> get_faces(uint32_t p_usage_flags) const;
@@ -122,6 +132,7 @@ public:
};
VARIANT_ENUM_CAST(Light::Param);
+VARIANT_ENUM_CAST(Light::BakeMode);
class DirectionalLight : public Light {
diff --git a/scene/3d/voxel_light_baker.cpp b/scene/3d/voxel_light_baker.cpp
new file mode 100644
index 0000000000..e684343612
--- /dev/null
+++ b/scene/3d/voxel_light_baker.cpp
@@ -0,0 +1,2373 @@
+#include "voxel_light_baker.h"
+#include "os/os.h"
+#define FINDMINMAX(x0, x1, x2, min, max) \
+ min = max = x0; \
+ if (x1 < min) min = x1; \
+ if (x1 > max) max = x1; \
+ if (x2 < min) min = x2; \
+ if (x2 > max) max = x2;
+
+static bool planeBoxOverlap(Vector3 normal, float d, Vector3 maxbox) {
+ int q;
+ Vector3 vmin, vmax;
+ for (q = 0; q <= 2; q++) {
+ if (normal[q] > 0.0f) {
+ vmin[q] = -maxbox[q];
+ vmax[q] = maxbox[q];
+ } else {
+ vmin[q] = maxbox[q];
+ vmax[q] = -maxbox[q];
+ }
+ }
+ if (normal.dot(vmin) + d > 0.0f) return false;
+ if (normal.dot(vmax) + d >= 0.0f) return true;
+
+ return false;
+}
+
+/*======================== X-tests ========================*/
+#define AXISTEST_X01(a, b, fa, fb) \
+ p0 = a * v0.y - b * v0.z; \
+ p2 = a * v2.y - b * v2.z; \
+ if (p0 < p2) { \
+ min = p0; \
+ max = p2; \
+ } else { \
+ min = p2; \
+ max = p0; \
+ } \
+ rad = fa * boxhalfsize.y + fb * boxhalfsize.z; \
+ if (min > rad || max < -rad) return false;
+
+#define AXISTEST_X2(a, b, fa, fb) \
+ p0 = a * v0.y - b * v0.z; \
+ p1 = a * v1.y - b * v1.z; \
+ if (p0 < p1) { \
+ min = p0; \
+ max = p1; \
+ } else { \
+ min = p1; \
+ max = p0; \
+ } \
+ rad = fa * boxhalfsize.y + fb * boxhalfsize.z; \
+ if (min > rad || max < -rad) return false;
+
+/*======================== Y-tests ========================*/
+#define AXISTEST_Y02(a, b, fa, fb) \
+ p0 = -a * v0.x + b * v0.z; \
+ p2 = -a * v2.x + b * v2.z; \
+ if (p0 < p2) { \
+ min = p0; \
+ max = p2; \
+ } else { \
+ min = p2; \
+ max = p0; \
+ } \
+ rad = fa * boxhalfsize.x + fb * boxhalfsize.z; \
+ if (min > rad || max < -rad) return false;
+
+#define AXISTEST_Y1(a, b, fa, fb) \
+ p0 = -a * v0.x + b * v0.z; \
+ p1 = -a * v1.x + b * v1.z; \
+ if (p0 < p1) { \
+ min = p0; \
+ max = p1; \
+ } else { \
+ min = p1; \
+ max = p0; \
+ } \
+ rad = fa * boxhalfsize.x + fb * boxhalfsize.z; \
+ if (min > rad || max < -rad) return false;
+
+ /*======================== Z-tests ========================*/
+
+#define AXISTEST_Z12(a, b, fa, fb) \
+ p1 = a * v1.x - b * v1.y; \
+ p2 = a * v2.x - b * v2.y; \
+ if (p2 < p1) { \
+ min = p2; \
+ max = p1; \
+ } else { \
+ min = p1; \
+ max = p2; \
+ } \
+ rad = fa * boxhalfsize.x + fb * boxhalfsize.y; \
+ if (min > rad || max < -rad) return false;
+
+#define AXISTEST_Z0(a, b, fa, fb) \
+ p0 = a * v0.x - b * v0.y; \
+ p1 = a * v1.x - b * v1.y; \
+ if (p0 < p1) { \
+ min = p0; \
+ max = p1; \
+ } else { \
+ min = p1; \
+ max = p0; \
+ } \
+ rad = fa * boxhalfsize.x + fb * boxhalfsize.y; \
+ if (min > rad || max < -rad) return false;
+
+static bool fast_tri_box_overlap(const Vector3 &boxcenter, const Vector3 boxhalfsize, const Vector3 *triverts) {
+
+ /* use separating axis theorem to test overlap between triangle and box */
+ /* need to test for overlap in these directions: */
+ /* 1) the {x,y,z}-directions (actually, since we use the AABB of the triangle */
+ /* we do not even need to test these) */
+ /* 2) normal of the triangle */
+ /* 3) crossproduct(edge from tri, {x,y,z}-directin) */
+ /* this gives 3x3=9 more tests */
+ Vector3 v0, v1, v2;
+ float min, max, d, p0, p1, p2, rad, fex, fey, fez;
+ Vector3 normal, e0, e1, e2;
+
+ /* This is the fastest branch on Sun */
+ /* move everything so that the boxcenter is in (0,0,0) */
+
+ v0 = triverts[0] - boxcenter;
+ v1 = triverts[1] - boxcenter;
+ v2 = triverts[2] - boxcenter;
+
+ /* compute triangle edges */
+ e0 = v1 - v0; /* tri edge 0 */
+ e1 = v2 - v1; /* tri edge 1 */
+ e2 = v0 - v2; /* tri edge 2 */
+
+ /* Bullet 3: */
+ /* test the 9 tests first (this was faster) */
+ fex = Math::abs(e0.x);
+ fey = Math::abs(e0.y);
+ fez = Math::abs(e0.z);
+ AXISTEST_X01(e0.z, e0.y, fez, fey);
+ AXISTEST_Y02(e0.z, e0.x, fez, fex);
+ AXISTEST_Z12(e0.y, e0.x, fey, fex);
+
+ fex = Math::abs(e1.x);
+ fey = Math::abs(e1.y);
+ fez = Math::abs(e1.z);
+ AXISTEST_X01(e1.z, e1.y, fez, fey);
+ AXISTEST_Y02(e1.z, e1.x, fez, fex);
+ AXISTEST_Z0(e1.y, e1.x, fey, fex);
+
+ fex = Math::abs(e2.x);
+ fey = Math::abs(e2.y);
+ fez = Math::abs(e2.z);
+ AXISTEST_X2(e2.z, e2.y, fez, fey);
+ AXISTEST_Y1(e2.z, e2.x, fez, fex);
+ AXISTEST_Z12(e2.y, e2.x, fey, fex);
+
+ /* Bullet 1: */
+ /* first test overlap in the {x,y,z}-directions */
+ /* find min, max of the triangle each direction, and test for overlap in */
+ /* that direction -- this is equivalent to testing a minimal AABB around */
+ /* the triangle against the AABB */
+
+ /* test in X-direction */
+ FINDMINMAX(v0.x, v1.x, v2.x, min, max);
+ if (min > boxhalfsize.x || max < -boxhalfsize.x) return false;
+
+ /* test in Y-direction */
+ FINDMINMAX(v0.y, v1.y, v2.y, min, max);
+ if (min > boxhalfsize.y || max < -boxhalfsize.y) return false;
+
+ /* test in Z-direction */
+ FINDMINMAX(v0.z, v1.z, v2.z, min, max);
+ if (min > boxhalfsize.z || max < -boxhalfsize.z) return false;
+
+ /* Bullet 2: */
+ /* test if the box intersects the plane of the triangle */
+ /* compute plane equation of triangle: normal*x+d=0 */
+ normal = e0.cross(e1);
+ d = -normal.dot(v0); /* plane eq: normal.x+d=0 */
+ if (!planeBoxOverlap(normal, d, boxhalfsize)) return false;
+
+ return true; /* box and triangle overlaps */
+}
+
+static _FORCE_INLINE_ Vector2 get_uv(const Vector3 &p_pos, const Vector3 *p_vtx, const Vector2 *p_uv) {
+
+ if (p_pos.distance_squared_to(p_vtx[0]) < CMP_EPSILON2)
+ return p_uv[0];
+ if (p_pos.distance_squared_to(p_vtx[1]) < CMP_EPSILON2)
+ return p_uv[1];
+ if (p_pos.distance_squared_to(p_vtx[2]) < CMP_EPSILON2)
+ return p_uv[2];
+
+ Vector3 v0 = p_vtx[1] - p_vtx[0];
+ Vector3 v1 = p_vtx[2] - p_vtx[0];
+ Vector3 v2 = p_pos - p_vtx[0];
+
+ float d00 = v0.dot(v0);
+ float d01 = v0.dot(v1);
+ float d11 = v1.dot(v1);
+ float d20 = v2.dot(v0);
+ float d21 = v2.dot(v1);
+ float denom = (d00 * d11 - d01 * d01);
+ if (denom == 0)
+ return p_uv[0];
+ float v = (d11 * d20 - d01 * d21) / denom;
+ float w = (d00 * d21 - d01 * d20) / denom;
+ float u = 1.0f - v - w;
+
+ return p_uv[0] * u + p_uv[1] * v + p_uv[2] * w;
+}
+
+void VoxelLightBaker::_plot_face(int p_idx, int p_level, int p_x, int p_y, int p_z, const Vector3 *p_vtx, const Vector2 *p_uv, const MaterialCache &p_material, const AABB &p_aabb) {
+
+ if (p_level == cell_subdiv - 1) {
+ //plot the face by guessing it's albedo and emission value
+
+ //find best axis to map to, for scanning values
+ int closest_axis = 0;
+ float closest_dot = 0;
+
+ Plane plane = Plane(p_vtx[0], p_vtx[1], p_vtx[2]);
+ Vector3 normal = plane.normal;
+
+ for (int i = 0; i < 3; i++) {
+
+ Vector3 axis;
+ axis[i] = 1.0;
+ float dot = ABS(normal.dot(axis));
+ if (i == 0 || dot > closest_dot) {
+ closest_axis = i;
+ closest_dot = dot;
+ }
+ }
+
+ Vector3 axis;
+ axis[closest_axis] = 1.0;
+ Vector3 t1;
+ t1[(closest_axis + 1) % 3] = 1.0;
+ Vector3 t2;
+ t2[(closest_axis + 2) % 3] = 1.0;
+
+ t1 *= p_aabb.size[(closest_axis + 1) % 3] / float(color_scan_cell_width);
+ t2 *= p_aabb.size[(closest_axis + 2) % 3] / float(color_scan_cell_width);
+
+ Color albedo_accum;
+ Color emission_accum;
+ Vector3 normal_accum;
+
+ float alpha = 0.0;
+
+ //map to a grid average in the best axis for this face
+ for (int i = 0; i < color_scan_cell_width; i++) {
+
+ Vector3 ofs_i = float(i) * t1;
+
+ for (int j = 0; j < color_scan_cell_width; j++) {
+
+ Vector3 ofs_j = float(j) * t2;
+
+ Vector3 from = p_aabb.position + ofs_i + ofs_j;
+ Vector3 to = from + t1 + t2 + axis * p_aabb.size[closest_axis];
+ Vector3 half = (to - from) * 0.5;
+
+ //is in this cell?
+ if (!fast_tri_box_overlap(from + half, half, p_vtx)) {
+ continue; //face does not span this cell
+ }
+
+ //go from -size to +size*2 to avoid skipping collisions
+ Vector3 ray_from = from + (t1 + t2) * 0.5 - axis * p_aabb.size[closest_axis];
+ Vector3 ray_to = ray_from + axis * p_aabb.size[closest_axis] * 2;
+
+ if (normal.dot(ray_from - ray_to) < 0) {
+ SWAP(ray_from, ray_to);
+ }
+
+ Vector3 intersection;
+
+ if (!plane.intersects_segment(ray_from, ray_to, &intersection)) {
+ if (ABS(plane.distance_to(ray_from)) < ABS(plane.distance_to(ray_to))) {
+ intersection = plane.project(ray_from);
+ } else {
+
+ intersection = plane.project(ray_to);
+ }
+ }
+
+ intersection = Face3(p_vtx[0], p_vtx[1], p_vtx[2]).get_closest_point_to(intersection);
+
+ Vector2 uv = get_uv(intersection, p_vtx, p_uv);
+
+ int uv_x = CLAMP(Math::fposmod(uv.x, 1.0f) * bake_texture_size, 0, bake_texture_size - 1);
+ int uv_y = CLAMP(Math::fposmod(uv.y, 1.0f) * bake_texture_size, 0, bake_texture_size - 1);
+
+ int ofs = uv_y * bake_texture_size + uv_x;
+ albedo_accum.r += p_material.albedo[ofs].r;
+ albedo_accum.g += p_material.albedo[ofs].g;
+ albedo_accum.b += p_material.albedo[ofs].b;
+ albedo_accum.a += p_material.albedo[ofs].a;
+
+ emission_accum.r += p_material.emission[ofs].r;
+ emission_accum.g += p_material.emission[ofs].g;
+ emission_accum.b += p_material.emission[ofs].b;
+
+ normal_accum += normal;
+
+ alpha += 1.0;
+ }
+ }
+
+ if (alpha == 0) {
+ //could not in any way get texture information.. so use closest point to center
+
+ Face3 f(p_vtx[0], p_vtx[1], p_vtx[2]);
+ Vector3 inters = f.get_closest_point_to(p_aabb.position + p_aabb.size * 0.5);
+
+ Vector2 uv = get_uv(inters, p_vtx, p_uv);
+
+ int uv_x = CLAMP(Math::fposmod(uv.x, 1.0f) * bake_texture_size, 0, bake_texture_size - 1);
+ int uv_y = CLAMP(Math::fposmod(uv.y, 1.0f) * bake_texture_size, 0, bake_texture_size - 1);
+
+ int ofs = uv_y * bake_texture_size + uv_x;
+
+ alpha = 1.0 / (color_scan_cell_width * color_scan_cell_width);
+
+ albedo_accum.r = p_material.albedo[ofs].r * alpha;
+ albedo_accum.g = p_material.albedo[ofs].g * alpha;
+ albedo_accum.b = p_material.albedo[ofs].b * alpha;
+ albedo_accum.a = p_material.albedo[ofs].a * alpha;
+
+ emission_accum.r = p_material.emission[ofs].r * alpha;
+ emission_accum.g = p_material.emission[ofs].g * alpha;
+ emission_accum.b = p_material.emission[ofs].b * alpha;
+
+ normal_accum *= alpha;
+
+ } else {
+
+ float accdiv = 1.0 / (color_scan_cell_width * color_scan_cell_width);
+ alpha *= accdiv;
+
+ albedo_accum.r *= accdiv;
+ albedo_accum.g *= accdiv;
+ albedo_accum.b *= accdiv;
+ albedo_accum.a *= accdiv;
+
+ emission_accum.r *= accdiv;
+ emission_accum.g *= accdiv;
+ emission_accum.b *= accdiv;
+
+ normal_accum *= accdiv;
+ }
+
+ //put this temporarily here, corrected in a later step
+ bake_cells[p_idx].albedo[0] += albedo_accum.r;
+ bake_cells[p_idx].albedo[1] += albedo_accum.g;
+ bake_cells[p_idx].albedo[2] += albedo_accum.b;
+ bake_cells[p_idx].emission[0] += emission_accum.r;
+ bake_cells[p_idx].emission[1] += emission_accum.g;
+ bake_cells[p_idx].emission[2] += emission_accum.b;
+ bake_cells[p_idx].normal[0] += normal_accum.x;
+ bake_cells[p_idx].normal[1] += normal_accum.y;
+ bake_cells[p_idx].normal[2] += normal_accum.z;
+ bake_cells[p_idx].alpha += alpha;
+
+ } else {
+ //go down
+
+ int half = (1 << (cell_subdiv - 1)) >> (p_level + 1);
+ for (int i = 0; i < 8; i++) {
+
+ AABB aabb = p_aabb;
+ aabb.size *= 0.5;
+
+ int nx = p_x;
+ int ny = p_y;
+ int nz = p_z;
+
+ if (i & 1) {
+ aabb.position.x += aabb.size.x;
+ nx += half;
+ }
+ if (i & 2) {
+ aabb.position.y += aabb.size.y;
+ ny += half;
+ }
+ if (i & 4) {
+ aabb.position.z += aabb.size.z;
+ nz += half;
+ }
+ //make sure to not plot beyond limits
+ if (nx < 0 || nx >= axis_cell_size[0] || ny < 0 || ny >= axis_cell_size[1] || nz < 0 || nz >= axis_cell_size[2])
+ continue;
+
+ {
+ AABB test_aabb = aabb;
+ //test_aabb.grow_by(test_aabb.get_longest_axis_size()*0.05); //grow a bit to avoid numerical error in real-time
+ Vector3 qsize = test_aabb.size * 0.5; //quarter size, for fast aabb test
+
+ if (!fast_tri_box_overlap(test_aabb.position + qsize, qsize, p_vtx)) {
+ //if (!Face3(p_vtx[0],p_vtx[1],p_vtx[2]).intersects_aabb2(aabb)) {
+ //does not fit in child, go on
+ continue;
+ }
+ }
+
+ if (bake_cells[p_idx].childs[i] == CHILD_EMPTY) {
+ //sub cell must be created
+
+ uint32_t child_idx = bake_cells.size();
+ bake_cells[p_idx].childs[i] = child_idx;
+ bake_cells.resize(bake_cells.size() + 1);
+ bake_cells[child_idx].level = p_level + 1;
+ }
+
+ _plot_face(bake_cells[p_idx].childs[i], p_level + 1, nx, ny, nz, p_vtx, p_uv, p_material, aabb);
+ }
+ }
+}
+
+Vector<Color> VoxelLightBaker::_get_bake_texture(Ref<Image> p_image, const Color &p_color_mul, const Color &p_color_add) {
+
+ Vector<Color> ret;
+
+ if (p_image.is_null() || p_image->empty()) {
+
+ ret.resize(bake_texture_size * bake_texture_size);
+ for (int i = 0; i < bake_texture_size * bake_texture_size; i++) {
+ ret[i] = p_color_add;
+ }
+
+ return ret;
+ }
+ p_image = p_image->duplicate();
+
+ if (p_image->is_compressed()) {
+ print_line("DECOMPRESSING!!!!");
+
+ p_image->decompress();
+ }
+ p_image->convert(Image::FORMAT_RGBA8);
+ p_image->resize(bake_texture_size, bake_texture_size, Image::INTERPOLATE_CUBIC);
+
+ PoolVector<uint8_t>::Read r = p_image->get_data().read();
+ ret.resize(bake_texture_size * bake_texture_size);
+
+ for (int i = 0; i < bake_texture_size * bake_texture_size; i++) {
+ Color c;
+ c.r = (r[i * 4 + 0] / 255.0) * p_color_mul.r + p_color_add.r;
+ c.g = (r[i * 4 + 1] / 255.0) * p_color_mul.g + p_color_add.g;
+ c.b = (r[i * 4 + 2] / 255.0) * p_color_mul.b + p_color_add.b;
+
+ c.a = r[i * 4 + 3] / 255.0;
+
+ ret[i] = c;
+ }
+
+ return ret;
+}
+
+VoxelLightBaker::MaterialCache VoxelLightBaker::_get_material_cache(Ref<Material> p_material) {
+
+ //this way of obtaining materials is inaccurate and also does not support some compressed formats very well
+ Ref<SpatialMaterial> mat = p_material;
+
+ Ref<Material> material = mat; //hack for now
+
+ if (material_cache.has(material)) {
+ return material_cache[material];
+ }
+
+ MaterialCache mc;
+
+ if (mat.is_valid()) {
+
+ Ref<Texture> albedo_tex = mat->get_texture(SpatialMaterial::TEXTURE_ALBEDO);
+
+ Ref<Image> img_albedo;
+ if (albedo_tex.is_valid()) {
+
+ img_albedo = albedo_tex->get_data();
+ mc.albedo = _get_bake_texture(img_albedo, mat->get_albedo(), Color(0, 0, 0)); // albedo texture, color is multiplicative
+ } else {
+ mc.albedo = _get_bake_texture(img_albedo, Color(1, 1, 1), mat->get_albedo()); // no albedo texture, color is additive
+ }
+
+ Ref<Texture> emission_tex = mat->get_texture(SpatialMaterial::TEXTURE_EMISSION);
+
+ Color emission_col = mat->get_emission();
+ float emission_energy = mat->get_emission_energy();
+
+ Ref<Image> img_emission;
+
+ if (emission_tex.is_valid()) {
+
+ img_emission = emission_tex->get_data();
+ }
+
+ if (mat->get_emission_operator() == SpatialMaterial::EMISSION_OP_ADD) {
+ mc.emission = _get_bake_texture(img_emission, Color(1, 1, 1) * emission_energy, emission_col * emission_energy);
+ } else {
+ mc.emission = _get_bake_texture(img_emission, emission_col * emission_energy, Color(0, 0, 0));
+ }
+
+ } else {
+ Ref<Image> empty;
+
+ mc.albedo = _get_bake_texture(empty, Color(0, 0, 0), Color(1, 1, 1));
+ mc.emission = _get_bake_texture(empty, Color(0, 0, 0), Color(0, 0, 0));
+ }
+
+ material_cache[p_material] = mc;
+ return mc;
+}
+
+void VoxelLightBaker::plot_mesh(const Transform &p_xform, Ref<Mesh> &p_mesh, const Vector<Ref<Material> > &p_materials, const Ref<Material> &p_override_material) {
+
+ for (int i = 0; i < p_mesh->get_surface_count(); i++) {
+
+ if (p_mesh->surface_get_primitive_type(i) != Mesh::PRIMITIVE_TRIANGLES)
+ continue; //only triangles
+
+ Ref<Material> src_material;
+
+ if (p_override_material.is_valid()) {
+ src_material = p_override_material;
+ } else if (i < p_materials.size() && p_materials[i].is_valid()) {
+ src_material = p_materials[i];
+ } else {
+ src_material = p_mesh->surface_get_material(i);
+ }
+ MaterialCache material = _get_material_cache(src_material);
+
+ Array a = p_mesh->surface_get_arrays(i);
+
+ PoolVector<Vector3> vertices = a[Mesh::ARRAY_VERTEX];
+ PoolVector<Vector3>::Read vr = vertices.read();
+ PoolVector<Vector2> uv = a[Mesh::ARRAY_TEX_UV];
+ PoolVector<Vector2>::Read uvr;
+ PoolVector<int> index = a[Mesh::ARRAY_INDEX];
+
+ bool read_uv = false;
+
+ if (uv.size()) {
+
+ uvr = uv.read();
+ read_uv = true;
+ }
+
+ if (index.size()) {
+
+ int facecount = index.size() / 3;
+ PoolVector<int>::Read ir = index.read();
+
+ for (int j = 0; j < facecount; j++) {
+
+ Vector3 vtxs[3];
+ Vector2 uvs[3];
+
+ for (int k = 0; k < 3; k++) {
+ vtxs[k] = p_xform.xform(vr[ir[j * 3 + k]]);
+ }
+
+ if (read_uv) {
+ for (int k = 0; k < 3; k++) {
+ uvs[k] = uvr[ir[j * 3 + k]];
+ }
+ }
+
+ //test against original bounds
+ if (!fast_tri_box_overlap(original_bounds.position + original_bounds.size * 0.5, original_bounds.size * 0.5, vtxs))
+ continue;
+ //plot
+ _plot_face(0, 0, 0, 0, 0, vtxs, uvs, material, po2_bounds);
+ }
+
+ } else {
+
+ int facecount = vertices.size() / 3;
+
+ for (int j = 0; j < facecount; j++) {
+
+ Vector3 vtxs[3];
+ Vector2 uvs[3];
+
+ for (int k = 0; k < 3; k++) {
+ vtxs[k] = p_xform.xform(vr[j * 3 + k]);
+ }
+
+ if (read_uv) {
+ for (int k = 0; k < 3; k++) {
+ uvs[k] = uvr[j * 3 + k];
+ }
+ }
+
+ //test against original bounds
+ if (!fast_tri_box_overlap(original_bounds.position + original_bounds.size * 0.5, original_bounds.size * 0.5, vtxs))
+ continue;
+ //plot face
+ _plot_face(0, 0, 0, 0, 0, vtxs, uvs, material, po2_bounds);
+ }
+ }
+ }
+
+ max_original_cells = bake_cells.size();
+}
+
+void VoxelLightBaker::_init_light_plot(int p_idx, int p_level, int p_x, int p_y, int p_z, uint32_t p_parent) {
+
+ bake_light[p_idx].x = p_x;
+ bake_light[p_idx].y = p_y;
+ bake_light[p_idx].z = p_z;
+
+ if (p_level == cell_subdiv - 1) {
+
+ bake_light[p_idx].next_leaf = first_leaf;
+ first_leaf = p_idx;
+ } else {
+
+ //go down
+ int half = (1 << (cell_subdiv - 1)) >> (p_level + 1);
+ for (int i = 0; i < 8; i++) {
+
+ uint32_t child = bake_cells[p_idx].childs[i];
+
+ if (child == CHILD_EMPTY)
+ continue;
+
+ int nx = p_x;
+ int ny = p_y;
+ int nz = p_z;
+
+ if (i & 1)
+ nx += half;
+ if (i & 2)
+ ny += half;
+ if (i & 4)
+ nz += half;
+
+ _init_light_plot(child, p_level + 1, nx, ny, nz, p_idx);
+ }
+ }
+}
+
+void VoxelLightBaker::begin_bake_light(BakeQuality p_quality, BakeMode p_bake_mode, float p_propagation, float p_energy) {
+ _check_init_light();
+ propagation = p_propagation;
+ bake_quality = p_quality;
+ bake_mode = p_bake_mode;
+ energy = p_energy;
+}
+
+void VoxelLightBaker::_check_init_light() {
+ if (bake_light.size() == 0) {
+
+ direct_lights_baked = false;
+ leaf_voxel_count = 0;
+ _fixup_plot(0, 0); //pre fixup, so normal, albedo, emission, etc. work for lighting.
+ bake_light.resize(bake_cells.size());
+ zeromem(bake_light.ptrw(), bake_light.size() * sizeof(Light));
+ first_leaf = -1;
+ _init_light_plot(0, 0, 0, 0, 0, CHILD_EMPTY);
+ }
+}
+
+static float _get_normal_advance(const Vector3 &p_normal) {
+
+ Vector3 normal = p_normal;
+ Vector3 unorm = normal.abs();
+
+ if ((unorm.x >= unorm.y) && (unorm.x >= unorm.z)) {
+ // x code
+ unorm = normal.x > 0.0 ? Vector3(1.0, 0.0, 0.0) : Vector3(-1.0, 0.0, 0.0);
+ } else if ((unorm.y > unorm.x) && (unorm.y >= unorm.z)) {
+ // y code
+ unorm = normal.y > 0.0 ? Vector3(0.0, 1.0, 0.0) : Vector3(0.0, -1.0, 0.0);
+ } else if ((unorm.z > unorm.x) && (unorm.z > unorm.y)) {
+ // z code
+ unorm = normal.z > 0.0 ? Vector3(0.0, 0.0, 1.0) : Vector3(0.0, 0.0, -1.0);
+ } else {
+ // oh-no we messed up code
+ // has to be
+ unorm = Vector3(1.0, 0.0, 0.0);
+ }
+
+ return 1.0 / normal.dot(unorm);
+}
+
+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)
+};
+
+uint32_t VoxelLightBaker::_find_cell_at_pos(const Cell *cells, int x, int y, int z) {
+
+ uint32_t cell = 0;
+
+ int ofs_x = 0;
+ int ofs_y = 0;
+ int ofs_z = 0;
+ int size = 1 << (cell_subdiv - 1);
+ int half = size / 2;
+
+ if (x < 0 || x >= size)
+ return -1;
+ if (y < 0 || y >= size)
+ return -1;
+ if (z < 0 || z >= size)
+ return -1;
+
+ for (int i = 0; i < cell_subdiv - 1; i++) {
+
+ const Cell *bc = &cells[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->childs[child];
+ if (cell == CHILD_EMPTY)
+ return CHILD_EMPTY;
+
+ half >>= 1;
+ }
+
+ return cell;
+}
+void VoxelLightBaker::plot_light_directional(const Vector3 &p_direction, const Color &p_color, float p_energy, float p_indirect_energy, bool p_direct) {
+
+ _check_init_light();
+
+ float max_len = Vector3(axis_cell_size[0], axis_cell_size[1], axis_cell_size[2]).length() * 1.1;
+
+ if (p_direct)
+ direct_lights_baked = true;
+
+ Vector3 light_axis = p_direction;
+ Plane clip[3];
+ int clip_planes = 0;
+
+ Light *light_data = bake_light.ptrw();
+ const Cell *cells = bake_cells.ptr();
+
+ for (int i = 0; i < 3; i++) {
+
+ if (ABS(light_axis[i]) < CMP_EPSILON)
+ continue;
+ clip[clip_planes].normal[i] = 1.0;
+
+ if (light_axis[i] < 0) {
+
+ clip[clip_planes].d = axis_cell_size[i] + 1;
+ } else {
+ clip[clip_planes].d -= 1.0;
+ }
+
+ clip_planes++;
+ }
+
+ float distance_adv = _get_normal_advance(light_axis);
+
+ int success_count = 0;
+
+ Vector3 light_energy = Vector3(p_color.r, p_color.g, p_color.b) * p_energy * p_indirect_energy;
+
+ int idx = first_leaf;
+ while (idx >= 0) {
+
+ //print_line("plot idx " + itos(idx));
+ Light *light = &light_data[idx];
+
+ Vector3 to(light->x + 0.5, light->y + 0.5, light->z + 0.5);
+ to += -light_axis.sign() * 0.47; //make it more likely to receive a ray
+
+ Vector3 from = to - max_len * light_axis;
+
+ for (int j = 0; j < clip_planes; j++) {
+
+ clip[j].intersects_segment(from, to, &from);
+ }
+
+ float distance = (to - from).length();
+ distance += distance_adv - Math::fmod(distance, distance_adv); //make it reach the center of the box always
+ from = to - light_axis * distance;
+
+ uint32_t result = 0xFFFFFFFF;
+
+ while (distance > -distance_adv) { //use this to avoid precision errors
+
+ result = _find_cell_at_pos(cells, int(floor(from.x)), int(floor(from.y)), int(floor(from.z)));
+ if (result != 0xFFFFFFFF) {
+ break;
+ }
+
+ from += light_axis * distance_adv;
+ distance -= distance_adv;
+ }
+
+ if (result == idx) {
+ //cell hit itself! hooray!
+
+ Vector3 normal(cells[idx].normal[0], cells[idx].normal[1], cells[idx].normal[2]);
+ if (normal == Vector3()) {
+ for (int i = 0; i < 6; i++) {
+ light->accum[i][0] += light_energy.x * cells[idx].albedo[0];
+ light->accum[i][1] += light_energy.y * cells[idx].albedo[1];
+ light->accum[i][2] += light_energy.z * cells[idx].albedo[2];
+ }
+
+ } else {
+
+ for (int i = 0; i < 6; i++) {
+ float s = MAX(0.0, aniso_normal[i].dot(-normal));
+ light->accum[i][0] += light_energy.x * cells[idx].albedo[0] * s;
+ light->accum[i][1] += light_energy.y * cells[idx].albedo[1] * s;
+ light->accum[i][2] += light_energy.z * cells[idx].albedo[2] * s;
+ }
+ }
+
+ for (int i = 0; i < 6; i++) {
+ float s = MAX(0.0, aniso_normal[i].dot(-light_axis)); //light depending on normal for direct
+ light->direct_accum[i][0] += light_energy.x * s;
+ light->direct_accum[i][1] += light_energy.y * s;
+ light->direct_accum[i][2] += light_energy.z * s;
+ }
+ success_count++;
+ }
+
+ idx = light_data[idx].next_leaf;
+ }
+}
+
+void VoxelLightBaker::plot_light_omni(const Vector3 &p_pos, const Color &p_color, float p_energy, float p_indirect_energy, float p_radius, float p_attenutation, bool p_direct) {
+
+ _check_init_light();
+
+ if (p_direct)
+ direct_lights_baked = true;
+
+ Plane clip[3];
+ int clip_planes = 0;
+
+ // uint64_t us = OS::get_singleton()->get_ticks_usec();
+
+ Vector3 light_pos = to_cell_space.xform(p_pos) + Vector3(0.5, 0.5, 0.5);
+ //Vector3 spot_axis = -light_cache.transform.basis.get_axis(2).normalized();
+
+ float local_radius = to_cell_space.basis.xform(Vector3(0, 0, 1)).length() * p_radius;
+
+ Light *light_data = bake_light.ptrw();
+ const Cell *cells = bake_cells.ptr();
+ Vector3 light_energy = Vector3(p_color.r, p_color.g, p_color.b) * p_energy * p_indirect_energy;
+
+ int idx = first_leaf;
+ while (idx >= 0) {
+
+ //print_line("plot idx " + itos(idx));
+ Light *light = &light_data[idx];
+
+ Vector3 to(light->x + 0.5, light->y + 0.5, light->z + 0.5);
+ to += (light_pos - to).sign() * 0.47; //make it more likely to receive a ray
+
+ Vector3 light_axis = (to - light_pos).normalized();
+ float distance_adv = _get_normal_advance(light_axis);
+
+ Vector3 normal(cells[idx].normal[0], cells[idx].normal[1], cells[idx].normal[2]);
+
+ if (normal != Vector3() && normal.dot(-light_axis) < 0.001) {
+ idx = light_data[idx].next_leaf;
+ continue;
+ }
+
+ float att = 1.0;
+ {
+ float d = light_pos.distance_to(to);
+ if (d + distance_adv > local_radius) {
+ idx = light_data[idx].next_leaf;
+ continue; // too far away
+ }
+
+ float dt = CLAMP((d + distance_adv) / local_radius, 0, 1);
+ att *= powf(1.0 - dt, p_attenutation);
+ }
+#if 0
+ if (light_cache.type == VS::LIGHT_SPOT) {
+
+ float angle = Math::rad2deg(acos(light_axis.dot(spot_axis)));
+ if (angle > light_cache.spot_angle)
+ continue;
+
+ float d = CLAMP(angle / light_cache.spot_angle, 1, 0);
+ att *= powf(1.0 - d, light_cache.spot_attenuation);
+ }
+#endif
+ clip_planes = 0;
+
+ for (int c = 0; c < 3; c++) {
+
+ if (ABS(light_axis[c]) < CMP_EPSILON)
+ continue;
+ clip[clip_planes].normal[c] = 1.0;
+
+ if (light_axis[c] < 0) {
+
+ clip[clip_planes].d = (1 << (cell_subdiv - 1)) + 1;
+ } else {
+ clip[clip_planes].d -= 1.0;
+ }
+
+ clip_planes++;
+ }
+
+ Vector3 from = light_pos;
+
+ for (int j = 0; j < clip_planes; j++) {
+
+ clip[j].intersects_segment(from, to, &from);
+ }
+
+ float distance = (to - from).length();
+
+ distance -= Math::fmod(distance, distance_adv); //make it reach the center of the box always, but this tame make it closer
+ from = to - light_axis * distance;
+ to += (light_pos - to).sign() * 0.47; //make it more likely to receive a ray
+
+ uint32_t result = 0xFFFFFFFF;
+
+ while (distance > -distance_adv) { //use this to avoid precision errors
+
+ result = _find_cell_at_pos(cells, int(floor(from.x)), int(floor(from.y)), int(floor(from.z)));
+ if (result != 0xFFFFFFFF) {
+ break;
+ }
+
+ from += light_axis * distance_adv;
+ distance -= distance_adv;
+ }
+
+ if (result == idx) {
+ //cell hit itself! hooray!
+
+ if (normal == Vector3()) {
+ for (int i = 0; i < 6; i++) {
+ light->accum[i][0] += light_energy.x * cells[idx].albedo[0] * att;
+ light->accum[i][1] += light_energy.y * cells[idx].albedo[1] * att;
+ light->accum[i][2] += light_energy.z * cells[idx].albedo[2] * att;
+ }
+
+ } else {
+
+ for (int i = 0; i < 6; i++) {
+ float s = MAX(0.0, aniso_normal[i].dot(-normal));
+ light->accum[i][0] += light_energy.x * cells[idx].albedo[0] * s * att;
+ light->accum[i][1] += light_energy.y * cells[idx].albedo[1] * s * att;
+ light->accum[i][2] += light_energy.z * cells[idx].albedo[2] * s * att;
+ }
+ }
+
+ for (int i = 0; i < 6; i++) {
+ float s = MAX(0.0, aniso_normal[i].dot(-light_axis)); //light depending on normal for direct
+ light->direct_accum[i][0] += light_energy.x * s * att;
+ light->direct_accum[i][1] += light_energy.y * s * att;
+ light->direct_accum[i][2] += light_energy.z * s * att;
+ }
+ }
+
+ idx = light_data[idx].next_leaf;
+ }
+}
+
+void VoxelLightBaker::plot_light_spot(const Vector3 &p_pos, const Vector3 &p_axis, const Color &p_color, float p_energy, float p_indirect_energy, float p_radius, float p_attenutation, float p_spot_angle, float p_spot_attenuation, bool p_direct) {
+
+ _check_init_light();
+
+ if (p_direct)
+ direct_lights_baked = true;
+
+ Plane clip[3];
+ int clip_planes = 0;
+
+ // uint64_t us = OS::get_singleton()->get_ticks_usec();
+
+ Vector3 light_pos = to_cell_space.xform(p_pos) + Vector3(0.5, 0.5, 0.5);
+ Vector3 spot_axis = to_cell_space.basis.xform(p_axis).normalized();
+
+ float local_radius = to_cell_space.basis.xform(Vector3(0, 0, 1)).length() * p_radius;
+
+ Light *light_data = bake_light.ptrw();
+ const Cell *cells = bake_cells.ptr();
+ Vector3 light_energy = Vector3(p_color.r, p_color.g, p_color.b) * p_energy * p_indirect_energy;
+
+ int idx = first_leaf;
+ while (idx >= 0) {
+
+ //print_line("plot idx " + itos(idx));
+ Light *light = &light_data[idx];
+
+ Vector3 to(light->x + 0.5, light->y + 0.5, light->z + 0.5);
+
+ Vector3 light_axis = (to - light_pos).normalized();
+ float distance_adv = _get_normal_advance(light_axis);
+
+ Vector3 normal(cells[idx].normal[0], cells[idx].normal[1], cells[idx].normal[2]);
+
+ if (normal != Vector3() && normal.dot(-light_axis) < 0.001) {
+ idx = light_data[idx].next_leaf;
+ continue;
+ }
+
+ float angle = Math::rad2deg(Math::acos(light_axis.dot(-spot_axis)));
+ if (angle > p_spot_angle) {
+ idx = light_data[idx].next_leaf;
+ continue; // too far away
+ }
+
+ float att = Math::pow(1.0 - angle / p_spot_angle, p_spot_attenuation);
+
+ {
+ float d = light_pos.distance_to(to);
+ if (d + distance_adv > local_radius) {
+ idx = light_data[idx].next_leaf;
+ continue; // too far away
+ }
+
+ float dt = CLAMP((d + distance_adv) / local_radius, 0, 1);
+ att *= powf(1.0 - dt, p_attenutation);
+ }
+#if 0
+ if (light_cache.type == VS::LIGHT_SPOT) {
+
+ float angle = Math::rad2deg(acos(light_axis.dot(spot_axis)));
+ if (angle > light_cache.spot_angle)
+ continue;
+
+ float d = CLAMP(angle / light_cache.spot_angle, 1, 0);
+ att *= powf(1.0 - d, light_cache.spot_attenuation);
+ }
+#endif
+ clip_planes = 0;
+
+ for (int c = 0; c < 3; c++) {
+
+ if (ABS(light_axis[c]) < CMP_EPSILON)
+ continue;
+ clip[clip_planes].normal[c] = 1.0;
+
+ if (light_axis[c] < 0) {
+
+ clip[clip_planes].d = (1 << (cell_subdiv - 1)) + 1;
+ } else {
+ clip[clip_planes].d -= 1.0;
+ }
+
+ clip_planes++;
+ }
+
+ Vector3 from = light_pos;
+
+ for (int j = 0; j < clip_planes; j++) {
+
+ clip[j].intersects_segment(from, to, &from);
+ }
+
+ float distance = (to - from).length();
+
+ distance -= Math::fmod(distance, distance_adv); //make it reach the center of the box always, but this tame make it closer
+ from = to - light_axis * distance;
+
+ uint32_t result = 0xFFFFFFFF;
+
+ while (distance > -distance_adv) { //use this to avoid precision errors
+
+ result = _find_cell_at_pos(cells, int(floor(from.x)), int(floor(from.y)), int(floor(from.z)));
+ if (result != 0xFFFFFFFF) {
+ break;
+ }
+
+ from += light_axis * distance_adv;
+ distance -= distance_adv;
+ }
+
+ if (result == idx) {
+ //cell hit itself! hooray!
+
+ if (normal == Vector3()) {
+ for (int i = 0; i < 6; i++) {
+ light->accum[i][0] += light_energy.x * cells[idx].albedo[0] * att;
+ light->accum[i][1] += light_energy.y * cells[idx].albedo[1] * att;
+ light->accum[i][2] += light_energy.z * cells[idx].albedo[2] * att;
+ }
+
+ } else {
+
+ for (int i = 0; i < 6; i++) {
+ float s = MAX(0.0, aniso_normal[i].dot(-normal));
+ light->accum[i][0] += light_energy.x * cells[idx].albedo[0] * s * att;
+ light->accum[i][1] += light_energy.y * cells[idx].albedo[1] * s * att;
+ light->accum[i][2] += light_energy.z * cells[idx].albedo[2] * s * att;
+ }
+ }
+
+ for (int i = 0; i < 6; i++) {
+ float s = MAX(0.0, aniso_normal[i].dot(-light_axis)); //light depending on normal for direct
+ light->direct_accum[i][0] += light_energy.x * s * att;
+ light->direct_accum[i][1] += light_energy.y * s * att;
+ light->direct_accum[i][2] += light_energy.z * s * att;
+ }
+ }
+
+ idx = light_data[idx].next_leaf;
+ }
+}
+
+void VoxelLightBaker::_fixup_plot(int p_idx, int p_level) {
+
+ if (p_level == cell_subdiv - 1) {
+
+ leaf_voxel_count++;
+ float alpha = bake_cells[p_idx].alpha;
+
+ bake_cells[p_idx].albedo[0] /= alpha;
+ bake_cells[p_idx].albedo[1] /= alpha;
+ bake_cells[p_idx].albedo[2] /= alpha;
+
+ //transfer emission to light
+ bake_cells[p_idx].emission[0] /= alpha;
+ bake_cells[p_idx].emission[1] /= alpha;
+ bake_cells[p_idx].emission[2] /= alpha;
+
+ bake_cells[p_idx].normal[0] /= alpha;
+ bake_cells[p_idx].normal[1] /= alpha;
+ bake_cells[p_idx].normal[2] /= alpha;
+
+ Vector3 n(bake_cells[p_idx].normal[0], bake_cells[p_idx].normal[1], bake_cells[p_idx].normal[2]);
+ if (n.length() < 0.01) {
+ //too much fight over normal, zero it
+ bake_cells[p_idx].normal[0] = 0;
+ bake_cells[p_idx].normal[1] = 0;
+ bake_cells[p_idx].normal[2] = 0;
+ } else {
+ n.normalize();
+ bake_cells[p_idx].normal[0] = n.x;
+ bake_cells[p_idx].normal[1] = n.y;
+ bake_cells[p_idx].normal[2] = n.z;
+ }
+
+ bake_cells[p_idx].alpha = 1.0;
+
+ /*if (bake_light.size()) {
+ for(int i=0;i<6;i++) {
+
+ }
+ }*/
+
+ } else {
+
+ //go down
+
+ bake_cells[p_idx].emission[0] = 0;
+ bake_cells[p_idx].emission[1] = 0;
+ bake_cells[p_idx].emission[2] = 0;
+ bake_cells[p_idx].normal[0] = 0;
+ bake_cells[p_idx].normal[1] = 0;
+ bake_cells[p_idx].normal[2] = 0;
+ bake_cells[p_idx].albedo[0] = 0;
+ bake_cells[p_idx].albedo[1] = 0;
+ bake_cells[p_idx].albedo[2] = 0;
+ if (bake_light.size()) {
+ for (int j = 0; j < 6; j++) {
+ bake_light[p_idx].accum[j][0] = 0;
+ bake_light[p_idx].accum[j][1] = 0;
+ bake_light[p_idx].accum[j][2] = 0;
+ }
+ }
+
+ float alpha_average = 0;
+ int children_found = 0;
+
+ for (int i = 0; i < 8; i++) {
+
+ uint32_t child = bake_cells[p_idx].childs[i];
+
+ if (child == CHILD_EMPTY)
+ continue;
+
+ _fixup_plot(child, p_level + 1);
+ alpha_average += bake_cells[child].alpha;
+
+ if (bake_light.size() > 0) {
+ for (int j = 0; j < 6; j++) {
+ bake_light[p_idx].accum[j][0] += bake_light[child].accum[j][0];
+ bake_light[p_idx].accum[j][1] += bake_light[child].accum[j][1];
+ bake_light[p_idx].accum[j][2] += bake_light[child].accum[j][2];
+ }
+ bake_cells[p_idx].emission[0] += bake_cells[child].emission[0];
+ bake_cells[p_idx].emission[1] += bake_cells[child].emission[1];
+ bake_cells[p_idx].emission[2] += bake_cells[child].emission[2];
+ }
+
+ children_found++;
+ }
+
+ bake_cells[p_idx].alpha = alpha_average / 8.0;
+ if (bake_light.size() && children_found) {
+ float divisor = Math::lerp(8, children_found, propagation);
+ for (int j = 0; j < 6; j++) {
+ bake_light[p_idx].accum[j][0] /= divisor;
+ bake_light[p_idx].accum[j][1] /= divisor;
+ bake_light[p_idx].accum[j][2] /= divisor;
+ }
+ bake_cells[p_idx].emission[0] /= divisor;
+ bake_cells[p_idx].emission[1] /= divisor;
+ bake_cells[p_idx].emission[2] /= divisor;
+ }
+ }
+}
+
+//make sure any cell (save for the root) has an empty cell previous to it, so it can be interpolated into
+
+void VoxelLightBaker::_plot_triangle(Vector2 *vertices, Vector3 *positions, Vector3 *normals, LightMap *pixels, int width, int height) {
+
+ int x[3];
+ int y[3];
+
+ for (int j = 0; j < 3; j++) {
+
+ x[j] = vertices[j].x * width;
+ y[j] = vertices[j].y * height;
+ //x[j] = CLAMP(x[j], 0, bt.width - 1);
+ //y[j] = CLAMP(y[j], 0, bt.height - 1);
+ }
+
+ // sort the points vertically
+ if (y[1] > y[2]) {
+ SWAP(x[1], x[2]);
+ SWAP(y[1], y[2]);
+ SWAP(positions[1], positions[2]);
+ SWAP(normals[1], normals[2]);
+ }
+ if (y[0] > y[1]) {
+ SWAP(x[0], x[1]);
+ SWAP(y[0], y[1]);
+ SWAP(positions[0], positions[1]);
+ SWAP(normals[0], normals[1]);
+ }
+ if (y[1] > y[2]) {
+ SWAP(x[1], x[2]);
+ SWAP(y[1], y[2]);
+ SWAP(positions[1], positions[2]);
+ SWAP(normals[1], normals[2]);
+ }
+
+ double dx_far = double(x[2] - x[0]) / (y[2] - y[0] + 1);
+ double dx_upper = double(x[1] - x[0]) / (y[1] - y[0] + 1);
+ double dx_low = double(x[2] - x[1]) / (y[2] - y[1] + 1);
+ double xf = x[0];
+ double xt = x[0] + dx_upper; // if y[0] == y[1], special case
+ for (int yi = y[0]; yi <= (y[2] > height - 1 ? height - 1 : y[2]); yi++) {
+ if (yi >= 0) {
+ for (int xi = (xf > 0 ? int(xf) : 0); xi <= (xt < width ? xt : width - 1); xi++) {
+ //pixels[int(x + y * width)] = color;
+
+ Vector2 v0 = Vector2(x[1] - x[0], y[1] - y[0]);
+ Vector2 v1 = Vector2(x[2] - x[0], y[2] - y[0]);
+ //vertices[2] - vertices[0];
+ Vector2 v2 = Vector2(xi - x[0], yi - y[0]);
+ float d00 = v0.dot(v0);
+ float d01 = v0.dot(v1);
+ float d11 = v1.dot(v1);
+ float d20 = v2.dot(v0);
+ float d21 = v2.dot(v1);
+ float denom = (d00 * d11 - d01 * d01);
+ Vector3 pos;
+ Vector3 normal;
+ if (denom == 0) {
+ pos = positions[0];
+ normal = normals[0];
+ } else {
+ float v = (d11 * d20 - d01 * d21) / denom;
+ float w = (d00 * d21 - d01 * d20) / denom;
+ float u = 1.0f - v - w;
+ pos = positions[0] * u + positions[1] * v + positions[2] * w;
+ normal = normals[0] * u + normals[1] * v + normals[2] * w;
+ }
+
+ int ofs = yi * width + xi;
+ pixels[ofs].normal = normal;
+ pixels[ofs].pos = pos;
+ }
+
+ for (int xi = (xf < width ? int(xf) : width - 1); xi >= (xt > 0 ? xt : 0); xi--) {
+ //pixels[int(x + y * width)] = color;
+ Vector2 v0 = Vector2(x[1] - x[0], y[1] - y[0]);
+ Vector2 v1 = Vector2(x[2] - x[0], y[2] - y[0]);
+ //vertices[2] - vertices[0];
+ Vector2 v2 = Vector2(xi - x[0], yi - y[0]);
+ float d00 = v0.dot(v0);
+ float d01 = v0.dot(v1);
+ float d11 = v1.dot(v1);
+ float d20 = v2.dot(v0);
+ float d21 = v2.dot(v1);
+ float denom = (d00 * d11 - d01 * d01);
+ Vector3 pos;
+ Vector3 normal;
+ if (denom == 0) {
+ pos = positions[0];
+ normal = normals[0];
+ } else {
+ float v = (d11 * d20 - d01 * d21) / denom;
+ float w = (d00 * d21 - d01 * d20) / denom;
+ float u = 1.0f - v - w;
+ pos = positions[0] * u + positions[1] * v + positions[2] * w;
+ normal = normals[0] * u + normals[1] * v + normals[2] * w;
+ }
+
+ int ofs = yi * width + xi;
+ pixels[ofs].normal = normal;
+ pixels[ofs].pos = pos;
+ }
+ }
+ xf += dx_far;
+ if (yi < y[1])
+ xt += dx_upper;
+ else
+ xt += dx_low;
+ }
+}
+
+void VoxelLightBaker::_sample_baked_octree_filtered_and_anisotropic(const Vector3 &p_posf, const Vector3 &p_direction, float p_level, Vector3 &r_color, float &r_alpha) {
+
+ int size = 1 << (cell_subdiv - 1);
+
+ int clamp_v = size - 1;
+ //first of all, clamp
+ Vector3 pos;
+ pos.x = CLAMP(p_posf.x, 0, clamp_v);
+ pos.y = CLAMP(p_posf.y, 0, clamp_v);
+ pos.z = CLAMP(p_posf.z, 0, clamp_v);
+
+ float level = (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;
+ }
+
+ const Cell *cells = bake_cells.ptr();
+ const Light *light = bake_light.ptr();
+
+ Vector3 color[2][8];
+ float alpha[2][8];
+ zeromem(alpha, 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 << (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 Cell *bc = &cells[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->childs[child];
+ if (cell == CHILD_EMPTY)
+ break;
+
+ half >>= 1;
+ }
+
+ if (cell == CHILD_EMPTY) {
+ alpha[c][n] = 0;
+ } else {
+ alpha[c][n] = cells[cell].alpha;
+
+ for (int i = 0; i < 6; i++) {
+ //anisotropic read light
+ float amount = p_direction.dot(aniso_normal[i]);
+ //if (c == 0) {
+ // print_line("\t" + itos(n) + " aniso " + itos(i) + " " + rtos(light[cell].accum[i][0]) + " VEC: " + aniso_normal[i]);
+ //}
+ if (amount < 0)
+ amount = 0;
+ //amount = 1;
+ color[c][n].x += light[cell].accum[i][0] * amount;
+ color[c][n].y += light[cell].accum[i][1] * amount;
+ color[c][n].z += light[cell].accum[i][2] * amount;
+ }
+
+ color[c][n].x += cells[cell].emission[0];
+ color[c][n].y += cells[cell].emission[1];
+ color[c][n].z += cells[cell].emission[2];
+ }
+
+ //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];
+
+ pos_fract[0].x = Math::fmod(pos.x, target_level_size) / target_level_size;
+ pos_fract[0].y = Math::fmod(pos.y, target_level_size) / target_level_size;
+ pos_fract[0].z = Math::fmod(pos.z, target_level_size) / target_level_size;
+
+ target_level_size = size >> MAX(0, target_level - 1);
+
+ pos_fract[1].x = Math::fmod(pos.x, target_level_size) / target_level_size;
+ pos_fract[1].y = Math::fmod(pos.y, target_level_size) / target_level_size;
+ pos_fract[1].z = Math::fmod(pos.z, target_level_size) / target_level_size;
+
+ 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));
+}
+
+Vector3 VoxelLightBaker::_voxel_cone_trace(const Vector3 &p_pos, const Vector3 &p_normal, float p_aperture) {
+
+ float bias = 2.5;
+ float max_distance = (Vector3(1, 1, 1) * (1 << (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);
+ //print_line("VCT: pos " + (p_pos + dist * p_normal) + " dist " + rtos(dist) + " mipmap " + rtos(log2(diameter)) + " alpha " + rtos(alpha));
+ //Plane scolor = textureLod(probe, (pos + dist * direction) * cell_size, log2(diameter) );
+ _sample_baked_octree_filtered_and_anisotropic(p_pos + dist * p_normal, p_normal, log2(diameter), scolor, salpha);
+ float a = (1.0 - alpha);
+ color += scolor * a;
+ alpha += a * salpha;
+ dist += diameter * 0.5;
+ }
+
+ /*if (blend_ambient) {
+ color.rgb = mix(ambient,color.rgb,min(1.0,alpha/0.95));
+ }*/
+
+ return color;
+}
+
+Vector3 VoxelLightBaker::_compute_pixel_light_at_pos(const Vector3 &p_pos, const Vector3 &p_normal) {
+
+ //find arbitrary tangent and bitangent, then build a matrix
+ Vector3 v0 = Math::abs(p_normal.z) < 0.999 ? Vector3(0, 0, 1) : Vector3(0, 1, 0);
+ Vector3 tangent = v0.cross(p_normal).normalized();
+ Vector3 bitangent = tangent.cross(p_normal).normalized();
+ Basis normal_xform = Basis(tangent, bitangent, p_normal).transposed();
+
+ // print_line("normal xform: " + normal_xform);
+ const Vector3 *cone_dirs;
+ const float *cone_weights;
+ int cone_dir_count;
+ float cone_aperture;
+
+ switch (bake_quality) {
+ case BAKE_QUALITY_LOW: {
+ //default quality
+ static const Vector3 dirs[4] = {
+ Vector3(0.707107, 0, 0.707107),
+ Vector3(0, 0.707107, 0.707107),
+ Vector3(-0.707107, 0, 0.707107),
+ Vector3(0, -0.707107, 0.707107)
+ };
+
+ static const float weights[4] = { 0.25, 0.25, 0.25, 0.25 };
+
+ cone_dirs = dirs;
+ cone_dir_count = 4;
+ cone_aperture = 1.0; // tan(angle) 90 degrees
+ cone_weights = weights;
+ } break;
+ case BAKE_QUALITY_MEDIUM: {
+ //default quality
+ static const Vector3 dirs[6] = {
+ 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)
+ };
+ static const float weights[6] = { 0.25, 0.15, 0.15, 0.15, 0.15, 0.15 };
+ //
+ cone_dirs = dirs;
+ cone_dir_count = 6;
+ cone_aperture = 0.577; // tan(angle) 60 degrees
+ cone_weights = weights;
+ } break;
+ case BAKE_QUALITY_HIGH: {
+
+ //high qualily
+ static const Vector3 dirs[10] = {
+ Vector3(0.8781648411741658, 0.0, 0.478358141694643),
+ Vector3(0.5369754325592234, 0.6794204427701518, 0.5000452447267606),
+ Vector3(-0.19849436573466497, 0.8429904390140635, 0.49996710542041645),
+ Vector3(-0.7856196499811189, 0.3639120321329737, 0.5003696617825604),
+ Vector3(-0.7856196499811189, -0.3639120321329737, 0.5003696617825604),
+ Vector3(-0.19849436573466497, -0.8429904390140635, 0.49996710542041645),
+ Vector3(0.5369754325592234, -0.6794204427701518, 0.5000452447267606),
+ Vector3(-0.4451656858129485, 0.0, 0.8954482185892644),
+ Vector3(0.19124006749743122, 0.39355745585016605, 0.8991883926788214),
+ Vector3(0.19124006749743122, -0.39355745585016605, 0.8991883926788214),
+ };
+ static const float weights[10] = { 0.08571, 0.08571, 0.08571, 0.08571, 0.08571, 0.08571, 0.08571, 0.133333, 0.133333, 0.13333 };
+ cone_dirs = dirs;
+ cone_dir_count = 10;
+ cone_aperture = 0.404; // tan(angle) 45 degrees
+ cone_weights = weights;
+ } break;
+ }
+
+ Vector3 accum;
+
+ for (int i = 0; i < cone_dir_count; i++) {
+ // if (i > 0)
+ // continue;
+ Vector3 dir = normal_xform.xform(cone_dirs[i]).normalized(); //normal may not completely correct when transformed to cell
+ //print_line("direction: " + dir);
+ accum += _voxel_cone_trace(p_pos, dir, cone_aperture) * cone_weights[i];
+ }
+
+ return accum;
+}
+
+Vector3 VoxelLightBaker::_compute_ray_trace_at_pos(const Vector3 &p_pos, const Vector3 &p_normal) {
+
+ int samples_per_quality[3] = { 48, 128, 512 };
+
+ int samples = samples_per_quality[bake_quality];
+
+ //create a basis in Z
+ Vector3 v0 = Math::abs(p_normal.z) < 0.999 ? Vector3(0, 0, 1) : Vector3(0, 1, 0);
+ Vector3 tangent = v0.cross(p_normal).normalized();
+ Vector3 bitangent = tangent.cross(p_normal).normalized();
+ Basis normal_xform = Basis(tangent, bitangent, p_normal).transposed();
+
+ float bias = 1.5;
+ int max_level = cell_subdiv - 1;
+ int size = 1 << max_level;
+
+ Vector3 accum;
+ float spread = Math::deg2rad(80.0);
+
+ const Light *light = bake_light.ptr();
+ const Cell *cells = bake_cells.ptr();
+
+ for (int i = 0; i < samples; i++) {
+
+ float random_angle1 = (((rand() % 65535) / 65535.0) * 2.0 - 1.0) * spread;
+ Vector3 axis(0, sin(random_angle1), cos(random_angle1));
+ float random_angle2 = ((rand() % 65535) / 65535.0) * Math_PI * 2.0;
+ Basis rot(Vector3(0, 0, 1), random_angle2);
+ axis = rot.xform(axis);
+
+ Vector3 direction = normal_xform.xform(axis).normalized();
+
+ Vector3 pos = p_pos + Vector3(0.5, 0.5, 0.5) + direction * bias;
+
+ Vector3 advance = direction * _get_normal_advance(direction);
+
+ uint32_t cell = CHILD_EMPTY;
+
+ while (cell == CHILD_EMPTY) {
+
+ int x = int(pos.x);
+ int y = int(pos.y);
+ int z = int(pos.z);
+
+ int ofs_x = 0;
+ int ofs_y = 0;
+ int ofs_z = 0;
+ int half = size / 2;
+
+ if (x < 0 || x >= size)
+ break;
+ if (y < 0 || y >= size)
+ break;
+ if (z < 0 || z >= size)
+ break;
+
+ //int level_limit = max_level;
+
+ cell = 0; //start from root
+ for (int i = 0; i < max_level; i++) {
+
+ const Cell *bc = &cells[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->childs[child];
+ if (cell == CHILD_EMPTY)
+ break;
+
+ half >>= 1;
+ }
+
+ pos += advance;
+ }
+
+ if (cell != CHILD_EMPTY) {
+ for (int i = 0; i < 6; i++) {
+ //anisotropic read light
+ float amount = direction.dot(aniso_normal[i]);
+ if (amount < 0)
+ amount = 0;
+ accum.x += light[cell].accum[i][0] * amount;
+ accum.y += light[cell].accum[i][1] * amount;
+ accum.z += light[cell].accum[i][2] * amount;
+ }
+ }
+ }
+
+ return accum / samples;
+}
+
+Error VoxelLightBaker::make_lightmap(const Transform &p_xform, Ref<Mesh> &p_mesh, LightMapData &r_lightmap, bool (*p_bake_time_func)(void *, float, float), void *p_bake_time_ud) {
+
+ //transfer light information to a lightmap
+ Ref<Mesh> mesh = p_mesh;
+
+ int width = mesh->get_lightmap_size_hint().x;
+ int height = mesh->get_lightmap_size_hint().y;
+
+ //step 1 - create lightmap
+ Vector<LightMap> lightmap;
+ lightmap.resize(width * height);
+
+ Transform xform = to_cell_space * p_xform;
+
+ //step 2 plot faces to lightmap
+ for (int i = 0; i < mesh->get_surface_count(); i++) {
+ Array arrays = mesh->surface_get_arrays(i);
+ PoolVector<Vector3> vertices = arrays[Mesh::ARRAY_VERTEX];
+ PoolVector<Vector3> normals = arrays[Mesh::ARRAY_NORMAL];
+ PoolVector<Vector2> uv2 = arrays[Mesh::ARRAY_TEX_UV2];
+ PoolVector<int> indices = arrays[Mesh::ARRAY_INDEX];
+
+ ERR_FAIL_COND_V(vertices.size() == 0, ERR_INVALID_PARAMETER);
+ ERR_FAIL_COND_V(normals.size() == 0, ERR_INVALID_PARAMETER);
+ ERR_FAIL_COND_V(uv2.size() == 0, ERR_INVALID_PARAMETER);
+
+ int vc = vertices.size();
+ PoolVector<Vector3>::Read vr = vertices.read();
+ PoolVector<Vector3>::Read nr = normals.read();
+ PoolVector<Vector2>::Read u2r = uv2.read();
+ PoolVector<int>::Read ir;
+ int ic = 0;
+
+ if (indices.size()) {
+ ic = indices.size();
+ ir = indices.read();
+ }
+
+ int faces = ic ? ic / 3 : vc / 3;
+ for (int i = 0; i < faces; i++) {
+ Vector3 vertex[3];
+ Vector3 normal[3];
+ Vector2 uv[3];
+ for (int j = 0; j < 3; j++) {
+ int idx = ic ? ir[i * 3 + j] : i * 3 + j;
+ vertex[j] = xform.xform(vr[idx]);
+ normal[j] = xform.basis.xform(nr[idx]).normalized();
+ uv[j] = u2r[idx];
+ }
+
+ _plot_triangle(uv, vertex, normal, lightmap.ptrw(), width, height);
+ }
+ }
+ //step 3 perform voxel cone trace on lightmap pixels
+
+ {
+ LightMap *lightmap_ptr = lightmap.ptrw();
+ uint64_t begin_time = OS::get_singleton()->get_ticks_usec();
+ volatile int lines = 0;
+
+ for (int i = 0; i < height; i++) {
+
+ //print_line("bake line " + itos(i) + " / " + itos(height));
+#ifdef _OPENMP
+#pragma omp parallel for
+#endif
+ for (int j = 0; j < width; j++) {
+
+ //if (i == 125 && j == 280) {
+
+ LightMap *pixel = &lightmap_ptr[i * width + j];
+ if (pixel->pos == Vector3())
+ continue; //unused, skipe
+
+ //print_line("pos: " + pixel->pos + " normal " + pixel->normal);
+ switch (bake_mode) {
+ case BAKE_MODE_CONE_TRACE: {
+ pixel->light = _compute_pixel_light_at_pos(pixel->pos, pixel->normal) * energy;
+ } break;
+ case BAKE_MODE_RAY_TRACE: {
+ pixel->light = _compute_ray_trace_at_pos(pixel->pos, pixel->normal) * energy;
+ } break;
+ // pixel->light = Vector3(1, 1, 1);
+ //}
+ }
+ }
+
+ lines = MAX(lines, i); //for multithread
+ if (p_bake_time_func) {
+ uint64_t elapsed = OS::get_singleton()->get_ticks_usec() - begin_time;
+ float elapsed_sec = double(elapsed) / 1000000.0;
+ float remaining = lines < 1 ? 0 : (elapsed_sec / lines) * (height - lines - 1);
+ if (p_bake_time_func(p_bake_time_ud, remaining, lines / float(height))) {
+ return ERR_SKIP;
+ }
+ }
+ }
+
+ if (bake_mode == BAKE_MODE_RAY_TRACE) {
+ //blur
+ print_line("bluring, use pos for separatable copy");
+ //gauss kernel, 7 step sigma 2
+ static const float gauss_kernel[4] = { 0.214607, 0.189879, 0.131514, 0.071303 };
+ //horizontal pass
+ for (int i = 0; i < height; i++) {
+ for (int j = 0; j < width; j++) {
+ if (lightmap_ptr[i * width + j].normal == Vector3()) {
+ continue; //empty
+ }
+ float gauss_sum = gauss_kernel[0];
+ Vector3 accum = lightmap_ptr[i * width + j].light * gauss_kernel[0];
+ for (int k = 1; k < 4; k++) {
+ int new_x = j + k;
+ if (new_x >= width || lightmap_ptr[i * width + new_x].normal == Vector3())
+ break;
+ gauss_sum += gauss_kernel[k];
+ accum += lightmap_ptr[i * width + new_x].light * gauss_kernel[k];
+ }
+ for (int k = 1; k < 4; k++) {
+ int new_x = j - k;
+ if (new_x < 0 || lightmap_ptr[i * width + new_x].normal == Vector3())
+ break;
+ gauss_sum += gauss_kernel[k];
+ accum += lightmap_ptr[i * width + new_x].light * gauss_kernel[k];
+ }
+
+ lightmap_ptr[i * width + j].pos = accum /= gauss_sum;
+ }
+ }
+ //vertical pass
+ for (int i = 0; i < height; i++) {
+ for (int j = 0; j < width; j++) {
+ if (lightmap_ptr[i * width + j].normal == Vector3())
+ continue; //empty, dont write over it anyway
+ float gauss_sum = gauss_kernel[0];
+ Vector3 accum = lightmap_ptr[i * width + j].pos * gauss_kernel[0];
+ for (int k = 1; k < 4; k++) {
+ int new_y = i + k;
+ if (new_y >= height || lightmap_ptr[new_y * width + j].normal == Vector3())
+ break;
+ gauss_sum += gauss_kernel[k];
+ accum += lightmap_ptr[new_y * width + j].pos * gauss_kernel[k];
+ }
+ for (int k = 1; k < 4; k++) {
+ int new_y = i - k;
+ if (new_y < 0 || lightmap_ptr[new_y * width + j].normal == Vector3())
+ break;
+ gauss_sum += gauss_kernel[k];
+ accum += lightmap_ptr[new_y * width + j].pos * gauss_kernel[k];
+ }
+
+ lightmap_ptr[i * width + j].light = accum /= gauss_sum;
+ }
+ }
+ }
+
+ //add directional light (do this after blur)
+ {
+ LightMap *lightmap_ptr = lightmap.ptrw();
+ const Cell *cells = bake_cells.ptr();
+ const Light *light = bake_light.ptr();
+
+ for (int i = 0; i < height; i++) {
+
+ //print_line("bake line " + itos(i) + " / " + itos(height));
+#ifdef _OPENMP
+#pragma omp parallel for
+#endif
+ for (int j = 0; j < width; j++) {
+
+ //if (i == 125 && j == 280) {
+
+ LightMap *pixel = &lightmap_ptr[i * width + j];
+ if (pixel->pos == Vector3())
+ continue; //unused, skipe
+
+ int x = int(pixel->pos.x) - 1;
+ int y = int(pixel->pos.y) - 1;
+ int z = int(pixel->pos.z) - 1;
+ Color accum;
+ int size = 1 << (cell_subdiv - 1);
+
+ int found = 0;
+
+ for (int k = 0; k < 8; k++) {
+
+ int ofs_x = x;
+ int ofs_y = y;
+ int ofs_z = z;
+
+ if (k & 1)
+ ofs_x++;
+ if (k & 2)
+ ofs_y++;
+ if (k & 4)
+ ofs_z++;
+
+ if (x < 0 || x >= size)
+ continue;
+ if (y < 0 || y >= size)
+ continue;
+ if (z < 0 || z >= size)
+ continue;
+
+ uint32_t cell = _find_cell_at_pos(cells, ofs_x, ofs_y, ofs_z);
+
+ if (cell == CHILD_EMPTY)
+ continue;
+ for (int l = 0; l < 6; l++) {
+ float s = pixel->normal.dot(aniso_normal[l]);
+ if (s < 0)
+ s = 0;
+ accum.r += light[cell].direct_accum[l][0] * s;
+ accum.g += light[cell].direct_accum[l][1] * s;
+ accum.b += light[cell].direct_accum[l][2] * s;
+ }
+ found++;
+ }
+ if (found) {
+ accum /= found;
+ pixel->light.x += accum.r;
+ pixel->light.y += accum.g;
+ pixel->light.z += accum.b;
+ }
+ }
+ }
+ }
+
+ {
+ //fill gaps with neighbour vertices to avoid filter fades to black on edges
+
+ for (int i = 0; i < height; i++) {
+ for (int j = 0; j < width; j++) {
+ if (lightmap_ptr[i * width + j].normal != Vector3()) {
+ continue; //filled, skip
+ }
+
+ //this can't be made separatable..
+
+ int closest_i = -1, closest_j = 1;
+ float closest_dist = 1e20;
+
+ const int margin = 3;
+ for (int y = i - margin; y <= i + margin; y++) {
+ for (int x = j - margin; x <= j + margin; x++) {
+
+ if (x == j && y == i)
+ continue;
+ if (x < 0 || x >= width)
+ continue;
+ if (y < 0 || y >= height)
+ continue;
+ if (lightmap_ptr[y * width + x].normal == Vector3())
+ continue; //also ensures that blitted stuff is not reused
+
+ float dist = Vector2(i - y, j - x).length();
+ if (dist > closest_dist)
+ continue;
+
+ closest_dist = dist;
+ closest_i = y;
+ closest_j = x;
+ }
+ }
+
+ if (closest_i != -1) {
+ lightmap_ptr[i * width + j].light = lightmap_ptr[closest_i * width + closest_j].light;
+ }
+ }
+ }
+ }
+
+ {
+ //fill the lightmap data
+ r_lightmap.width = width;
+ r_lightmap.height = height;
+ r_lightmap.light.resize(lightmap.size() * 3);
+ PoolVector<float>::Write w = r_lightmap.light.write();
+ for (int i = 0; i < lightmap.size(); i++) {
+ w[i * 3 + 0] = lightmap[i].light.x;
+ w[i * 3 + 1] = lightmap[i].light.y;
+ w[i * 3 + 2] = lightmap[i].light.z;
+ }
+ }
+
+#if 0
+ {
+ PoolVector<uint8_t> img;
+ int ls = lightmap.size();
+ img.resize(ls * 3);
+ {
+ PoolVector<uint8_t>::Write w = img.write();
+ for (int i = 0; i < ls; i++) {
+ w[i * 3 + 0] = CLAMP(lightmap_ptr[i].light.x * 255, 0, 255);
+ w[i * 3 + 1] = CLAMP(lightmap_ptr[i].light.y * 255, 0, 255);
+ w[i * 3 + 2] = CLAMP(lightmap_ptr[i].light.z * 255, 0, 255);
+ //w[i * 3 + 0] = CLAMP(lightmap_ptr[i].normal.x * 255, 0, 255);
+ //w[i * 3 + 1] = CLAMP(lightmap_ptr[i].normal.y * 255, 0, 255);
+ //w[i * 3 + 2] = CLAMP(lightmap_ptr[i].normal.z * 255, 0, 255);
+ //w[i * 3 + 0] = CLAMP(lightmap_ptr[i].pos.x / (1 << (cell_subdiv - 1)) * 255, 0, 255);
+ //w[i * 3 + 1] = CLAMP(lightmap_ptr[i].pos.y / (1 << (cell_subdiv - 1)) * 255, 0, 255);
+ //w[i * 3 + 2] = CLAMP(lightmap_ptr[i].pos.z / (1 << (cell_subdiv - 1)) * 255, 0, 255);
+ }
+ }
+
+ Ref<Image> image;
+ image.instance();
+ image->create(width, height, false, Image::FORMAT_RGB8, img);
+
+ String name = p_mesh->get_name();
+ if (name == "") {
+ name = "Mesh" + itos(p_mesh->get_instance_id());
+ }
+ image->save_png(name + ".png");
+ }
+#endif
+ }
+
+ return OK;
+}
+
+void VoxelLightBaker::begin_bake(int p_subdiv, const AABB &p_bounds) {
+
+ original_bounds = p_bounds;
+ cell_subdiv = p_subdiv;
+ bake_cells.resize(1);
+ material_cache.clear();
+
+ //find out the actual real bounds, power of 2, which gets the highest subdivision
+ po2_bounds = p_bounds;
+ int longest_axis = po2_bounds.get_longest_axis_index();
+ axis_cell_size[longest_axis] = (1 << (cell_subdiv - 1));
+ leaf_voxel_count = 0;
+
+ for (int i = 0; i < 3; i++) {
+
+ if (i == longest_axis)
+ continue;
+
+ axis_cell_size[i] = axis_cell_size[longest_axis];
+ float axis_size = po2_bounds.size[longest_axis];
+
+ //shrink until fit subdiv
+ while (axis_size / 2.0 >= po2_bounds.size[i]) {
+ axis_size /= 2.0;
+ axis_cell_size[i] >>= 1;
+ }
+
+ po2_bounds.size[i] = po2_bounds.size[longest_axis];
+ }
+
+ Transform to_bounds;
+ to_bounds.basis.scale(Vector3(po2_bounds.size[longest_axis], po2_bounds.size[longest_axis], po2_bounds.size[longest_axis]));
+ to_bounds.origin = po2_bounds.position;
+
+ Transform to_grid;
+ to_grid.basis.scale(Vector3(axis_cell_size[longest_axis], axis_cell_size[longest_axis], axis_cell_size[longest_axis]));
+
+ to_cell_space = to_grid * to_bounds.affine_inverse();
+
+ cell_size = po2_bounds.size[longest_axis] / axis_cell_size[longest_axis];
+}
+
+void VoxelLightBaker::end_bake() {
+ _fixup_plot(0, 0);
+}
+
+//create the data for visual server
+
+PoolVector<int> VoxelLightBaker::create_gi_probe_data() {
+
+ PoolVector<int> data;
+
+ data.resize(16 + (8 + 1 + 1 + 1 + 1) * bake_cells.size()); //4 for header, rest for rest.
+
+ {
+ PoolVector<int>::Write w = data.write();
+
+ uint32_t *w32 = (uint32_t *)w.ptr();
+
+ w32[0] = 0; //version
+ w32[1] = cell_subdiv; //subdiv
+ w32[2] = axis_cell_size[0];
+ w32[3] = axis_cell_size[1];
+ w32[4] = axis_cell_size[2];
+ w32[5] = bake_cells.size();
+ w32[6] = leaf_voxel_count;
+
+ int ofs = 16;
+
+ for (int i = 0; i < bake_cells.size(); i++) {
+
+ for (int j = 0; j < 8; j++) {
+ w32[ofs++] = bake_cells[i].childs[j];
+ }
+
+ { //albedo
+ uint32_t rgba = uint32_t(CLAMP(bake_cells[i].albedo[0] * 255.0, 0, 255)) << 16;
+ rgba |= uint32_t(CLAMP(bake_cells[i].albedo[1] * 255.0, 0, 255)) << 8;
+ rgba |= uint32_t(CLAMP(bake_cells[i].albedo[2] * 255.0, 0, 255)) << 0;
+
+ w32[ofs++] = rgba;
+ }
+ { //emission
+
+ Vector3 e(bake_cells[i].emission[0], bake_cells[i].emission[1], bake_cells[i].emission[2]);
+ float l = e.length();
+ if (l > 0) {
+ e.normalize();
+ l = CLAMP(l / 8.0, 0, 1.0);
+ }
+
+ uint32_t em = uint32_t(CLAMP(e[0] * 255, 0, 255)) << 24;
+ em |= uint32_t(CLAMP(e[1] * 255, 0, 255)) << 16;
+ em |= uint32_t(CLAMP(e[2] * 255, 0, 255)) << 8;
+ em |= uint32_t(CLAMP(l * 255, 0, 255));
+
+ w32[ofs++] = em;
+ }
+
+ //w32[ofs++]=bake_cells[i].used_sides;
+ { //normal
+
+ Vector3 n(bake_cells[i].normal[0], bake_cells[i].normal[1], bake_cells[i].normal[2]);
+ n = n * Vector3(0.5, 0.5, 0.5) + Vector3(0.5, 0.5, 0.5);
+ uint32_t norm = 0;
+
+ norm |= uint32_t(CLAMP(n.x * 255.0, 0, 255)) << 16;
+ norm |= uint32_t(CLAMP(n.y * 255.0, 0, 255)) << 8;
+ norm |= uint32_t(CLAMP(n.z * 255.0, 0, 255)) << 0;
+
+ w32[ofs++] = norm;
+ }
+
+ {
+ uint16_t alpha = CLAMP(uint32_t(bake_cells[i].alpha * 65535.0), 0, 65535);
+ uint16_t level = bake_cells[i].level;
+
+ w32[ofs++] = (uint32_t(level) << 16) | uint32_t(alpha);
+ }
+ }
+ }
+
+ return data;
+}
+
+void VoxelLightBaker::_debug_mesh(int p_idx, int p_level, const AABB &p_aabb, Ref<MultiMesh> &p_multimesh, int &idx, DebugMode p_mode) {
+
+ if (p_level == cell_subdiv - 1) {
+
+ Vector3 center = p_aabb.position + p_aabb.size * 0.5;
+ Transform xform;
+ xform.origin = center;
+ xform.basis.scale(p_aabb.size * 0.5);
+ p_multimesh->set_instance_transform(idx, xform);
+ Color col;
+ if (p_mode == DEBUG_ALBEDO) {
+ col = Color(bake_cells[p_idx].albedo[0], bake_cells[p_idx].albedo[1], bake_cells[p_idx].albedo[2]);
+ } else if (p_mode == DEBUG_LIGHT) {
+ for (int i = 0; i < 6; i++) {
+ col.r += bake_light[p_idx].accum[i][0];
+ col.g += bake_light[p_idx].accum[i][1];
+ col.b += bake_light[p_idx].accum[i][2];
+ col.r += bake_light[p_idx].direct_accum[i][0];
+ col.g += bake_light[p_idx].direct_accum[i][1];
+ col.b += bake_light[p_idx].direct_accum[i][2];
+ }
+ }
+ //Color col = Color(bake_cells[p_idx].emission[0], bake_cells[p_idx].emission[1], bake_cells[p_idx].emission[2]);
+ p_multimesh->set_instance_color(idx, col);
+
+ idx++;
+
+ } else {
+
+ for (int i = 0; i < 8; i++) {
+
+ uint32_t child = bake_cells[p_idx].childs[i];
+
+ if (child == CHILD_EMPTY || child >= max_original_cells)
+ continue;
+
+ AABB aabb = p_aabb;
+ aabb.size *= 0.5;
+
+ if (i & 1)
+ aabb.position.x += aabb.size.x;
+ if (i & 2)
+ aabb.position.y += aabb.size.y;
+ if (i & 4)
+ aabb.position.z += aabb.size.z;
+
+ _debug_mesh(bake_cells[p_idx].childs[i], p_level + 1, aabb, p_multimesh, idx, p_mode);
+ }
+ }
+}
+
+Ref<MultiMesh> VoxelLightBaker::create_debug_multimesh(DebugMode p_mode) {
+
+ Ref<MultiMesh> mm;
+
+ ERR_FAIL_COND_V(p_mode == DEBUG_LIGHT && bake_light.size() == 0, mm);
+ mm.instance();
+
+ mm->set_transform_format(MultiMesh::TRANSFORM_3D);
+ mm->set_color_format(MultiMesh::COLOR_8BIT);
+ print_line("leaf voxels: " + itos(leaf_voxel_count));
+ mm->set_instance_count(leaf_voxel_count);
+
+ Ref<ArrayMesh> mesh;
+ mesh.instance();
+
+ {
+ Array arr;
+ arr.resize(Mesh::ARRAY_MAX);
+
+ PoolVector<Vector3> vertices;
+ PoolVector<Color> colors;
+
+ int vtx_idx = 0;
+#define ADD_VTX(m_idx) \
+ ; \
+ vertices.push_back(face_points[m_idx]); \
+ colors.push_back(Color(1, 1, 1, 1)); \
+ vtx_idx++;
+
+ for (int i = 0; i < 6; i++) {
+
+ Vector3 face_points[4];
+
+ for (int j = 0; j < 4; j++) {
+
+ float v[3];
+ v[0] = 1.0;
+ v[1] = 1 - 2 * ((j >> 1) & 1);
+ v[2] = v[1] * (1 - 2 * (j & 1));
+
+ for (int k = 0; k < 3; k++) {
+
+ if (i < 3)
+ face_points[j][(i + k) % 3] = v[k] * (i >= 3 ? -1 : 1);
+ else
+ face_points[3 - j][(i + k) % 3] = v[k] * (i >= 3 ? -1 : 1);
+ }
+ }
+
+ //tri 1
+ ADD_VTX(0);
+ ADD_VTX(1);
+ ADD_VTX(2);
+ //tri 2
+ ADD_VTX(2);
+ ADD_VTX(3);
+ ADD_VTX(0);
+ }
+
+ arr[Mesh::ARRAY_VERTEX] = vertices;
+ arr[Mesh::ARRAY_COLOR] = colors;
+ mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, arr);
+ }
+
+ {
+ Ref<SpatialMaterial> fsm;
+ fsm.instance();
+ fsm->set_flag(SpatialMaterial::FLAG_SRGB_VERTEX_COLOR, true);
+ fsm->set_flag(SpatialMaterial::FLAG_ALBEDO_FROM_VERTEX_COLOR, true);
+ fsm->set_flag(SpatialMaterial::FLAG_UNSHADED, true);
+ fsm->set_albedo(Color(1, 1, 1, 1));
+
+ mesh->surface_set_material(0, fsm);
+ }
+
+ mm->set_mesh(mesh);
+
+ int idx = 0;
+ _debug_mesh(0, 0, po2_bounds, mm, idx, p_mode);
+
+ return mm;
+}
+
+struct VoxelLightBakerOctree {
+
+ enum {
+ CHILD_EMPTY = 0xFFFFFFFF
+ };
+
+ uint16_t light[6][3]; //anisotropic light
+ float alpha;
+ uint32_t children[8];
+};
+
+PoolVector<uint8_t> VoxelLightBaker::create_capture_octree(int p_subdiv) {
+
+ p_subdiv = MIN(p_subdiv, cell_subdiv); // use the smaller one
+
+ Vector<uint32_t> remap;
+ int bc = bake_cells.size();
+ remap.resize(bc);
+ Vector<uint32_t> demap;
+
+ int new_size = 0;
+ for (int i = 0; i < bc; i++) {
+ uint32_t c = CHILD_EMPTY;
+ if (bake_cells[i].level < p_subdiv) {
+ c = new_size;
+ new_size++;
+ demap.push_back(i);
+ }
+ remap[i] = c;
+ }
+
+ Vector<VoxelLightBakerOctree> octree;
+ octree.resize(new_size);
+
+ for (int i = 0; i < new_size; i++) {
+ octree[i].alpha = bake_cells[demap[i]].alpha;
+ for (int j = 0; j < 6; j++) {
+ for (int k = 0; k < 3; k++) {
+ float l = bake_light[demap[i]].accum[j][k]; //add anisotropic light
+ l += bake_cells[demap[i]].emission[k]; //add emission
+ octree[i].light[j][k] = CLAMP(l * 1024, 0, 65535); //give two more bits to octree
+ }
+ }
+
+ for (int j = 0; j < 8; j++) {
+ uint32_t child = bake_cells[demap[i]].childs[j];
+ octree[i].children[j] = child == CHILD_EMPTY ? CHILD_EMPTY : remap[child];
+ }
+ }
+
+ PoolVector<uint8_t> ret;
+ int ret_bytes = octree.size() * sizeof(VoxelLightBakerOctree);
+ ret.resize(ret_bytes);
+ {
+ PoolVector<uint8_t>::Write w = ret.write();
+ copymem(w.ptr(), octree.ptr(), ret_bytes);
+ }
+
+ return ret;
+}
+
+float VoxelLightBaker::get_cell_size() const {
+ return cell_size;
+}
+
+Transform VoxelLightBaker::get_to_cell_space_xform() const {
+ return to_cell_space;
+}
+VoxelLightBaker::VoxelLightBaker() {
+ color_scan_cell_width = 4;
+ bake_texture_size = 128;
+ propagation = 0.85;
+ energy = 1.0;
+}
diff --git a/scene/3d/voxel_light_baker.h b/scene/3d/voxel_light_baker.h
new file mode 100644
index 0000000000..6dee2ee69b
--- /dev/null
+++ b/scene/3d/voxel_light_baker.h
@@ -0,0 +1,148 @@
+#ifndef VOXEL_LIGHT_BAKER_H
+#define VOXEL_LIGHT_BAKER_H
+
+#include "scene/3d/mesh_instance.h"
+#include "scene/resources/multimesh.h"
+
+class VoxelLightBaker {
+public:
+ enum DebugMode {
+ DEBUG_ALBEDO,
+ DEBUG_LIGHT
+ };
+
+ enum BakeQuality {
+ BAKE_QUALITY_LOW,
+ BAKE_QUALITY_MEDIUM,
+ BAKE_QUALITY_HIGH
+ };
+
+ enum BakeMode {
+ BAKE_MODE_CONE_TRACE,
+ BAKE_MODE_RAY_TRACE,
+ };
+
+private:
+ enum {
+ CHILD_EMPTY = 0xFFFFFFFF
+
+ };
+
+ struct Cell {
+
+ uint32_t childs[8];
+ float albedo[3]; //albedo in RGB24
+ float emission[3]; //accumulated light in 16:16 fixed point (needs to be integer for moving lights fast)
+ float normal[3];
+ uint32_t used_sides;
+ float alpha; //used for upsampling
+ int level;
+
+ Cell() {
+ for (int i = 0; i < 8; i++) {
+ childs[i] = CHILD_EMPTY;
+ }
+
+ for (int i = 0; i < 3; i++) {
+ emission[i] = 0;
+ albedo[i] = 0;
+ normal[i] = 0;
+ }
+ alpha = 0;
+ used_sides = 0;
+ level = 0;
+ }
+ };
+
+ Vector<Cell> bake_cells;
+ int cell_subdiv;
+
+ struct Light {
+ int x, y, z;
+ float accum[6][3]; //rgb anisotropic
+ float direct_accum[6][3]; //for direct bake
+ int next_leaf;
+ };
+
+ int first_leaf;
+
+ Vector<Light> bake_light;
+
+ struct MaterialCache {
+ //128x128 textures
+ Vector<Color> albedo;
+ Vector<Color> emission;
+ };
+
+ Map<Ref<Material>, MaterialCache> material_cache;
+ int leaf_voxel_count;
+ bool direct_lights_baked;
+
+ AABB original_bounds;
+ AABB po2_bounds;
+ int axis_cell_size[3];
+
+ Transform to_cell_space;
+
+ int color_scan_cell_width;
+ int bake_texture_size;
+ float cell_size;
+ float propagation;
+ float energy;
+
+ BakeQuality bake_quality;
+ BakeMode bake_mode;
+
+ int max_original_cells;
+
+ void _init_light_plot(int p_idx, int p_level, int p_x, int p_y, int p_z, uint32_t p_parent);
+
+ Vector<Color> _get_bake_texture(Ref<Image> p_image, const Color &p_color_mul, const Color &p_color_add);
+ MaterialCache _get_material_cache(Ref<Material> p_material);
+ void _plot_face(int p_idx, int p_level, int p_x, int p_y, int p_z, const Vector3 *p_vtx, const Vector2 *p_uv, const MaterialCache &p_material, const AABB &p_aabb);
+ void _fixup_plot(int p_idx, int p_level);
+ void _debug_mesh(int p_idx, int p_level, const AABB &p_aabb, Ref<MultiMesh> &p_multimesh, int &idx, DebugMode p_mode);
+ void _check_init_light();
+
+ uint32_t _find_cell_at_pos(const Cell *cells, int x, int y, int z);
+
+ struct LightMap {
+ Vector3 light;
+ Vector3 pos;
+ Vector3 normal;
+ };
+
+ void _plot_triangle(Vector2 *vertices, Vector3 *positions, Vector3 *normals, LightMap *pixels, int width, int height);
+
+ _FORCE_INLINE_ void _sample_baked_octree_filtered_and_anisotropic(const Vector3 &p_posf, const Vector3 &p_direction, float p_level, Vector3 &r_color, float &r_alpha);
+ _FORCE_INLINE_ Vector3 _voxel_cone_trace(const Vector3 &p_pos, const Vector3 &p_normal, float p_aperture);
+ _FORCE_INLINE_ Vector3 _compute_pixel_light_at_pos(const Vector3 &p_pos, const Vector3 &p_normal);
+ _FORCE_INLINE_ Vector3 _compute_ray_trace_at_pos(const Vector3 &p_pos, const Vector3 &p_normal);
+
+public:
+ void begin_bake(int p_subdiv, const AABB &p_bounds);
+ void plot_mesh(const Transform &p_xform, Ref<Mesh> &p_mesh, const Vector<Ref<Material> > &p_materials, const Ref<Material> &p_override_material);
+ void begin_bake_light(BakeQuality p_quality = BAKE_QUALITY_MEDIUM, BakeMode p_bake_mode = BAKE_MODE_CONE_TRACE, float p_propagation = 0.85, float p_energy = 1);
+ void plot_light_directional(const Vector3 &p_direction, const Color &p_color, float p_energy, float p_indirect_energy, bool p_direct);
+ void plot_light_omni(const Vector3 &p_pos, const Color &p_color, float p_energy, float p_indirect_energy, float p_radius, float p_attenutation, bool p_direct);
+ void plot_light_spot(const Vector3 &p_pos, const Vector3 &p_axis, const Color &p_color, float p_energy, float p_indirect_energy, float p_radius, float p_attenutation, float p_spot_angle, float p_spot_attenuation, bool p_direct);
+ void end_bake();
+
+ struct LightMapData {
+ int width;
+ int height;
+ PoolVector<float> light;
+ };
+
+ Error make_lightmap(const Transform &p_xform, Ref<Mesh> &p_mesh, LightMapData &r_lightmap, bool (*p_bake_time_func)(void *, float, float) = NULL, void *p_bake_time_ud = NULL);
+
+ PoolVector<int> create_gi_probe_data();
+ Ref<MultiMesh> create_debug_multimesh(DebugMode p_mode = DEBUG_ALBEDO);
+ PoolVector<uint8_t> create_capture_octree(int p_subdiv);
+
+ float get_cell_size() const;
+ Transform get_to_cell_space_xform() const;
+ VoxelLightBaker();
+};
+
+#endif // VOXEL_LIGHT_BAKER_H
diff --git a/scene/main/node.cpp b/scene/main/node.cpp
index cae368aeca..efc5d269a6 100644
--- a/scene/main/node.cpp
+++ b/scene/main/node.cpp
@@ -177,8 +177,8 @@ void Node::_propagate_ready() {
}
data.blocked--;
if (data.ready_first) {
- notification(NOTIFICATION_READY);
data.ready_first = false;
+ notification(NOTIFICATION_READY);
}
}
diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp
index d6557f508e..9715e1d6a0 100644
--- a/scene/register_scene_types.cpp
+++ b/scene/register_scene_types.cpp
@@ -160,6 +160,7 @@
#include "scene/3d/area.h"
#include "scene/3d/arvr_nodes.h"
#include "scene/3d/audio_stream_player_3d.h"
+#include "scene/3d/baked_lightmap.h"
#include "scene/3d/bone_attachment.h"
#include "scene/3d/camera.h"
#include "scene/3d/collision_polygon.h"
@@ -375,6 +376,8 @@ void register_scene_types() {
ClassDB::register_class<ReflectionProbe>();
ClassDB::register_class<GIProbe>();
ClassDB::register_class<GIProbeData>();
+ ClassDB::register_class<BakedLightmap>();
+ ClassDB::register_class<BakedLightmapData>();
ClassDB::register_class<AnimationTreePlayer>();
ClassDB::register_class<Particles>();
ClassDB::register_class<Position3D>();
diff --git a/scene/resources/material.cpp b/scene/resources/material.cpp
index c8ab7c2a04..326320c60f 100644
--- a/scene/resources/material.cpp
+++ b/scene/resources/material.cpp
@@ -645,7 +645,7 @@ void SpatialMaterial::_update_shader() {
code += "\tvec2 base_uv = UV;\n";
}
- if ((features[FEATURE_DETAIL] && detail_uv == DETAIL_UV_2) || (features[FEATURE_AMBIENT_OCCLUSION] && flags[FLAG_AO_ON_UV2])) {
+ if ((features[FEATURE_DETAIL] && detail_uv == DETAIL_UV_2) || (features[FEATURE_AMBIENT_OCCLUSION] && flags[FLAG_AO_ON_UV2]) || (features[FEATURE_EMISSION] && flags[FLAG_EMISSION_ON_UV2])) {
code += "\tvec2 base_uv2 = UV2;\n";
}
@@ -729,11 +729,20 @@ void SpatialMaterial::_update_shader() {
}
if (features[FEATURE_EMISSION]) {
- if (flags[FLAG_UV1_USE_TRIPLANAR]) {
- code += "\tvec3 emission_tex = triplanar_texture(texture_emission,uv1_power_normal,uv1_triplanar_pos).rgb;\n";
+ if (flags[FLAG_EMISSION_ON_UV2]) {
+ if (flags[FLAG_UV2_USE_TRIPLANAR]) {
+ code += "\tvec3 emission_tex = triplanar_texture(texture_emission,uv2_power_normal,uv2_triplanar_pos).rgb;\n";
+ } else {
+ code += "\tvec3 emission_tex = texture(texture_emission,base_uv2).rgb;\n";
+ }
} else {
- code += "\tvec3 emission_tex = texture(texture_emission,base_uv).rgb;\n";
+ if (flags[FLAG_UV1_USE_TRIPLANAR]) {
+ code += "\tvec3 emission_tex = triplanar_texture(texture_emission,uv1_power_normal,uv1_triplanar_pos).rgb;\n";
+ } else {
+ code += "\tvec3 emission_tex = texture(texture_emission,base_uv).rgb;\n";
+ }
}
+
if (emission_op == EMISSION_OP_ADD) {
code += "\tEMISSION = (emission.rgb+emission_tex)*emission_energy;\n";
} else {
@@ -1892,6 +1901,7 @@ void SpatialMaterial::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::COLOR, "emission", PROPERTY_HINT_COLOR_NO_ALPHA), "set_emission", "get_emission");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "emission_energy", PROPERTY_HINT_RANGE, "0,16,0.01"), "set_emission_energy", "get_emission_energy");
ADD_PROPERTY(PropertyInfo(Variant::INT, "emission_operator", PROPERTY_HINT_ENUM, "Add,Multiply"), "set_emission_operator", "get_emission_operator");
+ ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "emission_on_uv2"), "set_flag", "get_flag", FLAG_EMISSION_ON_UV2);
ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "emission_texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_texture", "get_texture", TEXTURE_EMISSION);
ADD_GROUP("NormalMap", "normal_");
@@ -2034,6 +2044,7 @@ void SpatialMaterial::_bind_methods() {
BIND_ENUM_CONSTANT(FLAG_UV1_USE_TRIPLANAR);
BIND_ENUM_CONSTANT(FLAG_UV2_USE_TRIPLANAR);
BIND_ENUM_CONSTANT(FLAG_AO_ON_UV2);
+ BIND_ENUM_CONSTANT(FLAG_EMISSION_ON_UV2);
BIND_ENUM_CONSTANT(FLAG_USE_ALPHA_SCISSOR);
BIND_ENUM_CONSTANT(FLAG_TRIPLANAR_USE_WORLD);
BIND_ENUM_CONSTANT(FLAG_ALBEDO_TEXTURE_FORCE_SRGB);
diff --git a/scene/resources/material.h b/scene/resources/material.h
index 7cfa38fce4..d5c3ef83e2 100644
--- a/scene/resources/material.h
+++ b/scene/resources/material.h
@@ -184,6 +184,7 @@ public:
FLAG_UV2_USE_TRIPLANAR,
FLAG_TRIPLANAR_USE_WORLD,
FLAG_AO_ON_UV2,
+ FLAG_EMISSION_ON_UV2,
FLAG_USE_ALPHA_SCISSOR,
FLAG_ALBEDO_TEXTURE_FORCE_SRGB,
FLAG_MAX
@@ -234,7 +235,7 @@ private:
uint64_t blend_mode : 2;
uint64_t depth_draw_mode : 2;
uint64_t cull_mode : 2;
- uint64_t flags : 13;
+ uint64_t flags : 14;
uint64_t detail_blend_mode : 2;
uint64_t diffuse_mode : 3;
uint64_t specular_mode : 2;
diff --git a/scene/resources/mesh.cpp b/scene/resources/mesh.cpp
index 0b352efca2..bb33962be6 100644
--- a/scene/resources/mesh.cpp
+++ b/scene/resources/mesh.cpp
@@ -1123,27 +1123,29 @@ Error ArrayMesh::lightmap_unwrap(const Transform &p_base_transform, float p_texe
PoolVector<int> rindices = arrays[Mesh::ARRAY_INDEX];
int ic = rindices.size();
- int index_ofs = indices.size();
if (ic == 0) {
- indices.resize(index_ofs + vc);
- face_materials.resize((index_ofs + vc) / 3);
- for (int j = 0; j < vc; j++) {
- indices[index_ofs + j] = vertex_ofs + j;
- }
+
for (int j = 0; j < vc / 3; j++) {
- face_materials[(index_ofs / 3) + j] = i;
+ if (Face3(r[j * 3 + 0], r[j * 3 + 1], r[j * 3 + 2]).is_degenerate())
+ continue;
+
+ indices.push_back(vertex_ofs + j * 3 + 0);
+ indices.push_back(vertex_ofs + j * 3 + 1);
+ indices.push_back(vertex_ofs + j * 3 + 2);
+ face_materials.push_back(i);
}
} else {
PoolVector<int>::Read ri = rindices.read();
- indices.resize(index_ofs + ic);
- face_materials.resize((index_ofs + ic) / 3);
- for (int j = 0; j < ic; j++) {
- indices[index_ofs + j] = vertex_ofs + ri[j];
- }
+
for (int j = 0; j < ic / 3; j++) {
- face_materials[(index_ofs / 3) + j] = i;
+ if (Face3(r[ri[j * 3 + 0]], r[ri[j * 3 + 1]], r[ri[j * 3 + 2]]).is_degenerate())
+ continue;
+ indices.push_back(vertex_ofs + ri[j * 3 + 0]);
+ indices.push_back(vertex_ofs + ri[j * 3 + 1]);
+ indices.push_back(vertex_ofs + ri[j * 3 + 2]);
+ face_materials.push_back(i);
}
}
diff --git a/servers/visual/rasterizer.h b/servers/visual/rasterizer.h
index df41c3b5ce..c5c225a40a 100644
--- a/servers/visual/rasterizer.h
+++ b/servers/visual/rasterizer.h
@@ -112,6 +112,10 @@ public:
SelfList<InstanceBase> dependency_item;
+ InstanceBase *lightmap_capture;
+ RID lightmap;
+ Vector<Color> lightmap_capture_data; //in a array (12 values) to avoid wasting space if unused. Alpha is unused, but needed to send to shader
+
virtual void base_removed() = 0;
virtual void base_changed() = 0;
virtual void base_material_changed() = 0;
@@ -126,6 +130,7 @@ public:
depth_layer = 0;
layer_mask = 1;
baked_light = false;
+ lightmap_capture = NULL;
}
};
@@ -437,6 +442,32 @@ public:
virtual RID gi_probe_dynamic_data_create(int p_width, int p_height, int p_depth, GIProbeCompression p_compression) = 0;
virtual void gi_probe_dynamic_data_update(RID p_gi_probe_data, int p_depth_slice, int p_slice_count, int p_mipmap, const void *p_data) = 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<uint8_t> &p_octree) = 0;
+ virtual PoolVector<uint8_t> 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 const PoolVector<LightmapCaptureOctree> *lightmap_capture_get_octree_ptr(RID p_capture) const = 0;
+
/* PARTICLES */
virtual RID particles_create() = 0;
diff --git a/servers/visual/visual_server_raster.h b/servers/visual/visual_server_raster.h
index d843c443a2..a0e79e9d3e 100644
--- a/servers/visual/visual_server_raster.h
+++ b/servers/visual/visual_server_raster.h
@@ -361,6 +361,24 @@ public:
BIND2(gi_probe_set_dynamic_data, RID, const PoolVector<int> &)
BIND1RC(PoolVector<int>, gi_probe_get_dynamic_data, RID)
+ /* 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<uint8_t> &)
+ BIND1RC(PoolVector<uint8_t>, 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)
+
/* PARTICLES */
BIND0R(RID, particles_create)
@@ -504,6 +522,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)
+ BIND3(instance_set_use_lightmap, RID, RID, RID)
BIND2(instance_set_custom_aabb, RID, AABB)
diff --git a/servers/visual/visual_server_scene.cpp b/servers/visual/visual_server_scene.cpp
index dde69eedd3..22be2f6ff9 100644
--- a/servers/visual/visual_server_scene.cpp
+++ b/servers/visual/visual_server_scene.cpp
@@ -133,6 +133,19 @@ void *VisualServerScene::_instance_pair(void *p_self, OctreeElementID, Instance
geom->reflection_dirty = true;
return E; //this element should make freeing faster
+ } else if (B->base_type == VS::INSTANCE_LIGHTMAP_CAPTURE && ((1 << A->base_type) & VS::INSTANCE_GEOMETRY_MASK)) {
+
+ InstanceLightmapCaptureData *lightmap_capture = static_cast<InstanceLightmapCaptureData *>(B->base_data);
+ InstanceGeometryData *geom = static_cast<InstanceGeometryData *>(A->base_data);
+
+ InstanceLightmapCaptureData::PairInfo pinfo;
+ pinfo.geometry = A;
+ pinfo.L = geom->lightmap_captures.push_back(B);
+
+ List<InstanceLightmapCaptureData::PairInfo>::Element *E = lightmap_capture->geometries.push_back(pinfo);
+ ((VisualServerScene *)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 == VS::INSTANCE_GI_PROBE && ((1 << A->base_type) & VS::INSTANCE_GEOMETRY_MASK)) {
InstanceGIProbeData *gi_probe = static_cast<InstanceGIProbeData *>(B->base_data);
@@ -193,6 +206,16 @@ void VisualServerScene::_instance_unpair(void *p_self, OctreeElementID, Instance
reflection_probe->geometries.erase(E);
geom->reflection_dirty = true;
+ } else if (B->base_type == VS::INSTANCE_LIGHTMAP_CAPTURE && ((1 << A->base_type) & VS::INSTANCE_GEOMETRY_MASK)) {
+
+ InstanceLightmapCaptureData *lightmap_capture = static_cast<InstanceLightmapCaptureData *>(B->base_data);
+ InstanceGeometryData *geom = static_cast<InstanceGeometryData *>(A->base_data);
+
+ List<InstanceLightmapCaptureData::PairInfo>::Element *E = reinterpret_cast<List<InstanceLightmapCaptureData::PairInfo>::Element *>(udata);
+
+ geom->lightmap_captures.erase(E->get().L);
+ lightmap_capture->geometries.erase(E);
+ ((VisualServerScene *)p_self)->_instance_queue_update(A, false, false); //need to update capture
} else if (B->base_type == VS::INSTANCE_GI_PROBE && ((1 << A->base_type) & VS::INSTANCE_GEOMETRY_MASK)) {
@@ -344,6 +367,14 @@ void VisualServerScene::instance_set_base(RID p_instance, RID p_base) {
reflection_probe_render_list.remove(&reflection_probe->update_list);
}
} break;
+ case VS::INSTANCE_LIGHTMAP_CAPTURE: {
+
+ InstanceLightmapCaptureData *lightmap_capture = static_cast<InstanceLightmapCaptureData *>(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());
+ }
+ } break;
case VS::INSTANCE_GI_PROBE: {
InstanceGIProbeData *gi_probe = static_cast<InstanceGIProbeData *>(instance->base_data);
@@ -355,6 +386,14 @@ void VisualServerScene::instance_set_base(RID p_instance, RID p_base) {
VSG::storage->free(gi_probe->dynamic.probe_data);
}
+ if (instance->lightmap_capture) {
+ Instance *capture = (Instance *)instance->lightmap_capture;
+ InstanceLightmapCaptureData *lightmap_capture = static_cast<InstanceLightmapCaptureData *>(capture->base_data);
+ lightmap_capture->users.erase(instance);
+ instance->lightmap_capture = NULL;
+ instance->lightmap = RID();
+ }
+
VSG::scene_render->free(gi_probe->probe_instance);
} break;
@@ -412,6 +451,12 @@ void VisualServerScene::instance_set_base(RID p_instance, RID p_base) {
reflection_probe->instance = VSG::scene_render->reflection_probe_instance_create(p_base);
} break;
+ case VS::INSTANCE_LIGHTMAP_CAPTURE: {
+
+ InstanceLightmapCaptureData *lightmap_capture = memnew(InstanceLightmapCaptureData);
+ instance->base_data = lightmap_capture;
+ //lightmap_capture->instance = VSG::scene_render->lightmap_capture_instance_create(p_base);
+ } break;
case VS::INSTANCE_GI_PROBE: {
InstanceGIProbeData *gi_probe = memnew(InstanceGIProbeData);
@@ -591,6 +636,12 @@ void VisualServerScene::instance_set_visible(RID p_instance, bool p_visible) {
}
} break;
+ case VS::INSTANCE_LIGHTMAP_CAPTURE: {
+ if (instance->octree_id && instance->scenario) {
+ instance->scenario->octree.set_pairable(instance->octree_id, p_visible, 1 << VS::INSTANCE_LIGHTMAP_CAPTURE, p_visible ? VS::INSTANCE_GEOMETRY_MASK : 0);
+ }
+
+ } break;
case VS::INSTANCE_GI_PROBE: {
if (instance->octree_id && instance->scenario) {
instance->scenario->octree.set_pairable(instance->octree_id, p_visible, 1 << VS::INSTANCE_GI_PROBE, p_visible ? (VS::INSTANCE_GEOMETRY_MASK | (1 << VS::INSTANCE_LIGHT)) : 0);
@@ -599,11 +650,35 @@ void VisualServerScene::instance_set_visible(RID p_instance, bool p_visible) {
} break;
}
}
-
inline bool is_geometry_instance(VisualServer::InstanceType p_type) {
return p_type == VS::INSTANCE_MESH || p_type == VS::INSTANCE_MULTIMESH || p_type == VS::INSTANCE_PARTICLES || p_type == VS::INSTANCE_IMMEDIATE;
}
+void VisualServerScene::instance_set_use_lightmap(RID p_instance, RID p_lightmap_instance, RID p_lightmap) {
+
+ Instance *instance = instance_owner.get(p_instance);
+ ERR_FAIL_COND(!instance);
+ ERR_FAIL_COND(!is_geometry_instance(instance->base_type));
+
+ if (instance->lightmap_capture) {
+ InstanceLightmapCaptureData *lightmap_capture = static_cast<InstanceLightmapCaptureData *>(((Instance *)instance->lightmap_capture)->base_data);
+ lightmap_capture->users.erase(instance);
+ instance->lightmap = RID();
+ instance->lightmap_capture = NULL;
+ }
+
+ 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 != VS::INSTANCE_LIGHTMAP_CAPTURE);
+ instance->lightmap_capture = lightmap_instance;
+
+ InstanceLightmapCaptureData *lightmap_capture = static_cast<InstanceLightmapCaptureData *>(((Instance *)instance->lightmap_capture)->base_data);
+ lightmap_capture->users.insert(instance);
+ instance->lightmap = p_lightmap;
+ }
+}
+
void VisualServerScene::instance_set_custom_aabb(RID p_instance, AABB p_aabb) {
Instance *instance = instance_owner.get(p_instance);
@@ -811,6 +886,15 @@ void VisualServerScene::_update_instance(Instance *p_instance) {
light->shadow_dirty = true;
}
}
+
+ 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 = p_instance->transform.basis.determinant() < 0.0;
@@ -832,7 +916,7 @@ void VisualServerScene::_update_instance(Instance *p_instance) {
uint32_t pairable_mask = 0;
bool pairable = false;
- if (p_instance->base_type == VS::INSTANCE_LIGHT || p_instance->base_type == VS::INSTANCE_REFLECTION_PROBE) {
+ if (p_instance->base_type == VS::INSTANCE_LIGHT || p_instance->base_type == VS::INSTANCE_REFLECTION_PROBE || p_instance->base_type == VS::INSTANCE_LIGHTMAP_CAPTURE) {
pairable_mask = p_instance->visible ? VS::INSTANCE_GEOMETRY_MASK : 0;
pairable = true;
@@ -917,6 +1001,11 @@ void VisualServerScene::_update_instance_aabb(Instance *p_instance) {
new_aabb = VSG::storage->gi_probe_get_bounds(p_instance->base);
} break;
+ case VisualServer::INSTANCE_LIGHTMAP_CAPTURE: {
+
+ new_aabb = VSG::storage->lightmap_capture_get_bounds(p_instance->base);
+
+ } break;
default: {}
}
@@ -928,6 +1017,237 @@ void VisualServerScene::_update_instance_aabb(Instance *p_instance) {
p_instance->aabb = new_aabb;
}
+_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];
+ zeromem(alpha, 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] = 0;
+ } else {
+ 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)
+ amount = 0;
+ color[c][n].x += p_octree[cell].light[i][0] / 1024.0 * amount;
+ color[c][n].y += p_octree[cell].light[i][1] / 1024.0 * amount;
+ color[c][n].z += p_octree[cell].light[i][2] / 1024.0 * 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];
+
+ pos_fract[0].x = Math::fmod(pos.x, target_level_size) / target_level_size;
+ pos_fract[0].y = Math::fmod(pos.y, target_level_size) / target_level_size;
+ pos_fract[0].z = Math::fmod(pos.z, target_level_size) / target_level_size;
+
+ target_level_size = size >> MAX(0, target_level - 1);
+
+ pos_fract[1].x = Math::fmod(pos.x, target_level_size) / target_level_size;
+ pos_fract[1].y = Math::fmod(pos.y, target_level_size) / target_level_size;
+ pos_fract[1].z = Math::fmod(pos.z, target_level_size) / target_level_size;
+
+ 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 VisualServerScene::_update_instance_lightmap_captures(Instance *p_instance) {
+
+ InstanceGeometryData *geom = static_cast<InstanceGeometryData *>(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);
+
+ zeromem(p_instance->lightmap_capture_data.ptrw(), 12 * sizeof(Color));
+ //this could use some sort of blending..
+ for (List<Instance *>::Element *E = geom->lightmap_captures.front(); E; E = E->next()) {
+ const PoolVector<RasterizerStorage::LightmapCaptureOctree> *octree = VSG::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 = VSG::storage->lightmap_capture_get_octree_cell_transform(E->get()->base);
+ int cell_subdiv = VSG::storage->lightmap_capture_get_octree_cell_subdiv(E->get()->base);
+ to_cell_xform = to_cell_xform * E->get()->transform.affine_inverse();
+
+ PoolVector<RasterizerStorage::LightmapCaptureOctree>::Read octree_r = octree->read();
+
+ Vector3 pos = to_cell_xform.xform(p_instance->transform.origin);
+
+ 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);
+ p_instance->lightmap_capture_data[i] += capture;
+ }
+ }
+}
+
void VisualServerScene::_light_instance_update_shadow(Instance *p_instance, const Transform p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_orthogonal, RID p_shadow_atlas, Scenario *p_scenario) {
InstanceLightData *light = static_cast<InstanceLightData *>(p_instance->base_data);
@@ -2188,6 +2508,8 @@ void VisualServerScene::_bake_gi_probe_light(const GIProbeDataHeader *header, co
InstanceGIProbeData::LocalData *light = &local_data[idx];
Vector3 to(light->pos[0] + 0.5, light->pos[1] + 0.5, light->pos[2] + 0.5);
+ to += -light_axis.sign() * 0.47; //make it more likely to receive a ray
+
Vector3 norm(
(((cells[idx].normal >> 16) & 0xFF) / 255.0) * 2.0 - 1.0,
(((cells[idx].normal >> 8) & 0xFF) / 255.0) * 2.0 - 1.0,
@@ -2254,6 +2576,8 @@ void VisualServerScene::_bake_gi_probe_light(const GIProbeDataHeader *header, co
InstanceGIProbeData::LocalData *light = &local_data[idx];
Vector3 to(light->pos[0] + 0.5, light->pos[1] + 0.5, light->pos[2] + 0.5);
+ to += (light_pos - to).sign() * 0.47; //make it more likely to receive a ray
+
Vector3 norm(
(((cells[idx].normal >> 16) & 0xFF) / 255.0) * 2.0 - 1.0,
(((cells[idx].normal >> 8) & 0xFF) / 255.0) * 2.0 - 1.0,
@@ -2927,12 +3251,12 @@ void VisualServerScene::_update_dirty_instance(Instance *p_instance) {
}
}
+ _instance_update_list.remove(&p_instance->update_item);
+
_update_instance(p_instance);
p_instance->update_aabb = false;
p_instance->update_materials = false;
-
- _instance_update_list.remove(&p_instance->update_item);
}
void VisualServerScene::update_dirty_instances() {
diff --git a/servers/visual/visual_server_scene.h b/servers/visual/visual_server_scene.h
index 9e4701de65..4b0c4af09d 100644
--- a/servers/visual/visual_server_scene.h
+++ b/servers/visual/visual_server_scene.h
@@ -281,6 +281,8 @@ public:
List<Instance *> gi_probes;
bool gi_probes_dirty;
+ List<Instance *> lightmap_captures;
+
InstanceGeometryData() {
lighting_dirty = false;
@@ -445,6 +447,20 @@ public:
SelfList<InstanceGIProbeData>::List gi_probe_update_list;
+ struct InstanceLightmapCaptureData : public InstanceBaseData {
+
+ struct PairInfo {
+ List<Instance *>::Element *L; //iterator in geometry
+ Instance *geometry;
+ };
+ List<PairInfo> geometries;
+
+ Set<Instance *> users;
+
+ InstanceLightmapCaptureData() {
+ }
+ };
+
Instance *instance_cull_result[MAX_INSTANCE_CULL];
Instance *instance_shadow_cull_result[MAX_INSTANCE_CULL]; //used for generating shadowmaps
Instance *light_cull_result[MAX_LIGHTS_CULLED];
@@ -466,6 +482,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);
virtual void instance_set_custom_aabb(RID p_insatnce, AABB aabb);
@@ -489,6 +506,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_ void _light_instance_update_shadow(Instance *p_instance, const Transform p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_orthogonal, RID p_shadow_atlas, Scenario *p_scenario);
diff --git a/servers/visual/visual_server_wrap_mt.h b/servers/visual/visual_server_wrap_mt.h
index 94f450c024..cb6f67474e 100644
--- a/servers/visual/visual_server_wrap_mt.h
+++ b/servers/visual/visual_server_wrap_mt.h
@@ -294,6 +294,22 @@ public:
FUNC2(gi_probe_set_dynamic_data, RID, const PoolVector<int> &)
FUNC1RC(PoolVector<int>, gi_probe_get_dynamic_data, RID)
+ /* 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<uint8_t> &)
+ FUNC1RC(PoolVector<uint8_t>, 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)
+
/* PARTICLES */
FUNCRID(particles)
@@ -425,6 +441,8 @@ public:
FUNC3(instance_set_blend_shape_weight, RID, int, float)
FUNC3(instance_set_surface_material, RID, int, RID)
FUNC2(instance_set_visible, RID, bool)
+ FUNC3(instance_set_use_lightmap, RID, RID, RID)
+
FUNC2(instance_set_custom_aabb, RID, AABB)
FUNC2(instance_attach_skeleton, RID, RID)
diff --git a/servers/visual_server.h b/servers/visual_server.h
index de5ef7da0a..ad4d32b967 100644
--- a/servers/visual_server.h
+++ b/servers/visual_server.h
@@ -485,6 +485,20 @@ public:
virtual void gi_probe_set_compress(RID p_probe, bool p_enable) = 0;
virtual bool gi_probe_is_compressed(RID p_probe) const = 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<uint8_t> &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<uint8_t> 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;
+
/* PARTICLES API */
virtual RID particles_create() = 0;
@@ -735,6 +749,7 @@ public:
INSTANCE_LIGHT,
INSTANCE_REFLECTION_PROBE,
INSTANCE_GI_PROBE,
+ INSTANCE_LIGHTMAP_CAPTURE,
INSTANCE_MAX,
/*INSTANCE_BAKED_LIGHT_SAMPLER,*/
@@ -755,6 +770,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) = 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;