From ac64fde502f52f830dba80b6f4ba7452390735ec Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Thu, 5 Jan 2023 05:59:45 +0300 Subject: [PATCH] Fix object rendering Previous solution did not work well on high resolutions due to incorrect tile calculations - was not updating edges and sometimes hanged in endless loop trying to find upper-left or bottom-right tiles. New solution follows Sfall's HRP implementation. --- src/object.cc | 49 ++++++++++++++----------------------------------- src/tile.cc | 11 ++++++++++- src/tile.h | 2 +- 3 files changed, 25 insertions(+), 37 deletions(-) diff --git a/src/object.cc b/src/object.cc index 0b3fb40..60b3bd3 100644 --- a/src/object.cc +++ b/src/object.cc @@ -768,47 +768,23 @@ void _obj_render_pre_roof(Rect* rect, int elevation) int minY = updatedRect.top - 240; int maxX = updatedRect.right + 320; int maxY = updatedRect.bottom + 240; - int topLeftTile = tileFromScreenXY(minX, minY, elevation); + int upperLeftTile = tileFromScreenXY(minX, minY, elevation, true); int updateAreaHexWidth = (maxX - minX + 1) / 32; int updateAreaHexHeight = (maxY - minY + 1) / 12; - - // On some maps (which were designed too close to edges) HRP brings a new - // problem - extended update rect (+/- 320/240 stuff above) may end up - // outside of the map edge. In this case `topLeftTile` will be -1 which - // affect all subsequent calculations. In order to fix that attempt to - // find closest valid tile. - while (!hexGridTileIsValid(topLeftTile)) { - minX += 32; - minY += 12; - topLeftTile = tileFromScreenXY(minX, minY, elevation); - } - - // Do the same for the for bottom-right part of the extended update rect. - int bottomRightTile = tileFromScreenXY(maxX, maxY, elevation); - while (!hexGridTileIsValid(bottomRightTile)) { - maxX -= 32; - maxY -= 12; - bottomRightTile = tileFromScreenXY(maxX, maxY, elevation); - } - - updateAreaHexWidth = (maxX - minX + 1) / 32; - updateAreaHexHeight = (maxY - minY + 1) / 12; - int parity = gCenterTile & 1; - int* orders = _orderTable[parity]; - int* offsets = _offsetTable[parity]; _outlineCount = 0; int renderCount = 0; for (int i = 0; i < gObjectsUpdateAreaHexSize; i++) { - int offsetIndex = *orders++; + int offsetIndex = _orderTable[parity][i]; if (updateAreaHexHeight > _offsetDivTable[offsetIndex] && updateAreaHexWidth > _offsetModTable[offsetIndex]) { - int lightIntensity; - - ObjectListNode* objectListNode = hexGridTileIsValid(topLeftTile + offsets[offsetIndex]) - ? gObjectListHeadByTile[topLeftTile + offsets[offsetIndex]] + int tile = upperLeftTile + _offsetTable[parity][offsetIndex]; + ObjectListNode* objectListNode = hexGridTileIsValid(tile) + ? gObjectListHeadByTile[tile] : NULL; + + int lightIntensity; if (objectListNode != NULL) { // NOTE: Calls `lightGetTileIntensity` twice. lightIntensity = std::max(ambientIntensity, lightGetTileIntensity(elevation, objectListNode->obj->tile)); @@ -2995,7 +2971,7 @@ int _obj_intersects_with(Object* object, int x, int y) // 0x48C5C4 int _obj_create_intersect_list(int x, int y, int elevation, int objectType, ObjectWithFlags** entriesPtr) { - int v5 = tileFromScreenXY(x - 320, y - 240, elevation); + int upperLeftTile = tileFromScreenXY(x - 320, y - 240, elevation, true); *entriesPtr = NULL; if (gObjectsUpdateAreaHexSize <= 0) { @@ -3006,9 +2982,12 @@ int _obj_create_intersect_list(int x, int y, int elevation, int objectType, Obje int parity = gCenterTile & 1; for (int index = 0; index < gObjectsUpdateAreaHexSize; index++) { - int v7 = _orderTable[parity][index]; - if (_offsetDivTable[v7] < 30 && _offsetModTable[v7] < 20) { - ObjectListNode* objectListNode = gObjectListHeadByTile[_offsetTable[parity][v7] + v5]; + int offsetIndex = _orderTable[parity][index]; + if (_offsetDivTable[offsetIndex] < 30 && _offsetModTable[offsetIndex] < 20) { + int tile = _offsetTable[parity][offsetIndex] + upperLeftTile; + ObjectListNode* objectListNode = hexGridTileIsValid(tile) + ? gObjectListHeadByTile[tile] + : NULL; while (objectListNode != NULL) { Object* object = objectListNode->obj; if (object->elevation > elevation) { diff --git a/src/tile.cc b/src/tile.cc index f52705a..a7e98aa 100644 --- a/src/tile.cc +++ b/src/tile.cc @@ -692,8 +692,13 @@ int tileToScreenXY(int tile, int* screenX, int* screenY, int elevation) return 0; } +// CE: Added optional `ignoreBounds` param to return tile number without +// validating hex grid bounds. The resulting invalid tile number serves as an +// origin for calculations using prepared offsets table during objects +// rendering. +// // 0x4B1754 -int tileFromScreenXY(int screenX, int screenY, int elevation) +int tileFromScreenXY(int screenX, int screenY, int elevation, bool ignoreBounds) { int v2; int v3; @@ -763,6 +768,10 @@ int tileFromScreenXY(int screenX, int screenY, int elevation) return gHexGridWidth * v10 + v12; } + if (ignoreBounds) { + return gHexGridWidth * v10 + v12; + } + return -1; } diff --git a/src/tile.h b/src/tile.h index 398e777..ae227d2 100644 --- a/src/tile.h +++ b/src/tile.h @@ -28,7 +28,7 @@ void tileWindowRefresh(); int tileSetCenter(int tile, int flags); int tileRoofIsVisible(); int tileToScreenXY(int tile, int* x, int* y, int elevation); -int tileFromScreenXY(int x, int y, int elevation); +int tileFromScreenXY(int x, int y, int elevation, bool ignoreBounds = false); int tileDistanceBetween(int a1, int a2); bool tileIsInFrontOf(int tile1, int tile2); bool tileIsToRightOf(int tile1, int tile2);