@babylonjs/core 9.12.1 → 9.14.0
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/Animations/runtimeAnimation.js +11 -0
- package/Animations/runtimeAnimation.js.map +1 -1
- package/AudioV2/abstractAudio/abstractSound.d.ts +2 -1
- package/AudioV2/abstractAudio/abstractSound.js +3 -2
- package/AudioV2/abstractAudio/abstractSound.js.map +1 -1
- package/AudioV2/abstractAudio/abstractSoundInstance.d.ts +1 -1
- package/AudioV2/abstractAudio/abstractSoundInstance.js.map +1 -1
- package/AudioV2/abstractAudio/staticSound.d.ts +6 -0
- package/AudioV2/abstractAudio/staticSound.js +9 -1
- package/AudioV2/abstractAudio/staticSound.js.map +1 -1
- package/AudioV2/abstractAudio/streamingSound.d.ts +6 -0
- package/AudioV2/abstractAudio/streamingSound.js +9 -1
- package/AudioV2/abstractAudio/streamingSound.js.map +1 -1
- package/AudioV2/webAudio/webAudioStaticSound.d.ts +1 -1
- package/AudioV2/webAudio/webAudioStaticSound.js +6 -3
- package/AudioV2/webAudio/webAudioStaticSound.js.map +1 -1
- package/AudioV2/webAudio/webAudioStreamingSound.d.ts +1 -1
- package/AudioV2/webAudio/webAudioStreamingSound.js +7 -4
- package/AudioV2/webAudio/webAudioStreamingSound.js.map +1 -1
- package/Cameras/arcRotateCamera.pure.js +9 -3
- package/Cameras/arcRotateCamera.pure.js.map +1 -1
- package/Cameras/targetCamera.pure.js +18 -18
- package/Cameras/targetCamera.pure.js.map +1 -1
- package/Collisions/gpuPicker.d.ts +111 -4
- package/Collisions/gpuPicker.js +696 -119
- package/Collisions/gpuPicker.js.map +1 -1
- package/Engines/Native/nativeInterfaces.d.ts +4 -2
- package/Engines/Native/nativeInterfaces.js.map +1 -1
- package/Engines/Native/nativeRenderTargetWrapper.d.ts +3 -0
- package/Engines/Native/nativeRenderTargetWrapper.js +24 -1
- package/Engines/Native/nativeRenderTargetWrapper.js.map +1 -1
- package/Engines/WebGPU/Extensions/engine.videoTexture.pure.js +16 -3
- package/Engines/WebGPU/Extensions/engine.videoTexture.pure.js.map +1 -1
- package/Engines/abstractEngine.pure.d.ts +8 -1
- package/Engines/abstractEngine.pure.js +4 -3
- package/Engines/abstractEngine.pure.js.map +1 -1
- package/Engines/engine.d.ts +113 -0
- package/Engines/nullEngine.pure.js +5 -1
- package/Engines/nullEngine.pure.js.map +1 -1
- package/Engines/thinNativeEngine.pure.d.ts +5 -0
- package/Engines/thinNativeEngine.pure.js +158 -13
- package/Engines/thinNativeEngine.pure.js.map +1 -1
- package/FlowGraph/flowGraph.js +9 -0
- package/FlowGraph/flowGraph.js.map +1 -1
- package/FlowGraph/flowGraphDataConnection.pure.d.ts +14 -0
- package/FlowGraph/flowGraphDataConnection.pure.js +38 -1
- package/FlowGraph/flowGraphDataConnection.pure.js.map +1 -1
- package/FlowGraph/serialization.d.ts +11 -0
- package/FlowGraph/serialization.js +11 -1
- package/FlowGraph/serialization.js.map +1 -1
- package/FrameGraph/Tasks/Rendering/iblShadows/iblShadowsAccumulationTask.d.ts +2 -2
- package/FrameGraph/Tasks/Rendering/iblShadows/iblShadowsAccumulationTask.js +43 -51
- package/FrameGraph/Tasks/Rendering/iblShadows/iblShadowsAccumulationTask.js.map +1 -1
- package/FrameGraph/Tasks/Rendering/iblShadowsRendererTask.pure.d.ts +0 -1
- package/FrameGraph/Tasks/Rendering/iblShadowsRendererTask.pure.js +63 -35
- package/FrameGraph/Tasks/Rendering/iblShadowsRendererTask.pure.js.map +1 -1
- package/Lights/Clustered/clusteredLightContainer.pure.d.ts +1 -1
- package/Lights/Clustered/clusteredLightContainer.pure.js +2 -2
- package/Lights/Clustered/clusteredLightContainer.pure.js.map +1 -1
- package/Lights/areaLight.pure.js +5 -0
- package/Lights/areaLight.pure.js.map +1 -1
- package/Lights/light.d.ts +4 -2
- package/Lights/light.js +10 -4
- package/Lights/light.js.map +1 -1
- package/Lights/rectAreaLight.pure.d.ts +17 -0
- package/Lights/rectAreaLight.pure.js +67 -0
- package/Lights/rectAreaLight.pure.js.map +1 -1
- package/Loading/Plugins/babylonFileLoader.pure.js +1 -1
- package/Loading/Plugins/babylonFileLoader.pure.js.map +1 -1
- package/Loading/sceneLoader.d.ts +5 -1
- package/Loading/sceneLoader.js +298 -401
- package/Loading/sceneLoader.js.map +1 -1
- package/Materials/GaussianSplatting/gaussianSplattingGpuPickingMaterialPlugin.pure.d.ts +15 -0
- package/Materials/GaussianSplatting/gaussianSplattingGpuPickingMaterialPlugin.pure.js +34 -1
- package/Materials/GaussianSplatting/gaussianSplattingGpuPickingMaterialPlugin.pure.js.map +1 -1
- package/Materials/GaussianSplatting/gaussianSplattingMaterial.pure.d.ts +9 -0
- package/Materials/GaussianSplatting/gaussianSplattingMaterial.pure.js +26 -2
- package/Materials/GaussianSplatting/gaussianSplattingMaterial.pure.js.map +1 -1
- package/Materials/Node/Blocks/GaussianSplatting/gaussianSplattingBlock.pure.js +3 -1
- package/Materials/Node/Blocks/GaussianSplatting/gaussianSplattingBlock.pure.js.map +1 -1
- package/Materials/Textures/HTML/htmlInCanvasPolyfill.d.ts +43 -0
- package/Materials/Textures/HTML/htmlInCanvasPolyfill.js +64 -0
- package/Materials/Textures/HTML/htmlInCanvasPolyfill.js.map +1 -0
- package/Materials/Textures/HTML/htmlInteractionManager.d.ts +69 -0
- package/Materials/Textures/HTML/htmlInteractionManager.js +130 -0
- package/Materials/Textures/HTML/htmlInteractionManager.js.map +1 -0
- package/Materials/Textures/HTML/htmlRaycastInteractionManager.d.ts +84 -0
- package/Materials/Textures/HTML/htmlRaycastInteractionManager.js +246 -0
- package/Materials/Textures/HTML/htmlRaycastInteractionManager.js.map +1 -0
- package/Materials/Textures/HTML/htmlTexture.d.ts +148 -0
- package/Materials/Textures/HTML/htmlTexture.js +455 -0
- package/Materials/Textures/HTML/htmlTexture.js.map +1 -0
- package/Materials/Textures/HTML/index.d.ts +4 -0
- package/Materials/Textures/HTML/index.js +5 -0
- package/Materials/Textures/HTML/index.js.map +1 -0
- package/Materials/Textures/HTML/pure.d.ts +5 -0
- package/Materials/Textures/HTML/pure.js +6 -0
- package/Materials/Textures/HTML/pure.js.map +1 -0
- package/Materials/Textures/index.d.ts +1 -0
- package/Materials/Textures/index.js +1 -1
- package/Materials/Textures/index.js.map +1 -1
- package/Materials/Textures/pure.d.ts +1 -0
- package/Materials/Textures/pure.js +1 -0
- package/Materials/Textures/pure.js.map +1 -1
- package/Materials/Textures/videoTexture.pure.d.ts +3 -0
- package/Materials/Textures/videoTexture.pure.js +30 -0
- package/Materials/Textures/videoTexture.pure.js.map +1 -1
- package/Meshes/GaussianSplatting/gaussianSplattingMeshBase.pure.d.ts +36 -0
- package/Meshes/GaussianSplatting/gaussianSplattingMeshBase.pure.js +56 -0
- package/Meshes/GaussianSplatting/gaussianSplattingMeshBase.pure.js.map +1 -1
- package/Meshes/GaussianSplatting/gaussianSplattingSortWorker.d.ts +3 -1
- package/Meshes/GaussianSplatting/gaussianSplattingSortWorker.js +210 -48
- package/Meshes/GaussianSplatting/gaussianSplattingSortWorker.js.map +1 -1
- package/Misc/tools.pure.js +1 -1
- package/Misc/tools.pure.js.map +1 -1
- package/Rendering/IBLShadows/iblShadowsVoxelRenderer.pure.d.ts +1 -0
- package/Rendering/IBLShadows/iblShadowsVoxelRenderer.pure.js +34 -0
- package/Rendering/IBLShadows/iblShadowsVoxelRenderer.pure.js.map +1 -1
- package/Shaders/ShadersInclude/gaussianSplatting.js +1 -0
- package/Shaders/ShadersInclude/gaussianSplatting.js.map +1 -1
- package/Shaders/gaussianSplatting.fragment.js +17 -1
- package/Shaders/gaussianSplatting.fragment.js.map +1 -1
- package/Shaders/gaussianSplatting.vertex.js +1 -1
- package/Shaders/gaussianSplatting.vertex.js.map +1 -1
- package/Shaders/gaussianSplattingDepth.vertex.js +1 -1
- package/Shaders/gaussianSplattingDepth.vertex.js.map +1 -1
- package/Shaders/picking.fragment.js +39 -3
- package/Shaders/picking.fragment.js.map +1 -1
- package/ShadersWGSL/ShadersInclude/gaussianSplatting.js +3 -1
- package/ShadersWGSL/ShadersInclude/gaussianSplatting.js.map +1 -1
- package/ShadersWGSL/ShadersInclude/openpbrIblFunctions.js +4 -3
- package/ShadersWGSL/ShadersInclude/openpbrIblFunctions.js.map +1 -1
- package/ShadersWGSL/gaussianSplatting.fragment.js +14 -1
- package/ShadersWGSL/gaussianSplatting.fragment.js.map +1 -1
- package/ShadersWGSL/gaussianSplatting.vertex.js +2 -2
- package/ShadersWGSL/gaussianSplatting.vertex.js.map +1 -1
- package/ShadersWGSL/gaussianSplattingDepth.vertex.js +2 -2
- package/ShadersWGSL/gaussianSplattingDepth.vertex.js.map +1 -1
- package/ShadersWGSL/picking.fragment.js +22 -1
- package/ShadersWGSL/picking.fragment.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
import { PointerEventTypes } from "../../../Events/pointerEvents.js";
|
|
2
|
+
// Attribution: the raycast/UV event-routing technique used here is prior art from
|
|
3
|
+
// `three-html-render` (Palash Bansal, MIT) and Jake Archibald's `curved-markup` demo.
|
|
4
|
+
// The implementation below is a clean-room rewrite against Babylon.js' own picking APIs.
|
|
5
|
+
/**
|
|
6
|
+
* Maps a UV coordinate sampled from an {@link HtmlTexture} to a pixel position inside the source element.
|
|
7
|
+
*
|
|
8
|
+
* When `invertY` is true (the default, matching {@link HtmlTexture}'s default upload orientation) the V
|
|
9
|
+
* axis is flipped so that `v = 1` maps to the top of the element, matching the top-left origin used by
|
|
10
|
+
* DOM layout.
|
|
11
|
+
* @param u horizontal texture coordinate, normally in [0, 1]
|
|
12
|
+
* @param v vertical texture coordinate, normally in [0, 1]
|
|
13
|
+
* @param width element width in pixels
|
|
14
|
+
* @param height element height in pixels
|
|
15
|
+
* @param invertY whether the texture content is stored Y-inverted (default true)
|
|
16
|
+
* @returns the pixel position `{ x, y }` within the element
|
|
17
|
+
*/
|
|
18
|
+
export function GetElementPixelFromUv(u, v, width, height, invertY = true) {
|
|
19
|
+
return { x: u * width, y: (invertY ? 1 - v : v) * height };
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Routes Babylon pointer events to a live HTML element rendered through an {@link HtmlTexture}.
|
|
23
|
+
*
|
|
24
|
+
* On every pointer event the scene is picked; when the configured mesh is hit, the picked UV is mapped to
|
|
25
|
+
* a pixel inside the source element and an equivalent DOM pointer (and mouse) event is dispatched there.
|
|
26
|
+
* This works for arbitrary meshes (planes, boxes, curved surfaces) because it relies on UV coordinates
|
|
27
|
+
* rather than a flat CSS overlay. For a perspective-correct overlay on planar surfaces, see
|
|
28
|
+
* {@link HtmlInteractionManager}.
|
|
29
|
+
*/
|
|
30
|
+
export class HtmlRaycastInteractionManager {
|
|
31
|
+
/**
|
|
32
|
+
* Creates a raycast interaction manager.
|
|
33
|
+
* @param scene the scene to listen to for pointer events
|
|
34
|
+
* @param texture the HTML texture whose element should receive the forwarded events
|
|
35
|
+
* @param mesh the mesh displaying the texture; only hits on this mesh are forwarded
|
|
36
|
+
* @param options optional configuration
|
|
37
|
+
*/
|
|
38
|
+
constructor(scene, texture, mesh, options = {}) {
|
|
39
|
+
this._rectCache = new Map();
|
|
40
|
+
this._rectCacheValid = false;
|
|
41
|
+
this._resizeObserver = null;
|
|
42
|
+
this._mutationObserver = null;
|
|
43
|
+
this._scene = scene;
|
|
44
|
+
this._mesh = mesh;
|
|
45
|
+
this._element = options.targetElement ?? texture.element;
|
|
46
|
+
this._backFaceCulling = options.backFaceCulling ?? true;
|
|
47
|
+
this._invertY = options.invertY ?? true;
|
|
48
|
+
// The hosted element is a child of the engine's rendering canvas, where Babylon's own input
|
|
49
|
+
// listeners live. The synthetic events we dispatch bubble, so without this they would reach the
|
|
50
|
+
// canvas and be re-interpreted as real input - moving the camera and re-entering this manager in a
|
|
51
|
+
// feedback loop. Stopping propagation at the host element keeps them inside the HTML subtree (so
|
|
52
|
+
// event delegation still works) while never leaking back to the engine.
|
|
53
|
+
this._containHandler = (evt) => evt.stopPropagation();
|
|
54
|
+
for (const type of _ContainedEventTypes) {
|
|
55
|
+
this._element.addEventListener(type, this._containHandler);
|
|
56
|
+
}
|
|
57
|
+
// Resolving the target reads layout (getBoundingClientRect) for the element and its descendants on
|
|
58
|
+
// every pointer event. Cache those rects and only re-read them when layout can actually change - on
|
|
59
|
+
// size changes, DOM mutations, or viewport scroll/resize - so high-frequency pointermove events stay
|
|
60
|
+
// pure arithmetic instead of forcing a synchronous reflow each time.
|
|
61
|
+
this._invalidateRectCache = () => {
|
|
62
|
+
this._rectCacheValid = false;
|
|
63
|
+
};
|
|
64
|
+
if (typeof ResizeObserver !== "undefined") {
|
|
65
|
+
this._resizeObserver = new ResizeObserver(this._invalidateRectCache);
|
|
66
|
+
this._resizeObserver.observe(this._element);
|
|
67
|
+
}
|
|
68
|
+
if (typeof MutationObserver !== "undefined") {
|
|
69
|
+
this._mutationObserver = new MutationObserver(this._invalidateRectCache);
|
|
70
|
+
this._mutationObserver.observe(this._element, { attributes: true, childList: true, characterData: true, subtree: true });
|
|
71
|
+
}
|
|
72
|
+
if (typeof window !== "undefined") {
|
|
73
|
+
window.addEventListener("resize", this._invalidateRectCache);
|
|
74
|
+
window.addEventListener("scroll", this._invalidateRectCache, true);
|
|
75
|
+
}
|
|
76
|
+
this._observer = scene.onPointerObservable.add((pointerInfo) => this._onPointer(pointerInfo));
|
|
77
|
+
}
|
|
78
|
+
_onPointer(pointerInfo) {
|
|
79
|
+
const domName = _PointerTypeToDomEvent(pointerInfo.type);
|
|
80
|
+
if (!domName) {
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
const pickInfo = pointerInfo.pickInfo;
|
|
84
|
+
if (!pickInfo || !pickInfo.hit || pickInfo.pickedMesh !== this._mesh) {
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
if (this._backFaceCulling && this._isBackFace(pointerInfo)) {
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
const uv = pickInfo.getTextureCoordinates();
|
|
91
|
+
if (!uv) {
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
const rect = this._getRect(this._element);
|
|
95
|
+
const { x, y } = GetElementPixelFromUv(uv.x, uv.y, rect.width || this._element.offsetWidth, rect.height || this._element.offsetHeight, this._invertY);
|
|
96
|
+
this._dispatch(domName, rect.left + x, rect.top + y, pointerInfo);
|
|
97
|
+
}
|
|
98
|
+
_isBackFace(pointerInfo) {
|
|
99
|
+
const pickInfo = pointerInfo.pickInfo;
|
|
100
|
+
const normal = pickInfo.getNormal(true, true);
|
|
101
|
+
if (!normal || !pickInfo.pickedPoint) {
|
|
102
|
+
return false;
|
|
103
|
+
}
|
|
104
|
+
const camera = this._scene.activeCamera;
|
|
105
|
+
if (!camera) {
|
|
106
|
+
return false;
|
|
107
|
+
}
|
|
108
|
+
// The face is back-facing when its world normal points away from the camera-to-surface direction.
|
|
109
|
+
const toSurface = pickInfo.pickedPoint.subtract(camera.globalPosition);
|
|
110
|
+
return normal.dot(toSurface) > 0;
|
|
111
|
+
}
|
|
112
|
+
_dispatch(domName, clientX, clientY, pointerInfo) {
|
|
113
|
+
if (typeof PointerEvent === "undefined") {
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
const sourceEvent = pointerInfo.event;
|
|
117
|
+
// document.elementFromPoint is unreliable here: the element is hosted inside the engine's rendering
|
|
118
|
+
// canvas (whose painted WebGL/WebGPU content covers the DOM children) and is marked `inert`, so the
|
|
119
|
+
// browser's own hit testing skips it. Resolve the deepest descendant under the mapped point manually
|
|
120
|
+
// from the laid-out subtree instead; synthetic dispatch still triggers listeners on inert elements.
|
|
121
|
+
const target = this._resolveTarget(clientX, clientY);
|
|
122
|
+
const init = {
|
|
123
|
+
bubbles: true,
|
|
124
|
+
cancelable: true,
|
|
125
|
+
clientX,
|
|
126
|
+
clientY,
|
|
127
|
+
button: sourceEvent.button ?? 0,
|
|
128
|
+
buttons: sourceEvent.buttons ?? 0,
|
|
129
|
+
pointerId: sourceEvent.pointerId ?? 1,
|
|
130
|
+
pointerType: sourceEvent.pointerType ?? "mouse",
|
|
131
|
+
};
|
|
132
|
+
target.dispatchEvent(new PointerEvent(domName, init));
|
|
133
|
+
const mouseName = _PointerToMouseEvent(domName);
|
|
134
|
+
if (mouseName) {
|
|
135
|
+
target.dispatchEvent(new MouseEvent(mouseName, init));
|
|
136
|
+
}
|
|
137
|
+
// The browser synthesizes a "click" from a native down/up pair, but synthetic mouse events do not,
|
|
138
|
+
// so we emit it ourselves: on pointerdown remember the target, then on pointerup dispatch a click
|
|
139
|
+
// when the release lands on the same element (matching native click semantics closely enough).
|
|
140
|
+
// Native browsers only synthesize "click" for the primary button, so gate on button 0.
|
|
141
|
+
if (domName === "pointerdown") {
|
|
142
|
+
this._downTarget = target;
|
|
143
|
+
}
|
|
144
|
+
else if (domName === "pointerup") {
|
|
145
|
+
if (this._downTarget === target && init.button === 0) {
|
|
146
|
+
target.dispatchEvent(new MouseEvent("click", init));
|
|
147
|
+
}
|
|
148
|
+
this._downTarget = null;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Resolves the deepest descendant of the hosted element whose layout box contains the given client point.
|
|
153
|
+
* @param clientX horizontal client coordinate
|
|
154
|
+
* @param clientY vertical client coordinate
|
|
155
|
+
* @returns the deepest matching element, or the hosted element itself when no descendant matches
|
|
156
|
+
*/
|
|
157
|
+
_resolveTarget(clientX, clientY) {
|
|
158
|
+
let target = this._element;
|
|
159
|
+
let advanced = true;
|
|
160
|
+
while (advanced) {
|
|
161
|
+
advanced = false;
|
|
162
|
+
const children = target.children;
|
|
163
|
+
for (let i = children.length - 1; i >= 0; i--) {
|
|
164
|
+
const child = children[i];
|
|
165
|
+
const rect = this._getRect(child);
|
|
166
|
+
if (clientX >= rect.left && clientX <= rect.right && clientY >= rect.top && clientY <= rect.bottom) {
|
|
167
|
+
target = child;
|
|
168
|
+
advanced = true;
|
|
169
|
+
break;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
return target;
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Returns the client rect of an element, served from a cache that is rebuilt only when layout may have
|
|
177
|
+
* changed (see the constructor). This keeps pointer routing free of per-event layout reads.
|
|
178
|
+
* @param element the element whose bounding rect is needed
|
|
179
|
+
* @returns the element's cached {@link DOMRect}
|
|
180
|
+
*/
|
|
181
|
+
_getRect(element) {
|
|
182
|
+
if (!this._rectCacheValid) {
|
|
183
|
+
this._rectCache.clear();
|
|
184
|
+
this._rectCacheValid = true;
|
|
185
|
+
}
|
|
186
|
+
let rect = this._rectCache.get(element);
|
|
187
|
+
if (!rect) {
|
|
188
|
+
rect = element.getBoundingClientRect();
|
|
189
|
+
this._rectCache.set(element, rect);
|
|
190
|
+
}
|
|
191
|
+
return rect;
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Detaches the manager and stops forwarding pointer events.
|
|
195
|
+
*/
|
|
196
|
+
dispose() {
|
|
197
|
+
for (const type of _ContainedEventTypes) {
|
|
198
|
+
this._element.removeEventListener(type, this._containHandler);
|
|
199
|
+
}
|
|
200
|
+
if (this._resizeObserver) {
|
|
201
|
+
this._resizeObserver.disconnect();
|
|
202
|
+
this._resizeObserver = null;
|
|
203
|
+
}
|
|
204
|
+
if (this._mutationObserver) {
|
|
205
|
+
this._mutationObserver.disconnect();
|
|
206
|
+
this._mutationObserver = null;
|
|
207
|
+
}
|
|
208
|
+
if (typeof window !== "undefined") {
|
|
209
|
+
window.removeEventListener("resize", this._invalidateRectCache);
|
|
210
|
+
window.removeEventListener("scroll", this._invalidateRectCache, true);
|
|
211
|
+
}
|
|
212
|
+
this._rectCache.clear();
|
|
213
|
+
if (this._observer) {
|
|
214
|
+
this._scene.onPointerObservable.remove(this._observer);
|
|
215
|
+
this._observer = null;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
function _PointerTypeToDomEvent(type) {
|
|
220
|
+
switch (type) {
|
|
221
|
+
case PointerEventTypes.POINTERDOWN:
|
|
222
|
+
return "pointerdown";
|
|
223
|
+
case PointerEventTypes.POINTERUP:
|
|
224
|
+
return "pointerup";
|
|
225
|
+
case PointerEventTypes.POINTERMOVE:
|
|
226
|
+
return "pointermove";
|
|
227
|
+
default:
|
|
228
|
+
return null;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
function _PointerToMouseEvent(domName) {
|
|
232
|
+
switch (domName) {
|
|
233
|
+
case "pointerdown":
|
|
234
|
+
return "mousedown";
|
|
235
|
+
case "pointerup":
|
|
236
|
+
return "mouseup";
|
|
237
|
+
case "pointermove":
|
|
238
|
+
return "mousemove";
|
|
239
|
+
default:
|
|
240
|
+
return null;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
// Synthetic event types we dispatch into the hosted element; they are contained at the host so they do
|
|
244
|
+
// not bubble back into the engine's rendering canvas (see the constructor for the rationale).
|
|
245
|
+
const _ContainedEventTypes = ["pointerdown", "pointerup", "pointermove", "mousedown", "mouseup", "mousemove", "click"];
|
|
246
|
+
//# sourceMappingURL=htmlRaycastInteractionManager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"htmlRaycastInteractionManager.js","sourceRoot":"","sources":["../../../../../../dev/core/src/Materials/Textures/HTML/htmlRaycastInteractionManager.ts"],"names":[],"mappings":"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","sourcesContent":["import { type Scene } from \"../../../scene.pure\";\nimport { type AbstractMesh } from \"../../../Meshes/abstractMesh.pure\";\nimport { type HtmlTexture } from \"./htmlTexture\";\nimport { type Observer } from \"../../../Misc/observable.pure\";\nimport { type PointerInfo, PointerEventTypes } from \"../../../Events/pointerEvents\";\nimport { type Nullable } from \"../../../types\";\n\n// Attribution: the raycast/UV event-routing technique used here is prior art from\n// `three-html-render` (Palash Bansal, MIT) and Jake Archibald's `curved-markup` demo.\n// The implementation below is a clean-room rewrite against Babylon.js' own picking APIs.\n\n/**\n * Maps a UV coordinate sampled from an {@link HtmlTexture} to a pixel position inside the source element.\n *\n * When `invertY` is true (the default, matching {@link HtmlTexture}'s default upload orientation) the V\n * axis is flipped so that `v = 1` maps to the top of the element, matching the top-left origin used by\n * DOM layout.\n * @param u horizontal texture coordinate, normally in [0, 1]\n * @param v vertical texture coordinate, normally in [0, 1]\n * @param width element width in pixels\n * @param height element height in pixels\n * @param invertY whether the texture content is stored Y-inverted (default true)\n * @returns the pixel position `{ x, y }` within the element\n */\nexport function GetElementPixelFromUv(u: number, v: number, width: number, height: number, invertY: boolean = true): { x: number; y: number } {\n return { x: u * width, y: (invertY ? 1 - v : v) * height };\n}\n\n/**\n * Options for {@link HtmlRaycastInteractionManager}.\n */\nexport interface IHtmlRaycastInteractionManagerOptions {\n /** The DOM element that receives the forwarded pointer events (defaults to the texture's element). */\n targetElement?: HTMLElement;\n /** Whether hits on back-facing geometry are ignored (default true). */\n backFaceCulling?: boolean;\n /** Whether the texture content is stored Y-inverted, used when mapping UVs to element pixels (default true). */\n invertY?: boolean;\n}\n\n/**\n * Routes Babylon pointer events to a live HTML element rendered through an {@link HtmlTexture}.\n *\n * On every pointer event the scene is picked; when the configured mesh is hit, the picked UV is mapped to\n * a pixel inside the source element and an equivalent DOM pointer (and mouse) event is dispatched there.\n * This works for arbitrary meshes (planes, boxes, curved surfaces) because it relies on UV coordinates\n * rather than a flat CSS overlay. For a perspective-correct overlay on planar surfaces, see\n * {@link HtmlInteractionManager}.\n */\nexport class HtmlRaycastInteractionManager {\n private readonly _scene: Scene;\n private readonly _mesh: AbstractMesh;\n private readonly _element: HTMLElement;\n private readonly _backFaceCulling: boolean;\n private readonly _invertY: boolean;\n private _observer: Nullable<Observer<PointerInfo>>;\n private _downTarget: Nullable<EventTarget>;\n private readonly _containHandler: (evt: Event) => void;\n private readonly _rectCache = new Map<HTMLElement, DOMRect>();\n private _rectCacheValid = false;\n private readonly _invalidateRectCache: () => void;\n private _resizeObserver: Nullable<ResizeObserver> = null;\n private _mutationObserver: Nullable<MutationObserver> = null;\n\n /**\n * Creates a raycast interaction manager.\n * @param scene the scene to listen to for pointer events\n * @param texture the HTML texture whose element should receive the forwarded events\n * @param mesh the mesh displaying the texture; only hits on this mesh are forwarded\n * @param options optional configuration\n */\n constructor(scene: Scene, texture: HtmlTexture, mesh: AbstractMesh, options: IHtmlRaycastInteractionManagerOptions = {}) {\n this._scene = scene;\n this._mesh = mesh;\n this._element = options.targetElement ?? texture.element;\n this._backFaceCulling = options.backFaceCulling ?? true;\n this._invertY = options.invertY ?? true;\n\n // The hosted element is a child of the engine's rendering canvas, where Babylon's own input\n // listeners live. The synthetic events we dispatch bubble, so without this they would reach the\n // canvas and be re-interpreted as real input - moving the camera and re-entering this manager in a\n // feedback loop. Stopping propagation at the host element keeps them inside the HTML subtree (so\n // event delegation still works) while never leaking back to the engine.\n this._containHandler = (evt: Event) => evt.stopPropagation();\n for (const type of _ContainedEventTypes) {\n this._element.addEventListener(type, this._containHandler);\n }\n\n // Resolving the target reads layout (getBoundingClientRect) for the element and its descendants on\n // every pointer event. Cache those rects and only re-read them when layout can actually change - on\n // size changes, DOM mutations, or viewport scroll/resize - so high-frequency pointermove events stay\n // pure arithmetic instead of forcing a synchronous reflow each time.\n this._invalidateRectCache = () => {\n this._rectCacheValid = false;\n };\n if (typeof ResizeObserver !== \"undefined\") {\n this._resizeObserver = new ResizeObserver(this._invalidateRectCache);\n this._resizeObserver.observe(this._element);\n }\n if (typeof MutationObserver !== \"undefined\") {\n this._mutationObserver = new MutationObserver(this._invalidateRectCache);\n this._mutationObserver.observe(this._element, { attributes: true, childList: true, characterData: true, subtree: true });\n }\n if (typeof window !== \"undefined\") {\n window.addEventListener(\"resize\", this._invalidateRectCache);\n window.addEventListener(\"scroll\", this._invalidateRectCache, true);\n }\n\n this._observer = scene.onPointerObservable.add((pointerInfo) => this._onPointer(pointerInfo));\n }\n\n private _onPointer(pointerInfo: PointerInfo): void {\n const domName = _PointerTypeToDomEvent(pointerInfo.type);\n if (!domName) {\n return;\n }\n\n const pickInfo = pointerInfo.pickInfo;\n if (!pickInfo || !pickInfo.hit || pickInfo.pickedMesh !== this._mesh) {\n return;\n }\n\n if (this._backFaceCulling && this._isBackFace(pointerInfo)) {\n return;\n }\n\n const uv = pickInfo.getTextureCoordinates();\n if (!uv) {\n return;\n }\n\n const rect = this._getRect(this._element);\n const { x, y } = GetElementPixelFromUv(uv.x, uv.y, rect.width || this._element.offsetWidth, rect.height || this._element.offsetHeight, this._invertY);\n\n this._dispatch(domName, rect.left + x, rect.top + y, pointerInfo);\n }\n\n private _isBackFace(pointerInfo: PointerInfo): boolean {\n const pickInfo = pointerInfo.pickInfo!;\n const normal = pickInfo.getNormal(true, true);\n if (!normal || !pickInfo.pickedPoint) {\n return false;\n }\n const camera = this._scene.activeCamera;\n if (!camera) {\n return false;\n }\n // The face is back-facing when its world normal points away from the camera-to-surface direction.\n const toSurface = pickInfo.pickedPoint.subtract(camera.globalPosition);\n return normal.dot(toSurface) > 0;\n }\n\n private _dispatch(domName: string, clientX: number, clientY: number, pointerInfo: PointerInfo): void {\n if (typeof PointerEvent === \"undefined\") {\n return;\n }\n\n const sourceEvent = pointerInfo.event as Partial<PointerEvent>;\n // document.elementFromPoint is unreliable here: the element is hosted inside the engine's rendering\n // canvas (whose painted WebGL/WebGPU content covers the DOM children) and is marked `inert`, so the\n // browser's own hit testing skips it. Resolve the deepest descendant under the mapped point manually\n // from the laid-out subtree instead; synthetic dispatch still triggers listeners on inert elements.\n const target = this._resolveTarget(clientX, clientY);\n\n const init: PointerEventInit = {\n bubbles: true,\n cancelable: true,\n clientX,\n clientY,\n button: sourceEvent.button ?? 0,\n buttons: sourceEvent.buttons ?? 0,\n pointerId: sourceEvent.pointerId ?? 1,\n pointerType: sourceEvent.pointerType ?? \"mouse\",\n };\n\n target.dispatchEvent(new PointerEvent(domName, init));\n\n const mouseName = _PointerToMouseEvent(domName);\n if (mouseName) {\n target.dispatchEvent(new MouseEvent(mouseName, init));\n }\n\n // The browser synthesizes a \"click\" from a native down/up pair, but synthetic mouse events do not,\n // so we emit it ourselves: on pointerdown remember the target, then on pointerup dispatch a click\n // when the release lands on the same element (matching native click semantics closely enough).\n // Native browsers only synthesize \"click\" for the primary button, so gate on button 0.\n if (domName === \"pointerdown\") {\n this._downTarget = target;\n } else if (domName === \"pointerup\") {\n if (this._downTarget === target && init.button === 0) {\n target.dispatchEvent(new MouseEvent(\"click\", init));\n }\n this._downTarget = null;\n }\n }\n\n /**\n * Resolves the deepest descendant of the hosted element whose layout box contains the given client point.\n * @param clientX horizontal client coordinate\n * @param clientY vertical client coordinate\n * @returns the deepest matching element, or the hosted element itself when no descendant matches\n */\n private _resolveTarget(clientX: number, clientY: number): HTMLElement {\n let target: HTMLElement = this._element;\n let advanced = true;\n while (advanced) {\n advanced = false;\n const children = target.children;\n for (let i = children.length - 1; i >= 0; i--) {\n const child = children[i] as HTMLElement;\n const rect = this._getRect(child);\n if (clientX >= rect.left && clientX <= rect.right && clientY >= rect.top && clientY <= rect.bottom) {\n target = child;\n advanced = true;\n break;\n }\n }\n }\n return target;\n }\n\n /**\n * Returns the client rect of an element, served from a cache that is rebuilt only when layout may have\n * changed (see the constructor). This keeps pointer routing free of per-event layout reads.\n * @param element the element whose bounding rect is needed\n * @returns the element's cached {@link DOMRect}\n */\n private _getRect(element: HTMLElement): DOMRect {\n if (!this._rectCacheValid) {\n this._rectCache.clear();\n this._rectCacheValid = true;\n }\n let rect = this._rectCache.get(element);\n if (!rect) {\n rect = element.getBoundingClientRect();\n this._rectCache.set(element, rect);\n }\n return rect;\n }\n\n /**\n * Detaches the manager and stops forwarding pointer events.\n */\n public dispose(): void {\n for (const type of _ContainedEventTypes) {\n this._element.removeEventListener(type, this._containHandler);\n }\n if (this._resizeObserver) {\n this._resizeObserver.disconnect();\n this._resizeObserver = null;\n }\n if (this._mutationObserver) {\n this._mutationObserver.disconnect();\n this._mutationObserver = null;\n }\n if (typeof window !== \"undefined\") {\n window.removeEventListener(\"resize\", this._invalidateRectCache);\n window.removeEventListener(\"scroll\", this._invalidateRectCache, true);\n }\n this._rectCache.clear();\n if (this._observer) {\n this._scene.onPointerObservable.remove(this._observer);\n this._observer = null;\n }\n }\n}\n\nfunction _PointerTypeToDomEvent(type: number): Nullable<string> {\n switch (type) {\n case PointerEventTypes.POINTERDOWN:\n return \"pointerdown\";\n case PointerEventTypes.POINTERUP:\n return \"pointerup\";\n case PointerEventTypes.POINTERMOVE:\n return \"pointermove\";\n default:\n return null;\n }\n}\n\nfunction _PointerToMouseEvent(domName: string): Nullable<string> {\n switch (domName) {\n case \"pointerdown\":\n return \"mousedown\";\n case \"pointerup\":\n return \"mouseup\";\n case \"pointermove\":\n return \"mousemove\";\n default:\n return null;\n }\n}\n\n// Synthetic event types we dispatch into the hosted element; they are contained at the host so they do\n// not bubble back into the engine's rendering canvas (see the constructor for the rationale).\nconst _ContainedEventTypes = [\"pointerdown\", \"pointerup\", \"pointermove\", \"mousedown\", \"mouseup\", \"mousemove\", \"click\"] as const;\n"]}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
/** This module has no top-level side effects: the engine extension it depends on is registered lazily from the constructor */
|
|
2
|
+
import { type Nullable } from "../../../types.js";
|
|
3
|
+
import { BaseTexture } from "../baseTexture.pure.js";
|
|
4
|
+
import { Matrix } from "../../../Maths/math.vector.pure.js";
|
|
5
|
+
import { Observable } from "../../../Misc/observable.pure.js";
|
|
6
|
+
import { type AbstractEngine } from "../../../Engines/abstractEngine.pure.js";
|
|
7
|
+
import { type InternalTexture } from "../internalTexture.js";
|
|
8
|
+
import { type Scene } from "../../../scene.pure.js";
|
|
9
|
+
/**
|
|
10
|
+
* Detects whether the engine can upload an HTML element through the native (or polyfilled) WICG
|
|
11
|
+
* HTML-in-Canvas API, without emitting any warning. Used to decide whether the SVG fallback is needed.
|
|
12
|
+
* @param engine defines the engine to test
|
|
13
|
+
* @returns true when the WICG upload API is available for this engine
|
|
14
|
+
*/
|
|
15
|
+
export declare function IsHtmlInCanvasUploadSupported(engine: AbstractEngine): boolean;
|
|
16
|
+
/**
|
|
17
|
+
* Uploads a live HTML element (or a captured ElementImage) into an existing 2D texture using the WICG
|
|
18
|
+
* HTML-in-Canvas API (https://github.com/WICG/html-in-canvas).
|
|
19
|
+
*
|
|
20
|
+
* This is a side-effect-free helper - it is only reachable when {@link HtmlTexture} (or a direct caller)
|
|
21
|
+
* is imported, so it adds nothing to bundles that do not use it. It relies on
|
|
22
|
+
* `WebGLRenderingContext.texElementImage2D` (WebGL) or `GPUQueue.copyElementImageToTexture` (WebGPU),
|
|
23
|
+
* available either natively (behind chrome://flags/#canvas-draw-element) or through the
|
|
24
|
+
* three-html-render polyfill.
|
|
25
|
+
* @param engine defines the engine that owns the texture
|
|
26
|
+
* @param texture defines the internal texture to update
|
|
27
|
+
* @param element defines the source HTML element (or captured ElementImage) to upload
|
|
28
|
+
* @param invertY defines if data must be stored with Y axis inverted (true by default)
|
|
29
|
+
* @param config defines an optional source rectangle and sizing configuration
|
|
30
|
+
* @returns true if the upload succeeded, false otherwise (e.g. the API is unavailable)
|
|
31
|
+
*/
|
|
32
|
+
export declare function UploadHtmlElementToTexture(engine: AbstractEngine, texture: Nullable<InternalTexture>, element: Element | ElementImage, invertY?: boolean, config?: WebGLCopyElementImageConfig): boolean;
|
|
33
|
+
/**
|
|
34
|
+
* Defines the options used to create an {@link HtmlTexture}.
|
|
35
|
+
*/
|
|
36
|
+
export interface IHtmlTextureOptions {
|
|
37
|
+
/** Defines the width of the texture in pixels (defaults to the element's offset width, then 256). */
|
|
38
|
+
width?: number;
|
|
39
|
+
/** Defines the height of the texture in pixels (defaults to the element's offset height, then 256). */
|
|
40
|
+
height?: number;
|
|
41
|
+
/** Defines whether mip maps should be created or not (default: false). */
|
|
42
|
+
generateMipMaps?: boolean;
|
|
43
|
+
/** Defines the sampling mode of the texture (default: TEXTURE_BILINEAR_SAMPLINGMODE). */
|
|
44
|
+
samplingMode?: number;
|
|
45
|
+
/** Defines the associated texture format (default: TEXTUREFORMAT_RGBA). */
|
|
46
|
+
format?: number;
|
|
47
|
+
/** Defines whether the texture is automatically updated when the host canvas emits a paint event (default: true). */
|
|
48
|
+
autoUpdate?: boolean;
|
|
49
|
+
/**
|
|
50
|
+
* Defines whether to fall back to an SVG `<foreignObject>` rasterization when the native WICG
|
|
51
|
+
* HTML-in-Canvas API is unavailable (default: true). The fallback works in any browser but only
|
|
52
|
+
* captures same-origin, inline-styled content as a static snapshot (see the documentation for caveats).
|
|
53
|
+
*/
|
|
54
|
+
useSvgFallback?: boolean;
|
|
55
|
+
/** Defines the engine instance to use the texture with. Not mandatory if a scene is provided. */
|
|
56
|
+
engine?: Nullable<AbstractEngine>;
|
|
57
|
+
/** Defines the scene the texture belongs to. Not mandatory if an engine is provided. */
|
|
58
|
+
scene?: Nullable<Scene>;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* A texture whose content is rendered from a live HTML element using the WICG HTML-in-Canvas API
|
|
62
|
+
* (https://github.com/WICG/html-in-canvas).
|
|
63
|
+
*
|
|
64
|
+
* The element is hosted as a child of the engine's rendering canvas (which is marked `layoutsubtree`) so
|
|
65
|
+
* the browser can lay it out and snapshot it; the WICG API requires the source element to be a direct
|
|
66
|
+
* child of the canvas whose context performs the upload. Content is uploaded through
|
|
67
|
+
* `WebGLRenderingContext.texElementImage2D` (WebGL) or `GPUQueue.copyElementImageToTexture` (WebGPU),
|
|
68
|
+
* available either natively (behind chrome://flags/#canvas-draw-element) or via the three-html-render
|
|
69
|
+
* polyfill (https://github.com/repalash/three-html-render). When neither is present, a built-in SVG
|
|
70
|
+
* `<foreignObject>` fallback is used instead (see `useSvgFallback`).
|
|
71
|
+
*
|
|
72
|
+
* As with {@link HtmlElementTexture}, updates are not automatic unless `autoUpdate` is enabled; in
|
|
73
|
+
* that case the texture refreshes whenever the host canvas reports a paint change.
|
|
74
|
+
*/
|
|
75
|
+
export declare class HtmlTexture extends BaseTexture {
|
|
76
|
+
/**
|
|
77
|
+
* The HTML element rendered into the texture.
|
|
78
|
+
*/
|
|
79
|
+
readonly element: HTMLElement;
|
|
80
|
+
/**
|
|
81
|
+
* The element that hosts {@link element} in the document. This is the engine's rendering canvas when one
|
|
82
|
+
* is available (required by the WICG API, which can only capture the element from that canvas), or a
|
|
83
|
+
* hidden helper `<div>` otherwise - in which case only the SVG fallback can render.
|
|
84
|
+
*/
|
|
85
|
+
readonly host: Nullable<HTMLElement>;
|
|
86
|
+
/**
|
|
87
|
+
* Observable triggered once the texture has been rendered for the first time.
|
|
88
|
+
*/
|
|
89
|
+
onLoadObservable: Observable<HtmlTexture>;
|
|
90
|
+
private static readonly _DefaultOptions;
|
|
91
|
+
private readonly _format;
|
|
92
|
+
private readonly _generateMipMaps;
|
|
93
|
+
private readonly _samplingMode;
|
|
94
|
+
private readonly _useSvgFallback;
|
|
95
|
+
private readonly _textureMatrix;
|
|
96
|
+
private _paintHandler;
|
|
97
|
+
private _ownsHost;
|
|
98
|
+
private _originalParent;
|
|
99
|
+
private _originalNextSibling;
|
|
100
|
+
private _addedInert;
|
|
101
|
+
private _width;
|
|
102
|
+
private _height;
|
|
103
|
+
/**
|
|
104
|
+
* Instantiates an HtmlTexture from an HTML element.
|
|
105
|
+
* @param name Defines the name of the texture
|
|
106
|
+
* @param element Defines the HTML element to render into the texture
|
|
107
|
+
* @param options Defines the texture creation options
|
|
108
|
+
*/
|
|
109
|
+
constructor(name: string, element: HTMLElement, options: IHtmlTextureOptions);
|
|
110
|
+
private _createHost;
|
|
111
|
+
private _createInternalTexture;
|
|
112
|
+
/**
|
|
113
|
+
* @returns the texture matrix used in most of the material.
|
|
114
|
+
*/
|
|
115
|
+
getTextureMatrix(): Matrix;
|
|
116
|
+
/**
|
|
117
|
+
* Requests the host canvas to emit a paint event on the next rendering update. When auto-update is
|
|
118
|
+
* enabled, this triggers a texture refresh in sync with the DOM. Falls back to an immediate update
|
|
119
|
+
* when the WICG API is not available.
|
|
120
|
+
*/
|
|
121
|
+
requestUpdate(): void;
|
|
122
|
+
/**
|
|
123
|
+
* Updates the content of the texture from the current state of the HTML element.
|
|
124
|
+
* @param invertY Defines whether the texture should be inverted on Y (true by default)
|
|
125
|
+
*/
|
|
126
|
+
update(invertY?: boolean): void;
|
|
127
|
+
/**
|
|
128
|
+
* Renders the element through an SVG `<foreignObject>` snapshot and uploads it to the texture. This
|
|
129
|
+
* fallback works in any browser but only captures same-origin, inline-styled content as a static image.
|
|
130
|
+
* @param engine defines the engine that owns the texture
|
|
131
|
+
* @param invertY defines whether the texture should be inverted on Y
|
|
132
|
+
*/
|
|
133
|
+
private _updateFromSvgFallback;
|
|
134
|
+
/**
|
|
135
|
+
* Serializes the element into an SVG `<foreignObject>` data URL sized to the texture.
|
|
136
|
+
* @returns a data URL, or null if serialization failed
|
|
137
|
+
*/
|
|
138
|
+
private _buildSvgDataUrl;
|
|
139
|
+
/**
|
|
140
|
+
* Gets the current class name of the texture useful for serialization or dynamic coding.
|
|
141
|
+
* @returns "HtmlTexture"
|
|
142
|
+
*/
|
|
143
|
+
getClassName(): string;
|
|
144
|
+
/**
|
|
145
|
+
* Dispose the texture and release its associated resources.
|
|
146
|
+
*/
|
|
147
|
+
dispose(): void;
|
|
148
|
+
}
|