Motion interpolation
authorAndreas Eversberg <jolly@eversberg.eu>
Sat, 31 Mar 2018 10:35:53 +0000 (12:35 +0200)
committerAndreas Eversberg <jolly@eversberg.eu>
Thu, 5 Apr 2018 16:00:14 +0000 (18:00 +0200)
src/mercenary/main.c
src/mercenary/mercenary.h
src/mercenary/mercenary2.c
src/mercenary/mercenary3.c
src/mercenary/render.c
src/mercenary/render.h

index 11eceb1..cda13e1 100644 (file)
@@ -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 <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 <samples>\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) {
index 8715a2a..772064a 100644 (file)
@@ -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);
index db87556..1267c75 100644 (file)
 /* 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;
index c652c08..c2ca7f1 100644 (file)
 /* interrupt CPU execution at special break points and tell emulation what to do */
 const struct cpu_stop mercenary_stop_at[] = {
        { 0x5a826,      STOP_AT_WAIT_VBL },                     /* done with rendering, waiting for VBL */
-       { 0x55d8c,      STOP_AT_WAIT_VBL },                     /* after pressing 'HELP' key before showing menu line on benson */
-       { 0x56398,      STOP_AT_WAIT_VBL },                     /* waiting for menu command */
-       { 0x55d94,      STOP_AT_WAIT_VBL },                     /* after pressing 'HELP' key while showing menu line on benson */
-       { 0x563a6,      STOP_AT_WAIT_VBL },                     /* after pressing 'RETURN' while game waits for other key to resume */
+       { 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;
index 7c0bc1f..9fbf45f 100644 (file)
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+/* 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 <stdio.h>
 #include <stdint.h>
 #include <stdlib.h>
 
 //#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;
+}
+
index 67d9bc8..beeeebb 100644 (file)
@@ -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);