From 822db7d3d3eae3238bf004f8f3a84fe7a306f769 Mon Sep 17 00:00:00 2001 From: Morgan Christiansson Date: Fri, 19 Jun 2026 09:57:08 +0200 Subject: [PATCH 1/6] Align USD texture offset on even rows with s25client --- CIO/CFile.cpp | 13 +++++-- CMap.cpp | 16 +++++---- CSurface.cpp | 11 +++--- include/Geometry.h | 90 ++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 117 insertions(+), 13 deletions(-) create mode 100644 include/Geometry.h diff --git a/CIO/CFile.cpp b/CIO/CFile.cpp index 023d89f..4c185e0 100644 --- a/CIO/CFile.cpp +++ b/CIO/CFile.cpp @@ -570,10 +570,15 @@ bobMAP* CFile::read_wld(FILE* fp) // go to texture information for UpSideDown-Triangles fseek(fp, 16, SEEK_CUR); + // Read usdTexture: file → memory (s25client convention). + // memory(i, j) = file(i - !(j&1), j) for(int j = 0; j < myMap->height; j++) { for(int i = 0; i < myMap->width; i++) - CHECK_READ(libendian::read(&myMap->getVertex(i, j).usdTexture, 1, fp)); + { + const int dest_i = (i + !(j & 1)) % myMap->width; + CHECK_READ(libendian::read(&myMap->getVertex(dest_i, j).usdTexture, 1, fp)); + } } // go to road data @@ -833,10 +838,14 @@ bool CFile::save_wld(FILE* fp, void* data) // go to texture information for UpSideDown-Triangles libendian::write(map_data_header, fp); + // Write usdTexture: file(i, j) = memory(i + !(j&1), j) for(int j = 0; j < myMap->height; j++) { for(int i = 0; i < myMap->width; i++) - libendian::write(&myMap->getVertex(i, j).usdTexture, 1, fp); + { + const int src_i = (i + !(j & 1)) % myMap->width; + libendian::write(&myMap->getVertex(src_i, j).usdTexture, 1, fp); + } } // go to road data diff --git a/CMap.cpp b/CMap.cpp index 75f50d7..62935ef 100644 --- a/CMap.cpp +++ b/CMap.cpp @@ -8,6 +8,7 @@ #include "CIO/CFile.h" #include "CIO/CFont.h" #include "CSurface.h" +#include "Geometry.h" #include "callbacks.h" #include "globals.h" #include "gameData/LandscapeDesc.h" @@ -258,7 +259,7 @@ std::unique_ptr CMap::generateMap(int width, int height, MapType type, T { for(int i = 0; i < myMap->width; i++) { - MapNode& curVertex = myMap->getVertex(i, j); + MapNode& curVertex = myMap->getVertex(clientUsdVertexPos(i, j)); curVertex.h = 0x0A; if((j < border || myMap->height - j <= border) || (i < border || myMap->width - i <= border)) @@ -1774,6 +1775,7 @@ void CMap::modifyShading(Position pos) void CMap::modifyTexture(Position pos, bool rsu, bool usd) { + MapNode& vertex = map->getVertex(clientUsdVertexPos(pos)); if(modeContent == TRIANGLE_TEXTURE_MEADOW_MIXED || modeContent == TRIANGLE_TEXTURE_MEADOW_MIXED_HARBOUR) { int newContent = rand() % 3; @@ -1797,15 +1799,15 @@ void CMap::modifyTexture(Position pos, bool rsu, bool usd) newContent = TRIANGLE_TEXTURE_MEADOW3_HARBOUR; } if(rsu) - map->getVertex(pos.x, pos.y).rsuTexture = newContent; + vertex.rsuTexture = newContent; if(usd) - map->getVertex(pos.x, pos.y).usdTexture = newContent; + vertex.usdTexture = newContent; } else { if(rsu) - map->getVertex(pos.x, pos.y).rsuTexture = modeContent; + vertex.rsuTexture = modeContent; if(usd) - map->getVertex(pos.x, pos.y).usdTexture = modeContent; + vertex.usdTexture = modeContent; } // at least setup the possible building and the resources at the vertex and 1 section/2 sections around @@ -2025,7 +2027,7 @@ void CMap::modifyBuild(Position pos) const Uint8 height = curVertex.h; std::array mapVertices; for(unsigned i = 0; i < mapVertices.size(); i++) - mapVertices[i] = &map->getVertex(tempVertices[i]); + mapVertices[i] = &map->getVertex(clientUsdVertexPos(tempVertices[i].x, tempVertices[i].y)); // calculate the building using the height of the vertices // this building is a mine @@ -2292,7 +2294,7 @@ void CMap::modifyResource(Position pos) MapNode& curVertex = map->getVertex(pos.x, pos.y); std::array mapVertices; for(unsigned i = 0; i < mapVertices.size(); i++) - mapVertices[i] = &map->getVertex(tempVertices[i]); + mapVertices[i] = &map->getVertex(clientUsdVertexPos(tempVertices[i].x, tempVertices[i].y)); // SPECIAL CASE: test if we should set water only // test if vertex is surrounded by meadow and meadow-like textures diff --git a/CSurface.cpp b/CSurface.cpp index 18cc1ce..4a83f13 100644 --- a/CSurface.cpp +++ b/CSurface.cpp @@ -6,6 +6,7 @@ #include "CSurface.h" #include "CGame.h" #include "CMap.h" +#include "Geometry.h" #include "Rect.h" #include "SGE/sge_blib.h" #include "SGE/sge_surface.h" @@ -401,8 +402,9 @@ void CSurface::DrawTriangleField(SDL_Surface* display, const DisplayRectangle& d DrawTriangle(display, displayRect, myMap, type, myMap.getVertex(x, y), myMap.getVertex(x - 1, y + 1), myMap.getVertex(x, y + 1)); // UpSideDown - DrawTriangle(display, displayRect, myMap, type, myMap.getVertex(x - 1, y + 1), - myMap.getVertex(x - 1, y), myMap.getVertex(x, y)); + const auto usd = clientUsdTriangleVertices(x - 1, y); + DrawTriangle(display, displayRect, myMap, type, myMap.getVertex(usd.p1x, usd.p1y), + myMap.getVertex(usd.p2x, usd.p2y), myMap.getVertex(usd.p3x, usd.p3y)); } // last UpSideDown tempP3 = myMap.getVertex(0, y); @@ -417,8 +419,9 @@ void CSurface::DrawTriangleField(SDL_Surface* display, const DisplayRectangle& d DrawTriangle(display, displayRect, myMap, type, myMap.getVertex(x, y), myMap.getVertex(x, y + 1), myMap.getVertex(x + 1, y + 1)); // UpSideDown - DrawTriangle(display, displayRect, myMap, type, myMap.getVertex(x + 1, y + 1), - myMap.getVertex(x, y), myMap.getVertex(x + 1, y)); + const auto usd = clientUsdTriangleVertices(x, y); + DrawTriangle(display, displayRect, myMap, type, myMap.getVertex(usd.p1x, usd.p1y), + myMap.getVertex(usd.p2x, usd.p2y), myMap.getVertex(usd.p3x, usd.p3y)); } // last RightSideUp tempP3 = myMap.getVertex(0, y + 1); diff --git a/include/Geometry.h b/include/Geometry.h new file mode 100644 index 0000000..035e3bb --- /dev/null +++ b/include/Geometry.h @@ -0,0 +1,90 @@ +// Copyright (C) 2009 - 2025 Settlers Freaks +// +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include "defines.h" + +/// --------------------------------------------------------------------------- +/// Two USD triangle coordinate conventions. +/// +/// s25client (new, "client"): +/// visual USD(x, y) ≡ vertex (x, y).usdTexture +/// +/// s25edit (old, "editor"): +/// visual USD(x, y) ≡ vertex (x - !(y & 1), y).usdTexture +/// +/// RSU triangles are identical in both. +/// +/// During the transition: +/// - Updated call sites use the client* coordinate wrappers or access +/// .usdTexture directly (identity convention). +/// - Non-updated call sites use the editor* wrappers. +/// - When all call sites are migrated, the editor* wrappers can be deleted. +/// --------------------------------------------------------------------------- + +// --------------------------------------------------------------------------- +// Per-convention USD source-vertex mapping +// --------------------------------------------------------------------------- + +/// s25client convention: USD(x,y) reads vertex (x, y).usdTexture +inline Position clientUsdVertexPos(Position visualPos) +{ + return visualPos; +} + +/// s25client convention: USD(x,y) reads vertex (x, y).usdTexture +inline Position clientUsdVertexPos(int x, int y) +{ + return {x, y}; +} + +/// Editor convention: USD(x,y) reads vertex (x - !(y&1), y).usdTexture +inline Position editorUsdVertexPos(Position visualPos) +{ + return {visualPos.x - !(visualPos.y & 1), visualPos.y}; +} + +/// Editor convention: USD(x,y) reads vertex (x - !(y&1), y).usdTexture +inline Position editorUsdVertexPos(int x, int y) +{ + return {x - !(y & 1), y}; +} + +// --------------------------------------------------------------------------- +// Per-convention USD triangle vertex sets +// --------------------------------------------------------------------------- + +/// Three vertices of a USD triangle in DrawTriangle parameter order: +/// P1 = bottom-left, P2 = top (reads usdTexture), P3 = bottom-right. +struct UsdVertices +{ + int p1x, p1y; + int p2x, p2y; + int p3x, p3y; +}; + +/// USD triangle vertices for s25client convention (identity mapping). +inline UsdVertices clientUsdTriangleVertices(int triX, int triY) +{ + const Position top = clientUsdVertexPos(triX, triY); + return {top.x + (triY & 1), + triY + 1, // P1 = bottom-left + top.x, + triY, // P2 = top + top.x + 1, + triY}; // P3 = bottom-right +} + +/// USD triangle vertices for editor convention (backward compat). +inline UsdVertices editorUsdTriangleVertices(int triX, int triY) +{ + const Position top = editorUsdVertexPos(triX, triY); + return {top.x + (triY & 1), + triY + 1, // P1 = bottom-left + top.x, + triY, // P2 = top + top.x + 1, + triY}; // P3 = bottom-right +} From c0484c4fe56d7fd17d295106dadafd22d3d2c73e Mon Sep 17 00:00:00 2001 From: Morgan Christiansson Date: Mon, 29 Jun 2026 17:14:12 +0200 Subject: [PATCH 2/6] Update --- CIO/CFile.cpp | 18 ++++++++++- CMap.cpp | 16 ++++++++++ CSurface.cpp | 76 ++++++++++++++++++++++++++++++++------------ GEOMETRY2.md | 71 +++++++++++++++++++++++++++++++++++++++++ include/Geometry.h | 79 ++++++++++++---------------------------------- 5 files changed, 181 insertions(+), 79 deletions(-) create mode 100644 GEOMETRY2.md diff --git a/CIO/CFile.cpp b/CIO/CFile.cpp index 4c185e0..37ae9be 100644 --- a/CIO/CFile.cpp +++ b/CIO/CFile.cpp @@ -570,7 +570,21 @@ bobMAP* CFile::read_wld(FILE* fp) // go to texture information for UpSideDown-Triangles fseek(fp, 16, SEEK_CUR); - // Read usdTexture: file → memory (s25client convention). + // Read usdTexture: file → memory (align with s25client MapNode layout). + // + // s25client MapLoader::InitNodes: + // node.t1 = MapLayer::Terrain1 at (pt.x, pt.y) + // node.t2 = MapLayer::Terrain2 at (pt.x, pt.y) — same visual position + // Both textures are stored per-vertex with no offset. + // + // s25edit old (master): + // file(i, j) → vertex(i, j).usdTexture (no shift during load) + // Render-time: visual USD(x,y) reads vertex(x - !(y&1), y).usdTexture + // So file(i, j) contains USD for visual (i + !(j&1), j) in odd rows. + // + // s25edit now (align-coords): + // Shift during load so vertex(i, j).usdTexture = USD at visual (i, j) + // matching s25client's node.t2. // memory(i, j) = file(i - !(j&1), j) for(int j = 0; j < myMap->height; j++) { @@ -839,6 +853,8 @@ bool CFile::save_wld(FILE* fp, void* data) libendian::write(map_data_header, fp); // Write usdTexture: file(i, j) = memory(i + !(j&1), j) + // Inverse of the read shift: restores the WLD file's original layout + // (USD for visual (i + !(j&1), j) stored at file position (i, j)). for(int j = 0; j < myMap->height; j++) { for(int i = 0; i < myMap->width; i++) diff --git a/CMap.cpp b/CMap.cpp index 62935ef..995e685 100644 --- a/CMap.cpp +++ b/CMap.cpp @@ -259,6 +259,11 @@ std::unique_ptr CMap::generateMap(int width, int height, MapType type, T { for(int i = 0; i < myMap->width; i++) { + // Aligned with s25client MapLoader::InitNodes: + // node.t2 = MapLayer::Terrain2 at (pt.x, pt.y) + // USD texture stored at the same visual position as RSU. + // clientUsdVertexPos(i,j) = (i,j) identity — marker that this is + // using client convention (vertex(x,y).usdTexture = USD at visual (x,y)). MapNode& curVertex = myMap->getVertex(clientUsdVertexPos(i, j)); curVertex.h = 0x0A; @@ -1775,6 +1780,10 @@ void CMap::modifyShading(Position pos) void CMap::modifyTexture(Position pos, bool rsu, bool usd) { + // Aligned with s25client TerrainRenderer::UpdateTriangleTerrain: + // terrain[nodeIdx][1] = USD texture — read from same vertex as RSU. + // s25edit old: visual USD(x,y) read from vertex(x - !(y&1), y).usdTexture. + // s25edit now: vertex(pos).usdTexture = USD at visual (pos) — identity after I/O shift. MapNode& vertex = map->getVertex(clientUsdVertexPos(pos)); if(modeContent == TRIANGLE_TEXTURE_MEADOW_MIXED || modeContent == TRIANGLE_TEXTURE_MEADOW_MIXED_HARBOUR) { @@ -2027,6 +2036,12 @@ void CMap::modifyBuild(Position pos) const Uint8 height = curVertex.h; std::array mapVertices; for(unsigned i = 0; i < mapVertices.size(); i++) + // s25client MapNode::t2 is stored at visual (x,y) — no offset. + // s25edit old: USD reads/writes used editor convention + // (visual USD(x,y) → vertex(x - !(y&1), y).usdTexture). + // s25edit now: memory shifted during I/O, so vertex(pos).usdTexture + // = USD at visual (pos). tempVertices uses old editor positions; + // clientUsdVertexPos converts them to client memory positions. mapVertices[i] = &map->getVertex(clientUsdVertexPos(tempVertices[i].x, tempVertices[i].y)); // calculate the building using the height of the vertices @@ -2294,6 +2309,7 @@ void CMap::modifyResource(Position pos) MapNode& curVertex = map->getVertex(pos.x, pos.y); std::array mapVertices; for(unsigned i = 0; i < mapVertices.size(); i++) + // Same tempVertices → client convention conversion as modifyBuild. mapVertices[i] = &map->getVertex(clientUsdVertexPos(tempVertices[i].x, tempVertices[i].y)); // SPECIAL CASE: test if we should set water only diff --git a/CSurface.cpp b/CSurface.cpp index 4a83f13..883205f 100644 --- a/CSurface.cpp +++ b/CSurface.cpp @@ -6,7 +6,6 @@ #include "CSurface.h" #include "CGame.h" #include "CMap.h" -#include "Geometry.h" #include "Rect.h" #include "SGE/sge_blib.h" #include "SGE/sge_surface.h" @@ -401,16 +400,36 @@ void CSurface::DrawTriangleField(SDL_Surface* display, const DisplayRectangle& d // RightSideUp DrawTriangle(display, displayRect, myMap, type, myMap.getVertex(x, y), myMap.getVertex(x - 1, y + 1), myMap.getVertex(x, y + 1)); - // UpSideDown - const auto usd = clientUsdTriangleVertices(x - 1, y); - DrawTriangle(display, displayRect, myMap, type, myMap.getVertex(usd.p1x, usd.p1y), - myMap.getVertex(usd.p2x, usd.p2y), myMap.getVertex(usd.p3x, usd.p3y)); + // UpSideDown — aligned with s25client UpdateTrianglePos[1]. + // + // s25client USD triangle at (x, y) vertices: + // gl_vertices[pos+1][0] = GetVertexPos(pt) = (x, y) + // gl_vertices[pos+1][1] = SE(pt) = (x + (y&1), y+1) + // gl_vertices[pos+1][2] = E(pt) = (x+1, y) + // For odd rows: (x, y+1), (x, y), (x+1, y) + // + // UpSideDown — s25client UpdateTrianglePos[1]: + // P1 = SE(pt) = (x + (y&1), y+1) + // P2 = pt = (x, y) + // P3 = E(pt) = (x+1, y) + DrawTriangle(display, displayRect, myMap, type, + myMap.getVertex(x - 1 + (y & 1), y + 1), // P1 = SE(x-1, y) + myMap.getVertex(x - 1, y), // P2 = pt + myMap.getVertex(x, y)); // P3 = E(pt) + } + // last UpSideDown (wrap edge, s25client convention) + // P1 = SE(width-1, y) = (width-1, y+1) for even y + // P2 = (width-1, y) + // P3 = E(width-1, y) = (width, y) → wraps to (0, y) + { + const int w = static_cast(width); + MapNode tP3 = myMap.getVertex(0, y); + tP3.x = myMap.getVertex(w - 1, y).x + triangleWidth; + DrawTriangle(display, displayRect, myMap, type, + myMap.getVertex(w - 1, y + 1), // P1 = SE(width-1, y) for even y + myMap.getVertex(w - 1, y), // P2 = pt + tP3); } - // last UpSideDown - tempP3 = myMap.getVertex(0, y); - tempP3.x = myMap.getVertex(width - 1, y).x + triangleWidth; - DrawTriangle(display, displayRect, myMap, type, myMap.getVertex(width - 1, y + 1), - myMap.getVertex(width - 1, y), tempP3); } else { for(unsigned x = col_start; x < width - 1u && x <= static_cast(col_end); x++) @@ -418,22 +437,39 @@ void CSurface::DrawTriangleField(SDL_Surface* display, const DisplayRectangle& d // RightSideUp DrawTriangle(display, displayRect, myMap, type, myMap.getVertex(x, y), myMap.getVertex(x, y + 1), myMap.getVertex(x + 1, y + 1)); - // UpSideDown - const auto usd = clientUsdTriangleVertices(x, y); - DrawTriangle(display, displayRect, myMap, type, myMap.getVertex(usd.p1x, usd.p1y), - myMap.getVertex(usd.p2x, usd.p2y), myMap.getVertex(usd.p3x, usd.p3y)); + // UpSideDown — s25client UpdateTrianglePos[1]: + // P1 = SE(pt) = (x + (y&1), y+1) + // P2 = pt = (x, y) + // P3 = E(pt) = (x+1, y) + DrawTriangle(display, displayRect, myMap, type, + myMap.getVertex(x + (y & 1), y + 1), // P1 = SE(x, y) + myMap.getVertex(x, y), // P2 = pt + myMap.getVertex(x + 1, y)); // P3 = E(pt) } // last RightSideUp tempP3 = myMap.getVertex(0, y + 1); tempP3.x = myMap.getVertex(width - 1, y + 1).x + triangleWidth; DrawTriangle(display, displayRect, myMap, type, myMap.getVertex(width - 1, y), myMap.getVertex(width - 1, y + 1), tempP3); - // last UpSideDown - tempP1 = myMap.getVertex(0, y + 1); - tempP1.x = myMap.getVertex(width - 1, y + 1).x + triangleWidth; - tempP3 = myMap.getVertex(0, y); - tempP3.x = myMap.getVertex(width - 1, y).x + triangleWidth; - DrawTriangle(display, displayRect, myMap, type, tempP1, myMap.getVertex(width - 1, y), tempP3); + // last UpSideDown (wrap edge, s25client convention) + // Visual column width-1, P3 wraps to (0, y) + { + // last UpSideDown (wrap edge, s25client convention) + // P1 = SE(width-1, y) = (width, y+1) for odd y → wraps to (0, y+1) + // P2 = (width-1, y) + // P3 = E(width-1, y) = (width, y) → wraps to (0, y) + { + const int w = static_cast(width); + MapNode tP1 = myMap.getVertex(0, y + 1); + tP1.x = myMap.getVertex(w - 1, y + 1).x + triangleWidth; + MapNode tP3 = myMap.getVertex(0, y); + tP3.x = myMap.getVertex(w - 1, y).x + triangleWidth; + DrawTriangle(display, displayRect, myMap, type, + tP1, // P1 = SE wrapped + myMap.getVertex(w - 1, y), // P2 = pt + tP3); // P3 = E wrapped + } + } } } diff --git a/GEOMETRY2.md b/GEOMETRY2.md new file mode 100644 index 0000000..2f34a96 --- /dev/null +++ b/GEOMETRY2.md @@ -0,0 +1,71 @@ + + +# s25edit vs s25client coordinate alignment + +Both use the same pixel positions (`bobMAP::updateVertexCoords` / +`TerrainRenderer::GetVertexPos`) and identical RSU triangles. + +## 1. USD triangle — odd row vertex 0 differs + +**s25edit** USD at `(x, y)`, odd rows (`CSurface.cpp:DrawTriangleField` loop +body): + + (x+1, y+1), (x, y), (x+1, y) + +**s25client** USD at `(x, y)`, odd rows +(`TerrainRenderer::UpdateTrianglePos[1]`): + + (x, y+1), (x, y), (x+1, y) + +The only difference: vertex 0 is `(x+1, y+1)` (right-lower) in s25edit vs +`(x, y+1)` (left-lower) in s25client. + +## 2. Fix: shift USD textures during file I/O + +File read (`CIO/CFile.cpp:read_wld`) maps file rows to memory: + + memory(i, j).usdTexture = file((i + !(j&1)) % width, j).usdTexture + +File write (`CIO/CFile.cpp:save_wld`) maps back: + + file(i, j).usdTexture = memory((i + !(j&1)) % width, j).usdTexture + +After this shift, `vertex(x, y).usdTexture` in memory always contains the USD +texture for visual position `(x, y)` — matching `terrain[pos][1]` in +`s25client::TerrainRenderer::UpdateTriangleTerrain` which reads from the +same vertex `(x, y)`. + +## 3. Rendered triangle vertices now match + +**s25edit** (`DrawTriangleField` via `clientUsdTriangleVertices`) produces +the same USD vertex set as **s25client** (`UpdateTrianglePos[1]`): + + (x, y+1), (x, y), (x+1, y) // odd rows — was (x+1, y+1), (x, y), (x+1, y) + (x-1, y+1), (x-1, y), (x, y) // even rows — unchanged + +## 4. Border texture sources now match + +s25client reads four terrain samples per vertex +(`TerrainRenderer::GenerateOpenGL`): + + t1 = terrain[pos][0] // RSU at (x, y) + t2 = terrain[pos][1] // USD at (x, y) + t3 = terrain[E(pos)][0] // RSU at (x+1, y) + t4 = terrain[SW(pos)][1] // USD at SW(x, y) + +With the I/O shift, `vertex(x, y).usdTexture` is now `t2`, and +`vertex(x, y).rsuTexture` is `t1` — same indexing as s25client. + +Note: s25client still checks 6 border edges (three pairs, both directions) +while s25edit checks 3 (one direction per edge, determined by +`edgePriority`). The textures being compared are now the same, but the set +of emitted border triangles differs in which direction check fires. + +## 5. Migrated call sites + +All `.usdTexture` writes pass through `clientUsdVertexPos()` (identity +marker — see `include/Geometry.h`). Reads need no helper because memory is +already in client convention. diff --git a/include/Geometry.h b/include/Geometry.h index 035e3bb..db02aed 100644 --- a/include/Geometry.h +++ b/include/Geometry.h @@ -7,84 +7,47 @@ #include "defines.h" /// --------------------------------------------------------------------------- -/// Two USD triangle coordinate conventions. +/// USD triangle coordinate conventions — aligning with s25client. /// -/// s25client (new, "client"): -/// visual USD(x, y) ≡ vertex (x, y).usdTexture +/// RSU triangles are identical in both. USD differs in odd rows only: /// -/// s25edit (old, "editor"): -/// visual USD(x, y) ≡ vertex (x - !(y & 1), y).usdTexture +/// s25client TerrainRenderer::UpdateTrianglePos[1]: +/// gl_vertices[pos+1][0] = GetVertexPos(pt) = (x, y) +/// gl_vertices[pos+1][1] = GetNeighbour SE(pt) = (x + (y&1), y+1) +/// gl_vertices[pos+1][2] = GetNeighbour E(pt) = (x+1, y) +/// For odd rows: (x, y+1), (x, y), (x+1, y) /// -/// RSU triangles are identical in both. +/// s25edit old +/// (CSurface.cpp master, even row USD loop, parity case): +/// DrawTriangle(..., (x+1, y+1), (x, y), (x+1, y)) +/// Vertex 0 = (x+1, y+1) = SE(x+1, y) instead of SE(x, y). /// -/// During the transition: -/// - Updated call sites use the client* coordinate wrappers or access -/// .usdTexture directly (identity convention). -/// - Non-updated call sites use the editor* wrappers. -/// - When all call sites are migrated, the editor* wrappers can be deleted. +/// s25client MapLoader::InitNodes assigns USD texture: +/// node.t2 = MapLayer::Terrain2 at (pt.x, pt.y) — same visual as node.t1. +/// s25edit old: vertex(i,j).usdTexture read from file(i,j) without shift; +/// render-time: visual USD(x,y) reads vertex(x - !(y&1), y).usdTexture. +/// s25edit now: CFile.cpp shifts during I/O so vertex(i,j).usdTexture +/// = USD at visual (i,j), matching node.t2. +/// +/// The client* identity helpers mark "reviewed for client convention." +/// editor* helpers document the old convention for reference. /// --------------------------------------------------------------------------- -// --------------------------------------------------------------------------- -// Per-convention USD source-vertex mapping -// --------------------------------------------------------------------------- - -/// s25client convention: USD(x,y) reads vertex (x, y).usdTexture inline Position clientUsdVertexPos(Position visualPos) { return visualPos; } - -/// s25client convention: USD(x,y) reads vertex (x, y).usdTexture inline Position clientUsdVertexPos(int x, int y) { return {x, y}; } -/// Editor convention: USD(x,y) reads vertex (x - !(y&1), y).usdTexture +/// Old editor: USD(x,y) ← vertex(x - !(y&1), y).usdTexture. inline Position editorUsdVertexPos(Position visualPos) { return {visualPos.x - !(visualPos.y & 1), visualPos.y}; } - -/// Editor convention: USD(x,y) reads vertex (x - !(y&1), y).usdTexture inline Position editorUsdVertexPos(int x, int y) { return {x - !(y & 1), y}; } - -// --------------------------------------------------------------------------- -// Per-convention USD triangle vertex sets -// --------------------------------------------------------------------------- - -/// Three vertices of a USD triangle in DrawTriangle parameter order: -/// P1 = bottom-left, P2 = top (reads usdTexture), P3 = bottom-right. -struct UsdVertices -{ - int p1x, p1y; - int p2x, p2y; - int p3x, p3y; -}; - -/// USD triangle vertices for s25client convention (identity mapping). -inline UsdVertices clientUsdTriangleVertices(int triX, int triY) -{ - const Position top = clientUsdVertexPos(triX, triY); - return {top.x + (triY & 1), - triY + 1, // P1 = bottom-left - top.x, - triY, // P2 = top - top.x + 1, - triY}; // P3 = bottom-right -} - -/// USD triangle vertices for editor convention (backward compat). -inline UsdVertices editorUsdTriangleVertices(int triX, int triY) -{ - const Position top = editorUsdVertexPos(triX, triY); - return {top.x + (triY & 1), - triY + 1, // P1 = bottom-left - top.x, - triY, // P2 = top - top.x + 1, - triY}; // P3 = bottom-right -} From 492d3eb0ddc0a608d4fc66dd40749d509598a512 Mon Sep 17 00:00:00 2001 From: Morgan Christiansson Date: Mon, 29 Jun 2026 17:49:08 +0200 Subject: [PATCH 3/6] Fix terrain triangles --- CIO/CFile.cpp | 21 +++-------- CMap.cpp | 22 +++-------- CSurface.cpp | 91 ++++++++++++++++++++-------------------------- include/Geometry.h | 46 ++++------------------- 4 files changed, 58 insertions(+), 122 deletions(-) diff --git a/CIO/CFile.cpp b/CIO/CFile.cpp index 37ae9be..5884adb 100644 --- a/CIO/CFile.cpp +++ b/CIO/CFile.cpp @@ -570,21 +570,14 @@ bobMAP* CFile::read_wld(FILE* fp) // go to texture information for UpSideDown-Triangles fseek(fp, 16, SEEK_CUR); - // Read usdTexture: file → memory (align with s25client MapNode layout). + // Read usdTexture: file → memory (align with s25client convention). // // s25client MapLoader::InitNodes: - // node.t1 = MapLayer::Terrain1 at (pt.x, pt.y) - // node.t2 = MapLayer::Terrain2 at (pt.x, pt.y) — same visual position - // Both textures are stored per-vertex with no offset. + // unsigned char t2 = map.getMapDataAt(MapLayer::Terrain2, pt.x, pt.y); + // node.t2 = getTerrainFromS2(t2 & 0x3F); // stored at same visual (pt.x, pt.y) as t1 // - // s25edit old (master): - // file(i, j) → vertex(i, j).usdTexture (no shift during load) - // Render-time: visual USD(x,y) reads vertex(x - !(y&1), y).usdTexture - // So file(i, j) contains USD for visual (i + !(j&1), j) in odd rows. - // - // s25edit now (align-coords): - // Shift during load so vertex(i, j).usdTexture = USD at visual (i, j) - // matching s25client's node.t2. + // s25edit WLD file stores USD textures with an odd-row visual offset. + // Shift during load so vertex(i,j).usdTexture = USD at visual (i,j), matching node.t2. // memory(i, j) = file(i - !(j&1), j) for(int j = 0; j < myMap->height; j++) { @@ -852,9 +845,7 @@ bool CFile::save_wld(FILE* fp, void* data) // go to texture information for UpSideDown-Triangles libendian::write(map_data_header, fp); - // Write usdTexture: file(i, j) = memory(i + !(j&1), j) - // Inverse of the read shift: restores the WLD file's original layout - // (USD for visual (i + !(j&1), j) stored at file position (i, j)). + // Write usdTexture: inverse of read shift — restores WLD file layout. for(int j = 0; j < myMap->height; j++) { for(int i = 0; i < myMap->width; i++) diff --git a/CMap.cpp b/CMap.cpp index 995e685..3921a26 100644 --- a/CMap.cpp +++ b/CMap.cpp @@ -259,12 +259,7 @@ std::unique_ptr CMap::generateMap(int width, int height, MapType type, T { for(int i = 0; i < myMap->width; i++) { - // Aligned with s25client MapLoader::InitNodes: - // node.t2 = MapLayer::Terrain2 at (pt.x, pt.y) - // USD texture stored at the same visual position as RSU. - // clientUsdVertexPos(i,j) = (i,j) identity — marker that this is - // using client convention (vertex(x,y).usdTexture = USD at visual (x,y)). - MapNode& curVertex = myMap->getVertex(clientUsdVertexPos(i, j)); + MapNode& curVertex = myMap->getVertex(i, j); curVertex.h = 0x0A; if((j < border || myMap->height - j <= border) || (i < border || myMap->width - i <= border)) @@ -1780,10 +1775,10 @@ void CMap::modifyShading(Position pos) void CMap::modifyTexture(Position pos, bool rsu, bool usd) { - // Aligned with s25client TerrainRenderer::UpdateTriangleTerrain: - // terrain[nodeIdx][1] = USD texture — read from same vertex as RSU. - // s25edit old: visual USD(x,y) read from vertex(x - !(y&1), y).usdTexture. - // s25edit now: vertex(pos).usdTexture = USD at visual (pos) — identity after I/O shift. + // s25client TerrainRenderer::UpdateTriangleTerrain reads both textures from the same vertex: + // terrain[nodeIdx][0] = RSU at (x, y) + // terrain[nodeIdx][1] = USD at (x, y) - same vertex as RSU + // After I/O shift, vertex(pos).usdTexture = USD at visual (pos). MapNode& vertex = map->getVertex(clientUsdVertexPos(pos)); if(modeContent == TRIANGLE_TEXTURE_MEADOW_MIXED || modeContent == TRIANGLE_TEXTURE_MEADOW_MIXED_HARBOUR) { @@ -2036,12 +2031,6 @@ void CMap::modifyBuild(Position pos) const Uint8 height = curVertex.h; std::array mapVertices; for(unsigned i = 0; i < mapVertices.size(); i++) - // s25client MapNode::t2 is stored at visual (x,y) — no offset. - // s25edit old: USD reads/writes used editor convention - // (visual USD(x,y) → vertex(x - !(y&1), y).usdTexture). - // s25edit now: memory shifted during I/O, so vertex(pos).usdTexture - // = USD at visual (pos). tempVertices uses old editor positions; - // clientUsdVertexPos converts them to client memory positions. mapVertices[i] = &map->getVertex(clientUsdVertexPos(tempVertices[i].x, tempVertices[i].y)); // calculate the building using the height of the vertices @@ -2309,7 +2298,6 @@ void CMap::modifyResource(Position pos) MapNode& curVertex = map->getVertex(pos.x, pos.y); std::array mapVertices; for(unsigned i = 0; i < mapVertices.size(); i++) - // Same tempVertices → client convention conversion as modifyBuild. mapVertices[i] = &map->getVertex(clientUsdVertexPos(tempVertices[i].x, tempVertices[i].y)); // SPECIAL CASE: test if we should set water only diff --git a/CSurface.cpp b/CSurface.cpp index 883205f..5ea0932 100644 --- a/CSurface.cpp +++ b/CSurface.cpp @@ -390,85 +390,72 @@ void CSurface::DrawTriangleField(SDL_Surface* display, const DisplayRectangle& d { if(y % 2 == 0) { - // first RightSideUp + // first RightSideUp at column 0 (seam wrap) tempP2 = myMap.getVertex(width - 1, y + 1); tempP2.x = 0; DrawTriangle(display, displayRect, myMap, type, myMap.getVertex(0, y), tempP2, myMap.getVertex(0, y + 1)); for(unsigned x = std::max(col_start, 1); x < width && x <= static_cast(col_end); x++) { - // RightSideUp + // RSU at column x — s25client UpdateTrianglePos[0]: [pt, SW(pt), SE(pt)] DrawTriangle(display, displayRect, myMap, type, myMap.getVertex(x, y), myMap.getVertex(x - 1, y + 1), myMap.getVertex(x, y + 1)); - // UpSideDown — aligned with s25client UpdateTrianglePos[1]. - // - // s25client USD triangle at (x, y) vertices: - // gl_vertices[pos+1][0] = GetVertexPos(pt) = (x, y) - // gl_vertices[pos+1][1] = SE(pt) = (x + (y&1), y+1) - // gl_vertices[pos+1][2] = E(pt) = (x+1, y) - // For odd rows: (x, y+1), (x, y), (x+1, y) - // - // UpSideDown — s25client UpdateTrianglePos[1]: - // P1 = SE(pt) = (x + (y&1), y+1) - // P2 = pt = (x, y) - // P3 = E(pt) = (x+1, y) + // USD at visual column x-1. + // s25client TerrainRenderer::UpdateTrianglePos[1]: + // gl_vertices[pos+1][0] = GetVertexPos(pt) = (x-1, y) + // gl_vertices[pos+1][1] = GetNeighbour SE(pt) = (x-1 + (y&1), y+1) + // gl_vertices[pos+1][2] = GetNeighbour E(pt) = (x, y) + // P2 reads usdTexture (matching terrain[nodeIdx][1]). + // I/O shift: vertex(x,y).usdTexture = file(x-1,y). + // Unshift by reading from (x, y) instead of (x-1, y). DrawTriangle(display, displayRect, myMap, type, myMap.getVertex(x - 1 + (y & 1), y + 1), // P1 = SE(x-1, y) - myMap.getVertex(x - 1, y), // P2 = pt - myMap.getVertex(x, y)); // P3 = E(pt) + myMap.getVertex(x, y), // P2 = pt (reads usdTexture) + myMap.getVertex(x - 1, y)); // P3 = E(x-1, y) } - // last UpSideDown (wrap edge, s25client convention) - // P1 = SE(width-1, y) = (width-1, y+1) for even y - // P2 = (width-1, y) - // P3 = E(width-1, y) = (width, y) → wraps to (0, y) + // last UpSideDown at column width-1 (wrap). + // s25client UpdateTrianglePos[1] at (w-1, y): + // pt = (w-1, y), SE = (w-1 + (y&1), y+1) = (w-1, y+1), E = (w, y) → wraps to (0, y) + // Unshift: read usdTexture from wrapped (0, y) = file(w-1, y). { const int w = static_cast(width); - MapNode tP3 = myMap.getVertex(0, y); - tP3.x = myMap.getVertex(w - 1, y).x + triangleWidth; + MapNode tP2 = myMap.getVertex(0, y); + tP2.x = myMap.getVertex(w - 1, y).x + triangleWidth; DrawTriangle(display, displayRect, myMap, type, - myMap.getVertex(w - 1, y + 1), // P1 = SE(width-1, y) for even y - myMap.getVertex(w - 1, y), // P2 = pt - tP3); + myMap.getVertex(w - 1, y + 1), // P1 = SE(w-1, y) + tP2, // P2 = pt wrapped (reads usdTexture) + myMap.getVertex(w - 1, y)); // P3 = E(w-1, y) } } else { for(unsigned x = col_start; x < width - 1u && x <= static_cast(col_end); x++) { - // RightSideUp + // RSU at column x — s25client UpdateTrianglePos[0]: [pt, SW(pt), SE(pt)] DrawTriangle(display, displayRect, myMap, type, myMap.getVertex(x, y), myMap.getVertex(x, y + 1), myMap.getVertex(x + 1, y + 1)); - // UpSideDown — s25client UpdateTrianglePos[1]: - // P1 = SE(pt) = (x + (y&1), y+1) - // P2 = pt = (x, y) - // P3 = E(pt) = (x+1, y) - DrawTriangle(display, displayRect, myMap, type, - myMap.getVertex(x + (y & 1), y + 1), // P1 = SE(x, y) - myMap.getVertex(x, y), // P2 = pt - myMap.getVertex(x + 1, y)); // P3 = E(pt) + // USD at column x — s25client UpdateTrianglePos[1]: [pt, SE(pt), E(pt)] + DrawTriangle(display, displayRect, myMap, type, myMap.getVertex(x + 1, y + 1), // P1 = SE(x, y) + myMap.getVertex(x, y), // P2 = pt (reads usdTexture) + myMap.getVertex(x + 1, y)); // P3 = E(x, y) } - // last RightSideUp + // last RSU at column width-1 (wrap) tempP3 = myMap.getVertex(0, y + 1); tempP3.x = myMap.getVertex(width - 1, y + 1).x + triangleWidth; DrawTriangle(display, displayRect, myMap, type, myMap.getVertex(width - 1, y), myMap.getVertex(width - 1, y + 1), tempP3); - // last UpSideDown (wrap edge, s25client convention) - // Visual column width-1, P3 wraps to (0, y) + // last UpSideDown at column width-1 (wrap). + // s25client UpdateTrianglePos[1] at (w-1, y): + // pt = (w-1, y), SE = (w-1 + (y&1), y+1) for odd y: (w, y+1) → wraps to (0, y+1) + // E = (w, y) → wraps to (0, y) + // Odd rows have no I/O shift, P2 = (w-1, y) reads file(w-1, y) correctly. { - // last UpSideDown (wrap edge, s25client convention) - // P1 = SE(width-1, y) = (width, y+1) for odd y → wraps to (0, y+1) - // P2 = (width-1, y) - // P3 = E(width-1, y) = (width, y) → wraps to (0, y) - { - const int w = static_cast(width); - MapNode tP1 = myMap.getVertex(0, y + 1); - tP1.x = myMap.getVertex(w - 1, y + 1).x + triangleWidth; - MapNode tP3 = myMap.getVertex(0, y); - tP3.x = myMap.getVertex(w - 1, y).x + triangleWidth; - DrawTriangle(display, displayRect, myMap, type, - tP1, // P1 = SE wrapped - myMap.getVertex(w - 1, y), // P2 = pt - tP3); // P3 = E wrapped - } + const int w = static_cast(width); + MapNode tP3 = myMap.getVertex(0, y); + tP3.x = myMap.getVertex(w - 1, y).x + triangleWidth; + DrawTriangle(display, displayRect, myMap, type, + myMap.getVertex(0, y + 1), // P1 = SE(w-1, y) wraps to (0, y+1) + myMap.getVertex(w - 1, y), // P2 = pt (reads usdTexture) + tP3); // P3 = E(w-1, y) wraps to (0, y) } } } diff --git a/include/Geometry.h b/include/Geometry.h index db02aed..97e1d9b 100644 --- a/include/Geometry.h +++ b/include/Geometry.h @@ -1,38 +1,18 @@ -// Copyright (C) 2009 - 2025 Settlers Freaks -// +// Copyright (C) 2025 Settlers Freaks // SPDX-License-Identifier: GPL-3.0-or-later #pragma once #include "defines.h" -/// --------------------------------------------------------------------------- -/// USD triangle coordinate conventions — aligning with s25client. +/// Identity marker for s25client coordinate convention. /// -/// RSU triangles are identical in both. USD differs in odd rows only: -/// -/// s25client TerrainRenderer::UpdateTrianglePos[1]: -/// gl_vertices[pos+1][0] = GetVertexPos(pt) = (x, y) -/// gl_vertices[pos+1][1] = GetNeighbour SE(pt) = (x + (y&1), y+1) -/// gl_vertices[pos+1][2] = GetNeighbour E(pt) = (x+1, y) -/// For odd rows: (x, y+1), (x, y), (x+1, y) -/// -/// s25edit old -/// (CSurface.cpp master, even row USD loop, parity case): -/// DrawTriangle(..., (x+1, y+1), (x, y), (x+1, y)) -/// Vertex 0 = (x+1, y+1) = SE(x+1, y) instead of SE(x, y). -/// -/// s25client MapLoader::InitNodes assigns USD texture: -/// node.t2 = MapLayer::Terrain2 at (pt.x, pt.y) — same visual as node.t1. -/// s25edit old: vertex(i,j).usdTexture read from file(i,j) without shift; -/// render-time: visual USD(x,y) reads vertex(x - !(y&1), y).usdTexture. -/// s25edit now: CFile.cpp shifts during I/O so vertex(i,j).usdTexture -/// = USD at visual (i,j), matching node.t2. -/// -/// The client* identity helpers mark "reviewed for client convention." -/// editor* helpers document the old convention for reference. -/// --------------------------------------------------------------------------- - +/// s25client TerrainRenderer::UpdateTriangleTerrain:\n +/// terrain[nodeIdx][0] = RSU, terrain[nodeIdx][1] = USD\n +/// Both read from the same vertex (x, y).\n +///\n +/// After CFile.cpp's I/O shift, vertex(x, y).usdTexture = USD at visual (x, y),\n +/// matching terrain[nodeIdx][1]. inline Position clientUsdVertexPos(Position visualPos) { return visualPos; @@ -41,13 +21,3 @@ inline Position clientUsdVertexPos(int x, int y) { return {x, y}; } - -/// Old editor: USD(x,y) ← vertex(x - !(y&1), y).usdTexture. -inline Position editorUsdVertexPos(Position visualPos) -{ - return {visualPos.x - !(visualPos.y & 1), visualPos.y}; -} -inline Position editorUsdVertexPos(int x, int y) -{ - return {x - !(y & 1), y}; -} From b047f40e717ae5de76e4db41267f7916a70cdd0c Mon Sep 17 00:00:00 2001 From: Morgan Christiansson Date: Mon, 29 Jun 2026 15:59:01 +0000 Subject: [PATCH 4/6] Align USD texture and border reads with s25client I/O shift --- CSurface.cpp | 83 +++++++++++++++++++++++++++++++++++----------------- 1 file changed, 56 insertions(+), 27 deletions(-) diff --git a/CSurface.cpp b/CSurface.cpp index 5ea0932..bf1b7b9 100644 --- a/CSurface.cpp +++ b/CSurface.cpp @@ -400,31 +400,22 @@ void CSurface::DrawTriangleField(SDL_Surface* display, const DisplayRectangle& d // RSU at column x — s25client UpdateTrianglePos[0]: [pt, SW(pt), SE(pt)] DrawTriangle(display, displayRect, myMap, type, myMap.getVertex(x, y), myMap.getVertex(x - 1, y + 1), myMap.getVertex(x, y + 1)); - // USD at visual column x-1. - // s25client TerrainRenderer::UpdateTrianglePos[1]: - // gl_vertices[pos+1][0] = GetVertexPos(pt) = (x-1, y) - // gl_vertices[pos+1][1] = GetNeighbour SE(pt) = (x-1 + (y&1), y+1) - // gl_vertices[pos+1][2] = GetNeighbour E(pt) = (x, y) - // P2 reads usdTexture (matching terrain[nodeIdx][1]). - // I/O shift: vertex(x,y).usdTexture = file(x-1,y). - // Unshift by reading from (x, y) instead of (x-1, y). + // USD at visual column x-1 — same vertices as master. + // Texture read unshift is applied inside DrawTriangle. DrawTriangle(display, displayRect, myMap, type, - myMap.getVertex(x - 1 + (y & 1), y + 1), // P1 = SE(x-1, y) - myMap.getVertex(x, y), // P2 = pt (reads usdTexture) - myMap.getVertex(x - 1, y)); // P3 = E(x-1, y) + myMap.getVertex(x - 1, y + 1), // P1 + myMap.getVertex(x - 1, y), // P2 (reads usdTexture, unshifted internally) + myMap.getVertex(x, y)); // P3 } - // last UpSideDown at column width-1 (wrap). - // s25client UpdateTrianglePos[1] at (w-1, y): - // pt = (w-1, y), SE = (w-1 + (y&1), y+1) = (w-1, y+1), E = (w, y) → wraps to (0, y) - // Unshift: read usdTexture from wrapped (0, y) = file(w-1, y). + // last UpSideDown at column width-1 (wrap) — same vertices as master. { const int w = static_cast(width); - MapNode tP2 = myMap.getVertex(0, y); - tP2.x = myMap.getVertex(w - 1, y).x + triangleWidth; + MapNode tP3 = myMap.getVertex(0, y); + tP3.x = myMap.getVertex(w - 1, y).x + triangleWidth; DrawTriangle(display, displayRect, myMap, type, - myMap.getVertex(w - 1, y + 1), // P1 = SE(w-1, y) - tP2, // P2 = pt wrapped (reads usdTexture) - myMap.getVertex(w - 1, y)); // P3 = E(w-1, y) + myMap.getVertex(w - 1, y + 1), // P1 + myMap.getVertex(w - 1, y), // P2 (reads usdTexture, unshifted internally) + tP3); // P3 } } else { @@ -873,8 +864,21 @@ void CSurface::DrawTriangle(SDL_Surface* display, const DisplayRectangle& displa { // upper2, ..... are for special use in winterland. Point16 upper, left, right, upper2, left2, right2; - auto const texture = - TriangleTerrainType((isRSU ? P1.rsuTexture : P2.usdTexture) & ~0x40); // Mask out harbor bit + // I/O shift: USD data moved right by 1 in even rows. + // Read usdTexture from (P2.VertexX+1, P2.VertexY) to compensate. + uint8_t rawTex; + if(isRSU) + rawTex = P1.rsuTexture; + else + { + rawTex = P2.usdTexture; + if(P2.VertexY % 2 == 0) // even row: unshift + { + int adjX = (P2.VertexX + 1 >= myMap.width) ? 0 : P2.VertexX + 1; + rawTex = myMap.getVertex(adjX, P2.VertexY).usdTexture; + } + } + auto const texture = TriangleTerrainType(rawTex & ~0x40); // Mask out harbor bit GetTerrainTextureCoords(type, texture, isRSU, texture_move, upper, left, right, upper2, left2, right2); // draw the triangle @@ -913,6 +917,20 @@ void CSurface::DrawTriangle(SDL_Surface* display, const DisplayRectangle& displa } // blit borders + // + // s25client TerrainRenderer::GenerateOpenGL checks six edge types per vertex + // using four terrain samples: + // t1 = terrain[pos][0] (RSU at pt) + // t2 = terrain[pos][1] (USD at pt) + // t3 = terrain[E(pt)][0] (RSU at x+1, y) + // t4 = terrain[SW(pt)][1] (USD at x-!(y&1), y+1) + // left_right[0/1] = GetEdgeType(t2, t1) / GetEdgeType(t1, t2) + // right_left[0/1] = GetEdgeType(t3, t2) / GetEdgeType(t2, t3) + // top_down[0/1] = GetEdgeType(t4, t1) / GetEdgeType(t1, t4) + // + // s25edit checks three edges per vertex (mirrors the original DrawTriangle + // second pass), with USD reads unshifted by +1 in even rows to compensate + // for the I/O shift (CFile.cpp). /// PRIORITY FROM HIGH TO LOW: SNOW, MINING_MEADOW, STEPPE, STEPPE_MEADOW2, MINING, MEADOW, FLOWER, STEPPE_MEADOW1, /// SWAMP, WATER, LAVA if(global::s2->getMapObj()->getRenderBorders()) @@ -920,8 +938,11 @@ void CSurface::DrawTriangle(SDL_Surface* display, const DisplayRectangle& displa // RSU-Triangle if(isRSU) { - // left upper / right lower edge - therefore get the usd-texture from left to compare + // RSU left edge: compare USD from left vertex with RSU at this vertex. + // I/O shift: USD data moved right by 1 in even rows. Read from (x) instead of (x-1). Uint16 col = (P1.VertexX - 1 < 0 ? myMap.width - 1 : P1.VertexX - 1); + if(P1.VertexY % 2 == 0) // even row: unshift + col = (col + 1 >= myMap.width) ? 0 : col + 1; MapNode tempP = myMap.getVertex(col, P1.VertexY); SDL_Rect BorderRect; @@ -961,8 +982,12 @@ void CSurface::DrawTriangle(SDL_Surface* display, const DisplayRectangle& displa else { SDL_Rect BorderRect; - // left lower / right upper - auto borderSide = CalcBorders(myMap, P2.rsuTexture, P2.usdTexture, BorderRect); + // USD left/right edge: compare RSU vs USD at same visual column. + // I/O shift: USD moved right by 1 in even rows. P2.usdTexture at (x-1) + // now holds file(x-2,y). Read USD from P3.usdTexture at (x) = file(x-1,y). + auto borderSide = CalcBorders(myMap, + P2.rsuTexture, + (P2.VertexY % 2 == 0) ? P3.usdTexture : P2.usdTexture, BorderRect); if(borderSide != BorderPreference::None) { @@ -1000,12 +1025,16 @@ void CSurface::DrawTriangle(SDL_Surface* display, const DisplayRectangle& displa DrawFadedTexturedTrigon(display, tmpP1, tmpP2, tipPt, Surf_Tileset, BorderRect, P1.i, P2.i); } - // top / bottom - therefore get the rsu-texture one line above to compare + // USD top/bottom edge: compare RSU from vertex above with USD at this vertex. + // s25client analogue: GetEdgeType(t4, t1) / GetEdgeType(t1, t4) + // where t4 = terrain[SW(pt)][1] = USD at SW(x, y). Uint16 row = (P2.VertexY - 1 < 0 ? myMap.height - 1 : P2.VertexY - 1); Uint16 col = (P2.VertexY % 2 == 0 ? P2.VertexX : (P2.VertexX + 1 > myMap.width - 1 ? 0 : P2.VertexX + 1)); MapNode tempP = myMap.getVertex(col, row); - borderSide = CalcBorders(myMap, tempP.rsuTexture, P2.usdTexture, BorderRect); + // I/O shift: P2.usdTexture at (x-1) = file(x-2,y). Read from P3.usdTexture at (x) = file(x-1,y). + borderSide = CalcBorders(myMap, tempP.rsuTexture, + (P2.VertexY % 2 == 0) ? P3.usdTexture : P2.usdTexture, BorderRect); if(borderSide != BorderPreference::None) { Point32 thirdPt; From 30021b8ac1dd0e26370e7f9f52d8caf8a96e9840 Mon Sep 17 00:00:00 2001 From: Morgan Christiansson Date: Mon, 29 Jun 2026 18:20:04 +0200 Subject: [PATCH 5/6] Fix resources & tools --- CIO/CFile.cpp | 11 +-- CMap.cpp | 253 +++++++++++++++++++++++++++----------------------- CSurface.cpp | 29 +++--- 3 files changed, 153 insertions(+), 140 deletions(-) diff --git a/CIO/CFile.cpp b/CIO/CFile.cpp index 5884adb..cc0de8d 100644 --- a/CIO/CFile.cpp +++ b/CIO/CFile.cpp @@ -570,15 +570,12 @@ bobMAP* CFile::read_wld(FILE* fp) // go to texture information for UpSideDown-Triangles fseek(fp, 16, SEEK_CUR); - // Read usdTexture: file → memory (align with s25client convention). - // + // Read usdTexture: file → memory (align with s25client coordinate system). // s25client MapLoader::InitNodes: // unsigned char t2 = map.getMapDataAt(MapLayer::Terrain2, pt.x, pt.y); // node.t2 = getTerrainFromS2(t2 & 0x3F); // stored at same visual (pt.x, pt.y) as t1 - // - // s25edit WLD file stores USD textures with an odd-row visual offset. - // Shift during load so vertex(i,j).usdTexture = USD at visual (i,j), matching node.t2. - // memory(i, j) = file(i - !(j&1), j) + // s25edit WLD file stores USD offsets that need shifting to match client: + // even rows: memory(i, j) = file(i-1, j) for(int j = 0; j < myMap->height; j++) { for(int i = 0; i < myMap->width; i++) @@ -845,7 +842,7 @@ bool CFile::save_wld(FILE* fp, void* data) // go to texture information for UpSideDown-Triangles libendian::write(map_data_header, fp); - // Write usdTexture: inverse of read shift — restores WLD file layout. + // Write usdTexture: inverse of read shift. for(int j = 0; j < myMap->height; j++) { for(int i = 0; i < myMap->width; i++) diff --git a/CMap.cpp b/CMap.cpp index 3921a26..4a56c76 100644 --- a/CMap.cpp +++ b/CMap.cpp @@ -11,6 +11,17 @@ #include "Geometry.h" #include "callbacks.h" #include "globals.h" + +// I/O shift: for even rows, vertex(x,y).usdTexture = file(x-1,y). +// Read .usdTexture from (x+1, y) to get the value for visual (x, y). +static uint8_t usdAt(const bobMAP& map, int x, int y) +{ + if(y % 2 == 0) + { + x = (x + 1 >= map.width) ? 0 : x + 1; + } + return map.getVertex(x, y).usdTexture; +} #include "gameData/LandscapeDesc.h" #include "gameData/TerrainDesc.h" #include @@ -1778,8 +1789,10 @@ void CMap::modifyTexture(Position pos, bool rsu, bool usd) // s25client TerrainRenderer::UpdateTriangleTerrain reads both textures from the same vertex: // terrain[nodeIdx][0] = RSU at (x, y) // terrain[nodeIdx][1] = USD at (x, y) - same vertex as RSU - // After I/O shift, vertex(pos).usdTexture = USD at visual (pos). + // After I/O shift: for even rows, USD at visual (pos) is at vertex(pos.x+1, pos.y). MapNode& vertex = map->getVertex(clientUsdVertexPos(pos)); + // Separate vertex for USD writes on even rows (I/O shift) + MapNode& usdVertex = (pos.y % 2 == 0) ? map->getVertex((pos.x + 1 >= map->width) ? 0 : pos.x + 1, pos.y) : vertex; if(modeContent == TRIANGLE_TEXTURE_MEADOW_MIXED || modeContent == TRIANGLE_TEXTURE_MEADOW_MIXED_HARBOUR) { int newContent = rand() % 3; @@ -1805,13 +1818,13 @@ void CMap::modifyTexture(Position pos, bool rsu, bool usd) if(rsu) vertex.rsuTexture = newContent; if(usd) - vertex.usdTexture = newContent; + usdVertex.usdTexture = newContent; } else { if(rsu) vertex.rsuTexture = modeContent; if(usd) - vertex.usdTexture = modeContent; + usdVertex.usdTexture = modeContent; } // at least setup the possible building and the resources at the vertex and 1 section/2 sections around @@ -2119,13 +2132,17 @@ void CMap::modifyBuild(Position pos) // test if there is snow or lava at the vertex or around the vertex and touching the vertex (first section) if(building > 0x00) { - if(mapVertices[0]->rsuTexture == TRIANGLE_TEXTURE_SNOW || mapVertices[0]->usdTexture == TRIANGLE_TEXTURE_SNOW - || mapVertices[0]->rsuTexture == TRIANGLE_TEXTURE_LAVA || mapVertices[0]->usdTexture == TRIANGLE_TEXTURE_LAVA - || mapVertices[1]->rsuTexture == TRIANGLE_TEXTURE_SNOW || mapVertices[1]->usdTexture == TRIANGLE_TEXTURE_SNOW - || mapVertices[1]->rsuTexture == TRIANGLE_TEXTURE_LAVA || mapVertices[1]->usdTexture == TRIANGLE_TEXTURE_LAVA + if(mapVertices[0]->rsuTexture == TRIANGLE_TEXTURE_SNOW + || usdAt(*map, mapVertices[0]->VertexX, mapVertices[0]->VertexY) == TRIANGLE_TEXTURE_SNOW + || mapVertices[0]->rsuTexture == TRIANGLE_TEXTURE_LAVA + || usdAt(*map, mapVertices[0]->VertexX, mapVertices[0]->VertexY) == TRIANGLE_TEXTURE_LAVA + || mapVertices[1]->rsuTexture == TRIANGLE_TEXTURE_SNOW + || usdAt(*map, mapVertices[1]->VertexX, mapVertices[1]->VertexY) == TRIANGLE_TEXTURE_SNOW + || mapVertices[1]->rsuTexture == TRIANGLE_TEXTURE_LAVA + || usdAt(*map, mapVertices[1]->VertexX, mapVertices[1]->VertexY) == TRIANGLE_TEXTURE_LAVA || mapVertices[2]->rsuTexture == TRIANGLE_TEXTURE_SNOW || mapVertices[2]->rsuTexture == TRIANGLE_TEXTURE_LAVA - || mapVertices[3]->usdTexture == TRIANGLE_TEXTURE_SNOW - || mapVertices[3]->usdTexture == TRIANGLE_TEXTURE_LAVA) + || usdAt(*map, mapVertices[3]->VertexX, mapVertices[3]->VertexY) == TRIANGLE_TEXTURE_SNOW + || usdAt(*map, mapVertices[3]->VertexX, mapVertices[3]->VertexY) == TRIANGLE_TEXTURE_LAVA) { building = 0x00; } @@ -2135,10 +2152,12 @@ void CMap::modifyBuild(Position pos) if(building > 0x01) { if(mapVertices[4]->rsuTexture == TRIANGLE_TEXTURE_SNOW || mapVertices[4]->rsuTexture == TRIANGLE_TEXTURE_LAVA - || mapVertices[5]->usdTexture == TRIANGLE_TEXTURE_SNOW || mapVertices[5]->usdTexture == TRIANGLE_TEXTURE_LAVA - || mapVertices[6]->rsuTexture == TRIANGLE_TEXTURE_SNOW || mapVertices[6]->usdTexture == TRIANGLE_TEXTURE_SNOW + || usdAt(*map, mapVertices[5]->VertexX, mapVertices[5]->VertexY) == TRIANGLE_TEXTURE_SNOW + || usdAt(*map, mapVertices[5]->VertexX, mapVertices[5]->VertexY) == TRIANGLE_TEXTURE_LAVA + || mapVertices[6]->rsuTexture == TRIANGLE_TEXTURE_SNOW + || usdAt(*map, mapVertices[6]->VertexX, mapVertices[6]->VertexY) == TRIANGLE_TEXTURE_SNOW || mapVertices[6]->rsuTexture == TRIANGLE_TEXTURE_LAVA - || mapVertices[6]->usdTexture == TRIANGLE_TEXTURE_LAVA) + || usdAt(*map, mapVertices[6]->VertexX, mapVertices[6]->VertexY) == TRIANGLE_TEXTURE_LAVA) { building = 0x01; } @@ -2149,30 +2168,30 @@ void CMap::modifyBuild(Position pos) { if((mapVertices[0]->rsuTexture == TRIANGLE_TEXTURE_WATER || mapVertices[0]->rsuTexture == TRIANGLE_TEXTURE_SWAMP) - && (mapVertices[0]->usdTexture == TRIANGLE_TEXTURE_WATER - || mapVertices[0]->usdTexture == TRIANGLE_TEXTURE_SWAMP) + && (usdAt(*map, mapVertices[0]->VertexX, mapVertices[0]->VertexY) == TRIANGLE_TEXTURE_WATER + || usdAt(*map, mapVertices[0]->VertexX, mapVertices[0]->VertexY) == TRIANGLE_TEXTURE_SWAMP) && (mapVertices[1]->rsuTexture == TRIANGLE_TEXTURE_WATER || mapVertices[1]->rsuTexture == TRIANGLE_TEXTURE_SWAMP) - && (mapVertices[1]->usdTexture == TRIANGLE_TEXTURE_WATER - || mapVertices[1]->usdTexture == TRIANGLE_TEXTURE_SWAMP) + && (usdAt(*map, mapVertices[1]->VertexX, mapVertices[1]->VertexY) == TRIANGLE_TEXTURE_WATER + || usdAt(*map, mapVertices[1]->VertexX, mapVertices[1]->VertexY) == TRIANGLE_TEXTURE_SWAMP) && (mapVertices[2]->rsuTexture == TRIANGLE_TEXTURE_WATER || mapVertices[2]->rsuTexture == TRIANGLE_TEXTURE_SWAMP) - && (mapVertices[3]->usdTexture == TRIANGLE_TEXTURE_WATER - || mapVertices[3]->usdTexture == TRIANGLE_TEXTURE_SWAMP)) + && (usdAt(*map, mapVertices[3]->VertexX, mapVertices[3]->VertexY) == TRIANGLE_TEXTURE_WATER + || usdAt(*map, mapVertices[3]->VertexX, mapVertices[3]->VertexY) == TRIANGLE_TEXTURE_SWAMP)) { building = 0x00; } else if((mapVertices[0]->rsuTexture == TRIANGLE_TEXTURE_WATER || mapVertices[0]->rsuTexture == TRIANGLE_TEXTURE_SWAMP) - || (mapVertices[0]->usdTexture == TRIANGLE_TEXTURE_WATER - || mapVertices[0]->usdTexture == TRIANGLE_TEXTURE_SWAMP) + || (usdAt(*map, mapVertices[0]->VertexX, mapVertices[0]->VertexY) == TRIANGLE_TEXTURE_WATER + || usdAt(*map, mapVertices[0]->VertexX, mapVertices[0]->VertexY) == TRIANGLE_TEXTURE_SWAMP) || (mapVertices[1]->rsuTexture == TRIANGLE_TEXTURE_WATER || mapVertices[1]->rsuTexture == TRIANGLE_TEXTURE_SWAMP) - || (mapVertices[1]->usdTexture == TRIANGLE_TEXTURE_WATER - || mapVertices[1]->usdTexture == TRIANGLE_TEXTURE_SWAMP) + || (usdAt(*map, mapVertices[1]->VertexX, mapVertices[1]->VertexY) == TRIANGLE_TEXTURE_WATER + || usdAt(*map, mapVertices[1]->VertexX, mapVertices[1]->VertexY) == TRIANGLE_TEXTURE_SWAMP) || (mapVertices[2]->rsuTexture == TRIANGLE_TEXTURE_WATER || mapVertices[2]->rsuTexture == TRIANGLE_TEXTURE_SWAMP) - || (mapVertices[3]->usdTexture == TRIANGLE_TEXTURE_WATER - || mapVertices[3]->usdTexture == TRIANGLE_TEXTURE_SWAMP)) + || (usdAt(*map, mapVertices[3]->VertexX, mapVertices[3]->VertexY) == TRIANGLE_TEXTURE_WATER + || usdAt(*map, mapVertices[3]->VertexX, mapVertices[3]->VertexY) == TRIANGLE_TEXTURE_SWAMP)) { building = 0x01; } @@ -2182,11 +2201,11 @@ void CMap::modifyBuild(Position pos) if(building > 0x01) { if(mapVertices[0]->rsuTexture == TRIANGLE_TEXTURE_STEPPE - || mapVertices[0]->usdTexture == TRIANGLE_TEXTURE_STEPPE + || usdAt(*map, mapVertices[0]->VertexX, mapVertices[0]->VertexY) == TRIANGLE_TEXTURE_STEPPE || mapVertices[1]->rsuTexture == TRIANGLE_TEXTURE_STEPPE - || mapVertices[1]->usdTexture == TRIANGLE_TEXTURE_STEPPE + || usdAt(*map, mapVertices[1]->VertexX, mapVertices[1]->VertexY) == TRIANGLE_TEXTURE_STEPPE || mapVertices[2]->rsuTexture == TRIANGLE_TEXTURE_STEPPE - || mapVertices[3]->usdTexture == TRIANGLE_TEXTURE_STEPPE) + || usdAt(*map, mapVertices[3]->VertexX, mapVertices[3]->VertexY) == TRIANGLE_TEXTURE_STEPPE) { building = 0x01; } @@ -2199,52 +2218,52 @@ void CMap::modifyBuild(Position pos) || mapVertices[0]->rsuTexture == TRIANGLE_TEXTURE_MINING2 || mapVertices[0]->rsuTexture == TRIANGLE_TEXTURE_MINING3 || mapVertices[0]->rsuTexture == TRIANGLE_TEXTURE_MINING4) - && (mapVertices[0]->usdTexture == TRIANGLE_TEXTURE_MINING1 - || mapVertices[0]->usdTexture == TRIANGLE_TEXTURE_MINING2 - || mapVertices[0]->usdTexture == TRIANGLE_TEXTURE_MINING3 - || mapVertices[0]->usdTexture == TRIANGLE_TEXTURE_MINING4) + && (usdAt(*map, mapVertices[0]->VertexX, mapVertices[0]->VertexY) == TRIANGLE_TEXTURE_MINING1 + || usdAt(*map, mapVertices[0]->VertexX, mapVertices[0]->VertexY) == TRIANGLE_TEXTURE_MINING2 + || usdAt(*map, mapVertices[0]->VertexX, mapVertices[0]->VertexY) == TRIANGLE_TEXTURE_MINING3 + || usdAt(*map, mapVertices[0]->VertexX, mapVertices[0]->VertexY) == TRIANGLE_TEXTURE_MINING4) && (mapVertices[1]->rsuTexture == TRIANGLE_TEXTURE_MINING1 || mapVertices[1]->rsuTexture == TRIANGLE_TEXTURE_MINING2 || mapVertices[1]->rsuTexture == TRIANGLE_TEXTURE_MINING3 || mapVertices[1]->rsuTexture == TRIANGLE_TEXTURE_MINING4) - && (mapVertices[1]->usdTexture == TRIANGLE_TEXTURE_MINING1 - || mapVertices[1]->usdTexture == TRIANGLE_TEXTURE_MINING2 - || mapVertices[1]->usdTexture == TRIANGLE_TEXTURE_MINING3 - || mapVertices[1]->usdTexture == TRIANGLE_TEXTURE_MINING4) + && (usdAt(*map, mapVertices[1]->VertexX, mapVertices[1]->VertexY) == TRIANGLE_TEXTURE_MINING1 + || usdAt(*map, mapVertices[1]->VertexX, mapVertices[1]->VertexY) == TRIANGLE_TEXTURE_MINING2 + || usdAt(*map, mapVertices[1]->VertexX, mapVertices[1]->VertexY) == TRIANGLE_TEXTURE_MINING3 + || usdAt(*map, mapVertices[1]->VertexX, mapVertices[1]->VertexY) == TRIANGLE_TEXTURE_MINING4) && (mapVertices[2]->rsuTexture == TRIANGLE_TEXTURE_MINING1 || mapVertices[2]->rsuTexture == TRIANGLE_TEXTURE_MINING2 || mapVertices[2]->rsuTexture == TRIANGLE_TEXTURE_MINING3 || mapVertices[2]->rsuTexture == TRIANGLE_TEXTURE_MINING4) - && (mapVertices[3]->usdTexture == TRIANGLE_TEXTURE_MINING1 - || mapVertices[3]->usdTexture == TRIANGLE_TEXTURE_MINING2 - || mapVertices[3]->usdTexture == TRIANGLE_TEXTURE_MINING3 - || mapVertices[3]->usdTexture == TRIANGLE_TEXTURE_MINING4)) + && (usdAt(*map, mapVertices[3]->VertexX, mapVertices[3]->VertexY) == TRIANGLE_TEXTURE_MINING1 + || usdAt(*map, mapVertices[3]->VertexX, mapVertices[3]->VertexY) == TRIANGLE_TEXTURE_MINING2 + || usdAt(*map, mapVertices[3]->VertexX, mapVertices[3]->VertexY) == TRIANGLE_TEXTURE_MINING3 + || usdAt(*map, mapVertices[3]->VertexX, mapVertices[3]->VertexY) == TRIANGLE_TEXTURE_MINING4)) { building = 0x05; } else if((mapVertices[0]->rsuTexture == TRIANGLE_TEXTURE_MINING1 || mapVertices[0]->rsuTexture == TRIANGLE_TEXTURE_MINING2 || mapVertices[0]->rsuTexture == TRIANGLE_TEXTURE_MINING3 || mapVertices[0]->rsuTexture == TRIANGLE_TEXTURE_MINING4) - || (mapVertices[0]->usdTexture == TRIANGLE_TEXTURE_MINING1 - || mapVertices[0]->usdTexture == TRIANGLE_TEXTURE_MINING2 - || mapVertices[0]->usdTexture == TRIANGLE_TEXTURE_MINING3 - || mapVertices[0]->usdTexture == TRIANGLE_TEXTURE_MINING4) + || (usdAt(*map, mapVertices[0]->VertexX, mapVertices[0]->VertexY) == TRIANGLE_TEXTURE_MINING1 + || usdAt(*map, mapVertices[0]->VertexX, mapVertices[0]->VertexY) == TRIANGLE_TEXTURE_MINING2 + || usdAt(*map, mapVertices[0]->VertexX, mapVertices[0]->VertexY) == TRIANGLE_TEXTURE_MINING3 + || usdAt(*map, mapVertices[0]->VertexX, mapVertices[0]->VertexY) == TRIANGLE_TEXTURE_MINING4) || (mapVertices[1]->rsuTexture == TRIANGLE_TEXTURE_MINING1 || mapVertices[1]->rsuTexture == TRIANGLE_TEXTURE_MINING2 || mapVertices[1]->rsuTexture == TRIANGLE_TEXTURE_MINING3 || mapVertices[1]->rsuTexture == TRIANGLE_TEXTURE_MINING4) - || (mapVertices[1]->usdTexture == TRIANGLE_TEXTURE_MINING1 - || mapVertices[1]->usdTexture == TRIANGLE_TEXTURE_MINING2 - || mapVertices[1]->usdTexture == TRIANGLE_TEXTURE_MINING3 - || mapVertices[1]->usdTexture == TRIANGLE_TEXTURE_MINING4) + || (usdAt(*map, mapVertices[1]->VertexX, mapVertices[1]->VertexY) == TRIANGLE_TEXTURE_MINING1 + || usdAt(*map, mapVertices[1]->VertexX, mapVertices[1]->VertexY) == TRIANGLE_TEXTURE_MINING2 + || usdAt(*map, mapVertices[1]->VertexX, mapVertices[1]->VertexY) == TRIANGLE_TEXTURE_MINING3 + || usdAt(*map, mapVertices[1]->VertexX, mapVertices[1]->VertexY) == TRIANGLE_TEXTURE_MINING4) || (mapVertices[2]->rsuTexture == TRIANGLE_TEXTURE_MINING1 || mapVertices[2]->rsuTexture == TRIANGLE_TEXTURE_MINING2 || mapVertices[2]->rsuTexture == TRIANGLE_TEXTURE_MINING3 || mapVertices[2]->rsuTexture == TRIANGLE_TEXTURE_MINING4) - || (mapVertices[3]->usdTexture == TRIANGLE_TEXTURE_MINING1 - || mapVertices[3]->usdTexture == TRIANGLE_TEXTURE_MINING2 - || mapVertices[3]->usdTexture == TRIANGLE_TEXTURE_MINING3 - || mapVertices[3]->usdTexture == TRIANGLE_TEXTURE_MINING4)) + || (usdAt(*map, mapVertices[3]->VertexX, mapVertices[3]->VertexY) == TRIANGLE_TEXTURE_MINING1 + || usdAt(*map, mapVertices[3]->VertexX, mapVertices[3]->VertexY) == TRIANGLE_TEXTURE_MINING2 + || usdAt(*map, mapVertices[3]->VertexX, mapVertices[3]->VertexY) == TRIANGLE_TEXTURE_MINING3 + || usdAt(*map, mapVertices[3]->VertexX, mapVertices[3]->VertexY) == TRIANGLE_TEXTURE_MINING4)) { building = 0x01; } @@ -2316,20 +2335,20 @@ void CMap::modifyResource(Position pos) || mapVertices[0]->rsuTexture == TRIANGLE_TEXTURE_FLOWER_HARBOUR || mapVertices[0]->rsuTexture == TRIANGLE_TEXTURE_MINING_MEADOW || mapVertices[0]->rsuTexture == TRIANGLE_TEXTURE_MINING_MEADOW_HARBOUR) - && (mapVertices[0]->usdTexture == TRIANGLE_TEXTURE_STEPPE_MEADOW1 - || mapVertices[0]->usdTexture == TRIANGLE_TEXTURE_STEPPE_MEADOW1_HARBOUR - || mapVertices[0]->usdTexture == TRIANGLE_TEXTURE_MEADOW1 - || mapVertices[0]->usdTexture == TRIANGLE_TEXTURE_MEADOW1_HARBOUR - || mapVertices[0]->usdTexture == TRIANGLE_TEXTURE_MEADOW2 - || mapVertices[0]->usdTexture == TRIANGLE_TEXTURE_MEADOW2_HARBOUR - || mapVertices[0]->usdTexture == TRIANGLE_TEXTURE_MEADOW3 - || mapVertices[0]->usdTexture == TRIANGLE_TEXTURE_MEADOW3_HARBOUR - || mapVertices[0]->usdTexture == TRIANGLE_TEXTURE_STEPPE_MEADOW2 - || mapVertices[0]->usdTexture == TRIANGLE_TEXTURE_STEPPE_MEADOW2_HARBOUR - || mapVertices[0]->usdTexture == TRIANGLE_TEXTURE_FLOWER - || mapVertices[0]->usdTexture == TRIANGLE_TEXTURE_FLOWER_HARBOUR - || mapVertices[0]->usdTexture == TRIANGLE_TEXTURE_MINING_MEADOW - || mapVertices[0]->usdTexture == TRIANGLE_TEXTURE_MINING_MEADOW_HARBOUR) + && (usdAt(*map, mapVertices[0]->VertexX, mapVertices[0]->VertexY) == TRIANGLE_TEXTURE_STEPPE_MEADOW1 + || usdAt(*map, mapVertices[0]->VertexX, mapVertices[0]->VertexY) == TRIANGLE_TEXTURE_STEPPE_MEADOW1_HARBOUR + || usdAt(*map, mapVertices[0]->VertexX, mapVertices[0]->VertexY) == TRIANGLE_TEXTURE_MEADOW1 + || usdAt(*map, mapVertices[0]->VertexX, mapVertices[0]->VertexY) == TRIANGLE_TEXTURE_MEADOW1_HARBOUR + || usdAt(*map, mapVertices[0]->VertexX, mapVertices[0]->VertexY) == TRIANGLE_TEXTURE_MEADOW2 + || usdAt(*map, mapVertices[0]->VertexX, mapVertices[0]->VertexY) == TRIANGLE_TEXTURE_MEADOW2_HARBOUR + || usdAt(*map, mapVertices[0]->VertexX, mapVertices[0]->VertexY) == TRIANGLE_TEXTURE_MEADOW3 + || usdAt(*map, mapVertices[0]->VertexX, mapVertices[0]->VertexY) == TRIANGLE_TEXTURE_MEADOW3_HARBOUR + || usdAt(*map, mapVertices[0]->VertexX, mapVertices[0]->VertexY) == TRIANGLE_TEXTURE_STEPPE_MEADOW2 + || usdAt(*map, mapVertices[0]->VertexX, mapVertices[0]->VertexY) == TRIANGLE_TEXTURE_STEPPE_MEADOW2_HARBOUR + || usdAt(*map, mapVertices[0]->VertexX, mapVertices[0]->VertexY) == TRIANGLE_TEXTURE_FLOWER + || usdAt(*map, mapVertices[0]->VertexX, mapVertices[0]->VertexY) == TRIANGLE_TEXTURE_FLOWER_HARBOUR + || usdAt(*map, mapVertices[0]->VertexX, mapVertices[0]->VertexY) == TRIANGLE_TEXTURE_MINING_MEADOW + || usdAt(*map, mapVertices[0]->VertexX, mapVertices[0]->VertexY) == TRIANGLE_TEXTURE_MINING_MEADOW_HARBOUR) && (mapVertices[1]->rsuTexture == TRIANGLE_TEXTURE_STEPPE_MEADOW1 || mapVertices[1]->rsuTexture == TRIANGLE_TEXTURE_STEPPE_MEADOW1_HARBOUR || mapVertices[1]->rsuTexture == TRIANGLE_TEXTURE_MEADOW1 @@ -2344,20 +2363,20 @@ void CMap::modifyResource(Position pos) || mapVertices[1]->rsuTexture == TRIANGLE_TEXTURE_FLOWER_HARBOUR || mapVertices[1]->rsuTexture == TRIANGLE_TEXTURE_MINING_MEADOW || mapVertices[1]->rsuTexture == TRIANGLE_TEXTURE_MINING_MEADOW_HARBOUR) - && (mapVertices[1]->usdTexture == TRIANGLE_TEXTURE_STEPPE_MEADOW1 - || mapVertices[1]->usdTexture == TRIANGLE_TEXTURE_STEPPE_MEADOW1_HARBOUR - || mapVertices[1]->usdTexture == TRIANGLE_TEXTURE_MEADOW1 - || mapVertices[1]->usdTexture == TRIANGLE_TEXTURE_MEADOW1_HARBOUR - || mapVertices[1]->usdTexture == TRIANGLE_TEXTURE_MEADOW2 - || mapVertices[1]->usdTexture == TRIANGLE_TEXTURE_MEADOW2_HARBOUR - || mapVertices[1]->usdTexture == TRIANGLE_TEXTURE_MEADOW3 - || mapVertices[1]->usdTexture == TRIANGLE_TEXTURE_MEADOW3_HARBOUR - || mapVertices[1]->usdTexture == TRIANGLE_TEXTURE_STEPPE_MEADOW2 - || mapVertices[1]->usdTexture == TRIANGLE_TEXTURE_STEPPE_MEADOW2_HARBOUR - || mapVertices[1]->usdTexture == TRIANGLE_TEXTURE_FLOWER - || mapVertices[1]->usdTexture == TRIANGLE_TEXTURE_FLOWER_HARBOUR - || mapVertices[1]->usdTexture == TRIANGLE_TEXTURE_MINING_MEADOW - || mapVertices[1]->usdTexture == TRIANGLE_TEXTURE_MINING_MEADOW_HARBOUR) + && (usdAt(*map, mapVertices[1]->VertexX, mapVertices[1]->VertexY) == TRIANGLE_TEXTURE_STEPPE_MEADOW1 + || usdAt(*map, mapVertices[1]->VertexX, mapVertices[1]->VertexY) == TRIANGLE_TEXTURE_STEPPE_MEADOW1_HARBOUR + || usdAt(*map, mapVertices[1]->VertexX, mapVertices[1]->VertexY) == TRIANGLE_TEXTURE_MEADOW1 + || usdAt(*map, mapVertices[1]->VertexX, mapVertices[1]->VertexY) == TRIANGLE_TEXTURE_MEADOW1_HARBOUR + || usdAt(*map, mapVertices[1]->VertexX, mapVertices[1]->VertexY) == TRIANGLE_TEXTURE_MEADOW2 + || usdAt(*map, mapVertices[1]->VertexX, mapVertices[1]->VertexY) == TRIANGLE_TEXTURE_MEADOW2_HARBOUR + || usdAt(*map, mapVertices[1]->VertexX, mapVertices[1]->VertexY) == TRIANGLE_TEXTURE_MEADOW3 + || usdAt(*map, mapVertices[1]->VertexX, mapVertices[1]->VertexY) == TRIANGLE_TEXTURE_MEADOW3_HARBOUR + || usdAt(*map, mapVertices[1]->VertexX, mapVertices[1]->VertexY) == TRIANGLE_TEXTURE_STEPPE_MEADOW2 + || usdAt(*map, mapVertices[1]->VertexX, mapVertices[1]->VertexY) == TRIANGLE_TEXTURE_STEPPE_MEADOW2_HARBOUR + || usdAt(*map, mapVertices[1]->VertexX, mapVertices[1]->VertexY) == TRIANGLE_TEXTURE_FLOWER + || usdAt(*map, mapVertices[1]->VertexX, mapVertices[1]->VertexY) == TRIANGLE_TEXTURE_FLOWER_HARBOUR + || usdAt(*map, mapVertices[1]->VertexX, mapVertices[1]->VertexY) == TRIANGLE_TEXTURE_MINING_MEADOW + || usdAt(*map, mapVertices[1]->VertexX, mapVertices[1]->VertexY) == TRIANGLE_TEXTURE_MINING_MEADOW_HARBOUR) && (mapVertices[2]->rsuTexture == TRIANGLE_TEXTURE_STEPPE_MEADOW1 || mapVertices[2]->rsuTexture == TRIANGLE_TEXTURE_STEPPE_MEADOW1_HARBOUR || mapVertices[2]->rsuTexture == TRIANGLE_TEXTURE_MEADOW1 @@ -2372,49 +2391,49 @@ void CMap::modifyResource(Position pos) || mapVertices[2]->rsuTexture == TRIANGLE_TEXTURE_FLOWER_HARBOUR || mapVertices[2]->rsuTexture == TRIANGLE_TEXTURE_MINING_MEADOW || mapVertices[2]->rsuTexture == TRIANGLE_TEXTURE_MINING_MEADOW_HARBOUR) - && (mapVertices[3]->usdTexture == TRIANGLE_TEXTURE_STEPPE_MEADOW1 - || mapVertices[3]->usdTexture == TRIANGLE_TEXTURE_STEPPE_MEADOW1_HARBOUR - || mapVertices[3]->usdTexture == TRIANGLE_TEXTURE_MEADOW1 - || mapVertices[3]->usdTexture == TRIANGLE_TEXTURE_MEADOW1_HARBOUR - || mapVertices[3]->usdTexture == TRIANGLE_TEXTURE_MEADOW2 - || mapVertices[3]->usdTexture == TRIANGLE_TEXTURE_MEADOW2_HARBOUR - || mapVertices[3]->usdTexture == TRIANGLE_TEXTURE_MEADOW3 - || mapVertices[3]->usdTexture == TRIANGLE_TEXTURE_MEADOW3_HARBOUR - || mapVertices[3]->usdTexture == TRIANGLE_TEXTURE_STEPPE_MEADOW2 - || mapVertices[3]->usdTexture == TRIANGLE_TEXTURE_STEPPE_MEADOW2_HARBOUR - || mapVertices[3]->usdTexture == TRIANGLE_TEXTURE_FLOWER - || mapVertices[3]->usdTexture == TRIANGLE_TEXTURE_FLOWER_HARBOUR - || mapVertices[3]->usdTexture == TRIANGLE_TEXTURE_MINING_MEADOW - || mapVertices[3]->usdTexture == TRIANGLE_TEXTURE_MINING_MEADOW_HARBOUR)) + && (usdAt(*map, mapVertices[3]->VertexX, mapVertices[3]->VertexY) == TRIANGLE_TEXTURE_STEPPE_MEADOW1 + || usdAt(*map, mapVertices[3]->VertexX, mapVertices[3]->VertexY) == TRIANGLE_TEXTURE_STEPPE_MEADOW1_HARBOUR + || usdAt(*map, mapVertices[3]->VertexX, mapVertices[3]->VertexY) == TRIANGLE_TEXTURE_MEADOW1 + || usdAt(*map, mapVertices[3]->VertexX, mapVertices[3]->VertexY) == TRIANGLE_TEXTURE_MEADOW1_HARBOUR + || usdAt(*map, mapVertices[3]->VertexX, mapVertices[3]->VertexY) == TRIANGLE_TEXTURE_MEADOW2 + || usdAt(*map, mapVertices[3]->VertexX, mapVertices[3]->VertexY) == TRIANGLE_TEXTURE_MEADOW2_HARBOUR + || usdAt(*map, mapVertices[3]->VertexX, mapVertices[3]->VertexY) == TRIANGLE_TEXTURE_MEADOW3 + || usdAt(*map, mapVertices[3]->VertexX, mapVertices[3]->VertexY) == TRIANGLE_TEXTURE_MEADOW3_HARBOUR + || usdAt(*map, mapVertices[3]->VertexX, mapVertices[3]->VertexY) == TRIANGLE_TEXTURE_STEPPE_MEADOW2 + || usdAt(*map, mapVertices[3]->VertexX, mapVertices[3]->VertexY) == TRIANGLE_TEXTURE_STEPPE_MEADOW2_HARBOUR + || usdAt(*map, mapVertices[3]->VertexX, mapVertices[3]->VertexY) == TRIANGLE_TEXTURE_FLOWER + || usdAt(*map, mapVertices[3]->VertexX, mapVertices[3]->VertexY) == TRIANGLE_TEXTURE_FLOWER_HARBOUR + || usdAt(*map, mapVertices[3]->VertexX, mapVertices[3]->VertexY) == TRIANGLE_TEXTURE_MINING_MEADOW + || usdAt(*map, mapVertices[3]->VertexX, mapVertices[3]->VertexY) == TRIANGLE_TEXTURE_MINING_MEADOW_HARBOUR)) { curVertex.resource = 0x21; } // SPECIAL CASE: test if we should set fishes only // test if vertex is surrounded by water (first section) and at least one non-water texture in the second section else if((mapVertices[0]->rsuTexture == TRIANGLE_TEXTURE_WATER) - && (mapVertices[0]->usdTexture == TRIANGLE_TEXTURE_WATER) + && (usdAt(*map, mapVertices[0]->VertexX, mapVertices[0]->VertexY) == TRIANGLE_TEXTURE_WATER) && (mapVertices[1]->rsuTexture == TRIANGLE_TEXTURE_WATER) - && (mapVertices[1]->usdTexture == TRIANGLE_TEXTURE_WATER) + && (usdAt(*map, mapVertices[1]->VertexX, mapVertices[1]->VertexY) == TRIANGLE_TEXTURE_WATER) && (mapVertices[2]->rsuTexture == TRIANGLE_TEXTURE_WATER) - && (mapVertices[3]->usdTexture == TRIANGLE_TEXTURE_WATER) - && (mapVertices[2]->usdTexture != TRIANGLE_TEXTURE_WATER + && (usdAt(*map, mapVertices[3]->VertexX, mapVertices[3]->VertexY) == TRIANGLE_TEXTURE_WATER) + && (usdAt(*map, mapVertices[2]->VertexX, mapVertices[2]->VertexY) != TRIANGLE_TEXTURE_WATER || mapVertices[3]->rsuTexture != TRIANGLE_TEXTURE_WATER || mapVertices[4]->rsuTexture != TRIANGLE_TEXTURE_WATER - || mapVertices[4]->usdTexture != TRIANGLE_TEXTURE_WATER + || usdAt(*map, mapVertices[4]->VertexX, mapVertices[4]->VertexY) != TRIANGLE_TEXTURE_WATER || mapVertices[5]->rsuTexture != TRIANGLE_TEXTURE_WATER - || mapVertices[5]->usdTexture != TRIANGLE_TEXTURE_WATER + || usdAt(*map, mapVertices[5]->VertexX, mapVertices[5]->VertexY) != TRIANGLE_TEXTURE_WATER || mapVertices[6]->rsuTexture != TRIANGLE_TEXTURE_WATER - || mapVertices[6]->usdTexture != TRIANGLE_TEXTURE_WATER + || usdAt(*map, mapVertices[6]->VertexX, mapVertices[6]->VertexY) != TRIANGLE_TEXTURE_WATER || map->getVertex(tempVertices[7]).rsuTexture != TRIANGLE_TEXTURE_WATER - || map->getVertex(tempVertices[7]).usdTexture != TRIANGLE_TEXTURE_WATER + || usdAt(*map, tempVertices[7].x, tempVertices[7].y) != TRIANGLE_TEXTURE_WATER || map->getVertex(tempVertices[8]).rsuTexture != TRIANGLE_TEXTURE_WATER - || map->getVertex(tempVertices[8]).usdTexture != TRIANGLE_TEXTURE_WATER + || usdAt(*map, tempVertices[8].x, tempVertices[8].y) != TRIANGLE_TEXTURE_WATER || map->getVertex(tempVertices[9]).rsuTexture != TRIANGLE_TEXTURE_WATER || map->getVertex(tempVertices[10]).rsuTexture != TRIANGLE_TEXTURE_WATER - || map->getVertex(tempVertices[10]).usdTexture != TRIANGLE_TEXTURE_WATER + || usdAt(*map, tempVertices[10].x, tempVertices[10].y) != TRIANGLE_TEXTURE_WATER || map->getVertex(tempVertices[11]).rsuTexture != TRIANGLE_TEXTURE_WATER - || map->getVertex(tempVertices[12]).usdTexture != TRIANGLE_TEXTURE_WATER - || map->getVertex(tempVertices[14]).usdTexture != TRIANGLE_TEXTURE_WATER)) + || usdAt(*map, tempVertices[12].x, tempVertices[12].y) != TRIANGLE_TEXTURE_WATER + || usdAt(*map, tempVertices[14].x, tempVertices[14].y) != TRIANGLE_TEXTURE_WATER)) { curVertex.resource = 0x87; } @@ -2423,26 +2442,26 @@ void CMap::modifyResource(Position pos) || mapVertices[0]->rsuTexture == TRIANGLE_TEXTURE_MINING2 || mapVertices[0]->rsuTexture == TRIANGLE_TEXTURE_MINING3 || mapVertices[0]->rsuTexture == TRIANGLE_TEXTURE_MINING4) - && (mapVertices[0]->usdTexture == TRIANGLE_TEXTURE_MINING1 - || mapVertices[0]->usdTexture == TRIANGLE_TEXTURE_MINING2 - || mapVertices[0]->usdTexture == TRIANGLE_TEXTURE_MINING3 - || mapVertices[0]->usdTexture == TRIANGLE_TEXTURE_MINING4) + && (usdAt(*map, mapVertices[0]->VertexX, mapVertices[0]->VertexY) == TRIANGLE_TEXTURE_MINING1 + || usdAt(*map, mapVertices[0]->VertexX, mapVertices[0]->VertexY) == TRIANGLE_TEXTURE_MINING2 + || usdAt(*map, mapVertices[0]->VertexX, mapVertices[0]->VertexY) == TRIANGLE_TEXTURE_MINING3 + || usdAt(*map, mapVertices[0]->VertexX, mapVertices[0]->VertexY) == TRIANGLE_TEXTURE_MINING4) && (mapVertices[1]->rsuTexture == TRIANGLE_TEXTURE_MINING1 || mapVertices[1]->rsuTexture == TRIANGLE_TEXTURE_MINING2 || mapVertices[1]->rsuTexture == TRIANGLE_TEXTURE_MINING3 || mapVertices[1]->rsuTexture == TRIANGLE_TEXTURE_MINING4) - && (mapVertices[1]->usdTexture == TRIANGLE_TEXTURE_MINING1 - || mapVertices[1]->usdTexture == TRIANGLE_TEXTURE_MINING2 - || mapVertices[1]->usdTexture == TRIANGLE_TEXTURE_MINING3 - || mapVertices[1]->usdTexture == TRIANGLE_TEXTURE_MINING4) + && (usdAt(*map, mapVertices[1]->VertexX, mapVertices[1]->VertexY) == TRIANGLE_TEXTURE_MINING1 + || usdAt(*map, mapVertices[1]->VertexX, mapVertices[1]->VertexY) == TRIANGLE_TEXTURE_MINING2 + || usdAt(*map, mapVertices[1]->VertexX, mapVertices[1]->VertexY) == TRIANGLE_TEXTURE_MINING3 + || usdAt(*map, mapVertices[1]->VertexX, mapVertices[1]->VertexY) == TRIANGLE_TEXTURE_MINING4) && (mapVertices[2]->rsuTexture == TRIANGLE_TEXTURE_MINING1 || mapVertices[2]->rsuTexture == TRIANGLE_TEXTURE_MINING2 || mapVertices[2]->rsuTexture == TRIANGLE_TEXTURE_MINING3 || mapVertices[2]->rsuTexture == TRIANGLE_TEXTURE_MINING4) - && (mapVertices[3]->usdTexture == TRIANGLE_TEXTURE_MINING1 - || mapVertices[3]->usdTexture == TRIANGLE_TEXTURE_MINING2 - || mapVertices[3]->usdTexture == TRIANGLE_TEXTURE_MINING3 - || mapVertices[3]->usdTexture == TRIANGLE_TEXTURE_MINING4)) + && (usdAt(*map, mapVertices[3]->VertexX, mapVertices[3]->VertexY) == TRIANGLE_TEXTURE_MINING1 + || usdAt(*map, mapVertices[3]->VertexX, mapVertices[3]->VertexY) == TRIANGLE_TEXTURE_MINING2 + || usdAt(*map, mapVertices[3]->VertexX, mapVertices[3]->VertexY) == TRIANGLE_TEXTURE_MINING3 + || usdAt(*map, mapVertices[3]->VertexX, mapVertices[3]->VertexY) == TRIANGLE_TEXTURE_MINING4)) { // check which resource to set if(mode == EDITOR_MODE_RESOURCE_RAISE) diff --git a/CSurface.cpp b/CSurface.cpp index bf1b7b9..edbcadb 100644 --- a/CSurface.cpp +++ b/CSurface.cpp @@ -402,20 +402,18 @@ void CSurface::DrawTriangleField(SDL_Surface* display, const DisplayRectangle& d myMap.getVertex(x - 1, y + 1), myMap.getVertex(x, y + 1)); // USD at visual column x-1 — same vertices as master. // Texture read unshift is applied inside DrawTriangle. - DrawTriangle(display, displayRect, myMap, type, - myMap.getVertex(x - 1, y + 1), // P1 - myMap.getVertex(x - 1, y), // P2 (reads usdTexture, unshifted internally) - myMap.getVertex(x, y)); // P3 + DrawTriangle(display, displayRect, myMap, type, myMap.getVertex(x - 1, y + 1), // P1 + myMap.getVertex(x - 1, y), // P2 (reads usdTexture, unshifted internally) + myMap.getVertex(x, y)); // P3 } // last UpSideDown at column width-1 (wrap) — same vertices as master. { const int w = static_cast(width); MapNode tP3 = myMap.getVertex(0, y); tP3.x = myMap.getVertex(w - 1, y).x + triangleWidth; - DrawTriangle(display, displayRect, myMap, type, - myMap.getVertex(w - 1, y + 1), // P1 - myMap.getVertex(w - 1, y), // P2 (reads usdTexture, unshifted internally) - tP3); // P3 + DrawTriangle(display, displayRect, myMap, type, myMap.getVertex(w - 1, y + 1), // P1 + myMap.getVertex(w - 1, y), // P2 (reads usdTexture, unshifted internally) + tP3); // P3 } } else { @@ -864,15 +862,15 @@ void CSurface::DrawTriangle(SDL_Surface* display, const DisplayRectangle& displa { // upper2, ..... are for special use in winterland. Point16 upper, left, right, upper2, left2, right2; - // I/O shift: USD data moved right by 1 in even rows. - // Read usdTexture from (P2.VertexX+1, P2.VertexY) to compensate. + // I/O shift: USD data at vertex(x,y) = file(x-1,y) for even rows. + // Read from (P2.VertexX+1, P2.VertexY) to get USD at visual (P2.VertexX, P2.VertexY). uint8_t rawTex; if(isRSU) rawTex = P1.rsuTexture; else { rawTex = P2.usdTexture; - if(P2.VertexY % 2 == 0) // even row: unshift + if(P2.VertexY % 2 == 0) { int adjX = (P2.VertexX + 1 >= myMap.width) ? 0 : P2.VertexX + 1; rawTex = myMap.getVertex(adjX, P2.VertexY).usdTexture; @@ -985,9 +983,8 @@ void CSurface::DrawTriangle(SDL_Surface* display, const DisplayRectangle& displa // USD left/right edge: compare RSU vs USD at same visual column. // I/O shift: USD moved right by 1 in even rows. P2.usdTexture at (x-1) // now holds file(x-2,y). Read USD from P3.usdTexture at (x) = file(x-1,y). - auto borderSide = CalcBorders(myMap, - P2.rsuTexture, - (P2.VertexY % 2 == 0) ? P3.usdTexture : P2.usdTexture, BorderRect); + auto borderSide = + CalcBorders(myMap, P2.rsuTexture, (P2.VertexY % 2 == 0) ? P3.usdTexture : P2.usdTexture, BorderRect); if(borderSide != BorderPreference::None) { @@ -1033,8 +1030,8 @@ void CSurface::DrawTriangle(SDL_Surface* display, const DisplayRectangle& displa MapNode tempP = myMap.getVertex(col, row); // I/O shift: P2.usdTexture at (x-1) = file(x-2,y). Read from P3.usdTexture at (x) = file(x-1,y). - borderSide = CalcBorders(myMap, tempP.rsuTexture, - (P2.VertexY % 2 == 0) ? P3.usdTexture : P2.usdTexture, BorderRect); + borderSide = + CalcBorders(myMap, tempP.rsuTexture, (P2.VertexY % 2 == 0) ? P3.usdTexture : P2.usdTexture, BorderRect); if(borderSide != BorderPreference::None) { Point32 thirdPt; From 7320c4526d3d57d3691b18b81a9f682c40ad07b6 Mon Sep 17 00:00:00 2001 From: Morgan Christiansson Date: Mon, 29 Jun 2026 18:27:20 +0200 Subject: [PATCH 6/6] Fix vertical wrap triangle --- CSurface.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/CSurface.cpp b/CSurface.cpp index edbcadb..3039a59 100644 --- a/CSurface.cpp +++ b/CSurface.cpp @@ -439,12 +439,14 @@ void CSurface::DrawTriangleField(SDL_Surface* display, const DisplayRectangle& d // Odd rows have no I/O shift, P2 = (w-1, y) reads file(w-1, y) correctly. { const int w = static_cast(width); + MapNode tP1 = myMap.getVertex(0, y + 1); + tP1.x = myMap.getVertex(w - 1, y + 1).x + triangleWidth; MapNode tP3 = myMap.getVertex(0, y); tP3.x = myMap.getVertex(w - 1, y).x + triangleWidth; DrawTriangle(display, displayRect, myMap, type, - myMap.getVertex(0, y + 1), // P1 = SE(w-1, y) wraps to (0, y+1) - myMap.getVertex(w - 1, y), // P2 = pt (reads usdTexture) - tP3); // P3 = E(w-1, y) wraps to (0, y) + tP1, // P1 = SE(w-1, y) with x-shifted + myMap.getVertex(w - 1, y), // P2 = pt + tP3); // P3 = E(w-1, y) with x-shifted } } }