Implement raycasting with sphere shape

This commit is contained in:
Daniel Chappuis 2014-09-04 22:32:29 +02:00
parent 018b73ad04
commit 0dd55e716b
17 changed files with 92 additions and 45 deletions

View File

@ -213,13 +213,13 @@ bool CollisionBody::testPointInside(const Vector3& worldPoint) const {
} }
// Raycast method // Raycast method
bool CollisionBody::raycast(const Ray& ray, decimal distance) { bool CollisionBody::raycast(const Ray& ray) {
// For each collision shape of the body // For each collision shape of the body
for (ProxyShape* shape = mProxyCollisionShapes; shape != NULL; shape = shape->mNext) { for (ProxyShape* shape = mProxyCollisionShapes; shape != NULL; shape = shape->mNext) {
// Test if the ray hits the collision shape // Test if the ray hits the collision shape
if (shape->raycast(ray, distance)) return true; if (shape->raycast(ray)) return true;
} }
return false; return false;

View File

@ -166,7 +166,7 @@ class CollisionBody : public Body {
bool testPointInside(const Vector3& worldPoint) const; bool testPointInside(const Vector3& worldPoint) const;
/// Raycast method /// Raycast method
bool raycast(const Ray& ray, decimal distance = RAYCAST_INFINITY_DISTANCE); bool raycast(const Ray& ray);
/// Raycast method with feedback information /// Raycast method with feedback information
bool raycast(const Ray& ray, RaycastInfo& raycastInfo, bool raycast(const Ray& ray, RaycastInfo& raycastInfo,

View File

@ -100,7 +100,7 @@ class ProxyShape {
bool testPointInside(const Vector3& worldPoint); bool testPointInside(const Vector3& worldPoint);
/// Raycast method /// Raycast method
bool raycast(const Ray& ray, decimal distance = RAYCAST_INFINITY_DISTANCE); bool raycast(const Ray& ray);
/// Raycast method with feedback information /// Raycast method with feedback information
bool raycast(const Ray& ray, RaycastInfo& raycastInfo, bool raycast(const Ray& ray, RaycastInfo& raycastInfo,
@ -170,8 +170,8 @@ inline decimal ProxyShape::getMargin() const {
} }
// Raycast method // Raycast method
inline bool ProxyShape::raycast(const Ray& ray, decimal distance) { inline bool ProxyShape::raycast(const Ray& ray) {
return mCollisionShape->raycast(ray, this, distance); return mCollisionShape->raycast(ray, this);
} }
// Raycast method with feedback information // Raycast method with feedback information

View File

@ -62,7 +62,7 @@ void BoxShape::computeLocalInertiaTensor(Matrix3x3& tensor, decimal mass) const
} }
// Raycast method // Raycast method
bool BoxShape::raycast(const Ray& ray, ProxyShape* proxyShape, decimal distance) const { bool BoxShape::raycast(const Ray& ray, ProxyShape* proxyShape) const {
// TODO : Normalize the ray direction // TODO : Normalize the ray direction

View File

@ -79,8 +79,7 @@ class BoxShape : public CollisionShape {
virtual bool testPointInside(const Vector3& localPoint, ProxyShape* proxyShape) const; virtual bool testPointInside(const Vector3& localPoint, ProxyShape* proxyShape) const;
/// Raycast method /// Raycast method
virtual bool raycast(const Ray& ray, ProxyShape* proxyShape, virtual bool raycast(const Ray& ray, ProxyShape* proxyShape) const;
decimal distance = RAYCAST_INFINITY_DISTANCE) const;
/// Raycast method with feedback information /// Raycast method with feedback information
virtual bool raycast(const Ray& ray, RaycastInfo& raycastInfo, ProxyShape* proxyShape, virtual bool raycast(const Ray& ray, RaycastInfo& raycastInfo, ProxyShape* proxyShape,

View File

@ -143,7 +143,7 @@ bool CapsuleShape::testPointInside(const Vector3& localPoint, ProxyShape* proxyS
} }
// Raycast method // Raycast method
bool CapsuleShape::raycast(const Ray& ray, ProxyShape* proxyShape, decimal distance) const { bool CapsuleShape::raycast(const Ray& ray, ProxyShape* proxyShape) const {
// TODO : Normalize the ray direction // TODO : Normalize the ray direction

View File

@ -76,8 +76,7 @@ class CapsuleShape : public CollisionShape {
virtual bool testPointInside(const Vector3& localPoint, ProxyShape* proxyShape) const; virtual bool testPointInside(const Vector3& localPoint, ProxyShape* proxyShape) const;
/// Raycast method /// Raycast method
virtual bool raycast(const Ray& ray, ProxyShape* proxyShape, virtual bool raycast(const Ray& ray, ProxyShape* proxyShape) const;
decimal distance = RAYCAST_INFINITY_DISTANCE) const;
/// Raycast method with feedback information /// Raycast method with feedback information
virtual bool raycast(const Ray& ray, RaycastInfo& raycastInfo, ProxyShape* proxyShape, virtual bool raycast(const Ray& ray, RaycastInfo& raycastInfo, ProxyShape* proxyShape,

View File

@ -86,8 +86,7 @@ class CollisionShape {
virtual bool testPointInside(const Vector3& worldPoint, ProxyShape* proxyShape) const=0; virtual bool testPointInside(const Vector3& worldPoint, ProxyShape* proxyShape) const=0;
/// Raycast method /// Raycast method
virtual bool raycast(const Ray& ray, ProxyShape* proxyShape, virtual bool raycast(const Ray& ray, ProxyShape* proxyShape) const=0;
decimal distance = RAYCAST_INFINITY_DISTANCE) const=0;
/// Raycast method with feedback information /// Raycast method with feedback information
virtual bool raycast(const Ray& ray, RaycastInfo& raycastInfo, ProxyShape* proxyShape, virtual bool raycast(const Ray& ray, RaycastInfo& raycastInfo, ProxyShape* proxyShape,

View File

@ -95,7 +95,7 @@ Vector3 ConeShape::getLocalSupportPointWithoutMargin(const Vector3& direction,
} }
// Raycast method // Raycast method
bool ConeShape::raycast(const Ray& ray, ProxyShape* proxyShape, decimal distance) const { bool ConeShape::raycast(const Ray& ray, ProxyShape* proxyShape) const {
// TODO : Normalize the ray direction // TODO : Normalize the ray direction

View File

@ -84,8 +84,7 @@ class ConeShape : public CollisionShape {
virtual bool testPointInside(const Vector3& localPoint, ProxyShape* proxyShape) const; virtual bool testPointInside(const Vector3& localPoint, ProxyShape* proxyShape) const;
/// Raycast method /// Raycast method
virtual bool raycast(const Ray& ray, ProxyShape* proxyShape, virtual bool raycast(const Ray& ray, ProxyShape* proxyShape) const;
decimal distance = RAYCAST_INFINITY_DISTANCE) const;
/// Raycast method with feedback information /// Raycast method with feedback information
virtual bool raycast(const Ray& ray, RaycastInfo& raycastInfo, ProxyShape* proxyShape, virtual bool raycast(const Ray& ray, RaycastInfo& raycastInfo, ProxyShape* proxyShape,

View File

@ -231,10 +231,10 @@ bool ConvexMeshShape::isEqualTo(const CollisionShape& otherCollisionShape) const
} }
// Raycast method // Raycast method
bool ConvexMeshShape::raycast(const Ray& ray, ProxyShape* proxyShape, decimal distance) const { bool ConvexMeshShape::raycast(const Ray& ray, ProxyShape* proxyShape) const {
RaycastInfo raycastInfo; RaycastInfo raycastInfo;
return proxyShape->mBody->mWorld.mCollisionDetection.mNarrowPhaseGJKAlgorithm.raycast( return proxyShape->mBody->mWorld.mCollisionDetection.mNarrowPhaseGJKAlgorithm.raycast(
ray, proxyShape, raycastInfo, distance); ray, proxyShape, raycastInfo, RAYCAST_INFINITY_DISTANCE);
} }
// Raycast method with feedback information // Raycast method with feedback information

View File

@ -105,8 +105,7 @@ class ConvexMeshShape : public CollisionShape {
virtual bool testPointInside(const Vector3& localPoint, ProxyShape* proxyShape) const; virtual bool testPointInside(const Vector3& localPoint, ProxyShape* proxyShape) const;
/// Raycast method /// Raycast method
virtual bool raycast(const Ray& ray, ProxyShape* proxyShape, virtual bool raycast(const Ray& ray, ProxyShape* proxyShape) const;
decimal distance = RAYCAST_INFINITY_DISTANCE) const;
/// Raycast method with feedback information /// Raycast method with feedback information
virtual bool raycast(const Ray& ray, RaycastInfo& raycastInfo, ProxyShape* proxyShape, virtual bool raycast(const Ray& ray, RaycastInfo& raycastInfo, ProxyShape* proxyShape,

View File

@ -88,7 +88,7 @@ Vector3 CylinderShape::getLocalSupportPointWithoutMargin(const Vector3& directio
} }
// Raycast method // Raycast method
bool CylinderShape::raycast(const Ray& ray, ProxyShape* proxyShape, decimal distance) const { bool CylinderShape::raycast(const Ray& ray, ProxyShape* proxyShape) const {
// TODO : Normalize the ray direction // TODO : Normalize the ray direction

View File

@ -81,8 +81,7 @@ class CylinderShape : public CollisionShape {
virtual bool testPointInside(const Vector3& localPoint, ProxyShape* proxyShape) const; virtual bool testPointInside(const Vector3& localPoint, ProxyShape* proxyShape) const;
/// Raycast method /// Raycast method
virtual bool raycast(const Ray& ray, ProxyShape* proxyShape, virtual bool raycast(const Ray& ray, ProxyShape* proxyShape) const;
decimal distance = RAYCAST_INFINITY_DISTANCE) const;
/// Raycast method with feedback information /// Raycast method with feedback information
virtual bool raycast(const Ray& ray, RaycastInfo& raycastInfo, ProxyShape* proxyShape, virtual bool raycast(const Ray& ray, RaycastInfo& raycastInfo, ProxyShape* proxyShape,

View File

@ -25,6 +25,7 @@
// Libraries // Libraries
#include "SphereShape.h" #include "SphereShape.h"
#include "collision/ProxyShape.h"
#include "configuration.h" #include "configuration.h"
#include <cassert> #include <cassert>
@ -47,21 +48,74 @@ SphereShape::~SphereShape() {
} }
// Raycast method // Raycast method
bool SphereShape::raycast(const Ray& ray, ProxyShape* proxyShape, decimal distance) const { bool SphereShape::raycast(const Ray& ray, ProxyShape* proxyShape) const {
// TODO : Normalize the ray direction const Transform localToWorldTransform = proxyShape->getLocalToWorldTransform();
const Transform worldToLocalTransform = localToWorldTransform.getInverse();
Vector3 origin = worldToLocalTransform * ray.origin;
decimal c = origin.dot(origin) - mRadius * mRadius;
// If the origin of the ray is inside the sphere, we return no intersection
if (c < decimal(0.0)) return false;
// TODO : Implement this method Vector3 rayDirection = worldToLocalTransform.getOrientation() * ray.direction.getUnit();
return false; decimal b = origin.dot(rayDirection);
// If the origin of the ray is outside the sphere and the ray
// is pointing away from the sphere and there is no intersection
if (c >= decimal(0.0) && b > decimal(0.0)) return false;
// Compute the discriminant of the quadratic equation
decimal discriminant = b*b - c;
// If the discriminant is negative, there is no intersection
if (discriminant < decimal(0.0)) return false;
// There is an intersection
return true;
} }
// Raycast method with feedback information // Raycast method with feedback information
bool SphereShape::raycast(const Ray& ray, RaycastInfo& raycastInfo, ProxyShape* proxyShape, bool SphereShape::raycast(const Ray& ray, RaycastInfo& raycastInfo, ProxyShape* proxyShape,
decimal distance) const { decimal distance) const {
// TODO : Normalize the ray direction const Transform localToWorldTransform = proxyShape->getLocalToWorldTransform();
const Transform worldToLocalTransform = localToWorldTransform.getInverse();
Vector3 origin = worldToLocalTransform * ray.origin;
decimal c = origin.dot(origin) - mRadius * mRadius;
// TODO : Implement this method // If the origin of the ray is inside the sphere, we return no intersection
return false; if (c < decimal(0.0)) return false;
Vector3 rayDirection = worldToLocalTransform.getOrientation() * ray.direction.getUnit();
decimal b = origin.dot(rayDirection);
// If the origin of the ray is outside the sphere and the ray
// is pointing away from the sphere and there is no intersection
if (c >= decimal(0.0) && b > decimal(0.0)) return false;
// Compute the discriminant of the quadratic equation
decimal discriminant = b*b - c;
// If the discriminant is negative, there is no intersection
if (discriminant < decimal(0.0)) return false;
// Compute the solution "t" closest to the origin
decimal t = -b - std::sqrt(discriminant);
assert(t >= decimal(0.0));
// If the intersection distance is larger than the allowed distance, return no intersection
if (t > distance) return false;
// Compute the intersection information
Vector3 localPoint = origin + t * rayDirection;
raycastInfo.body = proxyShape->getBody();
raycastInfo.proxyShape = proxyShape;
raycastInfo.distance = t;
raycastInfo.worldPoint = localToWorldTransform * localPoint;
raycastInfo.worldNormal = (raycastInfo.worldPoint -
localToWorldTransform.getPosition()).getUnit();
return true;
} }

View File

@ -71,8 +71,7 @@ class SphereShape : public CollisionShape {
virtual bool testPointInside(const Vector3& localPoint, ProxyShape* proxyShape) const; virtual bool testPointInside(const Vector3& localPoint, ProxyShape* proxyShape) const;
/// Raycast method /// Raycast method
virtual bool raycast(const Ray& ray, ProxyShape* proxyShape, virtual bool raycast(const Ray& ray, ProxyShape* proxyShape) const;
decimal distance = RAYCAST_INFINITY_DISTANCE) const;
/// Raycast method with feedback information /// Raycast method with feedback information
virtual bool raycast(const Ray& ray, RaycastInfo& raycastInfo, ProxyShape* proxyShape, virtual bool raycast(const Ray& ray, RaycastInfo& raycastInfo, ProxyShape* proxyShape,

View File

@ -361,30 +361,30 @@ class TestRaycast : public Test {
test(mWorld->raycast(ray, raycastInfo)); test(mWorld->raycast(ray, raycastInfo));
test(raycastInfo.body == mSphereBody); test(raycastInfo.body == mSphereBody);
test(raycastInfo.proxyShape == mSphereShape); test(raycastInfo.proxyShape == mSphereShape);
test(approxEqual(raycastInfo.distance, 6)); test(approxEqual(raycastInfo.distance, 5, epsilon));
test(approxEqual(raycastInfo.worldPoint.x, hitPoint.x)); test(approxEqual(raycastInfo.worldPoint.x, hitPoint.x, epsilon));
test(approxEqual(raycastInfo.worldPoint.y, hitPoint.y)); test(approxEqual(raycastInfo.worldPoint.y, hitPoint.y, epsilon));
test(approxEqual(raycastInfo.worldPoint.z, hitPoint.z)); test(approxEqual(raycastInfo.worldPoint.z, hitPoint.z, epsilon));
// CollisionBody::raycast() // CollisionBody::raycast()
RaycastInfo raycastInfo2; RaycastInfo raycastInfo2;
test(mSphereBody->raycast(ray, raycastInfo2)); test(mSphereBody->raycast(ray, raycastInfo2));
test(raycastInfo2.body == mSphereBody); test(raycastInfo2.body == mSphereBody);
test(raycastInfo2.proxyShape == mSphereShape); test(raycastInfo2.proxyShape == mSphereShape);
test(approxEqual(raycastInfo2.distance, 6)); test(approxEqual(raycastInfo2.distance, 5, epsilon));
test(approxEqual(raycastInfo2.worldPoint.x, hitPoint.x)); test(approxEqual(raycastInfo2.worldPoint.x, hitPoint.x, epsilon));
test(approxEqual(raycastInfo2.worldPoint.y, hitPoint.y)); test(approxEqual(raycastInfo2.worldPoint.y, hitPoint.y, epsilon));
test(approxEqual(raycastInfo2.worldPoint.z, hitPoint.z)); test(approxEqual(raycastInfo2.worldPoint.z, hitPoint.z, epsilon));
// ProxyCollisionShape::raycast() // ProxyCollisionShape::raycast()
RaycastInfo raycastInfo3; RaycastInfo raycastInfo3;
test(mSphereShape->raycast(ray, raycastInfo3)); test(mSphereShape->raycast(ray, raycastInfo3));
test(raycastInfo3.body == mSphereBody); test(raycastInfo3.body == mSphereBody);
test(raycastInfo3.proxyShape == mSphereShape); test(raycastInfo3.proxyShape == mSphereShape);
test(approxEqual(raycastInfo3.distance, 6)); test(approxEqual(raycastInfo3.distance, 5, epsilon));
test(approxEqual(raycastInfo3.worldPoint.x, hitPoint.x)); test(approxEqual(raycastInfo3.worldPoint.x, hitPoint.x, epsilon));
test(approxEqual(raycastInfo3.worldPoint.y, hitPoint.y)); test(approxEqual(raycastInfo3.worldPoint.y, hitPoint.y, epsilon));
test(approxEqual(raycastInfo3.worldPoint.z, hitPoint.z)); test(approxEqual(raycastInfo3.worldPoint.z, hitPoint.z, epsilon));
Ray ray1(mLocalShapeToWorld * Vector3(0, 0, 0), mLocalToWorldMatrix * Vector3(5, 7, -1)); Ray ray1(mLocalShapeToWorld * Vector3(0, 0, 0), mLocalToWorldMatrix * Vector3(5, 7, -1));
Ray ray2(mLocalShapeToWorld * Vector3(5, 11, 7), mLocalToWorldMatrix * Vector3(4, 6, 7)); Ray ray2(mLocalShapeToWorld * Vector3(5, 11, 7), mLocalToWorldMatrix * Vector3(4, 6, 7));