Просмотр исходного кода

Restore RayShape as a regular shape type

Partial revert from previously removing ray shapes completely, added
back as a shape type but without the specific character controller code.
PouleyKetchoupp 4 лет назад
Родитель
Сommit
45c7af9862
33 измененных файлов с 842 добавлено и 26 удалено
  1. 14 7
      doc/classes/PhysicsServer2D.xml
  2. 17 9
      doc/classes/PhysicsServer3D.xml
  3. 23 0
      doc/classes/RayShape2D.xml
  4. 23 0
      doc/classes/RayShape3D.xml
  5. 1 0
      editor/icons/RayShape2D.svg
  6. 1 0
      editor/icons/RayShape3D.svg
  7. 6 0
      editor/import/resource_importer_scene.cpp
  8. 41 0
      editor/plugins/collision_shape_2d_editor_plugin.cpp
  9. 1 0
      editor/plugins/collision_shape_2d_editor_plugin.h
  10. 53 0
      editor/plugins/node_3d_editor_gizmos.cpp
  11. 5 0
      scene/register_scene_types.cpp
  12. 119 0
      scene/resources/ray_shape_2d.cpp
  13. 61 0
      scene/resources/ray_shape_2d.h
  14. 91 0
      scene/resources/ray_shape_3d.cpp
  15. 56 0
      scene/resources/ray_shape_3d.h
  16. 10 8
      servers/physics_2d/collision_solver_2d_sat.cpp
  17. 54 0
      servers/physics_2d/collision_solver_2d_sw.cpp
  18. 7 0
      servers/physics_2d/physics_server_2d_sw.cpp
  19. 1 0
      servers/physics_2d/physics_server_2d_sw.h
  20. 1 0
      servers/physics_2d/physics_server_2d_wrap_mt.h
  21. 40 0
      servers/physics_2d/shape_2d_sw.cpp
  22. 35 0
      servers/physics_2d/shape_2d_sw.h
  23. 4 2
      servers/physics_3d/collision_solver_3d_sat.cpp
  24. 47 0
      servers/physics_3d/collision_solver_3d_sw.cpp
  25. 6 0
      servers/physics_3d/physics_server_3d_sw.cpp
  26. 1 0
      servers/physics_3d/physics_server_3d_sw.h
  27. 1 0
      servers/physics_3d/physics_server_3d_wrap_mt.h
  28. 85 0
      servers/physics_3d/shape_3d_sw.cpp
  29. 28 0
      servers/physics_3d/shape_3d_sw.h
  30. 2 0
      servers/physics_server_2d.cpp
  31. 2 0
      servers/physics_server_2d.h
  32. 4 0
      servers/physics_server_3d.cpp
  33. 2 0
      servers/physics_server_3d.h

+ 14 - 7
doc/classes/PhysicsServer2D.xml

@@ -716,6 +716,11 @@
 				Sets a joint parameter. See [enum JointParam] for a list of available parameters.
 				Sets a joint parameter. See [enum JointParam] for a list of available parameters.
 			</description>
 			</description>
 		</method>
 		</method>
+		<method name="ray_shape_create">
+			<return type="RID" />
+			<description>
+			</description>
+		</method>
 		<method name="rectangle_shape_create">
 		<method name="rectangle_shape_create">
 			<return type="RID" />
 			<return type="RID" />
 			<description>
 			<description>
@@ -840,25 +845,27 @@
 		<constant name="SHAPE_WORLD_MARGIN" value="0" enum="ShapeType">
 		<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.
 			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>
-		<constant name="SHAPE_SEGMENT" value="1" enum="ShapeType">
+		<constant name="SHAPE_RAY" value="1" enum="ShapeType">
+		</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.
 			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>
-		<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.
 			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>
-		<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.
 			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>
-		<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.
 			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>
-		<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.
 			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>
-		<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.
 			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>
-		<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.
 			This constant is used internally by the engine. Any attempt to create this kind of shape results in an error.
 		</constant>
 		</constant>
 		<constant name="AREA_PARAM_GRAVITY" value="0" enum="AreaParameter">
 		<constant name="AREA_PARAM_GRAVITY" value="0" enum="AreaParameter">

+ 17 - 9
doc/classes/PhysicsServer3D.xml

@@ -849,6 +849,11 @@
 			<description>
 			<description>
 			</description>
 			</description>
 		</method>
 		</method>
+		<method name="ray_shape_create">
+			<return type="RID" />
+			<description>
+			</description>
+		</method>
 		<method name="set_active">
 		<method name="set_active">
 			<return type="void" />
 			<return type="void" />
 			<argument index="0" name="active" type="bool" />
 			<argument index="0" name="active" type="bool" />
@@ -1171,31 +1176,34 @@
 		<constant name="SHAPE_PLANE" value="0" enum="ShapeType">
 		<constant name="SHAPE_PLANE" value="0" enum="ShapeType">
 			The [Shape3D] is a [WorldMarginShape3D].
 			The [Shape3D] is a [WorldMarginShape3D].
 		</constant>
 		</constant>
-		<constant name="SHAPE_SPHERE" value="1" enum="ShapeType">
+		<constant name="SHAPE_RAY" value="1" enum="ShapeType">
+			The [Shape3D] is a [RayShape3D].
+		</constant>
+		<constant name="SHAPE_SPHERE" value="2" enum="ShapeType">
 			The [Shape3D] is a [SphereShape3D].
 			The [Shape3D] is a [SphereShape3D].
 		</constant>
 		</constant>
-		<constant name="SHAPE_BOX" value="2" enum="ShapeType">
+		<constant name="SHAPE_BOX" value="3" enum="ShapeType">
 			The [Shape3D] is a [BoxShape3D].
 			The [Shape3D] is a [BoxShape3D].
 		</constant>
 		</constant>
-		<constant name="SHAPE_CAPSULE" value="3" enum="ShapeType">
+		<constant name="SHAPE_CAPSULE" value="4" enum="ShapeType">
 			The [Shape3D] is a [CapsuleShape3D].
 			The [Shape3D] is a [CapsuleShape3D].
 		</constant>
 		</constant>
-		<constant name="SHAPE_CYLINDER" value="4" enum="ShapeType">
+		<constant name="SHAPE_CYLINDER" value="5" enum="ShapeType">
 			The [Shape3D] is a [CylinderShape3D].
 			The [Shape3D] is a [CylinderShape3D].
 		</constant>
 		</constant>
-		<constant name="SHAPE_CONVEX_POLYGON" value="5" enum="ShapeType">
+		<constant name="SHAPE_CONVEX_POLYGON" value="6" enum="ShapeType">
 			The [Shape3D] is a [ConvexPolygonShape3D].
 			The [Shape3D] is a [ConvexPolygonShape3D].
 		</constant>
 		</constant>
-		<constant name="SHAPE_CONCAVE_POLYGON" value="6" enum="ShapeType">
+		<constant name="SHAPE_CONCAVE_POLYGON" value="7" enum="ShapeType">
 			The [Shape3D] is a [ConcavePolygonShape3D].
 			The [Shape3D] is a [ConcavePolygonShape3D].
 		</constant>
 		</constant>
-		<constant name="SHAPE_HEIGHTMAP" value="7" enum="ShapeType">
+		<constant name="SHAPE_HEIGHTMAP" value="8" enum="ShapeType">
 			The [Shape3D] is a [HeightMapShape3D].
 			The [Shape3D] is a [HeightMapShape3D].
 		</constant>
 		</constant>
-		<constant name="SHAPE_SOFT_BODY" value="8" enum="ShapeType">
+		<constant name="SHAPE_SOFT_BODY" value="9" enum="ShapeType">
 			The [Shape3D] is a [SoftBody3D].
 			The [Shape3D] is a [SoftBody3D].
 		</constant>
 		</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.
 			This constant is used internally by the engine. Any attempt to create this kind of shape results in an error.
 		</constant>
 		</constant>
 		<constant name="AREA_PARAM_GRAVITY" value="0" enum="AreaParameter">
 		<constant name="AREA_PARAM_GRAVITY" value="0" enum="AreaParameter">

+ 23 - 0
doc/classes/RayShape2D.xml

@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="RayShape2D" inherits="Shape2D" version="4.0">
+	<brief_description>
+		Ray shape for 2D collisions.
+	</brief_description>
+	<description>
+		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="slips_on_slope" type="bool" setter="set_slips_on_slope" getter="get_slips_on_slope" default="false">
+			If [code]true[/code], allow the shape to return the correct normal.
+		</member>
+	</members>
+	<constants>
+	</constants>
+</class>

+ 23 - 0
doc/classes/RayShape3D.xml

@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="RayShape3D" inherits="Shape3D" version="4.0">
+	<brief_description>
+		Ray shape for 3D collisions.
+	</brief_description>
+	<description>
+		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="slips_on_slope" type="bool" setter="set_slips_on_slope" getter="get_slips_on_slope" default="false">
+			If [code]true[/code], allow the shape to return the correct normal.
+		</member>
+	</members>
+	<constants>
+	</constants>
+</class>

+ 1 - 0
editor/icons/RayShape2D.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>

+ 1 - 0
editor/icons/RayShape3D.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>

+ 6 - 0
editor/import/resource_importer_scene.cpp

@@ -45,6 +45,7 @@
 #include "scene/resources/animation.h"
 #include "scene/resources/animation.h"
 #include "scene/resources/box_shape_3d.h"
 #include "scene/resources/box_shape_3d.h"
 #include "scene/resources/packed_scene.h"
 #include "scene/resources/packed_scene.h"
+#include "scene/resources/ray_shape_3d.h"
 #include "scene/resources/resource_format_text.h"
 #include "scene/resources/resource_format_text.h"
 #include "scene/resources/sphere_shape_3d.h"
 #include "scene/resources/sphere_shape_3d.h"
 #include "scene/resources/surface_tool.h"
 #include "scene/resources/surface_tool.h"
@@ -379,6 +380,11 @@ Node *ResourceImporterScene::_pre_fix_node(Node *p_node, Node *p_root, Map<Ref<E
 				BoxShape3D *boxShape = memnew(BoxShape3D);
 				BoxShape3D *boxShape = memnew(BoxShape3D);
 				boxShape->set_size(Vector3(2, 2, 2));
 				boxShape->set_size(Vector3(2, 2, 2));
 				colshape->set_shape(boxShape);
 				colshape->set_shape(boxShape);
+			} else if (empty_draw_type == "SINGLE_ARROW") {
+				RayShape3D *rayShape = memnew(RayShape3D);
+				rayShape->set_length(1);
+				colshape->set_shape(rayShape);
+				Object::cast_to<Node3D>(sb)->rotate_x(Math_PI / 2);
 			} else if (empty_draw_type == "IMAGE") {
 			} else if (empty_draw_type == "IMAGE") {
 				WorldMarginShape3D *world_margin_shape = memnew(WorldMarginShape3D);
 				WorldMarginShape3D *world_margin_shape = memnew(WorldMarginShape3D);
 				colshape->set_shape(world_margin_shape);
 				colshape->set_shape(world_margin_shape);

+ 41 - 0
editor/plugins/collision_shape_2d_editor_plugin.cpp

@@ -36,6 +36,7 @@
 #include "scene/resources/circle_shape_2d.h"
 #include "scene/resources/circle_shape_2d.h"
 #include "scene/resources/concave_polygon_shape_2d.h"
 #include "scene/resources/concave_polygon_shape_2d.h"
 #include "scene/resources/convex_polygon_shape_2d.h"
 #include "scene/resources/convex_polygon_shape_2d.h"
+#include "scene/resources/ray_shape_2d.h"
 #include "scene/resources/rectangle_shape_2d.h"
 #include "scene/resources/rectangle_shape_2d.h"
 #include "scene/resources/segment_shape_2d.h"
 #include "scene/resources/segment_shape_2d.h"
 #include "scene/resources/world_margin_shape_2d.h"
 #include "scene/resources/world_margin_shape_2d.h"
@@ -80,6 +81,15 @@ Variant CollisionShape2DEditor::get_handle_value(int idx) const {
 
 
 		} break;
 		} break;
 
 
+		case RAY_SHAPE: {
+			Ref<RayShape2D> ray = node->get_shape();
+
+			if (idx == 0) {
+				return ray->get_length();
+			}
+
+		} break;
+
 		case RECTANGLE_SHAPE: {
 		case RECTANGLE_SHAPE: {
 			Ref<RectangleShape2D> rect = node->get_shape();
 			Ref<RectangleShape2D> rect = node->get_shape();
 
 
@@ -152,6 +162,15 @@ void CollisionShape2DEditor::set_handle(int idx, Point2 &p_point) {
 
 
 		} break;
 		} break;
 
 
+		case RAY_SHAPE: {
+			Ref<RayShape2D> ray = node->get_shape();
+
+			ray->set_length(Math::abs(p_point.y));
+
+			canvas_item_editor->update_viewport();
+
+		} break;
+
 		case RECTANGLE_SHAPE: {
 		case RECTANGLE_SHAPE: {
 			if (idx < 8) {
 			if (idx < 8) {
 				Ref<RectangleShape2D> rect = node->get_shape();
 				Ref<RectangleShape2D> rect = node->get_shape();
@@ -253,6 +272,16 @@ void CollisionShape2DEditor::commit_handle(int idx, Variant &p_org) {
 
 
 		} break;
 		} break;
 
 
+		case RAY_SHAPE: {
+			Ref<RayShape2D> 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: {
 		case RECTANGLE_SHAPE: {
 			Ref<RectangleShape2D> rect = node->get_shape();
 			Ref<RectangleShape2D> rect = node->get_shape();
 
 
@@ -394,6 +423,8 @@ void CollisionShape2DEditor::_get_current_shape_type() {
 		shape_type = CONVEX_POLYGON_SHAPE;
 		shape_type = CONVEX_POLYGON_SHAPE;
 	} else if (Object::cast_to<WorldMarginShape2D>(*s)) {
 	} else if (Object::cast_to<WorldMarginShape2D>(*s)) {
 		shape_type = WORLD_MARGIN_SHAPE;
 		shape_type = WORLD_MARGIN_SHAPE;
+	} else if (Object::cast_to<RayShape2D>(*s)) {
+		shape_type = RAY_SHAPE;
 	} else if (Object::cast_to<RectangleShape2D>(*s)) {
 	} else if (Object::cast_to<RectangleShape2D>(*s)) {
 		shape_type = RECTANGLE_SHAPE;
 		shape_type = RECTANGLE_SHAPE;
 	} else if (Object::cast_to<SegmentShape2D>(*s)) {
 	} else if (Object::cast_to<SegmentShape2D>(*s)) {
@@ -471,6 +502,16 @@ void CollisionShape2DEditor::forward_canvas_draw_over_viewport(Control *p_overla
 
 
 		} break;
 		} break;
 
 
+		case RAY_SHAPE: {
+			Ref<RayShape2D> 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: {
 		case RECTANGLE_SHAPE: {
 			Ref<RectangleShape2D> shape = node->get_shape();
 			Ref<RectangleShape2D> shape = node->get_shape();
 
 

+ 1 - 0
editor/plugins/collision_shape_2d_editor_plugin.h

@@ -47,6 +47,7 @@ class CollisionShape2DEditor : public Control {
 		CONCAVE_POLYGON_SHAPE,
 		CONCAVE_POLYGON_SHAPE,
 		CONVEX_POLYGON_SHAPE,
 		CONVEX_POLYGON_SHAPE,
 		WORLD_MARGIN_SHAPE,
 		WORLD_MARGIN_SHAPE,
+		RAY_SHAPE,
 		RECTANGLE_SHAPE,
 		RECTANGLE_SHAPE,
 		SEGMENT_SHAPE
 		SEGMENT_SHAPE
 	};
 	};

+ 53 - 0
editor/plugins/node_3d_editor_gizmos.cpp

@@ -66,6 +66,7 @@
 #include "scene/resources/cylinder_shape_3d.h"
 #include "scene/resources/cylinder_shape_3d.h"
 #include "scene/resources/height_map_shape_3d.h"
 #include "scene/resources/height_map_shape_3d.h"
 #include "scene/resources/primitive_meshes.h"
 #include "scene/resources/primitive_meshes.h"
+#include "scene/resources/ray_shape_3d.h"
 #include "scene/resources/sphere_shape_3d.h"
 #include "scene/resources/sphere_shape_3d.h"
 #include "scene/resources/surface_tool.h"
 #include "scene/resources/surface_tool.h"
 #include "scene/resources/world_margin_shape_3d.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";
 		return p_id == 0 ? "Radius" : "Height";
 	}
 	}
 
 
+	if (Object::cast_to<RayShape3D>(*s)) {
+		return "Length";
+	}
+
 	return "";
 	return "";
 }
 }
 
 
@@ -4098,6 +4103,11 @@ Variant CollisionShape3DGizmoPlugin::get_handle_value(const EditorNode3DGizmo *p
 		return p_id == 0 ? cs2->get_radius() : cs2->get_height();
 		return p_id == 0 ? cs2->get_radius() : cs2->get_height();
 	}
 	}
 
 
+	if (Object::cast_to<RayShape3D>(*s)) {
+		Ref<RayShape3D> cs2 = s;
+		return cs2->get_length();
+	}
+
 	return Variant();
 	return Variant();
 }
 }
 
 
@@ -4133,6 +4143,22 @@ void CollisionShape3DGizmoPlugin::set_handle(const EditorNode3DGizmo *p_gizmo, i
 		ss->set_radius(d);
 		ss->set_radius(d);
 	}
 	}
 
 
+	if (Object::cast_to<RayShape3D>(*s)) {
+		Ref<RayShape3D> 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)) {
 	if (Object::cast_to<BoxShape3D>(*s)) {
 		Vector3 axis;
 		Vector3 axis;
 		axis[p_id] = 1.0;
 		axis[p_id] = 1.0;
@@ -4287,6 +4313,20 @@ void CollisionShape3DGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo
 
 
 		ur->commit_action();
 		ur->commit_action();
 	}
 	}
+
+	if (Object::cast_to<RayShape3D>(*s)) {
+		Ref<RayShape3D> ss = s;
+		if (p_cancel) {
+			ss->set_length(p_restore);
+			return;
+		}
+
+		UndoRedo *ur = Node3DEditor::get_singleton()->get_undo_redo();
+		ur->create_action(TTR("Change 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) {
 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());
 		p_gizmo->add_collision_segments(cs2->get_debug_mesh_lines());
 	}
 	}
 
 
+	if (Object::cast_to<RayShape3D>(*s)) {
+		Ref<RayShape3D> 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)) {
 	if (Object::cast_to<HeightMapShape3D>(*s)) {
 		Ref<HeightMapShape3D> hms = s;
 		Ref<HeightMapShape3D> hms = s;
 
 

+ 5 - 0
scene/register_scene_types.cpp

@@ -157,6 +157,8 @@
 #include "scene/resources/physics_material.h"
 #include "scene/resources/physics_material.h"
 #include "scene/resources/polygon_path_finder.h"
 #include "scene/resources/polygon_path_finder.h"
 #include "scene/resources/primitive_meshes.h"
 #include "scene/resources/primitive_meshes.h"
+#include "scene/resources/ray_shape_2d.h"
+#include "scene/resources/ray_shape_3d.h"
 #include "scene/resources/rectangle_shape_2d.h"
 #include "scene/resources/rectangle_shape_2d.h"
 #include "scene/resources/resource_format_text.h"
 #include "scene/resources/resource_format_text.h"
 #include "scene/resources/segment_shape_2d.h"
 #include "scene/resources/segment_shape_2d.h"
@@ -752,6 +754,7 @@ void register_scene_types() {
 	OS::get_singleton()->yield(); //may take time to init
 	OS::get_singleton()->yield(); //may take time to init
 
 
 	GDREGISTER_VIRTUAL_CLASS(Shape3D);
 	GDREGISTER_VIRTUAL_CLASS(Shape3D);
+	GDREGISTER_CLASS(RayShape3D);
 	GDREGISTER_CLASS(SphereShape3D);
 	GDREGISTER_CLASS(SphereShape3D);
 	GDREGISTER_CLASS(BoxShape3D);
 	GDREGISTER_CLASS(BoxShape3D);
 	GDREGISTER_CLASS(CapsuleShape3D);
 	GDREGISTER_CLASS(CapsuleShape3D);
@@ -840,6 +843,7 @@ void register_scene_types() {
 	GDREGISTER_VIRTUAL_CLASS(Shape2D);
 	GDREGISTER_VIRTUAL_CLASS(Shape2D);
 	GDREGISTER_CLASS(WorldMarginShape2D);
 	GDREGISTER_CLASS(WorldMarginShape2D);
 	GDREGISTER_CLASS(SegmentShape2D);
 	GDREGISTER_CLASS(SegmentShape2D);
+	GDREGISTER_CLASS(RayShape2D);
 	GDREGISTER_CLASS(CircleShape2D);
 	GDREGISTER_CLASS(CircleShape2D);
 	GDREGISTER_CLASS(RectangleShape2D);
 	GDREGISTER_CLASS(RectangleShape2D);
 	GDREGISTER_CLASS(CapsuleShape2D);
 	GDREGISTER_CLASS(CapsuleShape2D);
@@ -960,6 +964,7 @@ void register_scene_types() {
 	ClassDB::add_compatibility_class("ProceduralSky", "Sky");
 	ClassDB::add_compatibility_class("ProceduralSky", "Sky");
 	ClassDB::add_compatibility_class("ProximityGroup", "ProximityGroup3D");
 	ClassDB::add_compatibility_class("ProximityGroup", "ProximityGroup3D");
 	ClassDB::add_compatibility_class("RayCast", "RayCast3D");
 	ClassDB::add_compatibility_class("RayCast", "RayCast3D");
+	ClassDB::add_compatibility_class("RayShape", "RayShape3D");
 	ClassDB::add_compatibility_class("RemoteTransform", "RemoteTransform3D");
 	ClassDB::add_compatibility_class("RemoteTransform", "RemoteTransform3D");
 	ClassDB::add_compatibility_class("RigidBody", "RigidBody3D");
 	ClassDB::add_compatibility_class("RigidBody", "RigidBody3D");
 	ClassDB::add_compatibility_class("Shape", "Shape3D");
 	ClassDB::add_compatibility_class("Shape", "Shape3D");

+ 119 - 0
scene/resources/ray_shape_2d.cpp

@@ -0,0 +1,119 @@
+/*************************************************************************/
+/*  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 "ray_shape_2d.h"
+
+#include "servers/physics_server_2d.h"
+#include "servers/rendering_server.h"
+
+void RayShape2D::_update_shape() {
+	Dictionary d;
+	d["length"] = length;
+	d["slips_on_slope"] = slips_on_slope;
+	PhysicsServer2D::get_singleton()->shape_set_data(get_rid(), d);
+	emit_changed();
+}
+
+void RayShape2D::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 RayShape2D::get_rect() const {
+	Rect2 rect;
+	rect.position = Vector2();
+	rect.expand_to(Vector2(0, length));
+	rect = rect.grow(Math_SQRT12 * 4);
+	return rect;
+}
+
+real_t RayShape2D::get_enclosing_radius() const {
+	return length;
+}
+
+void RayShape2D::_bind_methods() {
+	ClassDB::bind_method(D_METHOD("set_length", "length"), &RayShape2D::set_length);
+	ClassDB::bind_method(D_METHOD("get_length"), &RayShape2D::get_length);
+
+	ClassDB::bind_method(D_METHOD("set_slips_on_slope", "active"), &RayShape2D::set_slips_on_slope);
+	ClassDB::bind_method(D_METHOD("get_slips_on_slope"), &RayShape2D::get_slips_on_slope);
+
+	ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "length"), "set_length", "get_length");
+	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "slips_on_slope"), "set_slips_on_slope", "get_slips_on_slope");
+}
+
+void RayShape2D::set_length(real_t p_length) {
+	length = p_length;
+	_update_shape();
+}
+
+real_t RayShape2D::get_length() const {
+	return length;
+}
+
+void RayShape2D::set_slips_on_slope(bool p_active) {
+	slips_on_slope = p_active;
+	_update_shape();
+}
+
+bool RayShape2D::get_slips_on_slope() const {
+	return slips_on_slope;
+}
+
+RayShape2D::RayShape2D() :
+		Shape2D(PhysicsServer2D::get_singleton()->ray_shape_create()) {
+	_update_shape();
+}

+ 61 - 0
scene/resources/ray_shape_2d.h

@@ -0,0 +1,61 @@
+/*************************************************************************/
+/*  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 RAY_SHAPE_2D_H
+#define RAY_SHAPE_2D_H
+
+#include "scene/resources/shape_2d.h"
+
+class RayShape2D : public Shape2D {
+	GDCLASS(RayShape2D, Shape2D);
+
+	real_t length = 20.0;
+	bool slips_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_slips_on_slope(bool p_active);
+	bool get_slips_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;
+
+	RayShape2D();
+};
+
+#endif // RAY_SHAPE_2D_H

+ 91 - 0
scene/resources/ray_shape_3d.cpp

@@ -0,0 +1,91 @@
+/*************************************************************************/
+/*  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 "ray_shape_3d.h"
+
+#include "servers/physics_server_3d.h"
+
+Vector<Vector3> RayShape3D::get_debug_mesh_lines() const {
+	Vector<Vector3> points;
+	points.push_back(Vector3());
+	points.push_back(Vector3(0, 0, get_length()));
+
+	return points;
+}
+
+real_t RayShape3D::get_enclosing_radius() const {
+	return length;
+}
+
+void RayShape3D::_update_shape() {
+	Dictionary d;
+	d["length"] = length;
+	d["slips_on_slope"] = slips_on_slope;
+	PhysicsServer3D::get_singleton()->shape_set_data(get_shape(), d);
+	Shape3D::_update_shape();
+}
+
+void RayShape3D::set_length(float p_length) {
+	length = p_length;
+	_update_shape();
+	notify_change_to_owners();
+}
+
+float RayShape3D::get_length() const {
+	return length;
+}
+
+void RayShape3D::set_slips_on_slope(bool p_active) {
+	slips_on_slope = p_active;
+	_update_shape();
+	notify_change_to_owners();
+}
+
+bool RayShape3D::get_slips_on_slope() const {
+	return slips_on_slope;
+}
+
+void RayShape3D::_bind_methods() {
+	ClassDB::bind_method(D_METHOD("set_length", "length"), &RayShape3D::set_length);
+	ClassDB::bind_method(D_METHOD("get_length"), &RayShape3D::get_length);
+
+	ClassDB::bind_method(D_METHOD("set_slips_on_slope", "active"), &RayShape3D::set_slips_on_slope);
+	ClassDB::bind_method(D_METHOD("get_slips_on_slope"), &RayShape3D::get_slips_on_slope);
+
+	ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "length", PROPERTY_HINT_RANGE, "0,4096,0.001"), "set_length", "get_length");
+	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "slips_on_slope"), "set_slips_on_slope", "get_slips_on_slope");
+}
+
+RayShape3D::RayShape3D() :
+		Shape3D(PhysicsServer3D::get_singleton()->shape_create(PhysicsServer3D::SHAPE_RAY)) {
+	/* Code copied from setters to prevent the use of uninitialized variables */
+	_update_shape();
+	notify_change_to_owners();
+}

+ 56 - 0
scene/resources/ray_shape_3d.h

@@ -0,0 +1,56 @@
+/*************************************************************************/
+/*  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 RAY_SHAPE_H
+#define RAY_SHAPE_H
+#include "scene/resources/shape_3d.h"
+
+class RayShape3D : public Shape3D {
+	GDCLASS(RayShape3D, Shape3D);
+	float length = 1.0;
+	bool slips_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_slips_on_slope(bool p_active);
+	bool get_slips_on_slope() const;
+
+	virtual Vector<Vector3> get_debug_mesh_lines() const override;
+	virtual real_t get_enclosing_radius() const override;
+
+	RayShape3D();
+};
+#endif // RAY_SHAPE_H

+ 10 - 8
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();
 	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_WORLD_MARGIN, false);
+	ERR_FAIL_COND_V(type_A == PhysicsServer2D::SHAPE_RAY, false);
 	ERR_FAIL_COND_V(p_shape_A->is_concave(), false);
 	ERR_FAIL_COND_V(p_shape_A->is_concave(), false);
 
 
 	PhysicsServer2D::ShapeType type_B = p_shape_B->get_type();
 	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_WORLD_MARGIN, false);
+	ERR_FAIL_COND_V(type_B == PhysicsServer2D::SHAPE_RAY, false);
 	ERR_FAIL_COND_V(p_shape_B->is_concave(), false);
 	ERR_FAIL_COND_V(p_shape_B->is_concave(), false);
 
 
 	static const CollisionFunc collision_table[5][5] = {
 	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 (p_margin_A || p_margin_B) {
 		if (*motion_A == Vector2() && *motion_B == Vector2()) {
 		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()) {
 		} 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()) {
 		} 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 {
 		} 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 {
 	} else {
 		if (*motion_A == Vector2() && *motion_B == Vector2()) {
 		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()) {
 		} 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()) {
 		} 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 {
 		} 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];
 		}
 		}
 	}
 	}
 
 

+ 54 - 0
servers/physics_2d/collision_solver_2d_sw.cpp

@@ -73,6 +73,49 @@ bool CollisionSolver2DSW::solve_static_world_margin(const Shape2DSW *p_shape_A,
 	return found;
 	return found;
 }
 }
 
 
+bool CollisionSolver2DSW::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) {
+	const RayShape2DSW *ray = static_cast<const RayShape2DSW *>(p_shape_A);
+	if (p_shape_B->get_type() == PhysicsServer2D::SHAPE_RAY) {
+		return false;
+	}
+
+	Vector2 from = p_transform_A.get_origin();
+	Vector2 to = from + p_transform_A[1] * ray->get_length();
+	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 (sep_axis) {
+			*sep_axis = p_transform_A[1].normalized();
+		}
+		return false;
+	}
+
+	Vector2 support_B = p_transform_B.xform(p);
+	if (ray->get_slips_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 {
 struct _ConcaveCollisionInfo2D {
 	const Transform2D *transform_A;
 	const Transform2D *transform_A;
 	const Shape2DSW *shape_A;
 	const Shape2DSW *shape_A;
@@ -177,6 +220,17 @@ 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);
 			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_RAY) {
+		if (type_B == PhysicsServer2D::SHAPE_RAY) {
+			return false; //no ray-ray
+		}
+
+		if (swap) {
+			return solve_raycast(p_shape_B, p_motion_B, p_transform_B, p_shape_A, p_transform_A, p_result_callback, p_userdata, true, sep_axis);
+		} else {
+			return solve_raycast(p_shape_A, p_motion_A, p_transform_A, p_shape_B, p_transform_B, p_result_callback, p_userdata, false, sep_axis);
+		}
+
 	} else if (concave_B) {
 	} else if (concave_B) {
 		if (concave_A) {
 		if (concave_A) {
 			return false;
 			return false;

+ 7 - 0
servers/physics_2d/physics_server_2d_sw.cpp

@@ -45,6 +45,9 @@ RID PhysicsServer2DSW::_shape_create(ShapeType p_shape) {
 		case SHAPE_WORLD_MARGIN: {
 		case SHAPE_WORLD_MARGIN: {
 			shape = memnew(WorldMarginShape2DSW);
 			shape = memnew(WorldMarginShape2DSW);
 		} break;
 		} break;
+		case SHAPE_RAY: {
+			shape = memnew(RayShape2DSW);
+		} break;
 		case SHAPE_SEGMENT: {
 		case SHAPE_SEGMENT: {
 			shape = memnew(SegmentShape2DSW);
 			shape = memnew(SegmentShape2DSW);
 		} break;
 		} break;
@@ -79,6 +82,10 @@ RID PhysicsServer2DSW::world_margin_shape_create() {
 	return _shape_create(SHAPE_WORLD_MARGIN);
 	return _shape_create(SHAPE_WORLD_MARGIN);
 }
 }
 
 
+RID PhysicsServer2DSW::ray_shape_create() {
+	return _shape_create(SHAPE_RAY);
+}
+
 RID PhysicsServer2DSW::segment_shape_create() {
 RID PhysicsServer2DSW::segment_shape_create() {
 	return _shape_create(SHAPE_SEGMENT);
 	return _shape_create(SHAPE_SEGMENT);
 }
 }

+ 1 - 0
servers/physics_2d/physics_server_2d_sw.h

@@ -88,6 +88,7 @@ public:
 	};
 	};
 
 
 	virtual RID world_margin_shape_create() override;
 	virtual RID world_margin_shape_create() override;
+	virtual RID ray_shape_create() override;
 	virtual RID segment_shape_create() override;
 	virtual RID segment_shape_create() override;
 	virtual RID circle_shape_create() override;
 	virtual RID circle_shape_create() override;
 	virtual RID rectangle_shape_create() override;
 	virtual RID rectangle_shape_create() override;

+ 1 - 0
servers/physics_2d/physics_server_2d_wrap_mt.h

@@ -80,6 +80,7 @@ public:
 
 
 	//FUNC1RID(shape,ShapeType); todo fix
 	//FUNC1RID(shape,ShapeType); todo fix
 	FUNCRID(world_margin_shape)
 	FUNCRID(world_margin_shape)
+	FUNCRID(ray_shape)
 	FUNCRID(segment_shape)
 	FUNCRID(segment_shape)
 	FUNCRID(circle_shape)
 	FUNCRID(circle_shape)
 	FUNCRID(rectangle_shape)
 	FUNCRID(rectangle_shape)

+ 40 - 0
servers/physics_2d/shape_2d_sw.cpp

@@ -143,6 +143,46 @@ Variant WorldMarginShape2DSW::get_data() const {
 /*********************************************************/
 /*********************************************************/
 /*********************************************************/
 /*********************************************************/
 
 
+void RayShape2DSW::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 RayShape2DSW::contains_point(const Vector2 &p_point) const {
+	return false;
+}
+
+bool RayShape2DSW::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 RayShape2DSW::get_moment_of_inertia(real_t p_mass, const Size2 &p_scale) const {
+	return 0; //rays are mass-less
+}
+
+void RayShape2DSW::set_data(const Variant &p_data) {
+	Dictionary d = p_data;
+	length = d["length"];
+	slips_on_slope = d["slips_on_slope"];
+	configure(Rect2(0, 0, 0.001, length));
+}
+
+Variant RayShape2DSW::get_data() const {
+	Dictionary d;
+	d["length"] = length;
+	d["slips_on_slope"] = slips_on_slope;
+	return d;
+}
+
+/*********************************************************/
+/*********************************************************/
+/*********************************************************/
+
 void SegmentShape2DSW::get_supports(const Vector2 &p_normal, Vector2 *r_supports, int &r_amount) const {
 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) {
 	if (Math::abs(p_normal.dot(n)) > _SEGMENT_IS_VALID_SUPPORT_THRESHOLD) {
 		r_supports[0] = a;
 		r_supports[0] = a;

+ 35 - 0
servers/physics_2d/shape_2d_sw.h

@@ -177,6 +177,41 @@ public:
 	}
 	}
 };
 };
 
 
+class RayShape2DSW : public Shape2DSW {
+	real_t length;
+	bool slips_on_slope;
+
+public:
+	_FORCE_INLINE_ real_t get_length() const { return length; }
+	_FORCE_INLINE_ bool get_slips_on_slope() const { return slips_on_slope; }
+
+	virtual PhysicsServer2D::ShapeType get_type() const { return PhysicsServer2D::SHAPE_RAY; }
+
+	virtual void project_rangev(const Vector2 &p_normal, const Transform2D &p_transform, real_t &r_min, real_t &r_max) const { 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;
+
+	virtual bool contains_point(const Vector2 &p_point) const;
+	virtual bool intersect_segment(const Vector2 &p_begin, const Vector2 &p_end, Vector2 &r_point, Vector2 &r_normal) const;
+	virtual real_t get_moment_of_inertia(real_t p_mass, const Size2 &p_scale) const;
+
+	virtual void set_data(const Variant &p_data);
+	virtual Variant get_data() const;
+
+	_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_ RayShape2DSW() {}
+	_FORCE_INLINE_ RayShape2DSW(real_t p_length) { length = p_length; }
+};
+
 class SegmentShape2DSW : public Shape2DSW {
 class SegmentShape2DSW : public Shape2DSW {
 	Vector2 a;
 	Vector2 a;
 	Vector2 b;
 	Vector2 b;

+ 4 - 2
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();
 	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_PLANE, false);
+	ERR_FAIL_COND_V(type_A == PhysicsServer3D::SHAPE_RAY, false);
 	ERR_FAIL_COND_V(p_shape_A->is_concave(), false);
 	ERR_FAIL_COND_V(p_shape_A->is_concave(), false);
 
 
 	PhysicsServer3D::ShapeType type_B = p_shape_B->get_type();
 	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_PLANE, false);
+	ERR_FAIL_COND_V(type_B == PhysicsServer3D::SHAPE_RAY, false);
 	ERR_FAIL_COND_V(p_shape_B->is_concave(), false);
 	ERR_FAIL_COND_V(p_shape_B->is_concave(), false);
 
 
 	static const CollisionFunc collision_table[6][6] = {
 	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;
 	CollisionFunc collision_func;
 	if (margin_A != 0.0 || margin_B != 0.0) {
 	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 {
 	} 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);
 	ERR_FAIL_COND_V(!collision_func, false);
 
 

+ 47 - 0
servers/physics_3d/collision_solver_3d_sw.cpp

@@ -89,6 +89,39 @@ bool CollisionSolver3DSW::solve_static_plane(const Shape3DSW *p_shape_A, const T
 	return found;
 	return found;
 }
 }
 
 
+bool CollisionSolver3DSW::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) {
+	const RayShape3DSW *ray = static_cast<const RayShape3DSW *>(p_shape_A);
+
+	Vector3 from = p_transform_A.origin;
+	Vector3 to = from + p_transform_A.basis.get_axis(2) * ray->get_length();
+	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;
+	}
+
+	Vector3 support_B = p_transform_B.xform(p);
+	if (ray->get_slips_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 {
 struct _SoftBodyContactCollisionInfo {
 	int node_index = 0;
 	int node_index = 0;
 	CollisionSolver3DSW::CallbackResult result_callback = nullptr;
 	CollisionSolver3DSW::CallbackResult result_callback = nullptr;
@@ -318,6 +351,9 @@ bool CollisionSolver3DSW::solve_static(const Shape3DSW *p_shape_A, const Transfo
 		if (type_B == PhysicsServer3D::SHAPE_PLANE) {
 		if (type_B == PhysicsServer3D::SHAPE_PLANE) {
 			return false;
 			return false;
 		}
 		}
+		if (type_B == PhysicsServer3D::SHAPE_RAY) {
+			return false;
+		}
 		if (type_B == PhysicsServer3D::SHAPE_SOFT_BODY) {
 		if (type_B == PhysicsServer3D::SHAPE_SOFT_BODY) {
 			return false;
 			return false;
 		}
 		}
@@ -328,6 +364,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);
 			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_RAY) {
+		if (type_B == PhysicsServer3D::SHAPE_RAY) {
+			return false;
+		}
+
+		if (swap) {
+			return solve_ray(p_shape_B, p_transform_B, p_shape_A, p_transform_A, p_result_callback, p_userdata, true);
+		} else {
+			return solve_ray(p_shape_A, p_transform_A, p_shape_B, p_transform_B, p_result_callback, p_userdata, false);
+		}
+
 	} else if (type_B == PhysicsServer3D::SHAPE_SOFT_BODY) {
 	} else if (type_B == PhysicsServer3D::SHAPE_SOFT_BODY) {
 		if (type_A == PhysicsServer3D::SHAPE_SOFT_BODY) {
 		if (type_A == PhysicsServer3D::SHAPE_SOFT_BODY) {
 			// Soft Body / Soft Body not supported.
 			// Soft Body / Soft Body not supported.

+ 6 - 0
servers/physics_3d/physics_server_3d_sw.cpp

@@ -48,6 +48,12 @@ RID PhysicsServer3DSW::plane_shape_create() {
 	shape->set_self(rid);
 	shape->set_self(rid);
 	return rid;
 	return rid;
 }
 }
+RID PhysicsServer3DSW::ray_shape_create() {
+	Shape3DSW *shape = memnew(RayShape3DSW);
+	RID rid = shape_owner.make_rid(shape);
+	shape->set_self(rid);
+	return rid;
+}
 RID PhysicsServer3DSW::sphere_shape_create() {
 RID PhysicsServer3DSW::sphere_shape_create() {
 	Shape3DSW *shape = memnew(SphereShape3DSW);
 	Shape3DSW *shape = memnew(SphereShape3DSW);
 	RID rid = shape_owner.make_rid(shape);
 	RID rid = shape_owner.make_rid(shape);

+ 1 - 0
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);
 	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 plane_shape_create() override;
+	virtual RID ray_shape_create() override;
 	virtual RID sphere_shape_create() override;
 	virtual RID sphere_shape_create() override;
 	virtual RID box_shape_create() override;
 	virtual RID box_shape_create() override;
 	virtual RID capsule_shape_create() override;
 	virtual RID capsule_shape_create() override;

+ 1 - 0
servers/physics_3d/physics_server_3d_wrap_mt.h

@@ -79,6 +79,7 @@ public:
 
 
 	//FUNC1RID(shape,ShapeType); todo fix
 	//FUNC1RID(shape,ShapeType); todo fix
 	FUNCRID(plane_shape)
 	FUNCRID(plane_shape)
+	FUNCRID(ray_shape)
 	FUNCRID(sphere_shape)
 	FUNCRID(sphere_shape)
 	FUNCRID(box_shape)
 	FUNCRID(box_shape)
 	FUNCRID(capsule_shape)
 	FUNCRID(capsule_shape)

+ 85 - 0
servers/physics_3d/shape_3d_sw.cpp

@@ -164,6 +164,91 @@ Variant PlaneShape3DSW::get_data() const {
 PlaneShape3DSW::PlaneShape3DSW() {
 PlaneShape3DSW::PlaneShape3DSW() {
 }
 }
 
 
+//
+
+real_t RayShape3DSW::get_length() const {
+	return length;
+}
+
+bool RayShape3DSW::get_slips_on_slope() const {
+	return slips_on_slope;
+}
+
+void RayShape3DSW::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 RayShape3DSW::get_support(const Vector3 &p_normal) const {
+	if (p_normal.z > 0) {
+		return Vector3(0, 0, length);
+	} else {
+		return Vector3(0, 0, 0);
+	}
+}
+
+void RayShape3DSW::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 RayShape3DSW::intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal) const {
+	return false; //simply not possible
+}
+
+bool RayShape3DSW::intersect_point(const Vector3 &p_point) const {
+	return false; //simply not possible
+}
+
+Vector3 RayShape3DSW::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 RayShape3DSW::get_moment_of_inertia(real_t p_mass) const {
+	return Vector3();
+}
+
+void RayShape3DSW::_setup(real_t p_length, bool p_slips_on_slope) {
+	length = p_length;
+	slips_on_slope = p_slips_on_slope;
+	configure(AABB(Vector3(0, 0, 0), Vector3(0.1, 0.1, length)));
+}
+
+void RayShape3DSW::set_data(const Variant &p_data) {
+	Dictionary d = p_data;
+	_setup(d["length"], d["slips_on_slope"]);
+}
+
+Variant RayShape3DSW::get_data() const {
+	Dictionary d;
+	d["length"] = length;
+	d["slips_on_slope"] = slips_on_slope;
+	return d;
+}
+
+RayShape3DSW::RayShape3DSW() {
+	length = 1;
+	slips_on_slope = false;
+}
+
 /********** SPHERE *************/
 /********** SPHERE *************/
 
 
 real_t SphereShape3DSW::get_radius() const {
 real_t SphereShape3DSW::get_radius() const {

+ 28 - 0
servers/physics_3d/shape_3d_sw.h

@@ -134,6 +134,34 @@ public:
 	PlaneShape3DSW();
 	PlaneShape3DSW();
 };
 };
 
 
+class RayShape3DSW : public Shape3DSW {
+	real_t length;
+	bool slips_on_slope;
+
+	void _setup(real_t p_length, bool p_slips_on_slope);
+
+public:
+	real_t get_length() const;
+	bool get_slips_on_slope() const;
+
+	virtual real_t get_area() const { return 0.0; }
+	virtual PhysicsServer3D::ShapeType get_type() const { return PhysicsServer3D::SHAPE_RAY; }
+	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;
+
+	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 void set_data(const Variant &p_data);
+	virtual Variant get_data() const;
+
+	RayShape3DSW();
+};
+
 class SphereShape3DSW : public Shape3DSW {
 class SphereShape3DSW : public Shape3DSW {
 	real_t radius;
 	real_t radius;
 
 

+ 2 - 0
servers/physics_server_2d.cpp

@@ -514,6 +514,7 @@ bool PhysicsServer2D::_body_test_motion(RID p_body, const Transform2D &p_from, c
 
 
 void PhysicsServer2D::_bind_methods() {
 void PhysicsServer2D::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("world_margin_shape_create"), &PhysicsServer2D::world_margin_shape_create);
 	ClassDB::bind_method(D_METHOD("world_margin_shape_create"), &PhysicsServer2D::world_margin_shape_create);
+	ClassDB::bind_method(D_METHOD("ray_shape_create"), &PhysicsServer2D::ray_shape_create);
 	ClassDB::bind_method(D_METHOD("segment_shape_create"), &PhysicsServer2D::segment_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("circle_shape_create"), &PhysicsServer2D::circle_shape_create);
 	ClassDB::bind_method(D_METHOD("rectangle_shape_create"), &PhysicsServer2D::rectangle_shape_create);
 	ClassDB::bind_method(D_METHOD("rectangle_shape_create"), &PhysicsServer2D::rectangle_shape_create);
@@ -675,6 +676,7 @@ void PhysicsServer2D::_bind_methods() {
 	BIND_ENUM_CONSTANT(SPACE_PARAM_TEST_MOTION_MIN_CONTACT_DEPTH);
 	BIND_ENUM_CONSTANT(SPACE_PARAM_TEST_MOTION_MIN_CONTACT_DEPTH);
 
 
 	BIND_ENUM_CONSTANT(SHAPE_WORLD_MARGIN);
 	BIND_ENUM_CONSTANT(SHAPE_WORLD_MARGIN);
+	BIND_ENUM_CONSTANT(SHAPE_RAY);
 	BIND_ENUM_CONSTANT(SHAPE_SEGMENT);
 	BIND_ENUM_CONSTANT(SHAPE_SEGMENT);
 	BIND_ENUM_CONSTANT(SHAPE_CIRCLE);
 	BIND_ENUM_CONSTANT(SHAPE_CIRCLE);
 	BIND_ENUM_CONSTANT(SHAPE_RECTANGLE);
 	BIND_ENUM_CONSTANT(SHAPE_RECTANGLE);

+ 2 - 0
servers/physics_server_2d.h

@@ -220,6 +220,7 @@ public:
 
 
 	enum ShapeType {
 	enum ShapeType {
 		SHAPE_WORLD_MARGIN, ///< plane:"plane"
 		SHAPE_WORLD_MARGIN, ///< plane:"plane"
+		SHAPE_RAY, ///< float:"length"
 		SHAPE_SEGMENT, ///< float:"length"
 		SHAPE_SEGMENT, ///< float:"length"
 		SHAPE_CIRCLE, ///< float:"radius"
 		SHAPE_CIRCLE, ///< float:"radius"
 		SHAPE_RECTANGLE, ///< vec3:"extents"
 		SHAPE_RECTANGLE, ///< vec3:"extents"
@@ -230,6 +231,7 @@ public:
 	};
 	};
 
 
 	virtual RID world_margin_shape_create() = 0;
 	virtual RID world_margin_shape_create() = 0;
+	virtual RID ray_shape_create() = 0;
 	virtual RID segment_shape_create() = 0;
 	virtual RID segment_shape_create() = 0;
 	virtual RID circle_shape_create() = 0;
 	virtual RID circle_shape_create() = 0;
 	virtual RID rectangle_shape_create() = 0;
 	virtual RID rectangle_shape_create() = 0;

+ 4 - 0
servers/physics_server_3d.cpp

@@ -463,6 +463,8 @@ RID PhysicsServer3D::shape_create(ShapeType p_shape) {
 	switch (p_shape) {
 	switch (p_shape) {
 		case SHAPE_PLANE:
 		case SHAPE_PLANE:
 			return plane_shape_create();
 			return plane_shape_create();
+		case SHAPE_RAY:
+			return ray_shape_create();
 		case SHAPE_SPHERE:
 		case SHAPE_SPHERE:
 			return sphere_shape_create();
 			return sphere_shape_create();
 		case SHAPE_BOX:
 		case SHAPE_BOX:
@@ -488,6 +490,7 @@ void PhysicsServer3D::_bind_methods() {
 #ifndef _3D_DISABLED
 #ifndef _3D_DISABLED
 
 
 	ClassDB::bind_method(D_METHOD("plane_shape_create"), &PhysicsServer3D::plane_shape_create);
 	ClassDB::bind_method(D_METHOD("plane_shape_create"), &PhysicsServer3D::plane_shape_create);
+	ClassDB::bind_method(D_METHOD("ray_shape_create"), &PhysicsServer3D::ray_shape_create);
 	ClassDB::bind_method(D_METHOD("sphere_shape_create"), &PhysicsServer3D::sphere_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("box_shape_create"), &PhysicsServer3D::box_shape_create);
 	ClassDB::bind_method(D_METHOD("capsule_shape_create"), &PhysicsServer3D::capsule_shape_create);
 	ClassDB::bind_method(D_METHOD("capsule_shape_create"), &PhysicsServer3D::capsule_shape_create);
@@ -748,6 +751,7 @@ void PhysicsServer3D::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("get_process_info", "process_info"), &PhysicsServer3D::get_process_info);
 	ClassDB::bind_method(D_METHOD("get_process_info", "process_info"), &PhysicsServer3D::get_process_info);
 
 
 	BIND_ENUM_CONSTANT(SHAPE_PLANE);
 	BIND_ENUM_CONSTANT(SHAPE_PLANE);
+	BIND_ENUM_CONSTANT(SHAPE_RAY);
 	BIND_ENUM_CONSTANT(SHAPE_SPHERE);
 	BIND_ENUM_CONSTANT(SHAPE_SPHERE);
 	BIND_ENUM_CONSTANT(SHAPE_BOX);
 	BIND_ENUM_CONSTANT(SHAPE_BOX);
 	BIND_ENUM_CONSTANT(SHAPE_CAPSULE);
 	BIND_ENUM_CONSTANT(SHAPE_CAPSULE);

+ 2 - 0
servers/physics_server_3d.h

@@ -222,6 +222,7 @@ public:
 
 
 	enum ShapeType {
 	enum ShapeType {
 		SHAPE_PLANE, ///< plane:"plane"
 		SHAPE_PLANE, ///< plane:"plane"
+		SHAPE_RAY, ///< float:"length"
 		SHAPE_SPHERE, ///< float:"radius"
 		SHAPE_SPHERE, ///< float:"radius"
 		SHAPE_BOX, ///< vec3:"extents"
 		SHAPE_BOX, ///< vec3:"extents"
 		SHAPE_CAPSULE, ///< dict( float:"radius", float:"height"):capsule
 		SHAPE_CAPSULE, ///< dict( float:"radius", float:"height"):capsule
@@ -236,6 +237,7 @@ public:
 	RID shape_create(ShapeType p_shape);
 	RID shape_create(ShapeType p_shape);
 
 
 	virtual RID plane_shape_create() = 0;
 	virtual RID plane_shape_create() = 0;
+	virtual RID ray_shape_create() = 0;
 	virtual RID sphere_shape_create() = 0;
 	virtual RID sphere_shape_create() = 0;
 	virtual RID box_shape_create() = 0;
 	virtual RID box_shape_create() = 0;
 	virtual RID capsule_shape_create() = 0;
 	virtual RID capsule_shape_create() = 0;