diff options
| author | Magnus Auvinen <magnus.auvinen@gmail.com> | 2007-08-22 07:52:33 +0000 |
|---|---|---|
| committer | Magnus Auvinen <magnus.auvinen@gmail.com> | 2007-08-22 07:52:33 +0000 |
| commit | 8b3c16e6152a527f9aec1a88a9eed74119de7000 (patch) | |
| tree | f0bde5cea15e696e42cade06a3b12ff6b13acc57 /src/engine/client/gfx.c | |
| parent | 9899666a7ce6679a3b9667ab09f615f4d0769c16 (diff) | |
| download | zcatch-8b3c16e6152a527f9aec1a88a9eed74119de7000.tar.gz zcatch-8b3c16e6152a527f9aec1a88a9eed74119de7000.zip | |
major engine cleanup. dependency on baselib removed. engine is now C code (not ansi tho). some other cruft removed aswell
Diffstat (limited to 'src/engine/client/gfx.c')
| -rw-r--r-- | src/engine/client/gfx.c | 886 |
1 files changed, 886 insertions, 0 deletions
diff --git a/src/engine/client/gfx.c b/src/engine/client/gfx.c new file mode 100644 index 00000000..b48740b6 --- /dev/null +++ b/src/engine/client/gfx.c @@ -0,0 +1,886 @@ + +#include <engine/external/glfw/include/GL/glfw.h> +#include <engine/external/pnglite/pnglite.h> + +#include <engine/system.h> +#include <engine/interface.h> +#include <engine/config.h> +#include <engine/keys.h> + +#include <string.h> +#include <stdio.h> +#include <math.h> + +// compressed textures +#define GL_COMPRESSED_RGB_ARB 0x84ED +#define GL_COMPRESSED_RGBA_ARB 0x84EE + +// +typedef struct { float x, y, z; } VEC3; +typedef struct { float u, v; } TEXCOORD; +typedef struct { float r, g, b, a; } COLOR; + +typedef struct +{ + VEC3 pos; + TEXCOORD tex; + COLOR color; +} VERTEX; + +const int vertex_buffer_size = 32*1024; +static VERTEX *vertices = 0; +static int num_vertices = 0; + +static COLOR color[4]; +static TEXCOORD texture[4]; + +static int do_screenshot = 0; + +static int screen_width = -1; +static int screen_height = -1; +static float rotation = 0; +static int quads_drawing = 0; + +static float screen_x0 = 0; +static float screen_y0 = 0; +static float screen_x1 = 0; +static float screen_y1 = 0; + +typedef struct +{ + GLuint tex; + int memsize; + int flags; + int next; +} TEXTURE; + + +enum +{ + MAX_TEXTURES = 128 +}; + +static TEXTURE textures[MAX_TEXTURES]; +static int first_free_texture; +static int memory_usage = 0; + +static const unsigned char null_texture_data[] = { + 0xff,0x00,0x00,0xff, 0xff,0x00,0x00,0xff, 0x00,0xff,0x00,0xff, 0x00,0xff,0x00,0xff, + 0xff,0x00,0x00,0xff, 0xff,0x00,0x00,0xff, 0x00,0xff,0x00,0xff, 0x00,0xff,0x00,0xff, + 0x00,0x00,0xff,0xff, 0x00,0x00,0xff,0xff, 0xff,0xff,0x00,0xff, 0xff,0xff,0x00,0xff, + 0x00,0x00,0xff,0xff, 0x00,0x00,0xff,0xff, 0xff,0xff,0x00,0xff, 0xff,0xff,0x00,0xff, +}; + +static void draw_quad(int _bflush) +{ + if (!_bflush && ((num_vertices + 4) < vertex_buffer_size)) + { + // Just add + num_vertices += 4; + } + else if (num_vertices) + { + if (!_bflush) + num_vertices += 4; + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + glVertexPointer(3, GL_FLOAT, + sizeof(VERTEX), + (char*)vertices); + glTexCoordPointer(2, GL_FLOAT, + sizeof(VERTEX), + (char*)vertices + sizeof(float)*3); + glColorPointer(4, GL_FLOAT, + sizeof(VERTEX), + (char*)vertices + sizeof(float)*5); + glEnableClientState(GL_VERTEX_ARRAY); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glEnableClientState(GL_COLOR_ARRAY); + glDrawArrays(GL_QUADS, 0, num_vertices); + + // Reset pointer + num_vertices = 0; + } +} + +int gfx_init() +{ + screen_width = config.gfx_screen_width; + screen_height = config.gfx_screen_height; + + glfwInit(); + + if(config.stress) + { + screen_width = 320; + screen_height = 240; + } + + if(config.gfx_fullscreen) + { + int result = glfwOpenWindow(screen_width, screen_height, 8, 8, 8, 0, 0, 0, GLFW_FULLSCREEN); + if(result != GL_TRUE) + { + dbg_msg("game", "failed to create gl context"); + return 0; + } + } + else + { + int result = glfwOpenWindow(screen_width, screen_height, 0, 0, 0, 0, 0, 0, GLFW_WINDOW); + if(result != GL_TRUE) + { + dbg_msg("game", "failed to create gl context"); + return 0; + } + } + + glfwSetWindowTitle("Teewars"); + + // We don't want to see the window when we run the stress testing + if(config.stress) + glfwIconifyWindow(); + + // Init vertices + if (vertices) + mem_free(vertices); + vertices = (VERTEX*)mem_alloc(sizeof(VERTEX) * vertex_buffer_size, 1); + num_vertices = 0; + + + /* + dbg_msg("gfx", "OpenGL version %d.%d.%d", context.version_major(), + context.version_minor(), + context.version_rev());*/ + + gfx_mapscreen(0,0,config.gfx_screen_width, config.gfx_screen_height); + + // TODO: make wrappers for this + glEnable(GL_BLEND); + + // model + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + + // Set all z to -5.0f + int i; + for (i = 0; i < vertex_buffer_size; i++) + vertices[i].pos.z = -5.0f; + + // init textures + first_free_texture = 0; + for(i = 0; i < MAX_TEXTURES; i++) + textures[i].next = i+1; + textures[MAX_TEXTURES-1].next = -1; + + //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + // init input + inp_init(); + + // create null texture, will get id=0 + gfx_load_texture_raw(4,4,IMG_RGBA,null_texture_data); + + // set vsync as needed + gfx_set_vsync(config.gfx_vsync); + + return 1; +} + + +int gfx_window_active() +{ + return glfwGetWindowParam(GLFW_ACTIVE) == GL_TRUE ? 1 : 0; +} + + +VIDEO_MODE fakemodes[] = { + {320,240,8,8,8}, {400,300,8,8,8}, {640,480,8,8,8}, + {720,400,8,8,8}, {768,576,8,8,8}, {800,600,8,8,8}, + {1024,600,8,8,8}, {1024,768,8,8,8}, {1152,864,8,8,8}, + {1280,768,8,8,8}, {1280,800,8,8,8}, {1280,960,8,8,8}, + {1280,1024,8,8,8}, {1368,768,8,8,8}, {1400,1050,8,8,8}, + {1440,900,8,8,8}, {1440,1050,8,8,8}, {1600,1000,8,8,8}, + {1600,1200,8,8,8}, {1680,1050,8,8,8}, {1792,1344,8,8,8}, + {1800,1440,8,8,8}, {1856,1392,8,8,8}, {1920,1080,8,8,8}, + {1920,1200,8,8,8}, {1920,1440,8,8,8}, {1920,2400,8,8,8}, + {2048,1536,8,8,8}, + + {320,240,5,6,5}, {400,300,5,6,5}, {640,480,5,6,5}, + {720,400,5,6,5}, {768,576,5,6,5}, {800,600,5,6,5}, + {1024,600,5,6,5}, {1024,768,5,6,5}, {1152,864,5,6,5}, + {1280,768,5,6,5}, {1280,800,5,6,5}, {1280,960,5,6,5}, + {1280,1024,5,6,5}, {1368,768,5,6,5}, {1400,1050,5,6,5}, + {1440,900,5,6,5}, {1440,1050,5,6,5}, {1600,1000,5,6,5}, + {1600,1200,5,6,5}, {1680,1050,5,6,5}, {1792,1344,5,6,5}, + {1800,1440,5,6,5}, {1856,1392,5,6,5}, {1920,1080,5,6,5}, + {1920,1200,5,6,5}, {1920,1440,5,6,5}, {1920,2400,5,6,5}, + {2048,1536,5,6,5} +}; + +int gfx_get_video_modes(VIDEO_MODE *list, int maxcount) +{ + if(config.gfx_display_all_modes) + { + mem_copy(list, fakemodes, sizeof(fakemodes)); + int count = sizeof(fakemodes)/sizeof(VIDEO_MODE); + if(maxcount < count) + count = maxcount; + return count; + } + + return glfwGetVideoModes((GLFWvidmode *)list, maxcount); +} + +void gfx_set_vsync(int val) +{ + glfwSwapInterval(val); +} + +int gfx_unload_texture(int index) +{ + glDeleteTextures(1, &textures[index].tex); + textures[index].next = first_free_texture; + memory_usage -= textures[index].memsize; + first_free_texture = index; + return 0; +} + +void gfx_blend_normal() +{ + // TODO: wrapper for this + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); +} + +void gfx_blend_additive() +{ + // TODO: wrapper for this + glBlendFunc(GL_SRC_ALPHA, GL_ONE); +} + +int gfx_memory_usage() { return memory_usage; } + +static unsigned char sample(int w, int h, const unsigned char *data, int u, int v, int offset) +{ + return (data[(v*w+u)*4+offset]+ + data[(v*w+u+1)*4+offset]+ + data[((v+1)*w+u)*4+offset]+ + data[((v+1)*w+u+1)*4+offset])/4; +} + +int gfx_load_texture_raw(int w, int h, int format, const void *data) +{ + int mipmap = 1; + + // grab texture + int tex = first_free_texture; + first_free_texture = textures[tex].next; + textures[tex].next = -1; + + // resample if needed + unsigned char *texdata = (unsigned char *)data; + unsigned char *tmpdata = 0; + if(config.gfx_texture_quality==0) + { + if(w > 16 && h > 16 && format == IMG_RGBA) + { + w/=2; + h/=2; + unsigned char *tmpdata = (unsigned char *)mem_alloc(w*h*4, 1); + int c = 0; + int x, y; + for(y = 0; y < h; y++) + for(x = 0; x < w; x++, c++) + { + tmpdata[c*4] = sample(w*2, h*2, texdata, x*2,y*2, 0); + tmpdata[c*4+1] = sample(w*2, h*2, texdata, x*2,y*2, 1); + tmpdata[c*4+2] = sample(w*2, h*2, texdata, x*2,y*2, 2); + tmpdata[c*4+3] = sample(w*2, h*2, texdata, x*2,y*2, 3); + } + texdata = tmpdata; + } + } + + if(config.debug) + dbg_msg("gfx", "%d = %dx%d", tex, w, h); + + // upload texture + int oglformat = 0; + if(config.gfx_texture_compression) + { + oglformat = GL_COMPRESSED_RGBA_ARB; + if(format == IMG_RGB) + oglformat = GL_COMPRESSED_RGB_ARB; + } + else + { + oglformat = GL_RGBA; + if(format == IMG_RGB) + oglformat = GL_RGB; + } + + glGenTextures(1, &textures[tex].tex); + glBindTexture(GL_TEXTURE_2D, textures[tex].tex); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + gluBuild2DMipmaps(GL_TEXTURE_2D, oglformat, w, h, oglformat, GL_UNSIGNED_BYTE, texdata); + + // calculate memory usage + textures[tex].memsize = w*h*4; + if(mipmap) + { + while(w > 2 && h > 2) + { + w>>=1; + h>>=1; + textures[tex].memsize += w*h*4; + } + } + + memory_usage += textures[tex].memsize; + mem_free(tmpdata); + return tex; +} +/* +int gfx_load_mip_texture_raw(int w, int h, int format, const void *data) +{ + // grab texture + int tex = first_free_texture; + first_free_texture = textures[tex].next; + textures[tex].next = -1; + + // set data and return + // TODO: should be RGBA, not BGRA + dbg_msg("gfx", "%d = %dx%d", tex, w, h); + dbg_assert(format == IMG_RGBA, "not an RGBA image"); + + unsigned mip_w = w; + unsigned mip_h = h; + unsigned level = 0; + const unsigned char *ptr = (const unsigned char*)data; + while(mip_w > 0 && mip_h > 0) + { + dbg_msg("gfx mip", "%d = %dx%d", level, mip_w, mip_h); + textures[tex].tex.data2d_mip(mip_w, mip_h, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, level, ptr); + level++; + ptr = ptr + mip_w*mip_h*4; + mip_w = mip_w>>1; + mip_h = mip_h>>1; + } + return tex; +} +*/ +// simple uncompressed RGBA loaders +int gfx_load_texture(const char *filename) +{ + int l = strlen(filename); + if(l < 3) + return 0; + IMAGE_INFO img; + if(gfx_load_png(&img, filename)) + { + int id = gfx_load_texture_raw(img.width, img.height, img.format, img.data); + mem_free(img.data); + return id; + } + + return 0; +} + +int gfx_load_png(IMAGE_INFO *img, const char *filename) +{ + // open file for reading + png_init(0,0); + + png_t png; + if(png_open_file(&png, filename) != PNG_NO_ERROR) + { + dbg_msg("game/png", "failed to open file. filename='%s'", filename); + return 0; + } + + if(png.depth != 8 || (png.color_type != PNG_TRUECOLOR && png.color_type != PNG_TRUECOLOR_ALPHA)) + { + dbg_msg("game/png", "invalid format. filename='%s'", filename); + png_close_file(&png); + } + + unsigned char *buffer = (unsigned char *)mem_alloc(png.width * png.height * png.bpp, 1); + png_get_data(&png, buffer); + png_close_file(&png); + + img->width = png.width; + img->height = png.height; + if(png.color_type == PNG_TRUECOLOR) + img->format = IMG_RGB; + else if(png.color_type == PNG_TRUECOLOR_ALPHA) + img->format = IMG_RGBA; + img->data = buffer; + return 1; +} + +void gfx_shutdown() +{ + if (vertices) + mem_free(vertices); + glfwCloseWindow(); + glfwTerminate(); +} + +void gfx_screenshot() +{ + do_screenshot = 1; +} + +void gfx_swap() +{ + if(do_screenshot) + { + // fetch image data + int w = screen_width; + int h = screen_height; + unsigned char *pixel_data = (unsigned char *)mem_alloc(w*(h+1)*3, 1); + unsigned char *temp_row = pixel_data+w*h*3; + glReadPixels(0,0, w, h, GL_RGB, GL_UNSIGNED_BYTE, pixel_data); + + // flip the pixel because opengl works from bottom left corner + int y; + for(y = 0; y < h/2; y++) + { + mem_copy(temp_row, pixel_data+y*w*3, w*3); + mem_copy(pixel_data+y*w*3, pixel_data+(h-y-1)*w*3, w*3); + mem_copy(pixel_data+(h-y-1)*w*3, temp_row,w*3); + } + + // find filename + char filename[64]; + { + static int index = 1; + for(; index < 1000; index++) + { + sprintf(filename, "screenshot%04d.png", index); + IOHANDLE io = io_open(filename, IOFLAG_READ); + if(io) + io_close(io); + else + break; + } + } + + // save png + png_t png; + png_open_file_write(&png, filename); + png_set_data(&png, w, h, 8, PNG_TRUECOLOR, (unsigned char *)pixel_data); + png_close_file(&png); + + // clean up + mem_free(pixel_data); + do_screenshot = 0; + } + + glfwSwapBuffers(); + glfwPollEvents(); +} + +int gfx_screenwidth() +{ + return screen_width; +} + +int gfx_screenheight() +{ + return screen_height; +} + +void gfx_texture_set(int slot) +{ + dbg_assert(quads_drawing == 0, "called gfx_texture_set within quads_begin"); + if(slot == -1) + glDisable(GL_TEXTURE_2D); + else + { + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, textures[slot].tex); + } +} + +void gfx_clear(float r, float g, float b) +{ + glClearColor(r,g,b,1.0f); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); +} + +void gfx_mapscreen(float tl_x, float tl_y, float br_x, float br_y) +{ + screen_x0 = tl_x; + screen_y0 = tl_y; + screen_x1 = br_x; + screen_y1 = br_y; + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(tl_x, br_x, br_y, tl_y, 1.0f, 10.f); +} + +void gfx_getscreen(float *tl_x, float *tl_y, float *br_x, float *br_y) +{ + *tl_x = screen_x0; + *tl_y = screen_y0; + *br_x = screen_x1; + *br_y = screen_y1; +} + +void gfx_setoffset(float x, float y) +{ + //const float scale = 1.0f; + //mat4 mat = mat4::identity; + //mat.m[0] = scale; + //mat.m[5] = scale; + //mat.m[10] = scale; + //mat.m[12] = x*scale; + //mat.m[13] = y*scale; + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + glTranslatef(x, y, 0); +} + + +void gfx_quads_begin() +{ + dbg_assert(quads_drawing == 0, "called quads_begin twice"); + quads_drawing++; + + gfx_quads_setsubset(0,0,1,1); + gfx_quads_setrotation(0); + gfx_quads_setcolor(1,1,1,1); +} + +void gfx_quads_end() +{ + dbg_assert(quads_drawing == 1, "called quads_end without quads_begin"); + draw_quad(1); + quads_drawing--; +} + + +void gfx_quads_setrotation(float angle) +{ + dbg_assert(quads_drawing == 1, "called gfx_quads_setrotation without quads_begin"); + rotation = angle; +} + +void gfx_quads_setcolorvertex(int i, float r, float g, float b, float a) +{ + dbg_assert(quads_drawing == 1, "called gfx_quads_setcolorvertex without quads_begin"); + color[i].r = r; + color[i].g = g; + color[i].b = b; + color[i].a = a; +} + +void gfx_quads_setcolor(float r, float g, float b, float a) +{ + dbg_assert(quads_drawing == 1, "called gfx_quads_setcolor without quads_begin"); + gfx_quads_setcolorvertex(0, r, g, b, a); + gfx_quads_setcolorvertex(1, r, g, b, a); + gfx_quads_setcolorvertex(2, r, g, b, a); + gfx_quads_setcolorvertex(3, r, g, b, a); +} + +void gfx_quads_setsubset(float tl_u, float tl_v, float br_u, float br_v) +{ + dbg_assert(quads_drawing == 1, "called gfx_quads_setsubset without quads_begin"); + + texture[0].u = tl_u; + texture[0].v = tl_v; + //g_pVertices[g_iVertexEnd].tex.u = tl_u; + //g_pVertices[g_iVertexEnd].tex.v = tl_v; + + texture[1].u = br_u; + texture[1].v = tl_v; + //g_pVertices[g_iVertexEnd + 2].tex.u = br_u; + //g_pVertices[g_iVertexEnd + 2].tex.v = tl_v; + + texture[2].u = br_u; + texture[2].v = br_v; + //g_pVertices[g_iVertexEnd + 1].tex.u = tl_u; + //g_pVertices[g_iVertexEnd + 1].tex.v = br_v; + + texture[3].u = tl_u; + texture[3].v = br_v; + //g_pVertices[g_iVertexEnd + 3].tex.u = br_u; + //g_pVertices[g_iVertexEnd + 3].tex.v = br_v; +} + +static void rotate(VEC3 *center, VEC3 *point) +{ + float x = point->x - center->x; + float y = point->y - center->y; + point->x = x * cosf(rotation) - y * sinf(rotation) + center->x; + point->y = x * sinf(rotation) + y * cosf(rotation) + center->y; +} + +void gfx_quads_draw(float x, float y, float w, float h) +{ + gfx_quads_drawTL(x-w/2, y-h/2,w,h); +} + +void gfx_quads_drawTL(float x, float y, float width, float height) +{ + dbg_assert(quads_drawing == 1, "called quads_draw without quads_begin"); + + VEC3 center; + center.x = x + width/2; + center.y = y + height/2; + center.z = 0; + + vertices[num_vertices].pos.x = x; + vertices[num_vertices].pos.y = y; + vertices[num_vertices].tex = texture[0]; + vertices[num_vertices].color = color[0]; + rotate(¢er, &vertices[num_vertices].pos); + + vertices[num_vertices + 1].pos.x = x+width; + vertices[num_vertices + 1].pos.y = y; + vertices[num_vertices + 1].tex = texture[1]; + vertices[num_vertices + 1].color = color[1]; + rotate(¢er, &vertices[num_vertices + 1].pos); + + vertices[num_vertices + 2].pos.x = x + width; + vertices[num_vertices + 2].pos.y = y+height; + vertices[num_vertices + 2].tex = texture[2]; + vertices[num_vertices + 2].color = color[2]; + rotate(¢er, &vertices[num_vertices + 2].pos); + + vertices[num_vertices + 3].pos.x = x; + vertices[num_vertices + 3].pos.y = y+height; + vertices[num_vertices + 3].tex = texture[3]; + vertices[num_vertices + 3].color = color[3]; + rotate(¢er, &vertices[num_vertices + 3].pos); + + draw_quad(0); +} + +void gfx_quads_draw_freeform( + float x0, float y0, + float x1, float y1, + float x2, float y2, + float x3, float y3) +{ + dbg_assert(quads_drawing == 1, "called quads_draw_freeform without quads_begin"); + + vertices[num_vertices].pos.x = x0; + vertices[num_vertices].pos.y = y0; + vertices[num_vertices].tex = texture[0]; + vertices[num_vertices].color = color[0]; + + vertices[num_vertices + 1].pos.x = x1; + vertices[num_vertices + 1].pos.y = y1; + vertices[num_vertices + 1].tex = texture[1]; + vertices[num_vertices + 1].color = color[1]; + + vertices[num_vertices + 2].pos.x = x3; + vertices[num_vertices + 2].pos.y = y3; + vertices[num_vertices + 2].tex = texture[2]; + vertices[num_vertices + 2].color = color[2]; + + vertices[num_vertices + 3].pos.x = x2; + vertices[num_vertices + 3].pos.y = y2; + vertices[num_vertices + 3].tex = texture[3]; + vertices[num_vertices + 3].color = color[3]; + + draw_quad(0); +} + +void gfx_quads_text(float x, float y, float size, const char *text) +{ + gfx_quads_begin(); + float startx = x; + while(*text) + { + char c = *text; + text++; + + if(c == '\n') + { + x = startx; + y += size; + } + else + { + gfx_quads_setsubset( + (c%16)/16.0f, + (c/16)/16.0f, + (c%16)/16.0f+1.0f/16.0f, + (c/16)/16.0f+1.0f/16.0f); + + gfx_quads_drawTL(x,y,size,size); + x += size/2; + } + } + + gfx_quads_end(); +} + +typedef struct +{ + float m_CharStartTable[256]; + float m_CharEndTable[256]; + int font_texture; +} pretty_font; + +pretty_font default_font = +{ +{ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0.421875, 0.359375, 0.265625, 0.25, 0.1875, 0.25, 0.4375, 0.390625, 0.390625, 0.34375, 0.28125, 0.421875, 0.390625, 0.4375, 0.203125, + 0.265625, 0.28125, 0.28125, 0.265625, 0.25, 0.28125, 0.28125, 0.265625, 0.28125, 0.265625, 0.4375, 0.421875, 0.3125, 0.28125, 0.3125, 0.3125, + 0.25, 0.234375, 0.28125, 0.265625, 0.265625, 0.296875, 0.3125, 0.25, 0.25, 0.421875, 0.28125, 0.265625, 0.328125, 0.171875, 0.234375, 0.25, + 0.28125, 0.234375, 0.265625, 0.265625, 0.28125, 0.265625, 0.234375, 0.09375, 0.234375, 0.234375, 0.265625, 0.390625, 0.203125, 0.390625, 0.296875, 0.28125, + 0.375, 0.3125, 0.3125, 0.3125, 0.296875, 0.3125, 0.359375, 0.296875, 0.3125, 0.4375, 0.390625, 0.328125, 0.4375, 0.203125, 0.3125, 0.296875, + 0.3125, 0.296875, 0.359375, 0.3125, 0.328125, 0.3125, 0.296875, 0.203125, 0.296875, 0.296875, 0.328125, 0.375, 0.421875, 0.375, 0.28125, 0.3125, + 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, + 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, + 0, 0.421875, 0.3125, 0.265625, 0.25, 0.25, 0.421875, 0.265625, 0.375, 0.21875, 0.375, 0.328125, 0.3125, 0, 0.21875, 0.28125, + 0.359375, 0.28125, 0.34375, 0.34375, 0.421875, 0.3125, 0.265625, 0.421875, 0.421875, 0.34375, 0.375, 0.328125, 0.125, 0.125, 0.125, 0.296875, + 0.234375, 0.234375, 0.234375, 0.234375, 0.234375, 0.234375, 0.109375, 0.265625, 0.296875, 0.296875, 0.296875, 0.296875, 0.375, 0.421875, 0.359375, 0.390625, + 0.21875, 0.234375, 0.25, 0.25, 0.25, 0.25, 0.25, 0.296875, 0.21875, 0.265625, 0.265625, 0.265625, 0.265625, 0.234375, 0.28125, 0.3125, + 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.1875, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.375, 0.421875, 0.359375, 0.390625, + 0.3125, 0.3125, 0.296875, 0.296875, 0.296875, 0.296875, 0.296875, 0.28125, 0.28125, 0.3125, 0.3125, 0.3125, 0.3125, 0.296875, 0.3125, 0.296875, +}, +{ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0.2, 0.5625, 0.625, 0.71875, 0.734375, 0.796875, 0.765625, 0.546875, 0.59375, 0.59375, 0.65625, 0.703125, 0.546875, 0.59375, 0.5625, 0.6875, + 0.71875, 0.609375, 0.703125, 0.703125, 0.71875, 0.703125, 0.703125, 0.6875, 0.703125, 0.703125, 0.5625, 0.546875, 0.671875, 0.703125, 0.671875, 0.671875, + 0.734375, 0.75, 0.734375, 0.734375, 0.734375, 0.6875, 0.6875, 0.734375, 0.71875, 0.5625, 0.65625, 0.765625, 0.703125, 0.8125, 0.75, 0.734375, + 0.734375, 0.765625, 0.71875, 0.71875, 0.703125, 0.71875, 0.75, 0.890625, 0.75, 0.75, 0.71875, 0.59375, 0.6875, 0.59375, 0.6875, 0.703125, + 0.5625, 0.671875, 0.6875, 0.671875, 0.671875, 0.671875, 0.625, 0.671875, 0.671875, 0.5625, 0.546875, 0.703125, 0.5625, 0.78125, 0.671875, 0.671875, + 0.6875, 0.671875, 0.65625, 0.671875, 0.65625, 0.671875, 0.6875, 0.78125, 0.6875, 0.671875, 0.65625, 0.609375, 0.546875, 0.609375, 0.703125, 0.671875, + 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, + 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, + 0, 0.5625, 0.671875, 0.734375, 0.734375, 0.734375, 0.546875, 0.71875, 0.609375, 0.765625, 0.609375, 0.65625, 0.671875, 0, 0.765625, 0.703125, + 0.625, 0.703125, 0.640625, 0.640625, 0.609375, 0.671875, 0.703125, 0.546875, 0.5625, 0.578125, 0.609375, 0.65625, 0.859375, 0.859375, 0.859375, 0.671875, + 0.75, 0.75, 0.75, 0.75, 0.75, 0.75, 0.84375, 0.734375, 0.6875, 0.6875, 0.6875, 0.6875, 0.5625, 0.609375, 0.640625, 0.59375, + 0.734375, 0.75, 0.734375, 0.734375, 0.734375, 0.734375, 0.734375, 0.6875, 0.765625, 0.71875, 0.71875, 0.71875, 0.71875, 0.75, 0.734375, 0.6875, + 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.796875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.5625, 0.609375, 0.625, 0.59375, + 0.6875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.703125, 0.703125, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.6875, 0.671875, +}, +0 +}; + +double extra_kerning[256*256] = {0}; + +pretty_font *current_font = &default_font; + +static int word_length(const char *text) +{ + int s = 1; + while(1) + { + if(*text == 0) + return s-1; + if(*text == '\n' || *text == '\t' || *text == ' ') + return s; + text++; + s++; + } +} + +float gfx_pretty_text_raw(float x, float y, float size, const char *text_, int length) +{ + const unsigned char *text = (unsigned char *)text_; + const float spacing = 0.05f; + gfx_texture_set(current_font->font_texture); + gfx_quads_begin(); + + if(length < 0) + length = strlen(text_); + + while(length) + { + const int c = *text; + text++; + + const float width = current_font->m_CharEndTable[c] - current_font->m_CharStartTable[c]; + + x -= size * current_font->m_CharStartTable[c]; + + gfx_quads_setsubset( + (c%16)/16.0f, // startx + (c/16)/16.0f, // starty + (c%16)/16.0f+1.0f/16.0f, // endx + (c/16)/16.0f+1.0f/16.0f); // endy + + gfx_quads_drawTL(x, y, size, size); + + double x_nudge = 0; + if(length > 1 && text[1]) + x_nudge = extra_kerning[text[0] + text[1] * 256]; + + x += (width + current_font->m_CharStartTable[c] + spacing + x_nudge) * size; + length--; + } + + gfx_quads_end(); + + return x; +} + +void gfx_pretty_text(float x, float y, float size, const char *text, int max_width) +{ + if(max_width == -1) + gfx_pretty_text_raw(x, y, size, text, -1); + else + { + float startx = x; + while(*text) + { + int wlen = word_length(text); + float w = gfx_pretty_text_width(size, text, wlen); + if(x+w-startx > max_width) + { + y += size-2; + x = startx; + } + + x = gfx_pretty_text_raw(x, y, size, text, wlen); + + text += wlen; + } + } +} + +float gfx_pretty_text_width(float size, const char *text_, int length) +{ + const float spacing = 0.05f; + float w = 0.0f; + const unsigned char *text = (unsigned char *)text_; + + const unsigned char *stop; + if (length == -1) + stop = text + strlen((char*)text); + else + stop = text + length; + + while (text < stop) + { + const int c = *text; + const float width = current_font->m_CharEndTable[c] - current_font->m_CharStartTable[c]; + + double x_nudge = 0; + if (text[1]) + x_nudge = extra_kerning[text[0] + text[1] * 256]; + + w += (width + spacing + x_nudge) * size; + + text++; + } + + return w; +} |