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"