Created
March 14, 2014 23:52
-
-
Save progschj/9559497 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "glterm.h" | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <math.h> | |
#include <GL/glew.h> | |
#include <SDL2/SDL.h> | |
#include <png.h> | |
#define PANEL_SIZE 256 | |
typedef struct quad_vertex_t { | |
float x, y, u, v; | |
GLubyte fg[4]; | |
GLubyte bg[4]; | |
} quad_vertex; | |
struct window_t { | |
int width, height; | |
double ssaa; | |
SDL_Window *sdlwindow; | |
SDL_GLContext glcontext; | |
GLuint shader_program; | |
GLuint shader_program2; | |
GLuint shader_program3; | |
GLuint vbo; | |
GLuint fgtex, bgtex; | |
GLuint fbo, rbf; | |
int quad_vbo_size; | |
GLuint quad_vbo; | |
quad_vertex *quad_buffer; | |
float transform[16]; | |
char fg[4*256*256]; | |
char bg[4*256*256]; | |
int mousex, mousey; | |
}* window = NULL; | |
struct tileset_t { | |
int imgwidth, imgheight, components; | |
int width, height; | |
char *data; | |
GLuint tex; | |
int refcount; | |
}; | |
static char* image_load(const char *file_name, int *width, int *height, int *components) { | |
char *data = NULL; | |
png_byte header[8]; | |
FILE *fp = fopen(file_name, "rb"); | |
png_structp png_ptr = NULL; | |
if(!fp) goto error; | |
if(fread(header, 1, 8, fp) != 8 || png_sig_cmp(header, 0, 8)) goto error; | |
png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); | |
if(!png_ptr) goto error; | |
png_infop info_ptr = png_create_info_struct(png_ptr); | |
if(!info_ptr) goto error; | |
if(setjmp(png_jmpbuf(png_ptr))) goto error; | |
png_init_io(png_ptr, fp); | |
png_set_sig_bytes(png_ptr, 8); | |
png_read_png(png_ptr, info_ptr, PNG_TRANSFORM_STRIP_16 | PNG_TRANSFORM_PACKING, NULL); | |
*width = png_get_image_width(png_ptr, info_ptr); | |
*height = png_get_image_height(png_ptr, info_ptr); | |
*components = png_get_channels(png_ptr,info_ptr); | |
png_bytep *rows = png_get_rows(png_ptr, info_ptr); | |
data = malloc(*width * *height * *components); | |
int i = 0; | |
for(int h = 0;h<*height;++h) { | |
for(int w = 0;w<*width * *components;++w) { | |
data[i++] = rows[h][w]; | |
} | |
} | |
error: | |
png_destroy_read_struct(&png_ptr, &info_ptr, NULL); | |
if(fp) fclose(fp); | |
return data; | |
} | |
static GLuint texture_create(char *data, int width, int height, int components) { | |
GLuint tex; | |
glGenTextures(1, &tex); | |
glBindTexture(GL_TEXTURE_2D, tex); | |
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); | |
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); | |
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); | |
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); | |
switch(components) { | |
case 1: | |
glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, width, height, 0, GL_RED, GL_UNSIGNED_BYTE, data); | |
break; | |
case 3: | |
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data); | |
break; | |
case 4: | |
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); | |
break; | |
} | |
return tex; | |
} | |
static int check_shader_compile_status(GLuint obj) { | |
GLint status; | |
glGetShaderiv(obj, GL_COMPILE_STATUS, &status); | |
if(status == GL_FALSE) { | |
GLint length; | |
glGetShaderiv(obj, GL_INFO_LOG_LENGTH, &length); | |
char *log = malloc(length); | |
glGetShaderInfoLog(obj, length, &length, &log[0]); | |
fprintf(stderr, "%s", log); | |
free(log); | |
return 0; | |
} | |
return 1; | |
} | |
static int check_program_link_status(GLuint obj) { | |
GLint status; | |
glGetProgramiv(obj, GL_LINK_STATUS, &status); | |
if(status == GL_FALSE) { | |
GLint length; | |
glGetProgramiv(obj, GL_INFO_LOG_LENGTH, &length); | |
char *log = malloc(length); | |
glGetProgramInfoLog(obj, length, &length, &log[0]); | |
fprintf(stderr, "%s", log); | |
free(log); | |
return 0; | |
} | |
return 1; | |
} | |
typedef struct attribbind_t { | |
const char *name; | |
int location; | |
} attribbind; | |
static GLuint shader_build(const char *tile_vertex_source, const char *tile_fragment_source, const attribbind *attribs) { | |
GLuint shader_program, vertex_shader, fragment_shader; | |
vertex_shader = glCreateShader(GL_VERTEX_SHADER); | |
glShaderSource(vertex_shader, 1, &tile_vertex_source, 0); | |
glCompileShader(vertex_shader); | |
if(!check_shader_compile_status(vertex_shader)) { | |
return 0; | |
} | |
fragment_shader = glCreateShader(GL_FRAGMENT_SHADER); | |
glShaderSource(fragment_shader, 1, &tile_fragment_source, 0); | |
glCompileShader(fragment_shader); | |
if(!check_shader_compile_status(fragment_shader)) { | |
return 0; | |
} | |
shader_program = glCreateProgram(); | |
glAttachShader(shader_program, vertex_shader); | |
glAttachShader(shader_program, fragment_shader); | |
glDeleteShader(vertex_shader); | |
glDeleteShader(fragment_shader); | |
while(attribs->name != NULL) { | |
glBindAttribLocation(shader_program, attribs->location, attribs->name); | |
++attribs; | |
} | |
glLinkProgram(shader_program); | |
if(!check_program_link_status(shader_program)) { | |
return 0; | |
} | |
return shader_program; | |
} | |
const char *tile_vertex_source = | |
"#version 120\n" | |
"uniform mat4 transform;\n" | |
"uniform vec2 panelsize;\n" | |
"void main() {\n" | |
" gl_TexCoord[0] = gl_MultiTexCoord0;\n" | |
" gl_Position = transform*(gl_Vertex*vec4(panelsize, 0, 1))*vec4(1,-1,1,1);\n" | |
"}\n"; | |
const char *tile_fragment_source = | |
"#version 120\n" | |
"uniform sampler2D font;\n" | |
"uniform sampler2D foreground;\n" | |
"uniform sampler2D background;\n" | |
"uniform vec2 panelsize;\n" | |
"uniform vec2 tilesize;\n" | |
"const float eps = 0.001;\n" | |
"void main() {\n" | |
" vec2 tilecoord = eps + (1.0-2.0*eps)*fract(gl_TexCoord[0].xy*panelsize);\n" | |
" vec4 fg = texture2D(foreground, gl_TexCoord[0].xy*panelsize/256,0);\n" | |
" vec4 bg = texture2D(background, gl_TexCoord[0].xy*panelsize/256,0);\n" | |
" vec2 tile = vec2(fg.w, bg.w)*255.;\n" | |
" vec4 glyph = texture2D(font, (tile.xy+tilecoord)*tilesize);\n" | |
" gl_FragColor.xyz = mix(bg.xyz, fg.xyz, glyph.xyz);\n" | |
" gl_FragColor.w = glyph.w;\n" | |
"}\n"; | |
const char *quad_vertex_source = | |
"#version 120\n" | |
"uniform mat4 transform;\n" | |
"attribute vec4 foreground;\n" | |
"attribute vec4 background;\n" | |
"varying vec4 fg;\n" | |
"varying vec4 bg;\n" | |
"void main() {\n" | |
" fg = foreground;\n" | |
" bg = background;\n" | |
" gl_TexCoord[0] = gl_MultiTexCoord0;\n" | |
" gl_Position = transform*gl_Vertex*vec4(1,-1,1,1);\n" | |
"}\n"; | |
const char *quad_fragment_source = | |
"#version 120\n" | |
"uniform sampler2D font;\n" | |
"varying vec4 fg;\n" | |
"varying vec4 bg;\n" | |
"void main() {\n" | |
" vec4 glyph = texture2D(font, gl_TexCoord[0].xy);\n" | |
" gl_FragColor.xyz = mix(bg.xyz, fg.xyz, glyph.xyz);\n" | |
" gl_FragColor.w = glyph.w;\n" | |
"}\n"; | |
const char *color_vertex_source = | |
"#version 120\n" | |
"uniform mat4 transform;\n" | |
"attribute vec4 incolor;\n" | |
"varying vec4 color;\n" | |
"void main() {\n" | |
" color = incolor;" | |
" gl_Position = transform*gl_Vertex;\n" | |
"}\n"; | |
const char *color_fragment_source = | |
"#version 120\n" | |
"varying vec4 color;\n" | |
"void main() {\n" | |
" gl_FragColor = color;\n" | |
"}\n"; | |
const GLfloat quad_data[] = { | |
1.0f, 1.0f, 1.0f, 1.0f, | |
0.0f, 1.0f, 0.0f, 1.0f, | |
1.0f, 0.0f, 1.0f, 0.0f, | |
1.0f, 0.0f, 1.0f, 0.0f, | |
0.0f, 1.0f, 0.0f, 1.0f, | |
0.0f, 0.0f, 0.0f, 0.0f, | |
}; | |
tileset tileset_load(int width, int height, const char *filename) { | |
tileset ts = malloc(sizeof(struct tileset_t)); | |
ts->refcount = 1; | |
ts->width = width; | |
ts->height = height; | |
ts->data = image_load(filename, &ts->imgwidth, &ts->imgheight, &ts->components); | |
if(ts->data == NULL) { | |
fprintf(stderr, "failed to load tileset"); | |
free(ts); | |
return NULL; | |
} | |
ts->tex = texture_create(ts->data, ts->imgwidth, ts->imgheight, ts->components); | |
return ts; | |
} | |
void tileset_retain(tileset ts) { | |
++(ts->refcount); | |
} | |
void tileset_destroy(tileset ts) { | |
if(--(ts->refcount) <= 0) { | |
glDeleteTextures(1, &ts->tex); | |
free(ts); | |
} | |
} | |
int window_create(const char *title, int width, int height, double ssaa) { | |
window = malloc(sizeof(struct window_t)); | |
window->width = width; | |
window->height = height; | |
window->ssaa = ssaa; | |
window_transform_identity(); | |
if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS) < 0) { | |
fprintf(stderr, "failed to init SDL"); | |
return 1; | |
} | |
if((window->sdlwindow = SDL_CreateWindow(title, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, width, height, SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN)) == 0) { | |
fprintf(stderr, "failed to open window"); | |
SDL_Quit(); | |
free(window); | |
window = NULL; | |
return 1; | |
} | |
SDL_GetMouseState(&window->mousex, &window->mousey); | |
window->glcontext = SDL_GL_CreateContext(window->sdlwindow); | |
if(glewInit()) { | |
fprintf(stderr, "failed to init GLEW"); | |
SDL_GL_DeleteContext(window->glcontext); | |
SDL_DestroyWindow(window->sdlwindow); | |
SDL_Quit(); | |
free(window); | |
window = NULL; | |
return 1; | |
} | |
attribbind attribs[] = { | |
{NULL, 0} | |
}; | |
window->shader_program = shader_build(tile_vertex_source, tile_fragment_source, attribs); | |
glUseProgram(window->shader_program); | |
glUniform1i(glGetUniformLocation(window->shader_program, "font"), 0); | |
glUniform1i(glGetUniformLocation(window->shader_program, "foreground"), 1); | |
glUniform1i(glGetUniformLocation(window->shader_program, "background"), 2); | |
window->shader_program3 = shader_build(color_vertex_source, color_fragment_source, attribs); | |
glUseProgram(window->shader_program3); | |
attribbind attribs2[] = { | |
{"foreground", 1}, | |
{"background", 2}, | |
{NULL, 0} | |
}; | |
window->shader_program2 = shader_build(quad_vertex_source, quad_fragment_source, attribs2); | |
glUseProgram(window->shader_program2); | |
glUniform1i(glGetUniformLocation(window->shader_program2, "font"), 0); | |
glGenBuffers(1, &window->vbo); | |
glBindBuffer(GL_ARRAY_BUFFER, window->vbo); | |
glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat)*6*4, quad_data, GL_STATIC_DRAW); | |
window->quad_vbo_size = 1024; | |
glGenBuffers(1, &window->quad_vbo); | |
glBindBuffer(GL_ARRAY_BUFFER, window->quad_vbo); | |
glBufferData(GL_ARRAY_BUFFER, sizeof(quad_vertex)*6*window->quad_vbo_size, 0, GL_STREAM_DRAW); | |
window->quad_buffer = malloc(sizeof(quad_vertex)*6*window->quad_vbo_size); | |
glActiveTexture(GL_TEXTURE1); | |
glGenTextures(1, &window->fgtex); | |
glBindTexture(GL_TEXTURE_2D, window->fgtex); | |
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); | |
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); | |
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); | |
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); | |
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, PANEL_SIZE, PANEL_SIZE, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); | |
glActiveTexture(GL_TEXTURE2); | |
glGenTextures(1, &window->bgtex); | |
glBindTexture(GL_TEXTURE_2D, window->bgtex); | |
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); | |
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); | |
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); | |
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); | |
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, PANEL_SIZE, PANEL_SIZE, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); | |
glGenFramebuffers(1, &window->fbo); | |
glBindFramebuffer(GL_FRAMEBUFFER, window->fbo); | |
glGenRenderbuffers(1, &window->rbf); | |
glBindRenderbuffer(GL_RENDERBUFFER, window->rbf); | |
glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, ssaa*width, ssaa*height); | |
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, window->rbf); | |
glViewport(0, 0, ssaa*width, ssaa*height); | |
return 0; | |
} | |
void window_destroy() { | |
if(window != NULL) { | |
glDeleteTextures(1, &window->fgtex); | |
glDeleteTextures(1, &window->bgtex); | |
glDeleteBuffers(1, &window->vbo); | |
glDeleteBuffers(1, &window->quad_vbo); | |
free(window->quad_buffer); | |
glDeleteProgram(window->shader_program); | |
glDeleteProgram(window->shader_program2); | |
SDL_GL_DeleteContext(window->glcontext); | |
SDL_DestroyWindow(window->sdlwindow); | |
SDL_Quit(); | |
free(window); | |
window = NULL; | |
} | |
} | |
#define RED_FLOAT(c) (((c>>16u) & 0xFFu)/255.0f) | |
#define GREEN_FLOAT(c) (((c>>8u) & 0xFFu)/255.0f) | |
#define BLUE_FLOAT(c) (((c>>0u) & 0xFFu)/255.0f) | |
void window_clear(uint32_t color) { | |
glClearColor(RED_FLOAT(color), GREEN_FLOAT(color), BLUE_FLOAT(color), 1.0f); | |
glClear(GL_COLOR_BUFFER_BIT); | |
} | |
void window_blit_tiles(tileset ts, tile *tiles, int width, int height, mode md) { | |
if(md == OPAQUE) { | |
glDisable(GL_BLEND); | |
} else if(md == ALPHA_BLEND) { | |
glEnable(GL_BLEND); | |
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); | |
} else if(ADD){ | |
glEnable(GL_BLEND); | |
glBlendFunc(GL_ONE, GL_ONE); | |
} | |
//~ float aspect = (ts->imgwidth/(float)ts->width)/(ts->imgheight/(float)ts->height); | |
for(int i = 0;i<width*height;++i) { | |
uint32_t fg = tiles[i].foreground; | |
uint32_t bg = tiles[i].background; | |
uint32_t idx = tiles[i].index; | |
window->fg[4*i + 0] = (fg>>16) & 0xFF; | |
window->fg[4*i + 1] = (fg>> 8) & 0xFF; | |
window->fg[4*i + 2] = (fg>> 0) & 0xFF; | |
window->fg[4*i + 3] = idx % ts->width; | |
window->bg[4*i + 0] = (bg>>16) & 0xFF; | |
window->bg[4*i + 1] = (bg>> 8) & 0xFF; | |
window->bg[4*i + 2] = (bg>> 0) & 0xFF; | |
window->bg[4*i + 3] = idx / ts->width; | |
} | |
glActiveTexture(GL_TEXTURE1); | |
glBindTexture(GL_TEXTURE_2D, window->fgtex); | |
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, window->fg); | |
glActiveTexture(GL_TEXTURE2); | |
glBindTexture(GL_TEXTURE_2D, window->bgtex); | |
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, window->bg); | |
glActiveTexture(GL_TEXTURE0); | |
glBindTexture(GL_TEXTURE_2D, ts->tex); | |
glUseProgram(window->shader_program); | |
glUniformMatrix4fv(glGetUniformLocation(window->shader_program, "transform"), 1, GL_FALSE, window->transform); | |
glUniform2f(glGetUniformLocation(window->shader_program, "panelsize"), width, height); | |
glUniform2f(glGetUniformLocation(window->shader_program, "tilesize"), 1.0f/ts->width, 1.0f/ts->height); | |
glBindBuffer(GL_ARRAY_BUFFER, window->vbo); | |
glEnableClientState(GL_VERTEX_ARRAY); | |
glVertexPointer(2, GL_FLOAT, 4*sizeof(GLfloat), (char*)0 + 0*sizeof(GLfloat)); | |
glEnableClientState(GL_TEXTURE_COORD_ARRAY); | |
glTexCoordPointer(2, GL_FLOAT, 4*sizeof(GLfloat), (char*)0 + 2*sizeof(GLfloat)); | |
glDrawArrays(GL_TRIANGLES, 0, 6); | |
} | |
void window_draw_quads(tileset ts, quad *quads, int amount, mode md) { | |
if(md == OPAQUE) { | |
glDisable(GL_BLEND); | |
} else if(md == ALPHA_BLEND) { | |
glEnable(GL_BLEND); | |
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); | |
} else if(ADD){ | |
glEnable(GL_BLEND); | |
glBlendFunc(GL_ONE, GL_ONE); | |
} | |
float aspect = (ts->imgwidth/(float)ts->width)/(ts->imgheight/(float)ts->height); | |
glUseProgram(window->shader_program2); | |
glActiveTexture(GL_TEXTURE0); | |
glBindTexture(GL_TEXTURE_2D, ts->tex); | |
glUseProgram(window->shader_program2); | |
glUniformMatrix4fv(glGetUniformLocation(window->shader_program2, "transform"), 1, GL_FALSE, window->transform); | |
float x[] = {-0.5f, 0.5f, 0.5f,-0.5f, 0.5f,-0.5f}; | |
float y[] = { 0.5f, 0.5f,-0.5f, 0.5f,-0.5f,-0.5f}; | |
const float eps = 0.001; | |
float u[] = { 0.0f+eps, 1.0f-eps, 1.0f-eps, 0.0f+eps, 1.0f-eps, 0.0f+eps}; | |
float v[] = { 1.0f-eps, 1.0f-eps, 0.0f+eps, 1.0f-eps, 0.0f+eps, 0.0f+eps}; | |
float w = 1.0f/ts->width; | |
float h = 1.0f/ts->height; | |
quad_vertex *data = window->quad_buffer; | |
for(int i = 0;i<amount;++i) { | |
uint32_t idx = quads[i].index; | |
uint32_t fg = quads[i].foreground; | |
uint32_t bg = quads[i].background; | |
float c = cos(quads[i].angle) * quads[i].scale; | |
float s = sin(quads[i].angle) * quads[i].scale; | |
float u0 = (idx % ts->width)*w; | |
float v0 = (idx / ts->width)*h; | |
for(int j = 0;j<6;++j) { | |
data[6*i + j].x = quads[i].x + c*x[j]*aspect + s*y[j]; | |
data[6*i + j].y = quads[i].y - s*x[j]*aspect + c*y[j]; | |
data[6*i + j].u = u0+w*u[j]; | |
data[6*i + j].v = v0+h*v[j]; | |
data[6*i + j].fg[0] = (fg>>16) & 0xFF; | |
data[6*i + j].fg[1] = (fg>> 8) & 0xFF; | |
data[6*i + j].fg[2] = (fg>> 0) & 0xFF; | |
data[6*i + j].fg[3] = 0; | |
data[6*i + j].bg[0] = (bg>>16) & 0xFF; | |
data[6*i + j].bg[1] = (bg>> 8) & 0xFF; | |
data[6*i + j].bg[2] = (bg>> 0) & 0xFF; | |
data[6*i + j].bg[3] = 0; | |
} | |
} | |
glBindBuffer(GL_ARRAY_BUFFER, window->quad_vbo); | |
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(quad_vertex)*6*amount, data); | |
glEnableClientState(GL_VERTEX_ARRAY); | |
glVertexPointer(2, GL_FLOAT, sizeof(quad_vertex), (char*)0 + 0*sizeof(GLfloat)); | |
glEnableClientState(GL_TEXTURE_COORD_ARRAY); | |
glTexCoordPointer(2, GL_FLOAT, sizeof(quad_vertex), (char*)0 + 2*sizeof(GLfloat)); | |
int fgattr = 1; | |
int bgattr = 2; | |
glEnableVertexAttribArray(fgattr); | |
glVertexAttribPointer(fgattr, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(quad_vertex), (char*)0 + 4*sizeof(GLfloat)); | |
glEnableVertexAttribArray(bgattr); | |
glVertexAttribPointer(bgattr, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(quad_vertex), (char*)0 + 5*sizeof(GLfloat)); | |
glDrawArrays(GL_TRIANGLES, 0, 6*amount); | |
glEnableVertexAttribArray(bgattr); | |
glDisableVertexAttribArray(fgattr); | |
} | |
static void mul3(float *A, float *B, float *C) { | |
for(int i = 0;i<4;++i) { | |
for(int j = 0;j<4;++j) { | |
float dot = 0; | |
for(int k = 0;k<4;++k) { | |
dot += B[i+4*k]*C[k+4*j]; | |
} | |
A[i+4*j] = dot; | |
} | |
} | |
} | |
static void mul2(float *A, float *C) { | |
float B[16]; | |
for(int i = 0;i<16;++i) { | |
B[i] = A[i]; | |
} | |
mul3(A,B,C); | |
} | |
void window_transform_set(float *transform) { | |
for(int i = 0;i<16;++i) { | |
window->transform[i] = transform[i]; | |
} | |
} | |
void window_transform_get(float *transform) { | |
for(int i = 0;i<16;++i) { | |
transform[i] = window->transform[i]; | |
} | |
} | |
void window_transform_multiply(float *transform) { | |
mul2(window->transform, transform); | |
} | |
void window_transform_identity() { | |
window->transform[0] = 1.0f; window->transform[4] = 0.0f; window->transform[8] = 0.0f; window->transform[12] = 0.0f; | |
window->transform[1] = 0.0f; window->transform[5] = 1.0f; window->transform[9] = 0.0f; window->transform[13] = 0.0f; | |
window->transform[2] = 0.0f; window->transform[6] = 0.0f; window->transform[10] = 1.0f; window->transform[14] = 0.0f; | |
window->transform[3] = 0.0f; window->transform[7] = 0.0f; window->transform[11] = 0.0f; window->transform[15] = 1.0f; | |
} | |
void window_transform_scale(double sx, double sy) { | |
float transform[16] = { | |
sx, 0, 0, 0, | |
0, sy, 0, 0, | |
0, 0, 1, 0, | |
0, 0, 0, 1, | |
}; | |
window_transform_multiply(transform); | |
} | |
void window_transform_translate(double dx, double dy) { | |
float transform[16] = { | |
1, 0, 0, 0, | |
0, 1, 0, 0, | |
0, 0, 1, 0, | |
dx, dy, 0, 1, | |
}; | |
window_transform_multiply(transform); | |
} | |
void window_transform_rotate(double angle) { | |
float c = cos(angle); | |
float s = sin(angle); | |
float transform[16] = { | |
c, -s, 0, 0, | |
s, c, 0, 0, | |
0, 0, 1, 0, | |
0, 0, 0, 1, | |
}; | |
window_transform_multiply(transform); | |
} | |
void window_show() { | |
glBindFramebuffer(GL_READ_FRAMEBUFFER, window->fbo); | |
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); | |
glBlitFramebuffer( | |
0, 0, window->ssaa*window->width, window->ssaa*window->height, | |
0, 0, window->width, window->height, | |
GL_COLOR_BUFFER_BIT, window->ssaa<1?GL_NEAREST:GL_LINEAR | |
); | |
SDL_GL_SwapWindow(window->sdlwindow); | |
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, window->fbo); | |
} | |
event window_poll_event(int wait) { | |
event e; | |
SDL_Event event; | |
while(wait?SDL_WaitEvent(&event):SDL_PollEvent(&event)) { | |
switch(event.type) { | |
case SDL_KEYDOWN: | |
e.type = KEYBOARD_KEY_DOWN; | |
e.value = event.key.keysym.sym; | |
e.x = window->mousex; | |
e.y = window->mousey; | |
return e; | |
case SDL_KEYUP: | |
e.type = KEYBOARD_KEY_UP; | |
e.value = event.key.keysym.sym; | |
e.x = window->mousex; | |
e.y = window->mousey; | |
return e; | |
case SDL_MOUSEBUTTONDOWN: | |
e.type = MOUSE_BUTTON_DOWN; | |
e.value = event.button.button; | |
e.x = window->mousex = event.button.x; | |
e.y = window->mousey = event.button.y; | |
return e; | |
case SDL_MOUSEBUTTONUP: | |
e.type = MOUSE_BUTTON_UP; | |
e.value = event.button.button; | |
e.x = window->mousex = event.button.x; | |
e.y = window->mousey = event.button.y; | |
return e; | |
case SDL_MOUSEMOTION: | |
e.type = MOUSE_MOTION; | |
e.value = 0; | |
e.x = window->mousex = event.motion.x; | |
e.y = window->mousey = event.motion.y; | |
return e; | |
case SDL_WINDOWEVENT: | |
if(event.window.event == SDL_WINDOWEVENT_CLOSE) { | |
e.type = WINDOW_CLOSE; | |
e.value = 0; | |
e.x = window->mousex; | |
e.y = window->mousey; | |
return e; | |
} else { | |
break; | |
} | |
default: break; | |
} | |
} | |
e.type = NO_EVENT; | |
return e; | |
} | |
double window_get_seconds() { | |
return SDL_GetPerformanceCounter()/(double)SDL_GetPerformanceFrequency(); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#ifndef GLTERM_H | |
#define GLTERM_H | |
#include <stdint.h> | |
typedef struct tileset_t* tileset; | |
typedef enum { | |
OPAQUE, ALPHA_BLEND, ADD, | |
} mode; | |
typedef struct tile_t { | |
uint32_t index; | |
uint32_t foreground; | |
uint32_t background; | |
} tile; | |
typedef struct quad_t { | |
uint32_t index; | |
uint32_t foreground; | |
uint32_t background; | |
double angle, scale, x, y; | |
} quad; | |
typedef enum { | |
NO_EVENT, WINDOW_CLOSE, | |
KEYBOARD_KEY_DOWN, KEYBOARD_KEY_UP, | |
MOUSE_BUTTON_DOWN, MOUSE_BUTTON_UP, | |
MOUSE_MOTION | |
} event_type; | |
enum { | |
KEY_RETURN = '\r', | |
KEY_ESCAPE = '\033', | |
KEY_BACKSPACE = '\b', | |
KEY_TAB = '\t', | |
KEY_SPACE = ' ', | |
KEY_MASK = (1<<30), | |
KEY_F1 = 58 | KEY_MASK, | |
KEY_F2 = 59 | KEY_MASK, | |
KEY_F3 = 60 | KEY_MASK, | |
KEY_F4 = 61 | KEY_MASK, | |
KEY_F5 = 62 | KEY_MASK, | |
KEY_F6 = 63 | KEY_MASK, | |
KEY_F7 = 64 | KEY_MASK, | |
KEY_F8 = 65 | KEY_MASK, | |
KEY_F9 = 66 | KEY_MASK, | |
KEY_F10 = 67 | KEY_MASK, | |
KEY_F11 = 68 | KEY_MASK, | |
KEY_F12 = 69 | KEY_MASK, | |
KEY_HOME = 74 | KEY_MASK, | |
KEY_PAGEUP = 75 | KEY_MASK, | |
KEY_DELETE = 76 | KEY_MASK, | |
KEY_END = 77 | KEY_MASK, | |
KEY_PAGEDOWN = 78 | KEY_MASK, | |
KEY_RIGHT = 79 | KEY_MASK, | |
KEY_LEFT = 80 | KEY_MASK, | |
KEY_DOWN = 81 | KEY_MASK, | |
KEY_UP = 82 | KEY_MASK, | |
KEY_KP_DIVIDE = 84 | KEY_MASK, | |
KEY_KP_MULTIPLY = 85 | KEY_MASK, | |
KEY_KP_MINUS = 86 | KEY_MASK, | |
KEY_KP_PLUS = 87 | KEY_MASK, | |
KEY_KP_ENTER = 88 | KEY_MASK, | |
KEY_KP_1 = 89 | KEY_MASK, | |
KEY_KP_2 = 90 | KEY_MASK, | |
KEY_KP_3 = 91 | KEY_MASK, | |
KEY_KP_4 = 92 | KEY_MASK, | |
KEY_KP_5 = 93 | KEY_MASK, | |
KEY_KP_6 = 94 | KEY_MASK, | |
KEY_KP_7 = 95 | KEY_MASK, | |
KEY_KP_8 = 96 | KEY_MASK, | |
KEY_KP_9 = 97 | KEY_MASK, | |
KEY_KP_0 = 98 | KEY_MASK, | |
KEY_KP_PERIOD = 99 | KEY_MASK, | |
KEY_LCTRL = 224 | KEY_MASK, | |
KEY_LSHIFT = 225 | KEY_MASK, | |
KEY_LALT = 226 | KEY_MASK, | |
KEY_LGUI = 227 | KEY_MASK, | |
KEY_RCTRL = 228 | KEY_MASK, | |
KEY_RSHIFT = 229 | KEY_MASK, | |
KEY_RALT = 230 | KEY_MASK, | |
KEY_RGUI = 231 | KEY_MASK, | |
}; | |
typedef struct event_t { | |
event_type type; | |
unsigned int value; | |
int x, y; | |
} event; | |
// loads a tileset of widthxheight tiles from a png image and sets | |
// the reference count to 1 | |
tileset tileset_load(int width, int height, const char *filename); | |
// increases the reference count | |
void tileset_retain(tileset ts); | |
// decreases the reference count and frees the tileset when the count is zero | |
void tileset_destroy(tileset ts); | |
// creates the window with size width x height | |
int window_create(const char *title, int width, int height, double ssaa); | |
// destroys the window | |
void window_destroy(); | |
// clears the window to the specified color | |
void window_clear(uint32_t color); | |
// draws a rectangle of xtiles * ytiles tiles as specified by the tiles | |
// array in row major order | |
// the mode parameter specifies the blend mode | |
// OPAQUE: overwrite previous content | |
// ALPHA_BLEND: alpha transparency (alpha from tileset image) | |
// ADD: additive blend | |
void window_blit_tiles(tileset ts, tile *tiles, int xtiles, int ytiles, mode md); | |
// draws an array of quads with there individual transforms applied before | |
// the window transform | |
void window_draw_quads(tileset ts, quad *quads, int amount, mode md); | |
// window transform manipulation functions modify the internal 4x4 | |
// column major transformation matrix. | |
// set copies 16 elements from the argument to the window transform matrix | |
void window_transform_set(float *transform); | |
// get copies 16 elements from the window transform matrix to the argument | |
void window_transform_get(float *transform); | |
// multiplies the window transform with transform | |
void window_transform_multiply(float *transform); | |
// set the window transform to the identity matrix | |
void window_transform_identity(); | |
// multiply the window transform with a scale matrix | |
void window_transform_scale(double sx, double sy); | |
// multiply the window transform with a translation matrix | |
void window_transform_translate(double sx, double sy); | |
// multiply the window transform with a rotation matrix (angle in radians) | |
void window_transform_rotate(double angle); | |
// swap backbuffer | |
void window_show(); | |
// polls for events. wait = 1 will block until a event is received. | |
// returns a NO_EVENT event in case there are no events in the queue | |
event window_poll_event(int wait); | |
// get timer value in seconds | |
double window_get_seconds(); | |
#endif |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment