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

summaryrefslogtreecommitdiff
path: root/source/base/StarCellularLightArray.hpp
diff options
context:
space:
mode:
authorKae <80987908+Novaenia@users.noreply.github.com>2023-06-20 14:33:09 +1000
committerKae <80987908+Novaenia@users.noreply.github.com>2023-06-20 14:33:09 +1000
commit6352e8e3196f78388b6c771073f9e03eaa612673 (patch)
treee23772f79a7fbc41bc9108951e9e136857484bf4 /source/base/StarCellularLightArray.hpp
parent6741a057e5639280d85d0f88ba26f000baa58f61 (diff)
everything everywhere
all at once
Diffstat (limited to 'source/base/StarCellularLightArray.hpp')
-rw-r--r--source/base/StarCellularLightArray.hpp590
1 files changed, 590 insertions, 0 deletions
diff --git a/source/base/StarCellularLightArray.hpp b/source/base/StarCellularLightArray.hpp
new file mode 100644
index 0000000..f9e4094
--- /dev/null
+++ b/source/base/StarCellularLightArray.hpp
@@ -0,0 +1,590 @@
+#ifndef STAR_CELLULAR_LIGHT_ARRAY_HPP
+#define STAR_CELLULAR_LIGHT_ARRAY_HPP
+
+#include "StarList.hpp"
+#include "StarVector.hpp"
+
+namespace Star {
+
+// Operations for simple scalar lighting.
+struct ScalarLightTraits {
+ typedef float Value;
+
+ static float spread(float source, float dest, float drop);
+ static float subtract(float value, float drop);
+
+ static float maxIntensity(float value);
+ static float minIntensity(float value);
+
+ static float max(float v1, float v2);
+};
+
+// Operations for 3 component (colored) lighting. Spread and subtract are
+// applied proportionally, so that color ratios stay the same, to prevent hues
+// changing as light spreads.
+struct ColoredLightTraits {
+ typedef Vec3F Value;
+
+ static Vec3F spread(Vec3F const& source, Vec3F const& dest, float drop);
+ static Vec3F subtract(Vec3F value, float drop);
+
+ static float maxIntensity(Vec3F const& value);
+ static float minIntensity(Vec3F const& value);
+
+ static Vec3F max(Vec3F const& v1, Vec3F const& v2);
+};
+
+template <typename LightTraits>
+class CellularLightArray {
+public:
+ typedef typename LightTraits::Value LightValue;
+
+ struct Cell {
+ LightValue light;
+ bool obstacle;
+ };
+
+ struct SpreadLight {
+ Vec2F position;
+ LightValue value;
+ };
+
+ struct PointLight {
+ Vec2F position;
+ LightValue value;
+ float beam;
+ float beamAngle;
+ float beamAmbience;
+ };
+
+ void setParameters(unsigned spreadPasses, float spreadMaxAir, float spreadMaxObstacle,
+ float pointMaxAir, float pointMaxObstacle, float pointObstacleBoost);
+
+ // The border around the target lighting array where initial lighting / light
+ // source data is required. Based on parameters.
+ size_t borderCells() const;
+
+ // Begin a new calculation, setting internal storage to new width and height
+ // (if these are the same as last time this is cheap). Always clears all
+ // existing light and collision data.
+ void begin(size_t newWidth, size_t newHeight);
+
+ // Position is in index space, spread lights will have no effect if they are
+ // outside of the array. Integer points are assumed to be on the corners of
+ // the grid (not the center)
+ void addSpreadLight(SpreadLight const& spreadLight);
+ void addPointLight(PointLight const& pointLight);
+
+ // Directly set the lighting values for this position.
+ void setLight(size_t x, size_t y, LightValue const& light);
+
+ // Get current light value. Call after calling calculate() to pull final
+ // data out.
+ LightValue getLight(size_t x, size_t y) const;
+
+ // Set obstacle values for this position
+ void setObstacle(size_t x, size_t y, bool obstacle);
+ bool getObstacle(size_t x, size_t y) const;
+
+ Cell const& cell(size_t x, size_t y) const;
+ Cell& cell(size_t x, size_t y);
+
+ Cell const& cellAtIndex(size_t index) const;
+ Cell& cellAtIndex(size_t index);
+
+ // Calculate lighting in the given sub-rect, in order to properly do spread
+ // lighting, and initial lighting must be given for the ambient border this
+ // given rect, and the array size must be at least that large. xMax / yMax
+ // are not inclusive, the range is [xMin, xMax) and [yMin, yMax).
+ void calculate(size_t xMin, size_t yMin, size_t xMax, size_t yMax);
+
+private:
+ // Set 4 points based on interpolated light position and free space
+ // attenuation.
+ void setSpreadLightingPoints();
+
+ // Spreads light out in an octagonal based cellular automata
+ void calculateLightSpread(size_t xmin, size_t ymin, size_t xmax, size_t ymax);
+
+ // Loops through each light and adds light strength based on distance and
+ // obstacle attenuation. Calculates within the given sub-rect
+ void calculatePointLighting(size_t xmin, size_t ymin, size_t xmax, size_t ymax);
+
+ // Run Xiaolin Wu's anti-aliased line drawing algorithm from start to end,
+ // summing each block that would be drawn to to produce an attenuation. Not
+ // circularized.
+ float lineAttenuation(Vec2F const& start, Vec2F const& end, float perObstacleAttenuation, float maxAttenuation);
+
+ size_t m_width;
+ size_t m_height;
+ unique_ptr<Cell[]> m_cells;
+ List<SpreadLight> m_spreadLights;
+ List<PointLight> m_pointLights;
+
+ unsigned m_spreadPasses;
+ float m_spreadMaxAir;
+ float m_spreadMaxObstacle;
+ float m_pointMaxAir;
+ float m_pointMaxObstacle;
+ float m_pointObstacleBoost;
+};
+
+typedef CellularLightArray<ColoredLightTraits> ColoredCellularLightArray;
+typedef CellularLightArray<ScalarLightTraits> ScalarCellularLightArray;
+
+inline float ScalarLightTraits::spread(float source, float dest, float drop) {
+ return std::max(source - drop, dest);
+}
+
+inline float ScalarLightTraits::subtract(float c, float drop) {
+ return std::max(c - drop, 0.0f);
+}
+
+inline float ScalarLightTraits::maxIntensity(float value) {
+ return value;
+}
+
+inline float ScalarLightTraits::minIntensity(float value) {
+ return value;
+}
+
+inline float ScalarLightTraits::max(float v1, float v2) {
+ return std::max(v1, v2);
+}
+
+inline Vec3F ColoredLightTraits::spread(Vec3F const& source, Vec3F const& dest, float drop) {
+ float maxChannel = std::max(source[0], std::max(source[1], source[2]));
+ if (maxChannel <= 0.0f)
+ return dest;
+
+ drop /= maxChannel;
+ return Vec3F(
+ std::max(source[0] - source[0] * drop, dest[0]),
+ std::max(source[1] - source[1] * drop, dest[1]),
+ std::max(source[2] - source[2] * drop, dest[2])
+ );
+}
+
+inline Vec3F ColoredLightTraits::subtract(Vec3F c, float drop) {
+ float max = std::max(std::max(c[0], c[1]), c[2]);
+ if (max <= 0.0f)
+ return c;
+
+ for (size_t i = 0; i < 3; ++i) {
+ float pdrop = (drop * c[i]) / max;
+ if (c[i] > pdrop)
+ c[i] -= pdrop;
+ else
+ c[i] = 0;
+ }
+ return c;
+}
+
+inline float ColoredLightTraits::maxIntensity(Vec3F const& value) {
+ return value.max();
+}
+
+inline float ColoredLightTraits::minIntensity(Vec3F const& value) {
+ return value.min();
+}
+
+inline Vec3F ColoredLightTraits::max(Vec3F const& v1, Vec3F const& v2) {
+ return vmax(v1, v2);
+}
+
+template <typename LightTraits>
+void CellularLightArray<LightTraits>::setParameters(unsigned spreadPasses, float spreadMaxAir, float spreadMaxObstacle,
+ float pointMaxAir, float pointMaxObstacle, float pointObstacleBoost) {
+ m_spreadPasses = spreadPasses;
+ m_spreadMaxAir = spreadMaxAir;
+ m_spreadMaxObstacle = spreadMaxObstacle;
+ m_pointMaxAir = pointMaxAir;
+ m_pointMaxObstacle = pointMaxObstacle;
+ m_pointObstacleBoost = pointObstacleBoost;
+}
+
+template <typename LightTraits>
+size_t CellularLightArray<LightTraits>::borderCells() const {
+ return (size_t)ceil(max(0.0f, max(m_spreadMaxAir, m_pointMaxAir)));
+}
+
+template <typename LightTraits>
+void CellularLightArray<LightTraits>::begin(size_t newWidth, size_t newHeight) {
+ m_spreadLights.clear();
+ m_pointLights.clear();
+ starAssert(newWidth > 0 && newHeight > 0);
+
+ if (!m_cells || newWidth != m_width || newHeight != m_height) {
+ m_width = newWidth;
+ m_height = newHeight;
+
+ m_cells.reset(new Cell[m_width * m_height]());
+
+ } else {
+ std::fill(m_cells.get(), m_cells.get() + m_width * m_height, Cell{LightValue{}, false});
+ }
+}
+
+template <typename LightTraits>
+void CellularLightArray<LightTraits>::addSpreadLight(SpreadLight const& spreadLight) {
+ m_spreadLights.append(spreadLight);
+}
+
+template <typename LightTraits>
+void CellularLightArray<LightTraits>::addPointLight(PointLight const& pointLight) {
+ m_pointLights.append(pointLight);
+}
+
+template <typename LightTraits>
+void CellularLightArray<LightTraits>::setLight(size_t x, size_t y, LightValue const& lightValue) {
+ cell(x, y).light = lightValue;
+}
+
+template <typename LightTraits>
+void CellularLightArray<LightTraits>::setObstacle(size_t x, size_t y, bool obstacle) {
+ cell(x, y).obstacle = obstacle;
+}
+
+template <typename LightTraits>
+auto CellularLightArray<LightTraits>::getLight(size_t x, size_t y) const -> LightValue {
+ return cell(x, y).light;
+}
+
+template <typename LightTraits>
+bool CellularLightArray<LightTraits>::getObstacle(size_t x, size_t y) const {
+ return cell(x, y).obstacle;
+}
+
+template <typename LightTraits>
+auto CellularLightArray<LightTraits>::cell(size_t x, size_t y) const -> Cell const & {
+ starAssert(x < m_width && y < m_height);
+ return m_cells[x * m_height + y];
+}
+
+template <typename LightTraits>
+auto CellularLightArray<LightTraits>::cell(size_t x, size_t y) -> Cell & {
+ starAssert(x < m_width && y < m_height);
+ return m_cells[x * m_height + y];
+}
+
+template <typename LightTraits>
+auto CellularLightArray<LightTraits>::cellAtIndex(size_t index) const -> Cell const & {
+ starAssert(index < m_width * m_height);
+ return m_cells[index];
+}
+
+template <typename LightTraits>
+auto CellularLightArray<LightTraits>::cellAtIndex(size_t index) -> Cell & {
+ starAssert(index < m_width * m_height);
+ return m_cells[index];
+}
+
+template <typename LightTraits>
+void CellularLightArray<LightTraits>::calculate(size_t xMin, size_t yMin, size_t xMax, size_t yMax) {
+ setSpreadLightingPoints();
+ calculateLightSpread(xMin, yMin, xMax, yMax);
+ calculatePointLighting(xMin, yMin, xMax, yMax);
+}
+
+template <typename LightTraits>
+void CellularLightArray<LightTraits>::setSpreadLightingPoints() {
+ for (SpreadLight const& light : m_spreadLights) {
+ // - 0.5f to correct for lights being on the grid corners and not center
+ int minX = floor(light.position[0] - 0.5f);
+ int minY = floor(light.position[1] - 0.5f);
+ int maxX = minX + 1;
+ int maxY = minY + 1;
+
+ float xdist = light.position[0] - minX - 0.5f;
+ float ydist = light.position[1] - minY - 0.5f;
+
+ // Pick falloff here based on closest block obstacle value (probably not
+ // best)
+ Vec2I pos(light.position.floor());
+ float oneBlockAtt;
+ if (pos[0] >= 0 && pos[0] < (int)m_width && pos[1] >= 0 && pos[1] < (int)m_height && getObstacle(pos[0], pos[1]))
+ oneBlockAtt = 1.0f / m_spreadMaxObstacle;
+ else
+ oneBlockAtt = 1.0f / m_spreadMaxAir;
+
+ // "pre fall-off" a 2x2 area of blocks to smooth out floating point
+ // positions using the cellular algorithm
+
+ if (minX >= 0 && minX < (int)m_width && minY >= 0 && minY < (int)m_height)
+ setLight(minX, minY, LightTraits::max(getLight(minX, minY), LightTraits::subtract(light.value, oneBlockAtt * (2.0f - (1.0f - xdist) - (1.0f - ydist)))));
+
+ if (minX >= 0 && minX < (int)m_width && maxY >= 0 && maxY < (int)m_height)
+ setLight(minX, maxY, LightTraits::max(getLight(minX, maxY), LightTraits::subtract(light.value, oneBlockAtt * (2.0f - (1.0f - xdist) - (ydist)))));
+
+ if (maxX >= 0 && maxX < (int)m_width && minY >= 0 && minY < (int)m_height)
+ setLight(maxX, minY, LightTraits::max(getLight(maxX, minY), LightTraits::subtract(light.value, oneBlockAtt * (2.0f - (xdist) - (1.0f - ydist)))));
+
+ if (maxX >= 0 && maxX < (int)m_width && maxY >= 0 && maxY < (int)m_height)
+ setLight(maxX, maxY, LightTraits::max(getLight(maxX, maxY), LightTraits::subtract(light.value, oneBlockAtt * (2.0f - (xdist) - (ydist)))));
+ }
+}
+
+template <typename LightTraits>
+void CellularLightArray<LightTraits>::calculateLightSpread(size_t xMin, size_t yMin, size_t xMax, size_t yMax) {
+ starAssert(m_width > 0 && m_height > 0);
+
+ float dropoffAir = 1.0f / m_spreadMaxAir;
+ float dropoffObstacle = 1.0f / m_spreadMaxObstacle;
+ float dropoffAirDiag = 1.0f / m_spreadMaxAir * Constants::sqrt2;
+ float dropoffObstacleDiag = 1.0f / m_spreadMaxObstacle * Constants::sqrt2;
+
+ // enlarge x/y min/max taking into ambient spread of light
+ xMin = xMin - min(xMin, (size_t)ceil(m_spreadMaxAir));
+ yMin = yMin - min(yMin, (size_t)ceil(m_spreadMaxAir));
+ xMax = min(m_width, xMax + (size_t)ceil(m_spreadMaxAir));
+ yMax = min(m_height, yMax + (size_t)ceil(m_spreadMaxAir));
+
+ for (unsigned p = 0; p < m_spreadPasses; ++p) {
+ // Spread right and up and diag up right / diag down right
+ for (size_t x = xMin + 1; x < xMax - 1; ++x) {
+ size_t xCellOffset = x * m_height;
+ size_t xRightCellOffset = (x + 1) * m_height;
+
+ for (size_t y = yMin + 1; y < yMax - 1; ++y) {
+ auto cell = cellAtIndex(xCellOffset + y);
+ auto& cellRight = cellAtIndex(xRightCellOffset + y);
+ auto& cellUp = cellAtIndex(xCellOffset + y + 1);
+ auto& cellRightUp = cellAtIndex(xRightCellOffset + y + 1);
+ auto& cellRightDown = cellAtIndex(xRightCellOffset + y - 1);
+
+ float straightDropoff = cell.obstacle ? dropoffObstacle : dropoffAir;
+ float diagDropoff = cell.obstacle ? dropoffObstacleDiag : dropoffAirDiag;
+
+ cellRight.light = LightTraits::spread(cell.light, cellRight.light, straightDropoff);
+ cellUp.light = LightTraits::spread(cell.light, cellUp.light, straightDropoff);
+
+ cellRightUp.light = LightTraits::spread(cell.light, cellRightUp.light, diagDropoff);
+ cellRightDown.light = LightTraits::spread(cell.light, cellRightDown.light, diagDropoff);
+ }
+ }
+
+ // Spread left and down and diag up left / diag down left
+ for (size_t x = xMax - 2; x > xMin; --x) {
+ size_t xCellOffset = x * m_height;
+ size_t xLeftCellOffset = (x - 1) * m_height;
+
+ for (size_t y = yMax - 2; y > yMin; --y) {
+ auto cell = cellAtIndex(xCellOffset + y);
+ auto& cellLeft = cellAtIndex(xLeftCellOffset + y);
+ auto& cellDown = cellAtIndex(xCellOffset + y - 1);
+ auto& cellLeftUp = cellAtIndex(xLeftCellOffset + y + 1);
+ auto& cellLeftDown = cellAtIndex(xLeftCellOffset + y - 1);
+
+ float straightDropoff = cell.obstacle ? dropoffObstacle : dropoffAir;
+ float diagDropoff = cell.obstacle ? dropoffObstacleDiag : dropoffAirDiag;
+
+ cellLeft.light = LightTraits::spread(cell.light, cellLeft.light, straightDropoff);
+ cellDown.light = LightTraits::spread(cell.light, cellDown.light, straightDropoff);
+
+ cellLeftUp.light = LightTraits::spread(cell.light, cellLeftUp.light, diagDropoff);
+ cellLeftDown.light = LightTraits::spread(cell.light, cellLeftDown.light, diagDropoff);
+ }
+ }
+ }
+}
+
+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
+ // blocks using perObstacleAttenuation.
+ float obstacleAttenuation = 0.0;
+
+ // Apply correction because integer coordinates are lower left corner.
+ float x1 = start[0] - 0.5;
+ float y1 = start[1] - 0.5;
+ float x2 = end[0] - 0.5;
+ float y2 = end[1] - 0.5;
+
+ float dx = x2 - x1;
+ float dy = y2 - y1;
+
+ if (fabs(dx) < fabs(dy)) {
+ if (y2 < y1) {
+ swap(y1, y2);
+ swap(x1, x2);
+ }
+
+ float gradient = dx / dy;
+
+ // first end point
+ float yend = round(y1);
+ float xend = x1 + gradient * (yend - y1);
+ float ygap = rfpart(y1 + 0.5);
+ int ypxl1 = yend;
+ int xpxl1 = ipart(xend);
+
+ if (cell(xpxl1, ypxl1).obstacle)
+ obstacleAttenuation += rfpart(xend) * ygap * perObstacleAttenuation;
+
+ if (cell(xpxl1 + 1, ypxl1).obstacle)
+ obstacleAttenuation += fpart(xend) * ygap * perObstacleAttenuation;
+
+ if (obstacleAttenuation >= maxAttenuation)
+ return maxAttenuation;
+
+ float interx = xend + gradient;
+
+ // second end point
+ yend = round(y2);
+ xend = x2 + gradient * (yend - y2);
+ ygap = fpart(y2 + 0.5);
+ int ypxl2 = yend;
+ int xpxl2 = ipart(xend);
+
+ if (cell(xpxl2, ypxl2).obstacle)
+ obstacleAttenuation += rfpart(xend) * ygap * perObstacleAttenuation;
+
+ if (cell(xpxl2 + 1, ypxl2).obstacle)
+ obstacleAttenuation += fpart(xend) * ygap * perObstacleAttenuation;
+
+ if (obstacleAttenuation >= maxAttenuation)
+ return maxAttenuation;
+
+ for (int y = ypxl1 + 1; y < ypxl2; ++y) {
+ int interxIpart = ipart(interx);
+ float interxFpart = interx - interxIpart;
+ float interxRFpart = 1.0 - interxFpart;
+
+ if (cell(interxIpart, y).obstacle)
+ obstacleAttenuation += interxRFpart * perObstacleAttenuation;
+ if (cell(interxIpart + 1, y).obstacle)
+ obstacleAttenuation += interxFpart * perObstacleAttenuation;
+
+ if (obstacleAttenuation >= maxAttenuation)
+ return maxAttenuation;
+
+ interx += gradient;
+ }
+ } else {
+ if (x2 < x1) {
+ swap(x1, x2);
+ swap(y1, y2);
+ }
+
+ float gradient = dy / dx;
+
+ // first end point
+ float xend = round(x1);
+ float yend = y1 + gradient * (xend - x1);
+ float xgap = rfpart(x1 + 0.5);
+ int xpxl1 = xend;
+ int ypxl1 = ipart(yend);
+
+ if (cell(xpxl1, ypxl1).obstacle)
+ obstacleAttenuation += rfpart(yend) * xgap * perObstacleAttenuation;
+
+ if (cell(xpxl1, ypxl1 + 1).obstacle)
+ obstacleAttenuation += fpart(yend) * xgap * perObstacleAttenuation;
+
+ if (obstacleAttenuation >= maxAttenuation)
+ return maxAttenuation;
+
+ float intery = yend + gradient;
+
+ // second end point
+ xend = round(x2);
+ yend = y2 + gradient * (xend - x2);
+ xgap = fpart(x2 + 0.5);
+ int xpxl2 = xend;
+ int ypxl2 = ipart(yend);
+
+ if (cell(xpxl2, ypxl2).obstacle)
+ obstacleAttenuation += rfpart(yend) * xgap * perObstacleAttenuation;
+
+ if (cell(xpxl2, ypxl2 + 1).obstacle)
+ obstacleAttenuation += fpart(yend) * xgap * perObstacleAttenuation;
+
+ if (obstacleAttenuation >= maxAttenuation)
+ return maxAttenuation;
+
+ for (int x = xpxl1 + 1; x < xpxl2; ++x) {
+ int interyIpart = ipart(intery);
+ float interyFpart = intery - interyIpart;
+ float interyRFpart = 1.0 - interyFpart;
+
+ if (cell(x, interyIpart).obstacle)
+ obstacleAttenuation += interyRFpart * perObstacleAttenuation;
+ if (cell(x, interyIpart + 1).obstacle)
+ obstacleAttenuation += interyFpart * perObstacleAttenuation;
+
+ if (obstacleAttenuation >= maxAttenuation)
+ return maxAttenuation;
+
+ intery += gradient;
+ }
+ }
+
+ return min(obstacleAttenuation, maxAttenuation);
+}
+
+}
+
+#endif