Files
2021-06-02 00:33:08 +02:00

403 lines
10 KiB
C++

#include "wiIntersect.h"
#include "wiMath.h"
void AABB::createFromHalfWidth(const XMFLOAT3& center, const XMFLOAT3& halfwidth)
{
_min = XMFLOAT3(center.x - halfwidth.x, center.y - halfwidth.y, center.z - halfwidth.z);
_max = XMFLOAT3(center.x + halfwidth.x, center.y + halfwidth.y, center.z + halfwidth.z);
}
AABB AABB::transform(const XMMATRIX& mat) const
{
XMFLOAT3 corners[8];
for (int i = 0; i < 8; ++i)
{
XMFLOAT3 C = corner(i);
XMVECTOR point = XMVector3Transform(XMLoadFloat3(&C), mat);
XMStoreFloat3(&corners[i], point);
}
XMFLOAT3 min = corners[0];
XMFLOAT3 max = corners[6];
for (int i = 0; i < 8; ++i)
{
const XMFLOAT3& p = corners[i];
if (p.x < min.x) min.x = p.x;
if (p.y < min.y) min.y = p.y;
if (p.z < min.z) min.z = p.z;
if (p.x > max.x) max.x = p.x;
if (p.y > max.y) max.y = p.y;
if (p.z > max.z) max.z = p.z;
}
return AABB(min, max);
}
AABB AABB::transform(const XMFLOAT4X4& mat) const
{
return transform(XMLoadFloat4x4(&mat));
}
XMFLOAT3 AABB::getCenter() const
{
return XMFLOAT3((_min.x + _max.x)*0.5f, (_min.y + _max.y)*0.5f, (_min.z + _max.z)*0.5f);
}
XMFLOAT3 AABB::getHalfWidth() const
{
XMFLOAT3 center = getCenter();
return XMFLOAT3(abs(_max.x - center.x), abs(_max.y - center.y), abs(_max.z - center.z));
}
XMMATRIX AABB::AABB::getAsBoxMatrix() const
{
XMFLOAT3 ext = getHalfWidth();
XMMATRIX sca = XMMatrixScaling(ext.x, ext.y, ext.z);
XMFLOAT3 pos = getCenter();
XMMATRIX tra = XMMatrixTranslation(pos.x, pos.y, pos.z);
return sca*tra;
}
float AABB::getArea() const
{
XMFLOAT3 _min = getMin();
XMFLOAT3 _max = getMax();
return (_max.x - _min.x)*(_max.y - _min.y)*(_max.z - _min.z);
}
float AABB::getRadius() const {
XMFLOAT3 abc = getHalfWidth();
return std::max(std::max(abc.x, abc.y), abc.z);
}
AABB::INTERSECTION_TYPE AABB::intersects(const AABB& b) const {
XMFLOAT3 aMin = getMin(), aMax = getMax();
XMFLOAT3 bMin = b.getMin(), bMax = b.getMax();
if (bMin.x >= aMin.x && bMax.x <= aMax.x &&
bMin.y >= aMin.y && bMax.y <= aMax.y &&
bMin.z >= aMin.z && bMax.z <= aMax.z)
{
return INSIDE;
}
if (aMax.x < bMin.x || aMin.x > bMax.x)
return OUTSIDE;
if (aMax.y < bMin.y || aMin.y > bMax.y)
return OUTSIDE;
if (aMax.z < bMin.z || aMin.z > bMax.z)
return OUTSIDE;
return INTERSECTS;
}
AABB::INTERSECTION_TYPE AABB::intersects2D(const AABB& b) const {
XMFLOAT3 aMin = getMin(), aMax = getMax();
XMFLOAT3 bMin = b.getMin(), bMax = b.getMax();
if (bMin.x >= aMin.x && bMax.x <= aMax.x &&
bMin.y >= aMin.y && bMax.y <= aMax.y)
{
return INSIDE;
}
if (aMax.x < bMin.x || aMin.x > bMax.x)
return OUTSIDE;
if (aMax.y < bMin.y || aMin.y > bMax.y)
return OUTSIDE;
return INTERSECTS;
}
bool AABB::intersects(const XMFLOAT3& p) const {
XMFLOAT3 max = getMax();
XMFLOAT3 min = getMin();
if (p.x>max.x) return false;
if (p.x<min.x) return false;
if (p.y>max.y) return false;
if (p.y<min.y) return false;
if (p.z>max.z) return false;
if (p.z<min.z) return false;
return true;
}
bool AABB::intersects(const RAY& ray) const {
if (intersects(ray.origin))
return true;
XMFLOAT3 MIN = getMin();
XMFLOAT3 MAX = getMax();
float tx1 = (MIN.x - ray.origin.x)*ray.direction_inverse.x;
float tx2 = (MAX.x - ray.origin.x)*ray.direction_inverse.x;
float tmin = std::min(tx1, tx2);
float tmax = std::max(tx1, tx2);
float ty1 = (MIN.y - ray.origin.y)*ray.direction_inverse.y;
float ty2 = (MAX.y - ray.origin.y)*ray.direction_inverse.y;
tmin = std::max(tmin, std::min(ty1, ty2));
tmax = std::min(tmax, std::max(ty1, ty2));
float tz1 = (MIN.z - ray.origin.z)*ray.direction_inverse.z;
float tz2 = (MAX.z - ray.origin.z)*ray.direction_inverse.z;
tmin = std::max(tmin, std::min(tz1, tz2));
tmax = std::min(tmax, std::max(tz1, tz2));
return tmax >= tmin;
}
bool AABB::intersects(const SPHERE& sphere) const
{
return sphere.intersects(*this);
}
bool AABB::intersects(const BoundingFrustum& frustum) const
{
BoundingBox bb = BoundingBox(getCenter(), getHalfWidth());
bool intersection = frustum.Intersects(bb);
return intersection;
}
AABB AABB::operator* (float a)
{
XMFLOAT3 min = getMin();
XMFLOAT3 max = getMax();
min.x *= a;
min.y *= a;
min.z *= a;
max.x *= a;
max.y *= a;
max.z *= a;
return AABB(min, max);
}
AABB AABB::Merge(const AABB& a, const AABB& b)
{
return AABB(wiMath::Min(a.getMin(), b.getMin()), wiMath::Max(a.getMax(), b.getMax()));
}
void AABB::Serialize(wiArchive& archive, wiECS::EntitySerializer& seri)
{
if (archive.IsReadMode())
{
archive >> _min;
archive >> _max;
if (archive.GetVersion() >= 69)
{
archive >> layerMask;
archive >> userdata;
}
}
else
{
archive << _min;
archive << _max;
if (archive.GetVersion() >= 69)
{
archive << layerMask;
archive << userdata;
}
}
}
bool SPHERE::intersects(const AABB& b) const {
XMFLOAT3 min = b.getMin();
XMFLOAT3 max = b.getMax();
XMFLOAT3 closestPointInAabb = wiMath::Min(wiMath::Max(center, min), max);
double distanceSquared = wiMath::Distance(closestPointInAabb, center);
return distanceSquared < radius;
}
bool SPHERE::intersects(const SPHERE& b)const {
return wiMath::Distance(center, b.center) <= radius + b.radius;
}
bool SPHERE::intersects(const RAY& b) const {
XMVECTOR o = XMLoadFloat3(&b.origin);
XMVECTOR r = XMLoadFloat3(&b.direction);
XMVECTOR dist = XMVector3LinePointDistance(o, o + r, XMLoadFloat3(&center));
return XMVectorGetX(dist) <= radius;
}
bool CAPSULE::intersects(const CAPSULE& other, XMFLOAT3& position, XMFLOAT3& incident_normal, float& penetration_depth) const
{
if(getAABB().intersects(other.getAABB()) == AABB::INTERSECTION_TYPE::OUTSIDE)
return false;
XMVECTOR a_Base = XMLoadFloat3(&base);
XMVECTOR a_Tip = XMLoadFloat3(&tip);
XMVECTOR a_Radius = XMVectorReplicate(radius);
XMVECTOR a_Normal = XMVector3Normalize(a_Tip - a_Base);
XMVECTOR a_LineEndOffset = a_Normal * a_Radius;
XMVECTOR a_A = a_Base + a_LineEndOffset;
XMVECTOR a_B = a_Tip - a_LineEndOffset;
XMVECTOR b_Base = XMLoadFloat3(&other.base);
XMVECTOR b_Tip = XMLoadFloat3(&other.tip);
XMVECTOR b_Radius = XMVectorReplicate(other.radius);
XMVECTOR b_Normal = XMVector3Normalize(b_Tip - b_Base);
XMVECTOR b_LineEndOffset = b_Normal * b_Radius;
XMVECTOR b_A = b_Base + b_LineEndOffset;
XMVECTOR b_B = b_Tip - b_LineEndOffset;
// Vectors between line endpoints:
XMVECTOR v0 = b_A - a_A;
XMVECTOR v1 = b_B - a_A;
XMVECTOR v2 = b_A - a_B;
XMVECTOR v3 = b_B - a_B;
// Distances (squared) between line endpoints:
float d0 = XMVectorGetX(XMVector3LengthSq(v0));
float d1 = XMVectorGetX(XMVector3LengthSq(v1));
float d2 = XMVectorGetX(XMVector3LengthSq(v2));
float d3 = XMVectorGetX(XMVector3LengthSq(v3));
// Select best potential endpoint on capsule A:
XMVECTOR bestA;
if (d2 < d0 || d2 < d1 || d3 < d0 || d3 < d1)
{
bestA = a_B;
}
else
{
bestA = a_A;
}
// Select point on capsule B line segment nearest to best potential endpoint on A capsule:
XMVECTOR bestB = wiMath::ClosestPointOnLineSegment(b_A, b_B, bestA);
// Now do the same for capsule A segment:
bestA = wiMath::ClosestPointOnLineSegment(a_A, a_B, bestB);
// Finally, sphere collision:
XMVECTOR N = bestA - bestB;
XMVECTOR len = XMVector3Length(N);
N /= len;
float dist = XMVectorGetX(len);
XMStoreFloat3(&position, bestA - N * radius);
XMStoreFloat3(&incident_normal, N);
penetration_depth = radius + other.radius - dist;
return penetration_depth > 0;
}
bool RAY::intersects(const AABB& b) const {
return b.intersects(*this);
}
bool RAY::intersects(const SPHERE& b) const {
return b.intersects(*this);
}
void Frustum::Create(const XMMATRIX& viewProjection)
{
// We are interested in columns of the matrix, so transpose because we can access only rows:
const XMMATRIX mat = XMMatrixTranspose(viewProjection);
// Near plane:
XMStoreFloat4(&planes[0], XMPlaneNormalize(mat.r[2]));
// Far plane:
XMStoreFloat4(&planes[1], XMPlaneNormalize(mat.r[3] - mat.r[2]));
// Left plane:
XMStoreFloat4(&planes[2], XMPlaneNormalize(mat.r[3] + mat.r[0]));
// Right plane:
XMStoreFloat4(&planes[3], XMPlaneNormalize(mat.r[3] - mat.r[0]));
// Top plane:
XMStoreFloat4(&planes[4], XMPlaneNormalize(mat.r[3] - mat.r[1]));
// Bottom plane:
XMStoreFloat4(&planes[5], XMPlaneNormalize(mat.r[3] + mat.r[1]));
}
bool Frustum::CheckPoint(const XMFLOAT3& point) const
{
XMVECTOR p = XMLoadFloat3(&point);
for (short i = 0; i < 6; i++)
{
if (XMVectorGetX(XMPlaneDotCoord(XMLoadFloat4(&planes[i]), p)) < 0.0f)
{
return false;
}
}
return true;
}
bool Frustum::CheckSphere(const XMFLOAT3& center, float radius) const
{
int i;
XMVECTOR c = XMLoadFloat3(&center);
for (i = 0; i < 6; i++)
{
if (XMVectorGetX(XMPlaneDotCoord(XMLoadFloat4(&planes[i]), c)) < -radius)
{
return false;
}
}
return true;
}
Frustum::BoxFrustumIntersect Frustum::CheckBox(const AABB& box) const
{
int iTotalIn = 0;
for (int p = 0; p < 6; ++p)
{
int iInCount = 8;
int iPtIn = 1;
for (int i = 0; i < 8; ++i)
{
XMFLOAT3 C = box.corner(i);
if (XMVectorGetX(XMPlaneDotCoord(XMLoadFloat4(&planes[p]), XMLoadFloat3(&C))) < 0.0f)
{
iPtIn = 0;
--iInCount;
}
}
if (iInCount == 0)
return BOX_FRUSTUM_OUTSIDE;
iTotalIn += iPtIn;
}
if (iTotalIn == 6)
return(BOX_FRUSTUM_INSIDE);
return(BOX_FRUSTUM_INTERSECTS);
}
bool Frustum::CheckBoxFast(const AABB& box) const
{
XMVECTOR max = XMLoadFloat3(&box._max);
XMVECTOR min = XMLoadFloat3(&box._min);
XMVECTOR zero = XMVectorZero();
for (size_t p = 0; p < 6; ++p)
{
auto lt = XMVectorLess(XMLoadFloat4(&planes[p]), zero);
auto furthestFromPlane = XMVectorSelect(max, min, lt);
if (XMVectorGetX(XMPlaneDotCoord(XMLoadFloat4(&planes[p]), furthestFromPlane)) < 0.0f)
{
return false;
}
}
return true;
}
const XMFLOAT4& Frustum::getNearPlane() const { return planes[0]; }
const XMFLOAT4& Frustum::getFarPlane() const { return planes[1]; }
const XMFLOAT4& Frustum::getLeftPlane() const { return planes[2]; }
const XMFLOAT4& Frustum::getRightPlane() const { return planes[3]; }
const XMFLOAT4& Frustum::getTopPlane() const { return planes[4]; }
const XMFLOAT4& Frustum::getBottomPlane() const { return planes[5]; }
bool Hitbox2D::intersects(const Hitbox2D& b) const
{
return wiMath::Collision2D(pos, siz, b.pos, b.siz);
}