LumixEngine/src/engine/geometry.cpp
2022-09-23 18:11:01 +02:00

1045 lines
25 KiB
C++

#include "geometry.h"
#include "engine/crt.h"
#include "engine/math.h"
#include "engine/simd.h"
namespace Lumix
{
Sphere::Sphere() {}
Sphere::Sphere(float x, float y, float z, float _radius)
: position(x, y, z)
, radius(_radius)
{
}
Sphere::Sphere(const Vec3& point, float _radius)
: position(point)
, radius(_radius)
{
}
Sphere::Sphere(const Vec4& sphere)
: position(sphere.x, sphere.y, sphere.z)
, radius(sphere.w)
{
}
Frustum::Frustum()
{
xs[6] = xs[7] = 1;
ys[6] = ys[7] = 0;
zs[6] = zs[7] = 0;
ds[6] = ds[7] = 0;
}
bool ShiftedFrustum::intersectNearPlane(const DVec3& center, float radius) const {
const float x = float(center.x - origin.x);
const float y = float(center.y - origin.y);
const float z = float(center.z - origin.z);
const u32 i = (u32)Frustum::Planes::NEAR;
float distance = xs[i] * x + ys[i] * y + z * zs[i] + ds[i];
distance = distance < 0 ? -distance : distance;
return distance < radius;
}
bool Frustum::intersectNearPlane(const Vec3& center, float radius) const {
float x = center.x;
float y = center.y;
float z = center.z;
u32 i = (u32)Planes::NEAR;
float distance = xs[i] * x + ys[i] * y + z * zs[i] + ds[i];
distance = distance < 0 ? -distance : distance;
return distance < radius;
}
bool Frustum::intersectAABBWithOffset(const AABB& aabb, float size_offset) const {
Vec3 box[] = { aabb.min, aabb.max };
for (int i = 0; i < 6; ++i)
{
int px = (int)(xs[i] > 0.0f);
int py = (int)(ys[i] > 0.0f);
int pz = (int)(zs[i] > 0.0f);
float dp =
(xs[i] * box[px].x) +
(ys[i] * box[py].y) +
(zs[i] * box[pz].z);
if (dp < -ds[i] - size_offset) { return false; }
}
return true;
}
bool Frustum::intersectAABB(const AABB& aabb) const
{
Vec3 box[] = { aabb.min, aabb.max };
for (int i = 0; i < 6; ++i)
{
int px = (int)(xs[i] > 0.0f);
int py = (int)(ys[i] > 0.0f);
int pz = (int)(zs[i] > 0.0f);
float dp =
(xs[i] * box[px].x) +
(ys[i] * box[py].y) +
(zs[i] * box[pz].z);
if (dp < -ds[i]) { return false; }
}
return true;
}
bool ShiftedFrustum::containsAABB(const DVec3& pos, const Vec3& size) const
{
const Vec3 rel_pos = Vec3(pos - origin);
Vec3 box[] = { rel_pos, rel_pos + size };
for (int i = 0; i < 6; ++i)
{
int px = int(xs[i] < 0.0f);
int py = int(ys[i] < 0.0f);
int pz = int(zs[i] < 0.0f);
float dp =
(xs[i] * box[px].x) +
(ys[i] * box[py].y) +
(zs[i] * box[pz].z);
if (dp < -ds[i]) { return false; }
}
return true;
}
Frustum ShiftedFrustum::getRelative(const DVec3& origin) const
{
Frustum res;
const Vec3 offset = Vec3(this->origin - origin);
memcpy(res.points, points, sizeof(points));
const Vec3 n_near = getNormal(Frustum::Planes::NEAR);
const Vec3 n_far = getNormal(Frustum::Planes::FAR);
const Vec3 n_left = getNormal(Frustum::Planes::LEFT);
const Vec3 n_right = getNormal(Frustum::Planes::RIGHT);
const Vec3 n_top = getNormal(Frustum::Planes::TOP);
const Vec3 n_bottom = getNormal(Frustum::Planes::BOTTOM);
res.setPlane(Frustum::Planes::EXTRA0, n_near, points[0] + offset);
res.setPlane(Frustum::Planes::EXTRA1, n_near, points[0] + offset);
res.setPlane(Frustum::Planes::NEAR, n_near, points[0] + offset);
res.setPlane(Frustum::Planes::FAR, n_far, points[4] + offset);
res.setPlane(Frustum::Planes::LEFT, n_left, points[1] + offset);
res.setPlane(Frustum::Planes::RIGHT, n_right, points[0] + offset);
res.setPlane(Frustum::Planes::TOP, n_top, points[0] + offset);
res.setPlane(Frustum::Planes::BOTTOM, n_bottom, points[2] + offset);
for(Vec3& p : res.points) {
p += offset;
}
return res;
}
Vec3 ShiftedFrustum::getNormal(Frustum::Planes plane) const {
return {
xs[(int)plane],
ys[(int)plane],
zs[(int)plane]
};
}
bool ShiftedFrustum::intersectsAABB(const DVec3& pos, const Vec3& size) const
{
const Vec3 rel_pos = Vec3(pos - origin);
Vec3 box[] = { rel_pos, rel_pos + size };
for (int i = 0; i < 6; ++i)
{
int px = int(xs[i] > 0.0f);
int py = int(ys[i] > 0.0f);
int pz = int(zs[i] > 0.0f);
float dp =
(xs[i] * box[px].x) +
(ys[i] * box[py].y) +
(zs[i] * box[pz].z);
if (dp < -ds[i]) { return false; }
}
return true;
}
void Frustum::transform(const Matrix& mtx)
{
for (Vec3& p : points)
{
p = mtx.transformPoint(p);
}
for (u32 i = 0; i < lengthOf(xs); ++i)
{
Vec3 p;
if (xs[i] != 0) p = Vec3(-ds[i] / xs[i], 0, 0);
else if (ys[i] != 0) p = Vec3(0, -ds[i] / ys[i], 0);
else p = Vec3(0, 0, -ds[i] / zs[i]);
Vec3 n = {xs[i], ys[i], zs[i]};
n = mtx.transformVector(n);
p = mtx.transformPoint(p);
xs[i] = n.x;
ys[i] = n.y;
zs[i] = n.z;
ds[i] = -dot(p, n);
}
}
Frustum Frustum::transformed(const Matrix& mtx) const
{
Frustum res;
for (u32 i = 0; i < lengthOf(points); ++i) {
res.points[i] = mtx.transformPoint(points[i]);
}
for (u32 i = 0; i < lengthOf(xs); ++i) {
Vec3 p;
if (xs[i] != 0) p = Vec3(-ds[i] / xs[i], 0, 0);
else if (ys[i] != 0) p = Vec3(0, -ds[i] / ys[i], 0);
else p = Vec3(0, 0, -ds[i] / zs[i]);
Vec3 n = { xs[i], ys[i], zs[i] };
n = mtx.transformVector(n);
p = mtx.transformPoint(p);
res.xs[i] = n.x;
res.ys[i] = n.y;
res.zs[i] = n.z;
res.ds[i] = -dot(p, n);
}
return res;
}
Sphere Frustum::computeBoundingSphere() const
{
Sphere sphere;
sphere.position = points[0];
for (u32 i = 1; i < lengthOf(points); ++i)
{
sphere.position += points[i];
}
sphere.position *= 1.0f / lengthOf(points);
sphere.radius = 0;
for (u32 i = 0; i < lengthOf(points); ++i)
{
float len_sq = squaredLength(points[i] - sphere.position);
if (len_sq > sphere.radius) sphere.radius = len_sq;
}
sphere.radius = sqrtf(sphere.radius);
return sphere;
}
bool Frustum::isSphereInside(const Vec3& center, float radius) const
{
float4 px = f4Load(xs);
float4 py = f4Load(ys);
float4 pz = f4Load(zs);
float4 pd = f4Load(ds);
float4 cx = f4Splat(center.x);
float4 cy = f4Splat(center.y);
float4 cz = f4Splat(center.z);
float4 t = f4Mul(cx, px);
t = f4Add(t, f4Mul(cy, py));
t = f4Add(t, f4Mul(cz, pz));
t = f4Add(t, pd);
t = f4Sub(t, f4Splat(-radius));
if (f4MoveMask(t)) return false;
px = f4Load(&xs[4]);
py = f4Load(&ys[4]);
pz = f4Load(&zs[4]);
pd = f4Load(&ds[4]);
t = f4Mul(cx, px);
t = f4Add(t, f4Mul(cy, py));
t = f4Add(t, f4Mul(cz, pz));
t = f4Add(t, pd);
t = f4Sub(t, f4Splat(-radius));
return f4MoveMask(t) == 0;
}
void Frustum::computeOrtho(const Vec3& position,
const Vec3& direction,
const Vec3& up,
float width,
float height,
float near_distance,
float far_distance)
{
computeOrtho(position, direction, up, width, height, near_distance, far_distance, {-1, -1}, {1, 1});
}
void ShiftedFrustum::computeOrtho(const DVec3& position,
const Vec3& direction,
const Vec3& up,
float width,
float height,
float near_distance,
float far_distance)
{
computeOrtho(position, direction, up, width, height, near_distance, far_distance, {-1, -1}, {1, 1});
}
void Frustum::setPlanesFromPoints()
{
Vec3 normal_near = -normalize(cross(points[0] - points[1], points[0] - points[2]));
Vec3 normal_far = normalize(cross(points[4] - points[5], points[4] - points[6]));
setPlane(Frustum::Planes::EXTRA0, normal_near, points[0]);
setPlane(Frustum::Planes::EXTRA1, normal_near, points[0]);
setPlane(Frustum::Planes::NEAR, normal_near, points[0]);
setPlane(Frustum::Planes::FAR, normal_far, points[4]);
setPlane(Frustum::Planes::LEFT, normalize(cross(points[1] - points[2], points[1] - points[5])), points[1]);
setPlane(Frustum::Planes::RIGHT, -normalize(cross(points[0] - points[3], points[0] - points[4])), points[0]);
setPlane(Frustum::Planes::TOP, normalize(cross(points[0] - points[1], points[0] - points[4])), points[0]);
setPlane(Frustum::Planes::BOTTOM, normalize(cross(points[2] - points[3], points[2] - points[6])), points[2]);
}
void ShiftedFrustum::setPlanesFromPoints()
{
Vec3 normal_near = -normalize(cross(points[0] - points[1], points[0] - points[2]));
Vec3 normal_far = normalize(cross(points[4] - points[5], points[4] - points[6]));
setPlane(Frustum::Planes::EXTRA0, normal_near, points[0]);
setPlane(Frustum::Planes::EXTRA1, normal_near, points[0]);
setPlane(Frustum::Planes::NEAR, normal_near, points[0]);
setPlane(Frustum::Planes::FAR, normal_far, points[4]);
setPlane(Frustum::Planes::LEFT, normalize(cross(points[1] - points[2], points[1] - points[5])), points[1]);
setPlane(Frustum::Planes::RIGHT, -normalize(cross(points[0] - points[3], points[0] - points[4])), points[0]);
setPlane(Frustum::Planes::TOP, normalize(cross(points[0] - points[1], points[0] - points[4])), points[0]);
setPlane(Frustum::Planes::BOTTOM, normalize(cross(points[2] - points[3], points[2] - points[6])), points[2]);
}
template <typename T>
static void setPoints(T& frustum
, const Vec3& near_center
, const Vec3& far_center
, const Vec3& right_near
, const Vec3& up_near
, const Vec3& right_far
, const Vec3& up_far
, const Vec2& viewport_min
, const Vec2& viewport_max)
{
ASSERT(viewport_max.x >= viewport_min.x);
ASSERT(viewport_max.y >= viewport_min.y);
Vec3* points = frustum.points;
points[0] = near_center + right_near * viewport_max.x + up_near * viewport_max.y;
points[1] = near_center + right_near * viewport_min.x + up_near * viewport_max.y;
points[2] = near_center + right_near * viewport_min.x + up_near * viewport_min.y;
points[3] = near_center + right_near * viewport_max.x + up_near * viewport_min.y;
points[4] = far_center + right_far * viewport_max.x + up_far * viewport_max.y;
points[5] = far_center + right_far * viewport_min.x + up_far * viewport_max.y;
points[6] = far_center + right_far * viewport_min.x + up_far * viewport_min.y;
points[7] = far_center + right_far * viewport_max.x + up_far * viewport_min.y;
frustum.setPlanesFromPoints();
}
void Frustum::computeOrtho(const Vec3& position,
const Vec3& direction,
const Vec3& up,
float width,
float height,
float near_distance,
float far_distance,
const Vec2& viewport_min,
const Vec2& viewport_max)
{
Vec3 z = normalize(direction);
Vec3 near_center = position - z * near_distance;
Vec3 far_center = position - z * far_distance;
Vec3 x = normalize(cross(up, z)) * width;
Vec3 y = normalize(cross(z, x)) * height;
setPoints(*this, near_center, far_center, x, y, x, y, viewport_min, viewport_max);
}
void ShiftedFrustum::computeOrtho(const DVec3& position,
const Vec3& direction,
const Vec3& up,
float width,
float height,
float near_distance,
float far_distance,
const Vec2& viewport_min,
const Vec2& viewport_max)
{
Vec3 z = normalize(direction);
origin = position;
Vec3 near_center = - z * near_distance;
Vec3 far_center = - z * far_distance;
Vec3 x = normalize(cross(up, z)) * width;
Vec3 y = normalize(cross(z, x)) * height;
setPoints(*this, near_center, far_center, x, y, x, y, viewport_min, viewport_max);
}
void Frustum::setPlane(Planes side, const Vec3& normal, const Vec3& point)
{
xs[(u32)side] = normal.x;
ys[(u32)side] = normal.y;
zs[(u32)side] = normal.z;
ds[(u32)side] = -dot(point, normal);
}
void ShiftedFrustum::setPlane(Frustum::Planes side, const Vec3& normal, const Vec3& point)
{
xs[(u32)side] = normal.x;
ys[(u32)side] = normal.y;
zs[(u32)side] = normal.z;
ds[(u32)side] = -dot(point, normal);
}
void Frustum::setPlane(Planes side, const Vec3& normal, float d)
{
xs[(u32)side] = normal.x;
ys[(u32)side] = normal.y;
zs[(u32)side] = normal.z;
ds[(u32)side] = d;
}
void Frustum::computePerspective(const Vec3& position,
const Vec3& direction,
const Vec3& up,
float fov,
float ratio,
float near_distance,
float far_distance,
const Vec2& viewport_min,
const Vec2& viewport_max)
{
ASSERT(near_distance > 0);
ASSERT(far_distance > 0);
ASSERT(near_distance < far_distance);
ASSERT(fov > 0);
ASSERT(ratio > 0);
float scale = (float)tan(fov * 0.5f);
Vec3 right = cross(direction, up);
Vec3 up_near = up * near_distance * scale;
Vec3 right_near = right * (near_distance * scale * ratio);
Vec3 up_far = up * far_distance * scale;
Vec3 right_far = right * (far_distance * scale * ratio);
Vec3 z = normalize(direction);
Vec3 near_center = position + z * near_distance;
Vec3 far_center = position + z * far_distance;
setPoints(*this, near_center, far_center, right_near, up_near, right_far, up_far, viewport_min, viewport_max);
}
void ShiftedFrustum::computePerspective(const DVec3& position,
const Vec3& direction,
const Vec3& up,
float fov,
float ratio,
float near_distance,
float far_distance,
const Vec2& viewport_min,
const Vec2& viewport_max)
{
ASSERT(near_distance > 0);
ASSERT(far_distance > 0);
ASSERT(near_distance < far_distance);
ASSERT(fov > 0);
ASSERT(ratio > 0);
const float scale = (float)tan(fov * 0.5f);
const Vec3 right = cross(direction, up);
const Vec3 up_near = up * near_distance * scale;
const Vec3 right_near = right * (near_distance * scale * ratio);
const Vec3 up_far = up * far_distance * scale;
const Vec3 right_far = right * (far_distance * scale * ratio);
const Vec3 z = normalize(direction);
const Vec3 near_center = z * near_distance;
const Vec3 far_center = z * far_distance;
origin = position;
setPoints(*this, near_center, far_center, right_near, up_near, right_far, up_far, viewport_min, viewport_max);
}
void Frustum::computePerspective(const Vec3& position,
const Vec3& direction,
const Vec3& up,
float fov,
float ratio,
float near_distance,
float far_distance)
{
computePerspective(position, direction, up, fov, ratio, near_distance, far_distance, {-1, -1}, {1, 1});
}
void ShiftedFrustum::computePerspective(const DVec3& position,
const Vec3& direction,
const Vec3& up,
float fov,
float ratio,
float near_distance,
float far_distance)
{
computePerspective(position, direction, up, fov, ratio, near_distance, far_distance, {-1, -1}, {1, 1});
}
AABB::AABB() {}
AABB::AABB(const Vec3& _min, const Vec3& _max)
: min(_min)
, max(_max)
{}
void AABB::merge(const AABB& rhs) {
addPoint(rhs.min);
addPoint(rhs.max);
}
void AABB::addPoint(const Vec3& point) {
min = minCoords(point, min);
max = maxCoords(point, max);
}
bool AABB::contains(const Vec3& point) const {
if (min.x > point.x) return false;
if (min.y > point.y) return false;
if (min.z > point.z) return false;
if (point.x > max.x) return false;
if (point.y > max.y) return false;
if (point.z > max.z) return false;
return true;
}
bool AABB::overlaps(const AABB& aabb) const {
if (min.x > aabb.max.x) return false;
if (min.y > aabb.max.y) return false;
if (min.z > aabb.max.z) return false;
if (aabb.min.x > max.x) return false;
if (aabb.min.y > max.y) return false;
if (aabb.min.z > max.z) return false;
return true;
}
AABB AABB::intersection(const AABB& rhs) const {
return AABB(maximum(rhs.min, min), minimum(rhs.max, max));
}
void AABB::translate(const Vec3& v) {
min += v;
max += v;
}
void AABB::transform(const Matrix& matrix)
{
Vec3 points[8];
points[0] = min;
points[7] = max;
points[1] = Vec3(points[0].x, points[0].y, points[7].z);
points[2] = Vec3(points[0].x, points[7].y, points[0].z);
points[3] = Vec3(points[0].x, points[7].y, points[7].z);
points[4] = Vec3(points[7].x, points[0].y, points[0].z);
points[5] = Vec3(points[7].x, points[0].y, points[7].z);
points[6] = Vec3(points[7].x, points[7].y, points[0].z);
for (int j = 0; j < 8; ++j)
{
points[j] = matrix.transformPoint(points[j]);
}
Vec3 new_min = points[0];
Vec3 new_max = points[0];
for (int j = 0; j < 8; ++j)
{
new_min = minCoords(points[j], new_min);
new_max = maxCoords(points[j], new_max);
}
min = new_min;
max = new_max;
}
void AABB::getCorners(const Matrix& matrix, Vec3* points) const
{
Vec3 p(min.x, min.y, min.z);
points[0] = matrix.transformPoint(p);
p = Vec3(min.x, min.y, max.z);
points[1] = matrix.transformPoint(p);
p = Vec3(min.x, max.y, min.z);
points[2] = matrix.transformPoint(p);
p = Vec3(min.x, max.y, max.z);
points[3] = matrix.transformPoint(p);
p = Vec3(max.x, min.y, min.z);
points[4] = matrix.transformPoint(p);
p = Vec3(max.x, min.y, max.z);
points[5] = matrix.transformPoint(p);
p = Vec3(max.x, max.y, min.z);
points[6] = matrix.transformPoint(p);
p = Vec3(max.x, max.y, max.z);
points[7] = matrix.transformPoint(p);
}
void AABB::getCorners(const Transform& tr, DVec3* points) const
{
DVec3 p(min.x, min.y, min.z);
points[0] = tr.transform(p);
p = DVec3(min.x, min.y, max.z);
points[1] = tr.transform(p);
p = DVec3(min.x, max.y, min.z);
points[2] = tr.transform(p);
p = DVec3(min.x, max.y, max.z);
points[3] = tr.transform(p);
p = DVec3(max.x, min.y, min.z);
points[4] = tr.transform(p);
p = DVec3(max.x, min.y, max.z);
points[5] = tr.transform(p);
p = DVec3(max.x, max.y, min.z);
points[6] = tr.transform(p);
p = DVec3(max.x, max.y, max.z);
points[7] = tr.transform(p);
}
void AABB::shrink(float x) {
min += Vec3(x);
max -= Vec3(x);
}
Vec3 AABB::minCoords(const Vec3& a, const Vec3& b)
{
return Vec3(minimum(a.x, b.x), minimum(a.y, b.y), minimum(a.z, b.z));
}
Vec3 AABB::maxCoords(const Vec3& a, const Vec3& b)
{
return Vec3(maximum(a.x, b.x), maximum(a.y, b.y), maximum(a.z, b.z));
}
Matrix Viewport::getProjectionWithJitter() const
{
Matrix mtx;
const float ratio = h > 0 ? w / (float)h : 1;
if (is_ortho) {
mtx.setOrtho(-ortho_size * ratio,
ortho_size * ratio,
-ortho_size,
ortho_size,
near,
far,
true);
return mtx;
}
mtx.setPerspective(fov, ratio, near, far, true);
mtx.columns[2].x = pixel_offset.x;
mtx.columns[2].y = pixel_offset.y;
return mtx;
}
Matrix Viewport::getProjectionNoJitter() const
{
Matrix mtx;
const float ratio = h > 0 ? w / (float)h : 1;
if (is_ortho) {
mtx.setOrtho(-ortho_size * ratio,
ortho_size * ratio,
-ortho_size,
ortho_size,
near,
far,
true);
return mtx;
}
mtx.setPerspective(fov, ratio, near, far, true);
return mtx;
}
Matrix Viewport::getView(const DVec3& origin) const
{
Matrix view = rot.toMatrix();
view.setTranslation(Vec3(pos - origin));
return view.fastInverted();
}
Matrix Viewport::getViewRotation() const
{
Matrix view = rot.conjugated().toMatrix();
return view;
}
void Viewport::getRay(const Vec2& screen_pos, DVec3& origin, Vec3& dir) const
{
origin = pos;
if (w <= 0 || h <= 0) {
dir = rot.rotate(Vec3(0, 0, 1));
return;
}
const float nx = 2 * (screen_pos.x / w) - 1;
const float ny = 2 * ((h - screen_pos.y) / h) - 1;
const Matrix projection_matrix = getProjectionNoJitter();
if (is_ortho) {
const Vec3 x = rot * Vec3(1, 0, 0);
const Vec3 y = rot * Vec3(0, 1, 0);
float ratio = h > 0 ? w / (float)h : 1;
origin += x * nx * ortho_size * ratio
+ y * ny * ortho_size;
}
const Matrix view_matrix = getView(origin);
const Matrix inverted = (projection_matrix * view_matrix).inverted();
Vec4 p0 = inverted * Vec4(nx, ny, -1, 1);
Vec4 p1 = inverted * Vec4(nx, ny, 1, 1);
p0 *= 1 / p0.w;
p1 *= 1 / p1.w;
dir = normalize((p1 - p0).xyz());
if (is_ortho) dir *= -1.f;
}
Vec2 Viewport::worldToScreenPixels(const DVec3& world) const
{
const Matrix mtx = getProjectionNoJitter() * getView(world);
const Vec4 pos = mtx * Vec4(0, 0, 0, 1);
const float inv = 1 / pos.w;
const Vec2 screen_size((float)w, (float)h);
const Vec2 screen_pos = { 0.5f * pos.x * inv + 0.5f, 1.0f - (0.5f * pos.y * inv + 0.5f) };
return screen_pos * screen_size;
}
ShiftedFrustum Viewport::getFrustum(const Vec2& viewport_min_px, const Vec2& viewport_max_px) const
{
const Matrix mtx = rot.toMatrix();
ShiftedFrustum ret;
const float ratio = h > 0 ? w / (float)h : 1;
const Vec2 viewport_min = { viewport_min_px.x / w * 2 - 1, (1 - viewport_max_px.y / h) * 2 - 1 };
const Vec2 viewport_max = { viewport_max_px.x / w * 2 - 1, (1 - viewport_min_px.y / h) * 2 - 1 };
if (is_ortho) {
ret.computeOrtho({0, 0, 0},
mtx.getZVector(),
mtx.getYVector(),
ortho_size * ratio,
ortho_size,
near,
far,
viewport_min,
viewport_max);
ret.origin = pos;
return ret;
}
ret.computePerspective({0, 0, 0},
-mtx.getZVector(),
mtx.getYVector(),
fov,
ratio,
near,
far,
viewport_min,
viewport_max);
ret.origin = pos;
return ret;
}
ShiftedFrustum Viewport::getFrustum() const
{
ShiftedFrustum ret;
const float ratio = h > 0 ? w / (float)h : 1;
if (is_ortho) {
ret.computeOrtho({0, 0, 0},
rot * Vec3(0, 0, 1),
rot * Vec3(0, 1, 0),
ortho_size * ratio,
ortho_size,
near,
far);
ret.origin = pos;
return ret;
}
ret.computePerspective({0, 0, 0},
rot * Vec3(0, 0, -1),
rot * Vec3(0, 1, 0),
fov,
ratio,
near,
far);
ret.origin = pos;
return ret;
}
Vec4 makePlane(const Vec3& normal, const Vec3& point) {
ASSERT(squaredLength(normal) < 1.001f);
ASSERT(squaredLength(normal) > 0.999f);
return Vec4(normal, -dot(normal, point));
}
float planeDist(const Vec4& plane, const Vec3& point) {
return plane.x * point.x + plane.y * point.y + plane.z * point.z + plane.w;
}
bool getRayPlaneIntersecion(const Vec3& origin,
const Vec3& dir,
const Vec3& plane_point,
const Vec3& normal,
float& out)
{
float d = dot(dir, normal);
if (d == 0) return false;
d = dot(plane_point - origin, normal) / d;
out = d;
return true;
}
bool getRaySphereIntersection(const Vec3& origin,
const Vec3& dir,
const Vec3& center,
float radius,
float& out)
{
ASSERT(length(dir) < 1.01f && length(dir) > 0.99f);
Vec3 L = center - origin;
float tca = dot(L, dir);
float d2 = dot(L, L) - tca * tca;
if (d2 > radius * radius) return false;
float thc = sqrtf(radius * radius - d2);
float t = tca - thc;
out = t >= 0 ? t : tca + thc;
return true;
}
bool getRayAABBIntersection(const Vec3& origin,
const Vec3& dir,
const Vec3& min,
const Vec3& size,
Vec3& out)
{
Vec3 dirfrac;
dirfrac.x = 1.0f / (dir.x == 0 ? 0.00000001f : dir.x);
dirfrac.y = 1.0f / (dir.y == 0 ? 0.00000001f : dir.y);
dirfrac.z = 1.0f / (dir.z == 0 ? 0.00000001f : dir.z);
Vec3 max = min + size;
float t1 = (min.x - origin.x) * dirfrac.x;
float t2 = (max.x - origin.x) * dirfrac.x;
float t3 = (min.y - origin.y) * dirfrac.y;
float t4 = (max.y - origin.y) * dirfrac.y;
float t5 = (min.z - origin.z) * dirfrac.z;
float t6 = (max.z - origin.z) * dirfrac.z;
float tmin = maximum(maximum(minimum(t1, t2), minimum(t3, t4)), minimum(t5, t6));
float tmax = minimum(minimum(maximum(t1, t2), maximum(t3, t4)), maximum(t5, t6));
if (tmax < 0) return false;
if (tmin > tmax) return false;
out = tmin < 0 ? origin : origin + dir * tmin;
return true;
}
float getLineSegmentDistance(const Vec3& origin, const Vec3& dir, const Vec3& a, const Vec3& b)
{
Vec3 a_origin = origin - a;
Vec3 ab = b - a;
float dot1 = dot(ab, a_origin);
float dot2 = dot(ab, dir);
float dot3 = dot(dir, a_origin);
float dot4 = dot(ab, ab);
float dot5 = dot(dir, dir);
float denom = dot4 * dot5 - dot2 * dot2;
if (fabsf(denom) < 1e-5f)
{
Vec3 X = origin + dir * dot(b - origin, dir);
return length(b - X);
}
float numer = dot1 * dot2 - dot3 * dot4;
float param_a = numer / denom;
float param_b = (dot1 + dot2 * param_a) / dot4;
if (param_b < 0 || param_b > 1)
{
param_b = clamp(param_b, 0.0f, 1.0f);
Vec3 B = a + ab * param_b;
Vec3 X = origin + dir * dot(b - origin, dir);
return length(B - X);
}
Vec3 vec = (origin + dir * param_a) - (a + ab * param_b);
return length(vec);
}
bool getRayTriangleIntersection(const Vec3& origin,
const Vec3& dir,
const Vec3& p0,
const Vec3& p1,
const Vec3& p2,
float* out_t)
{
Vec3 normal = cross(p1 - p0, p2 - p0);
float q = dot(normal, dir);
if (q == 0) return false;
float d = -dot(normal, p0);
float t = -(dot(normal, origin) + d) / q;
if (t < 0) return false;
Vec3 hit_point = origin + dir * t;
Vec3 edge0 = p1 - p0;
Vec3 VP0 = hit_point - p0;
if (dot(normal, cross(edge0, VP0)) < 0)
{
return false;
}
Vec3 edge1 = p2 - p1;
Vec3 VP1 = hit_point - p1;
if (dot(normal, cross(edge1, VP1)) < 0)
{
return false;
}
Vec3 edge2 = p0 - p2;
Vec3 VP2 = hit_point - p2;
if (dot(normal, cross(edge2, VP2)) < 0)
{
return false;
}
if (out_t) *out_t = t;
return true;
}
LUMIX_ENGINE_API bool getSphereTriangleIntersection(const Vec3& center,
float radius,
const Vec3& v0,
const Vec3& v1,
const Vec3& v2)
{
Vec3 normal = normalize(cross(v0 - v1, v2 - v1));
float D = -dot(v0, normal);
float dist = dot(center, normal) + D;
if (fabs(dist) > radius) return false;
float squared_radius = radius * radius;
if (squaredLength(v0 - center) < squared_radius) return true;
if (squaredLength(v1 - center) < squared_radius) return true;
if (squaredLength(v2 - center) < squared_radius) return true;
return false;
}
static void getProjections(const Vec3& axis,
const Vec3 vertices[8],
float& min,
float& max)
{
max = dot(vertices[0], axis);
min = max;
for(int i = 1; i < 8; ++i)
{
float d = dot(vertices[i], axis);
min = minimum(d, min);
max = maximum(d, max);
}
}
static bool overlaps(float min1, float max1, float min2, float max2)
{
return (min1 <= min2 && min2 <= max1) || (min2 <= min1 && min1 <= max2);
}
bool testOBBCollision(const AABB& a, const Matrix& mtx_b, const AABB& b)
{
Vec3 box_a_points[8];
Vec3 box_b_points[8];
a.getCorners(Matrix::IDENTITY, box_a_points);
b.getCorners(mtx_b, box_b_points);
const Vec3 normals[] = {Vec3(1, 0, 0), Vec3(0, 1, 0), Vec3(0, 0, 1)};
for(int i = 0; i < 3; i++)
{
float box_a_min, box_a_max, box_b_min, box_b_max;
getProjections(normals[i], box_a_points, box_a_min, box_a_max);
getProjections(normals[i], box_b_points, box_b_min, box_b_max);
if(!overlaps(box_a_min, box_a_max, box_b_min, box_b_max))
{
return false;
}
}
Vec3 normals_b[] = {
normalize(mtx_b.getXVector()), normalize(mtx_b.getYVector()), normalize(mtx_b.getZVector())};
for(int i = 0; i < 3; i++)
{
float box_a_min, box_a_max, box_b_min, box_b_max;
getProjections(normals_b[i], box_a_points, box_a_min, box_a_max);
getProjections(normals_b[i], box_b_points, box_b_min, box_b_max);
if(!overlaps(box_a_min, box_a_max, box_b_min, box_b_max))
{
return false;
}
}
return true;
}
} // namespace Lumix