Skip to content

Isometrische Projektion

Steffen edited this page Jun 11, 2022 · 3 revisions

Umrechnung Map -> Bildschirm

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:

IsoMap

Der Renderer nimmt die von der Funktion mapToIso() berechnete Bildschirmposition und zeichnet an dieser Stelle die Grafik red.png.

red

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
};

Höhenkorrektur

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:

Height

Werden nun die Tiles für die Böschung gezeichnet, ergibt sich ein weiteres Problem:

elevation0

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
        );
    }
}

elevation1

Clone this wiki locally