@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.
- 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,92 @@
|
|
|
1
|
+
import { type ColorRepresentation, type WebGLProgramParametersWithUniforms } from 'three';
|
|
2
|
+
/** Props for {@link ColorMaterial}. */
|
|
3
|
+
export interface ColorMaterialProps {
|
|
4
|
+
/** Base color. Defaults to white, which is what a per-instance tint multiplies against. */
|
|
5
|
+
color?: ColorRepresentation;
|
|
6
|
+
/**
|
|
7
|
+
* Whether this batch draws volumes, which are the meshes carrying real z. A volume needs shading
|
|
8
|
+
* to read as solid, and depth testing to occlude itself and its neighbors. Defaults to `false`,
|
|
9
|
+
* which is the flat content every 2D primitive in a plan draws as.
|
|
10
|
+
*/
|
|
11
|
+
solid?: boolean;
|
|
12
|
+
/**
|
|
13
|
+
* Whether those volumes are translucent, which moves them into three's transparent pass.
|
|
14
|
+
*
|
|
15
|
+
* This means something only together with {@link solid}. Flat content never uses it, for the reason
|
|
16
|
+
* the note below gives.
|
|
17
|
+
*/
|
|
18
|
+
translucent?: boolean;
|
|
19
|
+
/**
|
|
20
|
+
* Runs on the shader before the dim patch runs. This is the slot for a caller's own shader surgery,
|
|
21
|
+
* such as the background quad's clip-space projection and its dim-uniform stand-in.
|
|
22
|
+
*
|
|
23
|
+
* This component captures the handler at the first render and keeps it for the material's lifetime,
|
|
24
|
+
* the way it keeps every other axis. A handler identity that changes risks a recompile per render.
|
|
25
|
+
*/
|
|
26
|
+
onBeforeCompile?: (parameters: WebGLProgramParametersWithUniforms) => void;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* The material a shape batch draws with. It renders as a child of the mesh, so R3F attaches, updates
|
|
30
|
+
* and disposes it. It is also troika's base material for text, which never asks for `solid`. See
|
|
31
|
+
* `Texts`.
|
|
32
|
+
*
|
|
33
|
+
* One component with a branch rather than two files. The two are the same thing along one axis, which
|
|
34
|
+
* is what a batch of shapes draws with. The three differences read best side by side:
|
|
35
|
+
*
|
|
36
|
+
* | | flat (default) | `solid` |
|
|
37
|
+
* | --- | --- | --- |
|
|
38
|
+
* | base | `MeshBasicMaterial`, unlit | `MeshLambertMaterial`, lit |
|
|
39
|
+
* | `depthWrite` | off, from {@link sharedParams} | on |
|
|
40
|
+
* | polygon offset | on, from {@link sharedParams} | off |
|
|
41
|
+
*
|
|
42
|
+
* Each difference carries its own weight. A volume is the one kind of content that writes depth. That
|
|
43
|
+
* is how its own faces resolve against each other, and it is what lets 2D content drawn afterwards be
|
|
44
|
+
* occluded by it. A volume carries no polygon offset, because solids are what everything else is
|
|
45
|
+
* biased against. An offset on the solids too only moves the problem.
|
|
46
|
+
*
|
|
47
|
+
* The lit branch is `MeshLambertMaterial` alone. It has no specular lobe at all, so this never sets
|
|
48
|
+
* `ior` or `specularIntensity`. three evaluates it per fragment with `BRDF_Lambert`, which is
|
|
49
|
+
* `albedo / π`. Its vertex shader carries `batching_vertex` and `color_vertex`, so
|
|
50
|
+
* `BatchedMesh.setColorAt` keeps working. Its fragment shader carries the output chain, so the dim
|
|
51
|
+
* patch keeps its anchor.
|
|
52
|
+
*
|
|
53
|
+
* The light rig alone holds a roof to its authored color now. `Lighting` derives both intensities from
|
|
54
|
+
* the one `contrast` control every frame. An up-facing surface then lands on the color it was authored
|
|
55
|
+
* with, and no surface the design accounts for clips.
|
|
56
|
+
*
|
|
57
|
+
* Per-instance color arrives through `BatchedMesh.setColorAt` on either branch. three injects the
|
|
58
|
+
* batching-color chunk for whatever material the batch draws with.
|
|
59
|
+
*
|
|
60
|
+
* `translucent` is the one place the array-order contract bends, and it bends on purpose. It sets
|
|
61
|
+
* `transparent: true`, which moves the batch into three's transparent list. three draws that list
|
|
62
|
+
* after every opaque object in the scene, whatever `renderOrder` says. See
|
|
63
|
+
* `WebGLRenderer.renderScene`. That is what lets a translucent volume blend with the opaque content
|
|
64
|
+
* behind it, even when that content lives in a later layer. No position in the ordered stream can do
|
|
65
|
+
* the same, because a translucent surface can only blend with what already sits in the framebuffer.
|
|
66
|
+
*
|
|
67
|
+
* The reverse case is what it costs. Take a planar def sitting on a translucent volume, such as a
|
|
68
|
+
* label or a logo on a glass roof. It draws in the opaque pass, so it lands before the volume. The
|
|
69
|
+
* label composites against whatever was under the glass rather than onto it, and the volume then
|
|
70
|
+
* glazes over the label. So a translucent solid is right about what is behind it and wrong about what is on it. Flat
|
|
71
|
+
* content never takes this flag for the same reason, and for one of its own: 2D content is coplanar,
|
|
72
|
+
* so it has no depth order to sort by and order is all it has.
|
|
73
|
+
*
|
|
74
|
+
* `forceSinglePass` is switched off here alone. three draws a `DoubleSide` transparent material
|
|
75
|
+
* twice, back faces and then front. See `WebGLRenderer.js:1348`. That is a correctly ordered two-pass
|
|
76
|
+
* for free. It is why a closed translucent volume does not blend its own far and near surfaces in
|
|
77
|
+
* whatever order the batch packed them.
|
|
78
|
+
*
|
|
79
|
+
* Depth writes stay on even when translucent, which is not three's usual convention. Two rules depend
|
|
80
|
+
* on it. A translucent volume must still be occluded by anything nearer, and a farther volume drawn
|
|
81
|
+
* later must not paint over it. MapLibre makes the same choice for `fill-extrusion`, with
|
|
82
|
+
* `DepthMode.ReadWrite` in both of its passes. The design spec's Decision 2 records the trade and the
|
|
83
|
+
* two rejected alternatives.
|
|
84
|
+
*
|
|
85
|
+
* The branch is stable for a mounted batch, because `Shapes` renders a separate element per kind and
|
|
86
|
+
* each one carries a constant `solid`. So R3F never swaps one base class for the other under a live
|
|
87
|
+
* mesh.
|
|
88
|
+
* @param props - The material's base color, and whether it draws volumes.
|
|
89
|
+
* @returns The material element.
|
|
90
|
+
*/
|
|
91
|
+
export declare function ColorMaterial(props: ColorMaterialProps): import("react").JSX.Element;
|
|
92
|
+
//# sourceMappingURL=ColorMaterial.d.ts.map
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useState } from 'react';
|
|
3
|
+
import { createLogger } from '../../utils/logging';
|
|
4
|
+
import { useLayerOverlay } from '../layer/layer-context';
|
|
5
|
+
import { applyAmbientOcclusion } from './effects/ao';
|
|
6
|
+
import { useDimPatch } from './effects/color-dim';
|
|
7
|
+
import { applyContactShadow } from './effects/contact-shadow';
|
|
8
|
+
import { useSceneEffects } from './effects/effects-context';
|
|
9
|
+
import { overlayParams, sharedParams } from './shared';
|
|
10
|
+
const logger = createLogger('color-material');
|
|
11
|
+
/**
|
|
12
|
+
* The material a shape batch draws with. It renders as a child of the mesh, so R3F attaches, updates
|
|
13
|
+
* and disposes it. It is also troika's base material for text, which never asks for `solid`. See
|
|
14
|
+
* `Texts`.
|
|
15
|
+
*
|
|
16
|
+
* One component with a branch rather than two files. The two are the same thing along one axis, which
|
|
17
|
+
* is what a batch of shapes draws with. The three differences read best side by side:
|
|
18
|
+
*
|
|
19
|
+
* | | flat (default) | `solid` |
|
|
20
|
+
* | --- | --- | --- |
|
|
21
|
+
* | base | `MeshBasicMaterial`, unlit | `MeshLambertMaterial`, lit |
|
|
22
|
+
* | `depthWrite` | off, from {@link sharedParams} | on |
|
|
23
|
+
* | polygon offset | on, from {@link sharedParams} | off |
|
|
24
|
+
*
|
|
25
|
+
* Each difference carries its own weight. A volume is the one kind of content that writes depth. That
|
|
26
|
+
* is how its own faces resolve against each other, and it is what lets 2D content drawn afterwards be
|
|
27
|
+
* occluded by it. A volume carries no polygon offset, because solids are what everything else is
|
|
28
|
+
* biased against. An offset on the solids too only moves the problem.
|
|
29
|
+
*
|
|
30
|
+
* The lit branch is `MeshLambertMaterial` alone. It has no specular lobe at all, so this never sets
|
|
31
|
+
* `ior` or `specularIntensity`. three evaluates it per fragment with `BRDF_Lambert`, which is
|
|
32
|
+
* `albedo / π`. Its vertex shader carries `batching_vertex` and `color_vertex`, so
|
|
33
|
+
* `BatchedMesh.setColorAt` keeps working. Its fragment shader carries the output chain, so the dim
|
|
34
|
+
* patch keeps its anchor.
|
|
35
|
+
*
|
|
36
|
+
* The light rig alone holds a roof to its authored color now. `Lighting` derives both intensities from
|
|
37
|
+
* the one `contrast` control every frame. An up-facing surface then lands on the color it was authored
|
|
38
|
+
* with, and no surface the design accounts for clips.
|
|
39
|
+
*
|
|
40
|
+
* Per-instance color arrives through `BatchedMesh.setColorAt` on either branch. three injects the
|
|
41
|
+
* batching-color chunk for whatever material the batch draws with.
|
|
42
|
+
*
|
|
43
|
+
* `translucent` is the one place the array-order contract bends, and it bends on purpose. It sets
|
|
44
|
+
* `transparent: true`, which moves the batch into three's transparent list. three draws that list
|
|
45
|
+
* after every opaque object in the scene, whatever `renderOrder` says. See
|
|
46
|
+
* `WebGLRenderer.renderScene`. That is what lets a translucent volume blend with the opaque content
|
|
47
|
+
* behind it, even when that content lives in a later layer. No position in the ordered stream can do
|
|
48
|
+
* the same, because a translucent surface can only blend with what already sits in the framebuffer.
|
|
49
|
+
*
|
|
50
|
+
* The reverse case is what it costs. Take a planar def sitting on a translucent volume, such as a
|
|
51
|
+
* label or a logo on a glass roof. It draws in the opaque pass, so it lands before the volume. The
|
|
52
|
+
* label composites against whatever was under the glass rather than onto it, and the volume then
|
|
53
|
+
* glazes over the label. So a translucent solid is right about what is behind it and wrong about what is on it. Flat
|
|
54
|
+
* content never takes this flag for the same reason, and for one of its own: 2D content is coplanar,
|
|
55
|
+
* so it has no depth order to sort by and order is all it has.
|
|
56
|
+
*
|
|
57
|
+
* `forceSinglePass` is switched off here alone. three draws a `DoubleSide` transparent material
|
|
58
|
+
* twice, back faces and then front. See `WebGLRenderer.js:1348`. That is a correctly ordered two-pass
|
|
59
|
+
* for free. It is why a closed translucent volume does not blend its own far and near surfaces in
|
|
60
|
+
* whatever order the batch packed them.
|
|
61
|
+
*
|
|
62
|
+
* Depth writes stay on even when translucent, which is not three's usual convention. Two rules depend
|
|
63
|
+
* on it. A translucent volume must still be occluded by anything nearer, and a farther volume drawn
|
|
64
|
+
* later must not paint over it. MapLibre makes the same choice for `fill-extrusion`, with
|
|
65
|
+
* `DepthMode.ReadWrite` in both of its passes. The design spec's Decision 2 records the trade and the
|
|
66
|
+
* two rejected alternatives.
|
|
67
|
+
*
|
|
68
|
+
* The branch is stable for a mounted batch, because `Shapes` renders a separate element per kind and
|
|
69
|
+
* each one carries a constant `solid`. So R3F never swaps one base class for the other under a live
|
|
70
|
+
* mesh.
|
|
71
|
+
* @param props - The material's base color, and whether it draws volumes.
|
|
72
|
+
* @returns The material element.
|
|
73
|
+
*/
|
|
74
|
+
export function ColorMaterial(props) {
|
|
75
|
+
// Read and then default, rather than a destructuring default. The React Compiler cannot lower
|
|
76
|
+
// `const { color = 'white' } = props` (BuildHIR::lowerAssignment, AssignmentPattern), and
|
|
77
|
+
// floorplan's dev build runs the compiler with `panicThreshold: 'all_errors'`, which turns that
|
|
78
|
+
// into a hard 500 for the whole app. Storybook's pass defaults to `panicThreshold: 'none'` and
|
|
79
|
+
// skips the component in silence, so only the running app shows the failure.
|
|
80
|
+
const color = props.color ?? 'white';
|
|
81
|
+
const solid = props.solid === true;
|
|
82
|
+
const overlay = useLayerOverlay();
|
|
83
|
+
// Overlay is a contract for planar content. A volume that stops depth testing loses its own self
|
|
84
|
+
// occlusion, because its far faces then paint over its near ones in pack order. Making it work
|
|
85
|
+
// needs a depth clear between the tiers, so a second render pass, which external mode does not
|
|
86
|
+
// own. So a volume ignores the flag completely and says once that it did.
|
|
87
|
+
useEffect(() => {
|
|
88
|
+
if (solid && overlay) {
|
|
89
|
+
logger.warn('overlay has no effect on a solid batch, so it is ignored.');
|
|
90
|
+
}
|
|
91
|
+
}, [solid, overlay]);
|
|
92
|
+
// Handed to both branches, because a volume dims the way flat content does. The patch runs on
|
|
93
|
+
// whatever `gl_FragColor` the material arrived at, which for a lit material is the shaded color.
|
|
94
|
+
const dimPatch = useDimPatch();
|
|
95
|
+
// The scene's own effect state, captured once. Its identity never changes, so the compiled program
|
|
96
|
+
// holds the uniform objects a frame and a slider write into, and neither one costs a recompile.
|
|
97
|
+
const effects = useSceneEffects();
|
|
98
|
+
// The caller's surgery first, then dimming. A pre-patch that declares the dim uniform's stand-in
|
|
99
|
+
// then sits in the shader before the dim patch looks for it. One stable handler, for the reason
|
|
100
|
+
// `useDimPatch` keeps its own.
|
|
101
|
+
const [onBeforeCompile] = useState(() => {
|
|
102
|
+
const pre = props.onBeforeCompile;
|
|
103
|
+
return (parameters) => {
|
|
104
|
+
pre?.(parameters);
|
|
105
|
+
// Volumes only. Flat content has no height to read, and its unit plane sits at `z = 0`, where
|
|
106
|
+
// the contact shadow takes the whole surface. Flat content also stays out of the depth prepass,
|
|
107
|
+
// so an occlusion read finds no stored depth that agrees with it.
|
|
108
|
+
//
|
|
109
|
+
// Both effects land ahead of the dim patch, so the three run in the order they describe: the
|
|
110
|
+
// shadow and the occlusion shade the surface, and dimming desaturates the result. All three
|
|
111
|
+
// insert above the same anchor, so the later install runs last.
|
|
112
|
+
if (solid) {
|
|
113
|
+
applyContactShadow(parameters, effects);
|
|
114
|
+
applyAmbientOcclusion(parameters, effects);
|
|
115
|
+
}
|
|
116
|
+
dimPatch(parameters);
|
|
117
|
+
};
|
|
118
|
+
});
|
|
119
|
+
if (solid) {
|
|
120
|
+
const translucent = props.translucent ?? false;
|
|
121
|
+
return (_jsx("meshLambertMaterial", { ...sharedParams, color: color, depthWrite: true, polygonOffset: false, transparent: translucent, forceSinglePass: !translucent, onBeforeCompile: onBeforeCompile }));
|
|
122
|
+
}
|
|
123
|
+
return (_jsx("meshBasicMaterial", { ...sharedParams, ...(overlay ? overlayParams : null), color: color, onBeforeCompile: onBeforeCompile }));
|
|
124
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { type ColorRepresentation } from 'three';
|
|
2
|
+
/** Props for {@link LineMaterial}. */
|
|
3
|
+
export interface LineMaterialProps {
|
|
4
|
+
/** Base color. Defaults to white, which is what a per-instance tint multiplies against. */
|
|
5
|
+
color?: ColorRepresentation;
|
|
6
|
+
/**
|
|
7
|
+
* Width every member without one of its own draws at, in screen-space pixels — `BatchedLinesImpl`
|
|
8
|
+
* resolves an unset per-member width to this. Defaults to 1.
|
|
9
|
+
*/
|
|
10
|
+
width?: number;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* The material line batches draw with, rendered as a child of the batch so R3F attaches, updates,
|
|
14
|
+
* and disposes it.
|
|
15
|
+
*
|
|
16
|
+
* Three's own, configured — it knows nothing about batching. What makes one of these draw members
|
|
17
|
+
* that differ in width and tint is a patch the batch installs on whatever material it is handed
|
|
18
|
+
* (`core/lines/lines-material`), so this stays a material a plain `LineSegments2` could draw with.
|
|
19
|
+
*
|
|
20
|
+
* `worldUnits` is left at three's default, so `width` is screen-space and a line keeps its
|
|
21
|
+
* thickness as the floorplan zooms. `depthFunc` and the blending come from the shared params, the
|
|
22
|
+
* same as every other 2D layer: order decides overlap, not depth.
|
|
23
|
+
*
|
|
24
|
+
* **The stencil is what keeps translucent lines from darkening where they overlap.** Alpha blending
|
|
25
|
+
* composites per fragment, so anywhere two line quads cover the same pixel — a route's shared
|
|
26
|
+
* endpoints, where two round caps land on top of each other, or two lines simply crossing — the
|
|
27
|
+
* color is blended twice and reads darker than either line. The three settings below make the first
|
|
28
|
+
* fragment to reach a pixel the only one that draws:
|
|
29
|
+
*
|
|
30
|
+
* - `stencilFunc: NotEqual` against {@link LINE_STENCIL_REF} passes only where the layer has not
|
|
31
|
+
* drawn yet.
|
|
32
|
+
* - `stencilZPass: Replace` stamps the ref as it passes, so the next fragment there fails.
|
|
33
|
+
* - `stencilWrite` turns both on.
|
|
34
|
+
*
|
|
35
|
+
* Two consequences worth knowing. Lines within a layer never blend with *each other* — only the
|
|
36
|
+
* winner shows — so a line that must blend over another belongs in its own layer. And "first
|
|
37
|
+
* fragment wins" is why a line batch is drawn back to front: the one that paints on top has to
|
|
38
|
+
* rasterize first. Between layers nothing changes, because the clear below hands each
|
|
39
|
+
* batch a fresh stencil, so ordinary blending and occlusion still apply across them.
|
|
40
|
+
* @param props - The material's base color and fallback width.
|
|
41
|
+
* @returns The material element.
|
|
42
|
+
*/
|
|
43
|
+
export declare function LineMaterial(props: LineMaterialProps): import("react").JSX.Element;
|
|
44
|
+
//# sourceMappingURL=LineMaterial.d.ts.map
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { extend } from '@react-three/fiber';
|
|
3
|
+
import { NotEqualStencilFunc, ReplaceStencilOp, } from 'three';
|
|
4
|
+
import { LineMaterial as LineMaterialBase } from 'three/addons/lines/LineMaterial.js';
|
|
5
|
+
import { useLayerOverlay } from '../layer/layer-context';
|
|
6
|
+
import { useDimPatch } from './effects/color-dim';
|
|
7
|
+
import { overlayParams, sharedParams } from './shared';
|
|
8
|
+
/**
|
|
9
|
+
* The stencil value a line layer stamps on every pixel it covers.
|
|
10
|
+
*
|
|
11
|
+
* `255` is deliberate rather than arbitrary. In the MapLibre embed the SDK renders into the map's
|
|
12
|
+
* own canvas and GL context, so this is *MapLibre's* stencil buffer: it assigns tile clipping ids
|
|
13
|
+
* counting up from zero, and the floorplan draws last, so taking the top of the range keeps the two
|
|
14
|
+
* out of each other's way.
|
|
15
|
+
*/
|
|
16
|
+
const LINE_STENCIL_REF = 255;
|
|
17
|
+
/**
|
|
18
|
+
* Registers three's line material as an R3F element, by value so the generated key cannot collide
|
|
19
|
+
* with anything three puts in the catalog when the canvas mounts.
|
|
20
|
+
*/
|
|
21
|
+
const LineMaterialElement = extend(LineMaterialBase);
|
|
22
|
+
/**
|
|
23
|
+
* The material line batches draw with, rendered as a child of the batch so R3F attaches, updates,
|
|
24
|
+
* and disposes it.
|
|
25
|
+
*
|
|
26
|
+
* Three's own, configured — it knows nothing about batching. What makes one of these draw members
|
|
27
|
+
* that differ in width and tint is a patch the batch installs on whatever material it is handed
|
|
28
|
+
* (`core/lines/lines-material`), so this stays a material a plain `LineSegments2` could draw with.
|
|
29
|
+
*
|
|
30
|
+
* `worldUnits` is left at three's default, so `width` is screen-space and a line keeps its
|
|
31
|
+
* thickness as the floorplan zooms. `depthFunc` and the blending come from the shared params, the
|
|
32
|
+
* same as every other 2D layer: order decides overlap, not depth.
|
|
33
|
+
*
|
|
34
|
+
* **The stencil is what keeps translucent lines from darkening where they overlap.** Alpha blending
|
|
35
|
+
* composites per fragment, so anywhere two line quads cover the same pixel — a route's shared
|
|
36
|
+
* endpoints, where two round caps land on top of each other, or two lines simply crossing — the
|
|
37
|
+
* color is blended twice and reads darker than either line. The three settings below make the first
|
|
38
|
+
* fragment to reach a pixel the only one that draws:
|
|
39
|
+
*
|
|
40
|
+
* - `stencilFunc: NotEqual` against {@link LINE_STENCIL_REF} passes only where the layer has not
|
|
41
|
+
* drawn yet.
|
|
42
|
+
* - `stencilZPass: Replace` stamps the ref as it passes, so the next fragment there fails.
|
|
43
|
+
* - `stencilWrite` turns both on.
|
|
44
|
+
*
|
|
45
|
+
* Two consequences worth knowing. Lines within a layer never blend with *each other* — only the
|
|
46
|
+
* winner shows — so a line that must blend over another belongs in its own layer. And "first
|
|
47
|
+
* fragment wins" is why a line batch is drawn back to front: the one that paints on top has to
|
|
48
|
+
* rasterize first. Between layers nothing changes, because the clear below hands each
|
|
49
|
+
* batch a fresh stencil, so ordinary blending and occlusion still apply across them.
|
|
50
|
+
* @param props - The material's base color and fallback width.
|
|
51
|
+
* @returns The material element.
|
|
52
|
+
*/
|
|
53
|
+
export function LineMaterial(props) {
|
|
54
|
+
// Read-then-default rather than destructuring defaults, which the React Compiler cannot lower —
|
|
55
|
+
// see `ColorMaterial` for what that costs.
|
|
56
|
+
const color = props.color ?? 'white';
|
|
57
|
+
const width = props.width ?? 1;
|
|
58
|
+
const onBeforeCompile = useDimPatch();
|
|
59
|
+
const overlay = useLayerOverlay();
|
|
60
|
+
/**
|
|
61
|
+
* Hands the batch about to draw a clean stencil to stamp.
|
|
62
|
+
*
|
|
63
|
+
* The stamps are per *layer*, not per frame: without this, a batch drawn later would be rejected
|
|
64
|
+
* everywhere an earlier one already covered, and a route's animated overlay would not appear over
|
|
65
|
+
* the route beneath it. Clearing here rather than in the batch keeps it beside the settings that
|
|
66
|
+
* make it necessary — this material is what each line layer mounts its own copy of, so "once per
|
|
67
|
+
* batch, before it draws" falls out of where the callback lives.
|
|
68
|
+
* @param renderer - The renderer about to draw the batch.
|
|
69
|
+
*/
|
|
70
|
+
function onBeforeRender(renderer) {
|
|
71
|
+
renderer.clearStencil();
|
|
72
|
+
}
|
|
73
|
+
return (_jsx(LineMaterialElement, { ...sharedParams, ...(overlay ? overlayParams : null), color: color, linewidth: width, stencilWrite: true, stencilFunc: NotEqualStencilFunc, stencilZPass: ReplaceStencilOp, stencilRef: LINE_STENCIL_REF, onBeforeRender: onBeforeRender, onBeforeCompile: onBeforeCompile }));
|
|
74
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { type Material } from 'three';
|
|
2
|
+
/** Props for {@link ModelMaterial}. */
|
|
3
|
+
export interface ModelMaterialProps {
|
|
4
|
+
/**
|
|
5
|
+
* The material the GLB authored. Every layer drawing the same model shares it, so nothing mutates
|
|
6
|
+
* it. This component clones it and adjusts the clone.
|
|
7
|
+
*/
|
|
8
|
+
source: Material;
|
|
9
|
+
/**
|
|
10
|
+
* The name of a per-instance `float` uniform to multiply alpha by, so one mesh can hold instances of
|
|
11
|
+
* differing opacity. The mesh owns the name and declares it. The material only patches its shader to
|
|
12
|
+
* read it.
|
|
13
|
+
*/
|
|
14
|
+
opacityUniform: string;
|
|
15
|
+
/**
|
|
16
|
+
* Whether any instance drawing this material is translucent.
|
|
17
|
+
*
|
|
18
|
+
* Per-instance opacity is a fragment multiplier, and whether alpha blends at all is a material
|
|
19
|
+
* decision. three drops a `NormalBlending` material with `transparent: false` to `NoBlending`, in
|
|
20
|
+
* `WebGLState.setMaterial`, which discards the alpha. The instance then composites against the page
|
|
21
|
+
* rather than against the scene. So the mesh has to tell the material that at least one of its
|
|
22
|
+
* instances needs blending.
|
|
23
|
+
*/
|
|
24
|
+
translucent?: boolean;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* The material a model draws with: the GLB's own, cloned and adjusted for the plan's depth rules, for
|
|
28
|
+
* the plan's shading, and for per-instance opacity.
|
|
29
|
+
*
|
|
30
|
+
* A model is one of the two kinds of content that write depth, translucent models included. That is
|
|
31
|
+
* what makes a model occlude itself, other models, and any 2D content drawn after it. The rest of the
|
|
32
|
+
* scene draws with `depthWrite: false` and a small polygon offset so that it can be biased against
|
|
33
|
+
* surfaces like these, so a model carries no offset of its own.
|
|
34
|
+
*
|
|
35
|
+
* A model takes both darkening terms, the way a solid shape batch does. The GLTF loader produces a
|
|
36
|
+
* `MeshStandardMaterial`, which carries the same output chain and the same placement chunk, so both
|
|
37
|
+
* patches apply unchanged. The contact shadow reads a plan-space height. That is what makes it correct
|
|
38
|
+
* for geometry authored in the GLB's own coordinates and placed by a per-instance matrix.
|
|
39
|
+
*
|
|
40
|
+
* An asset declaring `KHR_materials_unlit` takes both terms too, on purpose. The loader hands such a
|
|
41
|
+
* material over as a `MeshBasicMaterial`, which carries both anchors, so nothing here has to sniff a
|
|
42
|
+
* type to include it. Neither term is a light. They are the plan's own depth cue, applied to whatever
|
|
43
|
+
* color the material arrived at. The design asks for one shade of shadow across the plan. An unlit
|
|
44
|
+
* booth with no darkening at its base reads as floating beside a lit one. That is a worse answer to
|
|
45
|
+
* "unlit" than shading it.
|
|
46
|
+
*
|
|
47
|
+
* Everything else about the material stays as the GLB authored it: maps, roughness, metalness,
|
|
48
|
+
* emissive, side. A model's own appearance is the asset's business. How the plan is shaded is not.
|
|
49
|
+
* @param props - The source material and the per-instance opacity uniform's name.
|
|
50
|
+
* @returns The material element.
|
|
51
|
+
*/
|
|
52
|
+
export declare function ModelMaterial(props: ModelMaterialProps): import("react").JSX.Element;
|
|
53
|
+
//# sourceMappingURL=ModelMaterial.d.ts.map
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useState } from 'react';
|
|
3
|
+
import { LessEqualDepth, } from 'three';
|
|
4
|
+
import { applyAmbientOcclusion } from './effects/ao';
|
|
5
|
+
import { useDimPatch } from './effects/color-dim';
|
|
6
|
+
import { applyContactShadow } from './effects/contact-shadow';
|
|
7
|
+
import { useSceneEffects } from './effects/effects-context';
|
|
8
|
+
/**
|
|
9
|
+
* The material a model draws with: the GLB's own, cloned and adjusted for the plan's depth rules, for
|
|
10
|
+
* the plan's shading, and for per-instance opacity.
|
|
11
|
+
*
|
|
12
|
+
* A model is one of the two kinds of content that write depth, translucent models included. That is
|
|
13
|
+
* what makes a model occlude itself, other models, and any 2D content drawn after it. The rest of the
|
|
14
|
+
* scene draws with `depthWrite: false` and a small polygon offset so that it can be biased against
|
|
15
|
+
* surfaces like these, so a model carries no offset of its own.
|
|
16
|
+
*
|
|
17
|
+
* A model takes both darkening terms, the way a solid shape batch does. The GLTF loader produces a
|
|
18
|
+
* `MeshStandardMaterial`, which carries the same output chain and the same placement chunk, so both
|
|
19
|
+
* patches apply unchanged. The contact shadow reads a plan-space height. That is what makes it correct
|
|
20
|
+
* for geometry authored in the GLB's own coordinates and placed by a per-instance matrix.
|
|
21
|
+
*
|
|
22
|
+
* An asset declaring `KHR_materials_unlit` takes both terms too, on purpose. The loader hands such a
|
|
23
|
+
* material over as a `MeshBasicMaterial`, which carries both anchors, so nothing here has to sniff a
|
|
24
|
+
* type to include it. Neither term is a light. They are the plan's own depth cue, applied to whatever
|
|
25
|
+
* color the material arrived at. The design asks for one shade of shadow across the plan. An unlit
|
|
26
|
+
* booth with no darkening at its base reads as floating beside a lit one. That is a worse answer to
|
|
27
|
+
* "unlit" than shading it.
|
|
28
|
+
*
|
|
29
|
+
* Everything else about the material stays as the GLB authored it: maps, roughness, metalness,
|
|
30
|
+
* emissive, side. A model's own appearance is the asset's business. How the plan is shaded is not.
|
|
31
|
+
* @param props - The source material and the per-instance opacity uniform's name.
|
|
32
|
+
* @returns The material element.
|
|
33
|
+
*/
|
|
34
|
+
export function ModelMaterial(props) {
|
|
35
|
+
const { source, opacityUniform } = props;
|
|
36
|
+
const dimPatch = useDimPatch();
|
|
37
|
+
// The scene's own effect state, captured once. Its identity never changes, so the compiled program
|
|
38
|
+
// holds the uniform objects a frame and a slider write into, and neither one costs a recompile.
|
|
39
|
+
const effects = useSceneEffects();
|
|
40
|
+
const translucent = props.translucent ?? false;
|
|
41
|
+
// Cloned once and held in state. R3F disposes a material it created on unmount, and the source
|
|
42
|
+
// belongs to the module-level model cache, which can still be serving other layers. Every patch
|
|
43
|
+
// goes on with the clone, once. Each patch is stable and reads its current values through the
|
|
44
|
+
// uniforms it owns.
|
|
45
|
+
const [material] = useState(() => adjust(source.clone(), opacityUniform, dimPatch, effects, translucent));
|
|
46
|
+
useEffect(() => () => material.dispose(), [material]);
|
|
47
|
+
return _jsx("primitive", { object: material, attach: "material" });
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Applies the plan's depth rules, its two darkening terms and the per-instance opacity patch to a
|
|
51
|
+
* cloned GLB material.
|
|
52
|
+
* @param material - The clone to adjust.
|
|
53
|
+
* @param opacityUniform - The per-instance uniform's name.
|
|
54
|
+
* @param dimPatch - Installs dimming on the compiled shader.
|
|
55
|
+
* @param effects - The scene's shared effect state, which both darkening terms read.
|
|
56
|
+
* @param translucent - Whether any instance drawing it needs its alpha blended.
|
|
57
|
+
* @returns The same material.
|
|
58
|
+
*/
|
|
59
|
+
function adjust(material, opacityUniform, dimPatch, effects, translucent) {
|
|
60
|
+
material.depthFunc = LessEqualDepth;
|
|
61
|
+
material.polygonOffset = false;
|
|
62
|
+
// `transparent` moves a material into three's transparent list, which three draws after every
|
|
63
|
+
// opaque object in the scene whatever the layer order says. That is what lets a translucent model
|
|
64
|
+
// blend with the content behind it instead of with an empty frame.
|
|
65
|
+
//
|
|
66
|
+
// It is a material property and never a per-instance one, so which list a def belongs in is a
|
|
67
|
+
// partition rather than a flag. `Models` keeps a layer's translucent copies in a batch of their own,
|
|
68
|
+
// the way `Shapes` splits its volumes. It re-splits when a def's opacity crosses 1. `ColorMaterial`
|
|
69
|
+
// makes the same trade and records what it costs: a planar def sitting on a translucent one draws
|
|
70
|
+
// before it.
|
|
71
|
+
material.transparent =
|
|
72
|
+
material.transparent || translucent || material.opacity < 1;
|
|
73
|
+
// Depth writes stay on even when translucent, which is not three's usual convention and is the
|
|
74
|
+
// choice this renderer makes. Two of the plan's rules depend on it. A translucent model must still
|
|
75
|
+
// be occluded by anything nearer the camera. A planar def drawn after it, a label or a logo on a
|
|
76
|
+
// booth roof, must be occluded by it. Both are depth-buffer questions, and switching
|
|
77
|
+
// writes off answers them wrongly: a farther solid drawn later has nothing to reject it, and it
|
|
78
|
+
// paints straight over the translucent one in front of it.
|
|
79
|
+
//
|
|
80
|
+
// What it costs is a view through a translucent surface to content drawn after it, and no
|
|
81
|
+
// painter's-order renderer can offer that. Blending needs depth order, and array order is authoring
|
|
82
|
+
// order. MapLibre makes the same trade for `fill-extrusion`, with `DepthMode.ReadWrite` in both of
|
|
83
|
+
// its passes, and degrades to a color error rather than a structural one. The obligation it puts on
|
|
84
|
+
// callers is to order a translucent model's layer after whatever has to show through it. The design
|
|
85
|
+
// spec's Decision 2 spells that out, along with the two alternatives and why each one loses more
|
|
86
|
+
// than it gains.
|
|
87
|
+
material.depthWrite = true;
|
|
88
|
+
const chained = material.onBeforeCompile.bind(material);
|
|
89
|
+
material.onBeforeCompile = (parameters, ...rest) => {
|
|
90
|
+
chained(parameters, ...rest);
|
|
91
|
+
// Applied at the end of the fragment chain, after `<dithering_fragment>`, so it multiplies the
|
|
92
|
+
// final composed alpha rather than an intermediate one. The instanced mesh injects the uniform
|
|
93
|
+
// fetch at the top of `main()`, so the name is in scope by the time this runs.
|
|
94
|
+
parameters.fragmentShader = parameters.fragmentShader.replace('#include <dithering_fragment>',
|
|
95
|
+
/*glsl*/ `
|
|
96
|
+
#include <dithering_fragment>
|
|
97
|
+
gl_FragColor.a *= ${opacityUniform};
|
|
98
|
+
`.trim());
|
|
99
|
+
// The plan's shading, ahead of the dim patch. All three insert above the same anchor, so the later
|
|
100
|
+
// install runs last, and the order they run in is the order they describe: the shadow and the
|
|
101
|
+
// occlusion shade the surface, and dimming desaturates the result.
|
|
102
|
+
applyContactShadow(parameters, effects);
|
|
103
|
+
applyAmbientOcclusion(parameters, effects);
|
|
104
|
+
// Dim touches rgb and opacity touches alpha, so the two patches stay independent in any order. Dim
|
|
105
|
+
// lands ahead of the colorspace conversion either way, which is where a gamma-corrected darken has
|
|
106
|
+
// to be.
|
|
107
|
+
dimPatch(parameters);
|
|
108
|
+
};
|
|
109
|
+
material.needsUpdate = true;
|
|
110
|
+
return material;
|
|
111
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { type Texture } from 'three';
|
|
2
|
+
/** Props for {@link TextureMaterial}. */
|
|
3
|
+
export interface TextureMaterialProps {
|
|
4
|
+
/**
|
|
5
|
+
* Texture map. Atlas textures are uploaded with `flipY = false`, so v runs down the canvas and
|
|
6
|
+
* the region coordinates need no vertical inversion anywhere.
|
|
7
|
+
*/
|
|
8
|
+
map: Texture;
|
|
9
|
+
/**
|
|
10
|
+
* The name of a per-instance `vec4` uniform to source the texture region from, for an atlas on an
|
|
11
|
+
* instanced/batched mesh that declares and writes it. The mesh owns the name; the material only
|
|
12
|
+
* patches its shader to sample `map` through it. Omit it to draw the whole `map` unpatched, in any
|
|
13
|
+
* context.
|
|
14
|
+
*/
|
|
15
|
+
uvOffset?: string;
|
|
16
|
+
/**
|
|
17
|
+
* The name of a per-instance `float` uniform holding the instance's opacity, for a mesh that
|
|
18
|
+
* declares and writes one. Omit it to leave the fragment shader unpatched.
|
|
19
|
+
*/
|
|
20
|
+
opacityUniform?: string;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* The material used to render a texture, optionally through a per-instance uv-offset uniform so an
|
|
24
|
+
* instanced/batched mesh can pack many regions into one atlas.
|
|
25
|
+
*
|
|
26
|
+
* When {@link TextureMaterialProps.uvOffset} is set, the patch overwrites `vMapUv` after
|
|
27
|
+
* `<uv_vertex>`, replacing the stock chunk's value. Instanced meshes wrap this `onBeforeCompile`
|
|
28
|
+
* and inject their uniform fetch at the top of `main()`, so the uniform is in scope here.
|
|
29
|
+
* @param props - The material's texture and optional uv-offset uniform name.
|
|
30
|
+
* @returns The material element.
|
|
31
|
+
*/
|
|
32
|
+
export declare function TextureMaterial(props: TextureMaterialProps): import("react").JSX.Element;
|
|
33
|
+
//# sourceMappingURL=TextureMaterial.d.ts.map
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { OneFactor } from 'three';
|
|
3
|
+
import { useLayerOverlay } from '../layer/layer-context';
|
|
4
|
+
import { useDimPatch } from './effects/color-dim';
|
|
5
|
+
import { overlayParams, sharedParams } from './shared';
|
|
6
|
+
/**
|
|
7
|
+
* The material used to render a texture, optionally through a per-instance uv-offset uniform so an
|
|
8
|
+
* instanced/batched mesh can pack many regions into one atlas.
|
|
9
|
+
*
|
|
10
|
+
* When {@link TextureMaterialProps.uvOffset} is set, the patch overwrites `vMapUv` after
|
|
11
|
+
* `<uv_vertex>`, replacing the stock chunk's value. Instanced meshes wrap this `onBeforeCompile`
|
|
12
|
+
* and inject their uniform fetch at the top of `main()`, so the uniform is in scope here.
|
|
13
|
+
* @param props - The material's texture and optional uv-offset uniform name.
|
|
14
|
+
* @returns The material element.
|
|
15
|
+
*/
|
|
16
|
+
export function TextureMaterial(props) {
|
|
17
|
+
const { map, uvOffset, opacityUniform } = props;
|
|
18
|
+
const applyDim = useDimPatch();
|
|
19
|
+
const overlay = useLayerOverlay();
|
|
20
|
+
function onBeforeCompile(parameters) {
|
|
21
|
+
applyDim(parameters);
|
|
22
|
+
if (uvOffset) {
|
|
23
|
+
parameters.vertexShader = parameters.vertexShader.replace('#include <uv_vertex>',
|
|
24
|
+
/*glsl*/ `
|
|
25
|
+
#include <uv_vertex>
|
|
26
|
+
vMapUv = uv * ${uvOffset}.zw + ${uvOffset}.xy;
|
|
27
|
+
`.trim());
|
|
28
|
+
}
|
|
29
|
+
if (opacityUniform) {
|
|
30
|
+
// All four channels: the blend is premultiplied, so rgb already carries the texel's alpha and
|
|
31
|
+
// must carry the opacity too. Scaling `a` alone leaves a bright halo instead of a faded pin.
|
|
32
|
+
//
|
|
33
|
+
// After `colorspace_fragment`, so rgb is already sRGB-encoded and blends with the same weight
|
|
34
|
+
// as alpha. Before the encode, `0.5` would read about 73% opaque. Dim patches an earlier chunk
|
|
35
|
+
// (`tonemapping_fragment`), so the two never share an anchor.
|
|
36
|
+
parameters.fragmentShader = parameters.fragmentShader.replace('#include <colorspace_fragment>',
|
|
37
|
+
/*glsl*/ `
|
|
38
|
+
#include <colorspace_fragment>
|
|
39
|
+
gl_FragColor *= ${opacityUniform};
|
|
40
|
+
`.trim());
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
// An atlas texture is premultiplied (see `makeAtlasTexture`), so this needs `blendSrc: OneFactor`
|
|
44
|
+
// rather than `sharedParams`'s straight-alpha source term; `blendDst` is unaffected. The override
|
|
45
|
+
// lives here because `ColorMaterial` and `LineMaterial` share `sharedParams` and are never
|
|
46
|
+
// premultiplied.
|
|
47
|
+
return (_jsx("meshBasicMaterial", { ...sharedParams, ...(overlay ? overlayParams : null), blendSrc: OneFactor, map: map, onBeforeCompile: onBeforeCompile }));
|
|
48
|
+
}
|