diff options
112 files changed, 4829 insertions, 1770 deletions
diff --git a/core/input/input_map.cpp b/core/input/input_map.cpp index 878ce820fb..5b96babe44 100644 --- a/core/input/input_map.cpp +++ b/core/input/input_map.cpp @@ -511,6 +511,7 @@ const OrderedHashMap<String, List<Ref<InputEvent>>> &InputMap::get_builtins() { // Text Backspace and Delete inputs = List<Ref<InputEvent>>(); inputs.push_back(InputEventKey::create_reference(KEY_BACKSPACE)); + inputs.push_back(InputEventKey::create_reference(KEY_BACKSPACE | KEY_MASK_SHIFT)); default_builtin_cache.insert("ui_text_backspace", inputs); inputs = List<Ref<InputEvent>>(); diff --git a/core/templates/bin_sorted_array.h b/core/templates/bin_sorted_array.h new file mode 100644 index 0000000000..be9d0b5475 --- /dev/null +++ b/core/templates/bin_sorted_array.h @@ -0,0 +1,181 @@ +/*************************************************************************/ +/* bin_sorted_array.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef BIN_SORTED_ARRAY_H +#define BIN_SORTED_ARRAY_H + +#include "core/templates/local_vector.h" +#include "core/templates/paged_array.h" + +template <class T> +class BinSortedArray { + PagedArray<T> array; + LocalVector<uint64_t> bin_limits; + + // Implement if elements need to keep track of their own index in the array. + _FORCE_INLINE_ virtual void _update_idx(T &r_element, uint64_t p_idx) {} + + _FORCE_INLINE_ void _swap(uint64_t p_a, uint64_t p_b) { + SWAP(array[p_a], array[p_b]); + _update_idx(array[p_a], p_a); + _update_idx(array[p_b], p_b); + } + +public: + uint64_t insert(T &p_element, uint64_t p_bin) { + array.push_back(p_element); + uint64_t new_idx = array.size() - 1; + _update_idx(p_element, new_idx); + bin_limits[0] = new_idx; + if (p_bin != 0) { + new_idx = move(new_idx, p_bin); + } + return new_idx; + } + + uint64_t move(uint64_t p_idx, uint64_t p_bin) { + ERR_FAIL_COND_V(p_idx >= array.size(), -1); + + uint64_t current_bin = bin_limits.size() - 1; + while (p_idx > bin_limits[current_bin]) { + current_bin--; + } + + if (p_bin == current_bin) { + return p_idx; + } + + uint64_t current_idx = p_idx; + if (p_bin > current_bin) { + while (p_bin > current_bin) { + uint64_t swap_idx = 0; + + if (current_bin == bin_limits.size() - 1) { + bin_limits.push_back(0); + } else { + bin_limits[current_bin + 1]++; + swap_idx = bin_limits[current_bin + 1]; + } + + if (current_idx != swap_idx) { + _swap(current_idx, swap_idx); + current_idx = swap_idx; + } + + current_bin++; + } + } else { + while (p_bin < current_bin) { + uint64_t swap_idx = bin_limits[current_bin]; + + if (current_idx != swap_idx) { + _swap(current_idx, swap_idx); + } + + if (current_bin == bin_limits.size() - 1 && bin_limits[current_bin] == 0) { + bin_limits.resize(bin_limits.size() - 1); + } else { + bin_limits[current_bin]--; + } + current_idx = swap_idx; + current_bin--; + } + } + + return current_idx; + } + + void remove(uint64_t p_idx) { + ERR_FAIL_COND(p_idx >= array.size()); + uint64_t new_idx = move(p_idx, 0); + uint64_t swap_idx = array.size() - 1; + + if (new_idx != swap_idx) { + _swap(new_idx, swap_idx); + } + + if (bin_limits[0] > 0) { + bin_limits[0]--; + } + + array.pop_back(); + } + + void set_page_pool(PagedArrayPool<T> *p_page_pool) { + array.set_page_pool(p_page_pool); + } + + _FORCE_INLINE_ const T &operator[](uint64_t p_index) const { + return array[p_index]; + } + + _FORCE_INLINE_ T &operator[](uint64_t p_index) { + return array[p_index]; + } + + int get_bin_count() { + if (array.size() == 0) { + return 0; + } + return bin_limits.size(); + } + + int get_bin_start(int p_bin) { + ERR_FAIL_COND_V(p_bin >= get_bin_count(), ~0U); + if ((unsigned int)p_bin == bin_limits.size() - 1) { + return 0; + } + return bin_limits[p_bin + 1] + 1; + } + + int get_bin_size(int p_bin) { + ERR_FAIL_COND_V(p_bin >= get_bin_count(), 0); + if ((unsigned int)p_bin == bin_limits.size() - 1) { + return bin_limits[p_bin] + 1; + } + return bin_limits[p_bin] - bin_limits[p_bin + 1]; + } + + void reset() { + array.reset(); + bin_limits.clear(); + bin_limits.push_back(0); + } + + BinSortedArray() { + bin_limits.push_back(0); + } + + virtual ~BinSortedArray() { + reset(); + } +}; + +#endif //BIN_SORTED_ARRAY_H diff --git a/doc/classes/Camera3D.xml b/doc/classes/Camera3D.xml index 30f6c2b951..016eb05308 100644 --- a/doc/classes/Camera3D.xml +++ b/doc/classes/Camera3D.xml @@ -55,10 +55,19 @@ <argument index="0" name="world_point" type="Vector3"> </argument> <description> - Returns [code]true[/code] if the given position is behind the camera. + Returns [code]true[/code] if the given position is behind the camera (the blue part of the linked diagram). [url=https://raw.githubusercontent.com/godotengine/godot-docs/master/img/camera3d_position_frustum.png]See this diagram[/url] for an overview of position query methods. [b]Note:[/b] A position which returns [code]false[/code] may still be outside the camera's field of view. </description> </method> + <method name="is_position_in_frustum" qualifiers="const"> + <return type="bool"> + </return> + <argument index="0" name="world_point" type="Vector3"> + </argument> + <description> + Returns [code]true[/code] if the given position is inside the camera's frustum (the green part of the linked diagram). [url=https://raw.githubusercontent.com/godotengine/godot-docs/master/img/camera3d_position_frustum.png]See this diagram[/url] for an overview of position query methods. + </description> + </method> <method name="make_current"> <return type="void"> </return> diff --git a/doc/classes/GeometryInstance3D.xml b/doc/classes/GeometryInstance3D.xml index b2c3bfc3ed..667ca2dacf 100644 --- a/doc/classes/GeometryInstance3D.xml +++ b/doc/classes/GeometryInstance3D.xml @@ -52,25 +52,21 @@ </member> <member name="lod_bias" type="float" setter="set_lod_bias" getter="get_lod_bias" default="1.0"> </member> - <member name="lod_max_distance" type="float" setter="set_lod_max_distance" getter="get_lod_max_distance" default="0.0"> - The GeometryInstance3D's max LOD distance. - [b]Note:[/b] This property currently has no effect. + <member name="material_override" type="Material" setter="set_material_override" getter="get_material_override"> + The material override for the whole geometry. + If a material is assigned to this property, it will be used instead of any material set in any material slot of the mesh. </member> - <member name="lod_max_hysteresis" type="float" setter="set_lod_max_hysteresis" getter="get_lod_max_hysteresis" default="0.0"> - The GeometryInstance3D's max LOD margin. - [b]Note:[/b] This property currently has no effect. + <member name="visibility_range_begin" type="float" setter="set_visibility_range_begin" getter="get_visibility_range_begin" default="0.0"> + Starting distance from which the GeometryInstance3D will be visible, taking [member visibility_range_begin_margin] into account as well. The default value of 0 is used to disable the range check. </member> - <member name="lod_min_distance" type="float" setter="set_lod_min_distance" getter="get_lod_min_distance" default="0.0"> - The GeometryInstance3D's min LOD distance. - [b]Note:[/b] This property currently has no effect. + <member name="visibility_range_begin_margin" type="float" setter="set_visibility_range_begin_margin" getter="get_visibility_range_begin_margin" default="0.0"> + Margin for the [member visibility_range_begin] threshold. The GeometryInstance3D will only change its visibility state when it goes over or under the [member visibility_range_begin] threshold by this amount. </member> - <member name="lod_min_hysteresis" type="float" setter="set_lod_min_hysteresis" getter="get_lod_min_hysteresis" default="0.0"> - The GeometryInstance3D's min LOD margin. - [b]Note:[/b] This property currently has no effect. + <member name="visibility_range_end" type="float" setter="set_visibility_range_end" getter="get_visibility_range_end" default="0.0"> + Distance from which the GeometryInstance3D will be hidden, taking [member visibility_range_end_margin] into account as well. The default value of 0 is used to disable the range check.. </member> - <member name="material_override" type="Material" setter="set_material_override" getter="get_material_override"> - The material override for the whole geometry. - If a material is assigned to this property, it will be used instead of any material set in any material slot of the mesh. + <member name="visibility_range_end_margin" type="float" setter="set_visibility_range_end_margin" getter="get_visibility_range_end_margin" default="0.0"> + Margin for the [member visibility_range_end] threshold. The GeometryInstance3D will only change its visibility state when it goes over or under the [member visibility_range_end] threshold by this amount. </member> </members> <constants> diff --git a/doc/classes/Node3D.xml b/doc/classes/Node3D.xml index a6237708c6..1c161803db 100644 --- a/doc/classes/Node3D.xml +++ b/doc/classes/Node3D.xml @@ -20,7 +20,7 @@ Forces the transform to update. Transform changes in physics are not instant for performance reasons. Transforms are accumulated and then set. Use this if you need an up-to-date transform when doing physics operations. </description> </method> - <method name="get_parent_spatial" qualifiers="const"> + <method name="get_parent_node_3d" qualifiers="const"> <return type="Node3D"> </return> <description> @@ -310,6 +310,9 @@ <member name="transform" type="Transform3D" setter="set_transform" getter="get_transform" default="Transform3D( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0 )"> Local space [Transform3D] of this node, with respect to the parent node. </member> + <member name="visibility_parent" type="NodePath" setter="set_visibility_parent" getter="get_visibility_parent" default="NodePath("")"> + Defines the visibility range parent for this node and its subtree. The visibility parent must be a GeometryInstance3D. Any visual instance will only be visible if the visibility parent (and all of its visibility ancestors) is hidden by being closer to the camera than its own [member GeometryInstance3D.visibility_range_begin]. Nodes hidden via the [member Node3D.visible] property are essentially removed from the visibility dependency tree, so dependant instances will not take the hidden node or its ancestors into account. + </member> <member name="visible" type="bool" setter="set_visible" getter="is_visible" default="true"> If [code]true[/code], this node is drawn. The node is only visible if all of its antecedents are visible as well (in other words, [method is_visible_in_tree] must return [code]true[/code]). </member> diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml index bf8bad3bbd..360ccc597e 100644 --- a/doc/classes/ProjectSettings.xml +++ b/doc/classes/ProjectSettings.xml @@ -1612,6 +1612,9 @@ </member> <member name="rendering/vulkan/staging_buffer/texture_upload_region_size_px" type="int" setter="" getter="" default="64"> </member> + <member name="rendering/xr/enabled" type="bool" setter="" getter="" default="false"> + If [code]true[/code], XR support is enabled in Godot, this ensures required shaders are compiled. + </member> <member name="world/2d/cell_size" type="int" setter="" getter="" default="100"> Cell size used for the 2D hash grid that [VisibilityNotifier2D] uses (in pixels). </member> diff --git a/doc/classes/RenderingServer.xml b/doc/classes/RenderingServer.xml index ba19176788..9b9644093f 100644 --- a/doc/classes/RenderingServer.xml +++ b/doc/classes/RenderingServer.xml @@ -1175,17 +1175,6 @@ Once finished with your RID, you will want to free the RID using the RenderingServer's [method free_rid] static method. </description> </method> - <method name="instance_geometry_set_as_instance_lod"> - <return type="void"> - </return> - <argument index="0" name="instance" type="RID"> - </argument> - <argument index="1" name="as_lod_of_instance" type="RID"> - </argument> - <description> - Not implemented in Godot 3.x. - </description> - </method> <method name="instance_geometry_set_cast_shadows_setting"> <return type="void"> </return> @@ -1197,45 +1186,45 @@ Sets the shadow casting setting to one of [enum ShadowCastingSetting]. Equivalent to [member GeometryInstance3D.cast_shadow]. </description> </method> - <method name="instance_geometry_set_draw_range"> + <method name="instance_geometry_set_flag"> <return type="void"> </return> <argument index="0" name="instance" type="RID"> </argument> - <argument index="1" name="min" type="float"> - </argument> - <argument index="2" name="max" type="float"> - </argument> - <argument index="3" name="min_margin" type="float"> + <argument index="1" name="flag" type="int" enum="RenderingServer.InstanceFlags"> </argument> - <argument index="4" name="max_margin" type="float"> + <argument index="2" name="enabled" type="bool"> </argument> <description> - Not implemented in Godot 3.x. + Sets the flag for a given [enum InstanceFlags]. See [enum InstanceFlags] for more details. </description> </method> - <method name="instance_geometry_set_flag"> + <method name="instance_geometry_set_material_override"> <return type="void"> </return> <argument index="0" name="instance" type="RID"> </argument> - <argument index="1" name="flag" type="int" enum="RenderingServer.InstanceFlags"> - </argument> - <argument index="2" name="enabled" type="bool"> + <argument index="1" name="material" type="RID"> </argument> <description> - Sets the flag for a given [enum InstanceFlags]. See [enum InstanceFlags] for more details. + Sets a material that will override the material for all surfaces on the mesh associated with this instance. Equivalent to [member GeometryInstance3D.material_override]. </description> </method> - <method name="instance_geometry_set_material_override"> + <method name="instance_geometry_set_visibility_range"> <return type="void"> </return> <argument index="0" name="instance" type="RID"> </argument> - <argument index="1" name="material" type="RID"> + <argument index="1" name="min" type="float"> + </argument> + <argument index="2" name="max" type="float"> + </argument> + <argument index="3" name="min_margin" type="float"> + </argument> + <argument index="4" name="max_margin" type="float"> </argument> <description> - Sets a material that will override the material for all surfaces on the mesh associated with this instance. Equivalent to [member GeometryInstance3D.material_override]. + Sets the visibility range values for the given geometry instance. Equivalent to [member GeometryInstance3D.visibility_range_begin] and related properties. </description> </method> <method name="instance_set_base"> @@ -1341,6 +1330,17 @@ Sets the world space transform of the instance. Equivalent to [member Node3D.transform]. </description> </method> + <method name="instance_set_visibility_parent"> + <return type="void"> + </return> + <argument index="0" name="instance" type="RID"> + </argument> + <argument index="1" name="parent" type="RID"> + </argument> + <description> + Sets the visibility parent for the given instance. Equivalent to [member Node3D.visibility_parent]. + </description> + </method> <method name="instance_set_visible"> <return type="void"> </return> diff --git a/doc/classes/Sprite3D.xml b/doc/classes/Sprite3D.xml index a7d61a6bab..8c4b9e7d10 100644 --- a/doc/classes/Sprite3D.xml +++ b/doc/classes/Sprite3D.xml @@ -39,6 +39,11 @@ Emitted when the [member frame] changes. </description> </signal> + <signal name="texture_changed"> + <description> + Emitted when the [member texture] changes. + </description> + </signal> </signals> <constants> </constants> diff --git a/doc/classes/StyleBoxTexture.xml b/doc/classes/StyleBoxTexture.xml index 895d0c357d..f4a1f1f01d 100644 --- a/doc/classes/StyleBoxTexture.xml +++ b/doc/classes/StyleBoxTexture.xml @@ -127,13 +127,6 @@ The texture to use when drawing this style box. </member> </members> - <signals> - <signal name="texture_changed"> - <description> - Emitted when the stylebox's texture is changed. - </description> - </signal> - </signals> <constants> <constant name="AXIS_STRETCH_MODE_STRETCH" value="0" enum="AxisStretchMode"> Stretch the stylebox's texture. This results in visible distortion unless the texture size matches the stylebox's size perfectly. diff --git a/doc/classes/XRInterface.xml b/doc/classes/XRInterface.xml index 3bc4adcf94..fec98007c3 100644 --- a/doc/classes/XRInterface.xml +++ b/doc/classes/XRInterface.xml @@ -46,6 +46,13 @@ If supported, returns the status of our tracking. This will allow you to provide feedback to the user whether there are issues with positional tracking. </description> </method> + <method name="get_view_count"> + <return type="int"> + </return> + <description> + Returns the number of views that need to be rendered for this device. 1 for Monoscopic, 2 for Stereoscopic. + </description> + </method> <method name="initialize"> <return type="bool"> </return> @@ -57,13 +64,6 @@ While currently not used, you can activate additional interfaces. You may wish to do this if you want to track controllers from other platforms. However, at this point in time only one interface can render to an HMD. </description> </method> - <method name="is_stereo"> - <return type="bool"> - </return> - <description> - Returns [code]true[/code] if the current output of this interface is in stereo. - </description> - </method> <method name="uninitialize"> <return type="void"> </return> diff --git a/drivers/vulkan/rendering_device_vulkan.cpp b/drivers/vulkan/rendering_device_vulkan.cpp index 4cb878eb4d..c69b516f37 100644 --- a/drivers/vulkan/rendering_device_vulkan.cpp +++ b/drivers/vulkan/rendering_device_vulkan.cpp @@ -3244,7 +3244,7 @@ bool RenderingDeviceVulkan::texture_is_format_supported_for_usage(DataFormat p_f /**** ATTACHMENT ****/ /********************/ -VkRenderPass RenderingDeviceVulkan::_render_pass_create(const Vector<AttachmentFormat> &p_format, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, int *r_color_attachment_count) { +VkRenderPass RenderingDeviceVulkan::_render_pass_create(const Vector<AttachmentFormat> &p_format, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, int *r_color_attachment_count, uint32_t p_view_count) { Vector<VkAttachmentDescription> attachments; Vector<VkAttachmentReference> color_references; Vector<VkAttachmentReference> depth_stencil_references; @@ -3469,6 +3469,31 @@ VkRenderPass RenderingDeviceVulkan::_render_pass_create(const Vector<AttachmentF render_pass_create_info.dependencyCount = 0; render_pass_create_info.pDependencies = nullptr; + const uint32_t view_mask = (1 << p_view_count) - 1; + const uint32_t correlation_mask = (1 << p_view_count) - 1; + VkRenderPassMultiviewCreateInfo render_pass_multiview_create_info; + + if (p_view_count > 1) { + const VulkanContext::MultiviewCapabilities capabilities = context->get_multiview_capabilities(); + + // For now this only works with multiview! + ERR_FAIL_COND_V_MSG(!capabilities.is_supported, VK_NULL_HANDLE, "Multiview not supported"); + + // Make sure we limit this to the number of views we support. + ERR_FAIL_COND_V_MSG(p_view_count > capabilities.max_view_count, VK_NULL_HANDLE, "Hardware does not support requested number of views for Multiview render pass"); + + render_pass_multiview_create_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_MULTIVIEW_CREATE_INFO; + render_pass_multiview_create_info.pNext = nullptr; + render_pass_multiview_create_info.subpassCount = 1; + render_pass_multiview_create_info.pViewMasks = &view_mask; + render_pass_multiview_create_info.dependencyCount = 0; + render_pass_multiview_create_info.pViewOffsets = nullptr; + render_pass_multiview_create_info.correlationMaskCount = 1; + render_pass_multiview_create_info.pCorrelationMasks = &correlation_mask; + + render_pass_create_info.pNext = &render_pass_multiview_create_info; + } + VkRenderPass render_pass; VkResult res = vkCreateRenderPass(device, &render_pass_create_info, nullptr, &render_pass); ERR_FAIL_COND_V_MSG(res, VK_NULL_HANDLE, "vkCreateRenderPass failed with error " + itos(res) + "."); @@ -3479,11 +3504,12 @@ VkRenderPass RenderingDeviceVulkan::_render_pass_create(const Vector<AttachmentF return render_pass; } -RenderingDevice::FramebufferFormatID RenderingDeviceVulkan::framebuffer_format_create(const Vector<AttachmentFormat> &p_format) { +RenderingDevice::FramebufferFormatID RenderingDeviceVulkan::framebuffer_format_create(const Vector<AttachmentFormat> &p_format, uint32_t p_view_count) { _THREAD_SAFE_METHOD_ FramebufferFormatKey key; key.attachments = p_format; + key.view_count = p_view_count; const Map<FramebufferFormatKey, FramebufferFormatID>::Element *E = framebuffer_format_cache.find(key); if (E) { @@ -3492,7 +3518,7 @@ RenderingDevice::FramebufferFormatID RenderingDeviceVulkan::framebuffer_format_c } int color_references; - VkRenderPass render_pass = _render_pass_create(p_format, INITIAL_ACTION_CLEAR, FINAL_ACTION_READ, INITIAL_ACTION_CLEAR, FINAL_ACTION_READ, &color_references); //actions don't matter for this use case + VkRenderPass render_pass = _render_pass_create(p_format, INITIAL_ACTION_CLEAR, FINAL_ACTION_READ, INITIAL_ACTION_CLEAR, FINAL_ACTION_READ, &color_references, p_view_count); //actions don't matter for this use case if (render_pass == VK_NULL_HANDLE) { //was likely invalid return INVALID_ID; @@ -3505,6 +3531,7 @@ RenderingDevice::FramebufferFormatID RenderingDeviceVulkan::framebuffer_format_c fb_format.color_attachments = color_references; fb_format.render_pass = render_pass; fb_format.samples = p_format[0].samples; + fb_format.view_count = p_view_count; framebuffer_formats[id] = fb_format; return id; } @@ -3580,11 +3607,12 @@ RID RenderingDeviceVulkan::framebuffer_create_empty(const Size2i &p_size, Textur framebuffer.format_id = framebuffer_format_create_empty(p_samples); ERR_FAIL_COND_V(p_format_check != INVALID_FORMAT_ID && framebuffer.format_id != p_format_check, RID()); framebuffer.size = p_size; + framebuffer.view_count = 1; return framebuffer_owner.make_rid(framebuffer); } -RID RenderingDeviceVulkan::framebuffer_create(const Vector<RID> &p_texture_attachments, FramebufferFormatID p_format_check) { +RID RenderingDeviceVulkan::framebuffer_create(const Vector<RID> &p_texture_attachments, FramebufferFormatID p_format_check, uint32_t p_view_count) { _THREAD_SAFE_METHOD_ Vector<AttachmentFormat> attachments; @@ -3594,6 +3622,8 @@ RID RenderingDeviceVulkan::framebuffer_create(const Vector<RID> &p_texture_attac Texture *texture = texture_owner.getornull(p_texture_attachments[i]); ERR_FAIL_COND_V_MSG(!texture, RID(), "Texture index supplied for framebuffer (" + itos(i) + ") is not a valid texture."); + ERR_FAIL_COND_V_MSG(texture->layers != p_view_count, RID(), "Layers of our texture doesn't match view count for this framebuffer"); + if (i == 0) { size.width = texture->width; size.height = texture->height; @@ -3609,7 +3639,7 @@ RID RenderingDeviceVulkan::framebuffer_create(const Vector<RID> &p_texture_attac attachments.push_back(af); } - FramebufferFormatID format_id = framebuffer_format_create(attachments); + FramebufferFormatID format_id = framebuffer_format_create(attachments, p_view_count); if (format_id == INVALID_ID) { return RID(); } @@ -3621,6 +3651,7 @@ RID RenderingDeviceVulkan::framebuffer_create(const Vector<RID> &p_texture_attac framebuffer.format_id = format_id; framebuffer.texture_ids = p_texture_attachments; framebuffer.size = size; + framebuffer.view_count = p_view_count; RID id = framebuffer_owner.make_rid(framebuffer); @@ -5904,12 +5935,13 @@ Error RenderingDeviceVulkan::_draw_list_setup_framebuffer(Framebuffer *p_framebu vk.final_color_action = p_final_color_action; vk.initial_depth_action = p_initial_depth_action; vk.final_depth_action = p_final_depth_action; + vk.view_count = p_framebuffer->view_count; if (!p_framebuffer->framebuffers.has(vk)) { //need to create this version Framebuffer::Version version; - version.render_pass = _render_pass_create(framebuffer_formats[p_framebuffer->format_id].E->key().attachments, p_initial_color_action, p_final_color_action, p_initial_depth_action, p_final_depth_action); + version.render_pass = _render_pass_create(framebuffer_formats[p_framebuffer->format_id].E->key().attachments, p_initial_color_action, p_final_color_action, p_initial_depth_action, p_final_depth_action, nullptr, p_framebuffer->view_count); VkFramebufferCreateInfo framebuffer_create_info; framebuffer_create_info.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; diff --git a/drivers/vulkan/rendering_device_vulkan.h b/drivers/vulkan/rendering_device_vulkan.h index a2527d5c33..f4fe9cf956 100644 --- a/drivers/vulkan/rendering_device_vulkan.h +++ b/drivers/vulkan/rendering_device_vulkan.h @@ -234,7 +234,12 @@ class RenderingDeviceVulkan : public RenderingDevice { struct FramebufferFormatKey { Vector<AttachmentFormat> attachments; + uint32_t view_count = 1; bool operator<(const FramebufferFormatKey &p_key) const { + if (view_count != p_key.view_count) { + return view_count < p_key.view_count; + } + int as = attachments.size(); int bs = p_key.attachments.size(); if (as != bs) { @@ -261,7 +266,7 @@ class RenderingDeviceVulkan : public RenderingDevice { } }; - VkRenderPass _render_pass_create(const Vector<AttachmentFormat> &p_format, InitialAction p_initial_action, FinalAction p_final_action, InitialAction p_initial_depth_action, FinalAction p_final_depthcolor_action, int *r_color_attachment_count = nullptr); + VkRenderPass _render_pass_create(const Vector<AttachmentFormat> &p_format, InitialAction p_initial_action, FinalAction p_final_action, InitialAction p_initial_depth_action, FinalAction p_final_depthcolor_action, int *r_color_attachment_count = nullptr, uint32_t p_view_count = 1); // This is a cache and it's never freed, it ensures // IDs for a given format are always unique. @@ -271,6 +276,7 @@ class RenderingDeviceVulkan : public RenderingDevice { VkRenderPass render_pass = VK_NULL_HANDLE; //here for constructing shaders, never used, see section (7.2. Render Pass Compatibility from Vulkan spec) int color_attachments = 0; //used for pipeline validation TextureSamples samples; + uint32_t view_count = 1; // number of views }; Map<FramebufferFormatID, FramebufferFormat> framebuffer_formats; @@ -282,11 +288,16 @@ class RenderingDeviceVulkan : public RenderingDevice { FinalAction final_color_action; InitialAction initial_depth_action; FinalAction final_depth_action; + uint32_t view_count; bool operator<(const VersionKey &p_key) const { if (initial_color_action == p_key.initial_color_action) { if (final_color_action == p_key.final_color_action) { if (initial_depth_action == p_key.initial_depth_action) { - return final_depth_action < p_key.final_depth_action; + if (final_depth_action == p_key.final_depth_action) { + return view_count < p_key.view_count; + } else { + return final_depth_action < p_key.final_depth_action; + } } else { return initial_depth_action < p_key.initial_depth_action; } @@ -309,6 +320,7 @@ class RenderingDeviceVulkan : public RenderingDevice { Map<VersionKey, Version> framebuffers; Size2 size; + uint32_t view_count; }; RID_Owner<Framebuffer, true> framebuffer_owner; @@ -938,11 +950,11 @@ public: /**** FRAMEBUFFER ****/ /*********************/ - virtual FramebufferFormatID framebuffer_format_create(const Vector<AttachmentFormat> &p_format); + virtual FramebufferFormatID framebuffer_format_create(const Vector<AttachmentFormat> &p_format, uint32_t p_view_count = 1); virtual FramebufferFormatID framebuffer_format_create_empty(TextureSamples p_samples = TEXTURE_SAMPLES_1); virtual TextureSamples framebuffer_format_get_texture_samples(FramebufferFormatID p_format); - virtual RID framebuffer_create(const Vector<RID> &p_texture_attachments, FramebufferFormatID p_format_check = INVALID_ID); + virtual RID framebuffer_create(const Vector<RID> &p_texture_attachments, FramebufferFormatID p_format_check = INVALID_ID, uint32_t p_view_count = 1); virtual RID framebuffer_create_empty(const Size2i &p_size, TextureSamples p_samples = TEXTURE_SAMPLES_1, FramebufferFormatID p_format_check = INVALID_ID); virtual FramebufferFormatID framebuffer_get_format(RID p_framebuffer); diff --git a/drivers/vulkan/vulkan_context.cpp b/drivers/vulkan/vulkan_context.cpp index 23c0a64568..6ed43b5d31 100644 --- a/drivers/vulkan/vulkan_context.cpp +++ b/drivers/vulkan/vulkan_context.cpp @@ -337,6 +337,9 @@ Error VulkanContext::_initialize_extensions() { extension_names[enabled_extension_count++] = VK_EXT_DEBUG_UTILS_EXTENSION_NAME; enabled_debug_utils = true; } + if (!strcmp(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME, instance_extensions[i].extensionName)) { + extension_names[enabled_extension_count++] = VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME; + } if (enabled_extension_count >= MAX_EXTENSIONS) { free(instance_extensions); ERR_FAIL_V_MSG(ERR_BUG, "Enabled extension count reaches MAX_EXTENSIONS, BUG"); @@ -504,6 +507,8 @@ Error VulkanContext::_check_capabilities() { // assume not supported until proven otherwise multiview_capabilities.is_supported = false; + multiview_capabilities.geometry_shader_is_supported = false; + multiview_capabilities.tessellation_shader_is_supported = false; multiview_capabilities.max_view_count = 0; multiview_capabilities.max_instance_count = 0; subgroup_capabilities.size = 0; @@ -529,7 +534,8 @@ Error VulkanContext::_check_capabilities() { device_features_func(gpu, &device_features); multiview_capabilities.is_supported = multiview_features.multiview; - // For now we ignore if multiview is available in geometry and tessellation as we do not currently support those + multiview_capabilities.geometry_shader_is_supported = multiview_features.multiviewGeometryShader; + multiview_capabilities.tessellation_shader_is_supported = multiview_features.multiviewTessellationShader; } // check extended properties @@ -573,7 +579,7 @@ Error VulkanContext::_check_capabilities() { #ifdef DEBUG_ENABLED print_line("- Vulkan multiview supported:"); - print_line(" max views: " + itos(multiview_capabilities.max_view_count)); + print_line(" max view count: " + itos(multiview_capabilities.max_view_count)); print_line(" max instances: " + itos(multiview_capabilities.max_instance_count)); } else { print_line("- Vulkan multiview not supported"); @@ -772,6 +778,10 @@ Error VulkanContext::_create_physical_device() { swapchainExtFound = 1; extension_names[enabled_extension_count++] = VK_KHR_SWAPCHAIN_EXTENSION_NAME; } + if (!strcmp(VK_KHR_MULTIVIEW_EXTENSION_NAME, device_extensions[i].extensionName)) { + // if multiview is supported, enable it + extension_names[enabled_extension_count++] = VK_KHR_MULTIVIEW_EXTENSION_NAME; + } if (enabled_extension_count >= MAX_EXTENSIONS) { free(device_extensions); ERR_FAIL_V_MSG(ERR_BUG, "Enabled extension count reaches MAX_EXTENSIONS, BUG"); @@ -964,6 +974,39 @@ Error VulkanContext::_create_device() { queues[1].flags = 0; sdevice.queueCreateInfoCount = 2; } + +#ifdef VK_VERSION_1_2 + VkPhysicalDeviceVulkan11Features vulkan11features; + + vulkan11features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_FEATURES; + vulkan11features.pNext = nullptr; + // !BAS! Need to figure out which ones of these we want enabled... + vulkan11features.storageBuffer16BitAccess = 0; + vulkan11features.uniformAndStorageBuffer16BitAccess = 0; + vulkan11features.storagePushConstant16 = 0; + vulkan11features.storageInputOutput16 = 0; + vulkan11features.multiview = multiview_capabilities.is_supported; + vulkan11features.multiviewGeometryShader = multiview_capabilities.geometry_shader_is_supported; + vulkan11features.multiviewTessellationShader = multiview_capabilities.tessellation_shader_is_supported; + vulkan11features.variablePointersStorageBuffer = 0; + vulkan11features.variablePointers = 0; + vulkan11features.protectedMemory = 0; + vulkan11features.samplerYcbcrConversion = 0; + vulkan11features.shaderDrawParameters = 0; + + sdevice.pNext = &vulkan11features; +#elif VK_VERSION_1_1 + VkPhysicalDeviceMultiviewFeatures multiview_features; + + multiview_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_FEATURES; + multiview_features.pNext = nullptr; + multiview_features.multiview = multiview_capabilities.is_supported; + multiview_features.multiviewGeometryShader = multiview_capabilities.geometry_shader_is_supported; + multiview_features.multiviewTessellationShader = multiview_capabilities.tessellation_shader_is_supported; + + sdevice.pNext = &multiview_features; +#endif + err = vkCreateDevice(gpu, &sdevice, nullptr, &device); ERR_FAIL_COND_V(err, ERR_CANT_CREATE); diff --git a/drivers/vulkan/vulkan_context.h b/drivers/vulkan/vulkan_context.h index 3d9b295c5a..738ead4f96 100644 --- a/drivers/vulkan/vulkan_context.h +++ b/drivers/vulkan/vulkan_context.h @@ -56,8 +56,10 @@ public: struct MultiviewCapabilities { bool is_supported; - int32_t max_view_count; - int32_t max_instance_count; + bool geometry_shader_is_supported; + bool tessellation_shader_is_supported; + uint32_t max_view_count; + uint32_t max_instance_count; }; private: diff --git a/editor/editor_file_system.cpp b/editor/editor_file_system.cpp index 050e55c624..c61b097ccd 100644 --- a/editor/editor_file_system.cpp +++ b/editor/editor_file_system.cpp @@ -1934,19 +1934,6 @@ void EditorFileSystem::_reimport_thread(uint32_t p_index, ImportThreadData *p_im } void EditorFileSystem::reimport_files(const Vector<String> &p_files) { - { - // Ensure that ProjectSettings::IMPORTED_FILES_PATH exists. - DirAccess *da = DirAccess::open("res://"); - if (da->change_dir(ProjectSettings::IMPORTED_FILES_PATH) != OK) { - Error err = da->make_dir_recursive(ProjectSettings::IMPORTED_FILES_PATH); - if (err || da->change_dir(ProjectSettings::IMPORTED_FILES_PATH) != OK) { - memdelete(da); - ERR_FAIL_MSG("Failed to create '" + ProjectSettings::IMPORTED_FILES_PATH + "' folder."); - } - } - memdelete(da); - } - importing = true; EditorProgress pr("reimport", TTR("(Re)Importing Assets"), p_files.size()); @@ -2177,13 +2164,9 @@ EditorFileSystem::EditorFileSystem() { scanning_changes = false; scanning_changes_done = false; - DirAccess *da = DirAccess::create(DirAccess::ACCESS_RESOURCES); - if (da->change_dir(ProjectSettings::IMPORTED_FILES_PATH) != OK) { - da->make_dir(ProjectSettings::IMPORTED_FILES_PATH); - } // This should probably also work on Unix and use the string it returns for FAT32 or exFAT + DirAccessRef da = DirAccess::create(DirAccess::ACCESS_RESOURCES); using_fat32_or_exfat = (da->get_filesystem_type() == "FAT32" || da->get_filesystem_type() == "exFAT"); - memdelete(da); scan_total = 0; update_script_classes_queued.clear(); diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index d8c5a14d4f..9a94597f0b 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -3728,10 +3728,6 @@ bool EditorNode::is_scene_in_use(const String &p_path) { return false; } -void EditorNode::register_editor_paths(bool p_for_project_manager) { - EditorPaths::create(p_for_project_manager); -} - void EditorNode::register_editor_types() { ResourceLoader::set_timestamp_on_load(true); ResourceSaver::set_timestamp_on_save(true); diff --git a/editor/editor_node.h b/editor/editor_node.h index 9625b318e0..ce42fd3c8a 100644 --- a/editor/editor_node.h +++ b/editor/editor_node.h @@ -798,7 +798,6 @@ public: Error export_preset(const String &p_preset, const String &p_path, bool p_debug, bool p_pack_only); - static void register_editor_paths(bool p_for_project_manager); static void register_editor_types(); static void unregister_editor_types(); diff --git a/editor/editor_paths.cpp b/editor/editor_paths.cpp index ddba36c187..323707ec6c 100644 --- a/editor/editor_paths.cpp +++ b/editor/editor_paths.cpp @@ -29,8 +29,12 @@ /*************************************************************************/ #include "editor_paths.h" + +#include "core/config/engine.h" +#include "core/config/project_settings.h" #include "core/io/dir_access.h" #include "core/os/os.h" +#include "main/main.h" // For `is_project_manager`. EditorPaths *EditorPaths::singleton = nullptr; @@ -41,23 +45,32 @@ bool EditorPaths::are_paths_valid() const { String EditorPaths::get_data_dir() const { return data_dir; } + String EditorPaths::get_config_dir() const { return config_dir; } + String EditorPaths::get_cache_dir() const { return cache_dir; } + +String EditorPaths::get_project_data_dir() const { + return project_data_dir; +} + bool EditorPaths::is_self_contained() const { return self_contained; } + String EditorPaths::get_self_contained_file() const { return self_contained_file; } -void EditorPaths::create(bool p_for_project_manager) { +void EditorPaths::create() { ERR_FAIL_COND(singleton != nullptr); - memnew(EditorPaths(p_for_project_manager)); + memnew(EditorPaths()); } + void EditorPaths::free() { ERR_FAIL_COND(singleton == nullptr); memdelete(singleton); @@ -71,9 +84,10 @@ void EditorPaths::_bind_methods() { ClassDB::bind_method(D_METHOD("get_self_contained_file"), &EditorPaths::get_self_contained_file); } -EditorPaths::EditorPaths(bool p_for_project_mamanger) { +EditorPaths::EditorPaths() { singleton = this; + // Self-contained mode if a `._sc_` or `_sc_` file is present in executable dir. String exe_path = OS::get_singleton()->get_executable_path().get_base_dir(); { DirAccessRef d = DirAccess::create_for_path(exe_path); @@ -100,13 +114,13 @@ EditorPaths::EditorPaths(bool p_for_project_mamanger) { cache_path = exe_path; cache_dir = data_dir.plus_file("cache"); } else { - // Typically XDG_DATA_HOME or %APPDATA% + // Typically XDG_DATA_HOME or %APPDATA%. data_path = OS::get_singleton()->get_data_path(); data_dir = data_path.plus_file(OS::get_singleton()->get_godot_dir_name()); - // Can be different from data_path e.g. on Linux or macOS + // Can be different from data_path e.g. on Linux or macOS. config_path = OS::get_singleton()->get_config_path(); config_dir = config_path.plus_file(OS::get_singleton()->get_godot_dir_name()); - // Can be different from above paths, otherwise a subfolder of data_dir + // Can be different from above paths, otherwise a subfolder of data_dir. cache_path = OS::get_singleton()->get_cache_path(); if (cache_path == data_path) { cache_dir = data_dir.plus_file("cache"); @@ -116,37 +130,85 @@ EditorPaths::EditorPaths(bool p_for_project_mamanger) { } paths_valid = (data_path != "" && config_path != "" && cache_path != ""); + ERR_FAIL_COND_MSG(!paths_valid, "Editor data, config, or cache paths are invalid."); + + // Validate or create each dir and its relevant subdirectories. - if (paths_valid) { - DirAccessRef dir = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); + DirAccessRef dir = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); + + // Data dir. + { if (dir->change_dir(data_dir) != OK) { dir->make_dir_recursive(data_dir); if (dir->change_dir(data_dir) != OK) { - ERR_PRINT("Cannot create data directory!"); + ERR_PRINT("Could not create editor data directory: " + data_dir); paths_valid = false; } } - // Validate/create cache dir + if (!dir->dir_exists("templates")) { + dir->make_dir("templates"); + } + } - if (dir->change_dir(EditorPaths::get_singleton()->get_cache_dir()) != OK) { + // Config dir. + { + if (dir->change_dir(config_dir) != OK) { + dir->make_dir_recursive(config_dir); + if (dir->change_dir(config_dir) != OK) { + ERR_PRINT("Could not create editor config directory: " + config_dir); + paths_valid = false; + } + } + + if (!dir->dir_exists("text_editor_themes")) { + dir->make_dir("text_editor_themes"); + } + if (!dir->dir_exists("script_templates")) { + dir->make_dir("script_templates"); + } + if (!dir->dir_exists("feature_profiles")) { + dir->make_dir("feature_profiles"); + } + } + + // Cache dir. + { + if (dir->change_dir(cache_dir) != OK) { dir->make_dir_recursive(cache_dir); if (dir->change_dir(cache_dir) != OK) { - ERR_PRINT("Cannot create cache directory!"); + ERR_PRINT("Could not create editor cache directory: " + cache_dir); + paths_valid = false; } } + } - if (p_for_project_mamanger) { - Engine::get_singleton()->set_shader_cache_path(get_data_dir()); - } else { - DirAccessRef dir2 = DirAccess::open("res://"); - if (dir2->change_dir(".godot") != OK) { //ensure the .godot subdir exists - if (dir2->make_dir(".godot") != OK) { - ERR_PRINT("Cannot create res://.godot directory!"); - } + // Validate or create project-specific editor data dir (`res://.godot`), + // including shader cache subdir. + + if (Main::is_project_manager()) { + // Nothing to create, use shared editor data dir for shader cache. + Engine::get_singleton()->set_shader_cache_path(data_dir); + } else { + DirAccessRef dir_res = DirAccess::create(DirAccess::ACCESS_RESOURCES); + if (dir_res->change_dir(project_data_dir) != OK) { + dir_res->make_dir_recursive(project_data_dir); + if (dir_res->change_dir(project_data_dir) != OK) { + ERR_PRINT("Could not create project data directory (" + project_data_dir + ") in: " + dir_res->get_current_dir()); + paths_valid = false; } + } + Engine::get_singleton()->set_shader_cache_path(project_data_dir); - Engine::get_singleton()->set_shader_cache_path("res://.godot"); + // Editor metadata dir. + if (!dir_res->dir_exists("editor")) { + dir_res->make_dir("editor"); + } + // Imported assets dir. + if (!dir_res->dir_exists(ProjectSettings::IMPORTED_FILES_PATH)) { + dir_res->make_dir(ProjectSettings::IMPORTED_FILES_PATH); } } + + print_line("paths valid: " + itos((int)paths_valid)); } diff --git a/editor/editor_paths.h b/editor/editor_paths.h index c1be33f5c2..2c156b7c96 100644 --- a/editor/editor_paths.h +++ b/editor/editor_paths.h @@ -28,20 +28,22 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef EDITORPATHS_H -#define EDITORPATHS_H +#ifndef EDITOR_PATHS_H +#define EDITOR_PATHS_H -#include "core/config/engine.h" +#include "core/object/class_db.h" +#include "core/string/ustring.h" class EditorPaths : public Object { GDCLASS(EditorPaths, Object) - bool paths_valid = false; - String data_dir; //editor data dir - String config_dir; //editor config dir - String cache_dir; //editor cache dir - bool self_contained = false; //true if running self contained - String self_contained_file; //self contained file with configuration + bool paths_valid = false; // If any of the paths can't be created, this is false. + String data_dir; // Editor data (templates, shader cache, etc.). + String config_dir; // Editor config (settings, profiles, themes, etc.). + String cache_dir; // Editor cache (thumbnails, tmp generated files). + String project_data_dir = "res://.godot"; // Project-specific data (metadata, shader cache, etc.). + bool self_contained = false; // Self-contained means everything goes to `editor_data` dir. + String self_contained_file; // Self-contained file with configuration. static EditorPaths *singleton; @@ -54,6 +56,8 @@ public: String get_data_dir() const; String get_config_dir() const; String get_cache_dir() const; + String get_project_data_dir() const; + bool is_self_contained() const; String get_self_contained_file() const; @@ -61,10 +65,10 @@ public: return singleton; } - static void create(bool p_for_project_manager); + static void create(); static void free(); - EditorPaths(bool p_for_project_mamanger = false); + EditorPaths(); }; -#endif // EDITORPATHS_H +#endif // EDITOR_PATHS_H diff --git a/editor/editor_settings.cpp b/editor/editor_settings.cpp index 949638f0c9..fa7980b95f 100644 --- a/editor/editor_settings.cpp +++ b/editor/editor_settings.cpp @@ -768,8 +768,8 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) { for (int i = 0; i < list.size(); i++) { String name = list[i].replace("/", "::"); set("projects/" + name, list[i]); - }; - }; + } + } if (p_extra_config->has_section("presets")) { List<String> keys; @@ -779,9 +779,9 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) { String key = E->get(); Variant val = p_extra_config->get_value("presets", key); set(key, val); - }; - }; - }; + } + } + } } void EditorSettings::_load_godot2_text_editor_theme() { @@ -871,9 +871,8 @@ static void _create_script_templates(const String &p_path) { Dictionary templates = _get_builtin_script_templates(); List<Variant> keys; templates.get_key_list(&keys); - FileAccess *file = FileAccess::create(FileAccess::ACCESS_FILESYSTEM); - - DirAccess *dir = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); + FileAccessRef file = FileAccess::create(FileAccess::ACCESS_FILESYSTEM); + DirAccessRef dir = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); dir->change_dir(p_path); for (int i = 0; i < keys.size(); i++) { if (!dir->file_exists(keys[i])) { @@ -883,9 +882,6 @@ static void _create_script_templates(const String &p_path) { file->close(); } } - - memdelete(dir); - memdelete(file); } // PUBLIC METHODS @@ -895,103 +891,48 @@ EditorSettings *EditorSettings::get_singleton() { } void EditorSettings::create() { + // IMPORTANT: create() *must* create a valid EditorSettings singleton, + // as the rest of the engine code will assume it. As such, it should never + // return (incl. via ERR_FAIL) without initializing the singleton member. + if (singleton.ptr()) { - return; //pointless + ERR_PRINT("Can't recreate EditorSettings as it already exists."); + return; } + ClassDB::register_class<EditorSettings>(); // Otherwise it can't be unserialized. + + String config_file_path; Ref<ConfigFile> extra_config = memnew(ConfigFile); + if (!EditorPaths::get_singleton()) { + ERR_PRINT("Bug (please report): EditorPaths haven't been initialized, EditorSettings cannot be created properly."); + goto fail; + } + if (EditorPaths::get_singleton()->is_self_contained()) { Error err = extra_config->load(EditorPaths::get_singleton()->get_self_contained_file()); if (err != OK) { - ERR_PRINT("Can't load extra config from path :" + EditorPaths::get_singleton()->get_self_contained_file()); + ERR_PRINT("Can't load extra config from path: " + EditorPaths::get_singleton()->get_self_contained_file()); } } - DirAccess *dir = nullptr; - - ClassDB::register_class<EditorSettings>(); //otherwise it can't be unserialized - - String config_file_path; - if (EditorPaths::get_singleton()->are_paths_valid()) { - // Validate/create data dir and subdirectories - - String data_dir = EditorPaths::get_singleton()->get_data_dir(); - - dir = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); - if (dir->change_dir(data_dir) != OK) { - dir->make_dir_recursive(data_dir); - if (dir->change_dir(data_dir) != OK) { - ERR_PRINT("Cannot create data directory!"); - memdelete(dir); - goto fail; - } - } - - if (dir->change_dir("templates") != OK) { - dir->make_dir("templates"); - } else { - dir->change_dir(".."); - } + _create_script_templates(EditorPaths::get_singleton()->get_config_dir().plus_file("script_templates")); - // Validate/create config dir and subdirectories - - if (dir->change_dir(EditorPaths::get_singleton()->get_config_dir()) != OK) { - dir->make_dir_recursive(EditorPaths::get_singleton()->get_config_dir()); - if (dir->change_dir(EditorPaths::get_singleton()->get_config_dir()) != OK) { - ERR_PRINT("Cannot create config directory!"); - memdelete(dir); - goto fail; - } - } - - if (dir->change_dir("text_editor_themes") != OK) { - dir->make_dir("text_editor_themes"); - } else { - dir->change_dir(".."); - } - - if (dir->change_dir("script_templates") != OK) { - dir->make_dir("script_templates"); - } else { - dir->change_dir(".."); - } - - if (dir->change_dir("feature_profiles") != OK) { - dir->make_dir("feature_profiles"); - } else { - dir->change_dir(".."); - } - - _create_script_templates(dir->get_current_dir().plus_file("script_templates")); - - { - // Validate/create project-specific editor settings dir. - DirAccessRef da = DirAccess::create(DirAccess::ACCESS_RESOURCES); - if (da->change_dir(EditorSettings::PROJECT_EDITOR_SETTINGS_PATH) != OK) { - Error err = da->make_dir_recursive(EditorSettings::PROJECT_EDITOR_SETTINGS_PATH); - if (err || da->change_dir(EditorSettings::PROJECT_EDITOR_SETTINGS_PATH) != OK) { - ERR_FAIL_MSG("Failed to create '" + EditorSettings::PROJECT_EDITOR_SETTINGS_PATH + "' folder."); - } - } - } - - // Validate editor config file + // Validate editor config file. + DirAccessRef dir = DirAccess::open(EditorPaths::get_singleton()->get_config_dir()); String config_file_name = "editor_settings-" + itos(VERSION_MAJOR) + ".tres"; config_file_path = EditorPaths::get_singleton()->get_config_dir().plus_file(config_file_name); if (!dir->file_exists(config_file_name)) { - memdelete(dir); goto fail; } - memdelete(dir); - singleton = ResourceLoader::load(config_file_path, "EditorSettings"); if (singleton.is_null()) { - WARN_PRINT("Could not open config file."); + ERR_PRINT("Could not load editor settings from path: " + config_file_path); goto fail; } @@ -1009,7 +950,6 @@ void EditorSettings::create() { } fail: - // patch init projects String exe_path = OS::get_singleton()->get_executable_path().get_base_dir(); @@ -1017,9 +957,9 @@ fail: Vector<String> list = extra_config->get_value("init_projects", "list"); for (int i = 0; i < list.size(); i++) { list.write[i] = exe_path.plus_file(list[i]); - }; + } extra_config->set_value("init_projects", "list", list); - }; + } singleton = Ref<EditorSettings>(memnew(EditorSettings)); singleton->save_changed_setting = true; @@ -1251,16 +1191,15 @@ void EditorSettings::add_property_hint(const PropertyInfo &p_hint) { hints[p_hint.name] = p_hint; } -// Data directories +// Editor data and config directories +// EditorPaths::create() is responsible for the creation of these directories. String EditorSettings::get_templates_dir() const { return EditorPaths::get_singleton()->get_data_dir().plus_file("templates"); } -// Config directories - String EditorSettings::get_project_settings_dir() const { - return EditorSettings::PROJECT_EDITOR_SETTINGS_PATH; + return EditorPaths::get_singleton()->get_project_data_dir().plus_file("editor"); } String EditorSettings::get_text_editor_themes_dir() const { @@ -1275,8 +1214,6 @@ String EditorSettings::get_project_script_templates_dir() const { return ProjectSettings::get_singleton()->get("editor/script/templates_search_path"); } -// Cache directory - String EditorSettings::get_feature_profiles_dir() const { return EditorPaths::get_singleton()->get_config_dir().plus_file("feature_profiles"); } diff --git a/editor/editor_settings.h b/editor/editor_settings.h index 4c361403a9..d6a9a9d1b9 100644 --- a/editor/editor_settings.h +++ b/editor/editor_settings.h @@ -47,7 +47,6 @@ class EditorSettings : public Resource { _THREAD_SAFE_CLASS_ public: - inline static const String PROJECT_EDITOR_SETTINGS_PATH = "res://.godot/editor"; struct Plugin { EditorPlugin *instance = nullptr; String path; diff --git a/editor/editor_themes.cpp b/editor/editor_themes.cpp index 64f7500e8c..fa543b7455 100644 --- a/editor/editor_themes.cpp +++ b/editor/editor_themes.cpp @@ -960,14 +960,6 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) { style_content_panel->set_border_color(dark_color_2); theme->set_stylebox("panel", "TabContainer", style_content_panel); - // this is the stylebox used in 3d and 2d viewports (no borders) - Ref<StyleBoxFlat> style_content_panel_vp = style_content_panel->duplicate(); - style_content_panel_vp->set_default_margin(SIDE_LEFT, border_width * 2); - style_content_panel_vp->set_default_margin(SIDE_TOP, default_margin_size * EDSCALE); - style_content_panel_vp->set_default_margin(SIDE_RIGHT, border_width * 2); - style_content_panel_vp->set_default_margin(SIDE_BOTTOM, border_width * 2); - theme->set_stylebox("Content", "EditorStyles", style_content_panel_vp); - // These styleboxes can be used on tabs against the base color background (e.g. nested tabs). Ref<StyleBoxFlat> style_tab_selected_odd = style_tab_selected->duplicate(); style_tab_selected_odd->set_bg_color(disabled_bg_color); @@ -977,6 +969,22 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) { style_content_panel_odd->set_bg_color(disabled_bg_color); theme->set_stylebox("panel_odd", "TabContainer", style_content_panel_odd); + // This stylebox is used in 3d and 2d viewports (no borders). + Ref<StyleBoxFlat> style_content_panel_vp = style_content_panel->duplicate(); + style_content_panel_vp->set_default_margin(SIDE_LEFT, border_width * 2); + style_content_panel_vp->set_default_margin(SIDE_TOP, default_margin_size * EDSCALE); + style_content_panel_vp->set_default_margin(SIDE_RIGHT, border_width * 2); + style_content_panel_vp->set_default_margin(SIDE_BOTTOM, border_width * 2); + theme->set_stylebox("Content", "EditorStyles", style_content_panel_vp); + + // This stylebox is used by preview tabs in the Theme Editor. + Ref<StyleBoxFlat> style_theme_preview_tab = style_tab_selected_odd->duplicate(); + style_theme_preview_tab->set_expand_margin_size(SIDE_BOTTOM, 5 * EDSCALE); + theme->set_stylebox("ThemeEditorPreviewFG", "EditorStyles", style_theme_preview_tab); + Ref<StyleBoxFlat> style_theme_preview_bg_tab = style_tab_unselected->duplicate(); + style_theme_preview_bg_tab->set_expand_margin_size(SIDE_BOTTOM, 2 * EDSCALE); + theme->set_stylebox("ThemeEditorPreviewBG", "EditorStyles", style_theme_preview_bg_tab); + // Separators theme->set_stylebox("separator", "HSeparator", make_line_stylebox(separator_color, MAX(Math::round(EDSCALE), border_width))); theme->set_stylebox("separator", "VSeparator", make_line_stylebox(separator_color, MAX(Math::round(EDSCALE), border_width), 0, 0, true)); @@ -1346,6 +1354,15 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) { style_info_3d_viewport->set_border_width_all(0); theme->set_stylebox("Information3dViewport", "EditorStyles", style_info_3d_viewport); + // Theme editor. + theme->set_color("preview_picker_overlay_color", "ThemeEditor", Color(0.1, 0.1, 0.1, 0.25)); + Color theme_preview_picker_bg_color = accent_color; + theme_preview_picker_bg_color.a = 0.2; + Ref<StyleBoxFlat> theme_preview_picker_sb = make_flat_stylebox(theme_preview_picker_bg_color, 0, 0, 0, 0); + theme_preview_picker_sb->set_border_color(accent_color); + theme_preview_picker_sb->set_border_width_all(1.0 * EDSCALE); + theme->set_stylebox("preview_picker_overlay", "ThemeEditor", theme_preview_picker_sb); + // adaptive script theme constants // for comments and elements with lower relevance const Color dim_color = Color(font_color.r, font_color.g, font_color.b, 0.5); diff --git a/editor/import/resource_importer_scene.cpp b/editor/import/resource_importer_scene.cpp index e4d88850ea..08aa2fecbb 100644 --- a/editor/import/resource_importer_scene.cpp +++ b/editor/import/resource_importer_scene.cpp @@ -1213,7 +1213,7 @@ void ResourceImporterScene::_generate_meshes(Node *p_node, const Dictionary &p_m Node3D *n = src_mesh_node; while (n) { xf = n->get_transform() * xf; - n = n->get_parent_spatial(); + n = n->get_parent_node_3d(); } Vector<uint8_t> lightmap_cache; diff --git a/editor/plugins/texture_region_editor_plugin.cpp b/editor/plugins/texture_region_editor_plugin.cpp index 9526160674..686ff0f9ef 100644 --- a/editor/plugins/texture_region_editor_plugin.cpp +++ b/editor/plugins/texture_region_editor_plugin.cpp @@ -863,13 +863,13 @@ Sprite2D *TextureRegionEditor::get_sprite() { void TextureRegionEditor::edit(Object *p_obj) { if (node_sprite) { - node_sprite->disconnect("changed", callable_mp(this, &TextureRegionEditor::_texture_changed)); + node_sprite->disconnect("texture_changed", callable_mp(this, &TextureRegionEditor::_texture_changed)); } if (node_sprite_3d) { - node_sprite_3d->disconnect("changed", callable_mp(this, &TextureRegionEditor::_texture_changed)); + node_sprite_3d->disconnect("texture_changed", callable_mp(this, &TextureRegionEditor::_texture_changed)); } if (node_ninepatch) { - node_ninepatch->disconnect("changed", callable_mp(this, &TextureRegionEditor::_texture_changed)); + node_ninepatch->disconnect("texture_changed", callable_mp(this, &TextureRegionEditor::_texture_changed)); } if (obj_styleBox.is_valid()) { obj_styleBox->disconnect("changed", callable_mp(this, &TextureRegionEditor::_texture_changed)); @@ -881,13 +881,22 @@ void TextureRegionEditor::edit(Object *p_obj) { node_sprite = Object::cast_to<Sprite2D>(p_obj); node_sprite_3d = Object::cast_to<Sprite3D>(p_obj); node_ninepatch = Object::cast_to<NinePatchRect>(p_obj); + + bool is_resource = false; if (Object::cast_to<StyleBoxTexture>(p_obj)) { obj_styleBox = Ref<StyleBoxTexture>(Object::cast_to<StyleBoxTexture>(p_obj)); + is_resource = true; } if (Object::cast_to<AtlasTexture>(p_obj)) { atlas_tex = Ref<AtlasTexture>(Object::cast_to<AtlasTexture>(p_obj)); + is_resource = true; + } + + if (is_resource) { + p_obj->connect("changed", callable_mp(this, &TextureRegionEditor::_texture_changed)); + } else { + p_obj->connect("texture_changed", callable_mp(this, &TextureRegionEditor::_texture_changed)); } - p_obj->connect("changed", callable_mp(this, &TextureRegionEditor::_texture_changed)); _edit_region(); } else { node_sprite = nullptr; diff --git a/editor/plugins/theme_editor_plugin.cpp b/editor/plugins/theme_editor_plugin.cpp index 7d38a8cb98..6607bf6cad 100644 --- a/editor/plugins/theme_editor_plugin.cpp +++ b/editor/plugins/theme_editor_plugin.cpp @@ -30,12 +30,10 @@ #include "theme_editor_plugin.h" -#include "core/io/file_access.h" #include "core/os/keyboard.h" -#include "core/version.h" +#include "editor/editor_resource_picker.h" #include "editor/editor_scale.h" #include "editor/progress_dialog.h" -#include "scene/gui/progress_bar.h" void ThemeItemImportTree::_update_items_tree() { import_items_tree->clear(); @@ -756,7 +754,9 @@ void ThemeItemImportTree::_import_selected() { return; } - ProgressDialog::get_singleton()->add_task("import_theme_items", TTR("Importing Theme Items"), selected_items.size()); + // Prevent changes from immediatelly being reported while the operation is still ongoing. + edited_theme->_freeze_change_propagation(); + ProgressDialog::get_singleton()->add_task("import_theme_items", TTR("Importing Theme Items"), selected_items.size() + 2); int idx = 0; for (Map<ThemeItem, ItemCheckedState>::Element *E = selected_items.front(); E; E = E->next()) { @@ -814,6 +814,12 @@ void ThemeItemImportTree::_import_selected() { idx++; } + // Allow changes to be reported now that the operation is finished. + ProgressDialog::get_singleton()->task_step("import_theme_items", TTR("Updating the editor"), idx++); + edited_theme->_unfreeze_and_propagate_changes(); + // Make sure the task is not ended before the editor freezes to update the Inspector. + ProgressDialog::get_singleton()->task_step("import_theme_items", TTR("Finalizing"), idx++); + ProgressDialog::get_singleton()->end_task("import_theme_items"); emit_signal("items_imported"); } @@ -1201,7 +1207,7 @@ void ThemeItemEditorDialog::_close_dialog() { } void ThemeItemEditorDialog::_dialog_about_to_show() { - ERR_FAIL_COND(edited_theme.is_null()); + ERR_FAIL_COND_MSG(edited_theme.is_null(), "Invalid state of the Theme Editor; the Theme resource is missing."); _update_edit_types(); @@ -1458,6 +1464,9 @@ void ThemeItemEditorDialog::_add_theme_type() { edited_theme->add_color_type(edit_add_type_value->get_text()); edited_theme->add_constant_type(edit_add_type_value->get_text()); _update_edit_types(); + + // Force emit a change so that other parts of the editor can update. + edited_theme->emit_changed(); } void ThemeItemEditorDialog::_add_theme_item(Theme::DataType p_data_type, String p_item_name, String p_item_type) { @@ -1488,15 +1497,24 @@ void ThemeItemEditorDialog::_add_theme_item(Theme::DataType p_data_type, String void ThemeItemEditorDialog::_remove_data_type_items(Theme::DataType p_data_type, String p_item_type) { List<StringName> names; + // Prevent changes from immediatelly being reported while the operation is still ongoing. + edited_theme->_freeze_change_propagation(); + edited_theme->get_theme_item_list(p_data_type, p_item_type, &names); for (List<StringName>::Element *E = names.front(); E; E = E->next()) { edited_theme->clear_theme_item(p_data_type, E->get(), p_item_type); } + + // Allow changes to be reported now that the operation is finished. + edited_theme->_unfreeze_and_propagate_changes(); } void ThemeItemEditorDialog::_remove_class_items() { List<StringName> names; + // Prevent changes from immediatelly being reported while the operation is still ongoing. + edited_theme->_freeze_change_propagation(); + for (int dt = 0; dt < Theme::DATA_TYPE_MAX; dt++) { Theme::DataType data_type = (Theme::DataType)dt; @@ -1509,12 +1527,18 @@ void ThemeItemEditorDialog::_remove_class_items() { } } + // Allow changes to be reported now that the operation is finished. + edited_theme->_unfreeze_and_propagate_changes(); + _update_edit_item_tree(edited_item_type); } void ThemeItemEditorDialog::_remove_custom_items() { List<StringName> names; + // Prevent changes from immediatelly being reported while the operation is still ongoing. + edited_theme->_freeze_change_propagation(); + for (int dt = 0; dt < Theme::DATA_TYPE_MAX; dt++) { Theme::DataType data_type = (Theme::DataType)dt; @@ -1527,12 +1551,18 @@ void ThemeItemEditorDialog::_remove_custom_items() { } } + // Allow changes to be reported now that the operation is finished. + edited_theme->_unfreeze_and_propagate_changes(); + _update_edit_item_tree(edited_item_type); } void ThemeItemEditorDialog::_remove_all_items() { List<StringName> names; + // Prevent changes from immediatelly being reported while the operation is still ongoing. + edited_theme->_freeze_change_propagation(); + for (int dt = 0; dt < Theme::DATA_TYPE_MAX; dt++) { Theme::DataType data_type = (Theme::DataType)dt; @@ -1543,6 +1573,9 @@ void ThemeItemEditorDialog::_remove_all_items() { } } + // Allow changes to be reported now that the operation is finished. + edited_theme->_unfreeze_and_propagate_changes(); + _update_edit_item_tree(edited_item_type); } @@ -1907,268 +1940,1310 @@ ThemeItemEditorDialog::ThemeItemEditorDialog() { confirm_closing_dialog->connect("confirmed", callable_mp(this, &ThemeItemEditorDialog::_close_dialog)); } +VBoxContainer *ThemeTypeEditor::_create_item_list(Theme::DataType p_data_type) { + VBoxContainer *items_tab = memnew(VBoxContainer); + items_tab->set_custom_minimum_size(Size2(0, 160) * EDSCALE); + data_type_tabs->add_child(items_tab); + data_type_tabs->set_tab_title(data_type_tabs->get_tab_count() - 1, ""); + + ScrollContainer *items_sc = memnew(ScrollContainer); + items_sc->set_v_size_flags(SIZE_EXPAND_FILL); + items_sc->set_enable_h_scroll(false); + items_tab->add_child(items_sc); + VBoxContainer *items_list = memnew(VBoxContainer); + items_list->set_h_size_flags(SIZE_EXPAND_FILL); + items_sc->add_child(items_list); + + HBoxContainer *item_add_hb = memnew(HBoxContainer); + items_tab->add_child(item_add_hb); + LineEdit *item_add_edit = memnew(LineEdit); + item_add_edit->set_h_size_flags(SIZE_EXPAND_FILL); + item_add_hb->add_child(item_add_edit); + item_add_edit->connect("text_entered", callable_mp(this, &ThemeTypeEditor::_item_add_lineedit_cbk), varray(p_data_type, item_add_edit)); + Button *item_add_button = memnew(Button); + item_add_button->set_text(TTR("Add")); + item_add_hb->add_child(item_add_button); + item_add_button->connect("pressed", callable_mp(this, &ThemeTypeEditor::_item_add_cbk), varray(p_data_type, item_add_edit)); + + return items_list; +} + +void ThemeTypeEditor::_update_type_list() { + ERR_FAIL_COND(edited_theme.is_null()); + + if (updating) { + return; + } + updating = true; + + Control *focused = get_focus_owner(); + if (focused) { + if (focusables.has(focused)) { + // If focus is currently on one of the internal property editors, don't update. + updating = false; + return; + } + + Node *focus_parent = focused->get_parent(); + while (focus_parent) { + Control *c = Object::cast_to<Control>(focus_parent); + if (c && focusables.has(c)) { + // If focus is currently on one of the internal property editors, don't update. + updating = false; + return; + } + + focus_parent = focus_parent->get_parent(); + } + } + + List<StringName> theme_types; + edited_theme->get_type_list(&theme_types); + theme_types.sort_custom<StringName::AlphCompare>(); + + theme_type_list->clear(); + + if (theme_types.size() > 0) { + theme_type_list->set_disabled(false); + + bool item_reselected = false; + int e_idx = 0; + for (List<StringName>::Element *E = theme_types.front(); E; E = E->next()) { + Ref<Texture2D> item_icon; + if (E->get() == "") { + item_icon = get_theme_icon("NodeDisabled", "EditorIcons"); + } else { + item_icon = EditorNode::get_singleton()->get_class_icon(E->get(), "NodeDisabled"); + } + theme_type_list->add_icon_item(item_icon, E->get()); + + if (E->get() == edited_type) { + theme_type_list->select(e_idx); + item_reselected = true; + } + e_idx++; + } + + if (!item_reselected) { + theme_type_list->select(0); + _list_type_selected(0); + } else { + _update_type_items(); + } + } else { + theme_type_list->set_disabled(true); + theme_type_list->add_item(TTR("None")); + + edited_type = ""; + _update_type_items(); + } + + updating = false; +} + +void ThemeTypeEditor::_update_type_list_debounced() { + update_debounce_timer->start(); +} + +void ThemeTypeEditor::_update_add_type_options(const String &p_filter) { + add_type_options->clear(); + + List<StringName> names; + Theme::get_default()->get_type_list(&names); + names.sort_custom<StringName::AlphCompare>(); + + for (List<StringName>::Element *E = names.front(); E; E = E->next()) { + if (!p_filter.is_subsequence_ofi(String(E->get()))) { + continue; + } + + Ref<Texture2D> item_icon; + if (E->get() == "") { + item_icon = get_theme_icon("NodeDisabled", "EditorIcons"); + } else { + item_icon = EditorNode::get_singleton()->get_class_icon(E->get(), "NodeDisabled"); + } + + add_type_options->add_item(E->get(), item_icon); + } +} + +OrderedHashMap<StringName, bool> ThemeTypeEditor::_get_type_items(String p_type_name, void (Theme::*get_list_func)(StringName, List<StringName> *) const, bool include_default) { + OrderedHashMap<StringName, bool> items; + List<StringName> names; + + if (include_default) { + names.clear(); + (Theme::get_default().operator->()->*get_list_func)(p_type_name, &names); + names.sort_custom<StringName::AlphCompare>(); + for (List<StringName>::Element *E = names.front(); E; E = E->next()) { + items[E->get()] = false; + } + } + + { + names.clear(); + (edited_theme.operator->()->*get_list_func)(p_type_name, &names); + names.sort_custom<StringName::AlphCompare>(); + for (List<StringName>::Element *E = names.front(); E; E = E->next()) { + items[E->get()] = true; + } + } + + List<StringName> keys; + for (OrderedHashMap<StringName, bool>::Element E = items.front(); E; E = E.next()) { + keys.push_back(E.key()); + } + keys.sort_custom<StringName::AlphCompare>(); + + OrderedHashMap<StringName, bool> ordered_items; + for (List<StringName>::Element *E = keys.front(); E; E = E->next()) { + ordered_items[E->get()] = items[E->get()]; + } + + return ordered_items; +} + +HBoxContainer *ThemeTypeEditor::_create_property_control(Theme::DataType p_data_type, String p_item_name, bool p_editable) { + HBoxContainer *item_control = memnew(HBoxContainer); + + HBoxContainer *item_name_container = memnew(HBoxContainer); + item_name_container->set_h_size_flags(SIZE_EXPAND_FILL); + item_name_container->set_stretch_ratio(2.0); + item_control->add_child(item_name_container); + + Label *item_name = memnew(Label); + item_name->set_h_size_flags(SIZE_EXPAND_FILL); + item_name->set_clip_text(true); + item_name->set_text(p_item_name); + item_name->set_tooltip(p_item_name); + item_name_container->add_child(item_name); + + if (p_editable) { + LineEdit *item_name_edit = memnew(LineEdit); + item_name_edit->set_h_size_flags(SIZE_EXPAND_FILL); + item_name_edit->set_text(p_item_name); + item_name_container->add_child(item_name_edit); + item_name_edit->connect("text_entered", callable_mp(this, &ThemeTypeEditor::_item_rename_entered), varray(p_data_type, p_item_name, item_name_container)); + item_name_edit->hide(); + + Button *item_rename_button = memnew(Button); + item_rename_button->set_icon(get_theme_icon("Edit", "EditorIcons")); + item_rename_button->set_tooltip(TTR("Rename Item")); + item_rename_button->set_flat(true); + item_name_container->add_child(item_rename_button); + item_rename_button->connect("pressed", callable_mp(this, &ThemeTypeEditor::_item_rename_cbk), varray(p_data_type, p_item_name, item_name_container)); + + Button *item_remove_button = memnew(Button); + item_remove_button->set_icon(get_theme_icon("Remove", "EditorIcons")); + item_remove_button->set_tooltip(TTR("Remove Item")); + item_remove_button->set_flat(true); + item_name_container->add_child(item_remove_button); + item_remove_button->connect("pressed", callable_mp(this, &ThemeTypeEditor::_item_remove_cbk), varray(p_data_type, p_item_name)); + + Button *item_rename_confirm_button = memnew(Button); + item_rename_confirm_button->set_icon(get_theme_icon("ImportCheck", "EditorIcons")); + item_rename_confirm_button->set_tooltip(TTR("Confirm Item Rename")); + item_rename_confirm_button->set_flat(true); + item_name_container->add_child(item_rename_confirm_button); + item_rename_confirm_button->connect("pressed", callable_mp(this, &ThemeTypeEditor::_item_rename_confirmed), varray(p_data_type, p_item_name, item_name_container)); + item_rename_confirm_button->hide(); + + Button *item_rename_cancel_button = memnew(Button); + item_rename_cancel_button->set_icon(get_theme_icon("ImportFail", "EditorIcons")); + item_rename_cancel_button->set_tooltip(TTR("Cancel Item Rename")); + item_rename_cancel_button->set_flat(true); + item_name_container->add_child(item_rename_cancel_button); + item_rename_cancel_button->connect("pressed", callable_mp(this, &ThemeTypeEditor::_item_rename_canceled), varray(p_data_type, p_item_name, item_name_container)); + item_rename_cancel_button->hide(); + } else { + item_name->add_theme_color_override("font_color", get_theme_color("disabled_font_color", "Editor")); + + Button *item_override_button = memnew(Button); + item_override_button->set_icon(get_theme_icon("Add", "EditorIcons")); + item_override_button->set_tooltip(TTR("Override Item")); + item_override_button->set_flat(true); + item_name_container->add_child(item_override_button); + item_override_button->connect("pressed", callable_mp(this, &ThemeTypeEditor::_item_override_cbk), varray(p_data_type, p_item_name)); + } + + return item_control; +} + +void ThemeTypeEditor::_add_focusable(Control *p_control) { + focusables.append(p_control); +} + +void ThemeTypeEditor::_update_type_items() { + bool show_default = show_default_items_button->is_pressed(); + List<StringName> names; + + focusables.clear(); + + // Colors. + { + for (int i = color_items_list->get_child_count() - 1; i >= 0; i--) { + Node *node = color_items_list->get_child(i); + node->queue_delete(); + color_items_list->remove_child(node); + } + + OrderedHashMap<StringName, bool> color_items = _get_type_items(edited_type, &Theme::get_color_list, show_default); + for (OrderedHashMap<StringName, bool>::Element E = color_items.front(); E; E = E.next()) { + HBoxContainer *item_control = _create_property_control(Theme::DATA_TYPE_COLOR, E.key(), E.get()); + ColorPickerButton *item_editor = memnew(ColorPickerButton); + item_editor->set_h_size_flags(SIZE_EXPAND_FILL); + item_control->add_child(item_editor); + + if (E.get()) { + item_editor->set_pick_color(edited_theme->get_color(E.key(), edited_type)); + item_editor->connect("color_changed", callable_mp(this, &ThemeTypeEditor::_color_item_changed), varray(E.key())); + } else { + item_editor->set_pick_color(Theme::get_default()->get_color(E.key(), edited_type)); + item_editor->set_disabled(true); + } + + _add_focusable(item_editor); + color_items_list->add_child(item_control); + } + } + + // Constants. + { + for (int i = constant_items_list->get_child_count() - 1; i >= 0; i--) { + Node *node = constant_items_list->get_child(i); + node->queue_delete(); + constant_items_list->remove_child(node); + } + + OrderedHashMap<StringName, bool> constant_items = _get_type_items(edited_type, &Theme::get_constant_list, show_default); + for (OrderedHashMap<StringName, bool>::Element E = constant_items.front(); E; E = E.next()) { + HBoxContainer *item_control = _create_property_control(Theme::DATA_TYPE_CONSTANT, E.key(), E.get()); + SpinBox *item_editor = memnew(SpinBox); + item_editor->set_h_size_flags(SIZE_EXPAND_FILL); + item_editor->set_min(-100000); + item_editor->set_max(100000); + item_editor->set_step(1); + item_editor->set_allow_lesser(true); + item_editor->set_allow_greater(true); + item_control->add_child(item_editor); + + if (E.get()) { + item_editor->set_value(edited_theme->get_constant(E.key(), edited_type)); + item_editor->connect("value_changed", callable_mp(this, &ThemeTypeEditor::_constant_item_changed), varray(E.key())); + } else { + item_editor->set_value(Theme::get_default()->get_constant(E.key(), edited_type)); + item_editor->set_editable(false); + } + + _add_focusable(item_editor); + constant_items_list->add_child(item_control); + } + } + + // Fonts. + { + for (int i = font_items_list->get_child_count() - 1; i >= 0; i--) { + Node *node = font_items_list->get_child(i); + node->queue_delete(); + font_items_list->remove_child(node); + } + + OrderedHashMap<StringName, bool> font_items = _get_type_items(edited_type, &Theme::get_font_list, show_default); + for (OrderedHashMap<StringName, bool>::Element E = font_items.front(); E; E = E.next()) { + HBoxContainer *item_control = _create_property_control(Theme::DATA_TYPE_FONT, E.key(), E.get()); + EditorResourcePicker *item_editor = memnew(EditorResourcePicker); + item_editor->set_h_size_flags(SIZE_EXPAND_FILL); + item_editor->set_base_type("Font"); + item_control->add_child(item_editor); + + if (E.get()) { + if (edited_theme->has_font(E.key(), edited_type)) { + item_editor->set_edited_resource(edited_theme->get_font(E.key(), edited_type)); + } else { + item_editor->set_edited_resource(RES()); + } + item_editor->connect("resource_selected", callable_mp(this, &ThemeTypeEditor::_edit_resource_item), varray(item_control)); + item_editor->connect("resource_changed", callable_mp(this, &ThemeTypeEditor::_font_item_changed), varray(E.key())); + } else { + if (Theme::get_default()->has_font(E.key(), edited_type)) { + item_editor->set_edited_resource(Theme::get_default()->get_font(E.key(), edited_type)); + } else { + item_editor->set_edited_resource(RES()); + } + item_editor->set_editable(false); + } + + _add_focusable(item_editor); + font_items_list->add_child(item_control); + } + } + + // Fonts sizes. + { + for (int i = font_size_items_list->get_child_count() - 1; i >= 0; i--) { + Node *node = font_size_items_list->get_child(i); + node->queue_delete(); + font_size_items_list->remove_child(node); + } + + OrderedHashMap<StringName, bool> font_size_items = _get_type_items(edited_type, &Theme::get_font_size_list, show_default); + for (OrderedHashMap<StringName, bool>::Element E = font_size_items.front(); E; E = E.next()) { + HBoxContainer *item_control = _create_property_control(Theme::DATA_TYPE_FONT_SIZE, E.key(), E.get()); + SpinBox *item_editor = memnew(SpinBox); + item_editor->set_h_size_flags(SIZE_EXPAND_FILL); + item_editor->set_min(-100000); + item_editor->set_max(100000); + item_editor->set_step(1); + item_editor->set_allow_lesser(true); + item_editor->set_allow_greater(true); + item_control->add_child(item_editor); + + if (E.get()) { + item_editor->set_value(edited_theme->get_font_size(E.key(), edited_type)); + item_editor->connect("value_changed", callable_mp(this, &ThemeTypeEditor::_font_size_item_changed), varray(E.key())); + } else { + item_editor->set_value(Theme::get_default()->get_font_size(E.key(), edited_type)); + item_editor->set_editable(false); + } + + _add_focusable(item_editor); + font_size_items_list->add_child(item_control); + } + } + + // Icons. + { + for (int i = icon_items_list->get_child_count() - 1; i >= 0; i--) { + Node *node = icon_items_list->get_child(i); + node->queue_delete(); + icon_items_list->remove_child(node); + } + + OrderedHashMap<StringName, bool> icon_items = _get_type_items(edited_type, &Theme::get_icon_list, show_default); + for (OrderedHashMap<StringName, bool>::Element E = icon_items.front(); E; E = E.next()) { + HBoxContainer *item_control = _create_property_control(Theme::DATA_TYPE_ICON, E.key(), E.get()); + EditorResourcePicker *item_editor = memnew(EditorResourcePicker); + item_editor->set_h_size_flags(SIZE_EXPAND_FILL); + item_editor->set_base_type("Texture2D"); + item_control->add_child(item_editor); + + if (E.get()) { + if (edited_theme->has_icon(E.key(), edited_type)) { + item_editor->set_edited_resource(edited_theme->get_icon(E.key(), edited_type)); + } else { + item_editor->set_edited_resource(RES()); + } + item_editor->connect("resource_selected", callable_mp(this, &ThemeTypeEditor::_edit_resource_item), varray(item_control)); + item_editor->connect("resource_changed", callable_mp(this, &ThemeTypeEditor::_icon_item_changed), varray(E.key())); + } else { + if (Theme::get_default()->has_icon(E.key(), edited_type)) { + item_editor->set_edited_resource(Theme::get_default()->get_icon(E.key(), edited_type)); + } else { + item_editor->set_edited_resource(RES()); + } + item_editor->set_editable(false); + } + + _add_focusable(item_editor); + icon_items_list->add_child(item_control); + } + } + + // Styleboxes. + { + for (int i = stylebox_items_list->get_child_count() - 1; i >= 0; i--) { + Node *node = stylebox_items_list->get_child(i); + node->queue_delete(); + stylebox_items_list->remove_child(node); + } + + if (leading_stylebox.pinned) { + HBoxContainer *item_control = _create_property_control(Theme::DATA_TYPE_STYLEBOX, leading_stylebox.item_name, true); + EditorResourcePicker *item_editor = memnew(EditorResourcePicker); + item_editor->set_h_size_flags(SIZE_EXPAND_FILL); + item_editor->set_stretch_ratio(1.5); + item_editor->set_base_type("StyleBox"); + + Button *pin_leader_button = memnew(Button); + pin_leader_button->set_flat(true); + pin_leader_button->set_toggle_mode(true); + pin_leader_button->set_pressed(true); + pin_leader_button->set_icon(get_theme_icon("Pin", "EditorIcons")); + pin_leader_button->set_tooltip(TTR("Unpin this StyleBox as a main style.")); + item_control->add_child(pin_leader_button); + pin_leader_button->connect("pressed", callable_mp(this, &ThemeTypeEditor::_unpin_leading_stylebox)); + + item_control->add_child(item_editor); + + if (leading_stylebox.stylebox.is_valid()) { + item_editor->set_edited_resource(leading_stylebox.stylebox); + } else { + item_editor->set_edited_resource(RES()); + } + item_editor->connect("resource_selected", callable_mp(this, &ThemeTypeEditor::_edit_resource_item), varray(item_control)); + item_editor->connect("resource_changed", callable_mp(this, &ThemeTypeEditor::_stylebox_item_changed), varray(leading_stylebox.item_name)); + + stylebox_items_list->add_child(item_control); + stylebox_items_list->add_child(memnew(HSeparator)); + } + + OrderedHashMap<StringName, bool> stylebox_items = _get_type_items(edited_type, &Theme::get_stylebox_list, show_default); + for (OrderedHashMap<StringName, bool>::Element E = stylebox_items.front(); E; E = E.next()) { + if (leading_stylebox.pinned && leading_stylebox.item_name == E.key()) { + continue; + } + + HBoxContainer *item_control = _create_property_control(Theme::DATA_TYPE_STYLEBOX, E.key(), E.get()); + EditorResourcePicker *item_editor = memnew(EditorResourcePicker); + item_editor->set_h_size_flags(SIZE_EXPAND_FILL); + item_editor->set_stretch_ratio(1.5); + item_editor->set_base_type("StyleBox"); + + if (E.get()) { + Ref<StyleBox> stylebox_value; + if (edited_theme->has_stylebox(E.key(), edited_type)) { + stylebox_value = edited_theme->get_stylebox(E.key(), edited_type); + item_editor->set_edited_resource(stylebox_value); + } else { + item_editor->set_edited_resource(RES()); + } + item_editor->connect("resource_selected", callable_mp(this, &ThemeTypeEditor::_edit_resource_item), varray(item_control)); + item_editor->connect("resource_changed", callable_mp(this, &ThemeTypeEditor::_stylebox_item_changed), varray(E.key())); + + Button *pin_leader_button = memnew(Button); + pin_leader_button->set_flat(true); + pin_leader_button->set_toggle_mode(true); + pin_leader_button->set_icon(get_theme_icon("Pin", "EditorIcons")); + pin_leader_button->set_tooltip(TTR("Pin this StyleBox as a main style. Editing its properties will update the same properties in all other StyleBoxes of this type.")); + item_control->add_child(pin_leader_button); + pin_leader_button->connect("pressed", callable_mp(this, &ThemeTypeEditor::_pin_leading_stylebox), varray(stylebox_value, E.key())); + } else { + if (Theme::get_default()->has_stylebox(E.key(), edited_type)) { + item_editor->set_edited_resource(Theme::get_default()->get_stylebox(E.key(), edited_type)); + } else { + item_editor->set_edited_resource(RES()); + } + item_editor->set_editable(false); + } + + item_control->add_child(item_editor); + _add_focusable(item_editor); + stylebox_items_list->add_child(item_control); + } + } +} + +void ThemeTypeEditor::_list_type_selected(int p_index) { + edited_type = theme_type_list->get_item_text(p_index); + _update_type_items(); +} + +void ThemeTypeEditor::_add_type_button_cbk() { + add_type_dialog->popup_centered(Size2(560, 420) * EDSCALE); + add_type_filter->grab_focus(); +} + +void ThemeTypeEditor::_add_type_filter_cbk(const String &p_value) { + _update_add_type_options(p_value); +} + +void ThemeTypeEditor::_add_type_options_cbk(int p_index) { + add_type_filter->set_text(add_type_options->get_item_text(p_index)); +} + +void ThemeTypeEditor::_add_type_dialog_confirmed() { + select_type(add_type_filter->get_text().strip_edges()); +} + +void ThemeTypeEditor::_add_type_dialog_entered(const String &p_value) { + select_type(p_value.strip_edges()); + add_type_dialog->hide(); +} + +void ThemeTypeEditor::_add_type_dialog_activated(int p_index) { + select_type(add_type_options->get_item_text(p_index)); + add_type_dialog->hide(); +} + +void ThemeTypeEditor::_add_default_type_items() { + List<StringName> names; + + updating = true; + // Prevent changes from immediatelly being reported while the operation is still ongoing. + edited_theme->_freeze_change_propagation(); + + { + names.clear(); + Theme::get_default()->get_icon_list(edited_type, &names); + for (List<StringName>::Element *E = names.front(); E; E = E->next()) { + if (!edited_theme->has_icon(E->get(), edited_type)) { + edited_theme->set_icon(E->get(), edited_type, Ref<Texture2D>()); + } + } + } + { + names.clear(); + Theme::get_default()->get_stylebox_list(edited_type, &names); + for (List<StringName>::Element *E = names.front(); E; E = E->next()) { + if (!edited_theme->has_stylebox(E->get(), edited_type)) { + edited_theme->set_stylebox(E->get(), edited_type, Ref<StyleBox>()); + } + } + } + { + names.clear(); + Theme::get_default()->get_font_list(edited_type, &names); + for (List<StringName>::Element *E = names.front(); E; E = E->next()) { + if (!edited_theme->has_font(E->get(), edited_type)) { + edited_theme->set_font(E->get(), edited_type, Ref<Font>()); + } + } + } + { + names.clear(); + Theme::get_default()->get_font_size_list(edited_type, &names); + for (List<StringName>::Element *E = names.front(); E; E = E->next()) { + if (!edited_theme->has_font_size(E->get(), edited_type)) { + edited_theme->set_font_size(E->get(), edited_type, Theme::get_default()->get_font_size(E->get(), edited_type)); + } + } + } + { + names.clear(); + Theme::get_default()->get_color_list(edited_type, &names); + for (List<StringName>::Element *E = names.front(); E; E = E->next()) { + if (!edited_theme->has_color(E->get(), edited_type)) { + edited_theme->set_color(E->get(), edited_type, Theme::get_default()->get_color(E->get(), edited_type)); + } + } + } + { + names.clear(); + Theme::get_default()->get_constant_list(edited_type, &names); + for (List<StringName>::Element *E = names.front(); E; E = E->next()) { + if (!edited_theme->has_constant(E->get(), edited_type)) { + edited_theme->set_constant(E->get(), edited_type, Theme::get_default()->get_constant(E->get(), edited_type)); + } + } + } + + // Allow changes to be reported now that the operation is finished. + edited_theme->_unfreeze_and_propagate_changes(); + updating = false; + + _update_type_items(); +} + +void ThemeTypeEditor::_item_add_cbk(int p_data_type, Control *p_control) { + LineEdit *le = Object::cast_to<LineEdit>(p_control); + if (le->get_text().strip_edges().is_empty()) { + return; + } + + String item_name = le->get_text().strip_edges(); + switch (p_data_type) { + case Theme::DATA_TYPE_COLOR: { + edited_theme->set_color(item_name, edited_type, Color()); + } break; + case Theme::DATA_TYPE_CONSTANT: { + edited_theme->set_constant(item_name, edited_type, 0); + } break; + case Theme::DATA_TYPE_FONT: { + edited_theme->set_font(item_name, edited_type, Ref<Font>()); + } break; + case Theme::DATA_TYPE_FONT_SIZE: { + edited_theme->set_font_size(item_name, edited_type, -1); + } break; + case Theme::DATA_TYPE_ICON: { + edited_theme->set_icon(item_name, edited_type, Ref<Texture2D>()); + } break; + case Theme::DATA_TYPE_STYLEBOX: { + edited_theme->set_stylebox(item_name, edited_type, Ref<StyleBox>()); + } break; + } + + le->set_text(""); +} + +void ThemeTypeEditor::_item_add_lineedit_cbk(String p_value, int p_data_type, Control *p_control) { + _item_add_cbk(p_data_type, p_control); +} + +void ThemeTypeEditor::_item_override_cbk(int p_data_type, String p_item_name) { + switch (p_data_type) { + case Theme::DATA_TYPE_COLOR: { + edited_theme->set_color(p_item_name, edited_type, Theme::get_default()->get_color(p_item_name, edited_type)); + } break; + case Theme::DATA_TYPE_CONSTANT: { + edited_theme->set_constant(p_item_name, edited_type, Theme::get_default()->get_constant(p_item_name, edited_type)); + } break; + case Theme::DATA_TYPE_FONT: { + edited_theme->set_font(p_item_name, edited_type, Ref<Font>()); + } break; + case Theme::DATA_TYPE_FONT_SIZE: { + edited_theme->set_font_size(p_item_name, edited_type, Theme::get_default()->get_font_size(p_item_name, edited_type)); + } break; + case Theme::DATA_TYPE_ICON: { + edited_theme->set_icon(p_item_name, edited_type, Ref<Texture2D>()); + } break; + case Theme::DATA_TYPE_STYLEBOX: { + edited_theme->set_stylebox(p_item_name, edited_type, Ref<StyleBox>()); + } break; + } +} + +void ThemeTypeEditor::_item_remove_cbk(int p_data_type, String p_item_name) { + switch (p_data_type) { + case Theme::DATA_TYPE_COLOR: { + edited_theme->clear_color(p_item_name, edited_type); + } break; + case Theme::DATA_TYPE_CONSTANT: { + edited_theme->clear_constant(p_item_name, edited_type); + } break; + case Theme::DATA_TYPE_FONT: { + edited_theme->clear_font(p_item_name, edited_type); + } break; + case Theme::DATA_TYPE_FONT_SIZE: { + edited_theme->clear_font_size(p_item_name, edited_type); + } break; + case Theme::DATA_TYPE_ICON: { + edited_theme->clear_icon(p_item_name, edited_type); + } break; + case Theme::DATA_TYPE_STYLEBOX: { + edited_theme->clear_stylebox(p_item_name, edited_type); + } break; + } +} + +void ThemeTypeEditor::_item_rename_cbk(int p_data_type, String p_item_name, Control *p_control) { + // Label + Object::cast_to<Label>(p_control->get_child(0))->hide(); + // Label buttons + Object::cast_to<Button>(p_control->get_child(2))->hide(); + Object::cast_to<Button>(p_control->get_child(3))->hide(); + + // LineEdit + Object::cast_to<LineEdit>(p_control->get_child(1))->set_text(p_item_name); + Object::cast_to<LineEdit>(p_control->get_child(1))->show(); + // LineEdit buttons + Object::cast_to<Button>(p_control->get_child(4))->show(); + Object::cast_to<Button>(p_control->get_child(5))->show(); +} + +void ThemeTypeEditor::_item_rename_confirmed(int p_data_type, String p_item_name, Control *p_control) { + LineEdit *le = Object::cast_to<LineEdit>(p_control->get_child(1)); + if (le->get_text().strip_edges().is_empty()) { + return; + } + + String new_name = le->get_text().strip_edges(); + if (new_name == p_item_name) { + _item_rename_canceled(p_data_type, p_item_name, p_control); + return; + } + + switch (p_data_type) { + case Theme::DATA_TYPE_COLOR: { + edited_theme->rename_color(p_item_name, new_name, edited_type); + } break; + case Theme::DATA_TYPE_CONSTANT: { + edited_theme->rename_constant(p_item_name, new_name, edited_type); + } break; + case Theme::DATA_TYPE_FONT: { + edited_theme->rename_font(p_item_name, new_name, edited_type); + } break; + case Theme::DATA_TYPE_FONT_SIZE: { + edited_theme->rename_font_size(p_item_name, new_name, edited_type); + } break; + case Theme::DATA_TYPE_ICON: { + edited_theme->rename_icon(p_item_name, new_name, edited_type); + } break; + case Theme::DATA_TYPE_STYLEBOX: { + edited_theme->rename_stylebox(p_item_name, new_name, edited_type); + } break; + } +} + +void ThemeTypeEditor::_item_rename_entered(String p_value, int p_data_type, String p_item_name, Control *p_control) { + _item_rename_confirmed(p_data_type, p_item_name, p_control); +} + +void ThemeTypeEditor::_item_rename_canceled(int p_data_type, String p_item_name, Control *p_control) { + // LineEdit + Object::cast_to<LineEdit>(p_control->get_child(1))->hide(); + // LineEdit buttons + Object::cast_to<Button>(p_control->get_child(4))->hide(); + Object::cast_to<Button>(p_control->get_child(5))->hide(); + + // Label + Object::cast_to<Label>(p_control->get_child(0))->show(); + // Label buttons + Object::cast_to<Button>(p_control->get_child(2))->show(); + Object::cast_to<Button>(p_control->get_child(3))->show(); +} + +void ThemeTypeEditor::_color_item_changed(Color p_value, String p_item_name) { + edited_theme->set_color(p_item_name, edited_type, p_value); +} + +void ThemeTypeEditor::_constant_item_changed(float p_value, String p_item_name) { + edited_theme->set_constant(p_item_name, edited_type, int(p_value)); +} + +void ThemeTypeEditor::_font_size_item_changed(float p_value, String p_item_name) { + edited_theme->set_font_size(p_item_name, edited_type, int(p_value)); +} + +void ThemeTypeEditor::_edit_resource_item(RES p_resource, Control *p_editor) { + EditorNode::get_singleton()->edit_resource(p_resource); +} + +void ThemeTypeEditor::_font_item_changed(Ref<Font> p_value, String p_item_name) { + edited_theme->set_font(p_item_name, edited_type, p_value); +} + +void ThemeTypeEditor::_icon_item_changed(Ref<Texture2D> p_value, String p_item_name) { + edited_theme->set_icon(p_item_name, edited_type, p_value); +} + +void ThemeTypeEditor::_stylebox_item_changed(Ref<StyleBox> p_value, String p_item_name) { + edited_theme->set_stylebox(p_item_name, edited_type, p_value); + + if (leading_stylebox.pinned && leading_stylebox.item_name == p_item_name) { + if (leading_stylebox.stylebox.is_valid()) { + leading_stylebox.stylebox->disconnect("changed", callable_mp(this, &ThemeTypeEditor::_update_stylebox_from_leading)); + } + + leading_stylebox.stylebox = p_value; + leading_stylebox.ref_stylebox = (p_value.is_valid() ? p_value->duplicate() : RES()); + if (p_value.is_valid()) { + leading_stylebox.stylebox->connect("changed", callable_mp(this, &ThemeTypeEditor::_update_stylebox_from_leading)); + } + } +} + +void ThemeTypeEditor::_pin_leading_stylebox(Ref<StyleBox> p_stylebox, String p_item_name) { + if (leading_stylebox.stylebox.is_valid()) { + leading_stylebox.stylebox->disconnect("changed", callable_mp(this, &ThemeTypeEditor::_update_stylebox_from_leading)); + } + + LeadingStylebox leader; + leader.pinned = true; + leader.item_name = p_item_name; + leader.stylebox = p_stylebox; + leader.ref_stylebox = (p_stylebox.is_valid() ? p_stylebox->duplicate() : RES()); + + leading_stylebox = leader; + if (leading_stylebox.stylebox.is_valid()) { + leading_stylebox.stylebox->connect("changed", callable_mp(this, &ThemeTypeEditor::_update_stylebox_from_leading)); + } + + _update_type_items(); +} + +void ThemeTypeEditor::_unpin_leading_stylebox() { + if (leading_stylebox.stylebox.is_valid()) { + leading_stylebox.stylebox->disconnect("changed", callable_mp(this, &ThemeTypeEditor::_update_stylebox_from_leading)); + } + + LeadingStylebox leader; + leader.pinned = false; + leading_stylebox = leader; + + _update_type_items(); +} + +void ThemeTypeEditor::_update_stylebox_from_leading() { + if (!leading_stylebox.pinned || leading_stylebox.stylebox.is_null()) { + return; + } + + // Prevent changes from immediatelly being reported while the operation is still ongoing. + edited_theme->_freeze_change_propagation(); + + List<StringName> names; + edited_theme->get_stylebox_list(edited_type, &names); + List<Ref<StyleBox>> styleboxes; + for (List<StringName>::Element *E = names.front(); E; E = E->next()) { + if (E->get() == leading_stylebox.item_name) { + continue; + } + + Ref<StyleBox> sb = edited_theme->get_stylebox(E->get(), edited_type); + if (sb->get_class() == leading_stylebox.stylebox->get_class()) { + styleboxes.push_back(sb); + } + } + + List<PropertyInfo> props; + leading_stylebox.stylebox->get_property_list(&props); + for (List<PropertyInfo>::Element *E = props.front(); E; E = E->next()) { + if (!(E->get().usage & PROPERTY_USAGE_STORAGE)) { + continue; + } + + Variant value = leading_stylebox.stylebox->get(E->get().name); + Variant ref_value = leading_stylebox.ref_stylebox->get(E->get().name); + if (value == ref_value) { + continue; + } + + for (List<Ref<StyleBox>>::Element *F = styleboxes.front(); F; F = F->next()) { + Ref<StyleBox> sb = F->get(); + sb->set(E->get().name, value); + } + } + + leading_stylebox.ref_stylebox = leading_stylebox.stylebox->duplicate(); + + // Allow changes to be reported now that the operation is finished. + edited_theme->_unfreeze_and_propagate_changes(); +} + +void ThemeTypeEditor::_notification(int p_what) { + switch (p_what) { + case NOTIFICATION_ENTER_TREE: + case NOTIFICATION_THEME_CHANGED: { + add_type_button->set_icon(get_theme_icon("Add", "EditorIcons")); + + data_type_tabs->set_tab_icon(0, get_theme_icon("Color", "EditorIcons")); + data_type_tabs->set_tab_icon(1, get_theme_icon("MemberConstant", "EditorIcons")); + data_type_tabs->set_tab_icon(2, get_theme_icon("Font", "EditorIcons")); + data_type_tabs->set_tab_icon(3, get_theme_icon("FontSize", "EditorIcons")); + data_type_tabs->set_tab_icon(4, get_theme_icon("ImageTexture", "EditorIcons")); + data_type_tabs->set_tab_icon(5, get_theme_icon("StyleBoxFlat", "EditorIcons")); + + data_type_tabs->add_theme_style_override("tab_selected", get_theme_stylebox("tab_selected_odd", "TabContainer")); + data_type_tabs->add_theme_style_override("panel", get_theme_stylebox("panel_odd", "TabContainer")); + + _update_add_type_options(); + } break; + } +} + +void ThemeTypeEditor::set_edited_theme(const Ref<Theme> &p_theme) { + if (edited_theme.is_valid()) { + edited_theme->disconnect("changed", callable_mp(this, &ThemeTypeEditor::_update_type_list_debounced)); + } + + edited_theme = p_theme; + edited_theme->connect("changed", callable_mp(this, &ThemeTypeEditor::_update_type_list_debounced)); + _update_type_list(); +} + +void ThemeTypeEditor::select_type(String p_type_name) { + edited_type = p_type_name; + bool type_exists = false; + + for (int i = 0; i < theme_type_list->get_item_count(); i++) { + String type_name = theme_type_list->get_item_text(i); + if (type_name == edited_type) { + theme_type_list->select(i); + type_exists = true; + break; + } + } + + if (type_exists) { + _update_type_items(); + } else { + edited_theme->add_icon_type(edited_type); + edited_theme->add_stylebox_type(edited_type); + edited_theme->add_font_type(edited_type); + edited_theme->add_font_size_type(edited_type); + edited_theme->add_color_type(edited_type); + edited_theme->add_constant_type(edited_type); + + _update_type_list(); + } +} + +ThemeTypeEditor::ThemeTypeEditor() { + VBoxContainer *main_vb = memnew(VBoxContainer); + add_child(main_vb); + + HBoxContainer *type_list_hb = memnew(HBoxContainer); + main_vb->add_child(type_list_hb); + + Label *type_list_label = memnew(Label); + type_list_label->set_text(TTR("Type:")); + type_list_hb->add_child(type_list_label); + + theme_type_list = memnew(OptionButton); + theme_type_list->set_h_size_flags(SIZE_EXPAND_FILL); + type_list_hb->add_child(theme_type_list); + theme_type_list->connect("item_selected", callable_mp(this, &ThemeTypeEditor::_list_type_selected)); + + add_type_button = memnew(Button); + add_type_button->set_tooltip(TTR("Add Type")); + type_list_hb->add_child(add_type_button); + add_type_button->connect("pressed", callable_mp(this, &ThemeTypeEditor::_add_type_button_cbk)); + + add_type_dialog = memnew(ConfirmationDialog); + add_type_dialog->set_title(TTR("Add Item Type")); + type_list_hb->add_child(add_type_dialog); + add_type_dialog->connect("confirmed", callable_mp(this, &ThemeTypeEditor::_add_type_dialog_confirmed)); + + VBoxContainer *add_type_vb = memnew(VBoxContainer); + add_type_dialog->add_child(add_type_vb); + + Label *add_type_filter_label = memnew(Label); + add_type_filter_label->set_text(TTR("Name:")); + add_type_vb->add_child(add_type_filter_label); + add_type_filter = memnew(LineEdit); + add_type_vb->add_child(add_type_filter); + add_type_filter->connect("text_changed", callable_mp(this, &ThemeTypeEditor::_add_type_filter_cbk)); + add_type_filter->connect("text_entered", callable_mp(this, &ThemeTypeEditor::_add_type_dialog_entered)); + Label *add_type_options_label = memnew(Label); + add_type_options_label->set_text(TTR("Node Types:")); + add_type_vb->add_child(add_type_options_label); + add_type_options = memnew(ItemList); + add_type_options->set_v_size_flags(SIZE_EXPAND_FILL); + add_type_vb->add_child(add_type_options); + add_type_options->connect("item_selected", callable_mp(this, &ThemeTypeEditor::_add_type_options_cbk)); + add_type_options->connect("item_activated", callable_mp(this, &ThemeTypeEditor::_add_type_dialog_activated)); + + HBoxContainer *type_controls = memnew(HBoxContainer); + main_vb->add_child(type_controls); + + show_default_items_button = memnew(CheckButton); + show_default_items_button->set_h_size_flags(SIZE_EXPAND_FILL); + show_default_items_button->set_text(TTR("Show Default")); + show_default_items_button->set_tooltip(TTR("Show default type items alongside items that have been overridden.")); + show_default_items_button->set_pressed(true); + type_controls->add_child(show_default_items_button); + show_default_items_button->connect("pressed", callable_mp(this, &ThemeTypeEditor::_update_type_items)); + + Button *add_default_items_button = memnew(Button); + add_default_items_button->set_h_size_flags(SIZE_EXPAND_FILL); + add_default_items_button->set_text(TTR("Override All")); + add_default_items_button->set_tooltip(TTR("Override all default type items.")); + type_controls->add_child(add_default_items_button); + add_default_items_button->connect("pressed", callable_mp(this, &ThemeTypeEditor::_add_default_type_items)); + + data_type_tabs = memnew(TabContainer); + main_vb->add_child(data_type_tabs); + data_type_tabs->set_v_size_flags(SIZE_EXPAND_FILL); + data_type_tabs->set_use_hidden_tabs_for_min_size(true); + + color_items_list = _create_item_list(Theme::DATA_TYPE_COLOR); + constant_items_list = _create_item_list(Theme::DATA_TYPE_CONSTANT); + font_items_list = _create_item_list(Theme::DATA_TYPE_FONT); + font_size_items_list = _create_item_list(Theme::DATA_TYPE_FONT_SIZE); + icon_items_list = _create_item_list(Theme::DATA_TYPE_ICON); + stylebox_items_list = _create_item_list(Theme::DATA_TYPE_STYLEBOX); + + update_debounce_timer = memnew(Timer); + update_debounce_timer->set_one_shot(true); + update_debounce_timer->set_wait_time(0.5); + update_debounce_timer->connect("timeout", callable_mp(this, &ThemeTypeEditor::_update_type_list)); + add_child(update_debounce_timer); +} + void ThemeEditor::edit(const Ref<Theme> &p_theme) { + if (theme == p_theme) { + return; + } + theme = p_theme; + theme_type_editor->set_edited_theme(p_theme); theme_edit_dialog->set_edited_theme(p_theme); - main_panel->set_theme(p_theme); - main_container->set_theme(p_theme); -} -void ThemeEditor::_propagate_redraw(Control *p_at) { - p_at->notification(NOTIFICATION_THEME_CHANGED); - p_at->minimum_size_changed(); - p_at->update(); - for (int i = 0; i < p_at->get_child_count(); i++) { - Control *a = Object::cast_to<Control>(p_at->get_child(i)); - if (a) { - _propagate_redraw(a); + for (int i = 0; i < preview_tabs_content->get_child_count(); i++) { + ThemeEditorPreview *preview_tab = Object::cast_to<ThemeEditorPreview>(preview_tabs_content->get_child(i)); + if (!preview_tab) { + continue; } + + preview_tab->set_preview_theme(p_theme); } + + theme_name->set_text(TTR("Theme") + ": " + theme->get_path().get_file()); +} + +Ref<Theme> ThemeEditor::get_edited_theme() { + return theme; } -void ThemeEditor::_refresh_interval() { - _propagate_redraw(main_panel); - _propagate_redraw(main_container); +void ThemeEditor::_theme_save_button_cbk(bool p_save_as) { + ERR_FAIL_COND_MSG(theme.is_null(), "Invalid state of the Theme Editor; the Theme resource is missing."); + + if (p_save_as) { + EditorNode::get_singleton()->save_resource_as(theme); + } else { + EditorNode::get_singleton()->save_resource(theme); + } } void ThemeEditor::_theme_edit_button_cbk() { theme_edit_dialog->popup_centered(Size2(850, 760) * EDSCALE); } +void ThemeEditor::_add_preview_button_cbk() { + preview_scene_dialog->popup_file_dialog(); +} + +void ThemeEditor::_preview_scene_dialog_cbk(const String &p_path) { + SceneThemeEditorPreview *preview_tab = memnew(SceneThemeEditorPreview); + if (!preview_tab->set_preview_scene(p_path)) { + return; + } + + _add_preview_tab(preview_tab, p_path.get_file(), get_theme_icon("PackedScene", "EditorIcons")); + preview_tab->connect("scene_invalidated", callable_mp(this, &ThemeEditor::_remove_preview_tab_invalid), varray(preview_tab)); + preview_tab->connect("scene_reloaded", callable_mp(this, &ThemeEditor::_update_preview_tab), varray(preview_tab)); +} + +void ThemeEditor::_add_preview_tab(ThemeEditorPreview *p_preview_tab, const String &p_preview_name, const Ref<Texture2D> &p_icon) { + p_preview_tab->set_preview_theme(theme); + + preview_tabs->add_tab(p_preview_name, p_icon); + preview_tabs_content->add_child(p_preview_tab); + preview_tabs->set_tab_right_button(preview_tabs->get_tab_count() - 1, EditorNode::get_singleton()->get_gui_base()->get_theme_icon("close", "Tabs")); + p_preview_tab->connect("control_picked", callable_mp(this, &ThemeEditor::_preview_control_picked)); + + preview_tabs->set_current_tab(preview_tabs->get_tab_count() - 1); +} + +void ThemeEditor::_change_preview_tab(int p_tab) { + ERR_FAIL_INDEX_MSG(p_tab, preview_tabs_content->get_child_count(), "Attempting to open a preview tab that doesn't exist."); + + for (int i = 0; i < preview_tabs_content->get_child_count(); i++) { + Control *c = Object::cast_to<Control>(preview_tabs_content->get_child(i)); + if (!c) { + continue; + } + + c->set_visible(i == p_tab); + } +} + +void ThemeEditor::_remove_preview_tab(int p_tab) { + ERR_FAIL_INDEX_MSG(p_tab, preview_tabs_content->get_child_count(), "Attempting to remove a preview tab that doesn't exist."); + + ThemeEditorPreview *preview_tab = Object::cast_to<ThemeEditorPreview>(preview_tabs_content->get_child(p_tab)); + ERR_FAIL_COND_MSG(Object::cast_to<DefaultThemeEditorPreview>(preview_tab), "Attemptying to remove the default preview tab."); + + if (preview_tab) { + preview_tab->disconnect("control_picked", callable_mp(this, &ThemeEditor::_preview_control_picked)); + if (preview_tab->is_connected("scene_invalidated", callable_mp(this, &ThemeEditor::_remove_preview_tab_invalid))) { + preview_tab->disconnect("scene_invalidated", callable_mp(this, &ThemeEditor::_remove_preview_tab_invalid)); + } + if (preview_tab->is_connected("scene_reloaded", callable_mp(this, &ThemeEditor::_update_preview_tab))) { + preview_tab->disconnect("scene_reloaded", callable_mp(this, &ThemeEditor::_update_preview_tab)); + } + + preview_tabs_content->remove_child(preview_tab); + preview_tabs->remove_tab(p_tab); + _change_preview_tab(preview_tabs->get_current_tab()); + } +} + +void ThemeEditor::_remove_preview_tab_invalid(Node *p_tab_control) { + int tab_index = p_tab_control->get_index(); + _remove_preview_tab(tab_index); +} + +void ThemeEditor::_update_preview_tab(Node *p_tab_control) { + if (!Object::cast_to<SceneThemeEditorPreview>(p_tab_control)) { + return; + } + + int tab_index = p_tab_control->get_index(); + SceneThemeEditorPreview *scene_preview = Object::cast_to<SceneThemeEditorPreview>(p_tab_control); + preview_tabs->set_tab_title(tab_index, scene_preview->get_preview_scene_path().get_file()); +} + +void ThemeEditor::_preview_control_picked(String p_class_name) { + theme_type_editor->select_type(p_class_name); +} + void ThemeEditor::_notification(int p_what) { switch (p_what) { - case NOTIFICATION_PROCESS: { - time_left -= get_process_delta_time(); - if (time_left < 0) { - time_left = 1.5; - _refresh_interval(); - } + case NOTIFICATION_ENTER_TREE: + case NOTIFICATION_THEME_CHANGED: { + preview_tabs->add_theme_style_override("tab_selected", get_theme_stylebox("ThemeEditorPreviewFG", "EditorStyles")); + preview_tabs->add_theme_style_override("tab_unselected", get_theme_stylebox("ThemeEditorPreviewBG", "EditorStyles")); + preview_tabs_content->add_theme_style_override("panel", get_theme_stylebox("panel_odd", "TabContainer")); + + add_preview_button->set_icon(get_theme_icon("Add", "EditorIcons")); } break; } } -void ThemeEditor::_bind_methods() { -} - ThemeEditor::ThemeEditor() { HBoxContainer *top_menu = memnew(HBoxContainer); add_child(top_menu); - top_menu->add_child(memnew(Label(TTR("Preview:")))); + theme_name = memnew(Label); + theme_name->set_text(TTR("Theme") + ": "); + top_menu->add_child(theme_name); + top_menu->add_spacer(false); - theme_edit_button = memnew(Button); - theme_edit_button->set_text(TTR("Manage Items")); + Button *theme_save_button = memnew(Button); + theme_save_button->set_text(TTR("Save")); + theme_save_button->set_flat(true); + theme_save_button->connect("pressed", callable_mp(this, &ThemeEditor::_theme_save_button_cbk), varray(false)); + top_menu->add_child(theme_save_button); + + Button *theme_save_as_button = memnew(Button); + theme_save_as_button->set_text(TTR("Save As...")); + theme_save_as_button->set_flat(true); + theme_save_as_button->connect("pressed", callable_mp(this, &ThemeEditor::_theme_save_button_cbk), varray(true)); + top_menu->add_child(theme_save_as_button); + + top_menu->add_child(memnew(VSeparator)); + + Button *theme_edit_button = memnew(Button); + theme_edit_button->set_text(TTR("Manage Items...")); theme_edit_button->set_tooltip(TTR("Add, remove, organize and import Theme items.")); theme_edit_button->set_flat(true); theme_edit_button->connect("pressed", callable_mp(this, &ThemeEditor::_theme_edit_button_cbk)); top_menu->add_child(theme_edit_button); - ScrollContainer *scroll = memnew(ScrollContainer); - add_child(scroll); - scroll->set_enable_v_scroll(true); - scroll->set_enable_h_scroll(true); - scroll->set_v_size_flags(SIZE_EXPAND_FILL); - - MarginContainer *root_container = memnew(MarginContainer); - scroll->add_child(root_container); - root_container->set_theme(Theme::get_default()); - root_container->set_clip_contents(true); - root_container->set_custom_minimum_size(Size2(700, 0) * EDSCALE); - root_container->set_v_size_flags(SIZE_EXPAND_FILL); - root_container->set_h_size_flags(SIZE_EXPAND_FILL); - - //// Preview Controls //// - - main_panel = memnew(Panel); - root_container->add_child(main_panel); - - main_container = memnew(MarginContainer); - root_container->add_child(main_container); - main_container->add_theme_constant_override("margin_right", 4 * EDSCALE); - main_container->add_theme_constant_override("margin_top", 4 * EDSCALE); - main_container->add_theme_constant_override("margin_left", 4 * EDSCALE); - main_container->add_theme_constant_override("margin_bottom", 4 * EDSCALE); - - HBoxContainer *main_hb = memnew(HBoxContainer); - main_container->add_child(main_hb); - - VBoxContainer *first_vb = memnew(VBoxContainer); - main_hb->add_child(first_vb); - first_vb->set_h_size_flags(SIZE_EXPAND_FILL); - first_vb->add_theme_constant_override("separation", 10 * EDSCALE); - - first_vb->add_child(memnew(Label("Label"))); - - first_vb->add_child(memnew(Button("Button"))); - Button *bt = memnew(Button); - bt->set_text(TTR("Toggle Button")); - bt->set_toggle_mode(true); - bt->set_pressed(true); - first_vb->add_child(bt); - bt = memnew(Button); - bt->set_text(TTR("Disabled Button")); - bt->set_disabled(true); - first_vb->add_child(bt); - Button *tb = memnew(Button); - tb->set_flat(true); - tb->set_text("Button"); - first_vb->add_child(tb); - - CheckButton *cb = memnew(CheckButton); - cb->set_text("CheckButton"); - first_vb->add_child(cb); - CheckBox *cbx = memnew(CheckBox); - cbx->set_text("CheckBox"); - first_vb->add_child(cbx); - - MenuButton *test_menu_button = memnew(MenuButton); - test_menu_button->set_text("MenuButton"); - test_menu_button->get_popup()->add_item(TTR("Item")); - test_menu_button->get_popup()->add_item(TTR("Disabled Item")); - test_menu_button->get_popup()->set_item_disabled(1, true); - test_menu_button->get_popup()->add_separator(); - test_menu_button->get_popup()->add_check_item(TTR("Check Item")); - test_menu_button->get_popup()->add_check_item(TTR("Checked Item")); - test_menu_button->get_popup()->set_item_checked(4, true); - test_menu_button->get_popup()->add_separator(); - test_menu_button->get_popup()->add_radio_check_item(TTR("Radio Item")); - test_menu_button->get_popup()->add_radio_check_item(TTR("Checked Radio Item")); - test_menu_button->get_popup()->set_item_checked(7, true); - test_menu_button->get_popup()->add_separator(TTR("Named Sep.")); - - PopupMenu *test_submenu = memnew(PopupMenu); - test_menu_button->get_popup()->add_child(test_submenu); - test_submenu->set_name("submenu"); - test_menu_button->get_popup()->add_submenu_item(TTR("Submenu"), "submenu"); - test_submenu->add_item(TTR("Subitem 1")); - test_submenu->add_item(TTR("Subitem 2")); - first_vb->add_child(test_menu_button); - - OptionButton *test_option_button = memnew(OptionButton); - test_option_button->add_item("OptionButton"); - test_option_button->add_separator(); - test_option_button->add_item(TTR("Has")); - test_option_button->add_item(TTR("Many")); - test_option_button->add_item(TTR("Options")); - first_vb->add_child(test_option_button); - first_vb->add_child(memnew(ColorPickerButton)); - - VBoxContainer *second_vb = memnew(VBoxContainer); - second_vb->set_h_size_flags(SIZE_EXPAND_FILL); - main_hb->add_child(second_vb); - second_vb->add_theme_constant_override("separation", 10 * EDSCALE); - LineEdit *le = memnew(LineEdit); - le->set_text("LineEdit"); - second_vb->add_child(le); - le = memnew(LineEdit); - le->set_text(TTR("Disabled LineEdit")); - le->set_editable(false); - second_vb->add_child(le); - TextEdit *te = memnew(TextEdit); - te->set_text("TextEdit"); - te->set_custom_minimum_size(Size2(0, 100) * EDSCALE); - second_vb->add_child(te); - second_vb->add_child(memnew(SpinBox)); - - HBoxContainer *vhb = memnew(HBoxContainer); - second_vb->add_child(vhb); - vhb->set_custom_minimum_size(Size2(0, 100) * EDSCALE); - vhb->add_child(memnew(VSlider)); - VScrollBar *vsb = memnew(VScrollBar); - vsb->set_page(25); - vhb->add_child(vsb); - vhb->add_child(memnew(VSeparator)); - VBoxContainer *hvb = memnew(VBoxContainer); - vhb->add_child(hvb); - hvb->set_alignment(ALIGN_CENTER); - hvb->set_h_size_flags(SIZE_EXPAND_FILL); - hvb->add_child(memnew(HSlider)); - HScrollBar *hsb = memnew(HScrollBar); - hsb->set_page(25); - hvb->add_child(hsb); - HSlider *hs = memnew(HSlider); - hs->set_editable(false); - hvb->add_child(hs); - hvb->add_child(memnew(HSeparator)); - ProgressBar *pb = memnew(ProgressBar); - pb->set_value(50); - hvb->add_child(pb); - - VBoxContainer *third_vb = memnew(VBoxContainer); - third_vb->set_h_size_flags(SIZE_EXPAND_FILL); - third_vb->add_theme_constant_override("separation", 10 * EDSCALE); - main_hb->add_child(third_vb); - - TabContainer *tc = memnew(TabContainer); - third_vb->add_child(tc); - tc->set_custom_minimum_size(Size2(0, 135) * EDSCALE); - Control *tcc = memnew(Control); - tcc->set_name(TTR("Tab 1")); - tc->add_child(tcc); - tcc = memnew(Control); - tcc->set_name(TTR("Tab 2")); - tc->add_child(tcc); - tcc = memnew(Control); - tcc->set_name(TTR("Tab 3")); - tc->add_child(tcc); - tc->set_tab_disabled(2, true); - - Tree *test_tree = memnew(Tree); - third_vb->add_child(test_tree); - test_tree->set_custom_minimum_size(Size2(0, 175) * EDSCALE); - test_tree->add_theme_constant_override("draw_relationship_lines", 1); - - TreeItem *item = test_tree->create_item(); - item->set_text(0, "Tree"); - item = test_tree->create_item(test_tree->get_root()); - item->set_text(0, "Item"); - item = test_tree->create_item(test_tree->get_root()); - item->set_editable(0, true); - item->set_text(0, TTR("Editable Item")); - TreeItem *sub_tree = test_tree->create_item(test_tree->get_root()); - sub_tree->set_text(0, TTR("Subtree")); - item = test_tree->create_item(sub_tree); - item->set_cell_mode(0, TreeItem::CELL_MODE_CHECK); - item->set_editable(0, true); - item->set_text(0, "Check Item"); - item = test_tree->create_item(sub_tree); - item->set_cell_mode(0, TreeItem::CELL_MODE_RANGE); - item->set_editable(0, true); - item->set_range_config(0, 0, 20, 0.1); - item->set_range(0, 2); - item = test_tree->create_item(sub_tree); - item->set_cell_mode(0, TreeItem::CELL_MODE_RANGE); - item->set_editable(0, true); - item->set_text(0, TTR("Has,Many,Options")); - item->set_range(0, 2); - - main_hb->add_theme_constant_override("separation", 20 * EDSCALE); - theme_edit_dialog = memnew(ThemeItemEditorDialog); theme_edit_dialog->hide(); - add_child(theme_edit_dialog); + top_menu->add_child(theme_edit_dialog); + + HSplitContainer *main_hs = memnew(HSplitContainer); + main_hs->set_v_size_flags(SIZE_EXPAND_FILL); + add_child(main_hs); + + VBoxContainer *preview_tabs_vb = memnew(VBoxContainer); + preview_tabs_vb->set_h_size_flags(SIZE_EXPAND_FILL); + preview_tabs_vb->set_custom_minimum_size(Size2(520, 0) * EDSCALE); + preview_tabs_vb->add_theme_constant_override("separation", 2 * EDSCALE); + main_hs->add_child(preview_tabs_vb); + HBoxContainer *preview_tabbar_hb = memnew(HBoxContainer); + preview_tabs_vb->add_child(preview_tabbar_hb); + preview_tabs_content = memnew(PanelContainer); + preview_tabs_content->set_v_size_flags(SIZE_EXPAND_FILL); + preview_tabs_content->set_draw_behind_parent(true); + preview_tabs_vb->add_child(preview_tabs_content); + + preview_tabs = memnew(Tabs); + preview_tabs->set_tab_align(Tabs::ALIGN_LEFT); + preview_tabs->set_h_size_flags(SIZE_EXPAND_FILL); + preview_tabbar_hb->add_child(preview_tabs); + preview_tabs->connect("tab_changed", callable_mp(this, &ThemeEditor::_change_preview_tab)); + preview_tabs->connect("right_button_pressed", callable_mp(this, &ThemeEditor::_remove_preview_tab)); + + HBoxContainer *add_preview_button_hb = memnew(HBoxContainer); + preview_tabbar_hb->add_child(add_preview_button_hb); + add_preview_button = memnew(Button); + add_preview_button->set_text(TTR("Add Preview")); + add_preview_button_hb->add_child(add_preview_button); + add_preview_button->connect("pressed", callable_mp(this, &ThemeEditor::_add_preview_button_cbk)); + + DefaultThemeEditorPreview *default_preview_tab = memnew(DefaultThemeEditorPreview); + preview_tabs_content->add_child(default_preview_tab); + default_preview_tab->connect("control_picked", callable_mp(this, &ThemeEditor::_preview_control_picked)); + preview_tabs->add_tab(TTR("Default Preview")); + + preview_scene_dialog = memnew(EditorFileDialog); + preview_scene_dialog->set_file_mode(EditorFileDialog::FILE_MODE_OPEN_FILE); + preview_scene_dialog->set_title(TTR("Select UI Scene:")); + List<String> ext; + ResourceLoader::get_recognized_extensions_for_type("PackedScene", &ext); + for (List<String>::Element *E = ext.front(); E; E = E->next()) { + preview_scene_dialog->add_filter("*." + E->get() + "; Scene"); + } + main_hs->add_child(preview_scene_dialog); + preview_scene_dialog->connect("file_selected", callable_mp(this, &ThemeEditor::_preview_scene_dialog_cbk)); + + theme_type_editor = memnew(ThemeTypeEditor); + main_hs->add_child(theme_type_editor); + theme_type_editor->set_custom_minimum_size(Size2(360, 0) * EDSCALE); } void ThemeEditorPlugin::edit(Object *p_node) { if (Object::cast_to<Theme>(p_node)) { theme_editor->edit(Object::cast_to<Theme>(p_node)); + } else if (Object::cast_to<Font>(p_node) || Object::cast_to<StyleBox>(p_node) || Object::cast_to<Texture2D>(p_node)) { + // Do nothing, keep editing the existing theme. } else { theme_editor->edit(Ref<Theme>()); } } bool ThemeEditorPlugin::handles(Object *p_node) const { - return p_node->is_class("Theme"); + if (Object::cast_to<Theme>(p_node)) { + return true; + } + + Ref<Theme> edited_theme = theme_editor->get_edited_theme(); + if (edited_theme.is_null()) { + return false; + } + + // If we are editing a theme already and this particular resource happens to belong to it, + // then we just keep editing it, despite not being able to directly handle it. + // This only goes one layer deep, but if required this can be extended to support, say, FontData inside of Font. + bool belongs_to_theme = false; + + if (Object::cast_to<Font>(p_node)) { + Ref<Font> font_item = Object::cast_to<Font>(p_node); + List<StringName> types; + List<StringName> names; + + edited_theme->get_font_type_list(&types); + for (List<StringName>::Element *E = types.front(); E; E = E->next()) { + names.clear(); + edited_theme->get_font_list(E->get(), &names); + + for (List<StringName>::Element *F = names.front(); F; F = F->next()) { + if (font_item == edited_theme->get_font(F->get(), E->get())) { + belongs_to_theme = true; + break; + } + } + } + } else if (Object::cast_to<StyleBox>(p_node)) { + Ref<StyleBox> stylebox_item = Object::cast_to<StyleBox>(p_node); + List<StringName> types; + List<StringName> names; + + edited_theme->get_stylebox_type_list(&types); + for (List<StringName>::Element *E = types.front(); E; E = E->next()) { + names.clear(); + edited_theme->get_stylebox_list(E->get(), &names); + + for (List<StringName>::Element *F = names.front(); F; F = F->next()) { + if (stylebox_item == edited_theme->get_stylebox(F->get(), E->get())) { + belongs_to_theme = true; + break; + } + } + } + } else if (Object::cast_to<Texture2D>(p_node)) { + Ref<Texture2D> icon_item = Object::cast_to<Texture2D>(p_node); + List<StringName> types; + List<StringName> names; + + edited_theme->get_icon_type_list(&types); + for (List<StringName>::Element *E = types.front(); E; E = E->next()) { + names.clear(); + edited_theme->get_icon_list(E->get(), &names); + + for (List<StringName>::Element *F = names.front(); F; F = F->next()) { + if (icon_item == edited_theme->get_icon(F->get(), E->get())) { + belongs_to_theme = true; + break; + } + } + } + } + + return belongs_to_theme; } void ThemeEditorPlugin::make_visible(bool p_visible) { if (p_visible) { - theme_editor->set_process(true); button->show(); editor->make_bottom_panel_item_visible(theme_editor); } else { - theme_editor->set_process(false); if (theme_editor->is_visible_in_tree()) { editor->hide_bottom_panel(); } diff --git a/editor/plugins/theme_editor_plugin.h b/editor/plugins/theme_editor_plugin.h index c42ebf1a19..77baf46395 100644 --- a/editor/plugins/theme_editor_plugin.h +++ b/editor/plugins/theme_editor_plugin.h @@ -31,13 +31,13 @@ #ifndef THEME_EDITOR_PLUGIN_H #define THEME_EDITOR_PLUGIN_H -#include "scene/gui/check_box.h" -#include "scene/gui/file_dialog.h" #include "scene/gui/margin_container.h" #include "scene/gui/option_button.h" #include "scene/gui/scroll_container.h" +#include "scene/gui/tabs.h" #include "scene/gui/texture_rect.h" #include "scene/resources/theme.h" +#include "theme_editor_preview.h" #include "editor/editor_node.h" @@ -263,30 +263,123 @@ public: ThemeItemEditorDialog(); }; +class ThemeTypeEditor : public MarginContainer { + GDCLASS(ThemeTypeEditor, MarginContainer); + + Ref<Theme> edited_theme; + String edited_type; + bool updating = false; + + struct LeadingStylebox { + bool pinned = false; + StringName item_name; + Ref<StyleBox> stylebox; + Ref<StyleBox> ref_stylebox; + }; + + LeadingStylebox leading_stylebox; + + OptionButton *theme_type_list; + Button *add_type_button; + ConfirmationDialog *add_type_dialog; + LineEdit *add_type_filter; + ItemList *add_type_options; + + CheckButton *show_default_items_button; + + TabContainer *data_type_tabs; + VBoxContainer *color_items_list; + VBoxContainer *constant_items_list; + VBoxContainer *font_items_list; + VBoxContainer *font_size_items_list; + VBoxContainer *icon_items_list; + VBoxContainer *stylebox_items_list; + + Vector<Control *> focusables; + Timer *update_debounce_timer; + + VBoxContainer *_create_item_list(Theme::DataType p_data_type); + void _update_type_list(); + void _update_type_list_debounced(); + void _update_add_type_options(const String &p_filter = ""); + OrderedHashMap<StringName, bool> _get_type_items(String p_type_name, void (Theme::*get_list_func)(StringName, List<StringName> *) const, bool include_default); + HBoxContainer *_create_property_control(Theme::DataType p_data_type, String p_item_name, bool p_editable); + void _add_focusable(Control *p_control); + void _update_type_items(); + + void _list_type_selected(int p_index); + void _select_type(String p_type_name); + void _add_type_button_cbk(); + void _add_type_filter_cbk(const String &p_value); + void _add_type_options_cbk(int p_index); + void _add_type_dialog_confirmed(); + void _add_type_dialog_entered(const String &p_value); + void _add_type_dialog_activated(int p_index); + void _add_default_type_items(); + + void _item_add_cbk(int p_data_type, Control *p_control); + void _item_add_lineedit_cbk(String p_value, int p_data_type, Control *p_control); + void _item_override_cbk(int p_data_type, String p_item_name); + void _item_remove_cbk(int p_data_type, String p_item_name); + void _item_rename_cbk(int p_data_type, String p_item_name, Control *p_control); + void _item_rename_confirmed(int p_data_type, String p_item_name, Control *p_control); + void _item_rename_entered(String p_value, int p_data_type, String p_item_name, Control *p_control); + void _item_rename_canceled(int p_data_type, String p_item_name, Control *p_control); + + void _color_item_changed(Color p_value, String p_item_name); + void _constant_item_changed(float p_value, String p_item_name); + void _font_size_item_changed(float p_value, String p_item_name); + void _edit_resource_item(RES p_resource, Control *p_editor); + void _font_item_changed(Ref<Font> p_value, String p_item_name); + void _icon_item_changed(Ref<Texture2D> p_value, String p_item_name); + void _stylebox_item_changed(Ref<StyleBox> p_value, String p_item_name); + void _pin_leading_stylebox(Ref<StyleBox> p_stylebox, String p_item_name); + void _unpin_leading_stylebox(); + void _update_stylebox_from_leading(); + +protected: + void _notification(int p_what); + +public: + void set_edited_theme(const Ref<Theme> &p_theme); + void select_type(String p_type_name); + + ThemeTypeEditor(); +}; + class ThemeEditor : public VBoxContainer { GDCLASS(ThemeEditor, VBoxContainer); Ref<Theme> theme; - double time_left = 0; + Tabs *preview_tabs; + PanelContainer *preview_tabs_content; + Button *add_preview_button; + EditorFileDialog *preview_scene_dialog; - Button *theme_edit_button; - ThemeItemEditorDialog *theme_edit_dialog; + ThemeTypeEditor *theme_type_editor; - Panel *main_panel; - MarginContainer *main_container; - Tree *test_tree; + Label *theme_name; + ThemeItemEditorDialog *theme_edit_dialog; + void _theme_save_button_cbk(bool p_save_as); void _theme_edit_button_cbk(); - void _propagate_redraw(Control *p_at); - void _refresh_interval(); + + void _add_preview_button_cbk(); + void _preview_scene_dialog_cbk(const String &p_path); + void _add_preview_tab(ThemeEditorPreview *p_preview_tab, const String &p_preview_name, const Ref<Texture2D> &p_icon); + void _change_preview_tab(int p_tab); + void _remove_preview_tab(int p_tab); + void _remove_preview_tab_invalid(Node *p_tab_control); + void _update_preview_tab(Node *p_tab_control); + void _preview_control_picked(String p_class_name); protected: void _notification(int p_what); - static void _bind_methods(); public: void edit(const Ref<Theme> &p_theme); + Ref<Theme> get_edited_theme(); ThemeEditor(); }; diff --git a/editor/plugins/theme_editor_preview.cpp b/editor/plugins/theme_editor_preview.cpp new file mode 100644 index 0000000000..766f8508c1 --- /dev/null +++ b/editor/plugins/theme_editor_preview.cpp @@ -0,0 +1,464 @@ +/*************************************************************************/ +/* theme_editor_preview.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "theme_editor_preview.h" + +#include "core/input/input.h" +#include "core/math/math_funcs.h" +#include "scene/resources/packed_scene.h" + +#include "editor/editor_scale.h" + +void ThemeEditorPreview::set_preview_theme(const Ref<Theme> &p_theme) { + preview_content->set_theme(p_theme); +} + +void ThemeEditorPreview::add_preview_overlay(Control *p_overlay) { + preview_overlay->add_child(p_overlay); + p_overlay->hide(); +} + +void ThemeEditorPreview::_propagate_redraw(Control *p_at) { + p_at->notification(NOTIFICATION_THEME_CHANGED); + p_at->minimum_size_changed(); + p_at->update(); + for (int i = 0; i < p_at->get_child_count(); i++) { + Control *a = Object::cast_to<Control>(p_at->get_child(i)); + if (a) { + _propagate_redraw(a); + } + } +} + +void ThemeEditorPreview::_refresh_interval() { + // In case the project settings have changed. + preview_bg->set_color(GLOBAL_GET("rendering/environment/defaults/default_clear_color")); + + _propagate_redraw(preview_bg); + _propagate_redraw(preview_content); +} + +void ThemeEditorPreview::_preview_visibility_changed() { + set_process(is_visible()); +} + +void ThemeEditorPreview::_picker_button_cbk() { + picker_overlay->set_visible(picker_button->is_pressed()); +} + +Control *ThemeEditorPreview::_find_hovered_control(Control *p_parent, Vector2 p_mouse_position) { + Control *found = nullptr; + + for (int i = 0; i < p_parent->get_child_count(); i++) { + Control *cc = Object::cast_to<Control>(p_parent->get_child(i)); + if (!cc || !cc->is_visible()) { + continue; + } + + Rect2 crect = cc->get_rect(); + if (crect.has_point(p_mouse_position)) { + // Check if there is a child control under mouse. + if (cc->get_child_count() > 0) { + found = _find_hovered_control(cc, p_mouse_position - cc->get_position()); + } + + // If there are no applicable children, use the control itself. + if (!found) { + found = cc; + } + break; + } + } + + return found; +} + +void ThemeEditorPreview::_draw_picker_overlay() { + if (!picker_button->is_pressed()) { + return; + } + + picker_overlay->draw_rect(Rect2(Vector2(0.0, 0.0), picker_overlay->get_size()), get_theme_color("preview_picker_overlay_color", "ThemeEditor")); + if (hovered_control) { + Rect2 highlight_rect = hovered_control->get_global_rect(); + highlight_rect.position = picker_overlay->get_global_transform().affine_inverse().xform(highlight_rect.position); + + picker_overlay->draw_style_box(get_theme_stylebox("preview_picker_overlay", "ThemeEditor"), highlight_rect); + } +} + +void ThemeEditorPreview::_gui_input_picker_overlay(const Ref<InputEvent> &p_event) { + if (!picker_button->is_pressed()) { + return; + } + + Ref<InputEventMouseButton> mb = p_event; + + if (mb.is_valid() && mb->is_pressed() && mb->get_button_index() == MOUSE_BUTTON_LEFT) { + if (hovered_control) { + StringName theme_type = hovered_control->get_theme_custom_type(); + if (theme_type == StringName()) { + theme_type = hovered_control->get_class_name(); + } + + emit_signal("control_picked", theme_type); + picker_button->set_pressed(false); + picker_overlay->set_visible(false); + } + } + + Ref<InputEventMouseMotion> mm = p_event; + + if (mm.is_valid()) { + Vector2 mp = preview_content->get_local_mouse_position(); + hovered_control = _find_hovered_control(preview_content, mp); + picker_overlay->update(); + } +} + +void ThemeEditorPreview::_notification(int p_what) { + switch (p_what) { + case NOTIFICATION_ENTER_TREE: { + if (is_visible_in_tree()) { + set_process(true); + } + + connect("visibility_changed", callable_mp(this, &ThemeEditorPreview::_preview_visibility_changed)); + [[fallthrough]]; + } + case NOTIFICATION_THEME_CHANGED: { + picker_button->set_icon(get_theme_icon("ColorPick", "EditorIcons")); + } break; + case NOTIFICATION_PROCESS: { + time_left -= get_process_delta_time(); + if (time_left < 0) { + time_left = 1.5; + _refresh_interval(); + } + } break; + } +} + +void ThemeEditorPreview::_bind_methods() { + ADD_SIGNAL(MethodInfo("control_picked", PropertyInfo(Variant::STRING, "class_name"))); +} + +ThemeEditorPreview::ThemeEditorPreview() { + preview_toolbar = memnew(HBoxContainer); + add_child(preview_toolbar); + + picker_button = memnew(Button); + preview_toolbar->add_child(picker_button); + picker_button->set_flat(true); + picker_button->set_toggle_mode(true); + picker_button->set_tooltip(TTR("Toggle the control picker, allowing to visually select control types for edit.")); + picker_button->connect("pressed", callable_mp(this, &ThemeEditorPreview::_picker_button_cbk)); + + MarginContainer *preview_body = memnew(MarginContainer); + preview_body->set_custom_minimum_size(Size2(480, 0) * EDSCALE); + preview_body->set_v_size_flags(SIZE_EXPAND_FILL); + add_child(preview_body); + + ScrollContainer *preview_container = memnew(ScrollContainer); + preview_container->set_enable_v_scroll(true); + preview_container->set_enable_h_scroll(true); + preview_body->add_child(preview_container); + + MarginContainer *preview_root = memnew(MarginContainer); + preview_container->add_child(preview_root); + preview_root->set_theme(Theme::get_default()); + preview_root->set_clip_contents(true); + preview_root->set_custom_minimum_size(Size2(450, 0) * EDSCALE); + preview_root->set_v_size_flags(SIZE_EXPAND_FILL); + preview_root->set_h_size_flags(SIZE_EXPAND_FILL); + + preview_bg = memnew(ColorRect); + preview_bg->set_anchors_and_offsets_preset(PRESET_WIDE); + preview_bg->set_color(GLOBAL_GET("rendering/environment/defaults/default_clear_color")); + preview_root->add_child(preview_bg); + + preview_content = memnew(MarginContainer); + preview_root->add_child(preview_content); + preview_content->add_theme_constant_override("margin_right", 4 * EDSCALE); + preview_content->add_theme_constant_override("margin_top", 4 * EDSCALE); + preview_content->add_theme_constant_override("margin_left", 4 * EDSCALE); + preview_content->add_theme_constant_override("margin_bottom", 4 * EDSCALE); + + preview_overlay = memnew(MarginContainer); + preview_overlay->set_mouse_filter(MOUSE_FILTER_IGNORE); + preview_body->add_child(preview_overlay); + + picker_overlay = memnew(Control); + add_preview_overlay(picker_overlay); + picker_overlay->connect("draw", callable_mp(this, &ThemeEditorPreview::_draw_picker_overlay)); + picker_overlay->connect("gui_input", callable_mp(this, &ThemeEditorPreview::_gui_input_picker_overlay)); +} + +DefaultThemeEditorPreview::DefaultThemeEditorPreview() { + Panel *main_panel = memnew(Panel); + preview_content->add_child(main_panel); + + MarginContainer *main_mc = memnew(MarginContainer); + main_mc->add_theme_constant_override("margin_right", 4 * EDSCALE); + main_mc->add_theme_constant_override("margin_top", 4 * EDSCALE); + main_mc->add_theme_constant_override("margin_left", 4 * EDSCALE); + main_mc->add_theme_constant_override("margin_bottom", 4 * EDSCALE); + preview_content->add_child(main_mc); + + HBoxContainer *main_hb = memnew(HBoxContainer); + main_mc->add_child(main_hb); + main_hb->add_theme_constant_override("separation", 20 * EDSCALE); + + VBoxContainer *first_vb = memnew(VBoxContainer); + main_hb->add_child(first_vb); + first_vb->set_h_size_flags(SIZE_EXPAND_FILL); + first_vb->add_theme_constant_override("separation", 10 * EDSCALE); + + first_vb->add_child(memnew(Label("Label"))); + + first_vb->add_child(memnew(Button("Button"))); + Button *bt = memnew(Button); + bt->set_text(TTR("Toggle Button")); + bt->set_toggle_mode(true); + bt->set_pressed(true); + first_vb->add_child(bt); + bt = memnew(Button); + bt->set_text(TTR("Disabled Button")); + bt->set_disabled(true); + first_vb->add_child(bt); + Button *tb = memnew(Button); + tb->set_flat(true); + tb->set_text("Button"); + first_vb->add_child(tb); + + CheckButton *cb = memnew(CheckButton); + cb->set_text("CheckButton"); + first_vb->add_child(cb); + CheckBox *cbx = memnew(CheckBox); + cbx->set_text("CheckBox"); + first_vb->add_child(cbx); + + MenuButton *test_menu_button = memnew(MenuButton); + test_menu_button->set_text("MenuButton"); + test_menu_button->get_popup()->add_item(TTR("Item")); + test_menu_button->get_popup()->add_item(TTR("Disabled Item")); + test_menu_button->get_popup()->set_item_disabled(1, true); + test_menu_button->get_popup()->add_separator(); + test_menu_button->get_popup()->add_check_item(TTR("Check Item")); + test_menu_button->get_popup()->add_check_item(TTR("Checked Item")); + test_menu_button->get_popup()->set_item_checked(4, true); + test_menu_button->get_popup()->add_separator(); + test_menu_button->get_popup()->add_radio_check_item(TTR("Radio Item")); + test_menu_button->get_popup()->add_radio_check_item(TTR("Checked Radio Item")); + test_menu_button->get_popup()->set_item_checked(7, true); + test_menu_button->get_popup()->add_separator(TTR("Named Sep.")); + + PopupMenu *test_submenu = memnew(PopupMenu); + test_menu_button->get_popup()->add_child(test_submenu); + test_submenu->set_name("submenu"); + test_menu_button->get_popup()->add_submenu_item(TTR("Submenu"), "submenu"); + test_submenu->add_item(TTR("Subitem 1")); + test_submenu->add_item(TTR("Subitem 2")); + first_vb->add_child(test_menu_button); + + OptionButton *test_option_button = memnew(OptionButton); + test_option_button->add_item("OptionButton"); + test_option_button->add_separator(); + test_option_button->add_item(TTR("Has")); + test_option_button->add_item(TTR("Many")); + test_option_button->add_item(TTR("Options")); + first_vb->add_child(test_option_button); + first_vb->add_child(memnew(ColorPickerButton)); + + VBoxContainer *second_vb = memnew(VBoxContainer); + second_vb->set_h_size_flags(SIZE_EXPAND_FILL); + main_hb->add_child(second_vb); + second_vb->add_theme_constant_override("separation", 10 * EDSCALE); + LineEdit *le = memnew(LineEdit); + le->set_text("LineEdit"); + second_vb->add_child(le); + le = memnew(LineEdit); + le->set_text(TTR("Disabled LineEdit")); + le->set_editable(false); + second_vb->add_child(le); + TextEdit *te = memnew(TextEdit); + te->set_text("TextEdit"); + te->set_custom_minimum_size(Size2(0, 100) * EDSCALE); + second_vb->add_child(te); + second_vb->add_child(memnew(SpinBox)); + + HBoxContainer *vhb = memnew(HBoxContainer); + second_vb->add_child(vhb); + vhb->set_custom_minimum_size(Size2(0, 100) * EDSCALE); + vhb->add_child(memnew(VSlider)); + VScrollBar *vsb = memnew(VScrollBar); + vsb->set_page(25); + vhb->add_child(vsb); + vhb->add_child(memnew(VSeparator)); + VBoxContainer *hvb = memnew(VBoxContainer); + vhb->add_child(hvb); + hvb->set_alignment(BoxContainer::ALIGN_CENTER); + hvb->set_h_size_flags(SIZE_EXPAND_FILL); + hvb->add_child(memnew(HSlider)); + HScrollBar *hsb = memnew(HScrollBar); + hsb->set_page(25); + hvb->add_child(hsb); + HSlider *hs = memnew(HSlider); + hs->set_editable(false); + hvb->add_child(hs); + hvb->add_child(memnew(HSeparator)); + ProgressBar *pb = memnew(ProgressBar); + pb->set_value(50); + hvb->add_child(pb); + + VBoxContainer *third_vb = memnew(VBoxContainer); + third_vb->set_h_size_flags(SIZE_EXPAND_FILL); + third_vb->add_theme_constant_override("separation", 10 * EDSCALE); + main_hb->add_child(third_vb); + + TabContainer *tc = memnew(TabContainer); + third_vb->add_child(tc); + tc->set_custom_minimum_size(Size2(0, 135) * EDSCALE); + Control *tcc = memnew(Control); + tcc->set_name(TTR("Tab 1")); + tc->add_child(tcc); + tcc = memnew(Control); + tcc->set_name(TTR("Tab 2")); + tc->add_child(tcc); + tcc = memnew(Control); + tcc->set_name(TTR("Tab 3")); + tc->add_child(tcc); + tc->set_tab_disabled(2, true); + + Tree *test_tree = memnew(Tree); + third_vb->add_child(test_tree); + test_tree->set_custom_minimum_size(Size2(0, 175) * EDSCALE); + + TreeItem *item = test_tree->create_item(); + item->set_text(0, "Tree"); + item = test_tree->create_item(test_tree->get_root()); + item->set_text(0, "Item"); + item = test_tree->create_item(test_tree->get_root()); + item->set_editable(0, true); + item->set_text(0, TTR("Editable Item")); + TreeItem *sub_tree = test_tree->create_item(test_tree->get_root()); + sub_tree->set_text(0, TTR("Subtree")); + item = test_tree->create_item(sub_tree); + item->set_cell_mode(0, TreeItem::CELL_MODE_CHECK); + item->set_editable(0, true); + item->set_text(0, "Check Item"); + item = test_tree->create_item(sub_tree); + item->set_cell_mode(0, TreeItem::CELL_MODE_RANGE); + item->set_editable(0, true); + item->set_range_config(0, 0, 20, 0.1); + item->set_range(0, 2); + item = test_tree->create_item(sub_tree); + item->set_cell_mode(0, TreeItem::CELL_MODE_RANGE); + item->set_editable(0, true); + item->set_text(0, TTR("Has,Many,Options")); + item->set_range(0, 2); +} + +void SceneThemeEditorPreview::_reload_scene() { + if (loaded_scene.is_null()) { + return; + } + + if (loaded_scene->get_path().is_empty() || !ResourceLoader::exists(loaded_scene->get_path())) { + EditorNode::get_singleton()->show_warning(TTR("Invalid path, the PackedScene resource was probably moved or removed.")); + emit_signal("scene_invalidated"); + return; + } + + for (int i = preview_content->get_child_count() - 1; i >= 0; i--) { + Node *node = preview_content->get_child(i); + node->queue_delete(); + preview_content->remove_child(node); + } + + Node *instance = loaded_scene->instance(); + if (!instance || !Object::cast_to<Control>(instance)) { + EditorNode::get_singleton()->show_warning(TTR("Invalid PackedScene resource, must have a Control node at its root.")); + emit_signal("scene_invalidated"); + return; + } + + preview_content->add_child(instance); + emit_signal("scene_reloaded"); +} + +void SceneThemeEditorPreview::_notification(int p_what) { + switch (p_what) { + case NOTIFICATION_ENTER_TREE: + case NOTIFICATION_THEME_CHANGED: { + reload_scene_button->set_icon(get_theme_icon("Reload", "EditorIcons")); + } break; + } +} + +void SceneThemeEditorPreview::_bind_methods() { + ADD_SIGNAL(MethodInfo("scene_invalidated")); + ADD_SIGNAL(MethodInfo("scene_reloaded")); +} + +bool SceneThemeEditorPreview::set_preview_scene(const String &p_path) { + loaded_scene = ResourceLoader::load(p_path); + if (loaded_scene.is_null()) { + EditorNode::get_singleton()->show_warning(TTR("Invalid file, not a PackedScene resource.")); + return false; + } + + Node *instance = loaded_scene->instance(); + if (!instance || !Object::cast_to<Control>(instance)) { + EditorNode::get_singleton()->show_warning(TTR("Invalid PackedScene resource, must have a Control node at its root.")); + return false; + } + + preview_content->add_child(instance); + return true; +} + +String SceneThemeEditorPreview::get_preview_scene_path() const { + if (loaded_scene.is_null()) { + return ""; + } + + return loaded_scene->get_path(); +} + +SceneThemeEditorPreview::SceneThemeEditorPreview() { + preview_toolbar->add_child(memnew(VSeparator)); + + reload_scene_button = memnew(Button); + reload_scene_button->set_flat(true); + reload_scene_button->set_tooltip(TTR("Reload the scene to reflect its most actual state.")); + preview_toolbar->add_child(reload_scene_button); + reload_scene_button->connect("pressed", callable_mp(this, &SceneThemeEditorPreview::_reload_scene)); +} diff --git a/editor/plugins/theme_editor_preview.h b/editor/plugins/theme_editor_preview.h new file mode 100644 index 0000000000..efb7e424d4 --- /dev/null +++ b/editor/plugins/theme_editor_preview.h @@ -0,0 +1,118 @@ +/*************************************************************************/ +/* theme_editor_preview.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef THEME_EDITOR_PREVIEW_H +#define THEME_EDITOR_PREVIEW_H + +#include "scene/gui/box_container.h" +#include "scene/gui/check_box.h" +#include "scene/gui/check_button.h" +#include "scene/gui/color_picker.h" +#include "scene/gui/color_rect.h" +#include "scene/gui/label.h" +#include "scene/gui/margin_container.h" +#include "scene/gui/menu_button.h" +#include "scene/gui/option_button.h" +#include "scene/gui/panel.h" +#include "scene/gui/progress_bar.h" +#include "scene/gui/scroll_container.h" +#include "scene/gui/separator.h" +#include "scene/gui/spin_box.h" +#include "scene/gui/tab_container.h" +#include "scene/gui/text_edit.h" +#include "scene/gui/tree.h" +#include "scene/resources/theme.h" + +#include "editor/editor_node.h" + +class ThemeEditorPreview : public VBoxContainer { + GDCLASS(ThemeEditorPreview, VBoxContainer); + + ColorRect *preview_bg; + MarginContainer *preview_overlay; + Control *picker_overlay; + Control *hovered_control = nullptr; + + double time_left = 0; + + void _propagate_redraw(Control *p_at); + void _refresh_interval(); + void _preview_visibility_changed(); + + void _picker_button_cbk(); + Control *_find_hovered_control(Control *p_parent, Vector2 p_mouse_position); + + void _draw_picker_overlay(); + void _gui_input_picker_overlay(const Ref<InputEvent> &p_event); + +protected: + HBoxContainer *preview_toolbar; + MarginContainer *preview_content; + Button *picker_button; + + void add_preview_overlay(Control *p_overlay); + + void _notification(int p_what); + static void _bind_methods(); + +public: + void set_preview_theme(const Ref<Theme> &p_theme); + + ThemeEditorPreview(); +}; + +class DefaultThemeEditorPreview : public ThemeEditorPreview { + GDCLASS(DefaultThemeEditorPreview, ThemeEditorPreview); + +public: + DefaultThemeEditorPreview(); +}; + +class SceneThemeEditorPreview : public ThemeEditorPreview { + GDCLASS(SceneThemeEditorPreview, ThemeEditorPreview); + + Ref<PackedScene> loaded_scene; + + Button *reload_scene_button; + + void _reload_scene(); + +protected: + void _notification(int p_what); + static void _bind_methods(); + +public: + bool set_preview_scene(const String &p_path); + String get_preview_scene_path() const; + + SceneThemeEditorPreview(); +}; + +#endif // THEME_EDITOR_PREVIEW_H diff --git a/main/main.cpp b/main/main.cpp index 13a9986564..7d4e4ef372 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -1453,7 +1453,7 @@ Error Main::setup2(Thread::ID p_main_tid_override) { #ifdef TOOLS_ENABLED if (editor || project_manager) { - EditorNode::register_editor_paths(project_manager); + EditorPaths::create(); } #endif @@ -2379,10 +2379,8 @@ bool Main::start() { } // Load SSL Certificates from Editor Settings (or builtin) - Crypto::load_default_certificates(EditorSettings::get_singleton()->get_setting( - "network/ssl/editor_ssl_certificates") - . - operator String()); + Crypto::load_default_certificates( + EditorSettings::get_singleton()->get_setting("network/ssl/editor_ssl_certificates").operator String()); } #endif } diff --git a/modules/gdnative/gdnative_api.json b/modules/gdnative/gdnative_api.json index 175a6a3c91..8c65447e5d 100644 --- a/modules/gdnative/gdnative_api.json +++ b/modules/gdnative/gdnative_api.json @@ -5068,7 +5068,7 @@ }, { "name": "godot_xr_get_worldscale", - "return_type": "godot_float", + "return_type": "godot_real_t", "arguments": [] }, { @@ -5189,7 +5189,7 @@ "p_exis" ], [ - "godot_float", + "godot_real_t", "p_value" ], [ @@ -5200,7 +5200,7 @@ }, { "name": "godot_xr_get_controller_rumble", - "return_type": "godot_float", + "return_type": "godot_real_t", "arguments": [ [ "godot_int", diff --git a/modules/gdnative/include/xr/godot_xr.h b/modules/gdnative/include/xr/godot_xr.h index e09244f3f9..53cb830cbb 100644 --- a/modules/gdnative/include/xr/godot_xr.h +++ b/modules/gdnative/include/xr/godot_xr.h @@ -41,8 +41,8 @@ extern "C" { // version info to detect whether a call is available // Use these to populate version in your plugin -#define GODOTVR_API_MAJOR 1 -#define GODOTVR_API_MINOR 1 +#define GODOTVR_API_MAJOR 4 +#define GODOTVR_API_MINOR 0 typedef struct { godot_gdnative_api_version version; /* version of our API */ @@ -52,24 +52,31 @@ typedef struct { godot_int (*get_capabilities)(const void *); godot_bool (*get_anchor_detection_is_enabled)(const void *); void (*set_anchor_detection_is_enabled)(void *, godot_bool); - godot_bool (*is_stereo)(const void *); + godot_int (*get_view_count)(const void *); godot_bool (*is_initialized)(const void *); godot_bool (*initialize)(void *); void (*uninitialize)(void *); godot_vector2 (*get_render_targetsize)(const void *); - godot_transform3d (*get_transform_for_eye)(void *, godot_int, godot_transform3d *); - void (*fill_projection_for_eye)(void *, godot_float *, godot_int, godot_float, godot_float, godot_float); - void (*commit_for_eye)(void *, godot_int, godot_rid *, godot_rect2 *); + + godot_transform3d (*get_camera_transform)(void *); + godot_transform3d (*get_transform_for_view)(void *, godot_int, godot_transform3d *); + void (*fill_projection_for_view)(void *, godot_real_t *, godot_int, godot_real_t, godot_real_t, godot_real_t); + void (*commit_views)(void *, godot_rid *, godot_rect2 *); + void (*process)(void *); - godot_int (*get_external_texture_for_eye)(void *, godot_int); void (*notification)(void *, godot_int); godot_int (*get_camera_feed_id)(void *); + + // possibly deprecate but adding/keeping as a reminder these are in Godot 3 + void (*commit_for_eye)(void *, godot_int, godot_rid *, godot_rect2 *); + godot_int (*get_external_texture_for_eye)(void *, godot_int); + godot_int (*get_external_depth_for_eye)(void *, godot_int); } godot_xr_interface_gdnative; void GDAPI godot_xr_register_interface(const godot_xr_interface_gdnative *p_interface); // helper functions to access XRServer data -godot_float GDAPI godot_xr_get_worldscale(); +godot_real_t GDAPI godot_xr_get_worldscale(); godot_transform3d GDAPI godot_xr_get_reference_frame(); // helper functions for rendering @@ -81,8 +88,8 @@ godot_int GDAPI godot_xr_add_controller(char *p_device_name, godot_int p_hand, g void GDAPI godot_xr_remove_controller(godot_int p_controller_id); void GDAPI godot_xr_set_controller_transform(godot_int p_controller_id, godot_transform3d *p_transform, godot_bool p_tracks_orientation, godot_bool p_tracks_position); void GDAPI godot_xr_set_controller_button(godot_int p_controller_id, godot_int p_button, godot_bool p_is_pressed); -void GDAPI godot_xr_set_controller_axis(godot_int p_controller_id, godot_int p_axis, godot_float p_value, godot_bool p_can_be_negative); -godot_float GDAPI godot_xr_get_controller_rumble(godot_int p_controller_id); +void GDAPI godot_xr_set_controller_axis(godot_int p_controller_id, godot_int p_axis, godot_real_t p_value, godot_bool p_can_be_negative); +godot_real_t GDAPI godot_xr_get_controller_rumble(godot_int p_controller_id); #ifdef __cplusplus } diff --git a/modules/gdnative/nativescript/api_generator.cpp b/modules/gdnative/nativescript/api_generator.cpp index 4ad8901f32..57717010f7 100644 --- a/modules/gdnative/nativescript/api_generator.cpp +++ b/modules/gdnative/nativescript/api_generator.cpp @@ -38,6 +38,7 @@ #include "core/object/class_db.h" #include "core/string/string_builder.h" #include "core/templates/pair.h" +#include "core/variant/variant_parser.h" // helper stuff @@ -638,6 +639,7 @@ static List<String> generate_c_api_json(const List<ClassAPI> &p_api) { // I'm sorry for the \t mess List<String> source; + VariantWriter writer; source.push_back("[\n"); @@ -682,7 +684,12 @@ static List<String> generate_c_api_json(const List<ClassAPI> &p_api) { source.push_back("\t\t\t\t\t\t\"name\": \"" + e->get().argument_names[i] + "\",\n"); source.push_back("\t\t\t\t\t\t\"type\": \"" + e->get().argument_types[i] + "\",\n"); source.push_back(String("\t\t\t\t\t\t\"has_default_value\": ") + (e->get().default_arguments.has(i) ? "true" : "false") + ",\n"); - source.push_back("\t\t\t\t\t\t\"default_value\": \"" + (e->get().default_arguments.has(i) ? (String)e->get().default_arguments[i] : "") + "\"\n"); + String default_value; + if (e->get().default_arguments.has(i)) { + writer.write_to_string(e->get().default_arguments[i], default_value); + default_value = default_value.replace("\n", "").json_escape(); + } + source.push_back("\t\t\t\t\t\t\"default_value\": \"" + default_value + "\"\n"); source.push_back(String("\t\t\t\t\t}") + ((i < e->get().argument_names.size() - 1) ? "," : "") + "\n"); } source.push_back("\t\t\t\t]\n"); @@ -708,7 +715,12 @@ static List<String> generate_c_api_json(const List<ClassAPI> &p_api) { source.push_back("\t\t\t\t\t\t\"name\": \"" + e->get().argument_names[i] + "\",\n"); source.push_back("\t\t\t\t\t\t\"type\": \"" + e->get().argument_types[i] + "\",\n"); source.push_back(String("\t\t\t\t\t\t\"has_default_value\": ") + (e->get().default_arguments.has(i) ? "true" : "false") + ",\n"); - source.push_back("\t\t\t\t\t\t\"default_value\": \"" + (e->get().default_arguments.has(i) ? (String)e->get().default_arguments[i] : "") + "\"\n"); + String default_value; + if (e->get().default_arguments.has(i)) { + writer.write_to_string(e->get().default_arguments[i], default_value); + default_value = default_value.replace("\n", "").json_escape(); + } + source.push_back("\t\t\t\t\t\t\"default_value\": \"" + default_value + "\"\n"); source.push_back(String("\t\t\t\t\t}") + ((i < e->get().argument_names.size() - 1) ? "," : "") + "\n"); } source.push_back("\t\t\t\t]\n"); @@ -756,6 +768,8 @@ static void append_indented(StringBuilder &p_source, const char *p_text) { } static void write_builtin_method(StringBuilder &p_source, const MethodAPI &p_method) { + VariantWriter writer; + append_indented(p_source, vformat(R"("name": "%s",)", p_method.method_name)); append_indented(p_source, vformat(R"("return_type": "%s",)", p_method.return_type)); append_indented(p_source, vformat(R"("is_const": %s,)", p_method.is_const ? "true" : "false")); @@ -771,7 +785,12 @@ static void write_builtin_method(StringBuilder &p_source, const MethodAPI &p_met append_indented(p_source, vformat(R"("name": "%s",)", p_method.argument_names[i])); append_indented(p_source, vformat(R"("type": "%s",)", p_method.argument_types[i])); append_indented(p_source, vformat(R"("has_default_value": %s,)", p_method.default_arguments.has(i) ? "true" : "false")); - append_indented(p_source, vformat(R"("default_value": "%s")", p_method.default_arguments.has(i) ? p_method.default_arguments[i].operator String() : "")); + String default_value; + if (p_method.default_arguments.has(i)) { + writer.write_to_string(p_method.default_arguments[i], default_value); + default_value = default_value.replace("\n", "").json_escape(); + } + append_indented(p_source, vformat(R"("default_value": "%s")", default_value)); indent_level--; append_indented(p_source, i < p_method.argument_count - 1 ? "}," : "}"); diff --git a/modules/gdnative/xr/xr_interface_gdnative.cpp b/modules/gdnative/xr/xr_interface_gdnative.cpp index 258fb75000..ff959affa3 100644 --- a/modules/gdnative/xr/xr_interface_gdnative.cpp +++ b/modules/gdnative/xr/xr_interface_gdnative.cpp @@ -70,8 +70,13 @@ void XRInterfaceGDNative::set_interface(const godot_xr_interface_gdnative *p_int // this should only be called once, just being paranoid.. if (interface) { cleanup(); + interface = NULL; } + // validate + ERR_FAIL_NULL(p_interface); + ERR_FAIL_COND_MSG(p_interface->version.major < 4, "This is an incompatible GDNative XR plugin."); + // bind to our interface interface = p_interface; @@ -119,14 +124,14 @@ int XRInterfaceGDNative::get_camera_feed_id() { return (unsigned int)interface->get_camera_feed_id(data); } -bool XRInterfaceGDNative::is_stereo() { - bool stereo; +uint32_t XRInterfaceGDNative::get_view_count() { + uint32_t view_count; - ERR_FAIL_COND_V(interface == nullptr, false); + ERR_FAIL_COND_V(interface == nullptr, 1); - stereo = interface->is_stereo(data); + view_count = interface->get_view_count(data); - return stereo; + return view_count; } bool XRInterfaceGDNative::is_initialized() const { @@ -173,28 +178,52 @@ Size2 XRInterfaceGDNative::get_render_targetsize() { return *vec; } -Transform3D XRInterfaceGDNative::get_transform_for_eye(XRInterface::Eyes p_eye, const Transform3D &p_cam_transform) { +Transform3D XRInterfaceGDNative::get_camera_transform() { Transform3D *ret; ERR_FAIL_COND_V(interface == nullptr, Transform3D()); - godot_transform3d t = interface->get_transform_for_eye(data, (int)p_eye, (godot_transform3d *)&p_cam_transform); + godot_transform3d t = interface->get_camera_transform(data); ret = (Transform3D *)&t; return *ret; } -CameraMatrix XRInterfaceGDNative::get_projection_for_eye(XRInterface::Eyes p_eye, real_t p_aspect, real_t p_z_near, real_t p_z_far) { +Transform3D XRInterfaceGDNative::get_transform_for_view(uint32_t p_view, const Transform3D &p_cam_transform) { + Transform3D *ret; + + ERR_FAIL_COND_V(interface == nullptr, Transform3D()); + + godot_transform3d t = interface->get_transform_for_view(data, (int)p_view, (godot_transform3d *)&p_cam_transform); + + ret = (Transform3D *)&t; + + return *ret; +} + +CameraMatrix XRInterfaceGDNative::get_projection_for_view(uint32_t p_view, real_t p_aspect, real_t p_z_near, real_t p_z_far) { CameraMatrix cm; ERR_FAIL_COND_V(interface == nullptr, CameraMatrix()); - interface->fill_projection_for_eye(data, (godot_float *)cm.matrix, (godot_int)p_eye, p_aspect, p_z_near, p_z_far); + interface->fill_projection_for_view(data, (godot_real_t *)cm.matrix, (godot_int)p_view, p_aspect, p_z_near, p_z_far); return cm; } +Vector<BlitToScreen> XRInterfaceGDNative::commit_views(RID p_render_target, const Rect2 &p_screen_rect) { + // possibly move this as a member variable and add a callback to populate? + Vector<BlitToScreen> blit_to_screen; + + ERR_FAIL_COND_V(interface == nullptr, blit_to_screen); + + // must implement + interface->commit_views(data, (godot_rid *)&p_render_target, (godot_rect2 *)&p_screen_rect); + + return blit_to_screen; +} + unsigned int XRInterfaceGDNative::get_external_texture_for_eye(XRInterface::Eyes p_eye) { ERR_FAIL_COND_V(interface == nullptr, 0); @@ -234,7 +263,7 @@ void GDAPI godot_xr_register_interface(const godot_xr_interface_gdnative *p_inte XRServer::get_singleton()->add_interface(new_interface); } -godot_float GDAPI godot_xr_get_worldscale() { +godot_real_t GDAPI godot_xr_get_worldscale() { XRServer *xr_server = XRServer::get_singleton(); ERR_FAIL_NULL_V(xr_server, 1.0); @@ -388,7 +417,7 @@ void GDAPI godot_xr_set_controller_button(godot_int p_controller_id, godot_int p } } -void GDAPI godot_xr_set_controller_axis(godot_int p_controller_id, godot_int p_axis, godot_float p_value, godot_bool p_can_be_negative) { +void GDAPI godot_xr_set_controller_axis(godot_int p_controller_id, godot_int p_axis, godot_real_t p_value, godot_bool p_can_be_negative) { XRServer *xr_server = XRServer::get_singleton(); ERR_FAIL_NULL(xr_server); @@ -407,7 +436,7 @@ void GDAPI godot_xr_set_controller_axis(godot_int p_controller_id, godot_int p_a } } -godot_float GDAPI godot_xr_get_controller_rumble(godot_int p_controller_id) { +godot_real_t GDAPI godot_xr_get_controller_rumble(godot_int p_controller_id) { XRServer *xr_server = XRServer::get_singleton(); ERR_FAIL_NULL_V(xr_server, 0.0); diff --git a/modules/gdnative/xr/xr_interface_gdnative.h b/modules/gdnative/xr/xr_interface_gdnative.h index 42098ebeff..42e9206c1f 100644 --- a/modules/gdnative/xr/xr_interface_gdnative.h +++ b/modules/gdnative/xr/xr_interface_gdnative.h @@ -72,20 +72,24 @@ public: /** rendering and internal **/ virtual Size2 get_render_targetsize() override; - virtual bool is_stereo() override; - virtual Transform3D get_transform_for_eye(XRInterface::Eyes p_eye, const Transform3D &p_cam_transform) override; + virtual uint32_t get_view_count() override; + virtual Transform3D get_camera_transform() override; + virtual Transform3D get_transform_for_view(uint32_t p_view, const Transform3D &p_cam_transform) override; // we expose a Vector<float> version of this function to GDNative Vector<float> _get_projection_for_eye(XRInterface::Eyes p_eye, real_t p_aspect, real_t p_z_near, real_t p_z_far); // and a CameraMatrix version to XRServer - virtual CameraMatrix get_projection_for_eye(XRInterface::Eyes p_eye, real_t p_aspect, real_t p_z_near, real_t p_z_far) override; + virtual CameraMatrix get_projection_for_view(uint32_t p_view, real_t p_aspect, real_t p_z_near, real_t p_z_far) override; - virtual unsigned int get_external_texture_for_eye(XRInterface::Eyes p_eye) override; - virtual void commit_for_eye(XRInterface::Eyes p_eye, RID p_render_target, const Rect2 &p_screen_rect) override; + virtual Vector<BlitToScreen> commit_views(RID p_render_target, const Rect2 &p_screen_rect) override; virtual void process() override; virtual void notification(int p_what) override; + + // deprecated + virtual void commit_for_eye(XRInterface::Eyes p_eye, RID p_render_target, const Rect2 &p_screen_rect) override; + virtual unsigned int get_external_texture_for_eye(XRInterface::Eyes p_eye) override; }; #endif // XR_INTERFACE_GDNATIVE_H diff --git a/modules/gdscript/gdscript_vm.cpp b/modules/gdscript/gdscript_vm.cpp index 318ec966ae..8a261a88e3 100644 --- a/modules/gdscript/gdscript_vm.cpp +++ b/modules/gdscript/gdscript_vm.cpp @@ -1989,7 +1989,10 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a ip += instr_arg_count; - int self_fun = _code_ptr[ip + 1]; + int argc = _code_ptr[ip + 1]; + GD_ERR_BREAK(argc < 0); + + int self_fun = _code_ptr[ip + 2]; #ifdef DEBUG_ENABLED if (self_fun < 0 || self_fun >= _global_names_count) { err_text = "compiler bug, function name not found"; @@ -1998,9 +2001,6 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a #endif const StringName *methodname = &_global_names_ptr[self_fun]; - int argc = _code_ptr[ip + 2]; - GD_ERR_BREAK(argc < 0); - Variant **argptrs = instruction_args; GET_INSTRUCTION_ARG(dst, argc); diff --git a/modules/glslang/register_types.cpp b/modules/glslang/register_types.cpp index 8979eabfc3..730c6b89f7 100644 --- a/modules/glslang/register_types.cpp +++ b/modules/glslang/register_types.cpp @@ -116,6 +116,10 @@ static Vector<uint8_t> _compile_shader_glsl(RenderingDevice::ShaderStage p_stage } } + if (p_capabilities->supports_multiview) { + preamble += "#define has_VK_KHR_multiview 1\n"; + } + if (preamble != "") { shader.setPreamble(preamble.c_str()); } diff --git a/modules/mobile_vr/mobile_vr_interface.cpp b/modules/mobile_vr/mobile_vr_interface.cpp index 40b1745c35..590b95ab79 100644 --- a/modules/mobile_vr/mobile_vr_interface.cpp +++ b/modules/mobile_vr/mobile_vr_interface.cpp @@ -300,9 +300,9 @@ real_t MobileVRInterface::get_k2() const { return k2; }; -bool MobileVRInterface::is_stereo() { +uint32_t MobileVRInterface::get_view_count() { // needs stereo... - return true; + return 2; }; bool MobileVRInterface::is_initialized() const { @@ -361,7 +361,29 @@ Size2 MobileVRInterface::get_render_targetsize() { return target_size; }; -Transform3D MobileVRInterface::get_transform_for_eye(XRInterface::Eyes p_eye, const Transform3D &p_cam_transform) { +Transform3D MobileVRInterface::get_camera_transform() { + _THREAD_SAFE_METHOD_ + + Transform3D transform_for_eye; + + XRServer *xr_server = XRServer::get_singleton(); + ERR_FAIL_NULL_V(xr_server, transform_for_eye); + + if (initialized) { + float world_scale = xr_server->get_world_scale(); + + // just scale our origin point of our transform + Transform3D hmd_transform; + hmd_transform.basis = orientation; + hmd_transform.origin = Vector3(0.0, eye_height * world_scale, 0.0); + + transform_for_eye = (xr_server->get_reference_frame()) * hmd_transform; + } + + return transform_for_eye; +}; + +Transform3D MobileVRInterface::get_transform_for_view(uint32_t p_view, const Transform3D &p_cam_transform) { _THREAD_SAFE_METHOD_ Transform3D transform_for_eye; @@ -374,12 +396,12 @@ Transform3D MobileVRInterface::get_transform_for_eye(XRInterface::Eyes p_eye, co // we don't need to check for the existence of our HMD, doesn't affect our values... // note * 0.01 to convert cm to m and * 0.5 as we're moving half in each direction... - if (p_eye == XRInterface::EYE_LEFT) { + if (p_view == 0) { transform_for_eye.origin.x = -(intraocular_dist * 0.01 * 0.5 * world_scale); - } else if (p_eye == XRInterface::EYE_RIGHT) { + } else if (p_view == 1) { transform_for_eye.origin.x = intraocular_dist * 0.01 * 0.5 * world_scale; } else { - // for mono we don't reposition, we want our center position. + // should not have any other values.. }; // just scale our origin point of our transform @@ -396,21 +418,13 @@ Transform3D MobileVRInterface::get_transform_for_eye(XRInterface::Eyes p_eye, co return transform_for_eye; }; -CameraMatrix MobileVRInterface::get_projection_for_eye(XRInterface::Eyes p_eye, real_t p_aspect, real_t p_z_near, real_t p_z_far) { +CameraMatrix MobileVRInterface::get_projection_for_view(uint32_t p_view, real_t p_aspect, real_t p_z_near, real_t p_z_far) { _THREAD_SAFE_METHOD_ CameraMatrix eye; - if (p_eye == XRInterface::EYE_MONO) { - ///@TODO for now hardcode some of this, what is really needed here is that this needs to be in sync with the real camera's properties - // which probably means implementing a specific class for iOS and Android. For now this is purely here as an example. - // Note also that if you use a normal viewport with AR/VR turned off you can still use the tracker output of this interface - // to position a stock standard Godot camera and have control over this. - // This will make more sense when we implement ARkit on iOS (probably a separate interface). - eye.set_perspective(60.0, p_aspect, p_z_near, p_z_far, false); - } else { - eye.set_for_hmd(p_eye == XRInterface::EYE_LEFT ? 1 : 2, p_aspect, intraocular_dist, display_width, display_to_lens, oversample, p_z_near, p_z_far); - }; + aspect = p_aspect; + eye.set_for_hmd(p_view + 1, p_aspect, intraocular_dist, display_width, display_to_lens, oversample, p_z_near, p_z_far); return eye; }; @@ -440,6 +454,45 @@ void MobileVRInterface::commit_for_eye(XRInterface::Eyes p_eye, RID p_render_tar eye_center.y = 0.0; } +Vector<BlitToScreen> MobileVRInterface::commit_views(RID p_render_target, const Rect2 &p_screen_rect) { + _THREAD_SAFE_METHOD_ + + Vector<BlitToScreen> blit_to_screen; + + // We must have a valid render target + ERR_FAIL_COND_V(!p_render_target.is_valid(), blit_to_screen); + + // Because we are rendering to our device we must use our main viewport! + ERR_FAIL_COND_V(p_screen_rect == Rect2(), blit_to_screen); + + // and add our blits + BlitToScreen blit; + blit.render_target = p_render_target; + blit.multi_view.use_layer = true; + blit.lens_distortion.apply = true; + blit.lens_distortion.k1 = k1; + blit.lens_distortion.k2 = k2; + blit.lens_distortion.upscale = oversample; + blit.lens_distortion.aspect_ratio = aspect; + + // left eye + blit.rect = p_screen_rect; + blit.rect.size.width *= 0.5; + blit.multi_view.layer = 0; + blit.lens_distortion.eye_center.x = ((-intraocular_dist / 2.0) + (display_width / 4.0)) / (display_width / 2.0); + blit_to_screen.push_back(blit); + + // right eye + blit.rect = p_screen_rect; + blit.rect.size.width *= 0.5; + blit.rect.position.x = blit.rect.size.width; + blit.multi_view.layer = 1; + blit.lens_distortion.eye_center.x = ((intraocular_dist / 2.0) - (display_width / 4.0)) / (display_width / 2.0); + blit_to_screen.push_back(blit); + + return blit_to_screen; +} + void MobileVRInterface::process() { _THREAD_SAFE_METHOD_ diff --git a/modules/mobile_vr/mobile_vr_interface.h b/modules/mobile_vr/mobile_vr_interface.h index aad40ebb5b..29ce0f92c8 100644 --- a/modules/mobile_vr/mobile_vr_interface.h +++ b/modules/mobile_vr/mobile_vr_interface.h @@ -63,9 +63,9 @@ private: real_t display_to_lens = 4.0; real_t oversample = 1.5; - //@TODO not yet used, these are needed in our distortion shader... real_t k1 = 0.215; real_t k2 = 0.215; + real_t aspect = 1.0; /* logic for processing our sensor data, this was originally in our positional tracker logic but I think @@ -138,16 +138,20 @@ public: virtual void uninitialize() override; virtual Size2 get_render_targetsize() override; - virtual bool is_stereo() override; - virtual Transform3D get_transform_for_eye(XRInterface::Eyes p_eye, const Transform3D &p_cam_transform) override; - virtual CameraMatrix get_projection_for_eye(XRInterface::Eyes p_eye, real_t p_aspect, real_t p_z_near, real_t p_z_far) override; - virtual void commit_for_eye(XRInterface::Eyes p_eye, RID p_render_target, const Rect2 &p_screen_rect) override; + virtual uint32_t get_view_count() override; + virtual Transform3D get_camera_transform() override; + virtual Transform3D get_transform_for_view(uint32_t p_view, const Transform3D &p_cam_transform) override; + virtual CameraMatrix get_projection_for_view(uint32_t p_view, real_t p_aspect, real_t p_z_near, real_t p_z_far) override; + virtual Vector<BlitToScreen> commit_views(RID p_render_target, const Rect2 &p_screen_rect) override; virtual void process() override; virtual void notification(int p_what) override {} MobileVRInterface(); ~MobileVRInterface(); + + // deprecated + virtual void commit_for_eye(XRInterface::Eyes p_eye, RID p_render_target, const Rect2 &p_screen_rect) override; }; #endif // !MOBILE_VR_INTERFACE_H diff --git a/modules/tga/image_loader_tga.cpp b/modules/tga/image_loader_tga.cpp index 3cfd4ff36a..6eaa2d24a8 100644 --- a/modules/tga/image_loader_tga.cpp +++ b/modules/tga/image_loader_tga.cpp @@ -35,7 +35,7 @@ #include "core/os/os.h" #include "core/string/print_string.h" -Error ImageLoaderTGA::decode_tga_rle(const uint8_t *p_compressed_buffer, size_t p_pixel_size, uint8_t *p_uncompressed_buffer, size_t p_output_size) { +Error ImageLoaderTGA::decode_tga_rle(const uint8_t *p_compressed_buffer, size_t p_pixel_size, uint8_t *p_uncompressed_buffer, size_t p_output_size, size_t p_input_size) { Error error; Vector<uint8_t> pixels; @@ -56,11 +56,14 @@ Error ImageLoaderTGA::decode_tga_rle(const uint8_t *p_compressed_buffer, size_t compressed_pos += 1; count = (c & 0x7f) + 1; - if (output_pos + count * p_pixel_size > output_pos) { + if (output_pos + count * p_pixel_size > p_output_size) { return ERR_PARSE_ERROR; } if (c & 0x80) { + if (compressed_pos + p_pixel_size > p_input_size) { + return ERR_PARSE_ERROR; + } for (size_t i = 0; i < p_pixel_size; i++) { pixels_w[i] = p_compressed_buffer[compressed_pos]; compressed_pos += 1; @@ -72,6 +75,9 @@ Error ImageLoaderTGA::decode_tga_rle(const uint8_t *p_compressed_buffer, size_t output_pos += p_pixel_size; } } else { + if (compressed_pos + count * p_pixel_size > p_input_size) { + return ERR_PARSE_ERROR; + } count *= p_pixel_size; for (size_t i = 0; i < count; i++) { p_uncompressed_buffer[output_pos] = p_compressed_buffer[compressed_pos]; @@ -83,7 +89,7 @@ Error ImageLoaderTGA::decode_tga_rle(const uint8_t *p_compressed_buffer, size_t return OK; } -Error ImageLoaderTGA::convert_to_image(Ref<Image> p_image, const uint8_t *p_buffer, const tga_header_s &p_header, const uint8_t *p_palette, const bool p_is_monochrome, size_t p_output_size) { +Error ImageLoaderTGA::convert_to_image(Ref<Image> p_image, const uint8_t *p_buffer, const tga_header_s &p_header, const uint8_t *p_palette, const bool p_is_monochrome, size_t p_input_size) { #define TGA_PUT_PIXEL(r, g, b, a) \ int image_data_ofs = ((y * width) + x); \ image_data_w[image_data_ofs * 4 + 0] = r; \ @@ -134,7 +140,7 @@ Error ImageLoaderTGA::convert_to_image(Ref<Image> p_image, const uint8_t *p_buff if (p_is_monochrome) { while (y != y_end) { while (x != x_end) { - if (i > p_output_size) { + if (i >= p_input_size) { return ERR_PARSE_ERROR; } uint8_t shade = p_buffer[i]; @@ -150,7 +156,7 @@ Error ImageLoaderTGA::convert_to_image(Ref<Image> p_image, const uint8_t *p_buff } else { while (y != y_end) { while (x != x_end) { - if (i > p_output_size) { + if (i >= p_input_size) { return ERR_PARSE_ERROR; } uint8_t index = p_buffer[i]; @@ -181,7 +187,7 @@ Error ImageLoaderTGA::convert_to_image(Ref<Image> p_image, const uint8_t *p_buff } else if (p_header.pixel_depth == 24) { while (y != y_end) { while (x != x_end) { - if (i + 2 > p_output_size) { + if (i + 2 >= p_input_size) { return ERR_PARSE_ERROR; } @@ -200,7 +206,7 @@ Error ImageLoaderTGA::convert_to_image(Ref<Image> p_image, const uint8_t *p_buff } else if (p_header.pixel_depth == 32) { while (y != y_end) { while (x != x_end) { - if (i + 3 > p_output_size) { + if (i + 3 >= p_input_size) { return ERR_PARSE_ERROR; } @@ -307,7 +313,7 @@ Error ImageLoaderTGA::load_image(Ref<Image> p_image, FileAccess *f, bool p_force const uint8_t *buffer = nullptr; if (is_encoded) { - err = decode_tga_rle(src_image_r, pixel_size, uncompressed_buffer_w, buffer_size); + err = decode_tga_rle(src_image_r, pixel_size, uncompressed_buffer_w, buffer_size, src_image_len); if (err == OK) { uncompressed_buffer_r = uncompressed_buffer.ptr(); diff --git a/modules/tga/image_loader_tga.h b/modules/tga/image_loader_tga.h index cb2ce07edd..e4463a322f 100644 --- a/modules/tga/image_loader_tga.h +++ b/modules/tga/image_loader_tga.h @@ -72,8 +72,8 @@ class ImageLoaderTGA : public ImageFormatLoader { uint8_t pixel_depth = 0; uint8_t image_descriptor = 0; }; - static Error decode_tga_rle(const uint8_t *p_compressed_buffer, size_t p_pixel_size, uint8_t *p_uncompressed_buffer, size_t p_output_size); - static Error convert_to_image(Ref<Image> p_image, const uint8_t *p_buffer, const tga_header_s &p_header, const uint8_t *p_palette, const bool p_is_monochrome, size_t p_output_size); + static Error decode_tga_rle(const uint8_t *p_compressed_buffer, size_t p_pixel_size, uint8_t *p_uncompressed_buffer, size_t p_output_size, size_t p_input_size); + static Error convert_to_image(Ref<Image> p_image, const uint8_t *p_buffer, const tga_header_s &p_header, const uint8_t *p_palette, const bool p_is_monochrome, size_t p_input_size); public: virtual Error load_image(Ref<Image> p_image, FileAccess *f, bool p_force_linear, float p_scale); diff --git a/modules/visual_script/doc_classes/VisualScriptFunctionState.xml b/modules/visual_script/doc_classes/VisualScriptFunctionState.xml index f5bf195bbf..9ed71bf10e 100644 --- a/modules/visual_script/doc_classes/VisualScriptFunctionState.xml +++ b/modules/visual_script/doc_classes/VisualScriptFunctionState.xml @@ -28,7 +28,7 @@ <method name="resume"> <return type="Variant"> </return> - <argument index="0" name="args" type="Array" default="null"> + <argument index="0" name="args" type="Array" default="[ ]"> </argument> <description> </description> diff --git a/modules/visual_script/visual_script.cpp b/modules/visual_script/visual_script.cpp index c9ed912ac1..7badb1b717 100644 --- a/modules/visual_script/visual_script.cpp +++ b/modules/visual_script/visual_script.cpp @@ -2204,7 +2204,7 @@ Variant VisualScriptFunctionState::resume(Array p_args) { void VisualScriptFunctionState::_bind_methods() { ClassDB::bind_method(D_METHOD("connect_to_signal", "obj", "signals", "args"), &VisualScriptFunctionState::connect_to_signal); - ClassDB::bind_method(D_METHOD("resume", "args"), &VisualScriptFunctionState::resume, DEFVAL(Variant())); + ClassDB::bind_method(D_METHOD("resume", "args"), &VisualScriptFunctionState::resume, DEFVAL(Array())); ClassDB::bind_method(D_METHOD("is_valid"), &VisualScriptFunctionState::is_valid); ClassDB::bind_vararg_method(METHOD_FLAGS_DEFAULT, "_signal_callback", &VisualScriptFunctionState::_signal_callback, MethodInfo("_signal_callback")); } diff --git a/modules/webxr/webxr_interface_js.cpp b/modules/webxr/webxr_interface_js.cpp index a628cb0549..13981e73e1 100644 --- a/modules/webxr/webxr_interface_js.cpp +++ b/modules/webxr/webxr_interface_js.cpp @@ -265,7 +265,7 @@ void WebXRInterfaceJS::uninitialize() { }; }; -Transform WebXRInterfaceJS::_js_matrix_to_transform(float *p_js_matrix) { +Transform3D WebXRInterfaceJS::_js_matrix_to_transform(float *p_js_matrix) { Transform3D transform; transform.basis.elements[0].x = p_js_matrix[0]; @@ -305,13 +305,30 @@ Size2 WebXRInterfaceJS::get_render_targetsize() { return render_targetsize; }; -Transform WebXRInterfaceJS::get_transform_for_eye(XRInterface::Eyes p_eye, const Transform3D &p_cam_transform) { +Transform3D WebXRInterfaceJS::get_camera_transform() { Transform3D transform_for_eye; XRServer *xr_server = XRServer::get_singleton(); ERR_FAIL_NULL_V(xr_server, transform_for_eye); - float *js_matrix = godot_webxr_get_transform_for_eye(p_eye); + float *js_matrix = godot_webxr_get_transform_for_eye(0); + if (!initialized || js_matrix == nullptr) { + return transform_for_eye; + } + + transform_for_eye = _js_matrix_to_transform(js_matrix); + free(js_matrix); + + return xr_server->get_reference_frame() * transform_for_eye; +}; + +Transform3D WebXRInterfaceJS::get_transform_for_view(uint32_t p_view, const Transform3D &p_cam_transform) { + Transform3D transform_for_eye; + + XRServer *xr_server = XRServer::get_singleton(); + ERR_FAIL_NULL_V(xr_server, transform_for_eye); + + float *js_matrix = godot_webxr_get_transform_for_eye(p_view + 1); if (!initialized || js_matrix == nullptr) { transform_for_eye = p_cam_transform; return transform_for_eye; @@ -323,10 +340,10 @@ Transform WebXRInterfaceJS::get_transform_for_eye(XRInterface::Eyes p_eye, const return p_cam_transform * xr_server->get_reference_frame() * transform_for_eye; }; -CameraMatrix WebXRInterfaceJS::get_projection_for_eye(XRInterface::Eyes p_eye, real_t p_aspect, real_t p_z_near, real_t p_z_far) { +CameraMatrix WebXRInterfaceJS::get_projection_for_view(uint32_t p_view, real_t p_aspect, real_t p_z_near, real_t p_z_far) { CameraMatrix eye; - float *js_matrix = godot_webxr_get_projection_for_eye(p_eye); + float *js_matrix = godot_webxr_get_projection_for_eye(p_view + 1); if (!initialized || js_matrix == nullptr) { return eye; } diff --git a/modules/webxr/webxr_interface_js.h b/modules/webxr/webxr_interface_js.h index e9274a06e4..723ab952cd 100644 --- a/modules/webxr/webxr_interface_js.h +++ b/modules/webxr/webxr_interface_js.h @@ -84,8 +84,9 @@ public: virtual Size2 get_render_targetsize() override; virtual bool is_stereo() override; - virtual Transform get_transform_for_eye(XRInterface::Eyes p_eye, const Transform3D &p_cam_transform) override; - virtual CameraMatrix get_projection_for_eye(XRInterface::Eyes p_eye, real_t p_aspect, real_t p_z_near, real_t p_z_far) override; + virtual Transform3D get_camera_transform() override; + virtual Transform3D get_transform_for_view(uint32_t p_view, const Transform3D &p_cam_transform) override; + virtual CameraMatrix get_projection_for_view(uint32_t p_view, real_t p_aspect, real_t p_z_near, real_t p_z_far) override; virtual unsigned int get_external_texture_for_eye(XRInterface::Eyes p_eye) override; virtual void commit_for_eye(XRInterface::Eyes p_eye, RID p_render_target, const Rect2 &p_screen_rect) override; diff --git a/platform/javascript/api/javascript_tools_editor_plugin.cpp b/platform/javascript/api/javascript_tools_editor_plugin.cpp index 83a0fcc68f..54f541f607 100644 --- a/platform/javascript/api/javascript_tools_editor_plugin.cpp +++ b/platform/javascript/api/javascript_tools_editor_plugin.cpp @@ -41,7 +41,7 @@ // JavaScript functions defined in library_godot_editor_tools.js extern "C" { -extern int godot_js_os_download_buffer(const uint8_t *p_buf, int p_buf_size, const char *p_name, const char *p_mime); +extern void godot_js_os_download_buffer(const uint8_t *p_buf, int p_buf_size, const char *p_name, const char *p_mime); } static void _javascript_editor_init_callback() { diff --git a/platform/javascript/godot_js.h b/platform/javascript/godot_js.h index 8927a83cb3..d332af2c31 100644 --- a/platform/javascript/godot_js.h +++ b/platform/javascript/godot_js.h @@ -84,7 +84,7 @@ extern void godot_js_display_cursor_set_custom_shape(const char *p_shape, const extern void godot_js_display_cursor_set_visible(int p_visible); // Display gamepad -extern char *godot_js_display_gamepad_cb(void (*p_on_change)(int p_index, int p_connected, const char *p_id, const char *p_guid)); +extern void godot_js_display_gamepad_cb(void (*p_on_change)(int p_index, int p_connected, const char *p_id, const char *p_guid)); extern int godot_js_display_gamepad_sample(); extern int godot_js_display_gamepad_sample_count(); extern int godot_js_display_gamepad_sample_get(int p_idx, float r_btns[16], int32_t *r_btns_num, float r_axes[10], int32_t *r_axes_num, int32_t *r_standard); diff --git a/platform/javascript/javascript_singleton.cpp b/platform/javascript/javascript_singleton.cpp index 5ef67c0cdd..c441ed0517 100644 --- a/platform/javascript/javascript_singleton.cpp +++ b/platform/javascript/javascript_singleton.cpp @@ -28,12 +28,16 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifdef JAVASCRIPT_EVAL_ENABLED - #include "api/javascript_singleton.h" #include "emscripten.h" extern "C" { +extern void godot_js_os_download_buffer(const uint8_t *p_buf, int p_buf_size, const char *p_name, const char *p_mime); +} + +#ifdef JAVASCRIPT_EVAL_ENABLED + +extern "C" { typedef union { int64_t i; double r; @@ -301,7 +305,6 @@ union js_eval_ret { }; extern int godot_js_eval(const char *p_js, int p_use_global_ctx, union js_eval_ret *p_union_ptr, void *p_byte_arr, void *p_byte_arr_write, void *(*p_callback)(void *p_ptr, void *p_ptr2, int p_len)); -extern int godot_js_os_download_buffer(const uint8_t *p_buf, int p_buf_size, const char *p_name, const char *p_mime); } void *resize_PackedByteArray_and_open_write(void *p_arr, void *r_write, int p_len) { diff --git a/platform/javascript/js/libs/library_godot_display.js b/platform/javascript/js/libs/library_godot_display.js index 91cab5eacc..affae90370 100644 --- a/platform/javascript/js/libs/library_godot_display.js +++ b/platform/javascript/js/libs/library_godot_display.js @@ -683,7 +683,7 @@ const GodotDisplay = { return GodotDisplayScreen.exitFullscreen(); }, - godot_js_display_desired_size_set__sig: 'v', + godot_js_display_desired_size_set__sig: 'vii', godot_js_display_desired_size_set: function (width, height) { GodotDisplayScreen.desired_size = [width, height]; GodotDisplayScreen.updateSize(); diff --git a/platform/javascript/js/libs/library_godot_fetch.js b/platform/javascript/js/libs/library_godot_fetch.js index 04b90aea9c..615f9de8b0 100644 --- a/platform/javascript/js/libs/library_godot_fetch.js +++ b/platform/javascript/js/libs/library_godot_fetch.js @@ -126,7 +126,7 @@ const GodotFetch = { }, }, - godot_js_fetch_create__sig: 'iii', + godot_js_fetch_create__sig: 'iiiiiii', godot_js_fetch_create: function (p_method, p_url, p_headers, p_headers_size, p_body, p_body_size) { const method = GodotRuntime.parseString(p_method); const url = GodotRuntime.parseString(p_url); @@ -176,7 +176,7 @@ const GodotFetch = { return obj.status; }, - godot_js_fetch_read_headers__sig: 'iii', + godot_js_fetch_read_headers__sig: 'iiii', godot_js_fetch_read_headers: function (p_id, p_parse_cb, p_ref) { const obj = IDHandler.get(p_id); if (!obj || !obj.response) { @@ -193,7 +193,7 @@ const GodotFetch = { return 0; }, - godot_js_fetch_read_chunk__sig: 'ii', + godot_js_fetch_read_chunk__sig: 'iiii', godot_js_fetch_read_chunk: function (p_id, p_buf, p_buf_size) { const obj = IDHandler.get(p_id); if (!obj || !obj.response) { diff --git a/platform/javascript/js/libs/library_godot_javascript_singleton.js b/platform/javascript/js/libs/library_godot_javascript_singleton.js index 09ef4a1a5d..cb80273ca8 100644 --- a/platform/javascript/js/libs/library_godot_javascript_singleton.js +++ b/platform/javascript/js/libs/library_godot_javascript_singleton.js @@ -196,7 +196,7 @@ const GodotJSWrapper = { } }, - godot_js_wrapper_create_cb__sig: 'vii', + godot_js_wrapper_create_cb__sig: 'iii', godot_js_wrapper_create_cb: function (p_ref, p_func) { const func = GodotRuntime.get_func(p_func); let id = 0; diff --git a/platform/javascript/package-lock.json b/platform/javascript/package-lock.json index 8bf5c52ff6..8003619576 100644 --- a/platform/javascript/package-lock.json +++ b/platform/javascript/package-lock.json @@ -5,27 +5,27 @@ "requires": true, "dependencies": { "@babel/code-frame": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz", - "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==", + "version": "7.12.11", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", + "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==", "dev": true, "requires": { "@babel/highlight": "^7.10.4" } }, "@babel/helper-validator-identifier": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz", - "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==", + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.5.tgz", + "integrity": "sha512-5lsetuxCLilmVGyiLEfoHBRX8UCFD+1m2x3Rj97WrW3V7H3u4RWRXA4evMjImCsin2J2YT0QaVDGf+z8ondbAg==", "dev": true }, "@babel/highlight": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz", - "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==", + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.14.5.tgz", + "integrity": "sha512-qf9u2WFWVV0MppaL877j2dBtQIDgmidgjGk5VIMw3OadXvYaXn66U1BFlH2t4+t3i+8PhedppRv+i40ABzd+gg==", "dev": true, "requires": { - "@babel/helper-validator-identifier": "^7.10.4", + "@babel/helper-validator-identifier": "^7.14.5", "chalk": "^2.0.0", "js-tokens": "^4.0.0" }, @@ -40,39 +40,38 @@ "escape-string-regexp": "^1.0.5", "supports-color": "^5.3.0" } + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true } } }, "@babel/parser": { - "version": "7.14.1", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.14.1.tgz", - "integrity": "sha512-muUGEKu8E/ftMTPlNp+mc6zL3E9zKWmF5sDHZ5MSsoTP9Wyz64AhEf9kD08xYJ7w6Hdcu8H550ircnPyWSIF0Q==", + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.14.5.tgz", + "integrity": "sha512-TM8C+xtH/9n1qzX+JNHi7AN2zHMTiPUtspO0ZdHflW8KaskkALhMmuMHb4bCmNdv9VAPzJX3/bXqkVLnAvsPfg==", "dev": true }, "@eslint/eslintrc": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.1.3.tgz", - "integrity": "sha512-4YVwPkANLeNtRjMekzux1ci8hIaH5eGKktGqR0d3LWsKNn5B2X/1Z6Trxy7jQXl9EBGE6Yj02O+t09FMeRllaA==", + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.2.tgz", + "integrity": "sha512-8nmGq/4ycLpIwzvhI4tNDmQztZ8sp+hI7cyG8i1nQDhkAbRzHpXPidRAHlNvCZQpJTKw5ItIpMw9RSToGF00mg==", "dev": true, "requires": { "ajv": "^6.12.4", "debug": "^4.1.1", "espree": "^7.3.0", - "globals": "^12.1.0", + "globals": "^13.9.0", "ignore": "^4.0.6", "import-fresh": "^3.2.1", "js-yaml": "^3.13.1", - "lodash": "^4.17.19", "minimatch": "^3.0.4", "strip-json-comments": "^3.1.1" } }, - "@types/color-name": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz", - "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==", - "dev": true - }, "@types/json5": { "version": "0.0.29", "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", @@ -80,9 +79,9 @@ "dev": true }, "acorn": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.0.tgz", - "integrity": "sha512-+G7P8jJmCHr+S+cLfQxygbWhXy+8YTVGzAkpEbcLo2mLoL7tij/VG41QSHACSf5QgYRhMZYHuNc6drJaO0Da+w==", + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", "dev": true }, "acorn-jsx": { @@ -92,9 +91,9 @@ "dev": true }, "ajv": { - "version": "6.12.5", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.5.tgz", - "integrity": "sha512-lRF8RORchjpKG50/WFf8xmg7sgCLFiYNNnqdKflk63whMQcWR5ngGjiSXkL9bjxy6B2npOK2HSMN49jEBMSkag==", + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, "requires": { "fast-deep-equal": "^3.1.1", @@ -134,78 +133,39 @@ } }, "array-includes": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.1.tgz", - "integrity": "sha512-c2VXaCHl7zPsvpkFsw4nxvFie4fh1ur9bpcgsVkIjqn0H/Xwdg+7fv3n2r/isyS8EBj5b06M9kHyZuIr4El6WQ==", + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.3.tgz", + "integrity": "sha512-gcem1KlBU7c9rB+Rq8/3PPKsK2kjqeEBa3bD5kkQo4nYlOHQCJqIJFqBXDEfwaRuYTT4E+FxA9xez7Gf/e3Q7A==", "dev": true, "requires": { + "call-bind": "^1.0.2", "define-properties": "^1.1.3", - "es-abstract": "^1.17.0", + "es-abstract": "^1.18.0-next.2", + "get-intrinsic": "^1.1.1", "is-string": "^1.0.5" - }, - "dependencies": { - "es-abstract": { - "version": "1.17.6", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.6.tgz", - "integrity": "sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw==", - "dev": true, - "requires": { - "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.1", - "is-callable": "^1.2.0", - "is-regex": "^1.1.0", - "object-inspect": "^1.7.0", - "object-keys": "^1.1.1", - "object.assign": "^4.1.0", - "string.prototype.trimend": "^1.0.1", - "string.prototype.trimstart": "^1.0.1" - } - } } }, "array.prototype.flat": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.2.3.tgz", - "integrity": "sha512-gBlRZV0VSmfPIeWfuuy56XZMvbVfbEUnOXUvt3F/eUUUSyzlgLxhEX4YAEpxNAogRGehPSnfXyPtYyKAhkzQhQ==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.2.4.tgz", + "integrity": "sha512-4470Xi3GAPAjZqFcljX2xzckv1qeKPizoNkiS0+O4IoPR2ZNpcjE0pkhdihlDouK+x6QOast26B4Q/O9DJnwSg==", "dev": true, "requires": { + "call-bind": "^1.0.0", "define-properties": "^1.1.3", - "es-abstract": "^1.17.0-next.1" - }, - "dependencies": { - "es-abstract": { - "version": "1.17.6", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.6.tgz", - "integrity": "sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw==", - "dev": true, - "requires": { - "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.1", - "is-callable": "^1.2.0", - "is-regex": "^1.1.0", - "object-inspect": "^1.7.0", - "object-keys": "^1.1.1", - "object.assign": "^4.1.0", - "string.prototype.trimend": "^1.0.1", - "string.prototype.trimstart": "^1.0.1" - } - } + "es-abstract": "^1.18.0-next.1" } }, "astral-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz", - "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", "dev": true }, "balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true }, "bluebird": { @@ -224,6 +184,16 @@ "concat-map": "0.0.1" } }, + "call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "dev": true, + "requires": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + } + }, "callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -231,18 +201,18 @@ "dev": true }, "catharsis": { - "version": "0.8.11", - "resolved": "https://registry.npmjs.org/catharsis/-/catharsis-0.8.11.tgz", - "integrity": "sha512-a+xUyMV7hD1BrDQA/3iPV7oc+6W26BgVJO05PGEoatMyIuPScQKsde6i3YorWX1qs+AZjnJ18NqdKoCtKiNh1g==", + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/catharsis/-/catharsis-0.9.0.tgz", + "integrity": "sha512-prMTQVpcns/tzFgFVkVp6ak6RykZyWb3gu8ckUpd6YkTlacOd3DXGJjIpD4Q6zJirizvaiAjSSHlOsA+6sNh2A==", "dev": true, "requires": { - "lodash": "^4.17.14" + "lodash": "^4.17.15" } }, "chalk": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", - "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", + "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", "dev": true, "requires": { "ansi-styles": "^4.1.0", @@ -250,12 +220,11 @@ }, "dependencies": { "ansi-styles": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", - "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, "requires": { - "@types/color-name": "^1.1.1", "color-convert": "^2.0.1" } }, @@ -313,15 +282,9 @@ "dev": true }, "confusing-browser-globals": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/confusing-browser-globals/-/confusing-browser-globals-1.0.9.tgz", - "integrity": "sha512-KbS1Y0jMtyPgIxjO7ZzMAuUpAKMt1SzCL9fsrKsX6b0zJPTaT0SiSPmewwVZg9UAO83HVIlEhZF84LIjZ0lmAw==", - "dev": true - }, - "contains-path": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/contains-path/-/contains-path-0.1.0.tgz", - "integrity": "sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo=", + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/confusing-browser-globals/-/confusing-browser-globals-1.0.10.tgz", + "integrity": "sha512-gNld/3lySHwuhaVluJUKLePYirM3QNCKzVxqAdhJII9/WXKVX5PURzMVJspS1jTslSqjeuG4KMVTSouit5YPHA==", "dev": true }, "cross-spawn": { @@ -336,12 +299,12 @@ } }, "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", "dev": true, "requires": { - "ms": "^2.1.1" + "ms": "2.1.2" } }, "deep-is": { @@ -369,9 +332,9 @@ } }, "emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "dev": true }, "enquirer": { @@ -399,23 +362,27 @@ } }, "es-abstract": { - "version": "1.18.0-next.0", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.0.tgz", - "integrity": "sha512-elZXTZXKn51hUBdJjSZGYRujuzilgXo8vSPQzjGYXLvSlGiCo8VO8ZGV3kjo9a0WNJJ57hENagwbtlRuHuzkcQ==", + "version": "1.18.3", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.3.tgz", + "integrity": "sha512-nQIr12dxV7SSxE6r6f1l3DtAeEYdsGpps13dR0TwJg1S8gyp4ZPgy3FZcHBgbiQqnoqSTb+oC+kO4UQ0C/J8vw==", "dev": true, "requires": { + "call-bind": "^1.0.2", "es-to-primitive": "^1.2.1", "function-bind": "^1.1.1", + "get-intrinsic": "^1.1.1", "has": "^1.0.3", - "has-symbols": "^1.0.1", - "is-callable": "^1.2.0", - "is-negative-zero": "^2.0.0", - "is-regex": "^1.1.1", - "object-inspect": "^1.8.0", + "has-symbols": "^1.0.2", + "is-callable": "^1.2.3", + "is-negative-zero": "^2.0.1", + "is-regex": "^1.1.3", + "is-string": "^1.0.6", + "object-inspect": "^1.10.3", "object-keys": "^1.1.1", - "object.assign": "^4.1.0", - "string.prototype.trimend": "^1.0.1", - "string.prototype.trimstart": "^1.0.1" + "object.assign": "^4.1.2", + "string.prototype.trimend": "^1.0.4", + "string.prototype.trimstart": "^1.0.4", + "unbox-primitive": "^1.0.1" } }, "es-to-primitive": { @@ -430,35 +397,37 @@ } }, "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true }, "eslint": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.9.0.tgz", - "integrity": "sha512-V6QyhX21+uXp4T+3nrNfI3hQNBDa/P8ga7LoQOenwrlEFXrEnUEE+ok1dMtaS3b6rmLXhT1TkTIsG75HMLbknA==", + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.28.0.tgz", + "integrity": "sha512-UMfH0VSjP0G4p3EWirscJEQ/cHqnT/iuH6oNZOB94nBjWbMnhGEPxsZm1eyIW0C/9jLI0Fow4W5DXLjEI7mn1g==", "dev": true, "requires": { - "@babel/code-frame": "^7.0.0", - "@eslint/eslintrc": "^0.1.3", + "@babel/code-frame": "7.12.11", + "@eslint/eslintrc": "^0.4.2", "ajv": "^6.10.0", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", "debug": "^4.0.1", "doctrine": "^3.0.0", "enquirer": "^2.3.5", - "eslint-scope": "^5.1.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^5.1.1", "eslint-utils": "^2.1.0", - "eslint-visitor-keys": "^1.3.0", - "espree": "^7.3.0", - "esquery": "^1.2.0", + "eslint-visitor-keys": "^2.0.0", + "espree": "^7.3.1", + "esquery": "^1.4.0", "esutils": "^2.0.2", - "file-entry-cache": "^5.0.1", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", "functional-red-black-tree": "^1.0.1", - "glob-parent": "^5.0.0", - "globals": "^12.1.0", + "glob-parent": "^5.1.2", + "globals": "^13.6.0", "ignore": "^4.0.6", "import-fresh": "^3.0.0", "imurmurhash": "^0.1.4", @@ -466,7 +435,7 @@ "js-yaml": "^3.13.1", "json-stable-stringify-without-jsonify": "^1.0.1", "levn": "^0.4.1", - "lodash": "^4.17.19", + "lodash.merge": "^4.6.2", "minimatch": "^3.0.4", "natural-compare": "^1.4.0", "optionator": "^0.9.1", @@ -475,19 +444,19 @@ "semver": "^7.2.1", "strip-ansi": "^6.0.0", "strip-json-comments": "^3.1.0", - "table": "^5.2.3", + "table": "^6.0.9", "text-table": "^0.2.0", "v8-compile-cache": "^2.0.3" } }, "eslint-config-airbnb-base": { - "version": "14.2.0", - "resolved": "https://registry.npmjs.org/eslint-config-airbnb-base/-/eslint-config-airbnb-base-14.2.0.tgz", - "integrity": "sha512-Snswd5oC6nJaevs3nZoLSTvGJBvzTfnBqOIArkf3cbyTyq9UD79wOk8s+RiL6bhca0p/eRO6veczhf6A/7Jy8Q==", + "version": "14.2.1", + "resolved": "https://registry.npmjs.org/eslint-config-airbnb-base/-/eslint-config-airbnb-base-14.2.1.tgz", + "integrity": "sha512-GOrQyDtVEc1Xy20U7vsB2yAoB4nBlfH5HZJeatRXHleO+OS5Ot+MWij4Dpltw4/DyIkqUfqz1epfhVR5XWWQPA==", "dev": true, "requires": { - "confusing-browser-globals": "^1.0.9", - "object.assign": "^4.1.0", + "confusing-browser-globals": "^1.0.10", + "object.assign": "^4.1.2", "object.entries": "^1.1.2" } }, @@ -519,50 +488,46 @@ } }, "eslint-module-utils": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.6.0.tgz", - "integrity": "sha512-6j9xxegbqe8/kZY8cYpcp0xhbK0EgJlg3g9mib3/miLaExuuwc3n5UEfSnU6hWMbT0FAYVvDbL9RrRgpUeQIvA==", + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.6.1.tgz", + "integrity": "sha512-ZXI9B8cxAJIH4nfkhTwcRTEAnrVfobYqwjWy/QMCZ8rHkZHFjf9yO4BzpiF9kCSfNlMG54eKigISHpX0+AaT4A==", "dev": true, "requires": { - "debug": "^2.6.9", + "debug": "^3.2.7", "pkg-dir": "^2.0.0" }, "dependencies": { "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", "dev": true, "requires": { - "ms": "2.0.0" + "ms": "^2.1.1" } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true } } }, "eslint-plugin-import": { - "version": "2.22.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.22.0.tgz", - "integrity": "sha512-66Fpf1Ln6aIS5Gr/55ts19eUuoDhAbZgnr6UxK5hbDx6l/QgQgx61AePq+BV4PP2uXQFClgMVzep5zZ94qqsxg==", + "version": "2.23.4", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.23.4.tgz", + "integrity": "sha512-6/wP8zZRsnQFiR3iaPFgh5ImVRM1WN5NUWfTIRqwOdeiGJlBcSk82o1FEVq8yXmy4lkIzTo7YhHCIxlU/2HyEQ==", "dev": true, "requires": { - "array-includes": "^3.1.1", - "array.prototype.flat": "^1.2.3", - "contains-path": "^0.1.0", + "array-includes": "^3.1.3", + "array.prototype.flat": "^1.2.4", "debug": "^2.6.9", - "doctrine": "1.5.0", - "eslint-import-resolver-node": "^0.3.3", - "eslint-module-utils": "^2.6.0", + "doctrine": "^2.1.0", + "eslint-import-resolver-node": "^0.3.4", + "eslint-module-utils": "^2.6.1", + "find-up": "^2.0.0", "has": "^1.0.3", + "is-core-module": "^2.4.0", "minimatch": "^3.0.4", - "object.values": "^1.1.1", - "read-pkg-up": "^2.0.0", - "resolve": "^1.17.0", + "object.values": "^1.1.3", + "pkg-up": "^2.0.0", + "read-pkg-up": "^3.0.0", + "resolve": "^1.20.0", "tsconfig-paths": "^3.9.0" }, "dependencies": { @@ -576,13 +541,12 @@ } }, "doctrine": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz", - "integrity": "sha1-N53Ocw9hZvds76TmcHoVmwLFpvo=", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", "dev": true, "requires": { - "esutils": "^2.0.2", - "isarray": "^1.0.0" + "esutils": "^2.0.2" } }, "ms": { @@ -610,23 +574,39 @@ "dev": true, "requires": { "eslint-visitor-keys": "^1.1.0" + }, + "dependencies": { + "eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true + } } }, "eslint-visitor-keys": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", - "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", "dev": true }, "espree": { - "version": "7.3.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.0.tgz", - "integrity": "sha512-dksIWsvKCixn1yrEXO8UosNSxaDoSYpq9reEjZSbHLpT5hpaCAKTLBwq0RHtLrIr+c0ByiYzWT8KTMRzoRCNlw==", + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.1.tgz", + "integrity": "sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==", "dev": true, "requires": { "acorn": "^7.4.0", - "acorn-jsx": "^5.2.0", + "acorn-jsx": "^5.3.1", "eslint-visitor-keys": "^1.3.0" + }, + "dependencies": { + "eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true + } } }, "esprima": { @@ -636,9 +616,9 @@ "dev": true }, "esquery": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.3.1.tgz", - "integrity": "sha512-olpvt9QG0vniUBZspVRN6lwB7hOZoTRtT+jzR+tS4ffYx2mzbw+z0XCOk44aaLYKApNX5nMm+E+P6o25ip/DHQ==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", + "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", "dev": true, "requires": { "estraverse": "^5.1.0" @@ -700,12 +680,12 @@ "dev": true }, "file-entry-cache": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-5.0.1.tgz", - "integrity": "sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", "dev": true, "requires": { - "flat-cache": "^2.0.1" + "flat-cache": "^3.0.4" } }, "find-up": { @@ -718,20 +698,19 @@ } }, "flat-cache": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz", - "integrity": "sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA==", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", + "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", "dev": true, "requires": { - "flatted": "^2.0.0", - "rimraf": "2.6.3", - "write": "1.0.3" + "flatted": "^3.1.0", + "rimraf": "^3.0.2" } }, "flatted": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.2.tgz", - "integrity": "sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.1.1.tgz", + "integrity": "sha512-zAoAQiudy+r5SvnSw3KJy5os/oRJYHzrzja/tBDqrZtNhUw8bt6y8OBzMWcjWr+8liV8Eb6yOhw8WZ7VFZ5ZzA==", "dev": true }, "fs.realpath": { @@ -752,10 +731,21 @@ "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", "dev": true }, + "get-intrinsic": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", + "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", + "dev": true, + "requires": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1" + } + }, "glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "version": "7.1.7", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", + "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", "dev": true, "requires": { "fs.realpath": "^1.0.0", @@ -767,27 +757,27 @@ } }, "glob-parent": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz", - "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", "dev": true, "requires": { "is-glob": "^4.0.1" } }, "globals": { - "version": "12.4.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz", - "integrity": "sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg==", + "version": "13.9.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.9.0.tgz", + "integrity": "sha512-74/FduwI/JaIrr1H8e71UbDE+5x7pIPs1C2rrwC52SszOo043CsWOZEMW7o2Y58xwm9b+0RBKDxY5n2sUpEFxA==", "dev": true, "requires": { - "type-fest": "^0.8.1" + "type-fest": "^0.20.2" } }, "graceful-fs": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", + "version": "4.2.6", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.6.tgz", + "integrity": "sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==", "dev": true }, "has": { @@ -799,6 +789,12 @@ "function-bind": "^1.1.1" } }, + "has-bigints": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.1.tgz", + "integrity": "sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA==", + "dev": true + }, "has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", @@ -806,9 +802,9 @@ "dev": true }, "has-symbols": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", - "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", + "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==", "dev": true }, "hosted-git-info": { @@ -824,9 +820,9 @@ "dev": true }, "import-fresh": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.1.tgz", - "integrity": "sha512-6e1q1cnWP2RXD9/keSkxHScg508CdXqXWgWBaETNhyuBFz+kUZlKboh+ISK+bU++DmbHimVBrOz/zzPe0sZ3sQ==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", "dev": true, "requires": { "parent-module": "^1.0.0", @@ -861,16 +857,40 @@ "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", "dev": true }, + "is-bigint": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.2.tgz", + "integrity": "sha512-0JV5+SOCQkIdzjBK9buARcV804Ddu7A0Qet6sHi3FimE9ne6m4BGQZfRn+NZiXbBk4F4XmHfDZIipLj9pX8dSA==", + "dev": true + }, + "is-boolean-object": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.1.tgz", + "integrity": "sha512-bXdQWkECBUIAcCkeH1unwJLIpZYaa5VvuygSyS/c2lf719mTKZDU5UdDRlpd01UjADgmW8RfqaP+mRaVPdr/Ng==", + "dev": true, + "requires": { + "call-bind": "^1.0.2" + } + }, "is-callable": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.1.tgz", - "integrity": "sha512-wliAfSzx6V+6WfMOmus1xy0XvSgf/dlStkvTfq7F0g4bOIW0PSUbnyse3NhDwdyYS1ozfUtAAySqTws3z9Eqgg==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.3.tgz", + "integrity": "sha512-J1DcMe8UYTBSrKezuIUTUwjXsho29693unXM2YhJUTR2txK/eG47bvNa/wipPFmZFgr/N6f1GA66dv0mEyTIyQ==", "dev": true }, + "is-core-module": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.4.0.tgz", + "integrity": "sha512-6A2fkfq1rfeQZjxrZJGerpLCTHRNEBiSgnu0+obeJpEPZRUooHgsizvzv0ZjJwOz3iWIHdJtVWJ/tmPr3D21/A==", + "dev": true, + "requires": { + "has": "^1.0.3" + } + }, "is-date-object": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz", - "integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.4.tgz", + "integrity": "sha512-/b4ZVsG7Z5XVtIxs/h9W8nvfLgSAyKYdtGWQLbqy6jA1icmgjf8WCoTKgeS4wy5tYaPePouzFMANbnj94c2Z+A==", "dev": true }, "is-extglob": { @@ -880,9 +900,9 @@ "dev": true }, "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "dev": true }, "is-glob": { @@ -895,41 +915,42 @@ } }, "is-negative-zero": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.0.tgz", - "integrity": "sha1-lVOxIbD6wohp2p7UWeIMdUN4hGE=", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.1.tgz", + "integrity": "sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w==", + "dev": true + }, + "is-number-object": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.5.tgz", + "integrity": "sha512-RU0lI/n95pMoUKu9v1BZP5MBcZuNSVJkMkAG2dJqC4z2GlkGUNeH68SuHuBKBD/XFe+LHZ+f9BKkLET60Niedw==", "dev": true }, "is-regex": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.1.tgz", - "integrity": "sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.3.tgz", + "integrity": "sha512-qSVXFz28HM7y+IWX6vLCsexdlvzT1PJNFSBuaQLQ5o0IEw8UDYW6/2+eCMVyIsbM8CNLX2a/QWmSpyxYEHY7CQ==", "dev": true, "requires": { - "has-symbols": "^1.0.1" + "call-bind": "^1.0.2", + "has-symbols": "^1.0.2" } }, "is-string": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.5.tgz", - "integrity": "sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ==", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.6.tgz", + "integrity": "sha512-2gdzbKUuqtQ3lYNrUTQYoClPhm7oQu4UdpSZMp1/DGgkHBT8E2Z1l0yMdb6D4zNAxwDiMv8MdulKROJGNl0Q0w==", "dev": true }, "is-symbol": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz", - "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", + "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", "dev": true, "requires": { - "has-symbols": "^1.0.1" + "has-symbols": "^1.0.2" } }, - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true - }, "isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", @@ -943,9 +964,9 @@ "dev": true }, "js-yaml": { - "version": "3.14.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.0.tgz", - "integrity": "sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A==", + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", "dev": true, "requires": { "argparse": "^1.0.7", @@ -962,13 +983,14 @@ } }, "jsdoc": { - "version": "github:jsdoc/jsdoc#544a992824631fc652183d8b0b4b1281dc961560", - "from": "github:jsdoc/jsdoc#544a992824631fc652183d8b0b4b1281dc961560", + "version": "3.6.7", + "resolved": "https://registry.npmjs.org/jsdoc/-/jsdoc-3.6.7.tgz", + "integrity": "sha512-sxKt7h0vzCd+3Y81Ey2qinupL6DpRSZJclS04ugHDNmRUXGzqicMJ6iwayhSA0S0DwwX30c5ozyUthr1QKF6uw==", "dev": true, "requires": { "@babel/parser": "^7.9.4", "bluebird": "^3.7.2", - "catharsis": "^0.8.11", + "catharsis": "^0.9.0", "escape-string-regexp": "^2.0.0", "js2xmlparser": "^4.0.1", "klaw": "^3.0.0", @@ -979,7 +1001,7 @@ "requizzle": "^0.2.3", "strip-json-comments": "^3.1.0", "taffydb": "2.6.2", - "underscore": "~1.12.1" + "underscore": "~1.13.1" }, "dependencies": { "escape-string-regexp": { @@ -987,15 +1009,15 @@ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", "dev": true - }, - "mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "dev": true } } }, + "json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", + "dev": true + }, "json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -1046,14 +1068,14 @@ } }, "load-json-file": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", - "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", + "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", "dev": true, "requires": { "graceful-fs": "^4.1.2", - "parse-json": "^2.2.0", - "pify": "^2.0.0", + "parse-json": "^4.0.0", + "pify": "^3.0.0", "strip-bom": "^3.0.0" } }, @@ -1073,6 +1095,33 @@ "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", "dev": true }, + "lodash.clonedeep": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", + "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=", + "dev": true + }, + "lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, + "lodash.truncate": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", + "integrity": "sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM=", + "dev": true + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, "markdown-it": { "version": "10.0.0", "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-10.0.0.tgz", @@ -1093,9 +1142,9 @@ "dev": true }, "marked": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/marked/-/marked-2.0.3.tgz", - "integrity": "sha512-5otztIIcJfPc2qGTN8cVtOJEjNJZ0jwa46INMagrYfk0EvqtRuEHLsEe0LrFS0/q+ZRKT0+kXK7P2T1AN5lWRA==", + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/marked/-/marked-2.0.7.tgz", + "integrity": "sha512-BJXxkuIfJchcXOJWTT2DOL+yFWifFv2yGYOUzvXg8Qz610QKw+sHCvTMYwA+qWGhlA2uivBezChZ/pBy1tWdkQ==", "dev": true }, "mdurl": { @@ -1120,13 +1169,10 @@ "dev": true }, "mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", - "dev": true, - "requires": { - "minimist": "^1.2.5" - } + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true }, "ms": { "version": "2.1.2", @@ -1161,9 +1207,9 @@ } }, "object-inspect": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.8.0.tgz", - "integrity": "sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA==", + "version": "1.10.3", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.10.3.tgz", + "integrity": "sha512-e5mCJlSH7poANfC8z8S9s9S2IN5/4Zb3aZ33f5s8YqoazCFzNLloLU8r5VCG+G7WoqLvAAZoVMcy3tp/3X0Plw==", "dev": true }, "object-keys": { @@ -1173,80 +1219,37 @@ "dev": true }, "object.assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.1.tgz", - "integrity": "sha512-VT/cxmx5yaoHSOTSyrCygIDFco+RsibY2NM0a4RdEeY/4KgqezwFtK1yr3U67xYhqJSlASm2pKhLVzPj2lr4bA==", + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", + "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", "dev": true, "requires": { + "call-bind": "^1.0.0", "define-properties": "^1.1.3", - "es-abstract": "^1.18.0-next.0", "has-symbols": "^1.0.1", "object-keys": "^1.1.1" } }, "object.entries": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.2.tgz", - "integrity": "sha512-BQdB9qKmb/HyNdMNWVr7O3+z5MUIx3aiegEIJqjMBbBf0YT9RRxTJSim4mzFqtyr7PDAHigq0N9dO0m0tRakQA==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.4.tgz", + "integrity": "sha512-h4LWKWE+wKQGhtMjZEBud7uLGhqyLwj8fpHOarZhD2uY3C9cRtk57VQ89ke3moByLXMedqs3XCHzyb4AmA2DjA==", "dev": true, "requires": { + "call-bind": "^1.0.2", "define-properties": "^1.1.3", - "es-abstract": "^1.17.5", - "has": "^1.0.3" - }, - "dependencies": { - "es-abstract": { - "version": "1.17.6", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.6.tgz", - "integrity": "sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw==", - "dev": true, - "requires": { - "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.1", - "is-callable": "^1.2.0", - "is-regex": "^1.1.0", - "object-inspect": "^1.7.0", - "object-keys": "^1.1.1", - "object.assign": "^4.1.0", - "string.prototype.trimend": "^1.0.1", - "string.prototype.trimstart": "^1.0.1" - } - } + "es-abstract": "^1.18.2" } }, "object.values": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.1.tgz", - "integrity": "sha512-WTa54g2K8iu0kmS/us18jEmdv1a4Wi//BZ/DTVYEcH0XhLM5NYdpDHja3gt57VrZLcNAO2WGA+KpWsDBaHt6eA==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.4.tgz", + "integrity": "sha512-TnGo7j4XSnKQoK3MfvkzqKCi0nVe/D9I9IjwTNYdb/fxYHpjrluHVOgw0AF6jrRFGMPHdfuidR09tIDiIvnaSg==", "dev": true, "requires": { + "call-bind": "^1.0.2", "define-properties": "^1.1.3", - "es-abstract": "^1.17.0-next.1", - "function-bind": "^1.1.1", - "has": "^1.0.3" - }, - "dependencies": { - "es-abstract": { - "version": "1.17.6", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.6.tgz", - "integrity": "sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw==", - "dev": true, - "requires": { - "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.1", - "is-callable": "^1.2.0", - "is-regex": "^1.1.0", - "object-inspect": "^1.7.0", - "object-keys": "^1.1.1", - "object.assign": "^4.1.0", - "string.prototype.trimend": "^1.0.1", - "string.prototype.trimstart": "^1.0.1" - } - } + "es-abstract": "^1.18.2" } }, "once": { @@ -1306,12 +1309,13 @@ } }, "parse-json": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", - "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", "dev": true, "requires": { - "error-ex": "^1.2.0" + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" } }, "path-exists": { @@ -1333,24 +1337,24 @@ "dev": true }, "path-parse": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", - "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", "dev": true }, "path-type": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz", - "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", + "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", "dev": true, "requires": { - "pify": "^2.0.0" + "pify": "^3.0.0" } }, "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", "dev": true }, "pkg-dir": { @@ -1362,6 +1366,15 @@ "find-up": "^2.1.0" } }, + "pkg-up": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-2.0.0.tgz", + "integrity": "sha1-yBmscoBZpGHKscOImivjxJoATX8=", + "dev": true, + "requires": { + "find-up": "^2.1.0" + } + }, "prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -1381,30 +1394,36 @@ "dev": true }, "read-pkg": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz", - "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", + "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=", "dev": true, "requires": { - "load-json-file": "^2.0.0", + "load-json-file": "^4.0.0", "normalize-package-data": "^2.3.2", - "path-type": "^2.0.0" + "path-type": "^3.0.0" } }, "read-pkg-up": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz", - "integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-3.0.0.tgz", + "integrity": "sha1-PtSWaF26D4/hGNBpHcUfSh/5bwc=", "dev": true, "requires": { "find-up": "^2.0.0", - "read-pkg": "^2.0.0" + "read-pkg": "^3.0.0" } }, "regexpp": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.1.0.tgz", - "integrity": "sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", + "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", + "dev": true + }, + "require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", "dev": true }, "requizzle": { @@ -1417,11 +1436,12 @@ } }, "resolve": { - "version": "1.17.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz", - "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==", + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", + "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==", "dev": true, "requires": { + "is-core-module": "^2.2.0", "path-parse": "^1.0.6" } }, @@ -1432,19 +1452,22 @@ "dev": true }, "rimraf": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", - "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", "dev": true, "requires": { "glob": "^7.1.3" } }, "semver": { - "version": "7.3.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz", - "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==", - "dev": true + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } }, "shebang-command": { "version": "2.0.0", @@ -1462,14 +1485,40 @@ "dev": true }, "slice-ansi": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz", - "integrity": "sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", "dev": true, "requires": { - "ansi-styles": "^3.2.0", - "astral-regex": "^1.0.0", - "is-fullwidth-code-point": "^2.0.0" + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + } } }, "spdx-correct": { @@ -1499,9 +1548,9 @@ } }, "spdx-license-ids": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.6.tgz", - "integrity": "sha512-+orQK83kyMva3WyPf59k1+Y525csj5JejicWut55zeTWANuN17qSiSLUXWtzHeNWORSvT7GLDJ/E/XiIWoXBTw==", + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.9.tgz", + "integrity": "sha512-Ki212dKK4ogX+xDo4CtOZBVIwhsKBEfsEEcwmJfLQzirgc2jIWdzg40Unxz/HzEUqM1WFzVlQSMF9kZZ2HboLQ==", "dev": true }, "sprintf-js": { @@ -1511,93 +1560,34 @@ "dev": true }, "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", + "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", "dev": true, "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - }, - "dependencies": { - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dev": true, - "requires": { - "ansi-regex": "^4.1.0" - } - } + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" } }, "string.prototype.trimend": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.1.tgz", - "integrity": "sha512-LRPxFUaTtpqYsTeNKaFOw3R4bxIzWOnbQ837QfBylo8jIxtcbK/A/sMV7Q+OAV/vWo+7s25pOE10KYSjaSO06g==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz", + "integrity": "sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==", "dev": true, "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.5" - }, - "dependencies": { - "es-abstract": { - "version": "1.17.6", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.6.tgz", - "integrity": "sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw==", - "dev": true, - "requires": { - "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.1", - "is-callable": "^1.2.0", - "is-regex": "^1.1.0", - "object-inspect": "^1.7.0", - "object-keys": "^1.1.1", - "object.assign": "^4.1.0", - "string.prototype.trimend": "^1.0.1", - "string.prototype.trimstart": "^1.0.1" - } - } + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" } }, "string.prototype.trimstart": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.1.tgz", - "integrity": "sha512-XxZn+QpvrBI1FOcg6dIpxUPgWCPuNXvMD72aaRaUQv1eD4e/Qy8i/hFTe0BUmD60p/QA6bh1avmuPTfNjqVWRw==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz", + "integrity": "sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==", "dev": true, "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.5" - }, - "dependencies": { - "es-abstract": { - "version": "1.17.6", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.6.tgz", - "integrity": "sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw==", - "dev": true, - "requires": { - "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.1", - "is-callable": "^1.2.0", - "is-regex": "^1.1.0", - "object-inspect": "^1.7.0", - "object-keys": "^1.1.1", - "object.assign": "^4.1.0", - "string.prototype.trimend": "^1.0.1", - "string.prototype.trimstart": "^1.0.1" - } - } + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" } }, "strip-ansi": { @@ -1631,15 +1621,37 @@ } }, "table": { - "version": "5.4.6", - "resolved": "https://registry.npmjs.org/table/-/table-5.4.6.tgz", - "integrity": "sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug==", + "version": "6.7.1", + "resolved": "https://registry.npmjs.org/table/-/table-6.7.1.tgz", + "integrity": "sha512-ZGum47Yi6KOOFDE8m223td53ath2enHcYLgOCjGr5ngu8bdIARQk6mN/wRMv4yMRcHnCSnHbCEha4sobQx5yWg==", "dev": true, "requires": { - "ajv": "^6.10.2", - "lodash": "^4.17.14", - "slice-ansi": "^2.1.0", - "string-width": "^3.0.0" + "ajv": "^8.0.1", + "lodash.clonedeep": "^4.5.0", + "lodash.truncate": "^4.4.2", + "slice-ansi": "^4.0.0", + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0" + }, + "dependencies": { + "ajv": { + "version": "8.6.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.6.0.tgz", + "integrity": "sha512-cnUG4NSBiM4YFBxgZIj/In3/6KX+rQ2l2YPRVcvAMQGWEPKuXoPIhxzwqh31jA3IPbI4qEOp/5ILI4ynioXsGQ==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + } + }, + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + } } }, "taffydb": { @@ -1676,9 +1688,9 @@ } }, "type-fest": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", - "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", "dev": true }, "uc.micro": { @@ -1687,25 +1699,37 @@ "integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==", "dev": true }, + "unbox-primitive": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz", + "integrity": "sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw==", + "dev": true, + "requires": { + "function-bind": "^1.1.1", + "has-bigints": "^1.0.1", + "has-symbols": "^1.0.2", + "which-boxed-primitive": "^1.0.2" + } + }, "underscore": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.12.1.tgz", - "integrity": "sha512-hEQt0+ZLDVUMhebKxL4x1BTtDY7bavVofhZ9KZ4aI26X9SRaE+Y3m83XUL1UP2jn8ynjndwCCpEHdUG+9pP1Tw==", + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.1.tgz", + "integrity": "sha512-hzSoAVtJF+3ZtiFX0VgfFPHEDRm7Y/QPjGyNo4TVdnDTdft3tr8hEkD25a1jC+TjTuE7tkHGKkhwCgs9dgBB2g==", "dev": true }, "uri-js": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.0.tgz", - "integrity": "sha512-B0yRTzYdUCCn9n+F4+Gh4yIDtMQcaJsmYBDsTSG8g/OejKBodLQ2IHfN3bM7jUsRXndopT7OIXWdYqc1fjmV6g==", + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", "dev": true, "requires": { "punycode": "^2.1.0" } }, "v8-compile-cache": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.1.1.tgz", - "integrity": "sha512-8OQ9CL+VWyt3JStj7HX7/ciTL2V3Rl1Wf5OL+SNTm0yK1KvtReVulksyeRnCANHHuUxHlQig+JJDlUhBt1NQDQ==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", + "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", "dev": true }, "validate-npm-package-license": { @@ -1727,6 +1751,19 @@ "isexe": "^2.0.0" } }, + "which-boxed-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", + "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "dev": true, + "requires": { + "is-bigint": "^1.0.1", + "is-boolean-object": "^1.1.0", + "is-number-object": "^1.0.4", + "is-string": "^1.0.5", + "is-symbol": "^1.0.3" + } + }, "word-wrap": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", @@ -1739,20 +1776,17 @@ "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", "dev": true }, - "write": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/write/-/write-1.0.3.tgz", - "integrity": "sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig==", - "dev": true, - "requires": { - "mkdirp": "^0.5.1" - } - }, "xmlcreate": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/xmlcreate/-/xmlcreate-2.0.3.tgz", "integrity": "sha512-HgS+X6zAztGa9zIK3Y3LXuJes33Lz9x+YyTxgrkIdabu2vqcGOWwdfCpf1hWLRrd553wd4QCDf6BBO6FfdsRiQ==", "dev": true + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true } } } diff --git a/platform/javascript/package.json b/platform/javascript/package.json index 53748503f9..9dafae30c5 100644 --- a/platform/javascript/package.json +++ b/platform/javascript/package.json @@ -20,9 +20,9 @@ "author": "Godot Engine contributors", "license": "MIT", "devDependencies": { - "eslint": "^7.9.0", - "eslint-config-airbnb-base": "^14.2.0", - "eslint-plugin-import": "^2.22.0", - "jsdoc": "github:jsdoc/jsdoc#544a992824631fc652183d8b0b4b1281dc961560" + "eslint": "^7.28.0", + "eslint-config-airbnb-base": "^14.2.1", + "eslint-plugin-import": "^2.23.4", + "jsdoc": "^3.6.7" } } diff --git a/scene/2d/mesh_instance_2d.cpp b/scene/2d/mesh_instance_2d.cpp index b7a0028199..15008390b7 100644 --- a/scene/2d/mesh_instance_2d.cpp +++ b/scene/2d/mesh_instance_2d.cpp @@ -29,6 +29,7 @@ /*************************************************************************/ #include "mesh_instance_2d.h" +#include "scene/scene_string_names.h" void MeshInstance2D::_notification(int p_what) { if (p_what == NOTIFICATION_DRAW) { @@ -70,7 +71,7 @@ void MeshInstance2D::set_texture(const Ref<Texture2D> &p_texture) { } texture = p_texture; update(); - emit_signal("texture_changed"); + emit_signal(SceneStringNames::get_singleton()->texture_changed); } void MeshInstance2D::set_normal_map(const Ref<Texture2D> &p_texture) { diff --git a/scene/2d/multimesh_instance_2d.cpp b/scene/2d/multimesh_instance_2d.cpp index 72a899370e..1bff2f337d 100644 --- a/scene/2d/multimesh_instance_2d.cpp +++ b/scene/2d/multimesh_instance_2d.cpp @@ -29,6 +29,7 @@ /*************************************************************************/ #include "multimesh_instance_2d.h" +#include "scene/scene_string_names.h" void MultiMeshInstance2D::_notification(int p_what) { if (p_what == NOTIFICATION_DRAW) { @@ -70,7 +71,7 @@ void MultiMeshInstance2D::set_texture(const Ref<Texture2D> &p_texture) { } texture = p_texture; update(); - emit_signal("texture_changed"); + emit_signal(SceneStringNames::get_singleton()->texture_changed); } Ref<Texture2D> MultiMeshInstance2D::get_texture() const { diff --git a/scene/2d/sprite_2d.cpp b/scene/2d/sprite_2d.cpp index 20169b1075..40e0f4523f 100644 --- a/scene/2d/sprite_2d.cpp +++ b/scene/2d/sprite_2d.cpp @@ -153,7 +153,7 @@ void Sprite2D::set_texture(const Ref<Texture2D> &p_texture) { } update(); - emit_signal("texture_changed"); + emit_signal(SceneStringNames::get_singleton()->texture_changed); item_rect_changed(); } diff --git a/scene/2d/tile_map.cpp b/scene/2d/tile_map.cpp index e79dfb019c..e39c8841cd 100644 --- a/scene/2d/tile_map.cpp +++ b/scene/2d/tile_map.cpp @@ -711,12 +711,17 @@ Map<Vector2i, TileMapQuadrant> &TileMap::get_quadrant_map() { void TileMap::fix_invalid_tiles() { ERR_FAIL_COND_MSG(tile_set.is_null(), "Cannot fix invalid tiles if Tileset is not open."); + + Set<Vector2i> coords; for (Map<Vector2i, TileMapCell>::Element *E = tile_map.front(); E; E = E->next()) { TileSetSource *source = *tile_set->get_source(E->get().source_id); if (!source || !source->has_tile(E->get().get_atlas_coords()) || !source->has_alternative_tile(E->get().get_atlas_coords(), E->get().alternative_tile)) { - set_cell(E->key(), -1, TileSetSource::INVALID_ATLAS_COORDS, TileSetSource::INVALID_TILE_ALTERNATIVE); + coords.insert(E->key()); } } + for (Set<Vector2i>::Element *E = coords.front(); E; E = E->next()) { + set_cell(E->get(), -1, TileSetSource::INVALID_ATLAS_COORDS, TileSetSource::INVALID_TILE_ALTERNATIVE); + } } void TileMap::_recreate_quadrants() { diff --git a/scene/3d/camera_3d.cpp b/scene/3d/camera_3d.cpp index 1c9418ae83..6b8851b4f8 100644 --- a/scene/3d/camera_3d.cpp +++ b/scene/3d/camera_3d.cpp @@ -500,6 +500,7 @@ void Camera3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_doppler_tracking", "mode"), &Camera3D::set_doppler_tracking); ClassDB::bind_method(D_METHOD("get_doppler_tracking"), &Camera3D::get_doppler_tracking); ClassDB::bind_method(D_METHOD("get_frustum"), &Camera3D::get_frustum); + ClassDB::bind_method(D_METHOD("is_position_in_frustum", "world_point"), &Camera3D::is_position_in_frustum); ClassDB::bind_method(D_METHOD("get_camera_rid"), &Camera3D::get_camera); ClassDB::bind_method(D_METHOD("set_cull_mask_bit", "layer", "enable"), &Camera3D::set_cull_mask_bit); @@ -623,6 +624,16 @@ Vector<Plane> Camera3D::get_frustum() const { return cm.get_projection_planes(get_camera_transform()); } +bool Camera3D::is_position_in_frustum(const Vector3 &p_position) const { + Vector<Plane> frustum = get_frustum(); + for (int i = 0; i < frustum.size(); i++) { + if (frustum[i].is_point_over(p_position)) { + return false; + } + } + return true; +} + void Camera3D::set_v_offset(float p_offset) { v_offset = p_offset; _update_camera(); diff --git a/scene/3d/camera_3d.h b/scene/3d/camera_3d.h index d9ebe78f1a..c6efc8f9a9 100644 --- a/scene/3d/camera_3d.h +++ b/scene/3d/camera_3d.h @@ -153,6 +153,7 @@ public: bool get_cull_mask_bit(int p_layer) const; virtual Vector<Plane> get_frustum() const; + bool is_position_in_frustum(const Vector3 &p_position) const; void set_environment(const Ref<Environment> &p_environment); Ref<Environment> get_environment() const; diff --git a/scene/3d/node_3d.cpp b/scene/3d/node_3d.cpp index e96e4df55c..e96b8ee1f9 100644 --- a/scene/3d/node_3d.cpp +++ b/scene/3d/node_3d.cpp @@ -32,6 +32,7 @@ #include "core/config/engine.h" #include "core/object/message_queue.h" +#include "scene/3d/visual_instance_3d.h" #include "scene/main/scene_tree.h" #include "scene/main/window.h" #include "scene/scene_string_names.h" @@ -148,6 +149,7 @@ void Node3D::_notification(int p_what) { _notify_dirty(); notification(NOTIFICATION_ENTER_WORLD); + _update_visibility_parent(true); } break; case NOTIFICATION_EXIT_TREE: { @@ -161,6 +163,7 @@ void Node3D::_notification(int p_what) { data.parent = nullptr; data.C = nullptr; data.top_level_active = false; + _update_visibility_parent(true); } break; case NOTIFICATION_ENTER_WORLD: { data.inside_world = true; @@ -282,8 +285,12 @@ Transform3D Node3D::get_local_gizmo_transform() const { } #endif -Node3D *Node3D::get_parent_spatial() const { - return data.parent; +Node3D *Node3D::get_parent_node_3d() const { + if (data.top_level) { + return nullptr; + } + + return Object::cast_to<Node3D>(get_parent()); } Transform3D Node3D::get_relative_transform(const Node *p_parent) const { @@ -690,6 +697,51 @@ void Node3D::force_update_transform() { notification(NOTIFICATION_TRANSFORM_CHANGED); } +void Node3D::_update_visibility_parent(bool p_update_root) { + RID new_parent; + + if (!visibility_parent_path.is_empty()) { + if (!p_update_root) { + return; + } + Node *parent = get_node_or_null(visibility_parent_path); + ERR_FAIL_COND_MSG(!parent, "Can't find visibility parent node at path: " + visibility_parent_path); + ERR_FAIL_COND_MSG(parent == this, "The visibility parent can't be the same node."); + GeometryInstance3D *gi = Object::cast_to<GeometryInstance3D>(parent); + ERR_FAIL_COND_MSG(!gi, "The visibility parent node must be a GeometryInstance3D, at path: " + visibility_parent_path); + new_parent = gi ? gi->get_instance() : RID(); + } else if (data.parent) { + new_parent = data.parent->data.visibility_parent; + } + + if (new_parent == data.visibility_parent) { + return; + } + + data.visibility_parent = new_parent; + + VisualInstance3D *vi = Object::cast_to<VisualInstance3D>(this); + if (vi) { + RS::get_singleton()->instance_set_visibility_parent(vi->get_instance(), data.visibility_parent); + } + + for (List<Node3D *>::Element *E = data.children.front(); E; E = E->next()) { + Node3D *c = E->get(); + c->_update_visibility_parent(false); + } +} + +void Node3D::set_visibility_parent(const NodePath &p_path) { + visibility_parent_path = p_path; + if (is_inside_tree()) { + _update_visibility_parent(true); + } +} + +NodePath Node3D::get_visibility_parent() const { + return visibility_parent_path; +} + void Node3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_transform", "local"), &Node3D::set_transform); ClassDB::bind_method(D_METHOD("get_transform"), &Node3D::get_transform); @@ -703,7 +755,7 @@ void Node3D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_scale"), &Node3D::get_scale); ClassDB::bind_method(D_METHOD("set_global_transform", "global"), &Node3D::set_global_transform); ClassDB::bind_method(D_METHOD("get_global_transform"), &Node3D::get_global_transform); - ClassDB::bind_method(D_METHOD("get_parent_spatial"), &Node3D::get_parent_spatial); + ClassDB::bind_method(D_METHOD("get_parent_node_3d"), &Node3D::get_parent_node_3d); ClassDB::bind_method(D_METHOD("set_ignore_transform_notification", "enabled"), &Node3D::set_ignore_transform_notification); ClassDB::bind_method(D_METHOD("set_as_top_level", "enable"), &Node3D::set_as_top_level); ClassDB::bind_method(D_METHOD("is_set_as_top_level"), &Node3D::is_set_as_top_level); @@ -713,6 +765,9 @@ void Node3D::_bind_methods() { ClassDB::bind_method(D_METHOD("force_update_transform"), &Node3D::force_update_transform); + ClassDB::bind_method(D_METHOD("set_visibility_parent", "path"), &Node3D::set_visibility_parent); + ClassDB::bind_method(D_METHOD("get_visibility_parent"), &Node3D::get_visibility_parent); + ClassDB::bind_method(D_METHOD("_update_gizmo"), &Node3D::_update_gizmo); ClassDB::bind_method(D_METHOD("update_gizmo"), &Node3D::update_gizmo); @@ -768,6 +823,7 @@ void Node3D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM3D, "transform", PROPERTY_HINT_NONE, ""), "set_transform", "get_transform"); ADD_GROUP("Visibility", ""); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "visible"), "set_visible", "is_visible"); + ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "visibility_parent", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "GeometryInstance3D"), "set_visibility_parent", "get_visibility_parent"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "gizmo", PROPERTY_HINT_RESOURCE_TYPE, "Node3DGizmo", 0), "set_gizmo", "get_gizmo"); ADD_SIGNAL(MethodInfo("visibility_changed")); diff --git a/scene/3d/node_3d.h b/scene/3d/node_3d.h index 5f076ceb12..c7e36cf2ec 100644 --- a/scene/3d/node_3d.h +++ b/scene/3d/node_3d.h @@ -75,6 +75,8 @@ class Node3D : public Node { bool top_level = false; bool inside_world = false; + RID visibility_parent; + int children_lock = 0; Node3D *parent = nullptr; List<Node3D *> children; @@ -95,12 +97,17 @@ class Node3D : public Node { } data; + NodePath visibility_parent_path; + void _update_gizmo(); void _notify_dirty(); void _propagate_transform_changed(Node3D *p_origin); void _propagate_visibility_changed(); + void _propagate_visibility_parent(); + void _update_visibility_parent(bool p_update_root); + protected: _FORCE_INLINE_ void set_ignore_transform_notification(bool p_ignore) { data.ignore_notification = p_ignore; } @@ -118,7 +125,7 @@ public: NOTIFICATION_LOCAL_TRANSFORM_CHANGED = 44, }; - Node3D *get_parent_spatial() const; + Node3D *get_parent_node_3d() const; Ref<World3D> get_world_3d() const; @@ -196,6 +203,9 @@ public: void force_update_transform(); + void set_visibility_parent(const NodePath &p_path); + NodePath get_visibility_parent() const; + Node3D(); }; diff --git a/scene/3d/sprite_3d.cpp b/scene/3d/sprite_3d.cpp index be474e82e0..9ec461d973 100644 --- a/scene/3d/sprite_3d.cpp +++ b/scene/3d/sprite_3d.cpp @@ -505,6 +505,7 @@ void Sprite3D::set_texture(const Ref<Texture2D> &p_texture) { texture->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &Sprite3D::_texture_changed)); } _queue_update(); + emit_signal(SceneStringNames::get_singleton()->texture_changed); } Ref<Texture2D> Sprite3D::get_texture() const { @@ -663,6 +664,7 @@ void Sprite3D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::RECT2, "region_rect"), "set_region_rect", "get_region_rect"); ADD_SIGNAL(MethodInfo("frame_changed")); + ADD_SIGNAL(MethodInfo("texture_changed")); } Sprite3D::Sprite3D() { diff --git a/scene/3d/visual_instance_3d.cpp b/scene/3d/visual_instance_3d.cpp index 6971c1ce2a..c16e3c2d78 100644 --- a/scene/3d/visual_instance_3d.cpp +++ b/scene/3d/visual_instance_3d.cpp @@ -150,40 +150,40 @@ Ref<Material> GeometryInstance3D::get_material_override() const { return material_override; } -void GeometryInstance3D::set_lod_min_distance(float p_dist) { - lod_min_distance = p_dist; - RS::get_singleton()->instance_geometry_set_draw_range(get_instance(), lod_min_distance, lod_max_distance, lod_min_hysteresis, lod_max_hysteresis); +void GeometryInstance3D::set_visibility_range_begin(float p_dist) { + visibility_range_begin = p_dist; + RS::get_singleton()->instance_geometry_set_visibility_range(get_instance(), visibility_range_begin, visibility_range_end, visibility_range_begin_margin, visibility_range_end_margin); } -float GeometryInstance3D::get_lod_min_distance() const { - return lod_min_distance; +float GeometryInstance3D::get_visibility_range_begin() const { + return visibility_range_begin; } -void GeometryInstance3D::set_lod_max_distance(float p_dist) { - lod_max_distance = p_dist; - RS::get_singleton()->instance_geometry_set_draw_range(get_instance(), lod_min_distance, lod_max_distance, lod_min_hysteresis, lod_max_hysteresis); +void GeometryInstance3D::set_visibility_range_end(float p_dist) { + visibility_range_end = p_dist; + RS::get_singleton()->instance_geometry_set_visibility_range(get_instance(), visibility_range_begin, visibility_range_end, visibility_range_begin_margin, visibility_range_end_margin); } -float GeometryInstance3D::get_lod_max_distance() const { - return lod_max_distance; +float GeometryInstance3D::get_visibility_range_end() const { + return visibility_range_end; } -void GeometryInstance3D::set_lod_min_hysteresis(float p_dist) { - lod_min_hysteresis = p_dist; - RS::get_singleton()->instance_geometry_set_draw_range(get_instance(), lod_min_distance, lod_max_distance, lod_min_hysteresis, lod_max_hysteresis); +void GeometryInstance3D::set_visibility_range_begin_margin(float p_dist) { + visibility_range_begin_margin = p_dist; + RS::get_singleton()->instance_geometry_set_visibility_range(get_instance(), visibility_range_begin, visibility_range_end, visibility_range_begin_margin, visibility_range_end_margin); } -float GeometryInstance3D::get_lod_min_hysteresis() const { - return lod_min_hysteresis; +float GeometryInstance3D::get_visibility_range_begin_margin() const { + return visibility_range_begin_margin; } -void GeometryInstance3D::set_lod_max_hysteresis(float p_dist) { - lod_max_hysteresis = p_dist; - RS::get_singleton()->instance_geometry_set_draw_range(get_instance(), lod_min_distance, lod_max_distance, lod_min_hysteresis, lod_max_hysteresis); +void GeometryInstance3D::set_visibility_range_end_margin(float p_dist) { + visibility_range_end_margin = p_dist; + RS::get_singleton()->instance_geometry_set_visibility_range(get_instance(), visibility_range_begin, visibility_range_end, visibility_range_begin_margin, visibility_range_end_margin); } -float GeometryInstance3D::get_lod_max_hysteresis() const { - return lod_max_hysteresis; +float GeometryInstance3D::get_visibility_range_end_margin() const { + return visibility_range_end_margin; } void GeometryInstance3D::_notification(int p_what) { @@ -357,17 +357,17 @@ void GeometryInstance3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_lod_bias", "bias"), &GeometryInstance3D::set_lod_bias); ClassDB::bind_method(D_METHOD("get_lod_bias"), &GeometryInstance3D::get_lod_bias); - ClassDB::bind_method(D_METHOD("set_lod_max_hysteresis", "mode"), &GeometryInstance3D::set_lod_max_hysteresis); - ClassDB::bind_method(D_METHOD("get_lod_max_hysteresis"), &GeometryInstance3D::get_lod_max_hysteresis); + ClassDB::bind_method(D_METHOD("set_visibility_range_end_margin", "distance"), &GeometryInstance3D::set_visibility_range_end_margin); + ClassDB::bind_method(D_METHOD("get_visibility_range_end_margin"), &GeometryInstance3D::get_visibility_range_end_margin); - ClassDB::bind_method(D_METHOD("set_lod_max_distance", "mode"), &GeometryInstance3D::set_lod_max_distance); - ClassDB::bind_method(D_METHOD("get_lod_max_distance"), &GeometryInstance3D::get_lod_max_distance); + ClassDB::bind_method(D_METHOD("set_visibility_range_end", "distance"), &GeometryInstance3D::set_visibility_range_end); + ClassDB::bind_method(D_METHOD("get_visibility_range_end"), &GeometryInstance3D::get_visibility_range_end); - ClassDB::bind_method(D_METHOD("set_lod_min_hysteresis", "mode"), &GeometryInstance3D::set_lod_min_hysteresis); - ClassDB::bind_method(D_METHOD("get_lod_min_hysteresis"), &GeometryInstance3D::get_lod_min_hysteresis); + ClassDB::bind_method(D_METHOD("set_visibility_range_begin_margin", "distance"), &GeometryInstance3D::set_visibility_range_begin_margin); + ClassDB::bind_method(D_METHOD("get_visibility_range_begin_margin"), &GeometryInstance3D::get_visibility_range_begin_margin); - ClassDB::bind_method(D_METHOD("set_lod_min_distance", "mode"), &GeometryInstance3D::set_lod_min_distance); - ClassDB::bind_method(D_METHOD("get_lod_min_distance"), &GeometryInstance3D::get_lod_min_distance); + ClassDB::bind_method(D_METHOD("set_visibility_range_begin", "distance"), &GeometryInstance3D::set_visibility_range_begin); + ClassDB::bind_method(D_METHOD("get_visibility_range_begin"), &GeometryInstance3D::get_visibility_range_begin); ClassDB::bind_method(D_METHOD("set_shader_instance_uniform", "uniform", "value"), &GeometryInstance3D::set_shader_instance_uniform); ClassDB::bind_method(D_METHOD("get_shader_instance_uniform", "uniform"), &GeometryInstance3D::get_shader_instance_uniform); @@ -398,11 +398,11 @@ void GeometryInstance3D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "gi_mode", PROPERTY_HINT_ENUM, "Disabled,Baked,Dynamic"), "set_gi_mode", "get_gi_mode"); ADD_PROPERTY(PropertyInfo(Variant::INT, "gi_lightmap_scale", PROPERTY_HINT_ENUM, "1x,2x,4x,8x"), "set_lightmap_scale", "get_lightmap_scale"); - ADD_GROUP("LOD", "lod_"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "lod_min_distance", PROPERTY_HINT_RANGE, "0,32768,0.01"), "set_lod_min_distance", "get_lod_min_distance"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "lod_min_hysteresis", PROPERTY_HINT_RANGE, "0,32768,0.01"), "set_lod_min_hysteresis", "get_lod_min_hysteresis"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "lod_max_distance", PROPERTY_HINT_RANGE, "0,32768,0.01"), "set_lod_max_distance", "get_lod_max_distance"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "lod_max_hysteresis", PROPERTY_HINT_RANGE, "0,32768,0.01"), "set_lod_max_hysteresis", "get_lod_max_hysteresis"); + ADD_GROUP("Visibility Range", "visibility_range_"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "visibility_range_begin", PROPERTY_HINT_RANGE, "0.0,4096.0,0.01"), "set_visibility_range_begin", "get_visibility_range_begin"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "visibility_range_begin_margin", PROPERTY_HINT_RANGE, "0.0,4096.0,0.01"), "set_visibility_range_begin_margin", "get_visibility_range_begin_margin"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "visibility_range_end", PROPERTY_HINT_RANGE, "0.0,4096.0,0.01"), "set_visibility_range_end", "get_visibility_range_end"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "visibility_range_end_margin", PROPERTY_HINT_RANGE, "0.0,4096.0,0.01"), "set_visibility_range_end_margin", "get_visibility_range_end_margin"); //ADD_SIGNAL( MethodInfo("visibility_changed")); diff --git a/scene/3d/visual_instance_3d.h b/scene/3d/visual_instance_3d.h index 68d29ef81e..2d5699859b 100644 --- a/scene/3d/visual_instance_3d.h +++ b/scene/3d/visual_instance_3d.h @@ -107,10 +107,13 @@ public: private: ShadowCastingSetting shadow_casting_setting = SHADOW_CASTING_SETTING_ON; Ref<Material> material_override; - float lod_min_distance = 0.0; - float lod_max_distance = 0.0; - float lod_min_hysteresis = 0.0; - float lod_max_hysteresis = 0.0; + + float visibility_range_begin = 0.0; + float visibility_range_end = 0.0; + float visibility_range_begin_margin = 0.0; + float visibility_range_end_margin = 0.0; + + Vector<NodePath> visibility_range_children; float lod_bias = 1.0; @@ -136,17 +139,20 @@ public: void set_cast_shadows_setting(ShadowCastingSetting p_shadow_casting_setting); ShadowCastingSetting get_cast_shadows_setting() const; - void set_lod_min_distance(float p_dist); - float get_lod_min_distance() const; + void set_visibility_range_begin(float p_dist); + float get_visibility_range_begin() const; + + void set_visibility_range_end(float p_dist); + float get_visibility_range_end() const; - void set_lod_max_distance(float p_dist); - float get_lod_max_distance() const; + void set_visibility_range_begin_margin(float p_dist); + float get_visibility_range_begin_margin() const; - void set_lod_min_hysteresis(float p_dist); - float get_lod_min_hysteresis() const; + void set_visibility_range_end_margin(float p_dist); + float get_visibility_range_end_margin() const; - void set_lod_max_hysteresis(float p_dist); - float get_lod_max_hysteresis() const; + void set_visibility_range_parent(const Node *p_parent); + void clear_visibility_range_parent(); void set_material_override(const Ref<Material> &p_material); Ref<Material> get_material_override() const; diff --git a/scene/3d/xr_nodes.cpp b/scene/3d/xr_nodes.cpp index 09662f3e7a..4f2c816934 100644 --- a/scene/3d/xr_nodes.cpp +++ b/scene/3d/xr_nodes.cpp @@ -86,7 +86,8 @@ Vector3 XRCamera3D::project_local_ray_normal(const Point2 &p_pos) const { Vector2 cpos = get_viewport()->get_camera_coords(p_pos); Vector3 ray; - CameraMatrix cm = xr_interface->get_projection_for_eye(XRInterface::EYE_MONO, viewport_size.aspect(), get_near(), get_far()); + // Just use the first view, if multiple views are supported this function has no good result + CameraMatrix cm = xr_interface->get_projection_for_view(0, viewport_size.aspect(), get_near(), get_far()); Vector2 screen_he = cm.get_viewport_half_extents(); ray = Vector3(((cpos.x / viewport_size.width) * 2.0 - 1.0) * screen_he.x, ((1.0 - (cpos.y / viewport_size.height)) * 2.0 - 1.0) * screen_he.y, -get_near()).normalized(); @@ -108,7 +109,8 @@ Point2 XRCamera3D::unproject_position(const Vector3 &p_pos) const { Size2 viewport_size = get_viewport()->get_visible_rect().size; - CameraMatrix cm = xr_interface->get_projection_for_eye(XRInterface::EYE_MONO, viewport_size.aspect(), get_near(), get_far()); + // Just use the first view, if multiple views are supported this function has no good result + CameraMatrix cm = xr_interface->get_projection_for_view(0, viewport_size.aspect(), get_near(), get_far()); Plane p(get_camera_transform().xform_inv(p_pos), 1.0); @@ -137,7 +139,8 @@ Vector3 XRCamera3D::project_position(const Point2 &p_point, float p_z_depth) con Size2 viewport_size = get_viewport()->get_visible_rect().size; - CameraMatrix cm = xr_interface->get_projection_for_eye(XRInterface::EYE_MONO, viewport_size.aspect(), get_near(), get_far()); + // Just use the first view, if multiple views are supported this function has no good result + CameraMatrix cm = xr_interface->get_projection_for_view(0, viewport_size.aspect(), get_near(), get_far()); Vector2 vp_he = cm.get_viewport_half_extents(); @@ -165,7 +168,8 @@ Vector<Plane> XRCamera3D::get_frustum() const { ERR_FAIL_COND_V(!is_inside_world(), Vector<Plane>()); Size2 viewport_size = get_viewport()->get_visible_rect().size; - CameraMatrix cm = xr_interface->get_projection_for_eye(XRInterface::EYE_MONO, viewport_size.aspect(), get_near(), get_far()); + // TODO Just use the first view for now, this is mostly for debugging so we may look into using our combined projection here. + CameraMatrix cm = xr_interface->get_projection_for_view(0, viewport_size.aspect(), get_near(), get_far()); return cm.get_projection_planes(get_camera_transform()); }; @@ -516,6 +520,11 @@ TypedArray<String> XROrigin3D::get_configuration_warnings() const { } } + bool xr_enabled = GLOBAL_GET("rendering/xr/enabled"); + if (!xr_enabled) { + warnings.push_back(TTR("XR is not enabled in rendering project settings. Stereoscopic output is not supported unless this is enabled.")); + } + return warnings; }; @@ -571,7 +580,7 @@ void XROrigin3D::_notification(int p_what) { Ref<XRInterface> xr_interface = xr_server->get_primary_interface(); if (xr_interface.is_valid() && tracked_camera != nullptr) { // get our positioning transform for our headset - Transform3D t = xr_interface->get_transform_for_eye(XRInterface::EYE_MONO, Transform3D()); + Transform3D t = xr_interface->get_camera_transform(); // now apply this to our camera tracked_camera->set_transform(t); diff --git a/scene/gui/code_edit.cpp b/scene/gui/code_edit.cpp index d5000e88d7..48e327ce78 100644 --- a/scene/gui/code_edit.cpp +++ b/scene/gui/code_edit.cpp @@ -1526,6 +1526,9 @@ void CodeEdit::_clear_delimiters(DelimiterType p_type) { } } delimiter_cache.clear(); + if (!setting_delimiters) { + _update_delimiter_cache(); + } } TypedArray<String> CodeEdit::_get_delimiters(DelimiterType p_type) const { diff --git a/scene/gui/nine_patch_rect.cpp b/scene/gui/nine_patch_rect.cpp index 29a38ad5e3..8bf25ac915 100644 --- a/scene/gui/nine_patch_rect.cpp +++ b/scene/gui/nine_patch_rect.cpp @@ -30,6 +30,7 @@ #include "nine_patch_rect.h" +#include "scene/scene_string_names.h" #include "servers/rendering_server.h" void NinePatchRect::_notification(int p_what) { @@ -97,7 +98,7 @@ void NinePatchRect::set_texture(const Ref<Texture2D> &p_tex) { texture->set_flags(texture->get_flags()&(~Texture::FLAG_REPEAT)); //remove repeat from texture, it looks bad in sprites */ minimum_size_changed(); - emit_signal("texture_changed"); + emit_signal(SceneStringNames::get_singleton()->texture_changed); } Ref<Texture2D> NinePatchRect::get_texture() const { diff --git a/scene/gui/slider.cpp b/scene/gui/slider.cpp index a407ef21cb..5947f3b99e 100644 --- a/scene/gui/slider.cpp +++ b/scene/gui/slider.cpp @@ -172,7 +172,7 @@ void Slider::_notification(int p_what) { int widget_width = style->get_minimum_size().width + style->get_center_size().width; float areasize = size.height - grabber->get_size().height; style->draw(ci, Rect2i(Point2i(size.width / 2 - widget_width / 2, 0), Size2i(widget_width, size.height))); - grabber_area->draw(ci, Rect2i(Point2i((size.width - widget_width) / 2, size.height - areasize * ratio - grabber->get_size().height / 2), Size2i(widget_width, areasize * ratio + grabber->get_size().width / 2))); + grabber_area->draw(ci, Rect2i(Point2i((size.width - widget_width) / 2, size.height - areasize * ratio - grabber->get_size().height / 2), Size2i(widget_width, areasize * ratio + grabber->get_size().height / 2))); if (ticks > 1) { int grabber_offset = (grabber->get_size().height / 2 - tick->get_height() / 2); diff --git a/scene/resources/skeleton_modification_2d.cpp b/scene/resources/skeleton_modification_2d.cpp index 9b07293965..b52a60006a 100644 --- a/scene/resources/skeleton_modification_2d.cpp +++ b/scene/resources/skeleton_modification_2d.cpp @@ -216,7 +216,9 @@ void SkeletonModification2D::set_editor_draw_gizmo(bool p_draw_gizmo) { editor_draw_gizmo = p_draw_gizmo; #ifdef TOOLS_ENABLED if (is_setup) { - stack->set_editor_gizmos_dirty(true); + if (stack) { + stack->set_editor_gizmos_dirty(true); + } } #endif // TOOLS_ENABLED } diff --git a/scene/resources/style_box.cpp b/scene/resources/style_box.cpp index 2159f1bc97..87371224e0 100644 --- a/scene/resources/style_box.cpp +++ b/scene/resources/style_box.cpp @@ -121,7 +121,6 @@ void StyleBoxTexture::set_texture(Ref<Texture2D> p_texture) { } else { region_rect = Rect2(Point2(), texture->get_size()); } - emit_signal("texture_changed"); emit_changed(); } @@ -285,8 +284,6 @@ void StyleBoxTexture::_bind_methods() { ClassDB::bind_method(D_METHOD("set_v_axis_stretch_mode", "mode"), &StyleBoxTexture::set_v_axis_stretch_mode); ClassDB::bind_method(D_METHOD("get_v_axis_stretch_mode"), &StyleBoxTexture::get_v_axis_stretch_mode); - ADD_SIGNAL(MethodInfo("texture_changed")); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_texture", "get_texture"); ADD_PROPERTY(PropertyInfo(Variant::RECT2, "region_rect"), "set_region_rect", "get_region_rect"); ADD_GROUP("Margin", "margin_"); diff --git a/scene/resources/theme.cpp b/scene/resources/theme.cpp index d6a6f31fad..89ac033207 100644 --- a/scene/resources/theme.cpp +++ b/scene/resources/theme.cpp @@ -33,6 +33,11 @@ #include "core/string/print_string.h" void Theme::_emit_theme_changed() { + if (no_change_propagation) { + return; + } + + notify_property_list_changed(); emit_changed(); } @@ -415,8 +420,7 @@ void Theme::set_default_theme_font(const Ref<Font> &p_default_font) { default_theme_font->connect("changed", callable_mp(this, &Theme::_emit_theme_changed), varray(), CONNECT_REFERENCE_COUNTED); } - notify_property_list_changed(); - emit_changed(); + _emit_theme_changed(); } Ref<Font> Theme::get_default_theme_font() const { @@ -430,8 +434,7 @@ void Theme::set_default_theme_font_size(int p_font_size) { default_theme_font_size = p_font_size; - notify_property_list_changed(); - emit_changed(); + _emit_theme_changed(); } int Theme::get_default_theme_font_size() const { @@ -478,8 +481,6 @@ void Theme::set_default_font_size(int p_font_size) { } void Theme::set_icon(const StringName &p_name, const StringName &p_theme_type, const Ref<Texture2D> &p_icon) { - bool new_value = !icon_map.has(p_theme_type) || !icon_map[p_theme_type].has(p_name); - if (icon_map[p_theme_type].has(p_name) && icon_map[p_theme_type][p_name].is_valid()) { icon_map[p_theme_type][p_name]->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed)); } @@ -490,10 +491,7 @@ void Theme::set_icon(const StringName &p_name, const StringName &p_theme_type, c icon_map[p_theme_type][p_name]->connect("changed", callable_mp(this, &Theme::_emit_theme_changed), varray(), CONNECT_REFERENCE_COUNTED); } - if (new_value) { - notify_property_list_changed(); - emit_changed(); - } + _emit_theme_changed(); } Ref<Texture2D> Theme::get_icon(const StringName &p_name, const StringName &p_theme_type) const { @@ -520,8 +518,7 @@ void Theme::rename_icon(const StringName &p_old_name, const StringName &p_name, icon_map[p_theme_type][p_name] = icon_map[p_theme_type][p_old_name]; icon_map[p_theme_type].erase(p_old_name); - notify_property_list_changed(); - emit_changed(); + _emit_theme_changed(); } void Theme::clear_icon(const StringName &p_name, const StringName &p_theme_type) { @@ -534,8 +531,7 @@ void Theme::clear_icon(const StringName &p_name, const StringName &p_theme_type) icon_map[p_theme_type].erase(p_name); - notify_property_list_changed(); - emit_changed(); + _emit_theme_changed(); } void Theme::get_icon_list(StringName p_theme_type, List<StringName> *p_list) const { @@ -569,8 +565,6 @@ void Theme::get_icon_type_list(List<StringName> *p_list) const { } void Theme::set_stylebox(const StringName &p_name, const StringName &p_theme_type, const Ref<StyleBox> &p_style) { - bool new_value = !style_map.has(p_theme_type) || !style_map[p_theme_type].has(p_name); - if (style_map[p_theme_type].has(p_name) && style_map[p_theme_type][p_name].is_valid()) { style_map[p_theme_type][p_name]->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed)); } @@ -581,10 +575,7 @@ void Theme::set_stylebox(const StringName &p_name, const StringName &p_theme_typ style_map[p_theme_type][p_name]->connect("changed", callable_mp(this, &Theme::_emit_theme_changed), varray(), CONNECT_REFERENCE_COUNTED); } - if (new_value) { - notify_property_list_changed(); - } - emit_changed(); + _emit_theme_changed(); } Ref<StyleBox> Theme::get_stylebox(const StringName &p_name, const StringName &p_theme_type) const { @@ -611,8 +602,7 @@ void Theme::rename_stylebox(const StringName &p_old_name, const StringName &p_na style_map[p_theme_type][p_name] = style_map[p_theme_type][p_old_name]; style_map[p_theme_type].erase(p_old_name); - notify_property_list_changed(); - emit_changed(); + _emit_theme_changed(); } void Theme::clear_stylebox(const StringName &p_name, const StringName &p_theme_type) { @@ -625,8 +615,7 @@ void Theme::clear_stylebox(const StringName &p_name, const StringName &p_theme_t style_map[p_theme_type].erase(p_name); - notify_property_list_changed(); - emit_changed(); + _emit_theme_changed(); } void Theme::get_stylebox_list(StringName p_theme_type, List<StringName> *p_list) const { @@ -660,8 +649,6 @@ void Theme::get_stylebox_type_list(List<StringName> *p_list) const { } void Theme::set_font(const StringName &p_name, const StringName &p_theme_type, const Ref<Font> &p_font) { - bool new_value = !font_map.has(p_theme_type) || !font_map[p_theme_type].has(p_name); - if (font_map[p_theme_type][p_name].is_valid()) { font_map[p_theme_type][p_name]->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed)); } @@ -672,10 +659,7 @@ void Theme::set_font(const StringName &p_name, const StringName &p_theme_type, c font_map[p_theme_type][p_name]->connect("changed", callable_mp(this, &Theme::_emit_theme_changed), varray(), CONNECT_REFERENCE_COUNTED); } - if (new_value) { - notify_property_list_changed(); - emit_changed(); - } + _emit_theme_changed(); } Ref<Font> Theme::get_font(const StringName &p_name, const StringName &p_theme_type) const { @@ -704,8 +688,7 @@ void Theme::rename_font(const StringName &p_old_name, const StringName &p_name, font_map[p_theme_type][p_name] = font_map[p_theme_type][p_old_name]; font_map[p_theme_type].erase(p_old_name); - notify_property_list_changed(); - emit_changed(); + _emit_theme_changed(); } void Theme::clear_font(const StringName &p_name, const StringName &p_theme_type) { @@ -717,8 +700,8 @@ void Theme::clear_font(const StringName &p_name, const StringName &p_theme_type) } font_map[p_theme_type].erase(p_name); - notify_property_list_changed(); - emit_changed(); + + _emit_theme_changed(); } void Theme::get_font_list(StringName p_theme_type, List<StringName> *p_list) const { @@ -752,14 +735,9 @@ void Theme::get_font_type_list(List<StringName> *p_list) const { } void Theme::set_font_size(const StringName &p_name, const StringName &p_theme_type, int p_font_size) { - bool new_value = !font_size_map.has(p_theme_type) || !font_size_map[p_theme_type].has(p_name); - font_size_map[p_theme_type][p_name] = p_font_size; - if (new_value) { - notify_property_list_changed(); - emit_changed(); - } + _emit_theme_changed(); } int Theme::get_font_size(const StringName &p_name, const StringName &p_theme_type) const { @@ -788,8 +766,7 @@ void Theme::rename_font_size(const StringName &p_old_name, const StringName &p_n font_size_map[p_theme_type][p_name] = font_size_map[p_theme_type][p_old_name]; font_size_map[p_theme_type].erase(p_old_name); - notify_property_list_changed(); - emit_changed(); + _emit_theme_changed(); } void Theme::clear_font_size(const StringName &p_name, const StringName &p_theme_type) { @@ -797,8 +774,8 @@ void Theme::clear_font_size(const StringName &p_name, const StringName &p_theme_ ERR_FAIL_COND_MSG(!font_size_map[p_theme_type].has(p_name), "Cannot clear the font size '" + String(p_name) + "' because it does not exist."); font_size_map[p_theme_type].erase(p_name); - notify_property_list_changed(); - emit_changed(); + + _emit_theme_changed(); } void Theme::get_font_size_list(StringName p_theme_type, List<StringName> *p_list) const { @@ -832,14 +809,9 @@ void Theme::get_font_size_type_list(List<StringName> *p_list) const { } void Theme::set_color(const StringName &p_name, const StringName &p_theme_type, const Color &p_color) { - bool new_value = !color_map.has(p_theme_type) || !color_map[p_theme_type].has(p_name); - color_map[p_theme_type][p_name] = p_color; - if (new_value) { - notify_property_list_changed(); - emit_changed(); - } + _emit_theme_changed(); } Color Theme::get_color(const StringName &p_name, const StringName &p_theme_type) const { @@ -866,8 +838,7 @@ void Theme::rename_color(const StringName &p_old_name, const StringName &p_name, color_map[p_theme_type][p_name] = color_map[p_theme_type][p_old_name]; color_map[p_theme_type].erase(p_old_name); - notify_property_list_changed(); - emit_changed(); + _emit_theme_changed(); } void Theme::clear_color(const StringName &p_name, const StringName &p_theme_type) { @@ -875,8 +846,8 @@ void Theme::clear_color(const StringName &p_name, const StringName &p_theme_type ERR_FAIL_COND_MSG(!color_map[p_theme_type].has(p_name), "Cannot clear the color '" + String(p_name) + "' because it does not exist."); color_map[p_theme_type].erase(p_name); - notify_property_list_changed(); - emit_changed(); + + _emit_theme_changed(); } void Theme::get_color_list(StringName p_theme_type, List<StringName> *p_list) const { @@ -910,13 +881,9 @@ void Theme::get_color_type_list(List<StringName> *p_list) const { } void Theme::set_constant(const StringName &p_name, const StringName &p_theme_type, int p_constant) { - bool new_value = !constant_map.has(p_theme_type) || !constant_map[p_theme_type].has(p_name); constant_map[p_theme_type][p_name] = p_constant; - if (new_value) { - notify_property_list_changed(); - emit_changed(); - } + _emit_theme_changed(); } int Theme::get_constant(const StringName &p_name, const StringName &p_theme_type) const { @@ -943,8 +910,7 @@ void Theme::rename_constant(const StringName &p_old_name, const StringName &p_na constant_map[p_theme_type][p_name] = constant_map[p_theme_type][p_old_name]; constant_map[p_theme_type].erase(p_old_name); - notify_property_list_changed(); - emit_changed(); + _emit_theme_changed(); } void Theme::clear_constant(const StringName &p_name, const StringName &p_theme_type) { @@ -952,8 +918,8 @@ void Theme::clear_constant(const StringName &p_name, const StringName &p_theme_t ERR_FAIL_COND_MSG(!constant_map[p_theme_type].has(p_name), "Cannot clear the constant '" + String(p_name) + "' because it does not exist."); constant_map[p_theme_type].erase(p_name); - notify_property_list_changed(); - emit_changed(); + + _emit_theme_changed(); } void Theme::get_constant_list(StringName p_theme_type, List<StringName> *p_list) const { @@ -1217,8 +1183,17 @@ void Theme::get_theme_item_type_list(DataType p_data_type, List<StringName> *p_l } } +void Theme::_freeze_change_propagation() { + no_change_propagation = true; +} + +void Theme::_unfreeze_and_propagate_changes() { + no_change_propagation = false; + _emit_theme_changed(); +} + void Theme::clear() { - //these need disconnecting + // These items need disconnecting. { const StringName *K = nullptr; while ((K = icon_map.next(K))) { @@ -1264,8 +1239,7 @@ void Theme::clear() { color_map.clear(); constant_map.clear(); - notify_property_list_changed(); - emit_changed(); + _emit_theme_changed(); } void Theme::copy_default_theme() { @@ -1279,6 +1253,8 @@ void Theme::copy_theme(const Ref<Theme> &p_other) { return; } + _freeze_change_propagation(); + // These items need reconnecting, so add them normally. { const StringName *K = nullptr; @@ -1315,8 +1291,7 @@ void Theme::copy_theme(const Ref<Theme> &p_other) { color_map = p_other->color_map; constant_map = p_other->constant_map; - notify_property_list_changed(); - emit_changed(); + _unfreeze_and_propagate_changes(); } void Theme::get_type_list(List<StringName> *p_list) const { diff --git a/scene/resources/theme.h b/scene/resources/theme.h index 4de1f065e1..fe64fd7290 100644 --- a/scene/resources/theme.h +++ b/scene/resources/theme.h @@ -41,6 +41,12 @@ class Theme : public Resource { GDCLASS(Theme, Resource); RES_BASE_EXTENSION("theme"); +#ifdef TOOLS_ENABLED + friend class ThemeItemImportTree; + friend class ThemeItemEditorDialog; + friend class ThemeTypeEditor; +#endif + public: enum DataType { DATA_TYPE_COLOR, @@ -53,6 +59,8 @@ public: }; private: + bool no_change_propagation = false; + void _emit_theme_changed(); HashMap<StringName, HashMap<StringName, Ref<Texture2D>>> icon_map; @@ -96,6 +104,9 @@ protected: static void _bind_methods(); + void _freeze_change_propagation(); + void _unfreeze_and_propagate_changes(); + virtual void reset_state() override; public: diff --git a/scene/scene_string_names.cpp b/scene/scene_string_names.cpp index 8e5f271cc7..8acab79c9e 100644 --- a/scene/scene_string_names.cpp +++ b/scene/scene_string_names.cpp @@ -175,6 +175,7 @@ SceneStringNames::SceneStringNames() { _toggled = StaticCString::create("_toggled"); frame_changed = StaticCString::create("frame_changed"); + texture_changed = StaticCString::create("texture_changed"); playback_speed = StaticCString::create("playback/speed"); playback_active = StaticCString::create("playback/active"); diff --git a/scene/scene_string_names.h b/scene/scene_string_names.h index 10b0707cab..0c528a45f7 100644 --- a/scene/scene_string_names.h +++ b/scene/scene_string_names.h @@ -184,6 +184,7 @@ public: StringName _mouse_exit; StringName frame_changed; + StringName texture_changed; StringName playback_speed; StringName playback_active; diff --git a/servers/rendering/rasterizer_dummy.h b/servers/rendering/rasterizer_dummy.h index 96c9137c6e..b44088e822 100644 --- a/servers/rendering/rasterizer_dummy.h +++ b/servers/rendering/rasterizer_dummy.h @@ -175,7 +175,7 @@ public: void voxel_gi_set_quality(RS::VoxelGIQuality) override {} - void render_scene(RID p_render_buffers, const Transform3D &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_ortogonal, const PagedArray<GeometryInstance *> &p_instances, const PagedArray<RID> &p_lights, const PagedArray<RID> &p_reflection_probes, const PagedArray<RID> &p_voxel_gi_instances, const PagedArray<RID> &p_decals, const PagedArray<RID> &p_lightmaps, RID p_environment, RID p_camera_effects, RID p_shadow_atlas, RID p_occluder_debug_tex, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass, float p_screen_lod_threshold, const RenderShadowData *p_render_shadows, int p_render_shadow_count, const RenderSDFGIData *p_render_sdfgi_regions, int p_render_sdfgi_region_count, const RenderSDFGIUpdateData *p_sdfgi_update_data = nullptr) override {} + void render_scene(RID p_render_buffers, const CameraData *p_camera_data, const PagedArray<GeometryInstance *> &p_instances, const PagedArray<RID> &p_lights, const PagedArray<RID> &p_reflection_probes, const PagedArray<RID> &p_voxel_gi_instances, const PagedArray<RID> &p_decals, const PagedArray<RID> &p_lightmaps, RID p_environment, RID p_camera_effects, RID p_shadow_atlas, RID p_occluder_debug_tex, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass, float p_screen_lod_threshold, const RenderShadowData *p_render_shadows, int p_render_shadow_count, const RenderSDFGIData *p_render_sdfgi_regions, int p_render_sdfgi_region_count, const RenderSDFGIUpdateData *p_sdfgi_update_data = nullptr) override {} void render_material(const Transform3D &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_ortogonal, const PagedArray<GeometryInstance *> &p_instances, RID p_framebuffer, const Rect2i &p_region) override {} void render_particle_collider_heightfield(RID p_collider, const Transform3D &p_transform, const PagedArray<GeometryInstance *> &p_instances) override {} @@ -184,7 +184,7 @@ public: void set_debug_draw_mode(RS::ViewportDebugDraw p_debug_draw) override {} RID render_buffers_create() override { return RID(); } - void render_buffers_configure(RID p_render_buffers, RID p_render_target, int p_width, int p_height, RS::ViewportMSAA p_msaa, RS::ViewportScreenSpaceAA p_screen_space_aa, bool p_use_debanding) override {} + void render_buffers_configure(RID p_render_buffers, RID p_render_target, int p_width, int p_height, RS::ViewportMSAA p_msaa, RS::ViewportScreenSpaceAA p_screen_space_aa, bool p_use_debanding, uint32_t p_view_count) override {} void gi_set_use_half_resolution(bool p_enable) override {} void screen_space_roughness_limiter_set_active(bool p_enable, float p_amount, float p_curve) override {} @@ -649,7 +649,7 @@ public: RID render_target_create() override { return RID(); } void render_target_set_position(RID p_render_target, int p_x, int p_y) override {} - void render_target_set_size(RID p_render_target, int p_width, int p_height) override {} + void render_target_set_size(RID p_render_target, int p_width, int p_height, uint32_t p_view_count) override {} RID render_target_get_texture(RID p_render_target) override { return RID(); } void render_target_set_external_texture(RID p_render_target, unsigned int p_texture_id) override {} void render_target_set_flag(RID p_render_target, RenderTargetFlags p_flag, bool p_value) override {} diff --git a/servers/rendering/renderer_compositor.cpp b/servers/rendering/renderer_compositor.cpp index 8861522d34..80c4625261 100644 --- a/servers/rendering/renderer_compositor.cpp +++ b/servers/rendering/renderer_compositor.cpp @@ -30,6 +30,7 @@ #include "renderer_compositor.h" +#include "core/config/project_settings.h" #include "core/os/os.h" #include "core/string/print_string.h" @@ -39,4 +40,12 @@ RendererCompositor *RendererCompositor::create() { return _create_func(); } +bool RendererCompositor::is_xr_enabled() const { + return xr_enabled; +} + +RendererCompositor::RendererCompositor() { + xr_enabled = GLOBAL_GET("rendering/xr/enabled"); +} + RendererCanvasRender *RendererCanvasRender::singleton = nullptr; diff --git a/servers/rendering/renderer_compositor.h b/servers/rendering/renderer_compositor.h index 41aaba0f4c..eabdebf4b3 100644 --- a/servers/rendering/renderer_compositor.h +++ b/servers/rendering/renderer_compositor.h @@ -40,7 +40,31 @@ #include "servers/rendering/renderer_storage.h" #include "servers/rendering_server.h" +struct BlitToScreen { + RID render_target; + Rect2i rect; + + struct { + bool use_layer = false; + uint32_t layer = 0; + } multi_view; + + struct { + //lens distorted parameters for VR + bool apply = false; + Vector2 eye_center; + float k1 = 0.0; + float k2 = 0.0; + + float upscale = 1.0; + float aspect_ratio = 1.0; + } lens_distortion; +}; + class RendererCompositor { +private: + bool xr_enabled = false; + protected: static RendererCompositor *(*_create_func)(); @@ -56,27 +80,6 @@ public: virtual void initialize() = 0; virtual void begin_frame(double frame_step) = 0; - struct BlitToScreen { - RID render_target; - Rect2i rect; - - struct { - bool use_layer = false; - uint32_t layer = 0; - } multi_view; - - struct { - //lens distorted parameters for VR - bool apply = false; - Vector2 eye_center; - float k1 = 0.0; - float k2 = 0.0; - - float upscale = 1.0; - float aspect_ratio = 1.0; - } lens_distortion; - }; - virtual void prepare_for_blitting_render_targets() = 0; virtual void blit_render_targets_to_screen(DisplayServer::WindowID p_screen, const BlitToScreen *p_render_targets, int p_amount) = 0; @@ -86,7 +89,9 @@ public: virtual float get_frame_delta_time() const = 0; virtual bool is_low_end() const = 0; + virtual bool is_xr_enabled() const; + RendererCompositor(); virtual ~RendererCompositor() {} }; diff --git a/servers/rendering/renderer_rd/effects_rd.cpp b/servers/rendering/renderer_rd/effects_rd.cpp index 872f7b5beb..4fd5520e56 100644 --- a/servers/rendering/renderer_rd/effects_rd.cpp +++ b/servers/rendering/renderer_rd/effects_rd.cpp @@ -34,6 +34,7 @@ #include "core/math/math_defs.h" #include "core/os/os.h" +#include "servers/rendering/renderer_rd/renderer_compositor_rd.h" #include "thirdparty/misc/cubemap_coeffs.h" static _FORCE_INLINE_ void store_transform_3x3(const Basis &p_basis, float *p_array) { @@ -732,6 +733,11 @@ void EffectsRD::tonemapper(RID p_source_color, RID p_dst_framebuffer, const Tone tonemap.push_constant.pixel_size[0] = 1.0 / p_settings.texture_size.x; tonemap.push_constant.pixel_size[1] = 1.0 / p_settings.texture_size.y; + if (p_settings.view_count > 1) { + // Use MULTIVIEW versions + mode += 4; + } + RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_dst_framebuffer, RD::INITIAL_ACTION_DROP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_DROP, RD::FINAL_ACTION_DISCARD); RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, tonemap.pipelines[mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(p_dst_framebuffer))); RD::get_singleton()->draw_list_bind_uniform_set(draw_list, _get_uniform_set_from_texture(p_source_color), 0); @@ -934,10 +940,10 @@ void EffectsRD::bokeh_dof(RID p_base_texture, RID p_depth_texture, const Size2i RD::get_singleton()->compute_list_end(); } -void EffectsRD::gather_ssao(RD::ComputeListID p_compute_list, const Vector<RID> p_ao_slices, const SSAOSettings &p_settings, bool p_adaptive_base_pass) { - RD::get_singleton()->compute_list_bind_uniform_set(p_compute_list, ssao.gather_uniform_set, 0); +void EffectsRD::gather_ssao(RD::ComputeListID p_compute_list, const Vector<RID> p_ao_slices, const SSAOSettings &p_settings, bool p_adaptive_base_pass, RID p_gather_uniform_set, RID p_importance_map_uniform_set) { + RD::get_singleton()->compute_list_bind_uniform_set(p_compute_list, p_gather_uniform_set, 0); if ((p_settings.quality == RS::ENV_SSAO_QUALITY_ULTRA) && !p_adaptive_base_pass) { - RD::get_singleton()->compute_list_bind_uniform_set(p_compute_list, ssao.importance_map_uniform_set, 1); + RD::get_singleton()->compute_list_bind_uniform_set(p_compute_list, p_importance_map_uniform_set, 1); } for (int i = 0; i < 4; i++) { @@ -960,7 +966,7 @@ void EffectsRD::gather_ssao(RD::ComputeListID p_compute_list, const Vector<RID> RD::get_singleton()->compute_list_add_barrier(p_compute_list); } -void EffectsRD::generate_ssao(RID p_depth_buffer, RID p_normal_buffer, RID p_depth_mipmaps_texture, const Vector<RID> &p_depth_mipmaps, RID p_ao, const Vector<RID> p_ao_slices, RID p_ao_pong, const Vector<RID> p_ao_pong_slices, RID p_upscale_buffer, RID p_importance_map, RID p_importance_map_pong, const CameraMatrix &p_projection, const SSAOSettings &p_settings, bool p_invalidate_uniform_sets) { +void EffectsRD::generate_ssao(RID p_depth_buffer, RID p_normal_buffer, RID p_depth_mipmaps_texture, const Vector<RID> &p_depth_mipmaps, RID p_ao, const Vector<RID> p_ao_slices, RID p_ao_pong, const Vector<RID> p_ao_pong_slices, RID p_upscale_buffer, RID p_importance_map, RID p_importance_map_pong, const CameraMatrix &p_projection, const SSAOSettings &p_settings, bool p_invalidate_uniform_sets, RID &r_downsample_uniform_set, RID &r_gather_uniform_set, RID &r_importance_map_uniform_set) { RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin(); RD::get_singleton()->draw_command_begin_label("SSAO"); /* FIRST PASS */ @@ -990,7 +996,7 @@ void EffectsRD::generate_ssao(RID p_depth_buffer, RID p_normal_buffer, RID p_dep u.ids.push_back(p_depth_mipmaps[3]); uniforms.push_back(u); } - ssao.downsample_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, ssao.downsample_shader.version_get_shader(ssao.downsample_shader_version, 2), 2); + r_downsample_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, ssao.downsample_shader.version_get_shader(ssao.downsample_shader_version, 2), 2); } float depth_linearize_mul = -p_projection.matrix[3][2]; @@ -1025,7 +1031,7 @@ void EffectsRD::generate_ssao(RID p_depth_buffer, RID p_normal_buffer, RID p_dep RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_compute_uniform_set_from_texture(p_depth_buffer), 0); RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_uniform_set_from_image(p_depth_mipmaps[0]), 1); if (p_settings.quality > RS::ENV_SSAO_QUALITY_MEDIUM) { - RD::get_singleton()->compute_list_bind_uniform_set(compute_list, ssao.downsample_uniform_set, 2); + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, r_downsample_uniform_set, 2); } RD::get_singleton()->compute_list_set_push_constant(compute_list, &ssao.downsample_push_constant, sizeof(SSAODownsamplePushConstant)); @@ -1109,7 +1115,7 @@ void EffectsRD::generate_ssao(RID p_depth_buffer, RID p_normal_buffer, RID p_dep u.ids.push_back(ssao.gather_constants_buffer); uniforms.push_back(u); } - ssao.gather_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, ssao.gather_shader.version_get_shader(ssao.gather_shader_version, 0), 0); + r_gather_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, ssao.gather_shader.version_get_shader(ssao.gather_shader_version, 0), 0); } if (p_invalidate_uniform_sets) { @@ -1136,7 +1142,7 @@ void EffectsRD::generate_ssao(RID p_depth_buffer, RID p_normal_buffer, RID p_dep u.ids.push_back(ssao.importance_map_load_counter); uniforms.push_back(u); } - ssao.importance_map_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, ssao.gather_shader.version_get_shader(ssao.gather_shader_version, 2), 1); + r_importance_map_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, ssao.gather_shader.version_get_shader(ssao.gather_shader_version, 2), 1); } if (p_settings.quality == RS::ENV_SSAO_QUALITY_ULTRA) { @@ -1147,7 +1153,7 @@ void EffectsRD::generate_ssao(RID p_depth_buffer, RID p_normal_buffer, RID p_dep ssao.importance_map_push_constant.power = p_settings.power; //base pass RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, ssao.pipelines[SSAO_GATHER_BASE]); - gather_ssao(compute_list, p_ao_pong_slices, p_settings, true); + gather_ssao(compute_list, p_ao_pong_slices, p_settings, true, r_gather_uniform_set, RID()); //generate importance map RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, ssao.pipelines[SSAO_GENERATE_IMPORTANCE_MAP]); @@ -1178,7 +1184,7 @@ void EffectsRD::generate_ssao(RID p_depth_buffer, RID p_normal_buffer, RID p_dep RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, ssao.pipelines[SSAO_GATHER]); } - gather_ssao(compute_list, p_ao_slices, p_settings, false); + gather_ssao(compute_list, p_ao_slices, p_settings, false, r_gather_uniform_set, r_importance_map_uniform_set); RD::get_singleton()->draw_command_end_label(); // Gather SSAO } @@ -1365,15 +1371,18 @@ void EffectsRD::cubemap_filter(RID p_source_cubemap, Vector<RID> p_dest_cubemap, RD::get_singleton()->compute_list_end(); } -void EffectsRD::render_sky(RD::DrawListID p_list, float p_time, RID p_fb, RID p_samplers, RID p_fog, PipelineCacheRD *p_pipeline, RID p_uniform_set, RID p_texture_set, const CameraMatrix &p_camera, const Basis &p_orientation, float p_multiplier, const Vector3 &p_position) { +void EffectsRD::render_sky(RD::DrawListID p_list, float p_time, RID p_fb, RID p_samplers, RID p_fog, PipelineCacheRD *p_pipeline, RID p_uniform_set, RID p_texture_set, uint32_t p_view_count, const CameraMatrix *p_projections, const Basis &p_orientation, float p_multiplier, const Vector3 &p_position) { SkyPushConstant sky_push_constant; memset(&sky_push_constant, 0, sizeof(SkyPushConstant)); - sky_push_constant.proj[0] = p_camera.matrix[2][0]; - sky_push_constant.proj[1] = p_camera.matrix[0][0]; - sky_push_constant.proj[2] = p_camera.matrix[2][1]; - sky_push_constant.proj[3] = p_camera.matrix[1][1]; + for (uint32_t v = 0; v < p_view_count; v++) { + // We only need key components of our projection matrix + sky_push_constant.projections[v][0] = p_projections[v].matrix[2][0]; + sky_push_constant.projections[v][1] = p_projections[v].matrix[0][0]; + sky_push_constant.projections[v][2] = p_projections[v].matrix[2][1]; + sky_push_constant.projections[v][3] = p_projections[v].matrix[1][1]; + } sky_push_constant.position[0] = p_position.x; sky_push_constant.position[1] = p_position.y; sky_push_constant.position[2] = p_position.z; @@ -1553,12 +1562,29 @@ EffectsRD::EffectsRD() { tonemap_modes.push_back("\n#define USE_1D_LUT\n"); tonemap_modes.push_back("\n#define USE_GLOW_FILTER_BICUBIC\n#define USE_1D_LUT\n"); + // multiview versions of our shaders + tonemap_modes.push_back("\n#define MULTIVIEW\n"); + tonemap_modes.push_back("\n#define MULTIVIEW\n#define USE_GLOW_FILTER_BICUBIC\n"); + tonemap_modes.push_back("\n#define MULTIVIEW\n#define USE_1D_LUT\n"); + tonemap_modes.push_back("\n#define MULTIVIEW\n#define USE_GLOW_FILTER_BICUBIC\n#define USE_1D_LUT\n"); + tonemap.shader.initialize(tonemap_modes); + if (!RendererCompositorRD::singleton->is_xr_enabled()) { + tonemap.shader.set_variant_enabled(TONEMAP_MODE_NORMAL_MULTIVIEW, false); + tonemap.shader.set_variant_enabled(TONEMAP_MODE_BICUBIC_GLOW_FILTER_MULTIVIEW, false); + tonemap.shader.set_variant_enabled(TONEMAP_MODE_1D_LUT_MULTIVIEW, false); + tonemap.shader.set_variant_enabled(TONEMAP_MODE_BICUBIC_GLOW_FILTER_1D_LUT_MULTIVIEW, false); + } + tonemap.shader_version = tonemap.shader.version_create(); for (int i = 0; i < TONEMAP_MODE_MAX; i++) { - tonemap.pipelines[i].setup(tonemap.shader.version_get_shader(tonemap.shader_version, i), RD::RENDER_PRIMITIVE_TRIANGLES, RD::PipelineRasterizationState(), RD::PipelineMultisampleState(), RD::PipelineDepthStencilState(), RD::PipelineColorBlendState::create_disabled(), 0); + if (tonemap.shader.is_variant_enabled(i)) { + tonemap.pipelines[i].setup(tonemap.shader.version_get_shader(tonemap.shader_version, i), RD::RENDER_PRIMITIVE_TRIANGLES, RD::PipelineRasterizationState(), RD::PipelineMultisampleState(), RD::PipelineDepthStencilState(), RD::PipelineColorBlendState::create_disabled(), 0); + } else { + tonemap.pipelines[i].clear(); + } } } diff --git a/servers/rendering/renderer_rd/effects_rd.h b/servers/rendering/renderer_rd/effects_rd.h index ab0100f8f9..8b31ffbbd0 100644 --- a/servers/rendering/renderer_rd/effects_rd.h +++ b/servers/rendering/renderer_rd/effects_rd.h @@ -55,6 +55,7 @@ #include "servers/rendering/renderer_rd/shaders/ssao_interleave.glsl.gen.h" #include "servers/rendering/renderer_rd/shaders/subsurface_scattering.glsl.gen.h" #include "servers/rendering/renderer_rd/shaders/tonemap.glsl.gen.h" +#include "servers/rendering/renderer_scene_render.h" #include "servers/rendering_server.h" @@ -170,6 +171,12 @@ class EffectsRD { TONEMAP_MODE_BICUBIC_GLOW_FILTER, TONEMAP_MODE_1D_LUT, TONEMAP_MODE_BICUBIC_GLOW_FILTER_1D_LUT, + + TONEMAP_MODE_NORMAL_MULTIVIEW, + TONEMAP_MODE_BICUBIC_GLOW_FILTER_MULTIVIEW, + TONEMAP_MODE_1D_LUT_MULTIVIEW, + TONEMAP_MODE_BICUBIC_GLOW_FILTER_1D_LUT_MULTIVIEW, + TONEMAP_MODE_MAX }; @@ -378,12 +385,10 @@ class EffectsRD { SSAODownsamplePushConstant downsample_push_constant; SsaoDownsampleShaderRD downsample_shader; RID downsample_shader_version; - RID downsample_uniform_set; SSAOGatherPushConstant gather_push_constant; SsaoShaderRD gather_shader; RID gather_shader_version; - RID gather_uniform_set; RID gather_constants_buffer; bool gather_initialized = false; @@ -391,7 +396,6 @@ class EffectsRD { SsaoImportanceMapShaderRD importance_map_shader; RID importance_map_shader_version; RID importance_map_load_counter; - RID importance_map_uniform_set; RID counter_uniform_set; SSAOBlurPushConstant blur_push_constant; @@ -453,12 +457,12 @@ class EffectsRD { } filter; struct SkyPushConstant { - float orientation[12]; - float proj[4]; - float position[3]; - float multiplier; - float time; - float pad[3]; + float orientation[12]; // 48 - 48 + float projections[RendererSceneRender::MAX_RENDER_VIEWS][4]; // 2 x 16 - 64, if we ever need more then 3 we should consider adding this to a set. + float position[3]; // 12 - 92 + float multiplier; // 4 - 96 + float time; // 4 - 100 + float pad[3]; // 12 - 112 }; enum SpecularMergeMode { @@ -714,6 +718,7 @@ public: bool use_fxaa = false; bool use_debanding = false; Vector2i texture_size; + uint32_t view_count = 1; }; struct SSAOSettings { @@ -738,13 +743,13 @@ public: void tonemapper(RID p_source_color, RID p_dst_framebuffer, const TonemapSettings &p_settings); - void gather_ssao(RD::ComputeListID p_compute_list, const Vector<RID> p_ao_slices, const SSAOSettings &p_settings, bool p_adaptive_base_pass); - void generate_ssao(RID p_depth_buffer, RID p_normal_buffer, RID p_depth_mipmaps_texture, const Vector<RID> &depth_mipmaps, RID p_ao, const Vector<RID> p_ao_slices, RID p_ao_pong, const Vector<RID> p_ao_pong_slices, RID p_upscale_buffer, RID p_importance_map, RID p_importance_map_pong, const CameraMatrix &p_projection, const SSAOSettings &p_settings, bool p_invalidate_uniform_sets); + void gather_ssao(RD::ComputeListID p_compute_list, const Vector<RID> p_ao_slices, const SSAOSettings &p_settings, bool p_adaptive_base_pass, RID p_gather_uniform_set, RID p_importance_map_uniform_set); + void generate_ssao(RID p_depth_buffer, RID p_normal_buffer, RID p_depth_mipmaps_texture, const Vector<RID> &depth_mipmaps, RID p_ao, const Vector<RID> p_ao_slices, RID p_ao_pong, const Vector<RID> p_ao_pong_slices, RID p_upscale_buffer, RID p_importance_map, RID p_importance_map_pong, const CameraMatrix &p_projection, const SSAOSettings &p_settings, bool p_invalidate_uniform_sets, RID &r_downsample_uniform_set, RID &r_gather_uniform_set, RID &r_importance_map_uniform_set); void roughness_limit(RID p_source_normal, RID p_roughness, const Size2i &p_size, float p_curve); void cubemap_downsample(RID p_source_cubemap, RID p_dest_cubemap, const Size2i &p_size); void cubemap_filter(RID p_source_cubemap, Vector<RID> p_dest_cubemap, bool p_use_array); - void render_sky(RD::DrawListID p_list, float p_time, RID p_fb, RID p_samplers, RID p_fog, PipelineCacheRD *p_pipeline, RID p_uniform_set, RID p_texture_set, const CameraMatrix &p_camera, const Basis &p_orientation, float p_multiplier, const Vector3 &p_position); + void render_sky(RD::DrawListID p_list, float p_time, RID p_fb, RID p_samplers, RID p_fog, PipelineCacheRD *p_pipeline, RID p_uniform_set, RID p_texture_set, uint32_t p_view_count, const CameraMatrix *p_projections, const Basis &p_orientation, float p_multiplier, const Vector3 &p_position); void screen_space_reflection(RID p_diffuse, RID p_normal_roughness, RS::EnvironmentSSRRoughnessQuality p_roughness_quality, RID p_blur_radius, RID p_blur_radius2, RID p_metallic, const Color &p_metallic_mask, RID p_depth, RID p_scale_depth, RID p_scale_normal, RID p_output, RID p_output_blur, const Size2i &p_screen_size, int p_max_steps, float p_fade_in, float p_fade_out, float p_tolerance, const CameraMatrix &p_camera); void merge_specular(RID p_dest_framebuffer, RID p_specular, RID p_base, RID p_reflection); diff --git a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp index e1c2836f6c..1653453c5c 100644 --- a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp +++ b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp @@ -183,9 +183,11 @@ void RenderForwardClustered::RenderBufferDataForwardClustered::clear() { } } -void RenderForwardClustered::RenderBufferDataForwardClustered::configure(RID p_color_buffer, RID p_depth_buffer, int p_width, int p_height, RS::ViewportMSAA p_msaa) { +void RenderForwardClustered::RenderBufferDataForwardClustered::configure(RID p_color_buffer, RID p_depth_buffer, int p_width, int p_height, RS::ViewportMSAA p_msaa, uint32_t p_view_count) { clear(); + ERR_FAIL_COND_MSG(p_view_count != 1, "Multiple views is currently not supported in this renderer, please use the mobile renderer for VR support"); + msaa = p_msaa; width = p_width; @@ -1091,6 +1093,8 @@ void RenderForwardClustered::_setup_lightmaps(const PagedArray<RID> &p_lightmaps } void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Color &p_default_bg_color) { + ERR_FAIL_COND_MSG(p_render_data->view_count != 1, "Multiview is currently not supported in the clustered renderer. Please use the mobile renderer for VR."); + RenderBufferDataForwardClustered *render_buffer = nullptr; if (p_render_data->render_buffers.is_valid()) { render_buffer = (RenderBufferDataForwardClustered *)render_buffers_get_data(p_render_data->render_buffers); @@ -1431,7 +1435,7 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co projection = correction * p_render_data->cam_projection; } RD::get_singleton()->draw_command_begin_label("Draw Sky"); - sky.draw(env, can_continue_color, can_continue_depth, opaque_framebuffer, projection, p_render_data->cam_transform, time); + sky.draw(env, can_continue_color, can_continue_depth, opaque_framebuffer, 1, &projection, p_render_data->cam_transform, time); RD::get_singleton()->draw_command_end_label(); } diff --git a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h index e276e55de2..579c8de05e 100644 --- a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h +++ b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h @@ -103,7 +103,7 @@ class RenderForwardClustered : public RendererSceneRenderRD { void ensure_specular(); void ensure_voxelgi(); void clear(); - virtual void configure(RID p_color_buffer, RID p_depth_buffer, int p_width, int p_height, RS::ViewportMSAA p_msaa); + virtual void configure(RID p_color_buffer, RID p_depth_buffer, int p_width, int p_height, RS::ViewportMSAA p_msaa, uint32_t p_view_count); ~RenderBufferDataForwardClustered(); }; @@ -204,7 +204,6 @@ class RenderForwardClustered : public RendererSceneRenderRD { struct UBO { float projection_matrix[16]; float inv_projection_matrix[16]; - float camera_matrix[16]; float inv_camera_matrix[16]; diff --git a/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp b/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp index dcfdf14784..f125931df8 100644 --- a/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp +++ b/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp @@ -560,17 +560,18 @@ void SceneShaderForwardClustered::init(RendererStorageRD *p_storage, const Strin { 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"); - shader_versions.push_back("\n#define MODE_RENDER_DEPTH\n#define MODE_RENDER_NORMAL_ROUGHNESS\n"); - shader_versions.push_back("\n#define MODE_RENDER_DEPTH\n#define MODE_RENDER_NORMAL_ROUGHNESS\n#define MODE_RENDER_VOXEL_GI\n"); - shader_versions.push_back("\n#define MODE_RENDER_DEPTH\n#define MODE_RENDER_MATERIAL\n"); - shader_versions.push_back("\n#define MODE_RENDER_DEPTH\n#define MODE_RENDER_SDF\n"); - shader_versions.push_back(""); - shader_versions.push_back("\n#define USE_FORWARD_GI\n"); - shader_versions.push_back("\n#define MODE_MULTIPLE_RENDER_TARGETS\n"); - shader_versions.push_back("\n#define USE_LIGHTMAP\n"); - shader_versions.push_back("\n#define MODE_MULTIPLE_RENDER_TARGETS\n#define USE_LIGHTMAP\n"); + shader_versions.push_back("\n#define MODE_RENDER_DEPTH\n"); // SHADER_VERSION_DEPTH_PASS + shader_versions.push_back("\n#define MODE_RENDER_DEPTH\n#define MODE_DUAL_PARABOLOID\n"); // SHADER_VERSION_DEPTH_PASS_DP + shader_versions.push_back("\n#define MODE_RENDER_DEPTH\n#define MODE_RENDER_NORMAL_ROUGHNESS\n"); // SHADER_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS + shader_versions.push_back("\n#define MODE_RENDER_DEPTH\n#define MODE_RENDER_NORMAL_ROUGHNESS\n#define MODE_RENDER_VOXEL_GI\n"); // SHADER_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS_AND_GIPROBE + shader_versions.push_back("\n#define MODE_RENDER_DEPTH\n#define MODE_RENDER_MATERIAL\n"); // SHADER_VERSION_DEPTH_PASS_WITH_MATERIAL + shader_versions.push_back("\n#define MODE_RENDER_DEPTH\n#define MODE_RENDER_SDF\n"); // SHADER_VERSION_DEPTH_PASS_WITH_SDF + shader_versions.push_back(""); // SHADER_VERSION_COLOR_PASS + shader_versions.push_back("\n#define USE_FORWARD_GI\n"); // SHADER_VERSION_COLOR_PASS_WITH_FORWARD_GI + shader_versions.push_back("\n#define MODE_MULTIPLE_RENDER_TARGETS\n"); // SHADER_VERSION_COLOR_PASS_WITH_SEPARATE_SPECULAR + shader_versions.push_back("\n#define USE_LIGHTMAP\n"); // SHADER_VERSION_LIGHTMAP_COLOR_PASS + shader_versions.push_back("\n#define MODE_MULTIPLE_RENDER_TARGETS\n#define USE_LIGHTMAP\n"); // SHADER_VERSION_LIGHTMAP_COLOR_PASS_WITH_SEPARATE_SPECULAR + shader.initialize(shader_versions, p_defines); } @@ -656,6 +657,11 @@ void SceneShaderForwardClustered::init(RendererStorageRD *p_storage, const Strin actions.renames["CUSTOM2"] = "custom2_attrib"; actions.renames["CUSTOM3"] = "custom3_attrib"; + // not implemented but need these just in case code is in the shaders + actions.renames["VIEW_INDEX"] = "0"; + actions.renames["VIEW_MONO_LEFT"] = "0"; + actions.renames["VIEW_RIGHT"] = "1"; + //for light actions.renames["VIEW"] = "view"; actions.renames["LIGHT_COLOR"] = "light_color"; diff --git a/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.h b/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.h index 6845133825..8add9f8095 100644 --- a/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.h +++ b/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.h @@ -56,6 +56,7 @@ public: SHADER_VERSION_COLOR_PASS_WITH_SEPARATE_SPECULAR, SHADER_VERSION_LIGHTMAP_COLOR_PASS, SHADER_VERSION_LIGHTMAP_COLOR_PASS_WITH_SEPARATE_SPECULAR, + SHADER_VERSION_MAX }; diff --git a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp index 370d8eca4e..ae09d215ff 100644 --- a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp +++ b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp @@ -53,13 +53,14 @@ void RenderForwardMobile::RenderBufferDataForwardMobile::clear() { color_fb = RID(); } -void RenderForwardMobile::RenderBufferDataForwardMobile::configure(RID p_color_buffer, RID p_depth_buffer, int p_width, int p_height, RS::ViewportMSAA p_msaa) { +void RenderForwardMobile::RenderBufferDataForwardMobile::configure(RID p_color_buffer, RID p_depth_buffer, int p_width, int p_height, RS::ViewportMSAA p_msaa, uint32_t p_view_count) { clear(); msaa = p_msaa; width = p_width; height = p_height; + view_count = p_view_count; color = p_color_buffer; depth = p_depth_buffer; @@ -71,13 +72,18 @@ void RenderForwardMobile::RenderBufferDataForwardMobile::configure(RID p_color_b fb.push_back(p_color_buffer); fb.push_back(depth); - color_fb = RD::get_singleton()->framebuffer_create(fb); + color_fb = RD::get_singleton()->framebuffer_create(fb, RenderingDevice::INVALID_ID, view_count); } else { RD::TextureFormat tf; + if (view_count > 1) { + tf.texture_type = RD::TEXTURE_TYPE_2D_ARRAY; + } else { + tf.texture_type = RD::TEXTURE_TYPE_2D; + } tf.format = RD::DATA_FORMAT_R16G16B16A16_SFLOAT; tf.width = p_width; tf.height = p_height; - tf.texture_type = RD::TEXTURE_TYPE_2D; + tf.array_layers = view_count; // create a layer for every view tf.usage_bits = RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | RD::TEXTURE_USAGE_CAN_COPY_FROM_BIT | RD::TEXTURE_USAGE_SAMPLING_BIT; RD::TextureSamples ts[RS::VIEWPORT_MSAA_MAX] = { @@ -103,7 +109,7 @@ void RenderForwardMobile::RenderBufferDataForwardMobile::configure(RID p_color_b fb.push_back(color_msaa); fb.push_back(depth_msaa); - color_fb = RD::get_singleton()->framebuffer_create(fb); + color_fb = RD::get_singleton()->framebuffer_create(fb, RenderingDevice::INVALID_ID, view_count); } } } @@ -479,7 +485,7 @@ void RenderForwardMobile::_render_scene(RenderDataRD *p_render_data, const Color Vector<Color> c; c.push_back(clear_color.to_linear()); - RenderListParameters render_list_params(render_list[RENDER_LIST_OPAQUE].elements.ptr(), render_list[RENDER_LIST_OPAQUE].element_info.ptr(), render_list[RENDER_LIST_OPAQUE].elements.size(), reverse_cull, PASS_MODE_COLOR, rp_uniform_set, get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_WIREFRAME, Vector2(), p_render_data->lod_camera_plane, p_render_data->lod_distance_multiplier, p_render_data->screen_lod_threshold); + RenderListParameters render_list_params(render_list[RENDER_LIST_OPAQUE].elements.ptr(), render_list[RENDER_LIST_OPAQUE].element_info.ptr(), render_list[RENDER_LIST_OPAQUE].elements.size(), reverse_cull, PASS_MODE_COLOR, rp_uniform_set, get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_WIREFRAME, Vector2(), p_render_data->lod_camera_plane, p_render_data->lod_distance_multiplier, p_render_data->screen_lod_threshold, p_render_data->view_count); _render_list_with_threads(&render_list_params, opaque_framebuffer, keep_color ? RD::INITIAL_ACTION_KEEP : RD::INITIAL_ACTION_CLEAR, will_continue_color ? RD::FINAL_ACTION_CONTINUE : RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_CLEAR, will_continue_depth ? RD::FINAL_ACTION_CONTINUE : RD::FINAL_ACTION_READ, c, 1.0, 0); } @@ -488,14 +494,16 @@ void RenderForwardMobile::_render_scene(RenderDataRD *p_render_data, const Color if (draw_sky || draw_sky_fog_only) { RENDER_TIMESTAMP("Render Sky"); - CameraMatrix projection = p_render_data->cam_projection; + RD::get_singleton()->draw_command_begin_label("Draw Sky"); + if (p_render_data->reflection_probe.is_valid()) { CameraMatrix correction; correction.set_depth_correction(true); - projection = correction * p_render_data->cam_projection; + CameraMatrix projection = correction * p_render_data->cam_projection; + sky.draw(env, can_continue_color, can_continue_depth, opaque_framebuffer, 1, &projection, p_render_data->cam_transform, time); + } else { + sky.draw(env, can_continue_color, can_continue_depth, opaque_framebuffer, p_render_data->view_count, p_render_data->view_projection, p_render_data->cam_transform, time); } - RD::get_singleton()->draw_command_begin_label("Draw Sky"); - sky.draw(env, can_continue_color, can_continue_depth, opaque_framebuffer, projection, p_render_data->cam_transform, time); RD::get_singleton()->draw_command_end_label(); } @@ -522,7 +530,7 @@ void RenderForwardMobile::_render_scene(RenderDataRD *p_render_data, const Color _setup_environment(p_render_data, p_render_data->reflection_probe.is_valid(), screen_size, !p_render_data->reflection_probe.is_valid(), p_default_bg_color, false); { - RenderListParameters render_list_params(render_list[RENDER_LIST_ALPHA].elements.ptr(), render_list[RENDER_LIST_ALPHA].element_info.ptr(), render_list[RENDER_LIST_ALPHA].elements.size(), reverse_cull, PASS_MODE_COLOR, rp_uniform_set, get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_WIREFRAME, Vector2(), p_render_data->lod_camera_plane, p_render_data->lod_distance_multiplier, p_render_data->screen_lod_threshold); + RenderListParameters render_list_params(render_list[RENDER_LIST_ALPHA].elements.ptr(), render_list[RENDER_LIST_ALPHA].element_info.ptr(), render_list[RENDER_LIST_ALPHA].elements.size(), reverse_cull, PASS_MODE_COLOR, rp_uniform_set, get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_WIREFRAME, Vector2(), p_render_data->lod_camera_plane, p_render_data->lod_distance_multiplier, p_render_data->screen_lod_threshold, p_render_data->view_count); _render_list_with_threads(&render_list_params, alpha_framebuffer, can_continue_color ? RD::INITIAL_ACTION_CONTINUE : RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, can_continue_depth ? RD::INITIAL_ACTION_CONTINUE : RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ); } @@ -555,6 +563,7 @@ void RenderForwardMobile::_render_shadow_append(RID p_framebuffer, const PagedAr RenderDataRD render_data; render_data.cam_projection = p_projection; render_data.cam_transform = p_transform; + render_data.view_projection[0] = p_projection; render_data.z_near = 0.0; render_data.z_far = p_zfar; render_data.instances = &p_instances; @@ -622,7 +631,7 @@ void RenderForwardMobile::_render_shadow_end(uint32_t p_barrier) { for (uint32_t i = 0; i < scene_state.shadow_passes.size(); i++) { SceneState::ShadowPass &shadow_pass = scene_state.shadow_passes[i]; - RenderListParameters render_list_parameters(render_list[RENDER_LIST_SECONDARY].elements.ptr() + shadow_pass.element_from, render_list[RENDER_LIST_SECONDARY].element_info.ptr() + shadow_pass.element_from, shadow_pass.element_count, shadow_pass.flip_cull, shadow_pass.pass_mode, shadow_pass.rp_uniform_set, false, Vector2(), shadow_pass.camera_plane, shadow_pass.lod_distance_multiplier, shadow_pass.screen_lod_threshold, shadow_pass.element_from, RD::BARRIER_MASK_NO_BARRIER); + RenderListParameters render_list_parameters(render_list[RENDER_LIST_SECONDARY].elements.ptr() + shadow_pass.element_from, render_list[RENDER_LIST_SECONDARY].element_info.ptr() + shadow_pass.element_from, shadow_pass.element_count, shadow_pass.flip_cull, shadow_pass.pass_mode, shadow_pass.rp_uniform_set, false, Vector2(), shadow_pass.camera_plane, shadow_pass.lod_distance_multiplier, shadow_pass.screen_lod_threshold, 1, shadow_pass.element_from, RD::BARRIER_MASK_NO_BARRIER); _render_list_with_threads(&render_list_parameters, shadow_pass.framebuffer, RD::INITIAL_ACTION_DROP, RD::FINAL_ACTION_DISCARD, shadow_pass.initial_depth_action, shadow_pass.final_depth_action, Vector<Color>(), 1.0, 0, shadow_pass.rect); } @@ -647,6 +656,7 @@ void RenderForwardMobile::_render_material(const Transform3D &p_cam_transform, c RenderDataRD render_data; render_data.cam_projection = p_cam_projection; render_data.cam_transform = p_cam_transform; + render_data.view_projection[0] = p_cam_projection; render_data.instances = &p_instances; _setup_environment(&render_data, true, Vector2(1, 1), false, Color()); @@ -757,6 +767,7 @@ void RenderForwardMobile::_render_particle_collider_heightfield(RID p_fb, const RenderDataRD render_data; render_data.cam_projection = p_cam_projection; render_data.cam_transform = p_cam_transform; + render_data.view_projection[0] = p_cam_projection; render_data.z_near = 0.0; render_data.z_far = p_cam_projection.get_z_far(); render_data.instances = &p_instances; @@ -1089,6 +1100,12 @@ void RenderForwardMobile::_setup_environment(const RenderDataRD *p_render_data, RendererStorageRD::store_transform(p_render_data->cam_transform, scene_state.ubo.camera_matrix); RendererStorageRD::store_transform(p_render_data->cam_transform.affine_inverse(), scene_state.ubo.inv_camera_matrix); + for (uint32_t v = 0; v < p_render_data->view_count; v++) { + projection = correction * p_render_data->view_projection[v]; + RendererStorageRD::store_camera(projection, scene_state.ubo.projection_matrix_view[v]); + RendererStorageRD::store_camera(projection.inverse(), scene_state.ubo.inv_projection_matrix_view[v]); + } + scene_state.ubo.z_far = p_render_data->z_far; scene_state.ubo.z_near = p_render_data->z_near; @@ -1426,18 +1443,20 @@ void RenderForwardMobile::_render_list_template(RenderingDevice::DrawListID p_dr case PASS_MODE_COLOR: case PASS_MODE_COLOR_TRANSPARENT: { if (element_info.uses_lightmap) { - shader_version = SceneShaderForwardMobile::SHADER_VERSION_LIGHTMAP_COLOR_PASS; + shader_version = p_params->view_count > 1 ? SceneShaderForwardMobile::SHADER_VERSION_LIGHTMAP_COLOR_PASS_MULTIVIEW : SceneShaderForwardMobile::SHADER_VERSION_LIGHTMAP_COLOR_PASS; } else { - shader_version = SceneShaderForwardMobile::SHADER_VERSION_COLOR_PASS; + shader_version = p_params->view_count > 1 ? SceneShaderForwardMobile::SHADER_VERSION_COLOR_PASS_MULTIVIEW : SceneShaderForwardMobile::SHADER_VERSION_COLOR_PASS; } } break; case PASS_MODE_SHADOW: { - shader_version = SceneShaderForwardMobile::SHADER_VERSION_SHADOW_PASS; + shader_version = p_params->view_count > 1 ? SceneShaderForwardMobile::SHADER_VERSION_SHADOW_PASS_MULTIVIEW : SceneShaderForwardMobile::SHADER_VERSION_SHADOW_PASS; } break; case PASS_MODE_SHADOW_DP: { - shader_version = SceneShaderForwardMobile::SHADER_VERSION_DEPTH_PASS_DP; + ERR_FAIL_COND_MSG(p_params->view_count > 1, "Multiview not supported for shadow DP pass"); + shader_version = SceneShaderForwardMobile::SHADER_VERSION_SHADOW_PASS_DP; } break; case PASS_MODE_DEPTH_MATERIAL: { + ERR_FAIL_COND_MSG(p_params->view_count > 1, "Multiview not supported for material pass"); shader_version = SceneShaderForwardMobile::SHADER_VERSION_DEPTH_PASS_WITH_MATERIAL; } break; } diff --git a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.h b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.h index a2189693d0..99cbd45b10 100644 --- a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.h +++ b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.h @@ -85,9 +85,10 @@ protected: RID color_fb; int width, height; + uint32_t view_count; void clear(); - virtual void configure(RID p_color_buffer, RID p_depth_buffer, int p_width, int p_height, RS::ViewportMSAA p_msaa); + virtual void configure(RID p_color_buffer, RID p_depth_buffer, int p_width, int p_height, RS::ViewportMSAA p_msaa, uint32_t p_view_count); ~RenderBufferDataForwardMobile(); }; @@ -120,6 +121,7 @@ protected: bool reverse_cull = false; PassMode pass_mode = PASS_MODE_COLOR; // bool no_gi = false; + uint32_t view_count = 1; RID render_pass_uniform_set; bool force_wireframe = false; Vector2 uv_offset; @@ -130,13 +132,14 @@ protected: uint32_t element_offset = 0; uint32_t barrier = RD::BARRIER_MASK_ALL; - RenderListParameters(GeometryInstanceSurfaceDataCache **p_elements, RenderElementInfo *p_element_info, int p_element_count, bool p_reverse_cull, PassMode p_pass_mode, RID p_render_pass_uniform_set, bool p_force_wireframe = false, const Vector2 &p_uv_offset = Vector2(), const Plane &p_lod_plane = Plane(), float p_lod_distance_multiplier = 0.0, float p_screen_lod_threshold = 0.0, uint32_t p_element_offset = 0, uint32_t p_barrier = RD::BARRIER_MASK_ALL) { + RenderListParameters(GeometryInstanceSurfaceDataCache **p_elements, RenderElementInfo *p_element_info, int p_element_count, bool p_reverse_cull, PassMode p_pass_mode, RID p_render_pass_uniform_set, bool p_force_wireframe = false, const Vector2 &p_uv_offset = Vector2(), const Plane &p_lod_plane = Plane(), float p_lod_distance_multiplier = 0.0, float p_screen_lod_threshold = 0.0, uint32_t p_view_count = 1, uint32_t p_element_offset = 0, uint32_t p_barrier = RD::BARRIER_MASK_ALL) { elements = p_elements; element_info = p_element_info; element_count = p_element_count; reverse_cull = p_reverse_cull; pass_mode = p_pass_mode; // no_gi = p_no_gi; + view_count = p_view_count; render_pass_uniform_set = p_render_pass_uniform_set; force_wireframe = p_force_wireframe; uv_offset = p_uv_offset; @@ -196,10 +199,12 @@ protected: struct UBO { float projection_matrix[16]; float inv_projection_matrix[16]; - float camera_matrix[16]; float inv_camera_matrix[16]; + float projection_matrix_view[RendererSceneRender::MAX_RENDER_VIEWS][16]; + float inv_projection_matrix_view[RendererSceneRender::MAX_RENDER_VIEWS][16]; + float viewport_size[2]; float screen_pixel_size[2]; diff --git a/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.cpp b/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.cpp index 0d9dfbc51b..b5fb9fbc62 100644 --- a/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.cpp +++ b/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.cpp @@ -32,6 +32,7 @@ #include "core/config/project_settings.h" #include "core/math/math_defs.h" #include "render_forward_mobile.h" +#include "servers/rendering/renderer_rd/renderer_compositor_rd.h" using namespace RendererSceneRenderImplementation; @@ -291,12 +292,12 @@ void SceneShaderForwardMobile::ShaderData::set_code(const String &p_code) { multisample_state.enable_alpha_to_one = true; } - if (k == SHADER_VERSION_COLOR_PASS || k == SHADER_VERSION_LIGHTMAP_COLOR_PASS) { + if (k == SHADER_VERSION_COLOR_PASS || k == SHADER_VERSION_COLOR_PASS_MULTIVIEW || k == SHADER_VERSION_LIGHTMAP_COLOR_PASS || k == SHADER_VERSION_LIGHTMAP_COLOR_PASS_MULTIVIEW) { blend_state = blend_state_blend; if (depth_draw == DEPTH_DRAW_OPAQUE) { depth_stencil.enable_depth_write = false; //alpha does not draw depth } - } else if (k == SHADER_VERSION_SHADOW_PASS || k == SHADER_VERSION_DEPTH_PASS_DP) { + } else if (k == SHADER_VERSION_SHADOW_PASS || k == SHADER_VERSION_SHADOW_PASS_MULTIVIEW || k == SHADER_VERSION_SHADOW_PASS_DP) { //none, blend state contains nothing } else if (k == SHADER_VERSION_DEPTH_PASS_WITH_MATERIAL) { blend_state = RD::PipelineColorBlendState::create_disabled(5); //writes to normal and roughness in opaque way @@ -304,57 +305,16 @@ void SceneShaderForwardMobile::ShaderData::set_code(const String &p_code) { pipelines[i][j][k].clear(); continue; // do not use this version (will error if using it is attempted) } - - /* - if (k == SHADER_VERSION_COLOR_PASS || k == SHADER_VERSION_COLOR_PASS_WITH_FORWARD_GI || k == SHADER_VERSION_LIGHTMAP_COLOR_PASS) { - blend_state = blend_state_blend; - if (depth_draw == DEPTH_DRAW_OPAQUE) { - depth_stencil.enable_depth_write = false; //alpha does not draw depth - } - } else if (uses_depth_pre_pass && (k == SHADER_VERSION_DEPTH_PASS || k == SHADER_VERSION_DEPTH_PASS_DP || k == SHADER_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS || k == SHADER_VERSION_DEPTH_PASS_WITH_MATERIAL)) { - if (k == SHADER_VERSION_DEPTH_PASS || k == SHADER_VERSION_DEPTH_PASS_DP) { - //none, blend state contains nothing - } else if (k == SHADER_VERSION_DEPTH_PASS_WITH_MATERIAL) { - blend_state = RD::PipelineColorBlendState::create_disabled(5); //writes to normal and roughness in opaque way - } else { - blend_state = blend_state_opaque; //writes to normal and roughness in opaque way - } - } else { - pipelines[i][j][k].clear(); - continue; // do not use this version (will error if using it is attempted) - } - */ } else { - if (k == SHADER_VERSION_COLOR_PASS || k == SHADER_VERSION_LIGHTMAP_COLOR_PASS) { + if (k == SHADER_VERSION_COLOR_PASS || k == SHADER_VERSION_COLOR_PASS_MULTIVIEW || k == SHADER_VERSION_LIGHTMAP_COLOR_PASS || k == SHADER_VERSION_LIGHTMAP_COLOR_PASS_MULTIVIEW) { blend_state = blend_state_opaque; - } else if (k == SHADER_VERSION_SHADOW_PASS || k == SHADER_VERSION_DEPTH_PASS_DP) { + } else if (k == SHADER_VERSION_SHADOW_PASS || k == SHADER_VERSION_SHADOW_PASS_MULTIVIEW || k == SHADER_VERSION_SHADOW_PASS_DP) { //none, leave empty } else if (k == SHADER_VERSION_DEPTH_PASS_WITH_MATERIAL) { blend_state = RD::PipelineColorBlendState::create_disabled(5); //writes to normal and roughness in opaque way } else { // ??? } - - /* - if (k == SHADER_VERSION_COLOR_PASS || k == SHADER_VERSION_COLOR_PASS_WITH_FORWARD_GI || k == SHADER_VERSION_LIGHTMAP_COLOR_PASS) { - blend_state = blend_state_opaque; - } else if (k == SHADER_VERSION_DEPTH_PASS || k == SHADER_VERSION_DEPTH_PASS_DP) { - //none, leave empty - } else if (k == SHADER_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS) { - blend_state = blend_state_depth_normal_roughness; - } else if (k == SHADER_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS_AND_VOXEL_GI) { - blend_state = blend_state_depth_normal_roughness_giprobe; - } else if (k == SHADER_VERSION_DEPTH_PASS_WITH_MATERIAL) { - blend_state = RD::PipelineColorBlendState::create_disabled(5); //writes to normal and roughness in opaque way - } else if (k == SHADER_VERSION_DEPTH_PASS_WITH_SDF) { - blend_state = RD::PipelineColorBlendState(); //no color targets for SDF - } else { - //specular write - blend_state = blend_state_opaque_specular; - depth_stencil.enable_depth_test = false; - depth_stencil.enable_depth_write = false; - } - */ } RID shader_variant = shader_singleton->shader.version_get_shader(version, k); @@ -585,10 +545,22 @@ void SceneShaderForwardMobile::init(RendererStorageRD *p_storage, const String p Vector<String> shader_versions; shader_versions.push_back(""); // SHADER_VERSION_COLOR_PASS shader_versions.push_back("\n#define USE_LIGHTMAP\n"); // SHADER_VERSION_LIGHTMAP_COLOR_PASS - shader_versions.push_back("\n#define MODE_RENDER_DEPTH\n"); // !BAS! SHADER_VERSION_SHADOW_PASS, should probably change this to MODE_RENDER_SHADOW because we don't have a depth pass here... - shader_versions.push_back("\n#define MODE_RENDER_DEPTH\n#define MODE_DUAL_PARABOLOID\n"); // SHADER_VERSION_DEPTH_PASS_DP (maybe rename to SHADER_VERSION_SHADOW_PASS_DP?) + shader_versions.push_back("\n#define MODE_RENDER_DEPTH\n"); // SHADER_VERSION_SHADOW_PASS, should probably change this to MODE_RENDER_SHADOW because we don't have a depth pass here... + shader_versions.push_back("\n#define MODE_RENDER_DEPTH\n#define MODE_DUAL_PARABOLOID\n"); // SHADER_VERSION_SHADOW_PASS_DP shader_versions.push_back("\n#define MODE_RENDER_DEPTH\n#define MODE_RENDER_MATERIAL\n"); // SHADER_VERSION_DEPTH_PASS_WITH_MATERIAL + + // multiview versions of our shaders + shader_versions.push_back("\n#define USE_MULTIVIEW\n"); // SHADER_VERSION_COLOR_PASS_MULTIVIEW + shader_versions.push_back("\n#define USE_MULTIVIEW\n#define USE_LIGHTMAP\n"); // SHADER_VERSION_LIGHTMAP_COLOR_PASS_MULTIVIEW + shader_versions.push_back("\n#define USE_MULTIVIEW\n#define MODE_RENDER_DEPTH\n"); // SHADER_VERSION_SHADOW_PASS_MULTIVIEW + shader.initialize(shader_versions, p_defines); + + if (!RendererCompositorRD::singleton->is_xr_enabled()) { + shader.set_variant_enabled(SHADER_VERSION_COLOR_PASS_MULTIVIEW, false); + shader.set_variant_enabled(SHADER_VERSION_LIGHTMAP_COLOR_PASS_MULTIVIEW, false); + shader.set_variant_enabled(SHADER_VERSION_SHADOW_PASS_MULTIVIEW, false); + } } storage->shader_set_data_request_function(RendererStorageRD::SHADER_TYPE_3D, _create_shader_funcs); @@ -603,7 +575,7 @@ void SceneShaderForwardMobile::init(RendererStorageRD *p_storage, const String p actions.renames["INV_CAMERA_MATRIX"] = "scene_data.inv_camera_matrix"; actions.renames["CAMERA_MATRIX"] = "scene_data.camera_matrix"; actions.renames["PROJECTION_MATRIX"] = "projection_matrix"; - actions.renames["INV_PROJECTION_MATRIX"] = "scene_data.inv_projection_matrix"; + actions.renames["INV_PROJECTION_MATRIX"] = "inv_projection_matrix"; actions.renames["MODELVIEW_MATRIX"] = "modelview"; actions.renames["MODELVIEW_NORMAL_MATRIX"] = "modelview_normal"; @@ -673,6 +645,10 @@ void SceneShaderForwardMobile::init(RendererStorageRD *p_storage, const String p actions.renames["CUSTOM2"] = "custom2_attrib"; actions.renames["CUSTOM3"] = "custom3_attrib"; + actions.renames["VIEW_INDEX"] = "ViewIndex"; + actions.renames["VIEW_MONO_LEFT"] = "0"; + actions.renames["VIEW_RIGHT"] = "1"; + //for light actions.renames["VIEW"] = "view"; actions.renames["LIGHT_COLOR"] = "light_color"; diff --git a/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.h b/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.h index 1517197d25..f4f6ceeb1d 100644 --- a/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.h +++ b/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.h @@ -47,8 +47,13 @@ public: SHADER_VERSION_COLOR_PASS, SHADER_VERSION_LIGHTMAP_COLOR_PASS, SHADER_VERSION_SHADOW_PASS, - SHADER_VERSION_DEPTH_PASS_DP, + SHADER_VERSION_SHADOW_PASS_DP, SHADER_VERSION_DEPTH_PASS_WITH_MATERIAL, + + SHADER_VERSION_COLOR_PASS_MULTIVIEW, + SHADER_VERSION_LIGHTMAP_COLOR_PASS_MULTIVIEW, + SHADER_VERSION_SHADOW_PASS_MULTIVIEW, + SHADER_VERSION_MAX }; diff --git a/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp b/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp index 31af108d60..be98fb42c0 100644 --- a/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp +++ b/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp @@ -1728,7 +1728,7 @@ void RendererSceneRenderRD::_process_ssao(RID p_render_buffers, RID p_environmen settings.half_screen_size = Size2i(buffer_width, buffer_height); settings.quarter_screen_size = Size2i(half_width, half_height); - storage->get_effects()->generate_ssao(rb->depth_texture, p_normal_buffer, rb->ssao.depth, rb->ssao.depth_slices, rb->ssao.ao_deinterleaved, rb->ssao.ao_deinterleaved_slices, rb->ssao.ao_pong, rb->ssao.ao_pong_slices, rb->ssao.ao_final, rb->ssao.importance_map[0], rb->ssao.importance_map[1], p_projection, settings, uniform_sets_are_invalid); + storage->get_effects()->generate_ssao(rb->depth_texture, p_normal_buffer, rb->ssao.depth, rb->ssao.depth_slices, rb->ssao.ao_deinterleaved, rb->ssao.ao_deinterleaved_slices, rb->ssao.ao_pong, rb->ssao.ao_pong_slices, rb->ssao.ao_final, rb->ssao.importance_map[0], rb->ssao.importance_map[1], p_projection, settings, uniform_sets_are_invalid, rb->ssao.downsample_uniform_set, rb->ssao.gather_uniform_set, rb->ssao.importance_map_uniform_set); } void RendererSceneRenderRD::_render_buffers_post_process_and_tonemap(const RenderDataRD *p_render_data) { @@ -1857,6 +1857,8 @@ void RendererSceneRenderRD::_render_buffers_post_process_and_tonemap(const Rende } } + tonemap.view_count = p_render_data->view_count; + storage->get_effects()->tonemapper(rb->texture, storage->render_target_get_rd_framebuffer(rb->render_target), tonemap); } @@ -2112,7 +2114,9 @@ float RendererSceneRenderRD::render_buffers_get_volumetric_fog_detail_spread(RID return rb->volumetric_fog->spread; } -void RendererSceneRenderRD::render_buffers_configure(RID p_render_buffers, RID p_render_target, int p_width, int p_height, RS::ViewportMSAA p_msaa, RenderingServer::ViewportScreenSpaceAA p_screen_space_aa, bool p_use_debanding) { +void RendererSceneRenderRD::render_buffers_configure(RID p_render_buffers, RID p_render_target, int p_width, int p_height, RS::ViewportMSAA p_msaa, RenderingServer::ViewportScreenSpaceAA p_screen_space_aa, bool p_use_debanding, uint32_t p_view_count) { + ERR_FAIL_COND_MSG(p_view_count == 0, "Must have atleast 1 view"); + RenderBuffers *rb = render_buffers_owner.getornull(p_render_buffers); rb->width = p_width; rb->height = p_height; @@ -2120,6 +2124,7 @@ void RendererSceneRenderRD::render_buffers_configure(RID p_render_buffers, RID p rb->msaa = p_msaa; rb->screen_space_aa = p_screen_space_aa; rb->use_debanding = p_use_debanding; + rb->view_count = p_view_count; if (is_clustered_enabled()) { if (rb->cluster_builder == nullptr) { @@ -2132,9 +2137,13 @@ void RendererSceneRenderRD::render_buffers_configure(RID p_render_buffers, RID p { RD::TextureFormat tf; + if (rb->view_count > 1) { + tf.texture_type = RD::TEXTURE_TYPE_2D_ARRAY; + } tf.format = RD::DATA_FORMAT_R16G16B16A16_SFLOAT; tf.width = rb->width; tf.height = rb->height; + tf.array_layers = rb->view_count; // create a layer for every view tf.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT; if (rb->msaa != RS::VIEWPORT_MSAA_DISABLED) { tf.usage_bits |= RD::TEXTURE_USAGE_CAN_COPY_TO_BIT | RD::TEXTURE_USAGE_STORAGE_BIT; @@ -2147,6 +2156,9 @@ void RendererSceneRenderRD::render_buffers_configure(RID p_render_buffers, RID p { RD::TextureFormat tf; + if (rb->view_count > 1) { + tf.texture_type = RD::TEXTURE_TYPE_2D_ARRAY; + } if (rb->msaa == RS::VIEWPORT_MSAA_DISABLED) { tf.format = RD::get_singleton()->texture_is_format_supported_for_usage(RD::DATA_FORMAT_D24_UNORM_S8_UINT, RD::TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) ? RD::DATA_FORMAT_D24_UNORM_S8_UINT : RD::DATA_FORMAT_D32_SFLOAT_S8_UINT; } else { @@ -2156,6 +2168,7 @@ void RendererSceneRenderRD::render_buffers_configure(RID p_render_buffers, RID p tf.width = p_width; tf.height = p_height; tf.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT; + tf.array_layers = rb->view_count; // create a layer for every view if (rb->msaa != RS::VIEWPORT_MSAA_DISABLED) { tf.usage_bits |= RD::TEXTURE_USAGE_CAN_COPY_TO_BIT | RD::TEXTURE_USAGE_STORAGE_BIT; @@ -2166,7 +2179,7 @@ void RendererSceneRenderRD::render_buffers_configure(RID p_render_buffers, RID p rb->depth_texture = RD::get_singleton()->texture_create(tf, RD::TextureView()); } - rb->data->configure(rb->texture, rb->depth_texture, p_width, p_height, p_msaa); + rb->data->configure(rb->texture, rb->depth_texture, p_width, p_height, p_msaa, p_view_count); if (is_clustered_enabled()) { rb->cluster_builder->setup(Size2i(p_width, p_height), max_cluster_elements, rb->depth_texture, storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED), rb->texture); @@ -3628,7 +3641,7 @@ void RendererSceneRenderRD::_pre_opaque_render(RenderDataRD *p_render_data, bool } } -void RendererSceneRenderRD::render_scene(RID p_render_buffers, const Transform3D &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_ortogonal, const PagedArray<GeometryInstance *> &p_instances, const PagedArray<RID> &p_lights, const PagedArray<RID> &p_reflection_probes, const PagedArray<RID> &p_voxel_gi_instances, const PagedArray<RID> &p_decals, const PagedArray<RID> &p_lightmaps, RID p_environment, RID p_camera_effects, RID p_shadow_atlas, RID p_occluder_debug_tex, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass, float p_screen_lod_threshold, const RenderShadowData *p_render_shadows, int p_render_shadow_count, const RenderSDFGIData *p_render_sdfgi_regions, int p_render_sdfgi_region_count, const RenderSDFGIUpdateData *p_sdfgi_update_data) { +void RendererSceneRenderRD::render_scene(RID p_render_buffers, const CameraData *p_camera_data, const PagedArray<GeometryInstance *> &p_instances, const PagedArray<RID> &p_lights, const PagedArray<RID> &p_reflection_probes, const PagedArray<RID> &p_voxel_gi_instances, const PagedArray<RID> &p_decals, const PagedArray<RID> &p_lightmaps, RID p_environment, RID p_camera_effects, RID p_shadow_atlas, RID p_occluder_debug_tex, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass, float p_screen_lod_threshold, const RenderShadowData *p_render_shadows, int p_render_shadow_count, const RenderSDFGIData *p_render_sdfgi_regions, int p_render_sdfgi_region_count, const RenderSDFGIUpdateData *p_sdfgi_update_data) { // getting this here now so we can direct call a bunch of things more easily RenderBuffers *rb = nullptr; if (p_render_buffers.is_valid()) { @@ -3641,11 +3654,19 @@ void RendererSceneRenderRD::render_scene(RID p_render_buffers, const Transform3D { render_data.render_buffers = p_render_buffers; - render_data.cam_transform = p_cam_transform; - render_data.cam_projection = p_cam_projection; - render_data.cam_ortogonal = p_cam_projection.is_orthogonal(); // !BAS! Shouldn't this be p_cam_ortogonal ? - render_data.z_near = p_cam_projection.get_z_near(); - render_data.z_far = p_cam_projection.get_z_far(); + // Our first camera is used by default + render_data.cam_transform = p_camera_data->main_transform; + render_data.cam_projection = p_camera_data->main_projection; + render_data.view_projection[0] = p_camera_data->main_projection; + render_data.cam_ortogonal = p_camera_data->is_ortogonal; + + render_data.view_count = p_camera_data->view_count; + for (uint32_t v = 0; v < p_camera_data->view_count; v++) { + render_data.view_projection[v] = p_camera_data->view_projection[v]; + } + + render_data.z_near = p_camera_data->main_projection.get_z_near(); + render_data.z_far = p_camera_data->main_projection.get_z_far(); render_data.instances = &p_instances; render_data.lights = &p_lights; @@ -3660,8 +3681,9 @@ void RendererSceneRenderRD::render_scene(RID p_render_buffers, const Transform3D render_data.reflection_probe = p_reflection_probe; render_data.reflection_probe_pass = p_reflection_probe_pass; - render_data.lod_distance_multiplier = p_cam_projection.get_lod_multiplier(); - render_data.lod_camera_plane = Plane(p_cam_transform.get_origin(), -p_cam_transform.basis.get_axis(Vector3::AXIS_Z)); + // this should be the same for all cameras.. + render_data.lod_distance_multiplier = p_camera_data->main_projection.get_lod_multiplier(); + render_data.lod_camera_plane = Plane(p_camera_data->main_transform.get_origin(), -p_camera_data->main_transform.basis.get_axis(Vector3::AXIS_Z)); if (get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_DISABLE_LOD) { render_data.screen_lod_threshold = 0.0; @@ -3733,7 +3755,7 @@ void RendererSceneRenderRD::render_scene(RID p_render_buffers, const Transform3D if (rb != nullptr) { if (rb->sdfgi) { rb->sdfgi->update_cascades(); - rb->sdfgi->pre_process_gi(p_cam_transform, &render_data, this); + rb->sdfgi->pre_process_gi(render_data.cam_transform, &render_data, this); rb->sdfgi->update_light(); } @@ -3781,7 +3803,7 @@ void RendererSceneRenderRD::render_scene(RID p_render_buffers, const Transform3D _render_buffers_post_process_and_tonemap(&render_data); _render_buffers_debug_draw(p_render_buffers, p_shadow_atlas, p_occluder_debug_tex); if (debug_draw == RS::VIEWPORT_DEBUG_DRAW_SDFGI && rb != nullptr && rb->sdfgi != nullptr) { - rb->sdfgi->debug_draw(p_cam_projection, p_cam_transform, rb->width, rb->height, rb->render_target, rb->texture); + rb->sdfgi->debug_draw(render_data.cam_projection, render_data.cam_transform, rb->width, rb->height, rb->render_target, rb->texture); } } } diff --git a/servers/rendering/renderer_rd/renderer_scene_render_rd.h b/servers/rendering/renderer_rd/renderer_scene_render_rd.h index c8fcbecc95..9a793e42c5 100644 --- a/servers/rendering/renderer_rd/renderer_scene_render_rd.h +++ b/servers/rendering/renderer_rd/renderer_scene_render_rd.h @@ -50,6 +50,10 @@ struct RenderDataRD { CameraMatrix cam_projection = CameraMatrix(); bool cam_ortogonal = false; + // For stereo rendering + uint32_t view_count = 1; + CameraMatrix view_projection[RendererSceneRender::MAX_RENDER_VIEWS]; + float z_near = 0.0; float z_far = 0.0; @@ -87,7 +91,7 @@ protected: double time_step = 0; struct RenderBufferData { - virtual void configure(RID p_color_buffer, RID p_depth_buffer, int p_width, int p_height, RS::ViewportMSAA p_msaa) = 0; + virtual void configure(RID p_color_buffer, RID p_depth_buffer, int p_width, int p_height, RS::ViewportMSAA p_msaa, uint32_t p_view_count) = 0; virtual ~RenderBufferData() {} }; virtual RenderBufferData *_create_render_buffer_data() = 0; @@ -411,6 +415,7 @@ private: RS::ViewportMSAA msaa = RS::VIEWPORT_MSAA_DISABLED; RS::ViewportScreenSpaceAA screen_space_aa = RS::VIEWPORT_SCREEN_SPACE_AA_DISABLED; bool use_debanding = false; + uint32_t view_count = 1; RID render_target; @@ -454,6 +459,10 @@ private: Vector<RID> ao_pong_slices; RID ao_final; RID importance_map[2]; + + RID downsample_uniform_set; + RID gather_uniform_set; + RID importance_map_uniform_set; } ssao; struct SSR { @@ -1118,7 +1127,7 @@ public: /* render buffers */ RID render_buffers_create(); - void render_buffers_configure(RID p_render_buffers, RID p_render_target, int p_width, int p_height, RS::ViewportMSAA p_msaa, RS::ViewportScreenSpaceAA p_screen_space_aa, bool p_use_debanding); + void render_buffers_configure(RID p_render_buffers, RID p_render_target, int p_width, int p_height, RS::ViewportMSAA p_msaa, RS::ViewportScreenSpaceAA p_screen_space_aa, bool p_use_debanding, uint32_t p_view_count); void gi_set_use_half_resolution(bool p_enable); RID render_buffers_get_ao_texture(RID p_render_buffers); @@ -1147,7 +1156,7 @@ public: float render_buffers_get_volumetric_fog_end(RID p_render_buffers); float render_buffers_get_volumetric_fog_detail_spread(RID p_render_buffers); - void render_scene(RID p_render_buffers, const Transform3D &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_ortogonal, const PagedArray<GeometryInstance *> &p_instances, const PagedArray<RID> &p_lights, const PagedArray<RID> &p_reflection_probes, const PagedArray<RID> &p_voxel_gi_instances, const PagedArray<RID> &p_decals, const PagedArray<RID> &p_lightmaps, RID p_environment, RID p_camera_effects, RID p_shadow_atlas, RID p_occluder_debug_tex, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass, float p_screen_lod_threshold, const RenderShadowData *p_render_shadows, int p_render_shadow_count, const RenderSDFGIData *p_render_sdfgi_regions, int p_render_sdfgi_region_count, const RenderSDFGIUpdateData *p_sdfgi_update_data = nullptr); + void render_scene(RID p_render_buffers, const CameraData *p_camera_data, const PagedArray<GeometryInstance *> &p_instances, const PagedArray<RID> &p_lights, const PagedArray<RID> &p_reflection_probes, const PagedArray<RID> &p_voxel_gi_instances, const PagedArray<RID> &p_decals, const PagedArray<RID> &p_lightmaps, RID p_environment, RID p_camera_effects, RID p_shadow_atlas, RID p_occluder_debug_tex, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass, float p_screen_lod_threshold, const RenderShadowData *p_render_shadows, int p_render_shadow_count, const RenderSDFGIData *p_render_sdfgi_regions, int p_render_sdfgi_region_count, const RenderSDFGIUpdateData *p_sdfgi_update_data = nullptr); void render_material(const Transform3D &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_ortogonal, const PagedArray<GeometryInstance *> &p_instances, RID p_framebuffer, const Rect2i &p_region); diff --git a/servers/rendering/renderer_rd/renderer_scene_sky_rd.cpp b/servers/rendering/renderer_rd/renderer_scene_sky_rd.cpp index e68f466015..1aa01dd16e 100644 --- a/servers/rendering/renderer_rd/renderer_scene_sky_rd.cpp +++ b/servers/rendering/renderer_rd/renderer_scene_sky_rd.cpp @@ -32,6 +32,7 @@ #include "core/config/project_settings.h" #include "core/math/math_defs.h" #include "renderer_scene_render_rd.h" +#include "servers/rendering/renderer_rd/renderer_compositor_rd.h" #include "servers/rendering/rendering_server_default.h" //////////////////////////////////////////////////////////////////////////////// @@ -126,8 +127,12 @@ void RendererSceneSkyRD::SkyShaderData::set_code(const String &p_code) { depth_stencil_state.enable_depth_test = true; depth_stencil_state.depth_compare_operator = RD::COMPARE_OP_LESS_OR_EQUAL; - RID shader_variant = scene_singleton->sky.sky_shader.shader.version_get_shader(version, i); - pipelines[i].setup(shader_variant, RD::RENDER_PRIMITIVE_TRIANGLES, RD::PipelineRasterizationState(), RD::PipelineMultisampleState(), depth_stencil_state, RD::PipelineColorBlendState::create_disabled(), 0); + if (scene_singleton->sky.sky_shader.shader.is_variant_enabled(i)) { + RID shader_variant = scene_singleton->sky.sky_shader.shader.version_get_shader(version, i); + pipelines[i].setup(shader_variant, RD::RENDER_PRIMITIVE_TRIANGLES, RD::PipelineRasterizationState(), RD::PipelineMultisampleState(), depth_stencil_state, RD::PipelineColorBlendState::create_disabled(), 0); + } else { + pipelines[i].clear(); + } } valid = true; @@ -694,7 +699,18 @@ void RendererSceneSkyRD::init(RendererStorageRD *p_storage) { sky_modes.push_back("\n#define USE_CUBEMAP_PASS\n"); // Cubemap sky_modes.push_back("\n#define USE_CUBEMAP_PASS\n#define USE_HALF_RES_PASS\n"); // Half Res Cubemap sky_modes.push_back("\n#define USE_CUBEMAP_PASS\n#define USE_QUARTER_RES_PASS\n"); // Quarter res Cubemap + + sky_modes.push_back("\n#define USE_MULTIVIEW\n"); // Full size multiview + sky_modes.push_back("\n#define USE_HALF_RES_PASS\n#define USE_MULTIVIEW\n"); // Half Res multiview + sky_modes.push_back("\n#define USE_QUARTER_RES_PASS\n#define USE_MULTIVIEW\n"); // Quarter res multiview + sky_shader.shader.initialize(sky_modes, defines); + + if (!RendererCompositorRD::singleton->is_xr_enabled()) { + sky_shader.shader.set_variant_enabled(SKY_VERSION_BACKGROUND_MULTIVIEW, false); + sky_shader.shader.set_variant_enabled(SKY_VERSION_HALF_RES_MULTIVIEW, false); + sky_shader.shader.set_variant_enabled(SKY_VERSION_QUARTER_RES_MULTIVIEW, false); + } } // register our shader funds @@ -1140,7 +1156,7 @@ void RendererSceneSkyRD::update(RendererSceneEnvironmentRD *p_env, const CameraM RID texture_uniform_set = sky->get_textures(storage, SKY_TEXTURE_SET_CUBEMAP_QUARTER_RES, sky_shader.default_shader_rd); cubemap_draw_list = RD::get_singleton()->draw_list_begin(sky->reflection.layers[0].mipmaps[2].framebuffers[i], RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD); - storage->get_effects()->render_sky(cubemap_draw_list, p_time, sky->reflection.layers[0].mipmaps[2].framebuffers[i], sky_scene_state.uniform_set, sky_scene_state.fog_uniform_set, pipeline, material->uniform_set, texture_uniform_set, cm, local_view.basis, multiplier, p_transform.origin); + storage->get_effects()->render_sky(cubemap_draw_list, p_time, sky->reflection.layers[0].mipmaps[2].framebuffers[i], sky_scene_state.uniform_set, sky_scene_state.fog_uniform_set, pipeline, material->uniform_set, texture_uniform_set, 1, &cm, local_view.basis, multiplier, p_transform.origin); RD::get_singleton()->draw_list_end(); } } @@ -1158,7 +1174,7 @@ void RendererSceneSkyRD::update(RendererSceneEnvironmentRD *p_env, const CameraM RID texture_uniform_set = sky->get_textures(storage, SKY_TEXTURE_SET_CUBEMAP_HALF_RES, sky_shader.default_shader_rd); cubemap_draw_list = RD::get_singleton()->draw_list_begin(sky->reflection.layers[0].mipmaps[1].framebuffers[i], RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD); - storage->get_effects()->render_sky(cubemap_draw_list, p_time, sky->reflection.layers[0].mipmaps[1].framebuffers[i], sky_scene_state.uniform_set, sky_scene_state.fog_uniform_set, pipeline, material->uniform_set, texture_uniform_set, cm, local_view.basis, multiplier, p_transform.origin); + storage->get_effects()->render_sky(cubemap_draw_list, p_time, sky->reflection.layers[0].mipmaps[1].framebuffers[i], sky_scene_state.uniform_set, sky_scene_state.fog_uniform_set, pipeline, material->uniform_set, texture_uniform_set, 1, &cm, local_view.basis, multiplier, p_transform.origin); RD::get_singleton()->draw_list_end(); } } @@ -1172,7 +1188,7 @@ void RendererSceneSkyRD::update(RendererSceneEnvironmentRD *p_env, const CameraM RID texture_uniform_set = sky->get_textures(storage, SKY_TEXTURE_SET_CUBEMAP, sky_shader.default_shader_rd); cubemap_draw_list = RD::get_singleton()->draw_list_begin(sky->reflection.layers[0].mipmaps[0].framebuffers[i], RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD); - storage->get_effects()->render_sky(cubemap_draw_list, p_time, sky->reflection.layers[0].mipmaps[0].framebuffers[i], sky_scene_state.uniform_set, sky_scene_state.fog_uniform_set, pipeline, material->uniform_set, texture_uniform_set, cm, local_view.basis, multiplier, p_transform.origin); + storage->get_effects()->render_sky(cubemap_draw_list, p_time, sky->reflection.layers[0].mipmaps[0].framebuffers[i], sky_scene_state.uniform_set, sky_scene_state.fog_uniform_set, pipeline, material->uniform_set, texture_uniform_set, 1, &cm, local_view.basis, multiplier, p_transform.origin); RD::get_singleton()->draw_list_end(); } @@ -1213,9 +1229,12 @@ void RendererSceneSkyRD::update(RendererSceneEnvironmentRD *p_env, const CameraM } } -void RendererSceneSkyRD::draw(RendererSceneEnvironmentRD *p_env, bool p_can_continue_color, bool p_can_continue_depth, RID p_fb, const CameraMatrix &p_projection, const Transform3D &p_transform, double p_time) { +void RendererSceneSkyRD::draw(RendererSceneEnvironmentRD *p_env, bool p_can_continue_color, bool p_can_continue_depth, RID p_fb, uint32_t p_view_count, const CameraMatrix *p_projections, const Transform3D &p_transform, double p_time) { ERR_FAIL_COND(!p_env); + ERR_FAIL_COND(p_view_count == 0); + ERR_FAIL_COND(p_view_count > RendererSceneRender::MAX_RENDER_VIEWS); + Sky *sky = get_sky(p_env->sky); ERR_FAIL_COND(!sky); @@ -1257,24 +1276,28 @@ void RendererSceneSkyRD::draw(RendererSceneEnvironmentRD *p_env, bool p_can_cont float multiplier = p_env->bg_energy; float custom_fov = p_env->sky_custom_fov; + // Camera CameraMatrix camera; + uint32_t view_count = p_view_count; + const CameraMatrix *projections = p_projections; if (custom_fov) { - float near_plane = p_projection.get_z_near(); - float far_plane = p_projection.get_z_far(); - float aspect = p_projection.get_aspect(); + // With custom fov we don't support stereo... + float near_plane = p_projections[0].get_z_near(); + float far_plane = p_projections[0].get_z_far(); + float aspect = p_projections[0].get_aspect(); camera.set_perspective(custom_fov, aspect, near_plane, far_plane); - } else { - camera = p_projection; + view_count = 1; + projections = &camera; } sky_transform = p_transform.basis * sky_transform; if (shader_data->uses_quarter_res) { - PipelineCacheRD *pipeline = &shader_data->pipelines[SKY_VERSION_QUARTER_RES]; + PipelineCacheRD *pipeline = &shader_data->pipelines[view_count > 1 ? SKY_VERSION_QUARTER_RES_MULTIVIEW : SKY_VERSION_QUARTER_RES]; RID texture_uniform_set = sky->get_textures(storage, SKY_TEXTURE_SET_QUARTER_RES, sky_shader.default_shader_rd); @@ -1282,12 +1305,12 @@ void RendererSceneSkyRD::draw(RendererSceneEnvironmentRD *p_env, bool p_can_cont clear_colors.push_back(Color(0.0, 0.0, 0.0)); RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(sky->quarter_res_framebuffer, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_DISCARD, clear_colors); - storage->get_effects()->render_sky(draw_list, p_time, sky->quarter_res_framebuffer, sky_scene_state.uniform_set, sky_scene_state.fog_uniform_set, pipeline, material->uniform_set, texture_uniform_set, camera, sky_transform, multiplier, p_transform.origin); + storage->get_effects()->render_sky(draw_list, p_time, sky->quarter_res_framebuffer, sky_scene_state.uniform_set, sky_scene_state.fog_uniform_set, pipeline, material->uniform_set, texture_uniform_set, view_count, projections, sky_transform, multiplier, p_transform.origin); RD::get_singleton()->draw_list_end(); } if (shader_data->uses_half_res) { - PipelineCacheRD *pipeline = &shader_data->pipelines[SKY_VERSION_HALF_RES]; + PipelineCacheRD *pipeline = &shader_data->pipelines[view_count > 1 ? SKY_VERSION_HALF_RES_MULTIVIEW : SKY_VERSION_HALF_RES]; RID texture_uniform_set = sky->get_textures(storage, SKY_TEXTURE_SET_HALF_RES, sky_shader.default_shader_rd); @@ -1295,11 +1318,11 @@ void RendererSceneSkyRD::draw(RendererSceneEnvironmentRD *p_env, bool p_can_cont clear_colors.push_back(Color(0.0, 0.0, 0.0)); RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(sky->half_res_framebuffer, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_DISCARD, clear_colors); - storage->get_effects()->render_sky(draw_list, p_time, sky->half_res_framebuffer, sky_scene_state.uniform_set, sky_scene_state.fog_uniform_set, pipeline, material->uniform_set, texture_uniform_set, camera, sky_transform, multiplier, p_transform.origin); + storage->get_effects()->render_sky(draw_list, p_time, sky->half_res_framebuffer, sky_scene_state.uniform_set, sky_scene_state.fog_uniform_set, pipeline, material->uniform_set, texture_uniform_set, view_count, projections, sky_transform, multiplier, p_transform.origin); RD::get_singleton()->draw_list_end(); } - PipelineCacheRD *pipeline = &shader_data->pipelines[SKY_VERSION_BACKGROUND]; + PipelineCacheRD *pipeline = &shader_data->pipelines[view_count > 1 ? SKY_VERSION_BACKGROUND_MULTIVIEW : SKY_VERSION_BACKGROUND]; RID texture_uniform_set; if (sky) { @@ -1309,7 +1332,7 @@ void RendererSceneSkyRD::draw(RendererSceneEnvironmentRD *p_env, bool p_can_cont } RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_fb, RD::INITIAL_ACTION_CONTINUE, p_can_continue_color ? RD::FINAL_ACTION_CONTINUE : RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_CONTINUE, p_can_continue_depth ? RD::FINAL_ACTION_CONTINUE : RD::FINAL_ACTION_READ); - storage->get_effects()->render_sky(draw_list, p_time, p_fb, sky_scene_state.uniform_set, sky_scene_state.fog_uniform_set, pipeline, material->uniform_set, texture_uniform_set, camera, sky_transform, multiplier, p_transform.origin); + storage->get_effects()->render_sky(draw_list, p_time, p_fb, sky_scene_state.uniform_set, sky_scene_state.fog_uniform_set, pipeline, material->uniform_set, texture_uniform_set, view_count, projections, sky_transform, multiplier, p_transform.origin); RD::get_singleton()->draw_list_end(); } diff --git a/servers/rendering/renderer_rd/renderer_scene_sky_rd.h b/servers/rendering/renderer_rd/renderer_scene_sky_rd.h index 063e4e662e..200902bff2 100644 --- a/servers/rendering/renderer_rd/renderer_scene_sky_rd.h +++ b/servers/rendering/renderer_rd/renderer_scene_sky_rd.h @@ -72,6 +72,11 @@ public: SKY_VERSION_CUBEMAP, SKY_VERSION_CUBEMAP_HALF_RES, SKY_VERSION_CUBEMAP_QUARTER_RES, + + SKY_VERSION_BACKGROUND_MULTIVIEW, + SKY_VERSION_HALF_RES_MULTIVIEW, + SKY_VERSION_QUARTER_RES_MULTIVIEW, + SKY_VERSION_MAX }; @@ -270,7 +275,7 @@ public: void setup(RendererSceneEnvironmentRD *p_env, RID p_render_buffers, const CameraMatrix &p_projection, const Transform3D &p_transform, const Size2i p_screen_size, RendererSceneRenderRD *p_scene_render); void update(RendererSceneEnvironmentRD *p_env, const CameraMatrix &p_projection, const Transform3D &p_transform, double p_time); - void draw(RendererSceneEnvironmentRD *p_env, bool p_can_continue_color, bool p_can_continue_depth, RID p_fb, const CameraMatrix &p_projection, const Transform3D &p_transform, double p_time); + void draw(RendererSceneEnvironmentRD *p_env, bool p_can_continue_color, bool p_can_continue_depth, RID p_fb, uint32_t p_view_count, const CameraMatrix *p_projections, const Transform3D &p_transform, double p_time); void invalidate_sky(Sky *p_sky); void update_dirty_skys(); diff --git a/servers/rendering/renderer_rd/renderer_storage_rd.cpp b/servers/rendering/renderer_rd/renderer_storage_rd.cpp index 4f96defc0f..64be54115f 100644 --- a/servers/rendering/renderer_rd/renderer_storage_rd.cpp +++ b/servers/rendering/renderer_rd/renderer_storage_rd.cpp @@ -1440,8 +1440,10 @@ void RendererStorageRD::shader_set_code(RID p_shader, const String &p_code) { material->shader_type = new_type; } - for (Map<StringName, RID>::Element *E = shader->default_texture_parameter.front(); E; E = E->next()) { - shader->data->set_default_texture_param(E->key(), E->get()); + if (shader->data) { + for (Map<StringName, RID>::Element *E = shader->default_texture_parameter.front(); E; E = E->next()) { + shader->data->set_default_texture_param(E->key(), E->get()); + } } } @@ -3823,7 +3825,7 @@ void RendererStorageRD::_update_dirty_multimeshes() { if (multimesh->data_cache_used_dirty_regions) { uint32_t data_cache_dirty_region_count = (multimesh->instances - 1) / MULTIMESH_DIRTY_REGION_SIZE + 1; - uint32_t visible_region_count = (visible_instances - 1) / MULTIMESH_DIRTY_REGION_SIZE + 1; + uint32_t visible_region_count = visible_instances == 0 ? 0 : (visible_instances - 1) / MULTIMESH_DIRTY_REGION_SIZE + 1; uint32_t region_size = multimesh->stride_cache * MULTIMESH_DIRTY_REGION_SIZE * sizeof(float); @@ -6911,9 +6913,13 @@ void RendererStorageRD::_update_render_target(RenderTarget *rt) { rd_format.width = rt->size.width; rd_format.height = rt->size.height; rd_format.depth = 1; - rd_format.array_layers = 1; + rd_format.array_layers = rt->view_count; // for stereo we create two (or more) layers, need to see if we can make fallback work like this too if we don't have multiview rd_format.mipmaps = 1; - rd_format.texture_type = RD::TEXTURE_TYPE_2D; + if (rd_format.array_layers > 1) { // why are we not using rt->texture_type ?? + rd_format.texture_type = RD::TEXTURE_TYPE_2D_ARRAY; + } else { + rd_format.texture_type = RD::TEXTURE_TYPE_2D; + } rd_format.samples = RD::TEXTURE_SAMPLES_1; rd_format.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | RD::TEXTURE_USAGE_CAN_COPY_FROM_BIT; rd_format.shareable_formats.push_back(rt->color_format); @@ -6925,7 +6931,7 @@ void RendererStorageRD::_update_render_target(RenderTarget *rt) { Vector<RID> fb_textures; fb_textures.push_back(rt->color); - rt->framebuffer = RD::get_singleton()->framebuffer_create(fb_textures); + rt->framebuffer = RD::get_singleton()->framebuffer_create(fb_textures, RenderingDevice::INVALID_ID, rt->view_count); if (rt->framebuffer.is_null()) { _clear_render_target(rt); ERR_FAIL_COND(rt->framebuffer.is_null()); @@ -7039,12 +7045,15 @@ void RendererStorageRD::render_target_set_position(RID p_render_target, int p_x, //unused for this render target } -void RendererStorageRD::render_target_set_size(RID p_render_target, int p_width, int p_height) { +void RendererStorageRD::render_target_set_size(RID p_render_target, int p_width, int p_height, uint32_t p_view_count) { RenderTarget *rt = render_target_owner.getornull(p_render_target); ERR_FAIL_COND(!rt); - rt->size.x = p_width; - rt->size.y = p_height; - _update_render_target(rt); + if (rt->size.x != p_width || rt->size.y != p_height || rt->view_count != p_view_count) { + rt->size.x = p_width; + rt->size.y = p_height; + rt->view_count = p_view_count; + _update_render_target(rt); + } } RID RendererStorageRD::render_target_get_texture(RID p_render_target) { diff --git a/servers/rendering/renderer_rd/renderer_storage_rd.h b/servers/rendering/renderer_rd/renderer_storage_rd.h index 8bfab545fd..28a1044705 100644 --- a/servers/rendering/renderer_rd/renderer_storage_rd.h +++ b/servers/rendering/renderer_rd/renderer_storage_rd.h @@ -1125,6 +1125,7 @@ private: struct RenderTarget { Size2i size; + uint32_t view_count; RID framebuffer; RID color; @@ -2282,7 +2283,7 @@ public: RID render_target_create(); void render_target_set_position(RID p_render_target, int p_x, int p_y); - void render_target_set_size(RID p_render_target, int p_width, int p_height); + void render_target_set_size(RID p_render_target, int p_width, int p_height, uint32_t p_view_count); RID render_target_get_texture(RID p_render_target); void render_target_set_external_texture(RID p_render_target, unsigned int p_texture_id); void render_target_set_flag(RID p_render_target, RenderTargetFlags p_flag, bool p_value); diff --git a/servers/rendering/renderer_rd/shaders/scene_forward_mobile.glsl b/servers/rendering/renderer_rd/shaders/scene_forward_mobile.glsl index 23edc47f88..aa8a0b96c5 100644 --- a/servers/rendering/renderer_rd/shaders/scene_forward_mobile.glsl +++ b/servers/rendering/renderer_rd/shaders/scene_forward_mobile.glsl @@ -96,6 +96,18 @@ layout(location = 8) out float dp_clip; #endif +#ifdef USE_MULTIVIEW +#ifdef has_VK_KHR_multiview +#define ViewIndex gl_ViewIndex +#else +// !BAS! This needs to become an input once we implement our fallback! +#define ViewIndex 0 +#endif +#else +// Set to zero, not supported in non stereo +#define ViewIndex 0 +#endif //USE_MULTIVIEW + invariant gl_Position; #GLOBALS @@ -234,7 +246,13 @@ void main() { vec4 position; #endif +#ifdef USE_MULTIVIEW + mat4 projection_matrix = scene_data.projection_matrix_view[ViewIndex]; + mat4 inv_projection_matrix = scene_data.inv_projection_matrix_view[ViewIndex]; +#else mat4 projection_matrix = scene_data.projection_matrix; + mat4 inv_projection_matrix = scene_data.inv_projection_matrix; +#endif //USE_MULTIVIEW //using world coordinates #if !defined(SKIP_TRANSFORM_USED) && defined(VERTEX_WORLD_COORDS_USED) @@ -386,10 +404,26 @@ layout(location = 8) in float dp_clip; #endif +#ifdef USE_MULTIVIEW +#ifdef has_VK_KHR_multiview +#define ViewIndex gl_ViewIndex +#else +// !BAS! This needs to become an input once we implement our fallback! +#define ViewIndex 0 +#endif +#else +// Set to zero, not supported in non stereo +#define ViewIndex 0 +#endif //USE_MULTIVIEW + //defines to keep compatibility with vertex #define world_matrix draw_call.transform +#ifdef USE_MULTIVIEW +#define projection_matrix scene_data.projection_matrix_view[ViewIndex] +#else #define projection_matrix scene_data.projection_matrix +#endif #if defined(ENABLE_SSS) && defined(ENABLE_TRANSMITTANCE) //both required for transmittance to be enabled diff --git a/servers/rendering/renderer_rd/shaders/scene_forward_mobile_inc.glsl b/servers/rendering/renderer_rd/shaders/scene_forward_mobile_inc.glsl index 656a764a89..7fcd84695d 100644 --- a/servers/rendering/renderer_rd/shaders/scene_forward_mobile_inc.glsl +++ b/servers/rendering/renderer_rd/shaders/scene_forward_mobile_inc.glsl @@ -1,4 +1,9 @@ #define M_PI 3.14159265359 +#define MAX_VIEWS 2 + +#if defined(USE_MULTIVIEW) && defined(has_VK_KHR_multiview) +#extension GL_EXT_multiview : enable +#endif #include "decal_data_inc.glsl" @@ -121,10 +126,13 @@ global_variables; layout(set = 1, binding = 0, std140) uniform SceneData { mat4 projection_matrix; mat4 inv_projection_matrix; - mat4 camera_matrix; mat4 inv_camera_matrix; + // only used for multiview + mat4 projection_matrix_view[MAX_VIEWS]; + mat4 inv_projection_matrix_view[MAX_VIEWS]; + vec2 viewport_size; vec2 screen_pixel_size; diff --git a/servers/rendering/renderer_rd/shaders/sky.glsl b/servers/rendering/renderer_rd/shaders/sky.glsl index 9924da37d5..41c6325bc5 100644 --- a/servers/rendering/renderer_rd/shaders/sky.glsl +++ b/servers/rendering/renderer_rd/shaders/sky.glsl @@ -4,11 +4,17 @@ #VERSION_DEFINES +#define MAX_VIEWS 2 + +#if defined(USE_MULTIVIEW) && defined(has_VK_KHR_multiview) +#extension GL_EXT_multiview : enable +#endif + layout(location = 0) out vec2 uv_interp; layout(push_constant, binding = 1, std430) uniform Params { mat3 orientation; - vec4 proj; + vec4 projections[MAX_VIEWS]; vec4 position_multiplier; float time; } @@ -26,15 +32,29 @@ void main() { #VERSION_DEFINES +#ifdef USE_MULTIVIEW +#ifdef has_VK_KHR_multiview +#extension GL_EXT_multiview : enable +#define ViewIndex gl_ViewIndex +#else // has_VK_KHR_multiview +// !BAS! This needs to become an input once we implement our fallback! +#define ViewIndex 0 +#endif // has_VK_KHR_multiview +#else // USE_MULTIVIEW +// Set to zero, not supported in non stereo +#define ViewIndex 0 +#endif //USE_MULTIVIEW + #define M_PI 3.14159265359 +#define MAX_VIEWS 2 layout(location = 0) in vec2 uv_interp; layout(push_constant, binding = 1, std430) uniform Params { mat3 orientation; - vec4 proj; + vec4 projections[MAX_VIEWS]; vec4 position_multiplier; - float time; //TODO consider adding vec2 screen res, and float radiance size + float time; } params; @@ -85,7 +105,6 @@ struct DirectionalLightData { layout(set = 0, binding = 3, std140) uniform DirectionalLights { DirectionalLightData data[MAX_DIRECTIONAL_LIGHT_DATA_STRUCTS]; } - directional_lights; #ifdef MATERIAL_UNIFORMS_USED @@ -154,8 +173,8 @@ vec4 fog_process(vec3 view, vec3 sky_color) { void main() { vec3 cube_normal; cube_normal.z = -1.0; - cube_normal.x = (cube_normal.z * (-uv_interp.x - params.proj.x)) / params.proj.y; - cube_normal.y = -(cube_normal.z * (-uv_interp.y - params.proj.z)) / params.proj.w; + cube_normal.x = (cube_normal.z * (-uv_interp.x - params.projections[ViewIndex].x)) / params.projections[ViewIndex].y; + cube_normal.y = -(cube_normal.z * (-uv_interp.y - params.projections[ViewIndex].z)) / params.projections[ViewIndex].w; cube_normal = mat3(params.orientation) * cube_normal; cube_normal.z = -cube_normal.z; cube_normal = normalize(cube_normal); diff --git a/servers/rendering/renderer_rd/shaders/tonemap.glsl b/servers/rendering/renderer_rd/shaders/tonemap.glsl index 86b4da6b08..23f83b3b9c 100644 --- a/servers/rendering/renderer_rd/shaders/tonemap.glsl +++ b/servers/rendering/renderer_rd/shaders/tonemap.glsl @@ -4,6 +4,12 @@ #VERSION_DEFINES +#ifdef MULTIVIEW +#ifdef has_VK_KHR_multiview +#extension GL_EXT_multiview : enable +#endif +#endif + layout(location = 0) out vec2 uv_interp; void main() { @@ -18,9 +24,22 @@ void main() { #VERSION_DEFINES +#ifdef MULTIVIEW +#ifdef has_VK_KHR_multiview +#extension GL_EXT_multiview : enable +#define ViewIndex gl_ViewIndex +#else // has_VK_KHR_multiview +#define ViewIndex 0 +#endif // has_VK_KHR_multiview +#endif //MULTIVIEW + layout(location = 0) in vec2 uv_interp; +#ifdef MULTIVIEW +layout(set = 0, binding = 0) uniform sampler2DArray source_color; +#else layout(set = 0, binding = 0) uniform sampler2D source_color; +#endif layout(set = 1, binding = 0) uniform sampler2D source_auto_exposure; layout(set = 2, binding = 0) uniform sampler2D source_glow; #ifdef USE_1D_LUT @@ -277,10 +296,17 @@ vec3 do_fxaa(vec3 color, float exposure, vec2 uv_interp) { const float FXAA_REDUCE_MUL = (1.0 / 8.0); const float FXAA_SPAN_MAX = 8.0; +#ifdef MULTIVIEW + vec3 rgbNW = textureLod(source_color, vec3(uv_interp + vec2(-1.0, -1.0) * params.pixel_size, ViewIndex), 0.0).xyz * exposure; + vec3 rgbNE = textureLod(source_color, vec3(uv_interp + vec2(1.0, -1.0) * params.pixel_size, ViewIndex), 0.0).xyz * exposure; + vec3 rgbSW = textureLod(source_color, vec3(uv_interp + vec2(-1.0, 1.0) * params.pixel_size, ViewIndex), 0.0).xyz * exposure; + vec3 rgbSE = textureLod(source_color, vec3(uv_interp + vec2(1.0, 1.0) * params.pixel_size, ViewIndex), 0.0).xyz * exposure; +#else vec3 rgbNW = textureLod(source_color, uv_interp + vec2(-1.0, -1.0) * params.pixel_size, 0.0).xyz * exposure; vec3 rgbNE = textureLod(source_color, uv_interp + vec2(1.0, -1.0) * params.pixel_size, 0.0).xyz * exposure; vec3 rgbSW = textureLod(source_color, uv_interp + vec2(-1.0, 1.0) * params.pixel_size, 0.0).xyz * exposure; vec3 rgbSE = textureLod(source_color, uv_interp + vec2(1.0, 1.0) * params.pixel_size, 0.0).xyz * exposure; +#endif vec3 rgbM = color; vec3 luma = vec3(0.299, 0.587, 0.114); float lumaNW = dot(rgbNW, luma); @@ -305,8 +331,13 @@ vec3 do_fxaa(vec3 color, float exposure, vec2 uv_interp) { dir * rcpDirMin)) * params.pixel_size; +#ifdef MULTIVIEW + vec3 rgbA = 0.5 * exposure * (textureLod(source_color, vec3(uv_interp + dir * (1.0 / 3.0 - 0.5), ViewIndex), 0.0).xyz + textureLod(source_color, vec3(uv_interp + dir * (2.0 / 3.0 - 0.5), ViewIndex), 0.0).xyz); + vec3 rgbB = rgbA * 0.5 + 0.25 * exposure * (textureLod(source_color, vec3(uv_interp + dir * -0.5, ViewIndex), 0.0).xyz + textureLod(source_color, vec3(uv_interp + dir * 0.5, ViewIndex), 0.0).xyz); +#else vec3 rgbA = 0.5 * exposure * (textureLod(source_color, uv_interp + dir * (1.0 / 3.0 - 0.5), 0.0).xyz + textureLod(source_color, uv_interp + dir * (2.0 / 3.0 - 0.5), 0.0).xyz); vec3 rgbB = rgbA * 0.5 + 0.25 * exposure * (textureLod(source_color, uv_interp + dir * -0.5, 0.0).xyz + textureLod(source_color, uv_interp + dir * 0.5, 0.0).xyz); +#endif float lumaB = dot(rgbB, luma); if ((lumaB < lumaMin) || (lumaB > lumaMax)) { @@ -329,7 +360,11 @@ vec3 screen_space_dither(vec2 frag_coord) { } void main() { +#ifdef MULTIVIEW + vec3 color = textureLod(source_color, vec3(uv_interp, ViewIndex), 0.0f).rgb; +#else vec3 color = textureLod(source_color, uv_interp, 0.0f).rgb; +#endif // Exposure diff --git a/servers/rendering/renderer_scene.h b/servers/rendering/renderer_scene.h index 355c092dba..3b25498ed8 100644 --- a/servers/rendering/renderer_scene.h +++ b/servers/rendering/renderer_scene.h @@ -63,6 +63,8 @@ public: virtual void scenario_set_reflection_atlas_size(RID p_scenario, int p_reflection_size, int p_reflection_count) = 0; virtual bool is_scenario(RID p_scenario) const = 0; virtual RID scenario_get_environment(RID p_scenario) = 0; + virtual void scenario_add_viewport_visibility_mask(RID p_scenario, RID p_viewport) = 0; + virtual void scenario_remove_viewport_visibility_mask(RID p_scenario, RID p_viewport) = 0; virtual RID instance_allocate() = 0; virtual void instance_initialize(RID p_rid) = 0; @@ -82,6 +84,7 @@ public: virtual void instance_set_exterior(RID p_instance, bool p_enabled) = 0; virtual void instance_set_extra_visibility_margin(RID p_instance, real_t p_margin) = 0; + virtual void instance_set_visibility_parent(RID p_instance, RID p_parent_instance) = 0; // don't use these in a game! virtual Vector<ObjectID> instances_cull_aabb(const AABB &p_aabb, RID p_scenario = RID()) const = 0; @@ -92,8 +95,7 @@ public: virtual void instance_geometry_set_cast_shadows_setting(RID p_instance, RS::ShadowCastingSetting p_shadow_casting_setting) = 0; virtual void instance_geometry_set_material_override(RID p_instance, RID p_material) = 0; - virtual void instance_geometry_set_draw_range(RID p_instance, float p_min, float p_max, float p_min_margin, float p_max_margin) = 0; - virtual void instance_geometry_set_as_instance_lod(RID p_instance, RID p_as_lod_of_instance) = 0; + virtual void instance_geometry_set_visibility_range(RID p_instance, float p_min, float p_max, float p_min_margin, float p_max_margin) = 0; virtual void instance_geometry_set_lightmap(RID p_instance, RID p_lightmap, const Rect2 &p_lightmap_uv_scale, int p_slice_index) = 0; virtual void instance_geometry_set_lod_bias(RID p_instance, float p_lod_bias) = 0; @@ -189,7 +191,7 @@ public: virtual RID render_buffers_create() = 0; - virtual void render_buffers_configure(RID p_render_buffers, RID p_render_target, int p_width, int p_height, RS::ViewportMSAA p_msaa, RS::ViewportScreenSpaceAA p_screen_space_aa, bool p_use_debanding) = 0; + virtual void render_buffers_configure(RID p_render_buffers, RID p_render_target, int p_width, int p_height, RS::ViewportMSAA p_msaa, RS::ViewportScreenSpaceAA p_screen_space_aa, bool p_use_debanding, uint32_t p_view_count) = 0; virtual void gi_set_use_half_resolution(bool p_enable) = 0; @@ -201,8 +203,7 @@ public: virtual void sdfgi_set_debug_probe_select(const Vector3 &p_position, const Vector3 &p_dir) = 0; virtual void render_empty_scene(RID p_render_buffers, RID p_scenario, RID p_shadow_atlas) = 0; - virtual void render_camera(RID p_render_buffers, RID p_camera, RID p_scenario, RID p_viewport, Size2 p_viewport_size, float p_lod_threshold, RID p_shadow_atlas) = 0; - virtual void render_camera(RID p_render_buffers, Ref<XRInterface> &p_interface, XRInterface::Eyes p_eye, RID p_camera, RID p_scenario, RID p_viewport, Size2 p_viewport_size, float p_lod_threshold, RID p_shadow_atlas) = 0; + virtual void render_camera(RID p_render_buffers, RID p_camera, RID p_scenario, RID p_viewport, Size2 p_viewport_size, float p_lod_threshold, RID p_shadow_atlas, Ref<XRInterface> &p_xr_interface) = 0; virtual void update() = 0; virtual void render_probes() = 0; diff --git a/servers/rendering/renderer_scene_cull.cpp b/servers/rendering/renderer_scene_cull.cpp index a001aea5f4..2c66b22089 100644 --- a/servers/rendering/renderer_scene_cull.cpp +++ b/servers/rendering/renderer_scene_cull.cpp @@ -323,6 +323,7 @@ void RendererSceneCull::scenario_initialize(RID p_rid) { scenario->instance_aabbs.set_page_pool(&instance_aabb_page_pool); scenario->instance_data.set_page_pool(&instance_data_page_pool); + scenario->instance_visibility.set_page_pool(&instance_visibility_data_page_pool); RendererSceneOcclusionCull::get_singleton()->add_scenario(p_rid); @@ -369,6 +370,37 @@ RID RendererSceneCull::scenario_get_environment(RID p_scenario) { return scenario->environment; } +void RendererSceneCull::scenario_remove_viewport_visibility_mask(RID p_scenario, RID p_viewport) { + Scenario *scenario = scenario_owner.getornull(p_scenario); + ERR_FAIL_COND(!scenario); + if (!scenario->viewport_visibility_masks.has(p_viewport)) { + return; + } + + uint64_t mask = scenario->viewport_visibility_masks[p_viewport]; + scenario->used_viewport_visibility_bits &= ~mask; + scenario->viewport_visibility_masks.erase(p_viewport); +} + +void RendererSceneCull::scenario_add_viewport_visibility_mask(RID p_scenario, RID p_viewport) { + Scenario *scenario = scenario_owner.getornull(p_scenario); + ERR_FAIL_COND(!scenario); + ERR_FAIL_COND(scenario->viewport_visibility_masks.has(p_viewport)); + + uint64_t new_mask = 1; + while (new_mask & scenario->used_viewport_visibility_bits) { + new_mask <<= 1; + } + + if (new_mask == 0) { + ERR_PRINT("Only 64 viewports per scenario allowed when using visibility ranges."); + new_mask = ((uint64_t)1) << 63; + } + + scenario->viewport_visibility_masks[p_viewport] = new_mask; + scenario->used_viewport_visibility_bits |= new_mask; +} + /* INSTANCING API */ void RendererSceneCull::_instance_queue_update(Instance *p_instance, bool p_update_aabb, bool p_update_dependencies) { @@ -1103,10 +1135,142 @@ void RendererSceneCull::instance_geometry_set_material_override(RID p_instance, } } -void RendererSceneCull::instance_geometry_set_draw_range(RID p_instance, float p_min, float p_max, float p_min_margin, float p_max_margin) { +void RendererSceneCull::instance_geometry_set_visibility_range(RID p_instance, float p_min, float p_max, float p_min_margin, float p_max_margin) { + Instance *instance = instance_owner.getornull(p_instance); + ERR_FAIL_COND(!instance); + + instance->visibility_range_begin = p_min; + instance->visibility_range_end = p_max; + instance->visibility_range_begin_margin = p_min_margin; + instance->visibility_range_end_margin = p_max_margin; + + _update_instance_visibility_dependencies(instance); + + if (instance->scenario && instance->visibility_index != -1) { + InstanceVisibilityData &vd = instance->scenario->instance_visibility[instance->visibility_index]; + vd.range_begin = instance->visibility_range_begin; + vd.range_end = instance->visibility_range_end; + vd.range_begin_margin = instance->visibility_range_begin_margin; + vd.range_end_margin = instance->visibility_range_end_margin; + } +} + +void RendererSceneCull::instance_set_visibility_parent(RID p_instance, RID p_parent_instance) { + Instance *instance = instance_owner.getornull(p_instance); + ERR_FAIL_COND(!instance); + + Instance *old_parent = instance->visibility_parent; + if (old_parent) { + if ((1 << old_parent->base_type) & RS::INSTANCE_GEOMETRY_MASK && old_parent->base_data) { + InstanceGeometryData *old_parent_geom = static_cast<InstanceGeometryData *>(old_parent->base_data); + old_parent_geom->visibility_dependencies.erase(instance); + _update_instance_visibility_depth(old_parent); + } + instance->visibility_parent = nullptr; + } + + Instance *parent = instance_owner.getornull(p_parent_instance); + ERR_FAIL_COND(p_parent_instance.is_valid() && !parent); + + if (parent) { + if ((1 << parent->base_type) & RS::INSTANCE_GEOMETRY_MASK && parent->base_data) { + InstanceGeometryData *parent_geom = static_cast<InstanceGeometryData *>(parent->base_data); + parent_geom->visibility_dependencies.insert(instance); + _update_instance_visibility_depth(parent); + } + instance->visibility_parent = parent; + } + + _update_instance_visibility_dependencies(instance); +} + +void RendererSceneCull::_update_instance_visibility_depth(Instance *p_instance) { + bool cycle_detected = false; + Set<Instance *> traversed_nodes; + + { + Instance *instance = p_instance; + while (instance && ((1 << instance->base_type) & RS::INSTANCE_GEOMETRY_MASK) && instance->base_data) { + InstanceGeometryData *geom = static_cast<InstanceGeometryData *>(instance->base_data); + if (!geom->visibility_dependencies.is_empty()) { + uint32_t depth = 0; + for (Set<Instance *>::Element *E = geom->visibility_dependencies.front(); E; E = E->next()) { + if (((1 << E->get()->base_type) & RS::INSTANCE_GEOMETRY_MASK) == 0 || !E->get()->base_data) { + continue; + } + InstanceGeometryData *child_geom = static_cast<InstanceGeometryData *>(E->get()->base_data); + depth = MAX(depth, child_geom->visibility_dependencies_depth); + } + geom->visibility_dependencies_depth = depth + 1; + } else { + geom->visibility_dependencies_depth = 0; + } + + if (instance->scenario && instance->visibility_index != -1) { + instance->scenario->instance_visibility.move(instance->visibility_index, geom->visibility_dependencies_depth); + } + + traversed_nodes.insert(instance); + + instance = instance->visibility_parent; + if (traversed_nodes.has(instance)) { + cycle_detected = true; + break; + } + } + } + + if (cycle_detected) { + ERR_PRINT("Cycle detected in the visibility dependecies tree."); + for (Set<Instance *>::Element *E = traversed_nodes.front(); E; E = E->next()) { + Instance *instance = E->get(); + InstanceGeometryData *geom = static_cast<InstanceGeometryData *>(instance->base_data); + geom->visibility_dependencies_depth = 0; + if (instance->scenario && instance->visibility_index != -1) { + instance->scenario->instance_visibility.move(instance->visibility_index, geom->visibility_dependencies_depth); + } + } + } } -void RendererSceneCull::instance_geometry_set_as_instance_lod(RID p_instance, RID p_as_lod_of_instance) { +void RendererSceneCull::_update_instance_visibility_dependencies(Instance *p_instance) { + bool is_geometry_instance = ((1 << p_instance->base_type) & RS::INSTANCE_GEOMETRY_MASK) && p_instance->base_data; + bool has_visibility_range = p_instance->visibility_range_begin > 0.0 || p_instance->visibility_range_end > 0.0; + bool needs_visibility_cull = has_visibility_range && is_geometry_instance && p_instance->array_index != -1; + + if (!needs_visibility_cull && p_instance->visibility_index != -1) { + p_instance->scenario->instance_visibility.remove(p_instance->visibility_index); + p_instance->visibility_index = -1; + } else if (needs_visibility_cull && p_instance->visibility_index == -1) { + InstanceVisibilityData vd; + vd.instance = p_instance; + vd.range_begin = p_instance->visibility_range_begin; + vd.range_end = p_instance->visibility_range_end; + vd.range_begin_margin = p_instance->visibility_range_begin_margin; + vd.range_end_margin = p_instance->visibility_range_end_margin; + vd.position = p_instance->transformed_aabb.get_position() + p_instance->transformed_aabb.get_size() / 2.0f; + vd.array_index = p_instance->array_index; + + InstanceGeometryData *geom_data = static_cast<InstanceGeometryData *>(p_instance->base_data); + p_instance->scenario->instance_visibility.insert(vd, geom_data->visibility_dependencies_depth); + } + + if (p_instance->scenario && p_instance->array_index != -1) { + p_instance->scenario->instance_data[p_instance->array_index].visibility_index = p_instance->visibility_index; + + InstanceGeometryData *geom_data = static_cast<InstanceGeometryData *>(p_instance->base_data); + if ((has_visibility_range || p_instance->visibility_parent) && (p_instance->visibility_index == -1 || (geom_data && geom_data->visibility_dependencies_depth == 0))) { + p_instance->scenario->instance_data[p_instance->array_index].flags |= InstanceData::FLAG_VISIBILITY_DEPENDENCY_NEEDS_CHECK; + } else { + p_instance->scenario->instance_data[p_instance->array_index].flags &= ~InstanceData::FLAG_VISIBILITY_DEPENDENCY_NEEDS_CHECK; + } + + if (p_instance->visibility_parent) { + p_instance->scenario->instance_data[p_instance->array_index].parent_array_index = p_instance->visibility_parent->array_index; + } else { + p_instance->scenario->instance_data[p_instance->array_index].parent_array_index = -1; + } + } } void RendererSceneCull::instance_geometry_set_lightmap(RID p_instance, RID p_lightmap, const Rect2 &p_lightmap_uv_scale, int p_slice_index) { @@ -1352,12 +1516,23 @@ void RendererSceneCull::_update_instance(Instance *p_instance) { idata.layer_mask = p_instance->layer_mask; idata.flags = p_instance->base_type; //changing it means de-indexing, so this never needs to be changed later idata.base_rid = p_instance->base; + idata.parent_array_index = p_instance->visibility_parent ? p_instance->visibility_parent->array_index : -1; + idata.visibility_index = p_instance->visibility_index; + switch (p_instance->base_type) { case RS::INSTANCE_MESH: case RS::INSTANCE_MULTIMESH: case RS::INSTANCE_IMMEDIATE: case RS::INSTANCE_PARTICLES: { - idata.instance_geometry = static_cast<InstanceGeometryData *>(p_instance->base_data)->geometry_instance; + InstanceGeometryData *geom = static_cast<InstanceGeometryData *>(p_instance->base_data); + idata.instance_geometry = geom->geometry_instance; + + for (Set<Instance *>::Element *E = geom->visibility_dependencies.front(); E; E = E->next()) { + Instance *dep_instance = E->get(); + if (dep_instance->array_index != -1) { + dep_instance->scenario->instance_data[dep_instance->array_index].parent_array_index = p_instance->array_index; + } + } } break; case RS::INSTANCE_LIGHT: { idata.instance_data_rid = static_cast<InstanceLightData *>(p_instance->base_data)->instance.get_id(); @@ -1404,6 +1579,7 @@ void RendererSceneCull::_update_instance(Instance *p_instance) { p_instance->scenario->instance_data.push_back(idata); p_instance->scenario->instance_aabbs.push_back(InstanceBounds(p_instance->transformed_aabb)); + _update_instance_visibility_dependencies(p_instance); } else { if ((1 << p_instance->base_type) & RS::INSTANCE_GEOMETRY_MASK) { p_instance->scenario->indexers[Scenario::INDEXER_GEOMETRY].update(p_instance->indexer_id, bvh_aabb); @@ -1413,6 +1589,10 @@ void RendererSceneCull::_update_instance(Instance *p_instance) { p_instance->scenario->instance_aabbs[p_instance->array_index] = InstanceBounds(p_instance->transformed_aabb); } + if (p_instance->visibility_index != -1) { + p_instance->scenario->instance_visibility[p_instance->visibility_index].position = p_instance->transformed_aabb.get_position() + p_instance->transformed_aabb.get_size() / 2.0f; + } + //move instance and repair pair_pass++; @@ -1486,9 +1666,24 @@ void RendererSceneCull::_unpair_instance(Instance *p_instance) { //replace this by last int32_t swap_with_index = p_instance->scenario->instance_data.size() - 1; if (swap_with_index != p_instance->array_index) { - p_instance->scenario->instance_data[swap_with_index].instance->array_index = p_instance->array_index; //swap + Instance *swapped_instance = p_instance->scenario->instance_data[swap_with_index].instance; + swapped_instance->array_index = p_instance->array_index; //swap p_instance->scenario->instance_data[p_instance->array_index] = p_instance->scenario->instance_data[swap_with_index]; p_instance->scenario->instance_aabbs[p_instance->array_index] = p_instance->scenario->instance_aabbs[swap_with_index]; + + if (swapped_instance->visibility_index != -1) { + swapped_instance->scenario->instance_visibility[swapped_instance->visibility_index].array_index = swapped_instance->array_index; + } + + if ((1 << swapped_instance->base_type) & RS::INSTANCE_GEOMETRY_MASK) { + InstanceGeometryData *geom = static_cast<InstanceGeometryData *>(swapped_instance->base_data); + for (Set<Instance *>::Element *E = geom->visibility_dependencies.front(); E; E = E->next()) { + Instance *dep_instance = E->get(); + if (dep_instance != p_instance && dep_instance->array_index != -1) { + dep_instance->scenario->instance_data[dep_instance->array_index].parent_array_index = swapped_instance->array_index; + } + } + } } // pop last @@ -1505,7 +1700,16 @@ void RendererSceneCull::_unpair_instance(Instance *p_instance) { scene_render->geometry_instance_pair_reflection_probe_instances(geom->geometry_instance, nullptr, 0); scene_render->geometry_instance_pair_decal_instances(geom->geometry_instance, nullptr, 0); scene_render->geometry_instance_pair_voxel_gi_instances(geom->geometry_instance, nullptr, 0); + + for (Set<Instance *>::Element *E = geom->visibility_dependencies.front(); E; E = E->next()) { + Instance *dep_instance = E->get(); + if (dep_instance->array_index != -1) { + dep_instance->scenario->instance_data[dep_instance->array_index].parent_array_index = -1; + } + } } + + _update_instance_visibility_dependencies(p_instance); } void RendererSceneCull::_update_instance_aabb(Instance *p_instance) { @@ -2186,153 +2390,159 @@ bool RendererSceneCull::_light_instance_update_shadow(Instance *p_instance, cons return animated_material_found; } -void RendererSceneCull::render_camera(RID p_render_buffers, RID p_camera, RID p_scenario, RID p_viewport, Size2 p_viewport_size, float p_screen_lod_threshold, RID p_shadow_atlas) { -// render to mono camera +void RendererSceneCull::render_camera(RID p_render_buffers, RID p_camera, RID p_scenario, RID p_viewport, Size2 p_viewport_size, float p_screen_lod_threshold, RID p_shadow_atlas, Ref<XRInterface> &p_xr_interface) { #ifndef _3D_DISABLED Camera *camera = camera_owner.getornull(p_camera); ERR_FAIL_COND(!camera); - /* STEP 1 - SETUP CAMERA */ - CameraMatrix camera_matrix; - bool ortho = false; - - switch (camera->type) { - case Camera::ORTHOGONAL: { - camera_matrix.set_orthogonal( - camera->size, - p_viewport_size.width / (float)p_viewport_size.height, - camera->znear, - camera->zfar, - camera->vaspect); - ortho = true; - } break; - case Camera::PERSPECTIVE: { - camera_matrix.set_perspective( - camera->fov, - p_viewport_size.width / (float)p_viewport_size.height, - camera->znear, - camera->zfar, - camera->vaspect); - ortho = false; + RendererSceneRender::CameraData camera_data; + + // Setup Camera(s) + if (p_xr_interface.is_null()) { + // Normal camera + Transform3D transform = camera->transform; + CameraMatrix projection; + bool vaspect = camera->vaspect; + bool is_ortogonal = false; + + switch (camera->type) { + case Camera::ORTHOGONAL: { + projection.set_orthogonal( + camera->size, + p_viewport_size.width / (float)p_viewport_size.height, + camera->znear, + camera->zfar, + camera->vaspect); + is_ortogonal = true; + } break; + case Camera::PERSPECTIVE: { + projection.set_perspective( + camera->fov, + p_viewport_size.width / (float)p_viewport_size.height, + camera->znear, + camera->zfar, + camera->vaspect); - } break; - case Camera::FRUSTUM: { - camera_matrix.set_frustum( - camera->size, - p_viewport_size.width / (float)p_viewport_size.height, - camera->offset, - camera->znear, - camera->zfar, - camera->vaspect); - ortho = false; - } break; + } break; + case Camera::FRUSTUM: { + projection.set_frustum( + camera->size, + p_viewport_size.width / (float)p_viewport_size.height, + camera->offset, + camera->znear, + camera->zfar, + camera->vaspect); + } break; + } + + camera_data.set_camera(transform, projection, is_ortogonal, vaspect); + } else { + // Setup our camera for our XR interface. + // We can support multiple views here each with their own camera + Transform3D transforms[RendererSceneRender::MAX_RENDER_VIEWS]; + CameraMatrix projections[RendererSceneRender::MAX_RENDER_VIEWS]; + + uint32_t view_count = p_xr_interface->get_view_count(); + ERR_FAIL_COND_MSG(view_count > RendererSceneRender::MAX_RENDER_VIEWS, "Requested view count is not supported"); + + float aspect = p_viewport_size.width / (float)p_viewport_size.height; + + Transform3D world_origin = XRServer::get_singleton()->get_world_origin(); + + // We ignore our camera position, it will have been positioned with a slightly old tracking position. + // Instead we take our origin point and have our XR interface add fresh tracking data! Whoohoo! + for (uint32_t v = 0; v < view_count; v++) { + transforms[v] = p_xr_interface->get_transform_for_view(v, world_origin); + projections[v] = p_xr_interface->get_projection_for_view(v, aspect, camera->znear, camera->zfar); + } + + if (view_count == 1) { + camera_data.set_camera(transforms[0], projections[0], false, camera->vaspect); + } else if (view_count == 2) { + camera_data.set_multiview_camera(view_count, transforms, projections, false, camera->vaspect); + } else { + // this won't be called (see fail check above) but keeping this comment to indicate we may support more then 2 views in the future... + } } RID environment = _render_get_environment(p_camera, p_scenario); RENDER_TIMESTAMP("Update occlusion buffer") - RendererSceneOcclusionCull::get_singleton()->buffer_update(p_viewport, camera->transform, camera_matrix, ortho, RendererThreadPool::singleton->thread_work_pool); + // For now just cull on the first camera + RendererSceneOcclusionCull::get_singleton()->buffer_update(p_viewport, camera_data.main_transform, camera_data.main_projection, camera_data.is_ortogonal, RendererThreadPool::singleton->thread_work_pool); - _render_scene(camera->transform, camera_matrix, ortho, camera->vaspect, p_render_buffers, environment, camera->effects, camera->visible_layers, p_scenario, p_viewport, p_shadow_atlas, RID(), -1, p_screen_lod_threshold); + _render_scene(&camera_data, p_render_buffers, environment, camera->effects, camera->visible_layers, p_scenario, p_viewport, p_shadow_atlas, RID(), -1, p_screen_lod_threshold); #endif } -void RendererSceneCull::render_camera(RID p_render_buffers, Ref<XRInterface> &p_interface, XRInterface::Eyes p_eye, RID p_camera, RID p_scenario, RID p_viewport, Size2 p_viewport_size, float p_screen_lod_threshold, RID p_shadow_atlas) { - // render for AR/VR interface -#if 0 - Camera *camera = camera_owner.getornull(p_camera); - ERR_FAIL_COND(!camera); +void RendererSceneCull::_visibility_cull_threaded(uint32_t p_thread, VisibilityCullData *cull_data) { + uint32_t total_threads = RendererThreadPool::singleton->thread_work_pool.get_thread_count(); + uint32_t bin_from = p_thread * cull_data->cull_count / total_threads; + uint32_t bin_to = (p_thread + 1 == total_threads) ? cull_data->cull_count : ((p_thread + 1) * cull_data->cull_count / total_threads); - /* SETUP CAMERA, we are ignoring type and FOV here */ - float aspect = p_viewport_size.width / (float)p_viewport_size.height; - CameraMatrix camera_matrix = p_interface->get_projection_for_eye(p_eye, aspect, camera->znear, camera->zfar); + _visibility_cull(*cull_data, cull_data->cull_offset + bin_from, cull_data->cull_offset + bin_to); +} - // We also ignore our camera position, it will have been positioned with a slightly old tracking position. - // Instead we take our origin point and have our ar/vr interface add fresh tracking data! Whoohoo! - Transform3D world_origin = XRServer::get_singleton()->get_world_origin(); - Transform3D cam_transform = p_interface->get_transform_for_eye(p_eye, world_origin); +void RendererSceneCull::_visibility_cull(const VisibilityCullData &cull_data, uint64_t p_from, uint64_t p_to) { + Scenario *scenario = cull_data.scenario; + for (unsigned int i = p_from; i < p_to; i++) { + InstanceVisibilityData &vd = scenario->instance_visibility[i]; + InstanceData &idata = scenario->instance_data[vd.array_index]; - RID environment = _render_get_environment(p_camera, p_scenario); + if (idata.parent_array_index >= 0) { + uint32_t parent_flags = scenario->instance_data[idata.parent_array_index].flags; + if ((parent_flags & InstanceData::FLAG_VISIBILITY_DEPENDENCY_HIDDEN) || (parent_flags & InstanceData::FLAG_VISIBILITY_DEPENDENCY_HIDDEN_CLOSE_RANGE) == 0) { + idata.flags |= InstanceData::FLAG_VISIBILITY_DEPENDENCY_HIDDEN; + idata.flags &= ~InstanceData::FLAG_VISIBILITY_DEPENDENCY_HIDDEN_CLOSE_RANGE; + continue; + } + } - // For stereo render we only prepare for our left eye and then reuse the outcome for our right eye - if (p_eye == XRInterface::EYE_LEFT) { - // Center our transform, we assume basis is equal. - Transform3D mono_transform = cam_transform; - Transform3D right_transform = p_interface->get_transform_for_eye(XRInterface::EYE_RIGHT, world_origin); - mono_transform.origin += right_transform.origin; - mono_transform.origin *= 0.5; - - // We need to combine our projection frustums for culling. - // Ideally we should use our clipping planes for this and combine them, - // however our shadow map logic uses our projection matrix. - // Note: as our left and right frustums should be mirrored, we don't need our right projection matrix. - - // - get some base values we need - float eye_dist = (mono_transform.origin - cam_transform.origin).length(); - float z_near = camera_matrix.get_z_near(); // get our near plane - float z_far = camera_matrix.get_z_far(); // get our far plane - float width = (2.0 * z_near) / camera_matrix.matrix[0][0]; - float x_shift = width * camera_matrix.matrix[2][0]; - float height = (2.0 * z_near) / camera_matrix.matrix[1][1]; - float y_shift = height * camera_matrix.matrix[2][1]; - - // printf("Eye_dist = %f, Near = %f, Far = %f, Width = %f, Shift = %f\n", eye_dist, z_near, z_far, width, x_shift); - - // - calculate our near plane size (horizontal only, right_near is mirrored) - float left_near = -eye_dist - ((width - x_shift) * 0.5); - - // - calculate our far plane size (horizontal only, right_far is mirrored) - float left_far = -eye_dist - (z_far * (width - x_shift) * 0.5 / z_near); - float left_far_right_eye = eye_dist - (z_far * (width + x_shift) * 0.5 / z_near); - if (left_far > left_far_right_eye) { - // on displays smaller then double our iod, the right eye far frustrum can overtake the left eyes. - left_far = left_far_right_eye; - } - - // - figure out required z-shift - float slope = (left_far - left_near) / (z_far - z_near); - float z_shift = (left_near / slope) - z_near; - - // - figure out new vertical near plane size (this will be slightly oversized thanks to our z-shift) - float top_near = (height - y_shift) * 0.5; - top_near += (top_near / z_near) * z_shift; - float bottom_near = -(height + y_shift) * 0.5; - bottom_near += (bottom_near / z_near) * z_shift; - - // printf("Left_near = %f, Left_far = %f, Top_near = %f, Bottom_near = %f, Z_shift = %f\n", left_near, left_far, top_near, bottom_near, z_shift); - - // - generate our frustum - CameraMatrix combined_matrix; - combined_matrix.set_frustum(left_near, -left_near, bottom_near, top_near, z_near + z_shift, z_far + z_shift); - - // and finally move our camera back - Transform3D apply_z_shift; - apply_z_shift.origin = Vector3(0.0, 0.0, z_shift); // z negative is forward so this moves it backwards - mono_transform *= apply_z_shift; - - // now prepare our scene with our adjusted transform projection matrix - _prepare_scene(mono_transform, combined_matrix, false, false, p_render_buffers, environment, camera->visible_layers, p_scenario, p_shadow_atlas, RID(), p_screen_lod_threshold); - } else if (p_eye == XRInterface::EYE_MONO) { - // For mono render, prepare as per usual - _prepare_scene(cam_transform, camera_matrix, false, false, p_render_buffers, environment, camera->visible_layers, p_scenario, p_shadow_atlas, RID(), p_screen_lod_threshold); - } - - // And render our scene... - _render_scene(p_render_buffers, cam_transform, camera_matrix, false, environment, camera->effects, p_scenario, p_shadow_atlas, RID(), -1, p_screen_lod_threshold); -#endif -}; + int range_check = _visibility_range_check(vd, cull_data.camera_position, cull_data.viewport_mask); + + if (range_check == -1) { + idata.flags |= InstanceData::FLAG_VISIBILITY_DEPENDENCY_HIDDEN; + idata.flags &= ~InstanceData::FLAG_VISIBILITY_DEPENDENCY_HIDDEN_CLOSE_RANGE; + } else if (range_check == 1) { + idata.flags &= ~InstanceData::FLAG_VISIBILITY_DEPENDENCY_HIDDEN; + idata.flags |= InstanceData::FLAG_VISIBILITY_DEPENDENCY_HIDDEN_CLOSE_RANGE; + } else { + idata.flags &= ~InstanceData::FLAG_VISIBILITY_DEPENDENCY_HIDDEN; + idata.flags &= ~InstanceData::FLAG_VISIBILITY_DEPENDENCY_HIDDEN_CLOSE_RANGE; + } + } +} -void RendererSceneCull::_frustum_cull_threaded(uint32_t p_thread, CullData *cull_data) { +int RendererSceneCull::_visibility_range_check(InstanceVisibilityData &r_vis_data, const Vector3 &p_camera_pos, uint64_t p_viewport_mask) { + float dist = p_camera_pos.distance_to(r_vis_data.position); + + bool in_range_last_frame = p_viewport_mask & r_vis_data.viewport_state; + float begin_offset = in_range_last_frame ? -r_vis_data.range_begin_margin : r_vis_data.range_begin_margin; + float end_offset = in_range_last_frame ? r_vis_data.range_end_margin : -r_vis_data.range_end_margin; + + if (r_vis_data.range_end > 0.0f && dist > r_vis_data.range_end + end_offset) { + r_vis_data.viewport_state &= ~p_viewport_mask; + return -1; + } else if (r_vis_data.range_begin > 0.0f && dist < r_vis_data.range_begin + begin_offset) { + r_vis_data.viewport_state &= ~p_viewport_mask; + return 1; + } else { + r_vis_data.viewport_state |= p_viewport_mask; + return 0; + } +} + +void RendererSceneCull::_scene_cull_threaded(uint32_t p_thread, CullData *cull_data) { uint32_t cull_total = cull_data->scenario->instance_data.size(); uint32_t total_threads = RendererThreadPool::singleton->thread_work_pool.get_thread_count(); uint32_t cull_from = p_thread * cull_total / total_threads; uint32_t cull_to = (p_thread + 1 == total_threads) ? cull_total : ((p_thread + 1) * cull_total / total_threads); - _frustum_cull(*cull_data, frustum_cull_result_threads[p_thread], cull_from, cull_to); + _scene_cull(*cull_data, scene_cull_result_threads[p_thread], cull_from, cull_to); } -void RendererSceneCull::_frustum_cull(CullData &cull_data, FrustumCullResult &cull_result, uint64_t p_from, uint64_t p_to) { +void RendererSceneCull::_scene_cull(CullData &cull_data, InstanceCullResult &cull_result, uint64_t p_from, uint64_t p_to) { uint64_t frame_number = RSG::rasterizer->get_frame_number(); float lightmap_probe_update_speed = RSG::storage->lightmap_get_probe_capture_update_speed() * RSG::rasterizer->get_frame_delta_time(); @@ -2347,177 +2557,192 @@ void RendererSceneCull::_frustum_cull(CullData &cull_data, FrustumCullResult &cu for (uint64_t i = p_from; i < p_to; i++) { bool mesh_visible = false; - if (cull_data.scenario->instance_aabbs[i].in_frustum(cull_data.cull->frustum) && (cull_data.occlusion_buffer == nullptr || cull_data.scenario->instance_data[i].flags & InstanceData::FLAG_IGNORE_OCCLUSION_CULLING || - !cull_data.occlusion_buffer->is_occluded(cull_data.scenario->instance_aabbs[i].bounds, cull_data.cam_transform.origin, inv_cam_transform, *cull_data.camera_matrix, z_near))) { - InstanceData &idata = cull_data.scenario->instance_data[i]; - uint32_t base_type = idata.flags & InstanceData::FLAG_BASE_TYPE_MASK; - - if ((cull_data.visible_layers & idata.layer_mask) == 0) { - //failure - } else if (base_type == RS::INSTANCE_LIGHT) { - cull_result.lights.push_back(idata.instance); - cull_result.light_instances.push_back(RID::from_uint64(idata.instance_data_rid)); - if (cull_data.shadow_atlas.is_valid() && RSG::storage->light_has_shadow(idata.base_rid)) { - scene_render->light_instance_mark_visible(RID::from_uint64(idata.instance_data_rid)); //mark it visible for shadow allocation later - } - - } else if (base_type == RS::INSTANCE_REFLECTION_PROBE) { - if (cull_data.render_reflection_probe != idata.instance) { - //avoid entering The Matrix + InstanceData &idata = cull_data.scenario->instance_data[i]; + uint32_t visibility_flags = idata.flags & (InstanceData::FLAG_VISIBILITY_DEPENDENCY_HIDDEN_CLOSE_RANGE | InstanceData::FLAG_VISIBILITY_DEPENDENCY_HIDDEN); + int32_t visibility_check = -1; - if ((idata.flags & InstanceData::FLAG_REFLECTION_PROBE_DIRTY) || scene_render->reflection_probe_instance_needs_redraw(RID::from_uint64(idata.instance_data_rid))) { - InstanceReflectionProbeData *reflection_probe = static_cast<InstanceReflectionProbeData *>(idata.instance->base_data); - cull_data.cull->lock.lock(); - if (!reflection_probe->update_list.in_list()) { - reflection_probe->render_step = 0; - reflection_probe_render_list.add_last(&reflection_probe->update_list); - } - cull_data.cull->lock.unlock(); +#define HIDDEN_BY_VISIBILITY_CHECKS (visibility_flags == InstanceData::FLAG_VISIBILITY_DEPENDENCY_HIDDEN_CLOSE_RANGE || visibility_flags == InstanceData::FLAG_VISIBILITY_DEPENDENCY_HIDDEN) +#define LAYER_CHECK (cull_data.visible_layers & idata.layer_mask) +#define IN_FRUSTUM(f) (cull_data.scenario->instance_aabbs[i].in_frustum(f)) +#define VIS_RANGE_CHECK ((idata.visibility_index == -1) || _visibility_range_check(cull_data.scenario->instance_visibility[idata.visibility_index], cull_data.cam_transform.origin, cull_data.visibility_cull_data->viewport_mask) == 0) +#define VIS_PARENT_CHECK ((idata.parent_array_index == -1) || ((cull_data.scenario->instance_data[idata.parent_array_index].flags & InstanceData::FLAG_VISIBILITY_DEPENDENCY_NEEDS_CHECK) == InstanceData::FLAG_VISIBILITY_DEPENDENCY_HIDDEN_CLOSE_RANGE)) +#define VIS_CHECK (visibility_check < 0 ? (visibility_check = (visibility_flags != InstanceData::FLAG_VISIBILITY_DEPENDENCY_NEEDS_CHECK || (VIS_RANGE_CHECK && VIS_PARENT_CHECK))) : visibility_check) +#define OCCLUSION_CULLED (cull_data.occlusion_buffer != nullptr && cull_data.scenario->instance_data[i].flags & InstanceData::FLAG_IGNORE_OCCLUSION_CULLING && cull_data.occlusion_buffer->is_occluded(cull_data.scenario->instance_aabbs[i].bounds, cull_data.cam_transform.origin, inv_cam_transform, *cull_data.camera_matrix, z_near)) - idata.flags &= ~uint32_t(InstanceData::FLAG_REFLECTION_PROBE_DIRTY); + if (!HIDDEN_BY_VISIBILITY_CHECKS) { + if (LAYER_CHECK && IN_FRUSTUM(cull_data.cull->frustum) && VIS_CHECK && !OCCLUSION_CULLED) { + uint32_t base_type = idata.flags & InstanceData::FLAG_BASE_TYPE_MASK; + if (base_type == RS::INSTANCE_LIGHT) { + cull_result.lights.push_back(idata.instance); + cull_result.light_instances.push_back(RID::from_uint64(idata.instance_data_rid)); + if (cull_data.shadow_atlas.is_valid() && RSG::storage->light_has_shadow(idata.base_rid)) { + scene_render->light_instance_mark_visible(RID::from_uint64(idata.instance_data_rid)); //mark it visible for shadow allocation later } - if (scene_render->reflection_probe_instance_has_reflection(RID::from_uint64(idata.instance_data_rid))) { - cull_result.reflections.push_back(RID::from_uint64(idata.instance_data_rid)); - } - } - } else if (base_type == RS::INSTANCE_DECAL) { - cull_result.decals.push_back(RID::from_uint64(idata.instance_data_rid)); + } else if (base_type == RS::INSTANCE_REFLECTION_PROBE) { + if (cull_data.render_reflection_probe != idata.instance) { + //avoid entering The Matrix - } else if (base_type == RS::INSTANCE_VOXEL_GI) { - InstanceVoxelGIData *voxel_gi = static_cast<InstanceVoxelGIData *>(idata.instance->base_data); - cull_data.cull->lock.lock(); - if (!voxel_gi->update_element.in_list()) { - voxel_gi_update_list.add(&voxel_gi->update_element); - } - cull_data.cull->lock.unlock(); - cull_result.voxel_gi_instances.push_back(RID::from_uint64(idata.instance_data_rid)); + if ((idata.flags & InstanceData::FLAG_REFLECTION_PROBE_DIRTY) || scene_render->reflection_probe_instance_needs_redraw(RID::from_uint64(idata.instance_data_rid))) { + InstanceReflectionProbeData *reflection_probe = static_cast<InstanceReflectionProbeData *>(idata.instance->base_data); + cull_data.cull->lock.lock(); + if (!reflection_probe->update_list.in_list()) { + reflection_probe->render_step = 0; + reflection_probe_render_list.add_last(&reflection_probe->update_list); + } + cull_data.cull->lock.unlock(); - } else if (base_type == RS::INSTANCE_LIGHTMAP) { - cull_result.lightmaps.push_back(RID::from_uint64(idata.instance_data_rid)); - } else if (((1 << base_type) & RS::INSTANCE_GEOMETRY_MASK) && !(idata.flags & InstanceData::FLAG_CAST_SHADOWS_ONLY)) { - bool keep = true; + idata.flags &= ~uint32_t(InstanceData::FLAG_REFLECTION_PROBE_DIRTY); + } - if (idata.flags & InstanceData::FLAG_REDRAW_IF_VISIBLE) { - RenderingServerDefault::redraw_request(); - } + if (scene_render->reflection_probe_instance_has_reflection(RID::from_uint64(idata.instance_data_rid))) { + cull_result.reflections.push_back(RID::from_uint64(idata.instance_data_rid)); + } + } + } else if (base_type == RS::INSTANCE_DECAL) { + cull_result.decals.push_back(RID::from_uint64(idata.instance_data_rid)); + + } else if (base_type == RS::INSTANCE_VOXEL_GI) { + InstanceVoxelGIData *voxel_gi = static_cast<InstanceVoxelGIData *>(idata.instance->base_data); + cull_data.cull->lock.lock(); + if (!voxel_gi->update_element.in_list()) { + voxel_gi_update_list.add(&voxel_gi->update_element); + } + cull_data.cull->lock.unlock(); + cull_result.voxel_gi_instances.push_back(RID::from_uint64(idata.instance_data_rid)); - if (base_type == RS::INSTANCE_MESH) { - mesh_visible = true; - } else if (base_type == RS::INSTANCE_PARTICLES) { - //particles visible? process them - if (RSG::storage->particles_is_inactive(idata.base_rid)) { - //but if nothing is going on, don't do it. - keep = false; - } else { - cull_data.cull->lock.lock(); - RSG::storage->particles_request_process(idata.base_rid); - cull_data.cull->lock.unlock(); - RSG::storage->particles_set_view_axis(idata.base_rid, -cull_data.cam_transform.basis.get_axis(2).normalized(), cull_data.cam_transform.basis.get_axis(1).normalized()); - //particles visible? request redraw + } else if (base_type == RS::INSTANCE_LIGHTMAP) { + cull_result.lightmaps.push_back(RID::from_uint64(idata.instance_data_rid)); + } else if (((1 << base_type) & RS::INSTANCE_GEOMETRY_MASK) && !(idata.flags & InstanceData::FLAG_CAST_SHADOWS_ONLY)) { + bool keep = true; + + if (idata.flags & InstanceData::FLAG_REDRAW_IF_VISIBLE) { RenderingServerDefault::redraw_request(); } - } - - if (geometry_instance_pair_mask & (1 << RS::INSTANCE_LIGHT) && (idata.flags & InstanceData::FLAG_GEOM_LIGHTING_DIRTY)) { - InstanceGeometryData *geom = static_cast<InstanceGeometryData *>(idata.instance->base_data); - uint32_t idx = 0; - for (Set<Instance *>::Element *E = geom->lights.front(); E; E = E->next()) { - InstanceLightData *light = static_cast<InstanceLightData *>(E->get()->base_data); - instance_pair_buffer[idx++] = light->instance; - if (idx == MAX_INSTANCE_PAIRS) { - break; + if (base_type == RS::INSTANCE_MESH) { + mesh_visible = true; + } else if (base_type == RS::INSTANCE_PARTICLES) { + //particles visible? process them + if (RSG::storage->particles_is_inactive(idata.base_rid)) { + //but if nothing is going on, don't do it. + keep = false; + } else { + cull_data.cull->lock.lock(); + RSG::storage->particles_request_process(idata.base_rid); + cull_data.cull->lock.unlock(); + RSG::storage->particles_set_view_axis(idata.base_rid, -cull_data.cam_transform.basis.get_axis(2).normalized(), cull_data.cam_transform.basis.get_axis(1).normalized()); + //particles visible? request redraw + RenderingServerDefault::redraw_request(); } } - scene_render->geometry_instance_pair_light_instances(geom->geometry_instance, instance_pair_buffer, idx); - idata.flags &= ~uint32_t(InstanceData::FLAG_GEOM_LIGHTING_DIRTY); - } + if (geometry_instance_pair_mask & (1 << RS::INSTANCE_LIGHT) && (idata.flags & InstanceData::FLAG_GEOM_LIGHTING_DIRTY)) { + InstanceGeometryData *geom = static_cast<InstanceGeometryData *>(idata.instance->base_data); + uint32_t idx = 0; - if (geometry_instance_pair_mask & (1 << RS::INSTANCE_REFLECTION_PROBE) && (idata.flags & InstanceData::FLAG_GEOM_REFLECTION_DIRTY)) { - InstanceGeometryData *geom = static_cast<InstanceGeometryData *>(idata.instance->base_data); - uint32_t idx = 0; + for (Set<Instance *>::Element *E = geom->lights.front(); E; E = E->next()) { + InstanceLightData *light = static_cast<InstanceLightData *>(E->get()->base_data); + instance_pair_buffer[idx++] = light->instance; + if (idx == MAX_INSTANCE_PAIRS) { + break; + } + } + + scene_render->geometry_instance_pair_light_instances(geom->geometry_instance, instance_pair_buffer, idx); + idata.flags &= ~uint32_t(InstanceData::FLAG_GEOM_LIGHTING_DIRTY); + } + + if (geometry_instance_pair_mask & (1 << RS::INSTANCE_REFLECTION_PROBE) && (idata.flags & InstanceData::FLAG_GEOM_REFLECTION_DIRTY)) { + InstanceGeometryData *geom = static_cast<InstanceGeometryData *>(idata.instance->base_data); + uint32_t idx = 0; - for (Set<Instance *>::Element *E = geom->reflection_probes.front(); E; E = E->next()) { - InstanceReflectionProbeData *reflection_probe = static_cast<InstanceReflectionProbeData *>(E->get()->base_data); + for (Set<Instance *>::Element *E = geom->reflection_probes.front(); E; E = E->next()) { + InstanceReflectionProbeData *reflection_probe = static_cast<InstanceReflectionProbeData *>(E->get()->base_data); - instance_pair_buffer[idx++] = reflection_probe->instance; - if (idx == MAX_INSTANCE_PAIRS) { - break; + instance_pair_buffer[idx++] = reflection_probe->instance; + if (idx == MAX_INSTANCE_PAIRS) { + break; + } } - } - scene_render->geometry_instance_pair_reflection_probe_instances(geom->geometry_instance, instance_pair_buffer, idx); - idata.flags &= ~uint32_t(InstanceData::FLAG_GEOM_REFLECTION_DIRTY); - } + scene_render->geometry_instance_pair_reflection_probe_instances(geom->geometry_instance, instance_pair_buffer, idx); + idata.flags &= ~uint32_t(InstanceData::FLAG_GEOM_REFLECTION_DIRTY); + } - if (geometry_instance_pair_mask & (1 << RS::INSTANCE_DECAL) && (idata.flags & InstanceData::FLAG_GEOM_DECAL_DIRTY)) { - InstanceGeometryData *geom = static_cast<InstanceGeometryData *>(idata.instance->base_data); - uint32_t idx = 0; + if (geometry_instance_pair_mask & (1 << RS::INSTANCE_DECAL) && (idata.flags & InstanceData::FLAG_GEOM_DECAL_DIRTY)) { + InstanceGeometryData *geom = static_cast<InstanceGeometryData *>(idata.instance->base_data); + uint32_t idx = 0; - for (Set<Instance *>::Element *E = geom->decals.front(); E; E = E->next()) { - InstanceDecalData *decal = static_cast<InstanceDecalData *>(E->get()->base_data); + for (Set<Instance *>::Element *E = geom->decals.front(); E; E = E->next()) { + InstanceDecalData *decal = static_cast<InstanceDecalData *>(E->get()->base_data); - instance_pair_buffer[idx++] = decal->instance; - if (idx == MAX_INSTANCE_PAIRS) { - break; + instance_pair_buffer[idx++] = decal->instance; + if (idx == MAX_INSTANCE_PAIRS) { + break; + } } + scene_render->geometry_instance_pair_decal_instances(geom->geometry_instance, instance_pair_buffer, idx); + idata.flags &= ~uint32_t(InstanceData::FLAG_GEOM_DECAL_DIRTY); } - scene_render->geometry_instance_pair_decal_instances(geom->geometry_instance, instance_pair_buffer, idx); - idata.flags &= ~uint32_t(InstanceData::FLAG_GEOM_DECAL_DIRTY); - } - if (idata.flags & InstanceData::FLAG_GEOM_VOXEL_GI_DIRTY) { - InstanceGeometryData *geom = static_cast<InstanceGeometryData *>(idata.instance->base_data); - uint32_t idx = 0; - for (Set<Instance *>::Element *E = geom->voxel_gi_instances.front(); E; E = E->next()) { - InstanceVoxelGIData *voxel_gi = static_cast<InstanceVoxelGIData *>(E->get()->base_data); + if (idata.flags & InstanceData::FLAG_GEOM_VOXEL_GI_DIRTY) { + InstanceGeometryData *geom = static_cast<InstanceGeometryData *>(idata.instance->base_data); + uint32_t idx = 0; + for (Set<Instance *>::Element *E = geom->voxel_gi_instances.front(); E; E = E->next()) { + InstanceVoxelGIData *voxel_gi = static_cast<InstanceVoxelGIData *>(E->get()->base_data); - instance_pair_buffer[idx++] = voxel_gi->probe_instance; - if (idx == MAX_INSTANCE_PAIRS) { - break; + instance_pair_buffer[idx++] = voxel_gi->probe_instance; + if (idx == MAX_INSTANCE_PAIRS) { + break; + } } - } - scene_render->geometry_instance_pair_voxel_gi_instances(geom->geometry_instance, instance_pair_buffer, idx); - idata.flags &= ~uint32_t(InstanceData::FLAG_GEOM_VOXEL_GI_DIRTY); - } + scene_render->geometry_instance_pair_voxel_gi_instances(geom->geometry_instance, instance_pair_buffer, idx); + idata.flags &= ~uint32_t(InstanceData::FLAG_GEOM_VOXEL_GI_DIRTY); + } - if ((idata.flags & InstanceData::FLAG_LIGHTMAP_CAPTURE) && idata.instance->last_frame_pass != frame_number && !idata.instance->lightmap_target_sh.is_empty() && !idata.instance->lightmap_sh.is_empty()) { - InstanceGeometryData *geom = static_cast<InstanceGeometryData *>(idata.instance->base_data); - Color *sh = idata.instance->lightmap_sh.ptrw(); - const Color *target_sh = idata.instance->lightmap_target_sh.ptr(); - for (uint32_t j = 0; j < 9; j++) { - sh[j] = sh[j].lerp(target_sh[j], MIN(1.0, lightmap_probe_update_speed)); + if ((idata.flags & InstanceData::FLAG_LIGHTMAP_CAPTURE) && idata.instance->last_frame_pass != frame_number && !idata.instance->lightmap_target_sh.is_empty() && !idata.instance->lightmap_sh.is_empty()) { + InstanceGeometryData *geom = static_cast<InstanceGeometryData *>(idata.instance->base_data); + Color *sh = idata.instance->lightmap_sh.ptrw(); + const Color *target_sh = idata.instance->lightmap_target_sh.ptr(); + for (uint32_t j = 0; j < 9; j++) { + sh[j] = sh[j].lerp(target_sh[j], MIN(1.0, lightmap_probe_update_speed)); + } + scene_render->geometry_instance_set_lightmap_capture(geom->geometry_instance, sh); + idata.instance->last_frame_pass = frame_number; } - scene_render->geometry_instance_set_lightmap_capture(geom->geometry_instance, sh); - idata.instance->last_frame_pass = frame_number; - } - if (keep) { - cull_result.geometry_instances.push_back(idata.instance_geometry); + if (keep) { + cull_result.geometry_instances.push_back(idata.instance_geometry); + } } } - } - for (uint32_t j = 0; j < cull_data.cull->shadow_count; j++) { - for (uint32_t k = 0; k < cull_data.cull->shadows[j].cascade_count; k++) { - if (cull_data.scenario->instance_aabbs[i].in_frustum(cull_data.cull->shadows[j].cascades[k].frustum)) { - InstanceData &idata = cull_data.scenario->instance_data[i]; - uint32_t base_type = idata.flags & InstanceData::FLAG_BASE_TYPE_MASK; + for (uint32_t j = 0; j < cull_data.cull->shadow_count; j++) { + for (uint32_t k = 0; k < cull_data.cull->shadows[j].cascade_count; k++) { + if (IN_FRUSTUM(cull_data.cull->shadows[j].cascades[k].frustum) && VIS_CHECK) { + uint32_t base_type = idata.flags & InstanceData::FLAG_BASE_TYPE_MASK; - if (((1 << base_type) & RS::INSTANCE_GEOMETRY_MASK) && idata.flags & InstanceData::FLAG_CAST_SHADOWS) { - cull_result.directional_shadows[j].cascade_geometry_instances[k].push_back(idata.instance_geometry); - mesh_visible = true; + if (((1 << base_type) & RS::INSTANCE_GEOMETRY_MASK) && idata.flags & InstanceData::FLAG_CAST_SHADOWS) { + cull_result.directional_shadows[j].cascade_geometry_instances[k].push_back(idata.instance_geometry); + mesh_visible = true; + } } } } } +#undef HIDDEN_BY_VISIBILITY_CHECKS +#undef LAYER_CHECK +#undef IN_FRUSTUM +#undef VIS_RANGE_CHECK +#undef VIS_PARENT_CHECK +#undef VIS_CHECK +#undef OCCLUSION_CULLED + for (uint32_t j = 0; j < cull_data.cull->sdfgi.region_count; j++) { if (cull_data.scenario->instance_aabbs[i].in_aabb(cull_data.cull->sdfgi.region_aabb[j])) { - InstanceData &idata = cull_data.scenario->instance_data[i]; uint32_t base_type = idata.flags & InstanceData::FLAG_BASE_TYPE_MASK; if (base_type == RS::INSTANCE_LIGHT) { @@ -2544,11 +2769,7 @@ void RendererSceneCull::_frustum_cull(CullData &cull_data, FrustumCullResult &cu } } -void RendererSceneCull::_render_scene(const Transform3D &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_orthogonal, bool p_cam_vaspect, RID p_render_buffers, RID p_environment, RID p_force_camera_effects, uint32_t p_visible_layers, RID p_scenario, RID p_viewport, RID p_shadow_atlas, RID p_reflection_probe, int p_reflection_probe_pass, float p_screen_lod_threshold, bool p_using_shadows) { - // Note, in stereo rendering: - // - p_cam_transform will be a transform in the middle of our two eyes - // - p_cam_projection is a wider frustrum that encompasses both eyes - +void RendererSceneCull::_render_scene(const RendererSceneRender::CameraData *p_camera_data, RID p_render_buffers, RID p_environment, RID p_force_camera_effects, uint32_t p_visible_layers, RID p_scenario, RID p_viewport, RID p_shadow_atlas, RID p_reflection_probe, int p_reflection_probe_pass, float p_screen_lod_threshold, bool p_using_shadows) { Instance *render_reflection_probe = instance_owner.getornull(p_reflection_probe); //if null, not rendering to it Scenario *scenario = scenario_owner.getornull(p_scenario); @@ -2559,16 +2780,44 @@ void RendererSceneCull::_render_scene(const Transform3D &p_cam_transform, const if (p_render_buffers.is_valid()) { //no rendering code here, this is only to set up what needs to be done, request regions, etc. - scene_render->sdfgi_update(p_render_buffers, p_environment, p_cam_transform.origin); //update conditions for SDFGI (whether its used or not) + scene_render->sdfgi_update(p_render_buffers, p_environment, p_camera_data->main_transform.origin); //update conditions for SDFGI (whether its used or not) + } + + RENDER_TIMESTAMP("Visibility Dependencies"); + + VisibilityCullData visibility_cull_data; + if (scenario->instance_visibility.get_bin_count() > 0) { + if (!scenario->viewport_visibility_masks.has(p_viewport)) { + scenario_add_viewport_visibility_mask(scenario->self, p_viewport); + } + + visibility_cull_data.scenario = scenario; + visibility_cull_data.viewport_mask = scenario->viewport_visibility_masks[p_viewport]; + visibility_cull_data.camera_position = p_camera_data->main_transform.origin; + + for (int i = scenario->instance_visibility.get_bin_count() - 1; i > 0; i--) { // We skip bin 0 + visibility_cull_data.cull_offset = scenario->instance_visibility.get_bin_start(i); + visibility_cull_data.cull_count = scenario->instance_visibility.get_bin_size(i); + + if (visibility_cull_data.cull_count == 0) { + continue; + } + + if (visibility_cull_data.cull_count > thread_cull_threshold) { + RendererThreadPool::singleton->thread_work_pool.do_work(RendererThreadPool::singleton->thread_work_pool.get_thread_count(), this, &RendererSceneCull::_visibility_cull_threaded, &visibility_cull_data); + } else { + _visibility_cull(visibility_cull_data, visibility_cull_data.cull_offset, visibility_cull_data.cull_offset + visibility_cull_data.cull_count); + } + } } - RENDER_TIMESTAMP("Frustum Culling"); + RENDER_TIMESTAMP("Culling"); - //rasterizer->set_camera(camera->transform, camera_matrix,ortho); + //rasterizer->set_camera(p_camera_data->main_transform, p_camera_data.main_projection, p_camera_data.is_ortogonal); - Vector<Plane> planes = p_cam_projection.get_projection_planes(p_cam_transform); + Vector<Plane> planes = p_camera_data->main_projection.get_projection_planes(p_camera_data->main_transform); - Plane near_plane(p_cam_transform.origin, -p_cam_transform.basis.get_axis(2).normalized()); + Plane near_plane(p_camera_data->main_transform.origin, -p_camera_data->main_transform.basis.get_axis(2).normalized()); /* STEP 2 - CULL */ @@ -2606,7 +2855,7 @@ void RendererSceneCull::_render_scene(const Transform3D &p_cam_transform, const scene_render->set_directional_shadow_count(lights_with_shadow.size()); for (int i = 0; i < lights_with_shadow.size(); i++) { - _light_instance_setup_directional_shadow(i, lights_with_shadow[i], p_cam_transform, p_cam_projection, p_cam_orthogonal, p_cam_vaspect); + _light_instance_setup_directional_shadow(i, lights_with_shadow[i], p_camera_data->main_transform, p_camera_data->main_projection, p_camera_data->is_ortogonal, p_camera_data->vaspect); } } @@ -2635,7 +2884,7 @@ void RendererSceneCull::_render_scene(const Transform3D &p_cam_transform, const } } - frustum_cull_result.clear(); + scene_cull_result.clear(); { uint64_t cull_from = 0; @@ -2647,30 +2896,30 @@ void RendererSceneCull::_render_scene(const Transform3D &p_cam_transform, const cull_data.cull = &cull; cull_data.scenario = scenario; cull_data.shadow_atlas = p_shadow_atlas; - cull_data.cam_transform = p_cam_transform; + cull_data.cam_transform = p_camera_data->main_transform; cull_data.visible_layers = p_visible_layers; cull_data.render_reflection_probe = render_reflection_probe; cull_data.occlusion_buffer = RendererSceneOcclusionCull::get_singleton()->buffer_get_ptr(p_viewport); - cull_data.camera_matrix = &p_cam_projection; + cull_data.camera_matrix = &p_camera_data->main_projection; //#define DEBUG_CULL_TIME #ifdef DEBUG_CULL_TIME uint64_t time_from = OS::get_singleton()->get_ticks_usec(); #endif if (cull_to > thread_cull_threshold) { //multiple threads - for (uint32_t i = 0; i < frustum_cull_result_threads.size(); i++) { - frustum_cull_result_threads[i].clear(); + for (uint32_t i = 0; i < scene_cull_result_threads.size(); i++) { + scene_cull_result_threads[i].clear(); } - RendererThreadPool::singleton->thread_work_pool.do_work(frustum_cull_result_threads.size(), this, &RendererSceneCull::_frustum_cull_threaded, &cull_data); + RendererThreadPool::singleton->thread_work_pool.do_work(scene_cull_result_threads.size(), this, &RendererSceneCull::_scene_cull_threaded, &cull_data); - for (uint32_t i = 0; i < frustum_cull_result_threads.size(); i++) { - frustum_cull_result.append_from(frustum_cull_result_threads[i]); + for (uint32_t i = 0; i < scene_cull_result_threads.size(); i++) { + scene_cull_result.append_from(scene_cull_result_threads[i]); } } else { //single threaded - _frustum_cull(cull_data, frustum_cull_result, cull_from, cull_to); + _scene_cull(cull_data, scene_cull_result, cull_from, cull_to); } #ifdef DEBUG_CULL_TIME @@ -2681,9 +2930,9 @@ void RendererSceneCull::_render_scene(const Transform3D &p_cam_transform, const print_line("time taken: " + rtos(time_avg / time_count)); #endif - if (frustum_cull_result.mesh_instances.size()) { - for (uint64_t i = 0; i < frustum_cull_result.mesh_instances.size(); i++) { - RSG::storage->mesh_instance_check_for_update(frustum_cull_result.mesh_instances[i]); + if (scene_cull_result.mesh_instances.size()) { + for (uint64_t i = 0; i < scene_cull_result.mesh_instances.size(); i++) { + RSG::storage->mesh_instance_check_for_update(scene_cull_result.mesh_instances[i]); } RSG::storage->update_mesh_instances(); } @@ -2707,14 +2956,14 @@ void RendererSceneCull::_render_scene(const Transform3D &p_cam_transform, const } render_shadow_data[max_shadows_used].light = cull.shadows[i].light_instance; render_shadow_data[max_shadows_used].pass = j; - render_shadow_data[max_shadows_used].instances.merge_unordered(frustum_cull_result.directional_shadows[i].cascade_geometry_instances[j]); + render_shadow_data[max_shadows_used].instances.merge_unordered(scene_cull_result.directional_shadows[i].cascade_geometry_instances[j]); max_shadows_used++; } } // Positional Shadowss - for (uint32_t i = 0; i < (uint32_t)frustum_cull_result.lights.size(); i++) { - Instance *ins = frustum_cull_result.lights[i]; + for (uint32_t i = 0; i < (uint32_t)scene_cull_result.lights.size(); i++) { + Instance *ins = scene_cull_result.lights[i]; if (!p_shadow_atlas.is_valid() || !RSG::storage->light_has_shadow(ins->base)) { continue; @@ -2726,12 +2975,12 @@ void RendererSceneCull::_render_scene(const Transform3D &p_cam_transform, const { //compute coverage - Transform3D cam_xf = p_cam_transform; - float zn = p_cam_projection.get_z_near(); + Transform3D cam_xf = p_camera_data->main_transform; + float zn = p_camera_data->main_projection.get_z_near(); Plane p(cam_xf.origin + cam_xf.basis.get_axis(2) * -zn, -cam_xf.basis.get_axis(2)); //camera near plane // near plane half width and height - Vector2 vp_half_extents = p_cam_projection.get_viewport_half_extents(); + Vector2 vp_half_extents = p_camera_data->main_projection.get_viewport_half_extents(); switch (RSG::storage->light_get_type(ins->base)) { case RS::LIGHT_OMNI: { @@ -2743,7 +2992,7 @@ void RendererSceneCull::_render_scene(const Transform3D &p_cam_transform, const ins->transform.origin + cam_xf.basis.get_axis(0) * radius }; - if (!p_cam_orthogonal) { + if (!p_camera_data->is_ortogonal) { //if using perspetive, map them to near plane for (int j = 0; j < 2; j++) { if (p.distance_to(points[j]) < 0) { @@ -2771,7 +3020,7 @@ void RendererSceneCull::_render_scene(const Transform3D &p_cam_transform, const base + cam_xf.basis.get_axis(0) * w }; - if (!p_cam_orthogonal) { + if (!p_camera_data->is_ortogonal) { //if using perspetive, map them to near plane for (int j = 0; j < 2; j++) { if (p.distance_to(points[j]) < 0) { @@ -2802,7 +3051,7 @@ void RendererSceneCull::_render_scene(const Transform3D &p_cam_transform, const if (redraw && max_shadows_used < MAX_UPDATE_SHADOWS) { //must redraw! RENDER_TIMESTAMP(">Rendering Light " + itos(i)); - light->shadow_dirty = _light_instance_update_shadow(ins, p_cam_transform, p_cam_projection, p_cam_orthogonal, p_cam_vaspect, p_shadow_atlas, scenario, p_screen_lod_threshold); + light->shadow_dirty = _light_instance_update_shadow(ins, p_camera_data->main_transform, p_camera_data->main_projection, p_camera_data->is_ortogonal, p_camera_data->vaspect, p_shadow_atlas, scenario, p_screen_lod_threshold); RENDER_TIMESTAMP("<Rendering Light " + itos(i)); } else { light->shadow_dirty = redraw; @@ -2818,13 +3067,13 @@ void RendererSceneCull::_render_scene(const Transform3D &p_cam_transform, const if (cull.sdfgi.region_count > 0) { //update regions for (uint32_t i = 0; i < cull.sdfgi.region_count; i++) { - render_sdfgi_data[i].instances.merge_unordered(frustum_cull_result.sdfgi_region_geometry_instances[i]); + render_sdfgi_data[i].instances.merge_unordered(scene_cull_result.sdfgi_region_geometry_instances[i]); render_sdfgi_data[i].region = i; } //check if static lights were culled bool static_lights_culled = false; for (uint32_t i = 0; i < cull.sdfgi.cascade_light_count; i++) { - if (frustum_cull_result.sdfgi_cascade_lights[i].size()) { + if (scene_cull_result.sdfgi_cascade_lights[i].size()) { static_lights_culled = true; break; } @@ -2833,7 +3082,7 @@ void RendererSceneCull::_render_scene(const Transform3D &p_cam_transform, const if (static_lights_culled) { sdfgi_update_data.static_cascade_count = cull.sdfgi.cascade_light_count; sdfgi_update_data.static_cascade_indices = cull.sdfgi.cascade_light_index; - sdfgi_update_data.static_positional_lights = frustum_cull_result.sdfgi_cascade_lights; + sdfgi_update_data.static_positional_lights = scene_cull_result.sdfgi_cascade_lights; sdfgi_update_data.update_static = true; } } @@ -2847,7 +3096,7 @@ void RendererSceneCull::_render_scene(const Transform3D &p_cam_transform, const //append the directional lights to the lights culled for (int i = 0; i < directional_lights.size(); i++) { - frustum_cull_result.light_instances.push_back(directional_lights[i]); + scene_cull_result.light_instances.push_back(directional_lights[i]); } RID camera_effects; @@ -2864,7 +3113,7 @@ void RendererSceneCull::_render_scene(const Transform3D &p_cam_transform, const } RENDER_TIMESTAMP("Render Scene "); - scene_render->render_scene(p_render_buffers, p_cam_transform, p_cam_projection, p_cam_orthogonal, frustum_cull_result.geometry_instances, frustum_cull_result.light_instances, frustum_cull_result.reflections, frustum_cull_result.voxel_gi_instances, frustum_cull_result.decals, frustum_cull_result.lightmaps, p_environment, camera_effects, p_shadow_atlas, occluders_tex, p_reflection_probe.is_valid() ? RID() : scenario->reflection_atlas, p_reflection_probe, p_reflection_probe_pass, p_screen_lod_threshold, render_shadow_data, max_shadows_used, render_sdfgi_data, cull.sdfgi.region_count, &sdfgi_update_data); + scene_render->render_scene(p_render_buffers, p_camera_data, scene_cull_result.geometry_instances, scene_cull_result.light_instances, scene_cull_result.reflections, scene_cull_result.voxel_gi_instances, scene_cull_result.decals, scene_cull_result.lightmaps, p_environment, camera_effects, p_shadow_atlas, occluders_tex, p_reflection_probe.is_valid() ? RID() : scenario->reflection_atlas, p_reflection_probe, p_reflection_probe_pass, p_screen_lod_threshold, render_shadow_data, max_shadows_used, render_sdfgi_data, cull.sdfgi.region_count, &sdfgi_update_data); for (uint32_t i = 0; i < max_shadows_used; i++) { render_shadow_data[i].instances.clear(); @@ -2911,7 +3160,11 @@ void RendererSceneCull::render_empty_scene(RID p_render_buffers, RID p_scenario, environment = scenario->fallback_environment; } RENDER_TIMESTAMP("Render Empty Scene "); - scene_render->render_scene(p_render_buffers, Transform3D(), CameraMatrix(), true, PagedArray<RendererSceneRender::GeometryInstance *>(), PagedArray<RID>(), PagedArray<RID>(), PagedArray<RID>(), PagedArray<RID>(), PagedArray<RID>(), RID(), RID(), p_shadow_atlas, RID(), scenario->reflection_atlas, RID(), 0, 0, nullptr, 0, nullptr, 0, nullptr); + + RendererSceneRender::CameraData camera_data; + camera_data.set_camera(Transform3D(), CameraMatrix(), true, false); + + scene_render->render_scene(p_render_buffers, &camera_data, PagedArray<RendererSceneRender::GeometryInstance *>(), PagedArray<RID>(), PagedArray<RID>(), PagedArray<RID>(), PagedArray<RID>(), PagedArray<RID>(), RID(), RID(), p_shadow_atlas, RID(), scenario->reflection_atlas, RID(), 0, 0, nullptr, 0, nullptr, 0, nullptr); #endif } @@ -2981,7 +3234,10 @@ bool RendererSceneCull::_render_reflection_probe_step(Instance *p_instance, int } RENDER_TIMESTAMP("Render Reflection Probe, Step " + itos(p_step)); - _render_scene(xform, cm, false, false, RID(), environment, RID(), RSG::storage->reflection_probe_get_cull_mask(p_instance->base), p_instance->scenario->self, RID(), shadow_atlas, reflection_probe->instance, p_step, lod_threshold, use_shadows); + RendererSceneRender::CameraData camera_data; + camera_data.set_camera(xform, cm, false, false); + + _render_scene(&camera_data, RID(), environment, RID(), RSG::storage->reflection_probe_get_cull_mask(p_instance->base), p_instance->scenario->self, RID(), shadow_atlas, reflection_probe->instance, p_step, lod_threshold, use_shadows); } else { //do roughness postprocess step until it believes it's done @@ -3192,7 +3448,7 @@ void RendererSceneCull::render_probes() { update_lights = true; } - frustum_cull_result.geometry_instances.clear(); + scene_cull_result.geometry_instances.clear(); RID instance_pair_buffer[MAX_INSTANCE_PAIRS]; @@ -3219,10 +3475,10 @@ void RendererSceneCull::render_probes() { ins->scenario->instance_data[ins->array_index].flags &= ~uint32_t(InstanceData::FLAG_GEOM_VOXEL_GI_DIRTY); } - frustum_cull_result.geometry_instances.push_back(geom->geometry_instance); + scene_cull_result.geometry_instances.push_back(geom->geometry_instance); } - scene_render->voxel_gi_update(probe->probe_instance, update_lights, probe->light_instances, frustum_cull_result.geometry_instances); + scene_render->voxel_gi_update(probe->probe_instance, update_lights, probe->light_instances, scene_cull_result.geometry_instances); voxel_gi_update_list.remove(voxel_gi); @@ -3237,7 +3493,7 @@ void RendererSceneCull::render_particle_colliders() { if (hfpc->scenario && hfpc->base_type == RS::INSTANCE_PARTICLES_COLLISION && RSG::storage->particles_collision_is_heightfield(hfpc->base)) { //update heightfield instance_cull_result.clear(); - frustum_cull_result.geometry_instances.clear(); + scene_cull_result.geometry_instances.clear(); struct CullAABB { PagedArray<Instance *> *result; @@ -3259,10 +3515,10 @@ void RendererSceneCull::render_particle_colliders() { continue; } InstanceGeometryData *geom = static_cast<InstanceGeometryData *>(instance->base_data); - frustum_cull_result.geometry_instances.push_back(geom->geometry_instance); + scene_cull_result.geometry_instances.push_back(geom->geometry_instance); } - scene_render->render_particle_collider_heightfield(hfpc->base, hfpc->transform, frustum_cull_result.geometry_instances); + scene_render->render_particle_collider_heightfield(hfpc->base, hfpc->transform, scene_cull_result.geometry_instances); } heightfield_particle_colliders_update_list.erase(heightfield_particle_colliders_update_list.front()); } @@ -3551,6 +3807,7 @@ bool RendererSceneCull::free(RID p_rid) { } scenario->instance_aabbs.reset(); scenario->instance_data.reset(); + scenario->instance_visibility.reset(); scene_render->free(scenario->reflection_probe_shadow_atlas); scene_render->free(scenario->reflection_atlas); @@ -3619,10 +3876,10 @@ RendererSceneCull::RendererSceneCull() { render_sdfgi_data[i].instances.set_page_pool(&geometry_instance_cull_page_pool); } - frustum_cull_result.init(&rid_cull_page_pool, &geometry_instance_cull_page_pool, &instance_cull_page_pool); - frustum_cull_result_threads.resize(RendererThreadPool::singleton->thread_work_pool.get_thread_count()); - for (uint32_t i = 0; i < frustum_cull_result_threads.size(); i++) { - frustum_cull_result_threads[i].init(&rid_cull_page_pool, &geometry_instance_cull_page_pool, &instance_cull_page_pool); + scene_cull_result.init(&rid_cull_page_pool, &geometry_instance_cull_page_pool, &instance_cull_page_pool); + scene_cull_result_threads.resize(RendererThreadPool::singleton->thread_work_pool.get_thread_count()); + for (uint32_t i = 0; i < scene_cull_result_threads.size(); i++) { + scene_cull_result_threads[i].init(&rid_cull_page_pool, &geometry_instance_cull_page_pool, &instance_cull_page_pool); } indexer_update_iterations = GLOBAL_GET("rendering/limits/spatial_indexer/update_iterations_per_frame"); @@ -3643,11 +3900,11 @@ RendererSceneCull::~RendererSceneCull() { render_sdfgi_data[i].instances.reset(); } - frustum_cull_result.reset(); - for (uint32_t i = 0; i < frustum_cull_result_threads.size(); i++) { - frustum_cull_result_threads[i].reset(); + scene_cull_result.reset(); + for (uint32_t i = 0; i < scene_cull_result_threads.size(); i++) { + scene_cull_result_threads[i].reset(); } - frustum_cull_result_threads.clear(); + scene_cull_result_threads.clear(); if (dummy_occlusion_culling) { memdelete(dummy_occlusion_culling); diff --git a/servers/rendering/renderer_scene_cull.h b/servers/rendering/renderer_scene_cull.h index f10eb67828..bdfbea95a2 100644 --- a/servers/rendering/renderer_scene_cull.h +++ b/servers/rendering/renderer_scene_cull.h @@ -31,6 +31,7 @@ #ifndef RENDERING_SERVER_SCENE_CULL_H #define RENDERING_SERVER_SCENE_CULL_H +#include "core/templates/bin_sorted_array.h" #include "core/templates/pass_func.h" #include "servers/rendering/renderer_compositor.h" @@ -259,6 +260,9 @@ public: FLAG_USES_MESH_INSTANCE = (1 << 17), FLAG_REFLECTION_PROBE_DIRTY = (1 << 18), FLAG_IGNORE_OCCLUSION_CULLING = (1 << 19), + FLAG_VISIBILITY_DEPENDENCY_NEEDS_CHECK = (3 << 20), // 2 bits, overlaps with the other vis. dependency flags + FLAG_VISIBILITY_DEPENDENCY_HIDDEN_CLOSE_RANGE = (1 << 20), + FLAG_VISIBILITY_DEPENDENCY_HIDDEN = (1 << 21), }; uint32_t flags = 0; @@ -269,10 +273,33 @@ public: RendererSceneRender::GeometryInstance *instance_geometry; }; Instance *instance = nullptr; + int32_t parent_array_index = -1; + int32_t visibility_index = -1; + }; + + struct InstanceVisibilityData { + uint64_t viewport_state = 0; + int32_t array_index = -1; + Vector3 position; + Instance *instance = nullptr; + float range_begin = 0.0f; + float range_end = 0.0f; + float range_begin_margin = 0.0f; + float range_end_margin = 0.0f; + }; + + class VisibilityArray : public BinSortedArray<InstanceVisibilityData> { + _FORCE_INLINE_ virtual void _update_idx(InstanceVisibilityData &r_element, uint64_t p_idx) { + r_element.instance->visibility_index = p_idx; + if (r_element.instance->scenario && r_element.instance->array_index != -1) { + r_element.instance->scenario->instance_data[r_element.instance->array_index].visibility_index = p_idx; + } + } }; PagedArrayPool<InstanceBounds> instance_aabb_page_pool; PagedArrayPool<InstanceData> instance_data_page_pool; + PagedArrayPool<InstanceVisibilityData> instance_visibility_data_page_pool; struct Scenario { enum IndexerType { @@ -292,6 +319,8 @@ public: RID camera_effects; RID reflection_probe_shadow_atlas; RID reflection_atlas; + uint64_t used_viewport_visibility_bits; + Map<RID, uint64_t> viewport_visibility_masks; SelfList<Instance>::List instances; @@ -299,11 +328,13 @@ public: PagedArray<InstanceBounds> instance_aabbs; PagedArray<InstanceData> instance_data; + VisibilityArray instance_visibility; Scenario() { indexers[INDEXER_GEOMETRY].set_index(INDEXER_GEOMETRY); indexers[INDEXER_VOLUMES].set_index(INDEXER_VOLUMES); debug = RS::SCENARIO_DEBUG_DISABLED; + used_viewport_visibility_bits = 0; } }; @@ -326,6 +357,8 @@ public: virtual void scenario_set_reflection_atlas_size(RID p_scenario, int p_reflection_size, int p_reflection_count); virtual bool is_scenario(RID p_scenario) const; virtual RID scenario_get_environment(RID p_scenario); + virtual void scenario_add_viewport_visibility_mask(RID p_scenario, RID p_viewport); + virtual void scenario_remove_viewport_visibility_mask(RID p_scenario, RID p_viewport); /* INSTANCING API */ @@ -399,6 +432,12 @@ public: //scenario stuff DynamicBVH::ID indexer_id; int32_t array_index; + int32_t visibility_index = -1; + float visibility_range_begin; + float visibility_range_end; + float visibility_range_begin_margin; + float visibility_range_end_margin; + Instance *visibility_parent = nullptr; Scenario *scenario; SelfList<Instance> scenario_item; @@ -412,12 +451,6 @@ public: float extra_margin; ObjectID object_id; - float lod_begin; - float lod_end; - float lod_begin_hysteresis; - float lod_end_hysteresis; - RID lod_instance; - Vector<Color> lightmap_target_sh; //target is used for incrementally changing the SH over time, this avoids pops in some corner cases and when going interior <-> exterior uint64_t last_frame_pass; @@ -495,10 +528,10 @@ public: visible = true; - lod_begin = 0; - lod_end = 0; - lod_begin_hysteresis = 0; - lod_end_hysteresis = 0; + visibility_range_begin = 0; + visibility_range_end = 0; + visibility_range_begin_margin = 0; + visibility_range_end_margin = 0; last_frame_pass = 0; version = 1; @@ -537,6 +570,8 @@ public: Set<Instance *> reflection_probes; Set<Instance *> voxel_gi_instances; Set<Instance *> lightmap_captures; + Set<Instance *> visibility_dependencies; + uint32_t visibility_dependencies_depth = 0; InstanceGeometryData() { can_cast_shadows = true; @@ -717,7 +752,7 @@ public: PagedArray<Instance *> instance_cull_result; PagedArray<Instance *> instance_shadow_cull_result; - struct FrustumCullResult { + struct InstanceCullResult { PagedArray<RendererSceneRender::GeometryInstance *> geometry_instances; PagedArray<Instance *> lights; PagedArray<RID> light_instances; @@ -782,7 +817,7 @@ public: } } - void append_from(FrustumCullResult &p_cull_result) { + void append_from(InstanceCullResult &p_cull_result) { geometry_instances.merge_unordered(p_cull_result.geometry_instances); lights.merge_unordered(p_cull_result.lights); light_instances.merge_unordered(p_cull_result.light_instances); @@ -832,8 +867,8 @@ public: } }; - FrustumCullResult frustum_cull_result; - LocalVector<FrustumCullResult> frustum_cull_result_threads; + InstanceCullResult scene_cull_result; + LocalVector<InstanceCullResult> scene_cull_result_threads; RendererSceneRender::RenderShadowData render_shadow_data[MAX_UPDATE_SHADOWS]; uint32_t max_shadows_used = 0; @@ -866,6 +901,11 @@ public: virtual void instance_set_extra_visibility_margin(RID p_instance, real_t p_margin); + virtual void instance_set_visibility_parent(RID p_instance, RID p_parent_instance); + + void _update_instance_visibility_depth(Instance *p_instance); + void _update_instance_visibility_dependencies(Instance *p_instance); + // don't use these in a game! virtual Vector<ObjectID> instances_cull_aabb(const AABB &p_aabb, RID p_scenario = RID()) const; virtual Vector<ObjectID> instances_cull_ray(const Vector3 &p_from, const Vector3 &p_to, RID p_scenario = RID()) const; @@ -875,8 +915,8 @@ public: virtual void instance_geometry_set_cast_shadows_setting(RID p_instance, RS::ShadowCastingSetting p_shadow_casting_setting); virtual void instance_geometry_set_material_override(RID p_instance, RID p_material); - virtual void instance_geometry_set_draw_range(RID p_instance, float p_min, float p_max, float p_min_margin, float p_max_margin); - virtual void instance_geometry_set_as_instance_lod(RID p_instance, RID p_as_lod_of_instance); + virtual void instance_geometry_set_visibility_range(RID p_instance, float p_min, float p_max, float p_min_margin, float p_max_margin); + virtual void instance_geometry_set_lightmap(RID p_instance, RID p_lightmap, const Rect2 &p_lightmap_uv_scale, int p_slice_index); virtual void instance_geometry_set_lod_bias(RID p_instance, float p_lod_bias); @@ -937,6 +977,19 @@ public: Frustum frustum; } cull; + struct VisibilityCullData { + uint64_t viewport_mask; + Scenario *scenario; + Vector3 camera_position; + uint32_t cull_offset; + uint32_t cull_count; + }; + + void _visibility_cull_threaded(uint32_t p_thread, VisibilityCullData *cull_data); + void _visibility_cull(const VisibilityCullData &cull_data, uint64_t p_from, uint64_t p_to); + _FORCE_INLINE_ void _visibility_cull(const VisibilityCullData &cull_data, uint64_t p_idx); + _FORCE_INLINE_ int _visibility_range_check(InstanceVisibilityData &r_vis_data, const Vector3 &p_camera_pos, uint64_t p_viewport_mask); + struct CullData { Cull *cull; Scenario *scenario; @@ -946,17 +999,17 @@ public: Instance *render_reflection_probe; const RendererSceneOcclusionCull::HZBuffer *occlusion_buffer; const CameraMatrix *camera_matrix; + const VisibilityCullData *visibility_cull_data; }; - void _frustum_cull_threaded(uint32_t p_thread, CullData *cull_data); - void _frustum_cull(CullData &cull_data, FrustumCullResult &cull_result, uint64_t p_from, uint64_t p_to); + void _scene_cull_threaded(uint32_t p_thread, CullData *cull_data); + void _scene_cull(CullData &cull_data, InstanceCullResult &cull_result, uint64_t p_from, uint64_t p_to); bool _render_reflection_probe_step(Instance *p_instance, int p_step); - void _render_scene(const Transform3D &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_orthogonal, bool p_cam_vaspect, RID p_render_buffers, RID p_environment, RID p_force_camera_effects, uint32_t p_visible_layers, RID p_scenario, RID p_viewport, RID p_shadow_atlas, RID p_reflection_probe, int p_reflection_probe_pass, float p_screen_lod_threshold, bool p_using_shadows = true); + void _render_scene(const RendererSceneRender::CameraData *p_camera_data, RID p_render_buffers, RID p_environment, RID p_force_camera_effects, uint32_t p_visible_layers, RID p_scenario, RID p_viewport, RID p_shadow_atlas, RID p_reflection_probe, int p_reflection_probe_pass, float p_screen_lod_threshold, bool p_using_shadows = true); void render_empty_scene(RID p_render_buffers, RID p_scenario, RID p_shadow_atlas); - void render_camera(RID p_render_buffers, RID p_camera, RID p_scenario, RID p_viewport, Size2 p_viewport_size, float p_screen_lod_threshold, RID p_shadow_atlas); - void render_camera(RID p_render_buffers, Ref<XRInterface> &p_interface, XRInterface::Eyes p_eye, RID p_camera, RID p_scenario, RID p_viewport, Size2 p_viewport_size, float p_screen_lod_threshold, RID p_shadow_atlas); + void render_camera(RID p_render_buffers, RID p_camera, RID p_scenario, RID p_viewport, Size2 p_viewport_size, float p_screen_lod_threshold, RID p_shadow_atlas, Ref<XRInterface> &p_xr_interface); void update_dirty_instances(); void render_particle_colliders(); @@ -1054,7 +1107,7 @@ public: /* Render Buffers */ PASS0R(RID, render_buffers_create) - PASS7(render_buffers_configure, RID, RID, int, int, RS::ViewportMSAA, RS::ViewportScreenSpaceAA, bool) + PASS8(render_buffers_configure, RID, RID, int, int, RS::ViewportMSAA, RS::ViewportScreenSpaceAA, bool, uint32_t) PASS1(gi_set_use_half_resolution, bool) /* Shadow Atlas */ diff --git a/servers/rendering/renderer_scene_render.cpp b/servers/rendering/renderer_scene_render.cpp index f27bdc6798..3aa97f4084 100644 --- a/servers/rendering/renderer_scene_render.cpp +++ b/servers/rendering/renderer_scene_render.cpp @@ -29,3 +29,153 @@ /*************************************************************************/ #include "renderer_scene_render.h" + +void RendererSceneRender::CameraData::set_camera(const Transform3D p_transform, const CameraMatrix p_projection, bool p_is_ortogonal, bool p_vaspect) { + view_count = 1; + is_ortogonal = p_is_ortogonal; + vaspect = p_vaspect; + + main_transform = p_transform; + main_projection = p_projection; + + view_offset[0] = Transform3D(); + view_projection[0] = p_projection; +} + +void RendererSceneRender::CameraData::set_multiview_camera(uint32_t p_view_count, const Transform3D *p_transforms, const CameraMatrix *p_projections, bool p_is_ortogonal, bool p_vaspect) { + ERR_FAIL_COND_MSG(p_view_count != 2, "Incorrect view count for stereoscopic view"); + + view_count = p_view_count; + is_ortogonal = p_is_ortogonal; + vaspect = p_vaspect; + Vector<Plane> planes[2]; + + ///////////////////////////////////////////////////////////////////////////// + // Figure out our center transform + + // 1. obtain our planes + for (uint32_t v = 0; v < view_count; v++) { + planes[v] = p_projections[v].get_projection_planes(p_transforms[v]); + } + + // 2. average and normalize plane normals to obtain z vector, cross them to obtain y vector, and from there the x vector for combined camera basis. + Vector3 n0 = planes[0][CameraMatrix::PLANE_LEFT].normal; + Vector3 n1 = planes[1][CameraMatrix::PLANE_RIGHT].normal; + Vector3 z = (n0 + n1).normalized(); + Vector3 y = n0.cross(n1).normalized(); + Vector3 x = y.cross(z).normalized(); + y = z.cross(x).normalized(); + main_transform.basis.set(x, y, z); + + // 3. create a horizon plane with one of the eyes and the up vector as normal. + Plane horizon(p_transforms[0].origin, y); + + // 4. Intersect horizon, left and right to obtain the combined camera origin. + ERR_FAIL_COND_MSG( + !horizon.intersect_3(planes[0][CameraMatrix::PLANE_LEFT], planes[1][CameraMatrix::PLANE_RIGHT], &main_transform.origin), "Can't determine camera origin"); + + // handy to have the inverse of the transform we just build + Transform3D main_transform_inv = main_transform.inverse(); + + // 5. figure out far plane, this could use some improvement, we may have our far plane too close like this, not sure if this matters + Vector3 far_center = (planes[0][CameraMatrix::PLANE_FAR].center() + planes[1][CameraMatrix::PLANE_FAR].center()) * 0.5; + Plane far(far_center, -z); + + ///////////////////////////////////////////////////////////////////////////// + // Figure out our top/bottom planes + + // 6. Intersect far and left planes with top planes from both eyes, save the point with highest y as top_left. + Vector3 top_left, other; + ERR_FAIL_COND_MSG( + !far.intersect_3(planes[0][CameraMatrix::PLANE_LEFT], planes[0][CameraMatrix::PLANE_TOP], &top_left), "Can't determine left camera far/left/top vector"); + ERR_FAIL_COND_MSG( + !far.intersect_3(planes[1][CameraMatrix::PLANE_LEFT], planes[1][CameraMatrix::PLANE_TOP], &other), "Can't determine right camera far/left/top vector"); + if (y.dot(top_left) < y.dot(other)) { + top_left = other; + } + + // 7. Intersect far and left planes with bottom planes from both eyes, save the point with lowest y as bottom_left. + Vector3 bottom_left; + ERR_FAIL_COND_MSG( + !far.intersect_3(planes[0][CameraMatrix::PLANE_LEFT], planes[0][CameraMatrix::PLANE_BOTTOM], &bottom_left), "Can't determine left camera far/left/bottom vector"); + ERR_FAIL_COND_MSG( + !far.intersect_3(planes[1][CameraMatrix::PLANE_LEFT], planes[1][CameraMatrix::PLANE_BOTTOM], &other), "Can't determine right camera far/left/bottom vector"); + if (y.dot(other) < y.dot(bottom_left)) { + bottom_left = other; + } + + // 8. Intersect far and right planes with top planes from both eyes, save the point with highest y as top_right. + Vector3 top_right; + ERR_FAIL_COND_MSG( + !far.intersect_3(planes[0][CameraMatrix::PLANE_RIGHT], planes[0][CameraMatrix::PLANE_TOP], &top_right), "Can't determine left camera far/right/top vector"); + ERR_FAIL_COND_MSG( + !far.intersect_3(planes[1][CameraMatrix::PLANE_RIGHT], planes[1][CameraMatrix::PLANE_TOP], &other), "Can't determine right camera far/right/top vector"); + if (y.dot(top_right) < y.dot(other)) { + top_right = other; + } + + // 9. Intersect far and right planes with bottom planes from both eyes, save the point with lowest y as bottom_right. + Vector3 bottom_right; + ERR_FAIL_COND_MSG( + !far.intersect_3(planes[0][CameraMatrix::PLANE_RIGHT], planes[0][CameraMatrix::PLANE_BOTTOM], &bottom_right), "Can't determine left camera far/right/bottom vector"); + ERR_FAIL_COND_MSG( + !far.intersect_3(planes[1][CameraMatrix::PLANE_RIGHT], planes[1][CameraMatrix::PLANE_BOTTOM], &other), "Can't determine right camera far/right/bottom vector"); + if (y.dot(other) < y.dot(bottom_right)) { + bottom_right = other; + } + + // 10. Create top plane with these points: camera origin, top_left, top_right + Plane top(main_transform.origin, top_left, top_right); + + // 11. Create bottom plane with these points: camera origin, bottom_left, bottom_right + Plane bottom(main_transform.origin, bottom_left, bottom_right); + + ///////////////////////////////////////////////////////////////////////////// + // Figure out our near plane points + + // 12. Create a near plane using -camera z and the eye further along in that axis. + Plane near; + Vector3 neg_z = -z; + if (neg_z.dot(p_transforms[1].origin) < neg_z.dot(p_transforms[0].origin)) { + near = Plane(p_transforms[0].origin, neg_z); + } else { + near = Plane(p_transforms[1].origin, neg_z); + } + + // 13. Intersect near plane with bottm/left planes, to obtain min_vec then top/right to obtain max_vec + Vector3 min_vec; + ERR_FAIL_COND_MSG( + !near.intersect_3(bottom, planes[0][CameraMatrix::PLANE_LEFT], &min_vec), "Can't determine left camera near/left/bottom vector"); + ERR_FAIL_COND_MSG( + !near.intersect_3(bottom, planes[1][CameraMatrix::PLANE_LEFT], &other), "Can't determine right camera near/left/bottom vector"); + if (x.dot(other) < x.dot(min_vec)) { + min_vec = other; + } + + Vector3 max_vec; + ERR_FAIL_COND_MSG( + !near.intersect_3(top, planes[0][CameraMatrix::PLANE_RIGHT], &max_vec), "Can't determine left camera near/right/top vector"); + ERR_FAIL_COND_MSG( + !near.intersect_3(top, planes[1][CameraMatrix::PLANE_RIGHT], &other), "Can't determine right camera near/right/top vector"); + if (x.dot(max_vec) < x.dot(other)) { + max_vec = other; + } + + // 14. transform these points by the inverse camera to obtain local_min_vec and local_max_vec + Vector3 local_min_vec = main_transform_inv.xform(min_vec); + Vector3 local_max_vec = main_transform_inv.xform(max_vec); + + // 15. get x and y from these to obtain left, top, right bottom for the frustum. Get the distance from near plane to camera origin to obtain near, and the distance from the far plane to the camer origin to obtain far. + float z_near = -near.distance_to(main_transform.origin); + float z_far = -far.distance_to(main_transform.origin); + + // 16. Use this to build the combined camera matrix. + main_projection.set_frustum(local_min_vec.x, local_max_vec.x, local_min_vec.y, local_max_vec.y, z_near, z_far); + + ///////////////////////////////////////////////////////////////////////////// + // 3. Copy our view data + for (uint32_t v = 0; v < view_count; v++) { + view_offset[v] = p_transforms[v] * main_transform_inv; + view_projection[v] = p_projections[v] * CameraMatrix(view_offset[v]); + } +} diff --git a/servers/rendering/renderer_scene_render.h b/servers/rendering/renderer_scene_render.h index 5f3e6df4e3..3682122dd3 100644 --- a/servers/rendering/renderer_scene_render.h +++ b/servers/rendering/renderer_scene_render.h @@ -39,7 +39,8 @@ class RendererSceneRender { public: enum { MAX_DIRECTIONAL_LIGHTS = 8, - MAX_DIRECTIONAL_LIGHT_CASCADES = 4 + MAX_DIRECTIONAL_LIGHT_CASCADES = 4, + MAX_RENDER_VIEWS = 2 }; struct GeometryInstance { @@ -216,7 +217,24 @@ public: uint32_t positional_light_count; }; - virtual void render_scene(RID p_render_buffers, const Transform3D &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_ortogonal, const PagedArray<GeometryInstance *> &p_instances, const PagedArray<RID> &p_lights, const PagedArray<RID> &p_reflection_probes, const PagedArray<RID> &p_voxel_gi_instances, const PagedArray<RID> &p_decals, const PagedArray<RID> &p_lightmaps, RID p_environment, RID p_camera_effects, RID p_shadow_atlas, RID p_occluder_debug_tex, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass, float p_screen_lod_threshold, const RenderShadowData *p_render_shadows, int p_render_shadow_count, const RenderSDFGIData *p_render_sdfgi_regions, int p_render_sdfgi_region_count, const RenderSDFGIUpdateData *p_sdfgi_update_data = nullptr) = 0; + struct CameraData { + // flags + uint32_t view_count; + bool is_ortogonal; + bool vaspect; + + // Main/center projection + Transform3D main_transform; + CameraMatrix main_projection; + + Transform3D view_offset[RendererSceneRender::MAX_RENDER_VIEWS]; + CameraMatrix view_projection[RendererSceneRender::MAX_RENDER_VIEWS]; + + void set_camera(const Transform3D p_transform, const CameraMatrix p_projection, bool p_is_ortogonal, bool p_vaspect); + void set_multiview_camera(uint32_t p_view_count, const Transform3D *p_transforms, const CameraMatrix *p_projections, bool p_is_ortogonal, bool p_vaspect); + }; + + virtual void render_scene(RID p_render_buffers, const CameraData *p_camera_data, const PagedArray<GeometryInstance *> &p_instances, const PagedArray<RID> &p_lights, const PagedArray<RID> &p_reflection_probes, const PagedArray<RID> &p_voxel_gi_instances, const PagedArray<RID> &p_decals, const PagedArray<RID> &p_lightmaps, RID p_environment, RID p_camera_effects, RID p_shadow_atlas, RID p_occluder_debug_tex, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass, float p_screen_lod_threshold, const RenderShadowData *p_render_shadows, int p_render_shadow_count, const RenderSDFGIData *p_render_sdfgi_regions, int p_render_sdfgi_region_count, const RenderSDFGIUpdateData *p_sdfgi_update_data = nullptr) = 0; virtual void render_material(const Transform3D &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_ortogonal, const PagedArray<GeometryInstance *> &p_instances, RID p_framebuffer, const Rect2i &p_region) = 0; virtual void render_particle_collider_heightfield(RID p_collider, const Transform3D &p_transform, const PagedArray<GeometryInstance *> &p_instances) = 0; @@ -226,7 +244,7 @@ public: virtual void set_debug_draw_mode(RS::ViewportDebugDraw p_debug_draw) = 0; virtual RID render_buffers_create() = 0; - virtual void render_buffers_configure(RID p_render_buffers, RID p_render_target, int p_width, int p_height, RS::ViewportMSAA p_msaa, RS::ViewportScreenSpaceAA p_screen_space_aa, bool p_use_debanding) = 0; + virtual void render_buffers_configure(RID p_render_buffers, RID p_render_target, int p_width, int p_height, RS::ViewportMSAA p_msaa, RS::ViewportScreenSpaceAA p_screen_space_aa, bool p_use_debanding, uint32_t p_view_count) = 0; virtual void gi_set_use_half_resolution(bool p_enable) = 0; virtual void screen_space_roughness_limiter_set_active(bool p_enable, float p_amount, float p_limit) = 0; diff --git a/servers/rendering/renderer_storage.h b/servers/rendering/renderer_storage.h index 0b9b82cba0..e6b9ac15ff 100644 --- a/servers/rendering/renderer_storage.h +++ b/servers/rendering/renderer_storage.h @@ -590,7 +590,7 @@ public: virtual RID render_target_create() = 0; virtual void render_target_set_position(RID p_render_target, int p_x, int p_y) = 0; - virtual void render_target_set_size(RID p_render_target, int p_width, int p_height) = 0; + virtual void render_target_set_size(RID p_render_target, int p_width, int p_height, uint32_t p_view_count) = 0; virtual RID render_target_get_texture(RID p_render_target) = 0; virtual void render_target_set_external_texture(RID p_render_target, unsigned int p_texture_id) = 0; virtual void render_target_set_flag(RID p_render_target, RenderTargetFlags p_flag, bool p_value) = 0; diff --git a/servers/rendering/renderer_viewport.cpp b/servers/rendering/renderer_viewport.cpp index f97e24947d..34bdb15c62 100644 --- a/servers/rendering/renderer_viewport.cpp +++ b/servers/rendering/renderer_viewport.cpp @@ -71,11 +71,11 @@ static Transform2D _canvas_get_transform(RendererViewport::Viewport *p_viewport, return xf; } -void RendererViewport::_draw_3d(Viewport *p_viewport, XRInterface::Eyes p_eye) { +void RendererViewport::_draw_3d(Viewport *p_viewport) { RENDER_TIMESTAMP(">Begin Rendering 3D Scene"); Ref<XRInterface> xr_interface; - if (XRServer::get_singleton() != nullptr) { + if (p_viewport->use_xr && XRServer::get_singleton() != nullptr) { xr_interface = XRServer::get_singleton()->get_primary_interface(); } @@ -95,15 +95,12 @@ void RendererViewport::_draw_3d(Viewport *p_viewport, XRInterface::Eyes p_eye) { } float screen_lod_threshold = p_viewport->lod_threshold / float(p_viewport->size.width); - if (p_viewport->use_xr && xr_interface.is_valid()) { - RSG::scene->render_camera(p_viewport->render_buffers, xr_interface, p_eye, p_viewport->camera, p_viewport->scenario, p_viewport->self, p_viewport->size, screen_lod_threshold, p_viewport->shadow_atlas); - } else { - RSG::scene->render_camera(p_viewport->render_buffers, p_viewport->camera, p_viewport->scenario, p_viewport->self, p_viewport->size, screen_lod_threshold, p_viewport->shadow_atlas); - } + RSG::scene->render_camera(p_viewport->render_buffers, p_viewport->camera, p_viewport->scenario, p_viewport->self, p_viewport->size, screen_lod_threshold, p_viewport->shadow_atlas, xr_interface); + RENDER_TIMESTAMP("<End Rendering 3D Scene"); } -void RendererViewport::_draw_viewport(Viewport *p_viewport, XRInterface::Eyes p_eye) { +void RendererViewport::_draw_viewport(Viewport *p_viewport, uint32_t p_view_count) { if (p_viewport->measure_render_time) { String rt_id = "vp_begin_" + itos(p_viewport->self.get_id()); RSG::storage->capture_timestamp(rt_id); @@ -139,13 +136,13 @@ void RendererViewport::_draw_viewport(Viewport *p_viewport, XRInterface::Eyes p_ if ((scenario_draw_canvas_bg || can_draw_3d) && !p_viewport->render_buffers.is_valid()) { //wants to draw 3D but there is no render buffer, create p_viewport->render_buffers = RSG::scene->render_buffers_create(); - RSG::scene->render_buffers_configure(p_viewport->render_buffers, p_viewport->render_target, p_viewport->size.width, p_viewport->size.height, p_viewport->msaa, p_viewport->screen_space_aa, p_viewport->use_debanding); + RSG::scene->render_buffers_configure(p_viewport->render_buffers, p_viewport->render_target, p_viewport->size.width, p_viewport->size.height, p_viewport->msaa, p_viewport->screen_space_aa, p_viewport->use_debanding, p_view_count); } RSG::storage->render_target_request_clear(p_viewport->render_target, bgcolor); if (!scenario_draw_canvas_bg && can_draw_3d) { - _draw_3d(p_viewport, p_eye); + _draw_3d(p_viewport); } if (!p_viewport->hide_canvas) { @@ -392,7 +389,7 @@ void RendererViewport::_draw_viewport(Viewport *p_viewport, XRInterface::Eyes p_ if (!can_draw_3d) { RSG::scene->render_empty_scene(p_viewport->render_buffers, p_viewport->scenario, p_viewport->shadow_atlas); } else { - _draw_3d(p_viewport, p_eye); + _draw_3d(p_viewport); } scenario_draw_canvas_bg = false; } @@ -433,7 +430,7 @@ void RendererViewport::_draw_viewport(Viewport *p_viewport, XRInterface::Eyes p_ if (!can_draw_3d) { RSG::scene->render_empty_scene(p_viewport->render_buffers, p_viewport->scenario, p_viewport->shadow_atlas); } else { - _draw_3d(p_viewport, p_eye); + _draw_3d(p_viewport); } scenario_draw_canvas_bg = false; @@ -444,7 +441,7 @@ void RendererViewport::_draw_viewport(Viewport *p_viewport, XRInterface::Eyes p_ if (!can_draw_3d) { RSG::scene->render_empty_scene(p_viewport->render_buffers, p_viewport->scenario, p_viewport->shadow_atlas); } else { - _draw_3d(p_viewport, p_eye); + _draw_3d(p_viewport); } } } @@ -481,7 +478,7 @@ void RendererViewport::draw_viewports() { //sort viewports active_viewports.sort_custom<ViewportSort>(); - Map<DisplayServer::WindowID, Vector<RendererCompositor::BlitToScreen>> blit_to_screen_list; + Map<DisplayServer::WindowID, Vector<BlitToScreen>> blit_to_screen_list; //draw viewports RENDER_TIMESTAMP(">Render Viewports"); @@ -535,52 +532,54 @@ void RendererViewport::draw_viewports() { RENDER_TIMESTAMP(">Rendering Viewport " + itos(i)); RSG::storage->render_target_set_as_unused(vp->render_target); -#if 0 - // TODO fix up this code after we change our commit_for_eye to accept our new render targets - if (vp->use_xr && xr_interface.is_valid()) { - // override our size, make sure it matches our required size + // override our size, make sure it matches our required size and is created as a stereo target vp->size = xr_interface->get_render_targetsize(); - RSG::storage->render_target_set_size(vp->render_target, vp->size.x, vp->size.y); + uint32_t view_count = xr_interface->get_view_count(); + RSG::storage->render_target_set_size(vp->render_target, vp->size.x, vp->size.y, view_count); - // render mono or left eye first - XRInterface::Eyes leftOrMono = xr_interface->is_stereo() ? XRInterface::EYE_LEFT : XRInterface::EYE_MONO; - - // check for an external texture destination for our left eye/mono - // TODO investigate how we're going to make external textures work - RSG::storage->render_target_set_external_texture(vp->render_target, xr_interface->get_external_texture_for_eye(leftOrMono)); + // check for an external texture destination (disabled for now, not yet supported) + // RSG::storage->render_target_set_external_texture(vp->render_target, xr_interface->get_external_texture_for_eye(leftOrMono)); + RSG::storage->render_target_set_external_texture(vp->render_target, 0); - // set our render target as current - RSG::rasterizer->set_current_render_target(vp->render_target); + // render... + RSG::scene->set_debug_draw_mode(vp->debug_draw); + RSG::storage->render_info_begin_capture(); - // and draw left eye/mono - _draw_viewport(vp, leftOrMono); - xr_interface->commit_for_eye(leftOrMono, vp->render_target, vp->viewport_to_screen_rect); + // and draw viewport + _draw_viewport(vp, view_count); - // render right eye - if (leftOrMono == XRInterface::EYE_LEFT) { - // check for an external texture destination for our right eye - RSG::storage->render_target_set_external_texture(vp->render_target, xr_interface->get_external_texture_for_eye(XRInterface::EYE_RIGHT)); + // measure + RSG::storage->render_info_end_capture(); + vp->render_info[RS::VIEWPORT_RENDER_INFO_OBJECTS_IN_FRAME] = RSG::storage->get_captured_render_info(RS::INFO_OBJECTS_IN_FRAME); + vp->render_info[RS::VIEWPORT_RENDER_INFO_VERTICES_IN_FRAME] = RSG::storage->get_captured_render_info(RS::INFO_VERTICES_IN_FRAME); + vp->render_info[RS::VIEWPORT_RENDER_INFO_MATERIAL_CHANGES_IN_FRAME] = RSG::storage->get_captured_render_info(RS::INFO_MATERIAL_CHANGES_IN_FRAME); + vp->render_info[RS::VIEWPORT_RENDER_INFO_SHADER_CHANGES_IN_FRAME] = RSG::storage->get_captured_render_info(RS::INFO_SHADER_CHANGES_IN_FRAME); + vp->render_info[RS::VIEWPORT_RENDER_INFO_SURFACE_CHANGES_IN_FRAME] = RSG::storage->get_captured_render_info(RS::INFO_SURFACE_CHANGES_IN_FRAME); + vp->render_info[RS::VIEWPORT_RENDER_INFO_DRAW_CALLS_IN_FRAME] = RSG::storage->get_captured_render_info(RS::INFO_DRAW_CALLS_IN_FRAME); - // commit for eye may have changed the render target - RSG::rasterizer->set_current_render_target(vp->render_target); + // commit our eyes + Vector<BlitToScreen> blits = xr_interface->commit_views(vp->render_target, vp->viewport_to_screen_rect); + if (vp->viewport_to_screen != DisplayServer::INVALID_WINDOW_ID && blits.size() > 0) { + if (!blit_to_screen_list.has(vp->viewport_to_screen)) { + blit_to_screen_list[vp->viewport_to_screen] = Vector<BlitToScreen>(); + } - _draw_viewport(vp, XRInterface::EYE_RIGHT); - xr_interface->commit_for_eye(XRInterface::EYE_RIGHT, vp->render_target, vp->viewport_to_screen_rect); + for (int b = 0; b < blits.size(); b++) { + blit_to_screen_list[vp->viewport_to_screen].push_back(blits[b]); + } } // and for our frame timing, mark when we've finished committing our eyes XRServer::get_singleton()->_mark_commit(); } else { -#endif - { RSG::storage->render_target_set_external_texture(vp->render_target, 0); RSG::scene->set_debug_draw_mode(vp->debug_draw); RSG::storage->render_info_begin_capture(); // render standard mono camera - _draw_viewport(vp); + _draw_viewport(vp, 1); RSG::storage->render_info_end_capture(); vp->render_info[RS::VIEWPORT_RENDER_INFO_OBJECTS_IN_FRAME] = RSG::storage->get_captured_render_info(RS::INFO_OBJECTS_IN_FRAME); @@ -592,7 +591,7 @@ void RendererViewport::draw_viewports() { if (vp->viewport_to_screen != DisplayServer::INVALID_WINDOW_ID && (!vp->viewport_render_direct_to_screen || !RSG::rasterizer->is_low_end())) { //copy to screen if set as such - RendererCompositor::BlitToScreen blit; + BlitToScreen blit; blit.render_target = vp->render_target; if (vp->viewport_to_screen_rect != Rect2()) { blit.rect = vp->viewport_to_screen_rect; @@ -602,7 +601,7 @@ void RendererViewport::draw_viewports() { } if (!blit_to_screen_list.has(vp->viewport_to_screen)) { - blit_to_screen_list[vp->viewport_to_screen] = Vector<RendererCompositor::BlitToScreen>(); + blit_to_screen_list[vp->viewport_to_screen] = Vector<BlitToScreen>(); } blit_to_screen_list[vp->viewport_to_screen].push_back(blit); @@ -621,7 +620,7 @@ void RendererViewport::draw_viewports() { //this needs to be called to make screen swapping more efficient RSG::rasterizer->prepare_for_blitting_render_targets(); - for (Map<int, Vector<RendererCompositor::BlitToScreen>>::Element *E = blit_to_screen_list.front(); E; E = E->next()) { + for (Map<int, Vector<BlitToScreen>>::Element *E = blit_to_screen_list.front(); E; E = E->next()) { RSG::rasterizer->blit_render_targets_to_screen(E->key(), E->get().ptr(), E->get().size()); } } @@ -646,7 +645,29 @@ void RendererViewport::viewport_set_use_xr(RID p_viewport, bool p_use_xr) { Viewport *viewport = viewport_owner.getornull(p_viewport); ERR_FAIL_COND(!viewport); + if (viewport->use_xr == p_use_xr) { + return; + } + viewport->use_xr = p_use_xr; + if (viewport->render_buffers.is_valid()) { + RSG::scene->render_buffers_configure(viewport->render_buffers, viewport->render_target, viewport->size.width, viewport->size.height, viewport->msaa, viewport->screen_space_aa, viewport->use_debanding, viewport->get_view_count()); + } +} + +uint32_t RendererViewport::Viewport::get_view_count() { + uint32_t view_count = 1; + + if (use_xr && XRServer::get_singleton() != nullptr) { + Ref<XRInterface> xr_interface; + + xr_interface = XRServer::get_singleton()->get_primary_interface(); + if (xr_interface.is_valid()) { + view_count = xr_interface->get_view_count(); + } + } + + return view_count; } void RendererViewport::viewport_set_size(RID p_viewport, int p_width, int p_height) { @@ -656,13 +677,14 @@ void RendererViewport::viewport_set_size(RID p_viewport, int p_width, int p_heig ERR_FAIL_COND(!viewport); viewport->size = Size2(p_width, p_height); - RSG::storage->render_target_set_size(viewport->render_target, p_width, p_height); + uint32_t view_count = viewport->get_view_count(); + RSG::storage->render_target_set_size(viewport->render_target, p_width, p_height, view_count); if (viewport->render_buffers.is_valid()) { if (p_width == 0 || p_height == 0) { RSG::scene->free(viewport->render_buffers); viewport->render_buffers = RID(); } else { - RSG::scene->render_buffers_configure(viewport->render_buffers, viewport->render_target, viewport->size.width, viewport->size.height, viewport->msaa, viewport->screen_space_aa, viewport->use_debanding); + RSG::scene->render_buffers_configure(viewport->render_buffers, viewport->render_target, viewport->size.width, viewport->size.height, viewport->msaa, viewport->screen_space_aa, viewport->use_debanding, view_count); } } @@ -704,7 +726,7 @@ void RendererViewport::viewport_attach_to_screen(RID p_viewport, const Rect2 &p_ // If using GLES2 we can optimize this operation by rendering directly to system_fbo // instead of rendering to fbo and copying to system_fbo after if (RSG::rasterizer->is_low_end() && viewport->viewport_render_direct_to_screen) { - RSG::storage->render_target_set_size(viewport->render_target, p_rect.size.x, p_rect.size.y); + RSG::storage->render_target_set_size(viewport->render_target, p_rect.size.x, p_rect.size.y, viewport->get_view_count()); RSG::storage->render_target_set_position(viewport->render_target, p_rect.position.x, p_rect.position.y); } @@ -714,7 +736,7 @@ void RendererViewport::viewport_attach_to_screen(RID p_viewport, const Rect2 &p_ // if render_direct_to_screen was used, reset size and position if (RSG::rasterizer->is_low_end() && viewport->viewport_render_direct_to_screen) { RSG::storage->render_target_set_position(viewport->render_target, 0, 0); - RSG::storage->render_target_set_size(viewport->render_target, viewport->size.x, viewport->size.y); + RSG::storage->render_target_set_size(viewport->render_target, viewport->size.x, viewport->size.y, viewport->get_view_count()); } viewport->viewport_to_screen_rect = Rect2(); @@ -733,7 +755,7 @@ void RendererViewport::viewport_set_render_direct_to_screen(RID p_viewport, bool // if disabled, reset render_target size and position if (!p_enable) { RSG::storage->render_target_set_position(viewport->render_target, 0, 0); - RSG::storage->render_target_set_size(viewport->render_target, viewport->size.x, viewport->size.y); + RSG::storage->render_target_set_size(viewport->render_target, viewport->size.x, viewport->size.y, viewport->get_view_count()); } RSG::storage->render_target_set_flag(viewport->render_target, RendererStorage::RENDER_TARGET_DIRECT_TO_SCREEN, p_enable); @@ -741,7 +763,7 @@ void RendererViewport::viewport_set_render_direct_to_screen(RID p_viewport, bool // if attached to screen already, setup screen size and position, this needs to happen after setting flag to avoid an unnecessary buffer allocation if (RSG::rasterizer->is_low_end() && viewport->viewport_to_screen_rect != Rect2() && p_enable) { - RSG::storage->render_target_set_size(viewport->render_target, viewport->viewport_to_screen_rect.size.x, viewport->viewport_to_screen_rect.size.y); + RSG::storage->render_target_set_size(viewport->render_target, viewport->viewport_to_screen_rect.size.x, viewport->viewport_to_screen_rect.size.y, viewport->get_view_count()); RSG::storage->render_target_set_position(viewport->render_target, viewport->viewport_to_screen_rect.position.x, viewport->viewport_to_screen_rect.position.y); } } @@ -802,6 +824,10 @@ void RendererViewport::viewport_set_scenario(RID p_viewport, RID p_scenario) { Viewport *viewport = viewport_owner.getornull(p_viewport); ERR_FAIL_COND(!viewport); + if (viewport->scenario.is_valid()) { + RSG::scene->scenario_remove_viewport_visibility_mask(viewport->scenario, p_viewport); + } + viewport->scenario = p_scenario; if (viewport->use_occlusion_culling) { RendererSceneOcclusionCull::get_singleton()->buffer_set_scenario(p_viewport, p_scenario); @@ -892,7 +918,7 @@ void RendererViewport::viewport_set_msaa(RID p_viewport, RS::ViewportMSAA p_msaa } viewport->msaa = p_msaa; if (viewport->render_buffers.is_valid()) { - RSG::scene->render_buffers_configure(viewport->render_buffers, viewport->render_target, viewport->size.width, viewport->size.height, p_msaa, viewport->screen_space_aa, viewport->use_debanding); + RSG::scene->render_buffers_configure(viewport->render_buffers, viewport->render_target, viewport->size.width, viewport->size.height, p_msaa, viewport->screen_space_aa, viewport->use_debanding, viewport->get_view_count()); } } @@ -905,7 +931,7 @@ void RendererViewport::viewport_set_screen_space_aa(RID p_viewport, RS::Viewport } viewport->screen_space_aa = p_mode; if (viewport->render_buffers.is_valid()) { - RSG::scene->render_buffers_configure(viewport->render_buffers, viewport->render_target, viewport->size.width, viewport->size.height, viewport->msaa, p_mode, viewport->use_debanding); + RSG::scene->render_buffers_configure(viewport->render_buffers, viewport->render_target, viewport->size.width, viewport->size.height, viewport->msaa, p_mode, viewport->use_debanding, viewport->get_view_count()); } } @@ -918,7 +944,7 @@ void RendererViewport::viewport_set_use_debanding(RID p_viewport, bool p_use_deb } viewport->use_debanding = p_use_debanding; if (viewport->render_buffers.is_valid()) { - RSG::scene->render_buffers_configure(viewport->render_buffers, viewport->render_target, viewport->size.width, viewport->size.height, viewport->msaa, viewport->screen_space_aa, p_use_debanding); + RSG::scene->render_buffers_configure(viewport->render_buffers, viewport->render_target, viewport->size.width, viewport->size.height, viewport->msaa, viewport->screen_space_aa, p_use_debanding, viewport->get_view_count()); } } diff --git a/servers/rendering/renderer_viewport.h b/servers/rendering/renderer_viewport.h index 5c372e8c9a..ffda9ad8f0 100644 --- a/servers/rendering/renderer_viewport.h +++ b/servers/rendering/renderer_viewport.h @@ -164,6 +164,8 @@ public: time_gpu_begin = 0; time_gpu_end = 0; } + + uint32_t get_view_count(); }; HashMap<String, RID> timestamp_vp_map; @@ -187,8 +189,8 @@ public: Vector<Viewport *> active_viewports; private: - void _draw_3d(Viewport *p_viewport, XRInterface::Eyes p_eye); - void _draw_viewport(Viewport *p_viewport, XRInterface::Eyes p_eye = XRInterface::EYE_MONO); + void _draw_3d(Viewport *p_viewport); + void _draw_viewport(Viewport *p_viewport, uint32_t p_view_count = 1); int occlusion_rays_per_thread = 512; diff --git a/servers/rendering/rendering_device.h b/servers/rendering/rendering_device.h index 4dcb9b963e..c13dc01dd7 100644 --- a/servers/rendering/rendering_device.h +++ b/servers/rendering/rendering_device.h @@ -516,11 +516,11 @@ public: typedef int64_t FramebufferFormatID; // This ID is warranted to be unique for the same formats, does not need to be freed - virtual FramebufferFormatID framebuffer_format_create(const Vector<AttachmentFormat> &p_format) = 0; + virtual FramebufferFormatID framebuffer_format_create(const Vector<AttachmentFormat> &p_format, uint32_t p_view_count = 1) = 0; virtual FramebufferFormatID framebuffer_format_create_empty(TextureSamples p_samples = TEXTURE_SAMPLES_1) = 0; virtual TextureSamples framebuffer_format_get_texture_samples(FramebufferFormatID p_format) = 0; - virtual RID framebuffer_create(const Vector<RID> &p_texture_attachments, FramebufferFormatID p_format_check = INVALID_ID) = 0; + virtual RID framebuffer_create(const Vector<RID> &p_texture_attachments, FramebufferFormatID p_format_check = INVALID_ID, uint32_t p_view_count = 1) = 0; virtual RID framebuffer_create_empty(const Size2i &p_size, TextureSamples p_samples = TEXTURE_SAMPLES_1, FramebufferFormatID p_format_check = INVALID_ID) = 0; virtual FramebufferFormatID framebuffer_get_format(RID p_framebuffer) = 0; diff --git a/servers/rendering/rendering_server_default.h b/servers/rendering/rendering_server_default.h index 6476d7c75d..e4d319ed6c 100644 --- a/servers/rendering/rendering_server_default.h +++ b/servers/rendering/rendering_server_default.h @@ -726,6 +726,7 @@ public: FUNC2(instance_set_exterior, RID, bool) FUNC2(instance_set_extra_visibility_margin, RID, real_t) + FUNC2(instance_set_visibility_parent, RID, RID) // don't use these in a game! FUNC2RC(Vector<ObjectID>, instances_cull_aabb, const AABB &, RID) @@ -736,8 +737,7 @@ public: FUNC2(instance_geometry_set_cast_shadows_setting, RID, ShadowCastingSetting) FUNC2(instance_geometry_set_material_override, RID, RID) - FUNC5(instance_geometry_set_draw_range, RID, float, float, float, float) - FUNC2(instance_geometry_set_as_instance_lod, RID, RID) + FUNC5(instance_geometry_set_visibility_range, RID, float, float, float, float) FUNC4(instance_geometry_set_lightmap, RID, RID, const Rect2 &, int) FUNC2(instance_geometry_set_lod_bias, RID, float) diff --git a/servers/rendering/shader_types.cpp b/servers/rendering/shader_types.cpp index 35f1b05796..c4e7511374 100644 --- a/servers/rendering/shader_types.cpp +++ b/servers/rendering/shader_types.cpp @@ -92,6 +92,10 @@ ShaderTypes::ShaderTypes() { shader_modes[RS::SHADER_SPATIAL].functions["vertex"].built_ins["VIEWPORT_SIZE"] = constt(ShaderLanguage::TYPE_VEC2); shader_modes[RS::SHADER_SPATIAL].functions["vertex"].built_ins["OUTPUT_IS_SRGB"] = constt(ShaderLanguage::TYPE_BOOL); + shader_modes[RS::SHADER_SPATIAL].functions["vertex"].built_ins["VIEW_INDEX"] = constt(ShaderLanguage::TYPE_INT); + shader_modes[RS::SHADER_SPATIAL].functions["vertex"].built_ins["VIEW_MONO_LEFT"] = constt(ShaderLanguage::TYPE_INT); + shader_modes[RS::SHADER_SPATIAL].functions["vertex"].built_ins["VIEW_RIGHT"] = constt(ShaderLanguage::TYPE_INT); + shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["VERTEX"] = constt(ShaderLanguage::TYPE_VEC3); shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["FRAGCOORD"] = constt(ShaderLanguage::TYPE_VEC4); shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["FRONT_FACING"] = constt(ShaderLanguage::TYPE_BOOL); @@ -131,6 +135,10 @@ ShaderTypes::ShaderTypes() { shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["SCREEN_UV"] = constt(ShaderLanguage::TYPE_VEC2); shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["POINT_COORD"] = constt(ShaderLanguage::TYPE_VEC2); + shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["VIEW_INDEX"] = constt(ShaderLanguage::TYPE_INT); + shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["VIEW_MONO_LEFT"] = constt(ShaderLanguage::TYPE_INT); + shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["VIEW_RIGHT"] = constt(ShaderLanguage::TYPE_INT); + shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["OUTPUT_IS_SRGB"] = constt(ShaderLanguage::TYPE_BOOL); shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["WORLD_MATRIX"] = constt(ShaderLanguage::TYPE_MAT4); diff --git a/servers/rendering_server.cpp b/servers/rendering_server.cpp index 88ddbd1bce..f25f255321 100644 --- a/servers/rendering_server.cpp +++ b/servers/rendering_server.cpp @@ -1725,11 +1725,11 @@ void RenderingServer::_bind_methods() { ClassDB::bind_method(D_METHOD("instance_attach_skeleton", "instance", "skeleton"), &RenderingServer::instance_attach_skeleton); ClassDB::bind_method(D_METHOD("instance_set_exterior", "instance", "enabled"), &RenderingServer::instance_set_exterior); ClassDB::bind_method(D_METHOD("instance_set_extra_visibility_margin", "instance", "margin"), &RenderingServer::instance_set_extra_visibility_margin); + ClassDB::bind_method(D_METHOD("instance_set_visibility_parent", "instance", "parent"), &RenderingServer::instance_set_visibility_parent); ClassDB::bind_method(D_METHOD("instance_geometry_set_flag", "instance", "flag", "enabled"), &RenderingServer::instance_geometry_set_flag); ClassDB::bind_method(D_METHOD("instance_geometry_set_cast_shadows_setting", "instance", "shadow_casting_setting"), &RenderingServer::instance_geometry_set_cast_shadows_setting); ClassDB::bind_method(D_METHOD("instance_geometry_set_material_override", "instance", "material"), &RenderingServer::instance_geometry_set_material_override); - ClassDB::bind_method(D_METHOD("instance_geometry_set_draw_range", "instance", "min", "max", "min_margin", "max_margin"), &RenderingServer::instance_geometry_set_draw_range); - ClassDB::bind_method(D_METHOD("instance_geometry_set_as_instance_lod", "instance", "as_lod_of_instance"), &RenderingServer::instance_geometry_set_as_instance_lod); + ClassDB::bind_method(D_METHOD("instance_geometry_set_visibility_range", "instance", "min", "max", "min_margin", "max_margin"), &RenderingServer::instance_geometry_set_visibility_range); ClassDB::bind_method(D_METHOD("instances_cull_aabb", "aabb", "scenario"), &RenderingServer::_instances_cull_aabb_bind, DEFVAL(RID())); ClassDB::bind_method(D_METHOD("instances_cull_ray", "from", "to", "scenario"), &RenderingServer::_instances_cull_ray_bind, DEFVAL(RID())); @@ -2420,6 +2420,8 @@ RenderingServer::RenderingServer() { GLOBAL_DEF("rendering/limits/cluster_builder/max_clustered_elements", 512); ProjectSettings::get_singleton()->set_custom_property_info("rendering/limits/cluster_builder/max_clustered_elements", PropertyInfo(Variant::FLOAT, "rendering/limits/cluster_builder/max_clustered_elements", PROPERTY_HINT_RANGE, "32,8192,1")); + + GLOBAL_DEF_RST("rendering/xr/enabled", false); } RenderingServer::~RenderingServer() { diff --git a/servers/rendering_server.h b/servers/rendering_server.h index 3247717e98..1806f1da18 100644 --- a/servers/rendering_server.h +++ b/servers/rendering_server.h @@ -1172,6 +1172,7 @@ public: virtual void instance_set_exterior(RID p_instance, bool p_enabled) = 0; virtual void instance_set_extra_visibility_margin(RID p_instance, real_t p_margin) = 0; + virtual void instance_set_visibility_parent(RID p_instance, RID p_parent_instance) = 0; // don't use these in a game! virtual Vector<ObjectID> instances_cull_aabb(const AABB &p_aabb, RID p_scenario = RID()) const = 0; @@ -1201,8 +1202,7 @@ public: virtual void instance_geometry_set_cast_shadows_setting(RID p_instance, ShadowCastingSetting p_shadow_casting_setting) = 0; virtual void instance_geometry_set_material_override(RID p_instance, RID p_material) = 0; - virtual void instance_geometry_set_draw_range(RID p_instance, float p_min, float p_max, float p_min_margin, float p_max_margin) = 0; - virtual void instance_geometry_set_as_instance_lod(RID p_instance, RID p_as_lod_of_instance) = 0; + virtual void instance_geometry_set_visibility_range(RID p_instance, float p_min, float p_max, float p_min_margin, float p_max_margin) = 0; virtual void instance_geometry_set_lightmap(RID p_instance, RID p_lightmap, const Rect2 &p_lightmap_uv_scale, int p_lightmap_slice) = 0; virtual void instance_geometry_set_lod_bias(RID p_instance, float p_lod_bias) = 0; diff --git a/servers/xr/xr_interface.cpp b/servers/xr/xr_interface.cpp index 9148631899..09e8e12f8b 100644 --- a/servers/xr/xr_interface.cpp +++ b/servers/xr/xr_interface.cpp @@ -29,6 +29,7 @@ /*************************************************************************/ #include "xr_interface.h" +#include "servers/rendering/renderer_compositor.h" void XRInterface::_bind_methods() { ClassDB::bind_method(D_METHOD("get_name"), &XRInterface::get_name); @@ -45,7 +46,7 @@ void XRInterface::_bind_methods() { ClassDB::bind_method(D_METHOD("get_tracking_status"), &XRInterface::get_tracking_status); ClassDB::bind_method(D_METHOD("get_render_targetsize"), &XRInterface::get_render_targetsize); - ClassDB::bind_method(D_METHOD("is_stereo"), &XRInterface::is_stereo); + ClassDB::bind_method(D_METHOD("get_view_count"), &XRInterface::get_view_count); ADD_GROUP("Interface", "interface_"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "interface_is_primary"), "set_is_primary", "is_primary"); diff --git a/servers/xr/xr_interface.h b/servers/xr/xr_interface.h index bf026ed773..6031bd7003 100644 --- a/servers/xr/xr_interface.h +++ b/servers/xr/xr_interface.h @@ -35,6 +35,9 @@ #include "core/os/thread_safe.h" #include "servers/xr_server.h" +// forward declaration +struct BlitToScreen; + /** @author Bastiaan Olij <mux213@gmail.com> @@ -105,17 +108,22 @@ public: /** rendering and internal **/ virtual Size2 get_render_targetsize() = 0; /* returns the recommended render target size per eye for this device */ - virtual bool is_stereo() = 0; /* returns true if this interface requires stereo rendering (for VR HMDs) or mono rendering (for mobile AR) */ - virtual Transform3D get_transform_for_eye(XRInterface::Eyes p_eye, const Transform3D &p_cam_transform) = 0; /* get each eyes camera transform, also implement EYE_MONO */ - virtual CameraMatrix get_projection_for_eye(XRInterface::Eyes p_eye, real_t p_aspect, real_t p_z_near, real_t p_z_far) = 0; /* get each eyes projection matrix */ - virtual unsigned int get_external_texture_for_eye(XRInterface::Eyes p_eye); /* if applicable return external texture to render to */ - virtual void commit_for_eye(XRInterface::Eyes p_eye, RID p_render_target, const Rect2 &p_screen_rect) = 0; /* output the left or right eye */ + virtual uint32_t get_view_count() = 0; /* returns the view count we need (1 is monoscopic, 2 is stereoscopic but can be more) */ + virtual Transform3D get_camera_transform() = 0; /* returns the position of our camera for updating our camera node. For monoscopic this is equal to the views transform, for stereoscopic this should be an average */ + virtual Transform3D get_transform_for_view(uint32_t p_view, const Transform3D &p_cam_transform) = 0; /* get each views transform */ + virtual CameraMatrix get_projection_for_view(uint32_t p_view, real_t p_aspect, real_t p_z_near, real_t p_z_far) = 0; /* get each view projection matrix */ + + virtual Vector<BlitToScreen> commit_views(RID p_render_target, const Rect2 &p_screen_rect) = 0; /* commit rendered views to the XR interface */ virtual void process() = 0; virtual void notification(int p_what) = 0; XRInterface(); ~XRInterface(); + + // deprecated + virtual unsigned int get_external_texture_for_eye(XRInterface::Eyes p_eye); /* if applicable return external texture to render to */ + virtual void commit_for_eye(XRInterface::Eyes p_eye, RID p_render_target, const Rect2 &p_screen_rect) = 0; /* output the left or right eye */ }; VARIANT_ENUM_CAST(XRInterface::Capabilities); diff --git a/servers/xr_server.cpp b/servers/xr_server.cpp index 3122ee685a..b12fff319d 100644 --- a/servers/xr_server.cpp +++ b/servers/xr_server.cpp @@ -119,7 +119,7 @@ void XRServer::center_on_hmd(RotationMode p_rotation_mode, bool p_keep_height) { reference_frame = Transform3D(); // requesting our EYE_MONO transform should return our current HMD position - Transform3D new_reference_frame = primary_interface->get_transform_for_eye(XRInterface::EYE_MONO, Transform3D()); + Transform3D new_reference_frame = primary_interface->get_camera_transform(); // remove our tilt if (p_rotation_mode == 1) { @@ -148,7 +148,7 @@ void XRServer::center_on_hmd(RotationMode p_rotation_mode, bool p_keep_height) { Transform3D XRServer::get_hmd_transform() { Transform3D hmd_transform; if (primary_interface != nullptr) { - hmd_transform = primary_interface->get_transform_for_eye(XRInterface::EYE_MONO, hmd_transform); + hmd_transform = primary_interface->get_camera_transform(); }; return hmd_transform; }; |