Browse Source

Fixed continuous collision detection to work with the compound shape.
Added CCD radius & motion threshold attributes to RigidBody.
Use CCD for NinjaSnowWar snowballs to prevent their tunneling through the ground.

Lasse Öörni 13 years ago
parent
commit
08ac95dbf7

+ 9 - 7
Bin/Data/Objects/SnowBall.xml

@@ -1,11 +1,11 @@
 <?xml version="1.0"?>
 <?xml version="1.0"?>
-<node id="3">
+<node id="2">
 	<attribute name="Name" value="SnowBall" />
 	<attribute name="Name" value="SnowBall" />
 	<attribute name="Position" value="0 10 20" />
 	<attribute name="Position" value="0 10 20" />
 	<attribute name="Rotation" value="1 0 0 0" />
 	<attribute name="Rotation" value="1 0 0 0" />
 	<attribute name="Scale" value="1 1 1" />
 	<attribute name="Scale" value="1 1 1" />
 	<attribute name="Variables" />
 	<attribute name="Variables" />
-	<component type="ScriptInstance" id="16777218">
+	<component type="ScriptInstance" id="16777216">
 		<attribute name="Script File" value="ScriptFile;Scripts/NinjaSnowWar.as" />
 		<attribute name="Script File" value="ScriptFile;Scripts/NinjaSnowWar.as" />
 		<attribute name="Class Name" value="SnowBall" />
 		<attribute name="Class Name" value="SnowBall" />
 		<attribute name="Is Active" value="true" />
 		<attribute name="Is Active" value="true" />
@@ -14,13 +14,13 @@
 		<attribute name="Delayed Method Calls" value="0" />
 		<attribute name="Delayed Method Calls" value="0" />
 		<attribute name="Script Data" value="" />
 		<attribute name="Script Data" value="" />
 	</component>
 	</component>
-	<component type="NetworkPriority" id="16777219">
+	<component type="NetworkPriority" id="16777217">
 		<attribute name="Base Priority" value="100" />
 		<attribute name="Base Priority" value="100" />
 		<attribute name="Distance Factor" value="0.02" />
 		<attribute name="Distance Factor" value="0.02" />
 		<attribute name="Minimum Priority" value="25" />
 		<attribute name="Minimum Priority" value="25" />
 		<attribute name="Always Update Owner" value="true" />
 		<attribute name="Always Update Owner" value="true" />
 	</component>
 	</component>
-	<component type="StaticModel" id="9">
+	<component type="StaticModel" id="4">
 		<attribute name="Model" value="Model;Models/SnowBall.mdl" />
 		<attribute name="Model" value="Model;Models/SnowBall.mdl" />
 		<attribute name="Material" value="Material;Materials/Snow.xml" />
 		<attribute name="Material" value="Material;Materials/Snow.xml" />
 		<attribute name="Is Visible" value="true" />
 		<attribute name="Is Visible" value="true" />
@@ -37,16 +37,16 @@
 		<attribute name="Zone Mask" value="-1" />
 		<attribute name="Zone Mask" value="-1" />
 		<attribute name="Ray/Occl. LOD Level" value="-1" />
 		<attribute name="Ray/Occl. LOD Level" value="-1" />
 	</component>
 	</component>
-	<component type="CollisionShape" id="10">
+	<component type="CollisionShape" id="5">
 		<attribute name="Shape Type" value="Box" />
 		<attribute name="Shape Type" value="Box" />
 		<attribute name="Size" value="15 15 15" />
 		<attribute name="Size" value="15 15 15" />
 		<attribute name="Offset Position" value="0 0 0" />
 		<attribute name="Offset Position" value="0 0 0" />
 		<attribute name="Offset Rotation" value="1 0 0 0" />
 		<attribute name="Offset Rotation" value="1 0 0 0" />
-		<attribute name="Collision Margin" value="1" />
 		<attribute name="Model" value="Model;" />
 		<attribute name="Model" value="Model;" />
 		<attribute name="LOD Level" value="0" />
 		<attribute name="LOD Level" value="0" />
+		<attribute name="Collision Margin" value="1" />
 	</component>
 	</component>
-	<component type="RigidBody" id="11">
+	<component type="RigidBody" id="6">
 		<attribute name="Physics Position" value="0 10 20" />
 		<attribute name="Physics Position" value="0 10 20" />
 		<attribute name="Physics Rotation" value="1 0 0 0" />
 		<attribute name="Physics Rotation" value="1 0 0 0" />
 		<attribute name="Mass" value="10" />
 		<attribute name="Mass" value="10" />
@@ -62,6 +62,8 @@
 		<attribute name="Angular Rest Threshold" value="1" />
 		<attribute name="Angular Rest Threshold" value="1" />
 		<attribute name="Collision Layer" value="1" />
 		<attribute name="Collision Layer" value="1" />
 		<attribute name="Collision Mask" value="3" />
 		<attribute name="Collision Mask" value="3" />
+		<attribute name="CCD Radius" value="5" />
+		<attribute name="CCD Motion Threshold" value="15" />
 		<attribute name="Collision Event Mode" value="When Active" />
 		<attribute name="Collision Event Mode" value="When Active" />
 		<attribute name="Use Gravity" value="true" />
 		<attribute name="Use Gravity" value="true" />
 		<attribute name="Is Kinematic" value="false" />
 		<attribute name="Is Kinematic" value="false" />

+ 0 - 3
Bin/Data/Scripts/NinjaSnowWar/Ninja.as

@@ -216,9 +216,6 @@ class Ninja : GameObject
         if ((controls.IsPressed(CTRL_FIRE, prevControls)) && (throwTime <= 0))
         if ((controls.IsPressed(CTRL_FIRE, prevControls)) && (throwTime <= 0))
         {
         {
             Vector3 projectileVel = GetAim() * ninjaThrowVelocity;
             Vector3 projectileVel = GetAim() * ninjaThrowVelocity;
-            // Hack: clamp projectile downward velocity to limit tunnelling when snowball is thrown almost directly down
-            if (projectileVel.y < -1000.0)
-                projectileVel.y = -1000.0;
 
 
             animCtrl.Play("Models/Ninja_Attack1.ani", LAYER_ATTACK, false, 0.0);
             animCtrl.Play("Models/Ninja_Attack1.ani", LAYER_ATTACK, false, 0.0);
             animCtrl.SetTime("Models/Ninja_Attack1.ani", 0.0); // Always play from beginning
             animCtrl.SetTime("Models/Ninja_Attack1.ani", 0.0); // Always play from beginning

+ 3 - 1
Docs/Reference.dox

@@ -835,7 +835,9 @@ The collision behaviour of a rigid body is controlled by several variables. Firs
 
 
 By default rigid bodies can move and rotate about all 3 coordinate axes when forces are applied. To limit the movement, use \ref RigidBody::SetLinearFactor "SetLinearFactor()" and \ref RigidBody::SetAngularFactor "SetAngularFactor()" and set the axes you wish to use to 1 and those you do not wish to use to 0. For example moving humanoid characters are often represented by a capsule shape: to ensure they stay upright and only rotate when you explicitly set the rotation in code, set the angular factor to 0, 0, 0.
 By default rigid bodies can move and rotate about all 3 coordinate axes when forces are applied. To limit the movement, use \ref RigidBody::SetLinearFactor "SetLinearFactor()" and \ref RigidBody::SetAngularFactor "SetAngularFactor()" and set the axes you wish to use to 1 and those you do not wish to use to 0. For example moving humanoid characters are often represented by a capsule shape: to ensure they stay upright and only rotate when you explicitly set the rotation in code, set the angular factor to 0, 0, 0.
 
 
-The physics simulation does all calculations in world space. Nodes containing a RigidBody component should be parented to the Scene (root node) to ensure correct operation.
+To prevent tunneling of a fast moving rigid body through obstacles, continuous collision detection can be used. It approximates the object as a swept sphere, but has a performance cost, so it should be used only when necessary. Call \ref RigidBody::SetCcdRadius "SetCcdRadius()" and \ref RigidBody::SetCcdMotionThreshold "SetCcdMotionThreshold()" with non-zero values to enable. To prevent false collisions, the body's actual collision shape should completely contain the radius. The motion threshold is the required motion per simulation step for CCD to kick in: for example a box with size 1 should have motion threshold 1 as well.
+
+All physics calculations are performed in world space. Nodes containing a RigidBody component should be parented to the Scene (root node) to ensure correct operation.
 
 
 The physics world sends 3 types of events during its update step:
 The physics world sends 3 types of events during its update step:
 
 

+ 2 - 1
Docs/ScriptAPI.dox

@@ -3997,11 +3997,12 @@ Properties:<br>
 - float angularDamping
 - float angularDamping
 - float friction
 - float friction
 - float restitution
 - float restitution
+- float ccdRadius
+- float ccdMotionThreshold
 - bool useGravity
 - bool useGravity
 - bool phantom
 - bool phantom
 - bool kinematic
 - bool kinematic
 - bool active (readonly)
 - bool active (readonly)
-- float ccdRadius
 - uint collisionLayer
 - uint collisionLayer
 - uint collisionMask
 - uint collisionMask
 - CollisionEventMode collisionEventMode
 - CollisionEventMode collisionEventMode

+ 4 - 0
Engine/Engine/PhysicsAPI.cpp

@@ -154,6 +154,10 @@ static void RegisterRigidBody(asIScriptEngine* engine)
     engine->RegisterObjectMethod("RigidBody", "float get_friction() const", asMETHOD(RigidBody, GetFriction), asCALL_THISCALL);
     engine->RegisterObjectMethod("RigidBody", "float get_friction() const", asMETHOD(RigidBody, GetFriction), asCALL_THISCALL);
     engine->RegisterObjectMethod("RigidBody", "void set_restitution(float)", asMETHOD(RigidBody, SetRestitution), asCALL_THISCALL);
     engine->RegisterObjectMethod("RigidBody", "void set_restitution(float)", asMETHOD(RigidBody, SetRestitution), asCALL_THISCALL);
     engine->RegisterObjectMethod("RigidBody", "float get_restitution() const", asMETHOD(RigidBody, GetRestitution), asCALL_THISCALL);
     engine->RegisterObjectMethod("RigidBody", "float get_restitution() const", asMETHOD(RigidBody, GetRestitution), asCALL_THISCALL);
+    engine->RegisterObjectMethod("RigidBody", "void set_ccdRadius(float)", asMETHOD(RigidBody, SetCcdRadius), asCALL_THISCALL);
+    engine->RegisterObjectMethod("RigidBody", "float get_ccdRadius() const", asMETHOD(RigidBody, GetCcdRadius), asCALL_THISCALL);
+    engine->RegisterObjectMethod("RigidBody", "void set_ccdMotionThreshold(float)", asMETHOD(RigidBody, SetCcdMotionThreshold), asCALL_THISCALL);
+    engine->RegisterObjectMethod("RigidBody", "float get_ccdMotionThreshold() const", asMETHOD(RigidBody, GetCcdMotionThreshold), asCALL_THISCALL);
     engine->RegisterObjectMethod("RigidBody", "void set_useGravity(bool)", asMETHOD(RigidBody, SetUseGravity), asCALL_THISCALL);
     engine->RegisterObjectMethod("RigidBody", "void set_useGravity(bool)", asMETHOD(RigidBody, SetUseGravity), asCALL_THISCALL);
     engine->RegisterObjectMethod("RigidBody", "bool get_useGravity() const", asMETHOD(RigidBody, GetUseGravity), asCALL_THISCALL);
     engine->RegisterObjectMethod("RigidBody", "bool get_useGravity() const", asMETHOD(RigidBody, GetUseGravity), asCALL_THISCALL);
     engine->RegisterObjectMethod("RigidBody", "void set_phantom(bool)", asMETHOD(RigidBody, SetPhantom), asCALL_THISCALL);
     engine->RegisterObjectMethod("RigidBody", "void set_phantom(bool)", asMETHOD(RigidBody, SetPhantom), asCALL_THISCALL);

+ 1 - 0
Engine/Physics/PhysicsWorld.cpp

@@ -86,6 +86,7 @@ PhysicsWorld::PhysicsWorld(Context* context) :
     world_ = new btDiscreteDynamicsWorld(collisionDispatcher_, broadphase_, solver_, collisionConfiguration_);
     world_ = new btDiscreteDynamicsWorld(collisionDispatcher_, broadphase_, solver_, collisionConfiguration_);
     
     
     world_->setGravity(ToBtVector3(DEFAULT_GRAVITY));
     world_->setGravity(ToBtVector3(DEFAULT_GRAVITY));
+    world_->getDispatchInfo().m_useContinuous = true;
     world_->setDebugDrawer(this);
     world_->setDebugDrawer(this);
     world_->setInternalTickCallback(InternalPreTickCallback, static_cast<void*>(this), true);
     world_->setInternalTickCallback(InternalPreTickCallback, static_cast<void*>(this), true);
     world_->setInternalTickCallback(InternalTickCallback, static_cast<void*>(this), false);
     world_->setInternalTickCallback(InternalTickCallback, static_cast<void*>(this), false);

+ 32 - 0
Engine/Physics/RigidBody.cpp

@@ -108,6 +108,8 @@ void RigidBody::RegisterObject(Context* context)
     ACCESSOR_ATTRIBUTE(RigidBody, VAR_FLOAT, "Angular Rest Threshold", GetAngularRestThreshold, SetAngularRestThreshold, float, 0.01f, AM_DEFAULT);
     ACCESSOR_ATTRIBUTE(RigidBody, VAR_FLOAT, "Angular Rest Threshold", GetAngularRestThreshold, SetAngularRestThreshold, float, 0.01f, AM_DEFAULT);
     ATTRIBUTE(RigidBody, VAR_INT, "Collision Layer", collisionLayer_, DEFAULT_COLLISION_LAYER, AM_DEFAULT);
     ATTRIBUTE(RigidBody, VAR_INT, "Collision Layer", collisionLayer_, DEFAULT_COLLISION_LAYER, AM_DEFAULT);
     ATTRIBUTE(RigidBody, VAR_INT, "Collision Mask", collisionMask_, DEFAULT_COLLISION_MASK, AM_DEFAULT);
     ATTRIBUTE(RigidBody, VAR_INT, "Collision Mask", collisionMask_, DEFAULT_COLLISION_MASK, AM_DEFAULT);
+    ACCESSOR_ATTRIBUTE(RigidBody, VAR_FLOAT, "CCD Radius", GetCcdRadius, SetCcdRadius, float, 0.0f, AM_DEFAULT);
+    ACCESSOR_ATTRIBUTE(RigidBody, VAR_FLOAT, "CCD Motion Threshold", GetCcdMotionThreshold, SetCcdMotionThreshold, float, 0.0f, AM_DEFAULT);
     REF_ACCESSOR_ATTRIBUTE(RigidBody, VAR_BUFFER, "Network Angular Velocity", GetNetAngularVelocityAttr, SetNetAngularVelocityAttr, PODVector<unsigned char>, PODVector<unsigned char>(), AM_NET | AM_LATESTDATA | AM_NOEDIT);
     REF_ACCESSOR_ATTRIBUTE(RigidBody, VAR_BUFFER, "Network Angular Velocity", GetNetAngularVelocityAttr, SetNetAngularVelocityAttr, PODVector<unsigned char>, PODVector<unsigned char>(), AM_NET | AM_LATESTDATA | AM_NOEDIT);
     ENUM_ATTRIBUTE(RigidBody, "Collision Event Mode", collisionEventMode_, collisionEventModeNames, COLLISION_ACTIVE, AM_DEFAULT);
     ENUM_ATTRIBUTE(RigidBody, "Collision Event Mode", collisionEventMode_, collisionEventModeNames, COLLISION_ACTIVE, AM_DEFAULT);
     ACCESSOR_ATTRIBUTE(RigidBody, VAR_BOOL, "Use Gravity", GetUseGravity, SetUseGravity, bool, true, AM_DEFAULT);
     ACCESSOR_ATTRIBUTE(RigidBody, VAR_BOOL, "Use Gravity", GetUseGravity, SetUseGravity, bool, true, AM_DEFAULT);
@@ -286,6 +288,20 @@ void RigidBody::SetRestitution(float restitution)
         body_->setRestitution(restitution);
         body_->setRestitution(restitution);
 }
 }
 
 
+void RigidBody::SetCcdRadius(float radius)
+{
+    radius = Max(radius, 0.0f);
+    if (body_)
+        body_->setCcdSweptSphereRadius(radius);
+}
+
+void RigidBody::SetCcdMotionThreshold(float threshold)
+{
+    threshold = Max(threshold, 0.0f);
+    if (body_)
+        body_->setCcdMotionThreshold(threshold);
+}
+
 void RigidBody::SetUseGravity(bool enable)
 void RigidBody::SetUseGravity(bool enable)
 {
 {
     if (physicsWorld_ && body_ && enable != GetUseGravity())
     if (physicsWorld_ && body_ && enable != GetUseGravity())
@@ -519,6 +535,22 @@ float RigidBody::GetRestitution() const
         return 0.0f;
         return 0.0f;
 }
 }
 
 
+float RigidBody::GetCcdRadius() const
+{
+    if (body_)
+        return body_->getCcdSweptSphereRadius();
+    else
+        return 0.0f;
+}
+
+float RigidBody::GetCcdMotionThreshold() const
+{
+    if (body_)
+        return body_->getCcdMotionThreshold();
+    else
+        return 0.0f;
+}
+
 bool RigidBody::GetUseGravity() const
 bool RigidBody::GetUseGravity() const
 {
 {
     if (body_)
     if (body_)

+ 8 - 0
Engine/Physics/RigidBody.h

@@ -95,6 +95,10 @@ public:
     void SetFriction(float friction);
     void SetFriction(float friction);
     /// %Set restitution coefficient.
     /// %Set restitution coefficient.
     void SetRestitution(float restitution);
     void SetRestitution(float restitution);
+    /// %Set continuous collision detection swept sphere radius.
+    void SetCcdRadius(float radius);
+    /// %Set continuous collision detection motion-per-simulation-step threshold. 0 disables, which is the default.
+    void SetCcdMotionThreshold(float threshold);
     /// %Set whether gravity is applied to rigid body.
     /// %Set whether gravity is applied to rigid body.
     void SetUseGravity(bool enable);
     void SetUseGravity(bool enable);
     /// %Set rigid body kinematic mode. In kinematic mode forces are not applied to the rigid body.
     /// %Set rigid body kinematic mode. In kinematic mode forces are not applied to the rigid body.
@@ -162,6 +166,10 @@ public:
     float GetFriction() const;
     float GetFriction() const;
     /// Return restitution coefficient.
     /// Return restitution coefficient.
     float GetRestitution() const;
     float GetRestitution() const;
+    /// Return continuous collision detection swept sphere radius.
+    float GetCcdRadius() const;
+    /// Return continuous collision detection motion-per-simulation-step threshold.
+    float GetCcdMotionThreshold() const;
     /// Return whether rigid body uses gravity.
     /// Return whether rigid body uses gravity.
     bool GetUseGravity() const;
     bool GetUseGravity() const;
     /// Return kinematic mode flag.
     /// Return kinematic mode flag.

+ 4 - 1
ThirdParty/Bullet/src/BulletDynamics/Dynamics/btDiscreteDynamicsWorld.cpp

@@ -13,6 +13,7 @@ subject to the following restrictions:
 3. This notice may not be removed or altered from any source distribution.
 3. This notice may not be removed or altered from any source distribution.
 */
 */
 
 
+// Modified by Lasse Öörni for Urho3D
 
 
 #include "btDiscreteDynamicsWorld.h"
 #include "btDiscreteDynamicsWorld.h"
 
 
@@ -881,7 +882,9 @@ void	btDiscreteDynamicsWorld::integrateTransforms(btScalar timeStep)
 			if (getDispatchInfo().m_useContinuous && body->getCcdSquareMotionThreshold() && body->getCcdSquareMotionThreshold() < squareMotion)
 			if (getDispatchInfo().m_useContinuous && body->getCcdSquareMotionThreshold() && body->getCcdSquareMotionThreshold() < squareMotion)
 			{
 			{
 				BT_PROFILE("CCD motion clamping");
 				BT_PROFILE("CCD motion clamping");
-				if (body->getCollisionShape()->isConvex())
+				
+				// Urho3D: Rigid bodies always use a compound shape to allow offset positions, so do not check for being convex here
+				//if (body->getCollisionShape()->isConvex())
 				{
 				{
 					gNumClampedCcdMotions++;
 					gNumClampedCcdMotions++;
 #ifdef USE_STATIC_ONLY
 #ifdef USE_STATIC_ONLY