From ff80a16c835f112f544a9fb9dae288131726b14c Mon Sep 17 00:00:00 2001 From: darkrose Date: Wed, 2 Mar 2016 19:35:03 +1000 Subject: [PATCH] font rendering... mostly --- Makefile.linux | 4 +- Makefile.sources | 2 +- data/shaders/ui_fragment.glsl | 4 +- data/shaders/ui_vertex.glsl | 6 +- inc/graphics.h | 98 +++++++++++++- src/client/kmap.c | 5 +- src/client/main.c | 23 +++- src/graphics/font.c | 127 ++++++++++++++++++ src/graphics/font_ttf.c | 299 ++++++++++++++++++++++++++++++++++++++++++ src/graphics/image.c | 36 +++++ src/graphics/render2d.c | 165 +++++++++++++++++++---- src/graphics/render3d.c | 2 +- src/graphics/textbuffer.c | 221 +++++++++++++++++++++++++++++++ src/graphics/texture.c | 23 +--- src/lib/array.c | 16 +++ 15 files changed, 974 insertions(+), 57 deletions(-) create mode 100644 src/graphics/font.c create mode 100644 src/graphics/font_ttf.c create mode 100644 src/graphics/textbuffer.c diff --git a/Makefile.linux b/Makefile.linux index 4e40c0b..f6a3430 100644 --- a/Makefile.linux +++ b/Makefile.linux @@ -6,7 +6,7 @@ TARGET_LDFLAGS ?= -g $(LDFLAGS) SERVER_CLIBS=$(TARGET_CLIBS) -CLIENT_CLIBS=$(TARGET_CLIBS) -lGL -lXxf86vm -lX11 -lpng +CLIENT_CLIBS=$(TARGET_CLIBS) -lGL -lXxf86vm -lX11 -lpng $(shell freetype-config --libs) PREFIX ?= /usr @@ -28,5 +28,7 @@ uninstall: package: @echo "no package support" +$(GLDIR)/font_ttf.o: $(GLDIR)/font_ttf.c + $(CC) $(TARGET_CFLAGS) $(shell freetype-config --cflags) $(CFLAG_DEFS) -o $@ -c $< .PHONY: install uninstall package diff --git a/Makefile.sources b/Makefile.sources index 92cab63..f9ead70 100644 --- a/Makefile.sources +++ b/Makefile.sources @@ -21,7 +21,7 @@ OBJS_CONTENT=$(CONTENTDIR)/content.o $(CONTENTDIR)/content_block.o $(CONTENTDIR) OBJS_MAP= -OBJS_GRAPHICS=$(GLDIR)/camera.o $(GLDIR)/image.o $(GLDIR)/image_bmp.o $(GLDIR)/image_png.o $(GLDIR)/image_tga.o $(GLDIR)/lighting.o $(GLDIR)/material.o $(GLDIR)/mesh.o $(GLDIR)/object.o $(GLDIR)/opengl.o $(GLDIR)/render.o $(GLDIR)/render2d.o $(GLDIR)/render3d.o $(GLDIR)/shader.o $(GLDIR)/texture.o $(GLDIR)/wm.o $(GLDIR)/wm_x11.o $(GLDIR)/wm_w32.o +OBJS_GRAPHICS=$(GLDIR)/camera.o $(GLDIR)/font.o $(GLDIR)/font_ttf.o $(GLDIR)/image.o $(GLDIR)/image_bmp.o $(GLDIR)/image_png.o $(GLDIR)/image_tga.o $(GLDIR)/lighting.o $(GLDIR)/material.o $(GLDIR)/mesh.o $(GLDIR)/object.o $(GLDIR)/opengl.o $(GLDIR)/render.o $(GLDIR)/render2d.o $(GLDIR)/render3d.o $(GLDIR)/shader.o $(GLDIR)/textbuffer.o $(GLDIR)/texture.o $(GLDIR)/wm.o $(GLDIR)/wm_x11.o $(GLDIR)/wm_w32.o OBJS_UI= diff --git a/data/shaders/ui_fragment.glsl b/data/shaders/ui_fragment.glsl index 72e8ac7..5778706 100644 --- a/data/shaders/ui_fragment.glsl +++ b/data/shaders/ui_fragment.glsl @@ -1,6 +1,6 @@ #version 330 core -in vec2 uvs; +in vec2 uv; out vec4 out_Color; @@ -8,6 +8,6 @@ uniform sampler2D tex; void main(void) { - out_Color = texture(tex,uvs); + out_Color = texture(tex,uv); } diff --git a/data/shaders/ui_vertex.glsl b/data/shaders/ui_vertex.glsl index 8ac8e3e..f9a26d0 100644 --- a/data/shaders/ui_vertex.glsl +++ b/data/shaders/ui_vertex.glsl @@ -1,13 +1,15 @@ #version 330 core in vec2 position; +in vec2 uvs; -out vec2 uvs; +out vec2 uv; uniform mat4 transformationMatrix; void main(void) { gl_Position = transformationMatrix*vec4(position, 0.0, 1.0); - uvs = vec2((position.x+1.0)/2.0, 1 - (position.y+1.0)/2.0); + + uv = uvs; } diff --git a/inc/graphics.h b/inc/graphics.h index 4bea695..9810832 100644 --- a/inc/graphics.h +++ b/inc/graphics.h @@ -37,6 +37,19 @@ #define RD2_QUAD 0x02 #define RD2_TEXT 0x03 +#define UI_ALIGN_LEFT 0 +#define UI_ALIGN_CENTRE -1 +#define UI_ALIGN_RIGHT -2 +#define UI_ALIGN_TOP 0 +#define UI_ALIGN_MIDDLE -1 +#define UI_ALIGN_BOTTOM -2 +#define UI_ALIGN_MOUSE -3 +#define UI_ALIGN_MOUSE_CENTRE -4 +#define UI_DEFAULT -5 + +#define TB_OPT_NONE 0x00 +#define TB_OPT_NUMERIC 0x01 + #ifndef _HAVE_FCOLOUR_TYPE #define _HAVE_FCOLOUR_TYPE typedef struct fcolour_s { @@ -230,10 +243,73 @@ typedef struct shader_s { } shader_t; #endif +#ifndef _HAVE_FONTCHAR_TYPE +#define _HAVE_FONTCHAR_TYPE +typedef struct fontchar_s { + float w; + float h; + float a; + float x[2]; + float y[2]; +} fontchar_t; +#endif + +#ifndef _HAVE_FONTPAGE_TYPE +#define _HAVE_FONTPAGE_TYPE +typedef struct fontpage_s { + GLubyte *pixels; + GLuint glid; + array_t sizes; +} fontpage_t; +#endif + +#ifndef _HAVE_FONT_TYPE +#define _HAVE_FONT_TYPE +typedef struct font_s { + char* token; + char* file; + array_t pages; +} font_t; +#endif + +#ifndef _HAVE_RENDERTEXT_TYPE +#define _HAVE_RENDERTEXT_TYPE +typedef struct rendertext_s { + uint32_t page; + uint32_t state; + GLuint glid; + GLuint vao; + GLuint vbo[2]; + array_t v; + array_t t; +} rendertext_t; +#endif + +#ifndef _HAVE_TEXTBUFFER_TYPE +#define _HAVE_TEXTBUFFER_TYPE +typedef struct textbuffer_s { + uint32_t* str; + uint32_t length; + uint32_t size; + int mx; + int my; + int mc; + float x; + float y; + float nx; + float ny; + font_t *font; + int font_size; + uint32_t options; + array_t data; +} textbuffer_t; +#endif + /* defined in image.c */ image_t *image_load(char* type, char* file); image_t *image_load_frommem(file_t *f); image_t *image_load_fromscreen(int x, int y, int w, int h, int alpha); +image_t *image_rgba(int x, int y, uint8_t r, uint8_t g, uint8_t b, uint8_t a); image_t *image_copy(image_t *img); void image_clear(image_t *img); void image_draw_rect(image_t *img, colour_t *c, int width, rect_t *area); @@ -327,7 +403,8 @@ float client_dtime(void); /* defined in render2d.c */ void render2d_line(material_t *m, float x, float y, float ex, float ey); void render2d_quad_mat(material_t *m, float x, float y, float w, float h); -void render2d_text(float x, float y, int font, int size, char* str); +void render2d_text(float x, float y, int w, int h, int font, int size, char* str); +void render2d_textbuffer(textbuffer_t *t); void render2d(void); /* defined in render3d.c */ @@ -352,4 +429,23 @@ int shader_uniform_matrix(shader_t *s, char* name, matrix_t *value); int shader_enable(shader_t *s); int shader_disable(shader_t *s); +/* defined in font.c */ +int font_load(char* file, char* token); +font_t *font_get(int id); +font_t *font_find(char* token); +int font_get_id(char* token); + +/* defined in font_ttf.c */ +int font_load_ttf(font_t *f, char* file); +int font_ttf_get_char(font_t *f, uint32_t ch, fontchar_t **fc, GLuint *glid); + +/* defined in textbuffer.c */ +void textbuffer_init(textbuffer_t *t, font_t *f, int size, float x, float y, int max_x, int max_y, int max_c, uint32_t options); +textbuffer_t *textbuffer_create(font_t *f, int size, float x, float y, int max_x, int max_y, int max_c, uint32_t options); +void textbuffer_clear(textbuffer_t *t); +void textbuffer_free(textbuffer_t *t); +int textbuffer_adjust(textbuffer_t *t, font_t *f, int size, float x, float y, int max_x, int max_y, int max_c, uint32_t options); +int textbuffer_addstr(textbuffer_t *t, char* str); +int textbuffer_addchar(textbuffer_t *t, uint32_t ch); + #endif diff --git a/src/client/kmap.c b/src/client/kmap.c index 0581643..b3fbd5a 100644 --- a/src/client/kmap.c +++ b/src/client/kmap.c @@ -91,6 +91,7 @@ static uint32_t kmap_strtosym(char* str, sym_t *sym) }else if (!strcmp(str,"tab")) { sym->type = SYM_TYPE_SKEY; sym->sym = SYM_KEY_TAB; + sym->ch = '\n'; return 0; }else if (!strcmp(str,"up")) { sym->type = SYM_TYPE_SKEY; @@ -110,6 +111,7 @@ static uint32_t kmap_strtosym(char* str, sym_t *sym) return 0; }else if (!strncmp(str,"kp",2)) { sym->type = SYM_TYPE_SKEY; + sym->ch = str[2]; switch (str[2]) { case '0': sym->sym = SYM_KEY_KP0; @@ -141,7 +143,8 @@ static uint32_t kmap_strtosym(char* str, sym_t *sym) case '9': sym->sym = SYM_KEY_KP9; break; - default:; + default: + sym->ch = 0; } }else if (str[0] == 'f' || str[0] == 'F') { int i = strtol(str+1,NULL,10); diff --git a/src/client/main.c b/src/client/main.c index 26a86a8..cd2b8b5 100644 --- a/src/client/main.c +++ b/src/client/main.c @@ -45,6 +45,11 @@ int main(int argc, char** argv) object_t *block2; v3_t p; float r; + textbuffer_t tb; + textbuffer_t tb2; + font_t *f; + int i; + char buff[256]; command_init(); path_init(); @@ -59,7 +64,15 @@ int main(int argc, char** argv) wm_init(); wm_title(PACKAGE " - " VERSION); - vlprintf("hello world"); + font_load("font.ttf","default"); + + f = font_find("default"); + + textbuffer_init(&tb,f,14,20,20,0,0,0,0); + textbuffer_init(&tb2,f,14,20,40,0,0,0,0); + i = textbuffer_addstr(&tb,"Hello world! - ößäндгя ygj"); + + vlprintf("hello world %d",i); logo = mat_from_image("texture","menulogo.png"); steel = mat_from_image("texture","steel_block.png"); @@ -88,6 +101,14 @@ int main(int argc, char** argv) object_rotate(block1,20.0,r,-r); object_rotate(block2,20.0,-r,r); + textbuffer_clear(&tb2); + textbuffer_init(&tb2,f,14,20,40,0,0,0,0); + sprintf(buff,"FPS: %d",wm_data.fps); + textbuffer_addstr(&tb2,buff); + + render2d_textbuffer(&tb); + render2d_textbuffer(&tb2); + render2d_quad_mat(logo,412,150,200,200); render_post(); diff --git a/src/graphics/font.c b/src/graphics/font.c new file mode 100644 index 0000000..35ea9ac --- /dev/null +++ b/src/graphics/font.c @@ -0,0 +1,127 @@ +/************************************************************************ +* font.c +* voxelands - 3d voxel world sandbox game +* Copyright (C) Lisa 'darkrose' Milne 2016 +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +* See the GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see +************************************************************************/ + +#include "common.h" +#include "path.h" +#define _WM_EXPOSE_ALL +#include "wm.h" +#include "graphics.h" +#include "array.h" + +#include + +array_t *fonts = NULL; + +/* load a font file */ +int font_load(char* file, char* token) +{ + char* t; + font_t *f; + int r = 1; + if (!fonts) + fonts = array_create(ARRAY_TYPE_PTR); + + if (!file || !token) + return 0; + + t = strrchr(file,'.'); + if (!t) + return 0; + + f = malloc(sizeof(font_t)); + f->token = strdup(token); + + t++; + + if (!strcmp(t,"ttf")) { + r = font_load_ttf(f,file); + }else{ + vlprintf(CN_ERROR "Unsupported Font: %s",file); + } + + if (r) { + free(f->token); + free(f); + return 0; + } + + array_push_ptr(fonts,f); + + return fonts->length; +} + +/* get a font by id */ +font_t *font_get(int id) +{ + font_t *f; + if (!fonts) + font_load("font.ttf","default"); + if (!fonts) + return NULL; + + if (id == UI_DEFAULT) + id = 1; + + f = array_get_ptr(fonts,id-1); + if (!f) + f = array_get_ptr(fonts,0); + + return f; +} + +/* get a font by token */ +font_t *font_find(char* token) +{ + int i; + font_t **f; + if (!fonts) + font_load("font.ttf","default"); + if (!fonts) + return NULL; + + f = fonts->data; + for (i=0; ilength; i++) { + if (!strcmp(f[i]->token,token)) + return f[i]; + } + + return array_get_ptr(fonts,0); +} + +/* get the id of a font */ +int font_get_id(char* token) +{ + int i; + font_t **f; + if (!fonts) + font_load("font.ttf","default"); + if (!fonts) + return 0; + + f = fonts->data; + for (i=0; ilength; i++) { + if (!strcmp(f[i]->token,token)) + return i+1; + } + + if (!strcmp(token,"default")) + return 1; + + return 0; +} diff --git a/src/graphics/font_ttf.c b/src/graphics/font_ttf.c new file mode 100644 index 0000000..2d3ad90 --- /dev/null +++ b/src/graphics/font_ttf.c @@ -0,0 +1,299 @@ +/************************************************************************ +* font.c +* voxelands - 3d voxel world sandbox game +* Copyright (C) Lisa 'darkrose' Milne 2016 +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +* See the GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see +************************************************************************/ + +#include "common.h" +#include "path.h" +#define _WM_EXPOSE_ALL +#include "wm.h" +#include "graphics.h" + +#include +#include +#include FT_FREETYPE_H +#include FT_GLYPH_H +#include FT_OUTLINE_H +#include FT_TRIGONOMETRY_H + +static uint32_t font_ttf_get_page_size(FT_Face face, uint32_t *mw, uint32_t *mh, int32_t *bl, uint32_t i) +{ + uint32_t w = 0; + uint32_t h = 0; + uint32_t ch; + FT_Glyph glyph; + FT_BitmapGlyph bitmap_glyph; + + *mh = 0; + *mw = 0; + *bl = 0; + + for (ch=0; ch<256; ch++) { + if (FT_Load_Glyph(face,FT_Get_Char_Index(face,ch+(i*256)),FT_LOAD_DEFAULT)) + return 1; + + if (FT_Get_Glyph(face->glyph,&glyph)) + return 1; + + /* make a bitmap of the glyph */ + FT_Glyph_To_Bitmap(&glyph,ft_render_mode_normal,0,1); + bitmap_glyph = (FT_BitmapGlyph)glyph; + + w = bitmap_glyph->bitmap.width; + h = bitmap_glyph->bitmap.rows; + + if (h > *mh) + *mh = h; + if (w > *mw) + *mw = w; + if (bitmap_glyph->top > *bl) + *bl = bitmap_glyph->top; + } + + if (!(*mw) || !(*mh)) + return 0; + + *mw += 6; + *mh += 6; + + w = (*mw)*16; + h = (*mh)*16; + + if (w > h) + return math_next_pow2(w); + + return math_next_pow2(h); +} + +static int font_ttf_gen_fontpage(font_t *f, uint32_t i) +{ + FT_Face face; + FT_Library library; + FT_Glyph glyph; + FT_BitmapGlyph bitmap_glyph; + fontpage_t *p; + fontchar_t *character; + uint32_t s; + uint32_t h = 32<<6; + uint32_t ch; + uint32_t gw; + uint32_t gh; + int32_t bl; + uint32_t mh = 0; + uint32_t mw = 0; + uint32_t sx; + uint32_t sy; + uint32_t ax; + uint32_t ay; + int b = 0; + int t = 0; + int m = 0; + int j; + int k; + int l; + + p = array_get_ptr(&f->pages,i); + if (p) + return 0; + + /* connect to freetype */ + if (FT_Init_FreeType(&library)) + return 1; + + if (FT_New_Face(library,f->file,0,&face)) { + FT_Done_FreeType(library); + return 1; + } + + if (FT_Select_Charmap(face, FT_ENCODING_UNICODE)) { + FT_Done_Face(face); + FT_Done_FreeType(library); + return 1; + } + + /* something something characters something something */ + if (FT_Set_Char_Size(face,h,h,96,96)) { + FT_Done_Face(face); + FT_Done_FreeType(library); + return 1; + } + + s = font_ttf_get_page_size(face,&mw,&mh,&bl,i); + + if (!s) { + FT_Done_Face(face); + FT_Done_FreeType(library); + return 1; + } + + p = malloc(sizeof(fontpage_t)); + if (!p) { + FT_Done_Face(face); + FT_Done_FreeType(library); + return 1; + } + + gw = 2*s*s; + + p->pixels = malloc(gw); + if (!p->pixels) { + free(p); + FT_Done_Face(face); + FT_Done_FreeType(library); + return 1; + } + + array_init(&p->sizes,ARRAY_TYPE_PTR); + + memset(p->pixels,0,gw); + + sx = 0; + sy = 0; + + for (ch=0; ch<256; ch++) { + if (FT_Load_Glyph(face,FT_Get_Char_Index(face,ch+(i*256)),FT_LOAD_DEFAULT)) + return 1; + + if (FT_Get_Glyph(face->glyph,&glyph)) + return 1; + + /* make a bitmap of the glyph */ + FT_Glyph_To_Bitmap(&glyph,ft_render_mode_normal,0,1); + bitmap_glyph = (FT_BitmapGlyph)glyph; + + /* TODO: a less shitty way to do this, should calculate for rows/columns */ + gw = bitmap_glyph->bitmap.width; + gh = bitmap_glyph->bitmap.rows; + character = malloc(sizeof(fontchar_t)); + character->x[0] = (float)((float)sx/(float)s); + character->y[0] = (float)((float)sy/(float)s); + character->x[1] = (float)character->x[0]+(float)((float)mw/(float)s); + character->y[1] = (float)character->y[0]+(float)((float)mh/(float)s); + character->a = (face->glyph->advance.x >> 6); + character->w = mw; + character->h = mh; + + array_push_ptr(&p->sizes,character); + for (j=0; jtop)); + l = (ax+(ay*s)); + p->pixels[l] = 255; + l++; + if (k >= bitmap_glyph->bitmap.width || j >= bitmap_glyph->bitmap.rows) { + p->pixels[l] = 0; + }else{ + p->pixels[l] = bitmap_glyph->bitmap.buffer[k + bitmap_glyph->bitmap.width*j]; + } + } + } + + sx += mw; + if (sx+mw > s) { + sx = 0; + sy += mh; + } + FT_Done_Glyph(glyph); + } + + glGenTextures(1,&p->glid); + glBindTexture(GL_TEXTURE_2D, p->glid); + + b = opengl_has_bilinear(); + t = opengl_has_trilinear(); + m = opengl_has_mipmap(); + + /* draw the pixels to the texture */ + glTexImage2D(GL_TEXTURE_2D,0,GL_RGBA8,s,s,0,GL_LUMINANCE_ALPHA,GL_UNSIGNED_BYTE,p->pixels); + + if (m) { + /* shouldn't ever need this, but there's an ATI/AMD bug that this fixes */ + glEnable(GL_TEXTURE_2D); + glGenerateMipmap(GL_TEXTURE_2D); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + } + + if (t) { + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR); + if (m) { + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR_MIPMAP_LINEAR); + }else{ + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + } + }else if (b) { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + }else{ + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + } + + if (opengl_has_anisotropic()) { + float ma = opengl_max_anisotropic(); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, ma); + } + + /* done! */ + FT_Done_Face(face); + FT_Done_FreeType(library); + + array_set_ptr(&f->pages,p,i); + + return 0; +} + +/* load a true type font file */ +int font_load_ttf(font_t *f, char* file) +{ + f->file = path_get("font",file,1,NULL,0); + + if (!f->file) + return 1; + + array_init(&f->pages,ARRAY_TYPE_PTR); + + /* create the ascii fontpage */ + if (font_ttf_gen_fontpage(f,0)) + vlprintf(CN_WARN "font_load_ttf: failed to generate ASCII fontpage"); + + return 0; +} + +/* returns the opengl texture id and texcoords for a character */ +int font_ttf_get_char(font_t *f, uint32_t ch, fontchar_t **fc, GLuint *glid) +{ + uint32_t pid; + fontpage_t *p; + pid = ch/256; + + if (font_ttf_gen_fontpage(f,pid)) + return 1; + + p = array_get_ptr(&f->pages,pid); + if (!p) + return 1; + + *fc = array_get_ptr(&p->sizes,ch%256); + if (!(*fc)) + return 1; + + *glid = p->glid; + + return 0; +} diff --git a/src/graphics/image.c b/src/graphics/image.c index bbde259..fa7c364 100644 --- a/src/graphics/image.c +++ b/src/graphics/image.c @@ -189,6 +189,42 @@ image_t *image_load_fromscreen(int x, int y, int w, int h, int alpha) return image; } +/* create an image from rgba values */ +image_t *image_rgba(int x, int y, uint8_t r, uint8_t g, uint8_t b, uint8_t a) +{ + char n[100]; + image_t *img; + int size; + int i; + + snprintf(n,100,"rgba-%u-%u-%u-%u-%d-%d",(uint32_t)r,(uint32_t)g,(uint32_t)b,(uint32_t)a,x,y); + + img = malloc(sizeof(image_t)); + if (!img) + return NULL; + + img->w = x; + img->h = y; + + size = x*y*4; + + img->pixels = malloc(sizeof(uint8_t)*size); + if (!img->pixels) { + free(img); + return NULL; + } + + i = 0; + while (ipixels[i++] = r; + img->pixels[i++] = g; + img->pixels[i++] = b; + img->pixels[i++] = a; + } + + return img; +} + /* create a copy of an image */ image_t *image_copy(image_t *img) { diff --git a/src/graphics/render2d.c b/src/graphics/render2d.c index f89b800..125ab35 100644 --- a/src/graphics/render2d.c +++ b/src/graphics/render2d.c @@ -31,17 +31,19 @@ typedef struct obj2d_s { int type; rectf_t box; material_t *mat; + textbuffer_t txt; + textbuffer_t *itxt; } obj2d_t; static struct { GLuint vao; - GLuint vbo; + GLuint vbo[2]; shader_t *shader; obj2d_t *stack; obj2d_t *current; } render2d_data = { 0, - 0, + {0,0}, NULL, NULL, NULL @@ -52,6 +54,9 @@ static obj2d_t *render2d_section_create() obj2d_t *r = malloc(sizeof(obj2d_t)); r->type = RD2_NONE; r->mat = NULL; + r->itxt = NULL; + + textbuffer_init(&r->txt,NULL,0,0,0,0,0,0,0); return r; } @@ -67,12 +72,21 @@ static void render2d_get_section(material_t *m, int t) } } - if (render2d_data.current->mat) { - if (!render2d_data.current->next) { - render2d_data.current = render2d_section_create(); - render2d_data.stack = list_push(&render2d_data.stack,render2d_data.current); - }else{ - render2d_data.current = render2d_data.current->next; + if (render2d_data.current->type != RD2_NONE) { + if ( + t == RD2_TEXT + || render2d_data.current->type != t + || ( + m + && m != render2d_data.current->mat + ) + ) { + if (!render2d_data.current->next) { + render2d_data.current = render2d_section_create(); + render2d_data.stack = list_push(&render2d_data.stack,render2d_data.current); + }else{ + render2d_data.current = render2d_data.current->next; + } } } if (m) @@ -113,21 +127,52 @@ void render2d_quad_mat(material_t *m, float x, float y, float w, float h) } /* render text */ -void render2d_text(float x, float y, int font, int size, char* str) +void render2d_text(float x, float y, int w, int h, int font, int size, char* str) { - /* + font_t *f; render2d_get_section(NULL,RD2_TEXT); - strcpy(render2d_data.current->txt,str); - render2d_vertex_push(x,y); - render2d_vertex_push(font,size); - */ + f = font_get(font); + + textbuffer_adjust(&render2d_data.current->txt,f,size,x,y,w,h,0,0); + textbuffer_addstr(&render2d_data.current->txt,str); } +/* render a pregenerated textbuffer */ +void render2d_textbuffer(textbuffer_t *t) +{ + float x; + float y; + float s; + render2d_get_section(NULL,RD2_TEXT); + + render2d_data.current->itxt = t; + + s = (((float)t->font_size)*0.15); + s /= (float)wm_data.size.height; + + x = t->x; + y = wm_data.size.height-t->y; + + x = (((x/(float)wm_data.size.width)*2.0)-1.0); + y = (((y/(float)wm_data.size.height)*2.0)-1.0); + + render2d_data.current->box.x = x; + render2d_data.current->box.y = y; + render2d_data.current->box.w = s; + render2d_data.current->box.h = s; +} + +/* render a textbuffer */ + /* render 2d graphics to the frame */ void render2d() { int s = 0; + int b = 0; + int i; + rendertext_t *t; + textbuffer_t *txt; matrix_t m; glDisable(GL_DEPTH_TEST); @@ -135,43 +180,113 @@ void render2d() render2d_data.current = render2d_data.stack; if (render2d_data.vao == 0) { - GLfloat vertices[8] = {-1.0,1.0,-1.0,-1.0,1.0,1.0,1.0,-1.0}; + GLfloat vertices[8] = {-1.0,1.0, -1.0,-1.0, 1.0,1.0, 1.0,-1.0}; + GLfloat texcoords[8] = {0.0,0.0, 0.0,1.0, 1.0,0.0, 1.0,1.0}; glGenVertexArrays(1,&render2d_data.vao); glBindVertexArray(render2d_data.vao); - glGenBuffers(1, &render2d_data.vbo); - glBindBuffer(GL_ARRAY_BUFFER, render2d_data.vbo); + glGenBuffers(2, render2d_data.vbo); + glBindBuffer(GL_ARRAY_BUFFER, render2d_data.vbo[0]); glBufferData(GL_ARRAY_BUFFER, 32, vertices, GL_STATIC_DRAW); glEnableVertexAttribArray(0); glVertexAttribPointer(0,2,GL_FLOAT,GL_FALSE,0,0); + glBindBuffer(GL_ARRAY_BUFFER, render2d_data.vbo[1]); + glBufferData(GL_ARRAY_BUFFER, 32, texcoords, GL_STATIC_DRAW); + glEnableVertexAttribArray(1); + glVertexAttribPointer(1,2,GL_FLOAT,GL_FALSE,0,0); + glBindBuffer(GL_ARRAY_BUFFER, 0); } /* shader */ if (!render2d_data.shader) { render2d_data.shader = shader_create("ui"); shader_attribute(render2d_data.shader,0,"position"); + shader_attribute(render2d_data.shader,1,"uvs"); } shader_enable(render2d_data.shader); glBindVertexArray(render2d_data.vao); glEnableVertexAttribArray(0); + glEnableVertexAttribArray(1); + b = 0; - while (render2d_data.current && render2d_data.current->mat) { - mat_use(render2d_data.current->mat,render2d_data.shader); + while (render2d_data.current && render2d_data.current->type != RD2_NONE) { + if (render2d_data.current->type == RD2_TEXT) { + txt = render2d_data.current->itxt; + if (!txt) + txt = &render2d_data.current->txt; + for (i=0; idata.length; i++) { + t = array_get_ptr(&txt->data,i); + switch (t->state) { + case 0: + glGenVertexArrays(1,&t->vao); + glBindVertexArray(t->vao); + glGenBuffers(2, t->vbo); + glBindBuffer(GL_ARRAY_BUFFER, t->vbo[0]); + glBufferData(GL_ARRAY_BUFFER, t->v.length*4, t->v.data, GL_STATIC_DRAW); + glEnableVertexAttribArray(0); + glVertexAttribPointer(0,2,GL_FLOAT,GL_FALSE,0,0); + glBindBuffer(GL_ARRAY_BUFFER, t->vbo[1]); + glBufferData(GL_ARRAY_BUFFER, t->t.length*4, t->t.data, GL_STATIC_DRAW); + glEnableVertexAttribArray(1); + glVertexAttribPointer(1,2,GL_FLOAT,GL_FALSE,0,0); + t->state = 1; + break; + case 1: + glBindVertexArray(t->vao); + glEnableVertexAttribArray(0); + glEnableVertexAttribArray(1); + break; + case 2: + glDeleteBuffers(2, t->vbo); + glGenBuffers(2, t->vbo); + glBindBuffer(GL_ARRAY_BUFFER, t->vbo[0]); + glBufferData(GL_ARRAY_BUFFER, t->v.length*4, t->v.data, GL_STATIC_DRAW); + glEnableVertexAttribArray(0); + glVertexAttribPointer(0,2,GL_FLOAT,GL_FALSE,0,0); + glBindBuffer(GL_ARRAY_BUFFER, t->vbo[1]); + glBufferData(GL_ARRAY_BUFFER, t->t.length*4, t->t.data, GL_STATIC_DRAW); + glEnableVertexAttribArray(1); + glVertexAttribPointer(1,2,GL_FLOAT,GL_FALSE,0,0); + t->state = 1; + break; + default:; + } + glBindTexture(GL_TEXTURE_2D,t->glid); + matrix_init(&m); + matrix_scale(&m,render2d_data.current->box.w,render2d_data.current->box.h,1.0); + matrix_translate(&m,render2d_data.current->box.x,render2d_data.current->box.y,1.0); + shader_uniform_matrix(render2d_data.shader,"transformationMatrix",&m); + glDrawArrays(GL_TRIANGLES,0,t->v.length/2); + } + b = 1; + render2d_data.current->itxt = NULL; + textbuffer_clear(&render2d_data.current->txt); + }else if (render2d_data.current->type == RD2_QUAD) { + if (b != 0) { + glBindVertexArray(render2d_data.vao); + glEnableVertexAttribArray(0); + glEnableVertexAttribArray(1); + b = 0; + } + mat_use(render2d_data.current->mat,render2d_data.shader); - matrix_init(&m); - matrix_scale(&m,render2d_data.current->box.w,render2d_data.current->box.h,1.0); - matrix_translate(&m,render2d_data.current->box.x,render2d_data.current->box.y,1.0); - shader_uniform_matrix(render2d_data.shader,"transformationMatrix",&m); + matrix_init(&m); + matrix_scale(&m,render2d_data.current->box.w,render2d_data.current->box.h,1.0); + matrix_translate(&m,render2d_data.current->box.x,render2d_data.current->box.y,1.0); + shader_uniform_matrix(render2d_data.shader,"transformationMatrix",&m); - glDrawArrays(GL_TRIANGLE_STRIP,0,4); + glDrawArrays(GL_TRIANGLE_STRIP,0,4); + render2d_data.current->mat = NULL; + } - render2d_data.current->mat = NULL; + render2d_data.current->type = RD2_NONE; render2d_data.current = render2d_data.current->next; s++; } glDisableVertexAttribArray(0); + glDisableVertexAttribArray(1); glBindVertexArray(0); /* shader */ diff --git a/src/graphics/render3d.c b/src/graphics/render3d.c index cd5c7b0..c785b12 100644 --- a/src/graphics/render3d.c +++ b/src/graphics/render3d.c @@ -354,7 +354,7 @@ void render3d_set_projection_matrix() fov = math_degrees_to_radians(fov/2.0); ratio = (float)wm_data.size.width/(float)wm_data.size.height; - y = (1.0/tan(fov))*ratio; + y = (1.0/tan(fov)); x = y/ratio; z = far_plane-near_plane; diff --git a/src/graphics/textbuffer.c b/src/graphics/textbuffer.c new file mode 100644 index 0000000..be8f7d6 --- /dev/null +++ b/src/graphics/textbuffer.c @@ -0,0 +1,221 @@ +/************************************************************************ +* textbuffer.c +* voxelands - 3d voxel world sandbox game +* Copyright (C) Lisa 'darkrose' Milne 2016 +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +* See the GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see +************************************************************************/ + +#include "common.h" +#define _WM_EXPOSE_ALL +#include "wm.h" +#include "graphics.h" + +#include + +static rendertext_t *textbuffer_get_rendertext(textbuffer_t *t, uint32_t page) +{ + rendertext_t *ft; + + ft = malloc(sizeof(rendertext_t)); + if (!ft) + return NULL; + + ft->page = page; + ft->state = 0; + ft->vao = 0; + ft->vbo[0] = 0; + ft->vbo[1] = 0; + array_init(&ft->v,ARRAY_TYPE_FLOAT); + array_init(&ft->t,ARRAY_TYPE_FLOAT); + + array_push_ptr(&t->data,ft); + + return ft; +} + +void textbuffer_init(textbuffer_t *t, font_t *f, int size, float x, float y, int max_x, int max_y, int max_c, uint32_t options) +{ + if (!t) + return; + + t->mx = max_x; + t->my = max_y; + t->mc = max_c; + t->options = options; + t->str = NULL; + t->length = 0; + t->size = 0; + t->x = x; + t->y = y; + t->nx = 0; + t->ny = 0; + t->font = f; + t->font_size = size; + + array_init(&t->data,ARRAY_TYPE_PTR); + + if (!t->mc) + return; + + t->str = malloc(sizeof(uint32_t)*t->mc); + if (!t->str) + return; + + t->size = t->mc; + t->str[0] = 0; +} + +textbuffer_t *textbuffer_create(font_t *f, int size, float x, float y, int max_x, int max_y, int max_c, uint32_t options) +{ + textbuffer_t *t = malloc(sizeof(textbuffer_t)); + if (!t) + return NULL; + + textbuffer_init(t,f,size,x,y,max_x,max_y, max_c, options); + + return t; +} + +void textbuffer_clear(textbuffer_t *t) +{ + if (!t) + return; + if (t->str) + free(t->str); + + t->str = NULL; + t->length = 0; + t->size = 0; + + array_free(&t->data,0); + + textbuffer_init(t,NULL,0,0,0,0,0,0,0); +} + +void textbuffer_free(textbuffer_t *t) +{ + if (!t) + return; + + textbuffer_clear(t); + free(t); +} + +int textbuffer_adjust(textbuffer_t *t, font_t *f, int size, float x, float y, int max_x, int max_y, int max_c, uint32_t options) +{ + if (!t) + return 1; + return 0; +} + +int textbuffer_addstr(textbuffer_t *t, char* str) +{ + int r = 0; + int i = 0; + uint32_t ch; + if (!t) + return 1; + + while ((ch = utf8_nextchar(str,&i))) { + r = textbuffer_addchar(t,ch); + if (r) + break; + } + + return r; +} + +int textbuffer_addchar(textbuffer_t *t, uint32_t ch) +{ + rendertext_t *rt = NULL; + fontchar_t *fc; + uint32_t page; + float sf; + v2_t v; + v2_t tc; + float w; + float h; + if (!t) + return 1; + + if ((t->options&TB_OPT_NUMERIC) == TB_OPT_NUMERIC && !iswdigit(ch)) + return -1; + + if (t->mc && t->length == t->mc) + return -1; + + page = ch/256; + + if (t->data.length) { + rt = array_get_ptr(&t->data,t->data.length-1); + if (rt && rt->page != page) + rt = NULL; + } + + if (!rt) + rt = textbuffer_get_rendertext(t,page); + + if (!rt) + return 1; + + if (font_ttf_get_char(t->font,ch,&fc,&rt->glid)) + return 1; + + sf = (float)t->font_size/fc->h; + + w = sf*fc->w; + h = sf*fc->h; + + v.x = t->nx; + v.y = t->ny-h; + tc.x = fc->x[0]; + tc.y = fc->y[1]; + array_push_v2t(&rt->v,&v); + array_push_v2t(&rt->t,&tc); + v.x = t->nx+w; + v.y = t->ny-h; + tc.x = fc->x[1]; + tc.y = fc->y[1]; + array_push_v2t(&rt->v,&v); + array_push_v2t(&rt->t,&tc); + v.x = t->nx; + v.y = t->ny; + tc.x = fc->x[0]; + tc.y = fc->y[0]; + array_push_v2t(&rt->v,&v); + array_push_v2t(&rt->t,&tc); + array_push_v2t(&rt->v,&v); + array_push_v2t(&rt->t,&tc); + v.x = t->nx+w; + v.y = t->ny-h; + tc.x = fc->x[1]; + tc.y = fc->y[1]; + array_push_v2t(&rt->v,&v); + array_push_v2t(&rt->t,&tc); + v.x = t->nx+w; + v.y = t->ny; + tc.x = fc->x[1]; + tc.y = fc->y[0]; + array_push_v2t(&rt->v,&v); + array_push_v2t(&rt->t,&tc); + + t->nx += (sf*fc->a); + if (t->mx && t->nx > t->mx) { + t->nx = 0; + t->ny += t->font_size; + } + + return 0; +} diff --git a/src/graphics/texture.c b/src/graphics/texture.c index bd7ba7e..6f4a9c6 100644 --- a/src/graphics/texture.c +++ b/src/graphics/texture.c @@ -230,8 +230,6 @@ texture_t *tex_from_rgba(int x, int y, uint8_t r, uint8_t g, uint8_t b, uint8_t char n[100]; texture_t *t; image_t *p; - int size; - int i; snprintf(n,100,"rgba-%u-%u-%u-%u-%d-%d",(uint32_t)r,(uint32_t)g,(uint32_t)b,(uint32_t)a,x,y); t = textures; @@ -244,29 +242,10 @@ texture_t *tex_from_rgba(int x, int y, uint8_t r, uint8_t g, uint8_t b, uint8_t if (t) return t; - p = malloc(sizeof(image_t)); + p = image_rgba(x,y,r,g,b,a); if (!p) return NULL; - p->w = x; - p->h = y; - - size = x*y*4; - - p->pixels = malloc(sizeof(uint8_t)*size); - if (!p->pixels) { - free(p); - return NULL; - } - - i = 0; - while (ipixels[i++] = r; - p->pixels[i++] = g; - p->pixels[i++] = b; - p->pixels[i++] = a; - } - t = tex_from_pixels(p); strcpy(t->name,n); diff --git a/src/lib/array.c b/src/lib/array.c index 00daaa4..370d393 100644 --- a/src/lib/array.c +++ b/src/lib/array.c @@ -357,11 +357,15 @@ int array_set_int(array_t *a, uint32_t v, int i) } if (a->size <= i) { + int k; int l = i+1; p = realloc(a->data,sizeof(uint32_t)*l); if (!p) return 1; + for (k=a->length; kdata = p; a->size = l; } @@ -389,11 +393,15 @@ int array_set_float(array_t *a, float v, int i) } if (a->size <= i) { + int k; int l = i+1; p = realloc(a->data,sizeof(float)*l); if (!p) return 1; + for (k=a->length; kdata = p; a->size = l; } @@ -413,11 +421,15 @@ int array_set_string(array_t *a, char* v, int i) return 1; if (a->size <= i) { + int k; int l = i+1; p = realloc(a->data,sizeof(char*)*l); if (!p) return 1; + for (k=a->length; kdata = p; a->size = l; } @@ -441,11 +453,15 @@ int array_set_ptr(array_t *a, void* v, int i) return 1; if (a->size <= i) { + int k; int l = i+1; p = realloc(a->data,sizeof(char*)*l); if (!p) return 1; + for (k=a->length; kdata = p; a->size = l; } -- 2.11.4.GIT