summaryrefslogtreecommitdiff
path: root/src/renderer.c
diff options
context:
space:
mode:
authorPaweł Redman <pawel.redman@gmail.com>2016-03-30 08:25:04 +0200
committerPaweł Redman <pawel.redman@gmail.com>2016-03-30 08:25:04 +0200
commit4bf61f911194bb0b40d302df8d7dfff2fda19892 (patch)
tree1d8619d4cff047bf2a72aa6a1bcfe4e683f43226 /src/renderer.c
Initial commit.
Diffstat (limited to 'src/renderer.c')
-rw-r--r--src/renderer.c473
1 files changed, 473 insertions, 0 deletions
diff --git a/src/renderer.c b/src/renderer.c
new file mode 100644
index 0000000..11c3706
--- /dev/null
+++ b/src/renderer.c
@@ -0,0 +1,473 @@
+#define _RENDERER_C
+#include "common.h"
+#include <SDL2/SDL_image.h>
+
+#define FONT_FILE "assets/font.png"
+
+float r_color_white[4] = {1, 1, 1, 1};
+float r_color_black[4] = {0, 0, 0, 1};
+float r_color_red[4] = {1, 0, 0, 1};
+float r_color_blue[4] = {0, 0, 1, 1};
+
+static struct {
+ SDL_Surface *font;
+ float font_aspect_ratio;
+
+ r_window *windows;
+} rs;
+
+int r_init(void)
+{
+ memset(&rs, 0, sizeof(rs));
+
+ con_printf("r_init: initializing SDL...\n");
+ if (SDL_Init(SDL_INIT_VIDEO) == -1) {
+ con_printf("r_init: SDL_Init(SDL_INIT_VIDEO) failed: %s\n",
+ SDL_GetError());
+ goto error;
+ }
+
+ con_printf("r_init: initializing SDL_image...\n");
+ if (IMG_Init(IMG_INIT_JPG | IMG_INIT_PNG) !=
+ (IMG_INIT_JPG | IMG_INIT_PNG)) {
+ con_printf("r_init: IMG_Init failed: %s\n", SDL_GetError());
+ goto error;
+ }
+
+ SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "2");
+
+ con_printf("r_init: loading font \""FONT_FILE"\"...\n");
+ rs.font = IMG_Load("assets/font.png");
+ if (!rs.font) {
+ con_printf("r_init: IMG_Load failed: %s\n",
+ IMG_GetError());
+ goto error;
+ }
+
+ rs.font_aspect_ratio = (float)rs.font->w / rs.font->h;
+
+ con_printf("r_init: renderer init complete\n");
+ return 0;
+
+error:
+ r_quit();
+ return 1;
+}
+
+r_window *r_window_create(void)
+{
+ r_window *rw;
+
+ rw = calloc(1, sizeof(r_window));
+ if (!rw)
+ abort();
+
+ rw->prev = NULL;
+ if (rs.windows)
+ rs.windows->prev = rw;
+ rw->next = rs.windows;
+ rs.windows = rw;
+
+ rw->window = SDL_CreateWindow(PROGRAM_NAME, SDL_WINDOWPOS_CENTERED,
+ SDL_WINDOWPOS_CENTERED, 300, 300,
+ SDL_WINDOW_RESIZABLE);
+ if (!rw->window) {
+ con_printf("r_window_create: SDL_CreateWindow failed: %s\n",
+ SDL_GetError());
+ goto error;
+ }
+
+ rw->renderer = SDL_CreateRenderer(rw->window, -1,
+ SDL_RENDERER_ACCELERATED |
+ SDL_RENDERER_PRESENTVSYNC);
+ if (!rw->renderer) {
+ con_printf("r_window_create: SDL_CreateRenderer failed: %s\n",
+ SDL_GetError());
+ goto error;
+ }
+
+ SDL_SetRenderDrawBlendMode(rw->renderer, SDL_BLENDMODE_BLEND);
+
+ rw->font_texture = SDL_CreateTextureFromSurface(rw->renderer, rs.font);
+ if (!rw->font_texture) {
+ con_printf("r_window_create: SDL_CreateTextureFromSurface"
+ " failed: %s\n", SDL_GetError());
+ goto error;
+ }
+
+ con_printf("r_window_create: created window #%i\n",
+ SDL_GetWindowID(rw->window));
+
+ return rw;
+
+error:
+ r_window_destroy(rw);
+ return NULL;
+}
+
+void r_window_destroy(r_window *rw)
+{
+ SDL_DestroyTexture(rw->font_texture);
+ SDL_DestroyRenderer(rw->renderer);
+ SDL_DestroyWindow(rw->window);
+
+ if (rw->prev)
+ rw->prev->next = rw->next;
+ if (rw->next)
+ rw->next->prev = rw->prev;
+
+ if (rs.windows == rw)
+ rs.windows = rw->next;
+
+ free(rw);
+}
+
+
+void r_quit(void)
+{
+ con_printf("r_quit: renderer is quitting\n");
+
+ while (rs.windows)
+ r_window_destroy(rs.windows);
+
+ SDL_FreeSurface(rs.font);
+ IMG_Quit();
+}
+
+static void r_set_color(SDL_Renderer *renderer, float *color)
+{
+ SDL_SetRenderDrawColor(renderer, color[0] * 255, color[1] * 255,
+ color[2] * 255, color[3] * 255);
+}
+
+
+void r_clear(r_window *rw, float *color)
+{
+ r_set_color(rw->renderer, color);
+ SDL_RenderClear(rw->renderer);
+}
+
+void r_flip(r_window *rw)
+{
+ SDL_RenderPresent(rw->renderer);
+}
+
+void r_clip_enable(r_window *rw, float x, float y, float w, float h)
+{
+ SDL_Rect rect = {x, y, w, h};
+ SDL_RenderSetClipRect(rw->renderer, &rect);
+}
+
+void r_clip_disable(r_window *rw)
+{
+ SDL_RenderSetClipRect(rw->renderer, NULL);
+}
+
+void r_draw_line(r_window *rw, float x0, float y0, float x1, float y1,
+ float *color)
+{
+
+ r_set_color(rw->renderer, color);
+ SDL_RenderDrawLine(rw->renderer, x0, y0, x1, y1);
+}
+
+void r_draw_rect(r_window *rw, float x, float y, float w, float h, float *color)
+{
+ SDL_Rect rect = {x, y, w, h};
+ r_set_color(rw->renderer, color);
+ SDL_RenderFillRect(rw->renderer, &rect);
+}
+
+static int r_iterate_chars(char **p)
+{
+ int rv;
+
+ if ((*p)[0] == '\0') {
+ return -1;
+ } else if (((*p)[0] & 0b10000000) == 0b00000000) {
+ rv = (*p)[0];
+ (*p)++;
+ return rv;
+ } else if (((*p)[0] & 0b11100000) == 0b11000000) {
+ if ((*p)[1] == '\0')
+ return -1;
+
+ rv = (((*p)[0] & 0b00011111) << 6) | ((*p)[1] & 0b00111111);
+ (*p) += 2;
+ return rv;
+ } else {
+ (*p)++;
+ return 0;
+ }
+}
+
+static int r_font_find_glyph(int ch)
+{
+ if (ch < 128)
+ return ch;
+
+ switch (ch) {
+ case 0x105: return 128; // ą
+ case 0xE4: return 129; // ä
+ case 0x107: return 130; // ć
+ case 0x119: return 131; // ę
+ case 0xEB: return 132; // ë
+ case 0x142: return 133; // ł
+ case 0x144: return 134; // ń
+ case 0xF3: return 135; // ó
+ case 0xF6: return 136; // ö
+ case 0x15B: return 137; // ś
+ case 0xFC: return 138; // ü
+ case 0x17C: return 139; // ż
+ case 0x17A: return 140; // ź
+ //case 0x: return 141; // free
+ //case 0x: return 142; // free
+ //case 0x: return 143; // free
+ case 0x104: return 144; // Ą
+ case 0xC4: return 145; // Ä
+ case 0x106: return 146; // Ć
+ case 0x118: return 147; // Ę
+ case 0xCB: return 148; // Ë
+ case 0x141: return 149; // Ł
+ case 0x143: return 150; // Ń
+ case 0xD3: return 151; // Ó
+ case 0xD6: return 152; // Ö
+ case 0x15A: return 153; // Ś
+ case 0xDC: return 154; // Ü
+ case 0x17B: return 155; // Ż
+ case 0x179: return 156; // Ź
+ //case 0x: return 157; // free
+ //case 0x: return 158; // free
+ //case 0x: return 159; // free
+ }
+
+ // lowercase greek
+ if (ch >= 0x3B1 && ch <= 0x3C1)
+ return ch - 0x3B1 + 160;
+ else if (ch >= 0x3C3 && ch <= 0x3C9)
+ return ch - 0x3C3 + 177;
+
+ // uppercase greek
+ if (ch >= 0x391 && ch <= 0x3A1)
+ return ch - 0x391 + 192;
+ else if (ch >= 0x3A3 && ch <= 0x3A9)
+ return ch - 0x3A3 + 209;
+
+ return 0;
+}
+
+float r_text_width(float h, char *text)
+{
+ char *p = text;
+ float rv = 0;
+
+ while(r_iterate_chars(&p) != -1)
+ rv += h * rs.font_aspect_ratio;
+
+ return rv;
+}
+
+void r_draw_text(r_window *rw, float x, float y, float h, char *text,
+ float *color, int flags)
+{
+ char *p = text;
+ float w = h * rs.font_aspect_ratio;
+ int ch;
+ float total_width;
+
+ if (flags & (TEXT_CENTERX | TEXT_RIGHTX)) {
+ total_width = r_text_width(h, text);
+
+ if (flags & TEXT_CENTERX)
+ x -= total_width / 2;
+ else
+ x -= total_width;
+ }
+
+ SDL_SetTextureColorMod(rw->font_texture, color[0] * 255, color[1] * 255,
+ color[2] * 255);
+ SDL_SetTextureAlphaMod(rw->font_texture, color[3] * 255);
+
+ while ((ch = r_iterate_chars(&p)) != -1) {
+ int glyph, glyph_x, glyph_y;
+ SDL_Rect src, dst;
+
+ glyph = r_font_find_glyph(ch);
+ glyph_x = glyph % 16;
+ glyph_y = glyph / 16;
+ src.x = rs.font->w / 16 * glyph_x;
+ src.y = rs.font->h / 16 * glyph_y;
+ src.w = rs.font->w / 16;
+ src.h = rs.font->h / 16;
+
+ dst.x = x;
+ dst.y = y;
+ dst.w = w;
+ dst.h = h;
+
+ SDL_RenderCopy(rw->renderer, rw->font_texture, &src, &dst);
+
+ x += w;
+ }
+}
+
+static uint8_t r_float_to_u8(float num)
+{
+ if (num >= 1.0f)
+ return 255;
+ else if (num <= -1.0f)
+ return 0;
+ else
+ return floor((num + 1.0f) * 0.5f * 256.0f);
+}
+
+static int r_xsection_alloc(SDL_Renderer *renderer, SDL_Texture **texture,
+ size_t width, size_t height)
+{
+ int old_width, old_height;
+
+ if (*texture) {
+ SDL_QueryTexture(*texture, NULL, NULL, &old_width, &old_height);
+
+ if (old_width != width || old_height != height) {
+ SDL_DestroyTexture(*texture);
+ *texture = NULL;
+ }
+ }
+
+ if (!*texture) {
+ *texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_BGR888,
+ SDL_TEXTUREACCESS_STREAMING,
+ width, height);
+ if (!*texture) {
+ con_printf("r_xsection_alloc: SDL_CreateTexture failed"
+ ": %s\n", SDL_GetError());
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+void r_xsection_create(r_xsection *xsection)
+{
+ memset(xsection, 0, sizeof(r_xsection));
+}
+
+void r_xsection_destroy(r_xsection *xsection)
+{
+ SDL_DestroyTexture(xsection->texture);
+}
+
+int r_xsection_update(r_window *rw, r_xsection *xsection,
+ phy_field_info *fi, float *field,
+ r_xsection_type type, float frac)
+{
+ size_t width, height, x, y, z;
+ uint8_t *pixels;
+ int pitch;
+
+ switch (type) {
+ case XSECTION_XY:
+ width = fi->width;
+ height = fi->height;
+ break;
+
+ case XSECTION_XZ:
+ width = fi->width;
+ height = fi->depth;
+ break;
+
+ case XSECTION_YZ:
+ width = fi->height;
+ height = fi->depth;
+ break;
+ }
+
+ if (frac < 0.0f)
+ frac = 0.0f;
+ else if (frac > 1.0f)
+ frac = 1.0f;
+
+ if (r_xsection_alloc(rw->renderer, &xsection->texture,
+ width, height)) {
+ return 1;
+ }
+
+ SDL_LockTexture(xsection->texture, NULL, (void**)&pixels, &pitch);
+
+ switch (type) {
+ case XSECTION_XY:
+ z = frac * fi->depth;
+ if (z >= fi->depth)
+ z = fi->depth - 1;
+
+ for (y = 0; y < fi->height; y++)
+ for (x = 0; x < fi->width; x++) {
+ uint8_t *pixel;
+ float *point;
+
+ pixel = pixels + (y * pitch + x * 4);
+ point = field + z * fi->zstr + y * fi->ystr +
+ x * fi->xstr;
+
+ pixel[0] = r_float_to_u8(point[0]);
+ pixel[1] = r_float_to_u8(point[1]);
+ pixel[2] = r_float_to_u8(point[2]);
+ }
+ break;
+
+ case XSECTION_XZ:
+ y = frac * fi->height;
+ if (y >= fi->height)
+ y = fi->height - 1;
+
+ for (z = 0; z < fi->depth; z++)
+ for (x = 0; x < fi->width; x++) {
+ uint8_t *pixel;
+ float *point;
+
+ pixel = pixels + (z * pitch + x * 4);
+ point = field + z * fi->zstr + y * fi->ystr +
+ x * fi->xstr;
+
+ pixel[0] = r_float_to_u8(point[0]);
+ pixel[1] = r_float_to_u8(point[1]);
+ pixel[2] = r_float_to_u8(point[2]);
+ }
+ break;
+
+ case XSECTION_YZ:
+ x = frac * fi->width;
+ if (x >= fi->width)
+ x = fi->width - 1;
+
+ for (z = 0; z < fi->depth; z++)
+ for (y = 0; y < fi->height; y++) {
+ uint8_t *pixel;
+ float *point;
+
+ pixel = pixels + (z * pitch + y * 4);
+ point = field + z * fi->zstr + y * fi->ystr +
+ x * fi->xstr;
+
+ pixel[0] = r_float_to_u8(point[0]);
+ pixel[1] = r_float_to_u8(point[1]);
+ pixel[2] = r_float_to_u8(point[2]);
+ }
+ break;
+ }
+
+ SDL_UnlockTexture(xsection->texture);
+
+ xsection->aspect_ratio = (float)width / height;
+
+ return 0;
+}
+
+void r_xsection_draw(r_window *rw, r_xsection *xsection,
+ float x, float y, float w, float h)
+{
+ SDL_Rect dst = {x, y, w, h};
+ SDL_RenderCopy(rw->renderer, xsection->texture, NULL, &dst);
+}