@chromatic-coherence/generative-engine 1.3.0 → 1.4.0
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 +8 -0
- package/dist/shaders.d.ts +2 -2
- package/dist/shaders.js +62 -8
- package/dist/world.d.ts +6 -0
- package/dist/world.js +15 -5
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -119,6 +119,14 @@ individually in the vertex stage (shadow pass included): per-instance scattered
|
|
|
119
119
|
height-scaled displacement, a second harmonic so the motion breathes. `w.wind = { dir,
|
|
120
120
|
speed }` is the one wind that blows through every swaying batch. Zero CPU per frame.
|
|
121
121
|
|
|
122
|
+
## GPU flight (v1.4)
|
|
123
|
+
|
|
124
|
+
`meshInstanced(wingMesh, Array.from({ length: 10000 }, () => ({})), { flight: true })` —
|
|
125
|
+
the batch IS a murmuration: per-bird orbit parameters ride the instance buffer, and the
|
|
126
|
+
whole flock (a wandering heart, lobes that split and merge, a ripple phase, flapping
|
|
127
|
+
wings) is evaluated in the vertex stage from time alone. Ten thousand birds, zero CPU.
|
|
128
|
+
The instance matrices are the shared flock frame — place, scale and steer it whole.
|
|
129
|
+
|
|
122
130
|
## Roadmap
|
|
123
131
|
|
|
124
132
|
Planar mirrors · real water · skeletal figures. The chart editions stay lean.
|
package/dist/shaders.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export declare const FACE_VS = "#version 300 es\nin vec3 aPos; in vec3 aNorm; in vec4 aCol; in float aBlood; in float aGrp;\nin vec3 aPos2; in vec3 aNorm2;\nuniform mat4 uVP; uniform mat4 uSVP;\nuniform mat4 uGM[8]; uniform mat3 uNM[8]; uniform float uMW[8]; uniform float uGA[8];\nout vec3 vPos; out vec3 vNorm; out vec4 vCol; out float vZ; out vec4 vSh;\nout float vGA; out float vBlood;\nvoid main(){\n int g = int(aGrp + 0.5);\n float w = uMW[g];\n vec3 p = mix(aPos, aPos2, w), n = mix(aNorm, aNorm2, w);\n vec4 wp = uGM[g] * vec4(p, 1.0);\n vPos = wp.xyz; vNorm = uNM[g] * n; vCol = aCol; vGA = uGA[g]; vBlood = aBlood;\n vec4 cp = uVP * wp; vZ = cp.w; vSh = uSVP * wp; gl_Position = cp;\n}";
|
|
2
2
|
export declare const FACE_FS = "#version 300 es\nprecision highp float;\nin vec3 vPos; in vec3 vNorm; in vec4 vCol; in float vZ; in vec4 vSh;\nin float vGA; in float vBlood;\nuniform vec3 uLdir; uniform float uAmb; uniform float uDif; uniform float uSigned;\nuniform vec3 uLp[8]; uniform float uLi[8]; uniform float uLf[8]; uniform int uLn;\nuniform vec3 uCam; uniform float uFogK; uniform float uSpec; uniform vec3 uBg;\nuniform vec3 uSkyC; uniform vec3 uGroundC; uniform float uPulse;\nuniform float uShadowOn; uniform float uShadowSoft; uniform float uShadowBias;\nuniform lowp sampler2DShadow uShadow; uniform float uShadowPx;\nout vec4 fragColor;\nfloat h3(vec3 p){ return fract(sin(dot(p, vec3(12.9898, 78.233, 37.719))) * 43758.5453); }\nfloat vnoise(vec3 p){ vec3 i = floor(p), f = fract(p); f = f * f * (3.0 - 2.0 * f);\n return mix(mix(mix(h3(i), h3(i + vec3(1,0,0)), f.x), mix(h3(i + vec3(0,1,0)), h3(i + vec3(1,1,0)), f.x), f.y),\n mix(mix(h3(i + vec3(0,0,1)), h3(i + vec3(1,0,1)), f.x), mix(h3(i + vec3(0,1,1)), h3(i + vec3(1,1,1)), f.x), f.y), f.z); }\nfloat shadowFactor(){\n vec3 pr = vSh.xyz / vSh.w * 0.5 + 0.5;\n if (pr.x < 0.0 || pr.x > 1.0 || pr.y < 0.0 || pr.y > 1.0 || pr.z > 1.0) return 1.0;\n float ref = pr.z - uShadowBias;\n // 3x3 taps over a hardware-compared (LINEAR sampler2DShadow => free 2x2 PCF each) map\n float s = 0.0;\n for (int sy = -1; sy <= 1; sy++)\n for (int sx = -1; sx <= 1; sx++)\n s += texture(uShadow, vec3(pr.xy + vec2(float(sx), float(sy)) * uShadowPx * uShadowSoft, ref));\n s /= 9.0;\n return 0.35 + 0.65 * s;\n}\nvoid main(){\n vec3 n = normalize(vNorm);\n vec3 V = normalize(uCam - vPos);\n float skin = clamp(vBlood * 5.0, 0.0, 1.0);\n { // procedural micro-relief: perturb the shading normal by a fine noise gradient\n float bs = 5.5, e = 0.12, n0 = vnoise(vPos * bs);\n vec3 g = vec3(vnoise(vPos * bs + vec3(e, 0.0, 0.0)) - n0,\n vnoise(vPos * bs + vec3(0.0, e, 0.0)) - n0,\n vnoise(vPos * bs + vec3(0.0, 0.0, e)) - n0) / e;\n n = normalize(n - g * (0.10 + 0.30 * skin));\n }\n float d = dot(n, normalize(uLdir));\n float shade = uAmb + uDif * mix(abs(d), max(d, 0.0), uSigned);\n float spec = 0.0;\n float sh = uShadowOn > 0.5 ? shadowFactor() : 1.0;\n for (int i = 0; i < 8; i++) { if (i >= uLn) break;\n vec3 v = uLp[i] - vPos; float d2 = dot(v, v); vec3 L = v * inversesqrt(d2 + 1e-4);\n float lam = dot(n, L); lam = mix(abs(lam), max(lam, 0.0), uSigned);\n float fall = 1.0 / (1.0 + d2 / (uLf[i] * uLf[i]));\n float k = (i == 0) ? sh : 1.0;\n shade += uLi[i] * lam * fall * k;\n // GGX microfacet + Fresnel \u2014 roughness from the face's gloss (colour alpha slot)\n vec3 H = normalize(L + V);\n float nh = max(dot(n, H), 0.0);\n float rough = clamp(1.0 - vCol.a, 0.08, 1.0), a2 = rough * rough; a2 = a2 * a2;\n float dnm = nh * nh * (a2 - 1.0) + 1.0;\n float ggx = a2 / (3.14159 * dnm * dnm);\n float fres = 0.04 + 0.96 * pow(1.0 - max(dot(H, V), 0.0), 5.0);\n spec += uLi[i] * min(ggx, 6.0) * fres * fall * k * 0.16;\n }\n // coloured hemispheric ambient: cool from above, warm from below\n vec3 tint = mix(uGroundC, uSkyC, n.y * 0.5 + 0.5);\n vec3 lit = vCol.rgb * (uAmb * tint + vec3(shade - uAmb));\n // the perfusion term: blood near the surface changes how it meets the light\n float b = clamp(vBlood + uPulse * vBlood * 6.0, 0.0, 1.0);\n lit *= mix(vec3(1.0), vec3(1.07, 0.95, 0.94), b);\n lit += vec3(0.62, 0.06, 0.08) * (b * 0.20 * (1.0 - abs(d)));\n // pore noise, only where there is blood (skin, not cloth)\n float pore = vnoise(vPos * 2.4) * 0.6 + vnoise(vPos * 6.0) * 0.4;\n lit *= 1.0 + (pore - 0.5) * 0.09 * skin;\n // subsurface rim: grazing light bleeds through thin tissue, warm\n float fres = pow(1.0 - clamp(dot(n, V), 0.0, 1.0), 3.0);\n lit += vec3(0.55, 0.09, 0.10) * fres * skin * (0.28 + 0.5 * b);\n float fog = clamp(uFogK / vZ, 0.08, 1.0);\n vec3 outC = lit + vec3(1.0) * spec * uSpec * vCol.a;\n // fog and fades resolve toward the REAL background colour (the engine owns its ground)\n fragColor = vec4(mix(uBg, outC, fog * vGA), 1.0);\n}";
|
|
3
|
-
export declare const FACE_INST_VS = "#version 300 es\nin vec3 aPos; in vec3 aNorm; in vec4 aCol; in float aBlood; in float aGrp;\nin vec3 aPos2; in vec3 aNorm2;\nin vec4 aI0; in vec4 aI1; in vec4 aI2; in vec4 aI3; in vec4 aIP;\nuniform mat4 uVP; uniform mat4 uSVP; uniform float uT; uniform vec3 uWindDir;\nuniform mat4 uGM[8]; uniform mat3 uNM[8]; uniform float uMW[8]; uniform float uGA[8];\nout vec3 vPos; out vec3 vNorm; out vec4 vCol; out float vZ; out vec4 vSh;\nout float vGA; out float vBlood;\nvoid main(){\n int g = int(aGrp + 0.5);\n float w = uMW[g];\n mat4 inst = mat4(aI0, aI1, aI2, aI3);\n vec3 p = mix(aPos, aPos2, w), n = mix(aNorm, aNorm2, w);\n // W9 GPU WIND: per-instance (phase, freq, amp); displacement grows with height,\n
|
|
4
|
-
export declare const DEPTH_INST_VS = "#version 300 es\nin vec3 aPos; in float aGrp; in vec3 aPos2;\nin vec4 aI0; in vec4 aI1; in vec4 aI2; in vec4 aI3; in vec4 aIP;\nuniform mat4 uVP; uniform float uT; uniform vec3 uWindDir;\nuniform mat4 uGM[8]; uniform mat3 uNM[8]; uniform float uMW[8]; uniform float uGA[8];\nvoid main(){\n int g = int(aGrp + 0.5);\n vec3 p = mix(aPos, aPos2, uMW[g]);\n float sw = sin(uT * aIP.y + aIP.x) + 0.5 * sin(uT * aIP.y * 2.3 + aIP.x * 1.7);\n
|
|
3
|
+
export declare const FACE_INST_VS = "#version 300 es\nin vec3 aPos; in vec3 aNorm; in vec4 aCol; in float aBlood; in float aGrp;\nin vec3 aPos2; in vec3 aNorm2;\nin vec4 aI0; in vec4 aI1; in vec4 aI2; in vec4 aI3; in vec4 aIP;\nuniform mat4 uVP; uniform mat4 uSVP; uniform float uT; uniform vec3 uWindDir;\nuniform float uFlight;\nuniform mat4 uGM[8]; uniform mat3 uNM[8]; uniform float uMW[8]; uniform float uGA[8];\nout vec3 vPos; out vec3 vNorm; out vec4 vCol; out float vZ; out vec4 vSh;\nout float vGA; out float vBlood;\n// W10 FLIGHT: aIP = (a0, r0, ph, lobe). The flock's heart wanders, three lobes orbit\n// it breathing apart and back, each bird orbits its lobe with a ripple phase \u2014 the\n// murmuration, evaluated per vertex from uT alone. The instance matrix is the flock\n// frame (shared), so one batch = one flock you can place, scale and steer.\nvec3 flightPos(vec4 ip, float t){\n float a0 = ip.x, r0 = ip.y, ph = ip.z, lobe = ip.w;\n float cx = sin(t * 0.11) * 700.0, cz = -500.0 + cos(t * 0.07) * 380.0;\n float cy = 430.0 + sin(t * 0.16) * 110.0;\n float spread = 180.0 + 140.0 * sin(t * 0.21 + 1.3);\n float la = t * 0.34 + lobe * 2.0944;\n vec3 lc = vec3(cx + cos(la) * spread, cy + sin(la * 2.0 + lobe) * 60.0, cz + sin(la) * spread * 0.7);\n float aa = a0 + t * (0.5 + 0.25 * sin(ph));\n float wob = sin(t * 1.1 + ph) * 0.35;\n float rr = r0 * (0.75 + 0.25 * sin(t * 0.5 + ph + lobe));\n return lc + vec3(cos(aa) * rr, sin(aa * 1.7 + ph) * rr * 0.32 + wob * 24.0, sin(aa) * rr * (0.55 + wob * 0.2));\n}\nvec3 flightLocal(vec3 pl, vec4 ip, float t){\n float aa = ip.x + t * (0.5 + 0.25 * sin(ip.z));\n pl.y += sin(t * 11.0 + ip.z * 3.0) * abs(pl.z) * 0.9; // the flap, growing to the wingtips\n float ca = cos(aa + 1.5708), sa = sin(aa + 1.5708); // heading = the orbit tangent\n return vec3(pl.x * ca - pl.z * sa, pl.y, pl.x * sa + pl.z * ca);\n}\nvoid main(){\n int g = int(aGrp + 0.5);\n float w = uMW[g];\n mat4 inst = mat4(aI0, aI1, aI2, aI3);\n vec3 p = mix(aPos, aPos2, w), n = mix(aNorm, aNorm2, w);\n if (uFlight > 0.5) {\n p = flightPos(aIP, uT) + flightLocal(p, aIP, uT);\n } else {\n // W9 GPU WIND: per-instance (phase, freq, amp); displacement grows with height,\n // plus a second harmonic so the sway breathes instead of ticking\n float sw = sin(uT * aIP.y + aIP.x) + 0.5 * sin(uT * aIP.y * 2.3 + aIP.x * 1.7);\n p += uWindDir * (sw * aIP.z * max(p.y, 0.0));\n }\n vec4 wp = uGM[g] * (inst * vec4(p, 1.0));\n vPos = wp.xyz; vNorm = uNM[g] * (mat3(inst) * n); vCol = aCol; vGA = uGA[g]; vBlood = aBlood;\n vec4 cp = uVP * wp; vZ = cp.w; vSh = uSVP * wp; gl_Position = cp;\n}";
|
|
4
|
+
export declare const DEPTH_INST_VS = "#version 300 es\nin vec3 aPos; in float aGrp; in vec3 aPos2;\nin vec4 aI0; in vec4 aI1; in vec4 aI2; in vec4 aI3; in vec4 aIP;\nuniform mat4 uVP; uniform float uT; uniform vec3 uWindDir;\nuniform float uFlight;\nuniform mat4 uGM[8]; uniform mat3 uNM[8]; uniform float uMW[8]; uniform float uGA[8];\n// W10 FLIGHT: aIP = (a0, r0, ph, lobe). The flock's heart wanders, three lobes orbit\n// it breathing apart and back, each bird orbits its lobe with a ripple phase \u2014 the\n// murmuration, evaluated per vertex from uT alone. The instance matrix is the flock\n// frame (shared), so one batch = one flock you can place, scale and steer.\nvec3 flightPos(vec4 ip, float t){\n float a0 = ip.x, r0 = ip.y, ph = ip.z, lobe = ip.w;\n float cx = sin(t * 0.11) * 700.0, cz = -500.0 + cos(t * 0.07) * 380.0;\n float cy = 430.0 + sin(t * 0.16) * 110.0;\n float spread = 180.0 + 140.0 * sin(t * 0.21 + 1.3);\n float la = t * 0.34 + lobe * 2.0944;\n vec3 lc = vec3(cx + cos(la) * spread, cy + sin(la * 2.0 + lobe) * 60.0, cz + sin(la) * spread * 0.7);\n float aa = a0 + t * (0.5 + 0.25 * sin(ph));\n float wob = sin(t * 1.1 + ph) * 0.35;\n float rr = r0 * (0.75 + 0.25 * sin(t * 0.5 + ph + lobe));\n return lc + vec3(cos(aa) * rr, sin(aa * 1.7 + ph) * rr * 0.32 + wob * 24.0, sin(aa) * rr * (0.55 + wob * 0.2));\n}\nvec3 flightLocal(vec3 pl, vec4 ip, float t){\n float aa = ip.x + t * (0.5 + 0.25 * sin(ip.z));\n pl.y += sin(t * 11.0 + ip.z * 3.0) * abs(pl.z) * 0.9; // the flap, growing to the wingtips\n float ca = cos(aa + 1.5708), sa = sin(aa + 1.5708); // heading = the orbit tangent\n return vec3(pl.x * ca - pl.z * sa, pl.y, pl.x * sa + pl.z * ca);\n}\nvoid main(){\n int g = int(aGrp + 0.5);\n vec3 p = mix(aPos, aPos2, uMW[g]);\n if (uFlight > 0.5) {\n p = flightPos(aIP, uT) + flightLocal(p, aIP, uT);\n } else {\n float sw = sin(uT * aIP.y + aIP.x) + 0.5 * sin(uT * aIP.y * 2.3 + aIP.x * 1.7);\n p += uWindDir * (sw * aIP.z * max(p.y, 0.0));\n }\n gl_Position = uVP * (uGM[g] * (mat4(aI0, aI1, aI2, aI3) * vec4(p, 1.0)));\n}";
|
|
5
5
|
export declare const DEPTH_VS = "#version 300 es\nin vec3 aPos; in float aGrp; in vec3 aPos2;\nuniform mat4 uVP;\nuniform mat4 uGM[8]; uniform mat3 uNM[8]; uniform float uMW[8]; uniform float uGA[8];\nvoid main(){\n int g = int(aGrp + 0.5);\n vec3 p = mix(aPos, aPos2, uMW[g]);\n gl_Position = uVP * (uGM[g] * vec4(p, 1.0));\n}";
|
|
6
6
|
export declare const DEPTH_FS = "#version 300 es\nprecision mediump float;\nvoid main(){}";
|
|
7
7
|
export declare const LINE_VS = "#version 300 es\nin vec3 aPos; in vec4 aCol; in float aGrp;\nuniform mat4 uVP;\nuniform mat4 uGM[8]; uniform mat3 uNM[8]; uniform float uMW[8]; uniform float uGA[8];\nout vec4 vCol; out float vZ; out float vGA;\nvoid main(){\n int g = int(aGrp + 0.5);\n vCol = aCol; vGA = uGA[g];\n vec4 p = uVP * (uGM[g] * vec4(aPos, 1.0)); vZ = p.w; gl_Position = p;\n}";
|
package/dist/shaders.js
CHANGED
|
@@ -105,18 +105,45 @@ export const FACE_INST_VS = `#version 300 es
|
|
|
105
105
|
in vec3 aPos; in vec3 aNorm; in vec4 aCol; in float aBlood; in float aGrp;
|
|
106
106
|
in vec3 aPos2; in vec3 aNorm2;
|
|
107
107
|
in vec4 aI0; in vec4 aI1; in vec4 aI2; in vec4 aI3; in vec4 aIP;
|
|
108
|
-
uniform mat4 uVP; uniform mat4 uSVP; uniform float uT; uniform vec3 uWindDir
|
|
108
|
+
uniform mat4 uVP; uniform mat4 uSVP; uniform float uT; uniform vec3 uWindDir;
|
|
109
|
+
uniform float uFlight;${GROUPS}
|
|
109
110
|
out vec3 vPos; out vec3 vNorm; out vec4 vCol; out float vZ; out vec4 vSh;
|
|
110
111
|
out float vGA; out float vBlood;
|
|
112
|
+
// W10 FLIGHT: aIP = (a0, r0, ph, lobe). The flock's heart wanders, three lobes orbit
|
|
113
|
+
// it breathing apart and back, each bird orbits its lobe with a ripple phase — the
|
|
114
|
+
// murmuration, evaluated per vertex from uT alone. The instance matrix is the flock
|
|
115
|
+
// frame (shared), so one batch = one flock you can place, scale and steer.
|
|
116
|
+
vec3 flightPos(vec4 ip, float t){
|
|
117
|
+
float a0 = ip.x, r0 = ip.y, ph = ip.z, lobe = ip.w;
|
|
118
|
+
float cx = sin(t * 0.11) * 700.0, cz = -500.0 + cos(t * 0.07) * 380.0;
|
|
119
|
+
float cy = 430.0 + sin(t * 0.16) * 110.0;
|
|
120
|
+
float spread = 180.0 + 140.0 * sin(t * 0.21 + 1.3);
|
|
121
|
+
float la = t * 0.34 + lobe * 2.0944;
|
|
122
|
+
vec3 lc = vec3(cx + cos(la) * spread, cy + sin(la * 2.0 + lobe) * 60.0, cz + sin(la) * spread * 0.7);
|
|
123
|
+
float aa = a0 + t * (0.5 + 0.25 * sin(ph));
|
|
124
|
+
float wob = sin(t * 1.1 + ph) * 0.35;
|
|
125
|
+
float rr = r0 * (0.75 + 0.25 * sin(t * 0.5 + ph + lobe));
|
|
126
|
+
return lc + vec3(cos(aa) * rr, sin(aa * 1.7 + ph) * rr * 0.32 + wob * 24.0, sin(aa) * rr * (0.55 + wob * 0.2));
|
|
127
|
+
}
|
|
128
|
+
vec3 flightLocal(vec3 pl, vec4 ip, float t){
|
|
129
|
+
float aa = ip.x + t * (0.5 + 0.25 * sin(ip.z));
|
|
130
|
+
pl.y += sin(t * 11.0 + ip.z * 3.0) * abs(pl.z) * 0.9; // the flap, growing to the wingtips
|
|
131
|
+
float ca = cos(aa + 1.5708), sa = sin(aa + 1.5708); // heading = the orbit tangent
|
|
132
|
+
return vec3(pl.x * ca - pl.z * sa, pl.y, pl.x * sa + pl.z * ca);
|
|
133
|
+
}
|
|
111
134
|
void main(){
|
|
112
135
|
int g = int(aGrp + 0.5);
|
|
113
136
|
float w = uMW[g];
|
|
114
137
|
mat4 inst = mat4(aI0, aI1, aI2, aI3);
|
|
115
138
|
vec3 p = mix(aPos, aPos2, w), n = mix(aNorm, aNorm2, w);
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
139
|
+
if (uFlight > 0.5) {
|
|
140
|
+
p = flightPos(aIP, uT) + flightLocal(p, aIP, uT);
|
|
141
|
+
} else {
|
|
142
|
+
// W9 GPU WIND: per-instance (phase, freq, amp); displacement grows with height,
|
|
143
|
+
// plus a second harmonic so the sway breathes instead of ticking
|
|
144
|
+
float sw = sin(uT * aIP.y + aIP.x) + 0.5 * sin(uT * aIP.y * 2.3 + aIP.x * 1.7);
|
|
145
|
+
p += uWindDir * (sw * aIP.z * max(p.y, 0.0));
|
|
146
|
+
}
|
|
120
147
|
vec4 wp = uGM[g] * (inst * vec4(p, 1.0));
|
|
121
148
|
vPos = wp.xyz; vNorm = uNM[g] * (mat3(inst) * n); vCol = aCol; vGA = uGA[g]; vBlood = aBlood;
|
|
122
149
|
vec4 cp = uVP * wp; vZ = cp.w; vSh = uSVP * wp; gl_Position = cp;
|
|
@@ -124,12 +151,39 @@ void main(){
|
|
|
124
151
|
export const DEPTH_INST_VS = `#version 300 es
|
|
125
152
|
in vec3 aPos; in float aGrp; in vec3 aPos2;
|
|
126
153
|
in vec4 aI0; in vec4 aI1; in vec4 aI2; in vec4 aI3; in vec4 aIP;
|
|
127
|
-
uniform mat4 uVP; uniform float uT; uniform vec3 uWindDir
|
|
154
|
+
uniform mat4 uVP; uniform float uT; uniform vec3 uWindDir;
|
|
155
|
+
uniform float uFlight;${GROUPS}
|
|
156
|
+
// W10 FLIGHT: aIP = (a0, r0, ph, lobe). The flock's heart wanders, three lobes orbit
|
|
157
|
+
// it breathing apart and back, each bird orbits its lobe with a ripple phase — the
|
|
158
|
+
// murmuration, evaluated per vertex from uT alone. The instance matrix is the flock
|
|
159
|
+
// frame (shared), so one batch = one flock you can place, scale and steer.
|
|
160
|
+
vec3 flightPos(vec4 ip, float t){
|
|
161
|
+
float a0 = ip.x, r0 = ip.y, ph = ip.z, lobe = ip.w;
|
|
162
|
+
float cx = sin(t * 0.11) * 700.0, cz = -500.0 + cos(t * 0.07) * 380.0;
|
|
163
|
+
float cy = 430.0 + sin(t * 0.16) * 110.0;
|
|
164
|
+
float spread = 180.0 + 140.0 * sin(t * 0.21 + 1.3);
|
|
165
|
+
float la = t * 0.34 + lobe * 2.0944;
|
|
166
|
+
vec3 lc = vec3(cx + cos(la) * spread, cy + sin(la * 2.0 + lobe) * 60.0, cz + sin(la) * spread * 0.7);
|
|
167
|
+
float aa = a0 + t * (0.5 + 0.25 * sin(ph));
|
|
168
|
+
float wob = sin(t * 1.1 + ph) * 0.35;
|
|
169
|
+
float rr = r0 * (0.75 + 0.25 * sin(t * 0.5 + ph + lobe));
|
|
170
|
+
return lc + vec3(cos(aa) * rr, sin(aa * 1.7 + ph) * rr * 0.32 + wob * 24.0, sin(aa) * rr * (0.55 + wob * 0.2));
|
|
171
|
+
}
|
|
172
|
+
vec3 flightLocal(vec3 pl, vec4 ip, float t){
|
|
173
|
+
float aa = ip.x + t * (0.5 + 0.25 * sin(ip.z));
|
|
174
|
+
pl.y += sin(t * 11.0 + ip.z * 3.0) * abs(pl.z) * 0.9; // the flap, growing to the wingtips
|
|
175
|
+
float ca = cos(aa + 1.5708), sa = sin(aa + 1.5708); // heading = the orbit tangent
|
|
176
|
+
return vec3(pl.x * ca - pl.z * sa, pl.y, pl.x * sa + pl.z * ca);
|
|
177
|
+
}
|
|
128
178
|
void main(){
|
|
129
179
|
int g = int(aGrp + 0.5);
|
|
130
180
|
vec3 p = mix(aPos, aPos2, uMW[g]);
|
|
131
|
-
|
|
132
|
-
|
|
181
|
+
if (uFlight > 0.5) {
|
|
182
|
+
p = flightPos(aIP, uT) + flightLocal(p, aIP, uT);
|
|
183
|
+
} else {
|
|
184
|
+
float sw = sin(uT * aIP.y + aIP.x) + 0.5 * sin(uT * aIP.y * 2.3 + aIP.x * 1.7);
|
|
185
|
+
p += uWindDir * (sw * aIP.z * max(p.y, 0.0));
|
|
186
|
+
}
|
|
133
187
|
gl_Position = uVP * (uGM[g] * (mat4(aI0, aI1, aI2, aI3) * vec4(p, 1.0)));
|
|
134
188
|
}`;
|
|
135
189
|
export const DEPTH_VS = `#version 300 es
|
package/dist/world.d.ts
CHANGED
|
@@ -255,6 +255,12 @@ export declare class World {
|
|
|
255
255
|
amp: number;
|
|
256
256
|
freq?: number;
|
|
257
257
|
};
|
|
258
|
+
/** W10 GPU flight: the batch IS a murmuration — per-bird orbit params generated
|
|
259
|
+
* here, positions evaluated in the vertex stage from time alone. The instance
|
|
260
|
+
* matrices become the shared flock frame (place / scale / steer the whole flock). */
|
|
261
|
+
flight?: boolean | {
|
|
262
|
+
lobes?: number;
|
|
263
|
+
};
|
|
258
264
|
}): void;
|
|
259
265
|
grow(fn: GrowFn): void;
|
|
260
266
|
drawLine(a: V3, b: V3, color: string, alpha: number, width?: number): void;
|
package/dist/world.js
CHANGED
|
@@ -395,13 +395,22 @@ export class World {
|
|
|
395
395
|
this.pushFace(store, v3(P[i], P[i + 1], P[i + 2]), v3(P[i + 3], P[i + 4], P[i + 5]), v3(P[i + 6], P[i + 7], P[i + 8]), o, v3(N[i], N[i + 1], N[i + 2]), v3(N[i + 3], N[i + 4], N[i + 5]), v3(N[i + 6], N[i + 7], N[i + 8]));
|
|
396
396
|
}
|
|
397
397
|
const mats = new Float32Array(instances.length * 20); // mat4 + sway vec4, one stride
|
|
398
|
+
const lobes = typeof o.flight === "object" ? (o.flight.lobes ?? 3) : 3;
|
|
398
399
|
instances.forEach((m, i) => {
|
|
399
400
|
mats.set(m instanceof Float32Array ? m : mCompose(m), i * 20);
|
|
400
401
|
const ph = ((i * 2654435761) % 6283) / 1000; // scattered phase
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
402
|
+
if (o.flight) { // aIP = (a0, r0, ph, lobe)
|
|
403
|
+
mats[i * 20 + 16] = ((i * 40503) % 6283) / 1000;
|
|
404
|
+
mats[i * 20 + 17] = 40 + ((i * 9973) % 1000) / 5;
|
|
405
|
+
mats[i * 20 + 18] = ph;
|
|
406
|
+
mats[i * 20 + 19] = i % lobes;
|
|
407
|
+
}
|
|
408
|
+
else {
|
|
409
|
+
mats[i * 20 + 16] = ph;
|
|
410
|
+
mats[i * 20 + 17] = (o.sway?.freq ?? 1.3) * (0.85 + ((i * 97) % 30) / 100);
|
|
411
|
+
mats[i * 20 + 18] = (o.sway?.amp ?? 0) * 0.01;
|
|
412
|
+
mats[i * 20 + 19] = 0;
|
|
413
|
+
}
|
|
405
414
|
});
|
|
406
415
|
const vbuf = gl.createBuffer();
|
|
407
416
|
gl.bindBuffer(gl.ARRAY_BUFFER, vbuf);
|
|
@@ -409,7 +418,7 @@ export class World {
|
|
|
409
418
|
const ibuf = gl.createBuffer();
|
|
410
419
|
gl.bindBuffer(gl.ARRAY_BUFFER, ibuf);
|
|
411
420
|
gl.bufferData(gl.ARRAY_BUFFER, mats, gl.STATIC_DRAW);
|
|
412
|
-
this.instanced.push({ vbuf, ibuf, verts: store.length / FACE_STRIDE, count: instances.length });
|
|
421
|
+
this.instanced.push({ vbuf, ibuf, verts: store.length / FACE_STRIDE, count: instances.length, flight: !!o.flight });
|
|
413
422
|
}
|
|
414
423
|
grow(fn) { this.hooks.push(fn); }
|
|
415
424
|
// ── the living verbs (gathered per frame) ──
|
|
@@ -611,6 +620,7 @@ export class World {
|
|
|
611
620
|
return;
|
|
612
621
|
const gl = this.gl;
|
|
613
622
|
for (const rec of this.instanced) {
|
|
623
|
+
gl.uniform1f(p.u("uFlight"), rec.flight ? 1 : 0);
|
|
614
624
|
gl.bindBuffer(gl.ARRAY_BUFFER, rec.vbuf);
|
|
615
625
|
this.attrib(p, "aPos", 3, FACE_STRIDE, 0);
|
|
616
626
|
if (!depthOnly) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@chromatic-coherence/generative-engine",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.0",
|
|
4
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.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|