~nch/gamelog

6a9607e8652255d444bf539a52a2e6567e8306ae — nc 4 months ago
```more debug draw, and first (untested) implementation of collision detection
```
```1 files changed, 169 insertions(+), 18 deletions(-)

M df.nim
```
`M df.nim => df.nim +169 -18`
```@@ 11,6 11,8 @@ import macros
import math
import complex
+import options
+import bitops

const max_entities = 1024;

@@ 45,6 47,61 @@ when isMainModule:
assert(where(@[(1, 2), (1, 2), (2, 3)], (1, @n)) == @[(1, 2), (1, 2)])

+## math
+
+type
+    Plane = tuple[pos: Vec3f, normal: Vec3f]
+    Ellipse = tuple[pos: Vec3f, radius: Vec3f]
+    Triangle = array[3, Vec3f]
+
+proc is_facing(p: Plane, dir: Vec3f): bool =
+    return p.normal.dot(dir) <= 0
+
+proc point_distance(p: Plane, point: Vec3f): float =
+    let v = point - p.pos
+    return v.dot(p.normal)
+
+proc triangle_plane(t: Triangle): Plane =
+    let n: Vec3f = (t[1] - t[0]).cross(t[2] - t[0]).normalize()
+    return (pos: t[0], normal: n).Plane
+
+proc get_lowest_root(a, b, c, max_r: float32): Option[float32] =
+    let det = b * b - 4 * a * c
+    if det < 0: return none(float32)
+
+    let sqrt_d = sqrt(det)
+    var r1 = (-b - sqrt_d) / (2 * a)
+    var r2 = (-b + sqrt_d) / (2 * a)
+
+    if r1 > r2: swap(r1, r2)
+    if r1 > 0 and r1 < max_r:
+        return some(r1)
+    if r2 > 0 and r2 < max_r:
+        return some(r2)
+
+
+
+proc in_triangle(p: Vec3f, t: Triangle): bool =
+    let e10 = t[1] - t[0]
+    let e20 = t[2] - t[0]
+
+    let a = e10.dot(e10)
+    let b = e10.dot(e20)
+    let c = e20.dot(e20)
+    let ac_bb = (a * c) - (b * b)
+    let vp = vec3f(p.x - t[0].x, p.y - t[0].y, p.z - t[0].z)
+
+    let d = vp.dot(e10)
+    let e = vp.dot(e20)
+
+    let x = (d * c) - (e * b)
+    let y = (e * a) - (d * b)
+    let z = x + y - ac_bb
+
+    return bitand(bitand(z.uint32, bitnot(bitor(x.uint32, y.uint32))), 0x80000000.uint32) != 0
+
+
+
proc make_program(shaders: varargs[(GLenum, string)]): uint32 =
let program = gl_create_program()

@@ 192,24 249,29 @@ proc debug_edge(vs: varargs[Vec3f]) =

-proc debug_ico_sphere(pos: Vec3f, radius: float) =
-    # constructing an icosahedron:
-    # https://www.math.ubc.ca/~cass/courses/m308-03b/projects-03b/keating/projectweppage2.htm
-    let z = 2 / 4
-    for i in 1..5:
-        let ang = exp(complex(0.float, 1.float) * (2 * PI / 5))
-        let half_ang = exp(complex(0.float, 1.float) * (PI / 5))
-        let u = complex(0.0, 1.0) * pow(ang, complex(i.float, 0.0))
-        let v = u * ang
-        let T = (v: Vec3f) => (M * vec4f(v.x, v.y, v.z, 1.float32)).xyz
-        # TODO: use matrix instead...
-        debug_edge(T(vec3f(u.re, u.im, z)), T(vec3f(0, 0, 0))) # top cap
-        debug_edge(T(vec3f(u.re, u.im, z)), T(vec3f(v.re, v.im, z))) # top ring
-        debug_edge(T(vec3f(u.re, u.im, z)), T(vec3f((u * half_ang).re, (u * half_ang).im, 2 - z))) # connect top/bottom ring A
-        debug_edge(T(vec3f(v.re, v.im, z)), T(vec3f((u * half_ang).re, (u * half_ang).im, 2 - z))) # connect top/bottom ring B
-        debug_edge(T(vec3f((v * half_ang).re, (v * half_ang).im, 2 - z)), T(vec3f((u * half_ang).re, (u * half_ang).im, 2 - z)))
-        debug_edge(T(vec3f((v * half_ang).re, (v * half_ang).im, 2 - z)), T(vec3f(0, 0, 2))) # bottom cap
+proc debug_sphere(pos: Vec3f, radius: float32, np: uint = 8, nm: uint = 16) =
+    for j in 0..< np:
+        let pp = PI * (j).float / np.float
+        let p = PI * (j + 1).float / np.float
+        let
+            spp = sin(pp)
+            cpp = cos(pp)
+        let
+            sp = sin(p)
+            cp = cos(p)
+        for i in 1.. nm:
+            let pm = 2.0 * PI * (i - 1).float / nm.float
+            let m = 2.0 * PI * i.float / nm.float
+            # ring
+            debug_edge(
+                pos + radius * vec3f(sp * cos(pm), cp, sp * sin(pm)),
+                pos + radius * vec3f(sp * cos(m), cp, sp * sin(m)))
+            # connecting edge
+            debug_edge(
+                pos + radius * vec3f(spp * cos(m), cpp, spp * sin(m)),
+                pos + radius * vec3f(sp * cos(m), cp, sp * sin(m)))
+
+proc debug_sphere(e: Ellipse) = debug_sphere(e.pos, e.radius.x)

proc debug_AABB(a, b: Vec3f) =
debug_edge(a, vec3f(a.x, b.y, a.z), vec3f(b.x, b.y, a.z), vec3f(b.x, a.y, a.z), a)

@@ 220,6 282,87 @@ proc debug_AABB(a, b: Vec3f) =
debug_edge(vec3f(b.x, b.y, a.z), vec3f(b.x, b.y, b.z))
debug_edge(vec3f(a.x, b.y, a.z), vec3f(a.x, b.y, b.z))

+proc check_collision(e: Ellipse, vel: Vec3f, tri: Triangle): Option[Vec3f] =
+    let tp = triangle_plane(tri)
+    if not tp.is_facing(-vel):
+        return
+
+    let dist = tp.point_distance(e.pos)
+    let norm_dot_vel = tp.normal.dot(vel)
+    var embedded = false
+    var t0, t1: float # time of start/stop intersection
+
+    if norm_dot_vel == 0: # parallel
+        if abs(dist) >= 1:
+            return
+        else:
+            embedded = true
+            t0 = 0
+            t1 = 1
+    else: # not parallel
+        t0 = (-1 - dist) / norm_dot_vel
+        t1 = ( 1 - dist) / norm_dot_vel
+
+        if t0 > t1:
+            swap(t0, t1)
+
+        if t0 > 1 or t1 < 0:
+            return
+
+        t0 = t0.clamp(0, 1)
+        t1 = t1.clamp(0, 1)
+
+    var found_col = false
+    var t = 1.0
+    var col_point: Vec3f
+
+    if not embedded:
+        let plane_intersection = (e.pos - tp.normal) + (t0.float32 * vel)
+        if plane_intersection.in_triangle(tri):
+            found_col = true
+            t = t0
+            col_point = plane_intersection
+
+    let vel_length2 = vel.length2()
+
+        # check points
+        for p in tri:
+            {.unroll: 3.}
+            let a = vel_length2
+            let b = 2.float32 * vel.dot(e.pos - p)
+            let c = (p - e.pos).length2() - 1.float32
+            let r = get_lowest_root(a, b, c, t)
+            if r.is_some():
+                t = r.get()
+                found_col = true
+                col_point = p
+
+
+        # check edges
+        for pair in [(tri[0], tri[1]), (tri[1], tri[2]), (tri[2], tri[0])]:
+            {.unroll: 3.}
+            let edge = pair[1] - pair[0]
+            let pos_to_vert = pair[0] - e.pos
+            let edge_len2 = edge.length2()
+            let edge_dot_vel = edge.dot(vel)
+            let edge_dot_pos_to_vert = edge.dot(pos_to_vert)
+
+            let a = edge_len2 * -vel_length2 + edge_dot_vel * edge_dot_vel
+            let b = edge_len2 * (2 * vel.dot(pos_to_vert)) - 2 * edge_dot_vel * edge_dot_pos_to_vert
+            let c = edge_len2 * (1 - pos_to_vert.length2()) + edge_dot_pos_to_vert * edge_dot_pos_to_vert
+
+            let r = get_lowest_root(a, b, c, t)
+            if r.is_some():
+                let f = (edge_dot_vel * r.get() - edge_dot_pos_to_vert) / edge_len2
+                if f >= 0 and f <= 1:
+                    t = r.get()
+                    found_col = true
+                    col_point = pair[0] + f * edge
+
+    if found_col:
+        return some(col_point)
+
var cam_pos = vec3f(0, 1, 2)
var cam_look = vec3f(0, 0, 1)
let cam_up = vec3f(0, 1, 0)

@@ 395,6 538,8 @@ gl_clear_depth(1)
gl_enable(GL_DEPTH_TEST)
gl_depth_func(GL_LEQUAL)

+var ell = (pos: vec3f(0, 0, 0), radius: vec3f(1, 1, 1)).Ellipse
+
let proj: Mat4[float32] = perspective[float32](Pi / 4, width.float32 / height.float32, 0.1, 100)

let use_progs = [prog, wireframe_prog]

@@ 413,6 558,11 @@ while true:
if w.is_key_down(Key.keyS): cam_pos -= move_speed.float32 * cam_look
if w.is_key_down(Key.keyA): cam_pos -= move_speed.float32 * cam_look.cross(cam_up).normalize
if w.is_key_down(Key.keyD): cam_pos += move_speed.float32 * cam_look.cross(cam_up).normalize
+    ####
+    if w.is_key_down(Key.keyI): ell.pos.y += 0.1
+    if w.is_key_down(Key.keyK): ell.pos.y -= 0.1
+    if w.is_key_down(Key.keyJ): ell.pos.x += 0.1
+    if w.is_key_down(Key.keyL): ell.pos.x -= 0.1

let view: Mat4[float32] = look_at(cam_pos, cam_pos + cam_look, cam_up)
gl_clear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT) # Clear color and depth buffers

@@ 436,6 586,7 @@ while true:
#debug_edge(vec3f(0, 0, 0), vec3f(0.1,0.1,0.1))
#debug_edge(vec3f(0.1,0.1,0.1), vec3f(-0.3,0.1,0.1))
debug_AABB(vec3f(0, 0, 2), vec3f(1, 1, 3))
+    debug_sphere(ell)

block: # draw debug
gl_use_program(debug_prog)

```