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

+ 2 - 1
Docs/ScriptAPI.dox

@@ -3997,11 +3997,12 @@ Properties:<br>
 - float angularDamping
 - float friction
 - float restitution
+- float ccdRadius
+- float ccdMotionThreshold
 - bool useGravity
 - bool phantom
 - bool kinematic
 - bool active (readonly)
-- float ccdRadius
 - uint collisionLayer
 - uint collisionMask
 - 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", "void set_restitution(float)", asMETHOD(RigidBody, SetRestitution), 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", "bool get_useGravity() const", asMETHOD(RigidBody, GetUseGravity), 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_->setGravity(ToBtVector3(DEFAULT_GRAVITY));
+    world_->getDispatchInfo().m_useContinuous = true;
     world_->setDebugDrawer(this);
     world_->setInternalTickCallback(InternalPreTickCallback, static_cast<void*>(this), true);
     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);
     ATTRIBUTE(RigidBody, VAR_INT, "Collision Layer", collisionLayer_, DEFAULT_COLLISION_LAYER, 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);
     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);
@@ -286,6 +288,20 @@ void RigidBody::SetRestitution(float 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)
 {
     if (physicsWorld_ && body_ && enable != GetUseGravity())
@@ -519,6 +535,22 @@ float RigidBody::GetRestitution() const
         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
 {
     if (body_)

+ 8 - 0
Engine/Physics/RigidBody.h

@@ -95,6 +95,10 @@ public:
     void SetFriction(float friction);
     /// %Set restitution coefficient.
     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.
     void SetUseGravity(bool enable);
     /// %Set rigid body kinematic mode. In kinematic mode forces are not applied to the rigid body.
@@ -162,6 +166,10 @@ public:
     float GetFriction() const;
     /// Return restitution coefficient.
     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.
     bool GetUseGravity() const;
     /// 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.
 */
 
+// Modified by Lasse Öörni for Urho3D
 
 #include "btDiscreteDynamicsWorld.h"
 
@@ -881,7 +882,9 @@ void	btDiscreteDynamicsWorld::integrateTransforms(btScalar timeStep)
 			if (getDispatchInfo().m_useContinuous && body->getCcdSquareMotionThreshold() && body->getCcdSquareMotionThreshold() < squareMotion)
 			{
 				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++;
 #ifdef USE_STATIC_ONLY