1 /**
2  * Retrograde Engine
3  *
4  * Authors:
5  *  Mike Bierlee, m.bierlee@lostmoment.com
6  * Copyright: 2014-2021 Mike Bierlee
7  * License:
8  *  This software is licensed under the terms of the MIT license.
9  *  The full terms of the license can be found in the LICENSE.txt file.
10  */
11 
12 module retrograde.sdl2.tilemap;
13 
14 version(Have_derelict_sdl2) {
15 
16 import retrograde.tiles;
17 import retrograde.graphics.twodee.sdl2.rendering;
18 import retrograde.math;
19 
20 import poodinis;
21 
22 import derelict.sdl2.sdl;
23 import derelict.sdl2.image;
24 
25 import std.string;
26 import std.experimental.logger;
27 
28 class Sdl2TilemapComposer {
29     private SDL_Surface*[const(Tileset)] tilesetSurfaces;
30 
31     @Autowire
32     private Sdl2RenderSystem renderer;
33 
34     @Autowire
35     private Logger logger;
36 
37     @Autowire
38     private CompositionStrategyFactory compositionStrategyFactory;
39 
40     public SDL_Texture* composeTilemapTexture(Tilemap tilemap) {
41         cleanState();
42         loadTilesetSurfaces(tilemap.tilesets);
43         SDL_Surface* composedSurface = blitLayers(tilemap);
44         if (!composedSurface) {
45             throw new TileCompositionException("Unable to compose composite surface of tilemap.");
46         }
47         SDL_Texture* composedTexture = renderer.createTextureFromSurface(composedSurface);
48         SDL_FreeSurface(composedSurface);
49         freeTilesetSurfaces();
50         return composedTexture;
51     }
52 
53     private SDL_Surface* blitLayers(Tilemap tilemap) {
54         auto compositionStrategy = compositionStrategyFactory.createStrategy(tilemap);
55         auto tilemapDimensions = compositionStrategy.getDimensions(tilemap);
56 
57         SDL_Surface* composedSurface = SDL_CreateRGBSurface(0, cast(int) tilemapDimensions.width, cast(int) tilemapDimensions.height, 32, 0, 0, 0, 0);
58         SDL_Rect destinationRectangle;
59         SDL_Rect sourceRectangle;
60         foreach (layer; tilemap.layers) {
61             ulong currentRow = 1;
62             ulong currentColumn = 1;
63 
64             foreach (tile; layer.tileData) {
65                 if (!tile.empty) {
66                     SDL_Surface* tilesetSurface = tilesetSurfaces[cast(const(Tileset)) tile.parentTileset];
67                     sourceRectangle.x = cast(int) tile.positionInTileset.x;
68                     sourceRectangle.y = cast(int) tile.positionInTileset.y;
69                     sourceRectangle.w = cast(int) tile.parentTileset.tileWidth;
70                     sourceRectangle.h = cast(int) tile.parentTileset.tileHeight;
71 
72                     auto tileDestination = compositionStrategy.getTileDestination(tilemap, currentRow, currentColumn);
73                     destinationRectangle.x = cast(int) tileDestination.x;
74                     destinationRectangle.y = cast(int) tileDestination.y;
75 
76                     SDL_BlitSurface(tilesetSurface, &sourceRectangle, composedSurface, &destinationRectangle);
77                 }
78 
79                 currentColumn += 1;
80                 if (currentColumn > tilemap.width) {
81                     currentRow += 1;
82                     currentColumn = 1;
83                 }
84             }
85         }
86 
87         return composedSurface;
88     }
89 
90     private void cleanState() {
91         tilesetSurfaces.destroy();
92     }
93 
94     private void loadTilesetSurfaces(Tileset[] tilesets) {
95         foreach (tileset; tilesets) {
96             SDL_Surface* surface = IMG_Load(tileset.imageName.toStringz());
97             if (!surface) {
98                 logger.errorf("Could not load tileset image %s", tileset.imageName);
99                 continue;
100             }
101 
102             tilesetSurfaces[tileset] = surface;
103         }
104     }
105 
106     private void freeTilesetSurfaces() {
107         foreach (surface; tilesetSurfaces) {
108             SDL_FreeSurface(surface);
109         }
110     }
111 }
112 
113 }