vireuikit 0.3.0 → 0.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 +27 -7
- package/dist/{chunk-2AFVQ65S.js → chunk-VY22O2II.js} +324 -33
- package/dist/chunk-VY22O2II.js.map +1 -0
- package/dist/{dynamics-7Bq3LLds.d.cts → dynamics-DqGk_f1n.d.cts} +157 -13
- package/dist/{dynamics-7Bq3LLds.d.ts → dynamics-DqGk_f1n.d.ts} +157 -13
- package/dist/index.cjs +291 -30
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +19 -4
- package/dist/index.d.ts +19 -4
- package/dist/index.js +1 -1
- package/dist/web/index.cjs +693 -9
- package/dist/web/index.cjs.map +1 -1
- package/dist/web/index.d.cts +63 -2
- package/dist/web/index.d.ts +63 -2
- package/dist/web/index.js +449 -7
- package/dist/web/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-2AFVQ65S.js.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -274,9 +274,46 @@ function neutralSpecies(baseColor) {
|
|
|
274
274
|
}
|
|
275
275
|
|
|
276
276
|
// src/medium/params.ts
|
|
277
|
+
var MEDIUM_MAX_LIGHTS = 3;
|
|
277
278
|
var BASE_COLOR = [0.07, 0.08, 0.1];
|
|
279
|
+
var normalize2 = (v) => {
|
|
280
|
+
const len = Math.hypot(v[0], v[1]) || 1;
|
|
281
|
+
return [v[0] / len, v[1] / len];
|
|
282
|
+
};
|
|
283
|
+
var MEDIUM_DEFAULT_LIGHT_COLOR = [0.78, 0.85, 0.94];
|
|
284
|
+
var MEDIUM_DEFAULT_LIGHTS = [
|
|
285
|
+
{
|
|
286
|
+
position: [0.5, 0],
|
|
287
|
+
direction: [0, 1],
|
|
288
|
+
coneAngle: Math.PI,
|
|
289
|
+
falloff: 16,
|
|
290
|
+
color: MEDIUM_DEFAULT_LIGHT_COLOR,
|
|
291
|
+
intensity: 0.28
|
|
292
|
+
},
|
|
293
|
+
{
|
|
294
|
+
position: [0.12, 1.45],
|
|
295
|
+
direction: normalize2([0.38, -1.03]),
|
|
296
|
+
coneAngle: 9 * Math.PI / 180,
|
|
297
|
+
falloff: 0.7,
|
|
298
|
+
color: MEDIUM_DEFAULT_LIGHT_COLOR,
|
|
299
|
+
intensity: 1.1
|
|
300
|
+
},
|
|
301
|
+
{
|
|
302
|
+
position: [0.9, 1.4],
|
|
303
|
+
direction: normalize2([-0.4, -0.98]),
|
|
304
|
+
coneAngle: 8 * Math.PI / 180,
|
|
305
|
+
falloff: 0.75,
|
|
306
|
+
color: MEDIUM_DEFAULT_LIGHT_COLOR,
|
|
307
|
+
intensity: 0.9
|
|
308
|
+
}
|
|
309
|
+
];
|
|
278
310
|
var MEDIUM_DEFAULTS = {
|
|
279
|
-
|
|
311
|
+
// A stamped track has to visibly broaden and fade within the reference's "roughly one to two
|
|
312
|
+
// seconds", not the ~11s half-life a 0.06 decay gives — measured against check-medium.mjs's track
|
|
313
|
+
// gate (birth vs +1.5s cross-section width and total density): 1.1 leaves ~30% of the peak at 1s
|
|
314
|
+
// and ~17% at 1.5s, comfortably faded without dying before MacCormack's own diffusion has time to
|
|
315
|
+
// visibly broaden the stamp.
|
|
316
|
+
decay: 1.1,
|
|
280
317
|
curlFreq: 0.045,
|
|
281
318
|
curlSpeed: 0.12,
|
|
282
319
|
advectSpeed: 26,
|
|
@@ -291,6 +328,7 @@ var MEDIUM_DEFAULTS = {
|
|
|
291
328
|
condensateGain: 5,
|
|
292
329
|
condensateColor: [0.94, 0.95, 0.97],
|
|
293
330
|
channelColors: neutralSpecies(BASE_COLOR),
|
|
331
|
+
channelScatter: [1, 1, 1],
|
|
294
332
|
baseColor: BASE_COLOR,
|
|
295
333
|
baseWeight: 0.9,
|
|
296
334
|
depthFarScale: 1.6,
|
|
@@ -299,7 +337,11 @@ var MEDIUM_DEFAULTS = {
|
|
|
299
337
|
depthFarWeight: 0.5,
|
|
300
338
|
gravityVaporDrift: 0.6,
|
|
301
339
|
gravityBottomBoost: 2,
|
|
302
|
-
gravityBottomBoostStart: 0.75
|
|
340
|
+
gravityBottomBoostStart: 0.75,
|
|
341
|
+
lights: MEDIUM_DEFAULT_LIGHTS,
|
|
342
|
+
shadowExtinction: 3,
|
|
343
|
+
trackLightFloor: 0.8,
|
|
344
|
+
trackLayerAmount: 1
|
|
303
345
|
};
|
|
304
346
|
function depthFarOffsetPx(offsetFrac, gridWidth, gridHeight) {
|
|
305
347
|
return [offsetFrac[0] * gridWidth, offsetFrac[1] * gridHeight];
|
|
@@ -321,7 +363,7 @@ var MEDIUM_TRACK_PRESETS = {
|
|
|
321
363
|
tailDim: 0.55,
|
|
322
364
|
intensity: 1.15
|
|
323
365
|
},
|
|
324
|
-
|
|
366
|
+
electron: {
|
|
325
367
|
lengthFrac: 0.42,
|
|
326
368
|
headWidthFrac: 0.018,
|
|
327
369
|
tailWidthFrac: 0.05,
|
|
@@ -329,17 +371,17 @@ var MEDIUM_TRACK_PRESETS = {
|
|
|
329
371
|
raggedFreq: 5,
|
|
330
372
|
settleFrac: 0.05,
|
|
331
373
|
tailDim: 0.45,
|
|
332
|
-
intensity: 0.
|
|
374
|
+
intensity: 0.6
|
|
333
375
|
},
|
|
334
|
-
|
|
335
|
-
lengthFrac: 0.
|
|
336
|
-
headWidthFrac: 0.
|
|
337
|
-
tailWidthFrac: 0.
|
|
338
|
-
raggedFrac: 0
|
|
339
|
-
raggedFreq:
|
|
340
|
-
settleFrac: 0.
|
|
341
|
-
tailDim: 0.
|
|
342
|
-
intensity: 0.
|
|
376
|
+
muon: {
|
|
377
|
+
lengthFrac: 0.7,
|
|
378
|
+
headWidthFrac: 0.012,
|
|
379
|
+
tailWidthFrac: 0.02,
|
|
380
|
+
raggedFrac: 0,
|
|
381
|
+
raggedFreq: 0,
|
|
382
|
+
settleFrac: 0.01,
|
|
383
|
+
tailDim: 0.75,
|
|
384
|
+
intensity: 0.5
|
|
343
385
|
}
|
|
344
386
|
};
|
|
345
387
|
var MEDIUM_DRIFT_TURBULENCE = 0.045;
|
|
@@ -348,12 +390,60 @@ var MEDIUM_TURBULENCE_TAU = 2.5;
|
|
|
348
390
|
var MEDIUM_EMISSION_BEATS = 4;
|
|
349
391
|
var MEDIUM_EMISSION_PROBABILITY = 0.55;
|
|
350
392
|
var MEDIUM_MIN_EMISSION_INTERVAL = 1.2;
|
|
351
|
-
var MEDIUM_NATURAL_INTERVAL =
|
|
352
|
-
var MEDIUM_NATURAL_JITTER =
|
|
393
|
+
var MEDIUM_NATURAL_INTERVAL = 8;
|
|
394
|
+
var MEDIUM_NATURAL_JITTER = 12;
|
|
353
395
|
var MEDIUM_SENSITIVE_TOP = 0.55;
|
|
354
396
|
var MEDIUM_SENSITIVE_BOTTOM = 0.95;
|
|
355
|
-
var
|
|
397
|
+
var MEDIUM_ALPHA_BURST_RAYS = [8, 16];
|
|
398
|
+
var MEDIUM_ALPHA_RAY_LENGTH_SCALE = [0.55, 1];
|
|
399
|
+
var MEDIUM_TRACK_DEPTH_WIDTH_RANGE = [0.55, 1.2];
|
|
356
400
|
var MEDIUM_TRACK_DEPTH_INTENSITY_RANGE = [0.4, 1];
|
|
401
|
+
var MEDIUM_TRACK_LAYER_LIFE_SECONDS = {
|
|
402
|
+
alpha: 1.8,
|
|
403
|
+
electron: 1.4,
|
|
404
|
+
muon: 2.2
|
|
405
|
+
};
|
|
406
|
+
var MEDIUM_TRACK_LAYER_MAX_TRACKS = 64;
|
|
407
|
+
var MEDIUM_TRACK_LAYER_LOOK = {
|
|
408
|
+
alpha: {
|
|
409
|
+
sigmaFrac: 22e-4,
|
|
410
|
+
endSigmaMul: 1.9,
|
|
411
|
+
broaden: 3.2,
|
|
412
|
+
wiggleFrac: 0,
|
|
413
|
+
beadFrac: 6e-3,
|
|
414
|
+
dropletFrac: 35e-4,
|
|
415
|
+
dropletChance: 0.5,
|
|
416
|
+
startFrac: 0.035,
|
|
417
|
+
gain: 1.8
|
|
418
|
+
},
|
|
419
|
+
electron: {
|
|
420
|
+
sigmaFrac: 9e-4,
|
|
421
|
+
endSigmaMul: 1.3,
|
|
422
|
+
broaden: 6,
|
|
423
|
+
wiggleFrac: 0.012,
|
|
424
|
+
beadFrac: 8e-3,
|
|
425
|
+
dropletFrac: 6e-3,
|
|
426
|
+
dropletChance: 0.55,
|
|
427
|
+
startFrac: 0,
|
|
428
|
+
gain: 2.2
|
|
429
|
+
},
|
|
430
|
+
muon: {
|
|
431
|
+
sigmaFrac: 8e-4,
|
|
432
|
+
endSigmaMul: 1,
|
|
433
|
+
broaden: 5,
|
|
434
|
+
wiggleFrac: 0,
|
|
435
|
+
beadFrac: 7e-3,
|
|
436
|
+
dropletFrac: 5e-3,
|
|
437
|
+
dropletChance: 0.5,
|
|
438
|
+
startFrac: 0,
|
|
439
|
+
gain: 2.2
|
|
440
|
+
}
|
|
441
|
+
};
|
|
442
|
+
var MEDIUM_TRACK_LAYER_BURST_SPREAD = 1.6;
|
|
443
|
+
var MEDIUM_TRACK_LAYER_BURST_ACCENT = 3;
|
|
444
|
+
var MEDIUM_TRACK_LAYER_FADE_GAMMA = 1.6;
|
|
445
|
+
var MEDIUM_TRACK_LAYER_GRAVITY_SAG = 0.018;
|
|
446
|
+
var MEDIUM_TRACK_LAYER_CURL_PUSH = 0.05;
|
|
357
447
|
|
|
358
448
|
// src/medium/noise.ts
|
|
359
449
|
var VG_VALUE_NOISE = `
|
|
@@ -803,10 +893,35 @@ half4 ${fnName}(float2 px) {
|
|
|
803
893
|
}
|
|
804
894
|
`;
|
|
805
895
|
}
|
|
896
|
+
var VG_LIGHT = `
|
|
897
|
+
float vgLightAmount(float2 xy, float2 lightPosFrac, float2 lightDir, float cosCone, float falloff, float2 resolution, float seed) {
|
|
898
|
+
float2 rel = xy - lightPosFrac * resolution;
|
|
899
|
+
// A cone of 180\xB0 or more is an edge strip: light spreads from a line, not a point.
|
|
900
|
+
if (cosCone <= -0.999) {
|
|
901
|
+
float depth = max(dot(rel, lightDir), 0.0);
|
|
902
|
+
return exp(-(depth / max(resolution.y, 1.0)) * falloff);
|
|
903
|
+
}
|
|
904
|
+
float dist = length(rel);
|
|
905
|
+
float2 dirN = dist > 1e-4 ? rel / dist : lightDir;
|
|
906
|
+
float cosAngle = dot(dirN, lightDir);
|
|
907
|
+
// A soft lobe with half intensity at the cone angle and no boundary at all: a lamp in haze has
|
|
908
|
+
// no drawn edge.
|
|
909
|
+
float lobe = log(0.5) / log(clamp(cosCone, 0.05, 0.999));
|
|
910
|
+
float edge = pow(max(cosAngle, 0.0), lobe);
|
|
911
|
+
float atten = exp(-(dist / max(resolution.y, 1.0)) * falloff);
|
|
912
|
+
if (edge * atten < 0.001) return 0.0;
|
|
913
|
+
// Static shafts inside the beam: brightness varies with the angle around the lamp, so streaks
|
|
914
|
+
// fan out from the source the way light through dusty air does.
|
|
915
|
+
float angle = atan(dirN.x * lightDir.y - dirN.y * lightDir.x, cosAngle);
|
|
916
|
+
float shafts = 0.6 + 0.32 * vgValueNoise(float2(angle * 14.0, seed)) + 0.18 * vgValueNoise(float2(angle * 33.0, seed + 5.3));
|
|
917
|
+
return edge * atten * clamp(shafts, 0.0, 1.0);
|
|
918
|
+
}
|
|
919
|
+
`;
|
|
806
920
|
var MEDIUM_COMPOSITE_SHADER = `
|
|
807
921
|
uniform shader u_vapor;
|
|
808
922
|
uniform shader u_condensate;
|
|
809
923
|
uniform shader u_track;
|
|
924
|
+
uniform shader u_shadow;
|
|
810
925
|
uniform float2 u_dyeScale;
|
|
811
926
|
uniform float3 u_lab0;
|
|
812
927
|
uniform float3 u_lab1;
|
|
@@ -822,11 +937,33 @@ uniform float u_farBlurRadius;
|
|
|
822
937
|
uniform float u_farWeight;
|
|
823
938
|
uniform float u_gravityBoost;
|
|
824
939
|
uniform float u_gravityBoostStart;
|
|
940
|
+
uniform float3 u_channelScatter;
|
|
941
|
+
uniform float2 u_light0Pos;
|
|
942
|
+
uniform float2 u_light0Dir;
|
|
943
|
+
uniform float u_light0CosCone;
|
|
944
|
+
uniform float u_light0Falloff;
|
|
945
|
+
uniform float3 u_light0Color;
|
|
946
|
+
uniform float u_light0Intensity;
|
|
947
|
+
uniform float2 u_light1Pos;
|
|
948
|
+
uniform float2 u_light1Dir;
|
|
949
|
+
uniform float u_light1CosCone;
|
|
950
|
+
uniform float u_light1Falloff;
|
|
951
|
+
uniform float3 u_light1Color;
|
|
952
|
+
uniform float u_light1Intensity;
|
|
953
|
+
uniform float2 u_light2Pos;
|
|
954
|
+
uniform float2 u_light2Dir;
|
|
955
|
+
uniform float u_light2CosCone;
|
|
956
|
+
uniform float u_light2Falloff;
|
|
957
|
+
uniform float3 u_light2Color;
|
|
958
|
+
uniform float u_light2Intensity;
|
|
959
|
+
uniform float u_trackLightFloor;
|
|
825
960
|
|
|
826
961
|
${VG_OKLAB_TO_SRGB}
|
|
827
962
|
${VG_CUBIC_WEIGHTS}
|
|
828
963
|
${vgBicubicSampler("vgBicubicVapor", "u_vapor")}
|
|
829
964
|
${vgBicubicSampler("vgBicubicCondensate", "u_condensate")}
|
|
965
|
+
${VG_VALUE_NOISE}
|
|
966
|
+
${VG_LIGHT}
|
|
830
967
|
|
|
831
968
|
half4 main(float2 xy) {
|
|
832
969
|
float2 baseSrc = xy * u_dyeScale;
|
|
@@ -894,7 +1031,34 @@ half4 main(float2 xy) {
|
|
|
894
1031
|
float total = max(w0 + w1 + w2 + wBg + wCondensate, 1e-4);
|
|
895
1032
|
float3 mixLab =
|
|
896
1033
|
(u_labBg * wBg + u_lab0 * w0 + u_lab1 * w1 + u_lab2 * w2 + condensateLab * wCondensate) / total;
|
|
897
|
-
|
|
1034
|
+
// The dark ambient base \u2014 this is where the OLD mechanism stops (see the file header on
|
|
1035
|
+
// VG_LIGHT): unclamped here, clamped once at the very end, after the lit layer adds in.
|
|
1036
|
+
float3 rgbLinearBase = vgOklabToLinear(mixLab);
|
|
1037
|
+
|
|
1038
|
+
// Spotlights reach this point dimmed by the gas in front of them (shadow-shader.ts).
|
|
1039
|
+
half4 shadow = u_shadow.eval(baseSrc);
|
|
1040
|
+
float light0 = vgLightAmount(xy, u_light0Pos, u_light0Dir, u_light0CosCone, u_light0Falloff, u_resolution, 1.7) * u_light0Intensity;
|
|
1041
|
+
float light1 = vgLightAmount(xy, u_light1Pos, u_light1Dir, u_light1CosCone, u_light1Falloff, u_resolution, 4.1) * u_light1Intensity
|
|
1042
|
+
* float(shadow.r);
|
|
1043
|
+
float light2 = vgLightAmount(xy, u_light2Pos, u_light2Dir, u_light2CosCone, u_light2Falloff, u_resolution, 8.3) * u_light2Intensity
|
|
1044
|
+
* float(shadow.g);
|
|
1045
|
+
float3 illum = u_light0Color * light0 + u_light1Color * light1 + u_light2Color * light2;
|
|
1046
|
+
|
|
1047
|
+
float totalScatter = gasR * u_channelScatter.x + gasG * u_channelScatter.y + gasB * u_channelScatter.z;
|
|
1048
|
+
float fogDensity = totalScatter + wCondensate;
|
|
1049
|
+
float mist = 0.006 + fogDensity * 0.2;
|
|
1050
|
+
|
|
1051
|
+
// TRACKS: lit the same way as the mist, plus a floor so a track already on screen never vanishes
|
|
1052
|
+
// entirely just because it drifted out of a cone (a fresh ionization trail reads brighter than
|
|
1053
|
+
// ambient mist in a real chamber). The floor is added to ITS OWN color, not multiplied into illum:
|
|
1054
|
+
// illum is exactly [0,0,0] in a fully unlit spot (every light's amount is 0 there), so a floor
|
|
1055
|
+
// multiplied by illum would vanish exactly where it exists to help \u2014 it needs a color of its own
|
|
1056
|
+
// (a neutral, self-luminous glow) rather than borrowing whichever light happens to be nearby.
|
|
1057
|
+
float trackDensity = max(float(track.r), max(float(track.g), float(track.b)));
|
|
1058
|
+
float3 trackGlow = (1.0 - exp(-trackDensity * 0.4)) * (illum + float3(u_trackLightFloor)) * 0.08;
|
|
1059
|
+
|
|
1060
|
+
float3 rgbLinear = rgbLinearBase * 0.025 + illum * mist + trackGlow;
|
|
1061
|
+
rgbLinear = float3(1.0) - exp(-rgbLinear * 1.0);
|
|
898
1062
|
float3 srgb = vgLinearToSrgb(rgbLinear);
|
|
899
1063
|
return half4(half3(srgb), half(1.0));
|
|
900
1064
|
}
|
|
@@ -912,7 +1076,6 @@ uniform float u_settle;
|
|
|
912
1076
|
uniform float u_tailDim;
|
|
913
1077
|
uniform float u_intensity;
|
|
914
1078
|
uniform float u_seed;
|
|
915
|
-
uniform float3 u_channelMask;
|
|
916
1079
|
|
|
917
1080
|
${VG_VALUE_NOISE}
|
|
918
1081
|
|
|
@@ -935,20 +1098,46 @@ half4 main(float2 xy) {
|
|
|
935
1098
|
best = max(best, v);
|
|
936
1099
|
}
|
|
937
1100
|
|
|
938
|
-
float3 dye =
|
|
1101
|
+
float3 dye = float3(best * u_intensity);
|
|
939
1102
|
return half4(half3(dye), half(1.0));
|
|
940
1103
|
}
|
|
941
1104
|
`;
|
|
942
1105
|
|
|
1106
|
+
// src/medium/shadow-shader.ts
|
|
1107
|
+
var MEDIUM_SHADOW_SHADER = `
|
|
1108
|
+
uniform shader u_vapor;
|
|
1109
|
+
uniform shader u_condensate;
|
|
1110
|
+
uniform float u_condensateGain;
|
|
1111
|
+
uniform float2 u_light1Grid;
|
|
1112
|
+
uniform float2 u_light2Grid;
|
|
1113
|
+
uniform float u_extinction;
|
|
1114
|
+
|
|
1115
|
+
float vgOpticalDepth(float2 p, float2 lightP) {
|
|
1116
|
+
float2 toLight = lightP - p;
|
|
1117
|
+
float optical = 0.0;
|
|
1118
|
+
for (int i = 1; i <= 24; i++) {
|
|
1119
|
+
float2 q = p + toLight * (float(i) / 24.0);
|
|
1120
|
+
float inside = step(0.0, q.x) * step(q.x, u_resolution.x) * step(0.0, q.y) * step(q.y, u_resolution.y);
|
|
1121
|
+
half4 v = u_vapor.eval(q);
|
|
1122
|
+
half4 c = u_condensate.eval(q);
|
|
1123
|
+
optical += inside * (float(v.r + v.g + v.b) + float(c.r) * u_condensateGain);
|
|
1124
|
+
}
|
|
1125
|
+
return optical * length(toLight) / 24.0 / max(u_resolution.y, 1.0);
|
|
1126
|
+
}
|
|
1127
|
+
|
|
1128
|
+
half4 main(float2 xy) {
|
|
1129
|
+
float2 p = float2(xy.x, u_resolution.y - xy.y);
|
|
1130
|
+
float t1 = exp(-vgOpticalDepth(p, u_light1Grid) * u_extinction);
|
|
1131
|
+
float t2 = exp(-vgOpticalDepth(p, u_light2Grid) * u_extinction);
|
|
1132
|
+
return half4(half(t1), half(t2), half(0.0), half(1.0));
|
|
1133
|
+
}
|
|
1134
|
+
`;
|
|
1135
|
+
|
|
943
1136
|
// src/medium/dynamics.ts
|
|
944
1137
|
function hash01(n) {
|
|
945
1138
|
const s = Math.sin(n * 12.9898) * 43758.5453;
|
|
946
1139
|
return s - Math.floor(s);
|
|
947
1140
|
}
|
|
948
|
-
function channelFromSeed(seed) {
|
|
949
|
-
const idx = Math.floor(hash01(seed * 1.37) * 3) % 3;
|
|
950
|
-
return idx === 0 ? [1, 0, 0] : idx === 1 ? [0, 1, 0] : [0, 0, 1];
|
|
951
|
-
}
|
|
952
1141
|
function lerp(range, t) {
|
|
953
1142
|
return range[0] + (range[1] - range[0]) * t;
|
|
954
1143
|
}
|
|
@@ -962,18 +1151,19 @@ function applyTrackDepth(preset, depth) {
|
|
|
962
1151
|
intensity: preset.intensity * intensityScale
|
|
963
1152
|
};
|
|
964
1153
|
}
|
|
965
|
-
function buildEmission(presetName, source, seed, intensityScale) {
|
|
1154
|
+
function buildEmission(presetName, source, seed, intensityScale, angleOverride, lengthScale = 1) {
|
|
966
1155
|
const preset = MEDIUM_TRACK_PRESETS[presetName];
|
|
967
1156
|
const depth = hash01(seed * 4.63);
|
|
968
1157
|
const depthApplied = applyTrackDepth(preset, depth);
|
|
969
1158
|
return {
|
|
970
1159
|
...depthApplied,
|
|
1160
|
+
lengthFrac: depthApplied.lengthFrac * lengthScale,
|
|
971
1161
|
intensity: depthApplied.intensity * intensityScale,
|
|
972
1162
|
source,
|
|
973
|
-
angle: hash01(seed * 7.31) * Math.PI * 2,
|
|
1163
|
+
angle: angleOverride ?? hash01(seed * 7.31) * Math.PI * 2,
|
|
974
1164
|
seed,
|
|
975
|
-
|
|
976
|
-
|
|
1165
|
+
depth,
|
|
1166
|
+
kind: presetName
|
|
977
1167
|
};
|
|
978
1168
|
}
|
|
979
1169
|
function createMediumDynamics() {
|
|
@@ -999,8 +1189,15 @@ function createMediumDynamics() {
|
|
|
999
1189
|
const eligible = hash01(bar * 0.618) < MEDIUM_EMISSION_PROBABILITY && simTime - lastEmissionAt >= MEDIUM_MIN_EMISSION_INTERVAL;
|
|
1000
1190
|
if (eligible) {
|
|
1001
1191
|
lastEmissionAt = simTime;
|
|
1002
|
-
const
|
|
1003
|
-
|
|
1192
|
+
const rayCount = Math.round(lerp(MEDIUM_ALPHA_BURST_RAYS, hash01(bar * 9.17)));
|
|
1193
|
+
for (let i = 0; i < rayCount; i += 1) {
|
|
1194
|
+
const raySeed = bar + (i + 1) * 0.0137;
|
|
1195
|
+
const angle = (i + hash01(raySeed * 5.23)) / rayCount * Math.PI * 2;
|
|
1196
|
+
const lengthScale = lerp(MEDIUM_ALPHA_RAY_LENGTH_SCALE, hash01(raySeed * 2.91));
|
|
1197
|
+
emissions.push(
|
|
1198
|
+
buildEmission("alpha", sourcePoint, raySeed, 0.6 + 0.4 * amp, angle, lengthScale)
|
|
1199
|
+
);
|
|
1200
|
+
}
|
|
1004
1201
|
}
|
|
1005
1202
|
}
|
|
1006
1203
|
} else if (state === "idle") {
|
|
@@ -1012,7 +1209,8 @@ function createMediumDynamics() {
|
|
|
1012
1209
|
hash01(idx * 2.1 + 0.5),
|
|
1013
1210
|
MEDIUM_SENSITIVE_TOP + hash01(idx * 5.9 + 1.7) * (MEDIUM_SENSITIVE_BOTTOM - MEDIUM_SENSITIVE_TOP)
|
|
1014
1211
|
];
|
|
1015
|
-
|
|
1212
|
+
const presetName = hash01(idx * 3.14) < 0.5 ? "electron" : "muon";
|
|
1213
|
+
emissions.push(buildEmission(presetName, randomSource, idx, 1));
|
|
1016
1214
|
nextNaturalAt = idleClock + MEDIUM_NATURAL_INTERVAL + hash01(idx * 1.7) * MEDIUM_NATURAL_JITTER;
|
|
1017
1215
|
}
|
|
1018
1216
|
}
|
|
@@ -1021,21 +1219,73 @@ function createMediumDynamics() {
|
|
|
1021
1219
|
return { step };
|
|
1022
1220
|
}
|
|
1023
1221
|
|
|
1222
|
+
// src/medium/light-rig.ts
|
|
1223
|
+
var clamp012 = (v) => Math.min(1, Math.max(0, v));
|
|
1224
|
+
var EDGE_STRIP_CONE_ANGLE = Math.PI - 1e-6;
|
|
1225
|
+
var EDGE_STRIP_TINT_MIX = 0.22;
|
|
1226
|
+
var GEL_LIGHTNESS = 0.7;
|
|
1227
|
+
var LIGHT_HUE_SPREAD = MEDIUM_SPECIES_HUE_SPREAD / 2;
|
|
1228
|
+
function retint(base, hueDeg, chroma, mix2) {
|
|
1229
|
+
const [baseL, a, b] = srgbToOklab(base);
|
|
1230
|
+
const l = baseL + (Math.min(baseL, GEL_LIGHTNESS) - baseL) * mix2;
|
|
1231
|
+
const rad = hueDeg * Math.PI / 180;
|
|
1232
|
+
const targetA = chroma * Math.cos(rad);
|
|
1233
|
+
const targetB = chroma * Math.sin(rad);
|
|
1234
|
+
const mixedA = a + (targetA - a) * mix2;
|
|
1235
|
+
const mixedB = b + (targetB - b) * mix2;
|
|
1236
|
+
let lo = 0;
|
|
1237
|
+
let hi = 1;
|
|
1238
|
+
if (inGamut(oklabToSrgb([l, mixedA, mixedB]))) lo = 1;
|
|
1239
|
+
else {
|
|
1240
|
+
for (let i = 0; i < 12; i += 1) {
|
|
1241
|
+
const mid = (lo + hi) / 2;
|
|
1242
|
+
if (inGamut(oklabToSrgb([l, mixedA * mid, mixedB * mid]))) lo = mid;
|
|
1243
|
+
else hi = mid;
|
|
1244
|
+
}
|
|
1245
|
+
}
|
|
1246
|
+
const rgb = oklabToSrgb([l, mixedA * lo, mixedB * lo]).map(clamp012);
|
|
1247
|
+
const gain = Math.max(...base) / Math.max(...rgb, 1e-6);
|
|
1248
|
+
return rgb.map((v) => clamp012(v * gain));
|
|
1249
|
+
}
|
|
1250
|
+
var inGamut = (rgb) => rgb.every((v) => v >= -1e-4 && v <= 1 + 1e-4);
|
|
1251
|
+
function lightRigForCharacter(character, baseLights = MEDIUM_DEFAULT_LIGHTS) {
|
|
1252
|
+
const spotlightIndices = [];
|
|
1253
|
+
baseLights.forEach((light, i) => {
|
|
1254
|
+
if (light.coneAngle < EDGE_STRIP_CONE_ANGLE) spotlightIndices.push(i);
|
|
1255
|
+
});
|
|
1256
|
+
const spotlightCount = spotlightIndices.length;
|
|
1257
|
+
return baseLights.map((light, i) => {
|
|
1258
|
+
if (light.coneAngle >= EDGE_STRIP_CONE_ANGLE) {
|
|
1259
|
+
return { ...light, color: retint(light.color, character.hue, character.chroma, EDGE_STRIP_TINT_MIX) };
|
|
1260
|
+
}
|
|
1261
|
+
const slot = spotlightIndices.indexOf(i);
|
|
1262
|
+
const hueOffset = spotlightCount > 1 ? LIGHT_HUE_SPREAD * (2 * slot / (spotlightCount - 1) - 1) : 0;
|
|
1263
|
+
return { ...light, color: retint(light.color, character.hue + hueOffset, character.chroma, 1) };
|
|
1264
|
+
});
|
|
1265
|
+
}
|
|
1266
|
+
function lightRigForCover(cover, baseLights = MEDIUM_DEFAULT_LIGHTS) {
|
|
1267
|
+
return lightRigForCharacter(characterOf(cover), baseLights);
|
|
1268
|
+
}
|
|
1269
|
+
|
|
1024
1270
|
exports.ATOM_AT_REST = ATOM_AT_REST;
|
|
1025
1271
|
exports.DEFAULT_GROWTH_TIMING = DEFAULT_GROWTH_TIMING;
|
|
1026
1272
|
exports.LINEAR_TO_LMS = LINEAR_TO_LMS;
|
|
1027
1273
|
exports.LMS_TO_LINEAR = LMS_TO_LINEAR;
|
|
1028
1274
|
exports.LMS_TO_OKLAB = LMS_TO_OKLAB;
|
|
1275
|
+
exports.MEDIUM_ALPHA_BURST_RAYS = MEDIUM_ALPHA_BURST_RAYS;
|
|
1276
|
+
exports.MEDIUM_ALPHA_RAY_LENGTH_SCALE = MEDIUM_ALPHA_RAY_LENGTH_SCALE;
|
|
1029
1277
|
exports.MEDIUM_COMPOSITE_SHADER = MEDIUM_COMPOSITE_SHADER;
|
|
1030
1278
|
exports.MEDIUM_CONDENSATE_CORRECT_SHADER = MEDIUM_CONDENSATE_CORRECT_SHADER;
|
|
1031
1279
|
exports.MEDIUM_CONDENSATE_FORWARD_SHADER = MEDIUM_CONDENSATE_FORWARD_SHADER;
|
|
1032
1280
|
exports.MEDIUM_CONDENSATE_REACT_SHADER = MEDIUM_CONDENSATE_REACT_SHADER;
|
|
1033
1281
|
exports.MEDIUM_DEFAULTS = MEDIUM_DEFAULTS;
|
|
1034
1282
|
exports.MEDIUM_DEFAULT_CELL_PX = MEDIUM_DEFAULT_CELL_PX;
|
|
1283
|
+
exports.MEDIUM_DEFAULT_LIGHTS = MEDIUM_DEFAULT_LIGHTS;
|
|
1035
1284
|
exports.MEDIUM_DRIFT_TURBULENCE = MEDIUM_DRIFT_TURBULENCE;
|
|
1036
1285
|
exports.MEDIUM_EMISSION_BEATS = MEDIUM_EMISSION_BEATS;
|
|
1037
1286
|
exports.MEDIUM_EMISSION_PROBABILITY = MEDIUM_EMISSION_PROBABILITY;
|
|
1038
1287
|
exports.MEDIUM_EMIT_SHADER = MEDIUM_EMIT_SHADER;
|
|
1288
|
+
exports.MEDIUM_MAX_LIGHTS = MEDIUM_MAX_LIGHTS;
|
|
1039
1289
|
exports.MEDIUM_MIN_EMISSION_INTERVAL = MEDIUM_MIN_EMISSION_INTERVAL;
|
|
1040
1290
|
exports.MEDIUM_NATURAL_INTERVAL = MEDIUM_NATURAL_INTERVAL;
|
|
1041
1291
|
exports.MEDIUM_NATURAL_JITTER = MEDIUM_NATURAL_JITTER;
|
|
@@ -1045,12 +1295,21 @@ exports.MEDIUM_RESAMPLE_SHADER = MEDIUM_RESAMPLE_SHADER;
|
|
|
1045
1295
|
exports.MEDIUM_SEED_SHADER = MEDIUM_SEED_SHADER;
|
|
1046
1296
|
exports.MEDIUM_SENSITIVE_BOTTOM = MEDIUM_SENSITIVE_BOTTOM;
|
|
1047
1297
|
exports.MEDIUM_SENSITIVE_TOP = MEDIUM_SENSITIVE_TOP;
|
|
1298
|
+
exports.MEDIUM_SHADOW_SHADER = MEDIUM_SHADOW_SHADER;
|
|
1048
1299
|
exports.MEDIUM_SPECIES_HUE_SPREAD = MEDIUM_SPECIES_HUE_SPREAD;
|
|
1049
1300
|
exports.MEDIUM_SPECIES_LIGHTNESS = MEDIUM_SPECIES_LIGHTNESS;
|
|
1050
1301
|
exports.MEDIUM_TIME_PERIOD = MEDIUM_TIME_PERIOD;
|
|
1051
1302
|
exports.MEDIUM_TRACK_ADVECT_SHADER = MEDIUM_TRACK_ADVECT_SHADER;
|
|
1052
1303
|
exports.MEDIUM_TRACK_DEPTH_INTENSITY_RANGE = MEDIUM_TRACK_DEPTH_INTENSITY_RANGE;
|
|
1053
1304
|
exports.MEDIUM_TRACK_DEPTH_WIDTH_RANGE = MEDIUM_TRACK_DEPTH_WIDTH_RANGE;
|
|
1305
|
+
exports.MEDIUM_TRACK_LAYER_BURST_ACCENT = MEDIUM_TRACK_LAYER_BURST_ACCENT;
|
|
1306
|
+
exports.MEDIUM_TRACK_LAYER_BURST_SPREAD = MEDIUM_TRACK_LAYER_BURST_SPREAD;
|
|
1307
|
+
exports.MEDIUM_TRACK_LAYER_CURL_PUSH = MEDIUM_TRACK_LAYER_CURL_PUSH;
|
|
1308
|
+
exports.MEDIUM_TRACK_LAYER_FADE_GAMMA = MEDIUM_TRACK_LAYER_FADE_GAMMA;
|
|
1309
|
+
exports.MEDIUM_TRACK_LAYER_GRAVITY_SAG = MEDIUM_TRACK_LAYER_GRAVITY_SAG;
|
|
1310
|
+
exports.MEDIUM_TRACK_LAYER_LIFE_SECONDS = MEDIUM_TRACK_LAYER_LIFE_SECONDS;
|
|
1311
|
+
exports.MEDIUM_TRACK_LAYER_LOOK = MEDIUM_TRACK_LAYER_LOOK;
|
|
1312
|
+
exports.MEDIUM_TRACK_LAYER_MAX_TRACKS = MEDIUM_TRACK_LAYER_MAX_TRACKS;
|
|
1054
1313
|
exports.MEDIUM_TRACK_PRESETS = MEDIUM_TRACK_PRESETS;
|
|
1055
1314
|
exports.MEDIUM_TURBULENCE_TAU = MEDIUM_TURBULENCE_TAU;
|
|
1056
1315
|
exports.MEDIUM_VAPOR_CORRECT_SHADER = MEDIUM_VAPOR_CORRECT_SHADER;
|
|
@@ -1071,6 +1330,8 @@ exports.depthFarOffsetPx = depthFarOffsetPx;
|
|
|
1071
1330
|
exports.growthFrame = growthFrame;
|
|
1072
1331
|
exports.growthInk = growthInk;
|
|
1073
1332
|
exports.growthPhase = growthPhase;
|
|
1333
|
+
exports.lightRigForCharacter = lightRigForCharacter;
|
|
1334
|
+
exports.lightRigForCover = lightRigForCover;
|
|
1074
1335
|
exports.neutralSpecies = neutralSpecies;
|
|
1075
1336
|
exports.oklabToSrgb = oklabToSrgb;
|
|
1076
1337
|
exports.smoothstep = smoothstep;
|