55 lines
2.0 KiB
JavaScript
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 };
|
|
}
|