mirror of https://github.com/bjornbytes/lovr.git
Compare commits
3 Commits
e5d4ac10cb
...
408fcb8b37
Author | SHA1 | Date |
---|---|---|
|
408fcb8b37 | |
|
1752b220bf | |
|
af2c53a762 |
|
@ -440,7 +440,7 @@ void luax_optcolor(lua_State* L, int index, float color[4]) {
|
|||
color[0] = color[1] = color[2] = color[3] = 1.f;
|
||||
break;
|
||||
case LUA_TNUMBER: {
|
||||
uint32_t x = lua_tonumber(L, -1);
|
||||
uint32_t x = lua_tonumber(L, index);
|
||||
color[0] = ((x >> 16) & 0xff) / 255.f;
|
||||
color[1] = ((x >> 8) & 0xff) / 255.f;
|
||||
color[2] = ((x >> 0) & 0xff) / 255.f;
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#include "util.h"
|
||||
#include <lua.h>
|
||||
#include <lauxlib.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
static int l_lovrPassGetType(lua_State* L) {
|
||||
|
@ -555,14 +556,35 @@ static int l_lovrPassText(lua_State* L) {
|
|||
Pass* pass = luax_checktype(L, 1, Pass);
|
||||
Font* font = luax_totype(L, 2, Font);
|
||||
int index = font ? 3 : 2;
|
||||
size_t length;
|
||||
const char* text = luaL_checklstring(L, index++, &length);
|
||||
uint32_t count = 0;
|
||||
ColoredString string;
|
||||
ColoredString* strings = &string;
|
||||
if (lua_istable(L, index)) {
|
||||
count = luax_len(L, index) / 2;
|
||||
strings = malloc(count * sizeof(*strings));
|
||||
lovrAssert(strings, "Out of memory");
|
||||
for (uint32_t i = 0; i < count; i++) {
|
||||
lua_rawgeti(L, index, i * 2 + 1);
|
||||
lua_rawgeti(L, index, i * 2 + 2);
|
||||
luax_optcolor(L, -2, strings[i].color);
|
||||
lovrCheck(lua_isstring(L, -1), "Expected a string to print");
|
||||
strings[i].string = luaL_checklstring(L, -1, &strings[i].length);
|
||||
lua_pop(L, 2);
|
||||
}
|
||||
index++;
|
||||
} else {
|
||||
strings[0].string = luaL_checklstring(L, index, &strings[0].length);
|
||||
strings[0].color[0] = strings[0].color[1] = strings[0].color[2] = strings[0].color[3] = 1.f;
|
||||
count = 1;
|
||||
index++;
|
||||
}
|
||||
float transform[16];
|
||||
index = luax_readmat4(L, index++, transform, 1);
|
||||
float wrap = luax_optfloat(L, index++, 0.);
|
||||
HorizontalAlign halign = luax_checkenum(L, index++, HorizontalAlign, "center");
|
||||
VerticalAlign valign = luax_checkenum(L, index++, VerticalAlign, "middle");
|
||||
lovrPassText(pass, font, text, length, transform, wrap, halign, valign);
|
||||
lovrPassText(pass, font, strings, count, transform, wrap, halign, valign);
|
||||
if (strings != &string) free(strings);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -1887,7 +1887,7 @@ static Glyph* lovrFontGetGlyph(Font* font, uint32_t codepoint) {
|
|||
return glyph;
|
||||
}
|
||||
|
||||
static void lovrFontUploadNewGlyphs(Font* font, uint32_t start, const char* str, uint32_t length, GlyphVertex* vertices) {
|
||||
static void lovrFontUploadNewGlyphs(Font* font, uint32_t start, ColoredString* strings, uint32_t count, GlyphVertex* vertices) {
|
||||
if (start >= font->glyphs.length) {
|
||||
return;
|
||||
}
|
||||
|
@ -1950,23 +1950,26 @@ static void lovrFontUploadNewGlyphs(Font* font, uint32_t start, const char* str,
|
|||
}
|
||||
|
||||
// Adjust current vertex uvs
|
||||
size_t bytes;
|
||||
uint32_t codepoint;
|
||||
const char* end = str + length;
|
||||
while ((bytes = utf8_decode(str, end, &codepoint)) > 0) {
|
||||
Glyph* glyph = lovrFontGetGlyph(font, codepoint);
|
||||
if (glyph->box[2] - glyph->box[0] != 0.f) {
|
||||
vertices[0].uv.u = glyph->uv[0];
|
||||
vertices[0].uv.v = glyph->uv[1];
|
||||
vertices[1].uv.u = glyph->uv[2];
|
||||
vertices[1].uv.v = glyph->uv[1];
|
||||
vertices[2].uv.u = glyph->uv[0];
|
||||
vertices[2].uv.v = glyph->uv[3];
|
||||
vertices[3].uv.u = glyph->uv[2];
|
||||
vertices[3].uv.v = glyph->uv[3];
|
||||
vertices += 4;
|
||||
for (uint32_t i = 0; i < count; i++) {
|
||||
size_t bytes;
|
||||
uint32_t codepoint;
|
||||
const char* str = strings[i].string;
|
||||
const char* end = strings[i].string + strings[i].length;
|
||||
while ((bytes = utf8_decode(str, end, &codepoint)) > 0) {
|
||||
Glyph* glyph = lovrFontGetGlyph(font, codepoint);
|
||||
if (glyph->box[2] - glyph->box[0] != 0.f) {
|
||||
vertices[0].uv.u = glyph->uv[0];
|
||||
vertices[0].uv.v = glyph->uv[1];
|
||||
vertices[1].uv.u = glyph->uv[2];
|
||||
vertices[1].uv.v = glyph->uv[1];
|
||||
vertices[2].uv.u = glyph->uv[0];
|
||||
vertices[2].uv.v = glyph->uv[3];
|
||||
vertices[3].uv.u = glyph->uv[2];
|
||||
vertices[3].uv.v = glyph->uv[3];
|
||||
vertices += 4;
|
||||
}
|
||||
str += bytes;
|
||||
}
|
||||
str += bytes;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3400,11 +3403,16 @@ static void aline(GlyphVertex* vertices, uint32_t head, uint32_t tail, Horizonta
|
|||
}
|
||||
}
|
||||
|
||||
void lovrPassText(Pass* pass, Font* font, const char* text, uint32_t length, float* transform, float wrap, HorizontalAlign halign, VerticalAlign valign) {
|
||||
void lovrPassText(Pass* pass, Font* font, ColoredString* strings, uint32_t count, float* transform, float wrap, HorizontalAlign halign, VerticalAlign valign) {
|
||||
font = font ? font : lovrGraphicsGetDefaultFont();
|
||||
uint32_t originalGlyphCount = font->glyphs.length;
|
||||
|
||||
GlyphVertex* vertices = tempAlloc(length * 4 * sizeof(GlyphVertex));
|
||||
uint32_t maxGlyphCount = 0;
|
||||
for (uint32_t i = 0; i < count; i++) {
|
||||
maxGlyphCount += strings[i].length;
|
||||
}
|
||||
|
||||
GlyphVertex* vertices = tempAlloc(maxGlyphCount * 4 * sizeof(GlyphVertex));
|
||||
uint32_t vertexCount = 0;
|
||||
uint32_t glyphCount = 0;
|
||||
uint32_t lineCount = 1;
|
||||
|
@ -3419,79 +3427,86 @@ void lovrPassText(Pass* pass, Font* font, const char* text, uint32_t length, flo
|
|||
float scale = 1.f / font->pixelDensity;
|
||||
wrap /= scale;
|
||||
|
||||
size_t bytes;
|
||||
uint32_t codepoint;
|
||||
uint32_t previous = '\0';
|
||||
const char* str = text;
|
||||
const char* end = text + length;
|
||||
while ((bytes = utf8_decode(str, end, &codepoint)) > 0) {
|
||||
if (codepoint == ' ' || codepoint == '\t') {
|
||||
Glyph* glyph = lovrFontGetGlyph(font, ' ');
|
||||
wordStart = vertexCount;
|
||||
x += codepoint == '\t' ? glyph->advance * 4.f : glyph->advance;
|
||||
previous = '\0';
|
||||
str += bytes;
|
||||
continue;
|
||||
} else if (codepoint == '\n') {
|
||||
aline(vertices, lineStart, vertexCount, halign);
|
||||
lineStart = vertexCount;
|
||||
wordStart = vertexCount;
|
||||
x = 0.f;
|
||||
y -= leading;
|
||||
lineCount++;
|
||||
previous = '\0';
|
||||
str += bytes;
|
||||
continue;
|
||||
}
|
||||
for (uint32_t i = 0; i < count; i++) {
|
||||
size_t bytes;
|
||||
uint32_t codepoint;
|
||||
uint32_t previous = '\0';
|
||||
const char* str = strings[i].string;
|
||||
const char* end = strings[i].string + strings[i].length;
|
||||
uint8_t r = (uint8_t) (CLAMP(strings[i].color[0], 0.f, 1.f) * 255.f);
|
||||
uint8_t g = (uint8_t) (CLAMP(strings[i].color[1], 0.f, 1.f) * 255.f);
|
||||
uint8_t b = (uint8_t) (CLAMP(strings[i].color[2], 0.f, 1.f) * 255.f);
|
||||
uint8_t a = (uint8_t) (CLAMP(strings[i].color[3], 0.f, 1.f) * 255.f);
|
||||
|
||||
Glyph* glyph = lovrFontGetGlyph(font, codepoint);
|
||||
|
||||
// Keming
|
||||
if (previous) x += lovrFontGetKerning(font, previous, codepoint);
|
||||
previous = codepoint;
|
||||
|
||||
// Wrap
|
||||
if (wrap > 0.f && x + glyph->box[2] > wrap && wordStart != lineStart) {
|
||||
float dx = wordStart == vertexCount ? x : vertices[wordStart].position.x;
|
||||
float dy = leading;
|
||||
|
||||
// Shift the vertices of the overflowing word down a line and back to the beginning
|
||||
for (uint32_t i = wordStart; i < vertexCount; i++) {
|
||||
vertices[i].position.x -= dx;
|
||||
vertices[i].position.y -= dy;
|
||||
while ((bytes = utf8_decode(str, end, &codepoint)) > 0) {
|
||||
if (codepoint == ' ' || codepoint == '\t') {
|
||||
Glyph* glyph = lovrFontGetGlyph(font, ' ');
|
||||
wordStart = vertexCount;
|
||||
x += codepoint == '\t' ? glyph->advance * 4.f : glyph->advance;
|
||||
previous = '\0';
|
||||
str += bytes;
|
||||
continue;
|
||||
} else if (codepoint == '\n') {
|
||||
aline(vertices, lineStart, vertexCount, halign);
|
||||
lineStart = vertexCount;
|
||||
wordStart = vertexCount;
|
||||
x = 0.f;
|
||||
y -= leading;
|
||||
lineCount++;
|
||||
previous = '\0';
|
||||
str += bytes;
|
||||
continue;
|
||||
}
|
||||
|
||||
aline(vertices, lineStart, wordStart, halign);
|
||||
lineStart = wordStart;
|
||||
lineCount++;
|
||||
x -= dx;
|
||||
y -= dy;
|
||||
Glyph* glyph = lovrFontGetGlyph(font, codepoint);
|
||||
|
||||
// Keming
|
||||
if (previous) x += lovrFontGetKerning(font, previous, codepoint);
|
||||
previous = codepoint;
|
||||
|
||||
// Wrap
|
||||
if (wrap > 0.f && x + glyph->box[2] > wrap && wordStart != lineStart) {
|
||||
float dx = wordStart == vertexCount ? x : vertices[wordStart].position.x;
|
||||
float dy = leading;
|
||||
|
||||
// Shift the vertices of the overflowing word down a line and back to the beginning
|
||||
for (uint32_t v = wordStart; v < vertexCount; v++) {
|
||||
vertices[v].position.x -= dx;
|
||||
vertices[v].position.y -= dy;
|
||||
}
|
||||
|
||||
aline(vertices, lineStart, wordStart, halign);
|
||||
lineStart = wordStart;
|
||||
lineCount++;
|
||||
x -= dx;
|
||||
y -= dy;
|
||||
}
|
||||
|
||||
// Ignore bearing for the first character on a line so it lines up perfectly (questionable)
|
||||
if (vertexCount == lineStart) {
|
||||
x -= glyph->box[0];
|
||||
}
|
||||
|
||||
// Vertices
|
||||
float* bb = glyph->box;
|
||||
uint16_t* uv = glyph->uv;
|
||||
vertices[vertexCount++] = (GlyphVertex) { { x + bb[0], y + bb[3] }, { uv[0], uv[1] }, { r, g, b, a } };
|
||||
vertices[vertexCount++] = (GlyphVertex) { { x + bb[2], y + bb[3] }, { uv[2], uv[1] }, { r, g, b, a } };
|
||||
vertices[vertexCount++] = (GlyphVertex) { { x + bb[0], y + bb[1] }, { uv[0], uv[3] }, { r, g, b, a } };
|
||||
vertices[vertexCount++] = (GlyphVertex) { { x + bb[2], y + bb[1] }, { uv[2], uv[3] }, { r, g, b, a } };
|
||||
glyphCount++;
|
||||
|
||||
// Advance
|
||||
x += glyph->advance;
|
||||
str += bytes;
|
||||
}
|
||||
|
||||
// Ignore bearing for the first character on a line so it lines up perfectly (questionable)
|
||||
if (vertexCount == lineStart) {
|
||||
x -= glyph->box[0];
|
||||
}
|
||||
|
||||
// Vertices
|
||||
float* bb = glyph->box;
|
||||
uint16_t* uv = glyph->uv;
|
||||
vertices[vertexCount++] = (GlyphVertex) { { x + bb[0], y + bb[3] }, { uv[0], uv[1] }, { 255, 255, 255, 255 } };
|
||||
vertices[vertexCount++] = (GlyphVertex) { { x + bb[2], y + bb[3] }, { uv[2], uv[1] }, { 255, 255, 255, 255 } };
|
||||
vertices[vertexCount++] = (GlyphVertex) { { x + bb[0], y + bb[1] }, { uv[0], uv[3] }, { 255, 255, 255, 255 } };
|
||||
vertices[vertexCount++] = (GlyphVertex) { { x + bb[2], y + bb[1] }, { uv[2], uv[3] }, { 255, 255, 255, 255 } };
|
||||
glyphCount++;
|
||||
|
||||
// Advance
|
||||
x += glyph->advance;
|
||||
str += bytes;
|
||||
}
|
||||
|
||||
// Align last line
|
||||
aline(vertices, lineStart, vertexCount, halign);
|
||||
|
||||
// Resize atlas, rasterize new glyphs, upload them into atlas, recreate material
|
||||
lovrFontUploadNewGlyphs(font, originalGlyphCount, text, length, vertices);
|
||||
lovrFontUploadNewGlyphs(font, originalGlyphCount, strings, count, vertices);
|
||||
|
||||
mat4_scale(transform, scale, scale, scale);
|
||||
float totalHeight = height + leading * (lineCount - 1);
|
||||
|
|
|
@ -328,6 +328,24 @@ typedef struct {
|
|||
double spread;
|
||||
} FontInfo;
|
||||
|
||||
typedef struct {
|
||||
float color[4];
|
||||
const char* string;
|
||||
size_t length;
|
||||
} ColoredString;
|
||||
|
||||
typedef enum {
|
||||
ALIGN_LEFT,
|
||||
ALIGN_CENTER,
|
||||
ALIGN_RIGHT
|
||||
} HorizontalAlign;
|
||||
|
||||
typedef enum {
|
||||
ALIGN_TOP,
|
||||
ALIGN_MIDDLE,
|
||||
ALIGN_BOTTOM
|
||||
} VerticalAlign;
|
||||
|
||||
Font* lovrFontCreate(FontInfo* info);
|
||||
void lovrFontDestroy(void* ref);
|
||||
const FontInfo* lovrFontGetInfo(Font* font);
|
||||
|
@ -376,12 +394,6 @@ typedef enum {
|
|||
STYLE_LINE
|
||||
} DrawStyle;
|
||||
|
||||
typedef enum {
|
||||
ALIGN_LEFT,
|
||||
ALIGN_CENTER,
|
||||
ALIGN_RIGHT
|
||||
} HorizontalAlign;
|
||||
|
||||
typedef enum {
|
||||
STENCIL_KEEP,
|
||||
STENCIL_ZERO,
|
||||
|
@ -399,12 +411,6 @@ typedef enum {
|
|||
VERTEX_TRIANGLES
|
||||
} VertexMode;
|
||||
|
||||
typedef enum {
|
||||
ALIGN_TOP,
|
||||
ALIGN_MIDDLE,
|
||||
ALIGN_BOTTOM
|
||||
} VerticalAlign;
|
||||
|
||||
typedef enum {
|
||||
WINDING_COUNTERCLOCKWISE,
|
||||
WINDING_CLOCKWISE
|
||||
|
@ -483,7 +489,7 @@ void lovrPassCircle(Pass* pass, float* transform, DrawStyle style, float angle1,
|
|||
void lovrPassSphere(Pass* pass, float* transform, uint32_t segmentsH, uint32_t segmentsV);
|
||||
void lovrPassCylinder(Pass* pass, float* transform, bool capped, float angle1, float angle2, uint32_t segments);
|
||||
void lovrPassTorus(Pass* pass, float* transform, uint32_t segmentsT, uint32_t segmentsP);
|
||||
void lovrPassText(Pass* pass, Font* font, const char* text, uint32_t length, float* transform, float wrap, HorizontalAlign halign, VerticalAlign valign);
|
||||
void lovrPassText(Pass* pass, Font* font, ColoredString* strings, uint32_t count, float* transform, float wrap, HorizontalAlign halign, VerticalAlign valign);
|
||||
void lovrPassFill(Pass* pass, Texture* texture);
|
||||
void lovrPassMonkey(Pass* pass, float* transform);
|
||||
void lovrPassMesh(Pass* pass, Buffer* vertices, Buffer* indices, float* transform, uint32_t start, uint32_t count, uint32_t instances);
|
||||
|
|
Loading…
Reference in New Issue