diff --git a/doc/classes/CylinderShape.xml b/doc/classes/CylinderShape.xml
new file mode 100644
index 00000000000..a63cc8831e4
--- /dev/null
+++ b/doc/classes/CylinderShape.xml
@@ -0,0 +1,25 @@
+
+
+
+ Cylinder shape for collisions.
+
+
+ Cylinder shape for collisions.
+
+
+
+
+
+
+
+
+
+ The cylinder's height.
+
+
+ The cylinder's radius.
+
+
+
+
+
diff --git a/doc/classes/PhysicsServer.xml b/doc/classes/PhysicsServer.xml
index 6efbfdb5197..d45a3adc9ce 100644
--- a/doc/classes/PhysicsServer.xml
+++ b/doc/classes/PhysicsServer.xml
@@ -1413,16 +1413,19 @@
The [Shape] is a [CapsuleShape].
-
+
+ The [Shape] is a [CylinderShape].
+
+
The [Shape] is a [ConvexPolygonShape].
-
+
The [Shape] is a [ConcavePolygonShape].
-
+
The [Shape] is a [HeightMapShape].
-
+
This constant is used internally by the engine. Any attempt to create this kind of shape results in an error.
diff --git a/editor/icons/icon_cylinder_shape.svg b/editor/icons/icon_cylinder_shape.svg
new file mode 100644
index 00000000000..abda347ec57
--- /dev/null
+++ b/editor/icons/icon_cylinder_shape.svg
@@ -0,0 +1,6 @@
+
+
diff --git a/editor/spatial_editor_gizmos.cpp b/editor/spatial_editor_gizmos.cpp
index 2652b097633..a26ae67768b 100644
--- a/editor/spatial_editor_gizmos.cpp
+++ b/editor/spatial_editor_gizmos.cpp
@@ -36,6 +36,7 @@
#include "scene/resources/box_shape.h"
#include "scene/resources/capsule_shape.h"
#include "scene/resources/convex_polygon_shape.h"
+#include "scene/resources/cylinder_shape.h"
#include "scene/resources/plane_shape.h"
#include "scene/resources/primitive_meshes.h"
#include "scene/resources/ray_shape.h"
@@ -1861,6 +1862,11 @@ String CollisionShapeSpatialGizmo::get_handle_name(int p_idx) const {
return p_idx == 0 ? "Radius" : "Height";
}
+ if (Object::cast_to(*s)) {
+
+ return p_idx == 0 ? "Radius" : "Height";
+ }
+
if (Object::cast_to(*s)) {
return "Length";
@@ -1892,6 +1898,12 @@ Variant CollisionShapeSpatialGizmo::get_handle_value(int p_idx) const {
return p_idx == 0 ? cs->get_radius() : cs->get_height();
}
+ if (Object::cast_to(*s)) {
+
+ Ref cs = s;
+ return p_idx == 0 ? cs->get_radius() : cs->get_height();
+ }
+
if (Object::cast_to(*s)) {
Ref cs = s;
@@ -1972,6 +1984,24 @@ void CollisionShapeSpatialGizmo::set_handle(int p_idx, Camera *p_camera, const P
else if (p_idx == 1)
cs->set_height(d * 2.0);
}
+
+ if (Object::cast_to(*s)) {
+
+ Vector3 axis;
+ axis[p_idx == 0 ? 0 : 1] = 1.0;
+ Ref cs = s;
+ Vector3 ra, rb;
+ Geometry::get_closest_points_between_segments(Vector3(), axis * 4096, sg[0], sg[1], ra, rb);
+ float d = axis.dot(ra);
+
+ if (d < 0.001)
+ d = 0.001;
+
+ if (p_idx == 0)
+ cs->set_radius(d);
+ else if (p_idx == 1)
+ cs->set_height(d * 2.0);
+ }
}
void CollisionShapeSpatialGizmo::commit_handle(int p_idx, const Variant &p_restore, bool p_cancel) {
Ref s = cs->get_shape();
@@ -2033,6 +2063,31 @@ void CollisionShapeSpatialGizmo::commit_handle(int p_idx, const Variant &p_resto
ur->commit_action();
}
+ if (Object::cast_to(*s)) {
+
+ Ref ss = s;
+ if (p_cancel) {
+ if (p_idx == 0)
+ ss->set_radius(p_restore);
+ else
+ ss->set_height(p_restore);
+ return;
+ }
+
+ UndoRedo *ur = SpatialEditor::get_singleton()->get_undo_redo();
+ if (p_idx == 0) {
+ ur->create_action(TTR("Change Cylinder Shape Radius"));
+ ur->add_do_method(ss.ptr(), "set_radius", ss->get_radius());
+ ur->add_undo_method(ss.ptr(), "set_radius", p_restore);
+ } else {
+ ur->create_action(TTR("Change Cylinder Shape Height"));
+ ur->add_do_method(ss.ptr(), "set_height", ss->get_height());
+ ur->add_undo_method(ss.ptr(), "set_height", p_restore);
+ }
+
+ ur->commit_action();
+ }
+
if (Object::cast_to(*s)) {
Ref ss = s;
@@ -2209,6 +2264,67 @@ void CollisionShapeSpatialGizmo::redraw() {
add_handles(handles);
}
+ if (Object::cast_to(*s)) {
+
+ Ref cs = s;
+ float radius = cs->get_radius();
+ float height = cs->get_height();
+
+ Vector points;
+
+ Vector3 d(0, height * 0.5, 0);
+ for (int i = 0; i < 360; i++) {
+
+ float ra = Math::deg2rad((float)i);
+ float rb = Math::deg2rad((float)i + 1);
+ Point2 a = Vector2(Math::sin(ra), Math::cos(ra)) * radius;
+ Point2 b = Vector2(Math::sin(rb), Math::cos(rb)) * radius;
+
+ points.push_back(Vector3(a.x, 0, a.y) + d);
+ points.push_back(Vector3(b.x, 0, b.y) + d);
+
+ points.push_back(Vector3(a.x, 0, a.y) - d);
+ points.push_back(Vector3(b.x, 0, b.y) - d);
+
+ if (i % 90 == 0) {
+
+ points.push_back(Vector3(a.x, 0, a.y) + d);
+ points.push_back(Vector3(a.x, 0, a.y) - d);
+ }
+ }
+
+ add_lines(points, material);
+
+ Vector collision_segments;
+
+ for (int i = 0; i < 64; i++) {
+
+ float ra = i * Math_PI * 2.0 / 64.0;
+ float rb = (i + 1) * Math_PI * 2.0 / 64.0;
+ Point2 a = Vector2(Math::sin(ra), Math::cos(ra)) * radius;
+ Point2 b = Vector2(Math::sin(rb), Math::cos(rb)) * radius;
+
+ collision_segments.push_back(Vector3(a.x, 0, a.y) + d);
+ collision_segments.push_back(Vector3(b.x, 0, b.y) + d);
+
+ collision_segments.push_back(Vector3(a.x, 0, a.y) - d);
+ collision_segments.push_back(Vector3(b.x, 0, b.y) - d);
+
+ if (i % 16 == 0) {
+
+ collision_segments.push_back(Vector3(a.x, 0, a.y) + d);
+ collision_segments.push_back(Vector3(a.x, 0, a.y) - d);
+ }
+ }
+
+ add_collision_segments(collision_segments);
+
+ Vector handles;
+ handles.push_back(Vector3(cs->get_radius(), 0, 0));
+ handles.push_back(Vector3(0, cs->get_height() * 0.5, 0));
+ add_handles(handles);
+ }
+
if (Object::cast_to(*s)) {
Ref ps = s;
diff --git a/modules/bullet/bullet_physics_server.cpp b/modules/bullet/bullet_physics_server.cpp
index 6246a295eca..54431f93f11 100644
--- a/modules/bullet/bullet_physics_server.cpp
+++ b/modules/bullet/bullet_physics_server.cpp
@@ -113,6 +113,10 @@ RID BulletPhysicsServer::shape_create(ShapeType p_shape) {
shape = bulletnew(CapsuleShapeBullet);
} break;
+ case SHAPE_CYLINDER: {
+
+ shape = bulletnew(CylinderShapeBullet);
+ } break;
case SHAPE_CONVEX_POLYGON: {
shape = bulletnew(ConvexPolygonShapeBullet);
diff --git a/modules/bullet/rigid_body_bullet.cpp b/modules/bullet/rigid_body_bullet.cpp
index 2494063c22a..2fc96a77b5f 100644
--- a/modules/bullet/rigid_body_bullet.cpp
+++ b/modules/bullet/rigid_body_bullet.cpp
@@ -226,6 +226,7 @@ void RigidBodyBullet::KinematicUtilities::copyAllOwnerShapes() {
case PhysicsServer::SHAPE_SPHERE:
case PhysicsServer::SHAPE_BOX:
case PhysicsServer::SHAPE_CAPSULE:
+ case PhysicsServer::SHAPE_CYLINDER:
case PhysicsServer::SHAPE_CONVEX_POLYGON:
case PhysicsServer::SHAPE_RAY: {
shapes[i].shape = static_cast(shape_wrapper->shape->create_bt_shape(owner_scale * shape_wrapper->scale, safe_margin));
diff --git a/modules/bullet/shape_bullet.cpp b/modules/bullet/shape_bullet.cpp
index 76d96144652..92e7c2df98b 100644
--- a/modules/bullet/shape_bullet.cpp
+++ b/modules/bullet/shape_bullet.cpp
@@ -113,6 +113,10 @@ btCapsuleShapeZ *ShapeBullet::create_shape_capsule(btScalar radius, btScalar hei
return bulletnew(btCapsuleShapeZ(radius, height));
}
+btCylinderShape *ShapeBullet::create_shape_cylinder(btScalar radius, btScalar height) {
+ return bulletnew(btCylinderShape(btVector3(radius, height / 2.0, radius)));
+}
+
btConvexPointCloudShape *ShapeBullet::create_shape_convex(btAlignedObjectArray &p_vertices, const btVector3 &p_local_scaling) {
return bulletnew(btConvexPointCloudShape(&p_vertices[0], p_vertices.size(), p_local_scaling));
}
@@ -254,6 +258,39 @@ btCollisionShape *CapsuleShapeBullet::create_bt_shape(const btVector3 &p_implici
return prepare(ShapeBullet::create_shape_capsule(radius * p_implicit_scale[0] + p_margin, height * p_implicit_scale[1] + p_margin));
}
+/* Cylinder */
+
+CylinderShapeBullet::CylinderShapeBullet() :
+ ShapeBullet() {}
+
+void CylinderShapeBullet::set_data(const Variant &p_data) {
+ Dictionary d = p_data;
+ ERR_FAIL_COND(!d.has("radius"));
+ ERR_FAIL_COND(!d.has("height"));
+ setup(d["height"], d["radius"]);
+}
+
+Variant CylinderShapeBullet::get_data() const {
+ Dictionary d;
+ d["radius"] = radius;
+ d["height"] = height;
+ return d;
+}
+
+PhysicsServer::ShapeType CylinderShapeBullet::get_type() const {
+ return PhysicsServer::SHAPE_CYLINDER;
+}
+
+void CylinderShapeBullet::setup(real_t p_height, real_t p_radius) {
+ radius = p_radius;
+ height = p_height;
+ notifyShapeChanged();
+}
+
+btCollisionShape *CylinderShapeBullet::create_bt_shape(const btVector3 &p_implicit_scale, real_t p_margin) {
+ return prepare(ShapeBullet::create_shape_cylinder(radius * p_implicit_scale[0] + p_margin, height * p_implicit_scale[1] + p_margin));
+}
+
/* Convex polygon */
ConvexPolygonShapeBullet::ConvexPolygonShapeBullet() :
diff --git a/modules/bullet/shape_bullet.h b/modules/bullet/shape_bullet.h
index abeea0f9cec..16a5ac1fc64 100644
--- a/modules/bullet/shape_bullet.h
+++ b/modules/bullet/shape_bullet.h
@@ -82,6 +82,7 @@ public:
static class btSphereShape *create_shape_sphere(btScalar radius);
static class btBoxShape *create_shape_box(const btVector3 &boxHalfExtents);
static class btCapsuleShapeZ *create_shape_capsule(btScalar radius, btScalar height);
+ static class btCylinderShape *create_shape_cylinder(btScalar radius, btScalar height);
/// IMPORTANT: Remember to delete the shape interface by calling: delete my_shape->getMeshInterface();
static class btConvexPointCloudShape *create_shape_convex(btAlignedObjectArray &p_vertices, const btVector3 &p_local_scaling = btVector3(1, 1, 1));
static class btScaledBvhTriangleMeshShape *create_shape_concave(btBvhTriangleMeshShape *p_mesh_shape, const btVector3 &p_local_scaling = btVector3(1, 1, 1));
@@ -158,6 +159,25 @@ private:
void setup(real_t p_height, real_t p_radius);
};
+class CylinderShapeBullet : public ShapeBullet {
+
+ real_t height;
+ real_t radius;
+
+public:
+ CylinderShapeBullet();
+
+ _FORCE_INLINE_ real_t get_height() { return height; }
+ _FORCE_INLINE_ real_t get_radius() { return radius; }
+ virtual void set_data(const Variant &p_data);
+ virtual Variant get_data() const;
+ virtual PhysicsServer::ShapeType get_type() const;
+ virtual btCollisionShape *create_bt_shape(const btVector3 &p_implicit_scale, real_t p_margin = 0);
+
+private:
+ void setup(real_t p_height, real_t p_radius);
+};
+
class ConvexPolygonShapeBullet : public ShapeBullet {
public:
diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp
index 2f3d4df329e..5452a0346f3 100644
--- a/scene/register_scene_types.cpp
+++ b/scene/register_scene_types.cpp
@@ -128,6 +128,7 @@
#include "scene/resources/concave_polygon_shape_2d.h"
#include "scene/resources/convex_polygon_shape.h"
#include "scene/resources/convex_polygon_shape_2d.h"
+#include "scene/resources/cylinder_shape.h"
#include "scene/resources/default_theme/default_theme.h"
#include "scene/resources/dynamic_font.h"
#include "scene/resources/dynamic_font_stb.h"
@@ -510,6 +511,7 @@ void register_scene_types() {
ClassDB::register_class();
ClassDB::register_class();
ClassDB::register_class();
+ ClassDB::register_class();
ClassDB::register_class();
ClassDB::register_class();
ClassDB::register_class();
diff --git a/scene/resources/cylinder_shape.cpp b/scene/resources/cylinder_shape.cpp
new file mode 100644
index 00000000000..f760462d494
--- /dev/null
+++ b/scene/resources/cylinder_shape.cpp
@@ -0,0 +1,116 @@
+/*************************************************************************/
+/* cylinder_shape.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2018 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 "cylinder_shape.h"
+#include "servers/physics_server.h"
+
+Vector CylinderShape::_gen_debug_mesh_lines() {
+
+ float radius = get_radius();
+ float height = get_height();
+
+ Vector points;
+
+ Vector3 d(0, height * 0.5, 0);
+ for (int i = 0; i < 360; i++) {
+
+ float ra = Math::deg2rad((float)i);
+ float rb = Math::deg2rad((float)i + 1);
+ Point2 a = Vector2(Math::sin(ra), Math::cos(ra)) * radius;
+ Point2 b = Vector2(Math::sin(rb), Math::cos(rb)) * radius;
+
+ points.push_back(Vector3(a.x, 0, a.y) + d);
+ points.push_back(Vector3(b.x, 0, b.y) + d);
+
+ points.push_back(Vector3(a.x, 0, a.y) - d);
+ points.push_back(Vector3(b.x, 0, b.y) - d);
+
+ if (i % 90 == 0) {
+
+ points.push_back(Vector3(a.x, 0, a.y) + d);
+ points.push_back(Vector3(a.x, 0, a.y) - d);
+ }
+ }
+
+ return points;
+}
+
+void CylinderShape::_update_shape() {
+
+ Dictionary d;
+ d["radius"] = radius;
+ d["height"] = height;
+ PhysicsServer::get_singleton()->shape_set_data(get_shape(), d);
+}
+
+void CylinderShape::set_radius(float p_radius) {
+
+ radius = p_radius;
+ _update_shape();
+ notify_change_to_owners();
+ _change_notify("radius");
+}
+
+float CylinderShape::get_radius() const {
+
+ return radius;
+}
+
+void CylinderShape::set_height(float p_height) {
+
+ height = p_height;
+ _update_shape();
+ notify_change_to_owners();
+ _change_notify("height");
+}
+
+float CylinderShape::get_height() const {
+
+ return height;
+}
+
+void CylinderShape::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("set_radius", "radius"), &CylinderShape::set_radius);
+ ClassDB::bind_method(D_METHOD("get_radius"), &CylinderShape::get_radius);
+ ClassDB::bind_method(D_METHOD("set_height", "height"), &CylinderShape::set_height);
+ ClassDB::bind_method(D_METHOD("get_height"), &CylinderShape::get_height);
+
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "radius", PROPERTY_HINT_RANGE, "0.01,4096,0.01"), "set_radius", "get_radius");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "height", PROPERTY_HINT_RANGE, "0.01,4096,0.01"), "set_height", "get_height");
+}
+
+CylinderShape::CylinderShape() :
+ Shape(PhysicsServer::get_singleton()->shape_create(PhysicsServer::SHAPE_CYLINDER)) {
+
+ radius = 1.0;
+ height = 2.0;
+ _update_shape();
+}
diff --git a/scene/resources/cylinder_shape.h b/scene/resources/cylinder_shape.h
new file mode 100644
index 00000000000..f510758e915
--- /dev/null
+++ b/scene/resources/cylinder_shape.h
@@ -0,0 +1,57 @@
+/*************************************************************************/
+/* cylinder_shape.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2018 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 CYLINDER_SHAPE_H
+#define CYLINDER_SHAPE_H
+
+#include "scene/resources/shape.h"
+
+class CylinderShape : public Shape {
+
+ GDCLASS(CylinderShape, Shape);
+ float radius;
+ float height;
+
+protected:
+ static void _bind_methods();
+ virtual void _update_shape();
+
+ virtual Vector _gen_debug_mesh_lines();
+
+public:
+ void set_radius(float p_radius);
+ float get_radius() const;
+ void set_height(float p_height);
+ float get_height() const;
+
+ CylinderShape();
+};
+
+#endif // CYLINDER_SHAPE_H
diff --git a/servers/physics/physics_server_sw.cpp b/servers/physics/physics_server_sw.cpp
index f2dbb635f80..6c25ad43f9e 100644
--- a/servers/physics/physics_server_sw.cpp
+++ b/servers/physics/physics_server_sw.cpp
@@ -65,6 +65,11 @@ RID PhysicsServerSW::shape_create(ShapeType p_shape) {
shape = memnew(CapsuleShapeSW);
} break;
+ case SHAPE_CYLINDER: {
+
+ ERR_EXPLAIN("CylinderShape is not supported in GodotPhysics. Please switch to Bullet in the Project Settings.");
+ ERR_FAIL_V(RID());
+ } break;
case SHAPE_CONVEX_POLYGON: {
shape = memnew(ConvexPolygonShapeSW);
diff --git a/servers/physics_server.cpp b/servers/physics_server.cpp
index 82c4eb2e139..de173491b2f 100644
--- a/servers/physics_server.cpp
+++ b/servers/physics_server.cpp
@@ -664,6 +664,7 @@ void PhysicsServer::_bind_methods() {
BIND_ENUM_CONSTANT(SHAPE_SPHERE);
BIND_ENUM_CONSTANT(SHAPE_BOX);
BIND_ENUM_CONSTANT(SHAPE_CAPSULE);
+ BIND_ENUM_CONSTANT(SHAPE_CYLINDER);
BIND_ENUM_CONSTANT(SHAPE_CONVEX_POLYGON);
BIND_ENUM_CONSTANT(SHAPE_CONCAVE_POLYGON);
BIND_ENUM_CONSTANT(SHAPE_HEIGHTMAP);
diff --git a/servers/physics_server.h b/servers/physics_server.h
index 6712bee8dc9..8ecf17c0e6f 100644
--- a/servers/physics_server.h
+++ b/servers/physics_server.h
@@ -228,6 +228,7 @@ public:
SHAPE_SPHERE, ///< float:"radius"
SHAPE_BOX, ///< vec3:"extents"
SHAPE_CAPSULE, ///< dict( float:"radius", float:"height"):capsule
+ SHAPE_CYLINDER, ///< dict( float:"radius", float:"height"):cylinder
SHAPE_CONVEX_POLYGON, ///< array of planes:"planes"
SHAPE_CONCAVE_POLYGON, ///< vector3 array:"triangles" , or Dictionary with "indices" (int array) and "triangles" (Vector3 array)
SHAPE_HEIGHTMAP, ///< dict( int:"width", int:"depth",float:"cell_size", float_array:"heights"