4 #include "romimage.hpp"
5 #include "framebuffer.hpp"
6 #include "messages.hpp"
7 #include "library/minmax.hpp"
10 Point of escape is approximately at normalized y=0.16.
11 Baseline is approximately at normalized y=0.52
12 1st tile after starts approximately at y=0.38
21 //Projection parameters.
22 //Inverse of normalized y0 at baseline.
23 const double z0
= 1.9;
24 //Depth projection scale.
25 const double zscale
= 0.6;
26 //Point of escape on screen.
27 const double yescape
= 0.16;
28 //Horizontal draw scale.
29 const double hscale
= 0.15;
30 //Vertical draw scale.
31 const double vscale
= 0.1;
32 //Normalized floor thickness.
33 const double fthickness
= 0.33;
35 const unsigned pipe_slices
= 256;
36 //Type of point representation.
51 //double raise[pipe_slices];
52 uint32_t colors
[pipe_slices
];
54 struct pipe_cache pipecache
[7];
56 unsigned top_palette
[] = {61, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15};
57 unsigned front_palette
[] = {62, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30};
58 unsigned right_palette
[] = {63, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45};
59 unsigned left_palette
[] = {64, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60};
60 double slopepoints
[] = {-3.45, -2.45, -1.45, 0, 1.45, 2.45, 3.45};
61 double angles
[] = {1.35, 1.07, 0.88, 0.745};
62 const char thruster_pn
[80] ="BCAAABACBBABCABBCAACBBBACBCCBBCABACACABCBABCBBACBABCCABACCAAABBCBABCAACBAABBACC";
64 double calc_slope(double x
)
66 return 1.6 * (hscale
* x
) / (1 / z0
- yescape
);
69 std::pair
<signed, signed> point_project(double x
, double y
, double z
)
71 double c
= (z0
+ zscale
* z
);
75 double s
= (y0
- yescape
) / (1 / z0
- yescape
);
77 return std::make_pair(FB_WIDTH
* (hscale
* x
* s
+ 0.5), FB_HEIGHT
* (y0
- vscale
* y
* s
));
79 return std::make_pair(FB_WIDTH
/ 2, FB_HEIGHT
* yescape
);
82 point_t
point_project_b(double x
, double y
, double z
)
84 double c
= (z0
+ zscale
* z
);
88 double s
= (y0
- yescape
) / (1 / z0
- yescape
);
91 p
.x
= FB_WIDTH
* (hscale
* x
* s
+ 0.5);
92 p
.y
= FB_HEIGHT
* (y0
- vscale
* y
* s
);
95 p
.y
= FB_HEIGHT
* yescape
;
100 int32_t ship_sprite_normal(uint16_t hpos
, int vdir
, uint8_t thruster
)
103 if(hpos
< 18048) hdir
= 0;
104 else if(hpos
< 23936) hdir
= 1;
105 else if(hpos
< 29824) hdir
= 2;
106 else if(hpos
< 35712) hdir
= 3;
107 else if(hpos
< 41600) hdir
= 4;
108 else if(hpos
< 47488) hdir
= 5;
110 if(vdir
== -1) vdir
= 2;
111 return 9 * hdir
+ 3 * vdir
+ thruster
+ 14;
114 int32_t ship_sprite_explode(int32_t frame
)
116 return (frame
< 42) ? (frame
/ 3) : -1;
119 int32_t ship_sprite(physics
& p
)
122 return ship_sprite_explode(p
.expframe
);
123 if(p
.death
== physics::death_finished
)
127 if(p
.vspeed
> 300) vdir
= 1;
128 else if(p
.vspeed
< -300) vdir
= -1;
130 if(p
.death
== physics::death_fuel
)
133 //Some kind of PN sequence.
134 thruster
= thruster_pn
[p
.framecounter
% (sizeof(thruster_pn
) - 1)] - 'A';
136 return ship_sprite_normal(p
.hpos
, vdir
, thruster
% 3);
139 void draw_sprite(double h
, double v
, int32_t num
)
143 signed x
= FB_WIDTH
* hscale
* h
+ FB_WIDTH
/ 2 - FB_SCALE
* 15;
144 signed y
= FB_HEIGHT
/ z0
- FB_HEIGHT
* vscale
* v
- FB_SCALE
* ship
.width
+ FB_SCALE
;
145 uint32_t offset
= ship
.width
* 30 * num
;
146 //For some darn reason, CARS.LZS has the image rotated 90 degrees counterclockwise.
147 for(signed j
= 0; j
< FB_SCALE
* (int)ship
.width
; j
++) {
148 if(y
+ j
< 0 || y
+ j
> overlap_end
)
150 uint32_t offset2
= offset
+ j
/ FB_SCALE
;
151 size_t base
= FB_WIDTH
* (y
+ j
) + x
;
152 for(signed i
= 0; i
< 30 * FB_SCALE
; i
++) {
155 if(x
+ i
>= FB_WIDTH
)
157 uint8_t c
= ship
.decode
[offset2
+ ship
.width
* (i
/ FB_SCALE
)];
160 uint32_t pix
= ship
.palette
[c
] & 0xFFFFFF;
161 if((framebuffer
[base
+ i
] >> 24) == 0)
162 framebuffer
[base
+ i
] = pix
;
167 void draw_grav_g_meter(gstate
& s
)
169 uint16_t realgrav
= 100 * (s
.p
.gravity
- 3);
172 static size_t sep
= strlen(_numbers_g
) / 10;
174 uint8_t digit
= realgrav
% 10;
175 draw_block2(_numbers_g
+ sep
* digit
, y
* 320 + (x
- 5), dashpalette
[6], dashpalette
[5],
182 void draw_indicator(uint8_t& curval
, uint8_t newval
, gauge
& g
, uint8_t on1
, uint8_t on2
, uint8_t off1
,
185 unsigned tmp
= newval
;
186 if(tmp
> g
.maxlimit()) tmp
= g
.maxlimit();
188 for(unsigned i
= curval
; i
< tmp
; i
++) {
189 draw_block(g
.get_data(i
), g
.get_position(i
), dashpalette
[on1
], dashpalette
[on2
]);
192 for(unsigned i
= tmp
; i
< curval
; i
++) {
193 draw_block(g
.get_data(i
), g
.get_position(i
), dashpalette
[off1
], dashpalette
[off2
]);
198 void draw_gauges(gstate
& s
)
200 //draw_grav_g_meter(s);
201 uint8_t timermod
= ((s
.p
.framecounter
% 9) > 4) ? 1 : 0;
202 draw_indicator(s
.speedind
, (s
.p
.lspeed
- s
.p
.speedbias
) / 0x141, speed_dat
, 2, 3, 0, 1);
203 draw_indicator(s
.o2ind
, (s
.p
.o2_left
+ 0xbb7) / 0xbb8, oxydisp_dat
, 2, 3, 0, 1);
204 draw_indicator(s
.fuelind
, (s
.p
.fuel_left
+ 0xbb7) / 0xbb8, fueldisp_dat
, 2, 3, 0, 1);
206 bool lck
= s
.p
.is_set(physics::flag_locked
);
208 draw_block2(_lockind_g
+ (lck
? strlen(_lockind_g
) / 2 : 0), 0x9c * 320 + 0xcb,
209 dashpalette
[6], dashpalette
[5], true);
211 //Out of oxygen blink&beep.
212 if(s
.p
.death
== physics::death_o2
&& s
.beep_phase
!= timermod
) {
213 blink_between(0xa0, 0xa1, 7, 7, dashpalette
[7], dashpalette
[8]);
217 //Out of fuel blink&beep.
218 if(s
.p
.death
== physics::death_fuel
&& s
.beep_phase
!= timermod
) {
219 blink_between(0x9b, 0xa9, 16, 5, dashpalette
[7], dashpalette
[8]);
224 uint32_t res
= s
.curlevel
.apparent_length() / 88;
225 size_t tmp
= (s
.p
.lpos
- 3 * 65536) / res
;
226 for(unsigned i
= s
.distind
; i
< tmp
&& i
< 88; i
++)
227 draw_distance_column(i
, dashpalette
[4]);
229 s
.beep_phase
= timermod
;
232 inline double horizon_distance() { return (1 / yescape
- z0
) / zscale
; }
233 inline double near_distance() { return (600 / overlap_end
- z0
) / zscale
; }
236 void draw_quad_x(point_t p1
, point_t p2
, point_t p3
, point_t p4
, uint32_t color
)
238 if(fabs(p1
.x
- p2
.x
) < 0.1)
240 double qp1
= min(p1
.x
, p2
.x
);
241 double qp2
= max(p1
.x
, p2
.x
);
244 double utmax
= max(p1
.y
, p2
.y
);
245 double utmin
= min(p1
.y
, p2
.y
);
246 double ltmax
= max(p3
.y
, p4
.y
);
247 double ltmin
= min(p3
.y
, p4
.y
);
248 signed y1
= max((int)floor(utmin
), 0);
249 signed y2
= min((int)ceil(ltmax
), (int)overlap_end
);
250 for(signed j
= y1
; j
< y2
; j
++) {
251 signed xstart
= x1
, xend
= x2
;
254 xstart
= floor((qp1
- qp2
) * (j
- p2
.y
) / (p1
.y
- p2
.y
) + qp2
);
256 xend
= ceil((qp2
- qp1
) * (j
- p1
.y
) / (p2
.y
- p1
.y
) + qp1
);
260 xend
= ceil((qp1
- qp2
) * (j
- p4
.y
) / (p3
.y
- p4
.y
) + qp2
);
262 xstart
= floor((qp2
- qp1
) * (j
- p3
.y
) / (p4
.y
- p3
.y
) + qp1
);
264 xstart
= max(xstart
, 0);
265 xend
= min(xend
, FB_WIDTH
);
266 size_t base
= FB_WIDTH
* j
;
267 if(j
< overlap_start
)
268 for(signed i
= xstart
; i
< xend
; i
++)
269 framebuffer
[base
+ i
] = color
;
271 for(signed i
= xstart
; i
< xend
; i
++)
272 framebuffer_blend2(framebuffer
[base
+ i
], color
);
276 void draw_quad_y(point_t p1
, point_t p2
, point_t p3
, point_t p4
, uint32_t color
)
278 if(fabs(p1
.y
- p3
.y
) < 0.1)
280 signed y1
= max((int)floor(p1
.y
), 0);
281 signed y2
= min((int)ceil(p3
.y
), (int)overlap_end
);
282 for(signed j
= y1
; j
< y2
; j
++) {
284 xstart
= floor((p3
.x
- p1
.x
) * (j
- p1
.y
) / (p3
.y
- p1
.y
) + p1
.x
);
285 xend
= ceil((p4
.x
- p2
.x
) * (j
- p1
.y
) / (p3
.y
- p1
.y
) + p2
.x
);
286 xstart
= max(xstart
, 0);
287 xend
= min(xend
, FB_WIDTH
);
288 size_t base
= FB_WIDTH
* j
;
289 if(j
< overlap_start
)
290 for(signed i
= xstart
; i
< xend
; i
++)
291 framebuffer
[base
+ i
] = color
;
293 for(signed i
= xstart
; i
< xend
; i
++)
294 framebuffer_blend2(framebuffer
[base
+ i
], color
);
298 void draw_quad_z(point_t p1
, point_t p2
, point_t p3
, point_t p4
, uint32_t color
)
300 if(fabs(p1
.x
- p2
.x
) < 0.1 || fabs(p1
.y
- p3
.y
) < 0.1)
302 signed x1
= max((int)floor(p1
.x
), 0);
303 signed x2
= min((int)ceil(p2
.x
), FB_WIDTH
);
304 signed y1
= max((int)floor(p1
.y
), 0);
305 signed y2
= min((int)ceil(p3
.y
), (int)overlap_end
);
306 for(signed j
= y1
; j
< y2
; j
++) {
307 size_t base
= FB_WIDTH
* j
;
308 if(j
< overlap_start
)
309 for(signed i
= x1
; i
< x2
; i
++)
310 framebuffer
[base
+ i
] = color
;
312 for(signed i
= x1
; i
< x2
; i
++)
313 framebuffer_blend2(framebuffer
[base
+ i
], color
);
317 void draw_quad_z_tunshadow(point_t p1
, point_t p2
, point_t p3
, point_t p4
, uint32_t color
, int x
)
319 if(fabs(p1
.x
- p2
.x
) < 0.1 || fabs(p1
.y
- p3
.y
) < 0.1)
322 return; //Center pipe has no shadow.
323 signed x1
= max((int)floor(p1
.x
), 0);
324 signed x2
= min((int)ceil(p2
.x
), FB_WIDTH
);
325 signed y1
= max((int)floor(p1
.y
), 0);
326 signed y2
= min((int)ceil(p3
.y
), (int)overlap_end
);
327 double slope
= calc_slope(slopepoints
[x
+ 3]);
328 const double width
= p2
.x
- p1
.x
;
329 const double hwidth
= 0.45 * width
;
330 const double center
= (p1
.x
+ p2
.x
) / 2;
331 for(signed j
= y1
; j
< y2
; j
++) {
332 signed c1
= x2
, c2
= x1
;
333 size_t base
= FB_WIDTH
* j
;
334 double ry
= (p3
.y
- j
) / (0.625 * vscale
/ hscale
* 2 * hwidth
);
336 c1
= center
- hwidth
* sqrt(1 - ry
* ry
);
337 c2
= center
+ hwidth
* sqrt(1 - ry
* ry
);
340 //The shadow is on left.
341 c2
= center
- hwidth
- slope
* (p3
.y
- j
);
343 //The shadow is on right.
344 c1
= center
+ hwidth
- slope
* (p3
.y
- j
);
347 c2
= min(c2
, FB_WIDTH
);
348 if(j
< overlap_start
)
349 for(signed i
= c1
; i
< c2
; i
++)
350 framebuffer
[base
+ i
] = color
;
352 for(signed i
= c1
; i
< c2
; i
++)
353 framebuffer_blend2(framebuffer
[base
+ i
], color
);
357 void draw_quad_z_tunnel(point_t p1
, point_t p2
, point_t p3
, point_t p4
, uint32_t color
)
359 if(fabs(p1
.x
- p2
.x
) < 0.1 || fabs(p1
.y
- p3
.y
) < 0.1)
361 signed x1
= max((int)floor(p1
.x
), 0);
362 signed x2
= min((int)ceil(p2
.x
), FB_WIDTH
);
363 signed y1
= max((int)floor(p1
.y
), 0);
364 signed y2
= min((int)ceil(p3
.y
), (int)overlap_end
);
365 const double width
= p2
.x
- p1
.x
;
366 const double hwidth
= 0.45 * width
;
367 const double center
= (p1
.x
+ p2
.x
) / 2;
368 for(signed j
= y1
; j
< y2
; j
++) {
369 signed c1
= x2
, c2
= x2
;
370 size_t base
= FB_WIDTH
* j
;
371 double ry
= (p3
.y
- j
) / (0.625 * vscale
/ hscale
* 2 * hwidth
);
373 c1
= center
- hwidth
* sqrt(1 - ry
* ry
);
374 c2
= center
+ hwidth
* sqrt(1 - ry
* ry
);
378 x2
= min(x2
, FB_WIDTH
);
379 c1
= min(c1
, FB_WIDTH
);
380 if(j
< overlap_start
) {
381 for(signed i
= x1
; i
< c1
; i
++)
382 framebuffer
[base
+ i
] = color
;
383 for(signed i
= c2
; i
< x2
; i
++)
384 framebuffer
[base
+ i
] = color
;
386 for(signed i
= x1
; i
< c1
; i
++)
387 framebuffer_blend2(framebuffer
[base
+ i
], color
);
388 for(signed i
= c2
; i
< x2
; i
++)
389 framebuffer_blend2(framebuffer
[base
+ i
], color
);
394 void draw_quad_z_pipefront(double z
, int x
, uint32_t color
)
396 struct pipe_cache
& p
= pipecache
[x
+ 3];
397 auto p5
= point_project_b(x
- 0.5, 0, z
);
398 auto p6
= point_project_b(x
, 1, z
);
399 auto p7
= point_project_b(x
+ 0.5, 0, z
);
402 int16_t ymin
= floor(ymind
);
403 int16_t ymax
= ceil(ymaxd
);
404 if(ymaxd
- ymind
< 0.1)
406 int16_t cheight
= floor(p5
.y
- p6
.y
);
407 int16_t ciheight
= floor(0.9 * (p5
.y
- p6
.y
));
408 int16_t cwidth
= floor((p7
.x
- p5
.x
) / 2);
409 int16_t ciwidth
= floor(0.9 * (p7
.x
- p5
.x
) / 2);
410 int16_t ccenter
= floor((p7
.x
+ p5
.x
) / 2);
411 int16_t nxs
= ccenter
- cwidth
;
412 int16_t nxe
= ccenter
+ cwidth
;
413 for(signed j
= ymax
- cheight
; j
< ymax
; j
++) {
414 if(j
< 0 || j
> overlap_end
)
416 int16_t dstart
= nxs
;
418 int16_t cstart
= ccenter
;
419 int16_t cend
= ccenter
;
420 int16_t cistart
= ccenter
;
421 int16_t ciend
= ccenter
;
423 int16_t o
= ymax
- j
;
424 double o2
= 1.0 * o
/ cheight
;
425 int16_t w
= cwidth
* sqrt(1 - o2
* o2
);
429 if(j
>= ymax
- ciheight
) {
430 int16_t o
= ymax
- j
;
431 double o2
= 1.0 * o
/ ciheight
;
432 int16_t w
= ciwidth
* sqrt(1 - o2
* o2
);
436 dstart
= max(cstart
, (int16_t)0);
437 dend
= min(cend
, (int16_t)FB_WIDTH
);
438 size_t base
= FB_WIDTH
* j
;
439 if(j
< overlap_start
)
440 for(signed i
= dstart
; i
< dend
; i
++) {
441 if(i
< cistart
|| i
>= ciend
)
442 framebuffer
[base
+ i
] = color
;
445 for(signed i
= dstart
; i
< dend
; i
++) {
446 if(i
< cistart
|| i
>= ciend
)
447 framebuffer_blend2(framebuffer
[base
+ i
], color
);
452 // x = (k*y0+b)*c*(0.5*sin(alpha)+X0)
453 // y = -(k*y0+b)*d*cos(alpha)+y0
455 // dx/dy0 = k*c*0.5*sin(alpha)+k*c*X0
456 // dy/dy0 = k*d*cos(alpha)+1
457 // dx/dy = (k*c*0.5*sin(alpha)+k*c*X0)/(k*d*cos(alpha)+1)
458 // d/dalpha[(k*c*0.5*sin(alpha)+k*c*X0)/(k*d*cos(alpha)+1)]
459 //=(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
461 // 0.5*k*d+0.5*cos(alpha)+X0*(k*d*sin(alpha)) = 0
463 uint32_t mix_color(uint32_t* c
, uint32_t c2
)
465 uint32_t low
= c
[c2
/ 256];
466 uint32_t high
= c
[c2
/ 256 + 1];
467 uint16_t mod
= c2
% 256;
468 uint16_t imod
= 256 - mod
;
469 const uint32_t Lm
= 0x00FF00FF;
470 const uint32_t Hm
= 0xFF00FF00;
471 uint32_t L
= (((low
& Lm
) * imod
+ (high
& Lm
) * mod
) >> 8) & Lm
;
472 uint32_t H
= (((low
& Hm
) >> 8) * imod
+ ((high
& Hm
) >> 8) * mod
) & Hm
;
476 void rebuild_pipe_quad_cache(pipe_cache
& p
, int x
, uint32_t* c
)
478 double k
= 1 / (1 / z0
- yescape
);
479 double b
= -yescape
* k
;
480 double gmin
= 999999;
481 double gmax
= -999999;
482 double amin
= 999999;
483 double amax
= -999999;
484 double y00
= (1-b
)/k
;
485 for(unsigned i
= 0; i
< 256; i
++)
486 p
.colors
[i
] = 0xFFFFFFFF;
487 //Compute span range.
488 for(double a
= -M_PI
/ 2; a
< M_PI
/ 2; a
+= 0.01) {
489 double A
= vscale
* cos(a
);
490 double y0
= (y00
+ b
* A
) / (1 - k
* A
);
491 double xp
= (k
* y0
+ b
) * (x
+ 0.5 * sin(a
));
493 p
.min_h
= x
+ 0.5 * sin(a
);
499 p
.max_h
= x
+ 0.5 * sin(a
);
505 for(double a
= amin
; a
< amax
; a
+= 0.01) {
506 double A
= vscale
* cos(a
);
507 double y0
= (y00
+ b
* A
) / (1 - k
* A
);
508 double xp
= (k
* y0
+ b
) * (x
+ 0.5 * sin(a
));
509 signed c2
= (a
+ M_PI
/ 2) * 1280 / M_PI
;
510 signed x
= 255 * (xp
- gmin
) / (gmax
- gmin
);
511 c2
= max(min(c2
, 1280), 0);
512 x
= max(min(x
, 255), 0);
513 p
.colors
[x
] = mix_color(c
, c2
);
515 for(unsigned i
= 0; i
< 256; i
++) {
516 if(p
.colors
[i
] == 0xFFFFFFFF)
518 for(unsigned j
= 0; j
< 255; j
++)
519 if(p
.colors
[j
] != 0xFFFFFFFF) {
520 p
.colors
[i
] = p
.colors
[j
];
524 p
.colors
[i
] = p
.colors
[i
- 1];
526 //for(unsigned i = 0; i < 256; i++)
527 // p.colors[i] = 0xFF8000 + i;
530 void rebuild_pipe_quad_caches(uint32_t color1
, uint32_t color2
, uint32_t color3
, uint32_t color4
)
539 for(int x
= 0; x
< 7; x
++)
540 rebuild_pipe_quad_cache(pipecache
[x
], x
- 3, c
);
543 void draw_quad_y_pipe_last(double z
, int x
, bool top
)
545 struct pipe_cache
& p
= pipecache
[x
+ 3];
546 uint32_t fcolor
= (!top
&& z
< 0.2) ? 0x1000000 : 0;
547 //We need these for slope projection.
548 auto p1
= point_project_b(p
.min_h
, p
.min_v
, z
);
549 auto p2
= point_project_b(p
.max_h
, p
.max_v
, z
);
550 auto p3
= point_project_b(p
.min_h
, p
.min_v
, z
- 1);
551 auto p4
= point_project_b(p
.max_h
, p
.max_v
, z
- 1);
552 auto p5
= point_project_b(x
- 0.5, 0, z
);
553 auto p6
= point_project_b(x
, 1, z
);
554 auto p7
= point_project_b(x
+ 0.5, 0, z
);
555 if(p3
.y
- p1
.y
< 0.1)
557 if(p4
.y
- p2
.y
< 0.1)
561 int16_t ymin
= floor(ymind
);
562 int16_t ymax
= ceil(ymaxd
);
563 double sl1
= (p3
.x
- p1
.x
) / (p3
.y
- p1
.y
);
564 double sl2
= (p4
.x
- p2
.x
) / (p4
.y
- p2
.y
);
565 double c1
= p1
.x
- sl1
* p1
.y
;
566 double c2
= p2
.x
- sl2
* p2
.y
;
567 int16_t cheight
= floor(p5
.y
- p6
.y
);
568 int16_t cwidth
= floor((p7
.x
- p5
.x
) / 2);
569 int16_t ccenter
= floor((p7
.x
+ p5
.x
) / 2);
570 for(signed j
= ymax
- cheight
; j
< ymax
; j
++) {
571 if(j
< 0 || j
> overlap_end
)
573 int16_t nxs
= floor(sl1
* j
+ c1
);
574 int16_t nxe
= ceil(sl2
* j
+ c2
);
575 int16_t dstart
= nxs
;
577 uint32_t cstep
= 255 * 65536 / (nxe
- nxs
+ 1);
580 int16_t o
= ymax
- j
;
581 double o2
= 1.0 * o
/ cheight
;
582 int16_t w
= cwidth
* sqrt(1 - o2
* o2
);
583 dstart
= ccenter
- w
;
586 dstart
= max(max(dstart
, nxs
), (int16_t)0);
587 dend
= min(min(dend
, nxe
), (int16_t)FB_WIDTH
);
589 color
+= (dstart
- nxs
) * cstep
;
590 size_t base
= FB_WIDTH
* j
;
591 if(j
< overlap_start
)
592 for(signed i
= dstart
; i
< dend
; i
++) {
593 framebuffer
[base
+ i
] = fcolor
| p
.colors
[color
>> 16];
597 for(signed i
= dstart
; i
< dend
; i
++) {
598 framebuffer_blend2(framebuffer
[base
+ i
], fcolor
| p
.colors
[color
>> 16]);
604 void draw_quad_y_pipe_first(double z1
, double z2
, int x
, bool top
)
606 struct pipe_cache
& p
= pipecache
[x
+ 3];
607 uint32_t fcolor
= (!top
&& z1
< 0) ? 0x1000000 : 0;
608 auto p1
= point_project_b(p
.min_h
, p
.min_v
, z2
);
609 auto p2
= point_project_b(p
.max_h
, p
.max_v
, z2
);
610 auto p3
= point_project_b(p
.min_h
, p
.min_v
, z1
);
611 auto p4
= point_project_b(p
.max_h
, p
.max_v
, z1
);
612 auto p5
= point_project_b(x
- 0.5, 0, z1
);
613 auto p6
= point_project_b(x
, 1, z1
);
614 auto p7
= point_project_b(x
+ 0.5, 0, z1
);
615 double ymind
= min(p1
.y
, p2
.y
);
617 int16_t ymin
= floor(ymind
);
618 int16_t ymax
= ceil(ymaxd
);
619 int16_t ymin2
= ceil(max(p1
.y
, p2
.y
));
620 if(p3
.y
- p1
.y
< 0.1)
622 if(p4
.y
- p2
.y
< 0.1)
624 double sl1
= (p3
.x
- p1
.x
) / (p3
.y
- p1
.y
);
625 double sl2
= (p4
.x
- p2
.x
) / (p4
.y
- p2
.y
);
626 double c1
= p1
.x
- sl1
* p1
.y
;
627 double c2
= p2
.x
- sl2
* p2
.y
;
628 int16_t cheight
= floor(p5
.y
- p6
.y
);
629 int16_t cwidth
= floor((p7
.x
- p5
.x
) / 2);
630 int16_t ccenter
= floor((p7
.x
+ p5
.x
) / 2);
632 uint16_t mindist
= 65535;
633 for(signed j
= ymin
; j
< ymax
; j
++) {
634 if(j
< 0 || j
> overlap_end
)
636 int16_t nxs
= floor(sl1
* j
+ c1
);
637 int16_t nxe
= ceil(sl2
* j
+ c2
);
638 int16_t dstart
= nxs
;
640 uint32_t cstep
= 255 * 65536 / (nxe
- nxs
+ 1);
642 int16_t cstart
= ccenter
;
643 int16_t cend
= ccenter
;
644 if(j
< ymin2
&& p1
.y
!= p2
.y
) {
645 //The upper triangular region.
647 dend
= ceil((p2
.x
- p1
.x
) * (j
- p1
.y
) / (p2
.y
- p1
.y
) + p1
.x
);
649 dstart
= floor((p2
.x
- p1
.x
) * (j
- p1
.y
) / (p2
.y
- p1
.y
) + p1
.x
);
651 if(j
>= ymax
- cheight
) {
652 int16_t o
= ymax
- j
;
653 double o2
= 1.0 * o
/ cheight
;
654 int16_t w
= cwidth
* sqrt(1 - o2
* o2
);
659 dstart
= max((int16_t)dstart
, cstart
);
661 dend
= min((int16_t)dend
, cend
);
665 dist
= cstart
- dstart
;
675 dstart
= max(max(dstart
, nxs
), (int16_t)0);
676 dend
= min(min(dend
, nxe
), (int16_t)FB_WIDTH
);
678 color
+= (dstart
- nxs
) * cstep
;
679 size_t base
= FB_WIDTH
* j
;
680 if(j
< overlap_start
)
681 for(signed i
= dstart
; i
< dend
; i
++) {
682 if(i
< cstart
|| i
>= cend
)
683 framebuffer
[base
+ i
] = fcolor
| p
.colors
[color
>> 16];
687 for(signed i
= dstart
; i
< dend
; i
++) {
688 if(i
< cstart
|| i
>= cend
)
689 framebuffer_blend2(framebuffer
[base
+ i
], fcolor
|
690 p
.colors
[color
>> 16]);
696 void draw_quad_y_pipe(double z1
, double z2
, int x
, bool top
)
698 struct pipe_cache
& p
= pipecache
[x
+ 3];
699 uint32_t fcolor
= (!top
&& z1
< 0.2) ? 0x1000000 : 0;
700 auto p1
= point_project_b(p
.min_h
, p
.min_v
, z2
);
701 auto p2
= point_project_b(p
.max_h
, p
.max_v
, z2
);
702 auto p3
= point_project_b(p
.min_h
, p
.min_v
, z1
);
703 auto p4
= point_project_b(p
.max_h
, p
.max_v
, z1
);
704 double ymind
= min(p1
.y
, p2
.y
);
705 double ymaxd
= max(p3
.y
, p4
.y
);
706 int16_t ymin
= floor(ymind
);
707 int16_t ymax
= ceil(ymaxd
) + 1;
708 int16_t ymin2
= ceil(max(p1
.y
, p2
.y
));
709 int16_t ymax2
= floor(min(p3
.y
, p4
.y
));
710 if(p3
.y
- p1
.y
< 0.1)
712 if(p4
.y
- p2
.y
< 0.1)
714 double sl1
= (p3
.x
- p1
.x
) / (p3
.y
- p1
.y
);
715 double sl2
= (p4
.x
- p2
.x
) / (p4
.y
- p2
.y
);
716 double c1
= p1
.x
- sl1
* p1
.y
;
717 double c2
= p2
.x
- sl2
* p2
.y
;
718 //FIXME: This leaves some graphical glitching in seams.
719 for(signed j
= ymin
; j
< ymax
; j
++) {
720 if(j
< 0 || j
> overlap_end
)
722 int16_t nxs
= floor(sl1
* j
+ c1
);
723 int16_t nxe
= ceil(sl2
* j
+ c2
);
724 int16_t dstart
= nxs
;
726 uint32_t cstep
= 255 * 65536 / (nxe
- nxs
+ 1);
728 if(j
< ymin2
&& p1
.y
!= p2
.y
) {
729 //The upper triangular region.
731 dend
= ceil((p2
.x
- p1
.x
) * (j
- p1
.y
) / (p2
.y
- p1
.y
) + p1
.x
);
733 dstart
= floor((p2
.x
- p1
.x
) * (j
- p1
.y
) / (p2
.y
- p1
.y
) + p1
.x
);
735 if(j
>= ymax2
&& p3
.y
!= p4
.y
) {
736 //The lower triangular region.
738 dstart
= floor((p4
.x
- p3
.x
) * (j
- p3
.y
- 1) / (p4
.y
- p3
.y
) + p3
.x
);
740 dend
= ceil((p4
.x
- p3
.x
) * (j
- p3
.y
- 1) / (p4
.y
- p3
.y
) + p3
.x
);
742 dstart
= max(max(dstart
, nxs
), (int16_t)0);
743 dend
= min(min(dend
, nxe
), (int16_t)FB_WIDTH
);
745 color
+= (dstart
- nxs
) * cstep
;
746 size_t base
= FB_WIDTH
* j
;
747 if(j
< overlap_start
)
748 for(signed i
= dstart
; i
< dend
; i
++) {
749 framebuffer
[base
+ i
] = fcolor
| p
.colors
[color
>> 16];
753 for(signed i
= dstart
; i
< dend
; i
++) {
754 framebuffer_blend2(framebuffer
[base
+ i
], fcolor
| p
.colors
[color
>> 16]);
760 void draw_level(gstate
& s
)
762 for(unsigned i
= 0; i
< overlap_start
; i
++)
763 for(unsigned j
= 0; j
< FB_WIDTH
; j
++)
764 framebuffer
[i
* FB_WIDTH
+ j
] = origbuffer
[(i
/ FB_SCALE
) * 320 + (j
/ FB_SCALE
)];
765 for(unsigned i
= overlap_start
; i
< overlap_end
; i
++)
766 for(unsigned j
= 0; j
< FB_WIDTH
; j
++)
767 framebuffer_blend2(framebuffer
[i
* FB_WIDTH
+ j
], origbuffer
[(i
/ FB_SCALE
) * 320 +
769 static signed dorder
[] = {-3, 3, -2, 2, -1, 1, 0};
770 level
& l
= s
.curlevel
;
771 double zship
= s
.p
.lpos
/ 65536.0;
772 double horizon
= horizon_distance();
773 double near
= near_distance();
774 signed maxtile
= ceil(zship
+ horizon
);
775 signed mintile
= floor(zship
+ near
- 1);
776 for(signed zt
= maxtile
; zt
>= mintile
; zt
--) {
777 for(signed dxt
= 0; dxt
< 7; dxt
++) {
778 signed xt
= dorder
[dxt
];
779 tile t
= l
.at_tile(zt
, xt
);
780 tile tleft
= l
.at_tile(zt
, xt
- 1);
781 tile tright
= l
.at_tile(zt
, xt
+ 1);
782 tile tfront
= l
.at_tile(zt
- 1, xt
);
783 tile tback
= l
.at_tile(zt
+ 1, xt
);
784 //First, draw the floor level.
785 if(t
.lower_floor() && (!t
.is_rblock() || t
.is_tunnel())) {
786 bool ifront
= (s
.p
.vpos
< 10240);
787 ifront
&= (xt
>= 0 || s
.p
.hpos
< 5888 * xt
+ 32768 - 2944);
788 ifront
&= (xt
<= 0 || s
.p
.hpos
> 5888 * xt
+ 32768 + 2944);
789 draw_quad_y(point_project_b(xt
- 0.5, 0, zt
+ 1 - zship
),
790 point_project_b(xt
+ 0.5, 0, zt
+ 1 - zship
),
791 point_project_b(xt
- 0.5, 0, zt
- zship
),
792 point_project_b(xt
+ 0.5, 0, zt
- zship
),
793 l
.get_palette_color(top_palette
[t
.lower_floor()], ifront
));
796 if(t
.is_tunnel() && !tfront
.is_block()) {
797 //Pipe shadow never obscures the ship.
798 draw_quad_z_tunshadow(point_project_b(xt
- 0.5, 1, zt
- zship
),
799 point_project_b(xt
+ 0.5, 1, zt
- zship
),
800 point_project_b(xt
- 0.5, 0, zt
- zship
),
801 point_project_b(xt
+ 0.5, 0, zt
- zship
),
802 l
.get_palette_color(t
.is_rblock() ? 65 : 67), xt
);
804 //Draw the top surface.
806 signed q
= t
.apparent_height();
807 bool ifront
= (s
.p
.vpos
< 10240 + q
* 2560);
808 ifront
&= (xt
>= 0 || s
.p
.hpos
< 5888 * xt
+ 32768 - 2944);
809 ifront
&= (xt
<= 0 || s
.p
.hpos
> 5888 * xt
+ 32768 + 2944);
810 ifront
|= (l
.in_pipe(zt
* 65536, s
.p
.hpos
, s
.p
.vpos
) && zt
< zship
);
811 draw_quad_y(point_project_b(xt
- 0.5, q
, zt
+ 1 - zship
),
812 point_project_b(xt
+ 0.5, q
, zt
+ 1 - zship
),
813 point_project_b(xt
- 0.5, q
, zt
- zship
),
814 point_project_b(xt
+ 0.5, q
, zt
- zship
),
815 l
.get_palette_color(top_palette
[t
.upper_floor()], ifront
));
817 //Left/Right block surface.
818 if(t
.is_rblock() && xt
< 0 && tright
.apparent_height() < t
.apparent_height()) {
819 signed q1
= tright
.apparent_height();
820 signed q2
= t
.apparent_height();
821 bool ifront
= (s
.p
.vpos
< 10240 + q2
* 2560);
822 ifront
&= (s
.p
.hpos
< 5888 * xt
+ 32768 + 2944);
823 draw_quad_x(point_project_b(xt
+ 0.5, q2
, zt
- zship
),
824 point_project_b(xt
+ 0.5, q2
, zt
+ 1 - zship
),
825 point_project_b(xt
+ 0.5, q1
, zt
- zship
),
826 point_project_b(xt
+ 0.5, q1
, zt
+ 1 - zship
),
827 l
.get_palette_color(63, ifront
));
829 if(t
.is_rblock() && xt
> 0 && tleft
.apparent_height() < t
.apparent_height()) {
830 signed q1
= tleft
.apparent_height();
831 signed q2
= t
.apparent_height();
832 bool ifront
= (s
.p
.vpos
< 10240 + q2
* 2560);
833 ifront
&= (s
.p
.hpos
> 5888 * xt
+ 32768 - 2944);
834 draw_quad_x(point_project_b(xt
- 0.5, q2
, zt
+ 1 - zship
),
835 point_project_b(xt
- 0.5, q2
, zt
- zship
),
836 point_project_b(xt
- 0.5, q1
, zt
+ 1- zship
),
837 point_project_b(xt
- 0.5, q1
, zt
- zship
),
838 l
.get_palette_color(64, ifront
));
840 //Front block surface.
841 if(t
.is_rblock() && tfront
.apparent_height() < t
.apparent_height()) {
842 signed q1
= tfront
.apparent_height();
843 signed q2
= t
.apparent_height();
844 bool ifront
= (zt
< zship
);
845 if(t
.is_tunnel() && !tfront
.is_block())
846 draw_quad_z_tunnel(point_project_b(xt
- 0.5, q2
, zt
- zship
),
847 point_project_b(xt
+ 0.5, q2
, zt
- zship
),
848 point_project_b(xt
- 0.5, q1
, zt
- zship
),
849 point_project_b(xt
+ 0.5, q1
, zt
- zship
),
850 l
.get_palette_color(62, ifront
));
852 draw_quad_z(point_project_b(xt
- 0.5, q2
, zt
- zship
),
853 point_project_b(xt
+ 0.5, q2
, zt
- zship
),
854 point_project_b(xt
- 0.5, q1
, zt
- zship
),
855 point_project_b(xt
+ 0.5, q1
, zt
- zship
),
856 l
.get_palette_color(62, ifront
));
860 if(t
.is_tunnel() && !t
.is_rblock()) {
861 bool top
= (s
.p
.vpos
> 10240);
862 top
&= !l
.in_pipe(zt
* 65536, s
.p
.hpos
, s
.p
.vpos
);
863 if(tback
.is_rblock() || !tback
.is_block())
864 draw_quad_y_pipe_last(zt
+ 1 - zship
, xt
, top
);
865 //Standalone tunnel top.
866 if(tfront
.is_block())
867 draw_quad_y_pipe(zt
- zship
, zt
+ 1 - zship
, xt
, top
);
869 draw_quad_y_pipe_first(zt
- zship
, zt
+ 1 - zship
, xt
, top
);
870 //Standalone tunnel front.
871 if(!tfront
.is_block()) {
872 bool ifront
= (zt
< zship
);
873 draw_quad_z_pipefront(zt
- zship
, xt
, l
.get_palette_color(66, ifront
));
876 //Left/Right floor surface.
877 if(xt
< 0 && t
.lower_floor() && !tright
.lower_floor()) {
878 bool ifront
= (s
.p
.vpos
< 10240);
879 ifront
&= (s
.p
.hpos
> 5888 * xt
+ 32768);
880 draw_quad_x(point_project_b(xt
+ 0.5, 0, zt
- zship
),
881 point_project_b(xt
+ 0.5, 0, zt
+ 1 - zship
),
882 point_project_b(xt
+ 0.5, -fthickness
, zt
- zship
),
883 point_project_b(xt
+ 0.5, -fthickness
, zt
+ 1 - zship
),
884 l
.get_palette_color(right_palette
[t
.lower_floor()]));
886 if(xt
> 0 && t
.lower_floor() && !tleft
.lower_floor()) {
887 bool ifront
= (s
.p
.vpos
< 10240);
888 ifront
&= (s
.p
.hpos
> 5888 * xt
+ 32768);
889 draw_quad_x(point_project_b(xt
- 0.5, 0, zt
+ 1 - zship
),
890 point_project_b(xt
- 0.5, 0, zt
- zship
),
891 point_project_b(xt
- 0.5, -fthickness
, zt
+ 1- zship
),
892 point_project_b(xt
- 0.5, -fthickness
, zt
- zship
),
893 l
.get_palette_color(left_palette
[t
.lower_floor()], ifront
));
895 //Front floor surface.
896 if(t
.lower_floor() && !tfront
.lower_floor()) {
897 bool ifront
= (s
.p
.vpos
< 10240);
898 ifront
&= (s
.p
.hpos
< 5888 * xt
+ 32768);
899 draw_quad_z(point_project_b(xt
- 0.5, 0, zt
- zship
),
900 point_project_b(xt
+ 0.5, 0, zt
- zship
),
901 point_project_b(xt
- 0.5, -fthickness
, zt
- zship
),
902 point_project_b(xt
+ 0.5, -fthickness
, zt
- zship
),
903 l
.get_palette_color(front_palette
[t
.lower_floor()], ifront
));
907 draw_sprite((s
.p
.hpos
- 32768.0) / 5888.0, (s
.p
.vpos
- 10240.0) / 2560.0,
911 const char* period
= "%&(ccK";
912 const char* dash
= "%'(cScS";
913 const char* vline
= "%$(b";
914 const char* tback
= "%F*ccccccccccccccccccccccccccccccccccccccccc";
916 void draw_timeattack_time(const char* msg
)
919 uint16_t nst
= strlen(_numbers_g
) / 10;
920 draw_block2(tback
, 0, 0xFFFFFF, 0xFFFFFF, false);
922 if(*msg
>= '0' && *msg
<= '9') {
923 draw_block2(_numbers_g
+ (*msg
- '0') * nst
, w
, 0xFFFFFF, 0xFFFFFF, true);
924 draw_block2(vline
, w
+ 4, 0xFFFFFF, 0xFFFFFF, true);
926 } else if(*msg
== ':') {
927 draw_block2(period
, w
, 0xFFFFFF, 0xFFFFFF, true);
929 } else if(*msg
== '-') {
930 draw_block2(dash
, w
, 0xFFFFFF, 0xFFFFFF, true);
931 draw_block2(vline
, w
+ 4, 0xFFFFFF, 0xFFFFFF, true);
936 for(unsigned i
= 0; i
< 7; i
++)
937 for(unsigned j
= 0; j
< 35; j
++)
938 origbuffer
[320 * i
+ j
] ^= 0xFFFFFF;
939 render_framebuffer_update(0, 0, 35, 7);
942 void draw_timeattack_time(uint16_t frames
)
946 strcpy(msg
, "----:--");
948 unsigned seconds
= 18227 * frames
/ 656250;
949 unsigned subseconds
= 36454U * frames
/ 13125 - 100 * seconds
;
950 sprintf(msg
, "%u:%02u", seconds
, subseconds
);
952 draw_timeattack_time(msg
);