From 0edfa3de662137261dbdce4103ede3233e66b97c Mon Sep 17 00:00:00 2001 From: Gleb Mazovetskiy Date: Thu, 6 May 2021 20:48:46 +0100 Subject: [PATCH] =?UTF-8?q?=F0=9F=9A=9A=20Move=20CEL=20rendering=20to=20en?= =?UTF-8?q?gine/render/cel=5Frender.cpp?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CMakeLists.txt | 1 + Source/control.cpp | 3 +- Source/doom.cpp | 1 + Source/engine.cpp | 486 +--------------------------- Source/engine.h | 141 -------- Source/engine/render/cel_render.cpp | 475 +++++++++++++++++++++++++++ Source/engine/render/cel_render.hpp | 155 +++++++++ Source/engine/render/common_impl.h | 39 +++ Source/error.cpp | 1 + Source/gmenu.cpp | 1 + Source/interfac.cpp | 3 +- Source/inv.cpp | 1 + Source/items.cpp | 1 + Source/minitext.cpp | 1 + Source/quests.cpp | 1 + Source/scrollrt.cpp | 1 + Source/stores.cpp | 1 + 17 files changed, 684 insertions(+), 628 deletions(-) create mode 100644 Source/engine/render/cel_render.cpp create mode 100644 Source/engine/render/cel_render.hpp create mode 100644 Source/engine/render/common_impl.h diff --git a/CMakeLists.txt b/CMakeLists.txt index e9d1eac80..2c43c9a57 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -351,6 +351,7 @@ set(devilutionx_SRCS Source/controls/keymapper.cpp Source/engine/animationinfo.cpp Source/engine/render/automap_render.cpp + Source/engine/render/cel_render.cpp Source/engine/render/dun_render.cpp Source/qol/autopickup.cpp Source/qol/common.cpp diff --git a/Source/control.cpp b/Source/control.cpp index 270bddd0b..e9fefbfef 100644 --- a/Source/control.cpp +++ b/Source/control.cpp @@ -10,7 +10,9 @@ #include "DiabloUI/diabloui.h" #include "automap.h" +#include "controls/keymapper.hpp" #include "cursor.h" +#include "engine/render/cel_render.hpp" #include "error.h" #include "gamemenu.h" #include "init.h" @@ -23,7 +25,6 @@ #include "towners.h" #include "trigs.h" #include "utils/language.h" -#include "controls/keymapper.hpp" namespace devilution { namespace { diff --git a/Source/doom.cpp b/Source/doom.cpp index 988463d1d..aa47a9798 100644 --- a/Source/doom.cpp +++ b/Source/doom.cpp @@ -7,6 +7,7 @@ #include "control.h" #include "engine.h" +#include "engine/render/cel_render.hpp" #include "utils/stdcompat/optional.hpp" namespace devilution { diff --git a/Source/engine.cpp b/Source/engine.cpp index 476df7dc5..df7d60f2c 100644 --- a/Source/engine.cpp +++ b/Source/engine.cpp @@ -13,14 +13,12 @@ #include +#include "engine/render/common_impl.h" #include "lighting.h" #include "movie.h" #include "options.h" #include "storm/storm.h" -// TODO: temporary, remove. -#include "utils/log.hpp" - namespace devilution { /** Seed value before the most recent call to SetRndSeed() */ @@ -39,195 +37,7 @@ const uint32_t RndInc = 1; const uint32_t RndMult = 0x015A4E35; namespace { - -constexpr bool IsCelTransparent(std::uint8_t control) -{ - constexpr std::uint8_t CelTransparentMin = 0x80; - return control >= CelTransparentMin; -} - -constexpr std::uint8_t GetCelTransparentWidth(std::uint8_t control) -{ - return -static_cast(control); -} - constexpr std::uint8_t MaxCl2Width = 65; - -uint8_t *GetLightTable(char light) -{ - int idx = 4096; - if (light == 2) - idx += 256; // gray colors - if (light >= 4) - idx += (light - 1) << 8; - return &pLightTbl[idx]; -} - -struct ClipX { - std::int_fast16_t left; - std::int_fast16_t right; - std::int_fast16_t width; -}; - -ClipX CalculateClipX(std::int_fast16_t x, std::size_t w, const CelOutputBuffer &out) -{ - ClipX clip; - clip.left = static_cast(x < 0 ? -x : 0); - clip.right = static_cast(static_cast(x + w) > out.w() ? x + w - out.w() : 0); - clip.width = static_cast(w - clip.left - clip.right); - return clip; -} - -const byte *SkipRestOfCelLine(const byte *src, std::int_fast16_t remainingWidth) -{ - while (remainingWidth > 0) { - const auto v = static_cast(*src++); - if (!IsCelTransparent(v)) { - src += v; - remainingWidth -= v; - } else { - remainingWidth += v; - } - } - return src; -} - -/** Renders a CEL with only vertical clipping to the output buffer. */ -template -void RenderCelClipY(const CelOutputBuffer &out, Point position, const byte *src, std::size_t srcSize, std::size_t srcWidth, const RenderLine &renderLine) -{ - const auto *srcEnd = src + srcSize; - - // Skip the bottom clipped lines. - const auto dstHeight = out.h(); - while (position.y >= dstHeight && src != srcEnd) { - src = SkipRestOfCelLine(src, static_cast(srcWidth)); - --position.y; - } - - auto *dst = &out[position]; - const auto *dstBegin = out.begin(); - const auto dstPitch = out.pitch(); - while (src != srcEnd && dst >= dstBegin) { - for (std::size_t remainingWidth = srcWidth; remainingWidth > 0;) { - auto v = static_cast(*src++); - if (!IsCelTransparent(v)) { - renderLine(dst, reinterpret_cast(src), v); - src += v; - } else { - v = GetCelTransparentWidth(v); - } - dst += v; - remainingWidth -= v; - } - dst -= dstPitch + srcWidth; - } -} - -/** Renders a CEL with both horizontal and vertical clipping to the output buffer. */ -template -void RenderCelClipXY(const CelOutputBuffer &out, Point position, const byte *src, std::size_t srcSize, std::size_t srcWidth, ClipX clipX, const RenderLine &renderLine) { - const auto *srcEnd = src + srcSize; - - // Skip the bottom clipped lines. - const auto dstHeight = out.h(); - while (position.y >= dstHeight && src != srcEnd) { - src = SkipRestOfCelLine(src, static_cast(srcWidth)); - --position.y; - } - - position.x += static_cast(clipX.left); - - auto *dst = &out[position]; - const auto *dstBegin = out.begin(); - const auto dstPitch = out.pitch(); - while (src < srcEnd && dst >= dstBegin) { - // Skip initial src if clipping on the left. - // Handles overshoot, i.e. when the RLE segment goes into the unclipped area. - auto remainingWidth = clipX.width; - auto remainingLeftClip = clipX.left; - while (remainingLeftClip > 0) { - auto v = static_cast(*src++); - if (!IsCelTransparent(v)) { - if (v > remainingLeftClip) { - const auto overshoot = v - remainingLeftClip; - renderLine(dst, reinterpret_cast(src + remainingLeftClip), overshoot); - dst += overshoot; - remainingWidth -= overshoot; - } - src += v; - } else { - v = GetCelTransparentWidth(v); - if (v > remainingLeftClip) { - const auto overshoot = v - remainingLeftClip; - dst += overshoot; - remainingWidth -= overshoot; - } - } - remainingLeftClip -= v; - } - - // Draw the non-clipped segment - while (remainingWidth > 0) { - auto v = static_cast(*src++); - if (!IsCelTransparent(v)) { - if (v > remainingWidth) { - renderLine(dst, reinterpret_cast(src), remainingWidth); - src += v; - dst += remainingWidth; - remainingWidth -= v; - break; - } - renderLine(dst, reinterpret_cast(src), v); - src += v; - } else { - v = GetCelTransparentWidth(v); - if (v > remainingWidth) { - dst += remainingWidth; - remainingWidth -= v; - break; - } - } - dst += v; - remainingWidth -= v; - } - - // Skip the rest of src line if clipping on the right - assert(remainingWidth <= 0); - src = SkipRestOfCelLine(src, clipX.right + remainingWidth); - - dst -= dstPitch + clipX.width; - } - -} - -template -void RenderCel(const CelOutputBuffer &out, Point position, const byte *src, std::size_t srcSize, std::size_t srcWidth, const RenderLine &renderLine) -{ - const ClipX clipX = CalculateClipX(position.x, srcWidth, out); - if (clipX.width <= 0) - return; - if (static_cast(clipX.width) == srcWidth) { - RenderCelClipY(out, position, src, srcSize, srcWidth, renderLine); - } else { - RenderCelClipXY(out, position, src, srcSize, srcWidth, clipX, renderLine); - } -} - -void RenderCelWithLightTable(const CelOutputBuffer &out, Point position, const byte *src, std::size_t srcSize, std::size_t srcWidth, const std::uint8_t *tbl) -{ - RenderCel(out, position, src, srcSize, srcWidth, [tbl](std::uint8_t *dst, const std::uint8_t *src, std::size_t w) { - while (w-- > 0) { - *dst++ = tbl[static_cast(*src)]; - ++src; - } - }); -} - -constexpr auto RenderLineMemcpy = [](std::uint8_t *dst, const std::uint8_t *src, std::size_t w) { - std::memcpy(dst, src, w); -}; - } // namespace CelSprite LoadCel(const char *pszName, int width) @@ -240,300 +50,6 @@ CelSprite LoadCel(const char *pszName, const int *widths) return CelSprite(LoadFileInMem(pszName), widths); } -std::pair MeasureSolidHorizontalBounds(const CelSprite &cel, int frame) -{ - int nDataSize; - auto src = reinterpret_cast(CelGetFrame(cel.Data(), frame, &nDataSize)); - auto end = &src[nDataSize]; - const int celWidth = cel.Width(frame); - - int xBegin = celWidth; - int xEnd = 0; - - int transparentRun = 0; - int xCur = 0; - bool firstTransparentRun = true; - while (src < end) { - std::int_fast16_t remainingWidth = celWidth; - while (remainingWidth > 0) { - const auto val = static_cast(*src++); - if (IsCelTransparent(val)) { - const int width = GetCelTransparentWidth(val); - transparentRun += width; - xCur += width; - remainingWidth -= width; - if (remainingWidth == 0) { - xEnd = std::max(xEnd, celWidth - transparentRun); - xCur = 0; - firstTransparentRun = true; - transparentRun = 0; - } - } else { - if (firstTransparentRun) { - xBegin = std::min(xBegin, transparentRun); - firstTransparentRun = false; - if (xBegin == 0 && xEnd == celWidth) - break; - } - transparentRun = 0; - xCur += val; - src += val; - remainingWidth -= val; - if (remainingWidth == 0) { - xEnd = celWidth; - if (xBegin == 0) - break; - xCur = 0; - firstTransparentRun = true; - } - } - } - } - return { xBegin, xEnd }; -} - -void CelDrawTo(const CelOutputBuffer &out, int sx, int sy, const CelSprite &cel, int frame) -{ - int nDataSize; - const auto *pRLEBytes = CelGetFrame(cel.Data(), frame, &nDataSize); - CelBlitSafeTo(out, sx, sy, pRLEBytes, nDataSize, cel.Width(frame)); -} - -void CelClippedDrawTo(const CelOutputBuffer &out, int sx, int sy, const CelSprite &cel, int frame) -{ - int nDataSize; - const auto *pRLEBytes = CelGetFrameClipped(cel.Data(), frame, &nDataSize); - - CelBlitSafeTo(out, sx, sy, pRLEBytes, nDataSize, cel.Width(frame)); -} - -void CelDrawLightTo(const CelOutputBuffer &out, int sx, int sy, const CelSprite &cel, int frame, uint8_t *tbl) -{ - int nDataSize; - const auto *pRLEBytes = CelGetFrame(cel.Data(), frame, &nDataSize); - - if (light_table_index != 0 || tbl != nullptr) - CelBlitLightSafeTo(out, sx, sy, pRLEBytes, nDataSize, cel.Width(frame), tbl); - else - CelBlitSafeTo(out, sx, sy, pRLEBytes, nDataSize, cel.Width(frame)); -} - -void CelClippedDrawLightTo(const CelOutputBuffer &out, int sx, int sy, const CelSprite &cel, int frame) -{ - int nDataSize; - const auto *pRLEBytes = CelGetFrameClipped(cel.Data(), frame, &nDataSize); - - if (light_table_index != 0) - CelBlitLightSafeTo(out, sx, sy, pRLEBytes, nDataSize, cel.Width(frame), nullptr); - else - CelBlitSafeTo(out, sx, sy, pRLEBytes, nDataSize, cel.Width(frame)); -} - -void CelDrawLightRedTo(const CelOutputBuffer &out, int sx, int sy, const CelSprite &cel, int frame, char light) -{ - int nDataSize; - const auto *pRLEBytes = CelGetFrameClipped(cel.Data(), frame, &nDataSize); - const std::uint8_t *tbl = GetLightTable(light); - RenderCelWithLightTable(out, { sx, sy }, pRLEBytes, nDataSize, cel.Width(frame), tbl); -} - -void CelBlitSafeTo(const CelOutputBuffer &out, int sx, int sy, const byte *pRLEBytes, int nDataSize, int nWidth) -{ - assert(pRLEBytes != nullptr); - RenderCel(out, { sx, sy }, pRLEBytes, nDataSize, nWidth, RenderLineMemcpy); -} - -void CelClippedDrawSafeTo(const CelOutputBuffer &out, int sx, int sy, const CelSprite &cel, int frame) -{ - int nDataSize; - const auto *pRLEBytes = CelGetFrameClipped(cel.Data(), frame, &nDataSize); - CelBlitSafeTo(out, sx, sy, pRLEBytes, nDataSize, cel.Width(frame)); -} - -void CelBlitLightSafeTo(const CelOutputBuffer &out, int sx, int sy, const byte *pRLEBytes, int nDataSize, int nWidth, uint8_t *tbl) -{ - assert(pRLEBytes != nullptr); - if (tbl == nullptr) - tbl = &pLightTbl[light_table_index * 256]; - RenderCelWithLightTable(out, { sx, sy }, pRLEBytes, nDataSize, nWidth, tbl); -} - -void CelBlitLightTransSafeTo(const CelOutputBuffer &out, int sx, int sy, const byte *pRLEBytes, int nDataSize, int nWidth) -{ - assert(pRLEBytes != nullptr); - - const auto *src = pRLEBytes; - BYTE *dst = out.at(sx, sy); - const uint8_t *tbl = &pLightTbl[light_table_index * 256]; - bool shift = (reinterpret_cast(dst) % 2) != 0; - - for (; src != &pRLEBytes[nDataSize]; dst -= out.pitch() + nWidth, shift = !shift) { - for (int w = nWidth; w > 0;) { - auto width = static_cast(*src++); - if (!IsCelTransparent(width)) { - w -= width; - if (dst < out.end() && dst > out.begin()) { - if (((size_t)dst % 2) == shift) { - if ((width & 1) == 0) { - goto L_ODD; - } else { - src++; - dst++; - L_EVEN: - width /= 2; - if ((width & 1) != 0) { - dst[0] = tbl[static_cast(src[0])]; - src += 2; - dst += 2; - } - width /= 2; - for (; width > 0; width--) { - dst[0] = tbl[static_cast(src[0])]; - dst[2] = tbl[static_cast(src[2])]; - src += 4; - dst += 4; - } - } - } else { - if ((width & 1) == 0) { - goto L_EVEN; - } else { - dst[0] = tbl[static_cast(src[0])]; - src++; - dst++; - L_ODD: - width /= 2; - if ((width & 1) != 0) { - dst[1] = tbl[static_cast(src[1])]; - src += 2; - dst += 2; - } - width /= 2; - for (; width > 0; width--) { - dst[1] = tbl[static_cast(src[1])]; - dst[3] = tbl[static_cast(src[3])]; - src += 4; - dst += 4; - } - } - } - } else { - src += width; - dst += width; - } - } else { - width = -static_cast(width); - dst += width; - w -= width; - } - } - } -} - -/** - * @brief Same as CelBlitLightSafe, with blended transparancy applied - * @param out The output buffer - * @param pRLEBytes CEL pixel stream (run-length encoded) - * @param nDataSize Size of CEL in bytes - * @param nWidth Width of sprite - * @param tbl Palette translation table - */ -static void CelBlitLightBlendedSafeTo(const CelOutputBuffer &out, int sx, int sy, const byte *pRLEBytes, int nDataSize, int nWidth, uint8_t *tbl) -{ - assert(pRLEBytes != nullptr); - if (tbl == nullptr) - tbl = &pLightTbl[light_table_index * 256]; - - RenderCel(out, { sx, sy }, pRLEBytes, nDataSize, nWidth, [tbl](std::uint8_t *dst, const uint8_t *src, std::size_t w) { - while (w-- > 0) { - *dst = paletteTransparencyLookup[*dst][tbl[*src++]]; - ++dst; - } - }); -} - -void CelClippedBlitLightTransTo(const CelOutputBuffer &out, int sx, int sy, const CelSprite &cel, int frame) -{ - int nDataSize; - const byte *pRLEBytes = CelGetFrameClipped(cel.Data(), frame, &nDataSize); - - if (cel_transparency_active) { - if (sgOptions.Graphics.bBlendedTransparancy) - CelBlitLightBlendedSafeTo(out, sx, sy, pRLEBytes, nDataSize, cel.Width(frame), nullptr); - else - CelBlitLightTransSafeTo(out, sx, sy, pRLEBytes, nDataSize, cel.Width(frame)); - } else if (light_table_index != 0) - CelBlitLightSafeTo(out, sx, sy, pRLEBytes, nDataSize, cel.Width(frame), nullptr); - else - CelBlitSafeTo(out, sx, sy, pRLEBytes, nDataSize, cel.Width(frame)); -} - -void CelDrawLightRedSafeTo(const CelOutputBuffer &out, int sx, int sy, const CelSprite &cel, int frame, char light) -{ - int nDataSize; - const auto *pRLEBytes = CelGetFrameClipped(cel.Data(), frame, &nDataSize); - RenderCelWithLightTable(out, { sx, sy }, pRLEBytes, nDataSize, cel.Width(frame), GetLightTable(light)); -} - -void CelDrawUnsafeTo(const CelOutputBuffer &out, int x, int y, const CelSprite &cel, int frame) -{ - int nDataSize; - const auto *pRLEBytes = CelGetFrame(cel.Data(), frame, &nDataSize); - RenderCelClipY(out, { x, y }, pRLEBytes, nDataSize, cel.Width(frame), RenderLineMemcpy); -} - -void CelBlitOutlineTo(const CelOutputBuffer &out, uint8_t col, int sx, int sy, const CelSprite &cel, int frame, bool skipColorIndexZero) -{ - int nDataSize; - - const byte *src = CelGetFrameClipped(cel.Data(), frame, &nDataSize); - const auto *end = &src[nDataSize]; - uint8_t *dst = out.at(sx, sy); - const int celWidth = static_cast(cel.Width(frame)); - - for (; src != end; dst -= out.pitch() + celWidth) { - for (int w = celWidth; w > 0;) { - auto width = static_cast(*src++); - if (!IsCelTransparent(width)) { - w -= width; - if (dst < out.end() && dst > out.begin()) { - if (dst >= out.end() - out.pitch()) { - while (width > 0) { - if (!skipColorIndexZero || static_cast(*src) > 0) { - dst[-out.pitch()] = col; - dst[-1] = col; - dst[1] = col; - } - src++; - dst++; - width--; - } - } else { - while (width > 0) { - if (!skipColorIndexZero || static_cast(*src) > 0) { - dst[-out.pitch()] = col; - dst[-1] = col; - dst[1] = col; - dst[out.pitch()] = col; - } - src++; - dst++; - width--; - } - } - } else { - src += width; - dst += width; - } - } else { - width = GetCelTransparentWidth(width); - dst += width; - w -= width; - } - } - } -} - void DrawHorizontalLine(const CelOutputBuffer &out, Point from, int width, std::uint8_t colorIndex) { if (from.y < 0 || from.y >= out.h() || from.x >= out.w() || width <= 0 || from.x + width <= 0) diff --git a/Source/engine.h b/Source/engine.h index 56fdc9ad0..2e700d443 100644 --- a/Source/engine.h +++ b/Source/engine.h @@ -396,147 +396,6 @@ private: CelSprite LoadCel(const char *pszName, int width); CelSprite LoadCel(const char *pszName, const int *widths); -/** - * Returns a pair of X coordinates containing the start (inclusive) and end (exclusive) - * of fully transparent columns in the sprite. - */ -std::pair MeasureSolidHorizontalBounds(const CelSprite &cel, int frame = 1); - -/** - * @brief Blit CEL sprite to the back buffer at the given coordinates - * @param out Target buffer - * @param sx Target buffer coordinate - * @param sy Target buffer coordinate - * @param cel CEL sprite - * @param frame CEL frame number - */ -void CelDrawTo(const CelOutputBuffer &out, int sx, int sy, const CelSprite &cel, int frame); - -/** - * @briefBlit CEL sprite to the given buffer, does not perform bounds-checking. - * @param out Target buffer - * @param x Cordinate in the target buffer - * @param y Cordinate in the target buffer - * @param cel CEL sprite - * @param frame CEL frame number - */ -void CelDrawUnsafeTo(const CelOutputBuffer &out, int x, int y, const CelSprite &cel, int frame); - -/** - * @brief Same as CelDrawTo but with the option to skip parts of the top and bottom of the sprite - * @param out Target buffer - * @param sx Target buffer coordinate - * @param sy Target buffer coordinate - * @param cel CEL sprite - * @param frame CEL frame number - */ -void CelClippedDrawTo(const CelOutputBuffer &out, int sx, int sy, const CelSprite &cel, int frame); - -/** - * @brief Blit CEL sprite, and apply lighting, to the back buffer at the given coordinates - * @param out Target buffer - * @param sx Target buffer coordinate - * @param sy Target buffer coordinate - * @param cel CEL sprite - * @param frame CEL frame number - */ -void CelDrawLightTo(const CelOutputBuffer &out, int sx, int sy, const CelSprite &cel, int frame, uint8_t *tbl); - -/** - * @brief Same as CelDrawLightTo but with the option to skip parts of the top and bottom of the sprite - * @param out Target buffer - * @param sx Target buffer coordinate - * @param sy Target buffer coordinate - * @param cel CEL sprite - * @param frame CEL frame number - */ -void CelClippedDrawLightTo(const CelOutputBuffer &out, int sx, int sy, const CelSprite &cel, int frame); - -/** - * @brief Same as CelBlitLightTransSafeTo - * @param out Target buffer - * @param sx Target buffer coordinate - * @param sy Target buffer coordinate - * @param cel CEL sprite - * @param frame CEL frame number - */ -void CelClippedBlitLightTransTo(const CelOutputBuffer &out, int sx, int sy, const CelSprite &cel, int frame); - -/** - * @brief Blit CEL sprite, and apply lighting, to the back buffer at the given coordinates, translated to a red hue - * @param out Target buffer - * @param sx Target buffer coordinate - * @param sy Target buffer coordinate - * @param cel CEL sprite - * @param frame CEL frame number - * @param light Light shade to use - */ -void CelDrawLightRedTo(const CelOutputBuffer &out, int sx, int sy, const CelSprite &cel, int frame, char light); - -/** - * @brief Blit CEL sprite to the given buffer, checks for drawing outside the buffer. - * @param out Target buffer - * @param sx Target buffer coordinate - * @param sy Target buffer coordinate - * @param pRLEBytes CEL pixel stream (run-length encoded) - * @param nDataSize Size of CEL in bytes - */ -void CelBlitSafeTo(const CelOutputBuffer &out, int sx, int sy, const byte *pRLEBytes, int nDataSize, int nWidth); - -/** - * @brief Same as CelClippedDrawTo but checks for drawing outside the buffer - * @param out Target buffer - * @param sx Target buffer coordinate - * @param sy Target buffer coordinate - * @param cel CEL sprite - * @param frame CEL frame number - */ -void CelClippedDrawSafeTo(const CelOutputBuffer &out, int sx, int sy, const CelSprite &cel, int frame); - -/** - * @brief Blit CEL sprite, and apply lighting, to the given buffer, checks for drawing outside the buffer - * @param out Target buffer - * @param sx Target buffer coordinate - * @param sy Target buffer coordinate - * @param pRLEBytes CEL pixel stream (run-length encoded) - * @param nDataSize Size of CEL in bytes - * @param tbl Palette translation table - */ -void CelBlitLightSafeTo(const CelOutputBuffer &out, int sx, int sy, const byte *pRLEBytes, int nDataSize, int nWidth, uint8_t *tbl); - -/** - * @brief Same as CelBlitLightSafeTo but with stippled transparancy applied - * @param out Target buffer - * @param sx Target buffer coordinate - * @param sy Target buffer coordinate - * @param pRLEBytes CEL pixel stream (run-length encoded) - * @param nDataSize Size of CEL in bytes - */ -void CelBlitLightTransSafeTo(const CelOutputBuffer &out, int sx, int sy, const byte *pRLEBytes, int nDataSize, int nWidth); - -/** - * @brief Same as CelDrawLightRedTo but checks for drawing outside the buffer - * @param out Target buffer - * @param sx Target buffer coordinate - * @param sy Target buffer coordinate - * @param cel CEL sprite - * @param frame CEL frame number - * @param light Light shade to use - */ -void CelDrawLightRedSafeTo(const CelOutputBuffer &out, int sx, int sy, const CelSprite &cel, int frame, char light); - -/** - * @brief Blit a solid colder shape one pixel larger then the given sprite shape, to the target buffer at the given coordianates - * @param out Target buffer - * @param col Color index from current palette - * @param sx Target buffer coordinate - * @param sy Target buffer coordinate - * @param pCelBuff CEL buffer - * @param frame CEL frame number - * @param skipColorIndexZero If true, color in index 0 will be treated as transparent (these are typically used for shadows in sprites) - */ -void CelBlitOutlineTo(const CelOutputBuffer &out, uint8_t col, int sx, int sy, const CelSprite &cel, int frame, bool skipColorIndexZero = true); - /** * @brief Set the value of a single pixel in the back buffer, checks bounds * @param out Target buffer diff --git a/Source/engine/render/cel_render.cpp b/Source/engine/render/cel_render.cpp new file mode 100644 index 000000000..67594fdf9 --- /dev/null +++ b/Source/engine/render/cel_render.cpp @@ -0,0 +1,475 @@ +#include "engine/render/cel_render.hpp" + +#include +#include +#include + +#include "engine/render/common_impl.h" +#include "options.h" +#include "palette.h" +#include "scrollrt.h" + +namespace devilution { + +namespace { + +constexpr bool IsCelTransparent(std::uint8_t control) +{ + constexpr std::uint8_t CelTransparentMin = 0x80; + return control >= CelTransparentMin; +} + +constexpr std::uint8_t GetCelTransparentWidth(std::uint8_t control) +{ + return -static_cast(control); +} + +const byte *SkipRestOfCelLine(const byte *src, std::int_fast16_t remainingWidth) +{ + while (remainingWidth > 0) { + const auto v = static_cast(*src++); + if (!IsCelTransparent(v)) { + src += v; + remainingWidth -= v; + } else { + remainingWidth += v; + } + } + return src; +} + +/** Renders a CEL with only vertical clipping to the output buffer. */ +template +void RenderCelClipY(const CelOutputBuffer &out, Point position, const byte *src, std::size_t srcSize, std::size_t srcWidth, const RenderLine &renderLine) +{ + const auto *srcEnd = src + srcSize; + + // Skip the bottom clipped lines. + const auto dstHeight = out.h(); + while (position.y >= dstHeight && src != srcEnd) { + src = SkipRestOfCelLine(src, static_cast(srcWidth)); + --position.y; + } + + auto *dst = &out[position]; + const auto *dstBegin = out.begin(); + const auto dstPitch = out.pitch(); + while (src != srcEnd && dst >= dstBegin) { + for (std::size_t remainingWidth = srcWidth; remainingWidth > 0;) { + auto v = static_cast(*src++); + if (!IsCelTransparent(v)) { + renderLine(dst, reinterpret_cast(src), v); + src += v; + } else { + v = GetCelTransparentWidth(v); + } + dst += v; + remainingWidth -= v; + } + dst -= dstPitch + srcWidth; + } +} + +/** Renders a CEL with both horizontal and vertical clipping to the output buffer. */ +template +void RenderCelClipXY(const CelOutputBuffer &out, Point position, const byte *src, std::size_t srcSize, std::size_t srcWidth, ClipX clipX, const RenderLine &renderLine) +{ + const auto *srcEnd = src + srcSize; + + // Skip the bottom clipped lines. + const auto dstHeight = out.h(); + while (position.y >= dstHeight && src != srcEnd) { + src = SkipRestOfCelLine(src, static_cast(srcWidth)); + --position.y; + } + + position.x += static_cast(clipX.left); + + auto *dst = &out[position]; + const auto *dstBegin = out.begin(); + const auto dstPitch = out.pitch(); + while (src < srcEnd && dst >= dstBegin) { + // Skip initial src if clipping on the left. + // Handles overshoot, i.e. when the RLE segment goes into the unclipped area. + auto remainingWidth = clipX.width; + auto remainingLeftClip = clipX.left; + while (remainingLeftClip > 0) { + auto v = static_cast(*src++); + if (!IsCelTransparent(v)) { + if (v > remainingLeftClip) { + const auto overshoot = v - remainingLeftClip; + renderLine(dst, reinterpret_cast(src + remainingLeftClip), overshoot); + dst += overshoot; + remainingWidth -= overshoot; + } + src += v; + } else { + v = GetCelTransparentWidth(v); + if (v > remainingLeftClip) { + const auto overshoot = v - remainingLeftClip; + dst += overshoot; + remainingWidth -= overshoot; + } + } + remainingLeftClip -= v; + } + + // Draw the non-clipped segment + while (remainingWidth > 0) { + auto v = static_cast(*src++); + if (!IsCelTransparent(v)) { + if (v > remainingWidth) { + renderLine(dst, reinterpret_cast(src), remainingWidth); + src += v; + dst += remainingWidth; + remainingWidth -= v; + break; + } + renderLine(dst, reinterpret_cast(src), v); + src += v; + } else { + v = GetCelTransparentWidth(v); + if (v > remainingWidth) { + dst += remainingWidth; + remainingWidth -= v; + break; + } + } + dst += v; + remainingWidth -= v; + } + + // Skip the rest of src line if clipping on the right + assert(remainingWidth <= 0); + src = SkipRestOfCelLine(src, clipX.right + remainingWidth); + + dst -= dstPitch + clipX.width; + } +} + +template +void RenderCel(const CelOutputBuffer &out, Point position, const byte *src, std::size_t srcSize, std::size_t srcWidth, const RenderLine &renderLine) +{ + const ClipX clipX = CalculateClipX(position.x, srcWidth, out); + if (clipX.width <= 0) + return; + if (static_cast(clipX.width) == srcWidth) { + RenderCelClipY(out, position, src, srcSize, srcWidth, renderLine); + } else { + RenderCelClipXY(out, position, src, srcSize, srcWidth, clipX, renderLine); + } +} + +void RenderCelWithLightTable(const CelOutputBuffer &out, Point position, const byte *src, std::size_t srcSize, std::size_t srcWidth, const std::uint8_t *tbl) +{ + RenderCel(out, position, src, srcSize, srcWidth, [tbl](std::uint8_t *dst, const std::uint8_t *src, std::size_t w) { + while (w-- > 0) { + *dst++ = tbl[static_cast(*src)]; + ++src; + } + }); +} + +constexpr auto RenderLineMemcpy = [](std::uint8_t *dst, const std::uint8_t *src, std::size_t w) { + std::memcpy(dst, src, w); +}; + +} // namespace + +void CelDrawTo(const CelOutputBuffer &out, int sx, int sy, const CelSprite &cel, int frame) +{ + int nDataSize; + const auto *pRLEBytes = CelGetFrame(cel.Data(), frame, &nDataSize); + CelBlitSafeTo(out, sx, sy, pRLEBytes, nDataSize, cel.Width(frame)); +} + +void CelClippedDrawTo(const CelOutputBuffer &out, int sx, int sy, const CelSprite &cel, int frame) +{ + int nDataSize; + const auto *pRLEBytes = CelGetFrameClipped(cel.Data(), frame, &nDataSize); + + CelBlitSafeTo(out, sx, sy, pRLEBytes, nDataSize, cel.Width(frame)); +} + +void CelDrawLightTo(const CelOutputBuffer &out, int sx, int sy, const CelSprite &cel, int frame, uint8_t *tbl) +{ + int nDataSize; + const auto *pRLEBytes = CelGetFrame(cel.Data(), frame, &nDataSize); + + if (light_table_index != 0 || tbl != nullptr) + CelBlitLightSafeTo(out, sx, sy, pRLEBytes, nDataSize, cel.Width(frame), tbl); + else + CelBlitSafeTo(out, sx, sy, pRLEBytes, nDataSize, cel.Width(frame)); +} + +void CelClippedDrawLightTo(const CelOutputBuffer &out, int sx, int sy, const CelSprite &cel, int frame) +{ + int nDataSize; + const auto *pRLEBytes = CelGetFrameClipped(cel.Data(), frame, &nDataSize); + + if (light_table_index != 0) + CelBlitLightSafeTo(out, sx, sy, pRLEBytes, nDataSize, cel.Width(frame), nullptr); + else + CelBlitSafeTo(out, sx, sy, pRLEBytes, nDataSize, cel.Width(frame)); +} + +void CelDrawLightRedTo(const CelOutputBuffer &out, int sx, int sy, const CelSprite &cel, int frame, char light) +{ + int nDataSize; + const auto *pRLEBytes = CelGetFrameClipped(cel.Data(), frame, &nDataSize); + const std::uint8_t *tbl = GetLightTable(light); + RenderCelWithLightTable(out, { sx, sy }, pRLEBytes, nDataSize, cel.Width(frame), tbl); +} + +void CelBlitSafeTo(const CelOutputBuffer &out, int sx, int sy, const byte *pRLEBytes, int nDataSize, int nWidth) +{ + assert(pRLEBytes != nullptr); + RenderCel(out, { sx, sy }, pRLEBytes, nDataSize, nWidth, RenderLineMemcpy); +} + +void CelClippedDrawSafeTo(const CelOutputBuffer &out, int sx, int sy, const CelSprite &cel, int frame) +{ + int nDataSize; + const auto *pRLEBytes = CelGetFrameClipped(cel.Data(), frame, &nDataSize); + CelBlitSafeTo(out, sx, sy, pRLEBytes, nDataSize, cel.Width(frame)); +} + +void CelBlitLightSafeTo(const CelOutputBuffer &out, int sx, int sy, const byte *pRLEBytes, int nDataSize, int nWidth, uint8_t *tbl) +{ + assert(pRLEBytes != nullptr); + if (tbl == nullptr) + tbl = &pLightTbl[light_table_index * 256]; + RenderCelWithLightTable(out, { sx, sy }, pRLEBytes, nDataSize, nWidth, tbl); +} + +void CelBlitLightTransSafeTo(const CelOutputBuffer &out, int sx, int sy, const byte *pRLEBytes, int nDataSize, int nWidth) +{ + assert(pRLEBytes != nullptr); + + const auto *src = pRLEBytes; + BYTE *dst = out.at(sx, sy); + const uint8_t *tbl = &pLightTbl[light_table_index * 256]; + bool shift = (reinterpret_cast(dst) % 2) != 0; + + for (; src != &pRLEBytes[nDataSize]; dst -= out.pitch() + nWidth, shift = !shift) { + for (int w = nWidth; w > 0;) { + auto width = static_cast(*src++); + if (!IsCelTransparent(width)) { + w -= width; + if (dst < out.end() && dst > out.begin()) { + if (((size_t)dst % 2) == shift) { + if ((width & 1) == 0) { + goto L_ODD; + } else { + src++; + dst++; + L_EVEN: + width /= 2; + if ((width & 1) != 0) { + dst[0] = tbl[static_cast(src[0])]; + src += 2; + dst += 2; + } + width /= 2; + for (; width > 0; width--) { + dst[0] = tbl[static_cast(src[0])]; + dst[2] = tbl[static_cast(src[2])]; + src += 4; + dst += 4; + } + } + } else { + if ((width & 1) == 0) { + goto L_EVEN; + } else { + dst[0] = tbl[static_cast(src[0])]; + src++; + dst++; + L_ODD: + width /= 2; + if ((width & 1) != 0) { + dst[1] = tbl[static_cast(src[1])]; + src += 2; + dst += 2; + } + width /= 2; + for (; width > 0; width--) { + dst[1] = tbl[static_cast(src[1])]; + dst[3] = tbl[static_cast(src[3])]; + src += 4; + dst += 4; + } + } + } + } else { + src += width; + dst += width; + } + } else { + width = -static_cast(width); + dst += width; + w -= width; + } + } + } +} + +/** + * @brief Same as CelBlitLightSafe, with blended transparancy applied + * @param out The output buffer + * @param pRLEBytes CEL pixel stream (run-length encoded) + * @param nDataSize Size of CEL in bytes + * @param nWidth Width of sprite + * @param tbl Palette translation table + */ +static void CelBlitLightBlendedSafeTo(const CelOutputBuffer &out, int sx, int sy, const byte *pRLEBytes, int nDataSize, int nWidth, uint8_t *tbl) +{ + assert(pRLEBytes != nullptr); + if (tbl == nullptr) + tbl = &pLightTbl[light_table_index * 256]; + + RenderCel(out, { sx, sy }, pRLEBytes, nDataSize, nWidth, [tbl](std::uint8_t *dst, const uint8_t *src, std::size_t w) { + while (w-- > 0) { + *dst = paletteTransparencyLookup[*dst][tbl[*src++]]; + ++dst; + } + }); +} + +void CelClippedBlitLightTransTo(const CelOutputBuffer &out, int sx, int sy, const CelSprite &cel, int frame) +{ + int nDataSize; + const byte *pRLEBytes = CelGetFrameClipped(cel.Data(), frame, &nDataSize); + + if (cel_transparency_active) { + if (sgOptions.Graphics.bBlendedTransparancy) + CelBlitLightBlendedSafeTo(out, sx, sy, pRLEBytes, nDataSize, cel.Width(frame), nullptr); + else + CelBlitLightTransSafeTo(out, sx, sy, pRLEBytes, nDataSize, cel.Width(frame)); + } else if (light_table_index != 0) + CelBlitLightSafeTo(out, sx, sy, pRLEBytes, nDataSize, cel.Width(frame), nullptr); + else + CelBlitSafeTo(out, sx, sy, pRLEBytes, nDataSize, cel.Width(frame)); +} + +void CelDrawLightRedSafeTo(const CelOutputBuffer &out, int sx, int sy, const CelSprite &cel, int frame, char light) +{ + int nDataSize; + const auto *pRLEBytes = CelGetFrameClipped(cel.Data(), frame, &nDataSize); + RenderCelWithLightTable(out, { sx, sy }, pRLEBytes, nDataSize, cel.Width(frame), GetLightTable(light)); +} + +void CelDrawUnsafeTo(const CelOutputBuffer &out, int x, int y, const CelSprite &cel, int frame) +{ + int nDataSize; + const auto *pRLEBytes = CelGetFrame(cel.Data(), frame, &nDataSize); + RenderCelClipY(out, { x, y }, pRLEBytes, nDataSize, cel.Width(frame), RenderLineMemcpy); +} + +void CelBlitOutlineTo(const CelOutputBuffer &out, uint8_t col, int sx, int sy, const CelSprite &cel, int frame, bool skipColorIndexZero) +{ + int nDataSize; + + const byte *src = CelGetFrameClipped(cel.Data(), frame, &nDataSize); + const auto *end = &src[nDataSize]; + uint8_t *dst = out.at(sx, sy); + const int celWidth = static_cast(cel.Width(frame)); + + for (; src != end; dst -= out.pitch() + celWidth) { + for (int w = celWidth; w > 0;) { + auto width = static_cast(*src++); + if (!IsCelTransparent(width)) { + w -= width; + if (dst < out.end() && dst > out.begin()) { + if (dst >= out.end() - out.pitch()) { + while (width > 0) { + if (!skipColorIndexZero || static_cast(*src) > 0) { + dst[-out.pitch()] = col; + dst[-1] = col; + dst[1] = col; + } + src++; + dst++; + width--; + } + } else { + while (width > 0) { + if (!skipColorIndexZero || static_cast(*src) > 0) { + dst[-out.pitch()] = col; + dst[-1] = col; + dst[1] = col; + dst[out.pitch()] = col; + } + src++; + dst++; + width--; + } + } + } else { + src += width; + dst += width; + } + } else { + width = GetCelTransparentWidth(width); + dst += width; + w -= width; + } + } + } +} + +std::pair MeasureSolidHorizontalBounds(const CelSprite &cel, int frame) +{ + int nDataSize; + auto src = reinterpret_cast(CelGetFrame(cel.Data(), frame, &nDataSize)); + auto end = &src[nDataSize]; + const int celWidth = cel.Width(frame); + + int xBegin = celWidth; + int xEnd = 0; + + int transparentRun = 0; + int xCur = 0; + bool firstTransparentRun = true; + while (src < end) { + std::int_fast16_t remainingWidth = celWidth; + while (remainingWidth > 0) { + const auto val = static_cast(*src++); + if (IsCelTransparent(val)) { + const int width = GetCelTransparentWidth(val); + transparentRun += width; + xCur += width; + remainingWidth -= width; + if (remainingWidth == 0) { + xEnd = std::max(xEnd, celWidth - transparentRun); + xCur = 0; + firstTransparentRun = true; + transparentRun = 0; + } + } else { + if (firstTransparentRun) { + xBegin = std::min(xBegin, transparentRun); + firstTransparentRun = false; + if (xBegin == 0 && xEnd == celWidth) { + return { xBegin, xEnd }; + } + } + transparentRun = 0; + xCur += val; + src += val; + remainingWidth -= val; + if (remainingWidth == 0) { + xEnd = celWidth; + if (xBegin == 0) { + return { xBegin, xEnd }; + } + xCur = 0; + firstTransparentRun = true; + } + } + } + } + return { xBegin, xEnd }; +} + +} // namespace devilution diff --git a/Source/engine/render/cel_render.hpp b/Source/engine/render/cel_render.hpp new file mode 100644 index 000000000..39c5a41af --- /dev/null +++ b/Source/engine/render/cel_render.hpp @@ -0,0 +1,155 @@ +/** + * @file cel_render.hpp + * + * CEL rendering. + */ +#pragma once + +#include + +#include "engine.h" + +namespace devilution { + +/** + * Returns a pair of X coordinates containing the start (inclusive) and end (exclusive) + * of fully transparent columns in the sprite. + */ +std::pair MeasureSolidHorizontalBounds(const CelSprite &cel, int frame = 1); + +/** + * @brief Blit CEL sprite to the back buffer at the given coordinates + * @param out Target buffer + * @param sx Target buffer coordinate + * @param sy Target buffer coordinate + * @param cel CEL sprite + * @param frame CEL frame number + */ +void CelDrawTo(const CelOutputBuffer &out, int sx, int sy, const CelSprite &cel, int frame); + +/** + * @briefBlit CEL sprite to the given buffer, does not perform bounds-checking. + * @param out Target buffer + * @param x Cordinate in the target buffer + * @param y Cordinate in the target buffer + * @param cel CEL sprite + * @param frame CEL frame number + */ +void CelDrawUnsafeTo(const CelOutputBuffer &out, int x, int y, const CelSprite &cel, int frame); + +/** + * @brief Same as CelDrawTo but with the option to skip parts of the top and bottom of the sprite + * @param out Target buffer + * @param sx Target buffer coordinate + * @param sy Target buffer coordinate + * @param cel CEL sprite + * @param frame CEL frame number + */ +void CelClippedDrawTo(const CelOutputBuffer &out, int sx, int sy, const CelSprite &cel, int frame); + +/** + * @brief Blit CEL sprite, and apply lighting, to the back buffer at the given coordinates + * @param out Target buffer + * @param sx Target buffer coordinate + * @param sy Target buffer coordinate + * @param cel CEL sprite + * @param frame CEL frame number + */ +void CelDrawLightTo(const CelOutputBuffer &out, int sx, int sy, const CelSprite &cel, int frame, uint8_t *tbl); + +/** + * @brief Same as CelDrawLightTo but with the option to skip parts of the top and bottom of the sprite + * @param out Target buffer + * @param sx Target buffer coordinate + * @param sy Target buffer coordinate + * @param cel CEL sprite + * @param frame CEL frame number + */ +void CelClippedDrawLightTo(const CelOutputBuffer &out, int sx, int sy, const CelSprite &cel, int frame); + +/** + * @brief Same as CelBlitLightTransSafeTo + * @param out Target buffer + * @param sx Target buffer coordinate + * @param sy Target buffer coordinate + * @param cel CEL sprite + * @param frame CEL frame number + */ +void CelClippedBlitLightTransTo(const CelOutputBuffer &out, int sx, int sy, const CelSprite &cel, int frame); + +/** + * @brief Blit CEL sprite, and apply lighting, to the back buffer at the given coordinates, translated to a red hue + * @param out Target buffer + * @param sx Target buffer coordinate + * @param sy Target buffer coordinate + * @param cel CEL sprite + * @param frame CEL frame number + * @param light Light shade to use + */ +void CelDrawLightRedTo(const CelOutputBuffer &out, int sx, int sy, const CelSprite &cel, int frame, char light); + +/** + * @brief Blit CEL sprite to the given buffer, checks for drawing outside the buffer. + * @param out Target buffer + * @param sx Target buffer coordinate + * @param sy Target buffer coordinate + * @param pRLEBytes CEL pixel stream (run-length encoded) + * @param nDataSize Size of CEL in bytes + */ +void CelBlitSafeTo(const CelOutputBuffer &out, int sx, int sy, const byte *pRLEBytes, int nDataSize, int nWidth); + +/** + * @brief Same as CelClippedDrawTo but checks for drawing outside the buffer + * @param out Target buffer + * @param sx Target buffer coordinate + * @param sy Target buffer coordinate + * @param cel CEL sprite + * @param frame CEL frame number + */ +void CelClippedDrawSafeTo(const CelOutputBuffer &out, int sx, int sy, const CelSprite &cel, int frame); + +/** + * @brief Blit CEL sprite, and apply lighting, to the given buffer, checks for drawing outside the buffer + * @param out Target buffer + * @param sx Target buffer coordinate + * @param sy Target buffer coordinate + * @param pRLEBytes CEL pixel stream (run-length encoded) + * @param nDataSize Size of CEL in bytes + * @param tbl Palette translation table + */ +void CelBlitLightSafeTo(const CelOutputBuffer &out, int sx, int sy, const byte *pRLEBytes, int nDataSize, int nWidth, uint8_t *tbl); + +/** + * @brief Same as CelBlitLightSafeTo but with stippled transparancy applied + * @param out Target buffer + * @param sx Target buffer coordinate + * @param sy Target buffer coordinate + * @param pRLEBytes CEL pixel stream (run-length encoded) + * @param nDataSize Size of CEL in bytes + */ +void CelBlitLightTransSafeTo(const CelOutputBuffer &out, int sx, int sy, const byte *pRLEBytes, int nDataSize, int nWidth); + +/** + * @brief Same as CelDrawLightRedTo but checks for drawing outside the buffer + * @param out Target buffer + * @param sx Target buffer coordinate + * @param sy Target buffer coordinate + * @param cel CEL sprite + * @param frame CEL frame number + * @param light Light shade to use + */ +void CelDrawLightRedSafeTo(const CelOutputBuffer &out, int sx, int sy, const CelSprite &cel, int frame, char light); + +/** + * @brief Blit a solid colder shape one pixel larger then the given sprite shape, to the target buffer at the given coordianates + * @param out Target buffer + * @param col Color index from current palette + * @param sx Target buffer coordinate + * @param sy Target buffer coordinate + * @param pCelBuff CEL buffer + * @param frame CEL frame number + * @param skipColorIndexZero If true, color in index 0 will be treated as transparent (these are typically used for shadows in sprites) + */ +void CelBlitOutlineTo(const CelOutputBuffer &out, uint8_t col, int sx, int sy, const CelSprite &cel, int frame, bool skipColorIndexZero = true); + +} // namespace devilution diff --git a/Source/engine/render/common_impl.h b/Source/engine/render/common_impl.h new file mode 100644 index 000000000..6bdab2e92 --- /dev/null +++ b/Source/engine/render/common_impl.h @@ -0,0 +1,39 @@ +/** + * Common code for implementing various renderers. + */ +#pragma once + +#include +#include + +#include "engine.h" +#include "lighting.h" + +namespace devilution { + +inline std::uint8_t *GetLightTable(char light) +{ + int idx = 4096; + if (light == 2) + idx += 256; // gray colors + if (light >= 4) + idx += (light - 1) << 8; + return &pLightTbl[idx]; +} + +struct ClipX { + std::int_fast16_t left; + std::int_fast16_t right; + std::int_fast16_t width; +}; + +inline ClipX CalculateClipX(std::int_fast16_t x, std::size_t w, const CelOutputBuffer &out) +{ + ClipX clip; + clip.left = static_cast(x < 0 ? -x : 0); + clip.right = static_cast(static_cast(x + w) > out.w() ? x + w - out.w() : 0); + clip.width = static_cast(w - clip.left - clip.right); + return clip; +} + +} // namespace devilution diff --git a/Source/error.cpp b/Source/error.cpp index c7c1e646f..34cca8af8 100644 --- a/Source/error.cpp +++ b/Source/error.cpp @@ -6,6 +6,7 @@ #include "error.h" #include "control.h" +#include "engine/render/cel_render.hpp" #include "stores.h" #include "utils/language.h" diff --git a/Source/gmenu.cpp b/Source/gmenu.cpp index 5a899dd4e..760616a26 100644 --- a/Source/gmenu.cpp +++ b/Source/gmenu.cpp @@ -9,6 +9,7 @@ #include "controls/axis_direction.h" #include "controls/controller_motion.h" #include "engine.h" +#include "engine/render/cel_render.hpp" #include "stores.h" #include "utils/language.h" #include "utils/stdcompat/optional.hpp" diff --git a/Source/interfac.cpp b/Source/interfac.cpp index 0fe758ec4..c583df483 100644 --- a/Source/interfac.cpp +++ b/Source/interfac.cpp @@ -6,10 +6,11 @@ #include -#include "control.h" #include "DiabloUI/art_draw.h" +#include "control.h" #include "dx.h" #include "engine.h" +#include "engine/render/cel_render.hpp" #include "init.h" #include "loadsave.h" #include "palette.h" diff --git a/Source/inv.cpp b/Source/inv.cpp index 7aedb864f..69f6143d5 100644 --- a/Source/inv.cpp +++ b/Source/inv.cpp @@ -6,6 +6,7 @@ #include #include "cursor.h" +#include "engine/render/cel_render.hpp" #include "minitext.h" #include "options.h" #include "plrmsg.h" diff --git a/Source/items.cpp b/Source/items.cpp index e3fc1b743..244e13794 100644 --- a/Source/items.cpp +++ b/Source/items.cpp @@ -10,6 +10,7 @@ #include "cursor.h" #include "doom.h" #include "dx.h" +#include "engine/render/cel_render.hpp" #include "init.h" #include "lighting.h" #include "missiles.h" diff --git a/Source/minitext.cpp b/Source/minitext.cpp index 3802a6870..e301ab17b 100644 --- a/Source/minitext.cpp +++ b/Source/minitext.cpp @@ -7,6 +7,7 @@ #include "control.h" #include "dx.h" #include "engine.h" +#include "engine/render/cel_render.hpp" #include "utils/language.h" #include "utils/stdcompat/optional.hpp" diff --git a/Source/quests.cpp b/Source/quests.cpp index 1d5fbdc6f..7fe561fc0 100644 --- a/Source/quests.cpp +++ b/Source/quests.cpp @@ -6,6 +6,7 @@ #include "control.h" #include "cursor.h" +#include "engine/render/cel_render.hpp" #include "gendung.h" #include "init.h" #include "minitext.h" diff --git a/Source/scrollrt.cpp b/Source/scrollrt.cpp index 36aa7479b..f2e9dfccd 100644 --- a/Source/scrollrt.cpp +++ b/Source/scrollrt.cpp @@ -10,6 +10,7 @@ #include "dead.h" #include "doom.h" #include "dx.h" +#include "engine/render/cel_render.hpp" #include "engine/render/dun_render.hpp" #include "error.h" #include "gmenu.h" diff --git a/Source/stores.cpp b/Source/stores.cpp index d04bc2e10..8e7c12862 100644 --- a/Source/stores.cpp +++ b/Source/stores.cpp @@ -8,6 +8,7 @@ #include #include "cursor.h" +#include "engine/render/cel_render.hpp" #include "init.h" #include "minitext.h" #include "options.h"