add embedded assets
This commit is contained in:
parent
bf325b74b5
commit
7197b68637
48
build.zig
48
build.zig
@ -5,10 +5,18 @@ pub fn build(b: *std.Build) !void {
|
|||||||
const target = b.standardTargetOptions(.{});
|
const target = b.standardTargetOptions(.{});
|
||||||
const optimize = b.standardOptimizeOption(.{});
|
const optimize = b.standardOptimizeOption(.{});
|
||||||
|
|
||||||
|
const assets_dir = "assets";
|
||||||
|
const asset_files = [_][]const u8{
|
||||||
|
"sokoban/sokoban_tilesheet.png"
|
||||||
|
};
|
||||||
|
|
||||||
|
const asset_dir_realpath = try b.build_root.join(b.graph.arena, &.{ assets_dir });
|
||||||
|
|
||||||
const is_wasm = target.result.cpu.arch.isWasm();
|
const is_wasm = target.result.cpu.arch.isWasm();
|
||||||
const is_debug = (optimize == .Debug);
|
const is_debug = (optimize == .Debug);
|
||||||
|
|
||||||
const has_imgui = b.option(bool, "imgui", "ImGui integration") orelse is_debug;
|
const has_imgui = b.option(bool, "imgui", "ImGui integration") orelse is_debug;
|
||||||
|
const assets_from_filesystem = b.option(bool, "assets-filesystem", "Allow reading assets from filesystem") orelse is_debug;
|
||||||
|
|
||||||
var has_tracy = false;
|
var has_tracy = false;
|
||||||
if (!is_wasm) {
|
if (!is_wasm) {
|
||||||
@ -23,8 +31,11 @@ pub fn build(b: *std.Build) !void {
|
|||||||
|
|
||||||
const build_options = b.addOptions();
|
const build_options = b.addOptions();
|
||||||
build_options.addOption(bool, "has_imgui", has_imgui);
|
build_options.addOption(bool, "has_imgui", has_imgui);
|
||||||
|
build_options.addOption(?[]const u8, "assets_dir", if (assets_from_filesystem) asset_dir_realpath else null);
|
||||||
exe_mod.addOptions("build_options", build_options);
|
exe_mod.addOptions("build_options", build_options);
|
||||||
|
|
||||||
|
exe_mod.addImport("embedded_assets", try createEmbeddedAssets(b, assets_dir, &asset_files));
|
||||||
|
|
||||||
const math_mod = b.createModule(.{
|
const math_mod = b.createModule(.{
|
||||||
.root_source_file = b.path("src/math.zig")
|
.root_source_file = b.path("src/math.zig")
|
||||||
});
|
});
|
||||||
@ -129,6 +140,43 @@ pub fn build(b: *std.Build) !void {
|
|||||||
run_step.dependOn(run_cmd_step);
|
run_step.dependOn(run_cmd_step);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn createEmbeddedAssets(
|
||||||
|
b: *std.Build,
|
||||||
|
assets_dir: []const u8,
|
||||||
|
asset_files: []const []const u8
|
||||||
|
) !*std.Build.Module {
|
||||||
|
const write_files = b.addWriteFiles();
|
||||||
|
|
||||||
|
var embedded_assets: std.ArrayList(u8) = .empty;
|
||||||
|
defer embedded_assets.deinit(b.allocator);
|
||||||
|
|
||||||
|
try embedded_assets.appendSlice(b.allocator, "pub const files = .{\n");
|
||||||
|
const mod_embedded_assets = b.createModule(.{});
|
||||||
|
for (asset_files) |asset_file| {
|
||||||
|
mod_embedded_assets.addAnonymousImport(asset_file, .{
|
||||||
|
.root_source_file = b.path(b.pathJoin(&.{ assets_dir, asset_file }))
|
||||||
|
});
|
||||||
|
|
||||||
|
try embedded_assets.appendSlice(b.allocator, " .{ ");
|
||||||
|
|
||||||
|
try embedded_assets.appendSlice(b.allocator, ".name = \"");
|
||||||
|
try embedded_assets.appendSlice(b.allocator, asset_file);
|
||||||
|
try embedded_assets.appendSlice(b.allocator, "\"");
|
||||||
|
|
||||||
|
try embedded_assets.appendSlice(b.allocator, ", ");
|
||||||
|
|
||||||
|
try embedded_assets.appendSlice(b.allocator, ".contents = @embedFile(\"");
|
||||||
|
try embedded_assets.appendSlice(b.allocator, asset_file);
|
||||||
|
try embedded_assets.appendSlice(b.allocator, "\")");
|
||||||
|
|
||||||
|
try embedded_assets.appendSlice(b.allocator, " },\n");
|
||||||
|
}
|
||||||
|
try embedded_assets.appendSlice(b.allocator, "};\n");
|
||||||
|
mod_embedded_assets.root_source_file = write_files.add("embedded_assets.zig", embedded_assets.items);
|
||||||
|
|
||||||
|
return mod_embedded_assets;
|
||||||
|
}
|
||||||
|
|
||||||
const BuildNativeOptions = struct {
|
const BuildNativeOptions = struct {
|
||||||
name: []const u8,
|
name: []const u8,
|
||||||
root_module: *std.Build.Module,
|
root_module: *std.Build.Module,
|
||||||
|
|||||||
@ -22,14 +22,7 @@ sprite_sprite: Gfx.Sprite.Id,
|
|||||||
wall_sprite: Gfx.Sprite.Id,
|
wall_sprite: Gfx.Sprite.Id,
|
||||||
|
|
||||||
pub fn init(self: *App, plt: Platform.Init) !?u8 {
|
pub fn init(self: *App, plt: Platform.Init) !?u8 {
|
||||||
const cwd = std.Io.Dir.cwd();
|
const sokoban_spritesheet = plt.assets.readFile("sokoban/sokoban_tilesheet.png");
|
||||||
const sokoban_spritesheet = try cwd.readFileAlloc(
|
|
||||||
plt.io,
|
|
||||||
"assets/sokoban/sokoban_tilesheet.png",
|
|
||||||
plt.gpa,
|
|
||||||
.unlimited
|
|
||||||
);
|
|
||||||
defer plt.gpa.free(sokoban_spritesheet);
|
|
||||||
|
|
||||||
var image = try STBImage.load(sokoban_spritesheet);
|
var image = try STBImage.load(sokoban_spritesheet);
|
||||||
defer image.deinit();
|
defer image.deinit();
|
||||||
|
|||||||
103
src/platform.zig
103
src/platform.zig
@ -27,6 +27,8 @@ pub const ImGUI = @import("./imgui.zig");
|
|||||||
pub const Gfx = @import("./graphics.zig");
|
pub const Gfx = @import("./graphics.zig");
|
||||||
pub const Input = @import("./input.zig");
|
pub const Input = @import("./input.zig");
|
||||||
|
|
||||||
|
const EmbeddedAssets = @import("embedded_assets");
|
||||||
|
|
||||||
fn PlatformType(App: type) type {
|
fn PlatformType(App: type) type {
|
||||||
return struct {
|
return struct {
|
||||||
const Self = @This();
|
const Self = @This();
|
||||||
@ -40,6 +42,7 @@ fn PlatformType(App: type) type {
|
|||||||
started_at: Io.Timestamp,
|
started_at: Io.Timestamp,
|
||||||
last_frame_at: Io.Timestamp,
|
last_frame_at: Io.Timestamp,
|
||||||
|
|
||||||
|
assets: Assets,
|
||||||
input: Input,
|
input: Input,
|
||||||
events_buffer: [256]Input.Event,
|
events_buffer: [256]Input.Event,
|
||||||
events_overflow: bool,
|
events_overflow: bool,
|
||||||
@ -68,7 +71,8 @@ fn PlatformType(App: type) type {
|
|||||||
if (@hasDecl(App, "init")) {
|
if (@hasDecl(App, "init")) {
|
||||||
const plt = Init{
|
const plt = Init{
|
||||||
.gpa = self.gpa,
|
.gpa = self.gpa,
|
||||||
.io = self.io
|
.io = self.io,
|
||||||
|
.assets = &self.assets
|
||||||
};
|
};
|
||||||
if (try self.app.init(plt)) |exit_code| {
|
if (try self.app.init(plt)) |exit_code| {
|
||||||
std.process.exit(exit_code);
|
std.process.exit(exit_code);
|
||||||
@ -114,7 +118,8 @@ fn PlatformType(App: type) type {
|
|||||||
.io = self.io,
|
.io = self.io,
|
||||||
.input = &self.input,
|
.input = &self.input,
|
||||||
.input_events = self.events.items,
|
.input_events = self.events.items,
|
||||||
.frame = self.frame_arena.allocator()
|
.frame = self.frame_arena.allocator(),
|
||||||
|
.assets = &self.assets
|
||||||
};
|
};
|
||||||
try self.app.frame(plt);
|
try self.app.frame(plt);
|
||||||
}
|
}
|
||||||
@ -395,6 +400,7 @@ fn PlatformType(App: type) type {
|
|||||||
self.app.deinit(plt);
|
self.app.deinit(plt);
|
||||||
}
|
}
|
||||||
self.frame_arena.deinit();
|
self.frame_arena.deinit();
|
||||||
|
self.assets.deinit();
|
||||||
|
|
||||||
ImGUI.deinit();
|
ImGUI.deinit();
|
||||||
Gfx.deinit();
|
Gfx.deinit();
|
||||||
@ -410,9 +416,82 @@ pub const RunOptions = struct {
|
|||||||
height: i32 = 600,
|
height: i32 = 600,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub const Assets = struct {
|
||||||
|
arena: std.heap.ArenaAllocator,
|
||||||
|
gpa: Allocator,
|
||||||
|
io: Io,
|
||||||
|
|
||||||
|
dir: ?Io.Dir,
|
||||||
|
files: std.array_hash_map.String(File),
|
||||||
|
|
||||||
|
fn init(gpa: Allocator, io: Io, dir: ?Io.Dir) Assets {
|
||||||
|
return Assets{
|
||||||
|
.gpa = gpa,
|
||||||
|
.io = io,
|
||||||
|
.arena = .init(gpa),
|
||||||
|
.dir = dir,
|
||||||
|
.files = .empty
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn readFileFromAssetDir(self: *Assets, filename: []const u8) !?[]const u8 {
|
||||||
|
const dir = self.dir orelse return null;
|
||||||
|
|
||||||
|
const arena = self.arena.allocator();
|
||||||
|
const contents = try dir.readFileAlloc(
|
||||||
|
self.io,
|
||||||
|
filename,
|
||||||
|
arena,
|
||||||
|
.unlimited
|
||||||
|
);
|
||||||
|
|
||||||
|
const filename_owned = try arena.dupe(u8, filename);
|
||||||
|
try self.files.put(self.gpa, filename_owned, File{
|
||||||
|
.contents = contents
|
||||||
|
});
|
||||||
|
|
||||||
|
return contents;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn readFile(self: *Assets, filename: []const u8) []const u8 {
|
||||||
|
if (self.files.get(filename)) |file| {
|
||||||
|
return file.contents;
|
||||||
|
} else {
|
||||||
|
if (self.readFileFromAssetDir(filename)) |maybe_contents| {
|
||||||
|
if (maybe_contents) |contents| {
|
||||||
|
log.warn("Read file from which isn't defined in assets: '{s}'", .{filename});
|
||||||
|
return contents;
|
||||||
|
} else {
|
||||||
|
log.err("Attempt to read file '{s}', which doesn't exist", .{filename});
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
} else |err| {
|
||||||
|
log.err("Failed to read file from filesystem: {}", .{err});
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn insertStaticFile(self: *Assets, filename: []const u8, contents: []const u8) !void {
|
||||||
|
try self.files.put(self.gpa, filename, File{
|
||||||
|
.contents = contents
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deinit(self: *Assets) void {
|
||||||
|
self.arena.deinit();
|
||||||
|
self.files.deinit(self.gpa);
|
||||||
|
}
|
||||||
|
|
||||||
|
const File = struct {
|
||||||
|
contents: []const u8
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
pub const Init = struct {
|
pub const Init = struct {
|
||||||
gpa: Allocator,
|
gpa: Allocator,
|
||||||
io: Io,
|
io: Io,
|
||||||
|
assets: *Assets
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const Deinit = struct {
|
pub const Deinit = struct {
|
||||||
@ -427,6 +506,7 @@ pub const Frame = struct {
|
|||||||
frame: Allocator,
|
frame: Allocator,
|
||||||
gpa: Allocator,
|
gpa: Allocator,
|
||||||
io: Io,
|
io: Io,
|
||||||
|
assets: *Assets,
|
||||||
input: *Input,
|
input: *Input,
|
||||||
input_events: []Input.Event,
|
input_events: []Input.Event,
|
||||||
|
|
||||||
@ -446,12 +526,22 @@ pub const Frame = struct {
|
|||||||
pub fn run(App: type, opts: RunOptions) void {
|
pub fn run(App: type, opts: RunOptions) void {
|
||||||
const arena = opts.init.arena.allocator();
|
const arena = opts.init.arena.allocator();
|
||||||
const gpa = opts.init.gpa;
|
const gpa = opts.init.gpa;
|
||||||
|
const io = opts.init.io;
|
||||||
|
|
||||||
|
var assets_dir: ?Io.Dir = null;
|
||||||
|
if (build_options.assets_dir) |assets_dir_path| {
|
||||||
|
const cwd = Io.Dir.cwd();
|
||||||
|
assets_dir = cwd.openDir(io, assets_dir_path, .{}) catch |e| blk: {
|
||||||
|
log.err("Failed to open assets directory: {}", .{e});
|
||||||
|
break :blk null;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
const PlatformApp = PlatformType(App);
|
const PlatformApp = PlatformType(App);
|
||||||
const plt = arena.create(PlatformApp) catch @panic("OOM");
|
const plt = arena.create(PlatformApp) catch @panic("OOM");
|
||||||
plt.* = PlatformApp{
|
plt.* = PlatformApp{
|
||||||
.gpa = gpa,
|
.gpa = gpa,
|
||||||
.io = opts.init.io,
|
.io = io,
|
||||||
.cli_args = opts.init.minimal.args.toSlice(arena) catch @panic("OOM"),
|
.cli_args = opts.init.minimal.args.toSlice(arena) catch @panic("OOM"),
|
||||||
.app = undefined,
|
.app = undefined,
|
||||||
.input = undefined,
|
.input = undefined,
|
||||||
@ -463,9 +553,14 @@ pub fn run(App: type, opts: RunOptions) void {
|
|||||||
.last_key_press_is_repeat = false,
|
.last_key_press_is_repeat = false,
|
||||||
.last_key_pressed = null,
|
.last_key_pressed = null,
|
||||||
.frame_arena = .init(gpa),
|
.frame_arena = .init(gpa),
|
||||||
.show_imgui = builtin.mode == .Debug
|
.show_imgui = builtin.mode == .Debug,
|
||||||
|
.assets = .init(gpa, io, assets_dir)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
inline for (EmbeddedAssets.files) |file| {
|
||||||
|
plt.assets.insertStaticFile(file.name, file.contents) catch @panic("OOM");
|
||||||
|
}
|
||||||
|
|
||||||
var icon: sapp.IconDesc = .{};
|
var icon: sapp.IconDesc = .{};
|
||||||
icon.sokol_default = true;
|
icon.sokol_default = true;
|
||||||
// TODO: Don't hard code icon path, allow changing through options
|
// TODO: Don't hard code icon path, allow changing through options
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user