Prepare renering for improving game with OpenGL
authorAndreas Eversberg <jolly@eversberg.eu>
Thu, 8 Mar 2018 17:13:56 +0000 (18:13 +0100)
committerAndreas Eversberg <jolly@eversberg.eu>
Sun, 25 Mar 2018 12:23:14 +0000 (14:23 +0200)
A switch (command line and keyboard) can be used to toggle rendering.
A special swith (-o) can be used to render both legacy and improved
view top/bottom. This way it is easier to debug the expected result.

readme.txt
src/libsdl/opengl.c
src/libsdl/opengl.h
src/mercenary/Makefile.am
src/mercenary/main.c
src/mercenary/mercenary.h
src/mercenary/mercenary3.c
src/mercenary/render.c [new file with mode: 0644]
src/mercenary/render.h [new file with mode: 0644]

index 7b2d108..5a64640 100644 (file)
@@ -1,3 +1,8 @@
+Prerequisites:
+
+Install autoconf and libtool, so you can generate the configure scripts.
+Install SDL2 and GLEW, because this software requires SDL and OpenGL.
+
 To build:
 
 $ autoreconf -if
@@ -5,4 +10,10 @@ $ ./configure
 $ make
 $ make install
 
+Problems:
+
+$ libtoolize
+$ automake --add-missing
 
+./configure: line 15095: syntax error near unexpected token `sdl2,'
+./configure: line 15095: `  PKG_CHECK_MODULES(sdl2, sdl2 >= 2.0, with_sdl2=yes, with_sdl2=no)'
index ae3eaca..3e046f0 100644 (file)
 #include <stdio.h>
 #include <stdint.h>
 #include <stdlib.h>
+#include <math.h>
 #include <errno.h>
 #include "print.h"
 #include "opengl.h"
 #include <GL/glew.h>
 
-static uint8_t *text_rgb = NULL;
-static GLuint text_name;
+static uint8_t *legacy_rgb = NULL;
+static uint8_t *benson_rgb = NULL;
+static GLuint legacy_name;
+static GLuint benson_name;
 static int screen_width, screen_height;
 static int image_width, image_height;
 static int texture_size;
@@ -42,90 +45,240 @@ int init_opengl(int _image_width, int _image_height)
        /* generate texture */
        for (texture_size = 1; texture_size <= image_width || texture_size <= image_height; texture_size *= 2)
                ;
-       text_rgb = calloc(texture_size * texture_size * 10, 3);
-       if (!text_rgb) {
+
+       legacy_rgb = calloc(texture_size * texture_size, 3);
+       if (!legacy_rgb) {
                print_error("Failed to allocate texture\n");
                rc = -ENOMEM;
                goto error;
        }
-       glClearColor(0.0, 0.0, 0.0, 1.0);
        glShadeModel(GL_FLAT);
        glPixelStorei(GL_UNPACK_ALIGNMENT, 1); /* bytes */
-       glGenTextures(1, &text_name);
-       glBindTexture(GL_TEXTURE_2D, text_name);
+       glGenTextures(1, &legacy_name);
+       glBindTexture(GL_TEXTURE_2D, legacy_name);
+       glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+       glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+       glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, texture_size, texture_size, 0, GL_RGB, GL_UNSIGNED_BYTE, legacy_rgb);
+
+       benson_rgb = calloc(texture_size * texture_size, 3);
+       if (!benson_rgb) {
+               print_error("Failed to allocate texture\n");
+               rc = -ENOMEM;
+               goto error;
+       }
+
+       glShadeModel(GL_FLAT);
+       glPixelStorei(GL_UNPACK_ALIGNMENT, 1); /* bytes */
+       glGenTextures(1, &benson_name);
+       glBindTexture(GL_TEXTURE_2D, benson_name);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
-       glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, texture_size, texture_size, 0, GL_RGB, GL_UNSIGNED_BYTE, text_rgb);
+       glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, texture_size, texture_size, 0, GL_RGB, GL_UNSIGNED_BYTE, benson_rgb);
 
        return 0;
 
 error:
+       exit_opengl();
        return rc;
 }
 
 /* set clip planes so that the image portion of the image texture is centered and pixels are rectengular */
 void resize_opengl(int _screen_width, int _screen_height)
 {
-       double width_border = 1.0;
-       double height_border = 1.0;
-
-       if (_screen_width < 1 || _screen_height < 1)
-               return;
        screen_width = _screen_width;
        screen_height = _screen_height;
+}
+
 
-       if (image_width * screen_height > screen_width * image_height) {
-               height_border = (double)(image_width * screen_height) / (double)(screen_width * image_height);
+void opengl_clear(void)
+{
+       glClearColor(0.0, 0.0, 0.0, 1.0);
+       glClear(GL_COLOR_BUFFER_BIT);
+}
+
+/* set viewport for legacy image */
+void opengl_viewport_legacy(int split)
+{
+       int view_x, view_y;
+       int view_width, view_height;
+       int new_width, new_height;
+
+       if (!split) {
+               view_x = 0;
+               view_y = 0;
+               view_width = screen_width;
+               view_height = screen_height;
+       } else {
+               view_x = 0;
+               view_y = screen_height / 2;
+               view_width = screen_width;
+               view_height = screen_height / 2;
        }
-       if (image_width * screen_height < screen_width * image_height) {
-               width_border = (double)(screen_width * image_height / (double)(image_width * screen_height));
+
+       /* avoid division by zero, if one dimension is too small */
+       if (view_width < 1 || view_height < 1)
+               return;
+
+       /* calculate a viewport that has apect of image_width:image_height */
+       if (view_height * image_width > view_width * image_height) {
+               new_height = view_width * image_height / image_width;
+               view_y = view_y + view_height / 2 - new_height / 2;
+               view_height = new_height;
+       } else if (view_height * image_width < view_width * image_height) {
+               new_width = view_height * image_width / image_height;
+               view_x = view_x + view_width / 2 - new_width / 2;
+               view_width = new_width;
        }
 
+       /* avoid views that are too small */
+       if (view_width < 1 || view_height < 1)
+               return;
+
        /* viewport and projection matrix */
-       glViewport(0, 0, (GLsizei)screen_width, (GLsizei)screen_height);
+       glViewport((GLsizei)view_x, (GLsizei)view_y, (GLsizei)view_width, (GLsizei)view_height);
        glMatrixMode(GL_PROJECTION);
        glLoadIdentity();
-
-       double width = (double)image_width / (double)texture_size;
-       double height = (double)image_height / (double)texture_size;
-       glOrtho(
-                       -width * (width_border - 1.0) / 2,
-                       width / 2 + width * width_border / 2,
-                       height / 2 + height * height_border / 2,
-                       -height * (height_border - 1.0) / 2,
-                       -1.0, 1.0);
+       glOrtho(0.0, 1.0, 1.0, 0.0, -1.0, 1.0);
        glMatrixMode(GL_MODELVIEW);
 }
 
-/* render image texture */
-void render_opengl(uint8_t *rgb, int filter)
+/* render legacy image texture */
+void opengl_render_legacy(uint8_t *rgb, int filter)
 {
-       glClear(GL_COLOR_BUFFER_BIT);
+       double width = (double)image_width / (double)texture_size;
+       double height = (double)image_height / (double)texture_size;
+
        glEnable(GL_TEXTURE_2D);
        glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);  /* no modulation with color */
-       glBindTexture(GL_TEXTURE_2D, text_name);
+       glBindTexture(GL_TEXTURE_2D, legacy_name);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, (filter) ? GL_LINEAR : GL_NEAREST);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, (filter) ? GL_LINEAR : GL_NEAREST);
        glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, image_width, image_height, GL_RGB, GL_UNSIGNED_BYTE, rgb);
        glBegin(GL_QUADS);
        glTexCoord2f(0.0, 0.0);
        glVertex3f(0.0, 0.0, 0.0);
-       glTexCoord2f(1.0, 0.0);
+       glTexCoord2f(width, 0.0);
        glVertex3f(1.0, 0.0, 0.0);
-       glTexCoord2f(1.0, 1.0);
+       glTexCoord2f(width, height);
        glVertex3f(1.0, 1.0, 0.0);
-       glTexCoord2f(0.0, 1.0);
+       glTexCoord2f(0.0, height);
        glVertex3f(0.0, 1.0, 0.0);
        glEnd();
        glDisable(GL_TEXTURE_2D);
 }
 
+static double fov = 64.0;
+
+/* set viewport for improved rendering */
+void opengl_viewport_improved(int split, int benson_at_line)
+{
+       int view_x, view_y;
+       int view_width, view_height;
+       int new_width, new_height;
+
+       /* center view, or put it in the top half of the window */
+       if (!split) {
+               view_x = 0;
+               view_y = 0;
+               view_width = screen_width;
+               view_height = screen_height;
+       } else {
+               view_x = 0;
+               view_y = 0;
+               view_width = screen_width;
+               view_height = screen_height / 2;
+       }
+
+       /* avoid division by zero, if one dimension is too small */
+       if (view_width < 1 || view_height < 1)
+               return;
+
+       /* calculate a viewport that has apect of image_width:image_height */
+       if (view_height * image_width > view_width * image_height) {
+               new_height = view_width * image_height / image_width;
+               view_y = view_y + view_height / 2 - new_height / 2;
+               view_height = new_height;
+       } else if (view_height * image_width < view_width * image_height) {
+               new_width = view_height * image_width / image_height;
+               view_x = view_x + view_width / 2 - new_width / 2;
+               view_width = new_width;
+       }
+
+       /* avoid views that are too small */
+       if (view_width < 1 || view_height < 1)
+               return;
+
+       /* viewport and projection matrix */
+       glViewport((GLsizei)view_x, (GLsizei)view_y, (GLsizei)view_width, (GLsizei)view_height);
+       glMatrixMode(GL_PROJECTION);
+       glLoadIdentity();
+
+       /* calculate field-of-view */
+       double slope = tan(fov / 360 * M_PI);
+       /* 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;
+       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)
+{
+       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;
+
+       /* calculate field-of-view */
+       double slope = tan(fov / 360 * M_PI);
+
+       /* render benson */
+       rgb += image_width * benson_at_line * 3;
+       glEnable(GL_TEXTURE_2D);
+       glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);  /* no modulation with color */
+       glBindTexture(GL_TEXTURE_2D, benson_name);
+       glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, (filter) ? GL_LINEAR : GL_NEAREST);
+       glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, (filter) ? GL_LINEAR : GL_NEAREST);
+       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);
+       glTexCoord2f(texture_right, texture_bottom);
+       glVertex3f(2.0, 2.0 * benson_bottom, -2.0 / slope);
+       glTexCoord2f(texture_right, texture_top);
+       glVertex3f(2.0, 2.0 * benson_top, -2.0 / slope);
+       glTexCoord2f(texture_left, texture_top);
+       glVertex3f(-2.0, 2.0 * benson_top, -2.0 / slope);
+       glEnd();
+       glDisable(GL_TEXTURE_2D);
+}
+
 /* free image texture */
 void exit_opengl(void)
 {
-       if (text_rgb) {
-               free(text_rgb);
-               text_rgb = NULL;
+       if (legacy_rgb) {
+               free(legacy_rgb);
+               legacy_rgb = NULL;
+       }
+       if (benson_rgb) {
+               free(benson_rgb);
+               benson_rgb = NULL;
        }
 }
 
index 2d783ff..8d19b75 100644 (file)
@@ -1,6 +1,10 @@
 
 int init_opengl(int _image_width, int _image_height);
 void resize_opengl(int _screen_width, int _screen_height);
-void render_opengl(uint8_t *rgb, int filter);
+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 exit_opengl(void);
 
index 1cfc712..6bd8075 100644 (file)
@@ -6,6 +6,7 @@ if HAVE_GLEW
 noinst_LIBRARIES = libmain.a
 
 libmain_a_SOURCES = \
+       render.c \
        main.c
 
 bin_PROGRAMS = mercenary3 mercenary2
index c3ca13a..d831d26 100644 (file)
 #include "../libkeyboard/keyboard.h"
 #include "../libdisk/disk.h"
 #include "mercenary.h"
+#include "render.h"
 
 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_debug_opengl = 0;
 
 #define CPU_SPEED      7093790.0;
 
@@ -61,8 +64,8 @@ static int config_audio_filter = 1;
 #define        MEMORY_SIZE     0x80000
 static uint8_t *memory = NULL;
 static uint8_t *image = NULL;
-#define SCREEN_WIDTH   (320*3)
-#define SCREEN_HEIGHT  (200*3)
+#define SCREEN_WIDTH   320
+#define SCREEN_HEIGHT  200
 #define IMAGE_WIDTH    320
 #define IMAGE_HEIGHT   200
 #define BENSON_AT_LINE 136
@@ -73,6 +76,9 @@ 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 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 const char *home_dir;
 
@@ -91,6 +97,10 @@ int parse_args(int argc, char *argv[])
                        print_info("        Set video filter.\n");
                        print_info(" -a --audio-filter on | off\n");
                        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(" -o --debug-opengl\n");
+                       print_info("        Use split screen to debug opengl rendering vs. amiga rendering.\n");
                        return -1;
                } else
                if (!strcmp(argv[i], "-s") || !strcmp(argv[i], "--amiga-speed")) {
@@ -134,6 +144,21 @@ illegal_parameter:
                                config_audio_filter = 0;
                        else
                                goto illegal_parameter;
+               } else
+               if (!strcmp(argv[i], "-r") || !strcmp(argv[i], "--render")) {
+                       i++;
+                       if (argc == i)
+                               goto missing_parameter;
+                       if (!strcmp(argv[i], "original"))
+                               config_render = 0;
+                       else
+                       if (!strcmp(argv[i], "opengl"))
+                               config_render = 1;
+                       else
+                               goto illegal_parameter;
+               } else
+               if (!strcmp(argv[i], "-o") || !strcmp(argv[i], "--debug-opengl")) {
+                       config_debug_opengl = 1;
                } else {
                        print_info("Illegal option '%s', use '--help'!\n", argv[i]);
                        return -1;
@@ -146,6 +171,8 @@ illegal_parameter:
 
 static void special_event(int event)
 {
+       if (render_improved)
+               render_improved_event(event);
 }
 
 static void main_loop(void)
@@ -163,6 +190,22 @@ static void main_loop(void)
 
        /* render result on window */
        while (!quit) {
+               /* handle SDL events */
+               rc = event_sdl();
+               if (rc)
+                       break;
+
+               /* clear screen */
+               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 */
                /* don't render if we still delay */
                if (!render_delay) {
@@ -183,17 +226,22 @@ static void main_loop(void)
                                render_delay = (double)cycle_count / CPU_SPEED;
                }
 
-               /* STEP 2: render image in memory via OpenGL */
+               /* STEP 2: transfer legacy image 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
                         */
-                       emul_video(image, memory, palette, IMAGE_WIDTH, IMAGE_HEIGHT, IMAGE_DIWSTART, chipreg, 0, BENSON_AT_LINE, double_size);
+                       if (render_legacy)
+                               emul_video(image, memory, palette, IMAGE_WIDTH, IMAGE_HEIGHT, IMAGE_DIWSTART, chipreg, 0, BENSON_AT_LINE, double_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);
+               /* 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);
                }
-               rc = event_sdl();
-               if (rc)
-                       break;
-               render_opengl(image, config_video_filter);
                swap_sdl();
 
                /* STEP 3: execute interrupt at rate of 50Hz, render sound */
@@ -401,6 +449,12 @@ static void keyboard_sdl(int down, SDL_Keycode sym)
                        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");
+               }
+               break;
        case SDLK_c:
                if (down)
                        quit = 1;
@@ -629,13 +683,13 @@ int main(int argc, char *argv[])
        mercenary_patch();
 
        /* init SDL and OpenGL */
-       rc = init_sdl(argv[0], SCREEN_WIDTH, SCREEN_HEIGHT, 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);
        if (rc < 0)
                goto done;
        rc = init_opengl((double_size) ? IMAGE_WIDTH * 2 : IMAGE_WIDTH, (double_size) ? IMAGE_HEIGHT * 2 : IMAGE_HEIGHT);
        if (rc < 0)
                goto done;
-       resize_opengl(SCREEN_WIDTH, SCREEN_HEIGHT);
+       resize_opengl((config_debug_opengl) ? SCREEN_WIDTH * 2 : SCREEN_WIDTH * 3, (config_debug_opengl) ? SCREEN_HEIGHT * 4 : SCREEN_HEIGHT * 3);
 
        /* init audio filter */
        sound_init_filter(SOUND_SAMPLERATE);
@@ -651,6 +705,7 @@ int main(int argc, char *argv[])
        print_info("Press CTRL + s to toggle rendering speed.\n");
        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 + c to exit game.\n\n");
        print_info("Use '--help' as command line option for configuration settings.\n\n");
 
index bfd5106..dc66cf9 100644 (file)
@@ -1,6 +1,8 @@
 
 #define        STOP_AT_END             0
 #define STOP_AT_WAIT_VBL       1
+#define STOP_AT_PARSE_OBJECT   2
+#define STOP_AT_RENDER_POLYGONS        3
 
 extern const struct cpu_stop mercenary_stop_at[];
 void mercenary_load(void);
index 3ebec3f..7cb4ed2 100644 (file)
 
 /* 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 */
-       { 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 */
+       { 0x5408E,      STOP_AT_PARSE_OBJECT },         /* an object is about to be rendered, process vertex data */
+       { 0x0,          STOP_AT_END },                  /* end */
 };
 
 extern const uint32_t mercenary3_hex[];
diff --git a/src/mercenary/render.c b/src/mercenary/render.c
new file mode 100644 (file)
index 0000000..2bd8f76
--- /dev/null
@@ -0,0 +1,170 @@
+/* render routines that replaces the game rendering with modern rendering
+ *
+ * (C) 2018 by Andreas Eversberg <jolly@eversberg.eu>
+ * All Rights Reserved
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <math.h>
+#include "../libcpu/m68k.h"
+#include "../libcpu/execute.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];
+
+/* rendering starts, initialize variables */
+void render_start(void)
+{
+       object_coord_valid = 0;
+}
+
+/* parse object's coordinates, rotate them and store them */
+static void parse_object(void)
+{
+#if 0
+#warning tbd: handling in 540D2
+       int16_t r;
+       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;
+               } 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)
+                               break;
+                       word_y = (int16_t)m68k_read_memory_16(address);
+                       address += 2;
+                       word_z = (int16_t)m68k_read_memory_16(address);
+                       address += 2;
+               }
+
+               /* check if too many coordinates */
+               if (object_coord_num == OBJECT_COORD_MAX) {
+                       fprintf(stderr, "object has too many coordinates, please fix!\n");
+                       return;
+               }
+
+               /* convert to double */
+               x1 = (double)word_x;
+               y1 = (double)word_y;
+               z1 = (double)word_z;
+
+               /* rotate roll (tilt head to the right) */
+               x2 = x1 * cos(roll) - y1 * sin(roll);
+               y2 = x1 * sin(roll) + y1 * cos(roll);
+               z2 = z1;
+
+               /* rotate head (pitch down) */
+               y3 = y2 * cos(pitch) - z2 * sin(pitch);
+               z3 = y2 * sin(pitch) + z2 * cos(pitch);
+               x3 = x2;
+
+               /* rotate yaw (turn right) */
+               z4 = z3 * cos(yaw) - x3 * sin(yaw);
+               x4 = z3 * sin(yaw) + x3 * cos(yaw);
+               y4 = y3;
+
+               /* 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;
+       }
+
+       /* we are done, coordinates are valid */
+       object_coord_valid = 1;
+#endif
+}
+
+void render_polygons(void)
+{
+#if 0
+#warning beim polygon auf object_coord_num checken
+       if (!object_coord_valid) {
+               print failure, if coords not valid!!
+               return;
+       }
+
+
+       render..
+
+
+
+       /* done with rendering, mark coordinates as beeing invalid */
+       object_coord_valid = 0;
+#endif
+}
+
+/* stop event from CPU received */
+void render_improved_event(int event)
+{
+       switch (event) {
+       case STOP_AT_PARSE_OBJECT:
+               parse_object();
+               break;
+       case STOP_AT_RENDER_POLYGONS:
+               render_polygons();
+               break;
+       }
+}
diff --git a/src/mercenary/render.h b/src/mercenary/render.h
new file mode 100644 (file)
index 0000000..3e10a6f
--- /dev/null
@@ -0,0 +1,4 @@
+
+void render_start(void);
+void render_improved_event(int event);
+