@expofp/renderer 0.0.0-experimental.2d6c681

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 (280) hide show
  1. package/README.md +145 -0
  2. package/dist/Renderer.d.ts +102 -0
  3. package/dist/Renderer.js +96 -0
  4. package/dist/controls/CameraControls.d.ts +108 -0
  5. package/dist/controls/CameraControls.js +203 -0
  6. package/dist/controls/Controls.d.ts +53 -0
  7. package/dist/controls/Controls.js +54 -0
  8. package/dist/controls/camera-api.d.ts +226 -0
  9. package/dist/controls/camera-api.js +179 -0
  10. package/dist/controls/gestures.d.ts +71 -0
  11. package/dist/controls/gestures.js +98 -0
  12. package/dist/controls/handlers/DoubleTapZoom.d.ts +23 -0
  13. package/dist/controls/handlers/DoubleTapZoom.js +82 -0
  14. package/dist/controls/handlers/Inertia.d.ts +60 -0
  15. package/dist/controls/handlers/Inertia.js +222 -0
  16. package/dist/controls/handlers/Pan.d.ts +23 -0
  17. package/dist/controls/handlers/Pan.js +212 -0
  18. package/dist/controls/handlers/Pitch.d.ts +19 -0
  19. package/dist/controls/handlers/Pitch.js +200 -0
  20. package/dist/controls/handlers/Roll.d.ts +30 -0
  21. package/dist/controls/handlers/Roll.js +196 -0
  22. package/dist/controls/handlers/Zoom.d.ts +28 -0
  23. package/dist/controls/handlers/Zoom.js +34 -0
  24. package/dist/controls/hooks.d.ts +136 -0
  25. package/dist/controls/hooks.js +200 -0
  26. package/dist/controls/index.d.ts +9 -0
  27. package/dist/controls/index.js +7 -0
  28. package/dist/controls/zoom-to.d.ts +89 -0
  29. package/dist/controls/zoom-to.js +91 -0
  30. package/dist/core/InstanceUniforms.d.ts +108 -0
  31. package/dist/core/InstanceUniforms.js +298 -0
  32. package/dist/core/SquareDataTexture.d.ts +277 -0
  33. package/dist/core/SquareDataTexture.js +476 -0
  34. package/dist/core/batch/BatchedMesh.d.ts +74 -0
  35. package/dist/core/batch/BatchedMesh.js +155 -0
  36. package/dist/core/instanced/FrustumCulling.d.ts +141 -0
  37. package/dist/core/instanced/FrustumCulling.js +246 -0
  38. package/dist/core/instanced/InstancedMesh.d.ts +302 -0
  39. package/dist/core/instanced/InstancedMesh.js +497 -0
  40. package/dist/core/instanced/Instances.d.ts +119 -0
  41. package/dist/core/instanced/Instances.js +230 -0
  42. package/dist/core/instanced/Raycasting.d.ts +118 -0
  43. package/dist/core/instanced/Raycasting.js +134 -0
  44. package/dist/core/instanced/instanced-material.d.ts +77 -0
  45. package/dist/core/instanced/instanced-material.js +228 -0
  46. package/dist/core/lines/BatchedLines.d.ts +137 -0
  47. package/dist/core/lines/BatchedLines.js +215 -0
  48. package/dist/core/lines/FrustumCullingLines.d.ts +44 -0
  49. package/dist/core/lines/FrustumCullingLines.js +85 -0
  50. package/dist/core/lines/RaycastingLines.d.ts +45 -0
  51. package/dist/core/lines/RaycastingLines.js +91 -0
  52. package/dist/core/lines/lines-material.d.ts +47 -0
  53. package/dist/core/lines/lines-material.js +124 -0
  54. package/dist/core/shader-source.d.ts +70 -0
  55. package/dist/core/shader-source.js +110 -0
  56. package/dist/core/sprites/BatchedSprites.d.ts +115 -0
  57. package/dist/core/sprites/BatchedSprites.js +173 -0
  58. package/dist/core/sprites/RaycastingSprites.d.ts +41 -0
  59. package/dist/core/sprites/RaycastingSprites.js +71 -0
  60. package/dist/core/sprites/sprite-material.d.ts +59 -0
  61. package/dist/core/sprites/sprite-material.js +192 -0
  62. package/dist/core/text/BatchedText.d.ts +233 -0
  63. package/dist/core/text/BatchedText.js +473 -0
  64. package/dist/core/text/MemberTexture.d.ts +90 -0
  65. package/dist/core/text/MemberTexture.js +289 -0
  66. package/dist/core/text/Text.d.ts +88 -0
  67. package/dist/core/text/Text.js +230 -0
  68. package/dist/core/text/batched-text-material.d.ts +56 -0
  69. package/dist/core/text/batched-text-material.js +123 -0
  70. package/dist/core/text/glyph-packing.d.ts +17 -0
  71. package/dist/core/text/glyph-packing.js +61 -0
  72. package/dist/core/typed-array.d.ts +16 -0
  73. package/dist/core/typed-array.js +20 -0
  74. package/dist/debug/components/DebugPanelProvider.d.ts +20 -0
  75. package/dist/debug/components/DebugPanelProvider.js +50 -0
  76. package/dist/debug/components/DebugTools.d.ts +22 -0
  77. package/dist/debug/components/DebugTools.js +28 -0
  78. package/dist/debug/components/Gui.d.ts +22 -0
  79. package/dist/debug/components/Gui.js +42 -0
  80. package/dist/debug/components/MemoryInfo.d.ts +22 -0
  81. package/dist/debug/components/MemoryInfo.js +42 -0
  82. package/dist/debug/components/Panel.d.ts +31 -0
  83. package/dist/debug/components/Panel.js +104 -0
  84. package/dist/debug/components/Stats.d.ts +9 -0
  85. package/dist/debug/components/Stats.js +24 -0
  86. package/dist/debug/debug-chunk.d.ts +26 -0
  87. package/dist/debug/debug-chunk.js +32 -0
  88. package/dist/debug/debug-context.d.ts +41 -0
  89. package/dist/debug/debug-context.js +59 -0
  90. package/dist/debug/index.d.ts +41 -0
  91. package/dist/debug/index.js +43 -0
  92. package/dist/events/LayoutDriver.d.ts +36 -0
  93. package/dist/events/LayoutDriver.js +192 -0
  94. package/dist/events/PointerMissed.d.ts +23 -0
  95. package/dist/events/PointerMissed.js +58 -0
  96. package/dist/events/def-culling.d.ts +46 -0
  97. package/dist/events/def-culling.js +69 -0
  98. package/dist/events/layout-targets.d.ts +151 -0
  99. package/dist/events/layout-targets.js +397 -0
  100. package/dist/events/placement/box.d.ts +59 -0
  101. package/dist/events/placement/box.js +96 -0
  102. package/dist/events/placement/grid.d.ts +161 -0
  103. package/dist/events/placement/grid.js +235 -0
  104. package/dist/events/placement/index.d.ts +2 -0
  105. package/dist/events/placement/index.js +1 -0
  106. package/dist/events/placement/pass.d.ts +70 -0
  107. package/dist/events/placement/pass.js +617 -0
  108. package/dist/events/pointer.d.ts +194 -0
  109. package/dist/events/pointer.js +356 -0
  110. package/dist/external/ExternalCanvas.d.ts +56 -0
  111. package/dist/external/ExternalCanvas.js +177 -0
  112. package/dist/external/external-camera.d.ts +18 -0
  113. package/dist/external/external-camera.js +103 -0
  114. package/dist/external/external-events.d.ts +14 -0
  115. package/dist/external/external-events.js +33 -0
  116. package/dist/index.d.ts +13 -0
  117. package/dist/index.js +14 -0
  118. package/dist/objects/Background.d.ts +31 -0
  119. package/dist/objects/Background.js +37 -0
  120. package/dist/objects/containers/SlotContainer.d.ts +312 -0
  121. package/dist/objects/containers/SlotContainer.js +311 -0
  122. package/dist/objects/containers/hooks.d.ts +61 -0
  123. package/dist/objects/containers/hooks.js +196 -0
  124. package/dist/objects/containers/index.d.ts +6 -0
  125. package/dist/objects/containers/index.js +4 -0
  126. package/dist/objects/containers/keys.d.ts +10 -0
  127. package/dist/objects/containers/keys.js +18 -0
  128. package/dist/objects/containers/mesh-writes.d.ts +106 -0
  129. package/dist/objects/containers/mesh-writes.js +97 -0
  130. package/dist/objects/containers/props.d.ts +33 -0
  131. package/dist/objects/containers/props.js +1 -0
  132. package/dist/objects/images/ImageInstance.d.ts +29 -0
  133. package/dist/objects/images/ImageInstance.js +152 -0
  134. package/dist/objects/images/Images.d.ts +49 -0
  135. package/dist/objects/images/Images.js +394 -0
  136. package/dist/objects/images/atlas-packer.d.ts +129 -0
  137. package/dist/objects/images/atlas-packer.js +287 -0
  138. package/dist/objects/images/image-container.d.ts +74 -0
  139. package/dist/objects/images/image-container.js +100 -0
  140. package/dist/objects/images/index.d.ts +2 -0
  141. package/dist/objects/images/index.js +1 -0
  142. package/dist/objects/images/pack-sources.d.ts +190 -0
  143. package/dist/objects/images/pack-sources.js +272 -0
  144. package/dist/objects/images/texture-budget.d.ts +120 -0
  145. package/dist/objects/images/texture-budget.js +215 -0
  146. package/dist/objects/images/worker/atlas-worker.d.ts +56 -0
  147. package/dist/objects/images/worker/atlas-worker.js +123 -0
  148. package/dist/objects/images/worker/client.d.ts +62 -0
  149. package/dist/objects/images/worker/client.js +249 -0
  150. package/dist/objects/images/worker/index.d.ts +10 -0
  151. package/dist/objects/images/worker/index.js +1 -0
  152. package/dist/objects/layer/Layer.d.ts +28 -0
  153. package/dist/objects/layer/Layer.js +134 -0
  154. package/dist/objects/layer/index.d.ts +2 -0
  155. package/dist/objects/layer/index.js +1 -0
  156. package/dist/objects/layer/layer-context.d.ts +74 -0
  157. package/dist/objects/layer/layer-context.js +82 -0
  158. package/dist/objects/layer/mount-scheduler.d.ts +131 -0
  159. package/dist/objects/layer/mount-scheduler.js +289 -0
  160. package/dist/objects/layer/visibility-gui.d.ts +19 -0
  161. package/dist/objects/layer/visibility-gui.js +93 -0
  162. package/dist/objects/lighting/Lighting.d.ts +92 -0
  163. package/dist/objects/lighting/Lighting.js +384 -0
  164. package/dist/objects/lighting/OcclusionPasses.d.ts +23 -0
  165. package/dist/objects/lighting/OcclusionPasses.js +271 -0
  166. package/dist/objects/lighting/index.d.ts +11 -0
  167. package/dist/objects/lighting/index.js +10 -0
  168. package/dist/objects/lighting/lighting-gui.d.ts +24 -0
  169. package/dist/objects/lighting/lighting-gui.js +114 -0
  170. package/dist/objects/lighting/occluder.d.ts +34 -0
  171. package/dist/objects/lighting/occluder.js +62 -0
  172. package/dist/objects/lighting/occlusion-targets.d.ts +143 -0
  173. package/dist/objects/lighting/occlusion-targets.js +276 -0
  174. package/dist/objects/lines/LineInstance.d.ts +21 -0
  175. package/dist/objects/lines/LineInstance.js +73 -0
  176. package/dist/objects/lines/Lines.d.ts +17 -0
  177. package/dist/objects/lines/Lines.js +31 -0
  178. package/dist/objects/lines/index.d.ts +2 -0
  179. package/dist/objects/lines/index.js +1 -0
  180. package/dist/objects/lines/line-container.d.ts +35 -0
  181. package/dist/objects/lines/line-container.js +40 -0
  182. package/dist/objects/materials/ColorMaterial.d.ts +92 -0
  183. package/dist/objects/materials/ColorMaterial.js +124 -0
  184. package/dist/objects/materials/LineMaterial.d.ts +44 -0
  185. package/dist/objects/materials/LineMaterial.js +74 -0
  186. package/dist/objects/materials/ModelMaterial.d.ts +53 -0
  187. package/dist/objects/materials/ModelMaterial.js +111 -0
  188. package/dist/objects/materials/TextureMaterial.d.ts +33 -0
  189. package/dist/objects/materials/TextureMaterial.js +48 -0
  190. package/dist/objects/materials/effects/ao/ambient-occlusion-pass.d.ts +77 -0
  191. package/dist/objects/materials/effects/ao/ambient-occlusion-pass.js +296 -0
  192. package/dist/objects/materials/effects/ao/ambient-occlusion-patch.d.ts +29 -0
  193. package/dist/objects/materials/effects/ao/ambient-occlusion-patch.js +115 -0
  194. package/dist/objects/materials/effects/ao/blur-pass.d.ts +61 -0
  195. package/dist/objects/materials/effects/ao/blur-pass.js +101 -0
  196. package/dist/objects/materials/effects/ao/index.d.ts +129 -0
  197. package/dist/objects/materials/effects/ao/index.js +72 -0
  198. package/dist/objects/materials/effects/color-dim.d.ts +115 -0
  199. package/dist/objects/materials/effects/color-dim.js +206 -0
  200. package/dist/objects/materials/effects/contact-shadow.d.ts +100 -0
  201. package/dist/objects/materials/effects/contact-shadow.js +167 -0
  202. package/dist/objects/materials/effects/effects-context.d.ts +53 -0
  203. package/dist/objects/materials/effects/effects-context.js +24 -0
  204. package/dist/objects/materials/shared.d.ts +24 -0
  205. package/dist/objects/materials/shared.js +72 -0
  206. package/dist/objects/models/ModelInstance.d.ts +22 -0
  207. package/dist/objects/models/ModelInstance.js +33 -0
  208. package/dist/objects/models/Models.d.ts +22 -0
  209. package/dist/objects/models/Models.js +186 -0
  210. package/dist/objects/models/gltf-cache.d.ts +17 -0
  211. package/dist/objects/models/gltf-cache.js +50 -0
  212. package/dist/objects/models/index.d.ts +2 -0
  213. package/dist/objects/models/index.js +1 -0
  214. package/dist/objects/models/model-asset.d.ts +48 -0
  215. package/dist/objects/models/model-asset.js +77 -0
  216. package/dist/objects/models/model-container.d.ts +123 -0
  217. package/dist/objects/models/model-container.js +207 -0
  218. package/dist/objects/shapes/ShapeInstance.d.ts +26 -0
  219. package/dist/objects/shapes/ShapeInstance.js +39 -0
  220. package/dist/objects/shapes/Shapes.d.ts +21 -0
  221. package/dist/objects/shapes/Shapes.js +131 -0
  222. package/dist/objects/shapes/index.d.ts +2 -0
  223. package/dist/objects/shapes/index.js +1 -0
  224. package/dist/objects/shapes/shape-container.d.ts +141 -0
  225. package/dist/objects/shapes/shape-container.js +242 -0
  226. package/dist/objects/shapes/shape-geometry.d.ts +27 -0
  227. package/dist/objects/shapes/shape-geometry.js +94 -0
  228. package/dist/objects/text/TextInstance.d.ts +22 -0
  229. package/dist/objects/text/TextInstance.js +95 -0
  230. package/dist/objects/text/Texts.d.ts +30 -0
  231. package/dist/objects/text/Texts.js +43 -0
  232. package/dist/objects/text/index.d.ts +2 -0
  233. package/dist/objects/text/index.js +1 -0
  234. package/dist/objects/text/text-container.d.ts +121 -0
  235. package/dist/objects/text/text-container.js +229 -0
  236. package/dist/objects/text/text-layout.d.ts +92 -0
  237. package/dist/objects/text/text-layout.js +180 -0
  238. package/dist/space/Camera.d.ts +103 -0
  239. package/dist/space/Camera.js +321 -0
  240. package/dist/space/Scene.d.ts +47 -0
  241. package/dist/space/Scene.js +127 -0
  242. package/dist/space/Space.d.ts +325 -0
  243. package/dist/space/Space.js +359 -0
  244. package/dist/space/VisibleRect.d.ts +45 -0
  245. package/dist/space/VisibleRect.js +129 -0
  246. package/dist/space/camera-handle.d.ts +67 -0
  247. package/dist/space/camera-handle.js +63 -0
  248. package/dist/space/camera-kind.d.ts +26 -0
  249. package/dist/space/camera-kind.js +30 -0
  250. package/dist/space/canvas-size.d.ts +24 -0
  251. package/dist/space/canvas-size.js +20 -0
  252. package/dist/space/clipping.d.ts +69 -0
  253. package/dist/space/clipping.js +130 -0
  254. package/dist/space/layout-measure.d.ts +67 -0
  255. package/dist/space/layout-measure.js +175 -0
  256. package/dist/space/viewport.d.ts +112 -0
  257. package/dist/space/viewport.js +67 -0
  258. package/dist/types/defs.d.ts +234 -0
  259. package/dist/types/defs.js +1 -0
  260. package/dist/types/events.d.ts +198 -0
  261. package/dist/types/events.js +1 -0
  262. package/dist/types/guards.d.ts +21 -0
  263. package/dist/types/guards.js +52 -0
  264. package/dist/update/index.d.ts +3 -0
  265. package/dist/update/index.js +2 -0
  266. package/dist/update/slot-writers.d.ts +30 -0
  267. package/dist/update/slot-writers.js +76 -0
  268. package/dist/update/store.d.ts +41 -0
  269. package/dist/update/store.js +104 -0
  270. package/dist/utils/arrays.d.ts +13 -0
  271. package/dist/utils/arrays.js +20 -0
  272. package/dist/utils/colors.d.ts +19 -0
  273. package/dist/utils/colors.js +27 -0
  274. package/dist/utils/hot-state.d.ts +41 -0
  275. package/dist/utils/hot-state.js +37 -0
  276. package/dist/utils/logging.d.ts +29 -0
  277. package/dist/utils/logging.js +55 -0
  278. package/dist/utils/math.d.ts +52 -0
  279. package/dist/utils/math.js +76 -0
  280. package/package.json +79 -0
@@ -0,0 +1,100 @@
1
+ import { Color, type IUniform, Matrix4, type WebGLProgramParametersWithUniforms } from 'three';
2
+ /**
3
+ * The scene-wide state this term owns: the uniforms every lit material reads, and the hex string a
4
+ * lil-gui color controller writes.
5
+ *
6
+ * Declared here rather than in `effects-context.ts`, because these are the term's own — the bundle
7
+ * that publishes them only assembles what each term declares. {@link CONTACT_SHADOW_DEFAULTS} sits
8
+ * here for the same reason.
9
+ *
10
+ * **Every field carries a `shadow` prefix**, because the bundle is flat and ambient occlusion brings
11
+ * a `strength` and a `color` of its own; the prefix is what keeps the two apart.
12
+ * {@link ContactShadow.planFromWorld} is the exception, because it names the plan rather than the
13
+ * shadow: it is the plan's own matrix, and the shadow is only its one reader today.
14
+ */
15
+ export interface ContactShadow {
16
+ /** The shadow's reach above a volume's base, in plan units. */
17
+ shadowDistance: IUniform<number>;
18
+ /** The shadow's depth, from `0` to `1`. */
19
+ shadowStrength: IUniform<number>;
20
+ /**
21
+ * The shade both darkening terms blend toward.
22
+ *
23
+ * Shared rather than one per term, so a plan has one shade of shadow rather than two — ambient
24
+ * occlusion binds this very object under a name of its own.
25
+ */
26
+ shadowColor: IUniform<Color>;
27
+ /**
28
+ * The shade as a hex string, which is what a lil-gui color controller reads and writes.
29
+ *
30
+ * Beside the uniform rather than inside it because three holds a `Color` in the working space
31
+ * while lil-gui writes display sRGB, and `Color.set` on a string is the conversion.
32
+ */
33
+ shadowHex: string;
34
+ /**
35
+ * World space back to the plan's own, which is the space's own `Space.matrixInverse`.
36
+ *
37
+ * `SceneGraph` points this uniform at that matrix once. The space recomputes the matrix in place
38
+ * on every refit and never replaces it, so the shader reads the current fit with no copy. Never
39
+ * write through this value, because the write lands in the space itself.
40
+ *
41
+ * The height a volume is shaded by is a height in the plan, and nothing else in the vertex shader
42
+ * knows the plan. A mesh's own matrices place it in the world, and the world is the fit, whose z
43
+ * the fit mirrors. This uniform takes a placed vertex back.
44
+ */
45
+ planFromWorld: IUniform<Matrix4>;
46
+ }
47
+ /**
48
+ * Installs the contact shadow into a lit shader about to be compiled.
49
+ *
50
+ * A fake, not a measurement. It reads the vertex's height above the plan and darkens toward the base
51
+ * of every volume, which separates a wall side from a wall top and stands in for the shadow a wall
52
+ * casts onto the floor it meets. Ambient occlusion is a separate term, applied at the same anchor by
53
+ * `ao/ambient-occlusion-patch.ts`.
54
+ *
55
+ * The reach is a fixed world distance rather than each volume's own height. It needs no new data and
56
+ * stays consistent across a plan. A roof above the reach keeps its authored color exactly; a volume
57
+ * shorter than the reach blends its roof as it blends the rest of its height.
58
+ *
59
+ * The blend goes toward an authored color rather than multiplying down toward black, so the shade is
60
+ * a choice and not only its depth. MapLibre multiplies toward black and clamps.
61
+ *
62
+ * The patch lands above three's output chain, where the dim patch lands, so the color it changes is
63
+ * still linear and scene-referred.
64
+ *
65
+ * **This reaches solid shape batches and models.** Floors and zones are planar defs drawn with
66
+ * `MeshBasicMaterial`, which no light and no darkening term reaches — and whose unit plane sits at
67
+ * `z = 0`, where it would take the whole shadow.
68
+ * @param parameters - The shader being compiled.
69
+ * @param shadow - The mount's own shadow state, whose uniform objects the compiled program captures.
70
+ * @throws {Error} If the shader has no output chain, or no placement to read a vertex's height
71
+ * after, either of which would leave the shadow silently absent from a material that asked for it.
72
+ */
73
+ export declare function applyContactShadow(parameters: WebGLProgramParametersWithUniforms, shadow: ContactShadow): void;
74
+ /**
75
+ * Builds this term's scene-wide state, at the agreed defaults.
76
+ *
77
+ * One object per mounted scene, never module state: a texture is not the only thing two `Renderer`
78
+ * mounts on one page would fight over — a program built in one context captures these very objects,
79
+ * so a singleton would let one scene's slider move the other's materials. `createSceneEffects`
80
+ * spreads this into the bundle it publishes.
81
+ * @returns A fresh object, whose identity is meant to outlive every write into it.
82
+ */
83
+ export declare function createContactShadow(): ContactShadow;
84
+ /**
85
+ * The contact shadow values this design agreed.
86
+ *
87
+ * Each one is a starting point rather than a settled number. The distance is an absolute figure in
88
+ * plan units, so it suits one plan's booth heights and not another's: a plan whose walls are a few
89
+ * units tall wants a few units of reach, and one whose walls are hundreds wants hundreds. It is
90
+ * tuned per plan through the GUI.
91
+ */
92
+ export declare const CONTACT_SHADOW_DEFAULTS: {
93
+ /** How far above a volume's base the shadow reaches, in plan units. */
94
+ distance: number;
95
+ /** How far the base is blended toward the shadow color, from `0` to `1`. */
96
+ strength: number;
97
+ /** The shade the shadow blends toward. */
98
+ color: string;
99
+ };
100
+ //# sourceMappingURL=contact-shadow.d.ts.map
@@ -0,0 +1,167 @@
1
+ import { Color, Matrix4 } from 'three';
2
+ import { declareBeforeMain, replaceAnchor, sceneColorAnchor } from '../../../core/shader-source';
3
+ /**
4
+ * Installs the contact shadow into a lit shader about to be compiled.
5
+ *
6
+ * A fake, not a measurement. It reads the vertex's height above the plan and darkens toward the base
7
+ * of every volume, which separates a wall side from a wall top and stands in for the shadow a wall
8
+ * casts onto the floor it meets. Ambient occlusion is a separate term, applied at the same anchor by
9
+ * `ao/ambient-occlusion-patch.ts`.
10
+ *
11
+ * The reach is a fixed world distance rather than each volume's own height. It needs no new data and
12
+ * stays consistent across a plan. A roof above the reach keeps its authored color exactly; a volume
13
+ * shorter than the reach blends its roof as it blends the rest of its height.
14
+ *
15
+ * The blend goes toward an authored color rather than multiplying down toward black, so the shade is
16
+ * a choice and not only its depth. MapLibre multiplies toward black and clamps.
17
+ *
18
+ * The patch lands above three's output chain, where the dim patch lands, so the color it changes is
19
+ * still linear and scene-referred.
20
+ *
21
+ * **This reaches solid shape batches and models.** Floors and zones are planar defs drawn with
22
+ * `MeshBasicMaterial`, which no light and no darkening term reaches — and whose unit plane sits at
23
+ * `z = 0`, where it would take the whole shadow.
24
+ * @param parameters - The shader being compiled.
25
+ * @param shadow - The mount's own shadow state, whose uniform objects the compiled program captures.
26
+ * @throws {Error} If the shader has no output chain, or no placement to read a vertex's height
27
+ * after, either of which would leave the shadow silently absent from a material that asked for it.
28
+ */
29
+ export function applyContactShadow(parameters, shadow) {
30
+ // Already installed, which is how a repeated compile stays harmless.
31
+ if (parameters.fragmentShader.includes(SHADOW_FUNCTION))
32
+ return;
33
+ const anchor = sceneColorAnchor(parameters.fragmentShader);
34
+ if (!anchor) {
35
+ throw new Error('applyContactShadow: the shader has no output chain to darken before.');
36
+ }
37
+ // Both rewrites before any write, so this either installs the shadow or leaves the parameters
38
+ // exactly as it found them. A shader whose vertex stage places its vertices some other way throws
39
+ // from `replaceAnchor` below, and a caller that means to carry on — a material whose asset is not
40
+ // one three built — must not be left holding four bindings for a program nothing patched.
41
+ const vertexShader = replaceAnchor(declareBeforeMain(parameters.vertexShader, VERTEX_DEFS), PLACEMENT_ANCHOR, `${PLACEMENT_ANCHOR}\n${VERTEX_APPLY}`);
42
+ const fragmentShader = declareBeforeMain(parameters.fragmentShader, FRAGMENT_DEFS).replace(anchor, `${SHADOW_APPLY}\n${anchor}`);
43
+ parameters.uniforms[SHADOW_DISTANCE] = shadow.shadowDistance;
44
+ parameters.uniforms[SHADOW_STRENGTH] = shadow.shadowStrength;
45
+ parameters.uniforms[SHADOW_COLOR] = shadow.shadowColor;
46
+ parameters.uniforms[PLAN_FROM_WORLD] = shadow.planFromWorld;
47
+ parameters.vertexShader = vertexShader;
48
+ parameters.fragmentShader = fragmentShader;
49
+ }
50
+ /**
51
+ * Builds this term's scene-wide state, at the agreed defaults.
52
+ *
53
+ * One object per mounted scene, never module state: a texture is not the only thing two `Renderer`
54
+ * mounts on one page would fight over — a program built in one context captures these very objects,
55
+ * so a singleton would let one scene's slider move the other's materials. `createSceneEffects`
56
+ * spreads this into the bundle it publishes.
57
+ * @returns A fresh object, whose identity is meant to outlive every write into it.
58
+ */
59
+ export function createContactShadow() {
60
+ return {
61
+ shadowDistance: { value: CONTACT_SHADOW_DEFAULTS.distance },
62
+ shadowStrength: { value: CONTACT_SHADOW_DEFAULTS.strength },
63
+ shadowColor: { value: new Color(CONTACT_SHADOW_DEFAULTS.color) },
64
+ shadowHex: CONTACT_SHADOW_DEFAULTS.color,
65
+ planFromWorld: { value: new Matrix4() },
66
+ };
67
+ }
68
+ /**
69
+ * The contact shadow values this design agreed.
70
+ *
71
+ * Each one is a starting point rather than a settled number. The distance is an absolute figure in
72
+ * plan units, so it suits one plan's booth heights and not another's: a plan whose walls are a few
73
+ * units tall wants a few units of reach, and one whose walls are hundreds wants hundreds. It is
74
+ * tuned per plan through the GUI.
75
+ */
76
+ export const CONTACT_SHADOW_DEFAULTS = {
77
+ /** How far above a volume's base the shadow reaches, in plan units. */
78
+ distance: 1.5,
79
+ /** How far the base is blended toward the shadow color, from `0` to `1`. */
80
+ strength: 0.2,
81
+ /** The shade the shadow blends toward. */
82
+ color: '#4a5260',
83
+ };
84
+ /** The uniform carrying the shadow's reach. */
85
+ const SHADOW_DISTANCE = 'uShadowDistance';
86
+ /** The uniform carrying the shadow's depth. */
87
+ const SHADOW_STRENGTH = 'uShadowStrength';
88
+ /** The uniform carrying the shade the shadow blends toward. */
89
+ const SHADOW_COLOR = 'uShadowColor';
90
+ /** The uniform carrying world space back to the plan's own. */
91
+ const PLAN_FROM_WORLD = 'uPlanFromWorld';
92
+ /** The signature {@link applyContactShadow} looks for, to tell a patched shader from a fresh one. */
93
+ const SHADOW_FUNCTION = 'vec3 contactShadow(';
94
+ /** The varying carrying a vertex's height above the plan. */
95
+ const HEIGHT_VARYING = 'vHeight';
96
+ /** The local three's own placement leaves the vertex's world position in. */
97
+ const WORLD_POSITION = 'worldPosition';
98
+ /**
99
+ * Where the height read lands: three's own placement of the vertex into world space.
100
+ *
101
+ * `<worldpos_vertex>` declares {@link WORLD_POSITION} and applies the batching matrix, the instance
102
+ * matrix and the model matrix to it, so the read below composes nothing of its own. This package
103
+ * widens that chunk's instancing guard to fire under indirect instancing as well. See
104
+ * `instanced-material.ts`.
105
+ *
106
+ * The read cannot ride on `openMain` the way the rest of this package's vertex injections do.
107
+ * `instanceMatrix` is the local that the indirect instancing prologue declares at the top of `main`,
108
+ * and it shadows the placeholder attribute three declares for any `isInstancedMesh` object.
109
+ * `openMain` inserts at the top and the last caller runs first, so a patch installed from a
110
+ * material's own handler would land above the mesh's prologue and read the empty attribute.
111
+ */
112
+ const PLACEMENT_ANCHOR = /* glsl */ `#include <worldpos_vertex>`;
113
+ /**
114
+ * The vertex-stage declarations, and the define that makes three build the world position at all.
115
+ *
116
+ * three guards the whole body of `<worldpos_vertex>` behind five conditions: `USE_ENVMAP`,
117
+ * `DISTANCE`, `USE_SHADOWMAP`, `USE_TRANSMISSION`, and a spot light coord count above zero. This
118
+ * renderer sets none of them, so the chunk expands to nothing and the shader declares no world
119
+ * position to read. `DISTANCE` is the one of the five that no chunk this renderer includes reads, in
120
+ * either stage, so defining it buys the world position and changes nothing else. three's own
121
+ * `distance` material defines it for itself, and nothing here draws with that material.
122
+ *
123
+ * `declareBeforeMain` puts this above `main`, which is above every `#include` inside `main`, so the
124
+ * preprocessor reads the define before it reaches the guard.
125
+ */
126
+ const VERTEX_DEFS = /* glsl */ `
127
+ #define DISTANCE
128
+ uniform mat4 ${PLAN_FROM_WORLD};
129
+ varying float ${HEIGHT_VARYING};
130
+ `;
131
+ /**
132
+ * The vertex-stage read: the vertex's height in the plan's own space.
133
+ *
134
+ * `<worldpos_vertex>` runs one line above and leaves the vertex's world position in
135
+ * {@link WORLD_POSITION}. {@link PLAN_FROM_WORLD} takes that position back to the plan, and the
136
+ * height is its `z`.
137
+ *
138
+ * three's chunk composes the placement, which is what makes the read correct for content whose
139
+ * geometry is not authored in plan coordinates. A model carries GLB-local coordinates and a
140
+ * per-instance placement matrix, so its own `position.z` means nothing there. The solid shape batches
141
+ * do carry absolute plan coordinates, and they drew right only while their instance transform stayed
142
+ * identity. Nothing enforces that.
143
+ */
144
+ const VERTEX_APPLY = /* glsl */ `
145
+ ${HEIGHT_VARYING} = (${PLAN_FROM_WORLD} * ${WORLD_POSITION}).z;
146
+ `;
147
+ /** The fragment-stage declarations. */
148
+ const FRAGMENT_DEFS = /* glsl */ `
149
+ uniform float ${SHADOW_DISTANCE};
150
+ uniform float ${SHADOW_STRENGTH};
151
+ uniform vec3 ${SHADOW_COLOR};
152
+ varying float ${HEIGHT_VARYING};
153
+
154
+ ${SHADOW_FUNCTION}vec3 rgb, float height) {
155
+ float lit = clamp(height / max(${SHADOW_DISTANCE}, 1e-4), 0., 1.);
156
+ return mix(rgb, ${SHADOW_COLOR}, ${SHADOW_STRENGTH} * (1. - lit));
157
+ }
158
+ `;
159
+ /**
160
+ * The shadow applied while the color is still linear and scene-referred.
161
+ *
162
+ * The call is spelled out rather than built from {@link SHADOW_FUNCTION}, which stays the
163
+ * already-patched detector and nothing else.
164
+ */
165
+ const SHADOW_APPLY = /* glsl */ `
166
+ gl_FragColor.rgb = contactShadow(gl_FragColor.rgb, ${HEIGHT_VARYING});
167
+ `;
@@ -0,0 +1,53 @@
1
+ import { type AmbientOcclusion } from './ao';
2
+ import { type ContactShadow } from './contact-shadow';
3
+ /**
4
+ * Everything one mounted scene's shared effects hold: the tuned values, the registry of what asks
5
+ * for ambient occlusion, and the uniforms the lit shaders read.
6
+ *
7
+ * **This is scene-wide shading state, not post processing.** Nothing here runs over the finished
8
+ * image: the ambient occlusion pass is a *pre*pass, and every term is applied inside the lit shader
9
+ * itself, so there is no compositing stage for this to be a part of.
10
+ *
11
+ * **Each term owns its own state, and this only assembles it.** Every field here is declared by the
12
+ * term that owns it, and this alias gathers them. The two terms share one shade of shadow, which the
13
+ * contact shadow declares and ambient occlusion binds under a name of its own.
14
+ *
15
+ * **An alias over an intersection, rather than an interface that extends.** An intersection states
16
+ * what the bundle is, an assembly of what the terms declare, where `extends` reads as a subtype
17
+ * relationship, which is not what is meant. And an interface that extends exactly one parent and
18
+ * declares nothing of its own is what `@typescript-eslint/no-empty-object-type` exists to flag, so
19
+ * that form would need a suppression this one does not.
20
+ *
21
+ * **One object per mount, never module state.** Two `Renderer` mounts on one page have two GL
22
+ * contexts and two sets of render targets, and a module singleton would hand both of their materials
23
+ * whichever texture wrote last. The identity never changes once created, so the passes write into it
24
+ * every frame with no React render and no shader recompile behind them — the discipline
25
+ * `contact-shadow.ts` uses for its three uniforms, published the way `SpaceContext` publishes a
26
+ * `Space` that is refitted in place.
27
+ *
28
+ * **`LayerDimContext` and `LayerOverlayContext` stay where they are and are not merged in.** Each
29
+ * carries one primitive of a layer's inherited state, is re-provided by every layer, and nests.
30
+ * This bundle is one object per renderer mount whose identity must never change, which is what
31
+ * lets a material capture it in `onBeforeCompile` and a frame write `.value` with no recompile.
32
+ * Merging either one in would either re-provide this object at every layer boundary or flatten
33
+ * that nesting.
34
+ */
35
+ export type SceneEffects = AmbientOcclusion & ContactShadow;
36
+ /**
37
+ * Builds one scene's effect state, from each term's own factory.
38
+ * @returns A fresh object, whose identity is meant to outlive every write into it.
39
+ */
40
+ export declare function createSceneEffects(): SceneEffects;
41
+ /**
42
+ * The effect state of the enclosing scene.
43
+ * @returns The object `Scene` published, or a standalone one when rendered outside a scene.
44
+ */
45
+ export declare function useSceneEffects(): SceneEffects;
46
+ /**
47
+ * The effect state every lit material and both passes read.
48
+ *
49
+ * The default is a standalone object rather than `null`, so a material rendered outside a scene
50
+ * still compiles and simply shades with the terms switched off — the same shape `SpaceContext` takes.
51
+ */
52
+ export declare const SceneEffectsContext: import("react").Context<SceneEffects>;
53
+ //# sourceMappingURL=effects-context.d.ts.map
@@ -0,0 +1,24 @@
1
+ import { createContext, use } from 'react';
2
+ import { createAmbientOcclusion } from './ao';
3
+ import { createContactShadow } from './contact-shadow';
4
+ /**
5
+ * Builds one scene's effect state, from each term's own factory.
6
+ * @returns A fresh object, whose identity is meant to outlive every write into it.
7
+ */
8
+ export function createSceneEffects() {
9
+ return { ...createAmbientOcclusion(), ...createContactShadow() };
10
+ }
11
+ /**
12
+ * The effect state of the enclosing scene.
13
+ * @returns The object `Scene` published, or a standalone one when rendered outside a scene.
14
+ */
15
+ export function useSceneEffects() {
16
+ return use(SceneEffectsContext);
17
+ }
18
+ /**
19
+ * The effect state every lit material and both passes read.
20
+ *
21
+ * The default is a standalone object rather than `null`, so a material rendered outside a scene
22
+ * still compiles and simply shades with the terms switched off — the same shape `SpaceContext` takes.
23
+ */
24
+ export const SceneEffectsContext = createContext(createSceneEffects());
@@ -0,0 +1,24 @@
1
+ import { type MaterialParameters } from 'three';
2
+ /**
3
+ * Material parameter defaults, shared between all types
4
+ */
5
+ export declare const sharedParams: MaterialParameters;
6
+ /**
7
+ * What an overlay layer's planar materials add on top of {@link sharedParams}.
8
+ *
9
+ * Two obstacles, and render order clears neither.
10
+ *
11
+ * Depth. A solid shape sets `depthWrite: true`, so booth volumes fill the depth buffer. A planar def
12
+ * drawn later is still depth tested against them and loses. Turning the test off leaves order inside
13
+ * the tier as pure painter's order, because nothing in the tier writes depth.
14
+ *
15
+ * Pass. A translucent solid carries `transparent: true`, so it lives in three's transparent list.
16
+ * three draws that list after every opaque object, whatever render order says. An overlay that
17
+ * stayed in the opaque list would lose to a glass roof. So it opts back in. Inside the transparent
18
+ * list `reversePainterSortStable` compares `groupOrder` first, and the overlay tier's value of at
19
+ * least 1 beats every plan-tier value.
20
+ *
21
+ * `forceSinglePass` is not touched, so a double-sided overlay quad still blends once per pixel.
22
+ */
23
+ export declare const overlayParams: MaterialParameters;
24
+ //# sourceMappingURL=shared.d.ts.map
@@ -0,0 +1,72 @@
1
+ import { CustomBlending, DoubleSide, LessEqualDepth, OneFactor, OneMinusSrcAlphaFactor, SrcAlphaFactor, } from 'three';
2
+ /**
3
+ * Setting transparent: true on material changes render order, these parameters imitate transparent blending
4
+ * without reordering layers
5
+ */
6
+ const blendingParams = {
7
+ blending: CustomBlending,
8
+ blendSrc: SrcAlphaFactor,
9
+ blendSrcAlpha: OneFactor,
10
+ blendDst: OneMinusSrcAlphaFactor,
11
+ blendDstAlpha: OneMinusSrcAlphaFactor,
12
+ };
13
+ /**
14
+ * Resolvable depth steps of camera-ward bias on every 2D fragment — `polygonOffsetUnits`, negated.
15
+ *
16
+ * The flat, face-on term of the decal offset: with no tilt there is no depth slope for `factor` to
17
+ * scale, and all that separates a label from the roof it sits on is how two different transform
18
+ * chains rounded the same plane. Each chain lands within a float ULP or two of the exact depth, and
19
+ * one ULP of view depth is worth up to two resolvable steps behind a near plane keeping 98%
20
+ * headroom — so a couple of ULPs of disagreement is about four steps, which is what this covers.
21
+ */
22
+ const FLAT_BIAS_UNITS = 4;
23
+ /**
24
+ * Material parameter defaults, shared between all types
25
+ */
26
+ export const sharedParams = {
27
+ ...blendingParams,
28
+ side: DoubleSide,
29
+ depthFunc: LessEqualDepth,
30
+ depthWrite: false,
31
+ // 2D content never writes depth, so the buffer only ever holds what 3D content put there — and a
32
+ // 2D fragment coplanar with a solid surface (a label on a booth roof) is compared against a depth
33
+ // some other triangle interpolated. A small bias toward the camera wins that comparison; it is
34
+ // inert while nothing writes depth.
35
+ //
36
+ // `factor` covers camera tilt: the hardware scales it by the polygon's own window-space depth
37
+ // slope, making the bias one pixel's worth of that slope at any pitch — clear of every
38
+ // slope-proportional interpolation difference, yet unable to climb through anything that occludes
39
+ // the surface by more than a pixel. The imperative renderer had to carry this term as a constant,
40
+ // `⌈tan(maxPolarAngle)⌉ = 12`: with every layer writing depth, coplanar layers were kept apart by
41
+ // per-layer `units` steps, and surfaces sharing a plane share a slope, so `factor` cancelled
42
+ // between them and only a worst-case constant could size the step. With a single bias level there
43
+ // is no step, the real slope replaces the ceiling, and nothing here derives from `MAX_PITCH`.
44
+ //
45
+ // `units` covers the flat, top-down case, where the slope is zero and `factor` contributes
46
+ // nothing — sized by `FLAT_BIAS_UNITS`.
47
+ polygonOffset: true,
48
+ polygonOffsetFactor: -1,
49
+ polygonOffsetUnits: -FLAT_BIAS_UNITS,
50
+ forceSinglePass: true,
51
+ };
52
+ /**
53
+ * What an overlay layer's planar materials add on top of {@link sharedParams}.
54
+ *
55
+ * Two obstacles, and render order clears neither.
56
+ *
57
+ * Depth. A solid shape sets `depthWrite: true`, so booth volumes fill the depth buffer. A planar def
58
+ * drawn later is still depth tested against them and loses. Turning the test off leaves order inside
59
+ * the tier as pure painter's order, because nothing in the tier writes depth.
60
+ *
61
+ * Pass. A translucent solid carries `transparent: true`, so it lives in three's transparent list.
62
+ * three draws that list after every opaque object, whatever render order says. An overlay that
63
+ * stayed in the opaque list would lose to a glass roof. So it opts back in. Inside the transparent
64
+ * list `reversePainterSortStable` compares `groupOrder` first, and the overlay tier's value of at
65
+ * least 1 beats every plan-tier value.
66
+ *
67
+ * `forceSinglePass` is not touched, so a double-sided overlay quad still blends once per pixel.
68
+ */
69
+ export const overlayParams = {
70
+ depthTest: false,
71
+ transparent: true,
72
+ };
@@ -0,0 +1,22 @@
1
+ import { type ModelDef } from '../../types/defs';
2
+ import { type InstanceProps } from '../containers';
3
+ import { type ModelContainer } from './model-container';
4
+ /** Props for {@link ModelInstance}. */
5
+ export type ModelInstanceProps = InstanceProps<ModelDef, ModelContainer>;
6
+ /**
7
+ * Keeps one model's instance current: claims a slot on mount, writes the def's placement, opacity
8
+ * and visibility on mount and after every `update(def)`, and releases the slot on unmount.
9
+ *
10
+ * Renders no scene node — the mesh belongs to the container. Nothing here can force a re-claim: the
11
+ * geometry is the model part's, shared by every def drawing it, so moving a model is an ordinary
12
+ * write and its slot survives. `url` is not updatable by contract, which is what makes that true: a
13
+ * different model is a different def, and a different def a different instance.
14
+ *
15
+ * Memoized for the same reason as `ShapeInstance`: a structural change hands the layer a fresh
16
+ * `defs` snapshot, so element-identity bailout never applies and only prop equality keeps unchanged
17
+ * siblings from re-rendering. Correctness never depends on it.
18
+ * @param props - The def and its container.
19
+ * @returns `null`; the instance lives in the mesh, not the scene graph.
20
+ */
21
+ export declare const ModelInstance: import("react").MemoExoticComponent<(props: ModelInstanceProps) => null>;
22
+ //# sourceMappingURL=ModelInstance.d.ts.map
@@ -0,0 +1,33 @@
1
+ import { memo } from 'react';
2
+ import { useDefSync } from '../containers';
3
+ import { modelKindOf } from './model-container';
4
+ /**
5
+ * Keeps one model's instance current: claims a slot on mount, writes the def's placement, opacity
6
+ * and visibility on mount and after every `update(def)`, and releases the slot on unmount.
7
+ *
8
+ * Renders no scene node — the mesh belongs to the container. Nothing here can force a re-claim: the
9
+ * geometry is the model part's, shared by every def drawing it, so moving a model is an ordinary
10
+ * write and its slot survives. `url` is not updatable by contract, which is what makes that true: a
11
+ * different model is a different def, and a different def a different instance.
12
+ *
13
+ * Memoized for the same reason as `ShapeInstance`: a structural change hands the layer a fresh
14
+ * `defs` snapshot, so element-identity bailout never applies and only prop equality keeps unchanged
15
+ * siblings from re-rendering. Correctness never depends on it.
16
+ * @param props - The def and its container.
17
+ * @returns `null`; the instance lives in the mesh, not the scene graph.
18
+ */
19
+ export const ModelInstance = memo(function ModelInstance(props) {
20
+ const { def, container } = props;
21
+ // No per-instance resource: every def on this mesh draws the same merged geometry, so the slot is
22
+ // backed by nothing and nothing can invalidate it.
23
+ useDefSync(container, def, null, (slot) => {
24
+ container.place(slot, def);
25
+ container.setOpacity(slot, def);
26
+ // Checked on every sync, because a def leaves its batch's kind without re-rendering this layer:
27
+ // `opacity` crossing 1 reaches the instance through `update()` and stops there, so this is where
28
+ // a batch learns it is holding something whose material describes the wrong render list.
29
+ if (modelKindOf(def) !== container.kind)
30
+ container.reportKindMismatch();
31
+ });
32
+ return null;
33
+ });
@@ -0,0 +1,22 @@
1
+ import { type ModelDef } from '../../types/defs';
2
+ import { type DefsProps } from '../containers';
3
+ /** Props for {@link Models}. */
4
+ export type ModelsProps = DefsProps<ModelDef>;
5
+ /**
6
+ * Draws a layer's models. Every def sharing a URL draws from the same loaded asset, and every part of
7
+ * that asset — one per source material, merged at load — becomes a single `InstancedMesh`. A layer
8
+ * holding twenty copies of a two-material chair is therefore **two draw calls**, not forty.
9
+ *
10
+ * `InstancedMesh` rather than `BatchedMesh` on purpose. Nothing here ever addresses a sub-mesh
11
+ * individually — the addressable unit is the def — so merging at load is strictly better than
12
+ * carrying per-sub-mesh bookkeeping, and it avoids the `WEBGL_multi_draw` path a batched mesh needs,
13
+ * which is slow in Firefox.
14
+ *
15
+ * **Loading is an effect, not Suspense.** A suspended leaf would never run the effect that reports
16
+ * back to the mount scheduler, so the scene's admission queue would stall permanently on the first
17
+ * model layer. Instead nothing renders until the assets resolve.
18
+ * @param props - The models to draw.
19
+ * @returns One mesh per part of every loaded model, with an instance component per def.
20
+ */
21
+ export declare function Models(props: ModelsProps): import("react").JSX.Element;
22
+ //# sourceMappingURL=Models.d.ts.map