From 90c6e8a475982df68445bb7b0dcafb5d89669abb Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Sat, 31 Mar 2018 12:35:53 +0200 Subject: [PATCH] Motion interpolation --- src/mercenary/main.c | 71 ++++- src/mercenary/mercenary.h | 13 +- src/mercenary/mercenary2.c | 42 ++- src/mercenary/mercenary3.c | 45 ++- src/mercenary/render.c | 769 ++++++++++++++++++++++++++++++++++++++------- src/mercenary/render.h | 4 +- 6 files changed, 772 insertions(+), 172 deletions(-) diff --git a/src/mercenary/main.c b/src/mercenary/main.c index 11eceb1..cda13e1 100644 --- a/src/mercenary/main.c +++ b/src/mercenary/main.c @@ -43,6 +43,8 @@ static int config_ctrl_c = 0; static int config_amiga_speed = 1; +#warning fixme: make use of 10 for intro, then be as fast as given +static double config_fps = 16.0; static const char *config_gamesave_dir = ".mercenary"; static int config_video_filter = 1; static int config_audio_filter = 1; @@ -54,7 +56,7 @@ 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 */ +static int config_improve_extend_roads = 1; /* set to 1 to extend roads */ #define CPU_SPEED 7093790.0; @@ -107,14 +109,16 @@ int parse_args(int argc, char *argv[]) while (argc > i) { if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) { print_info("Usage: %s\n", argv[0]); - print_info(" -s --amiga-speed original | full\n"); - print_info(" Set speed of rendering to original Amiga or full speed.\n"); + print_info(" -s --render-speed original | fast\n"); + print_info(" Set speed of rendering to original Amiga or fast speed.\n"); + print_info(" --fps \n"); + print_info(" Set frames per second for fast rate (default = %.1f).\n", config_fps); print_info(" -v --video-filter on | off\n"); print_info(" Set video filter.\n"); print_info(" -a --audio-filter on | off\n"); print_info(" Set audio filter.\n"); - print_info(" -r --render original | opegl\n"); - print_info(" Set speed of rendering to original Amiga or full speed.\n"); + print_info(" -r --render original | opengl\n"); + print_info(" Set speed of rendering to original Amiga or OpenGL.\n"); print_info(" -b --benson normal | half\n"); print_info(" Size of 'Benson' (control panel).\n"); print_info(" -m --multisampling \n"); @@ -132,7 +136,7 @@ int parse_args(int argc, char *argv[]) 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")) { + if (!strcmp(argv[i], "-s") || !strcmp(argv[i], "--render-speed")) { i++; if (argc == i) { missing_parameter: @@ -142,7 +146,7 @@ missing_parameter: if (!strcmp(argv[i], "original")) config_amiga_speed = 1; else - if (!strcmp(argv[i], "full")) + if (!strcmp(argv[i], "fast")) config_amiga_speed = 0; else { illegal_parameter: @@ -150,6 +154,14 @@ illegal_parameter: return -1; } } else + if (!strcmp(argv[i], "--fps")) { + i++; + if (argc == i) + goto missing_parameter; + config_fps = atof(argv[i]); + if (config_fov <= 0.0) + goto illegal_parameter; + } else if (!strcmp(argv[i], "-v") || !strcmp(argv[i], "--video-filter")) { i++; if (argc == i) @@ -243,11 +255,12 @@ static void special_event(int event) static void main_loop(void) { + double frame_step, frame_time = 0.0, frame_render = 1; int had_first_irq = 0; static uint32_t current_time, last_time = 0, diff; int i, rc; int space, length; - int cycle_count, event; + int cycle_count, event = STOP_AT_END; double render_delay = 0.0; uint32_t palette_address; uint16_t palette[16]; @@ -256,7 +269,16 @@ static void main_loop(void) /* render result on window */ while (!quit) { -printf("frame rate: %.6f\n", 1.0 / vbl_duration); +#warning oder ganz anders: bevor landung machen wir amiga-rate als interpolation + /* if we are in interstellar fligt, we use 50 Hz */ + /* if we are approaching to Eris Space Port, we use 10 Hz */ + /* else we use whatever frame rate the user wants */ + if (render_capture_is_interstellar()) + frame_step = vbl_duration * 50.0; + else + frame_step = vbl_duration * config_fps; + if (frame_step > 1.0) + frame_step = 1.0; /* handle SDL events */ rc = event_sdl(); if (rc) @@ -270,13 +292,22 @@ printf("frame rate: %.6f\n", 1.0 / vbl_duration); benson_size = config_benson_size; render_legacy = (!config_render) || debug_opengl; render_improved = config_render || debug_opengl; + if (!render_improved) { + /* be sure to clean all capture history, so we don't get glichtes when turning on improved rendering again */ + render_capture_reset(); + } 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) { + /* amgia speed: don't render if we still delay */ + /* non amiga speed: render if we need a new frame */ + /* NOTE: at input event we must render after every VBL, so we do this in every loop */ + if ((frame_render && !config_amiga_speed) + || (config_amiga_speed && render_delay <= 0.0) + || event == STOP_AT_WAIT_INPUT) { + frame_render = 0; /* start capturing for improved graphics */ if (render_improved) render_capture_start(config_fov, config_improve_extend_roads, config_debug_transparent); @@ -286,9 +317,8 @@ printf("frame rate: %.6f\n", 1.0 / vbl_duration); do { cycle_count += execute_cpu(0, &event); /* handle special events */ - if (event != STOP_AT_WAIT_VBL) - special_event(event); - } while (event != STOP_AT_WAIT_VBL); + special_event(event); + } while (event != STOP_AT_WAIT_VBL && event != STOP_AT_WAIT_INPUT); /* stop capturing for improved graphics */ if (render_improved) render_capture_stop(); @@ -298,11 +328,18 @@ printf("frame rate: %.6f\n", 1.0 / vbl_duration); palette[i] = m68k_read_memory_16(palette_address + i*2); /* for amiga speed: set delay by the number of cycles */ if (config_amiga_speed) - render_delay = (double)cycle_count / CPU_SPEED; + render_delay += (double)cycle_count / CPU_SPEED; } - /* render improved graphics */ + /* render improved graphics, interpolate, if required */ if (render_improved) - render_all_items(); + render_all_items((config_amiga_speed) ? 1.0 : frame_time); + /* advance frame time */ +printf("frame rate: %.6f, frame-step=%.5f frame-time=%.5f\n", 1.0 / vbl_duration, frame_step,frame_time); + frame_time += frame_step; + if (frame_time >= 1.0) { + frame_time -= 1.0; + frame_render = 1; + } /* STEP 2: transfer legacy image (or just benson) in memory to OpenGL texture */ if (had_first_irq) { diff --git a/src/mercenary/mercenary.h b/src/mercenary/mercenary.h index 8715a2a..772064a 100644 --- a/src/mercenary/mercenary.h +++ b/src/mercenary/mercenary.h @@ -3,10 +3,15 @@ enum { STOP_AT_END = 0, STOP_AT_WAIT_VBL, + STOP_AT_WAIT_INPUT, STOP_AT_CLEAR_SCREEN1, STOP_AT_CLEAR_SCREEN2, STOP_AT_CLEAR_SCREEN3, STOP_AT_DRAW_GROUND, + STOP_AT_INFO_OBJECT_MOVING, + STOP_AT_INFO_OBJECT_FIX, + STOP_AT_TAG_IS_OBJECT_1, + STOP_AT_TAG_IS_OBJECT_0, STOP_AT_COORD_OBJECT, STOP_AT_POLY_OBJECT_M3, STOP_AT_POLY_OBJECT_M2, @@ -55,8 +60,7 @@ enum { STOP_AT_LINE_ISLANDS, STOP_AT_DRAW_SIGHTS, STOP_AT_POLY_UKN2, - STOP_AT_PLANET_UKN1, - STOP_AT_PLANET_UKN2, + STOP_AT_EXPLOSION, }; extern const struct cpu_stop mercenary_stop_at[]; @@ -69,9 +73,10 @@ 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_get_orientation_planet(double *inclination, double *azimuth); +void mercenary_get_location(int32_t *east, int32_t *height, int32_t *north); +void mercenary_get_object_info(int *id, int32_t *east, int32_t *height, int32_t *north); 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); diff --git a/src/mercenary/mercenary2.c b/src/mercenary/mercenary2.c index db87556..1267c75 100644 --- a/src/mercenary/mercenary2.c +++ b/src/mercenary/mercenary2.c @@ -33,10 +33,10 @@ /* 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 */ + { 0x54c26, STOP_AT_WAIT_INPUT }, /* after pressing 'HELP' key before showing menu line on benson */ + { 0x55438, STOP_AT_WAIT_INPUT }, /* waiting for menu command */ + { 0x54c2e, STOP_AT_WAIT_INPUT }, /* after pressing 'HELP' key while showing menu line on benson */ + { 0x55446, STOP_AT_WAIT_INPUT }, /* 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 }, @@ -46,6 +46,10 @@ const struct cpu_stop mercenary_stop_at[] = { { 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 */ + { 0x531EA, STOP_AT_INFO_OBJECT_MOVING }, /* get ID and position of next object */ + /* note: there a no fix objects in this game (no taxi/bus/intercity) */ + { 0x534EA, STOP_AT_TAG_IS_OBJECT_1 }, /* indicates that the next tag is an object */ + { 0x534EE, STOP_AT_TAG_IS_OBJECT_0 }, /* indicates that the tag of object was rendered */ { 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 */ @@ -81,9 +85,9 @@ const struct cpu_stop mercenary_stop_at[] = { { 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 */ + { 0x52C1A, STOP_AT_MATRIX_PLANET }, /* 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 */ + { 0x5277C, STOP_AT_MATRIX_PLANET }, /* 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 */ @@ -98,6 +102,8 @@ const struct cpu_stop mercenary_stop_at[] = { { 0x56416, STOP_AT_POLY_ISLANDS }, /* island's polygon is rendered */ { 0x56410, STOP_AT_LINE_ISLANDS }, { 0x511FE, STOP_AT_DRAW_SIGHTS }, /* when sights are rendered */ + { 0x5351A, STOP_AT_EXPLOSION }, /* explosion debris */ + { 0x4C514, STOP_AT_EXPLOSION }, { 0x0, STOP_AT_END }, /* end */ }; @@ -190,7 +196,7 @@ void mercenary_get_orientation_raw(int16_t *pitch, uint16_t *yaw) *yaw = m68k_read_memory_16(0x007AA2); } -void mercenary_get_orientation_planet(double *inclination, double *rotation) +void mercenary_get_orientation_planet(double *inclination, double *azimuth) { int16_t r; @@ -198,7 +204,22 @@ void mercenary_get_orientation_planet(double *inclination, double *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; + *azimuth = -(double)r / 1024.0 * 2 * M_PI; +} + +void mercenary_get_location(int32_t *east, int32_t *height, int32_t *north) +{ + *east = (int32_t)m68k_read_memory_32(0x7a92); + *height = (int32_t)m68k_read_memory_32(0x7a30); + *north = (int32_t)m68k_read_memory_32(0x7a9a); +} + +void mercenary_get_object_info(int *id, int32_t *east, int32_t *height, int32_t *north) +{ + *id = REG_A[0]; + *east = (int32_t)m68k_read_memory_32(REG_A[0] + 25512); + *height = (int32_t)m68k_read_memory_32(REG_A[0] + 26536); + *north = (int32_t)m68k_read_memory_32(REG_A[0] + 27560); } void mercenary_coord_building_interior(int16_t *east, int32_t *height1, int32_t *height2, int32_t *height3, int32_t *height4, int16_t *north) @@ -211,11 +232,6 @@ void mercenary_coord_building_interior(int16_t *east, int32_t *height1, int32_t *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; diff --git a/src/mercenary/mercenary3.c b/src/mercenary/mercenary3.c index c652c08..c2ca7f1 100644 --- a/src/mercenary/mercenary3.c +++ b/src/mercenary/mercenary3.c @@ -39,10 +39,10 @@ /* 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 */ + { 0x55d8c, STOP_AT_WAIT_INPUT }, /* after pressing 'HELP' key before showing menu line on benson */ + { 0x56398, STOP_AT_WAIT_INPUT }, /* waiting for menu command */ + { 0x55d94, STOP_AT_WAIT_INPUT }, /* after pressing 'HELP' key while showing menu line on benson */ + { 0x563a6, STOP_AT_WAIT_INPUT }, /* 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 }, @@ -52,6 +52,11 @@ const struct cpu_stop mercenary_stop_at[] = { { 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 */ + { 0x53FE2, STOP_AT_INFO_OBJECT_MOVING }, /* get ID and position of next object */ + { 0x53E42, STOP_AT_INFO_OBJECT_FIX }, /* next object is FIX (taxi/bus/intercity) */ + { 0x53E8E, STOP_AT_INFO_OBJECT_FIX }, + { 0x54330, STOP_AT_TAG_IS_OBJECT_1 }, /* indicates that the next tag is an object */ + { 0x54334, STOP_AT_TAG_IS_OBJECT_0 }, /* indicates that the tag of object was rendered */ { 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 */ @@ -88,9 +93,9 @@ const struct cpu_stop mercenary_stop_at[] = { { 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 */ + { 0x53A44, STOP_AT_MATRIX_PLANET }, /* 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 */ + { 0x537A4, STOP_AT_MATRIX_PLANET }, /* 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 */ @@ -103,8 +108,8 @@ const struct cpu_stop mercenary_stop_at[] = { { 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 }, + { 0x54362, STOP_AT_EXPLOSION }, /* explosion debris */ + { 0x4CA18, STOP_AT_EXPLOSION }, { 0x0, STOP_AT_END }, /* end */ }; @@ -228,7 +233,7 @@ void mercenary_get_orientation_raw(int16_t *pitch, uint16_t *yaw) *yaw = m68k_read_memory_16(DS_0+0x1E2a); } -void mercenary_get_orientation_planet(double *inclination, double *rotation) +void mercenary_get_orientation_planet(double *inclination, double *azimuth) { int16_t r; @@ -236,7 +241,22 @@ void mercenary_get_orientation_planet(double *inclination, double *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; + *azimuth = -(double)r / 1024.0 * 2 * M_PI; +} + +void mercenary_get_location(int32_t *east, int32_t *height, int32_t *north) +{ + *east = (int32_t)m68k_read_memory_32(DS_0+0x1E1A); + *height = (int32_t)m68k_read_memory_32(DS_0+0x1DBA); + *north = (int32_t)m68k_read_memory_32(DS_0+0x1E22); +} + +void mercenary_get_object_info(int *id, int32_t *east, int32_t *height, int32_t *north) +{ + *id = REG_A[0]; + *east = (int32_t)m68k_read_memory_32(REG_A[0] + 22556); + *height = (int32_t)m68k_read_memory_32(REG_A[0] + 23580); + *north = (int32_t)m68k_read_memory_32(REG_A[0] + 24604); } void mercenary_coord_building_interior(int16_t *east, int32_t *height1, int32_t *height2, int32_t *height3, int32_t *height4, int16_t *north) @@ -250,11 +270,6 @@ void mercenary_coord_building_interior(int16_t *east, int32_t *height1, int32_t } -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; diff --git a/src/mercenary/render.c b/src/mercenary/render.c index 7c0bc1f..9fbf45f 100644 --- a/src/mercenary/render.c +++ b/src/mercenary/render.c @@ -17,6 +17,22 @@ * along with this program. If not, see . */ +/* How it works: + * + * 1. Each game's rendering is capured: + * a: call to render_capture_start() + * b: several calls to render_capture_event() to capture all data + * c: call to render_capture_stop() + * 2. The captured motion is then interpolated to get smooth motion: + * - new_motion for motion of current/recent capture + * - old_motion for motion of previous capture + * - interpolation for interpolated result + * 3. The complete scene is rendered: + * - render_all_items() calls the render_item() for each item + * - The recent capture (NEW) is rendered + * - Interpolation result is taken into account + */ + #include #include #include @@ -32,26 +48,26 @@ //#define DEBUG_COLOR //#define DEBUG_VERTEX -#define DEBUG_ITEMS +//#define DEBUG_ITEMS #define MAX_POLYGON 16 /* number of polygon complexity (vertices) */ #define MAX_VERTEX 0x100 /* this is the value range, these are 64 vertices */ #define MAX_INTERIOR_VERTEX 0x400 /* do we need that much? */ #define MAX_INTERSTARS 80 /* always 80 stars */ +#define MAX_MOVING_OBJECTS 16 /* maximum number of moving objects (used for interpolation) */ +#define MAX_EXPLOSION 256 /* how many explosion particles can be stored in one object */ #define PLANET_VERTICES 128 #define PLANET_ELIPSE 1.17 +#define EXPLOSION_VERTICES 16 +#define EXPLOSION_ELIPSE 1.17 +/* + * render item definition and structures + */ -static int extend_roads; /* extend roads in its original width, rather than just using a single point */ -static double fov; -static double debug_opacity; -static double frustum_slope_64, frustum_slope_fov; -static double orientation_roll, orientation_pitch, orientation_yaw; -static uint16_t orientation_raw_yaw; -static int16_t orientation_raw_pitch; -static double planet_inclination, planet_azimuth; - +/* render items */ enum render_item_type { + RENDER_ITEM_OBJECT_INFO, RENDER_ITEM_VERTICES_0, RENDER_ITEM_VERTICES_1, RENDER_ITEM_VERTICES_2, @@ -69,14 +85,23 @@ enum render_item_type { RENDER_ITEM_COMET_POLYGON, RENDER_ITEM_ROAD_LINE, RENDER_ITEM_ROAD_POLYGON, - RENDER_ITEM_TAG_LINE, - RENDER_ITEM_TAG_POLYGON, + RENDER_ITEM_TAG_LINE_OBJECT, + RENDER_ITEM_TAG_LINE_OTHER, + RENDER_ITEM_TAG_POLYGON_OBJECT, + RENDER_ITEM_TAG_POLYGON_OTHER, RENDER_ITEM_PLANET, RENDER_ITEM_STARS, RENDER_ITEM_INTERSTELLAR_STARS, RENDER_ITEM_INTERSTELLAR_SUN, RENDER_ITEM_ISLAND_POLYGON, RENDER_ITEM_SIGHTS, + RENDER_ITEM_EXPLOSION, +}; + +struct render_item_info { + int moving; + int id; + int32_t east, height, north; }; struct render_item_vertices { @@ -84,7 +109,8 @@ struct render_item_vertices { }; struct render_item_vertices_interior { - int32_t x[MAX_INTERIOR_VERTEX >> 2], y[4], z[MAX_INTERIOR_VERTEX >> 2]; + uint8_t set[MAX_INTERIOR_VERTEX >> 2]; + double x[MAX_INTERIOR_VERTEX >> 2], y[4], z[MAX_INTERIOR_VERTEX >> 2]; }; struct render_item_sky { @@ -145,10 +171,17 @@ struct render_item_interstars { int count; }; +struct render_item_explosion { + double red, green, blue; + int32_t x[MAX_EXPLOSION], y[MAX_EXPLOSION], z[MAX_EXPLOSION]; + int count; +}; + typedef struct render_item { struct render_item *next; enum render_item_type type; union { + struct render_item_info info; struct render_item_vertices vertices; struct render_item_vertices_interior vertices_interior; struct render_item_sky sky; @@ -161,15 +194,72 @@ typedef struct render_item { struct render_item_planet planet; struct render_item_stars stars; struct render_item_interstars interstars; + struct render_item_explosion explosion; } u; } render_item_t; -static render_item_t *render_list_start = NULL, **render_list_end = &render_list_start; +/* information about motion in each game rendering */ +typedef struct motion { + int32_t position_east, position_height, position_north; + double orientation_roll, orientation_pitch, orientation_yaw; + uint16_t orientation_raw_yaw; + int16_t orientation_raw_pitch; + int planet_rotation; + double planet_inclination, planet_azimuth; +} motion_t; + +/* information about interpolation between two game renedrings */ +typedef struct interpolation { + double offset_east, offset_height, offset_north; + double orientation_roll, orientation_pitch, orientation_yaw; + double orientation_raw_yaw, orientation_raw_pitch; + int object_id[MAX_MOVING_OBJECTS]; + double object_offset_east[MAX_MOVING_OBJECTS], object_offset_height[MAX_MOVING_OBJECTS], object_offset_north[MAX_MOVING_OBJECTS]; + int object_count; + render_item_t *interior; +} interpolation_t; + +#define GET_ORIENTATION \ + double roll = interpolation.orientation_roll; \ + double pitch = interpolation.orientation_pitch; \ + double yaw = interpolation.orientation_yaw + +#define GET_ORIENTATION_FIX \ + roll = motion_new.orientation_roll; \ + pitch = motion_new.orientation_pitch; \ + yaw = motion_new.orientation_yaw + +/* rendering options */ +static int extend_roads; /* extend roads in its original width, rather than just using a single point */ +static double fov; +static double debug_opacity; +static double frustum_slope_64, frustum_slope_fov; + +/* states while collecting render items */ +static motion_t motion_old, motion_new; +static int32_t old_height_offset = 0, new_height_offset = 0; +static interpolation_t interpolation; +static int ground_index, last_ground_index = -1; +static int interior_level12 = 0; +static int interior_level34 = 0; +static int tag_is_object; +/* current render item list */ +static render_item_t *render_list_new = NULL, **render_list_end = &render_list_new; +/* previous render item list */ +static render_item_t *render_list_old = NULL; +/* current item to be processed */ static render_item_t *render_item; +/* current object info */ +static render_item_t *render_item_object_info; +/* current vertices */ static render_item_t *render_item_vertices_0, *render_item_vertices_1, *render_item_vertices_2; static render_item_t *render_item_vertices_interior; -void render_item_add(enum render_item_type type) +/* + * capturing + */ + +static void render_item_add(enum render_item_type type) { render_item = calloc(1, sizeof(render_item_t)); if (!render_item) { @@ -181,6 +271,16 @@ void render_item_add(enum render_item_type type) render_list_end = &render_item->next; } +static void flush_old_items(void) +{ + /* flush old render list */ + while (render_list_old) { + render_item = render_list_old; + render_list_old = render_list_old->next; + free(render_item); + } +} + /* rendering starts, initialize variables */ void render_capture_start(double _fov, int _extend_roads, int debug) { @@ -188,14 +288,16 @@ void render_capture_start(double _fov, int _extend_roads, int debug) printf("start rendering a new frame...\n"); #endif - /* flush render list */ - while (render_list_start) { - render_item = render_list_start; - render_list_start = render_list_start->next; - free(render_item); - } - render_list_end = &render_list_start; + flush_old_items(); render_item = NULL; + /* move new render list to old render list */ + render_list_old = render_list_new; + /* setup new render list */ + render_list_new = NULL; + render_list_end = &render_list_new; + + /* move new motion to old motion */ + memcpy(&motion_old, &motion_new, sizeof(motion_old)); /* set rendering options */ fov = _fov; @@ -207,13 +309,28 @@ void render_capture_start(double _fov, int _extend_roads, int debug) frustum_slope_64 = tan(64.0 / 2.0 / 180.0 * M_PI); frustum_slope_fov = tan(fov / 2.0 / 180.0 * M_PI); - /* get orientation */ - mercenary_get_orientation(&orientation_roll, &orientation_pitch, &orientation_yaw); - mercenary_get_orientation_raw(&orientation_raw_pitch, &orientation_raw_yaw); - mercenary_get_orientation_planet(&planet_inclination, &planet_azimuth); + /* init motion */ + mercenary_get_location(&motion_new.position_east, &motion_new.position_height, &motion_new.position_north); + /* in case of player on the ground, there is no roll, so it will be reset to 0 when screen is cleared */ + mercenary_get_orientation(&motion_new.orientation_roll, &motion_new.orientation_pitch, &motion_new.orientation_yaw); + mercenary_get_orientation_raw(&motion_new.orientation_raw_pitch, &motion_new.orientation_raw_yaw); + motion_new.planet_rotation = 0; + mercenary_get_orientation_planet(&motion_new.planet_inclination, &motion_new.planet_azimuth); + render_item_object_info = NULL; render_item_vertices_0 = render_item_vertices_1 = render_item_vertices_2 = NULL; render_item_vertices_interior = NULL; + + /* detect elevator movement */ + old_height_offset = new_height_offset; + if (render_list_old) + new_height_offset = motion_new.position_height - motion_old.position_height; + + /* detect switching between space (-1) and over ground (>=0) */ + last_ground_index = ground_index; + + /* init tag type */ + tag_is_object = 0; } void render_capture_stop(void) @@ -282,6 +399,13 @@ again: *blue = (double)(color & 0xf) / 15.0; } +static int32_t wrap_int28(int32_t value) +{ + value <<= 4; + value >>= 4; + return value; +} + static void store_coord(const char __attribute__((unused)) *what, uint32_t vertex, int32_t x, int32_t y, int32_t z) { if ((vertex & 3)) { @@ -325,6 +449,10 @@ static void store_coord(const char __attribute__((unused)) *what, uint32_t verte #ifdef DEBUG_VERTEX printf("storing %s coordinates: vertex=%d, x=%.0f, y=%.0f, z=%.0f\n", what, vertex, x, y, z); #endif + /* use absolute position */ + x += motion_new.position_east; + y += motion_new.position_height; + z += motion_new.position_north; render_item->u.vertices.x[vertex] = x; render_item->u.vertices.y[vertex] = y; render_item->u.vertices.z[vertex] = z; @@ -346,16 +474,22 @@ static void store_interior_coord(const char __attribute__((unused)) *what, uint3 #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 - render_item->u.vertices_interior.x[vertex] = x; - render_item->u.vertices_interior.y[0] = y1; - render_item->u.vertices_interior.y[1] = y2; - render_item->u.vertices_interior.y[2] = y3; - render_item->u.vertices_interior.y[3] = y4; - render_item->u.vertices_interior.z[vertex] = z; + /* use absolute position */ + x += motion_new.position_east; + y1 += motion_new.position_height; + y2 += motion_new.position_height; + y3 += motion_new.position_height; + y4 += motion_new.position_height; + z += motion_new.position_north; + render_item->u.vertices_interior.x[vertex] = (double)x; + render_item->u.vertices_interior.y[0] = (double)y1; + render_item->u.vertices_interior.y[1] = (double)y2; + render_item->u.vertices_interior.y[2] = (double)y3; + render_item->u.vertices_interior.y[3] = (double)y4; + render_item->u.vertices_interior.z[vertex] = (double)z; + render_item->u.vertices_interior.set[vertex] = 1; } -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; @@ -379,8 +513,6 @@ static void rotate_coordinate(double roll, double pitch, double yaw, double *x, *y = out_y; } -static int ground_index; - /* clear screen color (sky / universe) */ static void clear_screen(int index) { @@ -416,6 +548,22 @@ static void draw_ground(void) gamecolor2gl_index(&render_item->u.ground.red, &render_item->u.ground.green, &render_item->u.ground.blue, ground_index); } +/* object info */ +static void info_object(int moving) +{ +#ifdef DEBUG_VERTEX + printf("add object's info:\n"); +#endif + + /* allocate render item */ + render_item_add(RENDER_ITEM_OBJECT_INFO); + + /* add info */ + render_item->u.info.moving = moving; + if (moving) + mercenary_get_object_info(&render_item->u.info.id, &render_item->u.info.east, &render_item->u.info.height, &render_item->u.info.north); +} + /* coordinates ready for an object */ static void coord_object(void) { @@ -493,9 +641,9 @@ static void coord_beacon(void) int32_t x, y, z; /* only 28 bits seem to be a correct signed int value */ - x = ((int32_t)REG_D[3] << 4) / 16; - y = ((int32_t)REG_D[4] << 4) / 16; - z = ((int32_t)REG_D[5] << 4) / 16; + x = (int32_t)(REG_D[3] << 4) / 16; + y = (int32_t)(REG_D[4] << 4) / 16; + z = (int32_t)(REG_D[5] << 4) / 16; store_coord("beacon", 0, x, y, z); } @@ -580,9 +728,6 @@ static void line_building_exterior(void) render_item->u.line.vertex[1] = vertex | 0x100; } -static int interior_level12 = 0; -static int interior_level34 = 0; - /* coordinates ready for a building (interior) */ static void coord_building_interior(void) { @@ -712,8 +857,7 @@ static void coord_line_road(void) int32_t x, y, z; x = REG_D[3]; - mercenary_get_height(&y); - y = -y; + y = -motion_new.position_height; z = REG_D[5]; store_coord("road", REG_A[0], x, y, z); } @@ -745,12 +889,10 @@ 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; + y = -motion_new.position_height; z = m68k_read_memory_32(576 + REG_A[0]); z -= REG_A[3]; store_coord("road/place", REG_A[0], x, y, z); @@ -825,7 +967,7 @@ static void line_tags(int last_color) #endif /* allocate render item */ - render_item_add(RENDER_ITEM_TAG_LINE); + render_item_add((tag_is_object) ? RENDER_ITEM_TAG_LINE_OBJECT : RENDER_ITEM_TAG_LINE_OTHER); /* set color */ if (!last_color) @@ -852,7 +994,7 @@ static void poly_tags(int last_color) #endif /* allocate render item */ - render_item_add(RENDER_ITEM_TAG_POLYGON); + render_item_add((tag_is_object) ? RENDER_ITEM_TAG_POLYGON_OBJECT : RENDER_ITEM_TAG_POLYGON_OTHER); /* set color */ if (!last_color) @@ -999,8 +1141,7 @@ static void coord_islands(void) 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; + y = -motion_new.position_height; z = ((int16_t)m68k_read_memory_16(REG_A[4]) * 65536); z += (int32_t)REG_A[3]; store_coord("island", REG_A[0], x, y, z); @@ -1052,12 +1193,38 @@ static void draw_sights(void) render_item_add(RENDER_ITEM_SIGHTS); } +static void draw_explosion(void) +{ + uint16_t color; + +#ifdef DEBUG_VERTEX + printf("add explosion:\n"); +#endif + + /* allocate render item */ + if (!render_item || render_item->type != RENDER_ITEM_EXPLOSION) { + render_item_add(RENDER_ITEM_EXPLOSION); + /* get color from render palette */ + color = (m68k_read_memory_16(mercenary_palette_view() + 13*2) & 0xfff) >> 1; + gamecolor2gl(&render_item->u.explosion.red, &render_item->u.explosion.green, &render_item->u.explosion.blue, color); + render_item->u.explosion.count = 0; + } + if (render_item->u.explosion.count == MAX_EXPLOSION) + return; + render_item->u.explosion.x[render_item->u.explosion.count] = REG_D[3]; + render_item->u.explosion.y[render_item->u.explosion.count] = REG_D[4]; + render_item->u.explosion.z[render_item->u.explosion.count] = REG_D[5]; + render_item->u.explosion.count++; +} + /* stop event from CPU received */ void render_capture_event(int event) { switch (event) { case STOP_AT_CLEAR_SCREEN1: clear_screen(16); /* color 16 is raster split */ + /* in case of screen clearing on the ground, there is no roll */ + motion_new.orientation_roll = 0; break; case STOP_AT_CLEAR_SCREEN2: clear_screen(15); @@ -1068,6 +1235,18 @@ void render_capture_event(int event) case STOP_AT_DRAW_GROUND: draw_ground(); break; + case STOP_AT_INFO_OBJECT_MOVING: + info_object(1); + break; + case STOP_AT_INFO_OBJECT_FIX: + info_object(0); + break; + case STOP_AT_TAG_IS_OBJECT_1: + tag_is_object = 1; + break; + case STOP_AT_TAG_IS_OBJECT_0: + tag_is_object = 0; + break; case STOP_AT_COORD_OBJECT: coord_object(); break; @@ -1158,9 +1337,9 @@ void render_capture_event(int event) * 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; + motion_new.planet_rotation = 1; else - planet_rotation = 0; + motion_new.planet_rotation = 0; break; case STOP_AT_POLY_COMET: poly_comet(); @@ -1247,16 +1426,17 @@ void render_capture_event(int event) 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"); + case STOP_AT_EXPLOSION: + draw_explosion(); break; } } -static int use_coord(const char __attribute__((unused)) *what, uint32_t vertex, double *x, double *y, double *z) +/* + * rendering + */ + +static int use_coord(const char __attribute__((unused)) *what, uint32_t vertex, double *x, double *y, double *z, int fix) { render_item_t *ri = NULL; @@ -1291,9 +1471,16 @@ static int use_coord(const char __attribute__((unused)) *what, uint32_t vertex, return -1; } vertex >>= 2; - *x = ri->u.vertices.x[vertex]; - *y = ri->u.vertices.y[vertex]; - *z = ri->u.vertices.z[vertex]; + /* translate to original position */ + *x = ri->u.vertices.x[vertex] - motion_new.position_east; + *y = ri->u.vertices.y[vertex] - motion_new.position_height; + *z = ri->u.vertices.z[vertex] - motion_new.position_north; + if (!fix) { + /* translate to floating (interpolated) position offset */ + *x -= interpolation.offset_east; + *y -= interpolation.offset_height; + *z -= interpolation.offset_north; + } #ifdef DEBUG_VERTEX printf("using %s coordinates: vertex=%d, x=%.0f, y=%.0f, z=%.0f\n", what, vertex, *x, *y, *z); #endif @@ -1307,7 +1494,7 @@ static int use_interior_coord(const char __attribute__((unused)) *what, uint32_t print_error("Vertex is not a multiple of four!\n"); return -1; } - if (vertex >= MAX_VERTEX) { + if (vertex >= MAX_INTERIOR_VERTEX) { print_error("Vertex %d exceeds maximum vertex number %d!\n", vertex, MAX_VERTEX); return -1; } @@ -1323,6 +1510,15 @@ static int use_interior_coord(const char __attribute__((unused)) *what, uint32_t *x = render_item_vertices_interior->u.vertices_interior.x[vertex]; *y = render_item_vertices_interior->u.vertices_interior.y[level - 1]; *z = render_item_vertices_interior->u.vertices_interior.z[vertex]; + /* translate to position back to original */ + *x -= motion_new.position_east; + *y -= motion_new.position_height; + *z -= motion_new.position_north; + /* translate to floating (interpolated) position offset */ + *x -= interpolation.offset_east; + *y -= interpolation.offset_height; + *z -= interpolation.offset_north; + #ifdef DEBUG_VERTEX printf("using %s coordinates: vertex=%d, x=%.0f, y=%.0f, z=%.0f\n", what, vertex, *x, *y, *z); #endif @@ -1334,6 +1530,14 @@ static int use_interior_coord(const char __attribute__((unused)) *what, uint32_t void render_one_item(render_item_t *render_item) { switch (render_item->type) { + case RENDER_ITEM_OBJECT_INFO: + { +#ifdef DEBUG_ITEMS + printf("RENDER_ITEM_OBJECT_INFO object-id=%d\n", render_item->u.info.id); +#endif + render_item_object_info = render_item; + break; + } case RENDER_ITEM_VERTICES_0: { #ifdef DEBUG_ITEMS @@ -1363,7 +1567,10 @@ void render_one_item(render_item_t *render_item) #ifdef DEBUG_ITEMS printf("RENDER_ITEM_VERTICES_INTERIOR\n"); #endif - render_item_vertices_interior = render_item; + if (interpolation.interior) + render_item_vertices_interior = interpolation.interior; + else + render_item_vertices_interior = render_item; break; } case RENDER_ITEM_SKY: @@ -1385,7 +1592,7 @@ void render_one_item(render_item_t *render_item) } case RENDER_ITEM_GROUND: { - double roll = orientation_roll, pitch = orientation_pitch, yaw = orientation_yaw; + GET_ORIENTATION; double x[4], y[4], z[4]; int i; @@ -1407,30 +1614,53 @@ void render_one_item(render_item_t *render_item) break; } case RENDER_ITEM_OBJECT_POLYGON: - case RENDER_ITEM_TAG_POLYGON: + case RENDER_ITEM_TAG_POLYGON_OBJECT: + case RENDER_ITEM_TAG_POLYGON_OTHER: case RENDER_ITEM_ISLAND_POLYGON: { - double roll = orientation_roll, pitch = orientation_pitch, yaw = orientation_yaw; + GET_ORIENTATION; + int fix = 0; double x[MAX_POLYGON], y[MAX_POLYGON], z[MAX_POLYGON]; - int i; + int i, o; int rc; #ifdef DEBUG_ITEMS if (render_item->type == RENDER_ITEM_OBJECT_POLYGON) printf("RENDER_ITEM_OBJECT_POLYGON\n"); - if (render_item->type == RENDER_ITEM_TAG_POLYGON) - printf("RENDER_ITEM_TAG_POLYGON\n"); + if (render_item->type == RENDER_ITEM_TAG_POLYGON_OBJECT) + printf("RENDER_ITEM_TAG_POLYGON_OBJECT\n"); + if (render_item->type == RENDER_ITEM_TAG_POLYGON_OTHER) + printf("RENDER_ITEM_TAG_POLYGON_OTHER\n"); if (render_item->type == RENDER_ITEM_ISLAND_POLYGON) printf("RENDER_ITEM_ISLAND_POLYGON\n"); #endif + + /* special case where we don't want to interpolate motion (taxi/busses/intercity) */ + if (render_item->type == RENDER_ITEM_OBJECT_POLYGON && render_item_object_info && !render_item_object_info->u.info.moving) { +// GET_ORIENTATION_FIX; + fix = 1; + } + /* get color */ opengl_render_color(render_item->u.polygon.red, render_item->u.polygon.green, render_item->u.polygon.blue, debug_opacity); /* get and rotate vertex */ for (i = 0; i < render_item->u.polygon.vertices; i++) { /* get vertex */ - rc = use_coord("object", render_item->u.polygon.vertex[i], &x[i], &y[i], &z[i]); + rc = use_coord("object", render_item->u.polygon.vertex[i], &x[i], &y[i], &z[i], fix); if (rc < 0) break; + /* interpolate motion, if object is moving */ + if ((render_item->type == RENDER_ITEM_OBJECT_POLYGON || render_item->type == RENDER_ITEM_TAG_POLYGON_OBJECT) && render_item_object_info && render_item_object_info->u.info.moving) { + for (o = 0; o < interpolation.object_count; o++) { + if (interpolation.object_id[o] == render_item_object_info->u.info.id) + break; + } + if (o < interpolation.object_count) { + x[i] += interpolation.object_offset_east[o]; + y[i] += interpolation.object_offset_height[o]; + z[i] += interpolation.object_offset_north[o]; + } + } /* rotate vertex */ rotate_coordinate(roll, pitch, yaw, &x[i], &y[i], &z[i]); } @@ -1439,9 +1669,11 @@ void render_one_item(render_item_t *render_item) break; } case RENDER_ITEM_OBJECT_LINE: - case RENDER_ITEM_TAG_LINE: + case RENDER_ITEM_TAG_LINE_OBJECT: + case RENDER_ITEM_TAG_LINE_OTHER: { - double roll = orientation_roll, pitch = orientation_pitch, yaw = orientation_yaw; + GET_ORIENTATION; + int fix = 0; double x[2], y[2], z[2]; int i; int rc; @@ -1449,15 +1681,24 @@ void render_one_item(render_item_t *render_item) #ifdef DEBUG_ITEMS if (render_item->type == RENDER_ITEM_OBJECT_LINE) printf("RENDER_ITEM_OBJECT_LINE\n"); - if (render_item->type == RENDER_ITEM_TAG_LINE) - printf("RENDER_ITEM_TAG_LINE\n"); + if (render_item->type == RENDER_ITEM_TAG_LINE_OBJECT) + printf("RENDER_ITEM_TAG_LINE_OBJECT\n"); + if (render_item->type == RENDER_ITEM_TAG_LINE_OTHER) + printf("RENDER_ITEM_TAG_LINE_OTHER\n"); #endif + + /* special case where we don't want to interpolate motion (taxi/busses/intercity) */ + if ((render_item->type == RENDER_ITEM_OBJECT_LINE || render_item->type == RENDER_ITEM_TAG_LINE_OBJECT) && render_item_object_info && !render_item_object_info->u.info.moving) { +// GET_ORIENTATION_FIX; + fix = 1; + } + /* get color */ opengl_render_color(render_item->u.line.red, render_item->u.line.green, render_item->u.line.blue, debug_opacity); /* get and rotate vertex */ for (i = 0; i < 2; i++) { /* get vertex */ - rc = use_coord("object", render_item->u.line.vertex[i], &x[i], &y[i], &z[i]); + rc = use_coord("object", render_item->u.line.vertex[i], &x[i], &y[i], &z[i], fix); if (rc < 0) break; /* rotate vertex */ @@ -1469,7 +1710,7 @@ void render_one_item(render_item_t *render_item) } case RENDER_ITEM_BEACON_POINT: { - double roll = orientation_roll, pitch = orientation_pitch, yaw = orientation_yaw; + GET_ORIENTATION; double x, y, z; int rc; @@ -1479,7 +1720,7 @@ void render_one_item(render_item_t *render_item) /* get color */ opengl_render_color(render_item->u.point.red, render_item->u.point.green, render_item->u.point.blue, debug_opacity); /* get vertex */ - rc = use_coord("beacon", render_item->u.point.vertex, &x, &y, &z); + rc = use_coord("beacon", render_item->u.point.vertex, &x, &y, &z, 0); if (rc < 0) break; /* rotate vertex */ @@ -1490,7 +1731,7 @@ void render_one_item(render_item_t *render_item) } case RENDER_ITEM_BUILDING_EXTERIOR_POLYGON: { - double roll = orientation_roll, pitch = orientation_pitch, yaw = orientation_yaw; + GET_ORIENTATION; double x[MAX_POLYGON], y[MAX_POLYGON], z[MAX_POLYGON]; int i; int rc; @@ -1503,7 +1744,7 @@ void render_one_item(render_item_t *render_item) /* get and rotate vertex */ for (i = 0; i < render_item->u.polygon.vertices; i++) { /* get vertex */ - rc = use_coord("building exterior", render_item->u.polygon.vertex[i], &x[i], &y[i], &z[i]); + rc = use_coord("building exterior", render_item->u.polygon.vertex[i], &x[i], &y[i], &z[i], 0); if (rc < 0) break; /* rotate vertex */ @@ -1515,7 +1756,7 @@ void render_one_item(render_item_t *render_item) } case RENDER_ITEM_BUILDING_EXTERIOR_LINE: { - double roll = orientation_roll, pitch = orientation_pitch, yaw = orientation_yaw; + GET_ORIENTATION; double x[2], y[2], z[2]; int i; int rc; @@ -1528,7 +1769,7 @@ void render_one_item(render_item_t *render_item) /* get and rotate vertex */ for (i = 0; i < 2; i++) { /* get vertex */ - rc = use_coord("building exterior", render_item->u.line.vertex[i], &x[i], &y[i], &z[i]); + rc = use_coord("building exterior", render_item->u.line.vertex[i], &x[i], &y[i], &z[i], 0); if (rc < 0) break; /* rotate vertex */ @@ -1540,7 +1781,7 @@ void render_one_item(render_item_t *render_item) } case RENDER_ITEM_BUILDING_INTERIOR_1TO4: { - double roll = orientation_roll, pitch = orientation_pitch, yaw = orientation_yaw; + GET_ORIENTATION; double x[4], y[4], z[4]; int i; int rc; @@ -1565,7 +1806,7 @@ void render_one_item(render_item_t *render_item) } case RENDER_ITEM_BUILDING_INTERIOR_5TO6: { - double roll = orientation_roll, pitch = orientation_pitch, yaw = orientation_yaw; + GET_ORIENTATION; double x[4], y[4], z[4]; int i; int vertex, level; @@ -1593,7 +1834,7 @@ void render_one_item(render_item_t *render_item) } case RENDER_ITEM_BUILDING_INTERIOR_WALL: { - double roll = orientation_roll, pitch = orientation_pitch, yaw = orientation_yaw; + GET_ORIENTATION; double x[4], y[4], z[4]; int i; int vertex, level; @@ -1638,8 +1879,8 @@ void render_one_item(render_item_t *render_item) } case RENDER_ITEM_COMET_POLYGON: { - double roll = orientation_roll, pitch = orientation_pitch, yaw = orientation_yaw; - double inclination = planet_inclination, azimuth = planet_azimuth; + GET_ORIENTATION; + double inclination = motion_new.planet_inclination, azimuth = motion_new.planet_azimuth; double x[MAX_POLYGON], y[MAX_POLYGON], z[MAX_POLYGON]; int i; int rc; @@ -1652,11 +1893,11 @@ void render_one_item(render_item_t *render_item) /* get and rotate vertex */ for (i = 0; i < render_item->u.polygon.vertices; i++) { /* get vertex */ - rc = use_coord("comet tail", render_item->u.polygon.vertex[i], &x[i], &y[i], &z[i]); + rc = use_coord("comet tail", render_item->u.polygon.vertex[i], &x[i], &y[i], &z[i], motion_new.planet_rotation); if (rc < 0) break; /* rotate vertex */ - if (planet_rotation) + if (motion_new.planet_rotation) rotate_coordinate(0.0, inclination, azimuth, &x[i], &y[i], &z[i]); rotate_coordinate(roll, pitch, yaw, &x[i], &y[i], &z[i]); } @@ -1666,7 +1907,7 @@ void render_one_item(render_item_t *render_item) } case RENDER_ITEM_ROAD_LINE: { - double roll = orientation_roll, pitch = orientation_pitch, yaw = orientation_yaw; + GET_ORIENTATION; double x[2], y[2], z[2]; int i; int rc; @@ -1679,7 +1920,7 @@ void render_one_item(render_item_t *render_item) /* get and rotate vertex */ for (i = 0; i < 2; i++) { /* get vertex */ - rc = use_coord("road", render_item->u.line.vertex[i], &x[i], &y[i], &z[i]); + rc = use_coord("road", render_item->u.line.vertex[i], &x[i], &y[i], &z[i], 0); if (rc < 0) break; /* rotate vertex */ @@ -1691,7 +1932,7 @@ void render_one_item(render_item_t *render_item) } case RENDER_ITEM_ROAD_POLYGON: { - double roll = orientation_roll, pitch = orientation_pitch, yaw = orientation_yaw; + GET_ORIENTATION; double x[MAX_POLYGON], y[MAX_POLYGON], z[MAX_POLYGON]; int i, v; uint32_t vertex, vertex_prev, vertex_next; @@ -1712,19 +1953,19 @@ void render_one_item(render_item_t *render_item) for (v = 0; v < vertices_num; v++) { /* get vertex */ vertex = render_item->u.polygon.vertex[v]; - rc = use_coord("road/place", vertex, &x_current, &y_current, &z_current); + rc = use_coord("road/place", vertex, &x_current, &y_current, &z_current, 0); 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 = render_item->u.polygon.vertex[(v + vertices_num - 1) % vertices_num]; - rc = use_coord("road/place", vertex_prev, &x_prev, &y_prev, &z_prev); + rc = use_coord("road/place", vertex_prev, &x_prev, &y_prev, &z_prev, 0); if (rc < 0) break; /* next vertex */ vertex_next = render_item->u.polygon.vertex[(v + 1) % vertices_num]; - rc = use_coord("road/place", vertex_next, &x_next, &y_next, &z_next); + rc = use_coord("road/place", vertex_next, &x_next, &y_next, &z_next, 0); if (rc < 0) break; /* extend vertices to end point position @@ -1767,8 +2008,8 @@ void render_one_item(render_item_t *render_item) } case RENDER_ITEM_PLANET: { - double roll = orientation_roll, pitch = orientation_pitch, yaw = orientation_yaw; - double inclination = planet_inclination, azimuth = planet_azimuth; + GET_ORIENTATION; + double inclination = motion_new.planet_inclination, azimuth = motion_new.planet_azimuth; 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]; @@ -1780,16 +2021,16 @@ void render_one_item(render_item_t *render_item) printf("RENDER_ITEM_PLANET\n"); #endif /* get location */ - rc = use_coord("planet(sun)", 0, &sun_x, &sun_y, &sun_z); + rc = use_coord("planet(sun)", 0, &sun_x, &sun_y, &sun_z, motion_new.planet_rotation); if (rc < 0) break; - rc = use_coord("planet", render_item->u.planet.vertex, &loc_x, &loc_y, &loc_z); + rc = use_coord("planet", render_item->u.planet.vertex, &loc_x, &loc_y, &loc_z, motion_new.planet_rotation); if (rc < 0) break; /* get size */ size = render_item->u.planet.size; /* rotate vertex */ - if (planet_rotation) { + if (motion_new.planet_rotation) { rotate_coordinate(0.0, inclination, azimuth, &sun_x, &sun_y, &sun_z); rotate_coordinate(0.0, inclination, azimuth, &loc_x, &loc_y, &loc_z); } @@ -1874,13 +2115,13 @@ void render_one_item(render_item_t *render_item) } case RENDER_ITEM_STARS: { - int16_t tilt_offset = 0; - int x_offset = 0; + double tilt_offset = 0; + double x_offset = 0; uint16_t color[16]; - uint16_t view_width, yaw = orientation_raw_yaw; - int16_t pitch = orientation_raw_pitch; + double view_width, yaw = interpolation.orientation_raw_yaw; + double pitch = interpolation.orientation_raw_pitch; uint32_t table, table_start; - int16_t x, y; + double x, y; double z; int i; double red, green, blue; @@ -1904,48 +2145,48 @@ void render_one_item(render_item_t *render_item) if (render_item->u.stars.above_zenith) yaw = 0x200 + yaw; - yaw = (yaw + 91 - (int)(91.0 * (frustum_slope_fov / frustum_slope_64))) & 0x3ff; - yaw <<= 1; + yaw = fmod(yaw + 91.0 - (91.0 * (frustum_slope_fov / frustum_slope_64)) + 65536.0, 0x400); + yaw *= 2.0; 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; + table += m68k_read_memory_16(table + ((uint32_t)yaw & 0x7fe)); + yaw = yaw / (double)0x800 * 1800.0; if (render_item->u.stars.above_zenith) pitch = 0x200 - pitch; - pitch = pitch & 0x3ff; + pitch = fmod(pitch + 65536.0, 0x400); pitch -= render_item->u.stars.v_offset; - pitch <<= 2; - pitch = (pitch * 0x6ccc) >> 16; + pitch *= 4; + pitch = pitch * (double)0x6ccc / 65536.0; while (1) { x = m68k_read_memory_16(table); - if (x >= 1800) { - x_offset += 1800; + if (x >= 1800.0) { + x_offset += 1800.0; table = table_start; } x = (view_width - 1) - m68k_read_memory_16(table) - x_offset + yaw; table += 2; - if (x < 0) + if (x < 0.0) break; /* special case where we tilt the view when flying on the planet */ if (render_item->u.stars.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)) * render_item->u.stars.tilt_value) >> 16; + tilt_offset = ((x - (160.0 / frustum_slope_64 * frustum_slope_fov)) * render_item->u.stars.tilt_value) / 65536.0; } - y = ((m68k_read_memory_16(table)) & 0x1ff) - pitch + tilt_offset; + y = (double)((m68k_read_memory_16(table)) & 0x1ff) - pitch + tilt_offset; table += 2; if (render_item->u.stars.above_zenith) { - x = (view_width - 1) - x; - y = ~y + 136; + x = (double)(view_width - 1) - x; + y = -1 - y + 136; } /* get color */ gamecolor2gl(&red, &green, &blue, color[(m68k_read_memory_8(table - 2) & 0x3c) >> 2]); opengl_render_color(red, green, blue, debug_opacity); /* render point */ - opengl_render_point(160.0 / frustum_slope_64 * frustum_slope_fov - (double)x, 68.0 - (double)y, z, 0.0); + opengl_render_point(160.0 / frustum_slope_64 * frustum_slope_fov - x, 68.0 - y, z, 0.0); } break; } @@ -2013,18 +2254,302 @@ void render_one_item(render_item_t *render_item) opengl_render_polygon(x, y, z, 4, 0); /* no culling, because sights are always visible! */ break; } + case RENDER_ITEM_EXPLOSION: + { + GET_ORIENTATION; + double loc_x, loc_y, loc_z, size; + double x[EXPLOSION_VERTICES], y[EXPLOSION_VERTICES], z[EXPLOSION_VERTICES]; + int i, e; + +#ifdef DEBUG_ITEMS + printf("RENDER_ITEM_EXPLOSION\n"); +#endif + opengl_render_color(render_item->u.explosion.red, render_item->u.explosion.green, render_item->u.explosion.blue, debug_opacity); + + for (e = 0; e < render_item->u.explosion.count; e++) { + loc_x = render_item->u.explosion.x[e]; + loc_y = render_item->u.explosion.y[e]; + loc_z = render_item->u.explosion.z[e]; + size = 20; /* round about.... */ + /* rotate vertex */ + rotate_coordinate(roll, pitch, yaw, &loc_x, &loc_y, &loc_z); + /* create and render cicle */ + for (i = 0; i < EXPLOSION_VERTICES; i++) { + x[i] = loc_x + size * sin(2 * M_PI * (double)i / EXPLOSION_VERTICES) * EXPLOSION_ELIPSE; + y[i] = loc_y + size * cos(2 * M_PI * (double)i / EXPLOSION_VERTICES); + z[i] = loc_z; + } + opengl_render_polygon_and_line(x, y, z, EXPLOSION_VERTICES); /* no culling, its a debris! */ + opengl_render_point(loc_x, loc_y, loc_z, 0.0); /* debris is visible at any distance - at least as a point */ + } + break; + } default: print_error("Unknown type, please fix!\n"); } } -void render_all_items() +/* + * interpolation + */ + +static double interpolate_orientation(double old, double new, double inter) +{ + double turn = new - old; + + if (turn > M_PI) + turn -= 2.0 * M_PI; + if (turn < -M_PI) + turn += 2.0 * M_PI; + + /* don't interpolate, if our rotation was too fast. + * e.g: taxi drive around corder, load/quit game, ... + */ + if (turn > M_PI / 8.0 || turn < -M_PI / 8.0) + return new; + + new = old + turn * inter; + + if (new > M_PI) + new -= 2.0 * M_PI; + if (new < -M_PI) + new += 2.0 * M_PI; + + return new; +} + +static double interpolate_raw_orientation(uint16_t old, uint16_t new, double inter) +{ + int16_t turn = (new - old) & 0x3ff; + + if (turn > 0x200) + turn -= 0x400; + + /* don't interpolate, if our rotation was too fast. + * e.g: taxi drive around corder, load/quit game, ... + */ + if (turn > 0x200 / 8 || turn < -0x200 / 8) + return new; + + /* don't do modulo 0x400, since the user of this data does it */ + return (double)old + (double)turn * inter; +} + +static double interpolate_offset(int32_t old, int32_t new, double inter, int32_t limit) +{ + double offset; + + /* be sure to look only at the lower 28 bits, because on planet these bits are used only */ + if (ground_index >= 0) + offset = wrap_int28(old - new); + else + offset = (int32_t)(old - new); + + if (limit > 0 && (offset > limit || offset < -limit)) + return new; + + return offset * (1.0 - inter); +} + +static render_item_t *interpolate_door(double inter) +{ + static render_item_t interpolated; + render_item_t *old_vertices = render_list_old, *new_vertices = render_list_new; + int nomatch_x_count = 0; + int nomatch_z_count = 0; + int nomatch_x[4], nomatch_z[4]; + int i, ii; + + /* find old and new vertices */ + while (old_vertices && old_vertices->type != RENDER_ITEM_VERTICES_INTERIOR) + old_vertices = old_vertices->next; + while (new_vertices && new_vertices->type != RENDER_ITEM_VERTICES_INTERIOR) + new_vertices = new_vertices->next; + + /* building does not exist in old or new render */ + if (!old_vertices || !new_vertices) + return NULL; + + /* all verices must match except four */ + ii = MAX_INTERIOR_VERTEX >> 2; + for (i = 0; i < ii; i++) { + if (!old_vertices->u.vertices_interior.set[i] || !new_vertices->u.vertices_interior.set[i]) + continue; + if (old_vertices->u.vertices_interior.x[i] != new_vertices->u.vertices_interior.x[i]) { + if (nomatch_x_count == 4) + return NULL; + nomatch_x[nomatch_x_count++] = i; + } + } + for (i = 0; i < 4; i++) { + if (old_vertices->u.vertices_interior.y[i] != new_vertices->u.vertices_interior.y[i]) + return NULL; + } + for (i = 0; i < ii; i++) { + if (!old_vertices->u.vertices_interior.set[i] || !new_vertices->u.vertices_interior.set[i]) + continue; + if (old_vertices->u.vertices_interior.z[i] != new_vertices->u.vertices_interior.z[i]) { + if (nomatch_z_count == 4) + return NULL; + nomatch_z[nomatch_z_count++] = i; + } + } + + /* copy, even if not interpolated */ + memcpy(&interpolated, new_vertices, sizeof(interpolated)); + + /* only four x missmatch */ + if (nomatch_x_count == 4 || nomatch_x_count == 2) { + for (i = 0; i < nomatch_x_count; i++) { + interpolated.u.vertices_interior.x[nomatch_x[i]] = + (double)old_vertices->u.vertices_interior.x[nomatch_x[i]] * (1.0 - inter) + + (double)new_vertices->u.vertices_interior.x[nomatch_x[i]] * inter; + } + } + + /* only four z missmatch */ + if (nomatch_z_count == 4 || nomatch_z_count == 2) { + for (i = 0; i < nomatch_z_count; i++) { + interpolated.u.vertices_interior.z[nomatch_z[i]] = + (double)old_vertices->u.vertices_interior.z[nomatch_z[i]] * (1.0 - inter) + + (double)new_vertices->u.vertices_interior.z[nomatch_z[i]] * inter; + } + } + + return &interpolated; +} + +/* make a list of objects that moved and store their displacement */ +static void interpolate_objects(double inter) { + render_item_t *old_info, *new_info; + int count; + + /* hunt for objects that exist in both (old and new) lists and moved */ + count = 0; + for (new_info = render_list_new; new_info; new_info = new_info -> next) { + /* not an object */ + if (new_info->type != RENDER_ITEM_OBJECT_INFO) + continue; + /* not moving */ + if (!new_info->u.info.moving) + continue; + /* interiors don't move */ + if (new_info->u.info.id < 0) + continue; + /* check matching object with same ID */ + for (old_info = render_list_old; old_info; old_info = old_info -> next) { + /* not an object */ + if (old_info->type != RENDER_ITEM_OBJECT_INFO) + continue; + /* not moving */ + if (!old_info->u.info.moving) + continue; + /* same id */ + if (old_info->u.info.id == new_info->u.info.id) + break; + } + /* no matching object found */ + if (!old_info) + continue; + /* not moving */ + if (old_info->u.info.east == new_info->u.info.east + && old_info->u.info.height == new_info->u.info.height + && old_info->u.info.north == new_info->u.info.north) + continue; + /* interpolate and store */ + interpolation.object_id[count] = new_info->u.info.id; + interpolation.object_offset_east[count] = interpolate_offset(old_info->u.info.east, new_info->u.info.east, inter, 4096); + interpolation.object_offset_height[count] = interpolate_offset(old_info->u.info.height, new_info->u.info.height, inter, 4096); + interpolation.object_offset_north[count] = interpolate_offset(old_info->u.info.north, new_info->u.info.north, inter, 4096); + if (count++ == MAX_MOVING_OBJECTS) + break; + } + interpolation.object_count = count; +} + +/* always renders NEW! items + * use inter == 1.0 to render motion to vertices of NEW items + * use inter 0.0 .. 1.0 to interpolate motion between OLD and NEW items + */ +void render_all_items(double inter) +{ + render_item_object_info = NULL; render_item_vertices_0 = render_item_vertices_1 = render_item_vertices_2 = NULL; render_item_vertices_interior = NULL; - for (render_item = render_list_start; render_item; render_item = render_item->next) { + /* no interpolation when leaving or entering planet to/from space */ + if ((last_ground_index < 0 && ground_index >= 0) + || (last_ground_index >= 0 && ground_index < 0)) { + inter = 1.0; + } + + /* reset interpolation */ + memset(&interpolation, 0, sizeof(interpolation)); + interpolation.orientation_roll = motion_new.orientation_roll; + interpolation.orientation_pitch = motion_new.orientation_pitch; + interpolation.orientation_yaw = motion_new.orientation_yaw; + interpolation.orientation_raw_pitch = motion_new.orientation_raw_pitch; + interpolation.orientation_raw_yaw = motion_new.orientation_raw_yaw; +//printf("we are at %08x %08x %08x\n", motion_new.position_east, motion_new.position_height, motion_new.position_north); + + /* do interpolation */ + if (inter != 1.0 && render_list_old) { + /* interpolate orientation */ + interpolation.orientation_roll = interpolate_orientation(motion_old.orientation_roll, motion_new.orientation_roll, inter); + interpolation.orientation_pitch = interpolate_orientation(motion_old.orientation_pitch, motion_new.orientation_pitch, inter); + interpolation.orientation_yaw = interpolate_orientation(motion_old.orientation_yaw, motion_new.orientation_yaw, inter); + interpolation.orientation_raw_pitch = interpolate_raw_orientation(motion_old.orientation_raw_pitch, motion_new.orientation_raw_pitch, inter); + interpolation.orientation_raw_yaw = interpolate_raw_orientation(motion_old.orientation_raw_yaw, motion_new.orientation_raw_yaw, inter); + + /* interpolate position */ + interpolation.offset_east = interpolate_offset(motion_old.position_east, motion_new.position_east, inter, 0); + interpolation.offset_height = interpolate_offset(motion_old.position_height, motion_new.position_height, inter, 0); + interpolation.offset_north = interpolate_offset(motion_old.position_north, motion_new.position_north, inter, 0); + /* prevent glitch when using elevators: a sudden vertical move is ignored + * this is not the best solution, because fast vertical flying (from 0) will also be detected */ + if (old_height_offset == 0 + && (new_height_offset >= 150 || new_height_offset <= -150)) { + interpolation.offset_east = 0.0; + interpolation.offset_height = 0.0; + interpolation.offset_north = 0.0; + } + + /* interpolate doors of building (if any) */ + interpolation.interior = interpolate_door(inter); + + /* interpolate objects */ + interpolate_objects(inter); + } + + for (render_item = render_list_new; render_item; render_item = render_item->next) { render_one_item(render_item); } } +void render_capture_reset(void) +{ + /* flush old list, if exists */ + flush_old_items(); + /* flush new list, if exists */ + render_list_old = render_list_new; + flush_old_items(); + /* reset list pointers */ + render_list_old = NULL; + render_list_new = NULL; + render_list_end = &render_list_new; + render_item = NULL; +} + +int render_capture_is_interstellar(void) +{ + if (!render_list_new) + return 0; + for (render_item = render_list_new; render_item; render_item = render_item->next) { + if (render_item->type == RENDER_ITEM_INTERSTELLAR_STARS) + return 1; + } + + return 0; +} + diff --git a/src/mercenary/render.h b/src/mercenary/render.h index 67d9bc8..beeeebb 100644 --- a/src/mercenary/render.h +++ b/src/mercenary/render.h @@ -2,5 +2,7 @@ void render_capture_start(double _fov, int _extend_roads, int debug); void render_capture_stop(void); void render_capture_event(int event); -void render_all_items(); +void render_all_items(double inter); +void render_capture_reset(void); +int render_capture_is_interstellar(void); -- 2.13.6