#include "common.h" #include "renderer.h" #include #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) { if (!width || !height) { SDL_DestroyTexture(*texture); *texture = NULL; return 0; } SDL_QueryTexture(*texture, NULL, NULL, &old_width, &old_height); if (old_width != width || old_height != height) { SDL_DestroyTexture(*texture); *texture = NULL; } } if (!width || !height) return 0; 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_sim *sim, int flags, float frac) { phy_field_info *fi = &sim->field_info; float *field; size_t width, height, x[3], dx, dy, d0; uint8_t *pixels; int pitch; float scale; if (xsection->frame_index && xsection->frame_index == sim->frame_index && xsection->last_flags == flags && xsection->last_frac == frac) return 0; // nothing's changed if (flags & XSECTION_XY) { width = fi->dims[0]; height = fi->dims[1]; d0 = 2; dx = 0; dy = 1; } else if (flags & XSECTION_XZ) { width = fi->dims[0]; height = fi->dims[2]; d0 = 1; dx = 0; dy = 2; } else { width = fi->dims[1]; height = fi->dims[2]; d0 = 0; dx = 1; dy = 2; } if (flags & XSECTION_E) { field = sim->fields[1].E; scale = 1; } else { field = sim->fields[1].H; scale = 1; } 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; } if (!width || !height) return 0; SDL_LockTexture(xsection->texture, NULL, (void**)&pixels, &pitch); x[d0] = frac * fi->dims[d0]; if (x[d0] >= fi->dims[d0]) x[d0] = fi->dims[d0] - 1; if (flags & XSECTION_EPS) for (x[dy] = 0; x[dy] < fi->dims[dy]; x[dy]++) for (x[dx] = 0; x[dx] < fi->dims[dx]; x[dx]++) { uint8_t *pixel; float gray; phy_field_aux_point *aux; pixel = pixels + (x[dy] * pitch + x[dx] * 4); aux = sim->aux + x[2] * fi->zstr1 + x[1] * fi->ystr1 + x[0] * fi->xstr1; gray = remap(aux->eps, sim->eps_min, sim->eps_max, 0.0f, 1.0f); pixel[0] = pixel[1] = pixel[2] = r_float_to_u8(gray); } else for (x[dy] = 0; x[dy] < fi->dims[dy]; x[dy]++) for (x[dx] = 0; x[dx] < fi->dims[dx]; x[dx]++) { uint8_t *pixel; float *point; pixel = pixels + (x[dy] * pitch + x[dx] * 4); point = field + x[2] * fi->zstr + x[1] * fi->ystr + x[0] * fi->xstr; pixel[0] = r_float_to_u8(point[0] * scale); pixel[1] = r_float_to_u8(point[1] * scale); pixel[2] = r_float_to_u8(point[2] * scale); } SDL_UnlockTexture(xsection->texture); xsection->aspect_ratio = (float)width / height; xsection->last_flags = flags; xsection->last_frac = frac; xsection->frame_index = sim->frame_index; 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); }