Implemented a custom physics engine #316

Merged
direnbharwani merged 95 commits from SHPhysics into main 2023-01-23 15:55:45 +08:00
12 changed files with 320 additions and 174 deletions
Showing only changes of commit 6f55f202b9 - Show all commits

View File

@ -66,7 +66,7 @@
NumberOfChildren: 0 NumberOfChildren: 0
Components: Components:
Transform Component: Transform Component:
Translate: {x: -1.96328545, y: 7, z: 0.743723333} Translate: {x: -1.45715916, y: 7, z: 0.319963396}
Rotate: {x: -0, y: 0, z: -0} Rotate: {x: -0, y: 0, z: -0}
Scale: {x: 1, y: 1, z: 1} Scale: {x: 1, y: 1, z: 1}
IsActive: true IsActive: true

View File

@ -26,14 +26,12 @@ namespace SHADE
SHAABB::SHAABB() noexcept SHAABB::SHAABB() noexcept
{ {
type = Type::AABB; Extents = SHVec3::One * 0.5f;
} }
SHAABB::SHAABB(const SHVec3& c, const SHVec3& hE) noexcept SHAABB::SHAABB(const SHVec3& c, const SHVec3& hE) noexcept
{ {
type = Type::AABB; Center = c;
Center = c;
Extents = hE; Extents = hE;
} }
@ -43,18 +41,14 @@ namespace SHADE
if (this == &rhs) if (this == &rhs)
return; return;
type = Type::AABB; Center = rhs.Center;
Extents = rhs.Extents;
Center = rhs.Center;
Extents = rhs.Extents;
} }
SHAABB::SHAABB(SHAABB&& rhs) noexcept SHAABB::SHAABB(SHAABB&& rhs) noexcept
{ {
type = Type::AABB; Center = rhs.Center;
Extents = rhs.Extents;
Center = rhs.Center;
Extents = rhs.Extents;
} }
/*-----------------------------------------------------------------------------------*/ /*-----------------------------------------------------------------------------------*/
@ -63,14 +57,10 @@ namespace SHADE
SHAABB& SHAABB::operator=(const SHAABB& rhs) noexcept SHAABB& SHAABB::operator=(const SHAABB& rhs) noexcept
{ {
if (rhs.type != Type::AABB) if (this != &rhs)
{ {
SHLOG_WARNING("Cannot assign a non-bounding box to a bounding box!") Center = rhs.Center;
} Extents = rhs.Extents;
else if (this != &rhs)
{
Center = rhs.Center;
Extents = rhs.Extents;
} }
return *this; return *this;
@ -78,15 +68,8 @@ namespace SHADE
SHAABB& SHAABB::operator=(SHAABB&& rhs) noexcept SHAABB& SHAABB::operator=(SHAABB&& rhs) noexcept
{ {
if (rhs.type != Type::AABB) Center = rhs.Center;
{ Extents = rhs.Extents;
SHLOG_WARNING("Cannot assign a non-bounding box to a bounding box!")
}
else
{
Center = rhs.Center;
Extents = rhs.Extents;
}
return *this; return *this;
} }

View File

@ -14,7 +14,6 @@
// Project Headers // Project Headers
#include "SHShape.h" #include "SHShape.h"
#include "SH_API.h"
namespace SHADE namespace SHADE
{ {
@ -22,7 +21,11 @@ namespace SHADE
/* Type Definitions */ /* Type Definitions */
/*-----------------------------------------------------------------------------------*/ /*-----------------------------------------------------------------------------------*/
class SH_API SHAABB : public SHShape, /**
* @brief
* Encapsulates a 3D Axis-Aligned Bounding Box.
*/
class SH_API SHAABB : public SHShape,
private DirectX::BoundingBox private DirectX::BoundingBox
{ {
public: public:
@ -71,7 +74,7 @@ namespace SHADE
void SetMinMax (const SHVec3& min, const SHVec3& max) noexcept; void SetMinMax (const SHVec3& min, const SHVec3& max) noexcept;
/*---------------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------------*/
/* Function Members */ /* Member Functions */
/*---------------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------------*/
/** /**
@ -118,7 +121,7 @@ namespace SHADE
[[nodiscard]] float SurfaceArea () const noexcept; [[nodiscard]] float SurfaceArea () const noexcept;
/*---------------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------------*/
/* Static Function Members */ /* Static Member Functions */
/*---------------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------------*/
/** /**

View File

@ -27,13 +27,11 @@ namespace SHADE
SHBox::SHBox() noexcept SHBox::SHBox() noexcept
{ {
type = Type::BOX; Extents = SHVec3::One * 0.5f;
} }
SHBox::SHBox(const SHVec3& c, const SHVec3& hE, const SHQuaternion& o) noexcept SHBox::SHBox(const SHVec3& c, const SHVec3& hE, const SHQuaternion& o) noexcept
{ {
type = Type::BOX;
Center = c; Center = c;
Extents = hE; Extents = hE;
Orientation = o; Orientation = o;
@ -45,8 +43,6 @@ namespace SHADE
if (this == &rhs) if (this == &rhs)
return; return;
type = Type::BOX;
Center = rhs.Center; Center = rhs.Center;
Extents = rhs.Extents; Extents = rhs.Extents;
Orientation = rhs.Orientation; Orientation = rhs.Orientation;
@ -54,8 +50,6 @@ namespace SHADE
SHBox::SHBox(SHBox&& rhs) noexcept SHBox::SHBox(SHBox&& rhs) noexcept
{ {
type = Type::BOX;
Center = rhs.Center; Center = rhs.Center;
Extents = rhs.Extents; Extents = rhs.Extents;
Orientation = rhs.Orientation; Orientation = rhs.Orientation;
@ -67,11 +61,7 @@ namespace SHADE
SHBox& SHBox::operator=(const SHBox& rhs) noexcept SHBox& SHBox::operator=(const SHBox& rhs) noexcept
{ {
if (rhs.type != Type::BOX) if (this != &rhs)
{
SHLOG_WARNING("Cannot assign a non-bounding box to a bounding box!")
}
else if (this != &rhs)
{ {
Center = rhs.Center; Center = rhs.Center;
Extents = rhs.Extents; Extents = rhs.Extents;
@ -83,16 +73,9 @@ namespace SHADE
SHBox& SHBox::operator=(SHBox&& rhs) noexcept SHBox& SHBox::operator=(SHBox&& rhs) noexcept
{ {
if (rhs.type != Type::BOX) Center = rhs.Center;
{ Extents = rhs.Extents;
SHLOG_WARNING("Cannot assign a non-bounding box to a bounding box!") Orientation = rhs.Orientation;
}
else
{
Center = rhs.Center;
Extents = rhs.Extents;
Orientation = rhs.Orientation;
}
return *this; return *this;
} }

View File

@ -14,7 +14,6 @@
// Project Headers // Project Headers
#include "SHShape.h" #include "SHShape.h"
#include "SH_API.h"
namespace SHADE namespace SHADE
{ {
@ -22,6 +21,10 @@ namespace SHADE
/* Type Definitions */ /* Type Definitions */
/*-----------------------------------------------------------------------------------*/ /*-----------------------------------------------------------------------------------*/
/**
* @brief
* Encapsulates a 3D-oriented bounding box.
*/
class SH_API SHBox : public SHShape, class SH_API SHBox : public SHShape,
public DirectX::BoundingOrientedBox public DirectX::BoundingOrientedBox
{ {
@ -57,7 +60,7 @@ namespace SHADE
[[nodiscard]] std::vector<SHVec3> GetVertices () const noexcept; [[nodiscard]] std::vector<SHVec3> GetVertices () const noexcept;
/*---------------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------------*/
/* Function Members */ /* Member Functions */
/*---------------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------------*/
/** /**
@ -104,7 +107,7 @@ namespace SHADE
[[nodiscard]] float SurfaceArea () const noexcept; [[nodiscard]] float SurfaceArea () const noexcept;
/*---------------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------------*/
/* Static Function Members */ /* Static Member Functions */
/*---------------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------------*/
/** /**

View File

@ -0,0 +1,141 @@
/****************************************************************************************
* \file SHPlane.cpp
* \author Diren D Bharwani, diren.dbharwani, 390002520
* \brief Implementation for a plane.
*
* \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or
* disclosure of this file or its contents without the prior written consent
* of DigiPen Institute of Technology is prohibited.
****************************************************************************************/
#include <SHpch.h>
#include <DirectXMath.h>
// Primary Header
#include "SHPlane.h"
#include "Input/SHInputManager.h"
#include "Math/SHMathHelpers.h"
using namespace DirectX;
namespace SHADE
{
/*-----------------------------------------------------------------------------------*/
/* Constructors & Destructor Definitions */
/*-----------------------------------------------------------------------------------*/
SHPlane::SHPlane() noexcept
: planeEquation { SHVec3::One }
{
planeEquation.w = 0.0f;
}
SHPlane::SHPlane(const SHVec3& point, const SHVec3& normal) noexcept
{
XMStoreFloat4(&planeEquation, XMPlaneFromPointNormal(point, normal));
}
SHPlane::SHPlane(float a, float b, float c, float d) noexcept
: planeEquation { a, b, c, d }
{}
SHPlane::SHPlane(const SHVec3& normal, float distance) noexcept
: planeEquation { normal }
{
planeEquation.w = distance;
}
/*-----------------------------------------------------------------------------------*/
/* Operator Overload Definitions */
/*-----------------------------------------------------------------------------------*/
bool SHPlane::operator==(const SHPlane& rhs) const noexcept
{
return XMPlaneEqual(planeEquation, rhs.planeEquation);
}
bool SHPlane::operator!=(const SHPlane& rhs) const noexcept
{
return XMPlaneNotEqual(planeEquation, rhs.planeEquation);
}
/*-----------------------------------------------------------------------------------*/
/* Getter Function Definitions */
/*-----------------------------------------------------------------------------------*/
SHVec3 SHPlane::GetNormal() const noexcept
{
return SHVec3{ planeEquation.x, planeEquation.y, planeEquation.z };
}
float SHPlane::GetDistance() const noexcept
{
return planeEquation.w;
}
/*-----------------------------------------------------------------------------------*/
/* Setter Function Definitions */
/*-----------------------------------------------------------------------------------*/
void SHPlane::SetNormal(const SHVec3& normal) noexcept
{
planeEquation.x = normal.x;
planeEquation.y = normal.y;
planeEquation.z = normal.z;
}
void SHPlane::SetDistance(float distance) noexcept
{
planeEquation.w = distance;
}
/*-----------------------------------------------------------------------------------*/
/* Public Function Member Definitions */
/*-----------------------------------------------------------------------------------*/
bool SHPlane::TestPoint(const SHVec3& point) const noexcept
{
const float DISTANCE = SignedDistance(point);
return SHMath::CompareFloat(DISTANCE, 0.0f);
}
SHRaycastResult SHPlane::Raycast(const SHRay& ray) const noexcept
{
SHRaycastResult result;
const SHVec3 N = GetNormal();
// Check if ray is parallel to plane
const float N_DOT_D = N.Dot(ray.direction);
if (SHMath::CompareFloat(N_DOT_D, 0.0f))
{
result.hit = false;
return result;
}
const float DIST = (planeEquation.w - N.Dot(ray.position)) / N_DOT_D;
if (DIST < 0.0f || !SHMath::CompareFloat(DIST, 0.0f))
{
result.hit = false;
return result;
}
result.hit = true;
result.distance = DIST;
result.position = ray.position + ray.direction * DIST;
result.angle = SHVec3::Angle(ray.position, result.position);
// The normal is either the plane's normal or the reverse if the ray came from below
result.normal = N_DOT_D < 0.0f ? -N : N;
return result;
}
float SHPlane::SignedDistance(const SHVec3& point) const noexcept
{
return XMVectorGetX(XMPlaneDotCoord(planeEquation, point));
}
} // namespace SHADE

View File

@ -0,0 +1,121 @@
/****************************************************************************************
* \file SHPlane.h
* \author Diren D Bharwani, diren.dbharwani, 390002520
* \brief Interface for a plane.
*
* \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or
* disclosure of this file or its contents without the prior written consent
* of DigiPen Institute of Technology is prohibited.
****************************************************************************************/
#pragma once
// Project Headers
#include "SHShape.h"
#include "Math/Vector/SHVec4.h"
namespace SHADE
{
/*-----------------------------------------------------------------------------------*/
/* Type Definitions */
/*-----------------------------------------------------------------------------------*/
/**
* @brief
* Encapsulates a 3D plane in point-normal form.
*/
class SH_API SHPlane : public SHShape
{
public:
/*---------------------------------------------------------------------------------*/
/* Constructors & Destructor */
/*---------------------------------------------------------------------------------*/
~SHPlane () override = default;
SHPlane (const SHPlane& rhs) noexcept = default;
SHPlane (SHPlane&& rhs) noexcept = default;
SHPlane () noexcept;
SHPlane (const SHVec3& point, const SHVec3& normal) noexcept;
SHPlane (float a, float b, float c, float d) noexcept;
SHPlane (const SHVec3& normal, float distance) noexcept;
/*---------------------------------------------------------------------------------*/
/* Operator Overloads */
/*---------------------------------------------------------------------------------*/
SHPlane& operator= (const SHPlane& rhs) noexcept = default;
SHPlane& operator= (SHPlane&& rhs) noexcept = default;
bool operator== (const SHPlane& rhs) const noexcept;
bool operator!= (const SHPlane& rhs) const noexcept;
/*---------------------------------------------------------------------------------*/
/* Getter Functions */
/*---------------------------------------------------------------------------------*/
[[nodiscard]] SHVec3 GetNormal () const noexcept;
[[nodiscard]] float GetDistance () const noexcept;
/*---------------------------------------------------------------------------------*/
/* Setter Functions */
/*---------------------------------------------------------------------------------*/
void SetNormal (const SHVec3& normal) noexcept;
void SetDistance (float distance) noexcept;
/*---------------------------------------------------------------------------------*/
/* Member Functions */
/*---------------------------------------------------------------------------------*/
/**
* @brief
* Checks if a point is on the plane.
* @param point
* The point to check.
* @return
* True if the point is on the plane.
*/
[[nodiscard]] bool TestPoint (const SHVec3& point) const noexcept override;
/**
* @brief
* Casts a ray against the plane.
* @param ray
* The ray to cast.
* @return
* The result of the raycast. <br/>
* See the corresponding header for the contents of the raycast result object.
*/
[[nodiscard]] SHRaycastResult Raycast (const SHRay& ray) const noexcept override;
/**
* @brief
* Gets the signed distance from a point to the plane.
* @param point
* The point to check.
* @return
* The signed distance of the point to a plane. <br/>
* If the signed distance is negative, the point is behind the plane. <br/>
* If the signed distance is zero, the point is on the plane. <br/>
* If the signed distance is positive, the point is in front of the plane. <br/>
*/
[[nodiscard]] float SignedDistance (const SHVec3& point) const noexcept;
/*---------------------------------------------------------------------------------*/
/* Static Member Functions */
/*---------------------------------------------------------------------------------*/
/*
* TODO:
* Transform plane
* Intersection Tests
*/
private:
SHVec4 planeEquation;
};
} // namespace SHADE

View File

@ -1,35 +0,0 @@
/****************************************************************************************
* \file SHShape.cpp
* \author Diren D Bharwani, diren.dbharwani, 390002520
* \brief Implementation for a shape.
*
* \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or
* disclosure of this file or its contents without the prior written consent
* of DigiPen Institute of Technology is prohibited.
****************************************************************************************/
#include <SHpch.h>
// Primary Header
#include "SHShape.h"
namespace SHADE
{
/*-----------------------------------------------------------------------------------*/
/* Constructors & Destructor Definitions */
/*-----------------------------------------------------------------------------------*/
SHShape::SHShape()
: type { Type::NONE }
{}
/*-----------------------------------------------------------------------------------*/
/* Getter Function Definitions */
/*-----------------------------------------------------------------------------------*/
SHShape::Type SHShape::GetType() const noexcept
{
return type;
}
} // namespace SHADE

View File

@ -11,58 +11,26 @@
#pragma once #pragma once
// Project Headers // Project Headers
#include "SH_API.h"
#include "Math/SHRay.h" #include "Math/SHRay.h"
namespace SHADE namespace SHADE
{ {
/*-----------------------------------------------------------------------------------*/ /*-----------------------------------------------------------------------------------*/
/* Type Definitions */ /* Type Definitions */
/*-----------------------------------------------------------------------------------*/ /*-----------------------------------------------------------------------------------*/
/**
* @brief
* Encapsulates a base class for any shape.
*/
class SH_API SHShape class SH_API SHShape
{ {
public: public:
/*---------------------------------------------------------------------------------*/
/* Type Definitions */
/*---------------------------------------------------------------------------------*/
enum class Type
{
SPHERE
, AABB
, BOX
, COUNT
, NONE = -1
};
/*---------------------------------------------------------------------------------*/
/* Data Members */
/*---------------------------------------------------------------------------------*/
bool isIntersecting;
/*---------------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------------*/
/* Constructors & Destructor */ /* Constructors & Destructor */
/*---------------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------------*/
virtual ~SHShape () = default; virtual ~SHShape () = default;
SHShape (const SHShape&) = default;
SHShape (SHShape&&) = default;
SHShape& operator=(const SHShape&) = default;
SHShape& operator=(SHShape&&) = default;
SHShape();
/*---------------------------------------------------------------------------------*/
/* Getter Functions */
/*---------------------------------------------------------------------------------*/
[[nodiscard]] Type GetType () const noexcept;
/*---------------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------------*/
/* Function Members */ /* Function Members */
@ -70,12 +38,5 @@ namespace SHADE
[[nodiscard]] virtual bool TestPoint (const SHVec3& point) const noexcept = 0; [[nodiscard]] virtual bool TestPoint (const SHVec3& point) const noexcept = 0;
[[nodiscard]] virtual SHRaycastResult Raycast (const SHRay& ray) const noexcept = 0; [[nodiscard]] virtual SHRaycastResult Raycast (const SHRay& ray) const noexcept = 0;
protected:
/*---------------------------------------------------------------------------------*/
/* Data Members */
/*---------------------------------------------------------------------------------*/
Type type;
}; };
} // namespace SHADE } // namespace SHADE

View File

@ -1,5 +1,5 @@
/**************************************************************************************** /****************************************************************************************
* \file SHBoundingSphere.cpp * \file SHSphere.cpp
* \author Diren D Bharwani, diren.dbharwani, 390002520 * \author Diren D Bharwani, diren.dbharwani, 390002520
* \brief Implementation for a Bounding Sphere * \brief Implementation for a Bounding Sphere
* *
@ -26,13 +26,11 @@ namespace SHADE
SHSphere::SHSphere() noexcept SHSphere::SHSphere() noexcept
{ {
type = Type::SPHERE; Radius = 0.5f;
} }
SHSphere::SHSphere(const SHVec3& center, float radius) noexcept SHSphere::SHSphere(const SHVec3& center, float radius) noexcept
{ {
type = Type::SPHERE;
Center = center; Center = center;
Radius = radius; Radius = radius;
} }
@ -42,18 +40,14 @@ namespace SHADE
if (this == &rhs) if (this == &rhs)
return; return;
type = Type::SPHERE; Center = rhs.Center;
Radius = rhs.Radius;
Center = rhs.Center;
Radius = rhs.Radius;
} }
SHSphere::SHSphere(SHSphere&& rhs) noexcept SHSphere::SHSphere(SHSphere&& rhs) noexcept
{ {
type = Type::SPHERE; Center = rhs.Center;
Radius = rhs.Radius;
Center = rhs.Center;
Radius = rhs.Radius;
} }
/*-----------------------------------------------------------------------------------*/ /*-----------------------------------------------------------------------------------*/
@ -62,14 +56,10 @@ namespace SHADE
SHSphere& SHSphere::operator=(const SHSphere& rhs) noexcept SHSphere& SHSphere::operator=(const SHSphere& rhs) noexcept
{ {
if (rhs.type != Type::SPHERE) if (this != &rhs)
{ {
SHLOG_WARNING("Cannot assign a non-sphere to a sphere!") Center = rhs.Center;
} Radius = rhs.Radius;
else if (this != &rhs)
{
Center = rhs.Center;
Radius = rhs.Radius;
} }
return *this; return *this;
@ -77,15 +67,9 @@ namespace SHADE
SHSphere& SHSphere::operator=(SHSphere&& rhs) noexcept SHSphere& SHSphere::operator=(SHSphere&& rhs) noexcept
{ {
if (rhs.type != Type::SPHERE)
{ Center = rhs.Center;
SHLOG_WARNING("Cannot assign a non-sphere to a sphere!") Radius = rhs.Radius;
}
else
{
Center = rhs.Center;
Radius = rhs.Radius;
}
return *this; return *this;
} }

View File

@ -1,5 +1,5 @@
/**************************************************************************************** /****************************************************************************************
* \file SHBoundingSphere.h * \file SHSphere.h
* \author Diren D Bharwani, diren.dbharwani, 390002520 * \author Diren D Bharwani, diren.dbharwani, 390002520
* \brief Interface for a Bounding Sphere. * \brief Interface for a Bounding Sphere.
* *
@ -14,7 +14,6 @@
// Project Headers // Project Headers
#include "SHShape.h" #include "SHShape.h"
#include "SH_API.h"
namespace SHADE namespace SHADE
{ {
@ -22,6 +21,10 @@ namespace SHADE
/* Type Definitions */ /* Type Definitions */
/*-----------------------------------------------------------------------------------*/ /*-----------------------------------------------------------------------------------*/
/**
* @brief
* Encapsulates a 3D Sphere.
*/
class SH_API SHSphere : public SHShape, class SH_API SHSphere : public SHShape,
public DirectX::BoundingSphere public DirectX::BoundingSphere
{ {
@ -45,7 +48,7 @@ namespace SHADE
SHSphere& operator= (SHSphere&& rhs) noexcept; SHSphere& operator= (SHSphere&& rhs) noexcept;
/*---------------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------------*/
/* Function Members */ /* Member Functions */
/*---------------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------------*/
/** /**
@ -93,7 +96,7 @@ namespace SHADE
/*---------------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------------*/
/* Static Function Members */ /* Static Member Functions */
/*---------------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------------*/
/** /**

View File

@ -15,6 +15,7 @@
#include "SHCollision.h" #include "SHCollision.h"
// Project Headers // Project Headers
#include "Math/Geometry/SHPlane.h"
#include "Math/SHMathHelpers.h" #include "Math/SHMathHelpers.h"
#include "Physics/Collision/CollisionShapes/SHCollisionShape.h" #include "Physics/Collision/CollisionShapes/SHCollisionShape.h"
#include "Physics/Collision/CollisionShapes/SHBoxCollisionShape.h" #include "Physics/Collision/CollisionShapes/SHBoxCollisionShape.h"
@ -203,13 +204,11 @@ namespace SHADE
const SHHalfEdgeStructure::Face& FACE = polyhedron.GetFace(i); const SHHalfEdgeStructure::Face& FACE = polyhedron.GetFace(i);
// Build plane equation // Build plane equation
// Use first vertex to build the plane
const SHVec3 POINT = polyhedron.GetVertex(FACE.vertexIndices[0].index); // use the first vertex to build a plane const SHPlane FACE_PLANE { polyhedron.GetVertex(FACE.vertexIndices[0].index), polyhedron.GetNormal(i) };
const SHVec3& NORMAL = polyhedron.GetNormal(i);
const float D = -SHVec3::Dot(NORMAL, POINT);
// Find signed distance of center to plane // Find signed distance of center to plane
const float SIGNED_DIST = SHVec3::Dot(NORMAL, CENTER) + D; const float SIGNED_DIST = FACE_PLANE.SignedDistance(CENTER);
// Early out: // Early out:
// If face is facing away from center, signed dist is negative. // If face is facing away from center, signed dist is negative.