153 lines
6.6 KiB
C++
153 lines
6.6 KiB
C++
#include "StarCellularLightArray.hpp"
|
|
#include "StarInterpolation.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 pointPerBlockObstacleAttenuation = 1.0f / m_pointMaxObstacle;
|
|
float pointPerBlockAirAttenuation = 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 perBlockObstacleAttenuation = light.asSpread ? 1.0f / m_spreadMaxObstacle : pointPerBlockObstacleAttenuation;
|
|
float perBlockAirAttenuation = light.asSpread ? 1.0f / m_spreadMaxAir : pointPerBlockAirAttenuation;
|
|
|
|
float maxRange = maxIntensity * (light.asSpread ? m_spreadMaxAir : 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.0001f) {
|
|
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);
|
|
|
|
attenuation += blockAttenuation;
|
|
// Apply single obstacle boost (determine single obstacle by one
|
|
// block unit of attenuation).
|
|
if (!light.asSpread)
|
|
attenuation += min(blockAttenuation, circularizedPerBlockObstacleAttenuation) * m_pointObstacleBoost;
|
|
|
|
if (attenuation < 1.0f) {
|
|
auto newLight = ScalarLightTraits::subtract(light.value, attenuation);
|
|
if (ScalarLightTraits::maxIntensity(newLight) > 0.0001f) {
|
|
if (light.asSpread)
|
|
setLight(x, y, lvalue + newLight * 0.25f);
|
|
else
|
|
setLight(x, y, lvalue + newLight);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
template <>
|
|
void CellularLightArray<ColoredLightTraits>::calculatePointLighting(size_t xmin, size_t ymin, size_t xmax, size_t ymax) {
|
|
float pointPerBlockObstacleAttenuation = 1.0f / m_pointMaxObstacle;
|
|
float pointPerBlockAirAttenuation = 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 perBlockObstacleAttenuation = light.asSpread ? 1.0f / m_spreadMaxObstacle : pointPerBlockObstacleAttenuation;
|
|
float perBlockAirAttenuation = light.asSpread ? 1.0f / m_spreadMaxAir : pointPerBlockAirAttenuation;
|
|
|
|
float maxRange = maxIntensity * (light.asSpread ? m_spreadMaxAir : 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);
|
|
|
|
attenuation += blockAttenuation;
|
|
// Apply single obstacle boost (determine single obstacle by one
|
|
// block unit of attenuation).
|
|
if (!light.asSpread)
|
|
attenuation += min(blockAttenuation, circularizedPerBlockObstacleAttenuation) * m_pointObstacleBoost;
|
|
|
|
if (attenuation < 1.0f) {
|
|
auto newLight = ColoredLightTraits::subtract(light.value, attenuation);
|
|
if (ColoredLightTraits::maxIntensity(newLight) > 0.0001f) {
|
|
if (light.asSpread)
|
|
setLight(x, y, lvalue + newLight * 0.25f);
|
|
else
|
|
setLight(x, y, lvalue + newLight);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
} |