Back to Poly Dogfight
SOURCE / PINNED RELEASE

Made of little things.

Poly Dogfight

Release
b8511ee374e3…
Author-recorded commit
5579434b9d8b…
License
LICENSE
Author’s source reference
nostr://npub1ye5ptcxfyyxl5vjvdjar2ua3f0hynkjzpx552mu5snj3qmx5pzjscpknpr/wss%3A%2F%2Fgit.napplet.soy%2F/n-44f63e5422b

Archive hash verified: bcadd2430d8ea2bc…. The source-to-build association is the author’s claim; it has not been independently rebuilt.

src/sim/terrain.ts
/**
 * The single source of truth for the planet's shape. The renderer displaces the
 * planet mesh with terrainHeight and the flight sim uses it for ground collision.
 */
import { WORLD_SEED } from './constants.ts';
import { createNoise3, fbm, smoothstep } from './noise.ts';

const continents = createNoise3(WORLD_SEED);
const ridges = createNoise3(WORLD_SEED + 1);
const detail = createNoise3(WORLD_SEED + 2);
const warp = createNoise3(WORLD_SEED + 3);

function normalize3(x: number, y: number, z: number): [number, number, number] {
  const len = Math.hypot(x, y, z) || 1;
  return [x / len, y / len, z / len];
}

/** The Great Rift: a canyon cut into a raised plateau along part of a great circle. */
export const RIFT = (() => {
  const normal = normalize3(0.35, 0.82, 0.45);
  // Two tangent axes spanning the great circle's plane.
  const e1 = normalize3(-normal[1], normal[0], 0);
  const e2: [number, number, number] = [
    normal[1] * e1[2] - normal[2] * e1[1],
    normal[2] * e1[0] - normal[0] * e1[2],
    normal[0] * e1[1] - normal[1] * e1[0],
  ];
  return { normal, e1, e2, arcStart: 0.2, arcEnd: 2.5 };
})();

/** Returns the angle along the rift and the signed angular distance from its centreline. */
export function riftCoordinates(x: number, y: number, z: number): { along: number; across: number } {
  const { normal, e1, e2 } = RIFT;
  const along = Math.atan2(
    x * e2[0] + y * e2[1] + z * e2[2],
    x * e1[0] + y * e1[1] + z * e1[2],
  );
  const wiggle = 0.028 * Math.sin(along * 7.0) + 0.012 * Math.sin(along * 19.0 + 1.3);
  const across = Math.asin(Math.max(-1, Math.min(1, x * normal[0] + y * normal[1] + z * normal[2]))) + wiggle;
  return { along, across };
}

/**
 * Height above sea level (metres) of the ground in the unit direction (x, y, z).
 * Negative values are sea floor; the sea surface is at 0.
 */
export function terrainHeight(x: number, y: number, z: number): number {
  const wx = x + 0.22 * warp(x * 2.1, y * 2.1, z * 2.1);
  const wy = y + 0.22 * warp(y * 2.1 + 5.2, z * 2.1, x * 2.1);
  const wz = z + 0.22 * warp(z * 2.1, x * 2.1 + 9.7, y * 2.1);

  const base = fbm(continents, wx * 1.3, wy * 1.3, wz * 1.3, 5) + 0.05;
  let h: number;
  if (base <= 0) {
    h = Math.max(-70, base * 160);
  } else {
    // Gentle lowlands near the coast, rising into ridged mountain chains inland.
    const lowland = 34 * Math.pow(base, 1.15);
    let ridge = 0;
    let amp = 1;
    let freq = 3.2;
    for (let o = 0; o < 3; o++) {
      const r = 1 - Math.abs(ridges(wx * freq, wy * freq, wz * freq));
      ridge += amp * r * r;
      amp *= 0.45;
      freq *= 2.1;
    }
    const mountains = 62 * (ridge - 0.45) * smoothstep(0.06, 0.3, base);
    h = 2 + lowland + Math.max(0, mountains);
  }
  // Surface detail fades out toward the coast so beaches stay clean.
  if (base > 0) h += fbm(detail, x * 11, y * 11, z * 11, 2) * 5 * smoothstep(0.02, 0.16, base);

  // The Great Rift plateau and canyon.
  const { along, across } = riftCoordinates(x, y, z);
  const arcMask =
    smoothstep(RIFT.arcStart - 0.15, RIFT.arcStart + 0.1, along) *
    (1 - smoothstep(RIFT.arcEnd - 0.1, RIFT.arcEnd + 0.15, along));
  if (arcMask > 0) {
    const d = Math.abs(across) + 0.01 * detail(x * 30, y * 30, z * 30);
    const plateau = (52 + 8 * detail(x * 6, y * 6, z * 6)) * smoothstep(0.17, 0.11, d) * arcMask;
    if (plateau > 0) h = Math.max(h, plateau);
    const channel = smoothstep(0.05, 0.03, d) * arcMask;
    if (channel > 0) h = h + (6 - h) * channel;
  }
  return h;
}

/** Ground or sea surface height used for crashes and shadows. */
export function surfaceHeight(x: number, y: number, z: number): number {
  return Math.max(0, terrainHeight(x, y, z));
}