feat: use atlassian dep

This commit is contained in:
LordMZTE 2023-06-29 15:33:08 +02:00
parent 92e62ce910
commit 5baa9e057c
Signed by: LordMZTE
GPG key ID: B64802DC33A64FF6
4 changed files with 35 additions and 202 deletions

View file

@ -13,9 +13,11 @@ pub fn build(b: *std.build.Builder) void {
const dep_args = .{ .target = target, .optimize = optimize };
const boxflow_dep = b.dependency("boxflow", dep_args);
const atlassian_dep = b.dependency("atlassian", dep_args);
const zalgebra_dep = b.dependency("zalgebra", dep_args);
exe.addModule("boxflow", boxflow_dep.module("boxflow"));
exe.addModule("atlassian", atlassian_dep.module("atlassian"));
exe.addModule("zalgebra", zalgebra_dep.module("zalgebra"));
exe.linkLibC();

View file

@ -7,6 +7,10 @@
.url = "https://mzte.de/git/LordMZTE/boxflow/archive/f57b4413d060b32c7391719f319a4666a86ed092.tar.gz",
.hash = "1220053e7ffa108b9233858fbc2970865789575ae35d06a2cb4789a6829485b3a552",
},
.atlassian = .{
.url = "https://mzte.de/git/LordMZTE/atlassian/archive/3410b47f431422f2da1d2b68e83273b66e557543.tar.gz",
.hash = "122056e309d3b83351036dbda36eee1c877f8e9acdc2fdf4d618635a54bf20e5f126",
},
.zalgebra = .{
.url = "https://github.com/kooparse/zalgebra/archive/2835687295be384b1dbed4518a4a635422ace60a.tar.gz",
.hash = "1220f03cf7a6ee2ea0e1961cfbcadf5d283a76632179e354aaf944e7c80e2254d2f8",

View file

@ -1,194 +0,0 @@
const std = @import("std");
const gl = @import("gl.zig");
pub const AtlasSprite = struct {
xmin: usize,
ymin: usize,
xmax: usize,
ymax: usize,
fn isTouchingOnRight(self: *const AtlasSprite, other: *const AtlasSprite) bool {
return other.xmin == self.xmax and
other.ymin < self.ymax and
other.ymax > self.ymin;
}
fn isTouchingOnBottom(self: *const AtlasSprite, other: *const AtlasSprite) bool {
return other.ymin == self.ymax and
other.xmax > self.xmin and
other.xmin < self.xmax;
}
};
pub fn Atlas(comptime Pix: type) type {
if (Pix != u8 and Pix != u32) {
@compileError("atlas not implemented for pixel type " ++ Pix);
}
return struct {
pixels: []Pix,
stride: usize,
padding: usize,
collision_sprites: std.ArrayList(AtlasSprite),
const Self = @This();
pub fn init(
alloc: std.mem.Allocator,
stride: usize,
initial_height: usize,
padding: usize,
) !Self {
const pixels = try alloc.alloc(Pix, stride * initial_height);
for (pixels) |*pix|
pix.* = 0;
return .{
.pixels = pixels,
.stride = stride,
.collision_sprites = std.ArrayList(AtlasSprite).init(alloc),
.padding = padding,
};
}
pub fn deinit(self: *Self) void {
self.collision_sprites.allocator.free(self.pixels);
self.collision_sprites.deinit();
self.* = undefined;
}
pub fn addSprite(self: *Self, data: []const Pix, stride: usize) !AtlasSprite {
// Pile algorithm
// My idea actually! (but some greek mathematician mustve been first)
var collision_sprite = AtlasSprite{
.xmin = self.stride - stride - 1 - self.padding,
.ymin = @divExact(self.pixels.len, self.stride),
.xmax = self.stride - 1,
.ymax = @divExact(self.pixels.len, self.stride) +
@divExact(data.len, stride) + self.padding,
};
// yes, this is a state machine
const State = enum {
up,
left,
};
var state: State = .up;
while (true) {
switch (state) {
.up => {
if (self.isTouchingAnyOnTop(&collision_sprite)) {
state = .left;
} else {
collision_sprite.ymin -= 1;
collision_sprite.ymax -= 1;
}
},
.left => {
if (!self.isTouchingAnyOnTop(&collision_sprite)) {
state = .up;
} else if (!self.isTouchingAnyOnLeft(&collision_sprite)) {
collision_sprite.xmin -= 1;
collision_sprite.xmax -= 1;
} else {
break;
}
},
}
}
var sprite = collision_sprite;
sprite.xmax -= self.padding;
sprite.ymax -= self.padding;
try self.reserveHeight(collision_sprite.ymax);
self.copySpriteData(&sprite, data);
try self.collision_sprites.append(collision_sprite);
return sprite;
}
pub fn toGlTex(self: *const Self, tex: c_uint) !void {
if (Pix == u8) {
gl.pixelStorei(gl.UNPACK_ALIGNMENT, 1);
}
gl.bindTexture(gl.TEXTURE_2D, tex);
const format = if (Pix == u8) gl.RED else gl.RGBA;
gl.texImage2D(
gl.TEXTURE_2D,
0,
format,
@intCast(self.stride),
@intCast(@divExact(self.pixels.len, self.stride)),
0,
format,
if (Pix == u8) gl.UNSIGNED_BYTE else gl.UNSIGNED_INT_8_8_8_8,
self.pixels.ptr,
);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
}
fn isTouchingAnyOnLeft(self: *const Self, sprite: *const AtlasSprite) bool {
if (sprite.xmin == 0)
return true;
for (self.collision_sprites.items) |*s| {
if (s.isTouchingOnRight(sprite))
return true;
}
return false;
}
fn isTouchingAnyOnTop(self: *const Self, sprite: *const AtlasSprite) bool {
if (sprite.ymin == 0)
return true;
for (self.collision_sprites.items) |*s| {
if (s.isTouchingOnBottom(sprite))
return true;
}
return false;
}
fn copySpriteData(
self: *Self,
sprite: *const AtlasSprite,
data: []const Pix,
) void {
const sprite_width = sprite.xmax - sprite.xmin;
const sprite_height = sprite.ymax - sprite.ymin;
var x: usize = 0;
while (x < sprite_width) : (x += 1) {
var y: usize = 0;
while (y < sprite_height) : (y += 1) {
const atlas_x = sprite.xmin + x;
const atlas_y = sprite.ymin + y;
self.pixels[atlas_y * self.stride + atlas_x] =
data[y * sprite_width + x];
}
}
}
fn reserveHeight(self: *Self, height: usize) !void {
if (@divExact(self.pixels.len, self.stride) >= height)
return;
const prev_len = self.pixels.len;
self.pixels = try self.collision_sprites.allocator.realloc(
self.pixels,
self.stride * height,
);
for (self.pixels[prev_len..]) |*pix|
pix.* = 0;
}
};
}

View file

@ -1,13 +1,14 @@
const std = @import("std");
const zalgebra = @import("zalgebra");
const atlas = @import("atlas.zig");
const atlassian = @import("atlassian");
const c = @import("ffi.zig").c;
const gl = @import("gl.zig");
const glutil = @import("glutil.zig");
const GlState = @import("GlState.zig");
pub const FontInfo = struct {
atlas: atlas.Atlas(u8),
atlas: atlassian.Atlas(u8),
freetype: c.FT_Library,
font_face: c.FT_Face,
atlas_id: c_uint,
@ -26,7 +27,7 @@ pub const FontInfo = struct {
};
pub const Character = struct {
sprite: atlas.AtlasSprite,
sprite: atlassian.AtlasSprite,
/// Offset from the left/top
bearing: zalgebra.Vec2_i32,
advance: c_long,
@ -65,12 +66,18 @@ pub fn load() !FontInfo {
font_face.*.size.*.metrics.descender) >> 5;
var chars = CharMap.init(std.heap.c_allocator);
var atl = try atlas.Atlas(u8).init(std.heap.c_allocator, 512 - 64, 512 + 128, 4);
var atl = try atlassian.Atlas(u8).init(std.heap.c_allocator, 512 - 64, 512 + 128, 4);
errdefer atl.deinit();
var tex: c_uint = 0;
gl.genTextures(1, &tex);
gl.bindTexture(gl.TEXTURE_2D, tex);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
return .{
.atlas = atl,
.freetype = freetype,
@ -78,7 +85,7 @@ pub fn load() !FontInfo {
.atlas_id = tex,
.chars = chars,
.line_height = line_height,
.need_reupload = true,
.need_reupload = false,
};
}
@ -98,7 +105,7 @@ pub fn getChar(info: *FontInfo, codepoint: u21) !Character {
const bmp = &info.font_face.*.glyph.*.bitmap;
const sprite = if (bmp.rows * bmp.width == 0)
atlas.AtlasSprite{
atlassian.AtlasSprite{
.xmin = 0,
.ymin = 0,
.xmax = 0,
@ -204,15 +211,29 @@ pub fn textVertices(
}
pub fn renderText(vertices: []const f32, info: *FontInfo) void {
gl.bindTexture(gl.TEXTURE_2D, info.atlas_id);
if (info.need_reupload) {
std.log.info("reuploading font atlas", .{});
info.need_reupload = false;
try info.atlas.toGlTex(info.atlas_id);
gl.pixelStorei(gl.UNPACK_ALIGNMENT, 1);
gl.texImage2D(
gl.TEXTURE_2D,
0,
gl.RED,
@intCast(info.atlas.stride),
@intCast(@divExact(info.atlas.pixels.len, info.atlas.stride)),
0,
gl.RED,
gl.UNSIGNED_BYTE,
info.atlas.pixels.ptr,
);
}
gl.useProgram(GlState.state.font_shader_program);
gl.bindVertexArray(GlState.state.font_vao);
gl.bindTexture(gl.TEXTURE_2D, info.atlas_id);
gl.bindBuffer(gl.ARRAY_BUFFER, GlState.state.font_vbo);
gl.bufferData(
gl.ARRAY_BUFFER,