diff --git a/core/math/math_funcs.h b/core/math/math_funcs.h
index 267f6a4fe23..8cf13efdb61 100644
--- a/core/math/math_funcs.h
+++ b/core/math/math_funcs.h
@@ -103,6 +103,9 @@ public:
static _ALWAYS_INLINE_ double log(double p_x) { return ::log(p_x); }
static _ALWAYS_INLINE_ float log(float p_x) { return ::logf(p_x); }
+ static _ALWAYS_INLINE_ double log2(double p_x) { return ::log2(p_x); }
+ static _ALWAYS_INLINE_ float log2(float p_x) { return ::log2f(p_x); }
+
static _ALWAYS_INLINE_ double exp(double p_x) { return ::exp(p_x); }
static _ALWAYS_INLINE_ float exp(float p_x) { return ::expf(p_x); }
diff --git a/doc/classes/GeometryInstance3D.xml b/doc/classes/GeometryInstance3D.xml
index 631a30ababb..b2c3bfc3ed0 100644
--- a/doc/classes/GeometryInstance3D.xml
+++ b/doc/classes/GeometryInstance3D.xml
@@ -48,6 +48,8 @@
+
+
diff --git a/doc/classes/Occluder3D.xml b/doc/classes/Occluder3D.xml
new file mode 100644
index 00000000000..fc676c2b490
--- /dev/null
+++ b/doc/classes/Occluder3D.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/doc/classes/OccluderInstance3D.xml b/doc/classes/OccluderInstance3D.xml
new file mode 100644
index 00000000000..76b784d21d6
--- /dev/null
+++ b/doc/classes/OccluderInstance3D.xml
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml
index 2bfe7ad48fb..e090e20d9ff 100644
--- a/doc/classes/ProjectSettings.xml
+++ b/doc/classes/ProjectSettings.xml
@@ -1469,6 +1469,12 @@
+
+
+
+
+
+
Number of cubemaps to store in the reflection atlas. The number of [ReflectionProbe]s in a scene will be limited by this amount. A higher number requires more VRAM.
diff --git a/doc/classes/RenderingServer.xml b/doc/classes/RenderingServer.xml
index d6eaa1b88b5..638b0bb2973 100644
--- a/doc/classes/RenderingServer.xml
+++ b/doc/classes/RenderingServer.xml
@@ -1999,6 +1999,24 @@
Sets the number of instances visible at a given time. If -1, all instances that have been allocated are drawn. Equivalent to [member MultiMesh.visible_instance_count].
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -2412,6 +2430,16 @@
The scenario is the 3D world that all the visual instances exist in.
+
+
+
+
+
+
+
+
+
+
@@ -2897,6 +2925,22 @@
Sets the anti-aliasing mode. See [enum ViewportMSAA] for options.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -3002,6 +3046,16 @@
+
+
+
+
+
+
+
+
+
+
@@ -3454,6 +3508,8 @@
+
+
Uses high quality importance sampling to process the radiance map. In general, this results in much higher quality than [constant Sky.PROCESS_MODE_REALTIME] but takes much longer to generate. This should not be used if you plan on changing the sky at runtime. If you are finding that the reflection is not blurry enough and is showing sparkles or fireflies, try increasing [member ProjectSettings.rendering/reflections/sky_reflections/ggx_samples].
@@ -3606,6 +3662,12 @@
Draw all objects without shading. Equivalent to setting all objects shaders to [code]unshaded[/code].
+
+
+
+
+
+
The instance does not have a type.
@@ -3638,7 +3700,9 @@
The instance is a lightmap.
-
+
+
+
Represents the size of the [enum InstanceType] enum.
@@ -3653,7 +3717,9 @@
When set, manually requests to draw geometry on next frame.
-
+
+
+
Represents the size of the [enum InstanceFlags] enum.
diff --git a/doc/classes/Viewport.xml b/doc/classes/Viewport.xml
index 471d21374d0..cce57053798 100644
--- a/doc/classes/Viewport.xml
+++ b/doc/classes/Viewport.xml
@@ -267,6 +267,8 @@
+
+
The custom [World2D] which can be used as 2D environment source.
@@ -419,6 +421,8 @@
+
+
The texture filter reads from the nearest pixel only. The simplest and fastest method of filtering, but the texture will look pixelized.
diff --git a/drivers/dummy/rasterizer_dummy.h b/drivers/dummy/rasterizer_dummy.h
index 9d6be1a802f..f9a76d16033 100644
--- a/drivers/dummy/rasterizer_dummy.h
+++ b/drivers/dummy/rasterizer_dummy.h
@@ -548,6 +548,12 @@ public:
void lightmap_set_probe_capture_update_speed(float p_speed) override {}
float lightmap_get_probe_capture_update_speed() const override { return 0; }
+ /* OCCLUDER */
+
+ RID occluder_allocate() override { return RID(); }
+ void occluder_initialize(RID p_rid) override {}
+ void occluder_set_mesh(RID p_occluder, const PackedVector3Array &p_vertices, const PackedInt32Array &p_indices) {}
+
/* PARTICLES */
RID particles_allocate() override { return RID(); }
diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp
index 055baeb81e4..61376175649 100644
--- a/editor/editor_node.cpp
+++ b/editor/editor_node.cpp
@@ -143,6 +143,7 @@
#include "editor/plugins/multimesh_editor_plugin.h"
#include "editor/plugins/navigation_polygon_editor_plugin.h"
#include "editor/plugins/node_3d_editor_plugin.h"
+#include "editor/plugins/occluder_instance_3d_editor_plugin.h"
#include "editor/plugins/ot_features_plugin.h"
#include "editor/plugins/packed_scene_translation_parser_plugin.h"
#include "editor/plugins/path_2d_editor_plugin.h"
@@ -6800,6 +6801,7 @@ EditorNode::EditorNode() {
add_editor_plugin(memnew(TextureRegionEditorPlugin(this)));
add_editor_plugin(memnew(GIProbeEditorPlugin(this)));
add_editor_plugin(memnew(BakedLightmapEditorPlugin(this)));
+ add_editor_plugin(memnew(OccluderInstance3DEditorPlugin(this)));
add_editor_plugin(memnew(Path2DEditorPlugin(this)));
add_editor_plugin(memnew(Path3DEditorPlugin(this)));
add_editor_plugin(memnew(Line2DEditorPlugin(this)));
diff --git a/editor/node_3d_editor_gizmos.cpp b/editor/node_3d_editor_gizmos.cpp
index 7dcabafecea..afafd7d195c 100644
--- a/editor/node_3d_editor_gizmos.cpp
+++ b/editor/node_3d_editor_gizmos.cpp
@@ -47,6 +47,7 @@
#include "scene/3d/listener_3d.h"
#include "scene/3d/mesh_instance_3d.h"
#include "scene/3d/navigation_region_3d.h"
+#include "scene/3d/occluder_instance_3d.h"
#include "scene/3d/physics_joint_3d.h"
#include "scene/3d/position_3d.h"
#include "scene/3d/ray_cast_3d.h"
@@ -176,6 +177,7 @@ void EditorNode3DGizmo::Instance::create_instance(Node3D *p_base, bool p_hidden)
RS::get_singleton()->instance_geometry_set_cast_shadows_setting(instance, RS::SHADOW_CASTING_SETTING_OFF);
int layer = p_hidden ? 0 : 1 << Node3DEditorViewport::GIZMO_EDIT_LAYER;
RS::get_singleton()->instance_set_layer_mask(instance, layer); //gizmos are 26
+ RS::get_singleton()->instance_geometry_set_flag(instance, RS::INSTANCE_FLAG_IGNORE_OCCLUSION_CULLING, true);
}
void EditorNode3DGizmo::add_mesh(const Ref &p_mesh, bool p_billboard, const Ref &p_skin_reference, const Ref &p_material) {
@@ -1464,6 +1466,44 @@ void MeshInstance3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
}
/////
+
+OccluderInstance3DGizmoPlugin::OccluderInstance3DGizmoPlugin() {
+ create_material("line_material", EDITOR_DEF("editors/3d_gizmos/gizmo_colors/occluder", Color(0.8, 0.5, 1)));
+}
+
+bool OccluderInstance3DGizmoPlugin::has_gizmo(Node3D *p_spatial) {
+ return Object::cast_to(p_spatial) != nullptr;
+}
+
+String OccluderInstance3DGizmoPlugin::get_gizmo_name() const {
+ return "OccluderInstance3D";
+}
+
+int OccluderInstance3DGizmoPlugin::get_priority() const {
+ return -1;
+}
+
+void OccluderInstance3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
+ OccluderInstance3D *occluder_instance = Object::cast_to(p_gizmo->get_spatial_node());
+
+ p_gizmo->clear();
+
+ Ref o = occluder_instance->get_occluder();
+
+ if (!o.is_valid()) {
+ return;
+ }
+
+ Vector lines = o->get_debug_lines();
+ if (!lines.is_empty()) {
+ Ref material = get_material("line_material", p_gizmo);
+ p_gizmo->add_lines(lines, material);
+ p_gizmo->add_collision_segments(lines);
+ }
+}
+
+/////
+
Sprite3DGizmoPlugin::Sprite3DGizmoPlugin() {
}
diff --git a/editor/node_3d_editor_gizmos.h b/editor/node_3d_editor_gizmos.h
index 6f98d3a08ca..95344176ade 100644
--- a/editor/node_3d_editor_gizmos.h
+++ b/editor/node_3d_editor_gizmos.h
@@ -100,6 +100,18 @@ public:
MeshInstance3DGizmoPlugin();
};
+class OccluderInstance3DGizmoPlugin : public EditorNode3DGizmoPlugin {
+ GDCLASS(OccluderInstance3DGizmoPlugin, EditorNode3DGizmoPlugin);
+
+public:
+ bool has_gizmo(Node3D *p_spatial) override;
+ String get_gizmo_name() const override;
+ int get_priority() const override;
+ void redraw(EditorNode3DGizmo *p_gizmo) override;
+
+ OccluderInstance3DGizmoPlugin();
+};
+
class Sprite3DGizmoPlugin : public EditorNode3DGizmoPlugin {
GDCLASS(Sprite3DGizmoPlugin, EditorNode3DGizmoPlugin);
diff --git a/editor/plugins/node_3d_editor_plugin.cpp b/editor/plugins/node_3d_editor_plugin.cpp
index 13c7814dacb..023d91be303 100644
--- a/editor/plugins/node_3d_editor_plugin.cpp
+++ b/editor/plugins/node_3d_editor_plugin.cpp
@@ -2368,6 +2368,9 @@ void Node3DEditorViewport::_project_settings_changed() {
viewport->set_screen_space_aa(Viewport::ScreenSpaceAA(ssaa_mode));
const bool use_debanding = GLOBAL_GET("rendering/anti_aliasing/quality/use_debanding");
viewport->set_use_debanding(use_debanding);
+
+ const bool use_occlusion_culling = GLOBAL_GET("rendering/occlusion_culling/use_occlusion_culling");
+ viewport->set_use_occlusion_culling(use_occlusion_culling);
}
void Node3DEditorViewport::_notification(int p_what) {
@@ -3071,7 +3074,8 @@ void Node3DEditorViewport::_menu_option(int p_option) {
case VIEW_DISPLAY_DEBUG_CLUSTER_OMNI_LIGHTS:
case VIEW_DISPLAY_DEBUG_CLUSTER_SPOT_LIGHTS:
case VIEW_DISPLAY_DEBUG_CLUSTER_DECALS:
- case VIEW_DISPLAY_DEBUG_CLUSTER_REFLECTION_PROBES: {
+ case VIEW_DISPLAY_DEBUG_CLUSTER_REFLECTION_PROBES:
+ case VIEW_DISPLAY_DEBUG_OCCLUDERS: {
static const int display_options[] = {
VIEW_DISPLAY_NORMAL,
VIEW_DISPLAY_WIREFRAME,
@@ -3097,6 +3101,7 @@ void Node3DEditorViewport::_menu_option(int p_option) {
VIEW_DISPLAY_DEBUG_CLUSTER_SPOT_LIGHTS,
VIEW_DISPLAY_DEBUG_CLUSTER_DECALS,
VIEW_DISPLAY_DEBUG_CLUSTER_REFLECTION_PROBES,
+ VIEW_DISPLAY_DEBUG_OCCLUDERS,
VIEW_MAX
};
static const Viewport::DebugDraw debug_draw_modes[] = {
@@ -3124,6 +3129,7 @@ void Node3DEditorViewport::_menu_option(int p_option) {
Viewport::DEBUG_DRAW_CLUSTER_SPOT_LIGHTS,
Viewport::DEBUG_DRAW_CLUSTER_DECALS,
Viewport::DEBUG_DRAW_CLUSTER_REFLECTION_PROBES,
+ Viewport::DEBUG_DRAW_OCCLUDERS,
};
int idx = 0;
@@ -3173,6 +3179,7 @@ void Node3DEditorViewport::_init_gizmo_instance(int p_idx) {
RS::get_singleton()->instance_set_visible(move_gizmo_instance[i], false);
RS::get_singleton()->instance_geometry_set_cast_shadows_setting(move_gizmo_instance[i], RS::SHADOW_CASTING_SETTING_OFF);
RS::get_singleton()->instance_set_layer_mask(move_gizmo_instance[i], layer);
+ RS::get_singleton()->instance_geometry_set_flag(move_gizmo_instance[i], RS::INSTANCE_FLAG_IGNORE_OCCLUSION_CULLING, true);
move_plane_gizmo_instance[i] = RS::get_singleton()->instance_create();
RS::get_singleton()->instance_set_base(move_plane_gizmo_instance[i], spatial_editor->get_move_plane_gizmo(i)->get_rid());
@@ -3180,6 +3187,7 @@ void Node3DEditorViewport::_init_gizmo_instance(int p_idx) {
RS::get_singleton()->instance_set_visible(move_plane_gizmo_instance[i], false);
RS::get_singleton()->instance_geometry_set_cast_shadows_setting(move_plane_gizmo_instance[i], RS::SHADOW_CASTING_SETTING_OFF);
RS::get_singleton()->instance_set_layer_mask(move_plane_gizmo_instance[i], layer);
+ RS::get_singleton()->instance_geometry_set_flag(move_plane_gizmo_instance[i], RS::INSTANCE_FLAG_IGNORE_OCCLUSION_CULLING, true);
rotate_gizmo_instance[i] = RS::get_singleton()->instance_create();
RS::get_singleton()->instance_set_base(rotate_gizmo_instance[i], spatial_editor->get_rotate_gizmo(i)->get_rid());
@@ -3187,6 +3195,7 @@ void Node3DEditorViewport::_init_gizmo_instance(int p_idx) {
RS::get_singleton()->instance_set_visible(rotate_gizmo_instance[i], false);
RS::get_singleton()->instance_geometry_set_cast_shadows_setting(rotate_gizmo_instance[i], RS::SHADOW_CASTING_SETTING_OFF);
RS::get_singleton()->instance_set_layer_mask(rotate_gizmo_instance[i], layer);
+ RS::get_singleton()->instance_geometry_set_flag(rotate_gizmo_instance[i], RS::INSTANCE_FLAG_IGNORE_OCCLUSION_CULLING, true);
scale_gizmo_instance[i] = RS::get_singleton()->instance_create();
RS::get_singleton()->instance_set_base(scale_gizmo_instance[i], spatial_editor->get_scale_gizmo(i)->get_rid());
@@ -3194,6 +3203,7 @@ void Node3DEditorViewport::_init_gizmo_instance(int p_idx) {
RS::get_singleton()->instance_set_visible(scale_gizmo_instance[i], false);
RS::get_singleton()->instance_geometry_set_cast_shadows_setting(scale_gizmo_instance[i], RS::SHADOW_CASTING_SETTING_OFF);
RS::get_singleton()->instance_set_layer_mask(scale_gizmo_instance[i], layer);
+ RS::get_singleton()->instance_geometry_set_flag(scale_gizmo_instance[i], RS::INSTANCE_FLAG_IGNORE_OCCLUSION_CULLING, true);
scale_plane_gizmo_instance[i] = RS::get_singleton()->instance_create();
RS::get_singleton()->instance_set_base(scale_plane_gizmo_instance[i], spatial_editor->get_scale_plane_gizmo(i)->get_rid());
@@ -3201,6 +3211,7 @@ void Node3DEditorViewport::_init_gizmo_instance(int p_idx) {
RS::get_singleton()->instance_set_visible(scale_plane_gizmo_instance[i], false);
RS::get_singleton()->instance_geometry_set_cast_shadows_setting(scale_plane_gizmo_instance[i], RS::SHADOW_CASTING_SETTING_OFF);
RS::get_singleton()->instance_set_layer_mask(scale_plane_gizmo_instance[i], layer);
+ RS::get_singleton()->instance_geometry_set_flag(scale_plane_gizmo_instance[i], RS::INSTANCE_FLAG_IGNORE_OCCLUSION_CULLING, true);
}
// Rotation white outline
@@ -3210,6 +3221,7 @@ void Node3DEditorViewport::_init_gizmo_instance(int p_idx) {
RS::get_singleton()->instance_set_visible(rotate_gizmo_instance[3], false);
RS::get_singleton()->instance_geometry_set_cast_shadows_setting(rotate_gizmo_instance[3], RS::SHADOW_CASTING_SETTING_OFF);
RS::get_singleton()->instance_set_layer_mask(rotate_gizmo_instance[3], layer);
+ RS::get_singleton()->instance_geometry_set_flag(rotate_gizmo_instance[3], RS::INSTANCE_FLAG_IGNORE_OCCLUSION_CULLING, true);
}
void Node3DEditorViewport::_finish_gizmo_instances() {
@@ -4043,6 +4055,7 @@ Node3DEditorViewport::Node3DEditorViewport(Node3DEditor *p_spatial_editor, Edito
display_submenu->add_radio_check_item(TTR("Spot Light Cluster"), VIEW_DISPLAY_DEBUG_CLUSTER_SPOT_LIGHTS);
display_submenu->add_radio_check_item(TTR("Decal Cluster"), VIEW_DISPLAY_DEBUG_CLUSTER_DECALS);
display_submenu->add_radio_check_item(TTR("Reflection Probe Cluster"), VIEW_DISPLAY_DEBUG_CLUSTER_REFLECTION_PROBES);
+ display_submenu->add_radio_check_item(TTR("Occlusion Culling Buffer"), VIEW_DISPLAY_DEBUG_OCCLUDERS);
display_submenu->set_name("display_advanced");
view_menu->get_popup()->add_submenu_item(TTR("Display Advanced..."), "display_advanced", VIEW_DISPLAY_ADVANCED);
@@ -4625,6 +4638,7 @@ Object *Node3DEditor::_get_editor_data(Object *p_what) {
si->sbox_instance,
RS::SHADOW_CASTING_SETTING_OFF);
RS::get_singleton()->instance_set_layer_mask(si->sbox_instance, 1 << Node3DEditorViewport::MISC_TOOL_LAYER);
+ RS::get_singleton()->instance_geometry_set_flag(si->sbox_instance, RS::INSTANCE_FLAG_IGNORE_OCCLUSION_CULLING, true);
si->sbox_instance_xray = RenderingServer::get_singleton()->instance_create2(
selection_box_xray->get_rid(),
sp->get_world_3d()->get_scenario());
@@ -4632,6 +4646,7 @@ Object *Node3DEditor::_get_editor_data(Object *p_what) {
si->sbox_instance_xray,
RS::SHADOW_CASTING_SETTING_OFF);
RS::get_singleton()->instance_set_layer_mask(si->sbox_instance_xray, 1 << Node3DEditorViewport::MISC_TOOL_LAYER);
+ RS::get_singleton()->instance_geometry_set_flag(si->sbox_instance, RS::INSTANCE_FLAG_IGNORE_OCCLUSION_CULLING, true);
return si;
}
@@ -5403,6 +5418,7 @@ void Node3DEditor::_init_indicators() {
origin_instance = RenderingServer::get_singleton()->instance_create2(origin, get_tree()->get_root()->get_world_3d()->get_scenario());
RS::get_singleton()->instance_set_layer_mask(origin_instance, 1 << Node3DEditorViewport::GIZMO_GRID_LAYER);
+ RS::get_singleton()->instance_geometry_set_flag(origin_instance, RS::INSTANCE_FLAG_IGNORE_OCCLUSION_CULLING, true);
RenderingServer::get_singleton()->instance_geometry_set_cast_shadows_setting(origin_instance, RS::SHADOW_CASTING_SETTING_OFF);
}
@@ -5964,6 +5980,7 @@ void Node3DEditor::_init_grid() {
RenderingServer::get_singleton()->instance_set_visible(grid_instance[c], grid_visible[a]);
RenderingServer::get_singleton()->instance_geometry_set_cast_shadows_setting(grid_instance[c], RS::SHADOW_CASTING_SETTING_OFF);
RS::get_singleton()->instance_set_layer_mask(grid_instance[c], 1 << Node3DEditorViewport::GIZMO_GRID_LAYER);
+ RS::get_singleton()->instance_geometry_set_flag(grid_instance[c], RS::INSTANCE_FLAG_IGNORE_OCCLUSION_CULLING, true);
}
}
@@ -6465,6 +6482,7 @@ void Node3DEditor::_register_all_gizmos() {
add_gizmo_plugin(Ref(memnew(Light3DGizmoPlugin)));
add_gizmo_plugin(Ref(memnew(AudioStreamPlayer3DGizmoPlugin)));
add_gizmo_plugin(Ref(memnew(MeshInstance3DGizmoPlugin)));
+ add_gizmo_plugin(Ref(memnew(OccluderInstance3DGizmoPlugin)));
add_gizmo_plugin(Ref(memnew(SoftBody3DGizmoPlugin)));
add_gizmo_plugin(Ref(memnew(Sprite3DGizmoPlugin)));
add_gizmo_plugin(Ref(memnew(Skeleton3DGizmoPlugin)));
@@ -7340,6 +7358,7 @@ void EditorNode3DGizmoPlugin::create_material(const String &p_name, const Color
material->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED);
material->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA);
material->set_render_priority(StandardMaterial3D::RENDER_PRIORITY_MIN + 1);
+ material->set_cull_mode(StandardMaterial3D::CULL_DISABLED);
if (p_use_vertex_color) {
material->set_flag(StandardMaterial3D::FLAG_ALBEDO_FROM_VERTEX_COLOR, true);
diff --git a/editor/plugins/node_3d_editor_plugin.h b/editor/plugins/node_3d_editor_plugin.h
index 70329f90c7d..33f4c324713 100644
--- a/editor/plugins/node_3d_editor_plugin.h
+++ b/editor/plugins/node_3d_editor_plugin.h
@@ -221,6 +221,7 @@ class Node3DEditorViewport : public Control {
VIEW_DISPLAY_DEBUG_CLUSTER_SPOT_LIGHTS,
VIEW_DISPLAY_DEBUG_CLUSTER_DECALS,
VIEW_DISPLAY_DEBUG_CLUSTER_REFLECTION_PROBES,
+ VIEW_DISPLAY_DEBUG_OCCLUDERS,
VIEW_LOCK_ROTATION,
VIEW_CINEMATIC_PREVIEW,
diff --git a/editor/plugins/occluder_instance_3d_editor_plugin.cpp b/editor/plugins/occluder_instance_3d_editor_plugin.cpp
new file mode 100644
index 00000000000..0821f140b37
--- /dev/null
+++ b/editor/plugins/occluder_instance_3d_editor_plugin.cpp
@@ -0,0 +1,117 @@
+/*************************************************************************/
+/* occluder_instance_3d_editor_plugin.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "occluder_instance_3d_editor_plugin.h"
+
+void OccluderInstance3DEditorPlugin::_bake_select_file(const String &p_file) {
+ if (occluder_instance) {
+ OccluderInstance3D::BakeError err;
+ if (get_tree()->get_edited_scene_root() && get_tree()->get_edited_scene_root() == occluder_instance) {
+ err = occluder_instance->bake(occluder_instance, p_file);
+ } else {
+ err = occluder_instance->bake(occluder_instance->get_parent(), p_file);
+ }
+
+ switch (err) {
+ case OccluderInstance3D::BAKE_ERROR_NO_SAVE_PATH: {
+ String scene_path = occluder_instance->get_filename();
+ if (scene_path == String()) {
+ scene_path = occluder_instance->get_owner()->get_filename();
+ }
+ if (scene_path == String()) {
+ EditorNode::get_singleton()->show_warning(TTR("Can't determine a save path for the occluder.\nSave your scene and try again."));
+ break;
+ }
+ scene_path = scene_path.get_basename() + ".occ";
+
+ file_dialog->set_current_path(scene_path);
+ file_dialog->popup_file_dialog();
+
+ } break;
+ case OccluderInstance3D::BAKE_ERROR_NO_MESHES: {
+ EditorNode::get_singleton()->show_warning(TTR("No meshes to bake."));
+ break;
+ }
+ default: {
+ }
+ }
+ }
+}
+
+void OccluderInstance3DEditorPlugin::_bake() {
+ _bake_select_file("");
+}
+
+void OccluderInstance3DEditorPlugin::edit(Object *p_object) {
+ OccluderInstance3D *s = Object::cast_to(p_object);
+ if (!s) {
+ return;
+ }
+
+ occluder_instance = s;
+}
+
+bool OccluderInstance3DEditorPlugin::handles(Object *p_object) const {
+ return p_object->is_class("OccluderInstance3D");
+}
+
+void OccluderInstance3DEditorPlugin::make_visible(bool p_visible) {
+ if (p_visible) {
+ bake->show();
+ } else {
+ bake->hide();
+ }
+}
+
+void OccluderInstance3DEditorPlugin::_bind_methods() {
+ ClassDB::bind_method("_bake", &OccluderInstance3DEditorPlugin::_bake);
+}
+
+OccluderInstance3DEditorPlugin::OccluderInstance3DEditorPlugin(EditorNode *p_node) {
+ editor = p_node;
+ bake = memnew(Button);
+ bake->set_flat(true);
+ bake->set_icon(editor->get_gui_base()->get_theme_icon("Bake", "EditorIcons"));
+ bake->set_text(TTR("Bake Occluders"));
+ bake->hide();
+ bake->connect("pressed", Callable(this, "_bake"));
+ add_control_to_container(CONTAINER_SPATIAL_EDITOR_MENU, bake);
+ occluder_instance = nullptr;
+
+ file_dialog = memnew(EditorFileDialog);
+ file_dialog->set_file_mode(EditorFileDialog::FILE_MODE_SAVE_FILE);
+ file_dialog->add_filter("*.occ ; Occluder3D");
+ file_dialog->set_title(TTR("Select occluder bake file:"));
+ file_dialog->connect("file_selected", callable_mp(this, &OccluderInstance3DEditorPlugin::_bake_select_file));
+ bake->add_child(file_dialog);
+}
+
+OccluderInstance3DEditorPlugin::~OccluderInstance3DEditorPlugin() {
+}
diff --git a/editor/plugins/occluder_instance_3d_editor_plugin.h b/editor/plugins/occluder_instance_3d_editor_plugin.h
new file mode 100644
index 00000000000..161b17811c9
--- /dev/null
+++ b/editor/plugins/occluder_instance_3d_editor_plugin.h
@@ -0,0 +1,66 @@
+/*************************************************************************/
+/* occluder_instance_3d_editor_plugin.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef OCCLUDER_INSTANCE_3D_EDITOR_PLUGIN_H
+#define OCCLUDER_INSTANCE_3D_EDITOR_PLUGIN_H
+
+#include "editor/editor_node.h"
+#include "editor/editor_plugin.h"
+#include "scene/3d/occluder_instance_3d.h"
+#include "scene/resources/material.h"
+
+class OccluderInstance3DEditorPlugin : public EditorPlugin {
+ GDCLASS(OccluderInstance3DEditorPlugin, EditorPlugin);
+
+ OccluderInstance3D *occluder_instance;
+
+ Button *bake;
+ EditorNode *editor;
+
+ EditorFileDialog *file_dialog;
+
+ void _bake_select_file(const String &p_file);
+ void _bake();
+
+protected:
+ static void _bind_methods();
+
+public:
+ virtual String get_name() const override { return "OccluderInstance3D"; }
+ bool has_main_screen() const override { return false; }
+ virtual void edit(Object *p_object) override;
+ virtual bool handles(Object *p_object) const override;
+ virtual void make_visible(bool p_visible) override;
+
+ OccluderInstance3DEditorPlugin(EditorNode *p_node);
+ ~OccluderInstance3DEditorPlugin();
+};
+
+#endif
diff --git a/modules/raycast/SCsub b/modules/raycast/SCsub
new file mode 100644
index 00000000000..68e9df52634
--- /dev/null
+++ b/modules/raycast/SCsub
@@ -0,0 +1,86 @@
+#!/usr/bin/env python
+
+Import("env")
+Import("env_modules")
+
+embree_src = [
+ "common/sys/sysinfo.cpp",
+ "common/sys/alloc.cpp",
+ "common/sys/filename.cpp",
+ "common/sys/library.cpp",
+ "common/sys/thread.cpp",
+ "common/sys/string.cpp",
+ "common/sys/regression.cpp",
+ "common/sys/mutex.cpp",
+ "common/sys/condition.cpp",
+ "common/sys/barrier.cpp",
+ "common/math/constants.cpp",
+ "common/simd/sse.cpp",
+ "common/lexers/stringstream.cpp",
+ "common/lexers/tokenstream.cpp",
+ "common/tasking/taskschedulerinternal.cpp",
+ "common/algorithms/parallel_for.cpp",
+ "common/algorithms/parallel_reduce.cpp",
+ "common/algorithms/parallel_prefix_sum.cpp",
+ "common/algorithms/parallel_for_for.cpp",
+ "common/algorithms/parallel_for_for_prefix_sum.cpp",
+ "common/algorithms/parallel_partition.cpp",
+ "common/algorithms/parallel_sort.cpp",
+ "common/algorithms/parallel_set.cpp",
+ "common/algorithms/parallel_map.cpp",
+ "common/algorithms/parallel_filter.cpp",
+ "kernels/common/device.cpp",
+ "kernels/common/stat.cpp",
+ "kernels/common/acceln.cpp",
+ "kernels/common/accelset.cpp",
+ "kernels/common/state.cpp",
+ "kernels/common/rtcore.cpp",
+ "kernels/common/rtcore_builder.cpp",
+ "kernels/common/scene.cpp",
+ "kernels/common/alloc.cpp",
+ "kernels/common/geometry.cpp",
+ "kernels/common/scene_triangle_mesh.cpp",
+ "kernels/geometry/primitive4.cpp",
+ "kernels/builders/primrefgen.cpp",
+ "kernels/bvh/bvh.cpp",
+ "kernels/bvh/bvh_statistics.cpp",
+ "kernels/bvh/bvh4_factory.cpp",
+ "kernels/bvh/bvh8_factory.cpp",
+ "kernels/bvh/bvh_collider.cpp",
+ "kernels/bvh/bvh_rotate.cpp",
+ "kernels/bvh/bvh_refit.cpp",
+ "kernels/bvh/bvh_builder.cpp",
+ "kernels/bvh/bvh_builder_morton.cpp",
+ "kernels/bvh/bvh_builder_sah.cpp",
+ "kernels/bvh/bvh_builder_sah_spatial.cpp",
+ "kernels/bvh/bvh_builder_sah_mb.cpp",
+ "kernels/bvh/bvh_builder_twolevel.cpp",
+ "kernels/bvh/bvh_intersector1_bvh4.cpp",
+]
+
+embree_dir = "#thirdparty/embree-aarch64/"
+
+env_embree = env_modules.Clone()
+embree_sources = [embree_dir + file for file in embree_src]
+env_embree.Prepend(CPPPATH=[embree_dir, embree_dir + "include"])
+env_embree.Append(CPPFLAGS=["-DEMBREE_TARGET_SSE2", "-DEMBREE_LOWEST_ISA", "-DTASKING_INTERNAL", "-DNDEBUG"])
+
+if not env_embree.msvc:
+ env_embree.Append(CPPFLAGS=["-msse2", "-mxsave"])
+ if env["platform"] == "windows":
+ env_embree.Append(CPPFLAGS=["-mstackrealign"])
+
+if env["platform"] == "windows":
+ if env.msvc:
+ env.Append(LINKFLAGS=["psapi.lib"])
+ env_embree.Append(CPPFLAGS=["-D__SSE2__", "-D__SSE__"])
+ else:
+ env.Append(LIBS=["psapi"])
+
+env_embree.disable_warnings()
+env_embree.add_source_files(env.modules_sources, embree_sources)
+
+env_raycast = env_modules.Clone()
+env_raycast.Prepend(CPPPATH=[embree_dir, embree_dir + "include", embree_dir + "common"])
+
+env_raycast.add_source_files(env.modules_sources, "*.cpp")
diff --git a/modules/raycast/config.py b/modules/raycast/config.py
new file mode 100644
index 00000000000..26493da41bf
--- /dev/null
+++ b/modules/raycast/config.py
@@ -0,0 +1,12 @@
+def can_build(env, platform):
+ if platform == "android":
+ return env["android_arch"] in ["arm64v8", "x86", "x86_64"]
+
+ if platform == "javascript":
+ return False # No SIMD support yet
+
+ return True
+
+
+def configure(env):
+ pass
diff --git a/modules/raycast/godot_update_embree.py b/modules/raycast/godot_update_embree.py
new file mode 100644
index 00000000000..db4fa95c21b
--- /dev/null
+++ b/modules/raycast/godot_update_embree.py
@@ -0,0 +1,260 @@
+import glob, os, shutil, subprocess, re
+
+include_dirs = [
+ "common/tasking",
+ "kernels/bvh",
+ "kernels/builders",
+ "common/sys",
+ "kernels",
+ "kernels/common",
+ "common/math",
+ "common/algorithms",
+ "common/lexers",
+ "common/simd",
+ "include/embree3",
+ "kernels/subdiv",
+ "kernels/geometry",
+]
+
+cpp_files = [
+ "common/sys/sysinfo.cpp",
+ "common/sys/alloc.cpp",
+ "common/sys/filename.cpp",
+ "common/sys/library.cpp",
+ "common/sys/thread.cpp",
+ "common/sys/string.cpp",
+ "common/sys/regression.cpp",
+ "common/sys/mutex.cpp",
+ "common/sys/condition.cpp",
+ "common/sys/barrier.cpp",
+ "common/math/constants.cpp",
+ "common/simd/sse.cpp",
+ "common/lexers/stringstream.cpp",
+ "common/lexers/tokenstream.cpp",
+ "common/tasking/taskschedulerinternal.cpp",
+ "common/algorithms/parallel_for.cpp",
+ "common/algorithms/parallel_reduce.cpp",
+ "common/algorithms/parallel_prefix_sum.cpp",
+ "common/algorithms/parallel_for_for.cpp",
+ "common/algorithms/parallel_for_for_prefix_sum.cpp",
+ "common/algorithms/parallel_partition.cpp",
+ "common/algorithms/parallel_sort.cpp",
+ "common/algorithms/parallel_set.cpp",
+ "common/algorithms/parallel_map.cpp",
+ "common/algorithms/parallel_filter.cpp",
+ "kernels/common/device.cpp",
+ "kernels/common/stat.cpp",
+ "kernels/common/acceln.cpp",
+ "kernels/common/accelset.cpp",
+ "kernels/common/state.cpp",
+ "kernels/common/rtcore.cpp",
+ "kernels/common/rtcore_builder.cpp",
+ "kernels/common/scene.cpp",
+ "kernels/common/alloc.cpp",
+ "kernels/common/geometry.cpp",
+ "kernels/common/scene_triangle_mesh.cpp",
+ "kernels/geometry/primitive4.cpp",
+ "kernels/builders/primrefgen.cpp",
+ "kernels/bvh/bvh.cpp",
+ "kernels/bvh/bvh_statistics.cpp",
+ "kernels/bvh/bvh4_factory.cpp",
+ "kernels/bvh/bvh8_factory.cpp",
+ "kernels/bvh/bvh_collider.cpp",
+ "kernels/bvh/bvh_rotate.cpp",
+ "kernels/bvh/bvh_refit.cpp",
+ "kernels/bvh/bvh_builder.cpp",
+ "kernels/bvh/bvh_builder_morton.cpp",
+ "kernels/bvh/bvh_builder_sah.cpp",
+ "kernels/bvh/bvh_builder_sah_spatial.cpp",
+ "kernels/bvh/bvh_builder_sah_mb.cpp",
+ "kernels/bvh/bvh_builder_twolevel.cpp",
+ "kernels/bvh/bvh_intersector1.cpp",
+ "kernels/bvh/bvh_intersector1_bvh4.cpp",
+]
+
+os.chdir("../../thirdparty")
+
+dir_name = "embree-aarch64"
+if os.path.exists(dir_name):
+ shutil.rmtree(dir_name)
+
+subprocess.run(["git", "clone", "https://github.com/lighttransport/embree-aarch64.git", "embree-tmp"])
+os.chdir("embree-tmp")
+
+commit_hash = str(subprocess.check_output(["git", "rev-parse", "HEAD"], universal_newlines=True)).strip()
+
+all_files = set(cpp_files)
+
+dest_dir = os.path.join("..", dir_name)
+for include_dir in include_dirs:
+ headers = glob.iglob(os.path.join(include_dir, "*.h"))
+ all_files.update(headers)
+
+for f in all_files:
+ d = os.path.join(dest_dir, os.path.dirname(f))
+ if not os.path.exists(d):
+ os.makedirs(d)
+ shutil.copy2(f, d)
+
+with open(os.path.join(dest_dir, "kernels/hash.h"), "w") as hash_file:
+ hash_file.write(
+ f"""
+// Copyright 2009-2020 Intel Corporation
+// SPDX-License-Identifier: Apache-2.0
+
+#define RTC_HASH "{commit_hash}"
+"""
+ )
+
+with open(os.path.join(dest_dir, "kernels/config.h"), "w") as config_file:
+ config_file.write(
+ """
+// Copyright 2009-2020 Intel Corporation
+// SPDX-License-Identifier: Apache-2.0
+
+/* #undef EMBREE_RAY_MASK */
+/* #undef EMBREE_STAT_COUNTERS */
+/* #undef EMBREE_BACKFACE_CULLING */
+/* #undef EMBREE_BACKFACE_CULLING_CURVES */
+#define EMBREE_FILTER_FUNCTION
+/* #undef EMBREE_IGNORE_INVALID_RAYS */
+#define EMBREE_GEOMETRY_TRIANGLE
+/* #undef EMBREE_GEOMETRY_QUAD */
+/* #undef EMBREE_GEOMETRY_CURVE */
+/* #undef EMBREE_GEOMETRY_SUBDIVISION */
+/* #undef EMBREE_GEOMETRY_USER */
+/* #undef EMBREE_GEOMETRY_INSTANCE */
+/* #undef EMBREE_GEOMETRY_GRID */
+/* #undef EMBREE_GEOMETRY_POINT */
+/* #undef EMBREE_RAY_PACKETS */
+/* #undef EMBREE_COMPACT_POLYS */
+
+#define EMBREE_CURVE_SELF_INTERSECTION_AVOIDANCE_FACTOR 2.0
+
+#if defined(EMBREE_GEOMETRY_TRIANGLE)
+ #define IF_ENABLED_TRIS(x) x
+#else
+ #define IF_ENABLED_TRIS(x)
+#endif
+
+#if defined(EMBREE_GEOMETRY_QUAD)
+ #define IF_ENABLED_QUADS(x) x
+#else
+ #define IF_ENABLED_QUADS(x)
+#endif
+
+#if defined(EMBREE_GEOMETRY_CURVE) || defined(EMBREE_GEOMETRY_POINT)
+ #define IF_ENABLED_CURVES_OR_POINTS(x) x
+#else
+ #define IF_ENABLED_CURVES_OR_POINTS(x)
+#endif
+
+#if defined(EMBREE_GEOMETRY_CURVE)
+ #define IF_ENABLED_CURVES(x) x
+#else
+ #define IF_ENABLED_CURVES(x)
+#endif
+
+#if defined(EMBREE_GEOMETRY_POINT)
+ #define IF_ENABLED_POINTS(x) x
+#else
+ #define IF_ENABLED_POINTS(x)
+#endif
+
+#if defined(EMBREE_GEOMETRY_SUBDIVISION)
+ #define IF_ENABLED_SUBDIV(x) x
+#else
+ #define IF_ENABLED_SUBDIV(x)
+#endif
+
+#if defined(EMBREE_GEOMETRY_USER)
+ #define IF_ENABLED_USER(x) x
+#else
+ #define IF_ENABLED_USER(x)
+#endif
+
+#if defined(EMBREE_GEOMETRY_INSTANCE)
+ #define IF_ENABLED_INSTANCE(x) x
+#else
+ #define IF_ENABLED_INSTANCE(x)
+#endif
+
+#if defined(EMBREE_GEOMETRY_GRID)
+ #define IF_ENABLED_GRIDS(x) x
+#else
+ #define IF_ENABLED_GRIDS(x)
+#endif
+"""
+ )
+
+
+with open("CMakeLists.txt", "r") as cmake_file:
+ cmake_content = cmake_file.read()
+ major_version = int(re.compile(r"EMBREE_VERSION_MAJOR\s(\d+)").findall(cmake_content)[0])
+ minor_version = int(re.compile(r"EMBREE_VERSION_MINOR\s(\d+)").findall(cmake_content)[0])
+ patch_version = int(re.compile(r"EMBREE_VERSION_PATCH\s(\d+)").findall(cmake_content)[0])
+
+with open(os.path.join(dest_dir, "include/embree3/rtcore_config.h"), "w") as config_file:
+ config_file.write(
+ f"""
+// Copyright 2009-2020 Intel Corporation
+// SPDX-License-Identifier: Apache-2.0
+
+#pragma once
+
+#define RTC_VERSION_MAJOR {major_version}
+#define RTC_VERSION_MINOR {minor_version}
+#define RTC_VERSION_PATCH {patch_version}
+#define RTC_VERSION {major_version}{minor_version:02d}{patch_version:02d}
+#define RTC_VERSION_STRING "{major_version}.{minor_version}.{patch_version}"
+
+#define RTC_MAX_INSTANCE_LEVEL_COUNT 1
+
+#define EMBREE_MIN_WIDTH 0
+#define RTC_MIN_WIDTH EMBREE_MIN_WIDTH
+
+#define EMBREE_STATIC_LIB
+/* #undef EMBREE_API_NAMESPACE */
+
+#if defined(EMBREE_API_NAMESPACE)
+# define RTC_NAMESPACE
+# define RTC_NAMESPACE_BEGIN namespace {{
+# define RTC_NAMESPACE_END }}
+# define RTC_NAMESPACE_USE using namespace ;
+# define RTC_API_EXTERN_C
+# undef EMBREE_API_NAMESPACE
+#else
+# define RTC_NAMESPACE_BEGIN
+# define RTC_NAMESPACE_END
+# define RTC_NAMESPACE_USE
+# if defined(__cplusplus)
+# define RTC_API_EXTERN_C extern "C"
+# else
+# define RTC_API_EXTERN_C
+# endif
+#endif
+
+#if defined(ISPC)
+# define RTC_API_IMPORT extern "C" unmasked
+# define RTC_API_EXPORT extern "C" unmasked
+#elif defined(EMBREE_STATIC_LIB)
+# define RTC_API_IMPORT RTC_API_EXTERN_C
+# define RTC_API_EXPORT RTC_API_EXTERN_C
+#elif defined(_WIN32)
+# define RTC_API_IMPORT RTC_API_EXTERN_C __declspec(dllimport)
+# define RTC_API_EXPORT RTC_API_EXTERN_C __declspec(dllexport)
+#else
+# define RTC_API_IMPORT RTC_API_EXTERN_C
+# define RTC_API_EXPORT RTC_API_EXTERN_C __attribute__ ((visibility ("default")))
+#endif
+
+#if defined(RTC_EXPORT_API)
+# define RTC_API RTC_API_EXPORT
+#else
+# define RTC_API RTC_API_IMPORT
+#endif
+"""
+ )
+
+os.chdir("..")
+shutil.rmtree("embree-tmp")
diff --git a/modules/raycast/lightmap_raycaster.cpp b/modules/raycast/lightmap_raycaster.cpp
new file mode 100644
index 00000000000..9039622d3da
--- /dev/null
+++ b/modules/raycast/lightmap_raycaster.cpp
@@ -0,0 +1,202 @@
+/*************************************************************************/
+/* lightmap_raycaster.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifdef TOOLS_ENABLED
+
+#include "lightmap_raycaster.h"
+
+// From Embree.
+#include