1 // Copyright 2013-2018 the openage authors. See copying.md for legal info.
2
3 #include "terrain.h"
4
5 #include <cmath>
6 #include <memory>
7 #include <set>
8 #include <unordered_map>
9
10 #include "../log/log.h"
11 #include "../error/error.h"
12 #include "../engine.h"
13 #include "../game_renderer.h"
14 #include "../coord/pixel.h"
15 #include "../coord/chunk.h"
16 #include "../coord/tile.h"
17 #include "../util/misc.h"
18 #include "../util/strings.h"
19
20 #include "terrain_chunk.h"
21 #include "terrain_object.h"
22
23 namespace openage {
24
TileContent()25 TileContent::TileContent() :
26 terrain_id{0} {
27 }
28
~TileContent()29 TileContent::~TileContent() {}
30
Terrain(terrain_meta * meta,bool is_infinite)31 Terrain::Terrain(terrain_meta *meta, bool is_infinite)
32 :
33 infinite{is_infinite},
34 meta{meta} {
35
36 // TODO:
37 //this->limit_positive =
38 //this->limit_negative =
39
40 // maps chunk position to chunks
41 this->chunks = std::unordered_map<coord::chunk, TerrainChunk *, coord_chunk_hash>{};
42
43 }
44
~Terrain()45 Terrain::~Terrain() {
46 log::log(MSG(dbg) << "Cleanup terrain");
47
48 for (auto &chunk : this->chunks) {
49 // this chunk was autogenerated, so clean it up
50 if (chunk.second->manually_created == false) {
51 delete chunk.second;
52 }
53 }
54 }
55
used_chunks() const56 std::vector<coord::chunk> Terrain::used_chunks() const {
57 std::vector<coord::chunk> result;
58 for (auto &c : chunks) {
59 result.push_back(c.first);
60 }
61 return result;
62 }
63
fill(const int * data,const coord::tile_delta & size)64 bool Terrain::fill(const int *data, const coord::tile_delta &size) {
65 bool was_cut = false;
66
67 coord::tile pos = {0, 0};
68 for (; pos.ne < size.ne; pos.ne++) {
69 for (pos.se = 0; pos.se < size.se; pos.se++) {
70 if (this->check_tile(pos) == tile_state::invalid) {
71 was_cut = true;
72 continue;
73 }
74 int terrain_id = data[pos.ne * size.ne + pos.se];
75 TerrainChunk *chunk = this->get_create_chunk(pos);
76 chunk->get_data(pos)->terrain_id = terrain_id;
77 }
78 }
79 return was_cut;
80 }
81
attach_chunk(TerrainChunk * new_chunk,const coord::chunk & position,bool manually_created)82 void Terrain::attach_chunk(TerrainChunk *new_chunk,
83 const coord::chunk &position,
84 bool manually_created) {
85 new_chunk->set_terrain(this);
86 new_chunk->manually_created = manually_created;
87 log::log(MSG(dbg) << "Inserting new chunk at (" << position.ne << "," << position.se << ")");
88 this->chunks[position] = new_chunk;
89
90 struct chunk_neighbors neigh = this->get_chunk_neighbors(position);
91 for (int i = 0; i < 8; i++) {
92 TerrainChunk *neighbor = neigh.neighbor[i];
93 if (neighbor != nullptr) {
94 //set the new chunks neighbor to the neighbor chunk
95 new_chunk->neighbors.neighbor[i] = neighbor;
96
97 //set the neighbors neighbor on the opposite direction
98 //to the new chunk
99 neighbor->neighbors.neighbor[(i+4) % 8] = new_chunk;
100
101 log::log(MSG(dbg) << "Neighbor " << i << " gets notified of new neighbor.");
102 }
103 else {
104 log::log(MSG(dbg) << "Neighbor " << i << " not found.");
105 }
106 }
107 }
108
get_chunk(const coord::chunk & position)109 TerrainChunk *Terrain::get_chunk(const coord::chunk &position) {
110 auto iter = this->chunks.find(position);
111
112 if (iter == this->chunks.end()) {
113 return nullptr;
114 }
115 else {
116 return iter->second;
117 }
118 }
119
get_chunk(const coord::tile & position)120 TerrainChunk *Terrain::get_chunk(const coord::tile &position) {
121 return this->get_chunk(position.to_chunk());
122 }
123
get_create_chunk(const coord::chunk & position)124 TerrainChunk *Terrain::get_create_chunk(const coord::chunk &position) {
125 TerrainChunk *res = this->get_chunk(position);
126 if (res == nullptr) {
127 res = new TerrainChunk();
128 this->attach_chunk(res, position, false);
129 }
130 return res;
131 }
132
get_create_chunk(const coord::tile & position)133 TerrainChunk *Terrain::get_create_chunk(const coord::tile &position) {
134 return this->get_create_chunk(position.to_chunk());
135 }
136
get_data(const coord::tile & position)137 TileContent *Terrain::get_data(const coord::tile &position) {
138 TerrainChunk *c = this->get_chunk(position.to_chunk());
139 if (c == nullptr) {
140 return nullptr;
141 } else {
142 return c->get_data(position.get_pos_on_chunk());
143 }
144 }
145
obj_at_point(const coord::phys3 & point)146 TerrainObject *Terrain::obj_at_point(const coord::phys3 &point) {
147 coord::tile t = point.to_tile();
148 TileContent *tc = this->get_data(t);
149 if (!tc) {
150 return nullptr;
151 }
152
153 // prioritise selecting the smallest object
154 TerrainObject *smallest = nullptr;
155 for (auto obj_ptr : tc->obj) {
156 if (obj_ptr->contains(point) &&
157 (!smallest || obj_ptr->min_axis() < smallest->min_axis())) {
158 smallest = obj_ptr;
159 }
160 }
161 return smallest;
162 }
163
validate_terrain(terrain_t terrain_id)164 bool Terrain::validate_terrain(terrain_t terrain_id) {
165 if (terrain_id >= (ssize_t)this->meta->terrain_id_count) {
166 throw Error(MSG(err) << "Requested terrain_id is out of range: " << terrain_id);
167 }
168 else {
169 return true;
170 }
171 }
172
validate_mask(ssize_t mask_id)173 bool Terrain::validate_mask(ssize_t mask_id) {
174 if (mask_id >= (ssize_t)this->meta->blendmode_count) {
175 throw Error(MSG(err) << "Requested mask_id is out of range: " << mask_id);
176 }
177 else {
178 return true;
179 }
180 }
181
priority(terrain_t terrain_id)182 int Terrain::priority(terrain_t terrain_id) {
183 this->validate_terrain(terrain_id);
184 return this->meta->terrain_id_priority_map[terrain_id];
185 }
186
blendmode(terrain_t terrain_id)187 int Terrain::blendmode(terrain_t terrain_id) {
188 this->validate_terrain(terrain_id);
189 return this->meta->terrain_id_blendmode_map[terrain_id];
190 }
191
texture(terrain_t terrain_id)192 Texture *Terrain::texture(terrain_t terrain_id) {
193 this->validate_terrain(terrain_id);
194 return this->meta->textures[terrain_id];
195 }
196
blending_mask(ssize_t mask_id)197 Texture *Terrain::blending_mask(ssize_t mask_id) {
198 this->validate_mask(mask_id);
199 return this->meta->blending_masks[mask_id];
200 }
201
get_subtexture_id(const coord::tile & pos,unsigned atlas_size)202 unsigned Terrain::get_subtexture_id(const coord::tile &pos, unsigned atlas_size) {
203 unsigned result = 0;
204
205 result += util::mod<coord::tile_t>(pos.se, atlas_size);
206 result *= atlas_size;
207 result += util::mod<coord::tile_t>(pos.ne, atlas_size);
208
209 return result;
210 }
211
get_chunk_neighbors(const coord::chunk & position)212 struct chunk_neighbors Terrain::get_chunk_neighbors(const coord::chunk &position) {
213 struct chunk_neighbors ret;
214
215 for (int i = 0; i < 8; i++) {
216 coord::chunk tmp {
217 position.ne + (coord::chunk_t) neigh_offsets[i].ne,
218 position.se + (coord::chunk_t) neigh_offsets[i].se
219 };
220 ret.neighbor[i] = this->get_chunk(tmp);
221 }
222
223 return ret;
224 }
225
get_blending_mode(terrain_t base_id,terrain_t neighbor_id)226 int Terrain::get_blending_mode(terrain_t base_id, terrain_t neighbor_id) {
227 /*
228 * this function may require much more code, but this simple
229 * magnitude comparison seems to do the job.
230 * feel free to confirm or fix the behavior.
231 *
232 * my guess is that the blending mode encodes another information
233 * not publicly noticed yet: the overlay priority.
234 * the higher the blendmode id, the higher the mode priority.
235 * this may also be the reason why there are mask duplicates
236 * in blendomatic.dat
237 *
238 * funny enough, just using the modes in the dat file lead
239 * to a totally wrong render. the convert script reassigns the
240 * blending modes with a simple key=>val mapping,
241 * and after that, it looks perfect.
242 */
243
244 int base_mode = this->blendmode(base_id);
245 int neighbor_mode = this->blendmode(neighbor_id);
246
247 if (neighbor_mode > base_mode) {
248 return neighbor_mode;
249 } else {
250 return base_mode;
251 }
252 }
253
check_tile(const coord::tile & position)254 tile_state Terrain::check_tile(const coord::tile &position) {
255 if (this->check_tile_position(position) == false) {
256 return tile_state::invalid;
257 }
258 else {
259 TerrainChunk *chunk = this->get_chunk(position);
260 if (chunk == nullptr) {
261 return tile_state::creatable;
262 }
263 else {
264 return tile_state::existing;
265 }
266 }
267 }
268
check_tile_position(const coord::tile &)269 bool Terrain::check_tile_position(const coord::tile &/*pos*/) {
270 if (this->infinite == true) {
271 return true;
272 }
273 else {
274 throw Error(ERR << "non-infinite terrains are not supported yet");
275 }
276 }
277
draw(Engine * engine,RenderOptions * settings)278 void Terrain::draw(Engine *engine, RenderOptions *settings) {
279 // TODO: move this draw invokation to a render manager.
280 // it can reorder the draw instructions and minimize texture switching.
281
282 // query the window coordinates from the engine first
283 coord::viewport wbl = coord::viewport{0, 0};
284 coord::viewport wbr = coord::viewport{engine->coord.viewport_size.x, 0};
285 coord::viewport wtl = coord::viewport{0, engine->coord.viewport_size.y};
286 coord::viewport wtr = coord::viewport{engine->coord.viewport_size.x, engine->coord.viewport_size.y};
287
288 // top left, bottom right tile coordinates
289 // that are currently visible in the window
290 // then convert them to tile coordinates.
291 coord::tile tl = wtl.to_tile(engine->coord);
292 coord::tile tr = wtr.to_tile(engine->coord);
293 coord::tile bl = wbl.to_tile(engine->coord);
294 coord::tile br = wbr.to_tile(engine->coord);
295
296 // main terrain calculation call: get the `terrain_render_data`
297 auto draw_data = this->create_draw_advice(tl, tr, br, bl, settings->terrain_blending.value);
298
299 // TODO: the following loop is totally inefficient and shit.
300 // it reloads the drawing texture to the gpu FOR EACH TILE!
301 // nevertheless, currently it works.
302
303 // draw the terrain ground
304 for (auto &tile : draw_data.tiles) {
305
306 // iterate over all layers to be drawn
307 for (int i = 0; i < tile.count; i++) {
308 struct tile_data *layer = &tile.data[i];
309
310 // position, where the tile is drawn
311 coord::tile tile_pos = layer->pos;
312
313 int mask_id = layer->mask_id;
314 Texture *texture = layer->tex;
315 int subtexture_id = layer->subtexture_id;
316 Texture *mask_texture = layer->mask_tex;
317
318 texture->draw(engine->coord, *this, tile_pos, ALPHAMASKED, subtexture_id, mask_texture, mask_id);
319 }
320 }
321
322 // TODO: drawing buildings can't be the job of the terrain..
323 // draw the buildings
324 for (auto &object : draw_data.objects) {
325 object->draw(*engine);
326 }
327 }
328
create_draw_advice(const coord::tile & ab,const coord::tile & cd,const coord::tile & ef,const coord::tile & gh,bool blending_enabled)329 struct terrain_render_data Terrain::create_draw_advice(const coord::tile &ab,
330 const coord::tile &cd,
331 const coord::tile &ef,
332 const coord::tile &gh,
333 bool blending_enabled) {
334
335 /*
336 * The passed parameters define the screen corners.
337 *
338 * ne, se coordinates
339 * o = screen corner, where the tile coordinates can be queried.
340 * x = corner of the rhombus that will be drawn, calculated by all o.
341 *
342 * cb
343 * x
344 * . .
345 * . .
346 * ab o===========o cd
347 * . = visible = .
348 * gb x = screen = x cf
349 * . = = .
350 * gh o===========o ef
351 * . .
352 * . .
353 * x
354 * gf
355 *
356 * The rendering area may be optimized further in the future,
357 * to exactly fit the visible screen.
358 * For now, we are drawing the big rhombus.
359 */
360
361 // procedure: find all the tiles to be drawn
362 // and store them to a tile drawing instruction structure
363 struct terrain_render_data data;
364
365 // vector of tiles to be drawn
366 std::vector<struct tile_draw_data> *tiles = &data.tiles;
367
368 // ordered set of objects on the terrain (buildings.)
369 // it's ordered by the visibility layers.
370 auto objects = &data.objects;
371
372 coord::tile gb = {gh.ne, ab.se};
373 coord::tile cf = {cd.ne, ef.se};
374
375 // hint the vector about the number of tiles it will contain
376 size_t tiles_count = std::abs(cf.ne - gb.ne) * std::abs(cf.se - gb.se);
377 tiles->reserve(tiles_count);
378
379 // sweep the whole rhombus area
380 for (coord::tile tilepos = gb; tilepos.ne <= cf.ne; tilepos.ne++) {
381 for (tilepos.se = gb.se; tilepos.se <= cf.se; tilepos.se++) {
382
383 // get the terrain tile drawing data
384 auto tile = this->create_tile_advice(tilepos, blending_enabled);
385 tiles->push_back(tile);
386
387 // get the object standing on the tile
388 // TODO: make the terrain independent of objects standing on it.
389 TileContent *tile_content = this->get_data(tilepos);
390 if (tile_content != nullptr) {
391 for (auto obj_item : tile_content->obj) {
392 objects->insert(obj_item);
393 }
394 }
395 }
396 }
397
398 return data;
399 }
400
401
create_tile_advice(coord::tile position,bool blending_enabled)402 struct tile_draw_data Terrain::create_tile_advice(coord::tile position, bool blending_enabled) {
403 // this struct will be filled with all tiles and overlays to draw.
404 struct tile_draw_data tile;
405 tile.count = 0;
406
407 TileContent *base_tile_content = this->get_data(position);
408
409 // chunk of this tile does not exist
410 if (base_tile_content == nullptr) {
411 return tile;
412 }
413
414 struct tile_data base_tile_data;
415
416 // the base terrain id of the tile
417 base_tile_data.terrain_id = base_tile_content->terrain_id;
418
419 // the base terrain is not existant.
420 if (base_tile_data.terrain_id < 0) {
421 return tile;
422 }
423
424 this->validate_terrain(base_tile_data.terrain_id);
425
426 Texture *tex = this->texture(base_tile_data.terrain_id);
427
428 base_tile_data.state = tile_state::existing;
429 base_tile_data.pos = position;
430 base_tile_data.priority = this->priority(base_tile_data.terrain_id);
431 base_tile_data.tex = tex;
432 base_tile_data.subtexture_id = this->get_subtexture_id(
433 position,
434 std::sqrt(tex->get_subtexture_count())
435 );
436 base_tile_data.blend_mode = -1;
437 base_tile_data.mask_tex = nullptr;
438 base_tile_data.mask_id = -1;
439
440 tile.data[tile.count] = base_tile_data;
441 tile.count += 1;
442
443 // blendomatic!!111
444 // see doc/media/blendomatic for the idea behind this.
445 if (blending_enabled) {
446
447 // the neighbors of the base tile
448 struct neighbor_tile neigh_data[8];
449
450 // get all neighbor tiles around position, reset the influence directions.
451 this->get_neighbors(position, neigh_data, this->meta->influences_buf.get());
452
453 // create influence list (direction, priority)
454 // strip and order influences, get the final influence data structure
455 struct influence_group influence_group = this->calculate_influences(
456 &base_tile_data, neigh_data,
457 this->meta->influences_buf.get()
458 );
459
460 // create the draw_masks from the calculated influences
461 this->calculate_masks(position, &tile, &influence_group);
462 }
463
464 return tile;
465 }
466
get_neighbors(coord::tile basepos,neighbor_tile * neigh_data,influence * influences_by_terrain_id)467 void Terrain::get_neighbors(coord::tile basepos,
468 neighbor_tile *neigh_data,
469 influence *influences_by_terrain_id) {
470
471 // walk over all given neighbor tiles and store them to the influence list,
472 // group them by terrain id.
473
474 for (int neigh_id = 0; neigh_id < 8; neigh_id++) {
475
476 // the current neighbor
477 auto neighbor = &neigh_data[neigh_id];
478
479 // calculate the pos of the neighbor tile
480 coord::tile neigh_pos = basepos + neigh_offsets[neigh_id];
481
482 // get the neighbor data
483 TileContent *neigh_content = this->get_data(neigh_pos);
484
485 // chunk for neighbor or single tile is not existant
486 if (neigh_content == nullptr || neigh_content->terrain_id < 0) {
487 neighbor->state = tile_state::missing;
488 }
489 else {
490 neighbor->terrain_id = neigh_content->terrain_id;
491 neighbor->state = tile_state::existing;
492 neighbor->priority = this->priority(neighbor->terrain_id);
493
494 // reset influence directions for this tile
495 influences_by_terrain_id[neighbor->terrain_id].direction = 0;
496 }
497 }
498 }
499
calculate_influences(struct tile_data * base_tile,struct neighbor_tile * neigh_data,struct influence * influences_by_terrain_id)500 struct influence_group Terrain::calculate_influences(struct tile_data *base_tile,
501 struct neighbor_tile *neigh_data,
502 struct influence *influences_by_terrain_id) {
503 // influences to actually draw (-> maximum 8)
504 struct influence_group influences;
505 influences.count = 0;
506
507 // process adjacent neighbors first,
508 // then add diagonal influences, if no adjacent influence was found
509 constexpr int neigh_id_lookup[] = {1, 3, 5, 7, 0, 2, 4, 6};
510
511 for (int i = 0; i < 8; i++) {
512 // diagonal neighbors: (neigh_id % 2) == 0
513 // adjacent neighbors: (neigh_id % 2) == 1
514
515 int neigh_id = neigh_id_lookup[i];
516 bool is_adjacent_neighbor = neigh_id % 2 == 1;
517 bool is_diagonal_neighbor = not is_adjacent_neighbor;
518
519 // the current neighbor_tile.
520 auto neighbor = &neigh_data[neigh_id];
521
522 // neighbor is nonexistant
523 if (neighbor->state == tile_state::missing) {
524 continue;
525 }
526
527 // neighbor only interesting if it's a different terrain than the base.
528 // if it is the same id, the priorities are equal.
529 // neighbor draws over the base if it's priority is greater.
530 if (neighbor->priority > base_tile->priority) {
531
532 // get influence storage for the neighbor terrain id
533 // to group influences by id
534 auto influence = &influences_by_terrain_id[neighbor->terrain_id];
535
536 // check if diagonal influence is valid
537 if (is_diagonal_neighbor) {
538 // get the adjacent neighbors to the current diagonal
539 // influence
540 // (a & 0x07) == (a % 8)
541 uint8_t adj_neigh_0 = (neigh_id - 1) & 0x07;
542 uint8_t adj_neigh_1 = (neigh_id + 1) & 0x07;
543
544 uint8_t neigh_mask = (1 << adj_neigh_0) | (1 << adj_neigh_1);
545
546 // the adjacent neigbors are already influencing
547 // the current tile, therefore don't apply the diagonal mask
548 if ((influence->direction & neigh_mask) != 0) {
549 continue;
550 }
551 }
552
553 // this terrain id hasn't had influence so far:
554 // add it to the list of influences.
555 if (influence->direction == 0) {
556 influences.terrain_ids[influences.count] = neighbor->terrain_id;
557 influences.count += 1;
558 }
559
560 // as tile i has influence for this priority
561 // => bit i is set to 1 by 2^i
562 influence->direction |= 1 << neigh_id;
563 influence->priority = neighbor->priority;
564 influence->terrain_id = neighbor->terrain_id;
565 }
566 }
567
568 // influences_by_terrain_id will be merged in the following,
569 // unused terrain ids will be dropped now.
570
571 // shrink the big influence buffer that had entries for all terrains
572 // by copying the possible (max 8) influences to a separate buffer.
573 for (int k = 0; k < influences.count; k++) {
574 int relevant_id = influences.terrain_ids[k];
575 influences.data[k] = influences_by_terrain_id[relevant_id];
576 }
577
578 // order the influences by their priority
579 for (int k = 1; k < influences.count; k++) {
580 struct influence tmp_influence = influences.data[k];
581
582 int l = k - 1;
583 while (l >= 0 && influences.data[l].priority > tmp_influence.priority) {
584 influences.data[l + 1] = influences.data[l];
585 l -= 1;
586 }
587
588 influences.data[l + 1] = tmp_influence;
589 }
590
591 return influences;
592 }
593
594
calculate_masks(coord::tile position,struct tile_draw_data * tile_data,struct influence_group * influences)595 void Terrain::calculate_masks(coord::tile position,
596 struct tile_draw_data *tile_data,
597 struct influence_group *influences) {
598
599 // influences are grouped by terrain id.
600 // the direction member has each bit set to 1 that is an influence from that direction.
601 // create a mask for this direction combination.
602
603 // the base tile is stored at position 0 of the draw_mask
604 terrain_t base_terrain_id = tile_data->data[0].terrain_id;
605
606 // iterate over all neighbors (with different terrain_ids) that have influence
607 for (ssize_t i = 0; i < influences->count; i++) {
608
609 // neighbor id of the current influence
610 char direction_bits = influences->data[i].direction;
611
612 // all bits are 0 -> no influence directions stored.
613 // => no influence can be ignored.
614 if (direction_bits == 0) {
615 continue;
616 }
617
618 terrain_t neighbor_terrain_id = influences->data[i].terrain_id;
619 int adjacent_mask_id = -1;
620
621 /* neighbor ids:
622 0
623 7 1 => 8 neighbors that can have influence on
624 6 @ 2 the mask id selection.
625 5 3
626 4
627 */
628
629 // filter adjacent and diagonal influences neighbor_id: 76543210
630 uint8_t direction_bits_adjacent = direction_bits & 0xAA; //0b10101010
631 uint8_t direction_bits_diagonal = direction_bits & 0x55; //0b01010101
632
633 switch (direction_bits_adjacent) {
634 case 0x08: //0b00001000
635 adjacent_mask_id = 0; //0..3
636 break;
637 case 0x02: //0b00000010
638 adjacent_mask_id = 4; //4..7
639 break;
640 case 0x20: //0b00100000
641 adjacent_mask_id = 8; //8..11
642 break;
643 case 0x80: //0b10000000
644 adjacent_mask_id = 12; //12..15
645 break;
646 case 0x22: //0b00100010
647 adjacent_mask_id = 20;
648 break;
649 case 0x88: //0b10001000
650 adjacent_mask_id = 21;
651 break;
652 case 0xA0: //0b10100000
653 adjacent_mask_id = 22;
654 break;
655 case 0x82: //0b10000010
656 adjacent_mask_id = 23;
657 break;
658 case 0x28: //0b00101000
659 adjacent_mask_id = 24;
660 break;
661 case 0x0A: //0b00001010
662 adjacent_mask_id = 25;
663 break;
664 case 0x2A: //0b00101010
665 adjacent_mask_id = 26;
666 break;
667 case 0xA8: //0b10101000
668 adjacent_mask_id = 27;
669 break;
670 case 0xA2: //0b10100010
671 adjacent_mask_id = 28;
672 break;
673 case 0x8A: //0b10001010
674 adjacent_mask_id = 29;
675 break;
676 case 0xAA: //0b10101010
677 adjacent_mask_id = 30;
678 break;
679 }
680
681 // if it's the linear adjacent mask, cycle the 4 possible masks.
682 // e.g. long shorelines don't look the same then.
683 // maskid == 0x08 0x02 0x80 0x20 for that.
684 if (adjacent_mask_id <= 12 && adjacent_mask_id % 4 == 0) {
685 //we have 4 = 2^2 anti redundancy masks, so keep the last 2 bits
686 uint8_t anti_redundancy_offset = (position.ne + position.se) & 0x03;
687 adjacent_mask_id += anti_redundancy_offset;
688 }
689
690 // get the blending mode (the mask selection) for this transition
691 // the mode is dependent on the two meeting terrain types
692 int blend_mode = this->get_blending_mode(base_terrain_id, neighbor_terrain_id);
693
694 // append the mask for the adjacent blending
695 if (adjacent_mask_id >= 0) {
696 struct tile_data *overlay = &tile_data->data[tile_data->count];
697 overlay->pos = position;
698 overlay->mask_id = adjacent_mask_id;
699 overlay->blend_mode = blend_mode;
700 overlay->terrain_id = neighbor_terrain_id;
701 overlay->tex = this->texture(neighbor_terrain_id);
702 overlay->subtexture_id = this->get_subtexture_id(
703 position,
704 std::sqrt(overlay->tex->get_subtexture_count())
705 );
706 overlay->mask_tex = this->blending_mask(blend_mode);
707 overlay->state = tile_state::existing;
708
709 tile_data->count += 1;
710 }
711
712 // append the mask for the diagonal blending
713 if (direction_bits_diagonal > 0) {
714 for (int l = 0; l < 4; l++) {
715 // generate one mask for each influencing diagonal neighbor id.
716 // even if they all have the same terrain_id,
717 // because we don't have combined diagonal influence masks.
718
719 // l == 0: pos = 0b000000001, mask = 18
720 // l == 1: pos = 0b000000100, mask = 16
721 // l == 2: pos = 0b000010000, mask = 17
722 // l == 3: pos = 0b001000000, mask = 19
723
724 int current_direction_bit = 1 << (l*2);
725 constexpr int diag_mask_id_map[4] = {18, 16, 17, 19};
726
727 if (direction_bits_diagonal & current_direction_bit) {
728 struct tile_data *overlay = &tile_data->data[tile_data->count];
729 overlay->pos = position;
730 overlay->mask_id = diag_mask_id_map[l];
731 overlay->blend_mode = blend_mode;
732 overlay->terrain_id = neighbor_terrain_id;
733 overlay->tex = this->texture(neighbor_terrain_id);
734 overlay->subtexture_id = this->get_subtexture_id(
735 position,
736 std::sqrt(overlay->tex->get_subtexture_count())
737 );
738 overlay->mask_tex = this->blending_mask(blend_mode);
739 overlay->state = tile_state::existing;
740
741 tile_data->count += 1;
742 }
743 }
744 }
745 }
746 }
747
748 } // namespace openage
749