+void render_start(double _fov, int _extend_roads, int debug)
+{
+#if defined(DEBUG_COLOR) || defined(DEBUG_VERTEX)
+ printf("start rendering a new frame...\n");
+#endif
+
+ fov = _fov;
+ extend_roads = _extend_roads;
+
+ transparency = (debug) ? 0.5 : 0.0;
+ opengl_transparency_set(transparency);
+
+ /* calculate slope of 64 degree frustum and current FOV's frustum */
+ frustum_slope_64 = tan(64.0 / 2.0 / 180.0 * M_PI);
+ frustum_slope_fov = tan(fov / 2.0 / 180.0 * M_PI);
+}
+
+void render_finish(void)
+{
+ opengl_transparency_set(0.0);
+}
+
+static void gamecolor2gl_index(uint16_t index)
+{
+ uint32_t palette;
+ uint16_t color;
+
+ /* use given index from current palette, so we take the palette from M3:DS_0+0x1FA8 */
+ index <<= 1;
+ palette = mercenary_palette_render();
+ color = m68k_read_memory_16(palette + index);
+#ifdef DEBUG_COLOR
+ printf("using color index (%d) from current palette, color is now 0x%04x\n", index, color);
+#endif
+ if (color >= 0x8000) {
+#ifdef DEBUG_COLOR
+ print_error("Use of color index from current palette, but index is not defined as being set!\n");
+#endif
+ }
+ opengl_render_color(
+ (double)((color >> 8) & 0xf) / 15.0,
+ (double)((color >> 4) & 0xf) / 15.0,
+ (double)(color & 0xf) / 15.0
+ );
+}
+
+static void gamecolor2gl(uint16_t color)
+{
+ uint16_t index;
+ uint32_t palette;
+ int nesting = 0;
+
+#ifdef DEBUG_COLOR
+ printf("color is given as 0x%04x\n", color);
+#endif
+again:
+ /* color conversion: see for example M3: 0x4f830 */
+ if (color < 0x8000) {
+ /* use given color but shift it left by 1 */
+ color = color << 1;
+#ifdef DEBUG_COLOR
+ printf("using given color, color is now 0x%04x\n", color);
+#endif
+ } else if ((color & 0xff) < 0x80) {
+ gamecolor2gl_index(color & 0xf);
+ return;
+ } else {
+ /* use given index from pre-defined palette */
+ index = color & 0x7e;
+ palette = mercenary_palette_predefined();
+ color = m68k_read_memory_16(palette + index);
+#ifdef DEBUG_COLOR
+ printf("offset (%d) from pre-defined palette (at 0x%x) given, color is now 0x%04x\n", index, palette, color);
+#endif
+ /* now use that color info parse again (hopefully it does not contain a "pre-defined palette" again and again! */
+ if (nesting++ == 8) {
+ print_error("Color lookup from pre-defined palette is nesting too much, please fix!\n");
+ return;
+ }
+ goto again;
+ }
+ opengl_render_color(
+ (double)((color >> 8) & 0xf) / 15.0,
+ (double)((color >> 4) & 0xf) / 15.0,
+ (double)(color & 0xf) / 15.0
+ );
+}
+
+static void store_coord(const char __attribute__((unused)) *what, uint32_t vertex, double x, double y, double z)
+{
+ if ((vertex & 3)) {
+ print_error("Vertex %d is not a multiple of four!\n", vertex);
+ return;
+ }
+ if (vertex >= MAX_VERTEX) {
+ print_error("Vertex %d exceeds maximum vertex number %d!\n", vertex, MAX_VERTEX);
+ return;
+ }
+ vertex >>= 2;
+#ifdef DEBUG_VERTEX
+ printf("storing %s coordinates: vertex=%d, x=%.0f, y=%.0f, z=%.0f\n", what, vertex, x, y, z);
+#endif
+ coord_x[vertex] = x;
+ coord_y[vertex] = y;
+ coord_z[vertex] = z;
+}
+
+static int use_coord(const char __attribute__((unused)) *what, uint32_t vertex, double *x, double *y, double *z)
+{
+ if ((vertex & 3)) {
+ print_error("Vertex %d is not a multiple of four!\n", vertex);
+ return -1;
+ }
+ if (vertex >= MAX_VERTEX) {
+ print_error("Vertex %d exceeds maximum vertex number %d!\n", vertex, MAX_VERTEX);
+ return -1;
+ }
+ vertex >>= 2;
+ *x = coord_x[vertex];
+ *y = coord_y[vertex];
+ *z = coord_z[vertex];
+#ifdef DEBUG_VERTEX
+ printf("using %s coordinates: vertex=%d, x=%.0f, y=%.0f, z=%.0f\n", what, vertex, *x, *y, *z);
+#endif
+
+ return 0;
+}
+
+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)
+{
+ if ((vertex & 3)) {
+ print_error("Vertex is not a multiple of four!\n");
+ return;
+ }
+ if (vertex >= MAX_INTERIOR_VERTEX) {
+ print_error("Vertex %d exceeds maximum vertex number %d!\n", vertex, MAX_INTERIOR_VERTEX);
+ return;
+ }
+ vertex >>= 2;
+#ifdef DEBUG_VERTEX
+ printf("storing %s coordinates: vertex=%d, x=%.0f, y=%.0f;%.0f;%.0f;%.0F, Z=%.0F\n", what, vertex, x, y1, y2, y3, y4, z);
+#endif
+ interior_coord_x[vertex] = x;
+ interior_coord_y[vertex][0] = y1;
+ interior_coord_y[vertex][1] = y2;
+ interior_coord_y[vertex][2] = y3;
+ interior_coord_y[vertex][3] = y4;
+ interior_coord_z[vertex] = z;
+}
+
+static int use_interior_coord(const char __attribute__((unused)) *what, uint32_t vertex, int level, double *x, double *y, double *z)
+{
+ if ((vertex & 3)) {
+ print_error("Vertex is not a multiple of four!\n");
+ return -1;
+ }
+ if (vertex >= MAX_VERTEX) {
+ print_error("Vertex %d exceeds maximum vertex number %d!\n", vertex, MAX_VERTEX);
+ return -1;
+ }
+ if (level < 1 || level > 4) {
+ print_error("Level %d is out of range (1..4)!\n", level);
+ return -1;
+ }
+ vertex >>= 2;
+ *x = interior_coord_x[vertex];
+ *y = interior_coord_y[vertex][level - 1];
+ *z = interior_coord_z[vertex];
+#ifdef DEBUG_VERTEX
+ printf("using %s coordinates: vertex=%d, x=%.0f, y=%.0f, z=%.0f\n", what, vertex, *x, *y, *z);
+#endif
+
+ return 0;
+}
+
+static int planet_rotation = 0;
+
+static void rotate_coordinate(double roll, double pitch, double yaw, double *x, double *y, double *z)
+{
+ double out_x, out_y, out_z;
+
+ /* rotate yaw (German: Gier, turn view to the right) */
+ out_z = (*z) * cos(yaw) - (*x) * sin(yaw);
+ out_x = (*z) * sin(yaw) + (*x) * cos(yaw);
+ *z = out_z;
+ *x = out_x;
+ /* rotate pitch (German: Nick, turn head down) */
+ out_y = (*y) * cos(pitch) - (*z) * sin(pitch);
+ out_z = (*y) * sin(pitch) + (*z) * cos(pitch);
+ *y = out_y;
+ *z = out_z;
+ if (roll == 0.0)
+ return;
+ /* rotate roll (tilt head to the right) */
+ out_x = (*x) * cos(roll) - (*y) * sin(roll);
+ out_y = (*x) * sin(roll) + (*y) * cos(roll);
+ *x = out_x;
+ *y = out_y;
+}
+
+/* coordinates ready for an object */
+static void coord_object(void)
+{
+ int32_t x, y, z;
+
+ x = (int16_t)REG_D[3];
+ x += (int32_t)REG_A[1];
+ y = (int16_t)REG_D[4];
+ y += (int32_t)REG_A[2];
+ z = (int16_t)REG_D[5];
+ z += (int32_t)REG_A[3];
+ store_coord("object", REG_A[0], (double)x, (double)y, (double)z);
+}
+
+static int ground_index;
+
+/* clear screen (sky / universe) */
+static void clear_screen(int index)
+{
+ double x[4], y[4], z[4];
+
+#ifdef DEBUG_VERTEX
+ printf("clear screen:\n");
+#endif
+
+ opengl_transparency_set(0.0);
+
+ /* create plane to fill view */
+ x[0] = x[1] = y[1] = y[2] = -1000000;
+ x[2] = x[3] = y[0] = y[3] = 1000000;
+ z[0] = z[1] = z[2] = z[3] = 10;
+ gamecolor2gl_index(8);
+ opengl_render_polygon(x, y, z, 4, 0); /* no culling, because background is always visible! */
+
+ opengl_transparency_set(transparency);
+
+ /* store for later use after planets have been rendered */
+ ground_index = index;
+}
+
+/* draw ground */
+static void draw_ground(void)
+{
+ double roll, pitch, yaw, x[4], y[4], z[4];
+
+ /* no ground in space :) */
+ if (ground_index < 0)
+ return;
+
+#ifdef DEBUG_VERTEX
+ printf("draw ground plane:\n");
+#endif
+
+ opengl_transparency_set(0.0);
+
+ /* get orientation */
+ mercenary_get_orientation(&roll, &pitch, &yaw);
+ yaw = 0.0; /* no need to rotate x-z plane, we don't want look at a corner of the 'ground-square' */
+
+ /* create huge square */
+ x[0] = x[1] = z[1] = z[2] = -1000000;
+ x[2] = x[3] = z[0] = z[3] = 1000000;
+ y[0] = y[1] = y[2] = y[3] = -10;
+
+ /* rotate vertex */
+ rotate_coordinate(roll, pitch, yaw, &x[0], &y[0], &z[0]);
+ rotate_coordinate(roll, pitch, yaw, &x[1], &y[1], &z[1]);
+ rotate_coordinate(roll, pitch, yaw, &x[2], &y[2], &z[2]);
+ rotate_coordinate(roll, pitch, yaw, &x[3], &y[3], &z[3]);
+
+ gamecolor2gl_index(ground_index);
+ opengl_render_polygon(x, y, z, 4, 0); /* no culling, because ground is always visible! */
+
+ opengl_transparency_set(transparency);
+}
+
+/* render polygon of object */
+static void poly_object(int mercenary)
+{
+ uint32_t vertex_address = REG_A[0];
+ uint32_t vertex;
+ int i;
+ double roll, pitch, yaw, x[16], y[16], z[16];
+ int rc;
+
+#ifdef DEBUG_VERTEX
+ printf("draw object's polygon:\n");
+#endif
+ if (mercenary == 3)
+ gamecolor2gl(REG_D[0]);
+ else {
+ uint16_t color;
+ color = m68k_read_memory_8(vertex_address++) << 8;
+ color |= m68k_read_memory_8(vertex_address++);
+ gamecolor2gl(color);
+ }
+
+ /* get orientation */
+ mercenary_get_orientation(&roll, &pitch, &yaw);
+
+ /* the vertex list is zero-terminated */
+ for (i = 0; i < 16; i++) {
+ vertex = m68k_read_memory_8(vertex_address++);
+ if (vertex == 0 && i)
+ break;
+ /* get vertex */
+ rc = use_coord("object", vertex, &x[i], &y[i], &z[i]);
+ if (rc < 0)
+ break;
+ /* rotate vertex */
+ rotate_coordinate(roll, pitch, yaw, &x[i], &y[i], &z[i]);
+ }
+ /* render polygon to OpenGL */
+ opengl_render_polygon(x, y, z, i, 1); /* back face culling */
+}
+
+/* render line of object */
+static void line_object(void)
+{
+ uint32_t vertex_address = REG_A[0];
+ uint32_t vertex;
+ double roll, pitch, yaw, x1, y1, z1, x2, y2, z2;
+ int rc;
+
+#ifdef DEBUG_VERTEX
+ printf("draw object's line:\n");
+#endif
+ gamecolor2gl(REG_D[0]);
+
+ /* get orientation */
+ mercenary_get_orientation(&roll, &pitch, &yaw);
+
+ vertex = m68k_read_memory_8(vertex_address++);
+ /* get vertex */
+ rc = use_coord("object", vertex, &x1, &y1, &z1);
+ if (rc < 0)
+ return;
+ vertex = m68k_read_memory_8(vertex_address++);
+ /* get vertex */
+ rc = use_coord("object", vertex, &x2, &y2, &z2);
+ if (rc < 0)
+ return;
+ /* rotate vertices */
+ rotate_coordinate(roll, pitch, yaw, &x1, &y1, &z1);
+ rotate_coordinate(roll, pitch, yaw, &x2, &y2, &z2);
+ /* transfer line to OpenGL */
+ opengl_render_line(x1, y1, z1, x2, y2, z2, 0.0);
+}
+
+/* coordinates ready for a beacon */
+static void coord_beacon(void)
+{
+ int32_t x, y, z;
+
+ /* only 28 bits seem to be a correct signed int value */
+ x = (double)((int32_t)REG_D[3] << 4) / 16.0;
+ y = (double)((int32_t)REG_D[4] << 4) / 16.0;
+ z = (double)((int32_t)REG_D[5] << 4) / 16.0;
+ store_coord("beacon", 0, (double)x, (double)y, (double)z);
+}
+
+/* render point of beacon */
+static void point_beacon(void)
+{
+ double roll, pitch, yaw, x, y, z;
+ int rc;
+
+#ifdef DEBUG_VERTEX
+ printf("draw beacon's point:\n");
+#endif
+ gamecolor2gl(REG_D[2]);
+
+ /* get orientation */
+ mercenary_get_orientation(&roll, &pitch, &yaw);
+
+ /* get vertex */
+ rc = use_coord("beacon", 0, &x, &y, &z);
+ if (rc < 0)
+ return;
+ /* rotate vertex */
+ rotate_coordinate(roll, pitch, yaw, &x, &y, &z);
+ /* transfer point to OpenGL */
+ opengl_render_point(x, y, z, 0.0);
+}
+
+/* coordinates ready for a building (exterior) */
+static void coord_building_exterior(void)
+{
+ int x, y, z;
+
+ x = (int32_t)REG_D[3];
+ y = (int32_t)REG_D[4];
+ z = (int32_t)REG_D[5];
+ store_coord("building exterior", REG_A[0], (double)x, (double)y, (double)z);
+}
+
+/* render polygon of building (exterior) */
+static void poly_building_exterior(void)
+{
+ uint32_t vertex_address = REG_A[0];
+ uint32_t vertex;
+ int i;
+ double roll, pitch, yaw, x[16], y[16], z[16];
+ int rc;
+
+#ifdef DEBUG_VERTEX
+ printf("draw building's polygon:\n");
+#endif
+ uint16_t color;
+ color = m68k_read_memory_8(vertex_address++) << 8;
+ color |= m68k_read_memory_8(vertex_address++);
+ gamecolor2gl(color);
+
+ /* get orientation */
+ mercenary_get_orientation(&roll, &pitch, &yaw);
+
+ /* the vertex list is zero-terminated */
+ for (i = 0; i < 16; i++) {
+ vertex = m68k_read_memory_8(vertex_address++);
+ if (vertex == 0 && i)
+ break;
+ vertex |= 0x100;
+ /* get vertex */
+ rc = use_coord("building exterior", vertex, &x[i], &y[i], &z[i]);
+ if (rc < 0)
+ break;
+ /* rotate vertex */
+ rotate_coordinate(roll, pitch, yaw, &x[i], &y[i], &z[i]);
+ }
+ /* render polygon to OpenGL */
+ opengl_render_polygon(x, y, z, i, 1); /* back face culling */
+}
+
+/* render line of building (exterior) */
+static void line_building_exterior(void)
+{
+ uint32_t vertex_address = REG_A[0];
+ uint32_t vertex;
+ double roll, pitch, yaw, x1, y1, z1, x2, y2, z2;
+ int rc;
+
+#ifdef DEBUG_VERTEX
+ printf("draw building's line:\n");
+#endif
+ gamecolor2gl(REG_D[0]);
+
+ /* get orientation */
+ mercenary_get_orientation(&roll, &pitch, &yaw);
+
+ vertex = m68k_read_memory_8(vertex_address++);
+ vertex |= 0x100;
+ /* get vertex */
+ rc = use_coord("building line", vertex, &x1, &y1, &z1);
+ if (rc < 0)
+ return;
+ vertex = m68k_read_memory_8(vertex_address++);
+ vertex |= 0x100;
+ /* get vertex */
+ rc = use_coord("building line", vertex, &x2, &y2, &z2);
+ if (rc < 0)
+ return;
+ /* rotate vertices */
+ rotate_coordinate(roll, pitch, yaw, &x1, &y1, &z1);
+ rotate_coordinate(roll, pitch, yaw, &x2, &y2, &z2);
+ /* transfer line to OpenGL */
+ opengl_render_line(x1, y1, z1, x2, y2, z2, 0.0);
+}
+
+static int interior_level12 = 0;
+static int interior_level34 = 0;
+
+/* coordinates ready for a building (interior) */
+static void coord_building_interior(void)
+{
+ int16_t east, north;
+ int32_t height1, height2, height3, height4;
+
+ mercenary_coord_building_interior(&east, &height1, &height2, &height3, &height4, &north);
+ store_interior_coord("interior", REG_A[0], (double)east, (double)height1, (double)height2, (double)height3, (double)height4, (double)north);
+}
+
+/* render polygon of building (interior) */
+static void poly_building_interior1to4(int level)
+{
+ uint16_t color;
+ uint32_t vertex;
+ int i;
+ double roll, pitch, yaw, x[16], y[16], z[16];
+ int rc;
+
+#ifdef DEBUG_VERTEX
+ printf("draw roof/floor's polygon at level %d:\n", level);
+#endif
+ color = m68k_read_memory_8(REG_A[0]) << 8;
+ color |= m68k_read_memory_8(REG_A[0] + 1);
+ gamecolor2gl(color);
+
+ /* get orientation */
+ mercenary_get_orientation(&roll, &pitch, &yaw);
+
+ /* the vertex list is zero-terminated */
+ for (i = 0; i < 4; i++) {
+ vertex = REG_A[(2 + i)];
+ /* get vertex */
+ rc = use_interior_coord("interior", vertex, level, &x[i], &y[i], &z[i]);
+ if (rc < 0)
+ break;
+ /* rotate vertex */
+ rotate_coordinate(roll, pitch, yaw, &x[i], &y[i], &z[i]);
+ }
+ /* render polygon to OpenGL */
+ opengl_render_polygon(x, y, z, i, 1); /* back face culling */
+}
+
+/* render polygon of building (interior) */
+static void poly_building_interior5to6(int level12, int level34)
+{
+ uint16_t color;
+ uint32_t vertex;
+ int i;
+ double roll, pitch, yaw, x[16], y[16], z[16];
+ int rc;
+
+#ifdef DEBUG_VERTEX
+ printf("draw polygon above/below window/door at level %d/%d:\n", level12, level34);
+#endif
+ color = m68k_read_memory_8(REG_A[0]) << 8;
+ color |= m68k_read_memory_8(REG_A[0] + 1);
+ gamecolor2gl(color);
+
+ /* get orientation */
+ mercenary_get_orientation(&roll, &pitch, &yaw);
+
+ /* the vertex list is zero-terminated */
+ for (i = 0; i < 4; i++) {
+ vertex = (i == 0 || i == 3) ? REG_A[2] : REG_A[3];
+ /* get vertex */
+ rc = use_interior_coord("interior", vertex, (i == 0 || i == 1) ? level12 : level34, &x[i], &y[i], &z[i]);
+ if (rc < 0)
+ break;
+ /* rotate vertex */
+ rotate_coordinate(roll, pitch, yaw, &x[i], &y[i], &z[i]);
+ }
+ /* render polygon to OpenGL */
+ opengl_render_polygon(x, y, z, i, 1); /* back face culling */
+}
+
+static void wall_building(void)