1 /* render routines that replaces the game rendering by OpenGL rendering
3 * (C) 2018 by Andreas Eversberg <jolly@eversberg.eu>
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.
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.
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/>.
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"
33 //#define DEBUG_VERTEX
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];
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];
45 static int extend_roads; /* extend roads in its original width, rather than just using a single point */
47 static double transparency;
48 static double frustum_slope_64, frustum_slope_fov;
50 /* rendering starts, initialize variables */
51 void render_start(double _fov, int _extend_roads, int debug)
53 #if defined(DEBUG_COLOR) || defined(DEBUG_VERTEX)
54 printf("start rendering a new frame...\n");
58 extend_roads = _extend_roads;
60 transparency = (debug) ? 0.5 : 0.0;
61 opengl_transparency_set(transparency);
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);
68 void render_finish(void)
70 opengl_transparency_set(0.0);
73 static void gamecolor2gl_index(uint16_t index)
78 /* use given index from current palette, so we take the palette from M3:DS_0+0x1FA8 */
80 palette = mercenary_palette_render();
81 color = m68k_read_memory_16(palette + index);
83 printf("using color index (%d) from current palette, color is now 0x%04x\n", index, color);
85 if (color >= 0x8000) {
87 print_error("Use of color index from current palette, but index is not defined as being set!\n");
91 (double)((color >> 8) & 0xf) / 15.0,
92 (double)((color >> 4) & 0xf) / 15.0,
93 (double)(color & 0xf) / 15.0
97 static void gamecolor2gl(uint16_t color)
104 printf("color is given as 0x%04x\n", color);
107 /* color conversion: see for example M3: 0x4f830 */
108 if (color < 0x8000) {
109 /* use given color but shift it left by 1 */
112 printf("using given color, color is now 0x%04x\n", color);
114 } else if ((color & 0xff) < 0x80) {
115 gamecolor2gl_index(color & 0xf);
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);
123 printf("offset (%d) from pre-defined palette (at 0x%x) given, color is now 0x%04x\n", index, palette, color);
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");
133 (double)((color >> 8) & 0xf) / 15.0,
134 (double)((color >> 4) & 0xf) / 15.0,
135 (double)(color & 0xf) / 15.0
139 static void store_coord(const char __attribute__((unused)) *what, uint32_t vertex, double x, double y, double z)
142 print_error("Vertex %d is not a multiple of four!\n", vertex);
145 if (vertex >= MAX_VERTEX) {
146 print_error("Vertex %d exceeds maximum vertex number %d!\n", vertex, MAX_VERTEX);
151 printf("storing %s coordinates: vertex=%d, x=%.0f, y=%.0f, z=%.0f\n", what, vertex, x, y, z);
158 static int use_coord(const char __attribute__((unused)) *what, uint32_t vertex, double *x, double *y, double *z)
161 print_error("Vertex %d is not a multiple of four!\n", vertex);
164 if (vertex >= MAX_VERTEX) {
165 print_error("Vertex %d exceeds maximum vertex number %d!\n", vertex, MAX_VERTEX);
169 *x = coord_x[vertex];
170 *y = coord_y[vertex];
171 *z = coord_z[vertex];
173 printf("using %s coordinates: vertex=%d, x=%.0f, y=%.0f, z=%.0f\n", what, vertex, *x, *y, *z);
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)
182 print_error("Vertex is not a multiple of four!\n");
185 if (vertex >= MAX_INTERIOR_VERTEX) {
186 print_error("Vertex %d exceeds maximum vertex number %d!\n", vertex, MAX_INTERIOR_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);
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;
201 static int use_interior_coord(const char __attribute__((unused)) *what, uint32_t vertex, int level, double *x, double *y, double *z)
204 print_error("Vertex is not a multiple of four!\n");
207 if (vertex >= MAX_VERTEX) {
208 print_error("Vertex %d exceeds maximum vertex number %d!\n", vertex, MAX_VERTEX);
211 if (level < 1 || level > 4) {
212 print_error("Level %d is out of range (1..4)!\n", level);
216 *x = interior_coord_x[vertex];
217 *y = interior_coord_y[vertex][level - 1];
218 *z = interior_coord_z[vertex];
220 printf("using %s coordinates: vertex=%d, x=%.0f, y=%.0f, z=%.0f\n", what, vertex, *x, *y, *z);
226 static int planet_rotation = 0;
228 static void rotate_coordinate(double roll, double pitch, double yaw, double *x, double *y, double *z)
230 double out_x, out_y, out_z;
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);
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);
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);
251 /* coordinates ready for an object */
252 static void coord_object(void)
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);
265 static int ground_index;
267 /* clear screen (sky / universe) */
268 static void clear_screen(int index)
270 double x[4], y[4], z[4];
273 printf("clear screen:\n");
276 opengl_transparency_set(0.0);
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! */
285 opengl_transparency_set(transparency);
287 /* store for later use after planets have been rendered */
288 ground_index = index;
292 static void draw_ground(void)
294 double roll, pitch, yaw, x[4], y[4], z[4];
296 /* no ground in space :) */
297 if (ground_index < 0)
301 printf("draw ground plane:\n");
304 opengl_transparency_set(0.0);
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' */
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;
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]);
321 gamecolor2gl_index(ground_index);
322 opengl_render_polygon(x, y, z, 4, 0); /* no culling, because ground is always visible! */
324 opengl_transparency_set(transparency);
327 /* render polygon of object */
328 static void poly_object(int mercenary)
330 uint32_t vertex_address = REG_A[0];
333 double roll, pitch, yaw, x[16], y[16], z[16];
337 printf("draw object's polygon:\n");
340 gamecolor2gl(REG_D[0]);
343 color = m68k_read_memory_8(vertex_address++) << 8;
344 color |= m68k_read_memory_8(vertex_address++);
348 /* get orientation */
349 mercenary_get_orientation(&roll, &pitch, &yaw);
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)
357 rc = use_coord("object", vertex, &x[i], &y[i], &z[i]);
361 rotate_coordinate(roll, pitch, yaw, &x[i], &y[i], &z[i]);
363 /* render polygon to OpenGL */
364 opengl_render_polygon(x, y, z, i, 1); /* back face culling */
367 /* render line of object */
368 static void line_object(void)
370 uint32_t vertex_address = REG_A[0];
372 double roll, pitch, yaw, x1, y1, z1, x2, y2, z2;
376 printf("draw object's line:\n");
378 gamecolor2gl(REG_D[0]);
380 /* get orientation */
381 mercenary_get_orientation(&roll, &pitch, &yaw);
383 vertex = m68k_read_memory_8(vertex_address++);
385 rc = use_coord("object", vertex, &x1, &y1, &z1);
388 vertex = m68k_read_memory_8(vertex_address++);
390 rc = use_coord("object", vertex, &x2, &y2, &z2);
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);
400 /* coordinates ready for a beacon */
401 static void coord_beacon(void)
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);
412 /* render point of beacon */
413 static void point_beacon(void)
415 double roll, pitch, yaw, x, y, z;
419 printf("draw beacon's point:\n");
421 gamecolor2gl(REG_D[2]);
423 /* get orientation */
424 mercenary_get_orientation(&roll, &pitch, &yaw);
427 rc = use_coord("beacon", 0, &x, &y, &z);
431 rotate_coordinate(roll, pitch, yaw, &x, &y, &z);
432 /* transfer point to OpenGL */
433 opengl_render_point(x, y, z, 0.0);
436 /* coordinates ready for a building (exterior) */
437 static void coord_building_exterior(void)
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);
447 /* render polygon of building (exterior) */
448 static void poly_building_exterior(void)
450 uint32_t vertex_address = REG_A[0];
453 double roll, pitch, yaw, x[16], y[16], z[16];
457 printf("draw building's polygon:\n");
460 color = m68k_read_memory_8(vertex_address++) << 8;
461 color |= m68k_read_memory_8(vertex_address++);
464 /* get orientation */
465 mercenary_get_orientation(&roll, &pitch, &yaw);
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)
474 rc = use_coord("building exterior", vertex, &x[i], &y[i], &z[i]);
478 rotate_coordinate(roll, pitch, yaw, &x[i], &y[i], &z[i]);
480 /* render polygon to OpenGL */
481 opengl_render_polygon(x, y, z, i, 1); /* back face culling */
484 /* render line of building (exterior) */
485 static void line_building_exterior(void)
487 uint32_t vertex_address = REG_A[0];
489 double roll, pitch, yaw, x1, y1, z1, x2, y2, z2;
493 printf("draw building's line:\n");
495 gamecolor2gl(REG_D[0]);
497 /* get orientation */
498 mercenary_get_orientation(&roll, &pitch, &yaw);
500 vertex = m68k_read_memory_8(vertex_address++);
503 rc = use_coord("building line", vertex, &x1, &y1, &z1);
506 vertex = m68k_read_memory_8(vertex_address++);
509 rc = use_coord("building line", vertex, &x2, &y2, &z2);
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);
519 static int interior_level12 = 0;
520 static int interior_level34 = 0;
522 /* coordinates ready for a building (interior) */
523 static void coord_building_interior(void)
526 int32_t height1, height2, height3, height4;
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);
532 /* render polygon of building (interior) */
533 static void poly_building_interior1to4(int level)
538 double roll, pitch, yaw, x[16], y[16], z[16];
542 printf("draw roof/floor's polygon at level %d:\n", level);
544 color = m68k_read_memory_8(REG_A[0]) << 8;
545 color |= m68k_read_memory_8(REG_A[0] + 1);
548 /* get orientation */
549 mercenary_get_orientation(&roll, &pitch, &yaw);
551 /* the vertex list is zero-terminated */
552 for (i = 0; i < 4; i++) {
553 vertex = REG_A[(2 + i)];
555 rc = use_interior_coord("interior", vertex, level, &x[i], &y[i], &z[i]);
559 rotate_coordinate(roll, pitch, yaw, &x[i], &y[i], &z[i]);
561 /* render polygon to OpenGL */
562 opengl_render_polygon(x, y, z, i, 1); /* back face culling */
565 /* render polygon of building (interior) */
566 static void poly_building_interior5to6(int level12, int level34)
571 double roll, pitch, yaw, x[16], y[16], z[16];
575 printf("draw polygon above/below window/door at level %d/%d:\n", level12, level34);
577 color = m68k_read_memory_8(REG_A[0]) << 8;
578 color |= m68k_read_memory_8(REG_A[0] + 1);
581 /* get orientation */
582 mercenary_get_orientation(&roll, &pitch, &yaw);
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];
588 rc = use_interior_coord("interior", vertex, (i == 0 || i == 1) ? level12 : level34, &x[i], &y[i], &z[i]);
592 rotate_coordinate(roll, pitch, yaw, &x[i], &y[i], &z[i]);
594 /* render polygon to OpenGL */
595 opengl_render_polygon(x, y, z, i, 1); /* back face culling */
598 static void wall_building(void)
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;
608 printf("draw wall:\n");
610 gamecolor2gl(REG_D[3]);
612 /* get orientation */
613 mercenary_get_orientation(&roll, &pitch, &yaw);
615 /* get bottom coordinate */
616 rc = use_interior_coord("interior", REG_A[1], 1, &bottom1_x, &bottom1_y, &bottom1_z);
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);
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);
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);
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! */
649 /* transfer vertex to OpenGL */
650 opengl_render_line(bottom1_x, bottom1_y, bottom1_z, top1_x, top1_y, top1_z, 0.0);
654 /* coordinates ready for comet tail */
655 static void coord_comet(void)
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);
665 /* render polygon of comet tail */
666 static void poly_comet(void)
669 uint32_t vertex_address = REG_A[0];
672 double roll, pitch, yaw;
673 double inclination, rotation;
674 double x[16], y[16], z[16];
678 printf("draw comet's polygon:\n");
680 color = m68k_read_memory_8(vertex_address++) << 8;
681 color |= m68k_read_memory_8(vertex_address++);
684 /* get orientation */
685 mercenary_get_orientation(&roll, &pitch, &yaw);
687 mercenary_get_orientation_planet(&inclination, &rotation);
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)
695 rc = use_coord("comet tail", vertex, &x[i], &y[i], &z[i]);
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]);
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! */
707 /* coordinates ready for lines of a road / ground surface */
708 static void coord_line_road(void)
713 mercenary_get_height(&y);
716 store_coord("road", REG_A[0], (double)x, (double)y, (double)z);
719 /* render line of road */
720 static void line_road(void)
723 double roll, pitch, yaw, x1, y1, z1, x2, y2, z2;
727 printf("draw road's line:\n");
730 gamecolor2gl_index(mercenary_street_color_index());
732 /* get orientation */
733 mercenary_get_orientation(&roll, &pitch, &yaw);
737 rc = use_coord("road", vertex, &x1, &y1, &z1);
742 rc = use_coord("road", vertex, &x2, &y2, &z2);
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);
752 /* coordinates ready for polygons of a road / ground surface */
753 static void coord_poly_road(void)
758 x = m68k_read_memory_32(320 + REG_A[0]);
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);
763 z = m68k_read_memory_32(576 + REG_A[0]);
765 store_coord("road/place", REG_A[0], (double)x, (double)y, (double)z);
768 /* render polygon of road */
769 static void poly_road()
772 uint32_t vertex_address = REG_A[0];
773 uint32_t vertex, vertex_prev, vertex_next;
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];
784 printf("draw road/place's polygon:\n");
786 color = m68k_read_memory_8(vertex_address++) << 8;
787 color |= m68k_read_memory_8(vertex_address++);
790 /* get orientation */
791 mercenary_get_orientation(&roll, &pitch, &yaw);
794 /* count the vertex list, it is zero-terminated */
796 for (i = 0; i < 16; i++) {
797 vertex = m68k_read_memory_8(vertex_address++);
798 if (vertex == 0 && i)
800 vertices[i] = vertex;
804 /* the vertex list is zero-terminated */
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);
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);
819 vertex_next = vertices[(v + 1) % vertices_num];
820 rc = use_coord("road/place", vertex_next, &x_next, &y_next, &z_next);
823 /* extend vertices to end point position
824 * change x or z coordinate, whatever is greater
826 if (fabs(x_prev - x_current) > fabs(z_prev - z_current))
827 x_prev = x_next = x_current;
829 z_prev = z_next = z_current;
835 rotate_coordinate(roll, pitch, yaw, &x[i], &y[i], &z[i]);
842 rotate_coordinate(roll, pitch, yaw, &x[i], &y[i], &z[i]);
847 /* no extension, just keep the current point as is */
852 rotate_coordinate(roll, pitch, yaw, &x[i], &y[i], &z[i]);
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! */
861 /* coordinates ready for tags */
862 static void coord_tags(void)
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);
875 /* coordinates ready for large tags */
876 static void coord_tags2(void)
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);
889 /* render line of tag */
890 static void line_tags(int last_color)
892 uint32_t vertex_address = REG_A[0];
894 double roll, pitch, yaw, x1, y1, z1, x2, y2, z2;
898 printf("draw tag's line:\n");
902 gamecolor2gl(REG_D[0]);
904 gamecolor2gl_index(mercenary_line_tags_index());
907 /* get orientation */
908 mercenary_get_orientation(&roll, &pitch, &yaw);
910 vertex = m68k_read_memory_8(vertex_address++);
913 rc = use_coord("tag", vertex, &x1, &y1, &z1);
916 vertex = m68k_read_memory_8(vertex_address++);
919 rc = use_coord("tag", vertex, &x2, &y2, &z2);
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);
929 /* render polygon of tags */
930 static void poly_tags(int last_color)
932 uint32_t vertex_address = REG_A[0];
935 double roll, pitch, yaw, x[16], y[16], z[16];
939 printf("draw tag's polygon:\n");
942 gamecolor2gl(REG_D[0]);
944 gamecolor2gl(mercenary_poly_tags_color());
947 /* get orientation */
948 mercenary_get_orientation(&roll, &pitch, &yaw);
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)
957 rc = use_coord("tag", vertex, &x[i], &y[i], &z[i]);
961 rotate_coordinate(roll, pitch, yaw, &x[i], &y[i], &z[i]);
963 /* render polygon to OpenGL */
964 opengl_render_polygon(x, y, z, i, 1); /* back face culling */
967 /* coordinates ready for planet */
968 static void coord_planet(void)
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);
978 #define PLANET_VERTICES 128
979 #define PLANET_ELIPSE 1.17
982 static void draw_planet(int comet)
984 uint32_t color = REG_D[0];
985 uint32_t vertex = REG_A[0];
986 uint32_t scale_index;
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];
996 /* fixing (not noticable) bug in game: don't render comet twice */
997 if (!comet && vertex == 116)
1000 /* use background color for dark side */
1001 color |= (mercenary_background_index() << 16) | 0x80000000; /* shifted by 2 */
1003 /* make comet black on front side and bright on back */
1008 printf("draw planet/comet/sun: vertex=%d color=%08x\n", vertex, color);
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.
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;
1020 /* get orientation */
1021 mercenary_get_orientation(&roll, &pitch, &yaw);
1022 if (planet_rotation)
1023 mercenary_get_orientation_planet(&inclination, &rotation);
1026 rc = use_coord("planet(sun)", 0, &sun_x, &sun_y, &sun_z);
1029 rc = use_coord("planet", vertex, &loc_x, &loc_y, &loc_z);
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);
1037 rotate_coordinate(roll, pitch, yaw, &sun_x, &sun_y, &sun_z);
1038 rotate_coordinate(roll, pitch, yaw, &loc_x, &loc_y, &loc_z);
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;
1045 angle -= 2.0 * M_PI;
1047 angle += 2.0 * M_PI;
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);
1055 gamecolor2gl(color >> 16);
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);
1064 opengl_render_polygon_and_line(x, y, z, PLANET_VERTICES); /* no culling, its a planet! */
1067 /* sun has no crescent */
1071 /* on which side are we */
1072 if (angle < M_PI / 2.0 && angle > -M_PI / 2.0) {
1074 gamecolor2gl(color >> 16);
1076 /* set bright side */
1077 gamecolor2gl(color);
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;
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);
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);
1097 angle = fabs(angle);
1098 if (angle > M_PI / 2.0)
1099 angle = M_PI - angle;
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);
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);
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 */
1117 static void draw_stars(int16_t v_offset, int tilt, int above_zenith)
1120 int16_t tilt_offset = 0;
1123 uint16_t view_width, yaw;
1125 uint32_t table, table_start;
1131 printf("draw stars\n");
1133 /* use default fov of 64 to calculate z distance */
1134 z = 160.0 / frustum_slope_64;
1136 view_width = (int)(320.0 / frustum_slope_64 * frustum_slope_fov);
1139 for (i = 0; i < 16; i++)
1140 color[i] = m68k_read_memory_16(mercenary_palette_stars() + (i << 2));
1142 /* if we fly over the planet, we have tilt, so we get the calculated tilt value from game */
1144 tilt_value = (int32_t)REG_A[4];
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
1150 mercenary_get_orientation_raw(&pitch, &yaw);
1154 yaw = (yaw + 91 - (int)(91.0 * (frustum_slope_fov / frustum_slope_64))) & 0x3ff;
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;
1162 pitch = 0x200 - pitch;
1163 pitch = pitch & 0x3ff;
1166 pitch = (pitch * 0x6ccc) >> 16;
1169 x = m68k_read_memory_16(table);
1172 table = table_start;
1174 x = (view_width - 1) - m68k_read_memory_16(table) - x_offset + yaw;
1178 /* special case where we tilt the view when flying on the planet */
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
1183 tilt_offset = (int32_t)((x - (int16_t)(160.0 / frustum_slope_64 * frustum_slope_fov)) * tilt_value) >> 16;
1185 y = ((m68k_read_memory_16(table)) & 0x1ff) - pitch + tilt_offset;
1188 x = (view_width - 1) - x;
1192 gamecolor2gl(color[(m68k_read_memory_8(table - 2) & 0x3c) >> 2]);
1194 opengl_render_point(160.0 / frustum_slope_64 * frustum_slope_fov - (double)x, 68.0 - (double)y, z, 0.0);
1198 /* draw stars of interstellar flight */
1199 static void draw_stars_interstellar(void)
1208 printf("draw interstellar stars\n");
1210 /* use default fov of 64 to calculate z distance */
1211 z = 160.0 / frustum_slope_64;
1214 for (i = 0; i < 16; i++)
1215 color[i] = m68k_read_memory_16(mercenary_palette_stars() + (i << 2));
1218 count = REG_D[5] + 1;
1219 for (i = 0; i < count; i++) {
1222 gamecolor2gl(color[(m68k_read_memory_16(table) >> 5) & 0xf]);
1224 x = m68k_read_memory_16(table);
1226 y = m68k_read_memory_16(table);
1229 opengl_render_point(160.0 - (double)x, 68.0 - (double)y, z, 0.0);
1234 /* draw sun of interstellar flight (center dot) */
1235 static void draw_sun_interstellar(void)
1238 printf("draw interstellar sun\n");
1242 gamecolor2gl(0x777);
1244 opengl_render_point(0.0, 0.0, 100.0, 0.0);
1247 /* coordinates ready for polygons of islands */
1248 static void coord_islands(void)
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);
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);
1262 /* render polygon of island */
1263 static void poly_island()
1266 uint32_t vertex_address = REG_A[0];
1269 double roll, pitch, yaw, x[16], y[16], z[16];
1273 printf("draw island:\n");
1275 color = m68k_read_memory_8(vertex_address++) << 8;
1276 color |= m68k_read_memory_8(vertex_address++);
1277 gamecolor2gl(color);
1279 /* get orientation */
1280 mercenary_get_orientation(&roll, &pitch, &yaw);
1283 /* the vertex list is zero-terminated */
1286 vertex = m68k_read_memory_8(vertex_address++);
1287 if (vertex == 0 && i)
1289 /* skip mysterious points when rendering island */
1292 rc = use_coord("island", vertex, &x[i], &y[i], &z[i]);
1296 rotate_coordinate(roll, pitch, yaw, &x[i], &y[i], &z[i]);
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! */
1305 static void draw_sights(void)
1307 double x[4], y[4], z[4];
1309 /* use default fov of 64 to calculate z distance */
1310 z[0] = z[1] = z[2] = z[3] = 160.0 / frustum_slope_64;
1313 gamecolor2gl(0x777);
1317 x[0] = x[1] = -16.0;
1319 opengl_render_polygon(x, y, z, 4, 0); /* no culling, because sights are always visible! */
1322 opengl_render_polygon(x, y, z, 4, 0); /* no culling, because sights are always visible! */
1325 /* stop event from CPU received */
1326 void render_improved_event(int event)
1329 case STOP_AT_CLEAR_SCREEN1:
1330 clear_screen(16); /* color 16 is raster split */
1332 case STOP_AT_CLEAR_SCREEN2:
1335 case STOP_AT_CLEAR_SCREEN3:
1336 clear_screen(-1); /* no ground (in universe) */
1338 case STOP_AT_DRAW_GROUND:
1341 case STOP_AT_COORD_OBJECT:
1344 case STOP_AT_POLY_OBJECT_M3:
1347 case STOP_AT_POLY_OBJECT_M2:
1350 case STOP_AT_LINE_OBJECT:
1353 case STOP_AT_COORD_BEACON:
1356 case STOP_AT_POINT_BEACON:
1359 case STOP_AT_COORD_BUILDING_EXTERIOR:
1360 coord_building_exterior();
1362 case STOP_AT_POLY_BUILDING_EXTERIOR:
1363 poly_building_exterior();
1365 case STOP_AT_LINE_BUILDING_EXTERIOR:
1366 line_building_exterior();
1368 case STOP_AT_COORD_BUILDING_INTERIOR:
1369 coord_building_interior();
1371 case STOP_AT_POLY_BUILDING_INTERIOR1:
1373 interior_level12 = 1;
1374 interior_level34 = 1;
1376 case STOP_AT_POLY_BUILDING_INTERIOR2:
1378 interior_level12 = 2;
1379 interior_level34 = 2;
1381 case STOP_AT_POLY_BUILDING_INTERIOR3:
1382 /* door/window top */
1383 interior_level12 = 3;
1384 interior_level34 = 3;
1386 case STOP_AT_POLY_BUILDING_INTERIOR4:
1388 interior_level12 = 4;
1389 interior_level34 = 4;
1391 case STOP_AT_POLY_BUILDING_INTERIOR5:
1392 /* door/window top */
1393 interior_level12 = 2;
1394 interior_level34 = 3;
1396 case STOP_AT_POLY_BUILDING_INTERIOR6:
1398 interior_level12 = 1;
1399 interior_level34 = 4;
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");
1407 poly_building_interior1to4(interior_level12);
1408 interior_level12 = 0;
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");
1416 poly_building_interior5to6(interior_level12, interior_level34);
1417 interior_level12 = 0;
1419 case STOP_AT_WALL_BUILDING:
1422 case STOP_AT_COORD_COMET:
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;
1433 planet_rotation = 0;
1435 case STOP_AT_POLY_COMET:
1438 case STOP_AT_COORD_LINE_ROADS:
1441 case STOP_AT_LINE_ROADS:
1444 case STOP_AT_COORD_POLY_ROADS:
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.
1452 case STOP_AT_POLY_ROADS:
1455 case STOP_AT_COORD_TAGS:
1458 case STOP_AT_COORD_TAGS2:
1461 case STOP_AT_LINE_TAGS1:
1464 case STOP_AT_LINE_TAGS2:
1467 case STOP_AT_POLY_TAGS1:
1470 case STOP_AT_POLY_TAGS2:
1473 case STOP_AT_COORD_PLANET:
1476 case STOP_AT_DRAW_PLANET:
1479 case STOP_AT_DRAW_COMET:
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);
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! */
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! */
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! */
1498 case STOP_AT_DRAW_STARS_INTERSTELLAR:
1499 draw_stars_interstellar();
1501 case STOP_AT_DRAW_SUN_INTERSTELLAR:
1502 draw_sun_interstellar();
1504 case STOP_AT_COORD_ISLANDS:
1507 case STOP_AT_POLY_ISLANDS:
1510 case STOP_AT_LINE_ISLANDS:
1511 /* this is not used, as i had noticed so far */
1512 puts("line island");
1514 case STOP_AT_DRAW_SIGHTS:
1517 case STOP_AT_POLY_UKN2:
1520 case STOP_AT_PLANET_UKN1:
1521 puts("planet ukn1");
1523 case STOP_AT_PLANET_UKN2:
1524 puts("planet ukn2");