Add an ICollisionCallback to ISceneNodeAnimatorCollisionResponse to inform the application when collisions occur (and allow it to ignore them).  Thanks to garrit for this patch!  The existing collisionResponseAnimator unit test has been updated to exercise the new functionality.

git-svn-id: svn://svn.code.sf.net/p/irrlicht/code/trunk@2111 dfc29bdd-3216-0410-991c-e03cc46cb475
master
Rogerborg 2009-01-20 19:10:46 +00:00
parent e464ba11f4
commit d021013af2
6 changed files with 175 additions and 11 deletions

View File

@ -1,5 +1,7 @@
Changes in version 1.6
- Added an ICollisionCallback to ISceneNodeAnimatorCollisionResponse, to inform the application that a collision has occured. Thanks to garrittg for this.
- Added an startPosition parameter to createFlyCircleAnimator() to allow starting the animator at any position on the circle.
- Many uses of dimension2d<s32> changed to dimension2d<u32>, including IImage, ITexture and screen dimensions. You will have to change (at least) createDevice() calls to use dimension2d<u32>

View File

@ -12,6 +12,30 @@ namespace irr
namespace scene
{
class ISceneNodeAnimatorCollisionResponse;
//! Callback interface for catching events of collisions.
/** Implement this interface and use
ISceneNodeAnimatorCollisionResponse::setCollisionCallback to be able to
be notified if a collision has occurred.
**/
class ICollisionCallback : public virtual IReferenceCounted
{
public:
//! Will be called when a collision occurrs.
/** See ISceneNodeAnimatorCollisionResponse::setCollisionCallback for more information.
\param animator: Collision response animator in which the collision occurred. You can call
this animator's methods to find the node, collisionPoint and/or collision triangle.
\retval true if the collision was handled in the animator. The animator's target
node will *not* be moved to the collision point, but will instead move directly
to the location that triggered the collision check.
\retval false if the collision was not handled in the animator. The animator's
target node will be moved to the collision position.
*/
virtual bool onCollision(ISceneNodeAnimatorCollisionResponse* animator) = 0;
};
//! Special scene node animator for doing automatic collision detection and response.
/** This scene node animator can be attached to any single scene node
and will then prevent it from moving through specified collision geometry
@ -108,6 +132,21 @@ namespace scene
/** \return The node that this animator is acting on. */
virtual ISceneNode* getTargetNode(void) const = 0;
//! Returns true if a collision occurred during the last animateNode()
virtual bool collisionOccurred() const = 0;
//! Returns the point of collision
virtual core::vector3df getCollisionPoint() const = 0;
//! Returns the last triangle that caused a collision
virtual core::triangle3df getCollisionTriangle() const = 0;
//! Sets a callback interface which will be called if a collision occurs.
/** \param callback: collision callback handler that will be called when a collision
occurs. Set this to 0 to disable the callback.
*/
virtual void setCollisionCallback(ICollisionCallback* callback) = 0;
};

View File

@ -24,7 +24,8 @@ CSceneNodeAnimatorCollisionResponse::CSceneNodeAnimatorCollisionResponse(
: Radius(ellipsoidRadius), Gravity(gravityPerSecond), Translation(ellipsoidTranslation),
World(world), Object(object), SceneManager(scenemanager), LastTime(0),
SlidingSpeed(slidingSpeed), Falling(false), IsCamera(false),
AnimateCameraTarget(true)
AnimateCameraTarget(true), CollisionOccurred(false),
CollisionCallback(0)
{
#ifdef _DEBUG
setDebugName("CSceneNodeAnimatorCollisionResponse");
@ -42,6 +43,9 @@ CSceneNodeAnimatorCollisionResponse::~CSceneNodeAnimatorCollisionResponse()
{
if (World)
World->drop();
if (CollisionCallback)
CollisionCallback->drop();
}
@ -136,6 +140,8 @@ ITriangleSelector* CSceneNodeAnimatorCollisionResponse::getWorld() const
void CSceneNodeAnimatorCollisionResponse::animateNode(ISceneNode* node, u32 timeMs)
{
CollisionOccurred = false;
if (node != Object)
setNode(node);
@ -150,7 +156,8 @@ void CSceneNodeAnimatorCollisionResponse::animateNode(ISceneNode* node, u32 time
FallingVelocity += Gravity * (f32)diff * 0.001f;
core::triangle3df triangle = RefTriangle;
CollisionTriangle = RefTriangle;
CollisionPoint = core::vector3df();
core::vector3df force = vel + FallingVelocity;
@ -161,14 +168,15 @@ void CSceneNodeAnimatorCollisionResponse::animateNode(ISceneNode* node, u32 time
// TODO: divide SlidingSpeed by frame time
bool f = false;
core::vector3df collisionPosition; // Not used.
pos = SceneManager->getSceneCollisionManager()->getCollisionResultPosition(
World, LastPosition-Translation,
Radius, vel, triangle, collisionPosition, f, SlidingSpeed, FallingVelocity);
Radius, vel, CollisionTriangle, CollisionPoint, f, SlidingSpeed, FallingVelocity);
CollisionOccurred = (CollisionTriangle != RefTriangle);
pos += Translation;
if (f)//triangle == RefTriangle)
if (f)//CollisionTriangle == RefTriangle)
{
Falling = true;
}
@ -178,7 +186,20 @@ void CSceneNodeAnimatorCollisionResponse::animateNode(ISceneNode* node, u32 time
FallingVelocity.set(0, 0, 0);
}
Object->setPosition(pos);
bool collisionConsumed = false;
CollisionPoint = pos;
if (CollisionOccurred)
{
CollisionPoint = pos;
if(CollisionCallback)
collisionConsumed = CollisionCallback->onCollision(this);
}
if(!collisionConsumed)
Object->setPosition(pos);
}
// move camera target
@ -238,6 +259,16 @@ ISceneNodeAnimator* CSceneNodeAnimatorCollisionResponse::createClone(ISceneNode*
return newAnimator;
}
void CSceneNodeAnimatorCollisionResponse::setCollisionCallback(ICollisionCallback* callback)
{
if (CollisionCallback)
CollisionCallback->drop();
CollisionCallback = callback;
if (CollisionCallback)
CollisionCallback->grab();
}
} // end namespace scene
} // end namespace irr

View File

@ -95,6 +95,21 @@ namespace scene
//! Gets the single node that this animator is acting on.
virtual ISceneNode* getTargetNode(void) const { return Object; }
//! Returns true if a collision occurred during the last animateNode()
virtual bool collisionOccurred() const { return CollisionOccurred; }
//! Returns the last point of collision.
virtual core::vector3df getCollisionPoint() const { return CollisionPoint; }
//! Returns the last triangle that caused a collision.
virtual core::triangle3df getCollisionTriangle() const { return CollisionTriangle; }
//! Sets a callback interface which will be called if a collision occurs.
/** \param callback: collision callback handler that will be called when a collision
occurs. Set this to 0 to disable the callback.
*/
virtual void setCollisionCallback(ICollisionCallback* callback);
private:
void setNode(ISceneNode* node);
@ -115,6 +130,12 @@ namespace scene
bool Falling;
bool IsCamera;
bool AnimateCameraTarget;
bool CollisionOccurred;
core::vector3df CollisionPoint;
core::triangle3df CollisionTriangle;
ICollisionCallback* CollisionCallback;
};
} // end namespace scene

View File

@ -10,6 +10,50 @@ using namespace core;
using namespace scene;
using namespace video;
static bool expectedCollisionCallbackPositions = true;
class CMyCollisionCallback : public ICollisionCallback
{
public:
bool onCollision(ISceneNodeAnimatorCollisionResponse* animator)
{
const vector3df & collisionPoint = animator->getCollisionPoint();
logTestString("Collision callback at %f %f %f\n",
collisionPoint.X, collisionPoint.Y, collisionPoint.Z);
if(collisionPoint != ExpectedCollisionPoint)
{
logTestString("*** Error: expected %f %f %f\n",
ExpectedCollisionPoint.X, ExpectedCollisionPoint.Y, ExpectedCollisionPoint.Z);
expectedCollisionCallbackPositions = false;
assert(false);
}
if(animator->getTargetNode() != ExpectedTarget)
{
logTestString("*** Error: wrong node\n");
expectedCollisionCallbackPositions = false;
assert(false);
}
return ConsumeCollision;
}
void setNextExpectedCollision(ISceneNode* target, const vector3df& point, bool consume)
{
ExpectedTarget = target;
ExpectedCollisionPoint = point;
ConsumeCollision = consume;
}
private:
ISceneNode * ExpectedTarget;
vector3df ExpectedCollisionPoint;
bool ConsumeCollision;
};
/** Test that collision response animator will reset itself when removed from a
scene node, so that the scene node can then be moved without the animator
@ -39,6 +83,10 @@ bool collisionResponseAnimator(void)
vector3df(10,10,10),
vector3df(0, 0, 0));
testNode1->addAnimator(collisionAnimator1);
CMyCollisionCallback collisionCallback;
collisionAnimator1->setCollisionCallback(&collisionCallback);
collisionAnimator1->drop();
collisionAnimator1 = 0;
@ -48,6 +96,7 @@ bool collisionResponseAnimator(void)
vector3df(10,10,10),
vector3df(0, 0, 0));
testNode2->addAnimator(collisionAnimator2);
collisionAnimator2->setCollisionCallback(&collisionCallback);
wallSelector->drop();
// Don't drop() collisionAnimator2 since we're going to use it.
@ -59,6 +108,7 @@ bool collisionResponseAnimator(void)
// Try to move both nodes to the right of the wall.
// This one should be stopped by its animator.
testNode1->setPosition(vector3df(50, 0,0));
collisionCallback.setNextExpectedCollision(testNode1, vector3df(-15.004999f, 0, 0), false);
// Whereas this one, by forcing the animator to update its target node, should be
// able to pass through the wall. (In <=1.6 it was stopped by the wall even if
@ -89,17 +139,38 @@ bool collisionResponseAnimator(void)
// Now try to move the second node back through the wall again. Now it should be
// stopped by the wall.
testNode2->setPosition(vector3df(-50, 0, 0));
// We'll consume this collision, so the node will actually move all the way through.
collisionCallback.setNextExpectedCollision(testNode2, vector3df(15.004999f, 0, 0), true);
device->run();
smgr->drawAll();
if(testNode2->getAbsolutePosition().X < 15.f)
if(testNode2->getAbsolutePosition().X != -50.f)
{
logTestString("collisionResponseAnimator test node 2 wasn't stopped from moving.\n");
logTestString("collisionResponseAnimator test node 2 was stopped from moving.\n");
assert(false);
result = false;
}
// Now we'll try to move it back to the right and allow it to be stopped.
collisionCallback.setNextExpectedCollision(testNode2, vector3df(-15.004999f, 0, 0), false);
testNode2->setPosition(vector3df(50, 0, 0));
device->run();
smgr->drawAll();
if(testNode2->getAbsolutePosition().X > -15.f)
{
logTestString("collisionResponseAnimator test node 2 moved too far.\n");
assert(false);
result = false;
}
device->drop();
result &= expectedCollisionCallbackPositions;
return result;
}

View File

@ -1,2 +1,2 @@
Test suite pass at GMT Tue Jan 20 12:53:02 2009
Test suite pass at GMT Tue Jan 20 19:08:37 2009