// This file is compiled as part of the `odin.dll` file. It contains the // procs that `game_hot_reload.exe` will call, such as: // // game_init: Sets up the game state // game_update: Run once per frame // game_shutdown: Shuts down game and frees memory // game_memory: Run just before a hot reload, so game.exe has a pointer to the // game's memory. // game_hot_reloaded: Run after a hot reload so that the `g_mem` global variable // can be set to whatever pointer it was in the old DLL. // // Note: When compiled as part of the release executable this whole package is imported as a normal // odin package instead of a DLL. package game import "core:fmt" import "core:math/linalg" import rl "vendor:raylib" PIXEL_WINDOW_HEIGHT :: 360 Game_Memory :: struct { player_pos: rl.Vector3, camera_yaw_pitch: rl.Vector2, camera_speed: f32, mouse_captured: bool, } g_mem: ^Game_Memory game_camera :: proc() -> rl.Camera2D { w := f32(rl.GetScreenWidth()) h := f32(rl.GetScreenHeight()) return {zoom = h / PIXEL_WINDOW_HEIGHT, target = g_mem.player_pos.xy, offset = {w / 2, h / 2}} } camera_rotation_matrix :: proc() -> matrix[3, 3]f32 { return linalg.matrix3_from_euler_angles_xy(g_mem.camera_yaw_pitch.x, g_mem.camera_yaw_pitch.y) } camera_forward_vec :: proc() -> rl.Vector3 { rotation_matrix := camera_rotation_matrix() return rotation_matrix * rl.Vector3{0, 0, 1} } game_camera_3d :: proc() -> rl.Camera3D { return { position = g_mem.player_pos, up = {0, 1, 0}, fovy = 60, target = g_mem.player_pos + camera_forward_vec(), projection = .PERSPECTIVE, } } ui_camera :: proc() -> rl.Camera2D { return {zoom = f32(rl.GetScreenHeight()) / PIXEL_WINDOW_HEIGHT} } update_free_look_camera :: proc() { input: rl.Vector2 if rl.IsKeyDown(.UP) || rl.IsKeyDown(.W) { input.y -= 1 } if rl.IsKeyDown(.DOWN) || rl.IsKeyDown(.S) { input.y += 1 } if rl.IsKeyDown(.LEFT) || rl.IsKeyDown(.A) { input.x -= 1 } if rl.IsKeyDown(.RIGHT) || rl.IsKeyDown(.D) { input.x += 1 } if rl.IsKeyPressed(.ESCAPE) { if g_mem.mouse_captured { g_mem.mouse_captured = false rl.EnableCursor() } } if g_mem.mouse_captured { g_mem.camera_yaw_pitch += rl.GetMouseDelta().yx * -1 * 0.001 } g_mem.camera_speed += rl.GetMouseWheelMove() * 0.01 g_mem.camera_speed = linalg.clamp(g_mem.camera_speed, 0.01, 10) rotation_matrix := camera_rotation_matrix() forward := -rotation_matrix[2] right := linalg.cross(rl.Vector3{0, 1, 0}, forward) input = linalg.normalize0(input) g_mem.player_pos += (input.x * right + input.y * forward) * g_mem.camera_speed } update :: proc() { if rl.IsMouseButtonPressed(.LEFT) { g_mem.mouse_captured = true rl.DisableCursor() } update_free_look_camera() } draw :: proc() { rl.BeginDrawing() rl.ClearBackground(rl.BLACK) { rl.BeginMode3D(game_camera_3d()) defer rl.EndMode3D() rl.DrawBoundingBox(rl.BoundingBox{min = -1, max = 1}, {255, 0, 0, 255}) } rl.BeginMode2D(ui_camera()) // Note: main_hot_reload.odin clears the temp allocator at end of frame. rl.DrawText(fmt.ctprintf("player_pos: %v", g_mem.player_pos), 5, 5, 8, rl.WHITE) rl.EndMode2D() rl.EndDrawing() } @(export) game_update :: proc() -> bool { update() draw() return !rl.WindowShouldClose() } @(export) game_init_window :: proc() { rl.SetConfigFlags({.WINDOW_RESIZABLE, .VSYNC_HINT}) rl.InitWindow(1280, 720, "Odin + Raylib + Hot Reload template!") rl.SetExitKey(.KEY_NULL) rl.SetWindowPosition(200, 200) rl.SetTargetFPS(500) } @(export) game_init :: proc() { g_mem = new(Game_Memory) g_mem^ = Game_Memory{} game_hot_reloaded(g_mem) } @(export) game_shutdown :: proc() { free(g_mem) } @(export) game_shutdown_window :: proc() { rl.CloseWindow() } @(export) game_memory :: proc() -> rawptr { return g_mem } @(export) game_memory_size :: proc() -> int { return size_of(Game_Memory) } @(export) game_hot_reloaded :: proc(mem: rawptr) { g_mem = (^Game_Memory)(mem) } @(export) game_force_reload :: proc() -> bool { return rl.IsKeyPressed(.F5) } @(export) game_force_restart :: proc() -> bool { return rl.IsKeyPressed(.F6) }