blendomatic
===========


or: how to merge terrain tile edges.

why do we need that?
--------------------

the terrain.drs stores all the terrains as slp files, see the doc/media/terrain
for that.


placing these tiles beside each other works fine, if you use only one terrain (e.g. grass).


if two different terrains are next to each other, they need to be blended.
water and land will be blended to water and shore, etcetc.

this procedure allows merging any terrain type with any other.


blending procedure
------------------

blending terrain tiles works by using alpha masks.
to create a "transition tile", a base tile and an alpha-masked overlay tile are merged.
the transition tile is a mixture of the two adjacent terrain tiles,
where one of them gets drawn over partially by the neighbor.


each terrain type has a priority level (see table at the bottom).

the masked higher level tile is copied on top of the lower level tile.

this means that parts of the highlevel tile are visible in the original area
of the lower level tile, creating a smooth transition.

example:

######@@@@@@
######@@@@@@
######@@@@@@
######@@@@@@

let # be terrain with priority 8, and @ has priority 42.

@ has a higher priority than #, meaning that @ is alpha masked before copying.

mask:    tile:

000111   @@@@@@             @@@
001111   @@@@@@  masked    @@@@
000111   @@@@@@   ===>      @@@
000001   @@@@@@               @

(note that the mask actually has byte values for alpha blending, not just binary "draw" and "hide")

for the real blending masks, see below in section "blending directions".


the alpha-masked @-terrain is then drawn at the neighbor's position, on top of it.

###@@@@@@@@@
##@@@@@@@@@@
###@@@@@@@@@
#####@@@@@@@

you can see that this creates a transition from the two textures,
where higher prioritized terrains "flood" on their neighbor tile.

if you imagine @ as water, and # as shore, it's literally flooding...


which alpha mask is determined by two conditions:
- the tile positioning
    => what direction will the transition be?
- the blending mode
    => how does the transition look like?
    => associated to terrain class -> ice needs other edges than shore
    => which mode is used does not depend on the terrain priority,
       it depends on the meeting classes.
       e.g. when blending with ice,
       the ice blend mode is applied to the other blending terrain.
    * existing blending modes (mask shapes):
      - mode 0: rough transition, full, used for dirt, grass, ...
      - mode 1: same as mode 0
      - mode 2: smooth transition, full length
      - mode 3: smooth transition, short
      - mode 4: rough hard edges, spraylike
      - mode 5: sharp edges
      - mode 6: same as mode 4
      - mode 7: same as mode 0
      - mode 8: same as mode 0
      => you can see that we actually have 5 blending modes.
    * assigning the correct value:
      = tile type     stored mode    displayed mode
      - grass         0              0/1/7/8
      - farm          1              3
      - beach         2              2
      - water         3              0/1/7/8
      - shallows      4              ?
      - snow road     5              4/6
      - ice           6              5
      - snow          7              4/6



blending directions
-------------------

non-blended tile:

      #
    #####
  #########
#############
  #########
    #####
      #


blendomatic stores the following alpha masks:

id is the tile number for the 31 tiles within one blending mode.


0 means: keep the base tile pixel, don't overdraw      (mask 0)
# means: use this pixel from the dominant neighbor     (mask 1)
         to draw over the base tile

the id's 0..15 describe only 4 directions, but have 16 tiles.
these were created to avoid the obviously repeating pattern.
method to choose one of the 4:
-> use lower 2 bit of tile destination x or y coordinates.

id: 0..3           4..7           8..11          12..15
  lower right   upper right    lower left     upper left

      0              #              0              #
    00000          0####          00000          ####0
  000000000      00000####      000000000      ####00000
000000000####  000000000####  ####000000000  ####000000000
  00000####      000000000      ####00000      000000000
    0####          00000          ####0          00000
      #              0              #              0

id:  16             17             18             19
    right          down            up            left

      0              0              #              0
    00000          00000          #####          00000
  00000000#      000000000      000###000      #00000000
000000000####  0000000000000  0000000000000  ####000000000
  00000000#      000###000      000000000      #00000000
    00000          #####          00000          00000
      0              #              0              0

id:  20             21             22             23             24             25
  upperright     upperleft      onlyright      onlydown        onlyup        onlyleft
  lowerleft      lowerright

      #              #              #              #              0              #
    0####          ####0          ####0          #####          00000          0####
  00000####      ####00000      ######000      #########      000000000      000######
####00000####  ####00000####  ########00000  #############  #############  00000########
  ####00000      00000####      ######000      000000000      #########      000######
    ####0          0####          ####0          00000          #####          0####
      #              #              #              0              #              #

id:  26             27             28             29             30
    keep           keep           keep           keep           all
   upperleft     upperright     lowerright     lowerleft

      #              #              #              #              #
    0####          ####0          #####          #####          #####
  00000####      ####00000      ####0####      ####0####      ####0####
####00000####  ####00000####  ####00000####  ####00000####  ####00000####
  ####0####      ####0####      ####00000      00000####      ####0####
    #####          #####          ####0          0####          #####
      #              #              #              #              #


these 31 tiles are used to alphamask all possible terrain junctions.


blending algorithm
------------------


@ = tile to draw next

      0
    7   1      => 8 neighbours that have influence on
  6   @   2         the mask id selection.
    5   3
      4

for @ in alltiles:

	#draw the base tile:
	@.draw()

	#storage for influences by neighbor tiles
	influence = dict()

	#first step: gather information about possible influences
	#look at every neighbor tile for that
	for i in [0..7]:

		#neighbor only interesting if it's a different terrain than @
		if i.terrain_type != @.terraintype:

			#adjacent tile will draw onto @, as it's priority is higher
			#else, ignore this neighbor
			if i.priority > @.priority:

				if i.is_diagonal_influence:
					#get the ids of the adjacent neighbors of the diagonal
					#influence:
					i_neighbors = map(lambda x: x % 8, [i - 1, i + 1])

					if any of i_neighbors have influence:
						#don't apply diagonal influence i, as any of its
						#neighbors already influences the tile.
						continue

				#as tile i has influence for this priority
				# => bit i is set to 1 by 2^i (== 1 << i)
				#each priority is drawn seperately later.
				influence[i.terrain_id] |= 2**i

	#sort influences by priority, so that higher priorities get drawn last.
	influence = sorted(influence, by=influence.priority)

	#now: we got all influences, grouped by terrain priority.
	#for each of these influences, we continue finding the blendomatic mask
	# and apply it to the neighbors texture,
	# then draw the masked tile on top of the base (@) tile.

	#the terrain_id has influences coming from directions 'binf',
	#so we can select directional masks for that terrain.
	for terrain_id, binf in influence.items():

		#there is exactly one adjacent mask id for all combinations
		adjacent_mask_id  = []
		diagonal_mask_ids = []

		#find mask id by influencing neighbor tiles
		#                           neighbor id: 76543210
		adjacent_mask_id = [0 .. 3] if binf == 0b00001000
		           .add()  [4 .. 7] if binf == 0b00000010
		                   [8 ..11] if binf == 0b00100000
		                   [12..15] if binf == 0b10000000
		                   20       if binf == 0b00100010
		                   21       if binf == 0b10001000
		                   22       if binf == 0b10100000
		                   23       if binf == 0b10000010
		                   24       if binf == 0b00101000
		                   25       if binf == 0b00001010
		                   26       if binf == 0b00101010
		                   27       if binf == 0b10101000
		                   28       if binf == 0b10100010
		                   29       if binf == 0b10001010
		                   30       if binf == 0b10101010

		diagonal_mask_id.add(16)    if binf  & 0b00000100 > 0
		                     17     if binf  & 0b00010000 > 0
		                     18     if binf  & 0b00000001 > 0
		                     19     if binf  & 0b01000000 > 0


		#which of the 9 blending modes to use?
		#the selected blending mode is depending on which tiles meet.
		# (water->shore != water->ice)
		# terrain class => Land, Farm, Beach, Road, Water, Ice, ..
		#this means to determine:
		# use i.blendmode or @.blendmode
		#  when i is drawn onto @ later.
		blendmode    = get_blending_mode(priority, @)
		neighbortile = get_terrain_by_priority(priority)

		#all masks to draw: one adjacent mask xor 1 to 4 diagonal masks
		draw_masks = adjacent_mask_id + diagonal_mask_ids

		for maskid in draw_masks:
			#do the tile blending:
			#mask away pixels by applying the combined mask
			maskdata = mask[blendmode][maskid]

			#draw the masked terrain piece on top of our base (@) tile
			overlay_data = apply_mask(neighbortile.data, maskdata)
			overlay_data.draw()



the described alpha masks, are stored in blendomatic.dat:

blendomatic.dat file format
---------------------------

struct {
	struct {
		unsigned int nr_blending_modes;              //normally 9
		unsigned int nr_tiles;                       //normally 31
	} blendomatic_header;

	struct {
		unsigned int  tile_size;                     //normally 2353
		unsigned char tile_flags[nr_tiles];

		struct {
			uint8_t alpha_bitmask[tile_size / 8];    //with tile_size pixels, use the data bitwise.
		} tile_bitmasks[32];                         //why 32? maybe nr_tiles + 1?

		struct {
			uint8_t alpha_bytemap[tile_size];        //7-bit alpha value pixels
		} tile_bytemasks[nr_tiles];
	} blending_modes[nr_blending_modes]
} blendomatic.dat;


the alpha_bytemap array contains values to be drawn as *
we have to add the spaces (.) ourselve.

with the default tile_size, we end up drawing the rhombus with 49 rows.


the resulting tile is then used to overlay on a regular terrain tile, so that parts are alphamasked.

alpha_bitmasks selects which bytes from each tile are used,
the alpha_bytemap determines how much to blend.

this is our drawing goal (of course a bit bigger):

....*....
..*****..
*********
..*****..
....*....

each * is a pixel, with a 7 or 1 bit value, which just is an alpha threshold.

for alpha_bytemap:
--> 128 == when masking the dominant texture, draw the pixel fully opaque.
-->   0 == keep the base tile pixel here.

for alpha_bitmasks:
-->   1 == this pixel will be overdrawn by dominant neighbor.
-->   0 == keep the base tile pixel.
