@expofp/renderer 0.0.0-experimental.cf063fc
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +145 -0
- package/dist/Renderer.d.ts +102 -0
- package/dist/Renderer.js +96 -0
- package/dist/controls/CameraControls.d.ts +108 -0
- package/dist/controls/CameraControls.js +203 -0
- package/dist/controls/Controls.d.ts +53 -0
- package/dist/controls/Controls.js +54 -0
- package/dist/controls/camera-api.d.ts +226 -0
- package/dist/controls/camera-api.js +179 -0
- package/dist/controls/gestures.d.ts +71 -0
- package/dist/controls/gestures.js +98 -0
- package/dist/controls/handlers/DoubleTapZoom.d.ts +23 -0
- package/dist/controls/handlers/DoubleTapZoom.js +82 -0
- package/dist/controls/handlers/Inertia.d.ts +60 -0
- package/dist/controls/handlers/Inertia.js +222 -0
- package/dist/controls/handlers/Pan.d.ts +23 -0
- package/dist/controls/handlers/Pan.js +212 -0
- package/dist/controls/handlers/Pitch.d.ts +19 -0
- package/dist/controls/handlers/Pitch.js +200 -0
- package/dist/controls/handlers/Roll.d.ts +30 -0
- package/dist/controls/handlers/Roll.js +196 -0
- package/dist/controls/handlers/Zoom.d.ts +28 -0
- package/dist/controls/handlers/Zoom.js +34 -0
- package/dist/controls/hooks.d.ts +136 -0
- package/dist/controls/hooks.js +200 -0
- package/dist/controls/index.d.ts +9 -0
- package/dist/controls/index.js +7 -0
- package/dist/controls/zoom-to.d.ts +89 -0
- package/dist/controls/zoom-to.js +91 -0
- package/dist/core/InstanceUniforms.d.ts +108 -0
- package/dist/core/InstanceUniforms.js +298 -0
- package/dist/core/SquareDataTexture.d.ts +277 -0
- package/dist/core/SquareDataTexture.js +476 -0
- package/dist/core/batch/BatchedMesh.d.ts +74 -0
- package/dist/core/batch/BatchedMesh.js +155 -0
- package/dist/core/instanced/FrustumCulling.d.ts +141 -0
- package/dist/core/instanced/FrustumCulling.js +246 -0
- package/dist/core/instanced/InstancedMesh.d.ts +302 -0
- package/dist/core/instanced/InstancedMesh.js +497 -0
- package/dist/core/instanced/Instances.d.ts +119 -0
- package/dist/core/instanced/Instances.js +230 -0
- package/dist/core/instanced/Raycasting.d.ts +118 -0
- package/dist/core/instanced/Raycasting.js +134 -0
- package/dist/core/instanced/instanced-material.d.ts +77 -0
- package/dist/core/instanced/instanced-material.js +228 -0
- package/dist/core/lines/BatchedLines.d.ts +137 -0
- package/dist/core/lines/BatchedLines.js +215 -0
- package/dist/core/lines/FrustumCullingLines.d.ts +44 -0
- package/dist/core/lines/FrustumCullingLines.js +85 -0
- package/dist/core/lines/RaycastingLines.d.ts +45 -0
- package/dist/core/lines/RaycastingLines.js +91 -0
- package/dist/core/lines/lines-material.d.ts +47 -0
- package/dist/core/lines/lines-material.js +124 -0
- package/dist/core/shader-source.d.ts +70 -0
- package/dist/core/shader-source.js +110 -0
- package/dist/core/sprites/BatchedSprites.d.ts +115 -0
- package/dist/core/sprites/BatchedSprites.js +173 -0
- package/dist/core/sprites/RaycastingSprites.d.ts +41 -0
- package/dist/core/sprites/RaycastingSprites.js +71 -0
- package/dist/core/sprites/sprite-material.d.ts +59 -0
- package/dist/core/sprites/sprite-material.js +192 -0
- package/dist/core/text/BatchedText.d.ts +233 -0
- package/dist/core/text/BatchedText.js +473 -0
- package/dist/core/text/MemberTexture.d.ts +90 -0
- package/dist/core/text/MemberTexture.js +289 -0
- package/dist/core/text/Text.d.ts +88 -0
- package/dist/core/text/Text.js +230 -0
- package/dist/core/text/batched-text-material.d.ts +56 -0
- package/dist/core/text/batched-text-material.js +123 -0
- package/dist/core/text/glyph-packing.d.ts +17 -0
- package/dist/core/text/glyph-packing.js +61 -0
- package/dist/core/typed-array.d.ts +16 -0
- package/dist/core/typed-array.js +20 -0
- package/dist/debug/components/DebugPanelProvider.d.ts +20 -0
- package/dist/debug/components/DebugPanelProvider.js +50 -0
- package/dist/debug/components/DebugTools.d.ts +22 -0
- package/dist/debug/components/DebugTools.js +28 -0
- package/dist/debug/components/Gui.d.ts +22 -0
- package/dist/debug/components/Gui.js +42 -0
- package/dist/debug/components/MemoryInfo.d.ts +22 -0
- package/dist/debug/components/MemoryInfo.js +42 -0
- package/dist/debug/components/Panel.d.ts +31 -0
- package/dist/debug/components/Panel.js +104 -0
- package/dist/debug/components/Stats.d.ts +9 -0
- package/dist/debug/components/Stats.js +24 -0
- package/dist/debug/debug-chunk.d.ts +26 -0
- package/dist/debug/debug-chunk.js +32 -0
- package/dist/debug/debug-context.d.ts +41 -0
- package/dist/debug/debug-context.js +59 -0
- package/dist/debug/index.d.ts +41 -0
- package/dist/debug/index.js +43 -0
- package/dist/events/LayoutDriver.d.ts +36 -0
- package/dist/events/LayoutDriver.js +192 -0
- package/dist/events/PointerMissed.d.ts +23 -0
- package/dist/events/PointerMissed.js +58 -0
- package/dist/events/def-culling.d.ts +46 -0
- package/dist/events/def-culling.js +69 -0
- package/dist/events/layout-targets.d.ts +151 -0
- package/dist/events/layout-targets.js +397 -0
- package/dist/events/placement/box.d.ts +59 -0
- package/dist/events/placement/box.js +96 -0
- package/dist/events/placement/grid.d.ts +161 -0
- package/dist/events/placement/grid.js +235 -0
- package/dist/events/placement/index.d.ts +2 -0
- package/dist/events/placement/index.js +1 -0
- package/dist/events/placement/pass.d.ts +70 -0
- package/dist/events/placement/pass.js +617 -0
- package/dist/events/pointer.d.ts +194 -0
- package/dist/events/pointer.js +356 -0
- package/dist/external/ExternalCanvas.d.ts +56 -0
- package/dist/external/ExternalCanvas.js +177 -0
- package/dist/external/external-camera.d.ts +18 -0
- package/dist/external/external-camera.js +103 -0
- package/dist/external/external-events.d.ts +14 -0
- package/dist/external/external-events.js +33 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.js +14 -0
- package/dist/objects/Background.d.ts +31 -0
- package/dist/objects/Background.js +37 -0
- package/dist/objects/containers/SlotContainer.d.ts +312 -0
- package/dist/objects/containers/SlotContainer.js +311 -0
- package/dist/objects/containers/hooks.d.ts +61 -0
- package/dist/objects/containers/hooks.js +196 -0
- package/dist/objects/containers/index.d.ts +6 -0
- package/dist/objects/containers/index.js +4 -0
- package/dist/objects/containers/keys.d.ts +10 -0
- package/dist/objects/containers/keys.js +18 -0
- package/dist/objects/containers/mesh-writes.d.ts +106 -0
- package/dist/objects/containers/mesh-writes.js +97 -0
- package/dist/objects/containers/props.d.ts +33 -0
- package/dist/objects/containers/props.js +1 -0
- package/dist/objects/images/ImageInstance.d.ts +29 -0
- package/dist/objects/images/ImageInstance.js +152 -0
- package/dist/objects/images/Images.d.ts +49 -0
- package/dist/objects/images/Images.js +394 -0
- package/dist/objects/images/atlas-packer.d.ts +129 -0
- package/dist/objects/images/atlas-packer.js +287 -0
- package/dist/objects/images/image-container.d.ts +74 -0
- package/dist/objects/images/image-container.js +100 -0
- package/dist/objects/images/index.d.ts +2 -0
- package/dist/objects/images/index.js +1 -0
- package/dist/objects/images/pack-sources.d.ts +190 -0
- package/dist/objects/images/pack-sources.js +272 -0
- package/dist/objects/images/texture-budget.d.ts +120 -0
- package/dist/objects/images/texture-budget.js +215 -0
- package/dist/objects/images/worker/atlas-worker.d.ts +56 -0
- package/dist/objects/images/worker/atlas-worker.js +123 -0
- package/dist/objects/images/worker/client.d.ts +62 -0
- package/dist/objects/images/worker/client.js +249 -0
- package/dist/objects/images/worker/index.d.ts +10 -0
- package/dist/objects/images/worker/index.js +1 -0
- package/dist/objects/layer/Layer.d.ts +28 -0
- package/dist/objects/layer/Layer.js +134 -0
- package/dist/objects/layer/index.d.ts +2 -0
- package/dist/objects/layer/index.js +1 -0
- package/dist/objects/layer/layer-context.d.ts +74 -0
- package/dist/objects/layer/layer-context.js +82 -0
- package/dist/objects/layer/mount-scheduler.d.ts +131 -0
- package/dist/objects/layer/mount-scheduler.js +289 -0
- package/dist/objects/layer/visibility-gui.d.ts +19 -0
- package/dist/objects/layer/visibility-gui.js +93 -0
- package/dist/objects/lighting/Lighting.d.ts +92 -0
- package/dist/objects/lighting/Lighting.js +384 -0
- package/dist/objects/lighting/OcclusionPasses.d.ts +23 -0
- package/dist/objects/lighting/OcclusionPasses.js +271 -0
- package/dist/objects/lighting/index.d.ts +11 -0
- package/dist/objects/lighting/index.js +10 -0
- package/dist/objects/lighting/lighting-gui.d.ts +24 -0
- package/dist/objects/lighting/lighting-gui.js +114 -0
- package/dist/objects/lighting/occluder.d.ts +34 -0
- package/dist/objects/lighting/occluder.js +62 -0
- package/dist/objects/lighting/occlusion-targets.d.ts +143 -0
- package/dist/objects/lighting/occlusion-targets.js +276 -0
- package/dist/objects/lines/LineInstance.d.ts +21 -0
- package/dist/objects/lines/LineInstance.js +73 -0
- package/dist/objects/lines/Lines.d.ts +17 -0
- package/dist/objects/lines/Lines.js +31 -0
- package/dist/objects/lines/index.d.ts +2 -0
- package/dist/objects/lines/index.js +1 -0
- package/dist/objects/lines/line-container.d.ts +35 -0
- package/dist/objects/lines/line-container.js +40 -0
- package/dist/objects/materials/ColorMaterial.d.ts +92 -0
- package/dist/objects/materials/ColorMaterial.js +124 -0
- package/dist/objects/materials/LineMaterial.d.ts +44 -0
- package/dist/objects/materials/LineMaterial.js +74 -0
- package/dist/objects/materials/ModelMaterial.d.ts +53 -0
- package/dist/objects/materials/ModelMaterial.js +111 -0
- package/dist/objects/materials/TextureMaterial.d.ts +33 -0
- package/dist/objects/materials/TextureMaterial.js +48 -0
- package/dist/objects/materials/effects/ao/ambient-occlusion-pass.d.ts +77 -0
- package/dist/objects/materials/effects/ao/ambient-occlusion-pass.js +296 -0
- package/dist/objects/materials/effects/ao/ambient-occlusion-patch.d.ts +29 -0
- package/dist/objects/materials/effects/ao/ambient-occlusion-patch.js +115 -0
- package/dist/objects/materials/effects/ao/blur-pass.d.ts +61 -0
- package/dist/objects/materials/effects/ao/blur-pass.js +101 -0
- package/dist/objects/materials/effects/ao/index.d.ts +129 -0
- package/dist/objects/materials/effects/ao/index.js +72 -0
- package/dist/objects/materials/effects/color-dim.d.ts +115 -0
- package/dist/objects/materials/effects/color-dim.js +206 -0
- package/dist/objects/materials/effects/contact-shadow.d.ts +100 -0
- package/dist/objects/materials/effects/contact-shadow.js +167 -0
- package/dist/objects/materials/effects/effects-context.d.ts +53 -0
- package/dist/objects/materials/effects/effects-context.js +24 -0
- package/dist/objects/materials/shared.d.ts +24 -0
- package/dist/objects/materials/shared.js +72 -0
- package/dist/objects/models/ModelInstance.d.ts +22 -0
- package/dist/objects/models/ModelInstance.js +33 -0
- package/dist/objects/models/Models.d.ts +22 -0
- package/dist/objects/models/Models.js +186 -0
- package/dist/objects/models/gltf-cache.d.ts +17 -0
- package/dist/objects/models/gltf-cache.js +50 -0
- package/dist/objects/models/index.d.ts +2 -0
- package/dist/objects/models/index.js +1 -0
- package/dist/objects/models/model-asset.d.ts +48 -0
- package/dist/objects/models/model-asset.js +77 -0
- package/dist/objects/models/model-container.d.ts +123 -0
- package/dist/objects/models/model-container.js +207 -0
- package/dist/objects/shapes/ShapeInstance.d.ts +26 -0
- package/dist/objects/shapes/ShapeInstance.js +39 -0
- package/dist/objects/shapes/Shapes.d.ts +21 -0
- package/dist/objects/shapes/Shapes.js +131 -0
- package/dist/objects/shapes/index.d.ts +2 -0
- package/dist/objects/shapes/index.js +1 -0
- package/dist/objects/shapes/shape-container.d.ts +141 -0
- package/dist/objects/shapes/shape-container.js +242 -0
- package/dist/objects/shapes/shape-geometry.d.ts +27 -0
- package/dist/objects/shapes/shape-geometry.js +94 -0
- package/dist/objects/text/TextInstance.d.ts +22 -0
- package/dist/objects/text/TextInstance.js +95 -0
- package/dist/objects/text/Texts.d.ts +30 -0
- package/dist/objects/text/Texts.js +43 -0
- package/dist/objects/text/index.d.ts +2 -0
- package/dist/objects/text/index.js +1 -0
- package/dist/objects/text/text-container.d.ts +121 -0
- package/dist/objects/text/text-container.js +229 -0
- package/dist/objects/text/text-layout.d.ts +92 -0
- package/dist/objects/text/text-layout.js +180 -0
- package/dist/space/Camera.d.ts +103 -0
- package/dist/space/Camera.js +321 -0
- package/dist/space/Scene.d.ts +47 -0
- package/dist/space/Scene.js +127 -0
- package/dist/space/Space.d.ts +325 -0
- package/dist/space/Space.js +359 -0
- package/dist/space/VisibleRect.d.ts +45 -0
- package/dist/space/VisibleRect.js +129 -0
- package/dist/space/camera-handle.d.ts +67 -0
- package/dist/space/camera-handle.js +63 -0
- package/dist/space/camera-kind.d.ts +26 -0
- package/dist/space/camera-kind.js +30 -0
- package/dist/space/canvas-size.d.ts +24 -0
- package/dist/space/canvas-size.js +20 -0
- package/dist/space/clipping.d.ts +69 -0
- package/dist/space/clipping.js +130 -0
- package/dist/space/layout-measure.d.ts +67 -0
- package/dist/space/layout-measure.js +175 -0
- package/dist/space/viewport.d.ts +112 -0
- package/dist/space/viewport.js +67 -0
- package/dist/types/defs.d.ts +234 -0
- package/dist/types/defs.js +1 -0
- package/dist/types/events.d.ts +198 -0
- package/dist/types/events.js +1 -0
- package/dist/types/guards.d.ts +21 -0
- package/dist/types/guards.js +52 -0
- package/dist/update/index.d.ts +3 -0
- package/dist/update/index.js +2 -0
- package/dist/update/slot-writers.d.ts +30 -0
- package/dist/update/slot-writers.js +76 -0
- package/dist/update/store.d.ts +41 -0
- package/dist/update/store.js +104 -0
- package/dist/utils/arrays.d.ts +13 -0
- package/dist/utils/arrays.js +20 -0
- package/dist/utils/colors.d.ts +19 -0
- package/dist/utils/colors.js +27 -0
- package/dist/utils/hot-state.d.ts +41 -0
- package/dist/utils/hot-state.js +37 -0
- package/dist/utils/logging.d.ts +29 -0
- package/dist/utils/logging.js +55 -0
- package/dist/utils/math.d.ts +52 -0
- package/dist/utils/math.js +76 -0
- package/package.json +79 -0
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { Vector2, type WebGLRenderer, WebGLRenderTarget } from 'three';
|
|
2
|
+
/**
|
|
3
|
+
* The three render targets the occlusion passes draw into, over two color textures.
|
|
4
|
+
*
|
|
5
|
+
* | Target | Color texture | Depth attachment | Written by | Read by |
|
|
6
|
+
* | ------- | ---------------------- | ---------------------- | ---------- | --------------- |
|
|
7
|
+
* | `depth` | its own, never written | `DepthTexture`, 24-bit | prepass | nothing |
|
|
8
|
+
* | `ao` | its own | none | AO pass | the blur pass |
|
|
9
|
+
* | `blur` | the `depth` target's | none | blur pass | the lit shaders |
|
|
10
|
+
*
|
|
11
|
+
* The sharing brings rules, and they live here because this class is the only place that can keep
|
|
12
|
+
* them.
|
|
13
|
+
*
|
|
14
|
+
* A target must not carry a texture that the pass drawing into it samples. WebGL2 calls that a
|
|
15
|
+
* rendering feedback loop, leaves the result undefined, and browsers refuse the draw. Both the AO
|
|
16
|
+
* pass and the blur pass sample the prepass depth, so neither one can bind a target that carries it.
|
|
17
|
+
* That is why two of these three targets carry no depth attachment.
|
|
18
|
+
*
|
|
19
|
+
* Three targets over two color textures. The prepass writes depth with `colorWrite` off, so nothing
|
|
20
|
+
* writes its color attachment and nothing reads it. The blur pass writes what the lit shaders read,
|
|
21
|
+
* so it takes that attachment. The handover costs one framebuffer and no GL texture at all. three's
|
|
22
|
+
* `setupRenderTarget` creates a texture only for a target that has none. `RenderTarget` has a
|
|
23
|
+
* `texture` setter, so this is supported API rather than a reach into internals.
|
|
24
|
+
*
|
|
25
|
+
* Two textures and not one, because the blur pass reads the AO pass's output while it writes its own.
|
|
26
|
+
* One texture for both is the feedback loop above.
|
|
27
|
+
*
|
|
28
|
+
* The set costs `1.5` bytes for each canvas pixel: `1.0` for 24-bit depth and `0.25` for each `R8`
|
|
29
|
+
* color texture, all at a quarter of the canvas area. A `2880 × 1800` canvas therefore holds about
|
|
30
|
+
* `7.8 MB` of them.
|
|
31
|
+
*
|
|
32
|
+
* Never call `setSize`. It calls `dispose()`, and three's deallocation deletes the GL texture of
|
|
33
|
+
* every entry in `renderTarget.textures`, a shared one included. A size change rebuilds all three
|
|
34
|
+
* instead.
|
|
35
|
+
*
|
|
36
|
+
* Dispose all three together, from here. The first disposal of a shared texture deletes it and
|
|
37
|
+
* removes its properties, and the second disposal then finds nothing to delete. No one of the three
|
|
38
|
+
* can outlive the others.
|
|
39
|
+
*
|
|
40
|
+
* The completeness check stays, because a required format is no promise that a particular driver
|
|
41
|
+
* reports the combination complete.
|
|
42
|
+
*/
|
|
43
|
+
export declare class OcclusionTargets {
|
|
44
|
+
/** The prepass's target, or `null` until this builds a complete set. */
|
|
45
|
+
private depthTarget;
|
|
46
|
+
/** The AO pass's target, over a color texture of its own, or `null` beside it. */
|
|
47
|
+
private aoTarget;
|
|
48
|
+
/** The blur pass's target, over the prepass's color texture, or `null` beside them. */
|
|
49
|
+
private blurTarget;
|
|
50
|
+
/** The set's pixel size, which is the canvas size at {@link CANVAS_FRACTION}. */
|
|
51
|
+
private readonly targetSize;
|
|
52
|
+
/** Whether this context cannot give a complete set. Once this turns true it stays true. */
|
|
53
|
+
private unavailable;
|
|
54
|
+
/** The prepass's target, or `null` until {@link OcclusionTargets.sync} returns `true`. */
|
|
55
|
+
get depth(): WebGLRenderTarget | null;
|
|
56
|
+
/** The AO pass's target, or `null` until {@link OcclusionTargets.sync} returns `true`. */
|
|
57
|
+
get ao(): WebGLRenderTarget | null;
|
|
58
|
+
/** The blur pass's target, or `null` until {@link OcclusionTargets.sync} returns `true`. */
|
|
59
|
+
get blur(): WebGLRenderTarget | null;
|
|
60
|
+
/** The set's pixel size. Read-only in spirit: the set owns it and rewrites it on a rebuild. */
|
|
61
|
+
get size(): Vector2;
|
|
62
|
+
/**
|
|
63
|
+
* Whether a set is allocated right now.
|
|
64
|
+
*
|
|
65
|
+
* A caller frees the set once when the debug GUI switches occlusion off, rather than on every frame
|
|
66
|
+
* after. This getter tells the caller which frame that is. A flag kept beside the set drifts the
|
|
67
|
+
* first time a sync fails or a rebuild releases the set on its own.
|
|
68
|
+
*/
|
|
69
|
+
get held(): boolean;
|
|
70
|
+
/**
|
|
71
|
+
* Frees all three targets and the depth texture, and forgets their size.
|
|
72
|
+
*
|
|
73
|
+
* This takes no renderer. A `RenderTarget` disposal is an event three's own renderer listens for,
|
|
74
|
+
* so an unmount effect with nothing else in hand can call it.
|
|
75
|
+
*/
|
|
76
|
+
dispose(): void;
|
|
77
|
+
/**
|
|
78
|
+
* Brings the set to the canvas's current size, and rebuilds it when that size moves.
|
|
79
|
+
* @param gl - The renderer, for the canvas size and for the completeness check.
|
|
80
|
+
* @returns Whether a complete set now exists to draw into.
|
|
81
|
+
*/
|
|
82
|
+
sync(gl: WebGLRenderer): boolean;
|
|
83
|
+
/**
|
|
84
|
+
* Allocates the set at a size, at one of the two depth widths.
|
|
85
|
+
* @param width - The set's width in pixels.
|
|
86
|
+
* @param height - Its height.
|
|
87
|
+
* @param depthType - The depth texture's type, which decides its bit width.
|
|
88
|
+
*/
|
|
89
|
+
private allocate;
|
|
90
|
+
/**
|
|
91
|
+
* Rebuilds the set, and falls back to 16-bit depth once before it gives up for good.
|
|
92
|
+
* @param gl - The renderer, for the completeness check.
|
|
93
|
+
* @param width - The set's width in pixels.
|
|
94
|
+
* @param height - Its height.
|
|
95
|
+
* @returns Whether a complete set now exists.
|
|
96
|
+
*/
|
|
97
|
+
private build;
|
|
98
|
+
/**
|
|
99
|
+
* Frees whatever is allocated, together and in one place, and forgets its size.
|
|
100
|
+
*/
|
|
101
|
+
private release;
|
|
102
|
+
/**
|
|
103
|
+
* Asks the driver whether the two framebuffer combinations this set uses came back complete, once
|
|
104
|
+
* per allocation.
|
|
105
|
+
*
|
|
106
|
+
* Binding each target through three is what builds its framebuffer, so the check runs against the
|
|
107
|
+
* real thing rather than against an intention. This puts the previously bound target back, which
|
|
108
|
+
* keeps a shared context unaware that the check ran. The MapLibre embed is that shared context.
|
|
109
|
+
*
|
|
110
|
+
* **Two checks cover three targets, because two of the targets are one combination.** The depth
|
|
111
|
+
* target is a color attachment beside a sampleable depth texture, at whichever of the two widths
|
|
112
|
+
* `build` is trying, and it is the only one of the three carrying depth at all. That is its own
|
|
113
|
+
* risk and it takes its own check. The blur target is a single `R8` color attachment over a
|
|
114
|
+
* texture the depth target already owns. The AO target is that same single `R8` attachment over a
|
|
115
|
+
* texture of its own, so the blur target covers the AO target's combination and adds the sharing,
|
|
116
|
+
* which is the part of this design a driver is most likely to refuse. A third bind would ask a
|
|
117
|
+
* question the second one already answers.
|
|
118
|
+
*
|
|
119
|
+
* **three's own renderer never calls `checkFramebufferStatus`**, so this is the only thing that
|
|
120
|
+
* detects a driver refusing either combination. What it buys is the 16-bit retry and, after that,
|
|
121
|
+
* occlusion switching itself off, rather than a plan drawing out of an incomplete framebuffer.
|
|
122
|
+
*
|
|
123
|
+
* A context that throws while three binds it answers the same question the status code answers, so
|
|
124
|
+
* this catches the throw. Occlusion switching itself off is the documented outcome. Taking the
|
|
125
|
+
* frame loop down with it is the worse one.
|
|
126
|
+
* @param gl - The renderer whose context answers.
|
|
127
|
+
* @returns Whether both combinations are complete.
|
|
128
|
+
*/
|
|
129
|
+
private verify;
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* The share of the canvas size each axis of the set takes.
|
|
133
|
+
*
|
|
134
|
+
* Half, so each pass bills a quarter of the texels a full-size pass bills. Occlusion is a
|
|
135
|
+
* low-frequency signal. The lit read finds its own fragment by normalized coordinates, so the texel
|
|
136
|
+
* count behind that read changes nothing anyone can see.
|
|
137
|
+
*
|
|
138
|
+
* The canvas size and not the drawing buffer's, which is what `gl.getSize()` reports. A device pixel
|
|
139
|
+
* ratio of `2` squares into four times the texels, and both passes bill per texel. A set sized from
|
|
140
|
+
* the buffer therefore costs four times as much on a retina screen, for detail nothing asks of it.
|
|
141
|
+
*/
|
|
142
|
+
export declare const CANVAS_FRACTION = 0.5;
|
|
143
|
+
//# sourceMappingURL=occlusion-targets.d.ts.map
|
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
import { DepthTexture, NearestFilter, RedFormat, UnsignedByteType, UnsignedIntType, UnsignedShortType, Vector2, WebGLRenderTarget, } from 'three';
|
|
2
|
+
import { createLogger } from '../../utils/logging';
|
|
3
|
+
const logger = createLogger('occlusion');
|
|
4
|
+
/**
|
|
5
|
+
* The three render targets the occlusion passes draw into, over two color textures.
|
|
6
|
+
*
|
|
7
|
+
* | Target | Color texture | Depth attachment | Written by | Read by |
|
|
8
|
+
* | ------- | ---------------------- | ---------------------- | ---------- | --------------- |
|
|
9
|
+
* | `depth` | its own, never written | `DepthTexture`, 24-bit | prepass | nothing |
|
|
10
|
+
* | `ao` | its own | none | AO pass | the blur pass |
|
|
11
|
+
* | `blur` | the `depth` target's | none | blur pass | the lit shaders |
|
|
12
|
+
*
|
|
13
|
+
* The sharing brings rules, and they live here because this class is the only place that can keep
|
|
14
|
+
* them.
|
|
15
|
+
*
|
|
16
|
+
* A target must not carry a texture that the pass drawing into it samples. WebGL2 calls that a
|
|
17
|
+
* rendering feedback loop, leaves the result undefined, and browsers refuse the draw. Both the AO
|
|
18
|
+
* pass and the blur pass sample the prepass depth, so neither one can bind a target that carries it.
|
|
19
|
+
* That is why two of these three targets carry no depth attachment.
|
|
20
|
+
*
|
|
21
|
+
* Three targets over two color textures. The prepass writes depth with `colorWrite` off, so nothing
|
|
22
|
+
* writes its color attachment and nothing reads it. The blur pass writes what the lit shaders read,
|
|
23
|
+
* so it takes that attachment. The handover costs one framebuffer and no GL texture at all. three's
|
|
24
|
+
* `setupRenderTarget` creates a texture only for a target that has none. `RenderTarget` has a
|
|
25
|
+
* `texture` setter, so this is supported API rather than a reach into internals.
|
|
26
|
+
*
|
|
27
|
+
* Two textures and not one, because the blur pass reads the AO pass's output while it writes its own.
|
|
28
|
+
* One texture for both is the feedback loop above.
|
|
29
|
+
*
|
|
30
|
+
* The set costs `1.5` bytes for each canvas pixel: `1.0` for 24-bit depth and `0.25` for each `R8`
|
|
31
|
+
* color texture, all at a quarter of the canvas area. A `2880 × 1800` canvas therefore holds about
|
|
32
|
+
* `7.8 MB` of them.
|
|
33
|
+
*
|
|
34
|
+
* Never call `setSize`. It calls `dispose()`, and three's deallocation deletes the GL texture of
|
|
35
|
+
* every entry in `renderTarget.textures`, a shared one included. A size change rebuilds all three
|
|
36
|
+
* instead.
|
|
37
|
+
*
|
|
38
|
+
* Dispose all three together, from here. The first disposal of a shared texture deletes it and
|
|
39
|
+
* removes its properties, and the second disposal then finds nothing to delete. No one of the three
|
|
40
|
+
* can outlive the others.
|
|
41
|
+
*
|
|
42
|
+
* The completeness check stays, because a required format is no promise that a particular driver
|
|
43
|
+
* reports the combination complete.
|
|
44
|
+
*/
|
|
45
|
+
export class OcclusionTargets {
|
|
46
|
+
/** The prepass's target, or `null` until this builds a complete set. */
|
|
47
|
+
depthTarget = null;
|
|
48
|
+
/** The AO pass's target, over a color texture of its own, or `null` beside it. */
|
|
49
|
+
aoTarget = null;
|
|
50
|
+
/** The blur pass's target, over the prepass's color texture, or `null` beside them. */
|
|
51
|
+
blurTarget = null;
|
|
52
|
+
/** The set's pixel size, which is the canvas size at {@link CANVAS_FRACTION}. */
|
|
53
|
+
targetSize = new Vector2();
|
|
54
|
+
/** Whether this context cannot give a complete set. Once this turns true it stays true. */
|
|
55
|
+
unavailable = false;
|
|
56
|
+
/** The prepass's target, or `null` until {@link OcclusionTargets.sync} returns `true`. */
|
|
57
|
+
get depth() {
|
|
58
|
+
return this.depthTarget;
|
|
59
|
+
}
|
|
60
|
+
/** The AO pass's target, or `null` until {@link OcclusionTargets.sync} returns `true`. */
|
|
61
|
+
get ao() {
|
|
62
|
+
return this.aoTarget;
|
|
63
|
+
}
|
|
64
|
+
/** The blur pass's target, or `null` until {@link OcclusionTargets.sync} returns `true`. */
|
|
65
|
+
get blur() {
|
|
66
|
+
return this.blurTarget;
|
|
67
|
+
}
|
|
68
|
+
/** The set's pixel size. Read-only in spirit: the set owns it and rewrites it on a rebuild. */
|
|
69
|
+
get size() {
|
|
70
|
+
return this.targetSize;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Whether a set is allocated right now.
|
|
74
|
+
*
|
|
75
|
+
* A caller frees the set once when the debug GUI switches occlusion off, rather than on every frame
|
|
76
|
+
* after. This getter tells the caller which frame that is. A flag kept beside the set drifts the
|
|
77
|
+
* first time a sync fails or a rebuild releases the set on its own.
|
|
78
|
+
*/
|
|
79
|
+
get held() {
|
|
80
|
+
return this.depthTarget !== null;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Frees all three targets and the depth texture, and forgets their size.
|
|
84
|
+
*
|
|
85
|
+
* This takes no renderer. A `RenderTarget` disposal is an event three's own renderer listens for,
|
|
86
|
+
* so an unmount effect with nothing else in hand can call it.
|
|
87
|
+
*/
|
|
88
|
+
dispose() {
|
|
89
|
+
this.release();
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Brings the set to the canvas's current size, and rebuilds it when that size moves.
|
|
93
|
+
* @param gl - The renderer, for the canvas size and for the completeness check.
|
|
94
|
+
* @returns Whether a complete set now exists to draw into.
|
|
95
|
+
*/
|
|
96
|
+
sync(gl) {
|
|
97
|
+
if (this.unavailable)
|
|
98
|
+
return false;
|
|
99
|
+
const canvas = gl.getSize(_canvas);
|
|
100
|
+
const width = Math.floor(canvas.x * CANVAS_FRACTION);
|
|
101
|
+
const height = Math.floor(canvas.y * CANVAS_FRACTION);
|
|
102
|
+
// A canvas with no area yet, during a layout or before one. The next resize can undo that, so
|
|
103
|
+
// this is no reason to give up for good.
|
|
104
|
+
if (width < 1 || height < 1)
|
|
105
|
+
return false;
|
|
106
|
+
if (this.depthTarget && this.targetSize.x === width && this.targetSize.y === height) {
|
|
107
|
+
return true;
|
|
108
|
+
}
|
|
109
|
+
return this.build(gl, width, height);
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Allocates the set at a size, at one of the two depth widths.
|
|
113
|
+
* @param width - The set's width in pixels.
|
|
114
|
+
* @param height - Its height.
|
|
115
|
+
* @param depthType - The depth texture's type, which decides its bit width.
|
|
116
|
+
*/
|
|
117
|
+
allocate(width, height, depthType) {
|
|
118
|
+
this.depthTarget = new WebGLRenderTarget(width, height, {
|
|
119
|
+
...SINGLE_CHANNEL_TEXTURE,
|
|
120
|
+
depthBuffer: true,
|
|
121
|
+
depthTexture: new DepthTexture(width, height, depthType),
|
|
122
|
+
});
|
|
123
|
+
this.aoTarget = new WebGLRenderTarget(width, height, {
|
|
124
|
+
...SINGLE_CHANNEL_TEXTURE,
|
|
125
|
+
depthBuffer: false,
|
|
126
|
+
});
|
|
127
|
+
// No format of its own. The next line drops the texture this target is built with, so the
|
|
128
|
+
// missing depth attachment is the only option that outlives the constructor.
|
|
129
|
+
this.blurTarget = new WebGLRenderTarget(width, height, { depthBuffer: false });
|
|
130
|
+
// The sharing, in one line. `RenderTarget` has a `texture` setter, so this is supported API
|
|
131
|
+
// rather than a reach into internals. This drops the target's own fresh texture, which never
|
|
132
|
+
// uploaded. Nothing writes the prepass's color attachment and nothing reads it, so the blur pass
|
|
133
|
+
// can take it.
|
|
134
|
+
this.blurTarget.texture = this.depthTarget.texture;
|
|
135
|
+
this.targetSize.set(width, height);
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Rebuilds the set, and falls back to 16-bit depth once before it gives up for good.
|
|
139
|
+
* @param gl - The renderer, for the completeness check.
|
|
140
|
+
* @param width - The set's width in pixels.
|
|
141
|
+
* @param height - Its height.
|
|
142
|
+
* @returns Whether a complete set now exists.
|
|
143
|
+
*/
|
|
144
|
+
build(gl, width, height) {
|
|
145
|
+
this.release();
|
|
146
|
+
for (const depthType of DEPTH_TYPES) {
|
|
147
|
+
this.allocate(width, height, depthType);
|
|
148
|
+
if (this.verify(gl))
|
|
149
|
+
return true;
|
|
150
|
+
this.release();
|
|
151
|
+
}
|
|
152
|
+
// Off for good, and silent as far as a caller can tell. The SDK runs in old Android WebViews,
|
|
153
|
+
// and nobody can do anything about a driver that will not report these targets complete. The
|
|
154
|
+
// debug log is for whoever has to find out why a plan shipped without occlusion.
|
|
155
|
+
logger.debug('ambient occlusion off: this context gives no complete render targets');
|
|
156
|
+
this.unavailable = true;
|
|
157
|
+
return false;
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Frees whatever is allocated, together and in one place, and forgets its size.
|
|
161
|
+
*/
|
|
162
|
+
release() {
|
|
163
|
+
// The order does not matter. The first disposal of the shared texture deletes it and removes its
|
|
164
|
+
// properties, and the second one finds nothing left to delete. All three still have to happen.
|
|
165
|
+
// The AO target's own texture belongs to nobody else, so either order deletes it once.
|
|
166
|
+
this.depthTarget?.dispose();
|
|
167
|
+
this.aoTarget?.dispose();
|
|
168
|
+
this.blurTarget?.dispose();
|
|
169
|
+
this.depthTarget?.depthTexture?.dispose();
|
|
170
|
+
this.depthTarget = null;
|
|
171
|
+
this.aoTarget = null;
|
|
172
|
+
this.blurTarget = null;
|
|
173
|
+
this.targetSize.set(0, 0);
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Asks the driver whether the two framebuffer combinations this set uses came back complete, once
|
|
177
|
+
* per allocation.
|
|
178
|
+
*
|
|
179
|
+
* Binding each target through three is what builds its framebuffer, so the check runs against the
|
|
180
|
+
* real thing rather than against an intention. This puts the previously bound target back, which
|
|
181
|
+
* keeps a shared context unaware that the check ran. The MapLibre embed is that shared context.
|
|
182
|
+
*
|
|
183
|
+
* **Two checks cover three targets, because two of the targets are one combination.** The depth
|
|
184
|
+
* target is a color attachment beside a sampleable depth texture, at whichever of the two widths
|
|
185
|
+
* `build` is trying, and it is the only one of the three carrying depth at all. That is its own
|
|
186
|
+
* risk and it takes its own check. The blur target is a single `R8` color attachment over a
|
|
187
|
+
* texture the depth target already owns. The AO target is that same single `R8` attachment over a
|
|
188
|
+
* texture of its own, so the blur target covers the AO target's combination and adds the sharing,
|
|
189
|
+
* which is the part of this design a driver is most likely to refuse. A third bind would ask a
|
|
190
|
+
* question the second one already answers.
|
|
191
|
+
*
|
|
192
|
+
* **three's own renderer never calls `checkFramebufferStatus`**, so this is the only thing that
|
|
193
|
+
* detects a driver refusing either combination. What it buys is the 16-bit retry and, after that,
|
|
194
|
+
* occlusion switching itself off, rather than a plan drawing out of an incomplete framebuffer.
|
|
195
|
+
*
|
|
196
|
+
* A context that throws while three binds it answers the same question the status code answers, so
|
|
197
|
+
* this catches the throw. Occlusion switching itself off is the documented outcome. Taking the
|
|
198
|
+
* frame loop down with it is the worse one.
|
|
199
|
+
* @param gl - The renderer whose context answers.
|
|
200
|
+
* @returns Whether both combinations are complete.
|
|
201
|
+
*/
|
|
202
|
+
verify(gl) {
|
|
203
|
+
const context = webgl2(gl);
|
|
204
|
+
if (!context)
|
|
205
|
+
return false;
|
|
206
|
+
const previous = gl.getRenderTarget();
|
|
207
|
+
try {
|
|
208
|
+
let complete = true;
|
|
209
|
+
for (const target of [this.depthTarget, this.blurTarget]) {
|
|
210
|
+
gl.setRenderTarget(target);
|
|
211
|
+
if (context.checkFramebufferStatus(context.FRAMEBUFFER) !== context.FRAMEBUFFER_COMPLETE) {
|
|
212
|
+
complete = false;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
return complete;
|
|
216
|
+
}
|
|
217
|
+
catch {
|
|
218
|
+
return false;
|
|
219
|
+
}
|
|
220
|
+
finally {
|
|
221
|
+
gl.setRenderTarget(previous);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* The renderer's context, when it is one these targets can be built on.
|
|
227
|
+
*
|
|
228
|
+
* A runtime feature detection, and not a type narrowing a reader can drop. The SDK still runs in
|
|
229
|
+
* WebViews whose context can be neither WebGL2 nor able to carry a readable depth texture. The
|
|
230
|
+
* constructor itself can be missing, so this checks for it before it uses it.
|
|
231
|
+
* @param gl - The renderer to read.
|
|
232
|
+
* @returns The context, or `undefined` when the targets cannot be built on it.
|
|
233
|
+
*/
|
|
234
|
+
function webgl2(gl) {
|
|
235
|
+
const context = gl.getContext();
|
|
236
|
+
if (typeof WebGL2RenderingContext !== 'function')
|
|
237
|
+
return undefined;
|
|
238
|
+
return context instanceof WebGL2RenderingContext ? context : undefined;
|
|
239
|
+
}
|
|
240
|
+
/**
|
|
241
|
+
* The share of the canvas size each axis of the set takes.
|
|
242
|
+
*
|
|
243
|
+
* Half, so each pass bills a quarter of the texels a full-size pass bills. Occlusion is a
|
|
244
|
+
* low-frequency signal. The lit read finds its own fragment by normalized coordinates, so the texel
|
|
245
|
+
* count behind that read changes nothing anyone can see.
|
|
246
|
+
*
|
|
247
|
+
* The canvas size and not the drawing buffer's, which is what `gl.getSize()` reports. A device pixel
|
|
248
|
+
* ratio of `2` squares into four times the texels, and both passes bill per texel. A set sized from
|
|
249
|
+
* the buffer therefore costs four times as much on a retina screen, for detail nothing asks of it.
|
|
250
|
+
*/
|
|
251
|
+
export const CANVAS_FRACTION = 0.5;
|
|
252
|
+
/**
|
|
253
|
+
* The depth widths this tries, in order.
|
|
254
|
+
*
|
|
255
|
+
* 24 bits first, because 16-bit depth at this size over a plan's range bands visibly in the
|
|
256
|
+
* occlusion. 16 bits is the one retry worth making before occlusion latches off for good. It saves
|
|
257
|
+
* memory rather than costing it, which makes it a fallback rather than a default.
|
|
258
|
+
*/
|
|
259
|
+
const DEPTH_TYPES = [UnsignedIntType, UnsignedShortType];
|
|
260
|
+
/**
|
|
261
|
+
* The shared settings for the two color textures the occlusion passes draw into.
|
|
262
|
+
*
|
|
263
|
+
* `R8` holds one byte a texel. It is a color-renderable sized internal format in WebGL 2, so it is
|
|
264
|
+
* required to work rather than merely likely to.
|
|
265
|
+
*
|
|
266
|
+
* Both the blur pass and the lit read weight their samples by depth, so hardware filtering only
|
|
267
|
+
* smears occlusion across the edges those reads respect.
|
|
268
|
+
*/
|
|
269
|
+
const SINGLE_CHANNEL_TEXTURE = {
|
|
270
|
+
format: RedFormat,
|
|
271
|
+
type: UnsignedByteType,
|
|
272
|
+
minFilter: NearestFilter,
|
|
273
|
+
magFilter: NearestFilter,
|
|
274
|
+
};
|
|
275
|
+
/** Scratch for the canvas size, read once per sync. */
|
|
276
|
+
const _canvas = new Vector2();
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { type LineDef } from '../../types/defs';
|
|
2
|
+
import { type InstanceProps } from '../containers';
|
|
3
|
+
import { type LineContainer } from './line-container';
|
|
4
|
+
/** Props for {@link LineInstance}. */
|
|
5
|
+
export type LineInstanceProps = InstanceProps<LineDef, LineContainer>;
|
|
6
|
+
/**
|
|
7
|
+
* Keeps one line's instance current: claims a slot on mount, writes the def's endpoints, color,
|
|
8
|
+
* width and visibility on mount and after every `update(def)`, and releases the slot on unmount.
|
|
9
|
+
*
|
|
10
|
+
* Renders no scene node — the batch belongs to the container. Nothing here can force a re-claim:
|
|
11
|
+
* unlike a shape, whose geometry is its slot's resource, a line is the same segment template
|
|
12
|
+
* wherever it runs, so moving one is an ordinary write and its slot survives.
|
|
13
|
+
*
|
|
14
|
+
* Memoized for the same reason as `ShapeInstance`: a structural change hands the layer a fresh
|
|
15
|
+
* `defs` snapshot, so element-identity bailout never applies and only prop equality keeps unchanged
|
|
16
|
+
* siblings from re-rendering. Correctness never depends on it.
|
|
17
|
+
* @param props - The def and its container.
|
|
18
|
+
* @returns `null`; the instance lives in the batch, not the scene graph.
|
|
19
|
+
*/
|
|
20
|
+
export declare const LineInstance: import("react").MemoExoticComponent<(props: LineInstanceProps) => null>;
|
|
21
|
+
//# sourceMappingURL=LineInstance.d.ts.map
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { memo } from 'react';
|
|
2
|
+
import { Vector3 } from 'three';
|
|
3
|
+
import { createLogger } from '../../utils/logging';
|
|
4
|
+
import { useDefSync, writeColorIfChanged } from '../containers';
|
|
5
|
+
const logger = createLogger('line-instance');
|
|
6
|
+
/**
|
|
7
|
+
* Keeps one line's instance current: claims a slot on mount, writes the def's endpoints, color,
|
|
8
|
+
* width and visibility on mount and after every `update(def)`, and releases the slot on unmount.
|
|
9
|
+
*
|
|
10
|
+
* Renders no scene node — the batch belongs to the container. Nothing here can force a re-claim:
|
|
11
|
+
* unlike a shape, whose geometry is its slot's resource, a line is the same segment template
|
|
12
|
+
* wherever it runs, so moving one is an ordinary write and its slot survives.
|
|
13
|
+
*
|
|
14
|
+
* Memoized for the same reason as `ShapeInstance`: a structural change hands the layer a fresh
|
|
15
|
+
* `defs` snapshot, so element-identity bailout never applies and only prop equality keeps unchanged
|
|
16
|
+
* siblings from re-rendering. Correctness never depends on it.
|
|
17
|
+
* @param props - The def and its container.
|
|
18
|
+
* @returns `null`; the instance lives in the batch, not the scene graph.
|
|
19
|
+
*/
|
|
20
|
+
export const LineInstance = memo(function LineInstance(props) {
|
|
21
|
+
const { def, container } = props;
|
|
22
|
+
useDefSync(container, def, null, (slot) => {
|
|
23
|
+
writeLinePoints(container, slot, def);
|
|
24
|
+
writeColorIfChanged(container.mesh, slot, def, logger);
|
|
25
|
+
writeLineWidth(container, slot, def);
|
|
26
|
+
});
|
|
27
|
+
return null;
|
|
28
|
+
});
|
|
29
|
+
/**
|
|
30
|
+
* Writes a line's endpoints into its instance.
|
|
31
|
+
*
|
|
32
|
+
* A `LineLike` is 2D plus an optional elevation, which is what places the segment on its layer's
|
|
33
|
+
* plane; the batch compares and skips, so a def that only changed color pays two vector fills.
|
|
34
|
+
* @param container - The container holding the instance.
|
|
35
|
+
* @param slot - The instance's slot.
|
|
36
|
+
* @param def - The line def supplying the endpoints.
|
|
37
|
+
*/
|
|
38
|
+
function writeLinePoints(container, slot, def) {
|
|
39
|
+
const { p0, p1, elevation = 0 } = def.points;
|
|
40
|
+
_start.set(p0.x, p0.y, elevation);
|
|
41
|
+
_end.set(p1.x, p1.y, elevation);
|
|
42
|
+
// Compared before writing, like every other instance write: a color-only `update(def)` re-runs the
|
|
43
|
+
// whole sync, and an unchanged segment costs six comparisons instead of two uniform uploads and a
|
|
44
|
+
// bounds invalidation.
|
|
45
|
+
container.mesh.getPointsAt(slot, _heldStart, _heldEnd);
|
|
46
|
+
if (_heldStart.equals(_start) && _heldEnd.equals(_end))
|
|
47
|
+
return;
|
|
48
|
+
container.mesh.setPointsAt(slot, _start, _end);
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Writes a line's width into its instance, skipping the upload when it did not change.
|
|
52
|
+
*
|
|
53
|
+
* Clamped at zero, which is where the def's contract and the batch's part ways: the batch reads a
|
|
54
|
+
* negative width as "this member has none of its own" and falls back to the material's, an
|
|
55
|
+
* affordance for callers that never set one. A `LineDef` always carries a width, so a negative one
|
|
56
|
+
* is bad input rather than a request to inherit — and it must not come out thicker than a zero.
|
|
57
|
+
* @param container - The container holding the instance.
|
|
58
|
+
* @param slot - The instance's slot.
|
|
59
|
+
* @param def - The line def supplying the width.
|
|
60
|
+
*/
|
|
61
|
+
function writeLineWidth(container, slot, def) {
|
|
62
|
+
const width = Math.max(def.width, 0);
|
|
63
|
+
if (container.mesh.getWidthAt(slot) === width)
|
|
64
|
+
return;
|
|
65
|
+
container.mesh.setWidthAt(slot, width);
|
|
66
|
+
}
|
|
67
|
+
// Scratch shared by every endpoint write: filled and consumed within a single synchronous
|
|
68
|
+
// statement, so a write costs one buffer upload and no allocation. The `_held*` pair is only ever
|
|
69
|
+
// read back and compared, so it never collides with the endpoints being written.
|
|
70
|
+
const _start = new Vector3();
|
|
71
|
+
const _end = new Vector3();
|
|
72
|
+
const _heldStart = new Vector3();
|
|
73
|
+
const _heldEnd = new Vector3();
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { type LineDef } from '../../types/defs';
|
|
2
|
+
import { type DefsProps } from '../containers';
|
|
3
|
+
/** Props for {@link Lines}. */
|
|
4
|
+
export type LinesProps = DefsProps<LineDef>;
|
|
5
|
+
/**
|
|
6
|
+
* Draws a collection of lines as members of one `BatchedLines`, so a layer of route segments
|
|
7
|
+
* renders in a single instanced draw.
|
|
8
|
+
*
|
|
9
|
+
* Membership is reconciled by React exactly as it is for shapes: a new `defs` array re-renders this
|
|
10
|
+
* component and React mounts or unmounts only the delta, with no differ of our own. Order is applied
|
|
11
|
+
* as it is for images — the container labels each slot with its def's place in the layer, and the
|
|
12
|
+
* batch's `customSort` orders the draw list by it, so a reorder writes nothing to the GPU.
|
|
13
|
+
* @param props - The lines to draw.
|
|
14
|
+
* @returns The batch and one instance component per line.
|
|
15
|
+
*/
|
|
16
|
+
export declare function Lines(props: LinesProps): import("react").JSX.Element;
|
|
17
|
+
//# sourceMappingURL=Lines.d.ts.map
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { BatchedLines } from '../../core/lines/BatchedLines';
|
|
3
|
+
import { defKey, useContainer } from '../containers';
|
|
4
|
+
import { LineMaterial } from '../materials/LineMaterial';
|
|
5
|
+
import { LineContainer } from './line-container';
|
|
6
|
+
import { LineInstance } from './LineInstance';
|
|
7
|
+
/**
|
|
8
|
+
* Draws a collection of lines as members of one `BatchedLines`, so a layer of route segments
|
|
9
|
+
* renders in a single instanced draw.
|
|
10
|
+
*
|
|
11
|
+
* Membership is reconciled by React exactly as it is for shapes: a new `defs` array re-renders this
|
|
12
|
+
* component and React mounts or unmounts only the delta, with no differ of our own. Order is applied
|
|
13
|
+
* as it is for images — the container labels each slot with its def's place in the layer, and the
|
|
14
|
+
* batch's `customSort` orders the draw list by it, so a reorder writes nothing to the GPU.
|
|
15
|
+
* @param props - The lines to draw.
|
|
16
|
+
* @returns The batch and one instance component per line.
|
|
17
|
+
*/
|
|
18
|
+
export function Lines(props) {
|
|
19
|
+
const { defs } = props;
|
|
20
|
+
// Only the instances are staged; the container is sized and ordered from the whole membership.
|
|
21
|
+
const mounted = props.mounted ?? defs.length;
|
|
22
|
+
// Sized from the first render's defs; membership changes grow the batch in place, never rebuild
|
|
23
|
+
// it. The container is bookkeeping only, so the copy StrictMode's double-invoked initializer
|
|
24
|
+
// discards is plain garbage.
|
|
25
|
+
const container = useContainer(() => new LineContainer(defs), defs);
|
|
26
|
+
// `args` is diffed element-wise and a change would dispose and rebuild the batch, orphaning every
|
|
27
|
+
// claimed slot — it is a frozen snapshot, so that never happens. The `ref` arrow is
|
|
28
|
+
// deliberate: `ref={container.bind}` would make the React Compiler infer `container` itself as a
|
|
29
|
+
// ref and reject its render-time reads.
|
|
30
|
+
return (_jsxs(_Fragment, { children: [_jsx(BatchedLines, { args: container.args, ref: (mesh) => container.bind(mesh), customSort: container.customSort, children: _jsx(LineMaterial, {}) }), defs.slice(0, mounted).map((def) => (_jsx(LineInstance, { def: def, container: container }, defKey(def))))] }));
|
|
31
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { Lines } from './Lines';
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { type InstancedMeshImplParams } from '../../core/instanced/InstancedMesh';
|
|
2
|
+
import { type BatchedLinesImpl } from '../../core/lines/BatchedLines';
|
|
3
|
+
import { type LineDef } from '../../types/defs';
|
|
4
|
+
import { SlotContainer } from '../containers';
|
|
5
|
+
/**
|
|
6
|
+
* The constructor tuple R3F builds a line batch from, passed as `<BatchedLines args={…}>`. R3F diffs
|
|
7
|
+
* `args` element-wise and rebuilds the object when any element changes, so this is snapshotted once
|
|
8
|
+
* and never mutated.
|
|
9
|
+
*
|
|
10
|
+
* Only the options: the batch builds its own segment template and takes its material from the
|
|
11
|
+
* `<LineMaterial>` child R3F mounts under it.
|
|
12
|
+
*/
|
|
13
|
+
export type LineMeshArgs = [params: InstancedMeshImplParams];
|
|
14
|
+
/**
|
|
15
|
+
* One line layer's bookkeeping.
|
|
16
|
+
*
|
|
17
|
+
* Thinner than the shape container and the image one alike, because a line has no per-instance
|
|
18
|
+
* resource to allocate against — every member draws the same segment template, placed by its own
|
|
19
|
+
* endpoints. That is why the resource type is `null` here: nothing about a `LineDef` can force a
|
|
20
|
+
* re-claim, so a def keeps its slot for as long as it is mounted, `points` included.
|
|
21
|
+
*/
|
|
22
|
+
export declare class LineContainer extends SlotContainer<null, BatchedLinesImpl, LineDef> {
|
|
23
|
+
/** The constructor args R3F builds the batch from; a frozen snapshot, never mutated. */
|
|
24
|
+
readonly args: LineMeshArgs;
|
|
25
|
+
/**
|
|
26
|
+
* Builds a line layer's bookkeeping, sized to hold `defs` exactly so a layer that mounts full
|
|
27
|
+
* never reallocates. The batch itself is R3F's — the component renders `<BatchedLines>` and hands
|
|
28
|
+
* it over through {@link SlotContainer.bind} — so R3F disposes it on unmount.
|
|
29
|
+
* @param defs - The layer's lines, used only to size the initial capacity.
|
|
30
|
+
*/
|
|
31
|
+
constructor(defs: readonly LineDef[]);
|
|
32
|
+
protected addInstance(): number;
|
|
33
|
+
protected removeInstance(slot: number): void;
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=line-container.d.ts.map
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { SlotContainer } from '../containers';
|
|
2
|
+
/**
|
|
3
|
+
* One line layer's bookkeeping.
|
|
4
|
+
*
|
|
5
|
+
* Thinner than the shape container and the image one alike, because a line has no per-instance
|
|
6
|
+
* resource to allocate against — every member draws the same segment template, placed by its own
|
|
7
|
+
* endpoints. That is why the resource type is `null` here: nothing about a `LineDef` can force a
|
|
8
|
+
* re-claim, so a def keeps its slot for as long as it is mounted, `points` included.
|
|
9
|
+
*/
|
|
10
|
+
export class LineContainer extends SlotContainer {
|
|
11
|
+
/** The constructor args R3F builds the batch from; a frozen snapshot, never mutated. */
|
|
12
|
+
args;
|
|
13
|
+
/**
|
|
14
|
+
* Builds a line layer's bookkeeping, sized to hold `defs` exactly so a layer that mounts full
|
|
15
|
+
* never reallocates. The batch itself is R3F's — the component renders `<BatchedLines>` and hands
|
|
16
|
+
* it over through {@link SlotContainer.bind} — so R3F disposes it on unmount.
|
|
17
|
+
* @param defs - The layer's lines, used only to size the initial capacity.
|
|
18
|
+
*/
|
|
19
|
+
constructor(defs) {
|
|
20
|
+
// Reversed, and that is the material's doing rather than this layer's: a line batch stencils
|
|
21
|
+
// first-fragment-wins so translucent lines do not darken where they overlap, which hands the
|
|
22
|
+
// pixel to whichever member rasterizes *first*. Draw order is therefore the reverse of paint
|
|
23
|
+
// order. Slots are still labeled in paint order, so "last sibling on top" holds here as
|
|
24
|
+
// everywhere else.
|
|
25
|
+
super({ reverse: true });
|
|
26
|
+
this.args = [{ capacity: Math.max(defs.length, 1) }];
|
|
27
|
+
}
|
|
28
|
+
addInstance() {
|
|
29
|
+
// The batch grows its own buffers if the layer ever outgrows the capacity it was sized to, so
|
|
30
|
+
// there is nothing to pre-scan; the id is captured from the creation callback.
|
|
31
|
+
let slot = -1;
|
|
32
|
+
this.mesh.addInstances(1, (id) => {
|
|
33
|
+
slot = id;
|
|
34
|
+
});
|
|
35
|
+
return slot;
|
|
36
|
+
}
|
|
37
|
+
removeInstance(slot) {
|
|
38
|
+
this.mesh.removeInstances(slot);
|
|
39
|
+
}
|
|
40
|
+
}
|