Add shadows and light effects
[mercenary-reloaded.git] / src / mercenary / render.c
index 26977ba..2ecfe8d 100644 (file)
  *    - interpolation for interpolated result
  * 3. The complete scene is rendered:
  *    - render_all_items() calls the render_item() for each item
+ *    - All items before shadows on the ground are rendered first
+ *    - Then all items with shadows are rendered
+ *    - Then all items after shadows are rendered
  *    - The recent capture (NEW) is rendered
  *    - Interpolation result is taken into account
+ *
  */
 
 #include <stdio.h>
 //#define DEBUG_COLOR
 //#define DEBUG_VERTEX
 //#define DEBUG_ITEMS
+//#define DEBUG_LIGHT
+
+#define ELEVATION_SKY_NIGHT    -0.05   /* elevation of darkest sky */
+#define ELEVATION_SKY_DAY      0.3     /* elevation of brightest sky */
+#define ELEVATION_STARS_NIGHT  -0.1    /* elevation of brightest stars */
+#define ELEVATION_STARS_DAY    0.1     /* elevation of no stars */
+#define ELEVATION_SHADOW_MIN   0.03    /* elevation of shadow begin */
+#define ELEVATION_SHADOW_MAX   0.3     /* elevation of darkest shadow */
+#define SHADOW_LEVEL           0.9     /* opacity of darkest shadow */
 
-#define MAX_POLYGON            16      /* number of polygon complexity (vertices) */
+#define MAX_POLYGON            16      /* number of polygon corners (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 FIX_OBJECT_SCALE       16      /* intercity... */
 #define FIX_OBJECT_SCALE_TAG   2       /* line in faces of persons */
 
+enum render_shadow {
+       RENDER_BEFORE_SHADOW,
+       RENDER_AT_SHADOW,
+       RENDER_AFTER_SHADOW,
+};
+
 /*
  *  render item definition and structures
  */
@@ -101,6 +120,10 @@ enum render_item_type {
        RENDER_ITEM_ISLAND_POLYGON,
        RENDER_ITEM_SIGHTS,
        RENDER_ITEM_EXPLOSION,
+       RENDER_ITEM_SHADOW_BUILDING_POLYGON,
+       RENDER_ITEM_SHADOW_BUILDING_LINE,
+       RENDER_ITEM_SHADOW_OBJECT_POLYGON,
+       RENDER_ITEM_SHADOW_OBJECT_LINE,
 };
 
 struct render_item_info {
@@ -183,6 +206,17 @@ struct render_item_explosion {
        int count;
 };
 
+struct render_item_shadow_polygon {
+       int32_t x_pos, y_pos, z_pos;
+       int vertices;
+       int32_t x[MAX_POLYGON], y[MAX_POLYGON], z[MAX_POLYGON];
+};
+
+struct render_item_shadow_line {
+       int32_t x_pos, y_pos, z_pos;
+       int32_t x[2], y[2], z[2];
+};
+
 typedef struct render_item {
        struct render_item *next;
        enum render_item_type type;
@@ -201,6 +235,8 @@ typedef struct render_item {
                struct render_item_stars                stars;
                struct render_item_interstars           interstars;
                struct render_item_explosion            explosion;
+               struct render_item_shadow_polygon       shadow_polygon;
+               struct render_item_shadow_line          shadow_line;
        } u;
 } render_item_t;
 
@@ -241,6 +277,7 @@ typedef struct interpolation {
 static int extend_roads; /* extend roads in its original width, rather than just using a single point */
 static int improve_stars; /* stars are rendered spherical */
 static int fix_sky_rotation; /* sky will rotate correctly by rotating planets/comet by 180 degrees */
+static int draw_shadows; /* render shadows */
 static double planet_aspect, explosion_aspect; /* aspect ratio */
 static double fov;
 static double debug_opacity;
@@ -266,6 +303,13 @@ static render_item_t *render_item_object_info;
 static render_item_t *render_item_vertices_0, *render_item_vertices_1, *render_item_vertices_2;
 static render_item_t *render_item_vertices_planets, *render_item_vertices_interior;
 
+/* states while rendering */
+int sun_valid = 0;
+double stars_bright; /* the brightness of stars */
+double shadow_level; /* how 'dark' the shadow is (that is darker at day) */
+double sun_pos_x, sun_pos_y, sun_pos_z;
+double sky_color_red, sky_color_green, sky_color_blue;
+
 /*
  * capturing
  */
@@ -293,7 +337,7 @@ static void flush_old_items(void)
 }
 
 /* rendering starts, initialize variables */
-void render_capture_start(double _fov, int _extend_roads, int _smooth_planets, int _improve_stars, int _fix_sky_rotation, int _round_planets, int debug)
+void render_capture_start(double _fov, int _extend_roads, int _smooth_planets, int _improve_stars, int _fix_sky_rotation, int _round_planets, int _shadows, int debug)
 {
 #if defined(DEBUG_COLOR) || defined(DEBUG_VERTEX)
        printf("start rendering a new frame...\n");
@@ -317,6 +361,7 @@ void render_capture_start(double _fov, int _extend_roads, int _smooth_planets, i
        improve_stars = _improve_stars;
        planet_aspect = (_round_planets) ? 1.0 : PLANET_STRETCH;
        explosion_aspect = (_round_planets) ? 1.0 : EXPLOSION_STRETCH;
+       draw_shadows = _shadows;
        /* set some transpareny, if debugging is enabled */
        debug_opacity = (debug) ? 0.5 : 1.0;
 
@@ -478,6 +523,8 @@ static void store_coord(const char __attribute__((unused)) *what, uint32_t verte
        render_item->u.vertices.z[vertex] = (double)z * scale + motion_new.position_north;
 }
 
+static int use_coord(const char __attribute__((unused)) *what, uint32_t vertex, double *x, double *y, double *z, int fix);
+
 static void store_planets_coord(const char __attribute__((unused)) *what, uint32_t vertex, int32_t x, int32_t y, int32_t z)
 {
        if ((vertex & 3)) {
@@ -613,6 +660,7 @@ static void info_object(int moving)
        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);
+       render_item_object_info = render_item;
 }
 
 /* coordinates ready for an object */
@@ -632,9 +680,10 @@ static void coord_object(void)
 /* polygon of object */
 static void poly_object(int mercenary)
 {
-       uint32_t vertex_address = REG_A[0];
+       uint32_t vertex_address = REG_A[0], va;
        uint32_t vertex;
-       int i;
+       double x, y, z;
+       int i, rc;
 
 #ifdef DEBUG_VERTEX
        printf("add object's polygon:\n");
@@ -654,20 +703,47 @@ static void poly_object(int mercenary)
        }
 
        /* the vertex list is zero-terminated */
+       va = vertex_address;
        for (i = 0; i < MAX_POLYGON; i++) {
-               vertex = m68k_read_memory_8(vertex_address++);
+               vertex = m68k_read_memory_8(va++);
                if (vertex == 0 && i)
                        break;
                render_item->u.polygon.vertex[i] = vertex;
        }
        render_item->u.polygon.vertices = i;
+
+       /* apply only to not fixed objects (a fixed may be taxi interior) */
+       if (render_item_object_info && render_item_object_info->u.info.moving) {
+               /* allocate shadow item */
+               render_item_add(RENDER_ITEM_SHADOW_OBJECT_POLYGON);
+               render_item->u.shadow_polygon.x_pos = -motion_new.position_east;
+               render_item->u.shadow_polygon.y_pos = -motion_new.position_height;
+               render_item->u.shadow_polygon.z_pos = -motion_new.position_north;
+
+               /* the vertex list is zero-terminated */
+               va = vertex_address;
+               for (i = 0; i < MAX_POLYGON; i++) {
+                       vertex = m68k_read_memory_8(va++);
+                       if (vertex == 0 && i)
+                               break;
+                       rc = use_coord("object", vertex, &x, &y, &z, 1);
+                       if (rc < 0)
+                               break;
+                       render_item->u.shadow_polygon.x[i] = x + motion_new.position_east;
+                       render_item->u.shadow_polygon.y[i] = y + motion_new.position_height;
+                       render_item->u.shadow_polygon.z[i] = z + motion_new.position_north;
+               }
+               render_item->u.shadow_polygon.vertices = i;
+       }
 }
 
 /* line of object */
 static void line_object(void)
 {
-       uint32_t vertex_address = REG_A[0];
+       uint32_t vertex_address = REG_A[0], va;
        uint32_t vertex;
+       double x, y, z;
+       int rc;
 
 #ifdef DEBUG_VERTEX
        printf("add object's line:\n");
@@ -680,10 +756,36 @@ static void line_object(void)
        gamecolor2gl(&render_item->u.line.red, &render_item->u.line.green, &render_item->u.line.blue, REG_D[0]);
 
        /* two vertices */
-       vertex = m68k_read_memory_8(vertex_address++);
+       va = vertex_address;
+       vertex = m68k_read_memory_8(va++);
        render_item->u.line.vertex[0] = vertex;
-       vertex = m68k_read_memory_8(vertex_address++);
+       vertex = m68k_read_memory_8(va++);
        render_item->u.line.vertex[1] = vertex;
+
+       /* allocate shadow item */
+       render_item_add(RENDER_ITEM_SHADOW_OBJECT_LINE);
+       render_item->u.shadow_line.x_pos = -motion_new.position_east;
+       render_item->u.shadow_line.y_pos = -motion_new.position_height;
+       render_item->u.shadow_line.z_pos = -motion_new.position_north;
+
+       /* apply only to not fixed objects (a fixed may be taxi interior) */
+       if (render_item_object_info && render_item_object_info->u.info.moving) {
+               va = vertex_address;
+               vertex = m68k_read_memory_8(va++);
+               rc = use_coord("object", vertex, &x, &y, &z, 1);
+               if (rc < 0)
+                       x=y=z=0;
+               render_item->u.shadow_line.x[0] = x + motion_new.position_east;
+               render_item->u.shadow_line.y[0] = y + motion_new.position_height;
+               render_item->u.shadow_line.z[0] = z + motion_new.position_north;
+               vertex = m68k_read_memory_8(va++);
+               rc = use_coord("object", vertex, &x, &y, &z, 1);
+               if (rc < 0)
+                       x=y=z=0;
+               render_item->u.shadow_line.x[1] = x + motion_new.position_east;
+               render_item->u.shadow_line.y[1] = y + motion_new.position_height;
+               render_item->u.shadow_line.z[1] = z + motion_new.position_north;
+       }
 }
 
 /* coordinates ready for a beacon */
@@ -1298,6 +1400,156 @@ static void draw_explosion(void)
        render_item->u.explosion.count++;
 }
 
+/* get building data out of game data, so we use it to render shadow */
+static void draw_shadow_building(void)
+{
+       int32_t scale_x, scale_y, scale_z;
+       int32_t x, y, z;
+       uint16_t anim_flag, anim_x, anim_y, anim_z, anim_phase;
+       uint32_t a0, a0s, a5, a6, a4;
+       uint16_t s;
+       int16_t d3, d4, d5;
+       int32_t d3x, d4x, d5x;
+       int d1index;
+       int line_from, line_to, point;
+       int i;
+       struct render_item_vertices vertex;
+
+       mercenary_get_building_exterior_info(&x, &z, &scale_x, &scale_y, &scale_z, &anim_flag, &anim_x, &anim_y, &anim_z, &anim_phase, &a4, &a0, &a5);
+       x -= motion_new.position_east;
+       y = -motion_new.position_height;
+       z -= motion_new.position_north;
+
+       /* list of all vetices */
+       for (i = 1; i < 64; i++) {
+               d3 = m68k_read_memory_16(a4);
+               a4 += 2;
+               if (d3 <= (int16_t)0x8001)
+                       break;
+               d4 = m68k_read_memory_16(a4);
+               a4 += 2;
+               d5 = m68k_read_memory_16(a4);
+               a4 += 2;
+               if (d4 < 0) {
+                       d4 = ~d4;
+                       /* scale */
+                       d3x = d3 * scale_x;
+                       d4x = d4 * scale_y;
+                       d5x = d5 * scale_z;
+                       /* angle */
+                       double angle = (double)anim_phase / 65536.0 * 2.0 * M_PI;
+                       /* animate around what axis ? */
+                       if ((int16_t)anim_flag > 0) {
+                               /* rotate around z */
+                               d3 = (d3x & 0xffff) - anim_x;
+                               d4 = (d4x & 0xffff) - anim_y;
+                               if (angle) {
+                                       double cosd4 = (double) d4 * cos(angle);
+                                       double sind3 = (double) d3 * sin(angle);
+                                       double cosd3 = (double) d3 * cos(angle);
+                                       double sind4 = (double) d4 * sin(angle);
+                                       d3 = cosd3 - sind4;
+                                       d4 = cosd4 + sind3;
+                               }
+                               d3x = d3 + anim_x;
+                               d4x = d4 + anim_y;
+                       }
+                       if ((int16_t)anim_flag == 0) {
+                               /* rotate around y */
+                               d3 = (d3x & 0xffff) - anim_x;
+                               d5 = (d5x & 0xffff) - anim_z;
+                               if (angle) {
+                                       double cosd3 = (double) d3 * cos(angle);
+                                       double sind5 = (double) d5 * sin(angle);
+                                       double cosd5 = (double) d5 * cos(angle);
+                                       double sind3 = (double) d3 * sin(angle);
+                                       d3 = cosd3 + sind5;
+                                       d5 = cosd5 - sind3;
+                               }
+                               d3x = d3 + anim_x;
+                               d5x = d5 + anim_z;
+                       }
+                       if ((int16_t)anim_flag < 0) {
+                               /* rotate around x */
+                               d4 = (d4x & 0xffff) - anim_y;
+                               d5 = (d5x & 0xffff) - anim_z;
+                               if (angle) {
+                                       double cosd5 = (double) d5 * cos(angle);
+                                       double sind4 = (double) d4 * sin(angle);
+                                       double cosd4 = (double) d4 * cos(angle);
+                                       double sind5 = (double) d5 * sin(angle);
+                                       d4 = cosd4 + sind5;
+                                       d5 = cosd5 - sind4;
+                               }
+                               d4x = d4 + anim_y;
+                               d5x = d5 + anim_z;
+                       }
+               } else {
+                       /* scale */
+                       d3x = d3 * scale_x;
+                       d4x = d4 * scale_y;
+                       d5x = d5 * scale_z;
+               }
+               vertex.x[i] = d3x;
+               vertex.y[i] = d4x;
+               vertex.z[i] = d5x;
+       }
+
+       /* get all data sets */
+       do {
+               s = m68k_read_memory_16(a0);
+               a0 += 2;
+               if (s) {
+                       /* skip over distance cube */
+                       a0 += 4 * 6;
+               }
+               /* get address pointer to data set */
+               a0s = m68k_read_memory_32(a0);
+               a0 += 4;
+
+               /* walk through primitives (polygons and lines) */
+               while ((d1index = m68k_read_memory_8(a0s++))) {
+                       /* pointer to primitive */
+                       a6 = m68k_read_memory_32(a5 + d1index*4);
+                       if ((m68k_read_memory_8(a6) & 0x40) == 0) {
+                               /* polygon */
+                               a6 += 2;
+#ifdef DEBUG_VERTEX
+                               printf("add object's shadow polygon:\n");
+#endif
+                               /* allocate render item */
+                               render_item_add(RENDER_ITEM_SHADOW_BUILDING_POLYGON);
+                               render_item->u.shadow_polygon.x_pos = x;
+                               render_item->u.shadow_polygon.y_pos = y;
+                               render_item->u.shadow_polygon.z_pos = z;
+                               i = 0;
+                               while ((point = m68k_read_memory_8(a6++) >> 2)) {
+                                       render_item->u.shadow_polygon.x[i] = vertex.x[point];
+                                       render_item->u.shadow_polygon.y[i] = vertex.y[point];
+                                       render_item->u.shadow_polygon.z[i] = vertex.z[point];
+                                       i++;
+                               }
+                               render_item->u.shadow_polygon.vertices = i;
+                       } else if ((m68k_read_memory_16(a6) & 0x2000) == 0) {
+                               /* line */
+                               line_from = m68k_read_memory_8(a6 + 2) >> 2;
+                               line_to = m68k_read_memory_8(a6 + 3) >> 2;
+                               /* allocate render item */
+                               render_item_add(RENDER_ITEM_SHADOW_BUILDING_LINE);
+                               render_item->u.shadow_line.x_pos = x;
+                               render_item->u.shadow_line.y_pos = y;
+                               render_item->u.shadow_line.z_pos = z;
+                               render_item->u.shadow_line.x[0] = vertex.x[line_from];
+                               render_item->u.shadow_line.y[0] = vertex.y[line_from];
+                               render_item->u.shadow_line.z[0] = vertex.z[line_from];
+                               render_item->u.shadow_line.x[1] = vertex.x[line_to];
+                               render_item->u.shadow_line.y[1] = vertex.y[line_to];
+                               render_item->u.shadow_line.z[1] = vertex.z[line_to];
+                       }
+               }
+       } while (s);
+}
+
 /* stop event from CPU received */
 void render_capture_event(int event)
 {
@@ -1346,7 +1598,7 @@ void render_capture_event(int event)
        case STOP_AT_POINT_BEACON:
                point_beacon();
                /* note: we may not call the point-renderer, because projected coordinates are invalid */
-               mercenary_patch_render();
+               mercenary_patch_render(draw_shadows);
                break;
        case STOP_AT_COORD_BUILDING_EXTERIOR:
                coord_building_exterior();
@@ -1502,6 +1754,7 @@ void render_capture_event(int event)
        case STOP_AT_LINE_ISLANDS:
                /* this is not used, as i had noticed so far */
                puts("line island");
+#warning FIXME: this is used on LACHESIS to improve visibility of stripes on the ground. implement this and compate a version with lines to the version without lines
                break;
        case STOP_AT_DRAW_SIGHTS:
                draw_sights();
@@ -1516,7 +1769,10 @@ void render_capture_event(int event)
                draw_explosion();
                break;
        case STOP_AT_PATCH_RENDER:
-               mercenary_patch_render();
+               mercenary_patch_render(draw_shadows);
+               break;
+       case STOP_AT_SHADOW_BUILDING:
+               draw_shadow_building();
                break;
        }
 }
@@ -1525,7 +1781,7 @@ void render_capture_event(int event)
  * rendering
  */
 
-static int use_coord(const char __attribute__((unused)) *what, uint32_t vertex, double *x, double *y, double *z, int fix)
+static int use_coord(const char __attribute__((unused)) *what, uint32_t vertex, double *x, double *y, double *z, int no_inter)
 {
        render_item_t *ri = NULL;
 
@@ -1564,7 +1820,7 @@ static int use_coord(const char __attribute__((unused)) *what, uint32_t vertex,
        *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) {
+       if (!no_inter) {
                /* translate to floating (interpolated) position offset */
                *x -= interpolation.offset_east;
                *y -= interpolation.offset_height;
@@ -1646,8 +1902,54 @@ static int use_interior_coord(const char __attribute__((unused)) *what, uint32_t
        return 0;
 }
 
+/* this will calculate lighting parameters for sky and shadows */
+static void calculate_lighting(double sun_x, double sun_y, double sun_z, double *day, double *stars, double *shadow)
+{
+       double elevation;
+
+//     azimuth = atan2(sun_z, sun_x);
+
+       /* how high is the sun (1.0 is zenith, < 0 is below horizon) */
+       elevation = atan2(sun_y, sqrt(sun_x * sun_x + sun_z * sun_z)) / (M_PI / 2);
+#ifdef DEBUG_LIGHT
+       printf("Elevation of SUN: %.3f\n", elevation);
+#endif
+
+       /* 'day' is how much day color is used for sky (1.0) or night color (0.0) */
+       *day = (elevation - ELEVATION_SKY_NIGHT) / (ELEVATION_SKY_DAY - ELEVATION_SKY_NIGHT);
+       if (*day < 0.0)
+               *day = 0.0;
+       if (*day > 1.0)
+               *day = 1.0;
+#ifdef DEBUG_LIGHT
+       printf("How much day: %.3f\n", *day);
+#endif
+
+       /* 'stars' is how much we see the stars (1.0) fully bright, (0.0) no stars */
+       *stars = (elevation - ELEVATION_STARS_DAY) / (ELEVATION_STARS_NIGHT - ELEVATION_STARS_DAY);
+       if (*stars < 0.0)
+               *stars = 0.0;
+       if (*stars > 1.0)
+               *stars = 1.0;
+#ifdef DEBUG_LIGHT
+       printf("How much stars: %.3f\n", *stars);
+#endif
+
+       /* 'shadow' is how much color (darkness) the shadow has (1.0) totally black, (0.0) not visible */
+       *shadow = (elevation - ELEVATION_SHADOW_MIN) / (ELEVATION_SHADOW_MAX - ELEVATION_SHADOW_MIN);
+       if (*shadow < 0.0)
+               *shadow = 0.0;
+       if (*shadow > 1.0)
+               *shadow = 1.0;
+       *shadow *= SHADOW_LEVEL;
+#ifdef DEBUG_LIGHT
+       printf("How much shadow: %.3f\n", *shadow);
+#endif
+
+}
+
 /* renders one item from render list */
-void render_one_item(render_item_t *render_item, int vr)
+void render_one_item(render_item_t *render_item, int vr, enum render_shadow shadow)
 {
        switch (render_item->type) {
        case RENDER_ITEM_OBJECT_INFO:
@@ -1697,11 +1999,33 @@ void render_one_item(render_item_t *render_item, int vr)
        {
                double x[4], y[4], z[4];
 
+               if (shadow != RENDER_BEFORE_SHADOW)
+                       break;
+
 #ifdef DEBUG_ITEMS
                printf("RENDER_ITEM_SKY\n");
 #endif
                /* get color */
-               opengl_render_color(render_item->u.sky.red, render_item->u.sky.green, render_item->u.sky.blue, 1.0);
+               if (draw_shadows && ground_index >= 0) {
+                       uint16_t day, night;
+                       double day_red, day_green, day_blue;
+                       double night_red, night_green, night_blue;
+                       double day_factor;
+
+                       calculate_lighting(sun_pos_x, sun_pos_y, sun_pos_z, &day_factor, &stars_bright, &shadow_level);
+
+                       /* sky color */
+                       mercenary_get_sky_colors(&day, &night);
+                       /* shift them to the original format (atari style) */
+                       gamecolor2gl(&day_red, &day_green, &day_blue, day >> 1);
+                       gamecolor2gl(&night_red, &night_green, &night_blue, night >> 1);
+                       sky_color_red = day_red * day_factor + night_red * (1.0 - day_factor);
+                       sky_color_green = day_green * day_factor + night_green * (1.0 - day_factor);
+                       sky_color_blue = day_blue * day_factor + night_blue * (1.0 - day_factor);
+                       opengl_render_color(sky_color_red, sky_color_green, sky_color_blue, 1.0);
+
+               } else
+                       opengl_render_color(render_item->u.sky.red, render_item->u.sky.green, render_item->u.sky.blue, 1.0);
                /* create box to fill view */
                x[0] = x[1] = y[1] = y[2] = -1000;
                x[2] = x[3] = y[0] = y[3] = 1000;
@@ -1729,6 +2053,9 @@ void render_one_item(render_item_t *render_item, int vr)
                double x[4], y[4], z[4];
                int i;
 
+               if (shadow != RENDER_BEFORE_SHADOW)
+                       break;
+
 #ifdef DEBUG_ITEMS
                printf("RENDER_ITEM_GROUND\n");
 #endif
@@ -1757,6 +2084,14 @@ void render_one_item(render_item_t *render_item, int vr)
                int i, o;
                int rc;
 
+               if (render_item->type == RENDER_ITEM_ISLAND_POLYGON) {
+                       if (shadow != RENDER_BEFORE_SHADOW)
+                               break;
+               } else {
+                       if (shadow != RENDER_AFTER_SHADOW)
+                               break;
+               }
+
 #ifdef DEBUG_ITEMS
                if (render_item->type == RENDER_ITEM_OBJECT_POLYGON)
                        printf("RENDER_ITEM_OBJECT_POLYGON\n");
@@ -1817,6 +2152,9 @@ void render_one_item(render_item_t *render_item, int vr)
                int i, o;
                int rc;
 
+               if (shadow != RENDER_AFTER_SHADOW)
+                       break;
+
 #ifdef DEBUG_ITEMS
                if (render_item->type == RENDER_ITEM_OBJECT_LINE)
                        printf("RENDER_ITEM_OBJECT_LINE\n");
@@ -1877,6 +2215,9 @@ void render_one_item(render_item_t *render_item, int vr)
                double x, y, z;
                int rc;
 
+               if (shadow != RENDER_AFTER_SHADOW)
+                       break;
+
 #ifdef DEBUG_ITEMS
                printf("RENDER_ITEM_BEACON_POINT\n");
 #endif
@@ -1924,6 +2265,9 @@ void render_one_item(render_item_t *render_item, int vr)
                int i;
                int rc;
 
+               if (shadow != RENDER_AFTER_SHADOW)
+                       break;
+
 #ifdef DEBUG_ITEMS
                printf("RENDER_ITEM_BUILDING_EXTERIOR_LINE\n");
 #endif
@@ -1949,6 +2293,9 @@ void render_one_item(render_item_t *render_item, int vr)
                int i;
                int rc;
 
+               if (shadow != RENDER_AFTER_SHADOW)
+                       break;
+
 #ifdef DEBUG_ITEMS
                printf("RENDER_ITEM_BUILDING_INTERIOR_1TO4\n");
 #endif
@@ -1975,6 +2322,9 @@ void render_one_item(render_item_t *render_item, int vr)
                int vertex, level;
                int rc;
 
+               if (shadow != RENDER_AFTER_SHADOW)
+                       break;
+
 #ifdef DEBUG_ITEMS
                printf("RENDER_ITEM_BUILDING_INTERIOR_5TO6\n");
 #endif
@@ -2003,6 +2353,9 @@ void render_one_item(render_item_t *render_item, int vr)
                int vertex, level;
                int rc;
 
+               if (shadow != RENDER_AFTER_SHADOW)
+                       break;
+
 #ifdef DEBUG_ITEMS
                printf("RENDER_ITEM_BUILDING_INTERIOR_WALL\n");
 #endif
@@ -2049,6 +2402,9 @@ void render_one_item(render_item_t *render_item, int vr)
                int rc;
                double rotate_sky = 0.0;
 
+               if (shadow != RENDER_BEFORE_SHADOW)
+                       break;
+
 #ifdef DEBUG_ITEMS
                printf("RENDER_ITEM_COMET_POLYGON\n");
 #endif
@@ -2079,6 +2435,9 @@ void render_one_item(render_item_t *render_item, int vr)
                int i;
                int rc;
 
+               if (shadow != RENDER_BEFORE_SHADOW)
+                       break;
+
 #ifdef DEBUG_ITEMS
                printf("RENDER_ITEM_ROAD_LINE\n");
 #endif
@@ -2109,6 +2468,9 @@ void render_one_item(render_item_t *render_item, int vr)
                int vertices_num;
                int rc;
 
+               if (shadow != RENDER_BEFORE_SHADOW)
+                       break;
+
 #ifdef DEBUG_ITEMS
                printf("RENDER_ITEM_ROAD_POLYGON\n");
 #endif
@@ -2176,6 +2538,7 @@ void render_one_item(render_item_t *render_item, int vr)
        case RENDER_ITEM_PLANET:
        {
                GET_ORIENTATION;
+               double front_red, front_green, front_blue, back_red, back_green, back_blue;
                double inclination = interpolation.planet_inclination, azimuth = interpolation.planet_azimuth;
                double sun_x, sun_y, sun_z, angle_sun_h;
                double loc_x, loc_y, loc_z, angle_loc_h, angle_loc_v;
@@ -2188,9 +2551,30 @@ void render_one_item(render_item_t *render_item, int vr)
                int rc;
                double rotate_sky = 0.0;
 
+               if (shadow != RENDER_BEFORE_SHADOW)
+                       break;
+
 #ifdef DEBUG_ITEMS
                printf("RENDER_ITEM_PLANET\n");
 #endif
+
+               /* color of planets */
+               front_red = render_item->u.planet.front_red;
+               front_green = render_item->u.planet.front_green;
+               front_blue = render_item->u.planet.front_blue;
+               back_red = render_item->u.planet.back_red;
+               back_green = render_item->u.planet.back_green;
+               back_blue = render_item->u.planet.back_blue;
+               if (draw_shadows && ground_index >= 0) {
+                       /* front color is blended by sky */
+                       front_red = front_red * (1.0 - sky_color_red) + sky_color_red;
+                       front_green = front_green * (1.0 - sky_color_green) + sky_color_green;
+                       front_blue = front_blue * (1.0 - sky_color_blue) + sky_color_blue;
+                       back_red = sky_color_red * (1.0 - stars_bright);
+                       back_green = sky_color_green * (1.0 - stars_bright);
+                       back_blue = sky_color_blue * (1.0 - stars_bright);
+               }
+
                /* get location */
                rc = use_planet_coord("planet(sun)", 0, &sun_x, &sun_y, &sun_z);
                if (rc < 0)
@@ -2207,6 +2591,12 @@ void render_one_item(render_item_t *render_item, int vr)
                        rotate_coordinate(0.0, inclination, azimuth, &sun_x, &sun_y, &sun_z);
                        rotate_coordinate(0.0, inclination, azimuth, &loc_x, &loc_y, &loc_z);
                }
+               /* store sun location */
+               sun_valid = 1;
+               sun_pos_x = sun_x;
+               sun_pos_y = sun_y;
+               sun_pos_z = sun_z;
+               /* rotate sun */
                rotate_coordinate(roll, pitch, yaw + rotate_sky, &sun_x, &sun_y, &sun_z);
                rotate_coordinate(roll, pitch, yaw + rotate_sky, &loc_x, &loc_y, &loc_z);
 
@@ -2231,10 +2621,10 @@ void render_one_item(render_item_t *render_item, int vr)
                /* on which side are we (sun is always bright, vertex == 0) */
                if ((angle < M_PI / 2.0 && angle > -M_PI / 2.0) || render_item->u.planet.vertex == 0) {
                        /* get front side color */
-                       opengl_render_color(render_item->u.planet.front_red, render_item->u.planet.front_green, render_item->u.planet.front_blue, debug_opacity);
+                       opengl_render_color(front_red, front_green, front_blue, debug_opacity);
                } else {
                        /* get back side color */
-                       opengl_render_color(render_item->u.planet.back_red, render_item->u.planet.back_green, render_item->u.planet.back_blue, debug_opacity);
+                       opengl_render_color(back_red, back_green, back_blue, debug_opacity);
                }
 
                /* create and render cicle */
@@ -2282,10 +2672,10 @@ void render_one_item(render_item_t *render_item, int vr)
                /* on which side are we */
                if (angle < M_PI / 2.0 && angle > -M_PI / 2.0) {
                        /* get back side color */
-                       opengl_render_color(render_item->u.planet.back_red, render_item->u.planet.back_green, render_item->u.planet.back_blue, debug_opacity);
+                       opengl_render_color(back_red, back_green, back_blue, debug_opacity);
                } else {
                        /* get front side color */
-                       opengl_render_color(render_item->u.planet.front_red, render_item->u.planet.front_green, render_item->u.planet.front_blue, debug_opacity);
+                       opengl_render_color(front_red, front_green, front_blue, debug_opacity);
                }
 
                /* create and render crescent */
@@ -2332,9 +2722,17 @@ void render_one_item(render_item_t *render_item, int vr)
                double red, green, blue;
                double rotate_sky = 0.0;
 
+               if (shadow != RENDER_BEFORE_SHADOW)
+                       break;
+
 #ifdef DEBUG_ITEMS
                printf("RENDER_ITEM_STARS\n");
 #endif
+
+               /* no stars at day */
+               if (stars_bright == 0.0)
+                       break;
+
                /* use default fov of 64 to calculate z distance */
                z = 160.0 / frustum_slope_64;
 
@@ -2393,7 +2791,7 @@ void render_one_item(render_item_t *render_item, int vr)
                                }
                                /* get color */
                                gamecolor2gl(&red, &green, &blue, color[(m68k_read_memory_8(table - 2) & 0x3c) >> 2]);
-                               opengl_render_color(red, green, blue, debug_opacity);
+                               opengl_render_color(red, green, blue, debug_opacity * stars_bright);
                                /* render point */
                                opengl_render_point(160.0 / frustum_slope_64 * frustum_slope_fov - x, 68.0 - y, z, 0.0);
                        }
@@ -2419,7 +2817,7 @@ void render_one_item(render_item_t *render_item, int vr)
                                table += 2;
                                /* get color */
                                gamecolor2gl(&red, &green, &blue, color[(m68k_read_memory_8(table - 2) & 0x3c) >> 2]);
-                               opengl_render_color(red, green, blue, debug_opacity);
+                               opengl_render_color(red, green, blue, debug_opacity * stars_bright);
                                /* render point */
                                h = (900.0 - x + 160) / 900.0 * M_PI;
                                v1 = (68.0 - y) / 900.0 * M_PI;
@@ -2467,6 +2865,9 @@ void render_one_item(render_item_t *render_item, int vr)
                int i;
                double red, green, blue;
 
+               if (shadow != RENDER_BEFORE_SHADOW)
+                       break;
+
 #ifdef DEBUG_ITEMS
                printf("RENDER_ITEM_INTERSTELLAR_STARS\n");
 #endif
@@ -2489,6 +2890,9 @@ void render_one_item(render_item_t *render_item, int vr)
        {
                double red, green, blue;
 
+               if (shadow != RENDER_BEFORE_SHADOW)
+                       break;
+
 #ifdef DEBUG_ITEMS
                printf("RENDER_ITEM_INTERSTELLAR_SUN\n");
 #endif
@@ -2504,6 +2908,9 @@ void render_one_item(render_item_t *render_item, int vr)
                double x[4], y[4], z[4];
                double red, green, blue;
 
+               if (shadow != RENDER_AFTER_SHADOW)
+                       break;
+
 #ifdef DEBUG_ITEMS
                printf("RENDER_ITEM_SIGHTS\n");
 #endif
@@ -2531,6 +2938,9 @@ void render_one_item(render_item_t *render_item, int vr)
                double x[EXPLOSION_VERTICES], y[EXPLOSION_VERTICES], z[EXPLOSION_VERTICES];
                int i, e;
 
+               if (shadow != RENDER_AFTER_SHADOW)
+                       break;
+
 #ifdef DEBUG_ITEMS
                printf("RENDER_ITEM_EXPLOSION\n");
 #endif
@@ -2555,6 +2965,115 @@ void render_one_item(render_item_t *render_item, int vr)
                }
                break;
        }
+       case RENDER_ITEM_SHADOW_BUILDING_POLYGON:
+       case RENDER_ITEM_SHADOW_BUILDING_LINE:
+       case RENDER_ITEM_SHADOW_OBJECT_POLYGON:
+       case RENDER_ITEM_SHADOW_OBJECT_LINE:
+       {
+               GET_ORIENTATION;
+               double x[MAX_POLYGON], y[MAX_POLYGON], z[MAX_POLYGON];
+               double shadow_x, shadow_z;
+               int o;
+               int i;
+
+               if (shadow != RENDER_AT_SHADOW)
+                       break;
+
+#ifdef DEBUG_ITEMS
+               if (render_item->type == RENDER_ITEM_SHADOW_POLYGON)
+                       printf("RENDER_ITEM_SHADOW_POLYGON\n");
+               if (render_item->type == RENDER_ITEM_SHADOW_LINE)
+                       printf("RENDER_ITEM_SHADOW_LINE\n");
+#endif
+
+               if (!sun_valid || sun_pos_y < 1.0)
+                       return;
+
+               if (shadow_level == 0.0)
+                       return;
+
+               /* calculate shadow */
+               shadow_x = -sun_pos_x / sun_pos_y;
+               shadow_z = -sun_pos_z / sun_pos_y;
+
+               opengl_render_color_alpha(0, 0, 0, shadow_level);
+
+               if (render_item->type == RENDER_ITEM_SHADOW_BUILDING_POLYGON
+                || render_item->type == RENDER_ITEM_SHADOW_OBJECT_POLYGON) {
+                       /* get and rotate vertex */
+                       for (i = 0; i < render_item->u.shadow_polygon.vertices; i++) {
+                               x[i] = render_item->u.shadow_polygon.x[i];
+                               y[i] = render_item->u.shadow_polygon.y[i];
+                               z[i] = render_item->u.shadow_polygon.z[i];
+                               /* interpolate motion, if object is moving */
+                               if (render_item->type == RENDER_ITEM_SHADOW_OBJECT_POLYGON && 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];
+                                       }
+                               }
+                               /* flatten polygon into shadow */
+                               x[i] += y[i] * shadow_x;
+                               z[i] += y[i] * shadow_z;
+                               y[i] = 0.0;
+                               /* apply interpolation */
+                               x[i] -= interpolation.offset_east;
+                               y[i] -= interpolation.offset_height;
+                               z[i] -= interpolation.offset_north;
+                               /* apply position */
+                               x[i] += render_item->u.shadow_polygon.x_pos;
+                               y[i] += render_item->u.shadow_polygon.y_pos;
+                               z[i] += render_item->u.shadow_polygon.z_pos;
+                               /* rotate vertex */
+                               rotate_coordinate(roll, pitch, yaw, &x[i], &y[i], &z[i]);
+                       }
+                       /* render */
+                       opengl_render_polygon(x, y, z, render_item->u.shadow_polygon.vertices, 0); /* no back face culling */
+               }
+               if (render_item->type == RENDER_ITEM_SHADOW_BUILDING_LINE
+                || render_item->type == RENDER_ITEM_SHADOW_OBJECT_LINE) {
+                       /* get and rotate vertex */
+                       for (i = 0; i < 2; i++) {
+                               x[i] = render_item->u.shadow_line.x[i];
+                               y[i] = render_item->u.shadow_line.y[i];
+                               z[i] = render_item->u.shadow_line.z[i];
+                               /* interpolate motion, if object is moving */
+                               if (render_item->type == RENDER_ITEM_SHADOW_OBJECT_LINE && 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];
+                                       }
+                               }
+                               /* flatten polygon into shadow */
+                               x[i] += y[i] * shadow_x;
+                               z[i] += y[i] * shadow_z;
+                               y[i] = 0.0;
+                               /* apply interpolation */
+                               x[i] -= interpolation.offset_east;
+                               y[i] -= interpolation.offset_height;
+                               z[i] -= interpolation.offset_north;
+                               /* apply position */
+                               x[i] += render_item->u.shadow_line.x_pos;
+                               y[i] += render_item->u.shadow_line.y_pos;
+                               z[i] += render_item->u.shadow_line.z_pos;
+                               /* rotate vertex */
+                               rotate_coordinate(roll, pitch, yaw, &x[i], &y[i], &z[i]);
+                       }
+                       /* render */
+                       opengl_render_line(x[0], y[0], z[0], x[1], y[1], z[1], 0.0);
+               }
+               break;
+       }
        default:
                print_info("Unknown render item type, please fix!\n");
        }
@@ -2819,8 +3338,21 @@ int render_all_items(double inter, int vr)
        if (!render_list_new)
                return -1;
 
+       sun_valid = 0;
+       stars_bright = 1.0;
+
+       for (render_item = render_list_new; render_item; render_item = render_item->next) {
+               render_one_item(render_item, vr, RENDER_BEFORE_SHADOW);
+       }
+       if (draw_shadows && ground_index >= 0) {
+               begin_shadow_render();
+               for (render_item = render_list_new; render_item; render_item = render_item->next) {
+                       render_one_item(render_item, vr, RENDER_AT_SHADOW);
+               }
+               end_shadow_render();
+       }
        for (render_item = render_list_new; render_item; render_item = render_item->next) {
-               render_one_item(render_item, vr);
+               render_one_item(render_item, vr, RENDER_AFTER_SHADOW);
        }
 
        return 0;
@@ -2838,7 +3370,7 @@ int render_all_white(int vr)
        sky_item.u.ground.green = 1.0;
        sky_item.u.ground.blue = 1.0;
 
-       render_one_item(&sky_item, vr);
+       render_one_item(&sky_item, vr, RENDER_BEFORE_SHADOW);
 
        return 0;
 }