summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/classes/PhysicsServer2D.xml25
-rw-r--r--doc/classes/PhysicsServer3D.xml29
-rw-r--r--doc/classes/SeparationRayShape2D.xml24
-rw-r--r--doc/classes/SeparationRayShape3D.xml24
-rw-r--r--editor/icons/SeparationRayShape2D.svg1
-rw-r--r--editor/icons/SeparationRayShape3D.svg1
-rw-r--r--editor/import/resource_importer_scene.cpp6
-rw-r--r--editor/plugins/collision_shape_2d_editor_plugin.cpp41
-rw-r--r--editor/plugins/collision_shape_2d_editor_plugin.h1
-rw-r--r--editor/plugins/node_3d_editor_gizmos.cpp53
-rw-r--r--scene/2d/physics_body_2d.cpp10
-rw-r--r--scene/2d/physics_body_2d.h2
-rw-r--r--scene/3d/physics_body_3d.cpp8
-rw-r--r--scene/3d/physics_body_3d.h2
-rw-r--r--scene/register_scene_types.cpp6
-rw-r--r--scene/resources/separation_ray_shape_2d.cpp119
-rw-r--r--scene/resources/separation_ray_shape_2d.h61
-rw-r--r--scene/resources/separation_ray_shape_3d.cpp91
-rw-r--r--scene/resources/separation_ray_shape_3d.h56
-rw-r--r--servers/physics_2d/body_pair_2d_sw.cpp4
-rw-r--r--servers/physics_2d/collision_solver_2d_sat.cpp18
-rw-r--r--servers/physics_2d/collision_solver_2d_sw.cpp82
-rw-r--r--servers/physics_2d/collision_solver_2d_sw.h6
-rw-r--r--servers/physics_2d/physics_server_2d_sw.cpp11
-rw-r--r--servers/physics_2d/physics_server_2d_sw.h3
-rw-r--r--servers/physics_2d/physics_server_2d_wrap_mt.h5
-rw-r--r--servers/physics_2d/shape_2d_sw.cpp49
-rw-r--r--servers/physics_2d/shape_2d_sw.h39
-rw-r--r--servers/physics_2d/space_2d_sw.cpp20
-rw-r--r--servers/physics_2d/space_2d_sw.h2
-rw-r--r--servers/physics_3d/collision_solver_3d_sat.cpp6
-rw-r--r--servers/physics_3d/collision_solver_3d_sw.cpp57
-rw-r--r--servers/physics_3d/collision_solver_3d_sw.h2
-rw-r--r--servers/physics_3d/physics_server_3d_sw.cpp10
-rw-r--r--servers/physics_3d/physics_server_3d_sw.h3
-rw-r--r--servers/physics_3d/physics_server_3d_wrap_mt.h5
-rw-r--r--servers/physics_3d/shape_3d_sw.cpp85
-rw-r--r--servers/physics_3d/shape_3d_sw.h50
-rw-r--r--servers/physics_3d/space_3d_sw.cpp14
-rw-r--r--servers/physics_3d/space_3d_sw.h2
-rw-r--r--servers/physics_server_2d.cpp8
-rw-r--r--servers/physics_server_2d.h6
-rw-r--r--servers/physics_server_3d.cpp10
-rw-r--r--servers/physics_server_3d.h6
44 files changed, 964 insertions, 99 deletions
diff --git a/doc/classes/PhysicsServer2D.xml b/doc/classes/PhysicsServer2D.xml
index 1df2fd0158..d0ae665139 100644
--- a/doc/classes/PhysicsServer2D.xml
+++ b/doc/classes/PhysicsServer2D.xml
@@ -595,7 +595,8 @@
<argument index="2" name="motion" type="Vector2" />
<argument index="3" name="margin" type="float" default="0.08" />
<argument index="4" name="result" type="PhysicsTestMotionResult2D" default="null" />
- <argument index="5" name="exclude" type="Array" default="[]" />
+ <argument index="5" name="collide_separation_ray" type="bool" default="false" />
+ <argument index="6" name="exclude" type="Array" default="[]" />
<description>
Returns [code]true[/code] if a collision would result from moving in the given direction from a given point in space. Margin increases the size of the shapes involved in the collision detection. [PhysicsTestMotionResult2D] can be passed to return additional information in.
</description>
@@ -726,6 +727,11 @@
<description>
</description>
</method>
+ <method name="separation_ray_shape_create">
+ <return type="RID" />
+ <description>
+ </description>
+ </method>
<method name="set_active">
<return type="void" />
<argument index="0" name="active" type="bool" />
@@ -840,25 +846,28 @@
<constant name="SHAPE_WORLD_MARGIN" value="0" enum="ShapeType">
This is the constant for creating world margin shapes. A world margin shape is an [i]infinite[/i] line with an origin point, and a normal. Thus, it can be used for front/behind checks.
</constant>
- <constant name="SHAPE_SEGMENT" value="1" enum="ShapeType">
+ <constant name="SHAPE_SEPARATION_RAY" value="1" enum="ShapeType">
+ This is the constant for creating separation ray shapes. A separation ray is defined by a length and separates itself from what is touching its far endpoint. Useful for character controllers.
+ </constant>
+ <constant name="SHAPE_SEGMENT" value="2" enum="ShapeType">
This is the constant for creating segment shapes. A segment shape is a [i]finite[/i] line from a point A to a point B. It can be checked for intersections.
</constant>
- <constant name="SHAPE_CIRCLE" value="2" enum="ShapeType">
+ <constant name="SHAPE_CIRCLE" value="3" enum="ShapeType">
This is the constant for creating circle shapes. A circle shape only has a radius. It can be used for intersections and inside/outside checks.
</constant>
- <constant name="SHAPE_RECTANGLE" value="3" enum="ShapeType">
+ <constant name="SHAPE_RECTANGLE" value="4" enum="ShapeType">
This is the constant for creating rectangle shapes. A rectangle shape is defined by a width and a height. It can be used for intersections and inside/outside checks.
</constant>
- <constant name="SHAPE_CAPSULE" value="4" enum="ShapeType">
+ <constant name="SHAPE_CAPSULE" value="5" enum="ShapeType">
This is the constant for creating capsule shapes. A capsule shape is defined by a radius and a length. It can be used for intersections and inside/outside checks.
</constant>
- <constant name="SHAPE_CONVEX_POLYGON" value="5" enum="ShapeType">
+ <constant name="SHAPE_CONVEX_POLYGON" value="6" enum="ShapeType">
This is the constant for creating convex polygon shapes. A polygon is defined by a list of points. It can be used for intersections and inside/outside checks. Unlike the [member CollisionPolygon2D.polygon] property, polygons modified with [method shape_set_data] do not verify that the points supplied form is a convex polygon.
</constant>
- <constant name="SHAPE_CONCAVE_POLYGON" value="6" enum="ShapeType">
+ <constant name="SHAPE_CONCAVE_POLYGON" value="7" enum="ShapeType">
This is the constant for creating concave polygon shapes. A polygon is defined by a list of points. It can be used for intersections checks, but not for inside/outside checks.
</constant>
- <constant name="SHAPE_CUSTOM" value="7" enum="ShapeType">
+ <constant name="SHAPE_CUSTOM" value="8" enum="ShapeType">
This constant is used internally by the engine. Any attempt to create this kind of shape results in an error.
</constant>
<constant name="AREA_PARAM_GRAVITY" value="0" enum="AreaParameter">
diff --git a/doc/classes/PhysicsServer3D.xml b/doc/classes/PhysicsServer3D.xml
index 33eb0de698..c47a311161 100644
--- a/doc/classes/PhysicsServer3D.xml
+++ b/doc/classes/PhysicsServer3D.xml
@@ -571,7 +571,8 @@
<argument index="2" name="motion" type="Vector3" />
<argument index="3" name="margin" type="float" default="0.001" />
<argument index="4" name="result" type="PhysicsTestMotionResult3D" default="null" />
- <argument index="5" name="exclude" type="Array" default="[]" />
+ <argument index="5" name="collide_separation_ray" type="bool" default="false" />
+ <argument index="6" name="exclude" type="Array" default="[]" />
<description>
Returns [code]true[/code] if a collision would result from moving in the given direction from a given point in space. Margin increases the size of the shapes involved in the collision detection. [PhysicsTestMotionResult3D] can be passed to return additional information in.
</description>
@@ -849,6 +850,11 @@
<description>
</description>
</method>
+ <method name="separation_ray_shape_create">
+ <return type="RID" />
+ <description>
+ </description>
+ </method>
<method name="set_active">
<return type="void" />
<argument index="0" name="active" type="bool" />
@@ -1171,31 +1177,34 @@
<constant name="SHAPE_PLANE" value="0" enum="ShapeType">
The [Shape3D] is a [WorldMarginShape3D].
</constant>
- <constant name="SHAPE_SPHERE" value="1" enum="ShapeType">
+ <constant name="SHAPE_SEPARATION_RAY" value="1" enum="ShapeType">
+ The [Shape3D] is a [SeparationRayShape3D].
+ </constant>
+ <constant name="SHAPE_SPHERE" value="2" enum="ShapeType">
The [Shape3D] is a [SphereShape3D].
</constant>
- <constant name="SHAPE_BOX" value="2" enum="ShapeType">
+ <constant name="SHAPE_BOX" value="3" enum="ShapeType">
The [Shape3D] is a [BoxShape3D].
</constant>
- <constant name="SHAPE_CAPSULE" value="3" enum="ShapeType">
+ <constant name="SHAPE_CAPSULE" value="4" enum="ShapeType">
The [Shape3D] is a [CapsuleShape3D].
</constant>
- <constant name="SHAPE_CYLINDER" value="4" enum="ShapeType">
+ <constant name="SHAPE_CYLINDER" value="5" enum="ShapeType">
The [Shape3D] is a [CylinderShape3D].
</constant>
- <constant name="SHAPE_CONVEX_POLYGON" value="5" enum="ShapeType">
+ <constant name="SHAPE_CONVEX_POLYGON" value="6" enum="ShapeType">
The [Shape3D] is a [ConvexPolygonShape3D].
</constant>
- <constant name="SHAPE_CONCAVE_POLYGON" value="6" enum="ShapeType">
+ <constant name="SHAPE_CONCAVE_POLYGON" value="7" enum="ShapeType">
The [Shape3D] is a [ConcavePolygonShape3D].
</constant>
- <constant name="SHAPE_HEIGHTMAP" value="7" enum="ShapeType">
+ <constant name="SHAPE_HEIGHTMAP" value="8" enum="ShapeType">
The [Shape3D] is a [HeightMapShape3D].
</constant>
- <constant name="SHAPE_SOFT_BODY" value="8" enum="ShapeType">
+ <constant name="SHAPE_SOFT_BODY" value="9" enum="ShapeType">
The [Shape3D] is a [SoftBody3D].
</constant>
- <constant name="SHAPE_CUSTOM" value="9" enum="ShapeType">
+ <constant name="SHAPE_CUSTOM" value="10" enum="ShapeType">
This constant is used internally by the engine. Any attempt to create this kind of shape results in an error.
</constant>
<constant name="AREA_PARAM_GRAVITY" value="0" enum="AreaParameter">
diff --git a/doc/classes/SeparationRayShape2D.xml b/doc/classes/SeparationRayShape2D.xml
new file mode 100644
index 0000000000..fb90606577
--- /dev/null
+++ b/doc/classes/SeparationRayShape2D.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="SeparationRayShape2D" inherits="Shape2D" version="4.0">
+ <brief_description>
+ Separation ray shape for 2D collisions.
+ </brief_description>
+ <description>
+ Separation ray shape for 2D collisions. A ray is not really a collision body; instead, it tries to separate itself from whatever is touching its far endpoint. It's often useful for characters.
+ </description>
+ <tutorials>
+ </tutorials>
+ <methods>
+ </methods>
+ <members>
+ <member name="length" type="float" setter="set_length" getter="get_length" default="20.0">
+ The ray's length.
+ </member>
+ <member name="slide_on_slope" type="bool" setter="set_slide_on_slope" getter="get_slide_on_slope" default="false">
+ If [code]false[/code] (default), the shape always separates and returns a normal along its own direction.
+ If [code]true[/code], the shape can return the correct normal and separate in any direction, allowing sliding motion on slopes.
+ </member>
+ </members>
+ <constants>
+ </constants>
+</class>
diff --git a/doc/classes/SeparationRayShape3D.xml b/doc/classes/SeparationRayShape3D.xml
new file mode 100644
index 0000000000..ea57e4eb59
--- /dev/null
+++ b/doc/classes/SeparationRayShape3D.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="SeparationRayShape3D" inherits="Shape3D" version="4.0">
+ <brief_description>
+ Separation ray shape for 3D collisions.
+ </brief_description>
+ <description>
+ Separation ray shape for 3D collisions, which can be set into a [PhysicsBody3D] or [Area3D]. A ray is not really a collision body; instead, it tries to separate itself from whatever is touching its far endpoint. It's often useful for characters.
+ </description>
+ <tutorials>
+ </tutorials>
+ <methods>
+ </methods>
+ <members>
+ <member name="length" type="float" setter="set_length" getter="get_length" default="1.0">
+ The ray's length.
+ </member>
+ <member name="slide_on_slope" type="bool" setter="set_slide_on_slope" getter="get_slide_on_slope" default="false">
+ If [code]false[/code] (default), the shape always separates and returns a normal along its own direction.
+ If [code]true[/code], the shape can return the correct normal and separate in any direction, allowing sliding motion on slopes.
+ </member>
+ </members>
+ <constants>
+ </constants>
+</class>
diff --git a/editor/icons/SeparationRayShape2D.svg b/editor/icons/SeparationRayShape2D.svg
new file mode 100644
index 0000000000..aa8cee1210
--- /dev/null
+++ b/editor/icons/SeparationRayShape2D.svg
@@ -0,0 +1 @@
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m8 1a1 1 0 0 0 -1 1v9.5859l-1.293-1.293a1 1 0 0 0 -.7207-.29102 1 1 0 0 0 -.69336.29102 1 1 0 0 0 0 1.4141l3 3a1.0001 1.0001 0 0 0 .0039062.003907 1 1 0 0 0 .050781.044921 1.0001 1.0001 0 0 0 .03125.027344 1 1 0 0 0 .048828.035156 1.0001 1.0001 0 0 0 .023438.015625 1 1 0 0 0 .076172.044922 1.0001 1.0001 0 0 0 .0058593.003906 1 1 0 0 0 .013672.007813 1.0001 1.0001 0 0 0 .078125.035156 1 1 0 0 0 .074219.025391 1.0001 1.0001 0 0 0 .025391.009766 1 1 0 0 0 .039062.009765 1.0001 1.0001 0 0 0 .068359.013672 1.0001 1.0001 0 0 0 .097656.011719 1.0001 1.0001 0 0 0 .0078125 0 1 1 0 0 0 .0625.003906 1 1 0 0 0 .015625-.001953 1.0001 1.0001 0 0 0 .083984-.003906 1 1 0 0 0 .015625-.001953 1.0001 1.0001 0 0 0 .083984-.013672 1.0001 1.0001 0 0 0 .052734-.013672 1 1 0 0 0 .058594-.015625 1.0001 1.0001 0 0 0 .078125-.029297 1 1 0 0 0 .013672-.00586 1.0001 1.0001 0 0 0 .076172-.037109 1 1 0 0 0 .013672-.007812 1.0001 1.0001 0 0 0 .072266-.044922 1 1 0 0 0 .011719-.007813 1.0001 1.0001 0 0 0 .068359-.052734 1 1 0 0 0 .011719-.009766 1.0001 1.0001 0 0 0 .050781-.046875l.0097657-.011719 2.9902-2.9883a1 1 0 0 0 0-1.4141 1 1 0 0 0 -1.4141 0l-1.293 1.293v-9.5859a1 1 0 0 0 -1-1z" fill="#68b6ff" fill-rule="evenodd"/></svg>
diff --git a/editor/icons/SeparationRayShape3D.svg b/editor/icons/SeparationRayShape3D.svg
new file mode 100644
index 0000000000..44d32fe83b
--- /dev/null
+++ b/editor/icons/SeparationRayShape3D.svg
@@ -0,0 +1 @@
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><g fill-rule="evenodd"><path d="m8 1-6 5 4 2.666v4.334l2 2v-5-2z" fill="#a2d2ff"/><path d="m8 1v7 2l-2-1.334v1.334l2 1.5v3.5l2-2v-4.334l4-2.666z" fill="#2998ff"/></g></svg>
diff --git a/editor/import/resource_importer_scene.cpp b/editor/import/resource_importer_scene.cpp
index 2b03ad928c..c2244befa1 100644
--- a/editor/import/resource_importer_scene.cpp
+++ b/editor/import/resource_importer_scene.cpp
@@ -46,6 +46,7 @@
#include "scene/resources/box_shape_3d.h"
#include "scene/resources/packed_scene.h"
#include "scene/resources/resource_format_text.h"
+#include "scene/resources/separation_ray_shape_3d.h"
#include "scene/resources/sphere_shape_3d.h"
#include "scene/resources/surface_tool.h"
#include "scene/resources/world_margin_shape_3d.h"
@@ -379,6 +380,11 @@ Node *ResourceImporterScene::_pre_fix_node(Node *p_node, Node *p_root, Map<Ref<E
BoxShape3D *boxShape = memnew(BoxShape3D);
boxShape->set_size(Vector3(2, 2, 2));
colshape->set_shape(boxShape);
+ } else if (empty_draw_type == "SINGLE_ARROW") {
+ SeparationRayShape3D *rayShape = memnew(SeparationRayShape3D);
+ rayShape->set_length(1);
+ colshape->set_shape(rayShape);
+ Object::cast_to<Node3D>(sb)->rotate_x(Math_PI / 2);
} else if (empty_draw_type == "IMAGE") {
WorldMarginShape3D *world_margin_shape = memnew(WorldMarginShape3D);
colshape->set_shape(world_margin_shape);
diff --git a/editor/plugins/collision_shape_2d_editor_plugin.cpp b/editor/plugins/collision_shape_2d_editor_plugin.cpp
index c2684305ef..bfcc293625 100644
--- a/editor/plugins/collision_shape_2d_editor_plugin.cpp
+++ b/editor/plugins/collision_shape_2d_editor_plugin.cpp
@@ -38,6 +38,7 @@
#include "scene/resources/convex_polygon_shape_2d.h"
#include "scene/resources/rectangle_shape_2d.h"
#include "scene/resources/segment_shape_2d.h"
+#include "scene/resources/separation_ray_shape_2d.h"
#include "scene/resources/world_margin_shape_2d.h"
void CollisionShape2DEditor::_node_removed(Node *p_node) {
@@ -80,6 +81,15 @@ Variant CollisionShape2DEditor::get_handle_value(int idx) const {
} break;
+ case SEPARATION_RAY_SHAPE: {
+ Ref<SeparationRayShape2D> ray = node->get_shape();
+
+ if (idx == 0) {
+ return ray->get_length();
+ }
+
+ } break;
+
case RECTANGLE_SHAPE: {
Ref<RectangleShape2D> rect = node->get_shape();
@@ -152,6 +162,15 @@ void CollisionShape2DEditor::set_handle(int idx, Point2 &p_point) {
} break;
+ case SEPARATION_RAY_SHAPE: {
+ Ref<SeparationRayShape2D> ray = node->get_shape();
+
+ ray->set_length(Math::abs(p_point.y));
+
+ canvas_item_editor->update_viewport();
+
+ } break;
+
case RECTANGLE_SHAPE: {
if (idx < 8) {
Ref<RectangleShape2D> rect = node->get_shape();
@@ -253,6 +272,16 @@ void CollisionShape2DEditor::commit_handle(int idx, Variant &p_org) {
} break;
+ case SEPARATION_RAY_SHAPE: {
+ Ref<SeparationRayShape2D> ray = node->get_shape();
+
+ undo_redo->add_do_method(ray.ptr(), "set_length", ray->get_length());
+ undo_redo->add_do_method(canvas_item_editor, "update_viewport");
+ undo_redo->add_undo_method(ray.ptr(), "set_length", p_org);
+ undo_redo->add_undo_method(canvas_item_editor, "update_viewport");
+
+ } break;
+
case RECTANGLE_SHAPE: {
Ref<RectangleShape2D> rect = node->get_shape();
@@ -394,6 +423,8 @@ void CollisionShape2DEditor::_get_current_shape_type() {
shape_type = CONVEX_POLYGON_SHAPE;
} else if (Object::cast_to<WorldMarginShape2D>(*s)) {
shape_type = WORLD_MARGIN_SHAPE;
+ } else if (Object::cast_to<SeparationRayShape2D>(*s)) {
+ shape_type = SEPARATION_RAY_SHAPE;
} else if (Object::cast_to<RectangleShape2D>(*s)) {
shape_type = RECTANGLE_SHAPE;
} else if (Object::cast_to<SegmentShape2D>(*s)) {
@@ -471,6 +502,16 @@ void CollisionShape2DEditor::forward_canvas_draw_over_viewport(Control *p_overla
} break;
+ case SEPARATION_RAY_SHAPE: {
+ Ref<SeparationRayShape2D> shape = node->get_shape();
+
+ handles.resize(1);
+ handles.write[0] = Point2(0, shape->get_length());
+
+ p_overlay->draw_texture(h, gt.xform(handles[0]) - size);
+
+ } break;
+
case RECTANGLE_SHAPE: {
Ref<RectangleShape2D> shape = node->get_shape();
diff --git a/editor/plugins/collision_shape_2d_editor_plugin.h b/editor/plugins/collision_shape_2d_editor_plugin.h
index 056e1b5b7d..421e674df8 100644
--- a/editor/plugins/collision_shape_2d_editor_plugin.h
+++ b/editor/plugins/collision_shape_2d_editor_plugin.h
@@ -47,6 +47,7 @@ class CollisionShape2DEditor : public Control {
CONCAVE_POLYGON_SHAPE,
CONVEX_POLYGON_SHAPE,
WORLD_MARGIN_SHAPE,
+ SEPARATION_RAY_SHAPE,
RECTANGLE_SHAPE,
SEGMENT_SHAPE
};
diff --git a/editor/plugins/node_3d_editor_gizmos.cpp b/editor/plugins/node_3d_editor_gizmos.cpp
index 5d1b4d8ead..d04e88e915 100644
--- a/editor/plugins/node_3d_editor_gizmos.cpp
+++ b/editor/plugins/node_3d_editor_gizmos.cpp
@@ -66,6 +66,7 @@
#include "scene/resources/cylinder_shape_3d.h"
#include "scene/resources/height_map_shape_3d.h"
#include "scene/resources/primitive_meshes.h"
+#include "scene/resources/separation_ray_shape_3d.h"
#include "scene/resources/sphere_shape_3d.h"
#include "scene/resources/surface_tool.h"
#include "scene/resources/world_margin_shape_3d.h"
@@ -4067,6 +4068,10 @@ String CollisionShape3DGizmoPlugin::get_handle_name(const EditorNode3DGizmo *p_g
return p_id == 0 ? "Radius" : "Height";
}
+ if (Object::cast_to<SeparationRayShape3D>(*s)) {
+ return "Length";
+ }
+
return "";
}
@@ -4098,6 +4103,11 @@ Variant CollisionShape3DGizmoPlugin::get_handle_value(const EditorNode3DGizmo *p
return p_id == 0 ? cs2->get_radius() : cs2->get_height();
}
+ if (Object::cast_to<SeparationRayShape3D>(*s)) {
+ Ref<SeparationRayShape3D> cs2 = s;
+ return cs2->get_length();
+ }
+
return Variant();
}
@@ -4133,6 +4143,22 @@ void CollisionShape3DGizmoPlugin::set_handle(const EditorNode3DGizmo *p_gizmo, i
ss->set_radius(d);
}
+ if (Object::cast_to<SeparationRayShape3D>(*s)) {
+ Ref<SeparationRayShape3D> rs = s;
+ Vector3 ra, rb;
+ Geometry3D::get_closest_points_between_segments(Vector3(), Vector3(0, 0, 4096), sg[0], sg[1], ra, rb);
+ float d = ra.z;
+ if (Node3DEditor::get_singleton()->is_snap_enabled()) {
+ d = Math::snapped(d, Node3DEditor::get_singleton()->get_translate_snap());
+ }
+
+ if (d < 0.001) {
+ d = 0.001;
+ }
+
+ rs->set_length(d);
+ }
+
if (Object::cast_to<BoxShape3D>(*s)) {
Vector3 axis;
axis[p_id] = 1.0;
@@ -4287,6 +4313,20 @@ void CollisionShape3DGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo
ur->commit_action();
}
+
+ if (Object::cast_to<SeparationRayShape3D>(*s)) {
+ Ref<SeparationRayShape3D> ss = s;
+ if (p_cancel) {
+ ss->set_length(p_restore);
+ return;
+ }
+
+ UndoRedo *ur = Node3DEditor::get_singleton()->get_undo_redo();
+ ur->create_action(TTR("Change Separation Ray Shape Length"));
+ ur->add_do_method(ss.ptr(), "set_length", ss->get_length());
+ ur->add_undo_method(ss.ptr(), "set_length", p_restore);
+ ur->commit_action();
+ }
}
void CollisionShape3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
@@ -4557,6 +4597,19 @@ void CollisionShape3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
p_gizmo->add_collision_segments(cs2->get_debug_mesh_lines());
}
+ if (Object::cast_to<SeparationRayShape3D>(*s)) {
+ Ref<SeparationRayShape3D> rs = s;
+
+ Vector<Vector3> points;
+ points.push_back(Vector3());
+ points.push_back(Vector3(0, 0, rs->get_length()));
+ p_gizmo->add_lines(points, material);
+ p_gizmo->add_collision_segments(points);
+ Vector<Vector3> handles;
+ handles.push_back(Vector3(0, 0, rs->get_length()));
+ p_gizmo->add_handles(handles, handles_material);
+ }
+
if (Object::cast_to<HeightMapShape3D>(*s)) {
Ref<HeightMapShape3D> hms = s;
diff --git a/scene/2d/physics_body_2d.cpp b/scene/2d/physics_body_2d.cpp
index a9d4877cbb..8a9d28ae43 100644
--- a/scene/2d/physics_body_2d.cpp
+++ b/scene/2d/physics_body_2d.cpp
@@ -70,12 +70,12 @@ Ref<KinematicCollision2D> PhysicsBody2D::_move(const Vector2 &p_motion, bool p_t
return Ref<KinematicCollision2D>();
}
-bool PhysicsBody2D::move_and_collide(const Vector2 &p_motion, PhysicsServer2D::MotionResult &r_result, real_t p_margin, bool p_test_only, bool p_cancel_sliding, const Set<RID> &p_exclude) {
+bool PhysicsBody2D::move_and_collide(const Vector2 &p_motion, PhysicsServer2D::MotionResult &r_result, real_t p_margin, bool p_test_only, bool p_cancel_sliding, bool p_collide_separation_ray, const Set<RID> &p_exclude) {
if (is_only_update_transform_changes_enabled()) {
ERR_PRINT("Move functions do not work together with 'sync to physics' option. Please read the documentation.");
}
Transform2D gt = get_global_transform();
- bool colliding = PhysicsServer2D::get_singleton()->body_test_motion(get_rid(), gt, p_motion, p_margin, &r_result, p_exclude);
+ bool colliding = PhysicsServer2D::get_singleton()->body_test_motion(get_rid(), gt, p_motion, p_margin, &r_result, p_collide_separation_ray, p_exclude);
// Restore direction of motion to be along original motion,
// in order to avoid sliding due to recovery,
@@ -1075,7 +1075,7 @@ bool CharacterBody2D::move_and_slide() {
PhysicsServer2D::MotionResult floor_result;
Set<RID> exclude;
exclude.insert(platform_rid);
- if (move_and_collide(current_platform_velocity * delta, floor_result, margin, false, false, exclude)) {
+ if (move_and_collide(current_platform_velocity * delta, floor_result, margin, false, false, false, exclude)) {
motion_results.push_back(floor_result);
_set_collision_direction(floor_result);
}
@@ -1285,7 +1285,7 @@ void CharacterBody2D::_snap_on_floor(bool was_on_floor, bool vel_dir_facing_up)
Transform2D gt = get_global_transform();
PhysicsServer2D::MotionResult result;
- if (move_and_collide(up_direction * -floor_snap_length, result, margin, true, false)) {
+ if (move_and_collide(up_direction * -floor_snap_length, result, margin, true, false, true)) {
bool apply = true;
if (result.get_angle(up_direction) <= floor_max_angle + FLOOR_ANGLE_THRESHOLD) {
on_floor = true;
@@ -1319,7 +1319,7 @@ bool CharacterBody2D::_on_floor_if_snapped(bool was_on_floor, bool vel_dir_facin
}
PhysicsServer2D::MotionResult result;
- if (move_and_collide(up_direction * -floor_snap_length, result, margin, true, false)) {
+ if (move_and_collide(up_direction * -floor_snap_length, result, margin, true, false, true)) {
if (result.get_angle(up_direction) <= floor_max_angle + FLOOR_ANGLE_THRESHOLD) {
return true;
}
diff --git a/scene/2d/physics_body_2d.h b/scene/2d/physics_body_2d.h
index a999317953..885f0ace05 100644
--- a/scene/2d/physics_body_2d.h
+++ b/scene/2d/physics_body_2d.h
@@ -50,7 +50,7 @@ protected:
Ref<KinematicCollision2D> _move(const Vector2 &p_motion, bool p_test_only = false, real_t p_margin = 0.08);
public:
- bool move_and_collide(const Vector2 &p_motion, PhysicsServer2D::MotionResult &r_result, real_t p_margin, bool p_test_only = false, bool p_cancel_sliding = true, const Set<RID> &p_exclude = Set<RID>());
+ bool move_and_collide(const Vector2 &p_motion, PhysicsServer2D::MotionResult &r_result, real_t p_margin, bool p_test_only = false, bool p_cancel_sliding = true, bool p_collide_separation_ray = false, const Set<RID> &p_exclude = Set<RID>());
bool test_move(const Transform2D &p_from, const Vector2 &p_motion, const Ref<KinematicCollision2D> &r_collision = Ref<KinematicCollision2D>(), real_t p_margin = 0.08);
TypedArray<PhysicsBody2D> get_collision_exceptions();
diff --git a/scene/3d/physics_body_3d.cpp b/scene/3d/physics_body_3d.cpp
index d34331e1d8..a8a755972f 100644
--- a/scene/3d/physics_body_3d.cpp
+++ b/scene/3d/physics_body_3d.cpp
@@ -111,9 +111,9 @@ Ref<KinematicCollision3D> PhysicsBody3D::_move(const Vector3 &p_motion, bool p_t
return Ref<KinematicCollision3D>();
}
-bool PhysicsBody3D::move_and_collide(const Vector3 &p_motion, PhysicsServer3D::MotionResult &r_result, real_t p_margin, bool p_test_only, bool p_cancel_sliding, const Set<RID> &p_exclude) {
+bool PhysicsBody3D::move_and_collide(const Vector3 &p_motion, PhysicsServer3D::MotionResult &r_result, real_t p_margin, bool p_test_only, bool p_cancel_sliding, bool p_collide_separation_ray, const Set<RID> &p_exclude) {
Transform3D gt = get_global_transform();
- bool colliding = PhysicsServer3D::get_singleton()->body_test_motion(get_rid(), gt, p_motion, p_margin, &r_result, p_exclude);
+ bool colliding = PhysicsServer3D::get_singleton()->body_test_motion(get_rid(), gt, p_motion, p_margin, &r_result, p_collide_separation_ray, p_exclude);
// Restore direction of motion to be along original motion,
// in order to avoid sliding due to recovery,
@@ -1113,7 +1113,7 @@ bool CharacterBody3D::move_and_slide() {
PhysicsServer3D::MotionResult floor_result;
Set<RID> exclude;
exclude.insert(on_floor_body);
- if (move_and_collide(current_floor_velocity * delta, floor_result, margin, false, false, exclude)) {
+ if (move_and_collide(current_floor_velocity * delta, floor_result, margin, false, false, false, exclude)) {
motion_results.push_back(floor_result);
_set_collision_direction(floor_result);
}
@@ -1174,7 +1174,7 @@ bool CharacterBody3D::move_and_slide() {
// Apply snap.
Transform3D gt = get_global_transform();
PhysicsServer3D::MotionResult result;
- if (move_and_collide(snap, result, margin, true, false)) {
+ if (move_and_collide(snap, result, margin, true, false, true)) {
bool apply = true;
if (up_direction != Vector3()) {
if (result.get_angle(up_direction) <= floor_max_angle + FLOOR_ANGLE_THRESHOLD) {
diff --git a/scene/3d/physics_body_3d.h b/scene/3d/physics_body_3d.h
index 26b9a39047..17e688451d 100644
--- a/scene/3d/physics_body_3d.h
+++ b/scene/3d/physics_body_3d.h
@@ -53,7 +53,7 @@ protected:
Ref<KinematicCollision3D> _move(const Vector3 &p_motion, bool p_test_only = false, real_t p_margin = 0.001);
public:
- bool move_and_collide(const Vector3 &p_motion, PhysicsServer3D::MotionResult &r_result, real_t p_margin, bool p_test_only = false, bool p_cancel_sliding = true, const Set<RID> &p_exclude = Set<RID>());
+ bool move_and_collide(const Vector3 &p_motion, PhysicsServer3D::MotionResult &r_result, real_t p_margin, bool p_test_only = false, bool p_cancel_sliding = true, bool p_collide_separation_ray = false, const Set<RID> &p_exclude = Set<RID>());
bool test_move(const Transform3D &p_from, const Vector3 &p_motion, const Ref<KinematicCollision3D> &r_collision = Ref<KinematicCollision3D>(), real_t p_margin = 0.001);
void set_axis_lock(PhysicsServer3D::BodyAxis p_axis, bool p_lock);
diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp
index 2cea3c2337..25d3f6a3e7 100644
--- a/scene/register_scene_types.cpp
+++ b/scene/register_scene_types.cpp
@@ -160,6 +160,8 @@
#include "scene/resources/rectangle_shape_2d.h"
#include "scene/resources/resource_format_text.h"
#include "scene/resources/segment_shape_2d.h"
+#include "scene/resources/separation_ray_shape_2d.h"
+#include "scene/resources/separation_ray_shape_3d.h"
#include "scene/resources/skeleton_modification_2d.h"
#include "scene/resources/skeleton_modification_2d_ccdik.h"
#include "scene/resources/skeleton_modification_2d_fabrik.h"
@@ -752,6 +754,7 @@ void register_scene_types() {
OS::get_singleton()->yield(); //may take time to init
GDREGISTER_VIRTUAL_CLASS(Shape3D);
+ GDREGISTER_CLASS(SeparationRayShape3D);
GDREGISTER_CLASS(SphereShape3D);
GDREGISTER_CLASS(BoxShape3D);
GDREGISTER_CLASS(CapsuleShape3D);
@@ -840,6 +843,7 @@ void register_scene_types() {
GDREGISTER_VIRTUAL_CLASS(Shape2D);
GDREGISTER_CLASS(WorldMarginShape2D);
GDREGISTER_CLASS(SegmentShape2D);
+ GDREGISTER_CLASS(SeparationRayShape2D);
GDREGISTER_CLASS(CircleShape2D);
GDREGISTER_CLASS(RectangleShape2D);
GDREGISTER_CLASS(CapsuleShape2D);
@@ -960,6 +964,8 @@ void register_scene_types() {
ClassDB::add_compatibility_class("ProceduralSky", "Sky");
ClassDB::add_compatibility_class("ProximityGroup", "ProximityGroup3D");
ClassDB::add_compatibility_class("RayCast", "RayCast3D");
+ ClassDB::add_compatibility_class("RayShape", "SeparationRayShape3D");
+ ClassDB::add_compatibility_class("RayShape2D", "SeparationRayShape2D");
ClassDB::add_compatibility_class("RemoteTransform", "RemoteTransform3D");
ClassDB::add_compatibility_class("RigidBody", "RigidBody3D");
ClassDB::add_compatibility_class("Shape", "Shape3D");
diff --git a/scene/resources/separation_ray_shape_2d.cpp b/scene/resources/separation_ray_shape_2d.cpp
new file mode 100644
index 0000000000..0acd6d268d
--- /dev/null
+++ b/scene/resources/separation_ray_shape_2d.cpp
@@ -0,0 +1,119 @@
+/*************************************************************************/
+/* separation_ray_shape_2d.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 "separation_ray_shape_2d.h"
+
+#include "servers/physics_server_2d.h"
+#include "servers/rendering_server.h"
+
+void SeparationRayShape2D::_update_shape() {
+ Dictionary d;
+ d["length"] = length;
+ d["slide_on_slope"] = slide_on_slope;
+ PhysicsServer2D::get_singleton()->shape_set_data(get_rid(), d);
+ emit_changed();
+}
+
+void SeparationRayShape2D::draw(const RID &p_to_rid, const Color &p_color) {
+ const Vector2 target_position = Vector2(0, get_length());
+
+ const float max_arrow_size = 6;
+ const float line_width = 1.4;
+ bool no_line = target_position.length() < line_width;
+ float arrow_size = CLAMP(target_position.length() * 2 / 3, line_width, max_arrow_size);
+
+ if (no_line) {
+ arrow_size = target_position.length();
+ } else {
+ RS::get_singleton()->canvas_item_add_line(p_to_rid, Vector2(), target_position - target_position.normalized() * arrow_size, p_color, line_width);
+ }
+
+ Transform2D xf;
+ xf.rotate(target_position.angle());
+ xf.translate(Vector2(no_line ? 0 : target_position.length() - arrow_size, 0));
+
+ Vector<Vector2> pts;
+ pts.push_back(xf.xform(Vector2(arrow_size, 0)));
+ pts.push_back(xf.xform(Vector2(0, 0.5 * arrow_size)));
+ pts.push_back(xf.xform(Vector2(0, -0.5 * arrow_size)));
+
+ Vector<Color> cols;
+ for (int i = 0; i < 3; i++) {
+ cols.push_back(p_color);
+ }
+
+ RS::get_singleton()->canvas_item_add_primitive(p_to_rid, pts, cols, Vector<Point2>(), RID());
+}
+
+Rect2 SeparationRayShape2D::get_rect() const {
+ Rect2 rect;
+ rect.position = Vector2();
+ rect.expand_to(Vector2(0, length));
+ rect = rect.grow(Math_SQRT12 * 4);
+ return rect;
+}
+
+real_t SeparationRayShape2D::get_enclosing_radius() const {
+ return length;
+}
+
+void SeparationRayShape2D::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_length", "length"), &SeparationRayShape2D::set_length);
+ ClassDB::bind_method(D_METHOD("get_length"), &SeparationRayShape2D::get_length);
+
+ ClassDB::bind_method(D_METHOD("set_slide_on_slope", "active"), &SeparationRayShape2D::set_slide_on_slope);
+ ClassDB::bind_method(D_METHOD("get_slide_on_slope"), &SeparationRayShape2D::get_slide_on_slope);
+
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "length"), "set_length", "get_length");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "slide_on_slope"), "set_slide_on_slope", "get_slide_on_slope");
+}
+
+void SeparationRayShape2D::set_length(real_t p_length) {
+ length = p_length;
+ _update_shape();
+}
+
+real_t SeparationRayShape2D::get_length() const {
+ return length;
+}
+
+void SeparationRayShape2D::set_slide_on_slope(bool p_active) {
+ slide_on_slope = p_active;
+ _update_shape();
+}
+
+bool SeparationRayShape2D::get_slide_on_slope() const {
+ return slide_on_slope;
+}
+
+SeparationRayShape2D::SeparationRayShape2D() :
+ Shape2D(PhysicsServer2D::get_singleton()->separation_ray_shape_create()) {
+ _update_shape();
+}
diff --git a/scene/resources/separation_ray_shape_2d.h b/scene/resources/separation_ray_shape_2d.h
new file mode 100644
index 0000000000..5b74e6c727
--- /dev/null
+++ b/scene/resources/separation_ray_shape_2d.h
@@ -0,0 +1,61 @@
+/*************************************************************************/
+/* separation_ray_shape_2d.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 SEPARATION_RAY_SHAPE_2D_H
+#define SEPARATION_RAY_SHAPE_2D_H
+
+#include "scene/resources/shape_2d.h"
+
+class SeparationRayShape2D : public Shape2D {
+ GDCLASS(SeparationRayShape2D, Shape2D);
+
+ real_t length = 20.0;
+ bool slide_on_slope = false;
+
+ void _update_shape();
+
+protected:
+ static void _bind_methods();
+
+public:
+ void set_length(real_t p_length);
+ real_t get_length() const;
+
+ void set_slide_on_slope(bool p_active);
+ bool get_slide_on_slope() const;
+
+ virtual void draw(const RID &p_to_rid, const Color &p_color) override;
+ virtual Rect2 get_rect() const override;
+ virtual real_t get_enclosing_radius() const override;
+
+ SeparationRayShape2D();
+};
+
+#endif // SEPARATION_RAY_SHAPE_2D_H
diff --git a/scene/resources/separation_ray_shape_3d.cpp b/scene/resources/separation_ray_shape_3d.cpp
new file mode 100644
index 0000000000..376e04c844
--- /dev/null
+++ b/scene/resources/separation_ray_shape_3d.cpp
@@ -0,0 +1,91 @@
+/*************************************************************************/
+/* separation_ray_shape_3d.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 "separation_ray_shape_3d.h"
+
+#include "servers/physics_server_3d.h"
+
+Vector<Vector3> SeparationRayShape3D::get_debug_mesh_lines() const {
+ Vector<Vector3> points;
+ points.push_back(Vector3());
+ points.push_back(Vector3(0, 0, get_length()));
+
+ return points;
+}
+
+real_t SeparationRayShape3D::get_enclosing_radius() const {
+ return length;
+}
+
+void SeparationRayShape3D::_update_shape() {
+ Dictionary d;
+ d["length"] = length;
+ d["slide_on_slope"] = slide_on_slope;
+ PhysicsServer3D::get_singleton()->shape_set_data(get_shape(), d);
+ Shape3D::_update_shape();
+}
+
+void SeparationRayShape3D::set_length(float p_length) {
+ length = p_length;
+ _update_shape();
+ notify_change_to_owners();
+}
+
+float SeparationRayShape3D::get_length() const {
+ return length;
+}
+
+void SeparationRayShape3D::set_slide_on_slope(bool p_active) {
+ slide_on_slope = p_active;
+ _update_shape();
+ notify_change_to_owners();
+}
+
+bool SeparationRayShape3D::get_slide_on_slope() const {
+ return slide_on_slope;
+}
+
+void SeparationRayShape3D::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_length", "length"), &SeparationRayShape3D::set_length);
+ ClassDB::bind_method(D_METHOD("get_length"), &SeparationRayShape3D::get_length);
+
+ ClassDB::bind_method(D_METHOD("set_slide_on_slope", "active"), &SeparationRayShape3D::set_slide_on_slope);
+ ClassDB::bind_method(D_METHOD("get_slide_on_slope"), &SeparationRayShape3D::get_slide_on_slope);
+
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "length", PROPERTY_HINT_RANGE, "0,4096,0.001"), "set_length", "get_length");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "slide_on_slope"), "set_slide_on_slope", "get_slide_on_slope");
+}
+
+SeparationRayShape3D::SeparationRayShape3D() :
+ Shape3D(PhysicsServer3D::get_singleton()->shape_create(PhysicsServer3D::SHAPE_SEPARATION_RAY)) {
+ /* Code copied from setters to prevent the use of uninitialized variables */
+ _update_shape();
+ notify_change_to_owners();
+}
diff --git a/scene/resources/separation_ray_shape_3d.h b/scene/resources/separation_ray_shape_3d.h
new file mode 100644
index 0000000000..54058b6095
--- /dev/null
+++ b/scene/resources/separation_ray_shape_3d.h
@@ -0,0 +1,56 @@
+/*************************************************************************/
+/* separation_ray_shape_3d.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 SEPARATION_RAY_SHAPE_H
+#define SEPARATION_RAY_SHAPE_H
+#include "scene/resources/shape_3d.h"
+
+class SeparationRayShape3D : public Shape3D {
+ GDCLASS(SeparationRayShape3D, Shape3D);
+ float length = 1.0;
+ bool slide_on_slope = false;
+
+protected:
+ static void _bind_methods();
+ virtual void _update_shape() override;
+
+public:
+ void set_length(float p_length);
+ float get_length() const;
+
+ void set_slide_on_slope(bool p_active);
+ bool get_slide_on_slope() const;
+
+ virtual Vector<Vector3> get_debug_mesh_lines() const override;
+ virtual real_t get_enclosing_radius() const override;
+
+ SeparationRayShape3D();
+};
+#endif // SEPARATION_RAY_SHAPE_H
diff --git a/servers/physics_2d/body_pair_2d_sw.cpp b/servers/physics_2d/body_pair_2d_sw.cpp
index 91d747b492..8bcc4609f4 100644
--- a/servers/physics_2d/body_pair_2d_sw.cpp
+++ b/servers/physics_2d/body_pair_2d_sw.cpp
@@ -298,7 +298,7 @@ bool BodyPair2DSW::setup(real_t p_step) {
}
if (!prev_collided) {
- if (A->is_shape_set_as_one_way_collision(shape_A)) {
+ if (shape_B_ptr->allows_one_way_collision() && A->is_shape_set_as_one_way_collision(shape_A)) {
Vector2 direction = xform_A.get_axis(1).normalized();
bool valid = false;
for (int i = 0; i < contact_count; i++) {
@@ -319,7 +319,7 @@ bool BodyPair2DSW::setup(real_t p_step) {
}
}
- if (B->is_shape_set_as_one_way_collision(shape_B)) {
+ if (shape_A_ptr->allows_one_way_collision() && B->is_shape_set_as_one_way_collision(shape_B)) {
Vector2 direction = xform_B.get_axis(1).normalized();
bool valid = false;
for (int i = 0; i < contact_count; i++) {
diff --git a/servers/physics_2d/collision_solver_2d_sat.cpp b/servers/physics_2d/collision_solver_2d_sat.cpp
index 30a99d3d74..b1aee01bde 100644
--- a/servers/physics_2d/collision_solver_2d_sat.cpp
+++ b/servers/physics_2d/collision_solver_2d_sat.cpp
@@ -1115,11 +1115,13 @@ bool sat_2d_calculate_penetration(const Shape2DSW *p_shape_A, const Transform2D
PhysicsServer2D::ShapeType type_A = p_shape_A->get_type();
ERR_FAIL_COND_V(type_A == PhysicsServer2D::SHAPE_WORLD_MARGIN, false);
+ ERR_FAIL_COND_V(type_A == PhysicsServer2D::SHAPE_SEPARATION_RAY, false);
ERR_FAIL_COND_V(p_shape_A->is_concave(), false);
PhysicsServer2D::ShapeType type_B = p_shape_B->get_type();
ERR_FAIL_COND_V(type_B == PhysicsServer2D::SHAPE_WORLD_MARGIN, false);
+ ERR_FAIL_COND_V(type_B == PhysicsServer2D::SHAPE_SEPARATION_RAY, false);
ERR_FAIL_COND_V(p_shape_B->is_concave(), false);
static const CollisionFunc collision_table[5][5] = {
@@ -1382,23 +1384,23 @@ bool sat_2d_calculate_penetration(const Shape2DSW *p_shape_A, const Transform2D
if (p_margin_A || p_margin_B) {
if (*motion_A == Vector2() && *motion_B == Vector2()) {
- collision_func = collision_table_margin[type_A - 1][type_B - 1];
+ collision_func = collision_table_margin[type_A - 2][type_B - 2];
} else if (*motion_A != Vector2() && *motion_B == Vector2()) {
- collision_func = collision_table_castA_margin[type_A - 1][type_B - 1];
+ collision_func = collision_table_castA_margin[type_A - 2][type_B - 2];
} else if (*motion_A == Vector2() && *motion_B != Vector2()) {
- collision_func = collision_table_castB_margin[type_A - 1][type_B - 1];
+ collision_func = collision_table_castB_margin[type_A - 2][type_B - 2];
} else {
- collision_func = collision_table_castA_castB_margin[type_A - 1][type_B - 1];
+ collision_func = collision_table_castA_castB_margin[type_A - 2][type_B - 2];
}
} else {
if (*motion_A == Vector2() && *motion_B == Vector2()) {
- collision_func = collision_table[type_A - 1][type_B - 1];
+ collision_func = collision_table[type_A - 2][type_B - 2];
} else if (*motion_A != Vector2() && *motion_B == Vector2()) {
- collision_func = collision_table_castA[type_A - 1][type_B - 1];
+ collision_func = collision_table_castA[type_A - 2][type_B - 2];
} else if (*motion_A == Vector2() && *motion_B != Vector2()) {
- collision_func = collision_table_castB[type_A - 1][type_B - 1];
+ collision_func = collision_table_castB[type_A - 2][type_B - 2];
} else {
- collision_func = collision_table_castA_castB[type_A - 1][type_B - 1];
+ collision_func = collision_table_castA_castB[type_A - 2][type_B - 2];
}
}
diff --git a/servers/physics_2d/collision_solver_2d_sw.cpp b/servers/physics_2d/collision_solver_2d_sw.cpp
index 8f8a4a862c..f7a8593c50 100644
--- a/servers/physics_2d/collision_solver_2d_sw.cpp
+++ b/servers/physics_2d/collision_solver_2d_sw.cpp
@@ -73,6 +73,65 @@ bool CollisionSolver2DSW::solve_static_world_margin(const Shape2DSW *p_shape_A,
return found;
}
+bool CollisionSolver2DSW::solve_separation_ray(const Shape2DSW *p_shape_A, const Vector2 &p_motion_A, const Transform2D &p_transform_A, const Shape2DSW *p_shape_B, const Transform2D &p_transform_B, CallbackResult p_result_callback, void *p_userdata, bool p_swap_result, Vector2 *r_sep_axis, real_t p_margin) {
+ const SeparationRayShape2DSW *ray = static_cast<const SeparationRayShape2DSW *>(p_shape_A);
+ if (p_shape_B->get_type() == PhysicsServer2D::SHAPE_SEPARATION_RAY) {
+ return false;
+ }
+
+ Vector2 from = p_transform_A.get_origin();
+ Vector2 to = from + p_transform_A[1] * (ray->get_length() + p_margin);
+ if (p_motion_A != Vector2()) {
+ //not the best but should be enough
+ Vector2 normal = (to - from).normalized();
+ to += normal * MAX(0.0, normal.dot(p_motion_A));
+ }
+ Vector2 support_A = to;
+
+ Transform2D invb = p_transform_B.affine_inverse();
+ from = invb.xform(from);
+ to = invb.xform(to);
+
+ Vector2 p, n;
+ if (!p_shape_B->intersect_segment(from, to, p, n)) {
+ if (r_sep_axis) {
+ *r_sep_axis = p_transform_A[1].normalized();
+ }
+ return false;
+ }
+
+ // Discard contacts when the ray is fully contained inside the shape.
+ if (n == Vector2()) {
+ if (r_sep_axis) {
+ *r_sep_axis = p_transform_A[1].normalized();
+ }
+ return false;
+ }
+
+ // Discard contacts in the wrong direction.
+ if (n.dot(from - to) < CMP_EPSILON) {
+ if (r_sep_axis) {
+ *r_sep_axis = p_transform_A[1].normalized();
+ }
+ return false;
+ }
+
+ Vector2 support_B = p_transform_B.xform(p);
+ if (ray->get_slide_on_slope()) {
+ Vector2 global_n = invb.basis_xform_inv(n).normalized();
+ support_B = support_A + (support_B - support_A).length() * global_n;
+ }
+
+ if (p_result_callback) {
+ if (p_swap_result) {
+ p_result_callback(support_B, support_A, p_userdata);
+ } else {
+ p_result_callback(support_A, support_B, p_userdata);
+ }
+ }
+ return true;
+}
+
struct _ConcaveCollisionInfo2D {
const Transform2D *transform_A;
const Shape2DSW *shape_A;
@@ -106,7 +165,7 @@ void CollisionSolver2DSW::concave_callback(void *p_userdata, Shape2DSW *p_convex
cinfo.collisions++;
}
-bool CollisionSolver2DSW::solve_concave(const Shape2DSW *p_shape_A, const Transform2D &p_transform_A, const Vector2 &p_motion_A, const Shape2DSW *p_shape_B, const Transform2D &p_transform_B, const Vector2 &p_motion_B, CallbackResult p_result_callback, void *p_userdata, bool p_swap_result, Vector2 *sep_axis, real_t p_margin_A, real_t p_margin_B) {
+bool CollisionSolver2DSW::solve_concave(const Shape2DSW *p_shape_A, const Transform2D &p_transform_A, const Vector2 &p_motion_A, const Shape2DSW *p_shape_B, const Transform2D &p_transform_B, const Vector2 &p_motion_B, CallbackResult p_result_callback, void *p_userdata, bool p_swap_result, Vector2 *r_sep_axis, real_t p_margin_A, real_t p_margin_B) {
const ConcaveShape2DSW *concave_B = static_cast<const ConcaveShape2DSW *>(p_shape_B);
_ConcaveCollisionInfo2D cinfo;
@@ -119,7 +178,7 @@ bool CollisionSolver2DSW::solve_concave(const Shape2DSW *p_shape_A, const Transf
cinfo.swap_result = p_swap_result;
cinfo.collided = false;
cinfo.collisions = 0;
- cinfo.sep_axis = sep_axis;
+ cinfo.sep_axis = r_sep_axis;
cinfo.margin_A = p_margin_A;
cinfo.margin_B = p_margin_B;
@@ -150,7 +209,7 @@ bool CollisionSolver2DSW::solve_concave(const Shape2DSW *p_shape_A, const Transf
return cinfo.collided;
}
-bool CollisionSolver2DSW::solve(const Shape2DSW *p_shape_A, const Transform2D &p_transform_A, const Vector2 &p_motion_A, const Shape2DSW *p_shape_B, const Transform2D &p_transform_B, const Vector2 &p_motion_B, CallbackResult p_result_callback, void *p_userdata, Vector2 *sep_axis, real_t p_margin_A, real_t p_margin_B) {
+bool CollisionSolver2DSW::solve(const Shape2DSW *p_shape_A, const Transform2D &p_transform_A, const Vector2 &p_motion_A, const Shape2DSW *p_shape_B, const Transform2D &p_transform_B, const Vector2 &p_motion_B, CallbackResult p_result_callback, void *p_userdata, Vector2 *r_sep_axis, real_t p_margin_A, real_t p_margin_B) {
PhysicsServer2D::ShapeType type_A = p_shape_A->get_type();
PhysicsServer2D::ShapeType type_B = p_shape_B->get_type();
bool concave_A = p_shape_A->is_concave();
@@ -177,18 +236,29 @@ bool CollisionSolver2DSW::solve(const Shape2DSW *p_shape_A, const Transform2D &p
return solve_static_world_margin(p_shape_A, p_transform_A, p_shape_B, p_transform_B, p_result_callback, p_userdata, false);
}
+ } else if (type_A == PhysicsServer2D::SHAPE_SEPARATION_RAY) {
+ if (type_B == PhysicsServer2D::SHAPE_SEPARATION_RAY) {
+ return false; //no ray-ray
+ }
+
+ if (swap) {
+ return solve_separation_ray(p_shape_B, p_motion_B, p_transform_B, p_shape_A, p_transform_A, p_result_callback, p_userdata, true, r_sep_axis, p_margin_B);
+ } else {
+ return solve_separation_ray(p_shape_A, p_motion_A, p_transform_A, p_shape_B, p_transform_B, p_result_callback, p_userdata, false, r_sep_axis, p_margin_A);
+ }
+
} else if (concave_B) {
if (concave_A) {
return false;
}
if (!swap) {
- return solve_concave(p_shape_A, p_transform_A, p_motion_A, p_shape_B, p_transform_B, p_motion_B, p_result_callback, p_userdata, false, sep_axis, margin_A, margin_B);
+ return solve_concave(p_shape_A, p_transform_A, p_motion_A, p_shape_B, p_transform_B, p_motion_B, p_result_callback, p_userdata, false, r_sep_axis, margin_A, margin_B);
} else {
- return solve_concave(p_shape_B, p_transform_B, p_motion_B, p_shape_A, p_transform_A, p_motion_A, p_result_callback, p_userdata, true, sep_axis, margin_A, margin_B);
+ return solve_concave(p_shape_B, p_transform_B, p_motion_B, p_shape_A, p_transform_A, p_motion_A, p_result_callback, p_userdata, true, r_sep_axis, margin_A, margin_B);
}
} else {
- return collision_solver(p_shape_A, p_transform_A, p_motion_A, p_shape_B, p_transform_B, p_motion_B, p_result_callback, p_userdata, false, sep_axis, margin_A, margin_B);
+ return collision_solver(p_shape_A, p_transform_A, p_motion_A, p_shape_B, p_transform_B, p_motion_B, p_result_callback, p_userdata, false, r_sep_axis, margin_A, margin_B);
}
}
diff --git a/servers/physics_2d/collision_solver_2d_sw.h b/servers/physics_2d/collision_solver_2d_sw.h
index 17c0c2fe70..0678e3526c 100644
--- a/servers/physics_2d/collision_solver_2d_sw.h
+++ b/servers/physics_2d/collision_solver_2d_sw.h
@@ -40,11 +40,11 @@ public:
private:
static bool solve_static_world_margin(const Shape2DSW *p_shape_A, const Transform2D &p_transform_A, const Shape2DSW *p_shape_B, const Transform2D &p_transform_B, CallbackResult p_result_callback, void *p_userdata, bool p_swap_result);
static void concave_callback(void *p_userdata, Shape2DSW *p_convex);
- static bool solve_concave(const Shape2DSW *p_shape_A, const Transform2D &p_transform_A, const Vector2 &p_motion_A, const Shape2DSW *p_shape_B, const Transform2D &p_transform_B, const Vector2 &p_motion_B, CallbackResult p_result_callback, void *p_userdata, bool p_swap_result, Vector2 *sep_axis = nullptr, real_t p_margin_A = 0, real_t p_margin_B = 0);
- static bool solve_raycast(const Shape2DSW *p_shape_A, const Vector2 &p_motion_A, const Transform2D &p_transform_A, const Shape2DSW *p_shape_B, const Transform2D &p_transform_B, CallbackResult p_result_callback, void *p_userdata, bool p_swap_result, Vector2 *sep_axis = nullptr);
+ static bool solve_concave(const Shape2DSW *p_shape_A, const Transform2D &p_transform_A, const Vector2 &p_motion_A, const Shape2DSW *p_shape_B, const Transform2D &p_transform_B, const Vector2 &p_motion_B, CallbackResult p_result_callback, void *p_userdata, bool p_swap_result, Vector2 *r_sep_axis = nullptr, real_t p_margin_A = 0, real_t p_margin_B = 0);
+ static bool solve_separation_ray(const Shape2DSW *p_shape_A, const Vector2 &p_motion_A, const Transform2D &p_transform_A, const Shape2DSW *p_shape_B, const Transform2D &p_transform_B, CallbackResult p_result_callback, void *p_userdata, bool p_swap_result, Vector2 *r_sep_axis = nullptr, real_t p_margin = 0);
public:
- static bool solve(const Shape2DSW *p_shape_A, const Transform2D &p_transform_A, const Vector2 &p_motion_A, const Shape2DSW *p_shape_B, const Transform2D &p_transform_B, const Vector2 &p_motion_B, CallbackResult p_result_callback, void *p_userdata, Vector2 *sep_axis = nullptr, real_t p_margin_A = 0, real_t p_margin_B = 0);
+ static bool solve(const Shape2DSW *p_shape_A, const Transform2D &p_transform_A, const Vector2 &p_motion_A, const Shape2DSW *p_shape_B, const Transform2D &p_transform_B, const Vector2 &p_motion_B, CallbackResult p_result_callback, void *p_userdata, Vector2 *r_sep_axis = nullptr, real_t p_margin_A = 0, real_t p_margin_B = 0);
};
#endif // COLLISION_SOLVER_2D_SW_H
diff --git a/servers/physics_2d/physics_server_2d_sw.cpp b/servers/physics_2d/physics_server_2d_sw.cpp
index 88c097453e..813e5ddc17 100644
--- a/servers/physics_2d/physics_server_2d_sw.cpp
+++ b/servers/physics_2d/physics_server_2d_sw.cpp
@@ -45,6 +45,9 @@ RID PhysicsServer2DSW::_shape_create(ShapeType p_shape) {
case SHAPE_WORLD_MARGIN: {
shape = memnew(WorldMarginShape2DSW);
} break;
+ case SHAPE_SEPARATION_RAY: {
+ shape = memnew(SeparationRayShape2DSW);
+ } break;
case SHAPE_SEGMENT: {
shape = memnew(SegmentShape2DSW);
} break;
@@ -79,6 +82,10 @@ RID PhysicsServer2DSW::world_margin_shape_create() {
return _shape_create(SHAPE_WORLD_MARGIN);
}
+RID PhysicsServer2DSW::separation_ray_shape_create() {
+ return _shape_create(SHAPE_SEPARATION_RAY);
+}
+
RID PhysicsServer2DSW::segment_shape_create() {
return _shape_create(SHAPE_SEGMENT);
}
@@ -939,7 +946,7 @@ void PhysicsServer2DSW::body_set_pickable(RID p_body, bool p_pickable) {
body->set_pickable(p_pickable);
}
-bool PhysicsServer2DSW::body_test_motion(RID p_body, const Transform2D &p_from, const Vector2 &p_motion, real_t p_margin, MotionResult *r_result, const Set<RID> &p_exclude) {
+bool PhysicsServer2DSW::body_test_motion(RID p_body, const Transform2D &p_from, const Vector2 &p_motion, real_t p_margin, MotionResult *r_result, bool p_collide_separation_ray, const Set<RID> &p_exclude) {
Body2DSW *body = body_owner.getornull(p_body);
ERR_FAIL_COND_V(!body, false);
ERR_FAIL_COND_V(!body->get_space(), false);
@@ -947,7 +954,7 @@ bool PhysicsServer2DSW::body_test_motion(RID p_body, const Transform2D &p_from,
_update_shapes();
- return body->get_space()->test_body_motion(body, p_from, p_motion, p_margin, r_result, p_exclude);
+ return body->get_space()->test_body_motion(body, p_from, p_motion, p_margin, r_result, p_collide_separation_ray, p_exclude);
}
PhysicsDirectBodyState2D *PhysicsServer2DSW::body_get_direct_state(RID p_body) {
diff --git a/servers/physics_2d/physics_server_2d_sw.h b/servers/physics_2d/physics_server_2d_sw.h
index 3610f43f93..c5b6d550d7 100644
--- a/servers/physics_2d/physics_server_2d_sw.h
+++ b/servers/physics_2d/physics_server_2d_sw.h
@@ -88,6 +88,7 @@ public:
};
virtual RID world_margin_shape_create() override;
+ virtual RID separation_ray_shape_create() override;
virtual RID segment_shape_create() override;
virtual RID circle_shape_create() override;
virtual RID rectangle_shape_create() override;
@@ -246,7 +247,7 @@ public:
virtual void body_set_pickable(RID p_body, bool p_pickable) override;
- virtual bool body_test_motion(RID p_body, const Transform2D &p_from, const Vector2 &p_motion, real_t p_margin = 0.08, MotionResult *r_result = nullptr, const Set<RID> &p_exclude = Set<RID>()) override;
+ virtual bool body_test_motion(RID p_body, const Transform2D &p_from, const Vector2 &p_motion, real_t p_margin = 0.08, MotionResult *r_result = nullptr, bool p_collide_separation_ray = false, const Set<RID> &p_exclude = Set<RID>()) override;
// this function only works on physics process, errors and returns null otherwise
virtual PhysicsDirectBodyState2D *body_get_direct_state(RID p_body) override;
diff --git a/servers/physics_2d/physics_server_2d_wrap_mt.h b/servers/physics_2d/physics_server_2d_wrap_mt.h
index b93178919d..05d4ac64b0 100644
--- a/servers/physics_2d/physics_server_2d_wrap_mt.h
+++ b/servers/physics_2d/physics_server_2d_wrap_mt.h
@@ -80,6 +80,7 @@ public:
//FUNC1RID(shape,ShapeType); todo fix
FUNCRID(world_margin_shape)
+ FUNCRID(separation_ray_shape)
FUNCRID(segment_shape)
FUNCRID(circle_shape)
FUNCRID(rectangle_shape)
@@ -252,9 +253,9 @@ public:
FUNC2(body_set_pickable, RID, bool);
- bool body_test_motion(RID p_body, const Transform2D &p_from, const Vector2 &p_motion, real_t p_margin = 0.08, MotionResult *r_result = nullptr, const Set<RID> &p_exclude = Set<RID>()) override {
+ bool body_test_motion(RID p_body, const Transform2D &p_from, const Vector2 &p_motion, real_t p_margin = 0.08, MotionResult *r_result = nullptr, bool p_collide_separation_ray = false, const Set<RID> &p_exclude = Set<RID>()) override {
ERR_FAIL_COND_V(main_thread != Thread::get_caller_id(), false);
- return physics_2d_server->body_test_motion(p_body, p_from, p_motion, p_margin, r_result, p_exclude);
+ return physics_2d_server->body_test_motion(p_body, p_from, p_motion, p_margin, r_result, p_collide_separation_ray, p_exclude);
}
// this function only works on physics process, errors and returns null otherwise
diff --git a/servers/physics_2d/shape_2d_sw.cpp b/servers/physics_2d/shape_2d_sw.cpp
index b3e4ca84c3..757b2e0583 100644
--- a/servers/physics_2d/shape_2d_sw.cpp
+++ b/servers/physics_2d/shape_2d_sw.cpp
@@ -143,6 +143,46 @@ Variant WorldMarginShape2DSW::get_data() const {
/*********************************************************/
/*********************************************************/
+void SeparationRayShape2DSW::get_supports(const Vector2 &p_normal, Vector2 *r_supports, int &r_amount) const {
+ r_amount = 1;
+
+ if (p_normal.y > 0) {
+ *r_supports = Vector2(0, length);
+ } else {
+ *r_supports = Vector2();
+ }
+}
+
+bool SeparationRayShape2DSW::contains_point(const Vector2 &p_point) const {
+ return false;
+}
+
+bool SeparationRayShape2DSW::intersect_segment(const Vector2 &p_begin, const Vector2 &p_end, Vector2 &r_point, Vector2 &r_normal) const {
+ return false; //rays can't be intersected
+}
+
+real_t SeparationRayShape2DSW::get_moment_of_inertia(real_t p_mass, const Size2 &p_scale) const {
+ return 0; //rays are mass-less
+}
+
+void SeparationRayShape2DSW::set_data(const Variant &p_data) {
+ Dictionary d = p_data;
+ length = d["length"];
+ slide_on_slope = d["slide_on_slope"];
+ configure(Rect2(0, 0, 0.001, length));
+}
+
+Variant SeparationRayShape2DSW::get_data() const {
+ Dictionary d;
+ d["length"] = length;
+ d["slide_on_slope"] = slide_on_slope;
+ return d;
+}
+
+/*********************************************************/
+/*********************************************************/
+/*********************************************************/
+
void SegmentShape2DSW::get_supports(const Vector2 &p_normal, Vector2 *r_supports, int &r_amount) const {
if (Math::abs(p_normal.dot(n)) > _SEGMENT_IS_VALID_SUPPORT_THRESHOLD) {
r_supports[0] = a;
@@ -530,14 +570,7 @@ bool ConvexPolygonShape2DSW::intersect_segment(const Vector2 &p_begin, const Vec
}
}
- if (inters) {
- if (n.dot(r_normal) > 0) {
- r_normal = -r_normal;
- }
- }
-
- //return get_aabb().intersects_segment(p_begin,p_end,&r_point,&r_normal);
- return inters; //todo
+ return inters;
}
real_t ConvexPolygonShape2DSW::get_moment_of_inertia(real_t p_mass, const Size2 &p_scale) const {
diff --git a/servers/physics_2d/shape_2d_sw.h b/servers/physics_2d/shape_2d_sw.h
index 45d3379dfa..bc321adb42 100644
--- a/servers/physics_2d/shape_2d_sw.h
+++ b/servers/physics_2d/shape_2d_sw.h
@@ -64,6 +64,8 @@ public:
_FORCE_INLINE_ Rect2 get_aabb() const { return aabb; }
_FORCE_INLINE_ bool is_configured() const { return configured; }
+ virtual bool allows_one_way_collision() const { return true; }
+
virtual bool is_concave() const { return false; }
virtual bool contains_point(const Vector2 &p_point) const = 0;
@@ -177,6 +179,43 @@ public:
}
};
+class SeparationRayShape2DSW : public Shape2DSW {
+ real_t length;
+ bool slide_on_slope;
+
+public:
+ _FORCE_INLINE_ real_t get_length() const { return length; }
+ _FORCE_INLINE_ bool get_slide_on_slope() const { return slide_on_slope; }
+
+ virtual PhysicsServer2D::ShapeType get_type() const override { return PhysicsServer2D::SHAPE_SEPARATION_RAY; }
+
+ virtual bool allows_one_way_collision() const override { return false; }
+
+ virtual void project_rangev(const Vector2 &p_normal, const Transform2D &p_transform, real_t &r_min, real_t &r_max) const override { project_range(p_normal, p_transform, r_min, r_max); }
+ virtual void get_supports(const Vector2 &p_normal, Vector2 *r_supports, int &r_amount) const override;
+
+ virtual bool contains_point(const Vector2 &p_point) const override;
+ virtual bool intersect_segment(const Vector2 &p_begin, const Vector2 &p_end, Vector2 &r_point, Vector2 &r_normal) const override;
+ virtual real_t get_moment_of_inertia(real_t p_mass, const Size2 &p_scale) const override;
+
+ virtual void set_data(const Variant &p_data) override;
+ virtual Variant get_data() const override;
+
+ _FORCE_INLINE_ void project_range(const Vector2 &p_normal, const Transform2D &p_transform, real_t &r_min, real_t &r_max) const {
+ //real large
+ r_max = p_normal.dot(p_transform.get_origin());
+ r_min = p_normal.dot(p_transform.xform(Vector2(0, length)));
+ if (r_max < r_min) {
+ SWAP(r_max, r_min);
+ }
+ }
+
+ DEFAULT_PROJECT_RANGE_CAST
+
+ _FORCE_INLINE_ SeparationRayShape2DSW() {}
+ _FORCE_INLINE_ SeparationRayShape2DSW(real_t p_length) { length = p_length; }
+};
+
class SegmentShape2DSW : public Shape2DSW {
Vector2 a;
Vector2 b;
diff --git a/servers/physics_2d/space_2d_sw.cpp b/servers/physics_2d/space_2d_sw.cpp
index f04f3ab583..c0493749e0 100644
--- a/servers/physics_2d/space_2d_sw.cpp
+++ b/servers/physics_2d/space_2d_sw.cpp
@@ -528,7 +528,7 @@ int Space2DSW::_cull_aabb_for_body(Body2DSW *p_body, const Rect2 &p_aabb) {
return amount;
}
-bool Space2DSW::test_body_motion(Body2DSW *p_body, const Transform2D &p_from, const Vector2 &p_motion, real_t p_margin, PhysicsServer2D::MotionResult *r_result, const Set<RID> &p_exclude) {
+bool Space2DSW::test_body_motion(Body2DSW *p_body, const Transform2D &p_from, const Vector2 &p_motion, real_t p_margin, PhysicsServer2D::MotionResult *r_result, bool p_collide_separation_ray, const Set<RID> &p_exclude) {
//give me back regular physics engine logic
//this is madness
//and most people using this function will think
@@ -621,7 +621,7 @@ bool Space2DSW::test_body_motion(Body2DSW *p_body, const Transform2D &p_from, co
Transform2D col_obj_shape_xform = col_obj->get_transform() * col_obj->get_shape_transform(shape_idx);
- if (col_obj->is_shape_set_as_one_way_collision(shape_idx)) {
+ if (body_shape->allows_one_way_collision() && col_obj->is_shape_set_as_one_way_collision(shape_idx)) {
cbk.valid_dir = col_obj_shape_xform.get_axis(1).normalized();
real_t owc_margin = col_obj->get_shape_one_way_collision_margin(shape_idx);
@@ -726,6 +726,16 @@ bool Space2DSW::test_body_motion(Body2DSW *p_body, const Transform2D &p_from, co
}
Shape2DSW *body_shape = p_body->get_shape(body_shape_idx);
+
+ // Colliding separation rays allows to properly snap to the ground,
+ // otherwise it's not needed in regular motion.
+ if (!p_collide_separation_ray && (body_shape->get_type() == PhysicsServer2D::SHAPE_SEPARATION_RAY)) {
+ // When slide on slope is on, separation ray shape acts like a regular shape.
+ if (!static_cast<SeparationRayShape2DSW *>(body_shape)->get_slide_on_slope()) {
+ continue;
+ }
+ }
+
Transform2D body_shape_xform = body_transform * p_body->get_shape_transform(body_shape_idx);
bool stuck = false;
@@ -762,7 +772,7 @@ bool Space2DSW::test_body_motion(Body2DSW *p_body, const Transform2D &p_from, co
//test initial overlap
if (CollisionSolver2DSW::solve(body_shape, body_shape_xform, Vector2(), against_shape, col_obj_shape_xform, Vector2(), nullptr, nullptr, nullptr, 0)) {
- if (col_obj->is_shape_set_as_one_way_collision(col_shape_idx)) {
+ if (body_shape->allows_one_way_collision() && col_obj->is_shape_set_as_one_way_collision(col_shape_idx)) {
Vector2 direction = col_obj_shape_xform.get_axis(1).normalized();
if (motion_normal.dot(direction) < 0) {
continue;
@@ -806,7 +816,7 @@ bool Space2DSW::test_body_motion(Body2DSW *p_body, const Transform2D &p_from, co
}
}
- if (col_obj->is_shape_set_as_one_way_collision(col_shape_idx)) {
+ if (body_shape->allows_one_way_collision() && col_obj->is_shape_set_as_one_way_collision(col_shape_idx)) {
Vector2 cd[2];
PhysicsServer2DSW::CollCbkData cbk;
cbk.max = 1;
@@ -904,7 +914,7 @@ bool Space2DSW::test_body_motion(Body2DSW *p_body, const Transform2D &p_from, co
Transform2D col_obj_shape_xform = col_obj->get_transform() * col_obj->get_shape_transform(shape_idx);
- if (col_obj->is_shape_set_as_one_way_collision(shape_idx)) {
+ if (body_shape->allows_one_way_collision() && col_obj->is_shape_set_as_one_way_collision(shape_idx)) {
rcd.valid_dir = col_obj_shape_xform.get_axis(1).normalized();
real_t owc_margin = col_obj->get_shape_one_way_collision_margin(shape_idx);
diff --git a/servers/physics_2d/space_2d_sw.h b/servers/physics_2d/space_2d_sw.h
index 3be36852b0..392df9b025 100644
--- a/servers/physics_2d/space_2d_sw.h
+++ b/servers/physics_2d/space_2d_sw.h
@@ -183,7 +183,7 @@ public:
int get_collision_pairs() const { return collision_pairs; }
- bool test_body_motion(Body2DSW *p_body, const Transform2D &p_from, const Vector2 &p_motion, real_t p_margin, PhysicsServer2D::MotionResult *r_result, const Set<RID> &p_exclude = Set<RID>());
+ bool test_body_motion(Body2DSW *p_body, const Transform2D &p_from, const Vector2 &p_motion, real_t p_margin, PhysicsServer2D::MotionResult *r_result, bool p_collide_separation_ray = false, const Set<RID> &p_exclude = Set<RID>());
void set_debug_contacts(int p_amount) { contact_debug.resize(p_amount); }
_FORCE_INLINE_ bool is_debugging_contacts() const { return !contact_debug.is_empty(); }
diff --git a/servers/physics_3d/collision_solver_3d_sat.cpp b/servers/physics_3d/collision_solver_3d_sat.cpp
index 6e6a2cb9e7..de81348b4e 100644
--- a/servers/physics_3d/collision_solver_3d_sat.cpp
+++ b/servers/physics_3d/collision_solver_3d_sat.cpp
@@ -2273,11 +2273,13 @@ bool sat_calculate_penetration(const Shape3DSW *p_shape_A, const Transform3D &p_
PhysicsServer3D::ShapeType type_A = p_shape_A->get_type();
ERR_FAIL_COND_V(type_A == PhysicsServer3D::SHAPE_PLANE, false);
+ ERR_FAIL_COND_V(type_A == PhysicsServer3D::SHAPE_SEPARATION_RAY, false);
ERR_FAIL_COND_V(p_shape_A->is_concave(), false);
PhysicsServer3D::ShapeType type_B = p_shape_B->get_type();
ERR_FAIL_COND_V(type_B == PhysicsServer3D::SHAPE_PLANE, false);
+ ERR_FAIL_COND_V(type_B == PhysicsServer3D::SHAPE_SEPARATION_RAY, false);
ERR_FAIL_COND_V(p_shape_B->is_concave(), false);
static const CollisionFunc collision_table[6][6] = {
@@ -2382,10 +2384,10 @@ bool sat_calculate_penetration(const Shape3DSW *p_shape_A, const Transform3D &p_
CollisionFunc collision_func;
if (margin_A != 0.0 || margin_B != 0.0) {
- collision_func = collision_table_margin[type_A - 1][type_B - 1];
+ collision_func = collision_table_margin[type_A - 2][type_B - 2];
} else {
- collision_func = collision_table[type_A - 1][type_B - 1];
+ collision_func = collision_table[type_A - 2][type_B - 2];
}
ERR_FAIL_COND_V(!collision_func, false);
diff --git a/servers/physics_3d/collision_solver_3d_sw.cpp b/servers/physics_3d/collision_solver_3d_sw.cpp
index dcecac1c73..e7b79e8450 100644
--- a/servers/physics_3d/collision_solver_3d_sw.cpp
+++ b/servers/physics_3d/collision_solver_3d_sw.cpp
@@ -89,6 +89,49 @@ bool CollisionSolver3DSW::solve_static_plane(const Shape3DSW *p_shape_A, const T
return found;
}
+bool CollisionSolver3DSW::solve_separation_ray(const Shape3DSW *p_shape_A, const Transform3D &p_transform_A, const Shape3DSW *p_shape_B, const Transform3D &p_transform_B, CallbackResult p_result_callback, void *p_userdata, bool p_swap_result, real_t p_margin) {
+ const SeparationRayShape3DSW *ray = static_cast<const SeparationRayShape3DSW *>(p_shape_A);
+
+ Vector3 from = p_transform_A.origin;
+ Vector3 to = from + p_transform_A.basis.get_axis(2) * (ray->get_length() + p_margin);
+ Vector3 support_A = to;
+
+ Transform3D ai = p_transform_B.affine_inverse();
+
+ from = ai.xform(from);
+ to = ai.xform(to);
+
+ Vector3 p, n;
+ if (!p_shape_B->intersect_segment(from, to, p, n)) {
+ return false;
+ }
+
+ // Discard contacts when the ray is fully contained inside the shape.
+ if (n == Vector3()) {
+ return false;
+ }
+
+ // Discard contacts in the wrong direction.
+ if (n.dot(from - to) < CMP_EPSILON) {
+ return false;
+ }
+
+ Vector3 support_B = p_transform_B.xform(p);
+ if (ray->get_slide_on_slope()) {
+ Vector3 global_n = ai.basis.xform_inv(n).normalized();
+ support_B = support_A + (support_B - support_A).length() * global_n;
+ }
+
+ if (p_result_callback) {
+ if (p_swap_result) {
+ p_result_callback(support_B, 0, support_A, 0, p_userdata);
+ } else {
+ p_result_callback(support_A, 0, support_B, 0, p_userdata);
+ }
+ }
+ return true;
+}
+
struct _SoftBodyContactCollisionInfo {
int node_index = 0;
CollisionSolver3DSW::CallbackResult result_callback = nullptr;
@@ -318,6 +361,9 @@ bool CollisionSolver3DSW::solve_static(const Shape3DSW *p_shape_A, const Transfo
if (type_B == PhysicsServer3D::SHAPE_PLANE) {
return false;
}
+ if (type_B == PhysicsServer3D::SHAPE_SEPARATION_RAY) {
+ return false;
+ }
if (type_B == PhysicsServer3D::SHAPE_SOFT_BODY) {
return false;
}
@@ -328,6 +374,17 @@ bool CollisionSolver3DSW::solve_static(const Shape3DSW *p_shape_A, const Transfo
return solve_static_plane(p_shape_A, p_transform_A, p_shape_B, p_transform_B, p_result_callback, p_userdata, false);
}
+ } else if (type_A == PhysicsServer3D::SHAPE_SEPARATION_RAY) {
+ if (type_B == PhysicsServer3D::SHAPE_SEPARATION_RAY) {
+ return false;
+ }
+
+ if (swap) {
+ return solve_separation_ray(p_shape_B, p_transform_B, p_shape_A, p_transform_A, p_result_callback, p_userdata, true, p_margin_B);
+ } else {
+ return solve_separation_ray(p_shape_A, p_transform_A, p_shape_B, p_transform_B, p_result_callback, p_userdata, false, p_margin_A);
+ }
+
} else if (type_B == PhysicsServer3D::SHAPE_SOFT_BODY) {
if (type_A == PhysicsServer3D::SHAPE_SOFT_BODY) {
// Soft Body / Soft Body not supported.
diff --git a/servers/physics_3d/collision_solver_3d_sw.h b/servers/physics_3d/collision_solver_3d_sw.h
index a5dd7d48eb..c1a69e708f 100644
--- a/servers/physics_3d/collision_solver_3d_sw.h
+++ b/servers/physics_3d/collision_solver_3d_sw.h
@@ -43,7 +43,7 @@ private:
static void soft_body_concave_callback(void *p_userdata, Shape3DSW *p_convex);
static void concave_callback(void *p_userdata, Shape3DSW *p_convex);
static bool solve_static_plane(const Shape3DSW *p_shape_A, const Transform3D &p_transform_A, const Shape3DSW *p_shape_B, const Transform3D &p_transform_B, CallbackResult p_result_callback, void *p_userdata, bool p_swap_result);
- static bool solve_ray(const Shape3DSW *p_shape_A, const Transform3D &p_transform_A, const Shape3DSW *p_shape_B, const Transform3D &p_transform_B, CallbackResult p_result_callback, void *p_userdata, bool p_swap_result);
+ static bool solve_separation_ray(const Shape3DSW *p_shape_A, const Transform3D &p_transform_A, const Shape3DSW *p_shape_B, const Transform3D &p_transform_B, CallbackResult p_result_callback, void *p_userdata, bool p_swap_result, real_t p_margin = 0);
static bool solve_soft_body(const Shape3DSW *p_shape_A, const Transform3D &p_transform_A, const Shape3DSW *p_shape_B, const Transform3D &p_transform_B, CallbackResult p_result_callback, void *p_userdata, bool p_swap_result);
static bool solve_concave(const Shape3DSW *p_shape_A, const Transform3D &p_transform_A, const Shape3DSW *p_shape_B, const Transform3D &p_transform_B, CallbackResult p_result_callback, void *p_userdata, bool p_swap_result, real_t p_margin_A = 0, real_t p_margin_B = 0);
static void concave_distance_callback(void *p_userdata, Shape3DSW *p_convex);
diff --git a/servers/physics_3d/physics_server_3d_sw.cpp b/servers/physics_3d/physics_server_3d_sw.cpp
index fbb374bd74..fbc6f7eee8 100644
--- a/servers/physics_3d/physics_server_3d_sw.cpp
+++ b/servers/physics_3d/physics_server_3d_sw.cpp
@@ -48,6 +48,12 @@ RID PhysicsServer3DSW::plane_shape_create() {
shape->set_self(rid);
return rid;
}
+RID PhysicsServer3DSW::separation_ray_shape_create() {
+ Shape3DSW *shape = memnew(SeparationRayShape3DSW);
+ RID rid = shape_owner.make_rid(shape);
+ shape->set_self(rid);
+ return rid;
+}
RID PhysicsServer3DSW::sphere_shape_create() {
Shape3DSW *shape = memnew(SphereShape3DSW);
RID rid = shape_owner.make_rid(shape);
@@ -848,7 +854,7 @@ void PhysicsServer3DSW::body_set_ray_pickable(RID p_body, bool p_enable) {
body->set_ray_pickable(p_enable);
}
-bool PhysicsServer3DSW::body_test_motion(RID p_body, const Transform3D &p_from, const Vector3 &p_motion, real_t p_margin, MotionResult *r_result, const Set<RID> &p_exclude) {
+bool PhysicsServer3DSW::body_test_motion(RID p_body, const Transform3D &p_from, const Vector3 &p_motion, real_t p_margin, MotionResult *r_result, bool p_collide_separation_ray, const Set<RID> &p_exclude) {
Body3DSW *body = body_owner.getornull(p_body);
ERR_FAIL_COND_V(!body, false);
ERR_FAIL_COND_V(!body->get_space(), false);
@@ -856,7 +862,7 @@ bool PhysicsServer3DSW::body_test_motion(RID p_body, const Transform3D &p_from,
_update_shapes();
- return body->get_space()->test_body_motion(body, p_from, p_motion, p_margin, r_result, p_exclude);
+ return body->get_space()->test_body_motion(body, p_from, p_motion, p_margin, r_result, p_collide_separation_ray, p_exclude);
}
PhysicsDirectBodyState3D *PhysicsServer3DSW::body_get_direct_state(RID p_body) {
diff --git a/servers/physics_3d/physics_server_3d_sw.h b/servers/physics_3d/physics_server_3d_sw.h
index 6e59a77e89..a18593c90c 100644
--- a/servers/physics_3d/physics_server_3d_sw.h
+++ b/servers/physics_3d/physics_server_3d_sw.h
@@ -83,6 +83,7 @@ public:
static void _shape_col_cbk(const Vector3 &p_point_A, int p_index_A, const Vector3 &p_point_B, int p_index_B, void *p_userdata);
virtual RID plane_shape_create() override;
+ virtual RID separation_ray_shape_create() override;
virtual RID sphere_shape_create() override;
virtual RID box_shape_create() override;
virtual RID capsule_shape_create() override;
@@ -241,7 +242,7 @@ public:
virtual void body_set_ray_pickable(RID p_body, bool p_enable) override;
- virtual bool body_test_motion(RID p_body, const Transform3D &p_from, const Vector3 &p_motion, real_t p_margin = 0.001, MotionResult *r_result = nullptr, const Set<RID> &p_exclude = Set<RID>()) override;
+ virtual bool body_test_motion(RID p_body, const Transform3D &p_from, const Vector3 &p_motion, real_t p_margin = 0.001, MotionResult *r_result = nullptr, bool p_collide_separation_ray = false, const Set<RID> &p_exclude = Set<RID>()) override;
// this function only works on physics process, errors and returns null otherwise
virtual PhysicsDirectBodyState3D *body_get_direct_state(RID p_body) override;
diff --git a/servers/physics_3d/physics_server_3d_wrap_mt.h b/servers/physics_3d/physics_server_3d_wrap_mt.h
index 75174628bf..674c52d4a3 100644
--- a/servers/physics_3d/physics_server_3d_wrap_mt.h
+++ b/servers/physics_3d/physics_server_3d_wrap_mt.h
@@ -79,6 +79,7 @@ public:
//FUNC1RID(shape,ShapeType); todo fix
FUNCRID(plane_shape)
+ FUNCRID(separation_ray_shape)
FUNCRID(sphere_shape)
FUNCRID(box_shape)
FUNCRID(capsule_shape)
@@ -249,9 +250,9 @@ public:
FUNC2(body_set_ray_pickable, RID, bool);
- bool body_test_motion(RID p_body, const Transform3D &p_from, const Vector3 &p_motion, real_t p_margin = 0.001, MotionResult *r_result = nullptr, const Set<RID> &p_exclude = Set<RID>()) override {
+ bool body_test_motion(RID p_body, const Transform3D &p_from, const Vector3 &p_motion, real_t p_margin = 0.001, MotionResult *r_result = nullptr, bool p_collide_separation_ray = false, const Set<RID> &p_exclude = Set<RID>()) override {
ERR_FAIL_COND_V(main_thread != Thread::get_caller_id(), false);
- return physics_3d_server->body_test_motion(p_body, p_from, p_motion, p_margin, r_result, p_exclude);
+ return physics_3d_server->body_test_motion(p_body, p_from, p_motion, p_margin, r_result, p_collide_separation_ray, p_exclude);
}
// this function only works on physics process, errors and returns null otherwise
diff --git a/servers/physics_3d/shape_3d_sw.cpp b/servers/physics_3d/shape_3d_sw.cpp
index 664308ed7b..0794e7888e 100644
--- a/servers/physics_3d/shape_3d_sw.cpp
+++ b/servers/physics_3d/shape_3d_sw.cpp
@@ -164,6 +164,91 @@ Variant PlaneShape3DSW::get_data() const {
PlaneShape3DSW::PlaneShape3DSW() {
}
+//
+
+real_t SeparationRayShape3DSW::get_length() const {
+ return length;
+}
+
+bool SeparationRayShape3DSW::get_slide_on_slope() const {
+ return slide_on_slope;
+}
+
+void SeparationRayShape3DSW::project_range(const Vector3 &p_normal, const Transform3D &p_transform, real_t &r_min, real_t &r_max) const {
+ // don't think this will be even used
+ r_min = 0;
+ r_max = 1;
+}
+
+Vector3 SeparationRayShape3DSW::get_support(const Vector3 &p_normal) const {
+ if (p_normal.z > 0) {
+ return Vector3(0, 0, length);
+ } else {
+ return Vector3(0, 0, 0);
+ }
+}
+
+void SeparationRayShape3DSW::get_supports(const Vector3 &p_normal, int p_max, Vector3 *r_supports, int &r_amount, FeatureType &r_type) const {
+ if (Math::abs(p_normal.z) < _EDGE_IS_VALID_SUPPORT_THRESHOLD) {
+ r_amount = 2;
+ r_type = FEATURE_EDGE;
+ r_supports[0] = Vector3(0, 0, 0);
+ r_supports[1] = Vector3(0, 0, length);
+ } else if (p_normal.z > 0) {
+ r_amount = 1;
+ r_type = FEATURE_POINT;
+ *r_supports = Vector3(0, 0, length);
+ } else {
+ r_amount = 1;
+ r_type = FEATURE_POINT;
+ *r_supports = Vector3(0, 0, 0);
+ }
+}
+
+bool SeparationRayShape3DSW::intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal) const {
+ return false; //simply not possible
+}
+
+bool SeparationRayShape3DSW::intersect_point(const Vector3 &p_point) const {
+ return false; //simply not possible
+}
+
+Vector3 SeparationRayShape3DSW::get_closest_point_to(const Vector3 &p_point) const {
+ Vector3 s[2] = {
+ Vector3(0, 0, 0),
+ Vector3(0, 0, length)
+ };
+
+ return Geometry3D::get_closest_point_to_segment(p_point, s);
+}
+
+Vector3 SeparationRayShape3DSW::get_moment_of_inertia(real_t p_mass) const {
+ return Vector3();
+}
+
+void SeparationRayShape3DSW::_setup(real_t p_length, bool p_slide_on_slope) {
+ length = p_length;
+ slide_on_slope = p_slide_on_slope;
+ configure(AABB(Vector3(0, 0, 0), Vector3(0.1, 0.1, length)));
+}
+
+void SeparationRayShape3DSW::set_data(const Variant &p_data) {
+ Dictionary d = p_data;
+ _setup(d["length"], d["slide_on_slope"]);
+}
+
+Variant SeparationRayShape3DSW::get_data() const {
+ Dictionary d;
+ d["length"] = length;
+ d["slide_on_slope"] = slide_on_slope;
+ return d;
+}
+
+SeparationRayShape3DSW::SeparationRayShape3DSW() {
+ length = 1;
+ slide_on_slope = false;
+}
+
/********** SPHERE *************/
real_t SphereShape3DSW::get_radius() const {
diff --git a/servers/physics_3d/shape_3d_sw.h b/servers/physics_3d/shape_3d_sw.h
index ca58900111..dc35c3b172 100644
--- a/servers/physics_3d/shape_3d_sw.h
+++ b/servers/physics_3d/shape_3d_sw.h
@@ -117,23 +117,51 @@ class PlaneShape3DSW : public Shape3DSW {
public:
Plane get_plane() const;
- virtual real_t get_area() const { return INFINITY; }
- virtual PhysicsServer3D::ShapeType get_type() const { return PhysicsServer3D::SHAPE_PLANE; }
- virtual void project_range(const Vector3 &p_normal, const Transform3D &p_transform, real_t &r_min, real_t &r_max) const;
- virtual Vector3 get_support(const Vector3 &p_normal) const;
- virtual void get_supports(const Vector3 &p_normal, int p_max, Vector3 *r_supports, int &r_amount, FeatureType &r_type) const { r_amount = 0; }
+ virtual real_t get_area() const override { return INFINITY; }
+ virtual PhysicsServer3D::ShapeType get_type() const override { return PhysicsServer3D::SHAPE_PLANE; }
+ virtual void project_range(const Vector3 &p_normal, const Transform3D &p_transform, real_t &r_min, real_t &r_max) const override;
+ virtual Vector3 get_support(const Vector3 &p_normal) const override;
+ virtual void get_supports(const Vector3 &p_normal, int p_max, Vector3 *r_supports, int &r_amount, FeatureType &r_type) const override { r_amount = 0; }
- virtual bool intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal) const;
- virtual bool intersect_point(const Vector3 &p_point) const;
- virtual Vector3 get_closest_point_to(const Vector3 &p_point) const;
- virtual Vector3 get_moment_of_inertia(real_t p_mass) const;
+ virtual bool intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal) const override;
+ virtual bool intersect_point(const Vector3 &p_point) const override;
+ virtual Vector3 get_closest_point_to(const Vector3 &p_point) const override;
+ virtual Vector3 get_moment_of_inertia(real_t p_mass) const override;
- virtual void set_data(const Variant &p_data);
- virtual Variant get_data() const;
+ virtual void set_data(const Variant &p_data) override;
+ virtual Variant get_data() const override;
PlaneShape3DSW();
};
+class SeparationRayShape3DSW : public Shape3DSW {
+ real_t length;
+ bool slide_on_slope;
+
+ void _setup(real_t p_length, bool p_slide_on_slope);
+
+public:
+ real_t get_length() const;
+ bool get_slide_on_slope() const;
+
+ virtual real_t get_area() const override { return 0.0; }
+ virtual PhysicsServer3D::ShapeType get_type() const override { return PhysicsServer3D::SHAPE_SEPARATION_RAY; }
+ virtual void project_range(const Vector3 &p_normal, const Transform3D &p_transform, real_t &r_min, real_t &r_max) const override;
+ virtual Vector3 get_support(const Vector3 &p_normal) const override;
+ virtual void get_supports(const Vector3 &p_normal, int p_max, Vector3 *r_supports, int &r_amount, FeatureType &r_type) const override;
+
+ virtual bool intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal) const override;
+ virtual bool intersect_point(const Vector3 &p_point) const override;
+ virtual Vector3 get_closest_point_to(const Vector3 &p_point) const override;
+
+ virtual Vector3 get_moment_of_inertia(real_t p_mass) const override;
+
+ virtual void set_data(const Variant &p_data) override;
+ virtual Variant get_data() const override;
+
+ SeparationRayShape3DSW();
+};
+
class SphereShape3DSW : public Shape3DSW {
real_t radius;
diff --git a/servers/physics_3d/space_3d_sw.cpp b/servers/physics_3d/space_3d_sw.cpp
index f9e55ad525..37dee436df 100644
--- a/servers/physics_3d/space_3d_sw.cpp
+++ b/servers/physics_3d/space_3d_sw.cpp
@@ -569,7 +569,7 @@ int Space3DSW::_cull_aabb_for_body(Body3DSW *p_body, const AABB &p_aabb) {
return amount;
}
-bool Space3DSW::test_body_motion(Body3DSW *p_body, const Transform3D &p_from, const Vector3 &p_motion, real_t p_margin, PhysicsServer3D::MotionResult *r_result, const Set<RID> &p_exclude) {
+bool Space3DSW::test_body_motion(Body3DSW *p_body, const Transform3D &p_from, const Vector3 &p_motion, real_t p_margin, PhysicsServer3D::MotionResult *r_result, bool p_collide_separation_ray, const Set<RID> &p_exclude) {
//give me back regular physics engine logic
//this is madness
//and most people using this function will think
@@ -714,9 +714,19 @@ bool Space3DSW::test_body_motion(Body3DSW *p_body, const Transform3D &p_from, co
continue;
}
- Transform3D body_shape_xform = body_transform * p_body->get_shape_transform(j);
Shape3DSW *body_shape = p_body->get_shape(j);
+ // Colliding separation rays allows to properly snap to the ground,
+ // otherwise it's not needed in regular motion.
+ if (!p_collide_separation_ray && (body_shape->get_type() == PhysicsServer3D::SHAPE_SEPARATION_RAY)) {
+ // When slide on slope is on, separation ray shape acts like a regular shape.
+ if (!static_cast<SeparationRayShape3DSW *>(body_shape)->get_slide_on_slope()) {
+ continue;
+ }
+ }
+
+ Transform3D body_shape_xform = body_transform * p_body->get_shape_transform(j);
+
Transform3D body_shape_xform_inv = body_shape_xform.affine_inverse();
MotionShape3DSW mshape;
mshape.shape = body_shape;
diff --git a/servers/physics_3d/space_3d_sw.h b/servers/physics_3d/space_3d_sw.h
index 9b5b4de069..1c3d1cf9f6 100644
--- a/servers/physics_3d/space_3d_sw.h
+++ b/servers/physics_3d/space_3d_sw.h
@@ -203,7 +203,7 @@ public:
void set_elapsed_time(ElapsedTime p_time, uint64_t p_msec) { elapsed_time[p_time] = p_msec; }
uint64_t get_elapsed_time(ElapsedTime p_time) const { return elapsed_time[p_time]; }
- bool test_body_motion(Body3DSW *p_body, const Transform3D &p_from, const Vector3 &p_motion, real_t p_margin, PhysicsServer3D::MotionResult *r_result, const Set<RID> &p_exclude = Set<RID>());
+ bool test_body_motion(Body3DSW *p_body, const Transform3D &p_from, const Vector3 &p_motion, real_t p_margin, PhysicsServer3D::MotionResult *r_result, bool p_collide_separation_ray = false, const Set<RID> &p_exclude = Set<RID>());
Space3DSW();
~Space3DSW();
diff --git a/servers/physics_server_2d.cpp b/servers/physics_server_2d.cpp
index c96f85446c..4660b83f4d 100644
--- a/servers/physics_server_2d.cpp
+++ b/servers/physics_server_2d.cpp
@@ -500,7 +500,7 @@ void PhysicsTestMotionResult2D::_bind_methods() {
///////////////////////////////////////
-bool PhysicsServer2D::_body_test_motion(RID p_body, const Transform2D &p_from, const Vector2 &p_motion, real_t p_margin, const Ref<PhysicsTestMotionResult2D> &p_result, const Vector<RID> &p_exclude) {
+bool PhysicsServer2D::_body_test_motion(RID p_body, const Transform2D &p_from, const Vector2 &p_motion, real_t p_margin, const Ref<PhysicsTestMotionResult2D> &p_result, bool p_collide_separation_ray, const Vector<RID> &p_exclude) {
MotionResult *r = nullptr;
if (p_result.is_valid()) {
r = p_result->get_result_ptr();
@@ -509,11 +509,12 @@ bool PhysicsServer2D::_body_test_motion(RID p_body, const Transform2D &p_from, c
for (int i = 0; i < p_exclude.size(); i++) {
exclude.insert(p_exclude[i]);
}
- return body_test_motion(p_body, p_from, p_motion, p_margin, r, exclude);
+ return body_test_motion(p_body, p_from, p_motion, p_margin, r, p_collide_separation_ray, exclude);
}
void PhysicsServer2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("world_margin_shape_create"), &PhysicsServer2D::world_margin_shape_create);
+ ClassDB::bind_method(D_METHOD("separation_ray_shape_create"), &PhysicsServer2D::separation_ray_shape_create);
ClassDB::bind_method(D_METHOD("segment_shape_create"), &PhysicsServer2D::segment_shape_create);
ClassDB::bind_method(D_METHOD("circle_shape_create"), &PhysicsServer2D::circle_shape_create);
ClassDB::bind_method(D_METHOD("rectangle_shape_create"), &PhysicsServer2D::rectangle_shape_create);
@@ -635,7 +636,7 @@ void PhysicsServer2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("body_set_force_integration_callback", "body", "callable", "userdata"), &PhysicsServer2D::body_set_force_integration_callback, DEFVAL(Variant()));
- ClassDB::bind_method(D_METHOD("body_test_motion", "body", "from", "motion", "margin", "result", "exclude"), &PhysicsServer2D::_body_test_motion, DEFVAL(0.08), DEFVAL(Variant()), DEFVAL(Array()));
+ ClassDB::bind_method(D_METHOD("body_test_motion", "body", "from", "motion", "margin", "result", "collide_separation_ray", "exclude"), &PhysicsServer2D::_body_test_motion, DEFVAL(0.08), DEFVAL(Variant()), DEFVAL(false), DEFVAL(Array()));
ClassDB::bind_method(D_METHOD("body_get_direct_state", "body"), &PhysicsServer2D::body_get_direct_state);
@@ -675,6 +676,7 @@ void PhysicsServer2D::_bind_methods() {
BIND_ENUM_CONSTANT(SPACE_PARAM_TEST_MOTION_MIN_CONTACT_DEPTH);
BIND_ENUM_CONSTANT(SHAPE_WORLD_MARGIN);
+ BIND_ENUM_CONSTANT(SHAPE_SEPARATION_RAY);
BIND_ENUM_CONSTANT(SHAPE_SEGMENT);
BIND_ENUM_CONSTANT(SHAPE_CIRCLE);
BIND_ENUM_CONSTANT(SHAPE_RECTANGLE);
diff --git a/servers/physics_server_2d.h b/servers/physics_server_2d.h
index 4c559dd7bd..9afac0cbd1 100644
--- a/servers/physics_server_2d.h
+++ b/servers/physics_server_2d.h
@@ -210,7 +210,7 @@ class PhysicsServer2D : public Object {
static PhysicsServer2D *singleton;
- virtual bool _body_test_motion(RID p_body, const Transform2D &p_from, const Vector2 &p_motion, real_t p_margin = 0.08, const Ref<PhysicsTestMotionResult2D> &p_result = Ref<PhysicsTestMotionResult2D>(), const Vector<RID> &p_exclude = Vector<RID>());
+ virtual bool _body_test_motion(RID p_body, const Transform2D &p_from, const Vector2 &p_motion, real_t p_margin = 0.08, const Ref<PhysicsTestMotionResult2D> &p_result = Ref<PhysicsTestMotionResult2D>(), bool p_collide_separation_ray = false, const Vector<RID> &p_exclude = Vector<RID>());
protected:
static void _bind_methods();
@@ -220,6 +220,7 @@ public:
enum ShapeType {
SHAPE_WORLD_MARGIN, ///< plane:"plane"
+ SHAPE_SEPARATION_RAY, ///< float:"length"
SHAPE_SEGMENT, ///< float:"length"
SHAPE_CIRCLE, ///< float:"radius"
SHAPE_RECTANGLE, ///< vec3:"extents"
@@ -230,6 +231,7 @@ public:
};
virtual RID world_margin_shape_create() = 0;
+ virtual RID separation_ray_shape_create() = 0;
virtual RID segment_shape_create() = 0;
virtual RID circle_shape_create() = 0;
virtual RID rectangle_shape_create() = 0;
@@ -485,7 +487,7 @@ public:
}
};
- virtual bool body_test_motion(RID p_body, const Transform2D &p_from, const Vector2 &p_motion, real_t p_margin = 0.08, MotionResult *r_result = nullptr, const Set<RID> &p_exclude = Set<RID>()) = 0;
+ virtual bool body_test_motion(RID p_body, const Transform2D &p_from, const Vector2 &p_motion, real_t p_margin = 0.08, MotionResult *r_result = nullptr, bool p_collide_separation_ray = false, const Set<RID> &p_exclude = Set<RID>()) = 0;
struct SeparationResult {
real_t collision_depth;
diff --git a/servers/physics_server_3d.cpp b/servers/physics_server_3d.cpp
index 1f46a96b27..6cde4977f4 100644
--- a/servers/physics_server_3d.cpp
+++ b/servers/physics_server_3d.cpp
@@ -447,7 +447,7 @@ void PhysicsTestMotionResult3D::_bind_methods() {
///////////////////////////////////////
-bool PhysicsServer3D::_body_test_motion(RID p_body, const Transform3D &p_from, const Vector3 &p_motion, real_t p_margin, const Ref<PhysicsTestMotionResult3D> &p_result, const Vector<RID> &p_exclude) {
+bool PhysicsServer3D::_body_test_motion(RID p_body, const Transform3D &p_from, const Vector3 &p_motion, real_t p_margin, const Ref<PhysicsTestMotionResult3D> &p_result, bool p_collide_separation_ray, const Vector<RID> &p_exclude) {
MotionResult *r = nullptr;
if (p_result.is_valid()) {
r = p_result->get_result_ptr();
@@ -456,13 +456,15 @@ bool PhysicsServer3D::_body_test_motion(RID p_body, const Transform3D &p_from, c
for (int i = 0; i < p_exclude.size(); i++) {
exclude.insert(p_exclude[i]);
}
- return body_test_motion(p_body, p_from, p_motion, p_margin, r, exclude);
+ return body_test_motion(p_body, p_from, p_motion, p_margin, r, p_collide_separation_ray, exclude);
}
RID PhysicsServer3D::shape_create(ShapeType p_shape) {
switch (p_shape) {
case SHAPE_PLANE:
return plane_shape_create();
+ case SHAPE_SEPARATION_RAY:
+ return separation_ray_shape_create();
case SHAPE_SPHERE:
return sphere_shape_create();
case SHAPE_BOX:
@@ -488,6 +490,7 @@ void PhysicsServer3D::_bind_methods() {
#ifndef _3D_DISABLED
ClassDB::bind_method(D_METHOD("plane_shape_create"), &PhysicsServer3D::plane_shape_create);
+ ClassDB::bind_method(D_METHOD("separation_ray_shape_create"), &PhysicsServer3D::separation_ray_shape_create);
ClassDB::bind_method(D_METHOD("sphere_shape_create"), &PhysicsServer3D::sphere_shape_create);
ClassDB::bind_method(D_METHOD("box_shape_create"), &PhysicsServer3D::box_shape_create);
ClassDB::bind_method(D_METHOD("capsule_shape_create"), &PhysicsServer3D::capsule_shape_create);
@@ -609,7 +612,7 @@ void PhysicsServer3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("body_set_ray_pickable", "body", "enable"), &PhysicsServer3D::body_set_ray_pickable);
- ClassDB::bind_method(D_METHOD("body_test_motion", "body", "from", "motion", "margin", "result", "exclude"), &PhysicsServer3D::_body_test_motion, DEFVAL(0.001), DEFVAL(Variant()), DEFVAL(Array()));
+ ClassDB::bind_method(D_METHOD("body_test_motion", "body", "from", "motion", "margin", "result", "collide_separation_ray", "exclude"), &PhysicsServer3D::_body_test_motion, DEFVAL(0.001), DEFVAL(Variant()), DEFVAL(false), DEFVAL(Array()));
ClassDB::bind_method(D_METHOD("body_get_direct_state", "body"), &PhysicsServer3D::body_get_direct_state);
@@ -748,6 +751,7 @@ void PhysicsServer3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_process_info", "process_info"), &PhysicsServer3D::get_process_info);
BIND_ENUM_CONSTANT(SHAPE_PLANE);
+ BIND_ENUM_CONSTANT(SHAPE_SEPARATION_RAY);
BIND_ENUM_CONSTANT(SHAPE_SPHERE);
BIND_ENUM_CONSTANT(SHAPE_BOX);
BIND_ENUM_CONSTANT(SHAPE_CAPSULE);
diff --git a/servers/physics_server_3d.h b/servers/physics_server_3d.h
index 5201e76498..8f83e360f2 100644
--- a/servers/physics_server_3d.h
+++ b/servers/physics_server_3d.h
@@ -212,7 +212,7 @@ class PhysicsServer3D : public Object {
static PhysicsServer3D *singleton;
- virtual bool _body_test_motion(RID p_body, const Transform3D &p_from, const Vector3 &p_motion, real_t p_margin = 0.001, const Ref<PhysicsTestMotionResult3D> &p_result = Ref<PhysicsTestMotionResult3D>(), const Vector<RID> &p_exclude = Vector<RID>());
+ virtual bool _body_test_motion(RID p_body, const Transform3D &p_from, const Vector3 &p_motion, real_t p_margin = 0.001, const Ref<PhysicsTestMotionResult3D> &p_result = Ref<PhysicsTestMotionResult3D>(), bool p_collide_separation_ray = false, const Vector<RID> &p_exclude = Vector<RID>());
protected:
static void _bind_methods();
@@ -222,6 +222,7 @@ public:
enum ShapeType {
SHAPE_PLANE, ///< plane:"plane"
+ SHAPE_SEPARATION_RAY, ///< float:"length"
SHAPE_SPHERE, ///< float:"radius"
SHAPE_BOX, ///< vec3:"extents"
SHAPE_CAPSULE, ///< dict( float:"radius", float:"height"):capsule
@@ -236,6 +237,7 @@ public:
RID shape_create(ShapeType p_shape);
virtual RID plane_shape_create() = 0;
+ virtual RID separation_ray_shape_create() = 0;
virtual RID sphere_shape_create() = 0;
virtual RID box_shape_create() = 0;
virtual RID capsule_shape_create() = 0;
@@ -497,7 +499,7 @@ public:
}
};
- virtual bool body_test_motion(RID p_body, const Transform3D &p_from, const Vector3 &p_motion, real_t p_margin = 0.001, MotionResult *r_result = nullptr, const Set<RID> &p_exclude = Set<RID>()) = 0;
+ virtual bool body_test_motion(RID p_body, const Transform3D &p_from, const Vector3 &p_motion, real_t p_margin = 0.001, MotionResult *r_result = nullptr, bool p_collide_separation_ray = false, const Set<RID> &p_exclude = Set<RID>()) = 0;
/* SOFT BODY */