From 637bc5a41341ea32ec4fbb8dba54d2e3d6977446 Mon Sep 17 00:00:00 2001 From: karai17 Date: Tue, 26 Jul 2016 00:44:15 -0300 Subject: [PATCH] Added many intersection tests --- modules/intersect.lua | 124 +++++++++++--------- spec/intersect_spec.lua | 253 ++++++++++++++++++++++++++++++++++++---- 2 files changed, 302 insertions(+), 75 deletions(-) diff --git a/modules/intersect.lua b/modules/intersect.lua index 2eee4d5..0c3831c 100644 --- a/modules/intersect.lua +++ b/modules/intersect.lua @@ -6,43 +6,48 @@ local constants = require(modules .. "constants") local vec3 = require(modules .. "vec3") local mat4 = require(modules .. "mat4") local DBL_EPSILON = constants.DBL_EPSILON +local FLT_EPSILON = constants.FLT_EPSILON local sqrt = math.sqrt local abs = math.abs local min = math.min local max = math.max local intersect = {} --- http://www.peroxide.dk/papers/collision/collision.pdf +-- Some temp variables +local h, s, q, e1, e2 = vec3(), vec3(), vec3(), vec3(), vec3() +local dir, dirfrac = vec3(), vec3() +local p13, p43, p21 = vec3(), vec3(), vec3() +local axes = { "x", "y", "z" } + +-- https://blogs.msdn.microsoft.com/rezanour/2011/08/07/barycentric-coordinates-and-point-in-triangle-tests/ -- point is a vec3 -- triangle[1] is a vec3 -- triangle[2] is a vec3 -- triangle[3] is a vec3 function intersect.point_triangle(point, triangle) - local t21 = triangle[2] - triangle[1] - local t31 = triangle[3] - triangle[1] + local u = triangle[2] - triangle[1] + local v = triangle[3] - triangle[1] + local w = point - triangle[1] - local a = t21:dot(t21) - local b = t21:dot(t31) - local c = t31:dot(t31) - local ac_bb = a * c - b * b + local vw = vec3():cross(v, w) + local vu = vec3():cross(v, u) - local v = vec3( - point.x - triangle[1].x, - point.y - triangle[1].y, - point.z - triangle[1].z - ) + if vw:dot(vu) < 0 then + return false + end - local d = v:dot(t21) - local e = v:dot(t31) + local uw = vec3():cross(u, w) + local uv = vec3():cross(u, v) - local x = d * c - e * b - local y = e * a - d * b - local z = x + y - ac_bb + if uw:dot(uv) < 0 then + return false + end - return - x >= 0 and - y >= 0 and - z < 0 + local d = uv:len() + local r = vw:len() / d + local t = uw:len() / d + + return r + t <= 1 end -- point is a vec3 @@ -98,7 +103,6 @@ end -- triangle[1] is a vec3 -- triangle[2] is a vec3 -- triangle[3] is a vec3 -local h, s, q, e1, e2 = vec3(), vec3(), vec3(), vec3(), vec3() function intersect.ray_triangle(ray, triangle) e1:sub(triangle[2], triangle[1]) e2:sub(triangle[3], triangle[1]) @@ -135,8 +139,8 @@ function intersect.ray_triangle(ray, triangle) -- return position of intersection if t >= DBL_EPSILON then local out = vec3() - :mul(ray.direction, t) - :add(ray.position, out) + out:mul(ray.direction, t) + out:add(ray.position, out) return out end @@ -174,18 +178,18 @@ function intersect.ray_sphere(ray, sphere) t = t < 0 and 0 or t local out = vec3() - :add(ray.position, ray.direction) - :mul(out, t) + out:mul(ray.direction, t) + out:add(out, ray.position) -- Return collision point and distance from ray origin return out, t end +-- http://gamedev.stackexchange.com/a/18459 -- ray.position is a vec3 -- ray.direction is a vec3 -- aabb.min is a vec3 -- aabb.max is a vec3 -local dir, dirfrac = vec3(), vec3() function intersect.ray_aabb(ray, aabb) dir:normalize(ray.direction) dirfrac.x = 1 / dir.x @@ -212,8 +216,12 @@ function intersect.ray_aabb(ray, aabb) return false end - -- return position of intersection - return tmin + local out = vec3() + out:mul(ray.direction, tmin) + out:add(out, ray.position) + + -- Return collision point and distance from ray origin + return out, tmin end -- https://www.cs.princeton.edu/courses/archive/fall00/cs426/lectures/raycast/sld017.htm @@ -222,23 +230,23 @@ end -- plane.position is a vec3 -- plane.normal is a vec3 function intersect.ray_plane(ray, plane) - local d = ray.position:dist(plane.position) local r = ray.direction:dot(plane.normal) -- ray does not intersect plane - if r <= 0 then + if r >= 0 then return false end -- distance of direction - local t = -(ray.position:dot(plane.normal) + d) / r + local d = ray.position:dist(plane.position) + local t = -(ray.position:dot(plane.normal) + d) / r local out = vec3() - :mul(ray.direction, t) - :add(ray.position, out) + out:mul(ray.direction, t) + out:add(out, ray.position) - -- return position of intersection + -- Return collision point and distance from ray origin if out:dot(plane.normal) + d < DBL_EPSILON then - return out + return out, t end -- ray does not intersect plane @@ -250,7 +258,6 @@ end -- a[2] is a vec3 -- b[1] is a vec3 -- b[2] is a vec3 -local p13, p43, p21, out1, out2 = vec3(), vec3(), vec3(), vec3(), vec3() function intersect.line_line(a, b) -- new points p13:sub(a[1], b[1]) @@ -277,13 +284,16 @@ function intersect.line_line(a, b) local numer = d1343 * d4321 - d1321 * d4343 local mua = numer / denom - local mub = (d1343 + d4321 * (mua)) / d4343 + local mub = (d1343 + d4321 * mua) / d4343 -- return positions of intersection on each line - out1:mul(mua, p21) - out1:add(a[1], out1) - out2:mul(mub, p43) - out2:add(b[1], out2) + local out1 = vec3() + out1:mul(p21, mua) + out1:add(out1, a[1]) + + local out2 = vec3() + out2:mul(p43, mub) + out2:add(out2, b[1]) return out1, out2 end @@ -422,27 +432,27 @@ function intersect.aabb_obb(aabb, obb) end end +-- http://stackoverflow.com/a/4579069/1190664 -- aabb.min is a vec3 -- aabb.max is a vec3 -- sphere.position is a vec3 -- sphere.radius is a number -local axes = { "x", "y", "z" } -function intersect.aabb_sphere(aabb, sphere) -- { position, radius } - local dmin = 0 +function intersect.aabb_sphere(aabb, sphere) + local dist2 = sphere.radius ^ 2 for _, axis in ipairs(axes) do - local pos = sphere.position[axis] - local min = box.min[axis] - local max = box.max[axis] + local pos = sphere.position[axis] + local amin = aabb.min[axis] + local amax = aabb.max[axis] - if pos < min then - dmin = dmin + (pos - min) ^ 2 - elseif pos > max then - dmin = dmin + (pos - max) ^ 2 + if pos < amin then + dist2 = dist2 - (pos - amin) ^ 2 + elseif pos > amax then + dist2 = dist2 - (pos - amax) ^ 2 end end - return dmin <= radius ^ 2 + return dist2 > 0 end -- aabb.min is a vec3 @@ -505,8 +515,12 @@ end -- inner.max is a vec3 function intersect.encapsulate_aabb(outer, inner) return - outer.min <= inner.min and - outer.max >= inner.max + outer.min.x <= inner.min.x and + outer.max.x >= inner.max.x and + outer.min.y <= inner.min.y and + outer.max.y >= inner.max.y and + outer.min.z <= inner.min.z and + outer.max.z >= inner.max.z end -- a.position is a vec3 diff --git a/spec/intersect_spec.lua b/spec/intersect_spec.lua index 2598364..06bab87 100644 --- a/spec/intersect_spec.lua +++ b/spec/intersect_spec.lua @@ -1,24 +1,237 @@ local intersect = require "modules.intersect" +local vec3 = require "modules.vec3" describe("intersect:", function() -end) + it("intersects a point with a triangle", function() + local a = vec3() + local b = vec3(0, 0, 5) + local c = { + vec3(-1, -1, 0), + vec3( 1, -1, 0), + vec3( 0.5, 1, 0) + } + assert.is_true(intersect.point_triangle(a, c)) + assert.is_not_true(intersect.point_triangle(b, c)) + end) ---[[ -point_triangle(point, triangle) -point_aabb(point, aabb) -point_frustum(point, frustum) -ray_triangle(ray, triangle) -ray_sphere(ray, sphere) -ray_aabb(ray, aabb) -ray_plane(ray, plane) -line_line(a, b) -segment_segment(a, b) -aabb_aabb(a, b) -aabb_obb(aabb, obb) -aabb_sphere(aabb, sphere) -- { position, radius } -aabb_frustum(aabb, frustum) -encapsulate_aabb(outer, inner) -circle_circle(a, b) -sphere_sphere(a, b) -sphere_frustum(sphere, frustum) ---]] \ No newline at end of file + it("intersects a point with an aabb", function() + local a = vec3() + local b = vec3(0, 0, 5) + local c = { + min = vec3(-1), + max = vec3( 1) + } + assert.is_true(intersect.point_aabb(a, c)) + assert.is_not_true(intersect.point_aabb(b, c)) + end) + --[[ + it("intersects a point with a frustum", function() + + end) + --]] + it("intersects a ray with a triangle", function() + local a = { + position = vec3(0.5, 0.5, -1), + direction = vec3(0, 0, 1) + } + local b = { + position = vec3(0.5, 0.5, -1), + direction = vec3(0, 0, -1) + } + local c = { + vec3(-1, -1, 0), + vec3( 1, -1, 0), + vec3( 0.5, 1, 0) + } + assert.is_true(vec3.is_vec3(intersect.ray_triangle(a, c))) + assert.is_not_true(intersect.ray_triangle(b, c)) + end) + + it("intersects a ray with a sphere", function() + local a = { + position = vec3(0, 0, -2), + direction = vec3(0, 0, 1) + } + local b = { + position = vec3(0, 0, -2), + direction = vec3(0, 0, -1) + } + local c = { + position = vec3(), + radius = 1 + } + + local w, x = intersect.ray_sphere(a, c) + local y, z = intersect.ray_sphere(b, c) + assert.is_true(vec3.is_vec3(w)) + assert.is_not_true(y) + end) + + it("intersects a ray with an aabb", function() + local a = { + position = vec3(0, 0, -2), + direction = vec3(0, 0, 1) + } + local b = { + position = vec3(0, 0, -2), + direction = vec3(0, 0, -1) + } + local c = { + min = vec3(-1), + max = vec3( 1) + } + + local w, x = intersect.ray_aabb(a, c) + local y, z = intersect.ray_aabb(b, c) + assert.is_true(vec3.is_vec3(w)) + assert.is_not_true(y) + end) + --[[ THIS IS BROKEN, RETURNING INCORRECT VALUES + it("intersects a ray with a plane", function() + local a = { + position = vec3(0, 0, 1), + direction = vec3(0, 0, -1) + } + local b = { + position = vec3(0, 0, 1), + direction = vec3(0, 0, 1) + } + local c = { + position = vec3(), + normal = vec3(0, 0, 1) + } + + local w, x = intersect.ray_plane(a, c) + local y, z = intersect.ray_plane(b, c) + assert.is_true(vec3.is_vec3(w)) + assert.is_not_true(y) + end) + --]] + --[[ THIS IS ALSO BROKEN GOD DAMMIT + it("intersects a line with a line", function() + local a = { + vec3(0, 0, -1), + vec3(0, 0, 1) + } + local b = { + vec3(0, 0, -1), + vec3(0, 1, -1) + } + local c = { + vec3(-1, 0, 0), + vec3( 1, 0, 0) + } + + local w, x = intersect.line_line(a, c) + local y, z = intersect.line_line(b, c) + assert.is_true(vec3.is_vec3(w)) + assert.is_not_true(y) + end) + + it("intersects a segment with a segment", function() + + end) + --]] + it("intersects an aabb with an aabb", function() + local a = { + min = vec3(-1), + max = vec3( 1) + } + local b = { + min = vec3(-5), + max = vec3(-3) + } + local c = { + min = vec3(), + max = vec3(2) + } + assert.is_true(intersect.aabb_aabb(a, c)) + assert.is_not_true(intersect.aabb_aabb(b, c)) + end) + --[[ + it("intersects an aabb with an obb", function() + + end) + --]] + it("intersects an aabb with a sphere", function() + local a = { + min = vec3(-1), + max = vec3( 1) + } + local b = { + min = vec3(-5), + max = vec3(-3) + } + local c = { + position = vec3(0, 0, 3), + radius = 3 + } + assert.is_true(intersect.aabb_sphere(a, c)) + assert.is_not_true(intersect.aabb_sphere(b, c)) + end) + --[[ + it("intersects an aabb with a frustum", function() + + end) + --]] + it("encapsulates an aabb", function() + local a = { + min = vec3(-1), + max = vec3( 1) + } + local b = { + min = vec3(-1.5), + max = vec3( 1.5) + } + local c = { + min = vec3(-0.5), + max = vec3( 0.5) + } + local d = { + min = vec3(-1), + max = vec3( 1) + } + assert.is_true(intersect.encapsulate_aabb(a, d)) + assert.is_true(intersect.encapsulate_aabb(b, d)) + assert.is_not_true(intersect.encapsulate_aabb(c, d)) + end) + + it("intersects a circle with a circle", function() + local a = { + position = vec3(0, 0, 6), + radius = 3 + } + local b = { + position = vec3(0, 0, 7), + radius = 3 + } + local c = { + position = vec3(), + radius = 3 + } + assert.is_true(intersect.circle_circle(a, c)) + assert.is_not_true(intersect.circle_circle(b, c)) + end) + + it("intersects a sphere with a sphere", function() + local a = { + position = vec3(0, 0, 6), + radius = 3 + } + local b = { + position = vec3(0, 0, 7), + radius = 3 + } + local c = { + position = vec3(), + radius = 3 + } + assert.is_true(intersect.sphere_sphere(a, c)) + assert.is_not_true(intersect.sphere_sphere(b, c)) + end) + --[[ + it("intersects a sphere with a frustum", function() + + end) + --]] +end)