@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 +11 -1
- package/dist/world.d.ts +9 -1
- package/dist/world.js +41 -2
- package/package.json +2 -2
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
|
|
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
|
-
|
|
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
|
|
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,
|
|
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.
|
|
4
|
-
"description": "
|
|
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",
|