Skip to content

Instantly share code, notes, and snippets.

@progschj
Last active August 29, 2015 13:56
Show Gist options
  • Save progschj/8973686 to your computer and use it in GitHub Desktop.
Save progschj/8973686 to your computer and use it in GitHub Desktop.
#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;
SDL_Window *sdlwindow;
SDL_GLContext glcontext;
GLuint shader_program;
GLuint shader_program2;
GLuint vbo;
GLuint fgtex, bgtex;
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"
"void main() {\n"
" gl_TexCoord[0] = gl_MultiTexCoord0;\n"
" gl_Position = transform*gl_Vertex;\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"
"uniform vec2 texsize;\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*texsize);\n"
" vec4 bg = texture2D(background, gl_TexCoord[0].xy*texsize);\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;\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 GLfloat quad_data[] = {
1.0f, 1.0f, 1.0f, 0.0f,
-1.0f, 1.0f, 0.0f, 0.0f,
1.0f, -1.0f, 1.0f, 1.0f,
1.0f, -1.0f, 1.0f, 1.0f,
-1.0f, 1.0f, 0.0f, 0.0f,
-1.0f, -1.0f, 0.0f, 1.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) {
window = malloc(sizeof(struct window_t));
window->width = width;
window->height = height;
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);
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);
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);
}
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./ts->width, 1./ts->height);
glUniform2f(glGetUniformLocation(window->shader_program, "texsize"), width/256., height/256.);
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);
}
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[] = {-1.0f, 1.0f, 1.0f,-1.0f, 1.0f,-1.0f};
float y[] = {-1.0f,-1.0f, 1.0f,-1.0f, 1.0f, 1.0f};
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] + s*y[j];
data[6*i + j].y = quads[i].y - s*x[j] + 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_rect(double x, double y, double width, double height) {
double x0 = x, y0 = y, x1 = x+width, y1 = y+height;
x0 = 2*x0-1; x1 = 2*x1-1; y0 = 1-2*y0; y1 = 1-2*y1;
window->transform[0] = 0.5*(x1-x0); window->transform[4] = 0.0f; window->transform[8] = 0.0f; window->transform[12] = 0.5*(x0+x1);
window->transform[1] = 0.0f; window->transform[5] = 0.5*(y0-y1); window->transform[9] = 0.0f; window->transform[13] = 0.5*(y0+y1);
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() {
SDL_GL_SwapWindow(window->sdlwindow);
}
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;
}
#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);
// 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);
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();
// set the window transform such that the rect [-1,1]x[-1,1] gets
// transformed to the rect specified by x,y,width and height in normalized
// window coordinates
void window_transform_rect(double x, double y, double width, double height);
// 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);
#endif
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment