From: Andreas Eversberg Date: Sat, 17 Mar 2018 18:41:15 +0000 (+0100) Subject: Render game graphics using OpenGL X-Git-Url: http://git.eversberg.eu/gitweb.cgi?p=mercenary-reloaded.git;a=commitdiff_plain;h=4fa6282ea7046efbcde18c3964216a2631563f55 Render game graphics using OpenGL --- diff --git a/src/libsdl/opengl.c b/src/libsdl/opengl.c index 3e046f0..6d72ed2 100644 --- a/src/libsdl/opengl.c +++ b/src/libsdl/opengl.c @@ -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) { diff --git a/src/libsdl/opengl.h b/src/libsdl/opengl.h index 8d19b75..cc186d6 100644 --- a/src/libsdl/opengl.h +++ b/src/libsdl/opengl.h @@ -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); diff --git a/src/libsdl/sdl.c b/src/libsdl/sdl.c index e6a34e6..b0f464b 100644 --- a/src/libsdl/sdl.c +++ b/src/libsdl/sdl.c @@ -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; } } diff --git a/src/libsdl/sdl.h b/src/libsdl/sdl.h index 08eb7fc..f7e0423 100644 --- a/src/libsdl/sdl.h +++ b/src/libsdl/sdl.h @@ -1,6 +1,6 @@ #include -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); diff --git a/src/mercenary/main.c b/src/mercenary/main.c index f6a640e..d762ef4 100644 --- a/src/mercenary/main.c +++ b/src/mercenary/main.c @@ -35,13 +35,25 @@ #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 \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"); diff --git a/src/mercenary/mercenary.h b/src/mercenary/mercenary.h index dc66cf9..8715a2a 100644 --- a/src/mercenary/mercenary.h +++ b/src/mercenary/mercenary.h @@ -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; diff --git a/src/mercenary/mercenary2.c b/src/mercenary/mercenary2.c index 37d648a..db87556 100644 --- a/src/mercenary/mercenary2.c +++ b/src/mercenary/mercenary2.c @@ -20,8 +20,10 @@ #include #include #include +#include #include "../libsdl/print.h" #include "../libcpu/m68k.h" +#include "../libcpu/m68kcpu.h" #include "../libcpu/execute.h" #include "mercenary.h" @@ -30,13 +32,73 @@ /* 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"; diff --git a/src/mercenary/mercenary3.c b/src/mercenary/mercenary3.c index 7cb4ed2..c652c08 100644 --- a/src/mercenary/mercenary3.c +++ b/src/mercenary/mercenary3.c @@ -20,25 +20,92 @@ #include #include #include +#include #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"; diff --git a/src/mercenary/render.c b/src/mercenary/render.c index 2bd8f76..030213e 100644 --- a/src/mercenary/render.c +++ b/src/mercenary/render.c @@ -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 * All Rights Reserved @@ -21,150 +21,1508 @@ #include #include #include +#include +#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; } } + diff --git a/src/mercenary/render.h b/src/mercenary/render.h index 3e10a6f..4549675 100644 --- a/src/mercenary/render.h +++ b/src/mercenary/render.h @@ -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);