#import "Window_Creation"; Debug :: #import "Debug"; Simp :: #import "Simp"; Input :: #import "Input"; #import "Math"; #import "Basic"; Camera :: struct { pos: Vector2; zoom: float = 1.0; }; window: Window_Type; quit: bool; dt: float; panning: bool = false; camera : Camera; mouse_frozen: bool; mouse_frozen_at: Vector2; last_time: float64; last_mouse_pos: Vector2; window_width : s32 = 1280; window_height : s32 = 720; dbgprint :: print; // so can be detected and removed #scope_file; normalize :: (using v: *Vector2) -> float { sq := sqrt(x*x + y*y); factor := 1.0 / sq; x *= factor; y *= factor; return sq; } normalize :: (v: Vector2) -> Vector2 #must { normalize(*v); return v; } xy :: (x: int, y: int) -> Vector2 { return xy(cast(float)x, cast(float)y); } #scope_export; WHITE :: Vector4.{1.0, 1.0, 1.0, 1.0}; RED :: Vector4.{1.0, 0.0, 0.0, 1.0}; GREEN :: Vector4.{0.0, 1.0, 0.0, 1.0}; ORANGE :: Vector4.{1.0, 0xA5/255, 0.0, 1.0}; DrawingSettings :: struct { world_space: bool = true; color: Vector4 = WHITE; }; line :: (using d: DrawingSettings, _from: Vector2, _to: Vector2, thickness: float = 1.0) { width := thickness; from := _from; to := _to; if world_space { from = world_to_screen(from); to = world_to_screen(to); } Simp.set_shader_for_color(true); normal := rotate(unit_vector(to - from), PI/2.0); Simp.immediate_quad(from + normal*width, from - normal*width, to + normal*width, to - normal*width, color = color); } vector :: (d: DrawingSettings, from: Vector2, vector: Vector2) { line(d, from, from+vector); arrow_head_length := length(vector) * 0.5; // arrow head line(d, from + vector, from + vector + rotate(normalize(vector)*arrow_head_length, PI/2.0 + PI/4.0)); line(d, from + vector, from + vector + rotate(normalize(vector)*arrow_head_length, -PI/2.0 + -PI/4.0)); } LastingPip :: struct { alive_for: float; pos: Vector2; d: DrawingSettings; }; pips : [10] LastingPip; push_pip :: (d: DrawingSettings, at: Vector2) { for * pips { if it.alive_for <= 0.0 { it.alive_for = 1.0; it.pos = at; it.d = d; break; } } } draw_pips :: (dt: float) { for * pips if it.alive_for > 0.0 { d := it.d; d.color.w *= it.alive_for; pip(d, it.pos); it.alive_for -= dt; } } pip :: (using d: DrawingSettings, _at: Vector2, size: float = 0.05) { at := _at; Simp.set_shader_for_color(true); if world_space { at = world_to_screen(at); size /= camera.zoom; } Simp.immediate_quad(at + xy(-size, size), at + xy(size, size), at + xy(size, -size), at + xy(-size, -size), color = color); } font: *Simp.Dynamic_Font; text :: (using d: DrawingSettings, t: string, p: Vector2) { drawing_at := p; if world_space { drawing_at = world_to_screen(drawing_at); } Simp.draw_text(font, xx drawing_at.x, xx drawing_at.y, t, color = color); } init :: () { window = create_window(window_width, window_height, "Test Kit"); // Actual render size in pixels can be different from the window dimensions we specified above (for example on high-resolution displays on macOS/iOS). window_width, window_height = Simp.get_render_dimensions(window); camera.pos = -xy(window_width, window_height)/2.0; camera.zoom = 0.01; Simp.set_render_target(window); font = Simp.get_font_at_size(".", "Roboto-Regular.ttf", 18); assert(font != null); } // screen coordinates mouse_screen :: () -> Vector2 { if mouse_frozen return mouse_frozen_at; x, y := get_mouse_pointer_position(); pos: Vector2 = xy(cast(float)x, cast(float)y); // simp is lower left is (0, 0) and y+ is up pos.y = window_height - pos.y; return pos; } mouse_world :: () -> Vector2 { return screen_to_world(mouse_screen()); } // query quit to see if should quit. Call this in a loop frame_start :: () { dt = cast(float)(get_time() - last_time); last_time = get_time(); Input.update_window_events(); mouse_delta := mouse_screen() - last_mouse_pos; // using Input.mouse_delta_x seems to incorrectly accumulate last_mouse_pos = mouse_screen(); for Input.get_window_resizes() { Simp.update_window(it.window); // Simp will do nothing if it doesn't care about this window. if it.window == window { should_reinit := (it.width != window_width) || (it.height != window_height); window_width = it.width; window_height = it.height; //if should_reinit my_init_fonts(); // Resize the font for the new window size. } } camera.zoom *= 1.0 - 0.1*Input.mouse_delta_z/120.0; if panning { camera.pos -= mouse_delta; } Simp.immediate_begin(); Simp.clear_render_target(0.0, 0.0, 0.0, 1.0); // draw grid grid_d: DrawingSettings = .{true,.{0.2, 0.2, 0.2, 0.5}}; text_d := grid_d; text_d.color = .{0.6, 0.6, 0.6, 1.0}; help_text_max :: 8; help_zoom_max :: 0.04; for x: -30..30 { line(grid_d, xy(x, 30), xy(x, -30)); if camera.zoom <= help_zoom_max && abs(x) < help_text_max then text(text_d, tprint("%m", x), .{xx x, 0.0}); } for y: -30..30 { line(grid_d, xy(30, y), xy(-30, y)); if camera.zoom <= help_zoom_max && abs(y) < help_text_max then text(text_d, tprint("%m", y), .{0.0, xx y}); } draw_pips(dt); if mouse_frozen pip(.{false, ORANGE}, mouse_frozen_at, size = 20.0); // handle inputs for Input.events_this_frame { if it.type == .QUIT then quit = true; if it.type == { case .KEYBOARD; if it.key_pressed { if it.key_code == { case .ESCAPE; quit = true; case #char "T"; if !mouse_frozen mouse_frozen_at = mouse_screen(); mouse_frozen = !mouse_frozen; } } if it.key_code == .MOUSE_BUTTON_LEFT { panning = cast(bool)it.key_pressed; } } } } frame_end :: () { Simp.immediate_flush(); Simp.swap_buffers(window); } screen_to_world :: (screen: Vector2) -> Vector2 { using camera; return (screen + pos)*zoom; } world_to_screen :: (world: Vector2) -> Vector2 { using camera; // world = (screen + pos)*zoom; // world/zoom = screen + pos; // world/zoom - pos = screen; return (world/zoom) - pos; }