* 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,
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 {
};
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 {
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;
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) {
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)
{
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;
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)
*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)) {
#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;
#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;
*y = out_y;
}
-static int ground_index;
-
/* clear screen color (sky / universe) */
static void clear_screen(int index)
{
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)
{
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);
}
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)
{
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);
}
{
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);
#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)
#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)
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);
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);
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;
* 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();
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;
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
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;
}
*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
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
#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:
}
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;
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]);
}
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;
#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 */
}
case RENDER_ITEM_BEACON_POINT:
{
- double roll = orientation_roll, pitch = orientation_pitch, yaw = orientation_yaw;
+ GET_ORIENTATION;
double x, y, z;
int rc;
/* 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 */
}
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;
/* 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 */
}
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;
/* 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 */
}
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;
}
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;
}
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;
}
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;
/* 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]);
}
}
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;
/* 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 */
}
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;
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
}
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];
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);
}
}
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;
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;
}
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;
+}
+