commit 15e1670ec3ed958b4e1f702784fbcc2bcce0f3d0 Author: geemili Date: Fri Feb 23 14:44:27 2024 -0700 get medium cards rendering diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ee7098f --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +zig-out/ +zig-cache/ diff --git a/build.zig b/build.zig new file mode 100644 index 0000000..1cb0cfd --- /dev/null +++ b/build.zig @@ -0,0 +1,55 @@ +const std = @import("std"); + +// Although this function looks imperative, note that its job is to +// declaratively construct a build graph that will be executed by an external +// runner. +pub fn build(b: *std.Build) void { + // Standard target options allows the person running `zig build` to choose + // what target to build for. Here we do not override the defaults, which + // means any target is allowed, and the default is native. Other options + // for restricting supported target set are available. + const target = b.standardTargetOptions(.{}); + + // Standard optimization options allow the person running `zig build` to select + // between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall. Here we do not + // set a preferred release mode, allowing the user to decide how to optimize. + const optimize = b.standardOptimizeOption(.{}); + + const seizer = b.dependency("seizer", .{ + .target = target, + .optimize = optimize, + }); + + const exe = b.addExecutable(.{ + .name = "seizer-rummy", + .root_source_file = .{ .path = "src/main.zig" }, + .target = target, + .optimize = optimize, + }); + exe.root_module.addImport("seizer", seizer.module("seizer")); + 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 app"); + run_step.dependOn(&run_cmd.step); + + const exe_unit_tests = b.addTest(.{ + .root_source_file = .{ .path = "src/main.zig" }, + .target = target, + .optimize = optimize, + }); + + const run_exe_unit_tests = b.addRunArtifact(exe_unit_tests); + + // Similar to creating the run step earlier, this exposes a `test` step to + // the `zig build --help` menu, providing a way for the user to request + // running the unit tests. + const test_step = b.step("test", "Run unit tests"); + test_step.dependOn(&run_exe_unit_tests.step); +} diff --git a/build.zig.zon b/build.zig.zon new file mode 100644 index 0000000..cddf7e0 --- /dev/null +++ b/build.zig.zon @@ -0,0 +1,36 @@ +.{ + .name = "seizer-rummy", + // This is a [Semantic Version](https://semver.org/). + // In a future version of Zig it will be used for package deduplication. + .version = "0.0.0", + + // This field is optional. + // This is currently advisory only; Zig does not yet do anything + // with this value. + //.minimum_zig_version = "0.11.0", + + // This field is optional. + // Each dependency must either provide a `url` and `hash`, or a `path`. + // `zig build --fetch` can be used to fetch all dependencies of a package, recursively. + // Once all dependencies are fetched, `zig build` no longer requires + // internet connectivity. + .dependencies = .{ + .seizer = .{ + .url = "https://github.com/leroycep/seizer/archive/dd1f9f6c94e91edfd96f0075c5f5d33aa30758f4.tar.gz", + .hash = "122009caed9e40d713c847b24c2abf8540299e32fb5e77f73ab2acdf5c27bc3e4c90", + }, + }, + .paths = .{ + // This makes *all* files, recursively, included in this package. It is generally + // better to explicitly list the files and directories instead, to insure that + // fetching from tarballs, file system paths, and version control all result + // in the same contents hash. + "", + // For example... + //"build.zig", + //"build.zig.zon", + //"src", + //"LICENSE", + //"README.md", + }, +} diff --git a/src/cardsLarge_tilemap.png b/src/cardsLarge_tilemap.png new file mode 100644 index 0000000..6977359 Binary files /dev/null and b/src/cardsLarge_tilemap.png differ diff --git a/src/cardsMedium_tilemap.png b/src/cardsMedium_tilemap.png new file mode 100644 index 0000000..cde704a Binary files /dev/null and b/src/cardsMedium_tilemap.png differ diff --git a/src/cardsSmall_tilemap.png b/src/cardsSmall_tilemap.png new file mode 100644 index 0000000..a7c7c4d Binary files /dev/null and b/src/cardsSmall_tilemap.png differ diff --git a/src/main.zig b/src/main.zig new file mode 100644 index 0000000..b52440f --- /dev/null +++ b/src/main.zig @@ -0,0 +1,182 @@ +var gl_binding: gl.Binding = undefined; + +const Suit = enum(u2) { + clubs = 0b00, + spades = 0b01, + hearts = 0b10, + diamonds = 0b11, + + pub fn color(this: @This()) u1 { + return (@intFromEnum(this) & 0b10) >> 1; + } +}; +const Card = packed struct(u6) { + suit: Suit, + rank: u4, +}; + +/// A texture with a regular grid of sprites +const TileSheet = struct { + texture: seizer.Texture, + tile_size: [2]u32, + + pub const InitOptions = struct { + allocator: std.mem.Allocator, + image_file_contents: []const u8, + tile_size: [2]u32, + texture_options: seizer.Texture.InitFromFileOptions = .{}, + }; + + pub fn init(options: InitOptions) !@This() { + const texture = try seizer.Texture.initFromFileContents( + options.allocator, + options.image_file_contents, + options.texture_options, + ); + return @This(){ + .texture = texture, + .tile_size = options.tile_size, + }; + } + + pub fn deinit(this: *@This()) void { + this.texture.deinit(); + } + + pub fn renderTile(this: @This(), canvas: *seizer.Canvas, tile_id: u32, pos: [2]f32, options: struct { + size: ?[2]f32 = null, + }) void { + const texture_sizef = [2]f32{ + @floatFromInt(this.texture.size[0]), + @floatFromInt(this.texture.size[1]), + }; + const tile_sizef = [2]f32{ + @floatFromInt(this.tile_size[0]), + @floatFromInt(this.tile_size[1]), + }; + const size_in_tiles = [2]u32{ + @as(u32, @intCast(this.texture.size[0])) / this.tile_size[0], + @as(u32, @intCast(this.texture.size[1])) / this.tile_size[1], + }; + const pos_in_tiles = [2]u32{ + tile_id % size_in_tiles[0], + tile_id / size_in_tiles[0], + }; + const pos_in_tilesf = [2]f32{ + @floatFromInt(pos_in_tiles[0]), + @floatFromInt(pos_in_tiles[1]), + }; + const uv = seizer.geometry.AABB(f32){ + .min = .{ + (pos_in_tilesf[0] * tile_sizef[0]) / texture_sizef[0], + (pos_in_tilesf[1] * tile_sizef[1]) / texture_sizef[1], + }, + .max = .{ + ((pos_in_tilesf[0] + 1) * tile_sizef[0]) / texture_sizef[0], + ((pos_in_tilesf[1] + 1) * tile_sizef[1]) / texture_sizef[1], + }, + }; + _ = canvas.printText(.{ 0, 0 }, "uv = {}", .{uv}, .{}); + canvas.rect(pos, options.size orelse tile_sizef, .{ + .texture = this.texture.glTexture, + .uv = uv, + }); + } +}; +// const cards_medium_spritesheet = + +pub fn main() !void { + var gpa = std.heap.GeneralPurposeAllocator(.{}){}; + defer _ = gpa.deinit(); + + // GLFW setup + try seizer.backend.glfw.loadDynamicLibraries(gpa.allocator()); + + _ = seizer.backend.glfw.c.glfwSetErrorCallback(&seizer.backend.glfw.defaultErrorCallback); + + const glfw_init_res = seizer.backend.glfw.c.glfwInit(); + if (glfw_init_res != 1) { + std.debug.print("glfw init error: {}\n", .{glfw_init_res}); + std.process.exit(1); + } + defer seizer.backend.glfw.c.glfwTerminate(); + + seizer.backend.glfw.c.glfwWindowHint(seizer.backend.glfw.c.GLFW_OPENGL_DEBUG_CONTEXT, seizer.backend.glfw.c.GLFW_TRUE); + seizer.backend.glfw.c.glfwWindowHint(seizer.backend.glfw.c.GLFW_CLIENT_API, seizer.backend.glfw.c.GLFW_OPENGL_ES_API); + seizer.backend.glfw.c.glfwWindowHint(seizer.backend.glfw.c.GLFW_CONTEXT_VERSION_MAJOR, 3); + seizer.backend.glfw.c.glfwWindowHint(seizer.backend.glfw.c.GLFW_CONTEXT_VERSION_MINOR, 0); + + // Open window + const window = seizer.backend.glfw.c.glfwCreateWindow(720, 720, "Rummy", null, null) orelse return error.GlfwCreateWindow; + errdefer seizer.backend.glfw.c.glfwDestroyWindow(window); + + seizer.backend.glfw.c.glfwMakeContextCurrent(window); + + gl_binding.init(seizer.backend.glfw.GlBindingLoader); + gl.makeBindingCurrent(&gl_binding); + + // Set up input callbacks + _ = seizer.backend.glfw.c.glfwSetFramebufferSizeCallback(window, &glfw_framebuffer_size_callback); + + var card_tilemap = try TileSheet.init(.{ + .allocator = gpa.allocator(), + .image_file_contents = @embedFile("./cardsMedium_tilemap.png"), + .tile_size = .{ 32, 32 }, + .texture_options = .{ + .min_filter = .nearest, + .mag_filter = .nearest, + }, + }); + defer card_tilemap.deinit(); + + var canvas = try seizer.Canvas.init(gpa.allocator(), .{}); + defer canvas.deinit(gpa.allocator()); + + while (seizer.backend.glfw.c.glfwWindowShouldClose(window) != seizer.backend.glfw.c.GLFW_TRUE) { + seizer.backend.glfw.c.glfwPollEvents(); + + gl.clearColor(0.7, 0.5, 0.5, 1.0); + gl.clear(gl.COLOR_BUFFER_BIT); + + var window_size: [2]c_int = undefined; + seizer.backend.glfw.c.glfwGetWindowSize(window, &window_size[0], &window_size[1]); + + var framebuffer_size: [2]c_int = undefined; + seizer.backend.glfw.c.glfwGetFramebufferSize(window, &framebuffer_size[0], &framebuffer_size[1]); + + canvas.begin(.{ + .window_size = [2]f32{ + @floatFromInt(window_size[0]), + @floatFromInt(window_size[1]), + }, + .framebuffer_size = [2]f32{ + @floatFromInt(framebuffer_size[0]), + @floatFromInt(framebuffer_size[1]), + }, + }); + card_tilemap.renderTile(&canvas, 1, .{ 10, 10 }, .{}); + // canvas.rect(.{ 10, 10 }, .{ @as(f32, @floatFromInt(card_tilemap.size[0])) / 15.0, @as(f32, @floatFromInt(card_tilemap.size[1])) / 10.0 }, .{ + // .texture = card_tilemap.glTexture, + // .uv = .{ .min = .{ 0, 0 }, .max = .{ 1.0 / 15.0, 1.0 / 10.0 } }, + // }); + _ = canvas.writeText(.{ 50, 50 }, "Hello, world!", .{}); + _ = canvas.printText(.{ 50, 100 }, "window_size = {}, {}\nframebuffer_size = {}, {}", .{ window_size[0], window_size[1], framebuffer_size[0], framebuffer_size[1] }, .{}); + canvas.end(); + + seizer.backend.glfw.c.glfwSwapBuffers(window); + } +} + +fn glfw_framebuffer_size_callback(window: ?*seizer.backend.glfw.c.GLFWwindow, width: c_int, height: c_int) callconv(.C) void { + _ = window; + gl.viewport( + 0, + 0, + @intCast(width), + @intCast(height), + ); +} + +const seizer = @import("seizer"); +const gl = seizer.gl; +const std = @import("std");