summaryrefslogtreecommitdiff
path: root/modules
diff options
context:
space:
mode:
Diffstat (limited to 'modules')
-rw-r--r--modules/bullet/SCsub191
-rw-r--r--modules/bullet/SCsub_with_lib33
-rw-r--r--modules/bullet/area_bullet.cpp284
-rw-r--r--modules/bullet/area_bullet.h169
-rw-r--r--modules/bullet/btRayShape.cpp94
-rw-r--r--modules/bullet/btRayShape.h87
-rw-r--r--modules/bullet/bullet_physics_server.cpp1342
-rw-r--r--modules/bullet/bullet_physics_server.h359
-rw-r--r--modules/bullet/bullet_types_converter.cpp94
-rw-r--r--modules/bullet/bullet_types_converter.h57
-rw-r--r--modules/bullet/bullet_utilities.h44
-rw-r--r--modules/bullet/collision_object_bullet.cpp316
-rw-r--r--modules/bullet/collision_object_bullet.h234
-rw-r--r--modules/bullet/cone_twist_joint_bullet.cpp111
-rw-r--r--modules/bullet/cone_twist_joint_bullet.h58
-rw-r--r--modules/bullet/config.py6
-rw-r--r--modules/bullet/constraint_bullet.cpp50
-rw-r--r--modules/bullet/constraint_bullet.h64
-rw-r--r--modules/bullet/generic_6dof_joint_bullet.cpp241
-rw-r--r--modules/bullet/generic_6dof_joint_bullet.h65
-rw-r--r--modules/bullet/godot_collision_configuration.cpp91
-rw-r--r--modules/bullet/godot_collision_configuration.h50
-rw-r--r--modules/bullet/godot_collision_dispatcher.cpp54
-rw-r--r--modules/bullet/godot_collision_dispatcher.h48
-rw-r--r--modules/bullet/godot_motion_state.h96
-rw-r--r--modules/bullet/godot_ray_world_algorithm.cpp104
-rw-r--r--modules/bullet/godot_ray_world_algorithm.h83
-rw-r--r--modules/bullet/godot_result_callbacks.cpp291
-rw-r--r--modules/bullet/godot_result_callbacks.h189
-rw-r--r--modules/bullet/hinge_joint_bullet.cpp163
-rw-r--r--modules/bullet/hinge_joint_bullet.h54
-rw-r--r--modules/bullet/joint_bullet.cpp38
-rw-r--r--modules/bullet/joint_bullet.h49
-rw-r--r--modules/bullet/pin_joint_bullet.cpp112
-rw-r--r--modules/bullet/pin_joint_bullet.h57
-rw-r--r--modules/bullet/register_types.cpp47
-rw-r--r--modules/bullet/register_types.h37
-rw-r--r--modules/bullet/rid_bullet.h50
-rw-r--r--modules/bullet/rigid_body_bullet.cpp1009
-rw-r--r--modules/bullet/rigid_body_bullet.h304
-rw-r--r--modules/bullet/shape_bullet.cpp435
-rw-r--r--modules/bullet/shape_bullet.h225
-rw-r--r--modules/bullet/shape_owner_bullet.cpp32
-rw-r--r--modules/bullet/shape_owner_bullet.h52
-rw-r--r--modules/bullet/slider_joint_bullet.cpp385
-rw-r--r--modules/bullet/slider_joint_bullet.h118
-rw-r--r--modules/bullet/soft_body_bullet.cpp303
-rw-r--r--modules/bullet/soft_body_bullet.h136
-rw-r--r--modules/bullet/space_bullet.cpp1163
-rw-r--r--modules/bullet/space_bullet.h176
-rw-r--r--modules/gdnative/gdnative/basis.cpp2
-rw-r--r--modules/gdnative/gdnative/pool_arrays.cpp262
-rw-r--r--modules/gdnative/gdnative/string.cpp24
-rw-r--r--modules/gdnative/gdnative_api.json423
-rw-r--r--modules/gdnative/include/gdnative/basis.h2
-rw-r--r--modules/gdnative/include/gdnative/pool_arrays.h124
-rw-r--r--modules/gdnative/include/gdnative/string.h1
-rw-r--r--modules/gdnative/include/nativearvr/godot_nativearvr.h1
-rw-r--r--modules/gdnative/nativearvr/arvr_interface_gdnative.cpp12
-rw-r--r--modules/gdscript/gd_editor.cpp4
-rw-r--r--modules/gdscript/gd_functions.cpp22
-rw-r--r--modules/gdscript/gd_functions.h2
-rw-r--r--modules/mono/SCsub145
-rw-r--r--modules/mono/config.py81
-rw-r--r--modules/mono/csharp_script.cpp88
-rw-r--r--modules/mono/csharp_script.h11
-rw-r--r--modules/mono/editor/GodotSharpTools/Build/BuildSystem.cs66
-rw-r--r--modules/mono/editor/GodotSharpTools/GodotSharpTools.sln34
-rw-r--r--modules/mono/editor/bindings_generator.cpp1152
-rw-r--r--modules/mono/editor/bindings_generator.h7
-rw-r--r--modules/mono/editor/godotsharp_builds.cpp78
-rw-r--r--modules/mono/editor/godotsharp_builds.h9
-rw-r--r--modules/mono/editor/godotsharp_editor.cpp72
-rw-r--r--modules/mono/editor/godotsharp_editor.h24
-rw-r--r--modules/mono/editor/mono_bottom_panel.cpp15
-rw-r--r--modules/mono/editor/mono_bottom_panel.h2
-rw-r--r--modules/mono/glue/cs_files/Color.cs1180
-rw-r--r--modules/mono/glue/cs_files/ExportAttribute.cs2
-rw-r--r--modules/mono/glue/cs_files/MarshalUtils.cs2
-rw-r--r--modules/mono/glue/cs_files/Plane.cs418
-rw-r--r--modules/mono/glue/cs_files/Rect3.cs954
-rw-r--r--modules/mono/glue/cs_files/ToolAttribute.cs2
-rw-r--r--modules/mono/godotsharp_dirs.cpp9
-rw-r--r--modules/mono/mono_gc_handle.cpp4
-rw-r--r--modules/mono/mono_gd/gd_mono.cpp15
-rw-r--r--modules/mono/mono_gd/gd_mono_assembly.cpp7
-rw-r--r--modules/mono/mono_gd/gd_mono_marshal.cpp2
-rw-r--r--modules/mono/mono_gd/gd_mono_utils.h4
-rw-r--r--modules/mono/mono_reg_utils.py94
-rw-r--r--modules/mono/utils/path_utils.cpp6
-rw-r--r--modules/opus/audio_stream_opus.cpp6
-rw-r--r--modules/opus/audio_stream_opus.h2
-rw-r--r--modules/visual_script/visual_script_builtin_funcs.cpp40
-rw-r--r--modules/visual_script/visual_script_builtin_funcs.h2
-rw-r--r--modules/vorbis/audio_stream_ogg_vorbis.cpp8
-rw-r--r--modules/vorbis/audio_stream_ogg_vorbis.h2
96 files changed, 13236 insertions, 2036 deletions
diff --git a/modules/bullet/SCsub b/modules/bullet/SCsub
new file mode 100644
index 0000000000..7a37cca130
--- /dev/null
+++ b/modules/bullet/SCsub
@@ -0,0 +1,191 @@
+#!/usr/bin/env python
+
+Import('env')
+
+# build only version 2
+# Bullet 2.87
+bullet_src__2_x = [
+ # BulletCollision
+ "BulletCollision/BroadphaseCollision/btAxisSweep3.cpp"
+ , "BulletCollision/BroadphaseCollision/btBroadphaseProxy.cpp"
+ , "BulletCollision/BroadphaseCollision/btCollisionAlgorithm.cpp"
+ , "BulletCollision/BroadphaseCollision/btDbvt.cpp"
+ , "BulletCollision/BroadphaseCollision/btDbvtBroadphase.cpp"
+ , "BulletCollision/BroadphaseCollision/btDispatcher.cpp"
+ , "BulletCollision/BroadphaseCollision/btOverlappingPairCache.cpp"
+ , "BulletCollision/BroadphaseCollision/btQuantizedBvh.cpp"
+ , "BulletCollision/BroadphaseCollision/btSimpleBroadphase.cpp"
+ , "BulletCollision/CollisionDispatch/btActivatingCollisionAlgorithm.cpp"
+ , "BulletCollision/CollisionDispatch/btBoxBoxCollisionAlgorithm.cpp"
+ , "BulletCollision/CollisionDispatch/btBox2dBox2dCollisionAlgorithm.cpp"
+ , "BulletCollision/CollisionDispatch/btBoxBoxDetector.cpp"
+ , "BulletCollision/CollisionDispatch/btCollisionDispatcher.cpp"
+ , "BulletCollision/CollisionDispatch/btCollisionDispatcherMt.cpp"
+ , "BulletCollision/CollisionDispatch/btCollisionObject.cpp"
+ , "BulletCollision/CollisionDispatch/btCollisionWorld.cpp"
+ , "BulletCollision/CollisionDispatch/btCollisionWorldImporter.cpp"
+ , "BulletCollision/CollisionDispatch/btCompoundCollisionAlgorithm.cpp"
+ , "BulletCollision/CollisionDispatch/btCompoundCompoundCollisionAlgorithm.cpp"
+ , "BulletCollision/CollisionDispatch/btConvexConcaveCollisionAlgorithm.cpp"
+ , "BulletCollision/CollisionDispatch/btConvexConvexAlgorithm.cpp"
+ , "BulletCollision/CollisionDispatch/btConvexPlaneCollisionAlgorithm.cpp"
+ , "BulletCollision/CollisionDispatch/btConvex2dConvex2dAlgorithm.cpp"
+ , "BulletCollision/CollisionDispatch/btDefaultCollisionConfiguration.cpp"
+ , "BulletCollision/CollisionDispatch/btEmptyCollisionAlgorithm.cpp"
+ , "BulletCollision/CollisionDispatch/btGhostObject.cpp"
+ , "BulletCollision/CollisionDispatch/btHashedSimplePairCache.cpp"
+ , "BulletCollision/CollisionDispatch/btInternalEdgeUtility.cpp"
+ , "BulletCollision/CollisionDispatch/btManifoldResult.cpp"
+ , "BulletCollision/CollisionDispatch/btSimulationIslandManager.cpp"
+ , "BulletCollision/CollisionDispatch/btSphereBoxCollisionAlgorithm.cpp"
+ , "BulletCollision/CollisionDispatch/btSphereSphereCollisionAlgorithm.cpp"
+ , "BulletCollision/CollisionDispatch/btSphereTriangleCollisionAlgorithm.cpp"
+ , "BulletCollision/CollisionDispatch/btUnionFind.cpp"
+ , "BulletCollision/CollisionDispatch/SphereTriangleDetector.cpp"
+ , "BulletCollision/CollisionShapes/btBoxShape.cpp"
+ , "BulletCollision/CollisionShapes/btBox2dShape.cpp"
+ , "BulletCollision/CollisionShapes/btBvhTriangleMeshShape.cpp"
+ , "BulletCollision/CollisionShapes/btCapsuleShape.cpp"
+ , "BulletCollision/CollisionShapes/btCollisionShape.cpp"
+ , "BulletCollision/CollisionShapes/btCompoundShape.cpp"
+ , "BulletCollision/CollisionShapes/btConcaveShape.cpp"
+ , "BulletCollision/CollisionShapes/btConeShape.cpp"
+ , "BulletCollision/CollisionShapes/btConvexHullShape.cpp"
+ , "BulletCollision/CollisionShapes/btConvexInternalShape.cpp"
+ , "BulletCollision/CollisionShapes/btConvexPointCloudShape.cpp"
+ , "BulletCollision/CollisionShapes/btConvexPolyhedron.cpp"
+ , "BulletCollision/CollisionShapes/btConvexShape.cpp"
+ , "BulletCollision/CollisionShapes/btConvex2dShape.cpp"
+ , "BulletCollision/CollisionShapes/btConvexTriangleMeshShape.cpp"
+ , "BulletCollision/CollisionShapes/btCylinderShape.cpp"
+ , "BulletCollision/CollisionShapes/btEmptyShape.cpp"
+ , "BulletCollision/CollisionShapes/btHeightfieldTerrainShape.cpp"
+ , "BulletCollision/CollisionShapes/btMinkowskiSumShape.cpp"
+ , "BulletCollision/CollisionShapes/btMultimaterialTriangleMeshShape.cpp"
+ , "BulletCollision/CollisionShapes/btMultiSphereShape.cpp"
+ , "BulletCollision/CollisionShapes/btOptimizedBvh.cpp"
+ , "BulletCollision/CollisionShapes/btPolyhedralConvexShape.cpp"
+ , "BulletCollision/CollisionShapes/btScaledBvhTriangleMeshShape.cpp"
+ , "BulletCollision/CollisionShapes/btShapeHull.cpp"
+ , "BulletCollision/CollisionShapes/btSphereShape.cpp"
+ , "BulletCollision/CollisionShapes/btStaticPlaneShape.cpp"
+ , "BulletCollision/CollisionShapes/btStridingMeshInterface.cpp"
+ , "BulletCollision/CollisionShapes/btTetrahedronShape.cpp"
+ , "BulletCollision/CollisionShapes/btTriangleBuffer.cpp"
+ , "BulletCollision/CollisionShapes/btTriangleCallback.cpp"
+ , "BulletCollision/CollisionShapes/btTriangleIndexVertexArray.cpp"
+ , "BulletCollision/CollisionShapes/btTriangleIndexVertexMaterialArray.cpp"
+ , "BulletCollision/CollisionShapes/btTriangleMesh.cpp"
+ , "BulletCollision/CollisionShapes/btTriangleMeshShape.cpp"
+ , "BulletCollision/CollisionShapes/btUniformScalingShape.cpp"
+ , "BulletCollision/Gimpact/btContactProcessing.cpp"
+ , "BulletCollision/Gimpact/btGenericPoolAllocator.cpp"
+ , "BulletCollision/Gimpact/btGImpactBvh.cpp"
+ , "BulletCollision/Gimpact/btGImpactCollisionAlgorithm.cpp"
+ , "BulletCollision/Gimpact/btGImpactQuantizedBvh.cpp"
+ , "BulletCollision/Gimpact/btGImpactShape.cpp"
+ , "BulletCollision/Gimpact/btTriangleShapeEx.cpp"
+ , "BulletCollision/Gimpact/gim_box_set.cpp"
+ , "BulletCollision/Gimpact/gim_contact.cpp"
+ , "BulletCollision/Gimpact/gim_memory.cpp"
+ , "BulletCollision/Gimpact/gim_tri_collision.cpp"
+ , "BulletCollision/NarrowPhaseCollision/btContinuousConvexCollision.cpp"
+ , "BulletCollision/NarrowPhaseCollision/btConvexCast.cpp"
+ , "BulletCollision/NarrowPhaseCollision/btGjkConvexCast.cpp"
+ , "BulletCollision/NarrowPhaseCollision/btGjkEpa2.cpp"
+ , "BulletCollision/NarrowPhaseCollision/btGjkEpaPenetrationDepthSolver.cpp"
+ , "BulletCollision/NarrowPhaseCollision/btGjkPairDetector.cpp"
+ , "BulletCollision/NarrowPhaseCollision/btMinkowskiPenetrationDepthSolver.cpp"
+ , "BulletCollision/NarrowPhaseCollision/btPersistentManifold.cpp"
+ , "BulletCollision/NarrowPhaseCollision/btRaycastCallback.cpp"
+ , "BulletCollision/NarrowPhaseCollision/btSubSimplexConvexCast.cpp"
+ , "BulletCollision/NarrowPhaseCollision/btVoronoiSimplexSolver.cpp"
+ , "BulletCollision/NarrowPhaseCollision/btPolyhedralContactClipping.cpp"
+
+ # BulletDynamics
+ , "BulletDynamics/Character/btKinematicCharacterController.cpp"
+ , "BulletDynamics/ConstraintSolver/btConeTwistConstraint.cpp"
+ , "BulletDynamics/ConstraintSolver/btContactConstraint.cpp"
+ , "BulletDynamics/ConstraintSolver/btFixedConstraint.cpp"
+ , "BulletDynamics/ConstraintSolver/btGearConstraint.cpp"
+ , "BulletDynamics/ConstraintSolver/btGeneric6DofConstraint.cpp"
+ , "BulletDynamics/ConstraintSolver/btGeneric6DofSpringConstraint.cpp"
+ , "BulletDynamics/ConstraintSolver/btGeneric6DofSpring2Constraint.cpp"
+ , "BulletDynamics/ConstraintSolver/btHinge2Constraint.cpp"
+ , "BulletDynamics/ConstraintSolver/btHingeConstraint.cpp"
+ , "BulletDynamics/ConstraintSolver/btPoint2PointConstraint.cpp"
+ , "BulletDynamics/ConstraintSolver/btSequentialImpulseConstraintSolver.cpp"
+ , "BulletDynamics/ConstraintSolver/btNNCGConstraintSolver.cpp"
+ , "BulletDynamics/ConstraintSolver/btSliderConstraint.cpp"
+ , "BulletDynamics/ConstraintSolver/btSolve2LinearConstraint.cpp"
+ , "BulletDynamics/ConstraintSolver/btTypedConstraint.cpp"
+ , "BulletDynamics/ConstraintSolver/btUniversalConstraint.cpp"
+ , "BulletDynamics/Dynamics/btDiscreteDynamicsWorld.cpp"
+ , "BulletDynamics/Dynamics/btDiscreteDynamicsWorldMt.cpp"
+ , "BulletDynamics/Dynamics/btSimulationIslandManagerMt.cpp"
+ , "BulletDynamics/Dynamics/btRigidBody.cpp"
+ , "BulletDynamics/Dynamics/btSimpleDynamicsWorld.cpp"
+ #, "BulletDynamics/Dynamics/Bullet-C-API.cpp"
+ , "BulletDynamics/Vehicle/btRaycastVehicle.cpp"
+ , "BulletDynamics/Vehicle/btWheelInfo.cpp"
+ , "BulletDynamics/Featherstone/btMultiBody.cpp"
+ , "BulletDynamics/Featherstone/btMultiBodyConstraintSolver.cpp"
+ , "BulletDynamics/Featherstone/btMultiBodyDynamicsWorld.cpp"
+ , "BulletDynamics/Featherstone/btMultiBodyJointLimitConstraint.cpp"
+ , "BulletDynamics/Featherstone/btMultiBodyConstraint.cpp"
+ , "BulletDynamics/Featherstone/btMultiBodyPoint2Point.cpp"
+ , "BulletDynamics/Featherstone/btMultiBodyFixedConstraint.cpp"
+ , "BulletDynamics/Featherstone/btMultiBodySliderConstraint.cpp"
+ , "BulletDynamics/Featherstone/btMultiBodyJointMotor.cpp"
+ , "BulletDynamics/Featherstone/btMultiBodyGearConstraint.cpp"
+ , "BulletDynamics/MLCPSolvers/btDantzigLCP.cpp"
+ , "BulletDynamics/MLCPSolvers/btMLCPSolver.cpp"
+ , "BulletDynamics/MLCPSolvers/btLemkeAlgorithm.cpp"
+
+ # BulletInverseDynamics
+ , "BulletInverseDynamics/IDMath.cpp"
+ , "BulletInverseDynamics/MultiBodyTree.cpp"
+ , "BulletInverseDynamics/details/MultiBodyTreeInitCache.cpp"
+ , "BulletInverseDynamics/details/MultiBodyTreeImpl.cpp"
+
+ # BulletSoftBody
+ , "BulletSoftBody/btSoftBody.cpp"
+ , "BulletSoftBody/btSoftBodyConcaveCollisionAlgorithm.cpp"
+ , "BulletSoftBody/btSoftBodyHelpers.cpp"
+ , "BulletSoftBody/btSoftBodyRigidBodyCollisionConfiguration.cpp"
+ , "BulletSoftBody/btSoftRigidCollisionAlgorithm.cpp"
+ , "BulletSoftBody/btSoftRigidDynamicsWorld.cpp"
+ , "BulletSoftBody/btSoftMultiBodyDynamicsWorld.cpp"
+ , "BulletSoftBody/btSoftSoftCollisionAlgorithm.cpp"
+ , "BulletSoftBody/btDefaultSoftBodySolver.cpp"
+
+ # clew
+ , "clew/clew.c"
+
+ # LinearMath
+ , "LinearMath/btAlignedAllocator.cpp"
+ , "LinearMath/btConvexHull.cpp"
+ , "LinearMath/btConvexHullComputer.cpp"
+ , "LinearMath/btGeometryUtil.cpp"
+ , "LinearMath/btPolarDecomposition.cpp"
+ , "LinearMath/btQuickprof.cpp"
+ , "LinearMath/btSerializer.cpp"
+ , "LinearMath/btSerializer64.cpp"
+ , "LinearMath/btThreads.cpp"
+ , "LinearMath/btVector3.cpp"
+ ]
+
+thirdparty_dir = "#thirdparty/bullet/"
+thirdparty_src = thirdparty_dir + "src/"
+
+bullet_sources = [thirdparty_src + file for file in bullet_src__2_x]
+
+# include headers
+env.Append(CPPPATH=[thirdparty_src])
+
+env.add_source_files(env.modules_sources, bullet_sources)
+
+# Godot source files
+env.add_source_files(env.modules_sources, "*.cpp")
+
+Export('env')
diff --git a/modules/bullet/SCsub_with_lib b/modules/bullet/SCsub_with_lib
new file mode 100644
index 0000000000..b362a686ff
--- /dev/null
+++ b/modules/bullet/SCsub_with_lib
@@ -0,0 +1,33 @@
+#!/usr/bin/env python
+
+Import('env')
+
+thirdparty_dir = "#thirdparty/bullet/"
+thirdparty_lib = thirdparty_dir + "Win64/lib/"
+
+bullet_libs = [
+ "Bullet2FileLoader",
+ "Bullet3Collision",
+ "Bullet3Common",
+ "Bullet3Dynamics",
+ "Bullet3Geometry",
+ "Bullet3OpenCL_clew",
+ "BulletCollision",
+ "BulletDynamics",
+ "BulletInverseDynamics",
+ "BulletSoftBody",
+ "LinearMath"
+ ]
+
+thirdparty_src = thirdparty_dir + "src/"
+# include headers
+env.Append(CPPPATH=[thirdparty_src])
+
+# lib
+env.Append(LIBPATH=[thirdparty_dir + "/Win64/lib/"])
+
+bullet_libs = [file+'.lib' for file in bullet_libs]
+# LIBS doesn't work in windows
+env.Append(LINKFLAGS=bullet_libs)
+
+env.add_source_files(env.modules_sources, "*.cpp")
diff --git a/modules/bullet/area_bullet.cpp b/modules/bullet/area_bullet.cpp
new file mode 100644
index 0000000000..54024b4f90
--- /dev/null
+++ b/modules/bullet/area_bullet.cpp
@@ -0,0 +1,284 @@
+/*************************************************************************/
+/* area_bullet.cpp */
+/* Author: AndreaCatania */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2017 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 "area_bullet.h"
+#include "BulletCollision/CollisionDispatch/btGhostObject.h"
+#include "btBulletCollisionCommon.h"
+#include "bullet_types_converter.h"
+#include "bullet_utilities.h"
+#include "collision_object_bullet.h"
+#include "space_bullet.h"
+
+AreaBullet::AreaBullet()
+ : RigidCollisionObjectBullet(CollisionObjectBullet::TYPE_AREA),
+ monitorable(true),
+ isScratched(false),
+ spOv_mode(PhysicsServer::AREA_SPACE_OVERRIDE_DISABLED),
+ spOv_gravityPoint(false),
+ spOv_gravityPointDistanceScale(0),
+ spOv_gravityPointAttenuation(1),
+ spOv_gravityVec(0, -1, 0),
+ spOv_gravityMag(10),
+ spOv_linearDump(0.1),
+ spOv_angularDump(1),
+ spOv_priority(0) {
+
+ btGhost = bulletnew(btGhostObject);
+ btGhost->setCollisionShape(compoundShape);
+ setupBulletCollisionObject(btGhost);
+ /// Collision objects with a callback still have collision response with dynamic rigid bodies.
+ /// In order to use collision objects as trigger, you have to disable the collision response.
+ set_collision_enabled(false);
+
+ for (int i = 0; i < 5; ++i)
+ call_event_res_ptr[i] = &call_event_res[i];
+}
+
+AreaBullet::~AreaBullet() {
+ remove_all_overlapping_instantly();
+}
+
+void AreaBullet::dispatch_callbacks() {
+ if (!isScratched)
+ return;
+ isScratched = false;
+
+ // Reverse order because I've to remove EXIT objects
+ for (int i = overlappingObjects.size() - 1; 0 <= i; --i) {
+ OverlappingObjectData &otherObj = overlappingObjects[i];
+
+ switch (otherObj.state) {
+ case OVERLAP_STATE_ENTER:
+ otherObj.state = OVERLAP_STATE_INSIDE;
+ call_event(otherObj.object, PhysicsServer::AREA_BODY_ADDED);
+ otherObj.object->on_enter_area(this);
+ break;
+ case OVERLAP_STATE_EXIT:
+ call_event(otherObj.object, PhysicsServer::AREA_BODY_REMOVED);
+ otherObj.object->on_exit_area(this);
+ overlappingObjects.remove(i); // Remove after callback
+ break;
+ }
+ }
+}
+
+void AreaBullet::call_event(CollisionObjectBullet *p_otherObject, PhysicsServer::AreaBodyStatus p_status) {
+
+ InOutEventCallback &event = eventsCallbacks[static_cast<int>(p_otherObject->getType())];
+ Object *areaGodoObject = ObjectDB::get_instance(event.event_callback_id);
+
+ if (!areaGodoObject) {
+ event.event_callback_id = 0;
+ return;
+ }
+
+ call_event_res[0] = p_status;
+ call_event_res[1] = p_otherObject->get_self(); // Other body
+ call_event_res[2] = p_otherObject->get_instance_id(); // instance ID
+ call_event_res[3] = 0; // other_body_shape ID
+ call_event_res[4] = 0; // self_shape ID
+
+ Variant::CallError outResp;
+ areaGodoObject->call(event.event_callback_method, (const Variant **)call_event_res_ptr, 5, outResp);
+}
+
+void AreaBullet::scratch() {
+ if (isScratched)
+ return;
+ isScratched = true;
+}
+
+void AreaBullet::remove_all_overlapping_instantly() {
+ CollisionObjectBullet *supportObject;
+ for (int i = overlappingObjects.size() - 1; 0 <= i; --i) {
+ supportObject = overlappingObjects[i].object;
+ call_event(supportObject, PhysicsServer::AREA_BODY_REMOVED);
+ supportObject->on_exit_area(this);
+ }
+ overlappingObjects.clear();
+}
+
+void AreaBullet::remove_overlapping_instantly(CollisionObjectBullet *p_object) {
+ CollisionObjectBullet *supportObject;
+ for (int i = overlappingObjects.size() - 1; 0 <= i; --i) {
+ supportObject = overlappingObjects[i].object;
+ if (supportObject == p_object) {
+ call_event(supportObject, PhysicsServer::AREA_BODY_REMOVED);
+ supportObject->on_exit_area(this);
+ overlappingObjects.remove(i);
+ break;
+ }
+ }
+}
+
+int AreaBullet::find_overlapping_object(CollisionObjectBullet *p_colObj) {
+ const int size = overlappingObjects.size();
+ for (int i = 0; i < size; ++i) {
+ if (overlappingObjects[i].object == p_colObj) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+void AreaBullet::set_monitorable(bool p_monitorable) {
+ monitorable = p_monitorable;
+}
+
+bool AreaBullet::is_monitoring() const {
+ return get_godot_object_flags() & GOF_IS_MONITORING_AREA;
+}
+
+void AreaBullet::reload_body() {
+ if (space) {
+ space->remove_area(this);
+ space->add_area(this);
+ }
+}
+
+void AreaBullet::set_space(SpaceBullet *p_space) {
+ // Clear the old space if there is one
+ if (space) {
+ isScratched = false;
+
+ // Remove this object form the physics world
+ space->remove_area(this);
+ }
+
+ space = p_space;
+
+ if (space) {
+ space->add_area(this);
+ }
+}
+
+void AreaBullet::on_collision_filters_change() {
+ if (space) {
+ space->reload_collision_filters(this);
+ }
+}
+
+void AreaBullet::add_overlap(CollisionObjectBullet *p_otherObject) {
+ scratch();
+ overlappingObjects.push_back(OverlappingObjectData(p_otherObject, OVERLAP_STATE_ENTER));
+ p_otherObject->notify_new_overlap(this);
+}
+
+void AreaBullet::put_overlap_as_exit(int p_index) {
+ scratch();
+ overlappingObjects[p_index].state = OVERLAP_STATE_EXIT;
+}
+
+void AreaBullet::put_overlap_as_inside(int p_index) {
+ // This check is required to be sure this body was inside
+ if (OVERLAP_STATE_DIRTY == overlappingObjects[p_index].state) {
+ overlappingObjects[p_index].state = OVERLAP_STATE_INSIDE;
+ }
+}
+
+void AreaBullet::set_param(PhysicsServer::AreaParameter p_param, const Variant &p_value) {
+ switch (p_param) {
+ case PhysicsServer::AREA_PARAM_GRAVITY:
+ set_spOv_gravityMag(p_value);
+ break;
+ case PhysicsServer::AREA_PARAM_GRAVITY_VECTOR:
+ set_spOv_gravityVec(p_value);
+ break;
+ case PhysicsServer::AREA_PARAM_LINEAR_DAMP:
+ set_spOv_linearDump(p_value);
+ break;
+ case PhysicsServer::AREA_PARAM_ANGULAR_DAMP:
+ set_spOv_angularDump(p_value);
+ break;
+ case PhysicsServer::AREA_PARAM_PRIORITY:
+ set_spOv_priority(p_value);
+ break;
+ case PhysicsServer::AREA_PARAM_GRAVITY_IS_POINT:
+ set_spOv_gravityPoint(p_value);
+ break;
+ case PhysicsServer::AREA_PARAM_GRAVITY_DISTANCE_SCALE:
+ set_spOv_gravityPointDistanceScale(p_value);
+ break;
+ case PhysicsServer::AREA_PARAM_GRAVITY_POINT_ATTENUATION:
+ set_spOv_gravityPointAttenuation(p_value);
+ break;
+ default:
+ print_line("The Bullet areas dosn't suppot this param: " + itos(p_param));
+ }
+}
+
+Variant AreaBullet::get_param(PhysicsServer::AreaParameter p_param) const {
+ switch (p_param) {
+ case PhysicsServer::AREA_PARAM_GRAVITY:
+ return spOv_gravityMag;
+ case PhysicsServer::AREA_PARAM_GRAVITY_VECTOR:
+ return spOv_gravityVec;
+ case PhysicsServer::AREA_PARAM_LINEAR_DAMP:
+ return spOv_linearDump;
+ case PhysicsServer::AREA_PARAM_ANGULAR_DAMP:
+ return spOv_angularDump;
+ case PhysicsServer::AREA_PARAM_PRIORITY:
+ return spOv_priority;
+ case PhysicsServer::AREA_PARAM_GRAVITY_IS_POINT:
+ return spOv_gravityPoint;
+ case PhysicsServer::AREA_PARAM_GRAVITY_DISTANCE_SCALE:
+ return spOv_gravityPointDistanceScale;
+ case PhysicsServer::AREA_PARAM_GRAVITY_POINT_ATTENUATION:
+ return spOv_gravityPointAttenuation;
+ default:
+ print_line("The Bullet areas dosn't suppot this param: " + itos(p_param));
+ return Variant();
+ }
+}
+
+void AreaBullet::set_event_callback(Type p_callbackObjectType, ObjectID p_id, const StringName &p_method) {
+ InOutEventCallback &ev = eventsCallbacks[static_cast<int>(p_callbackObjectType)];
+ ev.event_callback_id = p_id;
+ ev.event_callback_method = p_method;
+
+ /// Set if monitoring
+ if (eventsCallbacks[0].event_callback_id || eventsCallbacks[1].event_callback_id) {
+ set_godot_object_flags(get_godot_object_flags() | GOF_IS_MONITORING_AREA);
+ } else {
+ set_godot_object_flags(get_godot_object_flags() & (~GOF_IS_MONITORING_AREA));
+ }
+}
+
+bool AreaBullet::has_event_callback(Type p_callbackObjectType) {
+ return eventsCallbacks[static_cast<int>(p_callbackObjectType)].event_callback_id;
+}
+
+void AreaBullet::on_enter_area(AreaBullet *p_area) {
+}
+
+void AreaBullet::on_exit_area(AreaBullet *p_area) {
+ CollisionObjectBullet::on_exit_area(p_area);
+}
diff --git a/modules/bullet/area_bullet.h b/modules/bullet/area_bullet.h
new file mode 100644
index 0000000000..f6e3b7e902
--- /dev/null
+++ b/modules/bullet/area_bullet.h
@@ -0,0 +1,169 @@
+/*************************************************************************/
+/* area_bullet.h */
+/* Author: AndreaCatania */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2017 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 AREABULLET_H
+#define AREABULLET_H
+
+#include "collision_object_bullet.h"
+#include "core/vector.h"
+#include "servers/physics_server.h"
+#include "space_bullet.h"
+
+class btGhostObject;
+
+class AreaBullet : public RigidCollisionObjectBullet {
+ friend void SpaceBullet::check_ghost_overlaps();
+
+public:
+ struct InOutEventCallback {
+ ObjectID event_callback_id;
+ StringName event_callback_method;
+
+ InOutEventCallback()
+ : event_callback_id(0) {}
+ };
+
+ enum OverlapState {
+ OVERLAP_STATE_DIRTY = 0, // Mark processed overlaps
+ OVERLAP_STATE_INSIDE, // Mark old overlap
+ OVERLAP_STATE_ENTER, // Mark just enter overlap
+ OVERLAP_STATE_EXIT // Mark ended overlaps
+ };
+
+ struct OverlappingObjectData {
+ CollisionObjectBullet *object;
+ OverlapState state;
+
+ OverlappingObjectData()
+ : object(NULL), state(OVERLAP_STATE_ENTER) {}
+ OverlappingObjectData(CollisionObjectBullet *p_object, OverlapState p_state)
+ : object(p_object), state(p_state) {}
+ OverlappingObjectData(const OverlappingObjectData &other) {
+ operator=(other);
+ }
+ void operator=(const OverlappingObjectData &other) {
+ object = other.object;
+ state = other.state;
+ }
+ };
+
+private:
+ // These are used by function callEvent. Instead to create this each call I create if one time.
+ Variant call_event_res[5];
+ Variant *call_event_res_ptr[5];
+
+ btGhostObject *btGhost;
+ Vector<OverlappingObjectData> overlappingObjects;
+ bool monitorable;
+
+ PhysicsServer::AreaSpaceOverrideMode spOv_mode;
+ bool spOv_gravityPoint;
+ real_t spOv_gravityPointDistanceScale;
+ real_t spOv_gravityPointAttenuation;
+ Vector3 spOv_gravityVec;
+ real_t spOv_gravityMag;
+ real_t spOv_linearDump;
+ real_t spOv_angularDump;
+ int spOv_priority;
+
+ bool isScratched;
+
+ InOutEventCallback eventsCallbacks[2];
+
+public:
+ AreaBullet();
+ ~AreaBullet();
+
+ _FORCE_INLINE_ btGhostObject *get_bt_ghost() const { return btGhost; }
+ int find_overlapping_object(CollisionObjectBullet *p_colObj);
+
+ void set_monitorable(bool p_monitorable);
+ _FORCE_INLINE_ bool is_monitorable() const { return monitorable; }
+
+ bool is_monitoring() const;
+
+ _FORCE_INLINE_ void set_spOv_mode(PhysicsServer::AreaSpaceOverrideMode p_mode) { spOv_mode = p_mode; }
+ _FORCE_INLINE_ PhysicsServer::AreaSpaceOverrideMode get_spOv_mode() { return spOv_mode; }
+
+ _FORCE_INLINE_ void set_spOv_gravityPoint(bool p_isGP) { spOv_gravityPoint = p_isGP; }
+ _FORCE_INLINE_ bool is_spOv_gravityPoint() { return spOv_gravityPoint; }
+
+ _FORCE_INLINE_ void set_spOv_gravityPointDistanceScale(real_t p_GPDS) { spOv_gravityPointDistanceScale = p_GPDS; }
+ _FORCE_INLINE_ real_t get_spOv_gravityPointDistanceScale() { return spOv_gravityPointDistanceScale; }
+
+ _FORCE_INLINE_ void set_spOv_gravityPointAttenuation(real_t p_GPA) { spOv_gravityPointAttenuation = p_GPA; }
+ _FORCE_INLINE_ real_t get_spOv_gravityPointAttenuation() { return spOv_gravityPointAttenuation; }
+
+ _FORCE_INLINE_ void set_spOv_gravityVec(Vector3 p_vec) { spOv_gravityVec = p_vec; }
+ _FORCE_INLINE_ const Vector3 &get_spOv_gravityVec() const { return spOv_gravityVec; }
+
+ _FORCE_INLINE_ void set_spOv_gravityMag(real_t p_gravityMag) { spOv_gravityMag = p_gravityMag; }
+ _FORCE_INLINE_ real_t get_spOv_gravityMag() { return spOv_gravityMag; }
+
+ _FORCE_INLINE_ void set_spOv_linearDump(real_t p_linearDump) { spOv_linearDump = p_linearDump; }
+ _FORCE_INLINE_ real_t get_spOv_linearDamp() { return spOv_linearDump; }
+
+ _FORCE_INLINE_ void set_spOv_angularDump(real_t p_angularDump) { spOv_angularDump = p_angularDump; }
+ _FORCE_INLINE_ real_t get_spOv_angularDamp() { return spOv_angularDump; }
+
+ _FORCE_INLINE_ void set_spOv_priority(int p_priority) { spOv_priority = p_priority; }
+ _FORCE_INLINE_ int get_spOv_priority() { return spOv_priority; }
+
+ virtual void reload_body();
+ virtual void set_space(SpaceBullet *p_space);
+
+ virtual void dispatch_callbacks();
+ void call_event(CollisionObjectBullet *p_otherObject, PhysicsServer::AreaBodyStatus p_status);
+ void set_on_state_change(ObjectID p_id, const StringName &p_method, const Variant &p_udata = Variant());
+ void scratch();
+
+ void remove_all_overlapping_instantly();
+ // Dispatch the callbacks and removes from overlapping list
+ void remove_overlapping_instantly(CollisionObjectBullet *p_object);
+
+ virtual void on_collision_filters_change();
+ virtual void on_collision_checker_start() {}
+
+ void add_overlap(CollisionObjectBullet *p_otherObject);
+ void put_overlap_as_exit(int p_index);
+ void put_overlap_as_inside(int p_index);
+
+ void set_param(PhysicsServer::AreaParameter p_param, const Variant &p_value);
+ Variant get_param(PhysicsServer::AreaParameter p_param) const;
+
+ void set_event_callback(Type p_callbackObjectType, ObjectID p_id, const StringName &p_method);
+ bool has_event_callback(Type p_callbackObjectType);
+
+ virtual void on_enter_area(AreaBullet *p_area);
+ virtual void on_exit_area(AreaBullet *p_area);
+};
+
+#endif
diff --git a/modules/bullet/btRayShape.cpp b/modules/bullet/btRayShape.cpp
new file mode 100644
index 0000000000..ac95faaac6
--- /dev/null
+++ b/modules/bullet/btRayShape.cpp
@@ -0,0 +1,94 @@
+/*************************************************************************/
+/* btRayShape.h */
+/* Author: AndreaCatania */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2017 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 "btRayShape.h"
+#include "LinearMath/btAabbUtil2.h"
+#include "math/math_funcs.h"
+
+btRayShape::btRayShape(btScalar length)
+ : btConvexInternalShape(),
+ m_shapeAxis(0, 0, 1) {
+ m_shapeType = CUSTOM_CONVEX_SHAPE_TYPE;
+ setLength(length);
+}
+
+btRayShape::~btRayShape() {
+}
+
+void btRayShape::setLength(btScalar p_length) {
+
+ m_length = p_length;
+ reload_cache();
+}
+
+btVector3 btRayShape::localGetSupportingVertex(const btVector3 &vec) const {
+ return localGetSupportingVertexWithoutMargin(vec) + (m_shapeAxis * m_collisionMargin);
+}
+
+btVector3 btRayShape::localGetSupportingVertexWithoutMargin(const btVector3 &vec) const {
+ if (vec.z() > 0)
+ return m_shapeAxis * m_cacheScaledLength;
+ else
+ return btVector3(0, 0, 0);
+}
+
+void btRayShape::batchedUnitVectorGetSupportingVertexWithoutMargin(const btVector3 *vectors, btVector3 *supportVerticesOut, int numVectors) const {
+ for (int i = 0; i < numVectors; ++i) {
+ supportVerticesOut[i] = localGetSupportingVertexWithoutMargin(vectors[i]);
+ }
+}
+
+void btRayShape::getAabb(const btTransform &t, btVector3 &aabbMin, btVector3 &aabbMax) const {
+#define MARGIN_BROADPHASE 0.1
+ btVector3 localAabbMin(0, 0, 0);
+ btVector3 localAabbMax(m_shapeAxis * m_length);
+ btTransformAabb(localAabbMin, localAabbMax, MARGIN_BROADPHASE, t, aabbMin, aabbMax);
+}
+
+void btRayShape::calculateLocalInertia(btScalar mass, btVector3 &inertia) const {
+ inertia.setZero();
+}
+
+int btRayShape::getNumPreferredPenetrationDirections() const {
+ return 0;
+}
+
+void btRayShape::getPreferredPenetrationDirection(int index, btVector3 &penetrationVector) const {
+ penetrationVector.setZero();
+}
+
+void btRayShape::reload_cache() {
+
+ m_cacheScaledLength = m_length * m_localScaling[2] + m_collisionMargin;
+
+ m_cacheSupportPoint.setIdentity();
+ m_cacheSupportPoint.setOrigin(m_shapeAxis * m_cacheScaledLength);
+}
diff --git a/modules/bullet/btRayShape.h b/modules/bullet/btRayShape.h
new file mode 100644
index 0000000000..1b63fb477c
--- /dev/null
+++ b/modules/bullet/btRayShape.h
@@ -0,0 +1,87 @@
+/*************************************************************************/
+/* btRayShape.h */
+/* Author: AndreaCatania */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2017 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. */
+/*************************************************************************/
+
+/// IMPORTANT The class name and filename was created by following Bullet writing rules for an easy (eventually ) porting to bullet
+/// This shape is a custom shape that is not present to Bullet physics engine
+#ifndef BTRAYSHAPE_H
+#define BTRAYSHAPE_H
+
+#include "BulletCollision/CollisionShapes/btConvexInternalShape.h"
+
+/// Ray shape around z axis
+ATTRIBUTE_ALIGNED16(class)
+btRayShape : public btConvexInternalShape {
+
+ btScalar m_length;
+ /// The default axis is the z
+ btVector3 m_shapeAxis;
+
+ btTransform m_cacheSupportPoint;
+ btScalar m_cacheScaledLength;
+
+public:
+ BT_DECLARE_ALIGNED_ALLOCATOR();
+
+ btRayShape(btScalar length);
+ virtual ~btRayShape();
+
+ void setLength(btScalar p_length);
+ btScalar getLength() const { return m_length; }
+
+ const btTransform &getSupportPoint() const { return m_cacheSupportPoint; }
+ const btScalar &getScaledLength() const { return m_cacheScaledLength; }
+
+ virtual btVector3 localGetSupportingVertex(const btVector3 &vec) const;
+#ifndef __SPU__
+ virtual btVector3 localGetSupportingVertexWithoutMargin(const btVector3 &vec) const;
+#endif //#ifndef __SPU__
+
+ virtual void batchedUnitVectorGetSupportingVertexWithoutMargin(const btVector3 *vectors, btVector3 *supportVerticesOut, int numVectors) const;
+
+ ///getAabb returns the axis aligned bounding box in the coordinate frame of the given transform t.
+ virtual void getAabb(const btTransform &t, btVector3 &aabbMin, btVector3 &aabbMax) const;
+
+#ifndef __SPU__
+ virtual void calculateLocalInertia(btScalar mass, btVector3 & inertia) const;
+
+ virtual const char *getName() const {
+ return "RayZ";
+ }
+#endif //__SPU__
+
+ virtual int getNumPreferredPenetrationDirections() const;
+ virtual void getPreferredPenetrationDirection(int index, btVector3 &penetrationVector) const;
+
+private:
+ void reload_cache();
+};
+
+#endif // BTRAYSHAPE_H
diff --git a/modules/bullet/bullet_physics_server.cpp b/modules/bullet/bullet_physics_server.cpp
new file mode 100644
index 0000000000..96875924a5
--- /dev/null
+++ b/modules/bullet/bullet_physics_server.cpp
@@ -0,0 +1,1342 @@
+/*************************************************************************/
+/* bullet_physics_server.cpp */
+/* Author: AndreaCatania */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2017 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 "bullet_physics_server.h"
+#include "LinearMath/btVector3.h"
+#include "bullet_utilities.h"
+#include "class_db.h"
+#include "cone_twist_joint_bullet.h"
+#include "core/error_macros.h"
+#include "core/ustring.h"
+#include "generic_6dof_joint_bullet.h"
+#include "hinge_joint_bullet.h"
+#include "pin_joint_bullet.h"
+#include "shape_bullet.h"
+#include "slider_joint_bullet.h"
+#include <assert.h>
+
+#define CreateThenReturnRID(owner, ridData) \
+ RID rid = owner.make_rid(ridData); \
+ ridData->set_self(rid); \
+ ridData->_set_physics_server(this); \
+ return rid;
+
+// <--------------- Joint creation asserts
+/// Assert the body is assigned to a space
+#define JointAssertSpace(body, bIndex, ret) \
+ if (!body->get_space()) { \
+ ERR_PRINTS("Before create a joint the Body" + String(bIndex) + " must be added to a space!"); \
+ return ret; \
+ }
+
+/// Assert the two bodies of joint are in the same space
+#define JointAssertSameSpace(bodyA, bodyB, ret) \
+ if (bodyA->get_space() != bodyB->get_space()) { \
+ ERR_PRINT("In order to create a joint the Body_A and Body_B must be in the same space!"); \
+ return RID(); \
+ }
+
+#define AddJointToSpace(body, joint, disableCollisionsBetweenLinkedBodies) \
+ body->get_space()->add_constraint(joint, disableCollisionsBetweenLinkedBodies);
+// <--------------- Joint creation asserts
+
+btEmptyShape *BulletPhysicsServer::emptyShape(ShapeBullet::create_shape_empty());
+
+btEmptyShape *BulletPhysicsServer::get_empty_shape() {
+ return emptyShape;
+}
+
+void BulletPhysicsServer::_bind_methods() {
+ //ClassDB::bind_method(D_METHOD("DoTest"), &BulletPhysicsServer::DoTest);
+}
+
+BulletPhysicsServer::BulletPhysicsServer()
+ : PhysicsServer(),
+ active(true),
+ active_spaces_count(0) {}
+
+BulletPhysicsServer::~BulletPhysicsServer() {}
+
+RID BulletPhysicsServer::shape_create(ShapeType p_shape) {
+ ShapeBullet *shape = NULL;
+
+ switch (p_shape) {
+ case SHAPE_PLANE: {
+
+ shape = bulletnew(PlaneShapeBullet);
+ } break;
+ case SHAPE_SPHERE: {
+
+ shape = bulletnew(SphereShapeBullet);
+ } break;
+ case SHAPE_BOX: {
+
+ shape = bulletnew(BoxShapeBullet);
+ } break;
+ case SHAPE_CAPSULE: {
+
+ shape = bulletnew(CapsuleShapeBullet);
+ } break;
+ case SHAPE_CONVEX_POLYGON: {
+
+ shape = bulletnew(ConvexPolygonShapeBullet);
+ } break;
+ case SHAPE_CONCAVE_POLYGON: {
+
+ shape = bulletnew(ConcavePolygonShapeBullet);
+ } break;
+ case SHAPE_HEIGHTMAP: {
+
+ shape = bulletnew(HeightMapShapeBullet);
+ } break;
+ case SHAPE_RAY: {
+ shape = bulletnew(RayShapeBullet);
+ } break;
+ case SHAPE_CUSTOM:
+ defaul:
+ ERR_FAIL_V(RID());
+ break;
+ }
+
+ CreateThenReturnRID(shape_owner, shape)
+}
+
+void BulletPhysicsServer::shape_set_data(RID p_shape, const Variant &p_data) {
+ ShapeBullet *shape = shape_owner.get(p_shape);
+ ERR_FAIL_COND(!shape);
+ shape->set_data(p_data);
+}
+
+void BulletPhysicsServer::shape_set_custom_solver_bias(RID p_shape, real_t p_bias) {
+ //WARN_PRINT("Bias not supported by Bullet physics engine");
+}
+
+PhysicsServer::ShapeType BulletPhysicsServer::shape_get_type(RID p_shape) const {
+ ShapeBullet *shape = shape_owner.get(p_shape);
+ ERR_FAIL_COND_V(!shape, PhysicsServer::SHAPE_CUSTOM);
+ return shape->get_type();
+}
+
+Variant BulletPhysicsServer::shape_get_data(RID p_shape) const {
+ ShapeBullet *shape = shape_owner.get(p_shape);
+ ERR_FAIL_COND_V(!shape, Variant());
+ return shape->get_data();
+}
+
+real_t BulletPhysicsServer::shape_get_custom_solver_bias(RID p_shape) const {
+ //WARN_PRINT("Bias not supported by Bullet physics engine");
+ return 0.;
+}
+
+RID BulletPhysicsServer::space_create() {
+ SpaceBullet *space = bulletnew(SpaceBullet(false));
+ CreateThenReturnRID(space_owner, space);
+}
+
+void BulletPhysicsServer::space_set_active(RID p_space, bool p_active) {
+
+ SpaceBullet *space = space_owner.get(p_space);
+ ERR_FAIL_COND(!space);
+
+ if (space_is_active(p_space) == p_active) {
+ return;
+ }
+
+ if (p_active) {
+ ++active_spaces_count;
+ active_spaces.push_back(space);
+ } else {
+ --active_spaces_count;
+ active_spaces.erase(space);
+ }
+}
+
+bool BulletPhysicsServer::space_is_active(RID p_space) const {
+ SpaceBullet *space = space_owner.get(p_space);
+ ERR_FAIL_COND_V(!space, false);
+
+ return -1 != active_spaces.find(space);
+}
+
+void BulletPhysicsServer::space_set_param(RID p_space, SpaceParameter p_param, real_t p_value) {
+ SpaceBullet *space = space_owner.get(p_space);
+ ERR_FAIL_COND(!space);
+ space->set_param(p_param, p_value);
+}
+
+real_t BulletPhysicsServer::space_get_param(RID p_space, SpaceParameter p_param) const {
+ SpaceBullet *space = space_owner.get(p_space);
+ ERR_FAIL_COND_V(!space, 0);
+ return space->get_param(p_param);
+}
+
+PhysicsDirectSpaceState *BulletPhysicsServer::space_get_direct_state(RID p_space) {
+ SpaceBullet *space = space_owner.get(p_space);
+ ERR_FAIL_COND_V(!space, NULL);
+
+ return space->get_direct_state();
+}
+
+void BulletPhysicsServer::space_set_debug_contacts(RID p_space, int p_max_contacts) {
+ SpaceBullet *space = space_owner.get(p_space);
+ ERR_FAIL_COND(!space);
+
+ space->set_debug_contacts(p_max_contacts);
+}
+
+Vector<Vector3> BulletPhysicsServer::space_get_contacts(RID p_space) const {
+ SpaceBullet *space = space_owner.get(p_space);
+ ERR_FAIL_COND_V(!space, Vector<Vector3>());
+
+ return space->get_debug_contacts();
+}
+
+int BulletPhysicsServer::space_get_contact_count(RID p_space) const {
+ SpaceBullet *space = space_owner.get(p_space);
+ ERR_FAIL_COND_V(!space, 0);
+
+ return space->get_debug_contact_count();
+}
+
+RID BulletPhysicsServer::area_create() {
+ AreaBullet *area = bulletnew(AreaBullet);
+ area->set_collision_layer(1);
+ area->set_collision_mask(1);
+ CreateThenReturnRID(area_owner, area)
+}
+
+void BulletPhysicsServer::area_set_space(RID p_area, RID p_space) {
+ AreaBullet *area = area_owner.get(p_area);
+ ERR_FAIL_COND(!area);
+ SpaceBullet *space = NULL;
+ if (p_space.is_valid()) {
+ space = space_owner.get(p_space);
+ ERR_FAIL_COND(!space);
+ }
+ area->set_space(space);
+}
+
+RID BulletPhysicsServer::area_get_space(RID p_area) const {
+ AreaBullet *area = area_owner.get(p_area);
+ return area->get_space()->get_self();
+}
+
+void BulletPhysicsServer::area_set_space_override_mode(RID p_area, AreaSpaceOverrideMode p_mode) {
+ AreaBullet *area = area_owner.get(p_area);
+ ERR_FAIL_COND(!area)
+
+ area->set_spOv_mode(p_mode);
+}
+
+PhysicsServer::AreaSpaceOverrideMode BulletPhysicsServer::area_get_space_override_mode(RID p_area) const {
+ AreaBullet *area = area_owner.get(p_area);
+ ERR_FAIL_COND_V(!area, PhysicsServer::AREA_SPACE_OVERRIDE_DISABLED);
+
+ return area->get_spOv_mode();
+}
+
+void BulletPhysicsServer::area_add_shape(RID p_area, RID p_shape, const Transform &p_transform) {
+ AreaBullet *area = area_owner.get(p_area);
+ ERR_FAIL_COND(!area);
+
+ ShapeBullet *shape = shape_owner.get(p_shape);
+ ERR_FAIL_COND(!shape);
+
+ area->add_shape(shape, p_transform);
+}
+
+void BulletPhysicsServer::area_set_shape(RID p_area, int p_shape_idx, RID p_shape) {
+ AreaBullet *area = area_owner.get(p_area);
+ ERR_FAIL_COND(!area);
+
+ ShapeBullet *shape = shape_owner.get(p_shape);
+ ERR_FAIL_COND(!shape);
+
+ area->set_shape(p_shape_idx, shape);
+}
+
+void BulletPhysicsServer::area_set_shape_transform(RID p_area, int p_shape_idx, const Transform &p_transform) {
+ AreaBullet *area = area_owner.get(p_area);
+ ERR_FAIL_COND(!area);
+
+ area->set_shape_transform(p_shape_idx, p_transform);
+}
+
+int BulletPhysicsServer::area_get_shape_count(RID p_area) const {
+ AreaBullet *area = area_owner.get(p_area);
+ ERR_FAIL_COND_V(!area, 0);
+
+ return area->get_shape_count();
+}
+
+RID BulletPhysicsServer::area_get_shape(RID p_area, int p_shape_idx) const {
+ AreaBullet *area = area_owner.get(p_area);
+ ERR_FAIL_COND_V(!area, RID());
+
+ return area->get_shape(p_shape_idx)->get_self();
+}
+
+Transform BulletPhysicsServer::area_get_shape_transform(RID p_area, int p_shape_idx) const {
+ AreaBullet *area = area_owner.get(p_area);
+ ERR_FAIL_COND_V(!area, Transform());
+
+ return area->get_shape_transform(p_shape_idx);
+}
+
+void BulletPhysicsServer::area_remove_shape(RID p_area, int p_shape_idx) {
+ AreaBullet *area = area_owner.get(p_area);
+ ERR_FAIL_COND(!area);
+ return area->remove_shape(p_shape_idx);
+}
+
+void BulletPhysicsServer::area_clear_shapes(RID p_area) {
+ AreaBullet *area = area_owner.get(p_area);
+ ERR_FAIL_COND(!area);
+
+ for (int i = area->get_shape_count(); 0 < i; --i)
+ area->remove_shape(0);
+}
+
+void BulletPhysicsServer::area_set_shape_disabled(RID p_area, int p_shape_idx, bool p_disabled) {
+ AreaBullet *area = area_owner.get(p_area);
+ ERR_FAIL_COND(!area);
+
+ area->set_shape_disabled(p_shape_idx, p_disabled);
+}
+
+void BulletPhysicsServer::area_attach_object_instance_id(RID p_area, ObjectID p_ID) {
+ if (space_owner.owns(p_area)) {
+ return;
+ }
+ AreaBullet *area = area_owner.get(p_area);
+ ERR_FAIL_COND(!area);
+ area->set_instance_id(p_ID);
+}
+
+ObjectID BulletPhysicsServer::area_get_object_instance_id(RID p_area) const {
+ if (space_owner.owns(p_area)) {
+ return 0;
+ }
+ AreaBullet *area = area_owner.get(p_area);
+ ERR_FAIL_COND_V(!area, ObjectID());
+ return area->get_instance_id();
+}
+
+void BulletPhysicsServer::area_set_param(RID p_area, AreaParameter p_param, const Variant &p_value) {
+ if (space_owner.owns(p_area)) {
+ SpaceBullet *space = space_owner.get(p_area);
+ if (space) {
+ space->set_param(p_param, p_value);
+ }
+ } else {
+
+ AreaBullet *area = area_owner.get(p_area);
+ ERR_FAIL_COND(!area);
+
+ area->set_param(p_param, p_value);
+ }
+}
+
+Variant BulletPhysicsServer::area_get_param(RID p_area, AreaParameter p_param) const {
+ if (space_owner.owns(p_area)) {
+ SpaceBullet *space = space_owner.get(p_area);
+ return space->get_param(p_param);
+ } else {
+ AreaBullet *area = area_owner.get(p_area);
+ ERR_FAIL_COND_V(!area, Variant());
+
+ return area->get_param(p_param);
+ }
+}
+
+void BulletPhysicsServer::area_set_transform(RID p_area, const Transform &p_transform) {
+ AreaBullet *area = area_owner.get(p_area);
+ ERR_FAIL_COND(!area);
+ area->set_transform(p_transform);
+}
+
+Transform BulletPhysicsServer::area_get_transform(RID p_area) const {
+ AreaBullet *area = area_owner.get(p_area);
+ ERR_FAIL_COND_V(!area, Transform());
+ return area->get_transform();
+}
+
+void BulletPhysicsServer::area_set_collision_mask(RID p_area, uint32_t p_mask) {
+ AreaBullet *area = area_owner.get(p_area);
+ ERR_FAIL_COND(!area);
+ area->set_collision_mask(p_mask);
+}
+
+void BulletPhysicsServer::area_set_collision_layer(RID p_area, uint32_t p_layer) {
+ AreaBullet *area = area_owner.get(p_area);
+ ERR_FAIL_COND(!area);
+ area->set_collision_layer(p_layer);
+}
+
+void BulletPhysicsServer::area_set_monitorable(RID p_area, bool p_monitorable) {
+ AreaBullet *area = area_owner.get(p_area);
+ ERR_FAIL_COND(!area);
+
+ area->set_monitorable(p_monitorable);
+}
+
+void BulletPhysicsServer::area_set_monitor_callback(RID p_area, Object *p_receiver, const StringName &p_method) {
+ AreaBullet *area = area_owner.get(p_area);
+ ERR_FAIL_COND(!area);
+
+ area->set_event_callback(CollisionObjectBullet::TYPE_RIGID_BODY, p_receiver ? p_receiver->get_instance_id() : 0, p_method);
+}
+
+void BulletPhysicsServer::area_set_area_monitor_callback(RID p_area, Object *p_receiver, const StringName &p_method) {
+ AreaBullet *area = area_owner.get(p_area);
+ ERR_FAIL_COND(!area);
+
+ area->set_event_callback(CollisionObjectBullet::TYPE_AREA, p_receiver ? p_receiver->get_instance_id() : 0, p_method);
+}
+
+void BulletPhysicsServer::area_set_ray_pickable(RID p_area, bool p_enable) {
+ AreaBullet *area = area_owner.get(p_area);
+ ERR_FAIL_COND(!area);
+ area->set_ray_pickable(p_enable);
+}
+
+bool BulletPhysicsServer::area_is_ray_pickable(RID p_area) const {
+ AreaBullet *area = area_owner.get(p_area);
+ ERR_FAIL_COND_V(!area, false);
+ return area->is_ray_pickable();
+}
+
+RID BulletPhysicsServer::body_create(BodyMode p_mode, bool p_init_sleeping) {
+ RigidBodyBullet *body = bulletnew(RigidBodyBullet);
+ body->set_mode(p_mode);
+ body->set_collision_layer(1);
+ body->set_collision_mask(1);
+ if (p_init_sleeping)
+ body->set_state(BODY_STATE_SLEEPING, p_init_sleeping);
+ CreateThenReturnRID(rigid_body_owner, body);
+}
+
+void BulletPhysicsServer::body_set_space(RID p_body, RID p_space) {
+ RigidBodyBullet *body = rigid_body_owner.get(p_body);
+ ERR_FAIL_COND(!body);
+ SpaceBullet *space = NULL;
+
+ if (p_space.is_valid()) {
+ space = space_owner.get(p_space);
+ ERR_FAIL_COND(!space);
+ }
+
+ if (body->get_space() == space)
+ return; //pointles
+
+ body->set_space(space);
+}
+
+RID BulletPhysicsServer::body_get_space(RID p_body) const {
+ RigidBodyBullet *body = rigid_body_owner.get(p_body);
+ ERR_FAIL_COND_V(!body, RID());
+
+ SpaceBullet *space = body->get_space();
+ if (!space)
+ return RID();
+ return space->get_self();
+}
+
+void BulletPhysicsServer::body_set_mode(RID p_body, PhysicsServer::BodyMode p_mode) {
+ RigidBodyBullet *body = rigid_body_owner.get(p_body);
+ ERR_FAIL_COND(!body);
+ body->set_mode(p_mode);
+}
+
+PhysicsServer::BodyMode BulletPhysicsServer::body_get_mode(RID p_body) const {
+ RigidBodyBullet *body = rigid_body_owner.get(p_body);
+ ERR_FAIL_COND_V(!body, BODY_MODE_STATIC);
+ return body->get_mode();
+}
+
+void BulletPhysicsServer::body_add_shape(RID p_body, RID p_shape, const Transform &p_transform) {
+
+ RigidBodyBullet *body = rigid_body_owner.get(p_body);
+ ERR_FAIL_COND(!body);
+
+ ShapeBullet *shape = shape_owner.get(p_shape);
+ ERR_FAIL_COND(!shape);
+
+ body->add_shape(shape, p_transform);
+}
+
+void BulletPhysicsServer::body_set_shape(RID p_body, int p_shape_idx, RID p_shape) {
+ RigidBodyBullet *body = rigid_body_owner.get(p_body);
+ ERR_FAIL_COND(!body);
+
+ ShapeBullet *shape = shape_owner.get(p_shape);
+ ERR_FAIL_COND(!shape);
+
+ body->set_shape(p_shape_idx, shape);
+}
+
+void BulletPhysicsServer::body_set_shape_transform(RID p_body, int p_shape_idx, const Transform &p_transform) {
+ RigidBodyBullet *body = rigid_body_owner.get(p_body);
+ ERR_FAIL_COND(!body);
+
+ body->set_shape_transform(p_shape_idx, p_transform);
+}
+
+int BulletPhysicsServer::body_get_shape_count(RID p_body) const {
+ RigidBodyBullet *body = rigid_body_owner.get(p_body);
+ ERR_FAIL_COND_V(!body, 0);
+ return body->get_shape_count();
+}
+
+RID BulletPhysicsServer::body_get_shape(RID p_body, int p_shape_idx) const {
+ RigidBodyBullet *body = rigid_body_owner.get(p_body);
+ ERR_FAIL_COND_V(!body, RID());
+
+ ShapeBullet *shape = body->get_shape(p_shape_idx);
+ ERR_FAIL_COND_V(!shape, RID());
+
+ return shape->get_self();
+}
+
+Transform BulletPhysicsServer::body_get_shape_transform(RID p_body, int p_shape_idx) const {
+ RigidBodyBullet *body = rigid_body_owner.get(p_body);
+ ERR_FAIL_COND_V(!body, Transform());
+ return body->get_shape_transform(p_shape_idx);
+}
+
+void BulletPhysicsServer::body_set_shape_disabled(RID p_body, int p_shape_idx, bool p_disabled) {
+ RigidBodyBullet *body = rigid_body_owner.get(p_body);
+ ERR_FAIL_COND(!body);
+
+ body->set_shape_disabled(p_shape_idx, p_disabled);
+}
+
+void BulletPhysicsServer::body_remove_shape(RID p_body, int p_shape_idx) {
+ RigidBodyBullet *body = rigid_body_owner.get(p_body);
+ ERR_FAIL_COND(!body);
+
+ body->remove_shape(p_shape_idx);
+}
+
+void BulletPhysicsServer::body_clear_shapes(RID p_body) {
+ RigidBodyBullet *body = rigid_body_owner.get(p_body);
+ ERR_FAIL_COND(!body);
+
+ body->remove_all_shapes();
+}
+
+void BulletPhysicsServer::body_attach_object_instance_id(RID p_body, uint32_t p_ID) {
+ CollisionObjectBullet *body = get_collisin_object(p_body);
+ if (!body) {
+ body = soft_body_owner.get(p_body);
+ }
+ ERR_FAIL_COND(!body);
+
+ body->set_instance_id(p_ID);
+}
+
+uint32_t BulletPhysicsServer::body_get_object_instance_id(RID p_body) const {
+ CollisionObjectBullet *body = get_collisin_object(p_body);
+ ERR_FAIL_COND_V(!body, 0);
+
+ return body->get_instance_id();
+}
+
+void BulletPhysicsServer::body_set_enable_continuous_collision_detection(RID p_body, bool p_enable) {
+ RigidBodyBullet *body = rigid_body_owner.get(p_body);
+ ERR_FAIL_COND(!body);
+
+ body->set_continuous_collision_detection(p_enable);
+}
+
+bool BulletPhysicsServer::body_is_continuous_collision_detection_enabled(RID p_body) const {
+ RigidBodyBullet *body = rigid_body_owner.get(p_body);
+ ERR_FAIL_COND_V(!body, false);
+
+ return body->is_continuous_collision_detection_enabled();
+}
+
+void BulletPhysicsServer::body_set_collision_layer(RID p_body, uint32_t p_layer) {
+ RigidBodyBullet *body = rigid_body_owner.get(p_body);
+ ERR_FAIL_COND(!body);
+
+ body->set_collision_layer(p_layer);
+}
+
+uint32_t BulletPhysicsServer::body_get_collision_layer(RID p_body) const {
+ const RigidBodyBullet *body = rigid_body_owner.get(p_body);
+ ERR_FAIL_COND_V(!body, 0);
+
+ return body->get_collision_layer();
+}
+
+void BulletPhysicsServer::body_set_collision_mask(RID p_body, uint32_t p_mask) {
+ RigidBodyBullet *body = rigid_body_owner.get(p_body);
+ ERR_FAIL_COND(!body);
+
+ body->set_collision_mask(p_mask);
+}
+
+uint32_t BulletPhysicsServer::body_get_collision_mask(RID p_body) const {
+ RigidBodyBullet *body = rigid_body_owner.get(p_body);
+ ERR_FAIL_COND_V(!body, 0);
+
+ return body->get_collision_mask();
+}
+
+void BulletPhysicsServer::body_set_user_flags(RID p_body, uint32_t p_flags) {
+ WARN_PRINT("This function si not currently supported by bullet and Godot");
+}
+
+uint32_t BulletPhysicsServer::body_get_user_flags(RID p_body) const {
+ WARN_PRINT("This function si not currently supported by bullet and Godot");
+ return 0;
+}
+
+void BulletPhysicsServer::body_set_param(RID p_body, BodyParameter p_param, float p_value) {
+ RigidBodyBullet *body = rigid_body_owner.get(p_body);
+ ERR_FAIL_COND(!body);
+
+ body->set_param(p_param, p_value);
+}
+
+float BulletPhysicsServer::body_get_param(RID p_body, BodyParameter p_param) const {
+ RigidBodyBullet *body = rigid_body_owner.get(p_body);
+ ERR_FAIL_COND_V(!body, 0);
+
+ return body->get_param(p_param);
+}
+
+void BulletPhysicsServer::body_set_state(RID p_body, BodyState p_state, const Variant &p_variant) {
+ RigidBodyBullet *body = rigid_body_owner.get(p_body);
+ ERR_FAIL_COND(!body);
+
+ body->set_state(p_state, p_variant);
+}
+
+Variant BulletPhysicsServer::body_get_state(RID p_body, BodyState p_state) const {
+ RigidBodyBullet *body = rigid_body_owner.get(p_body);
+ ERR_FAIL_COND_V(!body, Variant());
+
+ return body->get_state(p_state);
+}
+
+void BulletPhysicsServer::body_set_applied_force(RID p_body, const Vector3 &p_force) {
+ RigidBodyBullet *body = rigid_body_owner.get(p_body);
+ ERR_FAIL_COND(!body);
+
+ body->set_applied_force(p_force);
+}
+
+Vector3 BulletPhysicsServer::body_get_applied_force(RID p_body) const {
+ RigidBodyBullet *body = rigid_body_owner.get(p_body);
+ ERR_FAIL_COND_V(!body, Vector3());
+ return body->get_applied_force();
+}
+
+void BulletPhysicsServer::body_set_applied_torque(RID p_body, const Vector3 &p_torque) {
+ RigidBodyBullet *body = rigid_body_owner.get(p_body);
+ ERR_FAIL_COND(!body);
+
+ body->set_applied_torque(p_torque);
+}
+
+Vector3 BulletPhysicsServer::body_get_applied_torque(RID p_body) const {
+ RigidBodyBullet *body = rigid_body_owner.get(p_body);
+ ERR_FAIL_COND_V(!body, Vector3());
+
+ return body->get_applied_torque();
+}
+
+void BulletPhysicsServer::body_apply_impulse(RID p_body, const Vector3 &p_pos, const Vector3 &p_impulse) {
+ RigidBodyBullet *body = rigid_body_owner.get(p_body);
+ ERR_FAIL_COND(!body);
+
+ body->apply_impulse(p_pos, p_impulse);
+}
+
+void BulletPhysicsServer::body_apply_torque_impulse(RID p_body, const Vector3 &p_impulse) {
+ RigidBodyBullet *body = rigid_body_owner.get(p_body);
+ ERR_FAIL_COND(!body);
+
+ body->apply_torque_impulse(p_impulse);
+}
+
+void BulletPhysicsServer::body_set_axis_velocity(RID p_body, const Vector3 &p_axis_velocity) {
+ RigidBodyBullet *body = rigid_body_owner.get(p_body);
+ ERR_FAIL_COND(!body);
+
+ Vector3 v = body->get_linear_velocity();
+ Vector3 axis = p_axis_velocity.normalized();
+ v -= axis * axis.dot(v);
+ v += p_axis_velocity;
+ body->set_linear_velocity(v);
+}
+
+void BulletPhysicsServer::body_set_axis_lock(RID p_body, PhysicsServer::BodyAxisLock p_lock) {
+ RigidBodyBullet *body = rigid_body_owner.get(p_body);
+ ERR_FAIL_COND(!body);
+ body->set_axis_lock(p_lock);
+}
+
+PhysicsServer::BodyAxisLock BulletPhysicsServer::body_get_axis_lock(RID p_body) const {
+ const RigidBodyBullet *body = rigid_body_owner.get(p_body);
+ ERR_FAIL_COND_V(!body, BODY_AXIS_LOCK_DISABLED);
+ return body->get_axis_lock();
+}
+
+void BulletPhysicsServer::body_add_collision_exception(RID p_body, RID p_body_b) {
+ RigidBodyBullet *body = rigid_body_owner.get(p_body);
+ ERR_FAIL_COND(!body);
+
+ RigidBodyBullet *other_body = rigid_body_owner.get(p_body_b);
+ ERR_FAIL_COND(!other_body);
+
+ body->add_collision_exception(other_body);
+}
+
+void BulletPhysicsServer::body_remove_collision_exception(RID p_body, RID p_body_b) {
+ RigidBodyBullet *body = rigid_body_owner.get(p_body);
+ ERR_FAIL_COND(!body);
+
+ RigidBodyBullet *other_body = rigid_body_owner.get(p_body_b);
+ ERR_FAIL_COND(!other_body);
+
+ body->remove_collision_exception(other_body);
+}
+
+void BulletPhysicsServer::body_get_collision_exceptions(RID p_body, List<RID> *p_exceptions) {
+ RigidBodyBullet *body = rigid_body_owner.get(p_body);
+ ERR_FAIL_COND(!body);
+ for (int i = 0; i < body->get_exceptions().size(); i++) {
+ p_exceptions->push_back(body->get_exceptions()[i]);
+ }
+}
+
+void BulletPhysicsServer::body_set_max_contacts_reported(RID p_body, int p_contacts) {
+ RigidBodyBullet *body = rigid_body_owner.get(p_body);
+ ERR_FAIL_COND(!body);
+
+ body->set_max_collisions_detection(p_contacts);
+}
+
+int BulletPhysicsServer::body_get_max_contacts_reported(RID p_body) const {
+ RigidBodyBullet *body = rigid_body_owner.get(p_body);
+ ERR_FAIL_COND_V(!body, 0);
+
+ return body->get_max_collisions_detection();
+}
+
+void BulletPhysicsServer::body_set_contacts_reported_depth_threshold(RID p_body, float p_treshold) {
+ WARN_PRINT("Not supported by bullet and even Godot");
+}
+
+float BulletPhysicsServer::body_get_contacts_reported_depth_threshold(RID p_body) const {
+ WARN_PRINT("Not supported by bullet and even Godot");
+ return 0.;
+}
+
+void BulletPhysicsServer::body_set_omit_force_integration(RID p_body, bool p_omit) {
+ WARN_PRINT("Not supported by bullet");
+}
+
+bool BulletPhysicsServer::body_is_omitting_force_integration(RID p_body) const {
+ WARN_PRINT("Not supported by bullet");
+ return false;
+}
+
+void BulletPhysicsServer::body_set_force_integration_callback(RID p_body, Object *p_receiver, const StringName &p_method, const Variant &p_udata) {
+ RigidBodyBullet *body = rigid_body_owner.get(p_body);
+ ERR_FAIL_COND(!body);
+ body->set_force_integration_callback(p_receiver->get_instance_id(), p_method, p_udata);
+}
+
+void BulletPhysicsServer::body_set_ray_pickable(RID p_body, bool p_enable) {
+ RigidBodyBullet *body = rigid_body_owner.get(p_body);
+ ERR_FAIL_COND(!body);
+ body->set_ray_pickable(p_enable);
+}
+
+bool BulletPhysicsServer::body_is_ray_pickable(RID p_body) const {
+ RigidBodyBullet *body = rigid_body_owner.get(p_body);
+ ERR_FAIL_COND_V(!body, false);
+ return body->is_ray_pickable();
+}
+
+PhysicsDirectBodyState *BulletPhysicsServer::body_get_direct_state(RID p_body) {
+ RigidBodyBullet *body = rigid_body_owner.get(p_body);
+ ERR_FAIL_COND_V(!body, NULL);
+ return BulletPhysicsDirectBodyState::get_singleton(body);
+}
+
+bool BulletPhysicsServer::body_test_motion(RID p_body, const Transform &p_from, const Vector3 &p_motion, float p_margin, MotionResult *r_result) {
+ RigidBodyBullet *body = rigid_body_owner.get(p_body);
+ ERR_FAIL_COND_V(!body, false);
+ ERR_FAIL_COND_V(!body->get_space(), false);
+
+ return body->get_space()->test_body_motion(body, p_from, p_motion, p_margin, r_result);
+}
+
+RID BulletPhysicsServer::soft_body_create(bool p_init_sleeping) {
+ SoftBodyBullet *body = bulletnew(SoftBodyBullet);
+ body->set_collision_layer(1);
+ body->set_collision_mask(1);
+ if (p_init_sleeping)
+ body->set_activation_state(false);
+ CreateThenReturnRID(soft_body_owner, body);
+}
+
+void BulletPhysicsServer::soft_body_set_space(RID p_body, RID p_space) {
+ SoftBodyBullet *body = soft_body_owner.get(p_body);
+ ERR_FAIL_COND(!body);
+ SpaceBullet *space = NULL;
+
+ if (p_space.is_valid()) {
+ space = space_owner.get(p_space);
+ ERR_FAIL_COND(!space);
+ }
+
+ if (body->get_space() == space)
+ return; //pointles
+
+ body->set_space(space);
+}
+
+RID BulletPhysicsServer::soft_body_get_space(RID p_body) const {
+ SoftBodyBullet *body = soft_body_owner.get(p_body);
+ ERR_FAIL_COND_V(!body, RID());
+
+ SpaceBullet *space = body->get_space();
+ if (!space)
+ return RID();
+ return space->get_self();
+}
+
+void BulletPhysicsServer::soft_body_set_trimesh_body_shape(RID p_body, PoolVector<int> p_indices, PoolVector<Vector3> p_vertices, int p_triangles_num) {
+ SoftBodyBullet *body = soft_body_owner.get(p_body);
+ ERR_FAIL_COND(!body);
+
+ body->set_trimesh_body_shape(p_indices, p_vertices, p_triangles_num);
+}
+
+void BulletPhysicsServer::soft_body_set_collision_layer(RID p_body, uint32_t p_layer) {
+ SoftBodyBullet *body = soft_body_owner.get(p_body);
+ ERR_FAIL_COND(!body);
+
+ body->set_collision_layer(p_layer);
+}
+
+uint32_t BulletPhysicsServer::soft_body_get_collision_layer(RID p_body) const {
+ const SoftBodyBullet *body = soft_body_owner.get(p_body);
+ ERR_FAIL_COND_V(!body, 0);
+
+ return body->get_collision_layer();
+}
+
+void BulletPhysicsServer::soft_body_set_collision_mask(RID p_body, uint32_t p_mask) {
+ SoftBodyBullet *body = soft_body_owner.get(p_body);
+ ERR_FAIL_COND(!body);
+
+ body->set_collision_mask(p_mask);
+}
+
+uint32_t BulletPhysicsServer::soft_body_get_collision_mask(RID p_body) const {
+ const SoftBodyBullet *body = soft_body_owner.get(p_body);
+ ERR_FAIL_COND_V(!body, 0);
+
+ return body->get_collision_mask();
+}
+
+void BulletPhysicsServer::soft_body_add_collision_exception(RID p_body, RID p_body_b) {
+ SoftBodyBullet *body = soft_body_owner.get(p_body);
+ ERR_FAIL_COND(!body);
+
+ CollisionObjectBullet *other_body = rigid_body_owner.get(p_body_b);
+ if (!other_body) {
+ other_body = soft_body_owner.get(p_body_b);
+ }
+ ERR_FAIL_COND(!other_body);
+
+ body->add_collision_exception(other_body);
+}
+
+void BulletPhysicsServer::soft_body_remove_collision_exception(RID p_body, RID p_body_b) {
+ SoftBodyBullet *body = soft_body_owner.get(p_body);
+ ERR_FAIL_COND(!body);
+
+ CollisionObjectBullet *other_body = rigid_body_owner.get(p_body_b);
+ if (!other_body) {
+ other_body = soft_body_owner.get(p_body_b);
+ }
+ ERR_FAIL_COND(!other_body);
+
+ body->remove_collision_exception(other_body);
+}
+
+void BulletPhysicsServer::soft_body_get_collision_exceptions(RID p_body, List<RID> *p_exceptions) {
+ SoftBodyBullet *body = soft_body_owner.get(p_body);
+ ERR_FAIL_COND(!body);
+ for (int i = 0; i < body->get_exceptions().size(); i++) {
+ p_exceptions->push_back(body->get_exceptions()[i]);
+ }
+}
+
+void BulletPhysicsServer::soft_body_set_state(RID p_body, BodyState p_state, const Variant &p_variant) {
+ print_line("TODO MUST BE IMPLEMENTED");
+}
+
+Variant BulletPhysicsServer::soft_body_get_state(RID p_body, BodyState p_state) const {
+ print_line("TODO MUST BE IMPLEMENTED");
+ return Variant();
+}
+
+void BulletPhysicsServer::soft_body_set_transform(RID p_body, const Transform &p_transform) {
+ SoftBodyBullet *body = soft_body_owner.get(p_body);
+ ERR_FAIL_COND(!body);
+
+ body->set_transform(p_transform);
+}
+
+Transform BulletPhysicsServer::soft_body_get_transform(RID p_body) const {
+ const SoftBodyBullet *body = soft_body_owner.get(p_body);
+ ERR_FAIL_COND_V(!body, Transform());
+
+ return body->get_transform();
+}
+
+void BulletPhysicsServer::soft_body_set_ray_pickable(RID p_body, bool p_enable) {
+ SoftBodyBullet *body = soft_body_owner.get(p_body);
+ ERR_FAIL_COND(!body);
+ body->set_ray_pickable(p_enable);
+}
+
+bool BulletPhysicsServer::soft_body_is_ray_pickable(RID p_body) const {
+ SoftBodyBullet *body = soft_body_owner.get(p_body);
+ ERR_FAIL_COND_V(!body, false);
+ return body->is_ray_pickable();
+}
+
+PhysicsServer::JointType BulletPhysicsServer::joint_get_type(RID p_joint) const {
+ JointBullet *joint = joint_owner.get(p_joint);
+ ERR_FAIL_COND_V(!joint, JOINT_PIN);
+ return joint->get_type();
+}
+
+void BulletPhysicsServer::joint_set_solver_priority(RID p_joint, int p_priority) {
+ //WARN_PRINTS("Joint priority not supported by bullet");
+}
+
+int BulletPhysicsServer::joint_get_solver_priority(RID p_joint) const {
+ //WARN_PRINTS("Joint priority not supported by bullet");
+ return 0;
+}
+
+RID BulletPhysicsServer::joint_create_pin(RID p_body_A, const Vector3 &p_local_A, RID p_body_B, const Vector3 &p_local_B) {
+ RigidBodyBullet *body_A = rigid_body_owner.get(p_body_A);
+ ERR_FAIL_COND_V(!body_A, RID());
+
+ JointAssertSpace(body_A, "A", RID());
+
+ RigidBodyBullet *body_B = NULL;
+ if (p_body_B.is_valid()) {
+ body_B = rigid_body_owner.get(p_body_B);
+ JointAssertSpace(body_B, "B", RID());
+ JointAssertSameSpace(body_A, body_B, RID());
+ }
+
+ ERR_FAIL_COND_V(body_A == body_B, RID());
+
+ JointBullet *joint = bulletnew(PinJointBullet(body_A, p_local_A, body_B, p_local_B));
+ AddJointToSpace(body_A, joint, true);
+
+ CreateThenReturnRID(joint_owner, joint);
+}
+
+void BulletPhysicsServer::pin_joint_set_param(RID p_joint, PinJointParam p_param, float p_value) {
+ JointBullet *joint = joint_owner.get(p_joint);
+ ERR_FAIL_COND(!joint);
+ ERR_FAIL_COND(joint->get_type() != JOINT_PIN);
+ PinJointBullet *pin_joint = static_cast<PinJointBullet *>(joint);
+ pin_joint->set_param(p_param, p_value);
+}
+
+float BulletPhysicsServer::pin_joint_get_param(RID p_joint, PinJointParam p_param) const {
+ JointBullet *joint = joint_owner.get(p_joint);
+ ERR_FAIL_COND_V(!joint, 0);
+ ERR_FAIL_COND_V(joint->get_type() != JOINT_PIN, 0);
+ PinJointBullet *pin_joint = static_cast<PinJointBullet *>(joint);
+ return pin_joint->get_param(p_param);
+}
+
+void BulletPhysicsServer::pin_joint_set_local_a(RID p_joint, const Vector3 &p_A) {
+ JointBullet *joint = joint_owner.get(p_joint);
+ ERR_FAIL_COND(!joint);
+ ERR_FAIL_COND(joint->get_type() != JOINT_PIN);
+ PinJointBullet *pin_joint = static_cast<PinJointBullet *>(joint);
+ pin_joint->setPivotInA(p_A);
+}
+
+Vector3 BulletPhysicsServer::pin_joint_get_local_a(RID p_joint) const {
+ JointBullet *joint = joint_owner.get(p_joint);
+ ERR_FAIL_COND_V(!joint, Vector3());
+ ERR_FAIL_COND_V(joint->get_type() != JOINT_PIN, Vector3());
+ PinJointBullet *pin_joint = static_cast<PinJointBullet *>(joint);
+ return pin_joint->getPivotInA();
+}
+
+void BulletPhysicsServer::pin_joint_set_local_b(RID p_joint, const Vector3 &p_B) {
+ JointBullet *joint = joint_owner.get(p_joint);
+ ERR_FAIL_COND(!joint);
+ ERR_FAIL_COND(joint->get_type() != JOINT_PIN);
+ PinJointBullet *pin_joint = static_cast<PinJointBullet *>(joint);
+ pin_joint->setPivotInB(p_B);
+}
+
+Vector3 BulletPhysicsServer::pin_joint_get_local_b(RID p_joint) const {
+ JointBullet *joint = joint_owner.get(p_joint);
+ ERR_FAIL_COND_V(!joint, Vector3());
+ ERR_FAIL_COND_V(joint->get_type() != JOINT_PIN, Vector3());
+ PinJointBullet *pin_joint = static_cast<PinJointBullet *>(joint);
+ return pin_joint->getPivotInB();
+}
+
+RID BulletPhysicsServer::joint_create_hinge(RID p_body_A, const Transform &p_hinge_A, RID p_body_B, const Transform &p_hinge_B) {
+ RigidBodyBullet *body_A = rigid_body_owner.get(p_body_A);
+ ERR_FAIL_COND_V(!body_A, RID());
+ JointAssertSpace(body_A, "A", RID());
+
+ RigidBodyBullet *body_B = NULL;
+ if (p_body_B.is_valid()) {
+ body_B = rigid_body_owner.get(p_body_B);
+ JointAssertSpace(body_B, "B", RID());
+ JointAssertSameSpace(body_A, body_B, RID());
+ }
+
+ ERR_FAIL_COND_V(body_A == body_B, RID());
+
+ JointBullet *joint = bulletnew(HingeJointBullet(body_A, body_B, p_hinge_A, p_hinge_B));
+ AddJointToSpace(body_A, joint, true);
+
+ CreateThenReturnRID(joint_owner, joint);
+}
+
+RID BulletPhysicsServer::joint_create_hinge_simple(RID p_body_A, const Vector3 &p_pivot_A, const Vector3 &p_axis_A, RID p_body_B, const Vector3 &p_pivot_B, const Vector3 &p_axis_B) {
+ RigidBodyBullet *body_A = rigid_body_owner.get(p_body_A);
+ ERR_FAIL_COND_V(!body_A, RID());
+ JointAssertSpace(body_A, "A", RID());
+
+ RigidBodyBullet *body_B = NULL;
+ if (p_body_B.is_valid()) {
+ body_B = rigid_body_owner.get(p_body_B);
+ JointAssertSpace(body_B, "B", RID());
+ JointAssertSameSpace(body_A, body_B, RID());
+ }
+
+ ERR_FAIL_COND_V(body_A == body_B, RID());
+
+ JointBullet *joint = bulletnew(HingeJointBullet(body_A, body_B, p_pivot_A, p_pivot_B, p_axis_A, p_axis_B));
+ AddJointToSpace(body_A, joint, true);
+
+ CreateThenReturnRID(joint_owner, joint);
+}
+
+void BulletPhysicsServer::hinge_joint_set_param(RID p_joint, HingeJointParam p_param, float p_value) {
+ JointBullet *joint = joint_owner.get(p_joint);
+ ERR_FAIL_COND(!joint);
+ ERR_FAIL_COND(joint->get_type() != JOINT_HINGE);
+ HingeJointBullet *hinge_joint = static_cast<HingeJointBullet *>(joint);
+ hinge_joint->set_param(p_param, p_value);
+}
+
+float BulletPhysicsServer::hinge_joint_get_param(RID p_joint, HingeJointParam p_param) const {
+ JointBullet *joint = joint_owner.get(p_joint);
+ ERR_FAIL_COND_V(!joint, 0);
+ ERR_FAIL_COND_V(joint->get_type() != JOINT_HINGE, 0);
+ HingeJointBullet *hinge_joint = static_cast<HingeJointBullet *>(joint);
+ return hinge_joint->get_param(p_param);
+}
+
+void BulletPhysicsServer::hinge_joint_set_flag(RID p_joint, HingeJointFlag p_flag, bool p_value) {
+ JointBullet *joint = joint_owner.get(p_joint);
+ ERR_FAIL_COND(!joint);
+ ERR_FAIL_COND(joint->get_type() != JOINT_HINGE);
+ HingeJointBullet *hinge_joint = static_cast<HingeJointBullet *>(joint);
+ hinge_joint->set_flag(p_flag, p_value);
+}
+
+bool BulletPhysicsServer::hinge_joint_get_flag(RID p_joint, HingeJointFlag p_flag) const {
+ JointBullet *joint = joint_owner.get(p_joint);
+ ERR_FAIL_COND_V(!joint, false);
+ ERR_FAIL_COND_V(joint->get_type() != JOINT_HINGE, false);
+ HingeJointBullet *hinge_joint = static_cast<HingeJointBullet *>(joint);
+ return hinge_joint->get_flag(p_flag);
+}
+
+RID BulletPhysicsServer::joint_create_slider(RID p_body_A, const Transform &p_local_frame_A, RID p_body_B, const Transform &p_local_frame_B) {
+ RigidBodyBullet *body_A = rigid_body_owner.get(p_body_A);
+ ERR_FAIL_COND_V(!body_A, RID());
+ JointAssertSpace(body_A, "A", RID());
+
+ RigidBodyBullet *body_B = NULL;
+ if (p_body_B.is_valid()) {
+ body_B = rigid_body_owner.get(p_body_B);
+ JointAssertSpace(body_B, "B", RID());
+ JointAssertSameSpace(body_A, body_B, RID());
+ }
+
+ ERR_FAIL_COND_V(body_A == body_B, RID());
+
+ JointBullet *joint = bulletnew(SliderJointBullet(body_A, body_B, p_local_frame_A, p_local_frame_B));
+ AddJointToSpace(body_A, joint, true);
+
+ CreateThenReturnRID(joint_owner, joint);
+}
+
+void BulletPhysicsServer::slider_joint_set_param(RID p_joint, SliderJointParam p_param, float p_value) {
+ JointBullet *joint = joint_owner.get(p_joint);
+ ERR_FAIL_COND(!joint);
+ ERR_FAIL_COND(joint->get_type() != JOINT_SLIDER);
+ SliderJointBullet *slider_joint = static_cast<SliderJointBullet *>(joint);
+ slider_joint->set_param(p_param, p_value);
+}
+
+float BulletPhysicsServer::slider_joint_get_param(RID p_joint, SliderJointParam p_param) const {
+ JointBullet *joint = joint_owner.get(p_joint);
+ ERR_FAIL_COND_V(!joint, 0);
+ ERR_FAIL_COND_V(joint->get_type() != JOINT_SLIDER, 0);
+ SliderJointBullet *slider_joint = static_cast<SliderJointBullet *>(joint);
+ return slider_joint->get_param(p_param);
+}
+
+RID BulletPhysicsServer::joint_create_cone_twist(RID p_body_A, const Transform &p_local_frame_A, RID p_body_B, const Transform &p_local_frame_B) {
+ RigidBodyBullet *body_A = rigid_body_owner.get(p_body_A);
+ ERR_FAIL_COND_V(!body_A, RID());
+ JointAssertSpace(body_A, "A", RID());
+
+ RigidBodyBullet *body_B = NULL;
+ if (p_body_B.is_valid()) {
+ body_B = rigid_body_owner.get(p_body_B);
+ JointAssertSpace(body_B, "B", RID());
+ JointAssertSameSpace(body_A, body_B, RID());
+ }
+
+ JointBullet *joint = bulletnew(ConeTwistJointBullet(body_A, body_B, p_local_frame_A, p_local_frame_B));
+ AddJointToSpace(body_A, joint, true);
+
+ CreateThenReturnRID(joint_owner, joint);
+}
+
+void BulletPhysicsServer::cone_twist_joint_set_param(RID p_joint, ConeTwistJointParam p_param, float p_value) {
+ JointBullet *joint = joint_owner.get(p_joint);
+ ERR_FAIL_COND(!joint);
+ ERR_FAIL_COND(joint->get_type() != JOINT_CONE_TWIST);
+ ConeTwistJointBullet *coneTwist_joint = static_cast<ConeTwistJointBullet *>(joint);
+ coneTwist_joint->set_param(p_param, p_value);
+}
+
+float BulletPhysicsServer::cone_twist_joint_get_param(RID p_joint, ConeTwistJointParam p_param) const {
+ JointBullet *joint = joint_owner.get(p_joint);
+ ERR_FAIL_COND_V(!joint, 0.);
+ ERR_FAIL_COND_V(joint->get_type() != JOINT_CONE_TWIST, 0.);
+ ConeTwistJointBullet *coneTwist_joint = static_cast<ConeTwistJointBullet *>(joint);
+ return coneTwist_joint->get_param(p_param);
+}
+
+RID BulletPhysicsServer::joint_create_generic_6dof(RID p_body_A, const Transform &p_local_frame_A, RID p_body_B, const Transform &p_local_frame_B) {
+ RigidBodyBullet *body_A = rigid_body_owner.get(p_body_A);
+ ERR_FAIL_COND_V(!body_A, RID());
+ JointAssertSpace(body_A, "A", RID());
+
+ RigidBodyBullet *body_B = NULL;
+ if (p_body_B.is_valid()) {
+ body_B = rigid_body_owner.get(p_body_B);
+ JointAssertSpace(body_B, "B", RID());
+ JointAssertSameSpace(body_A, body_B, RID());
+ }
+
+ ERR_FAIL_COND_V(body_A == body_B, RID());
+
+ JointBullet *joint = bulletnew(Generic6DOFJointBullet(body_A, body_B, p_local_frame_A, p_local_frame_B, true));
+ AddJointToSpace(body_A, joint, true);
+
+ CreateThenReturnRID(joint_owner, joint);
+}
+
+void BulletPhysicsServer::generic_6dof_joint_set_param(RID p_joint, Vector3::Axis p_axis, G6DOFJointAxisParam p_param, float p_value) {
+ JointBullet *joint = joint_owner.get(p_joint);
+ ERR_FAIL_COND(!joint);
+ ERR_FAIL_COND(joint->get_type() != JOINT_6DOF);
+ Generic6DOFJointBullet *generic_6dof_joint = static_cast<Generic6DOFJointBullet *>(joint);
+ generic_6dof_joint->set_param(p_axis, p_param, p_value);
+}
+
+float BulletPhysicsServer::generic_6dof_joint_get_param(RID p_joint, Vector3::Axis p_axis, G6DOFJointAxisParam p_param) {
+ JointBullet *joint = joint_owner.get(p_joint);
+ ERR_FAIL_COND_V(!joint, 0);
+ ERR_FAIL_COND_V(joint->get_type() != JOINT_6DOF, 0);
+ Generic6DOFJointBullet *generic_6dof_joint = static_cast<Generic6DOFJointBullet *>(joint);
+ return generic_6dof_joint->get_param(p_axis, p_param);
+}
+
+void BulletPhysicsServer::generic_6dof_joint_set_flag(RID p_joint, Vector3::Axis p_axis, G6DOFJointAxisFlag p_flag, bool p_enable) {
+ JointBullet *joint = joint_owner.get(p_joint);
+ ERR_FAIL_COND(!joint);
+ ERR_FAIL_COND(joint->get_type() != JOINT_6DOF);
+ Generic6DOFJointBullet *generic_6dof_joint = static_cast<Generic6DOFJointBullet *>(joint);
+ generic_6dof_joint->set_flag(p_axis, p_flag, p_enable);
+}
+
+bool BulletPhysicsServer::generic_6dof_joint_get_flag(RID p_joint, Vector3::Axis p_axis, G6DOFJointAxisFlag p_flag) {
+ JointBullet *joint = joint_owner.get(p_joint);
+ ERR_FAIL_COND_V(!joint, false);
+ ERR_FAIL_COND_V(joint->get_type() != JOINT_6DOF, false);
+ Generic6DOFJointBullet *generic_6dof_joint = static_cast<Generic6DOFJointBullet *>(joint);
+ return generic_6dof_joint->get_flag(p_axis, p_flag);
+}
+
+void BulletPhysicsServer::free(RID p_rid) {
+ if (shape_owner.owns(p_rid)) {
+
+ ShapeBullet *shape = shape_owner.get(p_rid);
+
+ // Notify the shape is configured
+ for (Map<ShapeOwnerBullet *, int>::Element *element = shape->get_owners().front(); element; element = element->next()) {
+ static_cast<ShapeOwnerBullet *>(element->key())->remove_shape(shape);
+ }
+
+ shape_owner.free(p_rid);
+ bulletdelete(shape);
+ } else if (rigid_body_owner.owns(p_rid)) {
+
+ RigidBodyBullet *body = rigid_body_owner.get(p_rid);
+
+ body->set_space(NULL);
+
+ body->remove_all_shapes(true);
+
+ rigid_body_owner.free(p_rid);
+ bulletdelete(body);
+
+ } else if (soft_body_owner.owns(p_rid)) {
+
+ SoftBodyBullet *body = soft_body_owner.get(p_rid);
+
+ body->set_space(NULL);
+
+ soft_body_owner.free(p_rid);
+ bulletdelete(body);
+
+ } else if (area_owner.owns(p_rid)) {
+
+ AreaBullet *area = area_owner.get(p_rid);
+
+ area->set_space(NULL);
+
+ area->remove_all_shapes(true);
+
+ area_owner.free(p_rid);
+ bulletdelete(area);
+
+ } else if (joint_owner.owns(p_rid)) {
+
+ JointBullet *joint = joint_owner.get(p_rid);
+ joint->destroy_internal_constraint();
+ joint_owner.free(p_rid);
+ bulletdelete(joint);
+
+ } else if (space_owner.owns(p_rid)) {
+
+ SpaceBullet *space = space_owner.get(p_rid);
+
+ space->remove_all_collision_objects();
+
+ space_set_active(p_rid, false);
+ space_owner.free(p_rid);
+ bulletdelete(space);
+ } else {
+
+ ERR_EXPLAIN("Invalid ID");
+ ERR_FAIL();
+ }
+}
+
+void BulletPhysicsServer::init() {
+ BulletPhysicsDirectBodyState::initSingleton();
+}
+
+void BulletPhysicsServer::step(float p_deltaTime) {
+ if (!active)
+ return;
+
+ BulletPhysicsDirectBodyState::singleton_setDeltaTime(p_deltaTime);
+
+ for (int i = 0; i < active_spaces_count; ++i) {
+
+ active_spaces[i]->step(p_deltaTime);
+ }
+}
+
+void BulletPhysicsServer::sync() {
+}
+
+void BulletPhysicsServer::flush_queries() {
+}
+
+void BulletPhysicsServer::finish() {
+ BulletPhysicsDirectBodyState::destroySingleton();
+}
+
+int BulletPhysicsServer::get_process_info(ProcessInfo p_info) {
+ return 0;
+}
+
+CollisionObjectBullet *BulletPhysicsServer::get_collisin_object(RID p_object) const {
+ if (rigid_body_owner.owns(p_object)) {
+ return rigid_body_owner.getornull(p_object);
+ }
+ if (area_owner.owns(p_object)) {
+ return area_owner.getornull(p_object);
+ }
+ if (soft_body_owner.owns(p_object)) {
+ return soft_body_owner.getornull(p_object);
+ }
+ return NULL;
+}
+
+RigidCollisionObjectBullet *BulletPhysicsServer::get_rigid_collisin_object(RID p_object) const {
+ if (rigid_body_owner.owns(p_object)) {
+ return rigid_body_owner.getornull(p_object);
+ }
+ if (area_owner.owns(p_object)) {
+ return area_owner.getornull(p_object);
+ }
+ return NULL;
+}
diff --git a/modules/bullet/bullet_physics_server.h b/modules/bullet/bullet_physics_server.h
new file mode 100644
index 0000000000..04d9d89594
--- /dev/null
+++ b/modules/bullet/bullet_physics_server.h
@@ -0,0 +1,359 @@
+/*************************************************************************/
+/* bullet_physics_server.h */
+/* Author: AndreaCatania */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2017 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 BULLET_PHYSICS_SERVER_H
+#define BULLET_PHYSICS_SERVER_H
+
+#include "area_bullet.h"
+#include "joint_bullet.h"
+#include "rid.h"
+#include "rigid_body_bullet.h"
+#include "servers/physics_server.h"
+#include "shape_bullet.h"
+#include "soft_body_bullet.h"
+#include "space_bullet.h"
+
+class BulletPhysicsServer : public PhysicsServer {
+ GDCLASS(BulletPhysicsServer, PhysicsServer)
+
+ friend class BulletPhysicsDirectSpaceState;
+
+ bool active;
+ char active_spaces_count;
+ Vector<SpaceBullet *> active_spaces;
+
+ mutable RID_Owner<SpaceBullet> space_owner;
+ mutable RID_Owner<ShapeBullet> shape_owner;
+ mutable RID_Owner<AreaBullet> area_owner;
+ mutable RID_Owner<RigidBodyBullet> rigid_body_owner;
+ mutable RID_Owner<SoftBodyBullet> soft_body_owner;
+ mutable RID_Owner<JointBullet> joint_owner;
+
+private:
+ /// This is used when a collision shape is not active, so the bullet compound shapes index are always sync with godot index
+ static btEmptyShape *emptyShape;
+
+public:
+ static btEmptyShape *get_empty_shape();
+
+protected:
+ static void _bind_methods();
+
+public:
+ BulletPhysicsServer();
+ ~BulletPhysicsServer();
+
+ _FORCE_INLINE_ RID_Owner<SpaceBullet> *get_space_owner() {
+ return &space_owner;
+ }
+ _FORCE_INLINE_ RID_Owner<ShapeBullet> *get_shape_owner() {
+ return &shape_owner;
+ }
+ _FORCE_INLINE_ RID_Owner<AreaBullet> *get_area_owner() {
+ return &area_owner;
+ }
+ _FORCE_INLINE_ RID_Owner<RigidBodyBullet> *get_rigid_body_owner() {
+ return &rigid_body_owner;
+ }
+ _FORCE_INLINE_ RID_Owner<SoftBodyBullet> *get_soft_body_owner() {
+ return &soft_body_owner;
+ }
+ _FORCE_INLINE_ RID_Owner<JointBullet> *get_joint_owner() {
+ return &joint_owner;
+ }
+
+ /* SHAPE API */
+ virtual RID shape_create(ShapeType p_shape);
+ virtual void shape_set_data(RID p_shape, const Variant &p_data);
+ virtual ShapeType shape_get_type(RID p_shape) const;
+ virtual Variant shape_get_data(RID p_shape) const;
+
+ /// Not supported
+ virtual void shape_set_custom_solver_bias(RID p_shape, real_t p_bias);
+ /// Not supported
+ virtual real_t shape_get_custom_solver_bias(RID p_shape) const;
+
+ /* SPACE API */
+
+ virtual RID space_create();
+ virtual void space_set_active(RID p_space, bool p_active);
+ virtual bool space_is_active(RID p_space) const;
+
+ /// Not supported
+ virtual void space_set_param(RID p_space, SpaceParameter p_param, real_t p_value);
+ /// Not supported
+ virtual real_t space_get_param(RID p_space, SpaceParameter p_param) const;
+
+ virtual PhysicsDirectSpaceState *space_get_direct_state(RID p_space);
+
+ virtual void space_set_debug_contacts(RID p_space, int p_max_contacts);
+ virtual Vector<Vector3> space_get_contacts(RID p_space) const;
+ virtual int space_get_contact_count(RID p_space) const;
+
+ /* AREA API */
+
+ /// Bullet Physics Engine not support "Area", this must be handled by the game developer in another way.
+ /// Since godot Physics use the concept of area even to define the main world, the API area_set_param is used to set initial physics world information.
+ /// The API area_set_param is a bit hacky, and allow Godot to set some parameters on Bullet's world, a different use print a warning to console.
+ /// All other APIs returns a warning message if used
+
+ virtual RID area_create();
+
+ virtual void area_set_space(RID p_area, RID p_space);
+
+ virtual RID area_get_space(RID p_area) const;
+
+ virtual void area_set_space_override_mode(RID p_area, AreaSpaceOverrideMode p_mode);
+ virtual AreaSpaceOverrideMode area_get_space_override_mode(RID p_area) const;
+
+ virtual void area_add_shape(RID p_area, RID p_shape, const Transform &p_transform = Transform());
+ virtual void area_set_shape(RID p_area, int p_shape_idx, RID p_shape);
+ virtual void area_set_shape_transform(RID p_area, int p_shape_idx, const Transform &p_transform);
+ virtual int area_get_shape_count(RID p_area) const;
+ virtual RID area_get_shape(RID p_area, int p_shape_idx) const;
+ virtual Transform area_get_shape_transform(RID p_area, int p_shape_idx) const;
+ virtual void area_remove_shape(RID p_area, int p_shape_idx);
+ virtual void area_clear_shapes(RID p_area);
+ virtual void area_set_shape_disabled(RID p_area, int p_shape_idx, bool p_disabled);
+ virtual void area_attach_object_instance_id(RID p_area, ObjectID p_ID);
+ virtual ObjectID area_get_object_instance_id(RID p_area) const;
+
+ /// If you pass as p_area the SpaceBullet you can set some parameters as specified below
+ /// AREA_PARAM_GRAVITY
+ /// AREA_PARAM_GRAVITY_VECTOR
+ /// Otherwise you can set area parameters
+ virtual void area_set_param(RID p_area, AreaParameter p_param, const Variant &p_value);
+ virtual Variant area_get_param(RID p_parea, AreaParameter p_param) const;
+
+ virtual void area_set_transform(RID p_area, const Transform &p_transform);
+ virtual Transform area_get_transform(RID p_area) const;
+
+ virtual void area_set_collision_mask(RID p_area, uint32_t p_mask);
+ virtual void area_set_collision_layer(RID p_area, uint32_t p_layer);
+
+ virtual void area_set_monitorable(RID p_area, bool p_monitorable);
+ virtual void area_set_monitor_callback(RID p_area, Object *p_receiver, const StringName &p_method);
+ virtual void area_set_area_monitor_callback(RID p_area, Object *p_receiver, const StringName &p_method);
+ virtual void area_set_ray_pickable(RID p_area, bool p_enable);
+ virtual bool area_is_ray_pickable(RID p_area) const;
+
+ /* RIGID BODY API */
+
+ virtual RID body_create(BodyMode p_mode = BODY_MODE_RIGID, bool p_init_sleeping = false);
+
+ virtual void body_set_space(RID p_body, RID p_space);
+ virtual RID body_get_space(RID p_body) const;
+
+ virtual void body_set_mode(RID p_body, BodyMode p_mode);
+ virtual BodyMode body_get_mode(RID p_body) const;
+
+ virtual void body_add_shape(RID p_body, RID p_shape, const Transform &p_transform = Transform());
+ // Not supported, Please remove and add new shape
+ virtual void body_set_shape(RID p_body, int p_shape_idx, RID p_shape);
+ virtual void body_set_shape_transform(RID p_body, int p_shape_idx, const Transform &p_transform);
+
+ virtual int body_get_shape_count(RID p_body) const;
+ virtual RID body_get_shape(RID p_body, int p_shape_idx) const;
+ virtual Transform body_get_shape_transform(RID p_body, int p_shape_idx) const;
+
+ virtual void body_set_shape_disabled(RID p_body, int p_shape_idx, bool p_disabled);
+
+ virtual void body_remove_shape(RID p_body, int p_shape_idx);
+ virtual void body_clear_shapes(RID p_body);
+
+ // Used for Rigid and Soft Bodies
+ virtual void body_attach_object_instance_id(RID p_body, uint32_t p_ID);
+ virtual uint32_t body_get_object_instance_id(RID p_body) const;
+
+ virtual void body_set_enable_continuous_collision_detection(RID p_body, bool p_enable);
+ virtual bool body_is_continuous_collision_detection_enabled(RID p_body) const;
+
+ virtual void body_set_collision_layer(RID p_body, uint32_t p_layer);
+ virtual uint32_t body_get_collision_layer(RID p_body) const;
+
+ virtual void body_set_collision_mask(RID p_body, uint32_t p_mask);
+ virtual uint32_t body_get_collision_mask(RID p_body) const;
+
+ /// This is not supported by physics server
+ virtual void body_set_user_flags(RID p_body, uint32_t p_flags);
+ /// This is not supported by physics server
+ virtual uint32_t body_get_user_flags(RID p_body) const;
+
+ virtual void body_set_param(RID p_body, BodyParameter p_param, float p_value);
+ virtual float body_get_param(RID p_body, BodyParameter p_param) const;
+
+ virtual void body_set_state(RID p_body, BodyState p_state, const Variant &p_variant);
+ virtual Variant body_get_state(RID p_body, BodyState p_state) const;
+
+ virtual void body_set_applied_force(RID p_body, const Vector3 &p_force);
+ virtual Vector3 body_get_applied_force(RID p_body) const;
+
+ virtual void body_set_applied_torque(RID p_body, const Vector3 &p_torque);
+ virtual Vector3 body_get_applied_torque(RID p_body) const;
+
+ virtual void body_apply_impulse(RID p_body, const Vector3 &p_pos, const Vector3 &p_impulse);
+ virtual void body_apply_torque_impulse(RID p_body, const Vector3 &p_impulse);
+ virtual void body_set_axis_velocity(RID p_body, const Vector3 &p_axis_velocity);
+
+ virtual void body_set_axis_lock(RID p_body, BodyAxisLock p_lock);
+ virtual BodyAxisLock body_get_axis_lock(RID p_body) const;
+
+ virtual void body_add_collision_exception(RID p_body, RID p_body_b);
+ virtual void body_remove_collision_exception(RID p_body, RID p_body_b);
+ virtual void body_get_collision_exceptions(RID p_body, List<RID> *p_exceptions);
+
+ virtual void body_set_max_contacts_reported(RID p_body, int p_contacts);
+ virtual int body_get_max_contacts_reported(RID p_body) const;
+
+ virtual void body_set_contacts_reported_depth_threshold(RID p_body, float p_treshold);
+ virtual float body_get_contacts_reported_depth_threshold(RID p_body) const;
+
+ virtual void body_set_omit_force_integration(RID p_body, bool p_omit);
+ virtual bool body_is_omitting_force_integration(RID p_body) const;
+
+ virtual void body_set_force_integration_callback(RID p_body, Object *p_receiver, const StringName &p_method, const Variant &p_udata = Variant());
+
+ virtual void body_set_ray_pickable(RID p_body, bool p_enable);
+ virtual bool body_is_ray_pickable(RID p_body) const;
+
+ // this function only works on physics process, errors and returns null otherwise
+ virtual PhysicsDirectBodyState *body_get_direct_state(RID p_body);
+
+ virtual bool body_test_motion(RID p_body, const Transform &p_from, const Vector3 &p_motion, float p_margin = 0.001, MotionResult *r_result = NULL);
+
+ /* SOFT BODY API */
+
+ virtual RID soft_body_create(bool p_init_sleeping = false);
+
+ virtual void soft_body_set_space(RID p_body, RID p_space);
+ virtual RID soft_body_get_space(RID p_body) const;
+
+ virtual void soft_body_set_trimesh_body_shape(RID p_body, PoolVector<int> p_indices, PoolVector<Vector3> p_vertices, int p_triangles_num);
+
+ virtual void soft_body_set_collision_layer(RID p_body, uint32_t p_layer);
+ virtual uint32_t soft_body_get_collision_layer(RID p_body) const;
+
+ virtual void soft_body_set_collision_mask(RID p_body, uint32_t p_mask);
+ virtual uint32_t soft_body_get_collision_mask(RID p_body) const;
+
+ virtual void soft_body_add_collision_exception(RID p_body, RID p_body_b);
+ virtual void soft_body_remove_collision_exception(RID p_body, RID p_body_b);
+ virtual void soft_body_get_collision_exceptions(RID p_body, List<RID> *p_exceptions);
+
+ virtual void soft_body_set_state(RID p_body, BodyState p_state, const Variant &p_variant);
+ virtual Variant soft_body_get_state(RID p_body, BodyState p_state) const;
+
+ virtual void soft_body_set_transform(RID p_body, const Transform &p_transform);
+ virtual Transform soft_body_get_transform(RID p_body) const;
+
+ virtual void soft_body_set_ray_pickable(RID p_body, bool p_enable);
+ virtual bool soft_body_is_ray_pickable(RID p_body) const;
+
+ /* JOINT API */
+
+ virtual JointType joint_get_type(RID p_joint) const;
+
+ virtual void joint_set_solver_priority(RID p_joint, int p_priority);
+ virtual int joint_get_solver_priority(RID p_joint) const;
+
+ virtual RID joint_create_pin(RID p_body_A, const Vector3 &p_local_A, RID p_body_B, const Vector3 &p_local_B);
+
+ virtual void pin_joint_set_param(RID p_joint, PinJointParam p_param, float p_value);
+ virtual float pin_joint_get_param(RID p_joint, PinJointParam p_param) const;
+
+ virtual void pin_joint_set_local_a(RID p_joint, const Vector3 &p_A);
+ virtual Vector3 pin_joint_get_local_a(RID p_joint) const;
+
+ virtual void pin_joint_set_local_b(RID p_joint, const Vector3 &p_B);
+ virtual Vector3 pin_joint_get_local_b(RID p_joint) const;
+
+ virtual RID joint_create_hinge(RID p_body_A, const Transform &p_frame_A, RID p_body_B, const Transform &p_frame_B);
+ virtual RID joint_create_hinge_simple(RID p_body_A, const Vector3 &p_pivot_A, const Vector3 &p_axis_A, RID p_body_B, const Vector3 &p_pivot_B, const Vector3 &p_axis_B);
+
+ virtual void hinge_joint_set_param(RID p_joint, HingeJointParam p_param, float p_value);
+ virtual float hinge_joint_get_param(RID p_joint, HingeJointParam p_param) const;
+
+ virtual void hinge_joint_set_flag(RID p_joint, HingeJointFlag p_flag, bool p_value);
+ virtual bool hinge_joint_get_flag(RID p_joint, HingeJointFlag p_flag) const;
+
+ /// Reference frame is A
+ virtual RID joint_create_slider(RID p_body_A, const Transform &p_local_frame_A, RID p_body_B, const Transform &p_local_frame_B);
+
+ virtual void slider_joint_set_param(RID p_joint, SliderJointParam p_param, float p_value);
+ virtual float slider_joint_get_param(RID p_joint, SliderJointParam p_param) const;
+
+ /// Reference frame is A
+ virtual RID joint_create_cone_twist(RID p_body_A, const Transform &p_local_frame_A, RID p_body_B, const Transform &p_local_frame_B);
+
+ virtual void cone_twist_joint_set_param(RID p_joint, ConeTwistJointParam p_param, float p_value);
+ virtual float cone_twist_joint_get_param(RID p_joint, ConeTwistJointParam p_param) const;
+
+ /// Reference frame is A
+ virtual RID joint_create_generic_6dof(RID p_body_A, const Transform &p_local_frame_A, RID p_body_B, const Transform &p_local_frame_B);
+
+ virtual void generic_6dof_joint_set_param(RID p_joint, Vector3::Axis p_axis, G6DOFJointAxisParam p_param, float p_value);
+ virtual float generic_6dof_joint_get_param(RID p_joint, Vector3::Axis p_axis, G6DOFJointAxisParam p_param);
+
+ virtual void generic_6dof_joint_set_flag(RID p_joint, Vector3::Axis p_axis, G6DOFJointAxisFlag p_flag, bool p_enable);
+ virtual bool generic_6dof_joint_get_flag(RID p_joint, Vector3::Axis p_axis, G6DOFJointAxisFlag p_flag);
+
+ /* MISC */
+
+ virtual void free(RID p_rid);
+
+ virtual void set_active(bool p_active) {
+ active = p_active;
+ }
+
+ static bool singleton_isActive() {
+ return static_cast<BulletPhysicsServer *>(get_singleton())->active;
+ }
+
+ bool isActive() {
+ return active;
+ }
+
+ virtual void init();
+ virtual void step(float p_deltaTime);
+ virtual void sync();
+ virtual void flush_queries();
+ virtual void finish();
+
+ virtual int get_process_info(ProcessInfo p_info);
+
+ CollisionObjectBullet *get_collisin_object(RID p_object) const;
+ RigidCollisionObjectBullet *get_rigid_collisin_object(RID p_object) const;
+
+ /// Internal APIs
+public:
+};
+
+#endif
diff --git a/modules/bullet/bullet_types_converter.cpp b/modules/bullet/bullet_types_converter.cpp
new file mode 100644
index 0000000000..5010197a78
--- /dev/null
+++ b/modules/bullet/bullet_types_converter.cpp
@@ -0,0 +1,94 @@
+/*************************************************************************/
+/* bullet_types_converter.cpp */
+/* Author: AndreaCatania */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2017 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. */
+/*************************************************************************/
+
+#pragma once
+
+#include "bullet_types_converter.h"
+
+// ++ BULLET to GODOT ++++++++++
+void B_TO_G(btVector3 const &inVal, Vector3 &outVal) {
+ outVal[0] = inVal[0];
+ outVal[1] = inVal[1];
+ outVal[2] = inVal[2];
+}
+
+void INVERT_B_TO_G(btVector3 const &inVal, Vector3 &outVal) {
+ outVal[0] = inVal[0] != 0. ? 1. / inVal[0] : 0.;
+ outVal[1] = inVal[1] != 0. ? 1. / inVal[1] : 0.;
+ outVal[2] = inVal[2] != 0. ? 1. / inVal[2] : 0.;
+}
+
+void B_TO_G(btMatrix3x3 const &inVal, Basis &outVal) {
+ B_TO_G(inVal[0], outVal[0]);
+ B_TO_G(inVal[1], outVal[1]);
+ B_TO_G(inVal[2], outVal[2]);
+}
+
+void INVERT_B_TO_G(btMatrix3x3 const &inVal, Basis &outVal) {
+ INVERT_B_TO_G(inVal[0], outVal[0]);
+ INVERT_B_TO_G(inVal[1], outVal[1]);
+ INVERT_B_TO_G(inVal[2], outVal[2]);
+}
+
+void B_TO_G(btTransform const &inVal, Transform &outVal) {
+ B_TO_G(inVal.getBasis(), outVal.basis);
+ B_TO_G(inVal.getOrigin(), outVal.origin);
+}
+
+// ++ GODOT to BULLET ++++++++++
+void G_TO_B(Vector3 const &inVal, btVector3 &outVal) {
+ outVal[0] = inVal[0];
+ outVal[1] = inVal[1];
+ outVal[2] = inVal[2];
+}
+
+void INVERT_G_TO_B(Vector3 const &inVal, btVector3 &outVal) {
+ outVal[0] = inVal[0] != 0. ? 1. / inVal[0] : 0.;
+ outVal[1] = inVal[1] != 0. ? 1. / inVal[1] : 0.;
+ outVal[2] = inVal[2] != 0. ? 1. / inVal[2] : 0.;
+}
+
+void G_TO_B(Basis const &inVal, btMatrix3x3 &outVal) {
+ G_TO_B(inVal[0], outVal[0]);
+ G_TO_B(inVal[1], outVal[1]);
+ G_TO_B(inVal[2], outVal[2]);
+}
+
+void INVERT_G_TO_B(Basis const &inVal, btMatrix3x3 &outVal) {
+ INVERT_G_TO_B(inVal[0], outVal[0]);
+ INVERT_G_TO_B(inVal[1], outVal[1]);
+ INVERT_G_TO_B(inVal[2], outVal[2]);
+}
+
+void G_TO_B(Transform const &inVal, btTransform &outVal) {
+ G_TO_B(inVal.basis, outVal.getBasis());
+ G_TO_B(inVal.origin, outVal.getOrigin());
+}
diff --git a/modules/bullet/bullet_types_converter.h b/modules/bullet/bullet_types_converter.h
new file mode 100644
index 0000000000..ed6a349382
--- /dev/null
+++ b/modules/bullet/bullet_types_converter.h
@@ -0,0 +1,57 @@
+/*************************************************************************/
+/* bullet_types_converter.h */
+/* Author: AndreaCatania */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2017 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 BULLET_TYPES_CONVERTER_H
+#define BULLET_TYPES_CONVERTER_H
+
+#include "LinearMath/btMatrix3x3.h"
+#include "LinearMath/btTransform.h"
+#include "LinearMath/btVector3.h"
+#include "core/math/matrix3.h"
+#include "core/math/transform.h"
+#include "core/math/vector3.h"
+#include "core/typedefs.h"
+
+// Bullet to Godot
+extern void B_TO_G(btVector3 const &inVal, Vector3 &outVal);
+extern void INVERT_B_TO_G(btVector3 const &inVal, Vector3 &outVal);
+extern void B_TO_G(btMatrix3x3 const &inVal, Basis &outVal);
+extern void INVERT_B_TO_G(btMatrix3x3 const &inVal, Basis &outVal);
+extern void B_TO_G(btTransform const &inVal, Transform &outVal);
+
+// Godot TO Bullet
+extern void G_TO_B(Vector3 const &inVal, btVector3 &outVal);
+extern void INVERT_G_TO_B(Vector3 const &inVal, btVector3 &outVal);
+extern void G_TO_B(Basis const &inVal, btMatrix3x3 &outVal);
+extern void INVERT_G_TO_B(Basis const &inVal, btMatrix3x3 &outVal);
+extern void G_TO_B(Transform const &inVal, btTransform &outVal);
+
+#endif
diff --git a/modules/bullet/bullet_utilities.h b/modules/bullet/bullet_utilities.h
new file mode 100644
index 0000000000..45cde169b7
--- /dev/null
+++ b/modules/bullet/bullet_utilities.h
@@ -0,0 +1,44 @@
+/*************************************************************************/
+/* bullet_utilities.h */
+/* Author: AndreaCatania */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2017 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 BULLET_UTILITIES_H
+#define BULLET_UTILITIES_H
+
+#pragma once
+
+#define bulletnew(cl) \
+ new cl
+
+#define bulletdelete(cl) \
+ delete cl; \
+ cl = NULL;
+
+#endif
diff --git a/modules/bullet/collision_object_bullet.cpp b/modules/bullet/collision_object_bullet.cpp
new file mode 100644
index 0000000000..5739568d91
--- /dev/null
+++ b/modules/bullet/collision_object_bullet.cpp
@@ -0,0 +1,316 @@
+/*************************************************************************/
+/* collision_object_bullet.cpp */
+/* Author: AndreaCatania */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2017 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 "collision_object_bullet.h"
+#include "area_bullet.h"
+#include "btBulletCollisionCommon.h"
+#include "bullet_physics_server.h"
+#include "bullet_types_converter.h"
+#include "bullet_utilities.h"
+#include "shape_bullet.h"
+#include "space_bullet.h"
+
+#define enableDynamicAabbTree true
+#define initialChildCapacity 1
+
+CollisionObjectBullet::ShapeWrapper::~ShapeWrapper() {}
+
+void CollisionObjectBullet::ShapeWrapper::set_transform(const Transform &p_transform) {
+ G_TO_B(p_transform, transform);
+}
+void CollisionObjectBullet::ShapeWrapper::set_transform(const btTransform &p_transform) {
+ transform = p_transform;
+}
+
+CollisionObjectBullet::CollisionObjectBullet(Type p_type)
+ : RIDBullet(), space(NULL), type(p_type), collisionsEnabled(true), m_isStatic(false), bt_collision_object(NULL), body_scale(1., 1., 1.) {}
+
+CollisionObjectBullet::~CollisionObjectBullet() {
+ // Remove all overlapping
+ for (int i = areasOverlapped.size() - 1; 0 <= i; --i) {
+ areasOverlapped[i]->remove_overlapping_instantly(this);
+ }
+ // not required
+ // areasOverlapped.clear();
+
+ destroyBulletCollisionObject();
+}
+
+bool equal(real_t first, real_t second) {
+ return Math::abs(first - second) <= 0.001f;
+}
+
+void CollisionObjectBullet::set_body_scale(const Vector3 &p_new_scale) {
+ if (!equal(p_new_scale[0], body_scale[0]) || !equal(p_new_scale[1], body_scale[1]) || !equal(p_new_scale[2], body_scale[2])) {
+ G_TO_B(p_new_scale, body_scale);
+ on_body_scale_changed();
+ }
+}
+
+void CollisionObjectBullet::on_body_scale_changed() {
+}
+
+void CollisionObjectBullet::destroyBulletCollisionObject() {
+ bulletdelete(bt_collision_object);
+}
+
+void CollisionObjectBullet::setupBulletCollisionObject(btCollisionObject *p_collisionObject) {
+ bt_collision_object = p_collisionObject;
+ bt_collision_object->setUserPointer(this);
+ bt_collision_object->setUserIndex(type);
+ // Force the enabling of collision and avoid problems
+ set_collision_enabled(collisionsEnabled);
+}
+
+void CollisionObjectBullet::add_collision_exception(const CollisionObjectBullet *p_ignoreCollisionObject) {
+ exceptions.insert(p_ignoreCollisionObject->get_self());
+ bt_collision_object->setIgnoreCollisionCheck(p_ignoreCollisionObject->bt_collision_object, true);
+}
+
+void CollisionObjectBullet::remove_collision_exception(const CollisionObjectBullet *p_ignoreCollisionObject) {
+ exceptions.erase(p_ignoreCollisionObject->get_self());
+ bt_collision_object->setIgnoreCollisionCheck(p_ignoreCollisionObject->bt_collision_object, false);
+}
+
+bool CollisionObjectBullet::has_collision_exception(const CollisionObjectBullet *p_otherCollisionObject) const {
+ return !bt_collision_object->checkCollideWithOverride(p_otherCollisionObject->bt_collision_object);
+}
+
+void CollisionObjectBullet::set_collision_enabled(bool p_enabled) {
+ collisionsEnabled = p_enabled;
+ if (collisionsEnabled) {
+ bt_collision_object->setCollisionFlags(bt_collision_object->getCollisionFlags() & (~btCollisionObject::CF_NO_CONTACT_RESPONSE));
+ } else {
+ bt_collision_object->setCollisionFlags(bt_collision_object->getCollisionFlags() | btCollisionObject::CF_NO_CONTACT_RESPONSE);
+ }
+}
+
+bool CollisionObjectBullet::is_collisions_response_enabled() {
+ return collisionsEnabled;
+}
+
+void CollisionObjectBullet::notify_new_overlap(AreaBullet *p_area) {
+ areasOverlapped.push_back(p_area);
+}
+
+void CollisionObjectBullet::on_exit_area(AreaBullet *p_area) {
+ areasOverlapped.erase(p_area);
+}
+
+void CollisionObjectBullet::set_godot_object_flags(int flags) {
+ bt_collision_object->setUserIndex2(flags);
+}
+
+int CollisionObjectBullet::get_godot_object_flags() const {
+ return bt_collision_object->getUserIndex2();
+}
+
+void CollisionObjectBullet::set_transform(const Transform &p_global_transform) {
+
+ btTransform btTrans;
+ Basis decomposed_basis;
+
+ Vector3 decomposed_scale = p_global_transform.get_basis().rotref_posscale_decomposition(decomposed_basis);
+
+ G_TO_B(p_global_transform.get_origin(), btTrans.getOrigin());
+ G_TO_B(decomposed_basis, btTrans.getBasis());
+
+ set_body_scale(decomposed_scale);
+ set_transform__bullet(btTrans);
+}
+
+Transform CollisionObjectBullet::get_transform() const {
+ Transform t;
+ B_TO_G(get_transform__bullet(), t);
+ return t;
+}
+
+void CollisionObjectBullet::set_transform__bullet(const btTransform &p_global_transform) {
+ bt_collision_object->setWorldTransform(p_global_transform);
+}
+
+const btTransform &CollisionObjectBullet::get_transform__bullet() const {
+ return bt_collision_object->getWorldTransform();
+}
+
+RigidCollisionObjectBullet::RigidCollisionObjectBullet(Type p_type)
+ : CollisionObjectBullet(p_type), compoundShape(bulletnew(btCompoundShape(enableDynamicAabbTree, initialChildCapacity))) {
+}
+
+RigidCollisionObjectBullet::~RigidCollisionObjectBullet() {
+ remove_all_shapes(true);
+ bt_collision_object->setCollisionShape(NULL);
+ bulletdelete(compoundShape);
+}
+
+/* Not used
+void RigidCollisionObjectBullet::_internal_replaceShape(btCollisionShape *p_old_shape, btCollisionShape *p_new_shape) {
+ bool at_least_one_was_changed = false;
+ btTransform old_transf;
+ // Inverse because I need remove the shapes
+ // Fetch all shapes to be sure to remove all shapes
+ for (int i = compoundShape->getNumChildShapes() - 1; 0 <= i; --i) {
+ if (compoundShape->getChildShape(i) == p_old_shape) {
+
+ old_transf = compoundShape->getChildTransform(i);
+ compoundShape->removeChildShapeByIndex(i);
+ compoundShape->addChildShape(old_transf, p_new_shape);
+ at_least_one_was_changed = true;
+ }
+ }
+
+ if (at_least_one_was_changed) {
+ on_shapes_changed();
+ }
+}*/
+
+void RigidCollisionObjectBullet::add_shape(ShapeBullet *p_shape, const Transform &p_transform) {
+ shapes.push_back(ShapeWrapper(p_shape, p_transform, true));
+ p_shape->add_owner(this);
+ on_shapes_changed();
+}
+
+void RigidCollisionObjectBullet::set_shape(int p_index, ShapeBullet *p_shape) {
+ ShapeWrapper &shp = shapes[p_index];
+ shp.shape->remove_owner(this);
+ p_shape->add_owner(this);
+ shp.shape = p_shape;
+ on_shapes_changed();
+}
+
+void RigidCollisionObjectBullet::set_shape_transform(int p_index, const Transform &p_transform) {
+ ERR_FAIL_INDEX(p_index, get_shape_count());
+
+ shapes[p_index].set_transform(p_transform);
+ on_shapes_changed();
+}
+
+void RigidCollisionObjectBullet::remove_shape(ShapeBullet *p_shape) {
+ // Remove the shape, all the times it appears
+ // Reverse order required for delete.
+ for (int i = shapes.size() - 1; 0 <= i; --i) {
+ if (p_shape == shapes[i].shape) {
+ internal_shape_destroy(i);
+ shapes.remove(i);
+ }
+ }
+ on_shapes_changed();
+}
+
+void RigidCollisionObjectBullet::remove_shape(int p_index) {
+ ERR_FAIL_INDEX(p_index, get_shape_count());
+ internal_shape_destroy(p_index);
+ shapes.remove(p_index);
+ on_shapes_changed();
+}
+
+void RigidCollisionObjectBullet::remove_all_shapes(bool p_permanentlyFromThisBody) {
+ // Reverse order required for delete.
+ for (int i = shapes.size() - 1; 0 <= i; --i) {
+ internal_shape_destroy(i, p_permanentlyFromThisBody);
+ }
+ shapes.clear();
+ on_shapes_changed();
+}
+
+int RigidCollisionObjectBullet::get_shape_count() const {
+ return shapes.size();
+}
+
+ShapeBullet *RigidCollisionObjectBullet::get_shape(int p_index) const {
+ return shapes[p_index].shape;
+}
+
+btCollisionShape *RigidCollisionObjectBullet::get_bt_shape(int p_index) const {
+ return shapes[p_index].bt_shape;
+}
+
+Transform RigidCollisionObjectBullet::get_shape_transform(int p_index) const {
+ Transform trs;
+ B_TO_G(shapes[p_index].transform, trs);
+ return trs;
+}
+
+void RigidCollisionObjectBullet::on_shape_changed(const ShapeBullet *const p_shape) {
+ const int size = shapes.size();
+ for (int i = 0; i < size; ++i) {
+ if (shapes[i].shape == p_shape) {
+ bulletdelete(shapes[i].bt_shape);
+ }
+ }
+ on_shapes_changed();
+}
+
+void RigidCollisionObjectBullet::on_shapes_changed() {
+ int i;
+ // Remove all shapes, reverse order for performance reason (Array resize)
+ for (i = compoundShape->getNumChildShapes() - 1; 0 <= i; --i) {
+ compoundShape->removeChildShapeByIndex(i);
+ }
+
+ // Insert all shapes
+ ShapeWrapper *shpWrapper;
+ const int size = shapes.size();
+ for (i = 0; i < size; ++i) {
+ shpWrapper = &shapes[i];
+ if (!shpWrapper->bt_shape) {
+ shpWrapper->bt_shape = shpWrapper->shape->create_bt_shape();
+ }
+ if (shpWrapper->active) {
+ compoundShape->addChildShape(shpWrapper->transform, shpWrapper->bt_shape);
+ } else {
+ compoundShape->addChildShape(shpWrapper->transform, BulletPhysicsServer::get_empty_shape());
+ }
+ }
+
+ compoundShape->setLocalScaling(body_scale);
+ compoundShape->recalculateLocalAabb();
+}
+
+void RigidCollisionObjectBullet::set_shape_disabled(int p_index, bool p_disabled) {
+ shapes[p_index].active = !p_disabled;
+ on_shapes_changed();
+}
+
+bool RigidCollisionObjectBullet::is_shape_disabled(int p_index) {
+ return !shapes[p_index].active;
+}
+
+void RigidCollisionObjectBullet::on_body_scale_changed() {
+ CollisionObjectBullet::on_body_scale_changed();
+ on_shapes_changed();
+}
+
+void RigidCollisionObjectBullet::internal_shape_destroy(int p_index, bool p_permanentlyFromThisBody) {
+ ShapeWrapper &shp = shapes[p_index];
+ shp.shape->remove_owner(this, p_permanentlyFromThisBody);
+ bulletdelete(shp.bt_shape);
+}
diff --git a/modules/bullet/collision_object_bullet.h b/modules/bullet/collision_object_bullet.h
new file mode 100644
index 0000000000..153b8ea5bc
--- /dev/null
+++ b/modules/bullet/collision_object_bullet.h
@@ -0,0 +1,234 @@
+/*************************************************************************/
+/* collision_object_bullet.h */
+/* Author: AndreaCatania */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2017 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 COLLISION_OBJECT_BULLET_H
+#define COLLISION_OBJECT_BULLET_H
+
+#include "LinearMath/btTransform.h"
+#include "core/vset.h"
+#include "object.h"
+#include "shape_owner_bullet.h"
+#include "transform.h"
+#include "vector3.h"
+
+class AreaBullet;
+class ShapeBullet;
+class btCollisionObject;
+class btCompoundShape;
+class btCollisionShape;
+class SpaceBullet;
+
+class CollisionObjectBullet : public RIDBullet {
+public:
+ enum GodotObjectFlags {
+ GOF_IS_MONITORING_AREA = 1 << 0
+ // FLAG2 = 1 << 1,
+ // FLAG3 = 1 << 2,
+ // FLAG4 = 1 << 3,
+ // FLAG5 = 1 << 4,
+ // FLAG6 = 1 << 5
+ // etc..
+ };
+ enum Type {
+ TYPE_AREA = 0,
+ TYPE_RIGID_BODY,
+ TYPE_SOFT_BODY,
+ TYPE_KINEMATIC_GHOST_BODY
+ };
+
+ struct ShapeWrapper {
+ ShapeBullet *shape;
+ btCollisionShape *bt_shape;
+ btTransform transform;
+ bool active;
+
+ ShapeWrapper()
+ : shape(NULL), bt_shape(NULL), active(true) {}
+
+ ShapeWrapper(ShapeBullet *p_shape, const btTransform &p_transform, bool p_active)
+ : shape(p_shape), bt_shape(NULL), active(p_active) {
+ set_transform(p_transform);
+ }
+
+ ShapeWrapper(ShapeBullet *p_shape, const Transform &p_transform, bool p_active)
+ : shape(p_shape), bt_shape(NULL), active(p_active) {
+ set_transform(p_transform);
+ }
+ ~ShapeWrapper();
+
+ ShapeWrapper(const ShapeWrapper &otherShape) {
+ operator=(otherShape);
+ }
+
+ void operator=(const ShapeWrapper &otherShape) {
+ shape = otherShape.shape;
+ bt_shape = otherShape.bt_shape;
+ transform = otherShape.transform;
+ active = otherShape.active;
+ }
+
+ void set_transform(const Transform &p_transform);
+ void set_transform(const btTransform &p_transform);
+ };
+
+protected:
+ Type type;
+ ObjectID instance_id;
+ uint32_t collisionLayer;
+ uint32_t collisionMask;
+ bool collisionsEnabled;
+ bool m_isStatic;
+ bool ray_pickable;
+ btCollisionObject *bt_collision_object;
+ btVector3 body_scale;
+ SpaceBullet *space;
+
+ VSet<RID> exceptions;
+
+ /// This array is used to know all areas where this Object is overlapped in
+ /// New area is added when overlap with new area (AreaBullet::addOverlap), then is removed when it exit (CollisionObjectBullet::onExitArea)
+ /// This array is used mainly to know which area hold the pointer of this object
+ Vector<AreaBullet *> areasOverlapped;
+
+public:
+ CollisionObjectBullet(Type p_type);
+ virtual ~CollisionObjectBullet();
+
+ Type getType() { return type; }
+
+protected:
+ void destroyBulletCollisionObject();
+ void setupBulletCollisionObject(btCollisionObject *p_collisionObject);
+
+public:
+ _FORCE_INLINE_ btCollisionObject *get_bt_collision_object() { return bt_collision_object; }
+
+ _FORCE_INLINE_ void set_instance_id(const ObjectID &p_instance_id) { instance_id = p_instance_id; }
+ _FORCE_INLINE_ ObjectID get_instance_id() const { return instance_id; }
+
+ _FORCE_INLINE_ bool is_static() const { return m_isStatic; }
+
+ _FORCE_INLINE_ void set_ray_pickable(bool p_enable) { ray_pickable = p_enable; }
+ _FORCE_INLINE_ bool is_ray_pickable() const { return ray_pickable; }
+
+ void set_body_scale(const Vector3 &p_new_scale);
+ virtual void on_body_scale_changed();
+
+ void add_collision_exception(const CollisionObjectBullet *p_ignoreCollisionObject);
+ void remove_collision_exception(const CollisionObjectBullet *p_ignoreCollisionObject);
+ bool has_collision_exception(const CollisionObjectBullet *p_otherCollisionObject) const;
+ _FORCE_INLINE_ const VSet<RID> &get_exceptions() const { return exceptions; }
+
+ _FORCE_INLINE_ void set_collision_layer(uint32_t p_layer) {
+ collisionLayer = p_layer;
+ on_collision_filters_change();
+ }
+ _FORCE_INLINE_ uint32_t get_collision_layer() const { return collisionLayer; }
+
+ _FORCE_INLINE_ void set_collision_mask(uint32_t p_mask) {
+ collisionMask = p_mask;
+ on_collision_filters_change();
+ }
+ _FORCE_INLINE_ uint32_t get_collision_mask() const { return collisionMask; }
+
+ virtual void on_collision_filters_change() = 0;
+
+ _FORCE_INLINE_ bool test_collision_mask(CollisionObjectBullet *p_other) const {
+ return collisionLayer & p_other->collisionMask || p_other->collisionLayer & collisionMask;
+ }
+
+ virtual void reload_body() = 0;
+ virtual void set_space(SpaceBullet *p_space) = 0;
+ _FORCE_INLINE_ SpaceBullet *get_space() const { return space; }
+ /// This is an event that is called when a collision checker starts
+ virtual void on_collision_checker_start() = 0;
+
+ virtual void dispatch_callbacks() = 0;
+
+ void set_collision_enabled(bool p_enabled);
+ bool is_collisions_response_enabled();
+
+ void notify_new_overlap(AreaBullet *p_area);
+ virtual void on_enter_area(AreaBullet *p_area) = 0;
+ virtual void on_exit_area(AreaBullet *p_area);
+
+ /// GodotObjectFlags
+ void set_godot_object_flags(int flags);
+ int get_godot_object_flags() const;
+
+ void set_transform(const Transform &p_global_transform);
+ Transform get_transform() const;
+ virtual void set_transform__bullet(const btTransform &p_global_transform);
+ virtual const btTransform &get_transform__bullet() const;
+};
+
+class RigidCollisionObjectBullet : public CollisionObjectBullet, public ShapeOwnerBullet {
+protected:
+ /// This is required to combine some shapes together.
+ /// Since Godot allow to have multiple shapes for each body with custom relative location,
+ /// each body will attach the shapes using this class even if there is only one shape.
+ btCompoundShape *compoundShape;
+ Vector<ShapeWrapper> shapes;
+
+public:
+ RigidCollisionObjectBullet(Type p_type);
+ ~RigidCollisionObjectBullet();
+
+ _FORCE_INLINE_ const Vector<ShapeWrapper> &get_shapes_wrappers() const { return shapes; }
+
+ /// This is used to set new shape or replace existing
+ //virtual void _internal_replaceShape(btCollisionShape *p_old_shape, btCollisionShape *p_new_shape) = 0;
+ void add_shape(ShapeBullet *p_shape, const Transform &p_transform = Transform());
+ void set_shape(int p_index, ShapeBullet *p_shape);
+ void set_shape_transform(int p_index, const Transform &p_transform);
+ virtual void remove_shape(ShapeBullet *p_shape);
+ void remove_shape(int p_index);
+ void remove_all_shapes(bool p_permanentlyFromThisBody = false);
+
+ virtual void on_shape_changed(const ShapeBullet *const p_shape);
+ virtual void on_shapes_changed();
+
+ _FORCE_INLINE_ btCompoundShape *get_compound_shape() const { return compoundShape; }
+ int get_shape_count() const;
+ ShapeBullet *get_shape(int p_index) const;
+ btCollisionShape *get_bt_shape(int p_index) const;
+ Transform get_shape_transform(int p_index) const;
+
+ void set_shape_disabled(int p_index, bool p_disabled);
+ bool is_shape_disabled(int p_index);
+
+ virtual void on_body_scale_changed();
+
+private:
+ void internal_shape_destroy(int p_index, bool p_permanentlyFromThisBody = false);
+};
+
+#endif
diff --git a/modules/bullet/cone_twist_joint_bullet.cpp b/modules/bullet/cone_twist_joint_bullet.cpp
new file mode 100644
index 0000000000..f6ac40e001
--- /dev/null
+++ b/modules/bullet/cone_twist_joint_bullet.cpp
@@ -0,0 +1,111 @@
+/*************************************************************************/
+/* cone_twist_joint_bullet.cpp */
+/* Author: AndreaCatania */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2017 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 "cone_twist_joint_bullet.h"
+#include "BulletDynamics/ConstraintSolver/btConeTwistConstraint.h"
+#include "bullet_types_converter.h"
+#include "bullet_utilities.h"
+#include "rigid_body_bullet.h"
+
+ConeTwistJointBullet::ConeTwistJointBullet(RigidBodyBullet *rbA, RigidBodyBullet *rbB, const Transform &rbAFrame, const Transform &rbBFrame)
+ : JointBullet() {
+ btTransform btFrameA;
+ G_TO_B(rbAFrame, btFrameA);
+ if (rbB) {
+ btTransform btFrameB;
+ G_TO_B(rbBFrame, btFrameB);
+ coneConstraint = bulletnew(btConeTwistConstraint(*rbA->get_bt_rigid_body(), *rbB->get_bt_rigid_body(), btFrameA, btFrameB));
+ } else {
+ coneConstraint = bulletnew(btConeTwistConstraint(*rbA->get_bt_rigid_body(), btFrameA));
+ }
+ setup(coneConstraint);
+}
+
+void ConeTwistJointBullet::set_angular_only(bool angularOnly) {
+ coneConstraint->setAngularOnly(angularOnly);
+}
+
+void ConeTwistJointBullet::set_limit(real_t _swingSpan1, real_t _swingSpan2, real_t _twistSpan, real_t _softness, real_t _biasFactor, real_t _relaxationFactor) {
+ coneConstraint->setLimit(_swingSpan1, _swingSpan2, _twistSpan, _softness, _biasFactor, _relaxationFactor);
+}
+
+int ConeTwistJointBullet::get_solve_twist_limit() {
+ return coneConstraint->getSolveTwistLimit();
+}
+
+int ConeTwistJointBullet::get_solve_swing_limit() {
+ return coneConstraint->getSolveSwingLimit();
+}
+
+real_t ConeTwistJointBullet::get_twist_limit_sign() {
+ return coneConstraint->getTwistLimitSign();
+}
+
+void ConeTwistJointBullet::set_param(PhysicsServer::ConeTwistJointParam p_param, real_t p_value) {
+ switch (p_param) {
+ case PhysicsServer::CONE_TWIST_JOINT_SWING_SPAN:
+ coneConstraint->setLimit(5, p_value);
+ coneConstraint->setLimit(4, p_value);
+ break;
+ case PhysicsServer::CONE_TWIST_JOINT_TWIST_SPAN:
+ coneConstraint->setLimit(3, p_value);
+ break;
+ case PhysicsServer::CONE_TWIST_JOINT_BIAS:
+ coneConstraint->setLimit(coneConstraint->getSwingSpan1(), coneConstraint->getSwingSpan2(), coneConstraint->getTwistSpan(), coneConstraint->getLimitSoftness(), p_value, coneConstraint->getRelaxationFactor());
+ break;
+ case PhysicsServer::CONE_TWIST_JOINT_SOFTNESS:
+ coneConstraint->setLimit(coneConstraint->getSwingSpan1(), coneConstraint->getSwingSpan2(), coneConstraint->getTwistSpan(), p_value, coneConstraint->getBiasFactor(), coneConstraint->getRelaxationFactor());
+ break;
+ case PhysicsServer::CONE_TWIST_JOINT_RELAXATION:
+ coneConstraint->setLimit(coneConstraint->getSwingSpan1(), coneConstraint->getSwingSpan2(), coneConstraint->getTwistSpan(), coneConstraint->getLimitSoftness(), coneConstraint->getBiasFactor(), p_value);
+ break;
+ default:
+ WARN_PRINT("This parameter is not supported by Bullet engine");
+ }
+}
+
+real_t ConeTwistJointBullet::get_param(PhysicsServer::ConeTwistJointParam p_param) const {
+ switch (p_param) {
+ case PhysicsServer::CONE_TWIST_JOINT_SWING_SPAN:
+ return coneConstraint->getSwingSpan1();
+ case PhysicsServer::CONE_TWIST_JOINT_TWIST_SPAN:
+ return coneConstraint->getTwistSpan();
+ case PhysicsServer::CONE_TWIST_JOINT_BIAS:
+ return coneConstraint->getBiasFactor();
+ case PhysicsServer::CONE_TWIST_JOINT_SOFTNESS:
+ return coneConstraint->getLimitSoftness();
+ case PhysicsServer::CONE_TWIST_JOINT_RELAXATION:
+ return coneConstraint->getRelaxationFactor();
+ default:
+ WARN_PRINT("This parameter is not supported by Bullet engine");
+ return 0;
+ }
+}
diff --git a/modules/bullet/cone_twist_joint_bullet.h b/modules/bullet/cone_twist_joint_bullet.h
new file mode 100644
index 0000000000..1ce5ef9826
--- /dev/null
+++ b/modules/bullet/cone_twist_joint_bullet.h
@@ -0,0 +1,58 @@
+/*************************************************************************/
+/* cone_twist_joint_bullet.h */
+/* Author: AndreaCatania */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2017 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 CONE_TWIST_JOINT_BULLET_H
+#define CONE_TWIST_JOINT_BULLET_H
+
+#include "joint_bullet.h"
+
+class RigidBodyBullet;
+
+class ConeTwistJointBullet : public JointBullet {
+ class btConeTwistConstraint *coneConstraint;
+
+public:
+ ConeTwistJointBullet(RigidBodyBullet *rbA, RigidBodyBullet *rbB, const Transform &rbAFrame, const Transform &rbBFrame);
+
+ virtual PhysicsServer::JointType get_type() const { return PhysicsServer::JOINT_CONE_TWIST; }
+
+ void set_angular_only(bool angularOnly);
+
+ void set_limit(real_t _swingSpan1, real_t _swingSpan2, real_t _twistSpan, real_t _softness = 0.8f, real_t _biasFactor = 0.3f, real_t _relaxationFactor = 1.0f);
+ int get_solve_twist_limit();
+
+ int get_solve_swing_limit();
+ real_t get_twist_limit_sign();
+
+ void set_param(PhysicsServer::ConeTwistJointParam p_param, real_t p_value);
+ real_t get_param(PhysicsServer::ConeTwistJointParam p_param) const;
+};
+#endif
diff --git a/modules/bullet/config.py b/modules/bullet/config.py
new file mode 100644
index 0000000000..b00ea18328
--- /dev/null
+++ b/modules/bullet/config.py
@@ -0,0 +1,6 @@
+def can_build(platform):
+ return True
+
+def configure(env):
+ pass
+
diff --git a/modules/bullet/constraint_bullet.cpp b/modules/bullet/constraint_bullet.cpp
new file mode 100644
index 0000000000..08fc36f274
--- /dev/null
+++ b/modules/bullet/constraint_bullet.cpp
@@ -0,0 +1,50 @@
+/*************************************************************************/
+/* constraint_bullet.cpp */
+/* Author: AndreaCatania */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2017 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 "constraint_bullet.h"
+#include "collision_object_bullet.h"
+#include "space_bullet.h"
+
+ConstraintBullet::ConstraintBullet()
+ : space(NULL), constraint(NULL) {}
+
+void ConstraintBullet::setup(btTypedConstraint *p_constraint) {
+ constraint = p_constraint;
+ constraint->setUserConstraintPtr(this);
+}
+
+void ConstraintBullet::set_space(SpaceBullet *p_space) {
+ space = p_space;
+}
+
+void ConstraintBullet::destroy_internal_constraint() {
+ space->remove_constraint(this);
+}
diff --git a/modules/bullet/constraint_bullet.h b/modules/bullet/constraint_bullet.h
new file mode 100644
index 0000000000..b528ec6d7b
--- /dev/null
+++ b/modules/bullet/constraint_bullet.h
@@ -0,0 +1,64 @@
+/*************************************************************************/
+/* constraint_bullet.h */
+/* Author: AndreaCatania */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2017 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 CONSTRAINT_BULLET_H
+#define CONSTRAINT_BULLET_H
+
+#include "BulletDynamics/ConstraintSolver/btTypedConstraint.h"
+#include "bullet_utilities.h"
+#include "rid_bullet.h"
+
+class RigidBodyBullet;
+class SpaceBullet;
+class btTypedConstraint;
+
+class ConstraintBullet : public RIDBullet {
+
+protected:
+ SpaceBullet *space;
+ btTypedConstraint *constraint;
+
+public:
+ ConstraintBullet();
+
+ virtual void setup(btTypedConstraint *p_constraint);
+ virtual void set_space(SpaceBullet *p_space);
+ virtual void destroy_internal_constraint();
+
+public:
+ virtual ~ConstraintBullet() {
+ bulletdelete(constraint);
+ constraint = NULL;
+ }
+
+ _FORCE_INLINE_ btTypedConstraint *get_bt_constraint() { return constraint; }
+};
+#endif
diff --git a/modules/bullet/generic_6dof_joint_bullet.cpp b/modules/bullet/generic_6dof_joint_bullet.cpp
new file mode 100644
index 0000000000..647396c24c
--- /dev/null
+++ b/modules/bullet/generic_6dof_joint_bullet.cpp
@@ -0,0 +1,241 @@
+/*************************************************************************/
+/* generic_6dof_joint_bullet.cpp */
+/* Author: AndreaCatania */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2017 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 "generic_6dof_joint_bullet.h"
+#include "BulletDynamics/ConstraintSolver/btGeneric6DofConstraint.h"
+#include "bullet_types_converter.h"
+#include "bullet_utilities.h"
+#include "rigid_body_bullet.h"
+
+Generic6DOFJointBullet::Generic6DOFJointBullet(RigidBodyBullet *rbA, RigidBodyBullet *rbB, const Transform &frameInA, const Transform &frameInB, bool useLinearReferenceFrameA)
+ : JointBullet() {
+
+ btTransform btFrameA;
+ G_TO_B(frameInA, btFrameA);
+
+ if (rbB) {
+ btTransform btFrameB;
+ G_TO_B(frameInB, btFrameB);
+
+ sixDOFConstraint = bulletnew(btGeneric6DofConstraint(*rbA->get_bt_rigid_body(), *rbB->get_bt_rigid_body(), btFrameA, btFrameB, useLinearReferenceFrameA));
+ } else {
+ sixDOFConstraint = bulletnew(btGeneric6DofConstraint(*rbA->get_bt_rigid_body(), btFrameA, useLinearReferenceFrameA));
+ }
+
+ setup(sixDOFConstraint);
+}
+
+Transform Generic6DOFJointBullet::getFrameOffsetA() const {
+ btTransform btTrs = sixDOFConstraint->getFrameOffsetA();
+ Transform gTrs;
+ B_TO_G(btTrs, gTrs);
+ return gTrs;
+}
+
+Transform Generic6DOFJointBullet::getFrameOffsetB() const {
+ btTransform btTrs = sixDOFConstraint->getFrameOffsetB();
+ Transform gTrs;
+ B_TO_G(btTrs, gTrs);
+ return gTrs;
+}
+
+Transform Generic6DOFJointBullet::getFrameOffsetA() {
+ btTransform btTrs = sixDOFConstraint->getFrameOffsetA();
+ Transform gTrs;
+ B_TO_G(btTrs, gTrs);
+ return gTrs;
+}
+
+Transform Generic6DOFJointBullet::getFrameOffsetB() {
+ btTransform btTrs = sixDOFConstraint->getFrameOffsetB();
+ Transform gTrs;
+ B_TO_G(btTrs, gTrs);
+ return gTrs;
+}
+
+void Generic6DOFJointBullet::set_linear_lower_limit(const Vector3 &linearLower) {
+ btVector3 btVec;
+ G_TO_B(linearLower, btVec);
+ sixDOFConstraint->setLinearLowerLimit(btVec);
+}
+
+void Generic6DOFJointBullet::set_linear_upper_limit(const Vector3 &linearUpper) {
+ btVector3 btVec;
+ G_TO_B(linearUpper, btVec);
+ sixDOFConstraint->setLinearUpperLimit(btVec);
+}
+
+void Generic6DOFJointBullet::set_angular_lower_limit(const Vector3 &angularLower) {
+ btVector3 btVec;
+ G_TO_B(angularLower, btVec);
+ sixDOFConstraint->setAngularLowerLimit(btVec);
+}
+
+void Generic6DOFJointBullet::set_angular_upper_limit(const Vector3 &angularUpper) {
+ btVector3 btVec;
+ G_TO_B(angularUpper, btVec);
+ sixDOFConstraint->setAngularUpperLimit(btVec);
+}
+
+void Generic6DOFJointBullet::set_param(Vector3::Axis p_axis, PhysicsServer::G6DOFJointAxisParam p_param, real_t p_value) {
+ ERR_FAIL_INDEX(p_axis, 3);
+ switch (p_param) {
+ case PhysicsServer::G6DOF_JOINT_LINEAR_LOWER_LIMIT:
+ sixDOFConstraint->getTranslationalLimitMotor()->m_lowerLimit[p_axis] = p_value;
+ break;
+ case PhysicsServer::G6DOF_JOINT_LINEAR_UPPER_LIMIT:
+ sixDOFConstraint->getTranslationalLimitMotor()->m_upperLimit[p_axis] = p_value;
+ break;
+ case PhysicsServer::G6DOF_JOINT_LINEAR_LIMIT_SOFTNESS:
+ sixDOFConstraint->getTranslationalLimitMotor()->m_limitSoftness = p_value;
+ break;
+ case PhysicsServer::G6DOF_JOINT_LINEAR_RESTITUTION:
+ sixDOFConstraint->getTranslationalLimitMotor()->m_restitution = p_value;
+ break;
+ case PhysicsServer::G6DOF_JOINT_LINEAR_DAMPING:
+ sixDOFConstraint->getTranslationalLimitMotor()->m_damping = p_value;
+ break;
+ case PhysicsServer::G6DOF_JOINT_ANGULAR_LOWER_LIMIT:
+ sixDOFConstraint->getRotationalLimitMotor(p_axis)->m_loLimit = p_value;
+ break;
+ case PhysicsServer::G6DOF_JOINT_ANGULAR_UPPER_LIMIT:
+ sixDOFConstraint->getRotationalLimitMotor(p_axis)->m_hiLimit = p_value;
+ break;
+ case PhysicsServer::G6DOF_JOINT_ANGULAR_LIMIT_SOFTNESS:
+ sixDOFConstraint->getRotationalLimitMotor(p_axis)->m_limitSoftness = p_value;
+ break;
+ case PhysicsServer::G6DOF_JOINT_ANGULAR_DAMPING:
+ sixDOFConstraint->getRotationalLimitMotor(p_axis)->m_damping = p_value;
+ break;
+ case PhysicsServer::G6DOF_JOINT_ANGULAR_RESTITUTION:
+ sixDOFConstraint->getRotationalLimitMotor(p_axis)->m_bounce = p_value;
+ break;
+ case PhysicsServer::G6DOF_JOINT_ANGULAR_FORCE_LIMIT:
+ sixDOFConstraint->getRotationalLimitMotor(p_axis)->m_maxLimitForce = p_value;
+ break;
+ case PhysicsServer::G6DOF_JOINT_ANGULAR_ERP:
+ sixDOFConstraint->getRotationalLimitMotor(p_axis)->m_stopERP = p_value;
+ break;
+ case PhysicsServer::G6DOF_JOINT_ANGULAR_MOTOR_TARGET_VELOCITY:
+ sixDOFConstraint->getRotationalLimitMotor(p_axis)->m_targetVelocity = p_value;
+ break;
+ case PhysicsServer::G6DOF_JOINT_ANGULAR_MOTOR_FORCE_LIMIT:
+ sixDOFConstraint->getRotationalLimitMotor(p_axis)->m_maxLimitForce = p_value;
+ break;
+ default:
+ WARN_PRINT("This parameter is not supported");
+ }
+}
+
+real_t Generic6DOFJointBullet::get_param(Vector3::Axis p_axis, PhysicsServer::G6DOFJointAxisParam p_param) const {
+ ERR_FAIL_INDEX_V(p_axis, 3, 0.);
+ switch (p_param) {
+ case PhysicsServer::G6DOF_JOINT_LINEAR_LOWER_LIMIT:
+ return sixDOFConstraint->getTranslationalLimitMotor()->m_lowerLimit[p_axis];
+ case PhysicsServer::G6DOF_JOINT_LINEAR_UPPER_LIMIT:
+ return sixDOFConstraint->getTranslationalLimitMotor()->m_upperLimit[p_axis];
+ case PhysicsServer::G6DOF_JOINT_LINEAR_LIMIT_SOFTNESS:
+ return sixDOFConstraint->getTranslationalLimitMotor()->m_limitSoftness;
+ case PhysicsServer::G6DOF_JOINT_LINEAR_RESTITUTION:
+ return sixDOFConstraint->getTranslationalLimitMotor()->m_restitution;
+ case PhysicsServer::G6DOF_JOINT_LINEAR_DAMPING:
+ return sixDOFConstraint->getTranslationalLimitMotor()->m_damping;
+ case PhysicsServer::G6DOF_JOINT_ANGULAR_LOWER_LIMIT:
+ return sixDOFConstraint->getRotationalLimitMotor(p_axis)->m_loLimit;
+ case PhysicsServer::G6DOF_JOINT_ANGULAR_UPPER_LIMIT:
+ return sixDOFConstraint->getRotationalLimitMotor(p_axis)->m_hiLimit;
+ case PhysicsServer::G6DOF_JOINT_ANGULAR_LIMIT_SOFTNESS:
+ return sixDOFConstraint->getRotationalLimitMotor(p_axis)->m_limitSoftness;
+ case PhysicsServer::G6DOF_JOINT_ANGULAR_DAMPING:
+ return sixDOFConstraint->getRotationalLimitMotor(p_axis)->m_damping;
+ case PhysicsServer::G6DOF_JOINT_ANGULAR_RESTITUTION:
+ return sixDOFConstraint->getRotationalLimitMotor(p_axis)->m_bounce;
+ case PhysicsServer::G6DOF_JOINT_ANGULAR_FORCE_LIMIT:
+ return sixDOFConstraint->getRotationalLimitMotor(p_axis)->m_maxLimitForce;
+ case PhysicsServer::G6DOF_JOINT_ANGULAR_ERP:
+ return sixDOFConstraint->getRotationalLimitMotor(p_axis)->m_stopERP;
+ case PhysicsServer::G6DOF_JOINT_ANGULAR_MOTOR_TARGET_VELOCITY:
+ return sixDOFConstraint->getRotationalLimitMotor(p_axis)->m_targetVelocity;
+ case PhysicsServer::G6DOF_JOINT_ANGULAR_MOTOR_FORCE_LIMIT:
+ return sixDOFConstraint->getRotationalLimitMotor(p_axis)->m_maxLimitForce;
+ default:
+ WARN_PRINT("This parameter is not supported");
+ return 0.;
+ }
+}
+
+void Generic6DOFJointBullet::set_flag(Vector3::Axis p_axis, PhysicsServer::G6DOFJointAxisFlag p_flag, bool p_value) {
+ ERR_FAIL_INDEX(p_axis, 3);
+ switch (p_flag) {
+ case PhysicsServer::G6DOF_JOINT_FLAG_ENABLE_LINEAR_LIMIT:
+ if (p_value) {
+ if (!get_flag(p_axis, p_flag)) // avoid overwrite, if limited
+ sixDOFConstraint->setLimit(p_axis, 0, 0); // Limited
+ } else {
+ if (get_flag(p_axis, p_flag)) // avoid overwrite, if free
+ sixDOFConstraint->setLimit(p_axis, 0, -1); // Free
+ }
+ break;
+ case PhysicsServer::G6DOF_JOINT_FLAG_ENABLE_ANGULAR_LIMIT: {
+ int angularAxis = 3 + p_axis;
+ if (p_value) {
+ if (!get_flag(p_axis, p_flag)) // avoid overwrite, if Limited
+ sixDOFConstraint->setLimit(angularAxis, 0, 0); // Limited
+ } else {
+ if (get_flag(p_axis, p_flag)) // avoid overwrite, if free
+ sixDOFConstraint->setLimit(angularAxis, 0, -1); // Free
+ }
+ break;
+ }
+ case PhysicsServer::G6DOF_JOINT_FLAG_ENABLE_MOTOR:
+ //sixDOFConstraint->getTranslationalLimitMotor()->m_enableMotor[p_axis] = p_value;
+ sixDOFConstraint->getRotationalLimitMotor(p_axis)->m_enableMotor = p_value;
+ break;
+ default:
+ WARN_PRINT("This flag is not supported by Bullet engine");
+ }
+}
+
+bool Generic6DOFJointBullet::get_flag(Vector3::Axis p_axis, PhysicsServer::G6DOFJointAxisFlag p_flag) const {
+ ERR_FAIL_INDEX_V(p_axis, 3, false);
+ switch (p_flag) {
+ case PhysicsServer::G6DOF_JOINT_FLAG_ENABLE_LINEAR_LIMIT:
+ return sixDOFConstraint->getTranslationalLimitMotor()->isLimited(p_axis);
+ case PhysicsServer::G6DOF_JOINT_FLAG_ENABLE_ANGULAR_LIMIT:
+ return sixDOFConstraint->getRotationalLimitMotor(p_axis)->isLimited();
+ case PhysicsServer::G6DOF_JOINT_FLAG_ENABLE_MOTOR:
+ return //sixDOFConstraint->getTranslationalLimitMotor()->m_enableMotor[p_axis] &&
+ sixDOFConstraint->getRotationalLimitMotor(p_axis)->m_enableMotor;
+ default:
+ WARN_PRINT("This flag is not supported by Bullet engine");
+ return false;
+ }
+}
diff --git a/modules/bullet/generic_6dof_joint_bullet.h b/modules/bullet/generic_6dof_joint_bullet.h
new file mode 100644
index 0000000000..0d47b823de
--- /dev/null
+++ b/modules/bullet/generic_6dof_joint_bullet.h
@@ -0,0 +1,65 @@
+/*************************************************************************/
+/* generic_6dof_joint_bullet.h */
+/* Author: AndreaCatania */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2017 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 GENERIC_6DOF_JOINT_BULLET_H
+#define GENERIC_6DOF_JOINT_BULLET_H
+
+#include "joint_bullet.h"
+
+class RigidBodyBullet;
+
+class Generic6DOFJointBullet : public JointBullet {
+ class btGeneric6DofConstraint *sixDOFConstraint;
+
+public:
+ Generic6DOFJointBullet(RigidBodyBullet *rbA, RigidBodyBullet *rbB, const Transform &frameInA, const Transform &frameInB, bool useLinearReferenceFrameA);
+
+ virtual PhysicsServer::JointType get_type() const { return PhysicsServer::JOINT_6DOF; }
+
+ Transform getFrameOffsetA() const;
+ Transform getFrameOffsetB() const;
+ Transform getFrameOffsetA();
+ Transform getFrameOffsetB();
+
+ void set_linear_lower_limit(const Vector3 &linearLower);
+ void set_linear_upper_limit(const Vector3 &linearUpper);
+
+ void set_angular_lower_limit(const Vector3 &angularLower);
+ void set_angular_upper_limit(const Vector3 &angularUpper);
+
+ void set_param(Vector3::Axis p_axis, PhysicsServer::G6DOFJointAxisParam p_param, real_t p_value);
+ real_t get_param(Vector3::Axis p_axis, PhysicsServer::G6DOFJointAxisParam p_param) const;
+
+ void set_flag(Vector3::Axis p_axis, PhysicsServer::G6DOFJointAxisFlag p_flag, bool p_value);
+ bool get_flag(Vector3::Axis p_axis, PhysicsServer::G6DOFJointAxisFlag p_flag) const;
+};
+
+#endif
diff --git a/modules/bullet/godot_collision_configuration.cpp b/modules/bullet/godot_collision_configuration.cpp
new file mode 100644
index 0000000000..4e4228cc48
--- /dev/null
+++ b/modules/bullet/godot_collision_configuration.cpp
@@ -0,0 +1,91 @@
+/*************************************************************************/
+/* godot_collision_configuration.cpp */
+/* Author: AndreaCatania */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2017 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 "godot_collision_configuration.h"
+#include "BulletCollision/BroadphaseCollision/btBroadphaseProxy.h"
+#include "BulletDynamics/Dynamics/btDiscreteDynamicsWorld.h"
+#include "godot_ray_world_algorithm.h"
+
+GodotCollisionConfiguration::GodotCollisionConfiguration(const btDiscreteDynamicsWorld *world, const btDefaultCollisionConstructionInfo &constructionInfo)
+ : btDefaultCollisionConfiguration(constructionInfo) {
+
+ void *mem = NULL;
+
+ mem = btAlignedAlloc(sizeof(GodotRayWorldAlgorithm::CreateFunc), 16);
+ m_rayWorldCF = new (mem) GodotRayWorldAlgorithm::CreateFunc(world);
+
+ mem = btAlignedAlloc(sizeof(GodotRayWorldAlgorithm::SwappedCreateFunc), 16);
+ m_swappedRayWorldCF = new (mem) GodotRayWorldAlgorithm::SwappedCreateFunc(world);
+}
+
+GodotCollisionConfiguration::~GodotCollisionConfiguration() {
+ m_rayWorldCF->~btCollisionAlgorithmCreateFunc();
+ btAlignedFree(m_rayWorldCF);
+
+ m_swappedRayWorldCF->~btCollisionAlgorithmCreateFunc();
+ btAlignedFree(m_swappedRayWorldCF);
+}
+
+btCollisionAlgorithmCreateFunc *GodotCollisionConfiguration::getCollisionAlgorithmCreateFunc(int proxyType0, int proxyType1) {
+
+ if (CUSTOM_CONVEX_SHAPE_TYPE == proxyType0 && CUSTOM_CONVEX_SHAPE_TYPE == proxyType1) {
+
+ // This collision is not supported
+ return m_emptyCreateFunc;
+ } else if (CUSTOM_CONVEX_SHAPE_TYPE == proxyType0) {
+
+ return m_rayWorldCF;
+ } else if (CUSTOM_CONVEX_SHAPE_TYPE == proxyType1) {
+
+ return m_swappedRayWorldCF;
+ } else {
+
+ return btDefaultCollisionConfiguration::getCollisionAlgorithmCreateFunc(proxyType0, proxyType1);
+ }
+}
+
+btCollisionAlgorithmCreateFunc *GodotCollisionConfiguration::getClosestPointsAlgorithmCreateFunc(int proxyType0, int proxyType1) {
+
+ if (CUSTOM_CONVEX_SHAPE_TYPE == proxyType0 && CUSTOM_CONVEX_SHAPE_TYPE == proxyType1) {
+
+ // This collision is not supported
+ return m_emptyCreateFunc;
+ } else if (CUSTOM_CONVEX_SHAPE_TYPE == proxyType0) {
+
+ return m_rayWorldCF;
+ } else if (CUSTOM_CONVEX_SHAPE_TYPE == proxyType1) {
+
+ return m_swappedRayWorldCF;
+ } else {
+
+ return btDefaultCollisionConfiguration::getClosestPointsAlgorithmCreateFunc(proxyType0, proxyType1);
+ }
+}
diff --git a/modules/bullet/godot_collision_configuration.h b/modules/bullet/godot_collision_configuration.h
new file mode 100644
index 0000000000..ed99065f8c
--- /dev/null
+++ b/modules/bullet/godot_collision_configuration.h
@@ -0,0 +1,50 @@
+/*************************************************************************/
+/* godot_collision_configuration.h */
+/* Author: AndreaCatania */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2017 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 GODOT_COLLISION_CONFIGURATION_H
+#define GODOT_COLLISION_CONFIGURATION_H
+
+#include "BulletCollision/CollisionDispatch/btDefaultCollisionConfiguration.h"
+
+class btDiscreteDynamicsWorld;
+
+class GodotCollisionConfiguration : public btDefaultCollisionConfiguration {
+ btCollisionAlgorithmCreateFunc *m_rayWorldCF;
+ btCollisionAlgorithmCreateFunc *m_swappedRayWorldCF;
+
+public:
+ GodotCollisionConfiguration(const btDiscreteDynamicsWorld *world, const btDefaultCollisionConstructionInfo &constructionInfo = btDefaultCollisionConstructionInfo());
+ virtual ~GodotCollisionConfiguration();
+
+ virtual btCollisionAlgorithmCreateFunc *getCollisionAlgorithmCreateFunc(int proxyType0, int proxyType1);
+ virtual btCollisionAlgorithmCreateFunc *getClosestPointsAlgorithmCreateFunc(int proxyType0, int proxyType1);
+};
+#endif
diff --git a/modules/bullet/godot_collision_dispatcher.cpp b/modules/bullet/godot_collision_dispatcher.cpp
new file mode 100644
index 0000000000..ea75e4eef4
--- /dev/null
+++ b/modules/bullet/godot_collision_dispatcher.cpp
@@ -0,0 +1,54 @@
+/*************************************************************************/
+/* godot_collision_dispatcher.cpp */
+/* Author: AndreaCatania */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2017 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 "godot_collision_dispatcher.h"
+#include "collision_object_bullet.h"
+
+const int GodotCollisionDispatcher::CASTED_TYPE_AREA = static_cast<int>(CollisionObjectBullet::TYPE_AREA);
+
+GodotCollisionDispatcher::GodotCollisionDispatcher(btCollisionConfiguration *collisionConfiguration)
+ : btCollisionDispatcher(collisionConfiguration) {}
+
+bool GodotCollisionDispatcher::needsCollision(const btCollisionObject *body0, const btCollisionObject *body1) {
+ if (body0->getUserIndex() == CASTED_TYPE_AREA || body1->getUserIndex() == CASTED_TYPE_AREA) {
+ // Avoide area narrow phase
+ return false;
+ }
+ return btCollisionDispatcher::needsCollision(body0, body1);
+}
+
+bool GodotCollisionDispatcher::needsResponse(const btCollisionObject *body0, const btCollisionObject *body1) {
+ if (body0->getUserIndex() == CASTED_TYPE_AREA || body1->getUserIndex() == CASTED_TYPE_AREA) {
+ // Avoide area narrow phase
+ return false;
+ }
+ return btCollisionDispatcher::needsResponse(body0, body1);
+}
diff --git a/modules/bullet/godot_collision_dispatcher.h b/modules/bullet/godot_collision_dispatcher.h
new file mode 100644
index 0000000000..501b2078dd
--- /dev/null
+++ b/modules/bullet/godot_collision_dispatcher.h
@@ -0,0 +1,48 @@
+/*************************************************************************/
+/* godot_collision_dispatcher.h */
+/* Author: AndreaCatania */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2017 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 GODOT_COLLISION_DISPATCHER_H
+#define GODOT_COLLISION_DISPATCHER_H
+
+#include "int_types.h"
+#include <btBulletDynamicsCommon.h>
+
+/// This class is required to implement custom collision behaviour in the narrowphase
+class GodotCollisionDispatcher : public btCollisionDispatcher {
+private:
+ static const int CASTED_TYPE_AREA;
+
+public:
+ GodotCollisionDispatcher(btCollisionConfiguration *collisionConfiguration);
+ virtual bool needsCollision(const btCollisionObject *body0, const btCollisionObject *body1);
+ virtual bool needsResponse(const btCollisionObject *body0, const btCollisionObject *body1);
+};
+#endif
diff --git a/modules/bullet/godot_motion_state.h b/modules/bullet/godot_motion_state.h
new file mode 100644
index 0000000000..5111807394
--- /dev/null
+++ b/modules/bullet/godot_motion_state.h
@@ -0,0 +1,96 @@
+/*************************************************************************/
+/* godot_motion_state.h */
+/* Author: AndreaCatania */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2017 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 GODOT_MOTION_STATE_H
+#define GODOT_MOTION_STATE_H
+
+#include "LinearMath/btMotionState.h"
+#include "rigid_body_bullet.h"
+
+class RigidBodyBullet;
+
+// This clas is responsible to move kinematic actor
+// and sincronize rendering engine with Bullet
+/// DOC:
+/// http://www.bulletphysics.org/mediawiki-1.5.8/index.php/MotionStates#What.27s_a_MotionState.3F
+class GodotMotionState : public btMotionState {
+
+ /// This data is used to store the new world position for kinematic body
+ btTransform bodyKinematicWorldTransf;
+ /// This data is used to store last world position
+ btTransform bodyCurrentWorldTransform;
+
+ RigidBodyBullet *owner;
+
+public:
+ GodotMotionState(RigidBodyBullet *p_owner)
+ : bodyKinematicWorldTransf(btMatrix3x3(1., 0., 0., 0., 1., 0., 0., 0., 1.), btVector3(0., 0., 0.)),
+ bodyCurrentWorldTransform(btMatrix3x3(1., 0., 0., 0., 1., 0., 0., 0., 1.), btVector3(0., 0., 0.)),
+ owner(p_owner) {}
+
+ /// IMPORTANT DON'T USE THIS FUNCTION TO KNOW THE CURRENT BODY TRANSFORM
+ /// This class is used internally by Bullet
+ /// Use GodotMotionState::getCurrentWorldTransform to know current position
+ ///
+ /// This function is used by Bullet to get the position of object in the world
+ /// if the body is kinematic Bullet will move the object to this location
+ /// if the body is static Bullet doesn't move at all
+ virtual void getWorldTransform(btTransform &worldTrans) const {
+ worldTrans = bodyKinematicWorldTransf;
+ }
+
+ /// IMPORTANT: to move the body use: moveBody
+ /// IMPORTANT: DON'T CALL THIS FUNCTION, IT IS CALLED BY BULLET TO UPDATE RENDERING ENGINE
+ ///
+ /// This function is called each time by Bullet and set the current position of body
+ /// inside the physics world.
+ /// Don't allow Godot rendering scene takes world transform from this object because
+ /// the correct transform is set by Bullet only after the last step when there are sub steps
+ /// This function must update Godot transform rendering scene for this object.
+ virtual void setWorldTransform(const btTransform &worldTrans) {
+ bodyCurrentWorldTransform = worldTrans;
+
+ owner->scratch();
+ }
+
+public:
+ /// Use this function to move kinematic body
+ /// -- or set initial transfom before body creation.
+ void moveBody(const btTransform &newWorldTransform) {
+ bodyKinematicWorldTransf = newWorldTransform;
+ }
+
+ /// It returns the current body transform from last Bullet update
+ const btTransform &getCurrentWorldTransform() const {
+ return bodyCurrentWorldTransform;
+ }
+};
+#endif
diff --git a/modules/bullet/godot_ray_world_algorithm.cpp b/modules/bullet/godot_ray_world_algorithm.cpp
new file mode 100644
index 0000000000..98daf8398e
--- /dev/null
+++ b/modules/bullet/godot_ray_world_algorithm.cpp
@@ -0,0 +1,104 @@
+/*************************************************************************/
+/* godot_ray_world_algorithm.cpp */
+/* Author: AndreaCatania */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2017 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 "godot_ray_world_algorithm.h"
+#include "BulletDynamics/Dynamics/btDiscreteDynamicsWorld.h"
+#include "btRayShape.h"
+#include "collision_object_bullet.h"
+
+GodotRayWorldAlgorithm::CreateFunc::CreateFunc(const btDiscreteDynamicsWorld *world)
+ : m_world(world) {}
+
+GodotRayWorldAlgorithm::SwappedCreateFunc::SwappedCreateFunc(const btDiscreteDynamicsWorld *world)
+ : m_world(world) {}
+
+GodotRayWorldAlgorithm::GodotRayWorldAlgorithm(const btDiscreteDynamicsWorld *world, btPersistentManifold *mf, const btCollisionAlgorithmConstructionInfo &ci, const btCollisionObjectWrapper *body0Wrap, const btCollisionObjectWrapper *body1Wrap, bool isSwapped)
+ : btActivatingCollisionAlgorithm(ci, body0Wrap, body1Wrap),
+ m_manifoldPtr(mf),
+ m_ownManifold(false),
+ m_world(world),
+ m_isSwapped(isSwapped) {}
+
+GodotRayWorldAlgorithm::~GodotRayWorldAlgorithm() {
+ if (m_ownManifold && m_manifoldPtr) {
+ m_dispatcher->releaseManifold(m_manifoldPtr);
+ }
+}
+
+void GodotRayWorldAlgorithm::processCollision(const btCollisionObjectWrapper *body0Wrap, const btCollisionObjectWrapper *body1Wrap, const btDispatcherInfo &dispatchInfo, btManifoldResult *resultOut) {
+
+ if (!m_manifoldPtr) {
+ if (m_isSwapped) {
+ m_manifoldPtr = m_dispatcher->getNewManifold(body1Wrap->getCollisionObject(), body0Wrap->getCollisionObject());
+ } else {
+ m_manifoldPtr = m_dispatcher->getNewManifold(body0Wrap->getCollisionObject(), body1Wrap->getCollisionObject());
+ }
+ m_ownManifold = true;
+ }
+ m_manifoldPtr->clearManifold();
+ resultOut->setPersistentManifold(m_manifoldPtr);
+
+ const btRayShape *ray_shape;
+ btTransform ray_transform;
+
+ const btCollisionObjectWrapper *other_co_wrapper;
+
+ if (m_isSwapped) {
+
+ ray_shape = static_cast<const btRayShape *>(body1Wrap->getCollisionShape());
+ ray_transform = body1Wrap->getWorldTransform();
+
+ other_co_wrapper = body0Wrap;
+ } else {
+
+ ray_shape = static_cast<const btRayShape *>(body0Wrap->getCollisionShape());
+ ray_transform = body0Wrap->getWorldTransform();
+
+ other_co_wrapper = body1Wrap;
+ }
+
+ btTransform to(ray_transform * ray_shape->getSupportPoint());
+
+ btCollisionWorld::ClosestRayResultCallback btResult(ray_transform.getOrigin(), to.getOrigin());
+
+ m_world->rayTestSingleInternal(ray_transform, to, other_co_wrapper, btResult);
+
+ if (btResult.hasHit()) {
+ btVector3 ray_normal(to.getOrigin() - ray_transform.getOrigin());
+ ray_normal.normalize();
+ ray_normal *= -1;
+ resultOut->addContactPoint(ray_normal, btResult.m_hitPointWorld, ray_shape->getScaledLength() * (btResult.m_closestHitFraction - 1));
+ }
+}
+
+btScalar GodotRayWorldAlgorithm::calculateTimeOfImpact(btCollisionObject *body0, btCollisionObject *body1, const btDispatcherInfo &dispatchInfo, btManifoldResult *resultOut) {
+ return 1;
+}
diff --git a/modules/bullet/godot_ray_world_algorithm.h b/modules/bullet/godot_ray_world_algorithm.h
new file mode 100644
index 0000000000..15c71b8d7d
--- /dev/null
+++ b/modules/bullet/godot_ray_world_algorithm.h
@@ -0,0 +1,83 @@
+/*************************************************************************/
+/* godot_ray_world_algorithm.h */
+/* Author: AndreaCatania */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2017 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 GODOT_RAY_WORLD_ALGORITHM_H
+#define GODOT_RAY_WORLD_ALGORITHM_H
+
+#include "BulletCollision/CollisionDispatch/btActivatingCollisionAlgorithm.h"
+#include "BulletCollision/CollisionDispatch/btCollisionCreateFunc.h"
+#include "BulletCollision/CollisionDispatch/btCollisionDispatcher.h"
+
+class btDiscreteDynamicsWorld;
+
+class GodotRayWorldAlgorithm : public btActivatingCollisionAlgorithm {
+
+ const btDiscreteDynamicsWorld *m_world;
+ btPersistentManifold *m_manifoldPtr;
+ bool m_ownManifold;
+ bool m_isSwapped;
+
+public:
+ GodotRayWorldAlgorithm(const btDiscreteDynamicsWorld *m_world, btPersistentManifold *mf, const btCollisionAlgorithmConstructionInfo &ci, const btCollisionObjectWrapper *body0Wrap, const btCollisionObjectWrapper *body1Wrap, bool isSwapped);
+ virtual ~GodotRayWorldAlgorithm();
+
+ virtual void processCollision(const btCollisionObjectWrapper *body0Wrap, const btCollisionObjectWrapper *body1Wrap, const btDispatcherInfo &dispatchInfo, btManifoldResult *resultOut);
+ virtual btScalar calculateTimeOfImpact(btCollisionObject *body0, btCollisionObject *body1, const btDispatcherInfo &dispatchInfo, btManifoldResult *resultOut);
+
+ virtual void getAllContactManifolds(btManifoldArray &manifoldArray) {
+ ///should we use m_ownManifold to avoid adding duplicates?
+ if (m_manifoldPtr && m_ownManifold)
+ manifoldArray.push_back(m_manifoldPtr);
+ }
+ struct CreateFunc : public btCollisionAlgorithmCreateFunc {
+
+ const btDiscreteDynamicsWorld *m_world;
+ CreateFunc(const btDiscreteDynamicsWorld *world);
+
+ virtual btCollisionAlgorithm *CreateCollisionAlgorithm(btCollisionAlgorithmConstructionInfo &ci, const btCollisionObjectWrapper *body0Wrap, const btCollisionObjectWrapper *body1Wrap) {
+ void *mem = ci.m_dispatcher1->allocateCollisionAlgorithm(sizeof(GodotRayWorldAlgorithm));
+ return new (mem) GodotRayWorldAlgorithm(m_world, ci.m_manifold, ci, body0Wrap, body1Wrap, false);
+ }
+ };
+
+ struct SwappedCreateFunc : public btCollisionAlgorithmCreateFunc {
+
+ const btDiscreteDynamicsWorld *m_world;
+ SwappedCreateFunc(const btDiscreteDynamicsWorld *world);
+
+ virtual btCollisionAlgorithm *CreateCollisionAlgorithm(btCollisionAlgorithmConstructionInfo &ci, const btCollisionObjectWrapper *body0Wrap, const btCollisionObjectWrapper *body1Wrap) {
+ void *mem = ci.m_dispatcher1->allocateCollisionAlgorithm(sizeof(GodotRayWorldAlgorithm));
+ return new (mem) GodotRayWorldAlgorithm(m_world, ci.m_manifold, ci, body0Wrap, body1Wrap, true);
+ }
+ };
+};
+
+#endif // GODOT_RAY_WORLD_ALGORITHM_H
diff --git a/modules/bullet/godot_result_callbacks.cpp b/modules/bullet/godot_result_callbacks.cpp
new file mode 100644
index 0000000000..409d12f080
--- /dev/null
+++ b/modules/bullet/godot_result_callbacks.cpp
@@ -0,0 +1,291 @@
+/*************************************************************************/
+/* godot_result_callbacks.cpp */
+/* Author: AndreaCatania */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2017 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 "godot_result_callbacks.h"
+#include "bullet_types_converter.h"
+#include "collision_object_bullet.h"
+#include "rigid_body_bullet.h"
+
+bool GodotFilterCallback::test_collision_filters(uint32_t body0_collision_layer, uint32_t body0_collision_mask, uint32_t body1_collision_layer, uint32_t body1_collision_mask) {
+ return body0_collision_layer & body1_collision_mask || body1_collision_layer & body0_collision_mask;
+}
+
+bool GodotFilterCallback::needBroadphaseCollision(btBroadphaseProxy *proxy0, btBroadphaseProxy *proxy1) const {
+ return GodotFilterCallback::test_collision_filters(proxy0->m_collisionFilterGroup, proxy0->m_collisionFilterMask, proxy1->m_collisionFilterGroup, proxy1->m_collisionFilterMask);
+}
+
+bool GodotClosestRayResultCallback::needsCollision(btBroadphaseProxy *proxy0) const {
+ const bool needs = GodotFilterCallback::test_collision_filters(m_collisionFilterGroup, m_collisionFilterMask, proxy0->m_collisionFilterGroup, proxy0->m_collisionFilterMask);
+ if (needs) {
+ btCollisionObject *btObj = static_cast<btCollisionObject *>(proxy0->m_clientObject);
+ CollisionObjectBullet *gObj = static_cast<CollisionObjectBullet *>(btObj->getUserPointer());
+ if (m_pickRay && gObj->is_ray_pickable()) {
+ return true;
+ } else if (m_exclude->has(gObj->get_self())) {
+ return false;
+ }
+ return true;
+ } else {
+ return false;
+ }
+}
+
+bool GodotAllConvexResultCallback::needsCollision(btBroadphaseProxy *proxy0) const {
+ const bool needs = GodotFilterCallback::test_collision_filters(m_collisionFilterGroup, m_collisionFilterMask, proxy0->m_collisionFilterGroup, proxy0->m_collisionFilterMask);
+ if (needs) {
+ btCollisionObject *btObj = static_cast<btCollisionObject *>(proxy0->m_clientObject);
+ CollisionObjectBullet *gObj = static_cast<CollisionObjectBullet *>(btObj->getUserPointer());
+ if (m_exclude->has(gObj->get_self())) {
+ return false;
+ }
+ return true;
+ } else {
+ return false;
+ }
+}
+
+btScalar GodotAllConvexResultCallback::addSingleResult(btCollisionWorld::LocalConvexResult &convexResult, bool normalInWorldSpace) {
+ CollisionObjectBullet *gObj = static_cast<CollisionObjectBullet *>(convexResult.m_hitCollisionObject->getUserPointer());
+
+ PhysicsDirectSpaceState::ShapeResult &result = m_results[count];
+
+ result.shape = convexResult.m_localShapeInfo->m_shapePart;
+ result.rid = gObj->get_self();
+ result.collider_id = gObj->get_instance_id();
+ result.collider = 0 == result.collider_id ? NULL : ObjectDB::get_instance(result.collider_id);
+
+ ++count;
+ return count < m_resultMax;
+}
+
+bool GodotKinClosestConvexResultCallback::needsCollision(btBroadphaseProxy *proxy0) const {
+ const bool needs = GodotFilterCallback::test_collision_filters(m_collisionFilterGroup, m_collisionFilterMask, proxy0->m_collisionFilterGroup, proxy0->m_collisionFilterMask);
+ if (needs) {
+ btCollisionObject *btObj = static_cast<btCollisionObject *>(proxy0->m_clientObject);
+ CollisionObjectBullet *gObj = static_cast<CollisionObjectBullet *>(btObj->getUserPointer());
+ if (gObj == m_self_object) {
+ return false;
+ } else {
+ if (m_ignore_areas && gObj->getType() == CollisionObjectBullet::TYPE_AREA) {
+ return false;
+ } else if (m_self_object->has_collision_exception(gObj)) {
+ return false;
+ }
+ }
+ return true;
+ } else {
+ return false;
+ }
+}
+
+bool GodotClosestConvexResultCallback::needsCollision(btBroadphaseProxy *proxy0) const {
+ const bool needs = GodotFilterCallback::test_collision_filters(m_collisionFilterGroup, m_collisionFilterMask, proxy0->m_collisionFilterGroup, proxy0->m_collisionFilterMask);
+ if (needs) {
+ btCollisionObject *btObj = static_cast<btCollisionObject *>(proxy0->m_clientObject);
+ CollisionObjectBullet *gObj = static_cast<CollisionObjectBullet *>(btObj->getUserPointer());
+ if (m_exclude->has(gObj->get_self())) {
+ return false;
+ }
+ return true;
+ } else {
+ return false;
+ }
+}
+
+btScalar GodotClosestConvexResultCallback::addSingleResult(btCollisionWorld::LocalConvexResult &convexResult, bool normalInWorldSpace) {
+ btScalar res = btCollisionWorld::ClosestConvexResultCallback::addSingleResult(convexResult, normalInWorldSpace);
+ m_shapePart = convexResult.m_localShapeInfo->m_shapePart;
+ return res;
+}
+
+bool GodotAllContactResultCallback::needsCollision(btBroadphaseProxy *proxy0) const {
+ const bool needs = GodotFilterCallback::test_collision_filters(m_collisionFilterGroup, m_collisionFilterMask, proxy0->m_collisionFilterGroup, proxy0->m_collisionFilterMask);
+ if (needs) {
+ btCollisionObject *btObj = static_cast<btCollisionObject *>(proxy0->m_clientObject);
+ CollisionObjectBullet *gObj = static_cast<CollisionObjectBullet *>(btObj->getUserPointer());
+ if (m_exclude->has(gObj->get_self())) {
+ return false;
+ }
+ return true;
+ } else {
+ return false;
+ }
+}
+
+btScalar GodotAllContactResultCallback::addSingleResult(btManifoldPoint &cp, const btCollisionObjectWrapper *colObj0Wrap, int partId0, int index0, const btCollisionObjectWrapper *colObj1Wrap, int partId1, int index1) {
+
+ if (cp.getDistance() <= 0) {
+
+ PhysicsDirectSpaceState::ShapeResult &result = m_results[m_count];
+ // Penetrated
+
+ CollisionObjectBullet *colObj;
+ if (m_self_object == colObj0Wrap->getCollisionObject()) {
+ colObj = static_cast<CollisionObjectBullet *>(colObj1Wrap->getCollisionObject()->getUserPointer());
+ result.shape = cp.m_index1;
+ } else {
+ colObj = static_cast<CollisionObjectBullet *>(colObj0Wrap->getCollisionObject()->getUserPointer());
+ result.shape = cp.m_index0;
+ }
+
+ if (colObj)
+ result.collider_id = colObj->get_instance_id();
+ else
+ result.collider_id = 0;
+ result.collider = 0 == result.collider_id ? NULL : ObjectDB::get_instance(result.collider_id);
+ result.rid = colObj->get_self();
+ ++m_count;
+ }
+
+ return m_count < m_resultMax;
+}
+
+bool GodotContactPairContactResultCallback::needsCollision(btBroadphaseProxy *proxy0) const {
+ const bool needs = GodotFilterCallback::test_collision_filters(m_collisionFilterGroup, m_collisionFilterMask, proxy0->m_collisionFilterGroup, proxy0->m_collisionFilterMask);
+ if (needs) {
+ btCollisionObject *btObj = static_cast<btCollisionObject *>(proxy0->m_clientObject);
+ CollisionObjectBullet *gObj = static_cast<CollisionObjectBullet *>(btObj->getUserPointer());
+ if (m_exclude->has(gObj->get_self())) {
+ return false;
+ }
+ return true;
+ } else {
+ return false;
+ }
+}
+
+btScalar GodotContactPairContactResultCallback::addSingleResult(btManifoldPoint &cp, const btCollisionObjectWrapper *colObj0Wrap, int partId0, int index0, const btCollisionObjectWrapper *colObj1Wrap, int partId1, int index1) {
+
+ if (m_self_object == colObj0Wrap->getCollisionObject()) {
+ B_TO_G(cp.m_localPointA, m_results[m_count * 2 + 0]); // Local contact
+ B_TO_G(cp.m_localPointB, m_results[m_count * 2 + 1]);
+ } else {
+ B_TO_G(cp.m_localPointB, m_results[m_count * 2 + 0]); // Local contact
+ B_TO_G(cp.m_localPointA, m_results[m_count * 2 + 1]);
+ }
+
+ ++m_count;
+
+ return m_count < m_resultMax;
+}
+
+bool GodotRestInfoContactResultCallback::needsCollision(btBroadphaseProxy *proxy0) const {
+ const bool needs = GodotFilterCallback::test_collision_filters(m_collisionFilterGroup, m_collisionFilterMask, proxy0->m_collisionFilterGroup, proxy0->m_collisionFilterMask);
+ if (needs) {
+ btCollisionObject *btObj = static_cast<btCollisionObject *>(proxy0->m_clientObject);
+ CollisionObjectBullet *gObj = static_cast<CollisionObjectBullet *>(btObj->getUserPointer());
+ if (m_exclude->has(gObj->get_self())) {
+ return false;
+ }
+ return true;
+ } else {
+ return false;
+ }
+}
+
+btScalar GodotRestInfoContactResultCallback::addSingleResult(btManifoldPoint &cp, const btCollisionObjectWrapper *colObj0Wrap, int partId0, int index0, const btCollisionObjectWrapper *colObj1Wrap, int partId1, int index1) {
+
+ if (cp.getDistance() <= m_min_distance) {
+ m_min_distance = cp.getDistance();
+
+ CollisionObjectBullet *colObj;
+ if (m_self_object == colObj0Wrap->getCollisionObject()) {
+ colObj = static_cast<CollisionObjectBullet *>(colObj1Wrap->getCollisionObject()->getUserPointer());
+ m_result->shape = cp.m_index1;
+ B_TO_G(cp.getPositionWorldOnB(), m_result->point);
+ m_rest_info_bt_point = cp.getPositionWorldOnB();
+ m_rest_info_collision_object = colObj1Wrap->getCollisionObject();
+ } else {
+ colObj = static_cast<CollisionObjectBullet *>(colObj0Wrap->getCollisionObject()->getUserPointer());
+ m_result->shape = cp.m_index0;
+ B_TO_G(cp.m_normalWorldOnB * -1, m_result->normal);
+ m_rest_info_bt_point = cp.getPositionWorldOnA();
+ m_rest_info_collision_object = colObj0Wrap->getCollisionObject();
+ }
+
+ if (colObj)
+ m_result->collider_id = colObj->get_instance_id();
+ else
+ m_result->collider_id = 0;
+ m_result->rid = colObj->get_self();
+
+ m_collided = true;
+ }
+
+ return cp.getDistance();
+}
+
+bool GodotRecoverAndClosestContactResultCallback::needsCollision(btBroadphaseProxy *proxy0) const {
+ const bool needs = GodotFilterCallback::test_collision_filters(m_collisionFilterGroup, m_collisionFilterMask, proxy0->m_collisionFilterGroup, proxy0->m_collisionFilterMask);
+ if (needs) {
+ btCollisionObject *btObj = static_cast<btCollisionObject *>(proxy0->m_clientObject);
+ CollisionObjectBullet *gObj = static_cast<CollisionObjectBullet *>(btObj->getUserPointer());
+ if (gObj == m_self_object) {
+ return false;
+ } else {
+ if (m_ignore_areas && gObj->getType() == CollisionObjectBullet::TYPE_AREA) {
+ return false;
+ } else if (m_self_object->has_collision_exception(gObj)) {
+ return false;
+ }
+ }
+ return true;
+ } else {
+ return false;
+ }
+}
+
+btScalar GodotRecoverAndClosestContactResultCallback::addSingleResult(btManifoldPoint &cp, const btCollisionObjectWrapper *colObj0Wrap, int partId0, int index0, const btCollisionObjectWrapper *colObj1Wrap, int partId1, int index1) {
+
+ if (cp.getDistance() < -MAX_PENETRATION_DEPTH) {
+ if (m_most_penetrated_distance > cp.getDistance()) {
+ m_most_penetrated_distance = cp.getDistance();
+
+ // take other object
+ btScalar sign(1);
+ if (m_self_object == colObj0Wrap->getCollisionObject()->getUserPointer()) {
+ m_pointCollisionObject = colObj1Wrap->getCollisionObject();
+ m_other_compound_shape_index = cp.m_index1;
+ } else {
+ m_pointCollisionObject = colObj0Wrap->getCollisionObject();
+ sign = -1;
+ m_other_compound_shape_index = cp.m_index0;
+ }
+
+ m_pointNormalWorld = cp.m_normalWorldOnB * sign;
+ m_pointWorld = cp.getPositionWorldOnB();
+ m_penetration_distance = cp.getDistance();
+
+ m_recover_penetration -= cp.m_normalWorldOnB * sign * (cp.getDistance() + MAX_PENETRATION_DEPTH);
+ }
+ }
+ return 1;
+}
diff --git a/modules/bullet/godot_result_callbacks.h b/modules/bullet/godot_result_callbacks.h
new file mode 100644
index 0000000000..d505bd0de1
--- /dev/null
+++ b/modules/bullet/godot_result_callbacks.h
@@ -0,0 +1,189 @@
+/*************************************************************************/
+/* godot_result_callbacks.h */
+/* Author: AndreaCatania */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2017 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 GODOT_RESULT_CALLBACKS_H
+#define GODOT_RESULT_CALLBACKS_H
+
+#include "BulletCollision/BroadphaseCollision/btBroadphaseProxy.h"
+#include "btBulletDynamicsCommon.h"
+#include "servers/physics_server.h"
+
+#define MAX_PENETRATION_DEPTH 0.005
+
+class RigidBodyBullet;
+
+/// This class is required to implement custom collision behaviour in the broadphase
+struct GodotFilterCallback : public btOverlapFilterCallback {
+ static bool test_collision_filters(uint32_t body0_collision_layer, uint32_t body0_collision_mask, uint32_t body1_collision_layer, uint32_t body1_collision_mask);
+
+ // return true when pairs need collision
+ virtual bool needBroadphaseCollision(btBroadphaseProxy *proxy0, btBroadphaseProxy *proxy1) const;
+};
+
+/// It performs an additional check allow exclusions.
+struct GodotClosestRayResultCallback : public btCollisionWorld::ClosestRayResultCallback {
+ const Set<RID> *m_exclude;
+ bool m_pickRay;
+
+public:
+ GodotClosestRayResultCallback(const btVector3 &rayFromWorld, const btVector3 &rayToWorld, const Set<RID> *p_exclude)
+ : btCollisionWorld::ClosestRayResultCallback(rayFromWorld, rayToWorld), m_exclude(p_exclude), m_pickRay(false) {}
+
+ virtual bool needsCollision(btBroadphaseProxy *proxy0) const;
+};
+
+// store all colliding object
+struct GodotAllConvexResultCallback : public btCollisionWorld::ConvexResultCallback {
+public:
+ PhysicsDirectSpaceState::ShapeResult *m_results;
+ int m_resultMax;
+ int count;
+ const Set<RID> *m_exclude;
+
+ GodotAllConvexResultCallback(PhysicsDirectSpaceState::ShapeResult *p_results, int p_resultMax, const Set<RID> *p_exclude)
+ : m_results(p_results), m_exclude(p_exclude), m_resultMax(p_resultMax), count(0) {}
+
+ virtual bool needsCollision(btBroadphaseProxy *proxy0) const;
+
+ virtual btScalar addSingleResult(btCollisionWorld::LocalConvexResult &convexResult, bool normalInWorldSpace);
+};
+
+struct GodotKinClosestConvexResultCallback : public btCollisionWorld::ClosestConvexResultCallback {
+public:
+ const RigidBodyBullet *m_self_object;
+ const bool m_ignore_areas;
+
+ GodotKinClosestConvexResultCallback(const btVector3 &convexFromWorld, const btVector3 &convexToWorld, const RigidBodyBullet *p_self_object, bool p_ignore_areas)
+ : btCollisionWorld::ClosestConvexResultCallback(convexFromWorld, convexToWorld), m_self_object(p_self_object), m_ignore_areas(p_ignore_areas) {}
+
+ virtual bool needsCollision(btBroadphaseProxy *proxy0) const;
+};
+
+struct GodotClosestConvexResultCallback : public btCollisionWorld::ClosestConvexResultCallback {
+public:
+ const Set<RID> *m_exclude;
+ int m_shapePart;
+
+ GodotClosestConvexResultCallback(const btVector3 &convexFromWorld, const btVector3 &convexToWorld, const Set<RID> *p_exclude)
+ : btCollisionWorld::ClosestConvexResultCallback(convexFromWorld, convexToWorld), m_exclude(p_exclude) {}
+
+ virtual bool needsCollision(btBroadphaseProxy *proxy0) const;
+
+ virtual btScalar addSingleResult(btCollisionWorld::LocalConvexResult &convexResult, bool normalInWorldSpace);
+};
+
+struct GodotAllContactResultCallback : public btCollisionWorld::ContactResultCallback {
+public:
+ const btCollisionObject *m_self_object;
+ PhysicsDirectSpaceState::ShapeResult *m_results;
+ int m_resultMax;
+ int m_count;
+ const Set<RID> *m_exclude;
+
+ GodotAllContactResultCallback(btCollisionObject *p_self_object, PhysicsDirectSpaceState::ShapeResult *p_results, int p_resultMax, const Set<RID> *p_exclude)
+ : m_self_object(p_self_object), m_results(p_results), m_exclude(p_exclude), m_resultMax(p_resultMax), m_count(0) {}
+
+ virtual bool needsCollision(btBroadphaseProxy *proxy0) const;
+
+ virtual btScalar addSingleResult(btManifoldPoint &cp, const btCollisionObjectWrapper *colObj0Wrap, int partId0, int index0, const btCollisionObjectWrapper *colObj1Wrap, int partId1, int index1);
+};
+
+/// Returns the list of contacts pairs in this order: Local contact, other body contact
+struct GodotContactPairContactResultCallback : public btCollisionWorld::ContactResultCallback {
+public:
+ const btCollisionObject *m_self_object;
+ Vector3 *m_results;
+ int m_resultMax;
+ int m_count;
+ const Set<RID> *m_exclude;
+
+ GodotContactPairContactResultCallback(btCollisionObject *p_self_object, Vector3 *p_results, int p_resultMax, const Set<RID> *p_exclude)
+ : m_self_object(p_self_object), m_results(p_results), m_exclude(p_exclude), m_resultMax(p_resultMax), m_count(0) {}
+
+ virtual bool needsCollision(btBroadphaseProxy *proxy0) const;
+
+ virtual btScalar addSingleResult(btManifoldPoint &cp, const btCollisionObjectWrapper *colObj0Wrap, int partId0, int index0, const btCollisionObjectWrapper *colObj1Wrap, int partId1, int index1);
+};
+
+struct GodotRestInfoContactResultCallback : public btCollisionWorld::ContactResultCallback {
+public:
+ const btCollisionObject *m_self_object;
+ PhysicsDirectSpaceState::ShapeRestInfo *m_result;
+ bool m_collided;
+ real_t m_min_distance;
+ const btCollisionObject *m_rest_info_collision_object;
+ btVector3 m_rest_info_bt_point;
+ const Set<RID> *m_exclude;
+
+ GodotRestInfoContactResultCallback(btCollisionObject *p_self_object, PhysicsDirectSpaceState::ShapeRestInfo *p_result, const Set<RID> *p_exclude)
+ : m_self_object(p_self_object), m_result(p_result), m_exclude(p_exclude), m_collided(false), m_min_distance(0) {}
+
+ virtual bool needsCollision(btBroadphaseProxy *proxy0) const;
+
+ virtual btScalar addSingleResult(btManifoldPoint &cp, const btCollisionObjectWrapper *colObj0Wrap, int partId0, int index0, const btCollisionObjectWrapper *colObj1Wrap, int partId1, int index1);
+};
+
+struct GodotRecoverAndClosestContactResultCallback : public btCollisionWorld::ContactResultCallback {
+public:
+ btVector3 m_pointNormalWorld;
+ btVector3 m_pointWorld;
+ btScalar m_penetration_distance;
+ int m_other_compound_shape_index;
+ const btCollisionObject *m_pointCollisionObject;
+
+ const RigidBodyBullet *m_self_object;
+ bool m_ignore_areas;
+
+ btScalar m_most_penetrated_distance;
+ btVector3 m_recover_penetration;
+
+ GodotRecoverAndClosestContactResultCallback()
+ : m_pointCollisionObject(NULL), m_penetration_distance(0), m_other_compound_shape_index(0), m_self_object(NULL), m_ignore_areas(true), m_most_penetrated_distance(1e20), m_recover_penetration(0, 0, 0) {}
+
+ GodotRecoverAndClosestContactResultCallback(const RigidBodyBullet *p_self_object, bool p_ignore_areas)
+ : m_pointCollisionObject(NULL), m_penetration_distance(0), m_other_compound_shape_index(0), m_self_object(p_self_object), m_ignore_areas(p_ignore_areas), m_most_penetrated_distance(9999999999), m_recover_penetration(0, 0, 0) {}
+
+ void reset() {
+ m_pointCollisionObject = NULL;
+ m_most_penetrated_distance = 1e20;
+ m_recover_penetration.setZero();
+ }
+
+ bool hasHit() {
+ return m_pointCollisionObject;
+ }
+
+ virtual bool needsCollision(btBroadphaseProxy *proxy0) const;
+
+ virtual btScalar addSingleResult(btManifoldPoint &cp, const btCollisionObjectWrapper *colObj0Wrap, int partId0, int index0, const btCollisionObjectWrapper *colObj1Wrap, int partId1, int index1);
+};
+
+#endif // GODOT_RESULT_CALLBACKS_H
diff --git a/modules/bullet/hinge_joint_bullet.cpp b/modules/bullet/hinge_joint_bullet.cpp
new file mode 100644
index 0000000000..bb70babd99
--- /dev/null
+++ b/modules/bullet/hinge_joint_bullet.cpp
@@ -0,0 +1,163 @@
+/*************************************************************************/
+/* hinge_joint_bullet.cpp */
+/* Author: AndreaCatania */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2017 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 "hinge_joint_bullet.h"
+#include "BulletDynamics/ConstraintSolver/btHingeConstraint.h"
+#include "bullet_types_converter.h"
+#include "bullet_utilities.h"
+#include "rigid_body_bullet.h"
+
+HingeJointBullet::HingeJointBullet(RigidBodyBullet *rbA, RigidBodyBullet *rbB, const Transform &frameA, const Transform &frameB)
+ : JointBullet() {
+ btTransform btFrameA;
+ G_TO_B(frameA, btFrameA);
+
+ if (rbB) {
+ btTransform btFrameB;
+ G_TO_B(frameB, btFrameB);
+
+ hingeConstraint = bulletnew(btHingeConstraint(*rbA->get_bt_rigid_body(), *rbB->get_bt_rigid_body(), btFrameA, btFrameB));
+ } else {
+
+ hingeConstraint = bulletnew(btHingeConstraint(*rbA->get_bt_rigid_body(), btFrameA));
+ }
+
+ setup(hingeConstraint);
+}
+
+HingeJointBullet::HingeJointBullet(RigidBodyBullet *rbA, RigidBodyBullet *rbB, const Vector3 &pivotInA, const Vector3 &pivotInB, const Vector3 &axisInA, const Vector3 &axisInB)
+ : JointBullet() {
+
+ btVector3 btPivotA;
+ btVector3 btAxisA;
+ G_TO_B(pivotInA, btPivotA);
+ G_TO_B(axisInA, btAxisA);
+
+ if (rbB) {
+ btVector3 btPivotB;
+ btVector3 btAxisB;
+ G_TO_B(pivotInB, btPivotB);
+ G_TO_B(axisInB, btAxisB);
+
+ hingeConstraint = bulletnew(btHingeConstraint(*rbA->get_bt_rigid_body(), *rbB->get_bt_rigid_body(), btPivotA, btPivotB, btAxisA, btAxisB));
+ } else {
+
+ hingeConstraint = bulletnew(btHingeConstraint(*rbA->get_bt_rigid_body(), btPivotA, btAxisA));
+ }
+
+ setup(hingeConstraint);
+}
+
+real_t HingeJointBullet::get_hinge_angle() {
+ return hingeConstraint->getHingeAngle();
+}
+
+void HingeJointBullet::set_param(PhysicsServer::HingeJointParam p_param, real_t p_value) {
+ switch (p_param) {
+ case PhysicsServer::HINGE_JOINT_BIAS:
+ if (0 < p_value) {
+ print_line("The Bullet Hinge Joint doesn't support bias, So it's always 0");
+ }
+ break;
+ case PhysicsServer::HINGE_JOINT_LIMIT_UPPER:
+ hingeConstraint->setLimit(hingeConstraint->getLowerLimit(), p_value, hingeConstraint->getLimitSoftness(), hingeConstraint->getLimitBiasFactor(), hingeConstraint->getLimitRelaxationFactor());
+ break;
+ case PhysicsServer::HINGE_JOINT_LIMIT_LOWER:
+ hingeConstraint->setLimit(p_value, hingeConstraint->getUpperLimit(), hingeConstraint->getLimitSoftness(), hingeConstraint->getLimitBiasFactor(), hingeConstraint->getLimitRelaxationFactor());
+ break;
+ case PhysicsServer::HINGE_JOINT_LIMIT_BIAS:
+ hingeConstraint->setLimit(hingeConstraint->getLowerLimit(), hingeConstraint->getUpperLimit(), hingeConstraint->getLimitSoftness(), p_value, hingeConstraint->getLimitRelaxationFactor());
+ break;
+ case PhysicsServer::HINGE_JOINT_LIMIT_SOFTNESS:
+ hingeConstraint->setLimit(hingeConstraint->getLowerLimit(), hingeConstraint->getUpperLimit(), p_value, hingeConstraint->getLimitBiasFactor(), hingeConstraint->getLimitRelaxationFactor());
+ break;
+ case PhysicsServer::HINGE_JOINT_LIMIT_RELAXATION:
+ hingeConstraint->setLimit(hingeConstraint->getLowerLimit(), hingeConstraint->getUpperLimit(), hingeConstraint->getLimitSoftness(), hingeConstraint->getLimitBiasFactor(), p_value);
+ break;
+ case PhysicsServer::HINGE_JOINT_MOTOR_TARGET_VELOCITY:
+ hingeConstraint->setMotorTargetVelocity(p_value);
+ break;
+ case PhysicsServer::HINGE_JOINT_MOTOR_MAX_IMPULSE:
+ hingeConstraint->setMaxMotorImpulse(p_value);
+ break;
+ default:
+ WARN_PRINTS("The Bullet Hinge Joint doesn't support this parameter: " + itos(p_param) + ", value: " + itos(p_value));
+ }
+}
+
+real_t HingeJointBullet::get_param(PhysicsServer::HingeJointParam p_param) const {
+ switch (p_param) {
+ case PhysicsServer::HINGE_JOINT_BIAS:
+ return 0;
+ break;
+ case PhysicsServer::HINGE_JOINT_LIMIT_UPPER:
+ return hingeConstraint->getUpperLimit();
+ case PhysicsServer::HINGE_JOINT_LIMIT_LOWER:
+ return hingeConstraint->getLowerLimit();
+ case PhysicsServer::HINGE_JOINT_LIMIT_BIAS:
+ return hingeConstraint->getLimitBiasFactor();
+ case PhysicsServer::HINGE_JOINT_LIMIT_SOFTNESS:
+ return hingeConstraint->getLimitSoftness();
+ case PhysicsServer::HINGE_JOINT_LIMIT_RELAXATION:
+ return hingeConstraint->getLimitRelaxationFactor();
+ case PhysicsServer::HINGE_JOINT_MOTOR_TARGET_VELOCITY:
+ return hingeConstraint->getMotorTargetVelocity();
+ case PhysicsServer::HINGE_JOINT_MOTOR_MAX_IMPULSE:
+ return hingeConstraint->getMaxMotorImpulse();
+ default:
+ WARN_PRINTS("The Bullet Hinge Joint doesn't support this parameter: " + itos(p_param));
+ return 0;
+ }
+}
+
+void HingeJointBullet::set_flag(PhysicsServer::HingeJointFlag p_flag, bool p_value) {
+ switch (p_flag) {
+ case PhysicsServer::HINGE_JOINT_FLAG_USE_LIMIT:
+ if (!p_value) {
+ hingeConstraint->setLimit(-Math_PI, Math_PI);
+ }
+ break;
+ case PhysicsServer::HINGE_JOINT_FLAG_ENABLE_MOTOR:
+ hingeConstraint->enableMotor(p_value);
+ break;
+ }
+}
+
+bool HingeJointBullet::get_flag(PhysicsServer::HingeJointFlag p_flag) const {
+ switch (p_flag) {
+ case PhysicsServer::HINGE_JOINT_FLAG_USE_LIMIT:
+ return true;
+ case PhysicsServer::HINGE_JOINT_FLAG_ENABLE_MOTOR:
+ return hingeConstraint->getEnableAngularMotor();
+ default:
+ return false;
+ }
+}
diff --git a/modules/bullet/hinge_joint_bullet.h b/modules/bullet/hinge_joint_bullet.h
new file mode 100644
index 0000000000..a78788a5e5
--- /dev/null
+++ b/modules/bullet/hinge_joint_bullet.h
@@ -0,0 +1,54 @@
+/*************************************************************************/
+/* hinge_joint_bullet.h */
+/* Author: AndreaCatania */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2017 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 HINGE_JOINT_BULLET_H
+#define HINGE_JOINT_BULLET_H
+
+#include "joint_bullet.h"
+
+class HingeJointBullet : public JointBullet {
+ class btHingeConstraint *hingeConstraint;
+
+public:
+ HingeJointBullet(RigidBodyBullet *rbA, RigidBodyBullet *rbB, const Transform &frameA, const Transform &frameB);
+ HingeJointBullet(RigidBodyBullet *rbA, RigidBodyBullet *rbB, const Vector3 &pivotInA, const Vector3 &pivotInB, const Vector3 &axisInA, const Vector3 &axisInB);
+
+ virtual PhysicsServer::JointType get_type() const { return PhysicsServer::JOINT_HINGE; }
+
+ real_t get_hinge_angle();
+
+ void set_param(PhysicsServer::HingeJointParam p_param, real_t p_value);
+ real_t get_param(PhysicsServer::HingeJointParam p_param) const;
+
+ void set_flag(PhysicsServer::HingeJointFlag p_flag, bool p_value);
+ bool get_flag(PhysicsServer::HingeJointFlag p_flag) const;
+};
+#endif
diff --git a/modules/bullet/joint_bullet.cpp b/modules/bullet/joint_bullet.cpp
new file mode 100644
index 0000000000..be544f89bf
--- /dev/null
+++ b/modules/bullet/joint_bullet.cpp
@@ -0,0 +1,38 @@
+/*************************************************************************/
+/* joint_bullet.cpp */
+/* Author: AndreaCatania */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2017 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 "joint_bullet.h"
+#include "space_bullet.h"
+
+JointBullet::JointBullet()
+ : ConstraintBullet() {}
+
+JointBullet::~JointBullet() {}
diff --git a/modules/bullet/joint_bullet.h b/modules/bullet/joint_bullet.h
new file mode 100644
index 0000000000..d47e677502
--- /dev/null
+++ b/modules/bullet/joint_bullet.h
@@ -0,0 +1,49 @@
+/*************************************************************************/
+/* joint_bullet.h */
+/* Author: AndreaCatania */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2017 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 JOINT_BULLET_H
+#define JOINT_BULLET_H
+
+#include "constraint_bullet.h"
+#include "servers/physics_server.h"
+
+class RigidBodyBullet;
+class btTypedConstraint;
+
+class JointBullet : public ConstraintBullet {
+
+public:
+ JointBullet();
+ virtual ~JointBullet();
+
+ virtual PhysicsServer::JointType get_type() const = 0;
+};
+#endif
diff --git a/modules/bullet/pin_joint_bullet.cpp b/modules/bullet/pin_joint_bullet.cpp
new file mode 100644
index 0000000000..cd9e9a4557
--- /dev/null
+++ b/modules/bullet/pin_joint_bullet.cpp
@@ -0,0 +1,112 @@
+/*************************************************************************/
+/* pin_joint_bullet.cpp */
+/* Author: AndreaCatania */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2017 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 "pin_joint_bullet.h"
+#include "BulletDynamics/ConstraintSolver/btPoint2PointConstraint.h"
+#include "bullet_types_converter.h"
+#include "rigid_body_bullet.h"
+
+PinJointBullet::PinJointBullet(RigidBodyBullet *p_body_a, const Vector3 &p_pos_a, RigidBodyBullet *p_body_b, const Vector3 &p_pos_b)
+ : JointBullet() {
+ if (p_body_b) {
+
+ btVector3 btPivotA;
+ btVector3 btPivotB;
+ G_TO_B(p_pos_a, btPivotA);
+ G_TO_B(p_pos_b, btPivotB);
+ p2pConstraint = bulletnew(btPoint2PointConstraint(*p_body_a->get_bt_rigid_body(),
+ *p_body_b->get_bt_rigid_body(),
+ btPivotA,
+ btPivotB));
+ } else {
+ btVector3 btPivotA;
+ G_TO_B(p_pos_a, btPivotA);
+ p2pConstraint = bulletnew(btPoint2PointConstraint(*p_body_a->get_bt_rigid_body(), btPivotA));
+ }
+
+ setup(p2pConstraint);
+}
+
+PinJointBullet::~PinJointBullet() {}
+
+void PinJointBullet::set_param(PhysicsServer::PinJointParam p_param, real_t p_value) {
+ switch (p_param) {
+ case PhysicsServer::PIN_JOINT_BIAS:
+ p2pConstraint->m_setting.m_tau = p_value;
+ break;
+ case PhysicsServer::PIN_JOINT_DAMPING:
+ p2pConstraint->m_setting.m_damping = p_value;
+ break;
+ case PhysicsServer::PIN_JOINT_IMPULSE_CLAMP:
+ p2pConstraint->m_setting.m_impulseClamp = p_value;
+ break;
+ }
+}
+
+real_t PinJointBullet::get_param(PhysicsServer::PinJointParam p_param) const {
+ switch (p_param) {
+ case PhysicsServer::PIN_JOINT_BIAS:
+ return p2pConstraint->m_setting.m_tau;
+ case PhysicsServer::PIN_JOINT_DAMPING:
+ return p2pConstraint->m_setting.m_damping;
+ case PhysicsServer::PIN_JOINT_IMPULSE_CLAMP:
+ return p2pConstraint->m_setting.m_impulseClamp;
+ default:
+ WARN_PRINTS("This get parameter is not supported");
+ return 0;
+ }
+}
+
+void PinJointBullet::setPivotInA(const Vector3 &p_pos) {
+ btVector3 btVec;
+ G_TO_B(p_pos, btVec);
+ p2pConstraint->setPivotA(btVec);
+}
+
+void PinJointBullet::setPivotInB(const Vector3 &p_pos) {
+ btVector3 btVec;
+ G_TO_B(p_pos, btVec);
+ p2pConstraint->setPivotB(btVec);
+}
+
+Vector3 PinJointBullet::getPivotInA() {
+ btVector3 vec = p2pConstraint->getPivotInA();
+ Vector3 gVec;
+ B_TO_G(vec, gVec);
+ return gVec;
+}
+
+Vector3 PinJointBullet::getPivotInB() {
+ btVector3 vec = p2pConstraint->getPivotInB();
+ Vector3 gVec;
+ B_TO_G(vec, gVec);
+ return gVec;
+}
diff --git a/modules/bullet/pin_joint_bullet.h b/modules/bullet/pin_joint_bullet.h
new file mode 100644
index 0000000000..3a0906bf83
--- /dev/null
+++ b/modules/bullet/pin_joint_bullet.h
@@ -0,0 +1,57 @@
+/*************************************************************************/
+/* pin_joint_bullet.h */
+/* Author: AndreaCatania */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2017 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 PIN_JOINT_BULLET_H
+#define PIN_JOINT_BULLET_H
+
+#include "joint_bullet.h"
+
+class RigidBodyBullet;
+
+class PinJointBullet : public JointBullet {
+ class btPoint2PointConstraint *p2pConstraint;
+
+public:
+ PinJointBullet(RigidBodyBullet *p_body_a, const Vector3 &p_pos_a, RigidBodyBullet *p_body_b, const Vector3 &p_pos_b);
+ ~PinJointBullet();
+
+ virtual PhysicsServer::JointType get_type() const { return PhysicsServer::JOINT_PIN; }
+
+ void set_param(PhysicsServer::PinJointParam p_param, real_t p_value);
+ real_t get_param(PhysicsServer::PinJointParam p_param) const;
+
+ void setPivotInA(const Vector3 &p_pos);
+ void setPivotInB(const Vector3 &p_pos);
+
+ Vector3 getPivotInA();
+ Vector3 getPivotInB();
+};
+#endif
diff --git a/modules/bullet/register_types.cpp b/modules/bullet/register_types.cpp
new file mode 100644
index 0000000000..1e697e7443
--- /dev/null
+++ b/modules/bullet/register_types.cpp
@@ -0,0 +1,47 @@
+/*************************************************************************/
+/* register_types.cpp */
+/* Author: AndreaCatania */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2017 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 "register_types.h"
+#include "bullet_physics_server.h"
+#include "class_db.h"
+
+PhysicsServer *_createBulletPhysicsCallback() {
+ return memnew(BulletPhysicsServer);
+}
+
+void register_bullet_types() {
+
+ PhysicsServerManager::register_server("Bullet", &_createBulletPhysicsCallback);
+ PhysicsServerManager::set_default_server("Bullet", 1);
+}
+
+void unregister_bullet_types() {
+}
diff --git a/modules/bullet/register_types.h b/modules/bullet/register_types.h
new file mode 100644
index 0000000000..ca0683fa3b
--- /dev/null
+++ b/modules/bullet/register_types.h
@@ -0,0 +1,37 @@
+/*************************************************************************/
+/* register_types.h */
+/* Author: AndreaCatania */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2017 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 REGISTER_BULLET_TYPES_H
+#define REGISTER_BULLET_TYPES_H
+
+void register_bullet_types();
+void unregister_bullet_types();
+#endif
diff --git a/modules/bullet/rid_bullet.h b/modules/bullet/rid_bullet.h
new file mode 100644
index 0000000000..da7517f246
--- /dev/null
+++ b/modules/bullet/rid_bullet.h
@@ -0,0 +1,50 @@
+/*************************************************************************/
+/* rid_bullet.h */
+/* Author: AndreaCatania */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2017 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 RID_BULLET_H
+#define RID_BULLET_H
+
+#include "core/rid.h"
+
+class BulletPhysicsServer;
+
+class RIDBullet : public RID_Data {
+ RID self;
+ BulletPhysicsServer *physicsServer;
+
+public:
+ _FORCE_INLINE_ void set_self(const RID &p_self) { self = p_self; }
+ _FORCE_INLINE_ RID get_self() const { return self; }
+
+ _FORCE_INLINE_ void _set_physics_server(BulletPhysicsServer *p_physicsServer) { physicsServer = p_physicsServer; }
+ _FORCE_INLINE_ BulletPhysicsServer *get_physics_server() const { return physicsServer; }
+};
+#endif
diff --git a/modules/bullet/rigid_body_bullet.cpp b/modules/bullet/rigid_body_bullet.cpp
new file mode 100644
index 0000000000..5d0513db76
--- /dev/null
+++ b/modules/bullet/rigid_body_bullet.cpp
@@ -0,0 +1,1009 @@
+/*************************************************************************/
+/* body_bullet.cpp */
+/* Author: AndreaCatania */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2017 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 "rigid_body_bullet.h"
+#include "BulletCollision/CollisionDispatch/btGhostObject.h"
+#include "BulletCollision/CollisionShapes/btConvexPointCloudShape.h"
+#include "BulletDynamics/Dynamics/btRigidBody.h"
+#include "btBulletCollisionCommon.h"
+#include "btRayShape.h"
+#include "bullet_physics_server.h"
+#include "bullet_types_converter.h"
+#include "bullet_utilities.h"
+#include "godot_motion_state.h"
+#include "joint_bullet.h"
+#include <assert.h>
+
+BulletPhysicsDirectBodyState *BulletPhysicsDirectBodyState::singleton = NULL;
+
+Vector3 BulletPhysicsDirectBodyState::get_total_gravity() const {
+ Vector3 gVec;
+ B_TO_G(body->btBody->getGravity(), gVec);
+ return gVec;
+}
+
+float BulletPhysicsDirectBodyState::get_total_angular_damp() const {
+ return body->btBody->getAngularDamping();
+}
+
+float BulletPhysicsDirectBodyState::get_total_linear_damp() const {
+ return body->btBody->getLinearDamping();
+}
+
+Vector3 BulletPhysicsDirectBodyState::get_center_of_mass() const {
+ Vector3 gVec;
+ B_TO_G(body->btBody->getCenterOfMassPosition(), gVec);
+ return gVec;
+}
+
+Basis BulletPhysicsDirectBodyState::get_principal_inertia_axes() const {
+ return Basis();
+}
+
+float BulletPhysicsDirectBodyState::get_inverse_mass() const {
+ return body->btBody->getInvMass();
+}
+
+Vector3 BulletPhysicsDirectBodyState::get_inverse_inertia() const {
+ Vector3 gVec;
+ B_TO_G(body->btBody->getInvInertiaDiagLocal(), gVec);
+ return gVec;
+}
+
+Basis BulletPhysicsDirectBodyState::get_inverse_inertia_tensor() const {
+ Basis gInertia;
+ B_TO_G(body->btBody->getInvInertiaTensorWorld(), gInertia);
+ return gInertia;
+}
+
+void BulletPhysicsDirectBodyState::set_linear_velocity(const Vector3 &p_velocity) {
+ body->set_linear_velocity(p_velocity);
+}
+
+Vector3 BulletPhysicsDirectBodyState::get_linear_velocity() const {
+ return body->get_linear_velocity();
+}
+
+void BulletPhysicsDirectBodyState::set_angular_velocity(const Vector3 &p_velocity) {
+ body->set_angular_velocity(p_velocity);
+}
+
+Vector3 BulletPhysicsDirectBodyState::get_angular_velocity() const {
+ return body->get_angular_velocity();
+}
+
+void BulletPhysicsDirectBodyState::set_transform(const Transform &p_transform) {
+ body->set_transform(p_transform);
+}
+
+Transform BulletPhysicsDirectBodyState::get_transform() const {
+ return body->get_transform();
+}
+
+void BulletPhysicsDirectBodyState::add_force(const Vector3 &p_force, const Vector3 &p_pos) {
+ body->apply_force(p_force, p_pos);
+}
+
+void BulletPhysicsDirectBodyState::apply_impulse(const Vector3 &p_pos, const Vector3 &p_j) {
+ body->apply_impulse(p_pos, p_j);
+}
+
+void BulletPhysicsDirectBodyState::apply_torque_impulse(const Vector3 &p_j) {
+ body->apply_torque_impulse(p_j);
+}
+
+void BulletPhysicsDirectBodyState::set_sleep_state(bool p_enable) {
+ body->set_activation_state(p_enable);
+}
+
+bool BulletPhysicsDirectBodyState::is_sleeping() const {
+ return !body->is_active();
+}
+
+int BulletPhysicsDirectBodyState::get_contact_count() const {
+ return body->collisionsCount;
+}
+
+Vector3 BulletPhysicsDirectBodyState::get_contact_local_position(int p_contact_idx) const {
+ return body->collisions[p_contact_idx].hitLocalLocation;
+}
+
+Vector3 BulletPhysicsDirectBodyState::get_contact_local_normal(int p_contact_idx) const {
+ return body->collisions[p_contact_idx].hitNormal;
+}
+
+int BulletPhysicsDirectBodyState::get_contact_local_shape(int p_contact_idx) const {
+ return body->collisions[p_contact_idx].local_shape;
+}
+
+RID BulletPhysicsDirectBodyState::get_contact_collider(int p_contact_idx) const {
+ return body->collisions[p_contact_idx].otherObject->get_self();
+}
+
+Vector3 BulletPhysicsDirectBodyState::get_contact_collider_position(int p_contact_idx) const {
+ return body->collisions[p_contact_idx].hitWorldLocation;
+}
+
+ObjectID BulletPhysicsDirectBodyState::get_contact_collider_id(int p_contact_idx) const {
+ return body->collisions[p_contact_idx].otherObject->get_instance_id();
+}
+
+int BulletPhysicsDirectBodyState::get_contact_collider_shape(int p_contact_idx) const {
+ return body->collisions[p_contact_idx].other_object_shape;
+}
+
+Vector3 BulletPhysicsDirectBodyState::get_contact_collider_velocity_at_position(int p_contact_idx) const {
+ RigidBodyBullet::CollisionData &colDat = body->collisions[p_contact_idx];
+
+ btVector3 hitLocation;
+ G_TO_B(colDat.hitLocalLocation, hitLocation);
+
+ Vector3 velocityAtPoint;
+ B_TO_G(colDat.otherObject->get_bt_rigid_body()->getVelocityInLocalPoint(hitLocation), velocityAtPoint);
+
+ return velocityAtPoint;
+}
+
+PhysicsDirectSpaceState *BulletPhysicsDirectBodyState::get_space_state() {
+ return body->get_space()->get_direct_state();
+}
+
+RigidBodyBullet::KinematicUtilities::KinematicUtilities(RigidBodyBullet *p_owner)
+ : m_owner(p_owner), m_margin(0.01) // Godot default margin 0.001
+{
+ m_ghostObject = bulletnew(btPairCachingGhostObject);
+
+ int clearedCurrentFlags = m_ghostObject->getCollisionFlags();
+ clearedCurrentFlags &= ~(btCollisionObject::CF_KINEMATIC_OBJECT | btCollisionObject::CF_STATIC_OBJECT);
+
+ m_ghostObject->setCollisionFlags(clearedCurrentFlags | btCollisionObject::CF_KINEMATIC_OBJECT);
+ m_ghostObject->setUserPointer(p_owner);
+ m_ghostObject->setUserIndex(TYPE_KINEMATIC_GHOST_BODY);
+
+ resetDefShape();
+}
+
+RigidBodyBullet::KinematicUtilities::~KinematicUtilities() {
+ just_delete_shapes(m_shapes.size()); // don't need to resize
+ bulletdelete(m_ghostObject);
+}
+
+void RigidBodyBullet::KinematicUtilities::resetDefShape() {
+ m_ghostObject->setCollisionShape(BulletPhysicsServer::get_empty_shape());
+}
+
+void RigidBodyBullet::KinematicUtilities::copyAllOwnerShapes() {
+ const Vector<CollisionObjectBullet::ShapeWrapper> &shapes_wrappers(m_owner->get_shapes_wrappers());
+ const int shapes_count = shapes_wrappers.size();
+
+ just_delete_shapes(shapes_count);
+
+ const CollisionObjectBullet::ShapeWrapper *shape_wrapper;
+
+ for (int i = shapes_count - 1; 0 <= i; --i) {
+ shape_wrapper = &shapes_wrappers[i];
+ if (!shape_wrapper->active) {
+ continue;
+ }
+ m_shapes[i].transform = shape_wrapper->transform;
+
+ btConvexShape *&kin_shape_ref = m_shapes[i].shape;
+
+ switch (shape_wrapper->shape->get_type()) {
+ case PhysicsServer::SHAPE_SPHERE: {
+ SphereShapeBullet *sphere = static_cast<SphereShapeBullet *>(shape_wrapper->shape);
+ kin_shape_ref = ShapeBullet::create_shape_sphere(sphere->get_radius() * m_owner->body_scale[0] + m_margin);
+ break;
+ }
+ case PhysicsServer::SHAPE_BOX: {
+ BoxShapeBullet *box = static_cast<BoxShapeBullet *>(shape_wrapper->shape);
+ kin_shape_ref = ShapeBullet::create_shape_box((box->get_half_extents() * m_owner->body_scale) + btVector3(m_margin, m_margin, m_margin));
+ break;
+ }
+ case PhysicsServer::SHAPE_CAPSULE: {
+ CapsuleShapeBullet *capsule = static_cast<CapsuleShapeBullet *>(shape_wrapper->shape);
+ kin_shape_ref = ShapeBullet::create_shape_capsule(capsule->get_radius() * m_owner->body_scale[0] + m_margin, capsule->get_height() * m_owner->body_scale[1] + m_margin);
+ break;
+ }
+ case PhysicsServer::SHAPE_CONVEX_POLYGON: {
+ ConvexPolygonShapeBullet *godot_convex = static_cast<ConvexPolygonShapeBullet *>(shape_wrapper->shape);
+ kin_shape_ref = ShapeBullet::create_shape_convex(godot_convex->vertices);
+ kin_shape_ref->setLocalScaling(m_owner->body_scale + btVector3(m_margin, m_margin, m_margin));
+ break;
+ }
+ case PhysicsServer::SHAPE_RAY: {
+ RayShapeBullet *godot_ray = static_cast<RayShapeBullet *>(shape_wrapper->shape);
+ kin_shape_ref = ShapeBullet::create_shape_ray(godot_ray->length * m_owner->body_scale[1] + m_margin);
+ break;
+ }
+ default:
+ WARN_PRINT("This shape is not supported to be kinematic!");
+ kin_shape_ref = NULL;
+ }
+ }
+}
+
+void RigidBodyBullet::KinematicUtilities::just_delete_shapes(int new_size) {
+ for (int i = m_shapes.size() - 1; 0 <= i; --i) {
+ if (m_shapes[i].shape) {
+ bulletdelete(m_shapes[i].shape);
+ }
+ }
+ m_shapes.resize(new_size);
+}
+
+RigidBodyBullet::RigidBodyBullet()
+ : RigidCollisionObjectBullet(CollisionObjectBullet::TYPE_RIGID_BODY),
+ kinematic_utilities(NULL),
+ gravity_scale(1),
+ mass(1),
+ linearDamp(0),
+ angularDamp(0),
+ can_sleep(true),
+ force_integration_callback(NULL),
+ isTransformChanged(false),
+ maxCollisionsDetection(0),
+ collisionsCount(0),
+ maxAreasWhereIam(10),
+ areaWhereIamCount(0),
+ countGravityPointSpaces(0),
+ isScratchedSpaceOverrideModificator(false) {
+
+ godotMotionState = bulletnew(GodotMotionState(this));
+
+ // Initial properties
+ const btVector3 localInertia(0, 0, 0);
+ btRigidBody::btRigidBodyConstructionInfo cInfo(mass, godotMotionState, compoundShape, localInertia);
+
+ btBody = bulletnew(btRigidBody(cInfo));
+ setupBulletCollisionObject(btBody);
+
+ set_mode(PhysicsServer::BODY_MODE_RIGID);
+ set_axis_lock(PhysicsServer::BODY_AXIS_LOCK_DISABLED);
+
+ areasWhereIam.resize(maxAreasWhereIam);
+ for (int i = areasWhereIam.size() - 1; 0 <= i; --i) {
+ areasWhereIam[i] = NULL;
+ }
+}
+
+RigidBodyBullet::~RigidBodyBullet() {
+ bulletdelete(godotMotionState);
+
+ if (force_integration_callback)
+ memdelete(force_integration_callback);
+
+ destroy_kinematic_utilities();
+}
+
+void RigidBodyBullet::init_kinematic_utilities() {
+ kinematic_utilities = memnew(KinematicUtilities(this));
+}
+
+void RigidBodyBullet::destroy_kinematic_utilities() {
+ if (kinematic_utilities) {
+ memdelete(kinematic_utilities);
+ kinematic_utilities = NULL;
+ }
+}
+
+void RigidBodyBullet::reload_body() {
+ if (space) {
+ space->remove_rigid_body(this);
+ space->add_rigid_body(this);
+ }
+}
+
+void RigidBodyBullet::set_space(SpaceBullet *p_space) {
+ // Clear the old space if there is one
+ if (space) {
+ isTransformChanged = false;
+
+ // Remove all eventual constraints
+ assert_no_constraints();
+
+ // Remove this object form the physics world
+ space->remove_rigid_body(this);
+ }
+
+ space = p_space;
+
+ if (space) {
+ space->add_rigid_body(this);
+ }
+}
+
+void RigidBodyBullet::dispatch_callbacks() {
+ /// The check isTransformChanged is necessary in order to call integrated forces only when the first transform is sent
+ if (btBody->isActive() && force_integration_callback && isTransformChanged) {
+
+ BulletPhysicsDirectBodyState *bodyDirect = BulletPhysicsDirectBodyState::get_singleton(this);
+
+ Variant variantBodyDirect = bodyDirect;
+
+ Object *obj = ObjectDB::get_instance(force_integration_callback->id);
+ if (!obj) {
+ // Remove integration callback
+ set_force_integration_callback(0, StringName());
+ } else {
+ const Variant *vp[2] = { &variantBodyDirect, &force_integration_callback->udata };
+
+ Variant::CallError responseCallError;
+ int argc = (force_integration_callback->udata.get_type() == Variant::NIL) ? 1 : 2;
+ obj->call(force_integration_callback->method, vp, argc, responseCallError);
+ }
+ }
+
+ if (isScratchedSpaceOverrideModificator || 0 < countGravityPointSpaces) {
+ isScratchedSpaceOverrideModificator = false;
+ reload_space_override_modificator();
+ }
+
+ /// Lock axis
+ btBody->setLinearVelocity(btBody->getLinearVelocity() * btBody->getLinearFactor());
+ btBody->setAngularVelocity(btBody->getAngularVelocity() * btBody->getAngularFactor());
+}
+
+void RigidBodyBullet::set_force_integration_callback(ObjectID p_id, const StringName &p_method, const Variant &p_udata) {
+
+ if (force_integration_callback) {
+ memdelete(force_integration_callback);
+ force_integration_callback = NULL;
+ }
+
+ if (p_id != 0) {
+ force_integration_callback = memnew(ForceIntegrationCallback);
+ force_integration_callback->id = p_id;
+ force_integration_callback->method = p_method;
+ force_integration_callback->udata = p_udata;
+ }
+}
+
+void RigidBodyBullet::scratch() {
+ isTransformChanged = true;
+}
+
+void RigidBodyBullet::scratch_space_override_modificator() {
+ isScratchedSpaceOverrideModificator = true;
+}
+
+void RigidBodyBullet::on_collision_filters_change() {
+ if (space) {
+ space->reload_collision_filters(this);
+ }
+}
+
+void RigidBodyBullet::on_collision_checker_start() {
+ collisionsCount = 0;
+}
+
+bool RigidBodyBullet::add_collision_object(RigidBodyBullet *p_otherObject, const Vector3 &p_hitWorldLocation, const Vector3 &p_hitLocalLocation, const Vector3 &p_hitNormal, int p_other_shape_index, int p_local_shape_index) {
+
+ if (collisionsCount >= maxCollisionsDetection) {
+ return false;
+ }
+
+ CollisionData &cd = collisions[collisionsCount];
+ cd.hitLocalLocation = p_hitLocalLocation;
+ cd.otherObject = p_otherObject;
+ cd.hitWorldLocation = p_hitWorldLocation;
+ cd.hitNormal = p_hitNormal;
+ cd.other_object_shape = p_other_shape_index;
+ cd.local_shape = p_local_shape_index;
+
+ ++collisionsCount;
+ return true;
+}
+
+void RigidBodyBullet::assert_no_constraints() {
+ if (btBody->getNumConstraintRefs()) {
+ WARN_PRINT("A body with a joints is destroyed. Please check the implementation in order to destroy the joint before the body.");
+ }
+ /*for(int i = btBody->getNumConstraintRefs()-1; 0<=i; --i){
+ btTypedConstraint* btConst = btBody->getConstraintRef(i);
+ JointBullet* joint = static_cast<JointBullet*>( btConst->getUserConstraintPtr() );
+ space->removeConstraint(joint);
+ }*/
+}
+
+void RigidBodyBullet::set_activation_state(bool p_active) {
+ if (p_active) {
+ btBody->setActivationState(ACTIVE_TAG);
+ } else {
+ btBody->setActivationState(WANTS_DEACTIVATION);
+ }
+}
+
+bool RigidBodyBullet::is_active() const {
+ return btBody->isActive();
+}
+
+void RigidBodyBullet::set_param(PhysicsServer::BodyParameter p_param, real_t p_value) {
+ switch (p_param) {
+ case PhysicsServer::BODY_PARAM_BOUNCE:
+ btBody->setRestitution(p_value);
+ break;
+ case PhysicsServer::BODY_PARAM_FRICTION:
+ btBody->setFriction(p_value);
+ break;
+ case PhysicsServer::BODY_PARAM_MASS: {
+ ERR_FAIL_COND(p_value < 0);
+ mass = p_value;
+ _internal_set_mass(p_value);
+ break;
+ }
+ case PhysicsServer::BODY_PARAM_LINEAR_DAMP:
+ linearDamp = p_value;
+ btBody->setDamping(linearDamp, angularDamp);
+ break;
+ case PhysicsServer::BODY_PARAM_ANGULAR_DAMP:
+ angularDamp = p_value;
+ btBody->setDamping(linearDamp, angularDamp);
+ break;
+ case PhysicsServer::BODY_PARAM_GRAVITY_SCALE:
+ gravity_scale = p_value;
+ /// The Bullet gravity will be is set by reload_space_override_modificator
+ scratch_space_override_modificator();
+ break;
+ default:
+ WARN_PRINTS("Parameter " + itos(p_param) + " not supported by bullet. Value: " + itos(p_value));
+ }
+}
+
+real_t RigidBodyBullet::get_param(PhysicsServer::BodyParameter p_param) const {
+ switch (p_param) {
+ case PhysicsServer::BODY_PARAM_BOUNCE:
+ return btBody->getRestitution();
+ case PhysicsServer::BODY_PARAM_FRICTION:
+ return btBody->getFriction();
+ case PhysicsServer::BODY_PARAM_MASS: {
+ const btScalar invMass = btBody->getInvMass();
+ return 0 == invMass ? 0 : 1 / invMass;
+ }
+ case PhysicsServer::BODY_PARAM_LINEAR_DAMP:
+ return linearDamp;
+ case PhysicsServer::BODY_PARAM_ANGULAR_DAMP:
+ return angularDamp;
+ case PhysicsServer::BODY_PARAM_GRAVITY_SCALE:
+ return gravity_scale;
+ default:
+ WARN_PRINTS("Parameter " + itos(p_param) + " not supported by bullet");
+ return 0;
+ }
+}
+
+void RigidBodyBullet::set_mode(PhysicsServer::BodyMode p_mode) {
+ // This is necessary to block force_integration untile next move
+ isTransformChanged = false;
+ destroy_kinematic_utilities();
+ // The mode change is relevant to its mass
+ switch (p_mode) {
+ case PhysicsServer::BODY_MODE_KINEMATIC:
+ mode = PhysicsServer::BODY_MODE_KINEMATIC;
+ set_axis_lock(axis_lock); // Reload axis lock
+ _internal_set_mass(0);
+ init_kinematic_utilities();
+ break;
+ case PhysicsServer::BODY_MODE_STATIC:
+ mode = PhysicsServer::BODY_MODE_STATIC;
+ set_axis_lock(axis_lock); // Reload axis lock
+ _internal_set_mass(0);
+ break;
+ case PhysicsServer::BODY_MODE_RIGID: {
+ mode = PhysicsServer::BODY_MODE_RIGID;
+ set_axis_lock(axis_lock); // Reload axis lock
+ _internal_set_mass(0 == mass ? 1 : mass);
+ break;
+ }
+ case PhysicsServer::BODY_MODE_CHARACTER: {
+ mode = PhysicsServer::BODY_MODE_CHARACTER;
+ set_axis_lock(axis_lock); // Reload axis lock
+ _internal_set_mass(0 == mass ? 1 : mass);
+ break;
+ }
+ }
+
+ btBody->setAngularVelocity(btVector3(0, 0, 0));
+ btBody->setLinearVelocity(btVector3(0, 0, 0));
+}
+PhysicsServer::BodyMode RigidBodyBullet::get_mode() const {
+ return mode;
+}
+
+void RigidBodyBullet::set_state(PhysicsServer::BodyState p_state, const Variant &p_variant) {
+
+ switch (p_state) {
+ case PhysicsServer::BODY_STATE_TRANSFORM:
+ set_transform(p_variant);
+ break;
+ case PhysicsServer::BODY_STATE_LINEAR_VELOCITY:
+ set_linear_velocity(p_variant);
+ break;
+ case PhysicsServer::BODY_STATE_ANGULAR_VELOCITY:
+ set_angular_velocity(p_variant);
+ break;
+ case PhysicsServer::BODY_STATE_SLEEPING:
+ set_activation_state(!bool(p_variant));
+ break;
+ case PhysicsServer::BODY_STATE_CAN_SLEEP:
+ can_sleep = bool(p_variant);
+ if (!can_sleep) {
+ // Can't sleep
+ btBody->forceActivationState(DISABLE_DEACTIVATION);
+ }
+ break;
+ }
+}
+
+Variant RigidBodyBullet::get_state(PhysicsServer::BodyState p_state) const {
+ switch (p_state) {
+ case PhysicsServer::BODY_STATE_TRANSFORM:
+ return get_transform();
+ case PhysicsServer::BODY_STATE_LINEAR_VELOCITY:
+ return get_linear_velocity();
+ case PhysicsServer::BODY_STATE_ANGULAR_VELOCITY:
+ return get_angular_velocity();
+ case PhysicsServer::BODY_STATE_SLEEPING:
+ return !is_active();
+ case PhysicsServer::BODY_STATE_CAN_SLEEP:
+ return can_sleep;
+ default:
+ WARN_PRINTS("This state " + itos(p_state) + " is not supported by Bullet");
+ return Variant();
+ }
+}
+
+void RigidBodyBullet::apply_central_impulse(const Vector3 &p_impulse) {
+ btVector3 btImpu;
+ G_TO_B(p_impulse, btImpu);
+ btBody->activate();
+ btBody->applyCentralImpulse(btImpu);
+}
+
+void RigidBodyBullet::apply_impulse(const Vector3 &p_pos, const Vector3 &p_impulse) {
+ btVector3 btImpu;
+ btVector3 btPos;
+ G_TO_B(p_impulse, btImpu);
+ G_TO_B(p_pos, btPos);
+ btBody->activate();
+ btBody->applyImpulse(btImpu, btPos);
+}
+
+void RigidBodyBullet::apply_torque_impulse(const Vector3 &p_impulse) {
+ btVector3 btImp;
+ G_TO_B(p_impulse, btImp);
+ btBody->activate();
+ btBody->applyTorqueImpulse(btImp);
+}
+
+void RigidBodyBullet::apply_force(const Vector3 &p_force, const Vector3 &p_pos) {
+ btVector3 btForce;
+ btVector3 btPos;
+ G_TO_B(p_force, btForce);
+ G_TO_B(p_pos, btPos);
+ btBody->activate();
+ btBody->applyForce(btForce, btPos);
+}
+
+void RigidBodyBullet::apply_central_force(const Vector3 &p_force) {
+ btVector3 btForce;
+ G_TO_B(p_force, btForce);
+ btBody->activate();
+ btBody->applyCentralForce(btForce);
+}
+
+void RigidBodyBullet::apply_torque(const Vector3 &p_torque) {
+ btVector3 btTorq;
+ G_TO_B(p_torque, btTorq);
+ btBody->activate();
+ btBody->applyTorque(btTorq);
+}
+
+void RigidBodyBullet::set_applied_force(const Vector3 &p_force) {
+ btVector3 btVec = btBody->getTotalTorque();
+
+ btBody->activate();
+
+ btBody->clearForces();
+ btBody->applyTorque(btVec);
+
+ G_TO_B(p_force, btVec);
+ btBody->applyCentralForce(btVec);
+}
+
+Vector3 RigidBodyBullet::get_applied_force() const {
+ Vector3 gTotForc;
+ B_TO_G(btBody->getTotalForce(), gTotForc);
+ return gTotForc;
+}
+
+void RigidBodyBullet::set_applied_torque(const Vector3 &p_torque) {
+ btVector3 btVec = btBody->getTotalForce();
+
+ btBody->activate();
+
+ btBody->clearForces();
+ btBody->applyCentralForce(btVec);
+
+ G_TO_B(p_torque, btVec);
+ btBody->applyTorque(btVec);
+}
+
+Vector3 RigidBodyBullet::get_applied_torque() const {
+ Vector3 gTotTorq;
+ B_TO_G(btBody->getTotalTorque(), gTotTorq);
+ return gTotTorq;
+}
+
+void RigidBodyBullet::set_axis_lock(PhysicsServer::BodyAxisLock p_lock) {
+ axis_lock = p_lock;
+
+ if (PhysicsServer::BODY_AXIS_LOCK_DISABLED == axis_lock) {
+ btBody->setLinearFactor(btVector3(1., 1., 1.));
+ btBody->setAngularFactor(btVector3(1., 1., 1.));
+ } else if (PhysicsServer::BODY_AXIS_LOCK_X == axis_lock) {
+ btBody->setLinearFactor(btVector3(0., 1., 1.));
+ btBody->setAngularFactor(btVector3(1., 0., 0.));
+ } else if (PhysicsServer::BODY_AXIS_LOCK_Y == axis_lock) {
+ btBody->setLinearFactor(btVector3(1., 0., 1.));
+ btBody->setAngularFactor(btVector3(0., 1., 0.));
+ } else if (PhysicsServer::BODY_AXIS_LOCK_Z == axis_lock) {
+ btBody->setLinearFactor(btVector3(1., 1., 0.));
+ btBody->setAngularFactor(btVector3(0., 0., 1.));
+ }
+
+ if (PhysicsServer::BODY_MODE_CHARACTER == mode) {
+ /// When character lock angular
+ btBody->setAngularFactor(btVector3(0., 0., 0.));
+ }
+}
+
+PhysicsServer::BodyAxisLock RigidBodyBullet::get_axis_lock() const {
+ btVector3 vec = btBody->getLinearFactor();
+ if (0. == vec.x()) {
+ return PhysicsServer::BODY_AXIS_LOCK_X;
+ } else if (0. == vec.y()) {
+ return PhysicsServer::BODY_AXIS_LOCK_Y;
+ } else if (0. == vec.z()) {
+ return PhysicsServer::BODY_AXIS_LOCK_Z;
+ } else {
+ return PhysicsServer::BODY_AXIS_LOCK_DISABLED;
+ }
+}
+
+void RigidBodyBullet::set_continuous_collision_detection(bool p_enable) {
+ if (p_enable) {
+ // This threshold enable CCD if the object moves more than
+ // 1 meter in one simulation frame
+ btBody->setCcdMotionThreshold(1);
+
+ /// Calculate using the rule writte below the CCD swept sphere radius
+ /// CCD works on an embedded sphere of radius, make sure this radius
+ /// is embedded inside the convex objects, preferably smaller:
+ /// for an object of dimentions 1 meter, try 0.2
+ btVector3 center;
+ btScalar radius;
+ btBody->getCollisionShape()->getBoundingSphere(center, radius);
+ btBody->setCcdSweptSphereRadius(radius * 0.2);
+ } else {
+ btBody->setCcdMotionThreshold(0.);
+ btBody->setCcdSweptSphereRadius(0.);
+ }
+}
+
+bool RigidBodyBullet::is_continuous_collision_detection_enabled() const {
+ return 0. != btBody->getCcdMotionThreshold();
+}
+
+void RigidBodyBullet::set_linear_velocity(const Vector3 &p_velocity) {
+ btVector3 btVec;
+ G_TO_B(p_velocity, btVec);
+ btBody->activate();
+ btBody->setLinearVelocity(btVec);
+}
+
+Vector3 RigidBodyBullet::get_linear_velocity() const {
+ Vector3 gVec;
+ B_TO_G(btBody->getLinearVelocity(), gVec);
+ return gVec;
+}
+
+void RigidBodyBullet::set_angular_velocity(const Vector3 &p_velocity) {
+ btVector3 btVec;
+ G_TO_B(p_velocity, btVec);
+ btBody->activate();
+ btBody->setAngularVelocity(btVec);
+}
+
+Vector3 RigidBodyBullet::get_angular_velocity() const {
+ Vector3 gVec;
+ B_TO_G(btBody->getAngularVelocity(), gVec);
+ return gVec;
+}
+
+void RigidBodyBullet::set_transform__bullet(const btTransform &p_global_transform) {
+ if (mode == PhysicsServer::BODY_MODE_KINEMATIC) {
+ // The kinematic use MotionState class
+ godotMotionState->moveBody(p_global_transform);
+ }
+ btBody->setWorldTransform(p_global_transform);
+}
+
+const btTransform &RigidBodyBullet::get_transform__bullet() const {
+ if (is_static()) {
+
+ return RigidCollisionObjectBullet::get_transform__bullet();
+ } else {
+
+ return godotMotionState->getCurrentWorldTransform();
+ }
+}
+
+void RigidBodyBullet::on_shapes_changed() {
+ RigidCollisionObjectBullet::on_shapes_changed();
+
+ const btScalar invMass = btBody->getInvMass();
+ const btScalar mass = invMass == 0 ? 0 : 1 / invMass;
+
+ btVector3 inertia;
+ btBody->getCollisionShape()->calculateLocalInertia(mass, inertia);
+ btBody->setMassProps(mass, inertia);
+ btBody->updateInertiaTensor();
+
+ reload_kinematic_shapes();
+
+ reload_body();
+}
+
+void RigidBodyBullet::on_enter_area(AreaBullet *p_area) {
+ /// Add this area to the array in an ordered way
+ ++areaWhereIamCount;
+ if (areaWhereIamCount >= maxAreasWhereIam) {
+ --areaWhereIamCount;
+ return;
+ }
+ for (int i = 0; i < areaWhereIamCount; ++i) {
+
+ if (NULL == areasWhereIam[i]) {
+ // This area has the highest priority
+ areasWhereIam[i] = p_area;
+ break;
+ } else {
+ if (areasWhereIam[i]->get_spOv_priority() > p_area->get_spOv_priority()) {
+ // The position was found, just shift all elements
+ for (int j = i; j < areaWhereIamCount; ++j) {
+ areasWhereIam[j + 1] = areasWhereIam[j];
+ }
+ areasWhereIam[i] = p_area;
+ break;
+ }
+ }
+ }
+ if (PhysicsServer::AREA_SPACE_OVERRIDE_DISABLED != p_area->get_spOv_mode()) {
+ scratch_space_override_modificator();
+ }
+
+ if (p_area->is_spOv_gravityPoint()) {
+ ++countGravityPointSpaces;
+ assert(0 < countGravityPointSpaces);
+ }
+}
+
+void RigidBodyBullet::on_exit_area(AreaBullet *p_area) {
+ RigidCollisionObjectBullet::on_exit_area(p_area);
+ /// Remove this area and keep the order
+ /// N.B. Since I don't want resize the array I can't use the "erase" function
+ bool wasTheAreaFound = false;
+ for (int i = 0; i < areaWhereIamCount; ++i) {
+ if (p_area == areasWhereIam[i]) {
+ // The area was fount, just shift down all elements
+ for (int j = i; j < areaWhereIamCount; ++j) {
+ areasWhereIam[j] = areasWhereIam[j + 1];
+ }
+ wasTheAreaFound = true;
+ break;
+ }
+ }
+ if (wasTheAreaFound) {
+ if (p_area->is_spOv_gravityPoint()) {
+ --countGravityPointSpaces;
+ assert(0 <= countGravityPointSpaces);
+ }
+
+ --areaWhereIamCount;
+ areasWhereIam[areaWhereIamCount] = NULL; // Even if this is not required, I clear the last element to be safe
+ if (PhysicsServer::AREA_SPACE_OVERRIDE_DISABLED != p_area->get_spOv_mode()) {
+ scratch_space_override_modificator();
+ }
+ }
+}
+
+void RigidBodyBullet::reload_space_override_modificator() {
+
+ Vector3 newGravity(space->get_gravity_direction() * space->get_gravity_magnitude());
+ real_t newLinearDamp(linearDamp);
+ real_t newAngularDamp(angularDamp);
+
+ AreaBullet *currentArea;
+ // Variable used to calculate new gravity for gravity point areas, it is pointed by currentGravity pointer
+ Vector3 support_gravity(0, 0, 0);
+
+ int countCombined(0);
+ for (int i = areaWhereIamCount - 1; 0 <= i; --i) {
+
+ currentArea = areasWhereIam[i];
+
+ if (PhysicsServer::AREA_SPACE_OVERRIDE_DISABLED == currentArea->get_spOv_mode()) {
+ continue;
+ }
+
+ /// Here is calculated the gravity
+ if (currentArea->is_spOv_gravityPoint()) {
+
+ /// It calculates the direction of new gravity
+ support_gravity = currentArea->get_transform().xform(currentArea->get_spOv_gravityVec()) - get_transform().get_origin();
+ real_t distanceMag = support_gravity.length();
+ // Normalized in this way to avoid the double call of function "length()"
+ if (distanceMag == 0) {
+ support_gravity.x = 0;
+ support_gravity.y = 0;
+ support_gravity.z = 0;
+ } else {
+ support_gravity.x /= distanceMag;
+ support_gravity.y /= distanceMag;
+ support_gravity.z /= distanceMag;
+ }
+
+ /// Here is calculated the final gravity
+ if (currentArea->get_spOv_gravityPointDistanceScale() > 0) {
+ // Scaled gravity by distance
+ support_gravity *= currentArea->get_spOv_gravityMag() / Math::pow(distanceMag * currentArea->get_spOv_gravityPointDistanceScale() + 1, 2);
+ } else {
+ // Unscaled gravity
+ support_gravity *= currentArea->get_spOv_gravityMag();
+ }
+ } else {
+ support_gravity = currentArea->get_spOv_gravityVec() * currentArea->get_spOv_gravityMag();
+ }
+
+ switch (currentArea->get_spOv_mode()) {
+ ///case PhysicsServer::AREA_SPACE_OVERRIDE_DISABLED:
+ /// This area does not affect gravity/damp. These are generally areas
+ /// that exist only to detect collisions, and objects entering or exiting them.
+ /// break;
+ case PhysicsServer::AREA_SPACE_OVERRIDE_COMBINE:
+ /// This area adds its gravity/damp values to whatever has been
+ /// calculated so far. This way, many overlapping areas can combine
+ /// their physics to make interesting
+ newGravity += support_gravity;
+ newLinearDamp += currentArea->get_spOv_linearDamp();
+ newAngularDamp += currentArea->get_spOv_angularDamp();
+ ++countCombined;
+ break;
+ case PhysicsServer::AREA_SPACE_OVERRIDE_COMBINE_REPLACE:
+ /// This area adds its gravity/damp values to whatever has been calculated
+ /// so far. Then stops taking into account the rest of the areas, even the
+ /// default one.
+ newGravity += support_gravity;
+ newLinearDamp += currentArea->get_spOv_linearDamp();
+ newAngularDamp += currentArea->get_spOv_angularDamp();
+ ++countCombined;
+ goto endAreasCycle;
+ case PhysicsServer::AREA_SPACE_OVERRIDE_REPLACE:
+ /// This area replaces any gravity/damp, even the default one, and
+ /// stops taking into account the rest of the areas.
+ newGravity = support_gravity;
+ newLinearDamp = currentArea->get_spOv_linearDamp();
+ newAngularDamp = currentArea->get_spOv_angularDamp();
+ countCombined = 1;
+ goto endAreasCycle;
+ case PhysicsServer::AREA_SPACE_OVERRIDE_REPLACE_COMBINE:
+ /// This area replaces any gravity/damp calculated so far, but keeps
+ /// calculating the rest of the areas, down to the default one.
+ newGravity = support_gravity;
+ newLinearDamp = currentArea->get_spOv_linearDamp();
+ newAngularDamp = currentArea->get_spOv_angularDamp();
+ countCombined = 1;
+ break;
+ }
+ }
+endAreasCycle:
+
+ if (1 < countCombined) {
+ newGravity /= countCombined;
+ newLinearDamp /= countCombined;
+ newAngularDamp /= countCombined;
+ }
+
+ btVector3 newBtGravity;
+ G_TO_B(newGravity * gravity_scale, newBtGravity);
+
+ btBody->setGravity(newBtGravity);
+ btBody->setDamping(newLinearDamp, newAngularDamp);
+}
+
+void RigidBodyBullet::reload_kinematic_shapes() {
+ if (!kinematic_utilities) {
+ return;
+ }
+ kinematic_utilities->copyAllOwnerShapes();
+}
+
+void RigidBodyBullet::_internal_set_mass(real_t p_mass) {
+
+ btVector3 localInertia(0, 0, 0);
+
+ int clearedCurrentFlags = btBody->getCollisionFlags();
+ clearedCurrentFlags &= ~(btCollisionObject::CF_KINEMATIC_OBJECT | btCollisionObject::CF_STATIC_OBJECT | btCollisionObject::CF_CHARACTER_OBJECT);
+
+ // Rigidbody is dynamic if and only if mass is non Zero, otherwise static
+ const bool isDynamic = p_mass != 0.f;
+ if (isDynamic) {
+
+ ERR_FAIL_COND(PhysicsServer::BODY_MODE_RIGID != mode && PhysicsServer::BODY_MODE_CHARACTER != mode);
+
+ m_isStatic = false;
+ compoundShape->calculateLocalInertia(p_mass, localInertia);
+
+ if (PhysicsServer::BODY_MODE_RIGID == mode) {
+
+ btBody->setCollisionFlags(clearedCurrentFlags); // Just set the flags without Kin and Static
+ } else {
+
+ btBody->setCollisionFlags(clearedCurrentFlags | btCollisionObject::CF_CHARACTER_OBJECT);
+ }
+
+ if (can_sleep) {
+ btBody->forceActivationState(ACTIVE_TAG); // ACTIVE_TAG 1
+ } else {
+ btBody->forceActivationState(DISABLE_DEACTIVATION); // DISABLE_DEACTIVATION 4
+ }
+ } else {
+
+ ERR_FAIL_COND(PhysicsServer::BODY_MODE_STATIC != mode && PhysicsServer::BODY_MODE_KINEMATIC != mode);
+
+ m_isStatic = true;
+ if (PhysicsServer::BODY_MODE_STATIC == mode) {
+
+ btBody->setCollisionFlags(clearedCurrentFlags | btCollisionObject::CF_STATIC_OBJECT);
+ } else {
+
+ btBody->setCollisionFlags(clearedCurrentFlags | btCollisionObject::CF_KINEMATIC_OBJECT);
+ set_transform__bullet(btBody->getWorldTransform()); // Set current Transform using kinematic method
+ }
+ btBody->forceActivationState(DISABLE_SIMULATION); // DISABLE_SIMULATION 5
+ }
+
+ btBody->setMassProps(p_mass, localInertia);
+ btBody->updateInertiaTensor();
+
+ reload_body();
+}
diff --git a/modules/bullet/rigid_body_bullet.h b/modules/bullet/rigid_body_bullet.h
new file mode 100644
index 0000000000..0cf3f9f605
--- /dev/null
+++ b/modules/bullet/rigid_body_bullet.h
@@ -0,0 +1,304 @@
+/*************************************************************************/
+/* body_bullet.h */
+/* Author: AndreaCatania */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2017 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 BODYBULLET_H
+#define BODYBULLET_H
+
+#include "BulletCollision/BroadphaseCollision/btCollisionAlgorithm.h"
+#include "LinearMath/btTransform.h"
+#include "collision_object_bullet.h"
+#include "space_bullet.h"
+
+class AreaBullet;
+class SpaceBullet;
+class btRigidBody;
+class GodotMotionState;
+class BulletPhysicsDirectBodyState;
+
+/// This class could be used in multi thread with few changes but currently
+/// is setted to be only in one single thread.
+///
+/// In the system there is only one object at a time that manage all bodies and is
+/// created by BulletPhysicsServer and is held by the "singleton" variable of this class
+/// Each time something require it, the body must be setted again.
+class BulletPhysicsDirectBodyState : public PhysicsDirectBodyState {
+ GDCLASS(BulletPhysicsDirectBodyState, PhysicsDirectBodyState)
+
+ static BulletPhysicsDirectBodyState *singleton;
+
+public:
+ /// This class avoid the creation of more object of this class
+ static void initSingleton() {
+ if (!singleton) {
+ singleton = memnew(BulletPhysicsDirectBodyState);
+ }
+ }
+
+ static void destroySingleton() {
+ memdelete(singleton);
+ singleton = NULL;
+ }
+
+ static void singleton_setDeltaTime(real_t p_deltaTime) {
+ singleton->deltaTime = p_deltaTime;
+ }
+
+ static BulletPhysicsDirectBodyState *get_singleton(RigidBodyBullet *p_body) {
+ singleton->body = p_body;
+ return singleton;
+ }
+
+public:
+ RigidBodyBullet *body;
+ real_t deltaTime;
+
+private:
+ BulletPhysicsDirectBodyState() {}
+
+public:
+ virtual Vector3 get_total_gravity() const;
+ virtual float get_total_angular_damp() const;
+ virtual float get_total_linear_damp() const;
+
+ virtual Vector3 get_center_of_mass() const;
+ virtual Basis get_principal_inertia_axes() const;
+ // get the mass
+ virtual float get_inverse_mass() const;
+ // get density of this body space
+ virtual Vector3 get_inverse_inertia() const;
+ // get density of this body space
+ virtual Basis get_inverse_inertia_tensor() const;
+
+ virtual void set_linear_velocity(const Vector3 &p_velocity);
+ virtual Vector3 get_linear_velocity() const;
+
+ virtual void set_angular_velocity(const Vector3 &p_velocity);
+ virtual Vector3 get_angular_velocity() const;
+
+ virtual void set_transform(const Transform &p_transform);
+ virtual Transform get_transform() const;
+
+ virtual void add_force(const Vector3 &p_force, const Vector3 &p_pos);
+ virtual void apply_impulse(const Vector3 &p_pos, const Vector3 &p_j);
+ virtual void apply_torque_impulse(const Vector3 &p_j);
+
+ virtual void set_sleep_state(bool p_enable);
+ virtual bool is_sleeping() const;
+
+ virtual int get_contact_count() const;
+
+ virtual Vector3 get_contact_local_position(int p_contact_idx) const;
+ virtual Vector3 get_contact_local_normal(int p_contact_idx) const;
+ virtual int get_contact_local_shape(int p_contact_idx) const;
+
+ virtual RID get_contact_collider(int p_contact_idx) const;
+ virtual Vector3 get_contact_collider_position(int p_contact_idx) const;
+ virtual ObjectID get_contact_collider_id(int p_contact_idx) const;
+ virtual int get_contact_collider_shape(int p_contact_idx) const;
+ virtual Vector3 get_contact_collider_velocity_at_position(int p_contact_idx) const;
+
+ virtual real_t get_step() const { return deltaTime; }
+ virtual void integrate_forces() {
+ // Skip the execution of this function
+ }
+
+ virtual PhysicsDirectSpaceState *get_space_state();
+};
+
+class RigidBodyBullet : public RigidCollisionObjectBullet {
+
+public:
+ struct CollisionData {
+ RigidBodyBullet *otherObject;
+ int other_object_shape;
+ int local_shape;
+ Vector3 hitLocalLocation;
+ Vector3 hitWorldLocation;
+ Vector3 hitNormal;
+ };
+
+ struct ForceIntegrationCallback {
+ ObjectID id;
+ StringName method;
+ Variant udata;
+ };
+
+ /// Used to hold shapes
+ struct KinematicShape {
+ class btConvexShape *shape;
+ btTransform transform;
+
+ KinematicShape()
+ : shape(NULL) {}
+ const bool is_active() const { return shape; }
+ };
+
+ struct KinematicUtilities {
+ RigidBodyBullet *m_owner;
+ btScalar m_margin;
+ btManifoldArray m_manifoldArray; ///keep track of the contact manifolds
+ class btPairCachingGhostObject *m_ghostObject;
+ Vector<KinematicShape> m_shapes;
+
+ KinematicUtilities(RigidBodyBullet *p_owner);
+ ~KinematicUtilities();
+
+ /// Used to set the default shape to ghost
+ void resetDefShape();
+ void copyAllOwnerShapes();
+
+ private:
+ void just_delete_shapes(int new_size);
+ };
+
+private:
+ friend class BulletPhysicsDirectBodyState;
+
+ // This is required only for Kinematic movement
+ KinematicUtilities *kinematic_utilities;
+
+ PhysicsServer::BodyMode mode;
+ PhysicsServer::BodyAxisLock axis_lock;
+ GodotMotionState *godotMotionState;
+ btRigidBody *btBody;
+ real_t mass;
+ real_t gravity_scale;
+ real_t linearDamp;
+ real_t angularDamp;
+ bool can_sleep;
+
+ Vector<CollisionData> collisions;
+ // these parameters are used to avoid vector resize
+ int maxCollisionsDetection;
+ int collisionsCount;
+
+ Vector<AreaBullet *> areasWhereIam;
+ // these parameters are used to avoid vector resize
+ int maxAreasWhereIam;
+ int areaWhereIamCount;
+ // Used to know if the area is used as gravity point
+ int countGravityPointSpaces;
+ bool isScratchedSpaceOverrideModificator;
+
+ bool isTransformChanged;
+
+ ForceIntegrationCallback *force_integration_callback;
+
+public:
+ RigidBodyBullet();
+ ~RigidBodyBullet();
+
+ void init_kinematic_utilities();
+ void destroy_kinematic_utilities();
+ _FORCE_INLINE_ class KinematicUtilities *get_kinematic_utilities() const { return kinematic_utilities; }
+
+ _FORCE_INLINE_ btRigidBody *get_bt_rigid_body() { return btBody; }
+
+ virtual void reload_body();
+ virtual void set_space(SpaceBullet *p_space);
+
+ virtual void dispatch_callbacks();
+ void set_force_integration_callback(ObjectID p_id, const StringName &p_method, const Variant &p_udata = Variant());
+ void scratch();
+ void scratch_space_override_modificator();
+
+ virtual void on_collision_filters_change();
+ virtual void on_collision_checker_start();
+ void set_max_collisions_detection(int p_maxCollisionsDetection) {
+ maxCollisionsDetection = p_maxCollisionsDetection;
+ collisions.resize(p_maxCollisionsDetection);
+ collisionsCount = 0;
+ }
+ int get_max_collisions_detection() {
+ return maxCollisionsDetection;
+ }
+
+ bool can_add_collision() { return collisionsCount < maxCollisionsDetection; }
+ bool add_collision_object(RigidBodyBullet *p_otherObject, const Vector3 &p_hitWorldLocation, const Vector3 &p_hitLocalLocation, const Vector3 &p_hitNormal, int p_other_shape_index, int p_local_shape_index);
+
+ void assert_no_constraints();
+
+ void set_activation_state(bool p_active);
+ bool is_active() const;
+
+ void set_param(PhysicsServer::BodyParameter p_param, real_t);
+ real_t get_param(PhysicsServer::BodyParameter p_param) const;
+
+ void set_mode(PhysicsServer::BodyMode p_mode);
+ PhysicsServer::BodyMode get_mode() const;
+
+ void set_state(PhysicsServer::BodyState p_state, const Variant &p_variant);
+ Variant get_state(PhysicsServer::BodyState p_state) const;
+
+ void apply_impulse(const Vector3 &p_pos, const Vector3 &p_impulse);
+ void apply_central_impulse(const Vector3 &p_force);
+ void apply_torque_impulse(const Vector3 &p_impulse);
+
+ void apply_force(const Vector3 &p_force, const Vector3 &p_pos);
+ void apply_central_force(const Vector3 &p_force);
+ void apply_torque(const Vector3 &p_force);
+
+ void set_applied_force(const Vector3 &p_force);
+ Vector3 get_applied_force() const;
+ void set_applied_torque(const Vector3 &p_torque);
+ Vector3 get_applied_torque() const;
+
+ void set_axis_lock(PhysicsServer::BodyAxisLock p_lock);
+ PhysicsServer::BodyAxisLock get_axis_lock() const;
+
+ /// Doc:
+ /// http://www.bulletphysics.org/mediawiki-1.5.8/index.php?title=Anti_tunneling_by_Motion_Clamping
+ void set_continuous_collision_detection(bool p_enable);
+ bool is_continuous_collision_detection_enabled() const;
+
+ void set_linear_velocity(const Vector3 &p_velocity);
+ Vector3 get_linear_velocity() const;
+
+ void set_angular_velocity(const Vector3 &p_velocity);
+ Vector3 get_angular_velocity() const;
+
+ virtual void set_transform__bullet(const btTransform &p_global_transform);
+ virtual const btTransform &get_transform__bullet() const;
+
+ virtual void on_shapes_changed();
+
+ virtual void on_enter_area(AreaBullet *p_area);
+ virtual void on_exit_area(AreaBullet *p_area);
+ void reload_space_override_modificator();
+
+ /// Kinematic
+ void reload_kinematic_shapes();
+
+private:
+ void _internal_set_mass(real_t p_mass);
+};
+
+#endif
diff --git a/modules/bullet/shape_bullet.cpp b/modules/bullet/shape_bullet.cpp
new file mode 100644
index 0000000000..49150484d9
--- /dev/null
+++ b/modules/bullet/shape_bullet.cpp
@@ -0,0 +1,435 @@
+/*************************************************************************/
+/* shape_bullet.cpp */
+/* Author: AndreaCatania */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2017 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 "shape_bullet.h"
+#include "BulletCollision/CollisionShapes/btConvexPointCloudShape.h"
+#include "BulletCollision/CollisionShapes/btHeightfieldTerrainShape.h"
+#include "btBulletCollisionCommon.h"
+#include "btRayShape.h"
+#include "bullet_physics_server.h"
+#include "bullet_types_converter.h"
+#include "bullet_utilities.h"
+#include "shape_owner_bullet.h"
+
+ShapeBullet::ShapeBullet() {}
+
+ShapeBullet::~ShapeBullet() {}
+
+btCollisionShape *ShapeBullet::prepare(btCollisionShape *p_btShape) const {
+ p_btShape->setUserPointer(const_cast<ShapeBullet *>(this));
+ return p_btShape;
+}
+
+void ShapeBullet::notifyShapeChanged() {
+ for (Map<ShapeOwnerBullet *, int>::Element *E = owners.front(); E; E = E->next()) {
+ static_cast<ShapeOwnerBullet *>(E->key())->on_shape_changed(this);
+ }
+}
+
+void ShapeBullet::add_owner(ShapeOwnerBullet *p_owner) {
+ Map<ShapeOwnerBullet *, int>::Element *E = owners.find(p_owner);
+ if (E) {
+ E->get()++;
+ } else {
+ owners[p_owner] = 1; // add new owner
+ }
+}
+
+void ShapeBullet::remove_owner(ShapeOwnerBullet *p_owner, bool p_permanentlyFromThisBody) {
+ Map<ShapeOwnerBullet *, int>::Element *E = owners.find(p_owner);
+ ERR_FAIL_COND(!E);
+ E->get()--;
+ if (p_permanentlyFromThisBody || 0 >= E->get()) {
+ owners.erase(E);
+ }
+}
+
+bool ShapeBullet::is_owner(ShapeOwnerBullet *p_owner) const {
+
+ return owners.has(p_owner);
+}
+
+const Map<ShapeOwnerBullet *, int> &ShapeBullet::get_owners() const {
+ return owners;
+}
+
+btEmptyShape *ShapeBullet::create_shape_empty() {
+ return bulletnew(btEmptyShape);
+}
+
+btStaticPlaneShape *ShapeBullet::create_shape_plane(const btVector3 &planeNormal, btScalar planeConstant) {
+ return bulletnew(btStaticPlaneShape(planeNormal, planeConstant));
+}
+
+btSphereShape *ShapeBullet::create_shape_sphere(btScalar radius) {
+ return bulletnew(btSphereShape(radius));
+}
+
+btBoxShape *ShapeBullet::create_shape_box(const btVector3 &boxHalfExtents) {
+ return bulletnew(btBoxShape(boxHalfExtents));
+}
+
+btCapsuleShapeZ *ShapeBullet::create_shape_capsule(btScalar radius, btScalar height) {
+ return bulletnew(btCapsuleShapeZ(radius, height));
+}
+
+btConvexPointCloudShape *ShapeBullet::create_shape_convex(btAlignedObjectArray<btVector3> &p_vertices, const btVector3 &p_local_scaling) {
+ return bulletnew(btConvexPointCloudShape(&p_vertices[0], p_vertices.size(), p_local_scaling));
+}
+
+btScaledBvhTriangleMeshShape *ShapeBullet::create_shape_concave(btBvhTriangleMeshShape *p_mesh_shape, const btVector3 &p_local_scaling) {
+ if (p_mesh_shape) {
+ return bulletnew(btScaledBvhTriangleMeshShape(p_mesh_shape, p_local_scaling));
+ } else {
+ return NULL;
+ }
+}
+
+btHeightfieldTerrainShape *ShapeBullet::create_shape_height_field(PoolVector<real_t> &p_heights, int p_width, int p_depth, real_t p_cell_size) {
+ const btScalar ignoredHeightScale(1);
+ const btScalar fieldHeight(500); // Meters
+ const int YAxis = 1; // 0=X, 1=Y, 2=Z
+ const bool flipQuadEdges = false;
+ const void *heightsPtr = p_heights.read().ptr();
+
+ return bulletnew(btHeightfieldTerrainShape(p_width, p_depth, heightsPtr, ignoredHeightScale, -fieldHeight, fieldHeight, YAxis, PHY_FLOAT, flipQuadEdges));
+}
+
+btRayShape *ShapeBullet::create_shape_ray(real_t p_length) {
+ return bulletnew(btRayShape(p_length));
+}
+
+/* PLANE */
+
+PlaneShapeBullet::PlaneShapeBullet()
+ : ShapeBullet() {}
+
+void PlaneShapeBullet::set_data(const Variant &p_data) {
+ setup(p_data);
+}
+
+Variant PlaneShapeBullet::get_data() const {
+ return plane;
+}
+
+PhysicsServer::ShapeType PlaneShapeBullet::get_type() const {
+ return PhysicsServer::SHAPE_PLANE;
+}
+
+void PlaneShapeBullet::setup(const Plane &p_plane) {
+ plane = p_plane;
+ notifyShapeChanged();
+}
+
+btCollisionShape *PlaneShapeBullet::create_bt_shape() {
+ btVector3 btPlaneNormal;
+ G_TO_B(plane.normal, btPlaneNormal);
+ return prepare(PlaneShapeBullet::create_shape_plane(btPlaneNormal, plane.d));
+}
+
+/* Sphere */
+
+SphereShapeBullet::SphereShapeBullet()
+ : ShapeBullet() {}
+
+void SphereShapeBullet::set_data(const Variant &p_data) {
+ setup(p_data);
+}
+
+Variant SphereShapeBullet::get_data() const {
+ return radius;
+}
+
+PhysicsServer::ShapeType SphereShapeBullet::get_type() const {
+ return PhysicsServer::SHAPE_SPHERE;
+}
+
+void SphereShapeBullet::setup(real_t p_radius) {
+ radius = p_radius;
+ notifyShapeChanged();
+}
+
+btCollisionShape *SphereShapeBullet::create_bt_shape() {
+ return prepare(ShapeBullet::create_shape_sphere(radius));
+}
+
+/* Box */
+BoxShapeBullet::BoxShapeBullet()
+ : ShapeBullet() {}
+
+void BoxShapeBullet::set_data(const Variant &p_data) {
+ setup(p_data);
+}
+
+Variant BoxShapeBullet::get_data() const {
+ Vector3 g_half_extents;
+ B_TO_G(half_extents, g_half_extents);
+ return g_half_extents;
+}
+
+PhysicsServer::ShapeType BoxShapeBullet::get_type() const {
+ return PhysicsServer::SHAPE_BOX;
+}
+
+void BoxShapeBullet::setup(const Vector3 &p_half_extents) {
+ G_TO_B(p_half_extents, half_extents);
+ notifyShapeChanged();
+}
+
+btCollisionShape *BoxShapeBullet::create_bt_shape() {
+ return prepare(ShapeBullet::create_shape_box(half_extents));
+}
+
+/* Capsule */
+
+CapsuleShapeBullet::CapsuleShapeBullet()
+ : ShapeBullet() {}
+
+void CapsuleShapeBullet::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 CapsuleShapeBullet::get_data() const {
+ Dictionary d;
+ d["radius"] = radius;
+ d["height"] = height;
+ return d;
+}
+
+PhysicsServer::ShapeType CapsuleShapeBullet::get_type() const {
+ return PhysicsServer::SHAPE_CAPSULE;
+}
+
+void CapsuleShapeBullet::setup(real_t p_height, real_t p_radius) {
+ radius = p_radius;
+ height = p_height;
+ notifyShapeChanged();
+}
+
+btCollisionShape *CapsuleShapeBullet::create_bt_shape() {
+ return prepare(ShapeBullet::create_shape_capsule(radius, height));
+}
+
+/* Convex polygon */
+
+ConvexPolygonShapeBullet::ConvexPolygonShapeBullet()
+ : ShapeBullet() {}
+
+void ConvexPolygonShapeBullet::set_data(const Variant &p_data) {
+ setup(p_data);
+}
+
+void ConvexPolygonShapeBullet::get_vertices(Vector<Vector3> &out_vertices) {
+ const int n_of_vertices = vertices.size();
+ out_vertices.resize(n_of_vertices);
+ for (int i = n_of_vertices - 1; 0 <= i; --i) {
+ B_TO_G(vertices[i], out_vertices[i]);
+ }
+}
+
+Variant ConvexPolygonShapeBullet::get_data() const {
+ ConvexPolygonShapeBullet *variable_self = const_cast<ConvexPolygonShapeBullet *>(this);
+ Vector<Vector3> out_vertices;
+ variable_self->get_vertices(out_vertices);
+ return out_vertices;
+}
+
+PhysicsServer::ShapeType ConvexPolygonShapeBullet::get_type() const {
+ return PhysicsServer::SHAPE_CONVEX_POLYGON;
+}
+
+void ConvexPolygonShapeBullet::setup(const Vector<Vector3> &p_vertices) {
+ // Make a copy of verticies
+ const int n_of_vertices = p_vertices.size();
+ vertices.resize(n_of_vertices);
+ for (int i = n_of_vertices - 1; 0 <= i; --i) {
+ G_TO_B(p_vertices[i], vertices[i]);
+ }
+ notifyShapeChanged();
+}
+
+btCollisionShape *ConvexPolygonShapeBullet::create_bt_shape() {
+ return prepare(ShapeBullet::create_shape_convex(vertices));
+}
+
+/* Concave polygon */
+
+ConcavePolygonShapeBullet::ConcavePolygonShapeBullet()
+ : ShapeBullet(), meshShape(NULL) {}
+
+ConcavePolygonShapeBullet::~ConcavePolygonShapeBullet() {
+ if (meshShape) {
+ delete meshShape->getMeshInterface();
+ delete meshShape;
+ }
+ faces = PoolVector<Vector3>();
+}
+
+void ConcavePolygonShapeBullet::set_data(const Variant &p_data) {
+ setup(p_data);
+}
+
+Variant ConcavePolygonShapeBullet::get_data() const {
+ return faces;
+}
+
+PhysicsServer::ShapeType ConcavePolygonShapeBullet::get_type() const {
+ return PhysicsServer::SHAPE_CONCAVE_POLYGON;
+}
+
+void ConcavePolygonShapeBullet::setup(PoolVector<Vector3> p_faces) {
+ faces = p_faces;
+ if (meshShape) {
+ /// Clear previous created shape
+ delete meshShape->getMeshInterface();
+ bulletdelete(meshShape);
+ }
+ int src_face_count = faces.size();
+ if (0 < src_face_count) {
+
+ btTriangleMesh *shapeInterface = bulletnew(btTriangleMesh);
+
+ // It counts the faces and assert the array contains the correct number of vertices.
+ ERR_FAIL_COND(src_face_count % 3);
+ src_face_count /= 3;
+ PoolVector<Vector3>::Read r = p_faces.read();
+ const Vector3 *facesr = r.ptr();
+
+ btVector3 supVec_0;
+ btVector3 supVec_1;
+ btVector3 supVec_2;
+ for (int i = 0; i < src_face_count; ++i) {
+ G_TO_B(facesr[i * 3], supVec_0);
+ G_TO_B(facesr[i * 3 + 1], supVec_1);
+ G_TO_B(facesr[i * 3 + 2], supVec_2);
+
+ shapeInterface->addTriangle(supVec_0, supVec_1, supVec_2);
+ }
+
+ const bool useQuantizedAabbCompression = true;
+
+ meshShape = bulletnew(btBvhTriangleMeshShape(shapeInterface, useQuantizedAabbCompression));
+ } else {
+ meshShape = NULL;
+ ERR_PRINT("The faces count are 0, the mesh shape cannot be created");
+ }
+ notifyShapeChanged();
+}
+
+btCollisionShape *ConcavePolygonShapeBullet::create_bt_shape() {
+ btCollisionShape *cs = ShapeBullet::create_shape_concave(meshShape);
+ if (!cs) {
+ // This is necessary since if 0 faces the creation of concave return NULL
+ cs = ShapeBullet::create_shape_empty();
+ }
+ return prepare(cs);
+}
+
+/* Height map shape */
+
+HeightMapShapeBullet::HeightMapShapeBullet()
+ : ShapeBullet() {}
+
+void HeightMapShapeBullet::set_data(const Variant &p_data) {
+ ERR_FAIL_COND(p_data.get_type() != Variant::DICTIONARY);
+ Dictionary d = p_data;
+ ERR_FAIL_COND(!d.has("width"));
+ ERR_FAIL_COND(!d.has("depth"));
+ ERR_FAIL_COND(!d.has("cell_size"));
+ ERR_FAIL_COND(!d.has("heights"));
+
+ int l_width = d["width"];
+ int l_depth = d["depth"];
+ real_t l_cell_size = d["cell_size"];
+ PoolVector<real_t> l_heights = d["heights"];
+
+ ERR_FAIL_COND(l_width <= 0);
+ ERR_FAIL_COND(l_depth <= 0);
+ ERR_FAIL_COND(l_cell_size <= CMP_EPSILON);
+ ERR_FAIL_COND(l_heights.size() != (width * depth));
+ setup(heights, width, depth, cell_size);
+}
+
+Variant HeightMapShapeBullet::get_data() const {
+ ERR_FAIL_V(Variant());
+}
+
+PhysicsServer::ShapeType HeightMapShapeBullet::get_type() const {
+ return PhysicsServer::SHAPE_HEIGHTMAP;
+}
+
+void HeightMapShapeBullet::setup(PoolVector<real_t> &p_heights, int p_width, int p_depth, real_t p_cell_size) {
+ { // Copy
+ const int heights_size = p_heights.size();
+ heights.resize(heights_size);
+ PoolVector<real_t>::Read p_heights_r = p_heights.read();
+ PoolVector<real_t>::Write heights_w = heights.write();
+ for (int i = heights_size - 1; 0 <= i; --i) {
+ heights_w[i] = p_heights_r[i];
+ }
+ }
+ width = p_width;
+ depth = p_depth;
+ cell_size = p_cell_size;
+ notifyShapeChanged();
+}
+
+btCollisionShape *HeightMapShapeBullet::create_bt_shape() {
+ return prepare(ShapeBullet::create_shape_height_field(heights, width, depth, cell_size));
+}
+
+/* Ray shape */
+RayShapeBullet::RayShapeBullet()
+ : ShapeBullet(), length(1) {}
+
+void RayShapeBullet::set_data(const Variant &p_data) {
+ setup(p_data);
+}
+
+Variant RayShapeBullet::get_data() const {
+ return length;
+}
+
+PhysicsServer::ShapeType RayShapeBullet::get_type() const {
+ return PhysicsServer::SHAPE_RAY;
+}
+
+void RayShapeBullet::setup(real_t p_length) {
+ length = p_length;
+ notifyShapeChanged();
+}
+
+btCollisionShape *RayShapeBullet::create_bt_shape() {
+ return prepare(ShapeBullet::create_shape_ray(length));
+}
diff --git a/modules/bullet/shape_bullet.h b/modules/bullet/shape_bullet.h
new file mode 100644
index 0000000000..0a56fa1709
--- /dev/null
+++ b/modules/bullet/shape_bullet.h
@@ -0,0 +1,225 @@
+/*************************************************************************/
+/* shape_bullet.h */
+/* Author: AndreaCatania */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2017 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 SHAPE_BULLET_H
+#define SHAPE_BULLET_H
+
+#include "LinearMath/btAlignedObjectArray.h"
+#include "LinearMath/btScalar.h"
+#include "LinearMath/btVector3.h"
+#include "core/variant.h"
+#include "geometry.h"
+#include "rid_bullet.h"
+#include "servers/physics_server.h"
+
+class ShapeBullet;
+class btCollisionShape;
+class ShapeOwnerBullet;
+class btBvhTriangleMeshShape;
+
+class ShapeBullet : public RIDBullet {
+
+ Map<ShapeOwnerBullet *, int> owners;
+
+protected:
+ /// return self
+ btCollisionShape *prepare(btCollisionShape *p_btShape) const;
+ void notifyShapeChanged();
+
+public:
+ ShapeBullet();
+ virtual ~ShapeBullet();
+
+ virtual btCollisionShape *create_bt_shape() = 0;
+
+ void add_owner(ShapeOwnerBullet *p_owner);
+ void remove_owner(ShapeOwnerBullet *p_owner, bool p_permanentlyFromThisBody = false);
+ bool is_owner(ShapeOwnerBullet *p_owner) const;
+ const Map<ShapeOwnerBullet *, int> &get_owners() const;
+
+ /// Setup the shape
+ virtual void set_data(const Variant &p_data) = 0;
+ virtual Variant get_data() const = 0;
+
+ virtual PhysicsServer::ShapeType get_type() const = 0;
+
+public:
+ static class btEmptyShape *create_shape_empty();
+ static class btStaticPlaneShape *create_shape_plane(const btVector3 &planeNormal, btScalar planeConstant);
+ 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);
+ /// IMPORTANT: Remember to delete the shape interface by calling: delete my_shape->getMeshInterface();
+ static class btConvexPointCloudShape *create_shape_convex(btAlignedObjectArray<btVector3> &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));
+ static class btHeightfieldTerrainShape *create_shape_height_field(PoolVector<real_t> &p_heights, int p_width, int p_depth, real_t p_cell_size);
+ static class btRayShape *create_shape_ray(real_t p_length);
+};
+
+class PlaneShapeBullet : public ShapeBullet {
+
+ Plane plane;
+
+public:
+ PlaneShapeBullet();
+
+ virtual void set_data(const Variant &p_data);
+ virtual Variant get_data() const;
+ virtual PhysicsServer::ShapeType get_type() const;
+ virtual btCollisionShape *create_bt_shape();
+
+private:
+ void setup(const Plane &p_plane);
+};
+
+class SphereShapeBullet : public ShapeBullet {
+
+ real_t radius;
+
+public:
+ SphereShapeBullet();
+
+ _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();
+
+private:
+ void setup(real_t p_radius);
+};
+
+class BoxShapeBullet : public ShapeBullet {
+
+ btVector3 half_extents;
+
+public:
+ BoxShapeBullet();
+
+ _FORCE_INLINE_ const btVector3 &get_half_extents() { return half_extents; }
+ virtual void set_data(const Variant &p_data);
+ virtual Variant get_data() const;
+ virtual PhysicsServer::ShapeType get_type() const;
+ virtual btCollisionShape *create_bt_shape();
+
+private:
+ void setup(const Vector3 &p_half_extents);
+};
+
+class CapsuleShapeBullet : public ShapeBullet {
+
+ real_t height;
+ real_t radius;
+
+public:
+ CapsuleShapeBullet();
+
+ _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();
+
+private:
+ void setup(real_t p_height, real_t p_radius);
+};
+
+class ConvexPolygonShapeBullet : public ShapeBullet {
+
+public:
+ btAlignedObjectArray<btVector3> vertices;
+
+ ConvexPolygonShapeBullet();
+
+ virtual void set_data(const Variant &p_data);
+ void get_vertices(Vector<Vector3> &out_vertices);
+ virtual Variant get_data() const;
+ virtual PhysicsServer::ShapeType get_type() const;
+ virtual btCollisionShape *create_bt_shape();
+
+private:
+ void setup(const Vector<Vector3> &p_vertices);
+};
+
+class ConcavePolygonShapeBullet : public ShapeBullet {
+ class btBvhTriangleMeshShape *meshShape;
+
+public:
+ PoolVector<Vector3> faces;
+
+ ConcavePolygonShapeBullet();
+ virtual ~ConcavePolygonShapeBullet();
+
+ virtual void set_data(const Variant &p_data);
+ virtual Variant get_data() const;
+ virtual PhysicsServer::ShapeType get_type() const;
+ virtual btCollisionShape *create_bt_shape();
+
+private:
+ void setup(PoolVector<Vector3> p_faces);
+};
+
+class HeightMapShapeBullet : public ShapeBullet {
+
+public:
+ PoolVector<real_t> heights;
+ int width;
+ int depth;
+ real_t cell_size;
+
+ HeightMapShapeBullet();
+
+ virtual void set_data(const Variant &p_data);
+ virtual Variant get_data() const;
+ virtual PhysicsServer::ShapeType get_type() const;
+ virtual btCollisionShape *create_bt_shape();
+
+private:
+ void setup(PoolVector<real_t> &p_heights, int p_width, int p_depth, real_t p_cell_size);
+};
+
+class RayShapeBullet : public ShapeBullet {
+
+public:
+ real_t length;
+
+ RayShapeBullet();
+
+ virtual void set_data(const Variant &p_data);
+ virtual Variant get_data() const;
+ virtual PhysicsServer::ShapeType get_type() const;
+ virtual btCollisionShape *create_bt_shape();
+
+private:
+ void setup(real_t p_length);
+};
+#endif
diff --git a/modules/bullet/shape_owner_bullet.cpp b/modules/bullet/shape_owner_bullet.cpp
new file mode 100644
index 0000000000..04b2b01675
--- /dev/null
+++ b/modules/bullet/shape_owner_bullet.cpp
@@ -0,0 +1,32 @@
+/*************************************************************************/
+/* shape_owner_bullet.cpp */
+/* Author: AndreaCatania */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2017 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 "shape_owner_bullet.h"
diff --git a/modules/bullet/shape_owner_bullet.h b/modules/bullet/shape_owner_bullet.h
new file mode 100644
index 0000000000..d2f3d321c7
--- /dev/null
+++ b/modules/bullet/shape_owner_bullet.h
@@ -0,0 +1,52 @@
+/*************************************************************************/
+/* shape_owner_bullet.h */
+/* Author: AndreaCatania */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2017 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 SHAPE_OWNER_BULLET_H
+#define SHAPE_OWNER_BULLET_H
+
+#include "rid_bullet.h"
+
+class ShapeBullet;
+class btCollisionShape;
+class CollisionObjectBullet;
+
+/// Each clas that want to use Shapes must inherit this class
+/// E.G. BodyShape is a child of this
+class ShapeOwnerBullet {
+public:
+ /// This is used to set new shape or replace existing
+ //virtual void _internal_replaceShape(btCollisionShape *p_old_shape, btCollisionShape *p_new_shape) = 0;
+ virtual void on_shape_changed(const ShapeBullet *const p_shape) = 0;
+ virtual void on_shapes_changed() = 0;
+ virtual void remove_shape(class ShapeBullet *p_shape) = 0;
+ virtual ~ShapeOwnerBullet() {}
+};
+#endif
diff --git a/modules/bullet/slider_joint_bullet.cpp b/modules/bullet/slider_joint_bullet.cpp
new file mode 100644
index 0000000000..2da65677f5
--- /dev/null
+++ b/modules/bullet/slider_joint_bullet.cpp
@@ -0,0 +1,385 @@
+/*************************************************************************/
+/* slider_joint_bullet.cpp */
+/* Author: AndreaCatania */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2017 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 "slider_joint_bullet.h"
+#include "BulletDynamics/ConstraintSolver/btSliderConstraint.h"
+#include "bullet_types_converter.h"
+#include "bullet_utilities.h"
+#include "rigid_body_bullet.h"
+
+SliderJointBullet::SliderJointBullet(RigidBodyBullet *rbA, RigidBodyBullet *rbB, const Transform &frameInA, const Transform &frameInB)
+ : JointBullet() {
+ btTransform btFrameA;
+ G_TO_B(frameInA, btFrameA);
+ if (rbB) {
+ btTransform btFrameB;
+ G_TO_B(frameInB, btFrameB);
+ sliderConstraint = bulletnew(btSliderConstraint(*rbA->get_bt_rigid_body(), *rbB->get_bt_rigid_body(), btFrameA, btFrameB, true));
+
+ } else {
+ sliderConstraint = bulletnew(btSliderConstraint(*rbA->get_bt_rigid_body(), btFrameA, true));
+ }
+ setup(sliderConstraint);
+}
+
+const RigidBodyBullet *SliderJointBullet::getRigidBodyA() const {
+ return static_cast<RigidBodyBullet *>(sliderConstraint->getRigidBodyA().getUserPointer());
+}
+
+const RigidBodyBullet *SliderJointBullet::getRigidBodyB() const {
+ return static_cast<RigidBodyBullet *>(sliderConstraint->getRigidBodyB().getUserPointer());
+}
+
+const Transform SliderJointBullet::getCalculatedTransformA() const {
+ btTransform btTransform = sliderConstraint->getCalculatedTransformA();
+ Transform gTrans;
+ B_TO_G(btTransform, gTrans);
+ return gTrans;
+}
+
+const Transform SliderJointBullet::getCalculatedTransformB() const {
+ btTransform btTransform = sliderConstraint->getCalculatedTransformB();
+ Transform gTrans;
+ B_TO_G(btTransform, gTrans);
+ return gTrans;
+}
+
+const Transform SliderJointBullet::getFrameOffsetA() const {
+ btTransform btTransform = sliderConstraint->getFrameOffsetA();
+ Transform gTrans;
+ B_TO_G(btTransform, gTrans);
+ return gTrans;
+}
+
+const Transform SliderJointBullet::getFrameOffsetB() const {
+ btTransform btTransform = sliderConstraint->getFrameOffsetB();
+ Transform gTrans;
+ B_TO_G(btTransform, gTrans);
+ return gTrans;
+}
+
+Transform SliderJointBullet::getFrameOffsetA() {
+ btTransform btTransform = sliderConstraint->getFrameOffsetA();
+ Transform gTrans;
+ B_TO_G(btTransform, gTrans);
+ return gTrans;
+}
+
+Transform SliderJointBullet::getFrameOffsetB() {
+ btTransform btTransform = sliderConstraint->getFrameOffsetB();
+ Transform gTrans;
+ B_TO_G(btTransform, gTrans);
+ return gTrans;
+}
+
+real_t SliderJointBullet::getLowerLinLimit() const {
+ return sliderConstraint->getLowerLinLimit();
+}
+
+void SliderJointBullet::setLowerLinLimit(real_t lowerLimit) {
+ sliderConstraint->setLowerLinLimit(lowerLimit);
+}
+real_t SliderJointBullet::getUpperLinLimit() const {
+ return sliderConstraint->getUpperLinLimit();
+}
+
+void SliderJointBullet::setUpperLinLimit(real_t upperLimit) {
+ sliderConstraint->setUpperLinLimit(upperLimit);
+}
+
+real_t SliderJointBullet::getLowerAngLimit() const {
+ return sliderConstraint->getLowerAngLimit();
+}
+
+void SliderJointBullet::setLowerAngLimit(real_t lowerLimit) {
+ sliderConstraint->setLowerAngLimit(lowerLimit);
+}
+
+real_t SliderJointBullet::getUpperAngLimit() const {
+ return sliderConstraint->getUpperAngLimit();
+}
+
+void SliderJointBullet::setUpperAngLimit(real_t upperLimit) {
+ sliderConstraint->setUpperAngLimit(upperLimit);
+}
+
+real_t SliderJointBullet::getSoftnessDirLin() const {
+ return sliderConstraint->getSoftnessDirLin();
+}
+
+real_t SliderJointBullet::getRestitutionDirLin() const {
+ return sliderConstraint->getRestitutionDirLin();
+}
+
+real_t SliderJointBullet::getDampingDirLin() const {
+ return sliderConstraint->getDampingDirLin();
+}
+
+real_t SliderJointBullet::getSoftnessDirAng() const {
+ return sliderConstraint->getSoftnessDirAng();
+}
+
+real_t SliderJointBullet::getRestitutionDirAng() const {
+ return sliderConstraint->getRestitutionDirAng();
+}
+
+real_t SliderJointBullet::getDampingDirAng() const {
+ return sliderConstraint->getDampingDirAng();
+}
+
+real_t SliderJointBullet::getSoftnessLimLin() const {
+ return sliderConstraint->getSoftnessLimLin();
+}
+
+real_t SliderJointBullet::getRestitutionLimLin() const {
+ return sliderConstraint->getRestitutionLimLin();
+}
+
+real_t SliderJointBullet::getDampingLimLin() const {
+ return sliderConstraint->getDampingLimLin();
+}
+
+real_t SliderJointBullet::getSoftnessLimAng() const {
+ return sliderConstraint->getSoftnessLimAng();
+}
+
+real_t SliderJointBullet::getRestitutionLimAng() const {
+ return sliderConstraint->getRestitutionLimAng();
+}
+
+real_t SliderJointBullet::getDampingLimAng() const {
+ return sliderConstraint->getDampingLimAng();
+}
+
+real_t SliderJointBullet::getSoftnessOrthoLin() const {
+ return sliderConstraint->getSoftnessOrthoLin();
+}
+
+real_t SliderJointBullet::getRestitutionOrthoLin() const {
+ return sliderConstraint->getRestitutionOrthoLin();
+}
+
+real_t SliderJointBullet::getDampingOrthoLin() const {
+ return sliderConstraint->getDampingOrthoLin();
+}
+
+real_t SliderJointBullet::getSoftnessOrthoAng() const {
+ return sliderConstraint->getSoftnessOrthoAng();
+}
+
+real_t SliderJointBullet::getRestitutionOrthoAng() const {
+ return sliderConstraint->getRestitutionOrthoAng();
+}
+
+real_t SliderJointBullet::getDampingOrthoAng() const {
+ return sliderConstraint->getDampingOrthoAng();
+}
+
+void SliderJointBullet::setSoftnessDirLin(real_t softnessDirLin) {
+ sliderConstraint->setSoftnessDirLin(softnessDirLin);
+}
+
+void SliderJointBullet::setRestitutionDirLin(real_t restitutionDirLin) {
+ sliderConstraint->setRestitutionDirLin(restitutionDirLin);
+}
+
+void SliderJointBullet::setDampingDirLin(real_t dampingDirLin) {
+ sliderConstraint->setDampingDirLin(dampingDirLin);
+}
+
+void SliderJointBullet::setSoftnessDirAng(real_t softnessDirAng) {
+ sliderConstraint->setSoftnessDirAng(softnessDirAng);
+}
+
+void SliderJointBullet::setRestitutionDirAng(real_t restitutionDirAng) {
+ sliderConstraint->setRestitutionDirAng(restitutionDirAng);
+}
+
+void SliderJointBullet::setDampingDirAng(real_t dampingDirAng) {
+ sliderConstraint->setDampingDirAng(dampingDirAng);
+}
+
+void SliderJointBullet::setSoftnessLimLin(real_t softnessLimLin) {
+ sliderConstraint->setSoftnessLimLin(softnessLimLin);
+}
+
+void SliderJointBullet::setRestitutionLimLin(real_t restitutionLimLin) {
+ sliderConstraint->setRestitutionLimLin(restitutionLimLin);
+}
+
+void SliderJointBullet::setDampingLimLin(real_t dampingLimLin) {
+ sliderConstraint->setDampingLimLin(dampingLimLin);
+}
+
+void SliderJointBullet::setSoftnessLimAng(real_t softnessLimAng) {
+ sliderConstraint->setSoftnessLimAng(softnessLimAng);
+}
+
+void SliderJointBullet::setRestitutionLimAng(real_t restitutionLimAng) {
+ sliderConstraint->setRestitutionLimAng(restitutionLimAng);
+}
+
+void SliderJointBullet::setDampingLimAng(real_t dampingLimAng) {
+ sliderConstraint->setDampingLimAng(dampingLimAng);
+}
+
+void SliderJointBullet::setSoftnessOrthoLin(real_t softnessOrthoLin) {
+ sliderConstraint->setSoftnessOrthoLin(softnessOrthoLin);
+}
+
+void SliderJointBullet::setRestitutionOrthoLin(real_t restitutionOrthoLin) {
+ sliderConstraint->setRestitutionOrthoLin(restitutionOrthoLin);
+}
+
+void SliderJointBullet::setDampingOrthoLin(real_t dampingOrthoLin) {
+ sliderConstraint->setDampingOrthoLin(dampingOrthoLin);
+}
+
+void SliderJointBullet::setSoftnessOrthoAng(real_t softnessOrthoAng) {
+ sliderConstraint->setSoftnessOrthoAng(softnessOrthoAng);
+}
+
+void SliderJointBullet::setRestitutionOrthoAng(real_t restitutionOrthoAng) {
+ sliderConstraint->setRestitutionOrthoAng(restitutionOrthoAng);
+}
+
+void SliderJointBullet::setDampingOrthoAng(real_t dampingOrthoAng) {
+ sliderConstraint->setDampingOrthoAng(dampingOrthoAng);
+}
+
+void SliderJointBullet::setPoweredLinMotor(bool onOff) {
+ sliderConstraint->setPoweredLinMotor(onOff);
+}
+
+bool SliderJointBullet::getPoweredLinMotor() {
+ return sliderConstraint->getPoweredLinMotor();
+}
+
+void SliderJointBullet::setTargetLinMotorVelocity(real_t targetLinMotorVelocity) {
+ sliderConstraint->setTargetLinMotorVelocity(targetLinMotorVelocity);
+}
+
+real_t SliderJointBullet::getTargetLinMotorVelocity() {
+ return sliderConstraint->getTargetLinMotorVelocity();
+}
+
+void SliderJointBullet::setMaxLinMotorForce(real_t maxLinMotorForce) {
+ sliderConstraint->setMaxLinMotorForce(maxLinMotorForce);
+}
+
+real_t SliderJointBullet::getMaxLinMotorForce() {
+ return sliderConstraint->getMaxLinMotorForce();
+}
+
+void SliderJointBullet::setPoweredAngMotor(bool onOff) {
+ sliderConstraint->setPoweredAngMotor(onOff);
+}
+
+bool SliderJointBullet::getPoweredAngMotor() {
+ return sliderConstraint->getPoweredAngMotor();
+}
+
+void SliderJointBullet::setTargetAngMotorVelocity(real_t targetAngMotorVelocity) {
+ sliderConstraint->setTargetAngMotorVelocity(targetAngMotorVelocity);
+}
+
+real_t SliderJointBullet::getTargetAngMotorVelocity() {
+ return sliderConstraint->getTargetAngMotorVelocity();
+}
+
+void SliderJointBullet::setMaxAngMotorForce(real_t maxAngMotorForce) {
+ sliderConstraint->setMaxAngMotorForce(maxAngMotorForce);
+}
+
+real_t SliderJointBullet::getMaxAngMotorForce() {
+ return sliderConstraint->getMaxAngMotorForce();
+}
+
+real_t SliderJointBullet::getLinearPos() {
+ return sliderConstraint->getLinearPos();
+ ;
+}
+
+void SliderJointBullet::set_param(PhysicsServer::SliderJointParam p_param, real_t p_value) {
+ switch (p_param) {
+ case PhysicsServer::SLIDER_JOINT_LINEAR_LIMIT_UPPER: setUpperLinLimit(p_value); break;
+ case PhysicsServer::SLIDER_JOINT_LINEAR_LIMIT_LOWER: setLowerLinLimit(p_value); break;
+ case PhysicsServer::SLIDER_JOINT_LINEAR_LIMIT_SOFTNESS: setSoftnessLimLin(p_value); break;
+ case PhysicsServer::SLIDER_JOINT_LINEAR_LIMIT_RESTITUTION: setRestitutionLimLin(p_value); break;
+ case PhysicsServer::SLIDER_JOINT_LINEAR_LIMIT_DAMPING: setDampingLimLin(p_value); break;
+ case PhysicsServer::SLIDER_JOINT_LINEAR_MOTION_SOFTNESS: setSoftnessDirLin(p_value); break;
+ case PhysicsServer::SLIDER_JOINT_LINEAR_MOTION_RESTITUTION: setRestitutionDirLin(p_value); break;
+ case PhysicsServer::SLIDER_JOINT_LINEAR_MOTION_DAMPING: setDampingDirLin(p_value); break;
+ case PhysicsServer::SLIDER_JOINT_LINEAR_ORTHOGONAL_SOFTNESS: setSoftnessOrthoLin(p_value); break;
+ case PhysicsServer::SLIDER_JOINT_LINEAR_ORTHOGONAL_RESTITUTION: setRestitutionOrthoLin(p_value); break;
+ case PhysicsServer::SLIDER_JOINT_LINEAR_ORTHOGONAL_DAMPING: setDampingOrthoLin(p_value); break;
+ case PhysicsServer::SLIDER_JOINT_ANGULAR_LIMIT_UPPER: setUpperAngLimit(p_value); break;
+ case PhysicsServer::SLIDER_JOINT_ANGULAR_LIMIT_LOWER: setLowerAngLimit(p_value); break;
+ case PhysicsServer::SLIDER_JOINT_ANGULAR_LIMIT_SOFTNESS: setSoftnessLimAng(p_value); break;
+ case PhysicsServer::SLIDER_JOINT_ANGULAR_LIMIT_RESTITUTION: setRestitutionLimAng(p_value); break;
+ case PhysicsServer::SLIDER_JOINT_ANGULAR_LIMIT_DAMPING: setDampingLimAng(p_value); break;
+ case PhysicsServer::SLIDER_JOINT_ANGULAR_MOTION_SOFTNESS: setSoftnessDirAng(p_value); break;
+ case PhysicsServer::SLIDER_JOINT_ANGULAR_MOTION_RESTITUTION: setRestitutionDirAng(p_value); break;
+ case PhysicsServer::SLIDER_JOINT_ANGULAR_MOTION_DAMPING: setDampingDirAng(p_value); break;
+ case PhysicsServer::SLIDER_JOINT_ANGULAR_ORTHOGONAL_SOFTNESS: setSoftnessOrthoAng(p_value); break;
+ case PhysicsServer::SLIDER_JOINT_ANGULAR_ORTHOGONAL_RESTITUTION: setRestitutionOrthoAng(p_value); break;
+ case PhysicsServer::SLIDER_JOINT_ANGULAR_ORTHOGONAL_DAMPING: setDampingOrthoAng(p_value); break;
+ }
+}
+
+real_t SliderJointBullet::get_param(PhysicsServer::SliderJointParam p_param) const {
+ switch (p_param) {
+ case PhysicsServer::SLIDER_JOINT_LINEAR_LIMIT_UPPER: return getUpperLinLimit();
+ case PhysicsServer::SLIDER_JOINT_LINEAR_LIMIT_LOWER: return getLowerLinLimit();
+ case PhysicsServer::SLIDER_JOINT_LINEAR_LIMIT_SOFTNESS: return getSoftnessLimLin();
+ case PhysicsServer::SLIDER_JOINT_LINEAR_LIMIT_RESTITUTION: return getRestitutionLimLin();
+ case PhysicsServer::SLIDER_JOINT_LINEAR_LIMIT_DAMPING: return getDampingLimLin();
+ case PhysicsServer::SLIDER_JOINT_LINEAR_MOTION_SOFTNESS: return getSoftnessDirLin();
+ case PhysicsServer::SLIDER_JOINT_LINEAR_MOTION_RESTITUTION: return getRestitutionDirLin();
+ case PhysicsServer::SLIDER_JOINT_LINEAR_MOTION_DAMPING: return getDampingDirLin();
+ case PhysicsServer::SLIDER_JOINT_LINEAR_ORTHOGONAL_SOFTNESS: return getSoftnessOrthoLin();
+ case PhysicsServer::SLIDER_JOINT_LINEAR_ORTHOGONAL_RESTITUTION: return getRestitutionOrthoLin();
+ case PhysicsServer::SLIDER_JOINT_LINEAR_ORTHOGONAL_DAMPING: return getDampingOrthoLin();
+ case PhysicsServer::SLIDER_JOINT_ANGULAR_LIMIT_UPPER: return getUpperAngLimit();
+ case PhysicsServer::SLIDER_JOINT_ANGULAR_LIMIT_LOWER: return getLowerAngLimit();
+ case PhysicsServer::SLIDER_JOINT_ANGULAR_LIMIT_SOFTNESS: return getSoftnessLimAng();
+ case PhysicsServer::SLIDER_JOINT_ANGULAR_LIMIT_RESTITUTION: return getRestitutionLimAng();
+ case PhysicsServer::SLIDER_JOINT_ANGULAR_LIMIT_DAMPING: return getDampingLimAng();
+ case PhysicsServer::SLIDER_JOINT_ANGULAR_MOTION_SOFTNESS: return getSoftnessDirAng();
+ case PhysicsServer::SLIDER_JOINT_ANGULAR_MOTION_RESTITUTION: return getRestitutionDirAng();
+ case PhysicsServer::SLIDER_JOINT_ANGULAR_MOTION_DAMPING: return getDampingDirAng();
+ case PhysicsServer::SLIDER_JOINT_ANGULAR_ORTHOGONAL_SOFTNESS: return getSoftnessOrthoAng();
+ case PhysicsServer::SLIDER_JOINT_ANGULAR_ORTHOGONAL_RESTITUTION: return getRestitutionOrthoAng();
+ case PhysicsServer::SLIDER_JOINT_ANGULAR_ORTHOGONAL_DAMPING: return getDampingOrthoAng();
+ default:
+ return 0;
+ }
+}
diff --git a/modules/bullet/slider_joint_bullet.h b/modules/bullet/slider_joint_bullet.h
new file mode 100644
index 0000000000..d50c376ea6
--- /dev/null
+++ b/modules/bullet/slider_joint_bullet.h
@@ -0,0 +1,118 @@
+/*************************************************************************/
+/* slider_joint_bullet.h */
+/* Author: AndreaCatania */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2017 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 SLIDER_JOINT_BULLET_H
+#define SLIDER_JOINT_BULLET_H
+
+#include "joint_bullet.h"
+
+class RigidBodyBullet;
+
+class SliderJointBullet : public JointBullet {
+ class btSliderConstraint *sliderConstraint;
+
+public:
+ /// Reference frame is A
+ SliderJointBullet(RigidBodyBullet *rbA, RigidBodyBullet *rbB, const Transform &frameInA, const Transform &frameInB);
+
+ virtual PhysicsServer::JointType get_type() const { return PhysicsServer::JOINT_SLIDER; }
+
+ const RigidBodyBullet *getRigidBodyA() const;
+ const RigidBodyBullet *getRigidBodyB() const;
+ const Transform getCalculatedTransformA() const;
+ const Transform getCalculatedTransformB() const;
+ const Transform getFrameOffsetA() const;
+ const Transform getFrameOffsetB() const;
+ Transform getFrameOffsetA();
+ Transform getFrameOffsetB();
+ real_t getLowerLinLimit() const;
+ void setLowerLinLimit(real_t lowerLimit);
+ real_t getUpperLinLimit() const;
+ void setUpperLinLimit(real_t upperLimit);
+ real_t getLowerAngLimit() const;
+ void setLowerAngLimit(real_t lowerLimit);
+ real_t getUpperAngLimit() const;
+ void setUpperAngLimit(real_t upperLimit);
+
+ real_t getSoftnessDirLin() const;
+ real_t getRestitutionDirLin() const;
+ real_t getDampingDirLin() const;
+ real_t getSoftnessDirAng() const;
+ real_t getRestitutionDirAng() const;
+ real_t getDampingDirAng() const;
+ real_t getSoftnessLimLin() const;
+ real_t getRestitutionLimLin() const;
+ real_t getDampingLimLin() const;
+ real_t getSoftnessLimAng() const;
+ real_t getRestitutionLimAng() const;
+ real_t getDampingLimAng() const;
+ real_t getSoftnessOrthoLin() const;
+ real_t getRestitutionOrthoLin() const;
+ real_t getDampingOrthoLin() const;
+ real_t getSoftnessOrthoAng() const;
+ real_t getRestitutionOrthoAng() const;
+ real_t getDampingOrthoAng() const;
+ void setSoftnessDirLin(real_t softnessDirLin);
+ void setRestitutionDirLin(real_t restitutionDirLin);
+ void setDampingDirLin(real_t dampingDirLin);
+ void setSoftnessDirAng(real_t softnessDirAng);
+ void setRestitutionDirAng(real_t restitutionDirAng);
+ void setDampingDirAng(real_t dampingDirAng);
+ void setSoftnessLimLin(real_t softnessLimLin);
+ void setRestitutionLimLin(real_t restitutionLimLin);
+ void setDampingLimLin(real_t dampingLimLin);
+ void setSoftnessLimAng(real_t softnessLimAng);
+ void setRestitutionLimAng(real_t restitutionLimAng);
+ void setDampingLimAng(real_t dampingLimAng);
+ void setSoftnessOrthoLin(real_t softnessOrthoLin);
+ void setRestitutionOrthoLin(real_t restitutionOrthoLin);
+ void setDampingOrthoLin(real_t dampingOrthoLin);
+ void setSoftnessOrthoAng(real_t softnessOrthoAng);
+ void setRestitutionOrthoAng(real_t restitutionOrthoAng);
+ void setDampingOrthoAng(real_t dampingOrthoAng);
+ void setPoweredLinMotor(bool onOff);
+ bool getPoweredLinMotor();
+ void setTargetLinMotorVelocity(real_t targetLinMotorVelocity);
+ real_t getTargetLinMotorVelocity();
+ void setMaxLinMotorForce(real_t maxLinMotorForce);
+ real_t getMaxLinMotorForce();
+ void setPoweredAngMotor(bool onOff);
+ bool getPoweredAngMotor();
+ void setTargetAngMotorVelocity(real_t targetAngMotorVelocity);
+ real_t getTargetAngMotorVelocity();
+ void setMaxAngMotorForce(real_t maxAngMotorForce);
+ real_t getMaxAngMotorForce();
+ real_t getLinearPos();
+
+ void set_param(PhysicsServer::SliderJointParam p_param, real_t p_value);
+ real_t get_param(PhysicsServer::SliderJointParam p_param) const;
+};
+#endif
diff --git a/modules/bullet/soft_body_bullet.cpp b/modules/bullet/soft_body_bullet.cpp
new file mode 100644
index 0000000000..64ef7bfad2
--- /dev/null
+++ b/modules/bullet/soft_body_bullet.cpp
@@ -0,0 +1,303 @@
+/*************************************************************************/
+/* soft_body_bullet.cpp */
+/* Author: AndreaCatania */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2017 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 "soft_body_bullet.h"
+#include "bullet_types_converter.h"
+#include "bullet_utilities.h"
+#include "space_bullet.h"
+
+#include "scene/3d/immediate_geometry.h"
+
+SoftBodyBullet::SoftBodyBullet()
+ : CollisionObjectBullet(CollisionObjectBullet::TYPE_SOFT_BODY), mass(1), simulation_precision(5), stiffness(0.5f), pressure_coefficient(50), damping_coefficient(0.005), drag_coefficient(0.005), bt_soft_body(NULL), soft_shape_type(SOFT_SHAPETYPE_NONE), isScratched(false), soft_body_shape_data(NULL) {
+
+ test_geometry = memnew(ImmediateGeometry);
+
+ red_mat = Ref<SpatialMaterial>(memnew(SpatialMaterial));
+ red_mat->set_flag(SpatialMaterial::FLAG_UNSHADED, true);
+ red_mat->set_line_width(20.0);
+ red_mat->set_feature(SpatialMaterial::FEATURE_TRANSPARENT, true);
+ red_mat->set_flag(SpatialMaterial::FLAG_ALBEDO_FROM_VERTEX_COLOR, true);
+ red_mat->set_flag(SpatialMaterial::FLAG_SRGB_VERTEX_COLOR, true);
+ red_mat->set_albedo(Color(1, 0, 0, 1));
+ test_geometry->set_material_override(red_mat);
+
+ test_is_in_scene = false;
+}
+
+SoftBodyBullet::~SoftBodyBullet() {
+ bulletdelete(soft_body_shape_data);
+}
+
+void SoftBodyBullet::reload_body() {
+ if (space) {
+ space->remove_soft_body(this);
+ space->add_soft_body(this);
+ }
+}
+
+void SoftBodyBullet::set_space(SpaceBullet *p_space) {
+ if (space) {
+ isScratched = false;
+
+ // Remove this object from the physics world
+ space->remove_soft_body(this);
+ }
+
+ space = p_space;
+
+ if (space) {
+ space->add_soft_body(this);
+ }
+
+ reload_soft_body();
+}
+
+void SoftBodyBullet::dispatch_callbacks() {
+ if (!bt_soft_body) {
+ return;
+ }
+
+ if (!test_is_in_scene) {
+ test_is_in_scene = true;
+ SceneTree::get_singleton()->get_current_scene()->add_child(test_geometry);
+ }
+
+ test_geometry->clear();
+ test_geometry->begin(Mesh::PRIMITIVE_LINES, NULL);
+ bool first = true;
+ Vector3 pos;
+ for (int i = 0; i < bt_soft_body->m_nodes.size(); ++i) {
+ const btSoftBody::Node &n = bt_soft_body->m_nodes[i];
+ B_TO_G(n.m_x, pos);
+ test_geometry->add_vertex(pos);
+ if (!first) {
+ test_geometry->add_vertex(pos);
+ } else {
+ first = false;
+ }
+ }
+ test_geometry->end();
+}
+
+void SoftBodyBullet::on_collision_filters_change() {
+}
+
+void SoftBodyBullet::on_collision_checker_start() {
+}
+
+void SoftBodyBullet::on_enter_area(AreaBullet *p_area) {
+}
+
+void SoftBodyBullet::on_exit_area(AreaBullet *p_area) {
+}
+
+void SoftBodyBullet::set_trimesh_body_shape(PoolVector<int> p_indices, PoolVector<Vector3> p_vertices, int p_triangles_num) {
+
+ TrimeshSoftShapeData *shape_data = bulletnew(TrimeshSoftShapeData);
+ shape_data->m_triangles_indices = p_indices;
+ shape_data->m_vertices = p_vertices;
+ shape_data->m_triangles_num = p_triangles_num;
+
+ set_body_shape_data(shape_data, SOFT_SHAPE_TYPE_TRIMESH);
+ reload_soft_body();
+}
+
+void SoftBodyBullet::set_body_shape_data(SoftShapeData *p_soft_shape_data, SoftShapeType p_type) {
+ bulletdelete(soft_body_shape_data);
+ soft_body_shape_data = p_soft_shape_data;
+ soft_shape_type = p_type;
+}
+
+void SoftBodyBullet::set_transform(const Transform &p_transform) {
+ transform = p_transform;
+ if (bt_soft_body) {
+ // TODO the softbody set new transform considering the current transform as center of world
+ // like if it's local transform, so I must fix this by setting nwe transform considering the old
+ btTransform bt_trans;
+ G_TO_B(transform, bt_trans);
+ //bt_soft_body->transform(bt_trans);
+ }
+}
+
+const Transform &SoftBodyBullet::get_transform() const {
+ return transform;
+}
+
+void SoftBodyBullet::get_first_node_origin(btVector3 &p_out_origin) const {
+ if (bt_soft_body && bt_soft_body->m_nodes.size()) {
+ p_out_origin = bt_soft_body->m_nodes[0].m_x;
+ } else {
+ p_out_origin.setZero();
+ }
+}
+
+void SoftBodyBullet::set_activation_state(bool p_active) {
+ if (p_active) {
+ bt_soft_body->setActivationState(ACTIVE_TAG);
+ } else {
+ bt_soft_body->setActivationState(WANTS_DEACTIVATION);
+ }
+}
+
+void SoftBodyBullet::set_mass(real_t p_val) {
+ if (0 >= p_val) {
+ p_val = 1;
+ }
+ mass = p_val;
+ if (bt_soft_body) {
+ bt_soft_body->setTotalMass(mass);
+ }
+}
+
+void SoftBodyBullet::set_stiffness(real_t p_val) {
+ stiffness = p_val;
+ if (bt_soft_body) {
+ mat0->m_kAST = stiffness;
+ mat0->m_kLST = stiffness;
+ mat0->m_kVST = stiffness;
+ }
+}
+
+void SoftBodyBullet::set_simulation_precision(int p_val) {
+ simulation_precision = p_val;
+ if (bt_soft_body) {
+ bt_soft_body->m_cfg.piterations = simulation_precision;
+ }
+}
+
+void SoftBodyBullet::set_pressure_coefficient(real_t p_val) {
+ pressure_coefficient = p_val;
+ if (bt_soft_body) {
+ bt_soft_body->m_cfg.kPR = pressure_coefficient;
+ }
+}
+
+void SoftBodyBullet::set_damping_coefficient(real_t p_val) {
+ damping_coefficient = p_val;
+ if (bt_soft_body) {
+ bt_soft_body->m_cfg.kDP = damping_coefficient;
+ }
+}
+
+void SoftBodyBullet::set_drag_coefficient(real_t p_val) {
+ drag_coefficient = p_val;
+ if (bt_soft_body) {
+ bt_soft_body->m_cfg.kDG = drag_coefficient;
+ }
+}
+
+void SoftBodyBullet::reload_soft_body() {
+
+ destroy_soft_body();
+ create_soft_body();
+
+ if (bt_soft_body) {
+
+ // TODO the softbody set new transform considering the current transform as center of world
+ // like if it's local transform, so I must fix this by setting nwe transform considering the old
+ btTransform bt_trans;
+ G_TO_B(transform, bt_trans);
+ bt_soft_body->transform(bt_trans);
+
+ bt_soft_body->generateBendingConstraints(2, mat0);
+ mat0->m_kAST = stiffness;
+ mat0->m_kLST = stiffness;
+ mat0->m_kVST = stiffness;
+
+ bt_soft_body->m_cfg.piterations = simulation_precision;
+ bt_soft_body->m_cfg.kDP = damping_coefficient;
+ bt_soft_body->m_cfg.kDG = drag_coefficient;
+ bt_soft_body->m_cfg.kPR = pressure_coefficient;
+ bt_soft_body->setTotalMass(mass);
+ }
+ if (space) {
+ // TODO remove this please
+ space->add_soft_body(this);
+ }
+}
+
+void SoftBodyBullet::create_soft_body() {
+ if (!space || !soft_body_shape_data) {
+ return;
+ }
+ ERR_FAIL_COND(!space->is_using_soft_world());
+ switch (soft_shape_type) {
+ case SOFT_SHAPE_TYPE_TRIMESH: {
+ TrimeshSoftShapeData *trimesh_data = static_cast<TrimeshSoftShapeData *>(soft_body_shape_data);
+
+ Vector<int> indices;
+ Vector<btScalar> vertices;
+
+ int i;
+ const int indices_size = trimesh_data->m_triangles_indices.size();
+ const int vertices_size = trimesh_data->m_vertices.size();
+ indices.resize(indices_size);
+ vertices.resize(vertices_size * 3);
+
+ PoolVector<int>::Read i_r = trimesh_data->m_triangles_indices.read();
+ for (i = 0; i < indices_size; ++i) {
+ indices[i] = i_r[i];
+ }
+ i_r = PoolVector<int>::Read();
+
+ PoolVector<Vector3>::Read f_r = trimesh_data->m_vertices.read();
+ for (int j = i = 0; i < vertices_size; ++i, j += 3) {
+ vertices[j + 0] = f_r[i][0];
+ vertices[j + 1] = f_r[i][1];
+ vertices[j + 2] = f_r[i][2];
+ }
+ f_r = PoolVector<Vector3>::Read();
+
+ bt_soft_body = btSoftBodyHelpers::CreateFromTriMesh(*space->get_soft_body_world_info(), vertices.ptr(), indices.ptr(), trimesh_data->m_triangles_num);
+ } break;
+ default:
+ ERR_PRINT("Shape type not supported");
+ return;
+ }
+
+ setupBulletCollisionObject(bt_soft_body);
+ bt_soft_body->getCollisionShape()->setMargin(0.001f);
+ bt_soft_body->setCollisionFlags(bt_soft_body->getCollisionFlags() & (~(btCollisionObject::CF_KINEMATIC_OBJECT | btCollisionObject::CF_STATIC_OBJECT)));
+ mat0 = bt_soft_body->appendMaterial();
+}
+
+void SoftBodyBullet::destroy_soft_body() {
+ if (space) {
+ /// This step is required to assert that the body is not into the world during deletion
+ /// This step is required since to change the body shape the body must be re-created.
+ /// Here is handled the case when the body is assigned into a world and the body
+ /// shape is changed.
+ space->remove_soft_body(this);
+ }
+ destroyBulletCollisionObject();
+ bt_soft_body = NULL;
+}
diff --git a/modules/bullet/soft_body_bullet.h b/modules/bullet/soft_body_bullet.h
new file mode 100644
index 0000000000..9ee7cd76d3
--- /dev/null
+++ b/modules/bullet/soft_body_bullet.h
@@ -0,0 +1,136 @@
+/*************************************************************************/
+/* soft_body_bullet.h */
+/* Author: AndreaCatania */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2017 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 SOFT_BODY_BULLET_H
+#define SOFT_BODY_BULLET_H
+
+#ifdef None
+/// This is required to remove the macro None defined by x11 compiler because this word "None" is used internally by Bullet
+#undef None
+#define x11_None 0L
+#endif
+
+#include "BulletSoftBody/btSoftBodyHelpers.h"
+#include "collision_object_bullet.h"
+
+#ifdef x11_None
+/// This is required to re add the macro None defined by x11 compiler
+#undef x11_None
+#define None 0L
+#endif
+
+#include "scene/resources/material.h" // TODO remove thsi please
+
+struct SoftShapeData {};
+struct TrimeshSoftShapeData : public SoftShapeData {
+ PoolVector<int> m_triangles_indices;
+ PoolVector<Vector3> m_vertices;
+ int m_triangles_num;
+};
+
+class SoftBodyBullet : public CollisionObjectBullet {
+public:
+ enum SoftShapeType {
+ SOFT_SHAPETYPE_NONE = 0,
+ SOFT_SHAPE_TYPE_TRIMESH
+ };
+
+private:
+ btSoftBody *bt_soft_body;
+ btSoftBody::Material *mat0; // This is just a copy of pointer managed by btSoftBody
+ SoftShapeType soft_shape_type;
+ bool isScratched;
+
+ SoftShapeData *soft_body_shape_data;
+
+ Transform transform;
+ int simulation_precision;
+ real_t mass;
+ real_t stiffness; // [0,1]
+ real_t pressure_coefficient; // [-inf,+inf]
+ real_t damping_coefficient; // [0,1]
+ real_t drag_coefficient; // [0,1]
+
+ class ImmediateGeometry *test_geometry; // TODO remove this please
+ Ref<SpatialMaterial> red_mat; // TODO remove this please
+ bool test_is_in_scene; // TODO remove this please
+
+public:
+ SoftBodyBullet();
+ ~SoftBodyBullet();
+
+ virtual void reload_body();
+ virtual void set_space(SpaceBullet *p_space);
+
+ virtual void dispatch_callbacks();
+ virtual void on_collision_filters_change();
+ virtual void on_collision_checker_start();
+ virtual void on_enter_area(AreaBullet *p_area);
+ virtual void on_exit_area(AreaBullet *p_area);
+
+ _FORCE_INLINE_ btSoftBody *get_bt_soft_body() const { return bt_soft_body; }
+
+ void set_trimesh_body_shape(PoolVector<int> p_indices, PoolVector<Vector3> p_vertices, int p_triangles_num);
+ void set_body_shape_data(SoftShapeData *p_soft_shape_data, SoftShapeType p_type);
+
+ void set_transform(const Transform &p_transform);
+ /// This function doesn't return the exact COM transform.
+ /// It returns the origin only of first node (vertice) of current soft body
+ /// ---
+ /// The soft body doesn't have a fixed center of mass, but is a group of nodes (vertices)
+ /// that each has its own position in the world.
+ /// For this reason return the correct COM is not so simple and must be calculate
+ /// Check this to improve this function http://bulletphysics.org/Bullet/phpBB3/viewtopic.php?t=8803
+ const Transform &get_transform() const;
+ void get_first_node_origin(btVector3 &p_out_origin) const;
+
+ void set_activation_state(bool p_active);
+
+ void set_mass(real_t p_val);
+ _FORCE_INLINE_ real_t get_mass() const { return mass; }
+ void set_stiffness(real_t p_val);
+ _FORCE_INLINE_ real_t get_stiffness() const { return stiffness; }
+ void set_simulation_precision(int p_val);
+ _FORCE_INLINE_ int get_simulation_precision() const { return simulation_precision; }
+ void set_pressure_coefficient(real_t p_val);
+ _FORCE_INLINE_ real_t get_pressure_coefficient() const { return pressure_coefficient; }
+ void set_damping_coefficient(real_t p_val);
+ _FORCE_INLINE_ real_t get_damping_coefficient() const { return damping_coefficient; }
+ void set_drag_coefficient(real_t p_val);
+ _FORCE_INLINE_ real_t get_drag_coefficient() const { return drag_coefficient; }
+
+private:
+ void reload_soft_body();
+ void create_soft_body();
+ void destroy_soft_body();
+};
+
+#endif // SOFT_BODY_BULLET_H
diff --git a/modules/bullet/space_bullet.cpp b/modules/bullet/space_bullet.cpp
new file mode 100644
index 0000000000..af7f511fab
--- /dev/null
+++ b/modules/bullet/space_bullet.cpp
@@ -0,0 +1,1163 @@
+/*************************************************************************/
+/* space_bullet.cpp */
+/* Author: AndreaCatania */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2017 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 "space_bullet.h"
+#include "BulletCollision/CollisionDispatch/btCollisionObject.h"
+#include "BulletCollision/CollisionDispatch/btGhostObject.h"
+#include "BulletCollision/NarrowPhaseCollision/btGjkEpaPenetrationDepthSolver.h"
+#include "BulletCollision/NarrowPhaseCollision/btGjkPairDetector.h"
+#include "BulletCollision/NarrowPhaseCollision/btPointCollector.h"
+#include "BulletSoftBody/btSoftBodyRigidBodyCollisionConfiguration.h"
+#include "BulletSoftBody/btSoftRigidDynamicsWorld.h"
+#include "btBulletDynamicsCommon.h"
+#include "bullet_physics_server.h"
+#include "bullet_types_converter.h"
+#include "bullet_utilities.h"
+#include "constraint_bullet.h"
+#include "godot_collision_configuration.h"
+#include "godot_collision_dispatcher.h"
+#include "rigid_body_bullet.h"
+#include "servers/physics_server.h"
+#include "soft_body_bullet.h"
+#include "ustring.h"
+#include <assert.h>
+
+// test only
+//#include "scene/3d/immediate_geometry.h"
+
+BulletPhysicsDirectSpaceState::BulletPhysicsDirectSpaceState(SpaceBullet *p_space)
+ : PhysicsDirectSpaceState(), space(p_space) {}
+
+int BulletPhysicsDirectSpaceState::intersect_point(const Vector3 &p_point, ShapeResult *r_results, int p_result_max, const Set<RID> &p_exclude, uint32_t p_collision_layer, uint32_t p_object_type_mask) {
+
+ if (p_result_max <= 0)
+ return 0;
+
+ btVector3 bt_point;
+ G_TO_B(p_point, bt_point);
+
+ btSphereShape sphere_point(0.f);
+ btCollisionObject collision_object_point;
+ collision_object_point.setCollisionShape(&sphere_point);
+ collision_object_point.setWorldTransform(btTransform(btQuaternion::getIdentity(), bt_point));
+
+ // Setup query
+ GodotAllContactResultCallback btResult(&collision_object_point, r_results, p_result_max, &p_exclude);
+ btResult.m_collisionFilterGroup = p_collision_layer;
+ btResult.m_collisionFilterMask = p_object_type_mask;
+ space->dynamicsWorld->contactTest(&collision_object_point, btResult);
+
+ // The results is already populated by GodotAllConvexResultCallback
+ return btResult.m_count;
+}
+
+bool BulletPhysicsDirectSpaceState::intersect_ray(const Vector3 &p_from, const Vector3 &p_to, RayResult &r_result, const Set<RID> &p_exclude, uint32_t p_collision_layer, uint32_t p_object_type_mask, bool p_pick_ray) {
+
+ btVector3 btVec_from;
+ btVector3 btVec_to;
+
+ G_TO_B(p_from, btVec_from);
+ G_TO_B(p_to, btVec_to);
+
+ // setup query
+ GodotClosestRayResultCallback btResult(btVec_from, btVec_to, &p_exclude);
+ btResult.m_collisionFilterGroup = p_collision_layer;
+ btResult.m_collisionFilterMask = p_object_type_mask;
+ btResult.m_pickRay = p_pick_ray;
+
+ space->dynamicsWorld->rayTest(btVec_from, btVec_to, btResult);
+ if (btResult.hasHit()) {
+ B_TO_G(btResult.m_hitPointWorld, r_result.position);
+ B_TO_G(btResult.m_hitNormalWorld.normalize(), r_result.normal);
+ CollisionObjectBullet *gObj = static_cast<CollisionObjectBullet *>(btResult.m_collisionObject->getUserPointer());
+ if (gObj) {
+ r_result.shape = 0;
+ r_result.rid = gObj->get_self();
+ r_result.collider_id = gObj->get_instance_id();
+ r_result.collider = 0 == r_result.collider_id ? NULL : ObjectDB::get_instance(r_result.collider_id);
+ } else {
+ WARN_PRINTS("The raycast performed has hit a collision object that is not part of Godot scene, please check it.");
+ }
+ return true;
+ } else {
+ return false;
+ }
+}
+
+int BulletPhysicsDirectSpaceState::intersect_shape(const RID &p_shape, const Transform &p_xform, float p_margin, ShapeResult *p_results, int p_result_max, const Set<RID> &p_exclude, uint32_t p_collision_layer, uint32_t p_object_type_mask) {
+ if (p_result_max <= 0)
+ return 0;
+
+ ShapeBullet *shape = space->get_physics_server()->get_shape_owner()->get(p_shape);
+
+ btConvexShape *btConvex = dynamic_cast<btConvexShape *>(shape->create_bt_shape());
+ if (!btConvex) {
+ bulletdelete(btConvex);
+ ERR_PRINTS("The shape is not a convex shape, then is not supported: shape type: " + itos(shape->get_type()));
+ return 0;
+ }
+
+ btVector3 scale_with_margin;
+ G_TO_B(p_xform.basis.get_scale(), scale_with_margin);
+ btConvex->setLocalScaling(scale_with_margin);
+
+ btTransform bt_xform;
+ G_TO_B(p_xform, bt_xform);
+
+ btCollisionObject collision_object;
+ collision_object.setCollisionShape(btConvex);
+ collision_object.setWorldTransform(bt_xform);
+
+ GodotAllContactResultCallback btQuery(&collision_object, p_results, p_result_max, &p_exclude);
+ btQuery.m_collisionFilterGroup = p_collision_layer;
+ btQuery.m_collisionFilterMask = p_object_type_mask;
+ btQuery.m_closestDistanceThreshold = p_margin;
+ space->dynamicsWorld->contactTest(&collision_object, btQuery);
+
+ bulletdelete(btConvex);
+
+ return btQuery.m_count;
+}
+
+bool BulletPhysicsDirectSpaceState::cast_motion(const RID &p_shape, const Transform &p_xform, const Vector3 &p_motion, float p_margin, float &p_closest_safe, float &p_closest_unsafe, const Set<RID> &p_exclude, uint32_t p_collision_layer, uint32_t p_object_type_mask, ShapeRestInfo *r_info) {
+ ShapeBullet *shape = space->get_physics_server()->get_shape_owner()->get(p_shape);
+
+ btConvexShape *bt_convex_shape = dynamic_cast<btConvexShape *>(shape->create_bt_shape());
+ if (!bt_convex_shape) {
+ bulletdelete(bt_convex_shape);
+ ERR_PRINTS("The shape is not a convex shape, then is not supported: shape type: " + itos(shape->get_type()));
+ return 0;
+ }
+
+ btVector3 bt_motion;
+ G_TO_B(p_motion, bt_motion);
+
+ btVector3 scale_with_margin;
+ G_TO_B(p_xform.basis.get_scale() + Vector3(p_margin, p_margin, p_margin), scale_with_margin);
+ bt_convex_shape->setLocalScaling(scale_with_margin);
+
+ btTransform bt_xform_from;
+ G_TO_B(p_xform, bt_xform_from);
+
+ btTransform bt_xform_to(bt_xform_from);
+ bt_xform_to.getOrigin() += bt_motion;
+
+ GodotClosestConvexResultCallback btResult(bt_xform_from.getOrigin(), bt_xform_to.getOrigin(), &p_exclude);
+ btResult.m_collisionFilterGroup = p_collision_layer;
+ btResult.m_collisionFilterMask = p_object_type_mask;
+
+ space->dynamicsWorld->convexSweepTest(bt_convex_shape, bt_xform_from, bt_xform_to, btResult);
+
+ if (btResult.hasHit()) {
+ if (btCollisionObject::CO_RIGID_BODY == btResult.m_hitCollisionObject->getInternalType()) {
+ B_TO_G(static_cast<const btRigidBody *>(btResult.m_hitCollisionObject)->getVelocityInLocalPoint(btResult.m_hitPointWorld), r_info->linear_velocity);
+ }
+ CollisionObjectBullet *collision_object = static_cast<CollisionObjectBullet *>(btResult.m_hitCollisionObject->getUserPointer());
+ p_closest_safe = p_closest_unsafe = btResult.m_closestHitFraction;
+ B_TO_G(btResult.m_hitPointWorld, r_info->point);
+ B_TO_G(btResult.m_hitNormalWorld, r_info->normal);
+ r_info->rid = collision_object->get_self();
+ r_info->collider_id = collision_object->get_instance_id();
+ r_info->shape = btResult.m_shapePart;
+ }
+
+ bulletdelete(bt_convex_shape);
+ return btResult.hasHit();
+}
+
+/// Returns the list of contacts pairs in this order: Local contact, other body contact
+bool BulletPhysicsDirectSpaceState::collide_shape(RID p_shape, const Transform &p_shape_xform, float p_margin, Vector3 *r_results, int p_result_max, int &r_result_count, const Set<RID> &p_exclude, uint32_t p_collision_layer, uint32_t p_object_type_mask) {
+ if (p_result_max <= 0)
+ return 0;
+
+ ShapeBullet *shape = space->get_physics_server()->get_shape_owner()->get(p_shape);
+
+ btConvexShape *btConvex = dynamic_cast<btConvexShape *>(shape->create_bt_shape());
+ if (!btConvex) {
+ bulletdelete(btConvex);
+ ERR_PRINTS("The shape is not a convex shape, then is not supported: shape type: " + itos(shape->get_type()));
+ return 0;
+ }
+
+ btVector3 scale_with_margin;
+ G_TO_B(p_shape_xform.basis.get_scale(), scale_with_margin);
+ btConvex->setLocalScaling(scale_with_margin);
+
+ btTransform bt_xform;
+ G_TO_B(p_shape_xform, bt_xform);
+
+ btCollisionObject collision_object;
+ collision_object.setCollisionShape(btConvex);
+ collision_object.setWorldTransform(bt_xform);
+
+ GodotContactPairContactResultCallback btQuery(&collision_object, r_results, p_result_max, &p_exclude);
+ btQuery.m_collisionFilterGroup = p_collision_layer;
+ btQuery.m_collisionFilterMask = p_object_type_mask;
+ btQuery.m_closestDistanceThreshold = p_margin;
+ space->dynamicsWorld->contactTest(&collision_object, btQuery);
+
+ r_result_count = btQuery.m_count;
+ bulletdelete(btConvex);
+
+ return btQuery.m_count;
+}
+
+bool BulletPhysicsDirectSpaceState::rest_info(RID p_shape, const Transform &p_shape_xform, float p_margin, ShapeRestInfo *r_info, const Set<RID> &p_exclude, uint32_t p_collision_layer, uint32_t p_object_type_mask) {
+
+ ShapeBullet *shape = space->get_physics_server()->get_shape_owner()->get(p_shape);
+
+ btConvexShape *btConvex = dynamic_cast<btConvexShape *>(shape->create_bt_shape());
+ if (!btConvex) {
+ bulletdelete(btConvex);
+ ERR_PRINTS("The shape is not a convex shape, then is not supported: shape type: " + itos(shape->get_type()));
+ return 0;
+ }
+
+ btVector3 scale_with_margin;
+ G_TO_B(p_shape_xform.basis.get_scale() + Vector3(p_margin, p_margin, p_margin), scale_with_margin);
+ btConvex->setLocalScaling(scale_with_margin);
+
+ btTransform bt_xform;
+ G_TO_B(p_shape_xform, bt_xform);
+
+ btCollisionObject collision_object;
+ collision_object.setCollisionShape(btConvex);
+ collision_object.setWorldTransform(bt_xform);
+
+ GodotRestInfoContactResultCallback btQuery(&collision_object, r_info, &p_exclude);
+ btQuery.m_collisionFilterGroup = p_collision_layer;
+ btQuery.m_collisionFilterMask = p_object_type_mask;
+ btQuery.m_closestDistanceThreshold = p_margin;
+ space->dynamicsWorld->contactTest(&collision_object, btQuery);
+
+ bulletdelete(btConvex);
+
+ if (btQuery.m_collided) {
+ if (btCollisionObject::CO_RIGID_BODY == btQuery.m_rest_info_collision_object->getInternalType()) {
+ B_TO_G(static_cast<const btRigidBody *>(btQuery.m_rest_info_collision_object)->getVelocityInLocalPoint(btQuery.m_rest_info_bt_point), r_info->linear_velocity);
+ }
+ B_TO_G(btQuery.m_rest_info_bt_point, r_info->point);
+ }
+
+ return btQuery.m_collided;
+}
+
+Vector3 BulletPhysicsDirectSpaceState::get_closest_point_to_object_volume(RID p_object, const Vector3 p_point) const {
+
+ RigidCollisionObjectBullet *rigid_object = space->get_physics_server()->get_rigid_collisin_object(p_object);
+ ERR_FAIL_COND_V(!rigid_object, Vector3());
+
+ btVector3 out_closest_point(0, 0, 0);
+ btScalar out_distance = 1e20;
+
+ btVector3 bt_point;
+ G_TO_B(p_point, bt_point);
+
+ btGjkEpaPenetrationDepthSolver gjk_epa_pen_solver;
+ btVoronoiSimplexSolver gjk_simplex_solver;
+ gjk_simplex_solver.setEqualVertexThreshold(0.);
+
+ btSphereShape point_shape(0.);
+
+ btCollisionShape *shape;
+ btConvexShape *convex_shape;
+ btTransform child_transform;
+ btTransform body_transform(rigid_object->get_bt_collision_object()->getWorldTransform());
+
+ btGjkPairDetector::ClosestPointInput input;
+ input.m_transformA.getBasis().setIdentity();
+ input.m_transformA.setOrigin(bt_point);
+
+ bool shapes_found = false;
+
+ btCompoundShape *compound = rigid_object->get_compound_shape();
+ for (int i = compound->getNumChildShapes() - 1; 0 <= i; --i) {
+ shape = compound->getChildShape(i);
+ if (shape->isConvex()) {
+ child_transform = compound->getChildTransform(i);
+ convex_shape = static_cast<btConvexShape *>(shape);
+
+ input.m_transformB = body_transform * child_transform;
+
+ btPointCollector result;
+ btGjkPairDetector gjk_pair_detector(&point_shape, convex_shape, &gjk_simplex_solver, &gjk_epa_pen_solver);
+ gjk_pair_detector.getClosestPoints(input, result, 0);
+
+ if (out_distance > result.m_distance) {
+ out_distance = result.m_distance;
+ out_closest_point = result.m_pointInWorld;
+ }
+ }
+ shapes_found = true;
+ }
+
+ if (shapes_found) {
+
+ Vector3 out;
+ B_TO_G(out_closest_point, out);
+ return out;
+ } else {
+
+ // no shapes found, use distance to origin.
+ return rigid_object->get_transform().get_origin();
+ }
+}
+
+SpaceBullet::SpaceBullet(bool p_create_soft_world)
+ : broadphase(NULL),
+ dispatcher(NULL),
+ solver(NULL),
+ collisionConfiguration(NULL),
+ dynamicsWorld(NULL),
+ soft_body_world_info(NULL),
+ ghostPairCallback(NULL),
+ godotFilterCallback(NULL),
+ gravityDirection(0, -1, 0),
+ gravityMagnitude(10),
+ contactDebugCount(0) {
+
+ create_empty_world(p_create_soft_world);
+ direct_access = memnew(BulletPhysicsDirectSpaceState(this));
+}
+
+SpaceBullet::~SpaceBullet() {
+ memdelete(direct_access);
+ destroy_world();
+}
+
+void SpaceBullet::flush_queries() {
+ const btCollisionObjectArray &colObjArray = dynamicsWorld->getCollisionObjectArray();
+ for (int i = colObjArray.size() - 1; 0 <= i; --i) {
+ static_cast<CollisionObjectBullet *>(colObjArray[i]->getUserPointer())->dispatch_callbacks();
+ }
+}
+
+void SpaceBullet::step(real_t p_delta_time) {
+ dynamicsWorld->stepSimulation(p_delta_time, 0, 0);
+}
+
+void SpaceBullet::set_param(PhysicsServer::AreaParameter p_param, const Variant &p_value) {
+ assert(dynamicsWorld);
+
+ switch (p_param) {
+ case PhysicsServer::AREA_PARAM_GRAVITY:
+ gravityMagnitude = p_value;
+ update_gravity();
+ break;
+ case PhysicsServer::AREA_PARAM_GRAVITY_VECTOR:
+ gravityDirection = p_value;
+ update_gravity();
+ break;
+ case PhysicsServer::AREA_PARAM_LINEAR_DAMP:
+ case PhysicsServer::AREA_PARAM_ANGULAR_DAMP:
+ break; // No damp
+ case PhysicsServer::AREA_PARAM_PRIORITY:
+ // Priority is always 0, the lower
+ break;
+ case PhysicsServer::AREA_PARAM_GRAVITY_IS_POINT:
+ case PhysicsServer::AREA_PARAM_GRAVITY_DISTANCE_SCALE:
+ case PhysicsServer::AREA_PARAM_GRAVITY_POINT_ATTENUATION:
+ break;
+ default:
+ WARN_PRINTS("This set parameter (" + itos(p_param) + ") is ignored, the SpaceBullet doesn't support it.");
+ break;
+ }
+}
+
+Variant SpaceBullet::get_param(PhysicsServer::AreaParameter p_param) {
+ switch (p_param) {
+ case PhysicsServer::AREA_PARAM_GRAVITY:
+ return gravityMagnitude;
+ case PhysicsServer::AREA_PARAM_GRAVITY_VECTOR:
+ return gravityDirection;
+ case PhysicsServer::AREA_PARAM_LINEAR_DAMP:
+ case PhysicsServer::AREA_PARAM_ANGULAR_DAMP:
+ return 0; // No damp
+ case PhysicsServer::AREA_PARAM_PRIORITY:
+ return 0; // Priority is always 0, the lower
+ case PhysicsServer::AREA_PARAM_GRAVITY_IS_POINT:
+ return false;
+ case PhysicsServer::AREA_PARAM_GRAVITY_DISTANCE_SCALE:
+ return 0;
+ case PhysicsServer::AREA_PARAM_GRAVITY_POINT_ATTENUATION:
+ return 0;
+ default:
+ WARN_PRINTS("This get parameter (" + itos(p_param) + ") is ignored, the SpaceBullet doesn't support it.");
+ return Variant();
+ }
+}
+
+void SpaceBullet::set_param(PhysicsServer::SpaceParameter p_param, real_t p_value) {
+ switch (p_param) {
+ case PhysicsServer::SPACE_PARAM_CONTACT_RECYCLE_RADIUS:
+ case PhysicsServer::SPACE_PARAM_CONTACT_MAX_SEPARATION:
+ case PhysicsServer::SPACE_PARAM_BODY_MAX_ALLOWED_PENETRATION:
+ case PhysicsServer::SPACE_PARAM_BODY_LINEAR_VELOCITY_SLEEP_THRESHOLD:
+ case PhysicsServer::SPACE_PARAM_BODY_ANGULAR_VELOCITY_SLEEP_THRESHOLD:
+ case PhysicsServer::SPACE_PARAM_BODY_TIME_TO_SLEEP:
+ case PhysicsServer::SPACE_PARAM_BODY_ANGULAR_VELOCITY_DAMP_RATIO:
+ case PhysicsServer::SPACE_PARAM_CONSTRAINT_DEFAULT_BIAS:
+ default:
+ WARN_PRINTS("This set parameter (" + itos(p_param) + ") is ignored, the SpaceBullet doesn't support it.");
+ break;
+ }
+}
+
+real_t SpaceBullet::get_param(PhysicsServer::SpaceParameter p_param) {
+ switch (p_param) {
+ case PhysicsServer::SPACE_PARAM_CONTACT_RECYCLE_RADIUS:
+ case PhysicsServer::SPACE_PARAM_CONTACT_MAX_SEPARATION:
+ case PhysicsServer::SPACE_PARAM_BODY_MAX_ALLOWED_PENETRATION:
+ case PhysicsServer::SPACE_PARAM_BODY_LINEAR_VELOCITY_SLEEP_THRESHOLD:
+ case PhysicsServer::SPACE_PARAM_BODY_ANGULAR_VELOCITY_SLEEP_THRESHOLD:
+ case PhysicsServer::SPACE_PARAM_BODY_TIME_TO_SLEEP:
+ case PhysicsServer::SPACE_PARAM_BODY_ANGULAR_VELOCITY_DAMP_RATIO:
+ case PhysicsServer::SPACE_PARAM_CONSTRAINT_DEFAULT_BIAS:
+ default:
+ WARN_PRINTS("The SpaceBullet doesn't support this get parameter (" + itos(p_param) + "), 0 is returned.");
+ return 0.f;
+ }
+}
+
+void SpaceBullet::add_area(AreaBullet *p_area) {
+ areas.push_back(p_area);
+ dynamicsWorld->addCollisionObject(p_area->get_bt_ghost(), p_area->get_collision_layer(), p_area->get_collision_mask());
+}
+
+void SpaceBullet::remove_area(AreaBullet *p_area) {
+ areas.erase(p_area);
+ dynamicsWorld->removeCollisionObject(p_area->get_bt_ghost());
+}
+
+void SpaceBullet::reload_collision_filters(AreaBullet *p_area) {
+ // This is necessary to change collision filter
+ dynamicsWorld->removeCollisionObject(p_area->get_bt_ghost());
+ dynamicsWorld->addCollisionObject(p_area->get_bt_ghost(), p_area->get_collision_layer(), p_area->get_collision_mask());
+}
+
+void SpaceBullet::add_rigid_body(RigidBodyBullet *p_body) {
+ if (p_body->is_static()) {
+ dynamicsWorld->addCollisionObject(p_body->get_bt_rigid_body(), p_body->get_collision_layer(), p_body->get_collision_mask());
+ } else {
+ dynamicsWorld->addRigidBody(p_body->get_bt_rigid_body(), p_body->get_collision_layer(), p_body->get_collision_mask());
+ }
+}
+
+void SpaceBullet::remove_rigid_body(RigidBodyBullet *p_body) {
+ if (p_body->is_static()) {
+ dynamicsWorld->removeCollisionObject(p_body->get_bt_rigid_body());
+ } else {
+ dynamicsWorld->removeRigidBody(p_body->get_bt_rigid_body());
+ }
+}
+
+void SpaceBullet::reload_collision_filters(RigidBodyBullet *p_body) {
+ // This is necessary to change collision filter
+ remove_rigid_body(p_body);
+ add_rigid_body(p_body);
+}
+
+void SpaceBullet::add_soft_body(SoftBodyBullet *p_body) {
+ if (is_using_soft_world()) {
+ if (p_body->get_bt_soft_body()) {
+ static_cast<btSoftRigidDynamicsWorld *>(dynamicsWorld)->addSoftBody(p_body->get_bt_soft_body(), p_body->get_collision_layer(), p_body->get_collision_mask());
+ }
+ } else {
+ ERR_PRINT("This soft body can't be added to non soft world");
+ }
+}
+
+void SpaceBullet::remove_soft_body(SoftBodyBullet *p_body) {
+ if (is_using_soft_world()) {
+ if (p_body->get_bt_soft_body()) {
+ static_cast<btSoftRigidDynamicsWorld *>(dynamicsWorld)->removeSoftBody(p_body->get_bt_soft_body());
+ }
+ }
+}
+
+void SpaceBullet::reload_collision_filters(SoftBodyBullet *p_body) {
+ // This is necessary to change collision filter
+ remove_soft_body(p_body);
+ add_soft_body(p_body);
+}
+
+void SpaceBullet::add_constraint(ConstraintBullet *p_constraint, bool disableCollisionsBetweenLinkedBodies) {
+ p_constraint->set_space(this);
+ dynamicsWorld->addConstraint(p_constraint->get_bt_constraint(), disableCollisionsBetweenLinkedBodies);
+}
+
+void SpaceBullet::remove_constraint(ConstraintBullet *p_constraint) {
+ dynamicsWorld->removeConstraint(p_constraint->get_bt_constraint());
+}
+
+int SpaceBullet::get_num_collision_objects() const {
+ return dynamicsWorld->getNumCollisionObjects();
+}
+
+void SpaceBullet::remove_all_collision_objects() {
+ for (int i = dynamicsWorld->getNumCollisionObjects() - 1; 0 <= i; --i) {
+ btCollisionObject *btObj = dynamicsWorld->getCollisionObjectArray()[i];
+ CollisionObjectBullet *colObj = static_cast<CollisionObjectBullet *>(btObj->getUserPointer());
+ colObj->set_space(NULL);
+ }
+}
+
+void onBulletPreTickCallback(btDynamicsWorld *p_dynamicsWorld, btScalar timeStep) {
+ static_cast<SpaceBullet *>(p_dynamicsWorld->getWorldUserInfo())->flush_queries();
+}
+
+void onBulletTickCallback(btDynamicsWorld *p_dynamicsWorld, btScalar timeStep) {
+
+ // Notify all Collision objects the collision checker is started
+ const btCollisionObjectArray &colObjArray = p_dynamicsWorld->getCollisionObjectArray();
+ for (int i = colObjArray.size() - 1; 0 <= i; --i) {
+ CollisionObjectBullet *colObj = static_cast<CollisionObjectBullet *>(colObjArray[i]->getUserPointer());
+ assert(NULL != colObj);
+ colObj->on_collision_checker_start();
+ }
+
+ SpaceBullet *sb = static_cast<SpaceBullet *>(p_dynamicsWorld->getWorldUserInfo());
+ sb->check_ghost_overlaps();
+ sb->check_body_collision();
+}
+
+BulletPhysicsDirectSpaceState *SpaceBullet::get_direct_state() {
+ return direct_access;
+}
+
+btScalar calculateGodotCombinedRestitution(const btCollisionObject *body0, const btCollisionObject *body1) {
+ return MAX(body0->getRestitution(), body1->getRestitution());
+}
+
+void SpaceBullet::create_empty_world(bool p_create_soft_world) {
+ assert(NULL == broadphase);
+ assert(NULL == dispatcher);
+ assert(NULL == solver);
+ assert(NULL == collisionConfiguration);
+ assert(NULL == dynamicsWorld);
+ assert(NULL == ghostPairCallback);
+ assert(NULL == godotFilterCallback);
+
+ void *world_mem;
+ if (p_create_soft_world) {
+ world_mem = malloc(sizeof(btSoftRigidDynamicsWorld));
+ } else {
+ world_mem = malloc(sizeof(btDiscreteDynamicsWorld));
+ }
+
+ if (p_create_soft_world) {
+ collisionConfiguration = bulletnew(btSoftBodyRigidBodyCollisionConfiguration);
+ } else {
+ collisionConfiguration = bulletnew(GodotCollisionConfiguration(static_cast<btDiscreteDynamicsWorld *>(world_mem)));
+ }
+
+ dispatcher = bulletnew(GodotCollisionDispatcher(collisionConfiguration));
+ broadphase = bulletnew(btDbvtBroadphase);
+ solver = bulletnew(btSequentialImpulseConstraintSolver);
+
+ if (p_create_soft_world) {
+ dynamicsWorld = new (world_mem) btSoftRigidDynamicsWorld(dispatcher, broadphase, solver, collisionConfiguration);
+ soft_body_world_info = bulletnew(btSoftBodyWorldInfo);
+ } else {
+ dynamicsWorld = new (world_mem) btDiscreteDynamicsWorld(dispatcher, broadphase, solver, collisionConfiguration);
+ }
+
+ ghostPairCallback = bulletnew(btGhostPairCallback);
+ godotFilterCallback = bulletnew(GodotFilterCallback);
+ gCalculateCombinedRestitutionCallback = &calculateGodotCombinedRestitution;
+
+ dynamicsWorld->setWorldUserInfo(this);
+
+ dynamicsWorld->setInternalTickCallback(onBulletPreTickCallback, this, true);
+ dynamicsWorld->setInternalTickCallback(onBulletTickCallback, this, false);
+ dynamicsWorld->getBroadphase()->getOverlappingPairCache()->setInternalGhostPairCallback(ghostPairCallback); // Setup ghost check
+ dynamicsWorld->getPairCache()->setOverlapFilterCallback(godotFilterCallback);
+
+ if (soft_body_world_info) {
+ soft_body_world_info->m_broadphase = broadphase;
+ soft_body_world_info->m_dispatcher = dispatcher;
+ soft_body_world_info->m_sparsesdf.Initialize();
+ }
+
+ update_gravity();
+}
+
+void SpaceBullet::destroy_world() {
+ assert(NULL != broadphase);
+ assert(NULL != dispatcher);
+ assert(NULL != solver);
+ assert(NULL != collisionConfiguration);
+ assert(NULL != dynamicsWorld);
+ assert(NULL != ghostPairCallback);
+ assert(NULL != godotFilterCallback);
+
+ /// The world elements (like: Collision Objects, Constraints, Shapes) are managed by godot
+
+ dynamicsWorld->getBroadphase()->getOverlappingPairCache()->setInternalGhostPairCallback(NULL);
+ dynamicsWorld->getPairCache()->setOverlapFilterCallback(NULL);
+
+ bulletdelete(ghostPairCallback);
+ bulletdelete(godotFilterCallback);
+
+ // Deallocate world
+ dynamicsWorld->~btDiscreteDynamicsWorld();
+ free(dynamicsWorld);
+ dynamicsWorld = NULL;
+
+ bulletdelete(solver);
+ bulletdelete(broadphase);
+ bulletdelete(dispatcher);
+ bulletdelete(collisionConfiguration);
+ bulletdelete(soft_body_world_info);
+}
+
+void SpaceBullet::check_ghost_overlaps() {
+
+ /// Algorith support variables
+ btGjkEpaPenetrationDepthSolver gjk_epa_pen_solver;
+ btVoronoiSimplexSolver gjk_simplex_solver;
+ gjk_simplex_solver.setEqualVertexThreshold(0.f);
+ btConvexShape *other_body_shape;
+ btConvexShape *area_shape;
+ btGjkPairDetector::ClosestPointInput gjk_input;
+ AreaBullet *area;
+ RigidCollisionObjectBullet *otherObject;
+ int x(-1), i(-1), y(-1), z(-1), indexOverlap(-1);
+
+ /// For each areas
+ for (x = areas.size() - 1; 0 <= x; --x) {
+ area = areas[x];
+
+ if (!area->is_monitoring())
+ continue;
+
+ /// 1. Reset all states
+ for (i = area->overlappingObjects.size() - 1; 0 <= i; --i) {
+ AreaBullet::OverlappingObjectData &otherObj = area->overlappingObjects[i];
+ // This check prevent the overwrite of ENTER state
+ // if this function is called more times before dispatchCallbacks
+ if (otherObj.state != AreaBullet::OVERLAP_STATE_ENTER) {
+ otherObj.state = AreaBullet::OVERLAP_STATE_DIRTY;
+ }
+ }
+
+ /// 2. Check all overlapping objects using GJK
+
+ const btAlignedObjectArray<btCollisionObject *> ghostOverlaps = area->get_bt_ghost()->getOverlappingPairs();
+
+ // For each overlapping
+ for (i = ghostOverlaps.size() - 1; 0 <= i; --i) {
+
+ if (!(ghostOverlaps[i]->getUserIndex() == CollisionObjectBullet::TYPE_RIGID_BODY || ghostOverlaps[i]->getUserIndex() == CollisionObjectBullet::TYPE_AREA))
+ continue;
+
+ otherObject = static_cast<RigidCollisionObjectBullet *>(ghostOverlaps[i]->getUserPointer());
+
+ bool hasOverlap = false;
+
+ // For each area shape
+ for (y = area->get_compound_shape()->getNumChildShapes() - 1; 0 <= y; --y) {
+ if (!area->get_compound_shape()->getChildShape(y)->isConvex())
+ continue;
+
+ gjk_input.m_transformA = area->get_transform__bullet() * area->get_compound_shape()->getChildTransform(y);
+ area_shape = static_cast<btConvexShape *>(area->get_compound_shape()->getChildShape(y));
+
+ // For each other object shape
+ for (z = otherObject->get_compound_shape()->getNumChildShapes() - 1; 0 <= z; --z) {
+
+ if (!otherObject->get_compound_shape()->getChildShape(z)->isConvex())
+ continue;
+
+ other_body_shape = static_cast<btConvexShape *>(otherObject->get_compound_shape()->getChildShape(z));
+ gjk_input.m_transformB = otherObject->get_transform__bullet() * otherObject->get_compound_shape()->getChildTransform(z);
+
+ btPointCollector result;
+ btGjkPairDetector gjk_pair_detector(area_shape, other_body_shape, &gjk_simplex_solver, &gjk_epa_pen_solver);
+ gjk_pair_detector.getClosestPoints(gjk_input, result, 0);
+
+ if (0 >= result.m_distance) {
+ hasOverlap = true;
+ goto collision_found;
+ }
+ } // ~For each other object shape
+ } // ~For each area shape
+
+ collision_found:
+ if (!hasOverlap)
+ continue;
+
+ indexOverlap = area->find_overlapping_object(otherObject);
+ if (-1 == indexOverlap) {
+ // Not found
+ area->add_overlap(otherObject);
+ } else {
+ // Found
+ area->put_overlap_as_inside(indexOverlap);
+ }
+ }
+
+ /// 3. Remove not overlapping
+ for (i = area->overlappingObjects.size() - 1; 0 <= i; --i) {
+ // If the overlap has DIRTY state it means that it's no more overlapping
+ if (area->overlappingObjects[i].state == AreaBullet::OVERLAP_STATE_DIRTY) {
+ area->put_overlap_as_exit(i);
+ }
+ }
+ }
+}
+
+void SpaceBullet::check_body_collision() {
+#ifdef DEBUG_ENABLED
+ reset_debug_contact_count();
+#endif
+
+ const int numManifolds = dynamicsWorld->getDispatcher()->getNumManifolds();
+ for (int i = 0; i < numManifolds; ++i) {
+ btPersistentManifold *contactManifold = dynamicsWorld->getDispatcher()->getManifoldByIndexInternal(i);
+ const btCollisionObject *obA = contactManifold->getBody0();
+ const btCollisionObject *obB = contactManifold->getBody1();
+
+ if (btCollisionObject::CO_RIGID_BODY != obA->getInternalType() || btCollisionObject::CO_RIGID_BODY != obB->getInternalType()) {
+ // This checks is required to be sure the ghost object is skipped
+ // The ghost object "getUserPointer" return the BodyBullet owner so this check is required
+ continue;
+ }
+
+ // Asserts all Godot objects are assigned
+ assert(NULL != obA->getUserPointer());
+ assert(NULL != obB->getUserPointer());
+
+ // I know this static cast is a bit risky. But I'm checking its type just after it.
+ // This allow me to avoid a lot of other cast and checks
+ RigidBodyBullet *bodyA = static_cast<RigidBodyBullet *>(obA->getUserPointer());
+ RigidBodyBullet *bodyB = static_cast<RigidBodyBullet *>(obB->getUserPointer());
+
+ if (CollisionObjectBullet::TYPE_RIGID_BODY == bodyA->getType() && CollisionObjectBullet::TYPE_RIGID_BODY == bodyB->getType()) {
+ if (!bodyA->can_add_collision() && !bodyB->can_add_collision()) {
+ continue;
+ }
+
+ const int numContacts = contactManifold->getNumContacts();
+#define REPORT_ALL_CONTACTS 0
+#if REPORT_ALL_CONTACTS
+ for (int j = 0; j < numContacts; j++) {
+ btManifoldPoint &pt = contactManifold->getContactPoint(j);
+#else
+ // Since I don't need report all contacts for these objects, I'll report only the first
+ if (numContacts) {
+ btManifoldPoint &pt = contactManifold->getContactPoint(0);
+#endif
+ Vector3 collisionWorldPosition;
+ Vector3 collisionLocalPosition;
+ Vector3 normalOnB;
+ B_TO_G(pt.m_normalWorldOnB, normalOnB);
+
+ if (bodyA->can_add_collision()) {
+ B_TO_G(pt.getPositionWorldOnB(), collisionWorldPosition);
+ /// pt.m_localPointB Doesn't report the exact point in local space
+ B_TO_G(pt.getPositionWorldOnB() - obB->getWorldTransform().getOrigin(), collisionLocalPosition);
+ bodyA->add_collision_object(bodyB, collisionWorldPosition, collisionLocalPosition, normalOnB, pt.m_index1, pt.m_index0);
+ }
+ if (bodyB->can_add_collision()) {
+ B_TO_G(pt.getPositionWorldOnA(), collisionWorldPosition);
+ /// pt.m_localPointA Doesn't report the exact point in local space
+ B_TO_G(pt.getPositionWorldOnA() - obA->getWorldTransform().getOrigin(), collisionLocalPosition);
+ bodyB->add_collision_object(bodyA, collisionWorldPosition, collisionLocalPosition, normalOnB * -1, pt.m_index0, pt.m_index1);
+ }
+
+#ifdef DEBUG_ENABLED
+ if (is_debugging_contacts()) {
+ add_debug_contact(collisionWorldPosition);
+ }
+#endif
+ }
+ }
+ }
+}
+
+void SpaceBullet::update_gravity() {
+ btVector3 btGravity;
+ G_TO_B(gravityDirection * gravityMagnitude, btGravity);
+ dynamicsWorld->setGravity(btGravity);
+ if (soft_body_world_info) {
+ soft_body_world_info->m_gravity = btGravity;
+ }
+}
+
+/// IMPORTANT: Please don't turn it ON this is not managed correctly!!
+/// I'm leaving this here just for future tests.
+/// Debug motion and normal vector drawing
+#define debug_test_motion 0
+#if debug_test_motion
+static ImmediateGeometry *motionVec(NULL);
+static ImmediateGeometry *normalLine(NULL);
+static Ref<SpatialMaterial> red_mat;
+static Ref<SpatialMaterial> blue_mat;
+#endif
+
+#define IGNORE_AREAS_TRUE true
+bool SpaceBullet::test_body_motion(RigidBodyBullet *p_body, const Transform &p_from, const Vector3 &p_motion, real_t p_margin, PhysicsServer::MotionResult *r_result) {
+
+#if debug_test_motion
+ /// Yes I know this is not good, but I've used it as fast debugging.
+ /// I'm leaving it here just for speedup the other eventual debugs
+ if (!normalLine) {
+ motionVec = memnew(ImmediateGeometry);
+ normalLine = memnew(ImmediateGeometry);
+ SceneTree::get_singleton()->get_current_scene()->add_child(motionVec);
+ SceneTree::get_singleton()->get_current_scene()->add_child(normalLine);
+
+ red_mat = Ref<SpatialMaterial>(memnew(SpatialMaterial));
+ red_mat->set_flag(SpatialMaterial::FLAG_UNSHADED, true);
+ red_mat->set_line_width(20.0);
+ red_mat->set_feature(SpatialMaterial::FEATURE_TRANSPARENT, true);
+ red_mat->set_flag(SpatialMaterial::FLAG_ALBEDO_FROM_VERTEX_COLOR, true);
+ red_mat->set_flag(SpatialMaterial::FLAG_SRGB_VERTEX_COLOR, true);
+ red_mat->set_albedo(Color(1, 0, 0, 1));
+ motionVec->set_material_override(red_mat);
+
+ blue_mat = Ref<SpatialMaterial>(memnew(SpatialMaterial));
+ blue_mat->set_flag(SpatialMaterial::FLAG_UNSHADED, true);
+ blue_mat->set_line_width(20.0);
+ blue_mat->set_feature(SpatialMaterial::FEATURE_TRANSPARENT, true);
+ blue_mat->set_flag(SpatialMaterial::FLAG_ALBEDO_FROM_VERTEX_COLOR, true);
+ blue_mat->set_flag(SpatialMaterial::FLAG_SRGB_VERTEX_COLOR, true);
+ blue_mat->set_albedo(Color(0, 0, 1, 1));
+ normalLine->set_material_override(blue_mat);
+ }
+#endif
+
+ ///// Release all generated manifolds
+ //{
+ // if(p_body->get_kinematic_utilities()){
+ // for(int i= p_body->get_kinematic_utilities()->m_generatedManifold.size()-1; 0<=i; --i){
+ // dispatcher->releaseManifold( p_body->get_kinematic_utilities()->m_generatedManifold[i] );
+ // }
+ // p_body->get_kinematic_utilities()->m_generatedManifold.clear();
+ // }
+ //}
+
+ btVector3 recover_initial_position;
+ recover_initial_position.setZero();
+
+/// I'm performing the unstack at the end of movement so I'm sure the player is unstacked even after the movement.
+/// I've removed the initial unstack because this is useful just for the first tick since after the first
+/// the real unstack is performed at the end of process.
+/// However I'm leaving here the old code.
+/// Note: It has a bug when two shapes touches something simultaneously the body is moved too much away (I'm not fixing it for the reason written above).
+#define INITIAL_UNSTACK 0
+#if !INITIAL_UNSTACK
+ btTransform body_safe_position;
+ G_TO_B(p_from, body_safe_position);
+//btTransform body_unsafe_positino;
+//G_TO_B(p_from, body_unsafe_positino);
+#else
+ btTransform body_safe_position;
+ btTransform body_unsafe_positino;
+ { /// Phase one - multi shapes depenetration using margin
+ G_TO_B(p_from, body_safe_position);
+ G_TO_B(p_from, body_unsafe_positino);
+
+ // MAX_PENETRATION_DEPTH Is useful have the ghost a bit penetrated so I can detect the floor easily
+ recover_from_penetration(p_body, body_safe_position, MAX_PENETRATION_DEPTH, /* p_depenetration_speed */ 1, recover_initial_position);
+
+ /// Not required if I put p_depenetration_speed = 1
+ //for(int t = 0; t<4; ++t){
+ // if(!recover_from_penetration(p_body, body_safe_position, MAX_PENETRATION_DEPTH, /* p_depenetration_speed */0.2, recover_initial_position)){
+ // break;
+ // }
+ //}
+
+ // Add recover position to "From" and "To" transforms
+ body_safe_position.getOrigin() += recover_initial_position;
+ }
+#endif
+
+ int shape_most_recovered(-1);
+ btVector3 recovered_motion;
+ G_TO_B(p_motion, recovered_motion);
+ const int shape_count(p_body->get_shape_count());
+
+ { /// phase two - sweep test, from a secure position without margin
+
+#if debug_test_motion
+ Vector3 sup_line;
+ B_TO_G(body_safe_position.getOrigin(), sup_line);
+ motionVec->clear();
+ motionVec->begin(Mesh::PRIMITIVE_LINES, NULL);
+ motionVec->add_vertex(sup_line);
+ motionVec->add_vertex(sup_line + p_motion * 10);
+ motionVec->end();
+#endif
+
+ for (int shIndex = 0; shIndex < shape_count; ++shIndex) {
+ if (p_body->is_shape_disabled(shIndex)) {
+ continue;
+ }
+
+ btConvexShape *convex_shape_test(dynamic_cast<btConvexShape *>(p_body->get_bt_shape(shIndex)));
+ if (!convex_shape_test) {
+ // Skip no convex shape
+ continue;
+ }
+
+ btTransform shape_xform_from;
+ G_TO_B(p_body->get_shape_transform(shIndex), shape_xform_from);
+ //btTransform shape_xform_to(shape_xform_from);
+
+ // Add local shape transform
+ shape_xform_from.getOrigin() += body_safe_position.getOrigin();
+ shape_xform_from.getBasis() *= body_safe_position.getBasis();
+
+ btTransform shape_xform_to(shape_xform_from);
+ //shape_xform_to.getOrigin() += body_unsafe_positino.getOrigin();
+ //shape_xform_to.getBasis() *= body_unsafe_positino.getBasis();
+ shape_xform_to.getOrigin() += recovered_motion;
+
+ GodotKinClosestConvexResultCallback btResult(shape_xform_from.getOrigin(), shape_xform_to.getOrigin(), p_body, IGNORE_AREAS_TRUE);
+ btResult.m_collisionFilterGroup = p_body->get_collision_layer();
+ btResult.m_collisionFilterMask = p_body->get_collision_mask();
+
+ dynamicsWorld->convexSweepTest(convex_shape_test, shape_xform_from, shape_xform_to, btResult);
+
+ if (btResult.hasHit()) {
+ //recovered_motion *= btResult.m_closestHitFraction;
+ /// Since for each sweep test I fix the motion of new shapes in base the recover result,
+ /// if another shape will hit something it means that has a deepest recovering respect the previous shape
+ shape_most_recovered = shIndex;
+ }
+ }
+ }
+
+ bool hasHit = false;
+
+ { /// Phase three - contact test with margin
+
+ btGhostObject *ghost = p_body->get_kinematic_utilities()->m_ghostObject;
+
+ GodotRecoverAndClosestContactResultCallback result_callabck;
+
+ if (false && 0 <= shape_most_recovered) {
+ result_callabck.m_self_object = p_body;
+ result_callabck.m_ignore_areas = IGNORE_AREAS_TRUE;
+ result_callabck.m_collisionFilterGroup = p_body->get_collision_layer();
+ result_callabck.m_collisionFilterMask = p_body->get_collision_mask();
+
+ const RigidBodyBullet::KinematicShape &kin(p_body->get_kinematic_utilities()->m_shapes[shape_most_recovered]);
+ ghost->setCollisionShape(kin.shape);
+ ghost->setWorldTransform(body_safe_position);
+
+ ghost->getWorldTransform().getOrigin() += recovered_motion;
+ ghost->getWorldTransform().getOrigin() += kin.transform.getOrigin();
+ ghost->getWorldTransform().getBasis() *= kin.transform.getBasis();
+
+ dynamicsWorld->contactTest(ghost, result_callabck);
+
+ recovered_motion += result_callabck.m_recover_penetration; // Required to avoid all kind of penetration
+
+ } else {
+ // The sweep result does not return a penetrated shape, so I've to check all shapes
+ // Then return the most penetrated shape
+
+ GodotRecoverAndClosestContactResultCallback iter_result_callabck(p_body, IGNORE_AREAS_TRUE);
+ iter_result_callabck.m_collisionFilterGroup = p_body->get_collision_layer();
+ iter_result_callabck.m_collisionFilterMask = p_body->get_collision_mask();
+
+ btScalar max_penetration(99999999999);
+ for (int i = 0; i < shape_count; ++i) {
+
+ const RigidBodyBullet::KinematicShape &kin(p_body->get_kinematic_utilities()->m_shapes[i]);
+ if (!kin.is_active()) {
+ continue;
+ }
+
+ // reset callback each function
+ iter_result_callabck.reset();
+
+ ghost->setCollisionShape(kin.shape);
+ ghost->setWorldTransform(body_safe_position);
+ ghost->getWorldTransform().getOrigin() += recovered_motion;
+ ghost->getWorldTransform().getOrigin() += kin.transform.getOrigin();
+ ghost->getWorldTransform().getBasis() *= kin.transform.getBasis();
+
+ dynamicsWorld->contactTest(ghost, iter_result_callabck);
+
+ if (iter_result_callabck.hasHit()) {
+ if (max_penetration > iter_result_callabck.m_penetration_distance) {
+ max_penetration = iter_result_callabck.m_penetration_distance;
+ shape_most_recovered = i;
+ // This is more penetrated
+ result_callabck.m_pointCollisionObject = iter_result_callabck.m_pointCollisionObject;
+ result_callabck.m_pointNormalWorld = iter_result_callabck.m_pointNormalWorld;
+ result_callabck.m_pointWorld = iter_result_callabck.m_pointWorld;
+ result_callabck.m_penetration_distance = iter_result_callabck.m_penetration_distance;
+ result_callabck.m_other_compound_shape_index = iter_result_callabck.m_other_compound_shape_index;
+
+ recovered_motion += iter_result_callabck.m_recover_penetration; // Required to avoid all kind of penetration
+ }
+ }
+ }
+ }
+
+ hasHit = result_callabck.hasHit();
+
+ if (r_result) {
+
+ B_TO_G(recovered_motion + recover_initial_position, r_result->motion);
+
+ if (hasHit) {
+
+ if (btCollisionObject::CO_RIGID_BODY != result_callabck.m_pointCollisionObject->getInternalType()) {
+ ERR_PRINT("The collision is not against a rigid body. Please check what's going on.");
+ goto EndExecution;
+ }
+ const btRigidBody *btRigid = static_cast<const btRigidBody *>(result_callabck.m_pointCollisionObject);
+ CollisionObjectBullet *collisionObject = static_cast<CollisionObjectBullet *>(btRigid->getUserPointer());
+
+ r_result->remainder = p_motion - r_result->motion; // is the remaining movements
+ B_TO_G(result_callabck.m_pointWorld, r_result->collision_point);
+ B_TO_G(result_callabck.m_pointNormalWorld, r_result->collision_normal);
+ B_TO_G(btRigid->getVelocityInLocalPoint(result_callabck.m_pointWorld - btRigid->getWorldTransform().getOrigin()), r_result->collider_velocity); // It calculates velocity at point and assign it using special function Bullet_to_Godot
+ r_result->collider = collisionObject->get_self();
+ r_result->collider_id = collisionObject->get_instance_id();
+ r_result->collider_shape = result_callabck.m_other_compound_shape_index;
+ r_result->collision_local_shape = shape_most_recovered;
+
+//{ /// Add manifold point to manage collisions
+// btPersistentManifold* manifold = dynamicsWorld->getDispatcher()->getNewManifold(p_body->getBtBody(), btRigid);
+// btManifoldPoint manifoldPoint(result_callabck.m_pointWorld, result_callabck.m_pointWorld, result_callabck.m_pointNormalWorld, result_callabck.m_penetration_distance);
+// manifoldPoint.m_index0 = r_result->collision_local_shape;
+// manifoldPoint.m_index1 = r_result->collider_shape;
+// manifold->addManifoldPoint(manifoldPoint);
+// p_body->get_kinematic_utilities()->m_generatedManifold.push_back(manifold);
+//}
+
+#if debug_test_motion
+ Vector3 sup_line2;
+ B_TO_G(recovered_motion, sup_line2);
+ //Vector3 sup_pos;
+ //B_TO_G( pt.getPositionWorldOnB(), sup_pos);
+ normalLine->clear();
+ normalLine->begin(Mesh::PRIMITIVE_LINES, NULL);
+ normalLine->add_vertex(r_result->collision_point);
+ normalLine->add_vertex(r_result->collision_point + r_result->collision_normal * 10);
+ normalLine->end();
+#endif
+
+ } else {
+ r_result->remainder = Vector3();
+ }
+ }
+ }
+
+EndExecution:
+ p_body->get_kinematic_utilities()->resetDefShape();
+ return hasHit;
+}
+
+/// Note: It has a bug when two shapes touches something simultaneously the body is moved too much away
+/// (I'm not fixing it because I don't use it).
+bool SpaceBullet::recover_from_penetration(RigidBodyBullet *p_body, const btTransform &p_from, btScalar p_maxPenetrationDepth, btScalar p_depenetration_speed, btVector3 &out_recover_position) {
+
+ bool penetration = false;
+ btPairCachingGhostObject *ghost = p_body->get_kinematic_utilities()->m_ghostObject;
+
+ for (int kinIndex = p_body->get_kinematic_utilities()->m_shapes.size() - 1; 0 <= kinIndex; --kinIndex) {
+ const RigidBodyBullet::KinematicShape &kin_shape(p_body->get_kinematic_utilities()->m_shapes[kinIndex]);
+ if (!kin_shape.is_active()) {
+ continue;
+ }
+
+ btConvexShape *convexShape = kin_shape.shape;
+ btTransform shape_xform(kin_shape.transform);
+
+ // from local to world
+ shape_xform.getOrigin() += p_from.getOrigin();
+ shape_xform.getBasis() *= p_from.getBasis();
+
+ // Apply last recovery to avoid doubling the recovering
+ shape_xform.getOrigin() += out_recover_position;
+
+ ghost->setCollisionShape(convexShape);
+ ghost->setWorldTransform(shape_xform);
+
+ btVector3 minAabb, maxAabb;
+ convexShape->getAabb(shape_xform, minAabb, maxAabb);
+ dynamicsWorld->getBroadphase()->setAabb(ghost->getBroadphaseHandle(),
+ minAabb,
+ maxAabb,
+ dynamicsWorld->getDispatcher());
+
+ dynamicsWorld->getDispatcher()->dispatchAllCollisionPairs(ghost->getOverlappingPairCache(), dynamicsWorld->getDispatchInfo(), dynamicsWorld->getDispatcher());
+
+ for (int i = 0; i < ghost->getOverlappingPairCache()->getNumOverlappingPairs(); ++i) {
+ p_body->get_kinematic_utilities()->m_manifoldArray.resize(0);
+
+ btBroadphasePair *collisionPair = &ghost->getOverlappingPairCache()->getOverlappingPairArray()[i];
+
+ btCollisionObject *obj0 = static_cast<btCollisionObject *>(collisionPair->m_pProxy0->m_clientObject);
+ btCollisionObject *obj1 = static_cast<btCollisionObject *>(collisionPair->m_pProxy1->m_clientObject);
+
+ if ((obj0 && !obj0->hasContactResponse()) || (obj1 && !obj1->hasContactResponse()))
+ continue;
+
+ // This is not required since the dispatched does all the job
+ //if (!needsCollision(obj0, obj1))
+ // continue;
+
+ if (collisionPair->m_algorithm)
+ collisionPair->m_algorithm->getAllContactManifolds(p_body->get_kinematic_utilities()->m_manifoldArray);
+
+ for (int j = 0; j < p_body->get_kinematic_utilities()->m_manifoldArray.size(); ++j) {
+
+ btPersistentManifold *manifold = p_body->get_kinematic_utilities()->m_manifoldArray[j];
+ btScalar directionSign = manifold->getBody0() == ghost ? btScalar(-1.0) : btScalar(1.0);
+ for (int p = 0; p < manifold->getNumContacts(); ++p) {
+ const btManifoldPoint &pt = manifold->getContactPoint(p);
+
+ btScalar dist = pt.getDistance();
+ if (dist < -p_maxPenetrationDepth) {
+ penetration = true;
+ out_recover_position += pt.m_normalWorldOnB * directionSign * (dist + p_maxPenetrationDepth) * p_depenetration_speed;
+ //print_line("penetrate distance: " + rtos(dist));
+ }
+ //else {
+ // print_line("touching distance: " + rtos(dist));
+ //}
+ }
+ }
+ }
+ }
+
+ p_body->get_kinematic_utilities()->resetDefShape();
+ return penetration;
+}
diff --git a/modules/bullet/space_bullet.h b/modules/bullet/space_bullet.h
new file mode 100644
index 0000000000..cbbfdac1d7
--- /dev/null
+++ b/modules/bullet/space_bullet.h
@@ -0,0 +1,176 @@
+/*************************************************************************/
+/* space_bullet.h */
+/* Author: AndreaCatania */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2017 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 SPACE_BULLET_H
+#define SPACE_BULLET_H
+
+#include "BulletCollision/BroadphaseCollision/btBroadphaseProxy.h"
+#include "BulletCollision/BroadphaseCollision/btOverlappingPairCache.h"
+#include "LinearMath/btScalar.h"
+#include "LinearMath/btTransform.h"
+#include "LinearMath/btVector3.h"
+#include "core/variant.h"
+#include "core/vector.h"
+#include "godot_result_callbacks.h"
+#include "rid_bullet.h"
+#include "servers/physics_server.h"
+
+class AreaBullet;
+class btBroadphaseInterface;
+class btCollisionDispatcher;
+class btConstraintSolver;
+class btDefaultCollisionConfiguration;
+class btDynamicsWorld;
+class btDiscreteDynamicsWorld;
+class btEmptyShape;
+class btGhostPairCallback;
+class btSoftRigidDynamicsWorld;
+class btSoftBodyWorldInfo;
+class ConstraintBullet;
+class CollisionObjectBullet;
+class RigidBodyBullet;
+class SpaceBullet;
+class SoftBodyBullet;
+
+class BulletPhysicsDirectSpaceState : public PhysicsDirectSpaceState {
+ GDCLASS(BulletPhysicsDirectSpaceState, PhysicsDirectSpaceState)
+private:
+ SpaceBullet *space;
+
+public:
+ BulletPhysicsDirectSpaceState(SpaceBullet *p_space);
+
+ virtual int intersect_point(const Vector3 &p_point, ShapeResult *r_results, int p_result_max, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_layer = 0xFFFFFFFF, uint32_t p_object_type_mask = TYPE_MASK_COLLISION);
+ virtual bool intersect_ray(const Vector3 &p_from, const Vector3 &p_to, RayResult &r_result, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_layer = 0xFFFFFFFF, uint32_t p_object_type_mask = TYPE_MASK_COLLISION, bool p_pick_ray = false);
+ virtual int intersect_shape(const RID &p_shape, const Transform &p_xform, float p_margin, ShapeResult *r_results, int p_result_max, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_layer = 0xFFFFFFFF, uint32_t p_object_type_mask = TYPE_MASK_COLLISION);
+ virtual bool cast_motion(const RID &p_shape, const Transform &p_xform, const Vector3 &p_motion, float p_margin, float &p_closest_safe, float &p_closest_unsafe, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_layer = 0xFFFFFFFF, uint32_t p_object_type_mask = TYPE_MASK_COLLISION, ShapeRestInfo *r_info = NULL);
+ /// Returns the list of contacts pairs in this order: Local contact, other body contact
+ virtual bool collide_shape(RID p_shape, const Transform &p_shape_xform, float p_margin, Vector3 *r_results, int p_result_max, int &r_result_count, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_layer = 0xFFFFFFFF, uint32_t p_object_type_mask = TYPE_MASK_COLLISION);
+ virtual bool rest_info(RID p_shape, const Transform &p_shape_xform, float p_margin, ShapeRestInfo *r_info, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_layer = 0xFFFFFFFF, uint32_t p_object_type_mask = TYPE_MASK_COLLISION);
+ virtual Vector3 get_closest_point_to_object_volume(RID p_object, const Vector3 p_point) const;
+};
+
+class SpaceBullet : public RIDBullet {
+private:
+ friend class AreaBullet;
+ friend void onBulletTickCallback(btDynamicsWorld *world, btScalar timeStep);
+ friend class BulletPhysicsDirectSpaceState;
+
+ btBroadphaseInterface *broadphase;
+ btDefaultCollisionConfiguration *collisionConfiguration;
+ btCollisionDispatcher *dispatcher;
+ btConstraintSolver *solver;
+ btDiscreteDynamicsWorld *dynamicsWorld;
+ btGhostPairCallback *ghostPairCallback;
+ GodotFilterCallback *godotFilterCallback;
+ btSoftBodyWorldInfo *soft_body_world_info;
+
+ BulletPhysicsDirectSpaceState *direct_access;
+ Vector3 gravityDirection;
+ real_t gravityMagnitude;
+
+ Vector<AreaBullet *> areas;
+
+ Vector<Vector3> contactDebug;
+ int contactDebugCount;
+
+public:
+ SpaceBullet(bool p_create_soft_world);
+ virtual ~SpaceBullet();
+
+ void flush_queries();
+ void step(real_t p_delta_time);
+
+ _FORCE_INLINE_ btCollisionDispatcher *get_dispatcher() { return dispatcher; }
+ _FORCE_INLINE_ btSoftBodyWorldInfo *get_soft_body_world_info() { return soft_body_world_info; }
+ _FORCE_INLINE_ bool is_using_soft_world() { return soft_body_world_info; }
+
+ /// Used to set some parameters to Bullet world
+ /// @param p_param:
+ /// AREA_PARAM_GRAVITY to set the gravity magnitude of entire world
+ /// AREA_PARAM_GRAVITY_VECTOR to set the gravity direction of entire world
+ void set_param(PhysicsServer::AreaParameter p_param, const Variant &p_value);
+ /// Used to get some parameters to Bullet world
+ /// @param p_param:
+ /// AREA_PARAM_GRAVITY to get the gravity magnitude of entire world
+ /// AREA_PARAM_GRAVITY_VECTOR to get the gravity direction of entire world
+ Variant get_param(PhysicsServer::AreaParameter p_param);
+
+ void set_param(PhysicsServer::SpaceParameter p_param, real_t p_value);
+ real_t get_param(PhysicsServer::SpaceParameter p_param);
+
+ void add_area(AreaBullet *p_area);
+ void remove_area(AreaBullet *p_area);
+ void reload_collision_filters(AreaBullet *p_area);
+
+ void add_rigid_body(RigidBodyBullet *p_body);
+ void remove_rigid_body(RigidBodyBullet *p_body);
+ void reload_collision_filters(RigidBodyBullet *p_body);
+
+ void add_soft_body(SoftBodyBullet *p_body);
+ void remove_soft_body(SoftBodyBullet *p_body);
+ void reload_collision_filters(SoftBodyBullet *p_body);
+
+ void add_constraint(ConstraintBullet *p_constraint, bool disableCollisionsBetweenLinkedBodies = false);
+ void remove_constraint(ConstraintBullet *p_constraint);
+
+ int get_num_collision_objects() const;
+ void remove_all_collision_objects();
+
+ BulletPhysicsDirectSpaceState *get_direct_state();
+
+ void set_debug_contacts(int p_amount) { contactDebug.resize(p_amount); }
+ _FORCE_INLINE_ bool is_debugging_contacts() const { return !contactDebug.empty(); }
+ _FORCE_INLINE_ void reset_debug_contact_count() {
+ contactDebugCount = 0;
+ }
+ _FORCE_INLINE_ void add_debug_contact(const Vector3 &p_contact) {
+ if (contactDebugCount < contactDebug.size()) contactDebug[contactDebugCount++] = p_contact;
+ }
+ _FORCE_INLINE_ Vector<Vector3> get_debug_contacts() { return contactDebug; }
+ _FORCE_INLINE_ int get_debug_contact_count() { return contactDebugCount; }
+
+ const Vector3 &get_gravity_direction() const { return gravityDirection; }
+ real_t get_gravity_magnitude() const { return gravityMagnitude; }
+
+ void update_gravity();
+
+ bool test_body_motion(RigidBodyBullet *p_body, const Transform &p_from, const Vector3 &p_motion, real_t p_margin, PhysicsServer::MotionResult *r_result);
+
+private:
+ void create_empty_world(bool p_create_soft_world);
+ void destroy_world();
+ void check_ghost_overlaps();
+ void check_body_collision();
+
+ bool recover_from_penetration(RigidBodyBullet *p_body, const btTransform &p_from, btScalar p_maxPenetrationDepth, btScalar p_depenetration_speed, btVector3 &out_recover_position);
+};
+#endif
diff --git a/modules/gdnative/gdnative/basis.cpp b/modules/gdnative/gdnative/basis.cpp
index b1327cdaef..28af93f942 100644
--- a/modules/gdnative/gdnative/basis.cpp
+++ b/modules/gdnative/gdnative/basis.cpp
@@ -172,7 +172,7 @@ void GDAPI godot_basis_new_with_euler_quat(godot_basis *r_dest, const godot_quat
}
// p_elements is a pointer to an array of 3 (!!) vector3
-void GDAPI godot_basis_get_elements(godot_basis *p_self, godot_vector3 *p_elements) {
+void GDAPI godot_basis_get_elements(const godot_basis *p_self, godot_vector3 *p_elements) {
const Basis *self = (const Basis *)p_self;
Vector3 *elements = (Vector3 *)p_elements;
elements[0] = self->elements[0];
diff --git a/modules/gdnative/gdnative/pool_arrays.cpp b/modules/gdnative/gdnative/pool_arrays.cpp
index 1393374da2..731e930908 100644
--- a/modules/gdnative/gdnative/pool_arrays.cpp
+++ b/modules/gdnative/gdnative/pool_arrays.cpp
@@ -106,6 +106,16 @@ void GDAPI godot_pool_byte_array_resize(godot_pool_byte_array *p_self, const god
self->resize(p_size);
}
+godot_pool_byte_array_read_access GDAPI *godot_pool_byte_array_read(const godot_pool_byte_array *p_self) {
+ const PoolVector<uint8_t> *self = (const PoolVector<uint8_t> *)p_self;
+ return (godot_pool_byte_array_read_access *)memnew(PoolVector<uint8_t>::Read(self->read()));
+}
+
+godot_pool_byte_array_write_access GDAPI *godot_pool_byte_array_write(godot_pool_byte_array *p_self) {
+ PoolVector<uint8_t> *self = (PoolVector<uint8_t> *)p_self;
+ return (godot_pool_byte_array_write_access *)memnew(PoolVector<uint8_t>::Write(self->write()));
+}
+
void GDAPI godot_pool_byte_array_set(godot_pool_byte_array *p_self, const godot_int p_idx, const uint8_t p_data) {
PoolVector<uint8_t> *self = (PoolVector<uint8_t> *)p_self;
self->set(p_idx, p_data);
@@ -185,6 +195,16 @@ void GDAPI godot_pool_int_array_resize(godot_pool_int_array *p_self, const godot
self->resize(p_size);
}
+godot_pool_int_array_read_access GDAPI *godot_pool_int_array_read(const godot_pool_int_array *p_self) {
+ const PoolVector<godot_int> *self = (const PoolVector<godot_int> *)p_self;
+ return (godot_pool_int_array_read_access *)memnew(PoolVector<godot_int>::Read(self->read()));
+}
+
+godot_pool_int_array_write_access GDAPI *godot_pool_int_array_write(godot_pool_int_array *p_self) {
+ PoolVector<godot_int> *self = (PoolVector<godot_int> *)p_self;
+ return (godot_pool_int_array_write_access *)memnew(PoolVector<godot_int>::Write(self->write()));
+}
+
void GDAPI godot_pool_int_array_set(godot_pool_int_array *p_self, const godot_int p_idx, const godot_int p_data) {
PoolVector<godot_int> *self = (PoolVector<godot_int> *)p_self;
self->set(p_idx, p_data);
@@ -260,10 +280,20 @@ void GDAPI godot_pool_real_array_remove(godot_pool_real_array *p_self, const god
}
void GDAPI godot_pool_real_array_resize(godot_pool_real_array *p_self, const godot_int p_size) {
- PoolVector<godot_int> *self = (PoolVector<godot_int> *)p_self;
+ PoolVector<godot_real> *self = (PoolVector<godot_real> *)p_self;
self->resize(p_size);
}
+godot_pool_real_array_read_access GDAPI *godot_pool_real_array_read(const godot_pool_real_array *p_self) {
+ const PoolVector<godot_real> *self = (const PoolVector<godot_real> *)p_self;
+ return (godot_pool_real_array_read_access *)memnew(PoolVector<godot_real>::Read(self->read()));
+}
+
+godot_pool_int_array_write_access GDAPI *godot_pool_real_array_write(godot_pool_real_array *p_self) {
+ PoolVector<godot_real> *self = (PoolVector<godot_real> *)p_self;
+ return (godot_pool_real_array_write_access *)memnew(PoolVector<godot_real>::Write(self->write()));
+}
+
void GDAPI godot_pool_real_array_set(godot_pool_real_array *p_self, const godot_int p_idx, const godot_real p_data) {
PoolVector<godot_real> *self = (PoolVector<godot_real> *)p_self;
self->set(p_idx, p_data);
@@ -346,6 +376,16 @@ void GDAPI godot_pool_string_array_resize(godot_pool_string_array *p_self, const
self->resize(p_size);
}
+godot_pool_string_array_read_access GDAPI *godot_pool_string_array_read(const godot_pool_string_array *p_self) {
+ const PoolVector<String> *self = (const PoolVector<String> *)p_self;
+ return (godot_pool_string_array_read_access *)memnew(PoolVector<String>::Read(self->read()));
+}
+
+godot_pool_string_array_write_access GDAPI *godot_pool_string_array_write(godot_pool_string_array *p_self) {
+ PoolVector<String> *self = (PoolVector<String> *)p_self;
+ return (godot_pool_string_array_write_access *)memnew(PoolVector<String>::Write(self->write()));
+}
+
void GDAPI godot_pool_string_array_set(godot_pool_string_array *p_self, const godot_int p_idx, const godot_string *p_data) {
PoolVector<String> *self = (PoolVector<String> *)p_self;
String &s = *(String *)p_data;
@@ -433,6 +473,16 @@ void GDAPI godot_pool_vector2_array_resize(godot_pool_vector2_array *p_self, con
self->resize(p_size);
}
+godot_pool_vector2_array_read_access GDAPI *godot_pool_vector2_array_read(const godot_pool_vector2_array *p_self) {
+ const PoolVector<Vector2> *self = (const PoolVector<Vector2> *)p_self;
+ return (godot_pool_vector2_array_read_access *)memnew(PoolVector<Vector2>::Read(self->read()));
+}
+
+godot_pool_vector2_array_write_access GDAPI *godot_pool_vector2_array_write(godot_pool_vector2_array *p_self) {
+ PoolVector<Vector2> *self = (PoolVector<Vector2> *)p_self;
+ return (godot_pool_vector2_array_write_access *)memnew(PoolVector<Vector2>::Write(self->write()));
+}
+
void GDAPI godot_pool_vector2_array_set(godot_pool_vector2_array *p_self, const godot_int p_idx, const godot_vector2 *p_data) {
PoolVector<Vector2> *self = (PoolVector<Vector2> *)p_self;
Vector2 &s = *(Vector2 *)p_data;
@@ -519,6 +569,16 @@ void GDAPI godot_pool_vector3_array_resize(godot_pool_vector3_array *p_self, con
self->resize(p_size);
}
+godot_pool_vector3_array_read_access GDAPI *godot_pool_vector3_array_read(const godot_pool_vector3_array *p_self) {
+ const PoolVector<Vector3> *self = (const PoolVector<Vector3> *)p_self;
+ return (godot_pool_vector3_array_read_access *)memnew(PoolVector<Vector3>::Read(self->read()));
+}
+
+godot_pool_vector3_array_write_access GDAPI *godot_pool_vector3_array_write(godot_pool_vector3_array *p_self) {
+ PoolVector<Vector3> *self = (PoolVector<Vector3> *)p_self;
+ return (godot_pool_vector3_array_write_access *)memnew(PoolVector<Vector3>::Write(self->write()));
+}
+
void GDAPI godot_pool_vector3_array_set(godot_pool_vector3_array *p_self, const godot_int p_idx, const godot_vector3 *p_data) {
PoolVector<Vector3> *self = (PoolVector<Vector3> *)p_self;
Vector3 &s = *(Vector3 *)p_data;
@@ -605,6 +665,16 @@ void GDAPI godot_pool_color_array_resize(godot_pool_color_array *p_self, const g
self->resize(p_size);
}
+godot_pool_color_array_read_access GDAPI *godot_pool_color_array_read(const godot_pool_color_array *p_self) {
+ const PoolVector<Color> *self = (const PoolVector<Color> *)p_self;
+ return (godot_pool_color_array_read_access *)memnew(PoolVector<Color>::Read(self->read()));
+}
+
+godot_pool_color_array_write_access GDAPI *godot_pool_color_array_write(godot_pool_color_array *p_self) {
+ PoolVector<Color> *self = (PoolVector<Color> *)p_self;
+ return (godot_pool_color_array_write_access *)memnew(PoolVector<Color>::Write(self->write()));
+}
+
void GDAPI godot_pool_color_array_set(godot_pool_color_array *p_self, const godot_int p_idx, const godot_color *p_data) {
PoolVector<Color> *self = (PoolVector<Color> *)p_self;
Color &s = *(Color *)p_data;
@@ -628,6 +698,196 @@ void GDAPI godot_pool_color_array_destroy(godot_pool_color_array *p_self) {
((PoolVector<Color> *)p_self)->~PoolVector();
}
+//
+// read accessor functions
+//
+
+const uint8_t GDAPI *godot_pool_byte_array_read_access_ptr(const godot_pool_byte_array_read_access *p_read) {
+ const PoolVector<uint8_t>::Read *read = (const PoolVector<uint8_t>::Read *)p_read;
+ return read->ptr();
+}
+void GDAPI godot_pool_byte_array_read_access_operator_assign(godot_pool_byte_array_read_access *p_read, godot_pool_byte_array_read_access *p_other) {
+ PoolVector<uint8_t>::Read *read = (PoolVector<uint8_t>::Read *)p_read;
+ PoolVector<uint8_t>::Read *other = (PoolVector<uint8_t>::Read *)p_other;
+ read->operator=(*other);
+}
+void GDAPI godot_pool_byte_array_read_access_destroy(godot_pool_byte_array_read_access *p_read) {
+ memdelete((PoolVector<uint8_t>::Read *)p_read);
+}
+
+const godot_int GDAPI *godot_pool_int_array_read_access_ptr(const godot_pool_int_array_read_access *p_read) {
+ const PoolVector<godot_int>::Read *read = (const PoolVector<godot_int>::Read *)p_read;
+ return read->ptr();
+}
+void GDAPI godot_pool_int_array_read_access_operator_assign(godot_pool_int_array_read_access *p_read, godot_pool_int_array_read_access *p_other) {
+ PoolVector<godot_int>::Read *read = (PoolVector<godot_int>::Read *)p_read;
+ PoolVector<godot_int>::Read *other = (PoolVector<godot_int>::Read *)p_other;
+ read->operator=(*other);
+}
+void GDAPI godot_pool_int_array_read_access_destroy(godot_pool_int_array_read_access *p_read) {
+ memdelete((PoolVector<godot_int>::Read *)p_read);
+}
+
+const godot_real GDAPI *godot_pool_real_array_read_access_ptr(const godot_pool_real_array_read_access *p_read) {
+ const PoolVector<godot_real>::Read *read = (const PoolVector<godot_real>::Read *)p_read;
+ return read->ptr();
+}
+void GDAPI godot_pool_real_array_read_access_operator_assign(godot_pool_real_array_read_access *p_read, godot_pool_real_array_read_access *p_other) {
+ PoolVector<godot_real>::Read *read = (PoolVector<godot_real>::Read *)p_read;
+ PoolVector<godot_real>::Read *other = (PoolVector<godot_real>::Read *)p_other;
+ read->operator=(*other);
+}
+void GDAPI godot_pool_real_array_read_access_destroy(godot_pool_real_array_read_access *p_read) {
+ memdelete((PoolVector<godot_real>::Read *)p_read);
+}
+
+const godot_string GDAPI *godot_pool_string_array_read_access_ptr(const godot_pool_string_array_read_access *p_read) {
+ const PoolVector<String>::Read *read = (const PoolVector<String>::Read *)p_read;
+ return (const godot_string *)read->ptr();
+}
+void GDAPI godot_pool_string_array_read_access_operator_assign(godot_pool_string_array_read_access *p_read, godot_pool_string_array_read_access *p_other) {
+ PoolVector<String>::Read *read = (PoolVector<String>::Read *)p_read;
+ PoolVector<String>::Read *other = (PoolVector<String>::Read *)p_other;
+ read->operator=(*other);
+}
+void GDAPI godot_pool_string_array_read_access_destroy(godot_pool_string_array_read_access *p_read) {
+ memdelete((PoolVector<String>::Read *)p_read);
+}
+
+const godot_vector2 GDAPI *godot_pool_vector2_array_read_access_ptr(const godot_pool_vector2_array_read_access *p_read) {
+ const PoolVector<Vector2>::Read *read = (const PoolVector<Vector2>::Read *)p_read;
+ return (const godot_vector2 *)read->ptr();
+}
+void GDAPI godot_pool_vector2_array_read_access_operator_assign(godot_pool_vector2_array_read_access *p_read, godot_pool_vector2_array_read_access *p_other) {
+ PoolVector<Vector2>::Read *read = (PoolVector<Vector2>::Read *)p_read;
+ PoolVector<Vector2>::Read *other = (PoolVector<Vector2>::Read *)p_other;
+ read->operator=(*other);
+}
+void GDAPI godot_pool_vector2_array_read_access_destroy(godot_pool_vector2_array_read_access *p_read) {
+ memdelete((PoolVector<Vector2>::Read *)p_read);
+}
+
+const godot_vector3 GDAPI *godot_pool_vector3_array_read_access_ptr(const godot_pool_vector3_array_read_access *p_read) {
+ const PoolVector<Vector3>::Read *read = (const PoolVector<Vector3>::Read *)p_read;
+ return (const godot_vector3 *)read->ptr();
+}
+void GDAPI godot_pool_vector3_array_read_access_operator_assign(godot_pool_vector3_array_read_access *p_read, godot_pool_vector3_array_read_access *p_other) {
+ PoolVector<Vector3>::Read *read = (PoolVector<Vector3>::Read *)p_read;
+ PoolVector<Vector3>::Read *other = (PoolVector<Vector3>::Read *)p_other;
+ read->operator=(*other);
+}
+void GDAPI godot_pool_vector3_array_read_access_destroy(godot_pool_vector3_array_read_access *p_read) {
+ memdelete((PoolVector<Vector2>::Read *)p_read);
+}
+
+const godot_color GDAPI *godot_pool_color_array_read_access_ptr(const godot_pool_color_array_read_access *p_read) {
+ const PoolVector<Color>::Read *read = (const PoolVector<Color>::Read *)p_read;
+ return (const godot_color *)read->ptr();
+}
+void GDAPI godot_pool_color_array_read_access_operator_assign(godot_pool_color_array_read_access *p_read, godot_pool_color_array_read_access *p_other) {
+ PoolVector<Color>::Read *read = (PoolVector<Color>::Read *)p_read;
+ PoolVector<Color>::Read *other = (PoolVector<Color>::Read *)p_other;
+ read->operator=(*other);
+}
+void GDAPI godot_pool_color_array_read_access_destroy(godot_pool_color_array_read_access *p_read) {
+ memdelete((PoolVector<Color>::Read *)p_read);
+}
+
+//
+// write accessor functions
+//
+
+uint8_t GDAPI *godot_pool_byte_array_write_access_ptr(const godot_pool_byte_array_write_access *p_write) {
+ PoolVector<uint8_t>::Write *write = (PoolVector<uint8_t>::Write *)p_write;
+ return write->ptr();
+}
+void GDAPI godot_pool_byte_array_write_access_operator_assign(godot_pool_byte_array_write_access *p_write, godot_pool_byte_array_write_access *p_other) {
+ PoolVector<uint8_t>::Write *write = (PoolVector<uint8_t>::Write *)p_write;
+ PoolVector<uint8_t>::Write *other = (PoolVector<uint8_t>::Write *)p_other;
+ write->operator=(*other);
+}
+void GDAPI godot_pool_byte_array_write_access_destroy(godot_pool_byte_array_write_access *p_write) {
+ memdelete((PoolVector<uint8_t>::Write *)p_write);
+}
+
+godot_int GDAPI *godot_pool_int_array_write_access_ptr(const godot_pool_int_array_write_access *p_write) {
+ PoolVector<godot_int>::Write *write = (PoolVector<godot_int>::Write *)p_write;
+ return write->ptr();
+}
+void GDAPI godot_pool_int_array_write_access_operator_assign(godot_pool_int_array_write_access *p_write, godot_pool_int_array_write_access *p_other) {
+ PoolVector<godot_int>::Write *write = (PoolVector<godot_int>::Write *)p_write;
+ PoolVector<godot_int>::Write *other = (PoolVector<godot_int>::Write *)p_other;
+ write->operator=(*other);
+}
+void GDAPI godot_pool_int_array_write_access_destroy(godot_pool_int_array_write_access *p_write) {
+ memdelete((PoolVector<godot_int>::Write *)p_write);
+}
+
+godot_real GDAPI *godot_pool_real_array_write_access_ptr(const godot_pool_real_array_write_access *p_write) {
+ PoolVector<godot_real>::Write *write = (PoolVector<godot_real>::Write *)p_write;
+ return write->ptr();
+}
+void GDAPI godot_pool_real_array_write_access_operator_assign(godot_pool_real_array_write_access *p_write, godot_pool_real_array_write_access *p_other) {
+ PoolVector<godot_real>::Write *write = (PoolVector<godot_real>::Write *)p_write;
+ PoolVector<godot_real>::Write *other = (PoolVector<godot_real>::Write *)p_other;
+ write->operator=(*other);
+}
+void GDAPI godot_pool_real_array_write_access_destroy(godot_pool_real_array_write_access *p_write) {
+ memdelete((PoolVector<godot_real>::Write *)p_write);
+}
+
+godot_string GDAPI *godot_pool_string_array_write_access_ptr(const godot_pool_string_array_write_access *p_write) {
+ PoolVector<String>::Write *write = (PoolVector<String>::Write *)p_write;
+ return (godot_string *)write->ptr();
+}
+void GDAPI godot_pool_string_array_write_access_operator_assign(godot_pool_string_array_write_access *p_write, godot_pool_string_array_write_access *p_other) {
+ PoolVector<String>::Write *write = (PoolVector<String>::Write *)p_write;
+ PoolVector<String>::Write *other = (PoolVector<String>::Write *)p_other;
+ write->operator=(*other);
+}
+void GDAPI godot_pool_string_array_write_access_destroy(godot_pool_string_array_write_access *p_write) {
+ memdelete((PoolVector<String>::Write *)p_write);
+}
+
+godot_vector2 GDAPI *godot_pool_vector2_array_write_access_ptr(const godot_pool_vector2_array_write_access *p_write) {
+ PoolVector<Vector2>::Write *write = (PoolVector<Vector2>::Write *)p_write;
+ return (godot_vector2 *)write->ptr();
+}
+void GDAPI godot_pool_vector2_array_write_access_operator_assign(godot_pool_vector2_array_write_access *p_write, godot_pool_vector2_array_write_access *p_other) {
+ PoolVector<Vector2>::Write *write = (PoolVector<Vector2>::Write *)p_write;
+ PoolVector<Vector2>::Write *other = (PoolVector<Vector2>::Write *)p_other;
+ write->operator=(*other);
+}
+void GDAPI godot_pool_vector2_array_write_access_destroy(godot_pool_vector2_array_write_access *p_write) {
+ memdelete((PoolVector<Vector2>::Write *)p_write);
+}
+
+godot_vector3 GDAPI *godot_pool_vector3_array_write_access_ptr(const godot_pool_vector3_array_write_access *p_write) {
+ PoolVector<Vector3>::Write *write = (PoolVector<Vector3>::Write *)p_write;
+ return (godot_vector3 *)write->ptr();
+}
+void GDAPI godot_pool_vector3_array_write_access_operator_assign(godot_pool_vector3_array_write_access *p_write, godot_pool_vector3_array_write_access *p_other) {
+ PoolVector<Vector3>::Write *write = (PoolVector<Vector3>::Write *)p_write;
+ PoolVector<Vector3>::Write *other = (PoolVector<Vector3>::Write *)p_other;
+ write->operator=(*other);
+}
+void GDAPI godot_pool_vector3_array_write_access_destroy(godot_pool_vector3_array_write_access *p_write) {
+ memdelete((PoolVector<Vector3>::Write *)p_write);
+}
+
+godot_color GDAPI *godot_pool_color_array_write_access_ptr(const godot_pool_color_array_write_access *p_write) {
+ PoolVector<Color>::Write *write = (PoolVector<Color>::Write *)p_write;
+ return (godot_color *)write->ptr();
+}
+void GDAPI godot_pool_color_array_write_access_operator_assign(godot_pool_color_array_write_access *p_write, godot_pool_color_array_write_access *p_other) {
+ PoolVector<Color>::Write *write = (PoolVector<Color>::Write *)p_write;
+ PoolVector<Color>::Write *other = (PoolVector<Color>::Write *)p_other;
+ write->operator=(*other);
+}
+void GDAPI godot_pool_color_array_write_access_destroy(godot_pool_color_array_write_access *p_write) {
+ memdelete((PoolVector<Color>::Write *)p_write);
+}
+
#ifdef __cplusplus
}
#endif
diff --git a/modules/gdnative/gdnative/string.cpp b/modules/gdnative/gdnative/string.cpp
index 905c513d9d..619003083d 100644
--- a/modules/gdnative/gdnative/string.cpp
+++ b/modules/gdnative/gdnative/string.cpp
@@ -65,11 +65,20 @@ void GDAPI godot_string_new_unicode_data(godot_string *r_dest, const wchar_t *p_
void GDAPI godot_string_get_data(const godot_string *p_self, char *p_dest, int *p_size) {
String *self = (String *)p_self;
- if (p_size != NULL) {
- *p_size = self->utf8().length();
- }
- if (p_dest != NULL) {
- memcpy(p_dest, self->utf8().get_data(), *p_size);
+
+ if (p_size) {
+ // we have a length pointer, that means we either want to know
+ // the length or want to write *p_size bytes into a buffer
+
+ CharString utf8_string = self->utf8();
+
+ int len = utf8_string.length();
+
+ if (p_dest) {
+ memcpy(p_dest, utf8_string.get_data(), *p_size);
+ } else {
+ *p_size = len;
+ }
}
}
@@ -78,6 +87,11 @@ wchar_t GDAPI *godot_string_operator_index(godot_string *p_self, const godot_int
return &(self->operator[](p_idx));
}
+wchar_t GDAPI godot_string_operator_index_const(const godot_string *p_self, const godot_int p_idx) {
+ const String *self = (const String *)p_self;
+ return self->operator[](p_idx);
+}
+
const char GDAPI *godot_string_c_str(const godot_string *p_self) {
const String *self = (const String *)p_self;
return self->utf8().get_data();
diff --git a/modules/gdnative/gdnative_api.json b/modules/gdnative/gdnative_api.json
index 31b021b751..ac0293172d 100644
--- a/modules/gdnative/gdnative_api.json
+++ b/modules/gdnative/gdnative_api.json
@@ -847,7 +847,7 @@
"name": "godot_basis_get_elements",
"return_type": "void",
"arguments": [
- ["godot_basis *", "p_self"],
+ ["const godot_basis *", "p_self"],
["godot_vector3 *", "p_elements"]
]
},
@@ -1296,6 +1296,20 @@
]
},
{
+ "name": "godot_pool_byte_array_read",
+ "return_type": "godot_pool_byte_array_read_access *",
+ "arguments": [
+ ["const godot_pool_byte_array *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_pool_byte_array_write",
+ "return_type": "godot_pool_byte_array_write_access *",
+ "arguments": [
+ ["godot_pool_byte_array *", "p_self"]
+ ]
+ },
+ {
"name": "godot_pool_byte_array_set",
"return_type": "void",
"arguments": [
@@ -1406,6 +1420,20 @@
]
},
{
+ "name": "godot_pool_int_array_read",
+ "return_type": "godot_pool_int_array_read_access *",
+ "arguments": [
+ ["const godot_pool_int_array *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_pool_int_array_write",
+ "return_type": "godot_pool_int_array_write_access *",
+ "arguments": [
+ ["godot_pool_int_array *", "p_self"]
+ ]
+ },
+ {
"name": "godot_pool_int_array_set",
"return_type": "void",
"arguments": [
@@ -1516,6 +1544,20 @@
]
},
{
+ "name": "godot_pool_real_array_read",
+ "return_type": "godot_pool_real_array_read_access *",
+ "arguments": [
+ ["const godot_pool_real_array *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_pool_real_array_write",
+ "return_type": "godot_pool_real_array_write_access *",
+ "arguments": [
+ ["godot_pool_real_array *", "p_self"]
+ ]
+ },
+ {
"name": "godot_pool_real_array_set",
"return_type": "void",
"arguments": [
@@ -1626,6 +1668,20 @@
]
},
{
+ "name": "godot_pool_string_array_read",
+ "return_type": "godot_pool_string_array_read_access *",
+ "arguments": [
+ ["const godot_pool_string_array *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_pool_string_array_write",
+ "return_type": "godot_pool_string_array_write_access *",
+ "arguments": [
+ ["godot_pool_string_array *", "p_self"]
+ ]
+ },
+ {
"name": "godot_pool_string_array_set",
"return_type": "void",
"arguments": [
@@ -1736,6 +1792,20 @@
]
},
{
+ "name": "godot_pool_vector2_array_read",
+ "return_type": "godot_pool_vector2_array_read_access *",
+ "arguments": [
+ ["const godot_pool_vector2_array *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_pool_vector2_array_write",
+ "return_type": "godot_pool_vector2_array_write_access *",
+ "arguments": [
+ ["godot_pool_vector2_array *", "p_self"]
+ ]
+ },
+ {
"name": "godot_pool_vector2_array_set",
"return_type": "void",
"arguments": [
@@ -1846,6 +1916,20 @@
]
},
{
+ "name": "godot_pool_vector3_array_read",
+ "return_type": "godot_pool_vector3_array_read_access *",
+ "arguments": [
+ ["const godot_pool_vector3_array *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_pool_vector3_array_write",
+ "return_type": "godot_pool_vector3_array_write_access *",
+ "arguments": [
+ ["godot_pool_vector3_array *", "p_self"]
+ ]
+ },
+ {
"name": "godot_pool_vector3_array_set",
"return_type": "void",
"arguments": [
@@ -1956,6 +2040,20 @@
]
},
{
+ "name": "godot_pool_color_array_read",
+ "return_type": "godot_pool_color_array_read_access *",
+ "arguments": [
+ ["const godot_pool_color_array *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_pool_color_array_write",
+ "return_type": "godot_pool_color_array_write_access *",
+ "arguments": [
+ ["godot_pool_color_array *", "p_self"]
+ ]
+ },
+ {
"name": "godot_pool_color_array_set",
"return_type": "void",
"arguments": [
@@ -1987,6 +2085,314 @@
]
},
{
+ "name": "godot_pool_byte_array_read_access_ptr",
+ "return_type": "const uint8_t *",
+ "arguments": [
+ ["const godot_pool_byte_array_read_access *", "p_read"]
+ ]
+ },
+ {
+ "name": "godot_pool_byte_array_read_access_operator_assign",
+ "return_type": "void",
+ "arguments": [
+ ["godot_pool_byte_array_read_access *", "p_read"],
+ ["godot_pool_byte_array_read_access *", "p_other"]
+ ]
+ },
+ {
+ "name": "godot_pool_byte_array_read_access_destroy",
+ "return_type": "void",
+ "arguments": [
+ ["godot_pool_byte_array_read_access *", "p_read"]
+ ]
+ },
+ {
+ "name": "godot_pool_int_array_read_access_ptr",
+ "return_type": "const godot_int *",
+ "arguments": [
+ ["const godot_pool_int_array_read_access *", "p_read"]
+ ]
+ },
+ {
+ "name": "godot_pool_int_array_read_access_operator_assign",
+ "return_type": "void",
+ "arguments": [
+ ["godot_pool_int_array_read_access *", "p_read"],
+ ["godot_pool_int_array_read_access *", "p_other"]
+ ]
+ },
+ {
+ "name": "godot_pool_int_array_read_access_destroy",
+ "return_type": "void",
+ "arguments": [
+ ["godot_pool_int_array_read_access *", "p_read"]
+ ]
+ },
+ {
+ "name": "godot_pool_real_array_read_access_ptr",
+ "return_type": "const godot_real *",
+ "arguments": [
+ ["const godot_pool_real_array_read_access *", "p_read"]
+ ]
+ },
+ {
+ "name": "godot_pool_real_array_read_access_operator_assign",
+ "return_type": "void",
+ "arguments": [
+ ["godot_pool_real_array_read_access *", "p_read"],
+ ["godot_pool_real_array_read_access *", "p_other"]
+ ]
+ },
+ {
+ "name": "godot_pool_real_array_read_access_destroy",
+ "return_type": "void",
+ "arguments": [
+ ["godot_pool_real_array_read_access *", "p_read"]
+ ]
+ },
+ {
+ "name": "godot_pool_string_array_read_access_ptr",
+ "return_type": "const godot_string *",
+ "arguments": [
+ ["const godot_pool_string_array_read_access *", "p_read"]
+ ]
+ },
+ {
+ "name": "godot_pool_string_array_read_access_operator_assign",
+ "return_type": "void",
+ "arguments": [
+ ["godot_pool_string_array_read_access *", "p_read"],
+ ["godot_pool_string_array_read_access *", "p_other"]
+ ]
+ },
+ {
+ "name": "godot_pool_string_array_read_access_destroy",
+ "return_type": "void",
+ "arguments": [
+ ["godot_pool_string_array_read_access *", "p_read"]
+ ]
+ },
+ {
+ "name": "godot_pool_vector2_array_read_access_ptr",
+ "return_type": "const godot_vector2 *",
+ "arguments": [
+ ["const godot_pool_vector2_array_read_access *", "p_read"]
+ ]
+ },
+ {
+ "name": "godot_pool_vector2_array_read_access_operator_assign",
+ "return_type": "void",
+ "arguments": [
+ ["godot_pool_vector2_array_read_access *", "p_read"],
+ ["godot_pool_vector2_array_read_access *", "p_other"]
+ ]
+ },
+ {
+ "name": "godot_pool_vector2_array_read_access_destroy",
+ "return_type": "void",
+ "arguments": [
+ ["godot_pool_vector2_array_read_access *", "p_read"]
+ ]
+ },
+ {
+ "name": "godot_pool_vector3_array_read_access_ptr",
+ "return_type": "const godot_vector3 *",
+ "arguments": [
+ ["const godot_pool_vector3_array_read_access *", "p_read"]
+ ]
+ },
+ {
+ "name": "godot_pool_vector3_array_read_access_operator_assign",
+ "return_type": "void",
+ "arguments": [
+ ["godot_pool_vector3_array_read_access *", "p_read"],
+ ["godot_pool_vector3_array_read_access *", "p_other"]
+ ]
+ },
+ {
+ "name": "godot_pool_vector3_array_read_access_destroy",
+ "return_type": "void",
+ "arguments": [
+ ["godot_pool_vector3_array_read_access *", "p_read"]
+ ]
+ },
+ {
+ "name": "godot_pool_color_array_read_access_ptr",
+ "return_type": "const godot_color *",
+ "arguments": [
+ ["const godot_pool_color_array_read_access *", "p_read"]
+ ]
+ },
+ {
+ "name": "godot_pool_color_array_read_access_operator_assign",
+ "return_type": "void",
+ "arguments": [
+ ["godot_pool_color_array_read_access *", "p_read"],
+ ["godot_pool_color_array_read_access *", "p_other"]
+ ]
+ },
+ {
+ "name": "godot_pool_color_array_read_access_destroy",
+ "return_type": "void",
+ "arguments": [
+ ["godot_pool_color_array_read_access *", "p_read"]
+ ]
+ },
+ {
+ "name": "godot_pool_byte_array_write_access_ptr",
+ "return_type": "uint8_t *",
+ "arguments": [
+ ["const godot_pool_byte_array_write_access *", "p_write"]
+ ]
+ },
+ {
+ "name": "godot_pool_byte_array_write_access_operator_assign",
+ "return_type": "void",
+ "arguments": [
+ ["godot_pool_byte_array_write_access *", "p_write"],
+ ["godot_pool_byte_array_write_access *", "p_other"]
+ ]
+ },
+ {
+ "name": "godot_pool_byte_array_write_access_destroy",
+ "return_type": "void",
+ "arguments": [
+ ["godot_pool_byte_array_write_access *", "p_write"]
+ ]
+ },
+ {
+ "name": "godot_pool_int_array_write_access_ptr",
+ "return_type": "godot_int *",
+ "arguments": [
+ ["const godot_pool_int_array_write_access *", "p_write"]
+ ]
+ },
+ {
+ "name": "godot_pool_int_array_write_access_operator_assign",
+ "return_type": "void",
+ "arguments": [
+ ["godot_pool_int_array_write_access *", "p_write"],
+ ["godot_pool_int_array_write_access *", "p_other"]
+ ]
+ },
+ {
+ "name": "godot_pool_int_array_write_access_destroy",
+ "return_type": "void",
+ "arguments": [
+ ["godot_pool_int_array_write_access *", "p_write"]
+ ]
+ },
+ {
+ "name": "godot_pool_real_array_write_access_ptr",
+ "return_type": "godot_real *",
+ "arguments": [
+ ["const godot_pool_real_array_write_access *", "p_write"]
+ ]
+ },
+ {
+ "name": "godot_pool_real_array_write_access_operator_assign",
+ "return_type": "void",
+ "arguments": [
+ ["godot_pool_real_array_write_access *", "p_write"],
+ ["godot_pool_real_array_write_access *", "p_other"]
+ ]
+ },
+ {
+ "name": "godot_pool_real_array_write_access_destroy",
+ "return_type": "void",
+ "arguments": [
+ ["godot_pool_real_array_write_access *", "p_write"]
+ ]
+ },
+ {
+ "name": "godot_pool_string_array_write_access_ptr",
+ "return_type": "godot_string *",
+ "arguments": [
+ ["const godot_pool_string_array_write_access *", "p_write"]
+ ]
+ },
+ {
+ "name": "godot_pool_string_array_write_access_operator_assign",
+ "return_type": "void",
+ "arguments": [
+ ["godot_pool_string_array_write_access *", "p_write"],
+ ["godot_pool_string_array_write_access *", "p_other"]
+ ]
+ },
+ {
+ "name": "godot_pool_string_array_write_access_destroy",
+ "return_type": "void",
+ "arguments": [
+ ["godot_pool_string_array_write_access *", "p_write"]
+ ]
+ },
+ {
+ "name": "godot_pool_vector2_array_write_access_ptr",
+ "return_type": "godot_vector2 *",
+ "arguments": [
+ ["const godot_pool_vector2_array_write_access *", "p_write"]
+ ]
+ },
+ {
+ "name": "godot_pool_vector2_array_write_access_operator_assign",
+ "return_type": "void",
+ "arguments": [
+ ["godot_pool_vector2_array_write_access *", "p_write"],
+ ["godot_pool_vector2_array_write_access *", "p_other"]
+ ]
+ },
+ {
+ "name": "godot_pool_vector2_array_write_access_destroy",
+ "return_type": "void",
+ "arguments": [
+ ["godot_pool_vector2_array_write_access *", "p_write"]
+ ]
+ },
+ {
+ "name": "godot_pool_vector3_array_write_access_ptr",
+ "return_type": "godot_vector3 *",
+ "arguments": [
+ ["const godot_pool_vector3_array_write_access *", "p_write"]
+ ]
+ },
+ {
+ "name": "godot_pool_vector3_array_write_access_operator_assign",
+ "return_type": "void",
+ "arguments": [
+ ["godot_pool_vector3_array_write_access *", "p_write"],
+ ["godot_pool_vector3_array_write_access *", "p_other"]
+ ]
+ },
+ {
+ "name": "godot_pool_vector3_array_write_access_destroy",
+ "return_type": "void",
+ "arguments": [
+ ["godot_pool_vector3_array_write_access *", "p_write"]
+ ]
+ },
+ {
+ "name": "godot_pool_color_array_write_access_ptr",
+ "return_type": "godot_color *",
+ "arguments": [
+ ["const godot_pool_color_array_write_access *", "p_write"]
+ ]
+ },
+ {
+ "name": "godot_pool_color_array_write_access_operator_assign",
+ "return_type": "void",
+ "arguments": [
+ ["godot_pool_color_array_write_access *", "p_write"],
+ ["godot_pool_color_array_write_access *", "p_other"]
+ ]
+ },
+ {
+ "name": "godot_pool_color_array_write_access_destroy",
+ "return_type": "void",
+ "arguments": [
+ ["godot_pool_color_array_write_access *", "p_write"]
+ ]
+ },
+ {
"name": "godot_array_new",
"return_type": "void",
"arguments": [
@@ -3927,6 +4333,14 @@
]
},
{
+ "name": "godot_string_operator_index_const",
+ "return_type": "wchar_t",
+ "arguments": [
+ ["const godot_string *", "p_self"],
+ ["const godot_int", "p_idx"]
+ ]
+ },
+ {
"name": "godot_string_c_str",
"return_type": "const char *",
"arguments": [
@@ -5308,6 +5722,13 @@
["godot_real", "p_value"],
["godot_bool", "p_can_be_negative"]
]
+ },
+ {
+ "name": "godot_arvr_get_controller_rumble",
+ "return_type": "godot_real",
+ "arguments": [
+ ["godot_int", "p_controller_id"]
+ ]
}
]
}
diff --git a/modules/gdnative/include/gdnative/basis.h b/modules/gdnative/include/gdnative/basis.h
index b86b1c17d8..49ca765a01 100644
--- a/modules/gdnative/include/gdnative/basis.h
+++ b/modules/gdnative/include/gdnative/basis.h
@@ -97,7 +97,7 @@ void GDAPI godot_basis_new(godot_basis *r_dest);
void GDAPI godot_basis_new_with_euler_quat(godot_basis *r_dest, const godot_quat *p_euler);
// p_elements is a pointer to an array of 3 (!!) vector3
-void GDAPI godot_basis_get_elements(godot_basis *p_self, godot_vector3 *p_elements);
+void GDAPI godot_basis_get_elements(const godot_basis *p_self, godot_vector3 *p_elements);
godot_vector3 GDAPI godot_basis_get_axis(const godot_basis *p_self, const godot_int p_axis);
diff --git a/modules/gdnative/include/gdnative/pool_arrays.h b/modules/gdnative/include/gdnative/pool_arrays.h
index 93181f2a6b..81500c9186 100644
--- a/modules/gdnative/include/gdnative/pool_arrays.h
+++ b/modules/gdnative/include/gdnative/pool_arrays.h
@@ -36,6 +36,38 @@ extern "C" {
#include <stdint.h>
+/////// Read Access
+
+#define GODOT_POOL_ARRAY_READ_ACCESS_SIZE 1
+
+typedef struct {
+ uint8_t _dont_touch_that[GODOT_POOL_ARRAY_READ_ACCESS_SIZE];
+} godot_pool_array_read_access;
+
+typedef godot_pool_array_read_access godot_pool_byte_array_read_access;
+typedef godot_pool_array_read_access godot_pool_int_array_read_access;
+typedef godot_pool_array_read_access godot_pool_real_array_read_access;
+typedef godot_pool_array_read_access godot_pool_string_array_read_access;
+typedef godot_pool_array_read_access godot_pool_vector2_array_read_access;
+typedef godot_pool_array_read_access godot_pool_vector3_array_read_access;
+typedef godot_pool_array_read_access godot_pool_color_array_read_access;
+
+/////// Write Access
+
+#define GODOT_POOL_ARRAY_WRITE_ACCESS_SIZE 1
+
+typedef struct {
+ uint8_t _dont_touch_that[GODOT_POOL_ARRAY_WRITE_ACCESS_SIZE];
+} godot_pool_array_write_access;
+
+typedef godot_pool_array_write_access godot_pool_byte_array_write_access;
+typedef godot_pool_array_write_access godot_pool_int_array_write_access;
+typedef godot_pool_array_write_access godot_pool_real_array_write_access;
+typedef godot_pool_array_write_access godot_pool_string_array_write_access;
+typedef godot_pool_array_write_access godot_pool_vector2_array_write_access;
+typedef godot_pool_array_write_access godot_pool_vector3_array_write_access;
+typedef godot_pool_array_write_access godot_pool_color_array_write_access;
+
/////// PoolByteArray
#define GODOT_POOL_BYTE_ARRAY_SIZE sizeof(void *)
@@ -149,6 +181,10 @@ void GDAPI godot_pool_byte_array_remove(godot_pool_byte_array *p_self, const god
void GDAPI godot_pool_byte_array_resize(godot_pool_byte_array *p_self, const godot_int p_size);
+godot_pool_byte_array_read_access GDAPI *godot_pool_byte_array_read(const godot_pool_byte_array *p_self);
+
+godot_pool_byte_array_write_access GDAPI *godot_pool_byte_array_write(godot_pool_byte_array *p_self);
+
void GDAPI godot_pool_byte_array_set(godot_pool_byte_array *p_self, const godot_int p_idx, const uint8_t p_data);
uint8_t GDAPI godot_pool_byte_array_get(const godot_pool_byte_array *p_self, const godot_int p_idx);
@@ -176,6 +212,10 @@ void GDAPI godot_pool_int_array_remove(godot_pool_int_array *p_self, const godot
void GDAPI godot_pool_int_array_resize(godot_pool_int_array *p_self, const godot_int p_size);
+godot_pool_int_array_read_access GDAPI *godot_pool_int_array_read(const godot_pool_int_array *p_self);
+
+godot_pool_int_array_write_access GDAPI *godot_pool_int_array_write(godot_pool_int_array *p_self);
+
void GDAPI godot_pool_int_array_set(godot_pool_int_array *p_self, const godot_int p_idx, const godot_int p_data);
godot_int GDAPI godot_pool_int_array_get(const godot_pool_int_array *p_self, const godot_int p_idx);
@@ -203,6 +243,10 @@ void GDAPI godot_pool_real_array_remove(godot_pool_real_array *p_self, const god
void GDAPI godot_pool_real_array_resize(godot_pool_real_array *p_self, const godot_int p_size);
+godot_pool_real_array_read_access GDAPI *godot_pool_real_array_read(const godot_pool_real_array *p_self);
+
+godot_pool_real_array_write_access GDAPI *godot_pool_real_array_write(godot_pool_real_array *p_self);
+
void GDAPI godot_pool_real_array_set(godot_pool_real_array *p_self, const godot_int p_idx, const godot_real p_data);
godot_real GDAPI godot_pool_real_array_get(const godot_pool_real_array *p_self, const godot_int p_idx);
@@ -230,6 +274,10 @@ void GDAPI godot_pool_string_array_remove(godot_pool_string_array *p_self, const
void GDAPI godot_pool_string_array_resize(godot_pool_string_array *p_self, const godot_int p_size);
+godot_pool_string_array_read_access GDAPI *godot_pool_string_array_read(const godot_pool_string_array *p_self);
+
+godot_pool_string_array_write_access GDAPI *godot_pool_string_array_write(godot_pool_string_array *p_self);
+
void GDAPI godot_pool_string_array_set(godot_pool_string_array *p_self, const godot_int p_idx, const godot_string *p_data);
godot_string GDAPI godot_pool_string_array_get(const godot_pool_string_array *p_self, const godot_int p_idx);
@@ -257,6 +305,10 @@ void GDAPI godot_pool_vector2_array_remove(godot_pool_vector2_array *p_self, con
void GDAPI godot_pool_vector2_array_resize(godot_pool_vector2_array *p_self, const godot_int p_size);
+godot_pool_vector2_array_read_access GDAPI *godot_pool_vector2_array_read(const godot_pool_vector2_array *p_self);
+
+godot_pool_vector2_array_write_access GDAPI *godot_pool_vector2_array_write(godot_pool_vector2_array *p_self);
+
void GDAPI godot_pool_vector2_array_set(godot_pool_vector2_array *p_self, const godot_int p_idx, const godot_vector2 *p_data);
godot_vector2 GDAPI godot_pool_vector2_array_get(const godot_pool_vector2_array *p_self, const godot_int p_idx);
@@ -284,6 +336,10 @@ void GDAPI godot_pool_vector3_array_remove(godot_pool_vector3_array *p_self, con
void GDAPI godot_pool_vector3_array_resize(godot_pool_vector3_array *p_self, const godot_int p_size);
+godot_pool_vector3_array_read_access GDAPI *godot_pool_vector3_array_read(const godot_pool_vector3_array *p_self);
+
+godot_pool_vector3_array_write_access GDAPI *godot_pool_vector3_array_write(godot_pool_vector3_array *p_self);
+
void GDAPI godot_pool_vector3_array_set(godot_pool_vector3_array *p_self, const godot_int p_idx, const godot_vector3 *p_data);
godot_vector3 GDAPI godot_pool_vector3_array_get(const godot_pool_vector3_array *p_self, const godot_int p_idx);
@@ -311,6 +367,10 @@ void GDAPI godot_pool_color_array_remove(godot_pool_color_array *p_self, const g
void GDAPI godot_pool_color_array_resize(godot_pool_color_array *p_self, const godot_int p_size);
+godot_pool_color_array_read_access GDAPI *godot_pool_color_array_read(const godot_pool_color_array *p_self);
+
+godot_pool_color_array_write_access GDAPI *godot_pool_color_array_write(godot_pool_color_array *p_self);
+
void GDAPI godot_pool_color_array_set(godot_pool_color_array *p_self, const godot_int p_idx, const godot_color *p_data);
godot_color GDAPI godot_pool_color_array_get(const godot_pool_color_array *p_self, const godot_int p_idx);
@@ -318,6 +378,70 @@ godot_int GDAPI godot_pool_color_array_size(const godot_pool_color_array *p_self
void GDAPI godot_pool_color_array_destroy(godot_pool_color_array *p_self);
+//
+// read accessor functions
+//
+
+const uint8_t GDAPI *godot_pool_byte_array_read_access_ptr(const godot_pool_byte_array_read_access *p_read);
+void GDAPI godot_pool_byte_array_read_access_operator_assign(godot_pool_byte_array_read_access *p_read, godot_pool_byte_array_read_access *p_other);
+void GDAPI godot_pool_byte_array_read_access_destroy(godot_pool_byte_array_read_access *p_read);
+
+const godot_int GDAPI *godot_pool_int_array_read_access_ptr(const godot_pool_int_array_read_access *p_read);
+void GDAPI godot_pool_int_array_read_access_operator_assign(godot_pool_int_array_read_access *p_read, godot_pool_int_array_read_access *p_other);
+void GDAPI godot_pool_int_array_read_access_destroy(godot_pool_int_array_read_access *p_read);
+
+const godot_real GDAPI *godot_pool_real_array_read_access_ptr(const godot_pool_real_array_read_access *p_read);
+void GDAPI godot_pool_real_array_read_access_operator_assign(godot_pool_real_array_read_access *p_read, godot_pool_real_array_read_access *p_other);
+void GDAPI godot_pool_real_array_read_access_destroy(godot_pool_real_array_read_access *p_read);
+
+const godot_string GDAPI *godot_pool_string_array_read_access_ptr(const godot_pool_string_array_read_access *p_read);
+void GDAPI godot_pool_string_array_read_access_operator_assign(godot_pool_string_array_read_access *p_read, godot_pool_string_array_read_access *p_other);
+void GDAPI godot_pool_string_array_read_access_destroy(godot_pool_string_array_read_access *p_read);
+
+const godot_vector2 GDAPI *godot_pool_vector2_array_read_access_ptr(const godot_pool_vector2_array_read_access *p_read);
+void GDAPI godot_pool_vector2_array_read_access_operator_assign(godot_pool_vector2_array_read_access *p_read, godot_pool_vector2_array_read_access *p_other);
+void GDAPI godot_pool_vector2_array_read_access_destroy(godot_pool_vector2_array_read_access *p_read);
+
+const godot_vector3 GDAPI *godot_pool_vector3_array_read_access_ptr(const godot_pool_vector3_array_read_access *p_read);
+void GDAPI godot_pool_vector3_array_read_access_operator_assign(godot_pool_vector3_array_read_access *p_read, godot_pool_vector3_array_read_access *p_other);
+void GDAPI godot_pool_vector3_array_read_access_destroy(godot_pool_vector3_array_read_access *p_read);
+
+const godot_color GDAPI *godot_pool_color_array_read_access_ptr(const godot_pool_color_array_read_access *p_read);
+void GDAPI godot_pool_color_array_read_access_operator_assign(godot_pool_color_array_read_access *p_read, godot_pool_color_array_read_access *p_other);
+void GDAPI godot_pool_color_array_read_access_destroy(godot_pool_color_array_read_access *p_read);
+
+//
+// write accessor functions
+//
+
+uint8_t GDAPI *godot_pool_byte_array_write_access_ptr(const godot_pool_byte_array_write_access *p_write);
+void GDAPI godot_pool_byte_array_write_access_operator_assign(godot_pool_byte_array_write_access *p_write, godot_pool_byte_array_write_access *p_other);
+void GDAPI godot_pool_byte_array_write_access_destroy(godot_pool_byte_array_write_access *p_write);
+
+godot_int GDAPI *godot_pool_int_array_write_access_ptr(const godot_pool_int_array_write_access *p_write);
+void GDAPI godot_pool_int_array_write_access_operator_assign(godot_pool_int_array_write_access *p_write, godot_pool_int_array_write_access *p_other);
+void GDAPI godot_pool_int_array_write_access_destroy(godot_pool_int_array_write_access *p_write);
+
+godot_real GDAPI *godot_pool_real_array_write_access_ptr(const godot_pool_real_array_write_access *p_write);
+void GDAPI godot_pool_real_array_write_access_operator_assign(godot_pool_real_array_write_access *p_write, godot_pool_real_array_write_access *p_other);
+void GDAPI godot_pool_real_array_write_access_destroy(godot_pool_real_array_write_access *p_write);
+
+godot_string GDAPI *godot_pool_string_array_write_access_ptr(const godot_pool_string_array_write_access *p_write);
+void GDAPI godot_pool_string_array_write_access_operator_assign(godot_pool_string_array_write_access *p_write, godot_pool_string_array_write_access *p_other);
+void GDAPI godot_pool_string_array_write_access_destroy(godot_pool_string_array_write_access *p_write);
+
+godot_vector2 GDAPI *godot_pool_vector2_array_write_access_ptr(const godot_pool_vector2_array_write_access *p_write);
+void GDAPI godot_pool_vector2_array_write_access_operator_assign(godot_pool_vector2_array_write_access *p_write, godot_pool_vector2_array_write_access *p_other);
+void GDAPI godot_pool_vector2_array_write_access_destroy(godot_pool_vector2_array_write_access *p_write);
+
+godot_vector3 GDAPI *godot_pool_vector3_array_write_access_ptr(const godot_pool_vector3_array_write_access *p_write);
+void GDAPI godot_pool_vector3_array_write_access_operator_assign(godot_pool_vector3_array_write_access *p_write, godot_pool_vector3_array_write_access *p_other);
+void GDAPI godot_pool_vector3_array_write_access_destroy(godot_pool_vector3_array_write_access *p_write);
+
+godot_color GDAPI *godot_pool_color_array_write_access_ptr(const godot_pool_color_array_write_access *p_write);
+void GDAPI godot_pool_color_array_write_access_operator_assign(godot_pool_color_array_write_access *p_write, godot_pool_color_array_write_access *p_other);
+void GDAPI godot_pool_color_array_write_access_destroy(godot_pool_color_array_write_access *p_write);
+
#ifdef __cplusplus
}
#endif
diff --git a/modules/gdnative/include/gdnative/string.h b/modules/gdnative/include/gdnative/string.h
index f30fdb8dc7..29510313c9 100644
--- a/modules/gdnative/include/gdnative/string.h
+++ b/modules/gdnative/include/gdnative/string.h
@@ -66,6 +66,7 @@ void GDAPI godot_string_new_unicode_data(godot_string *r_dest, const wchar_t *p_
void GDAPI godot_string_get_data(const godot_string *p_self, char *p_dest, int *p_size);
wchar_t GDAPI *godot_string_operator_index(godot_string *p_self, const godot_int p_idx);
+wchar_t GDAPI godot_string_operator_index_const(const godot_string *p_self, const godot_int p_idx);
const char GDAPI *godot_string_c_str(const godot_string *p_self);
const wchar_t GDAPI *godot_string_unicode_str(const godot_string *p_self);
diff --git a/modules/gdnative/include/nativearvr/godot_nativearvr.h b/modules/gdnative/include/nativearvr/godot_nativearvr.h
index 1a8970d396..a4f4d6a9f1 100644
--- a/modules/gdnative/include/nativearvr/godot_nativearvr.h
+++ b/modules/gdnative/include/nativearvr/godot_nativearvr.h
@@ -70,6 +70,7 @@ void GDAPI godot_arvr_remove_controller(godot_int p_controller_id);
void GDAPI godot_arvr_set_controller_transform(godot_int p_controller_id, godot_transform *p_transform, godot_bool p_tracks_orientation, godot_bool p_tracks_position);
void GDAPI godot_arvr_set_controller_button(godot_int p_controller_id, godot_int p_button, godot_bool p_is_pressed);
void GDAPI godot_arvr_set_controller_axis(godot_int p_controller_id, godot_int p_axis, godot_real p_value, godot_bool p_can_be_negative);
+godot_real GDAPI godot_arvr_get_controller_rumble(godot_int p_controller_id);
#ifdef __cplusplus
}
diff --git a/modules/gdnative/nativearvr/arvr_interface_gdnative.cpp b/modules/gdnative/nativearvr/arvr_interface_gdnative.cpp
index ff8bda162f..e2a7019fa4 100644
--- a/modules/gdnative/nativearvr/arvr_interface_gdnative.cpp
+++ b/modules/gdnative/nativearvr/arvr_interface_gdnative.cpp
@@ -383,4 +383,16 @@ void GDAPI godot_arvr_set_controller_axis(godot_int p_controller_id, godot_int p
}
}
}
+
+godot_real GDAPI godot_arvr_get_controller_rumble(godot_int p_controller_id) {
+ ARVRServer *arvr_server = ARVRServer::get_singleton();
+ ERR_FAIL_NULL_V(arvr_server, 0.0);
+
+ ARVRPositionalTracker *tracker = arvr_server->find_by_type_and_id(ARVRServer::TRACKER_CONTROLLER, p_controller_id);
+ if (tracker != NULL) {
+ return tracker->get_rumble();
+ }
+
+ return 0.0;
+}
}
diff --git a/modules/gdscript/gd_editor.cpp b/modules/gdscript/gd_editor.cpp
index bcb998aee0..d9b10ff3fa 100644
--- a/modules/gdscript/gd_editor.cpp
+++ b/modules/gdscript/gd_editor.cpp
@@ -2111,9 +2111,9 @@ Error GDScriptLanguage::complete_code(const String &p_code, const String &p_base
for (List<String>::Element *E = opts.front(); E; E = E->next()) {
String opt = E->get().strip_edges();
- if (opt.begins_with("\"") && opt.ends_with("\"")) {
+ if (opt.is_quoted()) {
r_forced = true;
- String idopt = opt.substr(1, opt.length() - 2);
+ String idopt = opt.unquote();
if (idopt.replace("/", "_").is_valid_identifier()) {
options.insert(idopt);
} else {
diff --git a/modules/gdscript/gd_functions.cpp b/modules/gdscript/gd_functions.cpp
index 34d01c6beb..b43ec409a1 100644
--- a/modules/gdscript/gd_functions.cpp
+++ b/modules/gdscript/gd_functions.cpp
@@ -83,6 +83,8 @@ const char *GDFunctions::get_func_name(Function p_func) {
"rad2deg",
"linear2db",
"db2linear",
+ "wrapi",
+ "wrapf",
"max",
"min",
"clamp",
@@ -405,6 +407,14 @@ void GDFunctions::call(Function p_func, const Variant **p_args, int p_arg_count,
VALIDATE_ARG_NUM(0);
r_ret = Math::db2linear((double)*p_args[0]);
} break;
+ case MATH_WRAP: {
+ VALIDATE_ARG_COUNT(3);
+ r_ret = Math::wrapi((int64_t)*p_args[0], (int64_t)*p_args[1], (int64_t)*p_args[2]);
+ } break;
+ case MATH_WRAPF: {
+ VALIDATE_ARG_COUNT(3);
+ r_ret = Math::wrapf((double)*p_args[0], (double)*p_args[1], (double)*p_args[2]);
+ } break;
case LOGIC_MAX: {
VALIDATE_ARG_COUNT(2);
if (p_args[0]->get_type() == Variant::INT && p_args[1]->get_type() == Variant::INT) {
@@ -1285,6 +1295,8 @@ bool GDFunctions::is_deterministic(Function p_func) {
case MATH_RAD2DEG:
case MATH_LINEAR2DB:
case MATH_DB2LINEAR:
+ case MATH_WRAP:
+ case MATH_WRAPF:
case LOGIC_MAX:
case LOGIC_MIN:
case LOGIC_CLAMP:
@@ -1513,6 +1525,16 @@ MethodInfo GDFunctions::get_info(Function p_func) {
mi.return_val.type = Variant::REAL;
return mi;
} break;
+ case MATH_WRAP: {
+ MethodInfo mi("wrapi", PropertyInfo(Variant::INT, "value"), PropertyInfo(Variant::INT, "min"), PropertyInfo(Variant::INT, "max"));
+ mi.return_val.type = Variant::INT;
+ return mi;
+ } break;
+ case MATH_WRAPF: {
+ MethodInfo mi("wrapf", PropertyInfo(Variant::REAL, "value"), PropertyInfo(Variant::REAL, "min"), PropertyInfo(Variant::REAL, "max"));
+ mi.return_val.type = Variant::REAL;
+ return mi;
+ } break;
case LOGIC_MAX: {
MethodInfo mi("max", PropertyInfo(Variant::REAL, "a"), PropertyInfo(Variant::REAL, "b"));
mi.return_val.type = Variant::REAL;
diff --git a/modules/gdscript/gd_functions.h b/modules/gdscript/gd_functions.h
index a568c8f1cf..0de09f2e71 100644
--- a/modules/gdscript/gd_functions.h
+++ b/modules/gdscript/gd_functions.h
@@ -75,6 +75,8 @@ public:
MATH_RAD2DEG,
MATH_LINEAR2DB,
MATH_DB2LINEAR,
+ MATH_WRAP,
+ MATH_WRAPF,
LOGIC_MAX,
LOGIC_MIN,
LOGIC_CLAMP,
diff --git a/modules/mono/SCsub b/modules/mono/SCsub
index caf4fdb3ca..18a20ecac4 100644
--- a/modules/mono/SCsub
+++ b/modules/mono/SCsub
@@ -53,68 +53,155 @@ if env['tools']:
vars = Variables()
vars.Add(BoolVariable('mono_glue', 'Build with the mono glue sources', True))
+vars.Add(BoolVariable('xbuild_fallback', 'If MSBuild is not found, fallback to xbuild', False))
vars.Update(env)
# Glue sources
if env['mono_glue']:
env.add_source_files(env.modules_sources, 'glue/*.cpp')
else:
- env.Append(CPPDEFINES = [ 'MONO_GLUE_DISABLED' ])
+ env.Append(CPPDEFINES=['MONO_GLUE_DISABLED'])
if ARGUMENTS.get('yolo_copy', False):
- env.Append(CPPDEFINES = [ 'YOLO_COPY' ])
+ env.Append(CPPDEFINES=['YOLO_COPY'])
+
# Build GodotSharpTools solution
+
import os
-import subprocess
-import mono_reg_utils as monoreg
+
+
+def find_msbuild_unix(filename):
+ import os.path
+ import sys
+
+ hint_dirs = ['/opt/novell/mono/bin']
+ if sys.platform == "darwin":
+ hint_dirs = ['/Library/Frameworks/Mono.framework/Versions/Current/bin'] + hint_dirs
+
+ for hint_dir in hint_dirs:
+ hint_path = os.path.join(hint_dir, filename)
+ if os.path.isfile(hint_path):
+ return hint_path
+ elif os.path.isfile(hint_path + ".exe"):
+ return hint_path + ".exe"
+
+ for hint_dir in os.environ["PATH"].split(os.pathsep):
+ hint_dir = hint_dir.strip('"')
+ hint_path = os.path.join(hint_dir, filename)
+ if os.path.isfile(hint_path) and os.access(hint_path, os.X_OK):
+ return hint_path
+ if os.path.isfile(hint_path + ".exe") and os.access(hint_path + ".exe", os.X_OK):
+ return hint_path + ".exe"
+
+ return None
+
+
+def find_msbuild_windows():
+ import mono_reg_utils as monoreg
+
+ bits = env['bits']
+
+ if bits == '32':
+ if os.getenv('MONO32_PREFIX'):
+ mono_root = os.getenv('MONO32_PREFIX')
+ else:
+ mono_root = monoreg.find_mono_root_dir(bits)
+ else:
+ if os.getenv('MONO64_PREFIX'):
+ mono_root = os.getenv('MONO64_PREFIX')
+ else:
+ mono_root = monoreg.find_mono_root_dir(bits)
+
+ if not mono_root:
+ raise RuntimeError('Cannot find mono root directory')
+
+ msbuild_tools_path = monoreg.find_msbuild_tools_path_reg()
+
+ if msbuild_tools_path:
+ return (os.path.join(msbuild_tools_path, 'MSBuild.exe'), os.path.join(mono_root, 'lib', 'mono', '4.5'))
+ else:
+ msbuild_mono = os.path.join(mono_root, 'bin', 'msbuild.bat')
+
+ if os.path.isfile(msbuild_mono):
+ return (msbuild_mono, '')
+
+ return None
def mono_build_solution(source, target, env):
+ import subprocess
+ import mono_reg_utils as monoreg
+ from shutil import copyfile
+
+ framework_path_override = ''
+
if os.name == 'nt':
- msbuild_tools_path = monoreg.find_msbuild_tools_path_reg()
- if not msbuild_tools_path:
- raise RuntimeError('Cannot find MSBuild Tools Path in the registry')
- msbuild_path = os.path.join(msbuild_tools_path, 'MSBuild.exe')
+ msbuild_info = find_msbuild_windows()
+ if msbuild_info is None:
+ raise RuntimeError('Cannot find MSBuild executable')
+ msbuild_path = msbuild_info[0]
+ framework_path_override = msbuild_info[1]
else:
- msbuild_path = 'msbuild'
+ msbuild_path = find_msbuild_unix('msbuild')
+ if msbuild_path is None:
+ xbuild_fallback = env['xbuild_fallback']
+
+ if xbuild_fallback and os.name == 'nt':
+ print("Option 'xbuild_fallback' not supported on Windows")
+ xbuild_fallback = False
+
+ if xbuild_fallback:
+ print('Cannot find MSBuild executable, trying with xbuild')
+ print('Warning: xbuild is deprecated')
+
+ msbuild_path = find_msbuild_unix('xbuild')
+
+ if msbuild_path is None:
+ raise RuntimeError('Cannot find xbuild executable')
+ else:
+ raise RuntimeError('Cannot find MSBuild executable')
+
+ print('MSBuild path: ' + msbuild_path)
- output_path = os.path.abspath(os.path.join(str(target[0]), os.pardir))
+ build_config = 'Release'
msbuild_args = [
msbuild_path,
os.path.abspath(str(source[0])),
- '/p:Configuration=Release',
- '/p:OutputPath=' + output_path
+ '/p:Configuration=' + build_config,
]
+ if framework_path_override:
+ msbuild_args += ['/p:FrameworkPathOverride=' + framework_path_override]
+
msbuild_env = os.environ.copy()
# Needed when running from Developer Command Prompt for VS
if 'PLATFORM' in msbuild_env:
del msbuild_env['PLATFORM']
- msbuild_alt_paths = [ 'xbuild' ]
-
- while True:
- try:
- subprocess.check_call(msbuild_args, env = msbuild_env)
- break
- except subprocess.CalledProcessError:
- raise RuntimeError('GodotSharpTools build failed')
- except OSError:
- if os.name != 'nt':
- if not msbuild_alt_paths:
- raise RuntimeError('Could not find commands msbuild or xbuild')
- # Try xbuild
- msbuild_args[0] = msbuild_alt_paths.pop(0)
- else:
- raise RuntimeError('Could not find command MSBuild.exe')
+ try:
+ subprocess.check_call(msbuild_args, env=msbuild_env)
+ except subprocess.CalledProcessError:
+ raise RuntimeError('GodotSharpTools build failed')
+
+ src_dir = os.path.abspath(os.path.join(str(source[0]), os.pardir, 'bin', build_config))
+ dst_dir = os.path.abspath(os.path.join(str(target[0]), os.pardir))
+
+ if not os.path.isdir(dst_dir):
+ if os.path.exists(dst_dir):
+ raise RuntimeError('Target directory is a file')
+ os.makedirs(dst_dir)
+
+ asm_file = 'GodotSharpTools.dll'
+
+ copyfile(os.path.join(src_dir, asm_file), os.path.join(dst_dir, asm_file))
mono_sln_builder = Builder(action = mono_build_solution)
-env.Append(BUILDERS = { 'MonoBuildSolution' : mono_sln_builder })
+env.Append(BUILDERS={'MonoBuildSolution': mono_sln_builder})
env.MonoBuildSolution(
os.path.join(Dir('#bin').abspath, 'GodotSharpTools.dll'),
'editor/GodotSharpTools/GodotSharpTools.sln'
diff --git a/modules/mono/config.py b/modules/mono/config.py
index 0833d30ce1..7ad135e0b9 100644
--- a/modules/mono/config.py
+++ b/modules/mono/config.py
@@ -2,7 +2,6 @@
import imp
import os
import sys
-from shutil import copyfile
from SCons.Script import BoolVariable, Environment, Variables
@@ -16,8 +15,7 @@ def find_file_in_dir(directory, files, prefix='', extension=''):
for curfile in files:
if os.path.isfile(os.path.join(directory, prefix + curfile + extension)):
return curfile
-
- return None
+ return ''
def can_build(platform):
@@ -31,13 +29,32 @@ def is_enabled():
return False
+def copy_file_no_replace(src_dir, dst_dir, name):
+ from shutil import copyfile
+
+ src_path = os.path.join(src_dir, name)
+ dst_path = os.path.join(dst_dir, name)
+ need_copy = True
+
+ if not os.path.isdir(dst_dir):
+ os.mkdir(dst_dir)
+ elif os.path.exists(dst_path):
+ need_copy = False
+
+ if need_copy:
+ copyfile(src_path, dst_path)
+
+
def configure(env):
env.use_ptrcall = True
+ env.add_module_version_string("mono")
envvars = Variables()
envvars.Add(BoolVariable('mono_static', 'Statically link mono', False))
envvars.Update(env)
+ bits = env['bits']
+
mono_static = env['mono_static']
mono_lib_names = ['mono-2.0-sgen', 'monosgen-2.0']
@@ -46,18 +63,18 @@ def configure(env):
if mono_static:
raise RuntimeError('mono-static: Not supported on Windows')
- if env['bits'] == '32':
+ if bits == '32':
if os.getenv('MONO32_PREFIX'):
mono_root = os.getenv('MONO32_PREFIX')
elif os.name == 'nt':
- mono_root = monoreg.find_mono_root_dir()
+ mono_root = monoreg.find_mono_root_dir(bits)
else:
if os.getenv('MONO64_PREFIX'):
mono_root = os.getenv('MONO64_PREFIX')
elif os.name == 'nt':
- mono_root = monoreg.find_mono_root_dir()
+ mono_root = monoreg.find_mono_root_dir(bits)
- if mono_root is None:
+ if not mono_root:
raise RuntimeError('Mono installation directory not found')
mono_lib_path = os.path.join(mono_root, 'lib')
@@ -67,7 +84,7 @@ def configure(env):
mono_lib_name = find_file_in_dir(mono_lib_path, mono_lib_names, extension='.lib')
- if mono_lib_name is None:
+ if not mono_lib_name:
raise RuntimeError('Could not find mono library in: ' + mono_lib_path)
if os.getenv('VCINSTALLDIR'):
@@ -79,28 +96,23 @@ def configure(env):
mono_dll_name = find_file_in_dir(mono_bin_path, mono_lib_names, extension='.dll')
- mono_dll_src = os.path.join(mono_bin_path, mono_dll_name + '.dll')
- mono_dll_dst = os.path.join('bin', mono_dll_name + '.dll')
- copy_mono_dll = True
-
- if not os.path.isdir('bin'):
- os.mkdir('bin')
- elif os.path.exists(mono_dll_dst):
- copy_mono_dll = False
+ if not mono_dll_name:
+ raise RuntimeError('Could not find mono shared library in: ' + mono_bin_path)
- if copy_mono_dll:
- copyfile(mono_dll_src, mono_dll_dst)
+ copy_file_no_replace(mono_bin_path, 'bin', mono_dll_name + '.dll')
else:
- mono_root = None
+ sharedlib_ext = '.dylib' if sys.platform == 'darwin' else '.so'
- if env['bits'] == '32':
+ mono_root = ''
+
+ if bits == '32':
if os.getenv('MONO32_PREFIX'):
mono_root = os.getenv('MONO32_PREFIX')
else:
if os.getenv('MONO64_PREFIX'):
mono_root = os.getenv('MONO64_PREFIX')
- if mono_root is not None:
+ if mono_root:
mono_lib_path = os.path.join(mono_root, 'lib')
env.Append(LIBPATH=mono_lib_path)
@@ -108,7 +120,7 @@ def configure(env):
mono_lib = find_file_in_dir(mono_lib_path, mono_lib_names, prefix='lib', extension='.a')
- if mono_lib is None:
+ if not mono_lib:
raise RuntimeError('Could not find mono library in: ' + mono_lib_path)
env.Append(CPPFLAGS=['-D_REENTRANT'])
@@ -130,12 +142,37 @@ def configure(env):
elif sys.platform == "linux" or sys.platform == "linux2":
env.Append(LIBS=['m', 'rt', 'dl', 'pthread'])
+ if not mono_static:
+ mono_so_name = find_file_in_dir(mono_lib_path, mono_lib_names, prefix='lib', extension=sharedlib_ext)
+
+ if not mono_so_name:
+ raise RuntimeError('Could not find mono shared library in: ' + mono_lib_path)
+
+ copy_file_no_replace(mono_lib_path, 'bin', 'lib' + mono_so_name + sharedlib_ext)
else:
if mono_static:
raise RuntimeError('mono-static: Not supported with pkg-config. Specify a mono prefix manually')
env.ParseConfig('pkg-config monosgen-2 --cflags --libs')
+ mono_lib_path = ''
+ mono_so_name = ''
+
+ tmpenv = Environment()
+ tmpenv.ParseConfig('pkg-config monosgen-2 --libs-only-L')
+
+ for hint_dir in tmpenv['LIBPATH']:
+ name_found = find_file_in_dir(hint_dir, mono_lib_names, prefix='lib', extension=sharedlib_ext)
+ if name_found:
+ mono_lib_path = hint_dir
+ mono_so_name = name_found
+ break
+
+ if not mono_so_name:
+ raise RuntimeError('Could not find mono shared library in: ' + str(tmpenv['LIBPATH']))
+
+ copy_file_no_replace(mono_lib_path, 'bin', 'lib' + mono_so_name + sharedlib_ext)
+
env.Append(LINKFLAGS='-rdynamic')
diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp
index 8d4e19469a..161e130a07 100644
--- a/modules/mono/csharp_script.cpp
+++ b/modules/mono/csharp_script.cpp
@@ -122,6 +122,9 @@ void CSharpLanguage::init() {
void CSharpLanguage::finish() {
+ // Release gchandle bindings before finalizing mono runtime
+ gchandle_bindings.clear();
+
if (gdmono) {
memdelete(gdmono);
gdmono = NULL;
@@ -297,6 +300,20 @@ Ref<Script> CSharpLanguage::get_template(const String &p_class_name, const Strin
return script;
}
+bool CSharpLanguage::is_using_templates() {
+
+ return true;
+}
+
+void CSharpLanguage::make_template(const String &p_class_name, const String &p_base_class_name, Ref<Script> &p_script) {
+
+ String src = p_script->get_source_code();
+ src = src.replace("%BASE%", p_base_class_name)
+ .replace("%CLASS%", p_class_name)
+ .replace("%TS%", _get_indentation());
+ p_script->set_source_code(src);
+}
+
Script *CSharpLanguage::create_script() const {
return memnew(CSharpScript);
@@ -396,6 +413,25 @@ String CSharpLanguage::make_function(const String &p_class, const String &p_name
#endif
}
+String CSharpLanguage::_get_indentation() const {
+#ifdef TOOLS_ENABLED
+ if (Engine::get_singleton()->is_editor_hint()) {
+ bool use_space_indentation = EDITOR_DEF("text_editor/indent/type", 0);
+
+ if (use_space_indentation) {
+ int indent_size = EDITOR_DEF("text_editor/indent/size", 4);
+
+ String space_indent = "";
+ for (int i = 0; i < indent_size; i++) {
+ space_indent += " ";
+ }
+ return space_indent;
+ }
+ }
+#endif
+ return "\t";
+}
+
void CSharpLanguage::frame() {
const Ref<MonoGCHandle> &task_scheduler_handle = GDMonoUtils::mono_cache.task_scheduler_handle;
@@ -477,6 +513,7 @@ void CSharpLanguage::reload_tool_script(const Ref<Script> &p_script, bool p_soft
(void)p_script; // UNUSED
#ifdef TOOLS_ENABLED
+ MonoReloadNode::get_singleton()->restart_reload_timer();
reload_assemblies_if_needed(p_soft_reload);
#endif
}
@@ -488,13 +525,17 @@ void CSharpLanguage::reload_assemblies_if_needed(bool p_soft_reload) {
GDMonoAssembly *proj_assembly = gdmono->get_project_assembly();
+ String name = ProjectSettings::get_singleton()->get("application/config/name");
+ if (name.empty()) {
+ name = "UnnamedProject";
+ }
+
if (proj_assembly) {
String proj_asm_path = proj_assembly->get_path();
if (!FileAccess::exists(proj_assembly->get_path())) {
// Maybe it wasn't loaded from the default path, so check this as well
- String proj_asm_name = ProjectSettings::get_singleton()->get("application/config/name");
- proj_asm_path = GodotSharpDirs::get_res_temp_assemblies_dir().plus_file(proj_asm_name);
+ proj_asm_path = GodotSharpDirs::get_res_temp_assemblies_dir().plus_file(name);
if (!FileAccess::exists(proj_asm_path))
return; // No assembly to load
}
@@ -502,8 +543,7 @@ void CSharpLanguage::reload_assemblies_if_needed(bool p_soft_reload) {
if (FileAccess::get_modified_time(proj_asm_path) <= proj_assembly->get_modified_time())
return; // Already up to date
} else {
- String proj_asm_name = ProjectSettings::get_singleton()->get("application/config/name");
- if (!FileAccess::exists(GodotSharpDirs::get_res_temp_assemblies_dir().plus_file(proj_asm_name)))
+ if (!FileAccess::exists(GodotSharpDirs::get_res_temp_assemblies_dir().plus_file(name)))
return; // No assembly to load
}
}
@@ -621,6 +661,9 @@ void CSharpLanguage::reload_assemblies_if_needed(bool p_soft_reload) {
//if instance states were saved, set them!
}
+
+ if (Engine::get_singleton()->is_editor_hint())
+ EditorNode::get_singleton()->get_property_editor()->update_tree();
}
#endif
@@ -787,6 +830,14 @@ void *CSharpLanguage::alloc_instance_binding_data(Object *p_object) {
void CSharpLanguage::free_instance_binding_data(void *p_data) {
+ if (GDMono::get_singleton() == NULL) {
+#ifdef DEBUG_ENABLED
+ CRASH_COND(!gchandle_bindings.empty());
+#endif
+ // Mono runtime finalized, all the gchandle bindings were already released
+ return;
+ }
+
#ifndef NO_THREADS
script_bind_lock->lock();
#endif
@@ -1404,6 +1455,34 @@ void CSharpScript::_resource_path_changed() {
}
}
+bool CSharpScript::_get(const StringName &p_name, Variant &r_ret) const {
+
+ if (p_name == CSharpLanguage::singleton->string_names._script_source) {
+
+ r_ret = get_source_code();
+ return true;
+ }
+
+ return false;
+}
+
+bool CSharpScript::_set(const StringName &p_name, const Variant &p_value) {
+
+ if (p_name == CSharpLanguage::singleton->string_names._script_source) {
+
+ set_source_code(p_value);
+ reload();
+ return true;
+ }
+
+ return false;
+}
+
+void CSharpScript::_get_property_list(List<PropertyInfo> *p_properties) const {
+
+ p_properties->push_back(PropertyInfo(Variant::STRING, CSharpLanguage::singleton->string_names._script_source, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR));
+}
+
void CSharpScript::_bind_methods() {
ClassDB::bind_vararg_method(METHOD_FLAGS_DEFAULT, "new", &CSharpScript::_new, MethodInfo(Variant::OBJECT, "new"));
@@ -1977,5 +2056,6 @@ CSharpLanguage::StringNameCache::StringNameCache() {
_set = StaticCString::create("_set");
_get = StaticCString::create("_get");
_notification = StaticCString::create("_notification");
+ _script_source = StaticCString::create("script/source");
dotctor = StaticCString::create(".ctor");
}
diff --git a/modules/mono/csharp_script.h b/modules/mono/csharp_script.h
index 65a6450da5..255665b495 100644
--- a/modules/mono/csharp_script.h
+++ b/modules/mono/csharp_script.h
@@ -116,6 +116,9 @@ protected:
Variant call(const StringName &p_method, const Variant **p_args, int p_argcount, Variant::CallError &r_error);
virtual void _resource_path_changed();
+ bool _get(const StringName &p_name, Variant &r_ret) const;
+ bool _set(const StringName &p_name, const Variant &p_value);
+ void _get_property_list(List<PropertyInfo> *p_properties) const;
public:
virtual bool can_instance() const;
@@ -228,16 +231,17 @@ class CSharpLanguage : public ScriptLanguage {
StringName _set;
StringName _get;
StringName _notification;
+ StringName _script_source;
StringName dotctor; // .ctor
StringNameCache();
};
- StringNameCache string_names;
-
int lang_idx;
public:
+ StringNameCache string_names;
+
_FORCE_INLINE_ int get_language_index() { return lang_idx; }
void set_language_index(int p_idx);
@@ -266,6 +270,8 @@ public:
virtual void get_comment_delimiters(List<String> *p_delimiters) const;
virtual void get_string_delimiters(List<String> *p_delimiters) const;
virtual Ref<Script> get_template(const String &p_class_name, const String &p_base_class_name) const;
+ virtual bool is_using_templates();
+ virtual void make_template(const String &p_class_name, const String &p_base_class_name, Ref<Script> &p_script);
/* TODO */ virtual bool validate(const String &p_script, int &r_line_error, int &r_col_error, String &r_test_error, const String &p_path, List<String> *r_functions) const { return true; }
virtual Script *create_script() const;
virtual bool has_named_classes() const;
@@ -273,6 +279,7 @@ public:
/* TODO? */ virtual int find_function(const String &p_function, const String &p_code) const { return -1; }
virtual String make_function(const String &p_class, const String &p_name, const PoolStringArray &p_args) const;
/* TODO? */ Error complete_code(const String &p_code, const String &p_base_path, Object *p_owner, List<String> *r_options, String &r_call_hint) { return ERR_UNAVAILABLE; }
+ virtual String _get_indentation() const;
/* TODO? */ virtual void auto_indent_code(String &p_code, int p_from_line, int p_to_line) const {}
/* TODO */ virtual void add_global_constant(const StringName &p_variable, const Variant &p_value) {}
diff --git a/modules/mono/editor/GodotSharpTools/Build/BuildSystem.cs b/modules/mono/editor/GodotSharpTools/Build/BuildSystem.cs
index 5544233eb7..04da0600cc 100644
--- a/modules/mono/editor/GodotSharpTools/Build/BuildSystem.cs
+++ b/modules/mono/editor/GodotSharpTools/Build/BuildSystem.cs
@@ -4,6 +4,7 @@ using System.Collections.Specialized;
using System.Diagnostics;
using System.IO;
using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
using System.Security;
using Microsoft.Build.Framework;
@@ -12,22 +13,27 @@ namespace GodotSharpTools.Build
public class BuildInstance : IDisposable
{
[MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static void godot_icall_BuildInstance_ExitCallback(string solution, string config, int exitCode);
+ private extern static void godot_icall_BuildInstance_ExitCallback(string solution, string config, int exitCode);
[MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static string godot_icall_BuildInstance_get_MSBuildPath();
+ private extern static void godot_icall_BuildInstance_get_MSBuildInfo(ref string msbuildPath, ref string frameworkPath);
- private static string MSBuildPath
+ private struct MSBuildInfo
{
- get
- {
- string ret = godot_icall_BuildInstance_get_MSBuildPath();
+ public string path;
+ public string frameworkPathOverride;
+ }
- if (ret == null)
- throw new FileNotFoundException("Cannot find the MSBuild executable.");
+ private static MSBuildInfo GetMSBuildInfo()
+ {
+ MSBuildInfo msbuildInfo = new MSBuildInfo();
- return ret;
- }
+ godot_icall_BuildInstance_get_MSBuildInfo(ref msbuildInfo.path, ref msbuildInfo.frameworkPathOverride);
+
+ if (msbuildInfo.path == null)
+ throw new FileNotFoundException("Cannot find the MSBuild executable.");
+
+ return msbuildInfo;
}
private string solution;
@@ -48,9 +54,19 @@ namespace GodotSharpTools.Build
public bool Build(string loggerAssemblyPath, string loggerOutputDir, string[] customProperties = null)
{
- string compilerArgs = BuildArguments(loggerAssemblyPath, loggerOutputDir, customProperties);
+ MSBuildInfo msbuildInfo = GetMSBuildInfo();
+
+ List<string> customPropertiesList = new List<string>();
+
+ if (customProperties != null)
+ customPropertiesList.AddRange(customProperties);
+
+ if (msbuildInfo.frameworkPathOverride != null)
+ customPropertiesList.Add("FrameworkPathOverride=" + msbuildInfo.frameworkPathOverride);
- ProcessStartInfo startInfo = new ProcessStartInfo(MSBuildPath, compilerArgs);
+ string compilerArgs = BuildArguments(loggerAssemblyPath, loggerOutputDir, customPropertiesList);
+
+ ProcessStartInfo startInfo = new ProcessStartInfo(msbuildInfo.path, compilerArgs);
// No console output, thanks
startInfo.RedirectStandardOutput = true;
@@ -82,9 +98,19 @@ namespace GodotSharpTools.Build
if (process != null)
throw new InvalidOperationException("Already in use");
- string compilerArgs = BuildArguments(loggerAssemblyPath, loggerOutputDir, customProperties);
+ MSBuildInfo msbuildInfo = GetMSBuildInfo();
+
+ List<string> customPropertiesList = new List<string>();
+
+ if (customProperties != null)
+ customPropertiesList.AddRange(customProperties);
+
+ if (msbuildInfo.frameworkPathOverride.Length > 0)
+ customPropertiesList.Add("FrameworkPathOverride=" + msbuildInfo.frameworkPathOverride);
- ProcessStartInfo startInfo = new ProcessStartInfo("msbuild", compilerArgs);
+ string compilerArgs = BuildArguments(loggerAssemblyPath, loggerOutputDir, customPropertiesList);
+
+ ProcessStartInfo startInfo = new ProcessStartInfo(msbuildInfo.path, compilerArgs);
// No console output, thanks
startInfo.RedirectStandardOutput = true;
@@ -101,10 +127,13 @@ namespace GodotSharpTools.Build
process.Start();
+ process.BeginOutputReadLine();
+ process.BeginErrorReadLine();
+
return true;
}
- private string BuildArguments(string loggerAssemblyPath, string loggerOutputDir, string[] customProperties)
+ private string BuildArguments(string loggerAssemblyPath, string loggerOutputDir, List<string> customProperties)
{
string arguments = string.Format(@"""{0}"" /v:normal /t:Build ""/p:{1}"" ""/l:{2},{3};{4}""",
solution,
@@ -114,12 +143,9 @@ namespace GodotSharpTools.Build
loggerOutputDir
);
- if (customProperties != null)
+ foreach (string customProperty in customProperties)
{
- foreach (string customProperty in customProperties)
- {
- arguments += " /p:" + customProperty;
- }
+ arguments += " \"/p:" + customProperty + "\"";
}
return arguments;
diff --git a/modules/mono/editor/GodotSharpTools/GodotSharpTools.sln b/modules/mono/editor/GodotSharpTools/GodotSharpTools.sln
index 7eabcdff5d..5f7d0e8a39 100644
--- a/modules/mono/editor/GodotSharpTools/GodotSharpTools.sln
+++ b/modules/mono/editor/GodotSharpTools/GodotSharpTools.sln
@@ -1,17 +1,17 @@
-
-Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio 2012
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GodotSharpTools", "GodotSharpTools.csproj", "{A8CDAD94-C6D4-4B19-A7E7-76C53CC92984}"
-EndProject
-Global
- GlobalSection(SolutionConfigurationPlatforms) = preSolution
- Debug|Any CPU = Debug|Any CPU
- Release|Any CPU = Release|Any CPU
- EndGlobalSection
- GlobalSection(ProjectConfigurationPlatforms) = postSolution
- {A8CDAD94-C6D4-4B19-A7E7-76C53CC92984}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {A8CDAD94-C6D4-4B19-A7E7-76C53CC92984}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {A8CDAD94-C6D4-4B19-A7E7-76C53CC92984}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {A8CDAD94-C6D4-4B19-A7E7-76C53CC92984}.Release|Any CPU.Build.0 = Release|Any CPU
- EndGlobalSection
-EndGlobal
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 2012
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GodotSharpTools", "GodotSharpTools.csproj", "{A8CDAD94-C6D4-4B19-A7E7-76C53CC92984}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {A8CDAD94-C6D4-4B19-A7E7-76C53CC92984}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {A8CDAD94-C6D4-4B19-A7E7-76C53CC92984}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {A8CDAD94-C6D4-4B19-A7E7-76C53CC92984}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {A8CDAD94-C6D4-4B19-A7E7-76C53CC92984}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+EndGlobal
diff --git a/modules/mono/editor/bindings_generator.cpp b/modules/mono/editor/bindings_generator.cpp
index 6cb4d09a51..eb504ec021 100644
--- a/modules/mono/editor/bindings_generator.cpp
+++ b/modules/mono/editor/bindings_generator.cpp
@@ -439,7 +439,7 @@ Error BindingsGenerator::generate_cs_core_project(const String &p_output_dir, bo
}
if (verbose_output)
- OS::get_singleton()->print("Core API solution and C# project generated successfully!\n");
+ OS::get_singleton()->print("The solution and C# project for the Core API was generated successfully\n");
return OK;
}
@@ -534,7 +534,7 @@ Error BindingsGenerator::generate_cs_editor_project(const String &p_output_dir,
}
if (verbose_output)
- OS::get_singleton()->print("Editor API solution and C# project generated successfully!\n");
+ OS::get_singleton()->print("The solution and C# project for the Editor API was generated successfully\n");
return OK;
}
@@ -543,8 +543,6 @@ Error BindingsGenerator::generate_cs_editor_project(const String &p_output_dir,
// e.g.: warning CS0108: 'SpriteBase3D.FLAG_MAX' hides inherited member 'GeometryInstance.FLAG_MAX'. Use the new keyword if hiding was intended.
Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const String &p_output_file) {
- int method_bind_count = 0;
-
bool is_derived_type = itype.base_name.length();
List<InternalCall> &custom_icalls = itype.api_type == ClassDB::API_EDITOR ? editor_custom_icalls : core_custom_icalls;
@@ -554,51 +552,51 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str
String ctor_method(ICALL_PREFIX + itype.proxy_name + "_Ctor");
- List<String> cs_file;
+ List<String> output;
- cs_file.push_back("using System;\n"); // IntPtr
+ output.push_back("using System;\n"); // IntPtr
if (itype.requires_collections)
- cs_file.push_back("using System.Collections.Generic;\n"); // Dictionary
+ output.push_back("using System.Collections.Generic;\n"); // Dictionary
- cs_file.push_back("\nnamespace " BINDINGS_NAMESPACE "\n" OPEN_BLOCK);
+ output.push_back("\nnamespace " BINDINGS_NAMESPACE "\n" OPEN_BLOCK);
const DocData::ClassDoc *class_doc = itype.class_doc;
if (class_doc && class_doc->description.size()) {
- cs_file.push_back(INDENT1 "/// <summary>\n");
+ output.push_back(INDENT1 "/// <summary>\n");
Vector<String> description_lines = class_doc->description.split("\n");
for (int i = 0; i < description_lines.size(); i++) {
if (description_lines[i].size()) {
- cs_file.push_back(INDENT1 "/// ");
- cs_file.push_back(description_lines[i].strip_edges().xml_escape());
- cs_file.push_back("\n");
+ output.push_back(INDENT1 "/// ");
+ output.push_back(description_lines[i].strip_edges().xml_escape());
+ output.push_back("\n");
}
}
- cs_file.push_back(INDENT1 "/// </summary>\n");
+ output.push_back(INDENT1 "/// </summary>\n");
}
- cs_file.push_back(INDENT1 "public ");
- cs_file.push_back(itype.is_singleton ? "static class " : "class ");
- cs_file.push_back(itype.proxy_name);
+ output.push_back(INDENT1 "public ");
+ output.push_back(itype.is_singleton ? "static class " : "class ");
+ output.push_back(itype.proxy_name);
if (itype.is_singleton || !itype.is_object_type) {
- cs_file.push_back("\n");
+ output.push_back("\n");
} else if (!is_derived_type) {
- cs_file.push_back(" : IDisposable\n");
+ output.push_back(" : IDisposable\n");
} else if (obj_types.has(itype.base_name)) {
- cs_file.push_back(" : ");
- cs_file.push_back(obj_types[itype.base_name].proxy_name);
- cs_file.push_back("\n");
+ output.push_back(" : ");
+ output.push_back(obj_types[itype.base_name].proxy_name);
+ output.push_back("\n");
} else {
- ERR_PRINTS("Base type ' " + itype.base_name + "' does not exist");
+ ERR_PRINTS("Base type '" + itype.base_name + "' does not exist, for class " + itype.name);
return ERR_INVALID_DATA;
}
- cs_file.push_back(INDENT1 "{");
+ output.push_back(INDENT1 "{");
if (class_doc) {
@@ -608,270 +606,165 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str
const DocData::ConstantDoc &const_doc = class_doc->constants[i];
if (const_doc.description.size()) {
- cs_file.push_back(MEMBER_BEGIN "/// <summary>\n");
+ output.push_back(MEMBER_BEGIN "/// <summary>\n");
Vector<String> description_lines = const_doc.description.split("\n");
for (int i = 0; i < description_lines.size(); i++) {
if (description_lines[i].size()) {
- cs_file.push_back(INDENT2 "/// ");
- cs_file.push_back(description_lines[i].strip_edges().xml_escape());
- cs_file.push_back("\n");
+ output.push_back(INDENT2 "/// ");
+ output.push_back(description_lines[i].strip_edges().xml_escape());
+ output.push_back("\n");
}
}
- cs_file.push_back(INDENT2 "/// </summary>");
+ output.push_back(INDENT2 "/// </summary>");
}
- cs_file.push_back(MEMBER_BEGIN "public const int ");
- cs_file.push_back(const_doc.name);
- cs_file.push_back(" = ");
- cs_file.push_back(const_doc.value);
- cs_file.push_back(";");
+ output.push_back(MEMBER_BEGIN "public const int ");
+ output.push_back(const_doc.name);
+ output.push_back(" = ");
+ output.push_back(const_doc.value);
+ output.push_back(";");
}
if (class_doc->constants.size())
- cs_file.push_back("\n");
+ output.push_back("\n");
// Add properties
- const Vector<DocData::PropertyDoc> &properties = itype.class_doc->properties;
+ const Vector<DocData::PropertyDoc> &properties = class_doc->properties;
for (int i = 0; i < properties.size(); i++) {
const DocData::PropertyDoc &prop_doc = properties[i];
-
- const MethodInterface *setter = itype.find_method_by_name(prop_doc.setter);
-
- // Search it in base types too
- const TypeInterface *current_type = &itype;
- while (!setter && current_type->base_name.length()) {
- Map<String, TypeInterface>::Element *base_match = obj_types.find(current_type->base_name);
- ERR_FAIL_NULL_V(base_match, ERR_BUG);
- current_type = &base_match->get();
- setter = current_type->find_method_by_name(prop_doc.setter);
- }
-
- const MethodInterface *getter = itype.find_method_by_name(prop_doc.getter);
-
- // Search it in base types too
- current_type = &itype;
- while (!getter && current_type->base_name.length()) {
- Map<String, TypeInterface>::Element *base_match = obj_types.find(current_type->base_name);
- ERR_FAIL_NULL_V(base_match, ERR_BUG);
- current_type = &base_match->get();
- getter = current_type->find_method_by_name(prop_doc.getter);
- }
-
- ERR_FAIL_COND_V(!setter && !getter, ERR_BUG);
-
- bool is_valid = false;
- int prop_index = ClassDB::get_property_index(itype.name, prop_doc.name, &is_valid);
- ERR_FAIL_COND_V(!is_valid, ERR_BUG);
-
- if (setter) {
- int setter_argc = prop_index != -1 ? 2 : 1;
- ERR_FAIL_COND_V(setter->arguments.size() != setter_argc, ERR_BUG);
- }
-
- if (getter) {
- int getter_argc = prop_index != -1 ? 1 : 0;
- ERR_FAIL_COND_V(getter->arguments.size() != getter_argc, ERR_BUG);
- }
-
- if (getter && setter) {
- ERR_FAIL_COND_V(getter->return_type != setter->arguments.back()->get().type, ERR_BUG);
- }
-
- // Let's not trust PropertyDoc::type
- String proptype_name = getter ? getter->return_type : setter->arguments.back()->get().type;
-
- const TypeInterface *prop_itype = _get_type_by_name_or_null(proptype_name);
- if (!prop_itype) {
- // Try with underscore prefix
- prop_itype = _get_type_by_name_or_null("_" + proptype_name);
- }
-
- ERR_FAIL_NULL_V(prop_itype, ERR_BUG);
-
- String prop_proxy_name = escape_csharp_keyword(snake_to_pascal_case(prop_doc.name));
-
- // Prevent property and enclosing type from sharing the same name
- if (prop_proxy_name == itype.proxy_name) {
- if (verbose_output) {
- WARN_PRINTS("Name of property `" + prop_proxy_name + "` is ambiguous with the name of its class `" +
- itype.proxy_name + "`. Renaming property to `" + prop_proxy_name + "_`");
- }
-
- prop_proxy_name += "_";
- }
-
- if (prop_doc.description.size()) {
- cs_file.push_back(MEMBER_BEGIN "/// <summary>\n");
-
- Vector<String> description_lines = prop_doc.description.split("\n");
-
- for (int i = 0; i < description_lines.size(); i++) {
- if (description_lines[i].size()) {
- cs_file.push_back(INDENT2 "/// ");
- cs_file.push_back(description_lines[i].strip_edges().xml_escape());
- cs_file.push_back("\n");
- }
- }
-
- cs_file.push_back(INDENT2 "/// </summary>");
+ Error prop_err = _generate_cs_property(itype, prop_doc, output);
+ if (prop_err != OK) {
+ ERR_EXPLAIN("Failed to generate property '" + prop_doc.name + "' for class '" + itype.name + "'");
+ ERR_FAIL_V(prop_err);
}
-
- cs_file.push_back(MEMBER_BEGIN "public ");
-
- if (itype.is_singleton)
- cs_file.push_back("static ");
-
- cs_file.push_back(prop_itype->cs_type);
- cs_file.push_back(" ");
- cs_file.push_back(prop_proxy_name.replace("/", "__"));
- cs_file.push_back("\n" INDENT2 OPEN_BLOCK);
-
- if (getter) {
- cs_file.push_back(INDENT3 "get\n" OPEN_BLOCK_L3);
- cs_file.push_back("return ");
- cs_file.push_back(getter->proxy_name + "(");
- if (prop_index != -1)
- cs_file.push_back(itos(prop_index));
- cs_file.push_back(");\n" CLOSE_BLOCK_L3);
- }
-
- if (setter) {
- cs_file.push_back(INDENT3 "set\n" OPEN_BLOCK_L3);
- cs_file.push_back(setter->proxy_name + "(");
- if (prop_index != -1)
- cs_file.push_back(itos(prop_index) + ", ");
- cs_file.push_back("value);\n" CLOSE_BLOCK_L3);
- }
-
- cs_file.push_back(CLOSE_BLOCK_L2);
}
if (class_doc->properties.size())
- cs_file.push_back("\n");
+ output.push_back("\n");
}
if (!itype.is_object_type) {
- cs_file.push_back(MEMBER_BEGIN "private const string " BINDINGS_NATIVE_NAME_FIELD " = \"" + itype.name + "\";\n");
- cs_file.push_back(MEMBER_BEGIN "private bool disposed = false;\n");
- cs_file.push_back(MEMBER_BEGIN "internal IntPtr " BINDINGS_PTR_FIELD ";\n");
+ output.push_back(MEMBER_BEGIN "private const string " BINDINGS_NATIVE_NAME_FIELD " = \"" + itype.name + "\";\n");
+ output.push_back(MEMBER_BEGIN "private bool disposed = false;\n");
+ output.push_back(MEMBER_BEGIN "internal IntPtr " BINDINGS_PTR_FIELD ";\n");
- cs_file.push_back(MEMBER_BEGIN "internal static IntPtr " CS_SMETHOD_GETINSTANCE "(");
- cs_file.push_back(itype.proxy_name);
- cs_file.push_back(" instance)\n" OPEN_BLOCK_L2 "return instance == null ? IntPtr.Zero : instance." BINDINGS_PTR_FIELD ";\n" CLOSE_BLOCK_L2);
+ output.push_back(MEMBER_BEGIN "internal static IntPtr " CS_SMETHOD_GETINSTANCE "(");
+ output.push_back(itype.proxy_name);
+ output.push_back(" instance)\n" OPEN_BLOCK_L2 "return instance == null ? IntPtr.Zero : instance." BINDINGS_PTR_FIELD ";\n" CLOSE_BLOCK_L2);
// Add Destructor
- cs_file.push_back(MEMBER_BEGIN "~");
- cs_file.push_back(itype.proxy_name);
- cs_file.push_back("()\n" OPEN_BLOCK_L2 "Dispose(false);\n" CLOSE_BLOCK_L2);
+ output.push_back(MEMBER_BEGIN "~");
+ output.push_back(itype.proxy_name);
+ output.push_back("()\n" OPEN_BLOCK_L2 "Dispose(false);\n" CLOSE_BLOCK_L2);
// Add the Dispose from IDisposable
- cs_file.push_back(MEMBER_BEGIN "public void Dispose()\n" OPEN_BLOCK_L2 "Dispose(true);\n" INDENT3 "GC.SuppressFinalize(this);\n" CLOSE_BLOCK_L2);
+ output.push_back(MEMBER_BEGIN "public void Dispose()\n" OPEN_BLOCK_L2 "Dispose(true);\n" INDENT3 "GC.SuppressFinalize(this);\n" CLOSE_BLOCK_L2);
// Add the virtual Dispose
- cs_file.push_back(MEMBER_BEGIN "public virtual void Dispose(bool disposing)\n" OPEN_BLOCK_L2
- "if (disposed) return;\n" INDENT3
- "if (" BINDINGS_PTR_FIELD " != IntPtr.Zero)\n" OPEN_BLOCK_L3 "NativeCalls.godot_icall_");
- cs_file.push_back(itype.proxy_name);
- cs_file.push_back("_Dtor(" BINDINGS_PTR_FIELD ");\n" INDENT5 BINDINGS_PTR_FIELD " = IntPtr.Zero;\n" CLOSE_BLOCK_L3 INDENT3
- "GC.SuppressFinalize(this);\n" INDENT3 "disposed = true;\n" CLOSE_BLOCK_L2);
-
- cs_file.push_back(MEMBER_BEGIN "internal ");
- cs_file.push_back(itype.proxy_name);
- cs_file.push_back("(IntPtr " BINDINGS_PTR_FIELD ")\n" OPEN_BLOCK_L2 "this." BINDINGS_PTR_FIELD " = " BINDINGS_PTR_FIELD ";\n" CLOSE_BLOCK_L2);
-
- cs_file.push_back(MEMBER_BEGIN "public IntPtr NativeInstance\n" OPEN_BLOCK_L2
- "get { return " BINDINGS_PTR_FIELD "; }\n" CLOSE_BLOCK_L2);
+ output.push_back(MEMBER_BEGIN "public virtual void Dispose(bool disposing)\n" OPEN_BLOCK_L2
+ "if (disposed) return;\n" INDENT3
+ "if (" BINDINGS_PTR_FIELD " != IntPtr.Zero)\n" OPEN_BLOCK_L3 "NativeCalls.godot_icall_");
+ output.push_back(itype.proxy_name);
+ output.push_back("_Dtor(" BINDINGS_PTR_FIELD ");\n" INDENT5 BINDINGS_PTR_FIELD " = IntPtr.Zero;\n" CLOSE_BLOCK_L3 INDENT3
+ "GC.SuppressFinalize(this);\n" INDENT3 "disposed = true;\n" CLOSE_BLOCK_L2);
+
+ output.push_back(MEMBER_BEGIN "internal ");
+ output.push_back(itype.proxy_name);
+ output.push_back("(IntPtr " BINDINGS_PTR_FIELD ")\n" OPEN_BLOCK_L2 "this." BINDINGS_PTR_FIELD " = " BINDINGS_PTR_FIELD ";\n" CLOSE_BLOCK_L2);
+
+ output.push_back(MEMBER_BEGIN "public IntPtr NativeInstance\n" OPEN_BLOCK_L2
+ "get { return " BINDINGS_PTR_FIELD "; }\n" CLOSE_BLOCK_L2);
} else if (itype.is_singleton) {
// Add the type name and the singleton pointer as static fields
- cs_file.push_back(MEMBER_BEGIN "private const string " BINDINGS_NATIVE_NAME_FIELD " = \"");
- cs_file.push_back(itype.name);
- cs_file.push_back("\";\n");
+ output.push_back(MEMBER_BEGIN "private const string " BINDINGS_NATIVE_NAME_FIELD " = \"");
+ output.push_back(itype.name);
+ output.push_back("\";\n");
- cs_file.push_back(INDENT2 "internal static IntPtr " BINDINGS_PTR_FIELD " = ");
- cs_file.push_back(itype.api_type == ClassDB::API_EDITOR ? CS_CLASS_NATIVECALLS_EDITOR : CS_CLASS_NATIVECALLS);
- cs_file.push_back("." ICALL_PREFIX);
- cs_file.push_back(itype.name);
- cs_file.push_back(SINGLETON_ICALL_SUFFIX "();\n");
+ output.push_back(INDENT2 "internal static IntPtr " BINDINGS_PTR_FIELD " = ");
+ output.push_back(itype.api_type == ClassDB::API_EDITOR ? CS_CLASS_NATIVECALLS_EDITOR : CS_CLASS_NATIVECALLS);
+ output.push_back("." ICALL_PREFIX);
+ output.push_back(itype.name);
+ output.push_back(SINGLETON_ICALL_SUFFIX "();\n");
} else {
// Add member fields
- cs_file.push_back(MEMBER_BEGIN "private const string " BINDINGS_NATIVE_NAME_FIELD " = \"");
- cs_file.push_back(itype.name);
- cs_file.push_back("\";\n");
+ output.push_back(MEMBER_BEGIN "private const string " BINDINGS_NATIVE_NAME_FIELD " = \"");
+ output.push_back(itype.name);
+ output.push_back("\";\n");
// Only the base class stores the pointer to the native object
// This pointer is expected to be and must be of type Object*
if (!is_derived_type) {
- cs_file.push_back(MEMBER_BEGIN "private bool disposed = false;\n");
- cs_file.push_back(INDENT2 "internal IntPtr " BINDINGS_PTR_FIELD ";\n");
- cs_file.push_back(INDENT2 "internal bool " CS_FIELD_MEMORYOWN ";\n");
+ output.push_back(MEMBER_BEGIN "private bool disposed = false;\n");
+ output.push_back(INDENT2 "internal IntPtr " BINDINGS_PTR_FIELD ";\n");
+ output.push_back(INDENT2 "internal bool " CS_FIELD_MEMORYOWN ";\n");
}
// Add default constructor
if (itype.is_instantiable) {
- cs_file.push_back(MEMBER_BEGIN "public ");
- cs_file.push_back(itype.proxy_name);
- cs_file.push_back("() : this(");
- cs_file.push_back(itype.memory_own ? "true" : "false");
+ output.push_back(MEMBER_BEGIN "public ");
+ output.push_back(itype.proxy_name);
+ output.push_back("() : this(");
+ output.push_back(itype.memory_own ? "true" : "false");
// The default constructor may also be called by the engine when instancing existing native objects
// The engine will initialize the pointer field of the managed side before calling the constructor
// This is why we only allocate a new native object from the constructor if the pointer field is not set
- cs_file.push_back(")\n" OPEN_BLOCK_L2 "if (" BINDINGS_PTR_FIELD " == IntPtr.Zero)\n" INDENT4 BINDINGS_PTR_FIELD " = ");
- cs_file.push_back(itype.api_type == ClassDB::API_EDITOR ? CS_CLASS_NATIVECALLS_EDITOR : CS_CLASS_NATIVECALLS);
- cs_file.push_back("." + ctor_method);
- cs_file.push_back("(this);\n" CLOSE_BLOCK_L2);
+ output.push_back(")\n" OPEN_BLOCK_L2 "if (" BINDINGS_PTR_FIELD " == IntPtr.Zero)\n" INDENT4 BINDINGS_PTR_FIELD " = ");
+ output.push_back(itype.api_type == ClassDB::API_EDITOR ? CS_CLASS_NATIVECALLS_EDITOR : CS_CLASS_NATIVECALLS);
+ output.push_back("." + ctor_method);
+ output.push_back("(this);\n" CLOSE_BLOCK_L2);
} else {
// Hide the constructor
- cs_file.push_back(MEMBER_BEGIN "internal ");
- cs_file.push_back(itype.proxy_name);
- cs_file.push_back("() {}\n");
+ output.push_back(MEMBER_BEGIN "internal ");
+ output.push_back(itype.proxy_name);
+ output.push_back("() {}\n");
}
// Add.. em.. trick constructor. Sort of.
- cs_file.push_back(MEMBER_BEGIN "internal ");
- cs_file.push_back(itype.proxy_name);
+ output.push_back(MEMBER_BEGIN "internal ");
+ output.push_back(itype.proxy_name);
if (is_derived_type) {
- cs_file.push_back("(bool " CS_FIELD_MEMORYOWN ") : base(" CS_FIELD_MEMORYOWN ") {}\n");
+ output.push_back("(bool " CS_FIELD_MEMORYOWN ") : base(" CS_FIELD_MEMORYOWN ") {}\n");
} else {
- cs_file.push_back("(bool " CS_FIELD_MEMORYOWN ")\n" OPEN_BLOCK_L2
- "this." CS_FIELD_MEMORYOWN " = " CS_FIELD_MEMORYOWN ";\n" CLOSE_BLOCK_L2);
+ output.push_back("(bool " CS_FIELD_MEMORYOWN ")\n" OPEN_BLOCK_L2
+ "this." CS_FIELD_MEMORYOWN " = " CS_FIELD_MEMORYOWN ";\n" CLOSE_BLOCK_L2);
}
// Add methods
if (!is_derived_type) {
- cs_file.push_back(MEMBER_BEGIN "public IntPtr NativeInstance\n" OPEN_BLOCK_L2
- "get { return " BINDINGS_PTR_FIELD "; }\n" CLOSE_BLOCK_L2);
+ output.push_back(MEMBER_BEGIN "public IntPtr NativeInstance\n" OPEN_BLOCK_L2
+ "get { return " BINDINGS_PTR_FIELD "; }\n" CLOSE_BLOCK_L2);
- cs_file.push_back(MEMBER_BEGIN "internal static IntPtr " CS_SMETHOD_GETINSTANCE "(Object instance)\n" OPEN_BLOCK_L2
- "return instance == null ? IntPtr.Zero : instance." BINDINGS_PTR_FIELD ";\n" CLOSE_BLOCK_L2);
+ output.push_back(MEMBER_BEGIN "internal static IntPtr " CS_SMETHOD_GETINSTANCE "(Object instance)\n" OPEN_BLOCK_L2
+ "return instance == null ? IntPtr.Zero : instance." BINDINGS_PTR_FIELD ";\n" CLOSE_BLOCK_L2);
}
if (!is_derived_type) {
// Add destructor
- cs_file.push_back(MEMBER_BEGIN "~");
- cs_file.push_back(itype.proxy_name);
- cs_file.push_back("()\n" OPEN_BLOCK_L2 "Dispose(false);\n" CLOSE_BLOCK_L2);
+ output.push_back(MEMBER_BEGIN "~");
+ output.push_back(itype.proxy_name);
+ output.push_back("()\n" OPEN_BLOCK_L2 "Dispose(false);\n" CLOSE_BLOCK_L2);
// Add the Dispose from IDisposable
- cs_file.push_back(MEMBER_BEGIN "public void Dispose()\n" OPEN_BLOCK_L2 "Dispose(true);\n" INDENT3 "GC.SuppressFinalize(this);\n" CLOSE_BLOCK_L2);
+ output.push_back(MEMBER_BEGIN "public void Dispose()\n" OPEN_BLOCK_L2 "Dispose(true);\n" INDENT3 "GC.SuppressFinalize(this);\n" CLOSE_BLOCK_L2);
// Add the virtual Dispose
- cs_file.push_back(MEMBER_BEGIN "public virtual void Dispose(bool disposing)\n" OPEN_BLOCK_L2
- "if (disposed) return;\n" INDENT3
- "if (" BINDINGS_PTR_FIELD " != IntPtr.Zero)\n" OPEN_BLOCK_L3
- "if (" CS_FIELD_MEMORYOWN ")\n" OPEN_BLOCK_L4 CS_FIELD_MEMORYOWN
- " = false;\n" INDENT5 CS_CLASS_NATIVECALLS "." ICALL_OBJECT_DTOR
- "(" BINDINGS_PTR_FIELD ");\n" INDENT5 BINDINGS_PTR_FIELD
- " = IntPtr.Zero;\n" CLOSE_BLOCK_L4 CLOSE_BLOCK_L3 INDENT3
- "GC.SuppressFinalize(this);\n" INDENT3 "disposed = true;\n" CLOSE_BLOCK_L2);
+ output.push_back(MEMBER_BEGIN "public virtual void Dispose(bool disposing)\n" OPEN_BLOCK_L2
+ "if (disposed) return;\n" INDENT3
+ "if (" BINDINGS_PTR_FIELD " != IntPtr.Zero)\n" OPEN_BLOCK_L3
+ "if (" CS_FIELD_MEMORYOWN ")\n" OPEN_BLOCK_L4 CS_FIELD_MEMORYOWN
+ " = false;\n" INDENT5 CS_CLASS_NATIVECALLS "." ICALL_OBJECT_DTOR
+ "(" BINDINGS_PTR_FIELD ");\n" INDENT5 BINDINGS_PTR_FIELD
+ " = IntPtr.Zero;\n" CLOSE_BLOCK_L4 CLOSE_BLOCK_L3 INDENT3
+ "GC.SuppressFinalize(this);\n" INDENT3 "disposed = true;\n" CLOSE_BLOCK_L2);
Map<String, TypeInterface>::Element *array_itype = builtin_types.find("Array");
@@ -883,409 +776,387 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str
Map<String, TypeInterface>::Element *object_itype = obj_types.find("Object");
if (!object_itype) {
- ERR_PRINT("BUG: Array type interface not found!");
+ ERR_PRINT("BUG: Object type interface not found!");
return ERR_BUG;
}
- cs_file.push_back(MEMBER_BEGIN "public " CS_CLASS_SIGNALAWAITER " ToSignal(");
- cs_file.push_back(object_itype->get().cs_type);
- cs_file.push_back(" source, string signal)\n" OPEN_BLOCK_L2
- "return new " CS_CLASS_SIGNALAWAITER "(source, signal, this);\n" CLOSE_BLOCK_L2);
+ output.push_back(MEMBER_BEGIN "public " CS_CLASS_SIGNALAWAITER " ToSignal(");
+ output.push_back(object_itype->get().cs_type);
+ output.push_back(" source, string signal)\n" OPEN_BLOCK_L2
+ "return new " CS_CLASS_SIGNALAWAITER "(source, signal, this);\n" CLOSE_BLOCK_L2);
}
}
Map<String, String>::Element *extra_member = extra_members.find(itype.name);
if (extra_member)
- cs_file.push_back(extra_member->get());
+ output.push_back(extra_member->get());
+ int method_bind_count = 0;
for (const List<MethodInterface>::Element *E = itype.methods.front(); E; E = E->next()) {
const MethodInterface &imethod = E->get();
+ Error method_err = _generate_cs_method(itype, imethod, method_bind_count, output);
+ if (method_err != OK) {
+ ERR_EXPLAIN("Failed to generate method '" + imethod.name + "' for class '" + itype.name + "'");
+ ERR_FAIL_V(method_err);
+ }
+ }
- const TypeInterface *return_type = _get_type_by_name_or_placeholder(imethod.return_type);
+ if (itype.is_singleton) {
+ InternalCall singleton_icall = InternalCall(itype.api_type, ICALL_PREFIX + itype.name + SINGLETON_ICALL_SUFFIX, "IntPtr");
- String method_bind_field = "method_bind_" + itos(method_bind_count);
+ if (!find_icall_by_name(singleton_icall.name, custom_icalls))
+ custom_icalls.push_back(singleton_icall);
+ }
- String icall_params = method_bind_field + ", " + sformat(itype.cs_in, "this");
- String arguments_sig;
- String cs_in_statements;
+ if (itype.is_instantiable) {
+ InternalCall ctor_icall = InternalCall(itype.api_type, ctor_method, "IntPtr", itype.proxy_name + " obj");
- List<String> default_args_doc;
+ if (!find_icall_by_name(ctor_icall.name, custom_icalls))
+ custom_icalls.push_back(ctor_icall);
+ }
- // Retrieve information from the arguments
- for (const List<ArgumentInterface>::Element *F = imethod.arguments.front(); F; F = F->next()) {
- const ArgumentInterface &iarg = F->get();
- const TypeInterface *arg_type = _get_type_by_name_or_placeholder(iarg.type);
+ output.push_back(INDENT1 CLOSE_BLOCK CLOSE_BLOCK);
- // Add the current arguments to the signature
- // If the argument has a default value which is not a constant, we will make it Nullable
- {
- if (F != imethod.arguments.front())
- arguments_sig += ", ";
+ return _save_file(p_output_file, output);
+}
- if (iarg.def_param_mode == ArgumentInterface::NULLABLE_VAL)
- arguments_sig += "Nullable<";
+Error BindingsGenerator::_generate_cs_property(const BindingsGenerator::TypeInterface &p_itype, const DocData::PropertyDoc &p_prop_doc, List<String> &p_output) {
- arguments_sig += arg_type->cs_type;
+ const MethodInterface *setter = p_itype.find_method_by_name(p_prop_doc.setter);
- if (iarg.def_param_mode == ArgumentInterface::NULLABLE_VAL)
- arguments_sig += "> ";
- else
- arguments_sig += " ";
+ // Search it in base types too
+ const TypeInterface *current_type = &p_itype;
+ while (!setter && current_type->base_name.length()) {
+ Map<String, TypeInterface>::Element *base_match = obj_types.find(current_type->base_name);
+ ERR_FAIL_NULL_V(base_match, ERR_BUG);
+ current_type = &base_match->get();
+ setter = current_type->find_method_by_name(p_prop_doc.setter);
+ }
- arguments_sig += iarg.name;
+ const MethodInterface *getter = p_itype.find_method_by_name(p_prop_doc.getter);
- if (iarg.default_argument.size()) {
- if (iarg.def_param_mode != ArgumentInterface::CONSTANT)
- arguments_sig += " = null";
- else
- arguments_sig += " = " + sformat(iarg.default_argument, arg_type->cs_type);
- }
- }
+ // Search it in base types too
+ current_type = &p_itype;
+ while (!getter && current_type->base_name.length()) {
+ Map<String, TypeInterface>::Element *base_match = obj_types.find(current_type->base_name);
+ ERR_FAIL_NULL_V(base_match, ERR_BUG);
+ current_type = &base_match->get();
+ getter = current_type->find_method_by_name(p_prop_doc.getter);
+ }
- icall_params += ", ";
+ ERR_FAIL_COND_V(!setter && !getter, ERR_BUG);
- if (iarg.default_argument.size() && iarg.def_param_mode != ArgumentInterface::CONSTANT) {
- // The default value of an argument must be constant. Otherwise we make it Nullable and do the following:
- // Type arg_in = arg.HasValue ? arg.Value : <non-const default value>;
- String arg_in = iarg.name;
- arg_in += "_in";
+ bool is_valid = false;
+ int prop_index = ClassDB::get_property_index(p_itype.name, p_prop_doc.name, &is_valid);
+ ERR_FAIL_COND_V(!is_valid, ERR_BUG);
- cs_in_statements += arg_type->cs_type;
- cs_in_statements += " ";
- cs_in_statements += arg_in;
- cs_in_statements += " = ";
- cs_in_statements += iarg.name;
+ if (setter) {
+ int setter_argc = prop_index != -1 ? 2 : 1;
+ ERR_FAIL_COND_V(setter->arguments.size() != setter_argc, ERR_BUG);
+ }
- if (iarg.def_param_mode == ArgumentInterface::NULLABLE_VAL)
- cs_in_statements += ".HasValue ? ";
- else
- cs_in_statements += " != null ? ";
+ if (getter) {
+ int getter_argc = prop_index != -1 ? 1 : 0;
+ ERR_FAIL_COND_V(getter->arguments.size() != getter_argc, ERR_BUG);
+ }
- cs_in_statements += iarg.name;
+ if (getter && setter) {
+ ERR_FAIL_COND_V(getter->return_type != setter->arguments.back()->get().type, ERR_BUG);
+ }
- if (iarg.def_param_mode == ArgumentInterface::NULLABLE_VAL)
- cs_in_statements += ".Value : ";
- else
- cs_in_statements += " : ";
+ // Let's not trust PropertyDoc::type
+ String proptype_name = getter ? getter->return_type : setter->arguments.back()->get().type;
- String def_arg = sformat(iarg.default_argument, arg_type->cs_type);
+ const TypeInterface *prop_itype = _get_type_by_name_or_null(proptype_name);
+ if (!prop_itype) {
+ // Try with underscore prefix
+ prop_itype = _get_type_by_name_or_null("_" + proptype_name);
+ }
- cs_in_statements += def_arg;
- cs_in_statements += ";\n" INDENT3;
+ ERR_FAIL_NULL_V(prop_itype, ERR_BUG);
- icall_params += arg_type->cs_in.empty() ? arg_in : sformat(arg_type->cs_in, arg_in);
+ String prop_proxy_name = escape_csharp_keyword(snake_to_pascal_case(p_prop_doc.name));
- default_args_doc.push_back(INDENT2 "/// <param name=\"" + iarg.name + "\">If the param is null, then the default value is " + def_arg + "</param>\n");
- } else {
- icall_params += arg_type->cs_in.empty() ? iarg.name : sformat(arg_type->cs_in, iarg.name);
- }
+ // Prevent property and enclosing type from sharing the same name
+ if (prop_proxy_name == p_itype.proxy_name) {
+ if (verbose_output) {
+ WARN_PRINTS("Name of property `" + prop_proxy_name + "` is ambiguous with the name of its class `" +
+ p_itype.proxy_name + "`. Renaming property to `" + prop_proxy_name + "_`");
}
- // Generate method
- {
- if (!imethod.is_virtual && !imethod.requires_object_call) {
- cs_file.push_back(MEMBER_BEGIN "private ");
- cs_file.push_back(itype.is_singleton ? "static IntPtr " : "IntPtr ");
- cs_file.push_back(method_bind_field + " = " CS_CLASS_NATIVECALLS "." ICALL_GET_METHODBIND "(" BINDINGS_NATIVE_NAME_FIELD ", \"");
- cs_file.push_back(imethod.name);
- cs_file.push_back("\");\n");
- }
+ prop_proxy_name += "_";
+ }
- if (imethod.method_doc && imethod.method_doc->description.size()) {
- cs_file.push_back(MEMBER_BEGIN "/// <summary>\n");
+ if (p_prop_doc.description.size()) {
+ p_output.push_back(MEMBER_BEGIN "/// <summary>\n");
- Vector<String> description_lines = imethod.method_doc->description.split("\n");
+ Vector<String> description_lines = p_prop_doc.description.split("\n");
- for (int i = 0; i < description_lines.size(); i++) {
- if (description_lines[i].size()) {
- cs_file.push_back(INDENT2 "/// ");
- cs_file.push_back(description_lines[i].strip_edges().xml_escape());
- cs_file.push_back("\n");
- }
- }
+ for (int i = 0; i < description_lines.size(); i++) {
+ if (description_lines[i].size()) {
+ p_output.push_back(INDENT2 "/// ");
+ p_output.push_back(description_lines[i].strip_edges().xml_escape());
+ p_output.push_back("\n");
+ }
+ }
- for (List<String>::Element *E = default_args_doc.front(); E; E = E->next()) {
- cs_file.push_back(E->get().xml_escape());
- }
+ p_output.push_back(INDENT2 "/// </summary>");
+ }
- cs_file.push_back(INDENT2 "/// </summary>");
- }
+ p_output.push_back(MEMBER_BEGIN "public ");
- if (!imethod.is_internal) {
- cs_file.push_back(MEMBER_BEGIN "[GodotMethod(\"");
- cs_file.push_back(imethod.name);
- cs_file.push_back("\")]");
- }
+ if (p_itype.is_singleton)
+ p_output.push_back("static ");
- cs_file.push_back(MEMBER_BEGIN);
- cs_file.push_back(imethod.is_internal ? "internal " : "public ");
+ p_output.push_back(prop_itype->cs_type);
+ p_output.push_back(" ");
+ p_output.push_back(prop_proxy_name.replace("/", "__"));
+ p_output.push_back("\n" INDENT2 OPEN_BLOCK);
- if (itype.is_singleton) {
- cs_file.push_back("static ");
- } else if (imethod.is_virtual) {
- cs_file.push_back("virtual ");
- }
+ if (getter) {
+ p_output.push_back(INDENT3 "get\n" OPEN_BLOCK_L3);
+ p_output.push_back("return ");
+ p_output.push_back(getter->proxy_name + "(");
+ if (prop_index != -1)
+ p_output.push_back(itos(prop_index));
+ p_output.push_back(");\n" CLOSE_BLOCK_L3);
+ }
- cs_file.push_back(return_type->cs_type + " ");
- cs_file.push_back(imethod.proxy_name + "(");
- cs_file.push_back(arguments_sig + ")\n" OPEN_BLOCK_L2);
+ if (setter) {
+ p_output.push_back(INDENT3 "set\n" OPEN_BLOCK_L3);
+ p_output.push_back(setter->proxy_name + "(");
+ if (prop_index != -1)
+ p_output.push_back(itos(prop_index) + ", ");
+ p_output.push_back("value);\n" CLOSE_BLOCK_L3);
+ }
- if (imethod.is_virtual) {
- // Godot virtual method must be overridden, therefore we return a default value by default.
+ p_output.push_back(CLOSE_BLOCK_L2);
- if (return_type->name == "void") {
- cs_file.push_back("return;\n" CLOSE_BLOCK_L2);
- } else {
- cs_file.push_back("return default(");
- cs_file.push_back(return_type->cs_type);
- cs_file.push_back(");\n" CLOSE_BLOCK_L2);
- }
+ return OK;
+}
- continue;
- }
+Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterface &p_itype, const BindingsGenerator::MethodInterface &p_imethod, int &p_method_bind_count, List<String> &p_output) {
- if (imethod.requires_object_call) {
- // Fallback to Godot's object.Call(string, params)
+ const TypeInterface *return_type = _get_type_by_name_or_placeholder(p_imethod.return_type);
- cs_file.push_back(CS_METHOD_CALL "(\"");
- cs_file.push_back(imethod.name);
- cs_file.push_back("\"");
+ String method_bind_field = "method_bind_" + itos(p_method_bind_count);
- for (const List<ArgumentInterface>::Element *F = imethod.arguments.front(); F; F = F->next()) {
- cs_file.push_back(", ");
- cs_file.push_back(F->get().name);
- }
+ String icall_params = method_bind_field + ", " + sformat(p_itype.cs_in, "this");
+ String arguments_sig;
+ String cs_in_statements;
- cs_file.push_back(");\n" CLOSE_BLOCK_L2);
+ List<String> default_args_doc;
- continue;
- }
+ // Retrieve information from the arguments
+ for (const List<ArgumentInterface>::Element *F = p_imethod.arguments.front(); F; F = F->next()) {
+ const ArgumentInterface &iarg = F->get();
+ const TypeInterface *arg_type = _get_type_by_name_or_placeholder(iarg.type);
+
+ // Add the current arguments to the signature
+ // If the argument has a default value which is not a constant, we will make it Nullable
+ {
+ if (F != p_imethod.arguments.front())
+ arguments_sig += ", ";
- const Map<const MethodInterface *, const InternalCall *>::Element *match = method_icalls_map.find(&E->get());
- ERR_FAIL_NULL_V(match, ERR_BUG);
+ if (iarg.def_param_mode == ArgumentInterface::NULLABLE_VAL)
+ arguments_sig += "Nullable<";
- const InternalCall *im_icall = match->value();
+ arguments_sig += arg_type->cs_type;
- String im_call = im_icall->editor_only ? CS_CLASS_NATIVECALLS_EDITOR : CS_CLASS_NATIVECALLS;
- im_call += "." + im_icall->name + "(" + icall_params + ");\n";
+ if (iarg.def_param_mode == ArgumentInterface::NULLABLE_VAL)
+ arguments_sig += "> ";
+ else
+ arguments_sig += " ";
- if (imethod.arguments.size())
- cs_file.push_back(cs_in_statements);
+ arguments_sig += iarg.name;
- if (return_type->name == "void") {
- cs_file.push_back(im_call);
- } else if (return_type->cs_out.empty()) {
- cs_file.push_back("return " + im_call);
- } else {
- cs_file.push_back(return_type->im_type_out);
- cs_file.push_back(" " LOCAL_RET " = ");
- cs_file.push_back(im_call);
- cs_file.push_back(INDENT3);
- cs_file.push_back(sformat(return_type->cs_out, LOCAL_RET) + "\n");
+ if (iarg.default_argument.size()) {
+ if (iarg.def_param_mode != ArgumentInterface::CONSTANT)
+ arguments_sig += " = null";
+ else
+ arguments_sig += " = " + sformat(iarg.default_argument, arg_type->cs_type);
}
-
- cs_file.push_back(CLOSE_BLOCK_L2);
}
- method_bind_count++;
- }
+ icall_params += ", ";
- if (itype.is_singleton) {
- InternalCall singleton_icall = InternalCall(itype.api_type, ICALL_PREFIX + itype.name + SINGLETON_ICALL_SUFFIX, "IntPtr");
+ if (iarg.default_argument.size() && iarg.def_param_mode != ArgumentInterface::CONSTANT) {
+ // The default value of an argument must be constant. Otherwise we make it Nullable and do the following:
+ // Type arg_in = arg.HasValue ? arg.Value : <non-const default value>;
+ String arg_in = iarg.name;
+ arg_in += "_in";
- if (!find_icall_by_name(singleton_icall.name, custom_icalls))
- custom_icalls.push_back(singleton_icall);
- }
+ cs_in_statements += arg_type->cs_type;
+ cs_in_statements += " ";
+ cs_in_statements += arg_in;
+ cs_in_statements += " = ";
+ cs_in_statements += iarg.name;
- if (itype.is_instantiable) {
- InternalCall ctor_icall = InternalCall(itype.api_type, ctor_method, "IntPtr", itype.proxy_name + " obj");
+ if (iarg.def_param_mode == ArgumentInterface::NULLABLE_VAL)
+ cs_in_statements += ".HasValue ? ";
+ else
+ cs_in_statements += " != null ? ";
- if (!find_icall_by_name(ctor_icall.name, custom_icalls))
- custom_icalls.push_back(ctor_icall);
- }
+ cs_in_statements += iarg.name;
- cs_file.push_back(INDENT1 CLOSE_BLOCK CLOSE_BLOCK);
+ if (iarg.def_param_mode == ArgumentInterface::NULLABLE_VAL)
+ cs_in_statements += ".Value : ";
+ else
+ cs_in_statements += " : ";
- return _save_file(p_output_file, cs_file);
-}
+ String def_arg = sformat(iarg.default_argument, arg_type->cs_type);
-Error BindingsGenerator::generate_glue(const String &p_output_dir) {
+ cs_in_statements += def_arg;
+ cs_in_statements += ";\n" INDENT3;
- verbose_output = true;
+ icall_params += arg_type->cs_in.empty() ? arg_in : sformat(arg_type->cs_in, arg_in);
- bool dir_exists = DirAccess::exists(p_output_dir);
- ERR_EXPLAIN("The output directory does not exist.");
- ERR_FAIL_COND_V(!dir_exists, ERR_FILE_BAD_PATH);
+ default_args_doc.push_back(INDENT2 "/// <param name=\"" + iarg.name + "\">If the param is null, then the default value is " + def_arg + "</param>\n");
+ } else {
+ icall_params += arg_type->cs_in.empty() ? iarg.name : sformat(arg_type->cs_in, iarg.name);
+ }
+ }
- List<String> cpp_file;
+ // Generate method
+ {
+ if (!p_imethod.is_virtual && !p_imethod.requires_object_call) {
+ p_output.push_back(MEMBER_BEGIN "private ");
+ p_output.push_back(p_itype.is_singleton ? "static IntPtr " : "IntPtr ");
+ p_output.push_back(method_bind_field + " = " CS_CLASS_NATIVECALLS "." ICALL_GET_METHODBIND "(" BINDINGS_NATIVE_NAME_FIELD ", \"");
+ p_output.push_back(p_imethod.name);
+ p_output.push_back("\");\n");
+ }
- cpp_file.push_back("#include \"" GLUE_HEADER_FILE "\"\n"
- "\n");
+ if (p_imethod.method_doc && p_imethod.method_doc->description.size()) {
+ p_output.push_back(MEMBER_BEGIN "/// <summary>\n");
- List<const InternalCall *> generated_icall_funcs;
+ Vector<String> description_lines = p_imethod.method_doc->description.split("\n");
- for (Map<String, TypeInterface>::Element *type_elem = obj_types.front(); type_elem; type_elem = type_elem->next()) {
- const TypeInterface &itype = type_elem->get();
+ for (int i = 0; i < description_lines.size(); i++) {
+ if (description_lines[i].size()) {
+ p_output.push_back(INDENT2 "/// ");
+ p_output.push_back(description_lines[i].strip_edges().xml_escape());
+ p_output.push_back("\n");
+ }
+ }
- List<InternalCall> &custom_icalls = itype.api_type == ClassDB::API_EDITOR ? editor_custom_icalls : core_custom_icalls;
+ for (List<String>::Element *E = default_args_doc.front(); E; E = E->next()) {
+ p_output.push_back(E->get().xml_escape());
+ }
- OS::get_singleton()->print(String("Generating " + itype.name + "...\n").utf8());
+ p_output.push_back(INDENT2 "/// </summary>");
+ }
- String ctor_method(ICALL_PREFIX + itype.proxy_name + "_Ctor");
+ if (!p_imethod.is_internal) {
+ p_output.push_back(MEMBER_BEGIN "[GodotMethod(\"");
+ p_output.push_back(p_imethod.name);
+ p_output.push_back("\")]");
+ }
- for (const List<MethodInterface>::Element *E = itype.methods.front(); E; E = E->next()) {
- const MethodInterface &imethod = E->get();
+ p_output.push_back(MEMBER_BEGIN);
+ p_output.push_back(p_imethod.is_internal ? "internal " : "public ");
- if (imethod.is_virtual)
- continue;
+ if (p_itype.is_singleton) {
+ p_output.push_back("static ");
+ } else if (p_imethod.is_virtual) {
+ p_output.push_back("virtual ");
+ }
- bool ret_void = imethod.return_type == "void";
+ p_output.push_back(return_type->cs_type + " ");
+ p_output.push_back(p_imethod.proxy_name + "(");
+ p_output.push_back(arguments_sig + ")\n" OPEN_BLOCK_L2);
- const TypeInterface *return_type = _get_type_by_name_or_placeholder(imethod.return_type);
+ if (p_imethod.is_virtual) {
+ // Godot virtual method must be overridden, therefore we return a default value by default.
- String argc_str = itos(imethod.arguments.size());
+ if (return_type->name == "void") {
+ p_output.push_back("return;\n" CLOSE_BLOCK_L2);
+ } else {
+ p_output.push_back("return default(");
+ p_output.push_back(return_type->cs_type);
+ p_output.push_back(");\n" CLOSE_BLOCK_L2);
+ }
- String c_func_sig = "MethodBind* " CS_PARAM_METHODBIND ", " + itype.c_type_in + " " CS_PARAM_INSTANCE;
- String c_in_statements;
- String c_args_var_content;
+ return OK; // Won't increment method bind count
+ }
- // Get arguments information
- int i = 0;
- for (const List<ArgumentInterface>::Element *F = imethod.arguments.front(); F; F = F->next()) {
- const ArgumentInterface &iarg = F->get();
- const TypeInterface *arg_type = _get_type_by_name_or_placeholder(iarg.type);
+ if (p_imethod.requires_object_call) {
+ // Fallback to Godot's object.Call(string, params)
- String c_param_name = "arg" + itos(i + 1);
+ p_output.push_back(CS_METHOD_CALL "(\"");
+ p_output.push_back(p_imethod.name);
+ p_output.push_back("\"");
- if (imethod.is_vararg) {
- if (i < imethod.arguments.size() - 1) {
- c_in_statements += sformat(arg_type->c_in.size() ? arg_type->c_in : TypeInterface::DEFAULT_VARARG_C_IN, "Variant", c_param_name);
- c_in_statements += "\t" C_LOCAL_PTRCALL_ARGS ".set(0, ";
- c_in_statements += sformat("&%s_in", c_param_name);
- c_in_statements += ");\n";
- }
- } else {
- if (i > 0)
- c_args_var_content += ", ";
- if (arg_type->c_in.size())
- c_in_statements += sformat(arg_type->c_in, arg_type->c_type, c_param_name);
- c_args_var_content += sformat(arg_type->c_arg_in, c_param_name);
- }
+ for (const List<ArgumentInterface>::Element *F = p_imethod.arguments.front(); F; F = F->next()) {
+ p_output.push_back(", ");
+ p_output.push_back(F->get().name);
+ }
- c_func_sig += ", ";
- c_func_sig += arg_type->c_type_in;
- c_func_sig += " ";
- c_func_sig += c_param_name;
+ p_output.push_back(");\n" CLOSE_BLOCK_L2);
- i++;
- }
+ return OK; // Won't increment method bind count
+ }
- const Map<const MethodInterface *, const InternalCall *>::Element *match = method_icalls_map.find(&E->get());
- ERR_FAIL_NULL_V(match, ERR_BUG);
+ const Map<const MethodInterface *, const InternalCall *>::Element *match = method_icalls_map.find(&p_imethod);
+ ERR_FAIL_NULL_V(match, ERR_BUG);
- const InternalCall *im_icall = match->value();
- String icall_method = im_icall->name;
+ const InternalCall *im_icall = match->value();
- if (!generated_icall_funcs.find(im_icall)) {
- generated_icall_funcs.push_back(im_icall);
+ String im_call = im_icall->editor_only ? CS_CLASS_NATIVECALLS_EDITOR : CS_CLASS_NATIVECALLS;
+ im_call += "." + im_icall->name + "(" + icall_params + ");\n";
- if (im_icall->editor_only)
- cpp_file.push_back("#ifdef TOOLS_ENABLED\n");
+ if (p_imethod.arguments.size())
+ p_output.push_back(cs_in_statements);
- // Generate icall function
+ if (return_type->name == "void") {
+ p_output.push_back(im_call);
+ } else if (return_type->cs_out.empty()) {
+ p_output.push_back("return " + im_call);
+ } else {
+ p_output.push_back(return_type->im_type_out);
+ p_output.push_back(" " LOCAL_RET " = ");
+ p_output.push_back(im_call);
+ p_output.push_back(INDENT3);
+ p_output.push_back(sformat(return_type->cs_out, LOCAL_RET) + "\n");
+ }
- cpp_file.push_back(ret_void ? "void " : return_type->c_type_out + " ");
- cpp_file.push_back(icall_method);
- cpp_file.push_back("(");
- cpp_file.push_back(c_func_sig);
- cpp_file.push_back(") " OPEN_BLOCK);
+ p_output.push_back(CLOSE_BLOCK_L2);
+ }
- String fail_ret = ret_void ? "" : ", " + (return_type->c_type_out.ends_with("*") ? "NULL" : return_type->c_type_out + "()");
+ p_method_bind_count++;
+ return OK;
+}
- if (!ret_void) {
- String ptrcall_return_type;
- String initialization;
+Error BindingsGenerator::generate_glue(const String &p_output_dir) {
- if (return_type->is_object_type) {
- ptrcall_return_type = return_type->is_reference ? "Ref<Reference>" : return_type->c_type;
- initialization = return_type->is_reference ? "" : " = NULL";
- } else {
- ptrcall_return_type = return_type->c_type;
- }
+ verbose_output = true;
- cpp_file.push_back("\t" + ptrcall_return_type);
- cpp_file.push_back(" " LOCAL_RET);
- cpp_file.push_back(initialization + ";\n");
- cpp_file.push_back("\tERR_FAIL_NULL_V(" CS_PARAM_INSTANCE);
- cpp_file.push_back(fail_ret);
- cpp_file.push_back(");\n");
- } else {
- cpp_file.push_back("\tERR_FAIL_NULL(" CS_PARAM_INSTANCE ");\n");
- }
+ bool dir_exists = DirAccess::exists(p_output_dir);
+ ERR_EXPLAIN("The output directory does not exist.");
+ ERR_FAIL_COND_V(!dir_exists, ERR_FILE_BAD_PATH);
- if (imethod.arguments.size()) {
- if (imethod.is_vararg) {
- String err_fail_macro = ret_void ? "ERR_FAIL_COND" : "ERR_FAIL_COND_V";
- String vararg_arg = "arg" + argc_str;
- String real_argc_str = itos(imethod.arguments.size() - 1); // Arguments count without vararg
-
- cpp_file.push_back("\tVector<Variant> varargs;\n"
- "\tint vararg_length = mono_array_length(");
- cpp_file.push_back(vararg_arg);
- cpp_file.push_back(");\n\tint total_length = ");
- cpp_file.push_back(real_argc_str);
- cpp_file.push_back(" + vararg_length;\n\t");
- cpp_file.push_back(err_fail_macro);
- cpp_file.push_back("(varargs.resize(vararg_length) != OK");
- cpp_file.push_back(fail_ret);
- cpp_file.push_back(");\n\tVector<Variant*> " C_LOCAL_PTRCALL_ARGS ";\n\t");
- cpp_file.push_back(err_fail_macro);
- cpp_file.push_back("(call_args.resize(total_length) != OK");
- cpp_file.push_back(fail_ret);
- cpp_file.push_back(");\n");
- cpp_file.push_back(c_in_statements);
- cpp_file.push_back("\tfor (int i = 0; i < vararg_length; i++) " OPEN_BLOCK
- "\t\tMonoObject* elem = mono_array_get(");
- cpp_file.push_back(vararg_arg);
- cpp_file.push_back(", MonoObject*, i);\n"
- "\t\tvarargs.set(i, GDMonoMarshal::mono_object_to_variant(elem));\n"
- "\t\t" C_LOCAL_PTRCALL_ARGS ".set(");
- cpp_file.push_back(real_argc_str);
- cpp_file.push_back(" + i, &varargs[i]);\n\t" CLOSE_BLOCK);
- } else {
- cpp_file.push_back(c_in_statements);
- cpp_file.push_back("\tconst void* " C_LOCAL_PTRCALL_ARGS "[");
- cpp_file.push_back(argc_str + "] = { ");
- cpp_file.push_back(c_args_var_content + " };\n");
- }
- }
+ List<String> output;
- if (imethod.is_vararg) {
- cpp_file.push_back("\tVariant::CallError vcall_error;\n\t");
+ output.push_back("#include \"" GLUE_HEADER_FILE "\"\n"
+ "\n");
- if (!ret_void)
- cpp_file.push_back(LOCAL_RET " = ");
+ generated_icall_funcs.clear();
- cpp_file.push_back(CS_PARAM_METHODBIND "->call(" CS_PARAM_INSTANCE ", ");
- cpp_file.push_back(imethod.arguments.size() ? "(const Variant**)" C_LOCAL_PTRCALL_ARGS ".ptr()" : "NULL");
- cpp_file.push_back(", total_length, vcall_error);\n");
- } else {
- cpp_file.push_back("\t" CS_PARAM_METHODBIND "->ptrcall(" CS_PARAM_INSTANCE ", ");
- cpp_file.push_back(imethod.arguments.size() ? C_LOCAL_PTRCALL_ARGS ", " : "NULL, ");
- cpp_file.push_back(!ret_void ? "&" LOCAL_RET ");\n" : "NULL);\n");
- }
+ for (Map<String, TypeInterface>::Element *type_elem = obj_types.front(); type_elem; type_elem = type_elem->next()) {
+ const TypeInterface &itype = type_elem->get();
- if (!ret_void) {
- if (return_type->c_out.empty())
- cpp_file.push_back("\treturn " LOCAL_RET ";\n");
- else
- cpp_file.push_back(sformat(return_type->c_out, return_type->c_type_out, LOCAL_RET, return_type->name));
- }
+ List<InternalCall> &custom_icalls = itype.api_type == ClassDB::API_EDITOR ? editor_custom_icalls : core_custom_icalls;
+
+ OS::get_singleton()->print(String("Generating " + itype.name + "...\n").utf8());
- cpp_file.push_back(CLOSE_BLOCK "\n");
+ String ctor_method(ICALL_PREFIX + itype.proxy_name + "_Ctor");
- if (im_icall->editor_only)
- cpp_file.push_back("#endif // TOOLS_ENABLED\n");
+ for (const List<MethodInterface>::Element *E = itype.methods.front(); E; E = E->next()) {
+ const MethodInterface &imethod = E->get();
+ Error method_err = _generate_glue_method(itype, imethod, output);
+ if (method_err != OK) {
+ ERR_EXPLAIN("Failed to generate method '" + imethod.name + "' for class '" + itype.name + "'");
+ ERR_FAIL_V(method_err);
}
}
@@ -1296,11 +1167,11 @@ Error BindingsGenerator::generate_glue(const String &p_output_dir) {
if (!find_icall_by_name(singleton_icall.name, custom_icalls))
custom_icalls.push_back(singleton_icall);
- cpp_file.push_back("Object* ");
- cpp_file.push_back(singleton_icall_name);
- cpp_file.push_back("() " OPEN_BLOCK "\treturn ProjectSettings::get_singleton()->get_singleton_object(\"");
- cpp_file.push_back(itype.proxy_name);
- cpp_file.push_back("\");\n" CLOSE_BLOCK "\n");
+ output.push_back("Object* ");
+ output.push_back(singleton_icall_name);
+ output.push_back("() " OPEN_BLOCK "\treturn ProjectSettings::get_singleton()->get_singleton_object(\"");
+ output.push_back(itype.proxy_name);
+ output.push_back("\");\n" CLOSE_BLOCK "\n");
}
if (itype.is_instantiable) {
@@ -1309,36 +1180,36 @@ Error BindingsGenerator::generate_glue(const String &p_output_dir) {
if (!find_icall_by_name(ctor_icall.name, custom_icalls))
custom_icalls.push_back(ctor_icall);
- cpp_file.push_back("Object* ");
- cpp_file.push_back(ctor_method);
- cpp_file.push_back("(MonoObject* obj) " OPEN_BLOCK
- "\t" C_MACRO_OBJECT_CONSTRUCT "(instance, \"");
- cpp_file.push_back(itype.name);
- cpp_file.push_back("\");\n"
- "\t" C_METHOD_TIE_MANAGED_TO_UNMANAGED "(obj, instance);\n"
- "\treturn instance;\n" CLOSE_BLOCK "\n");
+ output.push_back("Object* ");
+ output.push_back(ctor_method);
+ output.push_back("(MonoObject* obj) " OPEN_BLOCK
+ "\t" C_MACRO_OBJECT_CONSTRUCT "(instance, \"");
+ output.push_back(itype.name);
+ output.push_back("\");\n"
+ "\t" C_METHOD_TIE_MANAGED_TO_UNMANAGED "(obj, instance);\n"
+ "\treturn instance;\n" CLOSE_BLOCK "\n");
}
}
- cpp_file.push_back("namespace GodotSharpBindings\n" OPEN_BLOCK);
- cpp_file.push_back("uint64_t get_core_api_hash() { return ");
- cpp_file.push_back(itos(GDMono::get_singleton()->get_api_core_hash()) + "; }\n");
- cpp_file.push_back("#ifdef TOOLS_ENABLED\n"
- "uint64_t get_editor_api_hash() { return ");
- cpp_file.push_back(itos(GDMono::get_singleton()->get_api_editor_hash()) +
- "; }\n#endif // TOOLS_ENABLED\n");
- cpp_file.push_back("void register_generated_icalls() " OPEN_BLOCK);
-
-#define ADD_INTERNAL_CALL_REGISTRATION(m_icall) \
- { \
- cpp_file.push_back("\tmono_add_internal_call("); \
- cpp_file.push_back("\"" BINDINGS_NAMESPACE "."); \
- cpp_file.push_back(m_icall.editor_only ? CS_CLASS_NATIVECALLS_EDITOR : CS_CLASS_NATIVECALLS); \
- cpp_file.push_back("::"); \
- cpp_file.push_back(m_icall.name); \
- cpp_file.push_back("\", (void*)"); \
- cpp_file.push_back(m_icall.name); \
- cpp_file.push_back(");\n"); \
+ output.push_back("namespace GodotSharpBindings\n" OPEN_BLOCK);
+ output.push_back("uint64_t get_core_api_hash() { return ");
+ output.push_back(itos(GDMono::get_singleton()->get_api_core_hash()) + "; }\n");
+ output.push_back("#ifdef TOOLS_ENABLED\n"
+ "uint64_t get_editor_api_hash() { return ");
+ output.push_back(itos(GDMono::get_singleton()->get_api_editor_hash()) +
+ "; }\n#endif // TOOLS_ENABLED\n");
+ output.push_back("void register_generated_icalls() " OPEN_BLOCK);
+
+#define ADD_INTERNAL_CALL_REGISTRATION(m_icall) \
+ { \
+ output.push_back("\tmono_add_internal_call("); \
+ output.push_back("\"" BINDINGS_NAMESPACE "."); \
+ output.push_back(m_icall.editor_only ? CS_CLASS_NATIVECALLS_EDITOR : CS_CLASS_NATIVECALLS); \
+ output.push_back("::"); \
+ output.push_back(m_icall.name); \
+ output.push_back("\", (void*)"); \
+ output.push_back(m_icall.name); \
+ output.push_back(");\n"); \
}
bool tools_sequence = false;
@@ -1347,11 +1218,11 @@ Error BindingsGenerator::generate_glue(const String &p_output_dir) {
if (tools_sequence) {
if (!E->get().editor_only) {
tools_sequence = false;
- cpp_file.push_back("#endif\n");
+ output.push_back("#endif\n");
}
} else {
if (E->get().editor_only) {
- cpp_file.push_back("#ifdef TOOLS_ENABLED\n");
+ output.push_back("#ifdef TOOLS_ENABLED\n");
tools_sequence = true;
}
}
@@ -1361,24 +1232,23 @@ Error BindingsGenerator::generate_glue(const String &p_output_dir) {
if (tools_sequence) {
tools_sequence = false;
- cpp_file.push_back("#endif\n");
+ output.push_back("#endif\n");
}
- cpp_file.push_back("#ifdef TOOLS_ENABLED\n");
+ output.push_back("#ifdef TOOLS_ENABLED\n");
for (const List<InternalCall>::Element *E = editor_custom_icalls.front(); E; E = E->next())
ADD_INTERNAL_CALL_REGISTRATION(E->get());
- cpp_file.push_back("#endif // TOOLS_ENABLED\n");
+ output.push_back("#endif // TOOLS_ENABLED\n");
for (const List<InternalCall>::Element *E = method_icalls.front(); E; E = E->next()) {
-
if (tools_sequence) {
if (!E->get().editor_only) {
tools_sequence = false;
- cpp_file.push_back("#endif\n");
+ output.push_back("#endif\n");
}
} else {
if (E->get().editor_only) {
- cpp_file.push_back("#ifdef TOOLS_ENABLED\n");
+ output.push_back("#ifdef TOOLS_ENABLED\n");
tools_sequence = true;
}
}
@@ -1388,18 +1258,18 @@ Error BindingsGenerator::generate_glue(const String &p_output_dir) {
if (tools_sequence) {
tools_sequence = false;
- cpp_file.push_back("#endif\n");
+ output.push_back("#endif\n");
}
#undef ADD_INTERNAL_CALL_REGISTRATION
- cpp_file.push_back(CLOSE_BLOCK "}\n");
+ output.push_back(CLOSE_BLOCK "}\n");
- Error save_err = _save_file(path_join(p_output_dir, "mono_glue.gen.cpp"), cpp_file);
+ Error save_err = _save_file(path_join(p_output_dir, "mono_glue.gen.cpp"), output);
if (save_err != OK)
return save_err;
- OS::get_singleton()->print("Mono glue generated successfully!\n");
+ OS::get_singleton()->print("Mono glue generated successfully\n");
return OK;
}
@@ -1420,6 +1290,163 @@ Error BindingsGenerator::_save_file(const String &p_path, const List<String> &p_
return OK;
}
+Error BindingsGenerator::_generate_glue_method(const BindingsGenerator::TypeInterface &p_itype, const BindingsGenerator::MethodInterface &p_imethod, List<String> &p_output) {
+
+ if (p_imethod.is_virtual)
+ return OK; // Ignore
+
+ bool ret_void = p_imethod.return_type == "void";
+
+ const TypeInterface *return_type = _get_type_by_name_or_placeholder(p_imethod.return_type);
+
+ String argc_str = itos(p_imethod.arguments.size());
+
+ String c_func_sig = "MethodBind* " CS_PARAM_METHODBIND ", " + p_itype.c_type_in + " " CS_PARAM_INSTANCE;
+ String c_in_statements;
+ String c_args_var_content;
+
+ // Get arguments information
+ int i = 0;
+ for (const List<ArgumentInterface>::Element *F = p_imethod.arguments.front(); F; F = F->next()) {
+ const ArgumentInterface &iarg = F->get();
+ const TypeInterface *arg_type = _get_type_by_name_or_placeholder(iarg.type);
+
+ String c_param_name = "arg" + itos(i + 1);
+
+ if (p_imethod.is_vararg) {
+ if (i < p_imethod.arguments.size() - 1) {
+ c_in_statements += sformat(arg_type->c_in.size() ? arg_type->c_in : TypeInterface::DEFAULT_VARARG_C_IN, "Variant", c_param_name);
+ c_in_statements += "\t" C_LOCAL_PTRCALL_ARGS ".set(0, ";
+ c_in_statements += sformat("&%s_in", c_param_name);
+ c_in_statements += ");\n";
+ }
+ } else {
+ if (i > 0)
+ c_args_var_content += ", ";
+ if (arg_type->c_in.size())
+ c_in_statements += sformat(arg_type->c_in, arg_type->c_type, c_param_name);
+ c_args_var_content += sformat(arg_type->c_arg_in, c_param_name);
+ }
+
+ c_func_sig += ", ";
+ c_func_sig += arg_type->c_type_in;
+ c_func_sig += " ";
+ c_func_sig += c_param_name;
+
+ i++;
+ }
+
+ const Map<const MethodInterface *, const InternalCall *>::Element *match = method_icalls_map.find(&p_imethod);
+ ERR_FAIL_NULL_V(match, ERR_BUG);
+
+ const InternalCall *im_icall = match->value();
+ String icall_method = im_icall->name;
+
+ if (!generated_icall_funcs.find(im_icall)) {
+ generated_icall_funcs.push_back(im_icall);
+
+ if (im_icall->editor_only)
+ p_output.push_back("#ifdef TOOLS_ENABLED\n");
+
+ // Generate icall function
+
+ p_output.push_back(ret_void ? "void " : return_type->c_type_out + " ");
+ p_output.push_back(icall_method);
+ p_output.push_back("(");
+ p_output.push_back(c_func_sig);
+ p_output.push_back(") " OPEN_BLOCK);
+
+ String fail_ret = ret_void ? "" : ", " + (return_type->c_type_out.ends_with("*") ? "NULL" : return_type->c_type_out + "()");
+
+ if (!ret_void) {
+ String ptrcall_return_type;
+ String initialization;
+
+ if (return_type->is_object_type) {
+ ptrcall_return_type = return_type->is_reference ? "Ref<Reference>" : return_type->c_type;
+ initialization = return_type->is_reference ? "" : " = NULL";
+ } else {
+ ptrcall_return_type = return_type->c_type;
+ }
+
+ p_output.push_back("\t" + ptrcall_return_type);
+ p_output.push_back(" " LOCAL_RET);
+ p_output.push_back(initialization + ";\n");
+ p_output.push_back("\tERR_FAIL_NULL_V(" CS_PARAM_INSTANCE);
+ p_output.push_back(fail_ret);
+ p_output.push_back(");\n");
+ } else {
+ p_output.push_back("\tERR_FAIL_NULL(" CS_PARAM_INSTANCE ");\n");
+ }
+
+ if (p_imethod.arguments.size()) {
+ if (p_imethod.is_vararg) {
+ String err_fail_macro = ret_void ? "ERR_FAIL_COND" : "ERR_FAIL_COND_V";
+ String vararg_arg = "arg" + argc_str;
+ String real_argc_str = itos(p_imethod.arguments.size() - 1); // Arguments count without vararg
+
+ p_output.push_back("\tVector<Variant> varargs;\n"
+ "\tint vararg_length = mono_array_length(");
+ p_output.push_back(vararg_arg);
+ p_output.push_back(");\n\tint total_length = ");
+ p_output.push_back(real_argc_str);
+ p_output.push_back(" + vararg_length;\n\t");
+ p_output.push_back(err_fail_macro);
+ p_output.push_back("(varargs.resize(vararg_length) != OK");
+ p_output.push_back(fail_ret);
+ p_output.push_back(");\n\tVector<Variant*> " C_LOCAL_PTRCALL_ARGS ";\n\t");
+ p_output.push_back(err_fail_macro);
+ p_output.push_back("(call_args.resize(total_length) != OK");
+ p_output.push_back(fail_ret);
+ p_output.push_back(");\n");
+ p_output.push_back(c_in_statements);
+ p_output.push_back("\tfor (int i = 0; i < vararg_length; i++) " OPEN_BLOCK
+ "\t\tMonoObject* elem = mono_array_get(");
+ p_output.push_back(vararg_arg);
+ p_output.push_back(", MonoObject*, i);\n"
+ "\t\tvarargs.set(i, GDMonoMarshal::mono_object_to_variant(elem));\n"
+ "\t\t" C_LOCAL_PTRCALL_ARGS ".set(");
+ p_output.push_back(real_argc_str);
+ p_output.push_back(" + i, &varargs[i]);\n\t" CLOSE_BLOCK);
+ } else {
+ p_output.push_back(c_in_statements);
+ p_output.push_back("\tconst void* " C_LOCAL_PTRCALL_ARGS "[");
+ p_output.push_back(argc_str + "] = { ");
+ p_output.push_back(c_args_var_content + " };\n");
+ }
+ }
+
+ if (p_imethod.is_vararg) {
+ p_output.push_back("\tVariant::CallError vcall_error;\n\t");
+
+ if (!ret_void)
+ p_output.push_back(LOCAL_RET " = ");
+
+ p_output.push_back(CS_PARAM_METHODBIND "->call(" CS_PARAM_INSTANCE ", ");
+ p_output.push_back(p_imethod.arguments.size() ? "(const Variant**)" C_LOCAL_PTRCALL_ARGS ".ptr()" : "NULL");
+ p_output.push_back(", total_length, vcall_error);\n");
+ } else {
+ p_output.push_back("\t" CS_PARAM_METHODBIND "->ptrcall(" CS_PARAM_INSTANCE ", ");
+ p_output.push_back(p_imethod.arguments.size() ? C_LOCAL_PTRCALL_ARGS ", " : "NULL, ");
+ p_output.push_back(!ret_void ? "&" LOCAL_RET ");\n" : "NULL);\n");
+ }
+
+ if (!ret_void) {
+ if (return_type->c_out.empty())
+ p_output.push_back("\treturn " LOCAL_RET ";\n");
+ else
+ p_output.push_back(sformat(return_type->c_out, return_type->c_type_out, LOCAL_RET, return_type->name));
+ }
+
+ p_output.push_back(CLOSE_BLOCK "\n");
+
+ if (im_icall->editor_only)
+ p_output.push_back("#endif // TOOLS_ENABLED\n");
+ }
+
+ return OK;
+}
+
const BindingsGenerator::TypeInterface *BindingsGenerator::_get_type_by_name_or_null(const String &p_name) {
const Map<String, TypeInterface>::Element *match = builtin_types.find(p_name);
@@ -2077,7 +2104,8 @@ void BindingsGenerator::handle_cmdline_args(const List<String> &p_cmdline_args)
const List<String>::Element *path_elem = elem->next();
if (path_elem) {
- get_singleton().generate_glue(path_elem->get());
+ if (get_singleton().generate_glue(path_elem->get()) != OK)
+ ERR_PRINT("Mono glue generation failed");
elem = elem->next();
} else {
ERR_PRINTS("--generate-mono-glue: No output directory specified");
@@ -2090,7 +2118,8 @@ void BindingsGenerator::handle_cmdline_args(const List<String> &p_cmdline_args)
const List<String>::Element *path_elem = elem->next();
if (path_elem) {
- get_singleton().generate_cs_core_project(path_elem->get());
+ if (get_singleton().generate_cs_core_project(path_elem->get()) != OK)
+ ERR_PRINT("Generation of solution and C# project for the Core API failed");
elem = elem->next();
} else {
ERR_PRINTS(cs_core_api_option + ": No output directory specified");
@@ -2104,7 +2133,8 @@ void BindingsGenerator::handle_cmdline_args(const List<String> &p_cmdline_args)
if (path_elem) {
if (path_elem->next()) {
- get_singleton().generate_cs_editor_project(path_elem->get(), path_elem->next()->get());
+ if (get_singleton().generate_cs_editor_project(path_elem->get(), path_elem->next()->get()) != OK)
+ ERR_PRINT("Generation of solution and C# project for the Editor API failed");
elem = path_elem->next();
} else {
ERR_PRINTS(cs_editor_api_option + ": No hint path for the Core API dll specified");
diff --git a/modules/mono/editor/bindings_generator.h b/modules/mono/editor/bindings_generator.h
index 437a566556..dfa3aa9911 100644
--- a/modules/mono/editor/bindings_generator.h
+++ b/modules/mono/editor/bindings_generator.h
@@ -368,6 +368,8 @@ class BindingsGenerator {
List<InternalCall> method_icalls;
Map<const MethodInterface *, const InternalCall *> method_icalls_map;
+ List<const InternalCall *> generated_icall_funcs;
+
List<InternalCall> core_custom_icalls;
List<InternalCall> editor_custom_icalls;
@@ -404,6 +406,11 @@ class BindingsGenerator {
Error _generate_cs_type(const TypeInterface &itype, const String &p_output_file);
+ Error _generate_cs_property(const TypeInterface &p_itype, const DocData::PropertyDoc &p_prop_doc, List<String> &p_output);
+ Error _generate_cs_method(const TypeInterface &p_itype, const MethodInterface &p_imethod, int &p_method_bind_count, List<String> &p_output);
+
+ Error _generate_glue_method(const TypeInterface &p_itype, const MethodInterface &p_imethod, List<String> &p_output);
+
Error _save_file(const String &path, const List<String> &content);
BindingsGenerator();
diff --git a/modules/mono/editor/godotsharp_builds.cpp b/modules/mono/editor/godotsharp_builds.cpp
index d3808769fb..a26f36d64f 100644
--- a/modules/mono/editor/godotsharp_builds.cpp
+++ b/modules/mono/editor/godotsharp_builds.cpp
@@ -32,6 +32,7 @@
#include "main/main.h"
#include "../godotsharp_dirs.h"
+#include "../mono_gd/gd_mono.h"
#include "../mono_gd/gd_mono_class.h"
#include "../mono_gd/gd_mono_marshal.h"
#include "../utils/path_utils.h"
@@ -52,6 +53,10 @@ String _find_build_engine_on_unix(const String &p_name) {
if (ret.length())
return ret;
+ String ret_fallback = path_which(p_name + ".exe");
+ if (ret_fallback.length())
+ return ret_fallback;
+
const char *locations[] = {
#ifdef OSX_ENABLED
"/Library/Frameworks/Mono.framework/Versions/Current/bin/",
@@ -60,10 +65,10 @@ String _find_build_engine_on_unix(const String &p_name) {
};
for (int i = 0; i < sizeof(locations) / sizeof(const char *); i++) {
- String location = locations[i];
+ String hint_path = locations[i] + p_name;
- if (FileAccess::exists(location + p_name)) {
- return location;
+ if (FileAccess::exists(hint_path)) {
+ return hint_path;
}
}
@@ -71,7 +76,7 @@ String _find_build_engine_on_unix(const String &p_name) {
}
#endif
-MonoString *godot_icall_BuildInstance_get_MSBuildPath() {
+void godot_icall_BuildInstance_get_MSBuildInfo(MonoString **r_msbuild_path, MonoString **r_framework_path) {
GodotSharpBuilds::BuildTool build_tool = GodotSharpBuilds::BuildTool(int(EditorSettings::get_singleton()->get("mono/builds/build_tool")));
@@ -84,11 +89,23 @@ MonoString *godot_icall_BuildInstance_get_MSBuildPath() {
if (!msbuild_tools_path.ends_with("\\"))
msbuild_tools_path += "\\";
- return GDMonoMarshal::mono_string_from_godot(msbuild_tools_path + "MSBuild.exe");
+ // FrameworkPathOverride
+ const MonoRegInfo &mono_reg_info = GDMono::get_singleton()->get_mono_reg_info();
+ if (mono_reg_info.assembly_dir.length()) {
+ *r_msbuild_path = GDMonoMarshal::mono_string_from_godot(msbuild_tools_path + "MSBuild.exe");
+
+ String framework_path = path_join(mono_reg_info.assembly_dir, "mono", "4.5");
+ *r_framework_path = GDMonoMarshal::mono_string_from_godot(framework_path);
+ } else {
+ ERR_PRINT("Cannot find Mono's assemblies directory in the registry");
+ }
+
+ return;
}
- OS::get_singleton()->print("Cannot find System's MSBuild. Trying with Mono's...\n");
- }
+ if (OS::get_singleton()->is_stdout_verbose())
+ OS::get_singleton()->print("Cannot find System's MSBuild. Trying with Mono's...\n");
+ } // fall through
case GodotSharpBuilds::MSBUILD_MONO: {
String msbuild_path = GDMono::get_singleton()->get_mono_reg_info().bin_dir.plus_file("msbuild.bat");
@@ -96,17 +113,10 @@ MonoString *godot_icall_BuildInstance_get_MSBuildPath() {
WARN_PRINTS("Cannot find msbuild ('mono/builds/build_tool'). Tried with path: " + msbuild_path);
}
- return GDMonoMarshal::mono_string_from_godot(msbuild_path);
- }
- case GodotSharpBuilds::XBUILD: {
- String xbuild_path = GDMono::get_singleton()->get_mono_reg_info().bin_dir.plus_file("xbuild.bat");
-
- if (!FileAccess::exists(xbuild_path)) {
- WARN_PRINTS("Cannot find xbuild ('mono/builds/build_tool'). Tried with path: " + xbuild_path);
- }
+ *r_msbuild_path = GDMonoMarshal::mono_string_from_godot(msbuild_path);
- return GDMonoMarshal::mono_string_from_godot(xbuild_path);
- }
+ return;
+ } break;
default:
ERR_EXPLAIN("You don't deserve to live");
CRASH_NOW();
@@ -118,25 +128,28 @@ MonoString *godot_icall_BuildInstance_get_MSBuildPath() {
if (build_tool != GodotSharpBuilds::XBUILD) {
if (msbuild_path.empty()) {
WARN_PRINT("Cannot find msbuild ('mono/builds/build_tool').");
- return NULL;
+ return;
}
} else {
if (xbuild_path.empty()) {
WARN_PRINT("Cannot find xbuild ('mono/builds/build_tool').");
- return NULL;
+ return;
}
}
- return GDMonoMarshal::mono_string_from_godot(build_tool != GodotSharpBuilds::XBUILD ? msbuild_path : xbuild_path);
+ *r_msbuild_path = GDMonoMarshal::mono_string_from_godot(build_tool != GodotSharpBuilds::XBUILD ? msbuild_path : xbuild_path);
+
+ return;
#else
- return NULL;
+ ERR_PRINT("Not implemented on this platform");
+ return;
#endif
}
void GodotSharpBuilds::_register_internal_calls() {
mono_add_internal_call("GodotSharpTools.Build.BuildSystem::godot_icall_BuildInstance_ExitCallback", (void *)godot_icall_BuildInstance_ExitCallback);
- mono_add_internal_call("GodotSharpTools.Build.BuildInstance::godot_icall_BuildInstance_get_MSBuildPath", (void *)godot_icall_BuildInstance_get_MSBuildPath);
+ mono_add_internal_call("GodotSharpTools.Build.BuildInstance::godot_icall_BuildInstance_get_MSBuildInfo", (void *)godot_icall_BuildInstance_get_MSBuildInfo);
}
void GodotSharpBuilds::show_build_error_dialog(const String &p_message) {
@@ -269,7 +282,7 @@ bool GodotSharpBuilds::make_api_sln(GodotSharpBuilds::APIType p_api_type) {
return true;
}
-bool godotsharp_build_callback() {
+bool GodotSharpBuilds::build_project_blocking() {
if (!FileAccess::exists(GodotSharpDirs::get_project_sln_path()))
return true; // No solution to build
@@ -348,14 +361,27 @@ GodotSharpBuilds::GodotSharpBuilds() {
singleton = this;
- EditorNode::get_singleton()->add_build_callback(&godotsharp_build_callback);
+ EditorNode::get_singleton()->add_build_callback(&GodotSharpBuilds::build_project_blocking);
// Build tool settings
EditorSettings *ed_settings = EditorSettings::get_singleton();
if (!ed_settings->has_setting("mono/builds/build_tool")) {
- ed_settings->set_setting("mono/builds/build_tool", MSBUILD);
+ ed_settings->set_setting("mono/builds/build_tool",
+#ifdef WINDOWS_ENABLED
+ // TODO: Default to MSBUILD_MONO if its csc.exe issue is fixed in the installed mono version
+ MSBUILD
+#else
+ MSBUILD_MONO
+#endif
+ );
}
- ed_settings->add_property_hint(PropertyInfo(Variant::INT, "mono/builds/build_tool", PROPERTY_HINT_ENUM, "MSBuild (System),MSBuild (Mono),xbuild"));
+ ed_settings->add_property_hint(PropertyInfo(Variant::INT, "mono/builds/build_tool", PROPERTY_HINT_ENUM,
+#ifdef WINDOWS_ENABLED
+ "MSBuild (Mono),MSBuild (System)"
+#else
+ "MSBuild (Mono),xbuild (Deprecated)"
+#endif
+ ));
}
GodotSharpBuilds::~GodotSharpBuilds() {
diff --git a/modules/mono/editor/godotsharp_builds.h b/modules/mono/editor/godotsharp_builds.h
index 6d5fa3b44a..7d2f38a774 100644
--- a/modules/mono/editor/godotsharp_builds.h
+++ b/modules/mono/editor/godotsharp_builds.h
@@ -67,9 +67,12 @@ public:
};
enum BuildTool {
- MSBUILD,
MSBUILD_MONO,
- XBUILD
+#ifdef WINDOWS_ENABLED
+ MSBUILD
+#else
+ XBUILD // Deprecated
+#endif
};
_FORCE_INLINE_ static GodotSharpBuilds *get_singleton() { return singleton; }
@@ -89,6 +92,8 @@ public:
static bool make_api_sln(APIType p_api_type);
+ static bool build_project_blocking();
+
GodotSharpBuilds();
~GodotSharpBuilds();
};
diff --git a/modules/mono/editor/godotsharp_editor.cpp b/modules/mono/editor/godotsharp_editor.cpp
index 30e7653256..837dbfde66 100644
--- a/modules/mono/editor/godotsharp_editor.cpp
+++ b/modules/mono/editor/godotsharp_editor.cpp
@@ -46,21 +46,6 @@
#include "../utils/mono_reg_utils.h"
#endif
-class MonoReloadNode : public Node {
- GDCLASS(MonoReloadNode, Node)
-
-protected:
- void _notification(int p_what) {
- switch (p_what) {
- case MainLoop::NOTIFICATION_WM_FOCUS_IN: {
- CSharpLanguage::get_singleton()->reload_assemblies_if_needed(true);
- } break;
- default: {
- } break;
- };
- }
-};
-
GodotSharpEditor *GodotSharpEditor::singleton = NULL;
bool GodotSharpEditor::_create_project_solution() {
@@ -71,6 +56,10 @@ bool GodotSharpEditor::_create_project_solution() {
String path = OS::get_singleton()->get_resource_dir();
String name = ProjectSettings::get_singleton()->get("application/config/name");
+ if (name.empty()) {
+ name = "UnnamedProject";
+ }
+
String guid = CSharpProject::generate_game_project(path, name);
if (guid.length()) {
@@ -182,11 +171,6 @@ Error GodotSharpEditor::open_in_external_editor(const Ref<Script> &p_script, int
String script_path = ProjectSettings::get_singleton()->globalize_path(p_script->get_path());
monodevel_instance->execute(script_path);
} break;
- case EDITOR_VISUAL_STUDIO:
- // TODO
- // devenv <PathToSolutionFolder>
- // devenv /edit <PathToCsFile> /command "edit.goto <Line>"
- // HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\VisualStudio\SxS\VS7
default:
return ERR_UNAVAILABLE;
}
@@ -240,7 +224,7 @@ GodotSharpEditor::GodotSharpEditor(EditorNode *p_editor) {
if (!ed_settings->has_setting("mono/editor/external_editor")) {
ed_settings->set_setting("mono/editor/external_editor", EDITOR_NONE);
}
- ed_settings->add_property_hint(PropertyInfo(Variant::INT, "mono/editor/external_editor", PROPERTY_HINT_ENUM, "None,MonoDevelop,Visual Studio,Visual Studio Code"));
+ ed_settings->add_property_hint(PropertyInfo(Variant::INT, "mono/editor/external_editor", PROPERTY_HINT_ENUM, "None,MonoDevelop,Visual Studio Code"));
}
GodotSharpEditor::~GodotSharpEditor() {
@@ -254,3 +238,49 @@ GodotSharpEditor::~GodotSharpEditor() {
monodevel_instance = NULL;
}
}
+
+MonoReloadNode *MonoReloadNode::singleton = NULL;
+
+void MonoReloadNode::_reload_timer_timeout() {
+
+ CSharpLanguage::get_singleton()->reload_assemblies_if_needed(false);
+}
+
+void MonoReloadNode::restart_reload_timer() {
+
+ reload_timer->stop();
+ reload_timer->start();
+}
+
+void MonoReloadNode::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("_reload_timer_timeout"), &MonoReloadNode::_reload_timer_timeout);
+}
+
+void MonoReloadNode::_notification(int p_what) {
+ switch (p_what) {
+ case MainLoop::NOTIFICATION_WM_FOCUS_IN: {
+ restart_reload_timer();
+ CSharpLanguage::get_singleton()->reload_assemblies_if_needed(true);
+ } break;
+ default: {
+ } break;
+ };
+}
+
+MonoReloadNode::MonoReloadNode() {
+
+ singleton = this;
+
+ reload_timer = memnew(Timer);
+ add_child(reload_timer);
+ reload_timer->set_one_shot(false);
+ reload_timer->set_wait_time(EDITOR_DEF("mono/assembly_watch_interval_sec", 0.5));
+ reload_timer->connect("timeout", this, "_reload_timer_timeout");
+ reload_timer->start();
+}
+
+MonoReloadNode::~MonoReloadNode() {
+
+ singleton = NULL;
+}
diff --git a/modules/mono/editor/godotsharp_editor.h b/modules/mono/editor/godotsharp_editor.h
index 1ecb8c7a94..0f2c163582 100644
--- a/modules/mono/editor/godotsharp_editor.h
+++ b/modules/mono/editor/godotsharp_editor.h
@@ -69,7 +69,6 @@ public:
enum ExternalEditor {
EDITOR_NONE,
EDITOR_MONODEVELOP,
- EDITOR_VISUAL_STUDIO,
EDITOR_CODE,
};
@@ -84,4 +83,27 @@ public:
~GodotSharpEditor();
};
+class MonoReloadNode : public Node {
+ GDCLASS(MonoReloadNode, Node)
+
+ Timer *reload_timer;
+
+ void _reload_timer_timeout();
+
+ static MonoReloadNode *singleton;
+
+protected:
+ static void _bind_methods();
+
+ void _notification(int p_what);
+
+public:
+ _FORCE_INLINE_ static MonoReloadNode *get_singleton() { return singleton; }
+
+ void restart_reload_timer();
+
+ MonoReloadNode();
+ ~MonoReloadNode();
+};
+
#endif // GODOTSHARP_EDITOR_H
diff --git a/modules/mono/editor/mono_bottom_panel.cpp b/modules/mono/editor/mono_bottom_panel.cpp
index 8d6a618ee8..31dc09856a 100644
--- a/modules/mono/editor/mono_bottom_panel.cpp
+++ b/modules/mono/editor/mono_bottom_panel.cpp
@@ -139,6 +139,14 @@ void MonoBottomPanel::_errors_toggled(bool p_pressed) {
build_tab->_update_issues_list();
}
+void MonoBottomPanel::_build_project_pressed() {
+
+ GodotSharpBuilds::get_singleton()->build_project_blocking();
+
+ MonoReloadNode::get_singleton()->restart_reload_timer();
+ CSharpLanguage::get_singleton()->reload_assemblies_if_needed(true);
+}
+
void MonoBottomPanel::_notification(int p_what) {
switch (p_what) {
@@ -153,6 +161,7 @@ void MonoBottomPanel::_notification(int p_what) {
void MonoBottomPanel::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("_build_project_pressed"), &MonoBottomPanel::_build_project_pressed);
ClassDB::bind_method(D_METHOD("_warnings_toggled", "pressed"), &MonoBottomPanel::_warnings_toggled);
ClassDB::bind_method(D_METHOD("_errors_toggled", "pressed"), &MonoBottomPanel::_errors_toggled);
ClassDB::bind_method(D_METHOD("_build_tab_item_selected", "idx"), &MonoBottomPanel::_build_tab_item_selected);
@@ -187,6 +196,12 @@ MonoBottomPanel::MonoBottomPanel(EditorNode *p_editor) {
toolbar_hbc->set_h_size_flags(SIZE_EXPAND_FILL);
panel_builds_tab->add_child(toolbar_hbc);
+ ToolButton *build_project_btn = memnew(ToolButton);
+ build_project_btn->set_text("Build Project");
+ build_project_btn->set_focus_mode(FOCUS_NONE);
+ build_project_btn->connect("pressed", this, "_build_project_pressed");
+ toolbar_hbc->add_child(build_project_btn);
+
toolbar_hbc->add_spacer();
warnings_btn = memnew(ToolButton);
diff --git a/modules/mono/editor/mono_bottom_panel.h b/modules/mono/editor/mono_bottom_panel.h
index 83da5b9809..5cc4aa3240 100644
--- a/modules/mono/editor/mono_bottom_panel.h
+++ b/modules/mono/editor/mono_bottom_panel.h
@@ -61,6 +61,8 @@ class MonoBottomPanel : public VBoxContainer {
void _warnings_toggled(bool p_pressed);
void _errors_toggled(bool p_pressed);
+ void _build_project_pressed();
+
static MonoBottomPanel *singleton;
protected:
diff --git a/modules/mono/glue/cs_files/Color.cs b/modules/mono/glue/cs_files/Color.cs
index df88a46832..0a00f83d47 100644
--- a/modules/mono/glue/cs_files/Color.cs
+++ b/modules/mono/glue/cs_files/Color.cs
@@ -1,590 +1,590 @@
-using System;
-
-namespace Godot
-{
- public struct Color : IEquatable<Color>
- {
- public float r;
- public float g;
- public float b;
- public float a;
-
- public int r8
- {
- get
- {
- return (int)(r * 255.0f);
- }
- }
-
- public int g8
- {
- get
- {
- return (int)(g * 255.0f);
- }
- }
-
- public int b8
- {
- get
- {
- return (int)(b * 255.0f);
- }
- }
-
- public int a8
- {
- get
- {
- return (int)(a * 255.0f);
- }
- }
-
- public float h
- {
- get
- {
- float max = Mathf.max(r, Mathf.max(g, b));
- float min = Mathf.min(r, Mathf.min(g, b));
-
- float delta = max - min;
-
- if (delta == 0)
- return 0;
-
- float h;
-
- if (r == max)
- h = (g - b) / delta; // Between yellow & magenta
- else if (g == max)
- h = 2 + (b - r) / delta; // Between cyan & yellow
- else
- h = 4 + (r - g) / delta; // Between magenta & cyan
-
- h /= 6.0f;
-
- if (h < 0)
- h += 1.0f;
-
- return h;
- }
- set
- {
- this = from_hsv(value, s, v);
- }
- }
-
- public float s
- {
- get
- {
- float max = Mathf.max(r, Mathf.max(g, b));
- float min = Mathf.min(r, Mathf.min(g, b));
-
- float delta = max - min;
-
- return max != 0 ? delta / max : 0;
- }
- set
- {
- this = from_hsv(h, value, v);
- }
- }
-
- public float v
- {
- get
- {
- return Mathf.max(r, Mathf.max(g, b));
- }
- set
- {
- this = from_hsv(h, s, value);
- }
- }
-
- private static readonly Color black = new Color(0f, 0f, 0f, 1.0f);
-
- public Color Black
- {
- get
- {
- return black;
- }
- }
-
- public float this [int index]
- {
- get
- {
- switch (index)
- {
- case 0:
- return r;
- case 1:
- return g;
- case 2:
- return b;
- case 3:
- return a;
- default:
- throw new IndexOutOfRangeException();
- }
- }
- set
- {
- switch (index)
- {
- case 0:
- r = value;
- return;
- case 1:
- g = value;
- return;
- case 2:
- b = value;
- return;
- case 3:
- a = value;
- return;
- default:
- throw new IndexOutOfRangeException();
- }
- }
- }
-
- public static void to_hsv(Color color, out float hue, out float saturation, out float value)
- {
- int max = Mathf.max(color.r8, Mathf.max(color.g8, color.b8));
- int min = Mathf.min(color.r8, Mathf.min(color.g8, color.b8));
-
- float delta = max - min;
-
- if (delta == 0)
- {
- hue = 0;
- }
- else
- {
- if (color.r == max)
- hue = (color.g - color.b) / delta; // Between yellow & magenta
- else if (color.g == max)
- hue = 2 + (color.b - color.r) / delta; // Between cyan & yellow
- else
- hue = 4 + (color.r - color.g) / delta; // Between magenta & cyan
-
- hue /= 6.0f;
-
- if (hue < 0)
- hue += 1.0f;
- }
-
- saturation = (max == 0) ? 0 : 1f - (1f * min / max);
- value = max / 255f;
- }
-
- public static Color from_hsv(float hue, float saturation, float value, float alpha = 1.0f)
- {
- if (saturation == 0)
- {
- // acp_hromatic (grey)
- return new Color(value, value, value, alpha);
- }
-
- int i;
- float f, p, q, t;
-
- hue *= 6.0f;
- hue %= 6f;
- i = (int)hue;
-
- f = hue - i;
- p = value * (1 - saturation);
- q = value * (1 - saturation * f);
- t = value * (1 - saturation * (1 - f));
-
- switch (i)
- {
- case 0: // Red is the dominant color
- return new Color(value, t, p, alpha);
- case 1: // Green is the dominant color
- return new Color(q, value, p, alpha);
- case 2:
- return new Color(p, value, t, alpha);
- case 3: // Blue is the dominant color
- return new Color(p, q, value, alpha);
- case 4:
- return new Color(t, p, value, alpha);
- default: // (5) Red is the dominant color
- return new Color(value, p, q, alpha);
- }
- }
-
- public Color blend(Color over)
- {
- Color res;
-
- float sa = 1.0f - over.a;
- res.a = a * sa + over.a;
-
- if (res.a == 0)
- {
- return new Color(0, 0, 0, 0);
- }
- else
- {
- res.r = (r * a * sa + over.r * over.a) / res.a;
- res.g = (g * a * sa + over.g * over.a) / res.a;
- res.b = (b * a * sa + over.b * over.a) / res.a;
- }
-
- return res;
- }
-
- public Color contrasted()
- {
- return new Color(
- (r + 0.5f) % 1.0f,
- (g + 0.5f) % 1.0f,
- (b + 0.5f) % 1.0f
- );
- }
-
- public float gray()
- {
- return (r + g + b) / 3.0f;
- }
-
- public Color inverted()
- {
- return new Color(
- 1.0f - r,
- 1.0f - g,
- 1.0f - b
- );
- }
-
- public Color linear_interpolate(Color b, float t)
- {
- Color res = this;
-
- res.r += (t * (b.r - this.r));
- res.g += (t * (b.g - this.g));
- res.b += (t * (b.b - this.b));
- res.a += (t * (b.a - this.a));
-
- return res;
- }
-
- public int to_32()
- {
- int c = (byte)(a * 255);
- c <<= 8;
- c |= (byte)(r * 255);
- c <<= 8;
- c |= (byte)(g * 255);
- c <<= 8;
- c |= (byte)(b * 255);
-
- return c;
- }
-
- public int to_ARGB32()
- {
- int c = (byte)(a * 255);
- c <<= 8;
- c |= (byte)(r * 255);
- c <<= 8;
- c |= (byte)(g * 255);
- c <<= 8;
- c |= (byte)(b * 255);
-
- return c;
- }
-
- public string to_html(bool include_alpha = true)
- {
- String txt = string.Empty;
-
- txt += _to_hex(r);
- txt += _to_hex(g);
- txt += _to_hex(b);
-
- if (include_alpha)
- txt = _to_hex(a) + txt;
-
- return txt;
- }
-
- public Color(float r, float g, float b, float a = 1.0f)
- {
- this.r = r;
- this.g = g;
- this.b = b;
- this.a = a;
- }
-
- public Color(int rgba)
- {
- this.a = (rgba & 0xFF) / 255.0f;
- rgba >>= 8;
- this.b = (rgba & 0xFF) / 255.0f;
- rgba >>= 8;
- this.g = (rgba & 0xFF) / 255.0f;
- rgba >>= 8;
- this.r = (rgba & 0xFF) / 255.0f;
- }
-
- private static float _parse_col(string str, int ofs)
- {
- int ig = 0;
-
- for (int i = 0; i < 2; i++)
- {
- int c = str[i + ofs];
- int v = 0;
-
- if (c >= '0' && c <= '9')
- {
- v = c - '0';
- }
- else if (c >= 'a' && c <= 'f')
- {
- v = c - 'a';
- v += 10;
- }
- else if (c >= 'A' && c <= 'F')
- {
- v = c - 'A';
- v += 10;
- }
- else
- {
- return -1;
- }
-
- if (i == 0)
- ig += v * 16;
- else
- ig += v;
- }
-
- return ig;
- }
-
- private String _to_hex(float val)
- {
- int v = (int)Mathf.clamp(val * 255.0f, 0, 255);
-
- string ret = string.Empty;
-
- for (int i = 0; i < 2; i++)
- {
- char[] c = { (char)0, (char)0 };
- int lv = v & 0xF;
-
- if (lv < 10)
- c[0] = (char)('0' + lv);
- else
- c[0] = (char)('a' + lv - 10);
-
- v >>= 4;
- ret = c + ret;
- }
-
- return ret;
- }
-
- internal static bool html_is_valid(string color)
- {
- if (color.Length == 0)
- return false;
-
- if (color[0] == '#')
- color = color.Substring(1, color.Length - 1);
-
- bool alpha = false;
-
- if (color.Length == 8)
- alpha = true;
- else if (color.Length == 6)
- alpha = false;
- else
- return false;
-
- if (alpha)
- {
- if ((int)_parse_col(color, 0) < 0)
- return false;
- }
-
- int from = alpha ? 2 : 0;
-
- if ((int)_parse_col(color, from + 0) < 0)
- return false;
- if ((int)_parse_col(color, from + 2) < 0)
- return false;
- if ((int)_parse_col(color, from + 4) < 0)
- return false;
-
- return true;
- }
-
- public static Color Color8(byte r8, byte g8, byte b8, byte a8)
- {
- return new Color((float)r8 / 255f, (float)g8 / 255f, (float)b8 / 255f, (float)a8 / 255f);
- }
-
- public Color(string rgba)
- {
- if (rgba.Length == 0)
- {
- r = 0f;
- g = 0f;
- b = 0f;
- a = 1.0f;
- return;
- }
-
- if (rgba[0] == '#')
- rgba = rgba.Substring(1);
-
- bool alpha = false;
-
- if (rgba.Length == 8)
- {
- alpha = true;
- }
- else if (rgba.Length == 6)
- {
- alpha = false;
- }
- else
- {
- throw new ArgumentOutOfRangeException("Invalid color code. Length is " + rgba.Length + " but a length of 6 or 8 is expected: " + rgba);
- }
-
- if (alpha)
- {
- a = _parse_col(rgba, 0);
-
- if (a < 0)
- throw new ArgumentOutOfRangeException("Invalid color code. Alpha is " + a + " but zero or greater is expected: " + rgba);
- }
- else
- {
- a = 1.0f;
- }
-
- int from = alpha ? 2 : 0;
-
- r = _parse_col(rgba, from + 0);
-
- if (r < 0)
- throw new ArgumentOutOfRangeException("Invalid color code. Red is " + r + " but zero or greater is expected: " + rgba);
-
- g = _parse_col(rgba, from + 2);
-
- if (g < 0)
- throw new ArgumentOutOfRangeException("Invalid color code. Green is " + g + " but zero or greater is expected: " + rgba);
-
- b = _parse_col(rgba, from + 4);
-
- if (b < 0)
- throw new ArgumentOutOfRangeException("Invalid color code. Blue is " + b + " but zero or greater is expected: " + rgba);
- }
-
- public static bool operator ==(Color left, Color right)
- {
- return left.Equals(right);
- }
-
- public static bool operator !=(Color left, Color right)
- {
- return !left.Equals(right);
- }
-
- public static bool operator <(Color left, Color right)
- {
- if (left.r == right.r)
- {
- if (left.g == right.g)
- {
- if (left.b == right.b)
- return (left.a < right.a);
- else
- return (left.b < right.b);
- }
- else
- {
- return left.g < right.g;
- }
- }
-
- return left.r < right.r;
- }
-
- public static bool operator >(Color left, Color right)
- {
- if (left.r == right.r)
- {
- if (left.g == right.g)
- {
- if (left.b == right.b)
- return (left.a > right.a);
- else
- return (left.b > right.b);
- }
- else
- {
- return left.g > right.g;
- }
- }
-
- return left.r > right.r;
- }
-
- public override bool Equals(object obj)
- {
- if (obj is Color)
- {
- return Equals((Color)obj);
- }
-
- return false;
- }
-
- public bool Equals(Color other)
- {
- return r == other.r && g == other.g && b == other.b && a == other.a;
- }
-
- public override int GetHashCode()
- {
- return r.GetHashCode() ^ g.GetHashCode() ^ b.GetHashCode() ^ a.GetHashCode();
- }
-
- public override string ToString()
- {
- return String.Format("{0},{1},{2},{3}", new object[]
- {
- this.r.ToString(),
- this.g.ToString(),
- this.b.ToString(),
- this.a.ToString()
- });
- }
-
- public string ToString(string format)
- {
- return String.Format("{0},{1},{2},{3}", new object[]
- {
- this.r.ToString(format),
- this.g.ToString(format),
- this.b.ToString(format),
- this.a.ToString(format)
- });
- }
- }
-}
+using System;
+
+namespace Godot
+{
+ public struct Color : IEquatable<Color>
+ {
+ public float r;
+ public float g;
+ public float b;
+ public float a;
+
+ public int r8
+ {
+ get
+ {
+ return (int)(r * 255.0f);
+ }
+ }
+
+ public int g8
+ {
+ get
+ {
+ return (int)(g * 255.0f);
+ }
+ }
+
+ public int b8
+ {
+ get
+ {
+ return (int)(b * 255.0f);
+ }
+ }
+
+ public int a8
+ {
+ get
+ {
+ return (int)(a * 255.0f);
+ }
+ }
+
+ public float h
+ {
+ get
+ {
+ float max = Mathf.max(r, Mathf.max(g, b));
+ float min = Mathf.min(r, Mathf.min(g, b));
+
+ float delta = max - min;
+
+ if (delta == 0)
+ return 0;
+
+ float h;
+
+ if (r == max)
+ h = (g - b) / delta; // Between yellow & magenta
+ else if (g == max)
+ h = 2 + (b - r) / delta; // Between cyan & yellow
+ else
+ h = 4 + (r - g) / delta; // Between magenta & cyan
+
+ h /= 6.0f;
+
+ if (h < 0)
+ h += 1.0f;
+
+ return h;
+ }
+ set
+ {
+ this = from_hsv(value, s, v);
+ }
+ }
+
+ public float s
+ {
+ get
+ {
+ float max = Mathf.max(r, Mathf.max(g, b));
+ float min = Mathf.min(r, Mathf.min(g, b));
+
+ float delta = max - min;
+
+ return max != 0 ? delta / max : 0;
+ }
+ set
+ {
+ this = from_hsv(h, value, v);
+ }
+ }
+
+ public float v
+ {
+ get
+ {
+ return Mathf.max(r, Mathf.max(g, b));
+ }
+ set
+ {
+ this = from_hsv(h, s, value);
+ }
+ }
+
+ private static readonly Color black = new Color(0f, 0f, 0f, 1.0f);
+
+ public Color Black
+ {
+ get
+ {
+ return black;
+ }
+ }
+
+ public float this [int index]
+ {
+ get
+ {
+ switch (index)
+ {
+ case 0:
+ return r;
+ case 1:
+ return g;
+ case 2:
+ return b;
+ case 3:
+ return a;
+ default:
+ throw new IndexOutOfRangeException();
+ }
+ }
+ set
+ {
+ switch (index)
+ {
+ case 0:
+ r = value;
+ return;
+ case 1:
+ g = value;
+ return;
+ case 2:
+ b = value;
+ return;
+ case 3:
+ a = value;
+ return;
+ default:
+ throw new IndexOutOfRangeException();
+ }
+ }
+ }
+
+ public static void to_hsv(Color color, out float hue, out float saturation, out float value)
+ {
+ int max = Mathf.max(color.r8, Mathf.max(color.g8, color.b8));
+ int min = Mathf.min(color.r8, Mathf.min(color.g8, color.b8));
+
+ float delta = max - min;
+
+ if (delta == 0)
+ {
+ hue = 0;
+ }
+ else
+ {
+ if (color.r == max)
+ hue = (color.g - color.b) / delta; // Between yellow & magenta
+ else if (color.g == max)
+ hue = 2 + (color.b - color.r) / delta; // Between cyan & yellow
+ else
+ hue = 4 + (color.r - color.g) / delta; // Between magenta & cyan
+
+ hue /= 6.0f;
+
+ if (hue < 0)
+ hue += 1.0f;
+ }
+
+ saturation = (max == 0) ? 0 : 1f - (1f * min / max);
+ value = max / 255f;
+ }
+
+ public static Color from_hsv(float hue, float saturation, float value, float alpha = 1.0f)
+ {
+ if (saturation == 0)
+ {
+ // acp_hromatic (grey)
+ return new Color(value, value, value, alpha);
+ }
+
+ int i;
+ float f, p, q, t;
+
+ hue *= 6.0f;
+ hue %= 6f;
+ i = (int)hue;
+
+ f = hue - i;
+ p = value * (1 - saturation);
+ q = value * (1 - saturation * f);
+ t = value * (1 - saturation * (1 - f));
+
+ switch (i)
+ {
+ case 0: // Red is the dominant color
+ return new Color(value, t, p, alpha);
+ case 1: // Green is the dominant color
+ return new Color(q, value, p, alpha);
+ case 2:
+ return new Color(p, value, t, alpha);
+ case 3: // Blue is the dominant color
+ return new Color(p, q, value, alpha);
+ case 4:
+ return new Color(t, p, value, alpha);
+ default: // (5) Red is the dominant color
+ return new Color(value, p, q, alpha);
+ }
+ }
+
+ public Color blend(Color over)
+ {
+ Color res;
+
+ float sa = 1.0f - over.a;
+ res.a = a * sa + over.a;
+
+ if (res.a == 0)
+ {
+ return new Color(0, 0, 0, 0);
+ }
+ else
+ {
+ res.r = (r * a * sa + over.r * over.a) / res.a;
+ res.g = (g * a * sa + over.g * over.a) / res.a;
+ res.b = (b * a * sa + over.b * over.a) / res.a;
+ }
+
+ return res;
+ }
+
+ public Color contrasted()
+ {
+ return new Color(
+ (r + 0.5f) % 1.0f,
+ (g + 0.5f) % 1.0f,
+ (b + 0.5f) % 1.0f
+ );
+ }
+
+ public float gray()
+ {
+ return (r + g + b) / 3.0f;
+ }
+
+ public Color inverted()
+ {
+ return new Color(
+ 1.0f - r,
+ 1.0f - g,
+ 1.0f - b
+ );
+ }
+
+ public Color linear_interpolate(Color b, float t)
+ {
+ Color res = this;
+
+ res.r += (t * (b.r - this.r));
+ res.g += (t * (b.g - this.g));
+ res.b += (t * (b.b - this.b));
+ res.a += (t * (b.a - this.a));
+
+ return res;
+ }
+
+ public int to_32()
+ {
+ int c = (byte)(a * 255);
+ c <<= 8;
+ c |= (byte)(r * 255);
+ c <<= 8;
+ c |= (byte)(g * 255);
+ c <<= 8;
+ c |= (byte)(b * 255);
+
+ return c;
+ }
+
+ public int to_ARGB32()
+ {
+ int c = (byte)(a * 255);
+ c <<= 8;
+ c |= (byte)(r * 255);
+ c <<= 8;
+ c |= (byte)(g * 255);
+ c <<= 8;
+ c |= (byte)(b * 255);
+
+ return c;
+ }
+
+ public string to_html(bool include_alpha = true)
+ {
+ String txt = string.Empty;
+
+ txt += _to_hex(r);
+ txt += _to_hex(g);
+ txt += _to_hex(b);
+
+ if (include_alpha)
+ txt = _to_hex(a) + txt;
+
+ return txt;
+ }
+
+ public Color(float r, float g, float b, float a = 1.0f)
+ {
+ this.r = r;
+ this.g = g;
+ this.b = b;
+ this.a = a;
+ }
+
+ public Color(int rgba)
+ {
+ this.a = (rgba & 0xFF) / 255.0f;
+ rgba >>= 8;
+ this.b = (rgba & 0xFF) / 255.0f;
+ rgba >>= 8;
+ this.g = (rgba & 0xFF) / 255.0f;
+ rgba >>= 8;
+ this.r = (rgba & 0xFF) / 255.0f;
+ }
+
+ private static float _parse_col(string str, int ofs)
+ {
+ int ig = 0;
+
+ for (int i = 0; i < 2; i++)
+ {
+ int c = str[i + ofs];
+ int v = 0;
+
+ if (c >= '0' && c <= '9')
+ {
+ v = c - '0';
+ }
+ else if (c >= 'a' && c <= 'f')
+ {
+ v = c - 'a';
+ v += 10;
+ }
+ else if (c >= 'A' && c <= 'F')
+ {
+ v = c - 'A';
+ v += 10;
+ }
+ else
+ {
+ return -1;
+ }
+
+ if (i == 0)
+ ig += v * 16;
+ else
+ ig += v;
+ }
+
+ return ig;
+ }
+
+ private String _to_hex(float val)
+ {
+ int v = (int)Mathf.clamp(val * 255.0f, 0, 255);
+
+ string ret = string.Empty;
+
+ for (int i = 0; i < 2; i++)
+ {
+ char[] c = { (char)0, (char)0 };
+ int lv = v & 0xF;
+
+ if (lv < 10)
+ c[0] = (char)('0' + lv);
+ else
+ c[0] = (char)('a' + lv - 10);
+
+ v >>= 4;
+ ret = c + ret;
+ }
+
+ return ret;
+ }
+
+ internal static bool html_is_valid(string color)
+ {
+ if (color.Length == 0)
+ return false;
+
+ if (color[0] == '#')
+ color = color.Substring(1, color.Length - 1);
+
+ bool alpha = false;
+
+ if (color.Length == 8)
+ alpha = true;
+ else if (color.Length == 6)
+ alpha = false;
+ else
+ return false;
+
+ if (alpha)
+ {
+ if ((int)_parse_col(color, 0) < 0)
+ return false;
+ }
+
+ int from = alpha ? 2 : 0;
+
+ if ((int)_parse_col(color, from + 0) < 0)
+ return false;
+ if ((int)_parse_col(color, from + 2) < 0)
+ return false;
+ if ((int)_parse_col(color, from + 4) < 0)
+ return false;
+
+ return true;
+ }
+
+ public static Color Color8(byte r8, byte g8, byte b8, byte a8)
+ {
+ return new Color((float)r8 / 255f, (float)g8 / 255f, (float)b8 / 255f, (float)a8 / 255f);
+ }
+
+ public Color(string rgba)
+ {
+ if (rgba.Length == 0)
+ {
+ r = 0f;
+ g = 0f;
+ b = 0f;
+ a = 1.0f;
+ return;
+ }
+
+ if (rgba[0] == '#')
+ rgba = rgba.Substring(1);
+
+ bool alpha = false;
+
+ if (rgba.Length == 8)
+ {
+ alpha = true;
+ }
+ else if (rgba.Length == 6)
+ {
+ alpha = false;
+ }
+ else
+ {
+ throw new ArgumentOutOfRangeException("Invalid color code. Length is " + rgba.Length + " but a length of 6 or 8 is expected: " + rgba);
+ }
+
+ if (alpha)
+ {
+ a = _parse_col(rgba, 0);
+
+ if (a < 0)
+ throw new ArgumentOutOfRangeException("Invalid color code. Alpha is " + a + " but zero or greater is expected: " + rgba);
+ }
+ else
+ {
+ a = 1.0f;
+ }
+
+ int from = alpha ? 2 : 0;
+
+ r = _parse_col(rgba, from + 0);
+
+ if (r < 0)
+ throw new ArgumentOutOfRangeException("Invalid color code. Red is " + r + " but zero or greater is expected: " + rgba);
+
+ g = _parse_col(rgba, from + 2);
+
+ if (g < 0)
+ throw new ArgumentOutOfRangeException("Invalid color code. Green is " + g + " but zero or greater is expected: " + rgba);
+
+ b = _parse_col(rgba, from + 4);
+
+ if (b < 0)
+ throw new ArgumentOutOfRangeException("Invalid color code. Blue is " + b + " but zero or greater is expected: " + rgba);
+ }
+
+ public static bool operator ==(Color left, Color right)
+ {
+ return left.Equals(right);
+ }
+
+ public static bool operator !=(Color left, Color right)
+ {
+ return !left.Equals(right);
+ }
+
+ public static bool operator <(Color left, Color right)
+ {
+ if (left.r == right.r)
+ {
+ if (left.g == right.g)
+ {
+ if (left.b == right.b)
+ return (left.a < right.a);
+ else
+ return (left.b < right.b);
+ }
+ else
+ {
+ return left.g < right.g;
+ }
+ }
+
+ return left.r < right.r;
+ }
+
+ public static bool operator >(Color left, Color right)
+ {
+ if (left.r == right.r)
+ {
+ if (left.g == right.g)
+ {
+ if (left.b == right.b)
+ return (left.a > right.a);
+ else
+ return (left.b > right.b);
+ }
+ else
+ {
+ return left.g > right.g;
+ }
+ }
+
+ return left.r > right.r;
+ }
+
+ public override bool Equals(object obj)
+ {
+ if (obj is Color)
+ {
+ return Equals((Color)obj);
+ }
+
+ return false;
+ }
+
+ public bool Equals(Color other)
+ {
+ return r == other.r && g == other.g && b == other.b && a == other.a;
+ }
+
+ public override int GetHashCode()
+ {
+ return r.GetHashCode() ^ g.GetHashCode() ^ b.GetHashCode() ^ a.GetHashCode();
+ }
+
+ public override string ToString()
+ {
+ return String.Format("{0},{1},{2},{3}", new object[]
+ {
+ this.r.ToString(),
+ this.g.ToString(),
+ this.b.ToString(),
+ this.a.ToString()
+ });
+ }
+
+ public string ToString(string format)
+ {
+ return String.Format("{0},{1},{2},{3}", new object[]
+ {
+ this.r.ToString(format),
+ this.g.ToString(format),
+ this.b.ToString(format),
+ this.a.ToString(format)
+ });
+ }
+ }
+}
diff --git a/modules/mono/glue/cs_files/ExportAttribute.cs b/modules/mono/glue/cs_files/ExportAttribute.cs
index a4e7d447dd..dce9cc59a0 100644
--- a/modules/mono/glue/cs_files/ExportAttribute.cs
+++ b/modules/mono/glue/cs_files/ExportAttribute.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
namespace Godot
{
diff --git a/modules/mono/glue/cs_files/MarshalUtils.cs b/modules/mono/glue/cs_files/MarshalUtils.cs
index 5d40111339..2bdfb95c51 100644
--- a/modules/mono/glue/cs_files/MarshalUtils.cs
+++ b/modules/mono/glue/cs_files/MarshalUtils.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Collections.Generic;
namespace Godot
diff --git a/modules/mono/glue/cs_files/Plane.cs b/modules/mono/glue/cs_files/Plane.cs
index ada6e465ac..37f70aca1e 100644
--- a/modules/mono/glue/cs_files/Plane.cs
+++ b/modules/mono/glue/cs_files/Plane.cs
@@ -1,209 +1,209 @@
-using System;
-
-namespace Godot
-{
- public struct Plane : IEquatable<Plane>
- {
- Vector3 normal;
-
- public float x
- {
- get
- {
- return normal.x;
- }
- set
- {
- normal.x = value;
- }
- }
-
- public float y
- {
- get
- {
- return normal.y;
- }
- set
- {
- normal.y = value;
- }
- }
-
- public float z
- {
- get
- {
- return normal.z;
- }
- set
- {
- normal.z = value;
- }
- }
-
- float d;
-
- public Vector3 Center
- {
- get
- {
- return normal * d;
- }
- }
-
- public float distance_to(Vector3 point)
- {
- return normal.dot(point) - d;
- }
-
- public Vector3 get_any_point()
- {
- return normal * d;
- }
-
- public bool has_point(Vector3 point, float epsilon = Mathf.Epsilon)
- {
- float dist = normal.dot(point) - d;
- return Mathf.abs(dist) <= epsilon;
- }
-
- public Vector3 intersect_3(Plane b, Plane c)
- {
- float denom = normal.cross(b.normal).dot(c.normal);
-
- if (Mathf.abs(denom) <= Mathf.Epsilon)
- return new Vector3();
-
- Vector3 result = (b.normal.cross(c.normal) * this.d) +
- (c.normal.cross(normal) * b.d) +
- (normal.cross(b.normal) * c.d);
-
- return result / denom;
- }
-
- public Vector3 intersect_ray(Vector3 from, Vector3 dir)
- {
- float den = normal.dot(dir);
-
- if (Mathf.abs(den) <= Mathf.Epsilon)
- return new Vector3();
-
- float dist = (normal.dot(from) - d) / den;
-
- // This is a ray, before the emiting pos (from) does not exist
- if (dist > Mathf.Epsilon)
- return new Vector3();
-
- return from + dir * -dist;
- }
-
- public Vector3 intersect_segment(Vector3 begin, Vector3 end)
- {
- Vector3 segment = begin - end;
- float den = normal.dot(segment);
-
- if (Mathf.abs(den) <= Mathf.Epsilon)
- return new Vector3();
-
- float dist = (normal.dot(begin) - d) / den;
-
- if (dist < -Mathf.Epsilon || dist > (1.0f + Mathf.Epsilon))
- return new Vector3();
-
- return begin + segment * -dist;
- }
-
- public bool is_point_over(Vector3 point)
- {
- return normal.dot(point) > d;
- }
-
- public Plane normalized()
- {
- float len = normal.length();
-
- if (len == 0)
- return new Plane(0, 0, 0, 0);
-
- return new Plane(normal / len, d / len);
- }
-
- public Vector3 project(Vector3 point)
- {
- return point - normal * distance_to(point);
- }
-
- public Plane(float a, float b, float c, float d)
- {
- normal = new Vector3(a, b, c);
- this.d = d;
- }
-
- public Plane(Vector3 normal, float d)
- {
- this.normal = normal;
- this.d = d;
- }
-
- public Plane(Vector3 v1, Vector3 v2, Vector3 v3)
- {
- normal = (v1 - v3).cross(v1 - v2);
- normal.normalize();
- d = normal.dot(v1);
- }
-
- public static Plane operator -(Plane plane)
- {
- return new Plane(-plane.normal, -plane.d);
- }
-
- public static bool operator ==(Plane left, Plane right)
- {
- return left.Equals(right);
- }
-
- public static bool operator !=(Plane left, Plane right)
- {
- return !left.Equals(right);
- }
-
- public override bool Equals(object obj)
- {
- if (obj is Plane)
- {
- return Equals((Plane)obj);
- }
-
- return false;
- }
-
- public bool Equals(Plane other)
- {
- return normal == other.normal && d == other.d;
- }
-
- public override int GetHashCode()
- {
- return normal.GetHashCode() ^ d.GetHashCode();
- }
-
- public override string ToString()
- {
- return String.Format("({0}, {1})", new object[]
- {
- this.normal.ToString(),
- this.d.ToString()
- });
- }
-
- public string ToString(string format)
- {
- return String.Format("({0}, {1})", new object[]
- {
- this.normal.ToString(format),
- this.d.ToString(format)
- });
- }
- }
-}
+using System;
+
+namespace Godot
+{
+ public struct Plane : IEquatable<Plane>
+ {
+ Vector3 normal;
+
+ public float x
+ {
+ get
+ {
+ return normal.x;
+ }
+ set
+ {
+ normal.x = value;
+ }
+ }
+
+ public float y
+ {
+ get
+ {
+ return normal.y;
+ }
+ set
+ {
+ normal.y = value;
+ }
+ }
+
+ public float z
+ {
+ get
+ {
+ return normal.z;
+ }
+ set
+ {
+ normal.z = value;
+ }
+ }
+
+ float d;
+
+ public Vector3 Center
+ {
+ get
+ {
+ return normal * d;
+ }
+ }
+
+ public float distance_to(Vector3 point)
+ {
+ return normal.dot(point) - d;
+ }
+
+ public Vector3 get_any_point()
+ {
+ return normal * d;
+ }
+
+ public bool has_point(Vector3 point, float epsilon = Mathf.Epsilon)
+ {
+ float dist = normal.dot(point) - d;
+ return Mathf.abs(dist) <= epsilon;
+ }
+
+ public Vector3 intersect_3(Plane b, Plane c)
+ {
+ float denom = normal.cross(b.normal).dot(c.normal);
+
+ if (Mathf.abs(denom) <= Mathf.Epsilon)
+ return new Vector3();
+
+ Vector3 result = (b.normal.cross(c.normal) * this.d) +
+ (c.normal.cross(normal) * b.d) +
+ (normal.cross(b.normal) * c.d);
+
+ return result / denom;
+ }
+
+ public Vector3 intersect_ray(Vector3 from, Vector3 dir)
+ {
+ float den = normal.dot(dir);
+
+ if (Mathf.abs(den) <= Mathf.Epsilon)
+ return new Vector3();
+
+ float dist = (normal.dot(from) - d) / den;
+
+ // This is a ray, before the emiting pos (from) does not exist
+ if (dist > Mathf.Epsilon)
+ return new Vector3();
+
+ return from + dir * -dist;
+ }
+
+ public Vector3 intersect_segment(Vector3 begin, Vector3 end)
+ {
+ Vector3 segment = begin - end;
+ float den = normal.dot(segment);
+
+ if (Mathf.abs(den) <= Mathf.Epsilon)
+ return new Vector3();
+
+ float dist = (normal.dot(begin) - d) / den;
+
+ if (dist < -Mathf.Epsilon || dist > (1.0f + Mathf.Epsilon))
+ return new Vector3();
+
+ return begin + segment * -dist;
+ }
+
+ public bool is_point_over(Vector3 point)
+ {
+ return normal.dot(point) > d;
+ }
+
+ public Plane normalized()
+ {
+ float len = normal.length();
+
+ if (len == 0)
+ return new Plane(0, 0, 0, 0);
+
+ return new Plane(normal / len, d / len);
+ }
+
+ public Vector3 project(Vector3 point)
+ {
+ return point - normal * distance_to(point);
+ }
+
+ public Plane(float a, float b, float c, float d)
+ {
+ normal = new Vector3(a, b, c);
+ this.d = d;
+ }
+
+ public Plane(Vector3 normal, float d)
+ {
+ this.normal = normal;
+ this.d = d;
+ }
+
+ public Plane(Vector3 v1, Vector3 v2, Vector3 v3)
+ {
+ normal = (v1 - v3).cross(v1 - v2);
+ normal.normalize();
+ d = normal.dot(v1);
+ }
+
+ public static Plane operator -(Plane plane)
+ {
+ return new Plane(-plane.normal, -plane.d);
+ }
+
+ public static bool operator ==(Plane left, Plane right)
+ {
+ return left.Equals(right);
+ }
+
+ public static bool operator !=(Plane left, Plane right)
+ {
+ return !left.Equals(right);
+ }
+
+ public override bool Equals(object obj)
+ {
+ if (obj is Plane)
+ {
+ return Equals((Plane)obj);
+ }
+
+ return false;
+ }
+
+ public bool Equals(Plane other)
+ {
+ return normal == other.normal && d == other.d;
+ }
+
+ public override int GetHashCode()
+ {
+ return normal.GetHashCode() ^ d.GetHashCode();
+ }
+
+ public override string ToString()
+ {
+ return String.Format("({0}, {1})", new object[]
+ {
+ this.normal.ToString(),
+ this.d.ToString()
+ });
+ }
+
+ public string ToString(string format)
+ {
+ return String.Format("({0}, {1})", new object[]
+ {
+ this.normal.ToString(format),
+ this.d.ToString(format)
+ });
+ }
+ }
+}
diff --git a/modules/mono/glue/cs_files/Rect3.cs b/modules/mono/glue/cs_files/Rect3.cs
index 0d25de1ec6..617d33e7fd 100644
--- a/modules/mono/glue/cs_files/Rect3.cs
+++ b/modules/mono/glue/cs_files/Rect3.cs
@@ -1,477 +1,477 @@
-using System;
-
-// file: core/math/rect3.h
-// commit: 7ad14e7a3e6f87ddc450f7e34621eb5200808451
-// file: core/math/rect3.cpp
-// commit: bd282ff43f23fe845f29a3e25c8efc01bd65ffb0
-// file: core/variant_call.cpp
-// commit: 5ad9be4c24e9d7dc5672fdc42cea896622fe5685
-
-namespace Godot
-{
- public struct Rect3 : IEquatable<Rect3>
- {
- private Vector3 position;
- private Vector3 size;
-
- public Vector3 Position
- {
- get
- {
- return position;
- }
- }
-
- public Vector3 Size
- {
- get
- {
- return size;
- }
- }
-
- public Vector3 End
- {
- get
- {
- return position + size;
- }
- }
-
- public bool encloses(Rect3 with)
- {
- Vector3 src_min = position;
- Vector3 src_max = position + size;
- Vector3 dst_min = with.position;
- Vector3 dst_max = with.position + with.size;
-
- return ((src_min.x <= dst_min.x) &&
- (src_max.x > dst_max.x) &&
- (src_min.y <= dst_min.y) &&
- (src_max.y > dst_max.y) &&
- (src_min.z <= dst_min.z) &&
- (src_max.z > dst_max.z));
- }
-
- public Rect3 expand(Vector3 to_point)
- {
- Vector3 begin = position;
- Vector3 end = position + size;
-
- if (to_point.x < begin.x)
- begin.x = to_point.x;
- if (to_point.y < begin.y)
- begin.y = to_point.y;
- if (to_point.z < begin.z)
- begin.z = to_point.z;
-
- if (to_point.x > end.x)
- end.x = to_point.x;
- if (to_point.y > end.y)
- end.y = to_point.y;
- if (to_point.z > end.z)
- end.z = to_point.z;
-
- return new Rect3(begin, end - begin);
- }
-
- public float get_area()
- {
- return size.x * size.y * size.z;
- }
-
- public Vector3 get_endpoint(int idx)
- {
- switch (idx)
- {
- case 0:
- return new Vector3(position.x, position.y, position.z);
- case 1:
- return new Vector3(position.x, position.y, position.z + size.z);
- case 2:
- return new Vector3(position.x, position.y + size.y, position.z);
- case 3:
- return new Vector3(position.x, position.y + size.y, position.z + size.z);
- case 4:
- return new Vector3(position.x + size.x, position.y, position.z);
- case 5:
- return new Vector3(position.x + size.x, position.y, position.z + size.z);
- case 6:
- return new Vector3(position.x + size.x, position.y + size.y, position.z);
- case 7:
- return new Vector3(position.x + size.x, position.y + size.y, position.z + size.z);
- default:
- throw new ArgumentOutOfRangeException(nameof(idx), String.Format("Index is {0}, but a value from 0 to 7 is expected.", idx));
- }
- }
-
- public Vector3 get_longest_axis()
- {
- Vector3 axis = new Vector3(1f, 0f, 0f);
- float max_size = size.x;
-
- if (size.y > max_size)
- {
- axis = new Vector3(0f, 1f, 0f);
- max_size = size.y;
- }
-
- if (size.z > max_size)
- {
- axis = new Vector3(0f, 0f, 1f);
- max_size = size.z;
- }
-
- return axis;
- }
-
- public Vector3.Axis get_longest_axis_index()
- {
- Vector3.Axis axis = Vector3.Axis.X;
- float max_size = size.x;
-
- if (size.y > max_size)
- {
- axis = Vector3.Axis.Y;
- max_size = size.y;
- }
-
- if (size.z > max_size)
- {
- axis = Vector3.Axis.Z;
- max_size = size.z;
- }
-
- return axis;
- }
-
- public float get_longest_axis_size()
- {
- float max_size = size.x;
-
- if (size.y > max_size)
- max_size = size.y;
-
- if (size.z > max_size)
- max_size = size.z;
-
- return max_size;
- }
-
- public Vector3 get_shortest_axis()
- {
- Vector3 axis = new Vector3(1f, 0f, 0f);
- float max_size = size.x;
-
- if (size.y < max_size)
- {
- axis = new Vector3(0f, 1f, 0f);
- max_size = size.y;
- }
-
- if (size.z < max_size)
- {
- axis = new Vector3(0f, 0f, 1f);
- max_size = size.z;
- }
-
- return axis;
- }
-
- public Vector3.Axis get_shortest_axis_index()
- {
- Vector3.Axis axis = Vector3.Axis.X;
- float max_size = size.x;
-
- if (size.y < max_size)
- {
- axis = Vector3.Axis.Y;
- max_size = size.y;
- }
-
- if (size.z < max_size)
- {
- axis = Vector3.Axis.Z;
- max_size = size.z;
- }
-
- return axis;
- }
-
- public float get_shortest_axis_size()
- {
- float max_size = size.x;
-
- if (size.y < max_size)
- max_size = size.y;
-
- if (size.z < max_size)
- max_size = size.z;
-
- return max_size;
- }
-
- public Vector3 get_support(Vector3 dir)
- {
- Vector3 half_extents = size * 0.5f;
- Vector3 ofs = position + half_extents;
-
- return ofs + new Vector3(
- (dir.x > 0f) ? -half_extents.x : half_extents.x,
- (dir.y > 0f) ? -half_extents.y : half_extents.y,
- (dir.z > 0f) ? -half_extents.z : half_extents.z);
- }
-
- public Rect3 grow(float by)
- {
- Rect3 res = this;
-
- res.position.x -= by;
- res.position.y -= by;
- res.position.z -= by;
- res.size.x += 2.0f * by;
- res.size.y += 2.0f * by;
- res.size.z += 2.0f * by;
-
- return res;
- }
-
- public bool has_no_area()
- {
- return size.x <= 0f || size.y <= 0f || size.z <= 0f;
- }
-
- public bool has_no_surface()
- {
- return size.x <= 0f && size.y <= 0f && size.z <= 0f;
- }
-
- public bool has_point(Vector3 point)
- {
- if (point.x < position.x)
- return false;
- if (point.y < position.y)
- return false;
- if (point.z < position.z)
- return false;
- if (point.x > position.x + size.x)
- return false;
- if (point.y > position.y + size.y)
- return false;
- if (point.z > position.z + size.z)
- return false;
-
- return true;
- }
-
- public Rect3 intersection(Rect3 with)
- {
- Vector3 src_min = position;
- Vector3 src_max = position + size;
- Vector3 dst_min = with.position;
- Vector3 dst_max = with.position + with.size;
-
- Vector3 min, max;
-
- if (src_min.x > dst_max.x || src_max.x < dst_min.x)
- {
- return new Rect3();
- }
- else
- {
- min.x = (src_min.x > dst_min.x) ? src_min.x : dst_min.x;
- max.x = (src_max.x < dst_max.x) ? src_max.x : dst_max.x;
- }
-
- if (src_min.y > dst_max.y || src_max.y < dst_min.y)
- {
- return new Rect3();
- }
- else
- {
- min.y = (src_min.y > dst_min.y) ? src_min.y : dst_min.y;
- max.y = (src_max.y < dst_max.y) ? src_max.y : dst_max.y;
- }
-
- if (src_min.z > dst_max.z || src_max.z < dst_min.z)
- {
- return new Rect3();
- }
- else
- {
- min.z = (src_min.z > dst_min.z) ? src_min.z : dst_min.z;
- max.z = (src_max.z < dst_max.z) ? src_max.z : dst_max.z;
- }
-
- return new Rect3(min, max - min);
- }
-
- public bool intersects(Rect3 with)
- {
- if (position.x >= (with.position.x + with.size.x))
- return false;
- if ((position.x + size.x) <= with.position.x)
- return false;
- if (position.y >= (with.position.y + with.size.y))
- return false;
- if ((position.y + size.y) <= with.position.y)
- return false;
- if (position.z >= (with.position.z + with.size.z))
- return false;
- if ((position.z + size.z) <= with.position.z)
- return false;
-
- return true;
- }
-
- public bool intersects_plane(Plane plane)
- {
- Vector3[] points =
- {
- new Vector3(position.x, position.y, position.z),
- new Vector3(position.x, position.y, position.z + size.z),
- new Vector3(position.x, position.y + size.y, position.z),
- new Vector3(position.x, position.y + size.y, position.z + size.z),
- new Vector3(position.x + size.x, position.y, position.z),
- new Vector3(position.x + size.x, position.y, position.z + size.z),
- new Vector3(position.x + size.x, position.y + size.y, position.z),
- new Vector3(position.x + size.x, position.y + size.y, position.z + size.z),
- };
-
- bool over = false;
- bool under = false;
-
- for (int i = 0; i < 8; i++)
- {
- if (plane.distance_to(points[i]) > 0)
- over = true;
- else
- under = true;
- }
-
- return under && over;
- }
-
- public bool intersects_segment(Vector3 from, Vector3 to)
- {
- float min = 0f;
- float max = 1f;
-
- for (int i = 0; i < 3; i++)
- {
- float seg_from = from[i];
- float seg_to = to[i];
- float box_begin = position[i];
- float box_end = box_begin + size[i];
- float cmin, cmax;
-
- if (seg_from < seg_to)
- {
- if (seg_from > box_end || seg_to < box_begin)
- return false;
-
- float length = seg_to - seg_from;
- cmin = seg_from < box_begin ? (box_begin - seg_from) / length : 0f;
- cmax = seg_to > box_end ? (box_end - seg_from) / length : 1f;
- }
- else
- {
- if (seg_to > box_end || seg_from < box_begin)
- return false;
-
- float length = seg_to - seg_from;
- cmin = seg_from > box_end ? (box_end - seg_from) / length : 0f;
- cmax = seg_to < box_begin ? (box_begin - seg_from) / length : 1f;
- }
-
- if (cmin > min)
- {
- min = cmin;
- }
-
- if (cmax < max)
- max = cmax;
- if (max < min)
- return false;
- }
-
- return true;
- }
-
- public Rect3 merge(Rect3 with)
- {
- Vector3 beg_1 = position;
- Vector3 beg_2 = with.position;
- Vector3 end_1 = new Vector3(size.x, size.y, size.z) + beg_1;
- Vector3 end_2 = new Vector3(with.size.x, with.size.y, with.size.z) + beg_2;
-
- Vector3 min = new Vector3(
- (beg_1.x < beg_2.x) ? beg_1.x : beg_2.x,
- (beg_1.y < beg_2.y) ? beg_1.y : beg_2.y,
- (beg_1.z < beg_2.z) ? beg_1.z : beg_2.z
- );
-
- Vector3 max = new Vector3(
- (end_1.x > end_2.x) ? end_1.x : end_2.x,
- (end_1.y > end_2.y) ? end_1.y : end_2.y,
- (end_1.z > end_2.z) ? end_1.z : end_2.z
- );
-
- return new Rect3(min, max - min);
- }
-
- public Rect3(Vector3 position, Vector3 size)
- {
- this.position = position;
- this.size = size;
- }
-
- public static bool operator ==(Rect3 left, Rect3 right)
- {
- return left.Equals(right);
- }
-
- public static bool operator !=(Rect3 left, Rect3 right)
- {
- return !left.Equals(right);
- }
-
- public override bool Equals(object obj)
- {
- if (obj is Rect3)
- {
- return Equals((Rect3)obj);
- }
-
- return false;
- }
-
- public bool Equals(Rect3 other)
- {
- return position == other.position && size == other.size;
- }
-
- public override int GetHashCode()
- {
- return position.GetHashCode() ^ size.GetHashCode();
- }
-
- public override string ToString()
- {
- return String.Format("{0} - {1}", new object[]
- {
- this.position.ToString(),
- this.size.ToString()
- });
- }
-
- public string ToString(string format)
- {
- return String.Format("{0} - {1}", new object[]
- {
- this.position.ToString(format),
- this.size.ToString(format)
- });
- }
- }
-}
+using System;
+
+// file: core/math/rect3.h
+// commit: 7ad14e7a3e6f87ddc450f7e34621eb5200808451
+// file: core/math/rect3.cpp
+// commit: bd282ff43f23fe845f29a3e25c8efc01bd65ffb0
+// file: core/variant_call.cpp
+// commit: 5ad9be4c24e9d7dc5672fdc42cea896622fe5685
+
+namespace Godot
+{
+ public struct Rect3 : IEquatable<Rect3>
+ {
+ private Vector3 position;
+ private Vector3 size;
+
+ public Vector3 Position
+ {
+ get
+ {
+ return position;
+ }
+ }
+
+ public Vector3 Size
+ {
+ get
+ {
+ return size;
+ }
+ }
+
+ public Vector3 End
+ {
+ get
+ {
+ return position + size;
+ }
+ }
+
+ public bool encloses(Rect3 with)
+ {
+ Vector3 src_min = position;
+ Vector3 src_max = position + size;
+ Vector3 dst_min = with.position;
+ Vector3 dst_max = with.position + with.size;
+
+ return ((src_min.x <= dst_min.x) &&
+ (src_max.x > dst_max.x) &&
+ (src_min.y <= dst_min.y) &&
+ (src_max.y > dst_max.y) &&
+ (src_min.z <= dst_min.z) &&
+ (src_max.z > dst_max.z));
+ }
+
+ public Rect3 expand(Vector3 to_point)
+ {
+ Vector3 begin = position;
+ Vector3 end = position + size;
+
+ if (to_point.x < begin.x)
+ begin.x = to_point.x;
+ if (to_point.y < begin.y)
+ begin.y = to_point.y;
+ if (to_point.z < begin.z)
+ begin.z = to_point.z;
+
+ if (to_point.x > end.x)
+ end.x = to_point.x;
+ if (to_point.y > end.y)
+ end.y = to_point.y;
+ if (to_point.z > end.z)
+ end.z = to_point.z;
+
+ return new Rect3(begin, end - begin);
+ }
+
+ public float get_area()
+ {
+ return size.x * size.y * size.z;
+ }
+
+ public Vector3 get_endpoint(int idx)
+ {
+ switch (idx)
+ {
+ case 0:
+ return new Vector3(position.x, position.y, position.z);
+ case 1:
+ return new Vector3(position.x, position.y, position.z + size.z);
+ case 2:
+ return new Vector3(position.x, position.y + size.y, position.z);
+ case 3:
+ return new Vector3(position.x, position.y + size.y, position.z + size.z);
+ case 4:
+ return new Vector3(position.x + size.x, position.y, position.z);
+ case 5:
+ return new Vector3(position.x + size.x, position.y, position.z + size.z);
+ case 6:
+ return new Vector3(position.x + size.x, position.y + size.y, position.z);
+ case 7:
+ return new Vector3(position.x + size.x, position.y + size.y, position.z + size.z);
+ default:
+ throw new ArgumentOutOfRangeException(nameof(idx), String.Format("Index is {0}, but a value from 0 to 7 is expected.", idx));
+ }
+ }
+
+ public Vector3 get_longest_axis()
+ {
+ Vector3 axis = new Vector3(1f, 0f, 0f);
+ float max_size = size.x;
+
+ if (size.y > max_size)
+ {
+ axis = new Vector3(0f, 1f, 0f);
+ max_size = size.y;
+ }
+
+ if (size.z > max_size)
+ {
+ axis = new Vector3(0f, 0f, 1f);
+ max_size = size.z;
+ }
+
+ return axis;
+ }
+
+ public Vector3.Axis get_longest_axis_index()
+ {
+ Vector3.Axis axis = Vector3.Axis.X;
+ float max_size = size.x;
+
+ if (size.y > max_size)
+ {
+ axis = Vector3.Axis.Y;
+ max_size = size.y;
+ }
+
+ if (size.z > max_size)
+ {
+ axis = Vector3.Axis.Z;
+ max_size = size.z;
+ }
+
+ return axis;
+ }
+
+ public float get_longest_axis_size()
+ {
+ float max_size = size.x;
+
+ if (size.y > max_size)
+ max_size = size.y;
+
+ if (size.z > max_size)
+ max_size = size.z;
+
+ return max_size;
+ }
+
+ public Vector3 get_shortest_axis()
+ {
+ Vector3 axis = new Vector3(1f, 0f, 0f);
+ float max_size = size.x;
+
+ if (size.y < max_size)
+ {
+ axis = new Vector3(0f, 1f, 0f);
+ max_size = size.y;
+ }
+
+ if (size.z < max_size)
+ {
+ axis = new Vector3(0f, 0f, 1f);
+ max_size = size.z;
+ }
+
+ return axis;
+ }
+
+ public Vector3.Axis get_shortest_axis_index()
+ {
+ Vector3.Axis axis = Vector3.Axis.X;
+ float max_size = size.x;
+
+ if (size.y < max_size)
+ {
+ axis = Vector3.Axis.Y;
+ max_size = size.y;
+ }
+
+ if (size.z < max_size)
+ {
+ axis = Vector3.Axis.Z;
+ max_size = size.z;
+ }
+
+ return axis;
+ }
+
+ public float get_shortest_axis_size()
+ {
+ float max_size = size.x;
+
+ if (size.y < max_size)
+ max_size = size.y;
+
+ if (size.z < max_size)
+ max_size = size.z;
+
+ return max_size;
+ }
+
+ public Vector3 get_support(Vector3 dir)
+ {
+ Vector3 half_extents = size * 0.5f;
+ Vector3 ofs = position + half_extents;
+
+ return ofs + new Vector3(
+ (dir.x > 0f) ? -half_extents.x : half_extents.x,
+ (dir.y > 0f) ? -half_extents.y : half_extents.y,
+ (dir.z > 0f) ? -half_extents.z : half_extents.z);
+ }
+
+ public Rect3 grow(float by)
+ {
+ Rect3 res = this;
+
+ res.position.x -= by;
+ res.position.y -= by;
+ res.position.z -= by;
+ res.size.x += 2.0f * by;
+ res.size.y += 2.0f * by;
+ res.size.z += 2.0f * by;
+
+ return res;
+ }
+
+ public bool has_no_area()
+ {
+ return size.x <= 0f || size.y <= 0f || size.z <= 0f;
+ }
+
+ public bool has_no_surface()
+ {
+ return size.x <= 0f && size.y <= 0f && size.z <= 0f;
+ }
+
+ public bool has_point(Vector3 point)
+ {
+ if (point.x < position.x)
+ return false;
+ if (point.y < position.y)
+ return false;
+ if (point.z < position.z)
+ return false;
+ if (point.x > position.x + size.x)
+ return false;
+ if (point.y > position.y + size.y)
+ return false;
+ if (point.z > position.z + size.z)
+ return false;
+
+ return true;
+ }
+
+ public Rect3 intersection(Rect3 with)
+ {
+ Vector3 src_min = position;
+ Vector3 src_max = position + size;
+ Vector3 dst_min = with.position;
+ Vector3 dst_max = with.position + with.size;
+
+ Vector3 min, max;
+
+ if (src_min.x > dst_max.x || src_max.x < dst_min.x)
+ {
+ return new Rect3();
+ }
+ else
+ {
+ min.x = (src_min.x > dst_min.x) ? src_min.x : dst_min.x;
+ max.x = (src_max.x < dst_max.x) ? src_max.x : dst_max.x;
+ }
+
+ if (src_min.y > dst_max.y || src_max.y < dst_min.y)
+ {
+ return new Rect3();
+ }
+ else
+ {
+ min.y = (src_min.y > dst_min.y) ? src_min.y : dst_min.y;
+ max.y = (src_max.y < dst_max.y) ? src_max.y : dst_max.y;
+ }
+
+ if (src_min.z > dst_max.z || src_max.z < dst_min.z)
+ {
+ return new Rect3();
+ }
+ else
+ {
+ min.z = (src_min.z > dst_min.z) ? src_min.z : dst_min.z;
+ max.z = (src_max.z < dst_max.z) ? src_max.z : dst_max.z;
+ }
+
+ return new Rect3(min, max - min);
+ }
+
+ public bool intersects(Rect3 with)
+ {
+ if (position.x >= (with.position.x + with.size.x))
+ return false;
+ if ((position.x + size.x) <= with.position.x)
+ return false;
+ if (position.y >= (with.position.y + with.size.y))
+ return false;
+ if ((position.y + size.y) <= with.position.y)
+ return false;
+ if (position.z >= (with.position.z + with.size.z))
+ return false;
+ if ((position.z + size.z) <= with.position.z)
+ return false;
+
+ return true;
+ }
+
+ public bool intersects_plane(Plane plane)
+ {
+ Vector3[] points =
+ {
+ new Vector3(position.x, position.y, position.z),
+ new Vector3(position.x, position.y, position.z + size.z),
+ new Vector3(position.x, position.y + size.y, position.z),
+ new Vector3(position.x, position.y + size.y, position.z + size.z),
+ new Vector3(position.x + size.x, position.y, position.z),
+ new Vector3(position.x + size.x, position.y, position.z + size.z),
+ new Vector3(position.x + size.x, position.y + size.y, position.z),
+ new Vector3(position.x + size.x, position.y + size.y, position.z + size.z),
+ };
+
+ bool over = false;
+ bool under = false;
+
+ for (int i = 0; i < 8; i++)
+ {
+ if (plane.distance_to(points[i]) > 0)
+ over = true;
+ else
+ under = true;
+ }
+
+ return under && over;
+ }
+
+ public bool intersects_segment(Vector3 from, Vector3 to)
+ {
+ float min = 0f;
+ float max = 1f;
+
+ for (int i = 0; i < 3; i++)
+ {
+ float seg_from = from[i];
+ float seg_to = to[i];
+ float box_begin = position[i];
+ float box_end = box_begin + size[i];
+ float cmin, cmax;
+
+ if (seg_from < seg_to)
+ {
+ if (seg_from > box_end || seg_to < box_begin)
+ return false;
+
+ float length = seg_to - seg_from;
+ cmin = seg_from < box_begin ? (box_begin - seg_from) / length : 0f;
+ cmax = seg_to > box_end ? (box_end - seg_from) / length : 1f;
+ }
+ else
+ {
+ if (seg_to > box_end || seg_from < box_begin)
+ return false;
+
+ float length = seg_to - seg_from;
+ cmin = seg_from > box_end ? (box_end - seg_from) / length : 0f;
+ cmax = seg_to < box_begin ? (box_begin - seg_from) / length : 1f;
+ }
+
+ if (cmin > min)
+ {
+ min = cmin;
+ }
+
+ if (cmax < max)
+ max = cmax;
+ if (max < min)
+ return false;
+ }
+
+ return true;
+ }
+
+ public Rect3 merge(Rect3 with)
+ {
+ Vector3 beg_1 = position;
+ Vector3 beg_2 = with.position;
+ Vector3 end_1 = new Vector3(size.x, size.y, size.z) + beg_1;
+ Vector3 end_2 = new Vector3(with.size.x, with.size.y, with.size.z) + beg_2;
+
+ Vector3 min = new Vector3(
+ (beg_1.x < beg_2.x) ? beg_1.x : beg_2.x,
+ (beg_1.y < beg_2.y) ? beg_1.y : beg_2.y,
+ (beg_1.z < beg_2.z) ? beg_1.z : beg_2.z
+ );
+
+ Vector3 max = new Vector3(
+ (end_1.x > end_2.x) ? end_1.x : end_2.x,
+ (end_1.y > end_2.y) ? end_1.y : end_2.y,
+ (end_1.z > end_2.z) ? end_1.z : end_2.z
+ );
+
+ return new Rect3(min, max - min);
+ }
+
+ public Rect3(Vector3 position, Vector3 size)
+ {
+ this.position = position;
+ this.size = size;
+ }
+
+ public static bool operator ==(Rect3 left, Rect3 right)
+ {
+ return left.Equals(right);
+ }
+
+ public static bool operator !=(Rect3 left, Rect3 right)
+ {
+ return !left.Equals(right);
+ }
+
+ public override bool Equals(object obj)
+ {
+ if (obj is Rect3)
+ {
+ return Equals((Rect3)obj);
+ }
+
+ return false;
+ }
+
+ public bool Equals(Rect3 other)
+ {
+ return position == other.position && size == other.size;
+ }
+
+ public override int GetHashCode()
+ {
+ return position.GetHashCode() ^ size.GetHashCode();
+ }
+
+ public override string ToString()
+ {
+ return String.Format("{0} - {1}", new object[]
+ {
+ this.position.ToString(),
+ this.size.ToString()
+ });
+ }
+
+ public string ToString(string format)
+ {
+ return String.Format("{0} - {1}", new object[]
+ {
+ this.position.ToString(format),
+ this.size.ToString(format)
+ });
+ }
+ }
+}
diff --git a/modules/mono/glue/cs_files/ToolAttribute.cs b/modules/mono/glue/cs_files/ToolAttribute.cs
index 0275982c7f..d8601b5b32 100644
--- a/modules/mono/glue/cs_files/ToolAttribute.cs
+++ b/modules/mono/glue/cs_files/ToolAttribute.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
namespace Godot
{
diff --git a/modules/mono/godotsharp_dirs.cpp b/modules/mono/godotsharp_dirs.cpp
index 6bcf0e2355..7cc2168b70 100644
--- a/modules/mono/godotsharp_dirs.cpp
+++ b/modules/mono/godotsharp_dirs.cpp
@@ -122,7 +122,14 @@ private:
#ifdef TOOLS_ENABLED
mono_solutions_dir = mono_user_dir.plus_file("solutions");
build_logs_dir = mono_user_dir.plus_file("build_logs");
- String base_path = String("res://") + ProjectSettings::get_singleton()->get("application/config/name");
+
+ String name = ProjectSettings::get_singleton()->get("application/config/name");
+ if (name.empty()) {
+ name = "UnnamedProject";
+ }
+
+ String base_path = String("res://") + name;
+
sln_filepath = ProjectSettings::get_singleton()->globalize_path(base_path + ".sln");
csproj_filepath = ProjectSettings::get_singleton()->globalize_path(base_path + ".csproj");
#endif
diff --git a/modules/mono/mono_gc_handle.cpp b/modules/mono/mono_gc_handle.cpp
index d3ad968135..e10e06df0e 100644
--- a/modules/mono/mono_gc_handle.cpp
+++ b/modules/mono/mono_gc_handle.cpp
@@ -59,6 +59,10 @@ Ref<MonoGCHandle> MonoGCHandle::create_weak(MonoObject *p_object) {
void MonoGCHandle::release() {
+#ifdef DEBUG_ENABLED
+ CRASH_COND(GDMono::get_singleton() == NULL);
+#endif
+
if (!released && GDMono::get_singleton()->is_runtime_initialized()) {
mono_gchandle_free(handle);
released = true;
diff --git a/modules/mono/mono_gd/gd_mono.cpp b/modules/mono/mono_gd/gd_mono.cpp
index 4b5f5eb137..c997b0f000 100644
--- a/modules/mono/mono_gd/gd_mono.cpp
+++ b/modules/mono/mono_gd/gd_mono.cpp
@@ -369,9 +369,12 @@ bool GDMono::_load_project_assembly() {
if (project_assembly)
return true;
- String project_assembly_name = ProjectSettings::get_singleton()->get("application/config/name");
+ String name = ProjectSettings::get_singleton()->get("application/config/name");
+ if (name.empty()) {
+ name = "UnnamedProject";
+ }
- bool success = _load_assembly(project_assembly_name, &project_assembly);
+ bool success = _load_assembly(name, &project_assembly);
if (success)
mono_assembly_set_main(project_assembly->get_assembly());
@@ -622,6 +625,8 @@ GDMono::~GDMono() {
if (gdmono_log)
memdelete(gdmono_log);
+
+ singleton = NULL;
}
_GodotSharp *_GodotSharp::singleton = NULL;
@@ -700,7 +705,7 @@ bool _GodotSharp::is_domain_loaded() {
void _GodotSharp::queue_dispose(Object *p_object) {
- if (Thread::get_main_id() == Thread::get_caller_id() && !GDMono::get_singleton()->is_finalizing_scripts_domain()) {
+ if (GDMonoUtils::is_main_thread() && !GDMono::get_singleton()->is_finalizing_scripts_domain()) {
_dispose_object(p_object);
} else {
#ifndef NO_THREADS
@@ -717,7 +722,7 @@ void _GodotSharp::queue_dispose(Object *p_object) {
void _GodotSharp::queue_dispose(NodePath *p_node_path) {
- if (Thread::get_main_id() == Thread::get_caller_id() && !GDMono::get_singleton()->is_finalizing_scripts_domain()) {
+ if (GDMonoUtils::is_main_thread() && !GDMono::get_singleton()->is_finalizing_scripts_domain()) {
memdelete(p_node_path);
} else {
#ifndef NO_THREADS
@@ -734,7 +739,7 @@ void _GodotSharp::queue_dispose(NodePath *p_node_path) {
void _GodotSharp::queue_dispose(RID *p_rid) {
- if (Thread::get_main_id() == Thread::get_caller_id() && !GDMono::get_singleton()->is_finalizing_scripts_domain()) {
+ if (GDMonoUtils::is_main_thread() && !GDMono::get_singleton()->is_finalizing_scripts_domain()) {
memdelete(p_rid);
} else {
#ifndef NO_THREADS
diff --git a/modules/mono/mono_gd/gd_mono_assembly.cpp b/modules/mono/mono_gd/gd_mono_assembly.cpp
index 0d9b137f99..7dc7043eec 100644
--- a/modules/mono/mono_gd/gd_mono_assembly.cpp
+++ b/modules/mono/mono_gd/gd_mono_assembly.cpp
@@ -107,10 +107,11 @@ MonoAssembly *GDMonoAssembly::_preload_hook(MonoAssemblyName *aname, char **asse
search_dirs.push_back(String(rootdir).plus_file("mono").plus_file("4.5"));
}
- while (assemblies_path) {
- if (*assemblies_path)
+ if (assemblies_path) {
+ while (*assemblies_path) {
search_dirs.push_back(*assemblies_path);
- ++assemblies_path;
+ ++assemblies_path;
+ }
}
}
diff --git a/modules/mono/mono_gd/gd_mono_marshal.cpp b/modules/mono/mono_gd/gd_mono_marshal.cpp
index 77a1ef3cb0..01392447f3 100644
--- a/modules/mono/mono_gd/gd_mono_marshal.cpp
+++ b/modules/mono/mono_gd/gd_mono_marshal.cpp
@@ -600,7 +600,7 @@ MonoArray *Array_to_mono_array(const Array &p_array) {
for (int i = 0; i < p_array.size(); i++) {
MonoObject *boxed = variant_to_mono_object(p_array[i]);
- mono_array_set(ret, MonoObject *, i, boxed);
+ mono_array_setref(ret, i, boxed);
}
return ret;
diff --git a/modules/mono/mono_gd/gd_mono_utils.h b/modules/mono/mono_gd/gd_mono_utils.h
index e3af57e78a..ebb5d28e4d 100644
--- a/modules/mono/mono_gd/gd_mono_utils.h
+++ b/modules/mono/mono_gd/gd_mono_utils.h
@@ -149,6 +149,10 @@ void attach_current_thread();
void detach_current_thread();
MonoThread *get_current_thread();
+_FORCE_INLINE_ bool is_main_thread() {
+ return mono_domain_get() != NULL && mono_thread_get_main() == mono_thread_current();
+}
+
GDMonoClass *get_object_class(MonoObject *p_object);
GDMonoClass *type_get_proxy_class(const StringName &p_type);
GDMonoClass *get_class_native_base(GDMonoClass *p_class);
diff --git a/modules/mono/mono_reg_utils.py b/modules/mono/mono_reg_utils.py
index e9988625f5..8ddddb3a24 100644
--- a/modules/mono/mono_reg_utils.py
+++ b/modules/mono/mono_reg_utils.py
@@ -1,4 +1,7 @@
import os
+import platform
+
+from compat import decode_utf8
if os.name == 'nt':
import sys
@@ -11,8 +14,7 @@ if os.name == 'nt':
def _reg_open_key(key, subkey):
try:
return winreg.OpenKey(key, subkey)
- except (WindowsError, EnvironmentError) as e:
- import platform
+ except (WindowsError, OSError):
if platform.architecture()[0] == '32bit':
bitness_sam = winreg.KEY_WOW64_64KEY
else:
@@ -20,39 +22,93 @@ def _reg_open_key(key, subkey):
return winreg.OpenKey(key, subkey, 0, winreg.KEY_READ | bitness_sam)
-def _find_mono_in_reg(subkey):
+def _reg_open_key_bits(key, subkey, bits):
+ sam = winreg.KEY_READ
+
+ if platform.architecture()[0] == '32bit':
+ if bits == '64':
+ # Force 32bit process to search in 64bit registry
+ sam |= winreg.KEY_WOW64_64KEY
+ else:
+ if bits == '32':
+ # Force 64bit process to search in 32bit registry
+ sam |= winreg.KEY_WOW64_32KEY
+
+ return winreg.OpenKey(key, subkey, 0, sam)
+
+
+def _find_mono_in_reg(subkey, bits):
try:
- with _reg_open_key(winreg.HKEY_LOCAL_MACHINE, subkey) as hKey:
+ with _reg_open_key_bits(winreg.HKEY_LOCAL_MACHINE, subkey, bits) as hKey:
value, regtype = winreg.QueryValueEx(hKey, 'SdkInstallRoot')
return value
- except (WindowsError, EnvironmentError) as e:
+ except (WindowsError, OSError):
return None
-def _find_mono_in_reg_old(subkey):
+
+def _find_mono_in_reg_old(subkey, bits):
try:
- with _reg_open_key(winreg.HKEY_LOCAL_MACHINE, subkey) as hKey:
+ with _reg_open_key_bits(winreg.HKEY_LOCAL_MACHINE, subkey, bits) as hKey:
default_clr, regtype = winreg.QueryValueEx(hKey, 'DefaultCLR')
if default_clr:
- return _find_mono_in_reg(subkey + '\\' + default_clr)
+ return _find_mono_in_reg(subkey + '\\' + default_clr, bits)
return None
except (WindowsError, EnvironmentError):
return None
-def find_mono_root_dir():
- dir = _find_mono_in_reg(r'SOFTWARE\Mono')
- if dir:
- return dir
- dir = _find_mono_in_reg_old(r'SOFTWARE\Novell\Mono')
- if dir:
- return dir
- return None
+def find_mono_root_dir(bits):
+ root_dir = _find_mono_in_reg(r'SOFTWARE\Mono', bits)
+ if root_dir is not None:
+ return root_dir
+ root_dir = _find_mono_in_reg_old(r'SOFTWARE\Novell\Mono', bits)
+ if root_dir is not None:
+ return root_dir
+ return ''
def find_msbuild_tools_path_reg():
+ import subprocess
+
+ vswhere = os.getenv('PROGRAMFILES(X86)')
+ if not vswhere:
+ vswhere = os.getenv('PROGRAMFILES')
+ vswhere += r'\Microsoft Visual Studio\Installer\vswhere.exe'
+
+ vswhere_args = ['-latest', '-requires', 'Microsoft.Component.MSBuild']
+
try:
- with _reg_open_key(winreg.HKEY_LOCAL_MACHINE, r'SOFTWARE\Microsoft\MSBuild\ToolsVersions\4.0') as hKey:
+ lines = subprocess.check_output([vswhere] + vswhere_args).splitlines()
+
+ for line in lines:
+ parts = decode_utf8(line).split(':', 1)
+
+ if len(parts) < 2 or parts[0] != 'installationPath':
+ continue
+
+ val = parts[1].strip()
+
+ if not val:
+ raise ValueError('Value of `installationPath` entry is empty')
+
+ return os.path.join(val, "MSBuild\\15.0\\Bin")
+
+ raise ValueError('Cannot find `installationPath` entry')
+ except ValueError as e:
+ print('Error reading output from vswhere: ' + e.message)
+ except WindowsError:
+ pass # Fine, vswhere not found
+ except (subprocess.CalledProcessError, OSError):
+ pass
+
+ # Try to find 14.0 in the Registry
+
+ try:
+ subkey = r'SOFTWARE\Microsoft\MSBuild\ToolsVersions\14.0'
+ with _reg_open_key(winreg.HKEY_LOCAL_MACHINE, subkey) as hKey:
value, regtype = winreg.QueryValueEx(hKey, 'MSBuildToolsPath')
return value
- except (WindowsError, EnvironmentError) as e:
- return None
+ except (WindowsError, OSError):
+ return ''
+
+ return ''
diff --git a/modules/mono/utils/path_utils.cpp b/modules/mono/utils/path_utils.cpp
index c8581f6122..105c2c981e 100644
--- a/modules/mono/utils/path_utils.cpp
+++ b/modules/mono/utils/path_utils.cpp
@@ -56,9 +56,6 @@ String path_which(const String &p_name) {
for (int i = 0; i < env_path.size(); i++) {
String p = path_join(env_path[i], p_name);
- if (FileAccess::exists(p))
- return p;
-
#ifdef WINDOWS_ENABLED
for (int j = 0; j < exts.size(); j++) {
String p2 = p + exts[j];
@@ -66,6 +63,9 @@ String path_which(const String &p_name) {
if (FileAccess::exists(p2))
return p2;
}
+#else
+ if (FileAccess::exists(p))
+ return p;
#endif
}
diff --git a/modules/opus/audio_stream_opus.cpp b/modules/opus/audio_stream_opus.cpp
index c7748b9b21..06eab4c94d 100644
--- a/modules/opus/audio_stream_opus.cpp
+++ b/modules/opus/audio_stream_opus.cpp
@@ -267,7 +267,7 @@ void AudioStreamPlaybackOpus::seek(float p_time) {
frames_mixed = osrate * p_time;
}
-int AudioStreamPlaybackOpus::mix(int16_t *p_bufer, int p_frames) {
+int AudioStreamPlaybackOpus::mix(int16_t *p_buffer, int p_frames) {
if (!playing)
return 0;
@@ -281,7 +281,7 @@ int AudioStreamPlaybackOpus::mix(int16_t *p_bufer, int p_frames) {
break;
}
- int ret = op_read(opus_file, (opus_int16 *)p_bufer, todo * stream_channels, &current_section);
+ int ret = op_read(opus_file, (opus_int16 *)p_buffer, todo * stream_channels, &current_section);
if (ret < 0) {
playing = false;
ERR_EXPLAIN("Error reading Opus File: " + file);
@@ -325,7 +325,7 @@ int AudioStreamPlaybackOpus::mix(int16_t *p_bufer, int p_frames) {
frames_mixed += ret;
- p_bufer += ret * stream_channels;
+ p_buffer += ret * stream_channels;
p_frames -= ret;
}
diff --git a/modules/opus/audio_stream_opus.h b/modules/opus/audio_stream_opus.h
index 7b7740a804..f8d8f585cf 100644
--- a/modules/opus/audio_stream_opus.h
+++ b/modules/opus/audio_stream_opus.h
@@ -107,7 +107,7 @@ public:
virtual int get_minimum_buffer_size() const;
- virtual int mix(int16_t *p_bufer, int p_frames);
+ virtual int mix(int16_t *p_buffer, int p_frames);
AudioStreamPlaybackOpus();
~AudioStreamPlaybackOpus();
diff --git a/modules/visual_script/visual_script_builtin_funcs.cpp b/modules/visual_script/visual_script_builtin_funcs.cpp
index 4b294325dc..7c9d306831 100644
--- a/modules/visual_script/visual_script_builtin_funcs.cpp
+++ b/modules/visual_script/visual_script_builtin_funcs.cpp
@@ -78,6 +78,8 @@ const char *VisualScriptBuiltinFunc::func_name[VisualScriptBuiltinFunc::FUNC_MAX
"rad2deg",
"linear2db",
"db2linear",
+ "wrapi",
+ "wrapf",
"max",
"min",
"clamp",
@@ -198,6 +200,8 @@ int VisualScriptBuiltinFunc::get_func_argument_count(BuiltinFunc p_func) {
case MATH_LERP:
case MATH_INVERSE_LERP:
case MATH_DECTIME:
+ case MATH_WRAP:
+ case MATH_WRAPF:
case LOGIC_CLAMP:
return 3;
case MATH_RANGE_LERP:
@@ -364,6 +368,22 @@ PropertyInfo VisualScriptBuiltinFunc::get_input_value_port_info(int p_idx) const
case MATH_DB2LINEAR: {
return PropertyInfo(Variant::REAL, "db");
} break;
+ case MATH_WRAP: {
+ if (p_idx == 0)
+ return PropertyInfo(Variant::INT, "value");
+ else if (p_idx == 1)
+ return PropertyInfo(Variant::INT, "min");
+ else
+ return PropertyInfo(Variant::INT, "max");
+ } break;
+ case MATH_WRAPF: {
+ if (p_idx == 0)
+ return PropertyInfo(Variant::REAL, "value");
+ else if (p_idx == 1)
+ return PropertyInfo(Variant::REAL, "min");
+ else
+ return PropertyInfo(Variant::REAL, "max");
+ } break;
case LOGIC_MAX: {
if (p_idx == 0)
return PropertyInfo(Variant::REAL, "a");
@@ -549,9 +569,13 @@ PropertyInfo VisualScriptBuiltinFunc::get_output_value_port_info(int p_idx) cons
case MATH_DEG2RAD:
case MATH_RAD2DEG:
case MATH_LINEAR2DB:
+ case MATH_WRAPF:
case MATH_DB2LINEAR: {
t = Variant::REAL;
} break;
+ case MATH_WRAP: {
+ t = Variant::INT;
+ } break;
case LOGIC_MAX:
case LOGIC_MIN:
case LOGIC_CLAMP: {
@@ -898,6 +922,18 @@ void VisualScriptBuiltinFunc::exec_func(BuiltinFunc p_func, const Variant **p_in
VALIDATE_ARG_NUM(0);
*r_return = Math::db2linear((double)*p_inputs[0]);
} break;
+ case VisualScriptBuiltinFunc::MATH_WRAP: {
+ VALIDATE_ARG_NUM(0);
+ VALIDATE_ARG_NUM(1);
+ VALIDATE_ARG_NUM(2);
+ *r_return = Math::wrapi((int64_t)*p_inputs[0], (int64_t)*p_inputs[1], (int64_t)*p_inputs[2]);
+ } break;
+ case VisualScriptBuiltinFunc::MATH_WRAPF: {
+ VALIDATE_ARG_NUM(0);
+ VALIDATE_ARG_NUM(1);
+ VALIDATE_ARG_NUM(2);
+ *r_return = Math::wrapf((double)*p_inputs[0], (double)*p_inputs[1], (double)*p_inputs[2]);
+ } break;
case VisualScriptBuiltinFunc::LOGIC_MAX: {
if (p_inputs[0]->get_type() == Variant::INT && p_inputs[1]->get_type() == Variant::INT) {
@@ -1258,6 +1294,8 @@ void VisualScriptBuiltinFunc::_bind_methods() {
BIND_ENUM_CONSTANT(MATH_RAD2DEG);
BIND_ENUM_CONSTANT(MATH_LINEAR2DB);
BIND_ENUM_CONSTANT(MATH_DB2LINEAR);
+ BIND_ENUM_CONSTANT(MATH_WRAP);
+ BIND_ENUM_CONSTANT(MATH_WRAPF);
BIND_ENUM_CONSTANT(LOGIC_MAX);
BIND_ENUM_CONSTANT(LOGIC_MIN);
BIND_ENUM_CONSTANT(LOGIC_CLAMP);
@@ -1343,6 +1381,8 @@ void register_visual_script_builtin_func_node() {
VisualScriptLanguage::singleton->add_register_func("functions/built_in/rad2deg", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_RAD2DEG>);
VisualScriptLanguage::singleton->add_register_func("functions/built_in/linear2db", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_LINEAR2DB>);
VisualScriptLanguage::singleton->add_register_func("functions/built_in/db2linear", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_DB2LINEAR>);
+ VisualScriptLanguage::singleton->add_register_func("functions/built_in/wrapi", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_WRAP>);
+ VisualScriptLanguage::singleton->add_register_func("functions/built_in/wrapf", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_WRAPF>);
VisualScriptLanguage::singleton->add_register_func("functions/built_in/max", create_builtin_func_node<VisualScriptBuiltinFunc::LOGIC_MAX>);
VisualScriptLanguage::singleton->add_register_func("functions/built_in/min", create_builtin_func_node<VisualScriptBuiltinFunc::LOGIC_MIN>);
diff --git a/modules/visual_script/visual_script_builtin_funcs.h b/modules/visual_script/visual_script_builtin_funcs.h
index 5758d23e8f..34a2825938 100644
--- a/modules/visual_script/visual_script_builtin_funcs.h
+++ b/modules/visual_script/visual_script_builtin_funcs.h
@@ -77,6 +77,8 @@ public:
MATH_RAD2DEG,
MATH_LINEAR2DB,
MATH_DB2LINEAR,
+ MATH_WRAP,
+ MATH_WRAPF,
LOGIC_MAX,
LOGIC_MIN,
LOGIC_CLAMP,
diff --git a/modules/vorbis/audio_stream_ogg_vorbis.cpp b/modules/vorbis/audio_stream_ogg_vorbis.cpp
index 0afb889199..6235799fc2 100644
--- a/modules/vorbis/audio_stream_ogg_vorbis.cpp
+++ b/modules/vorbis/audio_stream_ogg_vorbis.cpp
@@ -92,7 +92,7 @@ long AudioStreamPlaybackOGGVorbis::_ov_tell_func(void *_f) {
return fa->get_position();
}
-int AudioStreamPlaybackOGGVorbis::mix(int16_t *p_bufer, int p_frames) {
+int AudioStreamPlaybackOGGVorbis::mix(int16_t *p_buffer, int p_frames) {
if (!playing)
return 0;
@@ -109,9 +109,9 @@ int AudioStreamPlaybackOGGVorbis::mix(int16_t *p_bufer, int p_frames) {
//printf("to mix %i - mix me %i bytes\n",to_mix,to_mix*stream_channels*sizeof(int16_t));
#ifdef BIG_ENDIAN_ENABLED
- long ret = ov_read(&vf, (char *)p_bufer, todo * stream_channels * sizeof(int16_t), 1, 2, 1, &current_section);
+ long ret = ov_read(&vf, (char *)p_buffer, todo * stream_channels * sizeof(int16_t), 1, 2, 1, &current_section);
#else
- long ret = ov_read(&vf, (char *)p_bufer, todo * stream_channels * sizeof(int16_t), 0, 2, 1, &current_section);
+ long ret = ov_read(&vf, (char *)p_buffer, todo * stream_channels * sizeof(int16_t), 0, 2, 1, &current_section);
#endif
if (ret < 0) {
@@ -162,7 +162,7 @@ int AudioStreamPlaybackOGGVorbis::mix(int16_t *p_bufer, int p_frames) {
frames_mixed += ret;
- p_bufer += ret * stream_channels;
+ p_buffer += ret * stream_channels;
p_frames -= ret;
}
diff --git a/modules/vorbis/audio_stream_ogg_vorbis.h b/modules/vorbis/audio_stream_ogg_vorbis.h
index 929b2651e9..79eadec56e 100644
--- a/modules/vorbis/audio_stream_ogg_vorbis.h
+++ b/modules/vorbis/audio_stream_ogg_vorbis.h
@@ -103,7 +103,7 @@ public:
virtual int get_mix_rate() const { return stream_srate; }
virtual int get_minimum_buffer_size() const { return 0; }
- virtual int mix(int16_t *p_bufer, int p_frames);
+ virtual int mix(int16_t *p_buffer, int p_frames);
AudioStreamPlaybackOGGVorbis();
~AudioStreamPlaybackOGGVorbis();