@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,85 @@
|
|
|
1
|
+
import { Vector3, Vector4 } from 'three';
|
|
2
|
+
import { FrustumCulling } from '../instanced/FrustumCulling';
|
|
3
|
+
/**
|
|
4
|
+
* Culling for a line batch: each member tested by where it is *drawn*, not where it runs.
|
|
5
|
+
*
|
|
6
|
+
* A member's bounds are its two endpoints, which is the segment. What covers pixels is the ribbon the
|
|
7
|
+
* shader expands around it — after projection, by a width counted in screen pixels — so a member with
|
|
8
|
+
* both ends off screen still shows if that ribbon crosses the edge, and culling on the ends alone
|
|
9
|
+
* pops it there.
|
|
10
|
+
*
|
|
11
|
+
* The margin is added here rather than to the bounds themselves because it is not a property of the
|
|
12
|
+
* member: it depends on the camera, and on how far *this* member is from it. Which is also why it is
|
|
13
|
+
* per member rather than one figure for the batch — a member already knows its own width, and a batch
|
|
14
|
+
* of hairlines beside one wide route would otherwise be padded by the widest of them.
|
|
15
|
+
*/
|
|
16
|
+
export class FrustumCullingLines extends FrustumCulling {
|
|
17
|
+
instanceSphere(host, id, camera, target) {
|
|
18
|
+
const sphere = super.instanceSphere(host, id, camera, target);
|
|
19
|
+
sphere.radius += this.drawnMargin(host, id, camera, sphere);
|
|
20
|
+
return sphere;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* How far past its endpoints one member draws, in the units its bounds are measured in.
|
|
24
|
+
*
|
|
25
|
+
* Measured at the far side of the member, since a screen-space width covers more of the plan the
|
|
26
|
+
* further away it is — the near side would under-pad the end that is further off.
|
|
27
|
+
* @param host - The batch being culled.
|
|
28
|
+
* @param id - The member.
|
|
29
|
+
* @param camera - The camera the width is measured from.
|
|
30
|
+
* @param sphere - What encloses it, in the batch's own space.
|
|
31
|
+
* @returns The margin.
|
|
32
|
+
*/
|
|
33
|
+
drawnMargin(host, id, camera, sphere) {
|
|
34
|
+
const material = host.material;
|
|
35
|
+
const held = host.getWidthAt(id);
|
|
36
|
+
// A member that set no width of its own draws at the material's.
|
|
37
|
+
const width = held < 0 ? material.linewidth : held;
|
|
38
|
+
if (width <= 0)
|
|
39
|
+
return 0;
|
|
40
|
+
if (material.worldUnits)
|
|
41
|
+
return width / 2;
|
|
42
|
+
// Nothing to measure a screen-space width against until the batch has been sized to a viewport,
|
|
43
|
+
// which it does before this pass runs.
|
|
44
|
+
if (material.resolution.x === 0)
|
|
45
|
+
return 0;
|
|
46
|
+
// The distance is world-space and the radius it pads is the batch's own, which agree because a
|
|
47
|
+
// line batch is placed by translation — a layer moves one, nothing scales it.
|
|
48
|
+
_worldCenter.copy(sphere.center).applyMatrix4(host.matrixWorld);
|
|
49
|
+
const near = camera.near ?? 0;
|
|
50
|
+
const distance = _cameraPosition
|
|
51
|
+
.setFromMatrixPosition(camera.matrixWorld)
|
|
52
|
+
.distanceTo(_worldCenter);
|
|
53
|
+
return worldSpaceHalfWidth(camera, Math.max(near, distance + sphere.radius), width, material);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Half a screen-space width, in plan units, at a distance from the camera.
|
|
58
|
+
*
|
|
59
|
+
* Vendored from three's `LineSegments2`, where `getWorldSpaceHalfWidth` is module-private and takes
|
|
60
|
+
* its width off a module global. Worth vendoring rather than approximating: it is the same formula
|
|
61
|
+
* three expands a line's *pick* bounds by, so what a batch culls and what it can be picked by are
|
|
62
|
+
* decided the same way.
|
|
63
|
+
* @param camera - The camera in use.
|
|
64
|
+
* @param distance - How far from it the width is measured.
|
|
65
|
+
* @param width - The full width, in the pixels the material's resolution counts.
|
|
66
|
+
* @param material - The material, for the resolution those pixels are counted against.
|
|
67
|
+
* @returns Half the width, in plan units.
|
|
68
|
+
*/
|
|
69
|
+
function worldSpaceHalfWidth(camera, distance, width, material) {
|
|
70
|
+
// Transform into clip space, adjust the x and y values by the pixel width offset, then transform
|
|
71
|
+
// back into world space to get world offset. Note clip space is [-1, 1] so full width does not
|
|
72
|
+
// need to be halved.
|
|
73
|
+
const resolution = material.resolution;
|
|
74
|
+
_clipToWorld.set(0, 0, -distance, 1).applyMatrix4(camera.projectionMatrix);
|
|
75
|
+
_clipToWorld.multiplyScalar(1 / _clipToWorld.w);
|
|
76
|
+
_clipToWorld.x = width / resolution.width;
|
|
77
|
+
_clipToWorld.y = width / resolution.height;
|
|
78
|
+
_clipToWorld.applyMatrix4(camera.projectionMatrixInverse);
|
|
79
|
+
_clipToWorld.multiplyScalar(1 / _clipToWorld.w);
|
|
80
|
+
return Math.abs(Math.max(_clipToWorld.x, _clipToWorld.y));
|
|
81
|
+
}
|
|
82
|
+
// Scratch, each filled and consumed within one synchronous call.
|
|
83
|
+
const _worldCenter = new Vector3();
|
|
84
|
+
const _cameraPosition = new Vector3();
|
|
85
|
+
const _clipToWorld = new Vector4();
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { type Object3D, Vector3 } from 'three';
|
|
2
|
+
import { LineMaterial } from 'three/addons/lines/LineMaterial.js';
|
|
3
|
+
import { type RaycastHost, Raycasting } from '../instanced/Raycasting';
|
|
4
|
+
/** What {@link RaycastingLines} reads off the batch it tests, beyond what every instanced mesh offers. */
|
|
5
|
+
interface RaycastHostLines extends RaycastHost {
|
|
6
|
+
/** The material, whose width a member with none of its own draws at. */
|
|
7
|
+
material: LineMaterial;
|
|
8
|
+
/**
|
|
9
|
+
* Reads a member's endpoints.
|
|
10
|
+
* @param id - The member.
|
|
11
|
+
* @param start - Receives the segment start.
|
|
12
|
+
* @param end - Receives the segment end.
|
|
13
|
+
*/
|
|
14
|
+
getPointsAt(id: number, start: Vector3, end: Vector3): void;
|
|
15
|
+
/**
|
|
16
|
+
* A member's own width, or a negative value while it has none.
|
|
17
|
+
* @param id - The member.
|
|
18
|
+
* @returns The width.
|
|
19
|
+
*/
|
|
20
|
+
getWidthAt(id: number): number;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Picking for a line batch: each member tested in screen space, at the width it actually draws at.
|
|
24
|
+
*
|
|
25
|
+
* A line is picked by where it is *drawn*, not merely where it runs — a 10px route has to answer
|
|
26
|
+
* anywhere on its ribbon, not only along its centerline — and that ribbon is expanded after
|
|
27
|
+
* projection, so the test needs the camera and the viewport. Three has the math, in
|
|
28
|
+
* `LineSegments2.raycast`, and this is how a batch reaches it: one scratch line, holding one
|
|
29
|
+
* segment, stood up per member.
|
|
30
|
+
*
|
|
31
|
+
* Only {@link prepareInstance} differs from the instanced sweep this inherits. The whole-batch
|
|
32
|
+
* early-out, the frustum-culled candidates, the visibility test and naming the member each hit
|
|
33
|
+
* belongs to are the same questions for a segment as for a quad — and answering them in world space
|
|
34
|
+
* is what lets this inherit them rather than restate them, since three's screen-space math projects
|
|
35
|
+
* the ray through the camera and needs it there.
|
|
36
|
+
*
|
|
37
|
+
* The width is the one thing three has nowhere to read per segment: it is material state. So the
|
|
38
|
+
* scratch carries a material of its own, written per test, and nothing touches the batch's — which
|
|
39
|
+
* is in use by the draw the pointer is moving over.
|
|
40
|
+
*/
|
|
41
|
+
export declare class RaycastingLines extends Raycasting {
|
|
42
|
+
protected prepareInstance(host: RaycastHostLines, objectIndex: number): Object3D;
|
|
43
|
+
}
|
|
44
|
+
export {};
|
|
45
|
+
//# sourceMappingURL=RaycastingLines.d.ts.map
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { Box3, Sphere, Vector3 } from 'three';
|
|
2
|
+
import { LineMaterial } from 'three/addons/lines/LineMaterial.js';
|
|
3
|
+
import { LineSegments2 } from 'three/addons/lines/LineSegments2.js';
|
|
4
|
+
import { LineSegmentsGeometry } from 'three/addons/lines/LineSegmentsGeometry.js';
|
|
5
|
+
import { Raycasting } from '../instanced/Raycasting';
|
|
6
|
+
/**
|
|
7
|
+
* Picking for a line batch: each member tested in screen space, at the width it actually draws at.
|
|
8
|
+
*
|
|
9
|
+
* A line is picked by where it is *drawn*, not merely where it runs — a 10px route has to answer
|
|
10
|
+
* anywhere on its ribbon, not only along its centerline — and that ribbon is expanded after
|
|
11
|
+
* projection, so the test needs the camera and the viewport. Three has the math, in
|
|
12
|
+
* `LineSegments2.raycast`, and this is how a batch reaches it: one scratch line, holding one
|
|
13
|
+
* segment, stood up per member.
|
|
14
|
+
*
|
|
15
|
+
* Only {@link prepareInstance} differs from the instanced sweep this inherits. The whole-batch
|
|
16
|
+
* early-out, the frustum-culled candidates, the visibility test and naming the member each hit
|
|
17
|
+
* belongs to are the same questions for a segment as for a quad — and answering them in world space
|
|
18
|
+
* is what lets this inherit them rather than restate them, since three's screen-space math projects
|
|
19
|
+
* the ray through the camera and needs it there.
|
|
20
|
+
*
|
|
21
|
+
* The width is the one thing three has nowhere to read per segment: it is material state. So the
|
|
22
|
+
* scratch carries a material of its own, written per test, and nothing touches the batch's — which
|
|
23
|
+
* is in use by the draw the pointer is moving over.
|
|
24
|
+
*/
|
|
25
|
+
export class RaycastingLines extends Raycasting {
|
|
26
|
+
prepareInstance(host, objectIndex) {
|
|
27
|
+
host.getPointsAt(objectIndex, _start, _end);
|
|
28
|
+
// Written straight into the interleaved buffer the geometry already owns. `setPositions` would
|
|
29
|
+
// allocate that buffer and both attribute views over it again, per member per pick.
|
|
30
|
+
_segment.set([_start.x, _start.y, _start.z, _end.x, _end.y, _end.z]);
|
|
31
|
+
encloseSegment();
|
|
32
|
+
const held = host.getWidthAt(objectIndex);
|
|
33
|
+
_material.linewidth = held < 0 ? host.material.linewidth : held;
|
|
34
|
+
_material.worldUnits = host.material.worldUnits;
|
|
35
|
+
_material.resolution.copy(host.material.resolution);
|
|
36
|
+
// The batch's own placement. A per-instance transform, if a line ever carries one, composes in
|
|
37
|
+
// here — which is why this hands back a placed object rather than a geometry.
|
|
38
|
+
_line.matrixWorld.copy(host.matrixWorld);
|
|
39
|
+
return _line;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Fills the scratch geometry's bounds from the segment just written into it.
|
|
44
|
+
*
|
|
45
|
+
* Written rather than invalidated: three computes them when it finds them null, and both its
|
|
46
|
+
* `LineSegmentsGeometry` implementations allocate as they do — where a segment's box and sphere are
|
|
47
|
+
* two lines of arithmetic over the endpoints already in hand.
|
|
48
|
+
*/
|
|
49
|
+
function encloseSegment() {
|
|
50
|
+
const geometry = _line.geometry;
|
|
51
|
+
const box = (geometry.boundingBox ??= new Box3()).makeEmpty();
|
|
52
|
+
box.expandByPoint(_start).expandByPoint(_end);
|
|
53
|
+
const sphere = (geometry.boundingSphere ??= new Sphere());
|
|
54
|
+
box.getCenter(sphere.center);
|
|
55
|
+
sphere.radius = _start.distanceTo(_end) / 2;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* A geometry holding exactly one segment, whose endpoints are rewritten per test.
|
|
59
|
+
* @returns The geometry.
|
|
60
|
+
*/
|
|
61
|
+
function makeSegmentGeometry() {
|
|
62
|
+
const geometry = new LineSegmentsGeometry();
|
|
63
|
+
// The only way to get the interleaved buffer and its two attribute views built; what it is filled
|
|
64
|
+
// with here is irrelevant, since every test overwrites it.
|
|
65
|
+
geometry.setPositions(new Float32Array(FLOATS_PER_SEGMENT));
|
|
66
|
+
return geometry;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* The buffer a segment geometry holds its endpoints in.
|
|
70
|
+
* @param geometry - The geometry to read.
|
|
71
|
+
* @returns The endpoints, interleaved as `xyz xyz`.
|
|
72
|
+
*/
|
|
73
|
+
function segmentArrayOf(geometry) {
|
|
74
|
+
const start = geometry.getAttribute('instanceStart');
|
|
75
|
+
return start.data.array;
|
|
76
|
+
}
|
|
77
|
+
/** Floats one segment occupies: `x, y, z` for each end. */
|
|
78
|
+
const FLOATS_PER_SEGMENT = 6;
|
|
79
|
+
/**
|
|
80
|
+
* The line every member is tested through.
|
|
81
|
+
*
|
|
82
|
+
* One object rather than one per member, for the reason the instanced sweep keeps a scratch mesh:
|
|
83
|
+
* what differs between members is the data, and three's raycast reads all of it off the object it is
|
|
84
|
+
* called on. Never rendered, so neither its geometry nor its material holds anything to free — and
|
|
85
|
+
* its material is its own, so a test writes no state the batch is drawing with.
|
|
86
|
+
*/
|
|
87
|
+
const _line = new LineSegments2(makeSegmentGeometry(), new LineMaterial());
|
|
88
|
+
const _material = _line.material;
|
|
89
|
+
const _segment = segmentArrayOf(_line.geometry);
|
|
90
|
+
const _start = new Vector3();
|
|
91
|
+
const _end = new Vector3();
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { type WebGLProgramParametersWithUniforms } from 'three';
|
|
2
|
+
import { type LineMaterial } from 'three/addons/lines/LineMaterial.js';
|
|
3
|
+
/**
|
|
4
|
+
* The material side of a line batch: what three's line shader has to become before it can draw
|
|
5
|
+
* members that differ in width and tint.
|
|
6
|
+
*
|
|
7
|
+
* Free functions holding no state, as `instanced-material.ts` is — {@link patchLineMaterial} hands
|
|
8
|
+
* back its own undo rather than parking it on the caller.
|
|
9
|
+
*
|
|
10
|
+
* It lives here rather than in the material component for the reason the instancing patch does: the
|
|
11
|
+
* material is three's, and a `<LineMaterial>` has no business knowing whether it is drawing a batch
|
|
12
|
+
* or a plain `LineSegments2`. What it must keep working with is either.
|
|
13
|
+
*/
|
|
14
|
+
/** The per-instance segment start, named for the attribute three's line shader reads. */
|
|
15
|
+
export declare const START_UNIFORM = "instanceStart";
|
|
16
|
+
/** The per-instance segment end, named for the attribute three's line shader reads. */
|
|
17
|
+
export declare const END_UNIFORM = "instanceEnd";
|
|
18
|
+
/** The per-instance width. Distinct from three's `linewidth`, which it falls back to. */
|
|
19
|
+
export declare const WIDTH_UNIFORM = "lineWidth";
|
|
20
|
+
/**
|
|
21
|
+
* Wraps a material so the programs it compiles read a batch's per-instance endpoints, width and tint.
|
|
22
|
+
*
|
|
23
|
+
* Installed *inside* the wrap an instanced draw puts on, so that by the time this runs the prologue
|
|
24
|
+
* it reads is already in the shader — which is why the batch installs this one first.
|
|
25
|
+
* @param material - The material the batch is about to draw with.
|
|
26
|
+
* @returns Restores the material exactly as it arrived. Call it once, after the draw.
|
|
27
|
+
*/
|
|
28
|
+
export declare function patchLineMaterial(material: LineMaterial): () => void;
|
|
29
|
+
/**
|
|
30
|
+
* Points three's line shader at a batch's per-instance endpoints, width and tint.
|
|
31
|
+
*
|
|
32
|
+
* Deliberately small, and small in a particular way: the endpoints are *not* rewritten anywhere. The
|
|
33
|
+
* batch declares them under the names three's own shader already reads — `instanceStart` and
|
|
34
|
+
* `instanceEnd` — so the uniform prologue lands two locals of `main` that shadow the attributes, and
|
|
35
|
+
* every line of three's projection, near-plane trimming and cap math goes on reading what it always
|
|
36
|
+
* read. What is left is to drop the attribute declarations that no longer feed anything, resolve the
|
|
37
|
+
* width the shader expands by, and carry the instance color's alpha into an output that drops it.
|
|
38
|
+
*
|
|
39
|
+
* Does nothing until a batch's prologue is in the shader — the guard `applyDim` uses, and for the
|
|
40
|
+
* same reason: a material may be compiled for something that is not a batch at all, and the GLSL
|
|
41
|
+
* below reads names only the prologue brings into scope.
|
|
42
|
+
* @param shader - The program parameters to edit.
|
|
43
|
+
* @returns Whether the shader was patched.
|
|
44
|
+
* @throws {Error} If the prologue is present but the shader around it has moved what this anchors on.
|
|
45
|
+
*/
|
|
46
|
+
export declare function patchLineShader(shader: WebGLProgramParametersWithUniforms): boolean;
|
|
47
|
+
//# sourceMappingURL=lines-material.d.ts.map
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { openMain, replaceAnchor } from '../shader-source';
|
|
2
|
+
/**
|
|
3
|
+
* The material side of a line batch: what three's line shader has to become before it can draw
|
|
4
|
+
* members that differ in width and tint.
|
|
5
|
+
*
|
|
6
|
+
* Free functions holding no state, as `instanced-material.ts` is — {@link patchLineMaterial} hands
|
|
7
|
+
* back its own undo rather than parking it on the caller.
|
|
8
|
+
*
|
|
9
|
+
* It lives here rather than in the material component for the reason the instancing patch does: the
|
|
10
|
+
* material is three's, and a `<LineMaterial>` has no business knowing whether it is drawing a batch
|
|
11
|
+
* or a plain `LineSegments2`. What it must keep working with is either.
|
|
12
|
+
*/
|
|
13
|
+
/** The per-instance segment start, named for the attribute three's line shader reads. */
|
|
14
|
+
export const START_UNIFORM = 'instanceStart';
|
|
15
|
+
/** The per-instance segment end, named for the attribute three's line shader reads. */
|
|
16
|
+
export const END_UNIFORM = 'instanceEnd';
|
|
17
|
+
/** The per-instance width. Distinct from three's `linewidth`, which it falls back to. */
|
|
18
|
+
export const WIDTH_UNIFORM = 'lineWidth';
|
|
19
|
+
/**
|
|
20
|
+
* Wraps a material so the programs it compiles read a batch's per-instance endpoints, width and tint.
|
|
21
|
+
*
|
|
22
|
+
* Installed *inside* the wrap an instanced draw puts on, so that by the time this runs the prologue
|
|
23
|
+
* it reads is already in the shader — which is why the batch installs this one first.
|
|
24
|
+
* @param material - The material the batch is about to draw with.
|
|
25
|
+
* @returns Restores the material exactly as it arrived. Call it once, after the draw.
|
|
26
|
+
*/
|
|
27
|
+
export function patchLineMaterial(material) {
|
|
28
|
+
seedColorChunk(material);
|
|
29
|
+
// Saved raw rather than bound, for the reason `patchInstancedMaterial` spells out: restoring a
|
|
30
|
+
// bound copy would put a *different* function back, and the next draw would bind that one in turn.
|
|
31
|
+
/* eslint-disable @typescript-eslint/unbound-method -- restored verbatim; called with `.call`. */
|
|
32
|
+
const baseOnBeforeCompile = material.onBeforeCompile;
|
|
33
|
+
const baseCacheKey = material.customProgramCacheKey;
|
|
34
|
+
/* eslint-enable @typescript-eslint/unbound-method */
|
|
35
|
+
// three caches a compiled program under this key alone. A patched line material compiles source a
|
|
36
|
+
// plain one does not, so it needs a key of its own. A constant suffix is enough, because what this
|
|
37
|
+
// patch injects is fixed. `seedColorChunk` happens to change the source three derives its own
|
|
38
|
+
// shader id from, which has masked the absence of this until now. That is incidental, and a
|
|
39
|
+
// material that stopped needing the seed would lose the protection with it.
|
|
40
|
+
material.customProgramCacheKey = () => `efpLine_${baseCacheKey.call(material)}`;
|
|
41
|
+
material.onBeforeCompile = (shader, renderer) => {
|
|
42
|
+
patchLineShader(shader);
|
|
43
|
+
baseOnBeforeCompile.call(material, shader, renderer);
|
|
44
|
+
};
|
|
45
|
+
return () => {
|
|
46
|
+
material.onBeforeCompile = baseOnBeforeCompile;
|
|
47
|
+
material.customProgramCacheKey = baseCacheKey;
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Puts the chunk three seeds `vColor` in into the material's own source, once.
|
|
52
|
+
*
|
|
53
|
+
* That chunk is the only place an instanced draw has to fold a per-instance color in, and the line
|
|
54
|
+
* shader is the one stock material that does not include it. It goes into the *source* rather than
|
|
55
|
+
* into a compiling shader because the instancing injection looks for it before the patch below runs,
|
|
56
|
+
* and finding it is what makes it bind the colors texture at all.
|
|
57
|
+
*
|
|
58
|
+
* Left in place afterwards rather than undone with the wrap: it expands to nothing without the color
|
|
59
|
+
* defines, which only an instanced draw sets — so the material still draws a plain `LineSegments2` —
|
|
60
|
+
* and putting it back would change the source three keys its program cache by, on every draw.
|
|
61
|
+
* @param material - The material to seed.
|
|
62
|
+
*/
|
|
63
|
+
function seedColorChunk(material) {
|
|
64
|
+
if (material.vertexShader.includes(COLOR_VERTEX_CHUNK))
|
|
65
|
+
return;
|
|
66
|
+
material.vertexShader = openMain(material.vertexShader, COLOR_VERTEX_CHUNK);
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Points three's line shader at a batch's per-instance endpoints, width and tint.
|
|
70
|
+
*
|
|
71
|
+
* Deliberately small, and small in a particular way: the endpoints are *not* rewritten anywhere. The
|
|
72
|
+
* batch declares them under the names three's own shader already reads — `instanceStart` and
|
|
73
|
+
* `instanceEnd` — so the uniform prologue lands two locals of `main` that shadow the attributes, and
|
|
74
|
+
* every line of three's projection, near-plane trimming and cap math goes on reading what it always
|
|
75
|
+
* read. What is left is to drop the attribute declarations that no longer feed anything, resolve the
|
|
76
|
+
* width the shader expands by, and carry the instance color's alpha into an output that drops it.
|
|
77
|
+
*
|
|
78
|
+
* Does nothing until a batch's prologue is in the shader — the guard `applyDim` uses, and for the
|
|
79
|
+
* same reason: a material may be compiled for something that is not a batch at all, and the GLSL
|
|
80
|
+
* below reads names only the prologue brings into scope.
|
|
81
|
+
* @param shader - The program parameters to edit.
|
|
82
|
+
* @returns Whether the shader was patched.
|
|
83
|
+
* @throws {Error} If the prologue is present but the shader around it has moved what this anchors on.
|
|
84
|
+
*/
|
|
85
|
+
export function patchLineShader(shader) {
|
|
86
|
+
const vertex = shader.vertexShader;
|
|
87
|
+
if (!vertex.includes(WIDTH_UNIFORM))
|
|
88
|
+
return false;
|
|
89
|
+
// Two statements, in this order for a reason: the first reads the material uniform while it is
|
|
90
|
+
// still the only `linewidth` in scope, and the second declares the local that takes over from
|
|
91
|
+
// there on. Nothing downstream is rewritten, which is the point — three's own `offset *= linewidth`
|
|
92
|
+
// (screen space) and `float hw = linewidth * 0.5` (world units) both keep working, so flipping
|
|
93
|
+
// `material.worldUnits` needs no change here. The fallback triggers on a *negative* width, not on
|
|
94
|
+
// zero, so a member given zero expands to no area and draws nothing, which is what asking for a
|
|
95
|
+
// zero-width line means.
|
|
96
|
+
const resolveWidth = /* glsl */ `
|
|
97
|
+
float materialLineWidth = linewidth;
|
|
98
|
+
float linewidth = ${WIDTH_UNIFORM} < 0.0 ? materialLineWidth : ${WIDTH_UNIFORM};
|
|
99
|
+
`;
|
|
100
|
+
// The attributes stand unused once the prologue shadows them, so a compiler would drop them anyway
|
|
101
|
+
// — but an attribute that looks like the data source and is not would be read as one. Dropped with
|
|
102
|
+
// a plain replace, because a three that stopped declaring them is not a three this breaks on.
|
|
103
|
+
shader.vertexShader = replaceAnchor(vertex.replace(ATTRIBUTE_DECLARATIONS, ''), WIDTH_ANCHOR, `${resolveWidth}\n${WIDTH_ANCHOR}`);
|
|
104
|
+
// The instance color reaches `diffuseColor` through three's own `<color_fragment>`, which the line
|
|
105
|
+
// shader does include — but only its rgb survives, because the output discards the diffuse alpha
|
|
106
|
+
// and carries a coverage of its own. Folding the tint's alpha into that is all that is left.
|
|
107
|
+
shader.fragmentShader = replaceAnchor(shader.fragmentShader, FRAGMENT_OUTPUT,
|
|
108
|
+
/* glsl */ `gl_FragColor = vec4( diffuseColor.rgb, alpha * vColor.a );`);
|
|
109
|
+
return true;
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* The attribute declarations the per-instance endpoints replace. Matched loosely, because their
|
|
113
|
+
* indentation is three's and not ours to depend on.
|
|
114
|
+
*/
|
|
115
|
+
const ATTRIBUTE_DECLARATIONS = new RegExp(`attribute\\s+vec3\\s+${START_UNIFORM}\\s*;\\s*attribute\\s+vec3\\s+${END_UNIFORM}\\s*;`);
|
|
116
|
+
/**
|
|
117
|
+
* Where the width resolve goes: ahead of the first statement of three's own `main`, which puts it
|
|
118
|
+
* after the prologue that declares the per-instance width and before everything that expands by it.
|
|
119
|
+
*/
|
|
120
|
+
const WIDTH_ANCHOR = /*glsl*/ `float aspect = resolution.x / resolution.y;`;
|
|
121
|
+
/** The line shader's output, which the instance color's alpha is folded into. */
|
|
122
|
+
const FRAGMENT_OUTPUT = /*glsl*/ `gl_FragColor = vec4( diffuseColor.rgb, alpha );`;
|
|
123
|
+
/** Three's per-vertex color chunk, which an instanced draw substitutes its own for. */
|
|
124
|
+
const COLOR_VERTEX_CHUNK = /*glsl*/ `#include <color_vertex>`;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Editing a shader's source around the one construct it cannot be missing: the opening of `main`.
|
|
3
|
+
*
|
|
4
|
+
* Every injection here used to key on a chunk `#include` instead — the instancing declarations rode
|
|
5
|
+
* on `batching_pars_vertex`, which three includes in every mesh shader. That only holds for the
|
|
6
|
+
* materials three itself builds: a hand-written `ShaderMaterial` (three's `LineMaterial` among them)
|
|
7
|
+
* or a shader a glTF extension supplies may include no chunk at all, and a patch anchored on one that
|
|
8
|
+
* is not there silently does nothing — leaving a program that references identifiers nothing
|
|
9
|
+
* declared. `main` is the anchor that survives both.
|
|
10
|
+
*
|
|
11
|
+
* Both helpers throw when they cannot find it, for the same reason: what reaches the driver
|
|
12
|
+
* otherwise is a compile error naming an undeclared identifier, a long way from the patch that
|
|
13
|
+
* failed to declare it.
|
|
14
|
+
*
|
|
15
|
+
* {@link sceneColorAnchor} is not about `main`, but it answers the same kind of question: where in a
|
|
16
|
+
* shader's own text a patch belongs.
|
|
17
|
+
*/
|
|
18
|
+
/**
|
|
19
|
+
* Declares GLSL above `main`, where file-scope declarations belong — samplers, varyings, functions.
|
|
20
|
+
* @param source - The shader source.
|
|
21
|
+
* @param glsl - The declarations to add.
|
|
22
|
+
* @returns The source, with the declarations ahead of `main`.
|
|
23
|
+
* @throws {Error} If the source has no `main`.
|
|
24
|
+
*/
|
|
25
|
+
export declare function declareBeforeMain(source: string, glsl: string): string;
|
|
26
|
+
/**
|
|
27
|
+
* Inserts GLSL as `main`'s first statements.
|
|
28
|
+
*
|
|
29
|
+
* Successive calls each insert at the top, so **the last caller runs first**. That is what lets a
|
|
30
|
+
* value be supplied to a prologue already injected: the instanced meshes fetch their per-instance
|
|
31
|
+
* uniforms by an index resolved from `gl_InstanceID`, and the resolve is written afterwards
|
|
32
|
+
* precisely so it lands ahead of the fetch that reads it.
|
|
33
|
+
* @param source - The shader source.
|
|
34
|
+
* @param glsl - The statements to run first.
|
|
35
|
+
* @returns The source, with the statements opening `main`.
|
|
36
|
+
* @throws {Error} If the source has no `main`.
|
|
37
|
+
*/
|
|
38
|
+
export declare function openMain(source: string, glsl: string): string;
|
|
39
|
+
/**
|
|
40
|
+
* Rewrites the one place a shader spells `anchor`.
|
|
41
|
+
*
|
|
42
|
+
* For a patch that has to land somewhere a shader's *own* text decides — before the statement that
|
|
43
|
+
* reads a value, or over the line that outputs one. Throws for the same reason the two above do: an
|
|
44
|
+
* anchor that has moved leaves the shader compiling and drawing, just not the way the patch meant,
|
|
45
|
+
* and that is the failure worth being loud about.
|
|
46
|
+
* @param source - The shader source.
|
|
47
|
+
* @param anchor - The text to rewrite. Keep it in the replacement to insert around it.
|
|
48
|
+
* @param glsl - What takes its place.
|
|
49
|
+
* @returns The rewritten source.
|
|
50
|
+
* @throws {Error} If the source does not contain the anchor.
|
|
51
|
+
*/
|
|
52
|
+
export declare function replaceAnchor(source: string, anchor: string, glsl: string): string;
|
|
53
|
+
/**
|
|
54
|
+
* Where the fragment's **scene** color is final: the line a patch that changes the scene must land
|
|
55
|
+
* in front of.
|
|
56
|
+
*
|
|
57
|
+
* `<opaque_fragment>` writes the scene color, and three's output chain follows it — tone mapping,
|
|
58
|
+
* the colorspace conversion, fog, dithering. This returns the first of those. A patch below any of
|
|
59
|
+
* them would edit a value the chain already transformed, so its strength would depend on how bright
|
|
60
|
+
* the pixel already was.
|
|
61
|
+
*
|
|
62
|
+
* Two spellings, because three resolves `#include` after every `onBeforeCompile` while troika
|
|
63
|
+
* resolves it earlier, in its own rebuild of `main`. The expanded spelling is the `#if` line rather
|
|
64
|
+
* than the tone-mapping line inside it: the renderer pins `NoToneMapping`, which leaves
|
|
65
|
+
* `TONE_MAPPING` undefined, and the preprocessor then strips that block and anything put in it.
|
|
66
|
+
* @param fragmentShader - The shader to search.
|
|
67
|
+
* @returns The anchor found, or `undefined` if the shader has no output chain.
|
|
68
|
+
*/
|
|
69
|
+
export declare function sceneColorAnchor(fragmentShader: string): string | undefined;
|
|
70
|
+
//# sourceMappingURL=shader-source.d.ts.map
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Editing a shader's source around the one construct it cannot be missing: the opening of `main`.
|
|
3
|
+
*
|
|
4
|
+
* Every injection here used to key on a chunk `#include` instead — the instancing declarations rode
|
|
5
|
+
* on `batching_pars_vertex`, which three includes in every mesh shader. That only holds for the
|
|
6
|
+
* materials three itself builds: a hand-written `ShaderMaterial` (three's `LineMaterial` among them)
|
|
7
|
+
* or a shader a glTF extension supplies may include no chunk at all, and a patch anchored on one that
|
|
8
|
+
* is not there silently does nothing — leaving a program that references identifiers nothing
|
|
9
|
+
* declared. `main` is the anchor that survives both.
|
|
10
|
+
*
|
|
11
|
+
* Both helpers throw when they cannot find it, for the same reason: what reaches the driver
|
|
12
|
+
* otherwise is a compile error naming an undeclared identifier, a long way from the patch that
|
|
13
|
+
* failed to declare it.
|
|
14
|
+
*
|
|
15
|
+
* {@link sceneColorAnchor} is not about `main`, but it answers the same kind of question: where in a
|
|
16
|
+
* shader's own text a patch belongs.
|
|
17
|
+
*/
|
|
18
|
+
/**
|
|
19
|
+
* Declares GLSL above `main`, where file-scope declarations belong — samplers, varyings, functions.
|
|
20
|
+
* @param source - The shader source.
|
|
21
|
+
* @param glsl - The declarations to add.
|
|
22
|
+
* @returns The source, with the declarations ahead of `main`.
|
|
23
|
+
* @throws {Error} If the source has no `main`.
|
|
24
|
+
*/
|
|
25
|
+
export function declareBeforeMain(source, glsl) {
|
|
26
|
+
return rewriteMain(source, (main) => `${glsl}\n${main}`);
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Inserts GLSL as `main`'s first statements.
|
|
30
|
+
*
|
|
31
|
+
* Successive calls each insert at the top, so **the last caller runs first**. That is what lets a
|
|
32
|
+
* value be supplied to a prologue already injected: the instanced meshes fetch their per-instance
|
|
33
|
+
* uniforms by an index resolved from `gl_InstanceID`, and the resolve is written afterwards
|
|
34
|
+
* precisely so it lands ahead of the fetch that reads it.
|
|
35
|
+
* @param source - The shader source.
|
|
36
|
+
* @param glsl - The statements to run first.
|
|
37
|
+
* @returns The source, with the statements opening `main`.
|
|
38
|
+
* @throws {Error} If the source has no `main`.
|
|
39
|
+
*/
|
|
40
|
+
export function openMain(source, glsl) {
|
|
41
|
+
return rewriteMain(source, (main) => `${main}\n${glsl}`);
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Rewrites the one place a shader spells `anchor`.
|
|
45
|
+
*
|
|
46
|
+
* For a patch that has to land somewhere a shader's *own* text decides — before the statement that
|
|
47
|
+
* reads a value, or over the line that outputs one. Throws for the same reason the two above do: an
|
|
48
|
+
* anchor that has moved leaves the shader compiling and drawing, just not the way the patch meant,
|
|
49
|
+
* and that is the failure worth being loud about.
|
|
50
|
+
* @param source - The shader source.
|
|
51
|
+
* @param anchor - The text to rewrite. Keep it in the replacement to insert around it.
|
|
52
|
+
* @param glsl - What takes its place.
|
|
53
|
+
* @returns The rewritten source.
|
|
54
|
+
* @throws {Error} If the source does not contain the anchor.
|
|
55
|
+
*/
|
|
56
|
+
export function replaceAnchor(source, anchor, glsl) {
|
|
57
|
+
const at = source.indexOf(anchor);
|
|
58
|
+
if (at < 0)
|
|
59
|
+
throw new Error(`shader-source: the shader has no "${anchor}" to rewrite.`);
|
|
60
|
+
return source.slice(0, at) + glsl + source.slice(at + anchor.length);
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Where the fragment's **scene** color is final: the line a patch that changes the scene must land
|
|
64
|
+
* in front of.
|
|
65
|
+
*
|
|
66
|
+
* `<opaque_fragment>` writes the scene color, and three's output chain follows it — tone mapping,
|
|
67
|
+
* the colorspace conversion, fog, dithering. This returns the first of those. A patch below any of
|
|
68
|
+
* them would edit a value the chain already transformed, so its strength would depend on how bright
|
|
69
|
+
* the pixel already was.
|
|
70
|
+
*
|
|
71
|
+
* Two spellings, because three resolves `#include` after every `onBeforeCompile` while troika
|
|
72
|
+
* resolves it earlier, in its own rebuild of `main`. The expanded spelling is the `#if` line rather
|
|
73
|
+
* than the tone-mapping line inside it: the renderer pins `NoToneMapping`, which leaves
|
|
74
|
+
* `TONE_MAPPING` undefined, and the preprocessor then strips that block and anything put in it.
|
|
75
|
+
* @param fragmentShader - The shader to search.
|
|
76
|
+
* @returns The anchor found, or `undefined` if the shader has no output chain.
|
|
77
|
+
*/
|
|
78
|
+
export function sceneColorAnchor(fragmentShader) {
|
|
79
|
+
const include = /*glsl*/ `#include <tonemapping_fragment>`;
|
|
80
|
+
const expanded = /*glsl*/ `#if defined( TONE_MAPPING )`;
|
|
81
|
+
if (fragmentShader.includes(include))
|
|
82
|
+
return include;
|
|
83
|
+
return fragmentShader.includes(expanded) ? expanded : undefined;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Rewrites the first opening of `main`, splicing rather than replacing — so a `$` in the injected
|
|
87
|
+
* GLSL cannot be read as a replacement pattern.
|
|
88
|
+
* @param source - The shader source.
|
|
89
|
+
* @param rewrite - Receives the matched opening verbatim, whatever way it was spelled; returns what
|
|
90
|
+
* takes its place.
|
|
91
|
+
* @returns The rewritten source.
|
|
92
|
+
* @throws {Error} If the source has no `main`.
|
|
93
|
+
*/
|
|
94
|
+
function rewriteMain(source, rewrite) {
|
|
95
|
+
const found = MAIN.exec(source);
|
|
96
|
+
if (!found) {
|
|
97
|
+
throw new Error('shader-source: the shader has no "void main()" to inject into.');
|
|
98
|
+
}
|
|
99
|
+
const main = found[0];
|
|
100
|
+
return source.slice(0, found.index) + rewrite(main) + source.slice(found.index + main.length);
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* The opening of `main`, spelled every way GLSL allows it: with or without the redundant `void`
|
|
104
|
+
* parameter, and with whitespace — a newline included — wherever it is legal.
|
|
105
|
+
*
|
|
106
|
+
* Neither anchored nor global, so it finds the first one, which is the one to patch: troika renames
|
|
107
|
+
* the shader's original `main` to `troikaOrigMain` and writes a new one, and a patch belongs in the
|
|
108
|
+
* new one. The rename also puts `main` where no whitespace precedes it, so this does not match it.
|
|
109
|
+
*/
|
|
110
|
+
const MAIN = /void\s+main\s*\(\s*(?:void)?\s*\)\s*\{/;
|