Optimize text drawing
This adds a lower-level sprite batching API intended to reduce overhead of bulk sprite submissions when it is known that no pipeline state needs to be changed in-between individual sprites.
This commit is contained in:
parent
279114cbd5
commit
b14b8493d4
6 changed files with 243 additions and 197 deletions
|
@ -354,6 +354,32 @@ typedef union ShaderCustomParams {
|
|||
Color color;
|
||||
} ShaderCustomParams;
|
||||
|
||||
typedef struct SpriteStateParams {
|
||||
Texture *primary_texture;
|
||||
Texture *aux_textures[R_NUM_SPRITE_AUX_TEXTURES];
|
||||
BlendMode blend;
|
||||
ShaderProgram *shader;
|
||||
} SpriteStateParams;
|
||||
|
||||
typedef struct SpriteScaleParams {
|
||||
union {
|
||||
float x;
|
||||
float both;
|
||||
};
|
||||
|
||||
float y;
|
||||
} SpriteScaleParams;
|
||||
|
||||
typedef struct SpriteRotationParams {
|
||||
vec3 vector;
|
||||
float angle;
|
||||
} SpriteRotationParams;
|
||||
|
||||
typedef struct SpriteFlipParams {
|
||||
unsigned char x : 1;
|
||||
unsigned char y : 1;
|
||||
} SpriteFlipParams;
|
||||
|
||||
typedef struct SpriteParams {
|
||||
const char *sprite;
|
||||
Sprite *sprite_ptr;
|
||||
|
@ -362,34 +388,35 @@ typedef struct SpriteParams {
|
|||
ShaderProgram *shader_ptr;
|
||||
|
||||
Texture *aux_textures[R_NUM_SPRITE_AUX_TEXTURES];
|
||||
|
||||
const Color *color;
|
||||
const ShaderCustomParams *shader_params;
|
||||
|
||||
BlendMode blend;
|
||||
|
||||
FloatOffset pos;
|
||||
|
||||
struct {
|
||||
union {
|
||||
float x;
|
||||
float both;
|
||||
};
|
||||
|
||||
float y;
|
||||
} scale;
|
||||
|
||||
struct {
|
||||
float angle;
|
||||
vec3 vector;
|
||||
} rotation;
|
||||
|
||||
const ShaderCustomParams *shader_params;
|
||||
|
||||
struct {
|
||||
unsigned int x : 1;
|
||||
unsigned int y : 1;
|
||||
} flip;
|
||||
SpriteScaleParams scale;
|
||||
SpriteRotationParams rotation;
|
||||
SpriteFlipParams flip;
|
||||
} SpriteParams;
|
||||
|
||||
// Matches vertex buffer layout
|
||||
typedef struct SpriteInstanceAttribs {
|
||||
mat4 mv_transform;
|
||||
mat4 tex_transform;
|
||||
|
||||
union {
|
||||
FloatRect texrect;
|
||||
vec4 texrect_vec4;
|
||||
};
|
||||
|
||||
Color rgba;
|
||||
FloatExtent sprite_size;
|
||||
ShaderCustomParams custom;
|
||||
|
||||
// offsetof(end_of_fields) == size without padding.
|
||||
char end_of_fields;
|
||||
} SpriteInstanceAttribs;
|
||||
|
||||
/*
|
||||
* Creates an SDL window with proper flags, and, if needed, sets up a rendering context associated with it.
|
||||
* Must be called before anything else.
|
||||
|
@ -709,6 +736,9 @@ void r_draw_quad_instanced(uint instances);
|
|||
void r_draw_model_ptr(Model *model, uint instances, uint base_instance) attr_nonnull(1);
|
||||
void r_draw_sprite(const SpriteParams *params) attr_nonnull(1);
|
||||
|
||||
void r_sprite_batch_prepare_state(const SpriteStateParams *stp);
|
||||
void r_sprite_batch_add_instance(const SpriteInstanceAttribs *attribs);
|
||||
|
||||
void r_flush_sprites(void);
|
||||
|
||||
BlendMode r_blend_compose(
|
||||
|
|
|
@ -22,19 +22,7 @@
|
|||
#endif
|
||||
#endif
|
||||
|
||||
typedef struct SpriteAttribs {
|
||||
mat4 transform;
|
||||
mat4 tex_transform;
|
||||
float rgba[4];
|
||||
FloatRect texrect;
|
||||
float sprite_size[2];
|
||||
float custom[4];
|
||||
|
||||
// offset of this == size without padding.
|
||||
char end_of_fields;
|
||||
} SpriteAttribs;
|
||||
|
||||
#define SIZEOF_SPRITE_ATTRIBS (offsetof(SpriteAttribs, end_of_fields))
|
||||
#define SIZEOF_SPRITE_ATTRIBS (offsetof(SpriteInstanceAttribs, end_of_fields))
|
||||
|
||||
static struct SpriteBatchState {
|
||||
// constants (set once on init and not expected to change)
|
||||
|
@ -75,7 +63,7 @@ void _r_sprite_batch_init(void) {
|
|||
size_t sz_attr = SIZEOF_SPRITE_ATTRIBS;
|
||||
|
||||
#define VERTEX_OFS(attr) offsetof(GenericModelVertex, attr)
|
||||
#define INSTANCE_OFS(attr) offsetof(SpriteAttribs, attr)
|
||||
#define INSTANCE_OFS(attr) offsetof(SpriteInstanceAttribs, attr)
|
||||
|
||||
VertexAttribFormat fmt[] = {
|
||||
// Per-vertex attributes (for the static models buffer, bound at 0)
|
||||
|
@ -84,10 +72,10 @@ void _r_sprite_batch_init(void) {
|
|||
{ { 2, VA_FLOAT, VA_CONVERT_FLOAT, 0 }, sz_vert, VERTEX_OFS(uv), 0 },
|
||||
|
||||
// Per-instance attributes (for our own sprites buffer, bound at 1)
|
||||
{ { 4, VA_FLOAT, VA_CONVERT_FLOAT, 1 }, sz_attr, INSTANCE_OFS(transform[0]), 1 },
|
||||
{ { 4, VA_FLOAT, VA_CONVERT_FLOAT, 1 }, sz_attr, INSTANCE_OFS(transform[1]), 1 },
|
||||
{ { 4, VA_FLOAT, VA_CONVERT_FLOAT, 1 }, sz_attr, INSTANCE_OFS(transform[2]), 1 },
|
||||
{ { 4, VA_FLOAT, VA_CONVERT_FLOAT, 1 }, sz_attr, INSTANCE_OFS(transform[3]), 1 },
|
||||
{ { 4, VA_FLOAT, VA_CONVERT_FLOAT, 1 }, sz_attr, INSTANCE_OFS(mv_transform[0]), 1 },
|
||||
{ { 4, VA_FLOAT, VA_CONVERT_FLOAT, 1 }, sz_attr, INSTANCE_OFS(mv_transform[1]), 1 },
|
||||
{ { 4, VA_FLOAT, VA_CONVERT_FLOAT, 1 }, sz_attr, INSTANCE_OFS(mv_transform[2]), 1 },
|
||||
{ { 4, VA_FLOAT, VA_CONVERT_FLOAT, 1 }, sz_attr, INSTANCE_OFS(mv_transform[3]), 1 },
|
||||
{ { 4, VA_FLOAT, VA_CONVERT_FLOAT, 1 }, sz_attr, INSTANCE_OFS(tex_transform[0]), 1 },
|
||||
{ { 4, VA_FLOAT, VA_CONVERT_FLOAT, 1 }, sz_attr, INSTANCE_OFS(tex_transform[1]), 1 },
|
||||
{ { 4, VA_FLOAT, VA_CONVERT_FLOAT, 1 }, sz_attr, INSTANCE_OFS(tex_transform[2]), 1 },
|
||||
|
@ -140,7 +128,7 @@ void r_flush_sprites(void) {
|
|||
|
||||
uint pending = _r_sprite_batch.num_pending;
|
||||
|
||||
// needs to be done early to thwart recursive callss
|
||||
// needs to be done early to thwart recursive calls
|
||||
_r_sprite_batch.num_pending = 0;
|
||||
|
||||
#if SPRITE_BATCH_STATS
|
||||
|
@ -201,29 +189,33 @@ void r_flush_sprites(void) {
|
|||
r_state_pop();
|
||||
}
|
||||
|
||||
static void _r_sprite_batch_add(Sprite *spr, const SpriteParams *params, SDL_RWops *stream) {
|
||||
SpriteAttribs attribs;
|
||||
r_mat_current(MM_MODELVIEW, attribs.transform);
|
||||
static void _r_sprite_batch_compute_attribs(
|
||||
const Sprite *restrict spr,
|
||||
const SpriteParams *restrict params,
|
||||
SpriteInstanceAttribs *out_attribs
|
||||
) {
|
||||
SpriteInstanceAttribs attribs;
|
||||
r_mat_current(MM_MODELVIEW, attribs.mv_transform);
|
||||
r_mat_current(MM_TEXTURE, attribs.tex_transform);
|
||||
|
||||
float scale_x = params->scale.x ? params->scale.x : 1;
|
||||
float scale_y = params->scale.y ? params->scale.y : scale_x;
|
||||
|
||||
if(params->pos.x || params->pos.y) {
|
||||
glm_translate(attribs.transform, (vec3) { params->pos.x, params->pos.y });
|
||||
glm_translate(attribs.mv_transform, (vec3) { params->pos.x, params->pos.y });
|
||||
}
|
||||
|
||||
if(params->rotation.angle) {
|
||||
float *rvec = (float*)params->rotation.vector;
|
||||
|
||||
if(rvec[0] == 0 && rvec[1] == 0 && rvec[2] == 0) {
|
||||
glm_rotate(attribs.transform, params->rotation.angle, (vec3) { 0, 0, 1 });
|
||||
glm_rotate(attribs.mv_transform, params->rotation.angle, (vec3) { 0, 0, 1 });
|
||||
} else {
|
||||
glm_rotate(attribs.transform, params->rotation.angle, rvec);
|
||||
glm_rotate(attribs.mv_transform, params->rotation.angle, rvec);
|
||||
}
|
||||
}
|
||||
|
||||
glm_scale(attribs.transform, (vec3) { scale_x * spr->w, scale_y * spr->h, 1 });
|
||||
glm_scale(attribs.mv_transform, (vec3) { scale_x * spr->w, scale_y * spr->h, 1 });
|
||||
|
||||
float ofs_x = sprite_padded_offset_x(spr);
|
||||
float ofs_y = sprite_padded_offset_y(spr);
|
||||
|
@ -237,23 +229,26 @@ static void _r_sprite_batch_add(Sprite *spr, const SpriteParams *params, SDL_RWo
|
|||
ofs_y *= -1;
|
||||
}
|
||||
|
||||
glm_translate(attribs.transform, (vec3) { ofs_x / spr->w, ofs_y / spr->h });
|
||||
glm_translate(attribs.mv_transform, (vec3) { ofs_x / spr->w, ofs_y / spr->h });
|
||||
}
|
||||
|
||||
if(params->color == NULL) {
|
||||
// XXX: should we use r_color_current here?
|
||||
attribs.rgba[0] = attribs.rgba[1] = attribs.rgba[2] = attribs.rgba[3] = 1;
|
||||
attribs.rgba = *RGBA(1, 1, 1, 1);
|
||||
} else {
|
||||
memcpy(attribs.rgba, params->color, sizeof(attribs.rgba));
|
||||
attribs.rgba = *params->color;
|
||||
}
|
||||
|
||||
uint tw, th;
|
||||
r_texture_get_size(spr->tex, 0, &tw, &th);
|
||||
float rtw = 1.0f / tw;
|
||||
float rth = 1.0f / th;
|
||||
|
||||
attribs.texrect.x = spr->tex_area.x / tw;
|
||||
attribs.texrect.y = spr->tex_area.y / th;
|
||||
attribs.texrect.w = spr->tex_area.w / tw;
|
||||
attribs.texrect.h = spr->tex_area.h / th;
|
||||
glm_vec4_mul(
|
||||
(vec4) { spr->tex_area.x, spr->tex_area.y, spr->tex_area.w, spr->tex_area.h },
|
||||
(vec4) { rtw, rth, rtw, rth },
|
||||
attribs.texrect_vec4
|
||||
);
|
||||
|
||||
if(params->flip.x) {
|
||||
attribs.texrect.x += attribs.texrect.w;
|
||||
|
@ -265,40 +260,55 @@ static void _r_sprite_batch_add(Sprite *spr, const SpriteParams *params, SDL_RWo
|
|||
attribs.texrect.h *= -1;
|
||||
}
|
||||
|
||||
attribs.sprite_size[0] = spr->w;
|
||||
attribs.sprite_size[1] = spr->h;
|
||||
attribs.sprite_size = spr->extent;
|
||||
|
||||
if(params->shader_params != NULL) {
|
||||
memcpy(attribs.custom, params->shader_params, sizeof(attribs.custom));
|
||||
if(params->shader_params == NULL) {
|
||||
memset(&attribs.custom, 0, sizeof(attribs.custom));
|
||||
} else {
|
||||
memset(attribs.custom, 0, sizeof(attribs.custom));
|
||||
attribs.custom = *params->shader_params;
|
||||
}
|
||||
|
||||
SDL_RWwrite(stream, &attribs, SIZEOF_SPRITE_ATTRIBS, 1);
|
||||
|
||||
#if SPRITE_BATCH_STATS
|
||||
_r_sprite_batch.frame_stats.sprites++;
|
||||
#endif
|
||||
*out_attribs = attribs;
|
||||
}
|
||||
|
||||
void r_draw_sprite(const SpriteParams *params) {
|
||||
assert(!(params->shader && params->shader_ptr));
|
||||
assert(!(params->sprite && params->sprite_ptr));
|
||||
INLINE void _r_sprite_batch_process_params(
|
||||
const SpriteParams *restrict sprite_params,
|
||||
SpriteStateParams *restrict state_params,
|
||||
Sprite *restrict *sprite
|
||||
) {
|
||||
assert(!(sprite_params->shader && sprite_params->shader_ptr));
|
||||
assert(!(sprite_params->sprite && sprite_params->sprite_ptr));
|
||||
|
||||
Sprite *spr = params->sprite_ptr;
|
||||
|
||||
if(spr == NULL) {
|
||||
assert(params->sprite != NULL);
|
||||
spr = get_sprite(params->sprite);
|
||||
if((*sprite = sprite_params->sprite_ptr) == NULL) {
|
||||
assert(sprite_params->sprite != NULL);
|
||||
*sprite = get_sprite(sprite_params->sprite);
|
||||
}
|
||||
|
||||
if(spr->tex != _r_sprite_batch.primary_texture) {
|
||||
state_params->primary_texture = (*sprite)->tex;
|
||||
memcpy(&state_params->aux_textures, &sprite_params->aux_textures, sizeof(sprite_params->aux_textures));
|
||||
state_params->blend = sprite_params->blend;
|
||||
|
||||
if(state_params->blend == 0) {
|
||||
state_params->blend = r_blend_current();
|
||||
}
|
||||
|
||||
if((state_params->shader = sprite_params->shader_ptr) == NULL) {
|
||||
if(sprite_params->shader != NULL) {
|
||||
state_params->shader = r_shader_get(sprite_params->shader);
|
||||
} else {
|
||||
state_params->shader = r_shader_current();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void r_sprite_batch_prepare_state(const SpriteStateParams *stp) {
|
||||
if(stp->primary_texture != _r_sprite_batch.primary_texture) {
|
||||
r_flush_sprites();
|
||||
_r_sprite_batch.primary_texture = spr->tex;
|
||||
_r_sprite_batch.primary_texture = stp->primary_texture;
|
||||
}
|
||||
|
||||
for(uint i = 0; i < R_NUM_SPRITE_AUX_TEXTURES; ++i) {
|
||||
Texture *aux_tex = params->aux_textures[i];
|
||||
Texture *aux_tex = stp->aux_textures[i];
|
||||
|
||||
if(aux_tex != NULL && aux_tex != _r_sprite_batch.aux_textures[i]) {
|
||||
r_flush_sprites();
|
||||
|
@ -306,21 +316,16 @@ void r_draw_sprite(const SpriteParams *params) {
|
|||
}
|
||||
}
|
||||
|
||||
ShaderProgram *prog = params->shader_ptr;
|
||||
|
||||
if(prog == NULL) {
|
||||
if(params->shader == NULL) {
|
||||
prog = r_shader_current();
|
||||
} else {
|
||||
prog = r_shader_get(params->shader);
|
||||
}
|
||||
if(stp->shader != _r_sprite_batch.shader) {
|
||||
r_flush_sprites();
|
||||
_r_sprite_batch.shader = stp->shader;
|
||||
}
|
||||
|
||||
assert(prog != NULL);
|
||||
BlendMode blend = stp->blend;
|
||||
|
||||
if(prog != _r_sprite_batch.shader) {
|
||||
if(blend != _r_sprite_batch.blend) {
|
||||
r_flush_sprites();
|
||||
_r_sprite_batch.shader = prog;
|
||||
_r_sprite_batch.blend = blend;
|
||||
}
|
||||
|
||||
Framebuffer *fb = r_framebuffer_current();
|
||||
|
@ -330,17 +335,6 @@ void r_draw_sprite(const SpriteParams *params) {
|
|||
_r_sprite_batch.framebuffer = fb;
|
||||
}
|
||||
|
||||
BlendMode blend = params->blend;
|
||||
|
||||
if(blend == 0) {
|
||||
blend = r_blend_current();
|
||||
}
|
||||
|
||||
if(blend != _r_sprite_batch.blend) {
|
||||
r_flush_sprites();
|
||||
_r_sprite_batch.blend = blend;
|
||||
}
|
||||
|
||||
r_capability_bits_t caps = r_capabilities_current();
|
||||
DepthTestFunc depth_func = r_depth_func_current();
|
||||
CullFaceMode cull_mode = r_cull_current();
|
||||
|
@ -366,7 +360,9 @@ void r_draw_sprite(const SpriteParams *params) {
|
|||
r_flush_sprites();
|
||||
glm_mat4_copy(*current_projection, _r_sprite_batch.projection);
|
||||
}
|
||||
}
|
||||
|
||||
static SDL_RWops *_r_sprite_batch_prepare_buffer(void) {
|
||||
SDL_RWops *stream = r_vertex_buffer_get_stream(_r_sprite_batch.vbuf);
|
||||
size_t remaining = SDL_RWsize(stream) - SDL_RWtell(stream);
|
||||
|
||||
|
@ -380,8 +376,29 @@ void r_draw_sprite(const SpriteParams *params) {
|
|||
r_flush_sprites();
|
||||
}
|
||||
|
||||
return stream;
|
||||
}
|
||||
|
||||
void r_sprite_batch_add_instance(const SpriteInstanceAttribs *attribs) {
|
||||
SDL_RWops *stream = _r_sprite_batch_prepare_buffer();
|
||||
SDL_RWwrite(stream, attribs, SIZEOF_SPRITE_ATTRIBS, 1);
|
||||
|
||||
_r_sprite_batch.num_pending++;
|
||||
_r_sprite_batch_add(spr, params, stream);
|
||||
|
||||
#if SPRITE_BATCH_STATS
|
||||
_r_sprite_batch.frame_stats.sprites++;
|
||||
#endif
|
||||
}
|
||||
|
||||
void r_draw_sprite(const SpriteParams *params) {
|
||||
SpriteStateParams state_params;
|
||||
SpriteInstanceAttribs attribs;
|
||||
Sprite *spr;
|
||||
|
||||
_r_sprite_batch_process_params(params, &state_params, &spr);
|
||||
r_sprite_batch_prepare_state(&state_params);
|
||||
_r_sprite_batch_compute_attribs(spr, params, &attribs);
|
||||
r_sprite_batch_add_instance(&attribs);
|
||||
}
|
||||
|
||||
#if SPRITE_BATCH_STATS
|
||||
|
|
|
@ -13,13 +13,14 @@
|
|||
#include FT_STROKER_H
|
||||
|
||||
#include "font.h"
|
||||
#include "util.h"
|
||||
#include "util/rectpack.h"
|
||||
#include "util/graphics.h"
|
||||
#include "config.h"
|
||||
#include "video.h"
|
||||
#include "events.h"
|
||||
#include "renderer/api.h"
|
||||
#include "util.h"
|
||||
#include "util/glm.h"
|
||||
#include "util/graphics.h"
|
||||
#include "util/rectpack.h"
|
||||
#include "video.h"
|
||||
|
||||
static void init_fonts(void);
|
||||
static void post_init_fonts(void);
|
||||
|
@ -963,10 +964,8 @@ ShaderProgram* text_get_default_shader(void) {
|
|||
return globals.default_shader;
|
||||
}
|
||||
|
||||
// #define TEXT_DRAW_BBOX
|
||||
|
||||
attr_returns_nonnull
|
||||
static Font* font_from_params(const TextParams *params) {
|
||||
static Font *font_from_params(const TextParams *params) {
|
||||
Font *font = params->font_ptr;
|
||||
|
||||
if(font == NULL) {
|
||||
|
@ -981,9 +980,38 @@ static Font* font_from_params(const TextParams *params) {
|
|||
return font;
|
||||
}
|
||||
|
||||
static void set_batch_texture(SpriteStateParams *stp, Texture *tex, float *inverse_tex_w, float *inverse_tex_h) {
|
||||
if(stp->primary_texture != tex) {
|
||||
stp->primary_texture = tex;
|
||||
r_sprite_batch_prepare_state(stp);
|
||||
|
||||
uint tw, th;
|
||||
r_texture_get_size(tex, 0, &tw, &th);
|
||||
*inverse_tex_w = 1.0f / tw;
|
||||
*inverse_tex_h = 1.0f / th;
|
||||
}
|
||||
}
|
||||
|
||||
attr_nonnull(1, 2, 3)
|
||||
static double _text_ucs4_draw(Font *font, const uint32_t *ucs4text, const TextParams *params) {
|
||||
SpriteParams sp = { .sprite = NULL };
|
||||
SpriteStateParams batch_state_params;
|
||||
|
||||
memcpy(batch_state_params.aux_textures, params->aux_textures, sizeof(batch_state_params.aux_textures));
|
||||
|
||||
if((batch_state_params.blend = params->blend) == 0) {
|
||||
batch_state_params.blend = r_blend_current();
|
||||
}
|
||||
|
||||
if((batch_state_params.shader = params->shader_ptr) == NULL) {
|
||||
if(params->shader != NULL) {
|
||||
batch_state_params.shader = r_shader_get(params->shader);
|
||||
} else {
|
||||
batch_state_params.shader = r_shader_current();
|
||||
}
|
||||
}
|
||||
|
||||
batch_state_params.primary_texture = NULL;
|
||||
|
||||
BBox bbox;
|
||||
double x = params->pos.x;
|
||||
double y = params->pos.y;
|
||||
|
@ -997,33 +1025,31 @@ static double _text_ucs4_draw(Font *font, const uint32_t *ucs4text, const TextPa
|
|||
|
||||
text_ucs4_bbox(font, ucs4text, 0, &bbox);
|
||||
|
||||
sp.shader_ptr = params->shader_ptr;
|
||||
Color color;
|
||||
|
||||
if(sp.shader_ptr == NULL) {
|
||||
// don't defer this to r_draw_sprite; we don't want to call r_shader_get more than necessary.
|
||||
|
||||
if(params->shader != NULL) {
|
||||
sp.shader_ptr = r_shader_get(params->shader);
|
||||
} else {
|
||||
sp.shader_ptr = r_shader_current();
|
||||
}
|
||||
}
|
||||
|
||||
sp.color = params->color;
|
||||
sp.blend = params->blend;
|
||||
sp.shader_params = params->shader_params;
|
||||
memcpy(sp.aux_textures, params->aux_textures, sizeof(sp.aux_textures));
|
||||
|
||||
if(sp.color == NULL) {
|
||||
if(params->color == NULL) {
|
||||
// XXX: sprite batch code defaults this to RGB(1, 1, 1)
|
||||
sp.color = r_color_current();
|
||||
color = *r_color_current();
|
||||
} else {
|
||||
color = *params->color;
|
||||
}
|
||||
|
||||
MatrixMode mm_prev = r_mat_mode_current();
|
||||
r_mat_mode(MM_MODELVIEW);
|
||||
r_mat_push();
|
||||
r_mat_translate(x, y, 0);
|
||||
r_mat_scale(iscale, iscale, 1);
|
||||
ShaderCustomParams shader_params;
|
||||
|
||||
if(params->shader_params == NULL) {
|
||||
memset(&shader_params, 0, sizeof(shader_params));
|
||||
} else {
|
||||
shader_params = *params->shader_params;
|
||||
}
|
||||
|
||||
mat4 mat_texture;
|
||||
mat4 mat_model;
|
||||
|
||||
r_mat_current(MM_TEXTURE, mat_texture);
|
||||
r_mat_current(MM_MODELVIEW, mat_model);
|
||||
|
||||
glm_translate(mat_model, (vec3) { x, y } );
|
||||
glm_scale(mat_model, (vec3) { iscale, iscale, 1 } );
|
||||
|
||||
double orig_x = x;
|
||||
double orig_y = y;
|
||||
|
@ -1047,34 +1073,8 @@ static double _text_ucs4_draw(Font *font, const uint32_t *ucs4text, const TextPa
|
|||
overlay.w = overlay.x.max - overlay.x.min;
|
||||
overlay.h = overlay.y.max - overlay.y.min;
|
||||
|
||||
#ifdef TEXT_DRAW_BBOX
|
||||
double bbox_w = bbox.x.max - bbox.x.min;
|
||||
double bbox_h = bbox.y.max - bbox.y.min;
|
||||
double bbox_x_mid = x + bbox.x.min + bbox_w * 0.5;
|
||||
double bbox_y_mid = y + bbox.y.min + bbox_h * 0.5 - font->metrics.descent;
|
||||
|
||||
#if 0 /* enable to visualize the overlay projection instead */
|
||||
bbox_w = overlay.w;
|
||||
bbox_h = overlay.h;
|
||||
bbox_x_mid = overlay.x.min + overlay.w * 0.5;
|
||||
bbox_y_mid = overlay.y.min + overlay.h * 0.5;
|
||||
#endif
|
||||
|
||||
r_state_push();
|
||||
r_shader_standard_notex();
|
||||
r_mat_push();
|
||||
r_mat_translate(bbox_x_mid, bbox_y_mid, 0);
|
||||
r_mat_scale(bbox_w, bbox_h, 0);
|
||||
r_color(color_mul(RGBA(0.5, 0.5, 0.5, 0.5), sp.color));
|
||||
r_draw_quad();
|
||||
r_mat_pop();
|
||||
r_state_pop();
|
||||
#endif
|
||||
|
||||
r_mat_mode(MM_TEXTURE);
|
||||
r_mat_push();
|
||||
r_mat_scale(1/overlay.w, 1/overlay.h, 1.0);
|
||||
r_mat_translate(-overlay.x.min, overlay.y.min, 0);
|
||||
glm_scale(mat_texture, (vec3) { 1/overlay.w, 1/overlay.h, 1.0 });
|
||||
glm_translate(mat_texture, (vec3) { -overlay.x.min, overlay.y.min, 0 });
|
||||
|
||||
// FIXME: is there a better way?
|
||||
float texmat_offset_sign;
|
||||
|
@ -1088,6 +1088,8 @@ static double _text_ucs4_draw(Font *font, const uint32_t *ucs4text, const TextPa
|
|||
uint prev_glyph_idx = 0;
|
||||
const uint32_t *tptr = ucs4text;
|
||||
|
||||
float inverse_tex_w, inverse_tex_h;
|
||||
|
||||
while(*tptr) {
|
||||
uint32_t uchar = *tptr++;
|
||||
|
||||
|
@ -1105,47 +1107,44 @@ static double _text_ucs4_draw(Font *font, const uint32_t *ucs4text, const TextPa
|
|||
|
||||
x += apply_kerning(font, prev_glyph_idx, glyph);
|
||||
|
||||
sp.sprite_ptr = &glyph->sprite;
|
||||
|
||||
if(glyph->sprite.tex != NULL) {
|
||||
sp.pos.x = x + glyph->metrics.bearing_x + sp.sprite_ptr->w * 0.5;
|
||||
sp.pos.y = y - glyph->metrics.bearing_y + sp.sprite_ptr->h * 0.5 - font->metrics.descent;
|
||||
Sprite *spr = &glyph->sprite;
|
||||
set_batch_texture(&batch_state_params, spr->tex, &inverse_tex_w, &inverse_tex_h);
|
||||
|
||||
// HACK/FIXME: Glyphs have their sprite w/h unadjusted for scale.
|
||||
// We have to temporarily fix that up here so that the shader gets resolution-independent dimensions.
|
||||
float w_saved = sp.sprite_ptr->w;
|
||||
float h_saved = sp.sprite_ptr->h;
|
||||
sp.sprite_ptr->w /= font->metrics.scale;
|
||||
sp.sprite_ptr->h /= font->metrics.scale;
|
||||
sp.scale.both = font->metrics.scale;
|
||||
SpriteInstanceAttribs attribs;
|
||||
attribs.rgba = color;
|
||||
attribs.custom = shader_params;
|
||||
|
||||
r_mat_push();
|
||||
r_mat_translate(sp.pos.x, sp.pos.y * texmat_offset_sign + overlay.h, 0);
|
||||
r_mat_scale(w_saved, h_saved, 1.0);
|
||||
r_mat_translate(-0.5, -0.5, 0);
|
||||
float g_x = x + glyph->metrics.bearing_x + spr->w * 0.5;
|
||||
float g_y = y - glyph->metrics.bearing_y + spr->h * 0.5 - font->metrics.descent;
|
||||
|
||||
glm_translate_to(mat_texture, (vec3) { g_x - spr->w * 0.5, g_y * texmat_offset_sign + overlay.h - spr->h * 0.5 }, attribs.tex_transform );
|
||||
glm_scale(attribs.tex_transform, (vec3) { spr->w, spr->h, 1.0 });
|
||||
|
||||
glm_translate_to(mat_model, (vec3) { g_x, g_y }, attribs.mv_transform);
|
||||
glm_scale(attribs.mv_transform, (vec3) { spr->w, spr->h, 1.0 } );
|
||||
|
||||
glm_vec4_mul(
|
||||
(vec4) { spr->tex_area.x, spr->tex_area.y, spr->tex_area.w, spr->tex_area.h },
|
||||
(vec4) { inverse_tex_w, inverse_tex_h, inverse_tex_w, inverse_tex_h },
|
||||
attribs.texrect_vec4
|
||||
);
|
||||
|
||||
// NOTE: Glyphs have their sprite w/h unadjusted for scale.
|
||||
attribs.sprite_size.w = spr->w * iscale;
|
||||
attribs.sprite_size.h = spr->h * iscale;
|
||||
|
||||
if(params->glyph_callback.func != NULL) {
|
||||
x += params->glyph_callback.func(font, uchar, &sp, params->glyph_callback.userdata);
|
||||
params->glyph_callback.func(font, uchar, &attribs, params->glyph_callback.userdata);
|
||||
}
|
||||
|
||||
r_draw_sprite(&sp);
|
||||
|
||||
// HACK/FIXME: See above.
|
||||
sp.sprite_ptr->w = w_saved;
|
||||
sp.sprite_ptr->h = h_saved;
|
||||
|
||||
r_mat_pop();
|
||||
r_sprite_batch_add_instance(&attribs);
|
||||
}
|
||||
|
||||
x += glyph->metrics.advance;
|
||||
prev_glyph_idx = glyph->ft_index;
|
||||
}
|
||||
|
||||
r_mat_pop();
|
||||
r_mat_mode(MM_MODELVIEW);
|
||||
r_mat_pop();
|
||||
r_mat_mode(mm_prev);
|
||||
|
||||
return x * iscale;
|
||||
}
|
||||
|
||||
|
|
|
@ -54,7 +54,8 @@ typedef struct BBox {
|
|||
} y;
|
||||
} BBox;
|
||||
|
||||
typedef int (*GlyphDrawCallback)(Font *font, charcode_t charcode, SpriteParams *spr_params, void *userdata);
|
||||
// FIXME: this is a quite crude low-level-ish hack, and probably should be replaced with some kind of markup system.
|
||||
typedef int (*GlyphDrawCallback)(Font *font, charcode_t charcode, SpriteInstanceAttribs *spr_instance, void *userdata);
|
||||
|
||||
typedef struct TextParams {
|
||||
const char *font;
|
||||
|
|
|
@ -36,22 +36,22 @@ INLINE float sprite_padded_height(const Sprite *restrict spr) {
|
|||
return spr->extent.h + spr->padding.top + spr->padding.bottom;
|
||||
}
|
||||
|
||||
INLINE FloatExtent sprite_padded_extent(Sprite *restrict spr) {
|
||||
INLINE FloatExtent sprite_padded_extent(const Sprite *restrict spr) {
|
||||
FloatExtent e;
|
||||
e.w = sprite_padded_width(spr);
|
||||
e.h = sprite_padded_height(spr);
|
||||
return e;
|
||||
}
|
||||
|
||||
INLINE float sprite_padded_offset_x(Sprite *restrict spr) {
|
||||
INLINE float sprite_padded_offset_x(const Sprite *restrict spr) {
|
||||
return (spr->padding.left - spr->padding.right) * 0.5;
|
||||
}
|
||||
|
||||
INLINE float sprite_padded_offset_y(Sprite *restrict spr) {
|
||||
INLINE float sprite_padded_offset_y(const Sprite *restrict spr) {
|
||||
return (spr->padding.top - spr->padding.bottom) * 0.5;
|
||||
}
|
||||
|
||||
INLINE FloatOffset sprite_padded_offset(Sprite *restrict spr) {
|
||||
INLINE FloatOffset sprite_padded_offset(const Sprite *restrict spr) {
|
||||
FloatOffset o;
|
||||
o.x = sprite_padded_offset_x(spr);
|
||||
o.y = sprite_padded_offset_y(spr);
|
||||
|
|
|
@ -1068,14 +1068,14 @@ struct glyphcb_state {
|
|||
Color *color1, *color2;
|
||||
};
|
||||
|
||||
static int draw_numeric_callback(Font *font, charcode_t charcode, SpriteParams *spr_params, void *userdata) {
|
||||
static int draw_numeric_callback(Font *font, charcode_t charcode, SpriteInstanceAttribs *spr_attribs, void *userdata) {
|
||||
struct glyphcb_state *st = userdata;
|
||||
|
||||
if(charcode != '0' && charcode != ',') {
|
||||
st->color1 = st->color2;
|
||||
}
|
||||
|
||||
spr_params->color = st->color1;
|
||||
spr_attribs->rgba = *st->color1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1325,7 +1325,6 @@ static void stage_draw_hud_text(struct labels_s* labels) {
|
|||
font_set_kerning_enabled(font, kern_saved);
|
||||
}
|
||||
|
||||
|
||||
r_mat_push();
|
||||
r_mat_translate(HUD_X_SECONDARY_OFS_VALUE, 0, 0);
|
||||
|
||||
|
@ -1344,7 +1343,7 @@ static void stage_draw_hud_text(struct labels_s* labels) {
|
|||
.font_ptr = font,
|
||||
.glyph_callback = {
|
||||
draw_numeric_callback,
|
||||
&(struct glyphcb_state) { &stagedraw.hud_text.color.inactive },
|
||||
&(struct glyphcb_state) { &stagedraw.hud_text.color.inactive, &stagedraw.hud_text.color.active },
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -1391,7 +1390,7 @@ static void stage_draw_hud_text(struct labels_s* labels) {
|
|||
.font_ptr = font,
|
||||
.glyph_callback = {
|
||||
draw_numeric_callback,
|
||||
&(struct glyphcb_state) { &stagedraw.hud_text.color.inactive },
|
||||
&(struct glyphcb_state) { &stagedraw.hud_text.color.inactive, &stagedraw.hud_text.color.active },
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -1410,7 +1409,7 @@ static void stage_draw_hud_text(struct labels_s* labels) {
|
|||
.font_ptr = font,
|
||||
.glyph_callback = {
|
||||
draw_numeric_callback,
|
||||
&(struct glyphcb_state) { &stagedraw.hud_text.color.inactive },
|
||||
&(struct glyphcb_state) { &stagedraw.hud_text.color.inactive, &stagedraw.hud_text.color.active },
|
||||
}
|
||||
});
|
||||
|
||||
|
|
Loading…
Reference in a new issue