@woosh/meep-engine 3.60.3 → 3.60.4

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.
Files changed (163) hide show
  1. package/editor/ecs/component/editors/ecs/Trail3DEditor.d.ts +3 -0
  2. package/editor/ecs/component/editors/ecs/Trail3DEditor.d.ts.map +1 -1
  3. package/editor/ecs/component/editors/ecs/Trail3DEditor.js +6 -0
  4. package/package.json +1 -1
  5. package/src/engine/ecs/ik/PLAN.md +1 -1
  6. package/src/engine/graphics/ecs/mesh-v2/ShadedGeometryFlags.js +1 -2
  7. package/src/engine/graphics/ecs/trail3d/TRAIL3D_FADE_LINEAR.d.ts +14 -0
  8. package/src/engine/graphics/ecs/trail3d/TRAIL3D_FADE_LINEAR.d.ts.map +1 -0
  9. package/src/engine/graphics/ecs/trail3d/TRAIL3D_FADE_LINEAR.js +22 -0
  10. package/src/engine/graphics/ecs/trail3d/Trail3D.d.ts +19 -3
  11. package/src/engine/graphics/ecs/trail3d/Trail3D.d.ts.map +1 -1
  12. package/src/engine/graphics/ecs/trail3d/Trail3D.js +31 -4
  13. package/src/engine/graphics/ecs/trail3d/advance_trail.js +1 -1
  14. package/src/engine/graphics/trail/tube/simulator/TubeXFixedPhysicsSimulator.d.ts +5 -5
  15. package/src/engine/graphics/trail/tube/simulator/TubeXFixedPhysicsSimulator.d.ts.map +1 -1
  16. package/src/engine/graphics/trail/tube/simulator/TubeXFixedPhysicsSimulator.js +6 -6
  17. package/src/engine/graphics3/trail/trail_tube_write_geometry.d.ts +10 -0
  18. package/src/engine/graphics3/trail/trail_tube_write_geometry.d.ts.map +1 -1
  19. package/src/engine/graphics3/trail/trail_tube_write_geometry.js +13 -1
  20. package/src/engine/physics/cloth/ecs/cloth_geodesic.d.ts +2 -2
  21. package/src/engine/physics/cloth/ecs/cloth_geodesic.js +2 -2
  22. package/src/shade/playground/probe_clipmap/README.md +1 -0
  23. package/src/shade/playground/probe_clipmap/index.html +5 -0
  24. package/src/shade/playground/probe_clipmap/main.d.ts.map +1 -1
  25. package/src/shade/playground/probe_clipmap/main.js +16 -2
  26. package/src/shade/playground/vgeo_scene/README.md +1 -1
  27. package/src/shade/renderer/atmosphere/MULTISCATTER_LUT_CONFIG.d.ts +4 -6
  28. package/src/shade/renderer/atmosphere/MULTISCATTER_LUT_CONFIG.d.ts.map +1 -1
  29. package/src/shade/renderer/atmosphere/MULTISCATTER_LUT_CONFIG.js +34 -36
  30. package/src/shade/renderer/atmosphere/chunk_get_multiscatter_values.d.ts +1 -1
  31. package/src/shade/renderer/atmosphere/chunk_get_multiscatter_values.d.ts.map +1 -1
  32. package/src/shade/renderer/atmosphere/chunk_get_multiscatter_values.js +162 -163
  33. package/src/shade/renderer/atmosphere/compute_mie_absorption.d.ts +1 -1
  34. package/src/shade/renderer/atmosphere/compute_mie_absorption.js +1 -1
  35. package/src/shade/renderer/atmosphere/hillaire/ATMOSPHERE_EARTH.js +1 -1
  36. package/src/shade/renderer/atmosphere/hillaire/ATMOSPHERE_PARAMETERS_STRUCT.js +1 -1
  37. package/src/shade/renderer/atmosphere/shader_multiscatter_lut.js +164 -164
  38. package/src/shade/renderer/atmosphere/shader_sky_irradiance_lut.js +1 -1
  39. package/src/shade/renderer/geometry/sdf/GPUGeometrySDFManager.d.ts +2 -2
  40. package/src/shade/renderer/geometry/sdf/GPUGeometrySDFManager.js +2 -2
  41. package/src/shade/renderer/geometry/sdf/cards/mesh_cards_raster_pass_descriptor.js +2 -2
  42. package/src/shade/renderer/geometry/sdf/cards/shader_mesh_cards_histogram.d.ts +1 -1
  43. package/src/shade/renderer/geometry/sdf/cards/shader_mesh_cards_histogram.js +1 -1
  44. package/src/shade/renderer/geometry/sdf/chunk_blas_query_band_sdf.d.ts +4 -5
  45. package/src/shade/renderer/geometry/sdf/chunk_blas_query_band_sdf.d.ts.map +1 -1
  46. package/src/shade/renderer/geometry/sdf/chunk_blas_query_band_sdf.js +6 -7
  47. package/src/shade/renderer/geometry/virtual/ATTRIBUTE_PLAN_2026_09_18.md +1 -1
  48. package/src/shade/renderer/geometry/virtual/VIRTUAL_GEOMETRY_PLAN.md +2 -4
  49. package/src/shade/renderer/geometry/virtual/WEDGE_PLAN_2026_09_18.md +1 -1
  50. package/src/shade/renderer/geometry/virtual/runtime/SHADOW_PLAN_2026_09_28.md +3 -10
  51. package/src/shade/renderer/global_illumination/probe_clipmap/GPUProbeClipmapGI.d.ts.map +1 -1
  52. package/src/shade/renderer/global_illumination/probe_clipmap/GPUProbeClipmapGI.js +1 -0
  53. package/src/shade/renderer/global_illumination/probe_clipmap/PROBE_CLIPMAP_PROPOSAL_2026_10_27.md +119 -38
  54. package/src/shade/renderer/global_illumination/probe_clipmap/PROBE_RAYS_VARIABLE_AND_SORTED_SCOPE_2026_10_30.md +2 -2
  55. package/src/shade/renderer/global_illumination/probe_clipmap/ProbeClipmapSettings.d.ts +12 -0
  56. package/src/shade/renderer/global_illumination/probe_clipmap/ProbeClipmapSettings.d.ts.map +1 -1
  57. package/src/shade/renderer/global_illumination/probe_clipmap/ProbeClipmapSettings.js +22 -0
  58. package/src/shade/renderer/global_illumination/probe_clipmap/probe/GPUProbeClipmap.d.ts +4 -1
  59. package/src/shade/renderer/global_illumination/probe_clipmap/probe/GPUProbeClipmap.d.ts.map +1 -1
  60. package/src/shade/renderer/global_illumination/probe_clipmap/probe/GPUProbeClipmap.js +50 -4
  61. package/src/shade/renderer/global_illumination/probe_clipmap/probe/PROBE_CLIPMAP.d.ts +6 -0
  62. package/src/shade/renderer/global_illumination/probe_clipmap/probe/PROBE_CLIPMAP.d.ts.map +1 -1
  63. package/src/shade/renderer/global_illumination/probe_clipmap/probe/PROBE_CLIPMAP.js +7 -0
  64. package/src/shade/renderer/global_illumination/probe_clipmap/probe/PROBE_CLIPMAP_RESAMPLING.d.ts +13 -0
  65. package/src/shade/renderer/global_illumination/probe_clipmap/probe/PROBE_CLIPMAP_RESAMPLING.d.ts.map +1 -0
  66. package/src/shade/renderer/global_illumination/probe_clipmap/probe/PROBE_CLIPMAP_RESAMPLING.js +13 -0
  67. package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap.d.ts +17 -0
  68. package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap.d.ts.map +1 -1
  69. package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap.js +20 -0
  70. package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_resampled.d.ts +18 -0
  71. package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_resampled.d.ts.map +1 -0
  72. package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_resampled.js +42 -0
  73. package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_sample_radiance.d.ts.map +1 -1
  74. package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_sample_radiance.js +5 -4
  75. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_accumulate.d.ts +4 -0
  76. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_accumulate.d.ts.map +1 -1
  77. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_accumulate.js +13 -2
  78. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_resample.d.ts +52 -0
  79. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_resample.d.ts.map +1 -0
  80. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_resample.js +250 -0
  81. package/src/shade/renderer/light/cluster/PROPOSAL_ALGORITHM_2026_10_16.md +7 -7
  82. package/src/shade/renderer/light/cluster/PROPOSAL_HIERARCHICAL_2026_10_16.md +2 -2
  83. package/src/shade/renderer/light/environment/environment_level_1_blur_plan.d.ts +39 -0
  84. package/src/shade/renderer/light/environment/environment_level_1_blur_plan.d.ts.map +1 -0
  85. package/src/shade/renderer/light/environment/environment_level_1_blur_plan.js +119 -0
  86. package/src/shade/renderer/light/environment/execute_filter_environment_map.d.ts +15 -7
  87. package/src/shade/renderer/light/environment/execute_filter_environment_map.d.ts.map +1 -1
  88. package/src/shade/renderer/light/environment/execute_filter_environment_map.js +66 -83
  89. package/src/shade/renderer/light/environment/graph_filter_environment_map.d.ts.map +1 -1
  90. package/src/shade/renderer/light/environment/graph_filter_environment_map.js +15 -6
  91. package/src/shade/renderer/light/environment/shader_blur_environment_map.d.ts +3 -0
  92. package/src/shade/renderer/light/environment/shader_blur_environment_map.d.ts.map +1 -0
  93. package/src/shade/renderer/light/environment/shader_blur_environment_map.js +111 -0
  94. package/src/shade/renderer/light/environment/shader_convolve_reflection_map_from_blurred.d.ts +3 -0
  95. package/src/shade/renderer/light/environment/shader_convolve_reflection_map_from_blurred.d.ts.map +1 -0
  96. package/src/shade/renderer/light/environment/shader_convolve_reflection_map_from_blurred.js +128 -0
  97. package/src/shade/renderer/light/environment/shader_downsample_environment_map.d.ts.map +1 -1
  98. package/src/shade/renderer/light/environment/shader_downsample_environment_map.js +0 -1
  99. package/src/shade/renderer/particles/EMITTER_GLOBAL_STATE_PLAN_2026_10_29.md +1 -4
  100. package/src/shade/renderer/particles/ORIENTATION_PLAN_2026_10_15.md +2 -2
  101. package/src/shade/renderer/postprocess/nss/NOTES.md +14 -21
  102. package/src/shade/renderer/postprocess/nss/NSS.d.ts +1 -3
  103. package/src/shade/renderer/postprocess/nss/NSS.d.ts.map +1 -1
  104. package/src/shade/renderer/postprocess/nss/NSS.js +1 -3
  105. package/src/shade/renderer/postprocess/nss/NSS_NETWORK_SCHEMA.d.ts +5 -6
  106. package/src/shade/renderer/postprocess/nss/NSS_NETWORK_SCHEMA.d.ts.map +1 -1
  107. package/src/shade/renderer/postprocess/nss/NSS_NETWORK_SCHEMA.js +9 -10
  108. package/src/shade/renderer/postprocess/nss/shader_nss_extract_feedback.d.ts.map +1 -1
  109. package/src/shade/renderer/postprocess/nss/shader_nss_extract_feedback.js +48 -49
  110. package/src/shade/renderer/postprocess/nss/shader_nss_postprocess.d.ts.map +1 -1
  111. package/src/shade/renderer/postprocess/nss/shader_nss_postprocess.js +12 -16
  112. package/src/shade/renderer/postprocess/nss/shader_nss_preprocess.js +246 -246
  113. package/src/shade/renderer/rasterize/chunk_triangle_winding_is_reversed.d.ts +1 -2
  114. package/src/shade/renderer/rasterize/chunk_triangle_winding_is_reversed.d.ts.map +1 -1
  115. package/src/shade/renderer/rasterize/chunk_triangle_winding_is_reversed.js +34 -35
  116. package/src/shade/renderer/shader/chunk/atmosphere/C_MIE_SCATTERING_DECLARATION.d.ts +2 -2
  117. package/src/shade/renderer/shader/chunk/atmosphere/C_MIE_SCATTERING_DECLARATION.js +2 -2
  118. package/src/shade/renderer/shader/chunk/atmosphere/NOTES.md +1 -1
  119. package/src/shade/renderer/shader/chunk/atmosphere/chunk_compute_extinction.js +1 -1
  120. package/src/shade/renderer/shader/chunk/atmosphere/hillaire/DENSITY_PROFILE_LAYER_STRUCT.d.ts +1 -1
  121. package/src/shade/renderer/shader/chunk/atmosphere/hillaire/DENSITY_PROFILE_LAYER_STRUCT.js +1 -1
  122. package/src/shade/renderer/shader/chunk/brdf/FRESNEL_BRIEF_2026_10_16.md +4 -7
  123. package/src/shade/renderer/shader/chunk/brdf/fresnel/chunk_F_Schlick.d.ts +3 -3
  124. package/src/shade/renderer/shader/chunk/brdf/fresnel/chunk_F_Schlick.js +3 -3
  125. package/src/shade/renderer/shader/chunk/color/tonemap/ACES_TONE_MAPPING_BRIEF_2026_10_17.md +12 -19
  126. package/src/shade/renderer/shader/chunk/color/tonemap/aces/chunk_aces_ue_matrices.d.ts +9 -10
  127. package/src/shade/renderer/shader/chunk/color/tonemap/aces/chunk_aces_ue_matrices.d.ts.map +1 -1
  128. package/src/shade/renderer/shader/chunk/color/tonemap/aces/chunk_aces_ue_matrices.js +9 -10
  129. package/src/shade/renderer/shader/chunk/color/tonemap/aces/chunk_tonemap_ue_filmic.d.ts +4 -4
  130. package/src/shade/renderer/shader/chunk/color/tonemap/aces/chunk_tonemap_ue_filmic.js +5 -5
  131. package/src/shade/renderer/shader/chunk/material/MULTISCATTER.md +1 -1
  132. package/src/shade/renderer/shader/chunk/math/chunk_f16_round_stochastic.d.ts +1 -2
  133. package/src/shade/renderer/shader/chunk/math/chunk_f16_round_stochastic.d.ts.map +1 -1
  134. package/src/shade/renderer/shader/chunk/math/chunk_f16_round_stochastic.js +1 -2
  135. package/src/shade/renderer/shader/chunk/noise/chunk_stbn_sample_vec2.d.ts +14 -1
  136. package/src/shade/renderer/shader/chunk/noise/chunk_stbn_sample_vec2.d.ts.map +1 -1
  137. package/src/shade/renderer/shader/chunk/noise/chunk_stbn_sample_vec2.js +19 -6
  138. package/src/shade/renderer/shader/chunk/noise/chunk_stbn_sample_vec3.d.ts +10 -1
  139. package/src/shade/renderer/shader/chunk/noise/chunk_stbn_sample_vec3.d.ts.map +1 -1
  140. package/src/shade/renderer/shader/chunk/noise/chunk_stbn_sample_vec3.js +14 -6
  141. package/src/shade/renderer/shader/chunk/random/murmur/chunk_murmur3_vec3u.d.ts +1 -1
  142. package/src/shade/renderer/shader/chunk/random/murmur/chunk_murmur3_vec3u.js +1 -1
  143. package/src/shade/renderer/shader/compiler/SHADER_SWITCH_PROPOSAL_2026_10_16.md +9 -14
  144. package/src/shade/renderer/shadow/contact/FINDINGS.md +5 -5
  145. package/src/shade/renderer/shadow/vsm/DEFAULT_VSM_MAX_DYNAMIC_LOD_BIAS.d.ts +2 -3
  146. package/src/shade/renderer/shadow/vsm/DEFAULT_VSM_MAX_DYNAMIC_LOD_BIAS.d.ts.map +1 -1
  147. package/src/shade/renderer/shadow/vsm/DEFAULT_VSM_MAX_DYNAMIC_LOD_BIAS.js +7 -8
  148. package/src/shade/renderer/shadow/vsm/DEFAULT_VSM_MAX_LOAD_FACTOR.d.ts +1 -1
  149. package/src/shade/renderer/shadow/vsm/DEFAULT_VSM_MAX_LOAD_FACTOR.js +12 -12
  150. package/src/shade/renderer/shadow/vsm/VSMPageManager.d.ts +2 -2
  151. package/src/shade/renderer/shadow/vsm/VSMPageManager.d.ts.map +1 -1
  152. package/src/shade/renderer/shadow/vsm/VSMPageManager.js +3 -4
  153. package/src/shade/renderer/texture/mipmaps/NOTES.md +1 -2
  154. package/src/shade/renderer/volumetrics/VOLUMETRIC_NOISE_PLAN_2026_10_15.md +1 -1
  155. package/src/view/elements/progress/segmented/SegmentedResourceBarView.d.ts +0 -19
  156. package/src/view/elements/progress/segmented/SegmentedResourceBarView.d.ts.map +1 -1
  157. package/src/view/elements/progress/segmented/SegmentedResourceBarView.js +2 -52
  158. package/src/shade/renderer/light/environment/shader_convolve_reflection_map_from_averages.d.ts +0 -13
  159. package/src/shade/renderer/light/environment/shader_convolve_reflection_map_from_averages.d.ts.map +0 -1
  160. package/src/shade/renderer/light/environment/shader_convolve_reflection_map_from_averages.js +0 -198
  161. package/src/view/elements/progress/segmented/segment_frame_transform.d.ts +0 -15
  162. package/src/view/elements/progress/segmented/segment_frame_transform.d.ts.map +0 -1
  163. package/src/view/elements/progress/segmented/segment_frame_transform.js +0 -69
@@ -34,6 +34,9 @@ export class Trail3DEditor extends ObjectEditor {
34
34
  flags: {
35
35
  editor: BitFlagsEditor;
36
36
  };
37
+ fade: {
38
+ editor: import("../../TypeEditor.js").TypeEditor<any>;
39
+ };
37
40
  };
38
41
  };
39
42
  }
@@ -1 +1 @@
1
- {"version":3,"file":"Trail3DEditor.d.ts","sourceRoot":"","sources":["../../../../../../editor/ecs/component/editors/ecs/Trail3DEditor.js"],"names":[],"mappings":"AAIA;IACI;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;MAsCC;CACJ;6BA1C4B,8BAA8B;+BAD5B,6BAA6B"}
1
+ {"version":3,"file":"Trail3DEditor.d.ts","sourceRoot":"","sources":["../../../../../../editor/ecs/component/editors/ecs/Trail3DEditor.js"],"names":[],"mappings":"AAKA;IACI;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;MA2CC;CACJ;6BA/C4B,8BAA8B;+BAF5B,6BAA6B"}
@@ -1,5 +1,6 @@
1
1
  import { Trail3DFlags } from "../../../../../src/engine/graphics/ecs/trail3d/Trail3DFlags.js";
2
2
  import { BitFlagsEditor } from "../common/BitFlagsEditor.js";
3
+ import { noEditor } from "../common/noEditor.js";
3
4
  import { ObjectEditor } from "../primitive/ObjectEditor.js";
4
5
 
5
6
  export class Trail3DEditor extends ObjectEditor {
@@ -38,6 +39,11 @@ export class Trail3DEditor extends ObjectEditor {
38
39
  },
39
40
  flags: {
40
41
  editor: new BitFlagsEditor(Trail3DFlags)
42
+ },
43
+ fade: {
44
+ // the default is one frozen curve every trail shares, which a generic object
45
+ // editor would write into; there is no curve field editor to clone it first yet
46
+ editor: noEditor()
41
47
  }
42
48
  }
43
49
  };
package/package.json CHANGED
@@ -6,7 +6,7 @@
6
6
  "description": "Advanced 3D game engine",
7
7
  "type": "module",
8
8
  "author": "Alexander Goldring",
9
- "version": "3.60.3",
9
+ "version": "3.60.4",
10
10
  "exports": {
11
11
  "./build/*": "./build/*",
12
12
  "./src/*": "./src/*",
@@ -143,7 +143,7 @@ Each stage is red-then-green.
143
143
  - [A] Aristidou, Chrysanthou, Lasenby, *Extending FABRIK with model constraints*, CAVW 2016
144
144
  - [C] Aristidou, Lasenby, *FABRIK*, Graphical Models 73, 2011
145
145
  - [D] Baerlocher, Boulic, *Parametrization and range of motion of the ball-and-socket joint*, 2001
146
- - [G] UE 5.4 source: `Plugins/Experimental/FullBodyIK/Source/PBIK/`, `AnimNode_FootPlacement`
146
+ - [G] Unreal Engine FullBodyIK (PBIK) and Foot Placement documentation
147
147
  - [N] ozz-animation `ik_two_bone_job.h`, `samples/foot_ik/sample_foot_ik.cc`
148
148
 
149
149
  ## 9. After implementation (2026-10-06)
@@ -55,8 +55,7 @@ export const ShadedGeometryFlags = {
55
55
  * clipmap's GI — see it, unless its material is transparent. Without it the primitive is drawn
56
56
  * by the camera and invisible to those rays: it occludes nothing, emits nothing and bounces
57
57
  * nothing into GI, which is what it is for — a primitive too small or too busy to be worth its
58
- * place there. On by default, as {@link CastShadow} is. Unreal's
59
- * `bAffectDistanceFieldLighting` is the same switch.
58
+ * place there. On by default, as {@link CastShadow} is.
60
59
  */
61
60
  SDFWorld: 128,
62
61
  };
@@ -0,0 +1,14 @@
1
+ /**
2
+ * The fade every {@link Trail3D} starts with: full opacity at birth, falling in a straight line to
3
+ * none at `maxAge`.
4
+ *
5
+ * **One instance, shared by every trail that has not been given a curve of its own**, so it is
6
+ * frozen all the way down — the curve, its key array and each key — and an edit in place throws
7
+ * rather than reshaping every default trail at once. To author a fade, assign a curve of your own;
8
+ * `clone()` of this one is an unfrozen copy to start from.
9
+ *
10
+ * @type {AnimationCurve}
11
+ */
12
+ export const TRAIL3D_FADE_LINEAR: AnimationCurve;
13
+ import { AnimationCurve } from "../../../animation/curve/AnimationCurve.js";
14
+ //# sourceMappingURL=TRAIL3D_FADE_LINEAR.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TRAIL3D_FADE_LINEAR.d.ts","sourceRoot":"","sources":["../../../../../../src/engine/graphics/ecs/trail3d/TRAIL3D_FADE_LINEAR.js"],"names":[],"mappings":"AAUA;;;;;;;;;;GAUG;AACH,kCAFU,cAAc,CAEgC;+BArBzB,4CAA4C"}
@@ -0,0 +1,22 @@
1
+ import { AnimationCurve } from "../../../animation/curve/AnimationCurve.js";
2
+
3
+ const curve = AnimationCurve.linear(0, 1, 1, 0);
4
+
5
+ for (const key of curve.keys) {
6
+ Object.freeze(key);
7
+ }
8
+
9
+ Object.freeze(curve.keys);
10
+
11
+ /**
12
+ * The fade every {@link Trail3D} starts with: full opacity at birth, falling in a straight line to
13
+ * none at `maxAge`.
14
+ *
15
+ * **One instance, shared by every trail that has not been given a curve of its own**, so it is
16
+ * frozen all the way down — the curve, its key array and each key — and an edit in place throws
17
+ * rather than reshaping every default trail at once. To author a fade, assign a curve of your own;
18
+ * `clone()` of this one is an unfrozen copy to start from.
19
+ *
20
+ * @type {AnimationCurve}
21
+ */
22
+ export const TRAIL3D_FADE_LINEAR = Object.freeze(curve);
@@ -54,14 +54,25 @@ declare class Trail3D {
54
54
  */
55
55
  distanceSinceLastUpdate: number;
56
56
  /**
57
- * RGB tints the tube; alpha is the trail's base opacity, which the
58
- * age-driven fade scales down to zero.
57
+ * RGB tints the tube; alpha is the trail's base opacity, which {@link fade}
58
+ * scales as each knot ages.
59
59
  *
60
60
  * Linear color space.
61
61
  * @readonly
62
62
  * @type {Color}
63
63
  */
64
64
  readonly color: Color;
65
+ /**
66
+ * Opacity over a knot's life. The curve's time is the knot's age over {@link maxAge} — `0` at
67
+ * birth, `1` when the knot disappears — and its value multiplies {@link color}'s alpha. The
68
+ * opacity meant is the trail's as seen, whatever the renderer has to do to the tube's faces to
69
+ * show it.
70
+ *
71
+ * Every trail starts on {@link TRAIL3D_FADE_LINEAR}, one shared instance, frozen — assign a
72
+ * curve of your own to reshape a trail's fade rather than editing that one.
73
+ * @type {AnimationCurve}
74
+ */
75
+ fade: AnimationCurve;
65
76
  /**
66
77
  * Where the trail's head sits relative to the entity, in the entity's LOCAL
67
78
  * frame: {@link Trail3DSystem} carries it through the entity's transform, so
@@ -158,7 +169,7 @@ declare class Trail3D {
158
169
  * Clear the trail's geometry and reset its state.
159
170
  */
160
171
  clear(): void;
161
- fromJSON({ maxAge, width, radialSegments, spawnMode, spawnDistance, textureURL, depthWrite, lightingEnabled, offset, color }: {
172
+ fromJSON({ maxAge, width, radialSegments, spawnMode, spawnDistance, textureURL, depthWrite, lightingEnabled, offset, color, fade }: {
162
173
  maxAge?: number;
163
174
  width?: number;
164
175
  radialSegments?: number;
@@ -174,6 +185,7 @@ declare class Trail3D {
174
185
  b: number;
175
186
  a: number;
176
187
  };
188
+ fade: any;
177
189
  }): void;
178
190
  toJSON(): {
179
191
  maxAge: number;
@@ -187,6 +199,9 @@ declare class Trail3D {
187
199
  b: number;
188
200
  a: number;
189
201
  };
202
+ fade: {
203
+ keys: any[];
204
+ };
190
205
  textureURL: string;
191
206
  depthWrite: boolean;
192
207
  lightingEnabled: boolean;
@@ -216,6 +231,7 @@ declare namespace Trail3D {
216
231
  }
217
232
  import { TrailSpawnMode } from "../../trail/TrailSpawnMode.js";
218
233
  import { Color } from "../../../../core/color/Color.js";
234
+ import { AnimationCurve } from "../../../animation/curve/AnimationCurve.js";
219
235
  import Vector3 from "../../../../core/geom/Vector3.js";
220
236
  import { TubeX } from "../../trail/tube/TubeX.js";
221
237
  import { TubeXMaterialSpec } from "../../trail/tube/TubeXMaterialSpec.js";
@@ -1 +1 @@
1
- {"version":3,"file":"Trail3D.d.ts","sourceRoot":"","sources":["../../../../../../src/engine/graphics/ecs/trail3d/Trail3D.js"],"names":[],"mappings":";AAgCA;;;;;GAKG;AACH;IA8PI,oCAMC;IAlQD;;;OAGG;IACH,QAFU,MAAM,CAES;IAEzB;;;OAGG;IACH,OAFU,MAAM,CAEM;IAEtB;;;;OAIG;IACH,gBAFU,MAAM,CAEyB;IAEzC;;;OAGG;IACH,WAFU,cAAc,GAAC,MAAM,CAEC;IAEhC;;;;OAIG;IACH,eAFU,MAAM,CAEuB;IAEvC;;;OAGG;IACH,MAFU,MAAM,CAEP;IAET;;OAEG;IACH,eAFU,MAAM,CAEE;IAElB;;;OAGG;IACH,qBAFU,MAAM,CAEQ;IAExB;;;OAGG;IACH,yBAFU,MAAM,CAEY;IAE5B;;;;;;;OAOG;IACH,gBAFU,KAAK,CAEe;IAE9B;;;;;;;OAOG;IACH,iBAFU,OAAO,CAEM;IAEvB;;;OAGG;IACH,MAFU,KAAK,GAAC,IAAI,CAER;IAEZ;;;;;;;OAOG;IACH,MAFU,OAAK,IAAI,CAEP;IAEZ;;;OAGG;IACH,UAFU,iBAAiB,CAEQ;IAEnC;;;OAGG;IACH,cAFU,SAAS,CAEG;IAEtB;;;OAGG;IACH,cAAsB;IAEtB,gBAgBC;IAMD,0BAEC;IAND,yBAEC;IAUD,2BAEC;IAND,0BAEC;IA0BD;;OAEG;IACH,gCAQC;IA/BD;;;;;;;;;;;;;;;OAeG;IACH,+BAEC;IAeD;;;OAGG;IACH,cAHW,MAAM,GAAC,YAAY,GACjB,IAAI,CAIhB;IAED;;;OAGG;IACH,gBAHW,MAAM,GAAC,YAAY,GACjB,IAAI,CAIhB;IAED;;;OAGG;IACH,gBAHW,MAAM,GAAC,YAAY,SACnB,OAAO,QAQjB;IAED;;;OAGG;IACH,cAHW,MAAM,GAAC,YAAY,GACjB,OAAO,CAInB;IAED;;;;;;OAMG;IACH,qBAFW,MAAM,QAShB;IAED;;OAEG;IACH,cAmBC;IAUD;;;;;;;;;;;;;;;;aAwBC;IAED;;;;;;;;;;;;;;;;;;;;MAaC;IAED;;OAEG;IACH,cAFW,OAAO,WAcjB;IAkDD;;;;;;OAMG;IACH,cANW,MAAM,KACN,MAAM,KACN,MAAM,YACN,MAAM,GACJ,OAAO,CAiEnB;;CAEJ;;;;;+BArd8B,+BAA+B;sBANxC,iCAAiC;oBACnC,kCAAkC;sBAQhC,2BAA2B;kCACf,uCAAuC;0BAX/C,yCAAyC;6BAYtC,mBAAmB"}
1
+ {"version":3,"file":"Trail3D.d.ts","sourceRoot":"","sources":["../../../../../../src/engine/graphics/ecs/trail3d/Trail3D.js"],"names":[],"mappings":";AAkCA;;;;;GAKG;AACH;IA0QI,oCAMC;IA9QD;;;OAGG;IACH,QAFU,MAAM,CAES;IAEzB;;;OAGG;IACH,OAFU,MAAM,CAEM;IAEtB;;;;OAIG;IACH,gBAFU,MAAM,CAEyB;IAEzC;;;OAGG;IACH,WAFU,cAAc,GAAC,MAAM,CAEC;IAEhC;;;;OAIG;IACH,eAFU,MAAM,CAEuB;IAEvC;;;OAGG;IACH,MAFU,MAAM,CAEP;IAET;;OAEG;IACH,eAFU,MAAM,CAEE;IAElB;;;OAGG;IACH,qBAFU,MAAM,CAEQ;IAExB;;;OAGG;IACH,yBAFU,MAAM,CAEY;IAE5B;;;;;;;OAOG;IACH,gBAFU,KAAK,CAEe;IAE9B;;;;;;;;;OASG;IACH,MAFU,cAAc,CAEG;IAE3B;;;;;;;OAOG;IACH,iBAFU,OAAO,CAEM;IAEvB;;;OAGG;IACH,MAFU,KAAK,GAAC,IAAI,CAER;IAEZ;;;;;;;OAOG;IACH,MAFU,OAAK,IAAI,CAEP;IAEZ;;;OAGG;IACH,UAFU,iBAAiB,CAEQ;IAEnC;;;OAGG;IACH,cAFU,SAAS,CAEG;IAEtB;;;OAGG;IACH,cAAsB;IAEtB,gBAgBC;IAMD,0BAEC;IAND,yBAEC;IAUD,2BAEC;IAND,0BAEC;IA0BD;;OAEG;IACH,gCAQC;IA/BD;;;;;;;;;;;;;;;OAeG;IACH,+BAEC;IAeD;;;OAGG;IACH,cAHW,MAAM,GAAC,YAAY,GACjB,IAAI,CAIhB;IAED;;;OAGG;IACH,gBAHW,MAAM,GAAC,YAAY,GACjB,IAAI,CAIhB;IAED;;;OAGG;IACH,gBAHW,MAAM,GAAC,YAAY,SACnB,OAAO,QAQjB;IAED;;;OAGG;IACH,cAHW,MAAM,GAAC,YAAY,GACjB,OAAO,CAInB;IAED;;;;;;OAMG;IACH,qBAFW,MAAM,QAShB;IAED;;OAEG;IACH,cAmBC;IAUD;;;;;;;;;;;;;;;;;aAmCC;IAED;;;;;;;;;;;;;;;;;;;;;;;MAcC;IAED;;OAEG;IACH,cAFW,OAAO,WAejB;IAkDD;;;;;;OAMG;IACH,cANW,MAAM,KACN,MAAM,KACN,MAAM,YACN,MAAM,GACJ,OAAO,CAiEnB;;CAEJ;;;;;+BA/e8B,+BAA+B;sBAPxC,iCAAiC;+BAMxB,4CAA4C;oBALvD,kCAAkC;sBAShC,2BAA2B;kCACf,uCAAuC;0BAZ/C,yCAAyC;6BActC,mBAAmB"}
@@ -7,11 +7,13 @@ import { v3_distance } from "../../../../core/geom/vec3/v3_distance.js";
7
7
  import { v3_length } from "../../../../core/geom/vec3/v3_length.js";
8
8
  import { clamp01 } from "../../../../core/math/clamp01.js";
9
9
  import { EPSILON } from "../../../../core/math/EPSILON.js";
10
+ import { AnimationCurve } from "../../../animation/curve/AnimationCurve.js";
10
11
  import { TrailSpawnMode } from "../../trail/TrailSpawnMode.js";
11
12
  import { TUBE_ATTRIBUTE_ADDRESS_AGE } from "../../trail/tube/tube_attributes_spec.js";
12
13
  import { tube_frame_transport } from "../../trail/tube/tube_frame.js";
13
14
  import { TubeX } from "../../trail/tube/TubeX.js";
14
15
  import { TubeXMaterialSpec } from "../../trail/tube/TubeXMaterialSpec.js";
16
+ import { TRAIL3D_FADE_LINEAR } from "./TRAIL3D_FADE_LINEAR.js";
15
17
  import { Trail3DFlags } from "./Trail3DFlags.js";
16
18
 
17
19
  const DEFAULT_FLAGS = Trail3DFlags.Spawning | Trail3DFlags.Aging | Trail3DFlags.BoundsNeedUpdate;
@@ -94,8 +96,8 @@ class Trail3D {
94
96
  distanceSinceLastUpdate = 0;
95
97
 
96
98
  /**
97
- * RGB tints the tube; alpha is the trail's base opacity, which the
98
- * age-driven fade scales down to zero.
99
+ * RGB tints the tube; alpha is the trail's base opacity, which {@link fade}
100
+ * scales as each knot ages.
99
101
  *
100
102
  * Linear color space.
101
103
  * @readonly
@@ -103,6 +105,18 @@ class Trail3D {
103
105
  */
104
106
  color = new Color(1, 1, 1, 1);
105
107
 
108
+ /**
109
+ * Opacity over a knot's life. The curve's time is the knot's age over {@link maxAge} — `0` at
110
+ * birth, `1` when the knot disappears — and its value multiplies {@link color}'s alpha. The
111
+ * opacity meant is the trail's as seen, whatever the renderer has to do to the tube's faces to
112
+ * show it.
113
+ *
114
+ * Every trail starts on {@link TRAIL3D_FADE_LINEAR}, one shared instance, frozen — assign a
115
+ * curve of your own to reshape a trail's fade rather than editing that one.
116
+ * @type {AnimationCurve}
117
+ */
118
+ fade = TRAIL3D_FADE_LINEAR;
119
+
106
120
  /**
107
121
  * Where the trail's head sits relative to the entity, in the entity's LOCAL
108
122
  * frame: {@link Trail3DSystem} carries it through the entity's transform, so
@@ -308,7 +322,8 @@ class Trail3D {
308
322
  depthWrite = false,
309
323
  lightingEnabled = false,
310
324
  offset = Vector3.zero,
311
- color = { r: 1, g: 1, b: 1, a: 1 }
325
+ color = { r: 1, g: 1, b: 1, a: 1 },
326
+ fade
312
327
  }) {
313
328
 
314
329
  this.maxAge = maxAge;
@@ -322,6 +337,16 @@ class Trail3D {
322
337
 
323
338
  this.offset.fromJSON(offset);
324
339
  this.color.fromJSON(color);
340
+
341
+ if (fade === undefined) {
342
+ this.fade = TRAIL3D_FADE_LINEAR;
343
+ } else {
344
+ const curve = new AnimationCurve();
345
+
346
+ curve.fromJSON(fade);
347
+
348
+ this.fade = curve;
349
+ }
325
350
  }
326
351
 
327
352
  toJSON() {
@@ -332,6 +357,7 @@ class Trail3D {
332
357
  spawnMode: this.spawnMode,
333
358
  spawnDistance: this.spawnDistance,
334
359
  color: this.color.toJSON(),
360
+ fade: this.fade.toJSON(),
335
361
  textureURL: this.textureURL,
336
362
  depthWrite: this.depthWrite,
337
363
  lightingEnabled: this.lightingEnabled,
@@ -352,6 +378,7 @@ class Trail3D {
352
378
  && this.spawnMode === other.spawnMode
353
379
  && this.spawnDistance === other.spawnDistance
354
380
  && this.color.equals(other.color)
381
+ && this.fade.equals(other.fade)
355
382
  && this.offset.equals(other.offset)
356
383
  ;
357
384
  }
@@ -470,7 +497,7 @@ class Trail3D {
470
497
  // set head to fresh value; alpha follows the same fade law the simulator
471
498
  // applies every tick, so the knot doesn't pop when it stops being the head
472
499
  tube.setKnotAttribute_Scalar(head_index, TUBE_ATTRIBUTE_ADDRESS_AGE, head_age);
473
- tube.setKnotAlpha(head_index, (1 - clamp01(head_age / maxAge)) * this.color.w);
500
+ tube.setKnotAlpha(head_index, this.fade.evaluate(clamp01(head_age / maxAge)) * this.color.w);
474
501
  tube.setKnotThickness(head_index, this.width);
475
502
 
476
503
  return head_position_changed;
@@ -34,7 +34,7 @@ export function advance_trail(trail, transform, timeDelta, simulator) {
34
34
 
35
35
  const age_offset = max2(0, trail.maxAge - trail.time);
36
36
 
37
- simulator.update(tube, trail.maxAge, timeDelta, trail.color.w);
37
+ simulator.update(tube, trail.maxAge, timeDelta, trail.color.w, trail.fade);
38
38
 
39
39
  if (trail.getFlag(Trail3DFlags.Spawning)) {
40
40
  trail.updateHead(v3_head.x, v3_head.y, v3_head.z, age_offset);
@@ -1,16 +1,16 @@
1
1
  /**
2
2
  * Fixed-function simulation for {@link TubeX} tubes — the 3D analogue of
3
- * {@link RibbonXFixedPhysicsSimulator}. Ages every knot, fades its alpha from
4
- * `alpha` to zero as it approaches `max_age`, and flows the along-tube UV
5
- * coordinate with age.
3
+ * {@link RibbonXFixedPhysicsSimulator}. Ages every knot, sets its alpha to `alpha` times the `fade`
4
+ * curve at its age relative to `max_age`, and flows the along-tube UV coordinate with age.
6
5
  */
7
6
  export class TubeXFixedPhysicsSimulator {
8
7
  /**
9
8
  * @param {TubeX} tube
10
9
  * @param {number} max_age
11
10
  * @param {number} timeDelta
12
- * @param {number} [alpha] base alpha of an age-0 knot; the age fade scales it
11
+ * @param {number} alpha base alpha of a knot; the fade scales it
12
+ * @param {AnimationCurve} fade opacity over the knot's age relative to `max_age`, `0..1`
13
13
  */
14
- update(tube: TubeX, max_age: number, timeDelta: number, alpha?: number): void;
14
+ update(tube: TubeX, max_age: number, timeDelta: number, alpha: number, fade: AnimationCurve): void;
15
15
  }
16
16
  //# sourceMappingURL=TubeXFixedPhysicsSimulator.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"TubeXFixedPhysicsSimulator.d.ts","sourceRoot":"","sources":["../../../../../../../src/engine/graphics/trail/tube/simulator/TubeXFixedPhysicsSimulator.js"],"names":[],"mappings":"AAGA;;;;;GAKG;AACH;IAEI;;;;;OAKG;IACH,6BAJW,MAAM,aACN,MAAM,UACN,MAAM,QAiBhB;CACJ"}
1
+ {"version":3,"file":"TubeXFixedPhysicsSimulator.d.ts","sourceRoot":"","sources":["../../../../../../../src/engine/graphics/trail/tube/simulator/TubeXFixedPhysicsSimulator.js"],"names":[],"mappings":"AAGA;;;;GAIG;AACH;IAEI;;;;;;OAMG;IACH,6BALW,MAAM,aACN,MAAM,SACN,MAAM,8BAkBhB;CACJ"}
@@ -3,9 +3,8 @@ import { TUBE_ATTRIBUTE_ADDRESS_AGE, TUBE_ATTRIBUTE_ADDRESS_UV_V } from "../tube
3
3
 
4
4
  /**
5
5
  * Fixed-function simulation for {@link TubeX} tubes — the 3D analogue of
6
- * {@link RibbonXFixedPhysicsSimulator}. Ages every knot, fades its alpha from
7
- * `alpha` to zero as it approaches `max_age`, and flows the along-tube UV
8
- * coordinate with age.
6
+ * {@link RibbonXFixedPhysicsSimulator}. Ages every knot, sets its alpha to `alpha` times the `fade`
7
+ * curve at its age relative to `max_age`, and flows the along-tube UV coordinate with age.
9
8
  */
10
9
  export class TubeXFixedPhysicsSimulator {
11
10
 
@@ -13,9 +12,10 @@ export class TubeXFixedPhysicsSimulator {
13
12
  * @param {TubeX} tube
14
13
  * @param {number} max_age
15
14
  * @param {number} timeDelta
16
- * @param {number} [alpha] base alpha of an age-0 knot; the age fade scales it
15
+ * @param {number} alpha base alpha of a knot; the fade scales it
16
+ * @param {AnimationCurve} fade opacity over the knot's age relative to `max_age`, `0..1`
17
17
  */
18
- update(tube, max_age, timeDelta, alpha = 1) {
18
+ update(tube, max_age, timeDelta, alpha, fade) {
19
19
 
20
20
  const n = tube.getCount();
21
21
 
@@ -25,7 +25,7 @@ export class TubeXFixedPhysicsSimulator {
25
25
  const relative_age = clamp01(age / max_age);
26
26
 
27
27
  // fade out with age
28
- tube.setKnotAlpha(i, (1.0 - relative_age) * alpha);
28
+ tube.setKnotAlpha(i, fade.evaluate(relative_age) * alpha);
29
29
 
30
30
  // flow the along-tube UV with age
31
31
  tube.setKnotAttribute_Scalar(i, TUBE_ATTRIBUTE_ADDRESS_UV_V, relative_age);
@@ -20,6 +20,16 @@
20
20
  * base opacity — as the fourth channel. A knot that has aged out is written transparent rather than
21
21
  * dropped, which is what the index buffer expects, and blending takes it out of the picture.
22
22
  *
23
+ * **A knot's alpha is the opacity of the tube as seen, not of one face of it.** The tube is a closed
24
+ * hull drawn from both sides, so a line of sight through it crosses two faces, and the transparency
25
+ * pipeline composes their transmittances: written as is, an alpha of 0.5 would cover 0.75, and a
26
+ * linear fade would hold near solid for most of the trail's life and collapse at its tail. Each face
27
+ * therefore takes half the optical depth, `1 - √(1 - α)`, and the two together cover exactly `α`.
28
+ * This is the one place that knows the hull is crossed twice, so it is the one place that splits it;
29
+ * a ray through a bend crosses the tube twice over, and that overlap is real and reads denser. The
30
+ * alpha is clamped to `0..1` first, the range an opacity has — an authored fade curve may overshoot
31
+ * it, and the square root has no answer above one.
32
+ *
23
33
  * @param {Geometry} geometry
24
34
  * @param {TubeX} tube
25
35
  * @returns {void}
@@ -1 +1 @@
1
- {"version":3,"file":"trail_tube_write_geometry.d.ts","sourceRoot":"","sources":["../../../../../src/engine/graphics3/trail/trail_tube_write_geometry.js"],"names":[],"mappings":"AAkBA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,4EAFa,IAAI,CAgEhB"}
1
+ {"version":3,"file":"trail_tube_write_geometry.d.ts","sourceRoot":"","sources":["../../../../../src/engine/graphics3/trail/trail_tube_write_geometry.js"],"names":[],"mappings":"AAmBA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,4EAFa,IAAI,CAiEhB"}
@@ -1,4 +1,5 @@
1
1
  import { assert } from "../../../core/assert.js";
2
+ import { clamp01 } from "../../../core/math/clamp01.js";
2
3
  import {
3
4
  geometry_obtain_attribute,
4
5
  geometry_obtain_index
@@ -38,6 +39,16 @@ const TWO_PI = Math.PI * 2;
38
39
  * base opacity — as the fourth channel. A knot that has aged out is written transparent rather than
39
40
  * dropped, which is what the index buffer expects, and blending takes it out of the picture.
40
41
  *
42
+ * **A knot's alpha is the opacity of the tube as seen, not of one face of it.** The tube is a closed
43
+ * hull drawn from both sides, so a line of sight through it crosses two faces, and the transparency
44
+ * pipeline composes their transmittances: written as is, an alpha of 0.5 would cover 0.75, and a
45
+ * linear fade would hold near solid for most of the trail's life and collapse at its tail. Each face
46
+ * therefore takes half the optical depth, `1 - √(1 - α)`, and the two together cover exactly `α`.
47
+ * This is the one place that knows the hull is crossed twice, so it is the one place that splits it;
48
+ * a ray through a bend crosses the tube twice over, and that overlap is real and reads denser. The
49
+ * alpha is clamped to `0..1` first, the range an opacity has — an authored fade curve may overshoot
50
+ * it, and the square root has no answer above one.
51
+ *
41
52
  * @param {Geometry} geometry
42
53
  * @param {TubeX} tube
43
54
  * @returns {void}
@@ -94,7 +105,8 @@ export function trail_tube_write_geometry(geometry, tube) {
94
105
  colors[o] = halves[h];
95
106
  colors[o + 1] = halves[h + 1];
96
107
  colors[o + 2] = halves[h + 2];
97
- colors[o + 3] = floats[f + TUBE_ATTRIBUTE_ADDRESS_ALPHA];
108
+ // half the optical depth: the face behind this one carries the other half
109
+ colors[o + 3] = 1 - Math.sqrt(1 - clamp01(floats[f + TUBE_ATTRIBUTE_ADDRESS_ALPHA]));
98
110
  }
99
111
 
100
112
  const source_indices = tube.indices;
@@ -14,8 +14,8 @@ export function cloth_edge_incidence(node_count: number, edge: Int32Array): {
14
14
  * Distance from every node to the nearest of a set of source nodes, along the
15
15
  * edges, and which source that is.
16
16
  *
17
- * A node marked in `blocked` can be reached but is never passed through. That is
18
- * Chaos Cloth's rule for its long-range attachments (`ClothTetherData.cpp`):
17
+ * A node marked in `blocked` can be reached but is never passed through.
18
+ * That is Chaos Cloth's rule for its long-range attachments:
19
19
  * a path from one island of held particles does not run through another, because
20
20
  * the far side of another island is that island's business.
21
21
  *
@@ -65,8 +65,8 @@ export function cloth_edge_incidence(node_count, edge) {
65
65
  * Distance from every node to the nearest of a set of source nodes, along the
66
66
  * edges, and which source that is.
67
67
  *
68
- * A node marked in `blocked` can be reached but is never passed through. That is
69
- * Chaos Cloth's rule for its long-range attachments (`ClothTetherData.cpp`):
68
+ * A node marked in `blocked` can be reached but is never passed through.
69
+ * That is Chaos Cloth's rule for its long-range attachments:
70
70
  * a path from one island of held particles does not run through another, because
71
71
  * the far side of another island is that island's business.
72
72
  *
@@ -23,6 +23,7 @@ has the two lines that put it there. Another scene loads with `?scene=/some/othe
23
23
  | Indirect light | the probe clipmap, or IBL to compare against |
24
24
  | Debug view | the brick clipmap as the probes' rays see it; the geometry caches' mesh SDF; the hits' albedo from the cards |
25
25
  | Path tracer (P) | the accumulating path tracer's image in place of the rasterized one, as ground truth; TAA is off while it is on and comes back after |
26
+ | Re-sampling (R) | each probe update pools its rays with what four of its neighbours have already seen, where their depth confirms it (`ProbeClipmapSettings.resampling`); off by default, and measured slower to converge and a few per cent brighter on Sponza (PROBE_CLIPMAP_PROPOSAL_2026_10_27.md §8.8) |
26
27
  | Sun height, Sun turn | where the sun stands |
27
28
  | Point lights | eight shadowed point lights along the scene's long axis, which the probes' hits take as local lights |
28
29
  | TAA | the resolve the probe pick's noise is meant for |
@@ -108,6 +108,11 @@
108
108
  <span>Path tracer <span class="hint">— ground truth; P flips it</span></span>
109
109
  </label>
110
110
 
111
+ <label>
112
+ <input type="checkbox" id="resampling">
113
+ <span>Re-sampling <span class="hint">— probes take their neighbours' radiance; R flips it</span></span>
114
+ </label>
115
+
111
116
  <hr>
112
117
 
113
118
  <label>
@@ -1 +1 @@
1
- {"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../../../../../src/shade/playground/probe_clipmap/main.js"],"names":[],"mappings":"AA0FA;;;;;GAKG;AACH,yBAFa,QAAQ,IAAI,CAAC,CA8QzB"}
1
+ {"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../../../../../src/shade/playground/probe_clipmap/main.js"],"names":[],"mappings":"AA0FA;;;;;GAKG;AACH,yBAFa,QAAQ,IAAI,CAAC,CA4RzB"}
@@ -282,12 +282,26 @@ export async function start() {
282
282
 
283
283
  path_tracing_toggle.addEventListener('change', event => set_path_tracing(event.target.checked));
284
284
 
285
+ const resampling_toggle = element('resampling');
286
+
287
+ resampling_toggle.checked = renderer.probe_clipmap.resampling;
288
+ resampling_toggle.addEventListener('change', event => {
289
+ renderer.probe_clipmap.resampling = event.target.checked;
290
+ });
291
+
285
292
  document.addEventListener('keydown', event => {
286
- // not while typing a number, nor in a select, where P picks an option
293
+ // not while typing a number, nor in a select, where P and R pick an option
287
294
  const typing = event.target === bake_budget || event.target instanceof HTMLSelectElement;
288
295
 
289
- if (event.code === 'KeyP' && event.repeat === false && typing === false) {
296
+ if (event.repeat || typing) {
297
+ return;
298
+ }
299
+
300
+ if (event.code === 'KeyP') {
290
301
  set_path_tracing(!renderer.feature_path_tracing_enabled);
302
+ } else if (event.code === 'KeyR') {
303
+ resampling_toggle.checked = !resampling_toggle.checked;
304
+ renderer.probe_clipmap.resampling = resampling_toggle.checked;
291
305
  }
292
306
  });
293
307
 
@@ -273,7 +273,7 @@ every vertex shader over a meshlet emits that instance's triangle corners in the
273
273
  `triangle_list_reverse_winding`. The draw is non-indexed and the vertex shader resolves its own
274
274
  index, so which corner lands where is already the shader's choice; reversing it costs a `select`,
275
275
  no second pipeline, and no fourth field in the bucket key. It is what Nanite does for the same
276
- reason (`FInstanceSceneData::DeterminantSign`, read in `HWRasterizeVS`), and what the glTF spec
276
+ reason, and what the glTF spec
277
277
  requires of a conformant renderer. The tangent frame's handedness flips with it.
278
278
 
279
279
  The parameters remain, and now read as diagnostics rather than as fixes:
@@ -1,21 +1,19 @@
1
1
  /**
2
2
  * Directions are drawn on an 8x8 grid, stratified in azimuth and in the cosine of the polar
3
- * angle. Matches Hillaire's `SQRTSAMPLECOUNT`.
3
+ * angle.
4
4
  * @type {number}
5
5
  */
6
6
  export const MULTISCATTER_LUT_SQRT_SAMPLES: number;
7
7
  /**
8
8
  * One thread integrates one direction, so this is also the workgroup size.
9
9
  *
10
- * 64 is Hillaire's `[numthreads(1,1,64)]` count. It is a multiple of every legal subgroup
11
- * size up to 64, and the reduction handles a subgroup wider than the workgroup, so no
12
- * hardware needs a different number here.
10
+ * 64 is a multiple of every legal subgroup size up to 64, and the reduction handles a
11
+ * subgroup wider than the workgroup, so no hardware needs a different number here.
13
12
  * @type {number}
14
13
  */
15
14
  export const MULTISCATTER_LUT_DIRECTION_COUNT: number;
16
15
  /**
17
- * Ray-march steps per direction. Hillaire's `SampleCountIni` for this LUT; the comment there
18
- * reads "a minimum set of step is required for accuracy unfortunately".
16
+ * Ray-march steps per direction for this LUT.
19
17
  *
20
18
  * Compile-time on purpose: as a uniform the inner loop cannot be unrolled and the trip count
21
19
  * has to be re-tested against an f32 every iteration.
@@ -1 +1 @@
1
- {"version":3,"file":"MULTISCATTER_LUT_CONFIG.d.ts","sourceRoot":"","sources":["../../../../../src/shade/renderer/atmosphere/MULTISCATTER_LUT_CONFIG.js"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,4CAFU,MAAM,CAE+B;AAE/C;;;;;;;GAOG;AACH,+CAFU,MAAM,CAE8F;AAE9G;;;;;;;GAOG;AACH,qCAFU,MAAM,CAEyB;AAEzC;;;;;;;GAOG;AACH,qDAFU,MAAM,CAE2E"}
1
+ {"version":3,"file":"MULTISCATTER_LUT_CONFIG.d.ts","sourceRoot":"","sources":["../../../../../src/shade/renderer/atmosphere/MULTISCATTER_LUT_CONFIG.js"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,4CAFU,MAAM,CAE+B;AAE/C;;;;;;GAMG;AACH,+CAFU,MAAM,CAE8F;AAE9G;;;;;;GAMG;AACH,qCAFU,MAAM,CAEyB;AAEzC;;;;;;;GAOG;AACH,qDAFU,MAAM,CAE2E"}
@@ -1,36 +1,34 @@
1
- /**
2
- * Directions are drawn on an 8x8 grid, stratified in azimuth and in the cosine of the polar
3
- * angle. Matches Hillaire's `SQRTSAMPLECOUNT`.
4
- * @type {number}
5
- */
6
- export const MULTISCATTER_LUT_SQRT_SAMPLES = 8;
7
-
8
- /**
9
- * One thread integrates one direction, so this is also the workgroup size.
10
- *
11
- * 64 is Hillaire's `[numthreads(1,1,64)]` count. It is a multiple of every legal subgroup
12
- * size up to 64, and the reduction handles a subgroup wider than the workgroup, so no
13
- * hardware needs a different number here.
14
- * @type {number}
15
- */
16
- export const MULTISCATTER_LUT_DIRECTION_COUNT = MULTISCATTER_LUT_SQRT_SAMPLES * MULTISCATTER_LUT_SQRT_SAMPLES;
17
-
18
- /**
19
- * Ray-march steps per direction. Hillaire's `SampleCountIni` for this LUT; the comment there
20
- * reads "a minimum set of step is required for accuracy unfortunately".
21
- *
22
- * Compile-time on purpose: as a uniform the inner loop cannot be unrolled and the trip count
23
- * has to be re-tested against an f32 every iteration.
24
- * @type {number}
25
- */
26
- export const MULTISCATTER_LUT_STEPS = 20;
27
-
28
- /**
29
- * Upper bound on the number of subgroups in one workgroup, which is how many partial sums
30
- * the cross-subgroup reduction has to combine.
31
- *
32
- * WGSL pins the subgroup size to a power of two in [4, 128], so the worst case is
33
- * {@link MULTISCATTER_LUT_DIRECTION_COUNT} / 4.
34
- * @type {number}
35
- */
36
- export const MULTISCATTER_LUT_MAX_SUBGROUP_PARTIALS = MULTISCATTER_LUT_DIRECTION_COUNT / 4;
1
+ /**
2
+ * Directions are drawn on an 8x8 grid, stratified in azimuth and in the cosine of the polar
3
+ * angle.
4
+ * @type {number}
5
+ */
6
+ export const MULTISCATTER_LUT_SQRT_SAMPLES = 8;
7
+
8
+ /**
9
+ * One thread integrates one direction, so this is also the workgroup size.
10
+ *
11
+ * 64 is a multiple of every legal subgroup size up to 64, and the reduction handles a
12
+ * subgroup wider than the workgroup, so no hardware needs a different number here.
13
+ * @type {number}
14
+ */
15
+ export const MULTISCATTER_LUT_DIRECTION_COUNT = MULTISCATTER_LUT_SQRT_SAMPLES * MULTISCATTER_LUT_SQRT_SAMPLES;
16
+
17
+ /**
18
+ * Ray-march steps per direction for this LUT.
19
+ *
20
+ * Compile-time on purpose: as a uniform the inner loop cannot be unrolled and the trip count
21
+ * has to be re-tested against an f32 every iteration.
22
+ * @type {number}
23
+ */
24
+ export const MULTISCATTER_LUT_STEPS = 20;
25
+
26
+ /**
27
+ * Upper bound on the number of subgroups in one workgroup, which is how many partial sums
28
+ * the cross-subgroup reduction has to combine.
29
+ *
30
+ * WGSL pins the subgroup size to a power of two in [4, 128], so the worst case is
31
+ * {@link MULTISCATTER_LUT_DIRECTION_COUNT} / 4.
32
+ * @type {number}
33
+ */
34
+ export const MULTISCATTER_LUT_MAX_SUBGROUP_PARTIALS = MULTISCATTER_LUT_DIRECTION_COUNT / 4;
@@ -6,7 +6,7 @@
6
6
  * mean, which is what the paper's uniform phase p_u = 1/(4*PI) reduces to.
7
7
  *
8
8
  * Splitting one direction out of the loop is what lets `shader_multiscatter_lut` put a
9
- * thread on each of the 64 directions, the way Hillaire's `NewMultiScattCS` does.
9
+ * thread on each of the 64 directions.
10
10
  *
11
11
  * `pos` is assumed to be inside the atmosphere and at or above the surface, which is what
12
12
  * makes the far root of the outer sphere the atmosphere exit.
@@ -1 +1 @@
1
- {"version":3,"file":"chunk_get_multiscatter_values.d.ts","sourceRoot":"","sources":["../../../../../src/shade/renderer/atmosphere/chunk_get_multiscatter_values.js"],"names":[],"mappings":"AAuBA;;;;;;;;;;;;;;;;GAgBG;AACH,4CAFU,SAAS,CA4HlB;0BAjJyB,iCAAiC"}
1
+ {"version":3,"file":"chunk_get_multiscatter_values.d.ts","sourceRoot":"","sources":["../../../../../src/shade/renderer/atmosphere/chunk_get_multiscatter_values.js"],"names":[],"mappings":"AAuBA;;;;;;;;;;;;;;;;GAgBG;AACH,4CAFU,SAAS,CA2HlB;0BAhJyB,iCAAiC"}