From: Brendan Hansen Date: Tue, 6 Oct 2020 20:11:39 +0000 (-0500) Subject: frame rate independence and font rendering X-Git-Url: https://git.brendanfh.com/?a=commitdiff_plain;h=907170ea797c06911ab3d875f2cc13f927a0a3bc;p=onyx-game.git frame rate independence and font rendering --- diff --git a/js/environment.js b/js/environment.js index b004561..d011d8d 100644 --- a/js/environment.js +++ b/js/environment.js @@ -9,7 +9,7 @@ async function start_game() { while (true) { await anim_frame(); - WASM_EXPORTS.new_frame_callback(); + WASM_EXPORTS.loop(); } } diff --git a/res/tilemap.data b/res/tilemap.data index e53b233..3c0ea7a 100644 Binary files a/res/tilemap.data and b/res/tilemap.data differ diff --git a/res/tilemap.png b/res/tilemap.png index f165b3d..351e18d 100644 Binary files a/res/tilemap.png and b/res/tilemap.png differ diff --git a/src/gfx/atlas.onyx b/src/gfx/atlas.onyx index ae92c1c..03f227c 100644 --- a/src/gfx/atlas.onyx +++ b/src/gfx/atlas.onyx @@ -28,8 +28,18 @@ atlas_map :: proc (use atlas: ^Atlas, id: i32, sprite: AtlasSprite) { i32map_put(^map, id, sprite); } -atlas_lookup :: proc (use atlas: ^Atlas, id: i32) -> AtlasSprite { +atlas_lookup :: proc (use atlas: ^Atlas, id: i32, offset := 0) -> AtlasSprite { sprite := i32map_get(^map, id, AtlasSprite.{}); + + if offset != 0 { + sprite.x += ~~offset * sprite.w; + + while sprite.x >= ~~texture.width { + sprite.y += sprite.h; + sprite.x -= ~~texture.width; + } + } + sprite.x /= ~~texture.width; sprite.y /= ~~texture.width; sprite.w /= ~~texture.width; diff --git a/src/gfx/font.onyx b/src/gfx/font.onyx new file mode 100644 index 0000000..f3e4415 --- /dev/null +++ b/src/gfx/font.onyx @@ -0,0 +1,33 @@ +package gfx + +use package core +use package globals +use package vecmath +use package main { RenderContext, draw_textured_rect } + +#private_file +font_chars :: "0123456789!\"#$% ABCDEFGHIJKLMNOPQRSTUVWXYZ'()*+,abcdefghijklmnopqrstuvwxyz,-./\\,^:;<=>?[]~|_"; + +#private_file +char_to_tex_offset :: proc (ch: u8) -> u32 { + loc := 0; + for c: font_chars { + if c == ch do break; + loc += 1; + } + return loc; +} + +draw_text :: proc (renderer: ^RenderContext, s: string, pos: V2f, scale := 1.0f) { + x := pos.x; + y := pos.y; + + font_size := scale * 16.0f; + + for ch: s { + char_sprite := atlas_lookup(^atlas, 2, char_to_tex_offset(ch)); + draw_textured_rect(renderer, x, y, font_size, font_size, char_sprite.x, char_sprite.y, char_sprite.w, char_sprite.h); + + x += font_size * 0.85f; + } +} \ No newline at end of file diff --git a/src/input.onyx b/src/input.onyx index ba86a0f..00ea2ee 100644 --- a/src/input.onyx +++ b/src/input.onyx @@ -64,29 +64,29 @@ process_event :: proc (use state: ^InputState, ev: ^event.Event) { } case MouseDown { - mouse.dx += (ev.mouse.pos_x - half_window_width) - mouse.x; - mouse.dy += (ev.mouse.pos_y - half_window_height) - mouse.y; - mouse.x = ev.mouse.pos_x - half_window_width; - mouse.y = ev.mouse.pos_y - half_window_height; + mouse.dx += ev.mouse.pos_x - mouse.x; + mouse.dy += ev.mouse.pos_y - mouse.y; + mouse.x = ev.mouse.pos_x; + mouse.y = ev.mouse.pos_y; mouse.buttons_just_down[cast(u32) ev.mouse.button] = !mouse.buttons_down[cast(u32) ev.mouse.button]; mouse.buttons_down[cast(u32) ev.mouse.button] = true; } case MouseUp { - mouse.dx += (ev.mouse.pos_x - half_window_width) - mouse.x; - mouse.dy += (ev.mouse.pos_y - half_window_height) - mouse.y; - mouse.x = ev.mouse.pos_x - half_window_width; - mouse.y = ev.mouse.pos_y - half_window_height; + mouse.dx += ev.mouse.pos_x - mouse.x; + mouse.dy += ev.mouse.pos_y - mouse.y; + mouse.x = ev.mouse.pos_x; + mouse.y = ev.mouse.pos_y; mouse.buttons_down[cast(u32) ev.mouse.button] = false; mouse.buttons_just_down[cast(u32) ev.mouse.button] = false; } case MouseMove { - mouse.dx += (ev.mouse.pos_x - half_window_width) - mouse.x; - mouse.dy += (ev.mouse.pos_y - half_window_height) - mouse.y; - mouse.x = ev.mouse.pos_x - half_window_width; - mouse.y = ev.mouse.pos_y - half_window_height; + mouse.dx += ev.mouse.pos_x - mouse.x; + mouse.dy += ev.mouse.pos_y - mouse.y; + mouse.x = ev.mouse.pos_x; + mouse.y = ev.mouse.pos_y; } case MouseWheel { diff --git a/src/main.onyx b/src/main.onyx index 5016653..af6df41 100644 --- a/src/main.onyx +++ b/src/main.onyx @@ -8,6 +8,7 @@ package main #include_file "gfx/quad_renderer" #include_file "gfx/texture" #include_file "gfx/atlas" +#include_file "gfx/font" #include_file "events" #include_file "input" #include_file "font" @@ -92,7 +93,7 @@ draw_quad :: proc (use rc: ^RenderContext, quad: ^Quad) { } render_context_ui :: proc (use rc: ^RenderContext) { - quad_renderer_update_world(quad_renderer, 1.0f, 1.0f, 0.0f, 0.0f); + quad_renderer_update_world(quad_renderer, 1.0f, 1.0f, ~~-half_window_width, ~~-half_window_height); quad_rebuffer_data(quad_renderer); quad_renderer_draw(quad_renderer, curr_quad_idx); @@ -136,17 +137,17 @@ poll_events :: proc () { quad_scratch_buffer : [512 * 1024] u8; simulating := true; -update :: proc () { +update :: proc (dt: f32) { input.preupdate(^input_state); defer input.postupdate(^input_state); poll_events(); if input.key_just_down(^input_state, Key.Space) do simulating = !simulating; - if input.key_down(^input_state, Key.ArrowUp) do renderer.trans_y += 15.0f / renderer.scale; - if input.key_down(^input_state, Key.ArrowDown) do renderer.trans_y -= 15.0f / renderer.scale; - if input.key_down(^input_state, Key.ArrowLeft) do renderer.trans_x += 15.0f / renderer.scale; - if input.key_down(^input_state, Key.ArrowRight) do renderer.trans_x -= 15.0f / renderer.scale; + if input.key_down(^input_state, Key.ArrowUp) do renderer.trans_y += (500.0f / renderer.scale) * dt; + if input.key_down(^input_state, Key.ArrowDown) do renderer.trans_y -= (500.0f / renderer.scale) * dt; + if input.key_down(^input_state, Key.ArrowLeft) do renderer.trans_x += (500.0f / renderer.scale) * dt; + if input.key_down(^input_state, Key.ArrowRight) do renderer.trans_x -= (500.0f / renderer.scale) * dt; if input_state.mouse.wheel_ups > ~~0 do renderer.scale *= 1.125f; if input_state.mouse.wheel_downs > ~~0 do renderer.scale /= 1.125f; @@ -165,10 +166,10 @@ update :: proc () { quadtree_init(^dude_tree, AABB.{ ~~-half_window_width, ~~-half_window_height, ~~window_width, ~~window_height }); for ^d: dudes do quadtree_insert(^dude_tree, d, quad_alloc); - for ^d: dudes do dude_update(d, ^dude_tree); + for ^d: dudes do dude_update(d, dt, ^dude_tree); for ^d: dudes { - if d.can_move_x do d.pos.x += d.vel.x; - if d.can_move_y do d.pos.y += d.vel.y; + if d.can_move_x do d.pos.x += d.vel.x * dt; + if d.can_move_y do d.pos.y += d.vel.y * dt; if !d.can_move_x && !d.can_move_y do d.couldnt_move_count += ~~1; } @@ -204,6 +205,12 @@ draw :: proc () { // UI Rendering renderer.color = Color4f32.{ 0.0f, 0.0f, 0.0f, 1.0f }; draw_rect(^renderer, ~~input_state.mouse.x, ~~input_state.mouse.y, 10f, 10f); + + renderer.color = Color4f32.{ 1.0f, 1.0f, 1.0f, 1.0f }; + draw_rect(^renderer, 10.0f, 10.0f, 500.0f, 50.0f); + + renderer.color = Color4f32.{ 0.0f, 0.0f, 0.0f, 1.0f }; + draw_text(^renderer, "Hello. Test(12486),$! 0AaQq:", V2f.{ 10.0f, 10.0f }, 1.0f); render_context_ui(^renderer); } @@ -211,8 +218,19 @@ draw :: proc () { debugger :: proc () #foreign "dummy" "breakable" --- // This procedure is called asynchronously from JS every frame. -new_frame_callback :: proc () #export { - update(); + +// @CLEANUP: Add local persistant variables so this can go in the loop() body. +last_time := 0; + +loop :: proc () #export { + time_now :: proc () -> u32 #foreign "time" "now" ---; + + curr_time := time_now(); + if last_time == 0 do last_time = curr_time; + delta := curr_time - last_time; + last_time = curr_time; + + update(~~delta / 1000.0f); draw(); } @@ -237,6 +255,8 @@ main :: proc (args: [] cstring) { atlas_map(^atlas, 0, AtlasSprite.{ x = 16.0f, y = 0.0f, w = 16.0f, h = 16.0f }); atlas_map(^atlas, 1, AtlasSprite.{ x = 32.0f, y = 0.0f, w = 16.0f, h = 16.0f }); + atlas_map(^atlas, 2, AtlasSprite.{ x = 0.0f, y = 160.0f, w = 16.0f, h = 16.0f }); + event.init(); input.init(^input_state); @@ -245,7 +265,7 @@ main :: proc (args: [] cstring) { Dude_Color_Table[2] = Color4f32.{ 0.2f, 0.2f, 1.0f, 1.0f }; array_init(^dudes); - for i: 0 .. 500 { + for i: 0 .. 1000 { array_push(^dudes, dude_create_random()); } @@ -278,32 +298,31 @@ dude_create_random :: proc () -> Dude { }; } -dude_update :: proc (use dude: ^Dude, other_dudes: ^QuadTree(Dude)) { +dude_update :: proc (use dude: ^Dude, dt: f32, other_dudes: ^QuadTree(Dude)) { if random_between(0, 100) < 1 || couldnt_move_count >= ~~2 { - vel.x = random_float(-3.0f, 3.0f); - vel.y = random_float(-3.0f, 3.0f); + vel.x = random_float(-300.0f, 300.0f); + vel.y = random_float(-300.0f, 300.0f); couldnt_move_count = ~~0; } + dude_try_move(dude, dt, other_dudes); + if tile := tilemap_screen_coord_to_tile(^tilemap, pos); tile != null { target_r := cast(u8) cast(u32) (color.r * 255.0f); target_g := cast(u8) cast(u32) (color.g * 255.0f); target_b := cast(u8) cast(u32) (color.b * 255.0f); - diff_r := cast(i32) (target_r - tile.r) >>> 2; - diff_g := cast(i32) (target_g - tile.g) >>> 2; - diff_b := cast(i32) (target_b - tile.b) >>> 2; + diff_r := (cast(i32) target_r - cast(i32) tile.r) >>> 2; + diff_g := (cast(i32) target_g - cast(i32) tile.g) >>> 2; + diff_b := (cast(i32) target_b - cast(i32) tile.b) >>> 2; tile.r += ~~diff_r; tile.g += ~~diff_g; tile.b += ~~diff_b; } - - - dude_try_move(dude, other_dudes); } -dude_try_move :: proc (use dude: ^Dude, other_dudes: ^QuadTree(Dude)) { +dude_try_move :: proc (use dude: ^Dude, dt: f32, other_dudes: ^QuadTree(Dude)) { old_pos := pos; potential_dudes : [..] ^Dude; @@ -315,7 +334,7 @@ dude_try_move :: proc (use dude: ^Dude, other_dudes: ^QuadTree(Dude)) { collided := false; - pos.x += vel.x; + pos.x += vel.x * dt; if pos.x - size < ~~-half_window_width || pos.x + size >= ~~half_window_width do collided = true; dude_aabb := dude_get_aabb(dude); @@ -328,11 +347,11 @@ dude_try_move :: proc (use dude: ^Dude, other_dudes: ^QuadTree(Dude)) { } can_move_x = !collided; - pos.x -= vel.x; + pos.x -= vel.x * dt; collided = false; - pos.y += vel.y; + pos.y += vel.y * dt; if pos.y - size < ~~-half_window_height || pos.y + size >= ~~half_window_height do collided = true; dude_aabb = dude_get_aabb(dude); @@ -345,7 +364,7 @@ dude_try_move :: proc (use dude: ^Dude, other_dudes: ^QuadTree(Dude)) { } can_move_y = !collided; - pos.y -= vel.y; + pos.y -= vel.y * dt; } dude_get_aabb :: proc (use dude: ^Dude) -> AABB { diff --git a/src/quad_tree.onyx b/src/quad_tree.onyx index 58b3550..7c3f72d 100644 --- a/src/quad_tree.onyx +++ b/src/quad_tree.onyx @@ -2,6 +2,7 @@ package quad_tree use package core use package aabb +use package vecmath #private_file QUAD_TREE_MAX_POINTS :: 4; @@ -60,7 +61,7 @@ quadtree_subdivide :: proc (use qt: ^QuadTree($T), a: Allocator) { } quadtree_insert :: proc (use qt: ^QuadTree($T), t: ^T, alloc := context.allocator) -> bool { - pos := t.pos; // T is expected to have a 'pos' element. + pos: V2f = t.pos; // T is expected to have a 'pos' element. if !aabb_contains(region, pos) do return false; diff --git a/src/shaders/quad.frag b/src/shaders/quad.frag index 59ab7ef..2ab7cd2 100644 --- a/src/shaders/quad.frag +++ b/src/shaders/quad.frag @@ -13,6 +13,11 @@ void main() { if (v_tex_pos.x < 0.0) { fragColor = v_col; } else { - fragColor = v_col * texture(tex, v_tex_pos); + vec4 t_col = texture(tex, v_tex_pos); + if (t_col == vec4(1.0, 0.0, 1.0, 1.0)) { + fragColor = vec4(0.0, 0.0, 0.0, 0.0); + } else { + fragColor = v_col * texture(tex, v_tex_pos); + } } } diff --git a/tags b/tags index 4dd576e..92e0848 100644 --- a/tags +++ b/tags @@ -674,7 +674,7 @@ array_to_slice /usr/share/onyx/core/array.onyx /^array_to_slice :: proc (arr: ^[ atlas src/globals.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_lookup src/gfx/atlas.onyx /^atlas_lookup :: proc (use atlas: ^Atlas, id: i32) -> AtlasSprite {$/ +atlas_lookup src/gfx/atlas.onyx /^atlas_lookup :: proc (use atlas: ^Atlas, id: i32, offset := 0) -> AtlasSprite {$/ 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 {$/ @@ -755,12 +755,13 @@ drawElements /usr/share/onyx/core/js/webgl.onyx /^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_rect src/main.onyx /^draw_rect :: proc (use rc: ^RenderContext, x: f32, y: f32, w: f32, h: f32) {$/ +draw_text src/gfx/font.onyx /^draw_text :: proc (renderer: ^RenderContext, s: string, pos: V2f, scale := 1.0f) {$/ 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/globals.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)) {$/ +dude_try_move src/main.onyx /^dude_try_move :: proc (use dude: ^Dude, dt: f32, other_dudes: ^QuadTree(Dude)) {$/ +dude_update src/main.onyx /^dude_update :: proc (use dude: ^Dude, dt: f32, other_dudes: ^QuadTree(Dude)) {$/ dudes src/globals.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" ---$/ @@ -815,10 +816,12 @@ is_inside_polygon src/vecmath.onyx /^is_inside_polygon :: proc (polygon: [] V2($ 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 {$/ +last_time src/main.onyx /^last_time := 0;$/ 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 {$/ +loop src/main.onyx /^loop :: proc () #export {$/ main src/main.onyx /^main :: proc (args: [] cstring) {$/ max_f32 /usr/share/onyx/core/intrinsics.onyx /^max_f32 :: proc (lhs: f32, rhs: f32) -> f32 #intrinsic ---$/ max_f64 /usr/share/onyx/core/intrinsics.onyx /^max_f64 :: proc (lhs: f64, rhs: f64) -> f64 #intrinsic ---$/ @@ -833,7 +836,6 @@ min_f64 /usr/share/onyx/core/intrinsics.onyx /^min_f64 :: proc (lhs: f64, r min_poly /usr/share/onyx/core/math.onyx /^min_poly :: proc (a: $T, b: T) -> T {$/ nearest_f32 /usr/share/onyx/core/intrinsics.onyx /^nearest_f32 :: proc (val: f32) -> f32 #intrinsic ---$/ nearest_f64 /usr/share/onyx/core/intrinsics.onyx /^nearest_f64 :: proc (val: f64) -> f64 #intrinsic ---$/ -new_frame_callback src/main.onyx /^new_frame_callback :: proc () #export {$/ null /usr/share/onyx/core/builtin.onyx /^null :: cast(rawptr) 0;$/ or_i32 /usr/share/onyx/core/intrinsics.onyx /^or_i32 :: proc (lhs: i32, rhs: i32) -> i32 #intrinsic ---$/ or_i64 /usr/share/onyx/core/intrinsics.onyx /^or_i64 :: proc (lhs: i64, rhs: i64) -> i64 #intrinsic ---$/ @@ -993,7 +995,7 @@ uniform4i /usr/share/onyx/core/js/webgl.onyx /^uniform4i :: uniformMatrix2 /usr/share/onyx/core/js/webgl.onyx /^uniformMatrix2 :: proc (loc: GLUniformLocation, transpose: GLboolean, value: GLMat2) #foreign "gl" "uniformMatrix2" ---$/ uniformMatrix3 /usr/share/onyx/core/js/webgl.onyx /^uniformMatrix3 :: proc (loc: GLUniformLocation, transpose: GLboolean, value: GLMat3) #foreign "gl" "uniformMatrix3" ---$/ uniformMatrix4 /usr/share/onyx/core/js/webgl.onyx /^uniformMatrix4 :: proc (loc: GLUniformLocation, transpose: GLboolean, value: GLMat4) #foreign "gl" "uniformMatrix4" ---$/ -update src/main.onyx /^update :: proc () {$/ +update src/main.onyx /^update :: proc (dt: f32) {$/ 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_equal src/vecmath.onyx /^v2_equal :: proc (a: V2($T), b: V2(T)) -> bool {$/