glytch/fktry/src/render/remove.ts
LANE-SIM d1c3f2b09d [sim] round 3 NOTES
Confirms the units conversion (bufferCap 240 = 8.0s of cover at a 30/s
deficit) and reports scram survival: first scram t434, restart t934,
~718-tick duty cycle, ZERO brownout ticks across 20k, melt rate unchanged.

For the orchestrator:
- heat semantics need a one-line ruling. heatPerTick is GROSS in my model
  (net = heatPerTick - coolPerTick). DATA's committed 0.0025 against the
  0.004 default nets negative -- the decoder could never scram. Their
  in-flight 0.0033/0.001 works, so we agree by luck of timing.
- the codex's literal bloom loop is not expressible with per-grade recipes
- asic-cooler does nothing without a spatial heat mechanic
- my sim code landed inside 45003db [ui] REMOVE mode: the shared tree has
  one git index and an add-everything commit in another session swept it.
  Not a UI ownership violation. Suggest a no-`git add -A` rule.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 18:35:47 +10:00

108 lines
3.0 KiB
TypeScript

/**
* LANE-RENDER — remove-mode hover.
*
* Two reads at once: a hazard-striped decal on the machine's footprint (this is the
* ground you are about to clear) and a red wash over its volume (this is the thing that
* is going away). Stripes because the codex dresses everything horrifying as routine
* industrial equipment — demolition should look like a permit, not an explosion.
*
* Driven by the `__remove` ghost sentinel; see ghost.ts.
*/
import * as THREE from 'three';
const DEMOLITION = 0xff3b30;
const DECAL_VERT = /* glsl */ `
varying vec2 vUv;
void main() {
vUv = uv;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
`;
const DECAL_FRAG = /* glsl */ `
precision highp float;
uniform float uTime;
uniform vec2 uScale; // footprint in tiles, so stripes don't stretch on big machines
uniform vec3 uColor;
varying vec2 vUv;
void main() {
// Border: constant thickness regardless of footprint.
vec2 d = min(vUv, 1.0 - vUv) * uScale;
float edge = min(d.x, d.y);
float border = 1.0 - smoothstep(0.04, 0.10, edge);
// Hazard stripes, crawling.
vec2 p = vUv * uScale;
float s = fract((p.x + p.y) * 1.6 - uTime * 0.5);
float stripe = step(0.5, s);
float a = max(border, stripe * 0.28);
if (a < 0.01) discard;
gl_FragColor = vec4(uColor, a * 0.85);
}
`;
export class RemoveHover {
readonly group = new THREE.Group();
private decal: THREE.Mesh;
private wash: THREE.Mesh;
private uniforms = {
uTime: { value: 0 },
uScale: { value: new THREE.Vector2(1, 1) },
uColor: { value: new THREE.Color(DEMOLITION) },
};
constructor() {
this.group.name = 'removeHover';
this.group.visible = false;
this.group.renderOrder = 11;
this.decal = new THREE.Mesh(
new THREE.PlaneGeometry(1, 1),
new THREE.ShaderMaterial({
uniforms: this.uniforms,
vertexShader: DECAL_VERT,
fragmentShader: DECAL_FRAG,
transparent: true,
depthWrite: false,
}),
);
this.decal.rotation.x = -Math.PI / 2;
this.decal.position.y = 0.02; // just clear of the ground plane
this.group.add(this.decal);
this.wash = new THREE.Mesh(
new THREE.BoxGeometry(1, 1, 1),
new THREE.MeshBasicMaterial({
color: DEMOLITION,
transparent: true,
opacity: 0.3,
depthWrite: false,
}),
);
this.group.add(this.wash);
}
hide(): void {
this.group.visible = false;
}
/**
* Frame the machine we're about to demolish. `w`/`d` are its ROTATED footprint, so a
* 2x3 turned east reads as 3x2 — the decal has to match what's actually occupied.
*/
show(cx: number, cz: number, w: number, d: number, height: number, timeSec: number): void {
this.group.visible = true;
this.uniforms.uTime.value = timeSec;
this.uniforms.uScale.value.set(w, d);
this.decal.scale.set(w, d, 1);
this.decal.position.set(cx, 0.02, cz);
this.wash.scale.set(w * 0.94, height, d * 0.94);
this.wash.position.set(cx, height / 2, cz);
}
}