initial commit

This commit is contained in:
Rokas Puzonas 2025-12-05 03:27:30 +02:00
commit 6c79f701d3
10 changed files with 501 additions and 0 deletions

4
.gitignore vendored Normal file
View File

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

74
build.zig Normal file
View File

@ -0,0 +1,74 @@
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);
var targets = std.ArrayList(*std.Build.Step.Compile){};
const mod = b.createModule(.{
.target = target,
.optimize = optimize,
.link_libc = true,
});
mod.addIncludePath(b.path("./src/"));
{
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 = &.{}
});
}
}
}
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("."));
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));
}

24
build.zig.zon Normal file
View File

@ -0,0 +1,24 @@
.{
.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",
},
},
.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,
}

18
src/game.c Normal file
View File

@ -0,0 +1,18 @@
#include "main.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);
TracyCZoneEnd(game_tick)
}

125
src/main.c Normal file
View File

@ -0,0 +1,125 @@
#include "main.h"
extern 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);
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);
EndDrawing();
}
game_free(&game);
CloseWindow();
return 0;
}

75
src/main.h Normal file
View File

@ -0,0 +1,75 @@
#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)))
struct Resources {
};
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;
};
struct Input {
float dt;
Vector2 mouse;
};
struct Game {
Vector2 canvas_size;
struct EntityList entities;
struct Input input;
};
extern struct Resources g_resources;
void resources_init(struct Resources *resources);
void game_init(struct Game *game);
void game_free(struct Game *game);
void game_tick(struct Game *game);
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)
{
}

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);
}