~nch/gamelog

6a9607e8652255d444bf539a52a2e6567e8306ae — nc 4 months ago d69624e
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 stb_image/read as stbi
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()
    var shader_ids: seq[uint32] = @[]


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


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 M = mat4f(1).translate(pos.x, pos.y, pos.z).scale(radius, radius, radius).rotate(PI/2, vec3f(1.0, 0.0, 0.0))
        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()

    if not found_col:
        # 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)