diff --git a/Source/diablo.cpp b/Source/diablo.cpp index c0559cde1d6..b39ebcffd94 100644 --- a/Source/diablo.cpp +++ b/Source/diablo.cpp @@ -2857,6 +2857,8 @@ void LoadGameLevel(bool firstflag, lvl_entry lvldir) IncProgress(); LoadTrns(); MakeLightTable(); + LoadLevelSOLData(); + IncProgress(); LoadLvlGFX(); SetDungeonMicros(); ClearClxDrawCache(); @@ -2911,7 +2913,6 @@ void LoadGameLevel(bool firstflag, lvl_entry lvldir) if (!setlevel) { CreateLevel(lvldir); IncProgress(); - LoadLevelSOLData(); SetRndSeed(DungeonSeeds[currlevel]); if (leveltype != DTYPE_TOWN) { @@ -3039,8 +3040,6 @@ void LoadGameLevel(bool firstflag, lvl_entry lvldir) } InitCorpses(); IncProgress(); - LoadLevelSOLData(); - IncProgress(); if (lvldir == ENTRY_WARPLVL) GetPortalLvlPos(); diff --git a/Source/engine/render/dun_render.cpp b/Source/engine/render/dun_render.cpp index fc9c5af4212..b3c2bafe44c 100644 --- a/Source/engine/render/dun_render.cpp +++ b/Source/engine/render/dun_render.cpp @@ -49,18 +49,11 @@ constexpr int_fast16_t TriangleUpperHeight = DunFrameHeight / 2 - 1; /** Height of the upper rectangle of a trapezoid tile. */ constexpr int_fast16_t TrapezoidUpperHeight = DunFrameHeight / 2; -constexpr int_fast16_t TriangleHeight = LowerHeight + TriangleUpperHeight; +constexpr int_fast16_t TriangleHeight = DunFrameTriangleHeight; /** For triangles, for each pixel drawn vertically, this many pixels are drawn horizontally. */ constexpr int_fast16_t XStep = 2; -int_fast16_t GetTileHeight(TileType tile) -{ - if (tile == TileType::LeftTriangle || tile == TileType::RightTriangle) - return TriangleHeight; - return Height; -} - #ifdef DEBUG_STR std::pair GetTileDebugStr(TileType tile) { @@ -322,10 +315,12 @@ DVL_ALWAYS_INLINE DVL_ATTRIBUTE_HOT void RenderSquare(uint8_t *DVL_RESTRICT dst, } template -DVL_ALWAYS_INLINE DVL_ATTRIBUTE_HOT void RenderTransparentSquareFull(uint8_t *DVL_RESTRICT dst, uint16_t dstPitch, const uint8_t *DVL_RESTRICT src, const uint8_t *DVL_RESTRICT tbl) +DVL_ALWAYS_INLINE DVL_ATTRIBUTE_HOT void RenderTransparentSquareFull(uint8_t *DVL_RESTRICT dst, uint16_t dstPitch, const uint8_t *DVL_RESTRICT src, const uint8_t *DVL_RESTRICT tbl, unsigned height) { int8_t prefix = InitPrefix(); - for (auto i = 0; i < Height; ++i, dst -= dstPitch + Width) { + DVL_ASSUME(height >= 16); + DVL_ASSUME(height <= 32); + for (unsigned i = 0; i < height; ++i, dst -= dstPitch + Width) { uint_fast8_t drawWidth = Width; while (drawWidth > 0) { auto v = static_cast(*src++); @@ -427,8 +422,8 @@ DVL_ALWAYS_INLINE DVL_ATTRIBUTE_HOT void RenderTransparentSquareClipped(uint8_t template DVL_ALWAYS_INLINE DVL_ATTRIBUTE_HOT void RenderTransparentSquare(uint8_t *DVL_RESTRICT dst, uint16_t dstPitch, const uint8_t *DVL_RESTRICT src, const uint8_t *DVL_RESTRICT tbl, Clip clip) { - if (clip.width == Width && clip.height == Height) { - RenderTransparentSquareFull(dst, dstPitch, src, tbl); + if (clip.width == Width && clip.bottom == 0 && clip.top == 0) { + RenderTransparentSquareFull(dst, dstPitch, src, tbl, clip.height); } else { RenderTransparentSquareClipped(dst, dstPitch, src, tbl, clip); } @@ -972,18 +967,6 @@ DVL_ALWAYS_INLINE DVL_ATTRIBUTE_HOT void RenderTileType(TileType tile, uint8_t * } } -template -DVL_ALWAYS_INLINE DVL_ATTRIBUTE_HOT void RenderTransparentSquareDispatch(uint8_t *DVL_RESTRICT dst, uint16_t dstPitch, const uint8_t *DVL_RESTRICT src, const uint8_t *DVL_RESTRICT tbl, Clip clip) -{ - if (IsFullyDark(tbl)) { - RenderTransparentSquare(dst, dstPitch, src, tbl, clip); - } else if (IsFullyLit(tbl)) { - RenderTransparentSquare(dst, dstPitch, src, tbl, clip); - } else { - RenderTransparentSquare(dst, dstPitch, src, tbl, clip); - } -} - template DVL_ALWAYS_INLINE DVL_ATTRIBUTE_HOT void RenderLeftTrapezoidOrTransparentSquare(TileType tile, uint8_t *DVL_RESTRICT dst, uint16_t dstPitch, const uint8_t *DVL_RESTRICT src, const uint8_t *DVL_RESTRICT tbl, Clip clip) { @@ -1086,27 +1069,18 @@ std::string_view MaskTypeToString(MaskType maskType) } #endif -void RenderTile(const Surface &out, Point position, - LevelCelBlock levelCelBlock, MaskType maskType, const uint8_t *tbl) +void DVL_ATTRIBUTE_HOT RenderTileFrame(const Surface &out, const Point &position, TileType tile, const uint8_t *src, int_fast16_t height, + MaskType maskType, const uint8_t *tbl) { - const TileType tile = levelCelBlock.type(); - #ifdef DEBUG_RENDER_OFFSET_X position.x += DEBUG_RENDER_OFFSET_X; #endif #ifdef DEBUG_RENDER_OFFSET_Y position.y += DEBUG_RENDER_OFFSET_Y; #endif -#ifdef DEBUG_RENDER_COLOR - DBGCOLOR = GetTileDebugColor(tile); -#endif - - const Clip clip = CalculateClip(position.x, position.y, Width, GetTileHeight(tile), out); - if (clip.width <= 0 || clip.height <= 0) - return; + const Clip clip = CalculateClip(position.x, position.y, DunFrameWidth, height, out); + if (clip.width <= 0 || clip.height <= 0) return; - const auto *pFrameTable = reinterpret_cast(pDungeonCels.get()); - const auto *src = reinterpret_cast(&pDungeonCels[SDL_SwapLE32(pFrameTable[levelCelBlock.frame()])]); uint8_t *dst = out.at(static_cast(position.x + clip.left), static_cast(position.y - clip.bottom)); const uint16_t dstPitch = out.pitch(); @@ -1127,12 +1101,6 @@ void RenderTile(const Surface &out, Point position, case MaskType::Right: RenderRightTrapezoidOrTransparentSquareDispatch(tile, dst, dstPitch, src, tbl, clip); break; - case MaskType::LeftFoliage: - RenderTransparentSquareDispatch(dst, dstPitch, src, tbl, clip); - break; - case MaskType::RightFoliage: - RenderTransparentSquareDispatch(dst, dstPitch, src, tbl, clip); - break; } #ifdef DEBUG_STR diff --git a/Source/engine/render/dun_render.hpp b/Source/engine/render/dun_render.hpp index 684c8322094..98ab8c101db 100644 --- a/Source/engine/render/dun_render.hpp +++ b/Source/engine/render/dun_render.hpp @@ -7,11 +7,11 @@ #include -#include - #include "engine/point.hpp" #include "engine/surface.hpp" #include "levels/dun_tile.hpp" +#include "levels/gendung.h" +#include "utils/attributes.h" // #define DUN_RENDER_STATS #ifdef DUN_RENDER_STATS @@ -85,60 +85,6 @@ enum class MaskType : uint8_t { * 🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆 */ Left, - - /** - * @brief Only the upper-left triangle is rendered. - * - * Can only be used with `TileType::TransparentSquare`. - * - * The lower 16 rows are skipped. - * The upper 16 rows are arranged like this (🮆 = opaque, 🮐 = not rendered): - * - * 🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆 - * 🮐🮐🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆 - * 🮐🮐🮐🮐🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆 - * 🮐🮐🮐🮐🮐🮐🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆 - * 🮐🮐🮐🮐🮐🮐🮐🮐🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆 - * 🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆 - * 🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆 - * 🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆 - * 🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆 - * 🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆 - * 🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆 - * 🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆 - * 🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮆🮆🮆🮆🮆🮆🮆🮆 - * 🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮆🮆🮆🮆🮆🮆 - * 🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮆🮆🮆🮆 - * 🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮆🮆 - */ - RightFoliage, - - /** - * @brief Only the upper right triangle is rendered. - * - * Can only be used with `TileType::TransparentSquare`. - * - * The lower 16 rows are skipped. - * The upper 16 rows are arranged like this (🮆 = opaque, 🮐 = not rendered): - * - * 🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆 - * 🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮐🮐 - * 🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮐🮐🮐🮐 - * 🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮐🮐🮐🮐🮐🮐 - * 🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮐🮐🮐🮐🮐🮐🮐🮐 - * 🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐 - * 🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐 - * 🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐 - * 🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐 - * 🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐 - * 🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐 - * 🮆🮆🮆🮆🮆🮆🮆🮆🮆🮆🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐 - * 🮆🮆🮆🮆🮆🮆🮆🮆🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐 - * 🮆🮆🮆🮆🮆🮆🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐 - * 🮆🮆🮆🮆🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐 - * 🮆🮆🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐🮐 - */ - LeftFoliage, }; #ifdef DUN_RENDER_STATS @@ -163,6 +109,12 @@ std::string_view TileTypeToString(TileType tileType); std::string_view MaskTypeToString(MaskType maskType); #endif +/** + * @brief Low-level tile rendering function. + */ +void RenderTileFrame(const Surface &out, const Point &position, TileType tile, const uint8_t *src, int_fast16_t height, + MaskType maskType, const uint8_t *tbl); + /** * @brief Blit current world CEL to the given buffer * @param out Target buffer @@ -171,8 +123,26 @@ std::string_view MaskTypeToString(MaskType maskType); * @param maskType The mask to use, * @param tbl LightTable or TRN for a tile. */ -void RenderTile(const Surface &out, Point position, - LevelCelBlock levelCelBlock, MaskType maskType, const uint8_t *tbl); +DVL_ALWAYS_INLINE void RenderTile(const Surface &out, const Point &position, + LevelCelBlock levelCelBlock, MaskType maskType, const uint8_t *tbl) +{ + const TileType tileType = levelCelBlock.type(); + RenderTileFrame(out, position, tileType, + GetDunFrame(levelCelBlock.frame()), + (tileType == TileType::LeftTriangle || tileType == TileType::RightTriangle) + ? DunFrameTriangleHeight + : DunFrameHeight, + maskType, tbl); +} + +/** + * @brief Renders a floor foliage tile. + */ +DVL_ALWAYS_INLINE void RenderTileFoliage(const Surface &out, const Point &position, LevelCelBlock levelCelBlock, const uint8_t *tbl) +{ + RenderTileFrame(out, Point { position.x, position.y - 16 }, TileType::TransparentSquare, + GetDunFrameFoliage(levelCelBlock.frame()), /*height=*/16, MaskType::Solid, tbl); +} /** * @brief Render a black 64x31 tile ◆ diff --git a/Source/engine/render/scrollrt.cpp b/Source/engine/render/scrollrt.cpp index cd4e719676f..faeef5f49fe 100644 --- a/Source/engine/render/scrollrt.cpp +++ b/Source/engine/render/scrollrt.cpp @@ -29,6 +29,7 @@ #include "hwcursor.hpp" #include "init.h" #include "inv.h" +#include "levels/dun_tile.hpp" #include "levels/gendung.h" #include "lighting.h" #include "lua/lua.hpp" @@ -481,7 +482,6 @@ void DrawCell(const Surface &out, Point tilePosition, Point targetBufferPosition if ((SDL_GetModState() & KMOD_ALT) != 0) transparency = false; #endif - const bool foliage = IsFloor(tilePosition); const auto getFirstTileMaskLeft = [=](TileType tile) -> MaskType { if (transparency) { @@ -497,8 +497,6 @@ void DrawCell(const Surface &out, Point tilePosition, Point targetBufferPosition return MaskType::Transparent; } } - if (foliage) - return MaskType::LeftFoliage; return MaskType::Solid; }; @@ -516,40 +514,34 @@ void DrawCell(const Surface &out, Point tilePosition, Point targetBufferPosition return MaskType::Transparent; } } - if (foliage) - return MaskType::RightFoliage; return MaskType::Solid; }; - // The first micro tile may be rendered with a foliage mask. - // Only `TransparentSquare` tiles are rendered when `foliage` is true. - { - { - const LevelCelBlock levelCelBlock { pMap->mt[0] }; - const TileType tileType = levelCelBlock.type(); - const MaskType maskType = getFirstTileMaskLeft(tileType); - if (levelCelBlock.hasValue()) { - if (maskType != MaskType::LeftFoliage || tileType == TileType::TransparentSquare) { - RenderTile(out, targetBufferPosition, - levelCelBlock, maskType, tbl); - } + // If the first micro tile is a floor tile, it may be followed + // by foliage which should be rendered now. + const bool isFloor = IsFloor(tilePosition); + if (const LevelCelBlock levelCelBlock { pMap->mt[0] }; levelCelBlock.hasValue()) { + const TileType tileType = levelCelBlock.type(); + if (!isFloor || tileType == TileType::TransparentSquare) { + if (isFloor && tileType == TileType::TransparentSquare) { + RenderTileFoliage(out, targetBufferPosition, levelCelBlock, tbl); + } else { + RenderTile(out, targetBufferPosition, levelCelBlock, getFirstTileMaskLeft(tileType), tbl); } } - { - const LevelCelBlock levelCelBlock { pMap->mt[1] }; - const TileType tileType = levelCelBlock.type(); - const MaskType maskType = getFirstTileMaskRight(tileType); - if (levelCelBlock.hasValue()) { - if (transparency || !foliage || levelCelBlock.type() == TileType::TransparentSquare) { - if (maskType != MaskType::RightFoliage || tileType == TileType::TransparentSquare) { - RenderTile(out, targetBufferPosition + Displacement { TILE_WIDTH / 2, 0 }, - levelCelBlock, maskType, tbl); - } - } + } + if (const LevelCelBlock levelCelBlock { pMap->mt[1] }; levelCelBlock.hasValue()) { + const TileType tileType = levelCelBlock.type(); + if (!isFloor || tileType == TileType::TransparentSquare) { + if (isFloor && tileType == TileType::TransparentSquare) { + RenderTileFoliage(out, targetBufferPosition + Displacement { TILE_WIDTH / 2, 0 }, levelCelBlock, tbl); + } else { + RenderTile(out, targetBufferPosition + Displacement { TILE_WIDTH / 2, 0 }, + levelCelBlock, getFirstTileMaskRight(tileType), tbl); } } - targetBufferPosition.y -= TILE_HEIGHT; } + targetBufferPosition.y -= TILE_HEIGHT; for (uint_fast8_t i = 2, n = MicroTileLen; i < n; i += 2) { { @@ -592,15 +584,15 @@ void DrawFloorTile(const Surface &out, Point tilePosition, Point targetBufferPos { const LevelCelBlock levelCelBlock { DPieceMicros[levelPieceId].mt[0] }; if (levelCelBlock.hasValue()) { - RenderTile(out, targetBufferPosition, - levelCelBlock, MaskType::Solid, tbl); + RenderTileFrame(out, targetBufferPosition, TileType::LeftTriangle, + GetDunFrame(levelCelBlock.frame()), DunFrameTriangleHeight, MaskType::Solid, tbl); } } { const LevelCelBlock levelCelBlock { DPieceMicros[levelPieceId].mt[1] }; if (levelCelBlock.hasValue()) { - RenderTile(out, targetBufferPosition + Displacement { TILE_WIDTH / 2, 0 }, - levelCelBlock, MaskType::Solid, tbl); + RenderTileFrame(out, targetBufferPosition + Displacement { TILE_WIDTH / 2, 0 }, TileType::RightTriangle, + GetDunFrame(levelCelBlock.frame()), DunFrameTriangleHeight, MaskType::Solid, tbl); } } } diff --git a/Source/levels/dun_tile.hpp b/Source/levels/dun_tile.hpp index 0509dd574a2..447c5943444 100644 --- a/Source/levels/dun_tile.hpp +++ b/Source/levels/dun_tile.hpp @@ -2,6 +2,8 @@ #include +#include "utils/enum_traits.h" + #define TILE_WIDTH 64 #define TILE_HEIGHT 32 @@ -100,10 +102,30 @@ struct LevelCelBlock { } }; +enum class TileProperties : uint8_t { + // clang-format off + None = 0, + Solid = 1 << 0, + BlockLight = 1 << 1, + BlockMissile = 1 << 2, + Transparent = 1 << 3, + TransparentLeft = 1 << 4, + TransparentRight = 1 << 5, + Trap = 1 << 7, + // clang-format on +}; +use_enum_as_flags(TileProperties); + +struct MICROS { + LevelCelBlock mt[16]; +}; + /** Width of a tile rendering primitive. */ constexpr int_fast16_t DunFrameWidth = TILE_WIDTH / 2; /** Height of a tile rendering primitive (except triangles). */ constexpr int_fast16_t DunFrameHeight = TILE_HEIGHT; +constexpr int_fast16_t DunFrameTriangleHeight = 31; + } // namespace devilution diff --git a/Source/levels/gendung.cpp b/Source/levels/gendung.cpp index d894a7ff817..4a59eb13435 100644 --- a/Source/levels/gendung.cpp +++ b/Source/levels/gendung.cpp @@ -23,6 +23,7 @@ #include "lighting.h" #include "options.h" #include "utils/bitset2d.hpp" +#include "utils/log.hpp" namespace devilution { @@ -499,22 +500,25 @@ void SetDungeonMicros() size_t tileCount; std::unique_ptr levelPieces = LoadMinData(tileCount); - ankerl::unordered_dense::map frameToTypeMap; + ankerl::unordered_dense::map frameToTypeMap; frameToTypeMap.reserve(4096); - for (size_t i = 0; i < tileCount / blocks; i++) { - uint16_t *pieces = &levelPieces[blocks * i]; - for (size_t block = 0; block < blocks; block++) { + for (size_t levelPieceId = 0; levelPieceId < tileCount / blocks; levelPieceId++) { + uint16_t *pieces = &levelPieces[blocks * levelPieceId]; + for (uint32_t block = 0; block < blocks; block++) { const LevelCelBlock levelCelBlock { SDL_SwapLE16(pieces[blocks - 2 + (block & 1) - (block & 0xE)]) }; - DPieceMicros[i].mt[block] = levelCelBlock; + DPieceMicros[levelPieceId].mt[block] = levelCelBlock; if (levelCelBlock.hasValue()) { if (const auto it = frameToTypeMap.find(levelCelBlock.frame()); it == frameToTypeMap.end()) { - frameToTypeMap.emplace_hint(it, levelCelBlock.frame(), levelCelBlock.type()); + frameToTypeMap.emplace_hint(it, levelCelBlock.frame(), + DunFrameInfo { static_cast(block), levelCelBlock.type(), SOLData[levelPieceId] }); } } } } - std::vector> frameToTypeList = std::move(frameToTypeMap).extract(); - c_sort(frameToTypeList); + std::vector> frameToTypeList = std::move(frameToTypeMap).extract(); + c_sort(frameToTypeList, [](const std::pair &a, const std::pair &b) { + return a.first < b.first; + }); ReencodeDungeonCels(pDungeonCels, frameToTypeList); } diff --git a/Source/levels/gendung.h b/Source/levels/gendung.h index d92eedbcce0..716333f56f6 100644 --- a/Source/levels/gendung.h +++ b/Source/levels/gendung.h @@ -105,20 +105,6 @@ enum class DungeonFlag : uint8_t { }; use_enum_as_flags(DungeonFlag); -enum class TileProperties : uint8_t { - // clang-format off - None = 0, - Solid = 1 << 0, - BlockLight = 1 << 1, - BlockMissile = 1 << 2, - Transparent = 1 << 3, - TransparentLeft = 1 << 4, - TransparentRight = 1 << 5, - Trap = 1 << 7, - // clang-format on -}; -use_enum_as_flags(TileProperties); - enum _difficulty : uint8_t { DIFF_NORMAL, DIFF_NIGHTMARE, @@ -139,10 +125,6 @@ struct MegaTile { uint16_t micro4; }; -struct MICROS { - LevelCelBlock mt[16]; -}; - struct ShadowStruct { uint8_t strig; uint8_t s1; @@ -377,4 +359,15 @@ bool IsNearThemeRoom(WorldTilePosition position); void InitLevels(); void FloodTransparencyValues(uint8_t floorID); +DVL_ALWAYS_INLINE const uint8_t *GetDunFrame(uint32_t frame) +{ + const auto *pFrameTable = reinterpret_cast(pDungeonCels.get()); + return reinterpret_cast(&pDungeonCels[SDL_SwapLE32(pFrameTable[frame])]); +} + +DVL_ALWAYS_INLINE const uint8_t *GetDunFrameFoliage(uint32_t frame) +{ + return GetDunFrame(frame) + (544 - 32); +} + } // namespace devilution diff --git a/Source/levels/reencode_dun_cels.cpp b/Source/levels/reencode_dun_cels.cpp index 43c4fcf1c4e..a1149a5497c 100644 --- a/Source/levels/reencode_dun_cels.cpp +++ b/Source/levels/reencode_dun_cels.cpp @@ -106,17 +106,120 @@ DVL_ALWAYS_INLINE void ReencodeDungeonCelsRightTrapezoid(uint8_t *&dst, const ui dst += DunFrameWidth * 16; } -size_t GetReencodedSize(const uint8_t *dungeonCels, std::span> frames) +DVL_ALWAYS_INLINE void RenderTransparentSquare(uint8_t *dst, const uint8_t *src) +{ + for (unsigned i = 0; i < DunFrameHeight; ++i, dst -= 2 * DunFrameWidth) { + uint_fast8_t drawWidth = DunFrameWidth; + while (drawWidth > 0) { + auto v = static_cast(*src++); + if (v > 0) { + std::memcpy(dst, src, v); + src += v; + } else { + v = static_cast(-v); + } + dst += v; + drawWidth -= v; + } + } +} + +DVL_ALWAYS_INLINE void ExtractFoliageLeftTriangle(uint8_t *&dst, uint8_t *src) +{ + for (int w = 2, y = 31; y >= 16; --y, w += 2, src -= DunFrameWidth) { + std::memcpy(dst, src + (DunFrameWidth - w), w); + std::memset(src + (DunFrameWidth - w), 0, w); + dst += w; + } + for (int w = 30, y = 15; y > 0; --y, w -= 2, src -= DunFrameWidth) { + std::memcpy(dst, src + (DunFrameWidth - w), w); + std::memset(src + (DunFrameWidth - w), 0, w); + dst += w; + } +} + +DVL_ALWAYS_INLINE void ExtractFoliageRightTriangle(uint8_t *&dst, uint8_t *src) +{ + for (int w = 2, y = 31; y >= 16; --y, w += 2, src -= DunFrameWidth) { + std::memcpy(dst, src, w); + std::memset(src, 0, w); + dst += w; + } + for (int w = 30, y = 15; y > 0; --y, w -= 2, src -= DunFrameWidth) { + std::memcpy(dst, src, w); + std::memset(src, 0, w); + dst += w; + } +} + +DVL_ALWAYS_INLINE void ExtractFoliageTransparentSquare(uint8_t *&dst, const uint8_t *src) +{ + // The bottom 16 lines are always transparent, foliage only + // applies to the upper half of the tile. + src -= DunFrameHeight * 16; + for (int y = 16; y > 0; --y, src -= 2 * DunFrameWidth) { + unsigned transparentRun = 0; + unsigned solidRun = 0; + for (int x = 0; x < DunFrameWidth; ++x) { + if (*src++ != 0) { + if (transparentRun != 0) { + *dst++ = static_cast(-static_cast(transparentRun)); + transparentRun = 0; + } + ++solidRun; + } else { + if (solidRun != 0) { + *dst++ = solidRun; + std::memcpy(dst, src - solidRun, solidRun); + dst += solidRun; + solidRun = 0; + } + ++transparentRun; + } + } + if (transparentRun != 0) { + *dst++ = static_cast(-static_cast(transparentRun)); + } else if (solidRun != 0) { + *dst++ = solidRun; + std::memcpy(dst, src - solidRun, solidRun); + dst += solidRun; + } + } +} + +DVL_ALWAYS_INLINE void ReencodeFloorWithFoliage(uint8_t *&dst, const uint8_t *&src, TileType tileType) +{ + uint8_t surface[DunFrameWidth * DunFrameHeight] {}; + uint8_t *surfaceLastLine = &surface[DunFrameWidth * (DunFrameHeight - 1)]; + RenderTransparentSquare(surfaceLastLine, src); + if (tileType == TileType::LeftTriangle) { + ExtractFoliageLeftTriangle(dst, surfaceLastLine); + } else { + ExtractFoliageRightTriangle(dst, surfaceLastLine); + } + ExtractFoliageTransparentSquare(dst, surfaceLastLine); +} + +size_t GetReencodedSize(const uint8_t *dungeonCels, std::span> frames) { size_t result = (2 + frames.size()) * 4; const auto *srcOffsets = reinterpret_cast(dungeonCels); - for (const auto &[frame, type] : frames) { + for (const auto &[frame, info] : frames) { size_t frameSize; - switch (type) { + switch (info.type) { case TileType::TransparentSquare: { const uint32_t srcFrameBegin = SDL_SwapLE32(srcOffsets[frame]); - const uint32_t srcFrameEnd = SDL_SwapLE32(srcOffsets[frame + 1]); - frameSize = srcFrameEnd - srcFrameBegin; + if (info.isFloor()) { + uint8_t out[1024]; + uint8_t *outIt = out; + const uint8_t *src = &dungeonCels[srcFrameBegin]; + const TileType newType = info.isFloorLeft() ? TileType::LeftTriangle : TileType::RightTriangle; + ReencodeFloorWithFoliage(outIt, src, newType); + frameSize = outIt - out; + } else { + const uint32_t srcFrameEnd = SDL_SwapLE32(srcOffsets[frame + 1]); + frameSize = srcFrameEnd - srcFrameBegin; + } } break; case TileType::Square: { frameSize = DunFrameWidth * DunFrameHeight; @@ -137,11 +240,12 @@ size_t GetReencodedSize(const uint8_t *dungeonCels, std::span &dungeonCels, std::span> frames) +void ReencodeDungeonCels(std::unique_ptr &dungeonCels, std::span> frames) { const auto *srcData = reinterpret_cast(dungeonCels.get()); const auto *srcOffsets = reinterpret_cast(srcData); + int numFoliage = 0; LogVerbose("Re-encoding dungeon CELs: {} frames, {} bytes", FormatInteger(SDL_SwapLE32(srcOffsets[0])), FormatInteger(SDL_SwapLE32(srcOffsets[SDL_SwapLE32(srcOffsets[0]) + 1]))); @@ -149,18 +253,24 @@ void ReencodeDungeonCels(std::unique_ptr &dungeonCels, std::span result { new std::byte[outSize] }; auto *const resultPtr = reinterpret_cast(result.get()); - WriteLE32(resultPtr, frames.size()); + WriteLE32(resultPtr, static_cast(frames.size())); uint8_t *out = resultPtr + (2 + frames.size()) * 4; // number of frames, frame offsets, file size - for (const auto &[frame, type] : frames) { - WriteLE32(&resultPtr[static_cast(frame * 4)], out - resultPtr); + for (const auto &[frame, info] : frames) { + WriteLE32(&resultPtr[static_cast(frame * 4)], static_cast(out - resultPtr)); const uint32_t srcFrameBegin = SDL_SwapLE32(srcOffsets[frame]); const uint8_t *src = &srcData[srcFrameBegin]; - switch (type) { + switch (info.type) { case TileType::TransparentSquare: { - const uint32_t srcFrameEnd = SDL_SwapLE32(srcOffsets[frame + 1]); - const uint32_t size = srcFrameEnd - srcFrameBegin; - std::memcpy(out, src, size); - out += size; + if (info.isFloor()) { + const TileType newType = info.isFloorLeft() ? TileType::LeftTriangle : TileType::RightTriangle; + ReencodeFloorWithFoliage(out, src, newType); + ++numFoliage; + } else { + const uint32_t srcFrameEnd = SDL_SwapLE32(srcOffsets[frame + 1]); + const uint32_t size = srcFrameEnd - srcFrameBegin; + std::memcpy(out, src, size); + out += size; + } } break; case TileType::Square: std::memcpy(out, src, DunFrameWidth * DunFrameHeight); @@ -180,12 +290,13 @@ void ReencodeDungeonCels(std::unique_ptr &dungeonCels, std::span(outSize)); const auto *dstOffsets = reinterpret_cast(resultPtr); - LogVerbose(" Re-encoded dungeon CELs: {} frames, {} bytes", + LogVerbose(" Re-encoded dungeon CELs: {} frames, {} bytes. Extracted {} foliage tiles.", FormatInteger(SDL_SwapLE32(dstOffsets[0])), - FormatInteger(SDL_SwapLE32(dstOffsets[SDL_SwapLE32(dstOffsets[0]) + 1]))); + FormatInteger(SDL_SwapLE32(dstOffsets[SDL_SwapLE32(dstOffsets[0]) + 1])), + FormatInteger(numFoliage)); dungeonCels = std::move(result); } diff --git a/Source/levels/reencode_dun_cels.hpp b/Source/levels/reencode_dun_cels.hpp index dae08fd0194..977f1dd83eb 100644 --- a/Source/levels/reencode_dun_cels.hpp +++ b/Source/levels/reencode_dun_cels.hpp @@ -10,11 +10,29 @@ namespace devilution { +struct DunFrameInfo { + // Only floor tiles have this. + uint8_t microTileIndex; + + TileType type; + TileProperties properties; + + [[nodiscard]] bool isFloor() const + { + return !HasAnyOf(properties, TileProperties::Solid) + && (microTileIndex == 0 || microTileIndex == 1); + } + [[nodiscard]] bool isFloorLeft() const { return microTileIndex == 0; } +}; + /** - * @brief Re-encodes dungeon cels, removing redundant padding bytes from triangles and trapezoids. + * @brief Re-encodes dungeon cels. + * + * 1. Removing redundant padding bytes from triangles and trapezoids. + * 2. Extracts floor tile foliage into a triangle with the floor frame and a separate 16-px tall `TransparentSquare`. * * This reduces memory usage and simplifies the rendering. */ -void ReencodeDungeonCels(std::unique_ptr &dungeonCels, std::span> frames); +void ReencodeDungeonCels(std::unique_ptr &dungeonCels, std::span> frames); } // namespace devilution