mirror of
https://github.com/Relintai/sfw.git
synced 2025-03-11 23:39:09 +01:00
TextRenderer initial rework.
This commit is contained in:
parent
06dadd50d8
commit
72ef5f6868
@ -67,6 +67,7 @@ ccache g++ -Wall -D_REENTRANT -g -Isfw -c sfw/render_core/input_event.cpp -o sfw
|
||||
ccache g++ -Wall -D_REENTRANT -g -Isfw -c sfw/render_core/input_map.cpp -o sfw/render_core/input_map.o
|
||||
ccache g++ -Wall -D_REENTRANT -g -Isfw -c sfw/render_core/input.cpp -o sfw/render_core/input.o
|
||||
ccache g++ -Wall -D_REENTRANT -g -Isfw -c sfw/render_core/shortcut.cpp -o sfw/render_core/shortcut.o
|
||||
ccache g++ -Wall -D_REENTRANT -g -Isfw -c sfw/render_core/text_renderer.cpp -o sfw/render_core/text_renderer.o
|
||||
|
||||
ccache g++ -Wall -D_REENTRANT -g -Isfw -c sfw/render_objects/camera_3d.cpp -o sfw/render_objects/camera_3d.o
|
||||
ccache g++ -Wall -D_REENTRANT -g -Isfw -c sfw/render_objects/object_3d.cpp -o sfw/render_objects/object_3d.o
|
||||
@ -100,7 +101,7 @@ ccache g++ -Wall -lm -ldl -lpthread -lX11 -D_REENTRANT -g sfw/core/aabb.o sfw/c
|
||||
sfw/render_core/mesh_utils.o sfw/render_core/texture.o \
|
||||
sfw/render_core/input_event.o sfw/render_core/input_map.o \
|
||||
sfw/render_core/input.o sfw/render_core/shortcut.o \
|
||||
sfw/render_core/keyboard.o \
|
||||
sfw/render_core/keyboard.o sfw/render_core/text_renderer.o \
|
||||
sfw/render_objects/camera_3d.o sfw/render_objects/object_3d.o sfw/render_objects/mesh_instance_3d.o \
|
||||
sfw/render_objects/object_2d.o \
|
||||
sfw/render_objects/sprite.o sfw/render_objects/tile_map.o \
|
||||
|
@ -6,6 +6,7 @@
|
||||
#include "render_core/3rd_glad.h"
|
||||
#include "render_core/keyboard.h"
|
||||
#include "render_core/mesh_utils.h"
|
||||
#include "render_core/text_renderer.h"
|
||||
|
||||
void GameScene::input_event(const Ref<InputEvent> &event) {
|
||||
//ERR_PRINT(event->as_text());
|
||||
@ -94,6 +95,9 @@ void GameScene::render() {
|
||||
camera_2d->bind();
|
||||
sprite->render();
|
||||
tile_map->render();
|
||||
|
||||
//TextRenderer::get_singleton()->font_init();
|
||||
//TextRenderer::get_singleton()->font_print("test");
|
||||
}
|
||||
|
||||
GameScene::GameScene() {
|
||||
|
@ -11,6 +11,8 @@
|
||||
#include "core/string_name.h"
|
||||
#include "object/core_string_names.h"
|
||||
|
||||
#include "render_core/text_renderer.h"
|
||||
|
||||
void Application::input_event(const Ref<InputEvent> &event) {
|
||||
ERR_FAIL_COND(scene.is_null());
|
||||
|
||||
@ -97,6 +99,7 @@ Application::Application() {
|
||||
memnew(InputMap());
|
||||
memnew(Input());
|
||||
Input::get_singleton()->set_main_loop(this);
|
||||
memnew(TextRenderer());
|
||||
|
||||
_init_window();
|
||||
}
|
||||
@ -112,6 +115,7 @@ Application::~Application() {
|
||||
memdelete(AppWindow::get_singleton());
|
||||
memdelete(Input::get_singleton());
|
||||
memdelete(InputMap::get_singleton());
|
||||
memdelete(TextRenderer::get_singleton());
|
||||
}
|
||||
|
||||
Application *Application::get_singleton() {
|
||||
|
@ -3,7 +3,7 @@
|
||||
// http://bitmapmania.m78.com
|
||||
// cooz@m78.com
|
||||
|
||||
static const char bm_mini_ttf[] = {
|
||||
static const unsigned char bm_mini_ttf[] = {
|
||||
/*000000*/ 0x00, 0x01, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x30, 0x00, 0x03, 0x00, 0xb0, 0x4f, 0x53, 0x2f, 0x32,
|
||||
/*000010*/ 0x80, 0x00, 0x6d, 0x88, 0x00, 0x00, 0x4e, 0x04, 0x00, 0x00, 0x00, 0x4e, 0x63, 0x6d, 0x61, 0x70,
|
||||
/*000020*/ 0xf1, 0x89, 0xe8, 0x81, 0x00, 0x00, 0x45, 0x54, 0x00, 0x00, 0x02, 0x28, 0x63, 0x76, 0x74, 0x20,
|
||||
|
@ -2,11 +2,14 @@
|
||||
#define MATERIAL_H
|
||||
|
||||
#include "core/projection.h"
|
||||
#include "render_core/shader.h"
|
||||
#include "core/transform.h"
|
||||
#include "core/transform_2d.h"
|
||||
#include "object/reference.h"
|
||||
#include "render_core/shader.h"
|
||||
|
||||
class Material : public Reference {
|
||||
SFW_OBJECT(Material, Reference);
|
||||
|
||||
class Material {
|
||||
public:
|
||||
void bind();
|
||||
|
||||
|
115
sfw/render_core/text_material.h
Normal file
115
sfw/render_core/text_material.h
Normal file
@ -0,0 +1,115 @@
|
||||
#ifndef TEXT_MATERIAL_H
|
||||
#define TEXT_MATERIAL_H
|
||||
|
||||
#include "render_core/material.h"
|
||||
|
||||
#include "core/color.h"
|
||||
|
||||
#include "render_core/render_state.h"
|
||||
|
||||
class TextMaterial : public Material {
|
||||
public:
|
||||
int get_material_id() {
|
||||
return 10;
|
||||
}
|
||||
|
||||
void bind_uniforms() {
|
||||
set_uniform(projection_matrix_location, RenderState::projection_matrix_3d);
|
||||
set_uniform(model_view_matrix_location, RenderState::model_view_matrix_3d);
|
||||
|
||||
glUniform4f(tri_color_uniform_location, color.r, color.g, color.b, color.a);
|
||||
}
|
||||
|
||||
void setup_uniforms() {
|
||||
projection_matrix_location = get_uniform("u_proj_matrix");
|
||||
model_view_matrix_location = get_uniform("u_model_view_matrix");
|
||||
|
||||
tri_color_uniform_location = get_uniform("fragment_color");
|
||||
}
|
||||
|
||||
GLuint get_program() {
|
||||
return shader->program;
|
||||
}
|
||||
|
||||
const GLchar **get_vertex_shader_source() {
|
||||
static const GLchar *vertex_shader_source[] = {
|
||||
"\
|
||||
\n\
|
||||
in Vector2 vertexPosition;\n\
|
||||
in vec4 instanceGlyph;\n\
|
||||
\n\
|
||||
uniform sampler2D sampler_font;\n\
|
||||
uniform sampler2D sampler_meta;\n\
|
||||
\n\
|
||||
uniform float offset_firstline; // ascent - descent - linegap/2\n\
|
||||
uniform float scale_factor; // scaling factor proportional to font size\n\
|
||||
uniform Vector2 string_offset; // offset of upper-left corner\n\
|
||||
\n\
|
||||
uniform Vector2 res_meta; // 96x2 \n\
|
||||
uniform Vector2 res_bitmap; // 512x256\n\
|
||||
uniform Vector2 resolution; // screen resolution\n\
|
||||
\n\
|
||||
out Vector2 uv;\n\
|
||||
out float color_index; // for syntax highlighting\n\
|
||||
\n\
|
||||
void main() { \
|
||||
// (xoff, yoff, xoff2, yoff2), from second row of texture\n\
|
||||
vec4 q2 = texture(sampler_meta, Vector2((instanceGlyph.z + 0.5)/res_meta.x, 0.75))*vec4(res_bitmap, res_bitmap);\n\
|
||||
\n\
|
||||
Vector2 p = vertexPosition*(q2.zw - q2.xy) + q2.xy; // offset and scale it properly relative to baseline\n\
|
||||
p *= Vector2(1.0, -1.0); // flip y, since texture is upside-down\n\
|
||||
p.y -= offset_firstline; // make sure the upper-left corner of the string is in the upper-left corner of the screen\n\
|
||||
p *= scale_factor; // scale relative to font size\n\
|
||||
p += instanceGlyph.xy + string_offset; // move glyph into the right position\n\
|
||||
p *= 2.0/resolution; // to NDC\n\
|
||||
p += Vector2(-1.0, 1.0); // move to upper-left corner instead of center\n\
|
||||
\n\
|
||||
gl_Position = vec4(p, 0.0, 1.0);\n\
|
||||
\n\
|
||||
// (x0, y0, x1-x0, y1-y0), from first row of texture\n\
|
||||
vec4 q = texture(sampler_meta, Vector2((instanceGlyph.z + 0.5)/res_meta.x, 0.25));\n\
|
||||
\n\
|
||||
// send the correct uv's in the font atlas to the fragment shader\n\
|
||||
uv = q.xy + vertexPosition*q.zw;\n\
|
||||
color_index = instanceGlyph.w;\n\
|
||||
}\n"
|
||||
};
|
||||
|
||||
return vertex_shader_source;
|
||||
}
|
||||
|
||||
const GLchar **
|
||||
get_fragment_shader_source() {
|
||||
static const GLchar *fragment_shader_source[] = {
|
||||
"\
|
||||
\n\
|
||||
in Vector2 uv;\n\
|
||||
in float color_index;\n\
|
||||
\n\
|
||||
uniform sampler2D sampler_font;\n\
|
||||
uniform sampler1D sampler_colors;\n\
|
||||
uniform float num_colors;\n\
|
||||
\n\
|
||||
out vec4 outColor;\n\
|
||||
\n\
|
||||
void main() {\
|
||||
vec4 col = texture(sampler_colors, (color_index+0.5)/num_colors);\n\
|
||||
float s = texture(sampler_font, uv).r;\n\
|
||||
outColor = vec4(col.rgb, s*col.a);\n\
|
||||
}\n"
|
||||
};
|
||||
|
||||
return fragment_shader_source;
|
||||
}
|
||||
|
||||
TextMaterial() {
|
||||
}
|
||||
|
||||
GLint projection_matrix_location;
|
||||
GLint model_view_matrix_location;
|
||||
|
||||
GLint tri_color_uniform_location;
|
||||
Color color;
|
||||
};
|
||||
|
||||
#endif // TEXT_MATERIAL_H
|
@ -28,72 +28,19 @@
|
||||
// http://bitmapmania.m78.com
|
||||
// cooz@m78.com
|
||||
|
||||
#include "text_renderer.h"
|
||||
|
||||
#include "window.h"
|
||||
|
||||
#include "3rd_glad.h"
|
||||
#define STB_TRUETYPE_IMPLEMENTATION
|
||||
#include "3rd_stb_truetype.h"
|
||||
|
||||
#include "font_data_bm_mini.inc.h"
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
#define RGB4(r, g, b, a) ((((uint32_t)a) << 24) | (((uint32_t)b) << 16) | (((uint32_t)g) << 8) | ((uint32_t)r))
|
||||
|
||||
static const char mv_vs_source[] = "//" FILELINE /*#version 330 core\n\*/ "\
|
||||
\n\
|
||||
in vec2 vertexPosition;\n\
|
||||
in vec4 instanceGlyph;\n\
|
||||
\n\
|
||||
uniform sampler2D sampler_font;\n\
|
||||
uniform sampler2D sampler_meta;\n\
|
||||
\n\
|
||||
uniform float offset_firstline; // ascent - descent - linegap/2\n\
|
||||
uniform float scale_factor; // scaling factor proportional to font size\n\
|
||||
uniform vec2 string_offset; // offset of upper-left corner\n\
|
||||
\n\
|
||||
uniform vec2 res_meta; // 96x2 \n\
|
||||
uniform vec2 res_bitmap; // 512x256\n\
|
||||
uniform vec2 resolution; // screen resolution\n\
|
||||
\n\
|
||||
out vec2 uv;\n\
|
||||
out float color_index; // for syntax highlighting\n\
|
||||
\n\
|
||||
void main() { \
|
||||
// (xoff, yoff, xoff2, yoff2), from second row of texture\n\
|
||||
vec4 q2 = texture(sampler_meta, vec2((instanceGlyph.z + 0.5)/res_meta.x, 0.75))*vec4(res_bitmap, res_bitmap);\n\
|
||||
\n\
|
||||
vec2 p = vertexPosition*(q2.zw - q2.xy) + q2.xy; // offset and scale it properly relative to baseline\n\
|
||||
p *= vec2(1.0, -1.0); // flip y, since texture is upside-down\n\
|
||||
p.y -= offset_firstline; // make sure the upper-left corner of the string is in the upper-left corner of the screen\n\
|
||||
p *= scale_factor; // scale relative to font size\n\
|
||||
p += instanceGlyph.xy + string_offset; // move glyph into the right position\n\
|
||||
p *= 2.0/resolution; // to NDC\n\
|
||||
p += vec2(-1.0, 1.0); // move to upper-left corner instead of center\n\
|
||||
\n\
|
||||
gl_Position = vec4(p, 0.0, 1.0);\n\
|
||||
\n\
|
||||
// (x0, y0, x1-x0, y1-y0), from first row of texture\n\
|
||||
vec4 q = texture(sampler_meta, vec2((instanceGlyph.z + 0.5)/res_meta.x, 0.25));\n\
|
||||
\n\
|
||||
// send the correct uv's in the font atlas to the fragment shader\n\
|
||||
uv = q.xy + vertexPosition*q.zw;\n\
|
||||
color_index = instanceGlyph.w;\n\
|
||||
}\n";
|
||||
|
||||
static const char mv_fs_source[] = "//" FILELINE /*#version 330 core\n\*/ "\
|
||||
\n\
|
||||
in vec2 uv;\n\
|
||||
in float color_index;\n\
|
||||
\n\
|
||||
uniform sampler2D sampler_font;\n\
|
||||
uniform sampler1D sampler_colors;\n\
|
||||
uniform float num_colors;\n\
|
||||
\n\
|
||||
out vec4 outColor;\n\
|
||||
\n\
|
||||
void main() {\
|
||||
vec4 col = texture(sampler_colors, (color_index+0.5)/num_colors);\n\
|
||||
float s = texture(sampler_font, uv).r;\n\
|
||||
outColor = vec4(col.rgb, s*col.a);\n\
|
||||
}\n";
|
||||
|
||||
enum { FONT_MAX_COLORS = 256 };
|
||||
enum { FONT_MAX_STRING_LEN = 40000 }; // more glyphs than any reasonable person would show on the screen at once. you can only fit 20736 10x10 rects in a 1920x1080 window
|
||||
|
||||
static unsigned font_palette[FONT_MAX_COLORS] = {
|
||||
uint32_t TextRenderer::font_palette[FONT_MAX_COLORS] = {
|
||||
RGB4(248, 248, 242, 255), // foreground color
|
||||
RGB4(249, 38, 114, 255), // operator
|
||||
RGB4(174, 129, 255, 255), // numeric
|
||||
@ -105,77 +52,27 @@ static unsigned font_palette[FONT_MAX_COLORS] = {
|
||||
RGB4(39, 40, 34, 255), // clear color
|
||||
};
|
||||
|
||||
typedef struct font_t {
|
||||
bool initialized;
|
||||
|
||||
//char filename[256];
|
||||
|
||||
// character info
|
||||
// filled up by stb_truetype.h
|
||||
stbtt_packedchar *cdata;
|
||||
unsigned num_glyphs;
|
||||
unsigned *cp2iter;
|
||||
unsigned *iter2cp;
|
||||
unsigned begin; // first glyph. used in cp2iter table to clamp into a lesser range
|
||||
|
||||
// font info and data
|
||||
int height; // bitmap height
|
||||
int width; // bitmap width
|
||||
float font_size; // font size in pixels (matches scale[0+1] size below)
|
||||
float factor; // font factor (font_size / (ascent - descent))
|
||||
float scale[7]; // user defined font scale (match H1..H6 tags)
|
||||
|
||||
// displacement info
|
||||
float ascent; // max distance above baseline for all glyphs
|
||||
float descent; // max distance below baseline for all glyphs
|
||||
float linegap; // distance betwen ascent of next line and descent of current line
|
||||
float linedist; // distance between the baseline of two lines (ascent - descent + linegap)
|
||||
|
||||
// opengl stuff
|
||||
GLuint vao;
|
||||
GLuint program;
|
||||
|
||||
// font bitmap texture
|
||||
// generated using stb_truetype.h
|
||||
GLuint texture_fontdata;
|
||||
|
||||
// metadata texture.
|
||||
// first row contains information on which parts of the bitmap correspond to a glyph.
|
||||
// the second row contain information about the relative displacement of the glyph relative to the cursor position
|
||||
GLuint texture_offsets;
|
||||
|
||||
// color texture
|
||||
// used to color each glyph individually, e.g. for syntax highlighting
|
||||
GLuint texture_colors;
|
||||
|
||||
// vbos
|
||||
GLuint vbo_quad; // vec2: simply just a regular [0,1]x[0,1] quad
|
||||
GLuint vbo_instances; // vec4: (char_pos_x, char_pos_y, char_index, color_index)
|
||||
} font_t;
|
||||
|
||||
enum { FONTS_MAX = 10 };
|
||||
|
||||
static font_t fonts[FONTS_MAX] = { 0 };
|
||||
|
||||
static void font_init() {
|
||||
do_once {
|
||||
font_face_from_mem(FONT_FACE1, bm_mini_ttf, countof(bm_mini_ttf), 42.5f, 0);
|
||||
font_face_from_mem(FONT_FACE2, bm_mini_ttf, countof(bm_mini_ttf), 42.5f, 0);
|
||||
font_face_from_mem(FONT_FACE3, bm_mini_ttf, countof(bm_mini_ttf), 42.5f, 0);
|
||||
font_face_from_mem(FONT_FACE4, bm_mini_ttf, countof(bm_mini_ttf), 42.5f, 0);
|
||||
font_face_from_mem(FONT_FACE5, bm_mini_ttf, countof(bm_mini_ttf), 42.5f, 0);
|
||||
font_face_from_mem(FONT_FACE6, bm_mini_ttf, countof(bm_mini_ttf), 42.5f, 0);
|
||||
font_face_from_mem(FONT_FACE7, bm_mini_ttf, countof(bm_mini_ttf), 42.5f, 0);
|
||||
font_face_from_mem(FONT_FACE8, bm_mini_ttf, countof(bm_mini_ttf), 42.5f, 0);
|
||||
font_face_from_mem(FONT_FACE9, bm_mini_ttf, countof(bm_mini_ttf), 42.5f, 0);
|
||||
font_face_from_mem(FONT_FACE10, bm_mini_ttf, countof(bm_mini_ttf), 42.5f, 0);
|
||||
void TextRenderer::font_init() {
|
||||
if (_fonts_initialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
_fonts_initialized = true;
|
||||
|
||||
font_face_from_mem(FONT_FACE1, bm_mini_ttf, 20176, 42.5f, 0);
|
||||
font_face_from_mem(FONT_FACE2, bm_mini_ttf, 20176, 42.5f, 0);
|
||||
font_face_from_mem(FONT_FACE3, bm_mini_ttf, 20176, 42.5f, 0);
|
||||
font_face_from_mem(FONT_FACE4, bm_mini_ttf, 20176, 42.5f, 0);
|
||||
font_face_from_mem(FONT_FACE5, bm_mini_ttf, 20176, 42.5f, 0);
|
||||
font_face_from_mem(FONT_FACE6, bm_mini_ttf, 20176, 42.5f, 0);
|
||||
font_face_from_mem(FONT_FACE7, bm_mini_ttf, 20176, 42.5f, 0);
|
||||
font_face_from_mem(FONT_FACE8, bm_mini_ttf, 20176, 42.5f, 0);
|
||||
font_face_from_mem(FONT_FACE9, bm_mini_ttf, 20176, 42.5f, 0);
|
||||
font_face_from_mem(FONT_FACE10, bm_mini_ttf, 20176, 42.5f, 0);
|
||||
}
|
||||
|
||||
// Remap color within all existing color textures
|
||||
void font_color(const char *tag, uint32_t color) {
|
||||
font_init();
|
||||
|
||||
void TextRenderer::font_color(const char *tag, uint32_t color) {
|
||||
unsigned index = *tag - FONT_COLOR1[0];
|
||||
if (index < FONT_MAX_COLORS) {
|
||||
font_palette[index] = color;
|
||||
@ -191,22 +88,7 @@ void font_color(const char *tag, uint32_t color) {
|
||||
}
|
||||
}
|
||||
|
||||
void ui_font() {
|
||||
for (int i = 0; i < countof(fonts); ++i) {
|
||||
if (ui_collapse(va("Font %d", i), va("%p%d", &fonts[i], i))) {
|
||||
font_t *f = &fonts[i];
|
||||
ui_float("Ascent", &f->ascent);
|
||||
ui_float("Descent", &f->descent);
|
||||
ui_float("Line Gap", &f->linegap);
|
||||
f->linedist = (f->ascent - f->descent + f->linegap);
|
||||
ui_collapse_end();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void font_scales(const char *tag, float h1, float h2, float h3, float h4, float h5, float h6) {
|
||||
font_init();
|
||||
|
||||
void TextRenderer::font_scales(const char *tag, float h1, float h2, float h3, float h4, float h5, float h6) {
|
||||
unsigned index = *tag - FONT_FACE1[0];
|
||||
if (index > FONTS_MAX)
|
||||
return;
|
||||
@ -228,17 +110,23 @@ void font_scales(const char *tag, float h1, float h2, float h3, float h4, float
|
||||
// 1. Call stb_truetype.h routines to read and parse a .ttf file.
|
||||
// 1. Create a bitmap that is uploaded to the gpu using opengl.
|
||||
// 1. Calculate and save a bunch of useful variables and put them in the global font variable.
|
||||
void font_face_from_mem(const char *tag, const void *ttf_data, unsigned ttf_len, float font_size, unsigned flags) {
|
||||
void TextRenderer::font_face_from_mem(const char *tag, const void *ttf_data, unsigned ttf_len, float font_size, unsigned flags) {
|
||||
unsigned index = *tag - FONT_FACE1[0];
|
||||
if (index > FONTS_MAX)
|
||||
return;
|
||||
if (font_size <= 0 || font_size > 72)
|
||||
return;
|
||||
if (!ttf_data || !ttf_len)
|
||||
if (index > FONTS_MAX) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(flags & FONT_EM))
|
||||
if (font_size <= 0 || font_size > 72) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ttf_data || !ttf_len) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(flags & FONT_EM)) {
|
||||
flags |= FONT_ASCII; // ensure this minimal range [0020-00FF] is almost always in
|
||||
}
|
||||
|
||||
font_t *f = &fonts[index];
|
||||
f->initialized = 1;
|
||||
@ -260,91 +148,117 @@ void font_face_from_mem(const char *tag, const void *ttf_data, unsigned ttf_len,
|
||||
f->scale[5] = 0.3750f; // H5
|
||||
f->scale[6] = 0.2500f; // H6
|
||||
|
||||
const char *vs_filename = 0, *fs_filename = 0;
|
||||
const char *vs = vs_filename ? vfs_read(vs_filename) : mv_vs_source;
|
||||
const char *fs = fs_filename ? vfs_read(fs_filename) : mv_fs_source;
|
||||
f->program = shader(vs, fs, "vertexPosition,instanceGlyph", "outColor", NULL);
|
||||
//f->program = shader(vs, fs, "vertexPosition,instanceGlyph", "outColor", NULL);
|
||||
|
||||
f->program.instance();
|
||||
|
||||
// figure out what ranges we're about to bake
|
||||
#define MERGE_TABLE(table) \
|
||||
do { \
|
||||
for (unsigned i = 0; table[i]; i += 2) { \
|
||||
uint64_t begin = table[i + 0], end = table[i + 1]; \
|
||||
for (unsigned j = begin; j <= end; ++j) { \
|
||||
array_push(sorted, j); \
|
||||
} \
|
||||
} \
|
||||
} while (0)
|
||||
#define MERGE_PACKED_TABLE(codepoint_begin, table) \
|
||||
do { \
|
||||
for (int i = 0, begin = codepoint_begin, end = countof(table); i < end; i++) { \
|
||||
array_push(sorted, (unsigned)(begin + table[i])); \
|
||||
begin += table[i]; \
|
||||
} \
|
||||
} while (0)
|
||||
#define MERGE_TABLE(table, table_size) \
|
||||
int orig_size = sorted.size(); \
|
||||
sorted.resize(orig_size + table_size); \
|
||||
\
|
||||
uint64_t *p = sorted.ptrw(); \
|
||||
\
|
||||
for (int i = 0; i < table_size; ++i) { \
|
||||
p[orig_size + i] = table[i]; \
|
||||
}
|
||||
|
||||
array(uint64_t) sorted = 0;
|
||||
#define MERGE_PACKED_TABLE(codepoint_begin, table, table_size) \
|
||||
int orig_size = sorted.size(); \
|
||||
int begin = codepoint_begin; \
|
||||
sorted.resize(orig_size + table_size); \
|
||||
\
|
||||
uint64_t *p = sorted.ptrw(); \
|
||||
\
|
||||
for (int i = 0; i < table_size; ++i) { \
|
||||
p[orig_size + i] = (unsigned)(begin + table[i]); \
|
||||
begin += table[i]; \
|
||||
}
|
||||
|
||||
Vector<uint64_t> sorted;
|
||||
if (flags & FONT_ASCII) {
|
||||
MERGE_TABLE(table_common);
|
||||
MERGE_TABLE(table_common, 5);
|
||||
}
|
||||
if (flags & FONT_EM) {
|
||||
MERGE_TABLE(table_emoji);
|
||||
MERGE_TABLE(table_emoji, 7);
|
||||
}
|
||||
if (flags & FONT_EU) {
|
||||
MERGE_TABLE(table_eastern_europe);
|
||||
MERGE_TABLE(table_eastern_europe, 19);
|
||||
}
|
||||
if (flags & FONT_RU) {
|
||||
MERGE_TABLE(table_western_europe);
|
||||
MERGE_TABLE(table_western_europe, 15);
|
||||
}
|
||||
if (flags & FONT_EL) {
|
||||
MERGE_TABLE(table_western_europe);
|
||||
MERGE_TABLE(table_western_europe, 15);
|
||||
}
|
||||
if (flags & FONT_AR) {
|
||||
MERGE_TABLE(table_middle_east);
|
||||
MERGE_TABLE(table_middle_east, 7);
|
||||
}
|
||||
if (flags & FONT_HE) {
|
||||
MERGE_TABLE(table_middle_east);
|
||||
MERGE_TABLE(table_middle_east, 7);
|
||||
}
|
||||
if (flags & FONT_TH) {
|
||||
MERGE_TABLE(table_thai);
|
||||
MERGE_TABLE(table_thai, 5);
|
||||
}
|
||||
if (flags & FONT_VI) {
|
||||
MERGE_TABLE(table_vietnamese);
|
||||
MERGE_TABLE(table_vietnamese, 15);
|
||||
}
|
||||
if (flags & FONT_KR) {
|
||||
MERGE_TABLE(table_korean);
|
||||
MERGE_TABLE(table_korean, 7);
|
||||
}
|
||||
if (flags & FONT_JP) {
|
||||
MERGE_TABLE(table_chinese_japanese_common);
|
||||
MERGE_PACKED_TABLE(0x4E00, packed_table_japanese);
|
||||
{
|
||||
MERGE_TABLE(table_chinese_japanese_common, 7);
|
||||
}
|
||||
{
|
||||
MERGE_PACKED_TABLE(0x4E00, packed_table_japanese, 2999);
|
||||
}
|
||||
}
|
||||
if (flags & FONT_ZH) {
|
||||
MERGE_TABLE(table_chinese_japanese_common);
|
||||
MERGE_PACKED_TABLE(0x4E00, packed_table_chinese);
|
||||
{
|
||||
MERGE_TABLE(table_chinese_japanese_common, 7);
|
||||
}
|
||||
{
|
||||
MERGE_PACKED_TABLE(0x4E00, packed_table_chinese, 2500);
|
||||
}
|
||||
} // zh-simplified
|
||||
if (flags & FONT_ZH) {
|
||||
MERGE_TABLE(table_chinese_punctuation);
|
||||
MERGE_TABLE(table_chinese_punctuation, 3);
|
||||
} // both zh-simplified and zh-full
|
||||
// if(flags & FONT_ZH) { MERGE_TABLE(table_chinese_full); } // zh-full
|
||||
array_sort(sorted, less_64_ptr);
|
||||
array_unique(sorted, less_64_ptr); // sort + unique pass
|
||||
|
||||
sorted.sort();
|
||||
|
||||
for (int i = 0; i < sorted.size(); ++i) {
|
||||
uint64_t current_element = sorted[i];
|
||||
|
||||
for (int j = i + 1; j < sorted.size(); ++j) {
|
||||
if (sorted[j] == current_element) {
|
||||
sorted.remove(j);
|
||||
--j;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// pack and create bitmap
|
||||
unsigned char *bitmap = (unsigned char *)MALLOC(f->height * f->width);
|
||||
unsigned char *bitmap = memnew_arr(unsigned char, f->height * f->width);
|
||||
|
||||
int charCount = *array_back(sorted) - sorted[0] + 1; // 0xEFFFF;
|
||||
f->cdata = (stbtt_packedchar *)CALLOC(1, sizeof(stbtt_packedchar) * charCount);
|
||||
f->iter2cp = (unsigned *)MALLOC(sizeof(unsigned) * charCount);
|
||||
f->cp2iter = (unsigned *)MALLOC(sizeof(unsigned) * charCount);
|
||||
for (int i = 0; i < charCount; ++i)
|
||||
int charCount = sorted[sorted.size() - 1] - sorted[0] + 1; // 0xEFFFF;
|
||||
f->cdata = (stbtt_packedchar *)calloc(1, sizeof(stbtt_packedchar) * charCount);
|
||||
f->iter2cp = memnew_arr(unsigned, charCount);
|
||||
f->cp2iter = memnew_arr(unsigned, charCount);
|
||||
for (int i = 0; i < charCount; ++i) {
|
||||
f->iter2cp[i] = f->cp2iter[i] = 0xFFFD; // default invalid glyph
|
||||
}
|
||||
|
||||
// find first char
|
||||
{
|
||||
stbtt_fontinfo info = { 0 };
|
||||
stbtt_InitFont(&info, ttf_data, stbtt_GetFontOffsetForIndex(ttf_data, 0));
|
||||
stbtt_InitFont(&info, (const unsigned char *)ttf_data, stbtt_GetFontOffsetForIndex((const unsigned char *)ttf_data, 0));
|
||||
|
||||
for (int i = 0, end = array_count(sorted); i < end; ++i) {
|
||||
for (int i = 0, end = sorted.size(); i < end; ++i) {
|
||||
unsigned glyph = sorted[i];
|
||||
if (!stbtt_FindGlyphIndex(&info, glyph))
|
||||
continue;
|
||||
@ -355,43 +269,43 @@ void font_face_from_mem(const char *tag, const void *ttf_data, unsigned ttf_len,
|
||||
|
||||
stbtt_pack_context pc;
|
||||
if (!stbtt_PackBegin(&pc, bitmap, f->width, f->height, 0, 1, NULL)) {
|
||||
PANIC("Failed to initialize atlas font");
|
||||
ERR_FAIL_COND("Failed to initialize atlas font");
|
||||
}
|
||||
stbtt_PackSetOversampling(&pc, flags & FONT_OVERSAMPLE_X ? 2 : 1, flags & FONT_OVERSAMPLE_Y ? 2 : 1); /*useful on small fonts*/
|
||||
int count = 0;
|
||||
for (int i = 0, num = array_count(sorted); i < num; ++i) {
|
||||
for (int i = 0, num = sorted.size(); i < num; ++i) {
|
||||
uint64_t begin = sorted[i], end = sorted[i];
|
||||
while (i < (num - 1) && (sorted[i + 1] - sorted[i]) == 1)
|
||||
while (i < (num - 1) && (sorted[i + 1] - sorted[i]) == 1) {
|
||||
end = sorted[++i];
|
||||
}
|
||||
//printf("(%d,%d)", (unsigned)begin, (unsigned)end);
|
||||
|
||||
if (begin < f->begin)
|
||||
continue;
|
||||
|
||||
if (stbtt_PackFontRange(&pc, ttf_data, 0, f->font_size, begin, end - begin + 1, (stbtt_packedchar *)f->cdata + begin - f->begin)) {
|
||||
if (stbtt_PackFontRange(&pc, (const unsigned char *)ttf_data, 0, f->font_size, begin, end - begin + 1, (stbtt_packedchar *)f->cdata + begin - f->begin)) {
|
||||
for (uint64_t cp = begin; cp <= end; ++cp) {
|
||||
// unicode->index runtime lookup
|
||||
f->cp2iter[cp - f->begin] = count;
|
||||
f->iter2cp[count++] = cp;
|
||||
}
|
||||
} else {
|
||||
PRINTF("!Failed to pack atlas font. Likely out of texture mem.");
|
||||
ERR_PRINT("!Failed to pack atlas font. Likely out of texture mem.");
|
||||
}
|
||||
}
|
||||
stbtt_PackEnd(&pc);
|
||||
f->num_glyphs = count;
|
||||
|
||||
assert(f->num_glyphs < charCount);
|
||||
|
||||
array_free(sorted);
|
||||
//CRASH_COND(f->num_glyphs < (unsigned int)charCount);
|
||||
|
||||
// calculate vertical font metrics
|
||||
stbtt_fontinfo info = { 0 };
|
||||
stbtt_InitFont(&info, ttf_data, stbtt_GetFontOffsetForIndex(ttf_data, 0));
|
||||
stbtt_InitFont(&info, (const unsigned char *)ttf_data, stbtt_GetFontOffsetForIndex((const unsigned char *)ttf_data, 0));
|
||||
|
||||
int a, d, l;
|
||||
if (!stbtt_GetFontVMetricsOS2(&info, &a, &d, &l))
|
||||
if (!stbtt_GetFontVMetricsOS2(&info, &a, &d, &l)) {
|
||||
stbtt_GetFontVMetrics(&info, &a, &d, &l);
|
||||
}
|
||||
|
||||
f->ascent = a;
|
||||
f->descent = d;
|
||||
@ -402,7 +316,7 @@ void font_face_from_mem(const char *tag, const void *ttf_data, unsigned ttf_len,
|
||||
// save some gpu memory by truncating unused vertical space in atlas texture
|
||||
{
|
||||
int max_y1 = 0;
|
||||
for (int i = 0; i < f->num_glyphs; i++) {
|
||||
for (unsigned int i = 0; i < f->num_glyphs; i++) {
|
||||
int cp = f->iter2cp[i];
|
||||
if (cp == 0xFFFD)
|
||||
continue;
|
||||
@ -415,7 +329,7 @@ void font_face_from_mem(const char *tag, const void *ttf_data, unsigned ttf_len,
|
||||
f->height = max_y1 + 1;
|
||||
}
|
||||
|
||||
PRINTF("Font atlas size %dx%d (GL_R, %5.2fKiB) (%u glyphs)\n", f->width, f->height, f->width * f->height / 1024.f, f->num_glyphs);
|
||||
LOG_MSG("Font atlas size %dx%d (GL_R, %5.2fKiB) (%u glyphs)\n", f->width, f->height, f->width * f->height / 1024.f, f->num_glyphs);
|
||||
|
||||
// vao
|
||||
glGenVertexArrays(1, &f->vao);
|
||||
@ -454,10 +368,10 @@ void font_face_from_mem(const char *tag, const void *ttf_data, unsigned ttf_len,
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
|
||||
// last chance to inspect the font atlases
|
||||
if (flag("--font-debug"))
|
||||
stbi_write_png(va("font_debug%d.png", index), f->width, f->height, 1, bitmap, 0);
|
||||
//if (flag("--font-debug"))
|
||||
// stbi_write_png(va("font_debug%d.png", index), f->width, f->height, 1, bitmap, 0);
|
||||
|
||||
FREE(bitmap);
|
||||
memfree(bitmap);
|
||||
|
||||
// setup and upload font metadata texture
|
||||
// used for lookup in the bitmap texture
|
||||
@ -465,10 +379,10 @@ void font_face_from_mem(const char *tag, const void *ttf_data, unsigned ttf_len,
|
||||
glActiveTexture(GL_TEXTURE1);
|
||||
glBindTexture(GL_TEXTURE_2D, f->texture_offsets);
|
||||
|
||||
float *texture_offsets = (float *)MALLOC(sizeof(float) * 8 * f->num_glyphs);
|
||||
float *texture_offsets = memnew_arr(float, 8 * f->num_glyphs);
|
||||
|
||||
// remap larger 0xFFFF unicodes into smaller NUM_GLYPHS glyphs
|
||||
for (int i = 0, count = 0; i < f->num_glyphs; i++) {
|
||||
for (unsigned i = 0, count = 0; i < f->num_glyphs; i++) {
|
||||
unsigned cp = f->iter2cp[i];
|
||||
if (cp == 0xFFFD)
|
||||
continue;
|
||||
@ -497,7 +411,7 @@ void font_face_from_mem(const char *tag, const void *ttf_data, unsigned ttf_len,
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, f->num_glyphs, 2, 0, GL_RGBA, GL_FLOAT, texture_offsets);
|
||||
|
||||
FREE(texture_offsets);
|
||||
memfree(texture_offsets);
|
||||
|
||||
// setup color texture
|
||||
glGenTextures(1, &f->texture_colors);
|
||||
@ -509,18 +423,19 @@ void font_face_from_mem(const char *tag, const void *ttf_data, unsigned ttf_len,
|
||||
glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_REPEAT);
|
||||
|
||||
// upload constant uniforms
|
||||
glUseProgram(f->program);
|
||||
glUniform1i(glGetUniformLocation(f->program, "sampler_font"), 0);
|
||||
glUniform1i(glGetUniformLocation(f->program, "sampler_meta"), 1);
|
||||
glUniform1i(glGetUniformLocation(f->program, "sampler_colors"), 2);
|
||||
glUseProgram(f->program->get_program());
|
||||
glUniform1i(glGetUniformLocation(f->program->get_program(), "sampler_font"), 0);
|
||||
glUniform1i(glGetUniformLocation(f->program->get_program(), "sampler_meta"), 1);
|
||||
glUniform1i(glGetUniformLocation(f->program->get_program(), "sampler_colors"), 2);
|
||||
|
||||
glUniform2f(glGetUniformLocation(f->program, "res_bitmap"), f->width, f->height);
|
||||
glUniform2f(glGetUniformLocation(f->program, "res_meta"), f->num_glyphs, 2);
|
||||
glUniform1f(glGetUniformLocation(f->program, "num_colors"), FONT_MAX_COLORS);
|
||||
glUniform2f(glGetUniformLocation(f->program->get_program(), "res_bitmap"), f->width, f->height);
|
||||
glUniform2f(glGetUniformLocation(f->program->get_program(), "res_meta"), f->num_glyphs, 2);
|
||||
glUniform1f(glGetUniformLocation(f->program->get_program(), "num_colors"), FONT_MAX_COLORS);
|
||||
(void)flags;
|
||||
}
|
||||
|
||||
void font_face(const char *tag, const char *filename_ttf, float font_size, unsigned flags) {
|
||||
void TextRenderer::font_face(const char *tag, const char *filename_ttf, float font_size, unsigned flags) {
|
||||
/*
|
||||
font_init();
|
||||
|
||||
int len;
|
||||
@ -529,9 +444,10 @@ void font_face(const char *tag, const char *filename_ttf, float font_size, unsig
|
||||
buffer = file_load(filename_ttf, &len);
|
||||
|
||||
font_face_from_mem(tag, buffer, len, font_size, flags);
|
||||
*/
|
||||
}
|
||||
|
||||
static void font_draw_cmd(font_t *f, const float *glyph_data, int glyph_idx, float factor, vec2 offset) {
|
||||
void TextRenderer::font_draw_cmd(font_t *f, const float *glyph_data, int glyph_idx, float factor, Vector2 offset) {
|
||||
// Backup GL state
|
||||
GLint last_program, last_vertex_array;
|
||||
GLint last_texture0, last_texture1, last_texture2;
|
||||
@ -574,14 +490,14 @@ static void font_draw_cmd(font_t *f, const float *glyph_data, int glyph_idx, flo
|
||||
glBindVertexArray(f->vao);
|
||||
|
||||
// update uniforms
|
||||
glUseProgram(f->program);
|
||||
glUniform1f(glGetUniformLocation(f->program, "scale_factor"), factor);
|
||||
glUniform2fv(glGetUniformLocation(f->program, "string_offset"), 1, &offset.x);
|
||||
glUniform1f(glGetUniformLocation(f->program, "offset_firstline"), f->ascent * f->factor);
|
||||
glUseProgram(f->program->get_program());
|
||||
glUniform1f(glGetUniformLocation(f->program->get_program(), "scale_factor"), factor);
|
||||
glUniform2fv(glGetUniformLocation(f->program->get_program(), "string_offset"), 1, &offset.x);
|
||||
glUniform1f(glGetUniformLocation(f->program->get_program(), "offset_firstline"), f->ascent * f->factor);
|
||||
|
||||
GLint dims[4] = { 0 };
|
||||
glGetIntegerv(GL_VIEWPORT, dims);
|
||||
glUniform2f(glGetUniformLocation(f->program, "resolution"), dims[2], dims[3]);
|
||||
glUniform2f(glGetUniformLocation(f->program->get_program(), "resolution"), dims[2], dims[3]);
|
||||
|
||||
// actual uploading
|
||||
glBindBuffer(GL_ARRAY_BUFFER, f->vbo_instances);
|
||||
@ -611,18 +527,20 @@ static void font_draw_cmd(font_t *f, const float *glyph_data, int glyph_idx, flo
|
||||
// 1. call font_face() if it's the first time it's called.
|
||||
// 1. parse the string and update the instance vbo, then upload it
|
||||
// 1. draw the string
|
||||
static vec2 font_draw_ex(const char *text, vec2 offset, const char *col, void (*draw_cmd)(font_t *, const float *, int, float, vec2)) {
|
||||
Vector2 TextRenderer::font_draw_ex(const String &text, Vector2 offset, const char *col, void (*draw_cmd)(font_t *, const float *, int, float, Vector2)) {
|
||||
font_init();
|
||||
|
||||
// sanity checks
|
||||
int len = strlen(text);
|
||||
int len = text.length();
|
||||
if (len >= FONT_MAX_STRING_LEN) {
|
||||
return vec2(0, 0);
|
||||
return Vector2(0, 0);
|
||||
}
|
||||
|
||||
// pre-init
|
||||
static __thread float *text_glyph_data;
|
||||
do_once text_glyph_data = MALLOC(4 * FONT_MAX_STRING_LEN * sizeof(float));
|
||||
static __thread float *text_glyph_data = NULL;
|
||||
if (!text_glyph_data) {
|
||||
text_glyph_data = memnew_arr(float, 4 * FONT_MAX_STRING_LEN);
|
||||
}
|
||||
|
||||
// ready
|
||||
font_t *f = &fonts[0];
|
||||
@ -631,13 +549,10 @@ static vec2 font_draw_ex(const char *text, vec2 offset, const char *col, void (*
|
||||
float X = 0, Y = 0, W = 0, L = f->ascent * f->factor * f->scale[S], LL = L; // LL=largest linedist
|
||||
offset.y = -offset.y; // invert y polarity
|
||||
|
||||
// utf8 to utf32
|
||||
array(uint32_t) unicode = string32(text);
|
||||
|
||||
// parse string
|
||||
float *t = text_glyph_data;
|
||||
for (int i = 0, end = array_count(unicode); i < end; ++i) {
|
||||
uint32_t ch = unicode[i];
|
||||
for (int i = 0, end = text.length(); i < end; ++i) {
|
||||
uint32_t ch = text[i];
|
||||
|
||||
if (ch == '\n') {
|
||||
// change cursor, advance y, record largest x as width, increase height
|
||||
@ -701,304 +616,74 @@ static vec2 font_draw_ex(const char *text, vec2 offset, const char *col, void (*
|
||||
draw_cmd(f, text_glyph_data, (t - text_glyph_data) / 4, f->scale[S], offset);
|
||||
|
||||
//if(strstr(text, "fps")) printf("(%f,%f) (%f) L:%f LINEDIST:%f\n", X, Y, W, L, f->linedist);
|
||||
return abs2(vec2(W * W > X * X ? W : X, Y * Y > LL * LL ? Y : LL));
|
||||
return Vector2(W * W > X * X ? W : X, Y * Y > LL * LL ? Y : LL).abs();
|
||||
}
|
||||
|
||||
void *font_colorize(const char *text, const char *comma_types, const char *comma_keywords) {
|
||||
// reallocate memory
|
||||
static __thread int slot = 0;
|
||||
static __thread char *buf[16] = { 0 };
|
||||
static __thread array(char *) list[16] = { 0 };
|
||||
|
||||
slot = (slot + 1) % 16;
|
||||
buf[slot] = REALLOC(buf[slot], strlen(text) + 1);
|
||||
memset(buf[slot], 0, strlen(text) + 1);
|
||||
|
||||
// ready
|
||||
char *col = buf[slot];
|
||||
char *str = STRDUP(text);
|
||||
|
||||
// split inputs
|
||||
array(char *) TYPES = strsplit(comma_types, ", ");
|
||||
array(char *) KEYWORDS = strsplit(comma_keywords, ", ");
|
||||
|
||||
// ignored characters
|
||||
char delims[] = " ,(){}[];\t\n";
|
||||
int num_delims = strlen(delims);
|
||||
|
||||
char operators[] = "/+-*<>=&|";
|
||||
int num_operators = strlen(operators);
|
||||
|
||||
struct token {
|
||||
char *start, *stop;
|
||||
enum {
|
||||
TOKEN_OTHER,
|
||||
TOKEN_OPERATOR,
|
||||
TOKEN_NUMERIC,
|
||||
TOKEN_FUNCTION,
|
||||
TOKEN_KEYWORD,
|
||||
TOKEN_COMMENT,
|
||||
TOKEN_VARIABLE,
|
||||
TOKEN_UNSET
|
||||
} type;
|
||||
} tokens[9999]; // hurr
|
||||
int num_tokens = 0; // running counter
|
||||
|
||||
char *ptr = str;
|
||||
while (*ptr) {
|
||||
// skip delimiters
|
||||
int is_delim = 0;
|
||||
for (int i = 0; i < num_delims; i++) {
|
||||
if (*ptr == delims[i]) {
|
||||
is_delim = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (is_delim == 1) {
|
||||
ptr++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// found a token!
|
||||
char *start = ptr;
|
||||
|
||||
if (*ptr == '/' && *(ptr + 1) == '/') {
|
||||
// found a line comment, go to end of line or end of file
|
||||
while (*ptr != '\n' && *ptr != '\0') {
|
||||
ptr++;
|
||||
}
|
||||
|
||||
tokens[num_tokens].start = start;
|
||||
tokens[num_tokens].stop = ptr;
|
||||
tokens[num_tokens].type = TOKEN_COMMENT;
|
||||
num_tokens++;
|
||||
|
||||
ptr++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (*ptr == '/' && *(ptr + 1) == '*') {
|
||||
// found a block comment, go to end of line or end of file
|
||||
while (!(*ptr == '*' && *(ptr + 1) == '/') && *ptr != '\0') {
|
||||
ptr++;
|
||||
}
|
||||
ptr++;
|
||||
|
||||
tokens[num_tokens].start = start;
|
||||
tokens[num_tokens].stop = ptr + 1;
|
||||
tokens[num_tokens].type = TOKEN_COMMENT;
|
||||
num_tokens++;
|
||||
|
||||
ptr++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// check if it's an operator
|
||||
int is_operator = 0;
|
||||
for (int i = 0; i < num_operators; i++) {
|
||||
if (*ptr == operators[i]) {
|
||||
is_operator = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (is_operator == 1) {
|
||||
tokens[num_tokens].start = start;
|
||||
tokens[num_tokens].stop = ptr + 1;
|
||||
tokens[num_tokens].type = TOKEN_OPERATOR;
|
||||
num_tokens++;
|
||||
ptr++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// it's either a name, type, a keyword, a function, or an names separated by an operator without spaces
|
||||
while (*ptr) {
|
||||
// check whether it's an operator stuck between two names
|
||||
int is_operator2 = 0;
|
||||
for (int i = 0; i < num_operators; i++) {
|
||||
if (*ptr == operators[i]) {
|
||||
is_operator2 = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (is_operator2 == 1) {
|
||||
tokens[num_tokens].start = start;
|
||||
tokens[num_tokens].stop = ptr;
|
||||
tokens[num_tokens].type = TOKEN_UNSET;
|
||||
num_tokens++;
|
||||
break;
|
||||
}
|
||||
|
||||
// otherwise go until we find the next delimiter
|
||||
int is_delim2 = 0;
|
||||
for (int i = 0; i < num_delims; i++) {
|
||||
if (*ptr == delims[i]) {
|
||||
is_delim2 = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (is_delim2 == 1) {
|
||||
tokens[num_tokens].start = start;
|
||||
tokens[num_tokens].stop = ptr;
|
||||
tokens[num_tokens].type = TOKEN_UNSET;
|
||||
num_tokens++;
|
||||
ptr++;
|
||||
break;
|
||||
}
|
||||
|
||||
// did not find delimiter, check next char
|
||||
ptr++;
|
||||
}
|
||||
}
|
||||
|
||||
// determine the types of the unset tokens, i.e. either
|
||||
// a name, a type, a keyword, or a function
|
||||
int num_keywords = array_count(KEYWORDS);
|
||||
int num_types = array_count(TYPES);
|
||||
|
||||
for (int i = 0; i < num_tokens; i++) {
|
||||
// TOKEN_OPERATOR and TOKEN_COMMENT should already be set, so skip those
|
||||
if (tokens[i].type != TOKEN_UNSET) {
|
||||
continue;
|
||||
}
|
||||
|
||||
char end_char = *tokens[i].stop;
|
||||
|
||||
// temporarily null terminate at end of token, restored after parsing
|
||||
*tokens[i].stop = '\0';
|
||||
|
||||
// parse
|
||||
|
||||
// if it's a keyword
|
||||
int is_keyword = 0;
|
||||
for (int j = 0; j < num_keywords; j++) {
|
||||
if (strcmp(tokens[i].start, KEYWORDS[j]) == 0) {
|
||||
is_keyword = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (is_keyword == 1) {
|
||||
tokens[i].type = TOKEN_KEYWORD;
|
||||
*tokens[i].stop = end_char;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check if it's a function
|
||||
float f;
|
||||
if (end_char == '(') {
|
||||
tokens[i].type = TOKEN_FUNCTION;
|
||||
*tokens[i].stop = end_char;
|
||||
continue;
|
||||
}
|
||||
|
||||
// or if it's a numeric value. catches both integers and floats
|
||||
if (sscanf(tokens[i].start, "%f", &f) == 1) {
|
||||
tokens[i].type = TOKEN_NUMERIC;
|
||||
*tokens[i].stop = end_char;
|
||||
continue;
|
||||
}
|
||||
|
||||
// if it's a variable type
|
||||
int is_type = 0;
|
||||
for (int j = 0; j < num_types; j++) {
|
||||
if (strcmp(tokens[i].start, TYPES[j]) == 0) {
|
||||
is_type = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (is_type == 1) {
|
||||
tokens[i].type = TOKEN_VARIABLE;
|
||||
*tokens[i].stop = end_char;
|
||||
continue;
|
||||
}
|
||||
|
||||
// otherwise it's a regular variable name
|
||||
tokens[i].type = TOKEN_OTHER;
|
||||
*tokens[i].stop = end_char;
|
||||
}
|
||||
|
||||
// print all tokens and their types
|
||||
for (int i = 0; i < num_tokens; i++) {
|
||||
for (char *p = tokens[i].start; p != tokens[i].stop; p++) {
|
||||
col[(p - str)] = tokens[i].type;
|
||||
}
|
||||
}
|
||||
|
||||
FREE(str);
|
||||
return col;
|
||||
}
|
||||
|
||||
static vec2 gotoxy = { 0 };
|
||||
|
||||
// Return cursor
|
||||
vec2 font_xy() {
|
||||
Vector2 TextRenderer::font_xy() {
|
||||
return gotoxy;
|
||||
}
|
||||
|
||||
// Relocate cursor
|
||||
void font_goto(float x, float y) {
|
||||
gotoxy = vec2(x, y);
|
||||
void TextRenderer::font_goto(float x, float y) {
|
||||
gotoxy = Vector2(x, y);
|
||||
}
|
||||
|
||||
// Print and linefeed. Text may include markup code
|
||||
vec2 font_print(const char *text) {
|
||||
Vector2 TextRenderer::font_print(const String &text) {
|
||||
// @fixme: remove this hack
|
||||
if (text[0] == FONT_LEFT[0]) {
|
||||
int window_width = AppWindow::get_singleton()->get_width();
|
||||
int window_height = AppWindow::get_singleton()->get_height();
|
||||
|
||||
int l = text[1] == FONT_LEFT[1];
|
||||
int c = text[1] == FONT_CENTER[1];
|
||||
int r = text[1] == FONT_RIGHT[1];
|
||||
if (l || c || r) {
|
||||
vec2 rect = font_rect(text + 2);
|
||||
gotoxy.x = l ? 0 : r ? (window_width() - rect.x)
|
||||
: window_width() / 2 - rect.x / 2;
|
||||
return font_print(text + 2);
|
||||
String ntext = text.substr(2);
|
||||
|
||||
Vector2 rect = font_rect(ntext);
|
||||
gotoxy.x = l ? 0 : r ? (window_width - rect.x)
|
||||
: window_width / 2 - rect.x / 2;
|
||||
return font_print(ntext);
|
||||
}
|
||||
int t = text[1] == FONT_TOP[1];
|
||||
int b = text[1] == FONT_BOTTOM[1];
|
||||
int m = text[1] == FONT_MIDDLE[1];
|
||||
int B = text[1] == FONT_BASELINE[1];
|
||||
if (t || b || m || B) {
|
||||
vec2 rect = font_rect(text + 2);
|
||||
gotoxy.y = t ? 0 : b ? (window_height() - rect.y)
|
||||
: m ? window_height() / 2 - rect.y / 2
|
||||
: window_height() / 2 - rect.y / 1;
|
||||
return font_print(text + 2);
|
||||
String ntext = text.substr(2);
|
||||
|
||||
Vector2 rect = font_rect(ntext);
|
||||
gotoxy.y = t ? 0 : b ? (window_height - rect.y)
|
||||
: m ? window_height / 2 - rect.y / 2
|
||||
: window_height / 2 - rect.y / 1;
|
||||
return font_print(ntext);
|
||||
}
|
||||
}
|
||||
|
||||
vec2 dims = font_draw_ex(text, gotoxy, NULL, font_draw_cmd);
|
||||
gotoxy.y += strchr(text, '\n') ? dims.y : 0;
|
||||
gotoxy.x = strchr(text, '\n') ? 0 : gotoxy.x + dims.x;
|
||||
return dims;
|
||||
}
|
||||
Vector2 dims = font_draw_ex(text, gotoxy, NULL, font_draw_cmd);
|
||||
|
||||
// Print a code snippet with syntax highlighting
|
||||
vec2 font_highlight(const char *text, const void *colors) {
|
||||
vec2 dims = font_draw_ex(text, gotoxy, (const char *)colors, font_draw_cmd);
|
||||
gotoxy.y += strchr(text, '\n') ? dims.y : 0;
|
||||
gotoxy.x = strchr(text, '\n') ? 0 : gotoxy.x + dims.x;
|
||||
int nindex = text.find_char('\n');
|
||||
|
||||
gotoxy.y += nindex ? dims.y : 0;
|
||||
gotoxy.x = nindex ? 0 : gotoxy.x + dims.x;
|
||||
return dims;
|
||||
}
|
||||
|
||||
// Calculate the size of a string, in the pixel size specified. Count stray newlines too.
|
||||
vec2 font_rect(const char *str) {
|
||||
Vector2 TextRenderer::font_rect(const String &str) {
|
||||
return font_draw_ex(str, gotoxy, NULL, NULL);
|
||||
}
|
||||
|
||||
font_metrics_t font_metrics(const char *text) {
|
||||
TextRenderer::font_metrics_t TextRenderer::font_metrics(const String &text) {
|
||||
font_metrics_t m = { 0 };
|
||||
int S = 3;
|
||||
font_t *f = &fonts[0];
|
||||
|
||||
// utf8 to utf32
|
||||
array(uint32_t) unicode = string32(text);
|
||||
|
||||
// parse string
|
||||
for (int i = 0, end = array_count(unicode); i < end; ++i) {
|
||||
uint32_t ch = unicode[i];
|
||||
for (int i = 0, end = text.length(); i < end; ++i) {
|
||||
uint32_t ch = text[i];
|
||||
if (ch >= 1 && ch <= 6) {
|
||||
S = ch;
|
||||
continue;
|
||||
@ -1019,6 +704,13 @@ font_metrics_t font_metrics(const char *text) {
|
||||
return m;
|
||||
}
|
||||
|
||||
TextRenderer *TextRenderer::get_singleton();
|
||||
TextRenderer *TextRenderer::get_singleton() {
|
||||
return _singleton;
|
||||
}
|
||||
|
||||
TextRenderer *TextRenderer::_singleton;
|
||||
TextRenderer::TextRenderer() {
|
||||
_singleton = this;
|
||||
_fonts_initialized = false;
|
||||
}
|
||||
|
||||
TextRenderer *TextRenderer::_singleton = NULL;
|
||||
|
@ -7,6 +7,13 @@
|
||||
|
||||
#include "object/object.h"
|
||||
|
||||
#include "core/ustring.h"
|
||||
|
||||
#include "text_material.h"
|
||||
|
||||
// TODO figure out how to forward declare stbtt_packedchar
|
||||
#include "3rd_stb_truetype.h"
|
||||
|
||||
// font size tags
|
||||
#define FONT_H1 "\1" // largest
|
||||
#define FONT_H2 "\2"
|
||||
@ -86,6 +93,8 @@ public:
|
||||
float linedist; // distance between the baseline of two lines (ascent - descent + linegap)
|
||||
} font_metrics_t;
|
||||
|
||||
void font_init();
|
||||
|
||||
// configures
|
||||
void font_face(const char *face_tag, const char *filename_ttf, float font_size, unsigned flags);
|
||||
void font_face_from_mem(const char *tag, const void *ttf_buffer, unsigned ttf_len, float font_size, unsigned flags);
|
||||
@ -93,24 +102,81 @@ public:
|
||||
void font_color(const char *color_tag, uint32_t color);
|
||||
|
||||
// commands
|
||||
vec2 font_xy();
|
||||
Vector2 font_xy();
|
||||
void font_goto(float x, float y);
|
||||
vec2 font_print(const char *text);
|
||||
vec2 font_rect(const char *text);
|
||||
font_metrics_t font_metrics(const char *text);
|
||||
// void font_clip(vec2 topleft, vec2 bottomright);
|
||||
// void font_wrap(vec2 topleft, vec2 bottomright);
|
||||
|
||||
// syntax highlighting
|
||||
void *font_colorize(const char *text, const char *comma_types, const char *comma_keywords); // comma separated tokens. expensive, please cache result.
|
||||
vec2 font_highlight(const char *text, const void *colors);
|
||||
|
||||
// ui
|
||||
void ui_font();
|
||||
Vector2 font_print(const String &text);
|
||||
Vector2 font_rect(const String &str);
|
||||
font_metrics_t font_metrics(const String &text);
|
||||
|
||||
static TextRenderer *get_singleton();
|
||||
|
||||
TextRenderer();
|
||||
|
||||
protected:
|
||||
bool _fonts_initialized;
|
||||
|
||||
enum { FONT_MAX_COLORS = 256 };
|
||||
enum { FONT_MAX_STRING_LEN = 40000 }; // more glyphs than any reasonable person would show on the screen at once. you can only fit 20736 10x10 rects in a 1920x1080 window
|
||||
|
||||
static uint32_t font_palette[FONT_MAX_COLORS];
|
||||
|
||||
typedef struct font_t {
|
||||
bool initialized;
|
||||
|
||||
//char filename[256];
|
||||
|
||||
// character info
|
||||
// filled up by stb_truetype.h
|
||||
stbtt_packedchar *cdata;
|
||||
unsigned num_glyphs;
|
||||
unsigned *cp2iter;
|
||||
unsigned *iter2cp;
|
||||
unsigned begin; // first glyph. used in cp2iter table to clamp into a lesser range
|
||||
|
||||
// font info and data
|
||||
int height; // bitmap height
|
||||
int width; // bitmap width
|
||||
float font_size; // font size in pixels (matches scale[0+1] size below)
|
||||
float factor; // font factor (font_size / (ascent - descent))
|
||||
float scale[7]; // user defined font scale (match H1..H6 tags)
|
||||
|
||||
// displacement info
|
||||
float ascent; // max distance above baseline for all glyphs
|
||||
float descent; // max distance below baseline for all glyphs
|
||||
float linegap; // distance betwen ascent of next line and descent of current line
|
||||
float linedist; // distance between the baseline of two lines (ascent - descent + linegap)
|
||||
|
||||
// opengl stuff
|
||||
GLuint vao;
|
||||
Ref<TextMaterial> program;
|
||||
|
||||
// font bitmap texture
|
||||
// generated using stb_truetype.h
|
||||
GLuint texture_fontdata;
|
||||
|
||||
// metadata texture.
|
||||
// first row contains information on which parts of the bitmap correspond to a glyph.
|
||||
// the second row contain information about the relative displacement of the glyph relative to the cursor position
|
||||
GLuint texture_offsets;
|
||||
|
||||
// color texture
|
||||
// used to color each glyph individually, e.g. for syntax highlighting
|
||||
GLuint texture_colors;
|
||||
|
||||
// vbos
|
||||
GLuint vbo_quad; // Vector2: simply just a regular [0,1]x[0,1] quad
|
||||
GLuint vbo_instances; // vec4: (char_pos_x, char_pos_y, char_index, color_index)
|
||||
} font_t;
|
||||
|
||||
enum { FONTS_MAX = 10 };
|
||||
|
||||
font_t fonts[FONTS_MAX];
|
||||
|
||||
static void font_draw_cmd(font_t *f, const float *glyph_data, int glyph_idx, float factor, Vector2 offset);
|
||||
Vector2 font_draw_ex(const String &text, Vector2 offset, const char *col, void (*draw_cmd)(font_t *, const float *, int, float, Vector2));
|
||||
|
||||
Vector2 gotoxy;
|
||||
|
||||
static TextRenderer *_singleton;
|
||||
};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user