~nch/gamelog

75432213d3972f26c03ef636da68ed482f0ac908 — nc 3 months ago fffd102
refactoring debug code out a bit
4 files changed, 363 insertions(+), 337 deletions(-)

A debug_draw.nim
M df.nim
A draw_util.nim
A trace.sh
A debug_draw.nim => debug_draw.nim +95 -0
@@ 0,0 1,95 @@
import opengl
import glm
import sequtils
import draw_util

type DebugCtx = tuple
    mesh: seq[Vec3f]
    buffer: GLuint
    buffer_vao: GLuint
    prog: GLuint

proc init_debug*(): DebugCtx =
    var c: DebugCtx
    c.mesh = @[]
    c.prog = make_program((GL_VERTEX_SHADER, """
#version 450 core
    layout(location = 0) in vec3 vpos;
    uniform mat4 MVP;
    void main() {
       gl_Position = MVP * vec4(vpos, 1.0);
    }
    """),
    (GL_FRAGMENT_SHADER, """
#version 450 core

    out vec3 color;
    uniform sampler2D diffuse;

    void main() {
        color = vec3(1, 1, 0);
    }
    """))

    # setup debug draw buffer
    gl_create_vertex_arrays(1, c.buffer_vao.addr)
    gl_create_buffers(1, c.buffer.addr)
    gl_enable_vertex_array_attrib(c.buffer_vao, 0)
    gl_vertex_array_vertex_buffer(c.buffer_vao, 0, c.buffer, 0, Vec3f.sizeof.GLsizei)
    gl_vertex_array_attrib_format(c.buffer_vao, 0, 3, cGL_FLOAT, false, 0) # pos
    gl_named_buffer_data(c.buffer, Vec3f.sizeof * 1024, nil, GL_DYNAMIC_DRAW)

    return c

proc clear*(c: var DebugCtx) =
    c.mesh.set_len(0)

proc draw*(c: DebugCtx, mvp: var Mat4f) =
    gl_use_program(c.prog)
    let p = gl_map_named_buffer(c.buffer, GL_WRITE_ONLY)
    copy_mem(p, c.mesh[0].unsafeAddr, Vec3f.sizeof * len(c.mesh))
    discard gl_unmap_named_buffer(c.buffer)

    gl_bind_vertex_array(c.buffer_vao)
    gl_bind_buffer(GL_ARRAY_BUFFER, c.buffer)
    gl_uniform_matrix4fv(gl_get_uniform_location(c.prog, "MVP"), 1, false, mvp.caddr)
    gl_point_size(5)
    gl_draw_arrays(GL_LINES, 0, (len(c.mesh)).GLsizei)
    gl_draw_arrays(GL_POINTS, 0, (len(c.mesh)).GLsizei)

proc edge*(c: var DebugCtx, vs: varargs[Vec3f]) =
    for (a, b) in zip(vs, vs[1..^1]):
        c.mesh.add(a)
        c.mesh.add(b)

proc sphere*(c: var DebugCtx, 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
            c.edge(
                pos + radius * vec3f(sp * cos(pm), cp, sp * sin(pm)),
                pos + radius * vec3f(sp * cos(m), cp, sp * sin(m)))
            # connecting edge
            c.edge(
                pos + radius * vec3f(spp * cos(m), cpp, spp * sin(m)),
                pos + radius * vec3f(sp * cos(m), cp, sp * sin(m)))

proc AABB*(c: var DebugCtx, a, b: Vec3f) =
    c.edge(a, vec3f(a.x, b.y, a.z), vec3f(b.x, b.y, a.z), vec3f(b.x, a.y, a.z), a)
    c.edge(vec3f(a.x, a.y, b.z), vec3f(a.x, b.y, b.z), vec3f(b.x, b.y, b.z), vec3f(b.x, a.y, b.z), vec3f(a.x, a.y, b.z))

    c.edge(vec3f(a.x, a.y, a.z), vec3f(a.x, a.y, b.z))
    c.edge(vec3f(b.x, a.y, a.z), vec3f(b.x, a.y, b.z))
    c.edge(vec3f(b.x, b.y, a.z), vec3f(b.x, b.y, b.z))
    c.edge(vec3f(a.x, b.y, a.z), vec3f(a.x, b.y, b.z))


M df.nim => df.nim +195 -337
@@ 8,11 8,12 @@ import strutils
import sugar
import sets
import macros
import stb_image/read as stbi
import math
import complex
import options
import bitops
import draw_util
import debug_draw

const max_entities = 1024;



@@ 50,9 51,9 @@ when isMainModule:
## math

type
    Plane = tuple[pos: Vec3f, normal: Vec3f]
    Ellipse = tuple[pos: Vec3f, radius: Vec3f]
    Triangle = array[3, Vec3f]
    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


@@ 99,25 100,6 @@ proc in_triangle(p: Vec3f, t: Triangle): bool =
    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] = @[]
    for (ty, source) in shaders:
        let shader = gl_create_shader(ty)
        let a = alloc_c_string_array([source])
        defer: dealloc_c_string_array(a)
        gl_shader_source(shader, 1, a, nil)
        gl_compile_shader(shader)
        gl_attach_shader(program, shader) # now attach to the main program
        shader_ids.add(shader)
    gl_link_program(program)
    for s in shader_ids:
        gl_delete_shader(s)
    return program

proc parse_obj(path: string): (seq[float32], seq[uint16]) =
    var
        vertices = newSeq[Vec3f]()


@@ 185,104 167,7 @@ proc parse_obj(path: string): (seq[float32], seq[uint16]) =
        r.add(d)
    return (r, element_indices)

proc make_mesh(vertex_data: seq[float32], face_indices: seq[uint16]): uint =
    var vao_id: GLuint
    gl_gen_vertex_arrays(1, vao_id.addr)
    assert vao_id != 0
    gl_bind_vertex_array(vao_id)
    var vbo_id: GLuint
    gl_gen_buffers(1, vbo_id.addr)

    gl_bind_buffer(GL_ARRAY_BUFFER, vbo_id)
    gl_buffer_data(GL_ARRAY_BUFFER, vertex_data.len * float32.sizeof, vertex_data[0].unsafeAddr, GL_STATIC_DRAW)

    gl_vertex_attrib_pointer(0, 3, cGL_FLOAT, false, 8 * float32.sizeof, cast[ptr int](0)) # pos
    gl_enable_vertex_attrib_array(0)
    gl_vertex_attrib_pointer(1, 3, cGL_FLOAT, false, 8 * float32.sizeof, cast[ptr int](3 * float32.sizeof)) # normal
    gl_enable_vertex_attrib_array(1)
    gl_vertex_attrib_pointer(2, 2, cGL_FLOAT, false, 8 * float32.sizeof, cast[ptr int](6 * float32.sizeof)) # uv
    gl_enable_vertex_attrib_array(2)

    var ebo_id: GLuint
    gl_gen_buffers(1, ebo_id.addr)

    gl_bind_buffer(GL_ELEMENT_ARRAY_BUFFER, ebo_id)
    gl_buffer_data(GL_ELEMENT_ARRAY_BUFFER, face_indices.len * uint16.sizeof, face_indices[0].unsafeAddr, GL_STATIC_DRAW)

    gl_bind_vertex_array(0)
    return vao_id

proc load_mesh(path: string): uint16 =
    let (vert_data, face_indices) = parse_obj(path)
    let vao = make_mesh(vert_data, face_indices)
    meshes.add((filepath: path, vao: vao.uint32, nfaces: face_indices.len.uint16, nverts: (face_indices.len * 3).uint16).Mesh)
    return (meshes.len - 1).uint16

proc load_texture(file: string): uint64 =
    # TODO: check if the file existed first
    #let q = textures.where((file: file, id: _,
    var tex_id: GLuint
    var w, h, nchannels: int
    let data = stbi.load(file, w, h, nchannels, stbi.Default)

    gl_create_textures(GL_TEXTURE_2D, 1, tex_id.addr)
    gl_texture_parameteri(tex_id, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)
    gl_texture_parameteri(tex_id, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)
    gl_texture_parameteri(tex_id, GL_TEXTURE_MIN_FILTER, GL_LINEAR)
    gl_texture_parameteri(tex_id, GL_TEXTURE_MAG_FILTER, GL_LINEAR)

    let num_mipmaps = (log2(float(max(w, h) + 1))).GLsizei;
    gl_texture_storage_2d(tex_id, num_mipmaps, GL_RGB8, w.GLsizei, h.GLsizei);
    gl_texture_subimage_2d(tex_id, 0, 0, 0, w.GLsizei, h.GLsizei, GL_RGBA, GL_UNSIGNED_BYTE, data[0].unsafeAddr)
    gl_generate_texture_mipmap(tex_id)

    let handle = gl_get_texture_handle_arb(tex_id)
    gl_make_texture_handle_resident_arb(handle)
    textures.insert((filepath: file, handle: handle, width: w.uint16, height: h.uint16).Texture)
    return handle

var debug_mesh = newSeq[Vec3f]()

proc debug_edge(vs: varargs[Vec3f]) =
    for (a, b) in zip(vs, vs[1..^1]):
        debug_mesh.add(a)
        debug_mesh.add(b)


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)
    debug_edge(vec3f(a.x, a.y, b.z), vec3f(a.x, b.y, b.z), vec3f(b.x, b.y, b.z), vec3f(b.x, a.y, b.z), vec3f(a.x, a.y, b.z))

    debug_edge(vec3f(a.x, a.y, a.z), vec3f(a.x, a.y, b.z))
    debug_edge(vec3f(b.x, a.y, a.z), vec3f(b.x, a.y, b.z))
    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] =
proc check_collision*(e: Ellipse, vel: Vec3f, tri: Triangle): Option[Vec3f] =
    let tp = triangle_plane(tri)
    if not tp.is_facing(-vel):
        return


@@ 363,242 248,215 @@ proc check_collision(e: Ellipse, vel: Vec3f, tri: Triangle): Option[Vec3f] =
    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)
var cam_yaw: float = radians(0.0)
var cam_pitch: float = radians(90.0)

let width = 800
let height = 600

var last_x = (width/2).float64
var last_y = (height/2).float64

proc cursor_pos_callback(w: Window, pos: tuple[x, y: float64]) =
    let dx = last_x - pos.x
    let dy = last_y - pos.y
    last_x = pos.x
    last_y = pos.y
    cam_pitch += (dy * 0.1)
    cam_yaw -= (dx * 0.1)
    cam_look.x = cos(radians(cam_pitch)) * cos(radians(cam_yaw))
    cam_look.y = sin(radians(cam_pitch))
    cam_look.z = cos(radians(cam_pitch)) * sin(radians(cam_yaw))
    cam_look = cam_look.normalize()
proc load_mesh(path: string): uint16 =
    let (vert_data, face_indices) = parse_obj(path)
    let vao = make_mesh(vert_data, face_indices)
    meshes.add((filepath: path, vao: vao.uint32, nfaces: face_indices.len.uint16, nverts: (face_indices.len * 3).uint16).Mesh)
    return (meshes.len - 1).uint16

###
if is_main_module:
    var cam_pos = vec3f(0, 1, 2)
    var cam_look = vec3f(0, 0, 1)
    let cam_up = vec3f(0, 1, 0)
    var cam_yaw: float = radians(0.0)
    var cam_pitch: float = radians(90.0)

glfw.initialize()
    let width = 800
    let height = 600

load_extensions()
    var last_x = (width/2).float64
    var last_y = (height/2).float64

var c = DefaultOpenglWindowConfig
c.title = "game"
var w = new_window(c)
w.cursor_position_cb = cursor_pos_callback
w.cursor_mode = cm_disabled
    proc cursor_pos_callback(w: Window, pos: tuple[x, y: float64]) =
        let dx = last_x - pos.x
        let dy = last_y - pos.y
        last_x = pos.x
        last_y = pos.y
        cam_pitch += (dy * 0.1)
        cam_yaw -= (dx * 0.1)
        cam_look.x = cos(radians(cam_pitch)) * cos(radians(cam_yaw))
        cam_look.y = sin(radians(cam_pitch))
        cam_look.z = cos(radians(cam_pitch)) * sin(radians(cam_yaw))
        cam_look = cam_look.normalize()

glfw.make_context_current(w)
    ###

var debug_buffer: GLuint
var debug_buffer_vao: GLuint
let debug_prog = make_program((GL_VERTEX_SHADER, """
#version 450 core
layout(location = 0) in vec3 vpos;
uniform mat4 MVP;
void main() {
   gl_Position = MVP * vec4(vpos, 1.0);
}
"""),
(GL_FRAGMENT_SHADER, """
#version 450 core
    glfw.initialize()

out vec3 color;
uniform sampler2D diffuse;
    load_extensions()

void main() {
    color = vec3(1, 1, 0);
}
"""))
    var c = DefaultOpenglWindowConfig
    c.title = "game"
    var w = new_window(c)
    w.cursor_position_cb = cursor_pos_callback
    w.cursor_mode = cm_disabled

block: # setup debug draw buffer
    gl_create_vertex_arrays(1, debug_buffer_vao.addr)
    gl_create_buffers(1, debug_buffer.addr)
    gl_enable_vertex_array_attrib(debug_buffer_vao, 0)
    gl_vertex_array_vertex_buffer(debug_buffer_vao, 0, debug_buffer, 0, Vec3f.sizeof.GLsizei)
    gl_vertex_array_attrib_format(debug_buffer_vao, 0, 3, cGL_FLOAT, false, 0) # pos
    gl_named_buffer_data(debug_buffer, Vec3f.sizeof * 1024, nil, GL_DYNAMIC_DRAW)
    glfw.make_context_current(w)
    var debug = init_debug()

###
    ###

let prog = make_program((GL_VERTEX_SHADER, """
    let prog = make_program((GL_VERTEX_SHADER, """
#version 450 core
#extension GL_ARB_bindless_texture : require
layout(location = 0) in vec3 vpos;
layout(location = 1) in vec3 norm;
layout(location = 2) in vec2 iuv;
uniform mat4 M;
uniform mat4 MVP;

uniform mat4 lightSpaceMatrix;

out vec3 normal;
out vec3 worldPos;
out vec4 fragPosLightSpace;
out vec2 uv;
void main() {
    gl_Position = MVP * vec4(vpos, 1.0);
    uv = iuv;
    worldPos = (M * vec4(vpos, 1.0)).xyz;
    fragPosLightSpace = lightSpaceMatrix * vec4(worldPos, 1.0);
    normal = norm;
}
"""),
(GL_FRAGMENT_SHADER, """
    layout(location = 0) in vec3 vpos;
    layout(location = 1) in vec3 norm;
    layout(location = 2) in vec2 iuv;
    uniform mat4 M;
    uniform mat4 MVP;

    uniform mat4 lightSpaceMatrix;

    out vec3 normal;
    out vec3 worldPos;
    out vec4 fragPosLightSpace;
    out vec2 uv;
    void main() {
        gl_Position = MVP * vec4(vpos, 1.0);
        uv = iuv;
        worldPos = (M * vec4(vpos, 1.0)).xyz;
        fragPosLightSpace = lightSpaceMatrix * vec4(worldPos, 1.0);
        normal = norm;
    }
    """),
    (GL_FRAGMENT_SHADER, """
#version 450 core
#extension GL_ARB_bindless_texture : require
in vec3 normal;
in vec3 worldPos;
in vec4 fragPosLightSpace;
in vec2 uv;
out vec3 color;
layout(bindless_sampler, location=1) uniform sampler2D diffuse;

void main() {
    vec3 lightPos = vec3(4, 4, 4);
    vec3 n = normalize(normal);
    vec3 lightDir = normalize(lightPos - worldPos);
    float diff = max(dot(n, lightDir), 0.0);
    color = diff * texture(diffuse, uv).xyz;
}
"""
))

let wireframe_prog = make_program((GL_VERTEX_SHADER, """
    in vec3 normal;
    in vec3 worldPos;
    in vec4 fragPosLightSpace;
    in vec2 uv;
    out vec3 color;
    layout(bindless_sampler, location=1) uniform sampler2D diffuse;

    void main() {
        vec3 lightPos = vec3(4, 4, 4);
        vec3 n = normalize(normal);
        vec3 lightDir = normalize(lightPos - worldPos);
        float diff = max(dot(n, lightDir), 0.0);
        color = diff * texture(diffuse, uv).xyz;
    }
    """
    ))

    let wireframe_prog = make_program((GL_VERTEX_SHADER, """
#version 450 core
layout(location = 0) in vec3 vpos;
layout(location = 1) in vec3 norm;
layout(location = 2) in vec2 iuv;
uniform mat4 MVP;

void main() {
   gl_Position = MVP * vec4(vpos, 1.0);
}
"""),
(GL_GEOMETRY_SHADER, """
    layout(location = 0) in vec3 vpos;
    layout(location = 1) in vec3 norm;
    layout(location = 2) in vec2 iuv;
    uniform mat4 MVP;

    void main() {
       gl_Position = MVP * vec4(vpos, 1.0);
    }
    """),
    (GL_GEOMETRY_SHADER, """
#version 450

layout (triangles) in;
layout (line_strip /*for lines, use "points" for points*/, max_vertices=3) out;
    layout (triangles) in;
    layout (line_strip, max_vertices=3) out;

void main(void)
{
    int i;
    for (i = 0; i < gl_in.length(); i++)
    void main(void)
    {
        gl_Position = gl_in[i].gl_Position; //Pass through
        EmitVertex();
        int i;
        for (i = 0; i < gl_in.length(); i++)
        {
            gl_Position = gl_in[i].gl_Position; //Pass through
            EmitVertex();
        }
        EndPrimitive();
    }
    EndPrimitive();
}
"""
),
(GL_FRAGMENT_SHADER, """
    """
    ),
    (GL_FRAGMENT_SHADER, """
#version 450 core

out vec3 color;
uniform sampler2D diffuse;

void main() {
    color = vec3(0, 1, 1);
}
"""
))

let player_mesh = load_mesh("ninja.obj")
drawables.add((mesh_id: player_mesh, pos: vec3f(0, 0, 0)).Drawable)
let ninja_tex = load_texture("ninja_mask.png")
let test_tex = load_texture("test.png")
texture_bindings.insert((player_mesh, ninja_tex, "diffuse").TextureBinding)

let terrain_id = load_mesh("terrain.obj")
drawables.add((mesh_id: terrain_id, pos: vec3f(0, 0, 0)).Drawable)
texture_bindings.insert((terrain_id, test_tex, "diffuse").TextureBinding)

gl_clear_color(0, 0, 0, 1)
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]
var use_prog_i = 0
var use_prog = use_progs[use_prog_i]

proc keyboard_callback(w: Window, key: Key, scancode: int32, action: KeyAction, modKeys: set[ModifierKey]) =
    if key == Key.keyV and action == KeyAction.kaDown:
        use_prog_i = (use_prog_i + 1) mod len(use_progs)
        use_prog = use_progs[use_prog_i]
w.key_cb = keyboard_callback

while true:
    let move_speed = if w.is_key_down(Key.keyLeftShift): 0.2 else: 0.05
    if w.is_key_down(Key.keyW): cam_pos += move_speed.float32 * cam_look
    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
    gl_use_program(use_prog)
    debug_mesh = @[]
    for id, d in drawables:
        let mesh = meshes[d.mesh_id]

        for i, b in texture_bindings.where((drawable_id: id.uint16, texture_id: _, texture_name: _)):
            gl_uniform_handle_ui64ARB(gl_get_uniform_location(use_prog, b.texture_name), b.texture_handle)

        var m = translate(mat4f(), d.pos) # model matrix
        var mvp: Mat4[float32] = proj * view * m

        gl_uniform_matrix4fv(gl_get_uniform_location(use_prog, "MVP"), 1, false, mvp.caddr)
        gl_uniform_matrix4fv(gl_get_uniform_location(use_prog, "M"), 1, false, m.caddr)

        gl_bind_vertex_array(mesh.vao)
        gl_draw_elements(GL_TRIANGLES, mesh.nfaces.GLsizei, cGL_UNSIGNED_SHORT, cast[ptr int](0))

    #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)
        let p = gl_map_named_buffer(debug_buffer, GL_WRITE_ONLY)
        copy_mem(p, debug_mesh[0].unsafeAddr, Vec3f.sizeof * len(debug_mesh))
        discard gl_unmap_named_buffer(debug_buffer)

        gl_bind_vertex_array(debug_buffer_vao)
        gl_bind_buffer(GL_ARRAY_BUFFER, debug_buffer)
        var mvp: Mat4[float32] = proj * view
        gl_uniform_matrix4fv(gl_get_uniform_location(debug_prog, "MVP"), 1, false, mvp.caddr)
        gl_point_size(5)
        gl_draw_arrays(GL_LINES, 0, (len(debug_mesh)).GLsizei)
        gl_draw_arrays(GL_POINTS, 0, (len(debug_mesh)).GLsizei)

    w.swap_buffers
    poll_events()
    if w.should_close == true or w.is_key_down(keyEscape):
        break

w.destroy()
glfw.terminate()
    out vec3 color;
    uniform sampler2D diffuse;

    void main() {
        color = vec3(0, 1, 1);
    }
    """
    ))

    let player_mesh = load_mesh("ninja.obj")
    drawables.add((mesh_id: player_mesh, pos: vec3f(0, 0, 0)).Drawable)

    let ninja_tex = load_texture("ninja_mask.png").Texture
    textures.insert(ninja_tex)
    let test_tex = load_texture("test.png").Texture
    textures.insert(test_tex)
    texture_bindings.insert((player_mesh, ninja_tex.handle, "diffuse").TextureBinding)

    let terrain_id = load_mesh("terrain.obj")
    drawables.add((mesh_id: terrain_id, pos: vec3f(0, 0, 0)).Drawable)
    texture_bindings.insert((terrain_id, test_tex.handle, "diffuse").TextureBinding)

    gl_clear_color(0, 0, 0, 1)
    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]
    var use_prog_i = 0
    var use_prog = use_progs[use_prog_i]

    proc keyboard_callback(w: Window, key: Key, scancode: int32, action: KeyAction, modKeys: set[ModifierKey]) =
        if key == Key.keyV and action == KeyAction.kaDown:
            use_prog_i = (use_prog_i + 1) mod len(use_progs)
            use_prog = use_progs[use_prog_i]
    w.key_cb = keyboard_callback

    while true:
        let move_speed = if w.is_key_down(Key.keyLeftShift): 0.2 else: 0.05
        if w.is_key_down(Key.keyW): cam_pos += move_speed.float32 * cam_look
        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
        gl_use_program(use_prog)

        debug.clear()

        for id, d in drawables:
            let mesh = meshes[d.mesh_id]

            for i, b in texture_bindings.where((drawable_id: id.uint16, texture_id: _, texture_name: _)):
                gl_uniform_handle_ui64ARB(gl_get_uniform_location(use_prog, b.texture_name), b.texture_handle)

            var m = translate(mat4f(), d.pos) # model matrix
            var mvp: Mat4[float32] = proj * view * m

            gl_uniform_matrix4fv(gl_get_uniform_location(use_prog, "MVP"), 1, false, mvp.caddr)
            gl_uniform_matrix4fv(gl_get_uniform_location(use_prog, "M"), 1, false, m.caddr)

            gl_bind_vertex_array(mesh.vao)
            gl_draw_elements(GL_TRIANGLES, mesh.nfaces.GLsizei, cGL_UNSIGNED_SHORT, cast[ptr int](0))

        #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.pos, ell.radius.x)

        var debug_mvp = proj * view
        debug.draw(debug_mvp)

        w.swap_buffers
        poll_events()
        if w.should_close == true or w.is_key_down(keyEscape):
            break

    w.destroy()
    glfw.terminate()

A draw_util.nim => draw_util.nim +69 -0
@@ 0,0 1,69 @@
import opengl
import stb_image/read as stbi
import math

proc make_program*(shaders: varargs[(GLenum, string)]): uint32 =
    let program = gl_create_program()
    var shader_ids: seq[uint32] = @[]
    for (ty, source) in shaders:
        let shader = gl_create_shader(ty)
        let a = alloc_c_string_array([source])
        defer: dealloc_c_string_array(a)
        gl_shader_source(shader, 1, a, nil)
        gl_compile_shader(shader)
        gl_attach_shader(program, shader) # now attach to the main program
        shader_ids.add(shader)
    gl_link_program(program)
    for s in shader_ids:
        gl_delete_shader(s)
    return program

proc make_mesh*(vertex_data: seq[float32], face_indices: seq[uint16]): uint =
    var vao_id: GLuint
    gl_gen_vertex_arrays(1, vao_id.addr)
    assert vao_id != 0
    gl_bind_vertex_array(vao_id)
    var vbo_id: GLuint
    gl_gen_buffers(1, vbo_id.addr)

    gl_bind_buffer(GL_ARRAY_BUFFER, vbo_id)
    gl_buffer_data(GL_ARRAY_BUFFER, vertex_data.len * float32.sizeof, vertex_data[0].unsafeAddr, GL_STATIC_DRAW)

    gl_vertex_attrib_pointer(0, 3, cGL_FLOAT, false, 8 * float32.sizeof, cast[ptr int](0)) # pos
    gl_enable_vertex_attrib_array(0)
    gl_vertex_attrib_pointer(1, 3, cGL_FLOAT, false, 8 * float32.sizeof, cast[ptr int](3 * float32.sizeof)) # normal
    gl_enable_vertex_attrib_array(1)
    gl_vertex_attrib_pointer(2, 2, cGL_FLOAT, false, 8 * float32.sizeof, cast[ptr int](6 * float32.sizeof)) # uv
    gl_enable_vertex_attrib_array(2)

    var ebo_id: GLuint
    gl_gen_buffers(1, ebo_id.addr)

    gl_bind_buffer(GL_ELEMENT_ARRAY_BUFFER, ebo_id)
    gl_buffer_data(GL_ELEMENT_ARRAY_BUFFER, face_indices.len * uint16.sizeof, face_indices[0].unsafeAddr, GL_STATIC_DRAW)

    gl_bind_vertex_array(0)
    return vao_id

proc load_texture*(file: string): tuple[filepath: string, handle: GLuint64, width: uint16, height: uint16] =
    # TODO: check if the file existed first
    #let q = textures.where((file: file, id: _,
    var tex_id: GLuint
    var w, h, nchannels: int
    let data = stbi.load(file, w, h, nchannels, stbi.Default)

    gl_create_textures(GL_TEXTURE_2D, 1, tex_id.addr)
    gl_texture_parameteri(tex_id, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)
    gl_texture_parameteri(tex_id, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)
    gl_texture_parameteri(tex_id, GL_TEXTURE_MIN_FILTER, GL_LINEAR)
    gl_texture_parameteri(tex_id, GL_TEXTURE_MAG_FILTER, GL_LINEAR)

    let num_mipmaps = (log2(float(max(w, h) + 1))).GLsizei;
    gl_texture_storage_2d(tex_id, num_mipmaps, GL_RGB8, w.GLsizei, h.GLsizei);
    gl_texture_subimage_2d(tex_id, 0, 0, 0, w.GLsizei, h.GLsizei, GL_RGBA, GL_UNSIGNED_BYTE, data[0].unsafeAddr)
    gl_generate_texture_mipmap(tex_id)

    let handle = gl_get_texture_handle_arb(tex_id)
    gl_make_texture_handle_resident_arb(handle)
    return (filepath: file, handle: handle, width: w.uint16, height: h.uint16)


A trace.sh => trace.sh +4 -0
@@ 0,0 1,4 @@
#!/bin/sh
rm *.trace
apitrace trace ./df
qapitrace df.trace