summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/math/static_raycaster.cpp40
-rw-r--r--core/math/static_raycaster.h111
-rw-r--r--doc/classes/CharacterBody2D.xml49
-rw-r--r--doc/classes/CharacterBody3D.xml21
-rw-r--r--editor/import/resource_importer_scene.cpp15
-rw-r--r--editor/import/scene_importer_mesh.cpp499
-rw-r--r--editor/import/scene_importer_mesh.h14
-rw-r--r--editor/plugins/node_3d_editor_plugin.cpp3
-rw-r--r--modules/gdscript/gdscript_analyzer.cpp10
-rw-r--r--modules/raycast/SCsub14
-rw-r--r--modules/raycast/lightmap_raycaster.cpp9
-rw-r--r--modules/raycast/lightmap_raycaster.h2
-rw-r--r--modules/raycast/raycast_occlusion_cull.cpp120
-rw-r--r--modules/raycast/raycast_occlusion_cull.h14
-rw-r--r--modules/raycast/register_types.cpp5
-rw-r--r--modules/raycast/static_raycaster.cpp137
-rw-r--r--modules/raycast/static_raycaster.h64
-rw-r--r--scene/2d/physics_body_2d.cpp112
-rw-r--r--scene/2d/physics_body_2d.h32
-rw-r--r--scene/3d/physics_body_3d.cpp131
-rw-r--r--scene/3d/physics_body_3d.h20
-rw-r--r--scene/resources/texture.cpp2
-rw-r--r--servers/rendering_server.h2
-rw-r--r--thirdparty/README.md4
-rw-r--r--thirdparty/meshoptimizer/patches/attribute-aware-simplify-distance-only-metric.patch176
-rw-r--r--thirdparty/meshoptimizer/simplifier.cpp62
26 files changed, 1396 insertions, 272 deletions
diff --git a/core/math/static_raycaster.cpp b/core/math/static_raycaster.cpp
new file mode 100644
index 0000000000..da05d49428
--- /dev/null
+++ b/core/math/static_raycaster.cpp
@@ -0,0 +1,40 @@
+/*************************************************************************/
+/* static_raycaster.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "static_raycaster.h"
+
+StaticRaycaster *(*StaticRaycaster::create_function)() = nullptr;
+
+Ref<StaticRaycaster> StaticRaycaster::create() {
+ if (create_function) {
+ return Ref<StaticRaycaster>(create_function());
+ }
+ return Ref<StaticRaycaster>();
+}
diff --git a/core/math/static_raycaster.h b/core/math/static_raycaster.h
new file mode 100644
index 0000000000..3759c788a7
--- /dev/null
+++ b/core/math/static_raycaster.h
@@ -0,0 +1,111 @@
+/*************************************************************************/
+/* static_raycaster.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 STATIC_RAYCASTER_H
+#define STATIC_RAYCASTER_H
+
+#include "core/object/ref_counted.h"
+
+#if !defined(__aligned)
+
+#if defined(_WIN32) && defined(_MSC_VER)
+#define __aligned(...) __declspec(align(__VA_ARGS__))
+#else
+#define __aligned(...) __attribute__((aligned(__VA_ARGS__)))
+#endif
+
+#endif
+
+class StaticRaycaster : public RefCounted {
+ GDCLASS(StaticRaycaster, RefCounted)
+protected:
+ static StaticRaycaster *(*create_function)();
+
+public:
+ // compatible with embree3 rays
+ struct __aligned(16) Ray {
+ const static unsigned int INVALID_GEOMETRY_ID = ((unsigned int)-1); // from rtcore_common.h
+
+ /*! Default construction does nothing. */
+ _FORCE_INLINE_ Ray() :
+ geomID(INVALID_GEOMETRY_ID) {}
+
+ /*! Constructs a ray from origin, direction, and ray segment. Near
+ * has to be smaller than far. */
+ _FORCE_INLINE_ Ray(const Vector3 &org,
+ const Vector3 &dir,
+ float tnear = 0.0f,
+ float tfar = INFINITY) :
+ org(org),
+ tnear(tnear),
+ dir(dir),
+ time(0.0f),
+ tfar(tfar),
+ mask(-1),
+ u(0.0),
+ v(0.0),
+ primID(INVALID_GEOMETRY_ID),
+ geomID(INVALID_GEOMETRY_ID),
+ instID(INVALID_GEOMETRY_ID) {}
+
+ /*! Tests if we hit something. */
+ _FORCE_INLINE_ explicit operator bool() const { return geomID != INVALID_GEOMETRY_ID; }
+
+ public:
+ Vector3 org; //!< Ray origin + tnear
+ float tnear; //!< Start of ray segment
+ Vector3 dir; //!< Ray direction + tfar
+ float time; //!< Time of this ray for motion blur.
+ float tfar; //!< End of ray segment
+ unsigned int mask; //!< used to mask out objects during traversal
+ unsigned int id; //!< ray ID
+ unsigned int flags; //!< ray flags
+
+ Vector3 normal; //!< Not normalized geometry normal
+ float u; //!< Barycentric u coordinate of hit
+ float v; //!< Barycentric v coordinate of hit
+ unsigned int primID; //!< primitive ID
+ unsigned int geomID; //!< geometry ID
+ unsigned int instID; //!< instance ID
+ };
+
+ virtual bool intersect(Ray &p_ray) = 0;
+ virtual void intersect(Vector<Ray> &r_rays) = 0;
+
+ virtual void add_mesh(const PackedVector3Array &p_vertices, const PackedInt32Array &p_indices, unsigned int p_id) = 0;
+ virtual void commit() = 0;
+
+ virtual void set_mesh_filter(const Set<int> &p_mesh_ids) = 0;
+ virtual void clear_mesh_filter() = 0;
+
+ static Ref<StaticRaycaster> create();
+};
+
+#endif // STATIC_RAYCASTER_H
diff --git a/doc/classes/CharacterBody2D.xml b/doc/classes/CharacterBody2D.xml
index 35702070be..e14c2dc110 100644
--- a/doc/classes/CharacterBody2D.xml
+++ b/doc/classes/CharacterBody2D.xml
@@ -28,6 +28,12 @@
Returns the surface normal of the floor at the last collision point. Only valid after calling [method move_and_slide] and when [method is_on_floor] returns [code]true[/code].
</description>
</method>
+ <method name="get_last_motion" qualifiers="const">
+ <return type="Vector2" />
+ <description>
+ Returns the last motion applied to the [CharacterBody2D] during the last call to [method move_and_slide]. The movement can be split into multiple motions when sliding occurs, and this method return the last one, which is useful to retrieve the current direction of the movement.
+ </description>
+ </method>
<method name="get_last_slide_collision">
<return type="KinematicCollision2D" />
<description>
@@ -40,6 +46,18 @@
Returns the linear velocity of the platform at the last collision point. Only valid after calling [method move_and_slide].
</description>
</method>
+ <method name="get_position_delta" qualifiers="const">
+ <return type="Vector2" />
+ <description>
+ Returns the travel (position delta) that occurred during the last call to [method move_and_slide].
+ </description>
+ </method>
+ <method name="get_real_velocity" qualifiers="const">
+ <return type="Vector2" />
+ <description>
+ Returns the current real velocity since the last call to [method move_and_slide]. For example, when you climb a slope, you will move diagonally even though the velocity is horizontal. This method returns the diagonal movement, as opposed to [member motion_velocity] which returns the requested velocity.
+ </description>
+ </method>
<method name="get_slide_collision">
<return type="KinematicCollision2D" />
<argument index="0" name="slide_idx" type="int" />
@@ -68,6 +86,12 @@
Returns the number of times the body collided and changed direction during the last call to [method move_and_slide].
</description>
</method>
+ <method name="get_wall_normal" qualifiers="const">
+ <return type="Vector2" />
+ <description>
+ Returns the surface normal of the wall at the last collision point. Only valid after calling [method move_and_slide] and when [method is_on_wall] returns [code]true[/code].
+ </description>
+ </method>
<method name="is_on_ceiling" qualifiers="const">
<return type="bool" />
<description>
@@ -107,9 +131,9 @@
<method name="move_and_slide">
<return type="bool" />
<description>
- Moves the body based on [member linear_velocity]. If the body collides with another, it will slide along the other body (by default only on floor) rather than stop immediately. If the other body is a [CharacterBody2D] or [RigidDynamicBody2D], it will also be affected by the motion of the other body. You can use this to make moving and rotating platforms, or to make nodes push other nodes.
+ Moves the body based on [member motion_velocity]. If the body collides with another, it will slide along the other body (by default only on floor) rather than stop immediately. If the other body is a [CharacterBody2D] or [RigidDynamicBody2D], it will also be affected by the motion of the other body. You can use this to make moving and rotating platforms, or to make nodes push other nodes.
This method should be used in [method Node._physics_process] (or in a method called by [method Node._physics_process]), as it uses the physics step's [code]delta[/code] value automatically in calculations. Otherwise, the simulation will run at an incorrect speed.
- Modifies [member linear_velocity] if a slide collision occurred. To get the latest collision call [method get_last_slide_collision], for detailed information about collisions that occurred, use [method get_slide_collision].
+ Modifies [member motion_velocity] if a slide collision occurred. To get the latest collision call [method get_last_slide_collision], for detailed information about collisions that occurred, use [method get_slide_collision].
When the body touches a moving platform, the platform's velocity is automatically added to the body motion. If a collision occurs due to the platform's motion, it will always be first in the slide collisions.
The general behaviour and available properties change according to the [member motion_mode].
Returns [code]true[/code] if the body collided, otherwise, returns [code]false[/code].
@@ -138,20 +162,24 @@
As long as the snapping vector is in contact with the ground and the body moves against `up_direction`, the body will remain attached to the surface. Snapping is not applied if the body moves along `up_direction`, so it will be able to detach from the ground when jumping.
</member>
<member name="floor_stop_on_slope" type="bool" setter="set_floor_stop_on_slope_enabled" getter="is_floor_stop_on_slope_enabled" default="true">
- If [code]true[/code], the body will not slide on floor's slopes when you include gravity in [code]linear_velocity[/code] when calling [method move_and_slide] and the body is standing still.
+ If [code]true[/code], the body will not slide on slopes when calling [method move_and_slide] when the body is standing still.
+ If [code]false[/code], the body will slide on floor's slopes when [member motion_velocity] applies a downward force.
</member>
<member name="free_mode_min_slide_angle" type="float" setter="set_free_mode_min_slide_angle" getter="get_free_mode_min_slide_angle" default="0.261799">
Minimum angle (in radians) where the body is allowed to slide when it encounters a slope. The default value equals 15 degrees.
</member>
- <member name="linear_velocity" type="Vector2" setter="set_linear_velocity" getter="get_linear_velocity" default="Vector2(0, 0)">
- Current velocity vector in pixels per second, used and modified during calls to [method move_and_slide].
- </member>
<member name="max_slides" type="int" setter="set_max_slides" getter="get_max_slides" default="4">
Maximum number of times the body can change direction before it stops when calling [method move_and_slide].
</member>
<member name="motion_mode" type="int" setter="set_motion_mode" getter="get_motion_mode" enum="CharacterBody2D.MotionMode" default="0">
Sets the motion mode which defines the behaviour of [method move_and_slide]. See [enum MotionMode] constants for available modes.
</member>
+ <member name="motion_velocity" type="Vector2" setter="set_motion_velocity" getter="get_motion_velocity" default="Vector2(0, 0)">
+ Current velocity vector in pixels per second, used and modified during calls to [method move_and_slide].
+ </member>
+ <member name="moving_platform_apply_velocity_on_leave" type="int" setter="set_moving_platform_apply_velocity_on_leave" getter="get_moving_platform_apply_velocity_on_leave" enum="CharacterBody2D.MovingPlatformApplyVelocityOnLeave" default="0">
+ Sets the behaviour to apply when you leave a moving platform. By default, to be physically accurate, when you leave the last platform velocity is applied. See [enum MovingPlatformApplyVelocityOnLeave] constants for available behaviour.
+ </member>
<member name="moving_platform_floor_layers" type="int" setter="set_moving_platform_floor_layers" getter="get_moving_platform_floor_layers" default="4294967295">
Collision layers that will be included for detecting floor bodies that will act as moving platforms to be followed by the [CharacterBody2D]. By default, all floor bodies are detected and propagate their velocity.
</member>
@@ -172,5 +200,14 @@
<constant name="MOTION_MODE_FREE" value="1" enum="MotionMode">
Apply when there is no notion of floor or ceiling. All collisions will be reported as [code]on_wall[/code]. In this mode, when you slide, the speed will always be constant. This mode is suitable for top-down games.
</constant>
+ <constant name="PLATFORM_VEL_ON_LEAVE_ALWAYS" value="0" enum="MovingPlatformApplyVelocityOnLeave">
+ Add the last platform velocity to the [member motion_velocity] when you leave a moving platform.
+ </constant>
+ <constant name="PLATFORM_VEL_ON_LEAVE_UPWARD_ONLY" value="1" enum="MovingPlatformApplyVelocityOnLeave">
+ Add the last platform velocity to the [member motion_velocity] when you leave a moving platform, but any downward motion is ignored. It's useful to keep full jump height even when the platform is moving down.
+ </constant>
+ <constant name="PLATFORM_VEL_ON_LEAVE_NEVER" value="2" enum="MovingPlatformApplyVelocityOnLeave">
+ Do nothing when leaving a platform.
+ </constant>
</constants>
</class>
diff --git a/doc/classes/CharacterBody3D.xml b/doc/classes/CharacterBody3D.xml
index 66d9940b60..24b26fb16e 100644
--- a/doc/classes/CharacterBody3D.xml
+++ b/doc/classes/CharacterBody3D.xml
@@ -32,7 +32,7 @@
<method name="get_last_motion" qualifiers="const">
<return type="Vector3" />
<description>
- Returns the last motion applied to the [CharacterBody3D] during the last call to [method move_and_slide]. The movement can be split if needed into multiple motion, this method return the last one, it's useful to retrieve the current direction of the movement.
+ Returns the last motion applied to the [CharacterBody3D] during the last call to [method move_and_slide]. The movement can be split into multiple motions when sliding occurs, and this method return the last one, which is useful to retrieve the current direction of the movement.
</description>
</method>
<method name="get_last_slide_collision">
@@ -56,7 +56,7 @@
<method name="get_real_velocity" qualifiers="const">
<return type="Vector3" />
<description>
- Returns the current real velocity since the last call to [method move_and_slide]. For example, when you climb a slope, you will move diagonally even though the velocity is horizontal. This method returns the diagonal movement, as opposed to [member linear_velocity] which returns the requested velocity.
+ Returns the current real velocity since the last call to [method move_and_slide]. For example, when you climb a slope, you will move diagonally even though the velocity is horizontal. This method returns the diagonal movement, as opposed to [member motion_velocity] which returns the requested velocity.
</description>
</method>
<method name="get_slide_collision">
@@ -117,9 +117,9 @@
<method name="move_and_slide">
<return type="bool" />
<description>
- Moves the body based on [member linear_velocity]. If the body collides with another, it will slide along the other body rather than stop immediately. If the other body is a [CharacterBody3D] or [RigidDynamicBody3D], it will also be affected by the motion of the other body. You can use this to make moving and rotating platforms, or to make nodes push other nodes.
+ Moves the body based on [member motion_velocity]. If the body collides with another, it will slide along the other body rather than stop immediately. If the other body is a [CharacterBody3D] or [RigidDynamicBody3D], it will also be affected by the motion of the other body. You can use this to make moving and rotating platforms, or to make nodes push other nodes.
This method should be used in [method Node._physics_process] (or in a method called by [method Node._physics_process]), as it uses the physics step's [code]delta[/code] value automatically in calculations. Otherwise, the simulation will run at an incorrect speed.
- Modifies [member linear_velocity] if a slide collision occurred. To get the latest collision call [method get_last_slide_collision], for more detailed information about collisions that occurred, use [method get_slide_collision].
+ Modifies [member motion_velocity] if a slide collision occurred. To get the latest collision call [method get_last_slide_collision], for more detailed information about collisions that occurred, use [method get_slide_collision].
When the body touches a moving platform, the platform's velocity is automatically added to the body motion. If a collision occurs due to the platform's motion, it will always be first in the slide collisions.
Returns [code]true[/code] if the body collided, otherwise, returns [code]false[/code].
</description>
@@ -147,10 +147,8 @@
As long as the snapping vector is in contact with the ground and the body moves against `up_direction`, the body will remain attached to the surface. Snapping is not applied if the body moves along `up_direction`, so it will be able to detach from the ground when jumping.
</member>
<member name="floor_stop_on_slope" type="bool" setter="set_floor_stop_on_slope_enabled" getter="is_floor_stop_on_slope_enabled" default="true">
- If [code]true[/code], the body will not slide on slopes when you include gravity in [code]linear_velocity[/code] when calling [method move_and_slide] and the body is standing still.
- </member>
- <member name="linear_velocity" type="Vector3" setter="set_linear_velocity" getter="get_linear_velocity" default="Vector3(0, 0, 0)">
- Current velocity vector (typically meters per second), used and modified during calls to [method move_and_slide].
+ If [code]true[/code], the body will not slide on slopes when calling [method move_and_slide] when the body is standing still.
+ If [code]false[/code], the body will slide on floor's slopes when [member motion_velocity] applies a downward force.
</member>
<member name="max_slides" type="int" setter="set_max_slides" getter="get_max_slides" default="6">
Maximum number of times the body can change direction before it stops when calling [method move_and_slide].
@@ -158,6 +156,9 @@
<member name="motion_mode" type="int" setter="set_motion_mode" getter="get_motion_mode" enum="CharacterBody3D.MotionMode" default="0">
Sets the motion mode which defines the behaviour of [method move_and_slide]. See [enum MotionMode] constants for available modes.
</member>
+ <member name="motion_velocity" type="Vector3" setter="set_motion_velocity" getter="get_motion_velocity" default="Vector3(0, 0, 0)">
+ Current velocity vector (typically meters per second), used and modified during calls to [method move_and_slide].
+ </member>
<member name="moving_platform_apply_velocity_on_leave" type="int" setter="set_moving_platform_apply_velocity_on_leave" getter="get_moving_platform_apply_velocity_on_leave" enum="CharacterBody3D.MovingPlatformApplyVelocityOnLeave" default="0">
Sets the behaviour to apply when you leave a moving platform. By default, to be physically accurate, when you leave the last platform velocity is applied. See [enum MovingPlatformApplyVelocityOnLeave] constants for available behaviour.
</member>
@@ -185,10 +186,10 @@
Apply when there is no notion of floor or ceiling. All collisions will be reported as [code]on_wall[/code]. In this mode, when you slide, the speed will always be constant. This mode is suitable for games without ground like space games.
</constant>
<constant name="PLATFORM_VEL_ON_LEAVE_ALWAYS" value="0" enum="MovingPlatformApplyVelocityOnLeave">
- Add the last platform velocity to the [member linear_velocity] when you leave a moving platform.
+ Add the last platform velocity to the [member motion_velocity] when you leave a moving platform.
</constant>
<constant name="PLATFORM_VEL_ON_LEAVE_UPWARD_ONLY" value="1" enum="MovingPlatformApplyVelocityOnLeave">
- Add the last platform velocity to the [member linear_velocity] when you leave a moving platform, but any downward motion is ignored. It's useful to keep full jump height even when the platform is moving down.
+ Add the last platform velocity to the [member motion_velocity] when you leave a moving platform, but any downward motion is ignored. It's useful to keep full jump height even when the platform is moving down.
</constant>
<constant name="PLATFORM_VEL_ON_LEAVE_NEVER" value="2" enum="MovingPlatformApplyVelocityOnLeave">
Do nothing when leaving a platform.
diff --git a/editor/import/resource_importer_scene.cpp b/editor/import/resource_importer_scene.cpp
index c48d9bb117..2c9bc7dadf 100644
--- a/editor/import/resource_importer_scene.cpp
+++ b/editor/import/resource_importer_scene.cpp
@@ -980,6 +980,8 @@ void ResourceImporterScene::get_internal_import_options(InternalImportCategory p
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "generate/shadow_meshes", PROPERTY_HINT_ENUM, "Default,Enable,Disable"), 0));
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "generate/lightmap_uv", PROPERTY_HINT_ENUM, "Default,Enable,Disable"), 0));
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "generate/lods", PROPERTY_HINT_ENUM, "Default,Enable,Disable"), 0));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "lods/normal_split_angle", PROPERTY_HINT_RANGE, "0,180,0.1,degrees"), 25.0f));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "lods/normal_merge_angle", PROPERTY_HINT_RANGE, "0,180,0.1,degrees"), 60.0f));
} break;
case INTERNAL_IMPORT_CATEGORY_MATERIAL: {
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "use_external/enabled", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), false));
@@ -1259,6 +1261,8 @@ void ResourceImporterScene::_generate_meshes(Node *p_node, const Dictionary &p_m
//do mesh processing
bool generate_lods = p_generate_lods;
+ float split_angle = 25.0f;
+ float merge_angle = 60.0f;
bool create_shadow_meshes = p_create_shadow_meshes;
bool bake_lightmaps = p_light_bake_mode == LIGHT_BAKE_STATIC_LIGHTMAPS;
String save_to_file;
@@ -1301,6 +1305,14 @@ void ResourceImporterScene::_generate_meshes(Node *p_node, const Dictionary &p_m
}
}
+ if (mesh_settings.has("lods/normal_split_angle")) {
+ split_angle = mesh_settings["lods/normal_split_angle"];
+ }
+
+ if (mesh_settings.has("lods/normal_merge_angle")) {
+ merge_angle = mesh_settings["lods/normal_merge_angle"];
+ }
+
if (mesh_settings.has("save_to_file/enabled") && bool(mesh_settings["save_to_file/enabled"]) && mesh_settings.has("save_to_file/path")) {
save_to_file = mesh_settings["save_to_file/path"];
if (!save_to_file.is_resource_file()) {
@@ -1310,8 +1322,9 @@ void ResourceImporterScene::_generate_meshes(Node *p_node, const Dictionary &p_m
}
if (generate_lods) {
- src_mesh_node->get_mesh()->generate_lods();
+ src_mesh_node->get_mesh()->generate_lods(merge_angle, split_angle);
}
+
if (create_shadow_meshes) {
src_mesh_node->get_mesh()->create_shadow_mesh();
}
diff --git a/editor/import/scene_importer_mesh.cpp b/editor/import/scene_importer_mesh.cpp
index 5e6dd08e79..f83a7d046a 100644
--- a/editor/import/scene_importer_mesh.cpp
+++ b/editor/import/scene_importer_mesh.cpp
@@ -30,11 +30,99 @@
#include "scene_importer_mesh.h"
-#include "core/math/math_defs.h"
+#include "core/math/random_pcg.h"
+#include "core/math/static_raycaster.h"
#include "scene/resources/surface_tool.h"
#include <cstdint>
+void EditorSceneImporterMesh::Surface::split_normals(const LocalVector<int> &p_indices, const LocalVector<Vector3> &p_normals) {
+ ERR_FAIL_COND(arrays.size() != RS::ARRAY_MAX);
+
+ const PackedVector3Array &vertices = arrays[RS::ARRAY_VERTEX];
+ int current_vertex_count = vertices.size();
+ int new_vertex_count = p_indices.size();
+ int final_vertex_count = current_vertex_count + new_vertex_count;
+ const int *indices_ptr = p_indices.ptr();
+
+ for (int i = 0; i < arrays.size(); i++) {
+ if (i == RS::ARRAY_INDEX) {
+ continue;
+ }
+
+ if (arrays[i].get_type() == Variant::NIL) {
+ continue;
+ }
+
+ switch (arrays[i].get_type()) {
+ case Variant::PACKED_VECTOR3_ARRAY: {
+ PackedVector3Array data = arrays[i];
+ data.resize(final_vertex_count);
+ Vector3 *data_ptr = data.ptrw();
+ if (i == RS::ARRAY_NORMAL) {
+ const Vector3 *normals_ptr = p_normals.ptr();
+ memcpy(&data_ptr[current_vertex_count], normals_ptr, sizeof(Vector3) * new_vertex_count);
+ } else {
+ for (int j = 0; j < new_vertex_count; j++) {
+ data_ptr[current_vertex_count + j] = data_ptr[indices_ptr[j]];
+ }
+ }
+ arrays[i] = data;
+ } break;
+ case Variant::PACKED_VECTOR2_ARRAY: {
+ PackedVector2Array data = arrays[i];
+ data.resize(final_vertex_count);
+ Vector2 *data_ptr = data.ptrw();
+ for (int j = 0; j < new_vertex_count; j++) {
+ data_ptr[current_vertex_count + j] = data_ptr[indices_ptr[j]];
+ }
+ arrays[i] = data;
+ } break;
+ case Variant::PACKED_FLOAT32_ARRAY: {
+ PackedFloat32Array data = arrays[i];
+ int elements = data.size() / current_vertex_count;
+ data.resize(final_vertex_count * elements);
+ float *data_ptr = data.ptrw();
+ for (int j = 0; j < new_vertex_count; j++) {
+ memcpy(&data_ptr[(current_vertex_count + j) * elements], &data_ptr[indices_ptr[j] * elements], sizeof(float) * elements);
+ }
+ arrays[i] = data;
+ } break;
+ case Variant::PACKED_INT32_ARRAY: {
+ PackedInt32Array data = arrays[i];
+ int elements = data.size() / current_vertex_count;
+ data.resize(final_vertex_count * elements);
+ int32_t *data_ptr = data.ptrw();
+ for (int j = 0; j < new_vertex_count; j++) {
+ memcpy(&data_ptr[(current_vertex_count + j) * elements], &data_ptr[indices_ptr[j] * elements], sizeof(int32_t) * elements);
+ }
+ arrays[i] = data;
+ } break;
+ case Variant::PACKED_BYTE_ARRAY: {
+ PackedByteArray data = arrays[i];
+ int elements = data.size() / current_vertex_count;
+ data.resize(final_vertex_count * elements);
+ uint8_t *data_ptr = data.ptrw();
+ for (int j = 0; j < new_vertex_count; j++) {
+ memcpy(&data_ptr[(current_vertex_count + j) * elements], &data_ptr[indices_ptr[j] * elements], sizeof(uint8_t) * elements);
+ }
+ arrays[i] = data;
+ } break;
+ case Variant::PACKED_COLOR_ARRAY: {
+ PackedColorArray data = arrays[i];
+ data.resize(final_vertex_count);
+ Color *data_ptr = data.ptrw();
+ for (int j = 0; j < new_vertex_count; j++) {
+ data_ptr[current_vertex_count + j] = data_ptr[indices_ptr[j]];
+ }
+ } break;
+ default: {
+ ERR_FAIL_MSG("Uhandled array type.");
+ } break;
+ }
+ }
+}
+
void EditorSceneImporterMesh::add_blend_shape(const String &p_name) {
ERR_FAIL_COND(surfaces.size() > 0);
blend_shapes.push_back(p_name);
@@ -157,29 +245,14 @@ void EditorSceneImporterMesh::set_surface_material(int p_surface, const Ref<Mate
mesh.unref();
}
-Basis EditorSceneImporterMesh::compute_rotation_matrix_from_ortho_6d(Vector3 p_x_raw, Vector3 p_y_raw) {
- Vector3 x = p_x_raw.normalized();
- Vector3 z = x.cross(p_y_raw);
- z = z.normalized();
- Vector3 y = z.cross(x);
- Basis basis;
- basis.set_axis(Vector3::AXIS_X, x);
- basis.set_axis(Vector3::AXIS_Y, y);
- basis.set_axis(Vector3::AXIS_Z, z);
- return basis;
-}
-
-void EditorSceneImporterMesh::generate_lods() {
- if (!SurfaceTool::simplify_func) {
- return;
- }
+void EditorSceneImporterMesh::generate_lods(float p_normal_merge_angle, float p_normal_split_angle) {
if (!SurfaceTool::simplify_scale_func) {
return;
}
- if (!SurfaceTool::simplify_sloppy_func) {
+ if (!SurfaceTool::simplify_with_attrib_func) {
return;
}
- if (!SurfaceTool::simplify_with_attrib_func) {
+ if (!SurfaceTool::optimize_vertex_cache_func) {
return;
}
@@ -190,67 +263,343 @@ void EditorSceneImporterMesh::generate_lods() {
surfaces.write[i].lods.clear();
Vector<Vector3> vertices = surfaces[i].arrays[RS::ARRAY_VERTEX];
- Vector<int> indices = surfaces[i].arrays[RS::ARRAY_INDEX];
- if (indices.size() == 0) {
+ PackedInt32Array indices = surfaces[i].arrays[RS::ARRAY_INDEX];
+ Vector<Vector3> normals = surfaces[i].arrays[RS::ARRAY_NORMAL];
+ Vector<Vector2> uvs = surfaces[i].arrays[RS::ARRAY_TEX_UV];
+
+ unsigned int index_count = indices.size();
+ unsigned int vertex_count = vertices.size();
+
+ if (index_count == 0) {
continue; //no lods if no indices
}
- Vector<Vector3> normals = surfaces[i].arrays[RS::ARRAY_NORMAL];
- uint32_t vertex_count = vertices.size();
+
const Vector3 *vertices_ptr = vertices.ptr();
- Vector<float> attributes;
- Vector<float> normal_weights;
- int32_t attribute_count = 6;
- if (normals.size()) {
- attributes.resize(normals.size() * attribute_count);
- for (int32_t normal_i = 0; normal_i < normals.size(); normal_i++) {
- Basis basis;
- basis.set_euler(normals[normal_i]);
- Vector3 basis_x = basis.get_axis(0);
- Vector3 basis_y = basis.get_axis(1);
- basis = compute_rotation_matrix_from_ortho_6d(basis_x, basis_y);
- basis_x = basis.get_axis(0);
- basis_y = basis.get_axis(1);
- attributes.write[normal_i * attribute_count + 0] = basis_x.x;
- attributes.write[normal_i * attribute_count + 1] = basis_x.y;
- attributes.write[normal_i * attribute_count + 2] = basis_x.z;
- attributes.write[normal_i * attribute_count + 3] = basis_y.x;
- attributes.write[normal_i * attribute_count + 4] = basis_y.y;
- attributes.write[normal_i * attribute_count + 5] = basis_y.z;
- }
- normal_weights.resize(vertex_count);
- for (int32_t weight_i = 0; weight_i < normal_weights.size(); weight_i++) {
- normal_weights.write[weight_i] = 1.0;
+ const int *indices_ptr = indices.ptr();
+
+ if (normals.is_empty()) {
+ normals.resize(vertices.size());
+ Vector3 *n_ptr = normals.ptrw();
+ for (unsigned int j = 0; j < index_count; j += 3) {
+ const Vector3 &v0 = vertices_ptr[indices_ptr[j + 0]];
+ const Vector3 &v1 = vertices_ptr[indices_ptr[j + 1]];
+ const Vector3 &v2 = vertices_ptr[indices_ptr[j + 2]];
+ Vector3 n = vec3_cross(v0 - v2, v0 - v1).normalized();
+ n_ptr[j + 0] = n;
+ n_ptr[j + 1] = n;
+ n_ptr[j + 2] = n;
}
- } else {
- attribute_count = 0;
}
- const int min_indices = 10;
- const float error_tolerance = 1.44224'95703; // Cube root of 3
- const float threshold = 1.0 / error_tolerance;
- int index_target = indices.size() * threshold;
- float max_mesh_error_percentage = 1e0f;
+
+ float normal_merge_threshold = Math::cos(Math::deg2rad(p_normal_merge_angle));
+ float normal_pre_split_threshold = Math::cos(Math::deg2rad(MIN(180.0f, p_normal_split_angle * 2.0f)));
+ float normal_split_threshold = Math::cos(Math::deg2rad(p_normal_split_angle));
+ const Vector3 *normals_ptr = normals.ptr();
+
+ Map<Vector3, LocalVector<Pair<int, int>>> unique_vertices;
+
+ LocalVector<int> vertex_remap;
+ LocalVector<int> vertex_inverse_remap;
+ LocalVector<Vector3> merged_vertices;
+ LocalVector<Vector3> merged_normals;
+ LocalVector<int> merged_normals_counts;
+ const Vector2 *uvs_ptr = uvs.ptr();
+
+ for (unsigned int j = 0; j < vertex_count; j++) {
+ const Vector3 &v = vertices_ptr[j];
+ const Vector3 &n = normals_ptr[j];
+
+ Map<Vector3, LocalVector<Pair<int, int>>>::Element *E = unique_vertices.find(v);
+
+ if (E) {
+ const LocalVector<Pair<int, int>> &close_verts = E->get();
+
+ bool found = false;
+ for (unsigned int k = 0; k < close_verts.size(); k++) {
+ const Pair<int, int> &idx = close_verts[k];
+
+ // TODO check more attributes?
+ if ((!uvs_ptr || uvs_ptr[j].distance_squared_to(uvs_ptr[idx.second]) < CMP_EPSILON2) && normals[idx.second].dot(n) > normal_merge_threshold) {
+ vertex_remap.push_back(idx.first);
+ merged_normals[idx.first] += normals[idx.second];
+ merged_normals_counts[idx.first]++;
+ found = true;
+ break;
+ }
+ }
+
+ if (!found) {
+ int vcount = merged_vertices.size();
+ unique_vertices[v].push_back(Pair<int, int>(vcount, j));
+ vertex_inverse_remap.push_back(j);
+ merged_vertices.push_back(v);
+ vertex_remap.push_back(vcount);
+ merged_normals.push_back(normals_ptr[j]);
+ merged_normals_counts.push_back(1);
+ }
+ } else {
+ int vcount = merged_vertices.size();
+ unique_vertices[v] = LocalVector<Pair<int, int>>();
+ unique_vertices[v].push_back(Pair<int, int>(vcount, j));
+ vertex_inverse_remap.push_back(j);
+ merged_vertices.push_back(v);
+ vertex_remap.push_back(vcount);
+ merged_normals.push_back(normals_ptr[j]);
+ merged_normals_counts.push_back(1);
+ }
+ }
+
+ LocalVector<int> merged_indices;
+ merged_indices.resize(index_count);
+ for (unsigned int j = 0; j < index_count; j++) {
+ merged_indices[j] = vertex_remap[indices[j]];
+ }
+
+ unsigned int merged_vertex_count = merged_vertices.size();
+ const Vector3 *merged_vertices_ptr = merged_vertices.ptr();
+ const int32_t *merged_indices_ptr = merged_indices.ptr();
+
+ {
+ const int *counts_ptr = merged_normals_counts.ptr();
+ Vector3 *merged_normals_ptrw = merged_normals.ptr();
+ for (unsigned int j = 0; j < merged_vertex_count; j++) {
+ merged_normals_ptrw[j] /= counts_ptr[j];
+ }
+ }
+
+ LocalVector<float> normal_weights;
+ normal_weights.resize(merged_vertex_count);
+ for (unsigned int j = 0; j < merged_vertex_count; j++) {
+ normal_weights[j] = 2.0; // Give some weight to normal preservation, may be worth exposing as an import setting
+ }
+
+ const float max_mesh_error = FLT_MAX; // We don't want to limit by error, just by index target
+ float scale = SurfaceTool::simplify_scale_func((const float *)merged_vertices_ptr, merged_vertex_count, sizeof(Vector3));
float mesh_error = 0.0f;
- float scale = SurfaceTool::simplify_scale_func((const float *)vertices_ptr, vertex_count, sizeof(Vector3));
- while (index_target > min_indices) {
- Vector<int> new_indices;
- new_indices.resize(indices.size());
- size_t new_len = SurfaceTool::simplify_with_attrib_func((unsigned int *)new_indices.ptrw(), (const unsigned int *)indices.ptr(), indices.size(), (const float *)vertices_ptr, vertex_count, sizeof(Vector3), index_target, max_mesh_error_percentage, &mesh_error, (float *)attributes.ptrw(), normal_weights.ptrw(), attribute_count);
- if ((int)new_len > (index_target * error_tolerance)) {
- break;
+
+ unsigned int index_target = 12; // Start with the smallest target, 4 triangles
+ unsigned int last_index_count = 0;
+
+ int split_vertex_count = vertex_count;
+ LocalVector<Vector3> split_vertex_normals;
+ LocalVector<int> split_vertex_indices;
+ split_vertex_normals.reserve(index_count / 3);
+ split_vertex_indices.reserve(index_count / 3);
+
+ RandomPCG pcg;
+ pcg.seed(123456789); // Keep seed constant across imports
+
+ Ref<StaticRaycaster> raycaster = StaticRaycaster::create();
+ if (raycaster.is_valid()) {
+ raycaster->add_mesh(vertices, indices, 0);
+ raycaster->commit();
+ }
+
+ while (index_target < index_count) {
+ PackedInt32Array new_indices;
+ new_indices.resize(index_count);
+
+ size_t new_index_count = SurfaceTool::simplify_with_attrib_func((unsigned int *)new_indices.ptrw(), (const uint32_t *)merged_indices_ptr, index_count, (const float *)merged_vertices_ptr, merged_vertex_count, sizeof(Vector3), index_target, max_mesh_error, &mesh_error, (float *)merged_normals.ptr(), normal_weights.ptr(), 3);
+
+ if (new_index_count < last_index_count * 1.5f) {
+ index_target = index_target * 1.5f;
+ continue;
}
- Surface::LOD lod;
- lod.distance = mesh_error * scale;
- if (Math::is_zero_approx(mesh_error)) {
+
+ if (new_index_count <= 0 || (new_index_count >= (index_count * 0.75f))) {
break;
}
- if (new_len <= 0) {
- break;
+
+ new_indices.resize(new_index_count);
+
+ LocalVector<LocalVector<int>> vertex_corners;
+ vertex_corners.resize(vertex_count);
+ {
+ int *ptrw = new_indices.ptrw();
+ for (unsigned int j = 0; j < new_index_count; j++) {
+ const int &remapped = vertex_inverse_remap[ptrw[j]];
+ vertex_corners[remapped].push_back(j);
+ ptrw[j] = remapped;
+ }
}
- new_indices.resize(new_len);
+
+ if (raycaster.is_valid()) {
+ float error_factor = 1.0f / (scale * MAX(mesh_error, 0.15));
+ const float ray_bias = 0.05;
+ float ray_length = ray_bias + mesh_error * scale * 3.0f;
+
+ Vector<StaticRaycaster::Ray> rays;
+ LocalVector<Vector2> ray_uvs;
+
+ int32_t *new_indices_ptr = new_indices.ptrw();
+
+ int current_ray_count = 0;
+ for (unsigned int j = 0; j < new_index_count; j += 3) {
+ const Vector3 &v0 = vertices_ptr[new_indices_ptr[j + 0]];
+ const Vector3 &v1 = vertices_ptr[new_indices_ptr[j + 1]];
+ const Vector3 &v2 = vertices_ptr[new_indices_ptr[j + 2]];
+ Vector3 face_normal = vec3_cross(v0 - v2, v0 - v1);
+ float face_area = face_normal.length(); // Actually twice the face area, since it's the same error_factor on all faces, we don't care
+
+ Vector3 dir = face_normal / face_area;
+ int ray_count = CLAMP(5.0 * face_area * error_factor, 16, 64);
+
+ rays.resize(current_ray_count + ray_count);
+ StaticRaycaster::Ray *rays_ptr = rays.ptrw();
+
+ ray_uvs.resize(current_ray_count + ray_count);
+ Vector2 *ray_uvs_ptr = ray_uvs.ptr();
+
+ for (int k = 0; k < ray_count; k++) {
+ float u = pcg.randf();
+ float v = pcg.randf();
+
+ if (u + v >= 1.0f) {
+ u = 1.0f - u;
+ v = 1.0f - v;
+ }
+
+ u = 0.9f * u + 0.05f / 3.0f; // Give barycentric coordinates some padding, we don't want to sample right on the edge
+ v = 0.9f * v + 0.05f / 3.0f; // v = (v - one_third) * 0.95f + one_third;
+ float w = 1.0f - u - v;
+
+ Vector3 org = v0 * w + v1 * u + v2 * v;
+ org -= dir * ray_bias;
+ rays_ptr[current_ray_count + k] = StaticRaycaster::Ray(org, dir, 0.0f, ray_length);
+ rays_ptr[current_ray_count + k].id = j / 3;
+ ray_uvs_ptr[current_ray_count + k] = Vector2(u, v);
+ }
+
+ current_ray_count += ray_count;
+ }
+
+ raycaster->intersect(rays);
+
+ LocalVector<Vector3> ray_normals;
+ LocalVector<float> ray_normal_weights;
+
+ ray_normals.resize(new_index_count);
+ ray_normal_weights.resize(new_index_count);
+
+ for (unsigned int j = 0; j < new_index_count; j++) {
+ ray_normal_weights[j] = 0.0f;
+ }
+
+ const StaticRaycaster::Ray *rp = rays.ptr();
+ for (int j = 0; j < rays.size(); j++) {
+ if (rp[j].geomID != 0) { // Ray missed
+ continue;
+ }
+
+ if (rp[j].normal.normalized().dot(rp[j].dir) > 0.0f) { // Hit a back face.
+ continue;
+ }
+
+ const float &u = rp[j].u;
+ const float &v = rp[j].v;
+ const float w = 1.0f - u - v;
+
+ const unsigned int &hit_tri_id = rp[j].primID;
+ const unsigned int &orig_tri_id = rp[j].id;
+
+ const Vector3 &n0 = normals_ptr[indices_ptr[hit_tri_id * 3 + 0]];
+ const Vector3 &n1 = normals_ptr[indices_ptr[hit_tri_id * 3 + 1]];
+ const Vector3 &n2 = normals_ptr[indices_ptr[hit_tri_id * 3 + 2]];
+ Vector3 normal = n0 * w + n1 * u + n2 * v;
+
+ Vector2 orig_uv = ray_uvs[j];
+ float orig_bary[3] = { 1.0f - orig_uv.x - orig_uv.y, orig_uv.x, orig_uv.y };
+ for (int k = 0; k < 3; k++) {
+ int idx = orig_tri_id * 3 + k;
+ float weight = orig_bary[k];
+ ray_normals[idx] += normal * weight;
+ ray_normal_weights[idx] += weight;
+ }
+ }
+
+ for (unsigned int j = 0; j < new_index_count; j++) {
+ if (ray_normal_weights[j] < 1.0f) { // Not enough data, the new normal would be just a bad guess
+ ray_normals[j] = Vector3();
+ } else {
+ ray_normals[j] /= ray_normal_weights[j];
+ }
+ }
+
+ LocalVector<LocalVector<int>> normal_group_indices;
+ LocalVector<Vector3> normal_group_averages;
+ normal_group_indices.reserve(24);
+ normal_group_averages.reserve(24);
+
+ for (unsigned int j = 0; j < vertex_count; j++) {
+ const LocalVector<int> &corners = vertex_corners[j];
+ const Vector3 &vertex_normal = normals_ptr[j];
+
+ for (unsigned int k = 0; k < corners.size(); k++) {
+ const int &corner_idx = corners[k];
+ const Vector3 &ray_normal = ray_normals[corner_idx];
+
+ if (ray_normal.length_squared() < CMP_EPSILON2) {
+ continue;
+ }
+
+ bool found = false;
+ for (unsigned int l = 0; l < normal_group_indices.size(); l++) {
+ LocalVector<int> &group_indices = normal_group_indices[l];
+ Vector3 n = normal_group_averages[l] / group_indices.size();
+ if (n.dot(ray_normal) > normal_pre_split_threshold) {
+ found = true;
+ group_indices.push_back(corner_idx);
+ normal_group_averages[l] += ray_normal;
+ break;
+ }
+ }
+
+ if (!found) {
+ LocalVector<int> new_group;
+ new_group.push_back(corner_idx);
+ normal_group_indices.push_back(new_group);
+ normal_group_averages.push_back(ray_normal);
+ }
+ }
+
+ for (unsigned int k = 0; k < normal_group_indices.size(); k++) {
+ LocalVector<int> &group_indices = normal_group_indices[k];
+ Vector3 n = normal_group_averages[k] / group_indices.size();
+
+ if (vertex_normal.dot(n) < normal_split_threshold) {
+ split_vertex_indices.push_back(j);
+ split_vertex_normals.push_back(n);
+ int new_idx = split_vertex_count++;
+ for (unsigned int l = 0; l < group_indices.size(); l++) {
+ new_indices_ptr[group_indices[l]] = new_idx;
+ }
+ }
+ }
+
+ normal_group_indices.clear();
+ normal_group_averages.clear();
+ }
+ }
+
+ Surface::LOD lod;
+ lod.distance = MAX(mesh_error * scale, CMP_EPSILON2);
lod.indices = new_indices;
- print_line("Lod " + itos(surfaces.write[i].lods.size()) + " begin with " + itos(indices.size() / 3) + " triangles and shoot for " + itos(index_target / 3) + " triangles. Got " + itos(new_len / 3) + " triangles. Lod screen ratio " + rtos(lod.distance));
surfaces.write[i].lods.push_back(lod);
- index_target *= threshold;
+ index_target = MAX(new_index_count, index_target) * 2;
+ last_index_count = new_index_count;
+
+ if (mesh_error == 0.0f) {
+ break;
+ }
+ }
+
+ surfaces.write[i].split_normals(split_vertex_indices, split_vertex_normals);
+ surfaces.write[i].lods.sort_custom<Surface::LODComparator>();
+
+ for (int j = 0; j < surfaces.write[i].lods.size(); j++) {
+ Surface::LOD &lod = surfaces.write[i].lods.write[j];
+ unsigned int *lod_indices_ptr = (unsigned int *)lod.indices.ptrw();
+ SurfaceTool::optimize_vertex_cache_func(lod_indices_ptr, lod_indices_ptr, lod.indices.size(), split_vertex_count);
}
}
}
@@ -347,7 +696,7 @@ void EditorSceneImporterMesh::create_shadow_mesh() {
Map<Vector3, int> unique_vertices;
const Vector3 *vptr = vertices.ptr();
for (int j = 0; j < vertex_count; j++) {
- Vector3 v = vptr[j];
+ const Vector3 &v = vptr[j];
Map<Vector3, int>::Element *E = unique_vertices.find(v);
@@ -397,9 +746,9 @@ void EditorSceneImporterMesh::create_shadow_mesh() {
index_wptr = new_indices.ptrw();
for (int k = 0; k < index_count; k++) {
- int index = index_rptr[j];
+ int index = index_rptr[k];
ERR_FAIL_INDEX(index, vertex_count);
- index_wptr[j] = vertex_remap[index];
+ index_wptr[k] = vertex_remap[index];
}
lods[surfaces[i].lods[j].distance] = new_indices;
@@ -436,9 +785,9 @@ void EditorSceneImporterMesh::_set_data(const Dictionary &p_data) {
if (s.has("lods")) {
lods = s["lods"];
}
- Array blend_shapes;
- if (s.has("blend_shapes")) {
- blend_shapes = s["blend_shapes"];
+ Array b_shapes;
+ if (s.has("b_shapes")) {
+ b_shapes = s["b_shapes"];
}
Ref<Material> material;
if (s.has("material")) {
@@ -448,7 +797,7 @@ void EditorSceneImporterMesh::_set_data(const Dictionary &p_data) {
if (s.has("flags")) {
flags = s["flags"];
}
- add_surface(prim, arr, blend_shapes, lods, material, name, flags);
+ add_surface(prim, arr, b_shapes, lods, material, name, flags);
}
}
}
diff --git a/editor/import/scene_importer_mesh.h b/editor/import/scene_importer_mesh.h
index d32b1fdf74..111b191cae 100644
--- a/editor/import/scene_importer_mesh.h
+++ b/editor/import/scene_importer_mesh.h
@@ -32,6 +32,7 @@
#define EDITOR_SCENE_IMPORTER_MESH_H
#include "core/io/resource.h"
+#include "core/templates/local_vector.h"
#include "scene/resources/concave_polygon_shape_3d.h"
#include "scene/resources/convex_polygon_shape_3d.h"
#include "scene/resources/mesh.h"
@@ -55,12 +56,20 @@ class EditorSceneImporterMesh : public Resource {
Vector<BlendShape> blend_shape_data;
struct LOD {
Vector<int> indices;
- float distance;
+ float distance = 0.0f;
};
Vector<LOD> lods;
Ref<Material> material;
String name;
uint32_t flags = 0;
+
+ struct LODComparator {
+ _FORCE_INLINE_ bool operator()(const LOD &l, const LOD &r) const {
+ return l.distance < r.distance;
+ }
+ };
+
+ void split_normals(const LocalVector<int> &p_indices, const LocalVector<Vector3> &p_normals);
};
Vector<Surface> surfaces;
Vector<String> blend_shapes;
@@ -71,7 +80,6 @@ class EditorSceneImporterMesh : public Resource {
Ref<EditorSceneImporterMesh> shadow_mesh;
Size2i lightmap_size_hint;
- Basis compute_rotation_matrix_from_ortho_6d(Vector3 p_x_raw, Vector3 y_raw);
protected:
void _set_data(const Dictionary &p_data);
@@ -103,7 +111,7 @@ public:
void set_surface_material(int p_surface, const Ref<Material> &p_material);
- void generate_lods();
+ void generate_lods(float p_normal_merge_angle, float p_normal_split_angle);
void create_shadow_mesh();
Ref<EditorSceneImporterMesh> get_shadow_mesh() const;
diff --git a/editor/plugins/node_3d_editor_plugin.cpp b/editor/plugins/node_3d_editor_plugin.cpp
index e5e3be8a4d..857190bc92 100644
--- a/editor/plugins/node_3d_editor_plugin.cpp
+++ b/editor/plugins/node_3d_editor_plugin.cpp
@@ -2601,6 +2601,9 @@ void Node3DEditorViewport::_project_settings_changed() {
const bool use_occlusion_culling = GLOBAL_GET("rendering/occlusion_culling/use_occlusion_culling");
viewport->set_use_occlusion_culling(use_occlusion_culling);
+
+ const float lod_threshold = GLOBAL_GET("rendering/mesh_lod/lod_change/threshold_pixels");
+ viewport->set_lod_threshold(lod_threshold);
}
void Node3DEditorViewport::_notification(int p_what) {
diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp
index aa62ad20ff..032e08f5a0 100644
--- a/modules/gdscript/gdscript_analyzer.cpp
+++ b/modules/gdscript/gdscript_analyzer.cpp
@@ -2508,7 +2508,10 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod
result.enum_type = name;
p_identifier->set_datatype(result);
} else {
- push_error(vformat(R"(Cannot find value "%s" in "%s".)", name, base.to_string()), p_identifier);
+ // Consider as a Dictionary
+ GDScriptParser::DataType dummy;
+ dummy.kind = GDScriptParser::DataType::VARIANT;
+ p_identifier->set_datatype(dummy);
}
} else {
push_error(R"(Cannot get property from enum value.)", p_identifier);
@@ -3681,6 +3684,11 @@ bool GDScriptAnalyzer::is_type_compatible(const GDScriptParser::DataType &p_targ
if (p_source.kind == GDScriptParser::DataType::BUILTIN && p_source.builtin_type == Variant::INT) {
return true;
}
+ if (p_source.kind == GDScriptParser::DataType::ENUM) {
+ if (p_source.native_type == p_target.native_type) {
+ return true;
+ }
+ }
if (p_source.kind == GDScriptParser::DataType::ENUM_VALUE) {
if (p_source.native_type == p_target.native_type && p_target.enum_values.has(p_source.enum_type)) {
return true;
diff --git a/modules/raycast/SCsub b/modules/raycast/SCsub
index 4820cf7608..ef4c598194 100644
--- a/modules/raycast/SCsub
+++ b/modules/raycast/SCsub
@@ -87,6 +87,20 @@ if env["builtin_embree"]:
# Embree needs those, it will automatically use SSE2NEON in ARM
env_thirdparty.Append(CPPDEFINES=["__SSE2__", "__SSE__"])
+ if not env.msvc:
+ env_thirdparty.Append(
+ CPPFLAGS=[
+ "-fno-strict-overflow",
+ "-fno-delete-null-pointer-checks",
+ "-fwrapv",
+ "-fsigned-char",
+ "-fno-strict-aliasing",
+ "-fno-tree-vectorize",
+ "-fvisibility=hidden",
+ "-fvisibility-inlines-hidden",
+ ]
+ )
+
env.modules_sources += thirdparty_obj
diff --git a/modules/raycast/lightmap_raycaster.cpp b/modules/raycast/lightmap_raycaster.cpp
index 0583acc119..fdcf509da8 100644
--- a/modules/raycast/lightmap_raycaster.cpp
+++ b/modules/raycast/lightmap_raycaster.cpp
@@ -168,7 +168,7 @@ void LightmapRaycasterEmbree::clear_mesh_filter() {
filter_meshes.clear();
}
-void embree_error_handler(void *p_user_data, RTCError p_code, const char *p_str) {
+void embree_lm_error_handler(void *p_user_data, RTCError p_code, const char *p_str) {
print_error("Embree error: " + String(p_str));
}
@@ -179,16 +179,11 @@ LightmapRaycasterEmbree::LightmapRaycasterEmbree() {
#endif
embree_device = rtcNewDevice(nullptr);
- rtcSetDeviceErrorFunction(embree_device, &embree_error_handler, nullptr);
+ rtcSetDeviceErrorFunction(embree_device, &embree_lm_error_handler, nullptr);
embree_scene = rtcNewScene(embree_device);
}
LightmapRaycasterEmbree::~LightmapRaycasterEmbree() {
-#ifdef __SSE2__
- _MM_SET_FLUSH_ZERO_MODE(_MM_FLUSH_ZERO_OFF);
- _MM_SET_DENORMALS_ZERO_MODE(_MM_DENORMALS_ZERO_OFF);
-#endif
-
if (embree_scene != nullptr) {
rtcReleaseScene(embree_scene);
}
diff --git a/modules/raycast/lightmap_raycaster.h b/modules/raycast/lightmap_raycaster.h
index 4c3de27837..290b0a1cf3 100644
--- a/modules/raycast/lightmap_raycaster.h
+++ b/modules/raycast/lightmap_raycaster.h
@@ -30,9 +30,9 @@
#ifdef TOOLS_ENABLED
+#include "core/io/image.h"
#include "core/object/object.h"
#include "scene/3d/lightmapper.h"
-#include "scene/resources/mesh.h"
#include <embree3/rtcore.h>
diff --git a/modules/raycast/raycast_occlusion_cull.cpp b/modules/raycast/raycast_occlusion_cull.cpp
index 3975e83797..75491c98e5 100644
--- a/modules/raycast/raycast_occlusion_cull.cpp
+++ b/modules/raycast/raycast_occlusion_cull.cpp
@@ -41,9 +41,14 @@ RaycastOcclusionCull *RaycastOcclusionCull::raycast_singleton = nullptr;
void RaycastOcclusionCull::RaycastHZBuffer::clear() {
HZBuffer::clear();
- camera_rays.clear();
+ if (camera_rays_unaligned_buffer) {
+ memfree(camera_rays_unaligned_buffer);
+ camera_rays_unaligned_buffer = nullptr;
+ camera_rays = nullptr;
+ }
camera_ray_masks.clear();
- packs_size = Size2i();
+ camera_rays_tile_count = 0;
+ tile_grid_size = Size2i();
}
void RaycastOcclusionCull::RaycastHZBuffer::resize(const Size2i &p_size) {
@@ -58,10 +63,19 @@ void RaycastOcclusionCull::RaycastHZBuffer::resize(const Size2i &p_size) {
HZBuffer::resize(p_size);
- packs_size = Size2i(Math::ceil(p_size.x / (float)TILE_SIZE), Math::ceil(p_size.y / (float)TILE_SIZE));
- int ray_packets_count = packs_size.x * packs_size.y;
- camera_rays.resize(ray_packets_count);
- camera_ray_masks.resize(ray_packets_count * TILE_SIZE * TILE_SIZE);
+ tile_grid_size = Size2i(Math::ceil(p_size.x / (float)TILE_SIZE), Math::ceil(p_size.y / (float)TILE_SIZE));
+ camera_rays_tile_count = tile_grid_size.x * tile_grid_size.y;
+
+ if (camera_rays_unaligned_buffer) {
+ memfree(camera_rays_unaligned_buffer);
+ }
+
+ const int alignment = 64; // Embree requires ray packets to be 64-aligned
+ camera_rays_unaligned_buffer = (uint8_t *)memalloc(camera_rays_tile_count * sizeof(CameraRayTile) + alignment);
+ camera_rays = (CameraRayTile *)(camera_rays_unaligned_buffer + alignment - (((uint64_t)camera_rays_unaligned_buffer) % alignment));
+
+ camera_ray_masks.resize(camera_rays_tile_count * TILE_RAYS);
+ memset(camera_ray_masks.ptr(), ~0, camera_rays_tile_count * TILE_RAYS * sizeof(uint32_t));
}
void RaycastOcclusionCull::RaycastHZBuffer::update_camera_rays(const Transform3D &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_orthogonal, ThreadWorkPool &p_thread_work_pool) {
@@ -96,65 +110,55 @@ void RaycastOcclusionCull::RaycastHZBuffer::update_camera_rays(const Transform3D
}
void RaycastOcclusionCull::RaycastHZBuffer::_camera_rays_threaded(uint32_t p_thread, const CameraRayThreadData *p_data) {
- uint32_t packs_total = camera_rays.size();
+ uint32_t total_tiles = camera_rays_tile_count;
uint32_t total_threads = p_data->thread_count;
- uint32_t from = p_thread * packs_total / total_threads;
- uint32_t to = (p_thread + 1 == total_threads) ? packs_total : ((p_thread + 1) * packs_total / total_threads);
+ uint32_t from = p_thread * total_tiles / total_threads;
+ uint32_t to = (p_thread + 1 == total_threads) ? total_tiles : ((p_thread + 1) * total_tiles / total_threads);
_generate_camera_rays(p_data, from, to);
}
void RaycastOcclusionCull::RaycastHZBuffer::_generate_camera_rays(const CameraRayThreadData *p_data, int p_from, int p_to) {
const Size2i &buffer_size = sizes[0];
- RayPacket *ray_packets = camera_rays.ptr();
- uint32_t *ray_masks = camera_ray_masks.ptr();
-
for (int i = p_from; i < p_to; i++) {
- RayPacket &packet = ray_packets[i];
- int tile_x = (i % packs_size.x) * TILE_SIZE;
- int tile_y = (i / packs_size.x) * TILE_SIZE;
+ CameraRayTile &tile = camera_rays[i];
+ int tile_x = (i % tile_grid_size.x) * TILE_SIZE;
+ int tile_y = (i / tile_grid_size.x) * TILE_SIZE;
for (int j = 0; j < TILE_RAYS; j++) {
int x = tile_x + j % TILE_SIZE;
int y = tile_y + j / TILE_SIZE;
- if (x >= buffer_size.x || y >= buffer_size.y) {
- ray_masks[i * TILE_RAYS + j] = 0U;
- continue;
- }
-
- ray_masks[i * TILE_RAYS + j] = ~0U;
-
float u = (float(x) + 0.5f) / buffer_size.x;
float v = (float(y) + 0.5f) / buffer_size.y;
Vector3 pixel_pos = p_data->pixel_corner + u * p_data->pixel_u_interp + v * p_data->pixel_v_interp;
- packet.ray.tnear[j] = p_data->z_near;
+ tile.ray.tnear[j] = p_data->z_near;
Vector3 dir;
if (p_data->camera_orthogonal) {
dir = -p_data->camera_dir;
- packet.ray.org_x[j] = pixel_pos.x - dir.x * p_data->z_near;
- packet.ray.org_y[j] = pixel_pos.y - dir.y * p_data->z_near;
- packet.ray.org_z[j] = pixel_pos.z - dir.z * p_data->z_near;
+ tile.ray.org_x[j] = pixel_pos.x - dir.x * p_data->z_near;
+ tile.ray.org_y[j] = pixel_pos.y - dir.y * p_data->z_near;
+ tile.ray.org_z[j] = pixel_pos.z - dir.z * p_data->z_near;
} else {
dir = (pixel_pos - p_data->camera_pos).normalized();
- packet.ray.org_x[j] = p_data->camera_pos.x;
- packet.ray.org_y[j] = p_data->camera_pos.y;
- packet.ray.org_z[j] = p_data->camera_pos.z;
- packet.ray.tnear[j] /= dir.dot(p_data->camera_dir);
+ tile.ray.org_x[j] = p_data->camera_pos.x;
+ tile.ray.org_y[j] = p_data->camera_pos.y;
+ tile.ray.org_z[j] = p_data->camera_pos.z;
+ tile.ray.tnear[j] /= dir.dot(p_data->camera_dir);
}
- packet.ray.dir_x[j] = dir.x;
- packet.ray.dir_y[j] = dir.y;
- packet.ray.dir_z[j] = dir.z;
+ tile.ray.dir_x[j] = dir.x;
+ tile.ray.dir_y[j] = dir.y;
+ tile.ray.dir_z[j] = dir.z;
- packet.ray.tfar[j] = p_data->z_far;
- packet.ray.time[j] = 0.0f;
+ tile.ray.tfar[j] = p_data->z_far;
+ tile.ray.time[j] = 0.0f;
- packet.ray.flags[j] = 0;
- packet.ray.mask[j] = -1;
- packet.hit.geomID[j] = RTC_INVALID_GEOMETRY_ID;
+ tile.ray.flags[j] = 0;
+ tile.ray.mask[j] = ~0U;
+ tile.hit.geomID[j] = RTC_INVALID_GEOMETRY_ID;
}
}
}
@@ -163,8 +167,8 @@ void RaycastOcclusionCull::RaycastHZBuffer::sort_rays(const Vector3 &p_camera_di
ERR_FAIL_COND(is_empty());
Size2i buffer_size = sizes[0];
- for (int i = 0; i < packs_size.y; i++) {
- for (int j = 0; j < packs_size.x; j++) {
+ for (int i = 0; i < tile_grid_size.y; i++) {
+ for (int j = 0; j < tile_grid_size.x; j++) {
for (int tile_i = 0; tile_i < TILE_SIZE; tile_i++) {
for (int tile_j = 0; tile_j < TILE_SIZE; tile_j++) {
int x = j * TILE_SIZE + tile_j;
@@ -173,13 +177,13 @@ void RaycastOcclusionCull::RaycastHZBuffer::sort_rays(const Vector3 &p_camera_di
continue;
}
int k = tile_i * TILE_SIZE + tile_j;
- int packet_index = i * packs_size.x + j;
- float d = camera_rays[packet_index].ray.tfar[k];
+ int tile_index = i * tile_grid_size.x + j;
+ float d = camera_rays[tile_index].ray.tfar[k];
if (!p_orthogonal) {
- const float &dir_x = camera_rays[packet_index].ray.dir_x[k];
- const float &dir_y = camera_rays[packet_index].ray.dir_y[k];
- const float &dir_z = camera_rays[packet_index].ray.dir_z[k];
+ const float &dir_x = camera_rays[tile_index].ray.dir_x[k];
+ const float &dir_y = camera_rays[tile_index].ray.dir_y[k];
+ const float &dir_z = camera_rays[tile_index].ray.dir_z[k];
float cos_theta = p_camera_dir.x * dir_x + p_camera_dir.y * dir_y + p_camera_dir.z * dir_z;
d *= cos_theta;
}
@@ -191,6 +195,12 @@ void RaycastOcclusionCull::RaycastHZBuffer::sort_rays(const Vector3 &p_camera_di
}
}
+RaycastOcclusionCull::RaycastHZBuffer::~RaycastHZBuffer() {
+ if (camera_rays_unaligned_buffer) {
+ memfree(camera_rays_unaligned_buffer);
+ }
+}
+
////////////////////////////////////////////////////////
bool RaycastOcclusionCull::is_occluder(RID p_rid) {
@@ -474,7 +484,7 @@ void RaycastOcclusionCull::Scenario::_raycast(uint32_t p_idx, const RaycastThrea
rtcIntersect16((const int *)&p_raycast_data->masks[p_idx * TILE_RAYS], ebr_scene[current_scene_idx], &ctx, &p_raycast_data->rays[p_idx]);
}
-void RaycastOcclusionCull::Scenario::raycast(LocalVector<RayPacket> &r_rays, const LocalVector<uint32_t> p_valid_masks, ThreadWorkPool &p_thread_pool) const {
+void RaycastOcclusionCull::Scenario::raycast(CameraRayTile *r_rays, const uint32_t *p_valid_masks, uint32_t p_tile_count, ThreadWorkPool &p_thread_pool) const {
ERR_FAIL_COND(singleton == nullptr);
if (raycast_singleton->ebr_device == nullptr) {
return; // Embree is initialized on demand when there is some scenario with occluders in it.
@@ -485,10 +495,10 @@ void RaycastOcclusionCull::Scenario::raycast(LocalVector<RayPacket> &r_rays, con
}
RaycastThreadData td;
- td.rays = r_rays.ptr();
- td.masks = p_valid_masks.ptr();
+ td.rays = r_rays;
+ td.masks = p_valid_masks;
- p_thread_pool.do_work(r_rays.size(), this, &Scenario::_raycast, &td);
+ p_thread_pool.do_work(p_tile_count, this, &Scenario::_raycast, &td);
}
////////////////////////////////////////////////////////
@@ -536,7 +546,7 @@ void RaycastOcclusionCull::buffer_update(RID p_buffer, const Transform3D &p_cam_
buffer.update_camera_rays(p_cam_transform, p_cam_projection, p_cam_orthogonal, p_thread_pool);
- scenario.raycast(buffer.camera_rays, buffer.camera_ray_masks, p_thread_pool);
+ scenario.raycast(buffer.camera_rays, buffer.camera_ray_masks.ptr(), buffer.camera_rays_tile_count, p_thread_pool);
buffer.sort_rays(-p_cam_transform.basis.get_axis(2), p_cam_orthogonal);
buffer.update_mips();
}
@@ -592,13 +602,15 @@ RaycastOcclusionCull::~RaycastOcclusionCull() {
scenario.commit_thread->wait_to_finish();
memdelete(scenario.commit_thread);
}
+
+ for (int i = 0; i < 2; i++) {
+ if (scenario.ebr_scene[i]) {
+ rtcReleaseScene(scenario.ebr_scene[i]);
+ }
+ }
}
if (ebr_device != nullptr) {
-#ifdef __SSE2__
- _MM_SET_FLUSH_ZERO_MODE(_MM_FLUSH_ZERO_OFF);
- _MM_SET_DENORMALS_ZERO_MODE(_MM_DENORMALS_ZERO_OFF);
-#endif
rtcReleaseDevice(ebr_device);
}
diff --git a/modules/raycast/raycast_occlusion_cull.h b/modules/raycast/raycast_occlusion_cull.h
index cc87a6342c..ea96df5ff6 100644
--- a/modules/raycast/raycast_occlusion_cull.h
+++ b/modules/raycast/raycast_occlusion_cull.h
@@ -43,12 +43,12 @@
#include <embree3/rtcore.h>
class RaycastOcclusionCull : public RendererSceneOcclusionCull {
- typedef RTCRayHit16 RayPacket;
+ typedef RTCRayHit16 CameraRayTile;
public:
class RaycastHZBuffer : public HZBuffer {
private:
- Size2i packs_size;
+ Size2i tile_grid_size;
struct CameraRayThreadData {
int thread_count;
@@ -67,7 +67,9 @@ public:
void _generate_camera_rays(const CameraRayThreadData *p_data, int p_from, int p_to);
public:
- LocalVector<RayPacket> camera_rays;
+ unsigned int camera_rays_tile_count = 0;
+ uint8_t *camera_rays_unaligned_buffer = nullptr;
+ CameraRayTile *camera_rays = nullptr;
LocalVector<uint32_t> camera_ray_masks;
RID scenario_rid;
@@ -75,6 +77,8 @@ public:
virtual void resize(const Size2i &p_size) override;
void sort_rays(const Vector3 &p_camera_dir, bool p_orthogonal);
void update_camera_rays(const Transform3D &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_orthogonal, ThreadWorkPool &p_thread_work_pool);
+
+ ~RaycastHZBuffer();
};
private:
@@ -111,7 +115,7 @@ private:
struct Scenario {
struct RaycastThreadData {
- RayPacket *rays;
+ CameraRayTile *rays;
const uint32_t *masks;
};
@@ -144,7 +148,7 @@ private:
bool update(ThreadWorkPool &p_thread_pool);
void _raycast(uint32_t p_thread, const RaycastThreadData *p_raycast_data) const;
- void raycast(LocalVector<RayPacket> &r_rays, const LocalVector<uint32_t> p_valid_masks, ThreadWorkPool &p_thread_pool) const;
+ void raycast(CameraRayTile *r_rays, const uint32_t *p_valid_masks, uint32_t p_tile_count, ThreadWorkPool &p_thread_pool) const;
};
static RaycastOcclusionCull *raycast_singleton;
diff --git a/modules/raycast/register_types.cpp b/modules/raycast/register_types.cpp
index 78ca91309f..ed99e635e1 100644
--- a/modules/raycast/register_types.cpp
+++ b/modules/raycast/register_types.cpp
@@ -32,12 +32,14 @@
#include "lightmap_raycaster.h"
#include "raycast_occlusion_cull.h"
+#include "static_raycaster.h"
RaycastOcclusionCull *raycast_occlusion_cull = nullptr;
void register_raycast_types() {
#ifdef TOOLS_ENABLED
LightmapRaycasterEmbree::make_default_raycaster();
+ StaticRaycasterEmbree::make_default_raycaster();
#endif
raycast_occlusion_cull = memnew(RaycastOcclusionCull);
}
@@ -46,4 +48,7 @@ void unregister_raycast_types() {
if (raycast_occlusion_cull) {
memdelete(raycast_occlusion_cull);
}
+#ifdef TOOLS_ENABLED
+ StaticRaycasterEmbree::free();
+#endif
}
diff --git a/modules/raycast/static_raycaster.cpp b/modules/raycast/static_raycaster.cpp
new file mode 100644
index 0000000000..2ba65eebf8
--- /dev/null
+++ b/modules/raycast/static_raycaster.cpp
@@ -0,0 +1,137 @@
+/*************************************************************************/
+/* static_raycaster.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifdef TOOLS_ENABLED
+
+#include "static_raycaster.h"
+
+#ifdef __SSE2__
+#include <pmmintrin.h>
+#endif
+
+RTCDevice StaticRaycasterEmbree::embree_device;
+
+StaticRaycaster *StaticRaycasterEmbree::create_embree_raycaster() {
+ return memnew(StaticRaycasterEmbree);
+}
+
+void StaticRaycasterEmbree::make_default_raycaster() {
+ create_function = create_embree_raycaster;
+}
+
+void StaticRaycasterEmbree::free() {
+ if (embree_device) {
+ rtcReleaseDevice(embree_device);
+ }
+}
+
+bool StaticRaycasterEmbree::intersect(Ray &r_ray) {
+ RTCIntersectContext context;
+ rtcInitIntersectContext(&context);
+ rtcIntersect1(embree_scene, &context, (RTCRayHit *)&r_ray);
+ return r_ray.geomID != RTC_INVALID_GEOMETRY_ID;
+}
+
+void StaticRaycasterEmbree::intersect(Vector<Ray> &r_rays) {
+ Ray *rays = r_rays.ptrw();
+ for (int i = 0; i < r_rays.size(); ++i) {
+ intersect(rays[i]);
+ }
+}
+
+void StaticRaycasterEmbree::add_mesh(const PackedVector3Array &p_vertices, const PackedInt32Array &p_indices, unsigned int p_id) {
+ RTCGeometry embree_mesh = rtcNewGeometry(embree_device, RTC_GEOMETRY_TYPE_TRIANGLE);
+
+ int vertex_count = p_vertices.size();
+
+ Vector3 *embree_vertices = (Vector3 *)rtcSetNewGeometryBuffer(embree_mesh, RTC_BUFFER_TYPE_VERTEX, 0, RTC_FORMAT_FLOAT3, sizeof(Vector3), vertex_count);
+ memcpy(embree_vertices, p_vertices.ptr(), sizeof(Vector3) * vertex_count);
+
+ if (p_indices.is_empty()) {
+ ERR_FAIL_COND(vertex_count % 3 != 0);
+ uint32_t *embree_triangles = (uint32_t *)rtcSetNewGeometryBuffer(embree_mesh, RTC_BUFFER_TYPE_INDEX, 0, RTC_FORMAT_UINT3, sizeof(uint32_t) * 3, vertex_count / 3);
+ for (int i = 0; i < vertex_count; i++) {
+ embree_triangles[i] = i;
+ }
+ } else {
+ uint32_t *embree_triangles = (uint32_t *)rtcSetNewGeometryBuffer(embree_mesh, RTC_BUFFER_TYPE_INDEX, 0, RTC_FORMAT_UINT3, sizeof(uint32_t) * 3, p_indices.size() / 3);
+ memcpy(embree_triangles, p_indices.ptr(), sizeof(uint32_t) * p_indices.size());
+ }
+
+ rtcCommitGeometry(embree_mesh);
+ rtcAttachGeometryByID(embree_scene, embree_mesh, p_id);
+ rtcReleaseGeometry(embree_mesh);
+}
+
+void StaticRaycasterEmbree::commit() {
+ rtcCommitScene(embree_scene);
+}
+
+void StaticRaycasterEmbree::set_mesh_filter(const Set<int> &p_mesh_ids) {
+ for (Set<int>::Element *E = p_mesh_ids.front(); E; E = E->next()) {
+ rtcDisableGeometry(rtcGetGeometry(embree_scene, E->get()));
+ }
+ rtcCommitScene(embree_scene);
+ filter_meshes = p_mesh_ids;
+}
+
+void StaticRaycasterEmbree::clear_mesh_filter() {
+ for (Set<int>::Element *E = filter_meshes.front(); E; E = E->next()) {
+ rtcEnableGeometry(rtcGetGeometry(embree_scene, E->get()));
+ }
+ rtcCommitScene(embree_scene);
+ filter_meshes.clear();
+}
+
+void embree_error_handler(void *p_user_data, RTCError p_code, const char *p_str) {
+ print_error("Embree error: " + String(p_str));
+}
+
+StaticRaycasterEmbree::StaticRaycasterEmbree() {
+#ifdef __SSE2__
+ _MM_SET_FLUSH_ZERO_MODE(_MM_FLUSH_ZERO_ON);
+ _MM_SET_DENORMALS_ZERO_MODE(_MM_DENORMALS_ZERO_ON);
+#endif
+
+ if (!embree_device) {
+ embree_device = rtcNewDevice(nullptr);
+ rtcSetDeviceErrorFunction(embree_device, &embree_error_handler, nullptr);
+ }
+
+ embree_scene = rtcNewScene(embree_device);
+}
+
+StaticRaycasterEmbree::~StaticRaycasterEmbree() {
+ if (embree_scene != nullptr) {
+ rtcReleaseScene(embree_scene);
+ }
+}
+
+#endif
diff --git a/modules/raycast/static_raycaster.h b/modules/raycast/static_raycaster.h
new file mode 100644
index 0000000000..6b13ecf690
--- /dev/null
+++ b/modules/raycast/static_raycaster.h
@@ -0,0 +1,64 @@
+/*************************************************************************/
+/* static_raycaster.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. */
+/*************************************************************************/
+
+#ifdef TOOLS_ENABLED
+
+#include "core/math/static_raycaster.h"
+
+#include <embree3/rtcore.h>
+
+class StaticRaycasterEmbree : public StaticRaycaster {
+ GDCLASS(StaticRaycasterEmbree, StaticRaycaster);
+
+private:
+ static RTCDevice embree_device;
+ RTCScene embree_scene;
+
+ Set<int> filter_meshes;
+
+public:
+ virtual bool intersect(Ray &p_ray) override;
+ virtual void intersect(Vector<Ray> &r_rays) override;
+
+ virtual void add_mesh(const PackedVector3Array &p_vertices, const PackedInt32Array &p_indices, unsigned int p_id) override;
+ virtual void commit() override;
+
+ virtual void set_mesh_filter(const Set<int> &p_mesh_ids) override;
+ virtual void clear_mesh_filter() override;
+
+ static StaticRaycaster *create_embree_raycaster();
+ static void make_default_raycaster();
+ static void free();
+
+ StaticRaycasterEmbree();
+ ~StaticRaycasterEmbree();
+};
+
+#endif
diff --git a/scene/2d/physics_body_2d.cpp b/scene/2d/physics_body_2d.cpp
index d34a1e1c62..09bcbdbf0f 100644
--- a/scene/2d/physics_body_2d.cpp
+++ b/scene/2d/physics_body_2d.cpp
@@ -1052,6 +1052,8 @@ bool CharacterBody2D::move_and_slide() {
double delta = Engine::get_singleton()->is_in_physics_frame() ? get_physics_process_delta_time() : get_process_delta_time();
Vector2 current_platform_velocity = platform_velocity;
+ Transform2D gt = get_global_transform();
+ previous_position = gt.elements[2];
if ((on_floor || on_wall) && platform_rid.is_valid()) {
bool excluded = false;
@@ -1064,7 +1066,6 @@ bool CharacterBody2D::move_and_slide() {
//this approach makes sure there is less delay between the actual body velocity and the one we saved
PhysicsDirectBodyState2D *bs = PhysicsServer2D::get_singleton()->body_get_direct_state(platform_rid);
if (bs) {
- Transform2D gt = get_global_transform();
Vector2 local_position = gt.elements[2] - bs->get_transform().elements[2];
current_platform_velocity = bs->get_velocity_at_local_position(local_position);
}
@@ -1074,6 +1075,7 @@ bool CharacterBody2D::move_and_slide() {
}
motion_results.clear();
+ last_motion = Vector2();
bool was_on_floor = on_floor;
on_floor = false;
@@ -1096,16 +1098,24 @@ bool CharacterBody2D::move_and_slide() {
_move_and_slide_free(delta);
}
- if (!on_floor && !on_wall) {
+ // Compute real velocity.
+ real_velocity = get_position_delta() / delta;
+
+ if (moving_platform_apply_velocity_on_leave != PLATFORM_VEL_ON_LEAVE_NEVER) {
// Add last platform velocity when just left a moving platform.
- linear_velocity += current_platform_velocity;
+ if (!on_floor && !on_wall) {
+ if (moving_platform_apply_velocity_on_leave == PLATFORM_VEL_ON_LEAVE_UPWARD_ONLY && current_platform_velocity.dot(up_direction) < 0) {
+ current_platform_velocity = current_platform_velocity.slide(up_direction);
+ }
+ motion_velocity += current_platform_velocity;
+ }
}
return motion_results.size() > 0;
}
void CharacterBody2D::_move_and_slide_grounded(double p_delta, bool p_was_on_floor, const Vector2 &p_prev_platform_velocity) {
- Vector2 motion = linear_velocity * p_delta;
+ Vector2 motion = motion_velocity * p_delta;
Vector2 motion_slide_up = motion.slide(up_direction);
Vector2 prev_floor_normal = floor_normal;
@@ -1122,7 +1132,7 @@ void CharacterBody2D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo
// Constant speed can be applied only the first time sliding is enabled.
bool can_apply_constant_speed = sliding_enabled;
bool first_slide = true;
- bool vel_dir_facing_up = linear_velocity.dot(up_direction) > 0;
+ bool vel_dir_facing_up = motion_velocity.dot(up_direction) > 0;
Vector2 last_travel;
for (int iteration = 0; iteration < max_slides; ++iteration) {
@@ -1131,18 +1141,20 @@ void CharacterBody2D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo
Vector2 prev_position = get_global_transform().elements[2];
bool collided = move_and_collide(motion, result, margin, false, !sliding_enabled);
+ last_motion = result.travel;
if (collided) {
motion_results.push_back(result);
_set_collision_direction(result);
- if (on_floor && floor_stop_on_slope && (linear_velocity.normalized() + up_direction).length() < 0.01) {
+ if (on_floor && floor_stop_on_slope && (motion_velocity.normalized() + up_direction).length() < 0.01) {
Transform2D gt = get_global_transform();
if (result.travel.length() <= margin + CMP_EPSILON) {
gt.elements[2] -= result.travel;
}
set_global_transform(gt);
- linear_velocity = Vector2();
+ motion_velocity = Vector2();
+ last_motion = Vector2();
motion = Vector2();
break;
}
@@ -1168,7 +1180,8 @@ void CharacterBody2D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo
platform_layer = prev_platform_layer;
platform_velocity = p_prev_platform_velocity;
floor_normal = prev_floor_normal;
- linear_velocity = Vector2();
+ motion_velocity = Vector2();
+ last_motion = Vector2();
motion = Vector2();
break;
}
@@ -1189,7 +1202,7 @@ void CharacterBody2D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo
// Regular sliding, the last part of the test handle the case when you don't want to slide on the ceiling.
else if ((sliding_enabled || !on_floor) && (!on_ceiling || slide_on_ceiling || !vel_dir_facing_up)) {
Vector2 slide_motion = result.remainder.slide(result.collision_normal);
- if (slide_motion.dot(linear_velocity) > 0.0) {
+ if (slide_motion.dot(motion_velocity) > 0.0) {
motion = slide_motion;
} else {
motion = Vector2();
@@ -1197,10 +1210,10 @@ void CharacterBody2D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo
if (slide_on_ceiling && on_ceiling) {
// Apply slide only in the direction of the input motion, otherwise just stop to avoid jittering when moving against a wall.
if (vel_dir_facing_up) {
- linear_velocity = linear_velocity.slide(result.collision_normal);
+ motion_velocity = motion_velocity.slide(result.collision_normal);
} else {
// Avoid acceleration in slope when falling.
- linear_velocity = up_direction * up_direction.dot(linear_velocity);
+ motion_velocity = up_direction * up_direction.dot(motion_velocity);
}
}
}
@@ -1208,7 +1221,7 @@ void CharacterBody2D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo
else {
motion = result.remainder;
if (on_ceiling && !slide_on_ceiling && vel_dir_facing_up) {
- linear_velocity = linear_velocity.slide(up_direction);
+ motion_velocity = motion_velocity.slide(up_direction);
motion = motion.slide(up_direction);
}
}
@@ -1242,12 +1255,12 @@ void CharacterBody2D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo
// Reset the gravity accumulation when touching the ground.
if (on_floor && !vel_dir_facing_up) {
- linear_velocity = linear_velocity.slide(up_direction);
+ motion_velocity = motion_velocity.slide(up_direction);
}
}
void CharacterBody2D::_move_and_slide_free(double p_delta) {
- Vector2 motion = linear_velocity * p_delta;
+ Vector2 motion = motion_velocity * p_delta;
platform_rid = RID();
floor_normal = Vector2();
@@ -1258,12 +1271,18 @@ void CharacterBody2D::_move_and_slide_free(double p_delta) {
PhysicsServer2D::MotionResult result;
bool collided = move_and_collide(motion, result, margin, false, false);
+ last_motion = result.travel;
if (collided) {
motion_results.push_back(result);
_set_collision_direction(result);
- if (free_mode_min_slide_angle != 0 && result.get_angle(-linear_velocity.normalized()) < free_mode_min_slide_angle + FLOOR_ANGLE_THRESHOLD) {
+ if (result.remainder.is_equal_approx(Vector2())) {
+ motion = Vector2();
+ break;
+ }
+
+ if (free_mode_min_slide_angle != 0 && result.get_angle(-motion_velocity.normalized()) < free_mode_min_slide_angle + FLOOR_ANGLE_THRESHOLD) {
motion = Vector2();
} else if (first_slide) {
Vector2 motion_slide_norm = result.remainder.slide(result.collision_normal).normalized();
@@ -1272,16 +1291,16 @@ void CharacterBody2D::_move_and_slide_free(double p_delta) {
motion = result.remainder.slide(result.collision_normal);
}
- if (motion.dot(linear_velocity) <= 0.0) {
+ if (motion.dot(motion_velocity) <= 0.0) {
motion = Vector2();
}
}
- first_slide = false;
-
if (!collided || motion.is_equal_approx(Vector2())) {
break;
}
+
+ first_slide = false;
}
}
@@ -1349,6 +1368,7 @@ void CharacterBody2D::_set_collision_direction(const PhysicsServer2D::MotionResu
on_ceiling = true;
} else {
on_wall = true;
+ wall_normal = p_result.collision_normal;
// Don't apply wall velocity when the collider is a CharacterBody2D.
if (Object::cast_to<CharacterBody2D>(ObjectDB::get_instance(p_result.collider_id)) == nullptr) {
_set_platform_data(p_result);
@@ -1362,12 +1382,12 @@ void CharacterBody2D::_set_platform_data(const PhysicsServer2D::MotionResult &p_
platform_layer = PhysicsServer2D::get_singleton()->body_get_collision_layer(platform_rid);
}
-const Vector2 &CharacterBody2D::get_linear_velocity() const {
- return linear_velocity;
+const Vector2 &CharacterBody2D::get_motion_velocity() const {
+ return motion_velocity;
}
-void CharacterBody2D::set_linear_velocity(const Vector2 &p_velocity) {
- linear_velocity = p_velocity;
+void CharacterBody2D::set_motion_velocity(const Vector2 &p_velocity) {
+ motion_velocity = p_velocity;
}
bool CharacterBody2D::is_on_floor() const {
@@ -1394,16 +1414,32 @@ bool CharacterBody2D::is_on_ceiling_only() const {
return on_ceiling && !on_floor && !on_wall;
}
-Vector2 CharacterBody2D::get_floor_normal() const {
+const Vector2 &CharacterBody2D::get_floor_normal() const {
return floor_normal;
}
+const Vector2 &CharacterBody2D::get_wall_normal() const {
+ return wall_normal;
+}
+
+const Vector2 &CharacterBody2D::get_last_motion() const {
+ return last_motion;
+}
+
+Vector2 CharacterBody2D::get_position_delta() const {
+ return get_global_transform().elements[2] - previous_position;
+}
+
+const Vector2 &CharacterBody2D::get_real_velocity() const {
+ return real_velocity;
+}
+
real_t CharacterBody2D::get_floor_angle(const Vector2 &p_up_direction) const {
ERR_FAIL_COND_V(p_up_direction == Vector2(), 0);
return Math::acos(floor_normal.dot(p_up_direction));
}
-Vector2 CharacterBody2D::get_platform_velocity() const {
+const Vector2 &CharacterBody2D::get_platform_velocity() const {
return platform_velocity;
}
@@ -1503,6 +1539,14 @@ CharacterBody2D::MotionMode CharacterBody2D::get_motion_mode() const {
return motion_mode;
}
+void CharacterBody2D::set_moving_platform_apply_velocity_on_leave(MovingPlatformApplyVelocityOnLeave p_on_leave_apply_velocity) {
+ moving_platform_apply_velocity_on_leave = p_on_leave_apply_velocity;
+}
+
+CharacterBody2D::MovingPlatformApplyVelocityOnLeave CharacterBody2D::get_moving_platform_apply_velocity_on_leave() const {
+ return moving_platform_apply_velocity_on_leave;
+}
+
int CharacterBody2D::get_max_slides() const {
return max_slides;
}
@@ -1563,8 +1607,8 @@ void CharacterBody2D::_notification(int p_what) {
void CharacterBody2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("move_and_slide"), &CharacterBody2D::move_and_slide);
- ClassDB::bind_method(D_METHOD("set_linear_velocity", "linear_velocity"), &CharacterBody2D::set_linear_velocity);
- ClassDB::bind_method(D_METHOD("get_linear_velocity"), &CharacterBody2D::get_linear_velocity);
+ ClassDB::bind_method(D_METHOD("set_motion_velocity", "motion_velocity"), &CharacterBody2D::set_motion_velocity);
+ ClassDB::bind_method(D_METHOD("get_motion_velocity"), &CharacterBody2D::get_motion_velocity);
ClassDB::bind_method(D_METHOD("set_safe_margin", "pixels"), &CharacterBody2D::set_safe_margin);
ClassDB::bind_method(D_METHOD("get_safe_margin"), &CharacterBody2D::get_safe_margin);
@@ -1594,6 +1638,8 @@ void CharacterBody2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_up_direction", "up_direction"), &CharacterBody2D::set_up_direction);
ClassDB::bind_method(D_METHOD("set_motion_mode", "mode"), &CharacterBody2D::set_motion_mode);
ClassDB::bind_method(D_METHOD("get_motion_mode"), &CharacterBody2D::get_motion_mode);
+ ClassDB::bind_method(D_METHOD("set_moving_platform_apply_velocity_on_leave", "on_leave_apply_velocity"), &CharacterBody2D::set_moving_platform_apply_velocity_on_leave);
+ ClassDB::bind_method(D_METHOD("get_moving_platform_apply_velocity_on_leave"), &CharacterBody2D::get_moving_platform_apply_velocity_on_leave);
ClassDB::bind_method(D_METHOD("is_on_floor"), &CharacterBody2D::is_on_floor);
ClassDB::bind_method(D_METHOD("is_on_floor_only"), &CharacterBody2D::is_on_floor_only);
@@ -1602,6 +1648,10 @@ void CharacterBody2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("is_on_wall"), &CharacterBody2D::is_on_wall);
ClassDB::bind_method(D_METHOD("is_on_wall_only"), &CharacterBody2D::is_on_wall_only);
ClassDB::bind_method(D_METHOD("get_floor_normal"), &CharacterBody2D::get_floor_normal);
+ ClassDB::bind_method(D_METHOD("get_wall_normal"), &CharacterBody2D::get_wall_normal);
+ ClassDB::bind_method(D_METHOD("get_last_motion"), &CharacterBody2D::get_last_motion);
+ ClassDB::bind_method(D_METHOD("get_position_delta"), &CharacterBody2D::get_position_delta);
+ ClassDB::bind_method(D_METHOD("get_real_velocity"), &CharacterBody2D::get_real_velocity);
ClassDB::bind_method(D_METHOD("get_floor_angle", "up_direction"), &CharacterBody2D::get_floor_angle, DEFVAL(Vector2(0.0, -1.0)));
ClassDB::bind_method(D_METHOD("get_platform_velocity"), &CharacterBody2D::get_platform_velocity);
ClassDB::bind_method(D_METHOD("get_slide_collision_count"), &CharacterBody2D::get_slide_collision_count);
@@ -1609,10 +1659,11 @@ void CharacterBody2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_last_slide_collision"), &CharacterBody2D::_get_last_slide_collision);
ADD_PROPERTY(PropertyInfo(Variant::INT, "motion_mode", PROPERTY_HINT_ENUM, "Grounded,Free", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), "set_motion_mode", "get_motion_mode");
- ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "linear_velocity"), "set_linear_velocity", "get_linear_velocity");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "up_direction"), "set_up_direction", "get_up_direction");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "motion_velocity", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_motion_velocity", "get_motion_velocity");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "slide_on_ceiling"), "set_slide_on_ceiling_enabled", "is_slide_on_ceiling_enabled");
ADD_PROPERTY(PropertyInfo(Variant::INT, "max_slides", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_max_slides", "get_max_slides");
- ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "up_direction"), "set_up_direction", "get_up_direction");
+
ADD_GROUP("Free Mode", "free_mode_");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "free_mode_min_slide_angle", PROPERTY_HINT_RANGE, "0,180,0.1,radians", PROPERTY_USAGE_DEFAULT), "set_free_mode_min_slide_angle", "get_free_mode_min_slide_angle");
ADD_GROUP("Floor", "floor_");
@@ -1622,12 +1673,17 @@ void CharacterBody2D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "floor_max_angle", PROPERTY_HINT_RANGE, "0,180,0.1,radians"), "set_floor_max_angle", "get_floor_max_angle");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "floor_snap_length", PROPERTY_HINT_RANGE, "0,32,0.1,or_greater"), "set_floor_snap_length", "get_floor_snap_length");
ADD_GROUP("Moving platform", "moving_platform");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "moving_platform_apply_velocity_on_leave", PROPERTY_HINT_ENUM, "Always,Upward Only,Never", PROPERTY_USAGE_DEFAULT), "set_moving_platform_apply_velocity_on_leave", "get_moving_platform_apply_velocity_on_leave");
ADD_PROPERTY(PropertyInfo(Variant::INT, "moving_platform_floor_layers", PROPERTY_HINT_LAYERS_2D_PHYSICS), "set_moving_platform_floor_layers", "get_moving_platform_floor_layers");
ADD_PROPERTY(PropertyInfo(Variant::INT, "moving_platform_wall_layers", PROPERTY_HINT_LAYERS_2D_PHYSICS), "set_moving_platform_wall_layers", "get_moving_platform_wall_layers");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "collision/safe_margin", PROPERTY_HINT_RANGE, "0.001,256,0.001"), "set_safe_margin", "get_safe_margin");
BIND_ENUM_CONSTANT(MOTION_MODE_GROUNDED);
BIND_ENUM_CONSTANT(MOTION_MODE_FREE);
+
+ BIND_ENUM_CONSTANT(PLATFORM_VEL_ON_LEAVE_ALWAYS);
+ BIND_ENUM_CONSTANT(PLATFORM_VEL_ON_LEAVE_UPWARD_ONLY);
+ BIND_ENUM_CONSTANT(PLATFORM_VEL_ON_LEAVE_NEVER);
}
void CharacterBody2D::_validate_property(PropertyInfo &property) const {
diff --git a/scene/2d/physics_body_2d.h b/scene/2d/physics_body_2d.h
index e789ac4cb7..92b7fd1648 100644
--- a/scene/2d/physics_body_2d.h
+++ b/scene/2d/physics_body_2d.h
@@ -310,10 +310,15 @@ public:
MOTION_MODE_GROUNDED,
MOTION_MODE_FREE,
};
+ enum MovingPlatformApplyVelocityOnLeave {
+ PLATFORM_VEL_ON_LEAVE_ALWAYS,
+ PLATFORM_VEL_ON_LEAVE_UPWARD_ONLY,
+ PLATFORM_VEL_ON_LEAVE_NEVER,
+ };
bool move_and_slide();
- const Vector2 &get_linear_velocity() const;
- void set_linear_velocity(const Vector2 &p_velocity);
+ const Vector2 &get_motion_velocity() const;
+ void set_motion_velocity(const Vector2 &p_velocity);
bool is_on_floor() const;
bool is_on_floor_only() const;
@@ -321,9 +326,14 @@ public:
bool is_on_wall_only() const;
bool is_on_ceiling() const;
bool is_on_ceiling_only() const;
- Vector2 get_floor_normal() const;
+ const Vector2 &get_last_motion() const;
+ Vector2 get_position_delta() const;
+ const Vector2 &get_floor_normal() const;
+ const Vector2 &get_wall_normal() const;
+ const Vector2 &get_real_velocity() const;
+
real_t get_floor_angle(const Vector2 &p_up_direction = Vector2(0.0, -1.0)) const;
- Vector2 get_platform_velocity() const;
+ const Vector2 &get_platform_velocity() const;
int get_slide_collision_count() const;
PhysicsServer2D::MotionResult get_slide_collision(int p_bounce) const;
@@ -334,23 +344,29 @@ public:
private:
real_t margin = 0.08;
MotionMode motion_mode = MOTION_MODE_GROUNDED;
+ MovingPlatformApplyVelocityOnLeave moving_platform_apply_velocity_on_leave = PLATFORM_VEL_ON_LEAVE_ALWAYS;
bool floor_constant_speed = false;
bool floor_stop_on_slope = true;
bool floor_block_on_wall = true;
bool slide_on_ceiling = true;
int max_slides = 4;
- int platform_layer;
+ int platform_layer = 0;
real_t floor_max_angle = Math::deg2rad((real_t)45.0);
real_t floor_snap_length = 1;
real_t free_mode_min_slide_angle = Math::deg2rad((real_t)15.0);
Vector2 up_direction = Vector2(0.0, -1.0);
uint32_t moving_platform_floor_layers = UINT32_MAX;
uint32_t moving_platform_wall_layers = 0;
- Vector2 linear_velocity;
+ Vector2 motion_velocity;
Vector2 floor_normal;
Vector2 platform_velocity;
+ Vector2 wall_normal;
+ Vector2 last_motion;
+ Vector2 previous_position;
+ Vector2 real_velocity;
+
RID platform_rid;
bool on_floor = false;
bool on_ceiling = false;
@@ -395,6 +411,9 @@ private:
void set_motion_mode(MotionMode p_mode);
MotionMode get_motion_mode() const;
+ void set_moving_platform_apply_velocity_on_leave(MovingPlatformApplyVelocityOnLeave p_on_leave_velocity);
+ MovingPlatformApplyVelocityOnLeave get_moving_platform_apply_velocity_on_leave() const;
+
void _move_and_slide_free(double p_delta);
void _move_and_slide_grounded(double p_delta, bool p_was_on_floor, const Vector2 &p_prev_platform_velocity);
@@ -414,6 +433,7 @@ protected:
};
VARIANT_ENUM_CAST(CharacterBody2D::MotionMode);
+VARIANT_ENUM_CAST(CharacterBody2D::MovingPlatformApplyVelocityOnLeave);
class KinematicCollision2D : public RefCounted {
GDCLASS(KinematicCollision2D, RefCounted);
diff --git a/scene/3d/physics_body_3d.cpp b/scene/3d/physics_body_3d.cpp
index e6dc1ed3df..9e2358ed39 100644
--- a/scene/3d/physics_body_3d.cpp
+++ b/scene/3d/physics_body_3d.cpp
@@ -1089,13 +1089,16 @@ void RigidDynamicBody3D::_reload_physics_characteristics() {
bool CharacterBody3D::move_and_slide() {
// Hack in order to work with calling from _process as well as from _physics_process; calling from thread is risky
double delta = Engine::get_singleton()->is_in_physics_frame() ? get_physics_process_delta_time() : get_process_delta_time();
- previous_position = get_global_transform().origin;
+
for (int i = 0; i < 3; i++) {
if (locked_axis & (1 << i)) {
- linear_velocity[i] = 0.0;
+ motion_velocity[i] = 0.0;
}
}
+ Transform3D gt = get_global_transform();
+ previous_position = gt.origin;
+
Vector3 current_platform_velocity = platform_velocity;
if ((collision_state.floor || collision_state.wall) && platform_rid.is_valid()) {
@@ -1109,7 +1112,6 @@ bool CharacterBody3D::move_and_slide() {
//this approach makes sure there is less delay between the actual body velocity and the one we saved
PhysicsDirectBodyState3D *bs = PhysicsServer3D::get_singleton()->body_get_direct_state(platform_rid);
if (bs) {
- Transform3D gt = get_global_transform();
Vector3 local_position = gt.origin - bs->get_transform().origin;
current_platform_velocity = bs->get_velocity_at_local_position(local_position);
}
@@ -1123,6 +1125,8 @@ bool CharacterBody3D::move_and_slide() {
bool was_on_floor = collision_state.floor;
collision_state.state = 0;
+ last_motion = Vector3();
+
if (!current_platform_velocity.is_equal_approx(Vector3())) {
PhysicsServer3D::MotionResult floor_result;
Set<RID> exclude;
@@ -1150,20 +1154,15 @@ bool CharacterBody3D::move_and_slide() {
if (moving_platform_apply_velocity_on_leave == PLATFORM_VEL_ON_LEAVE_UPWARD_ONLY && current_platform_velocity.dot(up_direction) < 0) {
current_platform_velocity = current_platform_velocity.slide(up_direction);
}
- linear_velocity += current_platform_velocity;
+ motion_velocity += current_platform_velocity;
}
}
- // Reset the gravity accumulation when touching the ground.
- if (collision_state.floor && linear_velocity.dot(up_direction) <= 0) {
- linear_velocity = linear_velocity.slide(up_direction);
- }
-
return motion_results.size() > 0;
}
void CharacterBody3D::_move_and_slide_grounded(double p_delta, bool p_was_on_floor) {
- Vector3 motion = linear_velocity * p_delta;
+ Vector3 motion = motion_velocity * p_delta;
Vector3 motion_slide_up = motion.slide(up_direction);
Vector3 prev_floor_normal = floor_normal;
@@ -1178,12 +1177,13 @@ void CharacterBody3D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo
// Constant speed can be applied only the first time sliding is enabled.
bool can_apply_constant_speed = sliding_enabled;
bool first_slide = true;
- bool vel_dir_facing_up = linear_velocity.dot(up_direction) > 0;
+ bool vel_dir_facing_up = motion_velocity.dot(up_direction) > 0;
Vector3 total_travel;
for (int iteration = 0; iteration < max_slides; ++iteration) {
PhysicsServer3D::MotionResult result;
bool collided = move_and_collide(motion, result, margin, false, 4, !sliding_enabled);
+ last_motion = result.travel;
if (collided) {
motion_results.push_back(result);
@@ -1193,20 +1193,20 @@ void CharacterBody3D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo
CollisionState result_state;
_set_collision_direction(result, result_state);
- if (collision_state.floor && floor_stop_on_slope && (linear_velocity.normalized() + up_direction).length() < 0.01) {
+ if (collision_state.floor && floor_stop_on_slope && (motion_velocity.normalized() + up_direction).length() < 0.01) {
Transform3D gt = get_global_transform();
if (result.travel.length() <= margin + CMP_EPSILON) {
gt.origin -= result.travel;
}
set_global_transform(gt);
- linear_velocity = Vector3();
+ motion_velocity = Vector3();
motion = Vector3();
+ last_motion = Vector3();
break;
}
if (result.remainder.is_equal_approx(Vector3())) {
motion = Vector3();
- last_motion = result.travel;
break;
}
@@ -1255,7 +1255,7 @@ void CharacterBody3D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo
Vector3 forward = wall_normal.slide(up_direction).normalized();
motion = motion.slide(forward);
// Avoid accelerating when you jump on the wall and smooth falling.
- linear_velocity = linear_velocity.slide(forward);
+ motion_velocity = motion_velocity.slide(forward);
// Allow only lateral motion along previous floor when already on floor.
// Fixes slowing down when moving in diagonal against an inclined wall.
@@ -1284,7 +1284,7 @@ void CharacterBody3D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo
if (stop_all_motion) {
motion = Vector3();
- linear_velocity = Vector3();
+ motion_velocity = Vector3();
}
}
}
@@ -1295,7 +1295,7 @@ void CharacterBody3D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo
real_t motion_angle = Math::abs(Math::acos(-horizontal_normal.dot(motion_slide_up.normalized())));
if (motion_angle < wall_min_slide_angle) {
motion = up_direction * motion.dot(up_direction);
- linear_velocity = up_direction * linear_velocity.dot(up_direction);
+ motion_velocity = up_direction * motion_velocity.dot(up_direction);
apply_default_sliding = false;
}
@@ -1306,19 +1306,33 @@ void CharacterBody3D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo
// Regular sliding, the last part of the test handle the case when you don't want to slide on the ceiling.
if ((sliding_enabled || !collision_state.floor) && (!collision_state.ceiling || slide_on_ceiling || !vel_dir_facing_up)) {
const PhysicsServer3D::MotionCollision &collision = result.collisions[0];
+
Vector3 slide_motion = result.remainder.slide(collision.normal);
- if (slide_motion.dot(linear_velocity) > 0.0) {
+ if (collision_state.floor && !collision_state.wall) {
+ // Slide using the intersection between the motion plane and the floor plane,
+ // in order to keep the direction intact.
+ real_t motion_length = slide_motion.length();
+ slide_motion = up_direction.cross(result.remainder).cross(floor_normal);
+
+ // Keep the length from default slide to change speed in slopes by default,
+ // when constant speed is not enabled.
+ slide_motion.normalize();
+ slide_motion *= motion_length;
+ }
+
+ if (slide_motion.dot(motion_velocity) > 0.0) {
motion = slide_motion;
} else {
motion = Vector3();
}
+
if (slide_on_ceiling && result_state.ceiling) {
// Apply slide only in the direction of the input motion, otherwise just stop to avoid jittering when moving against a wall.
if (vel_dir_facing_up) {
- linear_velocity = linear_velocity.slide(collision.normal);
+ motion_velocity = motion_velocity.slide(collision.normal);
} else {
// Avoid acceleration in slope when falling.
- linear_velocity = up_direction * up_direction.dot(linear_velocity);
+ motion_velocity = up_direction * up_direction.dot(motion_velocity);
}
}
}
@@ -1326,18 +1340,19 @@ void CharacterBody3D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo
else {
motion = result.remainder;
if (result_state.ceiling && !slide_on_ceiling && vel_dir_facing_up) {
- linear_velocity = linear_velocity.slide(up_direction);
+ motion_velocity = motion_velocity.slide(up_direction);
motion = motion.slide(up_direction);
}
}
}
+ total_travel += result.travel;
+
// Apply Constant Speed.
- if (p_was_on_floor && floor_constant_speed && collision_state.floor && !motion.is_equal_approx(Vector3())) {
- motion = motion.normalized() * MAX(0, (motion_slide_up.length() - result.travel.slide(up_direction).length() - total_travel.slide(up_direction).length()));
+ if (p_was_on_floor && floor_constant_speed && can_apply_constant_speed && collision_state.floor && !motion.is_equal_approx(Vector3())) {
+ Vector3 travel_slide_up = total_travel.slide(up_direction);
+ motion = motion.normalized() * MAX(0, (motion_slide_up.length() - travel_slide_up.length()));
}
-
- total_travel += result.travel;
}
// When you move forward in a downward slope you don’t collide because you will be in the air.
// This test ensures that constant speed is applied, only if the player is still on the ground after the snap is applied.
@@ -1348,34 +1363,34 @@ void CharacterBody3D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo
gt.origin = gt.origin - result.travel;
set_global_transform(gt);
- Vector3 motion_slide_norm = motion.slide(prev_floor_normal).normalized();
+ // Slide using the intersection between the motion plane and the floor plane,
+ // in order to keep the direction intact.
+ Vector3 motion_slide_norm = up_direction.cross(motion).cross(prev_floor_normal);
+ motion_slide_norm.normalize();
+
motion = motion_slide_norm * (motion_slide_up.length());
collided = true;
}
- can_apply_constant_speed = !can_apply_constant_speed && !sliding_enabled;
- sliding_enabled = true;
- first_slide = false;
-
- if (!motion.is_equal_approx(Vector3())) {
- last_motion = motion;
- }
-
if (!collided || motion.is_equal_approx(Vector3())) {
break;
}
+
+ can_apply_constant_speed = !can_apply_constant_speed && !sliding_enabled;
+ sliding_enabled = true;
+ first_slide = false;
}
_snap_on_floor(p_was_on_floor, vel_dir_facing_up);
// Reset the gravity accumulation when touching the ground.
if (collision_state.floor && !vel_dir_facing_up) {
- linear_velocity = linear_velocity.slide(up_direction);
+ motion_velocity = motion_velocity.slide(up_direction);
}
}
void CharacterBody3D::_move_and_slide_free(double p_delta) {
- Vector3 motion = linear_velocity * p_delta;
+ Vector3 motion = motion_velocity * p_delta;
platform_rid = RID();
floor_normal = Vector3();
@@ -1386,6 +1401,7 @@ void CharacterBody3D::_move_and_slide_free(double p_delta) {
PhysicsServer3D::MotionResult result;
bool collided = move_and_collide(motion, result, margin, false, 1, false);
+ last_motion = result.travel;
if (collided) {
motion_results.push_back(result);
@@ -1393,7 +1409,12 @@ void CharacterBody3D::_move_and_slide_free(double p_delta) {
CollisionState result_state;
_set_collision_direction(result, result_state);
- if (wall_min_slide_angle != 0 && Math::acos(wall_normal.dot(-linear_velocity.normalized())) < wall_min_slide_angle + FLOOR_ANGLE_THRESHOLD) {
+ if (result.remainder.is_equal_approx(Vector3())) {
+ motion = Vector3();
+ break;
+ }
+
+ if (wall_min_slide_angle != 0 && Math::acos(wall_normal.dot(-motion_velocity.normalized())) < wall_min_slide_angle + FLOOR_ANGLE_THRESHOLD) {
motion = Vector3();
if (result.travel.length() < margin + CMP_EPSILON) {
Transform3D gt = get_global_transform();
@@ -1407,16 +1428,16 @@ void CharacterBody3D::_move_and_slide_free(double p_delta) {
motion = result.remainder.slide(wall_normal);
}
- if (motion.dot(linear_velocity) <= 0.0) {
+ if (motion.dot(motion_velocity) <= 0.0) {
motion = Vector3();
}
}
- first_slide = false;
-
if (!collided || motion.is_equal_approx(Vector3())) {
break;
}
+
+ first_slide = false;
}
}
@@ -1568,12 +1589,12 @@ real_t CharacterBody3D::get_safe_margin() const {
return margin;
}
-Vector3 CharacterBody3D::get_linear_velocity() const {
- return linear_velocity;
+const Vector3 &CharacterBody3D::get_motion_velocity() const {
+ return motion_velocity;
}
-void CharacterBody3D::set_linear_velocity(const Vector3 &p_velocity) {
- linear_velocity = p_velocity;
+void CharacterBody3D::set_motion_velocity(const Vector3 &p_velocity) {
+ motion_velocity = p_velocity;
}
bool CharacterBody3D::is_on_floor() const {
@@ -1600,15 +1621,15 @@ bool CharacterBody3D::is_on_ceiling_only() const {
return collision_state.ceiling && !collision_state.floor && !collision_state.wall;
}
-Vector3 CharacterBody3D::get_floor_normal() const {
+const Vector3 &CharacterBody3D::get_floor_normal() const {
return floor_normal;
}
-Vector3 CharacterBody3D::get_wall_normal() const {
+const Vector3 &CharacterBody3D::get_wall_normal() const {
return wall_normal;
}
-Vector3 CharacterBody3D::get_last_motion() const {
+const Vector3 &CharacterBody3D::get_last_motion() const {
return last_motion;
}
@@ -1616,19 +1637,23 @@ Vector3 CharacterBody3D::get_position_delta() const {
return get_transform().origin - previous_position;
}
-Vector3 CharacterBody3D::get_real_velocity() const {
+const Vector3 &CharacterBody3D::get_real_velocity() const {
return real_velocity;
-};
+}
real_t CharacterBody3D::get_floor_angle(const Vector3 &p_up_direction) const {
ERR_FAIL_COND_V(p_up_direction == Vector3(), 0);
return Math::acos(floor_normal.dot(p_up_direction));
}
-Vector3 CharacterBody3D::get_platform_velocity() const {
+const Vector3 &CharacterBody3D::get_platform_velocity() const {
return platform_velocity;
}
+Vector3 CharacterBody3D::get_linear_velocity() const {
+ return get_real_velocity();
+}
+
int CharacterBody3D::get_slide_collision_count() const {
return motion_results.size();
}
@@ -1783,8 +1808,8 @@ void CharacterBody3D::_notification(int p_what) {
void CharacterBody3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("move_and_slide"), &CharacterBody3D::move_and_slide);
- ClassDB::bind_method(D_METHOD("set_linear_velocity", "linear_velocity"), &CharacterBody3D::set_linear_velocity);
- ClassDB::bind_method(D_METHOD("get_linear_velocity"), &CharacterBody3D::get_linear_velocity);
+ ClassDB::bind_method(D_METHOD("set_motion_velocity", "motion_velocity"), &CharacterBody3D::set_motion_velocity);
+ ClassDB::bind_method(D_METHOD("get_motion_velocity"), &CharacterBody3D::get_motion_velocity);
ClassDB::bind_method(D_METHOD("set_safe_margin", "pixels"), &CharacterBody3D::set_safe_margin);
ClassDB::bind_method(D_METHOD("get_safe_margin"), &CharacterBody3D::get_safe_margin);
@@ -1837,7 +1862,7 @@ void CharacterBody3D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "motion_mode", PROPERTY_HINT_ENUM, "Grounded,Free", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), "set_motion_mode", "get_motion_mode");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "up_direction"), "set_up_direction", "get_up_direction");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "slide_on_ceiling"), "set_slide_on_ceiling_enabled", "is_slide_on_ceiling_enabled");
- ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "linear_velocity", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_linear_velocity", "get_linear_velocity");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "motion_velocity", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_motion_velocity", "get_motion_velocity");
ADD_PROPERTY(PropertyInfo(Variant::INT, "max_slides", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_max_slides", "get_max_slides");
ADD_GROUP("Free Mode", "free_mode_");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "wall_min_slide_angle", PROPERTY_HINT_RANGE, "0,180,0.1,radians", PROPERTY_USAGE_DEFAULT), "set_wall_min_slide_angle", "get_wall_min_slide_angle");
diff --git a/scene/3d/physics_body_3d.h b/scene/3d/physics_body_3d.h
index a53147cb8f..0f25d2469f 100644
--- a/scene/3d/physics_body_3d.h
+++ b/scene/3d/physics_body_3d.h
@@ -328,8 +328,8 @@ public:
};
bool move_and_slide();
- virtual Vector3 get_linear_velocity() const override;
- void set_linear_velocity(const Vector3 &p_velocity);
+ const Vector3 &get_motion_velocity() const;
+ void set_motion_velocity(const Vector3 &p_velocity);
bool is_on_floor() const;
bool is_on_floor_only() const;
@@ -337,13 +337,15 @@ public:
bool is_on_wall_only() const;
bool is_on_ceiling() const;
bool is_on_ceiling_only() const;
- Vector3 get_last_motion() const;
+ const Vector3 &get_last_motion() const;
Vector3 get_position_delta() const;
- Vector3 get_floor_normal() const;
- Vector3 get_wall_normal() const;
- Vector3 get_real_velocity() const;
+ const Vector3 &get_floor_normal() const;
+ const Vector3 &get_wall_normal() const;
+ const Vector3 &get_real_velocity() const;
real_t get_floor_angle(const Vector3 &p_up_direction = Vector3(0.0, 1.0, 0.0)) const;
- Vector3 get_platform_velocity() const;
+ const Vector3 &get_platform_velocity() const;
+
+ virtual Vector3 get_linear_velocity() const override;
int get_slide_collision_count() const;
PhysicsServer3D::MotionResult get_slide_collision(int p_bounce) const;
@@ -379,7 +381,7 @@ private:
bool floor_block_on_wall = true;
bool slide_on_ceiling = true;
int max_slides = 6;
- int platform_layer;
+ int platform_layer = 0;
RID platform_rid;
uint32_t moving_platform_floor_layers = UINT32_MAX;
uint32_t moving_platform_wall_layers = 0;
@@ -387,7 +389,7 @@ private:
real_t floor_max_angle = Math::deg2rad((real_t)45.0);
real_t wall_min_slide_angle = Math::deg2rad((real_t)15.0);
Vector3 up_direction = Vector3(0.0, 1.0, 0.0);
- Vector3 linear_velocity;
+ Vector3 motion_velocity;
Vector3 floor_normal;
Vector3 wall_normal;
Vector3 last_motion;
diff --git a/scene/resources/texture.cpp b/scene/resources/texture.cpp
index 80cab99373..1b3a7a5f2a 100644
--- a/scene/resources/texture.cpp
+++ b/scene/resources/texture.cpp
@@ -1249,7 +1249,7 @@ bool AtlasTexture::is_pixel_opaque(int p_x, int p_y) const {
}
Ref<Image> AtlasTexture::get_image() const {
- if (!atlas.is_valid()) {
+ if (!atlas.is_valid() || !atlas->get_image().is_valid()) {
return Ref<Image>();
}
diff --git a/servers/rendering_server.h b/servers/rendering_server.h
index 1b04a6e5e2..579f8abbe6 100644
--- a/servers/rendering_server.h
+++ b/servers/rendering_server.h
@@ -295,7 +295,7 @@ public:
AABB aabb;
struct LOD {
- float edge_length;
+ float edge_length = 0.0f;
Vector<uint8_t> index_data;
};
Vector<LOD> lods;
diff --git a/thirdparty/README.md b/thirdparty/README.md
index 3403a0be31..3d32609088 100644
--- a/thirdparty/README.md
+++ b/thirdparty/README.md
@@ -373,7 +373,9 @@ Files extracted from upstream repository:
- `LICENSE.md`.
An [experimental upstream feature](https://github.com/zeux/meshoptimizer/tree/simplify-attr),
-has been backported, see patch in `patches` directory.
+has been backported. On top of that, it was modified to report only distance error metrics
+instead of a combination of distance and attribute errors. Patches for both changes can be
+found in the `patches` directory.
## miniupnpc
diff --git a/thirdparty/meshoptimizer/patches/attribute-aware-simplify-distance-only-metric.patch b/thirdparty/meshoptimizer/patches/attribute-aware-simplify-distance-only-metric.patch
new file mode 100644
index 0000000000..54132a6c86
--- /dev/null
+++ b/thirdparty/meshoptimizer/patches/attribute-aware-simplify-distance-only-metric.patch
@@ -0,0 +1,176 @@
+diff --git a/thirdparty/meshoptimizer/simplifier.cpp b/thirdparty/meshoptimizer/simplifier.cpp
+index 0f10ebef4b..cf5db4e119 100644
+--- a/thirdparty/meshoptimizer/simplifier.cpp
++++ b/thirdparty/meshoptimizer/simplifier.cpp
+@@ -20,7 +20,7 @@
+ #define TRACESTATS(i) (void)0
+ #endif
+
+-#define ATTRIBUTES 8
++#define ATTRIBUTES 3
+
+ // This work is based on:
+ // Michael Garland and Paul S. Heckbert. Surface simplification using quadric error metrics. 1997
+@@ -445,6 +445,7 @@ struct Collapse
+ float error;
+ unsigned int errorui;
+ };
++ float distance_error;
+ };
+
+ static float normalize(Vector3& v)
+@@ -525,6 +526,34 @@ static float quadricError(const Quadric& Q, const Vector3& v)
+ return fabsf(r) * s;
+ }
+
++static float quadricErrorNoAttributes(const Quadric& Q, const Vector3& v)
++{
++ float rx = Q.b0;
++ float ry = Q.b1;
++ float rz = Q.b2;
++
++ rx += Q.a10 * v.y;
++ ry += Q.a21 * v.z;
++ rz += Q.a20 * v.x;
++
++ rx *= 2;
++ ry *= 2;
++ rz *= 2;
++
++ rx += Q.a00 * v.x;
++ ry += Q.a11 * v.y;
++ rz += Q.a22 * v.z;
++
++ float r = Q.c;
++ r += rx * v.x;
++ r += ry * v.y;
++ r += rz * v.z;
++
++ float s = Q.w == 0.f ? 0.f : 1.f / Q.w;
++
++ return fabsf(r) * s;
++}
++
+ static void quadricFromPlane(Quadric& Q, float a, float b, float c, float d, float w)
+ {
+ float aw = a * w;
+@@ -680,7 +709,7 @@ static void quadricUpdateAttributes(Quadric& Q, const Vector3& p0, const Vector3
+ }
+ #endif
+
+-static void fillFaceQuadrics(Quadric* vertex_quadrics, const unsigned int* indices, size_t index_count, const Vector3* vertex_positions, const unsigned int* remap)
++static void fillFaceQuadrics(Quadric* vertex_quadrics, Quadric* vertex_no_attrib_quadrics, const unsigned int* indices, size_t index_count, const Vector3* vertex_positions, const unsigned int* remap)
+ {
+ for (size_t i = 0; i < index_count; i += 3)
+ {
+@@ -690,6 +719,9 @@ static void fillFaceQuadrics(Quadric* vertex_quadrics, const unsigned int* indic
+
+ Quadric Q;
+ quadricFromTriangle(Q, vertex_positions[i0], vertex_positions[i1], vertex_positions[i2], 1.f);
++ quadricAdd(vertex_no_attrib_quadrics[remap[i0]], Q);
++ quadricAdd(vertex_no_attrib_quadrics[remap[i1]], Q);
++ quadricAdd(vertex_no_attrib_quadrics[remap[i2]], Q);
+
+ #if ATTRIBUTES
+ quadricUpdateAttributes(Q, vertex_positions[i0], vertex_positions[i1], vertex_positions[i2], Q.w);
+@@ -700,7 +732,7 @@ static void fillFaceQuadrics(Quadric* vertex_quadrics, const unsigned int* indic
+ }
+ }
+
+-static void fillEdgeQuadrics(Quadric* vertex_quadrics, const unsigned int* indices, size_t index_count, const Vector3* vertex_positions, const unsigned int* remap, const unsigned char* vertex_kind, const unsigned int* loop, const unsigned int* loopback)
++static void fillEdgeQuadrics(Quadric* vertex_quadrics, Quadric* vertex_no_attrib_quadrics, const unsigned int* indices, size_t index_count, const Vector3* vertex_positions, const unsigned int* remap, const unsigned char* vertex_kind, const unsigned int* loop, const unsigned int* loopback)
+ {
+ for (size_t i = 0; i < index_count; i += 3)
+ {
+@@ -744,6 +776,9 @@ static void fillEdgeQuadrics(Quadric* vertex_quadrics, const unsigned int* indic
+
+ quadricAdd(vertex_quadrics[remap[i0]], Q);
+ quadricAdd(vertex_quadrics[remap[i1]], Q);
++
++ quadricAdd(vertex_no_attrib_quadrics[remap[i0]], Q);
++ quadricAdd(vertex_no_attrib_quadrics[remap[i1]], Q);
+ }
+ }
+ }
+@@ -848,7 +883,7 @@ static size_t pickEdgeCollapses(Collapse* collapses, const unsigned int* indices
+ return collapse_count;
+ }
+
+-static void rankEdgeCollapses(Collapse* collapses, size_t collapse_count, const Vector3* vertex_positions, const Quadric* vertex_quadrics, const unsigned int* remap)
++static void rankEdgeCollapses(Collapse* collapses, size_t collapse_count, const Vector3* vertex_positions, const Quadric* vertex_quadrics, const Quadric* vertex_no_attrib_quadrics, const unsigned int* remap)
+ {
+ for (size_t i = 0; i < collapse_count; ++i)
+ {
+@@ -868,10 +903,14 @@ static void rankEdgeCollapses(Collapse* collapses, size_t collapse_count, const
+ float ei = quadricError(qi, vertex_positions[i1]);
+ float ej = quadricError(qj, vertex_positions[j1]);
+
++ const Quadric& naqi = vertex_no_attrib_quadrics[remap[i0]];
++ const Quadric& naqj = vertex_no_attrib_quadrics[remap[j0]];
++
+ // pick edge direction with minimal error
+ c.v0 = ei <= ej ? i0 : j0;
+ c.v1 = ei <= ej ? i1 : j1;
+ c.error = ei <= ej ? ei : ej;
++ c.distance_error = ei <= ej ? quadricErrorNoAttributes(naqi, vertex_positions[i1]) : quadricErrorNoAttributes(naqj, vertex_positions[j1]);
+ }
+ }
+
+@@ -968,7 +1007,7 @@ static void sortEdgeCollapses(unsigned int* sort_order, const Collapse* collapse
+ }
+ }
+
+-static size_t performEdgeCollapses(unsigned int* collapse_remap, unsigned char* collapse_locked, Quadric* vertex_quadrics, const Collapse* collapses, size_t collapse_count, const unsigned int* collapse_order, const unsigned int* remap, const unsigned int* wedge, const unsigned char* vertex_kind, const Vector3* vertex_positions, const EdgeAdjacency& adjacency, size_t triangle_collapse_goal, float error_limit, float& result_error)
++static size_t performEdgeCollapses(unsigned int* collapse_remap, unsigned char* collapse_locked, Quadric* vertex_quadrics, Quadric* vertex_no_attrib_quadrics, const Collapse* collapses, size_t collapse_count, const unsigned int* collapse_order, const unsigned int* remap, const unsigned int* wedge, const unsigned char* vertex_kind, const Vector3* vertex_positions, const EdgeAdjacency& adjacency, size_t triangle_collapse_goal, float error_limit, float& result_error)
+ {
+ size_t edge_collapses = 0;
+ size_t triangle_collapses = 0;
+@@ -1030,6 +1069,7 @@ static size_t performEdgeCollapses(unsigned int* collapse_remap, unsigned char*
+ assert(collapse_remap[r1] == r1);
+
+ quadricAdd(vertex_quadrics[r1], vertex_quadrics[r0]);
++ quadricAdd(vertex_no_attrib_quadrics[r1], vertex_no_attrib_quadrics[r0]);
+
+ if (vertex_kind[i0] == Kind_Complex)
+ {
+@@ -1067,7 +1107,7 @@ static size_t performEdgeCollapses(unsigned int* collapse_remap, unsigned char*
+ triangle_collapses += (vertex_kind[i0] == Kind_Border) ? 1 : 2;
+ edge_collapses++;
+
+- result_error = result_error < c.error ? c.error : result_error;
++ result_error = result_error < c.distance_error ? c.distance_error : result_error;
+ }
+
+ #if TRACE
+@@ -1455,9 +1495,11 @@ size_t meshopt_simplifyWithAttributes(unsigned int* destination, const unsigned
+
+ Quadric* vertex_quadrics = allocator.allocate<Quadric>(vertex_count);
+ memset(vertex_quadrics, 0, vertex_count * sizeof(Quadric));
++ Quadric* vertex_no_attrib_quadrics = allocator.allocate<Quadric>(vertex_count);
++ memset(vertex_no_attrib_quadrics, 0, vertex_count * sizeof(Quadric));
+
+- fillFaceQuadrics(vertex_quadrics, indices, index_count, vertex_positions, remap);
+- fillEdgeQuadrics(vertex_quadrics, indices, index_count, vertex_positions, remap, vertex_kind, loop, loopback);
++ fillFaceQuadrics(vertex_quadrics, vertex_no_attrib_quadrics, indices, index_count, vertex_positions, remap);
++ fillEdgeQuadrics(vertex_quadrics, vertex_no_attrib_quadrics, indices, index_count, vertex_positions, remap, vertex_kind, loop, loopback);
+
+ if (result != indices)
+ memcpy(result, indices, index_count * sizeof(unsigned int));
+@@ -1488,7 +1530,7 @@ size_t meshopt_simplifyWithAttributes(unsigned int* destination, const unsigned
+ if (edge_collapse_count == 0)
+ break;
+
+- rankEdgeCollapses(edge_collapses, edge_collapse_count, vertex_positions, vertex_quadrics, remap);
++ rankEdgeCollapses(edge_collapses, edge_collapse_count, vertex_positions, vertex_quadrics, vertex_no_attrib_quadrics, remap);
+
+ #if TRACE > 1
+ dumpEdgeCollapses(edge_collapses, edge_collapse_count, vertex_kind);
+@@ -1507,7 +1549,7 @@ size_t meshopt_simplifyWithAttributes(unsigned int* destination, const unsigned
+ printf("pass %d: ", int(pass_count++));
+ #endif
+
+- size_t collapses = performEdgeCollapses(collapse_remap, collapse_locked, vertex_quadrics, edge_collapses, edge_collapse_count, collapse_order, remap, wedge, vertex_kind, vertex_positions, adjacency, triangle_collapse_goal, error_limit, result_error);
++ size_t collapses = performEdgeCollapses(collapse_remap, collapse_locked, vertex_quadrics, vertex_no_attrib_quadrics, edge_collapses, edge_collapse_count, collapse_order, remap, wedge, vertex_kind, vertex_positions, adjacency, triangle_collapse_goal, error_limit, result_error);
+
+ // no edges can be collapsed any more due to hitting the error limit or triangle collapse limit
+ if (collapses == 0)
diff --git a/thirdparty/meshoptimizer/simplifier.cpp b/thirdparty/meshoptimizer/simplifier.cpp
index 0f10ebef4b..cf5db4e119 100644
--- a/thirdparty/meshoptimizer/simplifier.cpp
+++ b/thirdparty/meshoptimizer/simplifier.cpp
@@ -20,7 +20,7 @@
#define TRACESTATS(i) (void)0
#endif
-#define ATTRIBUTES 8
+#define ATTRIBUTES 3
// This work is based on:
// Michael Garland and Paul S. Heckbert. Surface simplification using quadric error metrics. 1997
@@ -445,6 +445,7 @@ struct Collapse
float error;
unsigned int errorui;
};
+ float distance_error;
};
static float normalize(Vector3& v)
@@ -525,6 +526,34 @@ static float quadricError(const Quadric& Q, const Vector3& v)
return fabsf(r) * s;
}
+static float quadricErrorNoAttributes(const Quadric& Q, const Vector3& v)
+{
+ float rx = Q.b0;
+ float ry = Q.b1;
+ float rz = Q.b2;
+
+ rx += Q.a10 * v.y;
+ ry += Q.a21 * v.z;
+ rz += Q.a20 * v.x;
+
+ rx *= 2;
+ ry *= 2;
+ rz *= 2;
+
+ rx += Q.a00 * v.x;
+ ry += Q.a11 * v.y;
+ rz += Q.a22 * v.z;
+
+ float r = Q.c;
+ r += rx * v.x;
+ r += ry * v.y;
+ r += rz * v.z;
+
+ float s = Q.w == 0.f ? 0.f : 1.f / Q.w;
+
+ return fabsf(r) * s;
+}
+
static void quadricFromPlane(Quadric& Q, float a, float b, float c, float d, float w)
{
float aw = a * w;
@@ -680,7 +709,7 @@ static void quadricUpdateAttributes(Quadric& Q, const Vector3& p0, const Vector3
}
#endif
-static void fillFaceQuadrics(Quadric* vertex_quadrics, const unsigned int* indices, size_t index_count, const Vector3* vertex_positions, const unsigned int* remap)
+static void fillFaceQuadrics(Quadric* vertex_quadrics, Quadric* vertex_no_attrib_quadrics, const unsigned int* indices, size_t index_count, const Vector3* vertex_positions, const unsigned int* remap)
{
for (size_t i = 0; i < index_count; i += 3)
{
@@ -690,6 +719,9 @@ static void fillFaceQuadrics(Quadric* vertex_quadrics, const unsigned int* indic
Quadric Q;
quadricFromTriangle(Q, vertex_positions[i0], vertex_positions[i1], vertex_positions[i2], 1.f);
+ quadricAdd(vertex_no_attrib_quadrics[remap[i0]], Q);
+ quadricAdd(vertex_no_attrib_quadrics[remap[i1]], Q);
+ quadricAdd(vertex_no_attrib_quadrics[remap[i2]], Q);
#if ATTRIBUTES
quadricUpdateAttributes(Q, vertex_positions[i0], vertex_positions[i1], vertex_positions[i2], Q.w);
@@ -700,7 +732,7 @@ static void fillFaceQuadrics(Quadric* vertex_quadrics, const unsigned int* indic
}
}
-static void fillEdgeQuadrics(Quadric* vertex_quadrics, const unsigned int* indices, size_t index_count, const Vector3* vertex_positions, const unsigned int* remap, const unsigned char* vertex_kind, const unsigned int* loop, const unsigned int* loopback)
+static void fillEdgeQuadrics(Quadric* vertex_quadrics, Quadric* vertex_no_attrib_quadrics, const unsigned int* indices, size_t index_count, const Vector3* vertex_positions, const unsigned int* remap, const unsigned char* vertex_kind, const unsigned int* loop, const unsigned int* loopback)
{
for (size_t i = 0; i < index_count; i += 3)
{
@@ -744,6 +776,9 @@ static void fillEdgeQuadrics(Quadric* vertex_quadrics, const unsigned int* indic
quadricAdd(vertex_quadrics[remap[i0]], Q);
quadricAdd(vertex_quadrics[remap[i1]], Q);
+
+ quadricAdd(vertex_no_attrib_quadrics[remap[i0]], Q);
+ quadricAdd(vertex_no_attrib_quadrics[remap[i1]], Q);
}
}
}
@@ -848,7 +883,7 @@ static size_t pickEdgeCollapses(Collapse* collapses, const unsigned int* indices
return collapse_count;
}
-static void rankEdgeCollapses(Collapse* collapses, size_t collapse_count, const Vector3* vertex_positions, const Quadric* vertex_quadrics, const unsigned int* remap)
+static void rankEdgeCollapses(Collapse* collapses, size_t collapse_count, const Vector3* vertex_positions, const Quadric* vertex_quadrics, const Quadric* vertex_no_attrib_quadrics, const unsigned int* remap)
{
for (size_t i = 0; i < collapse_count; ++i)
{
@@ -868,10 +903,14 @@ static void rankEdgeCollapses(Collapse* collapses, size_t collapse_count, const
float ei = quadricError(qi, vertex_positions[i1]);
float ej = quadricError(qj, vertex_positions[j1]);
+ const Quadric& naqi = vertex_no_attrib_quadrics[remap[i0]];
+ const Quadric& naqj = vertex_no_attrib_quadrics[remap[j0]];
+
// pick edge direction with minimal error
c.v0 = ei <= ej ? i0 : j0;
c.v1 = ei <= ej ? i1 : j1;
c.error = ei <= ej ? ei : ej;
+ c.distance_error = ei <= ej ? quadricErrorNoAttributes(naqi, vertex_positions[i1]) : quadricErrorNoAttributes(naqj, vertex_positions[j1]);
}
}
@@ -968,7 +1007,7 @@ static void sortEdgeCollapses(unsigned int* sort_order, const Collapse* collapse
}
}
-static size_t performEdgeCollapses(unsigned int* collapse_remap, unsigned char* collapse_locked, Quadric* vertex_quadrics, const Collapse* collapses, size_t collapse_count, const unsigned int* collapse_order, const unsigned int* remap, const unsigned int* wedge, const unsigned char* vertex_kind, const Vector3* vertex_positions, const EdgeAdjacency& adjacency, size_t triangle_collapse_goal, float error_limit, float& result_error)
+static size_t performEdgeCollapses(unsigned int* collapse_remap, unsigned char* collapse_locked, Quadric* vertex_quadrics, Quadric* vertex_no_attrib_quadrics, const Collapse* collapses, size_t collapse_count, const unsigned int* collapse_order, const unsigned int* remap, const unsigned int* wedge, const unsigned char* vertex_kind, const Vector3* vertex_positions, const EdgeAdjacency& adjacency, size_t triangle_collapse_goal, float error_limit, float& result_error)
{
size_t edge_collapses = 0;
size_t triangle_collapses = 0;
@@ -1030,6 +1069,7 @@ static size_t performEdgeCollapses(unsigned int* collapse_remap, unsigned char*
assert(collapse_remap[r1] == r1);
quadricAdd(vertex_quadrics[r1], vertex_quadrics[r0]);
+ quadricAdd(vertex_no_attrib_quadrics[r1], vertex_no_attrib_quadrics[r0]);
if (vertex_kind[i0] == Kind_Complex)
{
@@ -1067,7 +1107,7 @@ static size_t performEdgeCollapses(unsigned int* collapse_remap, unsigned char*
triangle_collapses += (vertex_kind[i0] == Kind_Border) ? 1 : 2;
edge_collapses++;
- result_error = result_error < c.error ? c.error : result_error;
+ result_error = result_error < c.distance_error ? c.distance_error : result_error;
}
#if TRACE
@@ -1455,9 +1495,11 @@ size_t meshopt_simplifyWithAttributes(unsigned int* destination, const unsigned
Quadric* vertex_quadrics = allocator.allocate<Quadric>(vertex_count);
memset(vertex_quadrics, 0, vertex_count * sizeof(Quadric));
+ Quadric* vertex_no_attrib_quadrics = allocator.allocate<Quadric>(vertex_count);
+ memset(vertex_no_attrib_quadrics, 0, vertex_count * sizeof(Quadric));
- fillFaceQuadrics(vertex_quadrics, indices, index_count, vertex_positions, remap);
- fillEdgeQuadrics(vertex_quadrics, indices, index_count, vertex_positions, remap, vertex_kind, loop, loopback);
+ fillFaceQuadrics(vertex_quadrics, vertex_no_attrib_quadrics, indices, index_count, vertex_positions, remap);
+ fillEdgeQuadrics(vertex_quadrics, vertex_no_attrib_quadrics, indices, index_count, vertex_positions, remap, vertex_kind, loop, loopback);
if (result != indices)
memcpy(result, indices, index_count * sizeof(unsigned int));
@@ -1488,7 +1530,7 @@ size_t meshopt_simplifyWithAttributes(unsigned int* destination, const unsigned
if (edge_collapse_count == 0)
break;
- rankEdgeCollapses(edge_collapses, edge_collapse_count, vertex_positions, vertex_quadrics, remap);
+ rankEdgeCollapses(edge_collapses, edge_collapse_count, vertex_positions, vertex_quadrics, vertex_no_attrib_quadrics, remap);
#if TRACE > 1
dumpEdgeCollapses(edge_collapses, edge_collapse_count, vertex_kind);
@@ -1507,7 +1549,7 @@ size_t meshopt_simplifyWithAttributes(unsigned int* destination, const unsigned
printf("pass %d: ", int(pass_count++));
#endif
- size_t collapses = performEdgeCollapses(collapse_remap, collapse_locked, vertex_quadrics, edge_collapses, edge_collapse_count, collapse_order, remap, wedge, vertex_kind, vertex_positions, adjacency, triangle_collapse_goal, error_limit, result_error);
+ size_t collapses = performEdgeCollapses(collapse_remap, collapse_locked, vertex_quadrics, vertex_no_attrib_quadrics, edge_collapses, edge_collapse_count, collapse_order, remap, wedge, vertex_kind, vertex_positions, adjacency, triangle_collapse_goal, error_limit, result_error);
// no edges can be collapsed any more due to hitting the error limit or triangle collapse limit
if (collapses == 0)