diff --git a/src/api/graphics.c b/src/api/graphics.c index 312b942e..043db7c1 100644 --- a/src/api/graphics.c +++ b/src/api/graphics.c @@ -11,11 +11,13 @@ map_int_t BlendModes; map_int_t CompareModes; map_int_t DrawModes; map_int_t FilterModes; +map_int_t HorizontalAligns; map_int_t MeshAttributeTypes; map_int_t MeshDrawModes; map_int_t MeshUsages; map_int_t PolygonWindings; map_int_t TextureProjections; +map_int_t VerticalAligns; map_int_t WrapModes; static void luax_readvertices(lua_State* L, int index, vec_float_t* points) { @@ -103,6 +105,11 @@ int l_lovrGraphicsInit(lua_State* L) { map_set(&FilterModes, "nearest", FILTER_NEAREST); map_set(&FilterModes, "linear", FILTER_LINEAR); + map_init(&HorizontalAligns); + map_set(&HorizontalAligns, "left", ALIGN_LEFT); + map_set(&HorizontalAligns, "right", ALIGN_RIGHT); + map_set(&HorizontalAligns, "center", ALIGN_CENTER); + map_init(&MeshAttributeTypes); map_set(&MeshAttributeTypes, "float", MESH_FLOAT); map_set(&MeshAttributeTypes, "byte", MESH_BYTE); @@ -127,6 +134,11 @@ int l_lovrGraphicsInit(lua_State* L) { map_set(&TextureProjections, "2d", PROJECTION_ORTHOGRAPHIC); map_set(&TextureProjections, "3d", PROJECTION_PERSPECTIVE); + map_init(&VerticalAligns); + map_set(&VerticalAligns, "top", ALIGN_TOP); + map_set(&VerticalAligns, "bottom", ALIGN_BOTTOM); + map_set(&VerticalAligns, "middle", ALIGN_MIDDLE); + map_init(&WrapModes); map_set(&WrapModes, "clamp", WRAP_CLAMP); map_set(&WrapModes, "repeat", WRAP_REPEAT); @@ -524,16 +536,12 @@ int l_lovrGraphicsCube(lua_State* L) { int l_lovrGraphicsPrint(lua_State* L) { const char* str = luaL_checkstring(L, 1); - float x = luaL_optnumber(L, 2, 0); - float y = luaL_optnumber(L, 3, 0); - float z = luaL_optnumber(L, 4, 0); - float w = luaL_optnumber(L, 5, 0); - float h = luaL_optnumber(L, 6, .1); - float angle = luaL_optnumber(L, 7, 0); - float ax = luaL_optnumber(L, 8, 0); - float ay = luaL_optnumber(L, 9, 1); - float az = luaL_optnumber(L, 10, 0); - lovrGraphicsPrint(str, x, y, z, w, h, angle, ax, ay, az); + float transform[16]; + int index = luax_readtransform(L, 2, transform); + float wrap = luaL_optnumber(L, index++, 0); + HorizontalAlign halign = *(HorizontalAlign*) luax_optenum(L, index++, "center", &HorizontalAligns, "alignment"); + VerticalAlign valign = *(VerticalAlign*) luax_optenum(L, index++, "middle", &VerticalAligns, "alignment"); + lovrGraphicsPrint(str, transform, wrap, halign, valign); return 0; } diff --git a/src/api/lovr.h b/src/api/lovr.h index 685ca162..af67def6 100644 --- a/src/api/lovr.h +++ b/src/api/lovr.h @@ -37,13 +37,15 @@ extern map_int_t DrawModes; extern map_int_t EventTypes; extern map_int_t FilterModes; extern map_int_t HeadsetEyes; +extern map_int_t HorizontalAligns; extern map_int_t MeshAttributeTypes; extern map_int_t MeshDrawModes; extern map_int_t MeshUsages; extern map_int_t PolygonWindings; extern map_int_t TextureProjections; extern map_int_t TimeUnits; +extern map_int_t VerticalAligns; extern map_int_t WrapModes; void luax_checkmeshformat(lua_State* L, int index, MeshFormat* format); -void luax_readtransform(lua_State* L, int i, mat4 transform); +int luax_readtransform(lua_State* L, int i, mat4 transform); diff --git a/src/api/types/transform.c b/src/api/types/transform.c index 7a61ba2b..005c7e8e 100644 --- a/src/api/types/transform.c +++ b/src/api/types/transform.c @@ -2,7 +2,7 @@ #include "math/mat4.h" #include "math/transform.h" -void luax_readtransform(lua_State* L, int i, mat4 m) { +int luax_readtransform(lua_State* L, int i, mat4 m) { if (lua_isnumber(L, i)) { float x = luaL_optnumber(L, i++, 0); float y = luaL_optnumber(L, i++, 0); @@ -13,11 +13,14 @@ void luax_readtransform(lua_State* L, int i, mat4 m) { float ay = luaL_optnumber(L, i++, 1); float az = luaL_optnumber(L, i++, 0); mat4_setTransform(m, x, y, z, s, angle, ax, ay, az); + return i; } else if (lua_isnoneornil(L, i)) { mat4_identity(m); + return i; } else { Transform* transform = luax_checktype(L, i, Transform); mat4_init(m, transform->matrix); + return ++i; } } diff --git a/src/graphics/font.c b/src/graphics/font.c index 6d448d98..e13b534b 100644 --- a/src/graphics/font.c +++ b/src/graphics/font.c @@ -8,6 +8,20 @@ #include #include +static int lovrFontAlignLine(vec_float_t* vertices, int index, float width, HorizontalAlign halign) { + while (index < vertices->length) { + if (halign == ALIGN_CENTER) { + vertices->data[index] -= width / 2; + } else if (halign == ALIGN_RIGHT) { + vertices->data[index] -= width; + } + + index += 5; + } + + return index; +} + Font* lovrFontCreate(FontData* fontData) { Font* font = lovrAlloc(sizeof(Font), lovrFontDestroy); if (!font) return NULL; @@ -52,14 +66,14 @@ void lovrFontDestroy(const Ref* ref) { free(font); } -void lovrFontPrint(Font* font, const char* str, float x, float y, float z, float w, float h, float angle, float ax, float ay, float az) { +void lovrFontPrint(Font* font, const char* str, mat4 transform, float wrap, HorizontalAlign halign, VerticalAlign valign) { FontAtlas* atlas = &font->atlas; float cx = 0; - float cy = -font->fontData->height * font->lineHeight / 2; + float cy = -font->fontData->height * .8; float u = atlas->width; float v = atlas->height; - float scale = h / font->fontData->height; + float scale = 1 / (float) font->fontData->height; int len = strlen(str); const char* start = str; @@ -69,6 +83,7 @@ void lovrFontPrint(Font* font, const char* str, float x, float y, float z, float size_t bytes; int linePtr = 0; + int lineCount = 1; vec_reserve(&font->vertices, len * 30); vec_clear(&font->vertices); @@ -76,16 +91,11 @@ void lovrFontPrint(Font* font, const char* str, float x, float y, float z, float while ((bytes = utf8_decode(str, end, &codepoint)) > 0) { // Newlines - if (codepoint == '\n' || (w && cx > w / scale && codepoint == ' ')) { - - // Center the line - while (linePtr < font->vertices.length) { - font->vertices.data[linePtr] -= cx / 2; - linePtr += 5; - } - + if (codepoint == '\n' || (wrap && cx * scale > wrap && codepoint == ' ')) { + linePtr = lovrFontAlignLine(&font->vertices, linePtr, cx, halign); + lineCount++; cx = 0; - cy -= font->fontData->size * font->lineHeight; + cy -= font->fontData->height * font->lineHeight; previous = '\0'; str += bytes; continue; @@ -100,7 +110,7 @@ void lovrFontPrint(Font* font, const char* str, float x, float y, float z, float // Start over if texture was repacked if (u != atlas->width || v != atlas->height) { - lovrFontPrint(font, start, x, y, z, w, h, angle, ax, ay, az); + lovrFontPrint(font, start, transform, wrap, halign, valign); return; } @@ -132,20 +142,25 @@ void lovrFontPrint(Font* font, const char* str, float x, float y, float z, float str += bytes; } - // Center the last line - while (linePtr < font->vertices.length) { - font->vertices.data[linePtr] -= cx / 2; - linePtr += 5; + // Align the last line + lovrFontAlignLine(&font->vertices, linePtr, cx, halign); + + // Calculate vertical offset + float offsety = 0; + if (valign == ALIGN_MIDDLE) { + offsety = lineCount * font->fontData->height * font->lineHeight * .5f; + } else if (valign == ALIGN_BOTTOM) { + offsety = lineCount * font->fontData->height * font->lineHeight; } // We override the depth test to LEQUAL to prevent blending issues with glyphs, not great CompareMode oldCompareMode = lovrGraphicsGetDepthTest(); + // Render! lovrGraphicsPush(); - lovrGraphicsTranslate(x, y, z); + lovrGraphicsMatrixTransform(transform); lovrGraphicsScale(scale, scale, scale); - lovrGraphicsRotate(angle, ax, ay, az); - lovrGraphicsTranslate(0, -cy / 2, 0); + lovrGraphicsTranslate(0, offsety, 0); lovrGraphicsSetDepthTest(COMPARE_LEQUAL); lovrGraphicsBindTexture(font->texture); lovrGraphicsSetShapeData(font->vertices.data, font->vertices.length); diff --git a/src/graphics/font.h b/src/graphics/font.h index e6e91e12..59944082 100644 --- a/src/graphics/font.h +++ b/src/graphics/font.h @@ -1,12 +1,25 @@ #include "loaders/font.h" #include "util.h" #include "graphics/texture.h" +#include "math/math.h" #include "lib/map/map.h" #include "lib/vec/vec.h" #include #pragma once +typedef enum { + ALIGN_LEFT, + ALIGN_RIGHT, + ALIGN_CENTER +} HorizontalAlign; + +typedef enum { + ALIGN_TOP, + ALIGN_BOTTOM, + ALIGN_MIDDLE +} VerticalAlign; + typedef struct { int x; int y; @@ -24,12 +37,12 @@ typedef struct { FontAtlas atlas; map_int_t kerning; vec_float_t vertices; - int lineHeight; + float lineHeight; } Font; Font* lovrFontCreate(FontData* fontData); void lovrFontDestroy(const Ref* ref); -void lovrFontPrint(Font* font, const char* str, float x, float y, float z, float w, float h, float angle, float ax, float ay, float az); +void lovrFontPrint(Font* font, const char* str, mat4 transform, float wrap, HorizontalAlign halign, VerticalAlign valign); float lovrFontGetLineHeight(Font* font); void lovrFontSetLineHeight(Font* font, float lineHeight); int lovrFontGetKerning(Font* font, unsigned int a, unsigned int b); diff --git a/src/graphics/graphics.c b/src/graphics/graphics.c index b88f8c6e..bba9e2d1 100644 --- a/src/graphics/graphics.c +++ b/src/graphics/graphics.c @@ -806,7 +806,7 @@ void lovrGraphicsSkybox(Skybox* skybox, float angle, float ax, float ay, float a lovrGraphicsPop(); } -void lovrGraphicsPrint(const char* str, float x, float y, float z, float w, float h, float angle, float ax, float ay, float az) { +void lovrGraphicsPrint(const char* str, mat4 transform, float wrap, HorizontalAlign halign, VerticalAlign valign) { lovrGraphicsEnsureFont(); - lovrFontPrint(state.activeFont, str, x, y, z, w, h, angle, ax, ay, az); + lovrFontPrint(state.activeFont, str, transform, wrap, halign, valign); } diff --git a/src/graphics/graphics.h b/src/graphics/graphics.h index 9e98a756..45c315b5 100644 --- a/src/graphics/graphics.h +++ b/src/graphics/graphics.h @@ -162,4 +162,4 @@ void lovrGraphicsPlane(DrawMode mode, Texture* texture, float x, float y, float void lovrGraphicsPlaneFullscreen(Texture* texture); void lovrGraphicsCube(DrawMode mode, Texture* texture, mat4 transform); void lovrGraphicsSkybox(Skybox* skybox, float angle, float ax, float ay, float az); -void lovrGraphicsPrint(const char* str, float x, float y, float z, float w, float h, float angle, float ax, float ay, float az); +void lovrGraphicsPrint(const char* str, mat4 transform, float wrap, HorizontalAlign halign, VerticalAlign valign);