#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 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