11#include " engine/render/light_render.hpp"
22
3+ #include < cassert>
4+ #include < span>
35#include < vector>
46
57#include " engine/displacement.hpp"
68#include " engine/point.hpp"
79#include " levels/dun_tile.hpp"
10+ #include " levels/gendung.h"
811#include " lighting.h"
912#include " options.h"
1013
@@ -367,7 +370,12 @@ void BuildLightmap(Point tilePosition, Point targetBufferPosition, uint16_t view
367370 if (!*GetOptions ().Graphics .perPixelLighting )
368371 return ;
369372
370- const size_t totalPixels = static_cast <size_t >(viewportWidth) * viewportHeight;
373+ // Since light may need to bleed up to the top of wall tiles,
374+ // expand the buffer space to include the full base diamond of the tallest tile graphics
375+ const uint16_t bufferHeight = viewportHeight + TILE_HEIGHT * (MicroTileLen / 2 + 1 );
376+ rows += MicroTileLen + 2 ;
377+
378+ const size_t totalPixels = static_cast <size_t >(viewportWidth) * bufferHeight;
371379 LightmapBuffer.resize (totalPixels);
372380
373381 // Since rendering occurs in cells between quads,
@@ -404,7 +412,7 @@ void BuildLightmap(Point tilePosition, Point targetBufferPosition, uint16_t view
404412 continue ;
405413 if (lightLevel < minLight)
406414 break ;
407- RenderCell (quad, center0, lightLevel, lightmap, viewportWidth, viewportHeight );
415+ RenderCell (quad, center0, lightLevel, lightmap, viewportWidth, bufferHeight );
408416 }
409417 }
410418
@@ -428,9 +436,13 @@ void BuildLightmap(Point tilePosition, Point targetBufferPosition, uint16_t view
428436
429437} // namespace
430438
431- Lightmap::Lightmap (const uint8_t *outBuffer, const uint8_t *lightmapBuffer, const uint8_t *lightTables, size_t lightTableSize)
439+ Lightmap::Lightmap (const uint8_t *outBuffer, uint16_t outPitch,
440+ std::span<const uint8_t > lightmapBuffer, uint16_t lightmapPitch,
441+ const uint8_t *lightTables, size_t lightTableSize)
432442 : outBuffer(outBuffer)
443+ , outPitch(outPitch)
433444 , lightmapBuffer(lightmapBuffer)
445+ , lightmapPitch(lightmapPitch)
434446 , lightTables(lightTables)
435447 , lightTableSize(lightTableSize)
436448{
@@ -441,7 +453,63 @@ Lightmap Lightmap::build(Point tilePosition, Point targetBufferPosition,
441453 const uint8_t *outBuffer, const uint8_t *lightTables, size_t lightTableSize)
442454{
443455 BuildLightmap (tilePosition, targetBufferPosition, viewportWidth, viewportHeight, rows, columns);
444- return Lightmap (outBuffer, LightmapBuffer.data (), lightTables, lightTableSize);
456+ return Lightmap (outBuffer, LightmapBuffer, gnScreenWidth, lightTables, lightTableSize);
457+ }
458+
459+ Lightmap Lightmap::bleedUp (const Lightmap &source, Point targetBufferPosition, std::span<uint8_t > lightmapBuffer)
460+ {
461+ assert (lightmapBuffer.size () >= TILE_WIDTH * TILE_HEIGHT);
462+
463+ if (!*GetOptions ().Graphics .perPixelLighting )
464+ return source;
465+
466+ const int sourceHeight = static_cast <int >(source.lightmapBuffer .size () / source.lightmapPitch );
467+ const int clipLeft = std::max (0 , -targetBufferPosition.x );
468+ const int clipTop = std::max (0 , -(targetBufferPosition.y - TILE_HEIGHT + 1 ));
469+ const int clipRight = std::max (0 , targetBufferPosition.x + TILE_WIDTH - source.outPitch );
470+ const int clipBottom = std::max (0 , targetBufferPosition.y - sourceHeight + 1 );
471+
472+ // Nothing we can do if the tile is completely outside the bounds of the lightmap
473+ if (clipLeft + clipRight >= TILE_WIDTH)
474+ return source;
475+ if (clipTop + clipBottom >= TILE_HEIGHT)
476+ return source;
477+
478+ const uint16_t lightmapPitch = std::max (0 , TILE_WIDTH - clipLeft - clipRight);
479+ const uint16_t lightmapHeight = TILE_HEIGHT - clipTop - clipBottom;
480+
481+ // Find the left edge of the last row in the tile
482+ const int outOffset = std::max (0 , (targetBufferPosition.y - clipBottom) * source.outPitch + targetBufferPosition.x + clipLeft);
483+ const uint8_t *outLoc = source.outBuffer + outOffset;
484+ const uint8_t *outBuffer = outLoc - (lightmapHeight - 1 ) * source.outPitch ;
485+
486+ // Start copying bytes from the bottom row of the tile
487+ const uint8_t *src = source.getLightingAt (outLoc);
488+ uint8_t *dst = lightmapBuffer.data () + (lightmapHeight - 1 ) * lightmapPitch;
489+
490+ int rowCount = clipBottom;
491+ while (src >= source.lightmapBuffer .data () && dst >= lightmapBuffer.data ()) {
492+ const int bleed = std::max (0 , (rowCount - TILE_HEIGHT / 2 ) * 2 );
493+ const int lightOffset = std::max (bleed, clipLeft) - clipLeft;
494+ const int lightLength = std::max (0 , TILE_WIDTH - clipLeft - std::max (bleed, clipRight) - lightOffset);
495+
496+ // Bleed pixels up by copying data from the row below this one
497+ if (rowCount > clipBottom && lightLength < lightmapPitch)
498+ memcpy (dst, dst + lightmapPitch, lightmapPitch);
499+
500+ // Copy data from the source lightmap between the top edge of the base diamond
501+ assert (dst + lightOffset + lightLength <= lightmapBuffer.data () + TILE_WIDTH * TILE_HEIGHT);
502+ assert (src + lightOffset + lightLength <= LightmapBuffer.data () + LightmapBuffer.size ());
503+ memcpy (dst + lightOffset, src + lightOffset, lightLength);
504+
505+ src -= source.lightmapPitch ;
506+ dst -= lightmapPitch;
507+ rowCount++;
508+ }
509+
510+ return Lightmap (outBuffer, source.outPitch ,
511+ lightmapBuffer, lightmapPitch,
512+ source.lightTables , source.lightTableSize );
445513}
446514
447515} // namespace devilution
0 commit comments