Complete.Org: Mailing Lists: Archives: freeciv-dev: March 2004:
[Freeciv-Dev] (PR#8416) 2d versus 3d fog
Home

[Freeciv-Dev] (PR#8416) 2d versus 3d fog

[Top] [All Lists]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index] [Thread Index]
To: undisclosed-recipients: ;
Subject: [Freeciv-Dev] (PR#8416) 2d versus 3d fog
From: "Jason Short" <jdorje@xxxxxxxxxxxxxxxxxxxxx>
Date: Mon, 29 Mar 2004 00:13:30 -0800
Reply-to: rt@xxxxxxxxxxx

<URL: http://rt.freeciv.org/Ticket/Display.html?id=8416 >

Currently freeciv fog is drawn 3d.  It correctly follows the painters 
algorithm.  Any tile that is fogged is completely fogged - its fog may 
cover other unfogged tiles behind it; other unfogged tiles in front of 
it are displayed fully unfogged.  See the attached orig.png.

Another way to do it is in 2d.  Now all terrain is in one layer and fog 
is drawn on a flat (2d) layer above it.  Now the painters algorithm is 
violated: a mountain can stick up out of the fog to be in an adjacent 
unfogged tile, or an unfogged mountain can have its peak covered with 
fog.  See the attached new.png (created with new.diff, a hack).

The rules for the 2d drawing are:

   - All normal terrain and unreal tiles are drawn in one phase.  Unknown
     tiles may be left empty, but unreal tiles must be drawn here.
   - Fog and unknown terrain is drawn on top in a second phase.

Note that in non-iso view the two methods are indistinguishable since 
tiles never overlap, so all of this only makes a difference in iso-view.


So the question is, which one is better?  Why would we consider 
changing, since surely there's a good reason for the current "more 
accurate" algorithm?

The advantages of the 3d algorithm are:

- It is more accurate.  Note this doesn't necessarily make for better 
graphics, but it certainly means more "correct" ones.

While the advantages of the 2d algorithm are:

- It is technically much simpler.  Rather than having to draw fog over 
each sprite (which is not very portable; see gui-sdl), a single fog 
sprite can be drawn at the end.  No doubt this makes it faster too (no 
numbers on this).

- Perhaps as a result of this, it's pretty easy to have a fog gradient. 
  See http://freeciv.org/~jdorje/FogOfWar.png.  This (non-GPL) file 
shows the 3^4 possible ways no-fog, fog, and unknown can meet at a tile 
corner.  Consider that the reddish colors are transparent and the 
white=>black represents an increasing alpha level.  Thus the (0,0) 
sprite is an unknown tile, the (4,4) sprite is a fogged tile, and the 
(8,8) tile is a known tile.  All the others give combinations of these 
three.  This allows a smoother transition to fog, like in 
http://www.civfanatics.com/civ3images/gallery1/screenshot1.shtml.  (In 
this screenshot you can also see an "unknown" mountain sticking up so 
that its peak is visible in the fog.)  I don't think it's feasible to 
have a fog gradient with the 3d system, although a partial gradient may 
be possible.

I don't really have an answer on this one.  I can say that supporting 
both methods will probably take twice the effort; there's very little 
overlap between the two algorithms.

jason

PNG image

PNG image

? data/civ3
? data/womoks
Index: client/mapview_common.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/mapview_common.c,v
retrieving revision 1.97
diff -u -r1.97 mapview_common.c
--- client/mapview_common.c     27 Mar 2004 19:08:40 -0000      1.97
+++ client/mapview_common.c     29 Mar 2004 07:02:54 -0000
@@ -51,6 +51,9 @@
 static void dirty_overview(void);
 static void flush_dirty_overview(void);
 
+static void myput_fog_iso(int map_x, int map_y, enum draw_type draw);
+static void myput_tile_iso(int map_x, int map_y, enum draw_type draw);
+
 /**************************************************************************
  Refreshes a single tile on the map canvas.
 **************************************************************************/
@@ -62,7 +65,8 @@
   }
 
   if (tile_visible_mapcanvas(x, y)) {
-    update_map_canvas(x, y, 1, 1, FALSE);
+    update_map_canvas(x, y, 1, 1, FALSE, myput_tile_iso);
+    update_map_canvas(x, y, 1, 1, FALSE, myput_fog_iso);
 
     if (update_city_text_in_refresh_tile
        && (draw_city_names || draw_city_productions)) {
@@ -1053,7 +1057,7 @@
   The coordinates have not been normalized, and are not guaranteed to be
   real (we have to draw unreal tiles too).
 **************************************************************************/
-static void put_tile_iso(int map_x, int map_y, enum draw_type draw)
+static void myput_tile_iso(int map_x, int map_y, enum draw_type draw)
 {
   int canvas_x, canvas_y;
 
@@ -1105,10 +1109,68 @@
                       offset_x, offset_y, offset_y_unit,
                       width, height, height_unit,
                       draw, FALSE);
+    }
+  }
+}
+
+static void myput_fog_iso(int map_x, int map_y, enum draw_type draw)
+{
+  int canvas_x, canvas_y;
+
+  if (map_to_canvas_pos(&canvas_x, &canvas_y, map_x, map_y)) {
+    int height, width, height_unit;
+    int offset_x, offset_y, offset_y_unit;
+
+    freelog(LOG_DEBUG, "putting (%d,%d) at (%d,%d), draw %x",
+           map_x, map_y, canvas_x, canvas_y, draw);
+
+    if ((draw & D_TMB_L) && (draw & D_TMB_R)) {
+      width = NORMAL_TILE_WIDTH;
+    } else {
+      width = NORMAL_TILE_WIDTH / 2;
+    }
+
+    if (draw & D_TMB_L) {
+      offset_x = 0;
+    } else {
+      offset_x = NORMAL_TILE_WIDTH / 2;
+    }
+
+    height = 0;
+    if (draw & D_M_LR) {
+      height += NORMAL_TILE_HEIGHT / 2;
+    }
+    if (draw & D_B_LR) {
+      height += NORMAL_TILE_HEIGHT / 2;
+    }
+
+    height_unit = height;
+    if (draw & D_T_LR) {
+      height_unit += NORMAL_TILE_HEIGHT / 2;
+    }
+
+    offset_y = (draw & D_M_LR) ? 0 : NORMAL_TILE_HEIGHT / 2;
+
+    if (draw & D_T_LR) {
+      offset_y_unit = 0;
+    } else if (draw & D_M_LR) {
+      offset_y_unit = NORMAL_TILE_HEIGHT / 2;
     } else {
-      canvas_put_sprite(mapview_canvas.store, canvas_x, canvas_y,
-                       sprites.black_tile,
-                       offset_x, offset_y, width, height);
+      offset_y_unit = NORMAL_TILE_HEIGHT;
+    }
+
+    if (normalize_map_pos(&map_x, &map_y)) {
+      if (tile_get_known(map_x, map_y) == TILE_UNKNOWN) {
+       canvas_put_sprite(mapview_canvas.store,
+                         canvas_x, canvas_y,
+                         sprites.black_tile,
+                         offset_x, offset_y, width, height);
+      } else if (tile_get_known(map_x, map_y) == TILE_KNOWN_FOGGED
+                && draw_fog_of_war) {
+       canvas_put_fog(mapview_canvas.store,
+                      canvas_x, canvas_y,
+                      offset_x, offset_y, width, height);
+      }
     }
   }
 }
@@ -1129,7 +1191,8 @@
   normalized or even real.
 **************************************************************************/
 void update_map_canvas(int x, int y, int width, int height, 
-                      bool write_to_screen)
+                      bool write_to_screen,
+                      void (put_tile_iso)(int, int, enum draw_type))
 {
   int canvas_start_x, canvas_start_y;
 
@@ -1283,12 +1346,14 @@
     width = mapview_canvas.tile_width + mapview_canvas.tile_height + 2;
     height = width;
     update_map_canvas(map_x0 - 1, map_y0 - mapview_canvas.tile_width - 1,
-                     width, height, FALSE);
+                     width, height, FALSE, myput_tile_iso);
+    update_map_canvas(map_x0 - 1, map_y0 - mapview_canvas.tile_width - 1,
+                     width, height, FALSE, myput_fog_iso);
   } else {
     update_map_canvas(map_x0, map_y0,
                      mapview_canvas.tile_width + 1,
                      mapview_canvas.tile_height + 1,
-                     FALSE);
+                     FALSE, NULL);
   }
 
   show_city_descriptions();
Index: client/mapview_common.h
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/mapview_common.h,v
retrieving revision 1.52
diff -u -r1.52 mapview_common.h
--- client/mapview_common.h     19 Mar 2004 20:22:08 -0000      1.52
+++ client/mapview_common.h     29 Mar 2004 07:02:54 -0000
@@ -184,7 +184,8 @@
                           enum draw_type draw);
 
 void update_map_canvas(int x, int y, int width, int height,
-                      bool write_to_screen);
+                      bool write_to_screen,
+                      void (put_tile_iso)(int, int, enum draw_type));
 void update_map_canvas_visible(void);
 
 void show_city_descriptions(void);
Index: client/packhand.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/packhand.c,v
retrieving revision 1.355
diff -u -r1.355 packhand.c
--- client/packhand.c   22 Mar 2004 20:58:11 -0000      1.355
+++ client/packhand.c   29 Mar 2004 07:02:54 -0000
@@ -570,10 +570,7 @@
   if ((draw_map_grid || draw_borders) && can_client_change_view()) {
     /* We have to make sure we update any workers on the map grid, then
      * redraw the city descriptions on top of them. */
-    update_map_canvas(pcity->x - CITY_MAP_SIZE / 2,
-                     pcity->y - CITY_MAP_SIZE / 2,
-                     CITY_MAP_SIZE, CITY_MAP_SIZE, FALSE);
-    queue_mapview_update(UPDATE_CITY_DESCRIPTIONS);
+    queue_mapview_update(UPDATE_MAP_CANVAS_VISIBLE);
   } else {
     refresh_tile_mapcanvas(pcity->x, pcity->y, FALSE);
   }
Index: client/gui-gtk-2.0/mapview.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/gui-gtk-2.0/mapview.c,v
retrieving revision 1.109
diff -u -r1.109 mapview.c
--- client/gui-gtk-2.0/mapview.c        24 Mar 2004 06:18:18 -0000      1.109
+++ client/gui-gtk-2.0/mapview.c        29 Mar 2004 07:02:54 -0000
@@ -61,8 +61,7 @@
                                         int canvas_x, int canvas_y,
                                         struct Sprite *ssprite,
                                         int offset_x, int offset_y,
-                                        int width, int height,
-                                        bool fog);
+                                        int width, int height);
 static void pixmap_put_tile_iso(GdkDrawable *pm, int x, int y,
                                int canvas_x, int canvas_y,
                                int citymode,
@@ -829,30 +828,35 @@
                                         int canvas_x, int canvas_y,
                                         struct Sprite *ssprite,
                                         int offset_x, int offset_y,
-                                        int width, int height,
-                                        bool fog)
+                                        int width, int height)
 {
   if (!ssprite || !width || !height)
     return;
 
   pixmap_put_sprite(pixmap, canvas_x, canvas_y, ssprite,
                    offset_x, offset_y, width, height);
+}
+
+void canvas_put_fog(struct canvas *pcanvas,
+                   int canvas_x, int canvas_y,
+                   int offset_x, int offset_y,
+                   int width, int height)
+{
+  struct Sprite *ssprite = sprites.black_tile;
 
   /* I imagine this could be done more efficiently. Some pixels We first
      draw from the sprite, and then draw black afterwards. It would be much
      faster to just draw every second pixel black in the first place. */
-  if (fog) {
-    gdk_gc_set_clip_origin(fill_tile_gc, canvas_x, canvas_y);
-    gdk_gc_set_clip_mask(fill_tile_gc, ssprite->mask);
-    gdk_gc_set_foreground(fill_tile_gc, colors_standard[COLOR_STD_BLACK]);
-    gdk_gc_set_stipple(fill_tile_gc, black50);
-
-    gdk_draw_rectangle(pixmap, fill_tile_gc, TRUE,
-                      canvas_x+offset_x, canvas_y+offset_y,
-                      MIN(width, MAX(0, ssprite->width-offset_x)),
-                      MIN(height, MAX(0, ssprite->height-offset_y)));
-    gdk_gc_set_clip_mask(fill_tile_gc, NULL);
-  }
+  gdk_gc_set_clip_origin(fill_tile_gc, canvas_x, canvas_y);
+  gdk_gc_set_clip_mask(fill_tile_gc, ssprite->mask);
+  gdk_gc_set_foreground(fill_tile_gc, colors_standard[COLOR_STD_BLACK]);
+  gdk_gc_set_stipple(fill_tile_gc, black50);
+
+  gdk_draw_rectangle(pcanvas->pixmap, fill_tile_gc, TRUE,
+                    canvas_x+offset_x, canvas_y+offset_y,
+                    MIN(width, MAX(0, ssprite->width-offset_x)),
+                    MIN(height, MAX(0, ssprite->height-offset_y)));
+  gdk_gc_set_clip_mask(fill_tile_gc, NULL);
 }
 
 /**************************************************************************
@@ -1019,16 +1023,14 @@
                                    int canvas_x, int canvas_y,
                                    struct drawn_sprite *pdsprite,
                                    int offset_x, int offset_y,
-                                   int width, int height,
-                                   bool fog)
+                                   int width, int height)
 {
   int ox = pdsprite->offset_x, oy = pdsprite->offset_y;
 
   pixmap_put_overlay_tile_draw(pixmap, canvas_x + ox, canvas_y + oy,
                               pdsprite->sprite,
                               offset_x - ox, offset_y - oy,
-                              width, height,
-                              fog);
+                              width, height);
 }
 
 /**************************************************************************
@@ -1037,8 +1039,7 @@
 static void put_city_pixmap_draw(struct city *pcity, GdkPixmap *pm,
                                 int canvas_x, int canvas_y,
                                 int offset_x, int offset_y_unit,
-                                int width, int height_unit,
-                                bool fog)
+                                int width, int height_unit)
 {
   struct drawn_sprite sprites[80];
   int count = fill_city_sprite_array_iso(sprites, pcity);
@@ -1047,8 +1048,7 @@
   for (i=0; i<count; i++) {
     if (sprites[i].sprite) {
       pixmap_put_drawn_sprite(pm, canvas_x, canvas_y, &sprites[i],
-                             offset_x, offset_y_unit, width, height_unit,
-                             fog);
+                             offset_x, offset_y_unit, width, height_unit);
     }
   }
 }
@@ -1088,7 +1088,7 @@
   struct unit *punit, *pfocus;
   enum tile_special_type special;
   int count, i;
-  bool solid_bg, fog, tile_hilited;
+  bool solid_bg, tile_hilited;
   struct canvas canvas_store = {pm};
 
   if (!width || !(height || height_unit))
@@ -1107,7 +1107,6 @@
   assert(is_real_map_pos(x, y));
   normalize_map_pos(&x, &y);
 
-  fog = tile_get_known(x, y) == TILE_KNOWN_FOGGED && draw_fog_of_war;
   pcity = map_get_city(x, y);
   punit = get_drawable_unit(x, y, citymode);
   pfocus = get_unit_in_focus();
@@ -1124,25 +1123,13 @@
                       MIN(width, MAX(0, sprites.black_tile->width-offset_x)),
                       MIN(height, MAX(0, 
sprites.black_tile->height-offset_y)));
     gdk_gc_set_clip_mask(fill_bg_gc, NULL);
-    if (fog) {
-      gdk_gc_set_clip_origin(fill_tile_gc, canvas_x, canvas_y);
-      gdk_gc_set_clip_mask(fill_tile_gc, sprites.black_tile->mask);
-      gdk_gc_set_foreground(fill_tile_gc, colors_standard[COLOR_STD_BLACK]);
-      gdk_gc_set_stipple(fill_tile_gc, black50);
-
-      gdk_draw_rectangle(pm, fill_tile_gc, TRUE,
-                        canvas_x+offset_x, canvas_y+offset_y,
-                        MIN(width, MAX(0, sprites.black_tile->width-offset_x)),
-                        MIN(height, MAX(0, 
sprites.black_tile->height-offset_y)));
-      gdk_gc_set_clip_mask(fill_tile_gc, NULL);
-    }
   }
 
   /*** Draw terrain and specials ***/
   for (i = 0; i < count; i++) {
     if (tile_sprs[i].sprite)
       pixmap_put_drawn_sprite(pm, canvas_x, canvas_y, &tile_sprs[i],
-                             offset_x, offset_y, width, height, fog);
+                             offset_x, offset_y, width, height);
     else
       freelog(LOG_ERROR, "sprite is NULL");
   }
@@ -1239,26 +1226,26 @@
     put_city_pixmap_draw(pcity, pm,
                         canvas_x, canvas_y - NORMAL_TILE_HEIGHT/2,
                         offset_x, offset_y_unit,
-                        width, height_unit, fog);
+                        width, height_unit);
   }
   if (contains_special(special, S_AIRBASE) && draw_fortress_airbase)
     pixmap_put_overlay_tile_draw(pm,
                                 canvas_x, canvas_y-NORMAL_TILE_HEIGHT/2,
                                 sprites.tx.airbase,
                                 offset_x, offset_y_unit,
-                                width, height_unit, fog);
+                                width, height_unit);
   if (contains_special(special, S_FALLOUT) && draw_pollution)
     pixmap_put_overlay_tile_draw(pm,
                                 canvas_x, canvas_y,
                                 sprites.tx.fallout,
                                 offset_x, offset_y,
-                                width, height, fog);
+                                width, height);
   if (contains_special(special, S_POLLUTION) && draw_pollution)
     pixmap_put_overlay_tile_draw(pm,
                                 canvas_x, canvas_y,
                                 sprites.tx.pollution,
                                 offset_x, offset_y,
-                                width, height, fog);
+                                width, height);
 
   /*** city size ***/
   /* Not fogged as it would be unreadable */
@@ -1267,12 +1254,12 @@
       pixmap_put_overlay_tile_draw(pm, canvas_x, canvas_y-NORMAL_TILE_HEIGHT/2,
                                   sprites.city.size_tens[pcity->size/10],
                                   offset_x, offset_y_unit,
-                                  width, height_unit, 0);
+                                  width, height_unit);
 
     pixmap_put_overlay_tile_draw(pm, canvas_x, canvas_y-NORMAL_TILE_HEIGHT/2,
                                 sprites.city.size[pcity->size%10],
                                 offset_x, offset_y_unit,
-                                width, height_unit, 0);
+                                width, height_unit);
   }
 
   /*** Unit ***/
@@ -1286,7 +1273,7 @@
                                   canvas_x, canvas_y-NORMAL_TILE_HEIGHT/2,
                                   sprites.unit.stack,
                                   offset_x, offset_y_unit,
-                                  width, height_unit, fog);
+                                  width, height_unit);
   }
 
   if (contains_special(special, S_FORTRESS) && draw_fortress_airbase)
@@ -1294,7 +1281,7 @@
                                 canvas_x, canvas_y-NORMAL_TILE_HEIGHT/2,
                                 sprites.tx.fortress,
                                 offset_x, offset_y_unit,
-                                width, height_unit, fog);
+                                width, height_unit);
 }
 
 /**************************************************************************
Index: client/include/mapview_g.h
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/include/mapview_g.h,v
retrieving revision 1.51
diff -u -r1.51 mapview_g.h
--- client/include/mapview_g.h  13 Mar 2004 19:07:30 -0000      1.51
+++ client/include/mapview_g.h  29 Mar 2004 07:02:54 -0000
@@ -42,6 +42,10 @@
                      int offset_x, int offset_y, int offset_y_unit,
                      int width, int height, int height_unit,
                      enum draw_type draw, bool citymode);
+void canvas_put_fog(struct canvas *pcanvas,
+                   int canvas_x, int canvas_y,
+                   int offset_x, int offset_y,
+                   int width, int height);
 void canvas_put_sprite(struct canvas *pcanvas,
                       int canvas_x, int canvas_y, struct Sprite *sprite,
                       int offset_x, int offset_y, int width, int height);

[Prev in Thread] Current Thread [Next in Thread]
  • [Freeciv-Dev] (PR#8416) 2d versus 3d fog, Jason Short <=