initial commit

This commit is contained in:
Rokas Puzonas 2025-12-06 15:37:34 +02:00
commit 937693b16e
18 changed files with 932 additions and 0 deletions

4
.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
zig-out
.zig-cache
.cache
compile_commands.json

164
build.zig Normal file
View File

@ -0,0 +1,164 @@
const std = @import("std");
const zcc = @import("compile_commands");
pub fn build(b: *std.Build) !void {
const target = b.standardTargetOptions(.{ });
const optimize = b.standardOptimizeOption(.{});
const tracy_enabled = b.option(bool, "tracy", "Enable tracy profiling") orelse (optimize == .Debug);
const imgui_enabled = b.option(bool, "imgui", "Enable ImGui integration") orelse (optimize == .Debug);
var targets: std.ArrayList(*std.Build.Step.Compile) = .empty;
const mod = b.createModule(.{
.target = target,
.optimize = optimize,
.link_libc = true,
});
mod.addIncludePath(b.path("./src/"));
var cflags: std.ArrayList([]const u8) = .empty;
const raylib_dep = b.dependency("raylib", .{
.target = target,
.optimize = optimize
});
mod.linkLibrary(raylib_dep.artifact("raylib"));
const tracy_dep = b.dependency("tracy", .{
.tracy_enable = tracy_enabled,
});
mod.linkLibrary(tracy_dep.artifact("tracy"));
const incbin_dep = b.dependency("incbin", .{});
mod.addIncludePath(incbin_dep.path("."));
if (imgui_enabled) {
if (try buildImGui(b, .{
.target = target,
.optimize = optimize,
.raylib = raylib_dep.artifact("raylib")
})) |imgui| {
mod.linkLibrary(imgui);
try targets.append(b.allocator, imgui);
}
try cflags.append(b.allocator, "-DIMGUI_ENABLED");
}
{
var src_dir = try std.fs.cwd().openDir("src", .{ .iterate = true });
defer src_dir.close();
var iter = src_dir.iterate();
while (try iter.next()) |entry| {
if (entry.kind == .file and std.mem.endsWith(u8, entry.name, ".c")) {
mod.addCSourceFile(.{
.file = b.path(b.pathJoin(&.{ "src", entry.name })),
.flags = cflags.items
});
}
}
}
const exe = b.addExecutable(.{
.name = "gaem",
.root_module = mod
});
try targets.append(b.allocator, exe);
if (target.result.os.tag == .windows) {
const show_console = b.option(bool, "console", "Show windows console") orelse (optimize == .Debug);
exe.subsystem = if (show_console) .Console else .Windows;
}
b.installArtifact(exe);
{
const run_cmd = b.addRunArtifact(exe);
run_cmd.step.dependOn(b.getInstallStep());
if (b.args) |args| {
run_cmd.addArgs(args);
}
const run_step = b.step("run", "Run the program");
run_step.dependOn(&run_cmd.step);
}
_ = zcc.createStep(b, "cdb", try targets.toOwnedSlice(b.allocator));
}
const ImGuiOptions = struct {
target: std.Build.ResolvedTarget,
optimize: std.builtin.OptimizeMode,
raylib: *std.Build.Step.Compile
};
fn buildImGui(b: *std.Build, opts: ImGuiOptions) !?*std.Build.Step.Compile {
const imgui = b.lazyDependency("imgui", .{}) orelse return null;
const cimgui = b.lazyDependency("cimgui", .{}) orelse return null;
const rlimgui = b.lazyDependency("rlimgui", .{}) orelse return null;
const cpp_flags = .{
"-fno-exceptions",
"-fno-rtti",
"-std=c++11",
"-DNO_FONT_AWESOME"
};
const imgui_lib = b.addLibrary(.{
.name = "imgui",
.root_module = b.createModule(.{
.target = opts.target,
.optimize = opts.optimize,
.link_libcpp = true,
})
});
imgui_lib.root_module.addCSourceFiles(.{
.root = imgui.path("."),
.files = &.{
"imgui.cpp",
"imgui_demo.cpp",
"imgui_draw.cpp",
"imgui_tables.cpp",
"imgui_widgets.cpp",
},
.flags = &cpp_flags
});
imgui_lib.root_module.addIncludePath(imgui.path("."));
// Needed for cimgui
imgui_lib.installHeadersDirectory(imgui.path("."), "imgui", .{});
// Needed for rlimgui
imgui_lib.installHeadersDirectory(imgui.path("."), ".", .{});
const lib = b.addLibrary(.{
.name = "imgui_raylib_cimgui",
.root_module = b.createModule(.{
.target = opts.target,
.optimize = opts.optimize,
.link_libcpp = true,
})
});
lib.root_module.linkLibrary(imgui_lib);
lib.root_module.addCSourceFile(.{
.file = cimgui.path("cimgui.cpp"),
.flags = &cpp_flags
});
lib.root_module.addIncludePath(cimgui.path("."));
lib.root_module.addCSourceFile(.{
.file = rlimgui.path("rlImGui.cpp"),
.flags = &cpp_flags,
});
lib.root_module.addIncludePath(rlimgui.path("."));
lib.root_module.linkLibrary(opts.raylib);
lib.installHeader(rlimgui.path("rlImGui.h"), "rlImGui.h");
lib.installHeader(cimgui.path("cimgui.h"), "cimgui.h");
return lib;
}

39
build.zig.zon Normal file
View File

@ -0,0 +1,39 @@
.{
.name = .raylib_template,
.version = "0.0.1",
.dependencies = .{
.raylib = .{
.url = "git+https://github.com/raysan5/raylib.git#6a048b7afeada62f9071969ba277ad14e0dce256",
.hash = "raylib-5.6.0-dev-whq8uM0UCAWTJ_Wd2f8GItDMnDg3hMImKT-nk78bscfL",
},
.tracy = .{
.path = "./libs/tracy"
},
.incbin = .{
.url = "git+https://github.com/graphitemaster/incbin.git#22061f51fe9f2f35f061f85c2b217b55dd75310d",
.hash = "N-V-__8AABmOAAB00zlm0f7NkMnWJxJD7g3y1PNsbgO7SCiQ",
},
.compile_commands = .{
.url = "https://github.com/the-argus/zig-compile-commands/archive/f74e2d13e43fafab3a71e19557a0e1cfbf0f2e1b.tar.gz",
.hash = "zig_compile_commands-0.0.1-OZg5-a3CAACM-h32Kjb1obTMqrKGs9YoDhorVZ8-LGle",
},
.imgui = .{
.url = "https://github.com/ocornut/imgui/archive/6d910d5487d11ca567b61c7824b0c78c569d62f0.tar.gz",
.hash = "N-V-__8AALp9cwA8tEuEno2YCZyivsMaobnF-Z7qZGY3qBOt",
.lazy = true
},
.rlimgui = .{
.url = "https://github.com/raylib-extras/rlImGui/archive/dc7f97679a024eee8f5f009e77cc311748200415.tar.gz",
.hash = "N-V-__8AAC-taQCKZ-3IjRuWJ-5Dc47QKHDPEmqlwXhvjBGq",
.lazy = true
},
.cimgui = .{
.url = "https://github.com/cimgui/cimgui/archive/bfd30140a9c5832b5e0dcf179d6e1e5c69373d5a.tar.gz",
.hash = "N-V-__8AAOY-OACWjhmHu289AXFy9Fro3w_30mGFYL8FTq71",
.lazy = true
}
},
.minimum_zig_version = "0.15.2",
.paths = .{""},
.fingerprint = 0xd9c063dda43688bf,
}

124
libs/tracy/build.zig Normal file
View File

@ -0,0 +1,124 @@
const std = @import("std");
pub fn build(b: *std.Build) void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
const options = b.addOptions();
const tracy_enable = option(b, options, bool, "tracy_enable", "Enable profiling", true);
const tracy_on_demand = option(b, options, bool, "tracy_on_demand", "On-demand profiling", false);
const tracy_callstack = callstack: {
const opt = b.option(u8, "tracy_callstack", "Enforce callstack collection for tracy regions");
options.addOption(?u8, "tracy_callstack", opt);
break :callstack opt;
};
const tracy_no_callstack = option(b, options, bool, "tracy_no_callstack", "Disable all callstack related functionality", false);
const tracy_no_callstack_inlines = option(b, options, bool, "tracy_no_callstack_inlines", "Disables the inline functions in callstacks", false);
const tracy_only_localhost = option(b, options, bool, "tracy_only_localhost", "Only listen on the localhost interface", false);
const tracy_no_broadcast = option(b, options, bool, "tracy_no_broadcast", "Disable client discovery by broadcast to local network", false);
const tracy_only_ipv4 = option(b, options, bool, "tracy_only_ipv4", "Tracy will only accept connections on IPv4 addresses (disable IPv6)", false);
const tracy_no_code_transfer = option(b, options, bool, "tracy_no_code_transfer", "Disable collection of source code", false);
const tracy_no_context_switch = option(b, options, bool, "tracy_no_context_switch", "Disable capture of context switches", false);
const tracy_no_exit = option(b, options, bool, "tracy_no_exit", "Client executable does not exit until all profile data is sent to server", false);
const tracy_no_sampling = option(b, options, bool, "tracy_no_sampling", "Disable call stack sampling", false);
const tracy_no_verify = option(b, options, bool, "tracy_no_verify", "Disable zone validation for C API", false);
const tracy_no_vsync_capture = option(b, options, bool, "tracy_no_vsync_capture", "Disable capture of hardware Vsync events", false);
const tracy_no_frame_image = option(b, options, bool, "tracy_no_frame_image", "Disable the frame image support and its thread", false);
// @FIXME: For some reason system tracing crashes the program, will need to investigate
// panics during some drawf thing within libbacktrace (c++)
const tracy_no_system_tracing = option(b, options, bool, "tracy_no_system_tracing", "Disable systrace sampling", true);
const tracy_delayed_init = option(b, options, bool, "tracy_delayed_init", "Enable delayed initialization of the library (init on first call)", false);
const tracy_manual_lifetime = option(b, options, bool, "tracy_manual_lifetime", "Enable the manual lifetime management of the profile", false);
const tracy_fibers = option(b, options, bool, "tracy_fibers", "Enable fibers support", false);
const tracy_no_crash_handler = option(b, options, bool, "tracy_no_crash_handler", "Disable crash handling", false);
const tracy_timer_fallback = option(b, options, bool, "tracy_timer_fallback", "Use lower resolution timers", false);
const shared = option(b, options, bool, "shared", "Build the tracy client as a shared libary", false);
const tracy_source_dep = b.dependency("tracy_source", .{});
const mod = b.addModule("tracy", .{
.target = target,
.optimize = optimize,
});
// Avoid building Tracy completely if it is disabled.
mod.addIncludePath(tracy_source_dep.path("public"));
if (target.result.os.tag == .windows) {
mod.linkSystemLibrary("dbghelp", .{ .needed = true });
mod.linkSystemLibrary("ws2_32", .{ .needed = true });
}
mod.link_libcpp = true;
const config_header = b.addConfigHeader(.{ .include_path = "TracyConfig.h" }, .{});
inline for (.{
.{ tracy_enable, "TRACY_ENABLE", "" },
.{ tracy_on_demand, "TRACY_ON_DEMAND", "" },
.{ tracy_callstack != null, "TRACY_CALLSTACK", b.fmt("{?d}", .{tracy_callstack}) },
.{ tracy_no_callstack, "TRACY_NO_CALLSTACK", "" },
.{ tracy_no_callstack_inlines, "TRACY_NO_CALLSTACK_INLINES", "" },
.{ tracy_only_localhost, "TRACY_ONLY_LOCALHOST", "" },
.{ tracy_no_broadcast, "TRACY_NO_BROADCAST", "" },
.{ tracy_only_ipv4, "TRACY_ONLY_IPV4", "" },
.{ tracy_no_code_transfer, "TRACY_NO_CODE_TRANSFER", "" },
.{ tracy_no_context_switch, "TRACY_NO_CONTEXT_SWITCH", "" },
.{ tracy_no_exit, "TRACY_NO_EXIT", "" },
.{ tracy_no_sampling, "TRACY_NO_SAMPLING", "" },
.{ tracy_no_verify, "TRACY_NO_VERIFY", "" },
.{ tracy_no_vsync_capture, "TRACY_NO_VSYNC_CAPTURE", ""},
.{ tracy_no_frame_image, "TRACY_NO_FRAME_IMAGE", ""},
.{ tracy_no_system_tracing, "TRACY_NO_SYSTEM_TRACING", ""},
.{ tracy_delayed_init, "TRACY_DELAYED_INIT", ""},
.{ tracy_manual_lifetime, "TRACY_MANUAL_LIFETIME", ""},
.{ tracy_fibers, "TRACY_FIBERS", "" },
.{ tracy_no_crash_handler, "TRACY_NO_CRASH_HANDLER", "" },
.{ tracy_timer_fallback, "TRACY_TIMER_FALLBACK", "" },
.{ shared and target.result.os.tag == .windows, "TRACY_EXPORTS", "" }
}) |triplet| {
const enabled, const name, const value = triplet;
if (enabled) {
mod.addCMacro(name, value);
config_header.addValue(name, []const u8, value);
}
}
mod.addCSourceFile(.{
.file = tracy_source_dep.path("public/TracyClient.cpp"),
.flags = &.{}
});
const lib = b.addLibrary(.{
.linkage = if (shared) .dynamic else .static,
.name = "tracy",
.root_module = mod,
});
const include_extensions = &.{".h",".hpp"};
lib.installHeadersDirectory(tracy_source_dep.path("public/tracy"), "tracy", .{
.include_extensions = include_extensions,
});
lib.installHeadersDirectory(tracy_source_dep.path("public/common"), "common", .{
.include_extensions = include_extensions,
});
lib.installHeadersDirectory(tracy_source_dep.path("public/client"), "client", .{
.include_extensions = include_extensions,
});
lib.installConfigHeader(config_header);
b.installArtifact(lib);
}
pub fn option(
b: *std.Build,
options: *std.Build.Step.Options,
comptime T: type,
name_raw: []const u8,
description_raw: []const u8,
default: T,
) T {
const opt = b.option(T, name_raw, description_raw) orelse default;
options.addOption(T, name_raw, opt);
return opt;
}

13
libs/tracy/build.zig.zon Normal file
View File

@ -0,0 +1,13 @@
.{
.name = .tracy,
.version = "0.0.1",
.minimum_zig_version = "0.15.1",
.dependencies = .{
.tracy_source = .{
.url = "https://github.com/wolfpld/tracy/archive/refs/tags/v0.13.0.zip",
.hash = "N-V-__8AAHW7KwHH_eNEviRWLkRfsxpWFrdOYM9f8VeCin4q",
},
},
.paths = .{""},
.fingerprint = 0x255a89ee3ed46b42,
}

36
src/entity_id.c Normal file
View File

@ -0,0 +1,36 @@
#include "entity_id.h"
bool entity_id_eql(EntityId lhs, EntityId rhs)
{
return lhs.packed == rhs.packed;
}
bool entity_id_is_nil(EntityId id)
{
return entity_id_eql(id, ENTITY_ID_NIL);
}
uint32_t entity_id_get_index(EntityId id)
{
return (id.packed & 0x00FFFFFF);
}
uint8_t entity_id_get_generation(EntityId id)
{
return (id.packed >> 24);
}
struct UnpackedEntityId entity_id_unpack(EntityId id)
{
return (struct UnpackedEntityId){
.generation = entity_id_get_generation(id),
.index = entity_id_get_index(id)
};
}
EntityId entity_id_pack(struct UnpackedEntityId unpacked_id)
{
return (EntityId){
.packed = (unpacked_id.generation << 24) | (unpacked_id.index & 0x00FFFFFF)
};
}

27
src/entity_id.h Normal file
View File

@ -0,0 +1,27 @@
#pragma once
#include "main.h"
#define ENTITY_ID_INDEX_BITS 24
#define ENTITY_ID_GENERATION_BITS 8
#define MAX_INDEX ((1 << ENTITY_ID_INDEX_BITS) - 1)
#define MAX_GENERATION ((1 << ENTITY_ID_GENERATION_BITS) - 1)
#define ENTITY_ID_NIL (EntityId){ 0 }
typedef struct {
uint32_t packed;
} EntityId;
struct UnpackedEntityId {
uint32_t index;
uint8_t generation;
};
bool entity_id_eql(EntityId lhs, EntityId rhs);
bool entity_id_is_nil(EntityId id);
EntityId entity_id_pack(struct UnpackedEntityId unpacked_id);
struct UnpackedEntityId entity_id_unpack(EntityId id);
uint32_t entity_id_get_index(EntityId id);
uint8_t entity_id_get_generation(EntityId id);

142
src/entity_list.c Normal file
View File

@ -0,0 +1,142 @@
#include "entity_list.h"
void entity_list_ensure_capacity(struct EntityList *list, size_t expected_capacity)
{
if (expected_capacity > list->capacity) {
size_t larger_capacity = MAX(MAX(list->capacity*2, 8), expected_capacity);
struct EntitySlot *larger_items = realloc(list->items, larger_capacity * sizeof(*larger_items));
if (larger_items == NULL) {
PANIC_OOM();
}
for (size_t i = list->capacity; i < larger_capacity; i++) {
larger_items[i].used = false;
larger_items[i].generation = 0;
}
list->items = larger_items;
list->capacity = larger_capacity;
}
}
void entity_list_copy(struct EntityList *destination, struct EntityList *source)
{
entity_list_ensure_capacity(destination, source->len);
if (source->len > 0) {
memcpy(
destination->items,
source->items,
sizeof(*source->items) * source->len
);
}
destination->len = source->len;
}
struct Entity *entity_list_add(struct EntityList *list)
{
struct Entity *result = NULL;
for (size_t i = 1; i < list->len; i++) {
struct EntitySlot *slot = &list->items[i];
if (!slot->used) {
slot->used = true;
result = &slot->entity;
break;
}
}
if (result == NULL) {
uint32_t index = MAX(list->len, 1);
ASSERT(index <= MAX_INDEX);
entity_list_ensure_capacity(list, index + 1);
list->len = index + 1;
struct EntitySlot *slot = &list->items[index];
ASSERT(!slot->used);
slot->used = true;
result = &slot->entity;
}
ASSERT(result != NULL);
memset(result, 0, sizeof(*result));
list->used_count++;
return result;
}
bool entity_list_remove(struct EntityList *list, struct Entity *entity)
{
ASSERT(entity != NULL);
struct EntitySlot *slot = container_of(entity, struct EntitySlot, entity);
if (!slot->used) {
return false;
}
slot->used = false;
slot->generation++;
ASSERT(list->used_count > 0);
list->used_count--;
return true;
}
bool entity_list_remove_by_id(struct EntityList *list, EntityId entity_id)
{
struct Entity *entity = entity_list_get(list, entity_id);
if (!entity) {
return false;
}
return entity_list_remove(list, entity);
}
bool entity_list_exists(struct EntityList *list, EntityId entity_id)
{
struct UnpackedEntityId unpacked = entity_id_unpack(entity_id);
if (unpacked.index >= list->len) {
return false;
}
struct EntitySlot *entity_slot = &list->items[unpacked.index];
if (!entity_slot->used) {
return false;
}
return entity_slot->generation == unpacked.generation;
}
struct Entity *entity_list_get(struct EntityList *list, EntityId entity_id)
{
if (entity_list_exists(list, entity_id)) {
struct UnpackedEntityId unpacked = entity_id_unpack(entity_id);
return &list->items[unpacked.index].entity;
}
return NULL;
}
EntityId entity_list_get_id(struct EntityList *list, struct Entity *entity)
{
ASSERT(entity != NULL);
struct EntitySlot *slot = container_of(entity, struct EntitySlot, entity);
uint32_t offset = (void*)slot - (void*)list->items;
uint32_t index = offset / sizeof(struct EntitySlot);
return entity_id_pack((struct UnpackedEntityId){
.generation = slot->generation,
.index = index
});
}
void entity_list_free(struct EntityList *list)
{
free(list->items);
*list = (struct EntityList){ 0 };
}

37
src/entity_list.h Normal file
View File

@ -0,0 +1,37 @@
#pragma once
#include "main.h"
#include "entity_id.h"
struct Entity {
};
struct EntitySlot {
bool used;
uint8_t generation;
struct Entity entity;
};
struct EntityList {
struct EntitySlot *items;
size_t len;
size_t capacity;
size_t used_count;
};
void entity_list_ensure_capacity(struct EntityList *list, size_t expected_capacity);
void entity_list_copy(struct EntityList *destination, struct EntityList *source);
void entity_list_free(struct EntityList *list);
struct Entity *entity_list_add(struct EntityList *list);
bool entity_list_exists(struct EntityList *list, EntityId entity_id);
struct Entity *entity_list_get(struct EntityList *list, EntityId entity_id);
EntityId entity_list_get_id(struct EntityList *list, struct Entity *entity);
bool entity_list_remove(struct EntityList *list, struct Entity *entity);
bool entity_list_remove_by_id(struct EntityList *list, EntityId entity_id);
#define entity_list_foreach(list, _entity) for ( \
size_t i = 1; \
i < (list)->len && (_entity = &(list)->items[i].entity, true); \
i++ \
) \
if (!(list)->items[i].used) {} else

37
src/game.c Normal file
View File

@ -0,0 +1,37 @@
#include "game.h"
void game_init(struct Game *game)
{
*game = (struct Game){
.canvas_size = { 800, 600 }
};
}
void game_free(struct Game *game)
{
}
void game_tick(struct Game *game)
{
TracyCZoneN(game_tick, "game_tick", true);
Vector2 dir = { 0 };
if (IsKeyDown(KEY_W)) {
dir.y -= 1;
}
if (IsKeyDown(KEY_S)) {
dir.y += 1;
}
if (IsKeyDown(KEY_D)) {
dir.x += 1;
}
if (IsKeyDown(KEY_A)) {
dir.x -= 1;
}
game->player_position = Vector2Add(game->player_position, Vector2Scale(dir, 50 * game->input.dt));
DrawRectangleV(game->player_position, (Vector2){ 50, 50 }, RED);
TracyCZoneEnd(game_tick)
}

22
src/game.h Normal file
View File

@ -0,0 +1,22 @@
#pragma once
#include "main.h"
#include "entity_list.h"
struct Input {
float dt;
Vector2 mouse;
};
struct Game {
Vector2 canvas_size;
struct EntityList entities;
Vector2 player_position;
struct Input input;
};
void game_init(struct Game *game);
void game_free(struct Game *game);
void game_tick(struct Game *game);

58
src/game_debug.c Normal file
View File

@ -0,0 +1,58 @@
#include "game_debug.h"
#ifdef IMGUI_ENABLED
#define NO_FONT_AWESOME
#include <rlImGui.h>
#define CIMGUI_DEFINE_ENUMS_AND_STRUCTS
#include <cimgui.h>
void game_debug_init()
{
rlImGuiBeginInitImGui();
ImGuiIO *io = igGetIO_ContextPtr(igGetCurrentContext());
io->IniFilename = NULL;
igStyleColorsDark(NULL);
rlImGuiEndInitImGui();
}
void game_debug_deinit()
{
rlImGuiShutdown();
}
void game_debug_show(struct Game *game)
{
rlImGuiBegin();
igSetNextWindowSize((ImVec2_c){ 400, 200 }, ImGuiCond_Once);
if (igBegin("Debug", NULL, ImGuiWindowFlags_None)) {
char label[64] = { 0 };
snprintf(label, sizeof(label), "FPS: %.1f (%.2fms)", 1.0f / game->input.dt, game->input.dt * 1000);
igTextEx(label, NULL, ImGuiTextFlags_None);
igTextEx("Hello, World", NULL, ImGuiTextFlags_None);
}
igEnd();
rlImGuiEnd();
}
#else
void game_debug_init()
{
}
void game_debug_deinit()
{
}
void game_debug_show(struct Game *game)
{
}
#endif

8
src/game_debug.h Normal file
View File

@ -0,0 +1,8 @@
#pragma once
#include "main.h"
#include "game.h"
void game_debug_init();
void game_debug_deinit();
void game_debug_show(struct Game *game);

133
src/main.c Normal file
View File

@ -0,0 +1,133 @@
#include "main.h"
#include "resources.h"
#include "game.h"
#include "game_debug.h"
struct Resources g_resources = { 0 };
struct WindowTransform {
Vector2 offset;
float scale;
};
static void begin_window_scaling(struct WindowTransform *transform, Vector2 virtual_screen_size)
{
Vector2 screen_size = {
GetScreenWidth(),
GetScreenHeight()
};
float scale = MIN(
screen_size.x / virtual_screen_size.x,
screen_size.y / virtual_screen_size.y
);
transform->scale = scale;
Vector2 filler_size = Vector2Scale(Vector2Subtract(screen_size, Vector2Scale(virtual_screen_size, transform->scale)), 0.5);
transform->offset = filler_size;
rlPushMatrix();
rlTranslatef(
filler_size.x,
filler_size.y,
0
);
rlScalef(scale, scale, 1);
}
static void end_window_scaling(struct WindowTransform *transform, Color color)
{
rlPopMatrix();
Vector2 filler_size = transform->offset;
DrawRectangleRec(
(struct Rectangle){
.x = 0,
.y = 0,
.width = GetScreenWidth(),
.height = filler_size.y
},
color
);
DrawRectangleRec(
(struct Rectangle){
.x = 0,
.y = GetScreenHeight() - filler_size.y,
.width = GetScreenWidth(),
.height = filler_size.y
},
color
);
DrawRectangleRec(
(struct Rectangle){
.x = 0,
.y = 0,
.width = filler_size.x,
.height = GetScreenHeight()
},
color
);
DrawRectangleRec(
(struct Rectangle){
.x = GetScreenWidth() - filler_size.x,
.y = 0,
.width = filler_size.x,
.height = GetScreenHeight()
},
color
);
}
int main(void)
{
InitWindow(800, 600, "gaem");
InitAudioDevice();
SetWindowState(FLAG_WINDOW_RESIZABLE);
SetTargetFPS(60);
resources_init(&g_resources);
struct Game game = { 0 };
game_init(&game);
game_debug_init();
while (!WindowShouldClose())
{
TracyCFrameMark;
// TODO: Can this be called every frame? Will it impact performance? Needs research.
SetWindowMinSize(game.canvas_size.x, game.canvas_size.y);
float dt = GetFrameTime();
BeginDrawing();
ClearBackground(RAYWHITE);
struct WindowTransform transform = { 0 };
begin_window_scaling(&transform, game.canvas_size);
Vector2 mouse = GetMousePosition();
mouse = Vector2Subtract(mouse, transform.offset);
mouse = Vector2Scale(mouse, 1/transform.scale);
game.input = (struct Input){
.dt = dt,
.mouse = mouse
};
game_tick(&game);
end_window_scaling(&transform, BLACK);
game_debug_show(&game);
EndDrawing();
}
game_free(&game);
game_debug_deinit();
CloseWindow();
return 0;
}

38
src/main.h Normal file
View File

@ -0,0 +1,38 @@
#pragma once
#include <raylib.h>
#include <raymath.h>
#include <rlgl.h>
#include <TracyConfig.h>
#include <tracy/TracyC.h>
#include <sys/param.h>
#include <stdint.h>
#include <inttypes.h>
#include <stdlib.h>
#include <stddef.h>
#include <stdio.h>
#include <string.h>
#define INCBIN_STYLE INCBIN_STYLE_SNAKE
#define INCBIN_PREFIX
#include <incbin.h>
#define PANIC(message, ...) \
do { \
printf("%s:%d "message"\n", __FILE__, __LINE__, ##__VA_ARGS__); \
exit(EXIT_FAILURE); \
} while (false)
#define ASSERT(condition) if (!(condition)) PANIC("Assertion failed: %s", #condition)
#define PANIC_OOM() PANIC("Out of memory")
#define UNREACHABLE() PANIC("Reached unreachable")
#define ARRAY_SIZE(x) (sizeof(x)/sizeof((x)[0]))
#define container_of(ptr, type, member) ((type *)((char *)(ptr) - offsetof(type, member)))
extern struct Resources g_resources;
Color rgb(uint8_t r, uint8_t g, uint8_t b);
Color hex(const char *str);

5
src/resources.c Normal file
View File

@ -0,0 +1,5 @@
#include "main.h"
void resources_init(struct Resources *resources)
{
}

6
src/resources.h Normal file
View File

@ -0,0 +1,6 @@
#pragma once
struct Resources {
};
void resources_init(struct Resources *resources);

39
src/utils.c Normal file
View File

@ -0,0 +1,39 @@
#include "main.h"
Color rgb(uint8_t r, uint8_t g, uint8_t b)
{
return (Color){
.r = r,
.g = g,
.b = b,
.a = 255
};
}
static uint8_t parse_hex_nibble(char nibble)
{
if ('0' <= nibble && nibble <= '9') {
return nibble - '0';
} else if ('a' <= nibble && nibble <= 'f') {
return nibble - 'a';
} else if ('A' <= nibble && nibble <= 'F') {
return nibble - 'A';
} else {
UNREACHABLE();
}
}
static uint8_t parse_hex_byte(const char *two_bytes)
{
return (parse_hex_nibble(two_bytes[0]) << 4) | parse_hex_nibble(two_bytes[1]);
}
Color hex(const char *str)
{
ASSERT(strlen(str) == 7);
ASSERT(str[0] == '#');
uint8_t r = parse_hex_byte(&str[1]);
uint8_t g = parse_hex_byte(&str[3]);
uint8_t b = parse_hex_byte(&str[5]);
return rgb(r, g, b);
}