diff --git a/build.zig b/build.zig index 020867d..642cbf1 100644 --- a/build.zig +++ b/build.zig @@ -50,8 +50,7 @@ fn stepBuildMain( exe.root_module.addImport("sdl", sdl_module); exe.step.dependOn(sdl_step); - const c_module = cModule(b, target, optimize); - exe.root_module.addImport("c", c_module); + exe.addIncludePath(b.path("lib/clibs")); return exe; } @@ -107,21 +106,6 @@ fn stepSdlModule( }; } -fn cModule( - b: *Build, - target: Build.ResolvedTarget, - optimize: std.builtin.OptimizeMode, -) *Build.Module { - const c_module = b.addModule("c", .{ - .root_source_file = b.path("lib/c.zig"), - .link_libc = true, - .target = target, - .optimize = optimize, - }); - c_module.addIncludePath(b.path("lib/clibs")); - return c_module; -} - fn stepCopyData( b: *Build, target: Build.ResolvedTarget, diff --git a/data/wawa.png b/data/wawa.png new file mode 100644 index 0000000..ddb1673 Binary files /dev/null and b/data/wawa.png differ diff --git a/lib/c.zig b/lib/c.zig deleted file mode 100644 index 26c88ca..0000000 --- a/lib/c.zig +++ /dev/null @@ -1,3 +0,0 @@ -pub usingnamespace @cImport({ - @cInclude("stb_image.h"); -}); diff --git a/src/assets.zig b/src/assets.zig new file mode 100644 index 0000000..03a43a3 --- /dev/null +++ b/src/assets.zig @@ -0,0 +1,104 @@ +const std = @import("std"); +const sdl = @import("sdl"); +const err = @import("error.zig"); +const c = @import("c.zig"); +const comp = @import("components.zig"); +const Game = @import("game.zig"); +const Graphics = @import("graphics.zig"); +const Assets = @This(); + +const Storage = comp.Storage(Asset, .{}); + +var storage: Storage = undefined; + +pub const AssetType = enum { + texture, +}; +pub const Texture = struct { + handle: Storage.Key, +}; + +const Asset = struct { + path: []const u8, + data: union(AssetType) { + texture: AssetTexture, + }, +}; +pub const AssetTexture = struct { + texture: *sdl.GPUTexture, + sampler: *sdl.GPUSampler, +}; + +pub fn init() void { + Assets.storage = Storage.init(); +} +pub fn deinit() void { + var iter = Assets.storage.iter(); + while (iter.next()) |asset| { + Assets.freeAsset(asset); + } + Assets.storage.deinit(); +} +pub fn load(comptime asset_type: AssetType, path: []const u8) typeFromAssetType(asset_type) { + switch (asset_type) { + .texture => { + const data = loadFile(Game.alloc, path) catch |e| err.file(e, path); + var x: i32 = undefined; + var y: i32 = undefined; + var z: i32 = undefined; + const image = c.stbi_load_from_memory(@ptrCast(data), @intCast(data.len), &x, &y, &z, 4); + Game.alloc.free(data); + if (image == null) err.stbi(); + const image_slice = image[0..@intCast(x * y * z)]; + const texture, const sampler = Graphics.loadTexture(@intCast(x), @intCast(y), image_slice); + c.stbi_image_free(image); + return .{ .handle = Assets.storage.add(.{ + .path = path, + .data = .{ .texture = .{ + .texture = texture, + .sampler = sampler, + } }, + }) }; + }, + } +} +pub fn free(asset: anytype) void { + if (Assets.storage.free(asset.handle)) |stored| { + freeAsset(stored); + } +} +pub fn freeAsset(asset: *Asset) void { + switch (asset.data) { + .texture => { + Graphics.unloadTexture(asset.data.texture.texture, asset.data.texture.sampler); + }, + } +} +pub fn get(asset: anytype) ?assetTypeFromType(@TypeOf(asset)) { + if (Assets.storage.get(asset.handle)) |stored| { + switch (@TypeOf(asset)) { + Texture => { + return stored.data.texture; + }, + else => @compileError("Cannot get asset of type " ++ @typeName(@TypeOf(asset))), + } + } + unreachable; +} + +fn loadFile(alloc: std.mem.Allocator, path: []const u8) ![]u8 { + const file = try std.fs.cwd().openFile(path, .{}); + defer file.close(); + return file.readToEndAlloc(alloc, std.math.maxInt(i32)); +} +fn typeFromAssetType(comptime asset_type: AssetType) type { + return switch (asset_type) { + .texture => Texture, + }; +} +fn assetTypeFromType(comptime T: type) type { + return switch (T) { + Texture => AssetTexture, + else => unreachable, + }; +} diff --git a/src/c.zig b/src/c.zig new file mode 100644 index 0000000..75e4d75 --- /dev/null +++ b/src/c.zig @@ -0,0 +1,8 @@ +pub usingnamespace @cImport({ + @cDefine("STBI_NO_GIF", "1"); + @cDefine("STBI_NO_JPEG", "1"); + @cDefine("STBI_NO_HDR", "1"); + @cDefine("STBI_NO_TGA", "1"); + @cDefine("STB_IMAGE_IMPLEMENTATION", "1"); + @cInclude("stb_image.h"); +}); diff --git a/src/components.zig b/src/components.zig index 9b94985..5212140 100644 --- a/src/components.zig +++ b/src/components.zig @@ -51,8 +51,12 @@ pub fn Storage(comptime T: type, comptime options: StorageOptions) type { } return null; } - pub fn free(self: *Self, key: Key) bool { + pub fn free(self: *Self, key: Key) ?*T { self.components.items[key.cell].count -= 1; + if (self.components.items[key.cell].count == 0) { + return &self.components.items[key.cell].component; + } + return null; } pub fn iter(self: *Self) Iterator { return .{ diff --git a/src/error.zig b/src/error.zig index a6389a5..28550dc 100644 --- a/src/error.zig +++ b/src/error.zig @@ -14,3 +14,7 @@ const FileError = std.mem.Allocator.Error || std.fs.File.OpenError || std.fs.Fil pub fn file(err: FileError, path: []const u8) noreturn { std.debug.panic("Error while reading \"{s}\": {any}", .{ path, err }); } + +pub fn stbi() noreturn { + std.debug.panic("STBI error!\n", .{}); +} diff --git a/src/game.zig b/src/game.zig index bbacff8..9cae0f6 100644 --- a/src/game.zig +++ b/src/game.zig @@ -5,6 +5,7 @@ const err = @import("error.zig"); const Mouse = @import("mouse.zig"); const Keyboard = @import("keyboard.zig"); +pub const Assets = @import("assets.zig"); pub const Graphics = @import("graphics.zig"); pub const World = @import("world.zig"); @@ -28,6 +29,7 @@ pub fn init(game_alloc: std.mem.Allocator) void { Game.keyboard = .{}; Game.mouse = .{ .x = 0, .y = 0, .dx = 0, .dy = 0 }; Graphics.create(); + Assets.init(); World.initDebug(); } @@ -114,6 +116,7 @@ fn processEvents() void { pub fn deinit() void { World.deinit(); + Assets.deinit(); Graphics.destroy(); sdl.Quit(); } diff --git a/src/graphics.zig b/src/graphics.zig index 6066c13..86b82e5 100644 --- a/src/graphics.zig +++ b/src/graphics.zig @@ -2,6 +2,7 @@ const std = @import("std"); const sdl = @import("sdl"); const err = @import("error.zig"); const presets = @import("graphics/presets.zig"); +const Assets = @import("assets.zig"); pub const Transform = @import("graphics/transform.zig"); pub const Camera = @import("graphics/camera.zig"); @@ -11,11 +12,6 @@ pub const Mesh = struct { vertex_count: usize, }; -pub const Texture = struct { - texture: *sdl.GPUTexture, - sampler: *sdl.GPUSampler, -}; - var window: *sdl.Window = undefined; var renderer: *sdl.Renderer = undefined; var device: *sdl.GPUDevice = undefined; @@ -45,7 +41,7 @@ var to_resize: ?[2]u32 = null; const VERTEX_BUFFER_DEFAULT_CAPACITY = 1024; const VERTEX_BUFFER_GROWTH_MULTIPLIER = 2; -const TRANSFER_BUFFER_DEFAULT_CAPACITY = 1024; +const TRANSFER_BUFFER_DEFAULT_CAPACITY = 4096; const BYTES_PER_VERTEX = 5 * 4; const Graphics = @This(); @@ -193,8 +189,9 @@ pub fn destroy() void { sdl.DestroyGPUDevice(Graphics.device); } -pub fn loadTexture(width: u32, height: u32, texture_bytes: []const u8) Texture { - const target_format = sdl.SDL_GetGPUSwapchainTextureFormat(Graphics.device, Graphics.window); +pub fn loadTexture(width: u32, height: u32, texture_bytes: []const u8) struct { *sdl.GPUTexture, *sdl.GPUSampler } { + // const target_format = sdl.SDL_GetGPUSwapchainTextureFormat(Graphics.device, Graphics.window); + const target_format = sdl.GPU_TEXTUREFORMAT_R8G8B8A8_UNORM; const texture = sdl.CreateGPUTexture(Graphics.device, &sdl.GPUTextureCreateInfo{ .format = target_format, @@ -242,15 +239,15 @@ pub fn loadTexture(width: u32, height: u32, texture_bytes: []const u8) Texture { .min_filter = sdl.GPU_FILTER_LINEAR, }) orelse err.sdl(); - return Texture{ - .texture = texture, - .sampler = sampler, + return .{ + texture, + sampler, }; } -pub fn unloadTexture(texture: Texture) void { - sdl.ReleaseGPUSampler(Graphics.device, texture.sampler); - sdl.ReleaseGPUTexture(Graphics.device, texture.texture); +pub fn unloadTexture(texture: *sdl.GPUTexture, sampler: *sdl.GPUSampler) void { + sdl.ReleaseGPUSampler(Graphics.device, sampler); + sdl.ReleaseGPUTexture(Graphics.device, texture); } pub fn loadMesh(mesh_bytes: []const u8) Mesh { @@ -388,13 +385,14 @@ pub fn beginDraw() bool { return true; } -pub fn drawMesh(mesh: Mesh, texture: Texture, transform: Transform) void { +pub fn drawMesh(mesh: Mesh, texture: Assets.Texture, transform: Transform) void { if (Graphics.render_pass == null) return; + const asset_texture = Assets.get(texture) orelse return; sdl.PushGPUVertexUniformData(Graphics.command_buffer, 1, &transform.matrix(), 16 * 4); sdl.BindGPUFragmentSamplers(Graphics.render_pass, 0, &sdl.GPUTextureSamplerBinding{ - .texture = texture.texture, - .sampler = texture.sampler, + .texture = asset_texture.texture, + .sampler = asset_texture.sampler, }, 1); sdl.DrawGPUPrimitives(Graphics.render_pass, @intCast(mesh.vertex_count), 1, @intCast(mesh.vertex_start), 0); } diff --git a/src/world.zig b/src/world.zig index 60dae8b..8c76120 100644 --- a/src/world.zig +++ b/src/world.zig @@ -1,4 +1,5 @@ const Graphics = @import("graphics.zig"); +const Assets = @import("assets.zig"); const Entity = @import("entity.zig"); const Time = @import("time.zig"); const comp = @import("components.zig"); @@ -9,7 +10,7 @@ var entities: comp.Storage(Entity, .{}) = undefined; pub var plane_mesh: Graphics.Mesh = undefined; pub var cube_mesh: Graphics.Mesh = undefined; -pub var texture: Graphics.Texture = undefined; +pub var texture: Assets.Texture = undefined; const World = @This(); pub fn initDebug() void { @@ -35,13 +36,13 @@ pub fn initDebug() void { time = Time.ZERO; World.plane_mesh = Graphics.loadMesh(@ptrCast(&PLANE_MESH_DATA)); World.cube_mesh = Graphics.loadMesh(@ptrCast(&CUBE_MESH_DATA)); - World.texture = Graphics.loadTexture(2, 2, @ptrCast(&TEXTURE_DATA)); + World.texture = Assets.load(.texture, "data/wawa.png"); } pub fn deinit() void { Graphics.unloadMesh(World.plane_mesh); Graphics.unloadMesh(World.cube_mesh); - Graphics.unloadTexture(World.texture); + Assets.free(World.texture); World.entities.deinit(); } @@ -99,30 +100,35 @@ const CUBE_MESH_DATA = [_]f32{ 0.5, -0.5, -0.5, 0.0, 0.0, -0.5, -0.5, -0.5, 0.0, 0.0, 0.5, 0.5, -0.5, 0.0, 0.0, + 0.5, 0.5, -0.5, 0.0, 0.0, 0.5, 0.5, 0.5, 0.0, 0.0, 0.5, -0.5, -0.5, 0.0, 0.0, 0.5, -0.5, 0.5, 0.0, 0.0, 0.5, -0.5, -0.5, 0.0, 0.0, 0.5, 0.5, 0.5, 0.0, 0.0, - 0.5, 0.5, 0.5, 0.0, 0.0, + + 0.5, 0.5, 0.5, 1.0, 0.0, -0.5, 0.5, 0.5, 0.0, 0.0, - 0.5, -0.5, 0.5, 0.0, 0.0, - -0.5, -0.5, 0.5, 0.0, 0.0, - 0.5, -0.5, 0.5, 0.0, 0.0, + 0.5, -0.5, 0.5, 1.0, 1.0, + -0.5, -0.5, 0.5, 0.0, 1.0, + 0.5, -0.5, 0.5, 1.0, 1.0, -0.5, 0.5, 0.5, 0.0, 0.0, + -0.5, 0.5, 0.5, 0.0, 0.0, -0.5, 0.5, -0.5, 0.0, 0.0, -0.5, -0.5, 0.5, 0.0, 0.0, -0.5, -0.5, -0.5, 0.0, 0.0, -0.5, -0.5, 0.5, 0.0, 0.0, -0.5, 0.5, -0.5, 0.0, 0.0, + -0.5, 0.5, 0.5, 0.0, 0.0, 0.5, 0.5, 0.5, 0.0, 0.0, -0.5, 0.5, -0.5, 0.0, 0.0, 0.5, 0.5, -0.5, 0.0, 0.0, -0.5, 0.5, -0.5, 0.0, 0.0, 0.5, 0.5, 0.5, 0.0, 0.0, + -0.5, -0.5, -0.5, 0.0, 0.0, 0.5, -0.5, -0.5, 0.0, 0.0, -0.5, -0.5, 0.5, 0.0, 0.0,