pike/val Public Public: anyone can read this; nobody can change it.

forked from benjivers/val

workbenchthis machine
Files 229dad72036da758215f63a6f96e2ff048659d3d
1
/**
2
 * Wall columns, collapsed into runs.
3
 *
4
 * A raycaster produces one vertical slice per screen column, and the naive
5
 * renderer pays one `fillRect` for each of them — a couple of thousand fills
6
 * a frame on a wide canvas, most of them identical to their neighbour. The
7
 * shading is quantised into levels already (`scene.js`), so a flat wall face
8
 * is a stretch of columns asking for exactly the same rectangle, drawn one
9
 * pixel at a time.
10
 *
11
 * This walks the columns once and hands back the runs: the stretches where
12
 * the level, the face, the haze over it and the geometry all agree, so the
13
 * caller draws one rectangle for the stretch. A corridor collapses hundreds
14
 * of fills into a handful and the picture does not change.
15
 *
16
 * Pure data in, pure data out — no canvas here — and nothing is allocated
17
 * per frame: the column arrays and the run objects are kept and rewritten.
18
 */
19
20
/** A level of -1 is sky: no wall was struck, and no run covers it. */
21
export const SKY = -1;
22
/** How far apart two slices may sit and still be drawn as one rectangle. */
23
export const GEOM = 0.5;
24
/** Haze is quantised this finely before it joins the run's identity. */
25
export const HAZE_STEPS = 16;
26
27
/** The columns of one frame: what the ray loop writes, all of it flat. */
28
export function makeColumns(count) {
29
  return {
30
    count,
31
    level: new Int16Array(count).fill(SKY),
32
    side: new Int8Array(count),
33
    top: new Float32Array(count),
34
    high: new Float32Array(count),
35
    haze: new Float32Array(count),
36
  };
37
}
38
39
const run = () => ({ from: 0, to: 0, level: SKY, side: 0, top: 0, high: 0, step: 0, haze: 0 });
40
41
/** The quantised haze of a column: equal steps draw as one rectangle. */
42
export const hazeStep = (haze) => Math.round(Math.min(1, Math.max(0, haze)) * HAZE_STEPS);
43
44
/**
45
 * Write the runs of `cols` into `out`, growing it as needed, and return how
46
 * many of its entries are this frame's. `out[i].from` and `.to` are column
47
 * indices, both inclusive.
48
 */
49
export function wallRuns(cols, out) {
50
  let used = 0;
51
  let open = false; // is out[used - 1] still growing, or did sky end it?
52
  for (let i = 0; i < cols.count; i++) {
53
    const level = cols.level[i];
54
    if (level === SKY) {
55
      open = false;
56
      continue;
57
    }
58
    const step = hazeStep(cols.haze[i]);
59
    const top = cols.top[i];
60
    const high = cols.high[i];
61
    const last = used > 0 ? out[used - 1] : null;
62
    if (
63
      open &&
64
      last.level === level &&
65
      last.side === cols.side[i] &&
66
      last.step === step &&
67
      Math.abs(last.top - top) <= GEOM &&
68
      Math.abs(last.high - high) <= GEOM
69
    ) {
70
      last.to = i;
71
      continue;
72
    }
73
    if (out.length <= used) out.push(run());
74
    const fresh = out[used];
75
    fresh.from = i;
76
    fresh.to = i;
77
    fresh.level = level;
78
    fresh.side = cols.side[i];
79
    fresh.top = top;
80
    fresh.high = high;
81
    fresh.step = step;
82
    fresh.haze = step / HAZE_STEPS;
83
    used++;
84
    open = true;
85
  }
86
  return used;
87
}
88
89
/** How many columns the runs cover: the picture's width, for a test to check. */
90
export function covered(out, used) {
91
  let total = 0;
92
  for (let i = 0; i < used; i++) total += out[i].to - out[i].from + 1;
93
  return total;
94
}
↑↓ navigate ↵ open esc close
↑↓ navigate ↵ open esc close

Keyboard shortcuts