Render game graphics using OpenGL
[mercenary-reloaded.git] / src / mercenary / render.c
1 /* render routines that replaces the game rendering by OpenGL rendering
2  *
3  * (C) 2018 by Andreas Eversberg <jolly@eversberg.eu>
4  * All Rights Reserved
5  *
6  * This program is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  */
19
20 #include <stdio.h>
21 #include <stdint.h>
22 #include <stdlib.h>
23 #include <math.h>
24 #include <GL/glew.h>
25 #include "../libsdl/print.h"
26 #include "../libcpu/m68k.h"
27 #include "../libcpu/m68kcpu.h"
28 #include "../libcpu/execute.h"
29 #include "../libsdl/opengl.h"
30 #include "mercenary.h"
31
32 //#define DEBUG_COLOR
33 //#define DEBUG_VERTEX
34
35 #define MAX_VERTEX 0x300
36 static int coord_x[MAX_VERTEX >> 2];
37 static int coord_y[MAX_VERTEX >> 2];
38 static int coord_z[MAX_VERTEX >> 2];
39
40 #define MAX_INTERIOR_VERTEX 0x400 /* do we need that much? */
41 static int interior_coord_x[MAX_INTERIOR_VERTEX >> 2];
42 static int interior_coord_y[MAX_INTERIOR_VERTEX >> 2][4]; /* 4 levels (floor; ceiling; window or door top; window bottom) */
43 static int interior_coord_z[MAX_INTERIOR_VERTEX >> 2];
44
45 static int extend_roads; /* extend roads in its original width, rather than just using a single point */
46 static double fov;
47 static double transparency;
48 static double frustum_slope_64, frustum_slope_fov;
49
50 /* rendering starts, initialize variables */
51 void render_start(double _fov, int _extend_roads, int debug)
52 {
53 #if defined(DEBUG_COLOR) || defined(DEBUG_VERTEX)
54         printf("start rendering a new frame...\n");
55 #endif
56
57         fov = _fov;
58         extend_roads = _extend_roads;
59
60         transparency = (debug) ? 0.5 : 0.0;
61         opengl_transparency_set(transparency);
62
63         /* calculate slope of 64 degree frustum and current FOV's frustum */    
64         frustum_slope_64 = tan(64.0 / 2.0 / 180.0 * M_PI);
65         frustum_slope_fov = tan(fov / 2.0 / 180.0 * M_PI);
66 }
67
68 void render_finish(void)
69 {
70         opengl_transparency_set(0.0);
71 }
72
73 static void gamecolor2gl_index(uint16_t index)
74 {
75         uint32_t palette;
76         uint16_t color;
77
78         /* use given index from current palette, so we take the palette from M3:DS_0+0x1FA8 */
79         index <<= 1;
80         palette = mercenary_palette_render();
81         color = m68k_read_memory_16(palette + index);
82 #ifdef DEBUG_COLOR
83         printf("using color index (%d) from current palette, color is now 0x%04x\n", index, color);
84 #endif
85         if (color >= 0x8000) {
86 #ifdef DEBUG_COLOR
87                 print_error("Use of color index from current palette, but index is not defined as being set!\n");
88 #endif
89         }
90         opengl_render_color(
91                 (double)((color >> 8) & 0xf) / 15.0,
92                 (double)((color >> 4) & 0xf) / 15.0,
93                 (double)(color & 0xf) / 15.0
94         );
95 }
96
97 static void gamecolor2gl(uint16_t color)
98 {
99         uint16_t index;
100         uint32_t palette;
101         int nesting = 0;
102
103 #ifdef DEBUG_COLOR
104         printf("color is given as 0x%04x\n", color);
105 #endif
106 again:
107         /* color conversion: see for example M3: 0x4f830 */
108         if (color < 0x8000) {
109                 /* use given color but shift it left by 1 */
110                 color = color << 1;
111 #ifdef DEBUG_COLOR
112                 printf("using given color, color is now 0x%04x\n", color);
113 #endif
114         } else if ((color & 0xff) < 0x80) {
115                 gamecolor2gl_index(color & 0xf);
116                 return;
117         } else {
118                 /* use given index from pre-defined palette */
119                 index = color & 0x7e;
120                 palette = mercenary_palette_predefined();
121                 color = m68k_read_memory_16(palette + index);
122 #ifdef DEBUG_COLOR
123                 printf("offset (%d) from pre-defined palette (at 0x%x) given, color is now 0x%04x\n", index, palette, color);
124 #endif
125                 /* now use that color info parse again (hopefully it does not contain a "pre-defined palette" again and again! */
126                 if (nesting++ == 8) {
127                         print_error("Color lookup from pre-defined palette is nesting too much, please fix!\n");
128                         return;
129                 }
130                 goto again;
131         }
132         opengl_render_color(
133                 (double)((color >> 8) & 0xf) / 15.0,
134                 (double)((color >> 4) & 0xf) / 15.0,
135                 (double)(color & 0xf) / 15.0
136         );
137 }
138
139 static void store_coord(const char __attribute__((unused)) *what, uint32_t vertex, double x, double y, double z)
140 {
141         if ((vertex & 3)) {
142                 print_error("Vertex %d is not a multiple of four!\n", vertex);
143                 return;
144         }
145         if (vertex >= MAX_VERTEX) {
146                 print_error("Vertex %d exceeds maximum vertex number %d!\n", vertex, MAX_VERTEX);
147                 return;
148         }
149         vertex >>= 2;
150 #ifdef DEBUG_VERTEX
151         printf("storing %s coordinates: vertex=%d, x=%.0f, y=%.0f, z=%.0f\n", what, vertex, x, y, z);
152 #endif
153         coord_x[vertex] = x;
154         coord_y[vertex] = y;
155         coord_z[vertex] = z;
156 }
157
158 static int use_coord(const char __attribute__((unused)) *what, uint32_t vertex, double *x, double *y, double *z)
159 {
160         if ((vertex & 3)) {
161                 print_error("Vertex %d is not a multiple of four!\n", vertex);
162                 return -1;
163         }
164         if (vertex >= MAX_VERTEX) {
165                 print_error("Vertex %d exceeds maximum vertex number %d!\n", vertex, MAX_VERTEX);
166                 return -1;
167         }
168         vertex >>= 2;
169         *x = coord_x[vertex];
170         *y = coord_y[vertex];
171         *z = coord_z[vertex];
172 #ifdef DEBUG_VERTEX
173         printf("using %s coordinates: vertex=%d, x=%.0f, y=%.0f, z=%.0f\n", what, vertex, *x, *y, *z);
174 #endif
175
176         return 0;
177 }
178
179 static void store_interior_coord(const char __attribute__((unused)) *what, uint32_t vertex, double x, double y1, double y2, double y3, double y4, double z)
180 {
181         if ((vertex & 3)) {
182                 print_error("Vertex is not a multiple of four!\n");
183                 return;
184         }
185         if (vertex >= MAX_INTERIOR_VERTEX) {
186                 print_error("Vertex %d exceeds maximum vertex number %d!\n", vertex, MAX_INTERIOR_VERTEX);
187                 return;
188         }
189         vertex >>= 2;
190 #ifdef DEBUG_VERTEX
191         printf("storing %s coordinates: vertex=%d, x=%.0f, y=%.0f;%.0f;%.0f;%.0F, Z=%.0F\n", what, vertex, x, y1, y2, y3, y4, z);
192 #endif
193         interior_coord_x[vertex] = x;
194         interior_coord_y[vertex][0] = y1;
195         interior_coord_y[vertex][1] = y2;
196         interior_coord_y[vertex][2] = y3;
197         interior_coord_y[vertex][3] = y4;
198         interior_coord_z[vertex] = z;
199 }
200
201 static int use_interior_coord(const char __attribute__((unused)) *what, uint32_t vertex, int level, double *x, double *y, double *z)
202 {
203         if ((vertex & 3)) {
204                 print_error("Vertex is not a multiple of four!\n");
205                 return -1;
206         }
207         if (vertex >= MAX_VERTEX) {
208                 print_error("Vertex %d exceeds maximum vertex number %d!\n", vertex, MAX_VERTEX);
209                 return -1;
210         }
211         if (level < 1 || level > 4) {
212                 print_error("Level %d is out of range (1..4)!\n", level);
213                 return -1;
214         }
215         vertex >>= 2;
216         *x = interior_coord_x[vertex];
217         *y = interior_coord_y[vertex][level - 1];
218         *z = interior_coord_z[vertex];
219 #ifdef DEBUG_VERTEX
220         printf("using %s coordinates: vertex=%d, x=%.0f, y=%.0f, z=%.0f\n", what, vertex, *x, *y, *z);
221 #endif
222
223         return 0;
224 }
225
226 static int planet_rotation = 0;
227
228 static void rotate_coordinate(double roll, double pitch, double yaw, double *x, double *y, double *z)
229 {
230         double out_x, out_y, out_z;
231
232         /* rotate yaw (German: Gier, turn view to the right) */
233         out_z = (*z) * cos(yaw) - (*x) * sin(yaw);
234         out_x = (*z) * sin(yaw) + (*x) * cos(yaw);
235         *z = out_z;
236         *x = out_x;
237         /* rotate pitch (German: Nick, turn head down) */
238         out_y = (*y) * cos(pitch) - (*z) * sin(pitch);
239         out_z = (*y) * sin(pitch) + (*z) * cos(pitch);
240         *y = out_y;
241         *z = out_z;
242         if (roll == 0.0)
243                 return;
244         /* rotate roll (tilt head to the right) */
245         out_x = (*x) * cos(roll) - (*y) * sin(roll);
246         out_y = (*x) * sin(roll) + (*y) * cos(roll);
247         *x = out_x;
248         *y = out_y;
249 }
250
251 /* coordinates ready for an object */
252 static void coord_object(void)
253 {
254         int32_t x, y, z;
255
256         x = (int16_t)REG_D[3];
257         x += (int32_t)REG_A[1];
258         y = (int16_t)REG_D[4];
259         y += (int32_t)REG_A[2];
260         z = (int16_t)REG_D[5];
261         z += (int32_t)REG_A[3];
262         store_coord("object", REG_A[0], (double)x, (double)y, (double)z);
263 }
264
265 static int ground_index;
266
267 /* clear screen (sky / universe) */
268 static void clear_screen(int index)
269 {
270         double x[4], y[4], z[4];
271
272 #ifdef DEBUG_VERTEX
273         printf("clear screen:\n");
274 #endif
275
276         opengl_transparency_set(0.0);
277
278         /* create plane to fill view */
279         x[0] = x[1] = y[1] = y[2] = -1000000;
280         x[2] = x[3] = y[0] = y[3] = 1000000;
281         z[0] = z[1] = z[2] = z[3] = 10;
282         gamecolor2gl_index(8);
283         opengl_render_polygon(x, y, z, 4, 0); /* no culling, because background is always visible! */
284
285         opengl_transparency_set(transparency);
286
287         /* store for later use after planets have been rendered */
288         ground_index = index;
289 }
290
291 /* draw ground */
292 static void draw_ground(void)
293 {
294         double roll, pitch, yaw, x[4], y[4], z[4];
295
296         /* no ground in space :) */
297         if (ground_index < 0)
298                 return;
299
300 #ifdef DEBUG_VERTEX
301         printf("draw ground plane:\n");
302 #endif
303
304         opengl_transparency_set(0.0);
305
306         /* get orientation */
307         mercenary_get_orientation(&roll, &pitch, &yaw);
308         yaw = 0.0; /* no need to rotate x-z plane, we don't want look at a corner of the 'ground-square' */
309
310         /* create huge square */
311         x[0] = x[1] = z[1] = z[2] = -1000000;
312         x[2] = x[3] = z[0] = z[3] = 1000000;
313         y[0] = y[1] = y[2] = y[3] = -10;
314
315         /* rotate vertex */
316         rotate_coordinate(roll, pitch, yaw, &x[0], &y[0], &z[0]);
317         rotate_coordinate(roll, pitch, yaw, &x[1], &y[1], &z[1]);
318         rotate_coordinate(roll, pitch, yaw, &x[2], &y[2], &z[2]);
319         rotate_coordinate(roll, pitch, yaw, &x[3], &y[3], &z[3]);
320
321         gamecolor2gl_index(ground_index);
322         opengl_render_polygon(x, y, z, 4, 0); /* no culling, because ground is always visible! */
323
324         opengl_transparency_set(transparency);
325 }
326
327 /* render polygon of object */
328 static void poly_object(int mercenary)
329 {
330         uint32_t vertex_address = REG_A[0];
331         uint32_t vertex;
332         int i;
333         double roll, pitch, yaw, x[16], y[16], z[16];
334         int rc;
335
336 #ifdef DEBUG_VERTEX
337         printf("draw object's polygon:\n");
338 #endif
339         if (mercenary == 3)
340                 gamecolor2gl(REG_D[0]);
341         else {
342                 uint16_t color;
343                 color = m68k_read_memory_8(vertex_address++) << 8;
344                 color |= m68k_read_memory_8(vertex_address++);
345                 gamecolor2gl(color);
346         }
347
348         /* get orientation */
349         mercenary_get_orientation(&roll, &pitch, &yaw);
350
351         /* the vertex list is zero-terminated */
352         for (i = 0; i < 16; i++) {
353                 vertex = m68k_read_memory_8(vertex_address++);
354                 if (vertex == 0 && i)
355                         break;
356                 /* get vertex */
357                 rc = use_coord("object", vertex, &x[i], &y[i], &z[i]);
358                 if (rc < 0)
359                         break;
360                 /* rotate vertex */
361                 rotate_coordinate(roll, pitch, yaw, &x[i], &y[i], &z[i]);
362         }
363         /* render polygon to OpenGL */
364         opengl_render_polygon(x, y, z, i, 1); /* back face culling */
365 }
366
367 /* render line of object */
368 static void line_object(void)
369 {
370         uint32_t vertex_address = REG_A[0];
371         uint32_t vertex;
372         double roll, pitch, yaw, x1, y1, z1, x2, y2, z2;
373         int rc;
374
375 #ifdef DEBUG_VERTEX
376         printf("draw object's line:\n");
377 #endif
378         gamecolor2gl(REG_D[0]);
379
380         /* get orientation */
381         mercenary_get_orientation(&roll, &pitch, &yaw);
382
383         vertex = m68k_read_memory_8(vertex_address++);
384         /* get vertex */
385         rc = use_coord("object", vertex, &x1, &y1, &z1);
386         if (rc < 0)
387                 return;
388         vertex = m68k_read_memory_8(vertex_address++);
389         /* get vertex */
390         rc = use_coord("object", vertex, &x2, &y2, &z2);
391         if (rc < 0)
392                 return;
393         /* rotate vertices */
394         rotate_coordinate(roll, pitch, yaw, &x1, &y1, &z1);
395         rotate_coordinate(roll, pitch, yaw, &x2, &y2, &z2);
396         /* transfer line to OpenGL */
397         opengl_render_line(x1, y1, z1, x2, y2, z2, 0.0);
398 }
399
400 /* coordinates ready for a beacon */
401 static void coord_beacon(void)
402 {
403         int32_t x, y, z;
404
405         /* only 28 bits seem to be a correct signed int value */
406         x = (double)((int32_t)REG_D[3] << 4) / 16.0;
407         y = (double)((int32_t)REG_D[4] << 4) / 16.0;
408         z = (double)((int32_t)REG_D[5] << 4) / 16.0;
409         store_coord("beacon", 0, (double)x, (double)y, (double)z);
410 }
411
412 /* render point of beacon */
413 static void point_beacon(void)
414 {
415         double roll, pitch, yaw, x, y, z;
416         int rc;
417
418 #ifdef DEBUG_VERTEX
419         printf("draw beacon's point:\n");
420 #endif
421         gamecolor2gl(REG_D[2]);
422
423         /* get orientation */
424         mercenary_get_orientation(&roll, &pitch, &yaw);
425
426         /* get vertex */
427         rc = use_coord("beacon", 0, &x, &y, &z);
428         if (rc < 0)
429                 return;
430         /* rotate vertex */
431         rotate_coordinate(roll, pitch, yaw, &x, &y, &z);
432         /* transfer point to OpenGL */
433         opengl_render_point(x, y, z, 0.0);
434 }
435
436 /* coordinates ready for a building (exterior) */
437 static void coord_building_exterior(void)
438 {
439         int x, y, z;
440
441         x = (int32_t)REG_D[3];
442         y = (int32_t)REG_D[4];
443         z = (int32_t)REG_D[5];
444         store_coord("building exterior", REG_A[0], (double)x, (double)y, (double)z);
445 }
446
447 /* render polygon of building (exterior) */
448 static void poly_building_exterior(void)
449 {
450         uint32_t vertex_address = REG_A[0];
451         uint32_t vertex;
452         int i;
453         double roll, pitch, yaw, x[16], y[16], z[16];
454         int rc;
455
456 #ifdef DEBUG_VERTEX
457         printf("draw building's polygon:\n");
458 #endif
459         uint16_t color;
460         color = m68k_read_memory_8(vertex_address++) << 8;
461         color |= m68k_read_memory_8(vertex_address++);
462         gamecolor2gl(color);
463
464         /* get orientation */
465         mercenary_get_orientation(&roll, &pitch, &yaw);
466
467         /* the vertex list is zero-terminated */
468         for (i = 0; i < 16; i++) {
469                 vertex = m68k_read_memory_8(vertex_address++);
470                 if (vertex == 0 && i)
471                         break;
472                 vertex |= 0x100;
473                 /* get vertex */
474                 rc = use_coord("building exterior", vertex, &x[i], &y[i], &z[i]);
475                 if (rc < 0)
476                         break;
477                 /* rotate vertex */
478                 rotate_coordinate(roll, pitch, yaw, &x[i], &y[i], &z[i]);
479         }
480         /* render polygon to OpenGL */
481         opengl_render_polygon(x, y, z, i, 1); /* back face culling */
482 }
483
484 /* render line of building (exterior) */
485 static void line_building_exterior(void)
486 {
487         uint32_t vertex_address = REG_A[0];
488         uint32_t vertex;
489         double roll, pitch, yaw, x1, y1, z1, x2, y2, z2;
490         int rc;
491
492 #ifdef DEBUG_VERTEX
493         printf("draw building's line:\n");
494 #endif
495         gamecolor2gl(REG_D[0]);
496
497         /* get orientation */
498         mercenary_get_orientation(&roll, &pitch, &yaw);
499
500         vertex = m68k_read_memory_8(vertex_address++);
501         vertex |= 0x100;
502         /* get vertex */
503         rc = use_coord("building line", vertex, &x1, &y1, &z1);
504         if (rc < 0)
505                 return;
506         vertex = m68k_read_memory_8(vertex_address++);
507         vertex |= 0x100;
508         /* get vertex */
509         rc = use_coord("building line", vertex, &x2, &y2, &z2);
510         if (rc < 0)
511                 return;
512         /* rotate vertices */
513         rotate_coordinate(roll, pitch, yaw, &x1, &y1, &z1);
514         rotate_coordinate(roll, pitch, yaw, &x2, &y2, &z2);
515         /* transfer line to OpenGL */
516         opengl_render_line(x1, y1, z1, x2, y2, z2, 0.0);
517 }
518
519 static int interior_level12 = 0;
520 static int interior_level34 = 0;
521
522 /* coordinates ready for a building (interior) */
523 static void coord_building_interior(void)
524 {
525         int16_t east, north;
526         int32_t height1, height2, height3, height4;
527
528         mercenary_coord_building_interior(&east, &height1, &height2, &height3, &height4, &north);
529         store_interior_coord("interior", REG_A[0], (double)east, (double)height1, (double)height2, (double)height3, (double)height4, (double)north);
530 }
531
532 /* render polygon of building (interior) */
533 static void poly_building_interior1to4(int level)
534 {
535         uint16_t color;
536         uint32_t vertex;
537         int i;
538         double roll, pitch, yaw, x[16], y[16], z[16];
539         int rc;
540
541 #ifdef DEBUG_VERTEX
542         printf("draw roof/floor's polygon at level %d:\n", level);
543 #endif
544         color = m68k_read_memory_8(REG_A[0]) << 8;
545         color |= m68k_read_memory_8(REG_A[0] + 1);
546         gamecolor2gl(color);
547
548         /* get orientation */
549         mercenary_get_orientation(&roll, &pitch, &yaw);
550
551         /* the vertex list is zero-terminated */
552         for (i = 0; i < 4; i++) {
553                 vertex = REG_A[(2 + i)];
554                 /* get vertex */
555                 rc = use_interior_coord("interior", vertex, level, &x[i], &y[i], &z[i]);
556                 if (rc < 0)
557                         break;
558                 /* rotate vertex */
559                 rotate_coordinate(roll, pitch, yaw, &x[i], &y[i], &z[i]);
560         }
561         /* render polygon to OpenGL */
562         opengl_render_polygon(x, y, z, i, 1); /* back face culling */
563 }
564
565 /* render polygon of building (interior) */
566 static void poly_building_interior5to6(int level12, int level34)
567 {
568         uint16_t color;
569         uint32_t vertex;
570         int i;
571         double roll, pitch, yaw, x[16], y[16], z[16];
572         int rc;
573
574 #ifdef DEBUG_VERTEX
575         printf("draw polygon above/below window/door at level %d/%d:\n", level12, level34);
576 #endif
577         color = m68k_read_memory_8(REG_A[0]) << 8;
578         color |= m68k_read_memory_8(REG_A[0] + 1);
579         gamecolor2gl(color);
580
581         /* get orientation */
582         mercenary_get_orientation(&roll, &pitch, &yaw);
583
584         /* the vertex list is zero-terminated */
585         for (i = 0; i < 4; i++) {
586                 vertex = (i == 0 || i == 3) ? REG_A[2] : REG_A[3];
587                 /* get vertex */
588                 rc = use_interior_coord("interior", vertex, (i == 0 || i == 1) ? level12 : level34, &x[i], &y[i], &z[i]);
589                 if (rc < 0)
590                         break;
591                 /* rotate vertex */
592                 rotate_coordinate(roll, pitch, yaw, &x[i], &y[i], &z[i]);
593         }
594         /* render polygon to OpenGL */
595         opengl_render_polygon(x, y, z, i, 1); /* back face culling */
596 }
597
598 static void wall_building(void)
599 {
600         double roll, pitch, yaw, x[4], y[4], z[4];
601         double bottom1_x, bottom1_y, bottom1_z;
602         double bottom2_x, bottom2_y, bottom2_z;
603         double top1_x, top1_y, top1_z;
604         double top2_x, top2_y, top2_z;
605         int rc;
606
607 #ifdef DEBUG_VERTEX
608         printf("draw wall:\n");
609 #endif
610         gamecolor2gl(REG_D[3]);
611
612         /* get orientation */
613         mercenary_get_orientation(&roll, &pitch, &yaw);
614
615         /* get bottom coordinate */
616         rc = use_interior_coord("interior", REG_A[1], 1, &bottom1_x, &bottom1_y, &bottom1_z);
617         if (rc < 0)
618                 return;
619         /* rotate vertex */
620         rotate_coordinate(roll, pitch, yaw, &bottom1_x, &bottom1_y, &bottom1_z);
621         /* get top coordinate according to bit 12 in D3 */
622         rc = use_interior_coord("interior", REG_A[1], (REG_D[3] & (1 << 12)) ? 3 : 2, &top1_x, &top1_y, &top1_z);
623         if (rc < 0)
624                 return;
625         /* rotate vertex */
626         rotate_coordinate(roll, pitch, yaw, &top1_x, &top1_y, &top1_z);
627         /* if wall is not just a strait line */
628         if (REG_A[1] != REG_A[2]) {
629                 /* get bottom coordinate */
630                 rc = use_interior_coord("interior", REG_A[2], 1, &bottom2_x, &bottom2_y, &bottom2_z);
631                 if (rc < 0)
632                         return;
633                 /* rotate vertex */
634                 rotate_coordinate(roll, pitch, yaw, &bottom2_x, &bottom2_y, &bottom2_z);
635                 /* get top coordinate according to bit 12 in D3 */
636                 rc = use_interior_coord("interior", REG_A[2], (REG_D[3] & (1 << 12)) ? 3 : 2, &top2_x, &top2_y, &top2_z);
637                 if (rc < 0)
638                         return;
639                 /* rotate vertex */
640                 rotate_coordinate(roll, pitch, yaw, &top2_x, &top2_y, &top2_z);
641                 /* transfer vertex to OpenGL */
642                 x[0] = bottom1_x;       y[0] = bottom1_y;       z[0] = bottom1_z;
643                 x[1] = top1_x;          y[1] = top1_y;          z[1] = top1_z;
644                 x[2] = top2_x;          y[2] = top2_y;          z[2] = top2_z;
645                 x[3] = bottom2_x;       y[3] = bottom2_y;       z[3] = bottom2_z;
646                 /* render polygon to OpenGL */
647                 opengl_render_polygon_and_line(x, y, z, 4); /* no culling, because walls are always visible! */
648         } else {
649                 /* transfer vertex to OpenGL */
650                 opengl_render_line(bottom1_x, bottom1_y, bottom1_z, top1_x, top1_y, top1_z, 0.0);
651         }
652 }
653
654 /* coordinates ready for comet tail */
655 static void coord_comet(void)
656 {
657         int32_t x, y, z;
658
659         x = (int32_t)REG_D[3];
660         y = (int32_t)REG_D[4];
661         z = (int32_t)REG_D[5];
662         store_coord("comet tail", REG_A[0], (double)x, (double)y, (double)z);
663 }
664
665 /* render polygon of comet tail */
666 static void poly_comet(void)
667 {
668         uint16_t color;
669         uint32_t vertex_address = REG_A[0];
670         uint32_t vertex;
671         int i;
672         double roll, pitch, yaw;
673         double inclination, rotation;
674         double x[16], y[16], z[16];
675         int rc;
676
677 #ifdef DEBUG_VERTEX
678         printf("draw comet's polygon:\n");
679 #endif
680         color = m68k_read_memory_8(vertex_address++) << 8;
681         color |= m68k_read_memory_8(vertex_address++);
682         gamecolor2gl(color);
683
684         /* get orientation */
685         mercenary_get_orientation(&roll, &pitch, &yaw);
686         if (planet_rotation)
687                 mercenary_get_orientation_planet(&inclination, &rotation);
688
689         /* the vertex list is zero-terminated */
690         for (i = 0; i < 16; i++) {
691                 vertex = m68k_read_memory_8(vertex_address++);
692                 if (vertex == 0 && i)
693                         break;
694                 /* get vertex */
695                 rc = use_coord("comet tail", vertex, &x[i], &y[i], &z[i]);
696                 if (rc < 0)
697                         break;
698                 /* rotate vertex */
699                 if (planet_rotation)
700                         rotate_coordinate(0.0, inclination, rotation, &x[i], &y[i], &z[i]);
701                 rotate_coordinate(roll, pitch, yaw, &x[i], &y[i], &z[i]);
702         }
703         /* render polygon to OpenGL */
704         opengl_render_polygon_and_line(x, y, z, i); /* no culling, because we render only two (out of four) planes! */
705 }
706
707 /* coordinates ready for lines of a road / ground surface */
708 static void coord_line_road(void)
709 {
710         int32_t x, y, z;
711
712         x = REG_D[3];
713         mercenary_get_height(&y);
714         y = -y;
715         z = REG_D[5];
716         store_coord("road", REG_A[0], (double)x, (double)y, (double)z);
717 }
718
719 /* render line of road */
720 static void line_road(void)
721 {
722         uint32_t vertex;
723         double roll, pitch, yaw, x1, y1, z1, x2, y2, z2;
724         int rc;
725
726 #ifdef DEBUG_VERTEX
727         printf("draw road's line:\n");
728 #endif
729
730         gamecolor2gl_index(mercenary_street_color_index());
731
732         /* get orientation */
733         mercenary_get_orientation(&roll, &pitch, &yaw);
734
735         vertex = REG_A[1];
736         /* get vertex */
737         rc = use_coord("road", vertex, &x1, &y1, &z1);
738         if (rc < 0)
739                 return;
740         vertex = REG_A[2];
741         /* get vertex */
742         rc = use_coord("road", vertex, &x2, &y2, &z2);
743         if (rc < 0)
744                 return;
745         /* rotate vertices */
746         rotate_coordinate(roll, pitch, yaw, &x1, &y1, &z1);
747         rotate_coordinate(roll, pitch, yaw, &x2, &y2, &z2);
748         /* transfer line to OpenGL */
749         opengl_render_line(x1, y1, z1, x2, y2, z2, 0.0);
750 }
751
752 /* coordinates ready for polygons of a road / ground surface */
753 static void coord_poly_road(void)
754 {
755         int32_t x, y, z;
756
757 //      puts("road");
758         x = m68k_read_memory_32(320 + REG_A[0]);
759         x -= REG_A[1];
760         /* the A2 is already converted to game's coordinate, so we use the memory location DS_0+0x1DBA (m3) instead */
761         mercenary_get_height(&y);
762         y = -y;
763         z = m68k_read_memory_32(576 + REG_A[0]);
764         z -= REG_A[3];
765         store_coord("road/place", REG_A[0], (double)x, (double)y, (double)z);
766 }
767
768 /* render polygon of road */
769 static void poly_road()
770 {
771         uint16_t color;
772         uint32_t vertex_address = REG_A[0];
773         uint32_t vertex, vertex_prev, vertex_next;
774         int i, v;
775         double roll, pitch, yaw, x[16], y[16], z[16];
776         double x_current, y_current, z_current;
777         double x_prev, y_prev, z_prev;
778         double x_next, y_next, z_next;
779         uint32_t vertices[16];
780         int vertices_num;
781         int rc;
782
783 #ifdef DEBUG_VERTEX
784         printf("draw road/place's polygon:\n");
785 #endif
786         color = m68k_read_memory_8(vertex_address++) << 8;
787         color |= m68k_read_memory_8(vertex_address++);
788         gamecolor2gl(color);
789
790         /* get orientation */
791         mercenary_get_orientation(&roll, &pitch, &yaw);
792
793
794         /* count the vertex list, it is zero-terminated */
795         vertices_num = 0;
796         for (i = 0; i < 16; i++) {
797                 vertex = m68k_read_memory_8(vertex_address++);
798                 if (vertex == 0 && i)
799                         break;
800                 vertices[i] = vertex;
801                 vertices_num++;
802         }
803
804         /* the vertex list is zero-terminated */
805         i = 0;
806         for (v = 0; v < vertices_num; v++) {
807                 vertex = vertices[v];
808                 rc = use_coord("road/place", vertex, &x_current, &y_current, &z_current);
809                 if (rc < 0)
810                         break;
811                 /* check for road extension, so we extend the road to the given end point */
812                 if (extend_roads && vertex >= 0xf0) {
813                         /* previous vertex */
814                         vertex_prev = vertices[(v + vertices_num - 1) % vertices_num];
815                         rc = use_coord("road/place", vertex_prev, &x_prev, &y_prev, &z_prev);
816                         if (rc < 0)
817                                 break;
818                         /* next vertex */
819                         vertex_next = vertices[(v + 1) % vertices_num];
820                         rc = use_coord("road/place", vertex_next, &x_next, &y_next, &z_next);
821                         if (rc < 0)
822                                 break;
823                         /* extend vertices to end point position
824                          * change x or z coordinate, whatever is greater
825                         */
826                         if (fabs(x_prev - x_current) > fabs(z_prev - z_current))
827                                 x_prev = x_next = x_current;
828                         else
829                                 z_prev = z_next = z_current;
830                         /* store vertices */
831                         x[i] = x_prev;
832                         y[i] = y_prev;
833                         z[i] = z_prev;
834                         /* rotate vertex */
835                         rotate_coordinate(roll, pitch, yaw, &x[i], &y[i], &z[i]);
836                         if (i++ == 16)
837                                 break;
838                         x[i] = x_next;
839                         y[i] = y_next;
840                         z[i] = z_next;
841                         /* rotate vertex */
842                         rotate_coordinate(roll, pitch, yaw, &x[i], &y[i], &z[i]);
843                         if (i++ == 16)
844                                 break;
845                         continue;
846                 } else {
847                         /* no extension, just keep the current point as is */
848                         x[i] = x_current;
849                         y[i] = y_current;
850                         z[i] = z_current;
851                         /* rotate vertex */
852                         rotate_coordinate(roll, pitch, yaw, &x[i], &y[i], &z[i]);
853                         if (i++ == 16)
854                                 break;
855                 }
856         }
857         /* render polygon to OpenGL */
858         opengl_render_polygon(x, y, z, i, 0); /* no culling, because polygons lay on the gound and are always visible! */
859 }
860
861 /* coordinates ready for tags */
862 static void coord_tags(void)
863 {
864         int32_t x, y, z;
865
866         x = (int16_t)REG_D[3];
867         x += (int32_t)REG_A[1];
868         y = (int16_t)REG_D[4];
869         y += (int32_t)REG_A[2];
870         z = (int16_t)REG_D[5];
871         z += (int32_t)REG_A[3];
872         store_coord("tags", REG_A[0], (double)x, (double)y, (double)z);
873 }
874
875 /* coordinates ready for large tags */
876 static void coord_tags2(void)
877 {
878         int32_t x, y, z;
879
880         x = (int16_t)REG_D[3];
881         x += 2 * (int32_t)REG_A[1];
882         y = (int16_t)REG_D[4];
883         y += 2 * (int32_t)REG_A[2];
884         z = (int16_t)REG_D[5];
885         z += 2 * (int32_t)REG_A[3];
886         store_coord("large tags", REG_A[0], (double)x, (double)y, (double)z);
887 }
888
889 /* render line of tag */
890 static void line_tags(int last_color)
891 {
892         uint32_t vertex_address = REG_A[0];
893         uint32_t vertex;
894         double roll, pitch, yaw, x1, y1, z1, x2, y2, z2;
895         int rc;
896
897 #ifdef DEBUG_VERTEX
898         printf("draw tag's line:\n");
899 #endif
900
901         if (!last_color) {
902                 gamecolor2gl(REG_D[0]);
903         } else {
904                 gamecolor2gl_index(mercenary_line_tags_index());
905         }
906
907         /* get orientation */
908         mercenary_get_orientation(&roll, &pitch, &yaw);
909
910         vertex = m68k_read_memory_8(vertex_address++);
911         vertex |= 0x200;
912         /* get vertex */
913         rc = use_coord("tag", vertex, &x1, &y1, &z1);
914         if (rc < 0)
915                 return;
916         vertex = m68k_read_memory_8(vertex_address++);
917         vertex |= 0x200;
918         /* get vertex */
919         rc = use_coord("tag", vertex, &x2, &y2, &z2);
920         if (rc < 0)
921                 return;
922         /* rotate vertices */
923         rotate_coordinate(roll, pitch, yaw, &x1, &y1, &z1);
924         rotate_coordinate(roll, pitch, yaw, &x2, &y2, &z2);
925         /* transfer line to OpenGL */
926         opengl_render_line(x1, y1, z1, x2, y2, z2, 0.0);
927 }
928
929 /* render polygon of tags */
930 static void poly_tags(int last_color)
931 {
932         uint32_t vertex_address = REG_A[0];
933         uint32_t vertex;
934         int i;
935         double roll, pitch, yaw, x[16], y[16], z[16];
936         int rc;
937
938 #ifdef DEBUG_VERTEX
939         printf("draw tag's polygon:\n");
940 #endif
941         if (!last_color) {
942                 gamecolor2gl(REG_D[0]);
943         } else {
944                 gamecolor2gl(mercenary_poly_tags_color());
945         }
946
947         /* get orientation */
948         mercenary_get_orientation(&roll, &pitch, &yaw);
949
950         /* the vertex list is zero-terminated */
951         for (i = 0; i < 16; i++) {
952                 vertex = m68k_read_memory_8(vertex_address++);
953                 if (vertex == 0 && i)
954                         break;
955                 vertex |= 0x200;
956                 /* get vertex */
957                 rc = use_coord("tag", vertex, &x[i], &y[i], &z[i]);
958                 if (rc < 0)
959                         break;
960                 /* rotate vertex */
961                 rotate_coordinate(roll, pitch, yaw, &x[i], &y[i], &z[i]);
962         }
963         /* render polygon to OpenGL */
964         opengl_render_polygon(x, y, z, i, 1); /* back face culling */
965 }
966
967 /* coordinates ready for planet */
968 static void coord_planet(void)
969 {
970         int32_t x, y, z;
971
972         x = (int32_t)REG_D[3];
973         y = (int32_t)REG_D[4];
974         z = (int32_t)REG_D[5];
975         store_coord("planet", REG_A[0], (double)x, (double)y, (double)z);
976 }
977
978 #define PLANET_VERTICES 128
979 #define PLANET_ELIPSE   1.17
980
981 /* render planet */
982 static void draw_planet(int comet)
983 {
984         uint32_t color = REG_D[0];
985         uint32_t vertex = REG_A[0];
986         uint32_t scale_index;
987         int i;
988         double scale1, scale2, size, angle;
989         double roll, pitch, yaw;
990         double inclination, rotation;
991         double sun_x, sun_y, sun_z, angle_sun;
992         double loc_x, loc_y, loc_z, angle_loc;
993         double x[PLANET_VERTICES], y[PLANET_VERTICES], z[PLANET_VERTICES];
994         int rc;
995
996         /* fixing (not noticable) bug in game: don't render comet twice */
997         if (!comet && vertex == 116)
998                 return;
999
1000         /* use background color for dark side */
1001         color |= (mercenary_background_index() << 16) | 0x80000000; /* shifted by 2 */
1002
1003         /* make comet black on front side and bright on back */
1004         if (comet)
1005                 color = 0x07770000;
1006
1007 #ifdef DEBUG_VERTEX
1008         printf("draw planet/comet/sun: vertex=%d color=%08x\n", vertex, color);
1009 #endif
1010         /* I REVERSED THE F*CKING PLANET SIZE, took me half a day!
1011          * the long word 21584(A0) contains two scales
1012          * the lower word contains the scale between 4096 .. 8191, this is 1.0 .. 2.0
1013          * the upper word defines how much this scale is shifted to the left.
1014          */
1015         scale_index = mercenary_planet_scale_index();
1016         scale1 = (double)(m68k_read_memory_16(scale_index + 2 + vertex) & 0x1fff) / 8192.0;
1017         scale2 = (double)(1 << (uint16_t)m68k_read_memory_16(scale_index + vertex));
1018         size = scale1 * scale2 / 128.0;
1019
1020         /* get orientation */
1021         mercenary_get_orientation(&roll, &pitch, &yaw);
1022         if (planet_rotation)
1023                 mercenary_get_orientation_planet(&inclination, &rotation);
1024
1025         /* get location */
1026         rc = use_coord("planet(sun)", 0, &sun_x, &sun_y, &sun_z);
1027         if (rc < 0)
1028                 return;
1029         rc = use_coord("planet", vertex, &loc_x, &loc_y, &loc_z);
1030         if (rc < 0)
1031                 return;
1032         /* rotate vertex */
1033         if (planet_rotation) {
1034                 rotate_coordinate(0.0, inclination, rotation, &sun_x, &sun_y, &sun_z);
1035                 rotate_coordinate(0.0, inclination, rotation, &loc_x, &loc_y, &loc_z);
1036         }
1037         rotate_coordinate(roll, pitch, yaw, &sun_x, &sun_y, &sun_z);
1038         rotate_coordinate(roll, pitch, yaw, &loc_x, &loc_y, &loc_z);
1039
1040         /* calculate direction of the sun */
1041         angle_sun = atan2(sun_x, sun_z);
1042         angle_loc = atan2(loc_x, loc_z);
1043         angle = angle_sun - angle_loc;
1044         if (angle > M_PI)
1045                 angle -= 2.0 * M_PI;
1046         if (angle < -M_PI)
1047                 angle += 2.0 * M_PI;
1048
1049         /* on which side are we (sun is always bright, vertex == 0) */
1050         if ((angle < M_PI / 2.0 && angle > -M_PI / 2.0) || vertex == 0) {
1051                 /* set bright side */
1052                 gamecolor2gl(color);
1053         } else {
1054                 /* set dark side */
1055                 gamecolor2gl(color >> 16);
1056         }
1057
1058         /* create and render cicle */
1059         for (i = 0; i < PLANET_VERTICES; i++) {
1060                 x[i] = loc_x + size * sin(2 * M_PI * (double)i / PLANET_VERTICES) * PLANET_ELIPSE;
1061                 y[i] = loc_y + size * cos(2 * M_PI * (double)i / PLANET_VERTICES);
1062                 z[i] = loc_z;
1063         }
1064         opengl_render_polygon_and_line(x, y, z, PLANET_VERTICES); /* no culling, its a planet! */
1065
1066         if (vertex == 0) {
1067                 /* sun has no crescent */
1068                 return;
1069         }
1070
1071         /* on which side are we */
1072         if (angle < M_PI / 2.0 && angle > -M_PI / 2.0) {
1073                 /* set dark side */
1074                 gamecolor2gl(color >> 16);
1075         } else {
1076                 /* set bright side */
1077                 gamecolor2gl(color);
1078         }
1079
1080         /* create and render crescent */
1081         if (angle > M_PI / 2.0 || (angle < 0.0 && angle > -M_PI / 2.0)) {
1082                 angle = fabs(angle);
1083                 if (angle > M_PI / 2.0)
1084                         angle = M_PI - angle;
1085                 /* to the right */
1086                 for (i = 0; i < PLANET_VERTICES / 2 + 1; i++) {
1087                         x[i] = loc_x + size * sin(2 * M_PI * (double)i / PLANET_VERTICES) * PLANET_ELIPSE;
1088                         y[i] = loc_y + size * cos(2 * M_PI * (double)i / PLANET_VERTICES);
1089                         z[i] = loc_z;
1090                 }
1091                 for (; i < PLANET_VERTICES; i++) {
1092                         x[i] = loc_x + size * sin(2 * M_PI * (double)i / PLANET_VERTICES) * PLANET_ELIPSE * sin((1.0 - angle / (M_PI / 2)) * (M_PI / 2.0));
1093                         y[i] = loc_y + size * cos(2 * M_PI * (double)i / PLANET_VERTICES);
1094                         z[i] = loc_z;
1095                 }
1096         } else {
1097                 angle = fabs(angle);
1098                 if (angle > M_PI / 2.0)
1099                         angle = M_PI - angle;
1100                 /* to the left */
1101                 for (i = 0; i < PLANET_VERTICES / 2 + 1; i++) {
1102                         x[i] = loc_x + size * sin(2 * M_PI * (double)i / PLANET_VERTICES) * PLANET_ELIPSE * sin((1.0 - angle / (M_PI / 2)) * (M_PI / 2.0));
1103                         y[i] = loc_y + size * cos(2 * M_PI * (double)i / PLANET_VERTICES);
1104                         z[i] = loc_z;
1105                 }
1106                 for (; i < PLANET_VERTICES; i++) {
1107                         x[i] = loc_x + size * sin(2 * M_PI * (double)i / PLANET_VERTICES) * PLANET_ELIPSE;
1108                         y[i] = loc_y + size * cos(2 * M_PI * (double)i / PLANET_VERTICES);
1109                         z[i] = loc_z;
1110                 }
1111         }
1112         opengl_render_polygon_and_line(x, y, z, PLANET_VERTICES); /* no culling, its a planet! */
1113         opengl_render_point(loc_x, loc_y, loc_z, 0.0); /* planet is visible at any distance - at least as a point  */
1114 }
1115
1116 /* draw stars */
1117 static void draw_stars(int16_t v_offset, int tilt, int above_zenith)
1118 {
1119         int32_t tilt_value;
1120         int16_t tilt_offset = 0;
1121         int x_offset = 0;
1122         uint16_t color[16];
1123         uint16_t view_width, yaw;
1124         int16_t pitch;
1125         uint32_t table, table_start;
1126         int16_t x, y;
1127         double z;
1128         int i;
1129
1130 #ifdef DEBUG_VERTEX
1131         printf("draw stars\n");
1132 #endif
1133         /* use default fov of 64 to calculate z distance */
1134         z = 160.0 / frustum_slope_64;
1135
1136         view_width = (int)(320.0 / frustum_slope_64 * frustum_slope_fov);
1137
1138         /* get palette */
1139         for (i = 0; i < 16; i++)
1140                 color[i] = m68k_read_memory_16(mercenary_palette_stars() + (i << 2));
1141
1142         /* if we fly over the planet, we have tilt, so we get the calculated tilt value from game */
1143         if (tilt)
1144                 tilt_value = (int32_t)REG_A[4];
1145
1146         /* table offset is 91, so we substract it and add it back with different FOV, so table begins at later
1147          * full turn is 1024, we have default of 64 degrees: 1024 / 360 * 64 = 182
1148          * then we half it, so we get to the center via 91
1149          */
1150         mercenary_get_orientation_raw(&pitch, &yaw);
1151
1152         if (above_zenith)
1153                 yaw = 0x200 + yaw;
1154         yaw = (yaw + 91 - (int)(91.0 * (frustum_slope_fov / frustum_slope_64))) & 0x3ff;
1155         yaw <<= 1;
1156         table = mercenary_star_table();
1157         table_start = table + m68k_read_memory_16(table);
1158         table += m68k_read_memory_16(table + yaw);
1159         yaw = ((uint32_t)yaw * 0xe10e) >> 16;
1160
1161         if (above_zenith)
1162                 pitch = 0x200 - pitch;
1163         pitch = pitch & 0x3ff;
1164         pitch -= v_offset;
1165         pitch <<= 2;
1166         pitch = (pitch * 0x6ccc) >> 16;
1167
1168         while (1) {
1169                 x = m68k_read_memory_16(table);
1170                 if (x >= 1800) {
1171                         x_offset += 1800;
1172                         table = table_start;
1173                 }
1174                 x = (view_width - 1) - m68k_read_memory_16(table) - x_offset + yaw;
1175                 table += 2;
1176                 if (x < 0)
1177                         break;
1178                 /* special case where we tilt the view when flying on the planet */
1179                 if (tilt) {
1180                         /* use offset as given by game: 160 is half of the screen width
1181                          * we extend the width to the actual FOV, so it fits
1182                          */
1183                         tilt_offset = (int32_t)((x - (int16_t)(160.0 / frustum_slope_64 * frustum_slope_fov)) * tilt_value) >> 16;
1184                 }
1185                 y = ((m68k_read_memory_16(table)) & 0x1ff) - pitch + tilt_offset;
1186                 table += 2;
1187                 if (above_zenith) {
1188                         x = (view_width - 1) - x;
1189                         y = ~y + 136;
1190                 }
1191                 /* get color */
1192                 gamecolor2gl(color[(m68k_read_memory_8(table - 2) & 0x3c) >> 2]);
1193                 /* render point */
1194                 opengl_render_point(160.0 / frustum_slope_64 * frustum_slope_fov - (double)x, 68.0 - (double)y, z, 0.0);
1195         }
1196 }
1197
1198 /* draw stars of interstellar flight */
1199 static void draw_stars_interstellar(void)
1200 {
1201         int i, count;
1202         uint32_t table;
1203         uint16_t color[16];
1204         int16_t x, y;
1205         double z;
1206
1207 #ifdef DEBUG_VERTEX
1208         printf("draw interstellar stars\n");
1209 #endif
1210         /* use default fov of 64 to calculate z distance */
1211         z = 160.0 / frustum_slope_64;
1212
1213         /* get palette */
1214         for (i = 0; i < 16; i++)
1215                 color[i] = m68k_read_memory_16(mercenary_palette_stars() + (i << 2));
1216
1217         table = REG_A[0];
1218         count = REG_D[5] + 1;
1219         for (i = 0; i < count; i++) {
1220                 table += 12;
1221                 /* get color */
1222                 gamecolor2gl(color[(m68k_read_memory_16(table) >> 5) & 0xf]);
1223                 table += 2;
1224                 x = m68k_read_memory_16(table);
1225                 table += 2;
1226                 y = m68k_read_memory_16(table);
1227                 table += 2;
1228                 /* render point */
1229                 opengl_render_point(160.0 - (double)x, 68.0 - (double)y, z, 0.0);
1230         }
1231
1232 }
1233
1234 /* draw sun of interstellar flight (center dot) */
1235 static void draw_sun_interstellar(void)
1236 {
1237 #ifdef DEBUG_VERTEX
1238         printf("draw interstellar sun\n");
1239 #endif
1240
1241         /* white */
1242         gamecolor2gl(0x777);
1243         /* render point */
1244         opengl_render_point(0.0, 0.0, 100.0, 0.0);
1245 }
1246
1247 /* coordinates ready for polygons of islands */
1248 static void coord_islands(void)
1249 {
1250         int32_t x, y, z;
1251
1252         x = ((int16_t)m68k_read_memory_16(REG_A[4] - 2) * 65536);
1253         x += (int32_t)REG_A[1];
1254         /* the A2 is already converted to game's coordinate, so we use the memory location DS_0+0x1DBA instead */
1255         mercenary_get_height(&y);
1256         y = -y;
1257         z = ((int16_t)m68k_read_memory_16(REG_A[4]) * 65536);
1258         z += (int32_t)REG_A[3];
1259         store_coord("island", REG_A[0], (double)x, (double)y, (double)z);
1260 }
1261
1262 /* render polygon of island */
1263 static void poly_island()
1264 {
1265         uint16_t color;
1266         uint32_t vertex_address = REG_A[0];
1267         uint32_t vertex;
1268         int i;
1269         double roll, pitch, yaw, x[16], y[16], z[16];
1270         int rc;
1271
1272 #ifdef DEBUG_VERTEX
1273         printf("draw island:\n");
1274 #endif
1275         color = m68k_read_memory_8(vertex_address++) << 8;
1276         color |= m68k_read_memory_8(vertex_address++);
1277         gamecolor2gl(color);
1278
1279         /* get orientation */
1280         mercenary_get_orientation(&roll, &pitch, &yaw);
1281
1282
1283         /* the vertex list is zero-terminated */
1284         i = 0;
1285         while (1) {
1286                 vertex = m68k_read_memory_8(vertex_address++);
1287                 if (vertex == 0 && i)
1288                         break;
1289                 /* skip mysterious points when rendering island */
1290                 if (vertex >= 0xf0)
1291                         continue;
1292                 rc = use_coord("island", vertex, &x[i], &y[i], &z[i]);
1293                 if (rc < 0)
1294                         break;
1295                 /* rotate vertex */
1296                 rotate_coordinate(roll, pitch, yaw, &x[i], &y[i], &z[i]);
1297                 if (i++ == 16)
1298                         break;
1299         }
1300         /* render polygon to OpenGL */
1301         opengl_render_polygon_and_line(x, y, z, i); /* no culling, because polygons lay on the gound and are always visible! */
1302 }
1303
1304 /* draw sights */
1305 static void draw_sights(void)
1306 {
1307         double x[4], y[4], z[4];
1308
1309         /* use default fov of 64 to calculate z distance */
1310         z[0] = z[1] = z[2] = z[3] = 160.0 / frustum_slope_64;
1311
1312         /* white */
1313         gamecolor2gl(0x777);
1314
1315         y[0] = y[3] = -1.0;
1316         y[1] = y[2] = 1.0;
1317         x[0] = x[1] = -16.0;
1318         x[2] = x[3] = -8.0;
1319         opengl_render_polygon(x, y, z, 4, 0); /* no culling, because sights are always visible! */
1320         x[0] = x[1] = 8.0;
1321         x[2] = x[3] = 16.0;
1322         opengl_render_polygon(x, y, z, 4, 0); /* no culling, because sights are always visible! */
1323 }
1324
1325 /* stop event from CPU received */
1326 void render_improved_event(int event)
1327 {
1328         switch (event) {
1329         case STOP_AT_CLEAR_SCREEN1:
1330                 clear_screen(16); /* color 16 is raster split */
1331                 break;
1332         case STOP_AT_CLEAR_SCREEN2:
1333                 clear_screen(15);
1334                 break;
1335         case STOP_AT_CLEAR_SCREEN3:
1336                 clear_screen(-1); /* no ground (in universe) */
1337                 break;
1338         case STOP_AT_DRAW_GROUND:
1339                 draw_ground();
1340                 break;
1341         case STOP_AT_COORD_OBJECT:
1342                 coord_object();
1343                 break;
1344         case STOP_AT_POLY_OBJECT_M3:
1345                 poly_object(3);
1346                 break;
1347         case STOP_AT_POLY_OBJECT_M2:
1348                 poly_object(2);
1349                 break;
1350         case STOP_AT_LINE_OBJECT:
1351                 line_object();
1352                 break;
1353         case STOP_AT_COORD_BEACON:
1354                 coord_beacon();
1355                 break;
1356         case STOP_AT_POINT_BEACON:
1357                 point_beacon();
1358                 break;
1359         case STOP_AT_COORD_BUILDING_EXTERIOR:
1360                 coord_building_exterior();
1361                 break;
1362         case STOP_AT_POLY_BUILDING_EXTERIOR:
1363                 poly_building_exterior();
1364                 break;
1365         case STOP_AT_LINE_BUILDING_EXTERIOR:
1366                 line_building_exterior();
1367                 break;
1368         case STOP_AT_COORD_BUILDING_INTERIOR:
1369                 coord_building_interior();
1370                 break;
1371         case STOP_AT_POLY_BUILDING_INTERIOR1:
1372                 /* floor */
1373                 interior_level12 = 1;
1374                 interior_level34 = 1;
1375                 break;
1376         case STOP_AT_POLY_BUILDING_INTERIOR2:
1377                 /* ceiling */
1378                 interior_level12 = 2;
1379                 interior_level34 = 2;
1380                 break;
1381         case STOP_AT_POLY_BUILDING_INTERIOR3:
1382                 /* door/window top */
1383                 interior_level12 = 3;
1384                 interior_level34 = 3;
1385                 break;
1386         case STOP_AT_POLY_BUILDING_INTERIOR4:
1387                 /* window bottom */
1388                 interior_level12 = 4;
1389                 interior_level34 = 4;
1390                 break;
1391         case STOP_AT_POLY_BUILDING_INTERIOR5:
1392                 /* door/window top */
1393                 interior_level12 = 2;
1394                 interior_level34 = 3;
1395                 break;
1396         case STOP_AT_POLY_BUILDING_INTERIOR6:
1397                 /* window bottom */
1398                 interior_level12 = 1;
1399                 interior_level34 = 4;
1400                 break;
1401         case STOP_AT_POLY_BUILDING_INTERIOR1to4:
1402                 /* before we come here, we must already passed the break points above, so we know the level to be rendered */
1403                 if (interior_level12 == 0) {
1404                         print_error("Interior level is not set, please fix!\n");
1405                         break;
1406                 }
1407                 poly_building_interior1to4(interior_level12);
1408                 interior_level12 = 0;
1409                 break;
1410         case STOP_AT_POLY_BUILDING_INTERIOR5to6:
1411                 /* before we come here, we must already passed the break points above, so we know the level to be rendered */
1412                 if (interior_level12 == 0) {
1413                         print_error("Interior level is not set, please fix!\n");
1414                         break;
1415                 }
1416                 poly_building_interior5to6(interior_level12, interior_level34);
1417                 interior_level12 = 0;
1418                 break;
1419         case STOP_AT_WALL_BUILDING:
1420                 wall_building();
1421                 break;
1422         case STOP_AT_COORD_COMET:
1423                 coord_comet();
1424                 break;
1425         case STOP_AT_MATRIX_COMET:
1426         case STOP_AT_MATRIX_PLANET:
1427                 /* track the rotation matrix
1428                  * if we have 0x42c44 matrix, we must add extra rotation to planet.
1429                  * the rotation will change the view from the planet's surface */
1430                 if (REG_A[4] == 0x42c44) /* same with M2 and M3 */
1431                         planet_rotation = 1;
1432                 else
1433                         planet_rotation = 0;
1434                 break;
1435         case STOP_AT_POLY_COMET:
1436                 poly_comet();
1437                 break;
1438         case STOP_AT_COORD_LINE_ROADS:
1439                 coord_line_road();
1440                 break;
1441         case STOP_AT_LINE_ROADS:
1442                 line_road();
1443                 break;
1444         case STOP_AT_COORD_POLY_ROADS:
1445                 coord_poly_road();
1446                 break;
1447         case STOP_AT_LINE_ROADS_CENTER:
1448                 /* we don't need to render center lines of roads, because there are polygons already
1449                  * it does not make sense, since OpenGL has much higher resolution.
1450                  */
1451                 break;
1452         case STOP_AT_POLY_ROADS:
1453                 poly_road();
1454                 break;
1455         case STOP_AT_COORD_TAGS:
1456                 coord_tags();
1457                 break;
1458         case STOP_AT_COORD_TAGS2:
1459                 coord_tags2();
1460                 break;
1461         case STOP_AT_LINE_TAGS1:
1462                 line_tags(0);
1463                 break;
1464         case STOP_AT_LINE_TAGS2:
1465                 line_tags(1);
1466                 break;
1467         case STOP_AT_POLY_TAGS1:
1468                 poly_tags(0);
1469                 break;
1470         case STOP_AT_POLY_TAGS2:
1471                 poly_tags(1);
1472                 break;
1473         case STOP_AT_COORD_PLANET:
1474                 coord_planet();
1475                 break;
1476         case STOP_AT_DRAW_PLANET:
1477                 draw_planet(0);
1478                 break;
1479         case STOP_AT_DRAW_COMET:
1480                 draw_planet(1);
1481                 break;
1482         case STOP_AT_DRAW_STARS_SPACE:
1483                 /* render stars with vertical offset 0x1c0, no tilt, not above zenith */
1484                 draw_stars(0x1c0, 0, 0);
1485                 break;
1486         case STOP_AT_DRAW_STARS_GROUND:
1487                 /* render stars with vertical offset 0x128, no tilt, not above zenith */
1488                 draw_stars(0x128, 0, 0); /* yet it's hex 128! */
1489                 break;
1490         case STOP_AT_DRAW_STARS_FLYING:
1491                 /* render stars with vertical offset 0x128, with tilt, not above zenith */
1492                 draw_stars(0x128, 1, 0); /* yet it's hex 128! */
1493                 break;
1494         case STOP_AT_DRAW_STARS_FLYING2:
1495                 /* render stars with vertical offset 0x128, with tilt, and above zenith */
1496                 draw_stars(0x128, 1, 1); /* yet it's hex 128! */
1497                 break;
1498         case STOP_AT_DRAW_STARS_INTERSTELLAR:
1499                 draw_stars_interstellar();
1500                 break;
1501         case STOP_AT_DRAW_SUN_INTERSTELLAR:
1502                 draw_sun_interstellar();
1503                 break;
1504         case STOP_AT_COORD_ISLANDS:
1505                 coord_islands();
1506                 break;
1507         case STOP_AT_POLY_ISLANDS:
1508                 poly_island();
1509                 break;
1510         case STOP_AT_LINE_ISLANDS:
1511                 /* this is not used, as i had noticed so far */
1512                 puts("line island");
1513                 break;
1514         case STOP_AT_DRAW_SIGHTS:
1515                 draw_sights();
1516                 break;
1517         case STOP_AT_POLY_UKN2:
1518                 puts("poly ukn2");
1519                 break;
1520         case STOP_AT_PLANET_UKN1:
1521                 puts("planet ukn1");
1522                 break;
1523         case STOP_AT_PLANET_UKN2:
1524                 puts("planet ukn2");
1525                 break;
1526         }
1527 }
1528