#include "engine/projection.h" #include #include namespace { struct ModeInfo { const char* name; int tileW, tileH, wallH; }; // Размеры подобраны так, чтобы плита занимала на экране сопоставимую площадь: // иначе смена вида меняла бы ещё и масштаб, и сравнить два вида было бы нельзя. const ModeInfo MODES[VIEW_MODE_COUNT] = { {"iso", TILE_W, TILE_H, WALL_H}, {"topdown", TILE_MAX_W, TILE_MAX_H, TILE_MAX_H}, }; } // namespace const char* ViewModeName(ViewMode m) { const int i = int(m); return (i >= 0 && i < VIEW_MODE_COUNT) ? MODES[i].name : "?"; } bool ParseViewMode(const char* name, ViewMode& out) { if (!name) return false; for (int i = 0; i < VIEW_MODE_COUNT; ++i) if (std::strcmp(name, MODES[i].name) == 0) { out = ViewMode(i); return true; } return false; } Projection Projection::For(ViewMode m) { const ModeInfo& info = MODES[int(m) < VIEW_MODE_COUNT ? int(m) : 0]; Projection p; p.mode = m; p.tileW = info.tileW; p.tileH = info.tileH; p.wallH = info.wallH; return p; } Vec2 Projection::WorldToScreen(Vec2 w) const { if (mode == ViewMode::TOP_DOWN) return {w.x * float(tileW), w.y * float(tileH)}; // sx = (wx - wy) * tileW/2 // sy = (wx + wy) * tileH/2 return {(w.x - w.y) * float(tileW) * 0.5f, (w.x + w.y) * float(tileH) * 0.5f}; } Vec2 Projection::ScreenToWorld(Vec2 s) const { if (mode == ViewMode::TOP_DOWN) return {s.x / float(tileW), s.y / float(tileH)}; const float a = s.x / float(tileW); // = (wx - wy) / 2 const float b = s.y / float(tileH); // = (wx + wy) / 2 return {b + a, b - a}; } float Projection::Depth(Vec2 w) const { // В изометрии «дальше от камеры» — это меньшая сумма координат; при виде // сверху глубины как таковой нет, и порядок задаёт только строка экрана. return (mode == ViewMode::TOP_DOWN) ? w.y : (w.x + w.y); } void Projection::TileRow(int y, int& x0, int& x1) const { if (mode == ViewMode::TOP_DOWN) { x0 = 0; x1 = tileW; return; } // Ступенька ромба — tileW/(tileH/2), то есть ровно наклон 2:1. const int half = std::max(1, tileH / 2); const int k = (y < half) ? y : (tileH - 1 - y); const int width = std::max(1, (tileW / half) * (k + 1)); x0 = (tileW - width) / 2; x1 = x0 + width; } int Projection::WallAnchorY() const { // Изометрия: низ блока садится на ромб пола, поэтому крыша поднята над // центром плиты на высоту тайла. Вид сверху: подниматься некуда. return (mode == ViewMode::TOP_DOWN) ? tileH / 2 : tileH / 2 + tileH; }