276 lines
9.3 KiB
C++
276 lines
9.3 KiB
C++
// =============================================
|
|
// Aster: light_manager.cpp
|
|
// Copyright (c) 2020-2024 Anish Bhobe
|
|
// =============================================
|
|
|
|
#include "light_manager.h"
|
|
|
|
#include "buffer.h"
|
|
#include "glm/ext/matrix_transform.hpp"
|
|
|
|
struct Light
|
|
{
|
|
union {
|
|
vec3 um_Position;
|
|
vec3 um_Direction;
|
|
};
|
|
f32 m_Range; // < 0.0 for invalid
|
|
u32 m_Color_; // LSB is used for flags. (R G B Flags)
|
|
f32 m_Intensity;
|
|
|
|
constexpr static u32 MAX_GEN = 0x40;
|
|
constexpr static u32 GEN_MASK = MAX_GEN - 1;
|
|
|
|
constexpr static u32 TYPE_MASK = 0xC0;
|
|
constexpr static u32 TYPE_INVALID = 0x0;
|
|
constexpr static u32 TYPE_DIRECTIONAL = 1 << 6;
|
|
constexpr static u32 TYPE_POINT = 2 << 6;
|
|
constexpr static u32 TYPE_SPOT = 3 << 6; // Currently Unused
|
|
|
|
constexpr static u32 COLOR_MASK = ~(GEN_MASK | TYPE_MASK);
|
|
};
|
|
|
|
// Static Checks
|
|
|
|
// Ensure layouts are exact.
|
|
static_assert(offsetof(DirectionalLight, m_Direction) == offsetof(Light, um_Direction));
|
|
static_assert(offsetof(DirectionalLight, m_Color_) == offsetof(Light, m_Color_));
|
|
static_assert(offsetof(DirectionalLight, m_Intensity) == offsetof(Light, m_Intensity));
|
|
static_assert(sizeof(DirectionalLight) <= sizeof(Light));
|
|
|
|
// Ensure layouts are exact.
|
|
static_assert(offsetof(PointLight, m_Position) == offsetof(Light, um_Position));
|
|
static_assert(offsetof(PointLight, m_Range) == offsetof(Light, m_Range));
|
|
static_assert(offsetof(PointLight, m_Color_) == offsetof(Light, m_Color_));
|
|
static_assert(offsetof(PointLight, m_Intensity) == offsetof(Light, m_Intensity));
|
|
static_assert(sizeof(PointLight) <= sizeof(Light));
|
|
|
|
// Ensure bitmask are in the right place.
|
|
static_assert((Light::TYPE_MASK & Light::TYPE_INVALID) == Light::TYPE_INVALID);
|
|
static_assert((Light::TYPE_MASK & Light::TYPE_DIRECTIONAL) == Light::TYPE_DIRECTIONAL);
|
|
static_assert((Light::TYPE_MASK & Light::TYPE_POINT) == Light::TYPE_POINT);
|
|
static_assert((Light::TYPE_MASK & Light::TYPE_SPOT) == Light::TYPE_SPOT);
|
|
static_assert(Light::COLOR_MASK == 0xFFFFFF00);
|
|
|
|
inline u32
|
|
ToColor32(const vec4 &col)
|
|
{
|
|
const u32 r = Cast<u32>(eastl::min(col.r, 1.0f) * 255.99f);
|
|
const u32 g = Cast<u32>(eastl::min(col.g, 1.0f) * 255.99f);
|
|
const u32 b = Cast<u32>(eastl::min(col.b, 1.0f) * 255.99f);
|
|
const u32 a = Cast<u32>(eastl::min(col.a, 1.0f) * 255.99f);
|
|
|
|
return r << 24 | g << 16 | b << 8 | a;
|
|
}
|
|
|
|
inline u32
|
|
ToColor32(const vec3 &col)
|
|
{
|
|
const u32 r = Cast<u32>(eastl::min(col.r, 1.0f) * 255.99f);
|
|
const u32 g = Cast<u32>(eastl::min(col.g, 1.0f) * 255.99f);
|
|
const u32 b = Cast<u32>(eastl::min(col.b, 1.0f) * 255.99f);
|
|
constexpr u32 a = 255;
|
|
|
|
return r << 24 | g << 16 | b << 8 | a;
|
|
}
|
|
|
|
LightManager::LightManager(GpuResourceManager *resourceManager)
|
|
: m_ResourceManager{resourceManager}
|
|
, m_DirectionalLightCount{}
|
|
, m_PointLightCount{}
|
|
, m_MetaInfo{}
|
|
, m_GpuBufferCapacity_{0}
|
|
{
|
|
}
|
|
|
|
LightManager::~LightManager()
|
|
{
|
|
m_ResourceManager->Release(m_MetaInfo.m_LightBuffer);
|
|
}
|
|
|
|
LightManager::LightManager(LightManager &&other) noexcept
|
|
: m_ResourceManager(other.m_ResourceManager)
|
|
, m_Lights(std::move(other.m_Lights))
|
|
, m_DirectionalLightCount(other.m_DirectionalLightCount)
|
|
, m_PointLightCount(other.m_PointLightCount)
|
|
, m_MetaInfo(other.m_MetaInfo)
|
|
, m_GpuBufferCapacity_(other.m_GpuBufferCapacity_)
|
|
{
|
|
other.m_MetaInfo.m_LightBuffer = {};
|
|
}
|
|
|
|
LightManager &
|
|
LightManager::operator=(LightManager &&other) noexcept
|
|
{
|
|
if (this == &other)
|
|
return *this;
|
|
m_ResourceManager = other.m_ResourceManager;
|
|
m_Lights = std::move(other.m_Lights);
|
|
m_DirectionalLightCount = other.m_DirectionalLightCount;
|
|
m_PointLightCount = other.m_PointLightCount;
|
|
m_MetaInfo = other.m_MetaInfo;
|
|
other.m_MetaInfo.m_LightBuffer = {};
|
|
m_GpuBufferCapacity_ = other.m_GpuBufferCapacity_;
|
|
return *this;
|
|
}
|
|
|
|
LightHandle
|
|
LightManager::AddDirectional(const vec3 &direction, const vec3 &color)
|
|
{
|
|
const vec3 normDirection = normalize(direction);
|
|
if (m_DirectionalLightCount < m_MetaInfo.m_DirectionalLightMaxCount)
|
|
{
|
|
u16 index = 0;
|
|
for (auto &light : m_Lights)
|
|
{
|
|
if (light.m_Range < 0)
|
|
{
|
|
const u8 gen = light.m_Color_ & Light::GEN_MASK;
|
|
|
|
light.m_Color_ = (ToColor32(color) & Light::COLOR_MASK) | Light::TYPE_DIRECTIONAL | gen;
|
|
light.m_Range = 1.0f;
|
|
light.um_Direction = normDirection;
|
|
|
|
m_GpuBufferCapacity_ |= UPDATE_REQUIRED_BIT;
|
|
|
|
return {Light::TYPE_DIRECTIONAL, gen, index};
|
|
}
|
|
++index;
|
|
assert(index < m_MetaInfo.m_DirectionalLightMaxCount); // Gap not found. But must exist
|
|
}
|
|
}
|
|
|
|
// In case we will end up intersecting, we move point lights away. (2 at a time)
|
|
if (m_DirectionalLightCount == m_MetaInfo.m_DirectionalLightMaxCount &&
|
|
m_MetaInfo.m_DirectionalLightMaxCount == m_MetaInfo.m_PointLightOffset)
|
|
{
|
|
const u16 oldPointLightOffset = m_MetaInfo.m_PointLightOffset;
|
|
const u32 pointLightMaxCount = m_MetaInfo.m_PointLightMaxCount;
|
|
// Might cause a capacity increase, but I want to use that for my gpu buffer resize.
|
|
m_Lights.push_back();
|
|
m_Lights.push_back();
|
|
|
|
if (m_MetaInfo.m_PointLightMaxCount > 0) // Edge Case: nullptr at size 0
|
|
{
|
|
Light *oldPointStart = m_Lights.data() + oldPointLightOffset;
|
|
Light *newPointStart = oldPointStart + 2;
|
|
|
|
static_assert(std::is_trivially_copyable_v<Light>);
|
|
memcpy(newPointStart, oldPointStart, pointLightMaxCount * sizeof *newPointStart);
|
|
}
|
|
|
|
m_MetaInfo.m_PointLightOffset += 2;
|
|
}
|
|
|
|
constexpr u8 gen = 0; // New light
|
|
m_Lights[m_DirectionalLightCount].m_Color_ = (ToColor32(color) & Light::COLOR_MASK) | Light::TYPE_DIRECTIONAL | gen;
|
|
m_Lights[m_DirectionalLightCount].m_Range = 1.0f;
|
|
m_Lights[m_DirectionalLightCount].um_Direction = normDirection;
|
|
const u16 index = m_DirectionalLightCount;
|
|
|
|
++m_DirectionalLightCount;
|
|
++m_MetaInfo.m_DirectionalLightMaxCount;
|
|
|
|
return {Light::TYPE_DIRECTIONAL, gen, index};
|
|
}
|
|
|
|
LightHandle
|
|
LightManager::AddPoint(const vec3 &position, const vec3 &color, const f32 radius)
|
|
{
|
|
assert(m_PointLightCount <= m_MetaInfo.m_PointLightMaxCount);
|
|
assert(radius >= 0.0f);
|
|
if (m_PointLightCount < m_MetaInfo.m_PointLightMaxCount)
|
|
{
|
|
Light *light = m_Lights.data() + m_MetaInfo.m_PointLightOffset;
|
|
for (u32 index = 0; index < m_MetaInfo.m_PointLightMaxCount; ++index)
|
|
{
|
|
if (light->m_Range < 0)
|
|
{
|
|
const u8 gen = light->m_Color_ & Light::GEN_MASK;
|
|
|
|
light->m_Color_ = (ToColor32(color) & Light::COLOR_MASK) | Light::TYPE_POINT | gen;
|
|
light->m_Range = radius;
|
|
light->um_Position = position;
|
|
|
|
m_GpuBufferCapacity_ |= UPDATE_REQUIRED_BIT;
|
|
|
|
return {Light::TYPE_POINT, gen, Cast<u16>(index)};
|
|
}
|
|
++light;
|
|
}
|
|
assert(false); // gap must exists.
|
|
return {};
|
|
}
|
|
|
|
m_Lights.push_back();
|
|
const u16 index = m_PointLightCount;
|
|
|
|
Light *light = &m_Lights[index + m_MetaInfo.m_PointLightOffset];
|
|
constexpr u8 gen = 0; // New light
|
|
|
|
light->m_Color_ = (ToColor32(color) & Light::COLOR_MASK) | Light::TYPE_POINT | gen;
|
|
light->m_Range = radius;
|
|
light->um_Position = position;
|
|
|
|
++m_PointLightCount;
|
|
++m_MetaInfo.m_PointLightMaxCount;
|
|
|
|
m_GpuBufferCapacity_ |= UPDATE_REQUIRED_BIT;
|
|
|
|
return {Light::TYPE_POINT, gen, index};
|
|
}
|
|
|
|
void
|
|
LightManager::Update()
|
|
{
|
|
const u16 requiredBufferCapacity = eastl::min(Cast<u16>(m_Lights.capacity()), MAX_LIGHTS);
|
|
if ((m_GpuBufferCapacity_ & CAPACITY_MASK) < requiredBufferCapacity)
|
|
{
|
|
StorageBuffer newBuffer;
|
|
newBuffer.Init(m_ResourceManager->m_Device, requiredBufferCapacity * sizeof m_Lights[0], true, "Light Buffer");
|
|
m_GpuBufferCapacity_ = requiredBufferCapacity | UPDATE_REQUIRED_BIT;
|
|
|
|
m_ResourceManager->Release(m_MetaInfo.m_LightBuffer);
|
|
m_MetaInfo.m_LightBuffer = m_ResourceManager->Commit(&newBuffer);
|
|
}
|
|
if (m_GpuBufferCapacity_ & UPDATE_REQUIRED_BIT)
|
|
{
|
|
m_ResourceManager->Write(m_MetaInfo.m_LightBuffer, 0, m_Lights.size() * sizeof m_Lights[0], m_Lights.data());
|
|
}
|
|
}
|
|
|
|
void
|
|
LightManager::RemoveLight(const LightHandle handle)
|
|
{
|
|
const u8 handleGen = handle.m_Generation;
|
|
|
|
if (handle.m_Type == Light::TYPE_DIRECTIONAL)
|
|
{
|
|
Light *lightSlot = &m_Lights[handle.m_Index];
|
|
const u8 slotGen = lightSlot->m_Color_ & Light::GEN_MASK;
|
|
if (slotGen > handleGen)
|
|
{
|
|
WARN("Invalid handle gen: {} being freed. (slot gen: {})", handleGen, slotGen);
|
|
return;
|
|
}
|
|
|
|
lightSlot->m_Range = -1.0f;
|
|
lightSlot->m_Color_ = Light::TYPE_INVALID | (slotGen + 1) % Light::MAX_GEN;
|
|
--m_DirectionalLightCount;
|
|
}
|
|
|
|
if (handle.m_Type == Light::TYPE_POINT)
|
|
{
|
|
Light *lightSlot = &m_Lights[handle.m_Index + m_MetaInfo.m_PointLightOffset];
|
|
const u8 slotGen = lightSlot->m_Color_ & Light::GEN_MASK;
|
|
if (slotGen > handleGen)
|
|
{
|
|
WARN("Invalid handle gen: {} being freed. (slot gen: {})", handleGen, slotGen);
|
|
return;
|
|
}
|
|
|
|
lightSlot->m_Range = -1.0f;
|
|
lightSlot->m_Color_ = Light::TYPE_INVALID | (slotGen + 1) % Light::MAX_GEN;
|
|
--m_PointLightCount;
|
|
}
|
|
} |