added quadtree implementation
authorBrendan Hansen <brendan.f.hansen@gmail.com>
Tue, 29 Sep 2020 03:37:16 +0000 (22:37 -0500)
committerBrendan Hansen <brendan.f.hansen@gmail.com>
Tue, 29 Sep 2020 03:37:16 +0000 (22:37 -0500)
15 files changed:
res/tilemap.data [new file with mode: 0644]
res/tilemap.png [new file with mode: 0644]
src/aabb.onyx [new file with mode: 0644]
src/font.onyx
src/gfx/atlas.onyx [new file with mode: 0644]
src/gfx/quad_renderer.onyx
src/gfx/texture.onyx
src/main.onyx
src/main_backup.onyx [new file with mode: 0644]
src/quad_tree.onyx [new file with mode: 0644]
src/shaders/basic.frag [deleted file]
src/shaders/basic.vert [deleted file]
src/shaders/quad.frag [new file with mode: 0644]
src/shaders/quad.vert [new file with mode: 0644]
tags

diff --git a/res/tilemap.data b/res/tilemap.data
new file mode 100644 (file)
index 0000000..dc86ab1
Binary files /dev/null and b/res/tilemap.data differ
diff --git a/res/tilemap.png b/res/tilemap.png
new file mode 100644 (file)
index 0000000..22b830c
Binary files /dev/null and b/res/tilemap.png differ
diff --git a/src/aabb.onyx b/src/aabb.onyx
new file mode 100644 (file)
index 0000000..3209719
--- /dev/null
@@ -0,0 +1,21 @@
+package aabb
+
+use package vecmath
+
+AABB :: struct {
+    x: f32; y: f32; w: f32; h: f32;
+}
+
+aabb_contains :: proc (r: AABB, v: V2f) -> bool {
+    return v.x >= r.x
+        && v.x <= r.x + r.w
+        && v.y >= r.y
+        && v.y <= r.y + r.h;
+}
+
+aabb_intersects :: proc (r1: AABB, r2: AABB) -> bool {
+    return r1.x < r2.x + r2.w
+        && r1.x + r1.w > r2.x
+        && r1.y < r2.y + r2.h
+        && r1.y + r1.h > r2.y;
+}
index 48597d1c13434b989c09cfe1fd6ca99be2783848..72b96b597115fcd4264e339c12fa08f59721ad51 100644 (file)
@@ -598,7 +598,7 @@ ttf_lookup_horizontal_metrics :: proc (use ttf: ^TrueTypeFont, glyph_index: u32)
 // To be used with gl.LUMANIANCE
 ttf_render_glyph :: proc (use ttf: ^TrueTypeFont, glyph: ^TTGlyph, data: ^u8, width: u32, height: u32) {
     // Black background
-    for y: 0 .. height do for x: 0 .. width do data[x + y * width] = cast(u8) 0;
+    for y: 0 .. height do for x: 0 .. width do data[x + y * width] = cast(u8) 255;
 
     // scale := ~~cast(i32) units_per_em / cast(f32) height;
 
diff --git a/src/gfx/atlas.onyx b/src/gfx/atlas.onyx
new file mode 100644 (file)
index 0000000..54f230b
--- /dev/null
@@ -0,0 +1,37 @@
+package gfx
+
+use package core
+use package gl as gl
+use package gl_utils as gl_utils
+
+Atlas :: struct {
+    texture : ^Texture;
+    map     : I32Map(AtlasSprite);
+}
+
+AtlasSprite :: struct {
+    x : i32 = 0;
+    y : i32 = 0;
+    w : i32 = 0;
+    h : i32 = 0;
+}
+
+atlas_create :: proc (tex: ^Texture) -> Atlas {
+    atlas : Atlas;
+    atlas.texture = tex;
+    i32map_init(^atlas.map);
+
+    return atlas;
+}
+
+atlas_map :: proc (use atlas: ^Atlas, id: i32, sprite: AtlasSprite) {
+    i32map_put(^map, id, sprite);
+}
+
+atlas_apply_to_quad :: proc (use atlas: ^Atlas, id: i32, quad: ^Quad) {
+    sprite := i32map_get(^map, id, AtlasSprite.{});
+    quad.tex_size.x = cast(f32) sprite.w / ~~texture.width;
+    quad.tex_size.y = cast(f32) sprite.h / ~~texture.height;
+    quad.tex_pos.x = cast(f32) sprite.x / ~~texture.width;
+    quad.tex_pos.y = cast(f32) sprite.y / ~~texture.height;
+}
index 64a1c87927bc6fe543b58c180dcdf5be1736e04c..eb8a7b1dae742c1ab5091d155a378dc74cd868d0 100644 (file)
@@ -3,12 +3,11 @@ package gfx
 use package core
 use package gl as gl
 use package gl_utils as gl_utils
+use package vecmath
 use package main { window_width, window_height }
 
-Vec2 :: struct {
-    x : f32;
-    y : f32;
-}
+#private_file
+quad_program : gl.GLProgram = -1;
 
 QuadRenderer :: struct {
     quad_data   : [..] Quad;
@@ -18,8 +17,6 @@ QuadRenderer :: struct {
     indexBuffer  : gl.GLBuffer;
     quadBuffer   : gl.GLBuffer;
 
-    program : gl.GLProgram;
-
     u_proj_loc : gl.GLUniformLocation;
 
     is_data_dirty : bool = false;
@@ -28,11 +25,11 @@ QuadRenderer :: struct {
 Color4f32 :: struct { r: f32; g: f32; b: f32; a: f32; }
 
 Quad :: struct {
-    pos  : Vec2 = Vec2.{ 0f, 0f };
-    size : Vec2 = Vec2.{ 0f, 0f };
+    pos  : V2f = V2f.{ 0f, 0f };
+    size : V2f = V2f.{ 0f, 0f };
 
-    tex_pos  : Vec2 = Vec2.{ 0f, 0f };
-    tex_size : Vec2 = Vec2.{ 0f, 0f };
+    tex_pos  : V2f = V2f.{ 0f, 0f };
+    tex_size : V2f = V2f.{ 0f, 0f };
 
     color : Color4f32 = Color4f32.{ 0f, 0f, 0f, 0f };
 }
@@ -42,16 +39,16 @@ quad_renderer_init :: proc (use qr: ^QuadRenderer, initial_quads := 10) {
     gl.bindVertexArray(vertexArray);
 
     // Set up vertex and index data
-    vertex_data : [4] Vec2;
-    vertex_data[0] = Vec2.{ 0.0f, 0.0f };
-    vertex_data[1] = Vec2.{ 0.0f, 1.0f };
-    vertex_data[2] = Vec2.{ 1.0f, 1.0f };
-    vertex_data[3] = Vec2.{ 1.0f, 0.0f };
+    vertex_data : [4] V2f;
+    vertex_data[0] = V2f.{ 0.0f, 0.0f };
+    vertex_data[1] = V2f.{ 0.0f, 1.0f };
+    vertex_data[2] = V2f.{ 1.0f, 1.0f };
+    vertex_data[3] = V2f.{ 1.0f, 0.0f };
 
     vertexBuffer = gl.createBuffer();
     gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
     gl.bufferData(gl.ARRAY_BUFFER, Buffer.{
-        count = sizeof [4] Vec2,
+        count = sizeof [4] V2f,
         data = cast(^void) vertex_data
     }, gl.STATIC_DRAW);
 
@@ -103,18 +100,24 @@ quad_renderer_init :: proc (use qr: ^QuadRenderer, initial_quads := 10) {
 
     gl.bindVertexArray(-1);
 
-    vertex_shader   := gl_utils.compile_shader(gl.VERTEX_SHADER, #file_contents "./src/shaders/basic.vert");
-    fragment_shader := gl_utils.compile_shader(gl.FRAGMENT_SHADER, #file_contents "./src/shaders/basic.frag");
-    program          = gl_utils.link_program(vertex_shader, fragment_shader);
-    gl.useProgram(program);
+    if quad_program == ~~ -1 {
+        vertex_shader   := gl_utils.compile_shader(gl.VERTEX_SHADER, #file_contents "./src/shaders/quad.vert");
+        fragment_shader := gl_utils.compile_shader(gl.FRAGMENT_SHADER, #file_contents "./src/shaders/quad.frag");
+        quad_program     = gl_utils.link_program(vertex_shader, fragment_shader);
+        gl.useProgram(quad_program);
+    }
 
-    u_proj_loc = gl.getUniformLocation(program, "u_proj");
+    u_proj_loc = gl.getUniformLocation(quad_program, "u_proj");
     quad_renderer_update_view(qr);
 }
 
-quad_renderer_draw :: proc (use qr: ^QuadRenderer) {
+quad_renderer_draw :: proc (use qr: ^QuadRenderer, count := -1) {
+    c := count;
+    if count == -1 do c = quad_data.count;
+
+    gl.useProgram(quad_program);
     gl.bindVertexArray(vertexArray);
-    gl.drawElementsInstanced(gl.TRIANGLES, 6, gl.UNSIGNED_BYTE, 0, quad_data.count);
+    gl.drawElementsInstanced(gl.TRIANGLES, 6, gl.UNSIGNED_BYTE, 0, c);
     gl.bindVertexArray(-1);
 }
 
index ed014a36712a485eb8fe0efe46758643afbd1c7e..2928c3e470fcdf9aba258d00578d35bea1f1c329 100644 (file)
@@ -4,6 +4,20 @@ use package core
 use package gl as gl
 use package gl_utils as gl_utils
 
+textures : struct {
+    tilemap : Texture;
+    smile   : Texture;
+}
+
+textures_init :: proc () {
+    textures.tilemap = texture_create(#file_contents "res/tilemap.data", 256, 256);
+    textures.smile   = texture_create(#file_contents "res/smile.data", 32, 32);
+
+    texture_prepare(^textures.tilemap);
+    texture_prepare(^textures.smile);
+}
+
+
 Texture :: struct {
     // NOTE: After a texture is loaded, we may not want to keep the
     // raw texture data in memory any longer. This would not be possible
index e07a9ff57439c4430d59657083d39e6941a781ee..407de6b823eaad27129dce9e5647a328a8c49f6e 100644 (file)
@@ -7,10 +7,13 @@ package main
 #include_file "utils/gl"
 #include_file "gfx/quad_renderer"
 #include_file "gfx/texture"
+#include_file "gfx/atlas"
 #include_file "events"
 #include_file "input"
 #include_file "font"
 #include_file "vecmath"
+#include_file "aabb"
+#include_file "quad_tree"
 
 use package core
 use package gfx
@@ -20,9 +23,10 @@ use package input as input { Key }
 use package vecmath
 use package ttf_font
 
-NUM_QUADS :: 1 << 10
-
+use package quad_tree
+use package aabb
 
+NUM_QUADS :: 1 << 17
 
 RenderContext :: struct {
     quad_renderer: ^QuadRenderer;
@@ -30,10 +34,6 @@ RenderContext :: struct {
     curr_quad_idx: i32 = 0;
     max_quad_idx:  i32 = 0;
 
-    // @CLEANUP  Allow for the syntax:
-    //      color := Color4f32.{ ... }
-    // OR
-    //      color: Color4f32 = .{ ... }
     color: Color4f32 = Color4f32.{ 1.0f, 1.0f, 1.0f, 1.0f };
 }
 
@@ -55,21 +55,42 @@ draw_rect :: proc (use rc: ^RenderContext, x: f32, y: f32, w: f32, h: f32) {
     if curr_quad_idx >= max_quad_idx do return;
 
     quad_update_at_index(quad_renderer, curr_quad_idx, Quad.{
-        pos   = Vec2.{ x, y },
-        size  = Vec2.{ w, h },
+        pos   = V2f.{ x, y },
+        size  = V2f.{ w, h },
         color = color,
 
-        tex_size = Vec2.{ 1.0f, 1.0f },
+        tex_pos = V2f.{ -1.0f, -1.0f },
     });
 
     curr_quad_idx += 1;
 }
 
+draw_textured_rect :: proc (use rc: ^RenderContext, x: f32, y: f32, w: f32, h: f32, tx: f32, ty: f32, tw: f32, th: f32) {
+    if curr_quad_idx >= max_quad_idx do return;
+
+    quad_update_at_index(quad_renderer, curr_quad_idx, Quad.{
+        pos   = V2f.{ x, y },
+        size  = V2f.{ w, h },
+        color = color,
+
+        tex_pos  = V2f.{ tx, ty },
+        tex_size = V2f.{ tw, th },
+    });
+
+    curr_quad_idx += 1;
+}
+
+draw_quad :: proc (use rc: ^RenderContext, quad: ^Quad) {
+    if curr_quad_idx >= max_quad_idx do return;
+    quad_update_at_index(quad_renderer, curr_quad_idx, *quad);
+    curr_quad_idx += 1;
+}
+
 render_context_flush :: proc (use rc: ^RenderContext) {
     quad_rebuffer_data(quad_renderer);
-    quad_renderer_draw(quad_renderer);
+    quad_renderer_draw(quad_renderer, curr_quad_idx);
 
-    for i: 0 .. curr_quad_idx do quad_update_at_index(quad_renderer, i, Quad.{});
+    // for i: 0 .. curr_quad_idx do quad_update_at_index(quad_renderer, i, Quad.{});
 
     curr_quad_idx = 0;
 }
@@ -82,13 +103,9 @@ window_height := 0
 
 renderer      : RenderContext
 input_state   : input.InputState
-ttf           : TrueTypeFont
-font_tex      : Texture
-smile         : Texture
-
-Player :: struct { use pos : Vec2; }
-player : Player
-
+atlas         : Atlas
+dudes         : [..] Dude
+dude_tree     : QuadTree(Dude)
 
 poll_events :: proc () {
     use event.DomEventKind;
@@ -109,69 +126,77 @@ poll_events :: proc () {
     }
 }
 
+quad_scratch_buffer : [512 * 1024] u8;
+
 update :: proc () {
     input.preupdate(^input_state);
     defer input.postupdate(^input_state);
     poll_events();
 
-    player_speed :: 4f;
+    quad_scratch : ScratchState;
+    quad_alloc : Allocator;
+    scratch_state_init(^quad_scratch, ~~quad_scratch_buffer, 128 * 1024);
+    scratch_alloc_init(^quad_alloc, ^quad_scratch);
 
-    if input.key_down(^input_state, Key.ArrowUp)    do player.y -= player_speed;
-    if input.key_down(^input_state, Key.ArrowDown)  do player.y += player_speed;
-    if input.key_down(^input_state, Key.ArrowLeft)  do player.x -= player_speed;
-    if input.key_down(^input_state, Key.ArrowRight) do player.x += player_speed;
-}
+    quadtree_init(^dude_tree, AABB.{ 0.0f, 0.0f, ~~window_width, ~~window_height });
+    for ^d: dudes do quadtree_insert(^dude_tree, d, quad_alloc);
 
-glyph_size :: 64
-glyph_data : [glyph_size * glyph_size] u8
+    for ^d: dudes do dude_update(d, ^dude_tree);
+    for ^d: dudes {
+        if d.can_move_x do d.pos.x += d.vel.x;
+        else            do d.vel.x = -d.vel.x;
+        if d.can_move_y do d.pos.y += d.vel.y;
+        else            do d.vel.y = -d.vel.y;
+    }
+}
 
 draw :: proc () {
-    gl.clearColor(0.2f, 0.2f, 0.2f, 1.0f);
-    // gl.clearColor(1.0f, 1.0f, 1.0f, 1.0f);
+    gl.clearColor(0.1f, 0.1f, 0.1f, 1.0f);
     gl.clear(gl.COLOR_BUFFER_BIT);
 
-    // renderer.color = Color4f32.{ 1f, 0f, 0f, 1f };
+    texture_use(^textures.tilemap);
 
+    draw_quad_tree(^dude_tree);
 
-    // points : [6] V2f;
-    // points[0] = V2f.{ 500.0f, 700.0f };
-    // points[1] = V2f.{ 600.0f, 200.0f };
-    // points[2] = V2f.{ 700.0f, 400.0f };
-    // points[3] = V2f.{ 1700.0f, 800.0f };
-    // points[4] = V2f.{ 1800.0f, 400.0f };
-    // points[5] = V2f.{ 1000.0f, 200.0f };
+    draw_rect(^renderer, ~~input_state.mouse.x, ~~input_state.mouse.y, 10f, 10f);
 
-    // for i: 0 .. 200 {
-    //     pos := bezier_curve(~~i / 200.0f, points[0 .. 6]);
-    //     draw_rect(^renderer, pos.x, pos.y, 16f, 16f);
-    // }
+    for ^d: dudes {
+        aabb := dude_get_aabb(d);
+        draw_rect(^renderer, aabb.x, aabb.y, aabb.w, aabb.h);
+    }
 
-    // for y: 0 .. glyph_size do for x: 0 .. glyph_size {
-    //     if glyph_data[x + y * glyph_size] != ~~0 {
-    //         draw_rect(^renderer, ~~(x * 2 + 100), ~~(y * 2 + 100), 2f, 2f);
-    //     }
-    // }
+    render_context_flush(^renderer);
+}
 
-    // mets := ttf_lookup_horizontal_metrics(^ttf, glyph_index);
-    // println(cast(u32) mets.advance_width);
+draw_quad_tree :: proc (qt: ^QuadTree($T), level := 0, corner := 0) {
+    col := cast(f32) (0.2f + 0.15f * ~~level);
+    if col > 1.0f do col = 1.0f;
 
-    // rx := 100.0f / ~~ cast(i32) (glyph.x_max - glyph.x_min);
-    // ry := 100.0f / ~~ cast(i32) (glyph.y_min - glyph.y_max);
-    // for p: glyph.points {
-    //     if p.on_curve do continue;
-    //     draw_rect(^renderer, ~~ cast(i32) p.x * rx + 100f, ~~ cast(i32) p.y * ry + 100f, 10f, 10f);
-    // }
+    color := Color4f32.{ 0.0f, 0.0f, 0.0f, 1.0f };
 
-    texture_use(^font_tex);
-    draw_rect(^renderer, 100f, 100f, 256f, 256f);
-    render_context_flush(^renderer);
+    if corner == 1 do color.r = col;
+    if corner == 2 do color.g = col;
+    if corner == 3 do color.b = col;
 
-    texture_use(^smile);
-    draw_rect(^renderer, ~~input_state.mouse.x, ~~input_state.mouse.y, 10f, 10f);
-    draw_rect(^renderer, player.pos.x, player.pos.y, 100f, 100f);
-    render_context_flush(^renderer);
+    quad := Quad.{
+        pos  = V2f.{ qt.region.x, qt.region.y },
+        size = V2f.{ qt.region.w, qt.region.h },
+        color = color,
+
+        tex_pos = V2f.{ -1.0f, -1.0f },
+    };
+    draw_quad(^renderer, ^quad);
+
+    if qt.nw != null {
+        draw_quad_tree(qt.nw, level + 1, 1);
+        draw_quad_tree(qt.ne, level + 1, 2);
+        draw_quad_tree(qt.sw, level + 1, 3);
+        draw_quad_tree(qt.se, level + 1, 4);
+    }
 }
 
+debugger :: proc () #foreign "dummy" "breakable" ---
+
 // This procedure is called asynchronously from JS every frame.
 new_frame_callback :: proc () #export {
     update();
@@ -193,31 +218,104 @@ main :: proc (args: [] cstring) {
     gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
 
     render_context_init(^renderer, null);
+    textures_init();
 
-    image_data := #file_contents "res/smile.data";
-    smile = texture_create(image_data, 32, 32);
-    texture_prepare(^smile);
+    atlas = atlas_create(^textures.tilemap);
+    atlas_map(^atlas, 0, AtlasSprite.{ x = 16, y = 0, w = 16, h = 16 });
 
     event.init();
     input.init(^input_state);
 
-    ttf_data := #file_contents "res/Hack-Regular.ttf";
-    ttf = ttf_create(ttf_data);
+    array_init(^dudes);
+    for i: 0 .. 500 {
+        array_push(^dudes, dude_create_random());
+    }
 
-    glyph : ^TTGlyph = null;
-    glyph_index := ttf_lookup_glyph_by_char(^ttf, ~~ #char "A");
-    glyph = ttf_read_glyph(^ttf, glyph_index);
-    if glyph == null do return;
-    defer ttf_glyph_destroy(glyph);
+    game_launch :: proc () #foreign "game" "launch" ---;
+    game_launch();
+}
 
-    ttf_render_glyph(^ttf, glyph, ~~glyph_data, glyph_size, glyph_size);
-    font_tex = texture_create(glyph_data[0 .. glyph_size * glyph_size], glyph_size, glyph_size, TextureDataMode.BRIGHTNESS);
-    texture_prepare(^font_tex);
 
-    game_launch();
+Dude_Color_Table : [4] Color4f32;
+
+Dude :: struct {
+    pos  : V2f;
+    vel  : V2f;
+    size : f32;
+    color : Color4f32;
+
+    can_move_x : bool = true;
+    can_move_y : bool = true;
+}
+
+dude_create_random :: proc () -> Dude {
+    return Dude.{
+        pos = V2f.{ random_float(0.0f, 300.0f), random_float(0.0f, 300.0f) },
+        vel = V2f.{ random_float(-3.0f, 3.0f), random_float(-3.0f, 3.0f) },
+        size = random_float(2.0f, 3.0f),
+        color = Dude_Color_Table[random_between(0, 3)],
+    };
+}
+
+dude_update :: proc (use dude: ^Dude, other_dudes: ^QuadTree(Dude)) {
+    // if random_between(0, 100) < 1 {
+    //     vel.x = random_float(-2.0f, 2.0f);
+    //     vel.y = random_float(-2.0f, 2.0f);
+    // }
+
+    dude_try_move(dude, other_dudes);
 }
 
+dude_try_move :: proc (use dude: ^Dude, other_dudes: ^QuadTree(Dude)) {
+    old_pos := pos;
 
+    potential_dudes : [..] ^Dude;
+    array_init(^potential_dudes);
+    defer array_free(^potential_dudes);
 
-game_launch :: proc () #foreign "game" "launch" ---
+    around := AABB.{ x = pos.x - size * 5.0f, y = pos.y - size * 5.0f, w = size * 10.0f, h = size * 10.0f };
+    quadtree_query(other_dudes, around, ^potential_dudes);
 
+    collided := false;
+
+    pos.x += vel.x;
+    if pos.x - size < 0.0f || pos.x + size >= ~~window_width do collided = true;
+
+    dude_aabb := dude_get_aabb(dude);
+    for other: potential_dudes {
+        if other == dude do continue;
+
+        other_aabb := dude_get_aabb(other);
+        if aabb_intersects(dude_aabb, other_aabb) {
+            // printf("COLLISION\n");
+            collided = true;
+            break;
+        }
+    }
+
+    can_move_x = !collided;
+    pos.x -= vel.x;
+
+    collided = false;
+
+    pos.y += vel.y;
+    if pos.y - size < 0.0f || pos.y + size >= ~~window_height do collided = true;
+
+    dude_aabb = dude_get_aabb(dude);
+    for other: potential_dudes {
+        if other == dude do continue;
+
+        other_aabb := dude_get_aabb(other);
+        if aabb_intersects(dude_aabb, other_aabb) {
+            collided = true;
+            break;
+        }
+    }
+
+    can_move_y = !collided;
+    pos.y -= vel.y;
+}
+
+dude_get_aabb :: proc (use dude: ^Dude) -> AABB {
+    return AABB.{ x = pos.x - size, y = pos.y - size, w = size * 2.0f, h = size * 2.0f };
+}
diff --git a/src/main_backup.onyx b/src/main_backup.onyx
new file mode 100644 (file)
index 0000000..591514f
--- /dev/null
@@ -0,0 +1,297 @@
+package main
+
+#include_file "core/std/js"
+#include_file "core/js/webgl"
+
+#include_folder "src/"
+#include_file "utils/gl"
+#include_file "gfx/quad_renderer"
+#include_file "gfx/texture"
+#include_file "gfx/atlas"
+#include_file "events"
+#include_file "input"
+#include_file "font"
+#include_file "vecmath"
+#include_file "aabb"
+#include_file "quad_tree"
+
+use package core
+use package gfx
+use package gl as gl
+use package event as event
+use package input as input { Key }
+use package vecmath
+use package ttf_font
+
+use package quad_tree
+use package aabb
+
+NUM_QUADS :: 1 << 10
+
+RenderContext :: struct {
+    quad_renderer: ^QuadRenderer;
+
+    curr_quad_idx: i32 = 0;
+    max_quad_idx:  i32 = 0;
+
+    color: Color4f32 = Color4f32.{ 1.0f, 1.0f, 1.0f, 1.0f };
+}
+
+render_context_init :: proc (use rc: ^RenderContext, qr: ^QuadRenderer = null) {
+    aqr := qr;
+
+    if aqr == null {
+        aqr = calloc(sizeof QuadRenderer);
+        quad_renderer_init(aqr, NUM_QUADS);
+    }
+
+    *rc = RenderContext.{
+        quad_renderer = aqr,
+        max_quad_idx = qr.quad_data.count,
+    };
+}
+
+draw_rect :: proc (use rc: ^RenderContext, x: f32, y: f32, w: f32, h: f32) {
+    if curr_quad_idx >= max_quad_idx do return;
+
+    quad_update_at_index(quad_renderer, curr_quad_idx, Quad.{
+        pos   = V2f.{ x, y },
+        size  = V2f.{ w, h },
+        color = color,
+
+        tex_pos = V2f.{ -1.0f, -1.0f },
+    });
+
+    curr_quad_idx += 1;
+}
+
+draw_textured_rect :: proc (use rc: ^RenderContext, x: f32, y: f32, w: f32, h: f32, tx: f32, ty: f32, tw: f32, th: f32) {
+    if curr_quad_idx >= max_quad_idx do return;
+
+    quad_update_at_index(quad_renderer, curr_quad_idx, Quad.{
+        pos   = V2f.{ x, y },
+        size  = V2f.{ w, h },
+        color = color,
+
+        tex_pos  = V2f.{ tx, ty },
+        tex_size = V2f.{ tw, th },
+    });
+
+    curr_quad_idx += 1;
+}
+
+draw_quad :: proc (use rc: ^RenderContext, quad: ^Quad) {
+    if curr_quad_idx >= max_quad_idx do return;
+    quad.color = color;
+    quad_update_at_index(quad_renderer, curr_quad_idx, *quad);
+    curr_quad_idx += 1;
+}
+
+render_context_flush :: proc (use rc: ^RenderContext) {
+    quad_rebuffer_data(quad_renderer);
+    quad_renderer_draw(quad_renderer, curr_quad_idx);
+
+    // for i: 0 .. curr_quad_idx do quad_update_at_index(quad_renderer, i, Quad.{});
+
+    curr_quad_idx = 0;
+}
+
+
+
+// @Cleanup
+window_width  := 0
+window_height := 0
+
+renderer      : RenderContext
+input_state   : input.InputState
+atlas         : Atlas
+dudes         : [..] Dude
+dude_tree     : QuadTree(Dude)
+
+poll_events :: proc () {
+    use event.DomEventKind;
+
+    ev : event.Event;
+    while event.poll(^ev) do switch ev.kind {
+        case KeyDown, KeyUp, MouseDown, MouseUp, MouseMove do input.process_event(^input_state, ^ev);
+
+        case Resize {
+            window_width  = ev.resize.width;
+            window_height = ev.resize.height;
+
+            gl.canvasSize(window_width, window_height);
+            gl.viewport(0, 0, window_width, window_height);
+
+            quad_renderer_update_view(renderer.quad_renderer);
+        }
+    }
+}
+
+quad_scratch_buffer : [128 * 1024] u8;
+
+update :: proc () {
+    input.preupdate(^input_state);
+    defer input.postupdate(^input_state);
+    poll_events();
+
+    quad_scratch : ScratchState;
+    quad_alloc : Allocator;
+    scratch_state_init(^quad_scratch, ~~quad_scratch_buffer, 128 * 1024);
+    scratch_alloc_init(^quad_alloc, ^quad_scratch);
+
+    quadtree_init(^dude_tree, AABB.{ 0.0f, 0.0f, ~~window_width, ~~window_height });
+    for ^d: dudes do quadtree_insert(^dude_tree, d, quad_alloc);
+
+    for ^d: dudes do dude_update(d, ^dude_tree);
+}
+
+draw :: proc () {
+    gl.clearColor(0.1f, 0.1f, 0.1f, 1.0f);
+    gl.clear(gl.COLOR_BUFFER_BIT);
+
+    texture_use(^textures.tilemap);
+    draw_rect(^renderer, ~~input_state.mouse.x, ~~input_state.mouse.y, 10f, 10f);
+
+    for ^d: dudes {
+        aabb := dude_get_aabb(d);
+        draw_rect(^renderer, aabb.x, aabb.y, aabb.w, aabb.h);
+    }
+
+    render_context_flush(^renderer);
+}
+
+// This procedure is called asynchronously from JS every frame.
+new_frame_callback :: proc () #export {
+    update();
+    draw();
+}
+
+main :: proc (args: [] cstring) {
+    println("Setting up WebGL2 canvas...");
+
+    if !gl.init("gamecanvas") {
+        print("Failed to initialize GL canvas.");
+        return;
+    }
+
+    gl.enable(gl.CULL_FACE);
+    gl.cullFace(gl.BACK);
+
+    gl.enable(gl.BLEND);
+    gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
+
+    render_context_init(^renderer, null);
+    textures_init();
+
+    atlas = atlas_create(^textures.tilemap);
+    atlas_map(^atlas, 0, AtlasSprite.{ x = 16, y = 0, w = 16, h = 16 });
+
+    event.init();
+    input.init(^input_state);
+
+    array_init(^dudes);
+    for i: 0 .. 50 {
+        array_push(^dudes, dude_create_random());
+    }
+    // array_push(^dudes, Dude.{
+    //     pos = V2f.{ 100.0f, 100.0f },
+    //     vel = V2f.{ 2.0f, 0.0f },
+    //     size = 10.0f,
+    //     color = Color4f32.{ 1.0f, 1.0f, 1.0f, 1.0f },
+    // });
+
+    // array_push(^dudes, Dude.{
+    //     pos = V2f.{ 1000.0f, 100.0f },
+    //     vel = V2f.{ -2.0f, 0.0f },
+    //     size = 10.0f,
+    //     color = Color4f32.{ 1.0f, 1.0f, 1.0f, 1.0f },
+    // });
+
+    game_launch :: proc () #foreign "game" "launch" ---;
+    game_launch();
+}
+
+
+Dude_Color_Table : [4] Color4f32;
+
+Dude :: struct {
+    pos  : V2f;
+    vel  : V2f;
+    size : f32;
+    color : Color4f32;
+}
+
+dude_create_random :: proc () -> Dude {
+    return Dude.{
+        pos = V2f.{ random_float(0.0f, 800.0f), random_float(0.0f, 800.0f) },
+        vel = V2f.{ random_float(-3.0f, 3.0f), random_float(-3.0f, 3.0f) },
+        size = random_float(5.0f, 30.0f),
+        color = Dude_Color_Table[random_between(0, 3)],
+    };
+}
+
+dude_update :: proc (use dude: ^Dude, other_dudes: ^QuadTree(Dude)) {
+    // if random_between(0, 100) < 2 {
+    //     vel.x = random_float(-2.0f, 2.0f);
+    //     vel.y = random_float(-2.0f, 2.0f);
+    // }
+
+    dude_try_move(dude, other_dudes);
+}
+
+dude_try_move :: proc (use dude: ^Dude, other_dudes: ^QuadTree(Dude)) {
+    old_pos := pos;
+
+    potential_dudes : [..] ^Dude;
+    array_init(^potential_dudes);
+    defer array_free(^potential_dudes);
+
+    around := AABB.{ x = pos.x - size * 10.0f, y = pos.y - size * 10.0f, w = size * 20.0f, h = size * 20.0f };
+    quadtree_query(other_dudes, around, ^potential_dudes);
+
+    collided := false;
+
+    pos.x += vel.x;
+    if pos.x - size < 0.0f || pos.x + size >= ~~window_width do collided = true;
+
+    dude_aabb := dude_get_aabb(dude);
+    for other: potential_dudes {
+        if other == dude do continue;
+
+        other_aabb := dude_get_aabb(other);
+        if aabb_intersects(dude_aabb, other_aabb) {
+            collided = true;
+            break;
+        }
+    }
+
+    if collided {
+        pos.x -= vel.x;
+        vel.x = -vel.x;
+    }
+
+    collided = false;
+
+    pos.y += vel.y;
+    if pos.y - size < 0.0f || pos.y + size >= ~~window_height do collided = true;
+
+    dude_aabb = dude_get_aabb(dude);
+    for other: potential_dudes {
+        if other == dude do continue;
+
+        other_aabb := dude_get_aabb(other);
+        if aabb_intersects(dude_aabb, other_aabb) {
+            collided = true;
+            break;
+        }
+    }
+
+    if collided {
+        pos.y -= vel.y;
+        vel.y = -vel.y;
+    }
+}
+
+dude_get_aabb :: proc (use dude: ^Dude) -> AABB {
+    return AABB.{ x = pos.x - size, y = pos.y - size, w = size * 2.0f, h = size * 2.0f };
+}
diff --git a/src/quad_tree.onyx b/src/quad_tree.onyx
new file mode 100644 (file)
index 0000000..4ec3e21
--- /dev/null
@@ -0,0 +1,97 @@
+package quad_tree
+
+use package core
+use package aabb
+
+#private_file
+QUAD_TREE_ENTITY_STORAGE :: 2;
+
+QuadTree :: struct ($T) {
+    region : AABB;
+
+    points : [QUAD_TREE_ENTITY_STORAGE] ^T;
+    points_count : u32 = 0;
+
+    nw: ^QuadTree(T) = null;
+    ne: ^QuadTree(T) = null;
+    sw: ^QuadTree(T) = null;
+    se: ^QuadTree(T) = null;
+}
+
+quadtree_init :: proc (qt: ^QuadTree($T), initial_region: AABB) {
+    qt.region = initial_region;
+    qt.nw = null;
+    qt.ne = null;
+    qt.sw = null;
+    qt.se = null;
+    qt.points_count = 0;
+
+    for ^p: qt.points do *p = null;
+}
+
+// quadtree_free :: proc (qt: ^QuadTree($T), initial := true) {
+//     if qt.nw != null {
+//         quadtree_free(qt.nw, false);
+//         quadtree_free(qt.ne, false);
+//         quadtree_free(qt.sw, false);
+//         quadtree_free(qt.se, false);
+//     }
+//
+//     if !initial {
+//         cfree(qt);
+//     }
+// }
+
+quadtree_subdivide :: proc (use qt: ^QuadTree($T), a: Allocator) {
+    if nw != null do return;
+
+    hw := region.w / ~~ 2;
+    hh := region.h / ~~ 2;
+
+    qt.nw = alloc(a, sizeof QuadTree(T));
+    qt.ne = alloc(a, sizeof QuadTree(T));
+    qt.sw = alloc(a, sizeof QuadTree(T));
+    qt.se = alloc(a, sizeof QuadTree(T));
+
+    quadtree_init(qt.nw, AABB.{ region.x,      region.y,      hw, hh });
+    quadtree_init(qt.ne, AABB.{ region.x + hw, region.y,      hw, hh });
+    quadtree_init(qt.sw, AABB.{ region.x,      region.y + hh, hw, hh });
+    quadtree_init(qt.se, AABB.{ region.x + hw, region.y + hh, hw, hh });
+}
+
+quadtree_insert :: proc (use qt: ^QuadTree($T), t: ^T, alloc := context.allocator) -> bool {
+    pos := t.pos; // T is expected to have a 'pos' element.
+
+    if !aabb_contains(region, pos) do return false;
+
+    if points_count < QUAD_TREE_ENTITY_STORAGE && nw == null {
+        points[points_count] = t;
+        points_count += 1;
+        return true;
+    }
+
+    if nw == null do quadtree_subdivide(qt, alloc);
+
+    if quadtree_insert(nw, t, alloc) do return true;
+    if quadtree_insert(ne, t, alloc) do return true;
+    if quadtree_insert(sw, t, alloc) do return true;
+    if quadtree_insert(se, t, alloc) do return true;
+
+    return false;
+}
+
+quadtree_query :: proc (use qt: ^QuadTree($T), r: AABB, p: ^[..] ^T) {
+    if !aabb_intersects(region, r) do return;
+
+    while i := 0; i < points_count {
+        if aabb_contains(r, points[i].pos) do array_push(p, points[i]);
+        i += 1;
+    }
+
+    if nw == null do return;
+
+    quadtree_query(nw, r, p);
+    quadtree_query(ne, r, p);
+    quadtree_query(sw, r, p);
+    quadtree_query(se, r, p);
+}
diff --git a/src/shaders/basic.frag b/src/shaders/basic.frag
deleted file mode 100644 (file)
index 0d9552f..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-#version 300 es
-
-precision mediump float;
-
-uniform sampler2D tex;
-
-in vec2 v_tex_pos;
-in vec4 v_col;
-
-out vec4 fragColor;
-
-void main() {
-       fragColor = v_col * texture(tex, v_tex_pos);
-}
diff --git a/src/shaders/basic.vert b/src/shaders/basic.vert
deleted file mode 100644 (file)
index cb8a34b..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-#version 300 es
-
-// Per Vertex
-layout(location = 0) in vec2 a_vert_pos;
-
-// Per Quad
-layout(location = 1) in vec2 a_pos;
-layout(location = 2) in vec2 a_size;
-layout(location = 3) in vec2 a_tex_pos;
-layout(location = 4) in vec2 a_tex_size;
-layout(location = 5) in vec4 a_col;
-
-uniform mat4 u_proj;
-
-out vec2 v_tex_pos;
-out vec4 v_col;
-
-void main() {
-       gl_Position = u_proj * vec4(a_vert_pos * a_size + a_pos, 0, 1);
-    v_col = a_col;
-
-    v_tex_pos = a_tex_pos + a_vert_pos * a_tex_size;
-}
diff --git a/src/shaders/quad.frag b/src/shaders/quad.frag
new file mode 100644 (file)
index 0000000..59ab7ef
--- /dev/null
@@ -0,0 +1,18 @@
+#version 300 es
+
+precision mediump float;
+
+uniform sampler2D tex;
+
+in vec2 v_tex_pos;
+in vec4 v_col;
+
+out vec4 fragColor;
+
+void main() {
+    if (v_tex_pos.x < 0.0) {
+        fragColor = v_col;
+    } else {
+        fragColor = v_col * texture(tex, v_tex_pos);
+    }
+}
diff --git a/src/shaders/quad.vert b/src/shaders/quad.vert
new file mode 100644 (file)
index 0000000..c7fa396
--- /dev/null
@@ -0,0 +1,28 @@
+#version 300 es
+
+// Per Vertex
+layout(location = 0) in vec2 a_vert_pos;
+
+// Per Quad
+layout(location = 1) in vec2 a_pos;
+layout(location = 2) in vec2 a_size;
+layout(location = 3) in vec2 a_tex_pos;
+layout(location = 4) in vec2 a_tex_size;
+layout(location = 5) in vec4 a_col;
+
+uniform mat4 u_proj;
+// uniform mat4 u_world;
+
+out vec2 v_tex_pos;
+out vec4 v_col;
+
+void main() {
+       gl_Position = u_proj * vec4(a_vert_pos * a_size + a_pos, 0, 1);
+    v_col = a_col;
+
+    if (a_tex_pos.x < 0.0) {
+        v_tex_pos = vec2(-1.0, -1.0);
+    } else {
+        v_tex_pos = a_tex_pos + a_vert_pos * a_tex_size;
+    }
+}
diff --git a/tags b/tags
index cc29dd4fe8e4d9ed9cafd8db98b8c67b9595d058..ed3f28bb13e915b6007a8a9dc2863d6159d63e31 100644 (file)
--- a/tags
+++ b/tags
@@ -6,6 +6,7 @@
 !_TAG_PROGRAM_NAME     Onyx Compiler
 !_TAG_PROGRAM_URL      https://github.com/brendanfh/onyx
 !_TAG_PROGRAM_VERSION  0.0.1
+AABB   src/aabb.onyx   /^AABB :: struct {$/
 ACTIVE_ATTRIBUTES      /usr/share/onyx/core/js/webgl.onyx      /^ACTIVE_ATTRIBUTES                :: 0x8B89$/
 ACTIVE_TEXTURE /usr/share/onyx/core/js/webgl.onyx      /^ACTIVE_TEXTURE                 :: 0x84E0$/
 ACTIVE_UNIFORMS        /usr/share/onyx/core/js/webgl.onyx      /^ACTIVE_UNIFORMS                  :: 0x8B86$/
@@ -23,6 +24,8 @@ ARRAY_BUFFER_BINDING  /usr/share/onyx/core/js/webgl.onyx      /^ARRAY_BUFFER_BINDING
 ATTACHED_SHADERS       /usr/share/onyx/core/js/webgl.onyx      /^ATTACHED_SHADERS                 :: 0x8B85$/
 AllocAction    /usr/share/onyx/core/builtin.onyx       /^AllocAction :: enum {$/
 Allocator      /usr/share/onyx/core/builtin.onyx       /^Allocator :: struct {$/
+Atlas  src/gfx/atlas.onyx      /^Atlas :: struct {$/
+AtlasSprite    src/gfx/atlas.onyx      /^AtlasSprite :: struct {$/
 BACK   /usr/share/onyx/core/js/webgl.onyx      /^BACK                           :: 0x0405$/
 BLEND  /usr/share/onyx/core/js/webgl.onyx      /^BLEND                          :: 0x0BE2$/
 BLEND_COLOR    /usr/share/onyx/core/js/webgl.onyx      /^BLEND_COLOR                    :: 0x8005$/
@@ -132,6 +135,8 @@ DYNAMIC_DRAW        /usr/share/onyx/core/js/webgl.onyx      /^DYNAMIC_DRAW
 DYNAMIC_READ   /usr/share/onyx/core/js/webgl.onyx      /^DYNAMIC_READ                                  :: 0x88E9$/
 DomEvent       src/events.onyx /^DomEvent :: struct {$/
 DomEventKind   src/events.onyx /^DomEventKind :: enum {$/
+Dude   src/main.onyx   /^Dude :: struct {$/
+Dude_Color_Table       src/main.onyx   /^Dude_Color_Table : [4] Color4f32;$/
 ELEMENT_ARRAY_BUFFER   /usr/share/onyx/core/js/webgl.onyx      /^ELEMENT_ARRAY_BUFFER           :: 0x8893$/
 ELEMENT_ARRAY_BUFFER_BINDING   /usr/share/onyx/core/js/webgl.onyx      /^ELEMENT_ARRAY_BUFFER_BINDING   :: 0x8895$/
 EQUAL  /usr/share/onyx/core/js/webgl.onyx      /^EQUAL                          :: 0x0202$/
@@ -331,13 +336,13 @@ POINTS    /usr/share/onyx/core/js/webgl.onyx      /^POINTS                         :: 0x
 POLYGON_OFFSET_FACTOR  /usr/share/onyx/core/js/webgl.onyx      /^POLYGON_OFFSET_FACTOR          :: 0x8038$/
 POLYGON_OFFSET_FILL    /usr/share/onyx/core/js/webgl.onyx      /^POLYGON_OFFSET_FILL            :: 0x8037$/
 POLYGON_OFFSET_UNITS   /usr/share/onyx/core/js/webgl.onyx      /^POLYGON_OFFSET_UNITS           :: 0x2A00$/
-Player src/main.onyx   /^Player :: struct { use pos : Vec2; }$/
 PtrMap /usr/share/onyx/core/ptrmap.onyx        /^PtrMap :: struct {$/
 PtrMapEntry    /usr/share/onyx/core/ptrmap.onyx        /^PtrMapEntry :: struct {$/
 QUERY_RESULT   /usr/share/onyx/core/js/webgl.onyx      /^QUERY_RESULT                                  :: 0x8866$/
 QUERY_RESULT_AVAILABLE /usr/share/onyx/core/js/webgl.onyx      /^QUERY_RESULT_AVAILABLE                        :: 0x8867$/
 Quad   src/gfx/quad_renderer.onyx      /^Quad :: struct {$/
 QuadRenderer   src/gfx/quad_renderer.onyx      /^QuadRenderer :: struct {$/
+QuadTree       src/quad_tree.onyx      /^QuadTree :: struct ($T) {$/
 R11F_G11F_B10F /usr/share/onyx/core/js/webgl.onyx      /^R11F_G11F_B10F                                :: 0x8C3A$/
 R16F   /usr/share/onyx/core/js/webgl.onyx      /^R16F                                          :: 0x822D$/
 R16I   /usr/share/onyx/core/js/webgl.onyx      /^R16I                                          :: 0x8233$/
@@ -635,9 +640,10 @@ VERTEX_ATTRIB_ARRAY_STRIDE /usr/share/onyx/core/js/webgl.onyx      /^VERTEX_ATTRIB_AR
 VERTEX_ATTRIB_ARRAY_TYPE       /usr/share/onyx/core/js/webgl.onyx      /^VERTEX_ATTRIB_ARRAY_TYPE           :: 0x8625$/
 VERTEX_SHADER  /usr/share/onyx/core/js/webgl.onyx      /^VERTEX_SHADER                    :: 0x8B31$/
 VIEWPORT       /usr/share/onyx/core/js/webgl.onyx      /^VIEWPORT                       :: 0x0BA2$/
-Vec2   src/gfx/quad_renderer.onyx      /^Vec2 :: struct {$/
 WAIT_FAILED    /usr/share/onyx/core/js/webgl.onyx      /^WAIT_FAILED                                   :: 0x911D$/
 ZERO   /usr/share/onyx/core/js/webgl.onyx      /^ZERO                           :: 0$/
+aabb_contains  src/aabb.onyx   /^aabb_contains :: proc (r: AABB, v: V2f) -> bool {$/
+aabb_intersects        src/aabb.onyx   /^aabb_intersects :: proc (r1: AABB, r2: AABB) -> bool {$/
 abs_f32        /usr/share/onyx/core/intrinsics.onyx    /^abs_f32      :: proc (val: f32) -> f32 #intrinsic ---$/
 abs_f64        /usr/share/onyx/core/intrinsics.onyx    /^abs_f64      :: proc (val: f64) -> f64 #intrinsic ---$/
 activeTexture  /usr/share/onyx/core/js/webgl.onyx      /^activeTexture                  :: proc (texture: GLenum) #foreign "gl" "activeTexture" ---$/
@@ -662,6 +668,10 @@ array_push /usr/share/onyx/core/array.onyx /^array_push :: proc (arr: ^[..] $T,
 array_remove   /usr/share/onyx/core/array.onyx /^array_remove :: proc (arr: ^[..] $T, elem: T) {$/
 array_sort     /usr/share/onyx/core/array.onyx /^array_sort :: proc (arr: ^[..] $T, cmp: proc (T, T) -> i32) {$/
 array_to_slice /usr/share/onyx/core/array.onyx /^array_to_slice :: proc (arr: ^[..] $T) -> [] T {$/
+atlas  src/main.onyx   /^atlas         : Atlas$/
+atlas_apply_to_quad    src/gfx/atlas.onyx      /^atlas_apply_to_quad :: proc (use atlas: ^Atlas, id: i32, quad: ^Quad) {$/
+atlas_create   src/gfx/atlas.onyx      /^atlas_create :: proc (tex: ^Texture) -> Atlas {$/
+atlas_map      src/gfx/atlas.onyx      /^atlas_map :: proc (use atlas: ^Atlas, id: i32, sprite: AtlasSprite) {$/
 attachShader   /usr/share/onyx/core/js/webgl.onyx      /^attachShader                   :: proc (program: GLProgram, shader: GLShader) -> GLProgram #foreign "gl" "attachShader" ---$/
 bezier_curve   src/font.onyx   /^bezier_curve :: proc (t: f32, ps: [] V2f) -> V2f {$/
 binary_reader_create   src/font.onyx   /^binary_reader_create :: proc (data: [] u8, initial_pos := 0) -> BinaryReader {$/
@@ -738,20 +748,28 @@ drawArrays        /usr/share/onyx/core/js/webgl.onyx      /^drawArrays                     :
 drawArraysInstanced    /usr/share/onyx/core/js/webgl.onyx      /^drawArraysInstanced            :: proc (mode: GLenum, first: GLint, count: GLsizei, instanceCount: GLsizei) #foreign "gl" "drawArraysInstanced" ---$/
 drawElements   /usr/share/onyx/core/js/webgl.onyx      /^drawElements                   :: proc (mode: GLenum, count: GLsizei, type: GLenum, offset: GLint) #foreign "gl" "drawElements" ---$/
 drawElementsInstanced  /usr/share/onyx/core/js/webgl.onyx      /^drawElementsInstanced          :: proc (mode: GLenum, count: GLsizei, type: GLenum, offset: GLint, instanceCount: GLsizei) #foreign "gl" "drawElementsInstanced" ---$/
+draw_quad      src/main.onyx   /^draw_quad :: proc (use rc: ^RenderContext, quad: ^Quad) {$/
+draw_quad_tree src/main.onyx   /^draw_quad_tree :: proc (qt: ^QuadTree($T), level := 0, corner := 4) {$/
 draw_rect      src/main.onyx   /^draw_rect :: proc (use rc: ^RenderContext, x: f32, y: f32, w: f32, h: f32) {$/
+draw_textured_rect     src/main.onyx   /^draw_textured_rect :: proc (use rc: ^RenderContext, x: f32, y: f32, w: f32, h: f32, tx: f32, ty: f32, tw: f32, th: f32) {$/
+dude_create_random     src/main.onyx   /^dude_create_random :: proc () -> Dude {$/
+dude_get_aabb  src/main.onyx   /^dude_get_aabb :: proc (use dude: ^Dude) -> AABB {$/
+dude_tree      src/main.onyx   /^dude_tree     : QuadTree(Dude)$/
+dude_try_move  src/main.onyx   /^dude_try_move :: proc (use dude: ^Dude, other_dudes: ^QuadTree(Dude)) {$/
+dude_update    src/main.onyx   /^dude_update :: proc (use dude: ^Dude, other_dudes: ^QuadTree(Dude)) {$/
+dudes  src/main.onyx   /^dudes         : [..] Dude$/
 enable /usr/share/onyx/core/js/webgl.onyx      /^enable                         :: proc (cap: GLenum) #foreign "gl" "enable" ---$/
 enableVertexAttribArray        /usr/share/onyx/core/js/webgl.onyx      /^enableVertexAttribArray        :: proc (index: GLuint) #foreign "gl" "enableVertexAttribArray" ---$/
+f64_to_string  /usr/share/onyx/core/string.onyx        /^f64_to_string :: proc (f: f64, buf: [] u8) -> string {$/
 finish /usr/share/onyx/core/js/webgl.onyx      /^finish                         :: proc () #foreign "gl" "finish" ---$/
 floor_f32      /usr/share/onyx/core/intrinsics.onyx    /^floor_f32    :: proc (val: f32) -> f32 #intrinsic ---$/
 floor_f64      /usr/share/onyx/core/intrinsics.onyx    /^floor_f64    :: proc (val: f64) -> f64 #intrinsic ---$/
 flush  /usr/share/onyx/core/js/webgl.onyx      /^flush                          :: proc () #foreign "gl" "flush" ---$/
-font_tex       src/main.onyx   /^font_tex      : Texture$/
 framebufferRenderbuffer        /usr/share/onyx/core/js/webgl.onyx      /^framebufferRenderbuffer        :: proc (target: GLenum, attachment: GLenum, renderbuffertarget: GLenum, renderbuffer: GLRenderbuffer) #foreign "gl" "framebufferRenderbuffer" ---$/
 framebufferTexture2D   /usr/share/onyx/core/js/webgl.onyx      /^framebufferTexture2D           :: proc (target: GLenum, attachment: GLenum, textarget: GLenum, texture: GLTexture, level: GLint) #foreign "gl" "framebufferTexture2D" ---$/
 framebufferTextureLayer        /usr/share/onyx/core/js/webgl.onyx      /^framebufferTextureLayer        :: proc (target: GLenum, attachment: GLenum, texture: GLTexture, level: GLint, layer: GLint) #foreign "gl" "framebufferTextureLayer" ---$/
 free   /usr/share/onyx/core/builtin.onyx       /^free :: proc (use a: Allocator, ptr: rawptr) {$/
 frontFace      /usr/share/onyx/core/js/webgl.onyx      /^frontFace                      :: proc (mode: GLenum) #foreign "gl" "frontFace" ---$/
-game_launch    src/main.onyx   /^game_launch :: proc () #foreign "game" "launch" ---$/
 generateMipmap /usr/share/onyx/core/js/webgl.onyx      /^generateMipmap                 :: proc (target: GLenum) #foreign "gl" "generateMipmap" ---$/
 getActiveAttrib        /usr/share/onyx/core/js/webgl.onyx      /^getActiveAttrib                :: proc (program: GLProgram, index: GLuint, out: ^GLActiveInfo) #foreign "gl" "getActiveAttrib" ---$/
 getActiveUniform       /usr/share/onyx/core/js/webgl.onyx      /^getActiveUniform               :: proc (program: GLProgram, index: GLuint, out: ^GLActiveInfo) #foreign "gl" "getActiveUniform" ---$/
@@ -763,8 +781,6 @@ getProgramParameter /usr/share/onyx/core/js/webgl.onyx      /^getProgramParameter
 getShaderParameter     /usr/share/onyx/core/js/webgl.onyx      /^getShaderParameter             :: proc (shader: GLShader, pname: GLenum) -> GLenum #foreign "gl" "getShaderParameter" ---$/
 getUniformLocation     /usr/share/onyx/core/js/webgl.onyx      /^getUniformLocation             :: proc (program: GLProgram, name: string) -> GLUniformLocation #foreign "gl" "getUniformLocation" ---$/
 getVertexAttribOffset  /usr/share/onyx/core/js/webgl.onyx      /^getVertexAttribOffset          :: proc (index: GLuint, pname: GLenum) #foreign "gl" "getVertexAttribOffset" ---$/
-glyph_data     src/main.onyx   /^glyph_data : [glyph_size * glyph_size] u8$/
-glyph_size     src/main.onyx   /^glyph_size :: 64$/
 heap_alloc     /usr/share/onyx/core/alloc.onyx /^heap_alloc :: proc (size_: u32, align: u32) -> rawptr {$/
 heap_alloc_proc        /usr/share/onyx/core/alloc.onyx /^heap_alloc_proc :: proc (data: rawptr, aa: AllocAction, size: u32, align: u32, oldptr: rawptr) -> rawptr {$/
 heap_allocator /usr/share/onyx/core/alloc.onyx /^heap_allocator : Allocator;$/
@@ -804,6 +820,7 @@ max_poly    /usr/share/onyx/core/math.onyx  /^max_poly :: proc (a: $T, b: T) -> T {$
 memory_copy    /usr/share/onyx/core/memory.onyx        /^memory_copy :: proc (dst_: rawptr, src_: rawptr, len: u32) {$/
 memory_grow    /usr/share/onyx/core/intrinsics.onyx    /^memory_grow  :: proc (val: i32) -> i32 #intrinsic ---$/
 memory_init    /usr/share/onyx/core/alloc.onyx /^memory_init :: proc () {$/
+memory_set     /usr/share/onyx/core/memory.onyx        /^memory_set :: proc (start: rawptr, length: u32, value: u8) {$/
 memory_size    /usr/share/onyx/core/intrinsics.onyx    /^memory_size  :: proc () -> i32 #intrinsic ---$/
 min_f32        /usr/share/onyx/core/intrinsics.onyx    /^min_f32      :: proc (lhs: f32, rhs: f32) -> f32 #intrinsic ---$/
 min_f64        /usr/share/onyx/core/intrinsics.onyx    /^min_f64      :: proc (lhs: f64, rhs: f64) -> f64 #intrinsic ---$/
@@ -816,7 +833,6 @@ or_i32      /usr/share/onyx/core/intrinsics.onyx    /^or_i32       :: proc (lhs: i32, rh
 or_i64 /usr/share/onyx/core/intrinsics.onyx    /^or_i64       :: proc (lhs: i64, rhs: i64) -> i64 #intrinsic ---$/
 output_string  /usr/share/onyx/core/sys/js.onyx        /^output_string :: proc (s: string) -> u32 #foreign "host" "print_str" ---$/
 pixelStorei    /usr/share/onyx/core/js/webgl.onyx      /^pixelStorei                    :: proc (pname: GLenum, param: GLenum) #foreign "gl" "pixelStorei" ---$/
-player src/main.onyx   /^player : Player$/
 poll   src/events.onyx /^poll :: proc (ev: ^Event) -> bool {$/
 poll_events    src/main.onyx   /^poll_events :: proc () {$/
 polygonOffset  /usr/share/onyx/core/js/webgl.onyx      /^polygonOffset                  :: proc (factor: GLfloat, units: GLfloat) #foreign "gl" "polygonOffset" ---$/
@@ -831,12 +847,15 @@ print_array       /usr/share/onyx/core/stdio.onyx /^print_array :: proc (arr: $T, sep
 print_bool     /usr/share/onyx/core/stdio.onyx /^print_bool    :: proc (b: bool) do string_builder_append(^print_buffer, b);$/
 print_buffer_flush     /usr/share/onyx/core/stdio.onyx /^print_buffer_flush :: proc () {$/
 print_cstring  /usr/share/onyx/core/stdio.onyx /^print_cstring :: proc (s: cstring) do string_builder_append(^print_buffer, s);$/
+print_f32      /usr/share/onyx/core/stdio.onyx /^print_f32     :: proc (n: f32)  do string_builder_append(^print_buffer, cast(f64) n);$/
+print_f64      /usr/share/onyx/core/stdio.onyx /^print_f64     :: proc (n: f64) do string_builder_append(^print_buffer, n);$/
 print_i32      /usr/share/onyx/core/stdio.onyx /^print_i32     :: proc (n: i32, base := 10)  do string_builder_append(^print_buffer, cast(i64) n, cast(u64) base);$/
 print_i64      /usr/share/onyx/core/stdio.onyx /^print_i64     :: proc (n: i64, base := 10l) do string_builder_append(^print_buffer, n, base);$/
 print_ptr      /usr/share/onyx/core/stdio.onyx /^print_ptr     :: proc (p: ^void) do string_builder_append(^print_buffer, cast(i64) p, 16l);$/
 print_range    /usr/share/onyx/core/stdio.onyx /^print_range :: proc (r: range, sep := " ") {$/
 print_string   /usr/share/onyx/core/stdio.onyx /^print_string  :: proc (s: string) {$/
 print_vec      src/vecmath.onyx        /^print_vec :: proc (v: V2($T)) {$/
+printf /usr/share/onyx/core/stdio.onyx /^printf :: proc (format: string, va: ...) {$/
 println        /usr/share/onyx/core/stdio.onyx /^println :: proc (x: $T) {$/
 process_event  src/input.onyx  /^process_event :: proc (use state: ^InputState, ev: ^event.Event) {$/
 ptrmap_clear   /usr/share/onyx/core/ptrmap.onyx        /^ptrmap_clear :: proc (use pmap: ^PtrMap) {$/
@@ -847,10 +866,15 @@ ptrmap_has        /usr/share/onyx/core/ptrmap.onyx        /^ptrmap_has :: proc (use pmap: ^Ptr
 ptrmap_init    /usr/share/onyx/core/ptrmap.onyx        /^ptrmap_init :: proc (use pmap: ^PtrMap, hash_count: i32 = 16) {$/
 ptrmap_put     /usr/share/onyx/core/ptrmap.onyx        /^ptrmap_put :: proc (use pmap: ^PtrMap, key: rawptr, value: rawptr) {$/
 quad_rebuffer_data     src/gfx/quad_renderer.onyx      /^quad_rebuffer_data :: proc (use qr: ^QuadRenderer) {$/
-quad_renderer_draw     src/gfx/quad_renderer.onyx      /^quad_renderer_draw :: proc (use qr: ^QuadRenderer) {$/
+quad_renderer_draw     src/gfx/quad_renderer.onyx      /^quad_renderer_draw :: proc (use qr: ^QuadRenderer, count := -1) {$/
 quad_renderer_init     src/gfx/quad_renderer.onyx      /^quad_renderer_init :: proc (use qr: ^QuadRenderer, initial_quads := 10) {$/
 quad_renderer_update_view      src/gfx/quad_renderer.onyx      /^quad_renderer_update_view :: proc (use qr: ^QuadRenderer) {$/
+quad_scratch_buffer    src/main.onyx   /^quad_scratch_buffer : [128 * 1024] u8;$/
 quad_update_at_index   src/gfx/quad_renderer.onyx      /^quad_update_at_index :: proc (use qr: ^QuadRenderer, idx: i32, quad: Quad) {$/
+quadtree_init  src/quad_tree.onyx      /^quadtree_init :: proc (qt: ^QuadTree($T), initial_region: AABB) {$/
+quadtree_insert        src/quad_tree.onyx      /^quadtree_insert :: proc (use qt: ^QuadTree($T), t: ^T, alloc := context.allocator) -> bool {$/
+quadtree_query src/quad_tree.onyx      /^quadtree_query :: proc (use qt: ^QuadTree($T), r: AABB, p: ^[..] ^T) {$/
+quadtree_subdivide     src/quad_tree.onyx      /^quadtree_subdivide :: proc (use qt: ^QuadTree($T), a: Allocator) {$/
 random /usr/share/onyx/core/random.onyx        /^random :: proc (s := ^seed) -> u32 {$/
 random_between /usr/share/onyx/core/random.onyx        /^random_between :: proc (lo: i32, hi: i32) -> i32 do return random() % (hi + 1 - lo) + lo;$/
 random_float   /usr/share/onyx/core/random.onyx        /^random_float :: proc (lo := 0.0f, hi := 1.0f) -> f32 {$/
@@ -881,7 +905,6 @@ shl_i64     /usr/share/onyx/core/intrinsics.onyx    /^shl_i64      :: proc (lhs: i64, r
 sin    /usr/share/onyx/core/math.onyx  /^sin :: proc (t_: f32) -> f32 {$/
 slr_i32        /usr/share/onyx/core/intrinsics.onyx    /^slr_i32      :: proc (lhs: i32, rhs: i32) -> i32 #intrinsic ---$/
 slr_i64        /usr/share/onyx/core/intrinsics.onyx    /^slr_i64      :: proc (lhs: i64, rhs: i64) -> i64 #intrinsic ---$/
-smile  src/main.onyx   /^smile         : Texture$/
 sqrt_f32       /usr/share/onyx/core/intrinsics.onyx    /^sqrt_f32     :: proc (val: f32) -> f32 #intrinsic ---$/
 sqrt_f64       /usr/share/onyx/core/intrinsics.onyx    /^sqrt_f64     :: proc (val: f64) -> f64 #intrinsic ---$/
 sqrt_i32       /usr/share/onyx/core/math.onyx  /^sqrt_i32 :: proc (x: i32) -> i32 {$/
@@ -896,6 +919,7 @@ string      /usr/share/onyx/core/builtin.onyx       /^string  :: #type []u8;$/
 string_advance_line    /usr/share/onyx/core/string.onyx        /^string_advance_line :: proc (str: ^string) {$/
 string_builder_add_bool        /usr/share/onyx/core/string.onyx        /^string_builder_add_bool :: proc (use sb: ^StringBuilder, b: bool) -> ^StringBuilder {$/
 string_builder_add_cstring     /usr/share/onyx/core/string.onyx        /^string_builder_add_cstring :: proc (use sb: ^StringBuilder, cstr: cstring) -> ^StringBuilder {$/
+string_builder_add_f64 /usr/share/onyx/core/string.onyx        /^string_builder_add_f64 :: proc (use sb: ^StringBuilder, f: f64) -> ^StringBuilder {$/
 string_builder_add_i64 /usr/share/onyx/core/string.onyx        /^string_builder_add_i64 :: proc (use sb: ^StringBuilder, n: i64, base := 10l) -> ^StringBuilder {$/
 string_builder_add_string      /usr/share/onyx/core/string.onyx        /^string_builder_add_string :: proc (use sb: ^StringBuilder, str: string) -> ^StringBuilder {$/
 string_builder_append  /usr/share/onyx/core/string.onyx        /^string_builder_append :: proc {$/
@@ -923,9 +947,10 @@ texSubImage2D      /usr/share/onyx/core/js/webgl.onyx      /^texSubImage2D
 texture_create src/gfx/texture.onyx    /^texture_create :: proc (texdata: [] u8, width: i32, height: i32, data_mode := TextureDataMode.RGB) -> Texture {$/
 texture_prepare        src/gfx/texture.onyx    /^texture_prepare :: proc (use tex: ^Texture) {$/
 texture_use    src/gfx/texture.onyx    /^texture_use :: proc (use tex: ^Texture) {$/
+textures       src/gfx/texture.onyx    /^textures : struct {$/
+textures_init  src/gfx/texture.onyx    /^textures_init :: proc () {$/
 trunc_f32      /usr/share/onyx/core/intrinsics.onyx    /^trunc_f32    :: proc (val: f32) -> f32 #intrinsic ---$/
 trunc_f64      /usr/share/onyx/core/intrinsics.onyx    /^trunc_f64    :: proc (val: f64) -> f64 #intrinsic ---$/
-ttf    src/main.onyx   /^ttf           : TrueTypeFont$/
 ttf_create     src/font.onyx   /^ttf_create :: proc (ttf_data: [] u8) -> TrueTypeFont {$/
 ttf_glyph_count        src/font.onyx   /^ttf_glyph_count :: proc (use ttf: ^TrueTypeFont) -> u32 {$/
 ttf_glyph_destroy      src/font.onyx   /^ttf_glyph_destroy :: proc (glyph: ^TTGlyph) {$/
@@ -963,6 +988,8 @@ v2_orientation      src/vecmath.onyx        /^v2_orientation :: proc (a: V2($T), b: V2(T), c
 v2_square_dist src/vecmath.onyx        /^v2_square_dist :: proc (a: V2($T), b: V2(T)) -> T {$/
 v2_sub src/vecmath.onyx        /^v2_sub :: proc (a: V2($T), b: V2(T)) -> V2(T) {$/
 validateProgram        /usr/share/onyx/core/js/webgl.onyx      /^validateProgram                :: proc (program: GLProgram) #foreign "gl" "validateProgram" ---$/
+vararg /usr/share/onyx/core/builtin.onyx       /^vararg :: #type ^struct {$/
+vararg_get     /usr/share/onyx/core/builtin.onyx       /^vararg_get :: proc (va: vararg, ret: ^$T) -> bool {$/
 vertexAttrib1f /usr/share/onyx/core/js/webgl.onyx      /^vertexAttrib1f                 :: proc (idx: GLuint, x: GLfloat) #foreign "gl" "vertexAttrib1f" ---$/
 vertexAttrib2f /usr/share/onyx/core/js/webgl.onyx      /^vertexAttrib2f                 :: proc (idx: GLuint, x: GLfloat, y: GLfloat) #foreign "gl" "vertexAttrib2f" ---$/
 vertexAttrib3f /usr/share/onyx/core/js/webgl.onyx      /^vertexAttrib3f                 :: proc (idx: GLuint, x: GLfloat, y: GLfloat, z: GLfloat) #foreign "gl" "vertexAttrib3f" ---$/