Render game graphics using OpenGL
authorAndreas Eversberg <jolly@eversberg.eu>
Sat, 17 Mar 2018 18:41:15 +0000 (19:41 +0100)
committerAndreas Eversberg <jolly@eversberg.eu>
Thu, 5 Apr 2018 16:00:13 +0000 (18:00 +0200)
src/libsdl/opengl.c
src/libsdl/opengl.h
src/libsdl/sdl.c
src/libsdl/sdl.h
src/mercenary/main.c
src/mercenary/mercenary.h
src/mercenary/mercenary2.c
src/mercenary/mercenary3.c
src/mercenary/render.c
src/mercenary/render.h

index 3e046f0..6d72ed2 100644 (file)
@@ -89,6 +89,13 @@ void resize_opengl(int _screen_width, int _screen_height)
        screen_height = _screen_height;
 }
 
+void opengl_copy_last(void)
+{
+       // FIXME: is it ok to copy full window height??? or just the current viewport
+       glReadBuffer(GL_FRONT);
+       glCopyPixels(0, 0, screen_width, screen_height, GL_COLOR);
+       glReadBuffer(GL_BACK);
+}
 
 void opengl_clear(void)
 {
@@ -143,7 +150,7 @@ void opengl_viewport_legacy(int split)
 }
 
 /* render legacy image texture */
-void opengl_render_legacy(uint8_t *rgb, int filter)
+void opengl_blit_legacy(uint8_t *rgb, int filter)
 {
        double width = (double)image_width / (double)texture_size;
        double height = (double)image_height / (double)texture_size;
@@ -167,10 +174,8 @@ void opengl_render_legacy(uint8_t *rgb, int filter)
        glDisable(GL_TEXTURE_2D);
 }
 
-static double fov = 64.0;
-
 /* set viewport for improved rendering */
-void opengl_viewport_improved(int split, int benson_at_line)
+void opengl_viewport_improved(int split, int benson_at_line, double fov, double benson_size)
 {
        int view_x, view_y;
        int view_width, view_height;
@@ -218,32 +223,24 @@ void opengl_viewport_improved(int split, int benson_at_line)
        /* make frustum to center the view in the game view above benson */
        double left = -slope;
        double right = slope;
-       double top = slope * (double)benson_at_line / (double)image_width;
-       double bottom = -slope * ((double)image_height * 2.0 - (double)benson_at_line) / (double)image_width;
+       double benson_start_at_position = ((double)image_height - (double)benson_at_line) * benson_size;
+       double top = slope * ((double)image_height - benson_start_at_position) / (double)image_width;
+       double bottom = -slope * ((double)image_height * 2.0 - ((double)image_height - benson_start_at_position)) / (double)image_width;
        glFrustum(left, right, bottom, top, 1.0, 5000000000.0);
        glMatrixMode(GL_MODELVIEW);
-
-#if 1
-       /* test rectangle */
-       glColor3d(0.5, 0.4, 0.4);
-       glBegin(GL_QUADS);
-       glVertex3f(-1.0, -1.0, -1.0);
-       glVertex3f(1.0, -1.0, -1.0);
-       glVertex3f(1.0, 1.0, -1.0);
-       glVertex3f(-1.0, 1.0, -1.0);
-       glEnd();
-#endif
 }
 
 /* render only benson */
-void opengl_render_benson(uint8_t *rgb, int filter, int benson_at_line)
+void opengl_blit_benson(uint8_t *rgb, int filter, int benson_at_line, double fov, double benson_size, int pixel_size)
 {
-       double texture_left = 0.0;
-       double texture_right = (double)image_width / (double)texture_size;
-       double texture_top = (double)benson_at_line / (double)texture_size;
-       double texture_bottom = (double)image_height / (double)texture_size;
-       double benson_top = -(double)benson_at_line / (double)image_width;
-       double benson_bottom = -((double)image_height * 2.0 - (double)benson_at_line) / (double)image_width;
+       double border = (benson_size != 1.0) ? (double)pixel_size : 0.0;
+       double texture_left = border / (double)texture_size;
+       double texture_right = ((double)image_width - border) / (double)texture_size;
+       double texture_top = ((double)benson_at_line + border) / (double)texture_size;
+       double texture_bottom = ((double)image_height -border) / (double)texture_size;
+       double benson_start_at_position = ((double)image_height - (double)benson_at_line) * benson_size;
+       double benson_top = -((double)image_height - benson_start_at_position) / (double)image_width;
+       double benson_bottom = -((double)image_height * 2.0 - ((double)image_height - benson_start_at_position)) / (double)image_width;
 
        /* calculate field-of-view */
        double slope = tan(fov / 360 * M_PI);
@@ -258,17 +255,98 @@ void opengl_render_benson(uint8_t *rgb, int filter, int benson_at_line)
        glTexSubImage2D(GL_TEXTURE_2D, 0, 0, benson_at_line, image_width, image_height - benson_at_line, GL_RGB, GL_UNSIGNED_BYTE, rgb);
        glBegin(GL_QUADS);
        glTexCoord2f(texture_left, texture_bottom);
-       glVertex3f(-2.0, 2.0 * benson_bottom, -2.0 / slope);
+       glVertex3f(-100.0 * benson_size, 100.0 * benson_bottom, -100.0 / slope);
        glTexCoord2f(texture_right, texture_bottom);
-       glVertex3f(2.0, 2.0 * benson_bottom, -2.0 / slope);
+       glVertex3f(100.0 * benson_size, 100.0 * benson_bottom, -100.0 / slope);
        glTexCoord2f(texture_right, texture_top);
-       glVertex3f(2.0, 2.0 * benson_top, -2.0 / slope);
+       glVertex3f(100.0 * benson_size, 100.0 * benson_top, -100.0 / slope);
        glTexCoord2f(texture_left, texture_top);
-       glVertex3f(-2.0, 2.0 * benson_top, -2.0 / slope);
+       glVertex3f(-100.0 * benson_size, 100.0 * benson_top, -100.0 / slope);
        glEnd();
        glDisable(GL_TEXTURE_2D);
 }
 
+static double transparency;
+
+/* start rendering scene */
+void opengl_transparency_set(double _transparency)
+{
+       if (transparency)
+               glDisable(GL_BLEND);
+
+       transparency = _transparency;
+
+       /* for debugging use transparent rendering */
+       if (transparency) {
+               glEnable(GL_BLEND);
+               glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+       }
+}
+
+void opengl_render_color(double r, double g, double b)
+{
+       if (transparency)
+               glColor4d(r, g, b, 1.0 - transparency);
+       else
+               glColor3d(r, g, b);
+}
+
+/* render polygon */
+void opengl_render_polygon(double *x, double *y, double *z, int count, int cull_face)
+{
+       int i;
+
+       if (cull_face) {
+               glEnable(GL_CULL_FACE);
+               glFrontFace(GL_CW);
+               glCullFace(GL_BACK);
+       }
+       glBegin(GL_POLYGON);
+       for (i = 0; i < count; i++)
+               glVertex3d(x[i], y[i], -z[i]);
+       glEnd();
+       if (cull_face)
+               glDisable(GL_CULL_FACE);
+}
+
+/* render polygon, but make sure any size of it is visible */
+void opengl_render_polygon_and_line(double *x, double *y, double *z, int count)
+{
+       int i;
+
+       glBegin(GL_POLYGON);
+       for (i = 0; i < count; i++)
+               glVertex3d(x[i], y[i], -z[i]);
+       glEnd();
+       glBegin(GL_LINE_LOOP);
+       for (i = 0; i < count; i++)
+               glVertex3d(x[i], y[i], -z[i]);
+       glEnd();
+}
+
+/* render line */
+void opengl_render_line(double x1, double y1, double z1, double x2, double y2, double z2, double size)
+{
+       if (size == 0.0) {
+               glBegin(GL_LINES);
+               glVertex3f(x1, y1, -z1);
+               glVertex3f(x2, y2, -z2);
+               glEnd();
+               return;
+       }
+}
+
+/* render point */
+void opengl_render_point(double x, double y, double z, double size)
+{
+       if (size == 0.0) {
+               glBegin(GL_POINTS);
+               glVertex3f(x, y, -z);
+               glEnd();
+               return;
+       }
+}
+
 /* free image texture */
 void exit_opengl(void)
 {
index 8d19b75..cc186d6 100644 (file)
@@ -1,10 +1,17 @@
 
 int init_opengl(int _image_width, int _image_height);
 void resize_opengl(int _screen_width, int _screen_height);
+void opengl_copy_last(void);
 void opengl_clear(void);
 void opengl_viewport_legacy(int top);
-void opengl_render_legacy(uint8_t *rgb, int filter);
-void opengl_viewport_improved(int bottom, int benson_at_line);
-void opengl_render_benson(uint8_t *rgb, int filter, int benson_at_line);
+void opengl_blit_legacy(uint8_t *rgb, int filter);
+void opengl_viewport_improved(int bottom, int benson_at_line, double fov, double benson_size);
+void opengl_blit_benson(uint8_t *rgb, int filter, int benson_at_line, double fov, double benson_size, int pixel_size);
+void opengl_transparency_set(double _transparency);
+void opengl_render_color(double r, double g, double b);
+void opengl_render_polygon(double *x, double *y, double *z, int count, int cull_face);
+void opengl_render_polygon_and_line(double *x, double *y, double *z, int count);
+void opengl_render_line(double x1, double y1, double z1, double x2, double y2, double z2, double size);
+void opengl_render_point(double x, double y, double z, double size);
 void exit_opengl(void);
 
index e6a34e6..b0f464b 100644 (file)
@@ -43,7 +43,7 @@ static void audio_cb(void __attribute__((unused)) *userdata, Uint8 *stream, int
        SDL_MixAudio(stream, (Uint8 *)audio_data, len, SDL_MIX_MAXVOLUME);
 }
 
-int init_sdl(const char *progname, int width, int height, int sound_samplerate, int sound_chunk, void (*keyboard)(int down, SDL_Keycode sym), void (*audio)(float *data, int len))
+int init_sdl(const char *progname, int width, int height, int sound_samplerate, int sound_chunk, void (*keyboard)(int down, SDL_Keycode sym), void (*audio)(float *data, int len), int multisampling)
 {
        int rc;
 
@@ -58,6 +58,16 @@ int init_sdl(const char *progname, int width, int height, int sound_samplerate,
        }
        sdl_initialized = 1;
 
+       if (multisampling) {
+               SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
+               SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
+               SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
+               SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8);
+               SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
+               SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1);
+               SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, multisampling);
+       }
+
        /* open window */
        gl_window = SDL_CreateWindow(progname, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, width, height, SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE);
        if (!gl_window) {
@@ -114,6 +124,8 @@ int init_sdl(const char *progname, int width, int height, int sound_samplerate,
        /* just in case */
        glDisable(GL_DEPTH_TEST);
        glDisable(GL_CULL_FACE);
+       if (multisampling)
+               glEnable(GL_MULTISAMPLE);
 
        /* open audio */
        SDL_AudioSpec want, have;
@@ -175,8 +187,10 @@ int event_sdl(void)
                                        if (fullscreen) {
                                                fullscreen = 0;
                                                SDL_SetWindowFullscreen(gl_window, 0);
+                                               SDL_ShowCursor(SDL_ENABLE);
                                        } else {
                                                SDL_SetWindowFullscreen(gl_window, SDL_WINDOW_FULLSCREEN_DESKTOP);
+                                               SDL_ShowCursor(SDL_DISABLE);
                                                fullscreen = 1;
                                        }
                                }
index 08eb7fc..f7e0423 100644 (file)
@@ -1,6 +1,6 @@
 #include <SDL2/SDL.h>
 
-int init_sdl(const char *progname, int width, int height, int sound_samplerate, int sound_chunk, void (*keyboard)(int down, SDL_Keycode sym), void (*audio)(float *data, int len));
+int init_sdl(const char *progname, int width, int height, int sound_samplerate, int sound_chunk, void (*keyboard)(int down, SDL_Keycode sym), void (*audio)(float *data, int len), int multisampling);
 int event_sdl(void);
 void swap_sdl(void);
 void exit_sdl(void);
index f6a640e..d762ef4 100644 (file)
 #include "mercenary.h"
 #include "render.h"
 
+#define FOV_MIN                10.0
+#define FOV_NOVAGEN    64.0
+#define FOV_JOLLY      80.0
+#define FOV_MAX                170.0
+
+static int config_ctrl_c = 0;
 static int config_amiga_speed = 1;
 static const char *config_gamesave_dir = ".mercenary";
 static int config_video_filter = 1;
 static int config_audio_filter = 1;
 static int config_render = 0;
 static int config_skip_intro = 0;
+static int config_multisampling = 16;
+static double config_fov = FOV_NOVAGEN;
+static double config_benson_size = 1.0;
+static int config_debug_transparent = 0;
 static int config_debug_opengl = 0;
+/* render improvements */
+static int config_improve_extend_roads = 0;    /* set to 1 to extend roads */
 
 #define CPU_SPEED      7093790.0;
 
@@ -77,10 +89,11 @@ static stereo_t *sound_buffer = NULL; /* sound buffer memory */
 static int sound_buffer_size; /* buffer sample size */
 static int sound_buffer_writep; /* write pointer at buffer */
 static int sound_buffer_readp; /* read pointer at buffer */
-static int double_size = 1; /* render in double size, so each pixle is 2*2 pixles wide */
+static int double_pixel_size = 1; /* render in double size, so each pixle is 2*2 pixles wide */
+static double benson_size; /* render size of benson */
 static int render_legacy = 0; /* render original amiga screen, if set */
 static int render_improved = 0; /* render improved image, if set */
-static int render_debug = 0; /* render both, amiga screen and  improved image, if set */
+static int debug_opengl = 0; /* render both, amiga screen and  improved image, if set */
 
 static const char *home_dir;
 
@@ -101,11 +114,21 @@ int parse_args(int argc, char *argv[])
                        print_info("        Set audio filter.\n");
                        print_info(" -r --render original | opegl\n");
                        print_info("        Set speed of rendering to original Amiga or full speed.\n");
+                       print_info(" -b --benson normal | half\n");
+                       print_info("        Size of 'Benson' (control panel).\n");
+                       print_info(" -m --multisampling <samples>\n");
+                       print_info("        Use multisampling (default = %d) to render the scene.\n", config_multisampling);
+                       print_info(" -f --fov <%.0f..%.0f..%.0f>\n", FOV_MIN, FOV_NOVAGEN, FOV_MAX);
+                       print_info("        Set field-of-view. Default is %.0f.\n", FOV_NOVAGEN);
                        print_info(" -i --skip-intro\n");
                        print_info("        Skip intro sequence approaching to Eris space port.\n");
                        print_info("Debug options:\n");
                        print_info(" -o --debug-opengl\n");
-                       print_info("        Use split screen to debug opengl rendering vs. amiga rendering.\n");
+                       print_info("        Use split screen to display both Amiga and OpenGL rendering.\n");
+                       print_info("    --debug-transparent\n");
+                       print_info("        Draw all things half transparent.\n");
+                       print_info("    --ctrl-c\n");
+                       print_info("        Use CTRL+C to exit game (used for development)\n");
                        return -1;
                } else
                if (!strcmp(argv[i], "-s") || !strcmp(argv[i], "--amiga-speed")) {
@@ -162,11 +185,45 @@ illegal_parameter:
                        else
                                goto illegal_parameter;
                } else
+               if (!strcmp(argv[i], "-b") || !strcmp(argv[i], "--benson")) {
+                       i++;
+                       if (argc == i)
+                               goto missing_parameter;
+                       if (!strcmp(argv[i], "normal"))
+                               config_benson_size = 1.0;
+                       else
+                       if (!strcmp(argv[i], "half")) {
+                               config_benson_size = 0.5;
+                               if (config_fov == FOV_NOVAGEN)
+                                       config_fov = FOV_JOLLY;
+                       } else
+                               goto illegal_parameter;
+               } else
+               if (!strcmp(argv[i], "-m") || !strcmp(argv[i], "--multisampling")) {
+                       i++;
+                       if (argc == i)
+                               goto missing_parameter;
+                       config_multisampling = atoi(argv[i]);
+               } else
+               if (!strcmp(argv[i], "-f") || !strcmp(argv[i], "--fov")) {
+                       i++;
+                       if (argc == i)
+                               goto missing_parameter;
+                       config_fov = atof(argv[i]);
+                       if (config_fov < 1.0 || config_fov > 179.0)
+                               goto illegal_parameter;
+               } else
                if (!strcmp(argv[i], "-i") || !strcmp(argv[i], "--skip-intro")) {
                        config_skip_intro = 1;
                } else
                if (!strcmp(argv[i], "-o") || !strcmp(argv[i], "--debug-opengl")) {
                        config_debug_opengl = 1;
+               } else
+               if (!strcmp(argv[i], "--debug-transparent")) {
+                       config_debug_transparent = 1;
+               } else
+               if (!strcmp(argv[i], "--ctrl-c")) {
+                       config_ctrl_c = 1;
                } else {
                        print_info("Illegal option '%s', use '--help'!\n", argv[i]);
                        return -1;
@@ -207,16 +264,21 @@ static void main_loop(void)
                opengl_clear();
 
                /* initialize rendering */
-               render_debug = config_debug_opengl;
-               render_legacy = (!config_render) || render_debug;
-               render_improved = config_render || render_debug;
-               /* start rendering for improved graphics */
-               if (render_improved)
-                       opengl_viewport_improved(render_debug, (double_size) ? BENSON_AT_LINE * 2 : BENSON_AT_LINE);
-
-               /* STEP 1: let the CPU render/process the game */
+               debug_opengl = config_debug_opengl;
+               benson_size = config_benson_size;
+               render_legacy = (!config_render) || debug_opengl;
+               render_improved = config_render || debug_opengl;
+               if (render_improved) {
+                       opengl_viewport_improved(debug_opengl, (double_pixel_size) ? BENSON_AT_LINE * 2 : BENSON_AT_LINE, config_fov, benson_size);
+                       opengl_copy_last();
+               }
+               /* STEP 1: let the CPU render/process the game, also improve rendering via OpenGL, if enabled */
                /* don't render if we still delay */
                if (!render_delay) {
+                       /* start rendering for improved graphics */
+                       if (render_improved)
+                               render_start(config_fov, config_improve_extend_roads, config_debug_transparent);
+
                        /* execute until the rendered image is ready (wait for VBL) */
                        cycle_count = 0;
                        do {
@@ -226,7 +288,7 @@ static void main_loop(void)
                                        special_event(event);
                        } while (event != STOP_AT_WAIT_VBL);
                        /* copy palette */
-                       palette_address = mercenary_palette();
+                       palette_address = mercenary_palette_view();
                        for (i = 0; i < 16; i++)
                                palette[i] = m68k_read_memory_16(palette_address + i*2);
                        /* for amiga speed: set delay by the number of cycles */
@@ -234,21 +296,23 @@ static void main_loop(void)
                                render_delay = (double)cycle_count / CPU_SPEED;
                }
 
-               /* STEP 2: transfer legacy image in memory to OpenGL texture */
+               /* STEP 2: transfer legacy image (or just benson) in memory to OpenGL texture */
                if (had_first_irq) {
                        /* render game view without benson
                         * because benson is not updated before VBL IRQ, we don't want old image from double buffer
                         */
                        if (render_legacy)
-                               emul_video(image, memory, palette, IMAGE_WIDTH, IMAGE_HEIGHT, IMAGE_DIWSTART, chipreg, 0, BENSON_AT_LINE, double_size);
+                               emul_video(image, memory, palette, IMAGE_WIDTH, IMAGE_HEIGHT, IMAGE_DIWSTART, chipreg, 0, BENSON_AT_LINE, double_pixel_size);
                }
                /* render benson on improved rendering, if enabled */
-               if (render_improved)
-                       opengl_render_benson(image, config_video_filter, (double_size) ? BENSON_AT_LINE * 2 : BENSON_AT_LINE);
+               if (render_improved) {
+                       render_finish();
+                       opengl_blit_benson(image, config_video_filter, (double_pixel_size) ? BENSON_AT_LINE * 2 : BENSON_AT_LINE, config_fov, benson_size, (double_pixel_size) ? 2 : 1);
+               }
                /* setup viewport for legacy image and render image, if enabled */
                if (render_legacy) {
-                       opengl_viewport_legacy(render_debug);
-                       opengl_render_legacy(image, config_video_filter);
+                       opengl_viewport_legacy(debug_opengl);
+                       opengl_blit_legacy(image, config_video_filter);
                }
                swap_sdl();
 
@@ -264,10 +328,10 @@ static void main_loop(void)
                        execute_cpu(3, NULL);
                        execute_cpu(4, NULL);
                        had_first_irq = 1;
-                       /* render benson without game view
+                       /* transfer benson without game view
                         * because we only got benson refreshed during VBL IRQ
                         */
-                       emul_video(image, memory, palette, IMAGE_WIDTH, IMAGE_HEIGHT, IMAGE_DIWSTART, chipreg, BENSON_AT_LINE, IMAGE_HEIGHT, double_size);
+                       emul_video(image, memory, palette, IMAGE_WIDTH, IMAGE_HEIGHT, IMAGE_DIWSTART, chipreg, BENSON_AT_LINE, IMAGE_HEIGHT, double_pixel_size);
                        /* render sound to sound buffer
                         * buffer pointer read and write is atomic, so no locking required!
                         */
@@ -439,34 +503,69 @@ static void keyboard_sdl(int down, SDL_Keycode sym)
        case SDLK_RCTRL:
                ctrl = down;
                break;
-       case SDLK_v:
-               if (down && ctrl) {
-                       config_video_filter ^= 1;
-                       print_info("video filter: %s\n", (config_video_filter) ? "on" : "off");
-               }
-               break;
-       case SDLK_a:
-               if (down && ctrl) {
-                       config_audio_filter ^= 1;
-                       print_info("audio filter: %s\n", (config_audio_filter) ? "on" : "off");
-               }
-               break;
-       case SDLK_s:
-               if (down && ctrl) {
-                       config_amiga_speed ^= 1;
-                       print_info("amiga speed: %s\n", (config_amiga_speed) ? "original" : "full");
-               }
-               break;
-       case SDLK_r:
-               if (down && ctrl) {
-                       config_render ^= 1;
-                       printf("render: %s\n", (config_render) ? "opengl" : "original");
+       }
+
+       if (ctrl) {
+               switch (sym) {
+               case SDLK_v:
+                       if (down) {
+                               config_video_filter ^= 1;
+                               print_info("video filter: %s\n", (config_video_filter) ? "on" : "off");
+                       }
+                       break;
+               case SDLK_a:
+                       if (down) {
+                               config_audio_filter ^= 1;
+                               print_info("audio filter: %s\n", (config_audio_filter) ? "on" : "off");
+                       }
+                       break;
+               case SDLK_s:
+                       if (down) {
+                               config_amiga_speed ^= 1;
+                               print_info("amiga speed: %s\n", (config_amiga_speed) ? "original" : "full");
+                       }
+                       break;
+               case SDLK_r:
+                       if (down) {
+                               config_render ^= 1;
+                               print_info("render: %s\n", (config_render) ? "opengl" : "original");
+                       }
+                       break;
+               case SDLK_b:
+                       if (down) {
+                               if (config_benson_size == 0.5) {
+                                       config_benson_size = 1.0;
+                                       config_fov = FOV_NOVAGEN;
+                               } else {
+                                       config_benson_size = 0.5;
+                                       config_fov = FOV_JOLLY;
+                               }
+                               print_info("benson: %s\n", (config_benson_size == 0.5) ? "half" : "normal");
+                       }
+                       break;
+               case SDLK_c:
+                       if (down) {
+                               if (config_ctrl_c)
+                                       quit = 1;
+                       }
+                       break;
+               case SDLK_KP_PLUS:
+                       if (down) {
+                               if (config_fov / 1.2 >= FOV_MIN)
+                                       config_fov /= 1.2;
+                               print_info("FOV: %.2f\n", config_fov);
+                       }
+                       break;
+               case SDLK_KP_MINUS:
+                       if (down) {
+                               if (config_fov * 1.2 <= FOV_MAX)
+                                       config_fov *= 1.2;
+                               print_info("FOV: %.2f\n", config_fov);
+                       }
+                       break;
                }
-               break;
-       case SDLK_c:
-               if (down)
-                       quit = 1;
-               break;
+               /* do not pass keys to game while holding CTRL */
+               return;
        }
 
        switch (sym) {
@@ -635,7 +734,7 @@ int main(int argc, char *argv[])
                return 0;
 
        /* allocate image */
-       image = calloc(IMAGE_WIDTH * IMAGE_HEIGHT * ((double_size) ? 4 : 1), 3);
+       image = calloc(IMAGE_WIDTH * IMAGE_HEIGHT * ((double_pixel_size) ? 4 : 1), 3);
        if (!image) {
                print_error("Failed to allocate image buffer\n");
                goto done;
@@ -696,10 +795,10 @@ int main(int argc, char *argv[])
        mercenary_patch();
 
        /* init SDL and OpenGL */
-       rc = init_sdl(argv[0], (config_debug_opengl) ? SCREEN_WIDTH * 2 : SCREEN_WIDTH * 3, (config_debug_opengl) ? SCREEN_HEIGHT * 4 : SCREEN_HEIGHT * 3, SOUND_SAMPLERATE, sdl_sound_chunk, keyboard_sdl, audio_sdl);
+       rc = init_sdl(argv[0], (config_debug_opengl) ? SCREEN_WIDTH * 2 : SCREEN_WIDTH * 3, (config_debug_opengl) ? SCREEN_HEIGHT * 4 : SCREEN_HEIGHT * 3, SOUND_SAMPLERATE, sdl_sound_chunk, keyboard_sdl, audio_sdl, config_multisampling);
        if (rc < 0)
                goto done;
-       rc = init_opengl((double_size) ? IMAGE_WIDTH * 2 : IMAGE_WIDTH, (double_size) ? IMAGE_HEIGHT * 2 : IMAGE_HEIGHT);
+       rc = init_opengl((double_pixel_size) ? IMAGE_WIDTH * 2 : IMAGE_WIDTH, (double_pixel_size) ? IMAGE_HEIGHT * 2 : IMAGE_HEIGHT);
        if (rc < 0)
                goto done;
        resize_opengl((config_debug_opengl) ? SCREEN_WIDTH * 2 : SCREEN_WIDTH * 3, (config_debug_opengl) ? SCREEN_HEIGHT * 4 : SCREEN_HEIGHT * 3);
@@ -731,6 +830,8 @@ int main(int argc, char *argv[])
        print_info("Press CTRL + v to toggle video filter.\n");
        print_info("Press CTRL + a to toggle audio filter.\n");
        print_info("Press CTRL + r to toggle rendering with opengl or orignal code.\n");
+       print_info("Press CTRL + Keypad Plus to decrement FOV.\n");
+       print_info("Press CTRL + Keypad Minus to increment FOV.\n");
        print_info("Press CTRL + c to exit game.\n\n");
        print_info("Use '--help' as command line option for configuration settings.\n\n");
 
index dc66cf9..8715a2a 100644 (file)
@@ -1,12 +1,82 @@
 
-#define        STOP_AT_END             0
-#define STOP_AT_WAIT_VBL       1
-#define STOP_AT_PARSE_OBJECT   2
-#define STOP_AT_RENDER_POLYGONS        3
+/* cause why the cpu execution loop stopped */
+enum {
+       STOP_AT_END = 0,
+       STOP_AT_WAIT_VBL,
+       STOP_AT_CLEAR_SCREEN1,
+       STOP_AT_CLEAR_SCREEN2,
+       STOP_AT_CLEAR_SCREEN3,
+       STOP_AT_DRAW_GROUND,
+       STOP_AT_COORD_OBJECT,
+       STOP_AT_POLY_OBJECT_M3,
+       STOP_AT_POLY_OBJECT_M2,
+       STOP_AT_LINE_OBJECT,
+       STOP_AT_COORD_BEACON,
+       STOP_AT_POINT_BEACON,
+       STOP_AT_COORD_BUILDING_EXTERIOR,
+       STOP_AT_POLY_BUILDING_EXTERIOR,
+       STOP_AT_LINE_BUILDING_EXTERIOR,
+       STOP_AT_COORD_BUILDING_INTERIOR,
+       STOP_AT_POLY_BUILDING_INTERIOR1,
+       STOP_AT_POLY_BUILDING_INTERIOR2,
+       STOP_AT_POLY_BUILDING_INTERIOR3,
+       STOP_AT_POLY_BUILDING_INTERIOR4,
+       STOP_AT_POLY_BUILDING_INTERIOR5,
+       STOP_AT_POLY_BUILDING_INTERIOR6,
+       STOP_AT_POLY_BUILDING_INTERIOR1to4,
+       STOP_AT_POLY_BUILDING_INTERIOR5to6,
+       STOP_AT_WALL_BUILDING,
+       STOP_AT_COORD_COMET,
+       STOP_AT_MATRIX_COMET,
+       STOP_AT_POLY_COMET,
+       STOP_AT_COORD_LINE_ROADS,
+       STOP_AT_LINE_ROADS,
+       STOP_AT_LINE_ROADS_CENTER,
+       STOP_AT_COORD_POLY_ROADS,
+       STOP_AT_POLY_ROADS,
+       STOP_AT_COORD_TAGS,
+       STOP_AT_COORD_TAGS2,
+       STOP_AT_LINE_TAGS1,
+       STOP_AT_LINE_TAGS2,
+       STOP_AT_POLY_TAGS1,
+       STOP_AT_POLY_TAGS2,
+       STOP_AT_COORD_PLANET,
+       STOP_AT_MATRIX_PLANET,
+       STOP_AT_DRAW_PLANET,
+       STOP_AT_DRAW_COMET,
+       STOP_AT_DRAW_STARS_SPACE,
+       STOP_AT_DRAW_STARS_GROUND,
+       STOP_AT_DRAW_STARS_FLYING,
+       STOP_AT_DRAW_STARS_FLYING2,
+       STOP_AT_DRAW_STARS_INTERSTELLAR,
+       STOP_AT_DRAW_SUN_INTERSTELLAR,
+       STOP_AT_COORD_ISLANDS,
+       STOP_AT_POLY_ISLANDS,
+       STOP_AT_LINE_ISLANDS,
+       STOP_AT_DRAW_SIGHTS,
+       STOP_AT_POLY_UKN2,
+       STOP_AT_PLANET_UKN1,
+       STOP_AT_PLANET_UKN2,
+};
 
 extern const struct cpu_stop mercenary_stop_at[];
 void mercenary_load(void);
 void mercenary_patch(void);
-uint32_t mercenary_palette(void);
+uint32_t mercenary_palette_view(void);
+uint32_t mercenary_palette_render(void);
+uint32_t mercenary_palette_predefined(void);
+uint32_t mercenary_palette_stars(void);
+uint32_t mercenary_object_vertex_register(void);
+void mercenary_get_orientation(double *roll, double *pitch, double *yaw);
+void mercenary_get_orientation_raw(int16_t *pitch, uint16_t *yaw);
+void mercenary_get_orientation_planet(double *inclination, double *rotation);
+void mercenary_coord_building_interior(int16_t *east, int32_t *height1, int32_t *height2, int32_t *height3, int32_t *height4, int16_t *north);
+void mercenary_get_height(int32_t *height);
+int mercenary_street_color_index(void);
+int mercenary_line_tags_index(void);
+uint16_t mercenary_poly_tags_color(void);
+int mercenary_background_index(void);
+uint32_t mercenary_planet_scale_index(void);
+uint32_t mercenary_star_table(void);
 extern const char *mercenary_gamesavesuffix;
 
index 37d648a..db87556 100644 (file)
 #include <stdio.h>
 #include <stdint.h>
 #include <stdlib.h>
+#include <math.h>
 #include "../libsdl/print.h"
 #include "../libcpu/m68k.h"
+#include "../libcpu/m68kcpu.h"
 #include "../libcpu/execute.h"
 #include "mercenary.h"
 
 
 /* interrupt CPU execution at special break points and tell emulation what to do */
 const struct cpu_stop mercenary_stop_at[] = {
-       { 0x59a4e,      STOP_AT_WAIT_VBL },     /* done with rendering, waiting for VBL */
-       { 0x54c26,      STOP_AT_WAIT_VBL },     /* after pressing 'HELP' key before showing menu line on benson */
-       { 0x55438,      STOP_AT_WAIT_VBL },     /* waiting for menu command */
-       { 0x54c2e,      STOP_AT_WAIT_VBL },     /* after pressing 'HELP' key while showing menu line on benson */
-       { 0x55446,      STOP_AT_WAIT_VBL },     /* after pressing 'RETURN' while game waits for other key to resume */
-       { 0x51620,      STOP_AT_WAIT_VBL },     /* after dying, waiting for VBL to fade out palette */
-       { 0x0,          STOP_AT_END },          /* end */
+       { 0x59a4e,      STOP_AT_WAIT_VBL },                     /* done with rendering, waiting for VBL */
+       { 0x54c26,      STOP_AT_WAIT_VBL },                     /* after pressing 'HELP' key before showing menu line on benson */
+       { 0x55438,      STOP_AT_WAIT_VBL },                     /* waiting for menu command */
+       { 0x54c2e,      STOP_AT_WAIT_VBL },                     /* after pressing 'HELP' key while showing menu line on benson */
+       { 0x55446,      STOP_AT_WAIT_VBL },                     /* after pressing 'RETURN' while game waits for other key to resume */
+       { 0x51620,      STOP_AT_WAIT_VBL },                     /* after dying, waiting for VBL to fade out palette */
+       { 0x596BC,      STOP_AT_CLEAR_SCREEN1 },                /* clear the screen (here we know the color 8, this is wy we cannot do it earlier) */
+       { 0x596F4,      STOP_AT_CLEAR_SCREEN1 },
+       { 0x59720,      STOP_AT_CLEAR_SCREEN1 },
+       { 0x598A8,      STOP_AT_CLEAR_SCREEN1 },
+       { 0x598E6,      STOP_AT_CLEAR_SCREEN2 },                /* special case where we use color index 15 when we are flying (no raster split for ground color) */
+       { 0x59982,      STOP_AT_CLEAR_SCREEN3 },                /* special case where we are in universe and do not have any ground */
+       { 0x55F2E,      STOP_AT_DRAW_GROUND },                  /* the ground is rendered. the color index is captured at CLEAR_SCREEN */
+       { 0x59710,      STOP_AT_DRAW_GROUND },                  /* at this point there is ground rendered, because game uses raster split for ground color, but we do render opengl */
+       { 0x5346E,      STOP_AT_COORD_OBJECT },                 /* object coordinates are ready */
+       { 0x534F6,      STOP_AT_POLY_OBJECT_M2 },               /* object polygon is rendered */
+       { 0x534F0,      STOP_AT_LINE_OBJECT },                  /* object line is rendered */
+       { 0x5324A,      STOP_AT_COORD_BEACON },                 /* beacon's point coordinates are ready */
+       { 0x53284,      STOP_AT_POINT_BEACON },                 /* becon point is rendered */
+       { 0x53A00,      STOP_AT_COORD_BUILDING_EXTERIOR },      /* building (house) coordinates are ready */
+       { 0x53A5C,      STOP_AT_POLY_BUILDING_EXTERIOR },       /* building polygons are rendered */
+       { 0x53A54,      STOP_AT_LINE_BUILDING_EXTERIOR },       /* lines of building, like radio tower on icarus */
+       { 0x5AA10,      STOP_AT_COORD_BUILDING_INTERIOR },      /* building coordinates for interrior */
+       { 0x5B218,      STOP_AT_POLY_BUILDING_INTERIOR1 },      /* floor of building will be rendered */
+       { 0x5B1BE,      STOP_AT_POLY_BUILDING_INTERIOR2 },      /* ceiling of building will be rendered */
+       { 0x5B154,      STOP_AT_POLY_BUILDING_INTERIOR3 },      /* ceiling of window/door will be rendered */
+       { 0x5B0E0,      STOP_AT_POLY_BUILDING_INTERIOR4 },      /* floor of window will be rendered */
+       { 0x5B2DE,      STOP_AT_POLY_BUILDING_INTERIOR1to4 },   /* ceiling/floor of building is rendered */
+       { 0x5AFF4,      STOP_AT_POLY_BUILDING_INTERIOR5 },      /* part above window/door will be rendered */
+       { 0x5AF8A,      STOP_AT_POLY_BUILDING_INTERIOR6 },      /* part below window will be rendered */
+       { 0x5B0BE,      STOP_AT_POLY_BUILDING_INTERIOR5to6 },   /* part below/above window/door of building is rendered */
+       { 0x5B36C,      STOP_AT_WALL_BUILDING },                /* a wall (between floor and ceiling/window/door) is rendered) */
+       { 0x4F462,      STOP_AT_COORD_COMET },                  /* comet's coordinates are ready */
+       { 0x4F496,      STOP_AT_MATRIX_COMET },                 /* what rotation matrix to use */
+       { 0x4F4B8,      STOP_AT_POLY_COMET },                   /* comet's horizontal polygon (without culling no need to render 0x4F4BC) */
+       { 0x4F4C0,      STOP_AT_POLY_COMET },                   /* comet's vertival polygon (without culling no need to render 0x4F4C4) */
+       { 0x54634,      STOP_AT_COORD_LINE_ROADS },             /* road's line coordinates are ready */
+       { 0x54676,      STOP_AT_LINE_ROADS },                   /* road's and ground surface's polygon */
+       { 0x5469C,      STOP_AT_LINE_ROADS },
+       { 0x546B2,      STOP_AT_LINE_ROADS },
+       { 0x56496,      STOP_AT_LINE_ROADS_CENTER },            /* center line of roads */
+       { 0x56442,      STOP_AT_COORD_POLY_ROADS },             /* road's and ground surface's coordinates are ready */
+       { 0x5649C,      STOP_AT_POLY_ROADS },                   /* road's and ground surface's polygon */
+       { 0x53CB6,      STOP_AT_COORD_TAGS },                   /* coordinates for tags, like key's marking are ready */
+       /* note: there are no coordinates for large tags in this game (no faces) */
+       /* note: there are no STOP_AT_LINE_TAGS1 and STOP_AT_POLY_TAGS1 in this game(given color) */
+       { 0x53CFA,      STOP_AT_LINE_TAGS2 },                   /* tag's line is rendered (use last color) */
+       { 0x53CF0,      STOP_AT_POLY_TAGS2 },                   /* tag's polygon is rendered (use last color) */
+       { 0x52BF4,      STOP_AT_COORD_PLANET },                 /* planet's coordinates are ready (viewed from ground) */
+       { 0x52C1A,      STOP_AT_MATRIX_COMET },                 /* what rotation matrix to use */
+       { 0x52756,      STOP_AT_COORD_PLANET },                 /* planet's coordinates are ready (viewed from universe) */
+       { 0x5277C,      STOP_AT_MATRIX_COMET },                 /* what rotation matrix to use */
+       { 0x52C70,      STOP_AT_DRAW_PLANET },                  /* planet's sphere is rendered, D0 is color (viewed from ground) */
+       { 0x52B36,      STOP_AT_DRAW_PLANET },                  /* planet's sphere is rendered, D0 is color (viewed from universe) */
+       { 0x4F4E6,      STOP_AT_DRAW_COMET },                   /* comet's sphere is rendered */
+       { 0x50006,      STOP_AT_DRAW_STARS_SPACE },             /* stars are rendered (viewed from universe) */
+       { 0x4FF4C,      STOP_AT_DRAW_STARS_GROUND },            /* stars are rendered (viewed from ground) */
+       { 0x4FE24,      STOP_AT_DRAW_STARS_FLYING },            /* stars are rendered (viewed from planet when flying) */
+       { 0x4FCAC,      STOP_AT_DRAW_STARS_FLYING2 },           /* same as above, but stars when upside down (above zenit) */
+       { 0x50FC2,      STOP_AT_DRAW_STARS_INTERSTELLAR },      /* interstellar star flight */
+       { 0x50BF4,      STOP_AT_DRAW_SUN_INTERSTELLAR },        /* draw sun dot while flying interstellar */
+       { 0x50BB8,      STOP_AT_CLEAR_SCREEN3 },                /* clear while flying interstellar */
+       { 0x563B8,      STOP_AT_COORD_ISLANDS },                /* island's coordinates are ready */
+       { 0x56416,      STOP_AT_POLY_ISLANDS },                 /* island's polygon is rendered */
+       { 0x56410,      STOP_AT_LINE_ISLANDS },
+       { 0x511FE,      STOP_AT_DRAW_SIGHTS },                  /* when sights are rendered */
+       { 0x0,          STOP_AT_END },                          /* end */
 };
 
 extern const uint32_t mercenary2_hex[];
@@ -54,12 +116,24 @@ void mercenary_load(void)
 
 void mercenary_patch(void)
 {
+       uint32_t address;
+
        /* initial stack */
        m68k_write_memory_32(0x00000, INITIAL_STACK);
 
        /* reset vector */
        m68k_write_memory_32(0x00004, RESET_VECTOR);
 
+       /* remove function that checks what stars are rendered when flying above plante
+        * instead of just rendering the necessary parts, both parts are always rendered:
+        * 1. stars from horizont up to zenith
+        * 2. stars from zenith up to horizon (upside down)
+        * we need that, so opengl rendering can use wider FOV without missing stars.
+        * the game will actually have no problem with it, except that it requires more cpu cycles
+        */
+       for (address = 0x4FD90; address < 0x4FDB6; address += 2)
+               m68k_write_memory_16(address, 0x4e71); /* nop */
+
        /* remove wait for VBL */
        m68k_write_memory_16(0x59a54, 0x4e71); /* nop */
 
@@ -77,10 +151,100 @@ void mercenary_patch(void)
        m68k_write_memory_32(0x54ffc, 1);
 }
 
-uint32_t mercenary_palette(void)
+uint32_t mercenary_palette_view(void)
 {
        return m68k_read_memory_32(0x007c14);
 }
 
+uint32_t mercenary_palette_render(void)
+{
+       return m68k_read_memory_32(0x007c18);
+}
+
+uint32_t mercenary_palette_predefined(void)
+{
+       return m68k_read_memory_32(0x007a0e);
+}
+
+uint32_t mercenary_palette_stars(void)
+{
+       return 0x500C4+66;
+}
+
+void mercenary_get_orientation(double *roll, double *pitch, double *yaw)
+{
+       int16_t r;
+
+       /* get observer's tilt, pitch, yaw */
+       r = (int16_t)(m68k_read_memory_16(0x007A9E) & 0x3ff);
+       *roll = (double)r / 1024.0 * 2 * M_PI;
+       r = (int16_t)((m68k_read_memory_16(0x007AA0) + 0x201) & 0x3ff); /* add one extra to make view leveled to ground */
+       *pitch = -(double)r / 1024.0 * 2 * M_PI;
+       r = (int16_t)((m68k_read_memory_16(0x007AA2) + 0x200) & 0x3ff);
+       *yaw = -(double)r / 1024.0 * 2 * M_PI;
+}
+
+void mercenary_get_orientation_raw(int16_t *pitch, uint16_t *yaw)
+{
+       *pitch = m68k_read_memory_16(0x007AA0);
+       *yaw = m68k_read_memory_16(0x007AA2);
+}
+
+void mercenary_get_orientation_planet(double *inclination, double *rotation)
+{
+       int16_t r;
+
+       /* get plant's inclination and rotation */
+       r = (int16_t)((m68k_read_memory_16(0x42C70)) & 0x3ff);
+       *inclination = (double)r / 1024.0 * 2 * M_PI;
+       r = (int16_t)((m68k_read_memory_16(0x42C6c)) & 0x3ff);
+       *rotation = -(double)r / 1024.0 * 2 * M_PI;
+}
+
+void mercenary_coord_building_interior(int16_t *east, int32_t *height1, int32_t *height2, int32_t *height3, int32_t *height4, int16_t *north)
+{
+       *east = (int16_t)m68k_read_memory_16(5698+REG_A[0]) - (int16_t)REG_A[2];
+       *north = (int16_t)m68k_read_memory_16(6722+REG_A[0]) - (int16_t)REG_A[3];
+       *height1 = -(int16_t)m68k_read_memory_16(0x79AC);
+       *height2 = (int16_t)m68k_read_memory_16(0x7B26) - (int16_t)m68k_read_memory_16(0x79AC);
+       *height3 = (int16_t)m68k_read_memory_16(0x7B28) - (int16_t)m68k_read_memory_16(0x79AC);
+       *height4 = (int16_t)m68k_read_memory_16(0x7B2A) - (int16_t)m68k_read_memory_16(0x79AC);
+}
+
+void mercenary_get_height(int32_t *height)
+{
+       *height = (int32_t)m68k_read_memory_32(0x7A30);
+}
+
+int mercenary_street_color_index(void)
+{
+       return (m68k_read_memory_16(0x7BE2) >> 5) & 0xf;
+}
+
+int mercenary_line_tags_index(void)
+{
+       return (m68k_read_memory_16(0x7BE2) >> 5) & 0xf;
+}
+
+uint16_t mercenary_poly_tags_color(void)
+{
+       return m68k_read_memory_16(0x7B06);
+}
+
+int mercenary_background_index(void)
+{
+       return m68k_read_memory_16(0x7AEA) >> 2;
+}
+
+uint32_t mercenary_planet_scale_index(void)
+{
+       return 24640;
+}
+
+uint32_t mercenary_star_table(void)
+{
+       return 0x005D8C0;
+}
+
 const char *mercenary_gamesavesuffix = ".m2save";
 
index 7cb4ed2..c652c08 100644 (file)
 #include <stdio.h>
 #include <stdint.h>
 #include <stdlib.h>
+#include <math.h>
 #include "../libsdl/print.h"
 #include "../libcpu/m68k.h"
+#include "../libcpu/m68kcpu.h"
 #include "../libcpu/execute.h"
 #include "mercenary.h"
 
+#define DS_0 0x530c
+#define DS_41 0x505E2
+#define DS_64 0x53d34
+#define DS_84 0x5D856
+
 #define        INITIAL_STACK   0x7fffa
 #define RESET_VECTOR   0x5a16c
 // #define RESET_VECTOR        0x5a1a4 /* strange reset vector that causes the player to fly around */
 
 /* interrupt CPU execution at special break points and tell emulation what to do */
 const struct cpu_stop mercenary_stop_at[] = {
-       { 0x5a826,      STOP_AT_WAIT_VBL },             /* done with rendering, waiting for VBL */
-       { 0x55d8c,      STOP_AT_WAIT_VBL },             /* after pressing 'HELP' key before showing menu line on benson */
-       { 0x56398,      STOP_AT_WAIT_VBL },             /* waiting for menu command */
-       { 0x55d94,      STOP_AT_WAIT_VBL },             /* after pressing 'HELP' key while showing menu line on benson */
-       { 0x563a6,      STOP_AT_WAIT_VBL },             /* after pressing 'RETURN' while game waits for other key to resume */
-       { 0x52946,      STOP_AT_WAIT_VBL },             /* after dying, waiting for VBL to fade out palette */
-       { 0x5408E,      STOP_AT_PARSE_OBJECT },         /* an object is about to be rendered, process vertex data */
-       { 0x0,          STOP_AT_END },                  /* end */
+       { 0x5a826,      STOP_AT_WAIT_VBL },                     /* done with rendering, waiting for VBL */
+       { 0x55d8c,      STOP_AT_WAIT_VBL },                     /* after pressing 'HELP' key before showing menu line on benson */
+       { 0x56398,      STOP_AT_WAIT_VBL },                     /* waiting for menu command */
+       { 0x55d94,      STOP_AT_WAIT_VBL },                     /* after pressing 'HELP' key while showing menu line on benson */
+       { 0x563a6,      STOP_AT_WAIT_VBL },                     /* after pressing 'RETURN' while game waits for other key to resume */
+       { 0x52946,      STOP_AT_WAIT_VBL },                     /* after dying, waiting for VBL to fade out palette */
+       { 0x5A456,      STOP_AT_CLEAR_SCREEN1 },                /* clear the screen (here we know the color 8, this is wy we cannot do it earlier) */
+       { 0x5A48E,      STOP_AT_CLEAR_SCREEN1 },
+       { 0x5A4BA,      STOP_AT_CLEAR_SCREEN1 },
+       { 0x5A672,      STOP_AT_CLEAR_SCREEN1 },
+       { 0x5A6B0,      STOP_AT_CLEAR_SCREEN2 },                /* special case where we use color index 15 when we are flying (no raster split for ground color) */
+       { 0x5A770,      STOP_AT_CLEAR_SCREEN3 },                /* special case where we are in universe and do not have any ground */
+       { 0x56E00,      STOP_AT_DRAW_GROUND },                  /* the ground is rendered. the color index is captured at CLEAR_SCREEN */
+       { 0x5A4AA,      STOP_AT_DRAW_GROUND },                  /* at this point there is ground rendered, because game uses raster split for ground color, but we do render opengl */
+       { 0x542B2,      STOP_AT_COORD_OBJECT },                 /* object coordinates are ready */
+       { 0x5431a,      STOP_AT_POLY_OBJECT_M3 },               /* object polygon is rendered */
+       { 0x54342,      STOP_AT_LINE_OBJECT },                  /* object line is rendered */
+       { 0x54042,      STOP_AT_COORD_BEACON },                 /* beacon's point coordinates are ready */
+       { 0x54088,      STOP_AT_POINT_BEACON },                 /* becon point is rendered */
+       { 0x54848,      STOP_AT_COORD_BUILDING_EXTERIOR },      /* building (house) coordinates are ready */
+       { 0x548a4,      STOP_AT_POLY_BUILDING_EXTERIOR },       /* building polygons are rendered */
+       { 0x5489C,      STOP_AT_LINE_BUILDING_EXTERIOR },       /* lines of building, like radio tower on icarus */
+       { 0x5B810,      STOP_AT_COORD_BUILDING_INTERIOR },      /* building coordinates for interrior */
+       { 0x5C01E,      STOP_AT_POLY_BUILDING_INTERIOR1 },      /* floor of building will be rendered */
+       { 0x5BFc4,      STOP_AT_POLY_BUILDING_INTERIOR2 },      /* ceiling of building will be rendered */
+       { 0x5BF5a,      STOP_AT_POLY_BUILDING_INTERIOR3 },      /* ceiling of window/door will be rendered */
+       { 0x5BEE6,      STOP_AT_POLY_BUILDING_INTERIOR4 },      /* floor of window will be rendered */
+       { 0x5C0E4,      STOP_AT_POLY_BUILDING_INTERIOR1to4 },   /* ceiling/floor of building is rendered */
+       { 0x5BDFA,      STOP_AT_POLY_BUILDING_INTERIOR5 },      /* part above window/door will be rendered */
+       { 0x5BD90,      STOP_AT_POLY_BUILDING_INTERIOR6 },      /* part below window will be rendered */
+       { 0x5BEC4,      STOP_AT_POLY_BUILDING_INTERIOR5to6 },   /* part below/above window/door of building is rendered */
+       { 0x5C172,      STOP_AT_WALL_BUILDING },                /* a wall (between floor and ceiling/window/door) is rendered) */
+       { 0x4FB08,      STOP_AT_COORD_COMET },                  /* comet's coordinates are ready */
+       { 0x4FB3C,      STOP_AT_MATRIX_COMET },                 /* what rotation matrix to use */
+       { 0x4FB5E,      STOP_AT_POLY_COMET },                   /* comet's horizontal polygon (without culling no need to render 0x4FB62) */
+       { 0x4FB66,      STOP_AT_POLY_COMET },                   /* comet's vertival polygon (without culling no need to render 0x4FB6A) */
+       { 0x5573E,      STOP_AT_COORD_LINE_ROADS },             /* road's line coordinates are ready */
+       { 0x55780,      STOP_AT_LINE_ROADS },                   /* road's and ground surface's polygon */
+       { 0x557A6,      STOP_AT_LINE_ROADS },
+       { 0x557BC,      STOP_AT_LINE_ROADS },
+       { 0x57370,      STOP_AT_LINE_ROADS_CENTER },            /* center line of roads */
+       { 0x5731C,      STOP_AT_COORD_POLY_ROADS },             /* road's and ground surface's coordinates are ready */
+       { 0x57376,      STOP_AT_POLY_ROADS },                   /* road's and ground surface's polygon */
+       { 0x54BF4,      STOP_AT_COORD_TAGS },                   /* coordinates for tags, like key's marking are ready */
+       { 0x54BAC,      STOP_AT_COORD_TAGS2 },                  /* coordinates for large tags are ready, like bill's face */
+       { 0x54C40,      STOP_AT_LINE_TAGS1 },                   /* tag's line is rendered (new color D0) */
+       { 0x54C3A,      STOP_AT_LINE_TAGS2 },                   /* tag's line is rendered (use last color) */
+       { 0x54C54,      STOP_AT_POLY_TAGS1 },                   /* tag's polygon is rendered (new color D0) */
+       { 0x54C30,      STOP_AT_POLY_TAGS2 },                   /* tag's polygon is rendered (use last color) */
+       { 0x53A1E,      STOP_AT_COORD_PLANET },                 /* planet's coordinates are ready (viewed from ground) */
+       { 0x53A44,      STOP_AT_MATRIX_COMET },                 /* what rotation matrix to use */
+       { 0x53712,      STOP_AT_COORD_PLANET },                 /* planet's coordinates are ready (viewed from universe) */
+       { 0x537A4,      STOP_AT_MATRIX_COMET },                 /* what rotation matrix to use */
+       { 0x53A9A,      STOP_AT_DRAW_PLANET },                  /* planet's sphere is rendered, D0 is color (viewed from ground) */
+       { 0x53960,      STOP_AT_DRAW_PLANET },                  /* planet's sphere is rendered, D0 is color (viewed from universe) */
+       { 0x4FB8C,      STOP_AT_DRAW_COMET },                   /* comet's sphere is rendered */
+       { 0x50524,      STOP_AT_DRAW_STARS_SPACE },             /* stars are rendered (viewed from universe) */
+       { 0x5046A,      STOP_AT_DRAW_STARS_GROUND },            /* stars are rendered (viewed from ground) */
+       { 0x50342,      STOP_AT_DRAW_STARS_FLYING },            /* stars are rendered (viewed from planet when flying) */
+       { 0x501CA,      STOP_AT_DRAW_STARS_FLYING2 },           /* same as above, but stars when upside down (above zenit) */
+       { 0x57290,      STOP_AT_COORD_ISLANDS },                /* island's coordinates are ready */
+       { 0x572EE,      STOP_AT_POLY_ISLANDS },                 /* island's polygon is rendered */
+       { 0x572E8,      STOP_AT_LINE_ISLANDS },
+       { 0x5214E,      STOP_AT_DRAW_SIGHTS },                  /* when sights are rendered */
+       { 0x4F748,      STOP_AT_POLY_UKN2 },
+       { 0x543B8,      STOP_AT_PLANET_UKN1 },
+       { 0x4CBF8,      STOP_AT_PLANET_UKN2 },
+       { 0x0,          STOP_AT_END },                          /* end */
 };
 
 extern const uint32_t mercenary3_hex[];
@@ -56,6 +123,8 @@ void mercenary_load(void)
 
 void mercenary_patch(void)
 {
+       uint32_t address;
+
        /* initial stack */
        m68k_write_memory_32(0x00000, INITIAL_STACK);
 
@@ -69,6 +138,38 @@ void mercenary_patch(void)
        m68k_write_memory_16(0x5a17e, 0x4e71); /* nop */
        m68k_write_memory_16(0x5a180, 0x4e71); /* nop */
 
+       /* remove function that checks what stars are rendered when flying above plante
+        * instead of just rendering the necessary parts, both parts are always rendered:
+        * 1. stars from horizont up to zenith
+        * 2. stars from zenith up to horizon (upside down)
+        * we need that, so opengl rendering can use wider FOV without missing stars.
+        * the game will actually have no problem with it, except that it requires more cpu cycles
+        */
+       for (address = 0x502AE; address < 0x502D4; address += 2)
+               m68k_write_memory_16(address, 0x4e71); /* nop */
+
+//m68k_write_memory_16(0x53A42, 0x2C20);
+
+//m68k_write_memory_16(0x58388, 0x4E75); /* rts */
+//m68k_write_memory_16(0x4FE3E, 0x4E75); /* rts */
+
+//test: loesche mit index 0
+//m68k_write_memory_16(0x5ABC2, 0x4e71); /* nop */
+//
+//m68k_write_memory_16(0x54342, 0x4e71); /* nop */
+//m68k_write_memory_16(0x54344, 0x4e71); /* nop */
+//m68k_write_memory_16(0x5059e, 0x3f0); /* nop */
+#if 0
+m68k_write_memory_16(0x4FB5E, 0x4e71); /* nop */
+m68k_write_memory_16(0x4FB60, 0x4e71); /* nop */
+m68k_write_memory_16(0x4FB62, 0x4e71); /* nop */
+m68k_write_memory_16(0x4FB64, 0x4e71); /* nop */
+m68k_write_memory_16(0x4FB66, 0x4e71); /* nop */
+m68k_write_memory_16(0x4FB68, 0x4e71); /* nop */
+m68k_write_memory_16(0x4FB6a, 0x4e71); /* nop */
+m68k_write_memory_16(0x4FB6c, 0x4e71); /* nop */
+#endif
+
        /* remove wait for VBL */
        m68k_write_memory_16(0x5a82C, 0x4e71); /* nop */
 
@@ -86,10 +187,103 @@ void mercenary_patch(void)
        m68k_write_memory_32(0x55f5c, 1);
 }
 
-uint32_t mercenary_palette(void)
+uint32_t mercenary_palette_view(void)
 {
        return m68k_read_memory_32(0x0072b0);
 }
 
+uint32_t mercenary_palette_render(void)
+{
+       return m68k_read_memory_32(0x0072b4);
+}
+
+uint32_t mercenary_palette_predefined(void)
+{
+       return m68k_read_memory_32(0x0070a4);
+}
+
+uint32_t mercenary_palette_stars(void)
+{
+       return DS_41+66;
+}
+
+void mercenary_get_orientation(double *roll, double *pitch, double *yaw)
+{
+       int16_t r;
+
+       /* we could use 0x1e4*, but then "floating on the water" is not included */
+
+       /* get observer's tilt, pitch, yaw */
+       r = (int16_t)(m68k_read_memory_16(DS_0+0x1E26) & 0x3ff);
+       *roll = (double)r / 1024.0 * 2 * M_PI;
+       r = (int16_t)((m68k_read_memory_16(DS_0+0x1E28) + 0x201) & 0x3ff); /* add one extra to make view leveled to ground */
+       *pitch = -(double)r / 1024.0 * 2 * M_PI;
+       r = (int16_t)((m68k_read_memory_16(DS_0+0x1E2a) + 0x200) & 0x3ff);
+       *yaw = -(double)r / 1024.0 * 2 * M_PI;
+}
+
+void mercenary_get_orientation_raw(int16_t *pitch, uint16_t *yaw)
+{
+       *pitch = m68k_read_memory_16(DS_0+0x1E28);
+       *yaw = m68k_read_memory_16(DS_0+0x1E2a);
+}
+
+void mercenary_get_orientation_planet(double *inclination, double *rotation)
+{
+       int16_t r;
+
+       /* get plant's inclination and rotation */
+       r = (int16_t)((m68k_read_memory_16(0x42C70)) & 0x3ff);
+       *inclination = (double)r / 1024.0 * 2 * M_PI;
+       r = (int16_t)((m68k_read_memory_16(0x42C6c)) & 0x3ff);
+       *rotation = -(double)r / 1024.0 * 2 * M_PI;
+}
+
+void mercenary_coord_building_interior(int16_t *east, int32_t *height1, int32_t *height2, int32_t *height3, int32_t *height4, int16_t *north)
+{
+       *east = (int16_t)m68k_read_memory_16(5698+REG_A[0]) - (int16_t)REG_A[2];
+       *north = (int16_t)m68k_read_memory_16(6298+REG_A[0]) - (int16_t)REG_A[3];
+       *height1 = -(int16_t)m68k_read_memory_16(DS_0+0x1D30);
+       *height2 = (int16_t)m68k_read_memory_16(DS_0+0x1EB2) - (int16_t)m68k_read_memory_16(DS_0+0x1D30);
+       *height3 = (int16_t)m68k_read_memory_16(DS_0+0x1EB4) - (int16_t)m68k_read_memory_16(DS_0+0x1D30);
+       *height4 = (int16_t)m68k_read_memory_16(DS_0+0x1EB6) - (int16_t)m68k_read_memory_16(DS_0+0x1D30);
+
+}
+
+void mercenary_get_height(int32_t *height)
+{
+       *height = (int32_t)m68k_read_memory_32(DS_0+0x1DBA);
+}
+
+int mercenary_street_color_index(void)
+{
+       return (m68k_read_memory_16(DS_0+0x1f70) >> 5) & 0xf;
+}
+
+int mercenary_line_tags_index(void)
+{
+       return (m68k_read_memory_16(DS_0+0x1f70) >> 5) & 0xf;
+}
+
+uint16_t mercenary_poly_tags_color(void)
+{
+       return m68k_read_memory_16(DS_0+0x1e92);
+}
+
+int mercenary_background_index(void)
+{
+       return m68k_read_memory_16(DS_0+0x1E76) >> 2;
+}
+
+uint32_t mercenary_planet_scale_index(void)
+{
+       return 21584;
+}
+
+uint32_t mercenary_star_table(void)
+{
+       return DS_84+0x6A;
+}
+
 const char *mercenary_gamesavesuffix = ".m3save";
 
index 2bd8f76..030213e 100644 (file)
@@ -1,4 +1,4 @@
-/* render routines that replaces the game rendering with modern rendering
+/* render routines that replaces the game rendering by OpenGL rendering
  *
  * (C) 2018 by Andreas Eversberg <jolly@eversberg.eu>
  * All Rights Reserved
 #include <stdint.h>
 #include <stdlib.h>
 #include <math.h>
+#include <GL/glew.h>
+#include "../libsdl/print.h"
 #include "../libcpu/m68k.h"
+#include "../libcpu/m68kcpu.h"
 #include "../libcpu/execute.h"
+#include "../libsdl/opengl.h"
 #include "mercenary.h"
 
-#define OBJECT_COORD_MAX 256
-static int object_coord_valid; /* set, if the coordinates below are valid */
-static int object_coord_num;
-static int object_coord_x[OBJECT_COORD_MAX];
-static int object_coord_y[OBJECT_COORD_MAX];
-static int object_coord_z[OBJECT_COORD_MAX];
+//#define DEBUG_COLOR
+//#define DEBUG_VERTEX
+
+#define MAX_VERTEX 0x300
+static int coord_x[MAX_VERTEX >> 2];
+static int coord_y[MAX_VERTEX >> 2];
+static int coord_z[MAX_VERTEX >> 2];
+
+#define MAX_INTERIOR_VERTEX 0x400 /* do we need that much? */
+static int interior_coord_x[MAX_INTERIOR_VERTEX >> 2];
+static int interior_coord_y[MAX_INTERIOR_VERTEX >> 2][4]; /* 4 levels (floor; ceiling; window or door top; window bottom) */
+static int interior_coord_z[MAX_INTERIOR_VERTEX >> 2];
+
+static int extend_roads; /* extend roads in its original width, rather than just using a single point */
+static double fov;
+static double transparency;
+static double frustum_slope_64, frustum_slope_fov;
 
 /* rendering starts, initialize variables */
-void render_start(void)
+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)
 {
-       object_coord_valid = 0;
+       double roll, pitch, yaw, x[4], y[4], z[4];
+       double bottom1_x, bottom1_y, bottom1_z;
+       double bottom2_x, bottom2_y, bottom2_z;
+       double top1_x, top1_y, top1_z;
+       double top2_x, top2_y, top2_z;
+       int rc;
+
+#ifdef DEBUG_VERTEX
+       printf("draw wall:\n");
+#endif
+       gamecolor2gl(REG_D[3]);
+
+       /* get orientation */
+       mercenary_get_orientation(&roll, &pitch, &yaw);
+
+       /* get bottom coordinate */
+       rc = use_interior_coord("interior", REG_A[1], 1, &bottom1_x, &bottom1_y, &bottom1_z);
+       if (rc < 0)
+               return;
+       /* rotate vertex */
+       rotate_coordinate(roll, pitch, yaw, &bottom1_x, &bottom1_y, &bottom1_z);
+       /* get top coordinate according to bit 12 in D3 */
+       rc = use_interior_coord("interior", REG_A[1], (REG_D[3] & (1 << 12)) ? 3 : 2, &top1_x, &top1_y, &top1_z);
+       if (rc < 0)
+               return;
+       /* rotate vertex */
+       rotate_coordinate(roll, pitch, yaw, &top1_x, &top1_y, &top1_z);
+       /* if wall is not just a strait line */
+       if (REG_A[1] != REG_A[2]) {
+               /* get bottom coordinate */
+               rc = use_interior_coord("interior", REG_A[2], 1, &bottom2_x, &bottom2_y, &bottom2_z);
+               if (rc < 0)
+                       return;
+               /* rotate vertex */
+               rotate_coordinate(roll, pitch, yaw, &bottom2_x, &bottom2_y, &bottom2_z);
+               /* get top coordinate according to bit 12 in D3 */
+               rc = use_interior_coord("interior", REG_A[2], (REG_D[3] & (1 << 12)) ? 3 : 2, &top2_x, &top2_y, &top2_z);
+               if (rc < 0)
+                       return;
+               /* rotate vertex */
+               rotate_coordinate(roll, pitch, yaw, &top2_x, &top2_y, &top2_z);
+               /* transfer vertex to OpenGL */
+               x[0] = bottom1_x;       y[0] = bottom1_y;       z[0] = bottom1_z;
+               x[1] = top1_x;          y[1] = top1_y;          z[1] = top1_z;
+               x[2] = top2_x;          y[2] = top2_y;          z[2] = top2_z;
+               x[3] = bottom2_x;       y[3] = bottom2_y;       z[3] = bottom2_z;
+               /* render polygon to OpenGL */
+               opengl_render_polygon_and_line(x, y, z, 4); /* no culling, because walls are always visible! */
+       } else {
+               /* transfer vertex to OpenGL */
+               opengl_render_line(bottom1_x, bottom1_y, bottom1_z, top1_x, top1_y, top1_z, 0.0);
+       }
 }
 
-/* parse object's coordinates, rotate them and store them */
-static void parse_object(void)
+/* coordinates ready for comet tail */
+static void coord_comet(void)
 {
-#if 0
-#warning tbd: handling in 540D2
-       int16_t r;
+       int32_t x, y, z;
+
+       x = (int32_t)REG_D[3];
+       y = (int32_t)REG_D[4];
+       z = (int32_t)REG_D[5];
+       store_coord("comet tail", REG_A[0], (double)x, (double)y, (double)z);
+}
+
+/* render polygon of comet tail */
+static void poly_comet(void)
+{
+       uint16_t color;
+       uint32_t vertex_address = REG_A[0];
+       uint32_t vertex;
+       int i;
        double roll, pitch, yaw;
-       uint32_t address;
-       int16_t word_x, word_y, word_z;
-       double  x1, y1, z1, x2, y2, z2, x3, y3, z3, x4, y4, z4;
-
-       if (object_coord_valid) {
-               fprintf(stderr, "Coodinates are valid, because previous object did not render, please fix!\n");
-               return;
-       }
-
-       /* get observer's tilt, pitch, yaw */
-       r = (int16_t)(m68k_read_memory_16(0x530c+0x1E46) & 0x1ff);
-       roll = (double)r / 512.0 * 2 * M_PI;
-       r = (int16_t)((m68k_read_memory_16(0x530c+0x1E48) + 0x200) & 0x1ff);
-       pitch = (double)r / 512.0 * 2 * M_PI;
-       r = (int16_t)((m68k_read_memory_16(0x530c+0x1E4a) + 0x200) & 0x1ff);
-       yaw = (double)r / 512.0 * 2 * M_PI;
-
-       /* get address that points to object vertices */
-       address = mercenary_object_vertex_register;
-
-       /* parse object, see M3 code at 0x540a6 */
-word_x = (int8_t)0xff;
-printf("testing %x, %d (should be extended to -1 as a word)\n", word_x, word_x);
-word_x = (int8_t)0xf1;
-printf("testing %x, %d (should be extended to -15 as a word)\n", word_x, word_x);
-word_x = (int8_t)0x80;
-printf("check value: %x (should be 0x80)\n", (uint8_t)word_x);
-exit(0);
-tbd: wie werden die koordinaten mit der objektposition translatiert?:
-       object_coord_num = 0;
-       while(42) {
-               word_x = (int8_t)m68k_read_memory_8(address);
-               address += 1;
-               /* check for 8 bit or 16 bit coordinate using this magic */
-               if ((uint8_t)word_x != 0x80) {
-                       /* we have an 8 bit coordinate */
-                       word_y = (int8_t)m68k_read_memory_8(address);
-                       address += 1;
-                       word_z = (int8_t)m68k_read_memory_8(address);
-                       address += 1;
+       double inclination, rotation;
+       double x[16], y[16], z[16];
+       int rc;
+
+#ifdef DEBUG_VERTEX
+       printf("draw comet's polygon:\n");
+#endif
+       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);
+       if (planet_rotation)
+               mercenary_get_orientation_planet(&inclination, &rotation);
+
+       /* 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("comet tail", vertex, &x[i], &y[i], &z[i]);
+               if (rc < 0)
+                       break;
+               /* rotate vertex */
+               if (planet_rotation)
+                       rotate_coordinate(0.0, inclination, rotation, &x[i], &y[i], &z[i]);
+               rotate_coordinate(roll, pitch, yaw, &x[i], &y[i], &z[i]);
+       }
+       /* render polygon to OpenGL */
+       opengl_render_polygon_and_line(x, y, z, i); /* no culling, because we render only two (out of four) planes! */
+}
+
+/* coordinates ready for lines of a road / ground surface */
+static void coord_line_road(void)
+{
+       int32_t x, y, z;
+
+       x = REG_D[3];
+       mercenary_get_height(&y);
+       y = -y;
+       z = REG_D[5];
+       store_coord("road", REG_A[0], (double)x, (double)y, (double)z);
+}
+
+/* render line of road */
+static void line_road(void)
+{
+       uint32_t vertex;
+       double roll, pitch, yaw, x1, y1, z1, x2, y2, z2;
+       int rc;
+
+#ifdef DEBUG_VERTEX
+       printf("draw road's line:\n");
+#endif
+
+       gamecolor2gl_index(mercenary_street_color_index());
+
+       /* get orientation */
+       mercenary_get_orientation(&roll, &pitch, &yaw);
+
+       vertex = REG_A[1];
+       /* get vertex */
+       rc = use_coord("road", vertex, &x1, &y1, &z1);
+       if (rc < 0)
+               return;
+       vertex = REG_A[2];
+       /* get vertex */
+       rc = use_coord("road", 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 polygons of a road / ground surface */
+static void coord_poly_road(void)
+{
+       int32_t x, y, z;
+
+//     puts("road");
+       x = m68k_read_memory_32(320 + REG_A[0]);
+       x -= REG_A[1];
+       /* the A2 is already converted to game's coordinate, so we use the memory location DS_0+0x1DBA (m3) instead */
+       mercenary_get_height(&y);
+       y = -y;
+       z = m68k_read_memory_32(576 + REG_A[0]);
+       z -= REG_A[3];
+       store_coord("road/place", REG_A[0], (double)x, (double)y, (double)z);
+}
+
+/* render polygon of road */
+static void poly_road()
+{
+       uint16_t color;
+       uint32_t vertex_address = REG_A[0];
+       uint32_t vertex, vertex_prev, vertex_next;
+       int i, v;
+       double roll, pitch, yaw, x[16], y[16], z[16];
+       double x_current, y_current, z_current;
+       double x_prev, y_prev, z_prev;
+       double x_next, y_next, z_next;
+       uint32_t vertices[16];
+       int vertices_num;
+       int rc;
+
+#ifdef DEBUG_VERTEX
+       printf("draw road/place's polygon:\n");
+#endif
+       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);
+
+
+       /* count the vertex list, it is zero-terminated */
+       vertices_num = 0;
+       for (i = 0; i < 16; i++) {
+               vertex = m68k_read_memory_8(vertex_address++);
+               if (vertex == 0 && i)
+                       break;
+               vertices[i] = vertex;
+               vertices_num++;
+       }
+
+       /* the vertex list is zero-terminated */
+       i = 0;
+       for (v = 0; v < vertices_num; v++) {
+               vertex = vertices[v];
+               rc = use_coord("road/place", vertex, &x_current, &y_current, &z_current);
+               if (rc < 0)
+                       break;
+               /* check for road extension, so we extend the road to the given end point */
+               if (extend_roads && vertex >= 0xf0) {
+                       /* previous vertex */
+                       vertex_prev = vertices[(v + vertices_num - 1) % vertices_num];
+                       rc = use_coord("road/place", vertex_prev, &x_prev, &y_prev, &z_prev);
+                       if (rc < 0)
+                               break;
+                       /* next vertex */
+                       vertex_next = vertices[(v + 1) % vertices_num];
+                       rc = use_coord("road/place", vertex_next, &x_next, &y_next, &z_next);
+                       if (rc < 0)
+                               break;
+                       /* extend vertices to end point position
+                        * change x or z coordinate, whatever is greater
+                       */
+                       if (fabs(x_prev - x_current) > fabs(z_prev - z_current))
+                               x_prev = x_next = x_current;
+                       else
+                               z_prev = z_next = z_current;
+                       /* store vertices */
+                       x[i] = x_prev;
+                       y[i] = y_prev;
+                       z[i] = z_prev;
+                       /* rotate vertex */
+                       rotate_coordinate(roll, pitch, yaw, &x[i], &y[i], &z[i]);
+                       if (i++ == 16)
+                               break;
+                       x[i] = x_next;
+                       y[i] = y_next;
+                       z[i] = z_next;
+                       /* rotate vertex */
+                       rotate_coordinate(roll, pitch, yaw, &x[i], &y[i], &z[i]);
+                       if (i++ == 16)
+                               break;
+                       continue;
                } else {
-                       /* we have an 16 bit coordinate, make address word align */
-                       if ((address & 1))
-                               address++;
-                       word_x = (int16_t)m68k_read_memory_16(address);
-                       address += 2;
-                       /* done if we get this magic */
-                       if ((uint16_t)word_x == 0x8000)
+                       /* no extension, just keep the current point as is */
+                       x[i] = x_current;
+                       y[i] = y_current;
+                       z[i] = z_current;
+                       /* rotate vertex */
+                       rotate_coordinate(roll, pitch, yaw, &x[i], &y[i], &z[i]);
+                       if (i++ == 16)
                                break;
-                       word_y = (int16_t)m68k_read_memory_16(address);
-                       address += 2;
-                       word_z = (int16_t)m68k_read_memory_16(address);
-                       address += 2;
                }
+       }
+       /* render polygon to OpenGL */
+       opengl_render_polygon(x, y, z, i, 0); /* no culling, because polygons lay on the gound and are always visible! */
+}
 
-               /* check if too many coordinates */
-               if (object_coord_num == OBJECT_COORD_MAX) {
-                       fprintf(stderr, "object has too many coordinates, please fix!\n");
-                       return;
-               }
+/* coordinates ready for tags */
+static void coord_tags(void)
+{
+       int32_t x, y, z;
 
-               /* convert to double */
-               x1 = (double)word_x;
-               y1 = (double)word_y;
-               z1 = (double)word_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("tags", REG_A[0], (double)x, (double)y, (double)z);
+}
 
-               /* rotate roll (tilt head to the right) */
-               x2 = x1 * cos(roll) - y1 * sin(roll);
-               y2 = x1 * sin(roll) + y1 * cos(roll);
-               z2 = z1;
+/* coordinates ready for large tags */
+static void coord_tags2(void)
+{
+       int32_t x, y, z;
 
-               /* rotate head (pitch down) */
-               y3 = y2 * cos(pitch) - z2 * sin(pitch);
-               z3 = y2 * sin(pitch) + z2 * cos(pitch);
-               x3 = x2;
+       x = (int16_t)REG_D[3];
+       x += 2 * (int32_t)REG_A[1];
+       y = (int16_t)REG_D[4];
+       y += 2 * (int32_t)REG_A[2];
+       z = (int16_t)REG_D[5];
+       z += 2 * (int32_t)REG_A[3];
+       store_coord("large tags", REG_A[0], (double)x, (double)y, (double)z);
+}
 
-               /* rotate yaw (turn right) */
-               z4 = z3 * cos(yaw) - x3 * sin(yaw);
-               x4 = z3 * sin(yaw) + x3 * cos(yaw);
-               y4 = y3;
+/* render line of tag */
+static void line_tags(int last_color)
+{
+       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 tag's line:\n");
+#endif
 
-               /* store vertices as float */
-               object_coord_x[object_coord_num] = x4;
-               object_coord_y[object_coord_num] = y4;
-               object_coord_z[object_coord_num] = z4;
+       if (!last_color) {
+               gamecolor2gl(REG_D[0]);
+       } else {
+               gamecolor2gl_index(mercenary_line_tags_index());
        }
 
-       /* we are done, coordinates are valid */
-       object_coord_valid = 1;
+       /* get orientation */
+       mercenary_get_orientation(&roll, &pitch, &yaw);
+
+       vertex = m68k_read_memory_8(vertex_address++);
+       vertex |= 0x200;
+       /* get vertex */
+       rc = use_coord("tag", vertex, &x1, &y1, &z1);
+       if (rc < 0)
+               return;
+       vertex = m68k_read_memory_8(vertex_address++);
+       vertex |= 0x200;
+       /* get vertex */
+       rc = use_coord("tag", 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);
+}
+
+/* render polygon of tags */
+static void poly_tags(int last_color)
+{
+       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 tag's polygon:\n");
 #endif
+       if (!last_color) {
+               gamecolor2gl(REG_D[0]);
+       } else {
+               gamecolor2gl(mercenary_poly_tags_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 |= 0x200;
+               /* get vertex */
+               rc = use_coord("tag", 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 */
 }
 
-void render_polygons(void)
+/* coordinates ready for planet */
+static void coord_planet(void)
 {
-#if 0
-#warning beim polygon auf object_coord_num checken
-       if (!object_coord_valid) {
-               print failure, if coords not valid!!
+       int32_t x, y, z;
+
+       x = (int32_t)REG_D[3];
+       y = (int32_t)REG_D[4];
+       z = (int32_t)REG_D[5];
+       store_coord("planet", REG_A[0], (double)x, (double)y, (double)z);
+}
+
+#define PLANET_VERTICES 128
+#define PLANET_ELIPSE  1.17
+
+/* render planet */
+static void draw_planet(int comet)
+{
+       uint32_t color = REG_D[0];
+       uint32_t vertex = REG_A[0];
+       uint32_t scale_index;
+       int i;
+       double scale1, scale2, size, angle;
+       double roll, pitch, yaw;
+       double inclination, rotation;
+       double sun_x, sun_y, sun_z, angle_sun;
+       double loc_x, loc_y, loc_z, angle_loc;
+       double x[PLANET_VERTICES], y[PLANET_VERTICES], z[PLANET_VERTICES];
+       int rc;
+
+       /* fixing (not noticable) bug in game: don't render comet twice */
+       if (!comet && vertex == 116)
+               return;
+
+       /* use background color for dark side */
+       color |= (mercenary_background_index() << 16) | 0x80000000; /* shifted by 2 */
+
+       /* make comet black on front side and bright on back */
+       if (comet)
+               color = 0x07770000;
+
+#ifdef DEBUG_VERTEX
+       printf("draw planet/comet/sun: vertex=%d color=%08x\n", vertex, color);
+#endif
+       /* I REVERSED THE F*CKING PLANET SIZE, took me half a day!
+        * the long word 21584(A0) contains two scales
+        * the lower word contains the scale between 4096 .. 8191, this is 1.0 .. 2.0
+        * the upper word defines how much this scale is shifted to the left.
+        */
+       scale_index = mercenary_planet_scale_index();
+       scale1 = (double)(m68k_read_memory_16(scale_index + 2 + vertex) & 0x1fff) / 8192.0;
+       scale2 = (double)(1 << (uint16_t)m68k_read_memory_16(scale_index + vertex));
+       size = scale1 * scale2 / 128.0;
+
+       /* get orientation */
+       mercenary_get_orientation(&roll, &pitch, &yaw);
+       if (planet_rotation)
+               mercenary_get_orientation_planet(&inclination, &rotation);
+
+       /* get location */
+       rc = use_coord("planet(sun)", 0, &sun_x, &sun_y, &sun_z);
+       if (rc < 0)
                return;
+       rc = use_coord("planet", vertex, &loc_x, &loc_y, &loc_z);
+       if (rc < 0)
+               return;
+       /* rotate vertex */
+       if (planet_rotation) {
+               rotate_coordinate(0.0, inclination, rotation, &sun_x, &sun_y, &sun_z);
+               rotate_coordinate(0.0, inclination, rotation, &loc_x, &loc_y, &loc_z);
        }
+       rotate_coordinate(roll, pitch, yaw, &sun_x, &sun_y, &sun_z);
+       rotate_coordinate(roll, pitch, yaw, &loc_x, &loc_y, &loc_z);
+
+       /* calculate direction of the sun */
+       angle_sun = atan2(sun_x, sun_z);
+       angle_loc = atan2(loc_x, loc_z);
+       angle = angle_sun - angle_loc;
+       if (angle > M_PI)
+               angle -= 2.0 * M_PI;
+       if (angle < -M_PI)
+               angle += 2.0 * M_PI;
 
+       /* on which side are we (sun is always bright, vertex == 0) */
+       if ((angle < M_PI / 2.0 && angle > -M_PI / 2.0) || vertex == 0) {
+               /* set bright side */
+               gamecolor2gl(color);
+       } else {
+               /* set dark side */
+               gamecolor2gl(color >> 16);
+       }
 
-       render..
+       /* create and render cicle */
+       for (i = 0; i < PLANET_VERTICES; i++) {
+               x[i] = loc_x + size * sin(2 * M_PI * (double)i / PLANET_VERTICES) * PLANET_ELIPSE;
+               y[i] = loc_y + size * cos(2 * M_PI * (double)i / PLANET_VERTICES);
+               z[i] = loc_z;
+       }
+       opengl_render_polygon_and_line(x, y, z, PLANET_VERTICES); /* no culling, its a planet! */
 
+       if (vertex == 0) {
+               /* sun has no crescent */
+               return;
+       }
 
+       /* on which side are we */
+       if (angle < M_PI / 2.0 && angle > -M_PI / 2.0) {
+               /* set dark side */
+               gamecolor2gl(color >> 16);
+       } else {
+               /* set bright side */
+               gamecolor2gl(color);
+       }
 
-       /* done with rendering, mark coordinates as beeing invalid */
-       object_coord_valid = 0;
+       /* create and render crescent */
+       if (angle > M_PI / 2.0 || (angle < 0.0 && angle > -M_PI / 2.0)) {
+               angle = fabs(angle);
+               if (angle > M_PI / 2.0)
+                       angle = M_PI - angle;
+               /* to the right */
+               for (i = 0; i < PLANET_VERTICES / 2 + 1; i++) {
+                       x[i] = loc_x + size * sin(2 * M_PI * (double)i / PLANET_VERTICES) * PLANET_ELIPSE;
+                       y[i] = loc_y + size * cos(2 * M_PI * (double)i / PLANET_VERTICES);
+                       z[i] = loc_z;
+               }
+               for (; i < PLANET_VERTICES; i++) {
+                       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));
+                       y[i] = loc_y + size * cos(2 * M_PI * (double)i / PLANET_VERTICES);
+                       z[i] = loc_z;
+               }
+       } else {
+               angle = fabs(angle);
+               if (angle > M_PI / 2.0)
+                       angle = M_PI - angle;
+               /* to the left */
+               for (i = 0; i < PLANET_VERTICES / 2 + 1; i++) {
+                       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));
+                       y[i] = loc_y + size * cos(2 * M_PI * (double)i / PLANET_VERTICES);
+                       z[i] = loc_z;
+               }
+               for (; i < PLANET_VERTICES; i++) {
+                       x[i] = loc_x + size * sin(2 * M_PI * (double)i / PLANET_VERTICES) * PLANET_ELIPSE;
+                       y[i] = loc_y + size * cos(2 * M_PI * (double)i / PLANET_VERTICES);
+                       z[i] = loc_z;
+               }
+       }
+       opengl_render_polygon_and_line(x, y, z, PLANET_VERTICES); /* no culling, its a planet! */
+       opengl_render_point(loc_x, loc_y, loc_z, 0.0); /* planet is visible at any distance - at least as a point  */
+}
+
+/* draw stars */
+static void draw_stars(int16_t v_offset, int tilt, int above_zenith)
+{
+       int32_t tilt_value;
+       int16_t tilt_offset = 0;
+       int x_offset = 0;
+       uint16_t color[16];
+       uint16_t view_width, yaw;
+       int16_t pitch;
+       uint32_t table, table_start;
+       int16_t x, y;
+       double z;
+       int i;
+
+#ifdef DEBUG_VERTEX
+       printf("draw stars\n");
 #endif
+       /* use default fov of 64 to calculate z distance */
+       z = 160.0 / frustum_slope_64;
+
+       view_width = (int)(320.0 / frustum_slope_64 * frustum_slope_fov);
+
+       /* get palette */
+       for (i = 0; i < 16; i++)
+               color[i] = m68k_read_memory_16(mercenary_palette_stars() + (i << 2));
+
+       /* if we fly over the planet, we have tilt, so we get the calculated tilt value from game */
+       if (tilt)
+               tilt_value = (int32_t)REG_A[4];
+
+       /* table offset is 91, so we substract it and add it back with different FOV, so table begins at later
+        * full turn is 1024, we have default of 64 degrees: 1024 / 360 * 64 = 182
+        * then we half it, so we get to the center via 91
+        */
+       mercenary_get_orientation_raw(&pitch, &yaw);
+
+       if (above_zenith)
+               yaw = 0x200 + yaw;
+       yaw = (yaw + 91 - (int)(91.0 * (frustum_slope_fov / frustum_slope_64))) & 0x3ff;
+       yaw <<= 1;
+       table = mercenary_star_table();
+       table_start = table + m68k_read_memory_16(table);
+       table += m68k_read_memory_16(table + yaw);
+       yaw = ((uint32_t)yaw * 0xe10e) >> 16;
+
+       if (above_zenith)
+               pitch = 0x200 - pitch;
+       pitch = pitch & 0x3ff;
+       pitch -= v_offset;
+       pitch <<= 2;
+       pitch = (pitch * 0x6ccc) >> 16;
+
+       while (1) {
+               x = m68k_read_memory_16(table);
+               if (x >= 1800) {
+                       x_offset += 1800;
+                       table = table_start;
+               }
+               x = (view_width - 1) - m68k_read_memory_16(table) - x_offset + yaw;
+               table += 2;
+               if (x < 0)
+                       break;
+               /* special case where we tilt the view when flying on the planet */
+               if (tilt) {
+                       /* use offset as given by game: 160 is half of the screen width
+                        * we extend the width to the actual FOV, so it fits
+                        */
+                       tilt_offset = (int32_t)((x - (int16_t)(160.0 / frustum_slope_64 * frustum_slope_fov)) * tilt_value) >> 16;
+               }
+               y = ((m68k_read_memory_16(table)) & 0x1ff) - pitch + tilt_offset;
+               table += 2;
+               if (above_zenith) {
+                       x = (view_width - 1) - x;
+                       y = ~y + 136;
+               }
+               /* get color */
+               gamecolor2gl(color[(m68k_read_memory_8(table - 2) & 0x3c) >> 2]);
+               /* render point */
+               opengl_render_point(160.0 / frustum_slope_64 * frustum_slope_fov - (double)x, 68.0 - (double)y, z, 0.0);
+       }
+}
+
+/* draw stars of interstellar flight */
+static void draw_stars_interstellar(void)
+{
+       int i, count;
+       uint32_t table;
+       uint16_t color[16];
+       int16_t x, y;
+       double z;
+
+#ifdef DEBUG_VERTEX
+       printf("draw interstellar stars\n");
+#endif
+       /* use default fov of 64 to calculate z distance */
+       z = 160.0 / frustum_slope_64;
+
+       /* get palette */
+       for (i = 0; i < 16; i++)
+               color[i] = m68k_read_memory_16(mercenary_palette_stars() + (i << 2));
+
+       table = REG_A[0];
+       count = REG_D[5] + 1;
+       for (i = 0; i < count; i++) {
+               table += 12;
+               /* get color */
+               gamecolor2gl(color[(m68k_read_memory_16(table) >> 5) & 0xf]);
+               table += 2;
+               x = m68k_read_memory_16(table);
+               table += 2;
+               y = m68k_read_memory_16(table);
+               table += 2;
+               /* render point */
+               opengl_render_point(160.0 - (double)x, 68.0 - (double)y, z, 0.0);
+       }
+
+}
+
+/* draw sun of interstellar flight (center dot) */
+static void draw_sun_interstellar(void)
+{
+#ifdef DEBUG_VERTEX
+       printf("draw interstellar sun\n");
+#endif
+
+       /* white */
+       gamecolor2gl(0x777);
+       /* render point */
+       opengl_render_point(0.0, 0.0, 100.0, 0.0);
+}
+
+/* coordinates ready for polygons of islands */
+static void coord_islands(void)
+{
+       int32_t x, y, z;
+
+       x = ((int16_t)m68k_read_memory_16(REG_A[4] - 2) * 65536);
+       x += (int32_t)REG_A[1];
+       /* the A2 is already converted to game's coordinate, so we use the memory location DS_0+0x1DBA instead */
+       mercenary_get_height(&y);
+       y = -y;
+       z = ((int16_t)m68k_read_memory_16(REG_A[4]) * 65536);
+       z += (int32_t)REG_A[3];
+       store_coord("island", REG_A[0], (double)x, (double)y, (double)z);
+}
+
+/* render polygon of island */
+static void poly_island()
+{
+       uint16_t color;
+       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 island:\n");
+#endif
+       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 */
+       i = 0;
+       while (1) {
+               vertex = m68k_read_memory_8(vertex_address++);
+               if (vertex == 0 && i)
+                       break;
+               /* skip mysterious points when rendering island */
+               if (vertex >= 0xf0)
+                       continue;
+               rc = use_coord("island", vertex, &x[i], &y[i], &z[i]);
+               if (rc < 0)
+                       break;
+               /* rotate vertex */
+               rotate_coordinate(roll, pitch, yaw, &x[i], &y[i], &z[i]);
+               if (i++ == 16)
+                       break;
+       }
+       /* render polygon to OpenGL */
+       opengl_render_polygon_and_line(x, y, z, i); /* no culling, because polygons lay on the gound and are always visible! */
+}
+
+/* draw sights */
+static void draw_sights(void)
+{
+       double x[4], y[4], z[4];
+
+       /* use default fov of 64 to calculate z distance */
+       z[0] = z[1] = z[2] = z[3] = 160.0 / frustum_slope_64;
+
+       /* white */
+       gamecolor2gl(0x777);
+
+       y[0] = y[3] = -1.0;
+       y[1] = y[2] = 1.0;
+       x[0] = x[1] = -16.0;
+       x[2] = x[3] = -8.0;
+       opengl_render_polygon(x, y, z, 4, 0); /* no culling, because sights are always visible! */
+       x[0] = x[1] = 8.0;
+       x[2] = x[3] = 16.0;
+       opengl_render_polygon(x, y, z, 4, 0); /* no culling, because sights are always visible! */
 }
 
 /* stop event from CPU received */
 void render_improved_event(int event)
 {
        switch (event) {
-       case STOP_AT_PARSE_OBJECT:
-               parse_object();
+       case STOP_AT_CLEAR_SCREEN1:
+               clear_screen(16); /* color 16 is raster split */
+               break;
+       case STOP_AT_CLEAR_SCREEN2:
+               clear_screen(15);
+               break;
+       case STOP_AT_CLEAR_SCREEN3:
+               clear_screen(-1); /* no ground (in universe) */
+               break;
+       case STOP_AT_DRAW_GROUND:
+               draw_ground();
+               break;
+       case STOP_AT_COORD_OBJECT:
+               coord_object();
+               break;
+       case STOP_AT_POLY_OBJECT_M3:
+               poly_object(3);
+               break;
+       case STOP_AT_POLY_OBJECT_M2:
+               poly_object(2);
+               break;
+       case STOP_AT_LINE_OBJECT:
+               line_object();
+               break;
+       case STOP_AT_COORD_BEACON:
+               coord_beacon();
+               break;
+       case STOP_AT_POINT_BEACON:
+               point_beacon();
                break;
-       case STOP_AT_RENDER_POLYGONS:
-               render_polygons();
+       case STOP_AT_COORD_BUILDING_EXTERIOR:
+               coord_building_exterior();
+               break;
+       case STOP_AT_POLY_BUILDING_EXTERIOR:
+               poly_building_exterior();
+               break;
+       case STOP_AT_LINE_BUILDING_EXTERIOR:
+               line_building_exterior();
+               break;
+       case STOP_AT_COORD_BUILDING_INTERIOR:
+               coord_building_interior();
+               break;
+       case STOP_AT_POLY_BUILDING_INTERIOR1:
+               /* floor */
+               interior_level12 = 1;
+               interior_level34 = 1;
+               break;
+       case STOP_AT_POLY_BUILDING_INTERIOR2:
+               /* ceiling */
+               interior_level12 = 2;
+               interior_level34 = 2;
+               break;
+       case STOP_AT_POLY_BUILDING_INTERIOR3:
+               /* door/window top */
+               interior_level12 = 3;
+               interior_level34 = 3;
+               break;
+       case STOP_AT_POLY_BUILDING_INTERIOR4:
+               /* window bottom */
+               interior_level12 = 4;
+               interior_level34 = 4;
+               break;
+       case STOP_AT_POLY_BUILDING_INTERIOR5:
+               /* door/window top */
+               interior_level12 = 2;
+               interior_level34 = 3;
+               break;
+       case STOP_AT_POLY_BUILDING_INTERIOR6:
+               /* window bottom */
+               interior_level12 = 1;
+               interior_level34 = 4;
+               break;
+       case STOP_AT_POLY_BUILDING_INTERIOR1to4:
+               /* before we come here, we must already passed the break points above, so we know the level to be rendered */
+               if (interior_level12 == 0) {
+                       print_error("Interior level is not set, please fix!\n");
+                       break;
+               }
+               poly_building_interior1to4(interior_level12);
+               interior_level12 = 0;
+               break;
+       case STOP_AT_POLY_BUILDING_INTERIOR5to6:
+               /* before we come here, we must already passed the break points above, so we know the level to be rendered */
+               if (interior_level12 == 0) {
+                       print_error("Interior level is not set, please fix!\n");
+                       break;
+               }
+               poly_building_interior5to6(interior_level12, interior_level34);
+               interior_level12 = 0;
+               break;
+       case STOP_AT_WALL_BUILDING:
+               wall_building();
+               break;
+       case STOP_AT_COORD_COMET:
+               coord_comet();
+               break;
+       case STOP_AT_MATRIX_COMET:
+       case STOP_AT_MATRIX_PLANET:
+               /* track the rotation matrix
+                * if we have 0x42c44 matrix, we must add extra rotation to planet.
+                * the rotation will change the view from the planet's surface */
+               if (REG_A[4] == 0x42c44) /* same with M2 and M3 */
+                       planet_rotation = 1;
+               else
+                       planet_rotation = 0;
+               break;
+       case STOP_AT_POLY_COMET:
+               poly_comet();
+               break;
+       case STOP_AT_COORD_LINE_ROADS:
+               coord_line_road();
+               break;
+       case STOP_AT_LINE_ROADS:
+               line_road();
+               break;
+       case STOP_AT_COORD_POLY_ROADS:
+               coord_poly_road();
+               break;
+       case STOP_AT_LINE_ROADS_CENTER:
+               /* we don't need to render center lines of roads, because there are polygons already
+                * it does not make sense, since OpenGL has much higher resolution.
+                */
+               break;
+       case STOP_AT_POLY_ROADS:
+               poly_road();
+               break;
+       case STOP_AT_COORD_TAGS:
+               coord_tags();
+               break;
+       case STOP_AT_COORD_TAGS2:
+               coord_tags2();
+               break;
+       case STOP_AT_LINE_TAGS1:
+               line_tags(0);
+               break;
+       case STOP_AT_LINE_TAGS2:
+               line_tags(1);
+               break;
+       case STOP_AT_POLY_TAGS1:
+               poly_tags(0);
+               break;
+       case STOP_AT_POLY_TAGS2:
+               poly_tags(1);
+               break;
+       case STOP_AT_COORD_PLANET:
+               coord_planet();
+               break;
+       case STOP_AT_DRAW_PLANET:
+               draw_planet(0);
+               break;
+       case STOP_AT_DRAW_COMET:
+               draw_planet(1);
+               break;
+       case STOP_AT_DRAW_STARS_SPACE:
+               /* render stars with vertical offset 0x1c0, no tilt, not above zenith */
+               draw_stars(0x1c0, 0, 0);
+               break;
+       case STOP_AT_DRAW_STARS_GROUND:
+               /* render stars with vertical offset 0x128, no tilt, not above zenith */
+               draw_stars(0x128, 0, 0); /* yet it's hex 128! */
+               break;
+       case STOP_AT_DRAW_STARS_FLYING:
+               /* render stars with vertical offset 0x128, with tilt, not above zenith */
+               draw_stars(0x128, 1, 0); /* yet it's hex 128! */
+               break;
+       case STOP_AT_DRAW_STARS_FLYING2:
+               /* render stars with vertical offset 0x128, with tilt, and above zenith */
+               draw_stars(0x128, 1, 1); /* yet it's hex 128! */
+               break;
+       case STOP_AT_DRAW_STARS_INTERSTELLAR:
+               draw_stars_interstellar();
+               break;
+       case STOP_AT_DRAW_SUN_INTERSTELLAR:
+               draw_sun_interstellar();
+               break;
+       case STOP_AT_COORD_ISLANDS:
+               coord_islands();
+               break;
+       case STOP_AT_POLY_ISLANDS:
+               poly_island();
+               break;
+       case STOP_AT_LINE_ISLANDS:
+               /* this is not used, as i had noticed so far */
+               puts("line island");
+               break;
+       case STOP_AT_DRAW_SIGHTS:
+               draw_sights();
+               break;
+       case STOP_AT_POLY_UKN2:
+               puts("poly ukn2");
+               break;
+       case STOP_AT_PLANET_UKN1:
+               puts("planet ukn1");
+               break;
+       case STOP_AT_PLANET_UKN2:
+               puts("planet ukn2");
                break;
        }
 }
+
index 3e10a6f..4549675 100644 (file)
@@ -1,4 +1,5 @@
 
-void render_start(void);
+void render_start(double _fov, int _extend_roads, int debug);
+void render_finish(void);
 void render_improved_event(int event);