@chromatic-coherence/generative-engine 1.6.0 → 1.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -44,6 +44,11 @@ w.start();
44
44
  | **Group transforms** | `w.setTransform(group, { translate, rotateY, scale })` — geometry moves rigidly in the vertex stage, zero CPU re-tessellation, shadow pass included. |
45
45
  | **Morph targets** | `w.meshMorph(geoA, geoB, opts)` + `w.morph(group, t)` — blend between two vertex sets in the shader (breathing, becoming). Eased like `fade`. |
46
46
  | **8 point lights** | Up from 4; `lights[0]` casts the shadows. HDR intensities welcome — bloom catches what exceeds 1.0. |
47
+ | **GPU crowds** | `meshInstanced` — a forest in one draw call; `sway` gives every instance its own wind, `flight: true` makes the batch a ten-thousand-bird murmuration — all evaluated in the vertex stage, zero CPU per frame. |
48
+ | **Real water** | `w.water` — the scene renders a second time through the reflected camera (flocks included) under a wave-displaced fresnel surface. Carve a basin, get a lake. |
49
+ | **Skeletal figures** | `humanoid()` / `quadruped()` / your own rig + `w.figure()` — bones grown from code, bent in the vertex stage, with `walkPose` / `trotPose` procedural cycles. |
50
+ | **Objectives** | `attachObjectives(w)` — give a scene a goal and it drives itself there; a live movement meter tells you what is moving and when it has settled. |
51
+ | **Cinema + flight** | Depth of field, camera motion blur, filmic grade — and `controls: "fly"`: drag to look, scroll to fly. |
47
52
  | **The carried light model** | Everything the chart edition learned rides along: GGX + Fresnel specular, procedural micro-relief, the perfusion term (`blood`, `bloodPulse`), pore noise, subsurface rim, coloured hemispheric ambient, fog — now resolving toward a real background colour the engine owns. |
48
53
 
49
54
  **The ink edition** (`theme: "light"`) still renders any recipe on paper instead of night —
@@ -143,9 +148,14 @@ Bones, grown from code like everything else — no asset pipeline. `humanoid()`
143
148
  in the vertex stage — scene, shadows and water reflections alike. `walkPose` / `trotPose`
144
149
  ship as procedural cycles; `composePose` takes any per-joint rotations you write.
145
150
 
151
+ ## Fly controls (v1.7)
152
+
153
+ `controls: "fly"` — free flight: drag to look, wheel to fly along your gaze, W/A/S/D to
154
+ move (shift for speed), `w.flySpeed` to tune. The pitch clamp opens to near-vertical.
155
+
146
156
  ## Roadmap
147
157
 
148
- The engine's four founding frontiers are landed. What comes next, the scenes will ask for. The chart editions stay lean.
158
+ The engine's founding frontiers are landed. What comes next, the scenes will ask for. The chart editions stay lean.
149
159
 
150
160
  ## Licence
151
161
 
package/dist/world.d.ts CHANGED
@@ -52,7 +52,9 @@ export type WorldOpts = {
52
52
  maxDpr?: number;
53
53
  drift?: number;
54
54
  parallax?: number;
55
- controls?: boolean;
55
+ /** true = orbit (drag + ctrl-wheel zoom). "fly" (W13) = free flight: drag to look,
56
+ * wheel to fly forward/back, W/A/S/D to move, shift for speed. */
57
+ controls?: boolean | "fly";
56
58
  /** shadow maps — lights[0] casts. true, or { size (px, default 2048), softness, bias } */
57
59
  shadows?: boolean | {
58
60
  size?: number;
@@ -84,6 +86,12 @@ export declare class World {
84
86
  private overlay;
85
87
  private og;
86
88
  private opts;
89
+ private flyMode;
90
+ private pitchMin;
91
+ private pitchMax;
92
+ private keys;
93
+ /** W13 fly speed, units/second (shift = 3x). */
94
+ flySpeed: number;
87
95
  private pFace;
88
96
  private pLine;
89
97
  private pPt;
package/dist/world.js CHANGED
@@ -34,6 +34,12 @@ export class World {
34
34
  this.groupNM.set(normalMat3(mat), i * 9);
35
35
  }
36
36
  constructor(o) {
37
+ this.flyMode = false;
38
+ this.pitchMin = 0.18;
39
+ this.pitchMax = 0.95;
40
+ this.keys = new Set();
41
+ /** W13 fly speed, units/second (shift = 3x). */
42
+ this.flySpeed = 420;
37
43
  this.instanced = [];
38
44
  /** W9 — the one wind that blows through every swaying instanced batch */
39
45
  this.wind = { dir: v3(1, 0, 0.35), speed: 1 };
@@ -159,8 +165,13 @@ export class World {
159
165
  maxDpr: o.maxDpr ?? 2,
160
166
  drift: o.drift ?? 0.045,
161
167
  parallax: o.parallax ?? 0.3,
162
- controls: o.controls ?? false,
168
+ controls: !!o.controls,
163
169
  };
170
+ this.flyMode = o.controls === "fly";
171
+ if (this.flyMode) {
172
+ this.pitchMin = -1.15;
173
+ this.pitchMax = 1.15;
174
+ }
164
175
  if (o.camera) {
165
176
  if (o.camera.target)
166
177
  this.cam.target = o.camera.target;
@@ -264,11 +275,26 @@ export class World {
264
275
  };
265
276
  const upFn = () => { dragging = false; cv.style.cursor = "grab"; };
266
277
  const wheel = (e) => {
278
+ if (this.flyMode) {
279
+ e.preventDefault();
280
+ // fly: the wheel moves the camera along its own gaze
281
+ const d = -e.deltaY * this.flySpeed * 0.004;
282
+ this.cam.target = add(this.cam.target, scale(this.fw, d));
283
+ return;
284
+ }
267
285
  if (!e.ctrlKey && !e.metaKey)
268
286
  return;
269
287
  e.preventDefault();
270
288
  this.cam.distT = clamp(this.cam.distT + e.deltaY * 0.6, 120, 2200);
271
289
  };
290
+ const kdown = (e) => { if (this.flyMode)
291
+ this.keys.add(e.key.toLowerCase()); };
292
+ const kup = (e) => { this.keys.delete(e.key.toLowerCase()); };
293
+ if (this.flyMode) {
294
+ window.addEventListener("keydown", kdown);
295
+ window.addEventListener("keyup", kup);
296
+ this.cleanup.push(() => { window.removeEventListener("keydown", kdown); window.removeEventListener("keyup", kup); });
297
+ }
272
298
  cv.addEventListener("pointerdown", down);
273
299
  cv.addEventListener("pointermove", move);
274
300
  cv.addEventListener("pointerup", upFn);
@@ -462,13 +488,26 @@ export class World {
462
488
  overlay2d(cb) { cb(this.og, this); }
463
489
  // ── camera + CPU projection for pick/labels ──
464
490
  setupCamera(t, dt) {
491
+ if (this.flyMode && this.keys.size && dt > 0) { // W13: keys fly the camera
492
+ const sp = this.flySpeed * (this.keys.has("shift") ? 3 : 1) * dt;
493
+ let m = v3();
494
+ if (this.keys.has("w"))
495
+ m = add(m, scale(this.fw, sp));
496
+ if (this.keys.has("s"))
497
+ m = add(m, scale(this.fw, -sp));
498
+ if (this.keys.has("a"))
499
+ m = add(m, scale(this.rt, -sp));
500
+ if (this.keys.has("d"))
501
+ m = add(m, scale(this.rt, sp));
502
+ this.cam.target = add(this.cam.target, m);
503
+ }
465
504
  this.mx += (this.mxT - this.mx) * Math.min(1, dt * 3);
466
505
  this.my += (this.myT - this.my) * Math.min(1, dt * 3);
467
506
  this.cam.yawUser += (this.yawUserT - this.cam.yawUser) * Math.min(1, dt * 5);
468
507
  this.cam.pitchUser += (this.pitchUserT - this.cam.pitchUser) * Math.min(1, dt * 5);
469
508
  this.cam.dist += (this.cam.distT - this.cam.dist) * Math.min(1, dt * 4);
470
509
  const yaw = this.cam.yawBase + t * this.opts.drift + this.cam.yawUser + this.mx * this.opts.parallax;
471
- const pitch = clamp(this.cam.pitch + this.cam.pitchUser + this.my * this.opts.parallax * 0.5, 0.18, 0.95);
510
+ const pitch = clamp(this.cam.pitch + this.cam.pitchUser + this.my * this.opts.parallax * 0.5, this.pitchMin, this.pitchMax);
472
511
  const cp = Math.cos(pitch), sp = Math.sin(pitch);
473
512
  this.cx = add(this.cam.target, scale(v3(cp * Math.cos(yaw), sp, cp * Math.sin(yaw)), this.cam.dist));
474
513
  this.fw = norm(sub(this.cam.target, this.cx));
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@chromatic-coherence/generative-engine",
3
- "version": "1.6.0",
4
- "description": "THE THIRD ENGINE — a zero-dependency WebGL2 graphics engine grown from generative-charts-3d: HDR + bloom + SSAO post chain, hardware-PCF shadow maps, a geometry stdlib, ribbon strokes, group transforms and morph targets. The charts API rides on top unchanged.",
3
+ "version": "1.7.1",
4
+ "description": "A zero-dependency WebGL2 graphics engine where everything is grown from code — no models, no textures, no asset pipeline. Real light and shadow with a cinematic HDR finish (bloom, SSAO, filmic grade, depth of field, motion blur), a geometry stdlib, GPU crowds (instanced wind, ten-thousand-bird flocks), real water with planar reflections, skeletal figures with procedural walk cycles, free-flight controls, and scenes that drive themselves toward objectives. The Generative Charts API rides on top unchanged.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
7
7
  "module": "./dist/index.js",