font rendering works! kinda
authorBrendan Hansen <brendan.f.hansen@gmail.com>
Tue, 22 Sep 2020 04:00:02 +0000 (23:00 -0500)
committerBrendan Hansen <brendan.f.hansen@gmail.com>
Tue, 22 Sep 2020 04:00:02 +0000 (23:00 -0500)
js/environment.js
res/Hack-Regular.ttf [new file with mode: 0644]
src/font.onyx
src/gfx/texture.onyx
src/main.onyx
src/vecmath.onyx
tags

index 84d0bb9b42a42138ba7180c6b2cbfd01364bed9d..17d8e53c2998c4a962043ca8aab69719ab836620 100644 (file)
@@ -49,6 +49,8 @@ return {
 
     game: { launch: start_game },
 
+    dummy: { breakable() { debugger; }, },
+
     event: {
         setup(esp, event_size) {
             // Indicies into a Uint32Array are not based on bytes,
diff --git a/res/Hack-Regular.ttf b/res/Hack-Regular.ttf
new file mode 100644 (file)
index 0000000..92a90cb
Binary files /dev/null and b/res/Hack-Regular.ttf differ
index eb0e3d440208f0b48fd6693dcffbe45f1599dd63..855cae7fa7e185d57ca6241bc85444a914187b88 100644 (file)
@@ -118,6 +118,22 @@ TrueTypeFont :: struct {
     font_direction_hint : i16;
     index_to_loc_format : TTIndexToLocFormat;
     glyph_data_format : i16;
+
+    hhea : struct {
+        version : u32;
+        ascent  : i16;
+        descent : i16;
+        line_gap : i16;
+        advance_width_max : u16;
+        min_left_side_bearing : i16;
+        min_right_side_bearing : i16;
+        x_max_extent : i16;
+        caret_slope_rise : i16;
+        caret_slope_run : i16;
+        caret_offset : i16;
+        metric_data_format : i16;
+        num_of_long_hor_metrics : u16;
+    };
 }
 
 TTTableInfo :: struct {
@@ -154,6 +170,9 @@ ttf_create :: proc (ttf_data: [] u8) -> TrueTypeFont {
     i32map_init(^ttf.table_map);
     array_init(^ttf.char_maps);
     ttf_read_offset_table(^ttf);
+    ttf_read_head_table(^ttf);
+    ttf_read_cmap_table(^ttf);
+    ttf_read_hhea_table(^ttf);
 
     return ttf;
 }
@@ -178,7 +197,6 @@ ttf_read_offset_table :: proc (use ttf: ^TrueTypeFont) {
         i32map_put(^table_map, tag_int, table_info);
 
         if !string_equal(tag, "head") {
-            // TODO: Calculate checksum to ensure everything looks right
             csum := ttf_calc_table_checksum(^reader, table_info.offset, table_info.length);
             if table_info.checksum != csum {
                 print("WARNING: Checksum for table '");
@@ -266,6 +284,12 @@ ttf_read_glyph :: proc (use ttf: ^TrueTypeFont, glyph_index: i32) -> ^TTGlyph {
     return glyph;
 }
 
+ttf_glyph_destroy :: proc (glyph: ^TTGlyph) {
+    array_free(^glyph.contour_ends);
+    array_free(^glyph.points);
+    cfree(glyph);
+}
+
 #private_file
 TTGlyphFlags :: enum #flags {
     On_Curve :: 0x01;
@@ -432,7 +456,7 @@ ttf_read_cmap0 :: proc (use ttf: ^TrueTypeFont) {
     cmap.cmap0.format = TTCmapFormat.Simple;
 
     glyphs : [..] u8;
-    array_init(^glyphs, 256); 
+    array_init(^glyphs, 256);
     for i: 0 .. 256 do array_push(^glyphs, read_u8(^reader));
 
     cmap.cmap0.glyph_indicies = array_to_slice(^glyphs);
@@ -444,6 +468,7 @@ ttf_read_cmap4 :: proc (use ttf: ^TrueTypeFont) {
     cmap : TTCmap;
     cmap.cmap4.format = TTCmapFormat.Segmented;
     map := ^cmap.cmap4;
+    i32map_init(^map.cache);
 
     map.seg_count = read_u16(^reader) >> ~~1;
     map.search_range = read_u16(^reader);
@@ -488,7 +513,7 @@ ttf_lookup_in_cmap0 :: proc (use ttf: ^TrueTypeFont, cmap: ^TTCmap0, charcode: u
 
 #private_file
 ttf_lookup_in_cmap4 :: proc (use ttf: ^TrueTypeFont, cmap: ^TTCmap4, charcode: u32) -> u32 {
-    // TODO: Lookup in the cache
+    if i32map_has(^cmap.cache, charcode) do return i32map_get(^cmap.cache, charcode);
 
     index := 0;
     for ^seg: cmap.segments {
@@ -505,9 +530,228 @@ ttf_lookup_in_cmap4 :: proc (use ttf: ^TrueTypeFont, cmap: ^TTCmap4, charcode: u
         }
     }
 
+    i32map_put(^cmap.cache, charcode, index);
+
     return index;
 }
 
+ttf_read_hhea_table :: proc (use ttf: ^TrueTypeFont) {
+    empty_table_hack := TTTableInfo.{};
+    hhea_table_info  := i32map_get(^table_map, string_to_beu32("hhea"), empty_table_hack);
+    seek(^reader, hhea_table_info.offset);
+
+    hhea.version = read_u32(^reader);
+    hhea.ascent  = read_fword(^reader);
+    hhea.descent = read_fword(^reader);
+    hhea.line_gap = read_fword(^reader);
+    hhea.advance_width_max = read_u16(^reader);
+    hhea.min_left_side_bearing = read_u16(^reader);
+    hhea.min_right_side_bearing = read_u16(^reader);
+    hhea.x_max_extent = read_fword(^reader);
+    hhea.caret_slope_rise = read_i16(^reader);
+    hhea.caret_slope_run = read_i16(^reader);
+    hhea.caret_offset = read_fword(^reader);
+    read_i16(^reader); // Reserved
+    read_i16(^reader); // Reserved
+    read_i16(^reader); // Reserved
+    read_i16(^reader); // Reserved
+    hhea.metric_data_format = read_i16(^reader);
+    hhea.num_of_long_hor_metrics = read_u16(^reader);
+}
+
+TTHorizontalMetrics :: struct {
+    advance_width     : u16;
+    left_side_bearing : i16;
+}
+
+ttf_lookup_horizontal_metrics :: proc (use ttf: ^TrueTypeFont, glyph_index: u32) -> TTHorizontalMetrics {
+    empty_table_hack := TTTableInfo.{};
+    hmtx_table_info  := i32map_get(^table_map, string_to_beu32("hmtx"), empty_table_hack);
+    offset := hmtx_table_info.offset;
+
+    hmtx : TTHorizontalMetrics;
+
+    nmets := cast(u32) hhea.num_of_long_hor_metrics;
+
+    if glyph_index < nmets {
+        offset += glyph_index * 4;
+        old := seek(^reader, offset);
+        defer seek(^reader, old);
+
+        hmtx.advance_width = read_u16(^reader);
+        hmtx.left_side_bearing = read_i16(^reader);
+
+    } else {
+        old := seek(^reader, offset + (nmets - 1) * 4);
+        defer seek(^reader, old);
+
+        hmtx.advance_width = read_u16(^reader);
+        seek(^reader, offset + nmets * 4 + 2 * (glyph_index - nmets));
+        hmtx.left_side_bearing = read_i16(^reader);
+    }
+
+    return hmtx;
+}
+
+
+// Renders a grayscale image
+// 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;
+
+    scale := cast(f32) height / ~~cast(i32) units_per_em;
+
+    state := 0;
+    count := 0;
+    contour_start := 0;
+
+    curr_x := 0;
+    curr_y := 0;
+
+    poly_points : [..] V2i;
+    array_init(^poly_points, 16);
+    defer array_free(^poly_points);
+
+    for i: 0 .. glyph.points.count {
+        p := glyph.points[i];
+
+
+        switch state {
+            case 0 {
+                // curr_x = cast(i32) p.x;
+                // curr_y = cast(i32) p.y;
+
+                array_push(^poly_points, V2i.{ ~~p.x, ~~p.y });
+                state = 1;
+            }
+
+            case 1 do if p.on_curve {
+                // draw_line(data, width, height, scale,
+                //     curr_x, curr_y,
+                //     ~~p.x, ~~p.y);
+
+                // curr_x = cast(i32) p.x;
+                // curr_y = cast(i32) p.y;
+                array_push(^poly_points, V2i.{ ~~p.x, ~~p.y });
+
+            } else {
+                state = 2;
+            }
+
+            case #default {
+                prev := glyph.points[i - 1];
+                if p.on_curve {
+                    // draw_quadratic_line(data, width, height, scale,
+                    //     curr_x, curr_y,
+                    //     ~~prev.x, ~~prev.y,
+                    //     ~~p.x, ~~p.y);
+
+                    // curr_x = cast(i32) p.x;
+                    // curr_y = cast(i32) p.y;
+                //     array_push(^poly_points, V2i.{ ~~prev.x, ~~prev.y });
+                    array_push(^poly_points, V2i.{ ~~p.x, ~~p.y });
+                    state = 1;
+
+                } else {
+                    //draw_quadratic_line(data, width, height, scale,
+                    //    curr_x, curr_y,
+                    //    ~~prev.x, ~~prev.y,
+                    //    ~~((prev.x + p.x) / ~~2), ~~((prev.y + p.y) / ~~2));
+
+                    //curr_x = cast(i32) ((prev.x + p.x) / ~~2);
+                    //curr_y = cast(i32) ((prev.y + p.y) / ~~2);
+                    array_push(^poly_points, V2i.{ ~~prev.x, ~~prev.y });
+                    array_push(^poly_points, V2i.{ ~~((prev.x + p.x) / ~~2), ~~((prev.y + p.y) / ~~2) });
+                }
+            }
+        }
+
+        if i == ~~glyph.contour_ends[count] {
+            prev := p;
+            p = glyph.points[contour_start];
+
+            if p.on_curve {
+                // draw_quadratic_line(data, width, height, scale,
+                //     curr_x, curr_y,
+                //     ~~prev.x, ~~prev.y,
+                //     ~~p.x, ~~p.y);
+
+                // curr_x = cast(i32) p.x;
+                // curr_y = cast(i32) p.y;
+                array_push(^poly_points, V2i.{ ~~prev.x, ~~prev.y });
+
+            } else {
+                // draw_quadratic_line(data, width, height, scale,
+                //     curr_x, curr_y,
+                //     ~~prev.x, ~~prev.y,
+                //     ~~((prev.x + p.x) / ~~2), ~~((prev.y + p.y) / ~~2));
+
+                // curr_x = cast(i32) ((prev.x + p.x) / ~~2);
+                // curr_y = cast(i32) ((prev.y + p.y) / ~~2);
+                array_push(^poly_points, V2i.{ ~~prev.x, ~~prev.y });
+                array_push(^poly_points, V2i.{ ~~((prev.x + p.x) / ~~2), ~~((prev.y + p.y) / ~~2) });
+            }
+
+            contour_start = i + 1;
+            count += 1;
+            state = 0;
+
+
+            // col := cast(u8) 255;
+            // if v2_orientation(poly_points[0], poly_points[1], poly_points[2]) < 0 do col = cast(u8) 0;
+
+            for p: poly_points do print_vec(p);
+            for y: 0 .. height do for x: 0 .. width {
+                if is_inside_polygon(array_to_slice(^poly_points), V2i.{ ~~(~~x / scale), ~~(~~y / scale) }) {
+                    curr := data[x + (height - 1 - y) * width];
+                    if curr == ~~0 do data[x + (height - 1 - y) * width] = cast(u8) 255;
+                    else           do data[x + (height - 1 - y) * width] = cast(u8) 0;
+                }
+            }
+
+            array_clear(^poly_points);
+        }
+    }
+}
+
+#private_file
+draw_line :: proc (data: ^u8, width: u32, height: u32, scale: f32, x0: u32, y0: u32, x1: u32, y1: u32) {
+    start := V2i.{ x0, y0 };
+    end   := V2i.{ x1, y1 };
+    dist  := sqrt_f32(~~v2_square_dist(start, end));
+
+    for t: 0 .. ~~dist {
+        p := v2_lerp(~~t / dist, start, end);
+        px := cast(i32) (~~p.x * scale);
+        py := cast(i32) (~~p.y * scale);
+        if px >= 0 && py >= 0 && px < width && py < height {
+            data[px + (height - 1 - py) * width] = cast(u8) 255;
+        }
+    }
+}
+
+#private_file
+draw_quadratic_line :: proc (data: ^u8, width: u32, height: u32, scale: f32, x0: u32, y0: u32, x1: u32, y1: u32, x2: u32, y2: u32) {
+    // draw_line(data, width, height, scale, x0, y0, x1, y1);
+    // draw_line(data, width, height, scale, x1, y1, x2, y2);
+
+    points : [3] V2f;
+    points[0] = V2f.{ ~~x0, ~~y0 };
+    points[1] = V2f.{ ~~x1, ~~y1 };
+    points[2] = V2f.{ ~~x2, ~~y2 };
+
+    for t: 0 .. 200 {
+        p := bezier_curve(~~t / 200.0f, points[0 .. 3]);
+
+        px := cast(i32) (~~p.x * scale);
+        py := cast(i32) (~~p.y * scale);
+        if px >= 0 && py >= 0 && px < width && py < height {
+            data[px + (height - 1 - py) * width] = cast(u8) 255;
+        }
+    }
+}
+
 #private_file
 ttf_calc_table_checksum :: proc (reader: ^BinaryReader, offset: u32, length: u32) -> u32 {
     old := seek(reader, offset);
index a3475ae2e4ee914399a15a96c75fe89be40ae942..74c601d4648bdeea05625a44fc252c9438b93dfc 100644 (file)
@@ -6,44 +6,51 @@ use package gl_utils as gl_utils
 
 Color3 :: struct { r: u8; g: u8; b: u8; }
 
+#private_file current_tex_id := 0;
+
 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
     // if it is stored in the data section.
-    data       : [] Color3;
+    data       : [] u8;
     width      : i32;
     height     : i32;
 
+    data_mode  : TextureDataMode;
+
     texture_id : gl.GLTexture;
 }
 
+TextureDataMode :: enum {
+    RGB        :: 0x01;
+    BRIGHTNESS :: 0x02;
+}
+
 // NOTE: This assumes that the data is raw u8 RGB packed color data.
-texture_create :: proc (texdata: [] Color3, width: i32, height: i32) -> Texture {
+texture_create :: proc (texdata: [] u8, width: i32, height: i32, data_mode := TextureDataMode.RGB) -> Texture {
     tex : Texture;
     tex.data = texdata;
     tex.width = width;
     tex.height = height;
+    tex.data_mode = data_mode;
     tex.texture_id = -1;
 
-    if width * height != tex.data.count {
-        println("Warning: Texture width and height does not match the size of data given.");
-    }
-
     return tex;
 }
 
 texture_prepare :: proc (use tex: ^Texture) {
     if texture_id != -1 do return;
 
-    texdata := (cast(^u8) data.data)[0 .. data.count * 3];
+    format := gl.RGB;
+    if data_mode == TextureDataMode.BRIGHTNESS do format = gl.LUMINANCE;
 
     texture_id = gl.createTexture();
-    gl.activeTexture(gl.TEXTURE0);
+    // gl.activeTexture(gl.TEXTURE0);
     gl.bindTexture(gl.TEXTURE_2D, texture_id);
-    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, width, height, 0, gl.RGB, gl.UNSIGNED_BYTE, texdata);
-    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
-    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
-    gl.bindTexture(gl.TEXTURE_2D, -1);
+    gl.texImage2D(gl.TEXTURE_2D, 0, format, width, height, 0, format, gl.UNSIGNED_BYTE, data);
+    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
+    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
+    // gl.bindTexture(gl.TEXTURE_2D, -1);
 }
 
 texture_use :: proc (use tex: ^Texture) {
index 6a94bccc17c60762c8a893256e450de55e598e1a..72c3cebf1158e002047297937bdab3dac1c2b188 100644 (file)
@@ -58,6 +58,8 @@ draw_rect :: proc (use rc: ^RenderContext, x: f32, y: f32, w: f32, h: f32) {
         pos   = Vec2.{ x, y },
         size  = Vec2.{ w, h },
         color = color,
+
+        tex_size = Vec2.{ 1.0f, 1.0f },
     });
 
     curr_quad_idx += 1;
@@ -67,6 +69,8 @@ render_context_flush :: proc (use rc: ^RenderContext) {
     quad_rebuffer_data(quad_renderer);
     quad_renderer_draw(quad_renderer);
 
+    for i: 0 .. curr_quad_idx do quad_update_at_index(quad_renderer, i, Quad.{});
+
     curr_quad_idx = 0;
 }
 
@@ -79,6 +83,8 @@ window_height := 0
 renderer      : RenderContext
 input_state   : input.InputState
 ttf           : TrueTypeFont
+font_tex      : Texture
+smile         : Texture
 
 Player :: struct { use pos : Vec2; }
 player : Player
@@ -120,44 +126,55 @@ update :: proc () {
     if input.key_down(^input_state, Key.ArrowRight) do player.x += player_speed;
 }
 
-draw :: proc () {
-    defer render_context_flush(^renderer);
+glyph_size :: 128
+glyph_data : [glyph_size * glyph_size] u8
 
+draw :: proc () {
     gl.clearColor(0.2f, 0.2f, 0.2f, 1.0f);
+    // gl.clearColor(1.0f, 1.0f, 1.0f, 1.0f);
     gl.clear(gl.COLOR_BUFFER_BIT);
 
-    renderer.color = Color4f32.{ 1f, 0f, 0f, 1f };
+    // renderer.color = Color4f32.{ 1f, 0f, 0f, 1f };
 
 
-    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 };
+    // 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 };
 
-    for i: 0 .. 200 {
-        pos := bezier_curve(~~i / 200.0f, points[0 .. 6]);
-        draw_rect(^renderer, pos.x, pos.y, 16f, 16f);
-    }
+    // for i: 0 .. 200 {
+    //     pos := bezier_curve(~~i / 200.0f, points[0 .. 6]);
+    //     draw_rect(^renderer, pos.x, pos.y, 16f, 16f);
+    // }
 
-    glyph : ^TTGlyph = null;
-    glyph_index := ttf_lookup_glyph_by_char(^ttf, ~~ #char "A");
-    glyph = ttf_read_glyph(^ttf, glyph_index);
-    defer cfree(glyph);
-    defer array_free(^glyph.points);
-    defer array_free(^glyph.contour_ends);
-
-    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);
-    }
+    // 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);
+    //     }
+    // }
+
+    // mets := ttf_lookup_horizontal_metrics(^ttf, glyph_index);
+    // println(cast(u32) mets.advance_width);
 
-    draw_rect(^renderer, ~~input_state.mouse.x, ~~input_state.mouse.y, 10f, 10f);
+    // 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);
+    // }
+
+
+    texture_use(^smile);
+    draw_rect(^renderer, ~~input_state.mouse.x, ~~input_state.mouse.y, 100f, 100f);
     draw_rect(^renderer, player.pos.x, player.pos.y, 100f, 100f);
+    render_context_flush(^renderer);
+
+    texture_use(^font_tex);
+    draw_rect(^renderer, 100f, 100f, 64f, 64f);
+    render_context_flush(^renderer);
 }
 
 // This procedure is called asynchronously from JS every frame.
@@ -182,39 +199,25 @@ main :: proc (args: [] cstring) {
 
     render_context_init(^renderer, null);
 
-    // CLEANUP: Maybe there should be a way to cast between slices?
-    // Automatically multiply / divide the count by the ratio of the type sizes?
-    // This could be very buggy
-    raw_image_data := #file_contents "res/smile.data";
-    image_data := (cast(^Color3) raw_image_data.data)[0 .. raw_image_data.count / 3];
-
-    smile := texture_create(image_data, 32, 32);
+    image_data := #file_contents "res/smile.data";
+    smile = texture_create(image_data, 32, 32);
     texture_prepare(^smile);
-    texture_use(^smile);
 
     event.init();
     input.init(^input_state);
 
-    ttf_data := #file_contents "res/Inconsolata-Regular.ttf";
+    ttf_data := #file_contents "res/Hack-Regular.ttf";
     ttf = ttf_create(ttf_data);
-    ttf_read_head_table(^ttf);
-    ttf_read_cmap_table(^ttf);
-
-    // for i: 0 .. ttf_glyph_count(^ttf) {
-    //     glyph := ttf_read_glyph(^ttf, i);
-    //     if glyph == null do continue;
-    //     defer cfree(glyph);
-
-    //     // println(glyph.points.count);
-    //     // for p: glyph.points {
-    //     //     print(p.on_curve);
-    //     //     print("   ");
-    //     //     print(cast(i32) p.x);
-    //     //     print(", ");
-    //     //     print(cast(i32) p.y);
-    //     //     print("\n");
-    //     // }
-    // }
+
+    glyph : ^TTGlyph = null;
+    glyph_index := ttf_lookup_glyph_by_char(^ttf, ~~ #char "%");
+    glyph = ttf_read_glyph(^ttf, glyph_index);
+    if glyph == null do return;
+    defer ttf_glyph_destroy(glyph);
+
+    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();
 }
index 0d638e07ddcb57edeaac5d487f97fd594609b6f8..034a48e383f975efa4661db37d281830cbb2877f 100644 (file)
@@ -1,5 +1,7 @@
 package vecmath
 
+use package core
+
 // BUG: Default values do not work on polymorphic structs.
 V2 :: struct ($T) { x: T; y: T; }
 
@@ -26,3 +28,82 @@ v2_mul :: proc (a: V2($T), scalar: T) -> V2(T) {
     ret.y = a.y * scalar;
     return ret;
 }
+
+v2_lerp :: proc (t: f32, start: V2($T), end: V2(T)) -> V2(T) {
+    ret : V2(T);
+    ret.x = ~~(t * ~~(end.x - start.x)) + start.x;
+    ret.y = ~~(t * ~~(end.y - start.y)) + start.y;
+    return ret;
+}
+
+v2_square_dist :: proc (a: V2($T), b: V2(T)) -> T {
+    return (a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y);
+}
+
+// -1 for counter-clockwise
+// 0 for colinear
+// 1 for clockwise
+v2_orientation :: proc (a: V2($T), b: V2(T), c: V2(T)) -> i32 {
+    val := (b.y - a.y) * (c.x - b.x) - (b.x - a.x) * (c.y - b.y);
+    if val == ~~0 do return 0;
+    if val > ~~0 do return 1;
+    return -1;
+}
+
+// assumes a b and c are colinear
+v2_on_segment :: proc (a: V2($T), b: V2(T), c: V2(T)) -> bool {
+    return b.x <= max_poly(a.x, c.x)
+        && b.x >= min_poly(a.x, c.x)
+        && b.y <= max_poly(a.y, c.y)
+        && b.y >= min_poly(a.y, c.y);
+}
+
+
+lines_intersect :: proc (p1: V2($T), q1: V2(T), p2: V2(T), q2: V2(T)) -> bool {
+    o1 := v2_orientation(p1, q1, p2);
+    o2 := v2_orientation(p1, q1, q2);
+    o3 := v2_orientation(p2, q2, p1);
+    o4 := v2_orientation(p2, q2, q1);
+
+    if o1 != o2 && o3 != o4 do return true;
+
+    if o1 == 0 && v2_on_segment(p1, p2, q1) do return true;
+    if o2 == 0 && v2_on_segment(p1, q2, q1) do return true;
+    if o3 == 0 && v2_on_segment(p2, p1, q2) do return true;
+    if o4 == 0 && v2_on_segment(p2, q1, q2) do return true;
+
+    return false;
+}
+
+is_inside_polygon :: proc (polygon: [] V2($T), p: V2(T)) -> bool {
+    if polygon.count < 3 do return false;
+
+    extreme : V2(T);
+    extreme.x = cast(T) 1000000; // Needs be infinity... close enough
+    extreme.y = p.y;
+
+    count := 0;
+    for i: 0 .. polygon.count {
+        next := (i + 1) % polygon.count;
+
+        if lines_intersect(polygon[i], polygon[next], p, extreme) {
+            if v2_orientation(polygon[i], p, polygon[next]) == 0 {
+                return v2_on_segment(polygon[i], p, polygon[next]);
+            }
+
+            count += 1;
+        }
+    }
+
+    return (count % 2) == 1;
+}
+
+
+
+print_vec :: proc (v: V2($T)) {
+    print("V2(");
+    print(v.x);
+    print(", ");
+    print(v.y);
+    println(")");
+}
diff --git a/tags b/tags
index f9f074e5296de223303deb090f41551492f014c5..96bdeda002e45c11c2c743d35bcbaabfa6ba6872 100644 (file)
--- a/tags
+++ b/tags
@@ -565,10 +565,12 @@ TTCmapBase        src/font.onyx   /^TTCmapBase  :: struct { format : TTCmapFormat; }$/
 TTCmapFormat   src/font.onyx   /^TTCmapFormat :: enum (u16) {$/
 TTGlyph        src/font.onyx   /^TTGlyph :: struct {$/
 TTGlyphPoint   src/font.onyx   /^TTGlyphPoint :: struct {$/
+TTHorizontalMetrics    src/font.onyx   /^TTHorizontalMetrics :: struct {$/
 TTIndexToLocFormat     src/font.onyx   /^TTIndexToLocFormat :: enum (i16) {$/
 TTSegment      src/font.onyx   /^TTSegment :: struct {$/
 TTTableInfo    src/font.onyx   /^TTTableInfo :: struct {$/
 Texture        src/gfx/texture.onyx    /^Texture :: struct {$/
+TextureDataMode        src/gfx/texture.onyx    /^TextureDataMode :: enum {$/
 TrueTypeFont   src/font.onyx   /^TrueTypeFont :: struct {$/
 UNIFORM_ARRAY_STRIDE   /usr/share/onyx/core/js/webgl.onyx      /^UNIFORM_ARRAY_STRIDE                          :: 0x8A3C$/
 UNIFORM_BLOCK_ACTIVE_UNIFORMS  /usr/share/onyx/core/js/webgl.onyx      /^UNIFORM_BLOCK_ACTIVE_UNIFORMS                 :: 0x8A42$/
@@ -744,6 +746,7 @@ finish      /usr/share/onyx/core/js/webgl.onyx      /^finish                         :: pr
 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" ---$/
@@ -761,6 +764,8 @@ 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 :: 128$/
 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;$/
@@ -785,10 +790,12 @@ input_state       src/main.onyx   /^input_state   : input.InputState$/
 invalidateFramebuffer  /usr/share/onyx/core/js/webgl.onyx      /^invalidateFramebuffer          :: proc (target: GLenum, attachments: string) #foreign "gl" "invalidateFramebuffer" ---$/
 invalidateSubFramebuffer       /usr/share/onyx/core/js/webgl.onyx      /^invalidateSubFramebuffer       :: proc (target: GLenum, attachments: string, x: GLint, y: GLint, width: GLsizei, height: GLsizei) #foreign "gl" "invalidateSubFramebuffer" ---$/
 isEnabled      /usr/share/onyx/core/js/webgl.onyx      /^isEnabled                      :: proc (cap: GLenum) -> GLboolean #foreign "gl" "isEnabled" ---$/
+is_inside_polygon      src/vecmath.onyx        /^is_inside_polygon :: proc (polygon: [] V2($T), p: V2(T)) -> bool {$/
 key_down       src/input.onyx  /^key_down :: proc (use state: ^InputState, key: Key) -> bool {$/
 key_just_down  src/input.onyx  /^key_just_down :: proc (use state: ^InputState, key: Key) -> bool {$/
 key_up src/input.onyx  /^key_up :: proc (use state: ^InputState, key: Key) -> bool {$/
 lineWidth      /usr/share/onyx/core/js/webgl.onyx      /^lineWidth                      :: proc (width: GLfloat) #foreign "gl" "lineWidth" ---$/
+lines_intersect        src/vecmath.onyx        /^lines_intersect :: proc (p1: V2($T), q1: V2(T), p2: V2(T), q2: V2(T)) -> bool {$/
 linkProgram    /usr/share/onyx/core/js/webgl.onyx      /^linkProgram                    :: proc (program: GLProgram) #foreign "gl" "linkProgram" ---$/
 link_program   src/utils/gl.onyx       /^link_program :: proc (vertex_shader: gl.GLShader, frag_shader: gl.GLShader) -> gl.GLProgram {$/
 main   src/main.onyx   /^main :: proc (args: [] cstring) {$/
@@ -830,6 +837,7 @@ print_i64   /usr/share/onyx/core/stdio.onyx /^print_i64     :: proc (n: i64, 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)) {$/
 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) {$/
@@ -874,8 +882,10 @@ 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 {$/
 stdio_init     /usr/share/onyx/core/stdio.onyx /^stdio_init :: proc () {$/
 stencilFunc    /usr/share/onyx/core/js/webgl.onyx      /^stencilFunc                    :: proc (func: GLenum, ref: GLint, mask: GLuint) #foreign "gl" "stencilFunc" ---$/
 stencilFuncSeparate    /usr/share/onyx/core/js/webgl.onyx      /^stencilFuncSeparate            :: proc (face: GLenum, func: GLenum, ref: GLint, mask: GLuint) #foreign "gl" "stencilFuncSeparate" ---$/
@@ -911,7 +921,7 @@ texImage2D  /usr/share/onyx/core/js/webgl.onyx      /^texImage2D                     :
 texParameterf  /usr/share/onyx/core/js/webgl.onyx      /^texParameterf                  :: proc (target: GLenum, pname: GLenum, param: GLfloat) #foreign "gl" "texParameterf" ---$/
 texParameteri  /usr/share/onyx/core/js/webgl.onyx      /^texParameteri                  :: proc (target: GLenum, pname: GLenum, param: GLint) #foreign "gl" "texParameteri" ---$/
 texSubImage2D  /usr/share/onyx/core/js/webgl.onyx      /^texSubImage2D                  :: proc (target: GLenum, level: GLint, xoff: GLint, yoff: GLint, width: GLsizei, height: GLsizei, format: GLenum, type: GLenum, pixels: string) #foreign "gl" "texSubImage2D" ---$/
-texture_create src/gfx/texture.onyx    /^texture_create :: proc (texdata: [] Color3, width: i32, height: i32) -> Texture {$/
+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) {$/
 trunc_f32      /usr/share/onyx/core/intrinsics.onyx    /^trunc_f32    :: proc (val: f32) -> f32 #intrinsic ---$/
@@ -919,15 +929,19 @@ trunc_f64 /usr/share/onyx/core/intrinsics.onyx    /^trunc_f64    :: proc (val: f64)
 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) {$/
 ttf_lookup_glyph_by_char       src/font.onyx   /^ttf_lookup_glyph_by_char :: proc (use ttf: ^TrueTypeFont, charcode: u32) -> u32 {$/
 ttf_lookup_glyph_offset        src/font.onyx   /^ttf_lookup_glyph_offset :: proc (use ttf: ^TrueTypeFont, glyph_index: i32) -> u32 {$/
+ttf_lookup_horizontal_metrics  src/font.onyx   /^ttf_lookup_horizontal_metrics :: proc (use ttf: ^TrueTypeFont, glyph_index: u32) -> TTHorizontalMetrics {$/
 ttf_read_cmap  src/font.onyx   /^ttf_read_cmap :: proc (use ttf: ^TrueTypeFont, offset: u32) {$/
 ttf_read_cmap0 src/font.onyx   /^ttf_read_cmap0 :: proc (use ttf: ^TrueTypeFont) {$/
 ttf_read_cmap4 src/font.onyx   /^ttf_read_cmap4 :: proc (use ttf: ^TrueTypeFont) {$/
 ttf_read_cmap_table    src/font.onyx   /^ttf_read_cmap_table :: proc (use ttf: ^TrueTypeFont) {$/
 ttf_read_glyph src/font.onyx   /^ttf_read_glyph :: proc (use ttf: ^TrueTypeFont, glyph_index: i32) -> ^TTGlyph {$/
 ttf_read_head_table    src/font.onyx   /^ttf_read_head_table :: proc (use ttf: ^TrueTypeFont) {$/
+ttf_read_hhea_table    src/font.onyx   /^ttf_read_hhea_table :: proc (use ttf: ^TrueTypeFont) {$/
 ttf_read_offset_table  src/font.onyx   /^ttf_read_offset_table :: proc (use ttf: ^TrueTypeFont) {$/
+ttf_render_glyph       src/font.onyx   /^ttf_render_glyph :: proc (use ttf: ^TrueTypeFont, glyph: ^TTGlyph, data: ^u8, width: u32, height: u32) {$/
 uniform1f      /usr/share/onyx/core/js/webgl.onyx      /^uniform1f                      :: proc (loc: GLUniformLocation, x: GLfloat) #foreign "gl" "uniform1f" ---$/
 uniform1i      /usr/share/onyx/core/js/webgl.onyx      /^uniform1i                      :: proc (loc: GLUniformLocation, x: GLint) #foreign "gl" "uniform1i" ---$/
 uniform2f      /usr/share/onyx/core/js/webgl.onyx      /^uniform2f                      :: proc (loc: GLUniformLocation, x: GLfloat, y: GLfloat) #foreign "gl" "uniform2f" ---$/
@@ -942,7 +956,11 @@ uniformMatrix4     /usr/share/onyx/core/js/webgl.onyx      /^uniformMatrix4
 update src/main.onyx   /^update :: proc () {$/
 useProgram     /usr/share/onyx/core/js/webgl.onyx      /^useProgram                     :: proc (program: GLProgram) #foreign "gl" "useProgram" ---$/
 v2_add src/vecmath.onyx        /^v2_add :: proc (a: V2($T), b: V2(T)) -> V2(T) {$/
+v2_lerp        src/vecmath.onyx        /^v2_lerp :: proc (t: f32, start: V2($T), end: V2(T)) -> V2(T) {$/
 v2_mul src/vecmath.onyx        /^v2_mul :: proc (a: V2($T), scalar: T) -> V2(T) {$/
+v2_on_segment  src/vecmath.onyx        /^v2_on_segment :: proc (a: V2($T), b: V2(T), c: V2(T)) -> bool {$/
+v2_orientation src/vecmath.onyx        /^v2_orientation :: proc (a: V2($T), b: V2(T), c: V2(T)) -> i32 {$/
+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" ---$/
 vertexAttrib1f /usr/share/onyx/core/js/webgl.onyx      /^vertexAttrib1f                 :: proc (idx: GLuint, x: GLfloat) #foreign "gl" "vertexAttrib1f" ---$/