BosonX/src/render.cpp

260 lines
7.0 KiB
C++

#define __BSD_VISIBLE 1
#include <math.h>
#include "render.h"
#include "game.h"
#include "util.h"
#include <azur/gint/render.h>
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<vec3> 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;
}
}
}