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/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
- decay: 0.06,
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
- beta: {
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.85
374
+ intensity: 0.6
333
375
  },
334
- natural: {
335
- lengthFrac: 0.13,
336
- headWidthFrac: 0.02,
337
- tailWidthFrac: 0.04,
338
- raggedFrac: 0.02,
339
- raggedFreq: 4,
340
- settleFrac: 0.02,
341
- tailDim: 0.5,
342
- intensity: 0.4
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 = 25;
352
- var MEDIUM_NATURAL_JITTER = 35;
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 MEDIUM_TRACK_DEPTH_WIDTH_RANGE = [0.55, 1];
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
- float3 rgbLinear = clamp(vgOklabToLinear(mixLab), float3(0.0), float3(1.0));
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 = u_channelMask * (best * u_intensity);
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
- channel: channelFromSeed(seed),
976
- depth
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 presetName = hash01(bar * 3.14) < 0.55 ? "alpha" : "beta";
1003
- emissions.push(buildEmission(presetName, sourcePoint, bar, 0.6 + 0.4 * amp));
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
- emissions.push(buildEmission("natural", randomSource, idx, 1));
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;