Complete.Org: Mailing Lists: Archives: freeciv-dev: February 2004:
[Freeciv-Dev] (PR#7534) generalization of coasts
Home

[Freeciv-Dev] (PR#7534) generalization of coasts

[Top] [All Lists]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index] [Thread Index]
To: undisclosed-recipients: ;
Subject: [Freeciv-Dev] (PR#7534) generalization of coasts
From: "Jason Short" <jdorje@xxxxxxxxxxxxxxxxxxxxx>
Date: Wed, 25 Feb 2004 17:52:07 -0800
Reply-to: rt@xxxxxxxxxxx

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

This patch generalizes the algorithm used in iso-view to draw ocean and 
coasts.

This algorithm is slick.  Consider a rectangular tile (in iso-view the 
tile is tilted; this makes no difference).  We divide it into four cells:

_______________
|      |      |
|   A  |  B   |
|      |      |
|-------------|
|      |      |
|   D  |  C   |
|______|______|

Now each of these cells borders 3 adjacent tiles (including the 
diagonal).  Consider each cell individually, and do a simple boolean 
match against those three adjacent tiles: 8 possible choices.  So for 
each cell we choose one of 8 different sprites to draw.

Currently this is done with lots of is_ocean() checks.  Of course we 
potentially want different types of ocean, so these checks probably 
aren't so good.  Under this patch instead of special-casing ocean all 
over the place this information is put into the ruleset.  A new variable 
is added to each terrain type: cell_type.  If this is "single" or unset, 
the traditional single-cell-drawing method is used.  If it's "rect", 
then the 4-rectangle drawing method is used.

Support is included for iso and non-iso view.  However non-iso view is 
untested since no tilesets use this option.  However, using this option 
(and some future options) I think a much better non-iso tileset could be 
created!  For anyone interested in this I refer you to the section on 
terrain sprites in doc/README.graphics.  Note that the data format may 
change as the algorithm is made more flexible.

In future this option may be extended in two ways:

- In addition to rectangular cells, we may be able to draw using 
triangular cells.  This is simpler than the rectangular cell method but 
may also give good results.

- Currently the match is a boolean one.  In future we may do a full 
match.  That means instead of 2^3 sprites per cell we'd need n^3 sprites 
per cell!  But the results are much better.

jason

Index: client/tilespec.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/tilespec.c,v
retrieving revision 1.145
diff -u -r1.145 tilespec.c
--- client/tilespec.c   2004/02/25 18:14:26     1.145
+++ client/tilespec.c   2004/02/26 01:50:47
@@ -689,6 +689,7 @@
 
   for (i = 0; i < num_terrains; i++) {
     struct terrain_drawing_data *terr = fc_malloc(sizeof(*terr));
+    char *cell_type;
 
     memset(terr, 0, sizeof(*terr));
     terr->name = mystrdup(terrains[i] + strlen("terrain_"));
@@ -698,6 +699,17 @@
                                           terrains[i]);
     terr->match_type = secfile_lookup_int(file, "%s.match_type",
                                          terrains[i]);
+    cell_type = secfile_lookup_str_default(file, "single", "%s.cell_type",
+                                          terrains[i]);
+    if (strcasecmp(cell_type, "single") == 0) {
+      terr->cell_type = CELL_SINGLE;
+    } else if (strcasecmp(cell_type, "rect") == 0) {
+      terr->cell_type = CELL_RECT;
+    } else {
+      freelog(LOG_ERROR, "Unknown cell type %s for %s.",
+             cell_type, terrains[i]);
+      terr->cell_type = CELL_SINGLE;
+    }
     terr->mine_tag = secfile_lookup_str_default(file, NULL, "%s.mine_sprite",
                                                terrains[i]);
     if (terr->mine_tag) {
@@ -1030,15 +1042,7 @@
     SET_SPRITE_ALT(tx.farmland[i], buffer, "tx.farmland");
   }
   
-  if (is_isometric) {
-    for (i=0; i<4; i++) {
-      for (j=0; j<8; j++) {
-       char *dir2 = "udlr";
-       my_snprintf(buffer, sizeof(buffer), "tx.coast_cape_%c%d", dir2[i], j);
-       SET_SPRITE(tx.coast_cape_iso[j][i], buffer);
-      }
-    }
-  } else {
+  if (!is_isometric) {
     for(i=1; i<NUM_DIRECTION_NSEW; i++) {
       my_snprintf(buffer, sizeof(buffer), "tx.darkness_%s", nsew_str(i));
       SET_SPRITE(tx.darkness[i], buffer);
@@ -1178,31 +1182,45 @@
 
   /* Currently ocean terrains have special handling.  Although a match type
    * may be specified it is ignored.  This is a hack. */
-  if (is_isometric && is_ocean(terrain)) {
+  if (draw->match_type == 0 || draw->is_layered) {
+    /* Load single sprite for this terrain. */
     my_snprintf(buffer1, sizeof(buffer1), "t.%s1", draw->name);
     draw->base = lookup_sprite_tag_alt(buffer1, "", TRUE, "tile_type",
                                       tt->terrain_name);
-  } else {
-    if (draw->match_type == 0 || draw->is_layered) {
-      /* Load single sprite for this terrain. */
-      my_snprintf(buffer1, sizeof(buffer1), "t.%s1", draw->name);
-      draw->base = lookup_sprite_tag_alt(buffer1, "", TRUE, "tile_type",
-                                        tt->terrain_name);
-    }
+  }
 
-    if (draw->match_type != 0) {
+  if (draw->match_type != 0) {
+    int j;
+
+    switch (draw->cell_type) {
+    case CELL_SINGLE:
       /* Load 16 cardinally-matched sprites. */
       for (i = 0; i < NUM_DIRECTION_NSEW; i++) {
        my_snprintf(buffer1, sizeof(buffer1),
                    "t.%s_%s", draw->name, nsew_str(i));
        draw->blend[i] = lookup_sprite_tag_alt(buffer1, "", TRUE,
-                                              "tile_type", tt->terrain_name);
+                                              "tile_type",
+                                              tt->terrain_name);
       }
-
-      if (!draw->base) {
-       draw->base = draw->blend[0];
+      break;
+    case CELL_RECT:
+      for (i = 0; i < 4; i++) {
+       for (j = 0; j < 8; j++) {
+         char *dir2 = "udlr";
+
+         my_snprintf(buffer1, sizeof(buffer1), "t.%s_cell_%c%d",
+                     draw->name, dir2[i], j);
+         draw->cells[j][i] = lookup_sprite_tag_alt(buffer1, "", TRUE,
+                                                   "tile_type",
+                                                   tt->terrain_name);
+       }
       }
+      break;
     }
+
+    if (!draw->base) {
+      draw->base = draw->blend[0];
+    }
   }
 
   for (i=0; i<2; i++) {
@@ -1887,62 +1905,73 @@
     ADD_SPRITE_SIMPLE(sprite);
     return 1;
   }
-
-  if (is_isometric && is_ocean(ttype)) {
-    /* painted via coasts. */
-    const enum direction8 dirs[4] = {
-      DIR8_NORTHWEST, /* up */
-      DIR8_SOUTHEAST, /* down */
-      DIR8_SOUTHWEST, /* left */
-      DIR8_NORTHEAST /* right */
-    };
-    const int offsets[4][2] = {
-      {NORMAL_TILE_WIDTH / 4, 0},
-      {NORMAL_TILE_WIDTH / 4, NORMAL_TILE_HEIGHT / 2},
-      {0, NORMAL_TILE_HEIGHT / 4},
-      {NORMAL_TILE_WIDTH / 2, NORMAL_TILE_HEIGHT / 4},
-    };
-    int i;
-
-    *dither_count = 4;
-
-    /* put coasts */
-    for (i = 0; i < 4; i++) {
-      int array_index = ((!is_ocean(ttype_near[dir_ccw(dirs[i])]) ? 1 : 0)
-                        + (!is_ocean(ttype_near[dirs[i]]) ? 2 : 0)
-                        + (!is_ocean(ttype_near[dir_cw(dirs[i])])
-                           ? 4 : 0));
 
-      ADD_SPRITE(sprites.tx.coast_cape_iso[array_index][i],
-                offsets[i][0], offsets[i][1]);
-    }
-  } else {
+  if (sprites.terrain[ttype]->match_type == 0
+      || sprites.terrain[ttype]->is_layered) {
+    ADD_SPRITE_SIMPLE(sprites.terrain[ttype]->base);
     *dither_count = 1;
+  }
 
-    if (sprites.terrain[ttype]->match_type == 0
-       || sprites.terrain[ttype]->is_layered) {
-      ADD_SPRITE_SIMPLE(sprites.terrain[ttype]->base);
-    }
+  if (sprites.terrain[ttype]->match_type != 0) {
+    int match_type = sprites.terrain[ttype]->match_type;
 
-    if (sprites.terrain[ttype]->match_type != 0) {
-      int match_type = sprites.terrain[ttype]->match_type;
-#define MATCH(dir) (sprites.terrain[ttype_near[(dir)]]->match_type)
-      int tileno = INDEX_NSEW(MATCH(DIR8_NORTH) == match_type,
-                             MATCH(DIR8_SOUTH) == match_type,
-                             MATCH(DIR8_EAST) == match_type,
-                             MATCH(DIR8_WEST) == match_type);
-#undef MATCH
+#define MATCH(dir) ((sprites.terrain[ttype_near[(dir)]]->match_type) \
+                   == match_type)
+    if (sprites.terrain[ttype]->cell_type == CELL_SINGLE) {
+      int tileno;
+
+      tileno = INDEX_NSEW(MATCH(DIR8_NORTH), MATCH(DIR8_SOUTH),
+                         MATCH(DIR8_EAST), MATCH(DIR8_WEST));
 
       ADD_SPRITE_SIMPLE(sprites.terrain[ttype]->blend[tileno]);
-    }
+      if (!sprites.terrain[ttype]->is_layered) {
+       *dither_count = 1;
+      }
+    } else if (sprites.terrain[ttype]->cell_type == CELL_RECT) {
+      /* Divide the tile up into four rectangular cells.  Now each of these
+       * cells covers one corner, and each is adjacent to 3 different
+       * tiles.  For each cell we pixk a sprite based upon the adjacent
+       * terrains at each of those tiles.  Thus we have 8 different sprites
+       * for each of the 4 cells (32 sprites total). */
+      const int W = NORMAL_TILE_WIDTH, H = NORMAL_TILE_HEIGHT;
+      const enum direction8 dirs[4] = {
+       DIR8_NORTHWEST, DIR8_SOUTHEAST, DIR8_SOUTHWEST, DIR8_NORTHEAST
+      };
+      const int iso_offsets[4][2] = {
+       {W / 4, 0},
+       {W / 4, H / 2},
+       {0, H / 4},
+       {W / 2, H / 4},
+      };
+      const int noniso_offsets[4][2] = {
+       {0, 0}, {W / 2, H / 2}, {0, H / 2}, {W / 2, 0}
+      };
+      int i;
+
+      /* put coasts */
+      for (i = 0; i < 4; i++) {
+       int array_index = ((!MATCH(dir_ccw(dirs[i])) ? 1 : 0)
+                          + (!MATCH(dirs[i]) ? 2 : 0)
+                          + (!MATCH(dir_cw(dirs[i])) ? 4 : 0));
+       int x = (is_isometric ? iso_offsets[i][0] : noniso_offsets[i][0]);
+       int y = (is_isometric ? iso_offsets[i][1] : noniso_offsets[i][1]);
+
+       ADD_SPRITE(sprites.terrain[ttype]->cells[array_index][i], x, y);
+      }
 
-    /* Short-cut past dithering if all adjacent tiles are the same. */
-    if (ttype_near[DIR8_NORTH] == ttype
-       && ttype_near[DIR8_SOUTH] == ttype
-       && ttype_near[DIR8_EAST] == ttype
-       && ttype_near[DIR8_WEST] == ttype) {
-      *dither_count = 0;
+      /* Short-cut past dithering if all adjacent tiles are the same. */
+      if (!sprites.terrain[ttype]->is_layered) {
+       if (ttype_near[DIR8_NORTH] == ttype
+           && ttype_near[DIR8_SOUTH] == ttype
+           && ttype_near[DIR8_EAST] == ttype
+           && ttype_near[DIR8_WEST] == ttype) {
+         *dither_count = 0;
+       } else {
+         *dither_count = 4;
+       }
+      }
     }
+#undef MATCH
   }
 
   /* Extra "capes" added on in non-iso view. */
Index: client/tilespec.h
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/tilespec.h,v
retrieving revision 1.54
diff -u -r1.54 tilespec.h
--- client/tilespec.h   2004/02/24 05:20:24     1.54
+++ client/tilespec.h   2004/02/26 01:50:47
@@ -95,6 +95,10 @@
   DIR4_NORTH = 0, DIR4_SOUTH, DIR4_EAST, DIR4_WEST
 };
 
+enum cell_type {
+  CELL_SINGLE, CELL_RECT
+};
+
 struct terrain_drawing_data {
   char *name;
   char *mine_tag;
@@ -102,9 +106,11 @@
   bool is_blended;
   bool is_layered;
   int match_type;
+  enum cell_type cell_type;
 
   struct Sprite *base;
   struct Sprite *blend[NUM_DIRECTION_NSEW];
+  struct Sprite *cells[8][4]; /* 4 = up down left right */
   struct Sprite *special[2];
   struct Sprite *mine;
 };
@@ -211,7 +217,6 @@
       *spec_river[NUM_DIRECTION_NSEW],
       *darkness[NUM_DIRECTION_NSEW],         /* first unused */
       *river_outlet[4],                /* indexed by enum direction4 */
-      *coast_cape_iso[8][4], /* 4 = up down left right */
       /* for non-isometric */
       *coast_cape[NUM_DIRECTION_NSEW];       /* first unused */
   } tx;                                /* terrain extra */
Index: data/isotrident.tilespec
===================================================================
RCS file: /home/freeciv/CVS/freeciv/data/isotrident.tilespec,v
retrieving revision 1.10
diff -u -r1.10 isotrident.tilespec
--- data/isotrident.tilespec    2004/02/20 15:57:18     1.10
+++ data/isotrident.tilespec    2004/02/26 01:50:48
@@ -100,7 +100,8 @@
 [terrain_ocean]
 is_blended = 0
 is_layered = 0
-match_type = 5
+match_type = 6
+cell_type = "rect"
 
 [terrain_plains]
 is_blended = 1
Index: data/isotrident/terrain2.spec
===================================================================
RCS file: /home/freeciv/CVS/freeciv/data/isotrident/terrain2.spec,v
retrieving revision 1.2
diff -u -r1.2 terrain2.spec
--- data/isotrident/terrain2.spec       2004/02/17 04:53:00     1.2
+++ data/isotrident/terrain2.spec       2004/02/26 01:50:48
@@ -137,39 +137,39 @@
 is_pixel_border = 1
 
 tiles = { "row", "column","tag"
- 0, 0,  "tx.coast_cape_u0"
- 0, 2,  "tx.coast_cape_u1"
- 0, 4,  "tx.coast_cape_u2"
- 0, 6,  "tx.coast_cape_u3"
- 0, 8,  "tx.coast_cape_u4"
- 0, 10, "tx.coast_cape_u5"
- 0, 12, "tx.coast_cape_u6"
- 0, 14, "tx.coast_cape_u7"
+ 0, 0,  "t.ocean_cell_u0"
+ 0, 2,  "t.ocean_cell_u1"
+ 0, 4,  "t.ocean_cell_u2"
+ 0, 6,  "t.ocean_cell_u3"
+ 0, 8,  "t.ocean_cell_u4"
+ 0, 10, "t.ocean_cell_u5"
+ 0, 12, "t.ocean_cell_u6"
+ 0, 14, "t.ocean_cell_u7"
  
- 1, 0,  "tx.coast_cape_d0"
- 1, 2,  "tx.coast_cape_d1"
- 1, 4,  "tx.coast_cape_d2"
- 1, 6,  "tx.coast_cape_d3"
- 1, 8,  "tx.coast_cape_d4"
- 1, 10, "tx.coast_cape_d5"
- 1, 12, "tx.coast_cape_d6"
- 1, 14, "tx.coast_cape_d7"
+ 1, 0,  "t.ocean_cell_d0"
+ 1, 2,  "t.ocean_cell_d1"
+ 1, 4,  "t.ocean_cell_d2"
+ 1, 6,  "t.ocean_cell_d3"
+ 1, 8,  "t.ocean_cell_d4"
+ 1, 10, "t.ocean_cell_d5"
+ 1, 12, "t.ocean_cell_d6"
+ 1, 14, "t.ocean_cell_d7"
 
- 2, 0,  "tx.coast_cape_l0"
- 2, 2,  "tx.coast_cape_l1"
- 2, 4,  "tx.coast_cape_l2"
- 2, 6,  "tx.coast_cape_l3"
- 2, 8,  "tx.coast_cape_l4"
- 2, 10, "tx.coast_cape_l5"
- 2, 12, "tx.coast_cape_l6"
- 2, 14, "tx.coast_cape_l7"
+ 2, 0,  "t.ocean_cell_l0"
+ 2, 2,  "t.ocean_cell_l1"
+ 2, 4,  "t.ocean_cell_l2"
+ 2, 6,  "t.ocean_cell_l3"
+ 2, 8,  "t.ocean_cell_l4"
+ 2, 10, "t.ocean_cell_l5"
+ 2, 12, "t.ocean_cell_l6"
+ 2, 14, "t.ocean_cell_l7"
 
- 2, 1,  "tx.coast_cape_r0"
- 2, 3,  "tx.coast_cape_r1"
- 2, 5,  "tx.coast_cape_r2"
- 2, 7,  "tx.coast_cape_r3"
- 2, 9,  "tx.coast_cape_r4"
- 2, 11, "tx.coast_cape_r5"
- 2, 13, "tx.coast_cape_r6"
- 2, 15, "tx.coast_cape_r7"
+ 2, 1,  "t.ocean_cell_r0"
+ 2, 3,  "t.ocean_cell_r1"
+ 2, 5,  "t.ocean_cell_r2"
+ 2, 7,  "t.ocean_cell_r3"
+ 2, 9,  "t.ocean_cell_r4"
+ 2, 11, "t.ocean_cell_r5"
+ 2, 13, "t.ocean_cell_r6"
+ 2, 15, "t.ocean_cell_r7"
 }
Index: doc/README.graphics
===================================================================
RCS file: /home/freeciv/CVS/freeciv/doc/README.graphics,v
retrieving revision 1.8
diff -u -r1.8 README.graphics
--- doc/README.graphics 2004/02/17 04:53:00     1.8
+++ doc/README.graphics 2004/02/26 01:50:48
@@ -133,6 +133,15 @@
                           sprite will be chosen that matches all cardinally
                           adjacent tiles whose terrain has the same
                           match_type.
+  cell_type             : With traditional tilesets each tile is drawn using
+                          one sprite.  Which sprite to use may be specified
+                          using a match_type, and there may be multiple layers
+                          (each having one sprite).  This method corresponds
+                          to cell_type "single".  A more sophisticated drawing
+                          method breaks the tile up into 4 rectangles.  Each
+                          rectangular cell is adjacent to 3 different tiles.
+                          Each adjacency is matched, giving 8 different sprites
+                          for each of the 4 cells.
  
   Sprites
   -------
@@ -149,7 +158,15 @@
                           (e.g., "t.hills_n0s0e1w0".  Each direcional value
                           <V> is either 0 or 1.  Note that the directions are
                           in map coordinates, so n (north) in iso-view is
-                          northeast on the mapview.
+                          northeast on the mapview.  (Note this only applies
+                          for cell_type "single".)
+  cell sprites          : For matched terrains that have cell_type "rect",
+                          32 different sprites are needed.  Each sprite is
+                          a rectangle corresponding to one cell, and there are
+                          8 different sprites per cell.  Each sprite has
+                          a name like "t.ocean_cell_u5" where "ocean" is the
+                          terrain, "u" means up (north on the map) and
+                          5 indicates which of the adjacent tiles are matched.
 
 Examples:
 

[Prev in Thread] Current Thread [Next in Thread]
  • [Freeciv-Dev] (PR#7534) generalization of coasts, Jason Short <=