Duet/src/render.c

228 lines
6.2 KiB
C

#include "duet.h"
#include <stdlib.h>
/* Extreme hacks for intersecting circles; we draw a pixel as [color], but if
there is already [rep1] on that pixel we turn it into [rep2], and if we see
[rep2] we don't paint over it */
void dcircle_pixel(int x, int y, int color, int rep1, int rep2)
{
if(x < 0 || x >= DWIDTH || y < 0 || y >= DHEIGHT)
return;
int i = DWIDTH * y + x;
if(gint_vram[i] == rep2)
return;
gint_vram[i] = (gint_vram[i] == rep1) ? rep2 : color;
}
/* Bresenham algorithm */
void dcircle(int cx, int cy, int r0, int color, bool fill, int rep1, int rep2)
{
int x=-r0, y=0, e=2-2*r0, r=r0;
do {
dcircle_pixel(cx-x, cy-y, color, rep1, rep2);
dcircle_pixel(cx+y, cy-x, color, rep1, rep2);
dcircle_pixel(cx+x, cy+y, color, rep1, rep2);
dcircle_pixel(cx-y, cy+x, color, rep1, rep2);
if(fill) {
for(int sx = cx+x; sx <= cx-x; sx++) {
dcircle_pixel(sx, cy-y, color, rep1, rep2);
dcircle_pixel(sx, cy+y, color, rep1, rep2);
}
}
r = e;
if(r <= y)
e += (++y * 2) + 1;
if(r > x || e > y)
e += (++x * 2) + 1;
}
while(x < 0);
}
/* Blend to white (slow) */
static uint16_t blend(uint16_t bg, uint16_t fg, int opacity)
{
int R_bg = (bg >> 11);
int G_bg = (bg >> 6) & 0x1f;
int B_bg = bg & 0x1f;
int R_fg = (fg >> 11);
int G_fg = (fg >> 6) & 0x1f;
int B_fg = fg & 0x1f;
int R = (R_fg * opacity + R_bg * (256 - opacity)) >> 8;
int G = (G_fg * opacity + G_bg * (256 - opacity)) >> 8;
int B = (B_fg * opacity + B_bg * (256 - opacity)) >> 8;
return C_RGB(R, G, B);
}
/* From Windmill::render_triangle_black */
static int edge_start(int x1, int y1, int x2, int y2, int px, int py)
{
return (y2 - y1) * (px - x1) - (x2 - x1) * (py - y1);
}
static int min(int x, int y)
{
return (x < y) ? x : y;
}
static int max(int x, int y)
{
return (x > y) ? x : y;
}
void dtriangle(int x1, int y1, int x2, int y2, int x3, int y3, int color,
int opacity)
{
// calcul du rectangle circonscrit au triangle
int min_x = max(0, min(x1, min(x2, x3)));
int max_x = min(DWIDTH-1, max(x1, max(x2, x3)));
int min_y = max(0, min(y1, min(y2, y3)));
int max_y = min(DHEIGHT-1, max(y1, max(y2, y3)));
// calcul des produits vectoriels
int u0_start = edge_start(x2, y2, x3, y3, min_x, min_y);
int u0_step_x = y3 - y2;
int u0_step_y = x2 - x3;
int u1_start = edge_start(x3, y3, x1, y1, min_x, min_y);
int u1_step_x = y1 - y3;
int u1_step_y = x3 - x1;
int u2_start = edge_start(x1, y1, x2, y2, min_x, min_y);
int u2_step_x = y2 - y1;
int u2_step_y = x1 - x2;
int u0, u1, u2;
// parcours en ligne
for(int x=min_x; x<=max_x; x++)
{
u0 = u0_start; u1 = u1_start; u2 = u2_start;
// parcours en colonne
for(int y=min_y; y<=max_y; y++)
{
// si le pixel (x;y) est dans le triangle
if ((u0 | u1 | u2) > 0)
{
int i = DWIDTH * y + x;
if(opacity > 0xff)
gint_vram[i] = color;
else
gint_vram[i] = blend(gint_vram[i], color, opacity);
}
u0 += u0_step_y; u1 += u1_step_y; u2 += u2_step_y;
}
u0_start += u0_step_x; u1_start += u1_step_x; u2_start += u2_step_x;
}
}
void drectoid(rect_t const *r, float extra_size, int color)
{
if(r->opacity == 0)
return;
float w = r->w + 2 * extra_size;
float h = r->h + 2 * extra_size;
if(r->x - max(w,h)/2 > DWIDTH || r->x + max(w,h)/2 < 0)
return;
float x0[4] = { w/2, -w/2, -w/2, w/2 };
float y0[4] = { -h/2, -h/2, h/2, h/2 };
if(r->r == 0) {
int x1=r->x+x0[1], x2=r->x+x0[3], y1=r->y+y0[1], y2=r->y+y0[3];
if(r->opacity > 0xff)
drect(x1, y1, x2, y2, color);
else {
y1 = max(y1, 0);
y2 = min(y2, DHEIGHT-1);
x1 = max(x1, 0);
x2 = min(x2, DWIDTH-1);
for(int y = y1; y <= y2; y++)
for(int x = x1; x <= x2; x++) {
int i = y * DWIDTH + x;
gint_vram[i] = blend(gint_vram[i], color, r->opacity);
}
}
return;
}
float sin, cos;
sincosf(r->r, &sin, &cos);
int x[4], y[4];
for(int i = 0; i < 4; i++) {
x[i] = r->x + x0[i] * cos + y0[i] * sin;
y[i] = r->y - x0[i] * sin + y0[i] * cos;
}
dtriangle(x[0], y[0], x[1], y[1], x[2], y[2], color, r->opacity);
dtriangle(x[2], y[2], x[3], y[3], x[0], y[0], color, r->opacity);
}
void render_player(int x, int y, float angle, int r, float opacity)
{
int c = 10 * opacity;
dcircle(x, y, r, C_RGB(c, c, c), false, -1, -1);
float x1, y1, x2, y2;
player_position(angle, &x1, &y1, &x2, &y2, r);
x1 = x1 - PLAYER_X + x;
y1 = y1 - DHEIGHT/2 + y;
x2 = x2 - PLAYER_X + x;
y2 = y2 - DHEIGHT/2 + y;
int c1 = C_RGB(0, 16, 26);
int c2 = C_RGB(31, 6, 0);
int c3 = C_WHITE;
dcircle(x1, y1, PLAYER_SIZE, c1, true, -1, -1);
dcircle(x2, y2, PLAYER_SIZE, c2, true, c1, c3);
}
void render_glow(int x, int y, int r1, int r2, int color1, int color2,
float angle, float opacity)
{
float s1, c1, s2, c2;
sincosf(angle, &s2, &c2);
for(int i = 0; i < 16; i++) {
s1 = s2;
c1 = c2;
angle += M_PI / 8.0;
sincosf(angle, &s2, &c2);
int r = (i & 1) ? r2 : r1;
int color = (i & 1) ? color2 : color1;
dtriangle(x, y, x+r*c2, y+r*s2, x+r*c1, y+r*s1, color, 256 * opacity);
}
}
void radial_fadeout(int cx, int cy, int r1, int r2, int c)
{
int x1 = max(0, cx - r2);
int x2 = min(DWIDTH-1, cx + r2);
int y1 = max(0, cy - r2);
int y2 = min(DHEIGHT-1, cy + r2);
for(int y = y1; y <= y2; y++)
for(int x = x1; x <= x2; x++) {
int i = DWIDTH * y + x;
int r_sq = (x - cx) * (x - cx) + (y - cy) * (y - cy);
if(r_sq < r1 * r1)
continue;
if(r_sq > r2 * r2) {
gint_vram[i] = c;
continue;
}
float r = sqrtf(r_sq);
float opacity = fmaxf(0.0, fminf(1.0, (r - r1) / (r2 - r1)));
gint_vram[i] = blend(gint_vram[i], c, 256*opacity);
}
}