lsnes rr2-β24
[lsnes.git] / src / emulation / sky / draw.cpp
blob77fd7726730a3cd0d1b5292d4a3dc622358fbc6d
1 #include "draw.hpp"
2 #include <cmath>
3 #include <cstdio>
4 #include "romimage.hpp"
5 #include "framebuffer.hpp"
6 #include "messages.hpp"
7 #include "instance.hpp"
8 #include "library/minmax.hpp"
11 Point of escape is approximately at normalized y=0.16.
12 Baseline is approximately at normalized y=0.52
13 1st tile after starts approximately at y=0.38
20 namespace sky
22 //Projection parameters.
23 //Inverse of normalized y0 at baseline.
24 const double z0 = 1.9;
25 //Depth projection scale.
26 const double zscale = 0.6;
27 //Point of escape on screen.
28 const double yescape = 0.16;
29 //Horizontal draw scale.
30 const double hscale = 0.15;
31 //Vertical draw scale.
32 const double vscale = 0.1;
33 //Normalized floor thickness.
34 const double fthickness = 0.33;
36 //Type of point representation.
37 struct point_t
39 double x;
40 double y;
43 const unsigned top_palette[] = {61, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15};
44 const unsigned front_palette[] = {62, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30};
45 const unsigned right_palette[] = {63, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45};
46 const unsigned left_palette[] = {64, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60};
47 const double slopepoints[] = {-3.45, -2.45, -1.45, 0, 1.45, 2.45, 3.45};
48 const double angles[] = {1.35, 1.07, 0.88, 0.745};
50 double calc_slope(double x)
52 return 1.6 * (hscale * x) / (1 / z0 - yescape);
55 std::pair<signed, signed> point_project(double x, double y, double z)
57 double c = (z0 + zscale * z);
58 if(c < 0.001)
59 c = 0.001;
60 double y0 = 1 / c;
61 double s = (y0 - yescape) / (1 / z0 - yescape);
62 if(s >= 0)
63 return std::make_pair(FB_WIDTH * (hscale * x * s + 0.5), FB_HEIGHT * (y0 - vscale * y * s));
64 else
65 return std::make_pair(FB_WIDTH / 2, FB_HEIGHT * yescape);
68 point_t point_project_b(double x, double y, double z)
70 double c = (z0 + zscale * z);
71 if(c < 0.001)
72 c = 0.001;
73 double y0 = 1 / c;
74 double s = (y0 - yescape) / (1 / z0 - yescape);
75 point_t p;
76 if(s >= 0) {
77 p.x = FB_WIDTH * (hscale * x * s + 0.5);
78 p.y = FB_HEIGHT * (y0 - vscale * y * s);
79 } else {
80 p.x = FB_WIDTH / 2;
81 p.y = FB_HEIGHT * yescape;
83 return p;
86 int32_t ship_sprite_normal(uint16_t hpos, int vdir, uint8_t thruster)
88 int hdir;
89 if(hpos < 18048) hdir = 0;
90 else if(hpos < 23936) hdir = 1;
91 else if(hpos < 29824) hdir = 2;
92 else if(hpos < 35712) hdir = 3;
93 else if(hpos < 41600) hdir = 4;
94 else if(hpos < 47488) hdir = 5;
95 else hdir = 6;
96 if(vdir == -1) vdir = 2;
97 return 9 * hdir + 3 * vdir + thruster + 14;
100 int32_t ship_sprite_explode(int32_t frame)
102 return (frame < 42) ? (frame / 3) : -1;
105 int32_t ship_sprite(gstate& s)
107 if(s.p.expframe)
108 return ship_sprite_explode(s.p.expframe);
109 if(s.p.death == physics::death_finished)
110 return -1;
111 int vdir;
112 uint16_t thruster;
113 if(s.p.vspeed > 300) vdir = 1;
114 else if(s.p.vspeed < -300) vdir = -1;
115 else vdir = 0;
116 if(s.p.death == physics::death_fuel)
117 thruster = 0;
118 else
119 thruster = s.rng.pull() % 3;
120 return ship_sprite_normal(s.p.hpos, vdir, thruster % 3);
123 void draw_sprite(struct instance& inst, double h, double v, int32_t num)
125 if(num < 0)
126 return;
127 signed x = FB_WIDTH * hscale * h + FB_WIDTH / 2 - FB_SCALE * 15;
128 signed y = FB_HEIGHT / z0 - FB_HEIGHT * vscale * v - FB_SCALE * inst.ship.width + FB_SCALE;
129 uint32_t offset = inst.ship.width * 30 * num;
130 //For some darn reason, CARS.LZS has the image rotated 90 degrees counterclockwise.
131 for(signed j = 0; j < FB_SCALE * (int)inst.ship.width; j++) {
132 if(y + j < 0 || y + j > inst.overlap_end)
133 continue;
134 uint32_t offset2 = offset + j / FB_SCALE;
135 size_t base = FB_WIDTH * (y + j) + x;
136 for(signed i = 0; i < 30 * FB_SCALE; i++) {
137 if(x + i < 0)
138 continue;
139 if(x + i >= FB_WIDTH)
140 break;
141 uint8_t c = inst.ship.decode[offset2 + inst.ship.width * (i / FB_SCALE)];
142 if(!c)
143 continue;
144 uint32_t pix = inst.ship.palette[c] & 0xFFFFFF;
145 if((inst.framebuffer[base + i] >> 24) == 0)
146 inst.framebuffer[base + i] = pix;
151 void draw_grav_g_meter(struct instance& inst)
153 uint16_t realgrav = 100 * (inst.state.p.gravity - 3);
154 uint16_t x = 116;
155 uint16_t y = 156;
156 static const size_t sep = strlen(_numbers_g) / 10;
157 while(realgrav) {
158 uint8_t digit = realgrav % 10;
159 draw_block2(inst, _numbers_g + sep * digit, y * 320 + (x - 5), inst.dashpalette[6],
160 inst.dashpalette[5], true);
161 x -= 5;
162 realgrav /= 10;
166 void draw_indicator(struct instance& inst, uint8_t& curval, uint8_t newval, gauge& g, uint8_t on1,
167 uint8_t on2, uint8_t off1, uint8_t off2)
169 unsigned tmp = newval;
170 if(tmp > g.maxlimit()) tmp = g.maxlimit();
171 if(curval < tmp)
172 for(unsigned i = curval; i < tmp; i++) {
173 draw_block(inst, g.get_data(i), g.get_position(i), inst.dashpalette[on1],
174 inst.dashpalette[on2]);
176 else
177 for(unsigned i = tmp; i < curval; i++) {
178 draw_block(inst, g.get_data(i), g.get_position(i), inst.dashpalette[off1],
179 inst.dashpalette[off2]);
181 curval = tmp;
184 void draw_gauges(struct instance& inst)
186 //draw_grav_g_meter(s);
187 uint8_t timermod = ((inst.state.p.framecounter % 9) > 4) ? 1 : 0;
188 draw_indicator(inst, inst.state.speedind, (inst.state.p.lspeed - inst.state.p.speedbias) / 0x141,
189 inst.speed_dat, 2, 3, 0, 1);
190 draw_indicator(inst, inst.state.o2ind, (inst.state.p.o2_left + 0xbb7) / 0xbb8, inst.oxydisp_dat, 2, 3,
191 0, 1);
192 draw_indicator(inst, inst.state.fuelind, (inst.state.p.fuel_left + 0xbb7) / 0xbb8, inst.fueldisp_dat,
193 2, 3, 0, 1);
194 //Lock indicator.
195 bool lck = inst.state.p.is_set(physics::flag_locked);
196 if(lck != inst.state.lockind)
197 draw_block2(inst, _lockind_g + (lck ? strlen(_lockind_g) / 2 : 0), 0x9c * 320 + 0xcb,
198 inst.dashpalette[6], inst.dashpalette[5], true);
199 inst.state.lockind = lck;
200 //Out of oxygen blink&beep.
201 if(inst.state.p.death == physics::death_o2 && inst.state.beep_phase != timermod) {
202 blink_between(inst, 0xa0, 0xa1, 7, 7, inst.dashpalette[7], inst.dashpalette[8]);
203 if(timermod)
204 inst.gsfx(sound_beep);
206 //Out of fuel blink&beep.
207 if(inst.state.p.death == physics::death_fuel && inst.state.beep_phase != timermod) {
208 blink_between(inst, 0x9b, 0xa9, 16, 5, inst.dashpalette[7], inst.dashpalette[8]);
209 if(timermod)
210 inst.gsfx(sound_beep);
212 //Distance gauge.
213 uint32_t res = inst.state.curlevel.apparent_length() / (29 * FB_SCALE + 1);
214 size_t tmp = (inst.state.p.lpos - 3 * 65536) / res;
215 for(unsigned i = inst.state.distind; i < tmp && i < (29 * FB_SCALE + 1); i++)
216 draw_distance_column(inst, i, inst.dashpalette[4]);
217 inst.state.distind = tmp;
218 inst.state.beep_phase = timermod;
221 inline double horizon_distance() { return (1 / yescape - z0) / zscale; }
222 inline double near_distance(struct instance& inst) { return (600 / inst.overlap_end - z0) / zscale; }
225 void draw_quad_x(struct instance& inst, point_t p1, point_t p2, point_t p3, point_t p4, uint32_t color)
227 if(fabs(p1.x - p2.x) < 0.1)
228 return;
229 double qp1 = min(p1.x, p2.x);
230 double qp2 = max(p1.x, p2.x);
231 int x1 = floor(qp1);
232 int x2 = ceil(qp2);
233 double utmax = max(p1.y, p2.y);
234 double utmin = min(p1.y, p2.y);
235 double ltmax = max(p3.y, p4.y);
236 double ltmin = min(p3.y, p4.y);
237 signed y1 = max((int)floor(utmin), 0);
238 signed y2 = min((int)ceil(ltmax), (int)inst.overlap_end);
239 for(signed j = y1; j < y2; j++) {
240 signed xstart = x1, xend = x2;
241 if(j < utmax) {
242 if(p1.y > p2.y)
243 xstart = floor((qp1 - qp2) * (j - p2.y) / (p1.y - p2.y) + qp2);
244 else
245 xend = ceil((qp2 - qp1) * (j - p1.y) / (p2.y - p1.y) + qp1);
247 if(j >= ltmin) {
248 if(p3.y > p4.y)
249 xend = ceil((qp1 - qp2) * (j - p4.y) / (p3.y - p4.y) + qp2);
250 else
251 xstart = floor((qp2 - qp1) * (j - p3.y) / (p4.y - p3.y) + qp1);
253 xstart = max(xstart, 0);
254 xend = min(xend, FB_WIDTH);
255 size_t base = FB_WIDTH * j;
256 if(j < inst.overlap_start)
257 for(signed i = xstart; i < xend; i++)
258 inst.framebuffer[base + i] = color;
259 else
260 for(signed i = xstart; i < xend; i++)
261 framebuffer_blend2(inst.framebuffer[base + i], color);
265 void draw_quad_y(struct instance& inst, point_t p1, point_t p2, point_t p3, point_t p4, uint32_t color)
267 if(fabs(p1.y - p3.y) < 0.1)
268 return;
269 signed y1 = max((int)floor(p1.y), 0);
270 signed y2 = min((int)ceil(p3.y), (int)inst.overlap_end);
271 for(signed j = y1; j < y2; j++) {
272 signed xstart, xend;
273 xstart = floor((p3.x - p1.x) * (j - p1.y) / (p3.y - p1.y) + p1.x);
274 xend = ceil((p4.x - p2.x) * (j - p1.y) / (p3.y - p1.y) + p2.x);
275 xstart = max(xstart, 0);
276 xend = min(xend, FB_WIDTH);
277 size_t base = FB_WIDTH * j;
278 if(j < inst.overlap_start)
279 for(signed i = xstart; i < xend; i++)
280 inst.framebuffer[base + i] = color;
281 else
282 for(signed i = xstart; i < xend; i++)
283 framebuffer_blend2(inst.framebuffer[base + i], color);
287 void draw_quad_z(struct instance& inst, point_t p1, point_t p2, point_t p3, point_t p4, uint32_t color)
289 if(fabs(p1.x - p2.x) < 0.1 || fabs(p1.y - p3.y) < 0.1)
290 return;
291 signed x1 = max((int)floor(p1.x), 0);
292 signed x2 = min((int)ceil(p2.x), FB_WIDTH);
293 signed y1 = max((int)floor(p1.y), 0);
294 signed y2 = min((int)ceil(p3.y), (int)inst.overlap_end);
295 for(signed j = y1; j < y2; j++) {
296 size_t base = FB_WIDTH * j;
297 if(j < inst.overlap_start)
298 for(signed i = x1; i < x2; i++)
299 inst.framebuffer[base + i] = color;
300 else
301 for(signed i = x1; i < x2; i++)
302 framebuffer_blend2(inst.framebuffer[base + i], color);
306 void draw_quad_z_tunshadow(struct instance& inst, point_t p1, point_t p2, point_t p3, point_t p4,
307 uint32_t color, int x)
309 if(fabs(p1.x - p2.x) < 0.1 || fabs(p1.y - p3.y) < 0.1)
310 return;
311 if(!x)
312 return; //Center pipe has no shadow.
313 signed x1 = max((int)floor(p1.x), 0);
314 signed x2 = min((int)ceil(p2.x), FB_WIDTH);
315 signed y1 = max((int)floor(p1.y), 0);
316 signed y2 = min((int)ceil(p3.y), (int)inst.overlap_end);
317 double slope = calc_slope(slopepoints[x + 3]);
318 const double width = p2.x - p1.x;
319 const double hwidth = 0.45 * width;
320 const double center = (p1.x + p2.x) / 2;
321 for(signed j = y1; j < y2; j++) {
322 signed c1 = x2, c2 = x1;
323 size_t base = FB_WIDTH * j;
324 double ry = (p3.y - j) / (0.625 * vscale / hscale * 2 * hwidth);
325 if(ry < 1) {
326 c1 = center - hwidth * sqrt(1 - ry * ry);
327 c2 = center + hwidth * sqrt(1 - ry * ry);
329 if(x < 0) {
330 //The shadow is on left.
331 c2 = center - hwidth - slope * (p3.y - j);
332 } else {
333 //The shadow is on right.
334 c1 = center + hwidth - slope * (p3.y - j);
336 c1 = max(c1, 0);
337 c2 = min(c2, FB_WIDTH);
338 if(j < inst.overlap_start)
339 for(signed i = c1; i < c2; i++)
340 inst.framebuffer[base + i] = color;
341 else
342 for(signed i = c1; i < c2; i++)
343 framebuffer_blend2(inst.framebuffer[base + i], color);
347 void draw_quad_z_tunnel(struct instance& inst, point_t p1, point_t p2, point_t p3, point_t p4, uint32_t color)
349 if(fabs(p1.x - p2.x) < 0.1 || fabs(p1.y - p3.y) < 0.1)
350 return;
351 signed x1 = max((int)floor(p1.x), 0);
352 signed x2 = min((int)ceil(p2.x), FB_WIDTH);
353 signed y1 = max((int)floor(p1.y), 0);
354 signed y2 = min((int)ceil(p3.y), (int)inst.overlap_end);
355 const double width = p2.x - p1.x;
356 const double hwidth = 0.45 * width;
357 const double center = (p1.x + p2.x) / 2;
358 for(signed j = y1; j < y2; j++) {
359 signed c1 = x2, c2 = x2;
360 size_t base = FB_WIDTH * j;
361 double ry = (p3.y - j) / (0.625 * vscale / hscale * 2 * hwidth);
362 if(ry < 1) {
363 c1 = center - hwidth * sqrt(1 - ry * ry);
364 c2 = center + hwidth * sqrt(1 - ry * ry);
366 x1 = max(x1, 0);
367 c2 = max(c2, 0);
368 x2 = min(x2, FB_WIDTH);
369 c1 = min(c1, FB_WIDTH);
370 if(j < inst.overlap_start) {
371 for(signed i = x1; i < c1; i++)
372 inst.framebuffer[base + i] = color;
373 for(signed i = c2; i < x2; i++)
374 inst.framebuffer[base + i] = color;
375 } else {
376 for(signed i = x1; i < c1; i++)
377 framebuffer_blend2(inst.framebuffer[base + i], color);
378 for(signed i = c2; i < x2; i++)
379 framebuffer_blend2(inst.framebuffer[base + i], color);
384 void draw_quad_z_pipefront(struct instance& inst, double z, int x, uint32_t color)
386 auto p5 = point_project_b(x - 0.5, 0, z);
387 auto p6 = point_project_b(x, 1, z);
388 auto p7 = point_project_b(x + 0.5, 0, z);
389 double ymind = p6.y;
390 double ymaxd = p5.y;
391 int16_t ymax = ceil(ymaxd);
392 if(ymaxd - ymind < 0.1)
393 return;
394 int16_t cheight = floor(p5.y - p6.y);
395 int16_t ciheight = floor(0.9 * (p5.y - p6.y));
396 int16_t cwidth = floor((p7.x - p5.x) / 2);
397 int16_t ciwidth = floor(0.9 * (p7.x - p5.x) / 2);
398 int16_t ccenter = floor((p7.x + p5.x) / 2);
399 int16_t nxs = ccenter - cwidth;
400 int16_t nxe = ccenter + cwidth;
401 for(signed j = ymax - cheight; j < ymax; j++) {
402 if(j < 0 || j > inst.overlap_end)
403 continue;
404 int16_t dstart = nxs;
405 int16_t dend = nxe;
406 int16_t cstart = ccenter;
407 int16_t cend = ccenter;
408 int16_t cistart = ccenter;
409 int16_t ciend = ccenter;
410 if(true) {
411 int16_t o = ymax - j;
412 double o2 = 1.0 * o / cheight;
413 int16_t w = cwidth * sqrt(1 - o2 * o2);
414 cstart -= w;
415 cend += w;
417 if(j >= ymax - ciheight) {
418 int16_t o = ymax - j;
419 double o2 = 1.0 * o / ciheight;
420 int16_t w = ciwidth * sqrt(1 - o2 * o2);
421 cistart -= w;
422 ciend += w;
424 dstart = max(cstart, (int16_t)0);
425 dend = min(cend, (int16_t)FB_WIDTH);
426 size_t base = FB_WIDTH * j;
427 if(j < inst.overlap_start)
428 for(signed i = dstart; i < dend; i++) {
429 if(i < cistart || i >= ciend)
430 inst.framebuffer[base + i] = color;
432 else
433 for(signed i = dstart; i < dend; i++) {
434 if(i < cistart || i >= ciend)
435 framebuffer_blend2(inst.framebuffer[base + i], color);
440 // x = (k*y0+b)*c*(0.5*sin(alpha)+X0)
441 // y = -(k*y0+b)*d*cos(alpha)+y0
443 // dx/dy0 = k*c*0.5*sin(alpha)+k*c*X0
444 // dy/dy0 = k*d*cos(alpha)+1
445 // dx/dy = (k*c*0.5*sin(alpha)+k*c*X0)/(k*d*cos(alpha)+1)
446 // d/dalpha[(k*c*0.5*sin(alpha)+k*c*X0)/(k*d*cos(alpha)+1)]
447 //=(k*c*0.5*cos(alpha))/(k*d*cos(alpha)+1) +(k*c*0.5*sin(alpha)+k*c*X0)*(k*d*sin(alpha))/(k*d*cos(alpha)+1)^2
449 // 0.5*k*d+0.5*cos(alpha)+X0*(k*d*sin(alpha)) = 0
451 uint32_t mix_color(uint32_t* c, uint32_t c2)
453 uint32_t low = c[c2 / 256];
454 uint32_t high = c[c2 / 256 + 1];
455 uint16_t mod = c2 % 256;
456 uint16_t imod = 256 - mod;
457 const uint32_t Lm = 0x00FF00FF;
458 const uint32_t Hm = 0xFF00FF00;
459 uint32_t L = (((low & Lm) * imod + (high & Lm) * mod) >> 8) & Lm;
460 uint32_t H = (((low & Hm) >> 8) * imod + ((high & Hm) >> 8) * mod) & Hm;
461 return L | H;
464 void rebuild_pipe_quad_cache(pipe_cache& p, int x, uint32_t* c)
466 double k = 1 / (1 / z0 - yescape);
467 double b = -yescape * k;
468 double gmin = 999999;
469 double gmax = -999999;
470 double amin = 999999;
471 double amax = -999999;
472 double y00 = (1-b)/k;
473 for(unsigned i = 0; i < 256; i++)
474 p.colors[i] = 0xFFFFFFFF;
475 //Compute span range.
476 for(double a = -M_PI / 2; a < M_PI / 2; a += 0.01) {
477 double A = vscale * cos(a);
478 double y0 = (y00 + b * A) / (1 - k * A);
479 double xp = (k * y0 + b) * (x + 0.5 * sin(a));
480 if(xp < gmin) {
481 p.min_h = x + 0.5 * sin(a);
482 p.min_v = cos(a);
483 gmin = xp;
484 amin = a;
486 if(xp > gmax) {
487 p.max_h = x + 0.5 * sin(a);
488 p.max_v = cos(a);
489 gmax = xp;
490 amax = a;
493 for(double a = amin; a < amax; a += 0.01) {
494 double A = vscale * cos(a);
495 double y0 = (y00 + b * A) / (1 - k * A);
496 double xp = (k * y0 + b) * (x + 0.5 * sin(a));
497 signed c2 = (a + M_PI / 2) * 1280 / M_PI;
498 signed x = 255 * (xp - gmin) / (gmax - gmin);
499 c2 = max(min(c2, 1280), 0);
500 x = max(min(x, 255), 0);
501 p.colors[x] = mix_color(c, c2);
503 for(unsigned i = 0; i < 256; i++) {
504 if(p.colors[i] == 0xFFFFFFFF) {
505 if(i == 0) {
506 for(unsigned j = 0; j < 255; j++)
507 if(p.colors[j] != 0xFFFFFFFF) {
508 p.colors[i] = p.colors[j];
509 break;
511 } else
512 p.colors[i] = p.colors[i - 1];
515 //for(unsigned i = 0; i < 256; i++)
516 // p.colors[i] = 0xFF8000 + i;
519 void rebuild_pipe_quad_caches(struct instance& i, uint32_t color1, uint32_t color2, uint32_t color3,
520 uint32_t color4)
522 uint32_t c[6];
523 c[0] = color4;
524 c[1] = color3;
525 c[2] = color2;
526 c[3] = color1;
527 c[4] = color2;
528 c[5] = color3;
529 for(int x = 0; x < 7; x++)
530 rebuild_pipe_quad_cache(i.pipecache[x], x - 3, c);
533 void draw_quad_y_pipe_last(struct instance& inst, double z, int x, bool top)
535 struct pipe_cache& p = inst.pipecache[x + 3];
536 uint32_t fcolor = (!top && z < 0.2) ? 0x1000000 : 0;
537 //We need these for slope projection.
538 auto p1 = point_project_b(p.min_h, p.min_v, z);
539 auto p2 = point_project_b(p.max_h, p.max_v, z);
540 auto p3 = point_project_b(p.min_h, p.min_v, z - 1);
541 auto p4 = point_project_b(p.max_h, p.max_v, z - 1);
542 auto p5 = point_project_b(x - 0.5, 0, z);
543 auto p6 = point_project_b(x, 1, z);
544 auto p7 = point_project_b(x + 0.5, 0, z);
545 if(p3.y - p1.y < 0.1)
546 return;
547 if(p4.y - p2.y < 0.1)
548 return;
549 double ymaxd = p5.y;
550 int16_t ymax = ceil(ymaxd);
551 double sl1 = (p3.x - p1.x) / (p3.y - p1.y);
552 double sl2 = (p4.x - p2.x) / (p4.y - p2.y);
553 double c1 = p1.x - sl1 * p1.y;
554 double c2 = p2.x - sl2 * p2.y;
555 int16_t cheight = floor(p5.y - p6.y);
556 int16_t cwidth = floor((p7.x - p5.x) / 2);
557 int16_t ccenter = floor((p7.x + p5.x) / 2);
558 for(signed j = ymax - cheight; j < ymax; j++) {
559 if(j < 0 || j > inst.overlap_end)
560 continue;
561 int16_t nxs = floor(sl1 * j + c1);
562 int16_t nxe = ceil(sl2 * j + c2);
563 int16_t dstart = nxs;
564 int16_t dend = nxe;
565 if(nxs >= nxe)
566 continue;
567 uint32_t cstep = 255 * 65536 / (nxe - nxs + 1);
568 uint32_t color = 0;
569 if(true) {
570 int16_t o = ymax - j;
571 double o2 = 1.0 * o / cheight;
572 int16_t w = cwidth * sqrt(1 - o2 * o2);
573 dstart = ccenter - w;
574 dend = ccenter + w;
576 dstart = max(max(dstart, nxs), (int16_t)0);
577 dend = min(min(dend, nxe), (int16_t)FB_WIDTH);
578 if(dstart > nxs)
579 color += (dstart - nxs) * cstep;
580 size_t base = FB_WIDTH * j;
581 if(j < inst.overlap_start)
582 for(signed i = dstart; i < dend; i++) {
583 inst.framebuffer[base + i] = fcolor | p.colors[color >> 16];
584 color += cstep;
586 else
587 for(signed i = dstart; i < dend; i++) {
588 framebuffer_blend2(inst.framebuffer[base + i], fcolor |
589 p.colors[color >> 16]);
590 color += cstep;
595 void draw_quad_y_pipe_first(struct instance& inst, double z1, double z2, int x, bool top)
597 struct pipe_cache& p = inst.pipecache[x + 3];
598 uint32_t fcolor = (!top && z1 < 0) ? 0x1000000 : 0;
599 auto p1 = point_project_b(p.min_h, p.min_v, z2);
600 auto p2 = point_project_b(p.max_h, p.max_v, z2);
601 auto p3 = point_project_b(p.min_h, p.min_v, z1);
602 auto p4 = point_project_b(p.max_h, p.max_v, z1);
603 auto p5 = point_project_b(x - 0.5, 0, z1);
604 auto p6 = point_project_b(x, 1, z1);
605 auto p7 = point_project_b(x + 0.5, 0, z1);
606 double ymind = min(p1.y, p2.y);
607 double ymaxd = p5.y;
608 int16_t ymin = floor(ymind);
609 int16_t ymax = ceil(ymaxd);
610 int16_t ymin2 = ceil(max(p1.y, p2.y));
611 if(p3.y - p1.y < 0.1)
612 return;
613 if(p4.y - p2.y < 0.1)
614 return;
615 double sl1 = (p3.x - p1.x) / (p3.y - p1.y);
616 double sl2 = (p4.x - p2.x) / (p4.y - p2.y);
617 double c1 = p1.x - sl1 * p1.y;
618 double c2 = p2.x - sl2 * p2.y;
619 int16_t cheight = floor(p5.y - p6.y);
620 int16_t cwidth = floor((p7.x - p5.x) / 2);
621 int16_t ccenter = floor((p7.x + p5.x) / 2);
622 bool hclip = false;
623 uint16_t mindist = 65535;
624 for(signed j = ymin; j < ymax; j++) {
625 if(j < 0 || j > inst.overlap_end)
626 continue;
627 int16_t nxs = floor(sl1 * j + c1);
628 int16_t nxe = ceil(sl2 * j + c2);
629 int16_t dstart = nxs;
630 int16_t dend = nxe;
631 if(nxe <= nxs)
632 continue;
633 uint32_t cstep = 255 * 65536 / (nxe - nxs + 1);
634 uint32_t color = 0;
635 int16_t cstart = ccenter;
636 int16_t cend = ccenter;
637 if(j < ymin2 && p1.y != p2.y) {
638 //The upper triangular region.
639 if(p1.y < p2.y)
640 dend = ceil((p2.x - p1.x) * (j - p1.y) / (p2.y - p1.y) + p1.x);
641 else
642 dstart = floor((p2.x - p1.x) * (j - p1.y) / (p2.y - p1.y) + p1.x);
644 if(j >= ymax - cheight) {
645 int16_t o = ymax - j;
646 double o2 = 1.0 * o / cheight;
647 int16_t w = cwidth * sqrt(1 - o2 * o2);
648 cstart -= w;
649 cend += w;
650 if(hclip) {
651 if(x < 0)
652 dstart = max((int16_t)dstart, cstart);
653 if(x > 0)
654 dend = min((int16_t)dend, cend);
655 } else {
656 uint16_t dist = 0;
657 if(x < 0)
658 dist = cstart - dstart;
659 if(x > 0)
660 dist = dend - cend;
662 if(dist > mindist)
663 hclip = true;
664 else
665 mindist = dist;
668 dstart = max(max(dstart, nxs), (int16_t)0);
669 dend = min(min(dend, nxe), (int16_t)FB_WIDTH);
670 if(dstart > nxs)
671 color += (dstart - nxs) * cstep;
672 size_t base = FB_WIDTH * j;
673 if(j < inst.overlap_start)
674 for(signed i = dstart; i < dend; i++) {
675 if(i < cstart || i >= cend)
676 inst.framebuffer[base + i] = fcolor | p.colors[color >> 16];
677 color += cstep;
679 else
680 for(signed i = dstart; i < dend; i++) {
681 if(i < cstart || i >= cend)
682 framebuffer_blend2(inst.framebuffer[base + i], fcolor |
683 p.colors[color >> 16]);
684 color += cstep;
689 void draw_quad_y_pipe(struct instance& inst, double z1, double z2, int x, bool top)
691 struct pipe_cache& p = inst.pipecache[x + 3];
692 uint32_t fcolor = (!top && z1 < 0.2) ? 0x1000000 : 0;
693 auto p1 = point_project_b(p.min_h, p.min_v, z2);
694 auto p2 = point_project_b(p.max_h, p.max_v, z2);
695 auto p3 = point_project_b(p.min_h, p.min_v, z1);
696 auto p4 = point_project_b(p.max_h, p.max_v, z1);
697 double ymind = min(p1.y, p2.y);
698 double ymaxd = max(p3.y, p4.y);
699 int16_t ymin = floor(ymind);
700 int16_t ymax = ceil(ymaxd) + 1;
701 int16_t ymin2 = ceil(max(p1.y, p2.y));
702 int16_t ymax2 = floor(min(p3.y, p4.y));
703 if(p3.y - p1.y < 0.1)
704 return;
705 if(p4.y - p2.y < 0.1)
706 return;
707 double sl1 = (p3.x - p1.x) / (p3.y - p1.y);
708 double sl2 = (p4.x - p2.x) / (p4.y - p2.y);
709 double c1 = p1.x - sl1 * p1.y;
710 double c2 = p2.x - sl2 * p2.y;
711 //FIXME: This leaves some graphical glitching in seams.
712 for(signed j = ymin; j < ymax; j++) {
713 if(j < 0 || j > inst.overlap_end)
714 continue;
715 int16_t nxs = floor(sl1 * j + c1);
716 int16_t nxe = ceil(sl2 * j + c2);
717 int16_t dstart = nxs;
718 int16_t dend = nxe;
719 if(nxe <= nxs)
720 continue;
721 uint32_t cstep = 255 * 65536 / (nxe - nxs + 1);
722 uint32_t color = 0;
723 if(j < ymin2 && p1.y != p2.y) {
724 //The upper triangular region.
725 if(p1.y < p2.y)
726 dend = ceil((p2.x - p1.x) * (j - p1.y) / (p2.y - p1.y) + p1.x);
727 else
728 dstart = floor((p2.x - p1.x) * (j - p1.y) / (p2.y - p1.y) + p1.x);
730 if(j >= ymax2 && p3.y != p4.y) {
731 //The lower triangular region.
732 if(p3.y < p4.y)
733 dstart = floor((p4.x - p3.x) * (j - p3.y - 1) / (p4.y - p3.y) + p3.x);
734 else
735 dend = ceil((p4.x - p3.x) * (j - p3.y - 1) / (p4.y - p3.y) + p3.x);
737 dstart = max(max(dstart, nxs), (int16_t)0);
738 dend = min(min(dend, nxe), (int16_t)FB_WIDTH);
739 if(dstart > nxs)
740 color += (dstart - nxs) * cstep;
741 size_t base = FB_WIDTH * j;
742 if(j < inst.overlap_start)
743 for(signed i = dstart; i < dend; i++) {
744 inst.framebuffer[base + i] = fcolor | p.colors[color >> 16];
745 color += cstep;
747 else
748 for(signed i = dstart; i < dend; i++) {
749 framebuffer_blend2(inst.framebuffer[base + i], fcolor | p.colors[color >> 16]);
750 color += cstep;
755 void draw_level(struct instance& inst)
757 for(unsigned i = 0; i < inst.overlap_start; i++)
758 for(unsigned j = 0; j < FB_WIDTH; j++)
759 inst.framebuffer[i * FB_WIDTH + j] = inst.origbuffer[(i / FB_SCALE) * 320 +
760 (j / FB_SCALE)];
761 for(unsigned i = inst.overlap_start; i < inst.overlap_end; i++)
762 for(unsigned j = 0; j < FB_WIDTH; j++)
763 framebuffer_blend2(inst.framebuffer[i * FB_WIDTH + j], inst.origbuffer[
764 (i / FB_SCALE) * 320 + (j / FB_SCALE)]);
765 static const signed dorder[] = {-3, 3, -2, 2, -1, 1, 0};
766 level& l = inst.state.curlevel;
767 double zship = inst.state.p.lpos / 65536.0;
768 double horizon = horizon_distance();
769 double near = near_distance(inst);
770 signed maxtile = ceil(zship + horizon);
771 signed mintile = floor(zship + near - 1);
772 for(signed zt = maxtile; zt >= mintile; zt--) {
773 for(signed dxt = 0; dxt < 7; dxt++) {
774 signed xt = dorder[dxt];
775 tile t = l.at_tile(zt, xt);
776 tile tleft = l.at_tile(zt, xt - 1);
777 tile tright = l.at_tile(zt, xt + 1);
778 tile tfront = l.at_tile(zt - 1, xt);
779 tile tback = l.at_tile(zt + 1, xt);
780 //First, draw the floor level.
781 if(t.lower_floor() && (!t.is_rblock() || t.is_tunnel())) {
782 bool ifront = (inst.state.p.vpos < 10240);
783 ifront &= (xt >= 0 || inst.state.p.hpos < 5888 * xt + 32768 - 2944);
784 ifront &= (xt <= 0 || inst.state.p.hpos > 5888 * xt + 32768 + 2944);
785 draw_quad_y(inst, point_project_b(xt - 0.5, 0, zt + 1 - zship),
786 point_project_b(xt + 0.5, 0, zt + 1 - zship),
787 point_project_b(xt - 0.5, 0, zt - zship),
788 point_project_b(xt + 0.5, 0, zt - zship),
789 l.get_palette_color(top_palette[t.lower_floor()], ifront));
791 //Draw pipe shadow.
792 if(t.is_tunnel() && !tfront.is_block()) {
793 //Pipe shadow never obscures the ship.
794 draw_quad_z_tunshadow(inst, point_project_b(xt - 0.5, 1, zt - zship),
795 point_project_b(xt + 0.5, 1, zt - zship),
796 point_project_b(xt - 0.5, 0, zt - zship),
797 point_project_b(xt + 0.5, 0, zt - zship),
798 l.get_palette_color(t.is_rblock() ? 65 : 67), xt);
800 //Draw the top surface.
801 if(t.is_rblock()) {
802 signed q = t.apparent_height();
803 bool ifront = (inst.state.p.vpos < 10240 + q * 2560);
804 ifront &= (xt >= 0 || inst.state.p.hpos < 5888 * xt + 32768 - 2944);
805 ifront &= (xt <= 0 || inst.state.p.hpos > 5888 * xt + 32768 + 2944);
806 ifront |= (l.in_pipe(zt * 65536, inst.state.p.hpos, inst.state.p.vpos) &&
807 zt < zship);
808 draw_quad_y(inst, point_project_b(xt - 0.5, q, zt + 1 - zship),
809 point_project_b(xt + 0.5, q, zt + 1 - zship),
810 point_project_b(xt - 0.5, q, zt - zship),
811 point_project_b(xt + 0.5, q, zt - zship),
812 l.get_palette_color(top_palette[t.upper_floor()], ifront));
814 //Left/Right block surface.
815 if(t.is_rblock() && xt < 0 && tright.apparent_height() < t.apparent_height()) {
816 signed q1 = tright.apparent_height();
817 signed q2 = t.apparent_height();
818 bool ifront = (inst.state.p.vpos < 10240 + q2 * 2560);
819 ifront &= (inst.state.p.hpos < 5888 * xt + 32768 + 2944);
820 draw_quad_x(inst, point_project_b(xt + 0.5, q2, zt - zship),
821 point_project_b(xt + 0.5, q2, zt + 1 - zship),
822 point_project_b(xt + 0.5, q1, zt - zship),
823 point_project_b(xt + 0.5, q1, zt + 1 - zship),
824 l.get_palette_color(63, ifront));
826 if(t.is_rblock() && xt > 0 && tleft.apparent_height() < t.apparent_height()) {
827 signed q1 = tleft.apparent_height();
828 signed q2 = t.apparent_height();
829 bool ifront = (inst.state.p.vpos < 10240 + q2 * 2560);
830 ifront &= (inst.state.p.hpos > 5888 * xt + 32768 - 2944);
831 draw_quad_x(inst, point_project_b(xt - 0.5, q2, zt + 1 - zship),
832 point_project_b(xt - 0.5, q2, zt - zship),
833 point_project_b(xt - 0.5, q1, zt + 1- zship),
834 point_project_b(xt - 0.5, q1, zt - zship),
835 l.get_palette_color(64, ifront));
837 //Front block surface.
838 if(t.is_rblock() && tfront.apparent_height() < t.apparent_height()) {
839 signed q1 = tfront.apparent_height();
840 signed q2 = t.apparent_height();
841 bool ifront = (zt < zship);
842 if(t.is_tunnel() && !tfront.is_block())
843 draw_quad_z_tunnel(inst, point_project_b(xt - 0.5, q2, zt - zship),
844 point_project_b(xt + 0.5, q2, zt - zship),
845 point_project_b(xt - 0.5, q1, zt - zship),
846 point_project_b(xt + 0.5, q1, zt - zship),
847 l.get_palette_color(62, ifront));
848 else {
849 draw_quad_z(inst, point_project_b(xt - 0.5, q2, zt - zship),
850 point_project_b(xt + 0.5, q2, zt - zship),
851 point_project_b(xt - 0.5, q1, zt - zship),
852 point_project_b(xt + 0.5, q1, zt - zship),
853 l.get_palette_color(62, ifront));
856 //Last tunnel block.
857 if(t.is_tunnel() && !t.is_rblock()) {
858 bool top = (inst.state.p.vpos > 10240);
859 top &= !l.in_pipe(zt * 65536, inst.state.p.hpos, inst.state.p.vpos);
860 if(tback.is_rblock() || !tback.is_block())
861 draw_quad_y_pipe_last(inst, zt + 1 - zship, xt, top);
862 //Standalone tunnel top.
863 if(tfront.is_block())
864 draw_quad_y_pipe(inst, zt - zship, zt + 1 - zship, xt, top);
865 else
866 draw_quad_y_pipe_first(inst, zt - zship, zt + 1 - zship, xt, top);
867 //Standalone tunnel front.
868 if(!tfront.is_block()) {
869 bool ifront = (zt < zship);
870 draw_quad_z_pipefront(inst, zt - zship, xt, l.get_palette_color(66,
871 ifront));
874 //Left/Right floor surface.
875 if(xt < 0 && t.lower_floor() && !tright.lower_floor()) {
876 bool ifront = (inst.state.p.vpos < 10240);
877 ifront &= (inst.state.p.hpos > 5888 * xt + 32768);
878 draw_quad_x(inst, point_project_b(xt + 0.5, 0, zt - zship),
879 point_project_b(xt + 0.5, 0, zt + 1 - zship),
880 point_project_b(xt + 0.5, -fthickness, zt - zship),
881 point_project_b(xt + 0.5, -fthickness, zt + 1 - zship),
882 l.get_palette_color(right_palette[t.lower_floor()]));
884 if(xt > 0 && t.lower_floor() && !tleft.lower_floor()) {
885 bool ifront = (inst.state.p.vpos < 10240);
886 ifront &= (inst.state.p.hpos > 5888 * xt + 32768);
887 draw_quad_x(inst, point_project_b(xt - 0.5, 0, zt + 1 - zship),
888 point_project_b(xt - 0.5, 0, zt - zship),
889 point_project_b(xt - 0.5, -fthickness, zt + 1- zship),
890 point_project_b(xt - 0.5, -fthickness, zt - zship),
891 l.get_palette_color(left_palette[t.lower_floor()], ifront));
893 //Front floor surface.
894 if(t.lower_floor() && !tfront.lower_floor()) {
895 bool ifront = (inst.state.p.vpos < 10240);
896 ifront &= (inst.state.p.hpos < 5888 * xt + 32768);
897 draw_quad_z(inst, point_project_b(xt - 0.5, 0, zt - zship),
898 point_project_b(xt + 0.5, 0, zt - zship),
899 point_project_b(xt - 0.5, -fthickness, zt - zship),
900 point_project_b(xt + 0.5, -fthickness, zt - zship),
901 l.get_palette_color(front_palette[t.lower_floor()], ifront));
905 draw_sprite(inst, (inst.state.p.hpos - 32768.0) / 5888.0, (inst.state.p.vpos - 10240.0) / 2560.0,
906 ship_sprite(inst.state));
909 const char* const period = "%&(ccK";
910 const char* const dash = "%'(cScS";
911 const char* const vline = "%$(b";
912 const char* const tback = "%F*ccccccccccccccccccccccccccccccccccccccccc";
914 void draw_timeattack_time(struct instance& inst, const char* msg)
916 uint16_t w = 321;
917 uint16_t nst = strlen(_numbers_g) / 10;
918 draw_block2(inst, tback, 0, 0xFFFFFF, 0xFFFFFF, false);
919 while(*msg) {
920 if(*msg >= '0' && *msg <= '9') {
921 draw_block2(inst, _numbers_g + (*msg - '0') * nst, w, 0xFFFFFF, 0xFFFFFF, true);
922 draw_block2(inst, vline, w + 4, 0xFFFFFF, 0xFFFFFF, true);
923 w += 5;
924 } else if(*msg == ':') {
925 draw_block2(inst, period, w, 0xFFFFFF, 0xFFFFFF, true);
926 w += 3;
927 } else if(*msg == '-') {
928 draw_block2(inst, dash, w, 0xFFFFFF, 0xFFFFFF, true);
929 draw_block2(inst, vline, w + 4, 0xFFFFFF, 0xFFFFFF, true);
930 w += 5;
932 msg++;
934 for(unsigned i = 0; i < 7; i++)
935 for(unsigned j = 0; j < 35; j++)
936 inst.origbuffer[320 * i + j] ^= 0xFFFFFF;
937 render_framebuffer_update(inst, 0, 0, 35, 7);
940 void draw_timeattack_time(struct instance& inst, uint16_t frames)
942 char msg[8];
943 if(frames > 64807) {
944 strcpy(msg, "----:--");
945 } else {
946 unsigned seconds = 18227 * frames / 656250;
947 unsigned subseconds = 36454U * frames / 13125 - 100 * seconds;
948 sprintf(msg, "%u:%02u", seconds, subseconds);
950 draw_timeattack_time(inst, msg);