parent
805f633af0
commit
dcce0ed11b
@ -1,7 +1,7 @@ |
|||||||
LDFLAGS = -lm -lgrapheme
|
LDFLAGS = -lm -lgrapheme -lSDL2 -lGLEW -lGL
|
||||||
CFLAGS = -g -Wall -Wextra -pedantic
|
CFLAGS = -g -Wall -Wextra -pedantic
|
||||||
SOURCE = main.c font.c cache.c hash.c util.c
|
SOURCE = main.c font.c cache.c hash.c util.c ren.c
|
||||||
HEADER = font.h hash.h cache.h util.h
|
HEADER = font.h hash.h cache.h util.h ren.h
|
||||||
|
|
||||||
test: ${HEADER} ${SOURCE} |
test: ${HEADER} ${SOURCE} |
||||||
gcc ${CFLAGS} ${LDFLAGS} ${SOURCE} -o test
|
gcc ${CFLAGS} ${LDFLAGS} ${SOURCE} -o test
|
||||||
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
@ -1,33 +1,30 @@ |
|||||||
#include <stdlib.h> |
#include <stdlib.h> |
||||||
#include <stdio.h> |
#include <stdio.h> |
||||||
#include <grapheme.h> |
|
||||||
|
|
||||||
#include "cache.h" |
#include "ren.h" |
||||||
#include "font.h" |
|
||||||
#include "util.h" |
#include "util.h" |
||||||
|
|
||||||
|
|
||||||
int main(void) |
int main(void) |
||||||
{ |
{ |
||||||
int err; |
SDL_Window *win = SDL_CreateWindow( |
||||||
struct font_atlas *at = font_init(); |
"test render", |
||||||
err = font_load(at, "./monospace.ttf"); |
SDL_WINDOWPOS_UNDEFINED, |
||||||
ERROR(err, -1, printf("failed to load font\n")); |
SDL_WINDOWPOS_UNDEFINED, |
||||||
|
500, |
||||||
const char *s = "ciao mamma"; |
500, |
||||||
const struct font_glyph *g; |
SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE); |
||||||
size_t ret, off; |
if (ren_init(win)) { |
||||||
uint_least32_t cp; |
printf("renderer init error: %s\n", ren_strerror()); |
||||||
for (off = 0; (ret = grapheme_decode_utf8(s+off, SIZE_MAX, &cp)) > 0 && cp != 0; off += ret) { |
return 1; |
||||||
printf("%.*s (%d) -> %d\n", (int)ret, s+off, (int)ret, cp); |
|
||||||
g = font_get_glyph_texture(at, cp); |
|
||||||
if (!g) |
|
||||||
printf("g is NULL\n"); |
|
||||||
} |
} |
||||||
|
|
||||||
font_dump(at, "./atlas.png"); |
ren_render_text("ciao mamma", 100, 100, 100, 100, 12); |
||||||
|
SDL_GL_SwapWindow(win); |
||||||
|
|
||||||
font_free(at); |
while(1); |
||||||
|
|
||||||
|
ren_free(); |
||||||
|
SDL_DestroyWindow(win); |
||||||
return 0; |
return 0; |
||||||
} |
} |
||||||
|
@ -1,53 +1,423 @@ |
|||||||
#include <SDL2/SDL.h> |
#include <SDL2/SDL.h> |
||||||
#include <GL/glew.h> |
#include <GL/glew.h> |
||||||
#include <SDL2/SDL_opengl.h> |
#include <SDL2/SDL_opengl.h> |
||||||
|
#include <SDL2/SDL_video.h> |
||||||
|
#include <grapheme.h> |
||||||
|
#include <stdio.h> |
||||||
|
|
||||||
|
#include "util.h" |
||||||
|
#include "font.h" |
||||||
|
#include "ren.h" |
||||||
|
|
||||||
#define GLSL_VERT_SHADER "vertex.glsl" |
|
||||||
#define GLSL_FRAG_SHADER "fragment.glsl" |
enum REN_ERR { |
||||||
#define PACKED __attribute__((packed)) |
REN_SUCCESS = 0, |
||||||
|
REN_ERRNO, |
||||||
|
REN_INVAL, |
||||||
|
REN_VERTEX, |
||||||
|
REN_FRAGMENT, |
||||||
|
REN_PROGRAM, |
||||||
|
REN_COMPILE, |
||||||
|
REN_LINK, |
||||||
|
REN_TEXTURE, |
||||||
|
REN_CONTEXT, |
||||||
|
REN_GLEW, |
||||||
|
REN_FONT, |
||||||
|
REN_BUFFER, |
||||||
|
}; |
||||||
|
|
||||||
|
|
||||||
|
// TODO: make a macro for enum-associated string arrays
|
||||||
|
const char * ren_err_msg[] = { |
||||||
|
[REN_SUCCESS] = "Success", |
||||||
|
[REN_ERRNO] = "Look at errno", |
||||||
|
[REN_INVAL] = "Invalid or NULL arguments", |
||||||
|
[REN_VERTEX] = "Failed to create opengl vertex shader", |
||||||
|
[REN_FRAGMENT] = "Failed to create opengl fragment shader", |
||||||
|
[REN_PROGRAM] = "Failed to create opengl program", |
||||||
|
[REN_COMPILE] = "Failed to compile shaders", |
||||||
|
[REN_LINK] = "Failed to link shaders", |
||||||
|
[REN_TEXTURE] = "Failed to create texture", |
||||||
|
[REN_CONTEXT] = "Failed to create SDL OpenGL context", |
||||||
|
[REN_GLEW] = "GLEW Error", |
||||||
|
[REN_FONT] = "Font Error", |
||||||
|
[REN_BUFFER] = "Failed to create opengl buffer", |
||||||
|
}; |
||||||
|
|
||||||
|
// different stacks
|
||||||
|
#include "stack.h" |
||||||
|
STACK_DECL(vtstack, struct v_text) |
||||||
|
STACK_DECL(vcstack, struct v_col) |
||||||
|
|
||||||
|
|
||||||
struct { |
struct { |
||||||
SDL_Window *w; |
|
||||||
SDL_GLContext *gl; |
SDL_GLContext *gl; |
||||||
GLuint gl_vertbuffer; |
struct font_atlas *font; |
||||||
GLuint gl_program; |
|
||||||
GLuint font_texture; |
GLuint font_texture; |
||||||
|
GLuint font_prog; |
||||||
|
GLuint box_prog; |
||||||
|
GLuint font_buffer; |
||||||
|
struct vtstack font_stack; |
||||||
|
struct vcstack box_stack; |
||||||
} ren = {0}; |
} ren = {0}; |
||||||
|
|
||||||
|
|
||||||
typedef struct PACKED { |
static int ren_errno; |
||||||
union { GLint x, u; }; |
|
||||||
union { GLint y, v; }; |
|
||||||
} vec2_i; |
|
||||||
|
|
||||||
typedef struct PACKED { |
|
||||||
union { GLint x, r; }; |
|
||||||
union { GLint y, g; }; |
|
||||||
union { GLint z, b; }; |
|
||||||
union { GLint w, a; }; |
|
||||||
} vec4_i; |
|
||||||
|
|
||||||
// textured vertex
|
// print shader compilation errors
|
||||||
struct PACKED v_text { |
static int shader_compile_error(GLuint shader) |
||||||
vec2_i pos; |
{ |
||||||
vec2_i uv; |
GLint status; |
||||||
}; |
glGetShaderiv(shader, GL_COMPILE_STATUS, &status); |
||||||
|
if (status != GL_FALSE) |
||||||
|
return 0; |
||||||
|
|
||||||
// colored vertex
|
GLint log_length; |
||||||
struct PACKED v_col { |
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &log_length); |
||||||
vec2_i pos; |
|
||||||
vec2_i col; |
|
||||||
}; |
|
||||||
|
|
||||||
|
GLchar *log_str = emalloc((log_length + 1)*sizeof(GLchar)); |
||||||
|
glGetShaderInfoLog(shader, log_length, NULL, log_str); |
||||||
|
|
||||||
// different stacks
|
const char *shader_type_str = NULL; |
||||||
#define _STACK_NAME vtstack |
GLint shader_type; |
||||||
#define _STACK_TYPE struct v_text |
glGetShaderiv(shader, GL_SHADER_TYPE, &shader_type); |
||||||
#include "stack.h" |
switch(shader_type) { |
||||||
#undef _STACK_NAME |
case GL_VERTEX_SHADER: shader_type_str = "vertex"; break; |
||||||
#undef _STACK_TYPE |
case GL_GEOMETRY_SHADER: shader_type_str = "geometry"; break; |
||||||
#define _STACK_NAME vcstack |
case GL_FRAGMENT_SHADER: shader_type_str = "fragment"; break; |
||||||
#define _STACK_TYPE struct v_col |
} |
||||||
#include "stack.h" |
|
||||||
|
fprintf(stderr, "Compile failure in %s shader:\n%s\n", shader_type_str, log_str); |
||||||
|
efree(log_str); |
||||||
|
return -1; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
// print shader link errors
|
||||||
|
static int shader_link_error(GLuint prog) |
||||||
|
{ |
||||||
|
GLint status; |
||||||
|
glGetProgramiv (prog, GL_LINK_STATUS, &status); |
||||||
|
if (status != GL_FALSE) |
||||||
|
return 0; |
||||||
|
|
||||||
|
GLint log_length; |
||||||
|
glGetProgramiv(prog, GL_INFO_LOG_LENGTH, &log_length); |
||||||
|
|
||||||
|
GLchar *log_str = emalloc((log_length + 1)*sizeof(GLchar)); |
||||||
|
glGetProgramInfoLog(prog, log_length, NULL, log_str); |
||||||
|
fprintf(stderr, "Linker failure: %s\n", log_str); |
||||||
|
efree(log_str); |
||||||
|
|
||||||
|
return -1; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
#define REN_RET(a,b) {ren_errno = b; return a;} |
||||||
|
static GLuint shader_compile(const char *shader, GLuint type) |
||||||
|
{ |
||||||
|
GLuint s; |
||||||
|
// initialize the vertex shader and get the corresponding id
|
||||||
|
s = glCreateShader(type); |
||||||
|
if (!s) REN_RET(0, REN_VERTEX) |
||||||
|
// get the shader into opengl
|
||||||
|
glShaderSource(s, 1, &shader, NULL); |
||||||
|
glCompileShader(s); |
||||||
|
if (shader_compile_error(s)) |
||||||
|
REN_RET(0, REN_COMPILE) |
||||||
|
return s; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
const char * ren_strerror(void) |
||||||
|
{ |
||||||
|
return ren_err_msg[ren_errno % (sizeof(ren_err_msg)/sizeof(char *))]; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
// compile a vertex shader (vs_path) and a fragment shader (fs_path) into an opengl
|
||||||
|
// program and return it's index
|
||||||
|
static GLuint ren_compile_program(const char *vs_path, const char *fs_path) |
||||||
|
{ |
||||||
|
GLuint gl_vertshader, gl_fragshader, prog; |
||||||
|
|
||||||
|
if (!vs_path || !fs_path) |
||||||
|
REN_RET(0, REN_INVAL) |
||||||
|
|
||||||
|
char *str = NULL; |
||||||
|
|
||||||
|
dump_file(vs_path, &str, NULL); |
||||||
|
if (!str) REN_RET(0, REN_ERRNO) |
||||||
|
gl_vertshader = shader_compile(str, GL_VERTEX_SHADER); |
||||||
|
efree(str); |
||||||
|
if (!gl_vertshader) |
||||||
|
return 0; |
||||||
|
|
||||||
|
dump_file(fs_path, &str, NULL); |
||||||
|
if (!str) REN_RET(0, REN_ERRNO) |
||||||
|
gl_fragshader = shader_compile(str, GL_FRAGMENT_SHADER); |
||||||
|
efree(str); |
||||||
|
if (!gl_fragshader) |
||||||
|
return 0; |
||||||
|
|
||||||
|
|
||||||
|
// create the main program object, it is an amalgamation of all shaders
|
||||||
|
prog = glCreateProgram(); |
||||||
|
if (!prog) REN_RET(0, REN_PROGRAM) |
||||||
|
|
||||||
|
// attach the shaders to the program (set which shaders are present)
|
||||||
|
glAttachShader(prog, gl_vertshader); |
||||||
|
glAttachShader(prog, gl_fragshader); |
||||||
|
// then link the program (basically the linking stage of the program)
|
||||||
|
glLinkProgram(prog); |
||||||
|
if (shader_link_error(prog)) |
||||||
|
REN_RET(0, REN_LINK) |
||||||
|
|
||||||
|
// after linking the shaders can be detached and the source freed from
|
||||||
|
// memory since the program is ready to use
|
||||||
|
glDetachShader(prog, gl_vertshader); |
||||||
|
glDetachShader(prog, gl_fragshader); |
||||||
|
|
||||||
|
return prog; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
static GLuint ren_texturergb_2d(const char *buf, int w, int h, int upscale, int downscale) |
||||||
|
{ |
||||||
|
GLuint t; |
||||||
|
|
||||||
|
if (!buf || w <= 0 || h <= 0) |
||||||
|
REN_RET(0, REN_INVAL) |
||||||
|
|
||||||
|
glGenTextures(1, &t); |
||||||
|
if (!t) REN_RET(0, REN_TEXTURE) |
||||||
|
|
||||||
|
glBindTexture(GL_TEXTURE_2D, t); |
||||||
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, w, h, 0, GL_RGB, GL_UNSIGNED_BYTE, buf); |
||||||
|
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); |
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); |
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, downscale); |
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, upscale); |
||||||
|
|
||||||
|
return t; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
static GLuint ren_texturergba_2d(const char *buf, int w, int h, int upscale, int downscale) |
||||||
|
{ |
||||||
|
GLuint t; |
||||||
|
|
||||||
|
if (!buf || w <= 0 || h <= 0) |
||||||
|
REN_RET(0, REN_INVAL) |
||||||
|
|
||||||
|
glGenTextures(1, &t); |
||||||
|
if (!t) REN_RET(0, REN_TEXTURE) |
||||||
|
|
||||||
|
glBindTexture(GL_TEXTURE_2D, t); |
||||||
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, buf); |
||||||
|
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); |
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); |
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, downscale); |
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, upscale); |
||||||
|
|
||||||
|
return t; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
// FIXME: update only the newly generated character instead of the whole texture
|
||||||
|
static int update_font_texture(void) |
||||||
|
{ |
||||||
|
glTexSubImage2D( |
||||||
|
ren.font_texture, |
||||||
|
0, 0, 0, |
||||||
|
ren.font->width, |
||||||
|
ren.font->height, |
||||||
|
GL_RGB, |
||||||
|
GL_UNSIGNED_BYTE, |
||||||
|
ren.font->atlas); |
||||||
|
font_dump(ren.font, "./atlas.png"); |
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
// TODO: push window size uniforms
|
||||||
|
int ren_init(SDL_Window *w) |
||||||
|
{ |
||||||
|
if (!w) |
||||||
|
REN_RET(-1, REN_INVAL) |
||||||
|
ren.gl = SDL_GL_CreateContext(w); |
||||||
|
if (!ren.gl) |
||||||
|
REN_RET(-1, REN_CONTEXT) |
||||||
|
|
||||||
|
// select some features
|
||||||
|
glEnable(GL_BLEND); |
||||||
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); |
||||||
|
glDisable(GL_CULL_FACE); |
||||||
|
glDisable(GL_DEPTH_TEST); |
||||||
|
glEnable(GL_SCISSOR_TEST); |
||||||
|
glEnable(GL_TEXTURE_2D); |
||||||
|
|
||||||
|
GLenum glew_err = glewInit(); |
||||||
|
if (glew_err != GLEW_OK) |
||||||
|
REN_RET(glew_err, REN_GLEW); |
||||||
|
|
||||||
|
ren.font = font_init(); |
||||||
|
if (!ren.font) REN_RET(-1, REN_FONT)
|
||||||
|
if (font_load(ren.font, FONT_PATH)) REN_RET(-1, REN_FONT) |
||||||
|
|
||||||
|
ren.font_texture = ren_texturergb_2d( |
||||||
|
(const char *)ren.font->atlas, |
||||||
|
ren.font->width, |
||||||
|
ren.font->height, |
||||||
|
GL_LINEAR, GL_LINEAR); |
||||||
|
if (!ren.font_texture) return -1; |
||||||
|
|
||||||
|
ren.font_prog = ren_compile_program(FONT_VERSHADER, FONT_FRAGSHADER); |
||||||
|
if (!ren.font_prog) return -1; |
||||||
|
|
||||||
|
ren.font_stack = vtstack_init(); |
||||||
|
ren.box_stack = vcstack_init();
|
||||||
|
|
||||||
|
glGenBuffers(1, &ren.font_buffer); |
||||||
|
if (!ren.font_buffer) REN_RET(-1, REN_BUFFER) |
||||||
|
glBindBuffer(GL_ARRAY_BUFFER, ren.font_buffer); |
||||||
|
glEnableVertexAttribArray(REN_VERTEX_IDX); |
||||||
|
glVertexAttribPointer( |
||||||
|
REN_VERTEX_IDX, |
||||||
|
2, |
||||||
|
GL_INT, |
||||||
|
GL_FALSE, |
||||||
|
sizeof(struct v_text), |
||||||
|
0); |
||||||
|
glEnableVertexAttribArray(REN_UV_IDX); |
||||||
|
glVertexAttribPointer( |
||||||
|
REN_UV_IDX, |
||||||
|
2, |
||||||
|
GL_INT, |
||||||
|
GL_FALSE, |
||||||
|
sizeof(struct v_text), |
||||||
|
(void*)sizeof(vec2_i)); |
||||||
|
|
||||||
|
glBindBuffer(GL_ARRAY_BUFFER, 0); |
||||||
|
|
||||||
|
int width, height; |
||||||
|
SDL_GetWindowSize(w, &width, &height); |
||||||
|
ren_update_viewport(width, height); |
||||||
|
glClearColor(0.3f, 0.3f, 0.3f, 0.f); |
||||||
|
glClear(GL_COLOR_BUFFER_BIT); |
||||||
|
|
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
static int ren_draw_font_stack(void) |
||||||
|
{ |
||||||
|
glUseProgram(ren.font_prog); |
||||||
|
glBindBuffer(GL_ARRAY_BUFFER, ren.font_buffer); |
||||||
|
|
||||||
|
// TODO: implement size and damage tracking on stacks
|
||||||
|
glBufferData( |
||||||
|
GL_ARRAY_BUFFER, |
||||||
|
ren.font_stack.idx*sizeof(struct v_text), |
||||||
|
ren.font_stack.items, |
||||||
|
GL_DYNAMIC_DRAW); |
||||||
|
glDrawArrays(GL_TRIANGLES, 0, ren.font_stack.idx); |
||||||
|
|
||||||
|
glBindBuffer(GL_ARRAY_BUFFER, 0); |
||||||
|
glUseProgram(0); |
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
int ren_update_viewport(int w, int h) |
||||||
|
{ |
||||||
|
glViewport(0, 0, w, h); |
||||||
|
glUniform2i(REN_SCREENSIZE_LOC, w, h); |
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
// TODO: implement font size
|
||||||
|
int ren_render_text(const char *str, int x, int y, int w, int h, int size) |
||||||
|
{ |
||||||
|
// x4,y4 x3,y3
|
||||||
|
// +-------------+
|
||||||
|
// |(x,y) /|
|
||||||
|
// | / |
|
||||||
|
// | 2 / |
|
||||||
|
// | / |
|
||||||
|
// | / |
|
||||||
|
// | / 1 |
|
||||||
|
// |/ |
|
||||||
|
// +-------------+
|
||||||
|
// x1,y1 x2,y2
|
||||||
|
|
||||||
|
const struct font_glyph *g; |
||||||
|
size_t ret, off; |
||||||
|
uint_least32_t cp; |
||||||
|
int updated, gx = x, gy = y; |
||||||
|
struct v_text v; |
||||||
|
for (off = 0; (ret = grapheme_decode_utf8(str+off, SIZE_MAX, &cp)) > 0 && cp != 0; off += ret) { |
||||||
|
g = font_get_glyph_texture(ren.font, cp, &updated); |
||||||
|
if (!g) REN_RET(-1, REN_FONT); |
||||||
|
if (updated) { |
||||||
|
if (update_font_texture()) |
||||||
|
REN_RET(-1, REN_TEXTURE); |
||||||
|
} |
||||||
|
|
||||||
|
// x1,y1
|
||||||
|
v = (struct v_text){ |
||||||
|
.pos = { .x = gx, .y = gy+g->h }, |
||||||
|
.uv = { .u = g->u, .v = g->v+g->h }, |
||||||
|
}; |
||||||
|
vtstack_push(&ren.font_stack, &v); |
||||||
|
// x2,y2
|
||||||
|
v = (struct v_text){ |
||||||
|
.pos = { .x = gx+g->w, .y = gy+g->h }, |
||||||
|
.uv = { .u = g->u+g->w, .v = g->v+g->h }, |
||||||
|
}; |
||||||
|
vtstack_push(&ren.font_stack, &v); |
||||||
|
// x3,y3
|
||||||
|
v = (struct v_text){ |
||||||
|
.pos = { .x = gx+g->w, .y = gy }, |
||||||
|
.uv = { .u = g->u+g->w, .v = g->v }, |
||||||
|
}; |
||||||
|
vtstack_push(&ren.font_stack, &v); |
||||||
|
// x4,y4
|
||||||
|
v = (struct v_text){ |
||||||
|
.pos = { .x = gx, .y = gy }, |
||||||
|
.uv = { .u = g->u, .v = g->v }, |
||||||
|
}; |
||||||
|
vtstack_push(&ren.font_stack, &v); |
||||||
|
|
||||||
|
// TODO: possible kerning needs to be applied here
|
||||||
|
gx += g->w; |
||||||
|
if (cp == '\n') |
||||||
|
gy += 20; // FIXME: encode and/or store line height
|
||||||
|
} |
||||||
|
|
||||||
|
// FIXME: here we are doing one draw call for string of text which is
|
||||||
|
// inefficient but is simpler and allows for individual scissors
|
||||||
|
glScissor(x, y, w, h); |
||||||
|
ren_draw_font_stack(); |
||||||
|
vtstack_clear(&ren.font_stack); |
||||||
|
|
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
int ren_free(void) |
||||||
|
{ |
||||||
|
glUseProgram(0); |
||||||
|
glBindBuffer(GL_ARRAY_BUFFER, 0); |
||||||
|
glDeleteTextures(1, &ren.font_texture); |
||||||
|
glDeleteBuffers(1, &ren.font_buffer); |
||||||
|
SDL_GL_DeleteContext(ren.gl); |
||||||
|
vtstack_free(&ren.font_stack); |
||||||
|
vcstack_free(&ren.box_stack); |
||||||
|
font_free(ren.font); |
||||||
|
return 0; |
||||||
|
} |
||||||
|
@ -0,0 +1,48 @@ |
|||||||
|
#ifndef _RENDERER_H |
||||||
|
#define _RENDERER_H |
||||||
|
|
||||||
|
#include <SDL2/SDL.h> |
||||||
|
|
||||||
|
|
||||||
|
#define FONT_PATH "./monospace.ttf" |
||||||
|
#define FONT_VERSHADER "./font_vertshader.glsl" |
||||||
|
#define FONT_FRAGSHADER "./font_fragshader.glsl" |
||||||
|
#define REN_VERTEX_IDX 0 |
||||||
|
#define REN_UV_IDX 1 |
||||||
|
#define REN_COLOR_IDX 2 |
||||||
|
#define REN_SCREENSIZE_LOC 0 |
||||||
|
|
||||||
|
|
||||||
|
typedef struct { |
||||||
|
union { int x, u; }; |
||||||
|
union { int y, v; }; |
||||||
|
} vec2_i; |
||||||
|
|
||||||
|
typedef struct { |
||||||
|
union { int x, r; }; |
||||||
|
union { int y, g; }; |
||||||
|
union { int z, b; }; |
||||||
|
union { int w, a; }; |
||||||
|
} vec4_i; |
||||||
|
|
||||||
|
// textured vertex
|
||||||
|
struct v_text { |
||||||
|
vec2_i pos; |
||||||
|
vec2_i uv; |
||||||
|
}; |
||||||
|
|
||||||
|
// colored vertex
|
||||||
|
struct v_col { |
||||||
|
vec2_i pos; |
||||||
|
vec2_i col; |
||||||
|
}; |
||||||
|
|
||||||
|
|
||||||
|
int ren_init(SDL_Window *sdl_window); |
||||||
|
int ren_free(void); |
||||||
|
const char * ren_strerror(void); |
||||||
|
int ren_update_viewport(int w, int h); |
||||||
|
int ren_render_text(const char *str, int x, int y, int w, int h, int size); |
||||||
|
|
||||||
|
|
||||||
|
#endif |
@ -1,61 +1,72 @@ |
|||||||
#ifdef _STACK_TYPE |
#ifndef _STACK_GENERIC_H |
||||||
#ifdef _STACK_NAME |
#define _STACK_GENERIC_H |
||||||
|
|
||||||
|
|
||||||
#ifndef _STACK_REALLOC |
#define STACK_STEP 8 |
||||||
#define _STACK_REALLOC(p, s) realloc(p,s) |
|
||||||
#endif |
|
||||||
#ifndef _STACK_MEMSET |
|
||||||
#define _STACK_MEMSET(p, c, s) memset(p,c,s) |
|
||||||
#endif |
|
||||||
#ifndef _STACK_STEP |
|
||||||
#define _STACK_STEP 8 |
|
||||||
#endif |
|
||||||
#ifndef _STACK_CAT |
|
||||||
#define _STACK_CAT_I(a,b) a ## b |
|
||||||
#define _STACK_CAT(a,b) _STACK_CAT_I(a,b) |
|
||||||
#endif |
|
||||||
|
|
||||||
|
|
||||||
// TODO: add a rolling hash
|
// TODO: add a rolling hash
|
||||||
struct _STACK_NAME { |
#define STACK_DECL(stackname, type) \ |
||||||
_STACK_TYPE *items; |
struct stackname { \
|
||||||
int size, idx; |
type *items; \
|
||||||
}; |
int size, idx; \
|
||||||
|
}; \
|
||||||
|
\
|
||||||
int _STACK_CAT(stack_grow_,_STACK_NAME)(struct _STACK_NAME *stack, int step) |
\
|
||||||
{ |
struct stackname stackname##_init(void) { return (struct stackname){0}; } \
|
||||||
if (!stack) |
\
|
||||||
return -1; |
\
|
||||||
stack->items = _STACK_REALLOC(stack->items, (stack->size+step)*sizeof(_STACK_TYPE)); |
int stackname##_grow(struct stackname *stack, int step) \
|
||||||
if(!stack->items) |
{ \
|
||||||
return -1; |
if (!stack) \
|
||||||
_STACK_MEMSET(&(stack->items[stack->size]), 0, step*sizeof(*(stack->items))); |
return -1; \
|
||||||
stack->size += step; |
stack->items = realloc(stack->items, (stack->size+step)*sizeof(type)); \
|
||||||
return 0; |
if(!stack->items) \
|
||||||
} |
return -1; \
|
||||||
|
memset(&(stack->items[stack->size]), 0, step*sizeof(*(stack->items))); \
|
||||||
|
stack->size += step; \
|
||||||
|
return 0; \
|
||||||
|
} \
|
||||||
|
\
|
||||||
|
\
|
||||||
|
int stackname##_push(struct stackname *stack, type *e) \
|
||||||
|
{ \
|
||||||
|
if (!stack || !e) \
|
||||||
|
return -1; \
|
||||||
|
if (stack->idx >= stack->size) \
|
||||||
|
if (stackname##_grow(stack, STACK_STEP)) \
|
||||||
|
return -1; \
|
||||||
|
stack->items[stack->idx++] = *e; \
|
||||||
|
return 0; \
|
||||||
|
} \
|
||||||
|
\
|
||||||
|
\
|
||||||
|
type stackname##_pop(struct stackname *stack) \
|
||||||
|
{ \
|
||||||
|
if (!stack || stack->idx == 0 || stack->size == 0) \
|
||||||
|
return (type){0}; \
|
||||||
|
return stack->items[stack->idx--]; \
|
||||||
|
} \
|
||||||
|
\
|
||||||
|
\
|
||||||
|
int stackname##_clear(struct stackname *stack) \
|
||||||
|
{ \
|
||||||
|
if (!stack) \
|
||||||
|
return -1; \
|
||||||
|
stack->idx = 0; \
|
||||||
|
return 0; \
|
||||||
|
} \
|
||||||
|
\
|
||||||
|
\
|
||||||
|
int stackname##_free(struct stackname *stack) \
|
||||||
|
{ \
|
||||||
|
if (stack) { \
|
||||||
|
stackname##_clear(stack); \
|
||||||
|
if (stack->items) \
|
||||||
|
free(stack->items); \
|
||||||
|
} \
|
||||||
|
return 0; \
|
||||||
|
} \
|
||||||
|
|
||||||
|
|
||||||
int _STACK_CAT(stack_push_,_STACK_NAME)(struct _STACK_NAME *stack, _STACK_TYPE *e) |
|
||||||
{ |
|
||||||
if (!stack || !e) |
|
||||||
return -1; |
|
||||||
if (stack->idx >= stack->size) |
|
||||||
if (_STACK_CAT(stack_grow_,_STACK_NAME)(stack, _STACK_STEP)) |
|
||||||
return -1; |
|
||||||
stack->items[stack->idx++] = *e; |
|
||||||
return 0; |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
int _STACK_CAT(stack_clear_,_STACK_NAME)(struct _STACK_NAME *stack) |
|
||||||
{ |
|
||||||
if (!stack) |
|
||||||
return -1; |
|
||||||
stack->idx = 0; |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
#endif |
|
||||||
#endif |
#endif |
||||||
|
Binary file not shown.
Loading…
Reference in new issue