Skip to content

Commit

Permalink
Reworked light system
Browse files Browse the repository at this point in the history
  • Loading branch information
nerudaj committed Dec 28, 2023
1 parent d80d1f9 commit ad7e509
Show file tree
Hide file tree
Showing 6 changed files with 163 additions and 61 deletions.
4 changes: 2 additions & 2 deletions src/lib-editor/include/Utilities/Literals.hpp
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
#pragma once

[[nodiscard]] consteval bool operator""_true(const char*, std::size_t) noexcept
[[nodiscard]] consteval bool operator""_True(const char*, std::size_t) noexcept
{
return true;
}

[[nodiscard]] consteval bool operator""_false(const char*, std::size_t) noexcept
[[nodiscard]] consteval bool operator""_False(const char*, std::size_t) noexcept
{
return false;
}
Expand Down
4 changes: 2 additions & 2 deletions src/lib-editor/src/Dialogs/DialogBase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -116,14 +116,14 @@ void DialogInterface::open(std::function<void()> confirmCallback)
{
addLabel(input.base.label, row);
addEditBox(
input.base.value, input.base.id, row, "narrow"_true);
input.base.value, input.base.id, row, "narrow"_True);
addHelperButton(input.buttonCallback, row);
},
[&](const OptionDeferredInputWithButton& input)
{
addLabel(input.base.label, row);
addEditBox(
input.base.value(), input.base.id, row, "narrow"_true);
input.base.value(), input.base.id, row, "narrow"_True);
addHelperButton(input.buttonCallback, row);
},
[&](const OptionCheckbox& input)
Expand Down
24 changes: 17 additions & 7 deletions src/lib-game/include/builder/LightmapBuilder.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@

#include <DGM/classes/Objects.hpp>
#include <LevelD.hpp>
#include <LevelTheme.hpp>
#include <SFML/System/Vector2.hpp>
#include <core/Types.hpp>
#include <core/Scene.hpp>
#include <queue>
#include <type_traits>

Expand All @@ -12,24 +13,33 @@ struct LightPoint
MeshItrType x, y;
LightType lightLevel;
LightType decayAmount;
// drop this light source if bottom tile is blocking
bool skipOnBottomSolid;
};

class LightmapBuilder final
{
public:
static LightmapType buildLightmap(
const dgm::Mesh& bottom, const dgm::Mesh& upper, const LevelD& level);
static LightmapType
buildLightmap(const LevelD& level, SkyboxTheme skyboxTheme);

private:
static std::queue<LightPoint>
getTileBasedLightSources(const dgm::Mesh& bottom, const dgm::Mesh& upper);
static std::queue<LightPoint> getTileBasedLightSources(
const LevelD::TileLayer& layer,
const sf::Vector2u layerSize,
SkyboxTheme skybox,
bool skipOnBottomSolid = false);

static std::queue<LightPoint> getItemBasedLightSources(
const LevelD& level, const sf::Vector2u& voxelSize);

static void processLightPointQueue(
std::queue<LightPoint> queue,
std::vector<LightType>& data,
const dgm::Mesh& bottom,
const dgm::Mesh& upper);
const sf::Vector2u& dataSize,
const std::vector<bool>& blockingTiles);

static void mergeLightmaps(
const std::vector<LightType>& source,
std::vector<LightType>& destination);
};
14 changes: 13 additions & 1 deletion src/lib-game/include/utils/SemanticTypes.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,4 +43,16 @@ operator""_damage(unsigned long long value) noexcept
operator""_seconds(unsigned long long value) noexcept
{
return static_cast<float>(value);
}
}

[[nodiscard]] consteval static bool
operator""_true(const char*, std::size_t) noexcept
{
return true;
}

[[nodiscard]] consteval static bool
operator""_false(const char*, std::size_t) noexcept
{
return false;
}
175 changes: 127 additions & 48 deletions src/lib-game/src/builder/LightmapBuilder.cpp
Original file line number Diff line number Diff line change
@@ -1,58 +1,139 @@
#include <builder/LightmapBuilder.hpp>
#include <core/Enums.hpp>
#include <utils/SemanticTypes.hpp>

constexpr LightType NATURAL_OUTDOOR_LIGHT_LEVEL = 255;
constexpr LightType DIMMED_NATURAL_OUTDOOR_LIGHT_LEVEL = 128;
constexpr LightType NIGHT_NATURAL_OUTDOOR_LIGHT_LEVEL = 32;
constexpr LightType ARTIFICIAL_LIGHT_LEVEL = 192;
constexpr LightType VERTICAL_LIGHT_DECAY_AMOUNT =
64; // anything that comes from tiles
constexpr LightType HORIZONTAL_LIGHT_DECAY_AMOUNT =
32; // anything that comes from items

LightmapType LightmapBuilder::buildLightmap(
const dgm::Mesh& bottom, const dgm::Mesh& upper, const LevelD& level)
LightmapType
LightmapBuilder::buildLightmap(const LevelD& level, SkyboxTheme skyboxTheme)
{
const auto& voxelSize = upper.getVoxelSize();
const auto dataSize = bottom.getDataSize().x * bottom.getDataSize().y;
auto data = std::vector<LightType>(dataSize, 0);
const auto& voxelSize =
sf::Vector2u(level.mesh.tileWidth, level.mesh.tileHeight);
const auto dataSize =
sf::Vector2u(level.mesh.layerWidth, level.mesh.layerHeight);
auto data = std::vector<LightType>(dataSize.x * dataSize.y, 0);

processLightPointQueue(
getTileBasedLightSources(bottom, upper), data, bottom, upper);
getTileBasedLightSources(
level.mesh.layers.at(0),
dataSize,
skyboxTheme,
"skipOnBottomSolid"_true),
data,
dataSize,
level.mesh.layers.at(0).blocks);
processLightPointQueue(
getItemBasedLightSources(level, voxelSize), data, bottom, upper);
getItemBasedLightSources(level, voxelSize),
data,
dataSize,
level.mesh.layers.at(0).blocks);

return LightmapType(data, bottom.getDataSize(), voxelSize);
processLightPointQueue(
getTileBasedLightSources(
level.mesh.layers.at(1), dataSize, skyboxTheme),
data,
dataSize,
level.mesh.layers.at(1).blocks);

return LightmapType(data, dataSize, voxelSize);
}

[[nodiscard]] static constexpr bool isNaturalLightSource(LevelTileId id)
{
using enum LevelTileId;
return id == CeilSky || id == WallWindow;
}

[[nodiscard]] static constexpr bool isArtificialLightSource(LevelTileId id)
{
using enum LevelTileId;
return id == FlatLight || id == WallLight;
}

[[nodiscard]] static constexpr bool isLightSource(LevelTileId id)
{
return isNaturalLightSource(id) || isArtificialLightSource(id);
}

[[nodiscard]] static constexpr LightType
getLightIntensity(LevelTileId tile, SkyboxTheme skybox)
{
using enum SkyboxTheme;

if (isNaturalLightSource(tile))
{
switch (skybox)
{
case Countryside:
return NATURAL_OUTDOOR_LIGHT_LEVEL;
case Dawntime:
return DIMMED_NATURAL_OUTDOOR_LIGHT_LEVEL;
case Nightsky:
return NIGHT_NATURAL_OUTDOOR_LIGHT_LEVEL;
}
}
else
{
return ARTIFICIAL_LIGHT_LEVEL;
}
}

std::queue<LightPoint> LightmapBuilder::getTileBasedLightSources(
const dgm::Mesh& bottom, const dgm::Mesh& upper)
const LevelD::TileLayer& layer,
const sf::Vector2u layerSize,
SkyboxTheme skybox,
bool skipOnBottomSolid)
{
std::queue<LightPoint> queue;

// Set base levels
for (MeshItrType y = 0, i = 0; y < bottom.getDataSize().y; ++y)
for (MeshItrType y = 0, i = 0; y < layerSize.y; ++y)
{
for (MeshItrType x = 0; x < bottom.getDataSize().y; ++x, ++i)
for (MeshItrType x = 0; x < layerSize.x; ++x, ++i)
{
auto upperTileType = static_cast<LevelTileId>(upper.at(x, y));
auto bottomTileType = static_cast<LevelTileId>(bottom.at(x, y));
const bool isBlocking = layer.blocks.at(i);
const auto tile = static_cast<LevelTileId>(layer.tiles.at(i));

if (upperTileType == LevelTileId::CeilSky)
{
if (!isLightSource(tile)) continue;

const auto lightAmount = getLightIntensity(tile, skybox);

if (isBlocking)
{ // wall source
queue.emplace(
x - 1,
y - 1,
lightAmount,
HORIZONTAL_LIGHT_DECAY_AMOUNT,
skipOnBottomSolid);
queue.emplace(
x + 1,
y - 1,
lightAmount,
HORIZONTAL_LIGHT_DECAY_AMOUNT,
skipOnBottomSolid);
queue.emplace(
LightPoint { .x = x,
.y = y,
.lightLevel = NATURAL_OUTDOOR_LIGHT_LEVEL,
.decayAmount = VERTICAL_LIGHT_DECAY_AMOUNT });
x - 1,
y + 1,
lightAmount,
HORIZONTAL_LIGHT_DECAY_AMOUNT,
skipOnBottomSolid);
queue.emplace(
x + 1,
y + 1,
lightAmount,
HORIZONTAL_LIGHT_DECAY_AMOUNT,
skipOnBottomSolid);
}
else if (
bottomTileType == LevelTileId::FlatLight
|| upperTileType == LevelTileId::FlatLight)
else // floor/ceil source
{
queue.emplace(
LightPoint { .x = x,
.y = y,
.lightLevel = ARTIFICIAL_LIGHT_LEVEL,
.decayAmount = VERTICAL_LIGHT_DECAY_AMOUNT });
queue.emplace(x, y, lightAmount, VERTICAL_LIGHT_DECAY_AMOUNT);
}
}
}
Expand All @@ -71,11 +152,12 @@ std::queue<LightPoint> LightmapBuilder::getItemBasedLightSources(
switch (static_cast<LevelItemId>(thing.id))
{
case FloorLamp:
queue.push(
LightPoint { .x = thing.x / voxelSize.x,
.y = thing.y / voxelSize.y,
.lightLevel = ARTIFICIAL_LIGHT_LEVEL,
.decayAmount = HORIZONTAL_LIGHT_DECAY_AMOUNT });
queue.emplace(
thing.x / voxelSize.x,
thing.y / voxelSize.y,
ARTIFICIAL_LIGHT_LEVEL,
HORIZONTAL_LIGHT_DECAY_AMOUNT,
"skipOnBottomSolid"_true);
break;
default:
break;
Expand All @@ -88,31 +170,28 @@ std::queue<LightPoint> LightmapBuilder::getItemBasedLightSources(
void LightmapBuilder::processLightPointQueue(
std::queue<LightPoint> queue,
std::vector<LightType>& data,
const dgm::Mesh& bottom,
const dgm::Mesh& upper)
const sf::Vector2u& dataSize,
const std::vector<bool>& blockingTiles)
{

while (!queue.empty())
{
auto front = queue.front();
queue.pop();

if (front.y >= bottom.getDataSize().y
|| front.x >= bottom.getDataSize().x)
continue;
// Skip out-of-bounds tiles
if (front.y >= dataSize.y || front.x >= dataSize.x) continue;

const auto index = front.y * bottom.getDataSize().x + front.x;
const auto index = front.y * dataSize.x + front.x;
if (blockingTiles.at(index) && front.skipOnBottomSolid) continue;

auto currentLevel = data.at(index);
if ((bottom[index] > 4 && upper[index] > 4)
|| currentLevel > front.lightLevel)
continue;
const auto currentLightLevel = data.at(index);
data.at(index) = std::max(currentLightLevel, front.lightLevel);

data.at(index) = front.lightLevel;
if (front.lightLevel < front.decayAmount) continue;
if (front.lightLevel <= front.decayAmount) continue;

const auto decayedLight =
static_cast<LightType>(front.lightLevel - front.decayAmount);
const LightType decayedLight =
front.lightLevel == 255 ? 224
: front.lightLevel - front.decayAmount;
queue.push(LightPoint { .x = front.x - 1,
.y = front.y,
.lightLevel = decayedLight,
Expand Down
3 changes: 2 additions & 1 deletion src/lib-game/src/builder/SceneBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,8 @@ Scene SceneBuilder::buildScene(const LevelD& level, unsigned maxPlayerCount)
.drawableLevel = { .bottomTextures = bottomTextureMesh,
.upperTextures = upperTextureMesh,
.lightmap = LightmapBuilder::buildLightmap(
bottomTextureMesh, upperTextureMesh, level) },
level,
SkyboxThemeUtils::fromString(theme.skybox)) },
.spatialIndex = createSpatialIndex(level),
.distanceIndex = DistanceIndex(bottomMesh),
.spawns = createSpawns(level),
Expand Down

0 comments on commit ad7e509

Please sign in to comment.