diff options
76 files changed, 511 insertions, 267 deletions
diff --git a/core/core_bind.cpp b/core/core_bind.cpp index 9a3234d4a2..8d03f35617 100644 --- a/core/core_bind.cpp +++ b/core/core_bind.cpp @@ -623,7 +623,6 @@ void OS::_bind_methods() { // Those default values need to be specified for the docs generator, // to avoid using values from the documentation writer's own OS instance. - ADD_PROPERTY_DEFAULT("exit_code", 0); ADD_PROPERTY_DEFAULT("low_processor_usage_mode", false); ADD_PROPERTY_DEFAULT("low_processor_usage_mode_sleep_usec", 6900); diff --git a/core/math/aabb.h b/core/math/aabb.h index 2eaaafa27d..3d19410ddf 100644 --- a/core/math/aabb.h +++ b/core/math/aabb.h @@ -41,7 +41,7 @@ */ class Variant; -class AABB { +class _NO_DISCARD_ AABB { public: Vector3 position; Vector3 size; diff --git a/core/math/basis.h b/core/math/basis.h index 709f2cb3cf..802da82089 100644 --- a/core/math/basis.h +++ b/core/math/basis.h @@ -34,7 +34,7 @@ #include "core/math/quaternion.h" #include "core/math/vector3.h" -class Basis { +class _NO_DISCARD_ Basis { private: void _set_diagonal(const Vector3 &p_diag); diff --git a/core/math/color.h b/core/math/color.h index 6c09f7941c..72a4a5f8be 100644 --- a/core/math/color.h +++ b/core/math/color.h @@ -34,7 +34,7 @@ #include "core/math/math_funcs.h" #include "core/string/ustring.h" -struct Color { +struct _NO_DISCARD_ Color { union { struct { float r; diff --git a/core/math/face3.h b/core/math/face3.h index 5a34858ccb..3dbbca09e0 100644 --- a/core/math/face3.h +++ b/core/math/face3.h @@ -36,7 +36,7 @@ #include "core/math/transform_3d.h" #include "core/math/vector3.h" -class Face3 { +class _NO_DISCARD_ Face3 { public: enum Side { SIDE_OVER, diff --git a/core/math/plane.h b/core/math/plane.h index bac946502b..8cb6f62b3b 100644 --- a/core/math/plane.h +++ b/core/math/plane.h @@ -35,7 +35,7 @@ class Variant; -class Plane { +class _NO_DISCARD_ Plane { public: Vector3 normal; real_t d = 0; diff --git a/core/math/quaternion.h b/core/math/quaternion.h index cf3762e090..2575d7d229 100644 --- a/core/math/quaternion.h +++ b/core/math/quaternion.h @@ -36,7 +36,7 @@ #include "core/math/vector3.h" #include "core/string/ustring.h" -class Quaternion { +class _NO_DISCARD_ Quaternion { public: union { struct { diff --git a/core/math/rect2.h b/core/math/rect2.h index f34550bef1..4ea24e8f88 100644 --- a/core/math/rect2.h +++ b/core/math/rect2.h @@ -35,7 +35,7 @@ struct Transform2D; -struct Rect2 { +struct _NO_DISCARD_ Rect2 { Point2 position; Size2 size; @@ -363,7 +363,7 @@ struct Rect2 { } }; -struct Rect2i { +struct _NO_DISCARD_ Rect2i { Point2i position; Size2i size; diff --git a/core/math/transform_2d.h b/core/math/transform_2d.h index 752a885eba..6c2d51bd9b 100644 --- a/core/math/transform_2d.h +++ b/core/math/transform_2d.h @@ -33,7 +33,7 @@ #include "core/math/rect2.h" // also includes vector2, math_funcs, and ustring -struct Transform2D { +struct _NO_DISCARD_ Transform2D { // Warning #1: basis of Transform2D is stored differently from Basis. In terms of elements array, the basis matrix looks like "on paper": // M = (elements[0][0] elements[1][0]) // (elements[0][1] elements[1][1]) diff --git a/core/math/transform_3d.h b/core/math/transform_3d.h index c0ef2ecfc1..c16c278e74 100644 --- a/core/math/transform_3d.h +++ b/core/math/transform_3d.h @@ -35,7 +35,7 @@ #include "core/math/basis.h" #include "core/math/plane.h" -class Transform3D { +class _NO_DISCARD_ Transform3D { public: Basis basis; Vector3 origin; diff --git a/core/math/vector2.h b/core/math/vector2.h index a340036ac7..af40b9e68d 100644 --- a/core/math/vector2.h +++ b/core/math/vector2.h @@ -36,7 +36,7 @@ struct Vector2i; -struct Vector2 { +struct _NO_DISCARD_ Vector2 { static const int AXIS_COUNT = 2; enum Axis { @@ -284,7 +284,7 @@ typedef Vector2 Point2; /* INTEGER STUFF */ -struct Vector2i { +struct _NO_DISCARD_ Vector2i { enum Axis { AXIS_X, AXIS_Y, diff --git a/core/math/vector3.h b/core/math/vector3.h index d7a72b05a8..b62edef40f 100644 --- a/core/math/vector3.h +++ b/core/math/vector3.h @@ -37,7 +37,7 @@ #include "core/string/ustring.h" class Basis; -struct Vector3 { +struct _NO_DISCARD_ Vector3 { static const int AXIS_COUNT = 3; enum Axis { diff --git a/core/math/vector3i.h b/core/math/vector3i.h index 1416c98057..1564ee9173 100644 --- a/core/math/vector3i.h +++ b/core/math/vector3i.h @@ -35,7 +35,7 @@ #include "core/string/ustring.h" #include "core/typedefs.h" -struct Vector3i { +struct _NO_DISCARD_ Vector3i { enum Axis { AXIS_X, AXIS_Y, diff --git a/core/typedefs.h b/core/typedefs.h index e6034eb375..5929b5123b 100644 --- a/core/typedefs.h +++ b/core/typedefs.h @@ -71,6 +71,17 @@ #endif #endif +// No discard allows the compiler to flag warnings if we don't use the return value of functions / classes +#ifndef _NO_DISCARD_ +#define _NO_DISCARD_ [[nodiscard]] +#endif + +// In some cases _NO_DISCARD_ will get false positives, +// we can prevent the warning in specific cases by preceding the call with a cast. +#ifndef _ALLOW_DISCARD_ +#define _ALLOW_DISCARD_ (void) +#endif + // Windows badly defines a lot of stuff we'll never use. Undefine it. #ifdef _WIN32 #undef min // override standard definition diff --git a/doc/classes/DisplayServer.xml b/doc/classes/DisplayServer.xml index c527922cd5..6e06bf454c 100644 --- a/doc/classes/DisplayServer.xml +++ b/doc/classes/DisplayServer.xml @@ -20,6 +20,12 @@ [b]Note:[/b] This method is only implemented on Linux. </description> </method> + <method name="clipboard_has" qualifiers="const"> + <return type="bool" /> + <description> + Returns [code]true[/code] if there is content on the user's clipboard. + </description> + </method> <method name="clipboard_set"> <return type="void" /> <argument index="0" name="clipboard" type="String" /> diff --git a/doc/classes/Environment.xml b/doc/classes/Environment.xml index dab9d7b054..b31162f10f 100644 --- a/doc/classes/Environment.xml +++ b/doc/classes/Environment.xml @@ -172,7 +172,7 @@ The number of cascades to use for SDFGI (between 1 and 8). A higher number of cascades allows displaying SDFGI further away while preserving detail up close, at the cost of performance. When using SDFGI on small-scale levels, [member sdfgi_cascades] can often be decreased between [code]1[/code] and [code]4[/code] to improve performance. </member> <member name="sdfgi_enabled" type="bool" setter="set_sdfgi_enabled" getter="is_sdfgi_enabled" default="false"> - If [code]true[/code], enables signed distance field global illumination for meshes that have their [member GeometryInstance3D.gi_mode] set to [constant GeometryInstance3D.GI_MODE_BAKED]. SDFGI is a real-time global illumination technique that works well with procedurally generated and user-built levels, including in situations where geometry is created during gameplay. The signed distance field is automatically generated around the camera as it moves. Dynamic lights are supported, but dynamic occluders and emissive surfaces are not. + If [code]true[/code], enables signed distance field global illumination for meshes that have their [member GeometryInstance3D.gi_mode] set to [constant GeometryInstance3D.GI_MODE_STATIC]. SDFGI is a real-time global illumination technique that works well with procedurally generated and user-built levels, including in situations where geometry is created during gameplay. The signed distance field is automatically generated around the camera as it moves. Dynamic lights are supported, but dynamic occluders and emissive surfaces are not. [b]Performance:[/b] SDFGI is relatively demanding on the GPU and is not suited to low-end hardware such as integrated graphics (consider [LightmapGI] instead). To improve SDFGI performance, enable [member ProjectSettings.rendering/global_illumination/gi/use_half_resolution] in the Project Settings. [b]Note:[/b] Meshes should have sufficiently thick walls to avoid light leaks (avoid one-sided walls). For interior levels, enclose your level geometry in a sufficiently large box and bridge the loops to close the mesh. </member> diff --git a/doc/classes/GeometryInstance3D.xml b/doc/classes/GeometryInstance3D.xml index cecd1e518f..c03a2b8b7c 100644 --- a/doc/classes/GeometryInstance3D.xml +++ b/doc/classes/GeometryInstance3D.xml @@ -41,7 +41,8 @@ The texel density to use for lightmapping in [LightmapGI]. Greater scale values provide higher resolution in the lightmap, which can result in sharper shadows for lights that have both direct and indirect light baked. However, greater scale values will also increase the space taken by the mesh in the lightmap texture, which increases the memory, storage, and bake time requirements. When using a single mesh at different scales, consider adjusting this value to keep the lightmap texel density consistent across meshes. </member> <member name="gi_mode" type="int" setter="set_gi_mode" getter="get_gi_mode" enum="GeometryInstance3D.GIMode" default="0"> - The global illumination mode to use for the whole geometry. Use a mode that matches the purpose + The global illumination mode to use for the whole geometry. To avoid inconsistent results, use a mode that matches the purpose of the mesh during gameplay (static/dynamic). + [b]Note:[/b] Lights' bake mode will also affect the global illumination rendering. See [member Light3D.light_bake_mode]. </member> <member name="ignore_occlusion_culling" type="bool" setter="set_ignore_occlusion_culling" getter="is_ignoring_occlusion_culling" default="false"> </member> @@ -93,13 +94,13 @@ In other words, the actual mesh will not be visible, only the shadows casted from the mesh will be. </constant> <constant name="GI_MODE_DISABLED" value="0" enum="GIMode"> - Disabled global illumination mode. Use for dynamic objects that do not contribute to global illumination (such as characters). When using [VoxelGI] and SDFGI, the geometry will [i]receive[/i] indirect lighting and reflections but will not be considered in GI baking. When using [LightmapGI], the object will receive indirect lighting using lightmap probes instead of using the lightmap texture. + Disabled global illumination mode. Use for dynamic objects that do not contribute to global illumination (such as characters). When using [VoxelGI] and SDFGI, the geometry will [i]receive[/i] indirect lighting and reflections but the geometry will not be considered in GI baking. When using [LightmapGI], the object will receive indirect lighting using lightmap probes instead of using the baked lightmap texture. </constant> - <constant name="GI_MODE_BAKED" value="1" enum="GIMode"> + <constant name="GI_MODE_STATIC" value="1" enum="GIMode"> Baked global illumination mode. Use for static objects that contribute to global illumination (such as level geometry). This GI mode is effective when using [VoxelGI], SDFGI and [LightmapGI]. </constant> <constant name="GI_MODE_DYNAMIC" value="2" enum="GIMode"> - Dynamic global illumination mode. Use for dynamic objects that contribute to global illumination. This GI mode is only effective when using [VoxelGI], but it has a higher performance impact than [constant GI_MODE_BAKED]. + Dynamic global illumination mode. Use for dynamic objects that contribute to global illumination. This GI mode is only effective when using [VoxelGI], but it has a higher performance impact than [constant GI_MODE_STATIC]. When using other GI methods, this will act the same as [constant GI_MODE_DISABLED]. </constant> <constant name="LIGHTMAP_SCALE_1X" value="0" enum="LightmapScale"> The standard texel density for lightmapping with [LightmapGI]. diff --git a/doc/classes/Light3D.xml b/doc/classes/Light3D.xml index 009ad1f609..b8d46755c7 100644 --- a/doc/classes/Light3D.xml +++ b/doc/classes/Light3D.xml @@ -34,8 +34,9 @@ <member name="light_angular_distance" type="float" setter="set_param" getter="get_param" default="0.0"> The light's angular size in degrees. Increasing this will make shadows softer at greater distances. Only available for [DirectionalLight3D]s. For reference, the Sun from the Earth is approximately [code]0.5[/code]. </member> - <member name="light_bake_mode" type="int" setter="set_bake_mode" getter="get_bake_mode" enum="Light3D.BakeMode" default="1"> - The light's bake mode. See [enum BakeMode]. + <member name="light_bake_mode" type="int" setter="set_bake_mode" getter="get_bake_mode" enum="Light3D.BakeMode" default="2"> + The light's bake mode. This will affect the global illumination techniques that have an effect on the light's rendering. See [enum BakeMode]. + [b]Note:[/b] Meshes' global illumination mode will also affect the global illumination rendering. See [member GeometryInstance3D.gi_mode]. </member> <member name="light_color" type="Color" setter="set_color" getter="get_color" default="Color(1, 1, 1, 1)"> The light's color. An [i]overbright[/i] color can be used to achieve a result equivalent to increasing the light's [member light_energy]. @@ -146,12 +147,14 @@ Represents the size of the [enum Param] enum. </constant> <constant name="BAKE_DISABLED" value="0" enum="BakeMode"> - Light is ignored when baking. - [b]Note:[/b] Hiding a light does [i]not[/i] affect baking. + Light is ignored when baking. This is the fastest mode, but the light will be taken into account when baking global illumination. This mode should generally be used for dynamic lights that change quickly, as the effect of global illumination is less noticeable on those lights. + [b]Note:[/b] Hiding a light does [i]not[/i] affect baking [LightmapGI]. Hiding a light will still affect baking [VoxelGI] and SDFGI (see [member Environment.sdfgi_enabled). </constant> - <constant name="BAKE_DYNAMIC" value="1" enum="BakeMode"> + <constant name="BAKE_STATIC" value="1" enum="BakeMode"> + Light is taken into account in static baking ([VoxelGI], [LightmapGI], SDFGI ([member Environment.sdfgi_enabled])). The light can be moved around or modified, but its global illumination will not update in real-time. This is suitable for subtle changes (such as flickering torches), but generally not large changes such as toggling a light on and off. </constant> - <constant name="BAKE_STATIC" value="2" enum="BakeMode"> + <constant name="BAKE_DYNAMIC" value="2" enum="BakeMode"> + Light is taken into account in dynamic baking ([VoxelGI] and SDFGI ([member Environment.sdfgi_enabled]) only). The light can be moved around or modified with global illumination updating in real-time. The light's global illumination appearance will be slightly different compared to [constant BAKE_STATIC]. This has a greater performance cost compared to [constant BAKE_STATIC]. </constant> </constants> </class> diff --git a/doc/classes/RenderingDevice.xml b/doc/classes/RenderingDevice.xml index 909a8337cc..d6510993d8 100644 --- a/doc/classes/RenderingDevice.xml +++ b/doc/classes/RenderingDevice.xml @@ -305,7 +305,7 @@ <description> </description> </method> - <method name="free"> + <method name="free_rid"> <return type="void" /> <argument index="0" name="rid" type="RID" /> <description> diff --git a/doc/classes/RenderingServer.xml b/doc/classes/RenderingServer.xml index 9e78372013..ff370bd953 100644 --- a/doc/classes/RenderingServer.xml +++ b/doc/classes/RenderingServer.xml @@ -3757,9 +3757,9 @@ </constant> <constant name="LIGHT_BAKE_DISABLED" value="0" enum="LightBakeMode"> </constant> - <constant name="LIGHT_BAKE_DYNAMIC" value="1" enum="LightBakeMode"> + <constant name="LIGHT_BAKE_STATIC" value="1" enum="LightBakeMode"> </constant> - <constant name="LIGHT_BAKE_STATIC" value="2" enum="LightBakeMode"> + <constant name="LIGHT_BAKE_DYNAMIC" value="2" enum="LightBakeMode"> </constant> <constant name="LIGHT_OMNI_SHADOW_DUAL_PARABOLOID" value="0" enum="LightOmniShadowMode"> Use a dual paraboloid shadow map for omni lights. diff --git a/doc/classes/Tree.xml b/doc/classes/Tree.xml index 5130851948..106bcf9d37 100644 --- a/doc/classes/Tree.xml +++ b/doc/classes/Tree.xml @@ -357,6 +357,13 @@ Emitted when a cell is selected. </description> </signal> + <signal name="check_propagated_to_item"> + <argument index="0" name="item" type="TreeItem" /> + <argument index="1" name="column" type="int" /> + <description> + Emitted when [method TreeItem.propagate_check] is called. Connect to this signal to process the items that are affected when [method TreeItem.propagate_check] is invoked. The order that the items affected will be processed is as follows: the item that invoked the method, children of that item, and finally parents of that item. + </description> + </signal> <signal name="column_title_pressed"> <argument index="0" name="column" type="int" /> <description> diff --git a/doc/classes/TreeItem.xml b/doc/classes/TreeItem.xml index 7da98788bb..0090fb555f 100644 --- a/doc/classes/TreeItem.xml +++ b/doc/classes/TreeItem.xml @@ -385,6 +385,14 @@ [b]Note:[/b] You can't move to the root or move the root. </description> </method> + <method name="propagate_check"> + <return type="void" /> + <argument index="0" name="column" type="int" /> + <argument index="1" name="emit_signal" type="bool" default="true" /> + <description> + Propagates this item's checked status to its children and parents for the given [code]column[/code]. It is possible to process the items affected by this method call by connecting to [signal Tree.check_propagated_to_item]. The order that the items affected will be processed is as follows: the item invoking this method, children of that item, and finally parents of that item. If [code]emit_signal[/code] is set to false, then [signal Tree.check_propagated_to_item] will not be emitted. + </description> + </method> <method name="remove_child"> <return type="void" /> <argument index="0" name="child" type="Object" /> diff --git a/doc/classes/VoxelGI.xml b/doc/classes/VoxelGI.xml index 2c3605aa1c..ac6f026a37 100644 --- a/doc/classes/VoxelGI.xml +++ b/doc/classes/VoxelGI.xml @@ -7,7 +7,7 @@ [VoxelGI]s are used to provide high-quality real-time indirect light and reflections to scenes. They precompute the effect of objects that emit light and the effect of static geometry to simulate the behavior of complex light in real-time. [VoxelGI]s need to be baked before having a visible effect. However, once baked, dynamic objects will receive light from them. Furthermore, lights can be fully dynamic or baked. [b]Procedural generation:[/b] [VoxelGI] can be baked in an exported project, which makes it suitable for procedurally generated or user-built levels as long as all the geometry is generated in advance. For games where geometry is generated at any time during gameplay, SDFGI is more suitable (see [member Environment.sdfgi_enabled]). [b]Performance:[/b] [VoxelGI] is relatively demanding on the GPU and is not suited to low-end hardware such as integrated graphics (consider [LightmapGI] instead). To improve performance, adjust [member ProjectSettings.rendering/global_illumination/voxel_gi/quality] and enable [member ProjectSettings.rendering/global_illumination/gi/use_half_resolution] in the Project Settings. To provide a fallback for low-end hardware, consider adding an option to disable [VoxelGI] in your project's options menus. A [VoxelGI] node can be disabled by hiding it. - [b]Note:[/b] Meshes should have sufficiently thick walls to avoid light leaks (avoid one-sided walls). For interior levels, enclose your level geometry in a sufficiently large box and bridge the loops to close the mesh. To further prevent light leaks, you can also strategically place temporary [MeshInstance3D] nodes with their [member GeometryInstance3D.gi_mode] set to [constant GeometryInstance3D.GI_MODE_BAKED]. These temporary nodes can then be hidden after baking the [VoxelGI] node. + [b]Note:[/b] Meshes should have sufficiently thick walls to avoid light leaks (avoid one-sided walls). For interior levels, enclose your level geometry in a sufficiently large box and bridge the loops to close the mesh. To further prevent light leaks, you can also strategically place temporary [MeshInstance3D] nodes with their [member GeometryInstance3D.gi_mode] set to [constant GeometryInstance3D.GI_MODE_STATIC]. These temporary nodes can then be hidden after baking the [VoxelGI] node. </description> <tutorials> <link title="GI probes">$DOCS_URL/tutorials/3d/gi_probes.html</link> @@ -19,7 +19,7 @@ <argument index="0" name="from_node" type="Node" default="null" /> <argument index="1" name="create_visual_debug" type="bool" default="false" /> <description> - Bakes the effect from all [GeometryInstance3D]s marked with [constant GeometryInstance3D.GI_MODE_BAKED] and [Light3D]s marked with either [constant Light3D.BAKE_DYNAMIC] or [constant Light3D.BAKE_STATIC]. If [code]create_visual_debug[/code] is [code]true[/code], after baking the light, this will generate a [MultiMesh] that has a cube representing each solid cell with each cube colored to the cell's albedo color. This can be used to visualize the [VoxelGI]'s data and debug any issues that may be occurring. + Bakes the effect from all [GeometryInstance3D]s marked with [constant GeometryInstance3D.GI_MODE_STATIC] and [Light3D]s marked with either [constant Light3D.BAKE_STATIC] or [constant Light3D.BAKE_DYNAMIC]. If [code]create_visual_debug[/code] is [code]true[/code], after baking the light, this will generate a [MultiMesh] that has a cube representing each solid cell with each cube colored to the cell's albedo color. This can be used to visualize the [VoxelGI]'s data and debug any issues that may be occurring. [b]Note:[/b] [method bake] works from the editor and in exported projects. This makes it suitable for procedurally generated or user-built levels. Baking a [VoxelGI] node generally takes from 5 to 20 seconds in most scenes. Reducing [member subdiv] can speed up baking. </description> </method> diff --git a/doc/classes/XRPose.xml b/doc/classes/XRPose.xml index 0de2bc9e48..4a09122b40 100644 --- a/doc/classes/XRPose.xml +++ b/doc/classes/XRPose.xml @@ -34,8 +34,22 @@ - [code]grip[/code] defines the location where the user grips the controller - [code]skeleton[/code] defines the root location a hand mesh should be placed when using hand tracking and the animated skeleton supplied by the XR runtime. </member> + <member name="tracking_confidence" type="int" setter="set_tracking_confidence" getter="get_tracking_confidence" enum="XRPose.TrackingConfidence" default="0"> + The tracking confidence for this pose, provides insight on how accurate the spatial positioning of this record is. + </member> <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)"> The transform containing the original and transform as reported by the XR runtime. </member> </members> + <constants> + <constant name="XR_TRACKING_CONFIDENCE_NONE" value="0" enum="TrackingConfidence"> + No tracking information is available for this pose. + </constant> + <constant name="XR_TRACKING_CONFIDENCE_LOW" value="1" enum="TrackingConfidence"> + Tracking information may be inaccurate or estimated. For instance with inside out tracking this would indicate a controller may be (partially) obscured. + </constant> + <constant name="XR_TRACKING_CONFIDENCE_HIGH" value="2" enum="TrackingConfidence"> + Tracking information is deemed accurate and up to date. + </constant> + </constants> </class> diff --git a/doc/classes/XRPositionalTracker.xml b/doc/classes/XRPositionalTracker.xml index 439bcfc382..c39a4aa7a7 100644 --- a/doc/classes/XRPositionalTracker.xml +++ b/doc/classes/XRPositionalTracker.xml @@ -54,8 +54,9 @@ <argument index="1" name="transform" type="Transform3D" /> <argument index="2" name="linear_velocity" type="Vector3" /> <argument index="3" name="angular_velocity" type="Vector3" /> + <argument index="4" name="tracking_confidence" type="int" enum="XRPose.TrackingConfidence" /> <description> - Sets the transform, linear velocity and angular velocity for the given pose. This method is called by a [XRInterface] implementation and should not be used directly. + Sets the transform, linear velocity, angular velocity and tracking confidence for the given pose. This method is called by a [XRInterface] implementation and should not be used directly. </description> </method> </methods> diff --git a/editor/editor_asset_installer.cpp b/editor/editor_asset_installer.cpp index b89b20b8cd..76c0811166 100644 --- a/editor/editor_asset_installer.cpp +++ b/editor/editor_asset_installer.cpp @@ -36,45 +36,6 @@ #include "editor_node.h" #include "progress_dialog.h" -void EditorAssetInstaller::_update_subitems(TreeItem *p_item, bool p_check, bool p_first) { - if (p_check) { - if (p_item->get_custom_color(0) == Color()) { - p_item->set_checked(0, true); - } - } else { - p_item->set_checked(0, false); - } - - if (p_item->get_first_child()) { - _update_subitems(p_item->get_first_child(), p_check); - } - - if (!p_first && p_item->get_next()) { - _update_subitems(p_item->get_next(), p_check); - } -} - -void EditorAssetInstaller::_uncheck_parent(TreeItem *p_item) { - if (!p_item) { - return; - } - - bool any_checked = false; - TreeItem *item = p_item->get_first_child(); - while (item) { - if (item->is_checked(0)) { - any_checked = true; - break; - } - item = item->get_next(); - } - - if (!any_checked) { - p_item->set_checked(0, false); - _uncheck_parent(p_item->get_parent()); - } -} - void EditorAssetInstaller::_item_edited() { if (updating) { return; @@ -85,22 +46,17 @@ void EditorAssetInstaller::_item_edited() { return; } - String path = item->get_metadata(0); - updating = true; - if (path.is_empty() || item == tree->get_root()) { //a dir or root - _update_subitems(item, item->is_checked(0), true); - } + item->propagate_check(0); + updating = false; +} - if (item->is_checked(0)) { - while (item) { - item->set_checked(0, true); - item = item->get_parent(); - } - } else { - _uncheck_parent(item->get_parent()); +void EditorAssetInstaller::_check_propagated_to_item(Object *p_obj, int column) { + TreeItem *affected_item = Object::cast_to<TreeItem>(p_obj); + if (affected_item && affected_item->get_custom_color(0) != Color()) { + affected_item->set_checked(0, false); + affected_item->propagate_check(0, false); } - updating = false; } void EditorAssetInstaller::open(const String &p_path, int p_depth) { @@ -259,6 +215,7 @@ void EditorAssetInstaller::open(const String &p_path, int p_depth) { ti->set_custom_color(0, tree->get_theme_color(SNAME("error_color"), SNAME("Editor"))); ti->set_tooltip(0, vformat(TTR("%s (already exists)"), res_path)); ti->set_checked(0, false); + ti->propagate_check(0); } else { ti->set_tooltip(0, res_path); } @@ -304,7 +261,7 @@ void EditorAssetInstaller::ok_pressed() { String name = String::utf8(fname); - if (status_map.has(name) && status_map[name]->is_checked(0)) { + if (status_map.has(name) && (status_map[name]->is_checked(0) || status_map[name]->is_indeterminate(0))) { String path = status_map[name]->get_metadata(0); if (path.is_empty()) { // a dir @@ -392,6 +349,7 @@ EditorAssetInstaller::EditorAssetInstaller() { tree = memnew(Tree); tree->set_v_size_flags(Control::SIZE_EXPAND_FILL); tree->connect("item_edited", callable_mp(this, &EditorAssetInstaller::_item_edited)); + tree->connect("check_propagated_to_item", callable_mp(this, &EditorAssetInstaller::_check_propagated_to_item)); vb->add_child(tree); error = memnew(AcceptDialog); diff --git a/editor/editor_asset_installer.h b/editor/editor_asset_installer.h index 2f59250933..f5993f73e7 100644 --- a/editor/editor_asset_installer.h +++ b/editor/editor_asset_installer.h @@ -43,9 +43,8 @@ class EditorAssetInstaller : public ConfirmationDialog { AcceptDialog *error; Map<String, TreeItem *> status_map; bool updating; - void _update_subitems(TreeItem *p_item, bool p_check, bool p_first = false); - void _uncheck_parent(TreeItem *p_item); void _item_edited(); + void _check_propagated_to_item(Object *p_obj, int column); virtual void ok_pressed() override; protected: diff --git a/editor/import/resource_importer_scene.cpp b/editor/import/resource_importer_scene.cpp index b6624a8cfa..af9a2f9ebe 100644 --- a/editor/import/resource_importer_scene.cpp +++ b/editor/import/resource_importer_scene.cpp @@ -1476,7 +1476,7 @@ void ResourceImporterScene::get_import_options(const String &p_path, List<Import r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "meshes/ensure_tangents"), true)); r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "meshes/generate_lods"), true)); r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "meshes/create_shadow_meshes"), true)); - r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "meshes/light_baking", PROPERTY_HINT_ENUM, "Disabled,Dynamic,Static,Static Lightmaps", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), 2)); + r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "meshes/light_baking", PROPERTY_HINT_ENUM, "Disabled,Static (VoxelGI/SDFGI),Static Lightmaps,Dynamic (VoxelGI only)", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), 1)); r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "meshes/lightmap_texel_size", PROPERTY_HINT_RANGE, "0.001,100,0.001"), 0.1)); r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "skins/use_named_skins"), true)); r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "animation/import"), true)); @@ -1660,7 +1660,7 @@ void ResourceImporterScene::_generate_meshes(Node *p_node, const Dictionary &p_m } break; case LIGHT_BAKE_STATIC: case LIGHT_BAKE_STATIC_LIGHTMAPS: { - mesh_node->set_gi_mode(GeometryInstance3D::GI_MODE_BAKED); + mesh_node->set_gi_mode(GeometryInstance3D::GI_MODE_STATIC); } break; } @@ -1776,7 +1776,8 @@ void ResourceImporterScene::_optimize_track_usage(AnimationPlayer *p_player, Ani if (bone_idx == -1) { continue; } - skel->get_bone_pose(bone_idx); + // Note that this is using get_bone_pose to update the bone pose cache. + _ALLOW_DISCARD_ skel->get_bone_pose(bone_idx); loc = skel->get_bone_pose_position(bone_idx); rot = skel->get_bone_pose_rotation(bone_idx); scale = skel->get_bone_pose_scale(bone_idx); diff --git a/editor/import/resource_importer_scene.h b/editor/import/resource_importer_scene.h index 066e8b603b..13b55b5754 100644 --- a/editor/import/resource_importer_scene.h +++ b/editor/import/resource_importer_scene.h @@ -159,9 +159,9 @@ class ResourceImporterScene : public ResourceImporter { enum LightBakeMode { LIGHT_BAKE_DISABLED, - LIGHT_BAKE_DYNAMIC, LIGHT_BAKE_STATIC, - LIGHT_BAKE_STATIC_LIGHTMAPS + LIGHT_BAKE_STATIC_LIGHTMAPS, + LIGHT_BAKE_DYNAMIC, }; enum MeshPhysicsMode { diff --git a/editor/plugins/theme_editor_plugin.cpp b/editor/plugins/theme_editor_plugin.cpp index 91c17399c2..f95b2b40b5 100644 --- a/editor/plugins/theme_editor_plugin.cpp +++ b/editor/plugins/theme_editor_plugin.cpp @@ -81,8 +81,6 @@ void ThemeItemImportTree::_update_items_tree() { bool is_matching_filter = (filter_text.is_empty() || type_name.findn(filter_text) > -1); bool has_filtered_items = false; - bool any_checked = false; - bool any_checked_with_data = false; for (int i = 0; i < Theme::DATA_TYPE_MAX; i++) { Theme::DataType dt = (Theme::DataType)i; @@ -178,9 +176,6 @@ void ThemeItemImportTree::_update_items_tree() { break; // Can't happen, but silences warning. } - bool data_type_any_checked = false; - bool data_type_any_checked_with_data = false; - filtered_names.sort_custom<StringName::AlphCompare>(); for (const StringName &F : filtered_names) { TreeItem *item_node = import_items_tree->create_item(data_type_node); @@ -194,20 +189,11 @@ void ThemeItemImportTree::_update_items_tree() { item_node->set_editable(IMPORT_ITEM_DATA, true); _restore_selected_item(item_node); - if (item_node->is_checked(IMPORT_ITEM)) { - data_type_any_checked = true; - any_checked = true; - } - if (item_node->is_checked(IMPORT_ITEM_DATA)) { - data_type_any_checked_with_data = true; - any_checked_with_data = true; - } + item_node->propagate_check(IMPORT_ITEM, false); + item_node->propagate_check(IMPORT_ITEM_DATA, false); item_list->push_back(item_node); } - - data_type_node->set_checked(IMPORT_ITEM, data_type_any_checked); - data_type_node->set_checked(IMPORT_ITEM_DATA, data_type_any_checked && data_type_any_checked_with_data); } // Remove the item if it doesn't match the filter in any way. @@ -221,9 +207,6 @@ void ThemeItemImportTree::_update_items_tree() { if (!filter_text.is_empty() && has_filtered_items) { type_node->set_collapsed(false); } - - type_node->set_checked(IMPORT_ITEM, any_checked); - type_node->set_checked(IMPORT_ITEM_DATA, any_checked && any_checked_with_data); } if (color_amount > 0) { @@ -471,23 +454,26 @@ void ThemeItemImportTree::_tree_item_edited() { if (is_checked) { if (edited_column == IMPORT_ITEM_DATA) { edited_item->set_checked(IMPORT_ITEM, true); + edited_item->propagate_check(IMPORT_ITEM); } - - _select_all_subitems(edited_item, (edited_column == IMPORT_ITEM_DATA)); } else { if (edited_column == IMPORT_ITEM) { edited_item->set_checked(IMPORT_ITEM_DATA, false); + edited_item->propagate_check(IMPORT_ITEM_DATA); } - - _deselect_all_subitems(edited_item, (edited_column == IMPORT_ITEM)); } - - _update_parent_items(edited_item); - _store_selected_item(edited_item); - + edited_item->propagate_check(edited_column); updating_tree = false; } +void ThemeItemImportTree::_check_propagated_to_tree_item(Object *p_obj, int p_column) { + TreeItem *item = Object::cast_to<TreeItem>(p_obj); + // Skip "category" tree items by checking for children. + if (item && !item->get_first_child()) { + _store_selected_item(item); + } +} + void ThemeItemImportTree::_select_all_subitems(TreeItem *p_root_item, bool p_select_with_data) { TreeItem *child_item = p_root_item->get_first_child(); while (child_item) { @@ -516,32 +502,6 @@ void ThemeItemImportTree::_deselect_all_subitems(TreeItem *p_root_item, bool p_d } } -void ThemeItemImportTree::_update_parent_items(TreeItem *p_root_item) { - TreeItem *parent_item = p_root_item->get_parent(); - if (!parent_item) { - return; - } - - bool any_checked = false; - bool any_checked_with_data = false; - - TreeItem *child_item = parent_item->get_first_child(); - while (child_item) { - if (child_item->is_checked(IMPORT_ITEM)) { - any_checked = true; - } - if (child_item->is_checked(IMPORT_ITEM_DATA)) { - any_checked_with_data = true; - } - - child_item = child_item->get_next(); - } - - parent_item->set_checked(IMPORT_ITEM, any_checked); - parent_item->set_checked(IMPORT_ITEM_DATA, any_checked && any_checked_with_data); - _update_parent_items(parent_item); -} - void ThemeItemImportTree::_select_all_items_pressed() { if (updating_tree) { return; @@ -629,7 +589,7 @@ void ThemeItemImportTree::_select_all_data_type_pressed(int p_data_type) { } child_item->set_checked(IMPORT_ITEM, true); - _update_parent_items(child_item); + child_item->propagate_check(IMPORT_ITEM, false); _store_selected_item(child_item); } @@ -685,7 +645,8 @@ void ThemeItemImportTree::_select_full_data_type_pressed(int p_data_type) { child_item->set_checked(IMPORT_ITEM, true); child_item->set_checked(IMPORT_ITEM_DATA, true); - _update_parent_items(child_item); + child_item->propagate_check(IMPORT_ITEM, false); + child_item->propagate_check(IMPORT_ITEM_DATA, false); _store_selected_item(child_item); } @@ -741,7 +702,8 @@ void ThemeItemImportTree::_deselect_all_data_type_pressed(int p_data_type) { child_item->set_checked(IMPORT_ITEM, false); child_item->set_checked(IMPORT_ITEM_DATA, false); - _update_parent_items(child_item); + child_item->propagate_check(IMPORT_ITEM, false); + child_item->propagate_check(IMPORT_ITEM_DATA, false); _store_selected_item(child_item); } @@ -937,6 +899,7 @@ ThemeItemImportTree::ThemeItemImportTree() { import_items_tree->set_h_size_flags(Control::SIZE_EXPAND_FILL); import_main_hb->add_child(import_items_tree); import_items_tree->connect("item_edited", callable_mp(this, &ThemeItemImportTree::_tree_item_edited)); + import_items_tree->connect("check_propagated_to_item", callable_mp(this, &ThemeItemImportTree::_check_propagated_to_tree_item)); import_items_tree->set_columns(3); import_items_tree->set_column_titles_visible(true); diff --git a/editor/plugins/theme_editor_plugin.h b/editor/plugins/theme_editor_plugin.h index 4c6b16a68c..c00ce3ae65 100644 --- a/editor/plugins/theme_editor_plugin.h +++ b/editor/plugins/theme_editor_plugin.h @@ -149,9 +149,9 @@ class ThemeItemImportTree : public VBoxContainer { void _update_total_selected(Theme::DataType p_data_type); void _tree_item_edited(); + void _check_propagated_to_tree_item(Object *p_obj, int p_column); void _select_all_subitems(TreeItem *p_root_item, bool p_select_with_data); void _deselect_all_subitems(TreeItem *p_root_item, bool p_deselect_completely); - void _update_parent_items(TreeItem *p_root_item); void _select_all_items_pressed(); void _select_full_items_pressed(); diff --git a/editor/project_export.cpp b/editor/project_export.cpp index 9bd8c1e227..f39a494df8 100644 --- a/editor/project_export.cpp +++ b/editor/project_export.cpp @@ -752,12 +752,10 @@ bool ProjectExportDialog::_fill_tree(EditorFileSystemDirectory *p_dir, TreeItem p_item->set_metadata(0, p_dir->get_path()); bool used = false; - bool checked = true; for (int i = 0; i < p_dir->get_subdir_count(); i++) { TreeItem *subdir = include_files->create_item(p_item); if (_fill_tree(p_dir->get_subdir(i), subdir, current, p_only_scenes)) { used = true; - checked = checked && subdir->is_checked(0); } else { memdelete(subdir); } @@ -782,12 +780,10 @@ bool ProjectExportDialog::_fill_tree(EditorFileSystemDirectory *p_dir, TreeItem file->set_editable(0, true); file->set_checked(0, current->has_export_file(path)); file->set_metadata(0, path); - checked = checked && file->is_checked(0); + file->propagate_check(0); used = true; } - - p_item->set_checked(0, checked); return used; } @@ -806,54 +802,24 @@ void ProjectExportDialog::_tree_changed() { return; } - String path = item->get_metadata(0); - bool added = item->is_checked(0); + item->propagate_check(0); +} - if (path.ends_with("/")) { - _check_dir_recursive(item, added); - } else { +void ProjectExportDialog::_check_propagated_to_item(Object *p_obj, int column) { + Ref<EditorExportPreset> current = get_current_preset(); + if (current.is_null()) { + return; + } + TreeItem *item = Object::cast_to<TreeItem>(p_obj); + String path = item->get_metadata(0); + if (item && !path.ends_with("/")) { + bool added = item->is_checked(0); if (added) { current->add_export_file(path); } else { current->remove_export_file(path); } } - _refresh_parent_checks(item); // Makes parent folder checked if all files/folders are checked. -} - -void ProjectExportDialog::_check_dir_recursive(TreeItem *p_dir, bool p_checked) { - for (TreeItem *child = p_dir->get_first_child(); child; child = child->get_next()) { - String path = child->get_metadata(0); - - child->set_checked(0, p_checked); - if (path.ends_with("/")) { - _check_dir_recursive(child, p_checked); - } else { - if (p_checked) { - get_current_preset()->add_export_file(path); - } else { - get_current_preset()->remove_export_file(path); - } - } - } -} - -void ProjectExportDialog::_refresh_parent_checks(TreeItem *p_item) { - TreeItem *parent = p_item->get_parent(); - if (!parent) { - return; - } - - bool checked = true; - for (TreeItem *child = parent->get_first_child(); child; child = child->get_next()) { - checked = checked && child->is_checked(0); - if (!checked) { - break; - } - } - parent->set_checked(0, checked); - - _refresh_parent_checks(parent); } void ProjectExportDialog::_export_pck_zip() { @@ -1126,6 +1092,7 @@ ProjectExportDialog::ProjectExportDialog() { include_files = memnew(Tree); include_margin->add_child(include_files); include_files->connect("item_edited", callable_mp(this, &ProjectExportDialog::_tree_changed)); + include_files->connect("check_propagated_to_item", callable_mp(this, &ProjectExportDialog::_check_propagated_to_item)); include_filters = memnew(LineEdit); resources_vb->add_margin_child( diff --git a/editor/project_export.h b/editor/project_export.h index af7ec083c4..3d90a0d3ff 100644 --- a/editor/project_export.h +++ b/editor/project_export.h @@ -124,8 +124,7 @@ private: void _fill_resource_tree(); bool _fill_tree(EditorFileSystemDirectory *p_dir, TreeItem *p_item, Ref<EditorExportPreset> ¤t, bool p_only_scenes); void _tree_changed(); - void _check_dir_recursive(TreeItem *p_dir, bool p_checked); - void _refresh_parent_checks(TreeItem *p_item); + void _check_propagated_to_item(Object *p_obj, int column); Variant get_drag_data_fw(const Point2 &p_point, Control *p_from); bool can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const; diff --git a/editor/scene_tree_dock.cpp b/editor/scene_tree_dock.cpp index c9acb7b668..3dbbb30b0e 100644 --- a/editor/scene_tree_dock.cpp +++ b/editor/scene_tree_dock.cpp @@ -2809,15 +2809,9 @@ void SceneTreeDock::_tree_rmb(const Vector2 &p_menu_pos) { menu->popup(); } -void SceneTreeDock::_open_tree_menu() { - menu->clear(); - - menu->add_check_item(TTR("Auto Expand to Selected"), TOOL_AUTO_EXPAND); - menu->set_item_checked(menu->get_item_idx_from_text(TTR("Auto Expand to Selected")), EditorSettings::get_singleton()->get("docks/scene_tree/auto_expand_to_selected")); - - menu->reset_size(); - menu->set_position(get_screen_position() + get_local_mouse_position()); - menu->popup(); +void SceneTreeDock::_update_tree_menu() { + PopupMenu *tree_menu = button_tree_menu->get_popup(); + tree_menu->set_item_checked(tree_menu->get_item_idx_from_text(TTR("Auto Expand to Selected")), EditorSettings::get_singleton()->get("docks/scene_tree/auto_expand_to_selected")); } void SceneTreeDock::_filter_changed(const String &p_filter) { @@ -3370,11 +3364,15 @@ SceneTreeDock::SceneTreeDock(EditorNode *p_editor, Node *p_scene_root, EditorSel filter_hbc->add_child(button_detach_script); button_detach_script->hide(); - button_tree_menu = memnew(Button); + button_tree_menu = memnew(MenuButton); button_tree_menu->set_flat(true); - button_tree_menu->connect("pressed", callable_mp(this, &SceneTreeDock::_open_tree_menu)); + button_tree_menu->connect("about_to_popup", callable_mp(this, &SceneTreeDock::_update_tree_menu)); filter_hbc->add_child(button_tree_menu); + PopupMenu *tree_menu = button_tree_menu->get_popup(); + tree_menu->add_check_item(TTR("Auto Expand to Selected"), TOOL_AUTO_EXPAND); + tree_menu->connect("id_pressed", callable_mp(this, &SceneTreeDock::_tool_selected), make_binds(false)); + button_hb = memnew(HBoxContainer); vbc->add_child(button_hb); diff --git a/editor/scene_tree_dock.h b/editor/scene_tree_dock.h index f442d3fc6b..dc7becfa2f 100644 --- a/editor/scene_tree_dock.h +++ b/editor/scene_tree_dock.h @@ -117,7 +117,7 @@ class SceneTreeDock : public VBoxContainer { Button *button_instance; Button *button_create_script; Button *button_detach_script; - Button *button_tree_menu; + MenuButton *button_tree_menu; Button *button_2d; Button *button_3d; @@ -242,7 +242,7 @@ class SceneTreeDock : public VBoxContainer { void _quick_open(); void _tree_rmb(const Vector2 &p_menu_pos); - void _open_tree_menu(); + void _update_tree_menu(); void _filter_changed(const String &p_filter); diff --git a/misc/dist/html/editor.html b/misc/dist/html/editor.html index 8b077a5725..a681a2a1c3 100644 --- a/misc/dist/html/editor.html +++ b/misc/dist/html/editor.html @@ -9,8 +9,8 @@ <meta name="apple-mobile-web-app-capable" content="yes" /> <meta name="application-name" content="Godot" /> <meta name="apple-mobile-web-app-title" content="Godot" /> - <meta name="theme-color" content="#478cbf" /> - <meta name="msapplication-navbutton-color" content="#478cbf" /> + <meta name="theme-color" content="#202531" /> + <meta name="msapplication-navbutton-color" content="#202531" /> <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" /> <meta name="msapplication-starturl" content="/latest" /> <meta property="og:site_name" content="Godot Engine Web Editor" /> diff --git a/misc/dist/html/manifest.json b/misc/dist/html/manifest.json index 0ca27b3742..adc8106e2a 100644 --- a/misc/dist/html/manifest.json +++ b/misc/dist/html/manifest.json @@ -6,7 +6,7 @@ "start_url": "./godot.tools.html", "display": "standalone", "orientation": "landscape", - "theme_color": "#478cbf", + "theme_color": "#202531", "icons": [ { "src": "favicon.png", diff --git a/misc/dist/html/offline.html b/misc/dist/html/offline.html index 000c21b4d3..5cfc3362d9 100644 --- a/misc/dist/html/offline.html +++ b/misc/dist/html/offline.html @@ -4,6 +4,8 @@ <meta charset="utf-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta name="viewport" content="width=device-width, initial-scale=1" /> + <meta name="theme-color" content="#202531" /> + <meta name="msapplication-navbutton-color" content="#202531" /> <title>You are offline</title> <style> html { diff --git a/modules/gridmap/doc_classes/GridMap.xml b/modules/gridmap/doc_classes/GridMap.xml index 885817caf1..9c28421a46 100644 --- a/modules/gridmap/doc_classes/GridMap.xml +++ b/modules/gridmap/doc_classes/GridMap.xml @@ -67,7 +67,7 @@ Returns whether or not the specified layer of the [member collision_mask] is enabled, given a [code]layer_number[/code] between 1 and 32. </description> </method> - <method name="get_meshes"> + <method name="get_meshes" qualifiers="const"> <return type="Array" /> <description> Returns an array of [Transform3D] and [Mesh] references corresponding to the non-empty cells in the grid. The transforms are specified in world space. diff --git a/modules/gridmap/grid_map.cpp b/modules/gridmap/grid_map.cpp index a861efcbf4..67deedf839 100644 --- a/modules/gridmap/grid_map.cpp +++ b/modules/gridmap/grid_map.cpp @@ -35,6 +35,7 @@ #include "scene/3d/light_3d.h" #include "scene/resources/mesh_library.h" #include "scene/resources/physics_material.h" +#include "scene/resources/primitive_meshes.h" #include "scene/resources/surface_tool.h" #include "scene/scene_string_names.h" #include "servers/navigation_server_3d.h" @@ -197,6 +198,24 @@ bool GridMap::get_collision_mask_value(int p_layer_number) const { return get_collision_mask() & (1 << (p_layer_number - 1)); } +Array GridMap::get_collision_shapes() const { + Array shapes; + for (const KeyValue<OctantKey, Octant *> &E : octant_map) { + Octant *g = E.value; + RID body = g->static_body; + Transform3D body_xform = PhysicsServer3D::get_singleton()->body_get_state(body, PhysicsServer3D::BODY_STATE_TRANSFORM); + int nshapes = PhysicsServer3D::get_singleton()->body_get_shape_count(body); + for (int i = 0; i < nshapes; i++) { + RID shape = PhysicsServer3D::get_singleton()->body_get_shape(body, i); + Transform3D xform = PhysicsServer3D::get_singleton()->body_get_shape_transform(body, i); + shapes.push_back(body_xform * xform); + shapes.push_back(shape); + } + } + + return shapes; +} + void GridMap::set_bake_navigation(bool p_bake_navigation) { bake_navigation = p_bake_navigation; _recreate_octant_data(); @@ -930,7 +949,7 @@ Array GridMap::get_used_cells() const { return a; } -Array GridMap::get_meshes() { +Array GridMap::get_meshes() const { if (mesh_library.is_null()) { return Array(); } @@ -938,7 +957,7 @@ Array GridMap::get_meshes() { Vector3 ofs = _get_offset(); Array meshes; - for (KeyValue<IndexKey, Cell> &E : cell_map) { + for (const KeyValue<IndexKey, Cell> &E : cell_map) { int id = E.value.item; if (!mesh_library->has_item(id)) { continue; diff --git a/modules/gridmap/grid_map.h b/modules/gridmap/grid_map.h index 546b530148..6cdc3b178d 100644 --- a/modules/gridmap/grid_map.h +++ b/modules/gridmap/grid_map.h @@ -229,6 +229,8 @@ public: void set_physics_material(Ref<PhysicsMaterial> p_material); Ref<PhysicsMaterial> get_physics_material() const; + Array get_collision_shapes() const; + void set_bake_navigation(bool p_bake_navigation); bool is_baking_navigation(); @@ -265,7 +267,7 @@ public: Array get_used_cells() const; - Array get_meshes(); + Array get_meshes() const; void clear_baked_meshes(); void make_baked_meshes(bool p_gen_lightmap_uv = false, float p_lightmap_uv_texel_size = 0.1); diff --git a/modules/mobile_vr/mobile_vr_interface.cpp b/modules/mobile_vr/mobile_vr_interface.cpp index 59854ad527..49044c4afe 100644 --- a/modules/mobile_vr/mobile_vr_interface.cpp +++ b/modules/mobile_vr/mobile_vr_interface.cpp @@ -181,6 +181,7 @@ void MobileVRInterface::set_position_from_sensors() { orientation = rotate * orientation; tracking_state = XRInterface::XR_NORMAL_TRACKING; + tracking_confidence = XRPose::XR_TRACKING_CONFIDENCE_HIGH; }; ///@TODO improve this, the magnetometer is very fidgety sometimes flipping the axis for no apparent reason (probably a bug on my part) @@ -193,6 +194,7 @@ void MobileVRInterface::set_position_from_sensors() { orientation = Basis(transform_quat); tracking_state = XRInterface::XR_NORMAL_TRACKING; + tracking_confidence = XRPose::XR_TRACKING_CONFIDENCE_HIGH; } else if (has_grav) { // use gravity vector to make sure down is down... // transform gravity into our world space @@ -512,7 +514,7 @@ void MobileVRInterface::process() { if (head.is_valid()) { // Set our head position, note in real space, reference frame and world scale is applied later - head->set_pose("default", head_transform, Vector3(), Vector3()); + head->set_pose("default", head_transform, Vector3(), Vector3(), tracking_confidence); } }; }; diff --git a/modules/mobile_vr/mobile_vr_interface.h b/modules/mobile_vr/mobile_vr_interface.h index ac04763569..47dc33c0c7 100644 --- a/modules/mobile_vr/mobile_vr_interface.h +++ b/modules/mobile_vr/mobile_vr_interface.h @@ -51,6 +51,7 @@ class MobileVRInterface : public XRInterface { private: bool initialized = false; XRInterface::TrackingStatus tracking_state; + XRPose::TrackingConfidence tracking_confidence = XRPose::XR_TRACKING_CONFIDENCE_NONE; // Just set some defaults for these. At some point we need to look at adding a lookup table for common device + headset combos and/or support reading cardboard QR codes double eye_height = 1.85; diff --git a/modules/navigation/navigation_mesh_generator.cpp b/modules/navigation/navigation_mesh_generator.cpp index 6cbdb645ca..77fe9cdd19 100644 --- a/modules/navigation/navigation_mesh_generator.cpp +++ b/modules/navigation/navigation_mesh_generator.cpp @@ -265,14 +265,100 @@ void NavigationMeshGenerator::_parse_geometry(Transform3D p_accumulated_transfor } #ifdef MODULE_GRIDMAP_ENABLED - if (Object::cast_to<GridMap>(p_node) && p_generate_from != NavigationMesh::PARSED_GEOMETRY_STATIC_COLLIDERS) { - GridMap *gridmap_instance = Object::cast_to<GridMap>(p_node); - Array meshes = gridmap_instance->get_meshes(); - Transform3D xform = gridmap_instance->get_transform(); - for (int i = 0; i < meshes.size(); i += 2) { - Ref<Mesh> mesh = meshes[i + 1]; - if (mesh.is_valid()) { - _add_mesh(mesh, p_accumulated_transform * xform * (Transform3D)meshes[i], p_vertices, p_indices); + GridMap *gridmap = Object::cast_to<GridMap>(p_node); + + if (gridmap) { + if (p_generate_from != NavigationMesh::PARSED_GEOMETRY_STATIC_COLLIDERS) { + Array meshes = gridmap->get_meshes(); + Transform3D xform = gridmap->get_transform(); + for (int i = 0; i < meshes.size(); i += 2) { + Ref<Mesh> mesh = meshes[i + 1]; + if (mesh.is_valid()) { + _add_mesh(mesh, p_accumulated_transform * xform * (Transform3D)meshes[i], p_vertices, p_indices); + } + } + } + + if (p_generate_from != NavigationMesh::PARSED_GEOMETRY_MESH_INSTANCES && (gridmap->get_collision_layer() & p_collision_mask)) { + Array shapes = gridmap->get_collision_shapes(); + for (int i = 0; i < shapes.size(); i += 2) { + RID shape = shapes[i + 1]; + PhysicsServer3D::ShapeType type = PhysicsServer3D::get_singleton()->shape_get_type(shape); + Variant data = PhysicsServer3D::get_singleton()->shape_get_data(shape); + Ref<Mesh> mesh; + + switch (type) { + case PhysicsServer3D::SHAPE_SPHERE: { + real_t radius = data; + Ref<SphereMesh> sphere_mesh; + sphere_mesh.instantiate(); + sphere_mesh->set_radius(radius); + sphere_mesh->set_height(radius * 2.0); + mesh = sphere_mesh; + } break; + case PhysicsServer3D::SHAPE_BOX: { + Vector3 extents = data; + Ref<BoxMesh> box_mesh; + box_mesh.instantiate(); + box_mesh->set_size(2.0 * extents); + mesh = box_mesh; + } break; + case PhysicsServer3D::SHAPE_CAPSULE: { + Dictionary dict = data; + real_t radius = dict["radius"]; + real_t height = dict["height"]; + Ref<CapsuleMesh> capsule_mesh; + capsule_mesh.instantiate(); + capsule_mesh->set_radius(radius); + capsule_mesh->set_height(height); + mesh = capsule_mesh; + } break; + case PhysicsServer3D::SHAPE_CYLINDER: { + Dictionary dict = data; + real_t radius = dict["radius"]; + real_t height = dict["height"]; + Ref<CylinderMesh> cylinder_mesh; + cylinder_mesh.instantiate(); + cylinder_mesh->set_height(height); + cylinder_mesh->set_bottom_radius(radius); + cylinder_mesh->set_top_radius(radius); + mesh = cylinder_mesh; + } break; + case PhysicsServer3D::SHAPE_CONVEX_POLYGON: { + PackedVector3Array vertices = data; + Geometry3D::MeshData md; + + Error err = ConvexHullComputer::convex_hull(vertices, md); + + if (err == OK) { + PackedVector3Array faces; + + for (int j = 0; j < md.faces.size(); ++j) { + Geometry3D::MeshData::Face face = md.faces[j]; + + for (int k = 2; k < face.indices.size(); ++k) { + faces.push_back(md.vertices[face.indices[0]]); + faces.push_back(md.vertices[face.indices[k - 1]]); + faces.push_back(md.vertices[face.indices[k]]); + } + } + + _add_faces(faces, shapes[i], p_vertices, p_indices); + } + } break; + case PhysicsServer3D::SHAPE_CONCAVE_POLYGON: { + Dictionary dict = data; + PackedVector3Array faces = Variant(dict["faces"]); + _add_faces(faces, shapes[i], p_vertices, p_indices); + } break; + default: { + WARN_PRINT("Unsupported collision shape type."); + } break; + } + + if (mesh.is_valid()) { + _add_mesh(mesh, shapes[i], p_vertices, p_indices); + } } } } diff --git a/platform/android/display_server_android.cpp b/platform/android/display_server_android.cpp index 941cf9fd53..15f61db27c 100644 --- a/platform/android/display_server_android.cpp +++ b/platform/android/display_server_android.cpp @@ -95,6 +95,17 @@ String DisplayServerAndroid::clipboard_get() const { } } +bool DisplayServerAndroid::clipboard_has() const { + GodotJavaWrapper *godot_java = OS_Android::get_singleton()->get_godot_java(); + ERR_FAIL_COND_V(!godot_java, false); + + if (godot_java->has_has_clipboard()) { + return godot_java->has_clipboard(); + } else { + return DisplayServer::clipboard_has(); + } +} + void DisplayServerAndroid::screen_set_keep_on(bool p_enable) { GodotJavaWrapper *godot_java = OS_Android::get_singleton()->get_godot_java(); ERR_FAIL_COND(!godot_java); diff --git a/platform/android/display_server_android.h b/platform/android/display_server_android.h index 8538b6e660..6aadc7e1a9 100644 --- a/platform/android/display_server_android.h +++ b/platform/android/display_server_android.h @@ -93,6 +93,7 @@ public: virtual void clipboard_set(const String &p_text) override; virtual String clipboard_get() const override; + virtual bool clipboard_has() const override; virtual void screen_set_keep_on(bool p_enable) override; virtual bool screen_is_kept_on() const override; diff --git a/platform/android/java/lib/src/org/godotengine/godot/Godot.java b/platform/android/java/lib/src/org/godotengine/godot/Godot.java index 3fbbd8fbcc..78848c109a 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/Godot.java +++ b/platform/android/java/lib/src/org/godotengine/godot/Godot.java @@ -660,10 +660,14 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC } } + public boolean hasClipboard() { + return mClipboard.hasPrimaryClip(); + } + public String getClipboard() { String copiedText = ""; - if (mClipboard.getPrimaryClip() != null) { + if (mClipboard.hasPrimaryClip()) { ClipData.Item item = mClipboard.getPrimaryClip().getItemAt(0); copiedText = item.getText().toString(); } diff --git a/platform/android/java_godot_wrapper.cpp b/platform/android/java_godot_wrapper.cpp index 115264d7ee..5beec6b611 100644 --- a/platform/android/java_godot_wrapper.cpp +++ b/platform/android/java_godot_wrapper.cpp @@ -66,6 +66,7 @@ GodotJavaWrapper::GodotJavaWrapper(JNIEnv *p_env, jobject p_activity, jobject p_ _get_GLES_version_code = p_env->GetMethodID(godot_class, "getGLESVersionCode", "()I"); _get_clipboard = p_env->GetMethodID(godot_class, "getClipboard", "()Ljava/lang/String;"); _set_clipboard = p_env->GetMethodID(godot_class, "setClipboard", "(Ljava/lang/String;)V"); + _has_clipboard = p_env->GetMethodID(godot_class, "hasClipboard", "()Z"); _request_permission = p_env->GetMethodID(godot_class, "requestPermission", "(Ljava/lang/String;)Z"); _request_permissions = p_env->GetMethodID(godot_class, "requestPermissions", "()Z"); _get_granted_permissions = p_env->GetMethodID(godot_class, "getGrantedPermissions", "()[Ljava/lang/String;"); @@ -248,6 +249,21 @@ void GodotJavaWrapper::set_clipboard(const String &p_text) { } } +bool GodotJavaWrapper::has_has_clipboard() { + return _has_clipboard != 0; +} + +bool GodotJavaWrapper::has_clipboard() { + if (_has_clipboard) { + JNIEnv *env = get_jni_env(); + ERR_FAIL_COND_V(env == nullptr, false); + + return env->CallBooleanMethod(godot_instance, _has_clipboard); + } else { + return false; + } +} + bool GodotJavaWrapper::request_permission(const String &p_name) { if (_request_permission) { JNIEnv *env = get_jni_env(); diff --git a/platform/android/java_godot_wrapper.h b/platform/android/java_godot_wrapper.h index 4d058ac426..42ae91480f 100644 --- a/platform/android/java_godot_wrapper.h +++ b/platform/android/java_godot_wrapper.h @@ -58,6 +58,7 @@ private: jmethodID _get_GLES_version_code = 0; jmethodID _get_clipboard = 0; jmethodID _set_clipboard = 0; + jmethodID _has_clipboard = 0; jmethodID _request_permission = 0; jmethodID _request_permissions = 0; jmethodID _get_granted_permissions = 0; @@ -92,6 +93,8 @@ public: String get_clipboard(); bool has_set_clipboard(); void set_clipboard(const String &p_text); + bool has_has_clipboard(); + bool has_clipboard(); bool request_permission(const String &p_name); bool request_permissions(); Vector<String> get_granted_permissions() const; diff --git a/platform/osx/display_server_osx.mm b/platform/osx/display_server_osx.mm index e3b4333ec8..60f1eac4b1 100644 --- a/platform/osx/display_server_osx.mm +++ b/platform/osx/display_server_osx.mm @@ -316,7 +316,7 @@ static NSCursor *_cursorFromSelector(SEL selector, SEL fallback = nil) { CGPoint lMouseWarpPos = { pointOnScreen.x, CGDisplayBounds(CGMainDisplayID()).size.height - pointOnScreen.y }; CGWarpMouseCursorPosition(lMouseWarpPos); } else { - _get_mouse_pos(wd, [wd.window_object mouseLocationOutsideOfEventStream]); + _ALLOW_DISCARD_ _get_mouse_pos(wd, [wd.window_object mouseLocationOutsideOfEventStream]); Input::get_singleton()->set_mouse_position(wd.mouse_pos); } @@ -1391,7 +1391,7 @@ inline void sendPanEvent(DisplayServer::WindowID window_id, double dx, double dy double deltaX, deltaY; - _get_mouse_pos(wd, [event locationInWindow]); + _ALLOW_DISCARD_ _get_mouse_pos(wd, [event locationInWindow]); deltaX = [event scrollingDeltaX]; deltaY = [event scrollingDeltaY]; @@ -2463,7 +2463,7 @@ void DisplayServerOSX::window_set_position(const Point2i &p_position, WindowID p [wd.window_object setFrameTopLeftPoint:NSMakePoint(position.x - offset.x, position.y - offset.y)]; _update_window(wd); - _get_mouse_pos(wd, [wd.window_object mouseLocationOutsideOfEventStream]); + _ALLOW_DISCARD_ _get_mouse_pos(wd, [wd.window_object mouseLocationOutsideOfEventStream]); } void DisplayServerOSX::window_set_max_size(const Size2i p_size, WindowID p_window) { diff --git a/scene/3d/light_3d.cpp b/scene/3d/light_3d.cpp index 0a0507207a..d88bb815bc 100644 --- a/scene/3d/light_3d.cpp +++ b/scene/3d/light_3d.cpp @@ -247,7 +247,7 @@ void Light3D::_bind_methods() { ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "light_angular_distance", PROPERTY_HINT_RANGE, "0,90,0.01"), "set_param", "get_param", PARAM_SIZE); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "light_negative"), "set_negative", "is_negative"); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "light_specular", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param", "get_param", PARAM_SPECULAR); - ADD_PROPERTY(PropertyInfo(Variant::INT, "light_bake_mode", PROPERTY_HINT_ENUM, "Disabled,Dynamic,Static"), "set_bake_mode", "get_bake_mode"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "light_bake_mode", PROPERTY_HINT_ENUM, "Disabled,Static (VoxelGI/SDFGI/LightmapGI),Dynamic (VoxelGI/SDFGI only)"), "set_bake_mode", "get_bake_mode"); ADD_PROPERTY(PropertyInfo(Variant::INT, "light_cull_mask", PROPERTY_HINT_LAYERS_3D_RENDER), "set_cull_mask", "get_cull_mask"); ADD_GROUP("Shadow", "shadow_"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "shadow_enabled"), "set_shadow", "has_shadow"); @@ -284,8 +284,8 @@ void Light3D::_bind_methods() { BIND_ENUM_CONSTANT(PARAM_MAX); BIND_ENUM_CONSTANT(BAKE_DISABLED); - BIND_ENUM_CONSTANT(BAKE_DYNAMIC); BIND_ENUM_CONSTANT(BAKE_STATIC); + BIND_ENUM_CONSTANT(BAKE_DYNAMIC); } Light3D::Light3D(RenderingServer::LightType p_type) { diff --git a/scene/3d/light_3d.h b/scene/3d/light_3d.h index 93dc8155bb..d5d2aee43d 100644 --- a/scene/3d/light_3d.h +++ b/scene/3d/light_3d.h @@ -63,8 +63,8 @@ public: enum BakeMode { BAKE_DISABLED, + BAKE_STATIC, BAKE_DYNAMIC, - BAKE_STATIC }; private: diff --git a/scene/3d/lightmap_gi.cpp b/scene/3d/lightmap_gi.cpp index 715c421632..825742da35 100644 --- a/scene/3d/lightmap_gi.cpp +++ b/scene/3d/lightmap_gi.cpp @@ -217,7 +217,7 @@ LightmapGIData::~LightmapGIData() { void LightmapGI::_find_meshes_and_lights(Node *p_at_node, Vector<MeshesFound> &meshes, Vector<LightsFound> &lights, Vector<Vector3> &probes) { MeshInstance3D *mi = Object::cast_to<MeshInstance3D>(p_at_node); - if (mi && mi->get_gi_mode() == GeometryInstance3D::GI_MODE_BAKED && mi->is_visible_in_tree()) { + if (mi && mi->get_gi_mode() == GeometryInstance3D::GI_MODE_STATIC && mi->is_visible_in_tree()) { Ref<Mesh> mesh = mi->get_mesh(); if (mesh.is_valid()) { bool all_have_uv2_and_normal = true; diff --git a/scene/3d/visual_instance_3d.cpp b/scene/3d/visual_instance_3d.cpp index 0db2fe9fc6..005bb5a737 100644 --- a/scene/3d/visual_instance_3d.cpp +++ b/scene/3d/visual_instance_3d.cpp @@ -247,7 +247,7 @@ bool GeometryInstance3D::_set(const StringName &p_name, const Variant &p_value) } #ifndef DISABLE_DEPRECATED if (p_name == SceneStringNames::get_singleton()->use_in_baked_light && bool(p_value)) { - set_gi_mode(GI_MODE_BAKED); + set_gi_mode(GI_MODE_STATIC); return true; } @@ -358,7 +358,7 @@ void GeometryInstance3D::set_gi_mode(GIMode p_mode) { RS::get_singleton()->instance_geometry_set_flag(get_instance(), RS::INSTANCE_FLAG_USE_BAKED_LIGHT, false); RS::get_singleton()->instance_geometry_set_flag(get_instance(), RS::INSTANCE_FLAG_USE_DYNAMIC_GI, false); } break; - case GI_MODE_BAKED: { + case GI_MODE_STATIC: { RS::get_singleton()->instance_geometry_set_flag(get_instance(), RS::INSTANCE_FLAG_USE_BAKED_LIGHT, true); RS::get_singleton()->instance_geometry_set_flag(get_instance(), RS::INSTANCE_FLAG_USE_DYNAMIC_GI, false); @@ -462,7 +462,7 @@ void GeometryInstance3D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "lod_bias", PROPERTY_HINT_RANGE, "0.001,128,0.001"), "set_lod_bias", "get_lod_bias"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "ignore_occlusion_culling"), "set_ignore_occlusion_culling", "is_ignoring_occlusion_culling"); ADD_GROUP("Global Illumination", "gi_"); - 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_mode", PROPERTY_HINT_ENUM, "Disabled,Static (VoxelGI/SDFGI/LightmapGI),Dynamic (VoxelGI only)"), "set_gi_mode", "get_gi_mode"); ADD_PROPERTY(PropertyInfo(Variant::INT, "gi_lightmap_scale", PROPERTY_HINT_ENUM, String::utf8("1×,2×,4×,8×")), "set_lightmap_scale", "get_lightmap_scale"); ADD_GROUP("Visibility Range", "visibility_range_"); @@ -479,7 +479,7 @@ void GeometryInstance3D::_bind_methods() { BIND_ENUM_CONSTANT(SHADOW_CASTING_SETTING_SHADOWS_ONLY); BIND_ENUM_CONSTANT(GI_MODE_DISABLED); - BIND_ENUM_CONSTANT(GI_MODE_BAKED); + BIND_ENUM_CONSTANT(GI_MODE_STATIC); BIND_ENUM_CONSTANT(GI_MODE_DYNAMIC); BIND_ENUM_CONSTANT(LIGHTMAP_SCALE_1X); diff --git a/scene/3d/visual_instance_3d.h b/scene/3d/visual_instance_3d.h index dd0eb25001..be964e5080 100644 --- a/scene/3d/visual_instance_3d.h +++ b/scene/3d/visual_instance_3d.h @@ -89,7 +89,7 @@ public: enum GIMode { GI_MODE_DISABLED, - GI_MODE_BAKED, + GI_MODE_STATIC, GI_MODE_DYNAMIC }; diff --git a/scene/3d/voxel_gi.cpp b/scene/3d/voxel_gi.cpp index 35ac1792e9..bfe3c80a4f 100644 --- a/scene/3d/voxel_gi.cpp +++ b/scene/3d/voxel_gi.cpp @@ -282,7 +282,7 @@ Vector3 VoxelGI::get_extents() const { void VoxelGI::_find_meshes(Node *p_at_node, List<PlotMesh> &plot_meshes) { MeshInstance3D *mi = Object::cast_to<MeshInstance3D>(p_at_node); - if (mi && mi->get_gi_mode() == GeometryInstance3D::GI_MODE_BAKED && mi->is_visible_in_tree()) { + if (mi && mi->get_gi_mode() == GeometryInstance3D::GI_MODE_STATIC && mi->is_visible_in_tree()) { Ref<Mesh> mesh = mi->get_mesh(); if (mesh.is_valid()) { AABB aabb = mesh->get_aabb(); diff --git a/scene/gui/popup_menu.cpp b/scene/gui/popup_menu.cpp index d7139d0140..c9fddc3e17 100644 --- a/scene/gui/popup_menu.cpp +++ b/scene/gui/popup_menu.cpp @@ -1403,12 +1403,12 @@ void PopupMenu::activate_item(int p_item) { need_hide = false; } - emit_signal(SNAME("id_pressed"), id); - emit_signal(SNAME("index_pressed"), p_item); - if (need_hide) { hide(); } + + emit_signal(SNAME("id_pressed"), id); + emit_signal(SNAME("index_pressed"), p_item); } void PopupMenu::remove_item(int p_idx) { diff --git a/scene/gui/tree.cpp b/scene/gui/tree.cpp index e46de43f1e..e75d147134 100644 --- a/scene/gui/tree.cpp +++ b/scene/gui/tree.cpp @@ -198,6 +198,65 @@ bool TreeItem::is_indeterminate(int p_column) const { return cells[p_column].indeterminate; } +void TreeItem::propagate_check(int p_column, bool p_emit_signal) { + bool ch = cells[p_column].checked; + + if (p_emit_signal) { + tree->emit_signal("check_propagated_to_item", this, p_column); + } + _propagate_check_through_children(p_column, ch, p_emit_signal); + _propagate_check_through_parents(p_column, p_emit_signal); +} + +void TreeItem::_propagate_check_through_children(int p_column, bool p_checked, bool p_emit_signal) { + TreeItem *current = get_first_child(); + while (current) { + current->set_checked(p_column, p_checked); + if (p_emit_signal) { + current->tree->emit_signal("check_propagated_to_item", current, p_column); + } + current->_propagate_check_through_children(p_column, p_checked, p_emit_signal); + current = current->get_next(); + } +} + +void TreeItem::_propagate_check_through_parents(int p_column, bool p_emit_signal) { + TreeItem *current = get_parent(); + if (!current) { + return; + } + + bool all_unchecked_and_not_indeterminate = true; + bool any_unchecked_or_indeterminate = false; + + TreeItem *child_item = current->get_first_child(); + while (child_item) { + if (!child_item->is_checked(p_column)) { + any_unchecked_or_indeterminate = true; + if (child_item->is_indeterminate(p_column)) { + all_unchecked_and_not_indeterminate = false; + break; + } + } else { + all_unchecked_and_not_indeterminate = false; + } + child_item = child_item->get_next(); + } + + if (all_unchecked_and_not_indeterminate) { + current->set_checked(p_column, false); + } else if (any_unchecked_or_indeterminate) { + current->set_indeterminate(p_column, true); + } else { + current->set_checked(p_column, true); + } + + if (p_emit_signal) { + current->tree->emit_signal("check_propagated_to_item", current, p_column); + } + current->_propagate_check_through_parents(p_column, p_emit_signal); +} + void TreeItem::set_text(int p_column, String p_text) { ERR_FAIL_INDEX(p_column, cells.size()); cells.write[p_column].text = p_text; @@ -1141,6 +1200,8 @@ void TreeItem::_bind_methods() { ClassDB::bind_method(D_METHOD("is_checked", "column"), &TreeItem::is_checked); ClassDB::bind_method(D_METHOD("is_indeterminate", "column"), &TreeItem::is_indeterminate); + ClassDB::bind_method(D_METHOD("propagate_check", "column", "emit_signal"), &TreeItem::propagate_check, DEFVAL(true)); + ClassDB::bind_method(D_METHOD("set_text", "column", "text"), &TreeItem::set_text); ClassDB::bind_method(D_METHOD("get_text", "column"), &TreeItem::get_text); @@ -4847,6 +4908,7 @@ void Tree::_bind_methods() { ADD_SIGNAL(MethodInfo("item_custom_button_pressed")); ADD_SIGNAL(MethodInfo("item_double_clicked")); ADD_SIGNAL(MethodInfo("item_collapsed", PropertyInfo(Variant::OBJECT, "item", PROPERTY_HINT_RESOURCE_TYPE, "TreeItem"))); + ADD_SIGNAL(MethodInfo("check_propagated_to_item", PropertyInfo(Variant::OBJECT, "item", PROPERTY_HINT_RESOURCE_TYPE, "TreeItem"), PropertyInfo(Variant::INT, "column"))); //ADD_SIGNAL( MethodInfo("item_double_clicked" ) ); ADD_SIGNAL(MethodInfo("button_pressed", PropertyInfo(Variant::OBJECT, "item", PROPERTY_HINT_RESOURCE_TYPE, "TreeItem"), PropertyInfo(Variant::INT, "column"), PropertyInfo(Variant::INT, "id"))); ADD_SIGNAL(MethodInfo("custom_popup_edited", PropertyInfo(Variant::BOOL, "arrow_clicked"))); diff --git a/scene/gui/tree.h b/scene/gui/tree.h index c60c87564e..33170cad35 100644 --- a/scene/gui/tree.h +++ b/scene/gui/tree.h @@ -212,6 +212,14 @@ public: bool is_checked(int p_column) const; bool is_indeterminate(int p_column) const; + void propagate_check(int p_column, bool p_emit_signal = true); + +private: + // Check helpers. + void _propagate_check_through_children(int p_column, bool p_checked, bool p_emit_signal); + void _propagate_check_through_parents(int p_column, bool p_emit_signal); + +public: void set_text(int p_column, String p_text); String get_text(int p_column) const; diff --git a/scene/main/canvas_item.cpp b/scene/main/canvas_item.cpp index 59d933fa1d..a0916c6291 100644 --- a/scene/main/canvas_item.cpp +++ b/scene/main/canvas_item.cpp @@ -1017,8 +1017,8 @@ void CanvasItem::set_notify_transform(bool p_enable) { notify_transform = p_enable; if (notify_transform && is_inside_tree()) { - //this ensures that invalid globals get resolved, so notifications can be received - get_global_transform(); + // This ensures that invalid globals get resolved, so notifications can be received. + _ALLOW_DISCARD_ get_global_transform(); } } diff --git a/scene/main/node.cpp b/scene/main/node.cpp index 9d96c71113..416dec3e4f 100644 --- a/scene/main/node.cpp +++ b/scene/main/node.cpp @@ -1316,12 +1316,14 @@ Node *Node::get_node_or_null(const NodePath &p_path) const { Node *Node::get_node(const NodePath &p_path) const { Node *node = get_node_or_null(p_path); - if (p_path.is_absolute()) { - ERR_FAIL_COND_V_MSG(!node, nullptr, - vformat(R"(Node not found: "%s" (absolute path attempted from "%s").)", p_path, get_path())); - } else { - ERR_FAIL_COND_V_MSG(!node, nullptr, - vformat(R"(Node not found: "%s" (relative to "%s").)", p_path, get_path())); + if (unlikely(!node)) { + if (p_path.is_absolute()) { + ERR_FAIL_V_MSG(nullptr, + vformat(R"(Node not found: "%s" (absolute path attempted from "%s").)", p_path, get_path())); + } else { + ERR_FAIL_V_MSG(nullptr, + vformat(R"(Node not found: "%s" (relative to "%s").)", p_path, get_path())); + } } return node; diff --git a/scene/resources/immediate_mesh.cpp b/scene/resources/immediate_mesh.cpp index b9469803a0..28afef8638 100644 --- a/scene/resources/immediate_mesh.cpp +++ b/scene/resources/immediate_mesh.cpp @@ -382,7 +382,7 @@ AABB ImmediateMesh::get_aabb() const { if (i == 0) { aabb = surfaces[i].aabb; } else { - aabb.merge(surfaces[i].aabb); + aabb = aabb.merge(surfaces[i].aabb); } } return aabb; diff --git a/servers/display_server.cpp b/servers/display_server.cpp index eb12b5ffc0..d4ff42bc34 100644 --- a/servers/display_server.cpp +++ b/servers/display_server.cpp @@ -155,6 +155,10 @@ String DisplayServer::clipboard_get() const { ERR_FAIL_V_MSG(String(), "Clipboard is not supported by this display server."); } +bool DisplayServer::clipboard_has() const { + return !clipboard_get().is_empty(); +} + void DisplayServer::clipboard_set_primary(const String &p_text) { WARN_PRINT("Primary clipboard is not supported by this display server."); } @@ -359,6 +363,7 @@ void DisplayServer::_bind_methods() { ClassDB::bind_method(D_METHOD("clipboard_set", "clipboard"), &DisplayServer::clipboard_set); ClassDB::bind_method(D_METHOD("clipboard_get"), &DisplayServer::clipboard_get); + ClassDB::bind_method(D_METHOD("clipboard_has"), &DisplayServer::clipboard_has); ClassDB::bind_method(D_METHOD("clipboard_set_primary", "clipboard_primary"), &DisplayServer::clipboard_set_primary); ClassDB::bind_method(D_METHOD("clipboard_get_primary"), &DisplayServer::clipboard_get_primary); diff --git a/servers/display_server.h b/servers/display_server.h index 2fb9b5946b..0c2bc5dd3a 100644 --- a/servers/display_server.h +++ b/servers/display_server.h @@ -160,6 +160,7 @@ public: virtual void clipboard_set(const String &p_text); virtual String clipboard_get() const; + virtual bool clipboard_has() const; virtual void clipboard_set_primary(const String &p_text); virtual String clipboard_get_primary() const; diff --git a/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp b/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp index 33f5a178e0..126f40584a 100644 --- a/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp +++ b/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp @@ -514,7 +514,7 @@ Ref<Image> RendererSceneRenderRD::environment_bake_panorama(RID p_env, bool p_ba ambient_color_sky_mix = env->ambient_sky_contribution; const float ambient_energy = env->ambient_light_energy; ambient_color = env->ambient_light; - ambient_color.to_linear(); + ambient_color = ambient_color.to_linear(); ambient_color.r *= ambient_energy; ambient_color.g *= ambient_energy; ambient_color.b *= ambient_energy; @@ -533,7 +533,7 @@ Ref<Image> RendererSceneRenderRD::environment_bake_panorama(RID p_env, bool p_ba } else { const float bg_energy = env->bg_energy; Color panorama_color = ((environment_background == RS::ENV_BG_CLEAR_COLOR) ? storage->get_default_clear_color() : env->bg_color); - panorama_color.to_linear(); + panorama_color = panorama_color.to_linear(); panorama_color.r *= bg_energy; panorama_color.g *= bg_energy; panorama_color.b *= bg_energy; diff --git a/servers/rendering/renderer_rd/shaders/light_data_inc.glsl b/servers/rendering/renderer_rd/shaders/light_data_inc.glsl index fdc7729338..52787bb204 100644 --- a/servers/rendering/renderer_rd/shaders/light_data_inc.glsl +++ b/servers/rendering/renderer_rd/shaders/light_data_inc.glsl @@ -1,6 +1,6 @@ #define LIGHT_BAKE_DISABLED 0 -#define LIGHT_BAKE_DYNAMIC 1 -#define LIGHT_BAKE_STATIC 2 +#define LIGHT_BAKE_STATIC 1 +#define LIGHT_BAKE_DYNAMIC 2 struct LightData { //this structure needs to be as packed as possible highp vec3 position; diff --git a/servers/rendering/rendering_device.cpp b/servers/rendering/rendering_device.cpp index 46fb3c8537..6fc5d0b3e8 100644 --- a/servers/rendering/rendering_device.cpp +++ b/servers/rendering/rendering_device.cpp @@ -451,7 +451,7 @@ void RenderingDevice::_bind_methods() { ClassDB::bind_method(D_METHOD("compute_list_add_barrier", "compute_list"), &RenderingDevice::compute_list_add_barrier); ClassDB::bind_method(D_METHOD("compute_list_end", "post_barrier"), &RenderingDevice::compute_list_end, DEFVAL(BARRIER_MASK_ALL)); - ClassDB::bind_method(D_METHOD("free", "rid"), &RenderingDevice::free); + ClassDB::bind_method(D_METHOD("free_rid", "rid"), &RenderingDevice::free); ClassDB::bind_method(D_METHOD("capture_timestamp", "name"), &RenderingDevice::capture_timestamp); ClassDB::bind_method(D_METHOD("get_captured_timestamps_count"), &RenderingDevice::get_captured_timestamps_count); diff --git a/servers/rendering_server.cpp b/servers/rendering_server.cpp index 863aae6e4c..21cc1017ae 100644 --- a/servers/rendering_server.cpp +++ b/servers/rendering_server.cpp @@ -1927,8 +1927,8 @@ void RenderingServer::_bind_methods() { BIND_ENUM_CONSTANT(LIGHT_PARAM_MAX); BIND_ENUM_CONSTANT(LIGHT_BAKE_DISABLED); - BIND_ENUM_CONSTANT(LIGHT_BAKE_DYNAMIC); BIND_ENUM_CONSTANT(LIGHT_BAKE_STATIC); + BIND_ENUM_CONSTANT(LIGHT_BAKE_DYNAMIC); BIND_ENUM_CONSTANT(LIGHT_OMNI_SHADOW_DUAL_PARABOLOID); BIND_ENUM_CONSTANT(LIGHT_OMNI_SHADOW_CUBE); diff --git a/servers/rendering_server.h b/servers/rendering_server.h index 945fd052c6..b067db3d27 100644 --- a/servers/rendering_server.h +++ b/servers/rendering_server.h @@ -447,8 +447,8 @@ public: enum LightBakeMode { LIGHT_BAKE_DISABLED, - LIGHT_BAKE_DYNAMIC, LIGHT_BAKE_STATIC, + LIGHT_BAKE_DYNAMIC, }; virtual void light_set_bake_mode(RID p_light, LightBakeMode p_bake_mode) = 0; diff --git a/servers/xr/xr_pose.cpp b/servers/xr/xr_pose.cpp index 0862fefef5..400f13b9c2 100644 --- a/servers/xr/xr_pose.cpp +++ b/servers/xr/xr_pose.cpp @@ -33,6 +33,10 @@ #include "servers/xr_server.h" void XRPose::_bind_methods() { + BIND_ENUM_CONSTANT(XR_TRACKING_CONFIDENCE_NONE); + BIND_ENUM_CONSTANT(XR_TRACKING_CONFIDENCE_LOW); + BIND_ENUM_CONSTANT(XR_TRACKING_CONFIDENCE_HIGH); + ClassDB::bind_method(D_METHOD("set_has_tracking_data", "has_tracking_data"), &XRPose::set_has_tracking_data); ClassDB::bind_method(D_METHOD("get_has_tracking_data"), &XRPose::get_has_tracking_data); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "has_tracking_data"), "set_has_tracking_data", "get_has_tracking_data"); @@ -53,6 +57,10 @@ void XRPose::_bind_methods() { ClassDB::bind_method(D_METHOD("set_angular_velocity", "velocity"), &XRPose::set_angular_velocity); ClassDB::bind_method(D_METHOD("get_angular_velocity"), &XRPose::get_angular_velocity); ADD_PROPERTY(PropertyInfo(Variant::STRING, "angular_velocity"), "set_angular_velocity", "get_angular_velocity"); + + ClassDB::bind_method(D_METHOD("set_tracking_confidence", "tracking_confidence"), &XRPose::set_tracking_confidence); + ClassDB::bind_method(D_METHOD("get_tracking_confidence"), &XRPose::get_tracking_confidence); + ADD_PROPERTY(PropertyInfo(Variant::INT, "tracking_confidence"), "set_tracking_confidence", "get_tracking_confidence"); } void XRPose::set_has_tracking_data(const bool p_has_tracking_data) { @@ -108,3 +116,11 @@ void XRPose::set_angular_velocity(const Vector3 p_velocity) { Vector3 XRPose::get_angular_velocity() const { return angular_velocity; } + +void XRPose::set_tracking_confidence(const XRPose::TrackingConfidence p_tracking_confidence) { + tracking_confidence = p_tracking_confidence; +} + +XRPose::TrackingConfidence XRPose::get_tracking_confidence() const { + return tracking_confidence; +} diff --git a/servers/xr/xr_pose.h b/servers/xr/xr_pose.h index 8c1fc63360..f306c22390 100644 --- a/servers/xr/xr_pose.h +++ b/servers/xr/xr_pose.h @@ -37,12 +37,20 @@ class XRPose : public RefCounted { GDCLASS(XRPose, RefCounted); public: + // TrackingConfidence gives an indication of how reliable our transform data is. + enum TrackingConfidence { + XR_TRACKING_CONFIDENCE_NONE, // No tracking information is available for this pose. + XR_TRACKING_CONFIDENCE_LOW, // Tracking information may be inaccurate or estimated. + XR_TRACKING_CONFIDENCE_HIGH // Tracking information is deemed accurate and up to date. + }; + private: bool has_tracking_data = false; StringName name; Transform3D transform; Vector3 linear_velocity; Vector3 angular_velocity; + TrackingConfidence tracking_confidence = XR_TRACKING_CONFIDENCE_NONE; protected: static void _bind_methods(); @@ -63,6 +71,11 @@ public: void set_angular_velocity(const Vector3 p_velocity); Vector3 get_angular_velocity() const; + + void set_tracking_confidence(const TrackingConfidence p_tracking_confidence); + TrackingConfidence get_tracking_confidence() const; }; +VARIANT_ENUM_CAST(XRPose::TrackingConfidence); + #endif diff --git a/servers/xr/xr_positional_tracker.cpp b/servers/xr/xr_positional_tracker.cpp index 74907a5675..62e011654e 100644 --- a/servers/xr/xr_positional_tracker.cpp +++ b/servers/xr/xr_positional_tracker.cpp @@ -56,7 +56,7 @@ void XRPositionalTracker::_bind_methods() { ClassDB::bind_method(D_METHOD("has_pose", "name"), &XRPositionalTracker::has_pose); ClassDB::bind_method(D_METHOD("get_pose", "name"), &XRPositionalTracker::get_pose); ClassDB::bind_method(D_METHOD("invalidate_pose", "name"), &XRPositionalTracker::invalidate_pose); - ClassDB::bind_method(D_METHOD("set_pose", "name", "transform", "linear_velocity", "angular_velocity"), &XRPositionalTracker::set_pose); + ClassDB::bind_method(D_METHOD("set_pose", "name", "transform", "linear_velocity", "angular_velocity", "tracking_confidence"), &XRPositionalTracker::set_pose); ADD_SIGNAL(MethodInfo("pose_changed", PropertyInfo(Variant::OBJECT, "pose", PROPERTY_HINT_RESOURCE_TYPE, "XRPose"))); ClassDB::bind_method(D_METHOD("get_input", "name"), &XRPositionalTracker::get_input); @@ -137,7 +137,7 @@ void XRPositionalTracker::invalidate_pose(const StringName &p_action_name) { } } -void XRPositionalTracker::set_pose(const StringName &p_action_name, const Transform3D &p_transform, const Vector3 &p_linear_velocity, const Vector3 &p_angular_velocity) { +void XRPositionalTracker::set_pose(const StringName &p_action_name, const Transform3D &p_transform, const Vector3 &p_linear_velocity, const Vector3 &p_angular_velocity, const XRPose::TrackingConfidence p_tracking_confidence) { Ref<XRPose> new_pose; new_pose.instantiate(); @@ -146,6 +146,7 @@ void XRPositionalTracker::set_pose(const StringName &p_action_name, const Transf new_pose->set_transform(p_transform); new_pose->set_linear_velocity(p_linear_velocity); new_pose->set_angular_velocity(p_angular_velocity); + new_pose->set_tracking_confidence(p_tracking_confidence); poses[p_action_name] = new_pose; emit_signal("pose_changed", new_pose); diff --git a/servers/xr/xr_positional_tracker.h b/servers/xr/xr_positional_tracker.h index 2bcbf2c018..2f358cbb21 100644 --- a/servers/xr/xr_positional_tracker.h +++ b/servers/xr/xr_positional_tracker.h @@ -82,7 +82,7 @@ public: bool has_pose(const StringName &p_action_name) const; Ref<XRPose> get_pose(const StringName &p_action_name) const; void invalidate_pose(const StringName &p_action_name); - void set_pose(const StringName &p_action_name, const Transform3D &p_transform, const Vector3 &p_linear_velocity, const Vector3 &p_angular_velocity); + void set_pose(const StringName &p_action_name, const Transform3D &p_transform, const Vector3 &p_linear_velocity, const Vector3 &p_angular_velocity, const XRPose::TrackingConfidence p_tracking_confidence = XRPose::XR_TRACKING_CONFIDENCE_HIGH); Variant get_input(const StringName &p_action_name) const; void set_input(const StringName &p_action_name, const Variant &p_value); diff --git a/thirdparty/thorvg/patches/thorvg-pr1166-vs2017-minmax.patch b/thirdparty/thorvg/patches/thorvg-pr1166-vs2017-minmax.patch new file mode 100644 index 0000000000..0b045bd05a --- /dev/null +++ b/thirdparty/thorvg/patches/thorvg-pr1166-vs2017-minmax.patch @@ -0,0 +1,49 @@ +diff --git a/thirdparty/thorvg/src/lib/sw_engine/tvgSwRenderer.cpp b/thirdparty/thorvg/src/lib/sw_engine/tvgSwRenderer.cpp +index 78537e7726..c75e73760e 100644 +--- a/thirdparty/thorvg/src/lib/sw_engine/tvgSwRenderer.cpp ++++ b/thirdparty/thorvg/src/lib/sw_engine/tvgSwRenderer.cpp +@@ -23,6 +23,7 @@ + #include "tvgSwCommon.h" + #include "tvgTaskScheduler.h" + #include "tvgSwRenderer.h" ++#include "tvgMath.h" + + /************************************************************************/ + /* Internal Class Implementation */ +@@ -594,10 +595,10 @@ void* SwRenderer::prepareCommon(SwTask* task, const RenderTransform* transform, + task->surface = surface; + task->mpool = mpool; + task->flags = flags; +- task->bbox.min.x = max(static_cast<SwCoord>(0), static_cast<SwCoord>(vport.x)); +- task->bbox.min.y = max(static_cast<SwCoord>(0), static_cast<SwCoord>(vport.y)); +- task->bbox.max.x = min(static_cast<SwCoord>(surface->w), static_cast<SwCoord>(vport.x + vport.w)); +- task->bbox.max.y = min(static_cast<SwCoord>(surface->h), static_cast<SwCoord>(vport.y + vport.h)); ++ task->bbox.min.x = mathMax(static_cast<SwCoord>(0), static_cast<SwCoord>(vport.x)); ++ task->bbox.min.y = mathMax(static_cast<SwCoord>(0), static_cast<SwCoord>(vport.y)); ++ task->bbox.max.x = mathMin(static_cast<SwCoord>(surface->w), static_cast<SwCoord>(vport.x + vport.w)); ++ task->bbox.max.y = mathMin(static_cast<SwCoord>(surface->h), static_cast<SwCoord>(vport.y + vport.h)); + + if (!task->pushed) { + task->pushed = true; +diff --git a/thirdparty/thorvg/src/lib/tvgMath.h b/thirdparty/thorvg/src/lib/tvgMath.h +index 9e5c915fc3..94b4fe1cf1 100644 +--- a/thirdparty/thorvg/src/lib/tvgMath.h ++++ b/thirdparty/thorvg/src/lib/tvgMath.h +@@ -29,6 +29,10 @@ + #include "tvgCommon.h" + + ++#define mathMin(x, y) (((x) < (y)) ? (x) : (y)) ++#define mathMax(x, y) (((x) > (y)) ? (x) : (y)) ++ ++ + static inline bool mathZero(float a) + { + return (fabsf(a) < FLT_EPSILON) ? true : false; +@@ -154,4 +158,4 @@ static inline Matrix mathMultiply(const Matrix* lhs, const Matrix* rhs) + } + + +-#endif //_TVG_MATH_H_ +\ No newline at end of file ++#endif //_TVG_MATH_H_ diff --git a/thirdparty/thorvg/src/lib/sw_engine/tvgSwRenderer.cpp b/thirdparty/thorvg/src/lib/sw_engine/tvgSwRenderer.cpp index 78537e7726..c75e73760e 100644 --- a/thirdparty/thorvg/src/lib/sw_engine/tvgSwRenderer.cpp +++ b/thirdparty/thorvg/src/lib/sw_engine/tvgSwRenderer.cpp @@ -23,6 +23,7 @@ #include "tvgSwCommon.h" #include "tvgTaskScheduler.h" #include "tvgSwRenderer.h" +#include "tvgMath.h" /************************************************************************/ /* Internal Class Implementation */ @@ -594,10 +595,10 @@ void* SwRenderer::prepareCommon(SwTask* task, const RenderTransform* transform, task->surface = surface; task->mpool = mpool; task->flags = flags; - task->bbox.min.x = max(static_cast<SwCoord>(0), static_cast<SwCoord>(vport.x)); - task->bbox.min.y = max(static_cast<SwCoord>(0), static_cast<SwCoord>(vport.y)); - task->bbox.max.x = min(static_cast<SwCoord>(surface->w), static_cast<SwCoord>(vport.x + vport.w)); - task->bbox.max.y = min(static_cast<SwCoord>(surface->h), static_cast<SwCoord>(vport.y + vport.h)); + task->bbox.min.x = mathMax(static_cast<SwCoord>(0), static_cast<SwCoord>(vport.x)); + task->bbox.min.y = mathMax(static_cast<SwCoord>(0), static_cast<SwCoord>(vport.y)); + task->bbox.max.x = mathMin(static_cast<SwCoord>(surface->w), static_cast<SwCoord>(vport.x + vport.w)); + task->bbox.max.y = mathMin(static_cast<SwCoord>(surface->h), static_cast<SwCoord>(vport.y + vport.h)); if (!task->pushed) { task->pushed = true; diff --git a/thirdparty/thorvg/src/lib/tvgMath.h b/thirdparty/thorvg/src/lib/tvgMath.h index 9e5c915fc3..94b4fe1cf1 100644 --- a/thirdparty/thorvg/src/lib/tvgMath.h +++ b/thirdparty/thorvg/src/lib/tvgMath.h @@ -29,6 +29,10 @@ #include "tvgCommon.h" +#define mathMin(x, y) (((x) < (y)) ? (x) : (y)) +#define mathMax(x, y) (((x) > (y)) ? (x) : (y)) + + static inline bool mathZero(float a) { return (fabsf(a) < FLT_EPSILON) ? true : false; @@ -154,4 +158,4 @@ static inline Matrix mathMultiply(const Matrix* lhs, const Matrix* rhs) } -#endif //_TVG_MATH_H_
\ No newline at end of file +#endif //_TVG_MATH_H_ |