Skip to content

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

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 :)

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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