add fontstash
This commit is contained in:
parent
44968f35f2
commit
12cf7201ac
@ -16,3 +16,10 @@ Cross-compile for Windows from Linux:
|
|||||||
```sh
|
```sh
|
||||||
zig build -Dtarget=x86_64-windows
|
zig build -Dtarget=x86_64-windows
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## TODO
|
||||||
|
|
||||||
|
* Use [Skribidi](https://github.com/memononen/Skribidi) instead of fontstash for text rendering
|
||||||
|
* Gamepad support.
|
||||||
|
* WASM Support. Currently a build config isn't provided for this target.
|
||||||
|
* Update build config for other platforms to reduce binary size. All of the video and audio drivers aren't needed, only gamepads
|
||||||
|
|||||||
51
build.zig
51
build.zig
@ -3,7 +3,7 @@ const cimgui = @import("cimgui");
|
|||||||
const sokol = @import("sokol");
|
const sokol = @import("sokol");
|
||||||
const builtin = @import("builtin");
|
const builtin = @import("builtin");
|
||||||
|
|
||||||
pub fn build(b: *std.Build) void {
|
pub fn build(b: *std.Build) !void {
|
||||||
const target = b.standardTargetOptions(.{});
|
const target = b.standardTargetOptions(.{});
|
||||||
const optimize = b.standardOptimizeOption(.{});
|
const optimize = b.standardOptimizeOption(.{});
|
||||||
|
|
||||||
@ -11,7 +11,9 @@ pub fn build(b: *std.Build) void {
|
|||||||
var has_tracy = b.option(bool, "tracy", "Tracy integration") orelse (optimize == .Debug);
|
var has_tracy = b.option(bool, "tracy", "Tracy integration") orelse (optimize == .Debug);
|
||||||
const has_console = b.option(bool, "console", "Show console (Window only)") orelse (optimize == .Debug);
|
const has_console = b.option(bool, "console", "Show console (Window only)") orelse (optimize == .Debug);
|
||||||
|
|
||||||
if (target.result.cpu.arch.isWasm()) {
|
const isWasm = target.result.cpu.arch.isWasm();
|
||||||
|
|
||||||
|
if (isWasm) {
|
||||||
has_tracy = false;
|
has_tracy = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -19,6 +21,7 @@ pub fn build(b: *std.Build) void {
|
|||||||
.root_source_file = b.path("src/main.zig"),
|
.root_source_file = b.path("src/main.zig"),
|
||||||
.target = target,
|
.target = target,
|
||||||
.optimize = optimize,
|
.optimize = optimize,
|
||||||
|
.link_libc = true
|
||||||
});
|
});
|
||||||
|
|
||||||
const cimgui_conf = cimgui.getConfig(false);
|
const cimgui_conf = cimgui.getConfig(false);
|
||||||
@ -27,6 +30,7 @@ pub fn build(b: *std.Build) void {
|
|||||||
.optimize = optimize,
|
.optimize = optimize,
|
||||||
.with_sokol_imgui = has_imgui,
|
.with_sokol_imgui = has_imgui,
|
||||||
});
|
});
|
||||||
|
mod_main.linkLibrary(dep_sokol.artifact("sokol_clib"));
|
||||||
mod_main.addImport("sokol", dep_sokol.module("sokol"));
|
mod_main.addImport("sokol", dep_sokol.module("sokol"));
|
||||||
|
|
||||||
if (has_imgui) {
|
if (has_imgui) {
|
||||||
@ -56,6 +60,41 @@ pub fn build(b: *std.Build) void {
|
|||||||
const dep_stb_image = b.dependency("stb_image", .{});
|
const dep_stb_image = b.dependency("stb_image", .{});
|
||||||
mod_main.addImport("stb_image", dep_stb_image.module("stb_image"));
|
mod_main.addImport("stb_image", dep_stb_image.module("stb_image"));
|
||||||
|
|
||||||
|
const dep_fontstash_c = b.dependency("fontstash_c", .{});
|
||||||
|
mod_main.addIncludePath(dep_fontstash_c.path("src"));
|
||||||
|
|
||||||
|
const dep_sokol_c = b.dependency("sokol_c", .{});
|
||||||
|
{
|
||||||
|
var cflags_buffer: [64][]const u8 = undefined;
|
||||||
|
var cflags = std.ArrayListUnmanaged([]const u8).initBuffer(&cflags_buffer);
|
||||||
|
switch (sokol.resolveSokolBackend(.auto, target.result)) {
|
||||||
|
.d3d11 => try cflags.appendBounded("-DSOKOL_D3D11"),
|
||||||
|
.metal => try cflags.appendBounded("-DSOKOL_METAL"),
|
||||||
|
.gl => try cflags.appendBounded("-DSOKOL_GLCORE"),
|
||||||
|
.gles3 => try cflags.appendBounded("-DSOKOL_GLES3"),
|
||||||
|
.wgpu => try cflags.appendBounded("-DSOKOL_WGPU"),
|
||||||
|
else => @panic("unknown sokol backend"),
|
||||||
|
}
|
||||||
|
|
||||||
|
mod_main.addIncludePath(dep_sokol_c.path("util"));
|
||||||
|
mod_main.addCSourceFile(.{
|
||||||
|
.file = b.path("src/engine/fontstash/sokol_fontstash_impl.c"),
|
||||||
|
.flags = cflags.items
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO:
|
||||||
|
// const sdl = b.dependency("sdl", .{
|
||||||
|
// .optimize = optimize,
|
||||||
|
// .target = target,
|
||||||
|
// .linkage = .static,
|
||||||
|
// .default_target_config = !isWasm
|
||||||
|
// });
|
||||||
|
// mod_main.linkLibrary(sdl.artifact("SDL3"));
|
||||||
|
// if (isWasm) {
|
||||||
|
// // TODO: Define buid config for wasm
|
||||||
|
// }
|
||||||
|
|
||||||
var options = b.addOptions();
|
var options = b.addOptions();
|
||||||
options.addOption(bool, "has_imgui", has_imgui);
|
options.addOption(bool, "has_imgui", has_imgui);
|
||||||
options.addOption(bool, "has_tracy", has_tracy);
|
options.addOption(bool, "has_tracy", has_tracy);
|
||||||
@ -96,7 +135,6 @@ fn buildNative(b: *std.Build, name: []const u8, mod: *std.Build.Module, has_cons
|
|||||||
const png_to_icon_step = b.addRunArtifact(png_to_icon_tool);
|
const png_to_icon_step = b.addRunArtifact(png_to_icon_tool);
|
||||||
png_to_icon_step.addFileArg(b.path("src/assets/icon.png"));
|
png_to_icon_step.addFileArg(b.path("src/assets/icon.png"));
|
||||||
const icon_file = png_to_icon_step.addOutputFileArg("icon.ico");
|
const icon_file = png_to_icon_step.addOutputFileArg("icon.ico");
|
||||||
// const icon_file = b.path("src/assets/icon.ico"); // png_to_icon_step.addOutputFileArg("icon.ico");
|
|
||||||
|
|
||||||
const add_icon_step = AddExecutableIcon.init(exe, icon_file);
|
const add_icon_step = AddExecutableIcon.init(exe, icon_file);
|
||||||
exe.step.dependOn(&add_icon_step.step);
|
exe.step.dependOn(&add_icon_step.step);
|
||||||
@ -139,13 +177,14 @@ fn patchWasmIncludeDirs(
|
|||||||
path: std.Build.LazyPath,
|
path: std.Build.LazyPath,
|
||||||
depend_step: *std.Build.Step
|
depend_step: *std.Build.Step
|
||||||
) void {
|
) void {
|
||||||
for (module.import_table.values()) |imported_module| {
|
if (module.link_libc != null and module.link_libc.?) {
|
||||||
if (imported_module.link_libc != null and imported_module.link_libc.?) {
|
|
||||||
// need to inject the Emscripten system header include path into
|
// need to inject the Emscripten system header include path into
|
||||||
// the cimgui C library otherwise the C/C++ code won't find
|
// the cimgui C library otherwise the C/C++ code won't find
|
||||||
// C stdlib headers
|
// C stdlib headers
|
||||||
imported_module.addSystemIncludePath(path);
|
module.addSystemIncludePath(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (module.import_table.values()) |imported_module| {
|
||||||
patchWasmIncludeDirs(imported_module, path, depend_step);
|
patchWasmIncludeDirs(imported_module, path, depend_step);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -8,21 +8,33 @@
|
|||||||
.url = "git+https://github.com/floooh/sokol-zig.git#aaf291ca2d3d1cedc05d65f5a1cacae0f53d934a",
|
.url = "git+https://github.com/floooh/sokol-zig.git#aaf291ca2d3d1cedc05d65f5a1cacae0f53d934a",
|
||||||
.hash = "sokol-0.1.0-pb1HK4iDNgCom5dkY66eUBm_bYBHEl8KWFDAiqwWgpEy",
|
.hash = "sokol-0.1.0-pb1HK4iDNgCom5dkY66eUBm_bYBHEl8KWFDAiqwWgpEy",
|
||||||
},
|
},
|
||||||
|
.sokol_c = .{
|
||||||
|
.url = "git+https://github.com/floooh/sokol.git#c66a1f04e6495d635c5e913335ab2308281e0492",
|
||||||
|
.hash = "N-V-__8AAC3eYABB1DVLb4dkcEzq_xVeEZZugVfQ6DoNQBDN",
|
||||||
|
},
|
||||||
|
.fontstash_c = .{
|
||||||
|
.url = "git+https://github.com/memononen/fontstash.git#b5ddc9741061343740d85d636d782ed3e07cf7be",
|
||||||
|
.hash = "N-V-__8AAA9xHgAxdLYPmlNTy6qzv9IYqiIePEHQUOPWYQ_6",
|
||||||
|
},
|
||||||
.cimgui = .{
|
.cimgui = .{
|
||||||
.url = "git+https://github.com/floooh/dcimgui.git#33c99ef426b68030412b5a4b11487a23da9d4f13",
|
.url = "git+https://github.com/floooh/dcimgui.git#33c99ef426b68030412b5a4b11487a23da9d4f13",
|
||||||
.hash = "cimgui-0.1.0-44ClkQRJlABdFMKRqIG8KDD6jy1eQbgPO335NziPYjmL",
|
.hash = "cimgui-0.1.0-44ClkQRJlABdFMKRqIG8KDD6jy1eQbgPO335NziPYjmL",
|
||||||
.lazy = true
|
.lazy = true,
|
||||||
},
|
},
|
||||||
.tracy = .{
|
.tracy = .{
|
||||||
.url = "git+https://github.com/sagehane/zig-tracy.git#80933723efe9bf840fe749b0bfc0d610f1db1669",
|
.url = "git+https://github.com/sagehane/zig-tracy.git#80933723efe9bf840fe749b0bfc0d610f1db1669",
|
||||||
.hash = "zig_tracy-0.0.5-aOIqsX1tAACKaRRB-sraMLuNiMASXi_y-4FtRuw4cTpx",
|
.hash = "zig_tracy-0.0.5-aOIqsX1tAACKaRRB-sraMLuNiMASXi_y-4FtRuw4cTpx",
|
||||||
},
|
},
|
||||||
.tiled = .{
|
.tiled = .{
|
||||||
.path = "./libs/tiled"
|
.path = "./libs/tiled",
|
||||||
},
|
},
|
||||||
.stb_image = .{
|
.stb_image = .{
|
||||||
.path = "./libs/stb_image"
|
.path = "./libs/stb_image",
|
||||||
}
|
},
|
||||||
|
// .sdl = .{
|
||||||
|
// .url = "git+https://github.com/allyourcodebase/SDL3.git#f85824b0db782b7d01c60aaad8bcb537892394e8",
|
||||||
|
// .hash = "sdl-0.0.0-i4QD0UuFqADRQysNyJ1OvCOZnq-clcVhq3BfPcBOf9zr",
|
||||||
|
// },
|
||||||
},
|
},
|
||||||
.paths = .{
|
.paths = .{
|
||||||
"build.zig",
|
"build.zig",
|
||||||
|
|||||||
@ -1,8 +1,27 @@
|
|||||||
|
const std = @import("std");
|
||||||
|
const Gfx = @import("./engine/graphics.zig");
|
||||||
|
|
||||||
const Assets = @This();
|
const Assets = @This();
|
||||||
|
|
||||||
|
const FontName = enum {
|
||||||
|
regular,
|
||||||
|
bold,
|
||||||
|
italic,
|
||||||
|
|
||||||
|
const EnumArray = std.EnumArray(FontName, Gfx.Font.Id);
|
||||||
|
};
|
||||||
|
|
||||||
|
font_id: FontName.EnumArray,
|
||||||
|
|
||||||
pub fn init() !Assets {
|
pub fn init() !Assets {
|
||||||
|
const font_id_array: FontName.EnumArray = .init(.{
|
||||||
|
.regular = try Gfx.addFont("regular", @embedFile("assets/roboto-font/Roboto-Regular.ttf")),
|
||||||
|
.bold = try Gfx.addFont("bold", @embedFile("assets/roboto-font/Roboto-Bold.ttf")),
|
||||||
|
.italic = try Gfx.addFont("italic", @embedFile("assets/roboto-font/Roboto-Italic.ttf")),
|
||||||
|
});
|
||||||
|
|
||||||
return Assets{
|
return Assets{
|
||||||
|
.font_id = font_id_array
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
250
src/engine/fontstash/context.zig
Normal file
250
src/engine/fontstash/context.zig
Normal file
@ -0,0 +1,250 @@
|
|||||||
|
const std = @import("std");
|
||||||
|
|
||||||
|
const Font = @import("./font.zig");
|
||||||
|
const Align = Font.Align;
|
||||||
|
|
||||||
|
const Context = @This();
|
||||||
|
|
||||||
|
const c = @cImport({
|
||||||
|
@cInclude("fontstash.h");
|
||||||
|
|
||||||
|
@cInclude("sokol/sokol_gfx.h");
|
||||||
|
@cInclude("sokol/sokol_gl.h");
|
||||||
|
@cInclude("sokol_fontstash.h");
|
||||||
|
});
|
||||||
|
|
||||||
|
pub const FONScontext = c.FONScontext;
|
||||||
|
|
||||||
|
pub const Size = struct {
|
||||||
|
width: u32,
|
||||||
|
height: u32,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const TextBounds = struct {
|
||||||
|
advance: f32,
|
||||||
|
min_x: f32,
|
||||||
|
max_x: f32,
|
||||||
|
min_y: f32,
|
||||||
|
max_y: f32,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const LineBounds = struct {
|
||||||
|
min_y: f32,
|
||||||
|
max_y: f32,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const VertMetrics = struct {
|
||||||
|
ascender: f32,
|
||||||
|
descender: f32,
|
||||||
|
lineh: f32
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const Quad = struct {
|
||||||
|
x0: f32,
|
||||||
|
y0: f32,
|
||||||
|
s0: f32,
|
||||||
|
t0: f32,
|
||||||
|
|
||||||
|
x1: f32,
|
||||||
|
y1: f32,
|
||||||
|
s1: f32,
|
||||||
|
t1: f32,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const TextIterator = struct {
|
||||||
|
context: Context,
|
||||||
|
iter: c.FONStextIter,
|
||||||
|
|
||||||
|
pub fn init(ctx: Context, x: f32, y: f32, text: []const u8) TextIterator {
|
||||||
|
var self = TextIterator{
|
||||||
|
.context = ctx,
|
||||||
|
.iter = undefined
|
||||||
|
};
|
||||||
|
|
||||||
|
const success = c.fonsTextIterInit(
|
||||||
|
self.context.ctx,
|
||||||
|
&self.iter,
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
text.ptr,
|
||||||
|
text.ptr + text.len
|
||||||
|
);
|
||||||
|
if (success != 1) {
|
||||||
|
return error.fonsTextIterInit;
|
||||||
|
}
|
||||||
|
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn next(self: TextIterator) ?Quad {
|
||||||
|
var quad: c.FONSquad = undefined;
|
||||||
|
const success = c.fonsTextIterNext(self.context, &self.iter, &quad);
|
||||||
|
if (success != 1) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Quad{
|
||||||
|
.x0 = quad.x0,
|
||||||
|
.y0 = quad.y0,
|
||||||
|
.s0 = quad.s0,
|
||||||
|
.t0 = quad.t0,
|
||||||
|
|
||||||
|
.x1 = quad.x0,
|
||||||
|
.y1 = quad.y0,
|
||||||
|
.s1 = quad.s0,
|
||||||
|
.t1 = quad.t0,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
ctx: *FONScontext,
|
||||||
|
|
||||||
|
pub fn init(desc: c.sfons_desc_t) !Context {
|
||||||
|
const ctx = c.sfons_create(&desc);
|
||||||
|
if (ctx == null) {
|
||||||
|
return error.sfons_create;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Context{
|
||||||
|
.ctx = ctx.?
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deinit(self: Context) void {
|
||||||
|
c.sfons_destroy(self.ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn flush(self: Context) void {
|
||||||
|
c.sfons_flush(self.ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn addFont(self: Context, name: [*c]const u8, data: []const u8) !Font.Id {
|
||||||
|
const font_id = c.fonsAddFontMem(
|
||||||
|
self.ctx,
|
||||||
|
name,
|
||||||
|
@constCast(data.ptr),
|
||||||
|
@intCast(data.len),
|
||||||
|
0
|
||||||
|
);
|
||||||
|
if (font_id == c.FONS_INVALID) {
|
||||||
|
return error.fonsAddFontMem;
|
||||||
|
}
|
||||||
|
return @enumFromInt(font_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn addFallbackFont(self: Context, base: Font.Id, fallback: Font.Id) void {
|
||||||
|
const success = c.fonsAddFallbackFont(self.ctx, @intFromEnum(base), @intFromEnum(fallback));
|
||||||
|
if (success != 1) {
|
||||||
|
return error.fonsAddFallbackFont;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn getFontByName(self: Context, name: [*c]const u8) ?Font.Id {
|
||||||
|
const font_id = c.fonsGetFontByName(self.ctx, name);
|
||||||
|
if (font_id == c.FONS_INVALID) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return @enumFromInt(font_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: fonsSetErrorCallback
|
||||||
|
|
||||||
|
pub fn getAtlasSize(self: Context) Size {
|
||||||
|
var result: Size = .{
|
||||||
|
.width = 0,
|
||||||
|
.height = 0
|
||||||
|
};
|
||||||
|
c.fonsGetAtlasSize(self.ctx, &result.width, &result.height);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn expandAtlas(self: Context, width: u32, height: u32) !void {
|
||||||
|
const success = c.fonsExpandAtlas(self.ctx, @bitCast(width), @bitCast(height));
|
||||||
|
if (success != 1) {
|
||||||
|
return error.fonsExpandAtlas;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn resetAtlas(self: Context) !void {
|
||||||
|
const success = c.fonsResetAtlas(self.ctx);
|
||||||
|
if (success != 1) {
|
||||||
|
return error.fonsResetAtlas;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pushState(self: Context) void {
|
||||||
|
c.fonsPushState(self.ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn popState(self: Context) void {
|
||||||
|
c.fonsPopState(self.ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn clearState(self: Context) void {
|
||||||
|
c.fonsClearState(self.ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn setSize(self: Context, size: f32) void {
|
||||||
|
c.fonsSetSize(self.ctx, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn setColor(self: Context, color: u32) void {
|
||||||
|
c.fonsSetColor(self.ctx, color);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn setSpacing(self: Context, spacing: f32) void {
|
||||||
|
c.fonsSetSpacing(self.ctx, spacing);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn setBlur(self: Context, blur: f32) void {
|
||||||
|
c.fonsSetSpacing(self.ctx, blur);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn setAlign(self: Context, alignment: Align) void {
|
||||||
|
c.fonsSetAlign(self.ctx, @intFromEnum(alignment.x) | @intFromEnum(alignment.y));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn setFont(self: Context, id: Font.Id) void {
|
||||||
|
c.fonsSetFont(self.ctx, @intFromEnum(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn drawText(self: Context, x: f32, y: f32, text: []const u8) void {
|
||||||
|
const advance = c.fonsDrawText(self.ctx, x, y, text.ptr, text.ptr + text.len);
|
||||||
|
_ = advance;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn textBounds(self: Context, x: f32, y: f32, text: []const u8) TextBounds {
|
||||||
|
var bounds: f32[4] = undefined;
|
||||||
|
const advance = c.fonsTextBounds(self.ctx, x, y, text.ptr, text.ptr + text.len, &bounds);
|
||||||
|
|
||||||
|
return TextBounds{
|
||||||
|
.advance = advance,
|
||||||
|
.min_x = bounds[0],
|
||||||
|
.max_x = bounds[1],
|
||||||
|
.min_y = bounds[2],
|
||||||
|
.max_y = bounds[3]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn lineBounds(self: Context, y: f32) LineBounds {
|
||||||
|
var result: LineBounds = .{
|
||||||
|
.max_y = 0,
|
||||||
|
.min_y = 0
|
||||||
|
};
|
||||||
|
c.fonsLineBounds(self.ctx, y, &result.min_y, &result.max_y);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn vertMetrics(self: Context) void {
|
||||||
|
var result: VertMetrics = .{
|
||||||
|
.ascender = 0,
|
||||||
|
.descender = 0,
|
||||||
|
.lineh = 0
|
||||||
|
};
|
||||||
|
c.fonsVertMetrics(self.ctx, &result.ascender, &result.descender, &result.lineh);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn drawDebug(self: Context, x: f32, y: f32) void {
|
||||||
|
c.fonsDrawDebug(self.ctx, x, y);
|
||||||
|
}
|
||||||
33
src/engine/fontstash/font.zig
Normal file
33
src/engine/fontstash/font.zig
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
const std = @import("std");
|
||||||
|
|
||||||
|
const c = @cImport({
|
||||||
|
@cInclude("fontstash.h");
|
||||||
|
});
|
||||||
|
|
||||||
|
const Font = @This();
|
||||||
|
|
||||||
|
pub const Id = enum(c_int) {
|
||||||
|
_,
|
||||||
|
|
||||||
|
pub const invalid: Id = @enumFromInt(c.FONS_INVALID);
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const AlignX = enum(c_int) {
|
||||||
|
left = c.FONS_ALIGN_LEFT,
|
||||||
|
right = c.FONS_ALIGN_RIGHT,
|
||||||
|
center = c.FONS_ALIGN_CENTER,
|
||||||
|
_,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const AlignY = enum(c_int) {
|
||||||
|
top = c.FONS_ALIGN_TOP,
|
||||||
|
middle = c.FONS_ALIGN_MIDDLE,
|
||||||
|
bottom = c.FONS_ALIGN_BOTTOM,
|
||||||
|
baseline = c.FONS_ALIGN_BASELINE,
|
||||||
|
_,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const Align = struct {
|
||||||
|
x: AlignX,
|
||||||
|
y: AlignY,
|
||||||
|
};
|
||||||
3
src/engine/fontstash/root.zig
Normal file
3
src/engine/fontstash/root.zig
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
|
||||||
|
pub const Font = @import("./font.zig");
|
||||||
|
pub const Context = @import("./context.zig");
|
||||||
12
src/engine/fontstash/sokol_fontstash_impl.c
Normal file
12
src/engine/fontstash/sokol_fontstash_impl.c
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#ifdef _WIN32
|
||||||
|
#include <windows.h>
|
||||||
|
#endif
|
||||||
|
#define FONTSTASH_IMPLEMENTATION
|
||||||
|
#include "fontstash.h"
|
||||||
|
|
||||||
|
#include "sokol/sokol_gfx.h"
|
||||||
|
#include "sokol/sokol_gl.h"
|
||||||
|
#define SOKOL_FONTSTASH_IMPL
|
||||||
|
#include "sokol_fontstash.h"
|
||||||
@ -18,6 +18,8 @@ const assert = std.debug.assert;
|
|||||||
|
|
||||||
const imgui = @import("imgui.zig");
|
const imgui = @import("imgui.zig");
|
||||||
const tracy = @import("tracy");
|
const tracy = @import("tracy");
|
||||||
|
const fontstash = @import("./fontstash/root.zig");
|
||||||
|
pub const Font = fontstash.Font;
|
||||||
|
|
||||||
// TODO: Seems that there is a vertical jitter bug when resizing a window in OpenGL. Seems like a driver bug.
|
// TODO: Seems that there is a vertical jitter bug when resizing a window in OpenGL. Seems like a driver bug.
|
||||||
// From other peoples research it seems that disabling vsync when a resize event occurs fixes it.
|
// From other peoples research it seems that disabling vsync when a resize event occurs fixes it.
|
||||||
@ -55,6 +57,8 @@ var draw_frame: DrawFrame = undefined;
|
|||||||
var main_pipeline: sgl.Pipeline = .{};
|
var main_pipeline: sgl.Pipeline = .{};
|
||||||
var linear_sampler: sg.Sampler = .{};
|
var linear_sampler: sg.Sampler = .{};
|
||||||
var nearest_sampler: sg.Sampler = .{};
|
var nearest_sampler: sg.Sampler = .{};
|
||||||
|
var font_context: fontstash.Context = undefined;
|
||||||
|
pub var font_resolution_scale: f32 = 1;
|
||||||
|
|
||||||
pub fn init(options: Options) !void {
|
pub fn init(options: Options) !void {
|
||||||
draw_frame.init();
|
draw_frame.init();
|
||||||
@ -119,10 +123,19 @@ pub fn init(options: Options) !void {
|
|||||||
.mipmap_filter = .NEAREST,
|
.mipmap_filter = .NEAREST,
|
||||||
.label = "nearest-sampler",
|
.label = "nearest-sampler",
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const dpi_scale = sapp.dpiScale();
|
||||||
|
const atlas_size = 512;
|
||||||
|
const atlas_dim = std.math.ceilPowerOfTwoAssert(u32, @intFromFloat(atlas_size * dpi_scale));
|
||||||
|
font_context = try fontstash.Context.init(.{
|
||||||
|
.width = @intCast(atlas_dim),
|
||||||
|
.height = @intCast(atlas_dim),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn deinit() void {
|
pub fn deinit() void {
|
||||||
imgui.shutdown();
|
imgui.shutdown();
|
||||||
|
font_context.deinit();
|
||||||
sgl.shutdown();
|
sgl.shutdown();
|
||||||
sg.shutdown();
|
sg.shutdown();
|
||||||
}
|
}
|
||||||
@ -142,6 +155,7 @@ pub fn beginFrame() void {
|
|||||||
.dpi_scale = sapp.dpiScale()
|
.dpi_scale = sapp.dpiScale()
|
||||||
});
|
});
|
||||||
|
|
||||||
|
font_context.clearState();
|
||||||
sgl.defaults();
|
sgl.defaults();
|
||||||
sgl.matrixModeProjection();
|
sgl.matrixModeProjection();
|
||||||
sgl.ortho(0, draw_frame.screen_size.x, draw_frame.screen_size.y, 0, -1, 1);
|
sgl.ortho(0, draw_frame.screen_size.x, draw_frame.screen_size.y, 0, -1, 1);
|
||||||
@ -166,6 +180,8 @@ pub fn endFrame() void {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
font_context.flush();
|
||||||
|
|
||||||
{
|
{
|
||||||
sg.beginPass(.{
|
sg.beginPass(.{
|
||||||
.action = pass_action,
|
.action = pass_action,
|
||||||
@ -260,3 +276,35 @@ pub fn popScissor() void {
|
|||||||
|
|
||||||
sgl.scissorRectf(rect.pos.x, rect.pos.y, rect.size.x, rect.size.y, true);
|
sgl.scissorRectf(rect.pos.x, rect.pos.y, rect.size.x, rect.size.y, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn addFont(name: [*c]const u8, data: []const u8) !Font.Id {
|
||||||
|
return try font_context.addFont(name, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const DrawTextOptions = struct {
|
||||||
|
font: Font.Id,
|
||||||
|
size: f32 = 16,
|
||||||
|
color: Vec4 = rgb(255, 255, 255),
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn drawText(position: Vec2, text: []const u8, opts: DrawTextOptions) void {
|
||||||
|
pushTransform(.{ .x = 0, .y = 0}, 1/font_resolution_scale);
|
||||||
|
defer popTransform();
|
||||||
|
|
||||||
|
font_context.setFont(opts.font);
|
||||||
|
font_context.setSize(opts.size * font_resolution_scale);
|
||||||
|
font_context.setAlign(.{ .x = .left, .y = .top });
|
||||||
|
font_context.setSpacing(0);
|
||||||
|
|
||||||
|
const r: u8 = @intFromFloat(opts.color.x * 255);
|
||||||
|
const g: u8 = @intFromFloat(opts.color.y * 255);
|
||||||
|
const b: u8 = @intFromFloat(opts.color.z * 255);
|
||||||
|
const a: u8 = @intFromFloat(opts.color.w * 255);
|
||||||
|
const color: u32 = r | (@as(u32, g) << 8) | (@as(u32, b) << 16) | (@as(u32, a) << 24);
|
||||||
|
font_context.setColor(color);
|
||||||
|
font_context.drawText(
|
||||||
|
position.x * font_resolution_scale,
|
||||||
|
position.y * font_resolution_scale,
|
||||||
|
text
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|||||||
@ -179,6 +179,8 @@ fn sokolFrame(self: *Engine) !void {
|
|||||||
const ctx = ScreenScalar.push(window_size, self.game.canvas_size);
|
const ctx = ScreenScalar.push(window_size, self.game.canvas_size);
|
||||||
defer ctx.pop();
|
defer ctx.pop();
|
||||||
|
|
||||||
|
Graphics.font_resolution_scale = ctx.scale;
|
||||||
|
|
||||||
try self.game.tick(Frame{
|
try self.game.tick(Frame{
|
||||||
.time_ns = time_passed,
|
.time_ns = time_passed,
|
||||||
.dt_ns = dt_ns,
|
.dt_ns = dt_ns,
|
||||||
|
|||||||
@ -6,6 +6,10 @@ const rgb = Math.rgb;
|
|||||||
|
|
||||||
const ScreenScalar = @This();
|
const ScreenScalar = @This();
|
||||||
|
|
||||||
|
// TODO: Implement a fractional pixel perfect scalar
|
||||||
|
// Based on this video: https://www.youtube.com/watch?v=d6tp43wZqps
|
||||||
|
// And this blog: https://colececil.dev/blog/2017/scaling-pixel-art-without-destroying-it/
|
||||||
|
|
||||||
window_size: Vec2,
|
window_size: Vec2,
|
||||||
translation: Vec2,
|
translation: Vec2,
|
||||||
scale: f32,
|
scale: f32,
|
||||||
|
|||||||
@ -50,12 +50,19 @@ pub fn tick(self: *Game, frame: Engine.Frame) !void {
|
|||||||
|
|
||||||
self.player = self.player.add(dir.multiplyScalar(50 * frame.dt));
|
self.player = self.player.add(dir.multiplyScalar(50 * frame.dt));
|
||||||
|
|
||||||
|
const regular_font = self.assets.font_id.get(.regular);
|
||||||
|
|
||||||
Gfx.drawRectangle(.init(0, 0), self.canvas_size, rgb(20, 20, 20));
|
Gfx.drawRectangle(.init(0, 0), self.canvas_size, rgb(20, 20, 20));
|
||||||
const size = Vec2.init(20, 20);
|
const size = Vec2.init(20, 20);
|
||||||
Gfx.drawRectangle(self.player.sub(size.divideScalar(2)), size, rgb(200, 20, 20));
|
Gfx.drawRectangle(self.player.sub(size.divideScalar(2)), size, rgb(200, 20, 20));
|
||||||
if (dir.x != 0 or dir.y != 0) {
|
if (dir.x != 0 or dir.y != 0) {
|
||||||
Gfx.drawRectanglOutline(self.player.sub(size.divideScalar(2)), size, rgb(20, 200, 20), 3);
|
Gfx.drawRectanglOutline(self.player.sub(size.divideScalar(2)), size, rgb(20, 200, 20), 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Gfx.drawText(self.player, "Player", .{
|
||||||
|
.font = regular_font,
|
||||||
|
.size = 10
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn debug(self: *Game) !void {
|
pub fn debug(self: *Game) !void {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user