From 8706387ff65c79afe7b4c842b962294ae13eff88 Mon Sep 17 00:00:00 2001 From: type-two Date: Sun, 19 Jul 2026 22:07:28 +1000 Subject: [PATCH] [lane B] The phage gets its wings: an iconic capsid-tail-legs silhouette, built from primitives MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #3 of the microbe pass. The phage ally was a bare icosahedron; now it's the recognizable T4 "lander" — icosahedral capsid + tail sheath + six splayed leg fibres. I ran the requested hunyuan3d_mlx path first (flux concept -> hunyuan): it gave a great FLUX reference but, exactly as ART_BIBLE predicts for thin features, the mesh WEBBED the legs into a near-cubic 1.27x1.41x0.94 blob — no better than the icosahedron, at 217KB. So the phage is built PROCEDURALLY (phageGeo): merged icosahedron + cylinders, ~150 tris, deterministic, bold enough to read as a spidery phage at gameplay scale. The one microbe where geometry beats photogrammetry — its shape IS a platonic solid plus cylinders. (mergeGeometries needs all-or-none indexed; icosahedron is non-indexed and cylinders indexed, so each part is toNonIndexed()'d before the merge.) Verified in-engine: three phages read clearly as capsid+legs from head-on, side, and away. Co-Authored-By: Claude Opus 4.8 --- web/js/combat/enemies.js | 40 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 36 insertions(+), 4 deletions(-) diff --git a/web/js/combat/enemies.js b/web/js/combat/enemies.js index e620958..6d34875 100644 --- a/web/js/combat/enemies.js +++ b/web/js/combat/enemies.js @@ -12,6 +12,7 @@ // Determinism: every random here comes from the seeded rng('enemies') stream. import * as THREE from 'three'; +import { mergeGeometries } from 'three/addons/utils/BufferGeometryUtils.js'; import { BALANCE as B } from './balance.js'; import { emissiveMat, EMISSIVE } from '../flight/emissive.js'; @@ -20,6 +21,37 @@ const _dir = new THREE.Vector3(), _v = new THREE.Vector3(); const FORWARD = new THREE.Vector3(0, 0, 1); const BUCKET = 20; // u of s per broad-phase bucket +// The bacteriophage silhouette, built from primitives. The phage is the ONE microbe that is +// geometric, not blobby — an icosahedral capsid on a tail sheath with splayed leg fibres — so +// image->3D (Trellis/Hunyuan) webs its thin legs into a lump (measured: a 1.27x1.41x0.94 blob), +// while merged cylinders nail the iconic "lander" at ~140 tris. Authored along +Z (travel dir): +// legs reach forward, capsid trails. +const _up = new THREE.Vector3(0, 1, 0); +function phageGeo(r) { + const parts = []; + const head = new THREE.IcosahedronGeometry(r * 0.82, 0); + head.translate(0, 0, -r * 0.75); + parts.push(head); + const tail = new THREE.CylinderGeometry(r * 0.22, r * 0.22, r * 0.9, 6); // BOLD sheath + tail.rotateX(Math.PI / 2); // Y-axis -> Z-axis + tail.translate(0, 0, -r * 0.1); + parts.push(tail); + const N = 6; + for (let i = 0; i < N; i++) { + const a = (i / N) * Math.PI * 2; + const leg = new THREE.CylinderGeometry(r * 0.1, r * 0.06, r * 1.0, 4); // thick, tapered + leg.translate(0, r * 0.5, 0); // base at origin, extends +Y + // splay WIDE (small z) so a head-on phage reads as a 6-point star, not a dot + const dir = new THREE.Vector3(Math.cos(a), Math.sin(a), 0.55).normalize(); + leg.applyQuaternion(new THREE.Quaternion().setFromUnitVectors(_up, dir)); + leg.translate(0, 0, r * 0.35); // shift to the +Z baseplate + parts.push(leg); + } + // Icosahedron is non-indexed, cylinders are indexed — mergeGeometries needs all-or-none, so + // drop every index before merging. + return mergeGeometries(parts.map((p) => (p.index ? p.toNonIndexed() : p)), false); +} + // Round-1 roster. `geo` is the procedural fallback; D's model swaps in via assets.get. const ARCHETYPES = { floater: { @@ -54,12 +86,12 @@ const ARCHETYPES = { color: EMISSIVE.hostile, }, // phage — the ALLY. A friendly seeker that hunts the nearest FOE and rams it (a bacteriophage - // bursts its host). Teal, distinct from cyan flora. Budded by high BIOME STANDING. Fallback - // silhouette is a bare icosahedron = the phage's real icosahedral capsid (a Hunyuan mesh with - // tail fibres is the eventual upgrade). + // bursts its host). Teal, distinct from cyan flora. Budded by high BIOME STANDING. Silhouette + // is the iconic capsid+tail+legs, built procedurally (phageGeo) — image->3D can't hold the + // thin legs, and the shape is geometric anyway. phage: { cfg: B.enemies.phage, - geo: () => new THREE.IcosahedronGeometry(B.enemies.phage.radius, 0), + geo: () => phageGeo(B.enemies.phage.radius), color: EMISSIVE.ally, foe: false, },