summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/callable_method_pointer.h1
-rw-r--r--core/hashfuncs.h3
-rw-r--r--core/math/basis.cpp4
-rw-r--r--core/resource.cpp3
-rw-r--r--core/rid.h5
-rw-r--r--core/rid_owner.h5
-rw-r--r--doc/classes/AudioStreamSample.xml12
-rw-r--r--doc/classes/PhysicalBone3D.xml41
-rw-r--r--doc/classes/RigidBody3D.xml2
-rw-r--r--drivers/vulkan/rendering_device_vulkan.cpp13
-rw-r--r--drivers/vulkan/vulkan_context.cpp11
-rw-r--r--editor/node_3d_editor_gizmos.cpp137
-rw-r--r--editor/node_3d_editor_gizmos.h18
-rw-r--r--editor/plugins/canvas_item_editor_plugin.cpp10
-rw-r--r--editor/plugins/node_3d_editor_plugin.cpp6
-rw-r--r--editor/plugins/node_3d_editor_plugin.h1
-rwxr-xr-xmisc/travis/android-tools-linux.sh2
-rw-r--r--modules/gdscript/gdscript_tokenizer.h1
-rw-r--r--modules/visual_script/visual_script_builtin_funcs.cpp4
-rw-r--r--platform/android/java/app/config.gradle2
-rw-r--r--platform/android/java/lib/build.gradle1
-rw-r--r--platform/android/java/plugins/godotpayment/build.gradle1
-rw-r--r--platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/ValidateTask.java4
-rw-r--r--platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/utils/CustomSSLSocketFactory.java (renamed from platform/android/java/lib/src/org/godotengine/godot/utils/CustomSSLSocketFactory.java)3
-rw-r--r--platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/utils/HttpRequester.java (renamed from platform/android/java/lib/src/org/godotengine/godot/utils/HttpRequester.java)3
-rw-r--r--platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/utils/RequestParams.java (renamed from platform/android/java/lib/src/org/godotengine/godot/utils/RequestParams.java)3
-rw-r--r--platform/linuxbsd/display_server_x11.cpp5
-rw-r--r--scene/3d/decal.cpp235
-rw-r--r--scene/3d/decal.h114
-rw-r--r--scene/3d/light_3d.cpp42
-rw-r--r--scene/3d/light_3d.h6
-rw-r--r--scene/3d/physics_body_3d.cpp130
-rw-r--r--scene/3d/physics_body_3d.h22
-rw-r--r--scene/3d/spring_arm_3d.cpp2
-rw-r--r--scene/gui/graph_edit.cpp25
-rw-r--r--scene/gui/graph_edit.h2
-rw-r--r--scene/main/viewport.cpp1
-rw-r--r--scene/main/viewport.h3
-rw-r--r--scene/register_scene_types.cpp2
-rw-r--r--servers/rendering/rasterizer.h23
-rw-r--r--servers/rendering/rasterizer_rd/light_cluster_builder.cpp1
-rw-r--r--servers/rendering/rasterizer_rd/light_cluster_builder.h30
-rw-r--r--servers/rendering/rasterizer_rd/rasterizer_effects_rd.cpp34
-rw-r--r--servers/rendering/rasterizer_rd/rasterizer_effects_rd.h16
-rw-r--r--servers/rendering/rasterizer_rd/rasterizer_scene_high_end_rd.cpp221
-rw-r--r--servers/rendering/rasterizer_rd/rasterizer_scene_high_end_rd.h26
-rw-r--r--servers/rendering/rasterizer_rd/rasterizer_scene_rd.cpp31
-rw-r--r--servers/rendering/rasterizer_rd/rasterizer_scene_rd.h27
-rw-r--r--servers/rendering/rasterizer_rd/rasterizer_storage_rd.cpp379
-rw-r--r--servers/rendering/rasterizer_rd/rasterizer_storage_rd.h164
-rw-r--r--servers/rendering/rasterizer_rd/shaders/copy.glsl2
-rw-r--r--servers/rendering/rasterizer_rd/shaders/copy_to_fb.glsl44
-rw-r--r--servers/rendering/rasterizer_rd/shaders/scene_high_end.glsl206
-rw-r--r--servers/rendering/rasterizer_rd/shaders/scene_high_end_inc.glsl41
-rw-r--r--servers/rendering/rendering_server_raster.h14
-rw-r--r--servers/rendering/rendering_server_scene.cpp76
-rw-r--r--servers/rendering/rendering_server_scene.h22
-rw-r--r--servers/rendering/rendering_server_wrap_mt.h14
-rw-r--r--servers/rendering_server.h24
59 files changed, 2133 insertions, 147 deletions
diff --git a/core/callable_method_pointer.h b/core/callable_method_pointer.h
index a931a344e6..fb809c2b44 100644
--- a/core/callable_method_pointer.h
+++ b/core/callable_method_pointer.h
@@ -34,6 +34,7 @@
#include "core/callable.h"
#include "core/hashfuncs.h"
#include "core/object.h"
+#include "core/os/copymem.h"
#include "core/simple_type.h"
class CallableCustomMethodPointerBase : public CallableCustom {
diff --git a/core/hashfuncs.h b/core/hashfuncs.h
index 219b8b2658..a41a034843 100644
--- a/core/hashfuncs.h
+++ b/core/hashfuncs.h
@@ -35,10 +35,10 @@
#include "core/math/math_funcs.h"
#include "core/node_path.h"
#include "core/object_id.h"
+#include "core/rid.h"
#include "core/string_name.h"
#include "core/typedefs.h"
#include "core/ustring.h"
-
/**
* Hashing functions
*/
@@ -150,6 +150,7 @@ struct HashMapHasherDefault {
static _FORCE_INLINE_ uint32_t hash(const uint8_t p_int) { return p_int; }
static _FORCE_INLINE_ uint32_t hash(const int8_t p_int) { return (uint32_t)p_int; }
static _FORCE_INLINE_ uint32_t hash(const wchar_t p_wchar) { return (uint32_t)p_wchar; }
+ static _FORCE_INLINE_ uint32_t hash(const RID &p_rid) { return hash_one_uint64(p_rid.get_id()); }
static _FORCE_INLINE_ uint32_t hash(const StringName &p_string_name) { return p_string_name.hash(); }
static _FORCE_INLINE_ uint32_t hash(const NodePath &p_path) { return p_path.hash(); }
diff --git a/core/math/basis.cpp b/core/math/basis.cpp
index 14079f811d..0f519a20d8 100644
--- a/core/math/basis.cpp
+++ b/core/math/basis.cpp
@@ -77,10 +77,6 @@ void Basis::invert() {
void Basis::orthonormalize() {
-#ifdef MATH_CHECKS
- ERR_FAIL_COND(determinant() == 0);
-#endif
-
// Gram-Schmidt Process
Vector3 x = get_axis(0);
diff --git a/core/resource.cpp b/core/resource.cpp
index d1883d8043..8d5c441b21 100644
--- a/core/resource.cpp
+++ b/core/resource.cpp
@@ -477,6 +477,9 @@ void ResourceCache::clear() {
resources.clear();
memdelete(lock);
+#ifdef TOOLS_ENABLED
+ memdelete(path_cache_lock);
+#endif
}
void ResourceCache::reload_externals() {
diff --git a/core/rid.h b/core/rid.h
index 3cc0ee3084..a2f73423a3 100644
--- a/core/rid.h
+++ b/core/rid.h
@@ -31,11 +31,6 @@
#ifndef RID_H
#define RID_H
-#include "core/list.h"
-#include "core/oa_hash_map.h"
-#include "core/os/memory.h"
-#include "core/safe_refcount.h"
-#include "core/set.h"
#include "core/typedefs.h"
class RID_AllocBase;
diff --git a/core/rid_owner.h b/core/rid_owner.h
index 946b2e396c..ad6996b9a7 100644
--- a/core/rid_owner.h
+++ b/core/rid_owner.h
@@ -31,8 +31,13 @@
#ifndef RID_OWNER_H
#define RID_OWNER_H
+#include "core/list.h"
+#include "core/oa_hash_map.h"
+#include "core/os/memory.h"
#include "core/print_string.h"
#include "core/rid.h"
+#include "core/safe_refcount.h"
+#include "core/set.h"
#include "core/spin_lock.h"
#include <stdio.h>
#include <typeinfo>
diff --git a/doc/classes/AudioStreamSample.xml b/doc/classes/AudioStreamSample.xml
index 6d99433c90..c12e1bd05c 100644
--- a/doc/classes/AudioStreamSample.xml
+++ b/doc/classes/AudioStreamSample.xml
@@ -30,13 +30,13 @@
Audio format. See [enum Format] constants for values.
</member>
<member name="loop_begin" type="int" setter="set_loop_begin" getter="get_loop_begin" default="0">
- Loop start in bytes.
+ The loop start point (in number of samples, relative to the beginning of the sample). This information will be imported automatically from the WAV file if present.
</member>
<member name="loop_end" type="int" setter="set_loop_end" getter="get_loop_end" default="0">
- Loop end in bytes.
+ The loop end point (in number of samples, relative to the beginning of the sample). This information will be imported automatically from the WAV file if present.
</member>
<member name="loop_mode" type="int" setter="set_loop_mode" getter="get_loop_mode" enum="AudioStreamSample.LoopMode" default="0">
- Loop mode. See [enum LoopMode] constants for values.
+ The loop mode. This information will be imported automatically from the WAV file if present. See [enum LoopMode] constants for values.
</member>
<member name="mix_rate" type="int" setter="set_mix_rate" getter="get_mix_rate" default="44100">
The sample rate for mixing this audio.
@@ -59,13 +59,13 @@
Audio does not loop.
</constant>
<constant name="LOOP_FORWARD" value="1" enum="LoopMode">
- Audio loops the data between [member loop_begin] and [member loop_end] playing forward only.
+ Audio loops the data between [member loop_begin] and [member loop_end], playing forward only.
</constant>
<constant name="LOOP_PING_PONG" value="2" enum="LoopMode">
- Audio loops the data between [member loop_begin] and [member loop_end] playing back and forth.
+ Audio loops the data between [member loop_begin] and [member loop_end], playing back and forth.
</constant>
<constant name="LOOP_BACKWARD" value="3" enum="LoopMode">
- Audio loops the data between [member loop_begin] and [member loop_end] playing backward only.
+ Audio loops the data between [member loop_begin] and [member loop_end], playing backward only.
</constant>
</constants>
</class>
diff --git a/doc/classes/PhysicalBone3D.xml b/doc/classes/PhysicalBone3D.xml
index d45c72ee87..75f1f3eab4 100644
--- a/doc/classes/PhysicalBone3D.xml
+++ b/doc/classes/PhysicalBone3D.xml
@@ -45,21 +45,62 @@
</method>
</methods>
<members>
+ <member name="angular_damp" type="float" setter="set_angular_damp" getter="get_angular_damp" default="-1.0">
+ Damps the body's rotation if greater than [code]0[/code].
+ </member>
+ <member name="axis_lock_angular_x" type="bool" setter="set_axis_lock" getter="get_axis_lock" default="false">
+ Lock the body's rotation in the X axis.
+ </member>
+ <member name="axis_lock_angular_y" type="bool" setter="set_axis_lock" getter="get_axis_lock" default="false">
+ Lock the body's rotation in the Y axis.
+ </member>
+ <member name="axis_lock_angular_z" type="bool" setter="set_axis_lock" getter="get_axis_lock" default="false">
+ Lock the body's rotation in the Z axis.
+ </member>
+ <member name="axis_lock_linear_x" type="bool" setter="set_axis_lock" getter="get_axis_lock" default="false">
+ Lock the body's movement in the X axis.
+ </member>
+ <member name="axis_lock_linear_y" type="bool" setter="set_axis_lock" getter="get_axis_lock" default="false">
+ Lock the body's movement in the Y axis.
+ </member>
+ <member name="axis_lock_linear_z" type="bool" setter="set_axis_lock" getter="get_axis_lock" default="false">
+ Lock the body's movement in the Z axis.
+ </member>
<member name="body_offset" type="Transform" setter="set_body_offset" getter="get_body_offset" default="Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0 )">
+ Sets the body's transform.
</member>
<member name="bounce" type="float" setter="set_bounce" getter="get_bounce" default="0.0">
+ The body's bounciness. Values range from [code]0[/code] (no bounce) to [code]1[/code] (full bounciness).
+ </member>
+ <member name="can_sleep" type="bool" setter="set_can_sleep" getter="is_able_to_sleep" default="true">
+ If [code]true[/code], the body is deactivated when there is no movement, so it will not take part in the simulation until it is awaken by an external force.
</member>
<member name="friction" type="float" setter="set_friction" getter="get_friction" default="1.0">
+ The body's friction, from [code]0[/code] (frictionless) to [code]1[/code] (max friction).
</member>
<member name="gravity_scale" type="float" setter="set_gravity_scale" getter="get_gravity_scale" default="1.0">
+ This is multiplied by the global 3D gravity setting found in [b]Project &gt; Project Settings &gt; Physics &gt; 3d[/b] to produce the body's gravity. For example, a value of 1 will be normal gravity, 2 will apply double gravity, and 0.5 will apply half gravity to this object.
</member>
<member name="joint_offset" type="Transform" setter="set_joint_offset" getter="get_joint_offset" default="Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0 )">
+ Sets the joint's transform.
+ </member>
+ <member name="joint_rotation" type="Vector3" setter="set_joint_rotation" getter="get_joint_rotation" default="Vector3( 0, 0, 0 )">
+ Sets the joint's rotation in radians.
+ </member>
+ <member name="joint_rotation_degrees" type="Vector3" setter="set_joint_rotation_degrees" getter="get_joint_rotation_degrees" default="Vector3( 0, 0, 0 )">
+ Sets the joint's rotation in degrees.
</member>
<member name="joint_type" type="int" setter="set_joint_type" getter="get_joint_type" enum="PhysicalBone3D.JointType" default="0">
+ Sets the joint type. See [enum JointType] for possible values.
+ </member>
+ <member name="linear_damp" type="float" setter="set_linear_damp" getter="get_linear_damp" default="-1.0">
+ Damps the body's movement if greater than [code]0[/code].
</member>
<member name="mass" type="float" setter="set_mass" getter="get_mass" default="1.0">
+ The body's mass.
</member>
<member name="weight" type="float" setter="set_weight" getter="get_weight" default="9.8">
+ The body's weight based on its mass and the global 3D gravity. Global values are set in [b]Project &gt; Project Settings &gt; Physics &gt; 3d[/b].
</member>
</members>
<constants>
diff --git a/doc/classes/RigidBody3D.xml b/doc/classes/RigidBody3D.xml
index 829589f650..cfb9ca513e 100644
--- a/doc/classes/RigidBody3D.xml
+++ b/doc/classes/RigidBody3D.xml
@@ -146,7 +146,7 @@
Lock the body's movement in the Z axis.
</member>
<member name="can_sleep" type="bool" setter="set_can_sleep" getter="is_able_to_sleep" default="true">
- If [code]true[/code], the RigidBody3D will not calculate forces and will act as a static body while there is no movement. It will wake up when forces are applied through other collisions or when the [code]apply_impulse[/code] method is used.
+ If [code]true[/code], the body is deactivated when there is no movement, so it will not take part in the simulation until it is awaken by an external force.
</member>
<member name="contact_monitor" type="bool" setter="set_contact_monitor" getter="is_contact_monitor_enabled" default="false">
If [code]true[/code], the RigidBody3D will emit signals when it collides with another RigidBody3D.
diff --git a/drivers/vulkan/rendering_device_vulkan.cpp b/drivers/vulkan/rendering_device_vulkan.cpp
index 23ac5f4eef..2769469838 100644
--- a/drivers/vulkan/rendering_device_vulkan.cpp
+++ b/drivers/vulkan/rendering_device_vulkan.cpp
@@ -2851,8 +2851,8 @@ Error RenderingDeviceVulkan::texture_clear(RID p_texture, const Color &p_color,
src_layer_count *= 6;
}
- ERR_FAIL_COND_V(src_tex->base_mipmap + p_base_mipmap + p_mipmaps > src_tex->mipmaps, ERR_INVALID_PARAMETER);
- ERR_FAIL_COND_V(src_tex->base_layer + p_base_layer + p_layers > src_layer_count, ERR_INVALID_PARAMETER);
+ ERR_FAIL_COND_V(p_base_mipmap + p_mipmaps > src_tex->mipmaps, ERR_INVALID_PARAMETER);
+ ERR_FAIL_COND_V(p_base_layer + p_layers > src_layer_count, ERR_INVALID_PARAMETER);
VkCommandBuffer command_buffer = p_sync_with_draw ? frames[frame].draw_command_buffer : frames[frame].setup_command_buffer;
@@ -2888,9 +2888,9 @@ Error RenderingDeviceVulkan::texture_clear(RID p_texture, const Color &p_color,
VkImageSubresourceRange range;
range.aspectMask = src_tex->read_aspect_mask;
- range.baseArrayLayer = p_base_layer;
+ range.baseArrayLayer = src_tex->base_layer + p_base_layer;
range.layerCount = p_layers;
- range.baseMipLevel = p_base_mipmap;
+ range.baseMipLevel = src_tex->base_mipmap + p_base_mipmap;
range.levelCount = p_mipmaps;
vkCmdClearColorImage(command_buffer, src_tex->image, layout, &clear_color, 1, &range);
@@ -7274,6 +7274,11 @@ void RenderingDeviceVulkan::finalize() {
vertex_formats.erase(temp);
}
+ for (int i = 0; i < framebuffer_formats.size(); i++) {
+ vkDestroyRenderPass(device, framebuffer_formats[i].render_pass, nullptr);
+ }
+ framebuffer_formats.clear();
+
//all these should be clear at this point
ERR_FAIL_COND(descriptor_pools.size());
ERR_FAIL_COND(dependency_map.size());
diff --git a/drivers/vulkan/vulkan_context.cpp b/drivers/vulkan/vulkan_context.cpp
index 68b4cdf4a4..0ce9ccce4c 100644
--- a/drivers/vulkan/vulkan_context.cpp
+++ b/drivers/vulkan/vulkan_context.cpp
@@ -1503,4 +1503,15 @@ VulkanContext::~VulkanContext() {
if (queue_props) {
free(queue_props);
}
+ for (uint32_t i = 0; i < FRAME_LAG; i++) {
+ vkDestroyFence(device, fences[i], nullptr);
+ vkDestroySemaphore(device, image_acquired_semaphores[i], nullptr);
+ vkDestroySemaphore(device, draw_complete_semaphores[i], nullptr);
+ if (separate_present_queue) {
+ vkDestroySemaphore(device, image_ownership_semaphores[i], nullptr);
+ }
+ }
+ DestroyDebugUtilsMessengerEXT(inst, dbg_messenger, nullptr);
+ vkDestroyDevice(device, nullptr);
+ vkDestroyInstance(inst, nullptr);
}
diff --git a/editor/node_3d_editor_gizmos.cpp b/editor/node_3d_editor_gizmos.cpp
index c06e5f3741..724782ac44 100644
--- a/editor/node_3d_editor_gizmos.cpp
+++ b/editor/node_3d_editor_gizmos.cpp
@@ -37,6 +37,7 @@
#include "scene/3d/collision_polygon_3d.h"
#include "scene/3d/collision_shape_3d.h"
#include "scene/3d/cpu_particles_3d.h"
+#include "scene/3d/decal.h"
#include "scene/3d/gi_probe.h"
#include "scene/3d/gpu_particles_3d.h"
#include "scene/3d/light_3d.h"
@@ -2718,7 +2719,143 @@ void ReflectionProbeGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
p_gizmo->add_unscaled_billboard(icon, 0.05);
p_gizmo->add_handles(handles, get_material("handles"));
}
+///////////////////////////////
+////
+
+DecalGizmoPlugin::DecalGizmoPlugin() {
+ Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/decal", Color(0.6, 0.5, 1.0));
+
+ create_material("decal_material", gizmo_color);
+
+ create_handle_material("handles");
+}
+
+bool DecalGizmoPlugin::has_gizmo(Node3D *p_spatial) {
+ return Object::cast_to<Decal>(p_spatial) != nullptr;
+}
+
+String DecalGizmoPlugin::get_name() const {
+ return "Decal";
+}
+
+int DecalGizmoPlugin::get_priority() const {
+ return -1;
+}
+
+String DecalGizmoPlugin::get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_idx) const {
+
+ switch (p_idx) {
+ case 0: return "Extents X";
+ case 1: return "Extents Y";
+ case 2: return "Extents Z";
+ }
+
+ return "";
+}
+Variant DecalGizmoPlugin::get_handle_value(EditorNode3DGizmo *p_gizmo, int p_idx) const {
+
+ Decal *decal = Object::cast_to<Decal>(p_gizmo->get_spatial_node());
+ return decal->get_extents();
+}
+void DecalGizmoPlugin::set_handle(EditorNode3DGizmo *p_gizmo, int p_idx, Camera3D *p_camera, const Point2 &p_point) {
+
+ Decal *decal = Object::cast_to<Decal>(p_gizmo->get_spatial_node());
+ Transform gt = decal->get_global_transform();
+
+ Transform gi = gt.affine_inverse();
+
+ Vector3 extents = decal->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 (Node3DEditor::get_singleton()->is_snap_enabled()) {
+ d = Math::stepify(d, Node3DEditor::get_singleton()->get_translate_snap());
+ }
+
+ if (d < 0.001)
+ d = 0.001;
+
+ extents[p_idx] = d;
+ decal->set_extents(extents);
+}
+
+void DecalGizmoPlugin::commit_handle(EditorNode3DGizmo *p_gizmo, int p_idx, const Variant &p_restore, bool p_cancel) {
+
+ Decal *decal = Object::cast_to<Decal>(p_gizmo->get_spatial_node());
+
+ Vector3 restore = p_restore;
+
+ if (p_cancel) {
+ decal->set_extents(restore);
+ return;
+ }
+
+ UndoRedo *ur = Node3DEditor::get_singleton()->get_undo_redo();
+ ur->create_action(TTR("Change Decal Extents"));
+ ur->add_do_method(decal, "set_extents", decal->get_extents());
+ ur->add_undo_method(decal, "set_extents", restore);
+ ur->commit_action();
+}
+
+void DecalGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
+
+ Decal *decal = Object::cast_to<Decal>(p_gizmo->get_spatial_node());
+
+ p_gizmo->clear();
+
+ Vector<Vector3> lines;
+ Vector3 extents = decal->get_extents();
+
+ AABB aabb;
+ aabb.position = -extents;
+ aabb.size = extents * 2;
+
+ for (int i = 0; i < 12; i++) {
+ Vector3 a, b;
+ aabb.get_edge(i, a, b);
+ if (a.y == b.y) {
+ lines.push_back(a);
+ lines.push_back(b);
+ } else {
+ Vector3 ah = a.linear_interpolate(b, 0.2);
+ lines.push_back(a);
+ lines.push_back(ah);
+ Vector3 bh = b.linear_interpolate(a, 0.2);
+ lines.push_back(b);
+ lines.push_back(bh);
+ }
+ }
+
+ lines.push_back(Vector3(0, extents.y, 0));
+ lines.push_back(Vector3(0, extents.y * 1.2, 0));
+
+ Vector<Vector3> handles;
+
+ for (int i = 0; i < 3; i++) {
+
+ Vector3 ax;
+ ax[i] = aabb.position[i] + aabb.size[i];
+ handles.push_back(ax);
+ }
+
+ Ref<Material> material = get_material("decal_material", p_gizmo);
+
+ p_gizmo->add_lines(lines, material);
+
+ p_gizmo->add_handles(handles, get_material("handles"));
+}
+
+///////////////////////////////
GIProbeGizmoPlugin::GIProbeGizmoPlugin() {
Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/gi_probe", Color(0.5, 1, 0.6));
diff --git a/editor/node_3d_editor_gizmos.h b/editor/node_3d_editor_gizmos.h
index 889b0e8315..8bc52b6ba9 100644
--- a/editor/node_3d_editor_gizmos.h
+++ b/editor/node_3d_editor_gizmos.h
@@ -285,6 +285,24 @@ public:
ReflectionProbeGizmoPlugin();
};
+class DecalGizmoPlugin : public EditorNode3DGizmoPlugin {
+
+ GDCLASS(DecalGizmoPlugin, EditorNode3DGizmoPlugin);
+
+public:
+ bool has_gizmo(Node3D *p_spatial);
+ String get_name() const;
+ int get_priority() const;
+ void redraw(EditorNode3DGizmo *p_gizmo);
+
+ String get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_idx) const;
+ Variant get_handle_value(EditorNode3DGizmo *p_gizmo, int p_idx) const;
+ void set_handle(EditorNode3DGizmo *p_gizmo, int p_idx, Camera3D *p_camera, const Point2 &p_point);
+ void commit_handle(EditorNode3DGizmo *p_gizmo, int p_idx, const Variant &p_restore, bool p_cancel = false);
+
+ DecalGizmoPlugin();
+};
+
class GIProbeGizmoPlugin : public EditorNode3DGizmoPlugin {
GDCLASS(GIProbeGizmoPlugin, EditorNode3DGizmoPlugin);
diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp
index 43a88a32c8..cd3df08276 100644
--- a/editor/plugins/canvas_item_editor_plugin.cpp
+++ b/editor/plugins/canvas_item_editor_plugin.cpp
@@ -3200,13 +3200,15 @@ void CanvasItemEditor::_draw_selection() {
RID ci = viewport->get_canvas_item();
- List<CanvasItem *> selection = _get_edited_canvas_items(false, false);
+ List<CanvasItem *> selection = _get_edited_canvas_items(true, false);
bool single = selection.size() == 1;
for (List<CanvasItem *>::Element *E = selection.front(); E; E = E->next()) {
CanvasItem *canvas_item = Object::cast_to<CanvasItem>(E->get());
CanvasItemEditorSelectedItem *se = editor_selection->get_node_editor_data<CanvasItemEditorSelectedItem>(canvas_item);
+ bool item_locked = canvas_item->has_meta("_edit_lock_");
+
// Draw the previous position if we are dragging the node
if (show_helpers &&
(drag_type == DRAG_MOVE || drag_type == DRAG_ROTATE ||
@@ -3246,6 +3248,10 @@ void CanvasItemEditor::_draw_selection() {
Color c = Color(1, 0.6, 0.4, 0.7);
+ if (item_locked) {
+ c = Color(0.7, 0.7, 0.7, 0.7);
+ }
+
for (int i = 0; i < 4; i++) {
viewport->draw_line(endpoints[i], endpoints[(i + 1) % 4], c, Math::round(2 * EDSCALE));
}
@@ -3258,7 +3264,7 @@ void CanvasItemEditor::_draw_selection() {
viewport->draw_set_transform_matrix(viewport->get_transform());
}
- if (single && (tool == TOOL_SELECT || tool == TOOL_MOVE || tool == TOOL_SCALE || tool == TOOL_ROTATE || tool == TOOL_EDIT_PIVOT)) { //kind of sucks
+ if (single && !item_locked && (tool == TOOL_SELECT || tool == TOOL_MOVE || tool == TOOL_SCALE || tool == TOOL_ROTATE || tool == TOOL_EDIT_PIVOT)) { //kind of sucks
// Draw the pivot
if (canvas_item->_edit_use_pivot()) {
diff --git a/editor/plugins/node_3d_editor_plugin.cpp b/editor/plugins/node_3d_editor_plugin.cpp
index 92497874c3..354c951a7c 100644
--- a/editor/plugins/node_3d_editor_plugin.cpp
+++ b/editor/plugins/node_3d_editor_plugin.cpp
@@ -3034,6 +3034,7 @@ void Node3DEditorViewport::_menu_option(int p_option) {
case VIEW_DISPLAY_DEBUG_SCENE_LUMINANCE:
case VIEW_DISPLAY_DEBUG_SSAO:
case VIEW_DISPLAY_DEBUG_PSSM_SPLITS:
+ case VIEW_DISPLAY_DEBUG_DECAL_ATLAS:
case VIEW_DISPLAY_DEBUG_ROUGHNESS_LIMITER: {
static const int display_options[] = {
@@ -3053,6 +3054,7 @@ void Node3DEditorViewport::_menu_option(int p_option) {
VIEW_DISPLAY_DEBUG_SSAO,
VIEW_DISPLAY_DEBUG_ROUGHNESS_LIMITER,
VIEW_DISPLAY_DEBUG_PSSM_SPLITS,
+ VIEW_DISPLAY_DEBUG_DECAL_ATLAS,
VIEW_MAX
};
static const Viewport::DebugDraw debug_draw_modes[] = {
@@ -3072,6 +3074,7 @@ void Node3DEditorViewport::_menu_option(int p_option) {
Viewport::DEBUG_DRAW_SSAO,
Viewport::DEBUG_DRAW_ROUGHNESS_LIMITER,
Viewport::DEBUG_DRAW_PSSM_SPLITS,
+ Viewport::DEBUG_DRAW_DECAL_ATLAS,
};
int idx = 0;
@@ -3933,6 +3936,8 @@ Node3DEditorViewport::Node3DEditorViewport(Node3DEditor *p_spatial_editor, Edito
display_submenu->add_radio_check_item(TTR("Shadow Atlas"), VIEW_DISPLAY_DEBUG_SHADOW_ATLAS);
display_submenu->add_radio_check_item(TTR("Directional Shadow"), VIEW_DISPLAY_DEBUG_DIRECTIONAL_SHADOW_ATLAS);
display_submenu->add_separator();
+ display_submenu->add_radio_check_item(TTR("Decal Atlas"), VIEW_DISPLAY_DEBUG_DECAL_ATLAS);
+ display_submenu->add_separator();
display_submenu->add_radio_check_item(TTR("GIProbe Lighting"), VIEW_DISPLAY_DEBUG_GIPROBE_LIGHTING);
display_submenu->add_radio_check_item(TTR("GIProbe Albedo"), VIEW_DISPLAY_DEBUG_GIPROBE_ALBEDO);
display_submenu->add_radio_check_item(TTR("GIProbe Emission"), VIEW_DISPLAY_DEBUG_GIPROBE_EMISSION);
@@ -5963,6 +5968,7 @@ void Node3DEditor::_register_all_gizmos() {
add_gizmo_plugin(Ref<GPUParticles3DGizmoPlugin>(memnew(GPUParticles3DGizmoPlugin)));
add_gizmo_plugin(Ref<CPUParticles3DGizmoPlugin>(memnew(CPUParticles3DGizmoPlugin)));
add_gizmo_plugin(Ref<ReflectionProbeGizmoPlugin>(memnew(ReflectionProbeGizmoPlugin)));
+ add_gizmo_plugin(Ref<DecalGizmoPlugin>(memnew(DecalGizmoPlugin)));
add_gizmo_plugin(Ref<GIProbeGizmoPlugin>(memnew(GIProbeGizmoPlugin)));
// add_gizmo_plugin(Ref<BakedIndirectLightGizmoPlugin>(memnew(BakedIndirectLightGizmoPlugin)));
add_gizmo_plugin(Ref<CollisionShape3DGizmoPlugin>(memnew(CollisionShape3DGizmoPlugin)));
diff --git a/editor/plugins/node_3d_editor_plugin.h b/editor/plugins/node_3d_editor_plugin.h
index 5f0ba1921b..2c3b15cfc8 100644
--- a/editor/plugins/node_3d_editor_plugin.h
+++ b/editor/plugins/node_3d_editor_plugin.h
@@ -219,6 +219,7 @@ class Node3DEditorViewport : public Control {
VIEW_DISPLAY_DEBUG_SSAO,
VIEW_DISPLAY_DEBUG_ROUGHNESS_LIMITER,
VIEW_DISPLAY_DEBUG_PSSM_SPLITS,
+ VIEW_DISPLAY_DEBUG_DECAL_ATLAS,
VIEW_LOCK_ROTATION,
VIEW_CINEMATIC_PREVIEW,
VIEW_AUTO_ORTHOGONAL,
diff --git a/misc/travis/android-tools-linux.sh b/misc/travis/android-tools-linux.sh
index 4eeb54412c..6114551861 100755
--- a/misc/travis/android-tools-linux.sh
+++ b/misc/travis/android-tools-linux.sh
@@ -76,7 +76,7 @@ yes | $ANDROID_SDK_DIR/tools/bin/sdkmanager --licenses > /dev/null
echo "Installing: Android Build and Platform Tools ..."
yes | $ANDROID_SDK_DIR/tools/bin/sdkmanager 'tools' > /dev/null
yes | $ANDROID_SDK_DIR/tools/bin/sdkmanager 'platform-tools' > /dev/null
-yes | $ANDROID_SDK_DIR/tools/bin/sdkmanager 'build-tools;28.0.3' > /dev/null
+yes | $ANDROID_SDK_DIR/tools/bin/sdkmanager 'build-tools;29.0.3' > /dev/null
echo
EXPORT_VAL="export ANDROID_HOME=$ANDROID_SDK_PATH"
diff --git a/modules/gdscript/gdscript_tokenizer.h b/modules/gdscript/gdscript_tokenizer.h
index 1b432ae8c1..180ec3c77e 100644
--- a/modules/gdscript/gdscript_tokenizer.h
+++ b/modules/gdscript/gdscript_tokenizer.h
@@ -32,6 +32,7 @@
#define GDSCRIPT_TOKENIZER_H
#include "core/pair.h"
+#include "core/set.h"
#include "core/string_name.h"
#include "core/ustring.h"
#include "core/variant.h"
diff --git a/modules/visual_script/visual_script_builtin_funcs.cpp b/modules/visual_script/visual_script_builtin_funcs.cpp
index 2b5a17c36f..bb291b1cb6 100644
--- a/modules/visual_script/visual_script_builtin_funcs.cpp
+++ b/modules/visual_script/visual_script_builtin_funcs.cpp
@@ -506,7 +506,9 @@ PropertyInfo VisualScriptBuiltinFunc::get_output_value_port_info(int p_idx) cons
case MATH_CEIL: {
t = Variant::FLOAT;
} break;
- case MATH_POSMOD:
+ case MATH_POSMOD: {
+ t = Variant::INT;
+ } break;
case MATH_ROUND: {
t = Variant::FLOAT;
} break;
diff --git a/platform/android/java/app/config.gradle b/platform/android/java/app/config.gradle
index 96cfd272f2..aa98194a10 100644
--- a/platform/android/java/app/config.gradle
+++ b/platform/android/java/app/config.gradle
@@ -3,7 +3,7 @@ ext.versions = [
compileSdk : 29,
minSdk : 18,
targetSdk : 29,
- buildTools : '29.0.1',
+ buildTools : '29.0.3',
supportCoreUtils : '28.0.0',
kotlinVersion : '1.3.61',
v4Support : '28.0.0'
diff --git a/platform/android/java/lib/build.gradle b/platform/android/java/lib/build.gradle
index c69e19fbfa..6bb438c249 100644
--- a/platform/android/java/lib/build.gradle
+++ b/platform/android/java/lib/build.gradle
@@ -12,7 +12,6 @@ def pathToRootDir = "../../../../"
android {
compileSdkVersion versions.compileSdk
buildToolsVersion versions.buildTools
- useLibrary 'org.apache.http.legacy'
defaultConfig {
minSdkVersion versions.minSdk
diff --git a/platform/android/java/plugins/godotpayment/build.gradle b/platform/android/java/plugins/godotpayment/build.gradle
index 4f376c4587..ffab86e26e 100644
--- a/platform/android/java/plugins/godotpayment/build.gradle
+++ b/platform/android/java/plugins/godotpayment/build.gradle
@@ -3,6 +3,7 @@ apply plugin: 'com.android.library'
android {
compileSdkVersion versions.compileSdk
buildToolsVersion versions.buildTools
+ useLibrary 'org.apache.http.legacy'
defaultConfig {
minSdkVersion versions.minSdk
diff --git a/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/ValidateTask.java b/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/ValidateTask.java
index 179cc08ed1..d42ded0c9b 100644
--- a/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/ValidateTask.java
+++ b/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/ValidateTask.java
@@ -34,8 +34,8 @@ import android.app.Activity;
import android.app.ProgressDialog;
import android.os.AsyncTask;
import java.lang.ref.WeakReference;
-import org.godotengine.godot.utils.HttpRequester;
-import org.godotengine.godot.utils.RequestParams;
+import org.godotengine.godot.plugin.payment.utils.HttpRequester;
+import org.godotengine.godot.plugin.payment.utils.RequestParams;
import org.json.JSONException;
import org.json.JSONObject;
diff --git a/platform/android/java/lib/src/org/godotengine/godot/utils/CustomSSLSocketFactory.java b/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/utils/CustomSSLSocketFactory.java
index c78e8c1c66..9571769cd3 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/utils/CustomSSLSocketFactory.java
+++ b/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/utils/CustomSSLSocketFactory.java
@@ -28,7 +28,8 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-package org.godotengine.godot.utils;
+package org.godotengine.godot.plugin.payment.utils;
+
import java.io.IOException;
import java.net.Socket;
import java.net.UnknownHostException;
diff --git a/platform/android/java/lib/src/org/godotengine/godot/utils/HttpRequester.java b/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/utils/HttpRequester.java
index 68f9a83597..dcb983201e 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/utils/HttpRequester.java
+++ b/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/utils/HttpRequester.java
@@ -28,7 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-package org.godotengine.godot.utils;
+package org.godotengine.godot.plugin.payment.utils;
import android.content.Context;
import android.content.SharedPreferences;
@@ -61,6 +61,7 @@ import org.apache.http.params.HttpParams;
import org.apache.http.params.HttpProtocolParams;
import org.apache.http.protocol.HTTP;
import org.apache.http.util.EntityUtils;
+import org.godotengine.godot.utils.Crypt;
/**
*
diff --git a/platform/android/java/lib/src/org/godotengine/godot/utils/RequestParams.java b/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/utils/RequestParams.java
index 25fa10647d..4be8b37473 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/utils/RequestParams.java
+++ b/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/utils/RequestParams.java
@@ -28,10 +28,9 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-package org.godotengine.godot.utils;
+package org.godotengine.godot.plugin.payment.utils;
import java.util.ArrayList;
-import java.util.Date;
import java.util.HashMap;
import java.util.List;
import org.apache.http.NameValuePair;
diff --git a/platform/linuxbsd/display_server_x11.cpp b/platform/linuxbsd/display_server_x11.cpp
index 6049dbf4d6..78ddef5ff6 100644
--- a/platform/linuxbsd/display_server_x11.cpp
+++ b/platform/linuxbsd/display_server_x11.cpp
@@ -1471,8 +1471,11 @@ DisplayServer::WindowMode DisplayServerX11::window_get_mode(WindowID p_window) c
if (result == Success && data) {
long *state = (long *)data;
- if (state[0] == WM_IconicState)
+ if (state[0] == WM_IconicState) {
+ XFree(data);
return WINDOW_MODE_MINIMIZED;
+ }
+ XFree(data);
}
}
diff --git a/scene/3d/decal.cpp b/scene/3d/decal.cpp
new file mode 100644
index 0000000000..4c824aedc4
--- /dev/null
+++ b/scene/3d/decal.cpp
@@ -0,0 +1,235 @@
+/*************************************************************************/
+/* decal.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 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 "decal.h"
+
+void Decal::set_extents(const Vector3 &p_extents) {
+ extents = p_extents;
+ RS::get_singleton()->decal_set_extents(decal, p_extents);
+ update_gizmo();
+ _change_notify("extents");
+}
+
+Vector3 Decal::get_extents() const {
+ return extents;
+}
+
+void Decal::set_texture(DecalTexture p_type, const Ref<Texture2D> &p_texture) {
+ ERR_FAIL_INDEX(p_type, TEXTURE_MAX);
+ textures[p_type] = p_texture;
+ RID texture_rid = p_texture.is_valid() ? p_texture->get_rid() : RID();
+ RS::get_singleton()->decal_set_texture(decal, RS::DecalTexture(p_type), texture_rid);
+}
+Ref<Texture2D> Decal::get_texture(DecalTexture p_type) const {
+ ERR_FAIL_INDEX_V(p_type, TEXTURE_MAX, Ref<Texture2D>());
+ return textures[p_type];
+}
+
+void Decal::set_emission_energy(float p_energy) {
+ emission_energy = p_energy;
+ RS::get_singleton()->decal_set_emission_energy(decal, emission_energy);
+}
+float Decal::get_emission_energy() const {
+ return emission_energy;
+}
+
+void Decal::set_albedo_mix(float p_mix) {
+ albedo_mix = p_mix;
+ RS::get_singleton()->decal_set_albedo_mix(decal, albedo_mix);
+}
+float Decal::get_albedo_mix() const {
+ return albedo_mix;
+}
+
+void Decal::set_upper_fade(float p_fade) {
+ upper_fade = p_fade;
+ RS::get_singleton()->decal_set_fade(decal, upper_fade, lower_fade);
+}
+float Decal::get_upper_fade() const {
+ return upper_fade;
+}
+
+void Decal::set_lower_fade(float p_fade) {
+ lower_fade = p_fade;
+ RS::get_singleton()->decal_set_fade(decal, upper_fade, lower_fade);
+}
+float Decal::get_lower_fade() const {
+ return lower_fade;
+}
+
+void Decal::set_normal_fade(float p_fade) {
+ normal_fade = p_fade;
+ RS::get_singleton()->decal_set_normal_fade(decal, normal_fade);
+}
+float Decal::get_normal_fade() const {
+ return normal_fade;
+}
+
+void Decal::set_modulate(Color p_modulate) {
+ modulate = p_modulate;
+ RS::get_singleton()->decal_set_modulate(decal, p_modulate);
+}
+
+Color Decal::get_modulate() const {
+ return modulate;
+}
+
+void Decal::set_enable_distance_fade(bool p_enable) {
+ distance_fade_enabled = p_enable;
+ RS::get_singleton()->decal_set_distance_fade(decal, distance_fade_enabled, distance_fade_begin, distance_fade_length);
+}
+bool Decal::is_distance_fade_enabled() const {
+ return distance_fade_enabled;
+}
+
+void Decal::set_distance_fade_begin(float p_distance) {
+ distance_fade_begin = p_distance;
+ RS::get_singleton()->decal_set_distance_fade(decal, distance_fade_enabled, distance_fade_begin, distance_fade_length);
+}
+float Decal::get_distance_fade_begin() const {
+ return distance_fade_begin;
+}
+
+void Decal::set_distance_fade_length(float p_length) {
+ distance_fade_length = p_length;
+ RS::get_singleton()->decal_set_distance_fade(decal, distance_fade_enabled, distance_fade_begin, distance_fade_length);
+}
+float Decal::get_distance_fade_length() const {
+ return distance_fade_length;
+}
+
+void Decal::set_cull_mask(uint32_t p_layers) {
+ cull_mask = p_layers;
+ RS::get_singleton()->decal_set_cull_mask(decal, cull_mask);
+}
+uint32_t Decal::get_cull_mask() const {
+ return cull_mask;
+}
+
+AABB Decal::get_aabb() const {
+
+ AABB aabb;
+ aabb.position = -extents;
+ aabb.size = extents * 2.0;
+ return aabb;
+}
+Vector<Face3> Decal::get_faces(uint32_t p_usage_flags) const {
+
+ return Vector<Face3>();
+}
+
+void Decal::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("set_extents", "extents"), &Decal::set_extents);
+ ClassDB::bind_method(D_METHOD("get_extents"), &Decal::get_extents);
+
+ ClassDB::bind_method(D_METHOD("set_texture", "type", "texture"), &Decal::set_texture);
+ ClassDB::bind_method(D_METHOD("get_texture", "type"), &Decal::get_texture);
+
+ ClassDB::bind_method(D_METHOD("set_emission_energy", "energy"), &Decal::set_emission_energy);
+ ClassDB::bind_method(D_METHOD("get_emission_energy"), &Decal::get_emission_energy);
+
+ ClassDB::bind_method(D_METHOD("set_albedo_mix", "energy"), &Decal::set_albedo_mix);
+ ClassDB::bind_method(D_METHOD("get_albedo_mix"), &Decal::get_albedo_mix);
+
+ ClassDB::bind_method(D_METHOD("set_modulate", "color"), &Decal::set_modulate);
+ ClassDB::bind_method(D_METHOD("get_modulate"), &Decal::get_modulate);
+
+ ClassDB::bind_method(D_METHOD("set_upper_fade", "fade"), &Decal::set_upper_fade);
+ ClassDB::bind_method(D_METHOD("get_upper_fade"), &Decal::get_upper_fade);
+
+ ClassDB::bind_method(D_METHOD("set_lower_fade", "fade"), &Decal::set_lower_fade);
+ ClassDB::bind_method(D_METHOD("get_lower_fade"), &Decal::get_lower_fade);
+
+ ClassDB::bind_method(D_METHOD("set_normal_fade", "fade"), &Decal::set_normal_fade);
+ ClassDB::bind_method(D_METHOD("get_normal_fade"), &Decal::get_normal_fade);
+
+ ClassDB::bind_method(D_METHOD("set_enable_distance_fade", "enable"), &Decal::set_enable_distance_fade);
+ ClassDB::bind_method(D_METHOD("is_distance_fade_enabled"), &Decal::is_distance_fade_enabled);
+
+ ClassDB::bind_method(D_METHOD("set_distance_fade_begin", "distance"), &Decal::set_distance_fade_begin);
+ ClassDB::bind_method(D_METHOD("get_distance_fade_begin"), &Decal::get_distance_fade_begin);
+
+ ClassDB::bind_method(D_METHOD("set_distance_fade_length", "distance"), &Decal::set_distance_fade_length);
+ ClassDB::bind_method(D_METHOD("get_distance_fade_length"), &Decal::get_distance_fade_length);
+
+ ClassDB::bind_method(D_METHOD("set_cull_mask", "mask"), &Decal::set_cull_mask);
+ ClassDB::bind_method(D_METHOD("get_cull_mask"), &Decal::get_cull_mask);
+
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "extents", PROPERTY_HINT_RANGE, "0,1024,0.001,or_greater"), "set_extents", "get_extents");
+ ADD_GROUP("Textures", "texture_");
+ ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "texture_albedo", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_texture", "get_texture", TEXTURE_ALBEDO);
+ ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "texture_normal", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_texture", "get_texture", TEXTURE_NORMAL);
+ ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "texture_orm", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_texture", "get_texture", TEXTURE_ORM);
+ ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "texture_emission", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_texture", "get_texture", TEXTURE_EMISSION);
+ ADD_GROUP("Parameters", "");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "emission_energy", PROPERTY_HINT_RANGE, "0,128,0.01"), "set_emission_energy", "get_emission_energy");
+ ADD_PROPERTY(PropertyInfo(Variant::COLOR, "modulate"), "set_modulate", "get_modulate");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "albedo_mix", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_albedo_mix", "get_albedo_mix");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "normal_fade", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_normal_fade", "get_normal_fade");
+ ADD_GROUP("Vertical Fade", "");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "upper_fade", PROPERTY_HINT_EXP_EASING, "attenuation"), "set_upper_fade", "get_upper_fade");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "lower_fade", PROPERTY_HINT_EXP_EASING, "attenuation"), "set_lower_fade", "get_lower_fade");
+ ADD_GROUP("Distance Fade", "distance_fade_");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "distance_fade_enabled"), "set_enable_distance_fade", "is_distance_fade_enabled");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "distance_fade_begin"), "set_distance_fade_begin", "get_distance_fade_begin");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "distance_fade_length"), "set_distance_fade_length", "get_distance_fade_length");
+ ADD_GROUP("Cull Mask", "");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "cull_mask", PROPERTY_HINT_LAYERS_3D_RENDER), "set_cull_mask", "get_cull_mask");
+
+ BIND_ENUM_CONSTANT(TEXTURE_ALBEDO);
+ BIND_ENUM_CONSTANT(TEXTURE_NORMAL);
+ BIND_ENUM_CONSTANT(TEXTURE_ORM);
+ BIND_ENUM_CONSTANT(TEXTURE_EMISSION);
+ BIND_ENUM_CONSTANT(TEXTURE_MAX);
+}
+
+Decal::Decal() {
+
+ extents = Vector3(1, 1, 1);
+ emission_energy = 1.0;
+ modulate = Color(1, 1, 1, 1);
+ albedo_mix = 1.0;
+ cull_mask = (1 << 20) - 1;
+ upper_fade = 0.3;
+ lower_fade = 0.3;
+ normal_fade = 0;
+ distance_fade_enabled = false;
+ distance_fade_begin = 10;
+ distance_fade_length = 1;
+
+ decal = RenderingServer::get_singleton()->decal_create();
+ RS::get_singleton()->instance_set_base(get_instance(), decal);
+}
+
+Decal::~Decal() {
+
+ RS::get_singleton()->free(decal);
+}
diff --git a/scene/3d/decal.h b/scene/3d/decal.h
new file mode 100644
index 0000000000..665444829d
--- /dev/null
+++ b/scene/3d/decal.h
@@ -0,0 +1,114 @@
+/*************************************************************************/
+/* decal.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 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 DECAL_H
+#define DECAL_H
+
+#include "scene/3d/visual_instance_3d.h"
+#include "scene/resources/texture.h"
+#include "servers/rendering_server.h"
+
+class Decal : public VisualInstance3D {
+ GDCLASS(Decal, VisualInstance3D);
+
+public:
+ enum DecalTexture {
+ TEXTURE_ALBEDO,
+ TEXTURE_NORMAL,
+ TEXTURE_ORM,
+ TEXTURE_EMISSION,
+ TEXTURE_MAX
+ };
+
+private:
+ RID decal;
+ Vector3 extents;
+ Ref<Texture2D> textures[TEXTURE_MAX];
+ float emission_energy;
+ float albedo_mix;
+ Color modulate;
+ uint32_t cull_mask;
+ float normal_fade;
+ float upper_fade;
+ float lower_fade;
+ bool distance_fade_enabled;
+ float distance_fade_begin;
+ float distance_fade_length;
+
+protected:
+ static void _bind_methods();
+
+public:
+ void set_extents(const Vector3 &p_extents);
+ Vector3 get_extents() const;
+
+ void set_texture(DecalTexture p_type, const Ref<Texture2D> &p_texture);
+ Ref<Texture2D> get_texture(DecalTexture p_type) const;
+
+ void set_emission_energy(float p_energy);
+ float get_emission_energy() const;
+
+ void set_albedo_mix(float p_mix);
+ float get_albedo_mix() const;
+
+ void set_modulate(Color p_modulate);
+ Color get_modulate() const;
+
+ void set_upper_fade(float p_energy);
+ float get_upper_fade() const;
+
+ void set_lower_fade(float p_fade);
+ float get_lower_fade() const;
+
+ void set_normal_fade(float p_fade);
+ float get_normal_fade() const;
+
+ void set_enable_distance_fade(bool p_enable);
+ bool is_distance_fade_enabled() const;
+
+ void set_distance_fade_begin(float p_distance);
+ float get_distance_fade_begin() const;
+
+ void set_distance_fade_length(float p_length);
+ float get_distance_fade_length() const;
+
+ void set_cull_mask(uint32_t p_layers);
+ uint32_t get_cull_mask() const;
+
+ virtual AABB get_aabb() const;
+ virtual Vector<Face3> get_faces(uint32_t p_usage_flags) const;
+
+ Decal();
+ ~Decal();
+};
+
+VARIANT_ENUM_CAST(Decal::DecalTexture);
+
+#endif // DECAL_H
diff --git a/scene/3d/light_3d.cpp b/scene/3d/light_3d.cpp
index 81a5c6f4f3..9928246d2b 100644
--- a/scene/3d/light_3d.cpp
+++ b/scene/3d/light_3d.cpp
@@ -70,7 +70,7 @@ void Light3D::set_shadow(bool p_enable) {
shadow = p_enable;
RS::get_singleton()->light_set_shadow(light, p_enable);
- if (type == RenderingServer::LIGHT_SPOT) {
+ if (type == RenderingServer::LIGHT_SPOT || type == RenderingServer::LIGHT_OMNI) {
update_configuration_warning();
}
}
@@ -166,6 +166,18 @@ Light3D::BakeMode Light3D::get_bake_mode() const {
return bake_mode;
}
+void Light3D::set_projector(const Ref<Texture2D> &p_texture) {
+
+ projector = p_texture;
+ RID tex_id = projector.is_valid() ? projector->get_rid() : RID();
+ RS::get_singleton()->light_set_projector(light, tex_id);
+ update_configuration_warning();
+}
+
+Ref<Texture2D> Light3D::get_projector() const {
+ return projector;
+}
+
void Light3D::_update_visibility() {
if (!is_inside_tree())
@@ -221,6 +233,10 @@ void Light3D::_validate_property(PropertyInfo &property) const {
property.usage = 0;
}
+ if (get_light_type() == RS::LIGHT_DIRECTIONAL && property.name == "light_projector") {
+ property.usage = 0;
+ }
+
if (get_light_type() != RS::LIGHT_DIRECTIONAL && property.name == "light_angular_distance") {
property.usage = 0;
}
@@ -255,10 +271,14 @@ void Light3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_bake_mode", "bake_mode"), &Light3D::set_bake_mode);
ClassDB::bind_method(D_METHOD("get_bake_mode"), &Light3D::get_bake_mode);
+ ClassDB::bind_method(D_METHOD("set_projector", "projector"), &Light3D::set_projector);
+ ClassDB::bind_method(D_METHOD("get_projector"), &Light3D::get_projector);
+
ADD_GROUP("Light", "light_");
ADD_PROPERTY(PropertyInfo(Variant::COLOR, "light_color", PROPERTY_HINT_COLOR_NO_ALPHA), "set_color", "get_color");
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "light_energy", PROPERTY_HINT_RANGE, "0,16,0.01,or_greater"), "set_param", "get_param", PARAM_ENERGY);
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "light_indirect_energy", PROPERTY_HINT_RANGE, "0,16,0.01,or_greater"), "set_param", "get_param", PARAM_INDIRECT_ENERGY);
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "light_projector", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_projector", "get_projector");
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "light_size", PROPERTY_HINT_RANGE, "0,64,0.01,or_greater"), "set_param", "get_param", PARAM_SIZE);
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "light_angular_distance", PROPERTY_HINT_RANGE, "0,90,0.01"), "set_param", "get_param", PARAM_SIZE);
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "light_negative"), "set_negative", "is_negative");
@@ -444,6 +464,19 @@ OmniLight3D::ShadowMode OmniLight3D::get_shadow_mode() const {
return shadow_mode;
}
+String OmniLight3D::get_configuration_warning() const {
+ String warning = Light3D::get_configuration_warning();
+
+ if (!has_shadow() && get_projector().is_valid()) {
+ if (warning != String()) {
+ warning += "\n\n";
+ }
+ warning += TTR("Projector texture only works with shadows active.");
+ }
+
+ return warning;
+}
+
void OmniLight3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_shadow_mode", "mode"), &OmniLight3D::set_shadow_mode);
@@ -475,6 +508,13 @@ String SpotLight3D::get_configuration_warning() const {
warning += TTR("A SpotLight3D with an angle wider than 90 degrees cannot cast shadows.");
}
+ if (!has_shadow() && get_projector().is_valid()) {
+ if (warning != String()) {
+ warning += "\n\n";
+ }
+ warning += TTR("Projector texture only works with shadows active.");
+ }
+
return warning;
}
diff --git a/scene/3d/light_3d.h b/scene/3d/light_3d.h
index 21e14f0e8b..6e78217342 100644
--- a/scene/3d/light_3d.h
+++ b/scene/3d/light_3d.h
@@ -81,6 +81,7 @@ private:
bool editor_only;
void _update_visibility();
BakeMode bake_mode;
+ Ref<Texture2D> projector;
// bind helpers
@@ -125,6 +126,9 @@ public:
void set_bake_mode(BakeMode p_mode);
BakeMode get_bake_mode() const;
+ void set_projector(const Ref<Texture2D> &p_texture);
+ Ref<Texture2D> get_projector() const;
+
virtual AABB get_aabb() const;
virtual Vector<Face3> get_faces(uint32_t p_usage_flags) const;
@@ -196,6 +200,8 @@ public:
void set_shadow_mode(ShadowMode p_mode);
ShadowMode get_shadow_mode() const;
+ virtual String get_configuration_warning() const;
+
OmniLight3D();
};
diff --git a/scene/3d/physics_body_3d.cpp b/scene/3d/physics_body_3d.cpp
index 2b6eb8ac8a..72d1762ab5 100644
--- a/scene/3d/physics_body_3d.cpp
+++ b/scene/3d/physics_body_3d.cpp
@@ -2134,6 +2134,10 @@ void PhysicalBone3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_joint_offset", "offset"), &PhysicalBone3D::set_joint_offset);
ClassDB::bind_method(D_METHOD("get_joint_offset"), &PhysicalBone3D::get_joint_offset);
+ ClassDB::bind_method(D_METHOD("set_joint_rotation", "euler"), &PhysicalBone3D::set_joint_rotation);
+ ClassDB::bind_method(D_METHOD("get_joint_rotation"), &PhysicalBone3D::get_joint_rotation);
+ ClassDB::bind_method(D_METHOD("set_joint_rotation_degrees", "euler_degrees"), &PhysicalBone3D::set_joint_rotation_degrees);
+ ClassDB::bind_method(D_METHOD("get_joint_rotation_degrees"), &PhysicalBone3D::get_joint_rotation_degrees);
ClassDB::bind_method(D_METHOD("set_body_offset", "offset"), &PhysicalBone3D::set_body_offset);
ClassDB::bind_method(D_METHOD("get_body_offset"), &PhysicalBone3D::get_body_offset);
@@ -2159,9 +2163,23 @@ void PhysicalBone3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_gravity_scale", "gravity_scale"), &PhysicalBone3D::set_gravity_scale);
ClassDB::bind_method(D_METHOD("get_gravity_scale"), &PhysicalBone3D::get_gravity_scale);
+ ClassDB::bind_method(D_METHOD("set_linear_damp", "linear_damp"), &PhysicalBone3D::set_linear_damp);
+ ClassDB::bind_method(D_METHOD("get_linear_damp"), &PhysicalBone3D::get_linear_damp);
+
+ ClassDB::bind_method(D_METHOD("set_angular_damp", "angular_damp"), &PhysicalBone3D::set_angular_damp);
+ ClassDB::bind_method(D_METHOD("get_angular_damp"), &PhysicalBone3D::get_angular_damp);
+
+ ClassDB::bind_method(D_METHOD("set_can_sleep", "able_to_sleep"), &PhysicalBone3D::set_can_sleep);
+ ClassDB::bind_method(D_METHOD("is_able_to_sleep"), &PhysicalBone3D::is_able_to_sleep);
+
+ ClassDB::bind_method(D_METHOD("set_axis_lock", "axis", "lock"), &PhysicalBone3D::set_axis_lock);
+ ClassDB::bind_method(D_METHOD("get_axis_lock", "axis"), &PhysicalBone3D::get_axis_lock);
+
ADD_GROUP("Joint", "joint_");
ADD_PROPERTY(PropertyInfo(Variant::INT, "joint_type", PROPERTY_HINT_ENUM, "None,PinJoint,ConeJoint,HingeJoint,SliderJoint,6DOFJoint"), "set_joint_type", "get_joint_type");
ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM, "joint_offset"), "set_joint_offset", "get_joint_offset");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "joint_rotation_degrees", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "set_joint_rotation_degrees", "get_joint_rotation_degrees");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "joint_rotation", PROPERTY_HINT_NONE, "", 0), "set_joint_rotation", "get_joint_rotation");
ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM, "body_offset"), "set_body_offset", "get_body_offset");
@@ -2170,6 +2188,17 @@ void PhysicalBone3D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "friction", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_friction", "get_friction");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "bounce", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_bounce", "get_bounce");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "gravity_scale", PROPERTY_HINT_RANGE, "-10,10,0.01"), "set_gravity_scale", "get_gravity_scale");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "linear_damp", PROPERTY_HINT_RANGE, "-1,100,0.001,or_greater"), "set_linear_damp", "get_linear_damp");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "angular_damp", PROPERTY_HINT_RANGE, "-1,100,0.001,or_greater"), "set_angular_damp", "get_angular_damp");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "can_sleep"), "set_can_sleep", "is_able_to_sleep");
+
+ ADD_GROUP("Axis Lock", "axis_lock_");
+ ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "axis_lock_linear_x"), "set_axis_lock", "get_axis_lock", PhysicsServer3D::BODY_AXIS_LINEAR_X);
+ ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "axis_lock_linear_y"), "set_axis_lock", "get_axis_lock", PhysicsServer3D::BODY_AXIS_LINEAR_Y);
+ ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "axis_lock_linear_z"), "set_axis_lock", "get_axis_lock", PhysicsServer3D::BODY_AXIS_LINEAR_Z);
+ ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "axis_lock_angular_x"), "set_axis_lock", "get_axis_lock", PhysicsServer3D::BODY_AXIS_ANGULAR_X);
+ ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "axis_lock_angular_y"), "set_axis_lock", "get_axis_lock", PhysicsServer3D::BODY_AXIS_ANGULAR_Y);
+ ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "axis_lock_angular_z"), "set_axis_lock", "get_axis_lock", PhysicsServer3D::BODY_AXIS_ANGULAR_Z);
BIND_ENUM_CONSTANT(JOINT_TYPE_NONE);
BIND_ENUM_CONSTANT(JOINT_TYPE_PIN);
@@ -2187,6 +2216,19 @@ Skeleton3D *PhysicalBone3D::find_skeleton_parent(Node *p_parent) {
return s ? s : find_skeleton_parent(p_parent->get_parent());
}
+void PhysicalBone3D::_update_joint_offset() {
+ _fix_joint_offset();
+
+ set_ignore_transform_notification(true);
+ reset_to_rest_position();
+ set_ignore_transform_notification(false);
+
+#ifdef TOOLS_ENABLED
+ if (get_gizmo().is_valid())
+ get_gizmo()->redraw();
+#endif
+}
+
void PhysicalBone3D::_fix_joint_offset() {
// Clamp joint origin to bone origin
if (parent_skeleton) {
@@ -2370,16 +2412,31 @@ PhysicalBone3D::JointType PhysicalBone3D::get_joint_type() const {
void PhysicalBone3D::set_joint_offset(const Transform &p_offset) {
joint_offset = p_offset;
- _fix_joint_offset();
+ _update_joint_offset();
+ _change_notify("joint_rotation_degrees");
+}
- set_ignore_transform_notification(true);
- reset_to_rest_position();
- set_ignore_transform_notification(false);
+const Transform &PhysicalBone3D::get_joint_offset() const {
+ return joint_offset;
+}
-#ifdef TOOLS_ENABLED
- if (get_gizmo().is_valid())
- get_gizmo()->redraw();
-#endif
+void PhysicalBone3D::set_joint_rotation(const Vector3 &p_euler_rad) {
+ joint_offset.basis.set_euler_scale(p_euler_rad, joint_offset.basis.get_scale());
+
+ _update_joint_offset();
+ _change_notify("joint_offset");
+}
+
+Vector3 PhysicalBone3D::get_joint_rotation() const {
+ return joint_offset.basis.get_rotation();
+}
+
+void PhysicalBone3D::set_joint_rotation_degrees(const Vector3 &p_euler_deg) {
+ set_joint_rotation(p_euler_deg * Math_PI / 180.0);
+}
+
+Vector3 PhysicalBone3D::get_joint_rotation_degrees() const {
+ return get_joint_rotation() * 180.0 / Math_PI;
}
const Transform &PhysicalBone3D::get_body_offset() const {
@@ -2390,20 +2447,7 @@ void PhysicalBone3D::set_body_offset(const Transform &p_offset) {
body_offset = p_offset;
body_offset_inverse = body_offset.affine_inverse();
- _fix_joint_offset();
-
- set_ignore_transform_notification(true);
- reset_to_rest_position();
- set_ignore_transform_notification(false);
-
-#ifdef TOOLS_ENABLED
- if (get_gizmo().is_valid())
- get_gizmo()->redraw();
-#endif
-}
-
-const Transform &PhysicalBone3D::get_joint_offset() const {
- return joint_offset;
+ _update_joint_offset();
}
void PhysicalBone3D::set_simulate_physics(bool p_simulate) {
@@ -2496,6 +2540,43 @@ real_t PhysicalBone3D::get_gravity_scale() const {
return gravity_scale;
}
+void PhysicalBone3D::set_linear_damp(real_t p_linear_damp) {
+ ERR_FAIL_COND(p_linear_damp < -1);
+ linear_damp = p_linear_damp;
+ PhysicsServer3D::get_singleton()->body_set_param(get_rid(), PhysicsServer3D::BODY_PARAM_LINEAR_DAMP, linear_damp);
+}
+
+real_t PhysicalBone3D::get_linear_damp() const {
+ return linear_damp;
+}
+
+void PhysicalBone3D::set_angular_damp(real_t p_angular_damp) {
+ ERR_FAIL_COND(p_angular_damp < -1);
+ angular_damp = p_angular_damp;
+ PhysicsServer3D::get_singleton()->body_set_param(get_rid(), PhysicsServer3D::BODY_PARAM_ANGULAR_DAMP, angular_damp);
+}
+
+real_t PhysicalBone3D::get_angular_damp() const {
+ return angular_damp;
+}
+
+void PhysicalBone3D::set_can_sleep(bool p_active) {
+ can_sleep = p_active;
+ PhysicsServer3D::get_singleton()->body_set_state(get_rid(), PhysicsServer3D::BODY_STATE_CAN_SLEEP, p_active);
+}
+
+bool PhysicalBone3D::is_able_to_sleep() const {
+ return can_sleep;
+}
+
+void PhysicalBone3D::set_axis_lock(PhysicsServer3D::BodyAxis p_axis, bool p_lock) {
+ PhysicsServer3D::get_singleton()->body_set_axis_lock(get_rid(), p_axis, p_lock);
+}
+
+bool PhysicalBone3D::get_axis_lock(PhysicsServer3D::BodyAxis p_axis) const {
+ return PhysicsServer3D::get_singleton()->body_is_axis_locked(get_rid(), p_axis);
+}
+
PhysicalBone3D::PhysicalBone3D() :
PhysicsBody3D(PhysicsServer3D::BODY_MODE_STATIC),
#ifdef TOOLS_ENABLED
@@ -2510,7 +2591,10 @@ PhysicalBone3D::PhysicalBone3D() :
bounce(0),
mass(1),
friction(1),
- gravity_scale(1) {
+ gravity_scale(1),
+ linear_damp(-1),
+ angular_damp(-1),
+ can_sleep(true) {
reset_physics_simulation_state();
}
diff --git a/scene/3d/physics_body_3d.h b/scene/3d/physics_body_3d.h
index bf7854b68d..2e71020233 100644
--- a/scene/3d/physics_body_3d.h
+++ b/scene/3d/physics_body_3d.h
@@ -562,6 +562,9 @@ private:
real_t mass;
real_t friction;
real_t gravity_scale;
+ real_t linear_damp;
+ real_t angular_damp;
+ bool can_sleep;
protected:
bool _set(const StringName &p_name, const Variant &p_value);
@@ -575,6 +578,7 @@ protected:
private:
static Skeleton3D *find_skeleton_parent(Node *p_parent);
+ void _update_joint_offset();
void _fix_joint_offset();
void _reload_joint();
@@ -599,6 +603,12 @@ public:
void set_joint_offset(const Transform &p_offset);
const Transform &get_joint_offset() const;
+ void set_joint_rotation(const Vector3 &p_euler_rad);
+ Vector3 get_joint_rotation() const;
+
+ void set_joint_rotation_degrees(const Vector3 &p_euler_deg);
+ Vector3 get_joint_rotation_degrees() const;
+
void set_body_offset(const Transform &p_offset);
const Transform &get_body_offset() const;
@@ -624,6 +634,18 @@ public:
void set_gravity_scale(real_t p_gravity_scale);
real_t get_gravity_scale() const;
+ void set_linear_damp(real_t p_linear_damp);
+ real_t get_linear_damp() const;
+
+ void set_angular_damp(real_t p_angular_damp);
+ real_t get_angular_damp() const;
+
+ void set_can_sleep(bool p_active);
+ bool is_able_to_sleep() const;
+
+ void set_axis_lock(PhysicsServer3D::BodyAxis p_axis, bool p_lock);
+ bool get_axis_lock(PhysicsServer3D::BodyAxis p_axis) const;
+
void apply_central_impulse(const Vector3 &p_impulse);
void apply_impulse(const Vector3 &p_pos, const Vector3 &p_impulse);
diff --git a/scene/3d/spring_arm_3d.cpp b/scene/3d/spring_arm_3d.cpp
index 281be3f7d3..a5460593c9 100644
--- a/scene/3d/spring_arm_3d.cpp
+++ b/scene/3d/spring_arm_3d.cpp
@@ -53,7 +53,7 @@ void SpringArm3D::_notification(int p_what) {
set_process_internal(false);
}
break;
- case NOTIFICATION_INTERNAL_PROCESS:
+ case NOTIFICATION_INTERNAL_PHYSICS_PROCESS:
process_spring();
break;
}
diff --git a/scene/gui/graph_edit.cpp b/scene/gui/graph_edit.cpp
index e37e93e2a9..0fe65462e4 100644
--- a/scene/gui/graph_edit.cpp
+++ b/scene/gui/graph_edit.cpp
@@ -852,7 +852,7 @@ void GraphEdit::_gui_input(const Ref<InputEvent> &p_ev) {
bool in_box = r.intersects(box_selecting_rect);
if (in_box)
- gn->set_selected(box_selection_mode_aditive);
+ gn->set_selected(box_selection_mode_additive);
else
gn->set_selected(previus_selected.find(gn) != nullptr);
}
@@ -951,8 +951,16 @@ void GraphEdit::_gui_input(const Ref<InputEvent> &p_ev) {
if (!gn->is_selected() && !InputFilter::get_singleton()->is_key_pressed(KEY_CONTROL)) {
for (int i = 0; i < get_child_count(); i++) {
GraphNode *o_gn = Object::cast_to<GraphNode>(get_child(i));
- if (o_gn)
- o_gn->set_selected(o_gn == gn);
+ if (o_gn) {
+ if (o_gn == gn) {
+ o_gn->set_selected(true);
+ } else {
+ if (o_gn->is_selected()) {
+ emit_signal("node_unselected", o_gn);
+ }
+ o_gn->set_selected(false);
+ }
+ }
}
}
@@ -974,7 +982,7 @@ void GraphEdit::_gui_input(const Ref<InputEvent> &p_ev) {
box_selecting = true;
box_selecting_from = get_local_mouse_position();
if (b->get_control()) {
- box_selection_mode_aditive = true;
+ box_selection_mode_additive = true;
previus_selected.clear();
for (int i = get_child_count() - 1; i >= 0; i--) {
@@ -985,7 +993,7 @@ void GraphEdit::_gui_input(const Ref<InputEvent> &p_ev) {
previus_selected.push_back(gn2);
}
} else if (b->get_shift()) {
- box_selection_mode_aditive = false;
+ box_selection_mode_additive = false;
previus_selected.clear();
for (int i = get_child_count() - 1; i >= 0; i--) {
@@ -996,14 +1004,16 @@ void GraphEdit::_gui_input(const Ref<InputEvent> &p_ev) {
previus_selected.push_back(gn2);
}
} else {
- box_selection_mode_aditive = true;
+ box_selection_mode_additive = true;
previus_selected.clear();
for (int i = get_child_count() - 1; i >= 0; i--) {
GraphNode *gn2 = Object::cast_to<GraphNode>(get_child(i));
if (!gn2)
continue;
-
+ if (gn2->is_selected()) {
+ emit_signal("node_unselected", gn2);
+ }
gn2->set_selected(false);
}
}
@@ -1311,6 +1321,7 @@ void GraphEdit::_bind_methods() {
ADD_SIGNAL(MethodInfo("copy_nodes_request"));
ADD_SIGNAL(MethodInfo("paste_nodes_request"));
ADD_SIGNAL(MethodInfo("node_selected", PropertyInfo(Variant::OBJECT, "node", PROPERTY_HINT_RESOURCE_TYPE, "Node")));
+ ADD_SIGNAL(MethodInfo("node_unselected", PropertyInfo(Variant::OBJECT, "node", PROPERTY_HINT_RESOURCE_TYPE, "Node")));
ADD_SIGNAL(MethodInfo("connection_to_empty", PropertyInfo(Variant::STRING_NAME, "from"), PropertyInfo(Variant::INT, "from_slot"), PropertyInfo(Variant::VECTOR2, "release_position")));
ADD_SIGNAL(MethodInfo("connection_from_empty", PropertyInfo(Variant::STRING_NAME, "to"), PropertyInfo(Variant::INT, "to_slot"), PropertyInfo(Variant::VECTOR2, "release_position")));
ADD_SIGNAL(MethodInfo("delete_nodes_request"));
diff --git a/scene/gui/graph_edit.h b/scene/gui/graph_edit.h
index 7f1d2699ba..f675f8c7f3 100644
--- a/scene/gui/graph_edit.h
+++ b/scene/gui/graph_edit.h
@@ -104,7 +104,7 @@ private:
float zoom;
bool box_selecting;
- bool box_selection_mode_aditive;
+ bool box_selection_mode_additive;
Point2 box_selecting_from;
Point2 box_selecting_to;
Rect2 box_selecting_rect;
diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp
index a3036da870..72b1a877c1 100644
--- a/scene/main/viewport.cpp
+++ b/scene/main/viewport.cpp
@@ -3517,6 +3517,7 @@ void Viewport::_bind_methods() {
BIND_ENUM_CONSTANT(DEBUG_DRAW_SSAO);
BIND_ENUM_CONSTANT(DEBUG_DRAW_ROUGHNESS_LIMITER);
BIND_ENUM_CONSTANT(DEBUG_DRAW_PSSM_SPLITS);
+ BIND_ENUM_CONSTANT(DEBUG_DRAW_DECAL_ATLAS);
BIND_ENUM_CONSTANT(MSAA_DISABLED);
BIND_ENUM_CONSTANT(MSAA_2X);
diff --git a/scene/main/viewport.h b/scene/main/viewport.h
index 0cd7f6fdaa..0cbc957307 100644
--- a/scene/main/viewport.h
+++ b/scene/main/viewport.h
@@ -141,7 +141,8 @@ public:
DEBUG_DRAW_SCENE_LUMINANCE,
DEBUG_DRAW_SSAO,
DEBUG_DRAW_ROUGHNESS_LIMITER,
- DEBUG_DRAW_PSSM_SPLITS
+ DEBUG_DRAW_PSSM_SPLITS,
+ DEBUG_DRAW_DECAL_ATLAS
};
enum DefaultCanvasItemTextureFilter {
diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp
index 035d26b3e4..ff49dbdc8f 100644
--- a/scene/register_scene_types.cpp
+++ b/scene/register_scene_types.cpp
@@ -186,6 +186,7 @@
#include "scene/3d/collision_polygon_3d.h"
#include "scene/3d/collision_shape_3d.h"
#include "scene/3d/cpu_particles_3d.h"
+#include "scene/3d/decal.h"
#include "scene/3d/gi_probe.h"
#include "scene/3d/gpu_particles_3d.h"
#include "scene/3d/immediate_geometry_3d.h"
@@ -424,6 +425,7 @@ void register_scene_types() {
ClassDB::register_class<OmniLight3D>();
ClassDB::register_class<SpotLight3D>();
ClassDB::register_class<ReflectionProbe>();
+ ClassDB::register_class<Decal>();
ClassDB::register_class<GIProbe>();
ClassDB::register_class<GIProbeData>();
//ClassDB::register_class<BakedLightmap>();
diff --git a/servers/rendering/rasterizer.h b/servers/rendering/rasterizer.h
index b28ec8e50f..5da9c2aeea 100644
--- a/servers/rendering/rasterizer.h
+++ b/servers/rendering/rasterizer.h
@@ -249,12 +249,15 @@ public:
virtual bool reflection_probe_instance_begin_render(RID p_instance, RID p_reflection_atlas) = 0;
virtual bool reflection_probe_instance_postprocess_step(RID p_instance) = 0;
+ virtual RID decal_instance_create(RID p_decal) = 0;
+ virtual void decal_instance_set_transform(RID p_decal, const Transform &p_transform) = 0;
+
virtual RID gi_probe_instance_create(RID p_gi_probe) = 0;
virtual void gi_probe_instance_set_transform_to_data(RID p_probe, const Transform &p_xform) = 0;
virtual bool gi_probe_needs_update(RID p_probe) const = 0;
virtual void gi_probe_update(RID p_probe, bool p_update_light_instances, const Vector<RID> &p_light_instances, int p_dynamic_object_count, InstanceBase **p_dynamic_objects) = 0;
- virtual void render_scene(RID p_render_buffers, const Transform &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_ortogonal, InstanceBase **p_cull_result, int p_cull_count, RID *p_light_cull_result, int p_light_cull_count, RID *p_reflection_probe_cull_result, int p_reflection_probe_cull_count, RID *p_gi_probe_cull_result, int p_gi_probe_cull_count, RID p_environment, RID p_camera_effects, RID p_shadow_atlas, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass) = 0;
+ virtual void render_scene(RID p_render_buffers, const Transform &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_ortogonal, InstanceBase **p_cull_result, int p_cull_count, RID *p_light_cull_result, int p_light_cull_count, RID *p_reflection_probe_cull_result, int p_reflection_probe_cull_count, RID *p_gi_probe_cull_result, int p_gi_probe_cull_count, RID *p_decal_cull_result, int p_decal_cull_count, RID p_environment, RID p_camera_effects, RID p_shadow_atlas, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass) = 0;
virtual void render_shadow(RID p_light, RID p_shadow_atlas, int p_pass, InstanceBase **p_cull_result, int p_cull_count) = 0;
virtual void render_material(const Transform &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_ortogonal, InstanceBase **p_cull_result, int p_cull_count, RID p_framebuffer, const Rect2i &p_region) = 0;
@@ -324,6 +327,9 @@ public:
virtual Size2 texture_size_with_proxy(RID p_proxy) = 0;
+ virtual void texture_add_to_decal_atlas(RID p_texture, bool p_panorama_to_dp = false) = 0;
+ virtual void texture_remove_from_decal_atlas(RID p_texture, bool p_panorama_to_dp = false) = 0;
+
/* SHADER API */
virtual RID shader_create() = 0;
@@ -504,6 +510,21 @@ public:
virtual void base_update_dependency(RID p_base, RasterizerScene::InstanceBase *p_instance) = 0;
virtual void skeleton_update_dependency(RID p_base, RasterizerScene::InstanceBase *p_instance) = 0;
+ /* DECAL API */
+
+ virtual RID decal_create() = 0;
+ virtual void decal_set_extents(RID p_decal, const Vector3 &p_extents) = 0;
+ virtual void decal_set_texture(RID p_decal, RS::DecalTexture p_type, RID p_texture) = 0;
+ virtual void decal_set_emission_energy(RID p_decal, float p_energy) = 0;
+ virtual void decal_set_albedo_mix(RID p_decal, float p_mix) = 0;
+ virtual void decal_set_modulate(RID p_decal, const Color &p_modulate) = 0;
+ virtual void decal_set_cull_mask(RID p_decal, uint32_t p_layers) = 0;
+ virtual void decal_set_distance_fade(RID p_decal, bool p_enabled, float p_begin, float p_length) = 0;
+ virtual void decal_set_fade(RID p_decal, float p_above, float p_below) = 0;
+ virtual void decal_set_normal_fade(RID p_decal, float p_fade) = 0;
+
+ virtual AABB decal_get_aabb(RID p_decal) const = 0;
+
/* GI PROBE API */
virtual RID gi_probe_create() = 0;
diff --git a/servers/rendering/rasterizer_rd/light_cluster_builder.cpp b/servers/rendering/rasterizer_rd/light_cluster_builder.cpp
index 943ef1c7fa..f75308a975 100644
--- a/servers/rendering/rasterizer_rd/light_cluster_builder.cpp
+++ b/servers/rendering/rasterizer_rd/light_cluster_builder.cpp
@@ -39,6 +39,7 @@ void LightClusterBuilder::begin(const Transform &p_view_transform, const CameraM
//reset counts
light_count = 0;
refprobe_count = 0;
+ decal_count = 0;
item_count = 0;
sort_id_count = 0;
}
diff --git a/servers/rendering/rasterizer_rd/light_cluster_builder.h b/servers/rendering/rasterizer_rd/light_cluster_builder.h
index 50a68e03cb..78288dc620 100644
--- a/servers/rendering/rasterizer_rd/light_cluster_builder.h
+++ b/servers/rendering/rasterizer_rd/light_cluster_builder.h
@@ -193,23 +193,25 @@ public:
refprobes = (OrientedBoxData *)memrealloc(refprobes, sizeof(OrientedBoxData) * refprobe_max);
}
+ Transform xform = view_xform * p_transform;
+
OrientedBoxData &rp = refprobes[refprobe_count];
- Vector3 origin = p_transform.origin;
+ Vector3 origin = xform.origin;
rp.position[0] = origin.x;
rp.position[1] = origin.y;
rp.position[2] = origin.z;
- Vector3 x_axis = p_transform.basis.get_axis(0) * p_half_extents.x;
+ Vector3 x_axis = xform.basis.get_axis(0) * p_half_extents.x;
rp.x_axis[0] = x_axis.x;
rp.x_axis[1] = x_axis.y;
rp.x_axis[2] = x_axis.z;
- Vector3 y_axis = p_transform.basis.get_axis(1) * p_half_extents.y;
+ Vector3 y_axis = xform.basis.get_axis(1) * p_half_extents.y;
rp.y_axis[0] = y_axis.x;
rp.y_axis[1] = y_axis.y;
rp.y_axis[2] = y_axis.z;
- Vector3 z_axis = p_transform.basis.get_axis(2) * p_half_extents.z;
+ Vector3 z_axis = xform.basis.get_axis(2) * p_half_extents.z;
rp.z_axis[0] = z_axis.x;
rp.z_axis[1] = z_axis.y;
rp.z_axis[2] = z_axis.z;
@@ -230,35 +232,37 @@ public:
refprobe_count++;
}
- _FORCE_INLINE_ void add_decal(const Transform &p_transform, const Vector2 &p_half_extents, float p_depth) {
+ _FORCE_INLINE_ void add_decal(const Transform &p_transform, const Vector3 &p_half_extents) {
if (unlikely(decal_count == decal_max)) {
decal_max = nearest_power_of_2_templated(decal_max + 1);
decals = (OrientedBoxData *)memrealloc(decals, sizeof(OrientedBoxData) * decal_max);
}
- OrientedBoxData &dc = decals[decal_count];
+ Transform xform = view_xform * p_transform;
- Vector3 z_axis = -p_transform.basis.get_axis(2) * p_depth * 0.5;
- dc.z_axis[0] = z_axis.x;
- dc.z_axis[1] = z_axis.y;
- dc.z_axis[2] = z_axis.z;
+ OrientedBoxData &dc = decals[decal_count];
- Vector3 origin = p_transform.origin - z_axis;
+ Vector3 origin = xform.origin;
dc.position[0] = origin.x;
dc.position[1] = origin.y;
dc.position[2] = origin.z;
- Vector3 x_axis = p_transform.basis.get_axis(0) * p_half_extents.x;
+ Vector3 x_axis = xform.basis.get_axis(0) * p_half_extents.x;
dc.x_axis[0] = x_axis.x;
dc.x_axis[1] = x_axis.y;
dc.x_axis[2] = x_axis.z;
- Vector3 y_axis = p_transform.basis.get_axis(1) * p_half_extents.y;
+ Vector3 y_axis = xform.basis.get_axis(1) * p_half_extents.y;
dc.y_axis[0] = y_axis.x;
dc.y_axis[1] = y_axis.y;
dc.y_axis[2] = y_axis.z;
+ Vector3 z_axis = xform.basis.get_axis(2) * p_half_extents.z;
+ dc.z_axis[0] = z_axis.x;
+ dc.z_axis[1] = z_axis.y;
+ dc.z_axis[2] = z_axis.z;
+
AABB aabb;
aabb.position = origin + x_axis + y_axis + z_axis;
diff --git a/servers/rendering/rasterizer_rd/rasterizer_effects_rd.cpp b/servers/rendering/rasterizer_rd/rasterizer_effects_rd.cpp
index 3da16ccb79..d469dd97ca 100644
--- a/servers/rendering/rasterizer_rd/rasterizer_effects_rd.cpp
+++ b/servers/rendering/rasterizer_rd/rasterizer_effects_rd.cpp
@@ -204,7 +204,29 @@ RID RasterizerEffectsRD::_get_compute_uniform_set_from_image_pair(RID p_texture1
return uniform_set;
}
-void RasterizerEffectsRD::copy_to_fb_rect(RID p_source_rd_texture, RID p_dest_framebuffer, const Rect2i &p_rect, bool p_flip_y, bool p_force_luminance) {
+void RasterizerEffectsRD::copy_to_atlas_fb(RID p_source_rd_texture, RID p_dest_framebuffer, const Rect2 &p_uv_rect, RD::DrawListID p_draw_list, bool p_flip_y, bool p_panorama) {
+
+ zeromem(&copy_to_fb.push_constant, sizeof(CopyToFbPushConstant));
+
+ copy_to_fb.push_constant.use_section = true;
+ copy_to_fb.push_constant.section[0] = p_uv_rect.position.x;
+ copy_to_fb.push_constant.section[1] = p_uv_rect.position.y;
+ copy_to_fb.push_constant.section[2] = p_uv_rect.size.x;
+ copy_to_fb.push_constant.section[3] = p_uv_rect.size.y;
+
+ if (p_flip_y) {
+ copy_to_fb.push_constant.flip_y = true;
+ }
+
+ RD::DrawListID draw_list = p_draw_list;
+ RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, copy_to_fb.pipelines[p_panorama ? COPY_TO_FB_COPY_PANORAMA_TO_DP : COPY_TO_FB_COPY].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(p_dest_framebuffer)));
+ RD::get_singleton()->draw_list_bind_uniform_set(draw_list, _get_uniform_set_from_texture(p_source_rd_texture), 0);
+ RD::get_singleton()->draw_list_bind_index_array(draw_list, index_array);
+ RD::get_singleton()->draw_list_set_push_constant(draw_list, &copy_to_fb.push_constant, sizeof(CopyToFbPushConstant));
+ RD::get_singleton()->draw_list_draw(draw_list, true);
+}
+
+void RasterizerEffectsRD::copy_to_fb_rect(RID p_source_rd_texture, RID p_dest_framebuffer, const Rect2i &p_rect, bool p_flip_y, bool p_force_luminance, bool p_alpha_to_zero) {
zeromem(&copy_to_fb.push_constant, sizeof(CopyToFbPushConstant));
if (p_flip_y) {
@@ -213,9 +235,12 @@ void RasterizerEffectsRD::copy_to_fb_rect(RID p_source_rd_texture, RID p_dest_fr
if (p_force_luminance) {
copy_to_fb.push_constant.force_luminance = true;
}
+ if (p_alpha_to_zero) {
+ copy_to_fb.push_constant.alpha_to_zero = true;
+ }
RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_dest_framebuffer, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD, Vector<Color>(), 1.0, 0, p_rect);
- RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, copy_to_fb.pipeline.get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(p_dest_framebuffer)));
+ RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, copy_to_fb.pipelines[COPY_TO_FB_COPY].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(p_dest_framebuffer)));
RD::get_singleton()->draw_list_bind_uniform_set(draw_list, _get_uniform_set_from_texture(p_source_rd_texture), 0);
RD::get_singleton()->draw_list_bind_index_array(draw_list, index_array);
RD::get_singleton()->draw_list_set_push_constant(draw_list, &copy_to_fb.push_constant, sizeof(CopyToFbPushConstant));
@@ -1213,6 +1238,7 @@ RasterizerEffectsRD::RasterizerEffectsRD() {
{
Vector<String> copy_modes;
copy_modes.push_back("\n");
+ copy_modes.push_back("\n#define MODE_PANORAMA_TO_DP\n");
copy_to_fb.shader.initialize(copy_modes);
@@ -1220,7 +1246,9 @@ RasterizerEffectsRD::RasterizerEffectsRD() {
//use additive
- copy_to_fb.pipeline.setup(copy_to_fb.shader.version_get_shader(copy_to_fb.shader_version, 0), RD::RENDER_PRIMITIVE_TRIANGLES, RD::PipelineRasterizationState(), RD::PipelineMultisampleState(), RD::PipelineDepthStencilState(), RD::PipelineColorBlendState::create_disabled(), 0);
+ for (int i = 0; i < COPY_TO_FB_MAX; i++) {
+ copy_to_fb.pipelines[i].setup(copy_to_fb.shader.version_get_shader(copy_to_fb.shader_version, i), RD::RENDER_PRIMITIVE_TRIANGLES, RD::PipelineRasterizationState(), RD::PipelineMultisampleState(), RD::PipelineDepthStencilState(), RD::PipelineColorBlendState::create_disabled(), 0);
+ }
}
{
diff --git a/servers/rendering/rasterizer_rd/rasterizer_effects_rd.h b/servers/rendering/rasterizer_rd/rasterizer_effects_rd.h
index aec381d193..531591442b 100644
--- a/servers/rendering/rasterizer_rd/rasterizer_effects_rd.h
+++ b/servers/rendering/rasterizer_rd/rasterizer_effects_rd.h
@@ -111,21 +111,30 @@ class RasterizerEffectsRD {
} copy;
+ enum CopyToFBMode {
+ COPY_TO_FB_COPY,
+ COPY_TO_FB_COPY_PANORAMA_TO_DP,
+ COPY_TO_FB_MAX,
+
+ };
+
struct CopyToFbPushConstant {
float section[4];
float pixel_size[2];
uint32_t flip_y;
uint32_t use_section;
+
uint32_t force_luminance;
- uint32_t pad[3];
+ uint32_t alpha_to_zero;
+ uint32_t pad[2];
};
struct CopyToFb {
CopyToFbPushConstant push_constant;
CopyToFbShaderRD shader;
RID shader_version;
- RenderPipelineVertexFormatCacheRD pipeline;
+ RenderPipelineVertexFormatCacheRD pipelines[COPY_TO_FB_MAX];
} copy_to_fb;
@@ -553,10 +562,11 @@ class RasterizerEffectsRD {
RID _get_compute_uniform_set_from_image_pair(RID p_texture, RID p_texture2);
public:
- void copy_to_fb_rect(RID p_source_rd_texture, RID p_dest_framebuffer, const Rect2i &p_rect, bool p_flip_y = false, bool p_force_luminance = false);
+ void copy_to_fb_rect(RID p_source_rd_texture, RID p_dest_framebuffer, const Rect2i &p_rect, bool p_flip_y = false, bool p_force_luminance = false, bool p_alpha_to_zero = false);
void copy_to_rect(RID p_source_rd_texture, RID p_dest_texture, const Rect2i &p_rect, bool p_flip_y = false, bool p_force_luminance = false, bool p_all_source = false, bool p_8_bit_dst = false);
void copy_depth_to_rect(RID p_source_rd_texture, RID p_dest_framebuffer, const Rect2i &p_rect, bool p_flip_y = false);
void copy_depth_to_rect_and_linearize(RID p_source_rd_texture, RID p_dest_texture, const Rect2i &p_rect, bool p_flip_y, float p_z_near, float p_z_far);
+ void copy_to_atlas_fb(RID p_source_rd_texture, RID p_dest_framebuffer, const Rect2 &p_uv_rect, RD::DrawListID p_draw_list, bool p_flip_y = false, bool p_panorama = false);
void gaussian_blur(RID p_source_rd_texture, RID p_texture, RID p_back_texture, const Rect2i &p_region, bool p_8bit_dst = false);
void gaussian_glow(RID p_source_rd_texture, RID p_texture, RID p_back_texture, const Size2i &p_size, float p_strength = 1.0, bool p_first_pass = false, float p_luminance_cap = 16.0, float p_exposure = 1.0, float p_bloom = 0.0, float p_hdr_bleed_treshold = 1.0, float p_hdr_bleed_scale = 1.0, RID p_auto_exposure = RID(), float p_auto_exposure_grey = 1.0);
diff --git a/servers/rendering/rasterizer_rd/rasterizer_scene_high_end_rd.cpp b/servers/rendering/rasterizer_rd/rasterizer_scene_high_end_rd.cpp
index 8ed58c0ef5..77096b95ba 100644
--- a/servers/rendering/rasterizer_rd/rasterizer_scene_high_end_rd.cpp
+++ b/servers/rendering/rasterizer_rd/rasterizer_scene_high_end_rd.cpp
@@ -52,6 +52,21 @@ static _FORCE_INLINE_ void store_transform(const Transform &p_mtx, float *p_arra
p_array[15] = 1;
}
+static _FORCE_INLINE_ void store_basis_3x4(const Basis &p_mtx, float *p_array) {
+ p_array[0] = p_mtx.elements[0][0];
+ p_array[1] = p_mtx.elements[1][0];
+ p_array[2] = p_mtx.elements[2][0];
+ p_array[3] = 0;
+ p_array[4] = p_mtx.elements[0][1];
+ p_array[5] = p_mtx.elements[1][1];
+ p_array[6] = p_mtx.elements[2][1];
+ p_array[7] = 0;
+ p_array[8] = p_mtx.elements[0][2];
+ p_array[9] = p_mtx.elements[1][2];
+ p_array[10] = p_mtx.elements[2][2];
+ p_array[11] = 0;
+}
+
static _FORCE_INLINE_ void store_transform_3x3(const Transform &p_mtx, float *p_array) {
p_array[0] = p_mtx.basis.elements[0][0];
p_array[1] = p_mtx.basis.elements[1][0];
@@ -1826,6 +1841,30 @@ void RasterizerSceneHighEndRD::_setup_lights(RID *p_light_cull_result, int p_lig
light_data.atlas_rect[2] = 0;
light_data.atlas_rect[3] = 0;
+ RID projector = storage->light_get_projector(base);
+
+ if (projector.is_valid()) {
+ Rect2 rect = storage->decal_atlas_get_texture_rect(projector);
+
+ if (type == RS::LIGHT_SPOT) {
+
+ light_data.projector_rect[0] = rect.position.x;
+ light_data.projector_rect[1] = rect.position.y + rect.size.height; //flip because shadow is flipped
+ light_data.projector_rect[2] = rect.size.width;
+ light_data.projector_rect[3] = -rect.size.height;
+ } else {
+ light_data.projector_rect[0] = rect.position.x;
+ light_data.projector_rect[1] = rect.position.y;
+ light_data.projector_rect[2] = rect.size.width;
+ light_data.projector_rect[3] = rect.size.height * 0.5; //used by dp, so needs to be half
+ }
+ } else {
+ light_data.projector_rect[0] = 0;
+ light_data.projector_rect[1] = 0;
+ light_data.projector_rect[2] = 0;
+ light_data.projector_rect[3] = 0;
+ }
+
if (p_using_shadows && p_shadow_atlas.is_valid() && shadow_atlas_owns_light_instance(p_shadow_atlas, li)) {
// fill in the shadow information
@@ -1877,17 +1916,11 @@ void RasterizerSceneHighEndRD::_setup_lights(RID *p_light_cull_result, int p_lig
} else if (type == RS::LIGHT_SPOT) {
- //used for clamping in this light type
- light_data.atlas_rect[2] += light_data.atlas_rect[0];
- light_data.atlas_rect[3] += light_data.atlas_rect[1];
-
Transform modelview = (p_camera_inverse_transform * light_transform).inverse();
CameraMatrix bias;
bias.set_light_bias();
- CameraMatrix rectm;
- rectm.set_light_atlas_rect(rect);
- CameraMatrix shadow_mtx = rectm * bias * light_instance_get_shadow_camera(li, 0) * modelview;
+ CameraMatrix shadow_mtx = bias * light_instance_get_shadow_camera(li, 0) * modelview;
store_camera(shadow_mtx, light_data.shadow_matrix);
if (size > 0.0) {
@@ -1925,7 +1958,140 @@ void RasterizerSceneHighEndRD::_setup_lights(RID *p_light_cull_result, int p_lig
}
}
-void RasterizerSceneHighEndRD::_render_scene(RID p_render_buffer, const Transform &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_ortogonal, InstanceBase **p_cull_result, int p_cull_count, RID *p_light_cull_result, int p_light_cull_count, RID *p_reflection_probe_cull_result, int p_reflection_probe_cull_count, RID *p_gi_probe_cull_result, int p_gi_probe_cull_count, RID p_environment, RID p_camera_effects, RID p_shadow_atlas, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass, const Color &p_default_bg_color) {
+void RasterizerSceneHighEndRD::_setup_decals(const RID *p_decal_instances, int p_decal_count, const Transform &p_camera_inverse_xform) {
+
+ Transform uv_xform;
+ uv_xform.basis.scale(Vector3(2.0, 1.0, 2.0));
+ uv_xform.origin = Vector3(-1.0, 0.0, -1.0);
+
+ p_decal_count = MIN((uint32_t)p_decal_count, scene_state.max_decals);
+ int idx = 0;
+ for (int i = 0; i < p_decal_count; i++) {
+
+ RID di = p_decal_instances[i];
+ RID decal = decal_instance_get_base(di);
+
+ Transform xform = decal_instance_get_transform(di);
+
+ float fade = 1.0;
+
+ if (storage->decal_is_distance_fade_enabled(decal)) {
+ real_t distance = -p_camera_inverse_xform.xform(xform.origin).z;
+ float fade_begin = storage->decal_get_distance_fade_begin(decal);
+ float fade_length = storage->decal_get_distance_fade_length(decal);
+
+ if (distance > fade_begin) {
+ if (distance > fade_begin + fade_length) {
+ continue; // do not use this decal, its invisible
+ }
+
+ fade = 1.0 - (distance - fade_begin) / fade_length;
+ }
+ }
+
+ DecalData &dd = scene_state.decals[idx];
+
+ Vector3 decal_extents = storage->decal_get_extents(decal);
+
+ Transform scale_xform;
+ scale_xform.basis.scale(Vector3(decal_extents.x, decal_extents.y, decal_extents.z));
+ Transform to_decal_xform = (p_camera_inverse_xform * decal_instance_get_transform(di) * scale_xform * uv_xform).affine_inverse();
+ store_transform(to_decal_xform, dd.xform);
+
+ Vector3 normal = xform.basis.get_axis(Vector3::AXIS_Y).normalized();
+ normal = p_camera_inverse_xform.basis.xform(normal); //camera is normalized, so fine
+
+ dd.normal[0] = normal.x;
+ dd.normal[1] = normal.y;
+ dd.normal[2] = normal.z;
+ dd.normal_fade = storage->decal_get_normal_fade(decal);
+
+ RID albedo_tex = storage->decal_get_texture(decal, RS::DECAL_TEXTURE_ALBEDO);
+ RID emission_tex = storage->decal_get_texture(decal, RS::DECAL_TEXTURE_EMISSION);
+ if (albedo_tex.is_valid()) {
+ Rect2 rect = storage->decal_atlas_get_texture_rect(albedo_tex);
+ dd.albedo_rect[0] = rect.position.x;
+ dd.albedo_rect[1] = rect.position.y;
+ dd.albedo_rect[2] = rect.size.x;
+ dd.albedo_rect[3] = rect.size.y;
+ } else {
+
+ if (!emission_tex.is_valid()) {
+ continue; //no albedo, no emission, no decal.
+ }
+ dd.albedo_rect[0] = 0;
+ dd.albedo_rect[1] = 0;
+ dd.albedo_rect[2] = 0;
+ dd.albedo_rect[3] = 0;
+ }
+
+ RID normal_tex = storage->decal_get_texture(decal, RS::DECAL_TEXTURE_NORMAL);
+
+ if (normal_tex.is_valid()) {
+ Rect2 rect = storage->decal_atlas_get_texture_rect(normal_tex);
+ dd.normal_rect[0] = rect.position.x;
+ dd.normal_rect[1] = rect.position.y;
+ dd.normal_rect[2] = rect.size.x;
+ dd.normal_rect[3] = rect.size.y;
+
+ Basis normal_xform = p_camera_inverse_xform.basis * xform.basis.orthonormalized();
+ store_basis_3x4(normal_xform, dd.normal_xform);
+ } else {
+ dd.normal_rect[0] = 0;
+ dd.normal_rect[1] = 0;
+ dd.normal_rect[2] = 0;
+ dd.normal_rect[3] = 0;
+ }
+
+ RID orm_tex = storage->decal_get_texture(decal, RS::DECAL_TEXTURE_ORM);
+ if (orm_tex.is_valid()) {
+ Rect2 rect = storage->decal_atlas_get_texture_rect(orm_tex);
+ dd.orm_rect[0] = rect.position.x;
+ dd.orm_rect[1] = rect.position.y;
+ dd.orm_rect[2] = rect.size.x;
+ dd.orm_rect[3] = rect.size.y;
+ } else {
+ dd.orm_rect[0] = 0;
+ dd.orm_rect[1] = 0;
+ dd.orm_rect[2] = 0;
+ dd.orm_rect[3] = 0;
+ }
+
+ if (emission_tex.is_valid()) {
+ Rect2 rect = storage->decal_atlas_get_texture_rect(emission_tex);
+ dd.emission_rect[0] = rect.position.x;
+ dd.emission_rect[1] = rect.position.y;
+ dd.emission_rect[2] = rect.size.x;
+ dd.emission_rect[3] = rect.size.y;
+ } else {
+ dd.emission_rect[0] = 0;
+ dd.emission_rect[1] = 0;
+ dd.emission_rect[2] = 0;
+ dd.emission_rect[3] = 0;
+ }
+
+ Color modulate = storage->decal_get_modulate(decal);
+ dd.modulate[0] = modulate.r;
+ dd.modulate[1] = modulate.g;
+ dd.modulate[2] = modulate.b;
+ dd.modulate[3] = modulate.a * fade;
+ dd.emission_energy = storage->decal_get_emission_energy(decal) * fade;
+ dd.albedo_mix = storage->decal_get_albedo_mix(decal);
+ dd.mask = storage->decal_get_cull_mask(decal);
+ dd.upper_fade = storage->decal_get_upper_fade(decal);
+ dd.lower_fade = storage->decal_get_lower_fade(decal);
+
+ cluster_builder.add_decal(xform, decal_extents);
+
+ idx++;
+ }
+
+ if (idx > 0) {
+ RD::get_singleton()->buffer_update(scene_state.decal_buffer, 0, sizeof(DecalData) * idx, scene_state.decals, true);
+ }
+}
+
+void RasterizerSceneHighEndRD::_render_scene(RID p_render_buffer, const Transform &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_ortogonal, InstanceBase **p_cull_result, int p_cull_count, RID *p_light_cull_result, int p_light_cull_count, RID *p_reflection_probe_cull_result, int p_reflection_probe_cull_count, RID *p_gi_probe_cull_result, int p_gi_probe_cull_count, RID *p_decal_cull_result, int p_decal_cull_count, RID p_environment, RID p_camera_effects, RID p_shadow_atlas, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass, const Color &p_default_bg_color) {
RenderBufferDataHighEnd *render_buffer = nullptr;
if (p_render_buffer.is_valid()) {
@@ -2042,6 +2208,7 @@ void RasterizerSceneHighEndRD::_render_scene(RID p_render_buffer, const Transfor
cluster_builder.begin(p_cam_transform.affine_inverse(), p_cam_projection); //prepare cluster
_setup_lights(p_light_cull_result, p_light_cull_count, p_cam_transform.affine_inverse(), p_shadow_atlas, using_shadows);
+ _setup_decals(p_decal_cull_result, p_decal_cull_count, p_cam_transform.affine_inverse());
_setup_reflections(p_reflection_probe_cull_result, p_reflection_probe_cull_count, p_cam_transform.affine_inverse(), p_environment);
_setup_gi_probes(p_gi_probe_cull_result, p_gi_probe_cull_count, p_cam_transform);
_setup_environment(p_environment, p_cam_projection, p_cam_transform, p_reflection_probe, p_reflection_probe.is_valid(), screen_pixel_size, p_shadow_atlas, !p_reflection_probe.is_valid(), p_default_bg_color, p_cam_projection.get_z_near(), p_cam_projection.get_z_far(), false);
@@ -2483,17 +2650,40 @@ void RasterizerSceneHighEndRD::_update_render_base_uniform_set() {
uniforms.push_back(u);
}
-
{
RD::Uniform u;
u.binding = 10;
u.type = RD::UNIFORM_TYPE_TEXTURE;
- u.ids.push_back(cluster_builder.get_cluster_texture());
+ RID decal_atlas = storage->decal_atlas_get_texture();
+ u.ids.push_back(decal_atlas);
uniforms.push_back(u);
}
{
RD::Uniform u;
u.binding = 11;
+ u.type = RD::UNIFORM_TYPE_TEXTURE;
+ RID decal_atlas = storage->decal_atlas_get_texture_srgb();
+ u.ids.push_back(decal_atlas);
+ uniforms.push_back(u);
+ }
+ {
+ RD::Uniform u;
+ u.binding = 12;
+ u.type = RD::UNIFORM_TYPE_STORAGE_BUFFER;
+ u.ids.push_back(scene_state.decal_buffer);
+ uniforms.push_back(u);
+ }
+
+ {
+ RD::Uniform u;
+ u.binding = 13;
+ u.type = RD::UNIFORM_TYPE_TEXTURE;
+ u.ids.push_back(cluster_builder.get_cluster_texture());
+ uniforms.push_back(u);
+ }
+ {
+ RD::Uniform u;
+ u.binding = 14;
u.type = RD::UNIFORM_TYPE_STORAGE_BUFFER;
u.ids.push_back(cluster_builder.get_cluster_indices_buffer());
uniforms.push_back(u);
@@ -2501,7 +2691,7 @@ void RasterizerSceneHighEndRD::_update_render_base_uniform_set() {
{
RD::Uniform u;
- u.binding = 12;
+ u.binding = 15;
u.type = RD::UNIFORM_TYPE_TEXTURE;
if (directional_shadow_get_texture().is_valid()) {
u.ids.push_back(directional_shadow_get_texture());
@@ -2722,6 +2912,13 @@ RasterizerSceneHighEndRD::RasterizerSceneHighEndRD(RasterizerStorageRD *p_storag
defines += "\n#define MAX_GI_PROBES " + itos(scene_state.max_gi_probes) + "\n";
}
+ { //decals
+ scene_state.max_decals = MIN(1024 * 1024, uniform_max_size) / sizeof(DecalData); //1mb of decals
+ uint32_t decal_buffer_size = scene_state.max_decals * sizeof(DecalData);
+ scene_state.decals = memnew_arr(DecalData, scene_state.max_decals);
+ scene_state.decal_buffer = RD::get_singleton()->storage_buffer_create(decal_buffer_size);
+ }
+
Vector<String> shader_versions;
shader_versions.push_back("\n#define MODE_RENDER_DEPTH\n");
shader_versions.push_back("\n#define MODE_RENDER_DEPTH\n#define MODE_DUAL_PARABOLOID\n");
@@ -3001,10 +3198,12 @@ RasterizerSceneHighEndRD::~RasterizerSceneHighEndRD() {
RD::get_singleton()->free(scene_state.directional_light_buffer);
RD::get_singleton()->free(scene_state.light_buffer);
RD::get_singleton()->free(scene_state.reflection_buffer);
+ RD::get_singleton()->free(scene_state.decal_buffer);
memdelete_arr(scene_state.instances);
memdelete_arr(scene_state.gi_probes);
memdelete_arr(scene_state.directional_lights);
memdelete_arr(scene_state.lights);
memdelete_arr(scene_state.reflections);
+ memdelete_arr(scene_state.decals);
}
}
diff --git a/servers/rendering/rasterizer_rd/rasterizer_scene_high_end_rd.h b/servers/rendering/rasterizer_rd/rasterizer_scene_high_end_rd.h
index 6ae4720306..83ff46ca7e 100644
--- a/servers/rendering/rasterizer_rd/rasterizer_scene_high_end_rd.h
+++ b/servers/rendering/rasterizer_rd/rasterizer_scene_high_end_rd.h
@@ -279,6 +279,7 @@ class RasterizerSceneHighEndRD : public RasterizerSceneRD {
float soft_shadow_scale;
uint32_t mask;
uint32_t pad[2];
+ float projector_rect[4];
};
struct DirectionalLightData {
@@ -328,6 +329,24 @@ class RasterizerSceneHighEndRD : public RasterizerSceneRD {
uint32_t pad[1];
};
+ struct DecalData {
+ float xform[16];
+ float inv_extents[3];
+ float albedo_mix;
+ float albedo_rect[4];
+ float normal_rect[4];
+ float orm_rect[4];
+ float emission_rect[4];
+ float modulate[4];
+ float emission_energy;
+ uint32_t mask;
+ float upper_fade;
+ float lower_fade;
+ float normal_xform[12];
+ float normal[3];
+ float normal_fade;
+ };
+
enum {
INSTANCE_DATA_FLAG_MULTIMESH = 1 << 12,
INSTANCE_DATA_FLAG_MULTIMESH_FORMAT_2D = 1 << 13,
@@ -413,6 +432,10 @@ class RasterizerSceneHighEndRD : public RasterizerSceneRD {
RID gi_probe_buffer;
uint32_t max_gi_probe_probes_per_instance;
+ DecalData *decals;
+ uint32_t max_decals;
+ RID decal_buffer;
+
LightData *lights;
uint32_t max_lights;
RID light_buffer;
@@ -604,6 +627,7 @@ class RasterizerSceneHighEndRD : public RasterizerSceneRD {
void _setup_environment(RID p_environment, const CameraMatrix &p_cam_projection, const Transform &p_cam_transform, RID p_reflection_probe, bool p_no_fog, const Size2 &p_screen_pixel_size, RID p_shadow_atlas, bool p_flip_y, const Color &p_default_bg_color, float p_znear, float p_zfar, bool p_opaque_render_buffers = false, bool p_pancake_shadows = false);
void _setup_lights(RID *p_light_cull_result, int p_light_cull_count, const Transform &p_camera_inverse_transform, RID p_shadow_atlas, bool p_using_shadows);
+ void _setup_decals(const RID *p_decal_instances, int p_decal_count, const Transform &p_camera_inverse_xform);
void _setup_reflections(RID *p_reflection_probe_cull_result, int p_reflection_probe_cull_count, const Transform &p_camera_inverse_transform, RID p_environment);
void _setup_gi_probes(RID *p_gi_probe_probe_cull_result, int p_gi_probe_probe_cull_count, const Transform &p_camera_transform);
@@ -615,7 +639,7 @@ class RasterizerSceneHighEndRD : public RasterizerSceneRD {
void _fill_render_list(InstanceBase **p_cull_result, int p_cull_count, PassMode p_pass_mode, bool p_no_gi);
protected:
- virtual void _render_scene(RID p_render_buffer, const Transform &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_ortogonal, InstanceBase **p_cull_result, int p_cull_count, RID *p_light_cull_result, int p_light_cull_count, RID *p_reflection_probe_cull_result, int p_reflection_probe_cull_count, RID *p_gi_probe_cull_result, int p_gi_probe_cull_count, RID p_environment, RID p_camera_effects, RID p_shadow_atlas, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass, const Color &p_default_bg_color);
+ virtual void _render_scene(RID p_render_buffer, const Transform &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_ortogonal, InstanceBase **p_cull_result, int p_cull_count, RID *p_light_cull_result, int p_light_cull_count, RID *p_reflection_probe_cull_result, int p_reflection_probe_cull_count, RID *p_gi_probe_cull_result, int p_gi_probe_cull_count, RID *p_decal_cull_result, int p_decal_cull_count, RID p_environment, RID p_camera_effects, RID p_shadow_atlas, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass, const Color &p_default_bg_color);
virtual void _render_shadow(RID p_framebuffer, InstanceBase **p_cull_result, int p_cull_count, const CameraMatrix &p_projection, const Transform &p_transform, float p_zfar, float p_bias, float p_normal_bias, bool p_use_dp, bool p_use_dp_flip, bool p_use_pancake);
virtual void _render_material(const Transform &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_ortogonal, InstanceBase **p_cull_result, int p_cull_count, RID p_framebuffer, const Rect2i &p_region);
diff --git a/servers/rendering/rasterizer_rd/rasterizer_scene_rd.cpp b/servers/rendering/rasterizer_rd/rasterizer_scene_rd.cpp
index 2ec09b2528..37e2aaad0e 100644
--- a/servers/rendering/rasterizer_rd/rasterizer_scene_rd.cpp
+++ b/servers/rendering/rasterizer_rd/rasterizer_scene_rd.cpp
@@ -2116,6 +2116,21 @@ RasterizerSceneRD::ShadowMap *RasterizerSceneRD::_get_shadow_map(const Size2i &p
return &shadow_maps[p_size];
}
+
+//////////////////////////
+
+RID RasterizerSceneRD::decal_instance_create(RID p_decal) {
+ DecalInstance di;
+ di.decal = p_decal;
+ return decal_instance_owner.make_rid(di);
+}
+
+void RasterizerSceneRD::decal_instance_set_transform(RID p_decal, const Transform &p_transform) {
+ DecalInstance *di = decal_instance_owner.getornull(p_decal);
+ ERR_FAIL_COND(!di);
+ di->transform = p_transform;
+}
+
/////////////////////////////////
RID RasterizerSceneRD::gi_probe_instance_create(RID p_base) {
@@ -3493,6 +3508,16 @@ void RasterizerSceneRD::_render_buffers_debug_draw(RID p_render_buffers, RID p_s
}
}
+ if (debug_draw == RS::VIEWPORT_DEBUG_DRAW_DECAL_ATLAS) {
+ RID decal_atlas = storage->decal_atlas_get_texture();
+
+ if (decal_atlas.is_valid()) {
+ Size2 rtsize = storage->render_target_get_size(rb->render_target);
+
+ effects->copy_to_fb_rect(decal_atlas, storage->render_target_get_rd_framebuffer(rb->render_target), Rect2i(Vector2(), rtsize / 2), false, false, true);
+ }
+ }
+
if (debug_draw == RS::VIEWPORT_DEBUG_DRAW_SCENE_LUMINANCE) {
if (rb->luminance.current.is_valid()) {
Size2 rtsize = storage->render_target_get_size(rb->render_target);
@@ -3686,7 +3711,7 @@ RasterizerSceneRD::RenderBufferData *RasterizerSceneRD::render_buffers_get_data(
return rb->data;
}
-void RasterizerSceneRD::render_scene(RID p_render_buffers, const Transform &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_ortogonal, InstanceBase **p_cull_result, int p_cull_count, RID *p_light_cull_result, int p_light_cull_count, RID *p_reflection_probe_cull_result, int p_reflection_probe_cull_count, RID *p_gi_probe_cull_result, int p_gi_probe_cull_count, RID p_environment, RID p_camera_effects, RID p_shadow_atlas, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass) {
+void RasterizerSceneRD::render_scene(RID p_render_buffers, const Transform &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_ortogonal, InstanceBase **p_cull_result, int p_cull_count, RID *p_light_cull_result, int p_light_cull_count, RID *p_reflection_probe_cull_result, int p_reflection_probe_cull_count, RID *p_gi_probe_cull_result, int p_gi_probe_cull_count, RID *p_decal_cull_result, int p_decal_cull_count, RID p_environment, RID p_camera_effects, RID p_shadow_atlas, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass) {
Color clear_color;
if (p_render_buffers.is_valid()) {
@@ -3697,7 +3722,7 @@ void RasterizerSceneRD::render_scene(RID p_render_buffers, const Transform &p_ca
clear_color = storage->get_default_clear_color();
}
- _render_scene(p_render_buffers, p_cam_transform, p_cam_projection, p_cam_ortogonal, p_cull_result, p_cull_count, p_light_cull_result, p_light_cull_count, p_reflection_probe_cull_result, p_reflection_probe_cull_count, p_gi_probe_cull_result, p_gi_probe_cull_count, p_environment, p_camera_effects, p_shadow_atlas, p_reflection_atlas, p_reflection_probe, p_reflection_probe_pass, clear_color);
+ _render_scene(p_render_buffers, p_cam_transform, p_cam_projection, p_cam_ortogonal, p_cull_result, p_cull_count, p_light_cull_result, p_light_cull_count, p_reflection_probe_cull_result, p_reflection_probe_cull_count, p_gi_probe_cull_result, p_gi_probe_cull_count, p_decal_cull_result, p_decal_cull_count, p_environment, p_camera_effects, p_shadow_atlas, p_reflection_atlas, p_reflection_probe, p_reflection_probe_pass, clear_color);
if (p_render_buffers.is_valid()) {
RENDER_TIMESTAMP("Tonemap");
@@ -3917,6 +3942,8 @@ bool RasterizerSceneRD::free(RID p_rid) {
//ReflectionProbeInstance *rpi = reflection_probe_instance_owner.getornull(p_rid);
reflection_probe_release_atlas_index(p_rid);
reflection_probe_instance_owner.free(p_rid);
+ } else if (decal_instance_owner.owns(p_rid)) {
+ decal_instance_owner.free(p_rid);
} else if (gi_probe_instance_owner.owns(p_rid)) {
GIProbeInstance *gi_probe = gi_probe_instance_owner.getornull(p_rid);
if (gi_probe->texture.is_valid()) {
diff --git a/servers/rendering/rasterizer_rd/rasterizer_scene_rd.h b/servers/rendering/rasterizer_rd/rasterizer_scene_rd.h
index 859b654214..bb42ce7182 100644
--- a/servers/rendering/rasterizer_rd/rasterizer_scene_rd.h
+++ b/servers/rendering/rasterizer_rd/rasterizer_scene_rd.h
@@ -78,7 +78,7 @@ protected:
};
virtual RenderBufferData *_create_render_buffer_data() = 0;
- virtual void _render_scene(RID p_render_buffer, const Transform &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_ortogonal, InstanceBase **p_cull_result, int p_cull_count, RID *p_light_cull_result, int p_light_cull_count, RID *p_reflection_probe_cull_result, int p_reflection_probe_cull_count, RID *p_gi_probe_cull_result, int p_gi_probe_cull_count, RID p_environment, RID p_camera_effects, RID p_shadow_atlas, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass, const Color &p_default_color) = 0;
+ virtual void _render_scene(RID p_render_buffer, const Transform &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_ortogonal, InstanceBase **p_cull_result, int p_cull_count, RID *p_light_cull_result, int p_light_cull_count, RID *p_reflection_probe_cull_result, int p_reflection_probe_cull_count, RID *p_gi_probe_cull_result, int p_gi_probe_cull_count, RID *p_decal_cull_result, int p_decal_cull_count, RID p_environment, RID p_camera_effects, RID p_shadow_atlas, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass, const Color &p_default_color) = 0;
virtual void _render_shadow(RID p_framebuffer, InstanceBase **p_cull_result, int p_cull_count, const CameraMatrix &p_projection, const Transform &p_transform, float p_zfar, float p_bias, float p_normal_bias, bool p_use_dp, bool use_dp_flip, bool p_use_pancake) = 0;
virtual void _render_material(const Transform &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_ortogonal, InstanceBase **p_cull_result, int p_cull_count, RID p_framebuffer, const Rect2i &p_region) = 0;
@@ -324,6 +324,16 @@ private:
mutable RID_Owner<ReflectionProbeInstance> reflection_probe_instance_owner;
+ /* REFLECTION PROBE INSTANCE */
+
+ struct DecalInstance {
+
+ RID decal;
+ Transform transform;
+ };
+
+ mutable RID_Owner<DecalInstance> decal_instance_owner;
+
/* GIPROBE INSTANCE */
struct GIProbeLight {
@@ -1103,6 +1113,19 @@ public:
return rpi->atlas_index;
}
+ virtual RID decal_instance_create(RID p_decal);
+ virtual void decal_instance_set_transform(RID p_decal, const Transform &p_transform);
+
+ _FORCE_INLINE_ RID decal_instance_get_base(RID p_decal) const {
+ DecalInstance *decal = decal_instance_owner.getornull(p_decal);
+ return decal->decal;
+ }
+
+ _FORCE_INLINE_ Transform decal_instance_get_transform(RID p_decal) const {
+ DecalInstance *decal = decal_instance_owner.getornull(p_decal);
+ return decal->transform;
+ }
+
RID gi_probe_instance_create(RID p_base);
void gi_probe_instance_set_transform_to_data(RID p_probe, const Transform &p_xform);
bool gi_probe_needs_update(RID p_probe) const;
@@ -1168,7 +1191,7 @@ public:
RID render_buffers_get_ao_texture(RID p_render_buffers);
RID render_buffers_get_back_buffer_texture(RID p_render_buffers);
- void render_scene(RID p_render_buffers, const Transform &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_ortogonal, InstanceBase **p_cull_result, int p_cull_count, RID *p_light_cull_result, int p_light_cull_count, RID *p_reflection_probe_cull_result, int p_reflection_probe_cull_count, RID *p_gi_probe_cull_result, int p_gi_probe_cull_count, RID p_environment, RID p_shadow_atlas, RID p_camera_effects, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass);
+ void render_scene(RID p_render_buffers, const Transform &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_ortogonal, InstanceBase **p_cull_result, int p_cull_count, RID *p_light_cull_result, int p_light_cull_count, RID *p_reflection_probe_cull_result, int p_reflection_probe_cull_count, RID *p_gi_probe_cull_result, int p_gi_probe_cull_count, RID *p_decal_cull_result, int p_decal_cull_count, RID p_environment, RID p_shadow_atlas, RID p_camera_effects, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass);
void render_shadow(RID p_light, RID p_shadow_atlas, int p_pass, InstanceBase **p_cull_result, int p_cull_count);
diff --git a/servers/rendering/rasterizer_rd/rasterizer_storage_rd.cpp b/servers/rendering/rasterizer_rd/rasterizer_storage_rd.cpp
index 6ac1f7c95e..148494692c 100644
--- a/servers/rendering/rasterizer_rd/rasterizer_storage_rd.cpp
+++ b/servers/rendering/rasterizer_rd/rasterizer_storage_rd.cpp
@@ -809,6 +809,12 @@ void RasterizerStorageRD::texture_replace(RID p_texture, RID p_by_texture) {
}
//delete last, so proxies can be updated
texture_owner.free(p_by_texture);
+
+ if (decal_atlas.textures.has(p_texture)) {
+ //belongs to decal atlas..
+
+ decal_atlas.dirty = true; //mark it dirty since it was most likely modified
+ }
}
void RasterizerStorageRD::texture_set_size_override(RID p_texture, int p_width, int p_height) {
Texture *tex = texture_owner.getornull(p_texture);
@@ -3174,7 +3180,19 @@ void RasterizerStorageRD::light_set_projector(RID p_light, RID p_texture) {
Light *light = light_owner.getornull(p_light);
ERR_FAIL_COND(!light);
+ if (light->projector == p_texture) {
+ return;
+ }
+
+ if (light->type != RS::LIGHT_DIRECTIONAL && light->projector.is_valid()) {
+ texture_remove_from_decal_atlas(light->projector, light->type == RS::LIGHT_OMNI);
+ }
+
light->projector = p_texture;
+
+ if (light->type != RS::LIGHT_DIRECTIONAL && light->projector.is_valid()) {
+ texture_add_to_decal_atlas(light->projector, light->type == RS::LIGHT_OMNI);
+ }
}
void RasterizerStorageRD::light_set_negative(RID p_light, bool p_enable) {
@@ -3553,6 +3571,94 @@ float RasterizerStorageRD::reflection_probe_get_interior_ambient_probe_contribut
return reflection_probe->interior_ambient_probe_contrib;
}
+RID RasterizerStorageRD::decal_create() {
+ return decal_owner.make_rid(Decal());
+}
+
+void RasterizerStorageRD::decal_set_extents(RID p_decal, const Vector3 &p_extents) {
+ Decal *decal = decal_owner.getornull(p_decal);
+ ERR_FAIL_COND(!decal);
+ decal->extents = p_extents;
+ decal->instance_dependency.instance_notify_changed(true, false);
+}
+void RasterizerStorageRD::decal_set_texture(RID p_decal, RS::DecalTexture p_type, RID p_texture) {
+ Decal *decal = decal_owner.getornull(p_decal);
+ ERR_FAIL_COND(!decal);
+ ERR_FAIL_INDEX(p_type, RS::DECAL_TEXTURE_MAX);
+
+ if (decal->textures[p_type] == p_texture) {
+ return;
+ }
+
+ ERR_FAIL_COND(p_texture.is_valid() && !texture_owner.owns(p_texture));
+
+ if (decal->textures[p_type].is_valid() && texture_owner.owns(decal->textures[p_type])) {
+ texture_remove_from_decal_atlas(decal->textures[p_type]);
+ }
+
+ decal->textures[p_type] = p_texture;
+
+ if (decal->textures[p_type].is_valid()) {
+ texture_add_to_decal_atlas(decal->textures[p_type]);
+ }
+
+ decal->instance_dependency.instance_notify_changed(false, true);
+}
+void RasterizerStorageRD::decal_set_emission_energy(RID p_decal, float p_energy) {
+ Decal *decal = decal_owner.getornull(p_decal);
+ ERR_FAIL_COND(!decal);
+ decal->emission_energy = p_energy;
+}
+
+void RasterizerStorageRD::decal_set_albedo_mix(RID p_decal, float p_mix) {
+ Decal *decal = decal_owner.getornull(p_decal);
+ ERR_FAIL_COND(!decal);
+ decal->albedo_mix = p_mix;
+}
+
+void RasterizerStorageRD::decal_set_modulate(RID p_decal, const Color &p_modulate) {
+ Decal *decal = decal_owner.getornull(p_decal);
+ ERR_FAIL_COND(!decal);
+ decal->modulate = p_modulate;
+}
+void RasterizerStorageRD::decal_set_cull_mask(RID p_decal, uint32_t p_layers) {
+ Decal *decal = decal_owner.getornull(p_decal);
+ ERR_FAIL_COND(!decal);
+ decal->cull_mask = p_layers;
+ decal->instance_dependency.instance_notify_changed(true, false);
+}
+
+void RasterizerStorageRD::decal_set_distance_fade(RID p_decal, bool p_enabled, float p_begin, float p_length) {
+
+ Decal *decal = decal_owner.getornull(p_decal);
+ ERR_FAIL_COND(!decal);
+ decal->distance_fade = p_enabled;
+ decal->distance_fade_begin = p_begin;
+ decal->distance_fade_length = p_length;
+}
+
+void RasterizerStorageRD::decal_set_fade(RID p_decal, float p_above, float p_below) {
+
+ Decal *decal = decal_owner.getornull(p_decal);
+ ERR_FAIL_COND(!decal);
+ decal->upper_fade = p_above;
+ decal->lower_fade = p_below;
+}
+
+void RasterizerStorageRD::decal_set_normal_fade(RID p_decal, float p_fade) {
+
+ Decal *decal = decal_owner.getornull(p_decal);
+ ERR_FAIL_COND(!decal);
+ decal->normal_fade = p_fade;
+}
+
+AABB RasterizerStorageRD::decal_get_aabb(RID p_decal) const {
+ Decal *decal = decal_owner.getornull(p_decal);
+ ERR_FAIL_COND_V(!decal, AABB());
+
+ return AABB(-decal->extents, decal->extents * 2.0);
+}
+
RID RasterizerStorageRD::gi_probe_create() {
return gi_probe_owner.make_rid(GIProbe());
@@ -4243,6 +4349,9 @@ void RasterizerStorageRD::base_update_dependency(RID p_base, RasterizerScene::In
} else if (reflection_probe_owner.owns(p_base)) {
ReflectionProbe *rp = reflection_probe_owner.getornull(p_base);
p_instance->update_dependency(&rp->instance_dependency);
+ } else if (decal_owner.owns(p_base)) {
+ Decal *decal = decal_owner.getornull(p_base);
+ p_instance->update_dependency(&decal->instance_dependency);
} else if (gi_probe_owner.owns(p_base)) {
GIProbe *gip = gi_probe_owner.getornull(p_base);
p_instance->update_dependency(&gip->instance_dependency);
@@ -4271,6 +4380,9 @@ RS::InstanceType RasterizerStorageRD::get_base_type(RID p_rid) const {
if (reflection_probe_owner.owns(p_rid)) {
return RS::INSTANCE_REFLECTION_PROBE;
}
+ if (decal_owner.owns(p_rid)) {
+ return RS::INSTANCE_DECAL;
+ }
if (gi_probe_owner.owns(p_rid)) {
return RS::INSTANCE_GI_PROBE;
}
@@ -4280,10 +4392,246 @@ RS::InstanceType RasterizerStorageRD::get_base_type(RID p_rid) const {
return RS::INSTANCE_NONE;
}
+
+void RasterizerStorageRD::texture_add_to_decal_atlas(RID p_texture, bool p_panorama_to_dp) {
+ if (!decal_atlas.textures.has(p_texture)) {
+ DecalAtlas::Texture t;
+ t.users = 1;
+ t.panorama_to_dp_users = p_panorama_to_dp ? 1 : 0;
+ decal_atlas.textures[p_texture] = t;
+ decal_atlas.dirty = true;
+ } else {
+ DecalAtlas::Texture *t = decal_atlas.textures.getptr(p_texture);
+ t->users++;
+ if (p_panorama_to_dp) {
+ t->panorama_to_dp_users++;
+ }
+ }
+}
+
+void RasterizerStorageRD::texture_remove_from_decal_atlas(RID p_texture, bool p_panorama_to_dp) {
+ DecalAtlas::Texture *t = decal_atlas.textures.getptr(p_texture);
+ ERR_FAIL_COND(!t);
+ t->users--;
+ if (p_panorama_to_dp) {
+ ERR_FAIL_COND(t->panorama_to_dp_users == 0);
+ t->panorama_to_dp_users--;
+ }
+ if (t->users == 0) {
+ decal_atlas.textures.erase(p_texture);
+ //do not mark it dirty, there is no need to since it remains working
+ }
+}
+
+RID RasterizerStorageRD::decal_atlas_get_texture() const {
+ return decal_atlas.texture;
+}
+
+RID RasterizerStorageRD::decal_atlas_get_texture_srgb() const {
+ return decal_atlas.texture;
+}
+
+void RasterizerStorageRD::_update_decal_atlas() {
+ if (!decal_atlas.dirty) {
+ return; //nothing to do
+ }
+
+ decal_atlas.dirty = false;
+
+ if (decal_atlas.texture.is_valid()) {
+ RD::get_singleton()->free(decal_atlas.texture);
+ decal_atlas.texture = RID();
+ decal_atlas.texture_srgb = RID();
+ decal_atlas.texture_mipmaps.clear();
+ }
+
+ int border = 1 << decal_atlas.mipmaps;
+
+ if (decal_atlas.textures.size()) {
+ //generate atlas
+ Vector<DecalAtlas::SortItem> itemsv;
+ itemsv.resize(decal_atlas.textures.size());
+ int base_size = 8;
+ const RID *K = NULL;
+
+ int idx = 0;
+ while ((K = decal_atlas.textures.next(K))) {
+ DecalAtlas::SortItem &si = itemsv.write[idx];
+
+ Texture *src_tex = texture_owner.getornull(*K);
+
+ si.size.width = (src_tex->width / border) + 1;
+ si.size.height = (src_tex->height / border) + 1;
+ si.pixel_size = Size2i(src_tex->width, src_tex->height);
+
+ if (base_size < si.size.width) {
+ base_size = nearest_power_of_2_templated(si.size.width);
+ }
+
+ si.texture = *K;
+ idx++;
+ }
+
+ //sort items by size
+ itemsv.sort();
+
+ //attempt to create atlas
+ int item_count = itemsv.size();
+ DecalAtlas::SortItem *items = itemsv.ptrw();
+
+ int atlas_height = 0;
+
+ while (true) {
+
+ Vector<int> v_offsetsv;
+ v_offsetsv.resize(base_size);
+
+ int *v_offsets = v_offsetsv.ptrw();
+ zeromem(v_offsets, sizeof(int) * base_size);
+
+ int max_height = 0;
+
+ for (int i = 0; i < item_count; i++) {
+ //best fit
+ DecalAtlas::SortItem &si = items[i];
+ int best_idx = -1;
+ int best_height = 0x7FFFFFFF;
+ for (int j = 0; j <= base_size - si.size.width; j++) {
+ int height = 0;
+ for (int k = 0; k < si.size.width; k++) {
+ int h = v_offsets[k + j];
+ if (h > height) {
+ height = h;
+ if (height > best_height) {
+ break; //already bad
+ }
+ }
+ }
+
+ if (height < best_height) {
+ best_height = height;
+ best_idx = j;
+ }
+ }
+
+ //update
+ for (int k = 0; k < si.size.width; k++) {
+ v_offsets[k + best_idx] = best_height + si.size.height;
+ }
+
+ si.pos.x = best_idx;
+ si.pos.y = best_height;
+
+ if (si.pos.y + si.size.height > max_height) {
+ max_height = si.pos.y + si.size.height;
+ }
+ }
+
+ if (max_height <= base_size * 2) {
+ atlas_height = max_height;
+ break; //good ratio, break;
+ }
+
+ base_size *= 2;
+ }
+
+ decal_atlas.size.width = base_size * border;
+ decal_atlas.size.height = nearest_power_of_2_templated(atlas_height * border);
+
+ for (int i = 0; i < item_count; i++) {
+ DecalAtlas::Texture *t = decal_atlas.textures.getptr(items[i].texture);
+ t->uv_rect.position = items[i].pos * border + Vector2i(border / 2, border / 2);
+ t->uv_rect.size = items[i].pixel_size;
+ //print_line("blitrect: " + t->uv_rect);
+ t->uv_rect.position /= Size2(decal_atlas.size);
+ t->uv_rect.size /= Size2(decal_atlas.size);
+ }
+ } else {
+
+ //use border as size, so it at least has enough mipmaps
+ decal_atlas.size.width = border;
+ decal_atlas.size.height = border;
+ }
+
+ //blit textures
+
+ RD::TextureFormat tformat;
+ tformat.format = RD::DATA_FORMAT_R8G8B8A8_UNORM;
+ tformat.width = decal_atlas.size.width;
+ tformat.height = decal_atlas.size.height;
+ tformat.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | RD::TEXTURE_USAGE_CAN_COPY_TO_BIT;
+ tformat.type = RD::TEXTURE_TYPE_2D;
+ tformat.mipmaps = decal_atlas.mipmaps;
+ tformat.shareable_formats.push_back(RD::DATA_FORMAT_R8G8B8A8_UNORM);
+ tformat.shareable_formats.push_back(RD::DATA_FORMAT_R8G8B8A8_SRGB);
+
+ decal_atlas.texture = RD::get_singleton()->texture_create(tformat, RD::TextureView());
+
+ {
+ //create the framebuffer
+
+ Size2i s = decal_atlas.size;
+
+ for (int i = 0; i < decal_atlas.mipmaps; i++) {
+ DecalAtlas::MipMap mm;
+ mm.texture = RD::get_singleton()->texture_create_shared_from_slice(RD::TextureView(), decal_atlas.texture, 0, i);
+ Vector<RID> fb;
+ fb.push_back(mm.texture);
+ mm.fb = RD::get_singleton()->framebuffer_create(fb);
+ mm.size = s;
+ decal_atlas.texture_mipmaps.push_back(mm);
+
+ s.width = MAX(1, s.width >> 1);
+ s.height = MAX(1, s.height >> 1);
+ }
+ {
+ //create the SRGB variant
+ RD::TextureView rd_view;
+ rd_view.format_override = RD::DATA_FORMAT_R8G8B8A8_SRGB;
+ decal_atlas.texture_srgb = RD::get_singleton()->texture_create_shared(rd_view, decal_atlas.texture);
+ }
+ }
+
+ RID prev_texture;
+ for (int i = 0; i < decal_atlas.texture_mipmaps.size(); i++) {
+ const DecalAtlas::MipMap &mm = decal_atlas.texture_mipmaps[i];
+
+ Color clear_color(0, 0, 0, 0);
+
+ if (decal_atlas.textures.size()) {
+
+ if (i == 0) {
+ Vector<Color> cc;
+ cc.push_back(clear_color);
+
+ RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(mm.fb, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_DROP, RD::FINAL_ACTION_DISCARD, cc);
+
+ const RID *K = NULL;
+ while ((K = decal_atlas.textures.next(K))) {
+ DecalAtlas::Texture *t = decal_atlas.textures.getptr(*K);
+ Texture *src_tex = texture_owner.getornull(*K);
+ effects.copy_to_atlas_fb(src_tex->rd_texture, mm.fb, t->uv_rect, draw_list, false, t->panorama_to_dp_users > 0);
+ }
+
+ RD::get_singleton()->draw_list_end();
+
+ prev_texture = mm.texture;
+ } else {
+
+ effects.copy_to_fb_rect(prev_texture, mm.fb, Rect2i(Point2i(), mm.size));
+ prev_texture = mm.texture;
+ }
+ } else {
+ RD::get_singleton()->texture_clear(mm.texture, clear_color, 0, 1, 0, 1, false);
+ }
+ }
+}
+
void RasterizerStorageRD::update_dirty_resources() {
_update_queued_materials();
_update_dirty_multimeshes();
_update_dirty_skeletons();
+ _update_decal_atlas();
}
bool RasterizerStorageRD::has_os_feature(const String &p_feature) const {
@@ -4332,6 +4680,11 @@ bool RasterizerStorageRD::free(RID p_rid) {
}
}
+ if (decal_atlas.textures.has(p_rid)) {
+ decal_atlas.textures.erase(p_rid);
+ //there is not much a point of making it dirty, just let it be.
+ }
+
for (int i = 0; i < t->proxies.size(); i++) {
Texture *p = texture_owner.getornull(t->proxies[i]);
ERR_CONTINUE(!p);
@@ -4382,6 +4735,15 @@ bool RasterizerStorageRD::free(RID p_rid) {
ReflectionProbe *reflection_probe = reflection_probe_owner.getornull(p_rid);
reflection_probe->instance_dependency.instance_notify_deleted(p_rid);
reflection_probe_owner.free(p_rid);
+ } else if (decal_owner.owns(p_rid)) {
+ Decal *decal = decal_owner.getornull(p_rid);
+ for (int i = 0; i < RS::DECAL_TEXTURE_MAX; i++) {
+ if (decal->textures[i].is_valid() && texture_owner.owns(decal->textures[i])) {
+ texture_remove_from_decal_atlas(decal->textures[i]);
+ }
+ }
+ decal->instance_dependency.instance_notify_deleted(p_rid);
+ decal_owner.free(p_rid);
} else if (gi_probe_owner.owns(p_rid)) {
gi_probe_allocate(p_rid, Transform(), AABB(), Vector3i(), Vector<uint8_t>(), Vector<uint8_t>(), Vector<uint8_t>(), Vector<int>()); //deallocate
GIProbe *gi_probe = gi_probe_owner.getornull(p_rid);
@@ -4390,6 +4752,7 @@ bool RasterizerStorageRD::free(RID p_rid) {
} else if (light_owner.owns(p_rid)) {
+ light_set_projector(p_rid, RID()); //clear projector
// delete the texture
Light *light = light_owner.getornull(p_rid);
light->instance_dependency.instance_notify_deleted(p_rid);
@@ -4485,6 +4848,10 @@ RasterizerStorageRD::RasterizerStorageRD() {
Vector<Vector<uint8_t>> vpv;
vpv.push_back(pv);
default_rd_textures[DEFAULT_RD_TEXTURE_BLACK] = RD::get_singleton()->texture_create(tformat, RD::TextureView(), vpv);
+
+ //take the chance and initialize decal atlas to something
+ decal_atlas.texture = RD::get_singleton()->texture_create(tformat, RD::TextureView(), vpv);
+ decal_atlas.texture_srgb = decal_atlas.texture;
}
for (int i = 0; i < 16; i++) {
@@ -4676,9 +5043,11 @@ RasterizerStorageRD::RasterizerStorageRD() {
//default rd buffers
{
- { //vertex
+ //vertex
+ {
Vector<uint8_t> buffer;
+
buffer.resize(sizeof(float) * 3);
{
uint8_t *w = buffer.ptrw();
@@ -4813,4 +5182,12 @@ RasterizerStorageRD::~RasterizerStorageRD() {
RD::get_singleton()->free(mesh_default_rd_buffers[i]);
}
giprobe_sdf_shader.version_free(giprobe_sdf_shader_version);
+
+ if (decal_atlas.textures.size()) {
+ ERR_PRINT("Decal Atlas: " + itos(decal_atlas.textures.size()) + " textures were not removed from the atlas.");
+ }
+
+ if (decal_atlas.texture.is_valid()) {
+ RD::get_singleton()->free(decal_atlas.texture);
+ }
}
diff --git a/servers/rendering/rasterizer_rd/rasterizer_storage_rd.h b/servers/rendering/rasterizer_rd/rasterizer_storage_rd.h
index 29a45ca90e..1980f043a0 100644
--- a/servers/rendering/rasterizer_rd/rasterizer_storage_rd.h
+++ b/servers/rendering/rasterizer_rd/rasterizer_storage_rd.h
@@ -173,6 +173,51 @@ private:
RID default_rd_textures[DEFAULT_RD_TEXTURE_MAX];
RID default_rd_samplers[RS::CANVAS_ITEM_TEXTURE_FILTER_MAX][RS::CANVAS_ITEM_TEXTURE_REPEAT_MAX];
+ /* DECAL ATLAS */
+
+ struct DecalAtlas {
+ struct Texture {
+
+ int panorama_to_dp_users;
+ int users;
+ Rect2 uv_rect;
+ };
+
+ struct SortItem {
+ RID texture;
+ Size2i pixel_size;
+ Size2i size;
+ Point2i pos;
+
+ bool operator<(const SortItem &p_item) const {
+ //sort larger to smaller
+ if (size.height == p_item.size.height) {
+ return size.width > p_item.size.width;
+ } else {
+ return size.height > p_item.size.height;
+ }
+ }
+ };
+
+ HashMap<RID, Texture> textures;
+ bool dirty = true;
+ int mipmaps = 5;
+
+ RID texture;
+ RID texture_srgb;
+ struct MipMap {
+ RID fb;
+ RID texture;
+ Size2i size;
+ };
+ Vector<MipMap> texture_mipmaps;
+
+ Size2i size;
+
+ } decal_atlas;
+
+ void _update_decal_atlas();
+
/* SHADER */
struct Material;
@@ -403,6 +448,28 @@ private:
mutable RID_Owner<ReflectionProbe> reflection_probe_owner;
+ /* DECAL */
+
+ struct Decal {
+
+ Vector3 extents = Vector3(1, 1, 1);
+ RID textures[RS::DECAL_TEXTURE_MAX];
+ float emission_energy = 1.0;
+ float albedo_mix = 1.0;
+ Color modulate = Color(1, 1, 1, 1);
+ uint32_t cull_mask = (1 << 20) - 1;
+ float upper_fade = 0.3;
+ float lower_fade = 0.3;
+ bool distance_fade = false;
+ float distance_fade_begin = 10;
+ float distance_fade_length = 1;
+ float normal_fade = 0.0;
+
+ RasterizerScene::InstanceDependency instance_dependency;
+ };
+
+ mutable RID_Owner<Decal> decal_owner;
+
/* GI PROBE */
struct GIProbe {
@@ -533,6 +600,20 @@ public:
virtual Size2 texture_size_with_proxy(RID p_proxy);
+ virtual void texture_add_to_decal_atlas(RID p_texture, bool p_panorama_to_dp = false);
+ virtual void texture_remove_from_decal_atlas(RID p_texture, bool p_panorama_to_dp = false);
+
+ RID decal_atlas_get_texture() const;
+ RID decal_atlas_get_texture_srgb() const;
+ _FORCE_INLINE_ Rect2 decal_atlas_get_texture_rect(RID p_texture) {
+ DecalAtlas::Texture *t = decal_atlas.textures.getptr(p_texture);
+ if (!t) {
+ return Rect2();
+ }
+
+ return t->uv_rect;
+ }
+
//internal usage
_FORCE_INLINE_ RID texture_get_rd_texture(RID p_texture, bool p_srgb = false) {
@@ -884,6 +965,14 @@ public:
return light->param[p_param];
}
+ _FORCE_INLINE_ RID light_get_projector(RID p_light) {
+
+ const Light *light = light_owner.getornull(p_light);
+ ERR_FAIL_COND_V(!light, RID());
+
+ return light->projector;
+ }
+
_FORCE_INLINE_ Color light_get_color(RID p_light) {
const Light *light = light_owner.getornull(p_light);
@@ -972,6 +1061,81 @@ public:
void base_update_dependency(RID p_base, RasterizerScene::InstanceBase *p_instance);
void skeleton_update_dependency(RID p_skeleton, RasterizerScene::InstanceBase *p_instance);
+ /* DECAL API */
+
+ virtual RID decal_create();
+ virtual void decal_set_extents(RID p_decal, const Vector3 &p_extents);
+ virtual void decal_set_texture(RID p_decal, RS::DecalTexture p_type, RID p_texture);
+ virtual void decal_set_emission_energy(RID p_decal, float p_energy);
+ virtual void decal_set_albedo_mix(RID p_decal, float p_mix);
+ virtual void decal_set_modulate(RID p_decal, const Color &p_modulate);
+ virtual void decal_set_cull_mask(RID p_decal, uint32_t p_layers);
+ virtual void decal_set_distance_fade(RID p_decal, bool p_enabled, float p_begin, float p_length);
+ virtual void decal_set_fade(RID p_decal, float p_above, float p_below);
+ virtual void decal_set_normal_fade(RID p_decal, float p_fade);
+
+ _FORCE_INLINE_ Vector3 decal_get_extents(RID p_decal) {
+ const Decal *decal = decal_owner.getornull(p_decal);
+ return decal->extents;
+ }
+
+ _FORCE_INLINE_ RID decal_get_texture(RID p_decal, RS::DecalTexture p_texture) {
+ const Decal *decal = decal_owner.getornull(p_decal);
+ return decal->textures[p_texture];
+ }
+
+ _FORCE_INLINE_ Color decal_get_modulate(RID p_decal) {
+ const Decal *decal = decal_owner.getornull(p_decal);
+ return decal->modulate;
+ }
+
+ _FORCE_INLINE_ float decal_get_emission_energy(RID p_decal) {
+ const Decal *decal = decal_owner.getornull(p_decal);
+ return decal->emission_energy;
+ }
+
+ _FORCE_INLINE_ float decal_get_albedo_mix(RID p_decal) {
+ const Decal *decal = decal_owner.getornull(p_decal);
+ return decal->albedo_mix;
+ }
+
+ _FORCE_INLINE_ uint32_t decal_get_cull_mask(RID p_decal) {
+ const Decal *decal = decal_owner.getornull(p_decal);
+ return decal->cull_mask;
+ }
+
+ _FORCE_INLINE_ float decal_get_upper_fade(RID p_decal) {
+ const Decal *decal = decal_owner.getornull(p_decal);
+ return decal->upper_fade;
+ }
+
+ _FORCE_INLINE_ float decal_get_lower_fade(RID p_decal) {
+ const Decal *decal = decal_owner.getornull(p_decal);
+ return decal->lower_fade;
+ }
+
+ _FORCE_INLINE_ float decal_get_normal_fade(RID p_decal) {
+ const Decal *decal = decal_owner.getornull(p_decal);
+ return decal->normal_fade;
+ }
+
+ _FORCE_INLINE_ bool decal_is_distance_fade_enabled(RID p_decal) {
+ const Decal *decal = decal_owner.getornull(p_decal);
+ return decal->distance_fade;
+ }
+
+ _FORCE_INLINE_ float decal_get_distance_fade_begin(RID p_decal) {
+ const Decal *decal = decal_owner.getornull(p_decal);
+ return decal->distance_fade_begin;
+ }
+
+ _FORCE_INLINE_ float decal_get_distance_fade_length(RID p_decal) {
+ const Decal *decal = decal_owner.getornull(p_decal);
+ return decal->distance_fade_length;
+ }
+
+ virtual AABB decal_get_aabb(RID p_decal) const;
+
/* GI PROBE API */
RID gi_probe_create();
diff --git a/servers/rendering/rasterizer_rd/shaders/copy.glsl b/servers/rendering/rasterizer_rd/shaders/copy.glsl
index 48c49ff7de..2d7661f65f 100644
--- a/servers/rendering/rasterizer_rd/shaders/copy.glsl
+++ b/servers/rendering/rasterizer_rd/shaders/copy.glsl
@@ -35,7 +35,7 @@ layout(push_constant, binding = 1, std430) uniform Params {
// DOF.
float camera_z_far;
float camera_z_near;
- uvec2 pad2;
+ uint pad2[2];
}
params;
diff --git a/servers/rendering/rasterizer_rd/shaders/copy_to_fb.glsl b/servers/rendering/rasterizer_rd/shaders/copy_to_fb.glsl
index 1f499cf372..07f8d09743 100644
--- a/servers/rendering/rasterizer_rd/shaders/copy_to_fb.glsl
+++ b/servers/rendering/rasterizer_rd/shaders/copy_to_fb.glsl
@@ -13,6 +13,7 @@ layout(push_constant, binding = 1, std430) uniform Params {
vec2 pixel_size;
bool flip_y;
bool use_section;
+
bool force_luminance;
uint pad[3];
}
@@ -23,11 +24,12 @@ void main() {
vec2 base_arr[4] = vec2[](vec2(0.0, 0.0), vec2(0.0, 1.0), vec2(1.0, 1.0), vec2(1.0, 0.0));
uv_interp = base_arr[gl_VertexIndex];
+ vec2 vpos = uv_interp;
if (params.use_section) {
- uv_interp = params.section.xy + uv_interp * params.section.zw;
+ vpos = params.section.xy + vpos * params.section.zw;
}
- gl_Position = vec4(uv_interp * 2.0 - 1.0, 0.0, 1.0);
+ gl_Position = vec4(vpos * 2.0 - 1.0, 0.0, 1.0);
if (params.flip_y) {
uv_interp.y = 1.0 - uv_interp.y;
@@ -46,8 +48,10 @@ layout(push_constant, binding = 1, std430) uniform Params {
vec2 pixel_size;
bool flip_y;
bool use_section;
+
bool force_luminance;
- uint pad[3];
+ bool alpha_to_zero;
+ uint pad[2];
} params;
@@ -60,9 +64,41 @@ layout(location = 0) out vec4 frag_color;
void main() {
- vec4 color = texture(source_color, uv_interp, 0.0);
+ vec2 uv = uv_interp;
+
+#ifdef MODE_PANORAMA_TO_DP
+
+ //obtain normal from dual paraboloid uv
+#define M_PI 3.14159265359
+
+ float side;
+ uv.y = modf(uv.y * 2.0, side);
+ side = side * 2.0 - 1.0;
+ vec3 normal = vec3(uv * 2.0 - 1.0, 0.0);
+ normal.z = 0.5 - 0.5 * ((normal.x * normal.x) + (normal.y * normal.y));
+ normal *= -side;
+ normal = normalize(normal);
+
+ //now convert normal to panorama uv
+
+ vec2 st = vec2(atan(normal.x, normal.z), acos(normal.y));
+
+ if (st.x < 0.0)
+ st.x += M_PI * 2.0;
+
+ uv = st / vec2(M_PI * 2.0, M_PI);
+
+ if (side < 0.0) {
+ //uv.y = 1.0 - uv.y;
+ uv = 1.0 - uv;
+ }
+#endif
+ vec4 color = textureLod(source_color, uv, 0.0);
if (params.force_luminance) {
color.rgb = vec3(max(max(color.r, color.g), color.b));
}
+ if (params.alpha_to_zero) {
+ color.rgb *= color.a;
+ }
frag_color = color;
}
diff --git a/servers/rendering/rasterizer_rd/shaders/scene_high_end.glsl b/servers/rendering/rasterizer_rd/shaders/scene_high_end.glsl
index ea9d50c11d..ec47887036 100644
--- a/servers/rendering/rasterizer_rd/shaders/scene_high_end.glsl
+++ b/servers/rendering/rasterizer_rd/shaders/scene_high_end.glsl
@@ -20,9 +20,7 @@ layout(location = 2) in vec4 tangent_attrib;
layout(location = 3) in vec4 color_attrib;
#endif
-#if defined(UV_USED)
layout(location = 4) in vec2 uv_attrib;
-#endif
#if defined(UV2_USED) || defined(USE_LIGHTMAP)
layout(location = 5) in vec2 uv2_attrib;
@@ -39,9 +37,7 @@ layout(location = 1) out vec3 normal_interp;
layout(location = 2) out vec4 color_interp;
#endif
-#if defined(UV_USED)
layout(location = 3) out vec2 uv_interp;
-#endif
#if defined(UV2_USED) || defined(USE_LIGHTMAP)
layout(location = 4) out vec2 uv2_interp;
@@ -157,9 +153,7 @@ void main() {
#endif
}
-#if defined(UV_USED)
uv_interp = uv_attrib;
-#endif
#if defined(UV2_USED) || defined(USE_LIGHTMAP)
uv2_interp = uv2_attrib;
@@ -290,9 +284,7 @@ layout(location = 1) in vec3 normal_interp;
layout(location = 2) in vec4 color_interp;
#endif
-#if defined(UV_USED)
layout(location = 3) in vec2 uv_interp;
-#endif
#if defined(UV2_USED) || defined(USE_LIGHTMAP)
layout(location = 4) in vec2 uv2_interp;
@@ -792,7 +784,7 @@ float sample_directional_soft_shadow(texture2D shadow, vec3 pssm_coord, vec2 tex
#endif //USE_NO_SHADOWS
-void light_process_omni(uint idx, vec3 vertex, vec3 eye_vec, vec3 normal, vec3 albedo, float roughness, float metallic, float specular, float p_blob_intensity,
+void light_process_omni(uint idx, vec3 vertex, vec3 eye_vec, vec3 normal, vec3 vertex_ddx, vec3 vertex_ddy, vec3 albedo, float roughness, float metallic, float specular, float p_blob_intensity,
#ifdef LIGHT_BACKLIGHT_USED
vec3 backlight,
#endif
@@ -984,8 +976,8 @@ void light_process_omni(uint idx, vec3 vertex, vec3 eye_vec, vec3 normal, vec3 a
//redo shadowmapping, but shrink the model a bit to avoid arctifacts
splane = (lights.data[idx].shadow_matrix * vec4(vertex - normalize(normal_interp) * lights.data[idx].transmittance_bias, 1.0));
- shadow_len = length(splane);
- splane = normalize(splane);
+ shadow_len = length(splane.xyz);
+ splane = normalize(splane.xyz);
if (splane.z >= 0.0) {
@@ -1007,7 +999,70 @@ void light_process_omni(uint idx, vec3 vertex, vec3 eye_vec, vec3 normal, vec3 a
}
#endif
- shadow_attenuation = mix(shadow_color_enabled.rgb, vec3(1.0), shadow);
+ vec3 no_shadow = vec3(1.0);
+
+ if (lights.data[idx].projector_rect != vec4(0.0)) {
+
+ vec3 local_v = (lights.data[idx].shadow_matrix * vec4(vertex, 1.0)).xyz;
+ local_v = normalize(local_v);
+
+ vec4 atlas_rect = lights.data[idx].projector_rect;
+
+ if (local_v.z >= 0.0) {
+
+ local_v.z += 1.0;
+ atlas_rect.y += atlas_rect.w;
+
+ } else {
+
+ local_v.z = 1.0 - local_v.z;
+ }
+
+ local_v.xy /= local_v.z;
+ local_v.xy = local_v.xy * 0.5 + 0.5;
+ vec2 proj_uv = local_v.xy * atlas_rect.zw;
+
+ vec2 proj_uv_ddx;
+ vec2 proj_uv_ddy;
+ {
+ vec3 local_v_ddx = (lights.data[idx].shadow_matrix * vec4(vertex + vertex_ddx, 1.0)).xyz;
+ local_v_ddx = normalize(local_v_ddx);
+
+ if (local_v_ddx.z >= 0.0) {
+
+ local_v_ddx.z += 1.0;
+ } else {
+
+ local_v_ddx.z = 1.0 - local_v_ddx.z;
+ }
+
+ local_v_ddx.xy /= local_v_ddx.z;
+ local_v_ddx.xy = local_v_ddx.xy * 0.5 + 0.5;
+
+ proj_uv_ddx = local_v_ddx.xy * atlas_rect.zw - proj_uv;
+
+ vec3 local_v_ddy = (lights.data[idx].shadow_matrix * vec4(vertex + vertex_ddy, 1.0)).xyz;
+ local_v_ddy = normalize(local_v_ddy);
+
+ if (local_v_ddy.z >= 0.0) {
+
+ local_v_ddy.z += 1.0;
+ } else {
+
+ local_v_ddy.z = 1.0 - local_v_ddy.z;
+ }
+
+ local_v_ddy.xy /= local_v_ddy.z;
+ local_v_ddy.xy = local_v_ddy.xy * 0.5 + 0.5;
+
+ proj_uv_ddy = local_v_ddy.xy * atlas_rect.zw - proj_uv;
+ }
+
+ vec4 proj = textureGrad(sampler2D(decal_atlas_srgb, material_samplers[SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP]), proj_uv + atlas_rect.xy, proj_uv_ddx, proj_uv_ddy);
+ no_shadow = mix(no_shadow, proj.rgb, proj.a);
+ }
+
+ shadow_attenuation = mix(shadow_color_enabled.rgb, no_shadow, shadow);
}
#endif //USE_NO_SHADOWS
@@ -1038,7 +1093,7 @@ void light_process_omni(uint idx, vec3 vertex, vec3 eye_vec, vec3 normal, vec3 a
specular_light);
}
-void light_process_spot(uint idx, vec3 vertex, vec3 eye_vec, vec3 normal, vec3 albedo, float roughness, float metallic, float specular, float p_blob_intensity,
+void light_process_spot(uint idx, vec3 vertex, vec3 eye_vec, vec3 normal, vec3 vertex_ddx, vec3 vertex_ddy, vec3 albedo, float roughness, float metallic, float specular, float p_blob_intensity,
#ifdef LIGHT_BACKLIGHT_USED
vec3 backlight,
#endif
@@ -1122,6 +1177,8 @@ void light_process_spot(uint idx, vec3 vertex, vec3 eye_vec, vec3 normal, vec3 a
//find blocker
+ vec2 shadow_uv = splane.xy * lights.data[idx].atlas_rect.zw + lights.data[idx].atlas_rect.xy;
+
float blocker_count = 0.0;
float blocker_average = 0.0;
@@ -1134,10 +1191,11 @@ void light_process_spot(uint idx, vec3 vertex, vec3 eye_vec, vec3 normal, vec3 a
}
float uv_size = lights.data[idx].soft_shadow_size * z_norm * lights.data[idx].soft_shadow_scale;
+ vec2 clamp_max = lights.data[idx].atlas_rect.xy + lights.data[idx].atlas_rect.zw;
for (uint i = 0; i < scene_data.penumbra_shadow_samples; i++) {
- vec2 suv = splane.xy + (disk_rotation * scene_data.penumbra_shadow_kernel[i].xy) * uv_size;
- suv = clamp(suv, lights.data[idx].atlas_rect.xy, lights.data[idx].atlas_rect.zw);
+ vec2 suv = shadow_uv + (disk_rotation * scene_data.penumbra_shadow_kernel[i].xy) * uv_size;
+ suv = clamp(suv, lights.data[idx].atlas_rect.xy, clamp_max);
float d = textureLod(sampler2D(shadow_atlas, material_samplers[SAMPLER_LINEAR_CLAMP]), suv, 0.0).r;
if (d < z_norm) {
blocker_average += d;
@@ -1154,8 +1212,8 @@ void light_process_spot(uint idx, vec3 vertex, vec3 eye_vec, vec3 normal, vec3 a
shadow = 0.0;
for (uint i = 0; i < scene_data.penumbra_shadow_samples; i++) {
- vec2 suv = splane.xy + (disk_rotation * scene_data.penumbra_shadow_kernel[i].xy) * uv_size;
- suv = clamp(suv, lights.data[idx].atlas_rect.xy, lights.data[idx].atlas_rect.zw);
+ vec2 suv = shadow_uv + (disk_rotation * scene_data.penumbra_shadow_kernel[i].xy) * uv_size;
+ suv = clamp(suv, lights.data[idx].atlas_rect.xy, clamp_max);
shadow += textureProj(sampler2DShadow(shadow_atlas, shadow_sampler), vec4(suv, z_norm, 1.0));
}
@@ -1168,17 +1226,41 @@ void light_process_spot(uint idx, vec3 vertex, vec3 eye_vec, vec3 normal, vec3 a
} else {
//hard shadow
- splane.z = z_norm;
- shadow = sample_pcf_shadow(shadow_atlas, lights.data[idx].soft_shadow_scale * scene_data.shadow_atlas_pixel_size, splane);
+ vec4 shadow_uv = vec4(splane.xy * lights.data[idx].atlas_rect.zw + lights.data[idx].atlas_rect.xy, z_norm, 1.0);
+
+ shadow = sample_pcf_shadow(shadow_atlas, lights.data[idx].soft_shadow_scale * scene_data.shadow_atlas_pixel_size, shadow_uv);
+ }
+
+ vec3 no_shadow = vec3(1.0);
+
+ if (lights.data[idx].projector_rect != vec4(0.0)) {
+
+ splane = (lights.data[idx].shadow_matrix * vec4(vertex, 1.0));
+ splane /= splane.w;
+
+ vec2 proj_uv = splane.xy * lights.data[idx].projector_rect.zw;
+
+ //ensure we have proper mipmaps
+ vec4 splane_ddx = (lights.data[idx].shadow_matrix * vec4(vertex + vertex_ddx, 1.0));
+ splane_ddx /= splane_ddx.w;
+ vec2 proj_uv_ddx = splane_ddx.xy * lights.data[idx].projector_rect.zw - proj_uv;
+
+ vec4 splane_ddy = (lights.data[idx].shadow_matrix * vec4(vertex + vertex_ddy, 1.0));
+ splane_ddy /= splane_ddy.w;
+ vec2 proj_uv_ddy = splane_ddy.xy * lights.data[idx].projector_rect.zw - proj_uv;
+
+ vec4 proj = textureGrad(sampler2D(decal_atlas_srgb, material_samplers[SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP]), proj_uv + lights.data[idx].projector_rect.xy, proj_uv_ddx, proj_uv_ddy);
+ no_shadow = mix(no_shadow, proj.rgb, proj.a);
}
- shadow_attenuation = mix(shadow_color_enabled.rgb, vec3(1.0), shadow);
+ shadow_attenuation = mix(shadow_color_enabled.rgb, no_shadow, shadow);
#ifdef LIGHT_TRANSMITTANCE_USED
{
- vec4 splane = (lights.data[idx].shadow_matrix * vec4(vertex - normalize(normal_interp) * lights.data[idx].transmittance_bias, 1.0));
+ splane = (lights.data[idx].shadow_matrix * vec4(vertex - normalize(normal_interp) * lights.data[idx].transmittance_bias, 1.0));
splane /= splane.w;
+ splane.xy = splane.xy * lights.data[idx].atlas_rect.zw + lights.data[idx].atlas_rect.xy;
float shadow_z = textureLod(sampler2D(shadow_atlas, material_samplers[SAMPLER_LINEAR_CLAMP]), splane.xy, 0.0).r;
//reconstruct depth
@@ -1612,9 +1694,7 @@ void main() {
}
#endif
-#if defined(UV_USED)
vec2 uv = uv_interp;
-#endif
#if defined(UV2_USED) || defined(USE_LIGHTMAP)
vec2 uv2 = uv2_interp;
@@ -1696,7 +1776,81 @@ FRAGMENT_SHADER_CODE
discard;
}
#endif
+ /////////////////////// DECALS ////////////////////////////////
+
+#ifndef MODE_RENDER_DEPTH
+
+ uvec4 cluster_cell = texture(usampler3D(cluster_texture, material_samplers[SAMPLER_NEAREST_CLAMP]), vec3(screen_uv, (abs(vertex.z) - scene_data.z_near) / (scene_data.z_far - scene_data.z_near)));
+ //used for interpolating anything cluster related
+ vec3 vertex_ddx = dFdx(vertex);
+ vec3 vertex_ddy = dFdy(vertex);
+
+ { // process decals
+
+ uint decal_count = cluster_cell.w >> CLUSTER_COUNTER_SHIFT;
+ uint decal_pointer = cluster_cell.w & CLUSTER_POINTER_MASK;
+
+ //do outside for performance and avoiding arctifacts
+
+ for (uint i = 0; i < decal_count; i++) {
+
+ uint decal_index = cluster_data.indices[decal_pointer + i];
+ if (!bool(decals.data[decal_index].mask & instances.data[instance_index].layer_mask)) {
+ continue; //not masked
+ }
+
+ vec3 uv_local = (decals.data[decal_index].xform * vec4(vertex, 1.0)).xyz;
+ if (any(lessThan(uv_local, vec3(0.0, -1.0, 0.0))) || any(greaterThan(uv_local, vec3(1.0)))) {
+ continue; //out of decal
+ }
+
+ //we need ddx/ddy for mipmaps, so simulate them
+ vec2 ddx = (decals.data[decal_index].xform * vec4(vertex_ddx, 0.0)).xz;
+ vec2 ddy = (decals.data[decal_index].xform * vec4(vertex_ddy, 0.0)).xz;
+ float fade = pow(1.0 - (uv_local.y > 0.0 ? uv_local.y : -uv_local.y), uv_local.y > 0.0 ? decals.data[decal_index].upper_fade : decals.data[decal_index].lower_fade);
+
+ if (decals.data[decal_index].normal_fade > 0.0) {
+ fade *= smoothstep(decals.data[decal_index].normal_fade, 1.0, dot(normal_interp, decals.data[decal_index].normal) * 0.5 + 0.5);
+ }
+
+ if (decals.data[decal_index].albedo_rect != vec4(0.0)) {
+ //has albedo
+ vec4 decal_albedo = textureGrad(sampler2D(decal_atlas_srgb, material_samplers[SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP]), uv_local.xz * decals.data[decal_index].albedo_rect.zw + decals.data[decal_index].albedo_rect.xy, ddx * decals.data[decal_index].albedo_rect.zw, ddy * decals.data[decal_index].albedo_rect.zw);
+ decal_albedo *= decals.data[decal_index].modulate;
+ decal_albedo.a *= fade;
+ albedo = mix(albedo, decal_albedo.rgb, decal_albedo.a * decals.data[decal_index].albedo_mix);
+
+ if (decals.data[decal_index].normal_rect != vec4(0.0)) {
+
+ vec3 decal_normal = textureGrad(sampler2D(decal_atlas, material_samplers[SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP]), uv_local.xz * decals.data[decal_index].normal_rect.zw + decals.data[decal_index].normal_rect.xy, ddx * decals.data[decal_index].normal_rect.zw, ddy * decals.data[decal_index].normal_rect.zw).xyz;
+ decal_normal.xy = decal_normal.xy * vec2(2.0, -2.0) - vec2(1.0, -1.0); //users prefer flipped y normal maps in most authoring software
+ decal_normal.z = sqrt(max(0.0, 1.0 - dot(decal_normal.xy, decal_normal.xy)));
+ //convert to view space, use xzy because y is up
+ decal_normal = (decals.data[decal_index].normal_xform * decal_normal.xzy).xyz;
+
+ normal = normalize(mix(normal, decal_normal, decal_albedo.a));
+ }
+
+ if (decals.data[decal_index].orm_rect != vec4(0.0)) {
+
+ vec3 decal_orm = textureGrad(sampler2D(decal_atlas, material_samplers[SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP]), uv_local.xz * decals.data[decal_index].orm_rect.zw + decals.data[decal_index].orm_rect.xy, ddx * decals.data[decal_index].orm_rect.zw, ddy * decals.data[decal_index].orm_rect.zw).xyz;
+#if defined(AO_USED)
+ ao = mix(ao, decal_orm.r, decal_albedo.a);
+#endif
+ roughness = mix(roughness, decal_orm.g, decal_albedo.a);
+ metallic = mix(metallic, decal_orm.b, decal_albedo.a);
+ }
+ }
+
+ if (decals.data[decal_index].emission_rect != vec4(0.0)) {
+ //emission is additive, so its independent from albedo
+ emission += textureGrad(sampler2D(decal_atlas_srgb, material_samplers[SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP]), uv_local.xz * decals.data[decal_index].emission_rect.zw + decals.data[decal_index].emission_rect.xy, ddx * decals.data[decal_index].emission_rect.zw, ddy * decals.data[decal_index].emission_rect.zw).xyz * decals.data[decal_index].emission_energy * fade;
+ }
+ }
+ }
+
+#endif //not render depth
/////////////////////// LIGHTING //////////////////////////////
//apply energy conservation
@@ -1801,8 +1955,6 @@ FRAGMENT_SHADER_CODE
}
#endif
- uvec4 cluster_cell = texture(usampler3D(cluster_texture, material_samplers[SAMPLER_NEAREST_CLAMP]), vec3(screen_uv, (abs(vertex.z) - scene_data.z_near) / (scene_data.z_far - scene_data.z_near)));
-
{ // process reflections
vec4 reflection_accum = vec4(0.0, 0.0, 0.0, 0.0);
@@ -2134,7 +2286,7 @@ FRAGMENT_SHADER_CODE
continue; //not masked
}
- light_process_omni(light_index, vertex, view, normal, albedo, roughness, metallic, specular, specular_blob_intensity,
+ light_process_omni(light_index, vertex, view, normal, vertex_ddx, vertex_ddy, albedo, roughness, metallic, specular, specular_blob_intensity,
#ifdef LIGHT_BACKLIGHT_USED
backlight,
#endif
@@ -2173,7 +2325,7 @@ FRAGMENT_SHADER_CODE
continue; //not masked
}
- light_process_spot(light_index, vertex, view, normal, albedo, roughness, metallic, specular, specular_blob_intensity,
+ light_process_spot(light_index, vertex, view, normal, vertex_ddx, vertex_ddy, albedo, roughness, metallic, specular, specular_blob_intensity,
#ifdef LIGHT_BACKLIGHT_USED
backlight,
#endif
diff --git a/servers/rendering/rasterizer_rd/shaders/scene_high_end_inc.glsl b/servers/rendering/rasterizer_rd/shaders/scene_high_end_inc.glsl
index db11e4b005..b5e3de5e82 100644
--- a/servers/rendering/rasterizer_rd/shaders/scene_high_end_inc.glsl
+++ b/servers/rendering/rasterizer_rd/shaders/scene_high_end_inc.glsl
@@ -139,7 +139,7 @@ struct InstanceData {
uint layer_mask;
};
-layout(set = 0, binding = 4, std430) buffer Instances {
+layout(set = 0, binding = 4, std430) restrict readonly buffer Instances {
InstanceData data[];
}
instances;
@@ -153,7 +153,7 @@ struct LightData { //this structure needs to be as packed as possible
uint color_specular; //rgb color, a specular (8 bit unorm)
uint cone_attenuation_angle; // attenuation and angle, (16bit float)
uint shadow_color_enabled; //shadow rgb color, a>0.5 enabled (8bit unorm)
- vec4 atlas_rect; // used for spot
+ vec4 atlas_rect; // rect in the shadow atlas
mat4 shadow_matrix;
float shadow_bias;
float shadow_normal_bias;
@@ -162,9 +162,10 @@ struct LightData { //this structure needs to be as packed as possible
float soft_shadow_scale; // scales the shadow kernel for blurrier shadows
uint mask;
uint pad[2];
+ vec4 projector_rect; //projector rect in srgb decal atlas
};
-layout(set = 0, binding = 5, std430) buffer Lights {
+layout(set = 0, binding = 5, std430) restrict readonly buffer Lights {
LightData data[];
}
lights;
@@ -251,14 +252,40 @@ layout(set = 0, binding = 9) uniform texture3D gi_probe_textures[MAX_GI_PROBE_TE
#define CLUSTER_POINTER_MASK ((1 << CLUSTER_COUNTER_SHIFT) - 1)
#define CLUSTER_COUNTER_MASK 0xfff
-layout(set = 0, binding = 10) uniform utexture3D cluster_texture;
+layout(set = 0, binding = 10) uniform texture2D decal_atlas;
+layout(set = 0, binding = 11) uniform texture2D decal_atlas_srgb;
+
+struct DecalData {
+ mat4 xform; //to decal transform
+ vec3 inv_extents;
+ float albedo_mix;
+ vec4 albedo_rect;
+ vec4 normal_rect;
+ vec4 orm_rect;
+ vec4 emission_rect;
+ vec4 modulate;
+ float emission_energy;
+ uint mask;
+ float upper_fade;
+ float lower_fade;
+ mat3x4 normal_xform;
+ vec3 normal;
+ float normal_fade;
+};
+
+layout(set = 0, binding = 12, std430) restrict readonly buffer Decals {
+ DecalData data[];
+}
+decals;
+
+layout(set = 0, binding = 13) uniform utexture3D cluster_texture;
-layout(set = 0, binding = 11, std430) buffer ClusterData {
+layout(set = 0, binding = 14, std430) restrict readonly buffer ClusterData {
uint indices[];
}
cluster_data;
-layout(set = 0, binding = 12) uniform texture2D directional_shadow_atlas;
+layout(set = 0, binding = 15) uniform texture2D directional_shadow_atlas;
// decal atlas
@@ -290,7 +317,7 @@ layout(set = 3, binding = 4) uniform texture2D ao_buffer;
/* Set 4 Skeleton & Instancing (Multimesh) */
-layout(set = 4, binding = 0, std430) buffer Transforms {
+layout(set = 4, binding = 0, std430) restrict readonly buffer Transforms {
vec4 data[];
}
transforms;
diff --git a/servers/rendering/rendering_server_raster.h b/servers/rendering/rendering_server_raster.h
index 8eb4dea9e6..6f7ce7728d 100644
--- a/servers/rendering/rendering_server_raster.h
+++ b/servers/rendering/rendering_server_raster.h
@@ -340,6 +340,20 @@ public:
BIND2(reflection_probe_set_cull_mask, RID, uint32_t)
BIND2(reflection_probe_set_resolution, RID, int)
+ /* DECAL API */
+
+ BIND0R(RID, decal_create)
+
+ BIND2(decal_set_extents, RID, const Vector3 &)
+ BIND3(decal_set_texture, RID, DecalTexture, RID)
+ BIND2(decal_set_emission_energy, RID, float)
+ BIND2(decal_set_albedo_mix, RID, float)
+ BIND2(decal_set_modulate, RID, const Color &)
+ BIND2(decal_set_cull_mask, RID, uint32_t)
+ BIND4(decal_set_distance_fade, RID, bool, float, float)
+ BIND3(decal_set_fade, RID, float, float)
+ BIND2(decal_set_normal_fade, RID, float)
+
/* BAKED LIGHT API */
BIND0R(RID, gi_probe_create)
diff --git a/servers/rendering/rendering_server_scene.cpp b/servers/rendering/rendering_server_scene.cpp
index d66708587a..fc7c160c0b 100644
--- a/servers/rendering/rendering_server_scene.cpp
+++ b/servers/rendering/rendering_server_scene.cpp
@@ -155,6 +155,20 @@ void *RenderingServerScene::_instance_pair(void *p_self, OctreeElementID, Instan
geom->reflection_dirty = true;
return E; //this element should make freeing faster
+ } else if (B->base_type == RS::INSTANCE_DECAL && ((1 << A->base_type) & RS::INSTANCE_GEOMETRY_MASK)) {
+
+ InstanceDecalData *decal = static_cast<InstanceDecalData *>(B->base_data);
+ InstanceGeometryData *geom = static_cast<InstanceGeometryData *>(A->base_data);
+
+ InstanceDecalData::PairInfo pinfo;
+ pinfo.geometry = A;
+ pinfo.L = geom->decals.push_back(B);
+
+ List<InstanceDecalData::PairInfo>::Element *E = decal->geometries.push_back(pinfo);
+
+ geom->decal_dirty = true;
+
+ return E; //this element should make freeing faster
} else if (B->base_type == RS::INSTANCE_LIGHTMAP_CAPTURE && ((1 << A->base_type) & RS::INSTANCE_GEOMETRY_MASK)) {
InstanceLightmapCaptureData *lightmap_capture = static_cast<InstanceLightmapCaptureData *>(B->base_data);
@@ -233,6 +247,17 @@ void RenderingServerScene::_instance_unpair(void *p_self, OctreeElementID, Insta
reflection_probe->geometries.erase(E);
geom->reflection_dirty = true;
+ } else if (B->base_type == RS::INSTANCE_DECAL && ((1 << A->base_type) & RS::INSTANCE_GEOMETRY_MASK)) {
+
+ InstanceDecalData *decal = static_cast<InstanceDecalData *>(B->base_data);
+ InstanceGeometryData *geom = static_cast<InstanceGeometryData *>(A->base_data);
+
+ List<InstanceDecalData::PairInfo>::Element *E = reinterpret_cast<List<InstanceDecalData::PairInfo>::Element *>(udata);
+
+ geom->decals.erase(E->get().L);
+ decal->geometries.erase(E);
+
+ geom->decal_dirty = true;
} else if (B->base_type == RS::INSTANCE_LIGHTMAP_CAPTURE && ((1 << A->base_type) & RS::INSTANCE_GEOMETRY_MASK)) {
InstanceLightmapCaptureData *lightmap_capture = static_cast<InstanceLightmapCaptureData *>(B->base_data);
@@ -387,6 +412,12 @@ void RenderingServerScene::instance_set_base(RID p_instance, RID p_base) {
reflection_probe_render_list.remove(&reflection_probe->update_list);
}
} break;
+ case RS::INSTANCE_DECAL: {
+
+ InstanceDecalData *decal = static_cast<InstanceDecalData *>(instance->base_data);
+ RSG::scene_render->free(decal->instance);
+
+ } break;
case RS::INSTANCE_LIGHTMAP_CAPTURE: {
InstanceLightmapCaptureData *lightmap_capture = static_cast<InstanceLightmapCaptureData *>(instance->base_data);
@@ -476,6 +507,14 @@ void RenderingServerScene::instance_set_base(RID p_instance, RID p_base) {
reflection_probe->instance = RSG::scene_render->reflection_probe_instance_create(p_base);
} break;
+ case RS::INSTANCE_DECAL: {
+
+ InstanceDecalData *decal = memnew(InstanceDecalData);
+ decal->owner = instance;
+ instance->base_data = decal;
+
+ decal->instance = RSG::scene_render->decal_instance_create(p_base);
+ } break;
case RS::INSTANCE_LIGHTMAP_CAPTURE: {
InstanceLightmapCaptureData *lightmap_capture = memnew(InstanceLightmapCaptureData);
@@ -691,6 +730,12 @@ void RenderingServerScene::instance_set_visible(RID p_instance, bool p_visible)
}
} break;
+ case RS::INSTANCE_DECAL: {
+ if (instance->octree_id && instance->scenario) {
+ instance->scenario->octree.set_pairable(instance->octree_id, p_visible, 1 << RS::INSTANCE_DECAL, p_visible ? RS::INSTANCE_GEOMETRY_MASK : 0);
+ }
+
+ } break;
case RS::INSTANCE_LIGHTMAP_CAPTURE: {
if (instance->octree_id && instance->scenario) {
instance->scenario->octree.set_pairable(instance->octree_id, p_visible, 1 << RS::INSTANCE_LIGHTMAP_CAPTURE, p_visible ? RS::INSTANCE_GEOMETRY_MASK : 0);
@@ -943,6 +988,13 @@ void RenderingServerScene::_update_instance(Instance *p_instance) {
reflection_probe->reflection_dirty = true;
}
+ if (p_instance->base_type == RS::INSTANCE_DECAL) {
+
+ InstanceDecalData *decal = static_cast<InstanceDecalData *>(p_instance->base_data);
+
+ RSG::scene_render->decal_instance_set_transform(decal->instance, p_instance->transform);
+ }
+
if (p_instance->base_type == RS::INSTANCE_GI_PROBE) {
InstanceGIProbeData *gi_probe = static_cast<InstanceGIProbeData *>(p_instance->base_data);
@@ -1000,7 +1052,7 @@ void RenderingServerScene::_update_instance(Instance *p_instance) {
uint32_t pairable_mask = 0;
bool pairable = false;
- if (p_instance->base_type == RS::INSTANCE_LIGHT || p_instance->base_type == RS::INSTANCE_REFLECTION_PROBE || p_instance->base_type == RS::INSTANCE_LIGHTMAP_CAPTURE) {
+ if (p_instance->base_type == RS::INSTANCE_LIGHT || p_instance->base_type == RS::INSTANCE_REFLECTION_PROBE || p_instance->base_type == RS::INSTANCE_DECAL || p_instance->base_type == RS::INSTANCE_LIGHTMAP_CAPTURE) {
pairable_mask = p_instance->visible ? RS::INSTANCE_GEOMETRY_MASK : 0;
pairable = true;
@@ -1080,6 +1132,11 @@ void RenderingServerScene::_update_instance_aabb(Instance *p_instance) {
new_aabb = RSG::storage->reflection_probe_get_aabb(p_instance->base);
} break;
+ case RenderingServer::INSTANCE_DECAL: {
+
+ new_aabb = RSG::storage->decal_get_aabb(p_instance->base);
+
+ } break;
case RenderingServer::INSTANCE_GI_PROBE: {
new_aabb = RSG::storage->gi_probe_get_bounds(p_instance->base);
@@ -2020,6 +2077,7 @@ void RenderingServerScene::_prepare_scene(const Transform p_cam_transform, const
light_cull_count = 0;
reflection_probe_cull_count = 0;
+ decal_cull_count = 0;
gi_probe_cull_count = 0;
//light_samplers_culled=0;
@@ -2089,6 +2147,18 @@ void RenderingServerScene::_prepare_scene(const Transform p_cam_transform, const
}
}
}
+ } else if (ins->base_type == RS::INSTANCE_DECAL && ins->visible) {
+
+ if (decal_cull_count < MAX_DECALS_CULLED) {
+
+ InstanceDecalData *decal = static_cast<InstanceDecalData *>(ins->base_data);
+
+ if (!decal->geometries.empty()) {
+ //do not add this decal if no geometry is affected by it..
+ decal_instance_cull_result[decal_cull_count] = decal->instance;
+ decal_cull_count++;
+ }
+ }
} else if (ins->base_type == RS::INSTANCE_GI_PROBE && ins->visible) {
@@ -2356,7 +2426,7 @@ void RenderingServerScene::_render_scene(RID p_render_buffers, const Transform p
/* PROCESS GEOMETRY AND DRAW SCENE */
RENDER_TIMESTAMP("Render Scene ");
- RSG::scene_render->render_scene(p_render_buffers, p_cam_transform, p_cam_projection, p_cam_orthogonal, (RasterizerScene::InstanceBase **)instance_cull_result, instance_cull_count, light_instance_cull_result, light_cull_count + directional_light_count, reflection_probe_instance_cull_result, reflection_probe_cull_count, gi_probe_instance_cull_result, gi_probe_cull_count, environment, camera_effects, p_shadow_atlas, p_reflection_probe.is_valid() ? RID() : scenario->reflection_atlas, p_reflection_probe, p_reflection_probe_pass);
+ RSG::scene_render->render_scene(p_render_buffers, p_cam_transform, p_cam_projection, p_cam_orthogonal, (RasterizerScene::InstanceBase **)instance_cull_result, instance_cull_count, light_instance_cull_result, light_cull_count + directional_light_count, reflection_probe_instance_cull_result, reflection_probe_cull_count, gi_probe_instance_cull_result, gi_probe_cull_count, decal_instance_cull_result, decal_cull_count, environment, camera_effects, p_shadow_atlas, p_reflection_probe.is_valid() ? RID() : scenario->reflection_atlas, p_reflection_probe, p_reflection_probe_pass);
}
void RenderingServerScene::render_empty_scene(RID p_render_buffers, RID p_scenario, RID p_shadow_atlas) {
@@ -2371,7 +2441,7 @@ void RenderingServerScene::render_empty_scene(RID p_render_buffers, RID p_scenar
else
environment = scenario->fallback_environment;
RENDER_TIMESTAMP("Render Empty Scene ");
- RSG::scene_render->render_scene(p_render_buffers, Transform(), CameraMatrix(), true, nullptr, 0, nullptr, 0, nullptr, 0, nullptr, 0, environment, RID(), p_shadow_atlas, scenario->reflection_atlas, RID(), 0);
+ RSG::scene_render->render_scene(p_render_buffers, Transform(), CameraMatrix(), true, nullptr, 0, nullptr, 0, nullptr, 0, nullptr, 0, nullptr, 0, environment, RID(), p_shadow_atlas, scenario->reflection_atlas, RID(), 0);
#endif
}
diff --git a/servers/rendering/rendering_server_scene.h b/servers/rendering/rendering_server_scene.h
index 0970fed6c4..f5f7c50ea0 100644
--- a/servers/rendering/rendering_server_scene.h
+++ b/servers/rendering/rendering_server_scene.h
@@ -48,6 +48,7 @@ public:
MAX_INSTANCE_CULL = 65536,
MAX_LIGHTS_CULLED = 4096,
MAX_REFLECTION_PROBES_CULLED = 4096,
+ MAX_DECALS_CULLED = 4096,
MAX_GI_PROBES_CULLED = 4096,
MAX_ROOM_CULL = 32,
MAX_EXTERIOR_PORTALS = 128,
@@ -237,6 +238,9 @@ public:
bool can_cast_shadows;
bool material_is_animated;
+ List<Instance *> decals;
+ bool decal_dirty;
+
List<Instance *> reflection_probes;
bool reflection_dirty;
@@ -252,6 +256,7 @@ public:
can_cast_shadows = true;
material_is_animated = true;
gi_probes_dirty = true;
+ decal_dirty = true;
}
};
@@ -279,6 +284,21 @@ public:
}
};
+ struct InstanceDecalData : public InstanceBaseData {
+
+ Instance *owner;
+ RID instance;
+
+ struct PairInfo {
+ List<Instance *>::Element *L; //reflection iterator in geometry
+ Instance *geometry;
+ };
+ List<PairInfo> geometries;
+
+ InstanceDecalData() {
+ }
+ };
+
SelfList<InstanceReflectionProbeData>::List reflection_probe_render_list;
struct InstanceLightData : public InstanceBaseData {
@@ -376,7 +396,9 @@ public:
int light_cull_count;
int directional_light_count;
RID reflection_probe_instance_cull_result[MAX_REFLECTION_PROBES_CULLED];
+ RID decal_instance_cull_result[MAX_DECALS_CULLED];
int reflection_probe_cull_count;
+ int decal_cull_count;
RID gi_probe_instance_cull_result[MAX_GI_PROBES_CULLED];
int gi_probe_cull_count;
diff --git a/servers/rendering/rendering_server_wrap_mt.h b/servers/rendering/rendering_server_wrap_mt.h
index c7d563c07c..79f328cb3b 100644
--- a/servers/rendering/rendering_server_wrap_mt.h
+++ b/servers/rendering/rendering_server_wrap_mt.h
@@ -264,6 +264,20 @@ public:
FUNC2(reflection_probe_set_cull_mask, RID, uint32_t)
FUNC2(reflection_probe_set_resolution, RID, int)
+ /* DECAL API */
+
+ FUNCRID(decal)
+
+ FUNC2(decal_set_extents, RID, const Vector3 &)
+ FUNC3(decal_set_texture, RID, DecalTexture, RID)
+ FUNC2(decal_set_emission_energy, RID, float)
+ FUNC2(decal_set_albedo_mix, RID, float)
+ FUNC2(decal_set_modulate, RID, const Color &)
+ FUNC2(decal_set_cull_mask, RID, uint32_t)
+ FUNC4(decal_set_distance_fade, RID, bool, float, float)
+ FUNC3(decal_set_fade, RID, float, float)
+ FUNC2(decal_set_normal_fade, RID, float)
+
/* BAKED LIGHT API */
FUNCRID(gi_probe)
diff --git a/servers/rendering_server.h b/servers/rendering_server.h
index a31c9f39fd..43c65d8007 100644
--- a/servers/rendering_server.h
+++ b/servers/rendering_server.h
@@ -462,6 +462,27 @@ public:
virtual void reflection_probe_set_cull_mask(RID p_probe, uint32_t p_layers) = 0;
virtual void reflection_probe_set_resolution(RID p_probe, int p_resolution) = 0;
+ /* DECAL API */
+
+ enum DecalTexture {
+ DECAL_TEXTURE_ALBEDO,
+ DECAL_TEXTURE_NORMAL,
+ DECAL_TEXTURE_ORM,
+ DECAL_TEXTURE_EMISSION,
+ DECAL_TEXTURE_MAX
+ };
+
+ virtual RID decal_create() = 0;
+ virtual void decal_set_extents(RID p_decal, const Vector3 &p_extents) = 0;
+ virtual void decal_set_texture(RID p_decal, DecalTexture p_type, RID p_texture) = 0;
+ virtual void decal_set_emission_energy(RID p_decal, float p_energy) = 0;
+ virtual void decal_set_albedo_mix(RID p_decal, float p_mix) = 0;
+ virtual void decal_set_modulate(RID p_decal, const Color &p_modulate) = 0;
+ virtual void decal_set_cull_mask(RID p_decal, uint32_t p_layers) = 0;
+ virtual void decal_set_distance_fade(RID p_decal, bool p_enabled, float p_begin, float p_length) = 0;
+ virtual void decal_set_fade(RID p_decal, float p_above, float p_below) = 0;
+ virtual void decal_set_normal_fade(RID p_decal, float p_fade) = 0;
+
/* GI PROBE API */
virtual RID gi_probe_create() = 0;
@@ -674,6 +695,7 @@ public:
VIEWPORT_DEBUG_DRAW_SSAO,
VIEWPORT_DEBUG_DRAW_ROUGHNESS_LIMITER,
VIEWPORT_DEBUG_DRAW_PSSM_SPLITS,
+ VIEWPORT_DEBUG_DRAW_DECAL_ATLAS,
};
@@ -868,6 +890,7 @@ public:
INSTANCE_PARTICLES,
INSTANCE_LIGHT,
INSTANCE_REFLECTION_PROBE,
+ INSTANCE_DECAL,
INSTANCE_GI_PROBE,
INSTANCE_LIGHTMAP_CAPTURE,
INSTANCE_MAX,
@@ -1167,6 +1190,7 @@ VARIANT_ENUM_CAST(RenderingServer::LightOmniShadowMode);
VARIANT_ENUM_CAST(RenderingServer::LightDirectionalShadowMode);
VARIANT_ENUM_CAST(RenderingServer::LightDirectionalShadowDepthRangeMode);
VARIANT_ENUM_CAST(RenderingServer::ReflectionProbeUpdateMode);
+VARIANT_ENUM_CAST(RenderingServer::DecalTexture);
VARIANT_ENUM_CAST(RenderingServer::ParticlesDrawOrder);
VARIANT_ENUM_CAST(RenderingServer::ViewportUpdateMode);
VARIANT_ENUM_CAST(RenderingServer::ViewportClearMode);