-
Notifications
You must be signed in to change notification settings - Fork 2
Isometrische Projektion
zur Mathematik siehe den Artikel von: Clint Bellanger
screen.x = (map.x - map.y) * TILE_WIDTH_HALF;
screen.y = (map.x + map.y) * TILE_HEIGHT_HALF;
Umsetzung in C++
void mdcii::SandboxState::Render()
{
auto mapToIso = [](const int t_x, const int t_y)
{
return glm::vec2(
(t_x - t_y) * TILE_WIDTH_HALF,
(t_x + t_y) * TILE_HEIGHT_HALF
);
};
for (auto y{ 0 }; y < HEIGHT; ++y)
{
for (auto x{ 0 }; x < WIDTH; ++x)
{
auto isoPos{ mapToIso(x, y) };
m_renderer->RenderTile(
renderer::RenderUtils::GetModelMatrix(
isoPos,
glm::vec2(TILE_WIDTH, TILE_HEIGHT)
),
ogl::resource::ResourceManager::LoadTexture("textures/red.png").id,
*context->window, *context->camera
);
m_textRenderer->RenderText(
std::to_string(x).append(", ").append(std::to_string(y)), // text
isoPos.x + 16, isoPos.y + 8, // position
0.25f, // scale
glm::vec3(1.0f, 0.0f, 0.0f), // red
*context->window, *context->camera
);
}
}
}
Eine 4x8 große Karte wird dann so dargestellt:
Der Renderer nimmt die von der Funktion mapToIso()
berechnete Bildschirmposition und zeichnet an dieser Stelle die Grafik red.png
.
Die Methode GetModelMatrix()
transformiert dafür ein aus zwei Dreiecken (sechs Vertices) bestehendes Quadrat an die richtige Bildschirmposition, und zwar in der angegebenen Größe. Ein Shader legt darauf die Textur.
constexpr float vertices[]
{
// pos // tex
0.0f, 1.0f, 0.0f, 1.0f,
1.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f, 1.0f,
1.0f, 1.0f, 1.0f, 1.0f,
1.0f, 0.0f, 1.0f, 0.0f
};
Das Problem stellt sich nur, wenn eine Grafik größer als das Tile ist.
tile width = 64
----------------------------------------
| ....... |
| .... .... |
| .... .... |
| .... .... |
|.... ...| tile height = 32
|... ...|
| .... .... |
| .... .... |
| .... .... |
| .... ... |
----------------------------------------
Hier passen Texturen der Größe 64x32 Pixel. Da das Rendern oben links beginnt, muss der mit mapToIso()
bestimmte y-Wert nach oben verschoben werden, wenn die Textur größer als 32 Pixel ist.
const auto w{ ogl::resource::ResourceManager::LoadTexture("textures/1320.png").width };
const auto h{ ogl::resource::ResourceManager::LoadTexture("textures/1320.png").height };
auto isoPos{ mapToIso(0, 0) };
isoPos.y -= static_cast<float>(h) - TILE_HEIGHT;
m_renderer->RenderTile(
renderer::RenderUtils::GetModelMatrix(
isoPos,
glm::vec2(w, h)
),
ogl::resource::ResourceManager::LoadTexture("textures/1320.png").id,
*context->window, *context->camera
);
Für die Position (0, 0) sieht das dann so aus:
Werden nun die Tiles für die Böschung gezeichnet, ergibt sich ein weiteres Problem:
Tiles, die sich über der Böschung befinden- also die komplette Inselbebauung - müssen weitere 20 Pixel weiter oben gerendert werden.
for (auto y{ 0 }; y < HEIGHT; ++y)
{
for (auto x{ 0 }; x < WIDTH; ++x)
{
const auto gfxMapValue{ m_map.at(getMapIndex(x, y)) };
if (gfxMapValue == 0)
{
continue;
}
const auto w{ static_cast<float>(m_stdBshFile->bshTextures[gfxMapValue]->width) };
const auto h{ static_cast<float>(m_stdBshFile->bshTextures[gfxMapValue]->height) };
auto isoPos{ mapToIso(x, y) };
isoPos.y -= h - TILE_HEIGHT;
// gfx 1320 und gfx 4 weitere 20 Pixel nach oben schieben
if (gfxMapValue == 1320 || gfxMapValue == 4)
{
isoPos.y -= 20.0f;
}
m_renderer->RenderTile(
renderer::RenderUtils::GetModelMatrix(isoPos, glm::vec2(w, h)),
m_stdBshFile->bshTextures[gfxMapValue]->textureId,
*context->window, *context->camera
);
}
}