Compare commits

...

11 Commits

Author SHA1 Message Date
bjorn a0cd36a77b Fix torus winding; 2022-07-10 18:02:49 -07:00
bjorn 0a251495bc Fix normal matrix; 2022-07-10 18:02:44 -07:00
bjorn 698977c5be Better default torus segments; 2022-07-10 18:02:31 -07:00
bjorn e8ebb17796 Fix hexcode render pass clear; 2022-07-10 17:07:04 -07:00
bjorn cfa5613db4 gpu: render pass cache uses correct resolve layout; 2022-07-10 17:06:43 -07:00
bjorn f23ae4009f gpu: Fix render pass cache eviction; 2022-07-10 17:06:20 -07:00
bjorn dacd17066d luax_checkendpoints;
Allows drawing a sphere/capsule using 2 endpoints.
2022-07-10 13:11:47 -07:00
bjorn 4e8fe82f94 Font:getVertices; 2022-07-10 12:49:47 -07:00
bjorn 889b1d8271 Improve Buffer length estimation; 2022-07-10 12:49:11 -07:00
bjorn f89b1811aa Adjust default Buffer field locations;
I guess it's better to match them up with the default attributes.
In most cases you're going to want to manually specify them with names
or numbers anyway.
2022-07-10 12:35:02 -07:00
bjorn f7d7281e70 Add index16/index32 FieldTypes;
Similar to u16/u32 but are 1-indexed when written using tables.
2022-07-10 12:18:31 -07:00
8 changed files with 189 additions and 77 deletions

View File

@ -106,6 +106,8 @@ StringEntry lovrFieldType[] = {
[FIELD_MAT2] = ENTRY("mat2"),
[FIELD_MAT3] = ENTRY("mat3"),
[FIELD_MAT4] = ENTRY("mat4"),
[FIELD_INDEX16] = ENTRY("index16"),
[FIELD_INDEX32] = ENTRY("index32"),
{ 0 }
};
@ -245,7 +247,9 @@ static struct { uint32_t size, scalarAlign, baseAlign, components; } fieldInfo[]
[FIELD_F32x4] = { 16, 4, 16, 4 },
[FIELD_MAT2] = { 16, 4, 8, 4 },
[FIELD_MAT3] = { 64, 4, 16, 9 },
[FIELD_MAT4] = { 64, 4, 16, 16 }
[FIELD_MAT4] = { 64, 4, 16, 16 },
[FIELD_INDEX16] = { 2, 2, 2, 1 },
[FIELD_INDEX32] = { 4, 4, 4, 1 }
};
static uint32_t luax_checkfieldtype(lua_State* L, int index, uint32_t* nameHash) {
@ -296,6 +300,10 @@ static uint32_t luax_checkfieldtype(lua_State* L, int index, uint32_t* nameHash)
return FIELD_UN8x4;
}
if (length == 6 && !memcmp(string, "index", length)) {
return FIELD_INDEX32;
}
for (int i = 0; lovrFieldType[i].length; i++) {
if (length == lovrFieldType[i].length && !memcmp(string, lovrFieldType[i].string, length)) {
return i;
@ -314,6 +322,7 @@ static void luax_checkbufferformat(lua_State* L, int index, BufferInfo* info) {
case LUA_TSTRING:
info->fieldCount = 1;
info->fields[0].type = luax_checkfieldtype(L, index, &info->fields[0].hash);
info->fields[0].location = 10;
info->stride = fieldInfo[info->fields[0].type].size;
break;
case LUA_TTABLE:
@ -326,7 +335,7 @@ static void luax_checkbufferformat(lua_State* L, int index, BufferInfo* info) {
uint32_t offset = 0;
uint32_t extent = 0;
uint32_t location = 0;
uint32_t location = 10;
uint32_t maxAlign = 1;
for (int i = 0; i < length; i++) {
BufferField* field = &info->fields[i];
@ -394,6 +403,50 @@ static void luax_checkbufferformat(lua_State* L, int index, BufferInfo* info) {
}
}
uint32_t luax_getbufferlength(lua_State* L, int index, BufferInfo* info) {
switch (lua_type(L, 1)) {
case LUA_TNUMBER: return lua_tointeger(L, 1); break;
case LUA_TTABLE: {
uint32_t length = luax_len(L, 1);
if (length < info->fieldCount) {
return 1;
}
lua_rawgeti(L, 1, 1);
if (lua_istable(L, -1)) {
lua_pop(L, 1);
return length;
}
lua_pop(L, 1);
uint32_t tableStride = 0;
for (uint32_t i = 0, j = 1; i < info->fieldCount; i++) {
lua_rawgeti(L, 1, (int) j);
if (lua_isuserdata(L, -1)) {
tableStride++;
j++;
} else {
uint32_t components = fieldInfo[info->fields[i].type].components;
tableStride += components;
j += components;
}
lua_pop(L, 1);
}
return length / tableStride;
}
default: {
Blob* blob = luax_totype(L, 1, Blob);
if (blob) {
return (uint32_t) blob->size / info->stride;
} else {
return luax_typeerror(L, 1, "number, table, or Blob");
}
}
}
}
static Canvas luax_checkcanvas(lua_State* L, int index) {
Canvas canvas = {
.loads = { LOAD_CLEAR, LOAD_CLEAR, LOAD_CLEAR, LOAD_CLEAR },
@ -487,7 +540,7 @@ static Canvas luax_checkcanvas(lua_State* L, int index) {
}
}
} else if (lua_isnumber(L, -1) || lua_isuserdata(L, -1)) {
luax_optcolor(L, -1, canvas.clears[1]);
luax_optcolor(L, -1, canvas.clears[0]);
} else if (!lua_isnil(L, -1)) {
LoadAction load = lua_toboolean(L, -1) ? LOAD_DISCARD : LOAD_KEEP;
canvas.loads[0] = canvas.loads[1] = canvas.loads[2] = canvas.loads[3] = load;
@ -735,20 +788,7 @@ static int l_lovrGraphicsGetBuffer(lua_State* L) {
BufferInfo info = { 0 };
luax_checkbufferformat(L, 2, &info);
switch (lua_type(L, 1)) {
case LUA_TNUMBER: info.length = lua_tointeger(L, 1); break;
case LUA_TTABLE: info.length = luax_len(L, 1); break;
default: {
Blob* blob = luax_totype(L, 1, Blob);
if (blob) {
info.length = blob->size / info.stride;
break;
} else {
return luax_typeerror(L, 1, "number, table, or Blob");
}
}
}
info.length = luax_getbufferlength(L, 1, &info);
void* pointer;
bool hasData = !lua_isnumber(L, 1);
@ -768,20 +808,7 @@ static int l_lovrGraphicsNewBuffer(lua_State* L) {
BufferInfo info = { 0 };
luax_checkbufferformat(L, 2, &info);
switch (lua_type(L, 1)) {
case LUA_TNUMBER: info.length = lua_tointeger(L, 1); break;
case LUA_TTABLE: info.length = luax_len(L, 1); break;
default: {
Blob* blob = luax_totype(L, 1, Blob);
if (blob) {
info.length = blob->size / info.stride;
break;
} else {
return luax_typeerror(L, 1, "number, table, or Blob");
}
}
}
info.length = luax_getbufferlength(L, 1, &info);
void* pointer;
bool hasData = !lua_isnumber(L, 1);

View File

@ -47,7 +47,9 @@ static const uint32_t fieldComponents[] = {
[FIELD_F32x4] = 4,
[FIELD_MAT2] = 4,
[FIELD_MAT3] = 9,
[FIELD_MAT4] = 16
[FIELD_MAT4] = 16,
[FIELD_INDEX16] = 1,
[FIELD_INDEX32] = 1
};
typedef union {
@ -131,6 +133,8 @@ void luax_readbufferfield(lua_State* L, int index, int type, void* data) {
case FIELD_MAT2: p.f32[i] = (float) x; break;
case FIELD_MAT3: p.f32[i] = (float) x; break;
case FIELD_MAT4: p.f32[i] = (float) x; break;
case FIELD_INDEX16: p.u16[i] = (uint16_t) x - 1; break;
case FIELD_INDEX32: p.u32[i] = (uint32_t) x - 1; break;
default: lovrUnreachable();
}
}

View File

@ -106,6 +106,16 @@ static void online(void* context, const char* string, size_t length) {
lua_rawseti(L, -2, index);
}
static int l_lovrFontGetWidth(lua_State* L) {
Font* font = luax_checktype(L, 1, Font);
uint32_t count;
ColoredString stack;
ColoredString* strings = luax_checkcoloredstrings(L, 2, &count, &stack);
float width = lovrFontGetWidth(font, strings, count);
lua_pushnumber(L, width);
return 1;
}
static int l_lovrFontGetLines(lua_State* L) {
Font* font = luax_checktype(L, 1, Font);
uint32_t count;
@ -118,14 +128,41 @@ static int l_lovrFontGetLines(lua_State* L) {
return 1;
}
static int l_lovrFontGetWidth(lua_State* L) {
static int l_lovrFontGetVertices(lua_State* L) {
Font* font = luax_checktype(L, 1, Font);
uint32_t count;
ColoredString stack;
ColoredString* strings = luax_checkcoloredstrings(L, 2, &count, &stack);
float width = lovrFontGetWidth(font, strings, count);
lua_pushnumber(L, width);
return 1;
float wrap = luax_checkfloat(L, 3);
HorizontalAlign halign = luax_checkenum(L, 4, HorizontalAlign, "center");
VerticalAlign valign = luax_checkenum(L, 5, VerticalAlign, "middle");
size_t totalLength = 0;
for (uint32_t i = 0; i < count; i++) {
totalLength += strings[i].length;
}
GlyphVertex* vertices = malloc(totalLength * 4 * sizeof(GlyphVertex));
lovrAssert(vertices, "Out of memory");
uint32_t glyphCount, lineCount;
Material* material;
lovrFontGetVertices(font, strings, count, wrap, halign, valign, vertices, &glyphCount, &lineCount, &material);
int vertexCount = glyphCount * 4;
lua_createtable(L, vertexCount, 0);
for (int i = 0; i < vertexCount; i++) {
lua_createtable(L, 4, 0);
lua_pushnumber(L, vertices[i].position.x);
lua_rawseti(L, -2, 1);
lua_pushnumber(L, vertices[i].position.y);
lua_rawseti(L, -2, 2);
lua_pushnumber(L, vertices[i].uv.u / 65535.f);
lua_rawseti(L, -2, 3);
lua_pushnumber(L, vertices[i].uv.v / 65535.f);
lua_rawseti(L, -2, 4);
lua_rawseti(L, -2, i + 1);
}
luax_pushtype(L, Material, material);
if (strings != &stack) free(strings);
free(vertices);
return 2;
}
const luaL_Reg lovrFont[] = {
@ -140,5 +177,6 @@ const luaL_Reg lovrFont[] = {
{ "getKerning", l_lovrFontGetKerning },
{ "getWidth", l_lovrFontGetWidth },
{ "getLines", l_lovrFontGetLines },
{ "getVertices", l_lovrFontGetVertices },
{ NULL, NULL }
};

View File

@ -529,11 +529,28 @@ static int l_lovrPassSphere(lua_State* L) {
return 0;
}
static bool luax_checkendpoints(lua_State* L, int index, float transform[16]) {
float *v, *u;
VectorType t1, t2;
if ((v = luax_tovector(L, index + 0, &t1)) == NULL || t1 != V_VEC3) return false;
if ((u = luax_tovector(L, index + 1, &t2)) == NULL || t2 != V_VEC3) return false;
float radius = luax_optfloat(L, index + 2, 1.);
float orientation[4];
float forward[4] = { 0.f, 0.f, -1.f, 0.f };
float direction[4];
vec3_sub(vec3_init(direction, u), v);
quat_between(orientation, forward, direction);
mat4_identity(transform);
mat4_translate(transform, (v[0] + u[0]) / 2.f, (v[1] + u[1]) / 2.f, (v[2] + u[2]) / 2.f);
mat4_rotateQuat(transform, orientation);
mat4_scale(transform, radius, radius, vec3_length(direction));
return true;
}
static int l_lovrPassCylinder(lua_State* L) {
Pass* pass = luax_checktype(L, 1, Pass);
float transform[16];
// TODO vec3+vec3
int index = luax_readmat4(L, 2, transform, -2);
int index = luax_checkendpoints(L, 2, transform) ? 5 : luax_readmat4(L, 2, transform, -2);
bool capped = lua_isnoneornil(L, index) ? true : lua_toboolean(L, index++);
float angle1 = luax_optfloat(L, index++, 0.f);
float angle2 = luax_optfloat(L, index++, 2.f * (float) M_PI);
@ -545,7 +562,7 @@ static int l_lovrPassCylinder(lua_State* L) {
static int l_lovrPassCapsule(lua_State* L) {
Pass* pass = luax_checktype(L, 1, Pass);
float transform[16];
int index = luax_readmat4(L, 2, transform, -2);
int index = luax_checkendpoints(L, 2, transform) ? 5 : luax_readmat4(L, 2, transform, -2);
uint32_t segments = luax_optu32(L, index, 32);
lovrPassCapsule(pass, transform, segments);
return 0;
@ -556,7 +573,7 @@ static int l_lovrPassTorus(lua_State* L) {
float transform[16];
int index = luax_readmat4(L, 2, transform, -2);
uint32_t segmentsT = luax_optu32(L, index++, 64);
uint32_t segmentsP = luax_optu32(L, index++, 32);
uint32_t segmentsP = luax_optu32(L, index++, segmentsT / 2);
lovrPassTorus(pass, transform, segmentsT, segmentsP);
return 0;
}

View File

@ -123,6 +123,7 @@ typedef struct {
struct {
VkFormat format;
VkImageLayout layout;
VkImageLayout resolveLayout;
gpu_load_op load;
gpu_save_op save;
} color[4];
@ -1276,6 +1277,7 @@ bool gpu_pipeline_init_graphics(gpu_pipeline* pipeline, gpu_pipeline_info* info)
for (uint32_t i = 0; i < info->colorCount; i++) {
pass.color[i].format = convertFormat(info->color[i].format, info->color[i].srgb);
pass.color[i].layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
pass.color[i].resolveLayout = resolve ? VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL : VK_IMAGE_LAYOUT_UNDEFINED;
pass.color[i].load = GPU_LOAD_OP_CLEAR;
pass.color[i].save = GPU_SAVE_OP_SAVE;
}
@ -1440,6 +1442,7 @@ void gpu_render_begin(gpu_stream* stream, gpu_canvas* canvas) {
if (pass.resolve) {
for (uint32_t i = 0; i < pass.count; i++) {
images[pass.count + i] = canvas->color[i].resolve->view;
pass.color[i].resolveLayout = canvas->color[i].resolve->layout;
}
pass.count <<= 1;
}
@ -2456,6 +2459,10 @@ static VkRenderPass getCachedRenderPass(gpu_pass_info* pass, bool compatible) {
count > 1 ? pass->color[1].layout : 0x00,
count > 2 ? pass->color[2].layout : 0x00,
count > 3 ? pass->color[3].layout : 0x00,
pass->resolve && count > 0 ? pass->color[0].resolveLayout : 0x00,
pass->resolve && count > 1 ? pass->color[1].resolveLayout : 0x00,
pass->resolve && count > 2 ? pass->color[2].resolveLayout : 0x00,
pass->resolve && count > 3 ? pass->color[3].resolveLayout : 0x00,
depth ? pass->depth.layout : 0x00,
0
};
@ -2476,8 +2483,8 @@ static VkRenderPass getCachedRenderPass(gpu_pass_info* pass, bool compatible) {
if ((row[i].hash & mask) == hash) {
gpu_cache_entry entry = row[i];
if (i > 0) {
for (uint32_t j = 0; j < i; j++) {
row[j + 1] = row[j];
for (uint32_t j = i; j >= 1; j--) {
row[j] = row[j - 1];
}
row[0] = entry;
}
@ -2505,7 +2512,7 @@ static VkRenderPass getCachedRenderPass(gpu_pass_info* pass, bool compatible) {
references[i].attachment = i;
references[i].layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
bool surface = pass->color[i].format == state.surfaceFormat;
bool discard = surface || pass->resolve || pass->color[i].load != GPU_LOAD_OP_LOAD;
bool discard = surface || pass->color[i].load != GPU_LOAD_OP_LOAD;
attachments[i] = (VkAttachmentDescription) {
.format = pass->color[i].format,
.samples = pass->samples,
@ -2528,7 +2535,7 @@ static VkRenderPass getCachedRenderPass(gpu_pass_info* pass, bool compatible) {
.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE,
.storeOp = storeOps[pass->color[i].save],
.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
.finalLayout = surface ? VK_IMAGE_LAYOUT_PRESENT_SRC_KHR : pass->color[i].layout
.finalLayout = surface ? VK_IMAGE_LAYOUT_PRESENT_SRC_KHR : pass->color[i].resolveLayout
};
}
}

View File

@ -469,7 +469,7 @@ MAF mat4 mat4_cofactor(mat4 m) {
m[6] = -(m00 * (m09 * m15 - m11 * m13) - m08 * (m01 * m15 - m03 * m13) + m12 * (m01 * m11 - m03 * m09));
m[7] = (m00 * (m09 * m14 - m10 * m13) - m08 * (m01 * m14 - m02 * m13) + m12 * (m01 * m10 - m02 * m09));
m[8] = (m01 * (m06 * m15 - m07 * m14) - m05 * (m02 * m15 - m03 * m14) + m13 * (m02 * m07 - m03 * m06));
m[9] = -(m00 * (m10 * m15 - m07 * m14) - m04 * (m02 * m15 - m03 * m14) + m12 * (m02 * m07 - m03 * m10));
m[9] = -(m00 * (m06 * m15 - m07 * m14) - m04 * (m02 * m15 - m03 * m14) + m12 * (m02 * m07 - m03 * m06));
m[10] = (m00 * (m05 * m15 - m07 * m13) - m04 * (m01 * m15 - m03 * m13) + m12 * (m01 * m07 - m03 * m05));
m[11] = -(m00 * (m05 * m14 - m06 * m13) - m04 * (m01 * m14 - m02 * m13) + m12 * (m01 * m06 - m02 * m05));
m[12] = -(m01 * (m06 * m11 - m07 * m10) - m05 * (m02 * m11 - m03 * m10) + m09 * (m02 * m07 - m03 * m06));

View File

@ -33,12 +33,6 @@ typedef struct {
struct { float u, v; } uv;
} ShapeVertex;
typedef struct {
struct { float x, y; } position;
struct { uint16_t u, v; } uv;
struct { uint8_t r, g, b, a; } color;
} GlyphVertex;
typedef struct {
struct { float x, y, z; } position;
struct { float x, y, z; } normal;
@ -2342,7 +2336,7 @@ Model* lovrModelCreate(ModelInfo* info) {
.length = data->indexCount,
.stride = indexSize,
.fieldCount = 1,
.fields[0] = { 0, 0, data->indexType == U32 ? FIELD_U32 : FIELD_I32, 0 }
.fields[0] = { 0, 0, data->indexType == U32 ? FIELD_INDEX32 : FIELD_INDEX16, 0 }
}, (void**) &indices);
}
@ -3435,6 +3429,7 @@ static void flushPipeline(Pass* pass, Draw* draw, Shader* shader) {
bool found = false;
for (uint32_t j = 0; j < draw->vertex.buffer->info.fieldCount; j++) {
BufferField field = draw->vertex.buffer->info.fields[j];
lovrCheck(field.type < FIELD_MAT2, "Currently, matrix and index types can not be used in vertex buffers");
if (field.hash ? (field.hash == attribute->hash) : (field.location == attribute->location)) {
pipeline->info.vertex.attributes[i] = (gpu_attribute) {
.buffer = 0,
@ -3563,6 +3558,10 @@ static void flushBuiltins(Pass* pass, Draw* draw, Shader* shader) {
float cofactor[16];
mat4_init(cofactor, transform);
cofactor[12] = 0.f;
cofactor[13] = 0.f;
cofactor[14] = 0.f;
cofactor[15] = 1.f;
mat4_cofactor(cofactor);
memcpy(pass->drawData->transform, transform, 64);
@ -4170,7 +4169,7 @@ void lovrPassTorus(Pass* pass, float* transform, uint32_t segmentsT, uint32_t se
uint16_t b = (t + 1) % segmentsT * segmentsP + p;
uint16_t c = (t + 0) * segmentsP + (p + 1) % segmentsP;
uint16_t d = (t + 1) % segmentsT * segmentsP + (p + 1) % segmentsP;
uint16_t quad[] = { a, b, c, b, c, d };
uint16_t quad[] = { a, b, c, c, b, d };
memcpy(indices, quad, sizeof(quad));
indices += COUNTOF(quad);
}
@ -4185,31 +4184,19 @@ static void aline(GlyphVertex* vertices, uint32_t head, uint32_t tail, float wid
}
}
void lovrPassText(Pass* pass, Font* font, ColoredString* strings, uint32_t count, float* transform, float wrap, HorizontalAlign halign, VerticalAlign valign) {
font = font ? font : lovrGraphicsGetDefaultFont();
float space = lovrFontGetGlyph(font, ' ', NULL)->advance;
size_t totalLength = 0;
for (uint32_t i = 0; i < count; i++) {
totalLength += strings[i].length;
}
uint32_t stack = tempPush();
GlyphVertex* vertices = tempAlloc(totalLength * 4 * sizeof(GlyphVertex));
void lovrFontGetVertices(Font* font, ColoredString* strings, uint32_t count, float wrap, HorizontalAlign halign, VerticalAlign valign, GlyphVertex* vertices, uint32_t* glyphCount, uint32_t* lineCount, Material** material) {
uint32_t vertexCount = 0;
uint32_t glyphCount = 0;
uint32_t lineCount = 1;
uint32_t lineStart = 0;
uint32_t wordStart = 0;
*glyphCount = 0;
*lineCount = 1;
float x = 0.f;
float y = 0.f;
float wordStartX = 0.f;
float prevWordEndX = 0.f;
float leading = lovrRasterizerGetLeading(font->info.rasterizer) * font->lineSpacing;
float ascent = lovrRasterizerGetAscent(font->info.rasterizer);
float scale = 1.f / font->pixelDensity;
wrap /= scale;
float space = lovrFontGetGlyph(font, ' ', NULL)->advance;
for (uint32_t i = 0; i < count; i++) {
size_t bytes;
@ -4239,7 +4226,7 @@ void lovrPassText(Pass* pass, Font* font, ColoredString* strings, uint32_t count
y -= leading;
wordStartX = 0.f;
prevWordEndX = 0.f;
lineCount++;
(*lineCount)++;
previous = '\0';
str += bytes;
continue;
@ -4252,8 +4239,7 @@ void lovrPassText(Pass* pass, Font* font, ColoredString* strings, uint32_t count
Glyph* glyph = lovrFontGetGlyph(font, codepoint, &resized);
if (resized) {
tempPop(stack);
lovrPassText(pass, font, strings, count, transform, wrap, halign, valign);
lovrFontGetVertices(font, strings, count, wrap, halign, valign, vertices, glyphCount, lineCount, material);
return;
}
@ -4275,7 +4261,7 @@ void lovrPassText(Pass* pass, Font* font, ColoredString* strings, uint32_t count
aline(vertices, lineStart, wordStart, prevWordEndX, halign);
lineStart = wordStart;
wordStartX = 0.f;
lineCount++;
(*lineCount)++;
x -= dx;
y -= dy;
}
@ -4287,7 +4273,7 @@ void lovrPassText(Pass* pass, Font* font, ColoredString* strings, uint32_t count
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++;
(*glyphCount)++;
// Advance
x += glyph->advance;
@ -4298,6 +4284,30 @@ void lovrPassText(Pass* pass, Font* font, ColoredString* strings, uint32_t count
// Align last line
aline(vertices, lineStart, vertexCount, x, halign);
*material = font->material;
}
void lovrPassText(Pass* pass, Font* font, ColoredString* strings, uint32_t count, float* transform, float wrap, HorizontalAlign halign, VerticalAlign valign) {
font = font ? font : lovrGraphicsGetDefaultFont();
size_t totalLength = 0;
for (uint32_t i = 0; i < count; i++) {
totalLength += strings[i].length;
}
uint32_t stack = tempPush();
GlyphVertex* vertices = tempAlloc(totalLength * 4 * sizeof(GlyphVertex));
uint32_t glyphCount;
uint32_t lineCount;
float leading = lovrRasterizerGetLeading(font->info.rasterizer) * font->lineSpacing;
float ascent = lovrRasterizerGetAscent(font->info.rasterizer);
float scale = 1.f / font->pixelDensity;
wrap /= scale;
Material* material;
lovrFontGetVertices(font, strings, count, wrap, halign, valign, vertices, &glyphCount, &lineCount, &material);
mat4_scale(transform, scale, scale, scale);
mat4_translate(transform, 0.f, -ascent + valign / 2.f * (leading * lineCount), 0.f);
@ -4308,13 +4318,13 @@ void lovrPassText(Pass* pass, Font* font, ColoredString* strings, uint32_t count
.material = font->material,
.transform = transform,
.vertex.format = VERTEX_GLYPH,
.vertex.count = vertexCount,
.vertex.count = glyphCount * 4,
.vertex.data = vertices,
.index.count = glyphCount * 6,
.index.pointer = (void**) &indices
});
for (uint32_t i = 0; i < vertexCount; i += 4) {
for (uint32_t i = 0; i < glyphCount * 4; i += 4) {
uint16_t quad[] = { i + 0, i + 2, i + 1, i + 1, i + 2, i + 3 };
memcpy(indices, quad, sizeof(quad));
indices += COUNTOF(quad);

View File

@ -138,7 +138,9 @@ typedef enum {
FIELD_F32x4,
FIELD_MAT2,
FIELD_MAT3,
FIELD_MAT4
FIELD_MAT4,
FIELD_INDEX16,
FIELD_INDEX32
} FieldType;
typedef struct {
@ -365,6 +367,12 @@ typedef enum {
ALIGN_BOTTOM
} VerticalAlign;
typedef struct {
struct { float x, y; } position;
struct { uint16_t u, v; } uv;
struct { uint8_t r, g, b, a; } color;
} GlyphVertex;
Font* lovrFontCreate(FontInfo* info);
void lovrFontDestroy(void* ref);
const FontInfo* lovrFontGetInfo(Font* font);
@ -375,6 +383,7 @@ void lovrFontSetLineSpacing(Font* font, float spacing);
float lovrFontGetKerning(Font* font, uint32_t first, uint32_t second);
float lovrFontGetWidth(Font* font, ColoredString* strings, uint32_t count);
void lovrFontGetLines(Font* font, ColoredString* strings, uint32_t count, float wrap, void (*callback)(void* context, const char* string, size_t length), void* context);
void lovrFontGetVertices(Font* font, ColoredString* strings, uint32_t count, float wrap, HorizontalAlign halign, VerticalAlign valign, GlyphVertex* vertices, uint32_t* glyphCount, uint32_t* lineCount, Material** material);
// Model