I want to implement an isometric map with rectangular tiles similar to how it was in simcity 2000, where tiles could have different heights and tiles with different heights were connected by slopes. Here's an example of a hill in this game:

[![][1]][1]

These hills can form complicated structures for things like mountains:[![][2]][2]

I also want to be able to select tiles with mouse. I used this solution for a flat map:
```
auto realMapPos = worldPos_;
realMapPos.x -= m_tileSize.x / 2.0f;
realMapPos.y -= m_tileSize.y / 2.0f;
realMapPos -= m_origin;
Vector2<int> res;
res.x = round((realMapPos.x / m_tileSize.x) - (realMapPos.y / m_tileSize.y));
res.y = round((realMapPos.x / m_tileSize.x) + (realMapPos.y / m_tileSize.y));
```
But it doesn't work for tiles with height.

### Solutions I've found: ###

 1. [This question][3]. The proposed solution does not work because slopes may have different silhouettes so you can't simplify every tile into lines with same angles

 2. [This solution][4]. As I understand, I'd have to create a second rectangular texture around each tile and use color data to represent central tile and its neighbours, but I don't think it would work well in my case because tile can be drawn far from its actual position due to height or it can be completely covered by a large hill in front of it. 
### The solution I've done by myself: ###
**Short version:**
I split my map into small chunks 100x100 pixels and use their red and green channels to store info about respective tiles

**Full version:**
I have these fields in my `TownLevel` class with map:
```
std::unique_ptr<Texture> m_mouseMap; // Full map
std::vector<std::unique_ptr<Texture>> m_mouseMapChunks; // Array of small chunks
Vector2<int> m_mouseChunkSize = {100, 100}; // Size of a single chunk
Vector2<int> m_mouseChunkMapSize; // Number of chunks along each axis
```
I render an entire map to one texture and split it into chunks. I can't use full map by itself because `SDL_RenderReadPixels()` works too slowly with large textures. The full map is not necessary and can break my code on other systems due to SDL_Texture size limitations, I'll remove it later. Also, I use a `Texture` class for all textures.
I create full map and chunks in the constructor:
```
 // Make full mouse map texture
 m_mouseMap = std::make_unique<Texture>(application_->getRenderer()->createTexture(size_.x, size_.y));
 //m_mouseMap = std::make_unique<Texture>(application_->getRenderer()->createTexture(1000, 1000));
 if (m_mouseMap->getSprite() == nullptr)
 {
 std::cout << "Failed to create mouse map\n";
 }
 else
 {
 SDL_SetTextureBlendMode(m_mouseMap->getSprite(), SDL_BLENDMODE_BLEND);
 renderer.setRenderTarget(m_mouseMap->getSprite());
 renderer.fillRenderer({0, 0, 0, 0});
 renderer.setRenderTarget(nullptr);
 }

 // Split map into small chunks
 m_mouseChunkMapSize.x = size_.x / m_mouseChunkSize.x;
 m_mouseChunkMapSize.y = size_.y / m_mouseChunkSize.y;

 // Create textures for chunks
 for (int i = 0; i < m_mouseChunkMapSize.x * m_mouseChunkMapSize.y; ++i)
 {
 auto chunk = std::make_unique<Texture>(application_->getRenderer()->createTexture(m_mouseChunkSize.x, m_mouseChunkSize.y));
 SDL_SetTextureBlendMode(chunk->getSprite(), SDL_BLENDMODE_BLEND);
 renderer.setRenderTarget(chunk->getSprite());
 renderer.fillRenderer({0, 0, 0, 0});
 m_mouseMapChunks.push_back(std::move(chunk));
 }

 renderer.setRenderTarget(nullptr);
```
`Texture` class has a method to generate a pure white version of a texture:
```
	void generateWhiteTexture(Renderer &renderer_)
	{
		if (whiteTex != nullptr)
			return;

		std::cout << "Created white " << w << " " << h << std::endl;

		whiteTex = renderer_.createTexture(w, h);

		auto blendmode = SDL_ComposeCustomBlendMode(SDL_BLENDFACTOR_ZERO, SDL_BLENDFACTOR_SRC_ALPHA, SDL_BLENDOPERATION_MAXIMUM, SDL_BLENDFACTOR_ONE, SDL_BLENDFACTOR_ZERO, SDL_BLENDOPERATION_ADD);

		renderer_.setRenderTarget(whiteTex);
 	renderer_.fillRenderer({255, 255, 255, 0});

		// Every pixel from original texture turns into {255 * a, 255 * a, 255 * a, a} (a is alpha)
 SDL_SetTextureBlendMode(tex, blendmode);
 renderer_.renderTexture(tex, 0, 0, w, h);
 SDL_SetTextureBlendMode(tex, SDL_BLENDMODE_BLEND);
 SDL_SetTextureBlendMode(whiteTex, SDL_BLENDMODE_BLEND);
		renderer_.setRenderTarget(nullptr);
	}
```
`Tile` class has a method that renders this white texture with `SDL_SetTextureColorMod()` and `SDL_SetTextureAlphaMod()`:
```
void Tile::renderFilled(Uint8 r, Uint8 g, Uint8 b, Uint8 alpha, Camera &camera_)
{
 auto &renderer = *m_application->getRenderer();
 SDL_SetTextureColorMod(m_tex->getWhiteSprite(), r, g, b);
 SDL_SetTextureAlphaMod(m_tex->getWhiteSprite(), alpha);
 renderer.renderTexture(m_tex->getWhiteSprite(), m_pos.x, m_pos.y, m_renderSize.x, m_renderSize.y, camera_, 0, SDL_FLIP_NONE);
}
```
`TownLevel` class uses this method sets these parameters to tile coordinates and generates mouse map:
```
 // Generate full map
 renderer.setRenderTarget(m_mouseMap->getSprite());
 for (auto *ent : m_entities)
 {
 auto pos = ent->getTilePos();
 Uint8 colors[4] = {(Uint8)pos.y, (Uint8)pos.x, 255, 255};
 ent->renderFilled(colors[0], colors[1], colors[2], colors[3], m_camera);
 }
 renderer.setRenderTarget(nullptr);

 // Generate chunks
 for (int y = 0; y < m_mouseChunkMapSize.y; ++y)
 {
 for (int x = 0; x < m_mouseChunkMapSize.x; ++x)
 {
 auto *chunk = m_mouseMapChunks[y * m_mouseChunkMapSize.x + x].get();
 renderer.setRenderTarget(chunk->getSprite());
 renderer.renderTexture(m_mouseMap->getSprite(), -m_mouseChunkSize.x * x, -m_mouseChunkSize.y * y);
 }
 }
 renderer.setRenderTarget(nullptr);
```
And I use this method to calculate the current chunk of the mouse and to get data from the pixel:
```
Vector2<int> TownLevel::worldPosToTile(const Vector2<float> worldPos_)
{
 auto &renderer = *m_application->getRenderer();
 Uint8 colors[4];
 Uint32 *data = (Uint32*)colors;
 
 // X and Y of chunk
 Vector2<int> chunkPos = {(int)worldPos_.x / m_mouseChunkSize.x, (int)worldPos_.y / m_mouseChunkSize.y};

 // Real position of top left corner of chunk
 Vector2<float> chunkOrigin = chunkPos.mulComponents(m_mouseChunkSize);

 std::cout << "Chunk " << chunkPos << " (" << chunkOrigin << ")\n";

 // Rect that selects single pixel in worldPos_ position in chunk
 SDL_Rect rect;
 rect.x = worldPos_.x - chunkOrigin.x;
 rect.y = worldPos_.y - chunkOrigin.y;
 rect.w = 1;
 rect.h = 1;

 // Read pixel from chunk
 renderer.setRenderTarget(m_mouseMapChunks[chunkPos.y * m_mouseChunkMapSize.x + chunkPos.x]->getSprite());
 SDL_RenderReadPixels(renderer.getRenderer(), &rect, SDL_PIXELFORMAT_RGBA8888, (void*)data, 1);
 std::cout << (int)colors[0] << " " << (int)colors[1] << " " << (int)colors[2] << " " << (int)colors[3] << std::endl;
 renderer.setRenderTarget(nullptr);

 // Return data from pixel
 return Vector2{(int)colors[2], (int)colors[3]};
}
```

### Question: ###

Since I use only 2 channels of SDL_PIXELFORMAT_RGBA8888 pixel instead of 3 or 4, I limit size of the map to 2^16 instead of 2^24 or 2^32. How can I use pixel data more efficiently?

 [1]: https://i.sstatic.net/pbSq6.png
 [2]: https://i.sstatic.net/Enuns.jpg
 [3]: https://stackoverflow.com/questions/21842814/mouse-position-to-isometric-tile-including-height
 [4]: https://www.gamedev.net/reference/articles/article2026.asp