Files

55 lines
2.0 KiB
JavaScript

// Distance from each sea tile to the nearest land, for the coastal wavelets.
//
// The ocean is drawn as a single flat quad, so the water shader cannot see the
// coastline directly. This walks a multi-source breadth-first search out of
// every land tile across the sea and stores, per tile, how many tiles away the
// shore is. The shader samples it to know where the wavelets should run. One
// byte per tile, normalised so 0 is the coast and 255 is `range` tiles or more
// offshore.
import { key } from "../../../shared/hex.js";
import { isWaterTile } from "./textures.js";
export function coastDistanceField(view, range = 8) {
const topo = view && view.topology;
const tiles = (view && view.tiles) || {};
if (!topo || range <= 0) return null;
const width = topo.width;
const height = topo.height;
const originX = topo.originX;
const originY = topo.originY;
const index = (x, y) => (y - originY) * width + (x - originX);
const dist = new Int32Array(width * height).fill(-1);
const queue = [];
for (let y = originY; y < originY + height; y++) {
for (let x = originX; x < originX + width; x++) {
const tile = tiles[key(x, y)];
if (!tile || isWaterTile(tile)) continue;
dist[index(x, y)] = 0;
queue.push({ x, y });
}
}
let head = 0;
while (head < queue.length) {
const { x, y } = queue[head++];
const distance = dist[index(x, y)];
if (distance >= range) continue;
for (const raw of topo.neighbours(x, y)) {
const nx = topo.wrapX(raw.x);
const ny = raw.y;
if (ny < originY || ny >= originY + height) continue;
const i = index(nx, ny);
if (dist[i] !== -1) continue;
if (!tiles[key(nx, ny)]) continue;
dist[i] = distance + 1;
queue.push({ x: nx, y: ny });
}
}
const data = new Uint8Array(width * height);
for (let i = 0; i < dist.length; i++) {
const distance = dist[i] < 0 ? range : Math.min(dist[i], range);
data[i] = Math.round((distance / range) * 255);
}
return { width, height, data };
}