Skip to content

Positioning of text for font rendering with addStringToSpriteBatch and addStringToSpriteBatchWithKerning does not behave as expected #61

Open
@stefanpartheym

Description

@stefanpartheym

Hi @Interrupt ,

recently I tried to use the builtin addStringToSpriteBatch and addStringToSpriteBatchWithKerning functions to render text to a 2D surface.
However, positioning the text seems to not work as it does for regular shapes or textures (for instance with Batcher.addRectangle).

In the following example I positioned both – a red rectangle and the text – at X: 50 and Y: 50 (using a math grid, meaning Y grows up and X grows to the right).
The result looks as follows:
Bildschirmfoto vom 2024-10-10 16-39-34

One can clearly see that the text is in a completely different position than the rectangle. I would have expected to be below the left-bottom corner of the rectangle.

const std = @import("std");
const delve = @import("delve");
const app = delve.app;

const debug = delve.debug;
const graphics = delve.platform.graphics;
const colors = delve.colors;
const input = delve.platform.input;
const math = delve.math;
const modules = delve.modules;

var gpa = std.heap.GeneralPurposeAllocator(.{}){};
var font_batch: delve.graphics.batcher.SpriteBatcher = undefined;
var batch: delve.graphics.batcher.Batcher = undefined;
var shader_blend: graphics.Shader = undefined;
const font_name = "DroidSans";

pub fn main() !void {
    try delve.init(std.heap.c_allocator);
    try registerModule();
    try app.start(app.AppConfig{ .title = "Delve Framework - 2D Fonts Example" });
}

pub fn registerModule() !void {
    const fontsExample = modules.Module{
        .name = "fonts_example",
        .init_fn = on_init,
        .tick_fn = on_tick,
        .pre_draw_fn = on_pre_draw,
        .draw_fn = on_draw,
        .cleanup_fn = on_cleanup,
    };

    try modules.registerModule(fontsExample);
}

pub fn getCamera2d() graphics.CameraMatrices {
    return graphics.CameraMatrices{
        .view = math.Mat4.lookat(
            .{ .x = 0, .y = 0, .z = 1 },
            math.Vec3.zero,
            math.Vec3.up,
        ),
        .proj = graphics.getProjectionOrtho(-1, 1, false),
    };
}

fn on_init() !void {
    debug.log("2D Fonts example module initializing", .{});
    graphics.setClearColor(colors.examples_bg_dark);

    font_batch = delve.graphics.batcher.SpriteBatcher.init(.{}) catch {
        debug.log("Error creating font sprite batch!", .{});
        return;
    };

    batch = delve.graphics.batcher.Batcher.init(.{}) catch {
        debug.log("Error creating shape batch!", .{});
        return;
    };

    _ = try delve.fonts.loadFont(font_name, "assets/fonts/" ++ font_name ++ ".ttf", 1024, 200);

    // make a shader with alpha blending
    shader_blend = try graphics.Shader.initDefault(.{ .blend_mode = graphics.BlendMode.BLEND });
}

fn on_tick(_: f32) void {
    if (input.isKeyJustPressed(.ESCAPE))
        delve.platform.app.exit();
}

fn on_pre_draw() void {
    const text_scale: f32 = 0.25;

    // Drawing position of characters, updated as each gets added
    var x_pos: f32 = 50.0;
    var y_pos: f32 = 50.0;

    const message = "This is some text!\nHello World!\n[]'.@%#$<>?;:-=_+'";

    const font_name_string = std.fmt.allocPrintZ(delve.mem.getAllocator(), "Drawing from {s}", .{font_name}) catch {
        return;
    };
    defer delve.mem.getAllocator().free(font_name_string);

    // Add font characters as sprites to our sprite batch
    // Ideally you would only do this when the text updates, and just draw the batch until then
    font_batch.reset();

    font_batch.useShader(shader_blend);

    if (delve.fonts.getLoadedFont(font_name)) |font| {
        // give the header a bit of padding
        const extra_header_line_height = font.font_size / 8;

        delve.fonts.addStringToSpriteBatchWithKerning(font, &font_batch, font_name_string, &x_pos, &y_pos, extra_header_line_height, 0, text_scale, colors.blue);
        delve.fonts.addStringToSpriteBatch(font, &font_batch, message, &x_pos, &y_pos, text_scale, colors.white);
    }

    font_batch.apply();

    batch.reset();
    const rect = delve.spatial.Rect.fromSize(math.vec2(100, 100)).setPosition(math.vec2(50, 50));
    batch.addRectangle(rect, delve.graphics.sprites.TextureRegion.default(), delve.colors.red);
    batch.apply();
}

fn on_draw() void {
    const cam = getCamera2d();
    batch.draw(cam, math.Mat4.identity);
    font_batch.draw(cam, math.Mat4.identity);
}

fn on_cleanup() !void {
    font_batch.deinit();
    shader_blend.destroy();
}

You can run the example by putting in the examples directory of the repo. Maybe you can also temporarily overwrite the fonts.zig example and the run zig build run-fonts.

Thanks :)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions