Веб-сайт самохостера Lotigara

summaryrefslogtreecommitdiff
path: root/source/base
diff options
context:
space:
mode:
authorKae <80987908+Novaenia@users.noreply.github.com>2024-03-20 01:53:34 +1100
committerKae <80987908+Novaenia@users.noreply.github.com>2024-03-20 01:53:34 +1100
commit6d76a11e256cd96c9cdd7ae5a10c0276e6347277 (patch)
treed52459889408115d1e0eb657a05dc58e098e50eb /source/base
parent58a346e563df12af9194c198c7ffe974411abb28 (diff)
experiment: unclamped lighting
Diffstat (limited to 'source/base')
-rw-r--r--source/base/CMakeLists.txt1
-rw-r--r--source/base/StarCellularLightArray.cpp130
-rw-r--r--source/base/StarCellularLightArray.hpp72
-rw-r--r--source/base/StarCellularLighting.cpp65
-rw-r--r--source/base/StarCellularLighting.hpp89
5 files changed, 295 insertions, 62 deletions
diff --git a/source/base/CMakeLists.txt b/source/base/CMakeLists.txt
index f16257a..8f4d18b 100644
--- a/source/base/CMakeLists.txt
+++ b/source/base/CMakeLists.txt
@@ -25,6 +25,7 @@ SET (star_base_HEADERS
SET (star_base_SOURCES
StarAnimatedPartSet.cpp
StarAssets.cpp
+ StarCellularLightArray.cpp
StarCellularLighting.cpp
StarConfiguration.cpp
StarDirectoryAssetSource.cpp
diff --git a/source/base/StarCellularLightArray.cpp b/source/base/StarCellularLightArray.cpp
new file mode 100644
index 0000000..60a6d95
--- /dev/null
+++ b/source/base/StarCellularLightArray.cpp
@@ -0,0 +1,130 @@
+#include "StarCellularLightArray.hpp"
+// just specializing these in a cpp file so I can iterate on them without recompiling like 40 files!!
+
+namespace Star {
+
+template <>
+void CellularLightArray<ScalarLightTraits>::calculatePointLighting(size_t xmin, size_t ymin, size_t xmax, size_t ymax) {
+ float perBlockObstacleAttenuation = 1.0f / m_pointMaxObstacle;
+ float perBlockAirAttenuation = 1.0f / m_pointMaxAir;
+
+ for (PointLight light : m_pointLights) {
+ if (light.position[0] < 0 || light.position[0] > m_width - 1 || light.position[1] < 0 || light.position[1] > m_height - 1)
+ continue;
+
+ float maxIntensity = ScalarLightTraits::maxIntensity(light.value);
+ Vec2F beamDirection = Vec2F(1, 0).rotate(light.beamAngle);
+
+ float maxRange = maxIntensity * m_pointMaxAir;
+ // The min / max considering the radius of the light
+ size_t lxmin = std::floor(std::max<float>(xmin, light.position[0] - maxRange));
+ size_t lymin = std::floor(std::max<float>(ymin, light.position[1] - maxRange));
+ size_t lxmax = std::ceil(std::min<float>(xmax, light.position[0] + maxRange));
+ size_t lymax = std::ceil(std::min<float>(ymax, light.position[1] + maxRange));
+
+ for (size_t x = lxmin; x < lxmax; ++x) {
+ for (size_t y = lymin; y < lymax; ++y) {
+ LightValue lvalue = getLight(x, y);
+ // + 0.5f to correct block position to center
+ Vec2F blockPos = Vec2F(x + 0.5f, y + 0.5f);
+
+ Vec2F relativeLightPosition = blockPos - light.position;
+ float distance = relativeLightPosition.magnitude();
+ if (distance == 0.0f) {
+ setLight(x, y, light.value + lvalue);
+ continue;
+ }
+
+ float attenuation = distance * perBlockAirAttenuation;
+ if (attenuation >= 1.0f)
+ continue;
+
+ Vec2F direction = relativeLightPosition / distance;
+ if (light.beam > 0.0f) {
+ attenuation += (1.0f - light.beamAmbience) * clamp(light.beam * (1.0f - direction * beamDirection), 0.0f, 1.0f);
+ if (attenuation >= 1.0f)
+ continue;
+ }
+
+ float remainingAttenuation = maxIntensity - attenuation;
+ if (remainingAttenuation <= 0.0f)
+ continue;
+
+ // Need to circularize manhattan attenuation here
+ float circularizedPerBlockObstacleAttenuation = perBlockObstacleAttenuation / max(fabs(direction[0]), fabs(direction[1]));
+ float blockAttenuation = lineAttenuation(blockPos, light.position, circularizedPerBlockObstacleAttenuation, remainingAttenuation);
+
+ // Apply single obstacle boost (determine single obstacle by one
+ // block unit of attenuation).
+ attenuation += blockAttenuation + min(blockAttenuation, circularizedPerBlockObstacleAttenuation) * m_pointObstacleBoost;
+
+ if (attenuation < 1.0f)
+ setLight(x, y, lvalue + ScalarLightTraits::subtract(light.value, attenuation));
+ }
+ }
+ }
+}
+
+template <>
+void CellularLightArray<ColoredLightTraits>::calculatePointLighting(size_t xmin, size_t ymin, size_t xmax, size_t ymax) {
+ float perBlockObstacleAttenuation = 1.0f / m_pointMaxObstacle;
+ float perBlockAirAttenuation = 1.0f / m_pointMaxAir;
+
+ for (PointLight light : m_pointLights) {
+ if (light.position[0] < 0 || light.position[0] > m_width - 1 || light.position[1] < 0 || light.position[1] > m_height - 1)
+ continue;
+
+ float maxIntensity = ColoredLightTraits::maxIntensity(light.value);
+ Vec2F beamDirection = Vec2F(1, 0).rotate(light.beamAngle);
+
+ float maxRange = maxIntensity * m_pointMaxAir;
+ // The min / max considering the radius of the light
+ size_t lxmin = std::floor(std::max<float>(xmin, light.position[0] - maxRange));
+ size_t lymin = std::floor(std::max<float>(ymin, light.position[1] - maxRange));
+ size_t lxmax = std::ceil(std::min<float>(xmax, light.position[0] + maxRange));
+ size_t lymax = std::ceil(std::min<float>(ymax, light.position[1] + maxRange));
+
+ for (size_t x = lxmin; x < lxmax; ++x) {
+ for (size_t y = lymin; y < lymax; ++y) {
+ LightValue lvalue = getLight(x, y);
+ // + 0.5f to correct block position to center
+ Vec2F blockPos = Vec2F(x + 0.5f, y + 0.5f);
+
+ Vec2F relativeLightPosition = blockPos - light.position;
+ float distance = relativeLightPosition.magnitude();
+ if (distance == 0.0f) {
+ setLight(x, y, light.value + lvalue);
+ continue;
+ }
+
+ float attenuation = distance * perBlockAirAttenuation;
+ if (attenuation >= 1.0f)
+ continue;
+
+ Vec2F direction = relativeLightPosition / distance;
+ if (light.beam > 0.0f) {
+ attenuation += (1.0f - light.beamAmbience) * clamp(light.beam * (1.0f - direction * beamDirection), 0.0f, 1.0f);
+ if (attenuation >= 1.0f)
+ continue;
+ }
+
+ float remainingAttenuation = maxIntensity - attenuation;
+ if (remainingAttenuation <= 0.0f)
+ continue;
+
+ // Need to circularize manhattan attenuation here
+ float circularizedPerBlockObstacleAttenuation = perBlockObstacleAttenuation / max(fabs(direction[0]), fabs(direction[1]));
+ float blockAttenuation = lineAttenuation(blockPos, light.position, circularizedPerBlockObstacleAttenuation, remainingAttenuation);
+
+ // Apply single obstacle boost (determine single obstacle by one
+ // block unit of attenuation).
+ attenuation += blockAttenuation + min(blockAttenuation, circularizedPerBlockObstacleAttenuation) * m_pointObstacleBoost;
+
+ if (attenuation < 1.0f)
+ setLight(x, y, lvalue + ColoredLightTraits::subtract(light.value, attenuation));
+ }
+ }
+ }
+}
+
+} \ No newline at end of file
diff --git a/source/base/StarCellularLightArray.hpp b/source/base/StarCellularLightArray.hpp
index d73cacf..225d42b 100644
--- a/source/base/StarCellularLightArray.hpp
+++ b/source/base/StarCellularLightArray.hpp
@@ -11,6 +11,7 @@ struct ScalarLightTraits {
static float spread(float source, float dest, float drop);
static float subtract(float value, float drop);
+ static float multiply(float v1, float v2);
static float maxIntensity(float value);
static float minIntensity(float value);
@@ -26,6 +27,7 @@ struct ColoredLightTraits {
static Vec3F spread(Vec3F const& source, Vec3F const& dest, float drop);
static Vec3F subtract(Vec3F value, float drop);
+ static Vec3F multiply(Vec3F value, float drop);
static float maxIntensity(Vec3F const& value);
static float minIntensity(Vec3F const& value);
@@ -139,6 +141,10 @@ inline float ScalarLightTraits::subtract(float c, float drop) {
return std::max(c - drop, 0.0f);
}
+inline float ScalarLightTraits::multiply(float v1, float v2) {
+ return v1 * v2;
+}
+
inline float ScalarLightTraits::maxIntensity(float value) {
return value;
}
@@ -179,6 +185,10 @@ inline Vec3F ColoredLightTraits::subtract(Vec3F c, float drop) {
return c;
}
+inline Vec3F ColoredLightTraits::multiply(Vec3F c, float drop) {
+ return c * drop;
+}
+
inline float ColoredLightTraits::maxIntensity(Vec3F const& value) {
return value.max();
}
@@ -388,68 +398,6 @@ void CellularLightArray<LightTraits>::calculateLightSpread(size_t xMin, size_t y
}
template <typename LightTraits>
-void CellularLightArray<LightTraits>::calculatePointLighting(size_t xmin, size_t ymin, size_t xmax, size_t ymax) {
- float perBlockObstacleAttenuation = 1.0f / m_pointMaxObstacle;
- float perBlockAirAttenuation = 1.0f / m_pointMaxAir;
-
- for (PointLight light : m_pointLights) {
- if (light.position[0] < 0 || light.position[0] > m_width - 1 || light.position[1] < 0 || light.position[1] > m_height - 1)
- continue;
-
- float maxIntensity = LightTraits::maxIntensity(light.value);
- Vec2F beamDirection = Vec2F(1, 0).rotate(light.beamAngle);
-
- float maxRange = maxIntensity * m_pointMaxAir;
- // The min / max considering the radius of the light
- size_t lxmin = std::floor(std::max<float>(xmin, light.position[0] - maxRange));
- size_t lymin = std::floor(std::max<float>(ymin, light.position[1] - maxRange));
- size_t lxmax = std::ceil(std::min<float>(xmax, light.position[0] + maxRange));
- size_t lymax = std::ceil(std::min<float>(ymax, light.position[1] + maxRange));
-
- for (size_t x = lxmin; x < lxmax; ++x) {
- for (size_t y = lymin; y < lymax; ++y) {
- LightValue lvalue = getLight(x, y);
- // + 0.5f to correct block position to center
- Vec2F blockPos = Vec2F(x + 0.5f, y + 0.5f);
-
- Vec2F relativeLightPosition = blockPos - light.position;
- float distance = relativeLightPosition.magnitude();
- if (distance == 0.0f) {
- setLight(x, y, LightTraits::max(light.value, lvalue));
- continue;
- }
-
- float attenuation = distance * perBlockAirAttenuation;
- if (attenuation >= 1.0f)
- continue;
-
- Vec2F direction = relativeLightPosition / distance;
- if (light.beam > 0.0f) {
- attenuation += (1.0f - light.beamAmbience) * clamp(light.beam * (1.0f - direction * beamDirection), 0.0f, 1.0f);
- if (attenuation >= 1.0f)
- continue;
- }
-
- float remainingAttenuation = maxIntensity - LightTraits::minIntensity(lvalue) - attenuation;
- if (remainingAttenuation <= 0.0f)
- continue;
-
- // Need to circularize manhattan attenuation here
- float circularizedPerBlockObstacleAttenuation = perBlockObstacleAttenuation / max(fabs(direction[0]), fabs(direction[1]));
- float blockAttenuation = lineAttenuation(blockPos, light.position, circularizedPerBlockObstacleAttenuation, remainingAttenuation);
-
- // Apply single obstacle boost (determine single obstacle by one
- // block unit of attenuation).
- attenuation += blockAttenuation + min(blockAttenuation, circularizedPerBlockObstacleAttenuation) * m_pointObstacleBoost;
-
- if (attenuation < 1.0f)
- setLight(x, y, LightTraits::max(LightTraits::subtract(light.value, attenuation), lvalue));
- }
- }
- }
-}
-
-template <typename LightTraits>
float CellularLightArray<LightTraits>::lineAttenuation(Vec2F const& start, Vec2F const& end,
float perObstacleAttenuation, float maxAttenuation) {
// Run Xiaolin Wu's line algorithm from start to end, summing over colliding
diff --git a/source/base/StarCellularLighting.cpp b/source/base/StarCellularLighting.cpp
index c0a92ea..579f48a 100644
--- a/source/base/StarCellularLighting.cpp
+++ b/source/base/StarCellularLighting.cpp
@@ -2,6 +2,45 @@
namespace Star {
+Lightmap::Lightmap() : m_width(0), m_height(0) {}
+
+Lightmap::Lightmap(unsigned width, unsigned height) : m_width(width), m_height(height) {
+ m_data = std::make_unique<float[]>(len());
+}
+
+Lightmap::Lightmap(Lightmap const& lightMap) {
+ operator=(lightMap);
+}
+
+Lightmap::Lightmap(Lightmap&& lightMap) noexcept {
+ operator=(std::move(lightMap));
+}
+
+Lightmap& Lightmap::operator=(Lightmap const& lightMap) {
+ m_width = lightMap.m_width;
+ m_height = lightMap.m_height;
+ if (lightMap.m_data) {
+ m_data = std::make_unique<float[]>(len());
+ memcpy(m_data.get(), lightMap.m_data.get(), len());
+ }
+ return *this;
+}
+
+Lightmap& Lightmap::operator=(Lightmap&& lightMap) noexcept {
+ m_width = take(lightMap.m_width);
+ m_height = take(lightMap.m_height);
+ m_data = take(lightMap.m_data);
+ return *this;
+}
+
+Lightmap::operator ImageView() {
+ ImageView view;
+ view.data = (uint8_t*)m_data.get();
+ view.size = size();
+ view.format = PixelFormat::RGB_F;
+ return view;
+}
+
CellularLightingCalculator::CellularLightingCalculator(bool monochrome)
: m_monochrome(monochrome)
{
@@ -104,6 +143,32 @@ void CellularLightingCalculator::calculate(Image& output) {
}
}
+void CellularLightingCalculator::calculate(Lightmap& output) {
+ Vec2S arrayMin = Vec2S(m_queryRegion.min() - m_calculationRegion.min());
+ Vec2S arrayMax = Vec2S(m_queryRegion.max() - m_calculationRegion.min());
+
+ if (m_monochrome)
+ m_lightArray.right().calculate(arrayMin[0], arrayMin[1], arrayMax[0], arrayMax[1]);
+ else
+ m_lightArray.left().calculate(arrayMin[0], arrayMin[1], arrayMax[0], arrayMax[1]);
+
+ output = Lightmap(arrayMax[0] - arrayMin[0], arrayMax[1] - arrayMin[1]);
+
+ if (m_monochrome) {
+ for (size_t x = arrayMin[0]; x < arrayMax[0]; ++x) {
+ for (size_t y = arrayMin[1]; y < arrayMax[1]; ++y) {
+ output.set(x - arrayMin[0], y - arrayMin[1], m_lightArray.right().getLight(x, y));
+ }
+ }
+ } else {
+ for (size_t x = arrayMin[0]; x < arrayMax[0]; ++x) {
+ for (size_t y = arrayMin[1]; y < arrayMax[1]; ++y) {
+ output.set(x - arrayMin[0], y - arrayMin[1], m_lightArray.left().getLight(x, y));
+ }
+ }
+ }
+}
+
void CellularLightingCalculator::setupImage(Image& image, PixelFormat format) const {
Vec2S arrayMin = Vec2S(m_queryRegion.min() - m_calculationRegion.min());
Vec2S arrayMax = Vec2S(m_queryRegion.max() - m_calculationRegion.min());
diff --git a/source/base/StarCellularLighting.hpp b/source/base/StarCellularLighting.hpp
index caa52ae..170cbb5 100644
--- a/source/base/StarCellularLighting.hpp
+++ b/source/base/StarCellularLighting.hpp
@@ -11,6 +11,93 @@
namespace Star {
+STAR_EXCEPTION(LightmapException, StarException);
+
+class Lightmap {
+public:
+ Lightmap();
+ Lightmap(unsigned width, unsigned height);
+ Lightmap(Lightmap const& lightMap);
+ Lightmap(Lightmap&& lightMap) noexcept;
+
+ Lightmap& operator=(Lightmap const& lightMap);
+ Lightmap& operator=(Lightmap&& lightMap) noexcept;
+
+ operator ImageView();
+
+ void set(unsigned x, unsigned y, float v);
+ void set(unsigned x, unsigned y, Vec3F const& v);
+ Vec3F get(unsigned x, unsigned y) const;
+
+ bool empty() const;
+
+ Vec2U size() const;
+ unsigned width() const;
+ unsigned height() const;
+ float* data();
+
+private:
+ size_t len() const;
+
+ std::unique_ptr<float[]> m_data;
+ unsigned m_width;
+ unsigned m_height;
+};
+
+inline void Lightmap::set(unsigned x, unsigned y, float v) {
+ if (x >= m_width || y >= m_height) {
+ throw LightmapException(strf("[{}, {}] out of range in Lightmap::set", x, y));
+ return;
+ }
+ float* ptr = m_data.get() + (y * m_width * 3 + x * 3);
+ ptr[0] = ptr[1] = ptr[2] = v;
+}
+
+inline void Lightmap::set(unsigned x, unsigned y, Vec3F const& v) {
+ if (x >= m_width || y >= m_height) {
+ throw LightmapException(strf("[{}, {}] out of range in Lightmap::set", x, y));
+ return;
+ }
+ float* ptr = m_data.get() + (y * m_width * 3 + x * 3);
+ ptr[0] = v.x();
+ ptr[1] = v.y();
+ ptr[2] = v.z();
+}
+
+
+inline Vec3F Lightmap::get(unsigned x, unsigned y) const {
+ if (x >= m_width || y >= m_height) {
+ throw LightmapException(strf("[{}, {}] out of range in Lightmap::get", x, y));
+ return Vec3F();
+ }
+ float* ptr = m_data.get() + (y * m_width * 3 + x * 3);
+ return Vec3F(ptr[0], ptr[1], ptr[2]);
+}
+
+inline bool Lightmap::empty() const {
+ return m_width == 0 || m_height == 0;
+}
+
+inline Vec2U Lightmap::size() const {
+ return { m_width, m_height };
+}
+
+inline unsigned Lightmap::width() const {
+ return m_width;
+}
+
+inline unsigned Lightmap::height() const {
+ return m_height;
+}
+
+inline float* Lightmap::data() {
+ return m_data.get();
+}
+
+inline size_t Lightmap::len() const {
+ return m_width * m_height * 3;
+}
+
// Produce lighting values from an integral cellular grid. Allows for floating
// positional point and cellular light sources, as well as pre-lighting cells
// individually.
@@ -43,6 +130,8 @@ public:
// output image. The image will be reset to the size of the region given in
// the call to 'begin', and formatted as RGB24.
void calculate(Image& output);
+ // Same as above, but the color data in a float buffer instead.
+ void calculate(Lightmap& output);
void setupImage(Image& image, PixelFormat format = PixelFormat::RGB24) const;
private: