#define __BSD_VISIBLE 1 #include #include "render.h" #include "game.h" #include "util.h" #include void camera::set_fov(num fov) { this->fov = fov; float fov_radians = (float)fov * 3.14159 / 180; float sd = 1 / tanf(fov_radians / 2); this->_sd = num(sd); } void camera_track(struct camera *camera, struct player const *player) { camera->pos = player->pos() + player->up() * RENDER_EYE_HEIGHT; camera->pos.z -= RENDER_CAMERA_BACK_DISTANCE; camera->platform = player->platform; if(player->jump_dir && player->jump_t >= TIME_ROTATION / 2) camera->platform += player->jump_dir; num angle = player->world_angle(); camera->angle_vector = vec2(num_cos(-angle), num_sin(-angle)); } vec3 camera_project_point(struct camera *camera, vec3 u) { if(u.z < camera->pos.z + camera->near_plane()) return u; u = vec_rotate_around_z(u - camera->pos, camera->angle_vector); num f = camera->screen_distance() * (camera->screen_size.y / 2) / u.z; u.x = u.x * f + camera->screen_size.x / 2; u.y = -u.y * f + camera->screen_size.y / 2 ; return u; } void camera_project_prect(struct camera *camera, struct prect *p) { /* Require the rectangle to be already clipped */ if(p->nl.z < camera->pos.z + camera->near_plane()) return; vec2 half_screen(camera->screen_size.x / 2, camera->screen_size.y / 2); p->nl = vec_rotate_around_z(p->nl - camera->pos, camera->angle_vector); p->nr = vec_rotate_around_z(p->nr - camera->pos, camera->angle_vector); p->fl = vec_rotate_around_z(p->fl - camera->pos, camera->angle_vector); p->fr = vec_rotate_around_z(p->fr - camera->pos, camera->angle_vector); /* We assume nl/nr have the same z, and so do fl/fr */ num f = camera->screen_distance() * half_screen.y; num near_f = f / p->nl.z; num far_f = f / p->fl.z; p->nl.x = p->nl.x * near_f + half_screen.x; p->nl.y = -p->nl.y * near_f + half_screen.y; p->nr.x = p->nr.x * near_f + half_screen.x; p->nr.y = -p->nr.y * near_f + half_screen.y; p->fl.x = p->fl.x * far_f + half_screen.x; p->fl.y = -p->fl.y * far_f + half_screen.y; p->fr.x = p->fr.x * far_f + half_screen.x; p->fr.y = -p->fr.y * far_f + half_screen.y; } int camera_platform_color(struct camera const *camera, int platform_id) { /* Accounting for the full angle is too precise and results in weird color jumps at different times across platforms, instead switch at half movement. */ int dist = platform_id - camera->platform; if(dist < 0) dist += PLATFORM_COUNT; dist = std::min(dist, PLATFORM_COUNT - dist); int gray = 31 - 2 * dist; return C_RGB(gray, gray, gray); } //======= Rendering tools =======// static uint16_t blue_ramp[32]; static void split_RGB(uint32_t RGB, int *R, int *G, int *B) { *R = (RGB >> 16) & 0xff; *G = (RGB >> 8) & 0xff; *B = (RGB >> 0) & 0xff; } static uint32_t make_RGB(int R, int G, int B) { return ((R & 0xff) << 16) | ((G & 0xff) << 8) | (B & 0xff); } static void gradient(uint32_t RGB1, uint32_t RGB2, uint16_t *gradient, int N) { int R1, G1, B1, R2, G2, B2; split_RGB(RGB1, &R1, &G1, &B1); split_RGB(RGB2, &R2, &G2, &B2); for(int i = 0; i < N; i++) { int R = ((N-1-i) * R1 + i * R2) / (N-1); int G = ((N-1-i) * G1 + i * G2) / (N-1); int B = ((N-1-i) * B1 + i * B2) / (N-1); gradient[i] = RGB24(make_RGB(R, G, B)); } } void render_init(void) { /* Generate a gradient ramp for blue platforms */ gradient(0x5090d0, 0x2060b0, blue_ramp, 32); } uint16_t render_color_blue_platform(num t) { int step = (int)(32 * t.frac()) & 31; int phase = (int)t & 1; return phase ? blue_ramp[31 - step] : blue_ramp[step]; } uint16_t render_platform_color(struct platform const *p, struct camera const *camera, num t) { if(p->type == PLATFORM_WHITE) return camera_platform_color(camera, p->face); if(p->type == PLATFORM_BLUE) return render_color_blue_platform(t); if(p->type == PLATFORM_RED) return RGB24(0xd06060); return RGB24(0xff00ff); } void render_triangle(vec3 *p1, vec3 *p2, vec3 *p3, int color) { azrp_triangle( (int)p1->x, (int)p1->y, (int)p2->x, (int)p2->y, (int)p3->x, (int)p3->y, color); } void render_dots(std::initializer_list const &&points) { extern image_t img_dot; for(auto p: points) azrp_image((int)p.x - 2, (int)p.y - 2, &img_dot); } void render_anim_frame(int x, int y, struct anim *anim, int frame) { azrp_image( x + anim->x_offsets[frame] - anim->x_anchor, y + anim->y_offsets[frame] - anim->y_anchor, anim->frames[frame]); } static bool render_energy_str_glyph_params(int c, int *ix, int *w) { if(c >= '0' && c <= '9') { *ix = 13 * (c - '0') - (c >= '2' ? 3 : 0); *w = (c == '1') ? 8 : 11; return true; } if(c == '.') { *ix = 127; *w = 4; return true; } if(c == '%') { *ix = 132; *w = 8; return true; } return false; } void render_energy_str(int x, int y, int halign, char const *str) { extern image_t img_font_energy; int ix, w; /* First compute width of string */ int total_width = 0; for(int i = 0; str[i]; i++) { if(render_energy_str_glyph_params(str[i], &ix, &w)) total_width += w + 2; } total_width -= (total_width ? 2 : 0); /* Apply horizontal alignment */ if(halign == DTEXT_CENTER) x -= total_width >> 1; if(halign == DTEXT_RIGHT) x -= total_width - 1; /* Render string */ for(int i = 0; str[i]; i++) { if(!render_energy_str_glyph_params(str[i], &ix, &w)) continue; azrp_subimage(x, y, &img_font_energy, ix, 0, w, img_font_energy.height, DIMAGE_NONE); x += w + 2; } } void render_effect_bpp(image_t **image, num t) { enum { BPP_PARTICLE = 0x81 }; static uint16_t bpp_palette[] = { [0] = RGB24(0xff00ff), /* alpha */ [BPP_PARTICLE-0x80] = RGB24(0x5090d0), }; static int const bpp_W = 34; static int const bpp_H = 68; if(!*image) { *image = image_alloc(bpp_W, bpp_H, IMAGE_P8_RGB565A); image_set_palette(*image, bpp_palette, 1, false); } image_clear(*image); uint8_t *pixels = (uint8_t *)(*image)->data; for(int i = 0; i < 24; i++) { /* Particule #i appears after i 32-th of a second */ num start = num(i) / 32; if(t < start) continue; num16 local_time = num16(2 * ((t - start) % num(0.5))); int x0_int = (23 * i) % (bpp_W - 2); num16 x0 = num16(x0_int); num16 y0 = num16(bpp_H - 1 - ((37 * i) & 15)); num16 vx = num16(bpp_W / 2 - x0_int) / 4; num16 x = x0 + vx * local_time; num16 y = y0 - (bpp_H - 16) * local_time; for(int dy = 0; dy <= 3; dy++) for(int dx = 0; dx <= 3; dx++) { pixels[((int)y + dy) * (*image)->stride + (int)x + dx] = BPP_PARTICLE; } } }